From 1d660ed2eea2fa012e183ba7afd9f12c1c3ecf5b Mon Sep 17 00:00:00 2001 From: cpathak Date: Tue, 16 Jun 2020 23:16:00 -0700 Subject: [PATCH 1/2] Added Import Wrapped Key Tested Import Wrapped key with jcard sim. Optimization is still necessary. --- .../javacard/keymaster/KMJcardSimulator.java | 16 +- .../javacard/keymaster/KMEnumArrayTag.java | 3 + .../javacard/keymaster/KMKeymasterApplet.java | 215 +++++++++--------- .../javacard/keymaster/KMRepository.java | 2 +- .../com/android/javacard/test/KMVTSTest.java | 117 +++++++++- 5 files changed, 238 insertions(+), 115 deletions(-) diff --git a/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMJcardSimulator.java b/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMJcardSimulator.java index 3a4a57b5..f9649564 100644 --- a/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMJcardSimulator.java +++ b/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMJcardSimulator.java @@ -198,10 +198,12 @@ public short aesGCMEncrypt( 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]; + if(key.getSize() == 128){ + keyMaterial = new byte[16]; + }else if(key.getSize() == 256){ + keyMaterial = new byte[32]; + } key.getKey(keyMaterial,(short)0); //print("KeyMaterial Enc", keyMaterial); //print("Authdata Enc", authData, authDataStart, authDataLen); @@ -323,10 +325,12 @@ public boolean aesGCMDecrypt( 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]; + if(key.getSize() == 128){ + keyMaterial = new byte[16]; + }else if(key.getSize() == 256){ + keyMaterial = new byte[32]; + } key.getKey(keyMaterial,(short)0); //print("KeyMaterial Dec", keyMaterial); //print("Authdata Dec", authData, authDataStart, authDataLen); diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMEnumArrayTag.java b/Applet/Applet/src/com/android/javacard/keymaster/KMEnumArrayTag.java index 6304d7a2..bb35920b 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMEnumArrayTag.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMEnumArrayTag.java @@ -263,6 +263,9 @@ public boolean isValidPurpose(byte alg){ return false; } break; + case KMType.WRAP_KEY: + if(alg != KMType.RSA) return false; + break; default: 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 c65b8fb9..b1baf675 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java @@ -50,7 +50,7 @@ public class KMKeymasterApplet extends Applet implements AppletEvent, ExtendedLe private static final byte CLA_ISO7816_NO_SM_NO_CHAN = (byte) 0x80; private static final short KM_HAL_VERSION = (short) 0x4000; private static final short MAX_AUTH_DATA_SIZE = (short) 512; - private static final short MAX_IO_LENGTH = 0x400; + private static final short MAX_IO_LENGTH = 0x600; // "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, @@ -758,10 +758,10 @@ private void processImportWrappedKeyCmd(APDU apdu) { // Receive the incoming request fully from the master into buffer. receiveIncoming(apdu); byte[] scratchPad = apdu.getBuffer(); - tmpVariables[1] = KMArray.instance((short) 11); + tmpVariables[1] = KMArray.instance((short) 12); // Arguments tmpVariables[2] = KMKeyParameters.exp(); - KMArray.cast(tmpVariables[1]).add((short) 0, tmpVariables[2]); // Key Params + KMArray.cast(tmpVariables[1]).add((short) 0, tmpVariables[2]); // Key Params of wrapped key 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 @@ -770,135 +770,138 @@ private void processImportWrappedKeyCmd(APDU apdu) { 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 + KMArray.cast(tmpVariables[1]).add((short) 9, KMByteBlob.exp()); // Wrapped Key ASSOCIATED AUTH DATA + KMArray.cast(tmpVariables[1]).add((short) 10, KMInteger.exp()); // Password Sid + KMArray.cast(tmpVariables[1]).add((short) 11, KMInteger.exp()); // Biometric Sid + short i = KMArray.cast(tmpVariables[1]).length(); // 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) { + short args = decoder.decode(tmpVariables[1], buffer, bufferStartOffset, bufferLength); + // Step -0 - check whether the key format and algorithm supported + // read algorithm + tmpVariables[0] = KMArray.cast(args).get((short) 0); + tmpVariables[1] = KMEnumTag.getValue(KMType.ALGORITHM, tmpVariables[0]); + // read key format + tmpVariables[2] = KMArray.cast(args).get((short) 1); + tmpVariables[2] = KMEnum.cast(tmpVariables[2]).getVal(); + // import of RSA and EC not supported with pkcs8 or x509 format + if ((tmpVariables[1] == KMType.RSA || tmpVariables[1] == KMType.EC) && + (tmpVariables[2] != 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) { + // Step -1 parse the wrapping key blob + // read wrapping key blob + data[KEY_BLOB] = KMArray.cast(args).get((short) 6); + // read un wrapping key params + data[KEY_PARAMETERS] = KMArray.cast(args).get((short) 8); + // Read App Id and App Data if any from un wrapping key params + data[APP_ID] = KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.APPLICATION_ID, data[KEY_PARAMETERS]); + data[APP_DATA] = KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.APPLICATION_DATA, data[KEY_PARAMETERS]); + if (data[APP_ID] != 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) { + if (data[APP_DATA] != KMTag.INVALID_VALUE) { data[APP_DATA] = KMByteTag.cast(tmpVariables[3]).getValue(); } - // wrapping key blob - data[KEY_BLOB] = KMArray.cast(tmpVariables[2]).get((short) 7); + // parse the wrapping key blob 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); + // check whether the wrapping key is RSA with purpose KEY_WRAP, padding RSA_OAEP and Digest SHA2_256. + if(KMEnumTag.getValue(KMType.ALGORITHM,data[HW_PARAMETERS]) != KMType.RSA ){ + KMException.throwIt(KMError.UNSUPPORTED_KEY_ENCRYPTION_ALGORITHM); } - if (!(KMEnumArrayTag.contains(KMType.DIGEST, KMType.SHA2_256, data[HW_PARAMETERS]))) { + 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]))) { + if(!KMEnumArrayTag.contains(KMType.PADDING,KMType.RSA_OAEP, data[HW_PARAMETERS])){ KMException.throwIt(KMError.INCOMPATIBLE_PADDING_MODE); } + if(!KMEnumArrayTag.contains(KMType.PURPOSE,KMType.WRAP_KEY, data[HW_PARAMETERS])){ + KMException.throwIt((KMError.INCOMPATIBLE_PURPOSE)); + } + // Step 2 - decrypt the encrypted transport key - 32 bytes AES-GCM key + // create rsa decipher KMCipher cipher = - cryptoProvider.createRsaDecipher( - 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()); + cryptoProvider.createRsaDecipher( + KMCipher.PAD_PKCS1, // TODO remove this when KMCipher.PAD_PKCS1_OAEP_SHA256 is supported + 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()); + //read encrypted transport key from args + tmpVariables[0] = KMArray.cast(args).get((short) 5); // 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]); + tmpVariables[1] = + cipher.doFinal( + KMByteBlob.cast(tmpVariables[0]).getBuffer(), + KMByteBlob.cast(tmpVariables[0]).getStartOff(), + KMByteBlob.cast(tmpVariables[0]).length(), + scratchPad, + (short) 0); + data[SECRET] = KMByteBlob.instance(scratchPad, (short) 0, tmpVariables[1]); cryptoProvider.delete(cipher); - - // Step 3 - XOR with masking key - tmpVariables[4] = KMByteBlob.cast(data[MASKING_KEY]).length(); - if (tmpVariables[3] != tmpVariables[4]) { + // Step 3 - XOR the decrypted AES-GCM key with with masking key + // read masking key + tmpVariables[0] = KMArray.cast(args).get((short) 7); + tmpVariables[1] = KMByteBlob.cast(tmpVariables[0]).length(); + // Length of masking key and transport key must be same. + if (tmpVariables[1] != KMByteBlob.cast(data[SECRET]).length()) { 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); + tmpVariables[2] = 0; // index + // Xor every byte of masking and key and store the result in data[SECRET] + while (tmpVariables[2] < tmpVariables[1]) { + tmpVariables[3] = (short)(((short)KMByteBlob.cast(tmpVariables[0]).get(tmpVariables[2]))&0x00FF); + tmpVariables[4] = (short)(((short)KMByteBlob.cast(data[SECRET]).get(tmpVariables[2]))&0x00FF); + KMByteBlob.cast(data[SECRET]).add(tmpVariables[2], (byte)(tmpVariables[3]^tmpVariables[4])); + tmpVariables[2]++; + } + // Step 4 - AES-GCM decrypt the wrapped key + data[INPUT_DATA] = KMArray.cast(args).get((short) 2); + data[AUTH_DATA] = KMArray.cast(args).get((short) 9); + data[AUTH_TAG] = KMArray.cast(args).get((short) 3); + data[NONCE] = KMArray.cast(args).get((short) 4); + Util.arrayFillNonAtomic(scratchPad,(short)0, KMByteBlob.cast(data[INPUT_DATA]).length(),(byte)0); AESKey key = - cryptoProvider.createAESKey( - KMByteBlob.cast(data[SECRET]).getBuffer(), - KMByteBlob.cast(data[SECRET]).getStartOff(), - KMByteBlob.cast(data[SECRET]).length()); + 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()); + cryptoProvider.aesGCMDecrypt( + key, + KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), + KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), + KMByteBlob.cast(data[INPUT_DATA]).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. + // Step 5 - Import decrypted key data[ORIGIN] = KMType.SECURELY_IMPORTED; - data[KEY_PARAMETERS] = KMArray.cast(tmpVariables[2]).get((short) 0); - importKey(apdu, scratchPad); + data[KEY_PARAMETERS] = KMArray.cast(args).get((short) 0); + // create key blob array + data[IMPORTED_KEY_BLOB] = KMArray.instance((short)1); + // add the byte blob containing decrypted input data + KMArray.cast(data[IMPORTED_KEY_BLOB]).add((short)0, + KMByteBlob.instance(scratchPad,(short)0, KMByteBlob.cast(data[INPUT_DATA]).length())); + // encode the key blob + tmpVariables[0] = repository.alloc((short)(KMByteBlob.cast(data[INPUT_DATA]).length()+16)); + tmpVariables[1] = encoder.encode(data[IMPORTED_KEY_BLOB],repository.getHeap(),tmpVariables[0]); + data[IMPORTED_KEY_BLOB] = KMByteBlob.instance(repository.getHeap(), tmpVariables[0], tmpVariables[1]); + importKey(apdu,scratchPad); } private void processAttestKeyCmd(APDU apdu) {} diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMRepository.java b/Applet/Applet/src/com/android/javacard/keymaster/KMRepository.java index 696b06f0..95355542 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMRepository.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMRepository.java @@ -23,7 +23,7 @@ public class KMRepository { //TODO make the sizes configurable - public static final short HEAP_SIZE = 0x2000; + public static final short HEAP_SIZE = 10000; 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; diff --git a/Applet/Applet/test/com/android/javacard/test/KMVTSTest.java b/Applet/Applet/test/com/android/javacard/test/KMVTSTest.java index 5ceb6aab..f79a3fc1 100644 --- a/Applet/Applet/test/com/android/javacard/test/KMVTSTest.java +++ b/Applet/Applet/test/com/android/javacard/test/KMVTSTest.java @@ -40,6 +40,7 @@ import com.licel.jcardsim.utils.AIDUtil; import javacard.framework.AID; import javacard.framework.Util; +import javacard.security.AESKey; import javacard.security.ECPrivateKey; import javacard.security.ECPublicKey; import javacard.security.KeyPair; @@ -142,7 +143,7 @@ private void cleanUp(){ } private CommandAPDU encodeApdu(byte ins, short cmd){ - byte[] buf = new byte[1024]; + byte[] buf = new byte[2048]; buf[0] = (byte)0x80; buf[1] = ins; buf[2] = (byte)0x40; @@ -467,11 +468,12 @@ private short generateRsaKey(byte[] clientId, byte[] appData){ KMByteBlob.cast(byteBlob).add((short)3, KMType.RSA_PSS); KMByteBlob.cast(byteBlob).add((short)4, KMType.PADDING_NONE); short padding = KMEnumArrayTag.instance(KMType.PADDING, byteBlob); - byteBlob = KMByteBlob.instance((short)4); + byteBlob = KMByteBlob.instance((short)5); KMByteBlob.cast(byteBlob).add((short)0, KMType.SIGN); KMByteBlob.cast(byteBlob).add((short)1, KMType.VERIFY); KMByteBlob.cast(byteBlob).add((short)2, KMType.ENCRYPT); KMByteBlob.cast(byteBlob).add((short)3, KMType.DECRYPT); + KMByteBlob.cast(byteBlob).add((short)4, KMType.WRAP_KEY); short purpose = KMEnumArrayTag.instance(KMType.PURPOSE, byteBlob); byte[] pub = {0,1,0,1}; short rsaPubExpTag = KMIntegerTag.instance(KMType.ULONG_TAG,KMType.RSA_PUBLIC_EXPONENT, KMInteger.uint_32(pub, (short)0)); @@ -690,6 +692,117 @@ public short generateAesDesKey(byte alg, short keysize, byte[] clientId, byte[] Assert.assertEquals(error, KMError.OK); return ret; } + @Test + public void testImportWrappedKey(){ + init(); + byte[] wrappedKey = new byte[16]; + cryptoProvider.newRandomNumber(wrappedKey,(short)0,(short)16); + byte[] encWrappedKey = new byte[16]; + AESKey transportKey = cryptoProvider.createAESKey((short)256); + byte[] transportKeyMaterial = new byte[32]; + cryptoProvider.newRandomNumber(transportKeyMaterial,(short)0,(short)32); + transportKey.setKey(transportKeyMaterial,(short)0); + byte[] nonce = new byte[12]; + cryptoProvider.newRandomNumber(nonce,(short)0,(short)12); + byte[] authData = "Auth Data".getBytes(); + byte[] authTag = new byte[12]; + cryptoProvider.aesGCMEncrypt(transportKey,wrappedKey,(short)0,(short)16,encWrappedKey,(short)0, + nonce,(short)0, (short)12,authData,(short)0,(short)authData.length, + authTag, (short)0, (short)12); + byte[] maskingKey = {1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0}; + byte[] maskedTransportKey = new byte[32]; + for(int i=0; i< maskingKey.length;i++){ + maskedTransportKey[i] = (byte)(transportKeyMaterial[i] ^ maskingKey[i]); + } + short rsaKeyArr = generateRsaKey(null,null); + short keyBlobPtr = KMArray.cast(rsaKeyArr).get((short)1); + byte[] wrappingKeyBlob= new byte[KMByteBlob.cast(keyBlobPtr).length()]; + Util.arrayCopyNonAtomic(KMByteBlob.cast(keyBlobPtr).getBuffer(), + KMByteBlob.cast(keyBlobPtr).getStartOff(), + wrappingKeyBlob,(short)0, (short)wrappingKeyBlob.length); + short inParams = getRsaParams(KMType.DIGEST_NONE, KMType.RSA_PKCS1_1_5_ENCRYPT); + short ret = processMessage(maskedTransportKey, + KMByteBlob.instance(wrappingKeyBlob,(short)0, (short)wrappingKeyBlob.length), + KMType.ENCRYPT, + KMKeyParameters.instance(inParams), + (short)0,null,false + ); + keyBlobPtr = KMArray.cast(ret).get((short)2); + byte[] encTransportKey = new byte[KMByteBlob.cast(keyBlobPtr).length()]; + Util.arrayCopyNonAtomic(KMByteBlob.cast(keyBlobPtr).getBuffer(), KMByteBlob.cast(keyBlobPtr).getStartOff(), + encTransportKey,(short)0, (short)encTransportKey.length); + short tagCount = 7; + short arrPtr = KMArray.instance(tagCount); + 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)2); + KMByteBlob.cast(byteBlob).add((short)0, KMType.ECB); + KMByteBlob.cast(byteBlob).add((short)1, KMType.CBC); + short blockModeTag = KMEnumArrayTag.instance(KMType.BLOCK_MODE, byteBlob); + byteBlob = KMByteBlob.instance((short)2); + KMByteBlob.cast(byteBlob).add((short)0, KMType.PKCS7); + KMByteBlob.cast(byteBlob).add((short)1, KMType.PADDING_NONE); + short paddingMode = KMEnumArrayTag.instance(KMType.PADDING, byteBlob); + byteBlob = KMByteBlob.instance((short)2); + KMByteBlob.cast(byteBlob).add((short)0, KMType.ENCRYPT); + KMByteBlob.cast(byteBlob).add((short)1, KMType.DECRYPT); + short purpose = KMEnumArrayTag.instance(KMType.PURPOSE, byteBlob); + short tagIndex = 0; + KMArray.cast(arrPtr).add(tagIndex++, boolTag); + KMArray.cast(arrPtr).add(tagIndex++, keySize); + KMArray.cast(arrPtr).add(tagIndex++, blockModeTag); + KMArray.cast(arrPtr).add(tagIndex++, paddingMode); + KMArray.cast(arrPtr).add(tagIndex++, KMEnumTag.instance(KMType.ALGORITHM, KMType.AES)); + KMArray.cast(arrPtr).add(tagIndex++, purpose); + KMArray.cast(arrPtr).add(tagIndex++, KMBoolTag.instance(KMType.CALLER_NONCE)); + short keyParams = KMKeyParameters.instance(arrPtr); + short nullParams = KMArray.instance((short)0); + nullParams = KMKeyParameters.instance(nullParams); + short arr = KMArray.instance((short)12); + KMArray.cast(arr).add((short) 0, keyParams); // Key Params of wrapped key + KMArray.cast(arr).add((short) 1, KMEnum.instance(KMType.KEY_FORMAT,KMType.RAW)); // Key Format + KMArray.cast(arr).add((short) 2, KMByteBlob.instance(encWrappedKey,(short)0,(short)encWrappedKey.length)); // Wrapped Import Key Blob + KMArray.cast(arr).add((short) 3, KMByteBlob.instance(authTag,(short)0,(short)authTag.length)); // Auth Tag + KMArray.cast(arr).add((short) 4, KMByteBlob.instance(nonce,(short)0,(short)nonce.length)); // IV - Nonce + KMArray.cast(arr).add((short) 5, KMByteBlob.instance(encTransportKey,(short)0,(short)encTransportKey.length)); // Encrypted Transport Key + KMArray.cast(arr).add((short) 6, KMByteBlob.instance(wrappingKeyBlob,(short)0, (short)wrappingKeyBlob.length)); // Wrapping Key KeyBlob + KMArray.cast(arr).add((short) 7, KMByteBlob.instance(maskingKey,(short)0,(short)maskingKey.length)); // Masking Key + KMArray.cast(arr).add((short) 8, nullParams); // Un-wrapping Params + KMArray.cast(arr).add((short) 9, KMByteBlob.instance(authData,(short)0,(short)authData.length)); // Wrapped Key ASSOCIATED AUTH DATA + KMArray.cast(arr).add((short) 10, KMInteger.uint_8((byte)0)); // Password Sid + KMArray.cast(arr).add((short) 11, KMInteger.uint_8((byte)0)); // Biometric Sid + CommandAPDU apdu = encodeApdu((byte)0x12, arr); + // print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(apdu); + 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.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.SECURELY_IMPORTED); + cleanUp(); + } @Test public void testGetKeyCharacteristicsWithIdDataSuccess() { From 04c08805ab05144e436653da8ec122cef261a804 Mon Sep 17 00:00:00 2001 From: cpathak Date: Thu, 18 Jun 2020 09:47:16 -0700 Subject: [PATCH 2/2] Added getHmacSharingParams, ComputeSharedHmacKey Also fixed vts related requirement for rsa decryption without padding. JCardSim automaticaly removes the leading zeros after decryption. VTS test fails due to this. --- .../javacard/keymaster/KMCipherImpl.java | 27 ++++- .../javacard/keymaster/KMJcardSimulator.java | 16 ++- .../javacard/keymaster/KMKeymasterApplet.java | 51 +++++---- .../javacard/keymaster/KMRepository.java | 52 +++++---- .../com/android/javacard/test/KMVTSTest.java | 100 +++++++++++++++++- Applet/default.output | 12 +-- 6 files changed, 199 insertions(+), 59 deletions(-) diff --git a/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMCipherImpl.java b/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMCipherImpl.java index ec34fd32..a1879401 100644 --- a/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMCipherImpl.java +++ b/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMCipherImpl.java @@ -1,19 +1,34 @@ package com.android.javacard.keymaster; -import com.android.javacard.keymaster.KMCipher; +import javacard.framework.Util; +import javacard.security.CryptoException; import javacardx.crypto.Cipher; + public class KMCipherImpl extends KMCipher{ Cipher cipher; short cipherAlg; short paddingAlg; + short mode; 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); + short len = cipher.doFinal(buffer, startOff, length, scratchPad, i); + // JCard Sim removes leading zeros during decryption in case of no padding - we add that back. + // TODO confirm whether this is fine to pass the VTS. + if(cipherAlg == Cipher.ALG_RSA_NOPAD && + mode == Cipher.MODE_DECRYPT && + len < 256){ + byte[] tempBuf = new byte[256]; + Util.arrayFillNonAtomic(tempBuf,(short)0, (short)256, (byte)0); + Util.arrayCopyNonAtomic(scratchPad,(short)0,tempBuf, (short)(i+256-len),len ); + Util.arrayCopyNonAtomic(tempBuf,(short)0,scratchPad,i,(short)256); + len = 256; + } + return len; } @Override @@ -39,4 +54,12 @@ public short getPaddingAlgorithm() { public void setPaddingAlgorithm(short alg) { paddingAlg = alg; } + + public short getMode() { + return mode; + } + + public void setMode(short mode) { + this.mode = mode; + } } diff --git a/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMJcardSimulator.java b/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMJcardSimulator.java index f9649564..fbc354eb 100644 --- a/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMJcardSimulator.java +++ b/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMJcardSimulator.java @@ -480,7 +480,11 @@ public KMCipher createRsaDecipher(short padding, byte[] secret, short secretStar key.setExponent(secret,secretStart,secretLength); key.setModulus(modBuffer, modOff, modLength); rsaCipher.init(key,Cipher.MODE_DECRYPT); - return new KMCipherImpl(rsaCipher); + KMCipherImpl inst = new KMCipherImpl(rsaCipher); + inst.setCipherAlgorithm(cipherAlg); + inst.setMode(Cipher.MODE_DECRYPT); + inst.setPaddingAlgorithm(padding); + return inst; } @Override @@ -574,9 +578,10 @@ public KMCipher createSymmetricCipher(short cipherAlg, short mode, short padding CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); break; } - KMCipher cipher = new KMCipherImpl(symmCipher); + KMCipherImpl cipher = new KMCipherImpl(symmCipher); cipher.setCipherAlgorithm(cipherAlg); cipher.setPaddingAlgorithm(padding); + cipher.setMode(mode); return cipher; } @@ -598,6 +603,7 @@ public KMCipher createGCMCipher(short mode, byte[] secret, short secretStart, sh if(secretLength == 32){ len = KeyBuilder.LENGTH_AES_256; } + return new KMCipherImpl(null); } @@ -733,7 +739,11 @@ public KMCipher createRsaCipher(short padding, byte[] modBuffer, short modOff, s key.setExponent(exponent,(short)0,(short)3); key.setModulus(modBuffer, modOff, modLength); rsaCipher.init(key,Cipher.MODE_ENCRYPT); - return new KMCipherImpl(rsaCipher); + KMCipherImpl inst = new KMCipherImpl(rsaCipher); + inst.setCipherAlgorithm(cipherAlg); + inst.setMode(Cipher.MODE_ENCRYPT); + inst.setPaddingAlgorithm(padding); + return inst; } @Override diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java b/Applet/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java index b1baf675..7200a0a1 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java @@ -167,24 +167,29 @@ protected KMKeymasterApplet() { setBootParamsDone = false; byte[] buf = JCSystem.makeTransientByteArray( - repository.HMAC_SEED_NONCE_SIZE, JCSystem.CLEAR_ON_DESELECT); + (short)32, JCSystem.CLEAR_ON_DESELECT); keymasterState = KMKeymasterApplet.INSTALL_STATE; data = JCSystem.makeTransientShortArray((short) DATA_ARRAY_SIZE, JCSystem.CLEAR_ON_RESET); repository = new KMRepository(); tmpVariables = JCSystem.makeTransientShortArray((short) TMP_VARIABLE_ARRAY_SIZE, JCSystem.CLEAR_ON_RESET); Util.arrayCopyNonAtomic( - cryptoProvider.getTrueRandomNumber(repository.HMAC_SEED_NONCE_SIZE), + cryptoProvider.getTrueRandomNumber(repository.MASTER_KEY_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); + repository.MASTER_KEY_SIZE); + repository.initMasterKey(buf, repository.MASTER_KEY_SIZE); + cryptoProvider.newRandomNumber(buf, (short) 0, repository.SHARED_SECRET_KEY_SIZE); // TODO remove this when key agreement protocol is implemented. - repository.initHmacKey(buf, repository.HMAC_SEED_NONCE_SIZE); + repository.initHmacSharedSecretKey(buf, repository.SHARED_SECRET_KEY_SIZE); + // TODO currently hmac nonce is generated once when installing the applet. Remove this once boot + // singnal reception is incorporated in the design. cryptoProvider.newRandomNumber(buf, (short) 0, repository.HMAC_SEED_NONCE_SIZE); - repository.initHmacSeed(buf, repository.HMAC_SEED_NONCE_SIZE); + repository.initHmacNonce(buf, (short)0, repository.HMAC_SEED_NONCE_SIZE); + // TODO Confirm before removing seed generation. + //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(); @@ -514,22 +519,18 @@ 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]); + KMHmacSharingParameters.cast(tmpVariables[2]).setNonce( + KMByteBlob.instance(repository.getHmacNonce(), (short) 0, + repository.HMAC_SEED_NONCE_SIZE)); + KMHmacSharingParameters.cast(tmpVariables[2]).setSeed(KMByteBlob.instance((short)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); + bufferLength = encoder.encode(tmpVariables[3], buffer, bufferStartOffset); sendOutgoing(apdu); } @@ -584,14 +585,12 @@ 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 of hmac params - KMArray.cast(tmpVariables[1]).add((short) 1, tmpVariables[2]); // Key Params + tmpVariables[1] = KMHmacSharingParameters.exp(); + tmpVariables[0] = KMArray.exp(tmpVariables[1]); + tmpVariables[2] = KMArray.instance((short)1); + KMArray.cast(tmpVariables[2]).add((short) 0, tmpVariables[0]); // Vector of hmac params // Decode the arguments - tmpVariables[2] = decoder.decode(tmpVariables[1], buffer, bufferStartOffset, bufferLength); - data[KEY_PARAMETERS] = KMArray.cast(tmpVariables[2]).get((short) 1); + tmpVariables[2] = decoder.decode(tmpVariables[2], buffer, bufferStartOffset, bufferLength); data[HMAC_SHARING_PARAMS] = KMArray.cast(tmpVariables[2]).get((short) 0); // Concatenate HMAC Params tmpVariables[0] = 0; @@ -604,7 +603,7 @@ private void processComputeSharedHmacCmd(APDU apdu) { 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*/) { + if (tmpVariables[4] == repository.HMAC_SEED_NONCE_SIZE) { // then copy that to scratchPad Util.arrayCopyNonAtomic( KMByteBlob.cast(tmpVariables[3]).getBuffer(), @@ -618,7 +617,7 @@ private void processComputeSharedHmacCmd(APDU apdu) { 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*/) { + if (tmpVariables[4] != repository.HMAC_SEED_NONCE_SIZE) { KMException.throwIt(KMError.INVALID_ARGUMENT); } // copy nonce to scratchPad @@ -634,7 +633,7 @@ private void processComputeSharedHmacCmd(APDU apdu) { // ckdf to derive hmac key - scratch pad has the context HMACKey key = cryptoProvider.cmacKdf( - repository.getHmacKey(), ckdfLable , scratchPad, (short) 0, tmpVariables[5]); + repository.getSharedKey(), ckdfLable , scratchPad, (short) 0, tmpVariables[5]); tmpVariables[5] = key.getKey(scratchPad, (short) 0); repository.initComputedHmac(scratchPad, (short) 0, tmpVariables[5]); // Generate sharingKey verification diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMRepository.java b/Applet/Applet/src/com/android/javacard/keymaster/KMRepository.java index 95355542..350bd97a 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMRepository.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMRepository.java @@ -26,7 +26,9 @@ public class KMRepository { public static final short HEAP_SIZE = 10000; 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 MASTER_KEY_SIZE = 16; + public static final short SHARED_SECRET_KEY_SIZE = 16; + public static final short HMAC_SEED_NONCE_SIZE = 32; public static final short MAX_OPS = 4; public static final short COMPUTED_HMAC_KEY_SIZE = 32; // Boot params constants @@ -36,7 +38,7 @@ public class KMRepository { private static KMRepository repository; private byte[] masterKey; private byte[] hmacSeed; - private byte[] hmacKey; + private byte[] sharedKey; private byte[] computedHmacKey; private byte[] hmacNonce; private byte[] heap; @@ -100,41 +102,49 @@ public void releaseOperation(KMOperationState op){ } public void initMasterKey(byte[] key, short len) { if (masterKey == null) { - masterKey = new byte[len]; + masterKey = new byte[MASTER_KEY_SIZE]; + if(len != MASTER_KEY_SIZE) ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); 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 initHmacSharedSecretKey(byte[] key, short len) { + if (sharedKey == null) { + sharedKey = new byte[SHARED_SECRET_KEY_SIZE]; } + if(len != SHARED_SECRET_KEY_SIZE) KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + Util.arrayCopy(key, (short) 0, sharedKey, (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); + computedHmacKey = new byte[COMPUTED_HMAC_KEY_SIZE]; } + if(len != COMPUTED_HMAC_KEY_SIZE) KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + 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) { + hmacNonce = new byte[HMAC_SEED_NONCE_SIZE]; + } + if (len != HMAC_SEED_NONCE_SIZE) { KMException.throwIt(KMError.INVALID_INPUT_LENGTH); } - Util.arrayCopy(nonce, (short) 0, hmacSeed, (short) 0, len); + Util.arrayCopy(nonce, (short) 0, hmacNonce, (short) 0, len); } - + /* TODO according to hal specs seed should always be empty. + Confirm this before removing the code as it is also specified that keymasterdevice with storage + must store and return the seed. + public void initHmacSeed(byte[] seed, short len) { + if (hmacSeed == null) { + hmacSeed = new byte[HMAC_SEED_NONCE_SIZE]; + } + if(len != HMAC_SEED_NONCE_SIZE) KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + Util.arrayCopy(seed, (short) 0, hmacSeed, (short) 0, len); + } +*/ public void onUninstall() { // TODO change this Util.arrayFillNonAtomic(masterKey, (short) 0, (short) masterKey.length, (byte) 0); @@ -171,8 +181,8 @@ public byte[] getHmacSeed() { return hmacSeed; } - public byte[] getHmacKey() { - return hmacKey; + public byte[] getSharedKey() { + return sharedKey; } public byte[] getHmacNonce() { diff --git a/Applet/Applet/test/com/android/javacard/test/KMVTSTest.java b/Applet/Applet/test/com/android/javacard/test/KMVTSTest.java index f79a3fc1..cc433d8d 100644 --- a/Applet/Applet/test/com/android/javacard/test/KMVTSTest.java +++ b/Applet/Applet/test/com/android/javacard/test/KMVTSTest.java @@ -29,6 +29,7 @@ import com.android.javacard.keymaster.KMEnumTag; import com.android.javacard.keymaster.KMError; import com.android.javacard.keymaster.KMHardwareAuthToken; +import com.android.javacard.keymaster.KMHmacSharingParameters; import com.android.javacard.keymaster.KMInteger; import com.android.javacard.keymaster.KMIntegerTag; import com.android.javacard.keymaster.KMKeyCharacteristics; @@ -692,6 +693,91 @@ public short generateAesDesKey(byte alg, short keysize, byte[] clientId, byte[] Assert.assertEquals(error, KMError.OK); return ret; } + + @Test + public void testComputeHmacParams(){ + init(); + short params1 = KMHmacSharingParameters.instance(); + KMHmacSharingParameters.cast(params1).setSeed(KMByteBlob.instance((short)0)); + short num = KMByteBlob.instance((short)32); + cryptoProvider.newRandomNumber( + KMByteBlob.cast(num).getBuffer(), + KMByteBlob.cast(num).getStartOff(), + KMByteBlob.cast(num).length()); + KMHmacSharingParameters.cast(params1).setNonce(num); + short params2 = KMHmacSharingParameters.instance(); + KMHmacSharingParameters.cast(params2).setSeed(KMByteBlob.instance((short)0)); + num = KMByteBlob.instance((short)32); + cryptoProvider.newRandomNumber( + KMByteBlob.cast(num).getBuffer(), + KMByteBlob.cast(num).getStartOff(), + KMByteBlob.cast(num).length()); + KMHmacSharingParameters.cast(params2).setNonce(num); + short arr = KMArray.instance((short)2); + KMArray.cast(arr).add((short)0, params1); + KMArray.cast(arr).add((short)1,params2); + short arrPtr = KMArray.instance((short)1); + KMArray.cast(arrPtr).add((short)0,arr); + CommandAPDU apdu = encodeApdu((byte)0x19, arrPtr); + // print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(apdu); + Assert.assertEquals(0x9000, response.getSW()); + short ret = KMArray.instance((short) 2); + KMArray.cast(ret).add((short) 0, KMInteger.exp()); + KMArray.cast(ret).add((short)1, KMByteBlob.exp()); + 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(); + Assert.assertEquals(0x9000, response.getSW()); + Assert.assertEquals(error, KMError.OK); + + cleanUp(); + } + @Test + public void testGetHmacSharingParams(){ + init(); + CommandAPDU commandAPDU = new CommandAPDU(0x80, 0x1C, 0x40, 0x00); + //print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(commandAPDU); + KMDecoder dec = new KMDecoder(); + short ret = KMArray.instance((short) 2); + KMArray.cast(ret).add((short) 0, KMInteger.exp()); + short inst = KMHmacSharingParameters.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); + short error = KMInteger.cast(KMArray.cast(ret).get((short)0)).getShort(); + KMHmacSharingParameters params = KMHmacSharingParameters.cast(KMArray.cast(ret).get((short)1)); + short seed = params.getSeed(); + short nonce = params.getNonce(); + Assert.assertTrue(KMByteBlob.cast(seed).length() == 0); + Assert.assertTrue(KMByteBlob.cast(nonce).length() == 32); + //print(seed); + //print(nonce); + Assert.assertEquals(error, KMError.OK); + cleanUp(); + } + public short[] getHmacSharingParams(){ + CommandAPDU commandAPDU = new CommandAPDU(0x80, 0x1C, 0x40, 0x00); + //print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(commandAPDU); + KMDecoder dec = new KMDecoder(); + short ret = KMArray.instance((short) 2); + KMArray.cast(ret).add((short) 0, KMInteger.exp()); + short inst = KMHmacSharingParameters.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); + short error = KMInteger.cast(KMArray.cast(ret).get((short)0)).getShort(); + KMHmacSharingParameters params = KMHmacSharingParameters.cast(KMArray.cast(ret).get((short)1)); + short seed = params.getSeed(); + short nonce = params.getNonce(); + return new short[]{seed, nonce}; + } + @Test public void testImportWrappedKey(){ init(); @@ -1280,8 +1366,10 @@ public void testEncryptDecryptWithRsa(byte digest, byte padding){ (short)0,null,false ); keyBlobPtr = KMArray.cast(ret).get((short)2); + short len = KMByteBlob.cast(keyBlobPtr).length(); + short start = KMByteBlob.cast(keyBlobPtr).getStartOff(); short equal = Util.arrayCompare(plainData,(short)0,KMByteBlob.cast(keyBlobPtr).getBuffer(), - KMByteBlob.cast(keyBlobPtr).getStartOff(),(short)plainData.length); + (short)(start+len-plainData.length),(short)plainData.length); Assert.assertTrue(equal == 0); } @@ -1586,5 +1674,15 @@ public short update(short operationHandle, short data, short inParams, short hwT } return ret; } + private void print(short blob){ + print(KMByteBlob.cast(blob).getBuffer(),KMByteBlob.cast(blob).getStartOff(),KMByteBlob.cast(blob).length()); + } + private void print(byte[] buf, short start, short length){ + StringBuilder sb = new StringBuilder(); + for(int i = start; i < (start+length); i++){ + sb.append(String.format(" 0x%02X", buf[i])) ; + } + System.out.println(sb.toString()); + } } diff --git a/Applet/default.output b/Applet/default.output index 6d9f7ea9..f17d2ad6 100644 --- a/Applet/default.output +++ b/Applet/default.output @@ -8,13 +8,13 @@ CLA: 80, INS: 23, P1: 40, P2: 00, Lc: 3b, 83, a1, 1a, 10, 00, 00, 02, 01, 00, 58 CLA: 80, INS: 24, P1: 40, P2: 00, Lc: 49, 86, 01, 01, 58, 20, 30, 30, 30, 31, 31, 31, 32, 32, 32, 33, 33, 33, 34, 34, 34, 35, 35, 35, 36, 36, 36, 37, 37, 37, 38, 38, 38, 39, 39, 39, 30, 30, 58, 20, 30, 30, 30, 31, 31, 31, 32, 32, 32, 33, 33, 33, 34, 34, 34, 35, 35, 35, 36, 36, 36, 37, 37, 37, 38, 38, 38, 39, 39, 39, 30, 30, 02, 00, Le: 00, SW1: 90, SW2: 00 CLA: 80, INS: 1e, P1: 40, P2: 00, Lc: 00, Le: 21, 83, 02, 57, 4a, 61, 76, 61, 63, 61, 72, 64, 4b, 65, 79, 6d, 61, 73, 74, 65, 72, 44, 65, 76, 69, 63, 65, 46, 47, 6f, 6f, 67, 6c, 65, SW1: 90, SW2: 00 CLA: 80, INS: 18, P1: 40, P2: 00, Lc: 23, 81, 58, 20, 00, 01, 02, 03, 04, 05, 06, 07, 08, 09, 0a, 0b, 0c, 0d, 0e, 0f, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 1a, 1b, 1c, 1d, 1e, 1f, Le: 00, SW1: 90, SW2: 00 -CLA: 80, INS: 10, P1: 40, P2: 00, Lc: 22, 81, a4, 1a, 10, 00, 00, 02, 01, 1a, 30, 00, 00, 03, 19, 08, 00, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 20, 00, 00, 01, 42, 02, 03, Le: 0d, 83, 00, 58, d4, 85, 58, 40, dc, 1d, ed, 50, 93, 56, 0c, 97, de, e7, 37, 82, 41, be, cc, 97, 19, 46, 6f, 18, 3e, 55, 1d, 95, b5, a2, 32, d6, 8d, c3, f9, ab, 16, e5, 8d, 7f, 8a, fe, 09, 86, f0, 83, 04, 50, ef, e1, 8d, 4e, d3, a9, 53, 99, b8, d4, bc, ad, 8f, a2, 5e, 8a, 84, 1a, ea, 29, 4c, 00, 47, 73, 4c, ad, c1, 48, a8, 52, e8, 13, dd, 4c, 48, c7, 53, a4, 74, a5, 3d, 80, 5c, 1a, ab, 92, 82, a0, a7, 1a, 10, 00, 00, 02, 01, 1a, 30, 00, 00, 03, 19, 08, 00, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 20, 00, 00, 01, 42, 02, 03, 1a, 10, 00, 02, be, 00, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, 58, 40, b4, 05, c4, 0e, 08, 15, 0e, a5, 86, 84, b2, 03, 00, 2c, cb, d7, 23, 85, c9, c1, 0b, 74, cb, 20, 10, b1, 25, 8e, 4b, 38, d4, 72, da, 2c, 2c, 64, 0e, e9, 2f, b2, d6, 74, 2e, 02, 77, 69, 74, 9f, a9, 98, e7, 7a, 4f, 9e, d4, 06, 62, 0c, 33, 6e, 80, 3a, 3f, e3, 82, a0, a7, 1a, 10, 00, 00, 02, 01, 1a, 30, 00, 00, 03, 19, 08, 00, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 20, 00, 00, 01, 42, 02, 03, 1a, 10, 00, 02, be, 00, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, SW1: 90, SW2: 00 +CLA: 80, INS: 10, P1: 40, P2: 00, Lc: 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, 9b, 0f, 38, 9f, 66, 4c, 5f, 7b, 78, c1, dd, 92, 19, c3, 01, 4c, 09, 46, 81, 1b, 1f, cf, b0, 11, 7d, 09, f2, 0a, df, 50, c4, 3c, e6, a8, 17, 90, 08, b5, da, 01, b4, e3, b2, 0e, df, fa, b7, a5, e9, de, 61, a6, 9c, 25, 98, 7f, c8, 61, 91, 9d, 65, 4c, d2, 85, 4c, 1a, fe, c5, 1e, 49, 48, 98, 03, b6, 89, 79, 3d, 4c, ad, 87, d8, b3, d9, 4b, 26, df, 17, 5f, 33, af, 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: 01, 03, 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, ac, 08, ac, 35, c5, b0, 8e, f4, 8a, 74, 5a, b7, ce, 25, 5e, 1e, 65, 8f, 17, ad, 86, 5f, 58, 4a, 4c, 7d, 9f, c1, 98, e9, 17, 75, c2, ca, c2, 4a, 74, 4c, 38, 78, 4e, 7e, 14, 6c, 55, dc, 46, 1b, da, 0e, 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, 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, 2a, 68, d4, 23, 13, 38, fd, 90, 20, 22, 41, 5b, 62, 6e, b8, a6, 50, 82, 36, 4f, a9, bd, f9, 20, 4c, 41, 3c, 08, 97, 5d, 3a, 1c, fa, 1f, a1, a7, 35, 4c, 76, e2, 2b, 00, 6a, 62, 1d, 03, 31, dc, e8, 00, 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: 01, 03, 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, c9, 2e, be, 94, f0, 2b, e7, 57, 9d, 90, 5b, b0, 08, 1c, b7, 93, 4c, ae, 8e, 3e, 17, 1d, 67, 15, cc, 16, e8, f2, c4, 4c, de, 14, d8, 8e, 94, 0e, 50, 63, ee, 34, 4e, 4f, 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, 47, 58, 70, e8, fd, 81, ea, a4, eb, 96, 3b, 69, 1b, 6d, fe, b2, 9d, 99, 4c, dd, 42, 4c, 49, dc, 8d, 98, bc, ca, 2a, d1, 51, 2b, ad, ab, f1, 3f, 8e, 8e, db, f1, 6a, ec, 87, 36, af, c3, b2, 9e, ba, 14, 92, c9, 84, d2, 96, 5c, fa, 73, 02, 98, 49, 08, 7e, 33, 4c, 11, 7d, e1, 4b, ab, 5e, d5, 21, e2, d5, d0, b9, 4c, a6, 49, cb, d7, 6f, 01, 98, cd, 8e, c4, 74, 8a, 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, a3, b5, 4b, b2, 54, 26, 92, ba, 13, e7, 2a, 8c, 04, fa, 7e, 5c, 74, 1e, 12, fb, 6f, 59, 7a, 27, 4c, c7, dd, 10, 77, 31, 33, 35, 0a, 83, 1d, 8b, f1, 4c, 13, 9b, 59, 6d, 06, 55, 6e, e4, a8, c2, bf, 69, 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: 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, ee, 44, 79, 56, 4b, b5, 23, d3, fc, cb, f5, ac, b3, 1b, 67, 72, 4c, 85, eb, 05, b9, cf, 20, e3, 68, b6, 30, 05, b4, 4c, 3f, c7, 86, 13, a1, 05, 35, 4f, 33, 0c, 92, 49, 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, 2d, 25, 19, 37, 27, a3, 2b, f8, e6, 1b, 53, 3d, 90, 8f, 19, 32, d1, 07, de, f4, 7e, fb, 79, a2, 2f, dc, 6d, 0c, 66, 25, ef, fe, e2, 28, f2, da, db, 33, a2, 83, 5e, 03, b3, aa, 2f, e6, 7b, 14, 29, fb, d9, 60, 89, 9c, c9, 91, 33, 64, fd, a4, 65, a7, 65, 5b, 4c, bf, f0, 84, 10, 07, e4, 0b, cd, bb, 61, 97, f3, 4c, c5, d5, a4, 8f, 2f, c9, 1f, e1, 36, 22, 04, 00, 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, a2, 6f, 0b, 5c, 49, 70, ea, 60, f5, 8e, a0, bd, b7, 43, 07, 50, f0, a7, 16, 5c, ec, 72, 93, 64, 4c, 1f, 07, 60, 2a, 98, 10, 42, 1e, c8, 80, 98, f9, 4c, cb, da, 20, 52, 94, 35, af, 81, 19, da, d6, 9a, 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: 01, 0c, 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, 89, ba, 5c, 77, e1, 0d, 5f, a8, 79, 30, 46, 0a, 77, 68, f7, 95, 4c, 70, dd, 86, 23, 76, 5a, 88, cb, f1, 89, 32, 38, 4c, e2, 41, 13, e8, 3e, 7a, 31, f9, dc, 2c, ae, af, 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: 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, dc, d4, 0f, 18, 4e, 5e, f7, 07, 80, be, f5, 64, 05, a9, da, c6, 4c, c0, 2c, 08, e0, d6, 47, 53, 3a, 2b, 8f, 09, c6, 4c, 15, 98, 4f, 23, c5, df, 67, 75, 7d, 40, 2b, d9, 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: 01, 0c, SW1: 90, SW2: 00