diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAESKey.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAESKey.java new file mode 100644 index 00000000..afdfb08e --- /dev/null +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAESKey.java @@ -0,0 +1,61 @@ +/* + * 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.keymaster; + +import org.globalplatform.upgrade.Element; + +import com.android.javacard.keymaster.KMMasterKey; + +import javacard.security.AESKey; + +public class KMAESKey implements KMMasterKey { + private AESKey aesKey; + + public KMAESKey(AESKey key) { + aesKey = key; + } + + public void setKey(byte[] keyData, short kOff) { + aesKey.setKey(keyData, kOff); + } + + public AESKey getKey() { + return aesKey; + } + + public short getKeySizeBits() { + return aesKey.getSize(); + } + + public static void onSave(Element element, KMAESKey kmKey) { + element.write(kmKey.aesKey); + } + + public static KMAESKey onRestore(Element element) { + AESKey aesKey = (AESKey) element.readObject(); + KMAESKey kmKey = new KMAESKey(aesKey); + return kmKey; + } + + public static short getBackupPrimitiveByteCount() { + return (short) 0; + } + + public static short getBackupObjectCount() { + return (short) 1; + } + +} diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java index 99dd47ce..bb6a2e86 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java @@ -1,3 +1,18 @@ +/* + * 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.keymaster; import org.globalplatform.upgrade.Element; diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEProvider.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEProvider.java index 20417d0f..78b90b57 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEProvider.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEProvider.java @@ -37,6 +37,13 @@ import javacardx.crypto.AEADCipher; import javacardx.crypto.Cipher; +import com.android.javacard.keymaster.KMAESKey; +import com.android.javacard.keymaster.KMAttestationKey; +import com.android.javacard.keymaster.KMECPrivateKey; +import com.android.javacard.keymaster.KMHmacKey; +import com.android.javacard.keymaster.KMMasterKey; +import com.android.javacard.keymaster.KMPreSharedKey; + public class KMAndroidSEProvider implements KMSEProvider { // static final variables // -------------------------------------------------------------- @@ -105,6 +112,7 @@ public class KMAndroidSEProvider implements KMSEProvider { public static final byte KEYSIZE_128_OFFSET = 0x00; public static final byte KEYSIZE_256_OFFSET = 0x01; public static final short TMP_ARRAY_SIZE = 256; + private static final short RSA_KEY_SIZE = 256; public static final short CERT_CHAIN_MAX_SIZE = 2500;//First 2 bytes for length. final byte[] CIPHER_ALGS = { @@ -158,6 +166,9 @@ public class KMAndroidSEProvider implements KMSEProvider { private RandomData rng; //For storing root certificate and intermediate certificates. private byte[] certificateChain; + private KMAESKey masterKey; + private KMECPrivateKey attestationKey; + private KMHmacKey preSharedKey; private static KMAndroidSEProvider androidSEProvider = null; @@ -177,7 +188,8 @@ public KMAndroidSEProvider() { hmacKey = (HMACKey) KeyBuilder.buildKey(KeyBuilder.TYPE_HMAC, (short) 512, false); rsaKeyPair = new KeyPair(KeyPair.ALG_RSA, KeyBuilder.LENGTH_RSA_2048); - initECKey(); + ecKeyPair = new KeyPair(KeyPair.ALG_EC_FP, KeyBuilder.LENGTH_EC_FP_256); + initECKey(ecKeyPair); // Re-usable cipher and signature instances cipherPool = new Object[(short) (CIPHER_ALGS.length * 4)]; @@ -210,8 +222,7 @@ public void clean() { Util.arrayFillNonAtomic(tmpArray, (short) 0, (short) 256, (byte) 0); } - private void initECKey() { - ecKeyPair = new KeyPair(KeyPair.ALG_EC_FP, KeyBuilder.LENGTH_EC_FP_256); + private void initECKey(KeyPair ecKeyPair) { ECPrivateKey privKey = (ECPrivateKey) ecKeyPair.getPrivate(); ECPublicKey pubkey = (ECPublicKey) ecKeyPair.getPublic(); pubkey.setFieldFP(secp256r1_P, (short) 0, (short) secp256r1_P.length); @@ -522,13 +533,27 @@ public void createAsymmetricKey(byte alg, byte[] privKeyBuf, 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(); - lengths[0] = privKey.getExponent(privKeyBuf, privKeyStart); - lengths[1] = privKey.getModulus(pubModBuf, pubModStart); - if (lengths[0] > privKeyLength || lengths[1] > pubModLength) { + //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(); @@ -600,13 +625,11 @@ public void addRngEntropy(byte[] num, short offset, short length) { rng.setSeed(num, offset, length); } - @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) { - + 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); } @@ -617,7 +640,6 @@ public short aesGCMEncrypt(byte[] aesKey, short aesKeyStart, short aesKeyLen, aesGcmCipher = (AEADCipher) Cipher.getInstance(AEADCipher.ALG_AES_GCM, false); } - AESKey key = createAESKey(aesKey, aesKeyStart, aesKeyLen); aesGcmCipher.init(key, Cipher.MODE_ENCRYPT, nonce, nonceStart, nonceLen); aesGcmCipher.updateAAD(authData, authDataStart, authDataLen); short ciphLen = aesGcmCipher.doFinal(secret, secretStart, secretLen, @@ -626,6 +648,32 @@ public short aesGCMEncrypt(byte[] aesKey, short aesKeyStart, short aesKeyLen, 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, @@ -648,8 +696,7 @@ public boolean aesGCMDecrypt(byte[] aesKey, short aesKeyStart, return verification; } - public HMACKey cmacKdf(byte[] keyMaterial, short keyMaterialStart, - short keyMaterialLen, byte[] label, short labelStart, short labelLen, + public HMACKey cmacKdf(KMPreSharedKey preSharedKey, byte[] label, short labelStart, short labelLen, byte[] context, short contextStart, short contextLength) { try { // This is hardcoded to requirement - 32 byte output with two concatenated @@ -667,10 +714,18 @@ public HMACKey cmacKdf(byte[] keyMaterial, short keyMaterialStart, // [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.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); - aesKeys[KEYSIZE_256_OFFSET].setKey(keyMaterial, (short) keyMaterialStart); - kdf.init(aesKeys[KEYSIZE_256_OFFSET], Signature.MODE_SIGN); + byte i = 1; short pos = 0; while (i <= n) { @@ -711,6 +766,21 @@ public short hmacSign(byte[] keyBuf, short keyStart, short keyLength, return hmacSign(key, data, dataStart, dataLength, mac, macStart); } + @Override + public short hmacKDF(KMMasterKey masterkey, byte[] data, short dataStart, + short dataLength, byte[] signature, short signatureStart) { + try { + AESKey aesKey = ((KMAESKey) masterkey).getKey(); + aesKey.getKey(tmpArray, (short) 0); + HMACKey key = createHMACKey(tmpArray, (short) 0, + (short) (aesKey.getSize() / 8)); + return hmacSign(key, data, dataStart, dataLength, signature, + signatureStart); + } finally { + clean(); + } + } + @Override public boolean hmacVerify(byte[] keyBuf, short keyStart, short keyLength, byte[] data, short dataStart, short dataLength, byte[] mac, @@ -733,17 +803,15 @@ public short rsaDecipherOAEP256(byte[] secret, short secretStart, outputDataBuf, (short) outputDataStart); } - public short ecSign256(byte[] secret, short secretStart, short secretLength, + public short ecSign256(KMAttestationKey attestationKey, byte[] inputDataBuf, short inputDataStart, short inputDataLength, byte[] outputDataBuf, short outputDataStart) { Signature.OneShot signer = null; try { - ECPrivateKey key = (ECPrivateKey) ecKeyPair.getPrivate(); - key.setS(secret, secretStart, secretLength); signer = Signature.OneShot.open(MessageDigest.ALG_SHA_256, Signature.SIG_CIPHER_ECDSA, Cipher.PAD_NULL); - signer.init(key, Signature.MODE_SIGN); + signer.init(((KMECPrivateKey)attestationKey).getPrivateKey(), Signature.MODE_SIGN); return signer.sign(inputDataBuf, inputDataStart, inputDataLength, outputDataBuf, outputDataStart); } finally { @@ -975,7 +1043,7 @@ public Cipher createRsaCipher(short padding, short digest, byte[] modBuffer, short modOff, short modLength) { try { byte cipherAlg = mapCipherAlg(KMType.RSA, (byte) padding, (byte) 0, (byte)digest); - // TODO Java Card does not support MGF1-SHA1 and digest as SHA256. + // Java Card does not support MGF1-SHA1 and digest as SHA256. // Both digest should be SHA256 as per Java Card, but as per Keymaster // MGF should use SHA1 and message digest should be SHA256. if (cipherAlg == Cipher.ALG_RSA_PKCS1_OAEP) { @@ -1116,12 +1184,11 @@ public KMAttestationCert getAttestationCert(boolean rsaCert) { } @Override - public short cmacKdf(byte[] keyMaterial, short keyMaterialStart, - short keyMaterialLen, byte[] label, short labelStart, short labelLen, - byte[] context, short contextStart, short contextLength, byte[] keyBuf, - short keyStart) { - HMACKey key = cmacKdf(keyMaterial, keyMaterialStart, keyMaterialLen, label, - labelStart, labelLen, context, contextStart, contextLength); + public short cmacKDF(KMPreSharedKey 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); } @@ -1187,25 +1254,100 @@ public void clearDeviceBooted(boolean resetBootFlag) { @Override public void onSave(Element element) { element.write(certificateChain); + KMAESKey.onSave(element, masterKey); + KMECPrivateKey.onSave(element, attestationKey); + KMHmacKey.onSave(element, preSharedKey); } @Override public void onRestore(Element element) { certificateChain = (byte[]) element.readObject(); + masterKey = KMAESKey.onRestore(element); + attestationKey = KMECPrivateKey.onRestore(element); + preSharedKey = KMHmacKey.onRestore(element); } @Override public short getBackupPrimitiveByteCount() { - return (short) 0; + short count = + (short) (KMAESKey.getBackupPrimitiveByteCount() + + KMECPrivateKey.getBackupPrimitiveByteCount() + + KMHmacKey.getBackupPrimitiveByteCount()); + return count; } @Override public short getBackupObjectCount() { - return (short) 1; + short count = + (short) (1 /*Certificate chain */ + + KMAESKey.getBackupObjectCount() + + KMECPrivateKey.getBackupObjectCount() + + KMHmacKey.getBackupObjectCount()); + return count; } @Override public boolean isUpgrading() { return UpgradeManager.isUpgrading(); } + + @Override + public KMMasterKey createMasterKey(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); + getTrueRandomNumber(tmpArray, (short) 0, keyLen); + masterKey.setKey(tmpArray, (short) 0); + } + return (KMMasterKey) masterKey; + } finally { + clean(); + } + } + + @Override + public KMAttestationKey createAttestationKey(byte[] keyData, short offset, + short length) { + if (attestationKey == null) { + // Strongbox supports only P-256 curve for EC key. + KeyPair ecKeyPair = new KeyPair(KeyPair.ALG_EC_FP, KeyBuilder.LENGTH_EC_FP_256); + initECKey(ecKeyPair); + attestationKey = new KMECPrivateKey(ecKeyPair); + } + attestationKey.setS(keyData, offset, length); + return (KMAttestationKey) attestationKey; + } + + @Override + public KMPreSharedKey createPresharedKey(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); + } + preSharedKey.setKey(keyData, offset, length); + return (KMPreSharedKey) preSharedKey; + } + + @Override + public KMMasterKey getMasterKey() { + return (KMMasterKey) masterKey; + } + + @Override + public KMAttestationKey getAttestationKey() { + return (KMAttestationKey) attestationKey; + } + + @Override + public KMPreSharedKey getPresharedKey() { + return (KMPreSharedKey) preSharedKey; + } } diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAttestationCertImpl.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAttestationCertImpl.java index 5a36e3c3..637a2fba 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAttestationCertImpl.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAttestationCertImpl.java @@ -1,7 +1,28 @@ +/* + * 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.keymaster; +import com.android.javacard.keymaster.KMAESKey; +import com.android.javacard.keymaster.KMByteBlob; +import com.android.javacard.keymaster.KMECPrivateKey; +import com.android.javacard.keymaster.KMMasterKey; + import javacard.framework.JCSystem; import javacard.framework.Util; +import javacard.security.AESKey; // The class encodes strongbox generated amd signed attestation certificate. This only encodes // required fields of the certificates. It is not meant to be generic X509 cert encoder. @@ -106,7 +127,6 @@ public static KMAttestationCert instance(boolean rsaCert) { } private static void init() { - // if (repo == null) repo = KMRepository.instance(); stack = null; stackPtr = 0; certStart = 0; @@ -262,7 +282,6 @@ private static void pushTbsCert(boolean rsaCert) { pushBytes(X509Subject, (short) 0, (short) X509Subject.length); pushValidity(); // issuer - der encoded - // pushBytes(repo.getCertDataBuffer(), repo.getIssuer(), repo.getIssuerLen()); pushBytes( KMByteBlob.cast(issuer).getBuffer(), KMByteBlob.cast(issuer).getStartOff(), @@ -283,23 +302,6 @@ private static void pushTbsCert(boolean rsaCert) { private static void pushExtensions() { short last = stackPtr; - // byte keyusage = 0; - // byte unusedBits = 8; - /* - if (KMEnumArrayTag.contains(KMType.PURPOSE, KMType.SIGN, hwParams)) { - keyusage = (byte) (keyusage | keyUsageSign); - unusedBits = 7; - } - if (KMEnumArrayTag.contains(KMType.PURPOSE, KMType.WRAP_KEY, hwParams)) { - keyusage = (byte) (keyusage | keyUsageKeyEncipher); - unusedBits = 5; - } - if (KMEnumArrayTag.contains(KMType.PURPOSE, KMType.DECRYPT, hwParams)) { - keyusage = (byte) (keyusage | keyUsageDataEncipher); - unusedBits = 4; - } - - */ if (keyUsage != 0) pushKeyUsage(keyUsage, unusedBits); pushKeyDescription(); pushSequenceHeader((short) (last - stackPtr)); @@ -544,7 +546,6 @@ private static void pushTag(short tag) { private static void pushRoT() { short last = stackPtr; // verified boot hash - // pushOctetString(repo.verifiedBootHash, (short) 0, (short) repo.verifiedBootHash.length); pushOctetString( KMByteBlob.cast(verifiedHash).getBuffer(), KMByteBlob.cast(verifiedHash).getStartOff(), @@ -649,7 +650,6 @@ private static void pushEnumTag(short tagId, byte val) { private static void pushIntegerTag(short tagId, byte[] buf, short start, short len) { short last = stackPtr; pushInteger(buf, start, len); - // pushIntegerHeader((short) (last - stackPtr)); pushTagIdHeader(tagId, (short) (last - stackPtr)); } // Ignore leading zeros. Only Unsigned Integers are required hence if MSB is set then add 0x00 @@ -774,12 +774,6 @@ public KMAttestationCert buffer(byte[] buf, short bufStart, short maxLen) { return this; } - @Override - public KMAttestationCert signingKey(short privKey) { - signPriv = privKey; - return this; - } - @Override public short getCertStart() { return certStart; @@ -808,11 +802,10 @@ public void build() { tbsLength = (short) (tbsLength - tbsOffset); pushSequenceHeader((short) (last - stackPtr)); certStart = stackPtr; - short sigLen = KMAndroidSEProvider.getInstance() + KMAndroidSEProvider androidSeProvider = KMAndroidSEProvider.getInstance(); + short sigLen = androidSeProvider .ecSign256( - KMByteBlob.cast(signPriv).getBuffer(), - KMByteBlob.cast(signPriv).getStartOff(), - KMByteBlob.cast(signPriv).length(), + androidSeProvider.getAttestationKey(), stack, tbsOffset, tbsLength, @@ -833,7 +826,7 @@ public void build() { public KMAttestationCert makeUniqueId(byte[] scratchPad, short scratchPadOff, byte[] creationTime, short timeOffset, short creationTimeLen, byte[] attestAppId, short appIdOff, short attestAppIdLen, - byte resetSinceIdRotation, byte[] key, short keyOff, short keyLen) { + byte resetSinceIdRotation, KMMasterKey masterKey) { // Concatenate T||C||R // temporal count T short temp = KMUtils.countTemporalCount(creationTime, timeOffset, @@ -852,7 +845,16 @@ public KMAttestationCert makeUniqueId(byte[] scratchPad, short scratchPadOff, scratchPadOff++; timeOffset = KMByteBlob.instance((short) 32); - appIdOff = KMAndroidSEProvider.getInstance().hmacSign(key, keyOff, keyLen, + //Get the key data from the master key and use it for HMAC Sign. + AESKey aesKey = ((KMAESKey) masterKey).getKey(); + short mKeyData = KMByteBlob.instance((short) (aesKey.getSize() / 8)); + aesKey.getKey( + KMByteBlob.cast(mKeyData).getBuffer(), + KMByteBlob.cast(mKeyData).getStartOff()); + appIdOff = KMAndroidSEProvider.getInstance().hmacSign( + KMByteBlob.cast(mKeyData).getBuffer(), /* Key */ + KMByteBlob.cast(mKeyData).getStartOff(), /* Key start*/ + KMByteBlob.cast(mKeyData).length(), /* Key length*/ scratchPad, /* data */ temp, /* data start */ scratchPadOff, /* data length */ diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMECPrivateKey.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMECPrivateKey.java new file mode 100644 index 00000000..fe6a636f --- /dev/null +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMECPrivateKey.java @@ -0,0 +1,68 @@ +/* + * 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.keymaster; + +import org.globalplatform.upgrade.Element; + +import com.android.javacard.keymaster.KMAESKey; +import com.android.javacard.keymaster.KMAttestationCert; +import com.android.javacard.keymaster.KMAttestationKey; + +import javacard.security.AESKey; +import javacard.security.ECPrivateKey; +import javacard.security.KeyPair; + +public class KMECPrivateKey implements KMAttestationKey { + + private KeyPair ecKeyPair; + + public KMECPrivateKey(KeyPair ecPair) { + ecKeyPair = ecPair; + } + + public void setS(byte[] buffer, short offset, short length) { + ECPrivateKey ecPriv = (ECPrivateKey) ecKeyPair.getPrivate(); + ecPriv.setS(buffer, offset, length); + } + + public short getS(byte[] buffer, short offset) { + ECPrivateKey ecPriv = (ECPrivateKey) ecKeyPair.getPrivate(); + return ecPriv.getS(buffer, offset); + } + + public ECPrivateKey getPrivateKey() { + return (ECPrivateKey) ecKeyPair.getPrivate(); + } + + public static void onSave(Element element, KMECPrivateKey kmKey) { + element.write(kmKey.ecKeyPair); + } + + public static KMECPrivateKey onRestore(Element element) { + KeyPair ecKey = (KeyPair) element.readObject(); + KMECPrivateKey kmKey = new KMECPrivateKey(ecKey); + return kmKey; + } + + public static short getBackupPrimitiveByteCount() { + return (short) 0; + } + + public static short getBackupObjectCount() { + return (short) 1; + } + +} diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMEcdsa256NoDigestSignature.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMEcdsa256NoDigestSignature.java index 727641c0..3f11a3b1 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMEcdsa256NoDigestSignature.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMEcdsa256NoDigestSignature.java @@ -1,3 +1,18 @@ +/* + * 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.keymaster; import javacard.security.CryptoException; diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMHmacKey.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMHmacKey.java new file mode 100644 index 00000000..b4ca3af4 --- /dev/null +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMHmacKey.java @@ -0,0 +1,59 @@ +/* + * 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.keymaster; + +import org.globalplatform.upgrade.Element; + +import com.android.javacard.keymaster.KMPreSharedKey; + +import javacard.security.HMACKey; + +public class KMHmacKey implements KMPreSharedKey { + private HMACKey hmacKey; + + public KMHmacKey(HMACKey key) { + hmacKey = key; + } + + public void setKey(byte[] keyData, short kOff, short length) { + hmacKey.setKey(keyData, kOff, length); + } + + public byte getKey(byte[] keyData, short kOff) { + return hmacKey.getKey(keyData, kOff); + } + + public short getKeySizeBits() { + return hmacKey.getSize(); + } + public static void onSave(Element element, KMHmacKey kmKey) { + element.write(kmKey.hmacKey); + } + + public static KMHmacKey onRestore(Element element) { + HMACKey hmacKey = (HMACKey) element.readObject(); + KMHmacKey kmKey = new KMHmacKey(hmacKey); + return kmKey; + } + + public static short getBackupPrimitiveByteCount() { + return (short) 0; + } + + public static short getBackupObjectCount() { + return (short) 1; + } +} diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMInstance.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMInstance.java index 3bf35e97..12655bc4 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMInstance.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMInstance.java @@ -1,3 +1,18 @@ +/* + * 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.keymaster; public class KMInstance { diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMOperationImpl.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMOperationImpl.java index 1c7ab32d..a38ee518 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMOperationImpl.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMOperationImpl.java @@ -1,3 +1,18 @@ +/* + * 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.keymaster; import javacard.framework.JCSystem; diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMRsa2048NoDigestSignature.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMRsa2048NoDigestSignature.java index 71c36a3d..991072e2 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMRsa2048NoDigestSignature.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMRsa2048NoDigestSignature.java @@ -1,3 +1,18 @@ +/* + * 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.keymaster; import javacard.framework.Util; diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMRsaOAEPEncoding.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMRsaOAEPEncoding.java index d4f4cb83..066d828f 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMRsaOAEPEncoding.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMRsaOAEPEncoding.java @@ -1,3 +1,18 @@ +/* + * 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.keymaster; import javacard.framework.JCSystem; @@ -80,13 +95,11 @@ public byte getAlgorithm() { @Override public byte getCipherAlgorithm() { - // TODO return 0; } @Override public byte getPaddingAlgorithm() { - // TODO return 0; } diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMUtils.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMUtils.java index cb8df259..65e83415 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMUtils.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMUtils.java @@ -1,3 +1,18 @@ +/* + * 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.keymaster; import javacard.framework.Util; @@ -332,29 +347,6 @@ public static void shiftRight(byte[] buf, short start) { } } - - // num1 must be greater then or equal to num2 and both must be positive - /*private short subtractIntegers(short num1, short num2) { - short buf = - repository.alloc((short)24); byte[] scratchPad = repository.getHeap(); - Util.arrayFillNonAtomic(scratchPad, buf, (short) 24, (byte) 0); - Util.arrayCopyNonAtomic(KMInteger.cast(num1).getBuffer(), - KMInteger.cast(num1).getStartOff(), scratchPad, - (short) (buf + 8 - KMInteger.cast(num1).length()), - KMInteger.cast(num1).length()); - Util.arrayCopyNonAtomic(KMInteger.cast(num2).getBuffer(), - KMInteger.cast(num2).getStartOff(), scratchPad, - (short) (buf + 16 - KMInteger.cast(num2).length()), - KMInteger.cast(num2).length()); - if (scratchPad[buf] < 0 || scratchPad[(short) (buf + 8)] < 0) - return KMType.INVALID_VALUE; - if (Util.arrayCompare(scratchPad, buf, scratchPad, (short) (buf + 8), - (short) 8) < 1) - return KMType.INVALID_VALUE; - subtract(scratchPad, buf, (short) (buf + 8), (short) (buf + 16)); - return KMInteger.uint_64(scratchPad, (short) (buf + 16)); - }*/ - public static void add(byte[] buf, short op1, short op2, short result) { byte index = 7; byte carry = 0; diff --git a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMAESKey.java b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMAESKey.java new file mode 100644 index 00000000..489d4f77 --- /dev/null +++ b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMAESKey.java @@ -0,0 +1,38 @@ +/* + * 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.keymaster; + +import javacard.security.AESKey; + +public class KMAESKey implements KMMasterKey { + private AESKey aesKey; + + public KMAESKey(AESKey key) { + aesKey = key; + } + + public void setKey(byte[] keyData, short kOff) { + aesKey.setKey(keyData, kOff); + } + + public byte getKey(byte[] keyData, short kOff) { + return aesKey.getKey(keyData, kOff); + } + + public short getKeySizeBits() { + return aesKey.getSize(); + } +} diff --git a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMAttestationCertImpl.java b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMAttestationCertImpl.java index cd9567d3..18e67e72 100644 --- a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMAttestationCertImpl.java +++ b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMAttestationCertImpl.java @@ -1,3 +1,18 @@ +/* + * 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.keymaster; import javacard.framework.JCSystem; @@ -106,7 +121,6 @@ public static KMAttestationCert instance(boolean rsaCert) { } private static void init() { - // if (repo == null) repo = KMRepository.instance(); stack = null; stackPtr = 0; certStart = 0; @@ -262,7 +276,6 @@ private static void pushTbsCert(boolean rsaCert) { pushBytes(X509Subject, (short) 0, (short) X509Subject.length); pushValidity(); // issuer - der encoded - // pushBytes(repo.getCertDataBuffer(), repo.getIssuer(), repo.getIssuerLen()); pushBytes( KMByteBlob.cast(issuer).getBuffer(), KMByteBlob.cast(issuer).getStartOff(), @@ -283,23 +296,6 @@ private static void pushTbsCert(boolean rsaCert) { private static void pushExtensions() { short last = stackPtr; - // byte keyusage = 0; - // byte unusedBits = 8; - /* - if (KMEnumArrayTag.contains(KMType.PURPOSE, KMType.SIGN, hwParams)) { - keyusage = (byte) (keyusage | keyUsageSign); - unusedBits = 7; - } - if (KMEnumArrayTag.contains(KMType.PURPOSE, KMType.WRAP_KEY, hwParams)) { - keyusage = (byte) (keyusage | keyUsageKeyEncipher); - unusedBits = 5; - } - if (KMEnumArrayTag.contains(KMType.PURPOSE, KMType.DECRYPT, hwParams)) { - keyusage = (byte) (keyusage | keyUsageDataEncipher); - unusedBits = 4; - } - - */ if (keyUsage != 0) pushKeyUsage(keyUsage, unusedBits); pushKeyDescription(); pushSequenceHeader((short) (last - stackPtr)); @@ -544,7 +540,6 @@ private static void pushTag(short tag) { private static void pushRoT() { short last = stackPtr; // verified boot hash - // pushOctetString(repo.verifiedBootHash, (short) 0, (short) repo.verifiedBootHash.length); pushOctetString( KMByteBlob.cast(verifiedHash).getBuffer(), KMByteBlob.cast(verifiedHash).getStartOff(), @@ -774,12 +769,6 @@ public KMAttestationCert buffer(byte[] buf, short bufStart, short maxLen) { return this; } - @Override - public KMAttestationCert signingKey(short privKey) { - signPriv = privKey; - return this; - } - @Override public short getCertStart() { return certStart; @@ -808,11 +797,10 @@ public void build() { tbsLength = (short) (tbsLength - tbsOffset); pushSequenceHeader((short) (last - stackPtr)); certStart = stackPtr; - short sigLen = KMJCardSimulator.getInstance() + KMJCardSimulator provider = KMJCardSimulator.getInstance(); + short sigLen = provider .ecSign256( - KMByteBlob.cast(signPriv).getBuffer(), - KMByteBlob.cast(signPriv).getStartOff(), - KMByteBlob.cast(signPriv).length(), + provider.getAttestationKey(), stack, tbsOffset, tbsLength, @@ -833,7 +821,7 @@ public void build() { public KMAttestationCert makeUniqueId(byte[] scratchPad, short scratchPadOff, byte[] creationTime, short timeOffset, short creationTimeLen, byte[] attestAppId, short appIdOff, short attestAppIdLen, - byte resetSinceIdRotation, byte[] key, short keyOff, short keyLen) { + byte resetSinceIdRotation, KMMasterKey masterKey) { // Concatenate T||C||R // temporal count T short temp = KMUtils.countTemporalCount(creationTime, timeOffset, @@ -851,8 +839,17 @@ public KMAttestationCert makeUniqueId(byte[] scratchPad, short scratchPadOff, scratchPad[scratchPadOff] = resetSinceIdRotation; scratchPadOff++; + //Get the key data from the master key + KMAESKey aesKey = (KMAESKey) masterKey; + short mKeyData = KMByteBlob.instance((short) (aesKey.getKeySizeBits() / 8)); + aesKey.getKey( + KMByteBlob.cast(mKeyData).getBuffer(), /* Key */ + KMByteBlob.cast(mKeyData).getStartOff()); /* Key start*/ timeOffset = KMByteBlob.instance((short) 32); - appIdOff = KMJCardSimulator.getInstance().hmacSign(key, keyOff, keyLen, + appIdOff = KMJCardSimulator.getInstance().hmacSign( + KMByteBlob.cast(mKeyData).getBuffer(), /* Key */ + KMByteBlob.cast(mKeyData).getStartOff(), /* Key start*/ + KMByteBlob.cast(mKeyData).length(), /* Key length*/ scratchPad, /* data */ temp, /* data start */ scratchPadOff, /* data length */ diff --git a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMCipher.java b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMCipher.java index 145fea9d..3e67ed5e 100644 --- a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMCipher.java +++ b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMCipher.java @@ -1,30 +1,21 @@ +/* + * 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.keymaster; public abstract class KMCipher { - /* - public static final byte CIPHER_RSA = 7; - public static final short PAD_PKCS1_OAEP = 9; - public static final short PAD_PKCS1_OAEP_SHA224 = 13; - public static final byte PAD_PKCS1_OAEP_SHA256 = 14; - public static final short PAD_PKCS1_OAEP_SHA384 = 15; - public static final short PAD_PKCS1_OAEP_SHA512 = 16; - public static final short PAD_NOPAD = 1; - public static final short PAD_PKCS1_PSS = 8; - public static final short PAD_NULL = 0; - public static final short PAD_PKCS7 = 31; // Not supported in javacard - public static final short ALG_DES_CBC_NOPAD = 1; - public static final short ALG_DES_ECB_NOPAD = 5; - public static final short ALG_AES_BLOCK_128_CBC_NOPAD= 13; - public static final short ALG_AES_BLOCK_128_ECB_NOPAD = 14; - public static final short ALG_AES_GCM = -13; - public static final short MODE_ENCRYPT = 2; - public static final short MODE_DECRYPT = 1; - public static final short PAD_PKCS1 = 7; - public static final short AES_BLOCK_SIZE = 16; - public static final short DES_BLOCK_SIZE = 8; - public static final short ALG_AES_CTR = -16; - - */ public static final short SUN_JCE = 0xE9; public abstract short doFinal(byte[] buffer, short startOff, short length, byte[] scratchPad, short i); diff --git a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMCipherImpl.java b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMCipherImpl.java index 264b74e5..bc07be8d 100644 --- a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMCipherImpl.java +++ b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMCipherImpl.java @@ -1,3 +1,18 @@ +/* + * 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.keymaster; import javacard.framework.Util; @@ -15,7 +30,6 @@ public class KMCipherImpl extends KMCipher{ private short cipherAlg; private short padding; private short mode; - private boolean verificationFlag; private short blockMode; KMCipherImpl(Cipher c){ cipher = c; @@ -44,7 +58,6 @@ public short doFinal(byte[] buffer, short startOff, short length, byte[] scratch return (short)sunCipher.doFinal(buffer,startOff,length,scratchPad,i); } catch (AEADBadTagException e) { e.printStackTrace(); - verificationFlag = false; KMException.throwIt(KMError.VERIFICATION_FAILED); } catch (ShortBufferException e) { e.printStackTrace(); diff --git a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMECPrivateKey.java b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMECPrivateKey.java new file mode 100644 index 00000000..65c069d1 --- /dev/null +++ b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMECPrivateKey.java @@ -0,0 +1,38 @@ +/* + * 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.keymaster; + +import javacard.security.ECPrivateKey; +import javacard.security.KeyPair; + +public class KMECPrivateKey implements KMAttestationKey { + + private KeyPair ecKeyPair; + + public KMECPrivateKey(KeyPair ecPair) { + ecKeyPair = ecPair; + } + + public void setS(byte[] buffer, short offset, short length) { + ECPrivateKey ecPriv = (ECPrivateKey) ecKeyPair.getPrivate(); + ecPriv.setS(buffer, offset, length); + } + + public ECPrivateKey getPrivateKey() { + return (ECPrivateKey) ecKeyPair.getPrivate(); + } + +} diff --git a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMEcdsa256NoDigestSignature.java b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMEcdsa256NoDigestSignature.java index 242499ed..42468363 100644 --- a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMEcdsa256NoDigestSignature.java +++ b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMEcdsa256NoDigestSignature.java @@ -1,3 +1,18 @@ +/* + * 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.keymaster; import java.math.BigInteger; diff --git a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMHmacKey.java b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMHmacKey.java new file mode 100644 index 00000000..8b75827a --- /dev/null +++ b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMHmacKey.java @@ -0,0 +1,38 @@ +/* + * 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.keymaster; + +import javacard.security.HMACKey; + +public class KMHmacKey implements KMPreSharedKey { + private HMACKey hmacKey; + + public KMHmacKey(HMACKey key) { + hmacKey = key; + } + + public void setKey(byte[] keyData, short kOff, short length) { + hmacKey.setKey(keyData, kOff, length); + } + + public byte getKey(byte[] keyData, short kOff) { + return hmacKey.getKey(keyData, kOff); + } + + public short getKeySizeBits() { + return hmacKey.getSize(); + } +} diff --git a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimApplet.java b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimApplet.java index e1ec74f8..fd6dd910 100644 --- a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimApplet.java +++ b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimApplet.java @@ -1,3 +1,18 @@ +/* + * 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.keymaster; public class KMJCardSimApplet extends KMKeymasterApplet { diff --git a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimulator.java b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimulator.java index 08c3d0ea..664ade77 100644 --- a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimulator.java +++ b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimulator.java @@ -72,6 +72,7 @@ public class KMJCardSimulator implements KMSEProvider { public static final short ENTROPY_POOL_SIZE = 16; // simulator does not support 256 bit aes keys public static final byte[] aesICV = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; private static final short CERT_CHAIN_MAX_SIZE = 2500;//First 2 bytes for length. + private static final short RSA_KEY_SIZE = 256; public static boolean jcardSim = false; @@ -84,6 +85,9 @@ public class KMJCardSimulator implements KMSEProvider { private static byte[] entropyPool; private static byte[] rndNum; private byte[] certificateChain; + private KMAESKey masterKey; + private KMECPrivateKey attestationKey; + private KMHmacKey preSharedKey; private static KMJCardSimulator jCardSimulator = null; @@ -152,7 +156,6 @@ public AESKey createAESKey(short keysize) { return createAESKey(rndNum, (short)0, (short)rndNum.length); } - // @Override public AESKey createAESKey(byte[] buf, short startOff, short length) { AESKey key = null; short keysize = (short)(length * 8); @@ -163,9 +166,6 @@ public AESKey createAESKey(byte[] buf, short startOff, short length) { key = (AESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_AES, KeyBuilder.LENGTH_AES_256, false); key.setKey(buf, (short) startOff); } - // byte[] buffer = new byte[length]; - // Util.arrayCopyNonAtomic(buf, startOff, buffer, (short)0,length); - // print("AES Key", buffer); return key; } @@ -218,13 +218,27 @@ public void createAsymmetricKey(byte alg, byte[] privKeyBuf, short privKeyStart, 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(); - lengths[0] = privKey.getExponent(privKeyBuf,privKeyStart); - lengths[1] = privKey.getModulus(pubModBuf,pubModStart); - if(lengths[0] > privKeyLength || lengths[1] > pubModLength){ + //Copy exponent. + byte[] exp = new byte[RSA_KEY_SIZE]; + lengths[0] = privKey.getExponent(exp, (short)0); + if (lengths[0] > privKeyLength) CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); - } + Util.arrayFillNonAtomic(privKeyBuf, privKeyStart, privKeyLength, (byte)0); + Util.arrayCopyNonAtomic(exp, (short)0, + privKeyBuf, (short)(privKeyStart + privKeyLength - lengths[0]), lengths[0]); + //Copy modulus + byte[] mod = new byte[RSA_KEY_SIZE]; + lengths[1] = privKey.getModulus(mod, (short)0); + if (lengths[1] > pubModLength) + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + Util.arrayFillNonAtomic(pubModBuf, pubModStart, pubModLength, (byte)0); + Util.arrayCopyNonAtomic(mod, (short)0, + pubModBuf, (short)(pubModStart + pubModLength - lengths[1]), lengths[1]); break; case KMType.EC: KeyPair ecKey = createECKeyPair(); @@ -309,19 +323,6 @@ public short aesGCMEncrypt( if(keyLen != 32 && keyLen != 16){ CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); } - /*byte[] keyMaterial = new byte[key]; - short keySize = 16; - if(key.getSize() == 128){ - keyMaterial = new byte[16]; - }else if(key.getSize() == 256){ - keyMaterial = new byte[32]; - keySize = 32; - } - key.getKey(keyMaterial,(short)0); - - */ - //print("KeyMaterial Enc", keyMaterial); - //print("Authdata Enc", authData, authDataStart, authDataLen); java.security.Key aesKey = new SecretKeySpec(keyBuf,keyStart,keyLen, "AES"); // Create the cipher javax.crypto.Cipher cipher = null; @@ -357,7 +358,6 @@ public short aesGCMEncrypt( // Create auth data byte[] aad = new byte[authDataLen]; Util.arrayCopyNonAtomic(authData,authDataStart,aad,(short)0,authDataLen); - // print("AAD", aad); cipher.updateAAD(aad); // Encrypt secret short len = 0; @@ -398,73 +398,67 @@ public boolean aesGCMDecrypt( short authDataLen, byte[] authTag, short authTagStart, - short authTagLen) { - //Create the sun jce compliant aes key - if(keyLen != 32 && keyLen != 16){ + short authTagLen) { + // Create the sun jce compliant aes key + if (keyLen != 32 && keyLen != 16) { CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); } - /*byte[] keyMaterial = new byte[16]; - short keySize = 16; - if(key.getSize() == 128){ - keyMaterial = new byte[16]; - }else if(key.getSize() == 256){ - keyMaterial = new byte[32]; - keySize = 32; - } - key.getKey(keyMaterial,(short)0); - - */ - //print("KeyMaterial Dec", keyMaterial); - //print("Authdata Dec", authData, authDataStart, authDataLen); - java.security.Key aesKey = new SecretKeySpec(keyBuf,keyStart,keyLen, "AES"); + java.security.Key aesKey = new SecretKeySpec(keyBuf, keyStart, keyLen, + "AES"); // Create the cipher - javax.crypto.Cipher cipher = null; - try { - cipher = javax.crypto.Cipher.getInstance("AES/GCM/NoPadding", "SunJCE"); - } catch (NoSuchAlgorithmException e) { - e.printStackTrace(); - CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); - } catch (NoSuchProviderException e) { - e.printStackTrace(); - CryptoException.throwIt(CryptoException.INVALID_INIT); - } catch (NoSuchPaddingException e) { - e.printStackTrace(); - CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); - } - // Copy nonce - if(nonceLen != AES_GCM_NONCE_LENGTH){ - CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); - } - byte[] iv = new byte[AES_GCM_NONCE_LENGTH]; - Util.arrayCopyNonAtomic(nonce,nonceStart,iv,(short)0,AES_GCM_NONCE_LENGTH); - // Init Cipher - GCMParameterSpec spec = new GCMParameterSpec(authTagLen * 8, nonce,nonceStart,AES_GCM_NONCE_LENGTH); - try { - cipher.init(javax.crypto.Cipher.DECRYPT_MODE, aesKey, spec); - } catch (InvalidKeyException e) { - e.printStackTrace(); - CryptoException.throwIt(CryptoException.INVALID_INIT); - } catch (InvalidAlgorithmParameterException e) { - e.printStackTrace(); - CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); - } - // Create auth data - byte[] aad = new byte[authDataLen]; - Util.arrayCopyNonAtomic(authData,authDataStart,aad,(short)0,authDataLen); - cipher.updateAAD(aad); - // Append the auth tag at the end of data - byte[] inputBuf = new byte[(short)(encSecretLen + authTagLen)]; - Util.arrayCopyNonAtomic(encSecret,encSecretStart,inputBuf,(short)0,encSecretLen); - Util.arrayCopyNonAtomic(authTag,authTagStart,inputBuf,encSecretLen,authTagLen); - // Decrypt + javax.crypto.Cipher cipher = null; + try { + cipher = javax.crypto.Cipher.getInstance("AES/GCM/NoPadding", "SunJCE"); + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); + } catch (NoSuchProviderException e) { + e.printStackTrace(); + CryptoException.throwIt(CryptoException.INVALID_INIT); + } catch (NoSuchPaddingException e) { + e.printStackTrace(); + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + } + // Copy nonce + if (nonceLen != AES_GCM_NONCE_LENGTH) { + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + } + byte[] iv = new byte[AES_GCM_NONCE_LENGTH]; + Util.arrayCopyNonAtomic(nonce, nonceStart, iv, (short) 0, + AES_GCM_NONCE_LENGTH); + // Init Cipher + GCMParameterSpec spec = new GCMParameterSpec(authTagLen * 8, nonce, + nonceStart, AES_GCM_NONCE_LENGTH); + try { + cipher.init(javax.crypto.Cipher.DECRYPT_MODE, aesKey, spec); + } catch (InvalidKeyException e) { + e.printStackTrace(); + CryptoException.throwIt(CryptoException.INVALID_INIT); + } catch (InvalidAlgorithmParameterException e) { + e.printStackTrace(); + CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); + } + // Create auth data + byte[] aad = new byte[authDataLen]; + Util.arrayCopyNonAtomic(authData, authDataStart, aad, (short) 0, + authDataLen); + cipher.updateAAD(aad); + // Append the auth tag at the end of data + byte[] inputBuf = new byte[(short) (encSecretLen + authTagLen)]; + Util.arrayCopyNonAtomic(encSecret, encSecretStart, inputBuf, (short) 0, + encSecretLen); + Util.arrayCopyNonAtomic(authTag, authTagStart, inputBuf, encSecretLen, + authTagLen); + // Decrypt short len = 0; - byte[] outputBuf = new byte[cipher.getOutputSize((short)inputBuf.length)]; + byte[] outputBuf = new byte[cipher.getOutputSize((short) inputBuf.length)]; try { - len = (short)(cipher.doFinal(inputBuf,(short)0,(short)inputBuf.length,outputBuf,(short)0)); - }catch(AEADBadTagException e){ + len = (short) (cipher.doFinal(inputBuf, (short) 0, + (short) inputBuf.length, outputBuf, (short) 0)); + } catch (AEADBadTagException e) { e.printStackTrace(); return false; - }catch (ShortBufferException e) { + } catch (ShortBufferException e) { e.printStackTrace(); CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); } catch (IllegalBlockSizeException e) { @@ -474,8 +468,8 @@ public boolean aesGCMDecrypt( e.printStackTrace(); CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); } - //Copy the decrypted data - Util.arrayCopyNonAtomic(outputBuf, (short)0,secret,secretStart,len); + // Copy the decrypted data + Util.arrayCopyNonAtomic(outputBuf, (short) 0, secret, secretStart, len); return true; } @@ -511,10 +505,16 @@ public HMACKey cmacKdf(byte[] keyMaterial, short keyMaterialStart, short keyMate } @Override - public short cmacKdf(byte[] keyMaterial, short keyMaterialStart, short keyMaterialLen, byte[] label, + public short cmacKDF(KMPreSharedKey pSharedKey, byte[] label, short labelStart, short labelLen, byte[] context, short contextStart, short contextLength, byte[] keyBuf, short keyStart) { - HMACKey key = cmacKdf(keyMaterial,keyMaterialStart, keyMaterialLen, label, labelStart, labelLen,context,contextStart,contextLength); - return key.getKey(keyBuf,keyStart); + KMHmacKey key = (KMHmacKey) pSharedKey; + short keyMaterialLen = key.getKeySizeBits(); + keyMaterialLen = (short) (keyMaterialLen / 8); + short keyMaterialStart = 0; + byte[] keyMaterial = new byte[keyMaterialLen]; + key.getKey(keyMaterial, keyMaterialStart); + HMACKey hmacKey = cmacKdf(keyMaterial,keyMaterialStart, keyMaterialLen, label, labelStart, labelLen,context,contextStart,contextLength); + return hmacKey.getKey(keyBuf,keyStart); } @@ -529,6 +529,17 @@ public boolean hmacVerify(HMACKey key, byte[] data, short dataStart, short dataL return hmacSignature.verify(data, dataStart, dataLength, mac, macStart, macLength); } + @Override + public short hmacKDF(KMMasterKey masterkey, byte[] data, short dataStart, + short dataLength, byte[] signature, short signatureStart) { + KMAESKey aesKey = (KMAESKey) masterkey; + short keyLen = (short) (aesKey.getKeySizeBits() / 8); + byte[] keyData = new byte[keyLen]; + aesKey.getKey(keyData, (short) 0); + return hmacSign(keyData, (short) 0, keyLen, data, dataStart, dataLength, + signature, signatureStart); + } + @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); @@ -620,50 +631,7 @@ public KMOperation initAsymmetricOperation(byte purpose, byte alg, byte padding, CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); return null; } -/* - @Override - public short updateOperation(short opHandle, byte[] inputDataBuf, short inputDataStart, short inputDataLength, byte[] outputDataBuf, short outputDataStart) { - Object op = findOperation(opHandle); - if(op instanceof Signature ){ - ((Signature)op).update(inputDataBuf,inputDataStart,inputDataLength); - return 0; - }else{ - return ((KMCipher)op).update(inputDataBuf,inputDataStart,inputDataLength,outputDataBuf,outputDataStart); - } - } - - @Override - public short finishOperation(short opHandle, byte[] inputDataBuf, short inputDataStart, short inputDataLength, byte[] outputDataBuf, short outputDataStart) { - Object op = findOperation(opHandle); - short ret = 0; - if(op instanceof Signature ){ - ret = ((Signature)op).sign(inputDataBuf,inputDataStart,inputDataLength,outputDataBuf,outputDataStart); - }else{ - ret = ((KMCipher)op).doFinal(inputDataBuf,inputDataStart,inputDataLength,outputDataBuf,outputDataStart); - } - removeOperation(opHandle); - return ret; - } - @Override - public void abortOperation(short opHandle) { - removeOperation(opHandle); - } - - @Override - public void updateAAD(short opHandle, byte[] dataBuf, short dataStart, short dataLength) { - KMCipher aesGcm = (KMCipher) findOperation(opHandle); - aesGcm.updateAAD(dataBuf, dataStart, dataLength); - } - - @Override - public void getAESGCMOutputSize(short opHandle, short dataSize, short macLength) { - KMCipher aesGcm = (KMCipher) findOperation(opHandle); - aesGcm.getAesGcmOutputSize(dataSize, macLength); - } - */ - - public KMCipher createRsaDecipher(short padding, short digest, byte[] secret, short secretStart, short secretLength, byte[] modBuffer, short modOff, short modLength) { byte cipherAlg = mapCipherAlg(KMType.RSA, (byte)padding, (byte)0); @@ -762,12 +730,7 @@ public Signature createRsaSigner(short digest, short padding, byte[] secret, (padding == KMType.RSA_PKCS1_1_5_SIGN && digest == KMType.DIGEST_NONE)) { return createNoDigestSigner(padding,secret, secretStart, secretLength, modBuffer, modOff, modLength); - }/* - else if (padding == KMCipher.PAD_PKCS1_PSS) alg = Signature.ALG_RSA_SHA_256_PKCS1_PSS; - else if (padding == KMCipher.PAD_PKCS1) { - alg = Signature.ALG_RSA_SHA_256_PKCS1; - }else CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); - */ + } Signature rsaSigner = Signature.getInstance((byte)alg, false); RSAPrivateKey key = (RSAPrivateKey) KeyBuilder.buildKey(KeyBuilder.TYPE_RSA_PRIVATE, KeyBuilder.LENGTH_RSA_2048, false); key.setExponent(secret,secretStart,secretLength); @@ -1017,8 +980,6 @@ public KMCipher createAesGcmCipher(short mode, short tagLen, byte[] secret, shor //Create the sun jce compliant aes key byte[] keyMaterial = new byte[secretLength]; Util.arrayCopyNonAtomic(secret,secretStart,keyMaterial,(short)0,secretLength); - //print("KeyMaterial Enc", keyMaterial); - //print("Authdata Enc", authData, authDataStart, authDataLen); java.security.Key aesKey = new SecretKeySpec(keyMaterial,(short)0,keyMaterial.length, "AES"); // Create the cipher javax.crypto.Cipher cipher = null; @@ -1197,7 +1158,6 @@ public Signature createEcVerifier(short digest, byte[] pubKey, short pubKeyStart @Override public KMAttestationCert getAttestationCert(boolean rsaCert) { - //certBuilder.reset(); return KMAttestationCertImpl.instance(rsaCert); } @@ -1213,13 +1173,11 @@ public short getCertificateChainLength() { } @Override - public short ecSign256(byte[] secret, short secretStart, short secretLength, + public short ecSign256(KMAttestationKey attestationKey, byte[] inputDataBuf, short inputDataStart, short inputDataLength, byte[] outputDataBuf, short outputDataStart) { - ECPrivateKey key = (ECPrivateKey) KeyBuilder.buildKey( - KeyBuilder.TYPE_EC_FP_PRIVATE, KeyBuilder.LENGTH_EC_FP_256, false); - key.setS(secret, secretStart, secretLength); + ECPrivateKey key = ((KMECPrivateKey)attestationKey).getPrivateKey(); Signature signer = Signature .getInstance(Signature.ALG_ECDSA_SHA_256, false); @@ -1272,36 +1230,84 @@ public boolean isDeviceRebooted() { @Override public void clearDeviceBooted(boolean resetBootFlag) { - // To be filled } @Override public void onSave(Element ele) { - // TODO Auto-generated method stub - } @Override public void onRestore(Element ele) { - // TODO Auto-generated method stub - } @Override public short getBackupPrimitiveByteCount() { - // TODO Auto-generated method stub return 0; } @Override public short getBackupObjectCount() { - // TODO Auto-generated method stub return 0; } @Override public boolean isUpgrading() { - // TODO Auto-generated method stub return false; } + + @Override + public KMMasterKey createMasterKey(short keySizeBits) { + if (masterKey == null) { + AESKey key = (AESKey) KeyBuilder.buildKey( + KeyBuilder.TYPE_AES, keySizeBits, false); + masterKey = new KMAESKey(key); + short keyLen = (short) (keySizeBits / 8); + byte[] keyData = new byte[keyLen]; + getTrueRandomNumber(keyData, (short) 0, keyLen); + masterKey.setKey(keyData, (short) 0); + } + return (KMMasterKey) masterKey; + } + + @Override + public KMAttestationKey createAttestationKey(byte[] keyData, short offset, + short length) { + if (attestationKey == null) { + // Strongbox supports only P-256 curve for EC key. + KeyPair ecKeyPair = new KeyPair(KeyPair.ALG_EC_FP, KeyBuilder.LENGTH_EC_FP_256); + attestationKey = new KMECPrivateKey(ecKeyPair); + } + attestationKey.setS(keyData, offset, length); + return (KMAttestationKey) attestationKey; + } + + @Override + public KMPreSharedKey createPresharedKey(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); + } + preSharedKey.setKey(keyData, offset, length); + return (KMPreSharedKey) preSharedKey; + } + + @Override + public KMMasterKey getMasterKey() { + return (KMMasterKey) masterKey; + } + + @Override + public KMAttestationKey getAttestationKey() { + return (KMAttestationKey) attestationKey; + } + + @Override + public KMPreSharedKey getPresharedKey() { + return (KMPreSharedKey) preSharedKey; + } } diff --git a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMOperationImpl.java b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMOperationImpl.java index dec071e6..78c01302 100644 --- a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMOperationImpl.java +++ b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMOperationImpl.java @@ -1,3 +1,18 @@ +/* + * 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.keymaster; import javacard.security.Signature; diff --git a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMRsa2048NoDigestSignature.java b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMRsa2048NoDigestSignature.java index 3ebf5fe0..855a3104 100644 --- a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMRsa2048NoDigestSignature.java +++ b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMRsa2048NoDigestSignature.java @@ -1,3 +1,18 @@ +/* + * 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.keymaster; import javacard.framework.Util; diff --git a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMUtils.java b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMUtils.java index cb8df259..65e83415 100644 --- a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMUtils.java +++ b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMUtils.java @@ -1,3 +1,18 @@ +/* + * 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.keymaster; import javacard.framework.Util; @@ -332,29 +347,6 @@ public static void shiftRight(byte[] buf, short start) { } } - - // num1 must be greater then or equal to num2 and both must be positive - /*private short subtractIntegers(short num1, short num2) { - short buf = - repository.alloc((short)24); byte[] scratchPad = repository.getHeap(); - Util.arrayFillNonAtomic(scratchPad, buf, (short) 24, (byte) 0); - Util.arrayCopyNonAtomic(KMInteger.cast(num1).getBuffer(), - KMInteger.cast(num1).getStartOff(), scratchPad, - (short) (buf + 8 - KMInteger.cast(num1).length()), - KMInteger.cast(num1).length()); - Util.arrayCopyNonAtomic(KMInteger.cast(num2).getBuffer(), - KMInteger.cast(num2).getStartOff(), scratchPad, - (short) (buf + 16 - KMInteger.cast(num2).length()), - KMInteger.cast(num2).length()); - if (scratchPad[buf] < 0 || scratchPad[(short) (buf + 8)] < 0) - return KMType.INVALID_VALUE; - if (Util.arrayCompare(scratchPad, buf, scratchPad, (short) (buf + 8), - (short) 8) < 1) - return KMType.INVALID_VALUE; - subtract(scratchPad, buf, (short) (buf + 8), (short) (buf + 16)); - return KMInteger.uint_64(scratchPad, (short) (buf + 16)); - }*/ - public static void add(byte[] buf, short op1, short op2, short result) { byte index = 7; byte carry = 0; diff --git a/Applet/JCardSimProvider/test/com/android/javacard/test/KMFunctionalTest.java b/Applet/JCardSimProvider/test/com/android/javacard/test/KMFunctionalTest.java index 74892031..a6308513 100644 --- a/Applet/JCardSimProvider/test/com/android/javacard/test/KMFunctionalTest.java +++ b/Applet/JCardSimProvider/test/com/android/javacard/test/KMFunctionalTest.java @@ -17,8 +17,6 @@ package com.android.javacard.test; import com.android.javacard.keymaster.KMArray; -import com.android.javacard.keymaster.KMAttestationCert; -import com.android.javacard.keymaster.KMAttestationCertImpl; import com.android.javacard.keymaster.KMBoolTag; import com.android.javacard.keymaster.KMByteBlob; import com.android.javacard.keymaster.KMByteTag; @@ -43,13 +41,15 @@ import com.android.javacard.keymaster.KMVerificationToken; import com.licel.jcardsim.smartcardio.CardSimulator; import com.licel.jcardsim.utils.AIDUtil; + import javacard.framework.AID; import javacard.framework.Util; - -import java.security.spec.PKCS8EncodedKeySpec; +import java.util.Arrays; +import java.util.Random; import javax.smartcardio.CommandAPDU; import javax.smartcardio.ResponseAPDU; + import org.junit.Assert; import org.junit.Test; @@ -2027,6 +2027,168 @@ public void testSignWithRsaNonePkcs1(){ cleanUp(); } + public byte[] EncryptMessage(byte[] input, short params, byte[] keyBlob) { + short ret = begin(KMType.ENCRYPT, + KMByteBlob.instance(keyBlob, (short) 0, (short) keyBlob.length), + KMKeyParameters.instance(params), (short) 0); + // Get the operation handle. + short opHandle = KMArray.cast(ret).get((short) 2); + byte[] opHandleBuf = new byte[KMRepository.OPERATION_HANDLE_SIZE]; + KMInteger.cast(opHandle).getValue(opHandleBuf, (short) 0, + (short) opHandleBuf.length); + opHandle = KMInteger.uint_64(opHandleBuf, (short) 0); + + ret = finish(opHandle, + KMByteBlob.instance(input, (short) 0, (short) input.length), null, + (short) 0, (short) 0, (short) 0, KMError.OK); + short dataPtr = KMArray.cast(ret).get((short) 2); + byte[] output = new byte[KMByteBlob.cast(dataPtr).length()]; + if (KMByteBlob.cast(dataPtr).length() > 0) { + Util.arrayCopyNonAtomic(KMByteBlob.cast(dataPtr).getBuffer(), KMByteBlob + .cast(dataPtr).getStartOff(), output, (short) 0, + KMByteBlob.cast(dataPtr).length()); + } + return output; + } + + public byte[] DecryptMessage(byte[] input, short params, byte[] keyBlob) { + short ret = begin(KMType.DECRYPT, + KMByteBlob.instance(keyBlob, (short) 0, (short) keyBlob.length), + KMKeyParameters.instance(params), (short) 0); + // Get the operation handle. + short opHandle = KMArray.cast(ret).get((short) 2); + byte[] opHandleBuf = new byte[KMRepository.OPERATION_HANDLE_SIZE]; + KMInteger.cast(opHandle).getValue(opHandleBuf, (short) 0, + (short) opHandleBuf.length); + opHandle = KMInteger.uint_64(opHandleBuf, (short) 0); + + ret = finish(opHandle, + KMByteBlob.instance(input, (short) 0, (short) input.length), null, + (short) 0, (short) 0, (short) 0, KMError.OK); + short dataPtr = KMArray.cast(ret).get((short) 2); + byte[] output = new byte[KMByteBlob.cast(dataPtr).length()]; + if (KMByteBlob.cast(dataPtr).length() > 0) { + Util.arrayCopyNonAtomic(KMByteBlob.cast(dataPtr).getBuffer(), KMByteBlob + .cast(dataPtr).getStartOff(), output, (short) 0, + KMByteBlob.cast(dataPtr).length()); + } + return output; + } + + public short generateRandom(short upperBound) { + Random rand = new Random(); + short int_random = (short) rand.nextInt(upperBound); + return int_random; + } + + @Test + public void testDesEcbPkcs7PaddingCorrupted() { + init(); + short desKey = generateAesDesKey(KMType.DES, (short) 168, null, null, false); + short desKeyPtr = KMArray.cast(desKey).get((short) 1); + byte[] keyBlob = new byte[KMByteBlob.cast(desKeyPtr).length()]; + Util.arrayCopyNonAtomic(KMByteBlob.cast(desKeyPtr).getBuffer(), KMByteBlob + .cast(desKeyPtr).getStartOff(), keyBlob, (short) 0, + (short) keyBlob.length); + + byte[] message = { + 0x61 }; + short desPkcs7Params = getAesDesParams(KMType.DES, KMType.ECB, + KMType.PKCS7, null); + byte[] cipherText1 = EncryptMessage(message, desPkcs7Params, keyBlob); + Assert.assertEquals(8, cipherText1.length); + Assert.assertFalse(Arrays.equals(message, cipherText1)); + + // Corrupt the cipher text. + ++cipherText1[(cipherText1.length / 2)]; + + // Decrypt operation + // Begin + desPkcs7Params = getAesDesParams(KMType.DES, KMType.ECB, KMType.PKCS7, null); + + short ret = begin(KMType.DECRYPT, + KMByteBlob.instance(keyBlob, (short) 0, (short) keyBlob.length), + KMKeyParameters.instance(desPkcs7Params), (short) 0); + // Get the operation handle. + short opHandle = KMArray.cast(ret).get((short) 2); + byte[] opHandleBuf = new byte[KMRepository.OPERATION_HANDLE_SIZE]; + KMInteger.cast(opHandle).getValue(opHandleBuf, (short) 0, + (short) opHandleBuf.length); + opHandle = KMInteger.uint_64(opHandleBuf, (short) 0); + + // Finish + short dataPtr = KMByteBlob.instance(cipherText1, (short) 0, + (short) cipherText1.length); + opHandle = KMInteger.uint_64(opHandleBuf, (short) 0); + ret = finish(opHandle, dataPtr, null, (short) 0, (short) 0, (short) 0, + KMError.INVALID_ARGUMENT); + cleanUp(); + } + + @Test + public void testVtsRsaPkcs1Success() { + init(); + byte[] message = { + 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x57, 0x6f, 0x72, 0x6c, 0x64, + 0x21 }; // "Hello World!"; + for (int i = 0; i < 250; i++) { + short key = generateRsaKey(null, null); + short rsaKeyPtr = KMArray.cast(key).get((short) 1); + byte[] keyBlob = new byte[KMByteBlob.cast(rsaKeyPtr).length()]; + Util.arrayCopyNonAtomic(KMByteBlob.cast(rsaKeyPtr).getBuffer(), + KMByteBlob.cast(rsaKeyPtr).getStartOff(), keyBlob, (short) 0, + (short) keyBlob.length); + short pkcs1Params = getRsaParams(KMType.DIGEST_NONE, + KMType.RSA_PKCS1_1_5_ENCRYPT); + + byte[] cipherText1 = EncryptMessage(message, pkcs1Params, keyBlob); + Assert.assertEquals((2048 / 8), cipherText1.length); + + pkcs1Params = getRsaParams(KMType.DIGEST_NONE, + KMType.RSA_PKCS1_1_5_ENCRYPT); + byte[] cipherText2 = EncryptMessage(message, pkcs1Params, keyBlob); + Assert.assertEquals((2048 / 8), cipherText2.length); + + // PKCS1 v1.5 randomizes padding so every result should be different. + Assert.assertFalse(Arrays.equals(cipherText1, cipherText2)); + + pkcs1Params = getRsaParams(KMType.DIGEST_NONE, + KMType.RSA_PKCS1_1_5_ENCRYPT); + byte[] plainText = DecryptMessage(cipherText1, pkcs1Params, keyBlob); + Assert.assertTrue(Arrays.equals(message, plainText)); + + // Decrypting corrupted ciphertext should fail. + short offset_to_corrupt = generateRandom((short) cipherText1.length); + + byte corrupt_byte; + do { + corrupt_byte = (byte) generateRandom((short) 256); + } while (corrupt_byte == cipherText1[offset_to_corrupt]); + cipherText1[offset_to_corrupt] = corrupt_byte; + + pkcs1Params = getRsaParams(KMType.DIGEST_NONE, + KMType.RSA_PKCS1_1_5_ENCRYPT); + // Do Begin operation. + short ret = begin(KMType.DECRYPT, + KMByteBlob.instance(keyBlob, (short) 0, (short) keyBlob.length), + KMKeyParameters.instance(pkcs1Params), (short) 0); + + // Get the operation handle. + short opHandle = KMArray.cast(ret).get((short) 2); + byte[] opHandleBuf = new byte[KMRepository.OPERATION_HANDLE_SIZE]; + KMInteger.cast(opHandle).getValue(opHandleBuf, (short) 0, + (short) opHandleBuf.length); + opHandle = KMInteger.uint_64(opHandleBuf, (short) 0); + + short dataPtr = KMByteBlob.instance(cipherText1, (short) 0, + (short) cipherText1.length); + // Finish should return UNKNOWN_ERROR. + ret = finish(opHandle, dataPtr, null, (short) 0, (short) 0, (short) 0, + KMError.UNKNOWN_ERROR); + } + cleanUp(); + } + @Test public void testSignVerifyWithHmacSHA256WithUpdate(){ init(); @@ -2560,9 +2722,9 @@ public short processMessage( opHandle = KMInteger.uint_64(opHandleBuf, (short) 0); if (keyPurpose == KMType.VERIFY) { - ret = finish(opHandle, dataPtr, signature, (short) 0, (short) 0, (short) 0); + ret = finish(opHandle, dataPtr, signature, (short) 0, (short) 0, (short) 0, KMError.OK); } else { - ret = finish(opHandle, dataPtr, null, (short) 0, (short) 0, (short) 0); + ret = finish(opHandle, dataPtr, null, (short) 0, (short) 0, (short) 0, KMError.OK); } if(len >0){ dataPtr = KMArray.cast(ret).get((short)2); @@ -2610,7 +2772,34 @@ public short begin(byte keyPurpose, short keyBlob, short keyParmas, short hwToke } } - public short finish(short operationHandle, short data, byte[] signature, short inParams, short hwToken, short verToken) { + public short translateExtendedErrorCodes(short err) { + switch (err) { + case KMError.SW_CONDITIONS_NOT_SATISFIED: + case KMError.UNSUPPORTED_CLA: + case KMError.INVALID_P1P2: + case KMError.INVALID_DATA: + case KMError.CRYPTO_ILLEGAL_USE: + case KMError.CRYPTO_ILLEGAL_VALUE: + case KMError.CRYPTO_INVALID_INIT: + case KMError.CRYPTO_UNINITIALIZED_KEY: + case KMError.GENERIC_UNKNOWN_ERROR: + err = KMError.UNKNOWN_ERROR; + break; + case KMError.CRYPTO_NO_SUCH_ALGORITHM: + err = KMError.UNSUPPORTED_ALGORITHM; + break; + case KMError.UNSUPPORTED_INSTRUCTION: + case KMError.CMD_NOT_ALLOWED: + case KMError.SW_WRONG_LENGTH: + err = KMError.UNIMPLEMENTED; + break; + default: + break; + } + return err; + } + + public short finish(short operationHandle, short data, byte[] signature, short inParams, short hwToken, short verToken, short expectedErr) { if(hwToken == 0) { hwToken = KMHardwareAuthToken.instance(); } @@ -2637,16 +2826,27 @@ public short finish(short operationHandle, short data, byte[] signature, short i CommandAPDU apdu = encodeApdu((byte)INS_FINISH_OPERATION_CMD, arrPtr); // print(commandAPDU.getBytes()); ResponseAPDU response = simulator.transmitCommand(apdu); - short ret = KMArray.instance((short) 3); - short outParams = KMKeyParameters.exp(); - KMArray.cast(ret).add((short)0, KMInteger.exp()); - KMArray.cast(ret).add((short)1, outParams); - KMArray.cast(ret).add((short)2, KMByteBlob.exp()); byte[] respBuf = response.getBytes(); short len = (short) respBuf.length; + short ret; + short error; + if (expectedErr == KMError.OK) { + ret = KMArray.instance((short) 3); + short outParams = KMKeyParameters.exp(); + KMArray.cast(ret).add((short)0, KMInteger.exp()); + KMArray.cast(ret).add((short)1, outParams); + KMArray.cast(ret).add((short)2, KMByteBlob.exp()); + } else { + ret = KMInteger.exp(); + } ret = decoder.decode(ret, respBuf, (short) 0, len); - short error = KMInteger.cast(KMArray.cast(ret).get((short)0)).getShort(); - Assert.assertEquals(error, KMError.OK); + if (expectedErr == KMError.OK) { + error = KMInteger.cast(KMArray.cast(ret).get((short)0)).getShort(); + } else { + error = KMInteger.cast(ret).getShort(); + error = translateExtendedErrorCodes(error); + } + Assert.assertEquals(error, expectedErr); return ret; } public short update(short operationHandle, short data, short inParams, short hwToken, short verToken) { diff --git a/Applet/src/com/android/javacard/keymaster/KMAttestationCert.java b/Applet/src/com/android/javacard/keymaster/KMAttestationCert.java index d27f015a..a472ff27 100644 --- a/Applet/src/com/android/javacard/keymaster/KMAttestationCert.java +++ b/Applet/src/com/android/javacard/keymaster/KMAttestationCert.java @@ -1,3 +1,18 @@ +/* + * 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.keymaster; /** @@ -44,15 +59,13 @@ public interface KMAttestationCert { * @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 key This buffer contains the master secret. - * @param keyOff Start offset of the master key. - * @param keyLen Length of the master key. + * @param instance of the master key. * @return instance of KMAttestationCert. */ KMAttestationCert makeUniqueId(byte[] scratchpad, short scratchPadOff, byte[] creationTime, short creationTimeOff, short creationTimeLen, byte[] attestAppId, short attestAppIdOff, short attestAppIdLen, byte resetSinceIdRotation, - byte[] key, short keyOff, short keyLen); + KMMasterKey masterKey); /** * Set start time received from creation/activation time tag. Used for certificate's valid period. @@ -130,14 +143,6 @@ KMAttestationCert notAfter(short usageExpiryTimeObj, */ KMAttestationCert buffer(byte[] buf, short bufStart, short maxLen); - /** - * Set signing key to be used to sign the cert. - * - * @param privateKey This is ECPrivateKey with curve P-256. - * @return instance of KMAttestationCert - */ - KMAttestationCert signingKey(short privateKey); - /** * Get the start of the certificate * diff --git a/Applet/src/com/android/javacard/keymaster/KMAttestationKey.java b/Applet/src/com/android/javacard/keymaster/KMAttestationKey.java new file mode 100644 index 00000000..8582d2b2 --- /dev/null +++ b/Applet/src/com/android/javacard/keymaster/KMAttestationKey.java @@ -0,0 +1,26 @@ +/* + * 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.keymaster; + +/** + * KMAttestationKey is a marker interface and the SE Provider has to implement + * this interface. Internally attestation key is stored as a Javacard EC + * key pair object, which will provide additional security. + * The attestation key is maintained by the SEProvider. + */ +public interface KMAttestationKey { + +} diff --git a/Applet/src/com/android/javacard/keymaster/KMEncoder.java b/Applet/src/com/android/javacard/keymaster/KMEncoder.java index 72f906bf..685ba468 100644 --- a/Applet/src/com/android/javacard/keymaster/KMEncoder.java +++ b/Applet/src/com/android/javacard/keymaster/KMEncoder.java @@ -363,15 +363,4 @@ private void incrementStartOff(short inc){ ISOException.throwIt(ISO7816.SW_DATA_INVALID); } } - /* - private static void print(byte[] buf, short start, short length){ - StringBuilder sb = new StringBuilder(); - for(int i = start; i < (start+length); i++){ - sb.append(String.format("%02X", buf[i])) ; - //if((i-start)%16 == 0 && (i-start) != 0) sb.append(String.format("\n")); - } - System.out.println(sb.toString()); - } - - */ } diff --git a/Applet/src/com/android/javacard/keymaster/KMError.java b/Applet/src/com/android/javacard/keymaster/KMError.java index 85c71654..83eb8c7c 100644 --- a/Applet/src/com/android/javacard/keymaster/KMError.java +++ b/Applet/src/com/android/javacard/keymaster/KMError.java @@ -1,3 +1,18 @@ +/* + * 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.keymaster; /** @@ -5,100 +20,100 @@ * positive unlike negative values in keymaster hal. */ public class KMError { - public static short OK = 0; - public static short ROOT_OF_TRUST_ALREADY_SET = 1; - public static short UNSUPPORTED_PURPOSE = 2; - public static short INCOMPATIBLE_PURPOSE = 3; - public static short UNSUPPORTED_ALGORITHM = 4; - public static short INCOMPATIBLE_ALGORITHM = 5; - public static short UNSUPPORTED_KEY_SIZE = 6; - public static short UNSUPPORTED_BLOCK_MODE = 7; - public static short INCOMPATIBLE_BLOCK_MODE = 8; - public static short UNSUPPORTED_MAC_LENGTH = 9; - public static short UNSUPPORTED_PADDING_MODE = 10; - public static short INCOMPATIBLE_PADDING_MODE = 11; - public static short UNSUPPORTED_DIGEST = 12; - public static short INCOMPATIBLE_DIGEST = 13; - public static short INVALID_EXPIRATION_TIME = 14; - public static short INVALID_USER_ID = 15; - public static short INVALID_AUTHORIZATION_TIMEOUT = 16; - public static short UNSUPPORTED_KEY_FORMAT = 17; - public static short INCOMPATIBLE_KEY_FORMAT = 18; - public static short UNSUPPORTED_KEY_ENCRYPTION_ALGORITHM = 19; + public static final short OK = 0; + public static final short ROOT_OF_TRUST_ALREADY_SET = 1; + public static final short UNSUPPORTED_PURPOSE = 2; + public static final short INCOMPATIBLE_PURPOSE = 3; + public static final short UNSUPPORTED_ALGORITHM = 4; + public static final short INCOMPATIBLE_ALGORITHM = 5; + public static final short UNSUPPORTED_KEY_SIZE = 6; + public static final short UNSUPPORTED_BLOCK_MODE = 7; + public static final short INCOMPATIBLE_BLOCK_MODE = 8; + public static final short UNSUPPORTED_MAC_LENGTH = 9; + public static final short UNSUPPORTED_PADDING_MODE = 10; + public static final short INCOMPATIBLE_PADDING_MODE = 11; + public static final short UNSUPPORTED_DIGEST = 12; + public static final short INCOMPATIBLE_DIGEST = 13; + public static final short INVALID_EXPIRATION_TIME = 14; + public static final short INVALID_USER_ID = 15; + public static final short INVALID_AUTHORIZATION_TIMEOUT = 16; + public static final short UNSUPPORTED_KEY_FORMAT = 17; + public static final short INCOMPATIBLE_KEY_FORMAT = 18; + public static final short UNSUPPORTED_KEY_ENCRYPTION_ALGORITHM = 19; /** For PKCS8 & PKCS12 */ - public static short UNSUPPORTED_KEY_VERIFICATION_ALGORITHM = 20; + public static final short UNSUPPORTED_KEY_VERIFICATION_ALGORITHM = 20; /** For PKCS8 & PKCS12 */ - public static short INVALID_INPUT_LENGTH = 21; + public static final short INVALID_INPUT_LENGTH = 21; - public static short KEY_EXPORT_OPTIONS_INVALID = 22; - public static short DELEGATION_NOT_ALLOWED = 23; - public static short KEY_NOT_YET_VALID = 24; - public static short KEY_EXPIRED = 25; - public static short KEY_USER_NOT_AUTHENTICATED = 26; - public static short OUTPUT_PARAMETER_NULL = 27; - public static short INVALID_OPERATION_HANDLE = 28; - public static short INSUFFICIENT_BUFFER_SPACE = 29; - public static short VERIFICATION_FAILED = 30; - public static short TOO_MANY_OPERATIONS = 31; - public static short UNEXPECTED_NULL_POINTER = 32; - public static short INVALID_KEY_BLOB = 33; - public static short IMPORTED_KEY_NOT_ENCRYPTED = 34; - public static short IMPORTED_KEY_DECRYPTION_FAILED = 35; - public static short IMPORTED_KEY_NOT_SIGNED = 36; - public static short IMPORTED_KEY_VERIFICATION_FAILED = 37; - public static short INVALID_ARGUMENT = 38; - public static short UNSUPPORTED_TAG = 39; - public static short INVALID_TAG = 40; - public static short MEMORY_ALLOCATION_FAILED = 41; - public static short IMPORT_PARAMETER_MISMATCH = 44; - public static short SECURE_HW_ACCESS_DENIED = 45; - public static short OPERATION_CANCELLED = 46; - public static short CONCURRENT_ACCESS_CONFLICT = 47; - public static short SECURE_HW_BUSY = 48; - public static short SECURE_HW_COMMUNICATION_FAILED = 49; - public static short UNSUPPORTED_EC_FIELD = 50; - public static short MISSING_NONCE = 51; - public static short INVALID_NONCE = 52; - public static short MISSING_MAC_LENGTH = 53; - public static short KEY_RATE_LIMIT_EXCEEDED = 54; - public static short CALLER_NONCE_PROHIBITED = 55; - public static short KEY_MAX_OPS_EXCEEDED = 56; - public static short INVALID_MAC_LENGTH = 57; - public static short MISSING_MIN_MAC_LENGTH = 58; - public static short UNSUPPORTED_MIN_MAC_LENGTH = 59; - public static short UNSUPPORTED_KDF = 60; - public static short UNSUPPORTED_EC_CURVE = 61; - public static short KEY_REQUIRES_UPGRADE = 62; - public static short ATTESTATION_CHALLENGE_MISSING = 63; - public static short KEYMASTER_NOT_CONFIGURED = 64; - public static short ATTESTATION_APPLICATION_ID_MISSING = 65; - public static short CANNOT_ATTEST_IDS = 66; - public static short ROLLBACK_RESISTANCE_UNAVAILABLE = 67; - public static short HARDWARE_TYPE_UNAVAILABLE = 68; - public static short PROOF_OF_PRESENCE_REQUIRED = 69; - public static short CONCURRENT_PROOF_OF_PRESENCE_REQUESTED = 70; - public static short NO_USER_CONFIRMATION = 71; - public static short DEVICE_LOCKED = 72; - public static short EARLY_BOOT_ENDED = 73; - public static short UNIMPLEMENTED = 100; - public static short VERSION_MISMATCH = 101; - public static short UNKNOWN_ERROR = 1000; + public static final short KEY_EXPORT_OPTIONS_INVALID = 22; + public static final short DELEGATION_NOT_ALLOWED = 23; + public static final short KEY_NOT_YET_VALID = 24; + public static final short KEY_EXPIRED = 25; + public static final short KEY_USER_NOT_AUTHENTICATED = 26; + public static final short OUTPUT_PARAMETER_NULL = 27; + public static final short INVALID_OPERATION_HANDLE = 28; + public static final short INSUFFICIENT_BUFFER_SPACE = 29; + public static final short VERIFICATION_FAILED = 30; + public static final short TOO_MANY_OPERATIONS = 31; + public static final short UNEXPECTED_NULL_POINTER = 32; + public static final short INVALID_KEY_BLOB = 33; + public static final short IMPORTED_KEY_NOT_ENCRYPTED = 34; + public static final short IMPORTED_KEY_DECRYPTION_FAILED = 35; + public static final short IMPORTED_KEY_NOT_SIGNED = 36; + public static final short IMPORTED_KEY_VERIFICATION_FAILED = 37; + public static final short INVALID_ARGUMENT = 38; + public static final short UNSUPPORTED_TAG = 39; + public static final short INVALID_TAG = 40; + public static final short MEMORY_ALLOCATION_FAILED = 41; + public static final short IMPORT_PARAMETER_MISMATCH = 44; + public static final short SECURE_HW_ACCESS_DENIED = 45; + public static final short OPERATION_CANCELLED = 46; + public static final short CONCURRENT_ACCESS_CONFLICT = 47; + public static final short SECURE_HW_BUSY = 48; + public static final short SECURE_HW_COMMUNICATION_FAILED = 49; + public static final short UNSUPPORTED_EC_FIELD = 50; + public static final short MISSING_NONCE = 51; + public static final short INVALID_NONCE = 52; + public static final short MISSING_MAC_LENGTH = 53; + public static final short KEY_RATE_LIMIT_EXCEEDED = 54; + public static final short CALLER_NONCE_PROHIBITED = 55; + public static final short KEY_MAX_OPS_EXCEEDED = 56; + public static final short INVALID_MAC_LENGTH = 57; + public static final short MISSING_MIN_MAC_LENGTH = 58; + public static final short UNSUPPORTED_MIN_MAC_LENGTH = 59; + public static final short UNSUPPORTED_KDF = 60; + public static final short UNSUPPORTED_EC_CURVE = 61; + public static final short KEY_REQUIRES_UPGRADE = 62; + public static final short ATTESTATION_CHALLENGE_MISSING = 63; + public static final short KEYMASTER_NOT_CONFIGURED = 64; + public static final short ATTESTATION_APPLICATION_ID_MISSING = 65; + public static final short CANNOT_ATTEST_IDS = 66; + public static final short ROLLBACK_RESISTANCE_UNAVAILABLE = 67; + public static final short HARDWARE_TYPE_UNAVAILABLE = 68; + public static final short PROOF_OF_PRESENCE_REQUIRED = 69; + public static final short CONCURRENT_PROOF_OF_PRESENCE_REQUESTED = 70; + public static final short NO_USER_CONFIRMATION = 71; + public static final short DEVICE_LOCKED = 72; + public static final short EARLY_BOOT_ENDED = 73; + public static final short UNIMPLEMENTED = 100; + public static final short VERSION_MISMATCH = 101; + public static final short UNKNOWN_ERROR = 1000; //Extended errors - public static short SW_CONDITIONS_NOT_SATISFIED = 10001; - public static short UNSUPPORTED_CLA = 10002; - public static short INVALID_P1P2 = 10003; - public static short UNSUPPORTED_INSTRUCTION = 10004; - public static short CMD_NOT_ALLOWED = 10005; - public static short SW_WRONG_LENGTH = 10006; - public static short INVALID_DATA = 10007; + public static final short SW_CONDITIONS_NOT_SATISFIED = 10001; + public static final short UNSUPPORTED_CLA = 10002; + public static final short INVALID_P1P2 = 10003; + public static final short UNSUPPORTED_INSTRUCTION = 10004; + public static final short CMD_NOT_ALLOWED = 10005; + public static final short SW_WRONG_LENGTH = 10006; + public static final short INVALID_DATA = 10007; //Crypto errors - public static short CRYPTO_ILLEGAL_USE = 10008; - public static short CRYPTO_ILLEGAL_VALUE = 10009; - public static short CRYPTO_INVALID_INIT = 10010; - public static short CRYPTO_NO_SUCH_ALGORITHM = 10011; - public static short CRYPTO_UNINITIALIZED_KEY = 10012; + public static final short CRYPTO_ILLEGAL_USE = 10008; + public static final short CRYPTO_ILLEGAL_VALUE = 10009; + public static final short CRYPTO_INVALID_INIT = 10010; + public static final short CRYPTO_NO_SUCH_ALGORITHM = 10011; + public static final short CRYPTO_UNINITIALIZED_KEY = 10012; //Generic Unknown error. - public static short GENERIC_UNKNOWN_ERROR = 10013; + public static final short GENERIC_UNKNOWN_ERROR = 10013; } diff --git a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java index 1b156d53..abf6e670 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java @@ -39,6 +39,7 @@ public class KMKeymasterApplet extends Applet implements AppletEvent, ExtendedLe private static final byte CLA_ISO7816_NO_SM_NO_CHAN = (byte) 0x80; private static final short KM_HAL_VERSION = (short) 0x4000; private static final short MAX_AUTH_DATA_SIZE = (short) 512; + private static final short DERIVE_KEY_INPUT_SIZE = (short) 256; // "Keymaster HMAC Verification" - used for HMAC key verification. public static final byte[] sharingCheck = { @@ -190,8 +191,7 @@ protected KMKeymasterApplet(KMSEProvider seImpl) { JCSystem.makeTransientShortArray((short) TMP_VARIABLE_ARRAY_SIZE, JCSystem.CLEAR_ON_RESET); if(!isUpgrading) { keymasterState = KMKeymasterApplet.INIT_STATE; - seProvider.getTrueRandomNumber(buf, (short) 0, KMRepository.MASTER_KEY_SIZE); - repository.initMasterKey(buf, (short)0, KMRepository.MASTER_KEY_SIZE); + seProvider.createMasterKey((short) (KMRepository.MASTER_KEY_SIZE * 8)); } KMType.initialize(); encoder = new KMEncoder(); @@ -520,16 +520,12 @@ private void processDeviceLockedCmd(APDU apdu) { data[VERIFICATION_TOKEN] = KMArray.cast(tmpVariables[0]).get((short) 1); validateVerificationToken(data[VERIFICATION_TOKEN], scratchPad); short verTime = KMVerificationToken.cast(data[VERIFICATION_TOKEN]).getTimestamp(); - // short lastDeviceLockedTime = KMInteger.uint_64(repository.deviceLockedTimestamp, (short)0); short lastDeviceLockedTime = repository.getDeviceTimeStamp(); if (KMInteger.compare(verTime, lastDeviceLockedTime) > 0) { Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 8, (byte) 0); KMInteger.cast(verTime).getValue(scratchPad, (short) 0, (short) 8); - // repository.deviceLockedFlag = true; repository.setDeviceLock(true); - // repository.deviceUnlockPasswordOnly = (tmpVariables[1] == 0x01); repository.setDeviceLockPasswordOnly(tmpVariables[1] == 0x01); - // Util.arrayCopy(scratchPad,(short)0,repository.deviceLockedTimestamp,(short)0,(short)repository.deviceLockedTimestamp.length); repository.setDeviceLockTimestamp(scratchPad, (short) 0, (short) 8); } sendError(apdu, KMError.OK); @@ -541,6 +537,11 @@ private void resetData() { data[index] = KMType.INVALID_VALUE; index++; } + index = 0; + while (index < tmpVariables.length) { + tmpVariables[index] = KMType.INVALID_VALUE; + index++; + } } /** Sends a response, may be extended response, as requested by the command. */ public static void sendOutgoing(APDU apdu) { @@ -550,7 +551,6 @@ public static void sendOutgoing(APDU apdu) { } // Send data apdu.setOutgoing(); - // short currentBlockSize = apdu.getOutBlockSize(); apdu.setOutgoingLength(bufferLength); apdu.sendBytesLong(buffer, bufferStartOffset, bufferLength); } @@ -752,7 +752,10 @@ private void processProvisionAttestationKey(APDU apdu) { importECKeys(scratchPad); // persist key - repository.persistAttestationKey(data[SECRET]); + seProvider.createAttestationKey( + KMByteBlob.cast(data[SECRET]).getBuffer(), + KMByteBlob.cast(data[SECRET]).getStartOff(), + KMByteBlob.cast(data[SECRET]).length()); } private void processProvisionAttestIdsCmd(APDU apdu) { @@ -795,7 +798,7 @@ private void processProvisionSharedSecretCmd(APDU apdu) { KMException.throwIt(KMError.INVALID_ARGUMENT); } // Persist shared Hmac. - repository.initHmacSharedSecretKey( + seProvider.createPresharedKey( KMByteBlob.cast(tmpVariables[0]).getBuffer(), KMByteBlob.cast(tmpVariables[0]).getStartOff(), KMByteBlob.cast(tmpVariables[0]).length()); @@ -915,7 +918,6 @@ private void processDeleteKeyCmd(APDU apdu) { // Receive the incoming request fully from the master. receiveIncoming(apdu); - // Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) apdu.getBuffer().length, (byte) 0); // Arguments short argsProto = KMArray.instance((short) 1); KMArray.cast(argsProto).add((short) 0, KMByteBlob.exp()); @@ -1033,12 +1035,9 @@ private void processComputeSharedHmacCmd(APDU apdu) { } // generate the key and store it in scratch pad - 32 bytes - tmpVariables[8] = repository.getSharedKey(); tmpVariables[6] = - seProvider.cmacKdf( - KMByteBlob.cast(tmpVariables[8]).getBuffer(), - KMByteBlob.cast(tmpVariables[8]).getStartOff(), - KMByteBlob.cast(tmpVariables[8]).length(), + seProvider.cmacKDF( + seProvider.getPresharedKey(), ckdfLable, (short) 0, (short) ckdfLable.length, @@ -1397,7 +1396,7 @@ private void processAttestKeyCmd(APDU apdu) { // validity period // active time or creation time - byte blob - // TODO current assumption is that if active and creation time are missing from characteristics + // current assumption is that if active and creation time are missing from characteristics // then // then it is an error. tmpVariables[1] = @@ -1422,16 +1421,15 @@ private void processAttestKeyCmd(APDU apdu) { addTags( KMKeyCharacteristics.cast(data[KEY_CHARACTERISTICS]).getSoftwareEnforced(), false, cert); - cert.deviceLocked(repository.getDeviceLock()); + cert.deviceLocked(repository.getBootLoaderLock()); cert.issuer(repository.getIssuer()); cert.publicKey(data[PUB_KEY]); - cert.signingKey(repository.getAttKey()); cert.verifiedBootHash(repository.getVerifiedBootHash()); cert.verifiedBootKey(repository.getVerifiedBootKey()); cert.verifiedBootState(repository.getBootState()); // buffer for cert - we allocate 2KBytes buffer - // TODO make this buffer size configurable + // make this buffer size configurable tmpVariables[3] = KMByteBlob.instance(MAX_CERT_SIZE); buffer = KMByteBlob.cast(tmpVariables[3]).getBuffer(); bufferStartOffset = KMByteBlob.cast(tmpVariables[3]).getStartOff(); @@ -1467,24 +1465,6 @@ private void addAttestationIds(KMAttestationCert cert) { } index++; } - /* - if(repository.isAttIdSupported()){ - short attTag; - short blob; - byte index = 0; - while (index < repository.ATT_ID_TABLE_SIZE) { - if (repository.getAttIdLen(index) != 0) { - blob = KMByteBlob.instance( - repository.getAttIdBuffer(index), - repository.getAttIdOffset(index), - repository.getAttIdLen(index)); - attTag = KMByteTag.instance(repository.getAttIdTag(index), blob); - cert.extensionTag(attTag,true); - index++; - } - } - } - */ } private void addTags(short params, boolean hwEnforced, KMAttestationCert cert) { @@ -1529,8 +1509,6 @@ private void setUniqueId(KMAttestationCert cert, byte[] scratchPad) { resetAfterRotation = 0x01; } - //master key. - tmpVariables[2] = repository.getMasterKeySecret(); cert.makeUniqueId( scratchPad, (short) 0, @@ -1540,9 +1518,7 @@ private void setUniqueId(KMAttestationCert cert, byte[] scratchPad) { KMByteBlob.cast(tmpVariables[1]).getBuffer(), KMByteBlob.cast(tmpVariables[1]).getStartOff(), KMByteBlob.cast(tmpVariables[1]).length(), resetAfterRotation, - KMByteBlob.cast(tmpVariables[2]).getBuffer(), - KMByteBlob.cast(tmpVariables[2]).getStartOff(), - KMByteBlob.cast(tmpVariables[2]).length()); + seProvider.getMasterKey()); } private void processDestroyAttIdsCmd(APDU apdu) { @@ -1637,10 +1613,6 @@ private void processFinishOperationCmd(APDU apdu) { private void finishEncryptOperation(KMOperationState op, byte[] scratchPad) { short len = KMByteBlob.cast(data[INPUT_DATA]).length(); switch (op.getAlgorithm()) { - // - // RSA Encryption is only supported for testing purpose - // TODO remove this later on if not required - // case KMType.RSA: // Output size is always 256 bytes data[OUTPUT_DATA] = KMByteBlob.instance((short) 256); @@ -1699,8 +1671,6 @@ private void finishEncryptOperation(KMOperationState op, byte[] scratchPad) { private void finishDecryptOperation(KMOperationState op, byte[] scratchPad) { short len = KMByteBlob.cast(data[INPUT_DATA]).length(); switch (op.getAlgorithm()) { - // Only supported for testing purpose - // TODO remove this later on case KMType.RSA: // Fill the scratch pad with zero Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 256, (byte) 0); @@ -1913,7 +1883,6 @@ private void authorizeUpdateFinishOperation(KMOperationState op, byte[] scratchP if (KMInteger.compare(tmpVariables[0], tmpVariables[2]) < 0) { KMException.throwIt(KMError.KEY_USER_NOT_AUTHENTICATED); } - // TODO this is not needed op.setAuthTimeoutValidated(true); } else if (op.isAuthPerOperationReqd()) { // If Auth per operation is required tmpVariables[0] = KMHardwareAuthToken.cast(data[HW_TOKEN]).getChallenge(); @@ -1936,11 +1905,6 @@ private void authorizeDeviceUnlock(short hwToken) { ptr = KMHardwareAuthToken.cast(hwToken).getTimestamp(); // Check if the current auth time stamp is greater then device locked time stamp short ts = repository.getDeviceTimeStamp(); - /*if (KMInteger.compare(ptr, KMInteger.uint_64(repository.deviceLockedTimestamp, (short) 0)) - <= 0) { - KMException.throwIt(KMError.DEVICE_LOCKED); - } - */ if (KMInteger.compare(ptr, ts) <= 0) { KMException.throwIt(KMError.DEVICE_LOCKED); } @@ -1957,7 +1921,6 @@ private void authorizeDeviceUnlock(short hwToken) { // repository.deviceLockedFlag = false; repository.setDeviceLock(false); repository.clearDeviceLockTimeStamp(); - // Util.arrayFillNonAtomic(repository.deviceLockedTimestamp, (short) 0, (short) 8, (byte) 0); } } @@ -2012,16 +1975,6 @@ private void validateVerificationToken(short verToken, byte[] scratchPad) { // hmac the data ptr = KMVerificationToken.cast(verToken).getMac(); short key = repository.getComputedHmacKey(); - /*boolean verified = - seProvider.hmacVerify(repository.getComputedHmacKey(), - (short) 0, - (short) repository.getComputedHmacKey().length, - scratchPad, (short) 0, len, - KMByteBlob.cast(ptr).getBuffer(), - KMByteBlob.cast(ptr).getStartOff(), - KMByteBlob.cast(ptr).length()); - - */ boolean verified = seProvider.hmacVerify( KMByteBlob.cast(key).getBuffer(), @@ -2515,8 +2468,6 @@ private void authorizeAndBeginOperation(KMOperationState op, byte[] scratchPad) private void beginCipherOperation(KMOperationState op) { switch (op.getAlgorithm()) { - // Not required to be supported - supported for testing purpose - // TODO remove this later case KMType.RSA: try { if (op.getPurpose() == KMType.DECRYPT) { @@ -2556,7 +2507,6 @@ private void beginCipherOperation(KMOperationState op) { op.setAesGcmUpdateStart(); } try { - // if (data[IV] != KMType.INVALID_VALUE) { op.setOperation( seProvider.initSymmetricOperation( (byte) op.getPurpose(), @@ -2775,15 +2725,6 @@ private boolean validateHwToken(short hwToken, byte[] scratchPad) { // hmac the data ptr = KMHardwareAuthToken.cast(hwToken).getMac(); short key = repository.getComputedHmacKey(); - /*return seProvider.hmacVerify( - repository.getComputedHmacKey(), - (short) 0, - (short) repository.getComputedHmacKey().length, - scratchPad, (short) 0, len, - KMByteBlob.cast(ptr).getBuffer(), - KMByteBlob.cast(ptr).getStartOff(), - KMByteBlob.cast(ptr).length()); - */ return seProvider.hmacVerify( KMByteBlob.cast(key).getBuffer(), KMByteBlob.cast(key).getStartOff(), @@ -3064,7 +3005,6 @@ private void importAESKey(byte[] scratchPad) { KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH); } } else { - // add the key size to scratch pad // add the key size to scratchPad tmpVariables[5] = KMInteger.uint_16(KMByteBlob.cast(data[SECRET]).length()); tmpVariables[6] = KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, tmpVariables[5]); @@ -3193,26 +3133,20 @@ private void processSetBootParamsCmd(APDU apdu) { receiveIncoming(apdu); byte[] scratchPad = apdu.getBuffer(); // Argument 1 OS Version - // short osVersionExp = KMIntegerTag.exp(KMType.UINT_TAG); tmpVariables[0] = KMInteger.exp(); // Argument 2 OS Patch level - // short osPatchExp = KMIntegerTag.exp(KMType.UINT_TAG); tmpVariables[1] = KMInteger.exp(); // Argument 3 Vendor Patch level tmpVariables[2] = KMInteger.exp(); // Argument 4 Boot Patch level tmpVariables[3] = KMInteger.exp(); // Argument 5 Verified Boot Key - // short bootKeyExp = KMByteBlob.exp(); tmpVariables[4] = KMByteBlob.exp(); // Argument 6 Verified Boot Hash - // short bootHashExp = KMByteBlob.exp(); tmpVariables[5] = KMByteBlob.exp(); // Argument 7 Verified Boot State - // short bootStateExp = KMEnum.instance(KMType.VERIFIED_BOOT_STATE); tmpVariables[6] = KMEnum.instance(KMType.VERIFIED_BOOT_STATE); // Argument 8 Device Locked - // short deviceLockedExp = KMEnum.instance(KMType.DEVICE_LOCKED); tmpVariables[7] = KMEnum.instance(KMType.DEVICE_LOCKED); // Array of expected arguments short argsProto = KMArray.instance((short) 8); @@ -3225,26 +3159,17 @@ private void processSetBootParamsCmd(APDU apdu) { KMArray.cast(argsProto).add((short) 6, tmpVariables[6]); KMArray.cast(argsProto).add((short) 7, tmpVariables[7]); // Decode the arguments - // System.out.println("Process boot params buffer: "+byteArrayToHexString(buffer)); short args = decoder.decode(argsProto, buffer, bufferStartOffset, bufferLength); //reclaim memory repository.reclaimMemory(bufferLength); - // short osVersionTagPtr = KMArray.cast(args).get((short) 0); tmpVariables[0] = KMArray.cast(args).get((short) 0); - // short osPatchTagPtr = KMArray.cast(args).get((short) 1); tmpVariables[1] = KMArray.cast(args).get((short) 1); - // short vendorPatchTagPtr = KMArray.cast(args).get((short) 2); tmpVariables[2] = KMArray.cast(args).get((short) 2); - // short BootPatchTagPtr = KMArray.cast(args).get((short) 3); tmpVariables[3] = KMArray.cast(args).get((short) 3); - // short verifiedBootKeyPtr = KMArray.cast(args).get((short) 4); tmpVariables[4] = KMArray.cast(args).get((short) 4); - // short verifiedBootHashPtr = KMArray.cast(args).get((short) 5); tmpVariables[5] = KMArray.cast(args).get((short) 5); - // short verifiedBootStatePtr = KMArray.cast(args).get((short) 6); tmpVariables[6] = KMArray.cast(args).get((short) 6); - // short deviceLockedPtr = KMArray.cast(args).get((short) 7); tmpVariables[7] = KMArray.cast(args).get((short) 7); if (KMByteBlob.cast(tmpVariables[4]).length() > KMRepository.BOOT_KEY_MAX_SIZE) { KMException.throwIt(KMError.INVALID_ARGUMENT); @@ -3286,7 +3211,7 @@ private void processSetBootParamsCmd(APDU apdu) { repository.setBootState(enumVal); enumVal = KMEnum.cast(tmpVariables[7]).getVal(); - repository.setDeviceLock(enumVal == KMType.DEVICE_LOCKED_TRUE); + repository.setBootloaderLocked(enumVal == KMType.DEVICE_LOCKED_TRUE); // Clear the Computed SharedHmac and Hmac nonce from persistent memory. repository.clearComputedHmac(); @@ -3428,7 +3353,6 @@ private static void generateRSAKey(byte[] scratchPad) { // Validate RSA Key validateRSAKey(scratchPad); // Now generate 2048 bit RSA keypair for the given exponent - // KeyPair rsaKey = seProvider.createRsaKeyPair(); short[] lengths = tmpVariables; data[PUB_KEY] = KMByteBlob.instance((short) 256); data[SECRET] = KMByteBlob.instance((short) 256); @@ -3486,8 +3410,6 @@ private static void generateAESKey(byte[] scratchPad) { validateAESKey(); tmpVariables[0] = KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); - // AESKey aesKey = seProvider.createAESKey(tmpVariables[0]); - // tmpVariables[0] = aesKey.getKey(scratchPad, (short) 0); tmpVariables[0] = seProvider.createSymmetricKey(KMType.AES, tmpVariables[0], scratchPad, (short) 0); data[SECRET] = KMByteBlob.instance(scratchPad, (short) 0, tmpVariables[0]); @@ -3510,7 +3432,6 @@ private static void validateECKeys() { private static void generateECKeys(byte[] scratchPad) { validateECKeys(); - // KeyPair ecKey = seProvider.createECKeyPair(); short[] lengths = tmpVariables; seProvider.createAsymmetricKey( KMType.EC, @@ -3521,9 +3442,7 @@ private static void generateECKeys(byte[] scratchPad) { (short) 128, (short) 128, lengths); - // tmpVariables[5] = ((ECPublicKey) ecKey.getPublic()).getW(scratchPad, (short) 0); data[PUB_KEY] = KMByteBlob.instance(scratchPad, (short) 128, lengths[1]); - // tmpVariables[5] = ((ECPrivateKey) ecKey.getPrivate()).getS(scratchPad, (short) 0); data[SECRET] = KMByteBlob.instance(scratchPad, (short) 0, lengths[0]); data[KEY_BLOB] = KMArray.instance((short) 5); KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_PUB_KEY, data[PUB_KEY]); @@ -3549,8 +3468,6 @@ private static void validateTDESKey() { private static void generateTDESKey(byte[] scratchPad) { validateTDESKey(); - // DESKey desKey = seProvider.createTDESKey(); - // tmpVariables[0] = desKey.getKey(scratchPad, (short) 0); tmpVariables[0] = seProvider.createSymmetricKey(KMType.DES, (short) 168, scratchPad, (short) 0); data[SECRET] = KMByteBlob.instance(scratchPad, (short) 0, tmpVariables[0]); data[KEY_BLOB] = KMArray.instance((short) 4); @@ -3596,8 +3513,6 @@ private static void generateHmacKey(byte[] scratchPad) { tmpVariables[0] = KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); // generate HMAC Key - // HMACKey hmacKey = seProvider.createHMACKey(tmpVariables[1]); - // tmpVariables[0] = hmacKey.getKey(scratchPad, (short) 0); tmpVariables[0] = seProvider.createSymmetricKey(KMType.HMAC, tmpVariables[0], scratchPad, (short) 0); data[SECRET] = KMByteBlob.instance(scratchPad, (short) 0, tmpVariables[0]); @@ -3624,20 +3539,6 @@ private void checkVersionAndPatchLevel(byte[] scratchPad) { KMException.throwIt(KMError.KEY_REQUIRES_UPGRADE); } } - /* - if ((tmpVariables[0] != KMType.INVALID_VALUE) - && (Util.arrayCompare( - repository.osVersion, (short) 0, scratchPad, (short) 0, tmpVariables[0]) - != 0)) { - if (Util.arrayCompare(repository.osVersion, (short) 0, scratchPad, (short) 0, tmpVariables[0]) - == -1) { - // If the key characteristics has os version > current os version - KMException.throwIt(KMError.INVALID_KEY_BLOB); - } else { - KMException.throwIt(KMError.KEY_REQUIRES_UPGRADE); - } - } - */ tmpVariables[0] = KMIntegerTag.getValue( scratchPad, (short) 0, KMType.UINT_TAG, KMType.OS_PATCH_LEVEL, data[HW_PARAMETERS]); @@ -3656,18 +3557,6 @@ private void checkVersionAndPatchLevel(byte[] scratchPad) { KMException.throwIt(KMError.KEY_REQUIRES_UPGRADE); } } - /*if ((tmpVariables[0] != KMType.INVALID_VALUE) - && (Util.arrayCompare(repository.osPatch, (short) 0, scratchPad, (short) 0, tmpVariables[0]) - != 0)) { - if (Util.arrayCompare(repository.osPatch, (short) 0, scratchPad, (short) 0, tmpVariables[0]) - == -1) { - // If the key characteristics has os patch level > current os patch - KMException.throwIt(KMError.INVALID_KEY_BLOB); - } else { - KMException.throwIt(KMError.KEY_REQUIRES_UPGRADE); - } - } - */ } private static void makeKeyCharacteristics(byte[] scratchPad) { @@ -3694,7 +3583,7 @@ private static void createEncryptedKeyBlob(byte[] scratchPad) { // make key characteristics - returns key characteristics in data[KEY_CHARACTERISTICS] makeKeyCharacteristics(scratchPad); // make root of trust blob - data[ROT] = repository.getVerifiedBootKey(); + data[ROT] = repository.readROT(); // make hidden key params list data[HIDDEN_PARAMETERS] = @@ -3753,11 +3642,7 @@ private static void parseEncryptedKeyBlob(byte[] scratchPad) { KMKeyCharacteristics.cast(data[KEY_CHARACTERISTICS]).getHardwareEnforced(); data[SW_PARAMETERS] = KMKeyCharacteristics.cast(data[KEY_CHARACTERISTICS]).getSoftwareEnforced(); - // make root of trust blob - // data[ROT] = - // KMByteBlob.instance( - // repository.verifiedBootKey, (short) 0, (short) repository.verifiedBootKey.length); - data[ROT] = repository.getVerifiedBootKey(); + data[ROT] = repository.readROT(); data[HIDDEN_PARAMETERS] = KMKeyParameters.makeHidden(data[APP_ID], data[APP_DATA], data[ROT], scratchPad); @@ -3886,62 +3771,37 @@ private static short addPtrToAAD(short dataArrPtr, byte[] aadBuf, short offset) private static short deriveKey(byte[] scratchPad) { tmpVariables[0] = KMKeyParameters.cast(data[HIDDEN_PARAMETERS]).getVals(); - tmpVariables[1] = repository.alloc((short) 256); + tmpVariables[1] = repository.alloc(DERIVE_KEY_INPUT_SIZE); // generate derivation material from hidden parameters tmpVariables[2] = encoder.encode(tmpVariables[0], repository.getHeap(), tmpVariables[1]); + if (DERIVE_KEY_INPUT_SIZE > tmpVariables[2]) { + // Copy KeyCharacteristics in the remaining space of DERIVE_KEY_INPUT_SIZE + Util.arrayCopyNonAtomic(repository.getHeap(), (short) (data[AUTH_DATA]), + repository.getHeap(), + (short) (tmpVariables[1] + tmpVariables[2]), + (short) (DERIVE_KEY_INPUT_SIZE - tmpVariables[2])); + } // KeyDerivation: - // 1. AesGCM Encryption, with below input parameters. - // authData - HIDDEN_PARAMTERS - // Key - Master Key - // InputData - AUTH_DATA - // IV - NONCE - // 2. After encryption it generates two outputs - // a. Encrypted output - // b. Auth Tag - // 3. Do HMAC Sign, with below input parameters. - // Key - Auth Tag (Generated in step 2). - // Input data - Encrypted output (Generated in step 2). - // 4. HMAC Sign generates an output of 32 bytes length. + // 1. Do HMAC Sign, with below input parameters. + // Key - 128 bit master key + // Input data - HIDDEN_PARAMETERS + KeyCharacateristics + // - Truncate beyond 256 bytes. + // 2. HMAC Sign generates an output of 32 bytes length. // Consume only first 16 bytes as derived key. - tmpVariables[4] = repository.getMasterKeySecret(); - tmpVariables[5] = repository.alloc(AES_GCM_AUTH_TAG_LENGTH); - tmpVariables[3] = - seProvider.aesGCMEncrypt( - KMByteBlob.cast(tmpVariables[4]).getBuffer(), - KMByteBlob.cast(tmpVariables[4]).getStartOff(), - KMByteBlob.cast(tmpVariables[4]).length(), - repository.getHeap(), - data[AUTH_DATA], - data[AUTH_DATA_LENGTH], - scratchPad, - (short) 0, - KMByteBlob.cast(data[NONCE]).getBuffer(), - KMByteBlob.cast(data[NONCE]).getStartOff(), - KMByteBlob.cast(data[NONCE]).length(), - repository.getHeap(), - tmpVariables[1], - tmpVariables[2], - repository.getHeap(), - tmpVariables[5], - AES_GCM_AUTH_TAG_LENGTH); // Hmac sign. - tmpVariables[3] = seProvider.hmacSign( + tmpVariables[3] = seProvider.hmacKDF( + seProvider.getMasterKey(), repository.getHeap(), - tmpVariables[5], - AES_GCM_AUTH_TAG_LENGTH, + tmpVariables[1], + DERIVE_KEY_INPUT_SIZE, scratchPad, - (short) 0, - tmpVariables[3], - repository.getHeap(), - tmpVariables[1]); + (short) 0); if (tmpVariables[3] < 16) { KMException.throwIt(KMError.UNKNOWN_ERROR); } tmpVariables[3] = 16; - Util.arrayCopyNonAtomic(repository.getHeap(), tmpVariables[1], scratchPad, - (short) 0, tmpVariables[3]); // store the derived secret in data dictionary - data[DERIVED_KEY] = repository.alloc(tmpVariables[3]); + data[DERIVED_KEY] = tmpVariables[1]; Util.arrayCopyNonAtomic( scratchPad, (short) 0, repository.getHeap(), data[DERIVED_KEY], tmpVariables[3]); return tmpVariables[3]; @@ -3986,44 +3846,4 @@ private void add(byte[] buf, short op1, short op2, short result) { index--; } } -/* - @Override - public void onCleanup() { - } - - @Override - public void onConsolidate() { - } - - @Override - public void onRestore(Element element) { - element.initRead(); - provisionStatus = element.readByte(); - keymasterState = element.readByte(); - repository.onRestore(element); - seProvider.onRestore(element); - } - - @Override - public Element onSave() { - // SEProvider count - short primitiveCount = seProvider.getBackupPrimitiveByteCount(); - short objectCount = seProvider.getBackupObjectCount(); - //Repository count - primitiveCount += repository.getBackupPrimitiveByteCount(); - objectCount += repository.getBackupObjectCount(); - //KMKeymasterApplet count - primitiveCount += computePrimitveDataSize(); - objectCount += computeObjectCount(); - - // Create element. - Element element = UpgradeManager.createElement(Element.TYPE_SIMPLE, - primitiveCount, objectCount); - element.write(provisionStatus); - element.write(keymasterState); - repository.onSave(element); - seProvider.onSave(element); - return element; - } -*/ } diff --git a/Applet/src/com/android/javacard/keymaster/KMMasterKey.java b/Applet/src/com/android/javacard/keymaster/KMMasterKey.java new file mode 100644 index 00000000..0ceb6291 --- /dev/null +++ b/Applet/src/com/android/javacard/keymaster/KMMasterKey.java @@ -0,0 +1,26 @@ +/* + * 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.keymaster; + +/** + * KMMasterKey is a marker interface and the SE Provider has to implement + * this interface. Internally Masterkey is stored as a Javacard AES key object, + * which will provide additional security. The master key is maintained by the + * SEProvider. + */ +public interface KMMasterKey { + +} diff --git a/Applet/src/com/android/javacard/keymaster/KMOperation.java b/Applet/src/com/android/javacard/keymaster/KMOperation.java index 4011d7f5..8db3312b 100644 --- a/Applet/src/com/android/javacard/keymaster/KMOperation.java +++ b/Applet/src/com/android/javacard/keymaster/KMOperation.java @@ -1,3 +1,18 @@ +/* + * 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.keymaster; /** diff --git a/Applet/src/com/android/javacard/keymaster/KMPreSharedKey.java b/Applet/src/com/android/javacard/keymaster/KMPreSharedKey.java new file mode 100644 index 00000000..71dfcae6 --- /dev/null +++ b/Applet/src/com/android/javacard/keymaster/KMPreSharedKey.java @@ -0,0 +1,26 @@ +/* + * 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.keymaster; + +/** + * KMPreSharedKey is a marker interface and the SE Provider has to implement + * this interface. Internally Preshared key is stored as a Javacard HMac key object, + * which will provide additional security. The pre-shared key is maintained by the + * SEProvider. + */ +public interface KMPreSharedKey { + +} diff --git a/Applet/src/com/android/javacard/keymaster/KMRepository.java b/Applet/src/com/android/javacard/keymaster/KMRepository.java index 4c30a613..ee7cc6a8 100644 --- a/Applet/src/com/android/javacard/keymaster/KMRepository.java +++ b/Applet/src/com/android/javacard/keymaster/KMRepository.java @@ -29,7 +29,7 @@ */ public class KMRepository implements KMUpgradable { // Data table configuration - public static final short DATA_INDEX_SIZE = 24; + public static final short DATA_INDEX_SIZE = 22; public static final short DATA_INDEX_ENTRY_SIZE = 4; public static final short DATA_MEM_SIZE = 2048; public static final short HEAP_SIZE = 10000; @@ -42,10 +42,8 @@ public class KMRepository implements KMUpgradable { private static final short OPERATION_HANDLE_ENTRY_SIZE = OPERATION_HANDLE_SIZE + OPERATION_HANDLE_STATUS_SIZE; // Data table offsets - public static final byte MASTER_KEY = 8; - public static final byte SHARED_KEY = 9; - public static final byte COMPUTED_HMAC_KEY = 10; - public static final byte HMAC_NONCE = 11; + public static final byte COMPUTED_HMAC_KEY = 8; + public static final byte HMAC_NONCE = 9; public static final byte ATT_ID_BRAND = 0; public static final byte ATT_ID_DEVICE = 1; public static final byte ATT_ID_PRODUCT = 2; @@ -54,18 +52,18 @@ public class KMRepository implements KMUpgradable { public static final byte ATT_ID_MEID = 5; public static final byte ATT_ID_MANUFACTURER = 6; public static final byte ATT_ID_MODEL = 7; - public static final byte ATT_EC_KEY = 12; - public static final byte CERT_ISSUER = 13; - public static final byte CERT_EXPIRY_TIME = 14; - public static final byte BOOT_OS_VERSION = 15; - public static final byte BOOT_OS_PATCH = 16; - public static final byte VENDOR_PATCH_LEVEL = 17; - public static final byte BOOT_PATCH_LEVEL = 18; - public static final byte BOOT_VERIFIED_BOOT_KEY = 19; - public static final byte BOOT_VERIFIED_BOOT_HASH = 20; - public static final byte BOOT_VERIFIED_BOOT_STATE = 21; - public static final byte BOOT_DEVICE_LOCKED_STATUS = 22; - public static final byte BOOT_DEVICE_LOCKED_TIME = 23; + public static final byte CERT_ISSUER = 10; + public static final byte CERT_EXPIRY_TIME = 11; + public static final byte BOOT_OS_VERSION = 12; + public static final byte BOOT_OS_PATCH = 13; + public static final byte VENDOR_PATCH_LEVEL = 14; + public static final byte BOOT_PATCH_LEVEL = 15; + public static final byte BOOT_VERIFIED_BOOT_KEY = 16; + public static final byte BOOT_VERIFIED_BOOT_HASH = 17; + public static final byte BOOT_VERIFIED_BOOT_STATE = 18; + public static final byte BOOT_DEVICE_LOCKED_STATUS = 19; + public static final byte DEVICE_LOCKED_TIME = 20; + public static final byte DEVICE_LOCKED = 21; // Data Item sizes public static final short MASTER_KEY_SIZE = 16; @@ -114,6 +112,11 @@ public KMRepository(boolean isUpgrading) { new Object[KMOperationState.MAX_REFS]}}; index++; } + //Initialize the device locked status + if (!isUpgrading) { + setDeviceLock(false); + setDeviceLockPasswordOnly(false); + } repository = this; } @@ -168,7 +171,6 @@ public KMOperationState reserveOperation(short opHandle){ return null; } - //TODO refactor following method public void persistOperation(byte[] data, short opHandle, KMOperation op) { short index = 0; byte[] opId; @@ -249,16 +251,6 @@ public void releaseOperation(KMOperationState op) { } } - public void initMasterKey(byte[] key, short start, short len) { - if(len != MASTER_KEY_SIZE) ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); - writeDataEntry(MASTER_KEY,key, start, len); - } - - public void initHmacSharedSecretKey(byte[] key, short start, short len) { - if(len != SHARED_SECRET_KEY_SIZE) KMException.throwIt(KMError.INVALID_INPUT_LENGTH); - writeDataEntry(SHARED_KEY,key,start,len); - } - public void initComputedHmac(byte[] key, short start, short len) { if(len != COMPUTED_HMAC_KEY_SIZE) KMException.throwIt(KMError.INVALID_INPUT_LENGTH); writeDataEntry(COMPUTED_HMAC_KEY,key,start,len); @@ -296,14 +288,9 @@ public void onSelect() { // If write through caching is implemented then this method will restore the data into cache } - public short getMasterKeySecret() { - return readData(MASTER_KEY); - } - // This function uses memory from the back of the heap(transient memory). Call // reclaimMemory function immediately after the use. public short allocReclaimableMemory(short length) { - // TODO Verify the below condition (HEAP_SIZE/2) if ((((short) (reclaimIndex - length)) <= heapIndex) || (length >= HEAP_SIZE / 2)) { ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); @@ -376,7 +363,6 @@ private void clearDataEntry(short id){ if (dataLen != 0) { short dataPtr = Util.getShort(dataTable,(short)(id+DATA_INDEX_ENTRY_OFFSET)); Util.arrayFillNonAtomic(dataTable, dataPtr,dataLen,(byte)0); - //Util.arrayFillNonAtomic(dataTable, id,DATA_INDEX_ENTRY_SIZE,(byte)0); } JCSystem.commitTransaction(); } @@ -422,10 +408,6 @@ public byte[] getHeap() { return heap; } - public short getSharedKey() { - return readData(SHARED_KEY); - } - public short getHmacNonce() { return readData(HMAC_NONCE); } @@ -434,17 +416,6 @@ public short getComputedHmacKey() { return readData(COMPUTED_HMAC_KEY); } - public void persistAttestationKey(short secret) { - writeDataEntry(ATT_EC_KEY, - KMByteBlob.cast(secret).getBuffer(), - KMByteBlob.cast(secret).getStartOff(), - KMByteBlob.cast(secret).length()); - } - - public short getAttKey() { - return readData(ATT_EC_KEY); - } - public void persistAttId(byte id, byte[] buf, short start, short len){ writeDataEntry(id, buf,start,len); } @@ -530,6 +501,39 @@ public short getOsPatch(){ } } + public short readROT() { + short length = dataLength(BOOT_VERIFIED_BOOT_KEY); + length += dataLength(BOOT_VERIFIED_BOOT_HASH); + length += dataLength(BOOT_VERIFIED_BOOT_STATE); + length += dataLength(BOOT_DEVICE_LOCKED_STATUS); + short blob = KMByteBlob.instance(length); + if((length = readDataEntry( + BOOT_VERIFIED_BOOT_KEY, + KMByteBlob.cast(blob).getBuffer(), + KMByteBlob.cast(blob).getStartOff())) == 0){ + return 0; + } + if((length += readDataEntry( + BOOT_VERIFIED_BOOT_HASH, + KMByteBlob.cast(blob).getBuffer(), + (short) (KMByteBlob.cast(blob).getStartOff() + length))) == 0){ + return 0; + } + if((length += readDataEntry( + BOOT_VERIFIED_BOOT_STATE, + KMByteBlob.cast(blob).getBuffer(), + (short) (KMByteBlob.cast(blob).getStartOff() + length))) == 0){ + return 0; + } + if((length += readDataEntry( + BOOT_DEVICE_LOCKED_STATUS, + KMByteBlob.cast(blob).getBuffer(), + (short) (KMByteBlob.cast(blob).getStartOff() + length))) == 0){ + return 0; + } + return blob; + } + public short getVerifiedBootKey(){ return readData(BOOT_VERIFIED_BOOT_KEY); } @@ -538,7 +542,7 @@ public short getVerifiedBootHash(){ return readData(BOOT_VERIFIED_BOOT_HASH); } - public boolean getDeviceLock(){ + public boolean getBootLoaderLock() { short blob = readData(BOOT_DEVICE_LOCKED_STATUS); return (byte)((getHeap())[KMByteBlob.cast(blob).getStartOff()] & 0xFE) != 0; } @@ -548,13 +552,18 @@ public byte getBootState(){ return (getHeap())[KMByteBlob.cast(blob).getStartOff()]; } + public boolean getDeviceLock(){ + short blob = readData(DEVICE_LOCKED); + return (byte)((getHeap())[KMByteBlob.cast(blob).getStartOff()] & 0xFE) != 0; + } + public boolean getDeviceLockPasswordOnly(){ - short blob = readData(BOOT_DEVICE_LOCKED_STATUS); + short blob = readData(DEVICE_LOCKED); return (byte)((getHeap())[KMByteBlob.cast(blob).getStartOff()] & 0xFD) != 0; } public short getDeviceTimeStamp(){ - short blob = readData(BOOT_DEVICE_LOCKED_TIME); + short blob = readData(DEVICE_LOCKED_TIME); if(blob != 0){ return KMInteger.uint_64(KMByteBlob.cast(blob).getBuffer(), KMByteBlob.cast(blob).getStartOff()); @@ -580,27 +589,34 @@ public void setBootPatchLevel(byte[] buf, short start, short len) { writeDataEntry(BOOT_PATCH_LEVEL, buf, start, len); } - public void setDeviceLock(boolean flag){ + public void setBootloaderLocked(boolean flag) { short start = alloc(DEVICE_LOCK_FLAG_SIZE); if(flag) (getHeap())[start] = (byte)((getHeap())[start] | 0x01); else (getHeap())[start] = (byte)((getHeap())[start] & 0xFE); writeDataEntry(BOOT_DEVICE_LOCKED_STATUS,getHeap(),start,DEVICE_LOCK_FLAG_SIZE); } + public void setDeviceLock(boolean flag){ + short start = alloc(DEVICE_LOCK_FLAG_SIZE); + if(flag) (getHeap())[start] = (byte)((getHeap())[start] | 0x01); + else (getHeap())[start] = (byte)((getHeap())[start] & 0xFE); + writeDataEntry(DEVICE_LOCKED,getHeap(),start,DEVICE_LOCK_FLAG_SIZE); + } + public void setDeviceLockPasswordOnly(boolean flag){ short start = alloc(DEVICE_LOCK_FLAG_SIZE); if(flag) (getHeap())[start] = (byte)((getHeap())[start] | 0x02); else (getHeap())[start] = (byte)((getHeap())[start] & 0xFD); - writeDataEntry(BOOT_DEVICE_LOCKED_STATUS,getHeap(),start,DEVICE_LOCK_FLAG_SIZE); + writeDataEntry(DEVICE_LOCKED,getHeap(),start,DEVICE_LOCK_FLAG_SIZE); } public void setDeviceLockTimestamp(byte[] buf, short start, short len){ if(len != DEVICE_LOCK_TS_SIZE) KMException.throwIt(KMError.INVALID_INPUT_LENGTH); - writeDataEntry(BOOT_DEVICE_LOCKED_TIME, buf, start,len); + writeDataEntry(DEVICE_LOCKED_TIME, buf, start,len); } public void clearDeviceLockTimeStamp(){ - clearDataEntry(BOOT_DEVICE_LOCKED_TIME); + clearDataEntry(DEVICE_LOCKED_TIME); } public void setOsPatch(byte[] buf, short start, short len){ diff --git a/Applet/src/com/android/javacard/keymaster/KMSEProvider.java b/Applet/src/com/android/javacard/keymaster/KMSEProvider.java index 3e981c9a..a057eb9e 100644 --- a/Applet/src/com/android/javacard/keymaster/KMSEProvider.java +++ b/Applet/src/com/android/javacard/keymaster/KMSEProvider.java @@ -1,3 +1,18 @@ +/* + * 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.keymaster; import org.globalplatform.upgrade.Element; @@ -205,9 +220,7 @@ boolean aesGCMDecrypt( * This is a oneshot operation that performs key derivation function using cmac kdf (CKDF) as * defined in android keymaster hal definition. * - * @param aesKey is the key to use for ckdf. - * @param aesKeyStart is the start of the aes key buffer. - * @param aesKeyLen is the length of the aes key buffer. + * @param instance 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. @@ -218,10 +231,8 @@ boolean aesGCMDecrypt( * @param keyStart is the start of the output buffer. * @return length of the derived key buffer in bytes. */ - short cmacKdf( - byte[] aesKey, - short aesKeyStart, - short aesKeyLen, + short cmacKDF( + KMPreSharedKey hmacKey, byte[] label, short labelStart, short labelLen, @@ -254,6 +265,26 @@ short hmacSign( 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 instance 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( + KMMasterKey masterkey, + byte[] data, + short dataStart, + short dataLength, + byte[] signature, + short signatureStart); + /** * This is a oneshot operation that verifies the signature using hmac algorithm. * @@ -313,9 +344,7 @@ short rsaDecipherOAEP256( /** * This is a oneshot operation that signs the data using EC private key. * - * @param secret is the private key of P-256 curve. - * @param secretStart is the start of the private key buffer. - * @param secretLength is the length of the private buffer in bytes. + * @param instance of KMAttestationKey. * @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 inpur data buffer in bytes. @@ -324,9 +353,7 @@ short rsaDecipherOAEP256( * @return length of the decrypted data. */ short ecSign256( - byte[] secret, - short secretStart, - short secretLength, + KMAttestationKey ecPrivKey, byte[] inputDataBuf, short inputDataStart, short inputDataLength, @@ -472,4 +499,62 @@ KMOperation initAsymmetricOperation( * @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 keySizeBits key size in bits. + * @return An instance of KMMasterKey. + */ + KMMasterKey createMasterKey(short keySizeBits); + + /** + * This function creates an ECKey and initializes the ECPrivateKey with + * the provided input key data. The initialized Key is maintained by the + * SEProvider. This function should be called only while provisioning the + * attestation key. + * + * @param keyData buffer containing the ec private key. + * @param offset start of the buffer. + * @param length length of the buffer. + * @return An instance of KMAttestationKey. + */ + KMAttestationKey createAttestationKey(byte[] keyData, short offset, short length); + + /** + * This function creates an HMACKey and initializes the key with the + * provided input key data. This created key is maintained by the + * SEProvider. This function should be called only while provisioing the + * pre-shared secret. + * + * @param keyData buffer containing the key data. + * @param offset start of the buffer. + * @param length length of the buffer. + * @return An instance of KMPreSharedKey. + */ + KMPreSharedKey createPresharedKey(byte[] keyData, short offset, short length); + + /** + * Returns the master key. + * + * @return Instance of the KMMasterKey + */ + KMMasterKey getMasterKey(); + + /** + * Returns the attestation key. + * + * @return Instance of the KMAttestationKey. + */ + KMAttestationKey getAttestationKey(); + + /** + * Returns the preshared key. + * + * @return Instance of the KMPreSharedKey. + */ + KMPreSharedKey getPresharedKey(); + } diff --git a/Applet/src/com/android/javacard/keymaster/KMUpgradable.java b/Applet/src/com/android/javacard/keymaster/KMUpgradable.java index e3958a67..6815374e 100644 --- a/Applet/src/com/android/javacard/keymaster/KMUpgradable.java +++ b/Applet/src/com/android/javacard/keymaster/KMUpgradable.java @@ -1,3 +1,18 @@ +/* + * 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.keymaster; import org.globalplatform.upgrade.Element; diff --git a/HAL/keymaster/4.1/JavacardKeymaster4Device.cpp b/HAL/keymaster/4.1/JavacardKeymaster4Device.cpp index 3086e287..c9f3ab96 100644 --- a/HAL/keymaster/4.1/JavacardKeymaster4Device.cpp +++ b/HAL/keymaster/4.1/JavacardKeymaster4Device.cpp @@ -38,7 +38,7 @@ #include #include -#define JAVACARD_KEYMASTER_NAME "JavacardKeymaster4.1Device v0.1" +#define JAVACARD_KEYMASTER_NAME "JavacardKeymaster4.1Device v1.0" #define JAVACARD_KEYMASTER_AUTHOR "Android Open Source Project" #define APDU_CLS 0x80