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