diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAESKey.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAESKey.java index 3b396bab..217d012c 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAESKey.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAESKey.java @@ -21,24 +21,12 @@ public class KMAESKey implements KMMasterKey { - private AESKey aesKey; + public 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); } diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java index 587b2653..d5d5674a 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java @@ -23,6 +23,11 @@ import javacard.framework.ISOException; public class KMAndroidSEApplet extends KMKeymasterApplet implements OnUpgradeListener { + // provisionStatus - 1 byte + // keymasterState - 1 byte + // MagicNumber - 1 byte + // Applet package version - 2 bytes. + private static final byte PRIMITIVE_DATA_STORAGE_SIZE = 0x05; KMAndroidSEApplet() { super(new KMAndroidSEProvider()); @@ -78,8 +83,8 @@ public Element onSave() { primitiveCount += repository.getBackupPrimitiveByteCount(); objectCount += repository.getBackupObjectCount(); //KMKeymasterApplet count - primitiveCount += computePrimitveDataSize(); - objectCount += computeObjectCount(); + primitiveCount += PRIMITIVE_DATA_STORAGE_SIZE; + // No objects to be stored in KMAndroidSEApplet. // Create element. Element element = UpgradeManager.createElement(Element.TYPE_SIMPLE, @@ -93,15 +98,6 @@ public Element onSave() { return element; } - private short computePrimitveDataSize() { - // provisionStatus + keymasterState + magic byte + version - return (short) 5; - } - - private short computeObjectCount() { - return (short) 0; - } - public boolean isUpgradeAllowed(short oldVersion) { boolean upgradeAllowed = false; short oldMajorVersion = (short) ((oldVersion >> 8) & 0x00FF); diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEProvider.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEProvider.java index 23b4b701..2e1da9d0 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEProvider.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEProvider.java @@ -17,6 +17,7 @@ import org.globalplatform.upgrade.Element; import org.globalplatform.upgrade.UpgradeManager; +import org.jcp.xml.dsig.internal.dom.Utils; import javacard.framework.APDU; import javacard.framework.JCSystem; @@ -227,14 +228,18 @@ public KMAndroidSEProvider() { initECKey(ecKeyPair); // Re-usable cipher and signature instances - cipherPool = new Object[(short) (CIPHER_ALGS.length * 4)]; + cipherPool = new Object[(short) (CIPHER_ALGS.length * MAX_OPERATION_INSTANCES)]; // Extra 4 algorithms are used to support TRUSTED_CONFIRMATION_REQUIRED feature. - sigPool = new Object[(short) ((SIG_ALGS.length * 4) + 4)]; + sigPool = + new Object[(short) ((SIG_ALGS.length * MAX_OPERATION_INSTANCES) + + MAX_OPERATION_INSTANCES)]; operationPool = new Object[MAX_OPERATION_INSTANCES]; hmacSignOperationPool = new Object[MAX_OPERATION_INSTANCES]; // Reserve (KEY_ALGS.length * 4) + 4) size of key pool // Extra 4 keys for TRUSTED_CONFIRMATION_REQUIRED feature. - keysPool = new Object[(short) ((KEY_ALGS.length * 4) + 4)]; + keysPool = + new Object[(short) ((KEY_ALGS.length * MAX_OPERATION_INSTANCES) + + MAX_OPERATION_INSTANCES)]; // Creates an instance of each cipher algorithm once. initializeCipherPool(); @@ -298,9 +303,8 @@ private void initECKey(KeyPair ecKeyPair) { } private boolean isCipherAlgorithm(byte alg) { - short index = 0; - while (index < CIPHER_ALGS.length) { - if (CIPHER_ALGS[index++] == alg) { + for (short index = 0; index < CIPHER_ALGS.length; index++) { + if (CIPHER_ALGS[index] == alg) { return true; } } @@ -308,9 +312,8 @@ private boolean isCipherAlgorithm(byte alg) { } private boolean isSignerAlgorithm(byte alg) { - short index = 0; - while (index < SIG_ALGS.length) { - if (SIG_ALGS[index++] == alg) { + for (short index = 0; index < SIG_ALGS.length; index++) { + if (SIG_ALGS[index] == alg) { return true; } } @@ -318,35 +321,27 @@ private boolean isSignerAlgorithm(byte alg) { } private void initializeOperationPool() { - short index = 0; - while (index < MAX_OPERATION_INSTANCES) { + for (short index = 0; index < MAX_OPERATION_INSTANCES; index++) { operationPool[index] = new KMOperationImpl(); - index++; } } private void initializeHmacSignOperationPool() { - short index = 0; - while (index < MAX_OPERATION_INSTANCES) { + for (short index = 0; index < MAX_OPERATION_INSTANCES; index++) { hmacSignOperationPool[index] = new KMOperationImpl(); - index++; } } // Create a signature instance of each algorithm once. private void initializeSigPool() { - short index = 0; - while (index < SIG_ALGS.length) { + for (short index = 0; index < SIG_ALGS.length; index++) { sigPool[index] = getSignatureInstance(SIG_ALGS[index]); - index++; } } private void initializeKeysPool() { - short index = 0; - while (index < KEY_ALGS.length) { + for (short index = 0; index < KEY_ALGS.length; index++) { keysPool[index] = createKeyObjectInstance(KEY_ALGS[index]); - index++; } } @@ -371,10 +366,8 @@ private Cipher getCipherInstance(byte alg) { // Create a cipher instance of each algorithm once. private void initializeCipherPool() { - short index = 0; - while (index < CIPHER_ALGS.length) { + for (short index = 0; index < CIPHER_ALGS.length; index++) { cipherPool[index] = getCipherInstance(CIPHER_ALGS[index]); - index++; } } @@ -477,13 +470,13 @@ public KMKeyObject getKeyObjectFromPool(byte algo, short secretLength) { if (KMType.HMAC == algo) { maxOperations = HMAC_MAX_OPERATION_INSTANCES; } - if(algo == KMType.AES) { + if (algo == KMType.AES) { if (secretLength == 16) { algo = AES_128; } else if (secretLength == 32) { - algo = AES_256; + algo = AES_256; } else { - CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); } } short index = 0; @@ -500,7 +493,7 @@ public KMKeyObject getKeyObjectFromPool(byte algo, short secretLength) { break; } keyObject = (KMKeyObject) keysPool[index]; - if (algo == keyObject.getAlgorithm()) { + if (algo == keyObject.algorithm) { // Check if the Object instance is not busy and free to use. if (!isResourceBusy(keyObject, RESOURCE_TYPE_KEY)) { break; @@ -797,8 +790,7 @@ public HMACKey cmacKdf(KMPreSharedKey preSharedKey, byte[] label, short labelSta 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); + ((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); @@ -837,8 +829,7 @@ public short hmacSign(HMACKey key, byte[] data, short dataStart, @Override public boolean hmacVerify(KMComputedHmacKey key, byte[] data, short dataStart, short dataLength, byte[] mac, short macStart, short macLength) { - KMHmacKey hmacKey = (KMHmacKey) key; - hmacSignature.init(hmacKey.getKey(), Signature.MODE_VERIFY); + hmacSignature.init(((KMHmacKey) key).hmacKey, Signature.MODE_VERIFY); return hmacSignature.verify(data, dataStart, dataLength, mac, macStart, macLength); } @@ -854,10 +845,9 @@ public short hmacSign(byte[] keyBuf, short keyStart, short keyLength, 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); + ((KMAESKey) masterkey).aesKey.getKey(tmpArray, (short) 0); HMACKey key = createHMACKey(tmpArray, (short) 0, - (short) (aesKey.getSize() / 8)); + (short) (((KMAESKey) masterkey).aesKey.getSize() / 8)); return hmacSign(key, data, dataStart, dataLength, signature, signatureStart); } finally { @@ -983,7 +973,7 @@ private byte mapCipherAlg(byte alg, byte padding, byte blockmode, byte digest) { public Cipher createSymmetricCipher(short alg, short purpose, short blockMode, short padding, byte[] secret, short secretStart, short secretLength, byte[] ivBuffer, short ivStart, short ivLength, KMKeyObject keyObject) { - Key key = (Key) keyObject.getKeyObjectInstance(); + Key key = (Key) keyObject.keyObjectInst; Cipher symmCipher = null; switch (secretLength) { case 16: @@ -1026,7 +1016,7 @@ public Cipher createSymmetricCipher(short alg, short purpose, private Signature createHmacSignerVerifier(short purpose, short digest, byte[] secret, short secretStart, short secretLength, KMKeyObject keyObject) { - HMACKey key = (HMACKey) keyObject.getKeyObjectInstance(); + HMACKey key = (HMACKey) keyObject.keyObjectInst; key.setKey(secret, secretStart, secretLength); return createHmacSignerVerifier(purpose, digest, key); } @@ -1084,8 +1074,7 @@ public KMOperation initSymmetricOperation(byte purpose, byte alg, @Override public KMOperation initTrustedConfirmationSymmetricOperation(KMComputedHmacKey computedHmacKey) { KMOperationImpl opr = null; - KMHmacKey key = (KMHmacKey) computedHmacKey; - short len = key.getKey(tmpArray, (short) 0); + short len = ((KMHmacKey) computedHmacKey).hmacKey.getKey(tmpArray, (short) 0); KMKeyObject keyObject = getKeyObjectFromPool(KMType.HMAC, len); Signature signerVerifier = createHmacSignerVerifier(KMType.VERIFY, KMType.SHA2_256, tmpArray, (short) 0, len, keyObject); @@ -1108,7 +1097,7 @@ public Signature createRsaSigner(short digest, short padding, byte[] secret, opMode = Signature.MODE_SIGN; } Signature rsaSigner = getSignatureInstanceFromPool(alg); - RSAPrivateKey key = (RSAPrivateKey) ((KeyPair)(keyObject.getKeyObjectInstance())).getPrivate(); + RSAPrivateKey key = (RSAPrivateKey) ((KeyPair)(keyObject.keyObjectInst)).getPrivate(); key.setExponent(secret, secretStart, secretLength); key.setModulus(modBuffer, modOff, modLength); rsaSigner.init(key, opMode); @@ -1120,7 +1109,7 @@ public Cipher createRsaDecipher(short padding, short digest, byte[] secret, short modLength, KMKeyObject keyObject) { byte cipherAlg = mapCipherAlg(KMType.RSA, (byte) padding, (byte) 0, (byte) digest); Cipher rsaCipher = getCipherInstanceFromPool(cipherAlg); - RSAPrivateKey key = (RSAPrivateKey) ((KeyPair)(keyObject.getKeyObjectInstance())).getPrivate(); + RSAPrivateKey key = (RSAPrivateKey) ((KeyPair)(keyObject.keyObjectInst)).getPrivate(); key.setExponent(secret, secretStart, secretLength); key.setModulus(modBuffer, modOff, modLength); rsaCipher.init(key, Cipher.MODE_DECRYPT); @@ -1131,7 +1120,7 @@ public Signature createEcSigner(short digest, byte[] secret, short secretStart, short secretLength, KMKeyObject keyObject) { byte alg = mapSignature256Alg(KMType.EC, (byte) 0, (byte) digest); Signature ecSigner = null; - ECPrivateKey key = (ECPrivateKey) ((KeyPair)(keyObject.getKeyObjectInstance())).getPrivate(); + ECPrivateKey key = (ECPrivateKey) ((KeyPair)(keyObject.keyObjectInst)).getPrivate(); key.setS(secret, secretStart, secretLength); ecSigner = getSignatureInstanceFromPool(alg); ecSigner.init(key, Signature.MODE_SIGN); @@ -1358,7 +1347,7 @@ public KMMasterKey createMasterKey(short keySizeBits) { masterKey = new KMAESKey(key); short keyLen = (short) (keySizeBits / 8); getTrueRandomNumber(tmpArray, (short) 0, keyLen); - masterKey.setKey(tmpArray, (short) 0); + ((KMAESKey) masterKey).aesKey.setKey(tmpArray, (short) 0); } return (KMMasterKey) masterKey; } finally { @@ -1375,10 +1364,11 @@ public KMAttestationKey createAttestationKey(byte[] keyData, short offset, initECKey(ecKeyPair); attestationKey = new KMECPrivateKey(ecKeyPair); } - attestationKey.setS(keyData, offset, length); + ECPrivateKey ecPriv = (ECPrivateKey) ((KMECPrivateKey) attestationKey).ecKeyPair.getPrivate(); + ecPriv.setS(keyData, offset, length); return (KMAttestationKey) attestationKey; } - + @Override public KMComputedHmacKey createComputedHmacKey(byte[] keyData, short offset, short length) { if (length != COMPUTED_HMAC_KEY_SIZE) { @@ -1389,7 +1379,7 @@ public KMComputedHmacKey createComputedHmacKey(byte[] keyData, short offset, sho false); computedHmacKey = new KMHmacKey(key); } - computedHmacKey.setKey(keyData, offset, length); + ((KMHmacKey) computedHmacKey).hmacKey.setKey(keyData, offset, length); return (KMComputedHmacKey) computedHmacKey; } @@ -1404,7 +1394,7 @@ public KMPreSharedKey createPresharedKey(byte[] keyData, short offset, short len false); preSharedKey = new KMHmacKey(key); } - preSharedKey.setKey(keyData, offset, length); + ((KMHmacKey) preSharedKey).hmacKey.setKey(keyData, offset, length); return (KMPreSharedKey) preSharedKey; } @@ -1502,7 +1492,10 @@ private KMKeyObject createKeyObjectInstance(byte alg) { KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM); } KMKeyObject ptr = new KMKeyObject(); - ptr.setKeyObjectData(alg, keyObject); + JCSystem.beginTransaction(); + ptr.algorithm = alg; + ptr.keyObjectInst = keyObject; + JCSystem.commitTransaction(); return ptr; } diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAttestationCertImpl.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAttestationCertImpl.java index e07cfdec..66ebd74f 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAttestationCertImpl.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAttestationCertImpl.java @@ -881,7 +881,7 @@ public KMAttestationCert makeUniqueId(byte[] scratchPad, short scratchPadOff, timeOffset = KMByteBlob.instance((short) 32); //Get the key data from the master key and use it for HMAC Sign. - AESKey aesKey = ((KMAESKey) masterKey).getKey(); + AESKey aesKey = ((KMAESKey) masterKey).aesKey; short mKeyData = KMByteBlob.instance((short) (aesKey.getSize() / 8)); aesKey.getKey( KMByteBlob.cast(mKeyData).getBuffer(), diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMECPrivateKey.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMECPrivateKey.java index 2ff77da5..bf62eb56 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMECPrivateKey.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMECPrivateKey.java @@ -22,22 +22,12 @@ public class KMECPrivateKey implements KMAttestationKey { - private KeyPair ecKeyPair; + public 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(); } diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMHmacKey.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMHmacKey.java index 8f5e3534..e68a796b 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMHmacKey.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMHmacKey.java @@ -21,28 +21,12 @@ public class KMHmacKey implements KMPreSharedKey, KMComputedHmacKey { - private HMACKey hmacKey; + public 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 HMACKey getKey() { - return hmacKey; - } - - public short getKeySizeBits() { - return hmacKey.getSize(); - } - public static void onSave(Element element, KMHmacKey kmKey) { element.write(kmKey.hmacKey); } diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMKeyObject.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMKeyObject.java index 1c150cb3..3c404257 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMKeyObject.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMKeyObject.java @@ -1,19 +1,6 @@ package com.android.javacard.keymaster; public class KMKeyObject { - private byte algorithm; - private Object keyObjectInst; - - public void setKeyObjectData(byte alg, Object keyObject) { - algorithm = alg; - keyObjectInst = keyObject; - } - - public byte getAlgorithm() { - return this.algorithm; - } - - public Object getKeyObjectInstance() { - return keyObjectInst; - } + public byte algorithm; + public Object keyObjectInst; } diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMUtils.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMUtils.java index b08fe40d..b6bbe6ff 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMUtils.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMUtils.java @@ -438,4 +438,15 @@ public static void convertToMilliseconds(byte[] buf, short inputOff, short outpu } } + public static boolean isEmpty(byte[] buf, short offset, short len) { + boolean empty = true; + for (short index = 0; index < len; index++) { + if (buf[(short) (index + offset)] != 0) { + empty = false; + break; + } + } + return empty; + } + } diff --git a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMUtils.java b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMUtils.java index 567b55cd..a8a338cf 100644 --- a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMUtils.java +++ b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMUtils.java @@ -436,4 +436,14 @@ public static void convertToMilliseconds(byte[] buf, short inputOff, short outpu } } + public static boolean isEmpty(byte[] buf, short offset, short len) { + boolean empty = true; + for (short index = 0; index < len; index++) { + if (buf[(short) (index + offset)] != 0) { + empty = false; + break; + } + } + return empty; + } } diff --git a/Applet/src/com/android/javacard/keymaster/KMAppletState.java b/Applet/src/com/android/javacard/keymaster/KMAppletState.java new file mode 100644 index 00000000..3561a412 --- /dev/null +++ b/Applet/src/com/android/javacard/keymaster/KMAppletState.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" 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; +// This class contains the states associated with +// the Keymaster Applet. +public class KMAppletState { + // Possible states of the applet. + public static final byte KM_BEGIN_STATE = 0x00; + //private static final byte ILLEGAL_STATE = KM_BEGIN_STATE + 1; // Unused. + public static final byte INIT_STATE = KM_BEGIN_STATE + 2; + public static final byte IN_PROVISION_STATE = KM_BEGIN_STATE + 3; + public static final byte ACTIVE_STATE = KM_BEGIN_STATE + 4; +} diff --git a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java index 6cb605de..1c55d5b1 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java @@ -99,14 +99,6 @@ public class KMKeymasterApplet extends Applet implements AppletEvent, ExtendedLe KMType.ATTESTATION_ID_SERIAL }; - - // Possible states of the applet. - private static final byte KM_BEGIN_STATE = 0x00; - private static final byte ILLEGAL_STATE = KM_BEGIN_STATE + 1; - private static final byte INIT_STATE = KM_BEGIN_STATE + 2; - private static final byte IN_PROVISION_STATE = KM_BEGIN_STATE + 3; - private static final byte ACTIVE_STATE = KM_BEGIN_STATE + 4; - // Commands private static final byte INS_BEGIN_KM_CMD = 0x00; // Instructions for Provision Commands. @@ -250,7 +242,7 @@ public class KMKeymasterApplet extends Applet implements AppletEvent, ExtendedLe public static final byte NEGOTIATED_SHARED_SECRET_SUCCESS = 0x04; // Keymaster Applet attributes - protected static byte keymasterState = ILLEGAL_STATE; + protected static byte keymasterState; protected static KMEncoder encoder; protected static KMDecoder decoder; protected static KMRepository repository; @@ -273,7 +265,7 @@ protected KMKeymasterApplet(KMSEProvider seImpl) { repository = new KMRepository(isUpgrading); initializeTransientArrays(); if (!isUpgrading) { - keymasterState = KMKeymasterApplet.INIT_STATE; + keymasterState = KMAppletState.INIT_STATE; seProvider.createMasterKey((short) (KMRepository.MASTER_KEY_SIZE * 8)); } packageVersion = KM_APPLET_PACKAGE_VERSION; @@ -300,8 +292,8 @@ private void initializeTransientArrays() { @Override public boolean select() { repository.onSelect(); - if (keymasterState == KMKeymasterApplet.INIT_STATE) { - keymasterState = KMKeymasterApplet.IN_PROVISION_STATE; + if (keymasterState == KMAppletState.INIT_STATE) { + keymasterState = KMAppletState.IN_PROVISION_STATE; } return true; } @@ -385,7 +377,7 @@ protected void validateApduHeader(APDU apdu) { @Override public void process(APDU apdu) { try { - resetData(); + resetTransientBuffers(); // Handle the card reset status before processing apdu. if (repository.isPowerResetEventOccurred()) { // Release all the operation instances. @@ -393,8 +385,7 @@ public void process(APDU apdu) { } repository.onProcess(); // Verify whether applet is in correct state. - if ((keymasterState == KMKeymasterApplet.INIT_STATE) - || (keymasterState == KMKeymasterApplet.ILLEGAL_STATE)) { + if (keymasterState == KMAppletState.INIT_STATE) { ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); } // If this is select applet apdu which is selecting this applet then @@ -416,13 +407,45 @@ public void process(APDU apdu) { } bufferRef[0] = repository.getHeap(); // Process the apdu - if (keymasterState == KMKeymasterApplet.IN_PROVISION_STATE) { + // Below instructions are allowed in both active state and provision state. + switch (apduIns) { + case INS_SET_BOOT_PARAMS_CMD: + // Allow set boot params only when the host device reboots and the applet is in + // active state. If host does not support boot signal event, then allow this + // instruction any time. + if (seProvider.isBootSignalEventSupported() + && (keymasterState == KMAppletState.ACTIVE_STATE) + && (!seProvider.isDeviceRebooted())) { + ISOException.throwIt(ISO7816.SW_COMMAND_NOT_ALLOWED); + } + // clear the device reboot status + repository.setDeviceBootStatus((byte) 0x00); + processSetBootParamsCmd(apdu); + //set the flag to mark boot started + repository.setDeviceBootStatus(SET_BOOT_PARAMS_SUCCESS); + seProvider.clearDeviceBooted(false); + + sendResponse(apdu, KMError.OK); + return; + + case INS_GET_PROVISION_STATUS_CMD: + processGetProvisionStatusCmd(apdu); + return; + + default: + // Fallback to instructions specific to either provision state or active state + // or both. + break; + } + + // Below instructions are allowed in only provision state. + if (keymasterState == KMAppletState.IN_PROVISION_STATE) { switch (apduIns) { case INS_PROVISION_ATTESTATION_KEY_CMD: if (!isSEFactoryProvisioningLocked()) { processProvisionAttestationKey(apdu); provisionStatus |= KMKeymasterApplet.PROVISION_STATUS_ATTESTATION_KEY; - sendError(apdu, KMError.OK); + sendResponse(apdu, KMError.OK); } else { ISOException.throwIt(ISO7816.SW_COMMAND_NOT_ALLOWED); } @@ -433,7 +456,7 @@ public void process(APDU apdu) { processProvisionAttestationCertDataCmd(apdu); provisionStatus |= (KMKeymasterApplet.PROVISION_STATUS_ATTESTATION_CERT_CHAIN | KMKeymasterApplet.PROVISION_STATUS_ATTESTATION_CERT_PARAMS); - sendError(apdu, KMError.OK); + sendResponse(apdu, KMError.OK); } else { ISOException.throwIt(ISO7816.SW_COMMAND_NOT_ALLOWED); } @@ -442,7 +465,7 @@ public void process(APDU apdu) { case INS_SE_FACTORY_LOCK_PROVISIONING_CMD: if (isSEFactoryProvisioningComplete()) { provisionStatus |= KMKeymasterApplet.PROVISION_STATUS_SE_FACTORY_PROVISIONING_LOCKED; - sendError(apdu, KMError.OK); + sendResponse(apdu, KMError.OK); } else { ISOException.throwIt(ISO7816.SW_COMMAND_NOT_ALLOWED); } @@ -451,19 +474,19 @@ public void process(APDU apdu) { case INS_PROVISION_OEM_ROOT_PUBLIC_KEY_CMD: processProvisionOEMRootPublicKeyCmd(apdu); provisionStatus |= KMKeymasterApplet.PROVISION_STATUS_OEM_ROOT_PUBLIC_KEY; - sendError(apdu, KMError.OK); + sendResponse(apdu, KMError.OK); return; case INS_PROVISION_ATTEST_IDS_CMD: processProvisionAttestIdsCmd(apdu); provisionStatus |= KMKeymasterApplet.PROVISION_STATUS_ATTEST_IDS; - sendError(apdu, KMError.OK); + sendResponse(apdu, KMError.OK); return; case INS_PROVISION_PRESHARED_SECRET_CMD: processProvisionSharedSecretCmd(apdu); provisionStatus |= KMKeymasterApplet.PROVISION_STATUS_PRESHARED_SECRET; - sendError(apdu, KMError.OK); + sendResponse(apdu, KMError.OK); return; case INS_OEM_LOCK_PROVISIONING_CMD: @@ -484,36 +507,17 @@ public void process(APDU apdu) { // UNLOCK command not allowed in IN_PROVISION_STATE ISOException.throwIt(ISO7816.SW_COMMAND_NOT_ALLOWED); return; - } - } - - if ((keymasterState == KMKeymasterApplet.ACTIVE_STATE) - || (keymasterState == KMKeymasterApplet.IN_PROVISION_STATE)) { - switch (apduIns) { - case INS_SET_BOOT_PARAMS_CMD: - if (seProvider.isBootSignalEventSupported() - && (keymasterState == KMKeymasterApplet.ACTIVE_STATE) - && (!seProvider.isDeviceRebooted())) { - ISOException.throwIt(ISO7816.SW_COMMAND_NOT_ALLOWED); - } - // clear the device reboot status - repository.setDeviceBootStatus((byte) 0x00); - processSetBootParamsCmd(apdu); - //set the flag to mark boot started - repository.setDeviceBootStatus(SET_BOOT_PARAMS_SUCCESS); - seProvider.clearDeviceBooted(false); - - sendError(apdu, KMError.OK); - return; - case INS_GET_PROVISION_STATUS_CMD: - processGetProvisionStatusCmd(apdu); - return; + default: + // Fallback to instructions specific to either active state or + // provision completed but not locked state. + break; } } - if ((keymasterState == KMKeymasterApplet.ACTIVE_STATE) - || ((keymasterState == KMKeymasterApplet.IN_PROVISION_STATE) + // Below instructions are allowed only in active state and provision completed state. + if ((keymasterState == KMAppletState.ACTIVE_STATE) + || ((keymasterState == KMAppletState.IN_PROVISION_STATE) && isProvisioningComplete())) { if (!isKeymasterReady(apduIns)) { @@ -610,17 +614,17 @@ && isProvisioningComplete())) { } } catch (KMException exception) { freeOperations(); - sendError(apdu, KMException.getReason()); + sendResponse(apdu, KMException.getReason()); exception.clear(); } catch (ISOException exp) { freeOperations(); - sendError(apdu, mapISOErrorToKMError(exp.getReason())); + sendResponse(apdu, mapISOErrorToKMError(exp.getReason())); } catch (CryptoException e) { freeOperations(); - sendError(apdu, mapCryptoErrorToKMError(e.getReason())); + sendResponse(apdu, mapCryptoErrorToKMError(e.getReason())); } catch (Exception e) { freeOperations(); - sendError(apdu, KMError.GENERIC_UNKNOWN_ERROR); + sendResponse(apdu, KMError.GENERIC_UNKNOWN_ERROR); } finally { repository.clean(); } @@ -694,16 +698,16 @@ private void processOEMUnlockProvisionCmd(APDU apdu) { authenticateOEM(OEM_UNLOCK_VERIFICATION_LABEL, apdu); // Set the OEM Lock bit LOW in provisionStatus. provisionStatus &= ~KMKeymasterApplet.PROVISION_STATUS_OEM_PROVISIONING_LOCKED; - keymasterState = IN_PROVISION_STATE; - sendError(apdu, KMError.OK); + keymasterState = KMAppletState.IN_PROVISION_STATE; + sendResponse(apdu, KMError.OK); } private void processOEMLockProvisionCmd(APDU apdu) { authenticateOEM(OEM_LOCK_VERIFICATION_LABEL, apdu); // Set the OEM Lock bit HIGH in provisionStatus. provisionStatus |= KMKeymasterApplet.PROVISION_STATUS_OEM_PROVISIONING_LOCKED; - keymasterState = ACTIVE_STATE; - sendError(apdu, KMError.OK); + keymasterState = KMAppletState.ACTIVE_STATE; + sendResponse(apdu, KMError.OK); } private void authenticateOEM(byte[] plainMsg, APDU apdu) { @@ -741,7 +745,7 @@ private void freeOperations() { private void processEarlyBootEndedCmd(APDU apdu) { repository.setEarlyBootEndedStatus(true); - sendError(apdu, KMError.OK); + sendResponse(apdu, KMError.OK); } private void processDeviceLockedCmd(APDU apdu) { @@ -763,16 +767,16 @@ private void processDeviceLockedCmd(APDU apdu) { short verTime = KMVerificationToken.cast(data[VERIFICATION_TOKEN]).getTimestamp(); 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); + Util.arrayFillNonAtomic(scratchPad, (short) 0, KMInteger.UINT_64, (byte) 0); + KMInteger.cast(verTime).getValue(scratchPad, (short) 0, KMInteger.UINT_64); repository.setDeviceLock(true); repository.setDeviceLockPasswordOnly(tmpVariables[1] == 0x01); - repository.setDeviceLockTimestamp(scratchPad, (short) 0, (short) 8); + repository.setDeviceLockTimestamp(scratchPad, (short) 0, KMInteger.UINT_64); } - sendError(apdu, KMError.OK); + sendResponse(apdu, KMError.OK); } - private void resetData() { + private void resetTransientBuffers() { short index = 0; while (index < data.length) { data[index] = KMType.INVALID_VALUE; @@ -832,12 +836,12 @@ private void processGetHwInfoCmd(APDU apdu) { bufferProp[BUF_START_OFFSET] = repository.allocAvailableMemory(); // Encode the response - actual bufferProp[BUF_LEN_OFFSET] is 86 bufferProp[BUF_LEN_OFFSET] = encoder.encode(respPtr, (byte[]) bufferRef[0], bufferProp[BUF_START_OFFSET]); - // send buffer to master + // send buffer to host sendOutgoing(apdu); } private void processAddRngEntropyCmd(APDU apdu) { - // Receive the incoming request fully from the master. + // Receive the incoming request fully from the host. receiveIncoming(apdu); // Argument 1 short argsProto = KMArray.instance((short) 1); @@ -854,7 +858,7 @@ private void processAddRngEntropyCmd(APDU apdu) { KMException.throwIt(KMError.INVALID_ARGUMENT); } seProvider.addRngEntropy(blob.getBuffer(), blob.getStartOff(), blob.length()); - sendError(apdu, KMError.OK); + sendResponse(apdu, KMError.OK); } private void processSetVersionAndPatchLevels(APDU apdu) { @@ -895,7 +899,7 @@ private void processSetVersionAndPatchLevels(APDU apdu) { KMInteger.cast(tmpVariables[2]).length()); setDeviceBootStatus(SET_SYSTEM_PROPERTIES_SUCCESS); - sendError(apdu, KMError.OK); + sendResponse(apdu, KMError.OK); } private short getProvisionedCertificateData(byte dataType) { @@ -1199,7 +1203,7 @@ private byte mapToAttId(short attTag) { } private void processGetKeyCharacteristicsCmd(APDU apdu) { - // Receive the incoming request fully from the master. + // Receive the incoming request fully from the host. receiveIncoming(apdu); // Re-purpose the apdu buffer as scratch pad. byte[] scratchPad = apdu.getBuffer(); @@ -1264,16 +1268,16 @@ private void processDeleteAllKeysCmd(APDU apdu) { // No arguments // Send ok - sendError(apdu, KMError.OK); + sendResponse(apdu, KMError.OK); } private void processDeleteKeyCmd(APDU apdu) { // Send ok - sendError(apdu, KMError.OK); + sendResponse(apdu, KMError.OK); } private void processComputeSharedHmacCmd(APDU apdu) { - // Receive the incoming request fully from the master into buffer. + // Receive the incoming request fully from the host into buffer. receiveIncoming(apdu); byte[] scratchPad = apdu.getBuffer(); tmpVariables[1] = KMHmacSharingParameters.exp(); @@ -1393,7 +1397,7 @@ private void processComputeSharedHmacCmd(APDU apdu) { } private void processUpgradeKeyCmd(APDU apdu) { - // Receive the incoming request fully from the master into buffer. + // Receive the incoming request fully from the host into buffer. receiveIncoming(apdu); byte[] scratchPad = apdu.getBuffer(); tmpVariables[1] = KMArray.instance((short) 2); @@ -1473,11 +1477,11 @@ private static void upgradeKeyBlobKeyCharacteristics(short hwParams, short swPar } private void processExportKeyCmd(APDU apdu) { - sendError(apdu, KMError.UNIMPLEMENTED); + sendResponse(apdu, KMError.UNIMPLEMENTED); } private void processImportWrappedKeyCmd(APDU apdu) { - // Receive the incoming request fully from the master into buffer. + // Receive the incoming request fully from the host into buffer. receiveIncoming(apdu); byte[] scratchPad = apdu.getBuffer(); tmpVariables[1] = KMArray.instance((short) 12); @@ -1627,7 +1631,7 @@ private void processImportWrappedKeyCmd(APDU apdu) { } private void processAttestKeyCmd(APDU apdu) { - // Receive the incoming request fully from the master into buffer. + // Receive the incoming request fully from the host into buffer. receiveIncoming(apdu); // Re-purpose the apdu buffer as scratch pad. @@ -1746,19 +1750,6 @@ private void processAttestKeyCmd(APDU apdu) { sendOutgoing(apdu); } - private boolean isEmpty(byte[] buf, short offset, short len) { - boolean empty = true; - short index = 0; - while (index < len) { - if (buf[(short) (index + offset)] != 0) { - empty = false; - break; - } - index++; - } - return empty; - } - // -------------------------------- // Only add the Attestation ids which are requested in the attestation parameters. // If the requested attestation ids are not provisioned or deleted then @@ -1778,7 +1769,7 @@ private void addAttestationIds(KMAttestationCert cert) { // Return CANNOT_ATTEST_IDS if Attestation IDs are not provisioned or // Attestation IDs are deleted. if (storedAttId == KMType.INVALID_VALUE || - isEmpty(KMByteBlob.cast(storedAttId).getBuffer(), + KMUtils.isEmpty(KMByteBlob.cast(storedAttId).getBuffer(), KMByteBlob.cast(storedAttId).getStartOff(), KMByteBlob.cast(storedAttId).length())) { KMException.throwIt(KMError.CANNOT_ATTEST_IDS); @@ -1856,11 +1847,11 @@ private void setUniqueId(KMAttestationCert cert, byte[] scratchPad) { private void processDestroyAttIdsCmd(APDU apdu) { repository.deleteAttIds(); - sendError(apdu, KMError.OK); + sendResponse(apdu, KMError.OK); } private void processVerifyAuthorizationCmd(APDU apdu) { - sendError(apdu, KMError.UNIMPLEMENTED); + sendResponse(apdu, KMError.UNIMPLEMENTED); } private void processAbortOperationCmd(APDU apdu) { @@ -1877,7 +1868,7 @@ private void processAbortOperationCmd(APDU apdu) { KMException.throwIt(KMError.INVALID_OPERATION_HANDLE); } repository.releaseOperation(op); - sendError(apdu, KMError.OK); + sendResponse(apdu, KMError.OK); } private void processFinishOperationCmd(APDU apdu) { @@ -2263,16 +2254,16 @@ private boolean verifyVerificationTokenMacInBigEndian(short verToken, byte[] scr short len = (short) authVerification.length; // concatenate challenge - 8 bytes short ptr = KMVerificationToken.cast(verToken).getChallenge(); - KMInteger.cast(ptr).value(scratchPad, (short) (len + (short) (8 - KMInteger.cast(ptr).length()))); - len += 8; + KMInteger.cast(ptr).value(scratchPad, (short) (len + (short) (KMInteger.UINT_64 - KMInteger.cast(ptr).length()))); + len += KMInteger.UINT_64; // concatenate timestamp -8 bytes ptr = KMVerificationToken.cast(verToken).getTimestamp(); - KMInteger.cast(ptr).value(scratchPad, (short) (len + (short) (8 - KMInteger.cast(ptr).length()))); - len += 8; + KMInteger.cast(ptr).value(scratchPad, (short) (len + (short) (KMInteger.UINT_64 - KMInteger.cast(ptr).length()))); + len += KMInteger.UINT_64; // concatenate security level - 4 bytes ptr = KMVerificationToken.cast(verToken).getSecurityLevel(); scratchPad[(short) (len + 3)] = KMEnum.cast(ptr).getVal(); - len += 4; + len += KMInteger.UINT_32; // concatenate Parameters verified - blob of encoded data. ptr = KMVerificationToken.cast(verToken).getParametersVerified(); if (KMByteBlob.cast(ptr).length() != 0) { @@ -2302,15 +2293,15 @@ private boolean verifyVerificationTokenMacInLittleEndian(short verToken, byte[] // concatenate challenge - 8 bytes short ptr = KMVerificationToken.cast(verToken).getChallenge(); KMInteger.cast(ptr).toLittleEndian(scratchPad, len); - len += 8; + len += KMInteger.UINT_64; // concatenate timestamp -8 bytes ptr = KMVerificationToken.cast(verToken).getTimestamp(); KMInteger.cast(ptr).toLittleEndian(scratchPad, len); - len += 8; + len += KMInteger.UINT_64; // concatenate security level - 4 bytes ptr = KMVerificationToken.cast(verToken).getSecurityLevel(); scratchPad[len] = KMEnum.cast(ptr).getVal(); - len += 4; + len += KMInteger.UINT_32; // concatenate Parameters verified - blob of encoded data. ptr = KMVerificationToken.cast(verToken).getParametersVerified(); if (KMByteBlob.cast(ptr).length() != 0) { @@ -2460,7 +2451,7 @@ private void processUpdateOperationCmd(APDU apdu) { } private void processBeginOperationCmd(APDU apdu) { - // Receive the incoming request fully from the master into buffer. + // Receive the incoming request fully from the host into buffer. receiveIncoming(apdu); byte[] scratchPad = apdu.getBuffer(); short args; @@ -3137,27 +3128,27 @@ private boolean verifyHwTokenMacInBigEndian(short hwToken, byte[] scratchPad) { // concatenate challenge - 8 bytes short ptr = KMHardwareAuthToken.cast(hwToken).getChallenge(); KMInteger.cast(ptr) - .value(scratchPad, (short) (len + (short) (8 - KMInteger.cast(ptr).length()))); - len += 8; + .value(scratchPad, (short) (len + (short) (KMInteger.UINT_64 - KMInteger.cast(ptr).length()))); + len += KMInteger.UINT_64; // concatenate user id - 8 bytes ptr = KMHardwareAuthToken.cast(hwToken).getUserId(); KMInteger.cast(ptr) - .value(scratchPad, (short) (len + (short) (8 - KMInteger.cast(ptr).length()))); - len += 8; + .value(scratchPad, (short) (len + (short) (KMInteger.UINT_64 - KMInteger.cast(ptr).length()))); + len += KMInteger.UINT_64; // concatenate authenticator id - 8 bytes ptr = KMHardwareAuthToken.cast(hwToken).getAuthenticatorId(); KMInteger.cast(ptr) - .value(scratchPad, (short) (len + (short) (8 - KMInteger.cast(ptr).length()))); - len += 8; + .value(scratchPad, (short) (len + (short) (KMInteger.UINT_64 - KMInteger.cast(ptr).length()))); + len += KMInteger.UINT_64; // concatenate authenticator type - 4 bytes ptr = KMHardwareAuthToken.cast(hwToken).getHwAuthenticatorType(); scratchPad[(short) (len + 3)] = KMEnum.cast(ptr).getVal(); - len += 4; + len += KMInteger.UINT_32; // concatenate timestamp -8 bytes ptr = KMHardwareAuthToken.cast(hwToken).getTimestamp(); KMInteger.cast(ptr) - .value(scratchPad, (short) (len + (short) (8 - KMInteger.cast(ptr).length()))); - len += 8; + .value(scratchPad, (short) (len + (short) (KMInteger.UINT_64 - KMInteger.cast(ptr).length()))); + len += KMInteger.UINT_64; ptr = KMHardwareAuthToken.cast(hwToken).getMac(); @@ -3182,24 +3173,24 @@ private boolean verifyHwTokenMacInLittleEndian(short hwToken, byte[] scratchPad) // concatenate challenge - 8 bytes short ptr = KMHardwareAuthToken.cast(hwToken).getChallenge(); KMInteger.cast(ptr).toLittleEndian(scratchPad, len); - len += 8; + len += KMInteger.UINT_64; // concatenate user id - 8 bytes ptr = KMHardwareAuthToken.cast(hwToken).getUserId(); KMInteger.cast(ptr).toLittleEndian(scratchPad, len); - len += 8; + len += KMInteger.UINT_64; // concatenate authenticator id - 8 bytes ptr = KMHardwareAuthToken.cast(hwToken).getAuthenticatorId(); KMInteger.cast(ptr).toLittleEndian(scratchPad, len); - len += 8; + len += KMInteger.UINT_64; // concatenate authenticator type - 4 bytes ptr = KMHardwareAuthToken.cast(hwToken).getHwAuthenticatorType(); scratchPad[(short) (len + 3)] = KMEnum.cast(ptr).getVal(); - len += 4; + len += KMInteger.UINT_32; // concatenate timestamp - 8 bytes ptr = KMHardwareAuthToken.cast(hwToken).getTimestamp(); KMInteger.cast(ptr) .value(scratchPad, (short) (len + (short) (8 - KMInteger.cast(ptr).length()))); - len += 8; + len += KMInteger.UINT_64; ptr = KMHardwareAuthToken.cast(hwToken).getMac(); @@ -3228,7 +3219,7 @@ private boolean validateHwToken(short hwToken, byte[] scratchPad) { } private void processImportKeyCmd(APDU apdu) { - // Receive the incoming request fully from the master into buffer. + // Receive the incoming request fully from the host into buffer. receiveIncoming(apdu); byte[] scratchPad = apdu.getBuffer(); tmpVariables[1] = KMArray.instance((short) 3); @@ -3738,7 +3729,7 @@ private void processSetBootParamsCmd(APDU apdu) { } private static void processGenerateKey(APDU apdu) { - // Receive the incoming request fully from the master into buffer. + // Receive the incoming request fully from the host into buffer. receiveIncoming(apdu); // Re-purpose the apdu buffer as scratch pad. byte[] scratchPad = apdu.getBuffer(); @@ -4397,7 +4388,7 @@ private static short buildErrorStatus(short err) { return int32Ptr; } - private static void sendError(APDU apdu, short err) { + private static void sendResponse(APDU apdu, short err) { bufferProp[BUF_START_OFFSET] = repository.alloc((short) 5); short int32Ptr = buildErrorStatus(err); bufferProp[BUF_LEN_OFFSET] = encoder.encodeError(int32Ptr, (byte[]) bufferRef[0],