diff --git a/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMCipherImpl.java b/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMCipherImpl.java new file mode 100644 index 00000000..66d988d4 --- /dev/null +++ b/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMCipherImpl.java @@ -0,0 +1,31 @@ +package com.android.javacard.keymaster; + +import com.android.javacard.keymaster.KMCipher; +import javacardx.crypto.Cipher; + +public class KMCipherImpl extends KMCipher{ + Cipher cipher; + KMCipherImpl(Cipher c){ + cipher = c; + } + + @Override + public short doFinal(byte[] buffer, short startOff, short length, byte[] scratchPad, short i) { + return cipher.doFinal(buffer, startOff, length, scratchPad, i); + } + + @Override + public short getCipherAlgorithm() { + return cipher.getCipherAlgorithm(); + } + + @Override + public short update(byte[] buffer, short startOff, short length, byte[] scratchPad, short i) { + return cipher.update(buffer,startOff,length,scratchPad,i); + } + + @Override + public short getPaddingAlgorithm() { + return cipher.getPaddingAlgorithm(); + } +} diff --git a/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMCryptoProviderImpl.java b/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMCryptoProviderImpl.java new file mode 100644 index 00000000..917b4911 --- /dev/null +++ b/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMCryptoProviderImpl.java @@ -0,0 +1,8 @@ + +package com.android.javacard.keymaster; + +public class KMCryptoProviderImpl { + public static KMCryptoProvider instance(){ + return new KMJcardSimulator(); + } +} diff --git a/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMJcardSimulator.java b/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMJcardSimulator.java new file mode 100644 index 00000000..2e12de18 --- /dev/null +++ b/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMJcardSimulator.java @@ -0,0 +1,672 @@ +/* + * 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; + +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import javacard.framework.ISO7816; +import javacard.framework.ISOException; +import javacard.framework.JCSystem; +import javacard.framework.Util; +import javacard.security.AESKey; +import javacard.security.CryptoException; +import javacard.security.DESKey; +import javacard.security.ECPrivateKey; +import javacard.security.HMACKey; +import javacard.security.Key; +import javacard.security.KeyBuilder; +import javacard.security.KeyPair; +import javacard.security.RSAPrivateKey; +import javacard.security.RandomData; +import javacard.security.Signature; +import javacardx.crypto.Cipher; +import javax.crypto.AEADBadTagException; +import javax.crypto.BadPaddingException; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.ShortBufferException; +import javax.crypto.spec.GCMParameterSpec; +import javax.crypto.spec.SecretKeySpec; + +/** + * Simulator only supports 512 bit RSA key pair, 128 AES Key, 128 bit 3Des key, less then 256 bit EC + * Key, and upto 512 bit HMAC key. Also simulator does not support TRNG, so this implementation just + * creates its own RNG using PRNG. + */ +public class KMJcardSimulator implements KMCryptoProvider { + public static final short AES_GCM_TAG_LENGTH = 12; + public static final short AES_GCM_NONCE_LENGTH = 12; + public static final short MAX_RND_NUM_SIZE = 64; + 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 int AES_GCM_KEY_SIZE = 16; + public static boolean jcardSim = false; + private static Signature kdf; + private static Signature hmacSignature; + + private static byte[] rngCounter; + private static AESKey aesRngKey; + private static Cipher aesRngCipher; + private static byte[] entropyPool; + private static byte[] rndNum; + + // Implements Oracle Simulator based restricted crypto provider + public KMJcardSimulator() { + // Various Keys + kdf = Signature.getInstance(Signature.ALG_AES_CMAC_128, false); + hmacSignature = Signature.getInstance(Signature.ALG_HMAC_SHA_256, false); + // RNG + rndNum = JCSystem.makeTransientByteArray(MAX_RND_NUM_SIZE, JCSystem.CLEAR_ON_RESET); + entropyPool = JCSystem.makeTransientByteArray(ENTROPY_POOL_SIZE, JCSystem.CLEAR_ON_RESET); + rngCounter = JCSystem.makeTransientByteArray((short) 8, JCSystem.CLEAR_ON_RESET); + initEntropyPool(entropyPool); + try { + aesRngCipher = Cipher.getInstance(Cipher.ALG_AES_BLOCK_128_CBC_NOPAD, false); + } catch (CryptoException exp) { + ISOException.throwIt(ISO7816.SW_COMMAND_NOT_ALLOWED); + } + aesRngKey = (AESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_AES, KeyBuilder.LENGTH_AES_128, false); + // various ciphers + + } + + @Override + public KeyPair createRsaKeyPair() { + KeyPair rsaKeyPair = new KeyPair(KeyPair.ALG_RSA, KeyBuilder.LENGTH_RSA_2048); + rsaKeyPair.genKeyPair(); + return rsaKeyPair; + } + + @Override + public RSAPrivateKey createRsaKey(byte[] modBuffer, short modOff, short modLength, + byte[] privBuffer, short privOff, short privLength) { + KeyPair rsaKeyPair = new KeyPair(KeyPair.ALG_RSA, KeyBuilder.LENGTH_RSA_2048); + RSAPrivateKey privKey = (RSAPrivateKey) rsaKeyPair.getPrivate(); + privKey.setExponent(privBuffer, privOff, privLength); + privKey.setModulus(modBuffer, modOff, modLength); + return privKey; + + } + + @Override + public KeyPair createECKeyPair() { + KeyPair ecKeyPair = new KeyPair(KeyPair.ALG_EC_FP, KeyBuilder.LENGTH_EC_FP_256); + ecKeyPair.genKeyPair(); + return ecKeyPair; + } + + @Override + public ECPrivateKey createEcKey(byte[] privBuffer, short privOff, short privLength) { + KeyPair ecKeyPair = new KeyPair(KeyPair.ALG_EC_FP, KeyBuilder.LENGTH_EC_FP_256); + ECPrivateKey privKey = (ECPrivateKey) ecKeyPair.getPrivate(); + if(privLength > 24){ + privLength = 24;// simulator does not support more then 24 bytes - 192 bit key. + }else if(privLength <= 20){ + return null; + } + privKey.setS(privBuffer,privOff, privLength); + return privKey; + } + + @Override + public AESKey createAESKey(short keysize) { + byte[] rndNum = new byte[(short) (keysize/8)]; + 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); + if (keysize == 128) { + key = (AESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_AES, KeyBuilder.LENGTH_AES_128, false); + key.setKey(buf, (short) startOff); + }else if (keysize == 256){ + 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; + } + + @Override + public DESKey createTDESKey() { + // TODO check whether 168 bit or 192 bit + byte[] rndNum = new byte[24]; + newRandomNumber(rndNum, (short) 0, (short)rndNum.length); + return createTDESKey(rndNum, (short)0, (short)rndNum.length); + } + + @Override + public DESKey createTDESKey(byte[] secretBuffer, short secretOff, short secretLength) { + DESKey triDesKey = + (DESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_DES, KeyBuilder.LENGTH_DES3_3KEY, false); + triDesKey.setKey(secretBuffer, secretOff); + return triDesKey; + } + + @Override + public HMACKey createHMACKey(short keysize) { + if((keysize % 8 != 0) || !(keysize >= 64 && keysize <= 512)){ + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + } + byte[] rndNum = new byte[(short) (keysize/8)]; + newRandomNumber(rndNum, (short) 0, (short)(keysize/8)); + return createHMACKey(rndNum, (short)0, (short)rndNum.length); + } + + @Override + public HMACKey createHMACKey(byte[] secretBuffer, short secretOff, short secretLength) { + HMACKey key = null; + key = (HMACKey) KeyBuilder.buildKey(KeyBuilder.TYPE_HMAC, + KeyBuilder.LENGTH_HMAC_SHA_256_BLOCK_64, false); + key.setKey(secretBuffer,secretOff,secretLength); + return key; + } + + @Override + 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) { + //Create the sun jce compliant aes key + if(key.getSize() != 128){ + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + } + byte[] keyMaterial = new byte[16]; + key.getKey(keyMaterial,(short)0); + // print("KeyMaterial", keyMaterial); + java.security.Key aesKey = new SecretKeySpec(keyMaterial,(short)0,(short)16, "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(AES_GCM_TAG_LENGTH * 8, nonce,nonceStart,AES_GCM_NONCE_LENGTH); + try { + cipher.init(javax.crypto.Cipher.ENCRYPT_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); + // print("AAD", aad); + cipher.updateAAD(aad); + // Encrypt secret + short len = 0; + byte[] outputBuf = new byte[cipher.getOutputSize(secretLen)]; + try { + len = (short)(cipher.doFinal(secret,secretStart,secretLen,outputBuf,(short)0)); + } catch (ShortBufferException e) { + e.printStackTrace(); + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + } catch (IllegalBlockSizeException e) { + e.printStackTrace(); + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + } catch (BadPaddingException e) { + e.printStackTrace(); + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + } + // Extract Tag appended at the end. + Util.arrayCopyNonAtomic(outputBuf, (short)(len - AES_GCM_TAG_LENGTH),authTag,authTagStart,AES_GCM_TAG_LENGTH); + //Copy the encrypted data + Util.arrayCopyNonAtomic(outputBuf, (short)0,encSecret,encSecretStart,(short)(len - AES_GCM_TAG_LENGTH)); + return (short)(len - AES_GCM_TAG_LENGTH); + } + +/* + // Decrypt; nonce is shared implicitly + cipher.init(Cipher.DECRYPT_MODE, key, spec); + + // EXPECTED: Uncommenting this will cause an AEADBadTagException when decrypting + // because AAD value is altered + if (testNum == 1) aad[1]++; + + cipher.updateAAD(aad); + + // EXPECTED: Uncommenting this will cause an AEADBadTagException when decrypting + // because the encrypted data has been altered + if (testNum == 2) cipherText[10]++; + + // EXPECTED: Uncommenting this will cause an AEADBadTagException when decrypting + // because the tag has been altered + if (testNum == 3) cipherText[cipherText.length - 2]++; + + try { + byte[] plainText = cipher.doFinal(cipherText); + if (testNum != 0) { + System.out.println("Test Failed: expected AEADBadTagException not thrown"); + } else { + // check if the decryption result matches + if (Arrays.equals(input, plainText)) { + System.out.println("Test Passed: match!"); + } else { + System.out.println("Test Failed: result mismatch!"); + System.out.println(new String(plainText)); + } + } + } catch(AEADBadTagException ex) { + if (testNum == 0) { + System.out.println("Test Failed: unexpected ex " + ex); + ex.printStackTrace(); + } else { + System.out.println("Test Passed: expected ex " + ex); + } + } + } + }*/ + + public boolean aesGCMDecrypt( + AESKey key, + byte[] encSecret, + short encSecretStart, + short encSecretLen, + byte[] secret, + short secretStart, + byte[] nonce, + short nonceStart, + short nonceLen, + byte[] authData, + short authDataStart, + short authDataLen, + byte[] authTag, + short authTagStart, + short authTagLen) { + //Create the sun jce compliant aes key + if(key.getSize() != 128){ + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + } + byte[] keyMaterial = new byte[16]; + key.getKey(keyMaterial,(short)0); + java.security.Key aesKey = new SecretKeySpec(keyMaterial,(short)0,(short)16, "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(AES_GCM_TAG_LENGTH * 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 + AES_GCM_TAG_LENGTH)]; + Util.arrayCopyNonAtomic(encSecret,encSecretStart,inputBuf,(short)0,encSecretLen); + Util.arrayCopyNonAtomic(authTag,authTagStart,inputBuf,encSecretLen,AES_GCM_TAG_LENGTH); + // Decrypt + short len = 0; + 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){ + e.printStackTrace(); + return false; + }catch (ShortBufferException e) { + e.printStackTrace(); + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + } catch (IllegalBlockSizeException e) { + e.printStackTrace(); + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + } catch (BadPaddingException e) { + e.printStackTrace(); + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + } + //Copy the decrypted data + Util.arrayCopyNonAtomic(outputBuf, (short)0,secret,secretStart,len); + return true; + } + + @Override + public byte[] getTrueRandomNumber(short i) { + // ignore the size as simulator only supports 128 bit entropy + return entropyPool; + } + + @Override + public short aesCCMSign( + byte[] bufIn, + short bufInStart, + short buffInLength, + byte[] masterKeySecret, + byte[] bufOut, + short bufStart) { + if (masterKeySecret.length > 16) { + return -1; + } + + AESKey key = (AESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_AES, KeyBuilder.LENGTH_AES_128, false); + key.setKey(masterKeySecret, (short) 0); + byte[] in = new byte[buffInLength]; + Util.arrayCopyNonAtomic(bufIn, bufInStart,in,(short)0,buffInLength); + kdf.init(key, Signature.MODE_SIGN); + short len = kdf.sign(bufIn, bufInStart, buffInLength, bufOut, bufStart); + byte[] out = new byte[len]; + Util.arrayCopyNonAtomic(bufOut, bufStart,out,(short)0,len); + return len; + } + + + @Override + public HMACKey cmacKdf(byte[] keyMaterial, byte[] label, byte[] context, short contextStart, short contextLength) { + return null; + } + + @Override + public short hmacSign(HMACKey key, byte[] data, short dataStart, short dataLength, byte[] mac, short macStart) { + hmacSignature.init(key, Signature.MODE_SIGN); + return hmacSignature.sign(data, dataStart, dataLength, mac, macStart); + } + + @Override + public boolean hmacVerify(HMACKey key, byte[] data, short dataStart, short dataLength, + byte[] mac, short macStart, short macLength) { + hmacSignature.init(key, Signature.MODE_VERIFY); + return hmacSignature.verify(data, dataStart, dataLength, mac, macStart, macLength); + } + + @Override + public KMCipher createRsaDecrypt(short cipherAlg, short padding, byte[] secret, short secretStart, + short secretLength, byte[] modBuffer, short modOff, short modLength) { + Cipher rsaCipher = Cipher.getInstance((byte)cipherAlg, + (byte)padding,false); + RSAPrivateKey key = (RSAPrivateKey) KeyBuilder.buildKey(KeyBuilder.TYPE_RSA_PRIVATE, KeyBuilder.LENGTH_RSA_2048, false); + key.setExponent(secret,secretStart,secretLength); + key.setModulus(modBuffer, modOff, modLength); + rsaCipher.init(key,Cipher.MODE_DECRYPT); + return new KMCipherImpl(rsaCipher); + } + + @Override + public Signature createRsaSigner(short msgDigestAlg, short padding, byte[] secret, short secretStart, short secretLength, byte[] modBuffer, short modOff, short modLength) { + Signature rsaSigner = Signature.getInstance((byte)msgDigestAlg, Signature.SIG_CIPHER_RSA,(byte)padding,false); + RSAPrivateKey key = (RSAPrivateKey) KeyBuilder.buildKey(KeyBuilder.TYPE_RSA_PRIVATE, KeyBuilder.LENGTH_RSA_2048, false); + key.setExponent(secret,secretStart,secretLength); + key.setModulus(modBuffer, modOff, modLength); + rsaSigner.init(key,Signature.MODE_SIGN); + return rsaSigner; + } + + @Override + public Signature createEcSigner(short msgDigestAlg, byte[] secret, short secretStart, short secretLength) { + Signature ecSigner = Signature.getInstance((byte)msgDigestAlg, Signature.SIG_CIPHER_ECDSA,Cipher.PAD_NOPAD,false); + ECPrivateKey key = (ECPrivateKey) KeyBuilder.buildKey(KeyBuilder.TYPE_EC_FP_PRIVATE, KeyBuilder.LENGTH_EC_FP_256, false); + key.setS(secret,secretStart,secretLength); + ecSigner.init(key,Signature.MODE_SIGN); + return ecSigner; + } + + @Override + public KMCipher createSymmetricCipher(short cipherAlg, short padding, short mode, byte[] secret, + short secretStart, short secretLength, + byte[] ivBuffer, short ivStart, short ivLength) { + Key key = null; + short len = 0; + if(cipherAlg == Cipher.CIPHER_AES_CBC || cipherAlg == Cipher.CIPHER_AES_CBC){ + if(secretLength == 32){ + len = KeyBuilder.LENGTH_AES_256; + }else if(secretLength == 16){ + len = KeyBuilder.LENGTH_AES_128; + }else{ + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + } + + //TODO + }else if(secretLength != 21){ // DES Key + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + }else{ //DES Key + len = KeyBuilder.LENGTH_DES3_3KEY; + } + switch(cipherAlg){ + case Cipher.CIPHER_AES_CBC: + case Cipher.CIPHER_AES_ECB: + key = KeyBuilder.buildKey(KeyBuilder.TYPE_AES,len,false); + ((AESKey) key).setKey(secret,secretStart); + break; + case Cipher.CIPHER_DES_CBC: + case Cipher.CIPHER_DES_ECB: + key = (DESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_DES,len,false); + ((DESKey) key).setKey(secret,secretStart); + break; + default://This should never happen + CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); + break; + } + + //TODO + Cipher symmCipher = Cipher.getInstance((byte)cipherAlg, Cipher.PAD_NOPAD,false); + if (ivBuffer != null) { + symmCipher.init(key, (byte) mode, ivBuffer, ivStart, ivLength); + }else{ + symmCipher.init(key, (byte) mode); + } + return new KMCipherImpl(symmCipher); + } + + @Override + public Signature createHmacSigner(short msgDigestAlg, byte[] secret, short secretStart, short secretLength) { + Signature hmacSigner = Signature.getInstance((byte)msgDigestAlg, Signature.SIG_CIPHER_HMAC,Cipher.PAD_NOPAD,false); + HMACKey key = (HMACKey) KeyBuilder.buildKey(KeyBuilder.TYPE_HMAC, (short)(secretLength*8), false); + key.setKey(secret,secretStart,secretLength); + hmacSigner.init(key,Signature.MODE_SIGN); + return hmacSigner; + } + + @Override + public KMCipher createGCMCipher(short mode, byte[] secret, short secretStart, short secretLength, byte[] ivBuffer, short ivStart, short ivLength) { + //TODO + short len = KeyBuilder.LENGTH_AES_128; + if(secretLength == 32){ + len = KeyBuilder.LENGTH_AES_256; + } + return new KMCipherImpl(null); + } + + @Override + public void delete(KMCipher cipher) { + //Don't do anything as we don't pool the objects. + } + + @Override + public void delete(Signature signature) { + //Don't do anything as we don't pool the objects. + } + + @Override + public void delete(Key key) { + // Don't do anything as we don't pool the objects. + } + + @Override + public void delete(KeyPair keyPair) { + // Don't do anything as we don't pool the objects. + } + + private void initEntropyPool(byte[] pool) { + byte index = 0; + RandomData trng; + while (index < rngCounter.length) { + rngCounter[index++] = 0; + } + try { + trng = RandomData.getInstance(RandomData.ALG_TRNG); + trng.nextBytes(pool, (short) 0, (short) pool.length); + } catch (CryptoException exp) { + if (exp.getReason() == CryptoException.NO_SUCH_ALGORITHM) { + // TODO change this when possible + // simulator does not support TRNG algorithm. So, PRNG algorithm (deprecated) is used. + trng = RandomData.getInstance(RandomData.ALG_PSEUDO_RANDOM); + trng.nextBytes(pool, (short) 0, (short) pool.length); + } else { + // TODO change this to proper error code + ISOException.throwIt(ISO7816.SW_UNKNOWN); + } + } + } + + // Generate a secure random number from existing entropy pool. This uses aes ecb algorithm with + // 8 byte rngCounter and 16 byte block size. + @Override + public void newRandomNumber(byte[] num, short startOff, short length) { + KMRepository repository = KMRepository.instance(); + byte[] bufPtr = repository.getHeap(); + short countBufInd = repository.alloc(KMKeymasterApplet.AES_BLOCK_SIZE); + short randBufInd = repository.alloc(KMKeymasterApplet.AES_BLOCK_SIZE); + short len = KMKeymasterApplet.AES_BLOCK_SIZE; + aesRngKey.setKey(entropyPool, (short) 0); + aesRngCipher.init(aesRngKey, Cipher.MODE_ENCRYPT, aesICV, (short) 0, (short) 16); + while (length > 0) { + if (length < len) len = length; + // increment rngCounter by one + incrementCounter(); + // copy the 8 byte rngCounter into the 16 byte rngCounter buffer. + Util.arrayCopy(rngCounter, (short) 0, bufPtr, countBufInd, (short) rngCounter.length); + // encrypt the rngCounter buffer with existing entropy which forms the aes key. + aesRngCipher.doFinal( + bufPtr, countBufInd, KMKeymasterApplet.AES_BLOCK_SIZE, bufPtr, randBufInd); + // copy the encrypted rngCounter block to buffer passed in the argument + Util.arrayCopy(bufPtr, randBufInd, num, startOff, len); + length = (short) (length - len); + startOff = (short) (startOff + len); + } + } + + // increment 8 byte rngCounter by one + private void incrementCounter() { + // start with least significant byte + short index = (short) (rngCounter.length - 1); + while (index >= 0) { + // if the msb of current byte is set then it will be negative + if (rngCounter[index] < 0) { + // then increment the rngCounter + rngCounter[index]++; + // is the msb still set? i.e. no carry over + if (rngCounter[index] < 0) break; // then break + else index--; // else go to the higher order byte + } else { + // if msb is not set then increment the rngCounter + rngCounter[index]++; + break; + } + } + } + + @Override + public void addRngEntropy(byte[] num, short offset, short length) { + // Maximum length can be 256 bytes. But currently we support max 32 bytes seed. + // Get existing entropy pool. + if (length > 32) length = 32; + // Create new temporary pool. + // Populate the new pool with the entropy which is derived from current entropy pool. + newRandomNumber(rndNum, (short) 0, (short) entropyPool.length); + // Copy the entropy to the current pool - updates the entropy pool. + Util.arrayCopy(rndNum, (short) 0, entropyPool, (short) 0, (short) entropyPool.length); + short index = 0; + short randIndex = 0; + // XOR the seed received from the master in the entropy pool - 16 bytes (entPool.length). + // at a time. + while (index < length) { + entropyPool[randIndex] = (byte) (entropyPool[randIndex] ^ num[(short) (offset + index)]); + randIndex++; + index++; + if (randIndex >= entropyPool.length) { + randIndex = 0; + } + } + } + private void print (String lab, byte[] b, short s, short l){ + byte[] i = new byte[l]; + Util.arrayCopyNonAtomic(b,s,i,(short)0,l); + print(lab,i); + } + private void print(String label, byte[] buf){ + System.out.println(label+": "); + StringBuilder sb = new StringBuilder(); + for(int i = 0; i < buf.length; i++){ + sb.append(String.format(" 0x%02X", buf[i])) ; + if(((i-1)%38 == 0) && ((i-1) >0)){ + sb.append(";\n"); + } + } + System.out.println(sb.toString()); + } + @Override + public void bypassAesGcm(){ + //ignore + } +} diff --git a/Applet/Applet/OracleSimProvider/com/android/javacard/keymaster/KMCipherImpl.java b/Applet/Applet/OracleSimProvider/com/android/javacard/keymaster/KMCipherImpl.java new file mode 100644 index 00000000..66d988d4 --- /dev/null +++ b/Applet/Applet/OracleSimProvider/com/android/javacard/keymaster/KMCipherImpl.java @@ -0,0 +1,31 @@ +package com.android.javacard.keymaster; + +import com.android.javacard.keymaster.KMCipher; +import javacardx.crypto.Cipher; + +public class KMCipherImpl extends KMCipher{ + Cipher cipher; + KMCipherImpl(Cipher c){ + cipher = c; + } + + @Override + public short doFinal(byte[] buffer, short startOff, short length, byte[] scratchPad, short i) { + return cipher.doFinal(buffer, startOff, length, scratchPad, i); + } + + @Override + public short getCipherAlgorithm() { + return cipher.getCipherAlgorithm(); + } + + @Override + public short update(byte[] buffer, short startOff, short length, byte[] scratchPad, short i) { + return cipher.update(buffer,startOff,length,scratchPad,i); + } + + @Override + public short getPaddingAlgorithm() { + return cipher.getPaddingAlgorithm(); + } +} diff --git a/Applet/Applet/OracleSimProvider/com/android/javacard/keymaster/KMCryptoProviderImpl.java b/Applet/Applet/OracleSimProvider/com/android/javacard/keymaster/KMCryptoProviderImpl.java new file mode 100644 index 00000000..182dda4e --- /dev/null +++ b/Applet/Applet/OracleSimProvider/com/android/javacard/keymaster/KMCryptoProviderImpl.java @@ -0,0 +1,8 @@ + +package com.android.javacard.keymaster; + +public class KMCryptoProviderImpl { + public static KMCryptoProvider instance(){ + return new KMSimulator(); + } +} diff --git a/Applet/Applet/OracleSimProvider/com/android/javacard/keymaster/KMSimulator.java b/Applet/Applet/OracleSimProvider/com/android/javacard/keymaster/KMSimulator.java new file mode 100644 index 00000000..51c87142 --- /dev/null +++ b/Applet/Applet/OracleSimProvider/com/android/javacard/keymaster/KMSimulator.java @@ -0,0 +1,505 @@ +/* + * 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; + +import javacard.framework.ISO7816; +import javacard.framework.ISOException; +import javacard.framework.JCSystem; +import javacard.framework.Util; +import javacard.security.AESKey; +import javacard.security.CryptoException; +import javacard.security.DESKey; +import javacard.security.ECPrivateKey; +import javacard.security.ECPublicKey; +import javacard.security.HMACKey; +import javacard.security.Key; +import javacard.security.KeyBuilder; +import javacard.security.KeyPair; +import javacard.security.RSAPrivateKey; +import javacard.security.RandomData; +import javacard.security.Signature; +import javacardx.crypto.AEADCipher; +import javacardx.crypto.Cipher; + +/** + * Simulator only supports 512 bit RSA key pair, 128 AES Key, 128 bit 3Des key, less then 256 bit EC + * Key, and upto 512 bit HMAC key. Also simulator does not support TRNG, so this implementation just + * creates its own RNG using PRNG. + */ +public class KMSimulator implements KMCryptoProvider { + public static final short AES_GCM_TAG_LENGTH = 12; + public static final short AES_GCM_NONCE_LENGTH = 12; + public static final short MAX_RND_NUM_SIZE = 64; + 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}; + public static boolean jcardSim = false; + + private static KeyPair rsa512KeyPair; + private static KeyPair ec192KeyPair; + private static AESKey aes128Key; + private static DESKey triDesKey; + private static HMACKey hmac128Key; + private static HMACKey hmac256Key; + private static AEADCipher aesGcmCipher; + private static AESKey derivedKey; + private static Signature kdf; + + private static byte[] rngCounter; + private static AESKey aesRngKey; + private static Cipher aesRngCipher; + private static byte[] entropyPool; + private static byte[] rndNum; + + // Implements Oracle Simulator based restricted crypto provider + public KMSimulator() { + // Various Keys + rsa512KeyPair = new KeyPair(KeyPair.ALG_RSA, KeyBuilder.LENGTH_RSA_512); + ec192KeyPair = new KeyPair(KeyPair.ALG_EC_FP, KeyBuilder.LENGTH_EC_FP_192); + aes128Key = (AESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_AES, KeyBuilder.LENGTH_AES_128, false); + triDesKey = + (DESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_DES, KeyBuilder.LENGTH_DES3_2KEY, false); + hmac128Key = (HMACKey) KeyBuilder.buildKey(KeyBuilder.TYPE_HMAC, (short) 128, false); + hmac256Key = (HMACKey) KeyBuilder.buildKey(KeyBuilder.TYPE_HMAC, (short) 256, false); + derivedKey = + (AESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_AES, KeyBuilder.LENGTH_AES_128, false); + kdf = Signature.getInstance(Signature.ALG_AES_CMAC_128, false); + + // RNG + rndNum = JCSystem.makeTransientByteArray(MAX_RND_NUM_SIZE, JCSystem.CLEAR_ON_RESET); + entropyPool = JCSystem.makeTransientByteArray(ENTROPY_POOL_SIZE, JCSystem.CLEAR_ON_RESET); + rngCounter = JCSystem.makeTransientByteArray((short) 8, JCSystem.CLEAR_ON_RESET); + initEntropyPool(entropyPool); + try { + aesRngCipher = Cipher.getInstance(Cipher.ALG_AES_BLOCK_128_CBC_NOPAD, false); + } catch (CryptoException exp) { + ISOException.throwIt(ISO7816.SW_COMMAND_NOT_ALLOWED); + } + aesRngKey = (AESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_AES, KeyBuilder.LENGTH_AES_128, false); + } + + @Override + public KeyPair createRsaKeyPair() { + // By default 65537 is used as public exponent no need to set the public exponent. Now generate + // 512 bit RSA keypair for the given exponent + rsa512KeyPair.genKeyPair(); + return rsa512KeyPair; + } + + @Override + public KeyPair createECKeyPair() { + // Simulator does not support 256 bit keys. + // Generate default 192 bit key pair supported by simulator. + ec192KeyPair.genKeyPair(); + return ec192KeyPair; + } + + @Override + public AESKey createAESKey(short keysize) { + // keysize is ignored as simulator only supports 128 bit aes key + newRandomNumber(rndNum, (short) 0, (short) 16); + aes128Key.setKey(rndNum, (short) 0); + return aes128Key; + } + + @Override + public AESKey createAESKey(byte[] buf, short startOff, short length) { + if (length > 16) length = 16; + else if(length < 16) return null; + aes128Key.setKey(buf, startOff); + return aes128Key; + } + + @Override + public DESKey createTDESKey() { + // only 128 bit keys are supported + newRandomNumber(rndNum, (short) 0, (short) 21); + triDesKey.setKey(rndNum, (short) 0); + return triDesKey; + } + + @Override + public HMACKey createHMACKey(short keysize) { + // simulator only supports HMAC keys for SHA1 and SHA256 with block size 64. + // So only 128 and 256 bit HMAC keys are supported. + HMACKey key; + if (keysize == 128) { + key = hmac128Key; + keysize = 16; + } else if (keysize == 256) { + key = hmac256Key; + keysize = 32; + } else { + key = hmac128Key; // by default the simulator will return 128 bit keys for SHA1 + keysize = 16; + } + newRandomNumber(rndNum, (short) 0, keysize); + key.setKey(rndNum, (short) 0, keysize); + return key; + } + + @Override + public void addRngEntropy(byte[] num, short offset, short length) { + // Maximum length can be 256 bytes. But currently we support max 32 bytes seed. + // Get existing entropy pool. + if (length > 32) length = 32; + // Create new temporary pool. + // Populate the new pool with the entropy which is derived from current entropy pool. + newRandomNumber(rndNum, (short) 0, (short) entropyPool.length); + // Copy the entropy to the current pool - updates the entropy pool. + Util.arrayCopy(rndNum, (short) 0, entropyPool, (short) 0, (short) entropyPool.length); + short index = 0; + short randIndex = 0; + // XOR the seed received from the master in the entropy pool - 16 bytes (entPool.length). + // at a time. + while (index < length) { + entropyPool[randIndex] = (byte) (entropyPool[randIndex] ^ num[(short) (offset + index)]); + randIndex++; + index++; + if (randIndex >= entropyPool.length) { + randIndex = 0; + } + } + } + + @Override + 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 (jcardSim) return -1; + if(authTagLen != AES_GCM_TAG_LENGTH){ + KMException.throwIt(KMError.UNKNOWN_ERROR); + } + if(nonceLen != AES_GCM_NONCE_LENGTH){ + KMException.throwIt(KMError.UNKNOWN_ERROR); + } + if (aesGcmCipher == null) { + aesGcmCipher = (AEADCipher) Cipher.getInstance(AEADCipher.ALG_AES_GCM, false); + } + byte[] aad = JCSystem.makeTransientByteArray(authDataLen, JCSystem.CLEAR_ON_RESET); + Util.arrayCopyNonAtomic(authData, authDataStart, aad, (short) 0, authDataLen); + try { + aesGcmCipher.init(key, Cipher.MODE_ENCRYPT, nonce, nonceStart, nonceLen); + } catch (CryptoException exp) { + KMException.throwIt(exp.getReason()); + } + // add the auth data + try { + aesGcmCipher.updateAAD(aad, (short) 0, authDataLen); + } catch (CryptoException exp) { + KMException.throwIt(exp.getReason()); + } + // encrypt the secret + short ciphLen = aesGcmCipher.doFinal(secret, secretStart, secretLen, encSecret, encSecretStart); + // The tag buffer must be exact size otherwise simulator returns 0 tag. + byte[] tag = JCSystem.makeTransientByteArray(AES_GCM_TAG_LENGTH, JCSystem.CLEAR_ON_RESET); + aesGcmCipher.retrieveTag(tag, (short) 0, AES_GCM_TAG_LENGTH); + Util.arrayCopyNonAtomic(tag,(short)0, authTag, authTagStart,AES_GCM_TAG_LENGTH); + return ciphLen; +/* aesGcmCipher.init(key, Cipher.MODE_DECRYPT, nonce, nonceStart, nonceLen); + try { + aesGcmCipher.updateAAD(aad, (short) 0, authDataLen); + } catch (CryptoException exp) { + KMException.throwIt(exp.getReason()); + } + byte[] plain = JCSystem.makeTransientByteArray(secretLen, JCSystem.CLEAR_ON_RESET); + // encrypt the secret + ciphLen = aesGcmCipher.doFinal(encSecret, encSecretStart, ciphLen, plain, (short) 0); + boolean ver = aesGcmCipher.verifyTag(tag, (short) 0, (short) 12, (short) 12); + if (ver == true) { + KMException.throwIt((short) 10); + } else { + KMException.throwIt((short) 20); + } + return 0; + */ + } + + public boolean aesGCMDecrypt( + AESKey key, + byte[] encSecret, + short encSecretStart, + short encSecretLen, + byte[] secret, + short secretStart, + byte[] nonce, + short nonceStart, + short nonceLen, + byte[] authData, + short authDataStart, + short authDataLen, + byte[] authTag, + short authTagStart, + short authTagLen) { + if(jcardSim) return true; + if (aesGcmCipher == null) { + aesGcmCipher = (AEADCipher) Cipher.getInstance(AEADCipher.ALG_AES_GCM, false); + } + // allocate aad buffer of exact size - otherwise simulator throws exception + byte[] aad = JCSystem.makeTransientByteArray(authDataLen, JCSystem.CLEAR_ON_RESET); + Util.arrayCopyNonAtomic(authData, authDataStart, aad, (short) 0, authDataLen); + // allocate tag of exact size. + byte[] tag = JCSystem.makeTransientByteArray(AES_GCM_TAG_LENGTH, JCSystem.CLEAR_ON_RESET); + Util.arrayCopyNonAtomic(authTag, authTagStart, tag, (short) 0, authTagLen); + boolean verification = false; + try { + aesGcmCipher.init(key, Cipher.MODE_DECRYPT, nonce, nonceStart, nonceLen); + aesGcmCipher.updateAAD(aad, (short) 0, authDataLen); + //byte[] plain = JCSystem.makeTransientByteArray(encSecretLen, JCSystem.CLEAR_ON_RESET); + // encrypt the secret + aesGcmCipher.doFinal(encSecret, encSecretStart, encSecretLen, secret, secretStart); + verification = aesGcmCipher.verifyTag(tag, (short) 0, (short) 12, (short) 12); + } catch (CryptoException exp) { + KMException.throwIt(KMError.UNKNOWN_ERROR); + } + return verification; + } + + @Override + public byte[] getTrueRandomNumber(short i) { + // ignore the size as simulator only supports 128 bit entropy + return entropyPool; + } + + @Override + public short aesCCMSign( + byte[] bufIn, + short bufInStart, + short buffInLength, + byte[] masterKeySecret, + byte[] bufOut, + short bufStart) { + if (masterKeySecret.length > 16) { + + return -1; + } + aes128Key.setKey(masterKeySecret, (short) 0); + kdf.init(aes128Key, Signature.MODE_SIGN); + return kdf.sign(bufIn, bufInStart, buffInLength, bufOut, bufStart); + } + + @Override + public ECPrivateKey createEcKey(byte[] privBuffer, short privOff, short privLength) { + return null; + } + + public ECPrivateKey createEcPrivateKey(byte[] pubBuffer, short pubOff, short pubLength, + byte[] privBuffer, short privOff, short privLength) { + // Simulator does not support NamedParameterSpec or 256 bit keys + ECPrivateKey privKey = (ECPrivateKey) ec192KeyPair.getPrivate(); + if(privLength > 24){ + privLength = 24;// simulator does not support more then 24 bytes - 192 bit key. + }else if(privLength <= 20){ + return null; + } + privKey.setS(privBuffer,privOff, privLength); + return privKey; + } + + @Override + public HMACKey createHMACKey(byte[] secretBuffer, short secretOff, short secretLength) { + // simulator only supports HMAC keys for SHA1 and SHA256 with block size 64. + // So only 128 and 256 bit HMAC keys are supported. + HMACKey key; + if (secretLength == 16) { + key = hmac128Key; + key.setKey(secretBuffer, secretOff, secretLength); + } else if (secretLength == 32) { + key = hmac256Key; + key.setKey(secretBuffer, secretOff, secretLength); + } else { + key = hmac128Key; // by default the simulator will return 128 bit keys for SHA1 + key.setKey(secretBuffer, secretOff, (short)16); + } + return key; + } + @Override + public DESKey createTDESKey(byte[] secretBuffer, short secretOff, short secretLength) { + // only 128 bit keys are supported + if(secretLength < 128) return null; + triDesKey.setKey(secretBuffer, secretOff); + return triDesKey; + } + + @Override + public RSAPrivateKey createRsaKey(byte[] modBuffer, short modOff, short modLength, byte[] privBuffer, short privOff, short privLength) { + return null; + } + + @Override + public HMACKey cmacKdf(byte[] keyMaterial, byte[] label, byte[] context, short contextStart, short contextLength) { + return null; + } + + @Override + public short hmacSign(HMACKey key, byte[] data, short dataStart, short dataLength, byte[] mac, short macStart) { + return 0; + } + + @Override + public boolean hmacVerify(HMACKey key, byte[] data, short dataStart, short dataLength, byte[] mac, short macStart, short macLength) { + return false; + } + + + @Override + public KMCipher createRsaDecrypt(short cipherAlg, short padding, byte[] secret, short secretStart, short secretLength, byte[] modBuffer, short modOff, short modLength) { + return null; + } + + @Override + public Signature createRsaSigner(short msgDigestAlg, short padding, byte[] secret, short secretStart, short secretLength, byte[] modBuffer, short modOff, short modLength) { + return null; + } + + @Override + public Signature createEcSigner(short msgDigestAlg, byte[] secret, short secretStart, short secretLength) { + return null; + } + + @Override + public KMCipher createSymmetricCipher(short cipherAlg, short padding, short mode, byte[] secret, short secretStart, short secretLength, byte[] ivBuffer, short ivStart, short ivLength) { + return null; + } + + @Override + public Signature createHmacSigner(short msgDigestAlg, byte[] secret, short secretStart, short secretLength) { + return null; + } + + @Override + public KMCipher createGCMCipher(short mode, byte[] secret, short secretStart, short secretLength, byte[] ivBuffer, short ivStart, short ivLength) { + return null; + } + + @Override + public void delete(KMCipher cipher) { + + } + + + @Override + public void delete(Signature signature) { + + } + + @Override + public void delete(Key key) { + + } + + @Override + public void delete(KeyPair keyPair) { + + } + + public RSAPrivateKey createRsaPrivateKey(byte[] modBuffer, short modOff, short modLength, byte[] privBuffer, short privOff, short privLength) { + RSAPrivateKey privKey = (RSAPrivateKey) rsa512KeyPair.getPrivate(); + if(privLength > 64) privLength = 64; + else if(privLength < 64)return null; + if(modLength > 64) modLength = 64; + else if( modLength < 64) return null; + privKey.setExponent(privBuffer, privOff, privLength); + privKey.setModulus(modBuffer, modOff, modLength); + return privKey; + } + + private void initEntropyPool(byte[] pool) { + byte index = 0; + RandomData trng; + while (index < rngCounter.length) { + rngCounter[index++] = 0; + } + try { + trng = RandomData.getInstance(RandomData.ALG_TRNG); + trng.nextBytes(pool, (short) 0, (short) pool.length); + } catch (CryptoException exp) { + if (exp.getReason() == CryptoException.NO_SUCH_ALGORITHM) { + // TODO change this when possible + // simulator does not support TRNG algorithm. So, PRNG algorithm (deprecated) is used. + trng = RandomData.getInstance(RandomData.ALG_PSEUDO_RANDOM); + trng.nextBytes(pool, (short) 0, (short) pool.length); + } else { + // TODO change this to proper error code + ISOException.throwIt(ISO7816.SW_UNKNOWN); + } + } + } + + // Generate a secure random number from existing entropy pool. This uses aes ecb algorithm with + // 8 byte rngCounter and 16 byte block size. + @Override + public void newRandomNumber(byte[] num, short startOff, short length) { + KMRepository repository = KMRepository.instance(); + byte[] bufPtr = repository.getHeap(); + short countBufInd = repository.alloc(KMKeymasterApplet.AES_BLOCK_SIZE); + short randBufInd = repository.alloc(KMKeymasterApplet.AES_BLOCK_SIZE); + short len = KMKeymasterApplet.AES_BLOCK_SIZE; + aesRngKey.setKey(entropyPool, (short) 0); + aesRngCipher.init(aesRngKey, Cipher.MODE_ENCRYPT, aesICV, (short) 0, (short) 16); + while (length > 0) { + if (length < len) len = length; + // increment rngCounter by one + incrementCounter(); + // copy the 8 byte rngCounter into the 16 byte rngCounter buffer. + Util.arrayCopy(rngCounter, (short) 0, bufPtr, countBufInd, (short) rngCounter.length); + // encrypt the rngCounter buffer with existing entropy which forms the aes key. + aesRngCipher.doFinal( + bufPtr, countBufInd, KMKeymasterApplet.AES_BLOCK_SIZE, bufPtr, randBufInd); + // copy the encrypted rngCounter block to buffer passed in the argument + Util.arrayCopy(bufPtr, randBufInd, num, startOff, len); + length = (short) (length - len); + startOff = (short) (startOff + len); + } + } + + // increment 8 byte rngCounter by one + private void incrementCounter() { + // start with least significant byte + short index = (short) (rngCounter.length - 1); + while (index >= 0) { + // if the msb of current byte is set then it will be negative + if (rngCounter[index] < 0) { + // then increment the rngCounter + rngCounter[index]++; + // is the msb still set? i.e. no carry over + if (rngCounter[index] < 0) break; // then break + else index--; // else go to the higher order byte + } else { + // if msb is not set then increment the rngCounter + rngCounter[index]++; + break; + } + } + } + @Override + public void bypassAesGcm(){ + jcardSim = true; + } +} diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMAbortOperationCmd.java b/Applet/Applet/src/com/android/javacard/keymaster/KMAbortOperationCmd.java deleted file mode 100644 index ee3e052a..00000000 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMAbortOperationCmd.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * 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; - -public class KMAbortOperationCmd extends KMAbstractCmd { - public static final byte INS_ABORT_OPERATION_CMD = 0x22; - - @Override - protected KMArray getExpectedArgs() { - return null; - } - - @Override - protected KMArray process(KMArray args, KMContext context) { - return null; - } - - @Override - public byte getIns() { - return INS_ABORT_OPERATION_CMD; - } -} diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMAbstractCmd.java b/Applet/Applet/src/com/android/javacard/keymaster/KMAbstractCmd.java deleted file mode 100644 index 02fad435..00000000 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMAbstractCmd.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * 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; - -public abstract class KMAbstractCmd implements KMCommand { - - /** - * Implements the KMCommand interface. - * - * @param context provides information required to execute the command. - */ - @Override - public void execute(KMContext context) { - // Assert the command's operational state - if (!this.validateState(context.getKeymasterState())) { - throw new KMException(KMException.CMD_NOT_ACCEPTED_WRONG_STATE); - } - KMEncoder encoder = context.getRepository().getEncoder(); - KMDecoder decoder = context.getRepository().getDecoder(); - // Get getExpectedArgs if expected - KMArray args = null; - if (hasArguments()) { - // Deserialize the getExpectedArgs - KMArray argsProto = getExpectedArgs(); - args = decoder.decode(argsProto, context.getBuffer(), (short) 0, context.getBufferLength()); - } - // Pass control to concrete command subclass - KMArray resp = this.process(args, context); - context.setBufferLength((short)0); - // If there is resp then serialize and send - if (resp != null) { - // set outgoing buffer - short len = encoder.encode(resp, context.getBuffer(), (short) 0, (short)context.getBuffer().length); - context.setBufferLength(len); - } - } - - /** - * Get the getExpectedArgs prototype expression from the concrete subclass. - * - * @return KMArray of KMType objects which provides expression for the command's getExpectedArgs.. - */ - protected abstract KMArray getExpectedArgs(); - - /** - * Implemented by the subclass to execute the command specific functionality. - * - * @param args which are decoded from the the apdu. - * @param context within which the command should be executed. - * @return Null or response having the result of the command's execution. - */ - protected abstract KMArray process(KMArray args, KMContext context); - - /** - * Validate the state required by the command to execute. By default all the commands can execute - * in active state. - * - * @param state is the current state of the applet - * @return true if the state is valid for command's execution else false is returned. - */ - protected boolean validateState(byte state) { - return (KMKeymasterApplet.ACTIVE_STATE == state); - } - - @Override - public boolean hasArguments(){ - return true; - } -} diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMAddRngEntropyCmd.java b/Applet/Applet/src/com/android/javacard/keymaster/KMAddRngEntropyCmd.java deleted file mode 100644 index cb431b8f..00000000 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMAddRngEntropyCmd.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * 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; - -import javacard.framework.ISO7816; -import javacard.framework.JCSystem; -import javacard.framework.Util; - -// This command adds entropy into the current entropy pool as per hal specifications. -public class KMAddRngEntropyCmd extends KMAbstractCmd { - public static final byte INS_ADD_RNG_ENTROPY_CMD = 0x18; - public static final short MAX_SEED_SIZE = 2048; - - @Override - protected KMArray getExpectedArgs() { - return KMArray.instance((short) 1).add((short) 0, KMByteBlob.instance()); - } - - @Override - protected KMArray process(KMArray args, KMContext context) { - KMByteBlob blob = (KMByteBlob) args.get((short) 0); - // maximum 2KiB of seed is allowed. - if (blob.length() > MAX_SEED_SIZE) { - throw new KMException(ISO7816.SW_WRONG_LENGTH); - } - KMUtil.init(context.getRepository()); - // Get existing entropy pool. - byte[] entPool = context.getRepository().getEntropyPool(); - // Create new temporary pool. - byte[] heapRef = context.getRepository().getByteHeapRef(); - short poolStart = context.getRepository().newByteArray((short) entPool.length); - // Populate the new pool with the entropy which is derived from current entropy pool. - KMUtil.newRandomNumber(heapRef, poolStart, (short) entPool.length); - // Copy the entropy to the current pool - updates the entropy pool. - Util.arrayCopy(heapRef, poolStart, entPool, (short) 0, (short) entPool.length); - short index = 0; - short randIndex = 0; - // Mix (XOR) the seed received from the master in the entropy pool - 32 bytes (entPool.length). - // at a time. - while (index < blob.length()) { - entPool[randIndex] = (byte) (entPool[randIndex] ^ blob.get(index)); - randIndex++; - index++; - if (randIndex >= entPool.length) { - randIndex = 0; - } - } - // TODO return success error code. - return null; - } - - @Override - public byte getIns() { - return INS_ADD_RNG_ENTROPY_CMD; - } -} diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMArray.java b/Applet/Applet/src/com/android/javacard/keymaster/KMArray.java index 23b5afe6..4646f46e 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMArray.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMArray.java @@ -17,66 +17,79 @@ package com.android.javacard.keymaster; import javacard.framework.ISO7816; +import javacard.framework.ISOException; +import javacard.framework.Util; public class KMArray extends KMType { - private KMType[] vals; - private short length; - private short startOff; + public static final short ANY_ARRAY_LENGTH = 0x1000; + // short Type + short Length + private static final short ARRAY_HEADER_SIZE = 4; + private static KMArray prototype; + private static short instPtr; - private KMArray() { - init(); + private KMArray() {} + + private static KMArray proto(short ptr) { + if (prototype == null) prototype = new KMArray(); + instPtr = ptr; + return prototype; } - @Override - public void init() { - vals = null; - startOff = 0; - length = 0; + public static short exp() { + short ptr = instance(ARRAY_TYPE, ARRAY_HEADER_SIZE); + Util.setShort(heap,(short)(ptr + TLV_HEADER_SIZE),(short)0 ); + Util.setShort(heap,(short)(ptr + TLV_HEADER_SIZE + 2),ANY_ARRAY_LENGTH ); + return ptr; } - @Override - public short length() { - return length; + public static short exp(short type) { + short ptr = instance(ARRAY_TYPE, ARRAY_HEADER_SIZE); + Util.setShort(heap,(short)(ptr + TLV_HEADER_SIZE),type); + Util.setShort(heap,(short)(ptr + TLV_HEADER_SIZE + 2),ANY_ARRAY_LENGTH ); + return ptr; } - public static void create(KMArray[] arrayRefTable) { - byte index = 0; - while (index < arrayRefTable.length) { - arrayRefTable[index] = new KMArray(); - index++; - } + public static short instance(short length) { + short ptr = KMType.instance(ARRAY_TYPE, (short)(ARRAY_HEADER_SIZE + (length*2))); + Util.setShort(heap,(short)(ptr + TLV_HEADER_SIZE),(short)0); + Util.setShort(heap,(short)(ptr + TLV_HEADER_SIZE + 2),length); + return ptr; } - public static KMArray instance() { - return repository.newArray(); + public static short instance(short length, byte type) { + short ptr = instance(length); + Util.setShort(heap,(short)(ptr + TLV_HEADER_SIZE),type); + return ptr; } - public static KMArray instance(short length) { + public static KMArray cast(short ptr) { + if (heap[ptr] != ARRAY_TYPE) ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + return proto(ptr); + } - KMArray inst = repository.newArray(); - inst.startOff = repository.newTypeArray(length); - inst.vals = repository.getTypeArrayRef(); - inst.length = length; - return inst; + public void add(short index, short objPtr) { + short len = length(); + if (index >= len) ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); + Util.setShort(heap, (short) (instPtr + TLV_HEADER_SIZE + ARRAY_HEADER_SIZE + (short)(index*2)), objPtr) ; } - public KMArray withLength(short length) { - this.length = length; - return this; + public short get(short index) { + short len = length(); + if (index >= len) ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); + return Util.getShort(heap,(short) (instPtr + TLV_HEADER_SIZE + ARRAY_HEADER_SIZE + (short)(index*2))); } - public KMArray add(short index, KMType val) { - if (index >= length) { - throw new KMException(ISO7816.SW_WRONG_LENGTH); - } - vals[(short) (startOff + index)] = val; - return this; + public short containedType(){ return Util.getShort(heap, (short)(instPtr + TLV_HEADER_SIZE));} + + public short getStartOff() { + return (short) (instPtr + TLV_HEADER_SIZE + ARRAY_HEADER_SIZE); + } + + public short length() { + return Util.getShort(heap, (short) (instPtr + TLV_HEADER_SIZE + 2)); } - public KMType get(short index) { - if (index >= length) { - throw new KMException(ISO7816.SW_WRONG_LENGTH); - } - return vals[(short) (startOff + index)]; + public byte[] getBuffer() { + return heap; } } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMAttestKeyCmd.java b/Applet/Applet/src/com/android/javacard/keymaster/KMAttestKeyCmd.java deleted file mode 100644 index 9d51e231..00000000 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMAttestKeyCmd.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * 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; - -public class KMAttestKeyCmd extends KMAbstractCmd { - public static final byte INS_ATTEST_KEY_CMD = 0x14; - - @Override - protected KMArray getExpectedArgs() { - return null; - } - - @Override - protected KMArray process(KMArray args, KMContext context) { - return null; - } - - @Override - public byte getIns() { - return INS_ATTEST_KEY_CMD; - } -} diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMAuthTag.java b/Applet/Applet/src/com/android/javacard/keymaster/KMAuthTag.java new file mode 100644 index 00000000..23d4f801 --- /dev/null +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMAuthTag.java @@ -0,0 +1,7 @@ +package com.android.javacard.keymaster; + +public class KMAuthTag { + public boolean reserved; + public byte[] authTag; + public short usageCount; +} diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMBeginOperationCmd.java b/Applet/Applet/src/com/android/javacard/keymaster/KMBeginOperationCmd.java deleted file mode 100644 index 2ab55233..00000000 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMBeginOperationCmd.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * 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; - -public class KMBeginOperationCmd extends KMAbstractCmd { - public static final byte INS_BEGIN_OPERATION_CMD = 0x1F; - - @Override - protected KMArray getExpectedArgs() { - return null; - } - - @Override - protected KMArray process(KMArray args, KMContext context) { - return null; - } - - @Override - public byte getIns() { - return INS_BEGIN_OPERATION_CMD; - } -} diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMBoolTag.java b/Applet/Applet/src/com/android/javacard/keymaster/KMBoolTag.java index 6cf41894..ae0f75ac 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMBoolTag.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMBoolTag.java @@ -17,8 +17,12 @@ package com.android.javacard.keymaster; import javacard.framework.ISO7816; +import javacard.framework.ISOException; +import javacard.framework.Util; public class KMBoolTag extends KMTag { + private static KMBoolTag prototype; + private static short instPtr; private static final short[] tags = { CALLER_NONCE, @@ -33,62 +37,53 @@ public class KMBoolTag extends KMTag { RESET_SINCE_ID_ROTATION }; - // Array of Tag Values. - private short key; - private byte val; + private KMBoolTag() {} - // assignBlob constructor - private KMBoolTag() { - init(); + private static KMBoolTag proto(short ptr) { + if (prototype == null) prototype = new KMBoolTag(); + instPtr = ptr; + return prototype; } - @Override - public void init() { - key = 0; - val = 1; // always 1. + // pointer to an empty instance used as expression + public static short exp() { + short ptr = instance(TAG_TYPE, (short)2); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE), BOOL_TAG); + return ptr; } - public static KMBoolTag instance() { - return repository.newBoolTag(); + public static short instance(short key) { + if (!validateKey(key)) { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + short ptr = KMType.instance(TAG_TYPE, (short)5); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE), BOOL_TAG); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE+2), key); + heap[(short)(ptr+TLV_HEADER_SIZE+4)] = 0x01; + return ptr; } - public static void create(KMBoolTag[] boolTagRefTable) { - byte index = 0; - while (index < boolTagRefTable.length) { - boolTagRefTable[index] = new KMBoolTag(); - index++; + public static KMBoolTag cast(short ptr) { + if (heap[ptr] != TAG_TYPE) ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + if (Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE)) != BOOL_TAG) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); } + return proto(ptr); } - @Override public short getKey() { - return key; - } - - @Override - public short length() { - return 1; + return Util.getShort(heap, (short)(instPtr+TLV_HEADER_SIZE+2)); } - @Override public short getTagType() { return KMType.BOOL_TAG; } public byte getVal() { - return val; - } - // create default assignBlob without any value - public static KMBoolTag instance(short key) { - if (!validateKey(key)) { - throw new KMException(ISO7816.SW_DATA_INVALID); - } - KMBoolTag tag = repository.newBoolTag(); - tag.key = key; - return tag; + return heap[(short)(instPtr+TLV_HEADER_SIZE+4)]; } - // validate the tag key + // isValidTag the tag key private static boolean validateKey(short key) { short index = (short) tags.length; while (--index >= 0) { @@ -98,4 +93,7 @@ private static boolean validateKey(short key) { } return false; } + public static short[] getTags(){ + return tags; + } } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMByteBlob.java b/Applet/Applet/src/com/android/javacard/keymaster/KMByteBlob.java index fca5b947..7f598eaa 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMByteBlob.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMByteBlob.java @@ -17,95 +17,96 @@ package com.android.javacard.keymaster; import javacard.framework.ISO7816; +import javacard.framework.ISOException; import javacard.framework.Util; -// Byte val represents contiguous memory buffer. +// Byte blob represents contiguous memory buffer. public class KMByteBlob extends KMType { - private byte[] val; - private short startOff; - private short length; + private static KMByteBlob prototype; + private static short instPtr; - private KMByteBlob() { - init(); + private KMByteBlob() {} + + private static KMByteBlob proto(short ptr) { + if (prototype == null) prototype = new KMByteBlob(); + instPtr = ptr; + return prototype; } - @Override - public void init() { - length = 0; - startOff = 0; - val = null; + // pointer to an empty instance used as expression + public static short exp() { + return KMType.exp(BYTE_BLOB_TYPE); } - @Override - public short length() { - return length; + // return an empty byte blob instance + public static short instance(short length) { + return KMType.instance(BYTE_BLOB_TYPE, length); } - public static KMByteBlob instance() { - return repository.newByteBlob(); + // byte blob from existing buf + public static short instance(byte[] buf, short startOff, short length) { + short ptr = instance(length); + Util.arrayCopyNonAtomic(buf, startOff, heap, (short) (ptr + TLV_HEADER_SIZE), length); + return ptr; } - // copy the blob - public static KMByteBlob instance(byte[] blob, short startOff, short length) { - if ((length <= 0) || ((short)(startOff+length) > blob.length)) { - throw new KMException(ISO7816.SW_WRONG_LENGTH); + // cast the ptr to KMByteBlob + public static KMByteBlob cast(short ptr) { + if (heap[ptr] != BYTE_BLOB_TYPE) ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + if (Util.getShort(heap, (short) (ptr + 1)) == INVALID_VALUE) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); } - KMByteBlob inst = instance(length); - Util.arrayCopyNonAtomic(blob, startOff, inst.val, inst.startOff, inst.length); - return inst; + return proto(ptr); } - // returns empty blob with given length - public static KMByteBlob instance(short length) { - if (length <= 0) { - throw new KMException(ISO7816.SW_WRONG_LENGTH); - } - KMByteBlob inst = instance(); - inst.startOff = repository.newByteArray(length); - inst.val = repository.getByteHeapRef(); - inst.length = length; - return inst; + // Add the byte + public void add(short index, byte val) { + short len = length(); + if (index >= len) ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); + heap[(short) (instPtr + TLV_HEADER_SIZE + index)] = val; } - public static void create(KMByteBlob[] byteBlobRefTable) { - byte index = 0; - while (index < byteBlobRefTable.length) { - byteBlobRefTable[index] = new KMByteBlob(); - index++; - } + // Get the byte + public byte get(short index) { + short len = length(); + if (index >= len) ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); + return heap[(short) (instPtr + TLV_HEADER_SIZE + index)]; } - // sets the expected length for prototype byte val. - public KMByteBlob withLength(short len) { - this.length = len; - return this; + // Get the start of blob + public short getStartOff() { + return (short) (instPtr + TLV_HEADER_SIZE); } - public void add(short index, byte val) { - if (index >= this.length) { - throw new KMException(ISO7816.SW_WRONG_LENGTH); - } - if (this.val == null) { - throw new KMException(ISO7816.SW_DATA_INVALID); - } - this.val[(short) (startOff + index)] = val; + // Get the length of the blob + public short length() { + return Util.getShort(heap, (short) (instPtr + 1)); } - public byte get(short index) { - if (index >= this.length) { - throw new KMException(ISO7816.SW_WRONG_LENGTH); - } - if (this.val == null) { - throw new KMException(ISO7816.SW_DATA_INVALID); - } - return this.val[(short) (startOff + index)]; + // Get the buffer pointer in which blob is contained. + public byte[] getBuffer() { + return heap; } - public byte[] getVal() { - return val; + public void getValue(byte[] destBuf, short destStart, short destLength){ + Util.arrayCopyNonAtomic(heap, getStartOff(), destBuf, destStart, destLength); + } + public short getValues(byte[] destBuf, short destStart){ + short destLength = length(); + Util.arrayCopyNonAtomic(heap, getStartOff(), destBuf, destStart, destLength); + return destLength; } - public short getStartOff() { - return startOff; + public void setValue(byte[] srcBuf, short srcStart, short srcLength){ + if(length() > srcLength){ + ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); + } + Util.arrayCopyNonAtomic(srcBuf, srcStart, heap, getStartOff(), length()); + } + public boolean isValid(){ + if (length() == 0) { + return false; + } + return true; } } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMByteTag.java b/Applet/Applet/src/com/android/javacard/keymaster/KMByteTag.java index c415abbc..a00d6c6f 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMByteTag.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMByteTag.java @@ -17,8 +17,12 @@ package com.android.javacard.keymaster; import javacard.framework.ISO7816; +import javacard.framework.ISOException; +import javacard.framework.Util; public class KMByteTag extends KMTag { + private static KMByteTag prototype; + private static short instPtr; private static final short[] tags = { APPLICATION_ID, @@ -37,73 +41,73 @@ public class KMByteTag extends KMTag { ATTESTATION_ID_MODEL, ASSOCIATED_DATA, NONCE, - CONFIRMATION_TOKEN + CONFIRMATION_TOKEN, + VERIFIED_BOOT_KEY, + VERIFIED_BOOT_HASH }; - private short key; - private KMByteBlob val; + private KMByteTag() {} - private KMByteTag() { - init(); + private static KMByteTag proto(short ptr) { + if (prototype == null) prototype = new KMByteTag(); + instPtr = ptr; + return prototype; } - @Override - public void init() { - key = 0; - val = null; + // pointer to an empty instance used as expression + public static short exp() { + short blobPtr = KMByteBlob.exp(); + short ptr = instance(TAG_TYPE, (short)6); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE), BYTES_TAG); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE+2), INVALID_TAG); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE+4), blobPtr); + return ptr; } - @Override - public short getKey() { - return key; - } - - @Override - public short length() { - return val.length(); + public static short instance(short key) { + if (!validateKey(key)) { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + return instance(key, KMByteBlob.exp()); } - @Override - public short getTagType() { - return KMType.BYTES_TAG; + public static short instance(short key, short byteBlob) { + if (!validateKey(key)) { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + if(heap[byteBlob] != BYTE_BLOB_TYPE) { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + short ptr = instance(TAG_TYPE, (short)6); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE), BYTES_TAG); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE+2), key); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE+4), byteBlob); + return ptr; } - public static KMByteTag instance() { - return repository.newByteTag(); + public static KMByteTag cast(short ptr) { + if (heap[ptr] != TAG_TYPE) ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + if (Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE)) != BYTES_TAG) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + return proto(ptr); } - public static KMByteTag instance(short key) { - if (!validateKey(key)) { - throw new KMException(ISO7816.SW_DATA_INVALID); - } - KMByteTag tag = repository.newByteTag(); - tag.key = key; - tag.val = null; - return tag; + public short getKey() { + return Util.getShort(heap, (short)(instPtr+TLV_HEADER_SIZE+2)); } - public static void create(KMByteTag[] byteTagRefTable) { - byte index = 0; - while (index < byteTagRefTable.length) { - byteTagRefTable[index] = new KMByteTag(); - index++; - } + public short getTagType() { + return KMType.BYTES_TAG; } - // create default assignBlob without any value - public static KMByteTag instance(short key, KMByteBlob array) { - if (!validateKey(key)) { - throw new KMException(ISO7816.SW_DATA_INVALID); - } - KMByteTag tag = repository.newByteTag(); - tag.key = key; - tag.val = array; - return tag; + public short getValue() { + return Util.getShort(heap, (short)(instPtr+TLV_HEADER_SIZE+4)); } - public KMByteTag withLength(short length) { - this.val.withLength(length); - return this; + public short length() { + short blobPtr = Util.getShort(heap, (short)(instPtr+TLV_HEADER_SIZE+4)); + return KMByteBlob.cast(blobPtr).length(); } private static boolean validateKey(short key) { @@ -115,13 +119,4 @@ private static boolean validateKey(short key) { } return false; } - - public KMByteBlob getValue() { - return val; - } - - public KMByteTag setValue(KMByteBlob val) { - this.val = val; - return this; - } } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMCipher.java b/Applet/Applet/src/com/android/javacard/keymaster/KMCipher.java new file mode 100644 index 00000000..922f9a43 --- /dev/null +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMCipher.java @@ -0,0 +1,30 @@ +package com.android.javacard.keymaster; + +public abstract class KMCipher { + + public static final byte CIPHER_RSA = 7; + + 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_NULL = 0; + public static final short PAD_PKCS7 = 31; // Not supported in javacard + public static final short CIPHER_DES_CBC = 3; + public static final short CIPHER_DES_ECB = 4; + public static final short CIPHER_AES_CBC = 1; + public static final short CIPHER_AES_ECB = 2; + public static final short MODE_ENCRYPT = 2; + public static final short MODE_DECRYPT = 1; + public static final short PAD_PKCS1 = 7; + + public abstract short doFinal(byte[] buffer, short startOff, short length, byte[] scratchPad, short i); + + public abstract short getCipherAlgorithm(); + + public abstract short update(byte[] buffer, short startOff, short length, byte[] scratchPad, short i); + + public abstract short getPaddingAlgorithm(); + +} diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMCommand.java b/Applet/Applet/src/com/android/javacard/keymaster/KMCommand.java deleted file mode 100644 index 9e17dd58..00000000 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMCommand.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * 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; - -import javacard.framework.APDU; - -/** This interface declares methods to be implemented by the command instances. */ -public interface KMCommand { - /** - * Execute this command within given context. If the command fails then it throws an exception - * - * @param context provides information required to execute the command. - */ - void execute(KMContext context); - - /** - * Return the instruction code associated with this command. The implementations will provide this - * code. - * - * @return instruction code which is related APDU INS. - */ - byte getIns(); - - /** - * Indicates whether command has arguments. - * @ return true if the command has arguments - */ - boolean hasArguments(); -} diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMComputeSharedHmacCmd.java b/Applet/Applet/src/com/android/javacard/keymaster/KMComputeSharedHmacCmd.java deleted file mode 100644 index 5d2e0b46..00000000 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMComputeSharedHmacCmd.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * 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; - -public class KMComputeSharedHmacCmd extends KMAbstractCmd { - public static final byte INS_COMPUTE_SHARED_HMAC_CMD = 0x19; - - @Override - protected KMArray getExpectedArgs() { - return null; - } - - @Override - protected KMArray process(KMArray args, KMContext context) { - return null; - } - - @Override - public byte getIns() { - return INS_COMPUTE_SHARED_HMAC_CMD; - } -} diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMContext.java b/Applet/Applet/src/com/android/javacard/keymaster/KMContext.java deleted file mode 100644 index 0d081f89..00000000 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMContext.java +++ /dev/null @@ -1,119 +0,0 @@ -/* - * 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 provides data structure for information which is passed between the Keymaster Applet - * and the commands. It is created by applet and initialized for the process request. Applet sets - * repository, apdu and keymasterState. Command sets and uses incoming buffer information, outgoing - * buffer information and operation state (if command is an operation). - */ -public class KMContext { - private KMRepository repository; - private byte keymasterState; - private KMOperationState opState; - private byte[] buffer; - private short bufferLength; - /** - * Setter for the keymasterState. Set by the applet. - * - * @param keymasterState represents current applet state. - */ - public void setKeymasterState(byte keymasterState) { - this.keymasterState = keymasterState; - } - - /** - * Getter for keymasterState. Used by the commands. - * - * @return keymasterState represents current applets state. - */ - public byte getKeymasterState() { - return keymasterState; - } - - - /** - * Getter for buffer used for receiving or sending data to or from the master. Used by the - * messenger. - * - * @return buffer which is used to copying data to and from apdu's buffer. Start offset is always - * 0. - */ - public byte[] getBuffer() { - return buffer; - } - - /** - * Setter for buffer. Used by the repository. - * - * @param buffer which is used to copying data to and from apdu's buffer. - */ - public void setBuffer(byte[] buffer) { - this.buffer = buffer; - } - - /** - * Getter for buffer length. Used by the messenger and commands. - * - * @return buffer length. - */ - public short getBufferLength() { - return bufferLength; - } - - /** - * Setter for buffer length. Used by the messenger commands. - * - * @param length of buffer. - */ - public void setBufferLength(short length) { - this.bufferLength = length; - } - - /** - * Getter for repository instance. Used by commands. - * - * @return repository - */ - public KMRepository getRepository() { - return repository; - } - - /** - * Setter for the repository instance. Used by the applet. - * - * @param repository is repository of the KMType objects and other objects. - */ - public void setRepository(KMRepository repository) { - this.repository = repository; - } - - /** - * Getter for the OperationState for operation specific commands. Used by commands. - * - * @return Operation state associated with the command. - */ - public KMOperationState getOpState() { - return opState; - } - - /** Setter for the OperationState for operation specific commands. Used by commands. */ - public void setOpState(KMOperationState opState) { - this.opState = opState; - } -} diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMCryptoProvider.java b/Applet/Applet/src/com/android/javacard/keymaster/KMCryptoProvider.java new file mode 100644 index 00000000..055822e5 --- /dev/null +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMCryptoProvider.java @@ -0,0 +1,108 @@ +package com.android.javacard.keymaster; + +import javacard.security.AESKey; +import javacard.security.DESKey; +import javacard.security.ECPrivateKey; +import javacard.security.HMACKey; +import javacard.security.Key; +import javacard.security.KeyPair; +import javacard.security.RSAPrivateKey; +import javacard.security.Signature; + +public interface KMCryptoProvider { + KeyPair createRsaKeyPair(); + + KeyPair createECKeyPair(); + + AESKey createAESKey(short keysize); + + AESKey createAESKey(byte[] buf, short startOff, short length); + + DESKey createTDESKey(); + + HMACKey createHMACKey(short keysize); + + void newRandomNumber(byte[] num, short offset, short length); + + void addRngEntropy(byte[] num, short offset, short length); + + 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); + + boolean aesGCMDecrypt( + AESKey key, + byte[] encSecret, + short encSecretStart, + short encSecretLen, + byte[] secret, + short secretStart, + byte[] nonce, + short nonceStart, + short nonceLen, + byte[] authData, + short authDataStart, + short authDataLen, + byte[] authTag, + short authTagStart, + short authTagLen); + + byte[] getTrueRandomNumber(short len); + + short aesCCMSign( + byte[] bufIn, + short bufInStart, + short buffInLength, + byte[] masterKeySecret, + byte[] bufOut, + short bufStart); + + ECPrivateKey createEcKey(byte[] privBuffer, short privOff, short privLength); + + HMACKey createHMACKey(byte[] secretBuffer, short secretOff, short secretLength); + + DESKey createTDESKey(byte[] secretBuffer, short secretOff, short secretLength); + + RSAPrivateKey createRsaKey(byte[] modBuffer, short modOff, short modLength, + byte[] privBuffer, short privOff, short privLength); + + HMACKey cmacKdf(byte[] keyMaterial, byte[] label, byte[] context, short contextStart, short contextLength); + + short hmacSign(HMACKey key, byte[] data, short dataStart, short dataLength, byte[] mac, short macStart); + boolean hmacVerify(HMACKey key, byte[] data, short dataStart, short dataLength, + byte[] mac, short macStart, short macLength); + + KMCipher createRsaDecrypt(short cipherAlg, short padding, + byte[] secret, short secretStart, short secretLength, + byte[] modBuffer, short modOff, short modLength); + Signature createRsaSigner(short msgDigestAlg, short padding, byte[] secret, short secretStart, + short secretLength,byte[] modBuffer, short modOff, short modLength); + Signature createEcSigner(short msgDigestAlg, byte[] secret, short secretStart, + short secretLength); + KMCipher createSymmetricCipher(short cipherAlg, short padding, short mode, + byte[] secret, short secretStart, short secretLength, + byte[] ivBuffer, short ivStart, short ivLength); + Signature createHmacSigner(short msgDigestAlg, + byte[] secret, short secretStart, short secretLength); + KMCipher createGCMCipher(short mode, byte[] secret, short secretStart, short secretLength, + byte[] ivBuffer, short ivStart, short ivLength); + void delete(KMCipher cipher); + void delete(Signature signature); + void delete(Key key); + void delete(KeyPair keyPair); + //TODO remove this later + void bypassAesGcm(); +} diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMDecoder.java b/Applet/Applet/src/com/android/javacard/keymaster/KMDecoder.java index 4cedfcaf..daefb282 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMDecoder.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMDecoder.java @@ -17,8 +17,9 @@ package com.android.javacard.keymaster; import javacard.framework.ISO7816; +import javacard.framework.ISOException; import javacard.framework.Util; -// TODO Clean and refactor the code. + public class KMDecoder { // major types private static final short UINT_TYPE = 0x00; @@ -36,7 +37,6 @@ public class KMDecoder { private static final short UINT32_LENGTH = 0x1A; private static final short UINT64_LENGTH = 0x1B; - // TODO move the following to transient memory. private byte[] buffer; private short startOff; private short length; @@ -49,67 +49,215 @@ public KMDecoder() { length = 0; } - public KMArray decode(KMArray expression, byte[] buffer, short startOff, short length) { + public short decode(short expression, byte[] buffer, short startOff, short length) { this.buffer = buffer; this.startOff = startOff; - this.length = length; + this.length = (short)(startOff+length); return decode(expression); } + public short decodeArray(short exp, byte[] buffer, short startOff, short length){ + this.buffer = buffer; + this.startOff = startOff; + this.length = (short)(startOff+length); + short payloadLength = readMajorTypeWithPayloadLength(ARRAY_TYPE); + short expLength = KMArray.cast(exp).length(); + if(payloadLength > expLength){ + ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); + } + short index = 0; + short obj; + short type; + short arrPtr = KMArray.instance(payloadLength); + while (index < payloadLength) { + type = KMArray.cast(exp).get(index); + obj = decode(type); + KMArray.cast(arrPtr).add(index, obj); + index++; + } + return arrPtr; + } + private short decode(short exp){ + byte type = KMType.getType(exp); + switch(type){ + case KMType.BYTE_BLOB_TYPE: + return decodeByteBlob(exp); + case KMType.INTEGER_TYPE: + return decodeInteger(exp); + case KMType.ARRAY_TYPE: + return decodeArray(exp); + case KMType.ENUM_TYPE: + return decodeEnum(exp); + case KMType.KEY_PARAM_TYPE: + return decodeKeyParam(exp); + case KMType.KEY_CHAR_TYPE: + return decodeKeyChar(exp); + case KMType.VERIFICATION_TOKEN_TYPE: + return decodeVeriToken(exp); + case KMType.HMAC_SHARING_PARAM_TYPE: + return decodeHmacSharingParam(exp); + case KMType.HW_AUTH_TOKEN_TYPE: + return decodeHwAuthToken(exp); + case KMType.TAG_TYPE: + short tagType = KMTag.getTagType(exp); + return decodeTag(tagType, exp); + default: + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + return 0; + } + } + private short decodeTag(short tagType, short exp){ + switch(tagType){ + case KMType.BYTES_TAG: + return decodeBytesTag(exp); + case KMType.BOOL_TAG: + return decodeBoolTag(exp); + case KMType.UINT_TAG: + case KMType.ULONG_TAG: + case KMType.DATE_TAG: + return decodeIntegerTag(exp); + case KMType.ULONG_ARRAY_TAG: + case KMType.UINT_ARRAY_TAG: + return decodeIntegerArrayTag(exp); + case KMType.ENUM_TAG: + return decodeEnumTag(exp); + case KMType.ENUM_ARRAY_TAG: + return decodeEnumArrayTag(exp); + default: + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + return 0; + } + } + + private short decodeVeriToken(short exp) { + short vals = decode(KMVerificationToken.cast(exp).getVals()); + return KMVerificationToken.instance(vals); + } + + private short decodeHwAuthToken(short exp) { + short vals = decode(KMHardwareAuthToken.cast(exp).getVals()); + return KMHardwareAuthToken.cast(exp).instance(vals); + } + + private short decodeHmacSharingParam(short exp) { + short vals = decode(KMHmacSharingParameters.cast(exp).getVals()); + return KMHmacSharingParameters.instance(vals); + } + + private short decodeKeyChar(short exp) { + short vals = decode(KMKeyCharacteristics.cast(exp).getVals()); + return KMKeyCharacteristics.instance(vals); + } + + private short decodeKeyParam(short exp) { + short payloadLength = readMajorTypeWithPayloadLength(MAP_TYPE); + // allowed tags + short allowedTags = KMKeyParameters.cast(exp).getVals(); + short vals = KMArray.instance(payloadLength); + short length = KMArray.cast(allowedTags).length(); + short index = 0; + boolean tagFound; + short tagInd; + short tagType; + short tagClass; + short allowedType; + short obj; + // For each tag in payload ... + while (index < payloadLength) { + tagFound = false; + tagInd = 0; + tagType = peekTagType(); + // Check against the allowed tags ... + while (tagInd < length) { + tagClass = KMArray.cast(allowedTags).get(tagInd); + allowedType = KMTag.getTagType(tagClass); + // If it is part of allowed tags ... + if (tagType == allowedType) { + // then decodeByteBlob and add that to the array. + obj = decode(tagClass); + KMArray.cast(vals).add(index,obj); + tagFound = true; + break; + } + tagInd++; + } + if(!tagFound){ + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + else { + index++; + } + } + return KMKeyParameters.instance(vals); + } - private KMEnumArrayTag decode(KMEnumArrayTag exp) { - readTagKey(exp.getTagType()); + private short decodeEnumArrayTag(short exp) { + readTagKey(KMEnumArrayTag.cast(exp).getTagType()); // The value must be byte blob // TODO check this out. - return exp.instance(this.tagKey, decode(exp.getValues())); + return KMEnumArrayTag.instance(this.tagKey, decode(KMEnumArrayTag.cast(exp).getValues())); } - private KMIntegerArrayTag decode(KMIntegerArrayTag exp) { - readTagKey(exp.getTagType()); + private short decodeIntegerArrayTag(short exp) { + readTagKey(KMIntegerArrayTag.cast(exp).getTagType()); // the values are array of integers. - if (!(exp.getValues().getType() instanceof KMInteger)) { - throw new KMException(ISO7816.SW_DATA_INVALID); - } - return exp.instance(this.tagKey, decode(exp.getValues(), (KMInteger) exp.getValues().getType())); + return KMIntegerArrayTag.instance(KMIntegerArrayTag.cast(exp).getTagType(), + this.tagKey, decode(KMIntegerArrayTag.cast(exp).getValues())); } - private KMIntegerTag decode(KMIntegerTag exp) { - readTagKey(exp.getTagType()); + private short decodeIntegerTag(short exp) { + readTagKey(KMIntegerTag.cast(exp).getTagType()); // the value is an integer - return exp.instance(this.tagKey, decode(exp.getValue())); + return KMIntegerTag.instance(KMIntegerTag.cast(exp).getTagType(), + this.tagKey, decode(KMIntegerTag.cast(exp).getValue())); } - private KMByteTag decode(KMByteTag exp) { - short key = 0; - readTagKey(exp.getTagType()); + private short decodeBytesTag(short exp) { + readTagKey(KMByteTag.cast(exp).getTagType()); // The value must be byte blob - return exp.instance(this.tagKey, decode(exp.getValue())); + return KMByteTag.instance(this.tagKey, decode(KMByteTag.cast(exp).getValue())); } - private KMBoolTag decode(KMBoolTag exp) { - readTagKey(exp.getTagType()); - // BOOL Tag is a leaf node and it must always have tiny encoded uint value = 1. - // TODO check this out. - if ((buffer[startOff] & MAJOR_TYPE_MASK) != UINT_TYPE) { - throw new KMException(ISO7816.SW_DATA_INVALID); - } - if ((byte) (buffer[startOff] & ADDITIONAL_MASK) != 0x01) { - throw new KMException(ISO7816.SW_DATA_INVALID); + private short decodeArray(short exp) { + short payloadLength = readMajorTypeWithPayloadLength(ARRAY_TYPE); + short arrPtr = KMArray.cast(exp).instance(payloadLength); + short index = 0; + short type; + short obj; + // check whether array contains one type of objects or multiple types + if( KMArray.cast(exp).containedType() == 0){// multiple types specified by expression. + if (KMArray.cast(exp).length() != KMArray.ANY_ARRAY_LENGTH) { + if (KMArray.cast(exp).length() != payloadLength) { + ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); + } + } + while (index < payloadLength) { + type = KMArray.cast(exp).get(index); + obj = decode(type); + KMArray.cast(arrPtr).add(index, obj); + index++; + } + }else{ // Array is a Vector containing objects of one type + type = KMArray.cast(exp).containedType(); + while(index < payloadLength){ + obj = decode(type); + KMArray.cast(arrPtr).add(index, obj); + index++; + } } - incrementStartOff((short) 1); - return exp.instance(tagKey); + return arrPtr; } - private KMEnumTag decode(KMEnumTag exp) { - readTagKey(exp.getTagType()); + private short decodeEnumTag(short exp) { + readTagKey(KMEnumTag.cast(exp).getTagType()); // Enum Tag value will always be integer with max 1 byte length. // TODO Check this out. if ((buffer[startOff] & MAJOR_TYPE_MASK) != UINT_TYPE) { - throw new KMException(ISO7816.SW_DATA_INVALID); + ISOException.throwIt(ISO7816.SW_DATA_INVALID); } short len = (short) (buffer[startOff] & ADDITIONAL_MASK); byte enumVal = 0; if (len > UINT8_LENGTH) { - throw new KMException(ISO7816.SW_WRONG_LENGTH); + ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); } if (len < UINT8_LENGTH) { enumVal = (byte)(len & ADDITIONAL_MASK); @@ -119,19 +267,32 @@ private KMEnumTag decode(KMEnumTag exp) { enumVal = buffer[startOff]; incrementStartOff((short) 1); } - return exp.instance(tagKey, enumVal); + return KMEnumTag.instance(tagKey, enumVal); } - private KMEnum decode(KMEnum exp) { + private short decodeBoolTag(short exp) { + readTagKey(KMBoolTag.cast(exp).getTagType()); + // BOOL Tag is a leaf node and it must always have tiny encoded uint value = 1. + // TODO check this out. + if ((buffer[startOff] & MAJOR_TYPE_MASK) != UINT_TYPE) { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + if ((byte) (buffer[startOff] & ADDITIONAL_MASK) != 0x01) { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + incrementStartOff((short) 1); + return KMBoolTag.instance(tagKey); + } + private short decodeEnum(short exp) { // Enum value will always be integer with max 1 byte length. if ((buffer[startOff] & MAJOR_TYPE_MASK) != UINT_TYPE) { - throw new KMException(ISO7816.SW_DATA_INVALID); + ISOException.throwIt(ISO7816.SW_DATA_INVALID); } short len = (short) (buffer[startOff] & ADDITIONAL_MASK); - byte enumVal = 0; + byte enumVal; if (len > UINT8_LENGTH) { - throw new KMException(ISO7816.SW_WRONG_LENGTH); + ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); } if (len < UINT8_LENGTH) { enumVal = (byte)(len & ADDITIONAL_MASK); @@ -141,175 +302,51 @@ private KMEnum decode(KMEnum exp) { enumVal = buffer[startOff]; incrementStartOff((short) 1); } - return exp.instance(exp.getType(), enumVal); + return KMEnum.instance(KMEnum.cast(exp).getEnumType(), enumVal); } - private KMInteger decode(KMInteger exp) { - KMInteger inst; + private short decodeInteger(short exp) { + short inst; if ((buffer[startOff] & MAJOR_TYPE_MASK) != UINT_TYPE) { - throw new KMException(ISO7816.SW_DATA_INVALID); + ISOException.throwIt(ISO7816.SW_DATA_INVALID); } short len = (short) (buffer[startOff] & ADDITIONAL_MASK); + if(len > UINT64_LENGTH){ + ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); + } incrementStartOff((short) 1); if (len < UINT8_LENGTH) { - inst = exp.uint_8((byte)(len & ADDITIONAL_MASK)); + inst = KMInteger.uint_8((byte)(len & ADDITIONAL_MASK)); } else if (len == UINT8_LENGTH) { - inst = exp.instance(buffer, startOff, (short) 1); + inst = KMInteger.instance(buffer, startOff, (short) 1); incrementStartOff((short) 1); } else if (len == UINT16_LENGTH) { - inst = exp.instance(buffer, startOff, (short) 2); + inst = KMInteger.instance(buffer, startOff, (short) 2); incrementStartOff((short) 2); } else if (len == UINT32_LENGTH) { - inst = exp.instance(buffer, startOff, (short) 4); + inst = KMInteger.instance(buffer, startOff, (short) 4); incrementStartOff((short) 4); - } else if (len == UINT64_LENGTH) { - inst = exp.instance(buffer, startOff, (short) 8); - incrementStartOff((short) 8); } else { - throw new KMException(ISO7816.SW_WRONG_LENGTH); + inst = KMInteger.instance(buffer, startOff, (short) 8); + incrementStartOff((short) 8); } return inst; } - private KMByteBlob decode(KMByteBlob exp) { + private short decodeByteBlob(short exp) { short payloadLength = readMajorTypeWithPayloadLength(BYTES_TYPE); - KMByteBlob inst = exp.instance(buffer, startOff, payloadLength); + short inst = KMByteBlob.instance(buffer, startOff, payloadLength); incrementStartOff(payloadLength); return inst; } - private KMArray decode(KMArray exp) { - short payloadLength = readMajorTypeWithPayloadLength(ARRAY_TYPE); - if (exp.length() != payloadLength) { - throw new KMException(ISO7816.SW_WRONG_LENGTH); - } - KMArray inst = exp.instance(payloadLength); - short index = 0; - while (index < payloadLength) { - KMType type = exp.get(index); - inst.add(index, decode(type)); - index++; - } - return inst; - } - - private KMVector decode(KMVector exp, KMInteger type) { - short payloadLength = readMajorTypeWithPayloadLength(ARRAY_TYPE); - KMVector inst = exp.instance(type, payloadLength); - short index = 0; - while (index < payloadLength) { - inst.add(index, decode(type)); - index++; - } - return inst; - } - - private KMVerificationToken decode(KMVerificationToken exp) { - KMArray vals = decode(exp.getVals()); - return exp.instance(vals); - } - - private KMHardwareAuthToken decode(KMHardwareAuthToken exp) { - KMArray vals = decode(exp.getVals()); - return exp.instance(vals); - } - - private KMHmacSharingParameters decode(KMHmacSharingParameters exp) { - KMArray vals = decode(exp.getVals()); - return exp.instance(vals); - } - - private KMKeyParameters decode(KMKeyParameters exp) { - short payloadLength = readMajorTypeWithPayloadLength(MAP_TYPE); - // allowed tags - // TODO expand the logic to handle prototypes with tag values also. - KMArray allowedTags = exp.getVals(); - KMArray vals = KMArray.instance(payloadLength); - short index = 0; - while (index < payloadLength) { - short tagInd = 0; - short tagType = peekTagType(); - while (tagInd < allowedTags.length()) { - KMTag tagClass = ((KMTag) allowedTags.get(tagInd)); - short allowedType = ((KMTag) allowedTags.get(tagInd)).getTagType(); - if (tagType == allowedType) { - vals.add(index, decode(tagClass)); - break; - } - tagInd++; - } - index++; - } - return KMKeyParameters.instance(vals); - } - - private KMKeyCharacteristics decode(KMKeyCharacteristics exp) { - KMArray vals = decode(exp.getVals()); - return exp.instance(vals); - } - - private KMType decode(KMType exp) { - if (exp instanceof KMByteBlob) { - return decode((KMByteBlob) exp); - } - if (exp instanceof KMInteger) { - return decode((KMInteger) exp); - } - if (exp instanceof KMArray) { - return decode((KMArray) exp); - } - if (exp instanceof KMVector) { - if (!((((KMVector) exp).getType()) instanceof KMInteger)) { - throw new KMException(ISO7816.SW_DATA_INVALID); - } - return decode((KMVector) exp, (KMInteger) ((KMVector) exp).getType()); - } - if (exp instanceof KMByteTag) { - return decode((KMByteTag) exp); - } - if (exp instanceof KMBoolTag) { - return decode((KMBoolTag) exp); - } - if (exp instanceof KMIntegerTag) { - return decode((KMIntegerTag) exp); - } - if (exp instanceof KMIntegerArrayTag) { - return decode((KMIntegerArrayTag) exp); - } - if (exp instanceof KMEnumTag) { - return decode((KMEnumTag) exp); - } - if (exp instanceof KMEnum) { - return decode((KMEnum) exp); - } - if (exp instanceof KMEnumArrayTag) { - return decode((KMEnumArrayTag) exp); - } - if (exp instanceof KMKeyParameters) { - return decode((KMKeyParameters) exp); - } - if (exp instanceof KMKeyCharacteristics) { - return decode((KMKeyCharacteristics) exp); - } - if (exp instanceof KMVerificationToken) { - return decode((KMVerificationToken) exp); - } - if (exp instanceof KMHmacSharingParameters) { - return decode((KMHmacSharingParameters) exp); - } - if (exp instanceof KMHardwareAuthToken) { - return decode((KMHardwareAuthToken) exp); - } - throw new KMException(ISO7816.SW_DATA_INVALID); - } - private short peekTagType() { if ((buffer[startOff] & MAJOR_TYPE_MASK) != UINT_TYPE) { - throw new KMException(ISO7816.SW_DATA_INVALID); + ISOException.throwIt(ISO7816.SW_DATA_INVALID); } if ((short) (buffer[startOff] & ADDITIONAL_MASK) != UINT32_LENGTH) { - throw new KMException(ISO7816.SW_WRONG_LENGTH); + ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); } return (short) ((Util.makeShort(buffer[(short) (startOff + 1)], buffer[(short) (startOff + 2)])) @@ -318,16 +355,16 @@ private short peekTagType() { private void readTagKey(short expectedTagType) { if ((buffer[startOff] & MAJOR_TYPE_MASK) != UINT_TYPE) { - throw new KMException(ISO7816.SW_DATA_INVALID); + ISOException.throwIt(ISO7816.SW_DATA_INVALID); } if ((byte) (buffer[startOff] & ADDITIONAL_MASK) != UINT32_LENGTH) { - throw new KMException(ISO7816.SW_WRONG_LENGTH); + ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); } incrementStartOff((short) 1); this.tagType = readShort(); this.tagKey = readShort(); if (tagType != expectedTagType) { - throw new KMException(ISO7816.SW_DATA_INVALID); + ISOException.throwIt(ISO7816.SW_DATA_INVALID); } } @@ -336,11 +373,11 @@ private short readMajorTypeWithPayloadLength(short majorType) { short payloadLength = 0; byte val = readByte(); if ((short) (val & MAJOR_TYPE_MASK) != majorType) { - throw new KMException(ISO7816.SW_DATA_INVALID); + ISOException.throwIt(ISO7816.SW_DATA_INVALID); } short lenType = (short) (val & ADDITIONAL_MASK); if (lenType > UINT16_LENGTH) { - throw new KMException(ISO7816.SW_WRONG_LENGTH); + ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); } if (lenType < UINT8_LENGTH) { payloadLength = lenType; @@ -367,7 +404,7 @@ private byte readByte() { private void incrementStartOff(short inc) { startOff += inc; if (startOff > this.length) { - throw new KMException(ISO7816.SW_DATA_INVALID); + ISOException.throwIt(ISO7816.SW_DATA_INVALID); } } } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMDeleteAllKeysCmd.java b/Applet/Applet/src/com/android/javacard/keymaster/KMDeleteAllKeysCmd.java deleted file mode 100644 index 5c476cd0..00000000 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMDeleteAllKeysCmd.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * 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; - -public class KMDeleteAllKeysCmd extends KMAbstractCmd { - public static final byte INS_DELETE_ALL_KEYS_CMD = 0x17; - - @Override - protected KMArray getExpectedArgs() { - return null; - } - - @Override - protected KMArray process(KMArray args, KMContext context) { - return null; - } - - @Override - public byte getIns() { - return INS_DELETE_ALL_KEYS_CMD; - } -} diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMDeleteKeyCmd.java b/Applet/Applet/src/com/android/javacard/keymaster/KMDeleteKeyCmd.java deleted file mode 100644 index ced6e7e4..00000000 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMDeleteKeyCmd.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * 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; - -public class KMDeleteKeyCmd extends KMAbstractCmd { - public static final byte INS_DELETE_KEY_CMD = 0x16; - - @Override - protected KMArray getExpectedArgs() { - return null; - } - - @Override - protected KMArray process(KMArray args, KMContext context) { - return null; - } - - @Override - public byte getIns() { - return INS_DELETE_KEY_CMD; - } -} diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMDestroyAttestationIdsCmd.java b/Applet/Applet/src/com/android/javacard/keymaster/KMDestroyAttestationIdsCmd.java deleted file mode 100644 index fa66ab1f..00000000 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMDestroyAttestationIdsCmd.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * 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; - -public class KMDestroyAttestationIdsCmd extends KMAbstractCmd { - public static final byte INS_DESTROY_ATT_IDS_CMD = 0x1A; - - @Override - protected KMArray getExpectedArgs() { - return null; - } - - @Override - protected KMArray process(KMArray args, KMContext context) { - return null; - } - - @Override - public byte getIns() { - return INS_DESTROY_ATT_IDS_CMD; - } -} diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMEncoder.java b/Applet/Applet/src/com/android/javacard/keymaster/KMEncoder.java index a231563b..01fb137a 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMEncoder.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMEncoder.java @@ -17,6 +17,9 @@ package com.android.javacard.keymaster; import javacard.framework.ISO7816; +import javacard.framework.ISOException; +import javacard.framework.JCSystem; +import javacard.framework.SystemException; import javacard.framework.Util; public class KMEncoder { @@ -42,177 +45,214 @@ public class KMEncoder { private byte[] buffer; private short startOff; private short length; + private static short[] stack; + private static byte stackPtr; public KMEncoder() { buffer = null; startOff = 0; length = 0; + stack = JCSystem.makeTransientShortArray((short)50, JCSystem.CLEAR_ON_RESET); } - public short encode(KMArray object, byte[] buffer, short startOff, short length) { + private static void push (short objPtr){ + stack[stackPtr] = objPtr; + stackPtr++; + } + private static short pop(){ + stackPtr--; + return stack[stackPtr]; + } + private void encode(short obj){ + push(obj); + } + public short encode(short object, byte[] buffer, short startOff) { + stackPtr = 0; this.buffer = buffer; this.startOff = startOff; - this.length = length; - encode(object); - this.length = this.startOff; - this.startOff = startOff; - return this.length; + short len = (short) buffer.length; + if((len <0) || (len > KMKeymasterApplet.MAX_LENGTH)){ + this.length = KMKeymasterApplet.MAX_LENGTH; + }else{ + this.length = (short)buffer.length; + } + //this.length = (short)(startOff + length); + push(object); + encode(); + return (short)(this.startOff - startOff); } - private void encode(KMType exp){ - if(exp instanceof KMByteBlob){ - encode((KMByteBlob) exp); - return; - } - if(exp instanceof KMEnum){ - encode((KMEnum) exp); - return; - } - if(exp instanceof KMInteger){ - encode((KMInteger)exp); - return; - } - if(exp instanceof KMArray){ - encode((KMArray)exp); - return; - } - if(exp instanceof KMVector){ - encode((KMVector)exp); - return; - } - if(exp instanceof KMByteTag){ - encode((KMByteTag)exp); - return; - } - if(exp instanceof KMBoolTag){ - encode((KMBoolTag) exp); - return; - } - if(exp instanceof KMIntegerTag){ - encode((KMIntegerTag)exp); - return; - } - if(exp instanceof KMIntegerArrayTag){ - encode((KMIntegerArrayTag)exp); - return; - } - if(exp instanceof KMEnumTag){ - encode((KMEnumTag) exp); - return; - } - if(exp instanceof KMEnumArrayTag){ - encode((KMEnumArrayTag) exp); - return; - } - if(exp instanceof KMKeyParameters){ - encode((KMKeyParameters) exp); - return; - } - if(exp instanceof KMKeyCharacteristics){ - encode((KMKeyCharacteristics) exp); - return; - } - if(exp instanceof KMVerificationToken){ - encode((KMVerificationToken) exp); - return; + + public short encodeError(short err, byte[] buffer, short startOff, short length) { + this.buffer = buffer; + this.startOff = startOff; + this.length = (short)(startOff + length); + // encode the err as UINT with value in err - should not be greater then 5 bytes. + if(err < UINT8_LENGTH){ + writeByte((byte)(UINT_TYPE | err )); + }else if(err < 0x100){ + writeByte((byte)(UINT_TYPE | UINT8_LENGTH)); + writeByte((byte)err); + }else { + writeByte((byte)(UINT_TYPE | UINT16_LENGTH)); + writeShort(err); } - if(exp instanceof KMHmacSharingParameters){ - encode((KMHmacSharingParameters) exp); - return; + return (short)(this.startOff - startOff); + } + + private void encode(){ + while (stackPtr > 0) { + short exp = pop(); + byte type = KMType.getType(exp); + switch (type) { + case KMType.BYTE_BLOB_TYPE: + encodeByteBlob(exp); + break; + case KMType.INTEGER_TYPE: + encodeInteger(exp); + break; + case KMType.ARRAY_TYPE: + encodeArray(exp); + break; + case KMType.ENUM_TYPE: + encodeEnum(exp); + break; + case KMType.KEY_PARAM_TYPE: + encodeKeyParam(exp); + break; + case KMType.KEY_CHAR_TYPE: + encodeKeyChar(exp); + break; + case KMType.VERIFICATION_TOKEN_TYPE: + encodeVeriToken(exp); + break; + case KMType.HMAC_SHARING_PARAM_TYPE: + encodeHmacSharingParam(exp); + break; + case KMType.HW_AUTH_TOKEN_TYPE: + encodeHwAuthToken(exp); + break; + case KMType.TAG_TYPE: + short tagType = KMTag.getTagType(exp); + encodeTag(tagType, exp); + break; + default: + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } } - if(exp instanceof KMHardwareAuthToken){ - encode((KMHardwareAuthToken) exp); - return; + } + private void encodeTag(short tagType, short exp){ + switch(tagType){ + case KMType.BYTES_TAG: + encodeBytesTag(exp); + return; + case KMType.BOOL_TAG: + encodeBoolTag(exp); + return; + case KMType.UINT_TAG: + case KMType.ULONG_TAG: + case KMType.DATE_TAG: + encodeIntegerTag(exp); + return; + case KMType.ULONG_ARRAY_TAG: + case KMType.UINT_ARRAY_TAG: + encodeIntegerArrayTag(exp); + return; + case KMType.ENUM_TAG: + encodeEnumTag(exp); + return; + case KMType.ENUM_ARRAY_TAG: + encodeEnumArrayTag(exp); + return; + default: + ISOException.throwIt(ISO7816.SW_DATA_INVALID); } - throw new KMException(ISO7816.SW_DATA_INVALID); } - private void encode(KMKeyParameters obj) { - encodeAsMap(obj.getVals()); + private void encodeKeyParam(short obj) { + encodeAsMap(KMKeyParameters.cast(obj).getVals()); } - private void encode(KMKeyCharacteristics obj) { - encode(obj.getVals()); + private void encodeKeyChar(short obj) { + encode(KMKeyCharacteristics.cast(obj).getVals()); } - private void encode(KMVerificationToken obj) { - encode(obj.getVals()); + private void encodeVeriToken(short obj) { + encode(KMVerificationToken.cast(obj).getVals()); } - private void encode(KMHardwareAuthToken obj) { - encode(obj.getVals()); + private void encodeHwAuthToken(short obj) { + encode(KMHardwareAuthToken.cast(obj).getVals()); } - private void encode(KMHmacSharingParameters obj) { - encode(obj.getVals()); + private void encodeHmacSharingParam(short obj) { + encode(KMHmacSharingParameters.cast(obj).getVals()); } - private void encode(KMArray obj) { - writeMajorTypeWithLength(ARRAY_TYPE, obj.length()); - short index = 0; - while(index < obj.length()){ - encode(obj.get(index)); - index++; + private void encodeArray(short obj) { + writeMajorTypeWithLength(ARRAY_TYPE, KMArray.cast(obj).length()); + short len = KMArray.cast(obj).length(); + short index = (short)(len-1); + while(index >= 0){ + encode(KMArray.cast(obj).get(index)); + index--; } } - private void encodeAsMap(KMArray obj){ - writeMajorTypeWithLength(MAP_TYPE, obj.length()); - short index = 0; - while(index < obj.length()){ - KMType t = obj.get(index); - encode(t); - //encode(obj.get(index)); - index++; + private void encodeAsMap(short obj){ + writeMajorTypeWithLength(MAP_TYPE, KMArray.cast(obj).length()); + short len = KMArray.cast(obj).length(); + short index = (short)(len-1); + short inst; + while(index >= 0){ + inst = KMArray.cast(obj).get(index); + encode(inst); + index--; } } - private void encode(KMVector obj){ - writeMajorTypeWithLength(ARRAY_TYPE, obj.length()); - short index = 0; - while(index 0){ + if(val[(short)(startOff + index)] > 0){ + break; + }else if(val[(short)(startOff + index)] < 0){ break; } index++; // index will be equal to len if value is 0. @@ -221,34 +261,36 @@ private void encode(KMInteger obj) { short diff = (short)(len - index); if(diff == 0){ writeByte((byte)(UINT_TYPE | 0)); - }else if((diff == 1) && val[index] < UINT8_LENGTH){ - writeByte((byte)(UINT_TYPE | val[index])); + }else if((diff == 1) && (val[(short)(startOff + index)] < UINT8_LENGTH) + &&(val[(short)(startOff + index)] >= 0)){ + writeByte((byte)(UINT_TYPE | val[(short)(startOff + index)])); }else if (diff == 1){ writeByte((byte)(UINT_TYPE | UINT8_LENGTH)); - writeByte(val[index]); + writeByte(val[(short)(startOff + index)]); }else if(diff == 2){ writeByte((byte)(UINT_TYPE | UINT16_LENGTH)); - writeBytes(val, index, (short)2); + writeBytes(val, (short)(startOff + index), (short)2); }else if(diff <= 4){ writeByte((byte)(UINT_TYPE | UINT32_LENGTH)); - writeBytes(val, (short)(len - 4), (short)4); + writeBytes(val, (short)(startOff + len - 4), (short)4); }else { writeByte((byte)(UINT_TYPE | UINT64_LENGTH)); - writeBytes(val, (short)0, (short)8); + writeBytes(val, startOff, (short)8); } } - private void encode(KMByteBlob obj) { - writeMajorTypeWithLength(BYTES_TYPE, obj.length()); - writeBytes(obj.getVal(), obj.getStartOff(), obj.length()); + private void encodeByteBlob(short obj) { + writeMajorTypeWithLength(BYTES_TYPE, KMByteBlob.cast(obj).length()); + writeBytes(KMByteBlob.cast(obj).getBuffer(), KMByteBlob.cast(obj).getStartOff(), + KMByteBlob.cast(obj).length()); } private void writeByteValue(byte val){ - if(val < UINT8_LENGTH){ + if((val < UINT8_LENGTH) && (val >=0)){ writeByte((byte)(UINT_TYPE | val)); }else{ writeByte((byte)(UINT_TYPE | UINT8_LENGTH)); - writeByte(val); + writeByte((byte)val); } } @@ -257,7 +299,6 @@ private void writeTag(short tagType, short tagKey){ writeShort(tagType); writeShort(tagKey); } - // TODO bug here private void writeMajorTypeWithLength(byte majorType, short len) { if(len <= TINY_PAYLOAD){ writeByte((byte)(majorType | (byte) (len & ADDITIONAL_MASK))); @@ -288,7 +329,7 @@ private void writeByte(byte val){ private void incrementStartOff(short inc){ startOff += inc; if (startOff >= this.length) { - throw new KMException(ISO7816.SW_DATA_INVALID); + ISOException.throwIt(ISO7816.SW_DATA_INVALID); } } } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMEnum.java b/Applet/Applet/src/com/android/javacard/keymaster/KMEnum.java index 7dad1d27..d5a69270 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMEnum.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMEnum.java @@ -17,45 +17,65 @@ package com.android.javacard.keymaster; import javacard.framework.ISO7816; +import javacard.framework.ISOException; +import javacard.framework.Util; public class KMEnum extends KMType { - private static short[] types = {HARDWARE_TYPE, KEY_FORMAT, KEY_DERIVATION_FUNCTION}; + private static KMEnum prototype; + private static short instPtr; + + private static short[] types = {HARDWARE_TYPE, KEY_FORMAT, KEY_DERIVATION_FUNCTION, + VERIFIED_BOOT_STATE, DEVICE_LOCKED, USER_AUTH_TYPE, PURPOSE}; private static Object[] enums = null; - private short type; - private byte val; + private KMEnum() {} - private KMEnum() { - init(); + private static KMEnum proto(short ptr) { + if (prototype == null) prototype = new KMEnum(); + instPtr = ptr; + return prototype; } - @Override - public void init() { - type = 0; - val = 0; + // pointer to an empty instance used as expression + public static short exp() { + return KMType.exp(ENUM_TYPE); } - @Override + public short length() { - return 1; + return Util.getShort(heap, (short) (instPtr + 1)); + } + + // cast the ptr to KMByteBlob + public static KMEnum cast(short ptr) { + if (heap[ptr] != ENUM_TYPE) ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + if (Util.getShort(heap, (short) (ptr + 1)) == INVALID_VALUE) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + return proto(ptr); } - public static KMEnum instance() { - return repository.newEnum(); + public static short instance(short enumType) { + if (!validateEnum(enumType, NO_VALUE)) { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + short ptr = KMType.instance(ENUM_TYPE, (short)2); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE), enumType); + return ptr; } - public static KMEnum instance(short enumType, byte val) { - KMEnum inst = repository.newEnum(); + public static short instance(short enumType, byte val) { if (!validateEnum(enumType, val)) { - throw new KMException(ISO7816.SW_DATA_INVALID); + ISOException.throwIt(ISO7816.SW_DATA_INVALID); } - inst.type = enumType; - inst.val = val; - return inst; + short ptr = KMType.instance(ENUM_TYPE, (short)3); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE), enumType); + heap[(short)(ptr+TLV_HEADER_SIZE+2)] = val; + return ptr; } - public static void create(KMEnum[] enumRefTable) { + private static void create() { if (enums == null) { enums = new Object[] { @@ -68,35 +88,36 @@ public static void create(KMEnum[] enumRefTable) { ISO18033_2_KDF1_SHA256, ISO18033_2_KDF2_SHA1, ISO18033_2_KDF2_SHA256 - } + }, + new byte[] {SELF_SIGNED_BOOT, VERIFIED_BOOT}, + new byte[] {DEVICE_LOCKED_TRUE, DEVICE_LOCKED_FALSE}, + new byte[] {USER_AUTH_NONE,PASSWORD,FINGERPRINT, BOTH}, + new byte[] {ENCRYPT, DECRYPT, SIGN, VERIFY, WRAP_KEY, ATTEST_KEY} }; } - byte index = 0; - while (index < enumRefTable.length) { - enumRefTable[index] = new KMEnum(); - index++; - } } - public KMEnum setVal(byte val) { - this.val = val; - return this; + public void setVal(byte val) { + heap[(short)(instPtr+TLV_HEADER_SIZE+2)] = val; } public byte getVal() { - return val; + return heap[(short)(instPtr+TLV_HEADER_SIZE+2)]; } - public KMEnum setType(short type) { - this.type = type; - return this; + public void setEnumType(short type) { + Util.setShort(heap, (short)(instPtr+TLV_HEADER_SIZE),type); } - public short getType() { - return type; + public short getEnumType() { + return Util.getShort(heap, (short)(instPtr+TLV_HEADER_SIZE)); } - // validate enumeration keys and values. + + // isValidTag enumeration keys and values. private static boolean validateEnum(short key, byte value) { + create(); + byte[] vals; + short enumInd; // check if key exists short index = (short) types.length; while (--index >= 0) { @@ -104,8 +125,8 @@ private static boolean validateEnum(short key, byte value) { // check if value given if (value != NO_VALUE) { // check if the value exist - byte[] vals = (byte[]) enums[index]; - short enumInd = (short) vals.length; + vals = (byte[]) enums[index]; + enumInd = (short) vals.length; while (--enumInd >= 0) { if (vals[enumInd] == value) { // return true if value exist @@ -122,4 +143,5 @@ private static boolean validateEnum(short key, byte value) { // return false if key does not exist return false; } + } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMEnumArrayTag.java b/Applet/Applet/src/com/android/javacard/keymaster/KMEnumArrayTag.java index 9c76ce66..1b1a56ee 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMEnumArrayTag.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMEnumArrayTag.java @@ -17,90 +17,119 @@ package com.android.javacard.keymaster; import javacard.framework.ISO7816; +import javacard.framework.ISOException; +import javacard.framework.Util; public class KMEnumArrayTag extends KMTag { + private static KMEnumArrayTag prototype; + private static short instPtr; + // Arrays given below, together they form multi dimensional array. // Tag private static short[] tags = {PURPOSE, BLOCK_MODE, DIGEST, PADDING}; // Tag Values. private static Object[] enums = null; - // Tag Key - private short key; - // Byte Array of Tag Values. - private KMByteBlob array; - // assignBlob constructor - private KMEnumArrayTag() { - init(); + private KMEnumArrayTag() {} + + private static KMEnumArrayTag proto(short ptr) { + if (prototype == null) prototype = new KMEnumArrayTag(); + instPtr = ptr; + return prototype; + } + + // pointer to an empty instance used as expression + public static short exp() { + short blobPtr = KMByteBlob.exp(); + short ptr = instance(TAG_TYPE, (short)6); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE), ENUM_ARRAY_TAG); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE+2), INVALID_TAG); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE+4), blobPtr); + return ptr; + } + + public static short instance(short key) { + byte[] vals = getAllowedEnumValues(key); + if (vals == null) { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + short blobPtr = KMByteBlob.exp(); + return instance(key, blobPtr); + } + + public static short instance(short key, short byteBlob) { + byte[] allowedVals = getAllowedEnumValues(key); + if (allowedVals == null) { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + KMByteBlob blob = KMByteBlob.cast(byteBlob); + short byteIndex = 0; + short enumIndex; + boolean validValue; + while (byteIndex < blob.length()) { + enumIndex = 0; + validValue = false; + while (enumIndex < allowedVals.length) { + if (blob.get(byteIndex) == allowedVals[enumIndex]) { + validValue = true; + break; + } + enumIndex++; + } + if (!validValue) { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + byteIndex++; + } + short ptr = instance(TAG_TYPE, (short)6); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE), ENUM_ARRAY_TAG); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE+2), key); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE+4), byteBlob); + return ptr; } - @Override - public void init() { - key = 0; - array = null; + public static KMEnumArrayTag cast(short ptr) { + if (heap[ptr] != TAG_TYPE) ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + if (Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE)) != ENUM_ARRAY_TAG) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + return proto(ptr); } - @Override public short getKey() { - return key; + return Util.getShort(heap, (short)(instPtr+TLV_HEADER_SIZE+2)); } - @Override public short getTagType() { return KMType.ENUM_ARRAY_TAG; } - // returns the length - @Override - public short length() { - return array.length(); + + public short getValues() { + return Util.getShort(heap, (short)(instPtr+TLV_HEADER_SIZE+4)); } - public static KMEnumArrayTag instance() { - return repository.newEnumArrayTag(); + public short length() { + short blobPtr = Util.getShort(heap, (short)(instPtr+TLV_HEADER_SIZE+4)); + return KMByteBlob.cast(blobPtr).length(); } - public static void create(KMEnumArrayTag[] enumArrayTagRefTable) { + public static void create() { if (enums == null) { enums = new Object[] { new byte[] {ENCRYPT, DECRYPT, SIGN, VERIFY, WRAP_KEY, ATTEST_KEY}, - new byte[] {ECB, CBC, CTR}, + new byte[] {ECB, CBC, CTR, GCM}, new byte[] {DIGEST_NONE, MD5, SHA1, SHA2_224, SHA2_256, SHA2_384, SHA2_512}, new byte[] { PADDING_NONE, RSA_OAEP, RSA_PSS, RSA_PKCS1_1_5_ENCRYPT, RSA_PKCS1_1_5_SIGN, PKCS7 } }; } - byte index = 0; - while (index < enumArrayTagRefTable.length) { - enumArrayTagRefTable[index] = new KMEnumArrayTag(); - index++; - } - } - - // create default assignBlob without any value array - public static KMEnumArrayTag instance(short key) { - // check if key is valid. - byte[] vals = getAllowedEnumValues(key); - if (vals == null) { - throw new KMException(ISO7816.SW_DATA_INVALID); - } - KMEnumArrayTag tag = repository.newEnumArrayTag(); - tag.key = key; - tag.array = null; - return tag; } - // Set the expected length for the prototype. - public KMEnumArrayTag withLength(short length) { - array.withLength(length); - return this; - } - - // get the allowed enum values for given tag key private static byte[] getAllowedEnumValues(short key) { - // check if key is allowed + create(); short index = (short) tags.length; while (--index >= 0) { if (tags[index] == key) { @@ -110,41 +139,50 @@ private static byte[] getAllowedEnumValues(short key) { return null; } - // get value array of this tag assignBlob. - public KMByteBlob getValues() { - return this.array; + public static short getValues(short tagId, short params, byte[] buf, short start) { + short tag = + KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, tagId, params); + if (tag == KMType.INVALID_VALUE) { + return KMType.INVALID_VALUE; + } + tag = KMEnumArrayTag.cast(tag).getValues(); + return KMByteBlob.cast(tag).getValues(buf, start); } - public KMEnumArrayTag setValues(KMByteBlob val) { - this.array = val; - return this; + public short get(short index){ + return KMByteBlob.cast(getValues()).get(index); } - // instantiate enum array pointing to existing array. - public static KMEnumArrayTag instance(short key, KMByteBlob blob) { - // validate key - byte[] allowedVals = getAllowedEnumValues(key); - if (allowedVals == null) { - throw new KMException(ISO7816.SW_DATA_INVALID); - } - short byteIndex = 0; - while (byteIndex < blob.length()) { - short enumIndex = 0; - boolean validValue = false; - while (enumIndex < allowedVals.length) { - if (blob.get(byteIndex) == allowedVals[enumIndex]) { - validValue = true; - break; + + public static boolean contains(short tagId, short tagValue, short params) { + short tag = + KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, tagId, params); + if (tag != KMType.INVALID_VALUE) { + short index = 0; + while (index < KMEnumArrayTag.cast(tag).length()) { + if (tagValue == KMEnumArrayTag.cast(tag).get(index)) { + return true; } - enumIndex++; + index++; } - if (!validValue) { - throw new KMException(ISO7816.SW_DATA_INVALID); + } + return false; + } + public static short length(short tagId, short params) { + short tag = + KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, tagId, params); + if (tag != KMType.INVALID_VALUE) { + return KMEnumArrayTag.cast(tag).length(); + } + return KMType.INVALID_VALUE; + } + public boolean contains(short tagValue){ + short index = 0; + while(index < length()){ + if(get(index) == (byte)tagValue){ + return true; } - byteIndex++; + index++; } - KMEnumArrayTag tag = repository.newEnumArrayTag(); - tag.key = key; - tag.array = blob; - return tag; + return false; } } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMEnumTag.java b/Applet/Applet/src/com/android/javacard/keymaster/KMEnumTag.java index 3cfa840e..b75563d6 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMEnumTag.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMEnumTag.java @@ -17,8 +17,12 @@ package com.android.javacard.keymaster; import javacard.framework.ISO7816; +import javacard.framework.ISOException; +import javacard.framework.Util; public class KMEnumTag extends KMTag { + private static KMEnumTag prototype; + private static short instPtr; private static short[] tags = { ALGORITHM, ECCURVE, BLOB_USAGE_REQ, USER_AUTH_TYPE, ORIGIN, HARDWARE_TYPE @@ -26,70 +30,81 @@ public class KMEnumTag extends KMTag { private static Object[] enums = null; - private short key; - private byte val; + private KMEnumTag() {} - // assignBlob constructor - private KMEnumTag() { - init(); + private static KMEnumTag proto(short ptr) { + if (prototype == null) prototype = new KMEnumTag(); + instPtr = ptr; + return prototype; } - @Override - public void init() { - key = 0; - val = 0; + // pointer to an empty instance used as expression + public static short exp() { + short ptr = instance(TAG_TYPE, (short)2); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE), ENUM_TAG); + return ptr; } - @Override - public short getKey() { - return key; + public static short instance(short key) { + if(!validateEnum(key, NO_VALUE)){ + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + short ptr = KMType.instance(TAG_TYPE, (short)4); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE), ENUM_TAG); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE+2), key); + return ptr; } - @Override - public short length() { - return 1; + public static short instance(short key, byte val) { + if(!validateEnum(key, val)){ + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + short ptr = instance(TAG_TYPE, (short)5); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE), ENUM_TAG); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE+2), key); + heap[(short)(ptr+TLV_HEADER_SIZE+4)]= val; + return ptr; } - @Override - public short getTagType() { - return KMType.ENUM_TAG; + public static KMEnumTag cast(short ptr) { + if (heap[ptr] != TAG_TYPE) ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + if (Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE)) != ENUM_TAG) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + return proto(ptr); } - public static KMEnumTag instance() { - return repository.newEnumTag(); + public short getKey() { + return Util.getShort(heap, (short)(instPtr+TLV_HEADER_SIZE+2)); } - public static KMEnumTag instance(short key) { - if (validateEnum(key, NO_VALUE)) { - KMEnumTag tag = repository.newEnumTag(); - tag.key = key; - return tag; - } else { - throw new KMException(ISO7816.SW_DATA_INVALID); - } + public short getTagType() { + return KMType.ENUM_TAG; + } + + public byte getValue() { + return heap[(short)(instPtr+TLV_HEADER_SIZE+4)]; } - public static void create(KMEnumTag[] enumTagRefTable) { + public static void create() { if (enums == null) { enums = new Object[] { new byte[] {RSA, DES, EC, AES, HMAC}, new byte[] {P_224, P_256, P_384, P_521}, new byte[] {STANDALONE, REQUIRES_FILE_SYSTEM}, - new byte[] {USER_AUTH_NONE, PASSWORD, FINGERPRINT, ANY}, + new byte[] {USER_AUTH_NONE, PASSWORD, FINGERPRINT, (byte)(PASSWORD & FINGERPRINT),ANY}, new byte[] {GENERATED, DERIVED, IMPORTED, UNKNOWN, SECURELY_IMPORTED}, new byte[] {SOFTWARE, TRUSTED_ENVIRONMENT, STRONGBOX} }; } - byte index = 0; - while (index < enumTagRefTable.length) { - enumTagRefTable[index] = new KMEnumTag(); - index++; - } } - // validate enumeration keys and values. + // isValidTag enumeration keys and values. private static boolean validateEnum(short key, byte value) { + create(); + byte[] vals; + short enumInd; // check if key exists short index = (short) tags.length; while (--index >= 0) { @@ -97,8 +112,8 @@ private static boolean validateEnum(short key, byte value) { // check if value given if (value != NO_VALUE) { // check if the value exist - byte[] vals = (byte[]) enums[index]; - short enumInd = (short) vals.length; + vals = (byte[]) enums[index]; + enumInd = (short) vals.length; while (--enumInd >= 0) { if (vals[enumInd] == value) { // return true if value exist @@ -116,18 +131,11 @@ private static boolean validateEnum(short key, byte value) { return false; } - // get value of this tag assignBlob. - public byte getValue() { - return val; - } - - // instantiate enum tag. - public static KMEnumTag instance(short key, byte val) { - if (!validateEnum(key, val)) { - throw new KMException(ISO7816.SW_DATA_INVALID); + public static short getValue(short tagType, short keyParameters){ + short tagPtr = KMKeyParameters.findTag(KMType.ENUM_TAG, tagType, keyParameters); + if(tagPtr != KMType.INVALID_VALUE){ + return heap[(short)(tagPtr+TLV_HEADER_SIZE+4)]; } - KMEnumTag tag = instance(key); - tag.val = val; - return tag; + return KMType.INVALID_VALUE; } } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMError.java b/Applet/Applet/src/com/android/javacard/keymaster/KMError.java new file mode 100644 index 00000000..25557d27 --- /dev/null +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMError.java @@ -0,0 +1,78 @@ +package com.android.javacard.keymaster; + +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; /** For PKCS8 & PKCS12 */ + public static short UNSUPPORTED_KEY_VERIFICATION_ALGORITHM = 20; /** For PKCS8 & PKCS12 */ + public static 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 UNIMPLEMENTED = 100; + public static short VERSION_MISMATCH = 101; + public static short UNKNOWN_ERROR = 1000; +} diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMException.java b/Applet/Applet/src/com/android/javacard/keymaster/KMException.java index 3e357613..1805a911 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMException.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMException.java @@ -16,14 +16,22 @@ package com.android.javacard.keymaster; -import javacard.framework.ISOException; - -public class KMException extends ISOException { +public class KMException extends RuntimeException { + public static short reason; + public static KMException exception; + private KMException(){ + } + public static void throwIt(short reason){ + KMException.reason = reason; + throw instance(); + } + public static KMException instance(){ + if(exception == null ) exception = new KMException(); + return exception; + } - // The Applet is not in a correct state in order to execute the command. - public static final short CMD_NOT_ACCEPTED_WRONG_STATE = (short) 0x6901; - public KMException(short i) { - super(i); + public void clear(){ + reason = KMError.UNKNOWN_ERROR; } } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMExportKeyCmd.java b/Applet/Applet/src/com/android/javacard/keymaster/KMExportKeyCmd.java deleted file mode 100644 index 05d28ef6..00000000 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMExportKeyCmd.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * 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; - -public class KMExportKeyCmd extends KMAbstractCmd { - public static final byte INS_EXPORT_KEY_CMD = 0x13; - - @Override - protected KMArray getExpectedArgs() { - return null; - } - - @Override - protected KMArray process(KMArray args, KMContext context) { - return null; - } - - @Override - public byte getIns() { - return INS_EXPORT_KEY_CMD; - } -} diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMFinishOperationCmd.java b/Applet/Applet/src/com/android/javacard/keymaster/KMFinishOperationCmd.java deleted file mode 100644 index 0267e4fb..00000000 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMFinishOperationCmd.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * 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; - -public class KMFinishOperationCmd extends KMAbstractCmd { - public static final byte INS_FINISH_OPERATION_CMD = 0x21; - - @Override - protected KMArray getExpectedArgs() { - return null; - } - - @Override - protected KMArray process(KMArray args, KMContext context) { - return null; - } - - @Override - public byte getIns() { - return INS_FINISH_OPERATION_CMD; - } -} diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMGenerateKeyCmd.java b/Applet/Applet/src/com/android/javacard/keymaster/KMGenerateKeyCmd.java deleted file mode 100644 index 0c2d69d1..00000000 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMGenerateKeyCmd.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * 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; - -public class KMGenerateKeyCmd extends KMAbstractCmd { - public static final byte INS_GENERATE_KEY_CMD = 0x10; - - @Override - protected KMArray getExpectedArgs() { - return null; - } - - @Override - protected KMArray process(KMArray args, KMContext context) { - return null; - } - - @Override - public byte getIns() { - return INS_GENERATE_KEY_CMD; - } -} diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMGetHWInfoCmd.java b/Applet/Applet/src/com/android/javacard/keymaster/KMGetHWInfoCmd.java deleted file mode 100644 index 3f6cb8f8..00000000 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMGetHWInfoCmd.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * 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; - -public class KMGetHWInfoCmd extends KMAbstractCmd { - public static final byte INS_GET_HW_INFO_CMD = 0x1E; - public static final byte[] JavacardKeymasterDevice = { - 0x4A, 0x61, 0x76, 0x61, 0x63, 0x61, 0x72, 0x64, 0x4B, 0x65, 0x79, 0x6D, 0x61, 0x73, 0x74, 0x65, - 0x72, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, - }; - public static final byte[] Google = {0x47, 0x6F, 0x6F, 0x67, 0x6C, 0x65}; - - @Override - protected KMArray getExpectedArgs() { - return null; - } - - @Override - protected KMArray process(KMArray args, KMContext context) { - return KMArray.instance((short) 3) - .add((short) 0, KMEnum.instance(KMType.HARDWARE_TYPE, KMType.STRONGBOX)) - .add( - (short) 1, - KMByteBlob.instance( - JavacardKeymasterDevice, (short) 0, (short) JavacardKeymasterDevice.length)) - .add((short) 2, KMByteBlob.instance(Google, (short) 0, (short) Google.length)); - } - - @Override - public byte getIns() { - return INS_GET_HW_INFO_CMD; - } - - @Override - public boolean hasArguments() { - return false; - } -} diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMGetHmacSharingParametersCmd.java b/Applet/Applet/src/com/android/javacard/keymaster/KMGetHmacSharingParametersCmd.java deleted file mode 100644 index 19165531..00000000 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMGetHmacSharingParametersCmd.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * 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; - -public class KMGetHmacSharingParametersCmd extends KMAbstractCmd { - public static final byte INS_GET_HMAC_SHARING_PARAM_CMD = 0x1C; - - @Override - protected KMArray getExpectedArgs() { - return null; - } - - @Override - protected KMArray process(KMArray args, KMContext context) { - return null; - } - - @Override - public byte getIns() { - return INS_GET_HMAC_SHARING_PARAM_CMD; - } -} diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMGetKeyCharacteristicsCmd.java b/Applet/Applet/src/com/android/javacard/keymaster/KMGetKeyCharacteristicsCmd.java deleted file mode 100644 index 20fef22b..00000000 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMGetKeyCharacteristicsCmd.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * 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; - -public class KMGetKeyCharacteristicsCmd extends KMAbstractCmd { - public static final byte INS_GET_KEY_CHARACTERISTICS_CMD = 0x1D; - - @Override - protected KMArray getExpectedArgs() { - return null; - } - - @Override - protected KMArray process(KMArray args, KMContext context) { - return null; - } - - @Override - public byte getIns() { - return INS_GET_KEY_CHARACTERISTICS_CMD; - } -} diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMHardwareAuthToken.java b/Applet/Applet/src/com/android/javacard/keymaster/KMHardwareAuthToken.java index ca3c2804..23245352 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMHardwareAuthToken.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMHardwareAuthToken.java @@ -17,6 +17,8 @@ package com.android.javacard.keymaster; import javacard.framework.ISO7816; +import javacard.framework.ISOException; +import javacard.framework.Util; public class KMHardwareAuthToken extends KMType { public static final byte CHALLENGE = 0x00; @@ -26,76 +28,121 @@ public class KMHardwareAuthToken extends KMType { public static final byte TIMESTAMP = 0x04; public static final byte MAC = 0x05; - private KMArray vals; + private static KMHardwareAuthToken prototype; + private static short instPtr; + + private KMHardwareAuthToken() {} + + public static short exp() { + short arrPtr = KMArray.instance((short)6); + KMArray arr = KMArray.cast(arrPtr); + arr.add(CHALLENGE, KMInteger.exp()); + arr.add(USER_ID, KMInteger.exp()); + arr.add(AUTHENTICATOR_ID, KMInteger.exp()); + arr.add(HW_AUTHENTICATOR_TYPE, KMEnum.instance(KMType.USER_AUTH_TYPE)); + arr.add(TIMESTAMP, KMInteger.exp()); + arr.add(MAC, KMByteBlob.exp()); + return instance(arrPtr); + } + + private static KMHardwareAuthToken proto(short ptr) { + if (prototype == null) prototype = new KMHardwareAuthToken(); + instPtr = ptr; + return prototype; + } - private KMHardwareAuthToken() { - init(); + public static short instance() { + short arrPtr = KMArray.instance((short)6); + return instance(arrPtr); } - @Override - public void init() { - vals = null; + public static short instance(short vals) { + KMArray arr = KMArray.cast(vals); + if(arr.length() != 6)ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); + short ptr = KMType.instance(HW_AUTH_TOKEN_TYPE, (short)2); + Util.setShort(heap, (short)(ptr + TLV_HEADER_SIZE), vals); + return ptr; + } + + public static KMHardwareAuthToken cast(short ptr) { + if (heap[ptr] != HW_AUTH_TOKEN_TYPE) ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + short arrPtr = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE)); + if(heap[arrPtr] != ARRAY_TYPE) ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + return proto(ptr); + } + + public short getVals() { + return Util.getShort(heap, (short) (instPtr + TLV_HEADER_SIZE)); } - @Override public short length() { - return vals.length(); + short arrPtr = getVals(); + return KMArray.cast(arrPtr).length(); + } + + public short getChallenge() { + short arrPtr = getVals(); + return KMArray.cast(arrPtr).get(CHALLENGE); + } + + public void setChallenge(short vals) { + KMInteger.cast(vals); + short arrPtr = getVals(); + KMArray.cast(arrPtr).add(CHALLENGE, vals); } - public static KMHardwareAuthToken instance() { - KMHardwareAuthToken inst = repository.newHwAuthToken(); - inst.vals = KMArray.instance((short) 6); - inst.vals.add(CHALLENGE, KMInteger.instance()); - inst.vals.add(USER_ID, KMInteger.instance()); - inst.vals.add(AUTHENTICATOR_ID, KMInteger.instance()); - inst.vals.add(HW_AUTHENTICATOR_TYPE, KMEnumTag.instance(KMType.USER_AUTH_TYPE)); - inst.vals.add(TIMESTAMP, KMInteger.instance()); - inst.vals.add(MAC, KMByteBlob.instance()); - return inst; + public short getUserId() { + short arrPtr = getVals(); + return KMArray.cast(arrPtr).get(USER_ID); } - public static KMHardwareAuthToken instance(KMArray vals) { - if (vals.length() != 6) { - throw new KMException(ISO7816.SW_WRONG_LENGTH); - } - KMHardwareAuthToken inst = repository.newHwAuthToken(); - inst.vals = vals; - return inst; + public void setUserId(short vals) { + KMInteger.cast(vals); + short arrPtr = getVals(); + KMArray.cast(arrPtr).add(USER_ID, vals); } - public static void create(KMHardwareAuthToken[] hwAuthTokenRefTable) { - byte index = 0; - while (index < hwAuthTokenRefTable.length) { - hwAuthTokenRefTable[index] = new KMHardwareAuthToken(); - index++; - } + public short getAuthenticatorId() { + short arrPtr = getVals(); + return KMArray.cast(arrPtr).get(AUTHENTICATOR_ID); } - public KMInteger getChallenge() { - return (KMInteger) vals.get(CHALLENGE); + public void setAuthenticatorId(short vals) { + KMInteger.cast(vals); + short arrPtr = getVals(); + KMArray.cast(arrPtr).add(AUTHENTICATOR_ID, vals); } - public KMInteger getUserId() { - return (KMInteger) vals.get(USER_ID); + public short getHwAuthenticatorType() { + short arrPtr = getVals(); + return KMArray.cast(arrPtr).get(HW_AUTHENTICATOR_TYPE); } - public KMInteger getAuthenticatorId() { - return (KMInteger) vals.get(AUTHENTICATOR_ID); + public void setHwAuthenticatorType(short vals) { + KMEnum.cast(vals); + short arrPtr = getVals(); + KMArray.cast(arrPtr).add(HW_AUTHENTICATOR_TYPE, vals); } - public byte getHwAuthenticatorType() { - return ((KMEnumTag) vals.get(HW_AUTHENTICATOR_TYPE)).getValue(); + public short getTimestamp() { + short arrPtr = getVals(); + return KMArray.cast(arrPtr).get(TIMESTAMP); } - public KMInteger getTimestamp() { - return (KMInteger) vals.get(TIMESTAMP); + public void setTimestamp(short vals) { + KMInteger.cast(vals); + short arrPtr = getVals(); + KMArray.cast(arrPtr).add(TIMESTAMP, vals); } - public KMByteBlob getMac() { - return (KMByteBlob) vals.get(MAC); + public short getMac() { + short arrPtr = getVals(); + return KMArray.cast(arrPtr).get(MAC); } - public KMArray getVals() { - return vals; + public void setMac(short vals) { + KMByteBlob.cast(vals); + short arrPtr = getVals(); + KMArray.cast(arrPtr).add(MAC, vals); } } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMHmacSharingParameters.java b/Applet/Applet/src/com/android/javacard/keymaster/KMHmacSharingParameters.java index ef08696b..bc34c217 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMHmacSharingParameters.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMHmacSharingParameters.java @@ -17,60 +17,79 @@ package com.android.javacard.keymaster; import javacard.framework.ISO7816; +import javacard.framework.ISOException; +import javacard.framework.Util; public class KMHmacSharingParameters extends KMType { public static final byte SEED = 0x00; public static final byte NONCE = 0x01; - private KMArray vals; - private KMHmacSharingParameters() { - init(); + private static KMHmacSharingParameters prototype; + private static short instPtr; + + private KMHmacSharingParameters() {} + + public static short exp() { + short arrPtr = KMArray.instance((short)2); + KMArray arr = KMArray.cast(arrPtr); + arr.add(SEED, KMByteBlob.exp()); + arr.add(NONCE, KMByteBlob.exp()); + return instance(arrPtr); } - @Override - public void init() { - vals = null; + private static KMHmacSharingParameters proto(short ptr) { + if (prototype == null) prototype = new KMHmacSharingParameters(); + instPtr = ptr; + return prototype; } - @Override - public short length() { - return vals.length(); + public static short instance() { + short arrPtr = KMArray.instance((short)2); + return instance(arrPtr); + } + + public static short instance(short vals) { + short ptr = KMType.instance(HMAC_SHARING_PARAM_TYPE, (short)2); + if(KMArray.cast(vals).length() != 2) ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); + Util.setShort(heap, (short)(ptr + TLV_HEADER_SIZE), vals); + return ptr; + } + + public static KMHmacSharingParameters cast(short ptr) { + if (heap[ptr] != HMAC_SHARING_PARAM_TYPE) ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + short arrPtr = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE)); + if(heap[arrPtr] != ARRAY_TYPE) ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + return proto(ptr); } - public static KMHmacSharingParameters instance() { - KMHmacSharingParameters inst = repository.newHmacSharingParameters(); - inst.vals = KMArray.instance((short) 2); - inst.vals.add(SEED, KMByteBlob.instance()); - inst.vals.add(NONCE, KMByteBlob.instance()); - return inst; + public short getVals() { + return Util.getShort(heap, (short) (instPtr + TLV_HEADER_SIZE)); } - public static KMHmacSharingParameters instance(KMArray vals) { - if (vals.length() != 2) { - throw new KMException(ISO7816.SW_WRONG_LENGTH); - } - KMHmacSharingParameters inst = repository.newHmacSharingParameters(); - inst.vals = vals; - return inst; + public short length() { + short arrPtr = getVals(); + return KMArray.cast(arrPtr).length(); } - public static void create(KMHmacSharingParameters[] hmacSharingParamsRefTable) { - byte index = 0; - while (index < hmacSharingParamsRefTable.length) { - hmacSharingParamsRefTable[index] = new KMHmacSharingParameters(); - index++; - } + public void setSeed(short vals) { + KMByteBlob.cast(vals); + short arrPtr = getVals(); + KMArray.cast(arrPtr).add(SEED, vals); } - public KMByteBlob getSeed() { - return (KMByteBlob) vals.get(SEED); + public void setNonce(short vals) { + KMByteBlob.cast(vals); + short arrPtr = getVals(); + KMArray.cast(arrPtr).add(NONCE, vals); } - public KMByteBlob getNonce() { - return (KMByteBlob) vals.get(NONCE); + public short getNonce() { + short arrPtr = getVals(); + return KMArray.cast(arrPtr).get(NONCE); } - public KMArray getVals() { - return vals; + public short getSeed() { + short arrPtr = getVals(); + return KMArray.cast(arrPtr).get(SEED); } } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMImportKeyCmd.java b/Applet/Applet/src/com/android/javacard/keymaster/KMImportKeyCmd.java deleted file mode 100644 index 6be0d0d7..00000000 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMImportKeyCmd.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * 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; - -public class KMImportKeyCmd extends KMAbstractCmd { - public static final byte INS_IMPORT_KEY_CMD = 0x11; - @Override - protected KMArray getExpectedArgs() { - return null; - } - - @Override - protected KMArray process(KMArray args, KMContext context) { - return null; - } - - @Override - public byte getIns() { - return INS_IMPORT_KEY_CMD; - } -} diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMImportWrappedKeyCmd.java b/Applet/Applet/src/com/android/javacard/keymaster/KMImportWrappedKeyCmd.java deleted file mode 100644 index e9896748..00000000 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMImportWrappedKeyCmd.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * 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; - -public class KMImportWrappedKeyCmd extends KMAbstractCmd { - public static final byte INS_IMPORT_WRAPPED_KEY_CMD = 0x12; - @Override - protected KMArray getExpectedArgs() { - return null; - } - - @Override - protected KMArray process(KMArray args, KMContext context) { - return null; - } - - @Override - public byte getIns() { - return INS_IMPORT_WRAPPED_KEY_CMD; - } -} diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMInteger.java b/Applet/Applet/src/com/android/javacard/keymaster/KMInteger.java index 19996d18..6fff9f04 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMInteger.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMInteger.java @@ -17,116 +17,145 @@ package com.android.javacard.keymaster; import javacard.framework.ISO7816; +import javacard.framework.ISOException; +import javacard.framework.JCSystem; import javacard.framework.Util; // Represents 8 bit, 16 bit, 32 bit and 64 bit integers public class KMInteger extends KMType { - private byte[] val; + public static final short UINT_32 = 4; + public static final short UINT_64 = 8; + private static KMInteger prototype; + private static short instPtr; - private KMInteger() { - init(); + private KMInteger() {} + + private static KMInteger proto(short ptr) { + if (prototype == null) prototype = new KMInteger(); + instPtr = ptr; + return prototype; } - @Override - public void init() { - val = null; + public static short exp() { + return KMType.exp(INTEGER_TYPE); } - @Override - public short length() { - return (short) this.val.length; + // return an empty integer instance + public static short instance(short length) { + if ((length <= 0) || (length > 8)) ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); + if (length > 4) { + length = UINT_64; + } else { + length = UINT_32; + } + return KMType.instance(INTEGER_TYPE, length); } - public static KMInteger instance() { - return repository.newInteger(); + public static short instance(byte[] num, short srcOff, short length) { + if (length > 8) { + ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); + } + if (length == 1) { + return uint_8(num[srcOff]); + } else if (length == 2) { + return uint_16(Util.getShort(num, srcOff)); + } else if (length == 4) { + return uint_32(num, srcOff); + } else { + return uint_64(num, srcOff); + } + } + + public static KMInteger cast(short ptr) { + byte[] heap = repository.getHeap(); + if (heap[ptr] != INTEGER_TYPE) ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + if (Util.getShort(heap, (short) (ptr + 1)) == INVALID_VALUE) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + return proto(ptr); } // create integer and copy byte value - public static KMInteger uint_8(byte num) { - KMInteger inst = repository.newInteger(); - inst.val = repository.newIntegerArray((short) 4); - inst.val[3] = num; - return inst; + public static short uint_8(byte num) { + short ptr = instance(UINT_32); + heap[(short) (ptr + TLV_HEADER_SIZE + 3)] = num; + return ptr; } // create integer and copy short value - public static KMInteger uint_16(short num) { - KMInteger inst = repository.newInteger(); - inst.val = repository.newIntegerArray((short) 4); - inst.val[2] = (byte) ((num >> 8) & 0xff); - inst.val[3] = (byte) (num & 0xff); - return inst; + public static short uint_16(short num) { + short ptr = instance(UINT_32); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), num); + return ptr; } // create integer and copy integer value - public static KMInteger uint_32(byte[] num, short offset) { - KMInteger inst = repository.newInteger(); - inst.val = repository.newIntegerArray((short) 4); - Util.arrayCopy(num, offset, inst.val, (short) 0, (short) 4); - return inst; + public static short uint_32(byte[] num, short offset) { + short ptr = instance(UINT_32); + Util.arrayCopy(num, offset, heap, (short) (ptr + TLV_HEADER_SIZE), UINT_32); + return ptr; } // create integer and copy integer value - public static KMInteger uint_64(byte[] num, short offset) { - KMInteger inst = repository.newInteger(); - inst.val = repository.newIntegerArray((short) 8); - Util.arrayCopy(num, offset, inst.val, (short) 0, (short) 8); - return inst; - } - - public static void create(KMInteger[] integerRefTable) { - byte index = 0; - while (index < integerRefTable.length) { - integerRefTable[index] = new KMInteger(); - index++; - } + public static short uint_64(byte[] num, short offset) { + short ptr = instance(UINT_64); + Util.arrayCopy(num, offset, heap, (short) (ptr + TLV_HEADER_SIZE), UINT_64); + return ptr; } - public byte[] getValue() { - return val; + // Get the length of the integer + public short length() { + return Util.getShort(heap, (short) (instPtr + 1)); } - public KMInteger setValue(short val) { - this.val[2] = (byte) (val >> 8); - this.val[3] = (byte) (val & 0xFF); - return this; + // Get the buffer pointer in which blob is contained. + public byte[] getBuffer() { + return heap; } - public KMInteger setValue(byte[] val) { - this.val = val; - return this; + // Get the start of value + public short getStartOff() { + return (short) (instPtr + TLV_HEADER_SIZE); } - public short getShort() { - if (val == null) { - throw new KMException(ISO7816.SW_DATA_INVALID); - } else if (val.length != 4) { - throw new KMException(ISO7816.SW_WRONG_LENGTH); - } - return Util.makeShort(val[2], val[3]); + public void getValue(byte[] dest, short destOff, short length){ + Util.arrayCopyNonAtomic(heap, (short)(instPtr+TLV_HEADER_SIZE), dest, destOff, length); + } + public void setValue(byte[] src, short srcOff){ + Util.arrayCopyNonAtomic(src, srcOff, heap, (short)(instPtr+TLV_HEADER_SIZE), length()); + } + public short value(byte[] dest, short destOff){ + Util.arrayCopyNonAtomic(heap, (short)(instPtr+TLV_HEADER_SIZE), dest, destOff, length()); + return length(); } + public short getShort() { + return Util.getShort(heap, (short) (instPtr + TLV_HEADER_SIZE + 2)); + } + public short getSignificantShort(){ + return Util.getShort(heap, (short) (instPtr + TLV_HEADER_SIZE)); + } public byte getByte() { - if (val == null) { - throw new KMException(ISO7816.SW_DATA_INVALID); - } else if (val.length != 4) { - throw new KMException(ISO7816.SW_WRONG_LENGTH); + return heap[(short) (instPtr + TLV_HEADER_SIZE + 3)]; + } + + public boolean isZero() { + if(getShort() == 0 && getSignificantShort() == 0){ + return true; } - return val[3]; + return false; } - // copy the integer value from bytes - public static KMInteger instance(byte[] num, short srcOff, short length) { - if (length == 1) { - return uint_8(num[srcOff]); - } else if (length == 2) { - return uint_16(Util.makeShort(num[srcOff], num[(short) (srcOff + 1)])); - } else if (length == 4) { - return uint_32(num, srcOff); - } else if (length == 8) { - return uint_64(num, srcOff); - } else { - throw new KMException(ISO7816.SW_WRONG_LENGTH); + public static short compare(short num1, short num2){ + short num1Ptr = KMInteger.cast(num1).getStartOff(); + short num2Ptr = KMInteger.cast(num2).getStartOff(); + short len = KMInteger.cast(num2).length(); + if(KMInteger.cast(num1).length() > KMInteger.cast(num2).length()){ + len = KMInteger.cast(num1).length(); } + return Util.arrayCompare( + repository.getHeap(), num1Ptr, + repository.getHeap(), num2Ptr, + len); } } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMIntegerArrayTag.java b/Applet/Applet/src/com/android/javacard/keymaster/KMIntegerArrayTag.java index 9c9258a2..c154f5a0 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMIntegerArrayTag.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMIntegerArrayTag.java @@ -17,77 +17,97 @@ package com.android.javacard.keymaster; import javacard.framework.ISO7816; +import javacard.framework.ISOException; +import javacard.framework.Util; public class KMIntegerArrayTag extends KMTag { + private static KMIntegerArrayTag prototype; + private static short instPtr; + private static final short[] tags = {USER_SECURE_ID}; - private short key; - private KMVector vals; - private short tagType; - private KMIntegerArrayTag() { - init(); + private KMIntegerArrayTag() {} + + private static KMIntegerArrayTag proto(short ptr) { + if (prototype == null) prototype = new KMIntegerArrayTag(); + instPtr = ptr; + return prototype; } - @Override - public void init() { - key = 0; - vals = null; - tagType = KMType.UINT_ARRAY_TAG; + public static short exp(short tagType) { + if (!validateTagType(tagType)) { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + short arrPtr = KMArray.exp(KMType.INTEGER_TYPE); + short ptr = instance(TAG_TYPE, (short)6); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE), tagType); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE+2), INVALID_TAG); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE+4), arrPtr); + return ptr; } - @Override - public short getKey() { - return key; + public static short instance(short tagType, short key) { + if (!validateTagType(tagType)) { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + if (!validateKey(key)) { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + short arrPtr = KMArray.exp(); + return instance(tagType, key, arrPtr); } - @Override - public short length() { - return this.vals.length(); + public static short instance(short tagType, short key, short arrObj) { + if (!validateTagType(tagType)) { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + if (!validateKey(key)) { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + if(heap[arrObj] != ARRAY_TYPE) { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + short ptr = instance(TAG_TYPE, (short)6); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE), tagType); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE+2), key); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE+4), arrObj); + return ptr; + } + + public static KMIntegerArrayTag cast(short ptr) { + if (heap[ptr] != TAG_TYPE) ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + short tagType = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE)); + if (!validateTagType(tagType)) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + return proto(ptr); } - @Override public short getTagType() { - return tagType; + return Util.getShort(heap, (short)(instPtr+TLV_HEADER_SIZE)); } - public static KMIntegerArrayTag instance() { - return repository.newIntegerArrayTag(); + public short getKey() { + return Util.getShort(heap, (short)(instPtr+TLV_HEADER_SIZE+2)); } - public static void create(KMIntegerArrayTag[] intArrayTagRefTable) { - byte index = 0; - while (index < intArrayTagRefTable.length) { - intArrayTagRefTable[index] = new KMIntegerArrayTag(); - index++; - } + public short getValues() { + return Util.getShort(heap, (short)(instPtr+TLV_HEADER_SIZE+4)); } - public KMIntegerArrayTag asUlongArray() { - tagType = KMType.ULONG_ARRAY_TAG; - return this; + public short length() { + short ptr = getValues(); + return KMArray.cast(ptr).length(); } - public static KMIntegerArrayTag instance(short key) { - if (!validateKey(key)) { - throw new KMException(ISO7816.SW_DATA_INVALID); - } - KMIntegerArrayTag tag = repository.newIntegerArrayTag(); - tag.key = key; - tag.vals = KMVector.instance(KMInteger.instance()); - return tag; + public void add(short index, short val) { + KMArray arr = KMArray.cast(getValues()); + arr.add(index, val); } - public static KMIntegerArrayTag instance(short key, KMVector val) { - if (!(val.getType() instanceof KMInteger)) { - throw new KMException(ISO7816.SW_DATA_INVALID); - } - if (!(validateKey(key))) { - throw new KMException(ISO7816.SW_DATA_INVALID); - } - KMIntegerArrayTag tag = repository.newIntegerArrayTag(); - tag.key = key; - tag.vals = val; - return tag; + public short get(short index) { + KMArray arr = KMArray.cast(getValues()); + return arr.get(index); } private static boolean validateKey(short key) { @@ -100,25 +120,28 @@ private static boolean validateKey(short key) { return false; } - public KMIntegerArrayTag withLength(short length) { - this.vals.withLength(length); - return this; - } - - public KMVector getValues() { - return this.vals; - } - - public KMIntegerArrayTag setValues(KMVector vals) { - this.vals = vals; - return this; + // TODO this should be combined with validateKey to actually isValidTag {tagType, tagKey} pair. + private static boolean validateTagType(short tagType) { + if ((tagType == ULONG_ARRAY_TAG) || (tagType == UINT_ARRAY_TAG)) { + return true; + } + return false; } - public void add(short index, KMInteger val) { - this.vals.add(index, val); + public static boolean contains(short tagId, short tagValue, short params) { + short tag = + KMKeyParameters.findTag(KMType.UINT_ARRAY_TAG, tagId, params); + if (tag != KMType.INVALID_VALUE) { + short index = 0; + tag = KMIntegerArrayTag.cast(tag).getValues(); + while (index < KMArray.cast(tag).length()) { + if (KMInteger.compare(tagValue, KMArray.cast(tag).get(index)) == 0) { + return true; + } + index++; + } + } + return false; } - public KMInteger get(short index) { - return (KMInteger) this.vals.get(index); - } } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMIntegerTag.java b/Applet/Applet/src/com/android/javacard/keymaster/KMIntegerTag.java index 00dbf256..0ac49e06 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMIntegerTag.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMIntegerTag.java @@ -17,9 +17,14 @@ package com.android.javacard.keymaster; import javacard.framework.ISO7816; +import javacard.framework.ISOException; +import javacard.framework.Util; // Implements UINT, ULONG and DATE tags. public class KMIntegerTag extends KMTag { + private static KMIntegerTag prototype; + private static short instPtr; + private static final short[] tags = { // UINT KEYSIZE, @@ -42,67 +47,81 @@ public class KMIntegerTag extends KMTag { CREATION_DATETIME }; - private short key; - private KMInteger val; - private short tagType; + private KMIntegerTag() {} - private KMIntegerTag() { - init(); + private static KMIntegerTag proto(short ptr) { + if (prototype == null) prototype = new KMIntegerTag(); + instPtr = ptr; + return prototype; } - @Override - public void init() { - key = 0; - val = null; - tagType = KMType.UINT_TAG; + public static short exp(short tagType) { + if (!validateTagType(tagType)) { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + short intPtr = KMInteger.exp(); + short ptr = instance(TAG_TYPE, (short)6); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE), tagType); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE+2), INVALID_TAG); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE+4), intPtr); + return ptr; } - @Override - public short getKey() { - return key; + public static short instance(short tagType, short key) { + if (!validateTagType(tagType)) { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + if (!validateKey(key)) { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + short intPtr = KMInteger.exp(); + return instance(tagType, key, intPtr); } - @Override - public short length() { - return (short) val.getValue().length; + public static short instance(short tagType, short key, short intObj) { + if (!validateTagType(tagType)) { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + if (!validateKey(key)) { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + if(heap[intObj] != INTEGER_TYPE) { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + short ptr = instance(TAG_TYPE, (short)6); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE), tagType); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE+2), key); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE+4), intObj); + return ptr; } - @Override - public short getTagType() { - return tagType; + public static KMIntegerTag cast(short ptr) { + if (heap[ptr] != TAG_TYPE) ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + short tagType = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE)); + if (!validateTagType(tagType)) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + return proto(ptr); } - public static KMIntegerTag instance() { - return repository.newIntegerTag(); + public short getTagType() { + return Util.getShort(heap, (short)(instPtr+TLV_HEADER_SIZE)); } - public static KMIntegerTag instance(short key) { - if (!validateKey(key)) { - throw new KMException(ISO7816.SW_DATA_INVALID); - } - KMIntegerTag tag = repository.newIntegerTag(); - tag.key = key; - tag.val = null; - return tag; + public short getKey() { + return Util.getShort(heap, (short)(instPtr+TLV_HEADER_SIZE+2)); } - public static KMIntegerTag instance(short givenKey, KMInteger val) { - KMIntegerTag tag = KMIntegerTag.instance(givenKey); - tag.val = val; - if (val.length() == 8) { - tag.tagType = KMType.ULONG_TAG; - } - return tag; + public short getValue() { + return Util.getShort(heap, (short)(instPtr+TLV_HEADER_SIZE+4)); } - public static void create(KMIntegerTag[] intTagRefTable) { - byte index = 0; - while (index < intTagRefTable.length) { - intTagRefTable[index] = new KMIntegerTag(); - index++; - } + public short length() { + KMInteger obj = KMInteger.cast(getValue()); + return obj.length(); } + private static boolean validateKey(short key) { short index = (short) tags.length; while (--index >= 0) { @@ -113,22 +132,37 @@ private static boolean validateKey(short key) { return false; } - public KMInteger getValue() { - return this.val; - } - - public KMIntegerTag setValue(KMInteger val) { - this.val = val; - return this; + // TODO this should be combined with validateKey to actually isValidTag {tagType, tagKey} pair. + private static boolean validateTagType(short tagType) { + if ((tagType == DATE_TAG) || (tagType == UINT_TAG) || (tagType == ULONG_TAG)) { + return true; + } + return false; } - public KMIntegerTag asULong() { - tagType = KMType.ULONG_TAG; - return this; + public static short getShortValue(short tagType, short tagKey, short keyParameters){ + short ptr; + if(tagType == UINT_TAG){ + ptr = KMKeyParameters.findTag(KMType.UINT_TAG, tagKey, keyParameters); + if (ptr != KMType.INVALID_VALUE) { + ptr = KMIntegerTag.cast(ptr).getValue(); + if(KMInteger.cast(ptr).getSignificantShort() == 0){ + return KMInteger.cast(ptr).getShort(); + } + } + } + return KMType.INVALID_VALUE; } - public KMIntegerTag asDate() { - tagType = KMType.DATE_TAG; - return this; + public static short getValue(byte[] buf, short offset, short tagType, short tagKey, short keyParameters){ + short ptr; + if((tagType == UINT_TAG) || (tagType == ULONG_TAG) || (tagType == DATE_TAG)){ + ptr = KMKeyParameters.findTag(tagType, tagKey, keyParameters); + if (ptr != KMType.INVALID_VALUE) { + ptr = KMIntegerTag.cast(ptr).getValue(); + return KMInteger.cast(ptr).value(buf,offset); + } + } + return KMType.INVALID_VALUE; } } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMKeyCharacteristics.java b/Applet/Applet/src/com/android/javacard/keymaster/KMKeyCharacteristics.java index a6b5c7fe..0421e6da 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMKeyCharacteristics.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMKeyCharacteristics.java @@ -17,60 +17,80 @@ package com.android.javacard.keymaster; import javacard.framework.ISO7816; +import javacard.framework.ISOException; +import javacard.framework.Util; public class KMKeyCharacteristics extends KMType { public static final byte SOFTWARE_ENFORCED = 0x00; public static final byte HARDWARE_ENFORCED = 0x01; - private KMArray vals; + private static KMKeyCharacteristics prototype; + private static short instPtr; - private KMKeyCharacteristics() { - init(); + private KMKeyCharacteristics() {} + + public static short exp() { + short softEnf = KMKeyParameters.exp(); + short hwEnf = KMKeyParameters.exp(); + short arrPtr = KMArray.instance((short)2); + KMArray arr = KMArray.cast(arrPtr); + arr.add(SOFTWARE_ENFORCED, softEnf); + arr.add(HARDWARE_ENFORCED, hwEnf); + return instance(arrPtr); } - @Override - public void init() { - vals = null; + private static KMKeyCharacteristics proto(short ptr) { + if (prototype == null) prototype = new KMKeyCharacteristics(); + instPtr = ptr; + return prototype; } - @Override - public short length() { - return vals.length(); + public static short instance() { + short arrPtr = KMArray.instance((short)2); + return instance(arrPtr); + } + + public static short instance(short vals) { + short ptr = KMType.instance(KEY_CHAR_TYPE, (short)2); + if(KMArray.cast(vals).length() != 2) ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); + Util.setShort(heap, (short)(ptr + TLV_HEADER_SIZE), vals); + return ptr; } - public static KMKeyCharacteristics instance() { - KMKeyCharacteristics inst = repository.newKeyCharacteristics(); - inst.vals = KMArray.instance((short) 2); - inst.vals.add(SOFTWARE_ENFORCED, KMKeyParameters.instance()); - inst.vals.add(HARDWARE_ENFORCED, KMKeyParameters.instance()); - return inst; + public static KMKeyCharacteristics cast(short ptr) { + if (heap[ptr] != KEY_CHAR_TYPE) ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + short arrPtr = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE)); + if(heap[arrPtr] != ARRAY_TYPE) ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + return proto(ptr); } - public static KMKeyCharacteristics instance(KMArray vals) { - if (vals.length() != 2) { - throw new KMException(ISO7816.SW_WRONG_LENGTH); - } - KMKeyCharacteristics inst = repository.newKeyCharacteristics(); - inst.vals = vals; - return inst; + public short getVals() { + return Util.getShort(heap, (short) (instPtr + TLV_HEADER_SIZE)); + } + + public short length() { + short arrPtr = getVals(); + return KMArray.cast(arrPtr).length(); } - public static void create(KMKeyCharacteristics[] keyCharRefTable) { - byte index = 0; - while (index < keyCharRefTable.length) { - keyCharRefTable[index] = new KMKeyCharacteristics(); - index++; - } + public short getSoftwareEnforced() { + short arrPtr = getVals(); + return KMArray.cast(arrPtr).get(SOFTWARE_ENFORCED); } - public KMKeyParameters getSoftwareEnforced() { - return (KMKeyParameters) vals.get(SOFTWARE_ENFORCED); + public short getHardwareEnforced() { + short arrPtr = getVals(); + return KMArray.cast(arrPtr).get(HARDWARE_ENFORCED); } - public KMKeyParameters getHardwareEnforced() { - return (KMKeyParameters) vals.get(HARDWARE_ENFORCED); + public void setSoftwareEnforced(short ptr) { + KMKeyParameters.cast(ptr); + short arrPtr = getVals(); + KMArray.cast(arrPtr).add(SOFTWARE_ENFORCED, ptr); } - public KMArray getVals() { - return vals; + public void setHardwareEnforced(short ptr) { + KMKeyParameters.cast(ptr); + short arrPtr = getVals(); + KMArray.cast(arrPtr).add(HARDWARE_ENFORCED, ptr); } } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java b/Applet/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java index 44e25e98..d3bf5af1 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java @@ -16,53 +16,260 @@ package com.android.javacard.keymaster; +import javacard.framework.ISO7816; +import javacard.framework.ISOException; +import javacard.framework.Util; + public class KMKeyParameters extends KMType { - private KMArray vals; - private KMKeyParameters() { - init(); + private static final short[] swEnforcedTags = {}; + + private static final short[] ignoredTags = { + KMType.ROOT_OF_TRUST, + KMType.RESET_SINCE_ID_ROTATION, + KMType.ALLOW_WHILE_ON_BODY, + KMType.ATTESTATION_CHALLENGE, + KMType.OS_VERSION, // not in hidden + KMType.OS_PATCH_LEVEL // not in hidden + // ALL_APPLICATIONS missing from types.hal + }; + + private static KMKeyParameters prototype; + private static short instPtr; + + private KMKeyParameters() {} + + private static KMKeyParameters proto(short ptr) { + if (prototype == null) prototype = new KMKeyParameters(); + instPtr = ptr; + return prototype; + } + + public static short exp() { + short arrPtr = KMArray.instance((short) 9); + KMArray arr = KMArray.cast(arrPtr); + arr.add((short) 0, KMIntegerTag.exp(UINT_TAG)); + arr.add((short) 1, KMIntegerArrayTag.exp(UINT_ARRAY_TAG)); + arr.add((short) 2, KMIntegerTag.exp(ULONG_TAG)); + arr.add((short) 3, KMIntegerTag.exp(DATE_TAG)); + arr.add((short) 4, KMIntegerArrayTag.exp(ULONG_ARRAY_TAG)); + arr.add((short) 5, KMEnumTag.exp()); + arr.add((short) 6, KMEnumArrayTag.exp()); + arr.add((short) 7, KMByteTag.exp()); + arr.add((short) 8, KMBoolTag.exp()); + return instance(arrPtr); } - @Override - public void init() { - vals = null; + public static short instance(short vals) { + short ptr = KMType.instance(KEY_PARAM_TYPE, (short) 2); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), vals); + return ptr; + } + + public static KMKeyParameters cast(short ptr) { + if (heap[ptr] != KEY_PARAM_TYPE) ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + short arrPtr = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE)); + if (heap[arrPtr] != ARRAY_TYPE) ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + return proto(ptr); + } + + public short getVals() { + return Util.getShort(heap, (short) (instPtr + TLV_HEADER_SIZE)); } - @Override public short length() { - return vals.length(); + short arrPtr = getVals(); + return KMArray.cast(arrPtr).length(); } - public static KMKeyParameters instance() { - KMKeyParameters inst = repository.newKeyParameters(); - inst.vals = KMArray.instance((short) 9); - inst.vals.add((short) 0, KMIntegerTag.instance()); - inst.vals.add((short) 1, KMIntegerArrayTag.instance()); - inst.vals.add((short) 2, KMIntegerTag.instance().asULong()); - inst.vals.add((short) 3, KMIntegerTag.instance().asDate()); - inst.vals.add((short) 4, KMIntegerArrayTag.instance().asUlongArray()); - inst.vals.add((short) 5, KMEnumTag.instance()); - inst.vals.add((short) 6, KMEnumArrayTag.instance()); - inst.vals.add((short) 7, KMByteTag.instance()); - inst.vals.add((short) 8, KMBoolTag.instance()); - return inst; + public static short findTag(short tagType, short tagKey, short keyParam) { + KMKeyParameters instParam = KMKeyParameters.cast(keyParam); + KMArray vals = KMArray.cast(instParam.getVals()); + short index = 0; + short length = vals.length(); + short key; + short type; + short ret = KMType.INVALID_VALUE; + short obj; + while (index < length) { + obj = vals.get(index); + key = KMTag.getKey(obj); + type = KMTag.getTagType(obj); + if ((tagKey == key) && (tagType == type)) { + ret = obj; + break; + } + index++; + } + return ret; } - public static KMKeyParameters instance(KMArray vals) { - KMKeyParameters inst = repository.newKeyParameters(); - inst.vals = vals; - return inst; + // KDF, ECIES_SINGLE_HASH_MODE missing from types.hal + public static short makeHwEnforced( + short keyParamsPtr, byte origin, short osVersionObjPtr, short osPatchObjPtr, byte[] scratchPad) { + final short[] hwEnforcedTagArr = { + // HW Enforced + KMType.ENUM_TAG, KMType.ORIGIN, + KMType.ENUM_ARRAY_TAG, KMType.PURPOSE, + KMType.ENUM_TAG, KMType.ALGORITHM, + KMType.UINT_TAG, KMType.KEYSIZE, + KMType.ULONG_TAG, KMType.RSA_PUBLIC_EXPONENT, + KMType.ENUM_TAG, KMType.BLOB_USAGE_REQ, + KMType.ENUM_ARRAY_TAG, KMType.DIGEST, + KMType.ENUM_ARRAY_TAG, KMType.PADDING, + KMType.ENUM_ARRAY_TAG, KMType.BLOCK_MODE, + KMType.UINT_TAG, KMType.MIN_SEC_BETWEEN_OPS, + KMType.UINT_TAG, KMType.MAX_USES_PER_BOOT, + KMType.ULONG_ARRAY_TAG, KMType.USER_SECURE_ID, + KMType.BOOL_TAG, KMType.NO_AUTH_REQUIRED, + KMType.UINT_TAG, KMType.AUTH_TIMEOUT, + KMType.BOOL_TAG, KMType.CALLER_NONCE, + KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, + KMType.ENUM_TAG, KMType.ECCURVE, + KMType.BOOL_TAG, KMType.TRUSTED_CONFIRMATION_REQUIRED, + KMType.BOOL_TAG, KMType.INCLUDE_UNIQUE_ID, + KMType.BOOL_TAG, KMType.ROLLBACK_RESISTANCE, + KMType.ENUM_TAG, KMType.USER_AUTH_TYPE + }; + byte index = 0; + short tagInd = 0; + short arrInd = 0; + short tagPtr = 0; + short tagKey = 0; + short tagType = 0; + short arrPtr = KMKeyParameters.cast(keyParamsPtr).getVals(); + short len = KMArray.cast(arrPtr).length(); + while (index < len) { + tagInd = 0; + tagPtr = KMArray.cast(arrPtr).get(index); + tagKey = KMTag.getKey(tagPtr); + tagType = KMTag.getTagType(tagPtr); + if (!isValidTag(tagType, tagKey)) { + KMException.throwIt(KMError.INVALID_KEY_BLOB); + } + while (tagInd < (short) hwEnforcedTagArr.length) { + if ((hwEnforcedTagArr[tagInd] == tagType) + && (hwEnforcedTagArr[(short) (tagInd + 1)] == tagKey)) { + Util.setShort(scratchPad, arrInd, tagPtr); + arrInd += 2; + break; + } + tagInd += 2; + } + index++; + } + short originTag = KMEnumTag.instance(KMType.ORIGIN, origin); + Util.setShort(scratchPad, arrInd, originTag); + arrInd += 2; + short osVersionTag = KMIntegerTag.instance(KMType.UINT_TAG, KMType.OS_VERSION, osVersionObjPtr); + Util.setShort(scratchPad, arrInd, osVersionTag); + arrInd += 2; + short osPatchTag = KMIntegerTag.instance(KMType.UINT_TAG, KMType.OS_PATCH_LEVEL, osPatchObjPtr); + Util.setShort(scratchPad, arrInd, osPatchTag); + arrInd += 2; + return createKeyParameters(scratchPad, (short) (arrInd / 2)); } - public static void create(KMKeyParameters[] keyParametersRefTable) { + // ALL_USERS, EXPORTABLE missing from types.hal + public static short makeSwEnforced(short keyParamsPtr, byte[] scratchPad) { + final short[] swEnforcedTagsArr = { + KMType.DATE_TAG, KMType.ACTIVE_DATETIME, + KMType.DATE_TAG, KMType.ORIGINATION_EXPIRE_DATETIME, + KMType.DATE_TAG, KMType.USAGE_EXPIRE_DATETIME, + KMType.UINT_TAG, KMType.USERID, + KMType.DATE_TAG, KMType.CREATION_DATETIME + }; byte index = 0; - while (index < keyParametersRefTable.length) { - keyParametersRefTable[index] = new KMKeyParameters(); + short tagInd = 0; + short arrInd = 0; + short tagPtr = 0; + short tagKey = 0; + short tagType = 0; + short arrPtr = KMKeyParameters.cast(keyParamsPtr).getVals(); + short len = KMArray.cast(arrPtr).length(); + while (index < len) { + tagInd = 0; + tagPtr = KMArray.cast(arrPtr).get(index); + tagKey = KMTag.getKey(tagPtr); + tagType = KMTag.getTagType(tagPtr); + if (!isValidTag(tagType, tagKey)){ + KMException.throwIt(KMError.INVALID_KEY_BLOB); + } + while (tagInd < (short) swEnforcedTagsArr.length) { + if ((swEnforcedTagsArr[tagInd] == tagType) + && (swEnforcedTagsArr[(short) (tagInd + 1)] == tagKey)) { + Util.setShort(scratchPad, arrInd, tagPtr); + arrInd += 2; + break; + } + tagInd += 2; + } index++; } + return createKeyParameters(scratchPad, (short) (arrInd / 2)); + } + + public static short makeHidden(short keyParamsPtr, short rootOfTrustBlob, byte[] scratchPad) { + short appId = KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.APPLICATION_ID, keyParamsPtr); + if (appId != KMTag.INVALID_VALUE) { + appId = KMByteTag.cast(appId).getValue(); + } + short appData = + KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.APPLICATION_DATA, keyParamsPtr); + if (appData != KMTag.INVALID_VALUE) { + appData = KMByteTag.cast(appData).getValue(); + } + return makeHidden(appId, appData, rootOfTrustBlob, scratchPad); } - public KMArray getVals() { - return vals; + public static short makeHidden(short appIdBlob, short appDataBlob, short rootOfTrustBlob, byte[] scratchPad){ + // Order in which the hidden array is created should not change. + short index = 0; + KMByteBlob.cast(rootOfTrustBlob); + Util.setShort(scratchPad, index, rootOfTrustBlob); + index += 2; + if (appIdBlob != KMTag.INVALID_VALUE) { + KMByteBlob.cast(appIdBlob); + Util.setShort(scratchPad, index, appIdBlob); + index += 2; + } + if (appDataBlob != KMTag.INVALID_VALUE) { + Util.setShort(scratchPad, index, appDataBlob); + index += 2; + } + return createKeyParameters(scratchPad, (short)(index/2)); + + } + public static boolean isValidTag(short tagType, short tagKey) { + short[] invalidTagsArr = { + KMType.BYTES_TAG, KMType.NONCE, + KMType.BYTES_TAG, KMType.ASSOCIATED_DATA, + KMType.BYTES_TAG, KMType.UNIQUE_ID, + KMType.UINT_TAG, KMType.MAC_LENGTH, + KMType.BOOL_TAG, KMType.BOOTLOADER_ONLY + }; + short index = 0; + if (tagKey == KMType.INVALID_TAG) { + return false; + } + while (index < invalidTagsArr.length) { + if ((tagType == invalidTagsArr[index]) && (tagKey == invalidTagsArr[(short) (index + 1)])) { + return false; + } + index += 2; + } + return true; + } + + public static short createKeyParameters(byte[] ptrArr, short len) { + short arrPtr = KMArray.instance(len); + short index = 0; + short ptr = 0; + while (index < len) { + KMArray.cast(arrPtr).add(index, Util.getShort(ptrArr, ptr)); + index++; + ptr += 2; + } + return KMKeyParameters.instance(arrPtr); } } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java b/Applet/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java index 83512f92..bfd769df 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java @@ -20,7 +20,18 @@ import javacard.framework.Applet; import javacard.framework.AppletEvent; import javacard.framework.ISO7816; +import javacard.framework.ISOException; +import javacard.framework.JCSystem; import javacard.framework.Util; +import javacard.security.AESKey; +import javacard.security.DESKey; +import javacard.security.ECPrivateKey; +import javacard.security.ECPublicKey; +import javacard.security.HMACKey; +import javacard.security.KeyPair; +import javacard.security.MessageDigest; +import javacard.security.RSAPrivateKey; +import javacard.security.Signature; import javacardx.apdu.ExtendedLength; /** @@ -32,29 +43,145 @@ // - remove this in future. public class KMKeymasterApplet extends Applet implements AppletEvent, ExtendedLength { // Constants. - public static final short MAX_LENGTH = (short) 0x04ff; // TODO: make this value configurable. + public static final byte AES_BLOCK_SIZE = 16; + public static final byte DES_BLOCK_SIZE = 8; + public static final short MAX_LENGTH = (short) 0x2000; private static final byte CLA_ISO7816_NO_SM_NO_CHAN = (byte) 0x80; - private static final byte KM_HAL_VERSION = (byte) 0x41; - + private static final short KM_HAL_VERSION = (short) 0x4000; + private static final short MAX_AUTH_DATA_SIZE = (short) 128; + private static final short MAX_IO_LENGTH = 0x400; + // "Keymaster HMAC Verification" - used for HMAC key verification. + public static final byte[] sharingCheck = { + 0x4B, 0x65, 0x79, 0x6D, 0x61, 0x73, 0x74, 0x65, 0x72, 0x20, 0x48, 0x4D, 0x41, 0x43, 0x20, 0x56, + 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E + }; + // "KeymasterSharedMac" + public static final byte[] ckdfLable = { + 0x4B, 0x65, 0x79, 0x6D, 0x61, 0x73, 0x74, 0x65, 0x72, 0x53, 0x68, 0x61, 0x72, 0x65, 0x64, 0x4D, + 0x61, 0x63 + }; + // "Auth Verification" + public static final byte[] authVerification = {0x41, 0x75, 0x74, 0x68, 0x20, 0x56, 0x65, 0x72, 0x69, + 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E}; // Possible states of the applet. - public static final byte ILLEGAL_STATE = 0x00; - public static final byte INSTALL_STATE = 0x01; - public static final byte FIRST_SELECT_STATE = 0x02; - public static final byte ACTIVE_STATE = 0x03; - public static final byte INACTIVE_STATE = 0x04; - public static final byte UNINSTALLED_STATE = 0x05; + private static final byte ILLEGAL_STATE = 0x00; + private static final byte INSTALL_STATE = 0x01; + private static final byte FIRST_SELECT_STATE = 0x02; + private static final byte ACTIVE_STATE = 0x03; + private static final byte INACTIVE_STATE = 0x04; + private static final byte UNINSTALLED_STATE = 0x05; + // Commands + private static final byte INS_GENERATE_KEY_CMD = 0x10; + private static final byte INS_IMPORT_KEY_CMD = 0x11; + private static final byte INS_IMPORT_WRAPPED_KEY_CMD = 0x12; + private static final byte INS_EXPORT_KEY_CMD = 0x13; + private static final byte INS_ATTEST_KEY_CMD = 0x14; + private static final byte INS_UPGRADE_KEY_CMD = 0x15; + private static final byte INS_DELETE_KEY_CMD = 0x16; + private static final byte INS_DELETE_ALL_KEYS_CMD = 0x17; + private static final byte INS_ADD_RNG_ENTROPY_CMD = 0x18; + private static final byte INS_COMPUTE_SHARED_HMAC_CMD = 0x19; + private static final byte INS_DESTROY_ATT_IDS_CMD = 0x1A; + private static final byte INS_VERIFY_AUTHORIZATION_CMD = 0x1B; + private static final byte INS_GET_HMAC_SHARING_PARAM_CMD = 0x1C; + private static final byte INS_GET_KEY_CHARACTERISTICS_CMD = 0x1D; + private static final byte INS_GET_HW_INFO_CMD = 0x1E; + private static final byte INS_BEGIN_OPERATION_CMD = 0x1F; + private static final byte INS_UPDATE_OPERATION_CMD = 0x20; + private static final byte INS_FINISH_OPERATION_CMD = 0x21; + private static final byte INS_ABORT_OPERATION_CMD = 0x22; + private static final byte INS_PROVISION_CMD = 0x23; + private static final byte INS_SET_BOOT_PARAMS_CMD = 0x24; + // Data Dictionary items + public static final byte DATA_ARRAY_SIZE = 30; + public static final byte TMP_VARIABLE_ARRAY_SIZE = 20; + public static final byte UPDATE_PARAM_ARRAY_SIZE = 40; + public static final byte KEY_PARAMETERS = 0; + public static final byte KEY_CHARACTERISTICS = 1; + public static final byte HIDDEN_PARAMETERS = 2; + public static final byte HW_PARAMETERS = 3; + public static final byte SW_PARAMETERS = 4; + public static final byte AUTH_DATA = 5; + public static final byte AUTH_TAG = 6; + public static final byte NONCE = 7; + public static final byte KEY_BLOB = 8; + public static final byte AUTH_DATA_LENGTH = 9; + public static final byte SECRET = 10; + public static final byte ROT = 11; + public static final byte DERIVED_KEY = 12; + public static final byte RSA_PUB_EXPONENT = 13; + public static final byte APP_ID = 14; + public static final byte APP_DATA = 15; + public static final byte PUB_KEY = 16; + public static final byte IMPORTED_KEY_BLOB = 17; + public static final byte ORIGIN = 18; + public static final byte ENC_TRANSPORT_KEY = 19; + public static final byte MASKING_KEY = 20; + public static final byte HMAC_SHARING_PARAMS = 21; + public static final byte OP_HANDLE = 22; + public static final byte IV = 23; + public static final byte INPUT_DATA = 24; + public static final byte OUTPUT_DATA = 25; + public static final byte HW_TOKEN = 26; + public static final byte VERIFICATION_TOKEN = 27; - // State of the applet. - private byte keymasterState = ILLEGAL_STATE; - private KMRepository repository; + // AddRngEntropy + private static final short MAX_SEED_SIZE = 2048; + // Keyblob constants + public static final byte KEY_BLOB_SECRET = 0; + public static final byte KEY_BLOB_NONCE = 1; + public static final byte KEY_BLOB_AUTH_TAG = 2; + public static final byte KEY_BLOB_KEYCHAR = 3; + public static final byte KEY_BLOB_PUB_KEY = 4; + // AES GCM constants + private static final byte AES_GCM_AUTH_TAG_LENGTH = 12; + private static final byte AES_GCM_NONCE_LENGTH = 12; + // ComputeHMAC constants + private static final short HMAC_SEED_SIZE = 32; + private static final short HMAC_NONCE_SIZE = 32; + // Keymaster Applet attributes + private static byte keymasterState = ILLEGAL_STATE; + private static KMEncoder encoder; + private static KMDecoder decoder; + private static KMRepository repository; + private static KMCryptoProvider cryptoProvider; + private static byte[] buffer; + private static short bufferLength; + private static short bufferStartOffset; + private static boolean provisionDone; + private static boolean setBootParamsDone; + private static short[] tmpVariables; + private static short[] data; - /** - * Registers this applet. - * - * @param repo reference to the repository which manages all the NVM objects. - */ - protected KMKeymasterApplet(KMRepository repo) { - repository = repo; + /** Registers this applet. */ + protected KMKeymasterApplet() { + // TODO change this to make this compile time variation. + cryptoProvider = KMCryptoProviderImpl.instance(); + provisionDone = false; + setBootParamsDone = false; + byte[] buf = + JCSystem.makeTransientByteArray( + repository.HMAC_SEED_NONCE_SIZE, JCSystem.CLEAR_ON_DESELECT); + keymasterState = KMKeymasterApplet.INSTALL_STATE; + data = JCSystem.makeTransientShortArray((short) DATA_ARRAY_SIZE, JCSystem.CLEAR_ON_RESET); + repository = new KMRepository(); + tmpVariables = + JCSystem.makeTransientShortArray((short) TMP_VARIABLE_ARRAY_SIZE, JCSystem.CLEAR_ON_RESET); + Util.arrayCopyNonAtomic( + cryptoProvider.getTrueRandomNumber(repository.HMAC_SEED_NONCE_SIZE), + (short) 0, + buf, + (short) 0, + repository.HMAC_SEED_NONCE_SIZE); + repository.initMasterKey(buf, repository.HMAC_SEED_NONCE_SIZE); + cryptoProvider.newRandomNumber(buf, (short) 0, repository.HMAC_SEED_NONCE_SIZE); + // TODO remove this when key agreement protocol is implemented. + repository.initHmacKey(buf, repository.HMAC_SEED_NONCE_SIZE); + cryptoProvider.newRandomNumber(buf, (short) 0, repository.HMAC_SEED_NONCE_SIZE); + repository.initHmacSeed(buf, repository.HMAC_SEED_NONCE_SIZE); + KMType.initialize(); + encoder = new KMEncoder(); + decoder = new KMDecoder(); register(); } @@ -66,11 +193,7 @@ protected KMKeymasterApplet(KMRepository repo) { * @param bLength the length in bytes of the parameter data in bArray */ public static void install(byte[] bArray, short bOffset, byte bLength) { - KMRepository repo = new KMRepository(); - // TODO: Read the configuration from the package and pass the data in initialize method. - repo.initialize(); - KMKeymasterApplet keymaster = new KMKeymasterApplet(repo); - keymaster.setKeymasterState(KMKeymasterApplet.INSTALL_STATE); + new KMKeymasterApplet(); } /** @@ -81,10 +204,10 @@ public static void install(byte[] bArray, short bOffset, byte bLength) { @Override public boolean select() { repository.onSelect(); - if (getKeymasterState() == KMKeymasterApplet.INSTALL_STATE) { - setKeymasterState(KMKeymasterApplet.FIRST_SELECT_STATE); - } else if (getKeymasterState() == KMKeymasterApplet.INACTIVE_STATE) { - setKeymasterState(KMKeymasterApplet.ACTIVE_STATE); + if (keymasterState == KMKeymasterApplet.INSTALL_STATE) { + keymasterState = KMKeymasterApplet.FIRST_SELECT_STATE; + } else if (keymasterState == KMKeymasterApplet.INACTIVE_STATE) { + keymasterState = KMKeymasterApplet.ACTIVE_STATE; } else { return false; } @@ -95,8 +218,8 @@ public boolean select() { @Override public void deselect() { repository.onDeselect(); - if (getKeymasterState() == KMKeymasterApplet.ACTIVE_STATE) { - setKeymasterState(KMKeymasterApplet.INACTIVE_STATE); + if (keymasterState == KMKeymasterApplet.ACTIVE_STATE) { + keymasterState = KMKeymasterApplet.INACTIVE_STATE; } } @@ -104,24 +227,23 @@ public void deselect() { @Override public void uninstall() { repository.onUninstall(); - if (getKeymasterState() != KMKeymasterApplet.UNINSTALLED_STATE) { - setKeymasterState(KMKeymasterApplet.UNINSTALLED_STATE); + if (keymasterState != KMKeymasterApplet.UNINSTALLED_STATE) { + keymasterState = KMKeymasterApplet.UNINSTALLED_STATE; } } /** * Processes an incoming APDU and handles it using command objects. * - * @see APDU * @param apdu the incoming APDU */ @Override public void process(APDU apdu) { repository.onProcess(); // Verify whether applet is in correct state. - if ((getKeymasterState() != KMKeymasterApplet.ACTIVE_STATE) - && (getKeymasterState() != KMKeymasterApplet.FIRST_SELECT_STATE)) { - throw new KMException(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + if ((keymasterState != KMKeymasterApplet.ACTIVE_STATE) + && (keymasterState != KMKeymasterApplet.FIRST_SELECT_STATE)) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); } // If this is select applet apdu which is selecting this applet then return if (apdu.isISOInterindustryCLA()) { @@ -129,105 +251,1293 @@ public void process(APDU apdu) { return; } } - // Read the apdu header and buffer. - byte[] buffer = apdu.getBuffer(); - byte apduClass = buffer[ISO7816.OFFSET_CLA]; - byte apduIns = buffer[ISO7816.OFFSET_INS]; - byte halVersion = buffer[ISO7816.OFFSET_P1]; - byte apduP2 = buffer[ISO7816.OFFSET_P2]; - + byte[] apduBuffer = apdu.getBuffer(); + byte apduClass = apduBuffer[ISO7816.OFFSET_CLA]; + byte apduIns = apduBuffer[ISO7816.OFFSET_INS]; + short P1P2 = Util.getShort(apduBuffer, ISO7816.OFFSET_P1); + buffer = repository.getHeap(); + bufferStartOffset = repository.alloc(MAX_IO_LENGTH); // Validate APDU Header. if ((apduClass != CLA_ISO7816_NO_SM_NO_CHAN)) { - throw new KMException(ISO7816.SW_CLA_NOT_SUPPORTED); - } else if ((halVersion != KMKeymasterApplet.KM_HAL_VERSION) && (apduP2 != (byte) 0x00)) { - throw new KMException(ISO7816.SW_INCORRECT_P1P2); + ISOException.throwIt(ISO7816.SW_CLA_NOT_SUPPORTED); + } else if (P1P2 != KMKeymasterApplet.KM_HAL_VERSION) { + ISOException.throwIt(ISO7816.SW_INCORRECT_P1P2); } - - // Process the APDU. + // Validate whether INS can be supported + if (!(apduIns >= INS_GENERATE_KEY_CMD && apduIns <= INS_SET_BOOT_PARAMS_CMD)) { + ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED); + } + // Validate if INS is provision command if applet is in FIRST_SELECT_STATE. + if (keymasterState == KMKeymasterApplet.FIRST_SELECT_STATE) { + if ((apduIns != INS_PROVISION_CMD) && (apduIns != INS_SET_BOOT_PARAMS_CMD)) { + ISOException.throwIt(ISO7816.SW_COMMAND_NOT_ALLOWED); + } + if (apduIns == INS_PROVISION_CMD && provisionDone) { + ISOException.throwIt(ISO7816.SW_COMMAND_NOT_ALLOWED); + } + if (apduIns == INS_SET_BOOT_PARAMS_CMD && setBootParamsDone) { + ISOException.throwIt(ISO7816.SW_COMMAND_NOT_ALLOWED); + } + } + // Process the apdu try { - // Get the command object for specific INS from the repository. - KMCommand command = repository.getCommand(apduIns); - // Get the empty context object from the repository. - KMContext context = repository.getContext(); - // Initialize context - context.setKeymasterState(getKeymasterState()); - context.setBuffer(repository.getBuffer()); - if(command.hasArguments()){ - receiveIncoming(context, apdu); - } - // Execute the command. If the execution fails then an exception is thrown. - command.execute(context); - - // context has data that needs to be sent - if(context.getBufferLength() >0 ){ - sendOutgoing(context, apdu); - } - - // Update the Keymaster state according to the context. - setKeymasterState(context.getKeymasterState()); + // Handle the command + switch (apduIns) { + case INS_GENERATE_KEY_CMD: + processGenerateKey(apdu); + break; + case INS_IMPORT_KEY_CMD: + processImportKeyCmd(apdu); + break; + case INS_IMPORT_WRAPPED_KEY_CMD: + processImportWrappedKeyCmd(apdu); + break; + case INS_EXPORT_KEY_CMD: + processExportKeyCmd(apdu); + break; + case INS_ATTEST_KEY_CMD: + processAttestKeyCmd(apdu); + break; + case INS_UPGRADE_KEY_CMD: + processUpgradeKeyCmd(apdu); + break; + case INS_DELETE_KEY_CMD: + processDeleteKeyCmd(apdu); + break; + case INS_DELETE_ALL_KEYS_CMD: + processDeleteAllKeysCmd(apdu); + break; + case INS_ADD_RNG_ENTROPY_CMD: + processAddRngEntropyCmd(apdu); + break; + case INS_COMPUTE_SHARED_HMAC_CMD: + processComputeSharedHmacCmd(apdu); + break; + case INS_DESTROY_ATT_IDS_CMD: + processDestroyAttIdsCmd(apdu); + break; + case INS_VERIFY_AUTHORIZATION_CMD: + processVerifyAuthorizationCmd(apdu); + break; + case INS_GET_HMAC_SHARING_PARAM_CMD: + processGetHmacSharingParamCmd(apdu); + break; + case INS_GET_KEY_CHARACTERISTICS_CMD: + processGetKeyCharacteristicsCmd(apdu); + break; + case INS_GET_HW_INFO_CMD: + processGetHwInfoCmd(apdu); + break; + case INS_BEGIN_OPERATION_CMD: + processBeginOperationCmd(apdu); + break; + case INS_UPDATE_OPERATION_CMD: + processUpdateOperationCmd(apdu); + break; + case INS_FINISH_OPERATION_CMD: + processFinishOperationCmd(apdu); + break; + case INS_ABORT_OPERATION_CMD: + processAbortOperationCmd(apdu); + break; + case INS_PROVISION_CMD: + processProvisionCmd(apdu); + break; + case INS_SET_BOOT_PARAMS_CMD: + processSetBootParamsCmd(apdu); + break; + default: + ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED); + } } catch (KMException exception) { - // TODO: error handling for command related error. - // TODO: This should result in ISOException or exception with keymaster specific error codes + if(data[OP_HANDLE] != KMType.INVALID_VALUE){ + KMOperationState op = repository.findOperation(KMInteger.cast(data[OP_HANDLE]).getShort()); + if(op != null){ + repository.releaseOperation(op); + } + } + sendError(apdu, exception.reason); + exception.clear(); + } finally { + resetData(); + repository.clean(); } } - - /** - * Sends a response, may be extended response, as requested by the command. - * - * @param context of current command. - */ - public void sendOutgoing(KMContext context, APDU apdu) { - // Initialize source - short srcLength = context.getBufferLength(); - if (srcLength > MAX_LENGTH) { - throw new KMException(ISO7816.SW_WRONG_LENGTH); + private void resetData(){ + short index = 0; + while (index < data.length){ + data[index] = KMType.INVALID_VALUE; + index++; + } + } + /** Sends a response, may be extended response, as requested by the command. */ + public static void sendOutgoing(APDU apdu) { + if (bufferLength > MAX_IO_LENGTH) { + ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); } // Send data - byte[] srcBuffer = context.getBuffer(); apdu.setOutgoing(); - apdu.setOutgoingLength(srcLength); - apdu.sendBytesLong(srcBuffer, (short) 0, srcLength); + // short currentBlockSize = apdu.getOutBlockSize(); + apdu.setOutgoingLength(bufferLength); + apdu.sendBytesLong(buffer, bufferStartOffset, bufferLength); } - /** - * Receives data, which can be extended data, as requested by the command instance. - * - * @param context of current command. - */ - public void receiveIncoming(KMContext context, APDU apdu) { - // Initialize source + /** Receives data, which can be extended data, as requested by the command instance. */ + public static void receiveIncoming(APDU apdu) { byte[] srcBuffer = apdu.getBuffer(); - // Initialize destination - byte[] destBuffer = context.getBuffer(); - short destOffset = (short) 0; - - // Receive data short recvLen = apdu.setIncomingAndReceive(); short srcOffset = apdu.getOffsetCdata(); - short srcLength = apdu.getIncomingLength(); - if (srcLength > MAX_LENGTH) { - throw new KMException(ISO7816.SW_WRONG_LENGTH); + bufferLength = apdu.getIncomingLength(); + short index = bufferStartOffset; + // Receive data + if (bufferLength > MAX_IO_LENGTH) { + ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); } - while (recvLen > 0) { - Util.arrayCopyNonAtomic(srcBuffer, srcOffset, destBuffer, destOffset, recvLen); - destOffset += recvLen; + while (recvLen > 0 && ((short) (index - bufferStartOffset) < bufferLength)) { + Util.arrayCopyNonAtomic(srcBuffer, srcOffset, buffer, index, recvLen); + index += recvLen; recvLen = apdu.receiveBytes(srcOffset); } - // Update the Context - context.setBufferLength(srcLength); } - /** - * Getter for keymaster state. - * - * @return keymasterState - current state of the applet. - */ - private byte getKeymasterState() { - return keymasterState; + private void processGetHwInfoCmd(APDU apdu) { + // No arguments expected + final byte[] JavacardKeymasterDevice = { + 0x4A, 0x61, 0x76, 0x61, 0x63, 0x61, 0x72, 0x64, 0x4B, 0x65, 0x79, 0x6D, 0x61, 0x73, 0x74, + 0x65, 0x72, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, + }; + final byte[] Google = {0x47, 0x6F, 0x6F, 0x67, 0x6C, 0x65}; + + // Make the response + short respPtr = KMArray.instance((short) 3); + KMArray resp = KMArray.cast(respPtr); + resp.add((short) 0, KMEnum.instance(KMType.HARDWARE_TYPE, KMType.STRONGBOX)); + resp.add( + (short) 1, + KMByteBlob.instance( + JavacardKeymasterDevice, (short) 0, (short) JavacardKeymasterDevice.length)); + resp.add((short) 2, KMByteBlob.instance(Google, (short) 0, (short) Google.length)); + // Encode the response - actual bufferLength is 86 + bufferLength = encoder.encode(respPtr, buffer, bufferStartOffset); + // send buffer to master + sendOutgoing(apdu); + } + + private void processAddRngEntropyCmd(APDU apdu) { + // Receive the incoming request fully from the master. + receiveIncoming(apdu); + // Re-purpose the apdu buffer as scratch pad. + byte[] scratchPad = apdu.getBuffer(); + Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) apdu.getBuffer().length, (byte) 0); + // Argument 1 + short argsProto = KMArray.instance((short) 1); + KMArray.cast(argsProto).add((short) 0, KMByteBlob.exp()); + // Decode the argument + short args = decoder.decode(argsProto, buffer, bufferStartOffset, bufferLength); + // Process + KMByteBlob blob = KMByteBlob.cast(KMArray.cast(args).get((short) 0)); + // Maximum 2KiB of seed is allowed. + if (blob.length() > MAX_SEED_SIZE) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + cryptoProvider.addRngEntropy(blob.getBuffer(), blob.getStartOff(), blob.length()); + } + + private void processProvisionCmd(APDU apdu) { + // Receive the incoming request fully from the master into buffer. + receiveIncoming(apdu); + // Re-purpose the apdu buffer as scratch pad. + byte[] scratchPad = apdu.getBuffer(); + // Arguments + short keyparams = KMKeyParameters.exp(); + short keyFormat = KMEnum.instance(KMType.KEY_FORMAT); + short keyBlob = KMByteBlob.exp(); + short argsProto = KMArray.instance((short) 3); + KMArray.cast(argsProto).add((short) 0, keyparams); + KMArray.cast(argsProto).add((short) 1, keyFormat); + KMArray.cast(argsProto).add((short) 2, keyBlob); + // Decode the argument + short args = decoder.decode(argsProto, buffer, bufferStartOffset, bufferLength); + // key params should have os patch, os version and verified root of trust + + // TODO execute the function + // Change the state to ACTIVE + if (keymasterState == KMKeymasterApplet.FIRST_SELECT_STATE) { + provisionDone = true; + if (setBootParamsDone) { + keymasterState = KMKeymasterApplet.ACTIVE_STATE; + } + } + } + + private void processGetKeyCharacteristicsCmd(APDU apdu) { + // Receive the incoming request fully from the master. + receiveIncoming(apdu); + // Re-purpose the apdu buffer as scratch pad. + byte[] scratchPad = apdu.getBuffer(); + // Arguments + tmpVariables[0] = KMArray.instance((short) 3); + KMArray.cast(tmpVariables[0]).add((short) 0, KMByteBlob.exp()); + KMArray.cast(tmpVariables[0]).add((short) 1, KMByteBlob.exp()); + KMArray.cast(tmpVariables[0]).add((short) 2, KMByteBlob.exp()); + // Decode the arguments + tmpVariables[0] = decoder.decode(tmpVariables[0], buffer, bufferStartOffset, bufferLength); + data[KEY_BLOB] = KMArray.cast(tmpVariables[0]).get((short) 0); + data[APP_ID] = KMArray.cast(tmpVariables[0]).get((short) 1); + data[APP_DATA] = KMArray.cast(tmpVariables[0]).get((short) 2); + if (!KMByteBlob.cast(data[APP_ID]).isValid()) { + data[APP_ID] = KMType.INVALID_VALUE; + } + if (!KMByteBlob.cast(data[APP_DATA]).isValid()) { + data[APP_DATA] = KMType.INVALID_VALUE; + } + // Parse Key Blob + parseEncryptedKeyBlob(scratchPad); + // Check Version and Patch Level + checkVersionAndPatchLevel(scratchPad); + // make response. + tmpVariables[0] = KMArray.instance((short) 2); + KMArray.cast(tmpVariables[0]).add((short) 0, KMInteger.uint_16(KMError.OK)); + KMArray.cast(tmpVariables[0]).add((short) 1, data[KEY_CHARACTERISTICS]); + // Encode the response + bufferLength = encoder.encode(tmpVariables[0], buffer, bufferStartOffset); + sendOutgoing(apdu); + } + + private void processGetHmacSharingParamCmd(APDU apdu) { + } + + private void processDeleteAllKeysCmd(APDU apdu) { + // No arguments + repository.removeAllAuthTags(); + // Send ok + sendError(apdu, KMError.OK); + } + + 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()); + // Decode the argument + short args = decoder.decode(argsProto, buffer, bufferStartOffset, bufferLength); + // Process + data[KEY_BLOB] = KMArray.cast(args).get((short) 0); + tmpVariables[0] = KMByteBlob.cast(data[KEY_BLOB]).getStartOff(); + tmpVariables[1] = KMArray.instance((short) 5); + KMArray.cast(tmpVariables[1]).add(KMKeymasterApplet.KEY_BLOB_SECRET, KMByteBlob.exp()); + KMArray.cast(tmpVariables[1]).add(KMKeymasterApplet.KEY_BLOB_AUTH_TAG, KMByteBlob.exp()); + KMArray.cast(tmpVariables[1]).add(KMKeymasterApplet.KEY_BLOB_NONCE, KMByteBlob.exp()); + tmpVariables[2] = KMKeyCharacteristics.exp(); + KMArray.cast(tmpVariables[1]).add(KMKeymasterApplet.KEY_BLOB_KEYCHAR, tmpVariables[2]); + KMArray.cast(tmpVariables[1]).add(KMKeymasterApplet.KEY_BLOB_PUB_KEY, KMByteBlob.exp()); + data[KEY_BLOB] = + decoder.decodeArray( + tmpVariables[1], + KMByteBlob.cast(data[KEY_BLOB]).getBuffer(), + KMByteBlob.cast(data[KEY_BLOB]).getStartOff(), + KMByteBlob.cast(data[KEY_BLOB]).length()); + tmpVariables[0] = KMArray.cast(data[KEY_BLOB]).length(); + if (tmpVariables[0] < 4) { + KMException.throwIt(KMError.INVALID_KEY_BLOB); + } + // Validate Auth Tag + data[AUTH_TAG] = KMArray.cast(data[KEY_BLOB]).get(KEY_BLOB_AUTH_TAG); + if (!repository.validateAuthTag(data[AUTH_TAG])) { + KMException.throwIt(KMError.INVALID_KEY_BLOB); + } + // delete the auth tag + repository.removeAuthTag(data[AUTH_TAG]); + // Send ok + sendError(apdu, KMError.OK); + } + + private void processComputeSharedHmacCmd(APDU apdu) { + } + + private void processUpgradeKeyCmd(APDU apdu) { + } + + private void processExportKeyCmd(APDU apdu) { + } + + private void processImportWrappedKeyCmd(APDU apdu) { + } + + private void processAttestKeyCmd(APDU apdu) {} + + private void processDestroyAttIdsCmd(APDU apdu) {} + + private void processVerifyAuthorizationCmd(APDU apdu) { + sendError(apdu, KMError.UNIMPLEMENTED); + } + + private void processAbortOperationCmd(APDU apdu) {} + + private void processFinishOperationCmd(APDU apdu) { + // TODO AES GCM + + } + + + private void processUpdateOperationCmd(APDU apdu) { + } + + private void processBeginOperationCmd(APDU apdu) {} + + + private void processImportKeyCmd(APDU apdu) { + if (repository.keyBlobCount > repository.MAX_BLOB_STORAGE) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + // Receive the incoming request fully from the master into buffer. + receiveIncoming(apdu); + byte[] scratchPad = apdu.getBuffer(); + tmpVariables[1] = KMArray.instance((short) 3); + // Arguments + tmpVariables[2] = KMKeyParameters.exp(); + KMArray.cast(tmpVariables[1]).add((short) 0, tmpVariables[2]); + KMArray.cast(tmpVariables[1]).add((short) 1, KMEnum.instance(KMType.KEY_FORMAT)); + KMArray.cast(tmpVariables[1]).add((short) 2, KMByteBlob.exp()); + // Decode the arguments + tmpVariables[2] = decoder.decode(tmpVariables[1], buffer, bufferStartOffset, bufferLength); + data[KEY_PARAMETERS] = KMArray.cast(tmpVariables[2]).get((short) 0); + tmpVariables[3] = KMArray.cast(tmpVariables[2]).get((short) 1); + data[IMPORTED_KEY_BLOB] = KMArray.cast(tmpVariables[2]).get((short) 2); + // Key format must be RAW format - X509 and PKCS8 not implemented. + tmpVariables[3] = KMEnum.cast(tmpVariables[3]).getVal(); + if (tmpVariables[3] != KMType.RAW) { + KMException.throwIt(KMError.UNIMPLEMENTED); + } + data[ORIGIN] = KMType.IMPORTED; + importKey(apdu, scratchPad); + } + + private void importKey(APDU apdu, byte[] scratchPad) { + // get algorithm + tmpVariables[3] = KMEnumTag.getValue(KMType.ALGORITHM, data[KEY_PARAMETERS]); + if (tmpVariables[3] == KMType.INVALID_VALUE) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + // Check algorithm and dispatch to appropriate handler. + switch (tmpVariables[3]) { + case KMType.RSA: + importRSAKey(scratchPad); + break; + case KMType.AES: + importAESKey(scratchPad); + break; + case KMType.DES: + importTDESKey(scratchPad); + break; + case KMType.HMAC: + importHmacKey(scratchPad); + break; + case KMType.EC: + importECKeys(scratchPad); + break; + default: + KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM); + break; + } + // create key blob + createEncryptedKeyBlob(scratchPad); + // persist auth tag for rollback resistance. + repository.persistAuthTag(data[AUTH_TAG]); + // prepare the response + tmpVariables[0] = KMArray.instance((short) 3); + KMArray.cast(tmpVariables[0]).add((short) 0, KMInteger.uint_16(KMError.OK)); + KMArray.cast(tmpVariables[0]).add((short) 1, data[KEY_BLOB]); + KMArray.cast(tmpVariables[0]).add((short) 2, data[KEY_CHARACTERISTICS]); + // Encode the response + bufferLength = encoder.encode(tmpVariables[0], buffer, bufferStartOffset); + sendOutgoing(apdu); + } + + private void importECKeys(byte[] scratchPad) { + // Decode key material + tmpVariables[0] = KMArray.instance((short) 3); + KMArray.cast(tmpVariables[0]).add((short) 0, KMByteBlob.exp()); // secret + KMArray.cast(tmpVariables[0]).add((short) 1, KMByteBlob.exp()); // public key + KMArray.cast(tmpVariables[0]).add((short) 2, KMEnumTag.exp()); // curve + tmpVariables[0] = + decoder.decode( + tmpVariables[0], + KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).getBuffer(), + KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).getStartOff(), + KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).length()); + data[SECRET] = KMArray.cast(tmpVariables[0]).get((short) 0); + data[PUB_KEY] = KMArray.cast(tmpVariables[0]).get((short) 1); + tmpVariables[1] = KMArray.cast(tmpVariables[0]).get((short) 2); + tmpVariables[1] = KMEnumTag.cast(tmpVariables[1]).getValue(); + // curve must be P_256 + if (tmpVariables[1] != KMType.P_256) { + KMException.throwIt(KMError.UNSUPPORTED_EC_CURVE); + } + // initialize 256 bit p256 key for given private key and public key. + ECPrivateKey ecKey = + cryptoProvider.createEcKey( + KMByteBlob.cast(data[SECRET]).getBuffer(), + KMByteBlob.cast(data[SECRET]).getStartOff(), + KMByteBlob.cast(data[SECRET]).length()); + tmpVariables[4] = 0; // index for update list in scratchPad + // check whether the keysize tag is present in key parameters. + tmpVariables[2] = + KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); + if (tmpVariables[2] != KMType.INVALID_VALUE) { + if (tmpVariables[2] != 256) { + KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH); + } + } else { + // add the key size to scratchPad + tmpVariables[5] = KMInteger.uint_16((short) 256); + tmpVariables[6] = KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, tmpVariables[5]); + Util.setShort(scratchPad, tmpVariables[4], tmpVariables[6]); + tmpVariables[4] += 2; + } + // check the curve if present in key parameters. + tmpVariables[3] = KMEnumTag.getValue(KMType.ECCURVE, data[KEY_PARAMETERS]); + if (tmpVariables[3] != KMType.INVALID_VALUE) { + if (tmpVariables[3] != tmpVariables[1]) { + KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH); + } + } else { + // add the curve to scratchPad + tmpVariables[5] = KMEnumTag.instance(KMType.ECCURVE, KMType.P_256); + Util.setShort(scratchPad, tmpVariables[4], tmpVariables[5]); + tmpVariables[4] += 2; + } + // add scratch pad to key parameters + updateKeyParameters(scratchPad, tmpVariables[4]); + // validate updated key parameters. + validateECKeys(scratchPad); + data[KEY_BLOB] = KMArray.instance((short) 5); + KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_PUB_KEY, data[PUB_KEY]); + } + + private void importHmacKey(byte[] scratchPad) { + // Get Key + tmpVariables[0] = KMArray.instance((short) 1); + KMArray.cast(tmpVariables[0]).add((short) 0, KMByteBlob.exp()); // secret + tmpVariables[0] = + decoder.decode( + tmpVariables[0], + KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).getBuffer(), + KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).getStartOff(), + KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).length()); + data[SECRET] = KMArray.cast(tmpVariables[0]).get((short) 0); + // create HMAC key of up to 512 bit + HMACKey hmacKey = + cryptoProvider.createHMACKey( + KMByteBlob.cast(data[SECRET]).getBuffer(), + KMByteBlob.cast(data[SECRET]).getStartOff(), + KMByteBlob.cast(data[SECRET]).length()); + tmpVariables[4] = 0; // index in scratchPad for update params + // check the keysize tag if present in key parameters. + tmpVariables[2] = + KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); + if (tmpVariables[2] != KMType.INVALID_VALUE) { + if (!(tmpVariables[2] >= 64 && tmpVariables[2] <= 512 && tmpVariables[2] % 8 == 0)) { + KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH); + } + } else { + // 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]); + Util.setShort(scratchPad, tmpVariables[4], tmpVariables[6]); + tmpVariables[4] += 2; + } + // update the key parameters list + updateKeyParameters(scratchPad, tmpVariables[4]); + // validate HMAC Key parameters + validateHmacKey(scratchPad); + + data[KEY_BLOB] = KMArray.instance((short) 4); + } + + private void importTDESKey(byte[] scratchPad) { + // Decode Key Material + tmpVariables[0] = KMArray.instance((short) 1); + KMArray.cast(tmpVariables[0]).add((short) 0, KMByteBlob.exp()); // secret + tmpVariables[0] = + decoder.decode( + tmpVariables[0], + KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).getBuffer(), + KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).getStartOff(), + KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).length()); + data[SECRET] = KMArray.cast(tmpVariables[0]).get((short) 0); + DESKey desKey = + cryptoProvider.createTDESKey( + KMByteBlob.cast(data[SECRET]).getBuffer(), + KMByteBlob.cast(data[SECRET]).getStartOff(), + KMByteBlob.cast(data[SECRET]).length()); + tmpVariables[4] = 0; // index in scratchPad for update params + // check the keysize tag if present in key parameters. + tmpVariables[2] = + KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); + if (tmpVariables[2] != KMType.INVALID_VALUE) { + if (tmpVariables[2] != 168) { + KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH); + } + } else { + // add the key size to scratchPad + tmpVariables[5] = KMInteger.uint_16((short) 168); + tmpVariables[6] = KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, tmpVariables[5]); + Util.setShort(scratchPad, tmpVariables[4], tmpVariables[6]); + tmpVariables[4] += 2; + } + // update the key parameters list + updateKeyParameters(scratchPad, tmpVariables[4]); + // validate TDES Key parameters + validateTDESKey(scratchPad); + + data[KEY_BLOB] = KMArray.instance((short) 4); + } + + private void importAESKey(byte[] scratchPad) { + // Get Key + tmpVariables[0] = KMArray.instance((short) 1); + KMArray.cast(tmpVariables[0]).add((short) 0, KMByteBlob.exp()); // secret + tmpVariables[0] = + decoder.decode( + tmpVariables[0], + KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).getBuffer(), + KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).getStartOff(), + KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).length()); + data[SECRET] = KMArray.cast(tmpVariables[0]).get((short) 0); + // create 128 or 256 bit AES key + AESKey aesKey = + cryptoProvider.createAESKey( + KMByteBlob.cast(data[SECRET]).getBuffer(), + KMByteBlob.cast(data[SECRET]).getStartOff(), + KMByteBlob.cast(data[SECRET]).length()); + tmpVariables[4] = 0; // index in scratchPad for update params + // check the keysize tag if present in key parameters. + tmpVariables[2] = + KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); + if (tmpVariables[2] != KMType.INVALID_VALUE) { + if (tmpVariables[2] != 128 && tmpVariables[2] != 256) { + 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]); + Util.setShort(scratchPad, tmpVariables[4], tmpVariables[6]); + tmpVariables[4] += 2; + } + // update the key parameters list + updateKeyParameters(scratchPad, tmpVariables[4]); + // validate AES Key parameters + validateAESKey(scratchPad); + data[KEY_BLOB] = KMArray.instance((short) 4); + } + + private void importRSAKey(byte[] scratchPad) { + // Decode key material + tmpVariables[0] = KMArray.instance((short) 2); + KMArray.cast(tmpVariables[0]).add((short) 0, KMByteBlob.exp()); // secret = private exponent + KMArray.cast(tmpVariables[0]).add((short) 1, KMByteBlob.exp()); // modulus + tmpVariables[0] = + decoder.decode( + tmpVariables[0], + KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).getBuffer(), + KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).getStartOff(), + KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).length()); + data[SECRET] = KMArray.cast(tmpVariables[0]).get((short) 0); + data[PUB_KEY] = KMArray.cast(tmpVariables[0]).get((short) 1); + tmpVariables[4] = 0; // index in scratchPad for update parameters. + // validate public exponent if present in key params - it must be 0x010001 + tmpVariables[2] = + KMIntegerTag.getValue( + scratchPad, + (short) 10, // using offset 10 as first 10 bytes reserved for update params + KMType.ULONG_TAG, + KMType.RSA_PUBLIC_EXPONENT, + data[KEY_PARAMETERS]); + if (tmpVariables[2] != KMTag.INVALID_VALUE) { + if (tmpVariables[2] != 4 + || Util.getShort(scratchPad, (short) 10) != 0x01 + || Util.getShort(scratchPad, (short) 12) != 0x01) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + } else { + // add public exponent to scratchPad + Util.setShort(scratchPad, (short) 10, (short) 0x01); + Util.setShort(scratchPad, (short) 12, (short) 0x01); + tmpVariables[5] = KMInteger.uint_32(scratchPad, (short) 10); + tmpVariables[6] = + KMIntegerTag.instance(KMType.ULONG_TAG, KMType.RSA_PUBLIC_EXPONENT, tmpVariables[5]); + Util.setShort(scratchPad, tmpVariables[4], tmpVariables[6]); + tmpVariables[4] += 2; + } + + // initialize 2048 bit private key for given private exp and modulus. + RSAPrivateKey rsaKey = + cryptoProvider.createRsaKey( + KMByteBlob.cast(data[PUB_KEY]).getBuffer(), + KMByteBlob.cast(data[PUB_KEY]).getStartOff(), + KMByteBlob.cast(data[PUB_KEY]).length(), + KMByteBlob.cast(data[SECRET]).getBuffer(), + KMByteBlob.cast(data[SECRET]).getStartOff(), + KMByteBlob.cast(data[SECRET]).length()); + // check the keysize tag if present in key parameters. + tmpVariables[2] = + KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); + if (tmpVariables[2] != KMType.INVALID_VALUE) { + if (tmpVariables[2] != 2048) { + KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH); + } + } else { + // add the key size to scratchPad + tmpVariables[5] = KMInteger.uint_16((short) 2048); + tmpVariables[6] = KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, tmpVariables[5]); + Util.setShort(scratchPad, tmpVariables[4], tmpVariables[6]); + tmpVariables[4] += 2; + } + // update the key parameters list + updateKeyParameters(scratchPad, tmpVariables[4]); + // validate RSA Key parameters + validateRSAKey(scratchPad); + data[KEY_BLOB] = KMArray.instance((short) 5); + KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_PUB_KEY, data[PUB_KEY]); + } + + private void updateKeyParameters(byte[] ptrArr, short len) { + if (len == 0) { + return; // nothing to update + } + // Create Update Param array and copy current params + tmpVariables[0] = KMKeyParameters.cast(data[KEY_PARAMETERS]).getVals(); + tmpVariables[1] = (short) (KMArray.cast(tmpVariables[0]).length() + (short) (len / 2)); + tmpVariables[1] = KMArray.instance(tmpVariables[1]); // update params + tmpVariables[2] = KMArray.cast(tmpVariables[0]).length(); + tmpVariables[3] = 0; + // copy the existing key parameters to updated array + while (tmpVariables[3] < tmpVariables[2]) { + tmpVariables[4] = KMArray.cast(tmpVariables[0]).get(tmpVariables[3]); + KMArray.cast(tmpVariables[1]).add(tmpVariables[3], tmpVariables[4]); + tmpVariables[3]++; + } + // copy new parameters to updated array + tmpVariables[2] = KMArray.cast(tmpVariables[1]).length(); + tmpVariables[5] = 0; // index in ptrArr + while (tmpVariables[3] < tmpVariables[2]) { + tmpVariables[4] = Util.getShort(ptrArr, tmpVariables[5]); + KMArray.cast(tmpVariables[1]).add(tmpVariables[3], tmpVariables[4]); + tmpVariables[3]++; + tmpVariables[5] += 2; + } + // replace with updated key parameters. + data[KEY_PARAMETERS] = KMKeyParameters.instance(tmpVariables[1]); + } + + // TODO Add Signature verification. + private void processSetBootParamsCmd(APDU apdu) { + receiveIncoming(apdu); + // Re-purpose the apdu buffer as scratch pad. + 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 Verified Boot Key + // short bootKeyExp = KMByteBlob.exp(); + tmpVariables[2] = KMByteBlob.exp(); + // Argument 4 Verified Boot Hash + // short bootHashExp = KMByteBlob.exp(); + tmpVariables[3] = KMByteBlob.exp(); + // Argument 5 Verified Boot State + // short bootStateExp = KMEnum.instance(KMType.VERIFIED_BOOT_STATE); + tmpVariables[4] = KMEnum.instance(KMType.VERIFIED_BOOT_STATE); + // Argument 6 Device Locked + // short deviceLockedExp = KMEnum.instance(KMType.DEVICE_LOCKED); + tmpVariables[5] = KMEnum.instance(KMType.DEVICE_LOCKED); + // Array of expected arguments + short argsProto = KMArray.instance((short) 6); + KMArray.cast(argsProto).add((short) 0, tmpVariables[0]); + KMArray.cast(argsProto).add((short) 1, tmpVariables[1]); + KMArray.cast(argsProto).add((short) 2, tmpVariables[2]); + KMArray.cast(argsProto).add((short) 3, tmpVariables[3]); + KMArray.cast(argsProto).add((short) 4, tmpVariables[4]); + KMArray.cast(argsProto).add((short) 5, tmpVariables[5]); + // Decode the arguments + //System.out.println("Process boot params buffer: "+byteArrayToHexString(buffer)); + short args = decoder.decode(argsProto, buffer, bufferStartOffset, 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 verifiedBootKeyPtr = KMArray.cast(args).get((short) 2); + tmpVariables[2] = KMArray.cast(args).get((short) 2); + // short verifiedBootHashPtr = KMArray.cast(args).get((short) 3); + tmpVariables[3] = KMArray.cast(args).get((short) 3); + // short verifiedBootStatePtr = KMArray.cast(args).get((short) 4); + tmpVariables[4] = KMArray.cast(args).get((short) 4); + // short deviceLockedPtr = KMArray.cast(args).get((short) 5); + tmpVariables[5] = KMArray.cast(args).get((short) 5); + if (KMByteBlob.cast(tmpVariables[2]).length() > repository.BOOT_KEY_MAX_SIZE) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + if (KMByteBlob.cast(tmpVariables[3]).length() > repository.BOOT_HASH_MAX_SIZE) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + // Begin transaction + JCSystem.beginTransaction(); + KMInteger.cast(tmpVariables[0]).value(repository.osVersion, (short) 0); + KMInteger.cast(tmpVariables[1]).value(repository.osPatch, (short) 0); + //KMInteger.cast(valPtr).getValue(repository.osVersion, (short) 0, (short) 4); + //valPtr = KMIntegerTag.cast(tmpVariables[1]).getValue(); + //KMInteger.cast(valPtr).getValue(repository.osPatch, (short) 0, (short) 4); + repository.actualBootKeyLength = KMByteBlob.cast(tmpVariables[2]).length(); + KMByteBlob.cast(tmpVariables[2]) + .getValue(repository.verifiedBootKey, (short) 0, repository.actualBootKeyLength); + repository.actualBootHashLength = KMByteBlob.cast(tmpVariables[3]).length(); + KMByteBlob.cast(tmpVariables[3]) + .getValue(repository.verifiedBootHash, (short) 0, repository.actualBootHashLength); + byte enumVal = KMEnum.cast(tmpVariables[4]).getVal(); + if (enumVal == KMTag.SELF_SIGNED_BOOT) { + repository.selfSignedBootFlag = true; + repository.verifiedBootFlag = false; + } else { + repository.selfSignedBootFlag = false; + repository.verifiedBootFlag = true; + } + enumVal = KMEnum.cast(tmpVariables[5]).getVal(); + if (enumVal == KMType.DEVICE_LOCKED_TRUE) { + repository.deviceLockedFlag = true; + } else { + repository.deviceLockedFlag = false; + } + if (keymasterState == KMKeymasterApplet.FIRST_SELECT_STATE) { + setBootParamsDone = true; + if (provisionDone) { + keymasterState = KMKeymasterApplet.ACTIVE_STATE; + } + } + // end transaction + JCSystem.commitTransaction(); + } + + private static void processGenerateKey(APDU apdu) { + // before generating key, check whether max count reached + if (repository.keyBlobCount > repository.MAX_BLOB_STORAGE) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + // Receive the incoming request fully from the master into buffer. + receiveIncoming(apdu); + // Re-purpose the apdu buffer as scratch pad. + byte[] scratchPad = apdu.getBuffer(); + // Argument + tmpVariables[0] = KMKeyParameters.exp(); + // Array of expected arguments + tmpVariables[1] = KMArray.instance((short) 1); + KMArray.cast(tmpVariables[1]).add((short) 0, tmpVariables[0]); + // Decode the argument + tmpVariables[2] = decoder.decode(tmpVariables[1], buffer, bufferStartOffset, bufferLength); + data[KEY_PARAMETERS] = KMArray.cast(tmpVariables[2]).get((short) 0); + // get algorithm + tmpVariables[3] = KMEnumTag.getValue(KMType.ALGORITHM, data[KEY_PARAMETERS]); + if (tmpVariables[3] == KMType.INVALID_VALUE) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + // Check algorithm and dispatch to appropriate handler. + switch (tmpVariables[3]) { + case KMType.RSA: + generateRSAKey(scratchPad); + break; + case KMType.AES: + generateAESKey(scratchPad); + break; + case KMType.DES: + generateTDESKey(scratchPad); + break; + case KMType.HMAC: + generateHmacKey(scratchPad); + break; + case KMType.EC: + generateECKeys(scratchPad); + break; + default: + KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM); + break; + } + // create key blob + data[ORIGIN] = KMType.GENERATED; + createEncryptedKeyBlob(scratchPad); + // persist auth tag for rollback resistance. + repository.persistAuthTag(data[AUTH_TAG]); + // prepare the response + tmpVariables[0] = KMArray.instance((short) 3); + KMArray.cast(tmpVariables[0]).add((short) 0, KMInteger.uint_16(KMError.OK)); + KMArray.cast(tmpVariables[0]).add((short) 1, data[KEY_BLOB]); + KMArray.cast(tmpVariables[0]).add((short) 2, data[KEY_CHARACTERISTICS]); + // Encode the response + bufferLength = encoder.encode(tmpVariables[0], buffer, bufferStartOffset); + sendOutgoing(apdu); + } + + private static void validateRSAKey(byte[] scratchPad) { + // Read key size + tmpVariables[0] = + KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); + if (tmpVariables[0] == KMTag.INVALID_VALUE) { + KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); + } + if (tmpVariables[0] != 2048) { + KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); + } + // Read public exponent into scratch pad + tmpVariables[1] = + KMIntegerTag.getValue( + scratchPad, + (short) 0, + KMType.ULONG_TAG, + KMType.RSA_PUBLIC_EXPONENT, + data[KEY_PARAMETERS]); + if ((tmpVariables[1] == KMTag.INVALID_VALUE) || (tmpVariables[1] != 4)) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + // Only exponent support is F4 - 65537 which is 0x00010001. + if (Util.getShort(scratchPad, (short) 0) != 0x01 + || Util.getShort(scratchPad, (short) 2) != 0x01) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + } + + // Generate key handlers + private static void generateRSAKey(byte[] scratchPad) { + // Validate RSA Key + validateRSAKey(scratchPad); + // Now generate 2048 bit RSA keypair for the given exponent + KeyPair rsaKey = cryptoProvider.createRsaKeyPair(); + // store the pub exponent + data[RSA_PUB_EXPONENT] = KMByteBlob.instance(scratchPad, (short) 0, tmpVariables[1]); + // extract modulus + tmpVariables[0] = ((RSAPrivateKey) rsaKey.getPrivate()).getModulus(scratchPad, (short) 0); + data[PUB_KEY] = KMByteBlob.instance(scratchPad, (short) 0, tmpVariables[0]); + // extract private key + tmpVariables[0] = ((RSAPrivateKey) rsaKey.getPrivate()).getExponent(scratchPad, (short) 0); + data[SECRET] = KMByteBlob.instance(scratchPad, (short) 0, tmpVariables[0]); + data[KEY_BLOB] = KMArray.instance((short) 5); + KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_PUB_KEY, data[PUB_KEY]); + } + + private static void validateAESKey(byte[] scratchPad) { + // Read key size + tmpVariables[0] = + KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); + if (tmpVariables[0] == KMTag.INVALID_VALUE) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + if ((tmpVariables[0] != 256) && (tmpVariables[0] != 128)) { + KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); + } + // Read Block mode - array of byte values + tmpVariables[1] = + KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.BLOCK_MODE, data[KEY_PARAMETERS]); + if (tmpVariables[1] != KMTag.INVALID_VALUE) { // block mode specified + // Find Minimum Mac length + tmpVariables[2] = + KMKeyParameters.findTag(KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, data[KEY_PARAMETERS]); + // If block modes contain GCM mode + if(KMEnumArrayTag.cast(tmpVariables[1]).contains(KMType.GCM)){ + // minimum mac length must be specified + if (tmpVariables[2] == KMTag.INVALID_VALUE) { + KMException.throwIt(KMError.MISSING_MIN_MAC_LENGTH); + } + tmpVariables[3] = KMIntegerTag.cast(tmpVariables[2]).getValue(); + // Validate the MIN_MAC_LENGTH for AES - should be multiple of 8, less then 128 bits + // and greater the 96 bits + if(KMInteger.cast(tmpVariables[3]).getSignificantShort() != 0 || + KMInteger.cast(tmpVariables[3]).getShort() > 128 || + KMInteger.cast(tmpVariables[3]).getShort() < 96 || + (KMInteger.cast(tmpVariables[3]).getShort() % 8) != 0){ + KMException.throwIt(KMError.UNSUPPORTED_MIN_MAC_LENGTH); + } + }else{ // No GCM mode then no minimum mac length must be specified + if (tmpVariables[2] != KMTag.INVALID_VALUE) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + } + } + } + + private static void generateAESKey(byte[] scratchPad) { + validateAESKey(scratchPad); + tmpVariables[0] = + KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); + AESKey aesKey = cryptoProvider.createAESKey(tmpVariables[0]); + tmpVariables[0] = aesKey.getKey(scratchPad, (short) 0); + data[SECRET] = KMByteBlob.instance(scratchPad, (short) 0, tmpVariables[0]); + data[KEY_BLOB] = KMArray.instance((short) 4); + } + + private static void validateECKeys(byte[] scratchPad) { + // Read key size + tmpVariables[0] = + KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); + tmpVariables[1] = KMEnumTag.getValue(KMType.ECCURVE, data[KEY_PARAMETERS]); + if ((tmpVariables[0] == KMTag.INVALID_VALUE) && (tmpVariables[1] == KMType.INVALID_VALUE)){ + KMException.throwIt(KMError.INVALID_ARGUMENT); + }else if((tmpVariables[1] != KMTag.INVALID_VALUE) && (tmpVariables[1] != KMType.P_256)){ + KMException.throwIt(KMError.UNSUPPORTED_EC_CURVE); + }else if ((tmpVariables[0] != KMTag.INVALID_VALUE) && (tmpVariables[0] != (short)256)){ + KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); + } + } + + private static void generateECKeys(byte[] scratchPad) { + validateECKeys(scratchPad); + KeyPair ecKey = cryptoProvider.createECKeyPair(); + tmpVariables[5] = ((ECPublicKey) ecKey.getPublic()).getW(scratchPad, (short) 0); + data[PUB_KEY] = KMByteBlob.instance(scratchPad, (short) 0, tmpVariables[5]); + tmpVariables[5] = ((ECPrivateKey) ecKey.getPrivate()).getS(scratchPad, (short) 0); + data[SECRET] = KMByteBlob.instance(scratchPad, (short) 0, tmpVariables[5]); + data[KEY_BLOB] = KMArray.instance((short) 5); + KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_PUB_KEY, data[PUB_KEY]); + } + + private static void validateTDESKey(byte[] scratchPad) { + // Read Minimum Mac length - it must not be present + tmpVariables[0] = + KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, data[KEY_PARAMETERS]); + if (tmpVariables[0] != KMType.INVALID_VALUE) { + KMException.throwIt(KMError.INVALID_TAG); + } + // Read keysize + tmpVariables[1] = + KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); + if (tmpVariables[1] == KMType.INVALID_VALUE) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + if (tmpVariables[1] != 168) { + KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); + } + } + + private static void generateTDESKey(byte[] scratchPad) { + validateTDESKey(scratchPad); + DESKey desKey = cryptoProvider.createTDESKey(); + tmpVariables[0] = desKey.getKey(scratchPad, (short) 0); + data[SECRET] = KMByteBlob.instance(scratchPad, (short) 0, tmpVariables[0]); + data[KEY_BLOB] = KMArray.instance((short) 4); + } + + private static void validateHmacKey(byte[] scratchPad) { + // check whether digest sizes are greater then or equal to min mac length. + // Only SHA256 digest must be supported. + if(!KMEnumArrayTag.contains(KMType.DIGEST, KMType.SHA2_256, data[KEY_PARAMETERS])){ + KMException.throwIt(KMError.INCOMPATIBLE_DIGEST); + } + if(KMEnumArrayTag.length(KMType.DIGEST,data[KEY_PARAMETERS]) != 1){ + KMException.throwIt(KMError.INCOMPATIBLE_DIGEST); + } + // Read Minimum Mac length + tmpVariables[0] = + KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, data[KEY_PARAMETERS]); + if (tmpVariables[0] == KMType.INVALID_VALUE) { + KMException.throwIt(KMError.MISSING_MIN_MAC_LENGTH); + } + if (((short) (tmpVariables[0] % 8) != 0) || + (tmpVariables[0] < (short) 64)|| + tmpVariables[0] > (short)256) { + KMException.throwIt(KMError.UNSUPPORTED_MIN_MAC_LENGTH); + } + // Read keysize + tmpVariables[1] = + KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); + if (tmpVariables[1] == KMType.INVALID_VALUE) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + if (!(tmpVariables[1] >= 64 && tmpVariables[1] <= 512 && tmpVariables[1] % 8 == 0)) { + KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); + } + } + + private static void generateHmacKey(byte[] scratchPad) { + validateHmacKey(scratchPad); + tmpVariables[1] = + KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); + // generate HMAC Key + HMACKey hmacKey = cryptoProvider.createHMACKey(tmpVariables[1]); + tmpVariables[0] = hmacKey.getKey(scratchPad, (short) 0); + data[SECRET] = KMByteBlob.instance(scratchPad, (short) 0, tmpVariables[0]); + data[KEY_BLOB] = KMArray.instance((short) 4); + } + + private void checkVersionAndPatchLevel(byte[] scratchPad) { + tmpVariables[0] = + KMIntegerTag.getValue( + scratchPad, (short) 0, KMType.UINT_TAG, KMType.OS_VERSION, data[HW_PARAMETERS]); + 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]); + 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) { + tmpVariables[0] = + KMInteger.instance(repository.osPatch, (short) 0, (short) repository.osPatch.length); + tmpVariables[1] = + KMInteger.instance(repository.osVersion, (short) 0, (short) repository.osVersion.length); + data[HW_PARAMETERS] = + KMKeyParameters.makeHwEnforced( + data[KEY_PARAMETERS], + (byte) data[ORIGIN], + tmpVariables[1], + tmpVariables[0], + scratchPad); + data[SW_PARAMETERS] = KMKeyParameters.makeSwEnforced(data[KEY_PARAMETERS], scratchPad); + data[KEY_CHARACTERISTICS] = KMKeyCharacteristics.instance(); + KMKeyCharacteristics.cast(data[KEY_CHARACTERISTICS]).setHardwareEnforced(data[HW_PARAMETERS]); + KMKeyCharacteristics.cast(data[KEY_CHARACTERISTICS]).setSoftwareEnforced(data[SW_PARAMETERS]); + } + + 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] = + KMByteBlob.instance( + repository.verifiedBootKey, (short) 0, (short) repository.verifiedBootKey.length); + // make hidden key params list + data[HIDDEN_PARAMETERS] = + KMKeyParameters.makeHidden(data[KEY_PARAMETERS], data[ROT], scratchPad); + // make authorization data + makeAuthData(scratchPad); + // encrypt the secret and cryptographically attach that to authorization data + encryptSecret(scratchPad); + + // create key blob array + KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_SECRET, data[SECRET]); + KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_AUTH_TAG, data[AUTH_TAG]); + KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_NONCE, data[NONCE]); + KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_KEYCHAR, data[KEY_CHARACTERISTICS]); + tmpVariables[0] = repository.alloc((short) 1024); // TODO use buffer + tmpVariables[1] = encoder.encode(data[KEY_BLOB], repository.getHeap(), tmpVariables[0]); + data[KEY_BLOB] = KMByteBlob.instance(repository.getHeap(), tmpVariables[0], tmpVariables[1]); + } + + private static void parseEncryptedKeyBlob(byte[] scratchPad) { + tmpVariables[0] = KMByteBlob.cast(data[KEY_BLOB]).getStartOff(); + tmpVariables[1] = KMArray.instance((short) 5); + KMArray.cast(tmpVariables[1]).add(KMKeymasterApplet.KEY_BLOB_SECRET, KMByteBlob.exp()); + KMArray.cast(tmpVariables[1]).add(KMKeymasterApplet.KEY_BLOB_AUTH_TAG, KMByteBlob.exp()); + KMArray.cast(tmpVariables[1]).add(KMKeymasterApplet.KEY_BLOB_NONCE, KMByteBlob.exp()); + tmpVariables[2] = KMKeyCharacteristics.exp(); + KMArray.cast(tmpVariables[1]).add(KMKeymasterApplet.KEY_BLOB_KEYCHAR, tmpVariables[2]); + KMArray.cast(tmpVariables[1]).add(KMKeymasterApplet.KEY_BLOB_PUB_KEY, KMByteBlob.exp()); + data[KEY_BLOB] = + decoder.decodeArray( + tmpVariables[1], + KMByteBlob.cast(data[KEY_BLOB]).getBuffer(), + KMByteBlob.cast(data[KEY_BLOB]).getStartOff(), + KMByteBlob.cast(data[KEY_BLOB]).length()); + tmpVariables[0] = KMArray.cast(data[KEY_BLOB]).length(); + if (tmpVariables[0] < 4) { + KMException.throwIt(KMError.INVALID_KEY_BLOB); + } + // Validate Auth Tag + data[AUTH_TAG] = KMArray.cast(data[KEY_BLOB]).get(KEY_BLOB_AUTH_TAG); + if (!repository.validateAuthTag(data[AUTH_TAG])) { + KMException.throwIt(KMError.INVALID_KEY_BLOB); + } + // initialize data + data[NONCE] = KMArray.cast(data[KEY_BLOB]).get(KEY_BLOB_NONCE); + data[SECRET] = KMArray.cast(data[KEY_BLOB]).get(KEY_BLOB_SECRET); + data[KEY_CHARACTERISTICS] = KMArray.cast(data[KEY_BLOB]).get(KEY_BLOB_KEYCHAR); + data[PUB_KEY] = KMType.INVALID_VALUE; + if (tmpVariables[0] == 5) { + data[PUB_KEY] = KMArray.cast(data[KEY_BLOB]).get(KEY_BLOB_PUB_KEY); + } + data[HW_PARAMETERS] = + 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[HIDDEN_PARAMETERS] = + KMKeyParameters.makeHidden(data[APP_ID], data[APP_DATA], data[ROT], scratchPad); + // make auth data + makeAuthData(scratchPad); + // Decrypt Secret and verify auth tag + decryptSecret(scratchPad); + } + + private static void decryptSecret(byte[] scratchPad) { + // derive master key - stored in derivedKey + tmpVariables[0] = deriveKey(scratchPad); + AESKey derivedKey = + cryptoProvider.createAESKey(repository.getHeap(), data[DERIVED_KEY], tmpVariables[0]); + if (derivedKey == null) { + KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); + } + boolean verification = + cryptoProvider.aesGCMDecrypt( + derivedKey, + KMByteBlob.cast(data[SECRET]).getBuffer(), + KMByteBlob.cast(data[SECRET]).getStartOff(), + KMByteBlob.cast(data[SECRET]).length(), + scratchPad, + (short) 0, + KMByteBlob.cast(data[NONCE]).getBuffer(), + KMByteBlob.cast(data[NONCE]).getStartOff(), + KMByteBlob.cast(data[NONCE]).length(), + repository.getHeap(), + data[AUTH_DATA], + data[AUTH_DATA_LENGTH], + KMByteBlob.cast(data[AUTH_TAG]).getBuffer(), + KMByteBlob.cast(data[AUTH_TAG]).getStartOff(), + KMByteBlob.cast(data[AUTH_TAG]).length()); + if (verification != true) { + KMException.throwIt(KMError.INVALID_KEY_BLOB); + } + // Copy the decrypted secret + data[SECRET] = + KMByteBlob.instance(scratchPad, (short) 0, KMByteBlob.cast(data[SECRET]).length()); + } + + private static void encryptSecret(byte[] scratchPad) { + // make nonce + data[NONCE] = KMByteBlob.instance((short) AES_GCM_NONCE_LENGTH); + data[AUTH_TAG] = KMByteBlob.instance(AES_GCM_AUTH_TAG_LENGTH); + Util.arrayCopyNonAtomic( + KMByteBlob.cast(data[NONCE]).getBuffer(), + KMByteBlob.cast(data[NONCE]).getStartOff(), + scratchPad, + (short) 0, + KMByteBlob.cast(data[NONCE]).length()); + cryptoProvider.newRandomNumber( + KMByteBlob.cast(data[NONCE]).getBuffer(), + KMByteBlob.cast(data[NONCE]).getStartOff(), + KMByteBlob.cast(data[NONCE]).length()); + // derive master key - stored in derivedKey + tmpVariables[0] = deriveKey(scratchPad); + AESKey derivedKey = + cryptoProvider.createAESKey(repository.getHeap(), data[DERIVED_KEY], tmpVariables[0]); + if (derivedKey == null) { + KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); + } + tmpVariables[1] = + cryptoProvider.aesGCMEncrypt( + derivedKey, + KMByteBlob.cast(data[SECRET]).getBuffer(), + KMByteBlob.cast(data[SECRET]).getStartOff(), + KMByteBlob.cast(data[SECRET]).length(), + scratchPad, + (short) 0, + KMByteBlob.cast(data[NONCE]).getBuffer(), + KMByteBlob.cast(data[NONCE]).getStartOff(), + KMByteBlob.cast(data[NONCE]).length(), + repository.getHeap(), + data[AUTH_DATA], + data[AUTH_DATA_LENGTH], + KMByteBlob.cast(data[AUTH_TAG]).getBuffer(), + KMByteBlob.cast(data[AUTH_TAG]).getStartOff(), + KMByteBlob.cast(data[AUTH_TAG]).length()); + if (tmpVariables[1] > 0) { + if (tmpVariables[1] != KMByteBlob.cast(data[SECRET]).length()) { + KMException.throwIt(KMError.INVALID_KEY_BLOB); + } + KMByteBlob.cast(data[SECRET]).setValue(scratchPad, (short) 0, tmpVariables[1]); + } } - /** Setter for keymaster state. */ - private void setKeymasterState(byte keymasterState) { - this.keymasterState = keymasterState; + + private static void makeAuthData(byte[] scratchPad) { + tmpVariables[0] = + addPtrToAAD(KMKeyParameters.cast(data[HW_PARAMETERS]).getVals(), scratchPad, (short) 0); + tmpVariables[0] += + addPtrToAAD( + KMKeyParameters.cast(data[SW_PARAMETERS]).getVals(), scratchPad, tmpVariables[0]); + tmpVariables[0] += + addPtrToAAD( + KMKeyParameters.cast(data[HIDDEN_PARAMETERS]).getVals(), scratchPad, tmpVariables[0]); + // convert scratch pad to KMArray + tmpVariables[1] = KMArray.instance(tmpVariables[0]); + short index = 0; + short objPtr = 0; + while (index < tmpVariables[0]) { + objPtr = Util.getShort(scratchPad, (short) (index * 2)); + KMArray.cast(tmpVariables[1]).add(index, objPtr); + index++; + } + data[AUTH_DATA] = repository.alloc(MAX_AUTH_DATA_SIZE); + short len = encoder.encode(tmpVariables[1], repository.getHeap(), data[AUTH_DATA]); + data[AUTH_DATA_LENGTH] = len; + } + + private static short addPtrToAAD(short dataArrPtr, byte[] aadBuf, short offset) { + short index = (short) (offset * 2); + short tagInd = 0; + short tagPtr = 0; + short arrLen = KMArray.cast(dataArrPtr).length(); + while (tagInd < arrLen) { + tagPtr = KMArray.cast(dataArrPtr).get(tagInd); + Util.setShort(aadBuf, index, tagPtr); + index += 2; + tagInd++; + } + return tagInd; } + + private static short deriveKey(byte[] scratchPad) { + tmpVariables[0] = KMKeyParameters.cast(data[HIDDEN_PARAMETERS]).getVals(); + tmpVariables[1] = repository.alloc((short) 256); + // generate derivation material from hidden parameters + tmpVariables[2] = encoder.encode(tmpVariables[0], repository.getHeap(), tmpVariables[1]); + // create derived key i.e. MAC + tmpVariables[3] = + cryptoProvider.aesCCMSign( + repository.getHeap(), + tmpVariables[1], + tmpVariables[2], + repository.getMasterKeySecret(), + scratchPad, + (short) 0); + if (tmpVariables[3] < 0) { + KMException.throwIt(KMError.UNKNOWN_ERROR); + } + // store the derived secret in data dictionary + data[DERIVED_KEY] = repository.alloc(tmpVariables[3]); + Util.arrayCopyNonAtomic( + scratchPad, (short) 0, repository.getHeap(), data[DERIVED_KEY], tmpVariables[3]); + return tmpVariables[3]; + } + + private static void sendError(APDU apdu, short err) { + bufferLength = encoder.encodeError(err, buffer, bufferStartOffset, (short) 5); + sendOutgoing(apdu); + } + } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMMessenger.java b/Applet/Applet/src/com/android/javacard/keymaster/KMMessenger.java deleted file mode 100644 index 3eb4b548..00000000 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMMessenger.java +++ /dev/null @@ -1,22 +0,0 @@ -/* - * 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; - -public interface KMMessenger { - void receiveIncoming(KMContext context); - void sendOutgoing(KMContext context); -} diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMOperationState.java b/Applet/Applet/src/com/android/javacard/keymaster/KMOperationState.java index 5aaa9948..2684efb3 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMOperationState.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMOperationState.java @@ -16,37 +16,128 @@ package com.android.javacard.keymaster; +import javacard.framework.Util; +import javacard.security.Signature; + // TODO complete the class design and implementation public class KMOperationState { - private KMInteger operationHandle; + private short opHandleCounter; + private boolean active; + private boolean trustedConfirmation; + // TODO This should be 64 bits + private short handle; + private short purpose; + private KMCipher cipher; + private Signature hmacSigner; // used for trusted confirmation. + private Signature signer; + private byte[] key; + private short keyLength; + private byte[] authTime; + private boolean authPerOperationReqd; + private boolean authTimeoutValidated; - private KMOperationState() { - operationHandle = null; + public KMOperationState(){ + authTime = new byte[8]; + key = new byte[256]; + reset(); } - public static KMOperationState instance(KMContext context) { - // TODO make operation handle - return context.getRepository().newOperationState(); + public void setTrustedConfirmationSigner(Signature hmacSigner){ + this.hmacSigner = hmacSigner; + trustedConfirmation = true; } - - public static void create(KMOperationState[] opStateRefTable) { - byte index = 0; - while (index < opStateRefTable.length) { - opStateRefTable[index] = new KMOperationState(); - index++; + public Signature getTrustedConfirmationSigner(){ + return hmacSigner; + } + public boolean isTrustedConfirmationRequired(){ + return trustedConfirmation; + } + public void activate(){ + active = true; + handle = getOpHandleCounter(); + } + public void reset(){ + Util.arrayFillNonAtomic(authTime, (short)0,(short)8, (byte)0); + keyLength = 0; + authPerOperationReqd = false; + active = false; + handle = 0; + key = null; + cipher = null; + signer = null; + purpose = KMType.INVALID_VALUE; + trustedConfirmation = false; + hmacSigner = null; + authTimeoutValidated = false; + } + //TODO make this random number + public short getOpHandleCounter() { + opHandleCounter++; + if(opHandleCounter < 0){ + opHandleCounter = 0; } + return opHandleCounter; + } + + public boolean isActive() { + return active; + } + + public short getHandle() { + return handle; + } + + public short getPurpose() { + return purpose; + } + + public void setPurpose(short purpose) { + this.purpose = purpose; + } + + public KMCipher getCipher() { + return cipher; + } + + public void setCipher(KMCipher cipher) { + this.cipher = cipher; + } + + public Signature getSigner() { + return signer; + } + + public void setSigner(Signature signer) { + this.signer = signer; + } + + public short getKey(byte[] buf, short start) { + Util.arrayCopy(key,(short)0, buf, start,keyLength); + return keyLength; + } + + public void setKey(byte[] buf, short start, short len) { + keyLength = len; + Util.arrayCopy(buf, start, key, (short)0, len); + } + + public boolean isAuthPerOperation() { + return authPerOperationReqd; + } + + public boolean isAuthTimeoutValidated() { + return authTimeoutValidated; } - public KMInteger getOperationHandle() { - return operationHandle; + public byte[] getAuthTime() { + return authTime; } - public void setOperationHandle(KMInteger operationHandle) { - this.operationHandle = operationHandle; + public void setAuthTime(byte[] time, short start) { + Util.arrayCopy(time, start, authTime, (short)0, (short)8); } - public void release(KMContext context) { - // TODO release handle - context.getRepository().releaseOperationState(this); + public void setAuthTimeoutValidated(boolean flag) { + authTimeoutValidated = flag; } } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMProvisionCmd.java b/Applet/Applet/src/com/android/javacard/keymaster/KMProvisionCmd.java deleted file mode 100644 index d1911f7e..00000000 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMProvisionCmd.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * 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; - -public class KMProvisionCmd extends KMAbstractCmd { - public static final byte INS_PROVISION_CMD = 0x23; - - @Override - public byte getIns() { - return INS_PROVISION_CMD; - } - - @Override - public KMArray process(KMArray args, KMContext context) { - KMKeyParameters arg1 = (KMKeyParameters)args.get((short)0); - KMEnum arg2 = (KMEnum)args.get((short)1); - KMByteBlob arg3 = (KMByteBlob)args.get((short)2); - provision(arg1, arg2.getVal(),arg3); - context.setKeymasterState(KMKeymasterApplet.ACTIVE_STATE); - //nothing to return - return null; - } - - // TODO implement functionality - private void provision(KMKeyParameters params, byte keyFormat, KMByteBlob keyBlob){ - } - - @Override - protected boolean validateState(byte state) { - return (KMKeymasterApplet.FIRST_SELECT_STATE == state); - } - - // Uses import key command signature but does not return anything back. - protected KMArray getExpectedArgs() { - // Argument 1 - KMKeyParameters keyparams = KMKeyParameters.instance(); - // Argument 2 - KMEnum keyFormat = KMEnum.instance().setType(KMType.KEY_FORMAT); - // Argument 3 - KMByteBlob keyBlob = KMByteBlob.instance(); - // Array of expected arguments - return KMArray.instance((short) 3) - .add((short) 0, keyparams) - .add((short) 1, keyFormat) - .add((short) 2, keyBlob); - } -} diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMRepository.java b/Applet/Applet/src/com/android/javacard/keymaster/KMRepository.java index 36d8f6f7..421065ab 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMRepository.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMRepository.java @@ -17,509 +17,281 @@ package com.android.javacard.keymaster; import javacard.framework.ISO7816; +import javacard.framework.ISOException; import javacard.framework.JCSystem; import javacard.framework.Util; -import javacard.security.AESKey; -import javacard.security.KeyBuilder; - -// TODO cleanup, move most of the buffers to transient memory with "clear on deselect". Only -// exception may be OperationState - TBD. The initialize and reset functions will be refactored -// to handle onInstall and onSelect. public class KMRepository { - private static final byte CMD_TABLE_LENGTH = 20; - private static final byte REF_TABLE_SIZE = 5; - private static final short HEAP_SIZE = 0x1000; - private static final byte INT_TABLE_SIZE = 10; - private static final byte TYPE_ARRAY_SIZE = 100; - private static final byte INT_SIZE = 4; - private static final byte LONG_SIZE = 8; - private static final short ENTROPY_POOL_SIZE = 32; - - private KMCommand[] commandTable = null; - private KMContext context = null; - private byte[] buffer = null; - private AESKey masterKey = null; - private boolean contextLocked = false; - private KMEncoder encoder = null; - private KMDecoder decoder = null; - - private KMByteBlob[] byteBlobRefTable = null; - private byte blobRefIndex = 0; - private KMInteger[] integerRefTable = null; - private byte intRefIndex = 0; - private KMArray[] arrayRefTable = null; - private byte arrayRefIndex = 0; - private KMVector[] vectorRefTable = null; - private byte vectorRefIndex = 0; - private KMEnum[] enumRefTable = null; - private byte enumRefIndex = 0; - private KMByteTag[] byteTagRefTable = null; - private byte byteTagRefIndex = 0; - private KMIntegerTag[] intTagRefTable = null; - private byte intTagRefIndex = 0; - private KMIntegerArrayTag[] intArrayTagRefTable = null; - private byte intArrayTagRefIndex = 0; - private KMEnumTag[] enumTagRefTable = null; - private byte enumTagRefIndex = 0; - private KMEnumArrayTag[] enumArrayTagRefTable = null; - private byte enumArrayTagRefIndex = 0; - private KMBoolTag[] boolTagRefTable = null; - private byte boolTagRefIndex = 0; - private KMKeyParameters[] keyParametersRefTable = null; - private byte keyParametersRefIndex = 0; - private KMKeyCharacteristics[] keyCharRefTable = null; - private byte keyCharRefIndex = 0; - private KMVerificationToken[] verTokenRefTable = null; - private byte verTokenRefIndex = 0; - private KMHmacSharingParameters[] hmacSharingParamsRefTable = null; - private byte hmacSharingParamsRefIndex = 0; - private KMHardwareAuthToken[] hwAuthTokenRefTable = null; - private byte hwAuthTokenRefIndex = 0; - private KMOperationState[] opStateRefTable = null; - private byte opStateRefIndex = 0; - private KMType[] typeRefTable = null; - private byte typeRefIndex = 0; - - private byte[] byteHeap = null; - private short byteHeapIndex = 0; - private Object[] uint32Array = null; - private byte uint32Index = 0; - private Object[] uint64Array = null; - private byte uint64Index = 0; - private KMOperationState[] operationStateTable = null; - private byte[] entropyPool = null; - private byte[] counter; - - public void initialize() { - // Initialize buffers and context. - JCSystem.beginTransaction(); - encoder = new KMEncoder(); - decoder = new KMDecoder(); - buffer = new byte[KMKeymasterApplet.MAX_LENGTH]; - context = new KMContext(); - context.setRepository(this); - contextLocked = false; - operationStateTable = new KMOperationState[4]; - // Initialize command table. - commandTable = new KMCommand[CMD_TABLE_LENGTH]; - commandTable[0] = new KMProvisionCmd(); - commandTable[1] = new KMGenerateKeyCmd(); - commandTable[2] = new KMImportKeyCmd(); - commandTable[3] = new KMExportKeyCmd(); - commandTable[4] = new KMComputeSharedHmacCmd(); - commandTable[5] = new KMBeginOperationCmd(); - commandTable[6] = new KMUpdateOperationCmd(); - commandTable[7] = new KMFinishOperationCmd(); - commandTable[8] = new KMAbortOperationCmd(); - commandTable[9] = new KMVerifyAuthorizationCmd(); - commandTable[10] = new KMAddRngEntropyCmd(); - commandTable[11] = new KMImportWrappedKeyCmd(); - commandTable[12] = new KMAttestKeyCmd(); - commandTable[13] = new KMUpgradeKeyCmd(); - commandTable[14] = new KMDeleteKeyCmd(); - commandTable[15] = new KMDeleteAllKeysCmd(); - commandTable[16] = new KMDestroyAttestationIdsCmd(); - commandTable[17] = new KMGetHWInfoCmd(); - commandTable[18] = new KMGetKeyCharacteristicsCmd(); - commandTable[19] = new KMGetHmacSharingParametersCmd(); - // Initialize masterkey - AES 256 bit key. - if (masterKey == null) { - masterKey = - (AESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_AES, KeyBuilder.LENGTH_AES_256, false); - } - // Initialize types - KMType.initialize(this); - byteBlobRefTable = new KMByteBlob[(short)(REF_TABLE_SIZE*4)]; - KMByteBlob.create(byteBlobRefTable); - integerRefTable = new KMInteger[REF_TABLE_SIZE]; - KMInteger.create(integerRefTable); - arrayRefTable = new KMArray[(short)(REF_TABLE_SIZE*4)]; - KMArray.create(arrayRefTable); - vectorRefTable = new KMVector[REF_TABLE_SIZE]; - KMVector.create(vectorRefTable); - enumRefTable = new KMEnum[(short)(REF_TABLE_SIZE*2)]; - KMEnum.create(enumRefTable); - byteTagRefTable = new KMByteTag[REF_TABLE_SIZE]; - KMByteTag.create(byteTagRefTable); - intTagRefTable = new KMIntegerTag[REF_TABLE_SIZE]; - KMIntegerTag.create(intTagRefTable); - intArrayTagRefTable = new KMIntegerArrayTag[REF_TABLE_SIZE]; - KMIntegerArrayTag.create(intArrayTagRefTable); - enumTagRefTable = new KMEnumTag[REF_TABLE_SIZE]; - KMEnumTag.create(enumTagRefTable); - enumArrayTagRefTable = new KMEnumArrayTag[REF_TABLE_SIZE]; - KMEnumArrayTag.create(enumArrayTagRefTable); - boolTagRefTable = new KMBoolTag[REF_TABLE_SIZE]; - KMBoolTag.create(boolTagRefTable); - keyParametersRefTable = new KMKeyParameters[REF_TABLE_SIZE]; - KMKeyParameters.create(keyParametersRefTable); - keyCharRefTable = new KMKeyCharacteristics[REF_TABLE_SIZE]; - KMKeyCharacteristics.create(keyCharRefTable); - verTokenRefTable = new KMVerificationToken[REF_TABLE_SIZE]; - KMVerificationToken.create(verTokenRefTable); - hmacSharingParamsRefTable = new KMHmacSharingParameters[REF_TABLE_SIZE]; - KMHmacSharingParameters.create(hmacSharingParamsRefTable); - hwAuthTokenRefTable = new KMHardwareAuthToken[REF_TABLE_SIZE]; - KMHardwareAuthToken.create(hwAuthTokenRefTable); - opStateRefTable = new KMOperationState[REF_TABLE_SIZE]; - KMOperationState.create(opStateRefTable); - - byteHeap = new byte[HEAP_SIZE]; - uint32Array = new Object[INT_TABLE_SIZE]; - uint64Array = new Object[INT_TABLE_SIZE]; - typeRefTable = new KMType[TYPE_ARRAY_SIZE]; - + //TODO make the sizes configurable + public static final short HEAP_SIZE = 0x2000; + public static final short MAX_BLOB_STORAGE = 32; + public static final short AES_GCM_AUTH_TAG_LENGTH = 12; + public static final short HMAC_SEED_NONCE_SIZE = 16; + public static final short MAX_OPS = 4; + public static final short COMPUTED_HMAC_KEY_SIZE = 32; + // Boot params constants + public static final byte BOOT_KEY_MAX_SIZE = 32; + public static final byte BOOT_HASH_MAX_SIZE = 32; + // Repository attributes + private static KMRepository repository; + private byte[] masterKey; + private byte[] hmacSeed; + private byte[] hmacKey; + private byte[] computedHmacKey; + private byte[] hmacNonce; + private byte[] heap; + private Object[] operationStateTable; + private short heapIndex; + // boot parameters + public Object[] authTagRepo; + public short keyBlobCount; + public byte[] osVersion; + public byte[] osPatch; + public byte[] verifiedBootKey; + public short actualBootKeyLength; + public byte[] verifiedBootHash; + public short actualBootHashLength; + public boolean verifiedBootFlag; + public boolean selfSignedBootFlag; + public boolean deviceLockedFlag; + + public static KMRepository instance() { + return repository; + } + + public KMRepository() { + heap = JCSystem.makeTransientByteArray(HEAP_SIZE, JCSystem.CLEAR_ON_RESET); + authTagRepo = new Object[MAX_BLOB_STORAGE]; short index = 0; - while (index < INT_TABLE_SIZE) { - uint32Array[index] = new byte[INT_SIZE]; - uint64Array[index] = new byte[LONG_SIZE]; + while (index < MAX_BLOB_STORAGE) { + authTagRepo[index] = new KMAuthTag(); + ((KMAuthTag) authTagRepo[index]).reserved = false; + ((KMAuthTag) authTagRepo[index]).authTag = new byte[AES_GCM_AUTH_TAG_LENGTH]; + ((KMAuthTag) authTagRepo[index]).usageCount = 0; index++; } - entropyPool = new byte[ENTROPY_POOL_SIZE]; - counter = new byte[8]; - - JCSystem.commitTransaction(); - } - - public KMEncoder getEncoder() { - return encoder; - } - - public KMDecoder getDecoder() { - return decoder; + osVersion = new byte[4]; + osPatch = new byte[4]; + verifiedBootKey = new byte[BOOT_KEY_MAX_SIZE]; + verifiedBootHash = new byte[BOOT_HASH_MAX_SIZE]; + operationStateTable = new Object[MAX_OPS]; + index = 0; + while(index < MAX_OPS){ + operationStateTable[index] = new KMOperationState(); + ((KMOperationState)operationStateTable[index]).reset(); + index++; + } + repository = this; } - public KMCommand getCommand(byte ins) throws KMException { - short cmdIndex = 0; - while (cmdIndex < CMD_TABLE_LENGTH) { - if (commandTable[cmdIndex].getIns() == ins) { - return commandTable[cmdIndex]; + public KMOperationState reserveOperation(){ + short index = 0; + while(index < MAX_OPS){ + if(!((KMOperationState)operationStateTable[index]).isActive()){ + ((KMOperationState)operationStateTable[index]).activate(); + return (KMOperationState)operationStateTable[index]; } - cmdIndex++; + index++; } - throw new KMException(ISO7816.SW_INS_NOT_SUPPORTED); + return null; } - - public KMContext getContext() throws KMException { - if (!contextLocked) { - contextLocked = true; - return context; - } else { - throw new KMException(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } + public void releaseOperation(KMOperationState op){ + op.reset(); } - - public void onUninstall() { - masterKey = null; + public void initMasterKey(byte[] key, short len) { + if (masterKey == null) { + masterKey = new byte[len]; + Util.arrayCopy(key, (short) 0, masterKey, (short) 0, len); + } } - public void onProcess() { - reset(); + public void initHmacKey(byte[] key, short len) { + if (hmacKey == null) { + hmacKey = new byte[len]; + Util.arrayCopy(key, (short) 0, hmacKey, (short) 0, len); + } } - private void reset() { - contextLocked = false; - Util.arrayFillNonAtomic(buffer, (short) 0, (short) buffer.length, (byte) 0); - Util.arrayFillNonAtomic(byteHeap, (short) 0, (short) buffer.length, (byte) 0); - byteHeapIndex = 0; - Util.arrayFillNonAtomic(buffer, (short) 0, (short) buffer.length, (byte) 0); - short index = 0; - while (index < typeRefTable.length) { - typeRefTable[index] = null; - index++; + public void initHmacSeed(byte[] seed, short len) { + if (hmacSeed == null) { + hmacSeed = new byte[len]; + Util.arrayCopy(seed, (short) 0, hmacSeed, (short) 0, len); } - typeRefIndex = 0; - index = 0; - while (index < uint32Array.length) { - byte[] num = (byte[]) uint32Array[index]; - byte numIndex = 0; - while (numIndex < INT_SIZE) { - num[numIndex] = 0; - numIndex++; - } - index++; - } - uint32Index = 0; - index = 0; - while (index < uint64Array.length) { - byte[] num = (byte[]) uint64Array[index]; - byte numIndex = 0; - while (numIndex < LONG_SIZE) { - num[numIndex] = 0; - numIndex++; - } - index++; - } - uint64Index = 0; - resetTypeObjects(byteBlobRefTable); - resetTypeObjects(integerRefTable); - resetTypeObjects(enumRefTable); - resetTypeObjects(byteTagRefTable); - resetTypeObjects(boolTagRefTable); - resetTypeObjects(arrayRefTable); - resetTypeObjects(enumTagRefTable); - resetTypeObjects(enumArrayTagRefTable); - resetTypeObjects(intTagRefTable); - resetTypeObjects(intArrayTagRefTable); - resetTypeObjects(vectorRefTable); - resetTypeObjects(keyCharRefTable); - resetTypeObjects(keyParametersRefTable); - resetTypeObjects(hmacSharingParamsRefTable); - resetTypeObjects(hwAuthTokenRefTable); - resetTypeObjects(verTokenRefTable); } - public void resetTypeObjects(KMType[] type){ - byte index = 0; - while(index < type.length){ - type[index].init(); - index++; + public void initComputedHmac(byte[] key, short start, short len) { + if (computedHmacKey == null) { + computedHmacKey = new byte[len]; + Util.arrayCopy(key, (short) 0, computedHmacKey, start, len); } } - public void onDeselect() { - // TODO clear operation state? - } - public void onSelect() { - // Nothing to be done currently. + public void initHmacNonce(byte[] nonce, short offset, short len) { + if (hmacNonce == null) { + hmacNonce = new byte[len]; + } else if (len != hmacNonce.length) { + KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + } + Util.arrayCopy(nonce, (short) 0, hmacSeed, (short) 0, len); } - public byte[] getBuffer() { - return buffer; + public void onUninstall() { + // TODO change this + Util.arrayFillNonAtomic(masterKey, (short) 0, (short) masterKey.length, (byte) 0); } - public AESKey getMasterKey() { - return masterKey; - } + public void onProcess() {} - // Allocate 4 bytes or 8 bytes buffer - public byte[] newIntegerArray(short length) { - if (length == 4) { - if (uint32Index >= uint32Array.length) { - // TODO this is placeholder exception value. This needs to be replaced by 910E, 91A1 or 9210 - throw new KMException(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - byte[] ret = (byte[]) uint32Array[uint32Index]; - uint32Index++; - return ret; - } else if (length == 8) { - if (uint64Index >= uint64Array.length) { - // TODO this is placeholder exception value. This needs to be replaced by 910E, 91A1 or 9210 - throw new KMException(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - byte[] ret = (byte[]) uint64Array[uint64Index]; - uint64Index++; - return ret; - } else { - throw new KMException(ISO7816.SW_WRONG_LENGTH); - } + public void clean() { + Util.arrayFillNonAtomic(heap, (short) 0, heapIndex, (byte) 0); + heapIndex = 0; } - public KMByteBlob newByteBlob() { - if (blobRefIndex >= byteBlobRefTable.length) { - // TODO this is placeholder exception value. - throw new KMException(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - KMByteBlob ret = byteBlobRefTable[blobRefIndex]; - blobRefIndex++; - return ret; - } + public void onDeselect() {} - public KMInteger newInteger() { - if (intRefIndex >= integerRefTable.length) { - // TODO this is placeholder exception value. - throw new KMException(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - KMInteger ret = integerRefTable[intRefIndex]; - intRefIndex++; - return ret; - } + public void onSelect() {} - public KMEnumTag newEnumTag() { - if (enumTagRefIndex >= enumTagRefTable.length) { - // TODO this is placeholder exception value. - throw new KMException(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - KMEnumTag ret = enumTagRefTable[enumTagRefIndex]; - enumTagRefIndex++; - return ret; + public byte[] getMasterKeySecret() { + return masterKey; } - public KMEnumArrayTag newEnumArrayTag() { - if (enumArrayTagRefIndex >= enumArrayTagRefTable.length) { - // TODO this is placeholder exception value. - throw new KMException(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + public short alloc(short length) { + if (((short) (heapIndex + length)) > heap.length) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); } - KMEnumArrayTag ret = enumArrayTagRefTable[enumArrayTagRefIndex]; - enumArrayTagRefIndex++; - return ret; + heapIndex += length; + return (short) (heapIndex - length); } - public KMIntegerTag newIntegerTag() { - if (intTagRefIndex >= intTagRefTable.length) { - // TODO this is placeholder exception value. - throw new KMException(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - KMIntegerTag ret = intTagRefTable[intTagRefIndex]; - intTagRefIndex++; - return ret; + public byte[] getHeap() { + return heap; } - public KMIntegerArrayTag newIntegerArrayTag() { - if (intArrayTagRefIndex >= intArrayTagRefTable.length) { - // TODO this is placeholder exception value. - throw new KMException(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - KMIntegerArrayTag ret = intArrayTagRefTable[intArrayTagRefIndex]; - intArrayTagRefIndex++; - return ret; + public byte[] getHmacSeed() { + return hmacSeed; } - public KMBoolTag newBoolTag() { - if (boolTagRefIndex >= boolTagRefTable.length) { - // TODO this is placeholder exception value. - throw new KMException(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - KMBoolTag ret = boolTagRefTable[boolTagRefIndex]; - boolTagRefIndex++; - return ret; + public byte[] getHmacKey() { + return hmacKey; } - public KMByteTag newByteTag() { - if (byteTagRefIndex >= byteTagRefTable.length) { - // TODO this is placeholder exception value. - throw new KMException(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - KMByteTag ret = byteTagRefTable[byteTagRefIndex]; - byteTagRefIndex++; - return ret; + public byte[] getHmacNonce() { + return hmacNonce; } - public KMKeyParameters newKeyParameters() { - if (keyParametersRefIndex >= keyParametersRefTable.length) { - // TODO this is placeholder exception value. - throw new KMException(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - KMKeyParameters ret = keyParametersRefTable[keyParametersRefIndex]; - keyParametersRefIndex++; - return ret; + public void setHmacNonce(byte[] hmacNonce) { + Util.arrayCopy(hmacNonce, (short) 0, this.hmacNonce, (short) 0, HMAC_SEED_NONCE_SIZE); } - - public KMArray newArray() { - if (arrayRefIndex >= arrayRefTable.length) { - // TODO this is placeholder exception value. - throw new KMException(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - KMArray ret = arrayRefTable[arrayRefIndex]; - arrayRefIndex++; - return ret; + public byte[] getComputedHmacKey() { + return computedHmacKey; } - public KMKeyCharacteristics newKeyCharacteristics() { - if (keyCharRefIndex >= keyCharRefTable.length) { - // TODO this is placeholder exception value. - throw new KMException(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - KMKeyCharacteristics ret = keyCharRefTable[keyCharRefIndex]; - keyCharRefIndex++; - return ret; + public void setComputedHmacKey(byte[] computedHmacKey) { + Util.arrayCopy( computedHmacKey, (short) 0, this.computedHmacKey, (short) 0, COMPUTED_HMAC_KEY_SIZE); } - public KMHardwareAuthToken newHwAuthToken() { - if (hwAuthTokenRefIndex >= hwAuthTokenRefTable.length) { - // TODO this is placeholder exception value. - throw new KMException(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + public void persistAuthTag(short authTag) { + short index = 0; + while (index < MAX_BLOB_STORAGE) { + if (!((KMAuthTag) authTagRepo[index]).reserved) { + JCSystem.beginTransaction(); + ((KMAuthTag) authTagRepo[index]).reserved = true; + Util.arrayCopy( + KMByteBlob.cast(authTag).getBuffer(), + KMByteBlob.cast(authTag).getStartOff(), + ((KMAuthTag) authTagRepo[index]).authTag , + (short) 0, + AES_GCM_AUTH_TAG_LENGTH); + keyBlobCount++; + JCSystem.commitTransaction(); + break; + } + index++; } - KMHardwareAuthToken ret = hwAuthTokenRefTable[hwAuthTokenRefIndex]; - hwAuthTokenRefIndex++; - return ret; } - public KMHmacSharingParameters newHmacSharingParameters() { - if (hmacSharingParamsRefIndex >= hmacSharingParamsRefTable.length) { - // TODO this is placeholder exception value. - throw new KMException(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + public boolean validateAuthTag(short authTag) { + KMAuthTag tag = findTag(authTag); + if(tag != null){ + return true; } - KMHmacSharingParameters ret = hmacSharingParamsRefTable[hmacSharingParamsRefIndex]; - hmacSharingParamsRefIndex++; - return ret; + return false; } - public KMVerificationToken newVerificationToken() { - if (verTokenRefIndex >= verTokenRefTable.length) { - // TODO this is placeholder exception value. - throw new KMException(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + public void removeAuthTag(short authTag) { + KMAuthTag tag = findTag(authTag); + if(tag == null){ + ISOException.throwIt(ISO7816.SW_COMMAND_NOT_ALLOWED); } - KMVerificationToken ret = verTokenRefTable[verTokenRefIndex]; - verTokenRefIndex++; - return ret; + JCSystem.beginTransaction(); + tag.reserved = false; + Util.arrayFill(tag.authTag, (short) 0, AES_GCM_AUTH_TAG_LENGTH, (byte) 0); + tag.usageCount = 0; + keyBlobCount--; + JCSystem.commitTransaction(); } - public KMOperationState newOperationState() { - if (opStateRefIndex >= opStateRefTable.length) { - // TODO this is placeholder exception value. - throw new KMException(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + public void removeAllAuthTags() { + JCSystem.beginTransaction(); + short index = 0; + while (index < MAX_BLOB_STORAGE) { + ((KMAuthTag) authTagRepo[index]).reserved = false; + Util.arrayFill( + ((KMAuthTag) authTagRepo[index]).authTag, (short) 0, AES_GCM_AUTH_TAG_LENGTH, (byte) 0); + ((KMAuthTag) authTagRepo[index]).usageCount = 0; + index++; } - KMOperationState ret = operationStateTable[opStateRefIndex]; - opStateRefIndex++; - return ret; + keyBlobCount = 0; + JCSystem.commitTransaction(); } - public void releaseOperationState(KMOperationState state){ - opStateRefIndex--; - if(opStateRefIndex <0){ - throw new KMException(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - opStateRefTable[opStateRefIndex] = state; - } - public KMVector newVector() { - if (vectorRefIndex >= vectorRefTable.length) { - // TODO this is placeholder exception value. - throw new KMException(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + public KMAuthTag findTag(short authTag) { + short index = 0; + short found = 0; + while (index < MAX_BLOB_STORAGE) { + if (((KMAuthTag) authTagRepo[index]).reserved) { + found = + Util.arrayCompare( + ((KMAuthTag) authTagRepo[index]).authTag, + (short) 0, + KMByteBlob.cast(authTag).getBuffer(), + KMByteBlob.cast(authTag).getStartOff(), + AES_GCM_AUTH_TAG_LENGTH); + if (found == 0) { + return (KMAuthTag) authTagRepo[index]; + } + } + index++; } - KMVector ret = vectorRefTable[vectorRefIndex]; - vectorRefIndex++; - return ret; + return null; } - public KMEnum newEnum() { - if (enumRefIndex >= enumRefTable.length) { - // TODO this is placeholder exception value. - throw new KMException(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + public short getRateLimitedKeyCount(short authTag) { + KMAuthTag tag = findTag(authTag); + if (tag != null) { + return tag.usageCount; } - KMEnum ret = enumRefTable[enumRefIndex]; - enumRefIndex++; - return ret; + return KMType.INVALID_VALUE; } - public KMType[] getTypeArrayRef(){ - return typeRefTable; - } - - public byte[] getByteHeapRef(){ - return byteHeap; - } - - public short newTypeArray(short length) { - if (((short) (typeRefIndex + length)) >= typeRefTable.length) { - // TODO this is placeholder exception value. - throw new KMException(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + public void setRateLimitedKeyCount(short authTag, short val) { + KMAuthTag tag = findTag(authTag); + JCSystem.beginTransaction(); + if (tag != null) { + tag.usageCount = val; } - typeRefIndex += length; - return (short) (typeRefIndex - length); + JCSystem.commitTransaction(); } - public short newByteArray(short length) { - if (((short) (byteHeapIndex + length)) >= byteHeap.length) { - // TODO this is placeholder exception value. - throw new KMException(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + public KMOperationState findOperation(short opHandle) { + short index = 0; + while(index < MAX_OPS){ + if(((KMOperationState)operationStateTable[index]).isActive() && + ((KMOperationState)operationStateTable[index]).getHandle() == opHandle){ + return (KMOperationState)operationStateTable[index]; + } + index++; } - byteHeapIndex += length; - return (short) (byteHeapIndex - length); - } - - public byte[] getEntropyPool() { - return entropyPool; + return null; } } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMUtil.java b/Applet/Applet/src/com/android/javacard/keymaster/KMRng.java similarity index 62% rename from Applet/Applet/src/com/android/javacard/keymaster/KMUtil.java rename to Applet/Applet/src/com/android/javacard/keymaster/KMRng.java index 5b42ae9d..a2e79901 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMUtil.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMRng.java @@ -1,6 +1,8 @@ package com.android.javacard.keymaster; import javacard.framework.ISO7816; +import javacard.framework.ISOException; +import javacard.framework.JCSystem; import javacard.framework.Util; import javacard.security.AESKey; import javacard.security.CryptoException; @@ -8,28 +10,23 @@ import javacard.security.RandomData; import javacardx.crypto.Cipher; -public class KMUtil { - public static final byte AES_BLOCK_SIZE = 16; - private static KMRepository repository = null; +public class KMRng { + 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 byte[] counter; - private static boolean initialized = false; private static AESKey aesKey; - private static Cipher aesEcb; - - public static void init(KMRepository repo) { - if (!initialized) { - KMUtil.repository = repo; - counter = repo.newIntegerArray((short) 8); - KMUtil.initEntropyPool(repo.getEntropyPool()); + private static Cipher aesCbc; + private static byte[] entropyPool; + public static void init() { + entropyPool = JCSystem.makeTransientByteArray(ENTROPY_POOL_SIZE, JCSystem.CLEAR_ON_RESET); + counter = JCSystem.makeTransientByteArray((short)8, JCSystem.CLEAR_ON_RESET); + KMRng.initEntropyPool(entropyPool); try { - aesEcb = Cipher.getInstance(Cipher.ALG_AES_BLOCK_128_ECB_NOPAD, false); + aesCbc = Cipher.getInstance(Cipher.ALG_AES_BLOCK_128_CBC_NOPAD, false); } catch (CryptoException exp) { - // TODO change this to proper error code - throw new KMException(ISO7816.SW_UNKNOWN); + ISOException.throwIt(ISO7816.SW_COMMAND_NOT_ALLOWED); } - aesKey = (AESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_AES, KeyBuilder.LENGTH_AES_256, false); - initialized = true; - } + aesKey = (AESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_AES, KeyBuilder.LENGTH_AES_128, false); } public static void initEntropyPool(byte[] pool) { @@ -40,26 +37,31 @@ public static void initEntropyPool(byte[] pool) { } try { trng = RandomData.getInstance(RandomData.ALG_TRNG); + trng.nextBytes(pool, (short) 0, (short) pool.length); } catch (CryptoException exp) { if (exp.getReason() == CryptoException.NO_SUCH_ALGORITHM) { //TODO change this when possible // simulator does not support TRNG algorithm. So, PRNG algorithm (deprecated) is used. trng = RandomData.getInstance(RandomData.ALG_PSEUDO_RANDOM); + trng.nextBytes(pool, (short) 0, (short) pool.length); } else { // TODO change this to proper error code - throw new KMException(ISO7816.SW_UNKNOWN); + ISOException.throwIt(ISO7816.SW_UNKNOWN); } } - trng.nextBytes(pool, (short) 0, (short) pool.length); + } // Generate a secure random number from existing entropy pool. This uses aes ecb algorithm with // 8 byte counter and 16 byte block size. public static void newRandomNumber(byte[] num, short startOff, short length) { - byte[] bufPtr = repository.getByteHeapRef(); - short countBufInd = repository.newByteArray(AES_BLOCK_SIZE); - short randBufInd = repository.newByteArray(AES_BLOCK_SIZE); - short len = AES_BLOCK_SIZE; + KMRepository repository = KMRepository.instance(); + byte[] bufPtr = repository.getHeap(); + short countBufInd = repository.alloc(KMKeymasterApplet.AES_BLOCK_SIZE); + short randBufInd = repository.alloc(KMKeymasterApplet.AES_BLOCK_SIZE); + short len = KMKeymasterApplet.AES_BLOCK_SIZE; + aesKey.setKey(entropyPool, (short) 0); + aesCbc.init(aesKey, Cipher.MODE_ENCRYPT, aesICV, (short)0, (short)16); while (length > 0) { if (length < len ) len = length; // increment counter by one @@ -67,9 +69,7 @@ public static void newRandomNumber(byte[] num, short startOff, short length) { // copy the 8 byte counter into the 16 byte counter buffer. Util.arrayCopy(counter, (short) 0, bufPtr, countBufInd, (short) counter.length); // encrypt the counter buffer with existing entropy which forms the aes key. - aesKey.setKey(repository.getEntropyPool(), (short) 0); - aesEcb.init(aesKey, Cipher.MODE_ENCRYPT); - aesEcb.doFinal(bufPtr, countBufInd, AES_BLOCK_SIZE, bufPtr, randBufInd); + aesCbc.doFinal(bufPtr, countBufInd, KMKeymasterApplet.AES_BLOCK_SIZE, bufPtr, randBufInd); // copy the encrypted counter block to buffer passed in the argument Util.arrayCopy(bufPtr, randBufInd, num, startOff, len); length = (short) (length - len); @@ -92,10 +92,13 @@ private static void incrementCounter() { } else { // if msb is not set then increment the counter counter[index]++; - // is the msb still not set i.e. no carry over - if (counter[index] >= 0) break; // then break - else index--; // else go to the higher order byte + break; } } } + + public static byte[] getEntropyPool() { + return entropyPool; + } + } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMTag.java b/Applet/Applet/src/com/android/javacard/keymaster/KMTag.java index c6a1b5ae..709f56f8 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMTag.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMTag.java @@ -16,8 +16,9 @@ package com.android.javacard.keymaster; -public abstract class KMTag extends KMType { - public abstract short getTagType(); +import javacard.framework.Util; - public abstract short getKey(); +public class KMTag extends KMType { + public static short getTagType(short ptr){return Util.getShort(heap, (short)(ptr+TLV_HEADER_SIZE));} + public static short getKey(short ptr){return Util.getShort(heap, (short)(ptr+TLV_HEADER_SIZE+2));} } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMType.java b/Applet/Applet/src/com/android/javacard/keymaster/KMType.java index 2aa08271..f136bd83 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMType.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMType.java @@ -16,8 +16,27 @@ package com.android.javacard.keymaster; +import javacard.framework.ISO7816; +import javacard.framework.ISOException; +import javacard.framework.Util; + public abstract class KMType { - public static final short TAG_TYPE_MASK = (short) 0xF000; + public static final short INVALID_VALUE = (short)0x8000; + protected static final byte TLV_HEADER_SIZE = 3; + + // Types + public static final byte BYTE_BLOB_TYPE = 0x01; + public static final byte INTEGER_TYPE = 0x02; + public static final byte ENUM_TYPE = 0x03; + public static final byte TAG_TYPE = 0x04; + public static final byte ARRAY_TYPE = 0x05; + public static final byte KEY_PARAM_TYPE = 0x06; + public static final byte KEY_CHAR_TYPE = 0x07; + public static final byte HW_AUTH_TOKEN_TYPE = 0x08; + public static final byte VERIFICATION_TOKEN_TYPE = 0x09; + public static final byte HMAC_SHARING_PARAM_TYPE = 0x0A; + + // Tag Types public static final short INVALID_TAG = 0x0000; public static final short ENUM_TAG = 0x1000; public static final short ENUM_ARRAY_TAG = 0x2000; @@ -29,6 +48,7 @@ public abstract class KMType { public static final short BIGNUM_TAG = (short) 0x8000; public static final short BYTES_TAG = (short) 0x9000; public static final short ULONG_ARRAY_TAG = (short) 0xA000; + public static final short TAG_TYPE_MASK = (short) 0xF000; // Enum Tag // Algorithm Enum Tag key and values @@ -56,6 +76,8 @@ public abstract class KMType { public static final byte USER_AUTH_NONE = 0x00; public static final byte PASSWORD = 0x01; public static final byte FINGERPRINT = 0x02; + public static final byte BOTH = 0x03; + // have to be power of 2 public static final byte ANY = (byte) 0xFF; // Origin Enum Tag key and values. @@ -88,21 +110,38 @@ public abstract class KMType { public static final byte PKCS8 = 0x01; public static final byte RAW = 0x03; + // Verified Boot State + public static final short VERIFIED_BOOT_STATE = (short) 0xF003; + public static final byte SELF_SIGNED_BOOT = 0x01; + public static final byte VERIFIED_BOOT = 0x02; + + // Verified Boot Key + public static final short VERIFIED_BOOT_KEY = (short) 0xF004; + + // Verified Boot Hash + public static final short VERIFIED_BOOT_HASH = (short) 0xF005; + + // Device Locked + public static final short DEVICE_LOCKED = (short) 0xF006; + public static final byte DEVICE_LOCKED_TRUE = 0x01; + public static final byte DEVICE_LOCKED_FALSE = 0x00; + // Enum Array Tag // Purpose - public static final short PURPOSE = 0x0002; - public static final byte ENCRYPT = 0x01; - public static final byte DECRYPT = 0x02; - public static final byte SIGN = 0x04; - public static final byte VERIFY = 0x05; - public static final byte WRAP_KEY = 0x06; - public static final byte ATTEST_KEY = (byte) 0x7F; + public static final short PURPOSE = 0x0001; + public static final byte ENCRYPT = 0x00; + public static final byte DECRYPT = 0x01; + public static final byte SIGN = 0x02; + public static final byte VERIFY = 0x03; + public static final byte WRAP_KEY = 0x05; + public static final byte ATTEST_KEY = (byte) 0x7F; /* TODO This is not present in types.hal */ // Block mode public static final short BLOCK_MODE = 0x0004; public static final byte ECB = 0x01; public static final byte CBC = 0x02; public static final byte CTR = 0x04; + public static final byte GCM = 0x20; // Digest public static final short DIGEST = 0x0005; @@ -156,7 +195,7 @@ public abstract class KMType { public static final short ACTIVE_DATETIME = 0x0190; public static final short ORIGINATION_EXPIRE_DATETIME = 0x0191; public static final short USAGE_EXPIRE_DATETIME = 0x0192; - public static final short CREATION_DATETIME = 0x0193; + public static final short CREATION_DATETIME = 0x02BD;//0x0193; // Integer Array Tags - ULONG_REP and UINT_REP. // User Secure Id @@ -225,12 +264,29 @@ public abstract class KMType { public static final byte NO_VALUE = (byte) 0xff; protected static KMRepository repository; + protected static byte[] heap; - public static void initialize(KMRepository repo) { - KMType.repository = repo; + public static void initialize() { + KMType.repository = KMRepository.instance(); + KMType.heap = repository.getHeap(); } - public abstract void init(); + public static byte getType(short ptr){return heap[ptr];} + public static short length(short ptr){return Util.getShort(heap, (short)(ptr+1));} + public static short getValue(short ptr){return Util.getShort(heap, (short)(ptr+TLV_HEADER_SIZE));} - public abstract short length(); + protected static short instance(byte type, short length){ + if (length < 0) ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); + short ptr = repository.alloc((short) (length + TLV_HEADER_SIZE)); + heap[ptr] = type; + Util.setShort(heap, (short) (ptr + 1), length); + return ptr; + } + + protected static short exp(byte type) { + short ptr = repository.alloc(TLV_HEADER_SIZE); + heap[ptr] = type; + Util.setShort(heap, (short) (ptr + 1), INVALID_VALUE); + return ptr; + } } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMUpdateOperationCmd.java b/Applet/Applet/src/com/android/javacard/keymaster/KMUpdateOperationCmd.java deleted file mode 100644 index 5b1e58f6..00000000 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMUpdateOperationCmd.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * 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; - -public class KMUpdateOperationCmd extends KMAbstractCmd { - public static final byte INS_UPDATE_OPERATION_CMD = 0x20; - - @Override - protected KMArray getExpectedArgs() { - return null; - } - - @Override - protected KMArray process(KMArray args, KMContext context) { - return null; - } - - @Override - public byte getIns() { - return INS_UPDATE_OPERATION_CMD; - } -} diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMUpgradeKeyCmd.java b/Applet/Applet/src/com/android/javacard/keymaster/KMUpgradeKeyCmd.java deleted file mode 100644 index d883068f..00000000 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMUpgradeKeyCmd.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * 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; - -public class KMUpgradeKeyCmd extends KMAbstractCmd { - public static final byte INS_UPGRADE_KEY_CMD = 0x15; - - @Override - protected KMArray getExpectedArgs() { - return null; - } - - @Override - protected KMArray process(KMArray args, KMContext context) { - return null; - } - - @Override - public byte getIns() { - return INS_UPGRADE_KEY_CMD; - } -} diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMVector.java b/Applet/Applet/src/com/android/javacard/keymaster/KMVector.java deleted file mode 100644 index fb9d20f5..00000000 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMVector.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * 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; - -public class KMVector extends KMType { - private KMType type; - private KMArray vals; - - private KMVector() { - init(); - } - - @Override - public void init() { - vals = null; - type = null; - } - - @Override - public short length() { - return vals.length(); - } - - public static KMVector instance(KMType type) { - KMVector inst = repository.newVector(); - inst.type = type; - inst.vals = KMArray.instance(); - return inst; - } - - public static KMVector instance(KMType type, short length) { - KMVector inst = repository.newVector(); - inst.type = type; - inst.vals = KMArray.instance(length); - return inst; - } - - public static void create(KMVector[] vectorRefTable) { - byte index = 0; - while (index < vectorRefTable.length) { - vectorRefTable[index] = new KMVector(); - index++; - } - } - - public KMArray getVals() { - return vals; - } - - public KMVector withLength(short length) { - this.vals.withLength(length); - return this; - } - - public KMVector add(short index, KMType val) { - vals.add(index, val); - return this; - } - - public KMType get(short index) { - return vals.get(index); - } - - public void setVals(KMArray vals) { - this.vals = vals; - } - - public KMType getType() { - return type; - } -} diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMVerificationToken.java b/Applet/Applet/src/com/android/javacard/keymaster/KMVerificationToken.java index f52fae55..168971c3 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMVerificationToken.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMVerificationToken.java @@ -17,6 +17,8 @@ package com.android.javacard.keymaster; import javacard.framework.ISO7816; +import javacard.framework.ISOException; +import javacard.framework.Util; public class KMVerificationToken extends KMType { public static final byte CHALLENGE = 0x00; @@ -24,71 +26,114 @@ public class KMVerificationToken extends KMType { public static final byte PARAMETERS_VERIFIED = 0x02; public static final byte SECURITY_LEVEL = 0x03; public static final byte MAC = 0x04; - private KMArray vals; - private KMVerificationToken() { - init(); + private static KMVerificationToken prototype; + private static short instPtr; + + private KMVerificationToken() {} + + public static short exp() { + short arrPtr = KMArray.instance((short)5); + KMArray arr = KMArray.cast(arrPtr); + arr.add(CHALLENGE, KMInteger.exp()); + arr.add(TIMESTAMP, KMInteger.exp()); + //arr.add(PARAMETERS_VERIFIED, KMKeyParameters.exp()); + arr.add(PARAMETERS_VERIFIED, KMByteBlob.exp()); + arr.add(SECURITY_LEVEL, KMEnum.instance(KMType.HARDWARE_TYPE)); + arr.add(MAC, KMByteBlob.exp()); + return instance(arrPtr); + } + + private static KMVerificationToken proto(short ptr) { + if (prototype == null) prototype = new KMVerificationToken(); + instPtr = ptr; + return prototype; + } + + + public static short instance() { + short arrPtr = KMArray.instance((short)5); + return instance(arrPtr); } - @Override - public void init() { - vals = null; + public static short instance(short vals) { + KMArray arr = KMArray.cast(vals); + if(arr.length() != 5)ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); + short ptr = KMType.instance(VERIFICATION_TOKEN_TYPE, (short)2); + Util.setShort(heap, (short)(ptr + TLV_HEADER_SIZE), vals); + return ptr; + } + + public static KMVerificationToken cast(short ptr) { + if (heap[ptr] != VERIFICATION_TOKEN_TYPE) ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + short arrPtr = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE)); + if(heap[arrPtr] != ARRAY_TYPE) ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + return proto(ptr); + } + + public short getVals() { + return Util.getShort(heap, (short) (instPtr + TLV_HEADER_SIZE)); } - @Override public short length() { - return vals.length(); + short arrPtr = getVals(); + return KMArray.cast(arrPtr).length(); } - public static void create(KMVerificationToken[] verTokenRefTable) { - byte index = 0; - while (index < verTokenRefTable.length) { - verTokenRefTable[index] = new KMVerificationToken(); - index++; - } + public short getChallenge() { + short arrPtr = getVals(); + return KMArray.cast(arrPtr).get(CHALLENGE); } - public static KMVerificationToken instance() { - KMVerificationToken inst = repository.newVerificationToken(); - inst.vals = KMArray.instance((short) 5); - inst.vals.add(CHALLENGE, KMInteger.instance()); - inst.vals.add(TIMESTAMP, KMInteger.instance()); - inst.vals.add(PARAMETERS_VERIFIED, KMKeyParameters.instance()); - inst.vals.add(SECURITY_LEVEL, KMEnumTag.instance(KMType.HARDWARE_TYPE)); - inst.vals.add(MAC, KMByteBlob.instance()); - return inst; + public void setChallenge(short vals) { + KMInteger.cast(vals); + short arrPtr = getVals(); + KMArray.cast(arrPtr).add(CHALLENGE, vals); } - public static KMVerificationToken instance(KMArray vals) { - if (vals.length() != 5) { - throw new KMException(ISO7816.SW_WRONG_LENGTH); - } - KMVerificationToken inst = repository.newVerificationToken(); - inst.vals = vals; - return inst; + public short getTimestamp() { + short arrPtr = getVals(); + return KMArray.cast(arrPtr).get(TIMESTAMP); } - public KMInteger getChallenge() { - return (KMInteger) vals.get(CHALLENGE); + public void setTimestamp(short vals) { + KMInteger.cast(vals); + short arrPtr = getVals(); + KMArray.cast(arrPtr).add(TIMESTAMP, vals); } - public KMInteger getTimestamp() { - return (KMInteger) vals.get(TIMESTAMP); + public short getMac() { + short arrPtr = getVals(); + return KMArray.cast(arrPtr).get(MAC); } - public KMKeyParameters getParametersVerified() { - return (KMKeyParameters) vals.get(PARAMETERS_VERIFIED); + public void setMac(short vals) { + KMByteBlob.cast(vals); + short arrPtr = getVals(); + KMArray.cast(arrPtr).add(MAC, vals); } - public byte getSecurityLevel() { - return ((KMEnumTag) vals.get(SECURITY_LEVEL)).getValue(); + public short getParametersVerified() { + short arrPtr = getVals(); + return KMArray.cast(arrPtr).get(PARAMETERS_VERIFIED); } - public KMByteBlob getMac() { - return (KMByteBlob) vals.get(MAC); + public void setParametersVerified(short vals) { + // KMKeyParameters.cast(vals); + KMByteBlob.cast(vals); + short arrPtr = getVals(); + KMArray.cast(arrPtr).add(PARAMETERS_VERIFIED, vals); } - public KMArray getVals() { - return vals; + public short getSecurityLevel() { + short arrPtr = getVals(); + return KMArray.cast(arrPtr).get(SECURITY_LEVEL); } + + public void setSecurityLevel(short vals) { + KMEnum.cast(vals); + short arrPtr = getVals(); + KMArray.cast(arrPtr).add(SECURITY_LEVEL, vals); + } + } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMVerifyAuthorizationCmd.java b/Applet/Applet/src/com/android/javacard/keymaster/KMVerifyAuthorizationCmd.java deleted file mode 100644 index 33289b44..00000000 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMVerifyAuthorizationCmd.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * 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; - -public class KMVerifyAuthorizationCmd extends KMAbstractCmd { - public static final byte INS_VERIFY_AUTHORIZATION_CMD = 0x1B; - - @Override - protected KMArray getExpectedArgs() { - return null; - } - - @Override - protected KMArray process(KMArray args, KMContext context) { - return null; - } - - @Override - public byte getIns() { - return INS_VERIFY_AUTHORIZATION_CMD; - } -} diff --git a/Applet/Applet/test/com/android/javacard/test/KMFrameworkTest.java b/Applet/Applet/test/com/android/javacard/test/KMFrameworkTest.java index 15e61109..0b2ae651 100644 --- a/Applet/Applet/test/com/android/javacard/test/KMFrameworkTest.java +++ b/Applet/Applet/test/com/android/javacard/test/KMFrameworkTest.java @@ -17,11 +17,19 @@ package com.android.javacard.test; import com.android.javacard.keymaster.KMArray; +import com.android.javacard.keymaster.KMBoolTag; import com.android.javacard.keymaster.KMByteBlob; +import com.android.javacard.keymaster.KMByteTag; +import com.android.javacard.keymaster.KMCryptoProvider; +import com.android.javacard.keymaster.KMCryptoProviderImpl; import com.android.javacard.keymaster.KMDecoder; import com.android.javacard.keymaster.KMEncoder; import com.android.javacard.keymaster.KMEnum; +import com.android.javacard.keymaster.KMEnumArrayTag; import com.android.javacard.keymaster.KMEnumTag; +import com.android.javacard.keymaster.KMInteger; +import com.android.javacard.keymaster.KMIntegerTag; +import com.android.javacard.keymaster.KMKeyCharacteristics; import com.android.javacard.keymaster.KMKeyParameters; import com.android.javacard.keymaster.KMKeymasterApplet; import com.android.javacard.keymaster.KMType; @@ -29,18 +37,32 @@ import com.licel.jcardsim.utils.AIDUtil; import javacard.framework.AID; import javacard.framework.Util; -import javacard.security.RandomData; +import javacard.security.AESKey; +import javacard.security.DESKey; +import javacard.security.ECPrivateKey; +import javacard.security.ECPublicKey; +import javacard.security.HMACKey; +import javacard.security.Key; +import javacard.security.KeyBuilder; +import javacard.security.KeyPair; +import javacard.security.RSAPrivateKey; import javax.smartcardio.CommandAPDU; import javax.smartcardio.ResponseAPDU; import org.junit.Assert; import org.junit.Test; -import org.junit.experimental.theories.suppliers.TestedOn; public class KMFrameworkTest { + private short status; + private short keyCharacteristics; + private short keyBlob; + private KMCryptoProvider sim; @Test public void test_Lifecycle_Success() { // Create simulator + //KMJcardSimulator.jcardSim = true; + sim = KMCryptoProviderImpl.instance(); + sim.bypassAesGcm(); CardSimulator simulator = new CardSimulator(); // Install applet @@ -49,43 +71,376 @@ public void test_Lifecycle_Success() { // Select applet simulator.selectApplet(appletAID1); +// testEncodeDecode(); testProvisionCmd(simulator); + testSetBootParams(simulator); testGetHwInfoCmd(simulator); testAddRngEntropyCmd(simulator); - + testGenerateRsaKey(simulator); + testImportRsaKey(simulator); + testGetKeyCharacteristics(simulator); + testGenerateAesKey(simulator); + testImportAesKey(simulator); + testGetKeyCharacteristics(simulator); + testGenerateEcKey(simulator); + testImportEcKey(simulator); + testGetKeyCharacteristics(simulator); + testGenerate3DesKey(simulator); + testImportDesKey(simulator); + testGetKeyCharacteristics(simulator); + testGenerateHmacKey(simulator); + testImportHmacKey(simulator); + testGetKeyCharacteristics(simulator); // Delete i.e. uninstall applet simulator.deleteApplet(appletAID1); } + private void testEncodeDecode() { + //128 + //ecb ode - blockmode + //padding pkcs 7 + short arrPtr = KMArray.instance((short)4); + short boolTag = KMBoolTag.instance(KMType.NO_AUTH_REQUIRED); + short keySize = KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, KMInteger.uint_16((short)128)); + short byteBlob = KMByteBlob.instance((short)1); + KMByteBlob.cast(byteBlob).add((short)0, KMType.ECB); + short blockMode = KMEnumArrayTag.instance(KMType.BLOCK_MODE,byteBlob); + byteBlob = KMByteBlob.instance((short)1); + KMByteBlob.cast(byteBlob).add((short)0, KMType.PKCS7); + short paddingMode = KMEnumArrayTag.instance(KMType.PADDING, byteBlob); + KMArray.cast(arrPtr).add((short)0, boolTag); + KMArray.cast(arrPtr).add((short)1, keySize); + KMArray.cast(arrPtr).add((short)2, blockMode); + KMArray.cast(arrPtr).add((short)3, paddingMode); + byte[] buf = new byte[1024]; + KMEncoder encode = new KMEncoder(); + KMDecoder decode = new KMDecoder(); + short len = encode.encode(arrPtr, buf, (short)0); + arrPtr = KMArray.instance((short)4); + KMArray.cast(arrPtr).add((short)0, KMBoolTag.exp()); + KMArray.cast(arrPtr).add((short)1, KMIntegerTag.exp(KMType.UINT_TAG)); + KMArray.cast(arrPtr).add((short)2, KMEnumArrayTag.exp()); + KMArray.cast(arrPtr).add((short)3, KMEnumArrayTag.exp()); + arrPtr = decode.decode(arrPtr,buf,(short)0,len); + KMArray arr = KMArray.cast(arrPtr); + short val = 0; + val = KMBoolTag.cast(arr.get((short)0)).getVal(); + val = KMInteger.cast(KMIntegerTag.cast(arr.get((short)1)).getValue()).getShort(); + val = KMEnumArrayTag.cast(arr.get((short)2)).get((short)0);; + val = KMEnumArrayTag.cast(arr.get((short)3)).get((short)0); + } + + private void testGetKeyCharacteristics(CardSimulator simulator) { + byte[] buf = new byte[1024]; + buf[0] = (byte)0x80; + buf[1] = (byte)0x1D; + buf[2] = (byte)0x40; + buf[3] = (byte)0x00; + buf[4] = 0; + short keyChar = keyCharacteristics; + short len = KMKeyCharacteristics.cast(keyChar).length(); + // test provision command + short cmd = makeGetKeyCharKeyCmd(); + KMEncoder enc = new KMEncoder(); + short actualLen = enc.encode(cmd, buf, (short) 7); + Util.setShort(buf, (short)5, actualLen); + byte[] apdu = new byte[7+actualLen]; + Util.arrayCopyNonAtomic(buf,(short)0,apdu,(short)0,(short)(7+actualLen)); + //CommandAPDU commandAPDU = new CommandAPDU(0x80, 0x10, 0x40, 0x00, buf, 0, actualLen); + CommandAPDU commandAPDU = new CommandAPDU(apdu); + //print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(commandAPDU); + extractKeyChar(response); + short len2 = KMKeyCharacteristics.cast(keyCharacteristics).length(); + short hwList = KMKeyCharacteristics.cast(keyCharacteristics).getHardwareEnforced(); + short len3 = KMKeyParameters.cast(hwList).length(); + short swList = KMKeyCharacteristics.cast(keyCharacteristics).getSoftwareEnforced(); + short len4 = KMKeyParameters.cast(swList).length(); + Assert.assertEquals(0x9000, response.getSW()); + } + public void testProvisionCmd(CardSimulator simulator){ - byte[] buf = new byte[512]; + byte[] buf = new byte[1024]; // test provision command - KMArray cmd = makeProvisionCmd(); + short cmd = makeProvisionCmd(); KMEncoder enc = new KMEncoder(); - short actualLen = enc.encode(cmd, buf, (short) 0, (short)buf.length); + short actualLen = enc.encode(cmd, buf, (short) 0); CommandAPDU commandAPDU = new CommandAPDU(0x80, 0x23, 0x40, 0x00, buf, 0, actualLen); //print(commandAPDU.getBytes());; ResponseAPDU response = simulator.transmitCommand(commandAPDU); Assert.assertEquals(0x9000, response.getSW()); } + public void testSetBootParams(CardSimulator simulator){ + byte[] buf = new byte[1024]; + // test provision command + short cmd = makeSetBootParamsCmd(); + KMEncoder enc = new KMEncoder(); + short actualLen = enc.encode(cmd, buf, (short) 0); + CommandAPDU commandAPDU = new CommandAPDU(0x80, 0x24, 0x40, 0x00, buf, 0, actualLen); + //print(commandAPDU.getBytes());; + ResponseAPDU response = simulator.transmitCommand(commandAPDU); + Assert.assertEquals(0x9000, response.getSW()); + } + + public void testGenerateRsaKey(CardSimulator simulator){ + byte[] buf = new byte[1024]; + buf[0] = (byte)0x80; + buf[1] = (byte)0x10; + buf[2] = (byte)0x40; + buf[3] = (byte)0x00; + buf[4] = 0; + // test provision command + short cmd = makeGenerateKeyCmd(KMType.RSA, (short)2048); + KMEncoder enc = new KMEncoder(); + short actualLen = enc.encode(cmd, buf, (short) 7); + Util.setShort(buf, (short)5, actualLen); + byte[] apdu = new byte[7+actualLen]; + Util.arrayCopyNonAtomic(buf,(short)0,apdu,(short)0,(short)(7+actualLen)); + //CommandAPDU commandAPDU = new CommandAPDU(0x80, 0x10, 0x40, 0x00, buf, 0, actualLen); + CommandAPDU commandAPDU = new CommandAPDU(apdu); + //print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(commandAPDU); + extractKeyCharAndBlob(response); + Assert.assertEquals(0x9000, response.getSW()); + } + + public void testImportRsaKey(CardSimulator simulator){ + byte[] buf = new byte[1024]; + buf[0] = (byte)0x80; + buf[1] = (byte)0x11; + buf[2] = (byte)0x40; + buf[3] = (byte)0x00; + buf[4] = 0; + short cmd = makeImportKeyRsaCmd(); + KMEncoder enc = new KMEncoder(); + short actualLen = enc.encode(cmd, buf, (short) 7); + Util.setShort(buf, (short)5, actualLen); + byte[] apdu = new byte[7+actualLen]; + Util.arrayCopyNonAtomic(buf,(short)0,apdu,(short)0,(short)(7+actualLen)); + //CommandAPDU commandAPDU = new CommandAPDU(0x80, 0x10, 0x40, 0x00, buf, 0, actualLen); + CommandAPDU commandAPDU = new CommandAPDU(apdu); + //print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(commandAPDU); + extractKeyCharAndBlob(response); + Assert.assertEquals(0x9000, response.getSW()); + } + + public void testImportEcKey(CardSimulator simulator){ + byte[] buf = new byte[1024]; + buf[0] = (byte)0x80; + buf[1] = (byte)0x11; + buf[2] = (byte)0x40; + buf[3] = (byte)0x00; + buf[4] = 0; + short cmd = makeImportKeyEcCmd(); + KMEncoder enc = new KMEncoder(); + short actualLen = enc.encode(cmd, buf, (short) 7); + Util.setShort(buf, (short)5, actualLen); + byte[] apdu = new byte[7+actualLen]; + Util.arrayCopyNonAtomic(buf,(short)0,apdu,(short)0,(short)(7+actualLen)); + //CommandAPDU commandAPDU = new CommandAPDU(0x80, 0x10, 0x40, 0x00, buf, 0, actualLen); + CommandAPDU commandAPDU = new CommandAPDU(apdu); + //print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(commandAPDU); + extractKeyCharAndBlob(response); + Assert.assertEquals(0x9000, response.getSW()); + } + + private void extractKeyCharAndBlob(ResponseAPDU response) { + short ret = KMArray.instance((short) 3); + KMArray.cast(ret).add((short) 0, KMInteger.exp()); + KMArray.cast(ret).add((short) 1, KMByteBlob.exp()); + short inst = KMKeyCharacteristics.exp(); + KMArray.cast(ret).add((short) 2, inst); + KMDecoder dec = new KMDecoder(); + byte[] respBuf = response.getBytes(); + short len = (short) respBuf.length; + ret = dec.decode(ret, respBuf, (short) 0, len); + status = KMArray.cast(ret).get((short)0); + keyBlob = KMArray.cast(ret).get((short)1); + keyCharacteristics = KMArray.cast(ret).get((short)2); + } + + private void extractKeyChar(ResponseAPDU response) { + short ret = KMArray.instance((short) 2); + KMArray.cast(ret).add((short) 0, KMInteger.exp()); + short inst = KMKeyCharacteristics.exp(); + KMArray.cast(ret).add((short) 1, inst); + KMDecoder dec = new KMDecoder(); + byte[] respBuf = response.getBytes(); + short len = (short) respBuf.length; + ret = dec.decode(ret, respBuf, (short) 0, len); + status = KMArray.cast(ret).get((short)0); + keyCharacteristics = KMArray.cast(ret).get((short)1); + } + + public void testGenerateAesKey(CardSimulator simulator){ + byte[] buf = new byte[1024]; + buf[0] = (byte)0x80; + buf[1] = (byte)0x10; + buf[2] = (byte)0x40; + buf[3] = (byte)0x00; + buf[4] = 0; + // test provision command + short cmd = makeGenerateKeyCmd(KMType.AES, (short)256); + KMEncoder enc = new KMEncoder(); + short actualLen = enc.encode(cmd, buf, (short) 7); + Util.setShort(buf, (short)5, actualLen); + byte[] apdu = new byte[7+actualLen]; + Util.arrayCopyNonAtomic(buf,(short)0,apdu,(short)0,(short)(7+actualLen)); + //CommandAPDU commandAPDU = new CommandAPDU(0x80, 0x10, 0x40, 0x00, buf, 0, actualLen); + CommandAPDU commandAPDU = new CommandAPDU(apdu); + //print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(commandAPDU); + extractKeyCharAndBlob(response); + Assert.assertEquals(0x9000, response.getSW()); + } + + public void testImportAesKey(CardSimulator simulator){ + byte[] buf = new byte[1024]; + buf[0] = (byte)0x80; + buf[1] = (byte)0x11; + buf[2] = (byte)0x40; + buf[3] = (byte)0x00; + buf[4] = 0; + short cmd = makeImportKeySymmCmd(KMType.AES, (short)128); + KMEncoder enc = new KMEncoder(); + short actualLen = enc.encode(cmd, buf, (short) 7); + Util.setShort(buf, (short)5, actualLen); + byte[] apdu = new byte[7+actualLen]; + Util.arrayCopyNonAtomic(buf,(short)0,apdu,(short)0,(short)(7+actualLen)); + //CommandAPDU commandAPDU = new CommandAPDU(0x80, 0x10, 0x40, 0x00, buf, 0, actualLen); + CommandAPDU commandAPDU = new CommandAPDU(apdu); + //print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(commandAPDU); + extractKeyCharAndBlob(response); + Assert.assertEquals(0x9000, response.getSW()); + } + public void testImportHmacKey(CardSimulator simulator){ + byte[] buf = new byte[1024]; + buf[0] = (byte)0x80; + buf[1] = (byte)0x11; + buf[2] = (byte)0x40; + buf[3] = (byte)0x00; + buf[4] = 0; + short cmd = makeImportKeySymmCmd(KMType.HMAC, (short)128); + KMEncoder enc = new KMEncoder(); + short actualLen = enc.encode(cmd, buf, (short) 7); + Util.setShort(buf, (short)5, actualLen); + byte[] apdu = new byte[7+actualLen]; + Util.arrayCopyNonAtomic(buf,(short)0,apdu,(short)0,(short)(7+actualLen)); + //CommandAPDU commandAPDU = new CommandAPDU(0x80, 0x10, 0x40, 0x00, buf, 0, actualLen); + CommandAPDU commandAPDU = new CommandAPDU(apdu); + //print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(commandAPDU); + extractKeyCharAndBlob(response); + Assert.assertEquals(0x9000, response.getSW()); + } + public void testImportDesKey(CardSimulator simulator){ + byte[] buf = new byte[1024]; + buf[0] = (byte)0x80; + buf[1] = (byte)0x11; + buf[2] = (byte)0x40; + buf[3] = (byte)0x00; + buf[4] = 0; + short cmd = makeImportKeySymmCmd(KMType.DES, (short)168); + KMEncoder enc = new KMEncoder(); + short actualLen = enc.encode(cmd, buf, (short) 7); + Util.setShort(buf, (short)5, actualLen); + byte[] apdu = new byte[7+actualLen]; + Util.arrayCopyNonAtomic(buf,(short)0,apdu,(short)0,(short)(7+actualLen)); + //CommandAPDU commandAPDU = new CommandAPDU(0x80, 0x10, 0x40, 0x00, buf, 0, actualLen); + CommandAPDU commandAPDU = new CommandAPDU(apdu); + //print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(commandAPDU); + extractKeyCharAndBlob(response); + Assert.assertEquals(0x9000, response.getSW()); + } + + public void testGenerateHmacKey(CardSimulator simulator){ + byte[] buf = new byte[1024]; + buf[0] = (byte)0x80; + buf[1] = (byte)0x10; + buf[2] = (byte)0x40; + buf[3] = (byte)0x00; + buf[4] = 0; + // test provision command + short cmd = makeGenerateKeyCmdHmac(KMType.HMAC, (short)128); + KMEncoder enc = new KMEncoder(); + short actualLen = enc.encode(cmd, buf, (short) 7); + Util.setShort(buf, (short)5, actualLen); + byte[] apdu = new byte[7+actualLen]; + Util.arrayCopyNonAtomic(buf,(short)0,apdu,(short)0,(short)(7+actualLen)); + //CommandAPDU commandAPDU = new CommandAPDU(0x80, 0x10, 0x40, 0x00, buf, 0, actualLen); + CommandAPDU commandAPDU = new CommandAPDU(apdu); + //print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(commandAPDU); + extractKeyCharAndBlob(response); + Assert.assertEquals(0x9000, response.getSW()); + } + + public void testGenerate3DesKey(CardSimulator simulator){ + byte[] buf = new byte[1024]; + buf[0] = (byte)0x80; + buf[1] = (byte)0x10; + buf[2] = (byte)0x40; + buf[3] = (byte)0x00; + buf[4] = 0; + // test provision command + short cmd = makeGenerateKeyCmd(KMType.DES, (short)168); + KMEncoder enc = new KMEncoder(); + short actualLen = enc.encode(cmd, buf, (short) 7); + Util.setShort(buf, (short)5, actualLen); + byte[] apdu = new byte[7+actualLen]; + Util.arrayCopyNonAtomic(buf,(short)0,apdu,(short)0,(short)(7+actualLen)); + //CommandAPDU commandAPDU = new CommandAPDU(0x80, 0x10, 0x40, 0x00, buf, 0, actualLen); + CommandAPDU commandAPDU = new CommandAPDU(apdu); + //print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(commandAPDU); + extractKeyCharAndBlob(response); + Assert.assertEquals(0x9000, response.getSW()); + } + + public void testGenerateEcKey(CardSimulator simulator){ + byte[] buf = new byte[1024]; + buf[0] = (byte)0x80; + buf[1] = (byte)0x10; + buf[2] = (byte)0x40; + buf[3] = (byte)0x00; + buf[4] = 0; + // test provision command + short cmd = makeGenerateKeyCmd(KMType.EC, (short)256); + KMEncoder enc = new KMEncoder(); + short actualLen = enc.encode(cmd, buf, (short) 7); + Util.setShort(buf, (short)5, actualLen); + byte[] apdu = new byte[7+actualLen]; + Util.arrayCopyNonAtomic(buf,(short)0,apdu,(short)0,(short)(7+actualLen)); + //CommandAPDU commandAPDU = new CommandAPDU(0x80, 0x10, 0x40, 0x00, buf, 0, actualLen); + CommandAPDU commandAPDU = new CommandAPDU(apdu); + //print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(commandAPDU); + extractKeyCharAndBlob(response); + Assert.assertEquals(0x9000, response.getSW()); + } public void testGetHwInfoCmd(CardSimulator simulator){ CommandAPDU commandAPDU = new CommandAPDU(0x80, 0x1E, 0x40, 0x00); //print(commandAPDU.getBytes()); ResponseAPDU response = simulator.transmitCommand(commandAPDU); KMDecoder dec = new KMDecoder(); - KMArray exp = KMArray.instance((short)3) - .add((short)0, KMEnum.instance().setType(KMType.HARDWARE_TYPE)) - .add((short)1, KMByteBlob.instance()) - .add((short)2, KMByteBlob.instance()); + short arrPtr = KMArray.instance((short)3); + KMArray exp = KMArray.cast(arrPtr); + exp.add((short)0, KMEnum.instance(KMType.HARDWARE_TYPE)); + exp.add((short)1, KMByteBlob.exp()); + exp.add((short)2, KMByteBlob.exp()); byte[] respBuf = response.getBytes(); short len = (short)respBuf.length; - KMArray resp = dec.decode(exp,respBuf, (short)0, len); - Assert.assertEquals(3, resp.length()); - KMEnum secLevel = (KMEnum)resp.get((short)0); - KMByteBlob kmName = (KMByteBlob) resp.get((short)1); - KMByteBlob authorName = (KMByteBlob) resp.get((short)2); - Assert.assertEquals(KMType.HARDWARE_TYPE, secLevel.getType()); + short respPtr = dec.decode(arrPtr,respBuf, (short)0, len); + Assert.assertEquals(3, KMArray.cast(respPtr).length()); + KMEnum secLevel = KMEnum.cast(KMArray.cast(respPtr).get((short)0)); + short kmName = KMArray.cast(respPtr).get((short)1); + short authorName = KMArray.cast(respPtr).get((short)2); + Assert.assertEquals(KMType.HARDWARE_TYPE, secLevel.getEnumType()); Assert.assertEquals(KMType.STRONGBOX, secLevel.getVal()); String kmNameStr = byteBlobToString(kmName); String authorNameStr = byteBlobToString(authorName); @@ -94,11 +449,11 @@ public void testGetHwInfoCmd(CardSimulator simulator){ Assert.assertEquals(0x9000, response.getSW()); } private void testAddRngEntropyCmd(CardSimulator simulator){ - byte[] buf = new byte[512]; + byte[] buf = new byte[1024]; // test provision command - KMArray cmd = makeAddRngEntropyCmd(); + short cmd = makeAddRngEntropyCmd(); KMEncoder enc = new KMEncoder(); - short actualLen = enc.encode(cmd, buf, (short) 0, (short)buf.length); + short actualLen = enc.encode(cmd, buf, (short) 0); CommandAPDU commandAPDU = new CommandAPDU(0x80, 0x18, 0x40, 0x00, buf, 0, actualLen); //print(commandAPDU.getBytes()); @@ -107,8 +462,9 @@ private void testAddRngEntropyCmd(CardSimulator simulator){ } - private String byteBlobToString(KMByteBlob blob) { + private String byteBlobToString(short blobPtr) { StringBuilder sb = new StringBuilder(); + KMByteBlob blob = KMByteBlob.cast(blobPtr); for(short i = 0; i + + @@ -60,7 +62,7 @@ - + + + + + + + + @@ -130,13 +146,22 @@ + + + + + + + + + diff --git a/Applet/default.output b/Applet/default.output index 49b8367e..465ff419 100644 --- a/Applet/default.output +++ b/Applet/default.output @@ -5,5 +5,16 @@ OUTPUT ON; CLA: 80, INS: b8, P1: 00, P2: 00, Lc: 0c, 0a, a0, 00, 00, 00, 62, 03, 01, 0c, 01, 01, 00, Le: 0a, a0, 00, 00, 00, 62, 03, 01, 0c, 01, 01, SW1: 90, SW2: 00 CLA: 00, INS: a4, P1: 04, P2: 00, Lc: 0a, a0, 00, 00, 00, 62, 03, 01, 0c, 01, 01, Le: 00, SW1: 90, SW2: 00 CLA: 80, INS: 23, P1: 40, P2: 00, Lc: 3b, 83, a1, 1a, 10, 00, 00, 02, 01, 00, 58, 30, 00, 01, 02, 03, 04, 05, 06, 07, 08, 09, 0a, 0b, 0c, 0d, 0e, 0f, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 1a, 1b, 1c, 1d, 1e, 1f, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 2a, 2b, 2c, 2d, 2e, 2f, Le: 00, SW1: 90, SW2: 00 +CLA: 80, INS: 24, P1: 40, P2: 00, Lc: 49, 86, 01, 01, 58, 20, 30, 30, 30, 31, 31, 31, 32, 32, 32, 33, 33, 33, 34, 34, 34, 35, 35, 35, 36, 36, 36, 37, 37, 37, 38, 38, 38, 39, 39, 39, 30, 30, 58, 20, 30, 30, 30, 31, 31, 31, 32, 32, 32, 33, 33, 33, 34, 34, 34, 35, 35, 35, 36, 36, 36, 37, 37, 37, 38, 38, 38, 39, 39, 39, 30, 30, 02, 00, Le: 00, SW1: 90, SW2: 00 CLA: 80, INS: 1e, P1: 40, P2: 00, Lc: 00, Le: 21, 83, 02, 57, 4a, 61, 76, 61, 63, 61, 72, 64, 4b, 65, 79, 6d, 61, 73, 74, 65, 72, 44, 65, 76, 69, 63, 65, 46, 47, 6f, 6f, 67, 6c, 65, SW1: 90, SW2: 00 CLA: 80, INS: 18, P1: 40, P2: 00, Lc: 23, 81, 58, 20, 00, 01, 02, 03, 04, 05, 06, 07, 08, 09, 0a, 0b, 0c, 0d, 0e, 0f, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 1a, 1b, 1c, 1d, 1e, 1f, Le: 00, SW1: 90, SW2: 00 +CLA: 80, INS: 10, P1: 40, P2: 00, Lc: 22, 81, a4, 1a, 10, 00, 00, 02, 01, 1a, 30, 00, 00, 03, 19, 08, 00, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 20, 00, 00, 01, 42, 02, 03, Le: 0d, 83, 00, 58, d4, 85, 58, 40, dc, 1d, ed, 50, 93, 56, 0c, 97, de, e7, 37, 82, 41, be, cc, 97, 19, 46, 6f, 18, 3e, 55, 1d, 95, b5, a2, 32, d6, 8d, c3, f9, ab, 16, e5, 8d, 7f, 8a, fe, 09, 86, f0, 83, 04, 50, ef, e1, 8d, 4e, d3, a9, 53, 99, b8, d4, bc, ad, 8f, a2, 5e, 8a, 84, 1a, ea, 29, 4c, 00, 47, 73, 4c, ad, c1, 48, a8, 52, e8, 13, dd, 4c, 48, c7, 53, a4, 74, a5, 3d, 80, 5c, 1a, ab, 92, 82, a0, a7, 1a, 10, 00, 00, 02, 01, 1a, 30, 00, 00, 03, 19, 08, 00, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 20, 00, 00, 01, 42, 02, 03, 1a, 10, 00, 02, be, 00, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, 58, 40, b4, 05, c4, 0e, 08, 15, 0e, a5, 86, 84, b2, 03, 00, 2c, cb, d7, 23, 85, c9, c1, 0b, 74, cb, 20, 10, b1, 25, 8e, 4b, 38, d4, 72, da, 2c, 2c, 64, 0e, e9, 2f, b2, d6, 74, 2e, 02, 77, 69, 74, 9f, a9, 98, e7, 7a, 4f, 9e, d4, 06, 62, 0c, 33, 6e, 80, 3a, 3f, e3, 82, a0, a7, 1a, 10, 00, 00, 02, 01, 1a, 30, 00, 00, 03, 19, 08, 00, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 20, 00, 00, 01, 42, 02, 03, 1a, 10, 00, 02, be, 00, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, SW1: 90, SW2: 00 +CLA: 80, INS: 10, P1: 40, P2: 00, Lc: 23, 81, a4, 1a, 10, 00, 00, 02, 18, 20, 1a, 30, 00, 00, 03, 19, 01, 00, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 20, 00, 00, 01, 42, 02, 03, Le: 9c, 83, 00, 58, 62, 84, 50, c9, a3, b8, 91, fe, 89, d9, f0, 6e, 62, 84, 1e, 4c, 60, eb, e3, 4c, e3, 82, 9d, 1c, ad, 27, 96, 78, 29, 11, 7e, 2e, 4c, cd, 00, 01, 7c, aa, 9d, 76, 28, 1f, 0a, e0, a0, 82, a0, a7, 1a, 10, 00, 00, 02, 18, 20, 1a, 30, 00, 00, 03, 19, 01, 00, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 20, 00, 00, 01, 42, 02, 03, 1a, 10, 00, 02, be, 00, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, 82, a0, a7, 1a, 10, 00, 00, 02, 18, 20, 1a, 30, 00, 00, 03, 19, 01, 00, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 20, 00, 00, 01, 42, 02, 03, 1a, 10, 00, 02, be, 00, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, SW1: 90, SW2: 00 +CLA: 80, INS: 10, P1: 40, P2: 00, Lc: 22, 81, a4, 1a, 10, 00, 00, 02, 03, 1a, 30, 00, 00, 03, 19, 01, 00, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 20, 00, 00, 01, 42, 02, 03, Le: d6, 83, 00, 58, 9d, 85, 58, 18, ba, 56, b4, 85, 20, 57, a9, b9, 77, 65, ba, e3, f4, 43, e6, 97, 70, fc, 3f, 49, 40, 93, 4e, 36, 4c, ae, 8e, 3e, 17, 1d, 67, 15, cc, 16, e8, f2, c4, 4c, 62, 66, 94, d1, 12, 0f, b0, 59, e4, c4, 1b, 91, 82, a0, a7, 1a, 10, 00, 00, 02, 03, 1a, 30, 00, 00, 03, 19, 01, 00, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 20, 00, 00, 01, 42, 02, 03, 1a, 10, 00, 02, be, 00, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, 58, 31, 04, 5a, 10, b8, d7, e9, be, 0a, e9, 47, 6c, 79, e4, 64, 15, 9c, 9d, 32, c6, 74, dd, 17, 11, 61, b7, 9a, 0c, 57, 8e, a1, 5f, 64, 1e, 8d, 51, 5b, 1e, 19, 4e, cc, 0e, 33, 15, d0, 9e, f6, f5, 8d, 2e, 82, a0, a7, 1a, 10, 00, 00, 02, 03, 1a, 30, 00, 00, 03, 19, 01, 00, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 20, 00, 00, 01, 42, 02, 03, 1a, 10, 00, 02, be, 00, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, SW1: 90, SW2: 00 +CLA: 80, INS: 10, P1: 40, P2: 00, Lc: 22, 81, a4, 1a, 10, 00, 00, 02, 18, 21, 1a, 30, 00, 00, 03, 18, a8, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 20, 00, 00, 01, 42, 02, 03, Le: 9a, 83, 00, 58, 61, 84, 50, ac, 61, 52, d1, e0, b1, 60, 86, 83, 04, f0, 27, 26, 82, 33, 5d, 4c, 70, dd, 86, 23, 76, 5a, 88, cb, f1, 89, 32, 38, 4c, 81, fb, d7, 08, c6, b7, 3d, c3, 4c, 4d, f2, 7f, 82, a0, a7, 1a, 10, 00, 00, 02, 18, 21, 1a, 30, 00, 00, 03, 18, a8, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 20, 00, 00, 01, 42, 02, 03, 1a, 10, 00, 02, be, 00, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, 82, a0, a7, 1a, 10, 00, 00, 02, 18, 21, 1a, 30, 00, 00, 03, 18, a8, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 20, 00, 00, 01, 42, 02, 03, 1a, 10, 00, 02, be, 00, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, SW1: 90, SW2: 00 +CLA: 80, INS: 10, P1: 40, P2: 00, Lc: 32, 81, a6, 1a, 10, 00, 00, 02, 18, 80, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, 1a, 90, 00, 02, 59, 44, 54, 65, 73, 74, 1a, 30, 00, 00, 08, 18, 80, 1a, 30, 00, 00, 03, 18, 80, 1a, 20, 00, 00, 05, 41, 04, Le: a6, 83, 00, 58, 67, 84, 50, b2, 44, 46, 9e, 6a, 14, 04, 41, a6, 58, 9c, 6f, 8b, ae, ed, 82, 4c, 5e, eb, 90, 37, 7a, 7b, f8, 61, 77, 30, 6c, 79, 4c, 0e, b8, 7e, 23, 1c, 0f, d8, af, b1, 97, 98, d6, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a7, 1a, 10, 00, 00, 02, 18, 80, 1a, 30, 00, 00, 08, 18, 80, 1a, 30, 00, 00, 03, 18, 80, 1a, 20, 00, 00, 05, 41, 04, 1a, 10, 00, 02, be, 00, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a7, 1a, 10, 00, 00, 02, 18, 80, 1a, 30, 00, 00, 08, 18, 80, 1a, 30, 00, 00, 03, 18, 80, 1a, 20, 00, 00, 05, 41, 04, 1a, 10, 00, 02, be, 00, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, SW1: 90, SW2: 00 +CLA: 80, INS: 11, P1: 40, P2: 00, Lc: b4, 83, a5, 1a, 10, 00, 00, 02, 01, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, 1a, 90, 00, 02, 59, 44, 54, 65, 73, 74, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 70, 00, 01, f7, 01, 03, 58, 85, 82, 58, 40, 80, 9a, 87, 4c, e1, a0, 71, 44, ae, 45, dd, 8d, 1c, 05, b4, d0, 44, 23, dd, 42, a7, c9, 53, 44, ac, 31, 4a, 22, 4a, 02, 65, a0, aa, 21, a8, 30, 94, 7d, 13, a1, bc, 89, 81, b5, 54, de, 75, 82, b9, 0b, 1a, 7a, 81, 0c, 51, e0, 2f, 91, 97, d4, e8, 33, 27, 61, 58, 40, 92, 6c, 79, 17, bb, 36, 6f, b7, 58, 25, 84, 98, a9, 56, 07, e6, 07, f6, 26, 92, 15, f6, 21, 9f, 6c, f0, b4, e7, 20, 42, ac, b6, d8, 30, 61, 06, c9, 3b, 30, 67, 1e, 8d, 74, 11, 8b, 06, 98, ab, 8d, 6a, 6c, cd, b7, 2f, c3, a8, 30, c7, 68, 03, 4f, 72, c7, 5b, Le: 1d, 83, 00, 58, dc, 85, 58, 40, ee, 9c, 17, 33, 10, 19, 8c, 42, 68, 14, dc, e7, d4, ac, 6e, 86, 74, af, 8c, 02, 9d, 9c, fb, f7, be, 3c, d4, bd, de, 9d, d8, fd, 51, eb, d8, df, fd, ce, a0, 9b, ff, 44, a9, a2, e8, eb, 44, 78, eb, 15, 04, ca, 6a, 98, 07, 7c, bb, ab, 07, be, 72, a0, f9, 1f, 4c, 74, b7, f8, 50, 81, 15, 16, c2, e9, 63, c2, 92, 4c, 75, ca, 0d, 0e, 29, 2e, 75, 05, 64, cf, a6, 91, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a7, 1a, 10, 00, 00, 02, 01, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 70, 00, 01, f7, 01, 1a, 30, 00, 00, 03, 19, 08, 00, 1a, 10, 00, 02, be, 02, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, 58, 40, 92, 6c, 79, 17, bb, 36, 6f, b7, 58, 25, 84, 98, a9, 56, 07, e6, 07, f6, 26, 92, 15, f6, 21, 9f, 6c, f0, b4, e7, 20, 42, ac, b6, d8, 30, 61, 06, c9, 3b, 30, 67, 1e, 8d, 74, 11, 8b, 06, 98, ab, 8d, 6a, 6c, cd, b7, 2f, c3, a8, 30, c7, 68, 03, 4f, 72, c7, 5b, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a7, 1a, 10, 00, 00, 02, 01, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 70, 00, 01, f7, 01, 1a, 30, 00, 00, 03, 19, 08, 00, 1a, 10, 00, 02, be, 02, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, SW1: 90, SW2: 00 +CLA: 80, INS: 11, P1: 40, P2: 00, Lc: 7d, 83, a4, 1a, 10, 00, 00, 02, 03, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, 1a, 90, 00, 02, 59, 44, 54, 65, 73, 74, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 03, 58, 54, 83, 58, 18, a6, 68, de, ec, 65, 6c, fb, ee, aa, 43, ef, 97, 9d, 10, 82, f0, 99, 5f, 10, f3, ee, 9c, 38, 57, 58, 31, 04, 3a, f8, f4, fa, 1f, e4, 4d, 62, a1, cd, 26, 8e, 1a, 5a, aa, f5, a8, 94, e3, 8b, 4c, ce, 49, a1, 57, 25, 81, 6d, be, 5c, 3b, 07, 95, b6, 89, 24, 6e, 9d, 25, 22, e6, 5f, 41, cc, 59, ce, 25, 0c, 1a, 10, 00, 00, 0a, 01, Le: e6, 83, 00, 58, a5, 85, 58, 18, dd, 87, 3a, 89, 4c, b4, 3b, b3, 7a, 02, dd, ac, 10, a9, 06, 27, b7, 86, bb, 06, 90, b8, 89, 86, 4c, 0d, ac, e8, c8, 2a, c9, 2a, e6, 8e, 4d, 7c, 5e, 4c, 5a, 32, 10, 2d, 01, 5a, 76, 62, 0f, e6, 62, 6c, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a7, 1a, 10, 00, 00, 02, 03, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 30, 00, 00, 03, 19, 01, 00, 1a, 10, 00, 00, 0a, 01, 1a, 10, 00, 02, be, 02, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, 58, 31, 04, 3a, f8, f4, fa, 1f, e4, 4d, 62, a1, cd, 26, 8e, 1a, 5a, aa, f5, a8, 94, e3, 8b, 4c, ce, 49, a1, 57, 25, 81, 6d, be, 5c, 3b, 07, 95, b6, 89, 24, 6e, 9d, 25, 22, e6, 5f, 41, cc, 59, ce, 25, 0c, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a7, 1a, 10, 00, 00, 02, 03, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 30, 00, 00, 03, 19, 01, 00, 1a, 10, 00, 00, 0a, 01, 1a, 10, 00, 02, be, 02, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, SW1: 90, SW2: 00 +CLA: 80, INS: 11, P1: 40, P2: 00, Lc: 3f, 83, a5, 1a, 10, 00, 00, 02, 18, 20, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, 1a, 90, 00, 02, 59, 44, 54, 65, 73, 74, 1a, 30, 00, 00, 03, 18, 80, 1a, 20, 00, 00, 05, 41, 04, 03, 52, 81, 50, 95, e6, 79, 36, 64, a5, ec, 72, bf, 01, 4c, 83, 6c, cd, cf, 51, Le: 98, 83, 00, 58, 60, 84, 50, 4c, 88, 06, bc, 20, cd, ba, 26, 2e, d1, 10, af, 70, 6e, 5f, 3a, 4c, 17, 80, e7, 2c, 13, ef, df, 3b, 09, e2, 0a, ff, 4c, b4, d5, 1d, 48, 47, a3, e1, e0, 20, 10, 05, 1c, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a6, 1a, 10, 00, 00, 02, 18, 20, 1a, 30, 00, 00, 03, 18, 80, 1a, 20, 00, 00, 05, 41, 04, 1a, 10, 00, 02, be, 02, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a6, 1a, 10, 00, 00, 02, 18, 20, 1a, 30, 00, 00, 03, 18, 80, 1a, 20, 00, 00, 05, 41, 04, 1a, 10, 00, 02, be, 02, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, SW1: 90, SW2: 00 +CLA: 80, INS: 11, P1: 40, P2: 00, Lc: 46, 83, a6, 1a, 10, 00, 00, 02, 18, 80, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, 1a, 90, 00, 02, 59, 44, 54, 65, 73, 74, 1a, 30, 00, 00, 03, 18, 80, 1a, 20, 00, 00, 05, 41, 04, 1a, 30, 00, 00, 08, 18, 80, 03, 52, 81, 50, fc, a6, 8f, 58, 68, 93, de, d0, c0, 74, 1c, 6f, 1d, 39, 2e, 4a, Le: a6, 83, 00, 58, 67, 84, 50, 0b, 7d, d5, e0, de, 79, f0, ce, 6c, f5, 6d, 5a, f9, f0, 3e, aa, 4c, 36, 7b, 2d, e5, cb, b7, 4a, 8e, 97, 1f, 44, d3, 4c, 4e, 09, 34, d3, 7f, d1, 23, 8b, 51, 70, fe, e8, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a7, 1a, 10, 00, 00, 02, 18, 80, 1a, 30, 00, 00, 03, 18, 80, 1a, 20, 00, 00, 05, 41, 04, 1a, 30, 00, 00, 08, 18, 80, 1a, 10, 00, 02, be, 02, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a7, 1a, 10, 00, 00, 02, 18, 80, 1a, 30, 00, 00, 03, 18, 80, 1a, 20, 00, 00, 05, 41, 04, 1a, 30, 00, 00, 08, 18, 80, 1a, 10, 00, 02, be, 02, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, SW1: 90, SW2: 00 +CLA: 80, INS: 11, P1: 40, P2: 00, Lc: 3f, 83, a5, 1a, 10, 00, 00, 02, 18, 21, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, 1a, 90, 00, 02, 59, 44, 54, 65, 73, 74, 1a, 30, 00, 00, 03, 18, a8, 1a, 20, 00, 00, 05, 41, 04, 03, 52, 81, 50, 8b, d4, d5, 84, 37, 39, c0, 1b, db, ed, 3c, 68, 99, 3a, dc, 3d, Le: 98, 83, 00, 58, 60, 84, 50, ad, 9d, 57, aa, 21, e9, c7, 97, b7, eb, 25, 14, c6, ad, f2, d1, 4c, b4, 12, 76, 4f, b3, 85, 6e, 0f, 79, 87, fc, 40, 4c, fc, 62, 73, 9c, 1b, 64, df, 15, 76, 28, 17, 43, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a6, 1a, 10, 00, 00, 02, 18, 21, 1a, 30, 00, 00, 03, 18, a8, 1a, 20, 00, 00, 05, 41, 04, 1a, 10, 00, 02, be, 02, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a6, 1a, 10, 00, 00, 02, 18, 21, 1a, 30, 00, 00, 03, 18, a8, 1a, 20, 00, 00, 05, 41, 04, 1a, 10, 00, 02, be, 02, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, SW1: 90, SW2: 00