1d416ed5362125619ca715e1b748a434c04322801Robert Berry/*
2d416ed5362125619ca715e1b748a434c04322801Robert Berry * Copyright (C) 2017 The Android Open Source Project
3d416ed5362125619ca715e1b748a434c04322801Robert Berry *
4d416ed5362125619ca715e1b748a434c04322801Robert Berry * Licensed under the Apache License, Version 2.0 (the "License");
5d416ed5362125619ca715e1b748a434c04322801Robert Berry * you may not use this file except in compliance with the License.
6d416ed5362125619ca715e1b748a434c04322801Robert Berry * You may obtain a copy of the License at
7d416ed5362125619ca715e1b748a434c04322801Robert Berry *
8d416ed5362125619ca715e1b748a434c04322801Robert Berry *      http://www.apache.org/licenses/LICENSE-2.0
9d416ed5362125619ca715e1b748a434c04322801Robert Berry *
10d416ed5362125619ca715e1b748a434c04322801Robert Berry * Unless required by applicable law or agreed to in writing, software
11d416ed5362125619ca715e1b748a434c04322801Robert Berry * distributed under the License is distributed on an "AS IS" BASIS,
12d416ed5362125619ca715e1b748a434c04322801Robert Berry * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13d416ed5362125619ca715e1b748a434c04322801Robert Berry * See the License for the specific language governing permissions and
14d416ed5362125619ca715e1b748a434c04322801Robert Berry * limitations under the License.
15d416ed5362125619ca715e1b748a434c04322801Robert Berry */
16d416ed5362125619ca715e1b748a434c04322801Robert Berry
17d416ed5362125619ca715e1b748a434c04322801Robert Berrypackage com.android.server.locksettings.recoverablekeystore;
18d416ed5362125619ca715e1b748a434c04322801Robert Berry
19235dc9da69049e9910febf664df3908363efbc42Robert Berryimport android.annotation.Nullable;
20c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhuimport com.android.internal.annotations.VisibleForTesting;
21c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhuimport java.math.BigInteger;
22c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhuimport java.nio.BufferUnderflowException;
23c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhuimport java.nio.ByteBuffer;
24c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhuimport java.nio.charset.StandardCharsets;
25c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhuimport java.security.InvalidAlgorithmParameterException;
26d416ed5362125619ca715e1b748a434c04322801Robert Berryimport java.security.InvalidKeyException;
27c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhuimport java.security.KeyFactory;
28c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhuimport java.security.KeyPair;
29c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhuimport java.security.KeyPairGenerator;
30d416ed5362125619ca715e1b748a434c04322801Robert Berryimport java.security.NoSuchAlgorithmException;
31235dc9da69049e9910febf664df3908363efbc42Robert Berryimport java.security.PrivateKey;
32d416ed5362125619ca715e1b748a434c04322801Robert Berryimport java.security.PublicKey;
33c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhuimport java.security.SecureRandom;
34c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhuimport java.security.interfaces.ECPublicKey;
35c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhuimport java.security.spec.ECFieldFp;
36c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhuimport java.security.spec.ECGenParameterSpec;
37c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhuimport java.security.spec.ECParameterSpec;
38c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhuimport java.security.spec.ECPoint;
39c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhuimport java.security.spec.ECPublicKeySpec;
40c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhuimport java.security.spec.EllipticCurve;
41c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhuimport java.security.spec.InvalidKeySpecException;
42c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhuimport java.util.Arrays;
43235dc9da69049e9910febf664df3908363efbc42Robert Berryimport javax.crypto.AEADBadTagException;
44c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhuimport javax.crypto.BadPaddingException;
45c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhuimport javax.crypto.Cipher;
46c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhuimport javax.crypto.IllegalBlockSizeException;
47c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhuimport javax.crypto.KeyAgreement;
48c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhuimport javax.crypto.Mac;
49c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhuimport javax.crypto.NoSuchPaddingException;
50c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhuimport javax.crypto.SecretKey;
51c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhuimport javax.crypto.spec.GCMParameterSpec;
52c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhuimport javax.crypto.spec.SecretKeySpec;
53235dc9da69049e9910febf664df3908363efbc42Robert Berry
54d416ed5362125619ca715e1b748a434c04322801Robert Berry/**
55c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu * Implementation of the SecureBox v2 crypto functions.
56c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu *
57c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu * <p>Securebox v2 provides a simple interface to perform encryptions by using any of the following
58c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu * credential types:
59c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu *
60c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu * <ul>
61c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu *   <li>A public key owned by the recipient,
62c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu *   <li>A secret shared between the sender and the recipient, or
63c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu *   <li>Both a recipient's public key and a shared secret.
64c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu * </ul>
65d416ed5362125619ca715e1b748a434c04322801Robert Berry *
66d416ed5362125619ca715e1b748a434c04322801Robert Berry * @hide
67d416ed5362125619ca715e1b748a434c04322801Robert Berry */
68d416ed5362125619ca715e1b748a434c04322801Robert Berrypublic class SecureBox {
69c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu
70c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu    private static final byte[] VERSION = new byte[] {(byte) 0x02, 0}; // LITTLE_ENDIAN_TWO_BYTES(2)
71c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu    private static final byte[] HKDF_SALT =
72c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu            concat("SECUREBOX".getBytes(StandardCharsets.UTF_8), VERSION);
73c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu    private static final byte[] HKDF_INFO_WITH_PUBLIC_KEY =
74c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu            "P256 HKDF-SHA-256 AES-128-GCM".getBytes(StandardCharsets.UTF_8);
75c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu    private static final byte[] HKDF_INFO_WITHOUT_PUBLIC_KEY =
76c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu            "SHARED HKDF-SHA-256 AES-128-GCM".getBytes(StandardCharsets.UTF_8);
77c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu    private static final byte[] CONSTANT_01 = {(byte) 0x01};
78c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu    private static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
79c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu    private static final byte EC_PUBLIC_KEY_PREFIX = (byte) 0x04;
80c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu
81c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu    private static final String CIPHER_ALG = "AES";
82c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu    private static final String EC_ALG = "EC";
83c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu    private static final String EC_P256_COMMON_NAME = "secp256r1";
84c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu    private static final String EC_P256_OPENSSL_NAME = "prime256v1";
85c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu    private static final String ENC_ALG = "AES/GCM/NoPadding";
86c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu    private static final String KA_ALG = "ECDH";
87c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu    private static final String MAC_ALG = "HmacSHA256";
88c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu
89c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu    private static final int EC_COORDINATE_LEN_BYTES = 32;
90c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu    private static final int EC_PUBLIC_KEY_LEN_BYTES = 2 * EC_COORDINATE_LEN_BYTES + 1;
91c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu    private static final int GCM_NONCE_LEN_BYTES = 12;
92c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu    private static final int GCM_KEY_LEN_BYTES = 16;
93c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu    private static final int GCM_TAG_LEN_BYTES = 16;
94c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu
95c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu    private static final BigInteger BIG_INT_02 = BigInteger.valueOf(2);
96c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu
97c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu    private enum AesGcmOperation {
98c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        ENCRYPT,
99c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        DECRYPT
100c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu    }
101c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu
102c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu    // Parameters for the NIST P-256 curve y^2 = x^3 + ax + b (mod p)
103c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu    private static final BigInteger EC_PARAM_P =
104c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu            new BigInteger("ffffffff00000001000000000000000000000000ffffffffffffffffffffffff", 16);
105c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu    private static final BigInteger EC_PARAM_A = EC_PARAM_P.subtract(new BigInteger("3"));
106c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu    private static final BigInteger EC_PARAM_B =
107c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu            new BigInteger("5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b", 16);
108c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu
109c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu    @VisibleForTesting static final ECParameterSpec EC_PARAM_SPEC;
110c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu
111c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu    static {
112c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        EllipticCurve curveSpec =
113c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu                new EllipticCurve(new ECFieldFp(EC_PARAM_P), EC_PARAM_A, EC_PARAM_B);
114c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        ECPoint generator =
115c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu                new ECPoint(
116c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu                        new BigInteger(
117c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu                                "6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296",
118c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu                                16),
119c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu                        new BigInteger(
120c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu                                "4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5",
121c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu                                16));
122c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        BigInteger generatorOrder =
123c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu                new BigInteger(
124c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu                        "ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551", 16);
125c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        EC_PARAM_SPEC = new ECParameterSpec(curveSpec, generator, generatorOrder, /* cofactor */ 1);
126c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu    }
127c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu
128c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu    private SecureBox() {}
129c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu
130d416ed5362125619ca715e1b748a434c04322801Robert Berry    /**
131c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu     * Randomly generates a public-key pair that can be used for the functions {@link #encrypt} and
132c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu     * {@link #decrypt}.
133d416ed5362125619ca715e1b748a434c04322801Robert Berry     *
134c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu     * @return the randomly generated public-key pair
135c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu     * @throws NoSuchAlgorithmException if the underlying crypto algorithm is not supported
136c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu     * @hide
137c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu     */
138c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu    public static KeyPair genKeyPair() throws NoSuchAlgorithmException {
139c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(EC_ALG);
140c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        try {
141c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu            // Try using the OpenSSL provider first
142c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu            keyPairGenerator.initialize(new ECGenParameterSpec(EC_P256_OPENSSL_NAME));
143c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu            return keyPairGenerator.generateKeyPair();
144c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        } catch (InvalidAlgorithmParameterException ex) {
145c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu            // Try another name for NIST P-256
146c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        }
147c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        try {
148c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu            keyPairGenerator.initialize(new ECGenParameterSpec(EC_P256_COMMON_NAME));
149c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu            return keyPairGenerator.generateKeyPair();
150c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        } catch (InvalidAlgorithmParameterException ex) {
151c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu            throw new NoSuchAlgorithmException("Unable to find the NIST P-256 curve", ex);
152c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        }
153c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu    }
154c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu
155c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu    /**
156c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu     * Encrypts {@code payload} by using {@code theirPublicKey} and/or {@code sharedSecret}. At
157c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu     * least one of {@code theirPublicKey} and {@code sharedSecret} must be non-null, and an empty
158c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu     * {@code sharedSecret} is equivalent to null.
159c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu     *
160c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu     * <p>Note that {@code header} will be authenticated (but not encrypted) together with {@code
161c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu     * payload}, and the same {@code header} has to be provided for {@link #decrypt}.
162c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu     *
163c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu     * @param theirPublicKey the recipient's public key, or null if the payload is to be encrypted
164c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu     *     only with the shared secret
165c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu     * @param sharedSecret the secret shared between the sender and the recipient, or null if the
166c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu     *     payload is to be encrypted only with the recipient's public key
167c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu     * @param header the data that will be authenticated with {@code payload} but not encrypted, or
168c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu     *     null if the data is empty
169c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu     * @param payload the data to be encrypted, or null if the data is empty
170c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu     * @return the encrypted payload
171c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu     * @throws NoSuchAlgorithmException if any underlying crypto algorithm is not supported
172c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu     * @throws InvalidKeyException if the provided key is invalid for underlying crypto algorithms
173d416ed5362125619ca715e1b748a434c04322801Robert Berry     * @hide
174d416ed5362125619ca715e1b748a434c04322801Robert Berry     */
175d416ed5362125619ca715e1b748a434c04322801Robert Berry    public static byte[] encrypt(
176235dc9da69049e9910febf664df3908363efbc42Robert Berry            @Nullable PublicKey theirPublicKey,
177235dc9da69049e9910febf664df3908363efbc42Robert Berry            @Nullable byte[] sharedSecret,
178235dc9da69049e9910febf664df3908363efbc42Robert Berry            @Nullable byte[] header,
179235dc9da69049e9910febf664df3908363efbc42Robert Berry            @Nullable byte[] payload)
180d416ed5362125619ca715e1b748a434c04322801Robert Berry            throws NoSuchAlgorithmException, InvalidKeyException {
181c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        sharedSecret = emptyByteArrayIfNull(sharedSecret);
182c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        if (theirPublicKey == null && sharedSecret.length == 0) {
183c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu            throw new IllegalArgumentException("Both the public key and shared secret are empty");
184c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        }
185c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        header = emptyByteArrayIfNull(header);
186c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        payload = emptyByteArrayIfNull(payload);
187c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu
188c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        KeyPair senderKeyPair;
189c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        byte[] dhSecret;
190c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        byte[] hkdfInfo;
191c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        if (theirPublicKey == null) {
192c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu            senderKeyPair = null;
193c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu            dhSecret = EMPTY_BYTE_ARRAY;
194c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu            hkdfInfo = HKDF_INFO_WITHOUT_PUBLIC_KEY;
195c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        } else {
196c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu            senderKeyPair = genKeyPair();
197c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu            dhSecret = dhComputeSecret(senderKeyPair.getPrivate(), theirPublicKey);
198c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu            hkdfInfo = HKDF_INFO_WITH_PUBLIC_KEY;
199c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        }
200c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu
201c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        byte[] randNonce = genRandomNonce();
202c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        byte[] keyingMaterial = concat(dhSecret, sharedSecret);
203c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        SecretKey encryptionKey = hkdfDeriveKey(keyingMaterial, HKDF_SALT, hkdfInfo);
204c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        byte[] ciphertext = aesGcmEncrypt(encryptionKey, randNonce, payload, header);
205c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        if (senderKeyPair == null) {
206c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu            return concat(VERSION, randNonce, ciphertext);
207c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        } else {
208c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu            return concat(
209c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu                    VERSION, encodePublicKey(senderKeyPair.getPublic()), randNonce, ciphertext);
210c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        }
211d416ed5362125619ca715e1b748a434c04322801Robert Berry    }
212235dc9da69049e9910febf664df3908363efbc42Robert Berry
213235dc9da69049e9910febf664df3908363efbc42Robert Berry    /**
214c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu     * Decrypts {@code encryptedPayload} by using {@code ourPrivateKey} and/or {@code sharedSecret}.
215c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu     * At least one of {@code ourPrivateKey} and {@code sharedSecret} must be non-null, and an empty
216c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu     * {@code sharedSecret} is equivalent to null.
217235dc9da69049e9910febf664df3908363efbc42Robert Berry     *
218c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu     * <p>Note that {@code header} should be the same data used for {@link #encrypt}, which is
219c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu     * authenticated (but not encrypted) together with {@code payload}; otherwise, an {@code
220c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu     * AEADBadTagException} will be thrown.
221c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu     *
222c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu     * @param ourPrivateKey the recipient's private key, or null if the payload was encrypted only
223c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu     *     with the shared secret
224c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu     * @param sharedSecret the secret shared between the sender and the recipient, or null if the
225c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu     *     payload was encrypted only with the recipient's public key
226c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu     * @param header the data that was authenticated with the original payload but not encrypted, or
227c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu     *     null if the data is empty
228c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu     * @param encryptedPayload the data to be decrypted
229c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu     * @return the original payload that was encrypted
230c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu     * @throws NoSuchAlgorithmException if any underlying crypto algorithm is not supported
231c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu     * @throws InvalidKeyException if the provided key is invalid for underlying crypto algorithms
232c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu     * @throws AEADBadTagException if the authentication tag contained in {@code encryptedPayload}
233b9a220b9b50ef8d0e19d619721209233b3253c2cRobert Berry     *     cannot be validated, or if the payload is not a valid SecureBox V2 payload.
234235dc9da69049e9910febf664df3908363efbc42Robert Berry     * @hide
235235dc9da69049e9910febf664df3908363efbc42Robert Berry     */
236235dc9da69049e9910febf664df3908363efbc42Robert Berry    public static byte[] decrypt(
237235dc9da69049e9910febf664df3908363efbc42Robert Berry            @Nullable PrivateKey ourPrivateKey,
238235dc9da69049e9910febf664df3908363efbc42Robert Berry            @Nullable byte[] sharedSecret,
239235dc9da69049e9910febf664df3908363efbc42Robert Berry            @Nullable byte[] header,
240235dc9da69049e9910febf664df3908363efbc42Robert Berry            byte[] encryptedPayload)
241235dc9da69049e9910febf664df3908363efbc42Robert Berry            throws NoSuchAlgorithmException, InvalidKeyException, AEADBadTagException {
242c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        sharedSecret = emptyByteArrayIfNull(sharedSecret);
243c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        if (ourPrivateKey == null && sharedSecret.length == 0) {
244c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu            throw new IllegalArgumentException("Both the private key and shared secret are empty");
245c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        }
246c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        header = emptyByteArrayIfNull(header);
247b9a220b9b50ef8d0e19d619721209233b3253c2cRobert Berry        if (encryptedPayload == null) {
248b9a220b9b50ef8d0e19d619721209233b3253c2cRobert Berry            throw new NullPointerException("Encrypted payload must not be null.");
249b9a220b9b50ef8d0e19d619721209233b3253c2cRobert Berry        }
250c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu
251c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        ByteBuffer ciphertextBuffer = ByteBuffer.wrap(encryptedPayload);
252c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        byte[] version = readEncryptedPayload(ciphertextBuffer, VERSION.length);
253c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        if (!Arrays.equals(version, VERSION)) {
254b9a220b9b50ef8d0e19d619721209233b3253c2cRobert Berry            throw new AEADBadTagException("The payload was not encrypted by SecureBox v2");
255c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        }
256c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu
257c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        byte[] senderPublicKeyBytes;
258c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        byte[] dhSecret;
259c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        byte[] hkdfInfo;
260c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        if (ourPrivateKey == null) {
261c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu            dhSecret = EMPTY_BYTE_ARRAY;
262c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu            hkdfInfo = HKDF_INFO_WITHOUT_PUBLIC_KEY;
263c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        } else {
264c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu            senderPublicKeyBytes = readEncryptedPayload(ciphertextBuffer, EC_PUBLIC_KEY_LEN_BYTES);
265c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu            dhSecret = dhComputeSecret(ourPrivateKey, decodePublicKey(senderPublicKeyBytes));
266c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu            hkdfInfo = HKDF_INFO_WITH_PUBLIC_KEY;
267c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        }
268c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu
269c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        byte[] randNonce = readEncryptedPayload(ciphertextBuffer, GCM_NONCE_LEN_BYTES);
270c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        byte[] ciphertext = readEncryptedPayload(ciphertextBuffer, ciphertextBuffer.remaining());
271c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        byte[] keyingMaterial = concat(dhSecret, sharedSecret);
272c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        SecretKey decryptionKey = hkdfDeriveKey(keyingMaterial, HKDF_SALT, hkdfInfo);
273c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        return aesGcmDecrypt(decryptionKey, randNonce, ciphertext, header);
274c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu    }
275c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu
276b9a220b9b50ef8d0e19d619721209233b3253c2cRobert Berry    private static byte[] readEncryptedPayload(ByteBuffer buffer, int length)
277b9a220b9b50ef8d0e19d619721209233b3253c2cRobert Berry            throws AEADBadTagException {
278c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        byte[] output = new byte[length];
279c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        try {
280c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu            buffer.get(output);
281c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        } catch (BufferUnderflowException ex) {
282b9a220b9b50ef8d0e19d619721209233b3253c2cRobert Berry            throw new AEADBadTagException("The encrypted payload is too short");
283c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        }
284c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        return output;
285c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu    }
286c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu
287c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu    private static byte[] dhComputeSecret(PrivateKey ourPrivateKey, PublicKey theirPublicKey)
288c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu            throws NoSuchAlgorithmException, InvalidKeyException {
289c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        KeyAgreement agreement = KeyAgreement.getInstance(KA_ALG);
290c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        try {
291c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu            agreement.init(ourPrivateKey);
292c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        } catch (RuntimeException ex) {
293c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu            // Rethrow the RuntimeException as InvalidKeyException
294c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu            throw new InvalidKeyException(ex);
295c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        }
296c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        agreement.doPhase(theirPublicKey, /*lastPhase=*/ true);
297c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        return agreement.generateSecret();
298c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu    }
299c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu
300c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu    /** Derives a 128-bit AES key. */
301c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu    private static SecretKey hkdfDeriveKey(byte[] secret, byte[] salt, byte[] info)
302c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu            throws NoSuchAlgorithmException {
303c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        Mac mac = Mac.getInstance(MAC_ALG);
304c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        try {
305c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu            mac.init(new SecretKeySpec(salt, MAC_ALG));
306c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        } catch (InvalidKeyException ex) {
307c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu            // This should never happen
308c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu            throw new RuntimeException(ex);
309c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        }
310c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        byte[] pseudorandomKey = mac.doFinal(secret);
311c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu
312c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        try {
313c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu            mac.init(new SecretKeySpec(pseudorandomKey, MAC_ALG));
314c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        } catch (InvalidKeyException ex) {
315c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu            // This should never happen
316c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu            throw new RuntimeException(ex);
317c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        }
318c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        mac.update(info);
319c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        // Hashing just one block will yield 256 bits, which is enough to construct the AES key
320c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        byte[] hkdfOutput = mac.doFinal(CONSTANT_01);
321c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu
322c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        return new SecretKeySpec(Arrays.copyOf(hkdfOutput, GCM_KEY_LEN_BYTES), CIPHER_ALG);
323c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu    }
324c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu
325c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu    private static byte[] aesGcmEncrypt(SecretKey key, byte[] nonce, byte[] plaintext, byte[] aad)
326c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu            throws NoSuchAlgorithmException, InvalidKeyException {
327c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        try {
328c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu            return aesGcmInternal(AesGcmOperation.ENCRYPT, key, nonce, plaintext, aad);
329c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        } catch (AEADBadTagException ex) {
330c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu            // This should never happen
331c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu            throw new RuntimeException(ex);
332c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        }
333c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu    }
334c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu
335c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu    private static byte[] aesGcmDecrypt(SecretKey key, byte[] nonce, byte[] ciphertext, byte[] aad)
336c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu            throws NoSuchAlgorithmException, InvalidKeyException, AEADBadTagException {
337c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        return aesGcmInternal(AesGcmOperation.DECRYPT, key, nonce, ciphertext, aad);
338c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu    }
339c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu
340c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu    private static byte[] aesGcmInternal(
341c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu            AesGcmOperation operation, SecretKey key, byte[] nonce, byte[] text, byte[] aad)
342c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu            throws NoSuchAlgorithmException, InvalidKeyException, AEADBadTagException {
343c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        Cipher cipher;
344c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        try {
345c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu            cipher = Cipher.getInstance(ENC_ALG);
346c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        } catch (NoSuchPaddingException ex) {
347c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu            // This should never happen because AES-GCM doesn't use padding
348c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu            throw new RuntimeException(ex);
349c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        }
350c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        GCMParameterSpec spec = new GCMParameterSpec(GCM_TAG_LEN_BYTES * 8, nonce);
351c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        try {
352c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu            if (operation == AesGcmOperation.DECRYPT) {
353c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu                cipher.init(Cipher.DECRYPT_MODE, key, spec);
354c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu            } else {
355c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu                cipher.init(Cipher.ENCRYPT_MODE, key, spec);
356c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu            }
357c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        } catch (InvalidAlgorithmParameterException ex) {
358c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu            // This should never happen
359c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu            throw new RuntimeException(ex);
360c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        }
361c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        try {
362c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu            cipher.updateAAD(aad);
363c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu            return cipher.doFinal(text);
364c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        } catch (AEADBadTagException ex) {
365c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu            // Catch and rethrow AEADBadTagException first because it's a subclass of
366c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu            // BadPaddingException
367c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu            throw ex;
368c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        } catch (IllegalBlockSizeException | BadPaddingException ex) {
369c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu            // This should never happen because AES-GCM can handle inputs of any length without
370c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu            // padding
371c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu            throw new RuntimeException(ex);
372c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        }
373c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu    }
374c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu
37594ea4e4caf0c41042df288b6fcdade01c0ce3430Robert Berry    /**
37694ea4e4caf0c41042df288b6fcdade01c0ce3430Robert Berry     * Encodes public key in format expected by the secure hardware module. This is used as part
37794ea4e4caf0c41042df288b6fcdade01c0ce3430Robert Berry     * of the vault params.
37894ea4e4caf0c41042df288b6fcdade01c0ce3430Robert Berry     *
37994ea4e4caf0c41042df288b6fcdade01c0ce3430Robert Berry     * @param publicKey The public key.
38094ea4e4caf0c41042df288b6fcdade01c0ce3430Robert Berry     * @return The key packed into a 65-byte array.
38194ea4e4caf0c41042df288b6fcdade01c0ce3430Robert Berry     */
382c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu    static byte[] encodePublicKey(PublicKey publicKey) {
383c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        ECPoint point = ((ECPublicKey) publicKey).getW();
384c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        byte[] x = point.getAffineX().toByteArray();
385c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        byte[] y = point.getAffineY().toByteArray();
386c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu
387c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        byte[] output = new byte[EC_PUBLIC_KEY_LEN_BYTES];
388c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        // The order of arraycopy() is important, because the coordinates may have a one-byte
389c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        // leading 0 for the sign bit of two's complement form
390c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        System.arraycopy(y, 0, output, EC_PUBLIC_KEY_LEN_BYTES - y.length, y.length);
391c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        System.arraycopy(x, 0, output, 1 + EC_COORDINATE_LEN_BYTES - x.length, x.length);
392c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        output[0] = EC_PUBLIC_KEY_PREFIX;
393c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        return output;
394c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu    }
395c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu
396c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu    @VisibleForTesting
397c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu    static PublicKey decodePublicKey(byte[] keyBytes)
398c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu            throws NoSuchAlgorithmException, InvalidKeyException {
399c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        BigInteger x =
400c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu                new BigInteger(
401c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu                        /*signum=*/ 1,
402c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu                        Arrays.copyOfRange(keyBytes, 1, 1 + EC_COORDINATE_LEN_BYTES));
403c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        BigInteger y =
404c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu                new BigInteger(
405c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu                        /*signum=*/ 1,
406c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu                        Arrays.copyOfRange(
407c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu                                keyBytes, 1 + EC_COORDINATE_LEN_BYTES, EC_PUBLIC_KEY_LEN_BYTES));
408c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu
409c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        // Checks if the point is indeed on the P-256 curve for security considerations
410c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        validateEcPoint(x, y);
411c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu
412c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        KeyFactory keyFactory = KeyFactory.getInstance(EC_ALG);
413c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        try {
414c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu            return keyFactory.generatePublic(new ECPublicKeySpec(new ECPoint(x, y), EC_PARAM_SPEC));
415c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        } catch (InvalidKeySpecException ex) {
416c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu            // This should never happen
417c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu            throw new RuntimeException(ex);
418c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        }
419c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu    }
420c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu
421c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu    private static void validateEcPoint(BigInteger x, BigInteger y) throws InvalidKeyException {
422c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        if (x.compareTo(EC_PARAM_P) >= 0
423c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu                || y.compareTo(EC_PARAM_P) >= 0
424c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu                || x.signum() == -1
425c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu                || y.signum() == -1) {
426c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu            throw new InvalidKeyException("Point lies outside of the expected curve");
427c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        }
428c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu
429c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        // Points on the curve satisfy y^2 = x^3 + ax + b (mod p)
430c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        BigInteger lhs = y.modPow(BIG_INT_02, EC_PARAM_P);
431c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        BigInteger rhs =
432c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu                x.modPow(BIG_INT_02, EC_PARAM_P) // x^2
433c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu                        .add(EC_PARAM_A) // x^2 + a
434c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu                        .mod(EC_PARAM_P) // This will speed up the next multiplication
435c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu                        .multiply(x) // (x^2 + a) * x = x^3 + ax
436c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu                        .add(EC_PARAM_B) // x^3 + ax + b
437c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu                        .mod(EC_PARAM_P);
438c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        if (!lhs.equals(rhs)) {
439c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu            throw new InvalidKeyException("Point lies outside of the expected curve");
440c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        }
441c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu    }
442c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu
443c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu    private static byte[] genRandomNonce() throws NoSuchAlgorithmException {
444c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        byte[] nonce = new byte[GCM_NONCE_LEN_BYTES];
445c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        new SecureRandom().nextBytes(nonce);
446c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        return nonce;
447c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu    }
448c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu
449c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu    @VisibleForTesting
450c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu    static byte[] concat(byte[]... inputs) {
451c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        int length = 0;
452c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        for (int i = 0; i < inputs.length; i++) {
453c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu            if (inputs[i] == null) {
454c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu                inputs[i] = EMPTY_BYTE_ARRAY;
455c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu            }
456c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu            length += inputs[i].length;
457c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        }
458c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu
459c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        byte[] output = new byte[length];
460c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        int outputPos = 0;
461c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        for (byte[] input : inputs) {
462c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu            System.arraycopy(input, /*srcPos=*/ 0, output, outputPos, input.length);
463c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu            outputPos += input.length;
464c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        }
465c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        return output;
466c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu    }
467c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu
468c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu    private static byte[] emptyByteArrayIfNull(@Nullable byte[] input) {
469c69d8097e5fb63de6ff66f252012506bd5406c7cBo Zhu        return input == null ? EMPTY_BYTE_ARRAY : input;
470235dc9da69049e9910febf664df3908363efbc42Robert Berry    }
471d416ed5362125619ca715e1b748a434c04322801Robert Berry}
472