1e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin/* 2e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin * Copyright (C) 2013 The Android Open Source Project 3e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin * 4e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin * Licensed under the Apache License, Version 2.0 (the "License"); 5e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin * you may not use this file except in compliance with the License. 6e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin * You may obtain a copy of the License at 7e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin * 8e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin * http://www.apache.org/licenses/LICENSE-2.0 9e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin * 10e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin * Unless required by applicable law or agreed to in writing, software 11e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin * distributed under the License is distributed on an "AS IS" BASIS, 12e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin * See the License for the specific language governing permissions and 14e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin * limitations under the License. 15e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin */ 16e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin 1738375a4d0b3d34e2babbd2f6a013976c7c439696Kenny Rootpackage org.conscrypt; 18e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin 19e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubinimport java.security.InvalidAlgorithmParameterException; 20e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubinimport java.security.InvalidKeyException; 21e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubinimport java.security.Key; 220469e3a6a9b5e854b8b985039de8ba4f6e6037bdAlex Klyubinimport java.security.KeyFactory; 230469e3a6a9b5e854b8b985039de8ba4f6e6037bdAlex Klyubinimport java.security.PrivateKey; 240469e3a6a9b5e854b8b985039de8ba4f6e6037bdAlex Klyubinimport java.security.PublicKey; 25e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubinimport java.security.SecureRandom; 26e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubinimport java.security.spec.AlgorithmParameterSpec; 27e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubinimport javax.crypto.KeyAgreementSpi; 28e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubinimport javax.crypto.SecretKey; 29e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubinimport javax.crypto.ShortBufferException; 30e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubinimport javax.crypto.spec.SecretKeySpec; 31e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin 32e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin/** 33e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin * Elliptic Curve Diffie-Hellman key agreement backed by the OpenSSL engine. 34e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin */ 35e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubinpublic final class OpenSSLECDHKeyAgreement extends KeyAgreementSpi { 36e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin 37e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin /** OpenSSL handle of the private key. Only available after the engine has been initialized. */ 38e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin private OpenSSLKey mOpenSslPrivateKey; 39e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin 40e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin /** 41e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin * Expected length (in bytes) of the agreed key ({@link #mResult}). Only available after the 42e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin * engine has been initialized. 43e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin */ 44e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin private int mExpectedResultLength; 45e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin 46e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin /** Agreed key. Only available after {@link #engineDoPhase(Key, boolean)} completes. */ 47e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin private byte[] mResult; 48e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin 49e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin @Override 50e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin public Key engineDoPhase(Key key, boolean lastPhase) throws InvalidKeyException { 51e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin if (mOpenSslPrivateKey == null) { 52e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin throw new IllegalStateException("Not initialized"); 53e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin } 54e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin if (!lastPhase) { 55e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin throw new IllegalStateException("ECDH only has one phase"); 56e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin } 570469e3a6a9b5e854b8b985039de8ba4f6e6037bdAlex Klyubin 58e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin if (key == null) { 59e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin throw new InvalidKeyException("key == null"); 60e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin } 610469e3a6a9b5e854b8b985039de8ba4f6e6037bdAlex Klyubin if (!(key instanceof PublicKey)) { 620469e3a6a9b5e854b8b985039de8ba4f6e6037bdAlex Klyubin throw new InvalidKeyException("Not a public key: " + key.getClass()); 63e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin } 640469e3a6a9b5e854b8b985039de8ba4f6e6037bdAlex Klyubin OpenSSLKey openSslPublicKey = translateKeyToEcOpenSSLKey(key); 65e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin 66e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin byte[] buffer = new byte[mExpectedResultLength]; 67e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin int actualResultLength = NativeCrypto.ECDH_compute_key( 68e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin buffer, 69e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin 0, 70e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin openSslPublicKey.getPkeyContext(), 71e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin mOpenSslPrivateKey.getPkeyContext()); 72e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin byte[] result; 73e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin if (actualResultLength == -1) { 74e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin throw new RuntimeException("Engine returned " + actualResultLength); 75e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin } else if (actualResultLength == mExpectedResultLength) { 76e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin // The output is as long as expected -- use the whole buffer 77e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin result = buffer; 78e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin } else if (actualResultLength < mExpectedResultLength) { 79e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin // The output is shorter than expected -- use only what's produced by the engine 80e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin result = new byte[actualResultLength]; 81e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin System.arraycopy(buffer, 0, mResult, 0, mResult.length); 82e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin } else { 83e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin // The output is longer than expected 84e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin throw new RuntimeException("Engine produced a longer than expected result. Expected: " 85e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin + mExpectedResultLength + ", actual: " + actualResultLength); 86e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin } 87e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin mResult = result; 88e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin 89e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin return null; // No intermediate key 90e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin } 91e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin 92e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin @Override 93e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin protected int engineGenerateSecret(byte[] sharedSecret, int offset) 94e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin throws ShortBufferException { 95e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin checkCompleted(); 96e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin int available = sharedSecret.length - offset; 97e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin if (mResult.length > available) { 98e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin throw new ShortBufferException( 99e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin "Needed: " + mResult.length + ", available: " + available); 100e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin } 101e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin 102e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin System.arraycopy(mResult, 0, sharedSecret, offset, mResult.length); 103e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin return mResult.length; 104e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin } 105e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin 106e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin @Override 107e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin protected byte[] engineGenerateSecret() { 108e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin checkCompleted(); 109e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin return mResult; 110e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin } 111e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin 112e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin @Override 113e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin protected SecretKey engineGenerateSecret(String algorithm) { 114e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin checkCompleted(); 115e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin return new SecretKeySpec(engineGenerateSecret(), algorithm); 116e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin } 117e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin 118e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin @Override 119e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin protected void engineInit(Key key, SecureRandom random) throws InvalidKeyException { 120e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin if (key == null) { 121e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin throw new InvalidKeyException("key == null"); 122e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin } 1230469e3a6a9b5e854b8b985039de8ba4f6e6037bdAlex Klyubin if (!(key instanceof PrivateKey)) { 1240469e3a6a9b5e854b8b985039de8ba4f6e6037bdAlex Klyubin throw new InvalidKeyException("Not a private key: " + key.getClass()); 125e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin } 1260469e3a6a9b5e854b8b985039de8ba4f6e6037bdAlex Klyubin 1270469e3a6a9b5e854b8b985039de8ba4f6e6037bdAlex Klyubin OpenSSLKey openSslKey = translateKeyToEcOpenSSLKey(key); 1280469e3a6a9b5e854b8b985039de8ba4f6e6037bdAlex Klyubin int fieldSizeBits = NativeCrypto.EC_GROUP_get_degree(NativeCrypto.EC_KEY_get0_group( 1290469e3a6a9b5e854b8b985039de8ba4f6e6037bdAlex Klyubin openSslKey.getPkeyContext())); 1300469e3a6a9b5e854b8b985039de8ba4f6e6037bdAlex Klyubin mExpectedResultLength = (fieldSizeBits + 7) / 8; 1310469e3a6a9b5e854b8b985039de8ba4f6e6037bdAlex Klyubin mOpenSslPrivateKey = openSslKey; 132e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin } 133e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin 134e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin @Override 135e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin protected void engineInit(Key key, AlgorithmParameterSpec params, 136e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException { 137e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin // ECDH doesn't need an AlgorithmParameterSpec 138e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin if (params != null) { 139e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin throw new InvalidAlgorithmParameterException("No algorithm parameters supported"); 140e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin } 141e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin engineInit(key, random); 142e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin } 143e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin 144e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin private void checkCompleted() { 145e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin if (mResult == null) { 146e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin throw new IllegalStateException("Key agreement not completed"); 147e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin } 148e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin } 1490469e3a6a9b5e854b8b985039de8ba4f6e6037bdAlex Klyubin 1500469e3a6a9b5e854b8b985039de8ba4f6e6037bdAlex Klyubin private static OpenSSLKey translateKeyToEcOpenSSLKey(Key key) throws InvalidKeyException { 1510469e3a6a9b5e854b8b985039de8ba4f6e6037bdAlex Klyubin try { 1520469e3a6a9b5e854b8b985039de8ba4f6e6037bdAlex Klyubin return ((OpenSSLKeyHolder) KeyFactory.getInstance( 1530469e3a6a9b5e854b8b985039de8ba4f6e6037bdAlex Klyubin "EC", OpenSSLProvider.PROVIDER_NAME).translateKey(key)).getOpenSSLKey(); 1540469e3a6a9b5e854b8b985039de8ba4f6e6037bdAlex Klyubin } catch (Exception e) { 1550469e3a6a9b5e854b8b985039de8ba4f6e6037bdAlex Klyubin throw new InvalidKeyException("Failed to translate key to OpenSSL EC key", e); 1560469e3a6a9b5e854b8b985039de8ba4f6e6037bdAlex Klyubin } 1570469e3a6a9b5e854b8b985039de8ba4f6e6037bdAlex Klyubin } 158e741559fd878ee6e3deca9102f7c27e1c1ca70d0Alex Klyubin} 159