From 1c4ee23cd2d232df61d4af5202055f2fcab412e6 Mon Sep 17 00:00:00 2001 From: cpathak Date: Mon, 8 Jun 2020 21:18:40 -0700 Subject: [PATCH 1/2] Changes with crypto provider Code changes to crypto provider. New feature implemented but not tested. --- .../javacard/keymaster/KMCipherImpl.java | 31 + .../keymaster/KMCryptoProviderImpl.java | 8 + .../javacard/keymaster/KMJcardSimulator.java | 672 +++++ .../javacard/keymaster/KMCipherImpl.java | 31 + .../keymaster/KMCryptoProviderImpl.java | 8 + .../javacard/keymaster/KMSimulator.java | 81 + .../android/javacard/keymaster/KMArray.java | 26 +- .../android/javacard/keymaster/KMAuthTag.java | 7 + .../android/javacard/keymaster/KMBoolTag.java | 4 +- .../javacard/keymaster/KMByteBlob.java | 12 +- .../android/javacard/keymaster/KMCipher.java | 30 + .../javacard/keymaster/KMCryptoProvider.java | 34 +- .../android/javacard/keymaster/KMDecoder.java | 8 +- .../android/javacard/keymaster/KMEnum.java | 6 +- .../javacard/keymaster/KMEnumArrayTag.java | 46 + .../keymaster/KMHardwareAuthToken.java | 5 +- .../android/javacard/keymaster/KMInteger.java | 20 + .../javacard/keymaster/KMIntegerArrayTag.java | 19 +- .../javacard/keymaster/KMKeymasterApplet.java | 2196 ++++++++++++++--- .../javacard/keymaster/KMOperationState.java | 132 +- .../javacard/keymaster/KMRepository.java | 247 +- .../android/javacard/keymaster/KMType.java | 21 +- .../android/javacard/keymaster/KMUtil.java | 109 - .../keymaster/KMVerificationToken.java | 11 +- .../javacard/test/KMFrameworkTest.java | 142 +- .../com/android/javacard/test/KMVTSTest.java | 646 +++++ Applet/JavaCardKeymaster.scr | 22 +- Applet/build.xml | 29 +- Applet/default.output | 22 +- 29 files changed, 3914 insertions(+), 711 deletions(-) create mode 100644 Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMCipherImpl.java create mode 100644 Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMCryptoProviderImpl.java create mode 100644 Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMJcardSimulator.java create mode 100644 Applet/Applet/OracleSimProvider/com/android/javacard/keymaster/KMCipherImpl.java create mode 100644 Applet/Applet/OracleSimProvider/com/android/javacard/keymaster/KMCryptoProviderImpl.java rename Applet/Applet/{src => OracleSimProvider}/com/android/javacard/keymaster/KMSimulator.java (88%) create mode 100644 Applet/Applet/src/com/android/javacard/keymaster/KMAuthTag.java create mode 100644 Applet/Applet/src/com/android/javacard/keymaster/KMCipher.java delete mode 100644 Applet/Applet/src/com/android/javacard/keymaster/KMUtil.java create mode 100644 Applet/Applet/test/com/android/javacard/test/KMVTSTest.java 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/src/com/android/javacard/keymaster/KMSimulator.java b/Applet/Applet/OracleSimProvider/com/android/javacard/keymaster/KMSimulator.java similarity index 88% rename from Applet/Applet/src/com/android/javacard/keymaster/KMSimulator.java rename to Applet/Applet/OracleSimProvider/com/android/javacard/keymaster/KMSimulator.java index 0d0c1394..51c87142 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMSimulator.java +++ b/Applet/Applet/OracleSimProvider/com/android/javacard/keymaster/KMSimulator.java @@ -26,6 +26,7 @@ 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; @@ -295,6 +296,7 @@ public short aesCCMSign( byte[] bufOut, short bufStart) { if (masterKeySecret.length > 16) { + return -1; } aes128Key.setKey(masterKeySecret, (short) 0); @@ -303,6 +305,10 @@ public short aesCCMSign( } @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 @@ -342,6 +348,77 @@ public DESKey createTDESKey(byte[] secretBuffer, short secretOff, short secretLe } @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; @@ -421,4 +498,8 @@ private void incrementCounter() { } } } + @Override + public void bypassAesGcm(){ + jcardSim = true; + } } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMArray.java b/Applet/Applet/src/com/android/javacard/keymaster/KMArray.java index 2935aa2b..4646f46e 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMArray.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMArray.java @@ -21,7 +21,9 @@ import javacard.framework.Util; public class KMArray extends KMType { - private static final short ARRAY_HEADER_SIZE = 3; + 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; @@ -35,28 +37,28 @@ private static KMArray proto(short ptr) { public static short exp() { short ptr = instance(ARRAY_TYPE, ARRAY_HEADER_SIZE); - heap[(short)(ptr + TLV_HEADER_SIZE)] = 0; - Util.setShort(heap,(short)(ptr + TLV_HEADER_SIZE + 1),(short)0 ); + Util.setShort(heap,(short)(ptr + TLV_HEADER_SIZE),(short)0 ); + Util.setShort(heap,(short)(ptr + TLV_HEADER_SIZE + 2),ANY_ARRAY_LENGTH ); return ptr; } - public static short exp(byte type) { + public static short exp(short type) { short ptr = instance(ARRAY_TYPE, ARRAY_HEADER_SIZE); - heap[(short)(ptr + TLV_HEADER_SIZE)] = type; - Util.setShort(heap,(short)(ptr + TLV_HEADER_SIZE + 1),(short)0 ); + 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 short instance(short length) { short ptr = KMType.instance(ARRAY_TYPE, (short)(ARRAY_HEADER_SIZE + (length*2))); - heap[(short)(ptr + TLV_HEADER_SIZE)] = 0; - Util.setShort(heap,(short)(ptr + TLV_HEADER_SIZE + 1),length); + Util.setShort(heap,(short)(ptr + TLV_HEADER_SIZE),(short)0); + Util.setShort(heap,(short)(ptr + TLV_HEADER_SIZE + 2),length); return ptr; } public static short instance(short length, byte type) { short ptr = instance(length); - heap[(short)(ptr + TLV_HEADER_SIZE)] = type; + Util.setShort(heap,(short)(ptr + TLV_HEADER_SIZE),type); return ptr; } @@ -74,17 +76,17 @@ public void add(short index, short objPtr) { 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 + 3 + (short)(index*2))); + return Util.getShort(heap,(short) (instPtr + TLV_HEADER_SIZE + ARRAY_HEADER_SIZE + (short)(index*2))); } - public byte containedType(){ return heap[(short)(instPtr + TLV_HEADER_SIZE)];} + 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 + 1)); + return Util.getShort(heap, (short) (instPtr + TLV_HEADER_SIZE + 2)); } public byte[] getBuffer() { 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/KMBoolTag.java b/Applet/Applet/src/com/android/javacard/keymaster/KMBoolTag.java index e56dc9f3..ae0f75ac 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMBoolTag.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMBoolTag.java @@ -47,8 +47,8 @@ private static KMBoolTag proto(short ptr) { // pointer to an empty instance used as expression public static short exp() { - short ptr = KMType.exp(TAG_TYPE); - Util.setShort(heap, (short)(ptr+1), BOOL_TAG); + short ptr = instance(TAG_TYPE, (short)2); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE), BOOL_TAG); return ptr; } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMByteBlob.java b/Applet/Applet/src/com/android/javacard/keymaster/KMByteBlob.java index 423220fa..7f598eaa 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMByteBlob.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMByteBlob.java @@ -91,6 +91,11 @@ public byte[] getBuffer() { 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 void setValue(byte[] srcBuf, short srcStart, short srcLength){ if(length() > srcLength){ @@ -98,5 +103,10 @@ public void setValue(byte[] srcBuf, short srcStart, short srcLength){ } 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/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/KMCryptoProvider.java b/Applet/Applet/src/com/android/javacard/keymaster/KMCryptoProvider.java index 8c44d28f..055822e5 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMCryptoProvider.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMCryptoProvider.java @@ -4,8 +4,10 @@ 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(); @@ -68,13 +70,39 @@ short aesCCMSign( byte[] bufOut, short bufStart); - ECPrivateKey createEcPrivateKey(byte[] pubBuffer, short pubOff, short pubLength, - byte[] privBuffer, short privOff, short privLength); + 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 createRsaPrivateKey(byte[] modBuffer, short modOff, short modLength, + 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 25c1c1b1..daefb282 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMDecoder.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMDecoder.java @@ -219,15 +219,17 @@ private short decodeBytesTag(short exp) { private short decodeArray(short exp) { short payloadLength = readMajorTypeWithPayloadLength(ARRAY_TYPE); - if (KMArray.cast(exp).length() != payloadLength) { - ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); - } 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); diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMEnum.java b/Applet/Applet/src/com/android/javacard/keymaster/KMEnum.java index 00ba0cd1..d5a69270 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMEnum.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMEnum.java @@ -25,7 +25,7 @@ public class KMEnum extends KMType { private static short instPtr; private static short[] types = {HARDWARE_TYPE, KEY_FORMAT, KEY_DERIVATION_FUNCTION, - VERIFIED_BOOT_STATE, DEVICE_LOCKED}; + VERIFIED_BOOT_STATE, DEVICE_LOCKED, USER_AUTH_TYPE, PURPOSE}; private static Object[] enums = null; @@ -90,7 +90,9 @@ private static void create() { ISO18033_2_KDF2_SHA256 }, new byte[] {SELF_SIGNED_BOOT, VERIFIED_BOOT}, - new byte[] {DEVICE_LOCKED_TRUE, DEVICE_LOCKED_FALSE} + 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} }; } } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMEnumArrayTag.java b/Applet/Applet/src/com/android/javacard/keymaster/KMEnumArrayTag.java index d29903e1..1b1a56ee 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMEnumArrayTag.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMEnumArrayTag.java @@ -139,4 +139,50 @@ private static byte[] getAllowedEnumValues(short key) { return null; } + 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 short get(short index){ + return KMByteBlob.cast(getValues()).get(index); + } + + 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; + } + index++; + } + } + 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; + } + index++; + } + return false; + } } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMHardwareAuthToken.java b/Applet/Applet/src/com/android/javacard/keymaster/KMHardwareAuthToken.java index c5c92d86..23245352 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMHardwareAuthToken.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMHardwareAuthToken.java @@ -39,7 +39,7 @@ public static short exp() { arr.add(CHALLENGE, KMInteger.exp()); arr.add(USER_ID, KMInteger.exp()); arr.add(AUTHENTICATOR_ID, KMInteger.exp()); - arr.add(HW_AUTHENTICATOR_TYPE, KMEnumTag.instance(KMType.USER_AUTH_TYPE)); + arr.add(HW_AUTHENTICATOR_TYPE, KMEnum.instance(KMType.USER_AUTH_TYPE)); arr.add(TIMESTAMP, KMInteger.exp()); arr.add(MAC, KMByteBlob.exp()); return instance(arrPtr); @@ -119,8 +119,7 @@ public short getHwAuthenticatorType() { } public void setHwAuthenticatorType(short vals) { - short key = KMEnumTag.cast(vals).getKey(); - if(key != USER_AUTH_TYPE) ISOException.throwIt(ISO7816.SW_DATA_INVALID); + KMEnum.cast(vals); short arrPtr = getVals(); KMArray.cast(arrPtr).add(HW_AUTHENTICATOR_TYPE, vals); } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMInteger.java b/Applet/Applet/src/com/android/javacard/keymaster/KMInteger.java index 994a94cc..6fff9f04 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMInteger.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMInteger.java @@ -138,4 +138,24 @@ public short getSignificantShort(){ public byte getByte() { return heap[(short) (instPtr + TLV_HEADER_SIZE + 3)]; } + + public boolean isZero() { + if(getShort() == 0 && getSignificantShort() == 0){ + return true; + } + return false; + } + + 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 5f0e6731..c154f5a0 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMIntegerArrayTag.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMIntegerArrayTag.java @@ -97,7 +97,7 @@ public short getValues() { public short length() { short ptr = getValues(); - return KMIntegerArrayTag.cast(ptr).length(); + return KMArray.cast(ptr).length(); } public void add(short index, short val) { @@ -127,4 +127,21 @@ private static boolean validateTagType(short tagType) { } return false; } + + 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; + } + } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java b/Applet/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java index 8ea7ad74..e8b102dd 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java @@ -29,7 +29,9 @@ 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; /** @@ -42,11 +44,25 @@ public class KMKeymasterApplet extends Applet implements AppletEvent, ExtendedLength { // Constants. public static final byte AES_BLOCK_SIZE = 16; - public static final short MAX_LENGTH = (short) 0x1000; + 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 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. private static final byte ILLEGAL_STATE = 0x00; private static final byte INSTALL_STATE = 0x01; @@ -77,7 +93,7 @@ public class KMKeymasterApplet extends Applet implements AppletEvent, ExtendedLe 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 = 25; + 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; @@ -99,6 +115,16 @@ public class KMKeymasterApplet extends Applet implements AppletEvent, ExtendedLe 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; + // AddRngEntropy private static final short MAX_SEED_SIZE = 2048; // Keyblob constants @@ -110,6 +136,9 @@ public class KMKeymasterApplet extends Applet implements AppletEvent, ExtendedLe // 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; @@ -127,11 +156,29 @@ public class KMKeymasterApplet extends Applet implements AppletEvent, ExtendedLe /** Registers this applet. */ protected KMKeymasterApplet() { // TODO change this to make this compile time variation. - cryptoProvider = new KMSimulator(); + 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); - tmpVariables = JCSystem.makeTransientShortArray((short) TMP_VARIABLE_ARRAY_SIZE, JCSystem.CLEAR_ON_RESET); - repository = new KMRepository(cryptoProvider.getTrueRandomNumber((short) 256)); + 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(); @@ -271,7 +318,7 @@ public void process(APDU apdu) { processDestroyAttIdsCmd(apdu); break; case INS_VERIFY_AUTHORIZATION_CMD: - processVerifyAuthenticationCmd(apdu); + processVerifyAuthorizationCmd(apdu); break; case INS_GET_HMAC_SHARING_PARAM_CMD: processGetHmacSharingParamCmd(apdu); @@ -304,13 +351,26 @@ public void process(APDU apdu) { ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED); } } catch (KMException exception) { + 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(); } } - + 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) { @@ -341,34 +401,6 @@ public static void receiveIncoming(APDU apdu) { } } - 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(); - Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) apdu.getBuffer().length, (byte) 0); - // 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 processGetHwInfoCmd(APDU apdu) { // No arguments expected final byte[] JavacardKeymasterDevice = { @@ -409,74 +441,1507 @@ private void processAddRngEntropyCmd(APDU apdu) { if (blob.length() > MAX_SEED_SIZE) { KMException.throwIt(KMError.INVALID_ARGUMENT); } - cryptoProvider.addRngEntropy(blob.getBuffer(), blob.getStartOff(), blob.length()); + 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) { + // No Arguments + byte[] scratchPad = apdu.getBuffer(); + // Create blob containing seed + tmpVariables[0] = + KMByteBlob.instance(repository.getHmacSeed(), (short) 0, repository.HMAC_SEED_NONCE_SIZE); + // Create blob containing nonce + cryptoProvider.newRandomNumber(scratchPad, (short) 0, repository.HMAC_SEED_NONCE_SIZE); + tmpVariables[1] = KMByteBlob.instance(scratchPad, (short) 0, repository.HMAC_SEED_NONCE_SIZE); + // Create HMAC Sharing Parameters + tmpVariables[2] = KMHmacSharingParameters.instance(); + KMHmacSharingParameters.cast(tmpVariables[2]).setNonce(tmpVariables[1]); + KMHmacSharingParameters.cast(tmpVariables[2]).setSeed(tmpVariables[0]); + // prepare the response + tmpVariables[3] = KMArray.instance((short) 2); + KMArray.cast(tmpVariables[3]).add((short) 0, KMInteger.uint_16(KMError.OK)); + KMArray.cast(tmpVariables[3]).add((short) 1, tmpVariables[2]); + // Encode the response + bufferLength = encoder.encode(tmpVariables[0], buffer, bufferStartOffset); + sendOutgoing(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) { + // Receive the incoming request fully from the master into buffer. + receiveIncoming(apdu); + byte[] scratchPad = apdu.getBuffer(); + tmpVariables[1] = KMArray.instance((short) 1); + tmpVariables[2] = KMKeyParameters.exp(); + tmpVariables[3] = KMHmacSharingParameters.exp(); + KMArray.cast(tmpVariables[1]).add((short) 0, KMArray.exp(tmpVariables[3])); // Vector + KMArray.cast(tmpVariables[1]).add((short) 1, tmpVariables[2]); // Key Params + // Decode the arguments + tmpVariables[2] = decoder.decode(tmpVariables[1], buffer, bufferStartOffset, bufferLength); + data[KEY_PARAMETERS] = KMArray.cast(tmpVariables[2]).get((short) 1); + data[HMAC_SHARING_PARAMS] = KMArray.cast(tmpVariables[2]).get((short) 0); + // Concatenate HMAC Params + tmpVariables[0] = 0; + tmpVariables[1] = KMArray.cast(data[HMAC_SHARING_PARAMS]).length(); + tmpVariables[5] = 0; // index in scratchPad + while (tmpVariables[0] < tmpVariables[1]) { + // read HmacSharingParam + tmpVariables[2] = KMArray.cast(data[HMAC_SHARING_PARAMS]).get(tmpVariables[0]); + // get seed + tmpVariables[3] = KMHmacSharingParameters.cast(tmpVariables[2]).getSeed(); + tmpVariables[4] = KMByteBlob.cast(tmpVariables[3]).length(); + // if seed is present + if (tmpVariables[4] == HMAC_SEED_SIZE /*32*/) { + // then copy that to scratchPad + Util.arrayCopyNonAtomic( + KMByteBlob.cast(tmpVariables[3]).getBuffer(), + KMByteBlob.cast(tmpVariables[3]).getStartOff(), + scratchPad, + tmpVariables[5], + tmpVariables[4]); + tmpVariables[5] += tmpVariables[4]; + } + // get nonce + tmpVariables[3] = KMHmacSharingParameters.cast(tmpVariables[2]).getNonce(); + tmpVariables[4] = KMByteBlob.cast(tmpVariables[3]).length(); + // if nonce is not present + if (tmpVariables[4] != HMAC_NONCE_SIZE /*32*/) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + // copy nonce to scratchPad + Util.arrayCopyNonAtomic( + KMByteBlob.cast(tmpVariables[3]).getBuffer(), + KMByteBlob.cast(tmpVariables[3]).getStartOff(), + scratchPad, + tmpVariables[5], + tmpVariables[4]); + tmpVariables[5] += tmpVariables[4]; + } + // ckdf to derive hmac key + HMACKey key = + cryptoProvider.cmacKdf( + repository.getHmacKey(), ckdfLable, scratchPad, (short) 0, tmpVariables[5]); + tmpVariables[5] = key.getKey(scratchPad, (short) 0); + repository.initComputedHmac(scratchPad, (short) 0, tmpVariables[5]); + // Generate sharingKey verification + tmpVariables[5] = + cryptoProvider.hmacSign( + key, sharingCheck, (short) 0, (short) sharingCheck.length, scratchPad, (short) 0); + tmpVariables[1] = KMByteBlob.instance(scratchPad, (short) 0, tmpVariables[5]); + // prepare the 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, tmpVariables[1]); + // Encode the response + bufferLength = encoder.encode(tmpVariables[0], buffer, bufferStartOffset); + sendOutgoing(apdu); + } + + private void processUpgradeKeyCmd(APDU apdu) { + // Receive the incoming request fully from the master into buffer. + receiveIncoming(apdu); + byte[] scratchPad = apdu.getBuffer(); + tmpVariables[1] = KMArray.instance((short) 2); + tmpVariables[2] = KMKeyParameters.exp(); + KMArray.cast(tmpVariables[1]).add((short) 0, KMByteBlob.exp()); // Key Blob + KMArray.cast(tmpVariables[1]).add((short) 1, tmpVariables[2]); // Key Params + // Decode the arguments + tmpVariables[2] = decoder.decode(tmpVariables[1], buffer, bufferStartOffset, bufferLength); + data[KEY_BLOB] = KMArray.cast(tmpVariables[2]).get((short) 0); + data[KEY_PARAMETERS] = KMArray.cast(tmpVariables[2]).get((short) 1); + tmpVariables[0] = + KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.APPLICATION_ID, data[KEY_PARAMETERS]); + if (tmpVariables[0] != KMTag.INVALID_VALUE) { + data[APP_ID] = KMByteTag.cast(tmpVariables[0]).getValue(); + } + tmpVariables[0] = + KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.APPLICATION_DATA, data[KEY_PARAMETERS]); + if (tmpVariables[0] != KMTag.INVALID_VALUE) { + data[APP_DATA] = KMByteTag.cast(tmpVariables[0]).getValue(); + } + // parse existing key blob + parseEncryptedKeyBlob(scratchPad); + // validate characteristics to be upgraded. + 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 + Util.arrayFillNonAtomic(scratchPad, (short) 0, tmpVariables[0], (byte) 0); + // If the os version is not zero + if (Util.arrayCompare( + repository.osVersion, (short) 0, scratchPad, (short) 0, tmpVariables[0]) + != 0) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + } + } + 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]) + < 0) { + // If the key characteristics has os patch level > current os patch + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + } + // remove Auth Tag + repository.removeAuthTag(data[AUTH_TAG]); + // copy origin + data[ORIGIN] = KMEnumTag.getValue(KMType.ORIGIN, data[HW_PARAMETERS]); + // create new key blob with current os version etc. + createEncryptedKeyBlob(scratchPad); + // persist new 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 processExportKeyCmd(APDU apdu) { + sendError(apdu, KMError.UNIMPLEMENTED); + } + + private void processImportWrappedKeyCmd(APDU apdu) { + // Currently only RAW formatted import key blob are supported + 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) 11); + // Arguments + tmpVariables[2] = KMKeyParameters.exp(); + KMArray.cast(tmpVariables[1]).add((short) 0, tmpVariables[2]); // Key Params + KMArray.cast(tmpVariables[1]).add((short) 1, KMEnum.instance(KMType.KEY_FORMAT)); // Key Format + KMArray.cast(tmpVariables[1]).add((short) 2, KMByteBlob.exp()); // Wrapped Import Key Blob + KMArray.cast(tmpVariables[1]).add((short) 3, KMByteBlob.exp()); // Auth Tag + KMArray.cast(tmpVariables[1]).add((short) 4, KMByteBlob.exp()); // IV - Nonce + KMArray.cast(tmpVariables[1]).add((short) 5, KMByteBlob.exp()); // Encrypted Transport Key + KMArray.cast(tmpVariables[1]).add((short) 6, KMByteBlob.exp()); // Wrapping Key KeyBlob + KMArray.cast(tmpVariables[1]).add((short) 7, KMByteBlob.exp()); // Masking Key + KMArray.cast(tmpVariables[1]).add((short) 8, tmpVariables[2]); // Un-wrapping Params + KMArray.cast(tmpVariables[1]).add((short) 9, KMInteger.exp()); // Password Sid + KMArray.cast(tmpVariables[1]).add((short) 10, KMInteger.exp()); // Biometric Sid + // Decode the arguments + tmpVariables[2] = decoder.decode(tmpVariables[1], buffer, bufferStartOffset, bufferLength); + tmpVariables[3] = KMArray.cast(tmpVariables[2]).get((short) 0); + // get algorithm + tmpVariables[3] = KMEnumTag.getValue(KMType.ALGORITHM, tmpVariables[3]); + if (tmpVariables[3] == KMType.INVALID_VALUE) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + tmpVariables[3] = KMEnumTag.getValue(KMType.ALGORITHM, data[KEY_PARAMETERS]); + if (tmpVariables[3] == KMType.RSA + || tmpVariables[3] == KMType.EC) { // RSA and EC not implemented + KMException.throwIt(KMError.UNIMPLEMENTED); + } + // Key format must be RAW format - X509 and PKCS8 not implemented. + tmpVariables[3] = KMArray.cast(tmpVariables[2]).get((short) 1); + tmpVariables[3] = KMEnum.cast(tmpVariables[3]).getVal(); + if (tmpVariables[3] != KMType.RAW) { + KMException.throwIt(KMError.UNIMPLEMENTED); + } + data[AUTH_DATA] = KMArray.cast(tmpVariables[2]).get((short) 3); + data[AUTH_TAG] = KMArray.cast(tmpVariables[2]).get((short) 4); + data[NONCE] = KMArray.cast(tmpVariables[2]).get((short) 5); + data[ENC_TRANSPORT_KEY] = KMArray.cast(tmpVariables[2]).get((short) 6); + data[MASKING_KEY] = KMArray.cast(tmpVariables[2]).get((short) 8); + // Step 1 - parse wrapping key blob + data[KEY_PARAMETERS] = KMArray.cast(tmpVariables[2]).get((short) 9); // wrapping key parameters + // Check for app id and app data. + data[APP_ID] = KMType.INVALID_VALUE; + data[APP_DATA] = KMType.INVALID_VALUE; + tmpVariables[3] = + KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.APPLICATION_ID, data[KEY_PARAMETERS]); + if (tmpVariables[3] != KMTag.INVALID_VALUE) { + data[APP_ID] = KMByteTag.cast(tmpVariables[3]).getValue(); + } + tmpVariables[3] = + KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.APPLICATION_DATA, data[KEY_PARAMETERS]); + if (tmpVariables[3] != KMTag.INVALID_VALUE) { + data[APP_DATA] = KMByteTag.cast(tmpVariables[3]).getValue(); + } + // wrapping key blob + data[KEY_BLOB] = KMArray.cast(tmpVariables[2]).get((short) 7); + parseEncryptedKeyBlob(scratchPad); + + // Step 2 - Decrypt the encrypted transport key + // enforce authorization for WRAP_KEY operation using RSA algorithm according to javacard caps. + if (KMEnumTag.getValue(KMType.ALGORITHM, data[HW_PARAMETERS]) != KMType.RSA) { + KMException.throwIt(KMError.INCOMPATIBLE_ALGORITHM); + } + if (!(KMEnumArrayTag.contains(KMType.DIGEST, KMType.SHA2_256, data[HW_PARAMETERS]))) { + KMException.throwIt(KMError.INCOMPATIBLE_DIGEST); + } + if (!(KMEnumArrayTag.contains(KMType.PADDING, KMType.RSA_OAEP, data[HW_PARAMETERS]))) { + KMException.throwIt(KMError.INCOMPATIBLE_PADDING_MODE); + } + KMCipher cipher = + cryptoProvider.createRsaDecrypt( + KMCipher.CIPHER_RSA, + KMCipher.PAD_PKCS1_OAEP_SHA256, + KMByteBlob.cast(data[SECRET]).getBuffer(), + KMByteBlob.cast(data[SECRET]).getStartOff(), + KMByteBlob.cast(data[SECRET]).length(), + KMByteBlob.cast(data[PUB_KEY]).getBuffer(), + KMByteBlob.cast(data[PUB_KEY]).getStartOff(), + KMByteBlob.cast(data[PUB_KEY]).length()); + // Decrypt the transport key + tmpVariables[3] = + cipher.doFinal( + KMByteBlob.cast(data[ENC_TRANSPORT_KEY]).getBuffer(), + KMByteBlob.cast(data[ENC_TRANSPORT_KEY]).getStartOff(), + KMByteBlob.cast(data[ENC_TRANSPORT_KEY]).length(), + scratchPad, + (short) 0); + data[SECRET] = KMByteBlob.instance(scratchPad, (short) 0, tmpVariables[3]); + cryptoProvider.delete(cipher); + + // Step 3 - XOR with masking key + tmpVariables[4] = KMByteBlob.cast(data[MASKING_KEY]).length(); + if (tmpVariables[3] != tmpVariables[4]) { + KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH); + } + tmpVariables[3] = 0; // index in scratchPad + byte[] buf = KMByteBlob.cast(MASKING_KEY).getBuffer(); + tmpVariables[5] = KMByteBlob.cast(MASKING_KEY).getStartOff(); + while (tmpVariables[3] < tmpVariables[4]) { + scratchPad[tmpVariables[3]] = + (byte) (scratchPad[tmpVariables[3]] ^ buf[(short) (tmpVariables[3] + tmpVariables[5])]); + scratchPad[3]++; + } + data[SECRET] = KMByteBlob.instance(scratchPad, (short) 0, tmpVariables[3]); + + // Step 4 - AES-GCM decrypt + data[IMPORTED_KEY_BLOB] = KMArray.cast(tmpVariables[2]).get((short) 2); + data[AUTH_DATA] = KMArray.cast(tmpVariables[2]).get((short) 3); + data[AUTH_TAG] = KMArray.cast(tmpVariables[2]).get((short) 4); + data[NONCE] = KMArray.cast(tmpVariables[2]).get((short) 5); + data[ENC_TRANSPORT_KEY] = KMArray.cast(tmpVariables[2]).get((short) 6); + data[MASKING_KEY] = KMArray.cast(tmpVariables[2]).get((short) 8); + AESKey key = + cryptoProvider.createAESKey( + KMByteBlob.cast(data[SECRET]).getBuffer(), + KMByteBlob.cast(data[SECRET]).getStartOff(), + KMByteBlob.cast(data[SECRET]).length()); + boolean verification = + cryptoProvider.aesGCMDecrypt( + key, + KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).getBuffer(), + KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).getStartOff(), + KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).length(), + scratchPad, + (short) 0, + KMByteBlob.cast(data[NONCE]).getBuffer(), + KMByteBlob.cast(data[NONCE]).getStartOff(), + KMByteBlob.cast(data[NONCE]).length(), + KMByteBlob.cast(data[AUTH_DATA]).getBuffer(), + KMByteBlob.cast(data[AUTH_DATA]).getStartOff(), + KMByteBlob.cast(data[AUTH_DATA]).length(), + KMByteBlob.cast(data[AUTH_TAG]).getBuffer(), + KMByteBlob.cast(data[AUTH_TAG]).getStartOff(), + KMByteBlob.cast(data[AUTH_TAG]).length()); + if (verification == false) { + KMException.throwIt(KMError.IMPORTED_KEY_VERIFICATION_FAILED); + } + cryptoProvider.delete(key); + + // Step 5 - Import Decrypted Key. + data[ORIGIN] = KMType.SECURELY_IMPORTED; + data[KEY_PARAMETERS] = KMArray.cast(tmpVariables[2]).get((short) 0); + importKey(apdu, scratchPad); + } + + 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 + receiveIncoming(apdu); + byte[] scratchPad = apdu.getBuffer(); + Util.arrayFill(scratchPad, (short)0,(short)256, (byte)0); + tmpVariables[1] = KMArray.instance((short) 6); + // Arguments + tmpVariables[2] = KMKeyParameters.exp(); + KMArray.cast(tmpVariables[1]).add((short) 0, KMInteger.exp()); + KMArray.cast(tmpVariables[1]).add((short) 1, tmpVariables[2]); + KMArray.cast(tmpVariables[1]).add((short) 2, KMByteBlob.exp()); + KMArray.cast(tmpVariables[1]).add((short) 3, KMByteBlob.exp()); + tmpVariables[3] = KMHardwareAuthToken.exp(); + KMArray.cast(tmpVariables[1]).add((short) 4, tmpVariables[3]); + tmpVariables[4] = KMVerificationToken.exp(); + KMArray.cast(tmpVariables[1]).add((short) 5, tmpVariables[4]); + // Decode the arguments + tmpVariables[2] = decoder.decode(tmpVariables[1], buffer, bufferStartOffset, bufferLength); + data[OP_HANDLE] = KMArray.cast(tmpVariables[2]).get((short) 0); + data[KEY_PARAMETERS] = KMArray.cast(tmpVariables[2]).get((short) 1); + data[INPUT_DATA] = KMArray.cast(tmpVariables[2]).get((short) 2); + data[HW_TOKEN] = KMArray.cast(tmpVariables[2]).get((short) 4); + data[VERIFICATION_TOKEN] = KMArray.cast(tmpVariables[2]).get((short) 5); + // Check Operation Handle + tmpVariables[1] = KMInteger.cast(data[OP_HANDLE]).getShort(); + KMOperationState op = repository.findOperation(tmpVariables[1]); + if (KMInteger.compare(data[OP_HANDLE], KMInteger.uint_16(op.getHandle())) != 0) { + KMException.throwIt(KMError.INVALID_OPERATION_HANDLE); + } + //Authorize the final operation + authorizeUpdateFinalOperation(op, scratchPad); + short len = 0; + // If the operation is signing + if(op.getPurpose() == KMType.SIGN){ + // Perform trusted confirmation if required + if (op.isTrustedConfirmationRequired()) { + tmpVariables[0] = KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.CONFIRMATION_TOKEN, data[KEY_PARAMETERS]); + if(tmpVariables[0] == KMType.INVALID_VALUE){ + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + tmpVariables[0] = KMByteTag.cast(tmpVariables[0]).getValue(); + tmpVariables[1] = op.getTrustedConfirmationSigner() + .sign( + KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), + KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), + KMByteBlob.cast(data[INPUT_DATA]).length(), scratchPad, (short)0); + if(tmpVariables[1] != KMByteBlob.cast(tmpVariables[0]).length() ){ + KMException.throwIt(KMError.VERIFICATION_FAILED); + } + tmpVariables[0]=Util.arrayCompare(scratchPad,(short)0, + KMByteBlob.cast(tmpVariables[0]).getBuffer(), + KMByteBlob.cast(tmpVariables[0]).getStartOff(), + tmpVariables[1]); + if(tmpVariables[0] != 0){ + KMException.throwIt(KMError.VERIFICATION_FAILED); + } + } + tmpVariables[1] = op.getSigner().getCipherAlgorithm(); + tmpVariables[2] = op.getSigner().getMessageDigestAlgorithm(); + tmpVariables[3] = op.getSigner().getPaddingAlgorithm(); + len = KMByteBlob.cast(data[INPUT_DATA]).length(); + //For RSA Signing algorithm + if(tmpVariables[1] == Signature.SIG_CIPHER_RSA){ + //If no padding and no digest - then zero padding up to 256 on left + if(tmpVariables[2] == MessageDigest.ALG_NULL && tmpVariables[3] == KMCipher.PAD_NOPAD){ + // If data length is greater then key length + if(len > 256){ + KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + }else if(len == 256){ // if data length is same as key length + // Compare the data with key value - date should be less then key value. + // TODO the assumption is that private key exponent value is considered here. + tmpVariables[0]= op.getKey(scratchPad,(short)0); + tmpVariables[0] = Util.arrayCompare( + KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), + KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), + scratchPad, (short)0, tmpVariables[0]); + if(tmpVariables[0] >= 0){ + KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + } + } + Util.arrayCopyNonAtomic( + KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), + KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), + scratchPad, (short)(256 - len),len); + len = (short)256; + } else if (tmpVariables[2] == MessageDigest.ALG_NULL + && tmpVariables[3] == KMCipher.PAD_PKCS1) { + // If PKCS1 padding and no digest - then 0x01||0x00||PS||0x00 on left such that PS = 8 bytes + if(len > 245){ // 256 -11 bytes + KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + } + scratchPad[0] = 0x00; + scratchPad[1] = 0x01; + cryptoProvider.newRandomNumber(scratchPad, (short)2, (short)8); + scratchPad[10] = 0x00; + Util.arrayCopyNonAtomic( + KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), + KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), + scratchPad, (short)11,len); + len += (short)11; + }else if (tmpVariables[2] != MessageDigest.ALG_NULL && tmpVariables[3] == KMCipher.PAD_PKCS1){ + //If PKCS1 padding and digest != ALG_NULL - just copy the data on the scratch pad + Util.arrayCopyNonAtomic( + KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), + KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), + scratchPad, (short)0,len); + } + }else if(tmpVariables[1] == Signature.SIG_CIPHER_ECDSA){ // For ECDSA algorithm + //If no digest then truncate the data to 32 byte if required + if(tmpVariables[2] == MessageDigest.ALG_NULL){ + if(len > 32){ + Util.arrayCopyNonAtomic( + KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), + KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), + scratchPad, (short)0,(short)32); + len = 32; + } + }else{ + //If digest is present then copy the data to scratchpad + Util.arrayCopyNonAtomic( + KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), + KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), + scratchPad, (short)0,len); + } + }else if(tmpVariables[1] == Signature.SIG_CIPHER_HMAC){ // For HMAC algorithm + // Just copy the data as digest is always present. + Util.arrayCopyNonAtomic( + KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), + KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), + scratchPad, (short)0,len); + }else{ // This is should never happen + KMException.throwIt(KMError.OPERATION_CANCELLED); + } + // Sign the data and also complete the trusted verification. + tmpVariables[0]= op.getSigner() + .sign( + KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), + KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), + KMByteBlob.cast(data[INPUT_DATA]).length(),scratchPad, (short)0); + data[OUTPUT_DATA] = KMByteBlob.instance(scratchPad, (short)0, tmpVariables[0]); + } else{ //If decrypt or encrypt operation + tmpVariables[1] = op.getCipher().getCipherAlgorithm(); + tmpVariables[2] = op.getCipher().getPaddingAlgorithm(); + len = KMByteBlob.cast(data[INPUT_DATA]).length(); + if(tmpVariables[1] == KMCipher.CIPHER_RSA){ // For RSA algorithm + // If no padding and no digest - then zero padding up to 256 on left + if (tmpVariables[2] == KMCipher.PAD_NOPAD) { + if(len > 256){ + KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + } + if(len < 256){ + Util.arrayCopyNonAtomic( + KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), + KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), + scratchPad, (short)(256 - len),len); + len = (short)256; + } + } else { + // If OAEP padding with digest - just copy the data to scratchpad and continue. + Util.arrayCopyNonAtomic( + KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), + KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), + scratchPad, (short)0,len); + } + }else if(tmpVariables[1] == KMCipher.CIPHER_DES_CBC || tmpVariables[1] == KMCipher.CIPHER_DES_ECB + || tmpVariables[1] == KMCipher.CIPHER_AES_CBC || + tmpVariables[1] == KMCipher.CIPHER_AES_ECB){ + if(tmpVariables[1] == KMCipher.CIPHER_AES_CBC || + tmpVariables[1] == KMCipher.CIPHER_AES_ECB){ // For AES algorithm + tmpVariables[5] = AES_BLOCK_SIZE; + }else{ + tmpVariables[5] = DES_BLOCK_SIZE; + } + //If no padding then data length must be block aligned + if (tmpVariables[2] == KMCipher.PAD_NOPAD && ((short)(len % tmpVariables[5]) != 0)){ + KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + } + //If padding i.e. pkcs7 then add padding to right + if(tmpVariables[2] != KMCipher.PAD_NOPAD){ + tmpVariables[3] = (short)(len % tmpVariables[5]); + if(tmpVariables[3] != 0){ + // If not block aligned then pkcs7 padding on right + tmpVariables[4] = (short)((len / tmpVariables[5])+tmpVariables[5]); + Util.arrayFillNonAtomic(scratchPad, (short)0, tmpVariables[4], (byte)tmpVariables[3]); + }else{ + // If block aligned then one complete block of pkcs7 padding of block length value + // on the right. + tmpVariables[4] = (short)(len + tmpVariables[5]); + Util.arrayFillNonAtomic(scratchPad, (short)0, tmpVariables[4], (byte)tmpVariables[5]); + } + Util.arrayCopyNonAtomic( KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), + KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), + scratchPad, (short)0,len); + len = tmpVariables[4]; + } + // AES / DES Cipher + tmpVariables[0]= op.getCipher() + .doFinal(scratchPad, (short)0,len, scratchPad, (short)len); + data[OUTPUT_DATA] = KMByteBlob.instance(scratchPad, (short)len, tmpVariables[0]); + } else{ // This should never happen + KMException.throwIt(KMError.OPERATION_CANCELLED); + } + } + // Remove the operation handle + repository.releaseOperation(op); + // Make response + // make response + tmpVariables[1] = KMArray.instance((short) 0); + tmpVariables[1] = KMKeyParameters.instance(tmpVariables[1]); + tmpVariables[2] = KMArray.instance((short) 4); + if (data[OUTPUT_DATA] == KMType.INVALID_VALUE) { + data[OUTPUT_DATA] = KMByteBlob.instance((short) 0); + } + KMArray.cast(tmpVariables[2]).add((short) 0, KMInteger.uint_16(KMError.OK)); + KMArray.cast(tmpVariables[2]).add((short) 1, tmpVariables[1]); + KMArray.cast(tmpVariables[2]).add((short) 2, data[OUTPUT_DATA]); + // Encode the response + bufferLength = encoder.encode(tmpVariables[0], buffer, bufferStartOffset); + sendOutgoing(apdu); + } + + private void authorizeUpdateFinalOperation(KMOperationState op, byte[] scratchPad) { + // User Authentication + if (!op.isAuthPerOperation()) { + if (!op.isAuthTimeoutValidated()) { + validateVerificationToken(op, data[VERIFICATION_TOKEN], scratchPad); + tmpVariables[0] = KMInteger.uint_64(op.getAuthTime(), (short) 0); + tmpVariables[2] = KMVerificationToken.cast(data[VERIFICATION_TOKEN]).getTimestamp(); + if (tmpVariables[3] == KMType.INVALID_VALUE) { + KMException.throwIt(KMError.VERIFICATION_FAILED); + } + if (KMInteger.compare(tmpVariables[0], tmpVariables[3]) >= 0) { + KMException.throwIt(KMError.KEY_USER_NOT_AUTHENTICATED); + } + op.setAuthTimeoutValidated(true); + } + } else { // Auth per operation + authorizeUserIdPerKeyOperation(data[HW_TOKEN], scratchPad); + } + } + + private void validateVerificationToken(KMOperationState op, short verToken, byte[] scratchPad) { + // CBOR Encoding is always big endian and Java is big endian + short ptr = KMVerificationToken.cast(verToken).getMac(); + short len = 0; + // If mac length is zero then token is empty. + if (KMByteBlob.cast(ptr).length() == 0) { + return; + } + // validate operation handle. + ptr = KMVerificationToken.cast(verToken).getChallenge(); + if(op.getHandle() != KMInteger.cast(ptr).getShort()){ + KMException.throwIt(KMError.VERIFICATION_FAILED); + } + // concatenation length will be 37 + length of verified parameters list. + short params = KMVerificationToken.cast(verToken).getParametersVerified(); + Util.arrayFillNonAtomic(scratchPad, (short) 0, + (short) (37+KMByteBlob.cast(params).length()), (byte) 0); + // Add "Auth Verification" - 17 bytes. + Util.arrayCopy(authVerification,(short)0, scratchPad, (short)0, (short)authVerification.length); + len = (short)authVerification.length; + // concatenate challenge - 8 bytes + ptr = KMVerificationToken.cast(verToken).getChallenge(); + KMInteger.cast(ptr) + .value(scratchPad, (short) (len + (short) (8 - KMInteger.cast(ptr).length()))); + len += 8; + // concatenate timestamp -8 bytes + ptr = KMVerificationToken.cast(verToken).getTimestamp(); + KMInteger.cast(ptr) + .value(scratchPad, (short) (len + (short) (8 - KMInteger.cast(ptr).length()))); + len += 8; + // concatenate security level - 4 bytes + ptr = KMVerificationToken.cast(verToken).getSecurityLevel(); + scratchPad[(short) (len + 3)] = KMEnum.cast(ptr).getVal(); + len += 4; + // concatenate Parameters verified - blob of encoded data. + ptr = KMVerificationToken.cast(verToken).getParametersVerified(); + len += KMByteBlob.cast(ptr).getValues(scratchPad, (short)0); + len += 4; + // hmac the data + HMACKey key = + cryptoProvider.createHMACKey( + repository.getComputedHmacKey(), + (short) 0, + (short) repository.getComputedHmacKey().length); + ptr = KMVerificationToken.cast(verToken).getMac(); + boolean verified = + cryptoProvider.hmacVerify(key, scratchPad, (short) 0, len, + KMByteBlob.cast(ptr).getBuffer(), + KMByteBlob.cast(ptr).getStartOff(), + KMByteBlob.cast(ptr).length()); + if(!verified){ + KMException.throwIt(KMError.VERIFICATION_FAILED); + } + /* + + // Compare mac. + ptr = KMVerificationToken.cast(verToken).getMac(); + if (macLen != KMByteBlob.cast(ptr).length()) { + KMException.throwIt(KMError.INVALID_MAC_LENGTH); + } + if (Util.arrayCompare( + scratchPad, (short) (len+1), + KMByteBlob.cast(ptr).getBuffer(), KMByteBlob.cast(ptr).getStartOff(), macLen) != 0) { + KMException.throwIt(KMError.VERIFICATION_FAILED); + } + */ + + } + + private void processUpdateOperationCmd(APDU apdu) { + // TODO Add Support for AES-GCM + receiveIncoming(apdu); + byte[] scratchPad = apdu.getBuffer(); + tmpVariables[1] = KMArray.instance((short) 5); + // Arguments + tmpVariables[2] = KMKeyParameters.exp(); + KMArray.cast(tmpVariables[1]).add((short) 0, KMInteger.exp()); + KMArray.cast(tmpVariables[1]).add((short) 1, tmpVariables[2]); + KMArray.cast(tmpVariables[1]).add((short) 2, KMByteBlob.exp()); + tmpVariables[3] = KMHardwareAuthToken.exp(); + KMArray.cast(tmpVariables[1]).add((short) 3, tmpVariables[3]); + tmpVariables[4] = KMVerificationToken.exp(); + KMArray.cast(tmpVariables[1]).add((short) 4, tmpVariables[4]); + // Decode the arguments + tmpVariables[2] = decoder.decode(tmpVariables[1], buffer, bufferStartOffset, bufferLength); + data[OP_HANDLE] = KMArray.cast(tmpVariables[2]).get((short) 0); + data[KEY_PARAMETERS] = KMArray.cast(tmpVariables[2]).get((short) 1); + data[INPUT_DATA] = KMArray.cast(tmpVariables[2]).get((short) 2); + data[HW_TOKEN] = KMArray.cast(tmpVariables[2]).get((short) 3); + data[VERIFICATION_TOKEN] = KMArray.cast(tmpVariables[2]).get((short) 4); + // Check Operation Handle and get op state + tmpVariables[1] = KMInteger.cast(data[OP_HANDLE]).getShort(); + KMOperationState op = repository.findOperation(tmpVariables[1]); + if (KMInteger.compare(data[OP_HANDLE], KMInteger.uint_16(op.getHandle())) != 0) { + KMException.throwIt(KMError.INVALID_OPERATION_HANDLE); + } + // authorize the update operation + authorizeUpdateFinalOperation(op, scratchPad); + // If signing without digest then do length validation checks + tmpVariables[0] = KMByteBlob.cast(data[INPUT_DATA]).length(); + if (op.getPurpose() == KMType.SIGN) { + // If signing without digest then update should not be called by HAL only final must be + // called + if (op.getSigner().getMessageDigestAlgorithm() == MessageDigest.ALG_NULL) { + KMException.throwIt(KMError.OPERATION_CANCELLED); + } + op.getSigner() + .update( + KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), + KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), + KMByteBlob.cast(data[INPUT_DATA]).length()); + + if (op.isTrustedConfirmationRequired()) { + op.getTrustedConfirmationSigner() + .update( + KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), + KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), + KMByteBlob.cast(data[INPUT_DATA]).length()); + } + data[OUTPUT_DATA] = KMType.INVALID_VALUE; + } else { + // purpose is Encrypt or Decrypt - input data must be block aligned. + tmpVariables[1] = op.getCipher().getCipherAlgorithm(); + // TODO Update for decrypt for RSA may not be necessary - confirm this + if (tmpVariables[1] == KMCipher.CIPHER_RSA) { + KMException.throwIt(KMError.OPERATION_CANCELLED); + } + if (tmpVariables[1] == KMCipher.CIPHER_AES_CBC + || op.getCipher().getCipherAlgorithm() == KMCipher.CIPHER_AES_ECB) { + // 128 bit block size - HAL must send block aligned data + if (tmpVariables[0] % 16 != 0) { + KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + } + } else if (op.getCipher().getCipherAlgorithm() == KMCipher.CIPHER_DES_CBC + || op.getCipher().getCipherAlgorithm() == KMCipher.CIPHER_DES_ECB) { + // 64 bit block size - HAL must send block aligned data + if (tmpVariables[0] % 8 != 0) { + KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + } + } + tmpVariables[1] = + op.getCipher() + .update( + KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), + KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), + KMByteBlob.cast(data[INPUT_DATA]).length(), + scratchPad, + (short) 0); + data[OUTPUT_DATA] = KMByteBlob.instance(scratchPad, (short) 0, tmpVariables[1]); + } + // make response + tmpVariables[1] = KMArray.instance((short) 0); + tmpVariables[1] = KMKeyParameters.instance(tmpVariables[1]); + tmpVariables[2] = KMArray.instance((short) 4); + if (data[OUTPUT_DATA] == KMType.INVALID_VALUE) { + data[OUTPUT_DATA] = KMByteBlob.instance((short) 0); + } + KMArray.cast(tmpVariables[2]).add((short) 0, KMInteger.uint_16(KMError.OK)); + KMArray.cast(tmpVariables[2]).add((short) 1, KMInteger.uint_16(tmpVariables[0])); + KMArray.cast(tmpVariables[2]).add((short) 2, tmpVariables[1]); + KMArray.cast(tmpVariables[2]).add((short) 3, data[OUTPUT_DATA]); + // Encode the response + bufferLength = encoder.encode(tmpVariables[0], buffer, bufferStartOffset); + sendOutgoing(apdu); + } + + private void processBeginOperationCmd(APDU apdu) { + // Receive the incoming request fully from the master into buffer. + receiveIncoming(apdu); + byte[] scratchPad = apdu.getBuffer(); + tmpVariables[1] = KMArray.instance((short) 4); + // Arguments + tmpVariables[2] = KMKeyParameters.exp(); + KMArray.cast(tmpVariables[1]).add((short) 0, KMEnum.instance(KMType.PURPOSE)); + KMArray.cast(tmpVariables[1]).add((short) 1, KMByteBlob.exp()); + KMArray.cast(tmpVariables[1]).add((short) 2, tmpVariables[2]); + tmpVariables[3] = KMHardwareAuthToken.exp(); + KMArray.cast(tmpVariables[1]).add((short) 3, tmpVariables[3]); + // Decode the arguments + tmpVariables[2] = decoder.decode(tmpVariables[1], buffer, bufferStartOffset, bufferLength); + data[KEY_PARAMETERS] = KMArray.cast(tmpVariables[2]).get((short) 2); + data[KEY_BLOB] = KMArray.cast(tmpVariables[2]).get((short) 1); + tmpVariables[0] = KMArray.cast(tmpVariables[2]).get((short) 0); + tmpVariables[0] = KMEnum.cast(tmpVariables[0]).getVal(); + tmpVariables[4] = KMArray.cast(tmpVariables[2]).get((short) 3); + // Check for app id and app data. + data[APP_ID] = KMType.INVALID_VALUE; + data[APP_DATA] = KMType.INVALID_VALUE; + tmpVariables[3] = + KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.APPLICATION_ID, data[KEY_PARAMETERS]); + if (tmpVariables[3] != KMTag.INVALID_VALUE) { + data[APP_ID] = KMByteTag.cast(tmpVariables[3]).getValue(); + } + tmpVariables[3] = + KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.APPLICATION_DATA, data[KEY_PARAMETERS]); + if (tmpVariables[3] != KMTag.INVALID_VALUE) { + data[APP_DATA] = KMByteTag.cast(tmpVariables[3]).getValue(); + } + // Parse the encrypted blob and decrypt it. + parseEncryptedKeyBlob(scratchPad); + // Authorize the begin operation and reserve op - data[OP_HANDLE] will have the handle. + // It will also set data[IV] field if required. + authorizeBeginOperation(tmpVariables[4], scratchPad); + // Check for trusted confirmation - if required then set the signer in op state. + tmpVariables[0] = + KMKeyParameters.findTag( + KMType.BOOL_TAG, KMType.TRUSTED_CONFIRMATION_REQUIRED, data[HW_PARAMETERS]); + if (tmpVariables[0] != KMType.INVALID_VALUE) { + // get operation + KMOperationState op = repository.findOperation(data[OP_HANDLE]); + // get the hmac key + if (repository.getComputedHmacKey() == null) { + KMException.throwIt(KMError.OPERATION_CANCELLED); + } + // set the Hmac signer + op.setTrustedConfirmationSigner( + cryptoProvider.createHmacSigner( + MessageDigest.ALG_SHA_256, + repository.getComputedHmacKey(), + (short) 0, + (short) repository.getComputedHmacKey().length)); + } + // If the data[IV] is required to be returned. + if (data[IV] != KMType.INVALID_VALUE) { + // TODO confirm why this is needed + tmpVariables[2] = KMArray.instance((short) 1); + KMArray.cast(tmpVariables[2]).add((short) 0, data[IV]); + } else { + tmpVariables[2] = KMArray.instance((short) 0); + } + tmpVariables[1] = KMKeyParameters.instance(tmpVariables[2]); + 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, tmpVariables[1]); + KMArray.cast(tmpVariables[0]).add((short) 2, data[OP_HANDLE]); + // Encode the response + bufferLength = encoder.encode(tmpVariables[0], buffer, bufferStartOffset); + sendOutgoing(apdu); + } + + private void authorizeBeginOperation(short hwToken, byte[] scratchPad) { + // Read purpose from key parameters - cannot be null. + short purpose = + KMEnumArrayTag.getValues(KMType.PURPOSE, data[KEY_PARAMETERS], scratchPad, (short) 0); + if (purpose == KMType.INVALID_VALUE) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + if (purpose != 1) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + purpose = scratchPad[0]; + if (!(KMEnumArrayTag.contains(KMType.PURPOSE, purpose, data[HW_PARAMETERS]))) { + KMException.throwIt(KMError.UNSUPPORTED_PURPOSE); + } + // Read digest from key parameters - can be null for EC. + short digest = + KMEnumArrayTag.getValues(KMType.DIGEST, data[KEY_PARAMETERS], scratchPad, (short) 0); + if (digest != KMType.INVALID_VALUE && digest != 1) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + digest = scratchPad[0]; + // Read padding from key parameters - can be null for AES/DES. + short padding = + KMEnumArrayTag.getValues(KMType.PADDING, data[KEY_PARAMETERS], scratchPad, (short) 0); + if (padding != KMType.INVALID_VALUE && padding != 1) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + padding = scratchPad[0]; + // Read Blockmode + short blockmode = + KMEnumArrayTag.getValues(KMType.BLOCK_MODE, data[KEY_PARAMETERS], scratchPad, (short) 0); + if (blockmode != KMType.INVALID_VALUE && blockmode != 1) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + blockmode = scratchPad[0]; + + // Max uses per boot + tmpVariables[0] = + KMKeyParameters.findTag(KMType.UINT_TAG, KMType.MAX_USES_PER_BOOT, data[HW_PARAMETERS]); + if (tmpVariables[0] != KMType.INVALID_VALUE) { + // get prescribed limit + tmpVariables[0] = KMIntegerTag.cast(tmpVariables[0]).getValue(); + authorizeKeyUsageForCount(tmpVariables[0]); + } + // Authorize UserId - auth timeout check cannot be done in javacard + tmpVariables[0] = + KMKeyParameters.findTag(KMType.ULONG_ARRAY_TAG, KMType.USER_SECURE_ID, data[HW_PARAMETERS]); + if (tmpVariables[0] != KMType.INVALID_VALUE) { + tmpVariables[0] = + KMKeyParameters.findTag(KMType.UINT_TAG, KMType.AUTH_TIMEOUT, data[HW_PARAMETERS]); + if (tmpVariables[0] != KMType.INVALID_VALUE) { + // check if hw token is empty - mac should not be empty. + tmpVariables[1] = KMHardwareAuthToken.cast(hwToken).getMac(); + if (KMByteBlob.cast(tmpVariables[1]).length() == 0) { + KMException.throwIt(KMError.INVALID_MAC_LENGTH); + } + authorizeUserId(hwToken, scratchPad); + } + } + // Authorize Caller Nonce - if caller nonce absent in key char and nonce present in + // key params then fail. + tmpVariables[2] = KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.NONCE, data[KEY_PARAMETERS]); + tmpVariables[0] = + KMKeyParameters.findTag(KMType.BOOL_TAG, KMType.CALLER_NONCE, data[HW_PARAMETERS]); + if (tmpVariables[0] == KMType.INVALID_VALUE) { + if (tmpVariables[2] != KMType.INVALID_VALUE) { + KMException.throwIt(KMError.CALLER_NONCE_PROHIBITED); + } + } + // Authorize Bootloader Only - assumption is that if this is is present then always fail. + tmpVariables[0] = + KMKeyParameters.findTag(KMType.BOOL_TAG, KMType.BOOTLOADER_ONLY, data[HW_PARAMETERS]); + if (tmpVariables[1] != KMType.INVALID_VALUE) { + KMException.throwIt(KMError.INVALID_KEY_BLOB); + } + tmpVariables[0] = KMEnumTag.getValue(KMType.ALGORITHM, data[HW_PARAMETERS]); + switch (tmpVariables[0]) { + case KMType.RSA: + authorizeRsa(purpose, digest, padding); + break; + case KMType.EC: + authorizeEC(purpose, digest); + break; + case KMType.DES: + case KMType.AES: + if (tmpVariables[2] == KMType.INVALID_VALUE) { + tmpVariables[2] = KMByteBlob.instance((short) 16); + cryptoProvider.newRandomNumber( + KMByteBlob.cast(tmpVariables[2]).getBuffer(), + KMByteBlob.cast(tmpVariables[2]).getStartOff(), + KMByteBlob.cast(tmpVariables[2]).length()); + } + data[IV] = tmpVariables[2]; + authorizeAesDes(tmpVariables[0], purpose, blockmode, padding); + break; + case KMType.HMAC: + authorizeHmac(purpose, digest); + break; + default: + KMException.throwIt(KMError.UNIMPLEMENTED); + break; + } + } + + private void authorizeGCM(short purpose, short padding) { + data[OP_HANDLE] = KMType.INVALID_VALUE; + if (purpose == KMType.SIGN || purpose == KMType.VERIFY) { + KMException.throwIt(KMError.UNSUPPORTED_PURPOSE); + } + if (purpose == KMType.ENCRYPT) { + purpose = KMCipher.MODE_ENCRYPT; + } else { + purpose = KMCipher.MODE_DECRYPT; + } + if (padding != KMType.PADDING_NONE) { + KMException.throwIt(KMError.INCOMPATIBLE_PADDING_MODE); + } + // Read and authorizeBeginOperation mac length + tmpVariables[0] = + KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.MAC_LENGTH, data[KEY_PARAMETERS]); + if (tmpVariables[0] == KMType.INVALID_VALUE) { + KMException.throwIt(KMError.MISSING_MAC_LENGTH); + } + if (tmpVariables[0] % 8 != 0) { + KMException.throwIt(KMError.INVALID_MAC_LENGTH); + } + tmpVariables[1] = + KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, data[HW_PARAMETERS]); + if (tmpVariables[0] < tmpVariables[1]) { + KMException.throwIt(KMError.INVALID_MAC_LENGTH); + } + if (tmpVariables[0] > 128) { + KMException.throwIt(KMError.INVALID_MAC_LENGTH); + } + KMOperationState op = repository.reserveOperation(); + if (op == null) { + KMException.throwIt(KMError.TOO_MANY_OPERATIONS); + } + op.setPurpose(purpose); + op.setKey(KMByteBlob.cast(data[SECRET]).getBuffer(), + KMByteBlob.cast(data[SECRET]).getStartOff(), + KMByteBlob.cast(data[SECRET]).length()); + op.setCipher( + cryptoProvider.createGCMCipher( + purpose, + KMByteBlob.cast(data[SECRET]).getBuffer(), + KMByteBlob.cast(data[SECRET]).getStartOff(), + KMByteBlob.cast(data[SECRET]).length(), + KMByteBlob.cast(data[IV]).getBuffer(), + KMByteBlob.cast(data[IV]).getStartOff(), + KMByteBlob.cast(data[IV]).length())); + data[OP_HANDLE] = op.getHandle(); + } + + private void authorizeHmac(short purpose, short digest) { + data[OP_HANDLE] = KMType.INVALID_VALUE; + if (purpose == KMType.ENCRYPT || purpose == KMType.DECRYPT) { + KMException.throwIt(KMError.UNSUPPORTED_PURPOSE); + } + if (digest == KMType.INVALID_VALUE) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + if (!(KMEnumArrayTag.contains(KMType.DIGEST, digest, data[HW_PARAMETERS]))) { + KMException.throwIt(KMError.UNSUPPORTED_DIGEST); + } + // Read and authorizeBeginOperation mac length + tmpVariables[0] = + KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.MAC_LENGTH, data[KEY_PARAMETERS]); + if (tmpVariables[0] == KMType.INVALID_VALUE) { + KMException.throwIt(KMError.MISSING_MAC_LENGTH); + } + if (tmpVariables[0] % 8 != 0) { + KMException.throwIt(KMError.INVALID_MAC_LENGTH); + } + tmpVariables[1] = + KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, data[HW_PARAMETERS]); + if (tmpVariables[0] < tmpVariables[1]) { + KMException.throwIt(KMError.INVALID_MAC_LENGTH); + } + + switch (digest) { + case KMType.MD5: + tmpVariables[2] = MessageDigest.ALG_MD5; + tmpVariables[1] = 128; + break; + case KMType.SHA1: + tmpVariables[2] = MessageDigest.ALG_SHA; + tmpVariables[1] = 160; + break; + case KMType.SHA2_224: + tmpVariables[2] = MessageDigest.ALG_SHA_224; + tmpVariables[1] = 224; + break; + case KMType.SHA2_256: + tmpVariables[2] = MessageDigest.ALG_SHA_256; + tmpVariables[1] = 256; + break; + case KMType.SHA2_384: + tmpVariables[2] = MessageDigest.ALG_SHA_384; + tmpVariables[1] = 384; + break; + case KMType.SHA2_512: + tmpVariables[2] = MessageDigest.ALG_SHA_512; + tmpVariables[1] = 512; + break; + default: + tmpVariables[1] = 512; + break; + } + if (tmpVariables[0] > tmpVariables[1]) { + KMException.throwIt(KMError.INVALID_MAC_LENGTH); + } + KMOperationState op = repository.reserveOperation(); + if (op == null) { + KMException.throwIt(KMError.TOO_MANY_OPERATIONS); + } + op.setPurpose(purpose); + op.setKey(KMByteBlob.cast(data[SECRET]).getBuffer(), + KMByteBlob.cast(data[SECRET]).getStartOff(), + KMByteBlob.cast(data[SECRET]).length()); + op.setSigner( + cryptoProvider.createHmacSigner( + tmpVariables[0], + KMByteBlob.cast(data[SECRET]).getBuffer(), + KMByteBlob.cast(data[SECRET]).getStartOff(), + KMByteBlob.cast(data[SECRET]).length())); + data[OP_HANDLE] = op.getHandle(); + } + + private void authorizeAesDes(short alg, short purpose, short blockmode, short padding) { + data[OP_HANDLE] = KMType.INVALID_VALUE; + if (purpose == KMType.SIGN || purpose == KMType.VERIFY) { + KMException.throwIt(KMError.UNSUPPORTED_PURPOSE); + } + if (blockmode == KMType.GCM) { + authorizeGCM(purpose, padding); + } + if (purpose == KMType.ENCRYPT) { + purpose = KMCipher.MODE_ENCRYPT; + } else { + purpose = KMCipher.MODE_DECRYPT; + } + KMOperationState op = null; + // padding must be no pad - PKCS7 is not supported in javacard + // TODO implement PCKS7 in cryptoProvider + if (padding == KMType.PADDING_NONE) { + padding = KMCipher.PAD_NULL; + } else if (padding == KMType.PKCS7) { + padding = KMCipher.PAD_PKCS7; + } else { + KMException.throwIt(KMError.INCOMPATIBLE_PADDING_MODE); + } + if (alg == KMType.AES) { + if (blockmode == KMType.CBC) { + tmpVariables[0] = KMCipher.CIPHER_AES_CBC; + } else if (blockmode == KMType.ECB) { + tmpVariables[0] = KMCipher.CIPHER_AES_ECB; + } else { + // data[CIPHER_ALGORITHM] = Cipher.CIPHER_AES_CTR; // Not supported in 3.0.5 + // TODO change this once we can test. + KMException.throwIt(KMError.UNSUPPORTED_BLOCK_MODE); + } + op = repository.reserveOperation(); + } else if (alg == KMType.DES) { + if (blockmode == KMType.CBC) { + tmpVariables[0] = KMCipher.CIPHER_DES_CBC; + } else if (blockmode == KMType.ECB) { + tmpVariables[0] = KMCipher.CIPHER_DES_ECB; + } else { + // data[CIPHER_ALGORITHM] = Cipher.CIPHER_DES_CTR; // Not supported in 3.0.5 + // TODO change this once we can test. + KMException.throwIt(KMError.UNSUPPORTED_BLOCK_MODE); + } + op = repository.reserveOperation(); + } else { + KMException.throwIt(KMError.INCOMPATIBLE_ALGORITHM); + } + if (op == null) { + KMException.throwIt(KMError.TOO_MANY_OPERATIONS); + } + op.setPurpose(purpose); + op.setKey(KMByteBlob.cast(data[SECRET]).getBuffer(), + KMByteBlob.cast(data[SECRET]).getStartOff(), + KMByteBlob.cast(data[SECRET]).length()); + op.setCipher( + cryptoProvider.createSymmetricCipher( + tmpVariables[0], + padding, + purpose, + KMByteBlob.cast(data[SECRET]).getBuffer(), + KMByteBlob.cast(data[SECRET]).getStartOff(), + KMByteBlob.cast(data[SECRET]).length(), + KMByteBlob.cast(data[IV]).getBuffer(), + KMByteBlob.cast(data[IV]).getStartOff(), + KMByteBlob.cast(data[IV]).length())); + data[OP_HANDLE] = op.getHandle(); } - private void processAbortOperationCmd(APDU apdu) {} - - private void processFinishOperationCmd(APDU apdu) {} - - private void processUpdateOperationCmd(APDU apdu) {} + private void authorizeEC(short purpose, short digest) { + data[OP_HANDLE] = KMType.INVALID_VALUE; + // purpose will be always sign. + // Only ECDSA signing supported + if (purpose == KMType.ENCRYPT || purpose == KMType.VERIFY || purpose == KMType.DECRYPT) { + KMException.throwIt(KMError.UNSUPPORTED_PURPOSE); + } + switch (digest) { + case KMType.DIGEST_NONE: + tmpVariables[0] = MessageDigest.ALG_NULL; + break; + case KMType.SHA1: + tmpVariables[0] = MessageDigest.ALG_SHA; + break; + case KMType.SHA2_224: + tmpVariables[0] = MessageDigest.ALG_SHA_224; + break; + case KMType.SHA2_256: + tmpVariables[0] = MessageDigest.ALG_SHA_256; + break; + case KMType.SHA2_384: + tmpVariables[0] = MessageDigest.ALG_SHA_384; + break; + case KMType.SHA2_512: + tmpVariables[0] = MessageDigest.ALG_SHA_512; + break; + default: + KMException.throwIt(KMError.INCOMPATIBLE_DIGEST); + break; + } + KMOperationState op = repository.reserveOperation(); + if (op == null) { + KMException.throwIt(KMError.TOO_MANY_OPERATIONS); + } + op.setPurpose(purpose); + op.setKey(KMByteBlob.cast(data[SECRET]).getBuffer(), + KMByteBlob.cast(data[SECRET]).getStartOff(), + KMByteBlob.cast(data[SECRET]).length()); + op.setSigner( + cryptoProvider.createEcSigner( + tmpVariables[0], + KMByteBlob.cast(data[SECRET]).getBuffer(), + KMByteBlob.cast(data[SECRET]).getStartOff(), + KMByteBlob.cast(data[SECRET]).length())); + data[OP_HANDLE] = op.getHandle(); + } - private void processBeginOperationCmd(APDU apdu) {} + private void authorizeRsa(short purpose, short digest, short padding) { + KMOperationState op = null; + data[OP_HANDLE] = KMType.INVALID_VALUE; + if (purpose == KMType.ENCRYPT || purpose == KMType.VERIFY) { + KMException.throwIt(KMError.UNSUPPORTED_PURPOSE); + } + switch (purpose) { + case KMType.DECRYPT: + tmpVariables[0] = KMCipher.CIPHER_RSA; + if (padding == KMType.PADDING_NONE) { + // There is no way to select digest with no padding. Digest is also none. + padding = KMCipher.PAD_NOPAD; + } else if (padding != KMType.RSA_OAEP) { + KMException.throwIt(KMError.INCOMPATIBLE_PADDING_MODE); + } else { + // There is no way to ascertain MGF1 and SHA1 in javacard - this should be part of PKCS1. + switch (digest) { + case KMType.DIGEST_NONE: + KMException.throwIt(KMError.UNSUPPORTED_DIGEST); + break; + case KMType.SHA2_224: + padding = KMCipher.PAD_PKCS1_OAEP_SHA224; + break; + case KMType.SHA2_256: + padding = KMCipher.PAD_PKCS1_OAEP_SHA256; + break; + case KMType.SHA2_384: + padding = KMCipher.PAD_PKCS1_OAEP_SHA384; + break; + case KMType.SHA2_512: + padding = KMCipher.PAD_PKCS1_OAEP_SHA512; + break; + default: + KMException.throwIt(KMError.INCOMPATIBLE_DIGEST); + break; + } + } + op = repository.reserveOperation(); + if (op == null) { + KMException.throwIt(KMError.TOO_MANY_OPERATIONS); + } + op.setPurpose(purpose); + op.setKey(KMByteBlob.cast(data[SECRET]).getBuffer(), + KMByteBlob.cast(data[SECRET]).getStartOff(), + KMByteBlob.cast(data[SECRET]).length()); + op.setCipher( + cryptoProvider.createRsaDecrypt( + tmpVariables[0], + padding, + KMByteBlob.cast(data[SECRET]).getBuffer(), + KMByteBlob.cast(data[SECRET]).getStartOff(), + KMByteBlob.cast(data[SECRET]).length(), + KMByteBlob.cast(data[PUB_KEY]).getBuffer(), + KMByteBlob.cast(data[PUB_KEY]).getStartOff(), + KMByteBlob.cast(data[PUB_KEY]).length())); + break; + case KMType.SIGN: + if (padding == KMType.PADDING_NONE) { + if(digest == KMType.DIGEST_NONE){ + tmpVariables[0] = MessageDigest.ALG_NULL; + padding = KMCipher.PAD_NOPAD; + }else{ + KMException.throwIt(KMError.INCOMPATIBLE_DIGEST); + } + } else if (padding != KMType.RSA_PKCS1_1_5_SIGN) { + KMException.throwIt(KMError.INCOMPATIBLE_PADDING_MODE); + } else { + padding = KMCipher.PAD_PKCS1; + switch (digest) { // TODO No digest not supported at this moment + case KMType.DIGEST_NONE: + tmpVariables[0] = MessageDigest.ALG_NULL; + break; + case KMType.SHA2_224: + tmpVariables[0] = MessageDigest.ALG_SHA_224; + break; + case KMType.SHA2_256: + tmpVariables[0] = MessageDigest.ALG_SHA_256; + break; + case KMType.SHA2_384: + tmpVariables[0] = MessageDigest.ALG_SHA_384; + break; + case KMType.SHA2_512: + tmpVariables[0] = MessageDigest.ALG_SHA_512; + break; + default: + KMException.throwIt(KMError.INCOMPATIBLE_DIGEST); + break; + } + } + op = repository.reserveOperation(); + if (op == null) { + KMException.throwIt(KMError.TOO_MANY_OPERATIONS); + } + op.setPurpose(purpose); + op.setKey(KMByteBlob.cast(data[SECRET]).getBuffer(), + KMByteBlob.cast(data[SECRET]).getStartOff(), + KMByteBlob.cast(data[SECRET]).length()); + op.setSigner( + cryptoProvider.createRsaSigner( + tmpVariables[0], + padding, + KMByteBlob.cast(data[SECRET]).getBuffer(), + KMByteBlob.cast(data[SECRET]).getStartOff(), + KMByteBlob.cast(data[SECRET]).length(), + KMByteBlob.cast(data[PUB_KEY]).getBuffer(), + KMByteBlob.cast(data[PUB_KEY]).getStartOff(), + KMByteBlob.cast(data[PUB_KEY]).length())); + break; + default: + KMException.throwIt(KMError.UNSUPPORTED_PURPOSE); + break; + } + data[OP_HANDLE] = op.getHandle(); + } - 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]).length() == 2){ - if(Util.getShort(repository.getHeap(),KMByteBlob.cast(data[APP_ID]).getStartOff()) == KMType.INVALID_VALUE){ - data[APP_ID] = KMType.INVALID_VALUE; + private void authorizeUserId(short hwToken, byte[] scratchPad) { + validateHwToken(hwToken, scratchPad); + tmpVariables[0] = KMHardwareAuthToken.cast(hwToken).getUserId(); + if (KMInteger.cast(tmpVariables[0]).isZero()) { + tmpVariables[0] = KMHardwareAuthToken.cast(hwToken).getAuthenticatorId(); + if (KMInteger.cast(tmpVariables[0]).isZero()) { + KMException.throwIt(KMError.KEY_USER_NOT_AUTHENTICATED); } } - if(KMByteBlob.cast(data[APP_DATA]).length() == 2){ - if(Util.getShort(repository.getHeap(),KMByteBlob.cast(data[APP_DATA]).getStartOff()) == KMType.INVALID_VALUE){ - data[APP_DATA] = KMType.INVALID_VALUE; - } + // check user secure id + if (!KMIntegerArrayTag.contains(KMType.USER_SECURE_ID, tmpVariables[0], data[HW_PARAMETERS])) { + KMException.throwIt(KMError.KEY_USER_NOT_AUTHENTICATED); + } + // check auth type + tmpVariables[1] = KMEnumTag.getValue(KMType.USER_AUTH_TYPE, data[HW_PARAMETERS]); + tmpVariables[2] = KMHardwareAuthToken.cast(hwToken).getHwAuthenticatorType(); + tmpVariables[2] = KMEnum.cast(tmpVariables[2]).getVal(); + if (((byte) tmpVariables[2] & (byte) tmpVariables[1]) == 0) { + KMException.throwIt(KMError.KEY_USER_NOT_AUTHENTICATED); } - // 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 processVerifyAuthenticationCmd(APDU apdu) {} - - private void processDestroyAttIdsCmd(APDU apdu) {} - - private void processComputeSharedHmacCmd(APDU apdu) {} - - private void processDeleteAllKeysCmd(APDU apdu) {} - - private void processDeleteKeyCmd(APDU apdu) {} - - private void processUpgradeKeyCmd(APDU apdu) {} - - private void processAttestKeyCmd(APDU apdu) {} + private void validateHwToken(short hwToken, byte[] scratchPad) { + // CBOR Encoding is always big endian + short ptr = KMHardwareAuthToken.cast(hwToken).getMac(); + short len = 0; + // If mac length is zero then token is empty. + if (KMByteBlob.cast(ptr).length() == 0) { + return; + } + // add 0 + Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 37, (byte) 0); + len = 1; + // concatenate challenge - 8 bytes + ptr = KMHardwareAuthToken.cast(hwToken).getChallenge(); + KMInteger.cast(ptr) + .value(scratchPad, (short) (len + (short) (8 - KMInteger.cast(ptr).length()))); + len += 8; + // concatenate user id - 8 bytes + ptr = KMHardwareAuthToken.cast(hwToken).getUserId(); + KMInteger.cast(tmpVariables[0]) + .value(scratchPad, (short) (len + (short) (8 - KMInteger.cast(ptr).length()))); + len += 8; + // concatenate authenticator id - 8 bytes + ptr = KMHardwareAuthToken.cast(hwToken).getAuthenticatorId(); + KMInteger.cast(tmpVariables[0]) + .value(scratchPad, (short) (len + (short) (8 - KMInteger.cast(ptr).length()))); + len += 8; + // concatenate authenticator type - 4 bytes + ptr = KMHardwareAuthToken.cast(hwToken).getHwAuthenticatorType(); + scratchPad[(short) (len + 3)] = KMEnum.cast(ptr).getVal(); + len += 4; + // concatenate timestamp -8 bytes + ptr = KMHardwareAuthToken.cast(hwToken).getTimestamp(); + KMInteger.cast(tmpVariables[0]) + .value(scratchPad, (short) (len + (short) (8 - KMInteger.cast(ptr).length()))); + len += 8; + // hmac the data + HMACKey key = + cryptoProvider.createHMACKey( + repository.getComputedHmacKey(), + (short) 0, + (short) repository.getComputedHmacKey().length); + ptr = KMHardwareAuthToken.cast(hwToken).getMac(); + boolean verified = + cryptoProvider.hmacVerify(key, scratchPad, (short) 0, len, + KMByteBlob.cast(ptr).getBuffer(), + KMByteBlob.cast(ptr).getStartOff(), + KMByteBlob.cast(ptr).length()); + if(!verified){ + KMException.throwIt(KMError.VERIFICATION_FAILED); + } +/* + len = + cryptoProvider.hmac(key, scratchPad, (short) 0, len, scratchPad, (short) (len + 1) ); + // Compare mac. + ptr = KMHardwareAuthToken.cast(hwToken).getMac(); + if (len != KMByteBlob.cast(ptr).length()) { + KMException.throwIt(KMError.INVALID_MAC_LENGTH); + } + if (Util.arrayCompare( + scratchPad, + (short) 38, + KMByteBlob.cast(ptr).getBuffer(), + KMByteBlob.cast(ptr).getStartOff(), + len) + != 0) { + KMException.throwIt(KMError.VERIFICATION_FAILED); + } + */ + } - private void processExportKeyCmd(APDU apdu) {} + private void authorizeUserIdPerKeyOperation(short hwToken, byte[] scratchPad) { + tmpVariables[0] = KMHardwareAuthToken.cast(hwToken).getChallenge(); + if (KMInteger.compare(data[OP_HANDLE], tmpVariables[0]) != 0) { + KMException.throwIt(KMError.KEY_USER_NOT_AUTHENTICATED); + } + authorizeUserId(hwToken, scratchPad); + } - private void processImportWrappedKeyCmd(APDU apdu) {} + private void authorizeKeyUsageForCount(short limit) { + // get current counter + // TODO currently only short counter supported - max count 32K. + short val = repository.getRateLimitedKeyCount(data[AUTH_TAG]); + if (val != KMType.INVALID_VALUE) { + short count = KMInteger.uint_16(val); + // compare 32 bit values - is current counter less then prescribed limit + if (KMInteger.compare(count, limit) != -1) { + KMException.throwIt(KMError.KEY_MAX_OPS_EXCEEDED); + } + // increment the counter and store it back. + val++; + repository.setRateLimitedKeyCount(data[AUTH_TAG], val); + } else { + KMException.throwIt(KMError.UNKNOWN_ERROR); + } + } private void processImportKeyCmd(APDU apdu) { if (repository.keyBlobCount > repository.MAX_BLOB_STORAGE) { @@ -485,7 +1950,7 @@ private void processImportKeyCmd(APDU apdu) { // Receive the incoming request fully from the master into buffer. receiveIncoming(apdu); byte[] scratchPad = apdu.getBuffer(); - tmpVariables[1] = KMArray.instance((short)3); + tmpVariables[1] = KMArray.instance((short) 3); // Arguments tmpVariables[2] = KMKeyParameters.exp(); KMArray.cast(tmpVariables[1]).add((short) 0, tmpVariables[2]); @@ -496,11 +1961,16 @@ private void processImportKeyCmd(APDU apdu) { 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); - // Keyformat must be RAW format - X509 and PKCS8 not implemented. + // 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) { @@ -528,7 +1998,6 @@ private void processImportKeyCmd(APDU apdu) { break; } // create key blob - data[ORIGIN] = KMType.IMPORTED; createEncryptedKeyBlob(scratchPad); // persist auth tag for rollback resistance. repository.persistAuthTag(data[AUTH_TAG]); @@ -549,61 +2018,57 @@ private void importECKeys(byte[] scratchPad) { 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()); + 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){ + 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.createEcPrivateKey( - 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() - ); + 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]); + 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{ + } 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; + 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]){ + 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{ + } 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; + 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); + data[KEY_BLOB] = KMArray.instance((short) 5); KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_PUB_KEY, data[PUB_KEY]); } @@ -612,39 +2077,39 @@ private void importHmacKey(byte[] scratchPad) { 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()); + 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() - ); + 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]); + 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)) { + if (!(tmpVariables[2] >= 64 && tmpVariables[2] <= 512 && tmpVariables[2] % 8 == 0)) { KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH); } - }else{ + } 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; + 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); + data[KEY_BLOB] = KMArray.instance((short) 4); } private void importTDESKey(byte[] scratchPad) { @@ -652,38 +2117,38 @@ private void importTDESKey(byte[] scratchPad) { 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()); + 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() - ); + 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]); + 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{ + } 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; + 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); + data[KEY_BLOB] = KMArray.instance((short) 4); } private void importAESKey(byte[] scratchPad) { @@ -691,39 +2156,39 @@ private void importAESKey(byte[] scratchPad) { 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()); + 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() - ); + 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]); + 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{ + } 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; + 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); + data[KEY_BLOB] = KMArray.instance((short) 4); } private void importRSAKey(byte[] scratchPad) { @@ -742,83 +2207,84 @@ private void importRSAKey(byte[] scratchPad) { 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]); + 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) { + if (tmpVariables[2] != 4 + || Util.getShort(scratchPad, (short) 10) != 0x01 + || Util.getShort(scratchPad, (short) 12) != 0x01) { KMException.throwIt(KMError.INVALID_ARGUMENT); } - }else{ + } 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; + 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.createRsaPrivateKey( - 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() - ); + 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]); + 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{ + } 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; + 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); + 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) { + 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[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]){ + while (tmpVariables[3] < tmpVariables[2]) { tmpVariables[4] = KMArray.cast(tmpVariables[0]).get(tmpVariables[3]); - KMArray.cast(tmpVariables[1]).add(tmpVariables[3],tmpVariables[4]); + 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]); + 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; + tmpVariables[5] += 2; } // replace with updated key parameters. data[KEY_PARAMETERS] = KMKeyParameters.instance(tmpVariables[1]); @@ -829,13 +2295,12 @@ private void processSetBootParamsCmd(APDU apdu) { 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 OS Version // short osVersionExp = KMIntegerTag.exp(KMType.UINT_TAG); - tmpVariables[0] = KMIntegerTag.exp(KMType.UINT_TAG); + tmpVariables[0] = KMInteger.exp(); // Argument 2 OS Patch level // short osPatchExp = KMIntegerTag.exp(KMType.UINT_TAG); - tmpVariables[1] = KMIntegerTag.exp(KMType.UINT_TAG); + tmpVariables[1] = KMInteger.exp(); // Argument 3 Verified Boot Key // short bootKeyExp = KMByteBlob.exp(); tmpVariables[2] = KMByteBlob.exp(); @@ -857,6 +2322,7 @@ private void processSetBootParamsCmd(APDU apdu) { 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); @@ -878,10 +2344,11 @@ private void processSetBootParamsCmd(APDU apdu) { } // Begin transaction JCSystem.beginTransaction(); - short valPtr = KMIntegerTag.cast(tmpVariables[0]).getValue(); - 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); + 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); @@ -970,30 +2437,30 @@ private static void processGenerateKey(APDU apdu) { sendOutgoing(apdu); } - private static void validateRSAKey(byte[] scratchPad){ + private static void validateRSAKey(byte[] scratchPad) { // Read key size tmpVariables[0] = - KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); + KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); if (tmpVariables[0] == KMTag.INVALID_VALUE) { - KMException.throwIt(KMError.INVALID_ARGUMENT); + 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]); + 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) { + || Util.getShort(scratchPad, (short) 2) != 0x01) { KMException.throwIt(KMError.INVALID_ARGUMENT); } } @@ -1015,10 +2482,11 @@ private static void generateRSAKey(byte[] scratchPad) { 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){ + + private static void validateAESKey(byte[] scratchPad) { // Read key size tmpVariables[0] = - KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); + KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); if (tmpVariables[0] == KMTag.INVALID_VALUE) { KMException.throwIt(KMError.INVALID_ARGUMENT); } @@ -1027,61 +2495,56 @@ private static void validateAESKey(byte[] scratchPad){ } // Read Block mode - array of byte values tmpVariables[1] = - KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.BLOCK_MODE, data[KEY_PARAMETERS]); + KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.BLOCK_MODE, data[KEY_PARAMETERS]); if (tmpVariables[1] != KMTag.INVALID_VALUE) { // block mode specified - tmpVariables[2] = KMEnumArrayTag.cast(tmpVariables[0]).getValues(); // byte blob - tmpVariables[3] = KMByteBlob.cast(tmpVariables[2]).length(); // length - tmpVariables[4] = 0; // index - tmpVariables[5] = AES_BLOCK_SIZE; // block size - tmpVariables[5] = - KMKeyParameters.findTag( - KMType.UINT_TAG, - KMType.MIN_MAC_LENGTH, - data[KEY_PARAMETERS]); // Find Minimum Mac length - while (tmpVariables[4] < tmpVariables[3]) { // for each value in block mode array - if (KMByteBlob.cast(tmpVariables[2]).get(tmpVariables[4]) == KMType.GCM) { // if GCM mode - if (tmpVariables[5] == KMTag.INVALID_VALUE) { // minimum mac length must be specified - KMException.throwIt(KMError.MISSING_MAC_LENGTH); - } - tmpVariables[6] = KMInteger.cast(KMIntegerTag.cast(tmpVariables[5]).getValue()).getByte(); - if (tmpVariables[6] < 12 || tmpVariables[6] > 16) { - KMException.throwIt(KMError.UNSUPPORTED_MAC_LENGTH); - } - tmpVariables[6] = 12; // simulator supports only 12 bits tag for GCM. - } else { // if not GCM mode - if (tmpVariables[5] != KMTag.INVALID_VALUE) { // no mac length should be specified - KMException.throwIt(KMError.INVALID_ARGUMENT); - } + // 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); } - tmpVariables[4]++; } } } + 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){ + private static void validateECKeys(byte[] scratchPad) { // Read key size tmpVariables[0] = - KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); - if (tmpVariables[0] == KMTag.INVALID_VALUE) { + 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); - } - if (tmpVariables[0] != 256) { + }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); } - // Read EC_CURVE - tmpVariables[1] = KMEnumTag.getValue(KMType.ECCURVE, data[KEY_PARAMETERS]); - if (tmpVariables[1] != KMType.INVALID_VALUE) { - if (tmpVariables[1] != KMType.P_256) { - KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); - } - } } private static void generateECKeys(byte[] scratchPad) { @@ -1095,16 +2558,16 @@ private static void generateECKeys(byte[] scratchPad) { KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_PUB_KEY, data[PUB_KEY]); } - private static void validateTDESKey(byte[] scratchPad){ + 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]); + 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]); + KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); if (tmpVariables[1] == KMType.INVALID_VALUE) { KMException.throwIt(KMError.INVALID_ARGUMENT); } @@ -1112,6 +2575,7 @@ private static void validateTDESKey(byte[] scratchPad){ KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); } } + private static void generateTDESKey(byte[] scratchPad) { validateTDESKey(scratchPad); DESKey desKey = cryptoProvider.createTDESKey(); @@ -1120,81 +2584,24 @@ private static void generateTDESKey(byte[] scratchPad) { data[KEY_BLOB] = KMArray.instance((short) 4); } - private static void validateHmacKey(byte[] scratchPad){ - // 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)) { - 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] > 512) || ((short) (tmpVariables[1] % 8) != 0)) { - KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); - } - // Read digests - tmpVariables[2] = - KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.DIGEST, data[KEY_PARAMETERS]); - if (tmpVariables[2] == KMType.INVALID_VALUE) { - KMException.throwIt(KMError.INVALID_ARGUMENT); - } - tmpVariables[3] = KMEnumArrayTag.cast(tmpVariables[2]).getValues(); - tmpVariables[4] = KMByteBlob.cast(tmpVariables[3]).length(); - tmpVariables[5] = 0; + private static void validateHmacKey(byte[] scratchPad) { // check whether digest sizes are greater then or equal to min mac length. - while (tmpVariables[5] < tmpVariables[4]) { - tmpVariables[6] = KMByteBlob.cast(tmpVariables[3]).get(tmpVariables[5]); - switch (tmpVariables[6]) { - case KMType.DIGEST_NONE: - KMException.throwIt(KMError.UNSUPPORTED_DIGEST); - break; - case KMType.MD5: - tmpVariables[7] = 128; - break; - case KMType.SHA1: - tmpVariables[7] = 160; - break; - case KMType.SHA2_224: - tmpVariables[7] = 224; - break; - case KMType.SHA2_256: - tmpVariables[7] = 256; - break; - case KMType.SHA2_384: - tmpVariables[7] = 384; - break; - case KMType.SHA2_512: - tmpVariables[7] = 512; - break; - default: - tmpVariables[7] = 0; - break; - } - if (tmpVariables[7] == 0) { - KMException.throwIt(KMError.UNSUPPORTED_DIGEST); - } - if (tmpVariables[0] > tmpVariables[7]) { - KMException.throwIt(KMError.UNSUPPORTED_MIN_MAC_LENGTH); - } - tmpVariables[5]++; + // 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); } - } - private static void generateHmacKey(byte[] scratchPad) { - validateHmacKey(scratchPad); // 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)) { + if (((short) (tmpVariables[0] % 8) != 0) || + (tmpVariables[0] < (short) 64)|| + tmpVariables[0] > (short)256) { KMException.throwIt(KMError.UNSUPPORTED_MIN_MAC_LENGTH); } // Read keysize @@ -1203,55 +2610,15 @@ private static void generateHmacKey(byte[] scratchPad) { if (tmpVariables[1] == KMType.INVALID_VALUE) { KMException.throwIt(KMError.INVALID_ARGUMENT); } - if ((tmpVariables[1] > 512) || ((short) (tmpVariables[1] % 8) != 0)) { + if (!(tmpVariables[1] >= 64 && tmpVariables[1] <= 512 && tmpVariables[1] % 8 == 0)) { KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); } - // Read digests - tmpVariables[2] = - KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.DIGEST, data[KEY_PARAMETERS]); - if (tmpVariables[2] == KMType.INVALID_VALUE) { - KMException.throwIt(KMError.INVALID_ARGUMENT); - } - tmpVariables[3] = KMEnumArrayTag.cast(tmpVariables[2]).getValues(); - tmpVariables[4] = KMByteBlob.cast(tmpVariables[3]).length(); - tmpVariables[5] = 0; - // check whether digest sizes are greater then or equal to min mac length. - while (tmpVariables[5] < tmpVariables[4]) { - tmpVariables[6] = KMByteBlob.cast(tmpVariables[3]).get(tmpVariables[5]); - switch (tmpVariables[6]) { - case KMType.DIGEST_NONE: - KMException.throwIt(KMError.UNSUPPORTED_DIGEST); - break; - case KMType.MD5: - tmpVariables[7] = 128; - break; - case KMType.SHA1: - tmpVariables[7] = 160; - break; - case KMType.SHA2_224: - tmpVariables[7] = 224; - break; - case KMType.SHA2_256: - tmpVariables[7] = 256; - break; - case KMType.SHA2_384: - tmpVariables[7] = 384; - break; - case KMType.SHA2_512: - tmpVariables[7] = 512; - break; - default: - tmpVariables[7] = 0; - break; - } - if (tmpVariables[7] == 0) { - KMException.throwIt(KMError.UNSUPPORTED_DIGEST); - } - if (tmpVariables[0] > tmpVariables[7]) { - KMException.throwIt(KMError.UNSUPPORTED_MIN_MAC_LENGTH); - } - tmpVariables[5]++; - } + } + + 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); @@ -1298,7 +2665,11 @@ private static void makeKeyCharacteristics(byte[] scratchPad) { 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[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]); @@ -1319,36 +2690,39 @@ private static void createEncryptedKeyBlob(byte[] scratchPad) { 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) 256); // TODO use buffer + 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); + 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()); + 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 (!KMRepository.validateAuthTag(data[AUTH_TAG])) { + if (!repository.validateAuthTag(data[AUTH_TAG])) { KMException.throwIt(KMError.INVALID_KEY_BLOB); } // initialize data @@ -1359,14 +2733,18 @@ private static void parseEncryptedKeyBlob(byte[] scratchPad) { 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(); + 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); + 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); } @@ -1399,13 +2777,16 @@ private static void decryptSecret(byte[] scratchPad) { 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.arrayCopy( + Util.arrayCopyNonAtomic( KMByteBlob.cast(data[NONCE]).getBuffer(), KMByteBlob.cast(data[NONCE]).getStartOff(), scratchPad, @@ -1512,4 +2893,21 @@ private static void sendError(APDU apdu, short err) { bufferLength = encoder.encodeError(err, buffer, bufferStartOffset, (short) 5); sendOutgoing(apdu); } + /* + private static 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 static 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()); + }*/ } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMOperationState.java b/Applet/Applet/src/com/android/javacard/keymaster/KMOperationState.java index 47cdabf8..2684efb3 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMOperationState.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMOperationState.java @@ -16,32 +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 static void create(KMOperationState[] opStateRefTable) { - byte index = 0; - while (index < opStateRefTable.length) { - opStateRefTable[index] = new KMOperationState(); - index++; + + public void setTrustedConfirmationSigner(Signature hmacSigner){ + this.hmacSigner = hmacSigner; + trustedConfirmation = true; + } + 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 byte[] getAuthTime() { + return authTime; } - public KMInteger getOperationHandle() { - return operationHandle; + public void setAuthTime(byte[] time, short start) { + Util.arrayCopy(time, start, authTime, (short)0, (short)8); } - public void setOperationHandle(KMInteger operationHandle) { - this.operationHandle = operationHandle; + public void setAuthTimeoutValidated(boolean flag) { + authTimeoutValidated = flag; } } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMRepository.java b/Applet/Applet/src/com/android/javacard/keymaster/KMRepository.java index 8e0f01c3..421065ab 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMRepository.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMRepository.java @@ -22,16 +22,25 @@ import javacard.framework.Util; public class KMRepository { - public static final short HEAP_SIZE = 0x1000; + //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; @@ -44,33 +53,91 @@ public class KMRepository { public short actualBootHashLength; public boolean verifiedBootFlag; public boolean selfSignedBootFlag; - public boolean deviceLockedFlag ; + public boolean deviceLockedFlag; public static KMRepository instance() { return repository; } - public KMRepository(byte[] masterKey) { + public KMRepository() { heap = JCSystem.makeTransientByteArray(HEAP_SIZE, JCSystem.CLEAR_ON_RESET); - this.masterKey = new byte[(short)masterKey.length]; - // Initialize masterkey - Util.arrayCopy(masterKey, (short)0, this.masterKey, (short)0, (short)masterKey.length); authTagRepo = new Object[MAX_BLOB_STORAGE]; short index = 0; while (index < MAX_BLOB_STORAGE) { - authTagRepo[index] = new byte[AES_GCM_AUTH_TAG_LENGTH]; + 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++; } 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 KMOperationState reserveOperation(){ + short index = 0; + while(index < MAX_OPS){ + if(!((KMOperationState)operationStateTable[index]).isActive()){ + ((KMOperationState)operationStateTable[index]).activate(); + return (KMOperationState)operationStateTable[index]; + } + index++; + } + return null; + } + public void releaseOperation(KMOperationState op){ + op.reset(); + } + 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 initHmacKey(byte[] key, short len) { + if (hmacKey == null) { + hmacKey = new byte[len]; + Util.arrayCopy(key, (short) 0, hmacKey, (short) 0, len); + } + } + + public void initHmacSeed(byte[] seed, short len) { + if (hmacSeed == null) { + hmacSeed = new byte[len]; + Util.arrayCopy(seed, (short) 0, hmacSeed, (short) 0, len); + } + } + + 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 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 void onUninstall() { - //TODO change this - Util.arrayFillNonAtomic(masterKey,(short)0,(short)masterKey.length,(byte) 0); + // TODO change this + Util.arrayFillNonAtomic(masterKey, (short) 0, (short) masterKey.length, (byte) 0); } public void onProcess() {} @@ -100,89 +167,131 @@ public byte[] getHeap() { return heap; } - public static void persistAuthTag(short authTag) { - final byte[] compare = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - }; // length equal to AES_GCM_AUTH_TAG_LENGTH. + public byte[] getHmacSeed() { + return hmacSeed; + } + + public byte[] getHmacKey() { + return hmacKey; + } + + public byte[] getHmacNonce() { + return hmacNonce; + } + + public void setHmacNonce(byte[] hmacNonce) { + Util.arrayCopy(hmacNonce, (short) 0, this.hmacNonce, (short) 0, HMAC_SEED_NONCE_SIZE); + } + public byte[] getComputedHmacKey() { + return computedHmacKey; + } + + public void setComputedHmacKey(byte[] computedHmacKey) { + Util.arrayCopy( computedHmacKey, (short) 0, this.computedHmacKey, (short) 0, COMPUTED_HMAC_KEY_SIZE); + } + + public void persistAuthTag(short authTag) { short index = 0; - byte ret = 0; while (index < MAX_BLOB_STORAGE) { - ret = - Util.arrayCompare( - (byte[]) (repository.authTagRepo[index]), - (short) 0, - compare, - (short) 0, - AES_GCM_AUTH_TAG_LENGTH); - if (ret == 0) { + 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++; } - // This should never happen - if (index >= repository.MAX_BLOB_STORAGE){ - ISOException.throwIt(ISO7816.SW_BYTES_REMAINING_00); + } + + public boolean validateAuthTag(short authTag) { + KMAuthTag tag = findTag(authTag); + if(tag != null){ + return true; + } + return false; + } + + public void removeAuthTag(short authTag) { + KMAuthTag tag = findTag(authTag); + if(tag == null){ + ISOException.throwIt(ISO7816.SW_COMMAND_NOT_ALLOWED); } JCSystem.beginTransaction(); - Util.arrayCopy( - KMByteBlob.cast(authTag).getBuffer(), - KMByteBlob.cast(authTag).getStartOff(), - (byte[]) (repository.authTagRepo[index]), - (short) 0, - AES_GCM_AUTH_TAG_LENGTH); - repository.keyBlobCount++; + tag.reserved = false; + Util.arrayFill(tag.authTag, (short) 0, AES_GCM_AUTH_TAG_LENGTH, (byte) 0); + tag.usageCount = 0; + keyBlobCount--; JCSystem.commitTransaction(); } - public static void removeAuthTag(short authTag) { - final byte[] zeroTag = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - }; // length equal to AES_GCM_AUTH_TAG_LENGTH. + public void removeAllAuthTags() { + JCSystem.beginTransaction(); short index = 0; - byte ret = 0; - while (index < repository.MAX_BLOB_STORAGE) { - ret = - Util.arrayCompare( - (byte[]) (repository.authTagRepo[index]), - (short) 0, - KMByteBlob.cast(authTag).getBuffer(), - KMByteBlob.cast(authTag).getStartOff(), - AES_GCM_AUTH_TAG_LENGTH); - if (ret == 0) { - break; - } + 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++; } - if (index >= MAX_BLOB_STORAGE) { - ISOException.throwIt(ISO7816.SW_COMMAND_NOT_ALLOWED); - } - JCSystem.beginTransaction(); - Util.arrayCopy( - zeroTag, (short) 0, (byte[]) (repository.authTagRepo[index]), (short) 0, AES_GCM_AUTH_TAG_LENGTH); - repository.keyBlobCount--; + keyBlobCount = 0; JCSystem.commitTransaction(); } - public static boolean validateAuthTag(short authTag) { + public KMAuthTag findTag(short authTag) { short index = 0; - byte ret = 0; + short found = 0; while (index < MAX_BLOB_STORAGE) { - ret = - Util.arrayCompare( - (byte[]) (repository.authTagRepo[index]), - (short) 0, - KMByteBlob.cast(authTag).getBuffer(), - KMByteBlob.cast(authTag).getStartOff(), - AES_GCM_AUTH_TAG_LENGTH); - if (ret == 0) { - break; + 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++; } - if (index >= MAX_BLOB_STORAGE) { - return false; + return null; + } + + public short getRateLimitedKeyCount(short authTag) { + KMAuthTag tag = findTag(authTag); + if (tag != null) { + return tag.usageCount; } - return true; + return KMType.INVALID_VALUE; } + public void setRateLimitedKeyCount(short authTag, short val) { + KMAuthTag tag = findTag(authTag); + JCSystem.beginTransaction(); + if (tag != null) { + tag.usageCount = val; + } + JCSystem.commitTransaction(); + } + + 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++; + } + return null; + } } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMType.java b/Applet/Applet/src/com/android/javacard/keymaster/KMType.java index 1e023647..f136bd83 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMType.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMType.java @@ -36,7 +36,7 @@ public abstract class KMType { public static final byte VERIFICATION_TOKEN_TYPE = 0x09; public static final byte HMAC_SHARING_PARAM_TYPE = 0x0A; - // Tags + // Tag Types public static final short INVALID_TAG = 0x0000; public static final short ENUM_TAG = 0x1000; public static final short ENUM_ARRAY_TAG = 0x2000; @@ -76,6 +76,7 @@ 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; @@ -127,13 +128,13 @@ public abstract class KMType { // 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; @@ -194,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 @@ -275,7 +276,7 @@ public static void initialize() { public static short getValue(short ptr){return Util.getShort(heap, (short)(ptr+TLV_HEADER_SIZE));} protected static short instance(byte type, short length){ - if (length <= 0) ISOException.throwIt(ISO7816.SW_WRONG_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); diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMUtil.java b/Applet/Applet/src/com/android/javacard/keymaster/KMUtil.java deleted file mode 100644 index 599da84e..00000000 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMUtil.java +++ /dev/null @@ -1,109 +0,0 @@ -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.KeyBuilder; -import javacard.security.RandomData; -import javacardx.crypto.Cipher; - -public class KMUtil { - private static final short ENTROPY_POOL_SIZE = 16; // simulator does not support 256 bit aes keys - public static final byte AES_BLOCK_SIZE = 16; - 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 AESKey aesKey; - 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); - KMUtil.initEntropyPool(entropyPool); - try { - //Note: ALG_AES_BLOCK_128_CBC_NOPAD not supported by simulator. - aesCbc = Cipher.getInstance(Cipher.ALG_AES_BLOCK_128_CBC_NOPAD, false); - } catch (CryptoException exp) { - // TODO change this to proper error code - ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); - } - aesKey = (AESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_AES, KeyBuilder.LENGTH_AES_128, false); - } - - public static void initEntropyPool(byte[] pool) { - byte index = 0; - RandomData trng; - while (index < counter.length) { - counter[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 counter and 16 byte block size. - public static void newRandomNumber(byte[] num, short startOff, short length) { - KMRepository repository = KMRepository.instance(); - byte[] bufPtr = repository.getHeap(); - short countBufInd = repository.alloc(AES_BLOCK_SIZE); - short randBufInd = repository.alloc(AES_BLOCK_SIZE); - short len = 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 - incrementCounter(); - // 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. - aesCbc.doFinal(bufPtr, countBufInd, 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); - startOff = (short)(startOff + len); - } - } - - // increment 8 byte counter by one - private static void incrementCounter() { - // start with least significant byte - short index = (short) (counter.length - 1); - while (index >= 0) { - // if the msb of current byte is set then it will be negative - if (counter[index] < 0) { - // then increment the counter - counter[index]++; - // is the msb still set? i.e. no carry over - if (counter[index] < 0) break; // then break - else index--; // else go to the higher order byte - } 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 - } - } - } - - public static byte[] getEntropyPool() { - return entropyPool; - } - -} diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMVerificationToken.java b/Applet/Applet/src/com/android/javacard/keymaster/KMVerificationToken.java index eefa9e60..168971c3 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMVerificationToken.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMVerificationToken.java @@ -37,8 +37,9 @@ public static short exp() { KMArray arr = KMArray.cast(arrPtr); arr.add(CHALLENGE, KMInteger.exp()); arr.add(TIMESTAMP, KMInteger.exp()); - arr.add(PARAMETERS_VERIFIED, KMKeyParameters.exp()); - arr.add(SECURITY_LEVEL, KMEnumTag.instance(KMType.HARDWARE_TYPE)); + //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); } @@ -118,7 +119,8 @@ public short getParametersVerified() { } public void setParametersVerified(short vals) { - KMKeyParameters.cast(vals); + // KMKeyParameters.cast(vals); + KMByteBlob.cast(vals); short arrPtr = getVals(); KMArray.cast(arrPtr).add(PARAMETERS_VERIFIED, vals); } @@ -129,8 +131,7 @@ public short getSecurityLevel() { } public void setSecurityLevel(short vals) { - short key = KMEnumTag.cast(vals).getKey(); - if(key != HARDWARE_TYPE) ISOException.throwIt(ISO7816.SW_DATA_INVALID); + KMEnum.cast(vals); short arrPtr = getVals(); KMArray.cast(arrPtr).add(SECURITY_LEVEL, vals); } diff --git a/Applet/Applet/test/com/android/javacard/test/KMFrameworkTest.java b/Applet/Applet/test/com/android/javacard/test/KMFrameworkTest.java index 5f43ebfa..0b2ae651 100644 --- a/Applet/Applet/test/com/android/javacard/test/KMFrameworkTest.java +++ b/Applet/Applet/test/com/android/javacard/test/KMFrameworkTest.java @@ -17,21 +17,21 @@ 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.KMError; import com.android.javacard.keymaster.KMInteger; -import com.android.javacard.keymaster.KMIntegerArrayTag; 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.KMSimulator; import com.android.javacard.keymaster.KMType; import com.licel.jcardsim.smartcardio.CardSimulator; import com.licel.jcardsim.utils.AIDUtil; @@ -46,24 +46,23 @@ import javacard.security.KeyBuilder; import javacard.security.KeyPair; import javacard.security.RSAPrivateKey; -import javacard.security.RandomData; 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 KMSimulator sim; + private KMCryptoProvider sim; @Test public void test_Lifecycle_Success() { // Create simulator - KMSimulator.jcardSim = true; - sim = new KMSimulator(); + //KMJcardSimulator.jcardSim = true; + sim = KMCryptoProviderImpl.instance(); + sim.bypassAesGcm(); CardSimulator simulator = new CardSimulator(); // Install applet @@ -72,6 +71,7 @@ public void test_Lifecycle_Success() { // Select applet simulator.selectApplet(appletAID1); +// testEncodeDecode(); testProvisionCmd(simulator); testSetBootParams(simulator); testGetHwInfoCmd(simulator); @@ -95,8 +95,43 @@ public void test_Lifecycle_Success() { 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[512]; + byte[] buf = new byte[1024]; buf[0] = (byte)0x80; buf[1] = (byte)0x1D; buf[2] = (byte)0x40; @@ -125,7 +160,7 @@ private void testGetKeyCharacteristics(CardSimulator simulator) { } public void testProvisionCmd(CardSimulator simulator){ - byte[] buf = new byte[512]; + byte[] buf = new byte[1024]; // test provision command short cmd = makeProvisionCmd(); KMEncoder enc = new KMEncoder(); @@ -136,7 +171,7 @@ public void testProvisionCmd(CardSimulator simulator){ Assert.assertEquals(0x9000, response.getSW()); } public void testSetBootParams(CardSimulator simulator){ - byte[] buf = new byte[512]; + byte[] buf = new byte[1024]; // test provision command short cmd = makeSetBootParamsCmd(); KMEncoder enc = new KMEncoder(); @@ -148,7 +183,7 @@ public void testSetBootParams(CardSimulator simulator){ } public void testGenerateRsaKey(CardSimulator simulator){ - byte[] buf = new byte[512]; + byte[] buf = new byte[1024]; buf[0] = (byte)0x80; buf[1] = (byte)0x10; buf[2] = (byte)0x40; @@ -170,7 +205,7 @@ public void testGenerateRsaKey(CardSimulator simulator){ } public void testImportRsaKey(CardSimulator simulator){ - byte[] buf = new byte[512]; + byte[] buf = new byte[1024]; buf[0] = (byte)0x80; buf[1] = (byte)0x11; buf[2] = (byte)0x40; @@ -184,14 +219,14 @@ public void testImportRsaKey(CardSimulator simulator){ 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()); + //print(commandAPDU.getBytes()); ResponseAPDU response = simulator.transmitCommand(commandAPDU); extractKeyCharAndBlob(response); Assert.assertEquals(0x9000, response.getSW()); } public void testImportEcKey(CardSimulator simulator){ - byte[] buf = new byte[512]; + byte[] buf = new byte[1024]; buf[0] = (byte)0x80; buf[1] = (byte)0x11; buf[2] = (byte)0x40; @@ -205,7 +240,7 @@ public void testImportEcKey(CardSimulator simulator){ 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()); + //print(commandAPDU.getBytes()); ResponseAPDU response = simulator.transmitCommand(commandAPDU); extractKeyCharAndBlob(response); Assert.assertEquals(0x9000, response.getSW()); @@ -240,7 +275,7 @@ private void extractKeyChar(ResponseAPDU response) { } public void testGenerateAesKey(CardSimulator simulator){ - byte[] buf = new byte[512]; + byte[] buf = new byte[1024]; buf[0] = (byte)0x80; buf[1] = (byte)0x10; buf[2] = (byte)0x40; @@ -262,13 +297,13 @@ public void testGenerateAesKey(CardSimulator simulator){ } public void testImportAesKey(CardSimulator simulator){ - byte[] buf = new byte[512]; + 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)256); + 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); @@ -276,13 +311,13 @@ public void testImportAesKey(CardSimulator simulator){ 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()); + //print(commandAPDU.getBytes()); ResponseAPDU response = simulator.transmitCommand(commandAPDU); extractKeyCharAndBlob(response); Assert.assertEquals(0x9000, response.getSW()); } public void testImportHmacKey(CardSimulator simulator){ - byte[] buf = new byte[512]; + byte[] buf = new byte[1024]; buf[0] = (byte)0x80; buf[1] = (byte)0x11; buf[2] = (byte)0x40; @@ -296,13 +331,13 @@ public void testImportHmacKey(CardSimulator simulator){ 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()); + //print(commandAPDU.getBytes()); ResponseAPDU response = simulator.transmitCommand(commandAPDU); extractKeyCharAndBlob(response); Assert.assertEquals(0x9000, response.getSW()); } public void testImportDesKey(CardSimulator simulator){ - byte[] buf = new byte[512]; + byte[] buf = new byte[1024]; buf[0] = (byte)0x80; buf[1] = (byte)0x11; buf[2] = (byte)0x40; @@ -316,14 +351,14 @@ public void testImportDesKey(CardSimulator simulator){ 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()); + //print(commandAPDU.getBytes()); ResponseAPDU response = simulator.transmitCommand(commandAPDU); extractKeyCharAndBlob(response); Assert.assertEquals(0x9000, response.getSW()); } public void testGenerateHmacKey(CardSimulator simulator){ - byte[] buf = new byte[512]; + byte[] buf = new byte[1024]; buf[0] = (byte)0x80; buf[1] = (byte)0x10; buf[2] = (byte)0x40; @@ -345,7 +380,7 @@ public void testGenerateHmacKey(CardSimulator simulator){ } public void testGenerate3DesKey(CardSimulator simulator){ - byte[] buf = new byte[512]; + byte[] buf = new byte[1024]; buf[0] = (byte)0x80; buf[1] = (byte)0x10; buf[2] = (byte)0x40; @@ -367,7 +402,7 @@ public void testGenerate3DesKey(CardSimulator simulator){ } public void testGenerateEcKey(CardSimulator simulator){ - byte[] buf = new byte[512]; + byte[] buf = new byte[1024]; buf[0] = (byte)0x80; buf[1] = (byte)0x10; buf[2] = (byte)0x40; @@ -414,7 +449,7 @@ 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 short cmd = makeAddRngEntropyCmd(); KMEncoder enc = new KMEncoder(); @@ -472,17 +507,23 @@ private short makeProvisionCmd() { private short makeGenerateKeyCmd(byte alg, short keysize) { // Argument - short arrPtr = KMArray.instance((short) 5); + short arrPtr = KMArray.instance((short) 4); KMArray vals = KMArray.cast(arrPtr); byte[] val = "Test".getBytes(); byte[] intVal = {1, 2, 3, 4}; byte[] pubVal = {0x00, 0x01, 0x00, 0x01}; - byte[] digest = {KMType.SHA1, KMType.SHA2_256}; + //byte[] digest = {KMType.SHA1, KMType.SHA2_256}; + byte[] digest = {KMType.DIGEST_NONE}; + byte[] padding = {KMType.PADDING_NONE}; + byte[] purpose = {0x02, 0x03}; vals.add((short)0, KMEnumTag.instance(KMType.ALGORITHM, alg)); - vals.add((short)1, KMIntegerTag.instance(KMType.UINT_TAG, KMType.USERID, KMInteger.uint_32(intVal, (short)0))); - vals.add((short)2, KMByteTag.instance(KMType.APPLICATION_ID, KMByteBlob.instance(val, (short)0, (short)val.length))); - vals.add((short)3, KMIntegerTag.instance(KMType.ULONG_TAG, KMType.RSA_PUBLIC_EXPONENT, KMInteger.uint_32(pubVal,(short)0))); - vals.add((short)4, KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, KMInteger.uint_16(keysize))); + vals.add((short)1, KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, KMInteger.uint_16(keysize))); + //vals.add((short)1, KMIntegerTag.instance(KMType.UINT_TAG, KMType.USERID, KMInteger.uint_32(intVal, (short)0))); + //vals.add((short)2, KMByteTag.instance(KMType.APPLICATION_ID, KMByteBlob.instance(val, (short)0, (short)val.length))); + vals.add((short)2, KMIntegerTag.instance(KMType.ULONG_TAG, KMType.RSA_PUBLIC_EXPONENT, KMInteger.uint_32(pubVal,(short)0))); + vals.add((short)3, KMEnumArrayTag.instance(KMType.PURPOSE, KMByteBlob.instance(purpose,(short)0, (short)purpose.length))); + // vals.add((short)4, KMEnumArrayTag.instance(KMType.DIGEST, KMByteBlob.instance(digest,(short)0, (short)digest.length))); + //vals.add((short)5, KMEnumArrayTag.instance(KMType.PADDING, KMByteBlob.instance(padding,(short)0, (short)padding.length))); short keyParamsPtr = KMKeyParameters.instance(arrPtr); // Array of expected arguments short argPtr = KMArray.instance((short) 1); @@ -502,7 +543,7 @@ private short makeImportKeySymmCmd(short alg, short size) { byte[] val = "Test".getBytes(); byte[] intVal = {1, 2, 3, 4}; byte[] pubVal = {0x00, 0x01, 0x00, 0x01}; - byte[] digest = {KMType.SHA1, KMType.SHA2_256}; + byte[] digest = {KMType.SHA2_256}; vals.add((short)0, KMEnumTag.instance(KMType.ALGORITHM, (byte)alg)); vals.add((short)1, KMIntegerTag.instance(KMType.UINT_TAG, KMType.USERID, KMInteger.uint_32(intVal, (short)0))); vals.add((short)2, KMByteTag.instance(KMType.APPLICATION_ID, KMByteBlob.instance(val, (short)0, (short)val.length))); @@ -553,7 +594,7 @@ private short makeImportKeySymmCmd(short alg, short size) { private short makeImportKeyRsaCmd() { // Argument 1 - short arrPtr = KMArray.instance((short) 4); + short arrPtr = KMArray.instance((short) 5); KMArray vals = KMArray.cast(arrPtr); byte[] val = "Test".getBytes(); byte[] intVal = {1, 2, 3, 4}; @@ -563,23 +604,25 @@ private short makeImportKeyRsaCmd() { vals.add((short)1, KMIntegerTag.instance(KMType.UINT_TAG, KMType.USERID, KMInteger.uint_32(intVal, (short)0))); vals.add((short)2, KMByteTag.instance(KMType.APPLICATION_ID, KMByteBlob.instance(val, (short)0, (short)val.length))); vals.add((short)3, KMIntegerTag.instance(KMType.ULONG_TAG, KMType.RSA_PUBLIC_EXPONENT, KMInteger.uint_32(pubVal,(short)0))); + vals.add((short)4, KMBoolTag.instance(KMType.NO_AUTH_REQUIRED)); + //vals.add((short)4, KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, KMInteger.uint_16((short)2048))); short keyParamsPtr = KMKeyParameters.instance(arrPtr); // Argument 2 short keyFormatPtr = KMEnum.instance(KMType.KEY_FORMAT, KMType.RAW); // Argument 3 - KeyPair rsa512KeyPair = new KeyPair(KeyPair.ALG_RSA, KeyBuilder.LENGTH_RSA_512); - rsa512KeyPair.genKeyPair(); - byte[] secret = new byte[64]; - byte[] modulus = new byte[64]; + KeyPair rsaKeyPair = new KeyPair(KeyPair.ALG_RSA, KeyBuilder.LENGTH_RSA_2048); + rsaKeyPair.genKeyPair(); + byte[] secret = new byte[256]; + byte[] modulus = new byte[256]; short keyBlob = KMArray.instance((short)2); - RSAPrivateKey key = (RSAPrivateKey) rsa512KeyPair.getPrivate(); + RSAPrivateKey key = (RSAPrivateKey) rsaKeyPair.getPrivate(); short len = key.getExponent(secret, (short)0); KMArray.cast(keyBlob).add((short)0, KMByteBlob.instance(secret,(short)0,len)); len = key.getModulus(modulus, (short)0); KMArray.cast(keyBlob).add((short)1, KMByteBlob.instance(modulus,(short)0,len)); KMEncoder encoder = new KMEncoder(); - byte[] blob = new byte[256]; + byte[] blob = new byte[1024]; len = encoder.encode(keyBlob,blob,(short)0); keyBlob = KMByteBlob.instance(blob, (short)0, len); // Array of expected arguments @@ -640,11 +683,10 @@ private short makeGetKeyCharKeyCmd() { byte[] val = "Test".getBytes(); byte[] intVal = {1, 2, 3, 4}; byte[] pubVal = {0x00, 0x01, 0x00, 0x01}; - byte[] digest = {KMType.SHA1, KMType.SHA2_256}; + byte[] digest = {KMType.SHA2_256}; vals.add((short)0, keyBlob); vals.add((short)1, KMByteBlob.instance(val, (short)0, (short)val.length)); - Util.setShort(val,(short)0,KMType.INVALID_VALUE); - vals.add((short)2, KMByteBlob.instance(val, (short)0, (short)2));// No App Data + vals.add((short)2, KMByteBlob.instance((short)0));// No App Data return argPtr; } private short makeGenerateKeyCmdHmac(byte alg, short keysize) { @@ -653,7 +695,7 @@ private short makeGenerateKeyCmdHmac(byte alg, short keysize) { KMArray vals = KMArray.cast(arrPtr); byte[] val = "Test".getBytes(); byte[] intVal = {1, 2, 3, 4}; - byte[] digest = {KMType.SHA1, KMType.SHA2_256}; + byte[] digest = {KMType.SHA2_256}; vals.add((short)0, KMEnumTag.instance(KMType.ALGORITHM, alg)); vals.add((short)1, KMIntegerTag.instance(KMType.UINT_TAG, KMType.USERID, KMInteger.uint_32(intVal, (short)0))); vals.add((short)2, KMByteTag.instance(KMType.APPLICATION_ID, KMByteBlob.instance(val, (short)0, (short)val.length))); @@ -671,9 +713,9 @@ private short makeGenerateKeyCmdHmac(byte alg, short keysize) { private short makeSetBootParamsCmd() { // Argument 1 OS Version short versionPatchPtr = KMInteger.uint_16((short)1); - short versionTagPtr = KMIntegerTag.instance(KMType.UINT_TAG, KMType.OS_VERSION,versionPatchPtr); +// short versionTagPtr = KMIntegerTag.instance(KMType.UINT_TAG, KMType.OS_VERSION,versionPatchPtr); // Argument 2 OS Patch level - short patchTagPtr = KMIntegerTag.instance(KMType.UINT_TAG, KMType.OS_PATCH_LEVEL, versionPatchPtr); +// short patchTagPtr = KMIntegerTag.instance(KMType.UINT_TAG, KMType.OS_PATCH_LEVEL, versionPatchPtr); // Argument 3 Verified Boot Key byte[] bootKeyHash = "00011122233344455566677788899900".getBytes(); short bootKeyPtr = KMByteBlob.instance(bootKeyHash,(short)0, (short)bootKeyHash.length); @@ -686,8 +728,8 @@ private short makeSetBootParamsCmd() { // Arguments short arrPtr = KMArray.instance((short) 6); KMArray vals = KMArray.cast(arrPtr); - vals.add((short)0, versionTagPtr); - vals.add((short) 1, patchTagPtr); + vals.add((short)0, versionPatchPtr); + vals.add((short) 1, versionPatchPtr); vals.add((short) 2, bootKeyPtr); vals.add((short) 3, bootHashPtr); vals.add((short) 4, bootStatePtr); diff --git a/Applet/Applet/test/com/android/javacard/test/KMVTSTest.java b/Applet/Applet/test/com/android/javacard/test/KMVTSTest.java new file mode 100644 index 00000000..c5ef65c9 --- /dev/null +++ b/Applet/Applet/test/com/android/javacard/test/KMVTSTest.java @@ -0,0 +1,646 @@ +/* + * 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.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.KMError; +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; +import com.licel.jcardsim.smartcardio.CardSimulator; +import com.licel.jcardsim.utils.AIDUtil; +import javacard.framework.AID; +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.Key; +import javacard.security.KeyBuilder; +import javacard.security.KeyPair; +import javacard.security.RSAPrivateKey; +import javacard.security.RSAPublicKey; +import javax.smartcardio.CommandAPDU; +import javax.smartcardio.ResponseAPDU; +import org.junit.Assert; +import org.junit.Test; + +public class KMVTSTest { + private KMCryptoProvider sim; + private CardSimulator simulator; + private KMEncoder encoder; + private KMDecoder decoder; + private KMCryptoProvider cryptoProvider; + + public KMVTSTest(){ + cryptoProvider = KMCryptoProviderImpl.instance(); + sim = KMCryptoProviderImpl.instance(); + simulator = new CardSimulator(); + encoder = new KMEncoder(); + decoder = new KMDecoder(); + } + + private void init(){ + // Create simulator + //KMJcardSimulator.jcardSim = true; + AID appletAID1 = AIDUtil.create("A000000062"); + simulator.installApplet(appletAID1, KMKeymasterApplet.class); + // Select applet + simulator.selectApplet(appletAID1); + // provision attest key + provisionCmd(simulator); + // set bootup parameters + setBootParams(simulator); + } + + private void setBootParams(CardSimulator simulator){ + // Argument 1 OS Version + short versionPatchPtr = KMInteger.uint_16((short)1); +// short versionTagPtr = KMIntegerTag.instance(KMType.UINT_TAG, KMType.OS_VERSION,versionPatchPtr); + // Argument 2 OS Patch level +// short patchTagPtr = KMIntegerTag.instance(KMType.UINT_TAG, KMType.OS_PATCH_LEVEL, versionPatchPtr); + // Argument 3 Verified Boot Key + byte[] bootKeyHash = "00011122233344455566677788899900".getBytes(); + short bootKeyPtr = KMByteBlob.instance(bootKeyHash,(short)0, (short)bootKeyHash.length); + // Argument 4 Verified Boot Hash + short bootHashPtr = KMByteBlob.instance(bootKeyHash,(short)0, (short)bootKeyHash.length); + // Argument 5 Verified Boot State + short bootStatePtr = KMEnum.instance(KMType.VERIFIED_BOOT_STATE,KMType.VERIFIED_BOOT); + // Argument 6 Device Locked + short deviceLockedPtr = KMEnum.instance(KMType.DEVICE_LOCKED, KMType.DEVICE_LOCKED_FALSE); + // Arguments + short arrPtr = KMArray.instance((short) 6); + KMArray vals = KMArray.cast(arrPtr); + vals.add((short)0, versionPatchPtr); + vals.add((short) 1, versionPatchPtr); + vals.add((short) 2, bootKeyPtr); + vals.add((short) 3, bootHashPtr); + vals.add((short) 4, bootStatePtr); + vals.add((short) 5, deviceLockedPtr); + CommandAPDU apdu = encodeApdu((byte)0x24, arrPtr); + // print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(apdu); + Assert.assertEquals(0x9000, response.getSW()); + + } + + private void provisionCmd(CardSimulator simulator) { + // Argument 1 + short arrPtr = KMArray.instance((short) 1); + KMArray vals = KMArray.cast(arrPtr); + vals.add((short) 0, KMEnumTag.instance(KMType.ALGORITHM, KMType.RSA)); + short keyparamsPtr = KMKeyParameters.instance(arrPtr); + // Argument 2 + short keyFormatPtr = KMEnum.instance(KMType.KEY_FORMAT, KMType.X509); + // Argument 3 + byte[] byteBlob = new byte[48]; + for (short i = 0; i < 48; i++) { + byteBlob[i] = (byte) i; + } + short keyBlobPtr = KMByteBlob.instance(byteBlob, (short) 0, (short)byteBlob.length); + // Array of expected arguments + short argPtr = KMArray.instance((short) 3); + KMArray arg = KMArray.cast(argPtr); + arg.add((short) 0, keyparamsPtr); + arg.add((short) 1, keyFormatPtr); + arg.add((short) 2, keyBlobPtr); + CommandAPDU apdu = encodeApdu((byte)0x23, argPtr); + // print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(apdu); + Assert.assertEquals(0x9000, response.getSW()); + } + + private void cleanUp(){ + AID appletAID1 = AIDUtil.create("A000000062"); + // Delete i.e. uninstall applet + simulator.deleteApplet(appletAID1); + } + + private CommandAPDU encodeApdu(byte ins, short cmd){ + byte[] buf = new byte[1024]; + buf[0] = (byte)0x80; + buf[1] = ins; + buf[2] = (byte)0x40; + buf[3] = (byte)0x00; + buf[4] = 0; + short len = encoder.encode(cmd, buf, (short) 7); + Util.setShort(buf, (short)5, len); + byte[] apdu = new byte[7+len]; + Util.arrayCopyNonAtomic(buf,(short)0,apdu,(short)0,(short)(7+len)); + //CommandAPDU commandAPDU = new CommandAPDU(0x80, 0x10, 0x40, 0x00, buf, 0, actualLen); + return new CommandAPDU(apdu); + } + + @Test + public void testAesImportKeySuccess() { + init(); + byte[] aesKeySecret = new byte[] {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + short arrPtr = KMArray.instance((short)5); + 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); + KMArray.cast(arrPtr).add((short)4, KMEnumTag.instance(KMType.ALGORITHM, KMType.AES)); + short keyParams = KMKeyParameters.instance(arrPtr); + short keyFormatPtr = KMEnum.instance(KMType.KEY_FORMAT, KMType.RAW); + short keyBlob = KMArray.instance((short)1); + KMArray.cast(keyBlob).add((short)0, KMByteBlob.instance(aesKeySecret,(short)0,(short)16)); + byte[] blob = new byte[256]; + short len = encoder.encode(keyBlob,blob,(short)0); + keyBlob = KMByteBlob.instance(blob, (short)0, len); + arrPtr = KMArray.instance((short)3); + KMArray arg = KMArray.cast(arrPtr); + arg.add((short) 0, keyParams); + arg.add((short)1, keyFormatPtr); + arg.add((short)2, keyBlob); + CommandAPDU apdu = encodeApdu((byte)0x11, arrPtr); + // print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(apdu); + 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); + byte[] respBuf = response.getBytes(); + len = (short) respBuf.length; + ret = decoder.decode(ret, respBuf, (short) 0, len); + short error = KMInteger.cast(KMArray.cast(ret).get((short)0)).getShort(); + short keyBlobLength = KMByteBlob.cast(KMArray.cast(ret).get((short)1)).length(); + short keyCharacteristics = KMArray.cast(ret).get((short)2); + short hwParams = KMKeyCharacteristics.cast(keyCharacteristics).getHardwareEnforced(); + short swParams = KMKeyCharacteristics.cast(keyCharacteristics).getSoftwareEnforced(); + Assert.assertEquals(0x9000, response.getSW()); + Assert.assertEquals(error, KMError.OK); + short tag = KMKeyParameters.findTag(KMType.BOOL_TAG, KMType.NO_AUTH_REQUIRED, hwParams); + Assert.assertEquals(KMBoolTag.cast(tag).getVal(),0x01); + tag = KMKeyParameters.findTag(KMType.UINT_TAG, KMType.KEYSIZE, hwParams); + Assert.assertEquals(KMInteger.cast(KMIntegerTag.cast(tag).getValue()).getShort(), 128); + tag = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.PADDING, hwParams); + Assert.assertTrue(KMEnumArrayTag.cast(tag).contains(KMType.PKCS7)); + tag = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.BLOCK_MODE, hwParams); + Assert.assertTrue(KMEnumArrayTag.cast(tag).contains(KMType.ECB)); + tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ALGORITHM, hwParams); + Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.AES); + tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ORIGIN, hwParams); + Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.IMPORTED); + cleanUp(); + } + + @Test + public void testHmacImportKeySuccess() { + init(); + byte[] hmacKeySecret = new byte[] {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + short arrPtr = KMArray.instance((short)5); + 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.SHA2_256); + short digest = KMEnumArrayTag.instance(KMType.DIGEST, byteBlob); + short minMacLength = KMIntegerTag.instance(KMType.UINT_TAG,KMType.MIN_MAC_LENGTH, KMInteger.uint_16((short)256)); + KMArray.cast(arrPtr).add((short)0, boolTag); + KMArray.cast(arrPtr).add((short)1, keySize); + KMArray.cast(arrPtr).add((short)2, digest); + KMArray.cast(arrPtr).add((short)3, minMacLength); + KMArray.cast(arrPtr).add((short)4, KMEnumTag.instance(KMType.ALGORITHM, KMType.HMAC)); + short keyParams = KMKeyParameters.instance(arrPtr); + short keyFormatPtr = KMEnum.instance(KMType.KEY_FORMAT, KMType.RAW); + short keyBlob = KMArray.instance((short)1); + KMArray.cast(keyBlob).add((short)0, KMByteBlob.instance(hmacKeySecret,(short)0,(short)16)); + byte[] blob = new byte[256]; + short len = encoder.encode(keyBlob,blob,(short)0); + keyBlob = KMByteBlob.instance(blob, (short)0, len); + arrPtr = KMArray.instance((short)3); + KMArray arg = KMArray.cast(arrPtr); + arg.add((short) 0, keyParams); + arg.add((short)1, keyFormatPtr); + arg.add((short)2, keyBlob); + CommandAPDU apdu = encodeApdu((byte)0x11, arrPtr); + // print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(apdu); + 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); + byte[] respBuf = response.getBytes(); + len = (short) respBuf.length; + ret = decoder.decode(ret, respBuf, (short) 0, len); + short error = KMInteger.cast(KMArray.cast(ret).get((short)0)).getShort(); + short keyBlobLength = KMByteBlob.cast(KMArray.cast(ret).get((short)1)).length(); + short keyCharacteristics = KMArray.cast(ret).get((short)2); + short hwParams = KMKeyCharacteristics.cast(keyCharacteristics).getHardwareEnforced(); + short swParams = KMKeyCharacteristics.cast(keyCharacteristics).getSoftwareEnforced(); + Assert.assertEquals(0x9000, response.getSW()); + Assert.assertEquals(error, KMError.OK); + short tag = KMKeyParameters.findTag(KMType.BOOL_TAG, KMType.NO_AUTH_REQUIRED, hwParams); + Assert.assertEquals(KMBoolTag.cast(tag).getVal(),0x01); + tag = KMKeyParameters.findTag(KMType.UINT_TAG, KMType.KEYSIZE, hwParams); + Assert.assertEquals(KMInteger.cast(KMIntegerTag.cast(tag).getValue()).getShort(), 128); + tag = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.DIGEST, hwParams); + Assert.assertTrue(KMEnumArrayTag.cast(tag).contains(KMType.SHA2_256)); + tag = KMKeyParameters.findTag(KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, hwParams); + Assert.assertEquals(KMInteger.cast(KMIntegerTag.cast(tag).getValue()).getShort(), 256); + tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ALGORITHM, hwParams); + Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.HMAC); + tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ORIGIN, hwParams); + Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.IMPORTED); + cleanUp(); + } + + @Test + public void testRsaImportKeySuccess() { + init(); + KeyPair rsaKeyPair = cryptoProvider.createRsaKeyPair(); + byte[] pub = new byte[4]; + short len = ((RSAPublicKey)rsaKeyPair.getPublic()).getExponent(pub,(short)1); + byte[] priv = new byte[256]; + byte[] mod = new byte[256]; + len = ((RSAPrivateKey)rsaKeyPair.getPrivate()).getModulus(mod,(short)0); + len = ((RSAPrivateKey)rsaKeyPair.getPrivate()).getExponent(priv,(short)0); + short arrPtr = KMArray.instance((short)6); + short boolTag = KMBoolTag.instance(KMType.NO_AUTH_REQUIRED); + short keySize = KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, KMInteger.uint_16((short)2048)); + short byteBlob = KMByteBlob.instance((short)1); + KMByteBlob.cast(byteBlob).add((short)0, KMType.SHA2_256); + short digest = KMEnumArrayTag.instance(KMType.DIGEST, byteBlob); + short rsaPubExpTag = KMIntegerTag.instance(KMType.ULONG_TAG,KMType.RSA_PUBLIC_EXPONENT, KMInteger.uint_32(pub, (short)0)); + byteBlob = KMByteBlob.instance((short)1); + KMByteBlob.cast(byteBlob).add((short)0, KMType.RSA_PSS); + short padding = 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, digest); + KMArray.cast(arrPtr).add((short)3, rsaPubExpTag); + KMArray.cast(arrPtr).add((short)4, KMEnumTag.instance(KMType.ALGORITHM, KMType.RSA)); + KMArray.cast(arrPtr).add((short)5, padding); + short keyParams = KMKeyParameters.instance(arrPtr); + short keyFormatPtr = KMEnum.instance(KMType.KEY_FORMAT, KMType.RAW);// Note: VTS uses PKCS8 + short keyBlob = KMArray.instance((short)2); + KMArray.cast(keyBlob).add((short)0, KMByteBlob.instance(priv,(short)0,(short)256)); + KMArray.cast(keyBlob).add((short)1, KMByteBlob.instance(mod,(short)0,(short)256)); + byte[] blob = new byte[620]; + len = encoder.encode(keyBlob,blob,(short)0); + keyBlob = KMByteBlob.instance(blob, (short)0, len); + arrPtr = KMArray.instance((short)3); + KMArray arg = KMArray.cast(arrPtr); + arg.add((short) 0, keyParams); + arg.add((short)1, keyFormatPtr); + arg.add((short)2, keyBlob); + CommandAPDU apdu = encodeApdu((byte)0x11, arrPtr); + // print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(apdu); + 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); + byte[] respBuf = response.getBytes(); + len = (short) respBuf.length; + ret = decoder.decode(ret, respBuf, (short) 0, len); + short error = KMInteger.cast(KMArray.cast(ret).get((short)0)).getShort(); + short keyBlobLength = KMByteBlob.cast(KMArray.cast(ret).get((short)1)).length(); + short keyCharacteristics = KMArray.cast(ret).get((short)2); + short hwParams = KMKeyCharacteristics.cast(keyCharacteristics).getHardwareEnforced(); + short swParams = KMKeyCharacteristics.cast(keyCharacteristics).getSoftwareEnforced(); + Assert.assertEquals(0x9000, response.getSW()); + Assert.assertEquals(error, KMError.OK); + short tag = KMKeyParameters.findTag(KMType.BOOL_TAG, KMType.NO_AUTH_REQUIRED, hwParams); + Assert.assertEquals(KMBoolTag.cast(tag).getVal(),0x01); + tag = KMKeyParameters.findTag(KMType.UINT_TAG, KMType.KEYSIZE, hwParams); + Assert.assertEquals(KMInteger.cast(KMIntegerTag.cast(tag).getValue()).getShort(), 2048); + tag = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.DIGEST, hwParams); + Assert.assertTrue(KMEnumArrayTag.cast(tag).contains(KMType.SHA2_256)); + tag = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.PADDING, hwParams); + Assert.assertTrue(KMEnumArrayTag.cast(tag).contains(KMType.RSA_PSS)); + tag = KMKeyParameters.findTag(KMType.ULONG_TAG, KMType.RSA_PUBLIC_EXPONENT, hwParams); + Assert.assertEquals(KMInteger.cast(KMIntegerTag.cast(tag).getValue()).getSignificantShort(), 0x01); + Assert.assertEquals(KMInteger.cast(KMIntegerTag.cast(tag).getValue()).getShort(), 0x01); + tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ALGORITHM, hwParams); + Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.RSA); + tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ORIGIN, hwParams); + Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.IMPORTED); + cleanUp(); + } + + @Test + public void testEcImportKeySuccess() { + init(); + KeyPair ecKeyPair = cryptoProvider.createECKeyPair(); + byte[] pub = new byte[128]; + short len = ((ECPublicKey)ecKeyPair.getPublic()).getW(pub,(short)0); + short pubBlob = KMByteBlob.instance(pub,(short)0,len); + byte[] priv = new byte[32]; + len = ((ECPrivateKey)ecKeyPair.getPrivate()).getS(priv,(short)0); + short privBlob = KMByteBlob.instance(priv,(short)0,len); + short arrPtr = KMArray.instance((short)5); + short boolTag = KMBoolTag.instance(KMType.NO_AUTH_REQUIRED); + short keySize = KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, KMInteger.uint_16((short)256)); + short byteBlob = KMByteBlob.instance((short)1); + KMByteBlob.cast(byteBlob).add((short)0, KMType.SHA2_256); + short digest = KMEnumArrayTag.instance(KMType.DIGEST, byteBlob); + short ecCurve = KMEnumTag.instance(KMType.ECCURVE, KMType.P_256); + KMArray.cast(arrPtr).add((short)0, boolTag); + KMArray.cast(arrPtr).add((short)1, keySize); + KMArray.cast(arrPtr).add((short)2, digest); + KMArray.cast(arrPtr).add((short)3, ecCurve); + KMArray.cast(arrPtr).add((short)4, KMEnumTag.instance(KMType.ALGORITHM, KMType.EC)); + short keyParams = KMKeyParameters.instance(arrPtr); + short keyFormatPtr = KMEnum.instance(KMType.KEY_FORMAT, KMType.RAW);// Note: VTS uses PKCS8 + short keyBlob = KMArray.instance((short)3); + KMArray.cast(keyBlob).add((short)0, privBlob); + KMArray.cast(keyBlob).add((short)1, pubBlob); + KMArray.cast(keyBlob).add((short)2, ecCurve); + byte[] blob = new byte[128]; + len = encoder.encode(keyBlob,blob,(short)0); + keyBlob = KMByteBlob.instance(blob, (short)0, len); + arrPtr = KMArray.instance((short)3); + KMArray arg = KMArray.cast(arrPtr); + arg.add((short) 0, keyParams); + arg.add((short)1, keyFormatPtr); + arg.add((short)2, keyBlob); + CommandAPDU apdu = encodeApdu((byte)0x11, arrPtr); + // print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(apdu); + 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); + byte[] respBuf = response.getBytes(); + len = (short) respBuf.length; + ret = decoder.decode(ret, respBuf, (short) 0, len); + short error = KMInteger.cast(KMArray.cast(ret).get((short)0)).getShort(); + short keyBlobLength = KMByteBlob.cast(KMArray.cast(ret).get((short)1)).length(); + short keyCharacteristics = KMArray.cast(ret).get((short)2); + short hwParams = KMKeyCharacteristics.cast(keyCharacteristics).getHardwareEnforced(); + short swParams = KMKeyCharacteristics.cast(keyCharacteristics).getSoftwareEnforced(); + Assert.assertEquals(0x9000, response.getSW()); + Assert.assertEquals(error, KMError.OK); + short tag = KMKeyParameters.findTag(KMType.BOOL_TAG, KMType.NO_AUTH_REQUIRED, hwParams); + Assert.assertEquals(KMBoolTag.cast(tag).getVal(),0x01); + tag = KMKeyParameters.findTag(KMType.UINT_TAG, KMType.KEYSIZE, hwParams); + Assert.assertEquals(KMInteger.cast(KMIntegerTag.cast(tag).getValue()).getShort(), 256); + tag = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.DIGEST, hwParams); + Assert.assertTrue(KMEnumArrayTag.cast(tag).contains(KMType.SHA2_256)); + tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ECCURVE, hwParams); + Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.P_256); + tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ALGORITHM, hwParams); + Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.EC); + tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ORIGIN, hwParams); + Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.IMPORTED); + cleanUp(); + } + + @Test + public void testRsaGenerateKeySuccess() { + init(); + short ret = generateRsaKey(null, null); + short error = KMInteger.cast(KMArray.cast(ret).get((short)0)).getShort(); + short keyBlobLength = KMByteBlob.cast(KMArray.cast(ret).get((short)1)).length(); + short keyCharacteristics = KMArray.cast(ret).get((short)2); + short hwParams = KMKeyCharacteristics.cast(keyCharacteristics).getHardwareEnforced(); + short swParams = KMKeyCharacteristics.cast(keyCharacteristics).getSoftwareEnforced(); + Assert.assertEquals(error, KMError.OK); + short tag = KMKeyParameters.findTag(KMType.UINT_TAG, KMType.KEYSIZE, hwParams); + Assert.assertEquals(KMInteger.cast(KMIntegerTag.cast(tag).getValue()).getShort(), 2048); + tag = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.DIGEST, hwParams); + Assert.assertTrue(KMEnumArrayTag.cast(tag).contains(KMType.DIGEST_NONE)); + tag = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.PADDING, hwParams); + Assert.assertTrue(KMEnumArrayTag.cast(tag).contains(KMType.PADDING_NONE)); + tag = KMKeyParameters.findTag(KMType.ULONG_TAG, KMType.RSA_PUBLIC_EXPONENT, hwParams); + Assert.assertEquals(KMInteger.cast(KMIntegerTag.cast(tag).getValue()).getSignificantShort(), 0x01); + Assert.assertEquals(KMInteger.cast(KMIntegerTag.cast(tag).getValue()).getShort(), 0x01); + tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ALGORITHM, hwParams); + Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.RSA); + tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ORIGIN, hwParams); + Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.GENERATED); + cleanUp(); + } + + private short generateRsaKey(byte[] clientId, byte[] appData){ + short tagCount = 5; + if(clientId != null) tagCount++; + if(appData != null) tagCount++; + short arrPtr = KMArray.instance(tagCount); + short keySize = KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, KMInteger.uint_16((short)2048)); + short byteBlob = KMByteBlob.instance((short)1); + KMByteBlob.cast(byteBlob).add((short)0, KMType.DIGEST_NONE); + short digest = KMEnumArrayTag.instance(KMType.DIGEST, byteBlob); + byteBlob = KMByteBlob.instance((short)1); + KMByteBlob.cast(byteBlob).add((short)0, KMType.PADDING_NONE); + short padding = KMEnumArrayTag.instance(KMType.PADDING, byteBlob); + byte[] pub = {0,1,0,1}; + short rsaPubExpTag = KMIntegerTag.instance(KMType.ULONG_TAG,KMType.RSA_PUBLIC_EXPONENT, KMInteger.uint_32(pub, (short)0)); + short tagIndex = 0; + KMArray.cast(arrPtr).add(tagIndex++, keySize); + KMArray.cast(arrPtr).add(tagIndex++, digest); + KMArray.cast(arrPtr).add(tagIndex++, rsaPubExpTag); + KMArray.cast(arrPtr).add(tagIndex++, KMEnumTag.instance(KMType.ALGORITHM, KMType.RSA)); + KMArray.cast(arrPtr).add(tagIndex++, padding); + if(clientId != null)KMArray.cast(arrPtr).add(tagIndex++, + KMByteTag.instance(KMType.APPLICATION_ID, KMByteBlob.instance(clientId,(short)0,(short)clientId.length))); + if(appData != null)KMArray.cast(arrPtr).add(tagIndex++, + KMByteTag.instance(KMType.APPLICATION_DATA, KMByteBlob.instance(appData,(short)0,(short)appData.length))); + short keyParams = KMKeyParameters.instance(arrPtr); + arrPtr = KMArray.instance((short)1); + KMArray arg = KMArray.cast(arrPtr); + arg.add((short) 0, keyParams); + CommandAPDU apdu = encodeApdu((byte)0x10, arrPtr); + // print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(apdu); + Assert.assertEquals(0x9000, response.getSW()); + 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); + byte[] respBuf = response.getBytes(); + short len = (short) respBuf.length; + ret = decoder.decode(ret, respBuf, (short) 0, len); + return ret; + } + + @Test + public void testEcGenerateKeySuccess() { + init(); + short arrPtr = KMArray.instance((short)3); + short keySize = KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, KMInteger.uint_16((short)256)); + short byteBlob = KMByteBlob.instance((short)1); + KMByteBlob.cast(byteBlob).add((short)0, KMType.DIGEST_NONE); + short digest = KMEnumArrayTag.instance(KMType.DIGEST, byteBlob); + KMArray.cast(arrPtr).add((short)0, keySize); + KMArray.cast(arrPtr).add((short)1, digest); + KMArray.cast(arrPtr).add((short)2, KMEnumTag.instance(KMType.ALGORITHM, KMType.EC)); + short keyParams = KMKeyParameters.instance(arrPtr); + arrPtr = KMArray.instance((short)1); + KMArray arg = KMArray.cast(arrPtr); + arg.add((short) 0, keyParams); + CommandAPDU apdu = encodeApdu((byte)0x10, arrPtr); + // print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(apdu); + 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); + byte[] respBuf = response.getBytes(); + short len = (short) respBuf.length; + ret = decoder.decode(ret, respBuf, (short) 0, len); + short error = KMInteger.cast(KMArray.cast(ret).get((short)0)).getShort(); + short keyBlobLength = KMByteBlob.cast(KMArray.cast(ret).get((short)1)).length(); + short keyCharacteristics = KMArray.cast(ret).get((short)2); + short hwParams = KMKeyCharacteristics.cast(keyCharacteristics).getHardwareEnforced(); + short swParams = KMKeyCharacteristics.cast(keyCharacteristics).getSoftwareEnforced(); + Assert.assertEquals(0x9000, response.getSW()); + Assert.assertEquals(error, KMError.OK); + short tag = KMKeyParameters.findTag(KMType.UINT_TAG, KMType.KEYSIZE, hwParams); + Assert.assertEquals(KMInteger.cast(KMIntegerTag.cast(tag).getValue()).getShort(), 256); + tag = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.DIGEST, hwParams); + Assert.assertTrue(KMEnumArrayTag.cast(tag).contains(KMType.DIGEST_NONE)); + tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ALGORITHM, hwParams); + Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.EC); + tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ORIGIN, hwParams); + Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.GENERATED); + cleanUp(); + } + + @Test + public void testHmacGenerateKeySuccess() { + init(); + short arrPtr = KMArray.instance((short)4); + 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.SHA2_256); + short digest = KMEnumArrayTag.instance(KMType.DIGEST, byteBlob); + short minMacLength = KMIntegerTag.instance(KMType.UINT_TAG,KMType.MIN_MAC_LENGTH, KMInteger.uint_16((short)128)); + KMArray.cast(arrPtr).add((short)0, keySize); + KMArray.cast(arrPtr).add((short)1, digest); + KMArray.cast(arrPtr).add((short)2, minMacLength); + KMArray.cast(arrPtr).add((short)3, KMEnumTag.instance(KMType.ALGORITHM, KMType.HMAC)); + short keyParams = KMKeyParameters.instance(arrPtr); + arrPtr = KMArray.instance((short)1); + KMArray arg = KMArray.cast(arrPtr); + arg.add((short) 0, keyParams); + CommandAPDU apdu = encodeApdu((byte)0x10, arrPtr); + // print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(apdu); + 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); + byte[] respBuf = response.getBytes(); + short len = (short) respBuf.length; + ret = decoder.decode(ret, respBuf, (short) 0, len); + short error = KMInteger.cast(KMArray.cast(ret).get((short)0)).getShort(); + short keyBlobLength = KMByteBlob.cast(KMArray.cast(ret).get((short)1)).length(); + short keyCharacteristics = KMArray.cast(ret).get((short)2); + short hwParams = KMKeyCharacteristics.cast(keyCharacteristics).getHardwareEnforced(); + short swParams = KMKeyCharacteristics.cast(keyCharacteristics).getSoftwareEnforced(); + Assert.assertEquals(0x9000, response.getSW()); + Assert.assertEquals(error, KMError.OK); + short tag = KMKeyParameters.findTag(KMType.UINT_TAG, KMType.KEYSIZE, hwParams); + Assert.assertEquals(KMInteger.cast(KMIntegerTag.cast(tag).getValue()).getShort(), 128); + tag = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.DIGEST, hwParams); + Assert.assertTrue(KMEnumArrayTag.cast(tag).contains(KMType.SHA2_256)); + tag = KMKeyParameters.findTag(KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, hwParams); + Assert.assertEquals(KMInteger.cast(KMIntegerTag.cast(tag).getValue()).getShort(), 128); + tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ALGORITHM, hwParams); + Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.HMAC); + tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ORIGIN, hwParams); + Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.GENERATED); + cleanUp(); + } + + @Test + public void testGetKeyCharacteristicsWithIdDataSuccess() { + init(); + byte[] clientId = "clientId".getBytes(); + byte[] appData = "appData".getBytes(); + short ret = generateRsaKey(clientId,appData); + short error = KMInteger.cast(KMArray.cast(ret).get((short)0)).getShort(); + Assert.assertEquals(error, KMError.OK); + short keyBlob = KMArray.cast(ret).get((short)1); + + short arrPtr = KMArray.instance((short)3); + KMArray.cast(arrPtr).add((short)0, keyBlob); + KMArray.cast(arrPtr).add((short)1, KMByteBlob.instance(clientId,(short)0, (short)clientId.length)); + KMArray.cast(arrPtr).add((short)2, KMByteBlob.instance(appData,(short)0, (short)appData.length)); + CommandAPDU apdu = encodeApdu((byte)0x1D, arrPtr); + // print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(apdu); + ret = KMArray.instance((short) 2); + KMArray.cast(ret).add((short) 0, KMInteger.exp()); + short inst = KMKeyCharacteristics.exp(); + KMArray.cast(ret).add((short) 1, inst); + byte[] respBuf = response.getBytes(); + short len = (short) respBuf.length; + ret = decoder.decode(ret, respBuf, (short) 0, len); + error = KMInteger.cast(KMArray.cast(ret).get((short)0)).getShort(); + Assert.assertEquals(error, KMError.OK); + cleanUp(); + } + + @Test + public void testGetKeyCharacteristicsSuccess() { + init(); + short ret = generateRsaKey(null, null); + short error = KMInteger.cast(KMArray.cast(ret).get((short)0)).getShort(); + Assert.assertEquals(error, KMError.OK); + short keyBlob = KMArray.cast(ret).get((short)1); + + short arrPtr = KMArray.instance((short)3); + KMArray.cast(arrPtr).add((short)0, keyBlob); + KMArray.cast(arrPtr).add((short)1, KMByteBlob.instance((short)0)); + KMArray.cast(arrPtr).add((short)2, KMByteBlob.instance((short)0)); + CommandAPDU apdu = encodeApdu((byte)0x1D, arrPtr); + // print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(apdu); + ret = KMArray.instance((short) 2); + KMArray.cast(ret).add((short) 0, KMInteger.exp()); + short inst = KMKeyCharacteristics.exp(); + KMArray.cast(ret).add((short) 1, inst); + byte[] respBuf = response.getBytes(); + short len = (short) respBuf.length; + ret = decoder.decode(ret, respBuf, (short) 0, len); + error = KMInteger.cast(KMArray.cast(ret).get((short)0)).getShort(); + Assert.assertEquals(error, KMError.OK); + cleanUp(); + } +} diff --git a/Applet/JavaCardKeymaster.scr b/Applet/JavaCardKeymaster.scr index b1a669be..6380faa2 100644 --- a/Applet/JavaCardKeymaster.scr +++ b/Applet/JavaCardKeymaster.scr @@ -10,7 +10,7 @@ output on; 0x80 0x23 0x40 0x00 0x3B 0x83 0xA1 0x1A 0x10 0x00 0x00 0x02 0x01 0x00 0x58 0x30 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0A 0x0B 0x0C 0x0D 0x0E 0x0F 0x10 0x11 0x12 0x13 0x14 0x15 0x16 0x17 0x18 0x19 0x1A 0x1B 0x1C 0x1D 0x1E 0x1F 0x20 0x21 0x22 0x23 0x24 0x25 0x26 0x27 0x28 0x29 0x2A 0x2B 0x2C 0x2D 0x2E 0x2F 0x7F; // Send Set Boot Params command - 0x80 0x24 0x40 0x00 0x53 0x86 0x1A 0x30 0x00 0x02 0xC1 0x01 0x1A 0x30 0x00 0x02 0xC2 0x01 0x58 0x20 0x30 0x30 0x30 0x31 0x31 0x31 0x32 0x32 0x32 0x33 0x33 0x33 0x34 0x34 0x34 0x35 0x35 0x35 0x36 0x36 0x36 0x37 0x37 0x37 0x38 0x38 0x38 0x39 0x39 0x39 0x30 0x30 0x58 0x20 0x30 0x30 0x30 0x31 0x31 0x31 0x32 0x32 0x32 0x33 0x33 0x33 0x34 0x34 0x34 0x35 0x35 0x35 0x36 0x36 0x36 0x37 0x37 0x37 0x38 0x38 0x38 0x39 0x39 0x39 0x30 0x30 0x02 0x00 0x7F; + 0x80 0x24 0x40 0x00 0x49 0x86 0x01 0x01 0x58 0x20 0x30 0x30 0x30 0x31 0x31 0x31 0x32 0x32 0x32 0x33 0x33 0x33 0x34 0x34 0x34 0x35 0x35 0x35 0x36 0x36 0x36 0x37 0x37 0x37 0x38 0x38 0x38 0x39 0x39 0x39 0x30 0x30 0x58 0x20 0x30 0x30 0x30 0x31 0x31 0x31 0x32 0x32 0x32 0x33 0x33 0x33 0x34 0x34 0x34 0x35 0x35 0x35 0x36 0x36 0x36 0x37 0x37 0x37 0x38 0x38 0x38 0x39 0x39 0x39 0x30 0x30 0x02 0x00 0x7F; // Send getHardwareInfo command 0x80 0x1E 0x40 0x00 0x00 0x7F; @@ -19,32 +19,32 @@ output on; 0x80 0x18 0x40 0x00 0x23 0x81 0x58 0x20 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0A 0x0B 0x0C 0x0D 0x0E 0x0F 0x10 0x11 0x12 0x13 0x14 0x15 0x16 0x17 0x18 0x19 0x1A 0x1B 0x1C 0x1D 0x1E 0x1F 0x7F; // Generate Key - RSA Key command - 0x80 0x10 0x40 0x00 0x2E 0x81 0xA5 0x1A 0x10 0x00 0x00 0x02 0x01 0x1A 0x30 0x00 0x01 0xF5 0x1A 0x01 0x02 0x03 0x04 0x1A 0x90 0x00 0x02 0x59 0x44 0x54 0x65 0x73 0x74 0x1A 0x50 0x00 0x00 0xC8 0x1A 0x00 0x01 0x00 0x01 0x1A 0x30 0x00 0x00 0x03 0x19 0x08 0x00 0x7F; + 0x80 0x10 0x40 0x00 0x22 0x81 0xA4 0x1A 0x10 0x00 0x00 0x02 0x01 0x1A 0x30 0x00 0x00 0x03 0x19 0x08 0x00 0x1A 0x50 0x00 0x00 0xC8 0x1A 0x00 0x01 0x00 0x01 0x1A 0x20 0x00 0x00 0x01 0x42 0x02 0x03 0x7F; // Generate Key - AES Key command - 0x80 0x10 0x40 0x00 0x2F 0x81 0xA5 0x1A 0x10 0x00 0x00 0x02 0x18 0x20 0x1A 0x30 0x00 0x01 0xF5 0x1A 0x01 0x02 0x03 0x04 0x1A 0x90 0x00 0x02 0x59 0x44 0x54 0x65 0x73 0x74 0x1A 0x50 0x00 0x00 0xC8 0x1A 0x00 0x01 0x00 0x01 0x1A 0x30 0x00 0x00 0x03 0x19 0x01 0x00 0x7F; + 0x80 0x10 0x40 0x00 0x23 0x81 0xA4 0x1A 0x10 0x00 0x00 0x02 0x18 0x20 0x1A 0x30 0x00 0x00 0x03 0x19 0x01 0x00 0x1A 0x50 0x00 0x00 0xC8 0x1A 0x00 0x01 0x00 0x01 0x1A 0x20 0x00 0x00 0x01 0x42 0x02 0x03 0x7F; // Generate Key - ECC Key command - 0x80 0x10 0x40 0x00 0x2E 0x81 0xA5 0x1A 0x10 0x00 0x00 0x02 0x03 0x1A 0x30 0x00 0x01 0xF5 0x1A 0x01 0x02 0x03 0x04 0x1A 0x90 0x00 0x02 0x59 0x44 0x54 0x65 0x73 0x74 0x1A 0x50 0x00 0x00 0xC8 0x1A 0x00 0x01 0x00 0x01 0x1A 0x30 0x00 0x00 0x03 0x19 0x01 0x00 0x7F; + 0x80 0x10 0x40 0x00 0x22 0x81 0xA4 0x1A 0x10 0x00 0x00 0x02 0x03 0x1A 0x30 0x00 0x00 0x03 0x19 0x01 0x00 0x1A 0x50 0x00 0x00 0xC8 0x1A 0x00 0x01 0x00 0x01 0x1A 0x20 0x00 0x00 0x01 0x42 0x02 0x03 0x7F; // Generate Key - DES Key command - 0x80 0x10 0x40 0x00 0x2E 0x81 0xA5 0x1A 0x10 0x00 0x00 0x02 0x18 0x21 0x1A 0x30 0x00 0x01 0xF5 0x1A 0x01 0x02 0x03 0x04 0x1A 0x90 0x00 0x02 0x59 0x44 0x54 0x65 0x73 0x74 0x1A 0x50 0x00 0x00 0xC8 0x1A 0x00 0x01 0x00 0x01 0x1A 0x30 0x00 0x00 0x03 0x18 0xA8 0x7F; + 0x80 0x10 0x40 0x00 0x22 0x81 0xA4 0x1A 0x10 0x00 0x00 0x02 0x18 0x21 0x1A 0x30 0x00 0x00 0x03 0x18 0xA8 0x1A 0x50 0x00 0x00 0xC8 0x1A 0x00 0x01 0x00 0x01 0x1A 0x20 0x00 0x00 0x01 0x42 0x02 0x03 0x7F; // Generate Key - HMAC Key command - 0x80 0x10 0x40 0x00 0x33 0x81 0xA6 0x1A 0x10 0x00 0x00 0x02 0x18 0x80 0x1A 0x30 0x00 0x01 0xF5 0x1A 0x01 0x02 0x03 0x04 0x1A 0x90 0x00 0x02 0x59 0x44 0x54 0x65 0x73 0x74 0x1A 0x30 0x00 0x00 0x08 0x18 0x80 0x1A 0x30 0x00 0x00 0x03 0x18 0x80 0x1A 0x20 0x00 0x00 0x05 0x42 0x02 0x04 0x7F; + 0x80 0x10 0x40 0x00 0x32 0x81 0xA6 0x1A 0x10 0x00 0x00 0x02 0x18 0x80 0x1A 0x30 0x00 0x01 0xF5 0x1A 0x01 0x02 0x03 0x04 0x1A 0x90 0x00 0x02 0x59 0x44 0x54 0x65 0x73 0x74 0x1A 0x30 0x00 0x00 0x08 0x18 0x80 0x1A 0x30 0x00 0x00 0x03 0x18 0x80 0x1A 0x20 0x00 0x00 0x05 0x41 0x04 0x7F; // Import RSA Key - 0x80 0x11 0x40 0x00 0xAE 0x83 0xA4 0x1A 0x10 0x00 0x00 0x02 0x01 0x1A 0x30 0x00 0x01 0xF5 0x1A 0x01 0x02 0x03 0x04 0x1A 0x90 0x00 0x02 0x59 0x44 0x54 0x65 0x73 0x74 0x1A 0x50 0x00 0x00 0xC8 0x1A 0x00 0x01 0x00 0x01 0x03 0x58 0x85 0x82 0x58 0x40 0x1F 0x1E 0xBD 0xB0 0xE0 0x3A 0x42 0xD7 0x7A 0x2C 0xBE 0xB7 0x00 0x7F 0x98 0x3C 0x32 0xC3 0x43 0x80 0x16 0x75 0x09 0xF4 0xAA 0x4E 0x45 0x85 0xE0 0x8B 0x34 0xC3 0x5B 0x3C 0x03 0x4A 0x2D 0x35 0x7D 0xFC 0x31 0x6A 0xB6 0x49 0xB1 0x3F 0x92 0x90 0x21 0x4C 0x99 0x5B 0x8B 0x4E 0xD6 0x00 0x7D 0x01 0x5D 0xEC 0x1F 0x87 0x6E 0x81 0x58 0x40 0x80 0x94 0x2B 0xC3 0x69 0x81 0x10 0xF9 0x66 0x3A 0xB2 0x4D 0x43 0x05 0x03 0xD6 0x79 0x4B 0x25 0xFB 0x30 0x98 0xE0 0x8E 0x5A 0x1A 0xA8 0xC0 0xFE 0x5F 0x0C 0x79 0x66 0x2C 0x0B 0x9D 0xDA 0xA5 0x45 0x4F 0x29 0x2C 0x80 0x9E 0xAF 0xB0 0x0A 0x6C 0xEA 0x5B 0xAE 0x22 0xF2 0x6C 0x37 0xFB 0x69 0x5F 0xED 0xDF 0xDB 0x3F 0x17 0xE5 0x7F; + 0x80 0x11 0x40 0x00 0xB4 0x83 0xA5 0x1A 0x10 0x00 0x00 0x02 0x01 0x1A 0x30 0x00 0x01 0xF5 0x1A 0x01 0x02 0x03 0x04 0x1A 0x90 0x00 0x02 0x59 0x44 0x54 0x65 0x73 0x74 0x1A 0x50 0x00 0x00 0xC8 0x1A 0x00 0x01 0x00 0x01 0x1A 0x70 0x00 0x01 0xF7 0x01 0x03 0x58 0x85 0x82 0x58 0x40 0x80 0x9A 0x87 0x4C 0xE1 0xA0 0x71 0x44 0xAE 0x45 0xDD 0x8D 0x1C 0x05 0xB4 0xD0 0x44 0x23 0xDD 0x42 0xA7 0xC9 0x53 0x44 0xAC 0x31 0x4A 0x22 0x4A 0x02 0x65 0xA0 0xAA 0x21 0xA8 0x30 0x94 0x7D 0x13 0xA1 0xBC 0x89 0x81 0xB5 0x54 0xDE 0x75 0x82 0xB9 0x0B 0x1A 0x7A 0x81 0x0C 0x51 0xE0 0x2F 0x91 0x97 0xD4 0xE8 0x33 0x27 0x61 0x58 0x40 0x92 0x6C 0x79 0x17 0xBB 0x36 0x6F 0xB7 0x58 0x25 0x84 0x98 0xA9 0x56 0x07 0xE6 0x07 0xF6 0x26 0x92 0x15 0xF6 0x21 0x9F 0x6C 0xF0 0xB4 0xE7 0x20 0x42 0xAC 0xB6 0xD8 0x30 0x61 0x06 0xC9 0x3B 0x30 0x67 0x1E 0x8D 0x74 0x11 0x8B 0x06 0x98 0xAB 0x8D 0x6A 0x6C 0xCD 0xB7 0x2F 0xC3 0xA8 0x30 0xC7 0x68 0x03 0x4F 0x72 0xC7 0x5B 0x7F; // Import EC Key - 0x80 0x11 0x40 0x00 0x7D 0x83 0xA4 0x1A 0x10 0x00 0x00 0x02 0x03 0x1A 0x30 0x00 0x01 0xF5 0x1A 0x01 0x02 0x03 0x04 0x1A 0x90 0x00 0x02 0x59 0x44 0x54 0x65 0x73 0x74 0x1A 0x50 0x00 0x00 0xC8 0x1A 0x00 0x01 0x00 0x01 0x03 0x58 0x54 0x83 0x58 0x18 0xA8 0x90 0x6D 0x38 0x3A 0x25 0x67 0x08 0x85 0xD3 0x07 0x17 0x3A 0x7C 0x0E 0x81 0x96 0x46 0xA0 0xDA 0x2C 0xBD 0xD2 0xEA 0x58 0x31 0x04 0x95 0xCE 0xB7 0x75 0x21 0xD8 0xAE 0xC9 0xA2 0x99 0xC0 0x00 0x6C 0x2E 0xC2 0x11 0x7D 0x79 0x52 0x56 0xB7 0x3D 0xC0 0xC8 0xE7 0x07 0x6D 0xD1 0xBD 0xCD 0xF6 0x05 0xD3 0xB3 0xF4 0xD1 0x56 0x86 0x90 0xED 0xD3 0x4F 0xA2 0x85 0xC2 0x3B 0xCE 0x45 0x1A 0x10 0x00 0x00 0x0A 0x01 0x7F; + 0x80 0x11 0x40 0x00 0x7D 0x83 0xA4 0x1A 0x10 0x00 0x00 0x02 0x03 0x1A 0x30 0x00 0x01 0xF5 0x1A 0x01 0x02 0x03 0x04 0x1A 0x90 0x00 0x02 0x59 0x44 0x54 0x65 0x73 0x74 0x1A 0x50 0x00 0x00 0xC8 0x1A 0x00 0x01 0x00 0x01 0x03 0x58 0x54 0x83 0x58 0x18 0xA6 0x68 0xDE 0xEC 0x65 0x6C 0xFB 0xEE 0xAA 0x43 0xEF 0x97 0x9D 0x10 0x82 0xF0 0x99 0x5F 0x10 0xF3 0xEE 0x9C 0x38 0x57 0x58 0x31 0x04 0x3A 0xF8 0xF4 0xFA 0x1F 0xE4 0x4D 0x62 0xA1 0xCD 0x26 0x8E 0x1A 0x5A 0xAA 0xF5 0xA8 0x94 0xE3 0x8B 0x4C 0xCE 0x49 0xA1 0x57 0x25 0x81 0x6D 0xBE 0x5C 0x3B 0x07 0x95 0xB6 0x89 0x24 0x6E 0x9D 0x25 0x22 0xE6 0x5F 0x41 0xCC 0x59 0xCE 0x25 0x0C 0x1A 0x10 0x00 0x00 0x0A 0x01 0x7F; // Import AES Key - 0x80 0x11 0x40 0x00 0x41 0x83 0xA5 0x1A 0x10 0x00 0x00 0x02 0x18 0x20 0x1A 0x30 0x00 0x01 0xF5 0x1A 0x01 0x02 0x03 0x04 0x1A 0x90 0x00 0x02 0x59 0x44 0x54 0x65 0x73 0x74 0x1A 0x30 0x00 0x00 0x03 0x19 0x01 0x00 0x1A 0x20 0x00 0x00 0x05 0x42 0x02 0x04 0x03 0x52 0x81 0x50 0xF6 0x16 0x03 0x1C 0x26 0xB5 0x6F 0x2E 0x42 0xCB 0x7C 0x37 0x96 0x1C 0x27 0xBA 0x7F; + 0x80 0x11 0x40 0x00 0x3F 0x83 0xA5 0x1A 0x10 0x00 0x00 0x02 0x18 0x20 0x1A 0x30 0x00 0x01 0xF5 0x1A 0x01 0x02 0x03 0x04 0x1A 0x90 0x00 0x02 0x59 0x44 0x54 0x65 0x73 0x74 0x1A 0x30 0x00 0x00 0x03 0x18 0x80 0x1A 0x20 0x00 0x00 0x05 0x41 0x04 0x03 0x52 0x81 0x50 0x95 0xE6 0x79 0x36 0x64 0xA5 0xEC 0x72 0xBF 0x01 0x4C 0x83 0x6C 0xCD 0xCF 0x51 0x7F; // Import Hmac Key - 0x80 0x11 0x40 0x00 0x47 0x83 0xA6 0x1A 0x10 0x00 0x00 0x02 0x18 0x80 0x1A 0x30 0x00 0x01 0xF5 0x1A 0x01 0x02 0x03 0x04 0x1A 0x90 0x00 0x02 0x59 0x44 0x54 0x65 0x73 0x74 0x1A 0x30 0x00 0x00 0x03 0x18 0x80 0x1A 0x20 0x00 0x00 0x05 0x42 0x02 0x04 0x1A 0x30 0x00 0x00 0x08 0x18 0x80 0x03 0x52 0x81 0x50 0xA8 0x66 0xA3 0xE5 0xBA 0x53 0xE9 0x93 0x00 0xE9 0x1A 0xEC 0xBF 0x8F 0xEC 0x90 0x7F; + 0x80 0x11 0x40 0x00 0x46 0x83 0xA6 0x1A 0x10 0x00 0x00 0x02 0x18 0x80 0x1A 0x30 0x00 0x01 0xF5 0x1A 0x01 0x02 0x03 0x04 0x1A 0x90 0x00 0x02 0x59 0x44 0x54 0x65 0x73 0x74 0x1A 0x30 0x00 0x00 0x03 0x18 0x80 0x1A 0x20 0x00 0x00 0x05 0x41 0x04 0x1A 0x30 0x00 0x00 0x08 0x18 0x80 0x03 0x52 0x81 0x50 0xFC 0xA6 0x8F 0x58 0x68 0x93 0xDE 0xD0 0xC0 0x74 0x1C 0x6F 0x1D 0x39 0x2E 0x4A 0x7F; // Import Des Key - 0x80 0x11 0x40 0x00 0x40 0x83 0xA5 0x1A 0x10 0x00 0x00 0x02 0x18 0x21 0x1A 0x30 0x00 0x01 0xF5 0x1A 0x01 0x02 0x03 0x04 0x1A 0x90 0x00 0x02 0x59 0x44 0x54 0x65 0x73 0x74 0x1A 0x30 0x00 0x00 0x03 0x18 0xA8 0x1A 0x20 0x00 0x00 0x05 0x42 0x02 0x04 0x03 0x52 0x81 0x50 0x80 0x5B 0x6F 0xCB 0xBB 0x1B 0x07 0xD3 0xB6 0xE1 0x67 0xAB 0x51 0xC6 0x34 0x29 0x7F; + 0x80 0x11 0x40 0x00 0x3F 0x83 0xA5 0x1A 0x10 0x00 0x00 0x02 0x18 0x21 0x1A 0x30 0x00 0x01 0xF5 0x1A 0x01 0x02 0x03 0x04 0x1A 0x90 0x00 0x02 0x59 0x44 0x54 0x65 0x73 0x74 0x1A 0x30 0x00 0x00 0x03 0x18 0xA8 0x1A 0x20 0x00 0x00 0x05 0x41 0x04 0x03 0x52 0x81 0x50 0x8B 0xD4 0xD5 0x84 0x37 0x39 0xC0 0x1B 0xDB 0xED 0x3C 0x68 0x99 0x3A 0xDC 0x3D 0x7F; diff --git a/Applet/build.xml b/Applet/build.xml index 17b0a546..41181873 100644 --- a/Applet/build.xml +++ b/Applet/build.xml @@ -4,6 +4,8 @@ + + @@ -60,7 +62,7 @@ - + + + + + + + + @@ -130,13 +146,22 @@ + + + + + + + + + diff --git a/Applet/default.output b/Applet/default.output index 3763ffa7..465ff419 100644 --- a/Applet/default.output +++ b/Applet/default.output @@ -5,16 +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: 53, 86, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 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: 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: 2e, 81, 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, 30, 00, 00, 03, 19, 08, 00, Le: 11, 83, 00, 58, d6, 85, 58, 40, 11, 8c, a2, 01, 8d, 5d, d0, d5, 79, dc, fc, 71, a5, a0, ea, fc, e5, 11, 32, e7, 8a, bd, d1, 5c, a0, d8, 5f, 01, 8e, 4d, b1, f0, 09, 79, 5c, b9, e3, 26, 09, 26, dc, 7c, 21, b4, ee, 9b, 28, aa, c2, 73, 0c, ca, e4, 9b, 29, 5d, a0, f4, aa, 87, 7a, 51, 31, 7a, 4c, 62, 92, 2f, 4c, 7d, da, 2a, 3d, 6d, 92, d2, 0a, 4c, 63, 8e, 40, 1f, 03, a1, b1, 1c, 32, 52, 22, 73, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a6, 1a, 10, 00, 00, 02, 01, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 30, 00, 00, 03, 19, 08, 00, 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, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a6, 1a, 10, 00, 00, 02, 01, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 30, 00, 00, 03, 19, 08, 00, 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: 2f, 81, 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, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 30, 00, 00, 03, 19, 01, 00, Le: a0, 83, 00, 58, 64, 84, 50, 4d, eb, 33, dc, 4d, 36, 74, 3a, 38, d4, b6, 1d, 0c, 48, 70, a6, 4c, fa, 0f, 3b, 66, 89, 7a, 20, 81, 20, c6, f5, f3, 4c, 14, cc, a8, 4b, 27, 04, 49, a0, 4f, 95, 93, 07, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a6, 1a, 10, 00, 00, 02, 18, 20, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 30, 00, 00, 03, 19, 01, 00, 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, a6, 1a, 10, 00, 00, 02, 18, 20, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 30, 00, 00, 03, 19, 01, 00, 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: 2e, 81, a5, 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, 1a, 30, 00, 00, 03, 19, 01, 00, Le: da, 83, 00, 58, 9f, 85, 58, 18, a7, 1b, 67, ff, 9e, ef, 1a, 20, 88, ce, dc, 29, 49, f1, 69, 0a, c7, 99, 3f, 68, cf, c1, 12, 2a, 4c, ee, 2c, 52, c6, 4d, 6f, e7, 9e, 60, 6c, 96, 50, 4c, 86, 14, 3a, 79, 7a, c6, 2e, cb, 70, 33, 83, c2, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a6, 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, 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, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a6, 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, 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: 2e, 81, 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, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 30, 00, 00, 03, 18, a8, Le: 9e, 83, 00, 58, 63, 84, 50, 6d, fd, 69, 01, 75, 25, d1, 54, 24, f1, 2c, c2, 6e, 6d, 8c, d3, 4c, e6, a1, 6a, dd, 67, 55, 62, df, f2, ed, 1c, e9, 4c, 42, ef, f8, a4, 56, 52, 52, 03, 73, d7, d7, f2, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a6, 1a, 10, 00, 00, 02, 18, 21, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 30, 00, 00, 03, 18, a8, 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, a6, 1a, 10, 00, 00, 02, 18, 21, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 30, 00, 00, 03, 18, a8, 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: 33, 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, 42, 02, 04, Le: a8, 83, 00, 58, 68, 84, 50, af, ee, 95, 3a, d3, f1, 6a, 0a, e6, 88, 30, 63, fa, 8d, e5, 27, 4c, de, af, d7, 29, c4, 08, 59, 9d, 3b, 55, 59, 4e, 4c, 11, 1f, 0b, 47, 4e, a9, e5, d5, 74, 5a, 14, 20, 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, 42, 02, 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, 42, 02, 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: ae, 83, a4, 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, 03, 58, 85, 82, 58, 40, 1f, 1e, bd, b0, e0, 3a, 42, d7, 7a, 2c, be, b7, 00, 7f, 98, 3c, 32, c3, 43, 80, 16, 75, 09, f4, aa, 4e, 45, 85, e0, 8b, 34, c3, 5b, 3c, 03, 4a, 2d, 35, 7d, fc, 31, 6a, b6, 49, b1, 3f, 92, 90, 21, 4c, 99, 5b, 8b, 4e, d6, 00, 7d, 01, 5d, ec, 1f, 87, 6e, 81, 58, 40, 80, 94, 2b, c3, 69, 81, 10, f9, 66, 3a, b2, 4d, 43, 05, 03, d6, 79, 4b, 25, fb, 30, 98, e0, 8e, 5a, 1a, a8, c0, fe, 5f, 0c, 79, 66, 2c, 0b, 9d, da, a5, 45, 4f, 29, 2c, 80, 9e, af, b0, 0a, 6c, ea, 5b, ae, 22, f2, 6c, 37, fb, 69, 5f, ed, df, db, 3f, 17, e5, Le: 11, 83, 00, 58, d6, 85, 58, 40, 3b, 5a, c7, 40, 44, 8e, b4, 9b, 73, 68, 11, bc, d7, 6b, 14, fb, 2f, 76, ea, 91, d1, 92, ba, 6c, 70, ce, 13, 5d, f9, 96, b0, 17, 6d, 4a, 6d, 16, fc, 60, 17, cd, 08, 8a, 47, 3a, a9, 7b, e5, 36, 70, 87, 2f, af, 65, cd, 9e, bc, 62, c2, a0, fe, 47, 6b, fa, 73, 4c, f4, b4, 76, af, 25, 7d, eb, 88, 6e, ca, f9, b8, 4c, a4, b9, a1, 20, 29, 43, 74, ac, 06, 8f, 61, 62, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a6, 1a, 10, 00, 00, 02, 01, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 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, 80, 94, 2b, c3, 69, 81, 10, f9, 66, 3a, b2, 4d, 43, 05, 03, d6, 79, 4b, 25, fb, 30, 98, e0, 8e, 5a, 1a, a8, c0, fe, 5f, 0c, 79, 66, 2c, 0b, 9d, da, a5, 45, 4f, 29, 2c, 80, 9e, af, b0, 0a, 6c, ea, 5b, ae, 22, f2, 6c, 37, fb, 69, 5f, ed, df, db, 3f, 17, e5, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a6, 1a, 10, 00, 00, 02, 01, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 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, a8, 90, 6d, 38, 3a, 25, 67, 08, 85, d3, 07, 17, 3a, 7c, 0e, 81, 96, 46, a0, da, 2c, bd, d2, ea, 58, 31, 04, 95, ce, b7, 75, 21, d8, ae, c9, a2, 99, c0, 00, 6c, 2e, c2, 11, 7d, 79, 52, 56, b7, 3d, c0, c8, e7, 07, 6d, d1, bd, cd, f6, 05, d3, b3, f4, d1, 56, 86, 90, ed, d3, 4f, a2, 85, c2, 3b, ce, 45, 1a, 10, 00, 00, 0a, 01, Le: e6, 83, 00, 58, a5, 85, 58, 18, e7, 0d, ce, 40, c4, 39, a6, 32, 09, 23, 63, 1d, cf, 03, 2c, c3, b4, 84, 8a, 1e, ab, d6, 3b, ab, 4c, 6b, 1d, 1e, 6d, 24, 79, 22, 9a, fe, 81, 37, 7c, 4c, 0f, 4a, 14, 9a, 91, 31, 39, 94, ea, a5, 2d, 14, 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, 95, ce, b7, 75, 21, d8, ae, c9, a2, 99, c0, 00, 6c, 2e, c2, 11, 7d, 79, 52, 56, b7, 3d, c0, c8, e7, 07, 6d, d1, bd, cd, f6, 05, d3, b3, f4, d1, 56, 86, 90, ed, d3, 4f, a2, 85, c2, 3b, ce, 45, 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: 41, 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, 19, 01, 00, 1a, 20, 00, 00, 05, 42, 02, 04, 03, 52, 81, 50, f6, 16, 03, 1c, 26, b5, 6f, 2e, 42, cb, 7c, 37, 96, 1c, 27, ba, Le: 9c, 83, 00, 58, 62, 84, 50, b8, 4e, b2, a0, 85, 11, 94, 9a, e3, f6, 13, fe, f9, a1, b3, dd, 4c, f6, 10, 1c, 18, 2b, 4f, 80, 8d, e3, 76, ed, 92, 4c, 41, ab, d8, 54, 0c, 54, 18, 01, 33, 45, 09, 1e, 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, 19, 01, 00, 1a, 20, 00, 00, 05, 42, 02, 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, 19, 01, 00, 1a, 20, 00, 00, 05, 42, 02, 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: 47, 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, 42, 02, 04, 1a, 30, 00, 00, 08, 18, 80, 03, 52, 81, 50, a8, 66, a3, e5, ba, 53, e9, 93, 00, e9, 1a, ec, bf, 8f, ec, 90, Le: a8, 83, 00, 58, 68, 84, 50, 63, 2d, d8, 58, c8, 5b, 66, 98, da, de, 51, 5f, e5, c8, 90, 99, 4c, 8a, 7e, 7c, 1e, 4c, ba, 19, e5, 66, d5, 27, 2d, 4c, 3d, ed, 1d, 79, c8, d8, b7, 3b, 93, 1e, fd, ad, 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, 42, 02, 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, 42, 02, 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: 40, 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, 42, 02, 04, 03, 52, 81, 50, 80, 5b, 6f, cb, bb, 1b, 07, d3, b6, e1, 67, ab, 51, c6, 34, 29, Le: 9a, 83, 00, 58, 61, 84, 50, c0, ec, 75, 64, dd, a9, 3f, 81, 56, aa, 95, c5, 60, c7, 7f, 5b, 4c, ac, 0a, 0f, 27, 80, f4, 2e, 25, 76, 68, 62, 21, 4c, ae, c2, 77, b3, f7, 43, e6, f5, 19, ac, 77, 12, 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, 42, 02, 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, 42, 02, 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: 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 From e8a1546fa8e6fa5b3daa6ba8371cb670fe74e699 Mon Sep 17 00:00:00 2001 From: cpathak Date: Tue, 9 Jun 2020 10:03:47 -0700 Subject: [PATCH 2/2] Removed untested commands from KeymasterApplet Removed untested code from KeymasterApplet. --- .../javacard/keymaster/KMKeymasterApplet.java | 1374 +---------------- 1 file changed, 2 insertions(+), 1372 deletions(-) diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java b/Applet/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java index e8b102dd..bfd769df 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java @@ -506,25 +506,6 @@ private void processGetKeyCharacteristicsCmd(APDU apdu) { } private void processGetHmacSharingParamCmd(APDU apdu) { - // No Arguments - byte[] scratchPad = apdu.getBuffer(); - // Create blob containing seed - tmpVariables[0] = - KMByteBlob.instance(repository.getHmacSeed(), (short) 0, repository.HMAC_SEED_NONCE_SIZE); - // Create blob containing nonce - cryptoProvider.newRandomNumber(scratchPad, (short) 0, repository.HMAC_SEED_NONCE_SIZE); - tmpVariables[1] = KMByteBlob.instance(scratchPad, (short) 0, repository.HMAC_SEED_NONCE_SIZE); - // Create HMAC Sharing Parameters - tmpVariables[2] = KMHmacSharingParameters.instance(); - KMHmacSharingParameters.cast(tmpVariables[2]).setNonce(tmpVariables[1]); - KMHmacSharingParameters.cast(tmpVariables[2]).setSeed(tmpVariables[0]); - // prepare the response - tmpVariables[3] = KMArray.instance((short) 2); - KMArray.cast(tmpVariables[3]).add((short) 0, KMInteger.uint_16(KMError.OK)); - KMArray.cast(tmpVariables[3]).add((short) 1, tmpVariables[2]); - // Encode the response - bufferLength = encoder.encode(tmpVariables[0], buffer, bufferStartOffset); - sendOutgoing(apdu); } private void processDeleteAllKeysCmd(APDU apdu) { @@ -575,303 +556,15 @@ private void processDeleteKeyCmd(APDU apdu) { } private void processComputeSharedHmacCmd(APDU apdu) { - // Receive the incoming request fully from the master into buffer. - receiveIncoming(apdu); - byte[] scratchPad = apdu.getBuffer(); - tmpVariables[1] = KMArray.instance((short) 1); - tmpVariables[2] = KMKeyParameters.exp(); - tmpVariables[3] = KMHmacSharingParameters.exp(); - KMArray.cast(tmpVariables[1]).add((short) 0, KMArray.exp(tmpVariables[3])); // Vector - KMArray.cast(tmpVariables[1]).add((short) 1, tmpVariables[2]); // Key Params - // Decode the arguments - tmpVariables[2] = decoder.decode(tmpVariables[1], buffer, bufferStartOffset, bufferLength); - data[KEY_PARAMETERS] = KMArray.cast(tmpVariables[2]).get((short) 1); - data[HMAC_SHARING_PARAMS] = KMArray.cast(tmpVariables[2]).get((short) 0); - // Concatenate HMAC Params - tmpVariables[0] = 0; - tmpVariables[1] = KMArray.cast(data[HMAC_SHARING_PARAMS]).length(); - tmpVariables[5] = 0; // index in scratchPad - while (tmpVariables[0] < tmpVariables[1]) { - // read HmacSharingParam - tmpVariables[2] = KMArray.cast(data[HMAC_SHARING_PARAMS]).get(tmpVariables[0]); - // get seed - tmpVariables[3] = KMHmacSharingParameters.cast(tmpVariables[2]).getSeed(); - tmpVariables[4] = KMByteBlob.cast(tmpVariables[3]).length(); - // if seed is present - if (tmpVariables[4] == HMAC_SEED_SIZE /*32*/) { - // then copy that to scratchPad - Util.arrayCopyNonAtomic( - KMByteBlob.cast(tmpVariables[3]).getBuffer(), - KMByteBlob.cast(tmpVariables[3]).getStartOff(), - scratchPad, - tmpVariables[5], - tmpVariables[4]); - tmpVariables[5] += tmpVariables[4]; - } - // get nonce - tmpVariables[3] = KMHmacSharingParameters.cast(tmpVariables[2]).getNonce(); - tmpVariables[4] = KMByteBlob.cast(tmpVariables[3]).length(); - // if nonce is not present - if (tmpVariables[4] != HMAC_NONCE_SIZE /*32*/) { - KMException.throwIt(KMError.INVALID_ARGUMENT); - } - // copy nonce to scratchPad - Util.arrayCopyNonAtomic( - KMByteBlob.cast(tmpVariables[3]).getBuffer(), - KMByteBlob.cast(tmpVariables[3]).getStartOff(), - scratchPad, - tmpVariables[5], - tmpVariables[4]); - tmpVariables[5] += tmpVariables[4]; - } - // ckdf to derive hmac key - HMACKey key = - cryptoProvider.cmacKdf( - repository.getHmacKey(), ckdfLable, scratchPad, (short) 0, tmpVariables[5]); - tmpVariables[5] = key.getKey(scratchPad, (short) 0); - repository.initComputedHmac(scratchPad, (short) 0, tmpVariables[5]); - // Generate sharingKey verification - tmpVariables[5] = - cryptoProvider.hmacSign( - key, sharingCheck, (short) 0, (short) sharingCheck.length, scratchPad, (short) 0); - tmpVariables[1] = KMByteBlob.instance(scratchPad, (short) 0, tmpVariables[5]); - // prepare the 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, tmpVariables[1]); - // Encode the response - bufferLength = encoder.encode(tmpVariables[0], buffer, bufferStartOffset); - sendOutgoing(apdu); } private void processUpgradeKeyCmd(APDU apdu) { - // Receive the incoming request fully from the master into buffer. - receiveIncoming(apdu); - byte[] scratchPad = apdu.getBuffer(); - tmpVariables[1] = KMArray.instance((short) 2); - tmpVariables[2] = KMKeyParameters.exp(); - KMArray.cast(tmpVariables[1]).add((short) 0, KMByteBlob.exp()); // Key Blob - KMArray.cast(tmpVariables[1]).add((short) 1, tmpVariables[2]); // Key Params - // Decode the arguments - tmpVariables[2] = decoder.decode(tmpVariables[1], buffer, bufferStartOffset, bufferLength); - data[KEY_BLOB] = KMArray.cast(tmpVariables[2]).get((short) 0); - data[KEY_PARAMETERS] = KMArray.cast(tmpVariables[2]).get((short) 1); - tmpVariables[0] = - KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.APPLICATION_ID, data[KEY_PARAMETERS]); - if (tmpVariables[0] != KMTag.INVALID_VALUE) { - data[APP_ID] = KMByteTag.cast(tmpVariables[0]).getValue(); - } - tmpVariables[0] = - KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.APPLICATION_DATA, data[KEY_PARAMETERS]); - if (tmpVariables[0] != KMTag.INVALID_VALUE) { - data[APP_DATA] = KMByteTag.cast(tmpVariables[0]).getValue(); - } - // parse existing key blob - parseEncryptedKeyBlob(scratchPad); - // validate characteristics to be upgraded. - 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 - Util.arrayFillNonAtomic(scratchPad, (short) 0, tmpVariables[0], (byte) 0); - // If the os version is not zero - if (Util.arrayCompare( - repository.osVersion, (short) 0, scratchPad, (short) 0, tmpVariables[0]) - != 0) { - KMException.throwIt(KMError.INVALID_ARGUMENT); - } - } - } - 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]) - < 0) { - // If the key characteristics has os patch level > current os patch - KMException.throwIt(KMError.INVALID_ARGUMENT); - } - } - // remove Auth Tag - repository.removeAuthTag(data[AUTH_TAG]); - // copy origin - data[ORIGIN] = KMEnumTag.getValue(KMType.ORIGIN, data[HW_PARAMETERS]); - // create new key blob with current os version etc. - createEncryptedKeyBlob(scratchPad); - // persist new 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 processExportKeyCmd(APDU apdu) { - sendError(apdu, KMError.UNIMPLEMENTED); } private void processImportWrappedKeyCmd(APDU apdu) { - // Currently only RAW formatted import key blob are supported - 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) 11); - // Arguments - tmpVariables[2] = KMKeyParameters.exp(); - KMArray.cast(tmpVariables[1]).add((short) 0, tmpVariables[2]); // Key Params - KMArray.cast(tmpVariables[1]).add((short) 1, KMEnum.instance(KMType.KEY_FORMAT)); // Key Format - KMArray.cast(tmpVariables[1]).add((short) 2, KMByteBlob.exp()); // Wrapped Import Key Blob - KMArray.cast(tmpVariables[1]).add((short) 3, KMByteBlob.exp()); // Auth Tag - KMArray.cast(tmpVariables[1]).add((short) 4, KMByteBlob.exp()); // IV - Nonce - KMArray.cast(tmpVariables[1]).add((short) 5, KMByteBlob.exp()); // Encrypted Transport Key - KMArray.cast(tmpVariables[1]).add((short) 6, KMByteBlob.exp()); // Wrapping Key KeyBlob - KMArray.cast(tmpVariables[1]).add((short) 7, KMByteBlob.exp()); // Masking Key - KMArray.cast(tmpVariables[1]).add((short) 8, tmpVariables[2]); // Un-wrapping Params - KMArray.cast(tmpVariables[1]).add((short) 9, KMInteger.exp()); // Password Sid - KMArray.cast(tmpVariables[1]).add((short) 10, KMInteger.exp()); // Biometric Sid - // Decode the arguments - tmpVariables[2] = decoder.decode(tmpVariables[1], buffer, bufferStartOffset, bufferLength); - tmpVariables[3] = KMArray.cast(tmpVariables[2]).get((short) 0); - // get algorithm - tmpVariables[3] = KMEnumTag.getValue(KMType.ALGORITHM, tmpVariables[3]); - if (tmpVariables[3] == KMType.INVALID_VALUE) { - KMException.throwIt(KMError.INVALID_ARGUMENT); - } - tmpVariables[3] = KMEnumTag.getValue(KMType.ALGORITHM, data[KEY_PARAMETERS]); - if (tmpVariables[3] == KMType.RSA - || tmpVariables[3] == KMType.EC) { // RSA and EC not implemented - KMException.throwIt(KMError.UNIMPLEMENTED); - } - // Key format must be RAW format - X509 and PKCS8 not implemented. - tmpVariables[3] = KMArray.cast(tmpVariables[2]).get((short) 1); - tmpVariables[3] = KMEnum.cast(tmpVariables[3]).getVal(); - if (tmpVariables[3] != KMType.RAW) { - KMException.throwIt(KMError.UNIMPLEMENTED); - } - data[AUTH_DATA] = KMArray.cast(tmpVariables[2]).get((short) 3); - data[AUTH_TAG] = KMArray.cast(tmpVariables[2]).get((short) 4); - data[NONCE] = KMArray.cast(tmpVariables[2]).get((short) 5); - data[ENC_TRANSPORT_KEY] = KMArray.cast(tmpVariables[2]).get((short) 6); - data[MASKING_KEY] = KMArray.cast(tmpVariables[2]).get((short) 8); - // Step 1 - parse wrapping key blob - data[KEY_PARAMETERS] = KMArray.cast(tmpVariables[2]).get((short) 9); // wrapping key parameters - // Check for app id and app data. - data[APP_ID] = KMType.INVALID_VALUE; - data[APP_DATA] = KMType.INVALID_VALUE; - tmpVariables[3] = - KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.APPLICATION_ID, data[KEY_PARAMETERS]); - if (tmpVariables[3] != KMTag.INVALID_VALUE) { - data[APP_ID] = KMByteTag.cast(tmpVariables[3]).getValue(); - } - tmpVariables[3] = - KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.APPLICATION_DATA, data[KEY_PARAMETERS]); - if (tmpVariables[3] != KMTag.INVALID_VALUE) { - data[APP_DATA] = KMByteTag.cast(tmpVariables[3]).getValue(); - } - // wrapping key blob - data[KEY_BLOB] = KMArray.cast(tmpVariables[2]).get((short) 7); - parseEncryptedKeyBlob(scratchPad); - - // Step 2 - Decrypt the encrypted transport key - // enforce authorization for WRAP_KEY operation using RSA algorithm according to javacard caps. - if (KMEnumTag.getValue(KMType.ALGORITHM, data[HW_PARAMETERS]) != KMType.RSA) { - KMException.throwIt(KMError.INCOMPATIBLE_ALGORITHM); - } - if (!(KMEnumArrayTag.contains(KMType.DIGEST, KMType.SHA2_256, data[HW_PARAMETERS]))) { - KMException.throwIt(KMError.INCOMPATIBLE_DIGEST); - } - if (!(KMEnumArrayTag.contains(KMType.PADDING, KMType.RSA_OAEP, data[HW_PARAMETERS]))) { - KMException.throwIt(KMError.INCOMPATIBLE_PADDING_MODE); - } - KMCipher cipher = - cryptoProvider.createRsaDecrypt( - KMCipher.CIPHER_RSA, - KMCipher.PAD_PKCS1_OAEP_SHA256, - KMByteBlob.cast(data[SECRET]).getBuffer(), - KMByteBlob.cast(data[SECRET]).getStartOff(), - KMByteBlob.cast(data[SECRET]).length(), - KMByteBlob.cast(data[PUB_KEY]).getBuffer(), - KMByteBlob.cast(data[PUB_KEY]).getStartOff(), - KMByteBlob.cast(data[PUB_KEY]).length()); - // Decrypt the transport key - tmpVariables[3] = - cipher.doFinal( - KMByteBlob.cast(data[ENC_TRANSPORT_KEY]).getBuffer(), - KMByteBlob.cast(data[ENC_TRANSPORT_KEY]).getStartOff(), - KMByteBlob.cast(data[ENC_TRANSPORT_KEY]).length(), - scratchPad, - (short) 0); - data[SECRET] = KMByteBlob.instance(scratchPad, (short) 0, tmpVariables[3]); - cryptoProvider.delete(cipher); - - // Step 3 - XOR with masking key - tmpVariables[4] = KMByteBlob.cast(data[MASKING_KEY]).length(); - if (tmpVariables[3] != tmpVariables[4]) { - KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH); - } - tmpVariables[3] = 0; // index in scratchPad - byte[] buf = KMByteBlob.cast(MASKING_KEY).getBuffer(); - tmpVariables[5] = KMByteBlob.cast(MASKING_KEY).getStartOff(); - while (tmpVariables[3] < tmpVariables[4]) { - scratchPad[tmpVariables[3]] = - (byte) (scratchPad[tmpVariables[3]] ^ buf[(short) (tmpVariables[3] + tmpVariables[5])]); - scratchPad[3]++; - } - data[SECRET] = KMByteBlob.instance(scratchPad, (short) 0, tmpVariables[3]); - - // Step 4 - AES-GCM decrypt - data[IMPORTED_KEY_BLOB] = KMArray.cast(tmpVariables[2]).get((short) 2); - data[AUTH_DATA] = KMArray.cast(tmpVariables[2]).get((short) 3); - data[AUTH_TAG] = KMArray.cast(tmpVariables[2]).get((short) 4); - data[NONCE] = KMArray.cast(tmpVariables[2]).get((short) 5); - data[ENC_TRANSPORT_KEY] = KMArray.cast(tmpVariables[2]).get((short) 6); - data[MASKING_KEY] = KMArray.cast(tmpVariables[2]).get((short) 8); - AESKey key = - cryptoProvider.createAESKey( - KMByteBlob.cast(data[SECRET]).getBuffer(), - KMByteBlob.cast(data[SECRET]).getStartOff(), - KMByteBlob.cast(data[SECRET]).length()); - boolean verification = - cryptoProvider.aesGCMDecrypt( - key, - KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).getBuffer(), - KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).getStartOff(), - KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).length(), - scratchPad, - (short) 0, - KMByteBlob.cast(data[NONCE]).getBuffer(), - KMByteBlob.cast(data[NONCE]).getStartOff(), - KMByteBlob.cast(data[NONCE]).length(), - KMByteBlob.cast(data[AUTH_DATA]).getBuffer(), - KMByteBlob.cast(data[AUTH_DATA]).getStartOff(), - KMByteBlob.cast(data[AUTH_DATA]).length(), - KMByteBlob.cast(data[AUTH_TAG]).getBuffer(), - KMByteBlob.cast(data[AUTH_TAG]).getStartOff(), - KMByteBlob.cast(data[AUTH_TAG]).length()); - if (verification == false) { - KMException.throwIt(KMError.IMPORTED_KEY_VERIFICATION_FAILED); - } - cryptoProvider.delete(key); - - // Step 5 - Import Decrypted Key. - data[ORIGIN] = KMType.SECURELY_IMPORTED; - data[KEY_PARAMETERS] = KMArray.cast(tmpVariables[2]).get((short) 0); - importKey(apdu, scratchPad); } private void processAttestKeyCmd(APDU apdu) {} @@ -886,1062 +579,15 @@ private void processAbortOperationCmd(APDU apdu) {} private void processFinishOperationCmd(APDU apdu) { // TODO AES GCM - receiveIncoming(apdu); - byte[] scratchPad = apdu.getBuffer(); - Util.arrayFill(scratchPad, (short)0,(short)256, (byte)0); - tmpVariables[1] = KMArray.instance((short) 6); - // Arguments - tmpVariables[2] = KMKeyParameters.exp(); - KMArray.cast(tmpVariables[1]).add((short) 0, KMInteger.exp()); - KMArray.cast(tmpVariables[1]).add((short) 1, tmpVariables[2]); - KMArray.cast(tmpVariables[1]).add((short) 2, KMByteBlob.exp()); - KMArray.cast(tmpVariables[1]).add((short) 3, KMByteBlob.exp()); - tmpVariables[3] = KMHardwareAuthToken.exp(); - KMArray.cast(tmpVariables[1]).add((short) 4, tmpVariables[3]); - tmpVariables[4] = KMVerificationToken.exp(); - KMArray.cast(tmpVariables[1]).add((short) 5, tmpVariables[4]); - // Decode the arguments - tmpVariables[2] = decoder.decode(tmpVariables[1], buffer, bufferStartOffset, bufferLength); - data[OP_HANDLE] = KMArray.cast(tmpVariables[2]).get((short) 0); - data[KEY_PARAMETERS] = KMArray.cast(tmpVariables[2]).get((short) 1); - data[INPUT_DATA] = KMArray.cast(tmpVariables[2]).get((short) 2); - data[HW_TOKEN] = KMArray.cast(tmpVariables[2]).get((short) 4); - data[VERIFICATION_TOKEN] = KMArray.cast(tmpVariables[2]).get((short) 5); - // Check Operation Handle - tmpVariables[1] = KMInteger.cast(data[OP_HANDLE]).getShort(); - KMOperationState op = repository.findOperation(tmpVariables[1]); - if (KMInteger.compare(data[OP_HANDLE], KMInteger.uint_16(op.getHandle())) != 0) { - KMException.throwIt(KMError.INVALID_OPERATION_HANDLE); - } - //Authorize the final operation - authorizeUpdateFinalOperation(op, scratchPad); - short len = 0; - // If the operation is signing - if(op.getPurpose() == KMType.SIGN){ - // Perform trusted confirmation if required - if (op.isTrustedConfirmationRequired()) { - tmpVariables[0] = KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.CONFIRMATION_TOKEN, data[KEY_PARAMETERS]); - if(tmpVariables[0] == KMType.INVALID_VALUE){ - KMException.throwIt(KMError.INVALID_ARGUMENT); - } - tmpVariables[0] = KMByteTag.cast(tmpVariables[0]).getValue(); - tmpVariables[1] = op.getTrustedConfirmationSigner() - .sign( - KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), - KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), - KMByteBlob.cast(data[INPUT_DATA]).length(), scratchPad, (short)0); - if(tmpVariables[1] != KMByteBlob.cast(tmpVariables[0]).length() ){ - KMException.throwIt(KMError.VERIFICATION_FAILED); - } - tmpVariables[0]=Util.arrayCompare(scratchPad,(short)0, - KMByteBlob.cast(tmpVariables[0]).getBuffer(), - KMByteBlob.cast(tmpVariables[0]).getStartOff(), - tmpVariables[1]); - if(tmpVariables[0] != 0){ - KMException.throwIt(KMError.VERIFICATION_FAILED); - } - } - tmpVariables[1] = op.getSigner().getCipherAlgorithm(); - tmpVariables[2] = op.getSigner().getMessageDigestAlgorithm(); - tmpVariables[3] = op.getSigner().getPaddingAlgorithm(); - len = KMByteBlob.cast(data[INPUT_DATA]).length(); - //For RSA Signing algorithm - if(tmpVariables[1] == Signature.SIG_CIPHER_RSA){ - //If no padding and no digest - then zero padding up to 256 on left - if(tmpVariables[2] == MessageDigest.ALG_NULL && tmpVariables[3] == KMCipher.PAD_NOPAD){ - // If data length is greater then key length - if(len > 256){ - KMException.throwIt(KMError.INVALID_INPUT_LENGTH); - }else if(len == 256){ // if data length is same as key length - // Compare the data with key value - date should be less then key value. - // TODO the assumption is that private key exponent value is considered here. - tmpVariables[0]= op.getKey(scratchPad,(short)0); - tmpVariables[0] = Util.arrayCompare( - KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), - KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), - scratchPad, (short)0, tmpVariables[0]); - if(tmpVariables[0] >= 0){ - KMException.throwIt(KMError.INVALID_INPUT_LENGTH); - } - } - Util.arrayCopyNonAtomic( - KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), - KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), - scratchPad, (short)(256 - len),len); - len = (short)256; - } else if (tmpVariables[2] == MessageDigest.ALG_NULL - && tmpVariables[3] == KMCipher.PAD_PKCS1) { - // If PKCS1 padding and no digest - then 0x01||0x00||PS||0x00 on left such that PS = 8 bytes - if(len > 245){ // 256 -11 bytes - KMException.throwIt(KMError.INVALID_INPUT_LENGTH); - } - scratchPad[0] = 0x00; - scratchPad[1] = 0x01; - cryptoProvider.newRandomNumber(scratchPad, (short)2, (short)8); - scratchPad[10] = 0x00; - Util.arrayCopyNonAtomic( - KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), - KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), - scratchPad, (short)11,len); - len += (short)11; - }else if (tmpVariables[2] != MessageDigest.ALG_NULL && tmpVariables[3] == KMCipher.PAD_PKCS1){ - //If PKCS1 padding and digest != ALG_NULL - just copy the data on the scratch pad - Util.arrayCopyNonAtomic( - KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), - KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), - scratchPad, (short)0,len); - } - }else if(tmpVariables[1] == Signature.SIG_CIPHER_ECDSA){ // For ECDSA algorithm - //If no digest then truncate the data to 32 byte if required - if(tmpVariables[2] == MessageDigest.ALG_NULL){ - if(len > 32){ - Util.arrayCopyNonAtomic( - KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), - KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), - scratchPad, (short)0,(short)32); - len = 32; - } - }else{ - //If digest is present then copy the data to scratchpad - Util.arrayCopyNonAtomic( - KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), - KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), - scratchPad, (short)0,len); - } - }else if(tmpVariables[1] == Signature.SIG_CIPHER_HMAC){ // For HMAC algorithm - // Just copy the data as digest is always present. - Util.arrayCopyNonAtomic( - KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), - KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), - scratchPad, (short)0,len); - }else{ // This is should never happen - KMException.throwIt(KMError.OPERATION_CANCELLED); - } - // Sign the data and also complete the trusted verification. - tmpVariables[0]= op.getSigner() - .sign( - KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), - KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), - KMByteBlob.cast(data[INPUT_DATA]).length(),scratchPad, (short)0); - data[OUTPUT_DATA] = KMByteBlob.instance(scratchPad, (short)0, tmpVariables[0]); - } else{ //If decrypt or encrypt operation - tmpVariables[1] = op.getCipher().getCipherAlgorithm(); - tmpVariables[2] = op.getCipher().getPaddingAlgorithm(); - len = KMByteBlob.cast(data[INPUT_DATA]).length(); - if(tmpVariables[1] == KMCipher.CIPHER_RSA){ // For RSA algorithm - // If no padding and no digest - then zero padding up to 256 on left - if (tmpVariables[2] == KMCipher.PAD_NOPAD) { - if(len > 256){ - KMException.throwIt(KMError.INVALID_INPUT_LENGTH); - } - if(len < 256){ - Util.arrayCopyNonAtomic( - KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), - KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), - scratchPad, (short)(256 - len),len); - len = (short)256; - } - } else { - // If OAEP padding with digest - just copy the data to scratchpad and continue. - Util.arrayCopyNonAtomic( - KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), - KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), - scratchPad, (short)0,len); - } - }else if(tmpVariables[1] == KMCipher.CIPHER_DES_CBC || tmpVariables[1] == KMCipher.CIPHER_DES_ECB - || tmpVariables[1] == KMCipher.CIPHER_AES_CBC || - tmpVariables[1] == KMCipher.CIPHER_AES_ECB){ - if(tmpVariables[1] == KMCipher.CIPHER_AES_CBC || - tmpVariables[1] == KMCipher.CIPHER_AES_ECB){ // For AES algorithm - tmpVariables[5] = AES_BLOCK_SIZE; - }else{ - tmpVariables[5] = DES_BLOCK_SIZE; - } - //If no padding then data length must be block aligned - if (tmpVariables[2] == KMCipher.PAD_NOPAD && ((short)(len % tmpVariables[5]) != 0)){ - KMException.throwIt(KMError.INVALID_INPUT_LENGTH); - } - //If padding i.e. pkcs7 then add padding to right - if(tmpVariables[2] != KMCipher.PAD_NOPAD){ - tmpVariables[3] = (short)(len % tmpVariables[5]); - if(tmpVariables[3] != 0){ - // If not block aligned then pkcs7 padding on right - tmpVariables[4] = (short)((len / tmpVariables[5])+tmpVariables[5]); - Util.arrayFillNonAtomic(scratchPad, (short)0, tmpVariables[4], (byte)tmpVariables[3]); - }else{ - // If block aligned then one complete block of pkcs7 padding of block length value - // on the right. - tmpVariables[4] = (short)(len + tmpVariables[5]); - Util.arrayFillNonAtomic(scratchPad, (short)0, tmpVariables[4], (byte)tmpVariables[5]); - } - Util.arrayCopyNonAtomic( KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), - KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), - scratchPad, (short)0,len); - len = tmpVariables[4]; - } - // AES / DES Cipher - tmpVariables[0]= op.getCipher() - .doFinal(scratchPad, (short)0,len, scratchPad, (short)len); - data[OUTPUT_DATA] = KMByteBlob.instance(scratchPad, (short)len, tmpVariables[0]); - } else{ // This should never happen - KMException.throwIt(KMError.OPERATION_CANCELLED); - } - } - // Remove the operation handle - repository.releaseOperation(op); - // Make response - // make response - tmpVariables[1] = KMArray.instance((short) 0); - tmpVariables[1] = KMKeyParameters.instance(tmpVariables[1]); - tmpVariables[2] = KMArray.instance((short) 4); - if (data[OUTPUT_DATA] == KMType.INVALID_VALUE) { - data[OUTPUT_DATA] = KMByteBlob.instance((short) 0); - } - KMArray.cast(tmpVariables[2]).add((short) 0, KMInteger.uint_16(KMError.OK)); - KMArray.cast(tmpVariables[2]).add((short) 1, tmpVariables[1]); - KMArray.cast(tmpVariables[2]).add((short) 2, data[OUTPUT_DATA]); - // Encode the response - bufferLength = encoder.encode(tmpVariables[0], buffer, bufferStartOffset); - sendOutgoing(apdu); - } - private void authorizeUpdateFinalOperation(KMOperationState op, byte[] scratchPad) { - // User Authentication - if (!op.isAuthPerOperation()) { - if (!op.isAuthTimeoutValidated()) { - validateVerificationToken(op, data[VERIFICATION_TOKEN], scratchPad); - tmpVariables[0] = KMInteger.uint_64(op.getAuthTime(), (short) 0); - tmpVariables[2] = KMVerificationToken.cast(data[VERIFICATION_TOKEN]).getTimestamp(); - if (tmpVariables[3] == KMType.INVALID_VALUE) { - KMException.throwIt(KMError.VERIFICATION_FAILED); - } - if (KMInteger.compare(tmpVariables[0], tmpVariables[3]) >= 0) { - KMException.throwIt(KMError.KEY_USER_NOT_AUTHENTICATED); - } - op.setAuthTimeoutValidated(true); - } - } else { // Auth per operation - authorizeUserIdPerKeyOperation(data[HW_TOKEN], scratchPad); - } } - private void validateVerificationToken(KMOperationState op, short verToken, byte[] scratchPad) { - // CBOR Encoding is always big endian and Java is big endian - short ptr = KMVerificationToken.cast(verToken).getMac(); - short len = 0; - // If mac length is zero then token is empty. - if (KMByteBlob.cast(ptr).length() == 0) { - return; - } - // validate operation handle. - ptr = KMVerificationToken.cast(verToken).getChallenge(); - if(op.getHandle() != KMInteger.cast(ptr).getShort()){ - KMException.throwIt(KMError.VERIFICATION_FAILED); - } - // concatenation length will be 37 + length of verified parameters list. - short params = KMVerificationToken.cast(verToken).getParametersVerified(); - Util.arrayFillNonAtomic(scratchPad, (short) 0, - (short) (37+KMByteBlob.cast(params).length()), (byte) 0); - // Add "Auth Verification" - 17 bytes. - Util.arrayCopy(authVerification,(short)0, scratchPad, (short)0, (short)authVerification.length); - len = (short)authVerification.length; - // concatenate challenge - 8 bytes - ptr = KMVerificationToken.cast(verToken).getChallenge(); - KMInteger.cast(ptr) - .value(scratchPad, (short) (len + (short) (8 - KMInteger.cast(ptr).length()))); - len += 8; - // concatenate timestamp -8 bytes - ptr = KMVerificationToken.cast(verToken).getTimestamp(); - KMInteger.cast(ptr) - .value(scratchPad, (short) (len + (short) (8 - KMInteger.cast(ptr).length()))); - len += 8; - // concatenate security level - 4 bytes - ptr = KMVerificationToken.cast(verToken).getSecurityLevel(); - scratchPad[(short) (len + 3)] = KMEnum.cast(ptr).getVal(); - len += 4; - // concatenate Parameters verified - blob of encoded data. - ptr = KMVerificationToken.cast(verToken).getParametersVerified(); - len += KMByteBlob.cast(ptr).getValues(scratchPad, (short)0); - len += 4; - // hmac the data - HMACKey key = - cryptoProvider.createHMACKey( - repository.getComputedHmacKey(), - (short) 0, - (short) repository.getComputedHmacKey().length); - ptr = KMVerificationToken.cast(verToken).getMac(); - boolean verified = - cryptoProvider.hmacVerify(key, scratchPad, (short) 0, len, - KMByteBlob.cast(ptr).getBuffer(), - KMByteBlob.cast(ptr).getStartOff(), - KMByteBlob.cast(ptr).length()); - if(!verified){ - KMException.throwIt(KMError.VERIFICATION_FAILED); - } - /* - - // Compare mac. - ptr = KMVerificationToken.cast(verToken).getMac(); - if (macLen != KMByteBlob.cast(ptr).length()) { - KMException.throwIt(KMError.INVALID_MAC_LENGTH); - } - if (Util.arrayCompare( - scratchPad, (short) (len+1), - KMByteBlob.cast(ptr).getBuffer(), KMByteBlob.cast(ptr).getStartOff(), macLen) != 0) { - KMException.throwIt(KMError.VERIFICATION_FAILED); - } - */ - - } private void processUpdateOperationCmd(APDU apdu) { - // TODO Add Support for AES-GCM - receiveIncoming(apdu); - byte[] scratchPad = apdu.getBuffer(); - tmpVariables[1] = KMArray.instance((short) 5); - // Arguments - tmpVariables[2] = KMKeyParameters.exp(); - KMArray.cast(tmpVariables[1]).add((short) 0, KMInteger.exp()); - KMArray.cast(tmpVariables[1]).add((short) 1, tmpVariables[2]); - KMArray.cast(tmpVariables[1]).add((short) 2, KMByteBlob.exp()); - tmpVariables[3] = KMHardwareAuthToken.exp(); - KMArray.cast(tmpVariables[1]).add((short) 3, tmpVariables[3]); - tmpVariables[4] = KMVerificationToken.exp(); - KMArray.cast(tmpVariables[1]).add((short) 4, tmpVariables[4]); - // Decode the arguments - tmpVariables[2] = decoder.decode(tmpVariables[1], buffer, bufferStartOffset, bufferLength); - data[OP_HANDLE] = KMArray.cast(tmpVariables[2]).get((short) 0); - data[KEY_PARAMETERS] = KMArray.cast(tmpVariables[2]).get((short) 1); - data[INPUT_DATA] = KMArray.cast(tmpVariables[2]).get((short) 2); - data[HW_TOKEN] = KMArray.cast(tmpVariables[2]).get((short) 3); - data[VERIFICATION_TOKEN] = KMArray.cast(tmpVariables[2]).get((short) 4); - // Check Operation Handle and get op state - tmpVariables[1] = KMInteger.cast(data[OP_HANDLE]).getShort(); - KMOperationState op = repository.findOperation(tmpVariables[1]); - if (KMInteger.compare(data[OP_HANDLE], KMInteger.uint_16(op.getHandle())) != 0) { - KMException.throwIt(KMError.INVALID_OPERATION_HANDLE); - } - // authorize the update operation - authorizeUpdateFinalOperation(op, scratchPad); - // If signing without digest then do length validation checks - tmpVariables[0] = KMByteBlob.cast(data[INPUT_DATA]).length(); - if (op.getPurpose() == KMType.SIGN) { - // If signing without digest then update should not be called by HAL only final must be - // called - if (op.getSigner().getMessageDigestAlgorithm() == MessageDigest.ALG_NULL) { - KMException.throwIt(KMError.OPERATION_CANCELLED); - } - op.getSigner() - .update( - KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), - KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), - KMByteBlob.cast(data[INPUT_DATA]).length()); - - if (op.isTrustedConfirmationRequired()) { - op.getTrustedConfirmationSigner() - .update( - KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), - KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), - KMByteBlob.cast(data[INPUT_DATA]).length()); - } - data[OUTPUT_DATA] = KMType.INVALID_VALUE; - } else { - // purpose is Encrypt or Decrypt - input data must be block aligned. - tmpVariables[1] = op.getCipher().getCipherAlgorithm(); - // TODO Update for decrypt for RSA may not be necessary - confirm this - if (tmpVariables[1] == KMCipher.CIPHER_RSA) { - KMException.throwIt(KMError.OPERATION_CANCELLED); - } - if (tmpVariables[1] == KMCipher.CIPHER_AES_CBC - || op.getCipher().getCipherAlgorithm() == KMCipher.CIPHER_AES_ECB) { - // 128 bit block size - HAL must send block aligned data - if (tmpVariables[0] % 16 != 0) { - KMException.throwIt(KMError.INVALID_INPUT_LENGTH); - } - } else if (op.getCipher().getCipherAlgorithm() == KMCipher.CIPHER_DES_CBC - || op.getCipher().getCipherAlgorithm() == KMCipher.CIPHER_DES_ECB) { - // 64 bit block size - HAL must send block aligned data - if (tmpVariables[0] % 8 != 0) { - KMException.throwIt(KMError.INVALID_INPUT_LENGTH); - } - } - tmpVariables[1] = - op.getCipher() - .update( - KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), - KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), - KMByteBlob.cast(data[INPUT_DATA]).length(), - scratchPad, - (short) 0); - data[OUTPUT_DATA] = KMByteBlob.instance(scratchPad, (short) 0, tmpVariables[1]); - } - // make response - tmpVariables[1] = KMArray.instance((short) 0); - tmpVariables[1] = KMKeyParameters.instance(tmpVariables[1]); - tmpVariables[2] = KMArray.instance((short) 4); - if (data[OUTPUT_DATA] == KMType.INVALID_VALUE) { - data[OUTPUT_DATA] = KMByteBlob.instance((short) 0); - } - KMArray.cast(tmpVariables[2]).add((short) 0, KMInteger.uint_16(KMError.OK)); - KMArray.cast(tmpVariables[2]).add((short) 1, KMInteger.uint_16(tmpVariables[0])); - KMArray.cast(tmpVariables[2]).add((short) 2, tmpVariables[1]); - KMArray.cast(tmpVariables[2]).add((short) 3, data[OUTPUT_DATA]); - // Encode the response - bufferLength = encoder.encode(tmpVariables[0], buffer, bufferStartOffset); - sendOutgoing(apdu); - } - - private void processBeginOperationCmd(APDU apdu) { - // Receive the incoming request fully from the master into buffer. - receiveIncoming(apdu); - byte[] scratchPad = apdu.getBuffer(); - tmpVariables[1] = KMArray.instance((short) 4); - // Arguments - tmpVariables[2] = KMKeyParameters.exp(); - KMArray.cast(tmpVariables[1]).add((short) 0, KMEnum.instance(KMType.PURPOSE)); - KMArray.cast(tmpVariables[1]).add((short) 1, KMByteBlob.exp()); - KMArray.cast(tmpVariables[1]).add((short) 2, tmpVariables[2]); - tmpVariables[3] = KMHardwareAuthToken.exp(); - KMArray.cast(tmpVariables[1]).add((short) 3, tmpVariables[3]); - // Decode the arguments - tmpVariables[2] = decoder.decode(tmpVariables[1], buffer, bufferStartOffset, bufferLength); - data[KEY_PARAMETERS] = KMArray.cast(tmpVariables[2]).get((short) 2); - data[KEY_BLOB] = KMArray.cast(tmpVariables[2]).get((short) 1); - tmpVariables[0] = KMArray.cast(tmpVariables[2]).get((short) 0); - tmpVariables[0] = KMEnum.cast(tmpVariables[0]).getVal(); - tmpVariables[4] = KMArray.cast(tmpVariables[2]).get((short) 3); - // Check for app id and app data. - data[APP_ID] = KMType.INVALID_VALUE; - data[APP_DATA] = KMType.INVALID_VALUE; - tmpVariables[3] = - KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.APPLICATION_ID, data[KEY_PARAMETERS]); - if (tmpVariables[3] != KMTag.INVALID_VALUE) { - data[APP_ID] = KMByteTag.cast(tmpVariables[3]).getValue(); - } - tmpVariables[3] = - KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.APPLICATION_DATA, data[KEY_PARAMETERS]); - if (tmpVariables[3] != KMTag.INVALID_VALUE) { - data[APP_DATA] = KMByteTag.cast(tmpVariables[3]).getValue(); - } - // Parse the encrypted blob and decrypt it. - parseEncryptedKeyBlob(scratchPad); - // Authorize the begin operation and reserve op - data[OP_HANDLE] will have the handle. - // It will also set data[IV] field if required. - authorizeBeginOperation(tmpVariables[4], scratchPad); - // Check for trusted confirmation - if required then set the signer in op state. - tmpVariables[0] = - KMKeyParameters.findTag( - KMType.BOOL_TAG, KMType.TRUSTED_CONFIRMATION_REQUIRED, data[HW_PARAMETERS]); - if (tmpVariables[0] != KMType.INVALID_VALUE) { - // get operation - KMOperationState op = repository.findOperation(data[OP_HANDLE]); - // get the hmac key - if (repository.getComputedHmacKey() == null) { - KMException.throwIt(KMError.OPERATION_CANCELLED); - } - // set the Hmac signer - op.setTrustedConfirmationSigner( - cryptoProvider.createHmacSigner( - MessageDigest.ALG_SHA_256, - repository.getComputedHmacKey(), - (short) 0, - (short) repository.getComputedHmacKey().length)); - } - // If the data[IV] is required to be returned. - if (data[IV] != KMType.INVALID_VALUE) { - // TODO confirm why this is needed - tmpVariables[2] = KMArray.instance((short) 1); - KMArray.cast(tmpVariables[2]).add((short) 0, data[IV]); - } else { - tmpVariables[2] = KMArray.instance((short) 0); - } - tmpVariables[1] = KMKeyParameters.instance(tmpVariables[2]); - 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, tmpVariables[1]); - KMArray.cast(tmpVariables[0]).add((short) 2, data[OP_HANDLE]); - // Encode the response - bufferLength = encoder.encode(tmpVariables[0], buffer, bufferStartOffset); - sendOutgoing(apdu); - } - - private void authorizeBeginOperation(short hwToken, byte[] scratchPad) { - // Read purpose from key parameters - cannot be null. - short purpose = - KMEnumArrayTag.getValues(KMType.PURPOSE, data[KEY_PARAMETERS], scratchPad, (short) 0); - if (purpose == KMType.INVALID_VALUE) { - KMException.throwIt(KMError.INVALID_ARGUMENT); - } - if (purpose != 1) { - KMException.throwIt(KMError.INVALID_ARGUMENT); - } - purpose = scratchPad[0]; - if (!(KMEnumArrayTag.contains(KMType.PURPOSE, purpose, data[HW_PARAMETERS]))) { - KMException.throwIt(KMError.UNSUPPORTED_PURPOSE); - } - // Read digest from key parameters - can be null for EC. - short digest = - KMEnumArrayTag.getValues(KMType.DIGEST, data[KEY_PARAMETERS], scratchPad, (short) 0); - if (digest != KMType.INVALID_VALUE && digest != 1) { - KMException.throwIt(KMError.INVALID_ARGUMENT); - } - digest = scratchPad[0]; - // Read padding from key parameters - can be null for AES/DES. - short padding = - KMEnumArrayTag.getValues(KMType.PADDING, data[KEY_PARAMETERS], scratchPad, (short) 0); - if (padding != KMType.INVALID_VALUE && padding != 1) { - KMException.throwIt(KMError.INVALID_ARGUMENT); - } - padding = scratchPad[0]; - // Read Blockmode - short blockmode = - KMEnumArrayTag.getValues(KMType.BLOCK_MODE, data[KEY_PARAMETERS], scratchPad, (short) 0); - if (blockmode != KMType.INVALID_VALUE && blockmode != 1) { - KMException.throwIt(KMError.INVALID_ARGUMENT); - } - blockmode = scratchPad[0]; - - // Max uses per boot - tmpVariables[0] = - KMKeyParameters.findTag(KMType.UINT_TAG, KMType.MAX_USES_PER_BOOT, data[HW_PARAMETERS]); - if (tmpVariables[0] != KMType.INVALID_VALUE) { - // get prescribed limit - tmpVariables[0] = KMIntegerTag.cast(tmpVariables[0]).getValue(); - authorizeKeyUsageForCount(tmpVariables[0]); - } - // Authorize UserId - auth timeout check cannot be done in javacard - tmpVariables[0] = - KMKeyParameters.findTag(KMType.ULONG_ARRAY_TAG, KMType.USER_SECURE_ID, data[HW_PARAMETERS]); - if (tmpVariables[0] != KMType.INVALID_VALUE) { - tmpVariables[0] = - KMKeyParameters.findTag(KMType.UINT_TAG, KMType.AUTH_TIMEOUT, data[HW_PARAMETERS]); - if (tmpVariables[0] != KMType.INVALID_VALUE) { - // check if hw token is empty - mac should not be empty. - tmpVariables[1] = KMHardwareAuthToken.cast(hwToken).getMac(); - if (KMByteBlob.cast(tmpVariables[1]).length() == 0) { - KMException.throwIt(KMError.INVALID_MAC_LENGTH); - } - authorizeUserId(hwToken, scratchPad); - } - } - // Authorize Caller Nonce - if caller nonce absent in key char and nonce present in - // key params then fail. - tmpVariables[2] = KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.NONCE, data[KEY_PARAMETERS]); - tmpVariables[0] = - KMKeyParameters.findTag(KMType.BOOL_TAG, KMType.CALLER_NONCE, data[HW_PARAMETERS]); - if (tmpVariables[0] == KMType.INVALID_VALUE) { - if (tmpVariables[2] != KMType.INVALID_VALUE) { - KMException.throwIt(KMError.CALLER_NONCE_PROHIBITED); - } - } - // Authorize Bootloader Only - assumption is that if this is is present then always fail. - tmpVariables[0] = - KMKeyParameters.findTag(KMType.BOOL_TAG, KMType.BOOTLOADER_ONLY, data[HW_PARAMETERS]); - if (tmpVariables[1] != KMType.INVALID_VALUE) { - KMException.throwIt(KMError.INVALID_KEY_BLOB); - } - tmpVariables[0] = KMEnumTag.getValue(KMType.ALGORITHM, data[HW_PARAMETERS]); - switch (tmpVariables[0]) { - case KMType.RSA: - authorizeRsa(purpose, digest, padding); - break; - case KMType.EC: - authorizeEC(purpose, digest); - break; - case KMType.DES: - case KMType.AES: - if (tmpVariables[2] == KMType.INVALID_VALUE) { - tmpVariables[2] = KMByteBlob.instance((short) 16); - cryptoProvider.newRandomNumber( - KMByteBlob.cast(tmpVariables[2]).getBuffer(), - KMByteBlob.cast(tmpVariables[2]).getStartOff(), - KMByteBlob.cast(tmpVariables[2]).length()); - } - data[IV] = tmpVariables[2]; - authorizeAesDes(tmpVariables[0], purpose, blockmode, padding); - break; - case KMType.HMAC: - authorizeHmac(purpose, digest); - break; - default: - KMException.throwIt(KMError.UNIMPLEMENTED); - break; - } - } - - private void authorizeGCM(short purpose, short padding) { - data[OP_HANDLE] = KMType.INVALID_VALUE; - if (purpose == KMType.SIGN || purpose == KMType.VERIFY) { - KMException.throwIt(KMError.UNSUPPORTED_PURPOSE); - } - if (purpose == KMType.ENCRYPT) { - purpose = KMCipher.MODE_ENCRYPT; - } else { - purpose = KMCipher.MODE_DECRYPT; - } - if (padding != KMType.PADDING_NONE) { - KMException.throwIt(KMError.INCOMPATIBLE_PADDING_MODE); - } - // Read and authorizeBeginOperation mac length - tmpVariables[0] = - KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.MAC_LENGTH, data[KEY_PARAMETERS]); - if (tmpVariables[0] == KMType.INVALID_VALUE) { - KMException.throwIt(KMError.MISSING_MAC_LENGTH); - } - if (tmpVariables[0] % 8 != 0) { - KMException.throwIt(KMError.INVALID_MAC_LENGTH); - } - tmpVariables[1] = - KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, data[HW_PARAMETERS]); - if (tmpVariables[0] < tmpVariables[1]) { - KMException.throwIt(KMError.INVALID_MAC_LENGTH); - } - if (tmpVariables[0] > 128) { - KMException.throwIt(KMError.INVALID_MAC_LENGTH); - } - KMOperationState op = repository.reserveOperation(); - if (op == null) { - KMException.throwIt(KMError.TOO_MANY_OPERATIONS); - } - op.setPurpose(purpose); - op.setKey(KMByteBlob.cast(data[SECRET]).getBuffer(), - KMByteBlob.cast(data[SECRET]).getStartOff(), - KMByteBlob.cast(data[SECRET]).length()); - op.setCipher( - cryptoProvider.createGCMCipher( - purpose, - KMByteBlob.cast(data[SECRET]).getBuffer(), - KMByteBlob.cast(data[SECRET]).getStartOff(), - KMByteBlob.cast(data[SECRET]).length(), - KMByteBlob.cast(data[IV]).getBuffer(), - KMByteBlob.cast(data[IV]).getStartOff(), - KMByteBlob.cast(data[IV]).length())); - data[OP_HANDLE] = op.getHandle(); - } - - private void authorizeHmac(short purpose, short digest) { - data[OP_HANDLE] = KMType.INVALID_VALUE; - if (purpose == KMType.ENCRYPT || purpose == KMType.DECRYPT) { - KMException.throwIt(KMError.UNSUPPORTED_PURPOSE); - } - if (digest == KMType.INVALID_VALUE) { - KMException.throwIt(KMError.INVALID_ARGUMENT); - } - if (!(KMEnumArrayTag.contains(KMType.DIGEST, digest, data[HW_PARAMETERS]))) { - KMException.throwIt(KMError.UNSUPPORTED_DIGEST); - } - // Read and authorizeBeginOperation mac length - tmpVariables[0] = - KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.MAC_LENGTH, data[KEY_PARAMETERS]); - if (tmpVariables[0] == KMType.INVALID_VALUE) { - KMException.throwIt(KMError.MISSING_MAC_LENGTH); - } - if (tmpVariables[0] % 8 != 0) { - KMException.throwIt(KMError.INVALID_MAC_LENGTH); - } - tmpVariables[1] = - KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, data[HW_PARAMETERS]); - if (tmpVariables[0] < tmpVariables[1]) { - KMException.throwIt(KMError.INVALID_MAC_LENGTH); - } - - switch (digest) { - case KMType.MD5: - tmpVariables[2] = MessageDigest.ALG_MD5; - tmpVariables[1] = 128; - break; - case KMType.SHA1: - tmpVariables[2] = MessageDigest.ALG_SHA; - tmpVariables[1] = 160; - break; - case KMType.SHA2_224: - tmpVariables[2] = MessageDigest.ALG_SHA_224; - tmpVariables[1] = 224; - break; - case KMType.SHA2_256: - tmpVariables[2] = MessageDigest.ALG_SHA_256; - tmpVariables[1] = 256; - break; - case KMType.SHA2_384: - tmpVariables[2] = MessageDigest.ALG_SHA_384; - tmpVariables[1] = 384; - break; - case KMType.SHA2_512: - tmpVariables[2] = MessageDigest.ALG_SHA_512; - tmpVariables[1] = 512; - break; - default: - tmpVariables[1] = 512; - break; - } - if (tmpVariables[0] > tmpVariables[1]) { - KMException.throwIt(KMError.INVALID_MAC_LENGTH); - } - KMOperationState op = repository.reserveOperation(); - if (op == null) { - KMException.throwIt(KMError.TOO_MANY_OPERATIONS); - } - op.setPurpose(purpose); - op.setKey(KMByteBlob.cast(data[SECRET]).getBuffer(), - KMByteBlob.cast(data[SECRET]).getStartOff(), - KMByteBlob.cast(data[SECRET]).length()); - op.setSigner( - cryptoProvider.createHmacSigner( - tmpVariables[0], - KMByteBlob.cast(data[SECRET]).getBuffer(), - KMByteBlob.cast(data[SECRET]).getStartOff(), - KMByteBlob.cast(data[SECRET]).length())); - data[OP_HANDLE] = op.getHandle(); - } - - private void authorizeAesDes(short alg, short purpose, short blockmode, short padding) { - data[OP_HANDLE] = KMType.INVALID_VALUE; - if (purpose == KMType.SIGN || purpose == KMType.VERIFY) { - KMException.throwIt(KMError.UNSUPPORTED_PURPOSE); - } - if (blockmode == KMType.GCM) { - authorizeGCM(purpose, padding); - } - if (purpose == KMType.ENCRYPT) { - purpose = KMCipher.MODE_ENCRYPT; - } else { - purpose = KMCipher.MODE_DECRYPT; - } - KMOperationState op = null; - // padding must be no pad - PKCS7 is not supported in javacard - // TODO implement PCKS7 in cryptoProvider - if (padding == KMType.PADDING_NONE) { - padding = KMCipher.PAD_NULL; - } else if (padding == KMType.PKCS7) { - padding = KMCipher.PAD_PKCS7; - } else { - KMException.throwIt(KMError.INCOMPATIBLE_PADDING_MODE); - } - if (alg == KMType.AES) { - if (blockmode == KMType.CBC) { - tmpVariables[0] = KMCipher.CIPHER_AES_CBC; - } else if (blockmode == KMType.ECB) { - tmpVariables[0] = KMCipher.CIPHER_AES_ECB; - } else { - // data[CIPHER_ALGORITHM] = Cipher.CIPHER_AES_CTR; // Not supported in 3.0.5 - // TODO change this once we can test. - KMException.throwIt(KMError.UNSUPPORTED_BLOCK_MODE); - } - op = repository.reserveOperation(); - } else if (alg == KMType.DES) { - if (blockmode == KMType.CBC) { - tmpVariables[0] = KMCipher.CIPHER_DES_CBC; - } else if (blockmode == KMType.ECB) { - tmpVariables[0] = KMCipher.CIPHER_DES_ECB; - } else { - // data[CIPHER_ALGORITHM] = Cipher.CIPHER_DES_CTR; // Not supported in 3.0.5 - // TODO change this once we can test. - KMException.throwIt(KMError.UNSUPPORTED_BLOCK_MODE); - } - op = repository.reserveOperation(); - } else { - KMException.throwIt(KMError.INCOMPATIBLE_ALGORITHM); - } - if (op == null) { - KMException.throwIt(KMError.TOO_MANY_OPERATIONS); - } - op.setPurpose(purpose); - op.setKey(KMByteBlob.cast(data[SECRET]).getBuffer(), - KMByteBlob.cast(data[SECRET]).getStartOff(), - KMByteBlob.cast(data[SECRET]).length()); - op.setCipher( - cryptoProvider.createSymmetricCipher( - tmpVariables[0], - padding, - purpose, - KMByteBlob.cast(data[SECRET]).getBuffer(), - KMByteBlob.cast(data[SECRET]).getStartOff(), - KMByteBlob.cast(data[SECRET]).length(), - KMByteBlob.cast(data[IV]).getBuffer(), - KMByteBlob.cast(data[IV]).getStartOff(), - KMByteBlob.cast(data[IV]).length())); - data[OP_HANDLE] = op.getHandle(); - } - - private void authorizeEC(short purpose, short digest) { - data[OP_HANDLE] = KMType.INVALID_VALUE; - // purpose will be always sign. - // Only ECDSA signing supported - if (purpose == KMType.ENCRYPT || purpose == KMType.VERIFY || purpose == KMType.DECRYPT) { - KMException.throwIt(KMError.UNSUPPORTED_PURPOSE); - } - switch (digest) { - case KMType.DIGEST_NONE: - tmpVariables[0] = MessageDigest.ALG_NULL; - break; - case KMType.SHA1: - tmpVariables[0] = MessageDigest.ALG_SHA; - break; - case KMType.SHA2_224: - tmpVariables[0] = MessageDigest.ALG_SHA_224; - break; - case KMType.SHA2_256: - tmpVariables[0] = MessageDigest.ALG_SHA_256; - break; - case KMType.SHA2_384: - tmpVariables[0] = MessageDigest.ALG_SHA_384; - break; - case KMType.SHA2_512: - tmpVariables[0] = MessageDigest.ALG_SHA_512; - break; - default: - KMException.throwIt(KMError.INCOMPATIBLE_DIGEST); - break; - } - KMOperationState op = repository.reserveOperation(); - if (op == null) { - KMException.throwIt(KMError.TOO_MANY_OPERATIONS); - } - op.setPurpose(purpose); - op.setKey(KMByteBlob.cast(data[SECRET]).getBuffer(), - KMByteBlob.cast(data[SECRET]).getStartOff(), - KMByteBlob.cast(data[SECRET]).length()); - op.setSigner( - cryptoProvider.createEcSigner( - tmpVariables[0], - KMByteBlob.cast(data[SECRET]).getBuffer(), - KMByteBlob.cast(data[SECRET]).getStartOff(), - KMByteBlob.cast(data[SECRET]).length())); - data[OP_HANDLE] = op.getHandle(); } - private void authorizeRsa(short purpose, short digest, short padding) { - KMOperationState op = null; - data[OP_HANDLE] = KMType.INVALID_VALUE; - if (purpose == KMType.ENCRYPT || purpose == KMType.VERIFY) { - KMException.throwIt(KMError.UNSUPPORTED_PURPOSE); - } - switch (purpose) { - case KMType.DECRYPT: - tmpVariables[0] = KMCipher.CIPHER_RSA; - if (padding == KMType.PADDING_NONE) { - // There is no way to select digest with no padding. Digest is also none. - padding = KMCipher.PAD_NOPAD; - } else if (padding != KMType.RSA_OAEP) { - KMException.throwIt(KMError.INCOMPATIBLE_PADDING_MODE); - } else { - // There is no way to ascertain MGF1 and SHA1 in javacard - this should be part of PKCS1. - switch (digest) { - case KMType.DIGEST_NONE: - KMException.throwIt(KMError.UNSUPPORTED_DIGEST); - break; - case KMType.SHA2_224: - padding = KMCipher.PAD_PKCS1_OAEP_SHA224; - break; - case KMType.SHA2_256: - padding = KMCipher.PAD_PKCS1_OAEP_SHA256; - break; - case KMType.SHA2_384: - padding = KMCipher.PAD_PKCS1_OAEP_SHA384; - break; - case KMType.SHA2_512: - padding = KMCipher.PAD_PKCS1_OAEP_SHA512; - break; - default: - KMException.throwIt(KMError.INCOMPATIBLE_DIGEST); - break; - } - } - op = repository.reserveOperation(); - if (op == null) { - KMException.throwIt(KMError.TOO_MANY_OPERATIONS); - } - op.setPurpose(purpose); - op.setKey(KMByteBlob.cast(data[SECRET]).getBuffer(), - KMByteBlob.cast(data[SECRET]).getStartOff(), - KMByteBlob.cast(data[SECRET]).length()); - op.setCipher( - cryptoProvider.createRsaDecrypt( - tmpVariables[0], - padding, - KMByteBlob.cast(data[SECRET]).getBuffer(), - KMByteBlob.cast(data[SECRET]).getStartOff(), - KMByteBlob.cast(data[SECRET]).length(), - KMByteBlob.cast(data[PUB_KEY]).getBuffer(), - KMByteBlob.cast(data[PUB_KEY]).getStartOff(), - KMByteBlob.cast(data[PUB_KEY]).length())); - break; - case KMType.SIGN: - if (padding == KMType.PADDING_NONE) { - if(digest == KMType.DIGEST_NONE){ - tmpVariables[0] = MessageDigest.ALG_NULL; - padding = KMCipher.PAD_NOPAD; - }else{ - KMException.throwIt(KMError.INCOMPATIBLE_DIGEST); - } - } else if (padding != KMType.RSA_PKCS1_1_5_SIGN) { - KMException.throwIt(KMError.INCOMPATIBLE_PADDING_MODE); - } else { - padding = KMCipher.PAD_PKCS1; - switch (digest) { // TODO No digest not supported at this moment - case KMType.DIGEST_NONE: - tmpVariables[0] = MessageDigest.ALG_NULL; - break; - case KMType.SHA2_224: - tmpVariables[0] = MessageDigest.ALG_SHA_224; - break; - case KMType.SHA2_256: - tmpVariables[0] = MessageDigest.ALG_SHA_256; - break; - case KMType.SHA2_384: - tmpVariables[0] = MessageDigest.ALG_SHA_384; - break; - case KMType.SHA2_512: - tmpVariables[0] = MessageDigest.ALG_SHA_512; - break; - default: - KMException.throwIt(KMError.INCOMPATIBLE_DIGEST); - break; - } - } - op = repository.reserveOperation(); - if (op == null) { - KMException.throwIt(KMError.TOO_MANY_OPERATIONS); - } - op.setPurpose(purpose); - op.setKey(KMByteBlob.cast(data[SECRET]).getBuffer(), - KMByteBlob.cast(data[SECRET]).getStartOff(), - KMByteBlob.cast(data[SECRET]).length()); - op.setSigner( - cryptoProvider.createRsaSigner( - tmpVariables[0], - padding, - KMByteBlob.cast(data[SECRET]).getBuffer(), - KMByteBlob.cast(data[SECRET]).getStartOff(), - KMByteBlob.cast(data[SECRET]).length(), - KMByteBlob.cast(data[PUB_KEY]).getBuffer(), - KMByteBlob.cast(data[PUB_KEY]).getStartOff(), - KMByteBlob.cast(data[PUB_KEY]).length())); - break; - default: - KMException.throwIt(KMError.UNSUPPORTED_PURPOSE); - break; - } - data[OP_HANDLE] = op.getHandle(); - } + private void processBeginOperationCmd(APDU apdu) {} - private void authorizeUserId(short hwToken, byte[] scratchPad) { - validateHwToken(hwToken, scratchPad); - tmpVariables[0] = KMHardwareAuthToken.cast(hwToken).getUserId(); - if (KMInteger.cast(tmpVariables[0]).isZero()) { - tmpVariables[0] = KMHardwareAuthToken.cast(hwToken).getAuthenticatorId(); - if (KMInteger.cast(tmpVariables[0]).isZero()) { - KMException.throwIt(KMError.KEY_USER_NOT_AUTHENTICATED); - } - } - // check user secure id - if (!KMIntegerArrayTag.contains(KMType.USER_SECURE_ID, tmpVariables[0], data[HW_PARAMETERS])) { - KMException.throwIt(KMError.KEY_USER_NOT_AUTHENTICATED); - } - // check auth type - tmpVariables[1] = KMEnumTag.getValue(KMType.USER_AUTH_TYPE, data[HW_PARAMETERS]); - tmpVariables[2] = KMHardwareAuthToken.cast(hwToken).getHwAuthenticatorType(); - tmpVariables[2] = KMEnum.cast(tmpVariables[2]).getVal(); - if (((byte) tmpVariables[2] & (byte) tmpVariables[1]) == 0) { - KMException.throwIt(KMError.KEY_USER_NOT_AUTHENTICATED); - } - } - - private void validateHwToken(short hwToken, byte[] scratchPad) { - // CBOR Encoding is always big endian - short ptr = KMHardwareAuthToken.cast(hwToken).getMac(); - short len = 0; - // If mac length is zero then token is empty. - if (KMByteBlob.cast(ptr).length() == 0) { - return; - } - // add 0 - Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 37, (byte) 0); - len = 1; - // concatenate challenge - 8 bytes - ptr = KMHardwareAuthToken.cast(hwToken).getChallenge(); - KMInteger.cast(ptr) - .value(scratchPad, (short) (len + (short) (8 - KMInteger.cast(ptr).length()))); - len += 8; - // concatenate user id - 8 bytes - ptr = KMHardwareAuthToken.cast(hwToken).getUserId(); - KMInteger.cast(tmpVariables[0]) - .value(scratchPad, (short) (len + (short) (8 - KMInteger.cast(ptr).length()))); - len += 8; - // concatenate authenticator id - 8 bytes - ptr = KMHardwareAuthToken.cast(hwToken).getAuthenticatorId(); - KMInteger.cast(tmpVariables[0]) - .value(scratchPad, (short) (len + (short) (8 - KMInteger.cast(ptr).length()))); - len += 8; - // concatenate authenticator type - 4 bytes - ptr = KMHardwareAuthToken.cast(hwToken).getHwAuthenticatorType(); - scratchPad[(short) (len + 3)] = KMEnum.cast(ptr).getVal(); - len += 4; - // concatenate timestamp -8 bytes - ptr = KMHardwareAuthToken.cast(hwToken).getTimestamp(); - KMInteger.cast(tmpVariables[0]) - .value(scratchPad, (short) (len + (short) (8 - KMInteger.cast(ptr).length()))); - len += 8; - // hmac the data - HMACKey key = - cryptoProvider.createHMACKey( - repository.getComputedHmacKey(), - (short) 0, - (short) repository.getComputedHmacKey().length); - ptr = KMHardwareAuthToken.cast(hwToken).getMac(); - boolean verified = - cryptoProvider.hmacVerify(key, scratchPad, (short) 0, len, - KMByteBlob.cast(ptr).getBuffer(), - KMByteBlob.cast(ptr).getStartOff(), - KMByteBlob.cast(ptr).length()); - if(!verified){ - KMException.throwIt(KMError.VERIFICATION_FAILED); - } -/* - len = - cryptoProvider.hmac(key, scratchPad, (short) 0, len, scratchPad, (short) (len + 1) ); - // Compare mac. - ptr = KMHardwareAuthToken.cast(hwToken).getMac(); - if (len != KMByteBlob.cast(ptr).length()) { - KMException.throwIt(KMError.INVALID_MAC_LENGTH); - } - if (Util.arrayCompare( - scratchPad, - (short) 38, - KMByteBlob.cast(ptr).getBuffer(), - KMByteBlob.cast(ptr).getStartOff(), - len) - != 0) { - KMException.throwIt(KMError.VERIFICATION_FAILED); - } - */ - } - - private void authorizeUserIdPerKeyOperation(short hwToken, byte[] scratchPad) { - tmpVariables[0] = KMHardwareAuthToken.cast(hwToken).getChallenge(); - if (KMInteger.compare(data[OP_HANDLE], tmpVariables[0]) != 0) { - KMException.throwIt(KMError.KEY_USER_NOT_AUTHENTICATED); - } - authorizeUserId(hwToken, scratchPad); - } - - private void authorizeKeyUsageForCount(short limit) { - // get current counter - // TODO currently only short counter supported - max count 32K. - short val = repository.getRateLimitedKeyCount(data[AUTH_TAG]); - if (val != KMType.INVALID_VALUE) { - short count = KMInteger.uint_16(val); - // compare 32 bit values - is current counter less then prescribed limit - if (KMInteger.compare(count, limit) != -1) { - KMException.throwIt(KMError.KEY_MAX_OPS_EXCEEDED); - } - // increment the counter and store it back. - val++; - repository.setRateLimitedKeyCount(data[AUTH_TAG], val); - } else { - KMException.throwIt(KMError.UNKNOWN_ERROR); - } - } private void processImportKeyCmd(APDU apdu) { if (repository.keyBlobCount > repository.MAX_BLOB_STORAGE) { @@ -2893,21 +1539,5 @@ private static void sendError(APDU apdu, short err) { bufferLength = encoder.encodeError(err, buffer, bufferStartOffset, (short) 5); sendOutgoing(apdu); } - /* - private static 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 static 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()); - }*/ + }