diff options
Diffstat (limited to 'ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com')
19 files changed, 4665 insertions, 0 deletions
diff --git a/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAESKey.java b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAESKey.java new file mode 100644 index 0000000..41059bb --- /dev/null +++ b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAESKey.java @@ -0,0 +1,53 @@ +/* + * Copyright(C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" (short)0IS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.javacard.seprovider; + +import javacard.security.AESKey; +import org.globalplatform.upgrade.Element; + +/** This is a wrapper class for AESKey. */ +public class KMAESKey implements KMKey { + + public AESKey aesKey; + + public KMAESKey(AESKey key) { + aesKey = key; + } + + public static void onSave(Element element, KMAESKey kmKey) { + element.write(kmKey.aesKey); + } + + public static KMAESKey onRestore(AESKey aesKey) { + if (aesKey == null) { + return null; + } + return new KMAESKey(aesKey); + } + + public static short getBackupPrimitiveByteCount() { + return (short) 0; + } + + public static short getBackupObjectCount() { + return (short) 1; + } + + @Override + public short getPublicKey(byte[] buf, short offset) { + return (short) 0; + } +} diff --git a/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAndroidSEProvider.java b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAndroidSEProvider.java new file mode 100644 index 0000000..cb33272 --- /dev/null +++ b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAndroidSEProvider.java @@ -0,0 +1,1578 @@ +/* + * Copyright(C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.javacard.seprovider; + +import javacard.framework.ISO7816; +import javacard.framework.ISOException; +import javacard.framework.JCSystem; +import javacard.framework.Util; +import javacard.security.AESKey; +import javacard.security.CryptoException; +import javacard.security.DESKey; +import javacard.security.ECPrivateKey; +import javacard.security.ECPublicKey; +import javacard.security.HMACKey; +import javacard.security.Key; +import javacard.security.KeyAgreement; +import javacard.security.KeyBuilder; +import javacard.security.KeyPair; +import javacard.security.MessageDigest; +import javacard.security.RSAPrivateKey; +import javacard.security.RandomData; +import javacard.security.Signature; +import javacardx.crypto.AEADCipher; +import javacardx.crypto.Cipher; +import org.globalplatform.upgrade.Element; +import org.globalplatform.upgrade.UpgradeManager; + +/** + * This class implements KMSEProvider and provides all the necessary crypto operations required to + * support the KeyMint specification. This class supports AES, 3DES, HMAC, RSA, ECDSA, ECDH + * algorithms additionally it also supports ECDSA_NO_DIGEST, RSA_NO_DIGEST and RSA_OAEP_MGF1_SHA1 + * and RSA_OAEP_MGF1_SHA256 algorithms. This class follows the pattern of Init-Update-Final for the + * crypto operations. + */ +public class KMAndroidSEProvider implements KMSEProvider { + + // The tag length for AES GCM algorithm. + public static final byte AES_GCM_TAG_LENGTH = 16; + // The nonce length for AES GCM algorithm. + public static final byte AES_GCM_NONCE_LENGTH = 12; + // AES keysize offsets in aesKeys[] for 128 and 256 sizes respectively. + public static final byte KEYSIZE_128_OFFSET = 0x00; + public static final byte KEYSIZE_256_OFFSET = 0x01; + // The size of the temporary buffer. + public static final short TMP_ARRAY_SIZE = 300; + // The length of the rsa key in bytes. + private static final short RSA_KEY_SIZE = 256; + // Below are the flag to denote device reset events + public static final byte POWER_RESET_FALSE = (byte) 0xAA; + public static final byte POWER_RESET_TRUE = (byte) 0x00; + // The computed HMAC key size. + private static final byte COMPUTED_HMAC_KEY_SIZE = 32; + // The constant 'L' as defiend in + // https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-108.pdf, page 12. + private static byte[] CMAC_KDF_CONSTANT_L; + // Constant to represent 0. + private static byte[] CMAC_KDF_CONSTANT_ZERO; + // KeyAgreement instance. + private static KeyAgreement keyAgreement; + + // AESKey + private AESKey aesKeys[]; + // DES3Key + private DESKey triDesKey; + // HMACKey + private HMACKey hmacKey; + // RSA Key Pair + private KeyPair rsaKeyPair; + // EC Key Pair. + private KeyPair ecKeyPair; + // Temporary array. + public byte[] tmpArray; + // This is used for internal encryption/decryption operations. + private static AEADCipher aesGcmCipher; + // Instance of Signature algorithm used in KDF. + private Signature kdf; + // Flag used to denote the power reset event. + public static byte[] resetFlag; + // Instance of HMAC Signature algorithm. + private Signature hmacSignature; + // For ImportwrappedKey operations. + private KMRsaOAEPEncoding rsaOaepDecipher; + // Instance of pool manager. + private KMPoolManager poolMgr; + // Instance of KMOperationImpl used only to encrypt/decrypt the KeyBlobs. + private KMOperationImpl globalOperation; + // Entropy + private RandomData rng; + // Singleton instance. + private static KMAndroidSEProvider androidSEProvider = null; + + public static KMAndroidSEProvider getInstance() { + return androidSEProvider; + } + + public KMAndroidSEProvider() { + initStatics(); + // Re-usable AES,DES and HMAC keys in persisted memory. + aesKeys = new AESKey[2]; + aesKeys[KEYSIZE_128_OFFSET] = + (AESKey) + KeyBuilder.buildKey( + KeyBuilder.TYPE_AES_TRANSIENT_RESET, KeyBuilder.LENGTH_AES_128, false); + aesKeys[KEYSIZE_256_OFFSET] = + (AESKey) + KeyBuilder.buildKey( + KeyBuilder.TYPE_AES_TRANSIENT_RESET, KeyBuilder.LENGTH_AES_256, false); + triDesKey = + (DESKey) + KeyBuilder.buildKey( + KeyBuilder.TYPE_DES_TRANSIENT_RESET, KeyBuilder.LENGTH_DES3_3KEY, false); + hmacKey = + (HMACKey) KeyBuilder.buildKey(KeyBuilder.TYPE_HMAC_TRANSIENT_RESET, (short) 512, false); + rsaKeyPair = new KeyPair(KeyPair.ALG_RSA, KeyBuilder.LENGTH_RSA_2048); + ecKeyPair = new KeyPair(KeyPair.ALG_EC_FP, KeyBuilder.LENGTH_EC_FP_256); + keyAgreement = KeyAgreement.getInstance(KeyAgreement.ALG_EC_SVDP_DH_PLAIN, false); + poolMgr = KMPoolManager.getInstance(); + poolMgr.initECKey(ecKeyPair); + // RsaOAEP Decipher + rsaOaepDecipher = new KMRsaOAEPEncoding(KMRsaOAEPEncoding.ALG_RSA_PKCS1_OAEP_SHA256_MGF1_SHA1); + + kdf = Signature.getInstance(Signature.ALG_AES_CMAC_128, false); + hmacSignature = Signature.getInstance(Signature.ALG_HMAC_SHA_256, false); + + globalOperation = new KMOperationImpl(); + + // Temporary transient array created to use locally inside functions. + tmpArray = JCSystem.makeTransientByteArray(TMP_ARRAY_SIZE, JCSystem.CLEAR_ON_DESELECT); + Util.arrayFillNonAtomic(tmpArray, (short) 0, TMP_ARRAY_SIZE, (byte) 0); + // Random number generator initialisation. + rng = RandomData.getInstance(RandomData.ALG_KEYGENERATION); + androidSEProvider = this; + resetFlag = JCSystem.makeTransientByteArray((short) 1, JCSystem.CLEAR_ON_RESET); + resetFlag[0] = (byte) POWER_RESET_FALSE; + } + + void initStatics() { + CMAC_KDF_CONSTANT_L = new byte[] {0x00, 0x00, 0x01, 0x00}; + CMAC_KDF_CONSTANT_ZERO = new byte[] {0x00}; + } + + public void clean() { + Util.arrayFillNonAtomic(tmpArray, (short) 0, TMP_ARRAY_SIZE, (byte) 0); + } + + public AESKey createAESKey(short keysize) { + try { + if (keysize > TMP_ARRAY_SIZE) { + KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + } + newRandomNumber(tmpArray, (short) 0, (short) (keysize / 8)); + return createAESKey(tmpArray, (short) 0, (short) (keysize / 8)); + } finally { + clean(); + } + } + + public AESKey createAESKey(byte[] buf, short startOff, short length) { + AESKey key = null; + short keysize = (short) (length * 8); + if (keysize == 128) { + key = (AESKey) aesKeys[KEYSIZE_128_OFFSET]; + key.setKey(buf, (short) startOff); + } else if (keysize == 256) { + key = (AESKey) aesKeys[KEYSIZE_256_OFFSET]; + key.setKey(buf, (short) startOff); + } else { + KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + } + return key; + } + + public DESKey createTDESKey() { + try { + newRandomNumber(tmpArray, (short) 0, (short) (KeyBuilder.LENGTH_DES3_3KEY / 8)); + return createTDESKey(tmpArray, (short) 0, (short) (KeyBuilder.LENGTH_DES3_3KEY / 8)); + } finally { + clean(); + } + } + + public DESKey createTDESKey(byte[] secretBuffer, short secretOff, short secretLength) { + triDesKey.setKey(secretBuffer, secretOff); + return triDesKey; + } + + public HMACKey createHMACKey(short keysize) { + // As per the KeyMint2.0 specification + // The minimum supported HMAC key size is 64 bits + // The maximum supported HMAC key size is 512 bits + // The keysize should be a multiple of 8. + if ((keysize % 8 != 0) || !(keysize >= 64 && keysize <= 512)) { + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + } + try { + newRandomNumber(tmpArray, (short) 0, (short) (keysize / 8)); + return createHMACKey(tmpArray, (short) 0, (short) (keysize / 8)); + } finally { + clean(); + } + } + + public HMACKey createHMACKey(byte[] secretBuffer, short secretOff, short secretLength) { + hmacKey.setKey(secretBuffer, secretOff, secretLength); + return hmacKey; + } + + public KeyPair createRsaKeyPair() { + rsaKeyPair.genKeyPair(); + return rsaKeyPair; + } + + public RSAPrivateKey createRsaKey( + byte[] modBuffer, + short modOff, + short modLength, + byte[] privBuffer, + short privOff, + short privLength) { + RSAPrivateKey privKey = (RSAPrivateKey) rsaKeyPair.getPrivate(); + privKey.setExponent(privBuffer, privOff, privLength); + privKey.setModulus(modBuffer, modOff, modLength); + return privKey; + } + + public KeyPair createECKeyPair() { + ecKeyPair.genKeyPair(); + return ecKeyPair; + } + + public ECPrivateKey createEcKey(byte[] privBuffer, short privOff, short privLength) { + ECPrivateKey privKey = (ECPrivateKey) ecKeyPair.getPrivate(); + privKey.setS(privBuffer, privOff, privLength); + return privKey; + } + + @Override + public short createSymmetricKey(byte alg, short keysize, byte[] buf, short startOff) { + switch (alg) { + case KMType.AES: + AESKey aesKey = createAESKey(keysize); + return aesKey.getKey(buf, startOff); + case KMType.DES: + DESKey desKey = createTDESKey(); + return desKey.getKey(buf, startOff); + case KMType.HMAC: + HMACKey hmacKey = createHMACKey(keysize); + return hmacKey.getKey(buf, startOff); + default: + CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); + break; + } + return 0; + } + + @Override + public void createAsymmetricKey( + byte alg, + byte[] privKeyBuf, + short privKeyStart, + short privKeyLength, + byte[] pubModBuf, + short pubModStart, + short pubModLength, + short[] lengths) { + switch (alg) { + case KMType.RSA: + if (RSA_KEY_SIZE != privKeyLength || RSA_KEY_SIZE != pubModLength) { + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + } + KeyPair rsaKey = createRsaKeyPair(); + RSAPrivateKey privKey = (RSAPrivateKey) rsaKey.getPrivate(); + // Copy exponent. + Util.arrayFillNonAtomic(tmpArray, (short) 0, RSA_KEY_SIZE, (byte) 0); + lengths[0] = privKey.getExponent(tmpArray, (short) 0); + if (lengths[0] > privKeyLength) { + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + } + Util.arrayFillNonAtomic(privKeyBuf, privKeyStart, privKeyLength, (byte) 0); + Util.arrayCopyNonAtomic( + tmpArray, + (short) 0, + privKeyBuf, + (short) (privKeyStart + privKeyLength - lengths[0]), + lengths[0]); + // Copy modulus + Util.arrayFillNonAtomic(tmpArray, (short) 0, RSA_KEY_SIZE, (byte) 0); + lengths[1] = privKey.getModulus(tmpArray, (short) 0); + if (lengths[1] > pubModLength) { + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + } + Util.arrayFillNonAtomic(pubModBuf, pubModStart, pubModLength, (byte) 0); + Util.arrayCopyNonAtomic( + tmpArray, + (short) 0, + pubModBuf, + (short) (pubModStart + pubModLength - lengths[1]), + lengths[1]); + break; + case KMType.EC: + KeyPair ecKey = createECKeyPair(); + ECPublicKey ecPubKey = (ECPublicKey) ecKey.getPublic(); + ECPrivateKey ecPrivKey = (ECPrivateKey) ecKey.getPrivate(); + lengths[0] = ecPrivKey.getS(privKeyBuf, privKeyStart); + lengths[1] = ecPubKey.getW(pubModBuf, pubModStart); + if (lengths[0] > privKeyLength || lengths[1] > pubModLength) { + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + } + break; + default: + CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); + break; + } + } + + @Override + public boolean importSymmetricKey( + byte alg, short keysize, byte[] buf, short startOff, short length) { + switch (alg) { + case KMType.AES: + createAESKey(buf, startOff, length); + break; + case KMType.DES: + createTDESKey(buf, startOff, length); + break; + case KMType.HMAC: + createHMACKey(buf, startOff, length); + break; + default: + CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); + break; + } + return true; + } + + @Override + public boolean importAsymmetricKey( + byte alg, + byte[] privKeyBuf, + short privKeyStart, + short privKeyLength, + byte[] pubModBuf, + short pubModStart, + short pubModLength) { + switch (alg) { + case KMType.RSA: + createRsaKey(pubModBuf, pubModStart, pubModLength, privKeyBuf, privKeyStart, privKeyLength); + break; + case KMType.EC: + createEcKey(privKeyBuf, privKeyStart, privKeyLength); + break; + default: + CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); + break; + } + return true; + } + + @Override + public void getTrueRandomNumber(byte[] buf, short start, short length) { + newRandomNumber(buf, start, length); + } + + @Override + public void newRandomNumber(byte[] num, short startOff, short length) { + rng.nextBytes(num, startOff, length); + } + + @Override + public void addRngEntropy(byte[] num, short offset, short length) { + rng.setSeed(num, offset, length); + } + + public short aesGCMEncrypt( + AESKey key, + byte[] secret, + short secretStart, + short secretLen, + byte[] encSecret, + short encSecretStart, + byte[] nonce, + short nonceStart, + short nonceLen, + byte[] authData, + short authDataStart, + short authDataLen, + byte[] authTag, + short authTagStart, + short authTagLen) { + if (authTagLen != AES_GCM_TAG_LENGTH) { + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + } + if (nonceLen != AES_GCM_NONCE_LENGTH) { + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + } + if (aesGcmCipher == null) { + aesGcmCipher = (AEADCipher) Cipher.getInstance(AEADCipher.ALG_AES_GCM, false); + } + aesGcmCipher.init(key, Cipher.MODE_ENCRYPT, nonce, nonceStart, nonceLen); + if (authDataLen != 0) { + aesGcmCipher.updateAAD(authData, authDataStart, authDataLen); + } + short ciphLen = aesGcmCipher.doFinal(secret, secretStart, secretLen, encSecret, encSecretStart); + aesGcmCipher.retrieveTag(authTag, authTagStart, authTagLen); + return ciphLen; + } + + @Override + public short aesGCMEncrypt( + byte[] aesKey, + short aesKeyStart, + short aesKeyLen, + byte[] secret, + short secretStart, + short secretLen, + byte[] encSecret, + short encSecretStart, + byte[] nonce, + short nonceStart, + short nonceLen, + byte[] authData, + short authDataStart, + short authDataLen, + byte[] authTag, + short authTagStart, + short authTagLen) { + + AESKey key = createAESKey(aesKey, aesKeyStart, aesKeyLen); + return aesGCMEncrypt( + key, + secret, + secretStart, + secretLen, + encSecret, + encSecretStart, + nonce, + nonceStart, + nonceLen, + authData, + authDataStart, + authDataLen, + authTag, + authTagStart, + authTagLen); + } + + @Override + public boolean aesGCMDecrypt( + byte[] aesKey, + short aesKeyStart, + short aesKeyLen, + byte[] encSecret, + short encSecretStart, + short encSecretLen, + byte[] secret, + short secretStart, + byte[] nonce, + short nonceStart, + short nonceLen, + byte[] authData, + short authDataStart, + short authDataLen, + byte[] authTag, + short authTagStart, + short authTagLen) { + if (aesGcmCipher == null) { + aesGcmCipher = (AEADCipher) Cipher.getInstance(AEADCipher.ALG_AES_GCM, false); + } + boolean verification = false; + AESKey key = createAESKey(aesKey, aesKeyStart, aesKeyLen); + aesGcmCipher.init(key, Cipher.MODE_DECRYPT, nonce, nonceStart, nonceLen); + if (authDataLen != 0) { + aesGcmCipher.updateAAD(authData, authDataStart, authDataLen); + } + // encrypt the secret + aesGcmCipher.doFinal(encSecret, encSecretStart, encSecretLen, secret, secretStart); + verification = + aesGcmCipher.verifyTag( + authTag, authTagStart, (short) authTagLen, (short) AES_GCM_TAG_LENGTH); + return verification; + } + + public HMACKey cmacKdf( + KMKey preSharedKey, + byte[] label, + short labelStart, + short labelLen, + byte[] context, + short contextStart, + short contextLength) { + // Note: the variables i and L correspond to i and L in the standard. See page 12 of + // http://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-108.pdf. + try { + // This is hardcoded to requirement - 32 byte output with two concatenated + // 16 bytes K1 and K2. + final byte n = 2; // hardcoded + + // [i] counter - 32 bits + short iBufLen = 4; + short keyOutLen = n * 16; + // Convert Hmackey to AES Key as the algorithm is ALG_AES_CMAC_128. + KMHmacKey hmacKey = ((KMHmacKey) preSharedKey); + hmacKey.hmacKey.getKey(tmpArray, (short) 0); + aesKeys[KEYSIZE_256_OFFSET].setKey(tmpArray, (short) 0); + // Initialize the key derivation function. + kdf.init(aesKeys[KEYSIZE_256_OFFSET], Signature.MODE_SIGN); + // Clear the tmpArray buffer. + Util.arrayFillNonAtomic(tmpArray, (short) 0, (short) 256, (byte) 0); + + Util.arrayFillNonAtomic(tmpArray, (short) 0, iBufLen, (byte) 0); + Util.arrayFillNonAtomic(tmpArray, (short) iBufLen, keyOutLen, (byte) 0); + + byte i = 1; + short pos = 0; + while (i <= n) { + tmpArray[3] = i; + // 4 bytes of iBuf with counter in it + kdf.update(tmpArray, (short) 0, (short) iBufLen); + kdf.update(label, labelStart, (short) labelLen); // label + kdf.update( + CMAC_KDF_CONSTANT_ZERO, + (short) 0, + (short) CMAC_KDF_CONSTANT_ZERO.length); // 1 byte of 0x00 + kdf.update(context, contextStart, contextLength); // context + // 4 bytes of L - signature of 16 bytes + pos = + kdf.sign( + CMAC_KDF_CONSTANT_L, + (short) 0, + (short) CMAC_KDF_CONSTANT_L.length, + tmpArray, + (short) (iBufLen + pos)); + i++; + } + return createHMACKey(tmpArray, (short) iBufLen, (short) keyOutLen); + } finally { + clean(); + } + } + + public short hmacSign( + HMACKey key, byte[] data, short dataStart, short dataLength, byte[] mac, short macStart) { + hmacSignature.init(key, Signature.MODE_SIGN); + return hmacSignature.sign(data, dataStart, dataLength, mac, macStart); + } + + @Override + public short hmacSign( + byte[] keyBuf, + short keyStart, + short keyLength, + byte[] data, + short dataStart, + short dataLength, + byte[] mac, + short macStart) { + HMACKey key = createHMACKey(keyBuf, keyStart, keyLength); + return hmacSign(key, data, dataStart, dataLength, mac, macStart); + } + + @Override + public short hmacSign( + Object key, byte[] data, short dataStart, short dataLength, byte[] mac, short macStart) { + if (!(key instanceof KMHmacKey)) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + KMHmacKey hmacKey = (KMHmacKey) key; + return hmacSign(hmacKey.hmacKey, data, dataStart, dataLength, mac, macStart); + } + + @Override + public short hmacKDF( + KMKey masterkey, + byte[] data, + short dataStart, + short dataLength, + byte[] signature, + short signatureStart) { + try { + KMAESKey aesKey = (KMAESKey) masterkey; + short keyLen = (short) (aesKey.aesKey.getSize() / 8); + aesKey.aesKey.getKey(tmpArray, (short) 0); + return hmacSign( + tmpArray, (short) 0, keyLen, data, dataStart, dataLength, signature, signatureStart); + } finally { + clean(); + } + } + + @Override + public boolean hmacVerify( + KMKey key, + byte[] data, + short dataStart, + short dataLength, + byte[] mac, + short macStart, + short macLength) { + KMHmacKey hmacKey = (KMHmacKey) key; + hmacSignature.init(hmacKey.hmacKey, Signature.MODE_VERIFY); + return hmacSignature.verify(data, dataStart, dataLength, mac, macStart, macLength); + } + + @Override + public short rsaDecipherOAEP256( + byte[] secret, + short secretStart, + short secretLength, + byte[] modBuffer, + short modOff, + short modLength, + byte[] inputDataBuf, + short inputDataStart, + short inputDataLength, + byte[] outputDataBuf, + short outputDataStart) { + RSAPrivateKey key = (RSAPrivateKey) rsaKeyPair.getPrivate(); + key.setExponent(secret, (short) secretStart, (short) secretLength); + key.setModulus(modBuffer, (short) modOff, (short) modLength); + rsaOaepDecipher.init(key, Cipher.MODE_DECRYPT); + return rsaOaepDecipher.doFinal( + inputDataBuf, + (short) inputDataStart, + (short) inputDataLength, + outputDataBuf, + (short) outputDataStart); + } + + private byte mapSignature256Alg(byte alg, byte padding, byte digest) { + switch (alg) { + case KMType.RSA: + switch (padding) { + case KMType.RSA_PKCS1_1_5_SIGN: + { + if (digest == KMType.DIGEST_NONE) { + return KMRsa2048NoDigestSignature.ALG_RSA_PKCS1_NODIGEST; + } else { + return Signature.ALG_RSA_SHA_256_PKCS1; + } + } + case KMType.RSA_PSS: + return Signature.ALG_RSA_SHA_256_PKCS1_PSS; + case KMType.PADDING_NONE: + return KMRsa2048NoDigestSignature.ALG_RSA_SIGN_NOPAD; + } + break; + case KMType.EC: + if (digest == KMType.DIGEST_NONE) { + return KMEcdsa256NoDigestSignature.ALG_ECDSA_NODIGEST; + } else { + return Signature.ALG_ECDSA_SHA_256; + } + case KMType.HMAC: + return Signature.ALG_HMAC_SHA_256; + } + return -1; + } + + private byte mapCipherAlg(byte alg, byte padding, byte blockmode, byte digest) { + switch (alg) { + case KMType.AES: + switch (blockmode) { + case KMType.ECB: + return Cipher.ALG_AES_BLOCK_128_ECB_NOPAD; + case KMType.CBC: + return Cipher.ALG_AES_BLOCK_128_CBC_NOPAD; + case KMType.CTR: + return Cipher.ALG_AES_CTR; + case KMType.GCM: + return AEADCipher.ALG_AES_GCM; + } + break; + case KMType.DES: + switch (blockmode) { + case KMType.ECB: + return Cipher.ALG_DES_ECB_NOPAD; + case KMType.CBC: + return Cipher.ALG_DES_CBC_NOPAD; + } + break; + case KMType.RSA: + switch (padding) { + case KMType.PADDING_NONE: + return Cipher.ALG_RSA_NOPAD; + case KMType.RSA_PKCS1_1_5_ENCRYPT: + return Cipher.ALG_RSA_PKCS1; + case KMType.RSA_OAEP: + { + if (digest == KMType.SHA1) { + /* MGF Digest is SHA1 */ + return KMRsaOAEPEncoding.ALG_RSA_PKCS1_OAEP_SHA256_MGF1_SHA1; + } else if (digest == KMType.SHA2_256) { + /* MGF Digest is SHA256 */ + return KMRsaOAEPEncoding.ALG_RSA_PKCS1_OAEP_SHA256_MGF1_SHA256; + } else { + KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM); + } + } + } + break; + } + return -1; + } + + public KMOperation createSymmetricCipher( + short alg, + short purpose, + short macLength, + short blockMode, + short padding, + byte[] secret, + short secretStart, + short secretLength, + byte[] ivBuffer, + short ivStart, + short ivLength, + boolean isRkp) { + + short cipherAlg = mapCipherAlg((byte) alg, (byte) padding, (byte) blockMode, (byte) 0); + KMOperation operation = null; + if (isRkp) { + operation = poolMgr.getRKpOperation(purpose, cipherAlg, alg, padding, blockMode, macLength); + } else { + operation = + poolMgr.getOperationImpl( + purpose, cipherAlg, alg, padding, blockMode, macLength, secretLength, false); + } + // Get the KeyObject from the operation and update the key with the secret key material. + KMKeyObject keyObj = operation.getKeyObject(); + Key key = (Key) keyObj.keyObjectInst; + switch (secretLength) { + case 32: + case 16: + ((AESKey) key).setKey(secret, secretStart); + break; + case 24: + ((DESKey) key).setKey(secret, secretStart); + break; + default: + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + break; + } + ((KMOperationImpl) operation).init(key, KMType.INVALID_VALUE, ivBuffer, ivStart, ivLength); + return operation; + } + + public KMOperation createHmacSignerVerifier( + short purpose, + short digest, + byte[] secret, + short secretStart, + short secretLength, + boolean isRkp) { + KMOperation operation = null; + if (digest != KMType.SHA2_256) { + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + } + if (isRkp) { + operation = + poolMgr.getRKpOperation( + purpose, + Signature.ALG_HMAC_SHA_256, + KMType.HMAC, + KMType.INVALID_VALUE, + KMType.INVALID_VALUE, + KMType.INVALID_VALUE); + } else { + operation = + poolMgr.getOperationImpl( + purpose, + Signature.ALG_HMAC_SHA_256, + KMType.HMAC, + KMType.INVALID_VALUE, + KMType.INVALID_VALUE, + KMType.INVALID_VALUE, + (short) 0, + false); + } + // Get the KeyObject from the operation and update the key with the secret key material. + KMKeyObject keyObj = operation.getKeyObject(); + HMACKey key = (HMACKey) keyObj.keyObjectInst; + key.setKey(secret, secretStart, secretLength); + ((KMOperationImpl) operation).init(key, digest, null, (short) 0, (short) 0); + return operation; + } + + private KMOperation createHmacSignerVerifier( + short purpose, short digest, HMACKey hmacKey, boolean isTrustedConf) { + if (digest != KMType.SHA2_256) { + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + } + KMOperation operation = + poolMgr.getOperationImpl( + purpose, + Signature.ALG_HMAC_SHA_256, + KMType.HMAC, + KMType.INVALID_VALUE, + KMType.INVALID_VALUE, + KMType.INVALID_VALUE, + (short) 0, + isTrustedConf); + // Get the KeyObject from the operation and update the key with the secret key material. + KMKeyObject keyObj = operation.getKeyObject(); + HMACKey key = (HMACKey) keyObj.keyObjectInst; + short len = hmacKey.getKey(tmpArray, (short) 0); + key.setKey(tmpArray, (short) 0, len); + ((KMOperationImpl) operation).init(key, digest, null, (short) 0, (short) 0); + return operation; + } + + @Override + public KMOperation getRkpOperation( + byte purpose, + byte alg, + byte digest, + byte padding, + byte blockMode, + byte[] keyBuf, + short keyStart, + short keyLength, + byte[] ivBuf, + short ivStart, + short ivLength, + short macLength) { + KMOperation opr = null; + switch (alg) { + case KMType.AES: + // Convert macLength to bytes + macLength = (short) (macLength / 8); + opr = + createSymmetricCipher( + alg, + purpose, + macLength, + blockMode, + padding, + keyBuf, + keyStart, + keyLength, + ivBuf, + ivStart, + ivLength, + true /* isRKP */); + break; + case KMType.HMAC: + opr = + createHmacSignerVerifier( + purpose, digest, keyBuf, keyStart, keyLength, true /* isRKP */); + break; + default: + CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); + break; + } + return opr; + } + + @Override + public KMOperation initSymmetricOperation( + byte purpose, + byte alg, + byte digest, + byte padding, + byte blockMode, + byte[] keyBuf, + short keyStart, + short keyLength, + byte[] ivBuf, + short ivStart, + short ivLength, + short macLength) { + KMOperation opr = null; + switch (alg) { + case KMType.AES: + case KMType.DES: + // Convert macLength to bytes + macLength = (short) (macLength / 8); + opr = + createSymmetricCipher( + alg, + purpose, + macLength, + blockMode, + padding, + keyBuf, + keyStart, + keyLength, + ivBuf, + ivStart, + ivLength, + false /* isRKP */); + break; + case KMType.HMAC: + opr = + createHmacSignerVerifier( + purpose, digest, keyBuf, keyStart, keyLength, false /* isRKP */); + break; + default: + CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); + break; + } + return opr; + } + + @Override + public KMOperation initSymmetricOperation( + byte purpose, + byte alg, + byte digest, + byte padding, + byte blockMode, + Object key, + byte interfaceType, + byte[] ivBuf, + short ivStart, + short ivLength, + short macLength, + boolean oneShot) { + short keyLen = 0; + globalOperation.setPurpose(purpose); + globalOperation.setAlgorithmType(alg); + globalOperation.setPaddingAlgorithm(padding); + globalOperation.setBlockMode(blockMode); + try { + switch (interfaceType) { + case KMDataStoreConstants.INTERFACE_TYPE_MASTER_KEY: + KMAESKey aesKey = (KMAESKey) key; + keyLen = (short) (aesKey.aesKey.getSize() / 8); + aesKey.aesKey.getKey(tmpArray, (short) 0); + break; + + default: + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + + switch (alg) { + case KMType.HMAC: + HMACKey hmackey = createHMACKey(tmpArray, (short) 0, keyLen); + globalOperation.setSignature(hmacSignature); + globalOperation.init(hmackey, digest, null, (short) 0, (short) 0); + break; + + default: + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + } finally { + clean(); + } + return globalOperation; + } + + @Override + public KMOperation initTrustedConfirmationSymmetricOperation(KMKey computedHmacKey) { + KMHmacKey key = (KMHmacKey) computedHmacKey; + return createHmacSignerVerifier(KMType.VERIFY, KMType.SHA2_256, key.hmacKey, true); + } + + public KMOperation createRsaSigner( + short digest, + short padding, + byte[] secret, + short secretStart, + short secretLength, + byte[] modBuffer, + short modOff, + short modLength) { + byte alg = mapSignature256Alg(KMType.RSA, (byte) padding, (byte) digest); + KMOperation operation = + poolMgr.getOperationImpl( + KMType.SIGN, + alg, + KMType.RSA, + padding, + KMType.INVALID_VALUE, + KMType.INVALID_VALUE, + secretLength, + false); + // Get the KeyObject from the operation and update the key with the secret key material. + KMKeyObject keyObj = operation.getKeyObject(); + RSAPrivateKey key = (RSAPrivateKey) ((KeyPair) (keyObj.keyObjectInst)).getPrivate(); + key.setExponent(secret, secretStart, secretLength); + key.setModulus(modBuffer, modOff, modLength); + ((KMOperationImpl) operation).init(key, digest, null, (short) 0, (short) 0); + return operation; + } + + public KMOperation createRsaDecipher( + short padding, + short mgfDigest, + byte[] secret, + short secretStart, + short secretLength, + byte[] modBuffer, + short modOff, + short modLength) { + byte cipherAlg = mapCipherAlg(KMType.RSA, (byte) padding, (byte) 0, (byte) mgfDigest); + KMOperation operation = + poolMgr.getOperationImpl( + KMType.DECRYPT, + cipherAlg, + KMType.RSA, + padding, + KMType.INVALID_VALUE, + KMType.INVALID_VALUE, + secretLength, + false); + // Get the KeyObject from the operation and update the key with the secret key material. + KMKeyObject keyObj = operation.getKeyObject(); + RSAPrivateKey key = (RSAPrivateKey) ((KeyPair) (keyObj.keyObjectInst)).getPrivate(); + key.setExponent(secret, secretStart, secretLength); + key.setModulus(modBuffer, modOff, modLength); + ((KMOperationImpl) operation).init(key, KMType.INVALID_VALUE, null, (short) 0, (short) 0); + return operation; + } + + public KMOperation createEcSigner( + short digest, byte[] secret, short secretStart, short secretLength) { + byte alg = mapSignature256Alg(KMType.EC, (byte) 0, (byte) digest); + KMOperation operation = + poolMgr.getOperationImpl( + KMType.SIGN, + alg, + KMType.EC, + KMType.INVALID_VALUE, + KMType.INVALID_VALUE, + KMType.INVALID_VALUE, + secretLength, + false); + KMKeyObject keyObj = operation.getKeyObject(); + ECPrivateKey key = (ECPrivateKey) ((KeyPair) (keyObj.keyObjectInst)).getPrivate(); + key.setS(secret, secretStart, secretLength); + ((KMOperationImpl) operation).init(key, digest, null, (short) 0, (short) 0); + return operation; + } + + public KMOperation createKeyAgreement(byte[] secret, short secretStart, short secretLength) { + KMOperation operation = + poolMgr.getOperationImpl( + KMType.AGREE_KEY, + KeyAgreement.ALG_EC_SVDP_DH_PLAIN, + KMType.EC, + KMType.INVALID_VALUE, + KMType.INVALID_VALUE, + KMType.INVALID_VALUE, + (short) 0, + false); + KMKeyObject keyObj = operation.getKeyObject(); + ECPrivateKey key = (ECPrivateKey) ((KeyPair) (keyObj.keyObjectInst)).getPrivate(); + key.setS(secret, secretStart, secretLength); + ((KMOperationImpl) operation).init(key, KMType.INVALID_VALUE, null, (short) 0, (short) 0); + return operation; + } + + @Override + public KMOperation initAsymmetricOperation( + byte purpose, + byte alg, + byte padding, + byte digest, + byte mgfDigest, + byte[] privKeyBuf, + short privKeyStart, + short privKeyLength, + byte[] pubModBuf, + short pubModStart, + short pubModLength) { + KMOperation opr = null; + if (alg == KMType.RSA) { + switch (purpose) { + case KMType.SIGN: + opr = + createRsaSigner( + digest, + padding, + privKeyBuf, + privKeyStart, + privKeyLength, + pubModBuf, + pubModStart, + pubModLength); + break; + case KMType.DECRYPT: + opr = + createRsaDecipher( + padding, + mgfDigest, + privKeyBuf, + privKeyStart, + privKeyLength, + pubModBuf, + pubModStart, + pubModLength); + break; + default: + KMException.throwIt(KMError.UNSUPPORTED_PURPOSE); + break; + } + } else if (alg == KMType.EC) { + switch (purpose) { + case KMType.SIGN: + opr = createEcSigner(digest, privKeyBuf, privKeyStart, privKeyLength); + break; + + case KMType.AGREE_KEY: + opr = createKeyAgreement(privKeyBuf, privKeyStart, privKeyLength); + break; + default: + KMException.throwIt(KMError.UNSUPPORTED_PURPOSE); + break; + } + } else { + CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); + } + return opr; + } + + @Override + public short cmacKDF( + KMKey pSharedKey, + byte[] label, + short labelStart, + short labelLen, + byte[] context, + short contextStart, + short contextLength, + byte[] keyBuf, + short keyStart) { + HMACKey key = + cmacKdf(pSharedKey, label, labelStart, labelLen, context, contextStart, contextLength); + return key.getKey(keyBuf, keyStart); + } + + @Override + public boolean isUpgrading() { + return UpgradeManager.isUpgrading(); + } + + @Override + public KMKey createMasterKey(KMKey masterKey, short keySizeBits) { + try { + if (masterKey == null) { + AESKey key = (AESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_AES, keySizeBits, false); + masterKey = new KMAESKey(key); + } + short keyLen = (short) (keySizeBits / 8); + Util.arrayFillNonAtomic(tmpArray, (short) 0, keyLen, (byte) 0); + getTrueRandomNumber(tmpArray, (short) 0, keyLen); + ((KMAESKey) masterKey).aesKey.setKey(tmpArray, (short) 0); + return (KMKey) masterKey; + } finally { + clean(); + } + } + + @Override + public KMKey createPreSharedKey(KMKey preSharedKey, byte[] keyData, short offset, short length) { + short lengthInBits = (short) (length * 8); + if ((lengthInBits % 8 != 0) || !(lengthInBits >= 64 && lengthInBits <= 512)) { + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + } + if (preSharedKey == null) { + HMACKey key = (HMACKey) KeyBuilder.buildKey(KeyBuilder.TYPE_HMAC, lengthInBits, false); + preSharedKey = new KMHmacKey(key); + } + ((KMHmacKey) preSharedKey).hmacKey.setKey(keyData, offset, length); + return (KMKey) preSharedKey; + } + + @Override + public KMKey createComputedHmacKey( + KMKey computedHmacKey, byte[] keyData, short offset, short length) { + if (length != COMPUTED_HMAC_KEY_SIZE) { + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + } + if (computedHmacKey == null) { + HMACKey key = + (HMACKey) KeyBuilder.buildKey(KeyBuilder.TYPE_HMAC, (short) (length * 8), false); + computedHmacKey = new KMHmacKey(key); + } + ((KMHmacKey) computedHmacKey).hmacKey.setKey(keyData, offset, length); + return (KMKey) computedHmacKey; + } + + @Override + public short ecSign256( + byte[] secret, + short secretStart, + short secretLength, + byte[] inputDataBuf, + short inputDataStart, + short inputDataLength, + byte[] outputDataBuf, + short outputDataStart) { + + ECPrivateKey key = (ECPrivateKey) ecKeyPair.getPrivate(); + key.setS(secret, secretStart, secretLength); + + Signature.OneShot signer = null; + try { + + signer = + Signature.OneShot.open( + MessageDigest.ALG_SHA_256, Signature.SIG_CIPHER_ECDSA, Cipher.PAD_NULL); + signer.init(key, Signature.MODE_SIGN); + return signer.sign( + inputDataBuf, inputDataStart, inputDataLength, outputDataBuf, outputDataStart); + } finally { + if (signer != null) { + signer.close(); + } + } + } + + @Override + public short rsaSign256Pkcs1( + byte[] secret, + short secretStart, + short secretLength, + byte[] modBuf, + short modStart, + short modLength, + byte[] inputDataBuf, + short inputDataStart, + short inputDataLength, + byte[] outputDataBuf, + short outputDataStart) { + + Signature.OneShot signer = null; + try { + + signer = + Signature.OneShot.open( + MessageDigest.ALG_SHA_256, Signature.SIG_CIPHER_RSA, Cipher.PAD_PKCS1); + + RSAPrivateKey key = (RSAPrivateKey) rsaKeyPair.getPrivate(); + ; + key.setExponent(secret, secretStart, secretLength); + key.setModulus(modBuf, modStart, modLength); + + signer.init(key, Signature.MODE_SIGN); + return signer.sign( + inputDataBuf, inputDataStart, inputDataLength, outputDataBuf, outputDataStart); + } finally { + if (signer != null) { + signer.close(); + } + } + } + + @Override + public boolean isAttestationKeyProvisioned() { + return false; + } + + @Override + public short getAttestationKeyAlgorithm() { + return KMType.INVALID_VALUE; + } + + @Override + public short hkdf( + byte[] ikm, + short ikmOff, + short ikmLen, + byte[] salt, + short saltOff, + short saltLen, + byte[] info, + short infoOff, + short infoLen, + byte[] out, + short outOff, + short outLen) { + // HMAC_extract + hkdfExtract(ikm, ikmOff, ikmLen, salt, saltOff, saltLen, tmpArray, (short) 0); + // HMAC_expand + return hkdfExpand(tmpArray, (short) 0, (short) 32, info, infoOff, infoLen, out, outOff, outLen); + } + + private short hkdfExtract( + byte[] ikm, + short ikmOff, + short ikmLen, + byte[] salt, + short saltOff, + short saltLen, + byte[] out, + short off) { + // https://tools.ietf.org/html/rfc5869#section-2.2 + HMACKey hmacKey = createHMACKey(salt, saltOff, saltLen); + hmacSignature.init(hmacKey, Signature.MODE_SIGN); + return hmacSignature.sign(ikm, ikmOff, ikmLen, out, off); + } + + private short hkdfExpand( + byte[] prk, + short prkOff, + short prkLen, + byte[] info, + short infoOff, + short infoLen, + byte[] out, + short outOff, + short outLen) { + // https://tools.ietf.org/html/rfc5869#section-2.3 + short digestLen = (short) 32; // SHA256 digest length. + // Calculate no of iterations N. + short n = (short) ((short) (outLen + digestLen - 1) / digestLen); + if (n > 255) { + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + } + HMACKey hmacKey = createHMACKey(prk, prkOff, prkLen); + Util.arrayFill(tmpArray, (short) 0, (short) 33, (byte) 0); + short bytesCopied = 0; + short len = 0; + for (short i = 0; i < n; i++) { + tmpArray[0]++; + hmacSignature.init(hmacKey, Signature.MODE_SIGN); + if (i != 0) { + hmacSignature.update(tmpArray, (short) 1, (short) 32); + } + hmacSignature.update(info, infoOff, infoLen); + len = hmacSignature.sign(tmpArray, (short) 0, (short) 1, tmpArray, (short) 1); + if ((short) (bytesCopied + len) > outLen) { + len = (short) (outLen - bytesCopied); + } + Util.arrayCopyNonAtomic(tmpArray, (short) 1, out, (short) (outOff + bytesCopied), len); + bytesCopied += len; + } + return outLen; + } + + @Override + public short ecdhKeyAgreement( + byte[] privKey, + short privKeyOff, + short privKeyLen, + byte[] publicKey, + short publicKeyOff, + short publicKeyLen, + byte[] secret, + short secretOff) { + keyAgreement.init(createEcKey(privKey, privKeyOff, privKeyLen)); + return keyAgreement.generateSecret(publicKey, publicKeyOff, publicKeyLen, secret, secretOff); + } + + @Override + public boolean ecVerify256( + byte[] pubKey, + short pubKeyOffset, + short pubKeyLen, + byte[] inputDataBuf, + short inputDataStart, + short inputDataLength, + byte[] signatureDataBuf, + short signatureDataStart, + short signatureDataLen) { + Signature.OneShot signer = null; + try { + signer = + Signature.OneShot.open( + MessageDigest.ALG_SHA_256, Signature.SIG_CIPHER_ECDSA, Cipher.PAD_NULL); + ECPublicKey key = (ECPublicKey) ecKeyPair.getPublic(); + key.setW(pubKey, pubKeyOffset, pubKeyLen); + signer.init(key, Signature.MODE_VERIFY); + return signer.verify( + inputDataBuf, + inputDataStart, + inputDataLength, + signatureDataBuf, + signatureDataStart, + (short) (signatureDataBuf[(short) (signatureDataStart + 1)] + 2)); + } finally { + if (signer != null) { + signer.close(); + } + } + } + + @Override + public short signWithDeviceUniqueKey( + KMKey ecPrivKey, + byte[] inputDataBuf, + short inputDataStart, + short inputDataLength, + byte[] outputDataBuf, + short outputDataStart) { + Signature.OneShot signer = null; + try { + signer = + Signature.OneShot.open( + MessageDigest.ALG_SHA_256, Signature.SIG_CIPHER_ECDSA, Cipher.PAD_NULL); + signer.init( + ((KMECDeviceUniqueKeyPair) ecPrivKey).ecKeyPair.getPrivate(), Signature.MODE_SIGN); + return signer.sign( + inputDataBuf, inputDataStart, inputDataLength, outputDataBuf, outputDataStart); + } finally { + if (signer != null) { + signer.close(); + } + } + } + + @Override + public KMKey createRkpDeviceUniqueKeyPair( + KMKey key, + byte[] pubKey, + short pubKeyOff, + short pubKeyLen, + byte[] privKey, + short privKeyOff, + short privKeyLen) { + if (key == null) { + KeyPair ecKeyPair = new KeyPair(KeyPair.ALG_EC_FP, KeyBuilder.LENGTH_EC_FP_256); + poolMgr.initECKey(ecKeyPair); + key = new KMECDeviceUniqueKeyPair(ecKeyPair); + } + ECPrivateKey ecKeyPair = (ECPrivateKey) ((KMECDeviceUniqueKeyPair) key).ecKeyPair.getPrivate(); + ECPublicKey ecPublicKey = (ECPublicKey) ((KMECDeviceUniqueKeyPair) key).ecKeyPair.getPublic(); + ecKeyPair.setS(privKey, privKeyOff, privKeyLen); + ecPublicKey.setW(pubKey, pubKeyOff, pubKeyLen); + return (KMKey) key; + } + + @Override + public KMKey createRkpMacKey(KMKey rkpMacKey, byte[] keyData, short offset, short length) { + if (rkpMacKey == null) { + HMACKey key = + (HMACKey) KeyBuilder.buildKey(KeyBuilder.TYPE_HMAC, (short) (length * 8), false); + rkpMacKey = new KMHmacKey(key); + } + ((KMHmacKey) rkpMacKey).hmacKey.setKey(keyData, offset, length); + return rkpMacKey; + } + + @Override + public short messageDigest256( + byte[] inBuff, short inOffset, short inLength, byte[] outBuff, short outOffset) { + MessageDigest.OneShot mDigest = null; + short len = 0; + try { + mDigest = MessageDigest.OneShot.open(MessageDigest.ALG_SHA_256); + len = mDigest.doFinal(inBuff, inOffset, inLength, outBuff, outOffset); + } finally { + if (mDigest != null) { + mDigest.close(); + mDigest = null; + } + } + return len; + } + + public boolean isPowerReset() { + boolean flag = false; + if (resetFlag[0] == POWER_RESET_TRUE) { + resetFlag[0] = POWER_RESET_FALSE; + flag = true; + if (poolMgr != null) { + poolMgr.powerReset(); + } + } + return flag; + } + + @Override + public void onSave(Element element, byte interfaceType, Object object) { + element.write(interfaceType); + if (object == null) { + element.write(null); + return; + } + switch (interfaceType) { + case KMDataStoreConstants.INTERFACE_TYPE_MASTER_KEY: + KMAESKey.onSave(element, (KMAESKey) object); + break; + case KMDataStoreConstants.INTERFACE_TYPE_PRE_SHARED_KEY: + KMHmacKey.onSave(element, (KMHmacKey) object); + break; + case KMDataStoreConstants.INTERFACE_TYPE_DEVICE_UNIQUE_KEY_PAIR: + KMECDeviceUniqueKeyPair.onSave(element, (KMECDeviceUniqueKeyPair) object); + break; + case KMDataStoreConstants.INTERFACE_TYPE_RKP_MAC_KEY: + KMHmacKey.onSave(element, (KMHmacKey) object); + break; + default: + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + } + + @Override + public Object onRestore(Element element) { + if (element == null) { + return null; + } + byte interfaceType = element.readByte(); + switch (interfaceType) { + case KMDataStoreConstants.INTERFACE_TYPE_COMPUTED_HMAC_KEY: + return KMHmacKey.onRestore((HMACKey) element.readObject()); + case KMDataStoreConstants.INTERFACE_TYPE_MASTER_KEY: + return KMAESKey.onRestore((AESKey) element.readObject()); + case KMDataStoreConstants.INTERFACE_TYPE_PRE_SHARED_KEY: + return KMHmacKey.onRestore((HMACKey) element.readObject()); + case KMDataStoreConstants.INTERFACE_TYPE_DEVICE_UNIQUE_KEY_PAIR: + return KMECDeviceUniqueKeyPair.onRestore((KeyPair) element.readObject()); + case KMDataStoreConstants.INTERFACE_TYPE_RKP_MAC_KEY: + return KMHmacKey.onRestore((HMACKey) element.readObject()); + default: + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + return null; + } + + @Override + public short getBackupPrimitiveByteCount(byte interfaceType) { + short primitiveCount = 1; // interface type + switch (interfaceType) { + case KMDataStoreConstants.INTERFACE_TYPE_MASTER_KEY: + primitiveCount += KMAESKey.getBackupPrimitiveByteCount(); + break; + case KMDataStoreConstants.INTERFACE_TYPE_PRE_SHARED_KEY: + primitiveCount += KMHmacKey.getBackupPrimitiveByteCount(); + break; + case KMDataStoreConstants.INTERFACE_TYPE_DEVICE_UNIQUE_KEY_PAIR: + primitiveCount += KMECDeviceUniqueKeyPair.getBackupPrimitiveByteCount(); + break; + case KMDataStoreConstants.INTERFACE_TYPE_RKP_MAC_KEY: + primitiveCount += KMHmacKey.getBackupPrimitiveByteCount(); + break; + default: + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + return primitiveCount; + } + + @Override + public short getBackupObjectCount(byte interfaceType) { + switch (interfaceType) { + case KMDataStoreConstants.INTERFACE_TYPE_MASTER_KEY: + return KMAESKey.getBackupObjectCount(); + case KMDataStoreConstants.INTERFACE_TYPE_PRE_SHARED_KEY: + return KMHmacKey.getBackupObjectCount(); + case KMDataStoreConstants.INTERFACE_TYPE_DEVICE_UNIQUE_KEY_PAIR: + return KMECDeviceUniqueKeyPair.getBackupObjectCount(); + case KMDataStoreConstants.INTERFACE_TYPE_RKP_MAC_KEY: + return KMHmacKey.getBackupObjectCount(); + default: + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + return 0; + } + + @Override + public boolean isBootSignalEventSupported() { + return false; + } + + @Override + public boolean isDeviceRebooted() { + return false; + } + + @Override + public void clearDeviceBooted(boolean resetBootFlag) { + // To be filled + } +} diff --git a/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAttestationCert.java b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAttestationCert.java new file mode 100644 index 0000000..05801b0 --- /dev/null +++ b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAttestationCert.java @@ -0,0 +1,198 @@ +/* + * Copyright(C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.javacard.seprovider; + +/** + * The KMAttestationCert interface represents a X509 compliant attestation certificate required to + * support keymaster's attestKey function. This cert will be created according to the specifications + * given in android keymaster hal documentation. KMSeProvider has to provide the instance of this + * certificate. This interface is designed based on builder pattern and hence each method returns + * instance of cert. + */ +public interface KMAttestationCert { + + /** + * Set verified boot hash. + * + * @param obj This is a KMByteBlob containing hash + * @return instance of KMAttestationCert + */ + KMAttestationCert verifiedBootHash(short obj); + + /** + * Set verified boot key received during booting up. + * + * @param obj This is a KMByteBlob containing verified boot key. + * @return instance of KMAttestationCert + */ + KMAttestationCert verifiedBootKey(short obj); + + /** + * Set verified boot state received during booting up. + * + * @param val This is a byte containing verified boot state value. + * @return instance of KMAttestationCert + */ + KMAttestationCert verifiedBootState(byte val); + + /** + * Set uniqueId received from CA certificate during provisioning. + * + * @param scratchpad Buffer to store intermediate results. + * @param scratchPadOff Start offset of the scratchpad buffer. + * @param creationTime This buffer contains the CREATION_TIME value. + * @param creationTimeOff Start offset of creattionTime buffer. + * @param creationTimeLen Length of the creationTime buffer. + * @param attestAppId This buffer contains the ATTESTATION_APPLICATION_ID value. + * @param attestAppIdOff Start offset of the attestAppId buffer. + * @param attestAppIdLen Length of the attestAppId buffer. + * @param resetSinceIdRotation This holds the information of RESET_SINCE_ID_ROTATION. + * @param masterKey + * @return instance of KMAttestationCert. + */ + KMAttestationCert makeUniqueId( + byte[] scratchpad, + short scratchPadOff, + byte[] creationTime, + short creationTimeOff, + short creationTimeLen, + byte[] attestAppId, + short attestAppIdOff, + short attestAppIdLen, + byte resetSinceIdRotation, + KMKey masterKey); + + /** + * Set start time received from creation/activation time tag. Used for certificate's valid period. + * + * @param obj This is a KMByteBlob object containing start time. + * @param scratchpad Buffer to store intermediate results. + * @return instance of KMAttestationCert. + */ + KMAttestationCert notBefore(short obj, boolean derEncoded, byte[] scratchpad); + + /** + * Set expiry time received from expiry time tag or ca certificates expiry time. Used for + * certificate's valid period. + * + * @param usageExpiryTimeObj This is a KMByteBlob containing expiry time. certificate. + * @param scratchPad Buffer to store intermediate results. + * @return instance of KMAttestationCert + */ + KMAttestationCert notAfter(short usageExpiryTimeObj, boolean derEncoded, byte[] scratchPad); + + /** + * Set device lock status received during booting time or due to device lock command. + * + * @param val This is true if device is locked. + * @return instance of KMAttestationCert + */ + KMAttestationCert deviceLocked(boolean val); + + /** + * Set public key to be attested received from attestKey command. + * + * @param obj This is KMByteBlob containing the public key. + * @return instance of KMAttestationCert + */ + KMAttestationCert publicKey(short obj); + + /** + * Set attestation challenge received from attestKey command. + * + * @param obj This is KMByteBlob containing the attestation challenge. + * @return instance of KMAttestationCert + */ + KMAttestationCert attestationChallenge(short obj); + + /** + * Set extension tag received from key characteristics which needs to be added to android + * extension. This method will called once for each tag. + * + * @param tag is the KMByteBlob containing KMTag. + * @param hwEnforced is true if the tag has to be added to hw enforced list or else added to sw + * enforced list. + * @return instance of KMAttestationCert + */ + KMAttestationCert extensionTag(short tag, boolean hwEnforced); + + /** + * Set ASN.1 encoded X509 issuer field received from attestation key CA cert. + * + * @param obj This is KMByteBlob containing the issuer. + * @return instance of KMAttestationCert + */ + KMAttestationCert issuer(short obj); + + /** + * Set byte buffer to be used to generate certificate. + * + * @param buf This is byte[] buffer. + * @param bufStart This is short start offset. + * @param maxLen This is short length of the buffer. + * @return instance of KMAttestationCert + */ + KMAttestationCert buffer(byte[] buf, short bufStart, short maxLen); + + /** + * Get the start of the certificate + * + * @return start of the attestation cert. + */ + short getCertStart(); + + /** + * Get the length of the certificate + * + * @return length of the attestation cert. + */ + short getCertLength(); + + /** + * Build a fake signed certificate. After this method executes the certificate is ready with the + * signature equal to 1 byte which is 0 and with rsa signature algorithm. + */ + void build(); + + /** + * Set the Serial number in the certificate. If no serial number is set then serial number is 1. + * + * @param serialNumber + */ + boolean serialNumber(short serialNumber); + + /** + * Set the Subject Name in the certificate. + * + * @param subject + */ + boolean subjectName(short subject); + + /** + * Set attestation key and mode. + * + * @param attestKey KMByteBlob of the key + * @param mode + */ + KMAttestationCert ecAttestKey(short attestKey, byte mode); + /** + * Set attestation key and mode. + * + * @param attestKey KMByteBlob of the key + * @param mode + */ + KMAttestationCert rsaAttestKey(short attestPrivExp, short attestMod, byte mode); +} diff --git a/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMDataStoreConstants.java b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMDataStoreConstants.java new file mode 100644 index 0000000..61ddb36 --- /dev/null +++ b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMDataStoreConstants.java @@ -0,0 +1,17 @@ +package com.android.javacard.seprovider; + +/** + * This class holds different interface type constants to differentiate between the instances of + * Computed Hmac key, device unique key pair, RKP Mac key, and master key when passed as generic + * objects. These constants are used in upgrade flow to retrieve the size of the object and + * primitive types saved and restored for respective key types. + */ +public class KMDataStoreConstants { + // INTERFACE Types + public static final byte INTERFACE_TYPE_COMPUTED_HMAC_KEY = 0x01; + // 0x02 reserved for INTERFACE_TYPE_ATTESTATION_KEY + public static final byte INTERFACE_TYPE_DEVICE_UNIQUE_KEY_PAIR = 0x03; + public static final byte INTERFACE_TYPE_MASTER_KEY = 0x04; + public static final byte INTERFACE_TYPE_PRE_SHARED_KEY = 0x05; + public static final byte INTERFACE_TYPE_RKP_MAC_KEY = 0x06; +} diff --git a/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMECDeviceUniqueKeyPair.java b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMECDeviceUniqueKeyPair.java new file mode 100644 index 0000000..0e430a3 --- /dev/null +++ b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMECDeviceUniqueKeyPair.java @@ -0,0 +1,55 @@ +/* + * Copyright(C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" (short)0IS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.javacard.seprovider; + +import javacard.security.ECPublicKey; +import javacard.security.KeyPair; +import org.globalplatform.upgrade.Element; + +/** This is a wrapper class for KeyPair. */ +public class KMECDeviceUniqueKeyPair implements KMKey { + + public KeyPair ecKeyPair; + + @Override + public short getPublicKey(byte[] buf, short offset) { + ECPublicKey publicKey = (ECPublicKey) ecKeyPair.getPublic(); + return publicKey.getW(buf, offset); + } + + public KMECDeviceUniqueKeyPair(KeyPair ecPair) { + ecKeyPair = ecPair; + } + + public static void onSave(Element element, KMECDeviceUniqueKeyPair kmKey) { + element.write(kmKey.ecKeyPair); + } + + public static KMECDeviceUniqueKeyPair onRestore(KeyPair ecKey) { + if (ecKey == null) { + return null; + } + return new KMECDeviceUniqueKeyPair(ecKey); + } + + public static short getBackupPrimitiveByteCount() { + return (short) 0; + } + + public static short getBackupObjectCount() { + return (short) 1; + } +} diff --git a/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMEcdsa256NoDigestSignature.java b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMEcdsa256NoDigestSignature.java new file mode 100644 index 0000000..83774ab --- /dev/null +++ b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMEcdsa256NoDigestSignature.java @@ -0,0 +1,138 @@ +/* + * Copyright(C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" (short)0IS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.javacard.seprovider; + +import javacard.framework.Util; +import javacard.security.CryptoException; +import javacard.security.Key; +import javacard.security.MessageDigest; +import javacard.security.Signature; +import javacardx.crypto.Cipher; + +/** + * This class provides support for ECDSA_NO_DIGEST signature algorithm. Added this because javacard + * 3.0.5 does not support this + */ +public class KMEcdsa256NoDigestSignature extends Signature { + + public static final byte ALG_ECDSA_NODIGEST = (byte) 0x67; + public static final byte MAX_NO_DIGEST_MSG_LEN = 32; + private byte algorithm; + private Signature inst; + + public KMEcdsa256NoDigestSignature(byte alg) { + algorithm = alg; + // There is no constant for no digest so ALG_ECDSA_SHA_256 is used. However, + // signPreComputedHash is used for signing which is equivalent to no digest sign. + inst = Signature.getInstance(Signature.ALG_ECDSA_SHA_256, false); + } + + @Override + public void init(Key key, byte b) throws CryptoException { + inst.init(key, b); + } + + @Override + public void init(Key key, byte b, byte[] bytes, short i, short i1) throws CryptoException { + inst.init(key, b, bytes, i, i1); + } + + @Override + public void setInitialDigest(byte[] bytes, short i, short i1, byte[] bytes1, short i2, short i3) + throws CryptoException {} + + @Override + public byte getAlgorithm() { + return algorithm; + } + + @Override + public byte getMessageDigestAlgorithm() { + return MessageDigest.ALG_NULL; + } + + @Override + public byte getCipherAlgorithm() { + return 0; + } + + @Override + public byte getPaddingAlgorithm() { + return Cipher.PAD_NULL; + } + + @Override + public short getLength() throws CryptoException { + return inst.getLength(); + } + + @Override + public void update(byte[] message, short msgStart, short messageLength) throws CryptoException { + // HAL accumulates the data and send it at finish operation. + } + + @Override + public short sign(byte[] bytes, short i, short i1, byte[] bytes1, short i2) + throws CryptoException { + try { + if (i1 > MAX_NO_DIGEST_MSG_LEN) { + CryptoException.throwIt(CryptoException.ILLEGAL_USE); + } + // add zeros to the left + if (i1 < MAX_NO_DIGEST_MSG_LEN) { + Util.arrayFillNonAtomic( + KMAndroidSEProvider.getInstance().tmpArray, + (short) 0, + (short) MAX_NO_DIGEST_MSG_LEN, + (byte) 0); + } + Util.arrayCopyNonAtomic( + bytes, + i, + KMAndroidSEProvider.getInstance().tmpArray, + (short) (MAX_NO_DIGEST_MSG_LEN - i1), + i1); + return inst.signPreComputedHash( + KMAndroidSEProvider.getInstance().tmpArray, + (short) 0, + (short) MAX_NO_DIGEST_MSG_LEN, + bytes1, + i2); + } finally { + KMAndroidSEProvider.getInstance().clean(); + } + } + + @Override + public short signPreComputedHash(byte[] bytes, short i, short i1, byte[] bytes1, short i2) + throws CryptoException { + return inst.sign(bytes, i, i1, bytes1, i2); + } + + @Override + public boolean verify(byte[] bytes, short i, short i1, byte[] bytes1, short i2, short i3) + throws CryptoException { + // Verification is handled inside HAL + return false; + } + + @Override + public boolean verifyPreComputedHash( + byte[] bytes, short i, short i1, byte[] bytes1, short i2, short i3) throws CryptoException { + // Verification is handled inside HAL + return false; + } +} diff --git a/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMError.java b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMError.java new file mode 100644 index 0000000..69cb069 --- /dev/null +++ b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMError.java @@ -0,0 +1,32 @@ +/* + * Copyright(C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.javacard.seprovider; + +/** + * KMError includes all the error codes from android keymaster hal specifications. The values are + * positive unlike negative values in keymaster hal. + */ +public class KMError { + + public static final short OK = 0; + public static final short UNSUPPORTED_PURPOSE = 2; + public static final short UNSUPPORTED_ALGORITHM = 4; + public static final short INVALID_INPUT_LENGTH = 21; + public static final short VERIFICATION_FAILED = 30; + public static final short TOO_MANY_OPERATIONS = 31; + public static final short INVALID_ARGUMENT = 38; + public static final short UNKNOWN_ERROR = 1000; +} diff --git a/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMException.java b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMException.java new file mode 100644 index 0000000..79983a2 --- /dev/null +++ b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMException.java @@ -0,0 +1,46 @@ +/* + * Copyright(C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.javacard.seprovider; + +import javacard.framework.JCSystem; + +/** + * KMException is shared instance of exception used for all exceptions in the applet. It is used to + * throw EMError errors. + */ +public class KMException extends RuntimeException { + + private static short[] reason; + private static KMException exception; + + private KMException() {} + + public static short reason() { + return reason[0]; + } + + public static void throwIt(short e) { + if (reason == null) { + reason = JCSystem.makeTransientShortArray((short) 1, JCSystem.CLEAR_ON_DESELECT); + } + if (exception == null) { + exception = new KMException(); + } + reason[0] = e; + throw exception; + } +} diff --git a/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMHmacKey.java b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMHmacKey.java new file mode 100644 index 0000000..e938a2b --- /dev/null +++ b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMHmacKey.java @@ -0,0 +1,53 @@ +/* + * Copyright(C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" (short)0IS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.javacard.seprovider; + +import javacard.security.HMACKey; +import org.globalplatform.upgrade.Element; + +/** This is a wrapper class for HMACKey. */ +public class KMHmacKey implements KMKey { + + public HMACKey hmacKey; + + public KMHmacKey(HMACKey key) { + hmacKey = key; + } + + public static void onSave(Element element, KMHmacKey kmKey) { + element.write(kmKey.hmacKey); + } + + public static KMHmacKey onRestore(HMACKey hmacKey) { + if (hmacKey == null) { + return null; + } + return new KMHmacKey(hmacKey); + } + + public static short getBackupPrimitiveByteCount() { + return (short) 0; + } + + public static short getBackupObjectCount() { + return (short) 1; + } + + @Override + public short getPublicKey(byte[] buf, short offset) { + return (short) 0; + } +} diff --git a/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMKey.java b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMKey.java new file mode 100644 index 0000000..9894382 --- /dev/null +++ b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMKey.java @@ -0,0 +1,10 @@ +package com.android.javacard.seprovider; + +/** + * This interface helps to decouple Javacard internal key objects from the keymaster package. Using + * Javacard key objects provides security by providing protection against side channel attacks. + * KMAESKey, KMECDeviceUniqueKey and KMHmacKey implements this interface. + */ +public interface KMKey { + short getPublicKey(byte[] buf, short offset); +} diff --git a/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMKeyObject.java b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMKeyObject.java new file mode 100644 index 0000000..a37da08 --- /dev/null +++ b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMKeyObject.java @@ -0,0 +1,10 @@ +package com.android.javacard.seprovider; + +/** + * This class holds the KeyObject and its associated algorithm value. Each KMKeyObject is tied to + * one of the crypto operations. + */ +public class KMKeyObject { + public byte algorithm; + public Object keyObjectInst; +} diff --git a/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMOperation.java b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMOperation.java new file mode 100644 index 0000000..12e691e --- /dev/null +++ b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMOperation.java @@ -0,0 +1,75 @@ +/* + * Copyright(C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.javacard.seprovider; + +/** + * KMOperation represents a persistent operation started by keymaster hal's beginOperation function. + * This operation is persistent i.e. it will be stored in non volatile memory of se card. It will be + * returned back to KMSEProvider for the reuse when the operation is finished. + */ +public interface KMOperation { + + // Used for cipher operations + short update( + byte[] inputDataBuf, + short inputDataStart, + short inputDataLength, + byte[] outputDataBuf, + short outputDataStart); + + // Used for signature operations + short update(byte[] inputDataBuf, short inputDataStart, short inputDataLength); + + // Used for finishing cipher operations or ecdh keyAgreement. + short finish( + byte[] inputDataBuf, + short inputDataStart, + short inputDataLength, + byte[] outputDataBuf, + short outputDataStart); + + // Used for finishing signing operations. + short sign( + byte[] inputDataBuf, + short inputDataStart, + short inputDataLength, + byte[] signBuf, + short signStart); + + // Used for finishing verifying operations. + boolean verify( + byte[] inputDataBuf, + short inputDataStart, + short inputDataLength, + byte[] signBuf, + short signStart, + short signLength); + + // Used for aborting the ongoing operations. + void abort(); + + // Used for AES GCM cipher operation. + void updateAAD(byte[] dataBuf, short dataStart, short dataLength); + + // Used for getting output size before finishing a AES GCM cipher operation. For encryption this + // will + // include the auth tag which is appended at the end of the encrypted data. For decryption this + // will be + // size of the decrypted data only. + short getAESGCMOutputSize(short dataSize, short macLength); + + KMKeyObject getKeyObject(); +} diff --git a/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMOperationImpl.java b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMOperationImpl.java new file mode 100644 index 0000000..8059e44 --- /dev/null +++ b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMOperationImpl.java @@ -0,0 +1,415 @@ +/* + * Copyright(C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" (short)0IS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.javacard.seprovider; + +import javacard.framework.JCSystem; +import javacard.framework.Util; +import javacard.security.CryptoException; +import javacard.security.Key; +import javacard.security.KeyAgreement; +import javacard.security.PrivateKey; +import javacard.security.Signature; +import javacardx.crypto.AEADCipher; +import javacardx.crypto.Cipher; + +/** + * This class contains the actual implementation of all the crypto operations. It internally uses + * the Javacard crypto library to perform the operations. + */ +public class KMOperationImpl implements KMOperation { + + private static final byte ALG_TYPE_OFFSET = 0x00; + private static final byte PADDING_OFFSET = 0x01; + private static final byte PURPOSE_OFFSET = 0x02; + private static final byte BLOCK_MODE_OFFSET = 0x03; + private static final byte MAC_LENGTH_OFFSET = 0x04; + private final byte[] EMPTY = {}; + // This will hold the length of the buffer stored inside the + // Java Card after the GCM update operation. + private static final byte AES_GCM_UPDATE_LEN_OFFSET = 0x05; + private static final byte PARAMETERS_LENGTH = 6; + private short[] parameters; + // Either one of Cipher/Signature instance is stored. + private Object[] operationInst; + + public KMOperationImpl() { + parameters = JCSystem.makeTransientShortArray(PARAMETERS_LENGTH, JCSystem.CLEAR_ON_RESET); + operationInst = JCSystem.makeTransientObjectArray((short) 2, JCSystem.CLEAR_ON_RESET); + reset(); + } + + public short getPurpose() { + return parameters[PURPOSE_OFFSET]; + } + + public void setPurpose(short mode) { + parameters[PURPOSE_OFFSET] = mode; + } + + public short getMacLength() { + return parameters[MAC_LENGTH_OFFSET]; + } + + public void setMacLength(short macLength) { + parameters[MAC_LENGTH_OFFSET] = macLength; + } + + public short getPaddingAlgorithm() { + return parameters[PADDING_OFFSET]; + } + + public void setPaddingAlgorithm(short alg) { + parameters[PADDING_OFFSET] = alg; + } + + public void setBlockMode(short mode) { + parameters[BLOCK_MODE_OFFSET] = mode; + } + + public short getBlockMode() { + return parameters[BLOCK_MODE_OFFSET]; + } + + public short getAlgorithmType() { + return parameters[ALG_TYPE_OFFSET]; + } + + public void setAlgorithmType(short cipherAlg) { + parameters[ALG_TYPE_OFFSET] = cipherAlg; + } + + public void setCipher(Cipher cipher) { + operationInst[KMPoolManager.RESOURCE_TYPE_CRYPTO] = cipher; + } + + public void setSignature(Signature signer) { + operationInst[KMPoolManager.RESOURCE_TYPE_CRYPTO] = signer; + } + + public void setKeyAgreement(KeyAgreement keyAgreement) { + operationInst[KMPoolManager.RESOURCE_TYPE_CRYPTO] = keyAgreement; + } + + public boolean isResourceMatches(Object object, byte resourceType) { + return operationInst[resourceType] == object; + } + + public void setKeyObject(KMKeyObject keyObject) { + operationInst[KMPoolManager.RESOURCE_TYPE_KEY] = keyObject; + } + + public KMKeyObject getKeyObject() { + return (KMKeyObject) operationInst[KMPoolManager.RESOURCE_TYPE_KEY]; + } + + private void reset() { + operationInst[KMPoolManager.RESOURCE_TYPE_CRYPTO] = null; + operationInst[KMPoolManager.RESOURCE_TYPE_KEY] = null; + parameters[MAC_LENGTH_OFFSET] = KMType.INVALID_VALUE; + parameters[AES_GCM_UPDATE_LEN_OFFSET] = 0; + parameters[BLOCK_MODE_OFFSET] = KMType.INVALID_VALUE; + parameters[PURPOSE_OFFSET] = KMType.INVALID_VALUE; + parameters[ALG_TYPE_OFFSET] = KMType.INVALID_VALUE; + parameters[PADDING_OFFSET] = KMType.INVALID_VALUE; + } + + private byte mapPurpose(short purpose) { + switch (purpose) { + case KMType.ENCRYPT: + return Cipher.MODE_ENCRYPT; + case KMType.DECRYPT: + return Cipher.MODE_DECRYPT; + case KMType.SIGN: + return Signature.MODE_SIGN; + case KMType.VERIFY: + return Signature.MODE_VERIFY; + } + return -1; + } + + private void initSymmetricCipher(Key key, byte[] ivBuffer, short ivStart, short ivLength) { + Cipher symmCipher = (Cipher) operationInst[KMPoolManager.RESOURCE_TYPE_CRYPTO]; + byte cipherAlg = symmCipher.getAlgorithm(); + switch (cipherAlg) { + case Cipher.ALG_AES_BLOCK_128_CBC_NOPAD: + case Cipher.ALG_AES_CTR: + symmCipher.init(key, mapPurpose(getPurpose()), ivBuffer, ivStart, ivLength); + break; + case Cipher.ALG_AES_BLOCK_128_ECB_NOPAD: + case Cipher.ALG_DES_ECB_NOPAD: + symmCipher.init(key, mapPurpose(getPurpose())); + break; + case Cipher.ALG_DES_CBC_NOPAD: + // Consume only 8 bytes of iv. the random number for iv is of 16 bytes. + // While sending back the iv, send only 8 bytes. + symmCipher.init(key, mapPurpose(getPurpose()), ivBuffer, ivStart, (short) 8); + break; + case AEADCipher.ALG_AES_GCM: + ((AEADCipher) symmCipher).init(key, mapPurpose(getPurpose()), ivBuffer, ivStart, ivLength); + break; + default: // This should never happen + CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); + break; + } + } + + private void initRsa(Key key, short digest) { + if (KMType.SIGN == getPurpose()) { + byte mode; + if (getPaddingAlgorithm() == KMType.PADDING_NONE + || (getPaddingAlgorithm() == KMType.RSA_PKCS1_1_5_SIGN && digest == KMType.DIGEST_NONE)) { + mode = Cipher.MODE_DECRYPT; + } else { + mode = Signature.MODE_SIGN; + } + ((Signature) operationInst[KMPoolManager.RESOURCE_TYPE_CRYPTO]).init((PrivateKey) key, mode); + } else { // RSA Cipher + ((Cipher) operationInst[KMPoolManager.RESOURCE_TYPE_CRYPTO]) + .init((PrivateKey) key, mapPurpose(getPurpose())); + } + } + + private void initEc(Key key) { + if (KMType.AGREE_KEY == getPurpose()) { + ((KeyAgreement) operationInst[KMPoolManager.RESOURCE_TYPE_CRYPTO]).init((PrivateKey) key); + } else { + ((Signature) operationInst[KMPoolManager.RESOURCE_TYPE_CRYPTO]) + .init((PrivateKey) key, mapPurpose(getPurpose())); + } + } + + public void init(Key key, short digest, byte[] buf, short start, short length) { + switch (getAlgorithmType()) { + case KMType.AES: + case KMType.DES: + initSymmetricCipher(key, buf, start, length); + break; + case KMType.HMAC: + ((Signature) operationInst[KMPoolManager.RESOURCE_TYPE_CRYPTO]) + .init(key, mapPurpose(getPurpose())); + break; + case KMType.RSA: + initRsa(key, digest); + break; + case KMType.EC: + initEc(key); + break; + default: // This should never happen + CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); + break; + } + } + + @Override + public short update( + byte[] inputDataBuf, + short inputDataStart, + short inputDataLength, + byte[] outputDataBuf, + short outputDataStart) { + short len = + ((Cipher) operationInst[KMPoolManager.RESOURCE_TYPE_CRYPTO]) + .update(inputDataBuf, inputDataStart, inputDataLength, outputDataBuf, outputDataStart); + if (parameters[ALG_TYPE_OFFSET] == KMType.AES && parameters[BLOCK_MODE_OFFSET] == KMType.GCM) { + // Every time Block size data is stored as intermediate result. + parameters[AES_GCM_UPDATE_LEN_OFFSET] += (short) (inputDataLength - len); + } + return len; + } + + @Override + public short update(byte[] inputDataBuf, short inputDataStart, short inputDataLength) { + ((Signature) operationInst[KMPoolManager.RESOURCE_TYPE_CRYPTO]) + .update(inputDataBuf, inputDataStart, inputDataLength); + return 0; + } + + private short finishKeyAgreement( + byte[] publicKey, short start, short len, byte[] output, short outputStart) { + return ((KeyAgreement) operationInst[KMPoolManager.RESOURCE_TYPE_CRYPTO]) + .generateSecret(publicKey, start, len, output, outputStart); + } + + private short finishCipher( + byte[] inputDataBuf, + short inputDataStart, + short inputDataLen, + byte[] outputDataBuf, + short outputDataStart) { + short len = 0; + try { + byte[] tmpArray = KMAndroidSEProvider.getInstance().tmpArray; + Cipher cipher = (Cipher) operationInst[KMPoolManager.RESOURCE_TYPE_CRYPTO]; + short cipherAlg = parameters[ALG_TYPE_OFFSET]; + short blockMode = parameters[BLOCK_MODE_OFFSET]; + short mode = parameters[PURPOSE_OFFSET]; + short macLength = parameters[MAC_LENGTH_OFFSET]; + short padding = parameters[PADDING_OFFSET]; + + if (cipherAlg == KMType.AES && blockMode == KMType.GCM) { + if (mode == KMType.DECRYPT) { + inputDataLen = (short) (inputDataLen - macLength); + } + } else if ((cipherAlg == KMType.DES || cipherAlg == KMType.AES) + && padding == KMType.PKCS7 + && mode == KMType.ENCRYPT) { + byte blkSize = 16; + byte paddingBytes; + short inputlen = inputDataLen; + if (cipherAlg == KMType.DES) { + blkSize = 8; + } + // padding bytes + if (inputlen % blkSize == 0) { + paddingBytes = blkSize; + } else { + paddingBytes = (byte) (blkSize - (inputlen % blkSize)); + } + // final len with padding + inputlen = (short) (inputlen + paddingBytes); + // intermediate buffer to copy input data+padding + // fill in the padding + Util.arrayFillNonAtomic(tmpArray, (short) 0, inputlen, paddingBytes); + // copy the input data + Util.arrayCopyNonAtomic(inputDataBuf, inputDataStart, tmpArray, (short) 0, inputDataLen); + inputDataBuf = tmpArray; + inputDataLen = inputlen; + inputDataStart = 0; + } + len = + cipher.doFinal( + inputDataBuf, inputDataStart, inputDataLen, outputDataBuf, outputDataStart); + if ((cipherAlg == KMType.AES || cipherAlg == KMType.DES) + && padding == KMType.PKCS7 + && mode == KMType.DECRYPT) { + byte blkSize = 16; + if (cipherAlg == KMType.DES) { + blkSize = 8; + } + if (len > 0) { + // verify if padding is corrupted. + byte paddingByte = outputDataBuf[(short) (outputDataStart + len - 1)]; + // padding byte always should be <= block size + if ((short) paddingByte > blkSize || (short) paddingByte <= 0) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + + for (short j = 1; j <= paddingByte; ++j) { + if (outputDataBuf[(short) (outputDataStart + len - j)] != paddingByte) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + } + len = (short) (len - (short) paddingByte); // remove the padding bytes + } + } else if (cipherAlg == KMType.AES && blockMode == KMType.GCM) { + if (mode == KMType.ENCRYPT) { + len += + ((AEADCipher) cipher) + .retrieveTag(outputDataBuf, (short) (outputDataStart + len), macLength); + } else { + boolean verified = + ((AEADCipher) cipher) + .verifyTag( + inputDataBuf, (short) (inputDataStart + inputDataLen), macLength, macLength); + if (!verified) { + KMException.throwIt(KMError.VERIFICATION_FAILED); + } + } + } + } finally { + KMAndroidSEProvider.getInstance().clean(); + } + return len; + } + + @Override + public short finish( + byte[] inputDataBuf, + short inputDataStart, + short inputDataLen, + byte[] outputDataBuf, + short outputDataStart) { + if (parameters[PURPOSE_OFFSET] == KMType.AGREE_KEY) { + return finishKeyAgreement( + inputDataBuf, inputDataStart, inputDataLen, outputDataBuf, outputDataStart); + } else { + return finishCipher( + inputDataBuf, inputDataStart, inputDataLen, outputDataBuf, outputDataStart); + } + } + + @Override + public short sign( + byte[] inputDataBuf, + short inputDataStart, + short inputDataLength, + byte[] signBuf, + short signStart) { + return ((Signature) operationInst[KMPoolManager.RESOURCE_TYPE_CRYPTO]) + .sign(inputDataBuf, inputDataStart, inputDataLength, signBuf, signStart); + } + + @Override + public boolean verify( + byte[] inputDataBuf, + short inputDataStart, + short inputDataLength, + byte[] signBuf, + short signStart, + short signLength) { + return ((Signature) operationInst[KMPoolManager.RESOURCE_TYPE_CRYPTO]) + .verify(inputDataBuf, inputDataStart, inputDataLength, signBuf, signStart, signLength); + } + + @Override + public void abort() { + // Few simulators does not reset the Hmac signer instance on init so as + // a workaround to reset the hmac signer instance in case of abort/failure of the operation + // the corresponding sign / verify function is called. + if (operationInst[KMPoolManager.RESOURCE_TYPE_CRYPTO] != null) { + if ((parameters[PURPOSE_OFFSET] == KMType.SIGN || parameters[PURPOSE_OFFSET] == KMType.VERIFY) + && (((Signature) operationInst[KMPoolManager.RESOURCE_TYPE_CRYPTO]).getAlgorithm() + == Signature.ALG_HMAC_SHA_256)) { + Signature signer = (Signature) operationInst[KMPoolManager.RESOURCE_TYPE_CRYPTO]; + try { + if (parameters[PURPOSE_OFFSET] == KMType.SIGN) { + signer.sign(EMPTY, (short) 0, (short) 0, EMPTY, (short) 0); + } else { + signer.verify(EMPTY, (short) 0, (short) 0, EMPTY, (short) 0, (short) 0); + } + } catch (Exception e) { + // Ignore. + } + } + } + reset(); + } + + @Override + public void updateAAD(byte[] dataBuf, short dataStart, short dataLength) { + ((AEADCipher) operationInst[KMPoolManager.RESOURCE_TYPE_CRYPTO]) + .updateAAD(dataBuf, dataStart, dataLength); + } + + @Override + public short getAESGCMOutputSize(short dataSize, short macLength) { + if (parameters[PURPOSE_OFFSET] == KMType.ENCRYPT) { + return (short) (parameters[AES_GCM_UPDATE_LEN_OFFSET] + dataSize + macLength); + } else { + return (short) (parameters[AES_GCM_UPDATE_LEN_OFFSET] + dataSize - macLength); + } + } +} diff --git a/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMPoolManager.java b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMPoolManager.java new file mode 100644 index 0000000..8ca2664 --- /dev/null +++ b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMPoolManager.java @@ -0,0 +1,665 @@ +/* + * Copyright(C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" (short)0IS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.javacard.seprovider; + +import javacard.framework.JCSystem; +import javacard.security.AESKey; +import javacard.security.CryptoException; +import javacard.security.DESKey; +import javacard.security.ECPrivateKey; +import javacard.security.ECPublicKey; +import javacard.security.HMACKey; +import javacard.security.KeyAgreement; +import javacard.security.KeyBuilder; +import javacard.security.KeyPair; +import javacard.security.Signature; +import javacardx.crypto.AEADCipher; +import javacardx.crypto.Cipher; + +/** + * This class creates and manages all the cipher, signer, key agreement, operation and trusted + * confirmation pool instances. Each cipher or signer pool can hold a maximum of 4 instances per + * algorithm; however, only one instance of each algorithm is created initially and if required more + * instances are created dynamically. A maximum of four operations can be performed simultaneously. + * Upon reaching the maximum limit of 4, further operations or crypto instances will throw a + * TOO_MANY_OPERATIONS error. TrustedConfirmation pool is to support any operation which has the + * TRUSTED_CONFIRMATION tag in its key parameters. + */ +public class KMPoolManager { + + public static final byte MAX_OPERATION_INSTANCES = 4; + private static final byte HMAC_MAX_OPERATION_INSTANCES = 8; + public static final byte AES_128 = 0x04; + public static final byte AES_256 = 0x05; + // Resource type constants + public static final byte RESOURCE_TYPE_CRYPTO = 0x00; + public static final byte RESOURCE_TYPE_KEY = 0x01; + // static final variables + // -------------------------------------------------------------- + // P-256 Curve Parameters + static byte[] secp256r1_P; + static byte[] secp256r1_A; + + static byte[] secp256r1_B; + static byte[] secp256r1_S; + + // Uncompressed form + static byte[] secp256r1_UCG; + static byte[] secp256r1_N; + static final short secp256r1_H = 1; + // -------------------------------------------------------------- + + // Cipher pool + private Object[] cipherPool; + // Signature pool + private Object[] signerPool; + // Keyagreement pool + private Object[] keyAgreementPool; + // KMOperationImpl pool + private Object[] operationPool; + // Hmac signer pool which is used to support TRUSTED_CONFIRMATION_REQUIRED tag. + private Object[] hmacSignOperationPool; + + private Object[] keysPool; + // RKP uses AESGCM and HMAC in generateCSR flow. + KMOperation rkpOPeration; + Cipher rkpAesGcm; + Signature rkpHmac; + KMKeyObject rkpHmacKey; + KMKeyObject rkpAesKey; + + final byte[] KEY_ALGS = { + AES_128, AES_256, KMType.DES, KMType.RSA, KMType.EC, KMType.HMAC, + }; + + final byte[] CIPHER_ALGS = { + Cipher.ALG_AES_BLOCK_128_CBC_NOPAD, + Cipher.ALG_AES_BLOCK_128_ECB_NOPAD, + Cipher.ALG_DES_CBC_NOPAD, + Cipher.ALG_DES_ECB_NOPAD, + Cipher.ALG_AES_CTR, + Cipher.ALG_RSA_PKCS1, + KMRsaOAEPEncoding.ALG_RSA_PKCS1_OAEP_SHA256_MGF1_SHA1, + KMRsaOAEPEncoding.ALG_RSA_PKCS1_OAEP_SHA256_MGF1_SHA256, + Cipher.ALG_RSA_NOPAD, + AEADCipher.ALG_AES_GCM + }; + + final byte[] SIG_ALGS = { + Signature.ALG_RSA_SHA_256_PKCS1, + Signature.ALG_RSA_SHA_256_PKCS1_PSS, + Signature.ALG_ECDSA_SHA_256, + Signature.ALG_HMAC_SHA_256, + KMRsa2048NoDigestSignature.ALG_RSA_SIGN_NOPAD, + KMRsa2048NoDigestSignature.ALG_RSA_PKCS1_NODIGEST, + KMEcdsa256NoDigestSignature.ALG_ECDSA_NODIGEST + }; + + final byte[] KEY_AGREE_ALGS = {KeyAgreement.ALG_EC_SVDP_DH_PLAIN}; + + private static KMPoolManager poolManager; + + public static KMPoolManager getInstance() { + if (poolManager == null) { + poolManager = new KMPoolManager(); + } + return poolManager; + } + + public static void initStatics() { + secp256r1_P = + new byte[] { + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF + }; + + secp256r1_A = + new byte[] { + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFC + }; + + secp256r1_B = + new byte[] { + (byte) 0x5A, (byte) 0xC6, (byte) 0x35, (byte) 0xD8, (byte) 0xAA, (byte) 0x3A, + (byte) 0x93, (byte) 0xE7, (byte) 0xB3, (byte) 0xEB, (byte) 0xBD, (byte) 0x55, + (byte) 0x76, (byte) 0x98, (byte) 0x86, (byte) 0xBC, (byte) 0x65, (byte) 0x1D, + (byte) 0x06, (byte) 0xB0, (byte) 0xCC, (byte) 0x53, (byte) 0xB0, (byte) 0xF6, + (byte) 0x3B, (byte) 0xCE, (byte) 0x3C, (byte) 0x3E, (byte) 0x27, (byte) 0xD2, + (byte) 0x60, (byte) 0x4B + }; + + secp256r1_S = + new byte[] { + (byte) 0xC4, (byte) 0x9D, (byte) 0x36, (byte) 0x08, (byte) 0x86, (byte) 0xE7, + (byte) 0x04, (byte) 0x93, (byte) 0x6A, (byte) 0x66, (byte) 0x78, (byte) 0xE1, + (byte) 0x13, (byte) 0x9D, (byte) 0x26, (byte) 0xB7, (byte) 0x81, (byte) 0x9F, + (byte) 0x7E, (byte) 0x90 + }; + + // Uncompressed form + secp256r1_UCG = + new byte[] { + (byte) 0x04, (byte) 0x6B, (byte) 0x17, (byte) 0xD1, (byte) 0xF2, (byte) 0xE1, + (byte) 0x2C, (byte) 0x42, (byte) 0x47, (byte) 0xF8, (byte) 0xBC, (byte) 0xE6, + (byte) 0xE5, (byte) 0x63, (byte) 0xA4, (byte) 0x40, (byte) 0xF2, (byte) 0x77, + (byte) 0x03, (byte) 0x7D, (byte) 0x81, (byte) 0x2D, (byte) 0xEB, (byte) 0x33, + (byte) 0xA0, (byte) 0xF4, (byte) 0xA1, (byte) 0x39, (byte) 0x45, (byte) 0xD8, + (byte) 0x98, (byte) 0xC2, (byte) 0x96, (byte) 0x4F, (byte) 0xE3, (byte) 0x42, + (byte) 0xE2, (byte) 0xFE, (byte) 0x1A, (byte) 0x7F, (byte) 0x9B, (byte) 0x8E, + (byte) 0xE7, (byte) 0xEB, (byte) 0x4A, (byte) 0x7C, (byte) 0x0F, (byte) 0x9E, + (byte) 0x16, (byte) 0x2B, (byte) 0xCE, (byte) 0x33, (byte) 0x57, (byte) 0x6B, + (byte) 0x31, (byte) 0x5E, (byte) 0xCE, (byte) 0xCB, (byte) 0xB6, (byte) 0x40, + (byte) 0x68, (byte) 0x37, (byte) 0xBF, (byte) 0x51, (byte) 0xF5 + }; + + secp256r1_N = + new byte[] { + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xBC, (byte) 0xE6, + (byte) 0xFA, (byte) 0xAD, (byte) 0xA7, (byte) 0x17, (byte) 0x9E, (byte) 0x84, + (byte) 0xF3, (byte) 0xB9, (byte) 0xCA, (byte) 0xC2, (byte) 0xFC, (byte) 0x63, + (byte) 0x25, (byte) 0x51 + }; + } + + private KMPoolManager() { + initStatics(); + cipherPool = new Object[(short) (CIPHER_ALGS.length * MAX_OPERATION_INSTANCES)]; + // Extra 4 algorithms are used to support TRUSTED_CONFIRMATION_REQUIRED feature. + signerPool = + new Object[(short) ((SIG_ALGS.length * MAX_OPERATION_INSTANCES) + MAX_OPERATION_INSTANCES)]; + keyAgreementPool = new Object[(short) (KEY_AGREE_ALGS.length * MAX_OPERATION_INSTANCES)]; + + keysPool = + new Object[(short) ((KEY_ALGS.length * MAX_OPERATION_INSTANCES) + MAX_OPERATION_INSTANCES)]; + operationPool = new Object[MAX_OPERATION_INSTANCES]; + hmacSignOperationPool = new Object[MAX_OPERATION_INSTANCES]; + /* Initialize pools */ + initializeOperationPool(); + initializeHmacSignOperationPool(); + initializeSignerPool(); + initializeCipherPool(); + initializeKeyAgreementPool(); + initializeKeysPool(); + // Initialize the Crypto and Key objects required for RKP flow. + initializeRKpObjects(); + } + + private void initializeRKpObjects() { + rkpOPeration = new KMOperationImpl(); + rkpAesGcm = Cipher.getInstance(AEADCipher.ALG_AES_GCM, false); + rkpHmac = Signature.getInstance(Signature.ALG_HMAC_SHA_256, false); + rkpAesKey = createKeyObjectInstance(AES_256); + rkpHmacKey = createKeyObjectInstance(KMType.HMAC); + } + + private void initializeKeysPool() { + for (short index = 0; index < KEY_ALGS.length; index++) { + keysPool[index] = createKeyObjectInstance(KEY_ALGS[index]); + } + } + + private void initializeOperationPool() { + for (short index = 0; index < MAX_OPERATION_INSTANCES; index++) { + operationPool[index] = new KMOperationImpl(); + } + } + + private void initializeHmacSignOperationPool() { + for (short index = 0; index < MAX_OPERATION_INSTANCES; index++) { + hmacSignOperationPool[index] = new KMOperationImpl(); + } + } + + // Create a signature instance of each algorithm once. + private void initializeSignerPool() { + short index; + for (index = 0; index < SIG_ALGS.length; index++) { + signerPool[index] = getSignatureInstance(SIG_ALGS[index]); + } + + // Allocate extra 4 HMAC signer instances required for trusted confirmation + for (short len = (short) (index + 4); index < len; index++) { + signerPool[index] = getSignatureInstance(Signature.ALG_HMAC_SHA_256); + } + } + + // Create a cipher instance of each algorithm once. + private void initializeCipherPool() { + for (short index = 0; index < CIPHER_ALGS.length; index++) { + cipherPool[index] = getCipherInstance(CIPHER_ALGS[index]); + } + } + + private void initializeKeyAgreementPool() { + for (short index = 0; index < KEY_AGREE_ALGS.length; index++) { + keyAgreementPool[index] = getKeyAgreementInstance(KEY_AGREE_ALGS[index]); + } + } + + private Object[] getCryptoPoolInstance(short purpose) { + switch (purpose) { + case KMType.AGREE_KEY: + return keyAgreementPool; + + case KMType.ENCRYPT: + case KMType.DECRYPT: + return cipherPool; + + case KMType.SIGN: + case KMType.VERIFY: + return signerPool; + + default: + KMException.throwIt(KMError.UNSUPPORTED_PURPOSE); + } + return null; + } + + private Object createInstance(short purpose, short alg) { + switch (purpose) { + case KMType.AGREE_KEY: + return getKeyAgreementInstance((byte) alg); + + case KMType.ENCRYPT: + case KMType.DECRYPT: + return getCipherInstance((byte) alg); + + case KMType.SIGN: + case KMType.VERIFY: + return getSignatureInstance((byte) alg); + + default: + KMException.throwIt(KMError.UNSUPPORTED_PURPOSE); + } + return null; + } + + private KeyAgreement getKeyAgreementInstance(byte alg) { + return KeyAgreement.getInstance(alg, false); + } + + private Signature getSignatureInstance(byte alg) { + if (KMRsa2048NoDigestSignature.ALG_RSA_SIGN_NOPAD == alg + || KMRsa2048NoDigestSignature.ALG_RSA_PKCS1_NODIGEST == alg) { + return new KMRsa2048NoDigestSignature(alg); + } else if (KMEcdsa256NoDigestSignature.ALG_ECDSA_NODIGEST == alg) { + return new KMEcdsa256NoDigestSignature(alg); + } else { + return Signature.getInstance(alg, false); + } + } + + private KMKeyObject createKeyObjectInstance(byte alg) { + Object keyObject = null; + switch (alg) { + case AES_128: + keyObject = + (AESKey) + KeyBuilder.buildKey( + KeyBuilder.TYPE_AES_TRANSIENT_RESET, KeyBuilder.LENGTH_AES_128, false); + break; + case AES_256: + keyObject = + (AESKey) + KeyBuilder.buildKey( + KeyBuilder.TYPE_AES_TRANSIENT_RESET, KeyBuilder.LENGTH_AES_256, false); + break; + case KMType.DES: + keyObject = + (DESKey) + KeyBuilder.buildKey( + KeyBuilder.TYPE_DES_TRANSIENT_RESET, KeyBuilder.LENGTH_DES3_3KEY, false); + break; + case KMType.RSA: + keyObject = new KeyPair(KeyPair.ALG_RSA, KeyBuilder.LENGTH_RSA_2048); + break; + case KMType.EC: + keyObject = new KeyPair(KeyPair.ALG_EC_FP, KeyBuilder.LENGTH_EC_FP_256); + initECKey((KeyPair) keyObject); + break; + case KMType.HMAC: + keyObject = + (HMACKey) KeyBuilder.buildKey(KeyBuilder.TYPE_HMAC_TRANSIENT_RESET, (short) 512, false); + break; + default: + KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM); + } + KMKeyObject ptr = new KMKeyObject(); + ptr.algorithm = alg; + ptr.keyObjectInst = keyObject; + return ptr; + } + + private Cipher getCipherInstance(byte alg) { + if ((KMRsaOAEPEncoding.ALG_RSA_PKCS1_OAEP_SHA256_MGF1_SHA1 == alg) + || (KMRsaOAEPEncoding.ALG_RSA_PKCS1_OAEP_SHA256_MGF1_SHA256 == alg)) { + return new KMRsaOAEPEncoding(alg); + } else { + return Cipher.getInstance(alg, false); + } + } + + /** + * Returns the first available resource from operation pool. + * + * @return instance of the available resource or null if no resource is available. + */ + public KMOperation getResourceFromOperationPool(boolean isTrustedConfOpr) { + short index = 0; + KMOperationImpl impl; + Object[] oprPool; + if (isTrustedConfOpr) { + oprPool = hmacSignOperationPool; + } else { + oprPool = operationPool; + } + while (index < oprPool.length) { + impl = (KMOperationImpl) oprPool[index]; + // Mode is always set. so compare using mode value. + if (impl.getPurpose() == KMType.INVALID_VALUE) { + return impl; + } + index++; + } + return null; + } + + private byte getAlgorithm(short purpose, Object object) { + switch (purpose) { + case KMType.AGREE_KEY: + return ((KeyAgreement) object).getAlgorithm(); + + case KMType.ENCRYPT: + case KMType.DECRYPT: + return ((Cipher) object).getAlgorithm(); + + case KMType.SIGN: + case KMType.VERIFY: + return ((Signature) object).getAlgorithm(); + + default: + KMException.throwIt(KMError.UNSUPPORTED_PURPOSE); + } + return 0; + } + + private boolean isResourceBusy(Object obj, byte resourceType) { + short index = 0; + while (index < MAX_OPERATION_INSTANCES) { + if (((KMOperationImpl) operationPool[index]).isResourceMatches(obj, resourceType) + || ((KMOperationImpl) hmacSignOperationPool[index]) + .isResourceMatches(obj, resourceType)) { + return true; + } + index++; + } + return false; + } + + private void setObject(short purpose, KMOperation operation, Object obj) { + switch (purpose) { + case KMType.AGREE_KEY: + ((KMOperationImpl) operation).setKeyAgreement((KeyAgreement) obj); + break; + case KMType.ENCRYPT: + case KMType.DECRYPT: + ((KMOperationImpl) operation).setCipher((Cipher) obj); + break; + case KMType.SIGN: + case KMType.VERIFY: + ((KMOperationImpl) operation).setSignature((Signature) obj); + break; + default: + KMException.throwIt(KMError.UNSUPPORTED_PURPOSE); + } + } + + private void reserveOperation( + KMOperation operation, + short purpose, + short strongboxAlgType, + short padding, + short blockMode, + short macLength, + Object obj, + KMKeyObject keyObject) { + ((KMOperationImpl) operation).setPurpose(purpose); + ((KMOperationImpl) operation).setAlgorithmType(strongboxAlgType); + ((KMOperationImpl) operation).setPaddingAlgorithm(padding); + ((KMOperationImpl) operation).setBlockMode(blockMode); + ((KMOperationImpl) operation).setMacLength(macLength); + ((KMOperationImpl) operation).setKeyObject(keyObject); + setObject(purpose, operation, obj); + } + + public KMOperation getRKpOperation( + short purpose, + short alg, + short strongboxAlgType, + short padding, + short blockMode, + short macLength) { + if (((KMOperationImpl) rkpOPeration).getPurpose() != KMType.INVALID_VALUE) { + // Should not come here. + KMException.throwIt(KMError.UNKNOWN_ERROR); + } + Object cryptoObj = null; + KMKeyObject keyObject = null; + + switch (alg) { + case AEADCipher.ALG_AES_GCM: + cryptoObj = rkpAesGcm; + keyObject = rkpAesKey; + break; + case Signature.ALG_HMAC_SHA_256: + cryptoObj = rkpHmac; + keyObject = rkpHmacKey; + break; + default: + // Should not come here. + KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM); + break; + } + reserveOperation( + rkpOPeration, + purpose, + strongboxAlgType, + padding, + blockMode, + macLength, + cryptoObj, + keyObject); + return rkpOPeration; + } + + public KMOperation getOperationImpl( + short purpose, + short alg, + short strongboxAlgType, + short padding, + short blockMode, + short macLength, + short secretLength, + boolean isTrustedConfOpr) { + KMOperation operation; + // Throw exception if no resource from operation pool is available. + if (null == (operation = getResourceFromOperationPool(isTrustedConfOpr))) { + KMException.throwIt(KMError.TOO_MANY_OPERATIONS); + } + // Get one of the pool instances (cipher / signer / keyAgreement) based on purpose. + Object[] pool = getCryptoPoolInstance(purpose); + short index = 0; + short usageCount = 0; + short maxOperations = MAX_OPERATION_INSTANCES; + if (Signature.ALG_HMAC_SHA_256 == alg) { + maxOperations = HMAC_MAX_OPERATION_INSTANCES; + } + + KMKeyObject keyObject = getKeyObjectFromPool(alg, secretLength, maxOperations); + while (index < pool.length) { + if (usageCount >= maxOperations) { + KMException.throwIt(KMError.TOO_MANY_OPERATIONS); + } + if (pool[index] == null) { + // Create one of the instance (Cipher / Signer / KeyAgreement] based on purpose. + Object cipherObject = createInstance(purpose, alg); + JCSystem.beginTransaction(); + pool[index] = cipherObject; + JCSystem.commitTransaction(); + reserveOperation( + operation, + purpose, + strongboxAlgType, + padding, + blockMode, + macLength, + pool[index], + keyObject); + break; + } + if (alg == getAlgorithm(purpose, pool[index])) { + // Check if the crypto instance is not busy and free to use. + if (!isResourceBusy(pool[index], RESOURCE_TYPE_CRYPTO)) { + reserveOperation( + operation, + purpose, + strongboxAlgType, + padding, + blockMode, + macLength, + pool[index], + keyObject); + break; + } + usageCount++; + } + index++; + } + return operation; + } + + public KMKeyObject getKeyObjectFromPool(short alg, short secretLength, short maxOperations) { + KMKeyObject keyObject = null; + byte algo = mapAlgorithm(alg, secretLength); + short index = 0; + short usageCount = 0; + while (index < keysPool.length) { + if (usageCount >= maxOperations) { + KMException.throwIt(KMError.TOO_MANY_OPERATIONS); + } + if (keysPool[index] == null) { + keyObject = createKeyObjectInstance(algo); + JCSystem.beginTransaction(); + keysPool[index] = keyObject; + JCSystem.commitTransaction(); + break; + } + keyObject = (KMKeyObject) keysPool[index]; + if (algo == keyObject.algorithm) { + // Check if the Object instance is not busy and free to use. + if (!isResourceBusy(keyObject, RESOURCE_TYPE_KEY)) { + break; + } + usageCount++; + } + index++; + } + return keyObject; + } + + private byte mapAlgorithm(short alg, short secretLength) { + byte algo = 0; + switch (alg) { + case Cipher.ALG_AES_BLOCK_128_CBC_NOPAD: + case Cipher.ALG_AES_BLOCK_128_ECB_NOPAD: + case Cipher.ALG_AES_CTR: + case AEADCipher.ALG_AES_GCM: + if (secretLength == 16) { + algo = AES_128; + } else if (secretLength == 32) { + algo = AES_256; + } else { + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + } + break; + case Cipher.ALG_DES_CBC_NOPAD: + case Cipher.ALG_DES_ECB_NOPAD: + algo = KMType.DES; + break; + case Cipher.ALG_RSA_PKCS1: + case KMRsaOAEPEncoding.ALG_RSA_PKCS1_OAEP_SHA256_MGF1_SHA1: + case KMRsaOAEPEncoding.ALG_RSA_PKCS1_OAEP_SHA256_MGF1_SHA256: + case Cipher.ALG_RSA_NOPAD: + case Signature.ALG_RSA_SHA_256_PKCS1: + case Signature.ALG_RSA_SHA_256_PKCS1_PSS: + case KMRsa2048NoDigestSignature.ALG_RSA_SIGN_NOPAD: + case KMRsa2048NoDigestSignature.ALG_RSA_PKCS1_NODIGEST: + algo = KMType.RSA; + break; + case Signature.ALG_ECDSA_SHA_256: + case KMEcdsa256NoDigestSignature.ALG_ECDSA_NODIGEST: + case KeyAgreement.ALG_EC_SVDP_DH_PLAIN: + algo = KMType.EC; + break; + case Signature.ALG_HMAC_SHA_256: + algo = KMType.HMAC; + break; + default: + KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM); + } + return algo; + } + + public void initECKey(KeyPair ecKeyPair) { + ECPrivateKey privKey = (ECPrivateKey) ecKeyPair.getPrivate(); + ECPublicKey pubkey = (ECPublicKey) ecKeyPair.getPublic(); + pubkey.setFieldFP(secp256r1_P, (short) 0, (short) secp256r1_P.length); + pubkey.setA(secp256r1_A, (short) 0, (short) secp256r1_A.length); + pubkey.setB(secp256r1_B, (short) 0, (short) secp256r1_B.length); + pubkey.setG(secp256r1_UCG, (short) 0, (short) secp256r1_UCG.length); + pubkey.setK(secp256r1_H); + pubkey.setR(secp256r1_N, (short) 0, (short) secp256r1_N.length); + + privKey.setFieldFP(secp256r1_P, (short) 0, (short) secp256r1_P.length); + privKey.setA(secp256r1_A, (short) 0, (short) secp256r1_A.length); + privKey.setB(secp256r1_B, (short) 0, (short) secp256r1_B.length); + privKey.setG(secp256r1_UCG, (short) 0, (short) secp256r1_UCG.length); + privKey.setK(secp256r1_H); + privKey.setR(secp256r1_N, (short) 0, (short) secp256r1_N.length); + } + + public void powerReset() { + short index = 0; + while (index < operationPool.length) { + ((KMOperationImpl) operationPool[index]).abort(); + ((KMOperationImpl) hmacSignOperationPool[index]).abort(); + index++; + } + // release rkp operation + rkpOPeration.abort(); + } +} diff --git a/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMRsa2048NoDigestSignature.java b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMRsa2048NoDigestSignature.java new file mode 100644 index 0000000..4890ce2 --- /dev/null +++ b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMRsa2048NoDigestSignature.java @@ -0,0 +1,140 @@ +/* + * Copyright(C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" (short)0IS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.javacard.seprovider; + +import javacard.framework.Util; +import javacard.security.CryptoException; +import javacard.security.Key; +import javacard.security.MessageDigest; +import javacard.security.Signature; +import javacardx.crypto.Cipher; + +/** This class provides support for RSA_NO_DIGEST signature algorithm. */ +public class KMRsa2048NoDigestSignature extends Signature { + + public static final byte ALG_RSA_SIGN_NOPAD = (byte) 0x65; + public static final byte ALG_RSA_PKCS1_NODIGEST = (byte) 0x66; + private byte algorithm; + private Cipher inst; + + public KMRsa2048NoDigestSignature(byte alg) { + algorithm = alg; + inst = Cipher.getInstance(Cipher.ALG_RSA_NOPAD, false); + } + + @Override + public void init(Key key, byte b) throws CryptoException { + inst.init(key, b); + } + + @Override + public void init(Key key, byte b, byte[] bytes, short i, short i1) throws CryptoException { + inst.init(key, b, bytes, i, i1); + } + + @Override + public void setInitialDigest(byte[] bytes, short i, short i1, byte[] bytes1, short i2, short i3) + throws CryptoException {} + + @Override + public byte getAlgorithm() { + return algorithm; + } + + @Override + public byte getMessageDigestAlgorithm() { + return MessageDigest.ALG_NULL; + } + + @Override + public byte getCipherAlgorithm() { + return algorithm; + } + + @Override + public byte getPaddingAlgorithm() { + return Cipher.PAD_NULL; + } + + @Override + public short getLength() throws CryptoException { + return 0; + } + + @Override + public void update(byte[] bytes, short i, short i1) throws CryptoException { + // HAL accumulates the data and send it at finish operation. + } + + @Override + public short sign(byte[] bytes, short i, short i1, byte[] bytes1, short i2) + throws CryptoException { + padData(bytes, i, i1, KMAndroidSEProvider.getInstance().tmpArray, (short) 0); + return inst.doFinal( + KMAndroidSEProvider.getInstance().tmpArray, (short) 0, (short) 256, bytes1, i2); + } + + @Override + public short signPreComputedHash(byte[] bytes, short i, short i1, byte[] bytes1, short i2) + throws CryptoException { + return 0; + } + + @Override + public boolean verify(byte[] bytes, short i, short i1, byte[] bytes1, short i2, short i3) + throws CryptoException { + // Verification is handled inside HAL + return false; + } + + @Override + public boolean verifyPreComputedHash( + byte[] bytes, short i, short i1, byte[] bytes1, short i2, short i3) throws CryptoException { + // Verification is handled inside HAL + return false; + } + + private void padData(byte[] buf, short start, short len, byte[] outBuf, short outBufStart) { + if (!isValidData(buf, start, len)) { + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + } + Util.arrayFillNonAtomic(outBuf, (short) outBufStart, (short) 256, (byte) 0x00); + if (algorithm == ALG_RSA_SIGN_NOPAD) { // add zero to right + } else if (algorithm == ALG_RSA_PKCS1_NODIGEST) { // 0x00||0x01||PS||0x00 + outBuf[0] = 0x00; + outBuf[1] = 0x01; + Util.arrayFillNonAtomic(outBuf, (short) 2, (short) (256 - len - 3), (byte) 0xFF); + outBuf[(short) (256 - len - 1)] = 0x00; + } else { + CryptoException.throwIt(CryptoException.ILLEGAL_USE); + } + Util.arrayCopyNonAtomic(buf, start, outBuf, (short) (256 - len), len); + } + + private boolean isValidData(byte[] buf, short start, short len) { + if (algorithm == ALG_RSA_SIGN_NOPAD) { + if (len > 256) { + return false; + } + } else { // ALG_RSA_PKCS1_NODIGEST + if (len > 245) { + KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + return false; + } + } + return true; + } +} diff --git a/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMRsaOAEPEncoding.java b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMRsaOAEPEncoding.java new file mode 100644 index 0000000..bf34ea1 --- /dev/null +++ b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMRsaOAEPEncoding.java @@ -0,0 +1,289 @@ +/* + * Copyright(C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" (short)0IS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.javacard.seprovider; + +import javacard.framework.JCSystem; +import javacard.framework.Util; +import javacard.security.CryptoException; +import javacard.security.Key; +import javacard.security.MessageDigest; +import javacardx.crypto.Cipher; + +/** This class has the implementation for RSA_OAEP decoding algorithm. */ +public class KMRsaOAEPEncoding extends Cipher { + + public static final byte ALG_RSA_PKCS1_OAEP_SHA256_MGF1_SHA1 = (byte) 0x1E; + public static final byte ALG_RSA_PKCS1_OAEP_SHA256_MGF1_SHA256 = (byte) 0x1F; + + final short MGF1_BUF_SIZE = 256; + static byte[] mgf1Buf; + private Cipher cipher; + private byte hash; + private byte mgf1Hash; + private byte algorithm; + + public KMRsaOAEPEncoding(byte alg) { + setDigests(alg); + cipher = Cipher.getInstance(Cipher.ALG_RSA_NOPAD, false); + algorithm = alg; + if (null == mgf1Buf) { + mgf1Buf = + JCSystem.makeTransientByteArray(MGF1_BUF_SIZE, JCSystem.MEMORY_TYPE_TRANSIENT_DESELECT); + } + } + + private void setDigests(byte alg) { + switch (alg) { + case ALG_RSA_PKCS1_OAEP_SHA256_MGF1_SHA1: + hash = MessageDigest.ALG_SHA_256; + mgf1Hash = MessageDigest.ALG_SHA; + break; + case ALG_RSA_PKCS1_OAEP_SHA256_MGF1_SHA256: + hash = MessageDigest.ALG_SHA_256; + mgf1Hash = MessageDigest.ALG_SHA_256; + break; + default: + CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); + } + } + + private short getDigestLength() { + switch (hash) { + case MessageDigest.ALG_SHA: + return MessageDigest.LENGTH_SHA; + case MessageDigest.ALG_SHA_224: + return MessageDigest.LENGTH_SHA_224; + case MessageDigest.ALG_SHA_256: + return MessageDigest.LENGTH_SHA_256; + case MessageDigest.ALG_SHA_384: + return MessageDigest.LENGTH_SHA_384; + case MessageDigest.ALG_SHA_512: + return MessageDigest.LENGTH_SHA_512; + default: + CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); + } + return 0; + } + + @Override + public void init(Key theKey, byte theMode) throws CryptoException { + cipher.init(theKey, theMode); + } + + @Override + public void init(Key theKey, byte theMode, byte[] bArray, short bOff, short bLen) + throws CryptoException { + cipher.init(theKey, theMode, bArray, bOff, bLen); + } + + @Override + public byte getAlgorithm() { + return algorithm; + } + + @Override + public byte getCipherAlgorithm() { + return 0; + } + + @Override + public byte getPaddingAlgorithm() { + return 0; + } + + @Override + public short doFinal( + byte[] inBuff, short inOffset, short inLength, byte[] outBuff, short outOffset) + throws CryptoException { + short len = cipher.doFinal(inBuff, inOffset, inLength, outBuff, outOffset); + + // https://tools.ietf.org/html/rfc8017#section-7.1 + // https://www.inf.pucrs.br/~calazans/graduate/TPVLSI_I/RSA-oaep_spec.pdf + // RSA OAEP Encoding and Decoding Mechanism for a 2048 bit RSA Key. + // Msg -> RSA-OAEP-ENCODE -> RSAEncryption -> RSADecryption -> + // RSA-OAEP-DECODE -> Msg + // RSA-OAEP-ENCODE generates an output length of 255, but RSAEncryption + // requires and input of length 256 so we pad 0 to the left of the input + // message and make the length equal to 256 and pass to RSAEncryption. + // RSADecryption takes input length equal to 256 and generates an + // output of length 256. After decryption the first byte of the output + // should be 0(left padding we did in encryption). + // RSA-OAEP-DECODE takes input of length 255 so remove the left padding of 1 + // byte. + if (len != 256 || outBuff[0] != 0) { + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + } + Util.arrayCopyNonAtomic( + outBuff, (short) (outOffset + 1), outBuff, (short) 0, (short) (len - 1)); + return rsaOAEPDecode(outBuff, (short) 0, (short) (len - 1)); + } + + @Override + public short update( + byte[] inBuff, short inOffset, short inLength, byte[] outBuff, short outOffset) + throws CryptoException { + return cipher.update(inBuff, inOffset, inLength, outBuff, outOffset); + } + + private void maskGenerationFunction1( + byte[] input, + short inputOffset, + short inputLen, + short expectedOutLen, + byte[] outBuf, + short outOffset) { + short counter = 0; + MessageDigest.OneShot md = null; + try { + md = MessageDigest.OneShot.open(mgf1Hash); + short digestLen = md.getLength(); + + Util.arrayCopyNonAtomic(input, inputOffset, mgf1Buf, (short) 0, inputLen); + while (counter < (short) (expectedOutLen / digestLen)) { + I2OS(counter, mgf1Buf, (short) inputLen); + md.doFinal( + mgf1Buf, + (short) 0, + (short) (4 + inputLen), + outBuf, + (short) (outOffset + (counter * digestLen))); + counter++; + } + + if ((short) (counter * digestLen) < expectedOutLen) { + I2OS(counter, mgf1Buf, (short) inputLen); + md.doFinal( + mgf1Buf, + (short) 0, + (short) (4 + inputLen), + outBuf, + (short) (outOffset + (counter * digestLen))); + } + + } finally { + if (md != null) { + md.close(); + } + Util.arrayFillNonAtomic(mgf1Buf, (short) 0, (short) MGF1_BUF_SIZE, (byte) 0); + } + } + + // Integer to Octet String conversion. + private void I2OS(short i, byte[] out, short offset) { + Util.arrayFillNonAtomic(out, (short) offset, (short) 4, (byte) 0); + out[(short) (offset + 3)] = (byte) (i >>> 0); + out[(short) (offset + 2)] = (byte) (i >>> 8); + } + + private short rsaOAEPDecode(byte[] encodedMsg, short encodedMsgOff, short encodedMsgLen) { + MessageDigest.OneShot md = null; + byte[] tmpArray = KMAndroidSEProvider.getInstance().tmpArray; + + try { + short hLen = getDigestLength(); + + if (encodedMsgLen < (short) (2 * hLen + 1)) { + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + } + // encodedMsg will be in the format of maskedSeed||maskedDB. + // maskedSeed length is hLen and maskedDB length is (encodedMsgLen - hLen) + // Now retrieve the seedMask by calling MGF(maskedDB, hLen). The length + // of the seedMask is hLen. + // seedMask = MGF(maskedDB, hLen) + maskGenerationFunction1( + encodedMsg, + (short) (encodedMsgOff + hLen), + (short) (encodedMsgLen - hLen), + hLen, + tmpArray, + (short) 0); + + // Get the seed by doing XOR of (maskedSeed ^ seedMask). + // seed = (maskedSeed ^ seedMask) + for (short i = 0; i < hLen; i++) { + // Store the seed in encodeMsg itself. + encodedMsg[(short) (encodedMsgOff + i)] ^= tmpArray[i]; + } + + // Now get the dbMask by calling MGF(seed , (emLen-hLen)). + // dbMask = MGF(seed , (emLen-hLen)). + maskGenerationFunction1( + encodedMsg, + (short) encodedMsgOff, + hLen, + (short) (encodedMsgLen - hLen), + tmpArray, + (short) 0); + + // Get the DB value. DB = (maskedDB ^ dbMask) + // DB = Hash(P)||00||01||Msg, where P is encoding parameters. (P = NULL) + for (short i = 0; i < (short) (encodedMsgLen - hLen); i++) { + // Store the DB inside encodeMsg itself. + encodedMsg[(short) (encodedMsgOff + i + hLen)] ^= tmpArray[i]; + } + + // Verify Hash. + md = MessageDigest.OneShot.open(hash); + Util.arrayFillNonAtomic(tmpArray, (short) 0, (short) 256, (byte) 0); + md.doFinal(tmpArray, (short) 0, (short) 0, tmpArray, (short) 0); + if (0 + != Util.arrayCompare( + encodedMsg, (short) (encodedMsgOff + hLen), tmpArray, (short) 0, hLen)) { + // Verification failed. + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + } + + // Find the Message block in DB. + // DB = Hash(P)||00||01||Msg, where P is encoding parameters. (P = NULL) + // The message will be located at the end of the Data block (DB). + // The DB block is first constructed by keeping the message at the end and + // to the message 0x01 byte is prepended. The hash of the + // encoding parameters is calculated and then copied from the + // starting of the block and a variable length of 0's are + // appended to the end of the hash till the 0x01 byte. + short start = (short) (encodedMsgOff + encodedMsgLen); + for (short i = (short) (encodedMsgOff + 2 * hLen); + i < (short) (encodedMsgOff + encodedMsgLen); + i++) { + if ((encodedMsg[i] != 0)) { + start = i; + break; + } + } + if ((start >= (short) (encodedMsgOff + encodedMsgLen)) || (encodedMsg[start] != 0x01)) { + // Bad Padding. + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + } + start++; // Message starting pos. + if (start < (short) (encodedMsgOff + encodedMsgLen)) { + // Copy the message + Util.arrayCopyNonAtomic( + encodedMsg, + start, + encodedMsg, + encodedMsgOff, + (short) (encodedMsgLen - (start - encodedMsgOff))); + } + return (short) (encodedMsgLen - (start - encodedMsgOff)); + + } finally { + if (md != null) { + md.close(); + } + Util.arrayFillNonAtomic(tmpArray, (short) 0, KMAndroidSEProvider.TMP_ARRAY_SIZE, (byte) 0); + } + } +} diff --git a/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMSEProvider.java b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMSEProvider.java new file mode 100644 index 0000000..9313c04 --- /dev/null +++ b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMSEProvider.java @@ -0,0 +1,782 @@ +/* + * Copyright(C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" (short)0IS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.javacard.seprovider; + +import org.globalplatform.upgrade.Element; + +/** + * KMSEProvider is facade to use SE specific methods. The main intention of this interface is to + * abstract the cipher, signature and backup and restore related functions. The instance of this + * interface is created by the singleton KMSEProviderImpl class for each provider. At a time there + * can be only one provider in the applet package. + */ +public interface KMSEProvider { + + /** + * This function tells if boot signal event is supported or not. + * + * @return true if supported, false otherwise. + */ + boolean isBootSignalEventSupported(); + + /** + * This function tells if the device is booted or not. + * + * @return true if device booted, false otherwise. + */ + boolean isDeviceRebooted(); + + /** + * This function is supposed to be used to reset the device booted stated after set boot param is + * handled + * + * @param resetBootFlag is false if event has been handled + */ + void clearDeviceBooted(boolean resetBootFlag); + + /** + * Create a symmetric key instance. If the algorithm and/or keysize are not supported then it + * should throw a CryptoException. + * + * @param alg will be KMType.AES, KMType.DES or KMType.HMAC. + * @param keysize will be 128 or 256 for AES or DES. It can be 64 to 512 (multiple of 8) for HMAC. + * @param buf is the buffer in which key has to be returned + * @param startOff is the start offset. + * @return length of the data in the buf. This should match the keysize (in bytes). + */ + short createSymmetricKey(byte alg, short keysize, byte[] buf, short startOff); + + /** + * Create a asymmetric key pair. If the algorithms are not supported then it should throw a + * CryptoException. For RSA the public key exponent must always be 0x010001. The key size of RSA + * key pair must be 2048 bits and key size of EC key pair must be for p256 curve. + * + * @param alg will be KMType.RSA or KMType.EC. + * @param privKeyBuf is the buffer to return the private key exponent in case of RSA or private + * key in case of EC. + * @param privKeyStart is the start offset. + * @param privKeyMaxLength is the maximum length of this private key buffer. + * @param pubModBuf is the buffer to return the modulus in case of RSA or public key in case of + * EC. + * @param pubModStart is the start of offset. + * @param pubModMaxLength is the maximum length of this public key buffer. + * @param lengths is the actual length of the key pair - lengths[0] should be private key and + * lengths[1] should be public key. + */ + void createAsymmetricKey( + byte alg, + byte[] privKeyBuf, + short privKeyStart, + short privKeyMaxLength, + byte[] pubModBuf, + short pubModStart, + short pubModMaxLength, + short[] lengths); + + /** + * Initializes the trusted confirmation operation. + * + * @param computedHmacKey Instance of the computed Hmac key. + * @return instance of KMOperation. + */ + KMOperation initTrustedConfirmationSymmetricOperation(KMKey computedHmacKey); + + /** + * Verify that the imported key is valid. If the algorithm and/or keysize are not supported then + * it should throw a CryptoException. + * + * @param alg will be KMType.AES, KMType.DES or KMType.HMAC. + * @param keysize will be 128 or 256 for AES or DES. It can be 64 to 512 (multiple of 8) for HMAC. + * @param buf is the buffer that contains the symmetric key. + * @param startOff is the start offset. + * @param length of the data in the buf. This should match the keysize (in bytes). + * @return true if the symmetric key is supported and valid. + */ + boolean importSymmetricKey(byte alg, short keysize, byte[] buf, short startOff, short length); + + /** + * Validate that the imported asymmetric key pair is valid. For RSA the public key exponent must + * always be 0x010001. The key size of RSA key pair must be 2048 bits and key size of EC key pair + * must be for p256 curve. If the algorithms are not supported then it should throw a + * CryptoException. + * + * @param alg will be KMType.RSA or KMType.EC. + * @param privKeyBuf is the buffer that contains the private key exponent in case of RSA or + * private key in case of EC. + * @param privKeyStart is the start offset. + * @param privKeyLength is the length of this private key buffer. + * @param pubModBuf is the buffer that contains the modulus in case of RSA or public key in case + * of EC. + * @param pubModStart is the start of offset. + * @param pubModLength is the length of this public key buffer. + * @return true if the key pair is supported and valid. + */ + boolean importAsymmetricKey( + byte alg, + byte[] privKeyBuf, + short privKeyStart, + short privKeyLength, + byte[] pubModBuf, + short pubModStart, + short pubModLength); + + /** + * This is a oneshot operation that generates random number of desired length. + * + * @param num is the buffer in which random number is returned to the applet. + * @param offset is start of the buffer. + * @param length indicates the size of buffer and desired length of random number in bytes. + */ + void newRandomNumber(byte[] num, short offset, short length); + + /** + * This is a oneshot operation that adds the entropy to the entropy pool. This operation + * corresponds to addRndEntropy command. This method may ignore the added entropy value if the SE + * provider does not support it. + * + * @param num is the buffer in which entropy value is given. + * @param offset is start of the buffer. + * @param length length of the buffer. + */ + void addRngEntropy(byte[] num, short offset, short length); + + /** + * This is a oneshot operation that generates and returns back a true random number. + * + * @param num is the buffer in which entropy value is returned. + * @param offset is start of the buffer. + * @param length length of the buffer. + */ + void getTrueRandomNumber(byte[] num, short offset, short length); + + /** + * This is a oneshot operation that performs encryption operation using AES GCM algorithm. It + * throws CryptoException if algorithm is not supported or if tag length is not equal to 16 or + * nonce length is not equal to 12. + * + * @param aesKey is the buffer that contains 128 bit or 256 bit aes key used to encrypt. + * @param aesKeyStart is the start in aes key buffer. + * @param aesKeyLen is the length of aes key buffer in bytes (16 or 32 bytes). + * @param data is the buffer that contains data to encrypt. + * @param dataStart is the start of the data buffer. + * @param dataLen is the length of the data buffer. + * @param encData is the buffer of the output encrypted data. + * @param encDataStart is the start of the encrypted data buffer. + * @param nonce is the buffer of nonce. + * @param nonceStart is the start of the nonce buffer. + * @param nonceLen is the length of the nonce buffer. + * @param authData is the authentication data buffer. + * @param authDataStart is the start of the authentication buffer. + * @param authDataLen is the length of the authentication buffer. + * @param authTag is the buffer to output authentication tag. + * @param authTagStart is the start of the buffer. + * @param authTagLen is the length of the buffer. + * @return length of the encrypted data. + */ + short aesGCMEncrypt( + byte[] aesKey, + short aesKeyStart, + short aesKeyLen, + byte[] data, + short dataStart, + short dataLen, + byte[] encData, + short encDataStart, + byte[] nonce, + short nonceStart, + short nonceLen, + byte[] authData, + short authDataStart, + short authDataLen, + byte[] authTag, + short authTagStart, + short authTagLen); + + /** + * This is a oneshot operation that performs decryption operation using AES GCM algorithm. It + * throws CryptoException if algorithm is not supported. + * + * @param aesKey is the buffer that contains 128 bit or 256 bit aes key used to encrypt. + * @param aesKeyStart is the start in aes key buffer. + * @param aesKeyLen is the length of aes key buffer in bytes (16 or 32 bytes). + * @param encData is the buffer of the input encrypted data. + * @param encDataStart is the start of the encrypted data buffer. + * @param encDataLen is the length of the data buffer. + * @param data is the buffer that contains output decrypted data. + * @param dataStart is the start of the data buffer. + * @param nonce is the buffer of nonce. + * @param nonceStart is the start of the nonce buffer. + * @param nonceLen is the length of the nonce buffer. + * @param authData is the authentication data buffer. + * @param authDataStart is the start of the authentication buffer. + * @param authDataLen is the length of the authentication buffer. + * @param authTag is the buffer to output authentication tag. + * @param authTagStart is the start of the buffer. + * @param authTagLen is the length of the buffer. + * @return true if the authentication is valid. + */ + boolean aesGCMDecrypt( + byte[] aesKey, + short aesKeyStart, + short aesKeyLen, + byte[] encData, + short encDataStart, + short encDataLen, + byte[] data, + short dataStart, + byte[] nonce, + short nonceStart, + short nonceLen, + byte[] authData, + short authDataStart, + short authDataLen, + byte[] authTag, + short authTagStart, + short authTagLen); + + /** + * This is a oneshot operation that performs key derivation function using cmac kdf (CKDF) as + * defined in android keymaster hal definition. + * + * @param hmacKey of pre-shared key. + * @param label is the label to be used for ckdf. + * @param labelStart is the start of label. + * @param labelLen is the length of the label. + * @param context is the context to be used for ckdf. + * @param contextStart is the start of the context + * @param contextLength is the length of the context + * @param key is the output buffer to return the derived key + * @param keyStart is the start of the output buffer. + * @return length of the derived key buffer in bytes. + */ + short cmacKDF( + KMKey hmacKey, + byte[] label, + short labelStart, + short labelLen, + byte[] context, + short contextStart, + short contextLength, + byte[] key, + short keyStart); + + /** + * This is a oneshot operation that signs the data using hmac algorithm. + * + * @param keyBuf is the buffer with hmac key. + * @param keyStart is the start of the buffer. + * @param keyLength is the length of the buffer which will be in bytes from 8 to 64. + * @param data is the buffer containing data to be signed. + * @param dataStart is the start of the data. + * @param dataLength is the length of the data. + * @param signature is the output signature buffer + * @param signatureStart is the start of the signature + * @return length of the signature buffer in bytes. + */ + short hmacSign( + byte[] keyBuf, + short keyStart, + short keyLength, + byte[] data, + short dataStart, + short dataLength, + byte[] signature, + short signatureStart); + + /** + * This is a oneshot operation that signs the data using hmac algorithm. + * + * @param hmacKey is the KMHmacKey. + * @param data is the buffer containing data to be signed. + * @param dataStart is the start of the data. + * @param dataLength is the length of the data. + * @param signature is the output signature buffer + * @param signatureStart is the start of the signature + * @return length of the signature buffer in bytes. + */ + short hmacSign( + Object hmacKey, + byte[] data, + short dataStart, + short dataLength, + byte[] signature, + short signatureStart); + + /** + * This is a oneshot operation that signs the data using hmac algorithm. This is used to derive + * the key, which is used to encrypt the keyblob. + * + * @param masterkey of masterkey. + * @param data is the buffer containing data to be signed. + * @param dataStart is the start of the data. + * @param dataLength is the length of the data. + * @param signature is the output signature buffer + * @param signatureStart is the start of the signature + * @return length of the signature buffer in bytes. + */ + short hmacKDF( + KMKey masterkey, + byte[] data, + short dataStart, + short dataLength, + byte[] signature, + short signatureStart); + + /** + * This is a oneshot operation that verifies the signature using hmac algorithm. + * + * @param keyBuf is the buffer with hmac key. + * @param keyStart is the start of the buffer. + * @param keyLength is the length of the buffer which will be in bytes from 8 to 64. + * @param data is the buffer containing data. + * @param dataStart is the start of the data. + * @param dataLength is the length of the data. + * @param signature is the signature buffer. + * @param signatureStart is the start of the signature buffer. + * @param signatureLen is the length of the signature buffer in bytes. + * @return true if the signature matches. + */ + boolean hmacVerify( + KMKey hmacKey, + byte[] data, + short dataStart, + short dataLength, + byte[] signature, + short signatureStart, + short signatureLen); + + /** + * This is a oneshot operation that decrypts the data using RSA algorithm with oaep256 padding. + * The public exponent is always 0x010001. It throws CryptoException if OAEP encoding validation + * fails. + * + * @param privExp is the private exponent (2048 bit) buffer. + * @param privExpStart is the start of the private exponent buffer. + * @param privExpLength is the length of the private exponent buffer in bytes. + * @param modBuffer is the modulus (2048 bit) buffer. + * @param modOff is the start of the modulus buffer. + * @param modLength is the length of the modulus buffer in bytes. + * @param inputDataBuf is the buffer of the input data. + * @param inputDataStart is the start of the input data buffer. + * @param inputDataLength is the length of the input data buffer in bytes. + * @param outputDataBuf is the output buffer that contains the decrypted data. + * @param outputDataStart is the start of the output data buffer. + * @return length of the decrypted data. + */ + short rsaDecipherOAEP256( + byte[] privExp, + short privExpStart, + short privExpLength, + byte[] modBuffer, + short modOff, + short modLength, + byte[] inputDataBuf, + short inputDataStart, + short inputDataLength, + byte[] outputDataBuf, + short outputDataStart); + + /** + * Implementation of HKDF as per RFC5869 https://datatracker.ietf.org/doc/html/rfc5869#section-2 + * + * @param ikm is the buffer containing input key material. + * @param ikmOff is the start of the input key. + * @param ikmLen is the length of the input key. + * @param salt is the buffer containing the salt. + * @param saltOff is the start of the salt buffer. + * @param saltLen is the length of the salt buffer. + * @param info is the buffer containing the application specific information + * @param infoOff is the start of the info buffer. + * @param infoLen is the length of the info buffer. + * @param out is the output buffer. + * @param outOff is the start of the output buffer. + * @param outLen is the length of the expected out buffer. + * @return Length of the out buffer which is outLen. + */ + short hkdf( + byte[] ikm, + short ikmOff, + short ikmLen, + byte[] salt, + short saltOff, + short saltLen, + byte[] info, + short infoOff, + short infoLen, + byte[] out, + short outOff, + short outLen); + + /** + * This function performs ECDH key agreement and generates a secret. + * + * @param privKey is the buffer containing the private key from first party. + * @param privKeyOff is the offset of the private key buffer. + * @param privKeyLen is the length of the private key buffer. + * @param publicKey is the buffer containing the public key from second party. + * @param publicKeyOff is the offset of the public key buffer. + * @param publicKeyLen is the length of the public key buffer. + * @param secret is the output buffer. + * @param secretOff is the offset of the output buffer. + * @return The length of the secret. + */ + short ecdhKeyAgreement( + byte[] privKey, + short privKeyOff, + short privKeyLen, + byte[] publicKey, + short publicKeyOff, + short publicKeyLen, + byte[] secret, + short secretOff); + + /** + * This is a oneshort operation that verifies the data using EC public key + * + * @param pubKey is the public key buffer. + * @param pubKeyOffset is the start of the public key buffer. + * @param pubKeyLen is the length of the public key. + * @param inputDataBuf is the buffer of the input data. + * @param inputDataStart is the start of the input data buffer. + * @param inputDataLength is the length of the input data buffer in bytes. + * @param signatureDataBuf is the buffer the signature input data. + * @param signatureDataStart is the start of the signature input data. + * @param signatureDataLen is the length of the signature input data. + * @return true if verification is successful, otherwise false. + */ + boolean ecVerify256( + byte[] pubKey, + short pubKeyOffset, + short pubKeyLen, + byte[] inputDataBuf, + short inputDataStart, + short inputDataLength, + byte[] signatureDataBuf, + short signatureDataStart, + short signatureDataLen); + + /** + * This is a oneshot operation that signs the data using device unique key. + * + * @param ecPrivKey instance of KMECDeviceUniqueKey to sign the input data. + * @param inputDataBuf is the buffer of the input data. + * @param inputDataStart is the start of the input data buffer. + * @param inputDataLength is the length of the input data buffer in bytes. + * @param outputDataBuf is the output buffer that contains the signature. + * @param outputDataStart is the start of the output data buffer. + * @return length of the decrypted data. + */ + short signWithDeviceUniqueKey( + KMKey deviceUniqueKey, + byte[] inputDataBuf, + short inputDataStart, + short inputDataLength, + byte[] outputDataBuf, + short outputDataStart); + + short ecSign256( + byte[] secret, + short secretStart, + short secretLength, + byte[] inputDataBuf, + short inputDataStart, + short inputDataLength, + byte[] outputDataBuf, + short outputDataStart); + + short rsaSign256Pkcs1( + byte[] secret, + short secretStart, + short secretLength, + byte[] modBuf, + short modStart, + short modLength, + byte[] inputDataBuf, + short inputDataStart, + short inputDataLength, + byte[] outputDataBuf, + short outputDataStart); + + /** + * This creates a persistent operation for signing, verify, encryption and decryption using HMAC, + * AES and DES algorithms when keymaster hal's beginOperation function is executed. The + * KMOperation instance can be reclaimed by the seProvider when KMOperation is finished or + * aborted. It throws CryptoException if algorithm is not supported. + * + * @param purpose is KMType.ENCRYPT or KMType.DECRYPT for AES and DES algorithm. It will be + * KMType.SIGN and KMType.VERIFY for HMAC algorithm + * @param alg is KMType.HMAC, KMType.AES or KMType.DES. + * @param digest is KMType.SHA2_256 in case of HMAC else it will be KMType.DIGEST_NONE. + * @param padding is KMType.PADDING_NONE or KMType.PKCS7 (in case of AES and DES). + * @param blockMode is KMType.CTR, KMType.GCM. KMType.CBC or KMType.ECB for AES or DES else it is + * 0. + * @param keyBuf is aes, des or hmac key buffer. + * @param keyStart is the start of the key buffer. + * @param keyLength is the length of the key buffer. + * @param ivBuf is the iv buffer (in case on AES and DES algorithm without ECB mode) + * @param ivStart is the start of the iv buffer. + * @param ivLength is the length of the iv buffer. It will be zero in case of HMAC and AES/DES + * with ECB mode. + * @param macLength is the mac length in case of signing operation for hmac algorithm. + * @return KMOperation instance. + */ + KMOperation initSymmetricOperation( + byte purpose, + byte alg, + byte digest, + byte padding, + byte blockMode, + byte[] keyBuf, + short keyStart, + short keyLength, + byte[] ivBuf, + short ivStart, + short ivLength, + short macLength); + + /** + * This creates a persistent operation for signing, verify, encryption and decryption using HMAC, + * AES and DES algorithms when keymaster hal's beginOperation function is executed. The + * KMOperation instance can be reclaimed by the seProvider when KMOperation is finished or + * aborted. It throws CryptoException if algorithm is not supported. + * + * @param purpose is KMType.ENCRYPT or KMType.DECRYPT for AES and DES algorithm. It will be + * KMType.SIGN and KMType.VERIFY for HMAC algorithm + * @param alg is KMType.HMAC, KMType.AES or KMType.DES. + * @param digest is KMType.SHA2_256 in case of HMAC else it will be KMType.DIGEST_NONE. + * @param padding is KMType.PADDING_NONE or KMType.PKCS7 (in case of AES and DES). + * @param blockMode is KMType.CTR, KMType.GCM. KMType.CBC or KMType.ECB for AES or DES else it is + * 0. + * @param key is a key object. + * @param interfaceType defines the type of key in the key object. + * @param ivBuf is the iv buffer (in case on AES and DES algorithm without ECB mode) + * @param ivStart is the start of the iv buffer. + * @param ivLength is the length of the iv buffer. It will be zero in case of HMAC and AES/DES + * with ECB mode. + * @param macLength is the mac length in case of signing operation for hmac algorithm. + * @param oneShot if true, creates oneshot operation. + * @return KMOperation instance. + */ + KMOperation initSymmetricOperation( + byte purpose, + byte alg, + byte digest, + byte padding, + byte blockMode, + Object key, + byte interfaceType, + byte[] ivBuf, + short ivStart, + short ivLength, + short macLength, + boolean oneShot); + + /** + * This function creates an Operation instance only for RKP module. + * + * @param purpose is KMType.ENCRYPT or KMType.DECRYPT for AES and DES algorithm. It will be + * KMType.SIGN and KMType.VERIFY for HMAC algorithm + * @param alg is KMType.HMAC, KMType.AES or KMType.DES. + * @param digest is KMType.SHA2_256 in case of HMAC else it will be KMType.DIGEST_NONE. + * @param padding is KMType.PADDING_NONE or KMType.PKCS7 (in case of AES and DES). + * @param blockMode is KMType.CTR, KMType.GCM. KMType.CBC or KMType.ECB for AES or DES else it is + * 0. + * @param keyBuf is aes, des or hmac key buffer. + * @param keyStart is the start of the key buffer. + * @param keyLength is the length of the key buffer. + * @param ivBuf is the iv buffer (in case on AES and DES algorithm without ECB mode) + * @param ivStart is the start of the iv buffer. + * @param ivLength is the length of the iv buffer. It will be zero in case of HMAC and AES/DES + * with ECB mode. + * @param macLength is the mac length in case of signing operation for hmac algorithm. + * @return KMOperation instance. + */ + KMOperation getRkpOperation( + byte purpose, + byte alg, + byte digest, + byte padding, + byte blockMode, + byte[] keyBuf, + short keyStart, + short keyLength, + byte[] ivBuf, + short ivStart, + short ivLength, + short macLength); + + /** + * This creates a persistent operation for signing, verify, encryption and decryption using RSA + * and EC algorithms when keymaster hal's beginOperation function is executed. For RSA the public + * exponent is always 0x0100101. For EC the curve is always p256. The KMOperation instance can be + * reclaimed by the seProvider when KMOperation is finished or aborted. It throws CryptoException + * if algorithm is not supported. + * + * @param purpose is KMType.ENCRYPT or KMType.DECRYPT for RSA. It will be * KMType.SIGN and + * KMType.VERIFY for RSA and EC algorithms. + * @param alg is KMType.RSA or KMType.EC algorithms. + * @param padding is KMType.PADDING_NONE or KMType.RSA_OAEP, KMType.RSA_PKCS1_1_5_ENCRYPT, + * KMType.RSA_PKCS1_1_5_SIGN or KMType.RSA_PSS. + * @param digest is KMType.DIGEST_NONE or KMType.SHA2_256. + * @param mgfDigest is the MGF digest. + * @param privKeyBuf is the private key in case of EC or private key exponent is case of RSA. + * @param privKeyStart is the start of the private key. + * @param privKeyLength is the length of the private key. + * @param pubModBuf is the modulus (in case of RSA) or public key (in case of EC). + * @param pubModStart is the start of the modulus. + * @param pubModLength is the length of the modulus. + * @return KMOperation instance that can be executed. + */ + KMOperation initAsymmetricOperation( + byte purpose, + byte alg, + byte padding, + byte digest, + byte mgfDigest, + byte[] privKeyBuf, + short privKeyStart, + short privKeyLength, + byte[] pubModBuf, + short pubModStart, + short pubModLength); + + /** + * This function tells if applet is upgrading or not. + * + * @return true if upgrading, otherwise false. + */ + boolean isUpgrading(); + + /** + * This function generates an AES Key of keySizeBits, which is used as an master key. This + * generated key is maintained by the SEProvider. This function should be called only once at the + * time of installation. + * + * @param instance of the masterkey. + * @param keySizeBits key size in bits. + * @return An instance of KMMasterKey. + */ + KMKey createMasterKey(KMKey masterKey, short keySizeBits); + + /** + * This function creates an HMACKey and initializes the key with the provided input key data. + * + * @param keyData buffer containing the key data. + * @param offset start of the buffer. + * @param length length of the buffer. + * @return An instance of the KMComputedHmacKey. + */ + KMKey createComputedHmacKey(KMKey computedHmacKey, byte[] keyData, short offset, short length); + + /** Returns true if factory provisioned attestation key is supported. */ + boolean isAttestationKeyProvisioned(); + + /** + * Returns algorithm type of the attestation key. It can be KMType.EC or KMType.RSA if the + * attestation key is provisioned in the factory. + */ + short getAttestationKeyAlgorithm(); + + /** + * Creates an ECKey instance and sets the public and private keys to it. + * + * @param testMode to indicate if current execution is for test or production. + * @param pubKey buffer containing the public key. + * @param pubKeyOff public key buffer start offset. + * @param pubKeyLen public key buffer length. + * @param privKey buffer containing the private key. + * @param privKeyOff private key buffer start offset. + * @param privKeyLen private key buffer length. + * @return instance of KMDeviceUniqueKey. + */ + KMKey createRkpDeviceUniqueKeyPair( + KMKey key, + byte[] pubKey, + short pubKeyOff, + short pubKeyLen, + byte[] privKey, + short privKeyOff, + short privKeyLen); + + /** + * This is a one-shot operation the does digest of the input mesage. + * + * @param inBuff input buffer to be digested. + * @param inOffset start offset of the input buffer. + * @param inLength length of the input buffer. + * @param outBuff is the output buffer that contains the digested data. + * @param outOffset start offset of the digested output buffer. + * @return length of the digested data. + */ + short messageDigest256( + byte[] inBuff, short inOffset, short inLength, byte[] outBuff, short outOffset); + + /** + * This function generates a HMAC key from the provided key buffers. + * + * @param presharedKey instance of the presharedkey. + * @param key buffer containing the key data. + * @param offset start offset of the buffer. + * @param length is the length of the key. + * @return instance of KMPresharedKey. + */ + KMKey createPreSharedKey(KMKey presharedKey, byte[] key, short offset, short length); + + /** + * This function saves the key objects while upgrade. + * + * @param element instance of the Element class where the objects to be stored. + * @param interfaceType the type interface of the parent object. + * @param object instance of the object to be saved. + */ + void onSave(Element element, byte interfaceType, Object object); + + /** + * This function restores the the object from element instance. + * + * @param element instance of the Element class. + * @return restored object. + */ + Object onRestore(Element element); + + /** + * This function returns the count of the primitive bytes required to be stored by the + * implementation of the interface type. + * + * @param interfaceType type interface of the parent object. + * @return count of the primitive bytes. + */ + short getBackupPrimitiveByteCount(byte interfaceType); + + /** + * This function returns the object count required to be stored by the implementation of the + * interface type. + * + * @param interfaceType type interface of the parent object. + * @return count of the objects. + */ + short getBackupObjectCount(byte interfaceType); + + /** + * This function creates an HMACKey and initializes the key with the provided input key data. + * + * @param keyData buffer containing the key data. + * @param offset start of the buffer. + * @param length length of the buffer. + * @return An instance of the KMRkpMacKey. + */ + KMKey createRkpMacKey(KMKey createComputedHmacKey, byte[] keyData, short offset, short length); +} diff --git a/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMType.java b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMType.java new file mode 100644 index 0000000..82cbe26 --- /dev/null +++ b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMType.java @@ -0,0 +1,79 @@ +/* + * Copyright(C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.javacard.seprovider; + +/** + * This class declares all types, tag types, and tag keys. It also establishes basic structure of + * any KMType i.e. struct{byte type, short length, value} where value can any of the KMType. Also, + * KMType refers to transient memory heap in the repository. Finally KMType's subtypes are singleton + * prototype objects which just cast the structure over contiguous memory buffer. + */ +public abstract class KMType { + + public static final short INVALID_VALUE = (short) 0x8000; + + // Algorithm Enum Tag key and values + public static final short ALGORITHM = 0x0002; + public static final byte RSA = 0x01; + public static final byte DES = 0x21; + public static final byte EC = 0x03; + public static final byte AES = 0x20; + public static final byte HMAC = (byte) 0x80; + + // EcCurve Enum Tag key and values. + public static final short ECCURVE = 0x000A; + public static final byte P_224 = 0x00; + public static final byte P_256 = 0x01; + public static final byte P_384 = 0x02; + public static final byte P_521 = 0x03; + + // Purpose + public static final short PURPOSE = 0x0001; + public static final byte ENCRYPT = 0x00; + public static final byte DECRYPT = 0x01; + public static final byte SIGN = 0x02; + public static final byte VERIFY = 0x03; + public static final byte DERIVE_KEY = 0x04; + public static final byte WRAP_KEY = 0x05; + public static final byte AGREE_KEY = 0x06; + public static final byte ATTEST_KEY = (byte) 0x07; + // Block mode + public static final short BLOCK_MODE = 0x0004; + public static final byte ECB = 0x01; + public static final byte CBC = 0x02; + public static final byte CTR = 0x03; + public static final byte GCM = 0x20; + + // Digest + public static final short DIGEST = 0x0005; + public static final byte DIGEST_NONE = 0x00; + public static final byte MD5 = 0x01; + public static final byte SHA1 = 0x02; + public static final byte SHA2_224 = 0x03; + public static final byte SHA2_256 = 0x04; + public static final byte SHA2_384 = 0x05; + public static final byte SHA2_512 = 0x06; + + // Padding mode + public static final short PADDING = 0x0006; + public static final byte PADDING_NONE = 0x01; + public static final byte RSA_OAEP = 0x02; + public static final byte RSA_PSS = 0x03; + public static final byte RSA_PKCS1_1_5_ENCRYPT = 0x04; + public static final byte RSA_PKCS1_1_5_SIGN = 0x05; + public static final byte PKCS7 = 0x40; +} diff --git a/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMUpgradable.java b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMUpgradable.java new file mode 100644 index 0000000..420b2c7 --- /dev/null +++ b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMUpgradable.java @@ -0,0 +1,30 @@ +/* + * Copyright(C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" (short)0IS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.javacard.seprovider; + +import org.globalplatform.upgrade.Element; + +/** This interface helps in storing and restoring the applet data during the applet upgrades. */ +public interface KMUpgradable { + + void onSave(Element ele); + + void onRestore(Element ele, short oldVersion, short currentVersion); + + short getBackupPrimitiveByteCount(); + + short getBackupObjectCount(); +} |