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