diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMX509Certificate.java b/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMAttestationCertImpl.java similarity index 71% rename from Applet/Applet/src/com/android/javacard/keymaster/KMX509Certificate.java rename to Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMAttestationCertImpl.java index cb5c99a2..60b8272e 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMX509Certificate.java +++ b/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMAttestationCertImpl.java @@ -1,7 +1,7 @@ package com.android.javacard.keymaster; +import javacard.framework.JCSystem; import javacard.framework.Util; -import javacard.security.Signature; // The class encodes strongbox generated amd signed attestation certificate. This only encodes // required fields of the certificates. It is not meant to be generic X509 cert encoder. @@ -9,22 +9,9 @@ // the values. // The certificate is assembled with leafs first and then the sequences. -public class KMX509Certificate { +public class KMAttestationCertImpl implements KMAttestationCert { + private static final byte MAX_PARAMS = 30; // DER encoded object identifiers required by the cert. - // sha256WithRSAEncryption - 1.2.840.113549.1.1.11 - private static final byte[] sha256WithRSAEncryption = { - 0x06, 0x09, 0x2A, (byte) 0x86, 0x48, (byte) 0x86, (byte) 0xF7, 0x0D, 0x01, 0x01, 0x0B - }; - // countryName - 2.5.4.6 - private static final byte[] country = {0x06, 0x03, 0x55, 0x04, 0x06}; - // stateOrProvinceName - 2.5.4.8 - private static final byte[] stateName = {0x06, 0x03, 0x55, 0x04, 0x08}; - // organizationName - 2.5.4.10 - private static final byte[] orgName = {0x06, 0x03, 0x55, 0x04, 0x0A}; - // organizationalUnitName - 2.5.4.11 - private static final byte[] orgUnitName = {0x06, 0x03, 0x55, 0x04, 0x0B}; - // commonName - 2.5.4.3 - private static final byte[] commonName = {0x06, 0x03, 0x55, 0x04, 0x03}; // rsaEncryption - 1.2.840.113549.1.1.1 private static final byte[] rsaEncryption = { 0x06, 0x09, 0x2A, (byte) 0x86, 0x48, (byte) 0x86, (byte) 0xF7, 0x0D, 0x01, 0x01, 0x01 @@ -42,11 +29,6 @@ public class KMX509Certificate { // Authority Key Identifier Extn - 2.5.29.35 private static final byte[] authKeyIdExtn = {0x06, 0x03, 0X55, 0X1D, 0X23}; - // Fixed field values - DER encoded - // Version with value 2 and EXPLICIT id 0 with INTEGER type- DER encoded - private static final byte[] X509Version = {(byte) 0XA0, 0x03, 0x02, 0x01, 0x02}; - // Serial Number with value 1 - private static final byte[] X509SerialNum = {0x02, 0x01, 0x01}; // Signature algorithm identifier - always sha256WithRSAEncryption - 1.2.840.113549.1.1.11 // SEQUENCE of alg OBJ ID and parameters = NULL. private static final byte[] X509SignAlgIdentifier = { @@ -66,20 +48,6 @@ public class KMX509Certificate { 0x05, 0x00 }; - // Issuer field is not fixed but it will be given in the provision command in DER Encoded form - // i.e. subject of the cert for attesting key. Following is the placeholder example sequence of - // 5 elements: C=US, ST=California, O=Google, Inc.,OU=Android, CN=Android Software Attestation Key - // TODO move the following to test - private static final byte[] X509Issuer = { - 0x30, 0x76, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, - 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x0C, 0x0A, 0x43, 0x61, 0x6C, 0x69, 0x66, 0x6F, - 0x72, 0x6E, 0x69, 0x61, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0A, 0x0C, 0x0C, 0x47, - 0x6F, 0x6F, 0x67, 0x6C, 0x65, 0x2C, 0x20, 0x49, 0x6E, 0x63, 0x2E, 0x31, 0x10, 0x30, 0x0E, 0x06, - 0x03, 0x55, 0x04, 0x0B, 0x0C, 0x07, 0x41, 0x6E, 0x64, 0x72, 0x6F, 0x69, 0x64, 0x31, 0x29, 0x30, - 0x27, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0C, 0x20, 0x41, 0x6E, 0x64, 0x72, 0x6F, 0x69, 0x64, 0x20, - 0x53, 0x6F, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x20, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, - 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x4B, 0x65, 0x79 - }; // Validity is not fixed field // Subject is a fixed field with only CN= Android Keystore Key - same for all the keys private static final byte[] X509Subject = { @@ -87,26 +55,6 @@ public class KMX509Certificate { 0x72, 0x6f, 0x69, 0x64, 0x20, 0x4B, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x20, 0x4B, 0x65, 0x79 }; - // Subject Public Key Info is not a fixed field. However the for rsa public key or ec dsa - // public key the algorithm identifier is always the same. - // rsaEncryption - 1.2.840.113549.1.1.1 followed by NULL parameters - private static final byte[] subPubKeyRsaAlgId = { - 0x30, - 0x0D, - 0x06, - 0x09, - 0x2A, - (byte) 0x86, - 0x48, - (byte) 0x86, - (byte) 0xF7, - 0x0D, - 0x01, - 0x01, - 0x01, - 0x05, - 0x00 - }; private static final byte keyUsageSign = (byte) 0x80; // 0 bit private static final byte keyUsageKeyEncipher = (byte) 0x20; // 2nd- bit @@ -122,41 +70,177 @@ public class KMX509Certificate { private static short signatureOffset; private static short tbsOffset; private static short tbsLength; - private static short attChallenge; - private static short attAppId; - private static short hwParams; - private static short swParams; - private static short notBefore; - private static short notAfter; - private static short pubKey; - private static short uniqueId; private static short stackPtr; private static byte[] stack; private static short start; private static short length; - private static KMRepository repo; - +// private static KMRepository repo; + private static short uniqueId; + private static short attChallenge; + private static short notBefore; + private static short notAfter; + private static short pubKey; + private static short[] swParams; + private static short swParamsIndex; + private static short[] hwParams; + private static short hwParamsIndex; + private static byte keyUsage; + private static byte unusedBits; + private static KMAttestationCert inst; + private static boolean rsaCert; + private static byte deviceLocked; + private static short verifiedBootKey; + private static byte verifiedState; + private static short verifiedHash; + private static short authKey; + private static short issuer; + private static short signPriv; + private static short signMod; + + + private KMAttestationCertImpl(){ + } + public static KMAttestationCert instance(boolean rsaCert){ + if(inst == null) inst = new KMAttestationCertImpl(); + init(); + KMAttestationCertImpl.rsaCert = rsaCert; + return inst; + } private static void init() { - if (repo == null) repo = KMRepository.instance(); +// if (repo == null) repo = KMRepository.instance(); stack = null; stackPtr = 0; certStart = 0; signatureOffset = 0; start = 0; length = 0; + tbsLength = 0; + if(swParams == null ) { + swParams = JCSystem.makeTransientShortArray((short)MAX_PARAMS, JCSystem.CLEAR_ON_RESET); + } + if(hwParams == null ) { + hwParams = JCSystem.makeTransientShortArray((short)MAX_PARAMS, JCSystem.CLEAR_ON_RESET); + } + + swParamsIndex = 0; + hwParamsIndex = 0; + keyUsage = 0; + unusedBits = 8; attChallenge = 0; - attAppId = 0; - hwParams = 0; - swParams = 0; notBefore = 0; notAfter = 0; pubKey = 0; uniqueId = 0; - tbsLength = 0; + verifiedBootKey = 0; + verifiedHash = 0; + verifiedState = 0; + rsaCert = true; + deviceLocked = 0; + authKey = 0; + signPriv = 0; + signMod = 0; + } + + @Override + public KMAttestationCert verifiedBootHash(short obj) { + verifiedHash = obj; + return this; + } + + @Override + public KMAttestationCert authKey(short obj) { + authKey = obj; + return this; + } + @Override + public KMAttestationCert verifiedBootKey(short obj) { + verifiedBootKey = obj; + return this; + } + @Override + public KMAttestationCert verifiedState(byte val) { + verifiedState = val; + return this; + } + @Override + public KMAttestationCert uniqueId(short obj) { + uniqueId = obj; + return this; + } + + @Override + public KMAttestationCert notBefore(short obj) { + notBefore = obj; + return this; + } + + @Override + public KMAttestationCert notAfter(short obj) { + notAfter = obj; + return this; + } + + @Override + public KMAttestationCert deviceLocked(boolean val) { + if(val) deviceLocked = (byte)0xFF; + else deviceLocked = 0; + return this; } - public static void encodeCert( + @Override + public KMAttestationCert publicKey(short obj) { + pubKey = obj; + return this; + } + + @Override + public KMAttestationCert attestationChallenge(short obj) { + attChallenge = obj; + return this; + } + + @Override + public KMAttestationCert extensionTag(short tag, boolean hwEnforced) { + if(hwEnforced){ + hwParams[hwParamsIndex] = tag; + hwParamsIndex++; + }else{ + swParams[swParamsIndex] = tag; + swParamsIndex++; + } + if (KMTag.getKey(tag) == KMType.PURPOSE) { + createKeyUsage(tag); + } + return this; + } + + @Override + public KMAttestationCert issuer(short obj) { + issuer = obj; + return this; + } + + private void createKeyUsage(short tag){ + short len = KMEnumArrayTag.cast(tag).length(); + byte index = 0; + while(index < len){ + if(KMEnumArrayTag.cast(tag).get(index) == KMType.SIGN){ + keyUsage = (byte) (keyUsage | keyUsageSign); + }else if(KMEnumArrayTag.cast(tag).get(index) == KMType.WRAP_KEY){ + keyUsage = (byte) (keyUsage | keyUsageKeyEncipher); + } else if (KMEnumArrayTag.cast(tag).get(index) == KMType.DECRYPT) { + keyUsage = (byte) (keyUsage | keyUsageDataEncipher); + } + index++; + } + index = keyUsage; + while(index != 0){ + index = (byte)(index << 1); + unusedBits--; + } + } + private static void encodeCert( short buf, short keyChar, short uniqueId, @@ -171,14 +255,16 @@ public static void encodeCert( start = KMByteBlob.cast(buf).getStartOff(); length = KMByteBlob.cast(buf).length(); stackPtr = (short) (start + length); - KMX509Certificate.attChallenge = attChallenge; - KMX509Certificate.attAppId = attAppId; - KMX509Certificate.hwParams = KMKeyCharacteristics.cast(keyChar).getHardwareEnforced(); - KMX509Certificate.swParams = KMKeyCharacteristics.cast(keyChar).getSoftwareEnforced(); - KMX509Certificate.notBefore = notBefore; - KMX509Certificate.notAfter = notAfter; - KMX509Certificate.pubKey = pubKey; - KMX509Certificate.uniqueId = uniqueId; +/* KMAttestationCertImpl.attChallenge = attChallenge; + KMAttestationCertImpl.attAppId = attAppId; + KMAttestationCertImpl.hwParams = KMKeyCharacteristics.cast(keyChar).getHardwareEnforced(); + KMAttestationCertImpl.swParams = KMKeyCharacteristics.cast(keyChar).getSoftwareEnforced(); + KMAttestationCertImpl.notBefore = notBefore; + KMAttestationCertImpl.notAfter = notAfter; + KMAttestationCertImpl.pubKey = pubKey; + KMAttestationCertImpl.uniqueId = uniqueId; + + */ short last = stackPtr; decrementStackPtr((short)256); signatureOffset = stackPtr; @@ -207,7 +293,12 @@ private static void pushTbsCert(boolean rsaCert) { pushBytes(X509Subject, (short) 0, (short) X509Subject.length); pushValidity(); // issuer - der encoded - pushBytes(repo.getCertDataBuffer(), repo.getIssuer(), repo.getIssuerLen()); +// pushBytes(repo.getCertDataBuffer(), repo.getIssuer(), repo.getIssuerLen()); + pushBytes( + KMByteBlob.cast(issuer).getBuffer(), + KMByteBlob.cast(issuer).getStartOff(), + KMByteBlob.cast(issuer).length() + ); // Algorithm Id pushAlgorithmId(X509SignAlgIdentifier); // Serial Number @@ -223,9 +314,10 @@ private static void pushTbsCert(boolean rsaCert) { } private static void pushExtensions(){ short last = stackPtr; - byte keyusage = 0; - byte unusedBits = 8; +// byte keyusage = 0; +// byte unusedBits = 8; pushAuthKeyId(); + /* if (KMEnumArrayTag.contains(KMType.PURPOSE, KMType.SIGN, hwParams)) { keyusage = (byte) (keyusage | keyUsageSign); unusedBits = 7; @@ -238,7 +330,9 @@ private static void pushExtensions(){ keyusage = (byte) (keyusage | keyUsageDataEncipher); unusedBits = 4; } - if (keyusage != 0) pushKeyUsage(keyusage, unusedBits); + + */ + if (keyUsage != 0) pushKeyUsage(keyUsage, unusedBits); pushKeyDescription(); pushSequenceHeader((short) (last - stackPtr)); // Extensions have explicit tag of [3] @@ -248,13 +342,15 @@ private static void pushExtensions(){ // Time SEQUENCE{UTCTime, UTC or Generalized Time) private static void pushValidity(){ - short last = stackPtr; if (notAfter != 0) { + short last = stackPtr; + if (notAfter != 0) { pushBytes( KMByteBlob.cast(notAfter).getBuffer(), KMByteBlob.cast(notAfter).getStartOff(), KMByteBlob.cast(notAfter).length()); } else { - pushBytes(repo.getCertDataBuffer(), repo.getCertExpiryTime(), repo.getCertExpiryTimeLen()); + //TODO move this to keymaster applet + //pushBytes(repo.getCertDataBuffer(), repo.getCertExpiryTime(), repo.getCertExpiryTimeLen()); } pushTimeHeader(KMByteBlob.cast(notAfter).length()); pushBytes( @@ -276,6 +372,7 @@ private static void pushTimeHeader(short len) { KMException.throwIt(KMError.INVALID_INPUT_LENGTH); } } + // SEQUENCE{SEQUENCE{algId, NULL}, bitString{SEQUENCE{ modulus as positive integer, public exponent // as positive integer} private static void pushRsaSubjectKeyInfo() { @@ -283,11 +380,12 @@ private static void pushRsaSubjectKeyInfo() { pushBytes(pubExponent, (short) 0, (short) pubExponent.length); pushIntegerHeader((short) pubExponent.length); pushBytes( - KMByteBlob.cast(pubKey).getBuffer(), - KMByteBlob.cast(pubKey).getStartOff(), - KMByteBlob.cast(pubKey).length()); + KMByteBlob.cast(pubKey).getBuffer(), + KMByteBlob.cast(pubKey).getStartOff(), + KMByteBlob.cast(pubKey).length()); + // encode modulus as positive if the MSB is 1. - if (KMByteBlob.cast(pubKey).get((short) 0) < 0){ + if (KMByteBlob.cast(pubKey).get((short)0) < 0){ pushByte((byte) 0x00); pushIntegerHeader((short)(KMByteBlob.cast(pubKey).length()+1)); } else { @@ -337,17 +435,16 @@ private static void pushKeyDescription() { pushSWParams(); if (uniqueId != 0) { pushOctetString( - KMByteBlob.cast(uniqueId).getBuffer(), - KMByteBlob.cast(uniqueId).getStartOff(), - KMByteBlob.cast(uniqueId).length()); + KMByteBlob.cast(uniqueId).getBuffer(), + KMByteBlob.cast(uniqueId).getStartOff(), + KMByteBlob.cast(uniqueId).length()); }else{ pushOctetStringHeader((short)0); } - pushOctetString( - KMByteBlob.cast(attChallenge).getBuffer(), - KMByteBlob.cast(attChallenge).getStartOff(), - KMByteBlob.cast(attChallenge).length()); + KMByteBlob.cast(attChallenge).getBuffer(), + KMByteBlob.cast(attChallenge).getStartOff(), + KMByteBlob.cast(attChallenge).length()); pushEnumerated(KMType.STRONGBOX); pushByte(KEYMASTER_VERSION); pushIntegerHeader((short) 1); @@ -362,17 +459,19 @@ private static void pushKeyDescription() { private static void pushSWParams() { short last = stackPtr; - //ATTESTATION_APPLICATION_ID 709 is softwareenforced. + //ATTESTATION_APPLICATION_ID 709 is softwareEnforced. short[] tagIds = {709, 706,705,704,703,702,701,601,600,509,508,507,506,505, 504, 503, 402,401,400,303,200,10,6,5,3,2,1 }; byte index = 0; do { + /* if(tagIds[index] == KMType.ATTESTATION_APPLICATION_ID) { pushAttIds(tagIds[index]); continue; } - pushParams(swParams, tagIds[index]); + */ + pushParams(swParams, swParamsIndex, tagIds[index]); }while(++index < tagIds.length); pushSequenceHeader((short) (last - stackPtr)); } @@ -385,24 +484,21 @@ private static void pushHWParams() { }; byte index = 0; do { - if(pushAttIds(tagIds[index])) continue; + //if(pushAttIds(tagIds[index])) continue; if(tagIds[index] == KMType.ROOT_OF_TRUST) { pushRoT(); continue; } - if(pushParams(hwParams,tagIds[index])) continue; + if(pushParams(hwParams,hwParamsIndex,tagIds[index])) continue; } while (++index < tagIds.length); pushSequenceHeader((short) (last - stackPtr)); } - private static boolean pushParams(short params, short tagId) { + private static boolean pushParams(short[] params, short len, short tagId) { short index = 0; - short arr = KMKeyParameters.cast(params).getVals(); - short len = KMArray.cast(arr).length(); while (index < len) { - short tag = KMArray.cast(arr).get(index); - if(tagId == KMTag.getKey(tag)) { - pushTag(tag); + if(tagId == KMTag.getKey(params[index])) { + pushTag(params[index]); return true; } index++; @@ -477,19 +573,36 @@ private static void pushRoT() { short last = stackPtr; byte val = 0x00; // verified boot hash - pushOctetString(repo.verifiedBootHash, (short) 0, (short) repo.verifiedBootHash.length); + //pushOctetString(repo.verifiedBootHash, (short) 0, (short) repo.verifiedBootHash.length); + pushOctetString( + KMByteBlob.cast(verifiedHash).getBuffer(), + KMByteBlob.cast(verifiedHash).getStartOff(), + KMByteBlob.cast(verifiedHash).length() + ); + /* // verified boot state // TODO change this once verifiedBootState is supported in repo if (repo.selfSignedBootFlag) val = KMType.SELF_SIGNED_BOOT; else if (repo.verifiedBootFlag) val = KMType.VERIFIED_BOOT; else val = KMType.UNVERIFIED_BOOT; + pushEnumerated(val); + + */ + pushEnumerated(verifiedState); // device locked - val = 0x00; + /*val = 0x00; if (repo.deviceLockedFlag) val = (byte) 0xFF; pushBoolean(val); + */ + pushBoolean(deviceLocked); // verified boot Key - pushOctetString(repo.verifiedBootKey, (short) 0, (short) repo.verifiedBootKey.length); + pushOctetString( + KMByteBlob.cast(verifiedBootKey).getBuffer(), + KMByteBlob.cast(verifiedBootKey).getStartOff(), + KMByteBlob.cast(verifiedBootKey).length() + ); + //pushOctetString(repo.verifiedBootKey, (short) 0, (short) repo.verifiedBootKey.length); // Finally sequence header pushSequenceHeader((short) (last - stackPtr)); // ... and tag Id @@ -510,16 +623,9 @@ private static void pushBooleanHeader(short len) { pushLength(len); pushByte((byte) 0x01); } +/* // All Attestation Id tags are byte tags/octet strings private static boolean pushAttIds(short tagId) { - if (attAppId != 0 && KMType.ATTESTATION_APPLICATION_ID == tagId) { - pushBytesTag( - KMType.ATTESTATION_APPLICATION_ID, - KMByteBlob.cast(attAppId).getBuffer(), - KMByteBlob.cast(attAppId).getStartOff(), - KMByteBlob.cast(attAppId).length()); - return true; - } if(!repo.isAttIdSupported()) return true; byte index = 0; while (index < repo.ATT_ID_TABLE_SIZE) { @@ -537,7 +643,7 @@ private static boolean pushAttIds(short tagId) { } return false; } - +*/ // Only SET of INTEGERS supported are padding, digest, purpose and blockmode // All of these are enum array tags i.e. byte long values private static void pushEnumArrayTag(short tagId, byte[] buf, short start, short len) { @@ -663,15 +769,24 @@ private static void pushKeyUsage(byte keyUsage, byte unusedBits) { pushBytes(keyUsageExtn, (short) 0, (short) keyUsageExtn.length); pushSequenceHeader((short) (last - stackPtr)); } + // SEQUENCE {ObjId, OCTET STRING{SEQUENCE{[0]keyIdentifier}}} private static void pushAuthKeyId() { short last = stackPtr; - if (repo.getAuthKeyId() == 0) return; - + //if (repo.getAuthKeyId() == 0) return; + if(authKey == 0) return; + /* pushKeyIdentifier( repo.getCertDataBuffer(), repo.getAuthKeyId(), repo.getAuthKeyIdLen()); // key identifier is [0]'th tagged in a sequence + + */ + pushKeyIdentifier( + KMByteBlob.cast(authKey).getBuffer(), + KMByteBlob.cast(authKey).getStartOff(), + KMByteBlob.cast(authKey).length() + ); pushSequenceHeader((short) (last - stackPtr)); pushOctetStringHeader((short) (last - stackPtr)); pushBytes(authKeyIdExtn, (short) 0, (short) authKeyIdExtn.length); // ObjId @@ -750,28 +865,70 @@ private static void decrementStackPtr(short cnt) { if (start > stackPtr) KMException.throwIt(KMError.UNKNOWN_ERROR); } - public static short sign(Signature signer) { - short ret = signer.sign(stack,tbsOffset,tbsLength,stack,signatureOffset); + public static short sign(KMSEProvider seProv, byte[] privBuf, short privStart, short privLength, + byte[] modBuf, short modStart, short modLength) { + //short ret = signer.sign(stack,tbsOffset,tbsLength,stack,signatureOffset); //print(getBuffer(),getCertStart(),getCertLength()); - return ret; + return seProv.rsaSignPKCS1256(privBuf,privStart,privLength,modBuf,modStart,modLength, + stack,tbsOffset,tbsLength,stack,signatureOffset); } - public static short getCertStart(){ - return certStart; + + + @Override + public KMAttestationCert buffer(byte[] buf, short bufStart, short maxLen){ + stack = buf; + start = bufStart; + length = maxLen; + stackPtr = (short) (start + length); + return this; } - public static short getCertEnd(){ - return (short)(start +length - 1); + + @Override + public KMAttestationCert signingKey(short privKey, short modulus){ + signPriv = privKey; + signMod = modulus; + return this; } - public static short getCertLength(){ - return (short)(getCertEnd() - getCertStart() + 1); + + @Override + public short getCertStart(){ + return certStart; } - public static short getBufferStart(){ - return start; + + @Override + public short getCertEnd(){ + return (short)(start + length -1); } - public static short getBufferLength(){ - return length; + + @Override + public short getCertLength(){ + return (short)(getCertEnd() - getCertStart() + 1); } - public static byte[] getBuffer(){ - return stack; + + @Override + public void build() { + short last = stackPtr; + decrementStackPtr((short)256); + signatureOffset = stackPtr; + pushBitStringHeader((byte) 0, (short)(last - stackPtr)); + //signatureOffset = pushSignature(null, (short) 0, (short) 256); + pushAlgorithmId(X509SignAlgIdentifier); + tbsLength = stackPtr; + pushTbsCert(rsaCert); + tbsOffset = stackPtr; + tbsLength = (short)(tbsLength - tbsOffset); + pushSequenceHeader((short)(last-stackPtr)); + certStart = stackPtr; + KMSEProviderImpl.instance().rsaSignPKCS1256( + KMByteBlob.cast(signPriv).getBuffer(), + KMByteBlob.cast(signPriv).getStartOff(), + KMByteBlob.cast(signPriv).length(), + KMByteBlob.cast(signMod).getBuffer(), + KMByteBlob.cast(signMod).getStartOff(), + KMByteBlob.cast(signMod).length(), + stack,tbsOffset,tbsLength, + stack,signatureOffset); +// print(stack, stackPtr, (short)(last - stackPtr)); } /* private static void print(byte[] buf, short start, short length){ @@ -781,5 +938,7 @@ public static byte[] getBuffer(){ //if((i-start)%16 == 0 && (i-start) != 0) sb.append(String.format("\n")); } System.out.println(sb.toString()); - }*/ + } + + */ } diff --git a/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMBackupRestoreAgent.java b/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMBackupRestoreAgent.java new file mode 100644 index 00000000..0c483334 --- /dev/null +++ b/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMBackupRestoreAgent.java @@ -0,0 +1,8 @@ +package com.android.javacard.keymaster; + +import javacard.framework.Shareable; + +public interface KMBackupRestoreAgent extends Shareable { + void backup(byte[] buf, short start, short len); + short restore(byte[] buf, short start); +} diff --git a/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMBackupStoreApplet.java b/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMBackupStoreApplet.java new file mode 100644 index 00000000..5f28c014 --- /dev/null +++ b/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMBackupStoreApplet.java @@ -0,0 +1,63 @@ +package com.android.javacard.keymaster; + +import javacard.framework.AID; +import javacard.framework.APDU; +import javacard.framework.Applet; +import javacard.framework.JCSystem; +import javacard.framework.Shareable; +import javacard.framework.Util; + +public class KMBackupStoreApplet extends Applet implements KMBackupRestoreAgent { + private static final short DATA_TABLE_MEM_SIZE = 2048; + private static final byte[] aidArr = new byte[]{ (byte)0xA0, 0x00, 0x00, 0x00, 0x62}; + + private byte[] dataTable; + private short dataTableSize; + + private KMBackupStoreApplet() { + dataTable = new byte[DATA_TABLE_MEM_SIZE]; + } + + public static void install(byte bArray[], short bOffset, byte bLength) { + new KMBackupStoreApplet().register(); + } + + @Override + public boolean select() { + return true; + } + + @Override + public void process(APDU apdu) { + + } + + @Override + public void backup(byte[] buf, short start, short len) { + // Store the data + if (len > 0) { + JCSystem.beginTransaction(); + dataTableSize = len; + Util.arrayCopy(buf, start, dataTable, (short) 0, len); + JCSystem.commitTransaction(); + } + } + + @Override + public short restore(byte[] buf, short start) { + // Restore the data + Util.arrayCopy(dataTable, (short) 0, buf, start, dataTableSize); + return dataTableSize; + } + + @Override + public Shareable getShareableInterfaceObject(AID aid, byte param){ + byte[] aidBytes = new byte[10]; + byte len = aid.getBytes(aidBytes, (short)0); + if(Util.arrayCompare(aidArr,(short)0,aidBytes,(short)0,len) == 0){ + return this; + } + return null; + } + +} diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMCipher.java b/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMCipher.java similarity index 94% rename from Applet/Applet/src/com/android/javacard/keymaster/KMCipher.java rename to Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMCipher.java index a36f11ab..b599c003 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMCipher.java +++ b/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMCipher.java @@ -1,6 +1,7 @@ package com.android.javacard.keymaster; public abstract class KMCipher { + /* public static final byte CIPHER_RSA = 7; public static final short PAD_PKCS1_OAEP = 9; public static final short PAD_PKCS1_OAEP_SHA224 = 13; @@ -22,6 +23,8 @@ public abstract class KMCipher { public static final short AES_BLOCK_SIZE = 16; public static final short DES_BLOCK_SIZE = 8; public static final short ALG_AES_CTR = -16; + + */ public static final short SUN_JCE = 0xE9; public abstract short doFinal(byte[] buffer, short startOff, short length, byte[] scratchPad, short i); @@ -30,6 +33,10 @@ public abstract class KMCipher { public abstract void updateAAD(byte[] buffer, short startOff, short length); + public abstract short getBlockMode(); + + public abstract void setBlockMode(short mode); + public abstract short getPaddingAlgorithm(); public abstract short getCipherAlgorithm(); diff --git a/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMCipherImpl.java b/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMCipherImpl.java index 67af5549..65ebfad0 100644 --- a/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMCipherImpl.java +++ b/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMCipherImpl.java @@ -13,11 +13,10 @@ public class KMCipherImpl extends KMCipher{ private Cipher cipher; private javax.crypto.Cipher sunCipher; private short cipherAlg; - private short paddingAlg; + private short padding; private short mode; private boolean verificationFlag; - public static short aes_gcm_decrypt_final_data = 0x00; - + private short blockMode; KMCipherImpl(Cipher c){ cipher = c; } @@ -27,8 +26,7 @@ public class KMCipherImpl extends KMCipher{ @Override public short doFinal(byte[] buffer, short startOff, short length, byte[] scratchPad, short i){ - if(cipherAlg == KMCipher.CIPHER_RSA && - (paddingAlg == KMCipher.PAD_PKCS1_OAEP_SHA256||paddingAlg == KMCipher.PAD_PKCS1_OAEP)){ + if(cipherAlg == KMType.RSA && padding == KMType.RSA_OAEP){ try { return (short)sunCipher.doFinal(buffer,startOff,length,scratchPad,i); } catch (ShortBufferException e) { @@ -41,17 +39,8 @@ public short doFinal(byte[] buffer, short startOff, short length, byte[] scratch e.printStackTrace(); CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); } - }else if(cipherAlg == KMCipher.ALG_AES_GCM){ + }else if(cipherAlg == KMType.AES && blockMode == KMType.GCM){ try { - /* - if (mode == javax.crypto.Cipher.DECRYPT_MODE) { - short acutalLen = (short)sunCipher.getOutputSize(length); - aes_gcm_decrypt_final_data = KMByteBlob.instance(acutalLen); - return (short)sunCipher.doFinal(buffer,startOff,length, - KMByteBlob.cast(aes_gcm_decrypt_final_data).getBuffer(), - KMByteBlob.cast(aes_gcm_decrypt_final_data).getStartOff()); - } - */ return (short)sunCipher.doFinal(buffer,startOff,length,scratchPad,i); } catch (AEADBadTagException e) { e.printStackTrace(); @@ -65,7 +54,7 @@ public short doFinal(byte[] buffer, short startOff, short length, byte[] scratch } catch (BadPaddingException e) { CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); } - } else if(cipherAlg == KMCipher.ALG_AES_CTR){ + } else if(cipherAlg == KMType.AES && blockMode == KMType.CTR){ try { return (short)sunCipher.doFinal(buffer,startOff,length,scratchPad,i); } catch (ShortBufferException e) { @@ -78,16 +67,62 @@ public short doFinal(byte[] buffer, short startOff, short length, byte[] scratch e.printStackTrace(); CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); } - } - else{ + } else{ + if(cipherAlg == KMType.RSA && padding == KMType.PADDING_NONE && mode == KMType.ENCRYPT ){ + // Length cannot be greater then key size according to JcardSim + if(length >= 256) KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + // make input equal to 255 bytes + byte[] tmp = new byte[255]; + Util.arrayFillNonAtomic(tmp,(short)0,(short)255, (byte)0); + Util.arrayCopyNonAtomic( + buffer, + startOff, + tmp, (short)(255 - length),length); + startOff = 0; + length = 255; + buffer = tmp; + + }else if((cipherAlg == KMType.DES || cipherAlg == KMType.AES) && padding ==KMType.PKCS7 && mode == KMType.ENCRYPT){ + byte blkSize = 16; + byte paddingBytes; + short len = length; + if (cipherAlg == KMType.DES) blkSize = 8; + // padding bytes + if (len % blkSize == 0) paddingBytes = blkSize; + else paddingBytes = (byte) (blkSize - (len % blkSize)); + // final len with padding + len = (short) (len + paddingBytes); + // intermediate buffer to copy input data+padding + byte[] tmp = new byte[len]; + // fill in the padding + Util.arrayFillNonAtomic(tmp, (short) 0, len, paddingBytes); + // copy the input data + Util.arrayCopyNonAtomic(buffer,startOff,tmp,(short)0,length); + buffer = tmp; + length = len; + startOff = 0; + } 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. - if (cipherAlg == Cipher.ALG_RSA_NOPAD && mode == Cipher.MODE_DECRYPT && len < 256) { + // JCard Sim removes leading zeros during decryption in case of no padding - so add that back. + if (cipherAlg == KMType.RSA && padding == KMType.PADDING_NONE && mode == KMType.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; + }else if((cipherAlg == KMType.AES || cipherAlg == KMType.DES) // PKCS7 + && padding == KMType.PKCS7 + && mode == KMType.DECRYPT){ + byte blkSize = 16; + if (cipherAlg == KMType.DES) blkSize = 8; + if(len >0) { + //verify if padding is corrupted. + byte paddingByte = scratchPad[i+len -1]; + //padding byte always should be <= block size + if((short)paddingByte > blkSize || + (short)paddingByte <= 0) KMException.throwIt(KMError.INVALID_ARGUMENT); + len = (short)(len - (short)paddingByte);// remove the padding bytes + } } return len; } @@ -106,13 +141,15 @@ public void setCipherAlgorithm(short alg) { @Override public short update(byte[] buffer, short startOff, short length, byte[] scratchPad, short i) { - short len = 0; - if(cipherAlg == KMCipher.ALG_AES_GCM || cipherAlg == KMCipher.ALG_AES_CTR){ + if(cipherAlg == KMType.AES && (blockMode == KMType.GCM || blockMode == KMType.CTR)){ try { return (short)sunCipher.update(buffer,startOff,length,scratchPad,i); } catch (ShortBufferException e) { e.printStackTrace(); CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + } catch (IllegalStateException e) { + e.printStackTrace(); + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); } } else{ return cipher.update(buffer, startOff, length, scratchPad, i); @@ -136,12 +173,22 @@ public void updateAAD(byte[] buffer, short startOff, short length) { @Override public short getPaddingAlgorithm() { - return paddingAlg; + return padding; } @Override public void setPaddingAlgorithm(short alg) { - paddingAlg = alg; + padding = alg; + } + + @Override + public void setBlockMode(short mode){ + blockMode = mode; + } + + @Override + public short getBlockMode(){ + return blockMode; } public short getMode() { @@ -169,4 +216,5 @@ public short getAesGcmOutputSize(short len, short macLength) { } } } + } diff --git a/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMJcardSimulator.java b/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMJcardSimulator.java index 72b6f5d9..9b017990 100644 --- a/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMJcardSimulator.java +++ b/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMJcardSimulator.java @@ -26,6 +26,7 @@ import java.security.spec.MGF1ParameterSpec; import java.security.spec.RSAPrivateKeySpec; import java.security.spec.RSAPublicKeySpec; +import javacard.framework.AID; import javacard.framework.ISO7816; import javacard.framework.ISOException; import javacard.framework.JCSystem; @@ -39,11 +40,11 @@ import javacard.security.Key; import javacard.security.KeyBuilder; import javacard.security.KeyPair; -import javacard.security.MessageDigest; import javacard.security.RSAPrivateKey; import javacard.security.RSAPublicKey; import javacard.security.RandomData; import javacard.security.Signature; +import javacardx.crypto.AEADCipher; import javacardx.crypto.Cipher; import javax.crypto.AEADBadTagException; import javax.crypto.BadPaddingException; @@ -68,6 +69,9 @@ public class KMJcardSimulator implements KMSEProvider { public static final short ENTROPY_POOL_SIZE = 16; // simulator does not support 256 bit aes keys public static final byte[] aesICV = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; private static final int AES_GCM_KEY_SIZE = 16; + private static final byte[] aidArr = new byte[]{ (byte)0xA0, 0x00, 0x00, 0x00, 0x63}; + + public static boolean jcardSim = false; private static Signature kdf; private static Signature hmacSignature; @@ -98,14 +102,14 @@ public KMJcardSimulator() { } - @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); @@ -116,14 +120,14 @@ public RSAPrivateKey createRsaKey(byte[] modBuffer, short modOff, short modLengt } - @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(); @@ -131,13 +135,13 @@ public ECPrivateKey createEcKey(byte[] privBuffer, short privOff, short privLeng return privKey; } - @Override + public AESKey createAESKey(short keysize) { byte[] rndNum = new byte[(short) (keysize/8)]; return createAESKey(rndNum, (short)0, (short)rndNum.length); } - @Override + // @Override public AESKey createAESKey(byte[] buf, short startOff, short length) { AESKey key = null; short keysize = (short)(length * 8); @@ -154,7 +158,7 @@ public AESKey createAESKey(byte[] buf, short startOff, short length) { return key; } - @Override + public DESKey createTDESKey() { // TODO check whether 168 bit or 192 bit byte[] rndNum = new byte[24]; @@ -162,7 +166,7 @@ public DESKey createTDESKey() { 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); @@ -170,7 +174,7 @@ public DESKey createTDESKey(byte[] secretBuffer, short secretOff, short secretLe return triDesKey; } - @Override + public HMACKey createHMACKey(short keysize) { if((keysize % 8 != 0) || !(keysize >= 64 && keysize <= 512)){ CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); @@ -182,10 +186,93 @@ public HMACKey createHMACKey(short keysize) { @Override public short createSymmetricKey(byte alg, short keysize, byte[] buf, short startOff) { + switch(alg){ + case KMType.AES: + AESKey aesKey = createAESKey(keysize); + return aesKey.getKey(buf,startOff); + case KMType.DES: + DESKey desKey = createTDESKey(); + return desKey.getKey(buf,startOff); + case KMType.HMAC: + HMACKey hmacKey = createHMACKey(keysize); + return hmacKey.getKey(buf,startOff); + default: + CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); + break; + } return 0; } @Override + public void createAsymmetricKey(byte alg, byte[] privKeyBuf, short privKeyStart, short privKeyLength, + byte[] pubModBuf, short pubModStart, short pubModLength, short[] lengths){ + switch (alg){ + case KMType.RSA: + KeyPair rsaKey = createRsaKeyPair(); + RSAPrivateKey privKey = (RSAPrivateKey) rsaKey.getPrivate(); + lengths[0] = privKey.getExponent(privKeyBuf,privKeyStart); + lengths[1] = privKey.getModulus(pubModBuf,pubModStart); + if(lengths[0] > privKeyLength || lengths[1] > pubModLength){ + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + } + break; + case KMType.EC: + KeyPair ecKey = createECKeyPair(); + ECPublicKey ecPubKey = (ECPublicKey) ecKey.getPublic(); + ECPrivateKey ecPrivKey = (ECPrivateKey) ecKey.getPrivate(); + lengths[0] = ecPrivKey.getS(privKeyBuf,privKeyStart); + lengths[1] = ecPubKey.getW(pubModBuf,pubModStart); + if(lengths[0] > privKeyLength || lengths[1] > pubModLength){ + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + } + break; + default: + CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); + break; + } + } + + @Override + public boolean importSymmetricKey(byte alg, short keysize, byte[] buf, short startOff, short length) { + switch(alg){ + case KMType.AES: + AESKey aesKey = createAESKey(buf,startOff,length); + break; + case KMType.DES: + DESKey desKey = createTDESKey(buf,startOff,length); + break; + case KMType.HMAC: + HMACKey hmacKey = createHMACKey(buf,startOff,length); + break; + default: + CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); + break; + } + return true; + } + + @Override + public boolean importAsymmetricKey(byte alg, byte[] buf, short start, short length, byte[] privKeyBuf, short privKeyStart, short privKeyLength, byte[] pubModBuf, short pubModStart, short pubModLength) { + return false; + } + + @Override + public boolean importAsymmetricKey(byte alg, byte[] privKeyBuf, short privKeyStart, short privKeyLength, byte[] pubModBuf, short pubModStart, short pubModLength) { + switch (alg){ + case KMType.RSA: + RSAPrivateKey rsaKey = createRsaKey(pubModBuf,pubModStart,pubModLength,privKeyBuf,privKeyStart,privKeyLength); + break; + case KMType.EC: + ECPrivateKey ecPrivKey = createEcKey(privKeyBuf,privKeyStart,privKeyLength); + break; + default: + CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); + break; + } + return true; + } + + public HMACKey createHMACKey(byte[] secretBuffer, short secretOff, short secretLength) { HMACKey key = null; key = (HMACKey) KeyBuilder.buildKey(KeyBuilder.TYPE_HMAC, @@ -196,7 +283,9 @@ public HMACKey createHMACKey(byte[] secretBuffer, short secretOff, short secretL @Override public short aesGCMEncrypt( - AESKey key, + byte[] keyBuf, + short keyStart, + short keyLen, byte[] secret, short secretStart, short secretLen, @@ -212,7 +301,10 @@ public short aesGCMEncrypt( short authTagStart, short authTagLen) { //Create the sun jce compliant aes key - byte[] keyMaterial = new byte[16]; + if(keyLen != 32 && keyLen != 16){ + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + } + /*byte[] keyMaterial = new byte[key]; short keySize = 16; if(key.getSize() == 128){ keyMaterial = new byte[16]; @@ -221,9 +313,11 @@ public short aesGCMEncrypt( keySize = 32; } key.getKey(keyMaterial,(short)0); + + */ //print("KeyMaterial Enc", keyMaterial); //print("Authdata Enc", authData, authDataStart, authDataLen); - java.security.Key aesKey = new SecretKeySpec(keyMaterial,(short)0,keySize, "AES"); + java.security.Key aesKey = new SecretKeySpec(keyBuf,keyStart,keyLen, "AES"); // Create the cipher javax.crypto.Cipher cipher = null; try { @@ -282,50 +376,10 @@ public short aesGCMEncrypt( 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[] keyBuf, + short keyStart, + short keyLen, byte[] encSecret, short encSecretStart, short encSecretLen, @@ -341,7 +395,10 @@ public boolean aesGCMDecrypt( short authTagStart, short authTagLen) { //Create the sun jce compliant aes key - byte[] keyMaterial = new byte[16]; + if(keyLen != 32 && keyLen != 16){ + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + } + /*byte[] keyMaterial = new byte[16]; short keySize = 16; if(key.getSize() == 128){ keyMaterial = new byte[16]; @@ -350,10 +407,11 @@ public boolean aesGCMDecrypt( keySize = 32; } key.getKey(keyMaterial,(short)0); + + */ //print("KeyMaterial Dec", keyMaterial); //print("Authdata Dec", authData, authDataStart, authDataLen); - - java.security.Key aesKey = new SecretKeySpec(keyMaterial,(short)0,(short)keySize, "AES"); + java.security.Key aesKey = new SecretKeySpec(keyBuf,keyStart,keyLen, "AES"); // Create the cipher javax.crypto.Cipher cipher = null; try { @@ -424,18 +482,19 @@ public byte[] getTrueRandomNumber(short i) { @Override public short aesCCMSign( - byte[] bufIn, - short bufInStart, - short buffInLength, - byte[] masterKeySecret, - byte[] bufOut, - short bufStart) { - if (masterKeySecret.length > 16) { + byte[] bufIn, + short bufInStart, + short buffInLength, + byte[] masterKeySecret, + short masterKeyStart, + short masterKeyLen, + byte[] bufOut, + short bufStart) { + if (masterKeyLen > 16) { return -1; } - AESKey key = (AESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_AES, KeyBuilder.LENGTH_AES_128, false); - key.setKey(masterKeySecret, (short) 0); + key.setKey(masterKeySecret, masterKeyStart); byte[] in = new byte[buffInLength]; Util.arrayCopyNonAtomic(bufIn, bufInStart,in,(short)0,buffInLength); kdf.init(key, Signature.MODE_SIGN); @@ -444,19 +503,17 @@ public short aesCCMSign( Util.arrayCopyNonAtomic(bufOut, bufStart,out,(short)0,len); return len; } - - - @Override - public HMACKey cmacKdf(byte[] keyMaterial, byte[] label, byte[] context, short contextStart, short contextLength) { + + public HMACKey cmacKdf(byte[] keyMaterial, short keyMaterialStart, short keyMaterialLen, byte[] label, byte[] context, short contextStart, short contextLength) { // This is hardcoded to requirement - 32 byte output with two concatenated 16 bytes K1 and K2. - final byte n = 2; // hardcoded - L is 32 bytes and h is 16 byte i.e. CMAC output of AES 128 bit key. + final byte n = 2; // hardcoded final byte[] L = {0,0,1,0}; // [L] 256 bits - hardcoded 32 bits as per reference impl in keymaster. - final byte[] zero = {0}; // + final byte[] zero = {0}; // byte byte[] iBuf = new byte[]{0,0,0,0}; // [i] counter - 32 bits byte[] keyOut = new byte[(short)(n*16)]; Signature prf = Signature.getInstance(Signature.ALG_AES_CMAC_128, false); - AESKey key = (AESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_AES, KeyBuilder.LENGTH_AES_128, false); - key.setKey(keyMaterial, (short) 0); + AESKey key = (AESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_AES, KeyBuilder.LENGTH_AES_256, false); + key.setKey(keyMaterial, keyMaterialStart); prf.init(key, Signature.MODE_SIGN); byte i =1; short pos = 0; @@ -465,7 +522,7 @@ public HMACKey cmacKdf(byte[] keyMaterial, byte[] label, byte[] context, short c prf.update(iBuf, (short) 0, (short) 4); // 4 bytes of iBuf with counter in it prf.update(label, (short) 0, (short) label.length); // label prf.update(zero, (short) 0, (short) 1); // 1 byte of 0x00 - prf.update(context, contextLength, contextLength); // context + prf.update(context, contextStart, contextLength); // context pos = prf.sign(L, (short) 0, (short) 4, keyOut, pos); // 4 bytes of L - signature of 16 bytes i++; } @@ -473,18 +530,17 @@ public HMACKey cmacKdf(byte[] keyMaterial, byte[] label, byte[] context, short c } @Override - public short cmacKdf(byte[] keyMaterial, byte[] label, byte[] context, short contextStart, short contextLength, byte[] keyBuf, short keyStart) { - HMACKey key = cmacKdf(keyMaterial,label,context,contextStart,contextLength); + public short cmacKdf(byte[] keyMaterial, short keyMaterialStart, short keyMaterialLen, byte[] label, byte[] context, short contextStart, short contextLength, byte[] keyBuf, short keyStart) { + HMACKey key = cmacKdf(keyMaterial,keyMaterialStart, keyMaterialLen, label,context,contextStart,contextLength); return key.getKey(keyBuf,keyStart); } - @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); @@ -493,91 +549,173 @@ public boolean hmacVerify(HMACKey key, byte[] data, short dataStart, short dataL @Override public short hmacSign(byte[] keyBuf, short keyStart, short keyLength, byte[] data, short dataStart, short dataLength, byte[] mac, short macStart) { - return 0; + HMACKey key = createHMACKey(keyBuf,keyStart,keyLength); + return hmacSign(key,data,dataStart,dataLength,mac,macStart); } @Override public boolean hmacVerify(byte[] keyBuf, short keyStart, short keyLength, byte[] data, short dataStart, short dataLength, byte[] mac, short macStart, short macLength) { - return false; + HMACKey key = createHMACKey(keyBuf,keyStart,keyLength); + return hmacVerify(key,data,dataStart,dataLength,mac,macStart,macLength); } @Override - public short initSymmetricOperation(byte purpose, byte alg, byte digest, byte padding, byte blockMode, byte[] keyBuf, short keyStart, short keyLength) { - return 0; + public short rsaDecipherOAEP256(byte[] secret, short secretStart, short secretLength, + byte[] modBuffer, short modOff, short modLength, + byte[] inputDataBuf, short inputDataStart, short inputDataLength, + byte[] outputDataBuf, short outputDataStart) { + KMCipher cipher = createRsaDecipher( + KMType.RSA_OAEP, KMType.SHA2_256, secret, secretStart, secretLength, modBuffer, modOff, modLength); + return cipher.doFinal( + inputDataBuf, inputDataStart, inputDataLength, outputDataBuf, outputDataStart); } @Override - public short initSymmetricOperation(byte purpose, byte alg, byte digest, byte[] keyBuf, short keyStart, short keyLength) { - return 0; + public short rsaSignPKCS1256(byte[] secret, short secretStart, short secretLength, + byte[] modBuffer, short modOff, short modLength, + byte[] inputDataBuf, short inputDataStart, short inputDataLength, + byte[] outputDataBuf, short outputDataStart) { + Signature signer = createRsaSigner( + KMType.SHA2_256, KMType.RSA_PKCS1_1_5_SIGN, secret,secretStart,secretLength, modBuffer,modOff,modLength); + return signer.sign( + inputDataBuf, inputDataStart, inputDataLength, outputDataBuf, outputDataStart); } @Override - public short initAsymmetricOperation(byte purpose, byte alg, byte padding, byte digest, byte[] privKeyBuf, short privKeyStart, short privKeyLength, byte[] modBuf, short modStart, short modLength) { - return 0; + public KMOperation initSymmetricOperation(byte purpose, byte alg, byte digest, byte padding, byte blockMode, + byte[] keyBuf, short keyStart, short keyLength, + byte[] ivBuf, short ivStart, short ivLength, short macLength) { + switch (alg){ + case KMType.AES: + case KMType.DES: + if(blockMode != KMType.GCM){ + KMCipher cipher = createSymmetricCipher(alg,purpose, blockMode, padding,keyBuf,keyStart,keyLength, + ivBuf,ivStart,ivLength); + return new KMOperationImpl(cipher); + }else { + KMCipher aesGcm = createAesGcmCipher(purpose,macLength,keyBuf,keyStart,keyLength,ivBuf,ivStart,ivLength); + return new KMOperationImpl(aesGcm); + } + case KMType.HMAC: + Signature signerVerifier = createHmacSignerVerifier(purpose,digest,keyBuf,keyStart,keyLength); + return new KMOperationImpl(signerVerifier); + default: + CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); + } + return null; } @Override - public short initAsymmetricOperation(byte purpose, byte alg, byte padding, byte digest, byte[] privKeyBuf, short privKeyStart, short privKeyLength) { - return 0; + public KMOperation initAsymmetricOperation(byte purpose, byte alg, byte padding, byte digest, + byte[] privKeyBuf, short privKeyStart, short privKeyLength, + byte[] pubModBuf, short pubModStart, short pubModLength) { + if (alg == KMType.RSA) { + switch (purpose) { + case KMType.SIGN: + Signature signer = + createRsaSigner( + digest, + padding, + privKeyBuf, + privKeyStart, + privKeyLength, + pubModBuf, + pubModStart, + pubModLength); + return new KMOperationImpl(signer); + case KMType.VERIFY: + Signature verifier = createRsaVerifier(digest, padding, pubModBuf, pubModStart, pubModLength); + return new KMOperationImpl(verifier); + case KMType.ENCRYPT: + KMCipher cipher = createRsaCipher(padding, digest, pubModBuf, pubModStart, pubModLength); + return new KMOperationImpl(cipher); + case KMType.DECRYPT: + KMCipher decipher = + createRsaDecipher( + padding, digest, privKeyBuf, privKeyStart, privKeyLength, pubModBuf, pubModStart, pubModLength); + return new KMOperationImpl(decipher); + } + }else if(alg == KMType.EC){ + switch(purpose){ + case KMType.SIGN: + Signature signer = + createEcSigner(digest,privKeyBuf,privKeyStart,privKeyLength); + return new KMOperationImpl(signer); + case KMType.VERIFY: + Signature verifier = createEcVerifier(digest,pubModBuf,pubModStart,pubModLength); + return new KMOperationImpl(verifier); + } + } + CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); + return null; } - +/* @Override public short updateOperation(short opHandle, byte[] inputDataBuf, short inputDataStart, short inputDataLength, byte[] outputDataBuf, short outputDataStart) { - return 0; + Object op = findOperation(opHandle); + if(op instanceof Signature ){ + ((Signature)op).update(inputDataBuf,inputDataStart,inputDataLength); + return 0; + }else{ + return ((KMCipher)op).update(inputDataBuf,inputDataStart,inputDataLength,outputDataBuf,outputDataStart); + } } @Override public short finishOperation(short opHandle, byte[] inputDataBuf, short inputDataStart, short inputDataLength, byte[] outputDataBuf, short outputDataStart) { - return 0; + Object op = findOperation(opHandle); + short ret = 0; + if(op instanceof Signature ){ + ret = ((Signature)op).sign(inputDataBuf,inputDataStart,inputDataLength,outputDataBuf,outputDataStart); + }else{ + ret = ((KMCipher)op).doFinal(inputDataBuf,inputDataStart,inputDataLength,outputDataBuf,outputDataStart); + } + removeOperation(opHandle); + return ret; } @Override public void abortOperation(short opHandle) { - - } - - @Override - public short hmacInit(byte[] keyBuf, short keyStart, short keyLength, byte digest, byte mode) { - return 0; + removeOperation(opHandle); } @Override - public short hmacSign(short opHandle, byte[] data, short dataStart, short dataLength, byte[] mac, short macStart) { - return 0; + public void updateAAD(short opHandle, byte[] dataBuf, short dataStart, short dataLength) { + KMCipher aesGcm = (KMCipher) findOperation(opHandle); + aesGcm.updateAAD(dataBuf, dataStart, dataLength); } @Override - public boolean hmacVerify(short opHandle, byte[] keyBuf, short keyStart, short keyLength, byte[] data, short dataStart, short dataLength, byte[] mac, short macStart, short macLength) { - return false; + public void getAESGCMOutputSize(short opHandle, short dataSize, short macLength) { + KMCipher aesGcm = (KMCipher) findOperation(opHandle); + aesGcm.getAesGcmOutputSize(dataSize, macLength); } + */ - @Override - public short hmacUpdate(short opHandle, byte[] dataBuf, short dataStart, short dataLength) { - return 0; - } - - @Override - public KMCipher createRsaDecipher(short padding, byte[] secret, short secretStart, + + public KMCipher createRsaDecipher(short padding, short digest, byte[] secret, short secretStart, short secretLength, byte[] modBuffer, short modOff, short modLength) { - byte cipherAlg = Cipher.ALG_RSA_NOPAD; + byte cipherAlg = mapCipherAlg(KMType.RSA, (byte)padding, (byte)0); // TODO implement OAEP algorithm using SunJCE. - if (padding == KMCipher.PAD_PKCS1_OAEP_SHA256 || padding == KMCipher.PAD_PKCS1_OAEP) { - return createRsaOAEP256Cipher(Cipher.MODE_DECRYPT,(byte)padding,secret,secretStart,secretLength,modBuffer,modOff,modLength); - }else if(padding == KMCipher.PAD_PKCS1) cipherAlg = Cipher.ALG_RSA_PKCS1; + if (cipherAlg == Cipher.ALG_RSA_PKCS1_OAEP) { + return createRsaOAEP256Cipher(KMType.DECRYPT,(byte)digest,secret,secretStart,secretLength,modBuffer,modOff,modLength); + } + /*else if(padding == KMCipher.PAD_PKCS1) cipherAlg = Cipher.ALG_RSA_PKCS1; else cipherAlg = Cipher.ALG_RSA_NOPAD; + */ Cipher rsaCipher = Cipher.getInstance(cipherAlg,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); KMCipherImpl inst = new KMCipherImpl(rsaCipher); - inst.setCipherAlgorithm(cipherAlg); - inst.setMode(Cipher.MODE_DECRYPT); + inst.setCipherAlgorithm(KMType.RSA); + inst.setMode(KMType.DECRYPT); inst.setPaddingAlgorithm(padding); return inst; } - private KMCipher createRsaOAEP256Cipher(byte mode,byte padding, + private KMCipher createRsaOAEP256Cipher(byte mode,byte digest, byte[] secret, short secretStart, short secretLen, byte[] modBuffer, short modOff, short modLength) { // Convert byte arrays into keys @@ -599,7 +737,7 @@ private KMCipher createRsaOAEP256Cipher(byte mode,byte padding, KeyFactory kf = KeyFactory.getInstance("RSA"); // Create cipher with oaep padding OAEPParameterSpec oaepSpec = null; - if(padding == KMCipher.PAD_PKCS1_OAEP_SHA256){ + if(digest == KMType.SHA2_256){ oaepSpec= new OAEPParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA1, PSource.PSpecified.DEFAULT); }else{ @@ -607,7 +745,7 @@ private KMCipher createRsaOAEP256Cipher(byte mode,byte padding, MGF1ParameterSpec.SHA1, PSource.PSpecified.DEFAULT); } rsaCipher = javax.crypto.Cipher.getInstance("RSA/ECB/OAEPPadding", "SunJCE"); - if (mode == KMCipher.MODE_ENCRYPT){ + if (mode == KMType.ENCRYPT){ RSAPublicKeySpec pubSpec = new RSAPublicKeySpec(modInt, expInt); java.security.interfaces.RSAPublicKey pubKey = (java.security.interfaces.RSAPublicKey) kf.generatePublic(pubSpec); rsaCipher.init(javax.crypto.Cipher.ENCRYPT_MODE, pubKey, oaepSpec); @@ -636,8 +774,8 @@ private KMCipher createRsaOAEP256Cipher(byte mode,byte padding, CryptoException.throwIt(CryptoException.INVALID_INIT); } KMCipherImpl ret = new KMCipherImpl(rsaCipher); - ret.setCipherAlgorithm(KMCipher.CIPHER_RSA); - ret.setPaddingAlgorithm(padding); + ret.setCipherAlgorithm(KMType.RSA); + ret.setPaddingAlgorithm(KMType.RSA_OAEP); ret.setMode(mode); return ret; } @@ -648,20 +786,21 @@ private String toHexString(byte[] num){ } return sb.toString(); } - @Override - public Signature createRsaSigner(short msgDigestAlg, short padding, byte[] secret, + + public Signature createRsaSigner(short digest, short padding, byte[] secret, short secretStart, short secretLength, byte[] modBuffer, short modOff, short modLength) { - short alg = Signature.ALG_RSA_SHA_256_PKCS1; - if (padding == KMCipher.PAD_NOPAD || - (padding == KMCipher.PAD_PKCS1 && msgDigestAlg == MessageDigest.ALG_NULL)) { + short alg = mapSignature256Alg(KMType.RSA, (byte)padding); + if (padding == KMType.PADDING_NONE || + (padding == KMType.RSA_PKCS1_1_5_SIGN && digest == KMType.DIGEST_NONE)) { return createNoDigestSigner(padding,secret, secretStart, secretLength, modBuffer, modOff, modLength); - } + }/* else if (padding == KMCipher.PAD_PKCS1_PSS) alg = Signature.ALG_RSA_SHA_256_PKCS1_PSS; else if (padding == KMCipher.PAD_PKCS1) { alg = Signature.ALG_RSA_SHA_256_PKCS1; }else CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); + */ Signature rsaSigner = Signature.getInstance((byte)alg, false); RSAPrivateKey key = (RSAPrivateKey) KeyBuilder.buildKey(KeyBuilder.TYPE_RSA_PRIVATE, KeyBuilder.LENGTH_RSA_2048, false); key.setExponent(secret,secretStart,secretLength); @@ -684,18 +823,13 @@ private Signature createNoDigestSigner(short padding, return inst; } - @Override - public Signature createEcSigner(short msgDigestAlg, byte[] secret, short secretStart, short secretLength) { - short alg = Signature.ALG_ECDSA_SHA_256; + + public Signature createEcSigner(short digest, byte[] secret, short secretStart, short secretLength) { + short alg = mapSignature256Alg(KMType.EC,(byte)0); Signature ecSigner; - if(msgDigestAlg == MessageDigest.ALG_NULL) { - //CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); + if(digest == KMType.DIGEST_NONE) { ecSigner = new KMEcdsa256NoDigestSignature(Signature.MODE_SIGN, secret, secretStart, secretLength); } else { - //KeyPair ecKeyPair = new KeyPair(KeyPair.ALG_EC_FP, KeyBuilder.LENGTH_EC_FP_256); - //ecKeyPair.genKeyPair(); - //ECPrivateKey privKey = (ECPrivateKey) ecKeyPair.getPrivate(); - //privKey.setS(secret,secretStart, secretLength); ECPrivateKey key = (ECPrivateKey) KeyBuilder.buildKey(KeyBuilder.TYPE_EC_FP_PRIVATE, KeyBuilder.LENGTH_EC_FP_256, false); key.setS(secret,secretStart,secretLength); ecSigner = Signature.getInstance((byte)alg,false); @@ -704,20 +838,20 @@ public Signature createEcSigner(short msgDigestAlg, byte[] secret, short secretS return ecSigner; } - @Override + public KMCipher createSymmetricCipher( - short cipherAlg, short mode, short padding, byte[] secret, short secretStart, short secretLength) { - return createSymmetricCipher(cipherAlg, mode, padding, secret,secretStart,secretLength,null,(short)0,(short)0); + short cipherAlg, short mode, short blockMode, short padding, byte[] secret, short secretStart, short secretLength) { + return createSymmetricCipher(cipherAlg, mode, blockMode, padding, secret,secretStart,secretLength,null,(short)0,(short)0); } - @Override - public KMCipher createSymmetricCipher(short cipherAlg, short mode, short padding, byte[] secret, + + public KMCipher createSymmetricCipher(short alg, short purpose, short blockMode, short padding, byte[] secret, short secretStart, short secretLength, byte[] ivBuffer, short ivStart, short ivLength) { Key key = null; Cipher symmCipher = null; short len = 0; - switch (secretLength){ + switch (secretLength) { case 32: len = KeyBuilder.LENGTH_AES_256; break; @@ -731,49 +865,114 @@ public KMCipher createSymmetricCipher(short cipherAlg, short mode, short padding CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); break; } + short cipherAlg = mapCipherAlg((byte)alg,(byte)padding,(byte)blockMode); switch(cipherAlg){ - case KMCipher.ALG_AES_BLOCK_128_CBC_NOPAD: - cipherAlg = Cipher.ALG_AES_BLOCK_128_CBC_NOPAD; + case Cipher.ALG_AES_BLOCK_128_CBC_NOPAD: key = KeyBuilder.buildKey(KeyBuilder.TYPE_AES,len,false); ((AESKey) key).setKey(secret,secretStart); symmCipher = Cipher.getInstance((byte)cipherAlg, false); - symmCipher.init(key, (byte) mode, ivBuffer, ivStart, ivLength); + symmCipher.init(key, mapPurpose(purpose), ivBuffer, ivStart, ivLength); break; - case KMCipher.ALG_AES_BLOCK_128_ECB_NOPAD: - cipherAlg = Cipher.ALG_AES_BLOCK_128_ECB_NOPAD; + case Cipher.ALG_AES_BLOCK_128_ECB_NOPAD: key = KeyBuilder.buildKey(KeyBuilder.TYPE_AES,len,false); ((AESKey) key).setKey(secret,secretStart); symmCipher = Cipher.getInstance((byte)cipherAlg, false); - symmCipher.init(key, (byte) mode); + symmCipher.init(key, mapPurpose(purpose)); break; - case KMCipher.ALG_DES_CBC_NOPAD: - cipherAlg = Cipher.ALG_DES_CBC_NOPAD; - key = (DESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_DES,len,false); + case Cipher.ALG_DES_CBC_NOPAD: + key = KeyBuilder.buildKey(KeyBuilder.TYPE_DES,len,false); ((DESKey) key).setKey(secret,secretStart); symmCipher = Cipher.getInstance((byte)cipherAlg, false); //TODO Consume only 8 bytes of iv. the random number for iv is of 16 bytes. //While sending back the iv send only 8 bytes. - symmCipher.init(key, (byte) mode, ivBuffer, ivStart, (short)8); + symmCipher.init(key, mapPurpose(purpose), ivBuffer, ivStart, (short)8); break; - case KMCipher.ALG_DES_ECB_NOPAD: - cipherAlg = Cipher.ALG_DES_ECB_NOPAD; - key = (DESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_DES,len,false); + case Cipher.ALG_DES_ECB_NOPAD: + key = KeyBuilder.buildKey(KeyBuilder.TYPE_DES,len,false); ((DESKey) key).setKey(secret,secretStart); symmCipher = Cipher.getInstance((byte)cipherAlg, false); - symmCipher.init(key, (byte) mode); + symmCipher.init(key, mapPurpose(purpose)); break; - case KMCipher.ALG_AES_CTR: // uses SUNJCE - return createAesCtrCipherNoPad(mode, secret,secretStart,secretLength,ivBuffer,ivStart,ivLength); + case Cipher.ALG_AES_CTR: // uses SUNJCE + return createAesCtrCipherNoPad(purpose, secret,secretStart,secretLength,ivBuffer,ivStart,ivLength); default://This should never happen CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); break; } KMCipherImpl cipher = new KMCipherImpl(symmCipher); - cipher.setCipherAlgorithm(cipherAlg); + cipher.setCipherAlgorithm(alg); cipher.setPaddingAlgorithm(padding); - cipher.setMode(mode); + cipher.setMode(purpose); + cipher.setBlockMode(blockMode); return cipher; } + private byte mapPurpose(short purpose){ + switch(purpose){ + case KMType.ENCRYPT: + return Cipher.MODE_ENCRYPT; + case KMType.DECRYPT: + return Cipher.MODE_DECRYPT; + case KMType.SIGN: + return Signature.MODE_SIGN; + case KMType.VERIFY: + return Signature.MODE_VERIFY; + } + return -1; + } + + private byte mapSignature256Alg(byte alg, byte padding){ + switch(alg){ + case KMType.RSA: + switch(padding){ + case KMType.RSA_PKCS1_1_5_SIGN: + return Signature.ALG_RSA_SHA_256_PKCS1; + case KMType.RSA_PSS: + return Signature.ALG_RSA_SHA_256_PKCS1_PSS; + } + break; + case KMType.EC: + return Signature.ALG_ECDSA_SHA_256; + case KMType.HMAC: + return Signature.ALG_HMAC_SHA_256; + } + return -1; + } + + private byte mapCipherAlg(byte alg, byte padding, byte blockmode){ + switch(alg){ + case KMType.AES: + switch(blockmode){ + case KMType.ECB: + return Cipher.ALG_AES_BLOCK_128_ECB_NOPAD; + case KMType.CBC: + return Cipher.ALG_AES_BLOCK_128_CBC_NOPAD; + case KMType.CTR: + return Cipher.ALG_AES_CTR; + case KMType.GCM: + return AEADCipher.ALG_AES_GCM; + } + break; + case KMType.DES: + switch(blockmode){ + case KMType.ECB: + return Cipher.ALG_DES_ECB_NOPAD; + case KMType.CBC: + return Cipher.ALG_DES_CBC_NOPAD; + } + break; + case KMType.RSA: + switch(padding){ + case KMType.PADDING_NONE: + return Cipher.ALG_RSA_NOPAD; + case KMType.RSA_PKCS1_1_5_ENCRYPT: + return Cipher.ALG_RSA_PKCS1; + case KMType.RSA_OAEP: + return Cipher.ALG_RSA_PKCS1_OAEP; + } + break; + } + return -1; + } private KMCipher createAesCtrCipherNoPad(short mode, byte[] secret, short secretStart, short secretLength, byte[] ivBuffer, short ivStart, short ivLength) { if(secretLength != 16 && secretLength != 32){ @@ -782,10 +981,9 @@ private KMCipher createAesCtrCipherNoPad(short mode, byte[] secret, short secret if(ivLength != 16){ CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); } - if(mode != KMCipher.MODE_ENCRYPT && mode != KMCipher.MODE_DECRYPT){ + if(mode != KMType.ENCRYPT && mode != KMType.DECRYPT){ CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); } - //Create the sun jce compliant aes key byte[] keyMaterial = new byte[secretLength]; Util.arrayCopyNonAtomic(secret,secretStart,keyMaterial,(short)0,secretLength); @@ -810,7 +1008,7 @@ private KMCipher createAesCtrCipherNoPad(short mode, byte[] secret, short secret // Init Cipher IvParameterSpec ivSpec = new IvParameterSpec(iv); try { - if(mode == KMCipher.MODE_ENCRYPT)cipher.init(javax.crypto.Cipher.ENCRYPT_MODE, aesKey, ivSpec); + if(mode == KMType.ENCRYPT) cipher.init(javax.crypto.Cipher.ENCRYPT_MODE, aesKey, ivSpec); else cipher.init(javax.crypto.Cipher.DECRYPT_MODE, aesKey, ivSpec); } catch (InvalidKeyException e) { e.printStackTrace(); @@ -820,16 +1018,17 @@ private KMCipher createAesCtrCipherNoPad(short mode, byte[] secret, short secret CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); } KMCipherImpl ret = new KMCipherImpl(cipher); - ret.setCipherAlgorithm(KMCipher.ALG_AES_CTR); + ret.setCipherAlgorithm(KMType.AES); ret.setMode(mode); ret.setPaddingAlgorithm((short)0); + ret.setBlockMode(KMType.CTR); return ret; } - @Override - public Signature createHmacSignerVerifier(short purpose, short msgDigestAlg, byte[] secret, short secretStart, short secretLength) { + + public Signature createHmacSignerVerifier(short purpose, short digest, byte[] secret, short secretStart, short secretLength) { short alg = Signature.ALG_HMAC_SHA_256; - if(msgDigestAlg != MessageDigest.ALG_SHA_256) CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + if(digest != KMType.SHA2_256) CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); Signature hmacSignerVerifier = Signature.getInstance((byte)alg,false); HMACKey key = (HMACKey) KeyBuilder.buildKey(KeyBuilder.TYPE_HMAC, (short)(secretLength*8), false); key.setKey(secret,secretStart,secretLength); @@ -837,7 +1036,7 @@ public Signature createHmacSignerVerifier(short purpose, short msgDigestAlg, byt return hmacSignerVerifier; } - @Override + public KMCipher createAesGcmCipher(short mode, short tagLen, byte[] secret, short secretStart, short secretLength, byte[] ivBuffer, short ivStart, short ivLength) { if(secretLength != 16 && secretLength != 32){ @@ -846,10 +1045,9 @@ public KMCipher createAesGcmCipher(short mode, short tagLen, byte[] secret, shor if(ivLength != AES_GCM_NONCE_LENGTH){ CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); } - if(mode != KMCipher.MODE_ENCRYPT && mode != KMCipher.MODE_DECRYPT){ + if(mode != KMType.ENCRYPT && mode != KMType.DECRYPT){ CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); } - //Create the sun jce compliant aes key byte[] keyMaterial = new byte[secretLength]; Util.arrayCopyNonAtomic(secret,secretStart,keyMaterial,(short)0,secretLength); @@ -876,7 +1074,7 @@ public KMCipher createAesGcmCipher(short mode, short tagLen, byte[] secret, shor // Init Cipher GCMParameterSpec spec = new GCMParameterSpec(tagLen, iv,(short)0,AES_GCM_NONCE_LENGTH); try { - if(mode == KMCipher.MODE_ENCRYPT)mode = javax.crypto.Cipher.ENCRYPT_MODE; + if(mode == KMType.ENCRYPT)mode = javax.crypto.Cipher.ENCRYPT_MODE; else mode = javax.crypto.Cipher.DECRYPT_MODE; cipher.init(mode, aesKey, spec); } catch (InvalidKeyException e) { @@ -887,32 +1085,13 @@ public KMCipher createAesGcmCipher(short mode, short tagLen, byte[] secret, shor CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); } KMCipherImpl ret = new KMCipherImpl(cipher); - ret.setCipherAlgorithm(KMCipher.ALG_AES_GCM); + ret.setCipherAlgorithm(KMType.AES); ret.setMode(mode); ret.setPaddingAlgorithm((short)0); + ret.setBlockMode(KMType.GCM); return ret; } - @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; @@ -1006,18 +1185,15 @@ public void addRngEntropy(byte[] num, short offset, short length) { } } - @Override - public void bypassAesGcm(){ - //ignore - } - - @Override - public KMCipher createRsaCipher(short padding, byte[] modBuffer, short modOff, short modLength) { - byte cipherAlg = Cipher.ALG_RSA_NOPAD; - if (padding == KMCipher.PAD_PKCS1_OAEP_SHA256 || padding == KMCipher.PAD_PKCS1_OAEP) { - return createRsaOAEP256Cipher(Cipher.MODE_ENCRYPT,(byte)padding,null,(short)0,(short)0,modBuffer,modOff,modLength); - }else if(padding == KMCipher.PAD_PKCS1) cipherAlg = Cipher.ALG_RSA_PKCS1; + + public KMCipher createRsaCipher(short padding, short digest, byte[] modBuffer, short modOff, short modLength) { + byte cipherAlg = mapCipherAlg(KMType.RSA, (byte)padding, (byte)0); + if (cipherAlg == Cipher.ALG_RSA_PKCS1_OAEP) { + return createRsaOAEP256Cipher(KMType.ENCRYPT, (byte)digest, null,(short)0,(short)0,modBuffer,modOff,modLength); + } + /*else if(padding == KMCipher.PAD_PKCS1) cipherAlg = Cipher.ALG_RSA_PKCS1; else cipherAlg = Cipher.ALG_RSA_NOPAD; + */ Cipher rsaCipher = Cipher.getInstance(cipherAlg,false); RSAPublicKey key = (RSAPublicKey) KeyBuilder.buildKey(KeyBuilder.TYPE_RSA_PUBLIC, KeyBuilder.LENGTH_RSA_2048, false); byte[] exponent = new byte[]{0x01,0x00,0x01}; @@ -1025,21 +1201,20 @@ public KMCipher createRsaCipher(short padding, byte[] modBuffer, short modOff, s key.setModulus(modBuffer, modOff, modLength); rsaCipher.init(key,Cipher.MODE_ENCRYPT); KMCipherImpl inst = new KMCipherImpl(rsaCipher); - inst.setCipherAlgorithm(cipherAlg); - inst.setMode(Cipher.MODE_ENCRYPT); + inst.setCipherAlgorithm(KMType.RSA); + inst.setMode(KMType.ENCRYPT); inst.setPaddingAlgorithm(padding); return inst; } - - @Override - public Signature createRsaVerifier(short msgDigestAlg, short padding, byte[] modBuffer, short modOff, short modLength) { - short alg = Signature.ALG_RSA_SHA_256_PKCS1; - if(msgDigestAlg == MessageDigest.ALG_NULL) CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); - else if(padding == KMCipher.PAD_NOPAD) CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); - else if(padding == KMCipher.PAD_PKCS1_PSS) alg = Signature.ALG_RSA_SHA_256_PKCS1_PSS; + + public Signature createRsaVerifier(short digest, short padding, byte[] modBuffer, short modOff, short modLength) { + short alg = mapSignature256Alg(KMType.RSA,(byte)padding); + if(digest == KMType.DIGEST_NONE || padding == KMType.PADDING_NONE) CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); + /*else if(padding == KMCipher.PAD_PKCS1_PSS) alg = Signature.ALG_RSA_SHA_256_PKCS1_PSS; else if(padding == KMCipher.PAD_PKCS1) alg = Signature.ALG_RSA_SHA_256_PKCS1; else CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); + */ Signature rsaVerifier = Signature.getInstance((byte)alg, false); RSAPublicKey key = (RSAPublicKey) KeyBuilder.buildKey(KeyBuilder.TYPE_RSA_PUBLIC, KeyBuilder.LENGTH_RSA_2048, false); byte[] exponent = new byte[]{0x01,0x00,0x01}; @@ -1048,18 +1223,15 @@ public Signature createRsaVerifier(short msgDigestAlg, short padding, byte[] mod rsaVerifier.init(key,Signature.MODE_VERIFY); return rsaVerifier; } - @Override - public Signature createEcVerifier(short msgDigestAlg, byte[] pubKey, short pubKeyStart, short pubKeyLength) { - short alg = Signature.ALG_ECDSA_SHA_256; + + + public Signature createEcVerifier(short digest, byte[] pubKey, short pubKeyStart, short pubKeyLength) { + short alg = mapSignature256Alg(KMType.EC, (byte)0); Signature ecVerifier; //if(msgDigestAlg == MessageDigest.ALG_NULL) CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); - if(msgDigestAlg == MessageDigest.ALG_NULL) { - //CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); + if(digest == KMType.DIGEST_NONE) { ecVerifier = new KMEcdsa256NoDigestSignature(Signature.MODE_VERIFY, pubKey, pubKeyStart, pubKeyLength); } else { - // KeyPair ecKeyPair = new KeyPair(KeyPair.ALG_EC_FP, KeyBuilder.LENGTH_EC_FP_256); - // ecKeyPair.genKeyPair(); - // ECPublicKey key = (ECPublicKey) ecKeyPair.getPublic(); ECPublicKey key = (ECPublicKey) KeyBuilder.buildKey(KeyBuilder.TYPE_EC_FP_PUBLIC, KeyBuilder.LENGTH_EC_FP_256, false); key.setW(pubKey,pubKeyStart,pubKeyLength); ecVerifier = Signature.getInstance((byte)alg,false); @@ -1068,59 +1240,35 @@ public Signature createEcVerifier(short msgDigestAlg, byte[] pubKey, short pubKe return ecVerifier; } - @Override - public short getSystemTimeInMilliSeconds(byte[] timeBuf, short timeStart, short timeOffset) { - return 0; - } - - @Override - public short addListener(KMEventListener listener, byte eventType) { - return 0; - } - - @Override - public short getEventData(byte[] eventBuf, short eventStart, short eventLength) { - return 0; - } - - @Override - public boolean isAlgSupported(byte alg) { - return true; - } @Override - public boolean isKeySizeSupported(byte alg, short keySize) { + public boolean isBackupRestoreSupported() { return true; } @Override - public boolean isCurveSupported(byte eccurve) { - return true; + public KMAttestationCert getAttestationCert(boolean rsaCert) { + //certBuilder.reset(); + return KMAttestationCertImpl.instance(rsaCert); } @Override - public boolean isDigestSupported(byte alg, byte digest) { - return true; + public void backup(byte[] buf, short start, short len) { + byte[] data = new byte[len]; + AID aid = JCSystem.lookupAID(aidArr,(short)0,(byte)aidArr.length); + KMBackupRestoreAgent backupStore = (KMBackupRestoreAgent) JCSystem.getAppletShareableInterfaceObject(aid,(byte)0); + Util.arrayCopyNonAtomic(buf,start,data,(short)0,len); + backupStore.backup(data,(short)0,len); } @Override - public boolean isPaddingSupported(byte alg, byte padding) { - return true; - } - - @Override - public boolean isBlockModeSupported(byte alg, byte blockMode) { - return true; - } - - @Override - public boolean isSystemTimerSupported() { - return false; - } - - @Override - public boolean isBootEventSupported() { - return false; + public short restore(byte[] buf, short start) { + byte[] data = new byte[2200]; + AID aid = JCSystem.lookupAID(aidArr,(short)0,(byte)aidArr.length); + KMBackupRestoreAgent backupStore = (KMBackupRestoreAgent) JCSystem.getAppletShareableInterfaceObject(aid,(byte)0); + short len = backupStore.restore(data,(short)0); + Util.arrayCopyNonAtomic(data,(short)0,buf,start,len); + return len; } /* diff --git a/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMOperationImpl.java b/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMOperationImpl.java new file mode 100644 index 00000000..755b7134 --- /dev/null +++ b/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMOperationImpl.java @@ -0,0 +1,60 @@ +package com.android.javacard.keymaster; + +import javacard.security.Signature; + +public class KMOperationImpl implements KMOperation { + private KMCipher cipher; + private Signature signature; + + public KMOperationImpl(KMCipher cipher){ + this.cipher = cipher; + this.signature = null; + } + + public KMOperationImpl(Signature sign){ + this.cipher = null; + this.signature = sign; + } + + @Override + public short update(byte[] inputDataBuf, short inputDataStart, short inputDataLength, + byte[] outputDataBuf, short outputDataStart) { + return cipher.update(inputDataBuf,inputDataStart,inputDataLength,outputDataBuf,outputDataStart); + } + @Override + public short update(byte[] inputDataBuf, short inputDataStart, short inputDataLength) { + signature.update(inputDataBuf,inputDataStart,inputDataLength); + return 0; + } + + @Override + public short finish(byte[] inputDataBuf, short inputDataStart, short inputDataLength, byte[] outputDataBuf, short outputDataStart) { + return cipher.doFinal(inputDataBuf,inputDataStart,inputDataLength,outputDataBuf,outputDataStart); + } + + @Override + public short sign(byte[] inputDataBuf, short inputDataStart, short inputDataLength, byte[] signBuf, short signStart) { + return signature.sign(inputDataBuf,inputDataStart,inputDataLength,signBuf,signStart); + } + + @Override + public boolean verify(byte[] inputDataBuf, short inputDataStart, short inputDataLength, byte[] signBuf, short signStart, short signLength) { + return signature.verify(inputDataBuf,inputDataStart,inputDataLength,signBuf,signStart,signLength); + } + + @Override + public void abort() { + // do nothing + } + + @Override + public void updateAAD(byte[] dataBuf, short dataStart, short dataLength) { + cipher.updateAAD(dataBuf, dataStart, dataLength); + } + + @Override + public short getAESGCMOutputSize(short dataSize, short macLength) { + return cipher.getAesGcmOutputSize(dataSize, macLength); + } + +} diff --git a/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMRsa2048NoDigestSignature.java b/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMRsa2048NoDigestSignature.java index 1aff36eb..82793bfb 100644 --- a/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMRsa2048NoDigestSignature.java +++ b/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMRsa2048NoDigestSignature.java @@ -26,12 +26,10 @@ public void init(Key key, byte b) throws CryptoException { @Override public void init(Key key, byte b, byte[] bytes, short i, short i1) throws CryptoException { - } @Override public void setInitialDigest(byte[] bytes, short i, short i1, byte[] bytes1, short i2, short i3) throws CryptoException { - } @Override @@ -61,7 +59,6 @@ public short getLength() throws CryptoException { @Override public void update(byte[] bytes, short i, short i1) throws CryptoException { - } @Override @@ -93,8 +90,8 @@ private byte[] padData(byte[] buf, short start, short len){ CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); } Util.arrayFillNonAtomic(inputData, (short) 0, (short) 256, (byte) 0x00); - if (padding == KMCipher.PAD_NOPAD) { // add zero to right - } else if (padding == KMCipher.PAD_PKCS1) {// 0x00||0x01||PS||0x00 + if (padding == KMType.PADDING_NONE) { // add zero to right + } else if (padding == KMType.RSA_PKCS1_1_5_SIGN) {// 0x00||0x01||PS||0x00 inputData[0] = 0x00; inputData[1] = 0x01; Util.arrayFillNonAtomic(inputData,(short)2,(short)(256-len-3),(byte)0xFF); @@ -107,7 +104,7 @@ private byte[] padData(byte[] buf, short start, short len){ } private boolean isValidData(byte[] buf, short start, short len) { - if (padding == KMCipher.PAD_NOPAD) { + if (padding == KMType.PADDING_NONE) { if (len > 256) return false; else if (len == 256) { short v = Util.arrayCompare(buf, start, rsaModulus, (short) 0, len); diff --git a/Applet/Applet/OracleSimProvider/com/android/javacard/keymaster/KMCipherImpl.java b/Applet/Applet/OracleSimProvider/com/android/javacard/keymaster/KMCipherImpl.java deleted file mode 100644 index 34e1851e..00000000 --- a/Applet/Applet/OracleSimProvider/com/android/javacard/keymaster/KMCipherImpl.java +++ /dev/null @@ -1,57 +0,0 @@ -package com.android.javacard.keymaster; - -import com.android.javacard.keymaster.KMCipher; -import javacardx.crypto.Cipher; - -public class KMCipherImpl extends KMCipher{ - Cipher cipher; - short cipherAlg; - short paddingAlg; - 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 void setPaddingAlgorithm(short alg) { - paddingAlg = alg; - } - - @Override - public void setCipherAlgorithm(short alg) { - cipherAlg = alg; - } - - @Override - public short getCipherProvider() { - return 0; - } - - @Override - public short getAesGcmOutputSize(short len, short macLength) { - return len; - } - - @Override - public short update(byte[] buffer, short startOff, short length, byte[] scratchPad, short i) { - return cipher.update(buffer,startOff,length,scratchPad,i); - } - @Override - public void updateAAD(byte[] buffer, short startOff, short length) { - } - - @Override - public short getPaddingAlgorithm() { - return cipher.getPaddingAlgorithm(); - } -} diff --git a/Applet/Applet/OracleSimProvider/com/android/javacard/keymaster/KMSimulator.java b/Applet/Applet/OracleSimProvider/com/android/javacard/keymaster/KMSimulator.java index f7868951..c5d74fe0 100644 --- a/Applet/Applet/OracleSimProvider/com/android/javacard/keymaster/KMSimulator.java +++ b/Applet/Applet/OracleSimProvider/com/android/javacard/keymaster/KMSimulator.java @@ -41,6 +41,7 @@ * creates its own RNG using PRNG. */ public class KMSimulator implements KMSEProvider { + 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; @@ -91,7 +92,6 @@ public KMSimulator() { aesRngKey = (AESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_AES, KeyBuilder.LENGTH_AES_128, false); } - @Override public KeyPair createRsaKeyPair() { // By default 65537 is used as public exponent no need to set the public exponent. Now generate // 512 bit RSA keypair for the given exponent @@ -99,7 +99,6 @@ public KeyPair createRsaKeyPair() { return rsa512KeyPair; } - @Override public KeyPair createECKeyPair() { // Simulator does not support 256 bit keys. // Generate default 192 bit key pair supported by simulator. @@ -107,7 +106,6 @@ public KeyPair createECKeyPair() { return ec192KeyPair; } - @Override public AESKey createAESKey(short keysize) { // keysize is ignored as simulator only supports 128 bit aes key newRandomNumber(rndNum, (short) 0, (short) 16); @@ -115,7 +113,6 @@ public AESKey createAESKey(short keysize) { return aes128Key; } - @Override public AESKey createAESKey(byte[] buf, short startOff, short length) { if (length > 16) length = 16; else if(length < 16) return null; @@ -123,7 +120,6 @@ public AESKey createAESKey(byte[] buf, short startOff, short length) { return aes128Key; } - @Override public DESKey createTDESKey() { // only 128 bit keys are supported newRandomNumber(rndNum, (short) 0, (short) 21); @@ -131,7 +127,6 @@ public DESKey createTDESKey() { return triDesKey; } - @Override public HMACKey createHMACKey(short keysize) { // simulator only supports HMAC keys for SHA1 and SHA256 with block size 64. // So only 128 and 256 bit HMAC keys are supported. @@ -156,6 +151,26 @@ public short createSymmetricKey(byte alg, short keysize, byte[] buf, short start return 0; } + @Override + public void createAsymmetricKey(byte alg, byte[] privKeyBuf, short privKeyStart, short privKeyLength, byte[] pubModBuf, short pubModStart, short pubModLength, short[] lengths) { + + } + + @Override + public boolean importSymmetricKey(byte alg, short keysize, byte[] buf, short startOff, short length) { + return false; + } + + @Override + public boolean importAsymmetricKey(byte alg, byte[] buf, short start, short length, byte[] privKeyBuf, short privKeyStart, short privKeyLength, byte[] pubModBuf, short pubModStart, short pubModLength) { + return false; + } + + @Override + public boolean importAsymmetricKey(byte alg, byte[] privKeyBuf, short privKeyStart, short privKeyLength, byte[] pubModBuf, short pubModStart, short pubModLength) { + return false; + } + @Override public void addRngEntropy(byte[] num, short offset, short length) { // Maximum length can be 256 bytes. But currently we support max 32 bytes seed. @@ -182,7 +197,9 @@ public void addRngEntropy(byte[] num, short offset, short length) { @Override public short aesGCMEncrypt( - AESKey key, + byte[] aesKey, + short keyStart, + short keyLen, byte[] secret, short secretStart, short secretLen, @@ -209,6 +226,7 @@ public short aesGCMEncrypt( } byte[] aad = JCSystem.makeTransientByteArray(authDataLen, JCSystem.CLEAR_ON_RESET); Util.arrayCopyNonAtomic(authData, authDataStart, aad, (short) 0, authDataLen); + AESKey key = createAESKey(aesKey, keyStart, keyLen); try { aesGcmCipher.init(key, Cipher.MODE_ENCRYPT, nonce, nonceStart, nonceLen); } catch (CryptoException exp) { @@ -247,8 +265,10 @@ public short aesGCMEncrypt( } public boolean aesGCMDecrypt( - AESKey key, - byte[] encSecret, + byte[] aesKey, + short keyStart, + short keyLen, + byte[] encSecret, short encSecretStart, short encSecretLen, byte[] secret, @@ -273,6 +293,7 @@ public boolean aesGCMDecrypt( byte[] tag = JCSystem.makeTransientByteArray(AES_GCM_TAG_LENGTH, JCSystem.CLEAR_ON_RESET); Util.arrayCopyNonAtomic(authTag, authTagStart, tag, (short) 0, authTagLen); boolean verification = false; + AESKey key = createAESKey(aesKey, keyStart, keyLen); try { aesGcmCipher.init(key, Cipher.MODE_DECRYPT, nonce, nonceStart, nonceLen); aesGcmCipher.updateAAD(aad, (short) 0, authDataLen); @@ -286,6 +307,12 @@ public boolean aesGCMDecrypt( return verification; } + + @Override + public short cmacKdf(byte[] keyMaterial, short keyMaterialStart, short keyMaterialLen, byte[] label, byte[] context, short contextStart, short contextLength, byte[] keyBuf, short keyStart) { + return 0; + } + @Override public byte[] getTrueRandomNumber(short i) { // ignore the size as simulator only supports 128 bit entropy @@ -298,22 +325,19 @@ public short aesCCMSign( short bufInStart, short buffInLength, byte[] masterKeySecret, + short masterKeyStart, + short masterKeyLen, byte[] bufOut, short bufStart) { - if (masterKeySecret.length > 16) { + if (masterKeyLen > 16) { return -1; } - aes128Key.setKey(masterKeySecret, (short) 0); + aes128Key.setKey(masterKeySecret, masterKeyStart); kdf.init(aes128Key, Signature.MODE_SIGN); return kdf.sign(bufIn, bufInStart, buffInLength, bufOut, bufStart); } - @Override - public ECPrivateKey createEcKey(byte[] privBuffer, short privOff, short privLength) { - return null; - } - public ECPrivateKey createEcPrivateKey(byte[] pubBuffer, short pubOff, short pubLength, byte[] privBuffer, short privOff, short privLength) { // Simulator does not support NamedParameterSpec or 256 bit keys @@ -327,7 +351,6 @@ public ECPrivateKey createEcPrivateKey(byte[] pubBuffer, short pubOff, short pub return privKey; } - @Override public HMACKey createHMACKey(byte[] secretBuffer, short secretOff, short secretLength) { // simulator only supports HMAC keys for SHA1 and SHA256 with block size 64. // So only 128 and 256 bit HMAC keys are supported. @@ -344,7 +367,6 @@ public HMACKey createHMACKey(byte[] secretBuffer, short secretOff, short secretL } return key; } - @Override public DESKey createTDESKey(byte[] secretBuffer, short secretOff, short secretLength) { // only 128 bit keys are supported if(secretLength < 128) return null; @@ -352,27 +374,18 @@ public DESKey createTDESKey(byte[] secretBuffer, short secretOff, short secretLe return triDesKey; } - @Override public RSAPrivateKey createRsaKey(byte[] modBuffer, short modOff, short modLength, byte[] privBuffer, short privOff, short privLength) { return null; } - @Override public HMACKey cmacKdf(byte[] keyMaterial, byte[] label, byte[] context, short contextStart, short contextLength) { return null; } - @Override - public short cmacKdf(byte[] keyMaterial, byte[] label, byte[] context, short contextStart, short contextLength, byte[] keyBuf, short keyStart) { - return 0; - } - - @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; } @@ -388,116 +401,25 @@ public boolean hmacVerify(byte[] keyBuf, short keyStart, short keyLength, byte[] } @Override - public short initSymmetricOperation(byte purpose, byte alg, byte digest, byte padding, byte blockMode, byte[] keyBuf, short keyStart, short keyLength) { - return 0; - } - - @Override - public short initSymmetricOperation(byte purpose, byte alg, byte digest, byte[] keyBuf, short keyStart, short keyLength) { - return 0; - } - - @Override - public short initAsymmetricOperation(byte purpose, byte alg, byte padding, byte digest, byte[] privKeyBuf, short privKeyStart, short privKeyLength, byte[] modBuf, short modStart, short modLength) { - return 0; - } - - @Override - public short initAsymmetricOperation(byte purpose, byte alg, byte padding, byte digest, byte[] privKeyBuf, short privKeyStart, short privKeyLength) { - return 0; - } - - @Override - public short updateOperation(short opHandle, byte[] inputDataBuf, short inputDataStart, short inputDataLength, byte[] outputDataBuf, short outputDataStart) { - return 0; - } - - @Override - public short finishOperation(short opHandle, byte[] inputDataBuf, short inputDataStart, short inputDataLength, byte[] outputDataBuf, short outputDataStart) { - return 0; - } - - @Override - public void abortOperation(short opHandle) { - - } - - @Override - public short hmacInit(byte[] keyBuf, short keyStart, short keyLength, byte digest, byte mode) { - return 0; - } - - @Override - public short hmacSign(short opHandle, byte[] data, short dataStart, short dataLength, byte[] mac, short macStart) { + public short rsaDecipherOAEP256(byte[] secret, short secretStart, short secretLength, byte[] modBuffer, short modOff, short modLength, byte[] inputDataBuf, short inputDataStart, short inputDataLength, byte[] outputDataBuf, short outputDataStart) { return 0; } @Override - public boolean hmacVerify(short opHandle, byte[] keyBuf, short keyStart, short keyLength, byte[] data, short dataStart, short dataLength, byte[] mac, short macStart, short macLength) { - return false; - } - - @Override - public short hmacUpdate(short opHandle, byte[] dataBuf, short dataStart, short dataLength) { + public short rsaSignPKCS1256(byte[] secret, short secretStart, short secretLength, byte[] modBuffer, short modOff, short modLength, byte[] inputDataBuf, short inputDataStart, short inputDataLength, byte[] outputDataBuf, short outputDataStart) { return 0; } @Override - public KMCipher createRsaDecipher(short padding, byte[] secret, short secretStart, short secretLength, byte[] modBuffer, short modOff, short modLength) { + public KMOperation initSymmetricOperation(byte purpose, byte alg, byte digest, byte padding, byte blockMode, byte[] keyBuf, short keyStart, short keyLength, byte[] ivBuf, short ivStart, short ivLength, short macLength) { return null; } @Override - public Signature createRsaSigner(short msgDigestAlg, short padding, byte[] secret, short secretStart, short secretLength, byte[] modBuffer, short modOff, short modLength) { + public KMOperation initAsymmetricOperation(byte purpose, byte alg, byte padding, byte digest, byte[] privKeyBuf, short privKeyStart, short privKeyLength, byte[] pubModBuf, short pubModStart, short pubModLength) { 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 KMCipher createSymmetricCipher(short cipherAlg, short mode, short padding, byte[] secret, short secretStart, short secretLength) { - return null; - } - - @Override - public Signature createHmacSignerVerifier(short purpose, short msgDigestAlg, byte[] secret, short secretStart, short secretLength) { - return null; - } - - @Override - public KMCipher createAesGcmCipher(short mode, short tagLen, 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; @@ -577,78 +499,25 @@ private void incrementCounter() { } } } - @Override - public void bypassAesGcm(){ - jcardSim = true; - } @Override - public KMCipher createRsaCipher(short padding, byte[] modBuffer, short modOff, short modLength) { - return null; + public boolean isBackupRestoreSupported() { + return false; } @Override - public Signature createRsaVerifier(short msgDigestAlg, short padding, byte[] modBuffer, short modOff, short modLength) { - return null; - } - @Override - public Signature createEcVerifier(short msgDigestAlg, byte[] pubKey, short pubKeyStart, short pubKeyLength) { + public KMAttestationCert getAttestationCert(boolean rsaCert) { return null; } @Override - public short getSystemTimeInMilliSeconds(byte[] timeBuf, short timeStart, short timeOffset) { - return 0; - } + public void backup(byte[] buf, short start, short len) { - @Override - public short addListener(KMEventListener listener, byte eventType) { - return 0; } @Override - public short getEventData(byte[] eventBuf, short eventStart, short eventLength) { + public short restore(byte[] buf, short start) { return 0; } - @Override - public boolean isAlgSupported(byte alg) { - return false; - } - - @Override - public boolean isKeySizeSupported(byte alg, short keySize) { - return false; - } - - @Override - public boolean isCurveSupported(byte eccurve) { - return false; - } - - @Override - public boolean isDigestSupported(byte alg, byte digest) { - return false; - } - - @Override - public boolean isPaddingSupported(byte alg, byte padding) { - return false; - } - - @Override - public boolean isBlockModeSupported(byte alg, byte blockMode) { - return false; - } - - @Override - public boolean isSystemTimerSupported() { - return false; - } - - @Override - public boolean isBootEventSupported() { - return false; - } - } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMAttestationCert.java b/Applet/Applet/src/com/android/javacard/keymaster/KMAttestationCert.java new file mode 100644 index 00000000..c39c0301 --- /dev/null +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMAttestationCert.java @@ -0,0 +1,33 @@ +package com.android.javacard.keymaster; + +public interface KMAttestationCert { + KMAttestationCert verifiedBootHash(short obj); + + KMAttestationCert authKey(short obj); + + KMAttestationCert verifiedBootKey(short obj); + + KMAttestationCert verifiedState(byte val); + + KMAttestationCert uniqueId(short obj); + KMAttestationCert notBefore(short obj); + KMAttestationCert notAfter(short obj); + + KMAttestationCert deviceLocked(boolean val); + + KMAttestationCert publicKey(short obj); + KMAttestationCert attestationChallenge(short obj); + KMAttestationCert extensionTag(short tag, boolean hwEnforced); + KMAttestationCert issuer(short obj); + short getCertLength(); + + KMAttestationCert buffer(byte[] buf, short bufStart, short maxLen); + + KMAttestationCert signingKey(short privKey, short modulus); + + short getCertStart(); + + short getCertEnd(); + + void build(); +} diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMBoolTag.java b/Applet/Applet/src/com/android/javacard/keymaster/KMBoolTag.java index ae0f75ac..15e6dc4f 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMBoolTag.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMBoolTag.java @@ -34,7 +34,9 @@ public class KMBoolTag extends KMTag { TRUSTED_USER_PRESENCE_REQUIRED, TRUSTED_CONFIRMATION_REQUIRED, UNLOCKED_DEVICE_REQUIRED, - RESET_SINCE_ID_ROTATION + RESET_SINCE_ID_ROTATION, + EARLY_BOOT_ENDED, + DEVICE_UNIQUE_ATTESTATION }; private KMBoolTag() {} diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMByteBlob.java b/Applet/Applet/src/com/android/javacard/keymaster/KMByteBlob.java index 7f598eaa..decf9631 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMByteBlob.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMByteBlob.java @@ -109,4 +109,10 @@ public boolean isValid(){ } return true; } + + public void decrementLength(short len){ + short length = Util.getShort(heap, (short) (instPtr + 1)); + length = (short)(length - len); + Util.setShort(heap, (short) (instPtr + 1),length); + } } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMError.java b/Applet/Applet/src/com/android/javacard/keymaster/KMError.java index 25557d27..9bf97298 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMError.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMError.java @@ -72,6 +72,7 @@ public class KMError { public static short CONCURRENT_PROOF_OF_PRESENCE_REQUESTED = 70; public static short NO_USER_CONFIRMATION = 71; public static short DEVICE_LOCKED = 72; + public static short EARLY_BOOT_ENDED = 73; public static short UNIMPLEMENTED = 100; public static short VERSION_MISMATCH = 101; public static short UNKNOWN_ERROR = 1000; diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java b/Applet/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java index 32eda210..5f6d823d 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java @@ -16,23 +16,16 @@ package com.android.javacard.keymaster; +import javacard.framework.AID; import javacard.framework.APDU; import javacard.framework.Applet; import javacard.framework.AppletEvent; import javacard.framework.ISO7816; import javacard.framework.ISOException; import javacard.framework.JCSystem; +import javacard.framework.Shareable; import javacard.framework.Util; -import javacard.security.AESKey; import javacard.security.CryptoException; -import javacard.security.DESKey; -import javacard.security.ECPrivateKey; -import javacard.security.ECPublicKey; -import javacard.security.HMACKey; -import javacard.security.KeyPair; -import javacard.security.MessageDigest; -import javacard.security.RSAPrivateKey; -import javacard.security.Signature; import javacardx.apdu.ExtendedLength; /** @@ -40,8 +33,6 @@ * objects. It also implements the keymaster state machine and handles javacard applet life cycle * events. */ -// TODO Currently implementing ExtendedLength for ease of testing -// - remove this in future. public class KMKeymasterApplet extends Applet implements AppletEvent, ExtendedLength { // Constants. public static final byte AES_BLOCK_SIZE = 16; @@ -51,6 +42,7 @@ public class KMKeymasterApplet extends Applet implements AppletEvent, ExtendedLe 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 = 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, @@ -99,6 +91,8 @@ public class KMKeymasterApplet extends Applet implements AppletEvent, ExtendedLe private static final byte INS_SET_BOOT_PARAMS_CMD = 0x24; private static final byte INS_DEVICE_LOCKED_CMD = 0x25; private static final byte INS_EARLY_BOOT_ENDED_CMD = 0x26; + private static final byte INS_BACKUP_CMD = 0x27; + private static final byte INS_RESTORE_CMD = 0x28; // Data Dictionary items public static final byte DATA_ARRAY_SIZE = 30; @@ -146,8 +140,7 @@ public class KMKeymasterApplet extends Applet implements AppletEvent, ExtendedLe 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; + private static final short HMAC_SHARED_PARAM_MAX_SIZE =64; // 64 bit unsigned calculations for time private final static byte[] oneSecMsec = {0,0,0,0,0,0,0x03,(byte)0xE8};//1000 msec private final static byte[] oneMinMsec = {0,0,0,0,0,0,(byte)0xEA,0x60};//60000 msec @@ -165,7 +158,7 @@ public class KMKeymasterApplet extends Applet implements AppletEvent, ExtendedLe private static KMEncoder encoder; private static KMDecoder decoder; private static KMRepository repository; - private static KMSEProvider cryptoProvider; + private static KMSEProvider seProvider; private static byte[] buffer; private static short bufferLength; private static short bufferStartOffset; @@ -173,11 +166,12 @@ public class KMKeymasterApplet extends Applet implements AppletEvent, ExtendedLe private static boolean setBootParamsDone; private static short[] tmpVariables; private static short[] data; + private static final short MAX_CERT_SIZE = 2048; /** Registers this applet. */ protected KMKeymasterApplet() { // TODO change this to make this compile time variation. - cryptoProvider = KMSEProviderImpl.instance(); + seProvider = KMSEProviderImpl.instance(); provisionDone = false; setBootParamsDone = false; byte[] buf = @@ -188,19 +182,13 @@ protected KMKeymasterApplet() { repository = new KMRepository(); tmpVariables = JCSystem.makeTransientShortArray((short) TMP_VARIABLE_ARRAY_SIZE, JCSystem.CLEAR_ON_RESET); - Util.arrayCopyNonAtomic(cryptoProvider.getTrueRandomNumber(repository.MASTER_KEY_SIZE), - (short) 0, buf, (short) 0, 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.initHmacSharedSecretKey(buf, repository.SHARED_SECRET_KEY_SIZE); - // TODO currently hmac nonce is generated once when installing the applet. Remove this once boot - // signal reception is incorporated in the design. - cryptoProvider.newRandomNumber(buf, (short) 0, 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); + Util.arrayCopyNonAtomic(seProvider.getTrueRandomNumber(KMRepository.MASTER_KEY_SIZE), + (short) 0, buf, (short) 0, KMRepository.MASTER_KEY_SIZE); + repository.initMasterKey(buf, KMRepository.MASTER_KEY_SIZE); + seProvider.newRandomNumber(buf, (short) 0, KMRepository.SHARED_SECRET_KEY_SIZE); + // TODO currently hmac nonce is generated once when installing the applet. + seProvider.newRandomNumber(buf, (short) 0, KMRepository.HMAC_SEED_NONCE_SIZE); + repository.initHmacNonce(buf, (short)0, KMRepository.HMAC_SEED_NONCE_SIZE); KMType.initialize(); encoder = new KMEncoder(); decoder = new KMDecoder(); @@ -215,7 +203,7 @@ protected KMKeymasterApplet() { * @param bLength the length in bytes of the parameter data in bArray */ public static void install(byte[] bArray, short bOffset, byte bLength) { - new KMKeymasterApplet(); + new KMKeymasterApplet(); } /** @@ -262,6 +250,7 @@ public void uninstall() { @Override public void process(APDU apdu) { repository.onProcess(); + //getRepository(); // Verify whether applet is in correct state. if ((keymasterState != KMKeymasterApplet.ACTIVE_STATE) && (keymasterState != KMKeymasterApplet.FIRST_SELECT_STATE)) { @@ -273,7 +262,6 @@ public void process(APDU apdu) { return; } } - // Read the apdu header and buffer. byte[] apduBuffer = apdu.getBuffer(); byte apduClass = apduBuffer[ISO7816.OFFSET_CLA]; @@ -288,12 +276,10 @@ public void process(APDU apdu) { ISOException.throwIt(ISO7816.SW_INCORRECT_P1P2); } // Validate whether INS can be supported - //TODO remove oracle Provisioning Cmd later. - if (!(apduIns >= INS_GENERATE_KEY_CMD && apduIns <= INS_EARLY_BOOT_ENDED_CMD)) { + if (!(apduIns >= INS_GENERATE_KEY_CMD && apduIns <= INS_RESTORE_CMD)) { ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED); } // Validate if INS is provision command if applet is in FIRST_SELECT_STATE. - //TODO remove oracle Provisioning Cmd later. if (keymasterState == KMKeymasterApplet.FIRST_SELECT_STATE) { if ((apduIns != INS_PROVISION_CMD) && (apduIns != INS_SET_BOOT_PARAMS_CMD)) { ISOException.throwIt(ISO7816.SW_COMMAND_NOT_ALLOWED); @@ -378,12 +364,18 @@ public void process(APDU apdu) { case INS_EARLY_BOOT_ENDED_CMD: processEarlyBootEndedCmd(apdu); break; + case INS_BACKUP_CMD: + processBackupCmd(apdu); + break; + case INS_RESTORE_CMD: + processRestoreCmd(apdu); + break; default: ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED); } } catch (KMException exception) { freeOperations(); - sendError(apdu, exception.reason); + sendError(apdu, KMException.reason); exception.clear(); } catch(ISOException exp){ freeOperations(); @@ -393,6 +385,25 @@ public void process(APDU apdu) { } } + private void processRestoreCmd(APDU apdu) { + //No arguments + if(seProvider.isBackupRestoreSupported()) sendError(apdu,KMError.UNIMPLEMENTED); + byte[] data = repository.getDataTable(); + short buf = KMByteBlob.instance((short)data.length); + short len = seProvider.restore(KMByteBlob.cast(buf).getBuffer(), + KMByteBlob.cast(buf).getStartOff()); + repository.restoreData(buf); + sendError(apdu, KMError.OK); + } + + private void processBackupCmd(APDU apdu) { + // No arguments + if(seProvider.isBackupRestoreSupported()) sendError(apdu,KMError.UNIMPLEMENTED); + byte[] data = repository.getDataTable(); + seProvider.backup(data,(short)0, (short)data.length); + sendError(apdu,KMError.OK); + } + private void freeOperations(){ if(data[OP_HANDLE] != KMType.INVALID_VALUE){ KMOperationState op = repository.findOperation(KMInteger.cast(data[OP_HANDLE]).getShort()); @@ -401,6 +412,7 @@ private void freeOperations(){ } } } + private void processEarlyBootEndedCmd(APDU apdu) { KMException.throwIt(KMError.UNIMPLEMENTED); } @@ -419,14 +431,17 @@ private void processDeviceLockedCmd(APDU apdu) { data[VERIFICATION_TOKEN] = KMArray.cast(tmpVariables[0]).get((short)1); validateVerificationToken(data[VERIFICATION_TOKEN],scratchPad); short verTime = KMVerificationToken.cast(data[VERIFICATION_TOKEN]).getTimestamp(); - short lastDeviceLockedTime = KMInteger.uint_64(repository.deviceLockedTimestamp, (short)0); + //short lastDeviceLockedTime = KMInteger.uint_64(repository.deviceLockedTimestamp, (short)0); + short lastDeviceLockedTime = repository.getDeviceTimeStamp(); if(KMInteger.compare(verTime,lastDeviceLockedTime) > 0){ Util.arrayFillNonAtomic(scratchPad,(short)0, (short)8, (byte)0); KMInteger.cast(verTime).getValue(scratchPad,(short)0,(short)8); - repository.deviceLockedFlag = true; - if(tmpVariables[1] == 0x01) repository.deviceUnlockPasswordOnly = true; - else repository.deviceUnlockPasswordOnly = false; - Util.arrayCopy(scratchPad,(short)0,repository.deviceLockedTimestamp,(short)0,(short)repository.deviceLockedTimestamp.length); + //repository.deviceLockedFlag = true; + repository.setDeviceLock(true); + //repository.deviceUnlockPasswordOnly = (tmpVariables[1] == 0x01); + repository.setDeviceLockPasswordOnly(tmpVariables[1] == 0x01); + //Util.arrayCopy(scratchPad,(short)0,repository.deviceLockedTimestamp,(short)0,(short)repository.deviceLockedTimestamp.length); + repository.setDeviceLockTimestamp(scratchPad,(short)0,(short)8); } sendError(apdu,KMError.OK); } @@ -494,9 +509,6 @@ private void processGetHwInfoCmd(APDU apdu) { private void processAddRngEntropyCmd(APDU apdu) { // Receive the incoming request fully from the master. receiveIncoming(apdu); - // Re-purpose the apdu buffer as scratch pad. - byte[] scratchPad = apdu.getBuffer(); - //Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) apdu.getBuffer().length, (byte) 0); // Argument 1 short argsProto = KMArray.instance((short) 1); KMArray.cast(argsProto).add((short) 0, KMByteBlob.exp()); @@ -508,7 +520,7 @@ private void processAddRngEntropyCmd(APDU apdu) { if (blob.length() > MAX_SEED_SIZE) { KMException.throwIt(KMError.INVALID_ARGUMENT); } - cryptoProvider.addRngEntropy(blob.getBuffer(), blob.getStartOff(), blob.length()); + seProvider.addRngEntropy(blob.getBuffer(), blob.getStartOff(), blob.length()); } private void processProvisionCmd(APDU apdu) { @@ -519,13 +531,14 @@ private void processProvisionCmd(APDU apdu) { short keyparams = KMKeyParameters.exp(); short keyFormat = KMEnum.instance(KMType.KEY_FORMAT); short blob = KMByteBlob.exp(); - short argsProto = KMArray.instance((short) 6); + short argsProto = KMArray.instance((short) 7); KMArray.cast(argsProto).add((short) 0, keyparams); KMArray.cast(argsProto).add((short) 1, keyFormat); KMArray.cast(argsProto).add((short) 2, blob); KMArray.cast(argsProto).add((short) 3, blob); // Cert - DER encoded issuer KMArray.cast(argsProto).add((short) 4, blob); // Cert - Expiry Time KMArray.cast(argsProto).add((short) 5, blob); // Cert - Auth Key Id + KMArray.cast(argsProto).add((short) 6, blob); // Shared Hmac Key Secret // Decode the argument short args = decoder.decode(argsProto, buffer, bufferStartOffset, bufferLength); @@ -608,6 +621,17 @@ private void processProvisionCmd(APDU apdu) { saveAttId(KMType.ATTESTATION_ID_IMEI); saveAttId(KMType.ATTESTATION_ID_MEID); saveAttId(KMType.ATTESTATION_ID_SERIAL); + + // Persist Hmac Shared Key Secret + tmpVariables[0] = KMArray.cast(args).get((short)6); + if(KMByteBlob.cast(tmpVariables[0]).length() != KMRepository.SHARED_SECRET_KEY_SIZE){ + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + repository.initHmacSharedSecretKey( + KMByteBlob.cast(tmpVariables[0]).getBuffer(), + KMByteBlob.cast(tmpVariables[0]).getStartOff(), + KMByteBlob.cast(tmpVariables[0]).length()); + // Change the state to ACTIVE if (keymasterState == KMKeymasterApplet.FIRST_SELECT_STATE) { provisionDone = true; @@ -615,6 +639,16 @@ private void processProvisionCmd(APDU apdu) { keymasterState = KMKeymasterApplet.ACTIVE_STATE; } } +/* + byte[] tmp = repository.getDataTable(); + seProvider.backup(tmp,(short)0,(short)data.length); + Util.arrayFillNonAtomic( + KMByteBlob.cast(tmp).getBuffer(), + KMByteBlob.cast(tmp).getStartOff(), + KMByteBlob.cast(tmp).length(),(byte)0); + seProvider.restore( + KMByteBlob.cast(tmp).getBuffer(), + KMByteBlob.cast(tmp).getStartOff());*/ sendError(apdu, KMError.OK); } @@ -635,25 +669,26 @@ private void saveAttId(short attTag){ private byte mapToAttId(short attTag){ switch (attTag){ case KMType.ATTESTATION_ID_BRAND: - return repository.ATT_ID_BRAND; + return KMRepository.ATT_ID_BRAND; case KMType.ATTESTATION_ID_DEVICE: - return repository.ATT_ID_DEVICE; + return KMRepository.ATT_ID_DEVICE; case KMType.ATTESTATION_ID_IMEI: - return repository.ATT_ID_IMEI; + return KMRepository.ATT_ID_IMEI; case KMType.ATTESTATION_ID_MANUFACTURER: - return repository.ATT_ID_MANUFACTURER; + return KMRepository.ATT_ID_MANUFACTURER; case KMType.ATTESTATION_ID_MEID: - return repository.ATT_ID_MEID; + return KMRepository.ATT_ID_MEID; case KMType.ATTESTATION_ID_MODEL: - return repository.ATT_ID_MODEL; + return KMRepository.ATT_ID_MODEL; case KMType.ATTESTATION_ID_PRODUCT: - return repository.ATT_ID_PRODUCT; + return KMRepository.ATT_ID_PRODUCT; case KMType.ATTESTATION_ID_SERIAL: - return repository.ATT_ID_SERIAL; + return KMRepository.ATT_ID_SERIAL; } KMException.throwIt(KMError.INVALID_TAG); return (byte)0xFF; // should never happen } + private void processGetKeyCharacteristicsCmd(APDU apdu) { // Receive the incoming request fully from the master. receiveIncoming(apdu); @@ -690,12 +725,14 @@ private void processGetKeyCharacteristicsCmd(APDU apdu) { private void processGetHmacSharingParamCmd(APDU apdu) { // No Arguments - byte[] scratchPad = apdu.getBuffer(); // Create HMAC Sharing Parameters tmpVariables[2] = KMHmacSharingParameters.instance(); + /* KMHmacSharingParameters.cast(tmpVariables[2]).setNonce( KMByteBlob.instance(repository.getHmacNonce(), (short) 0, - repository.HMAC_SEED_NONCE_SIZE)); + KMRepository.HMAC_SEED_NONCE_SIZE)); + */ + KMHmacSharingParameters.cast(tmpVariables[2]).setNonce(repository.getHmacNonce()); KMHmacSharingParameters.cast(tmpVariables[2]).setSeed(KMByteBlob.instance((short)0)); // prepare the response tmpVariables[3] = KMArray.instance((short) 2); @@ -707,6 +744,7 @@ private void processGetHmacSharingParamCmd(APDU apdu) { } private void processDeleteAllKeysCmd(APDU apdu) { + // No arguments repository.removeAllAuthTags(); // Send ok @@ -714,6 +752,7 @@ private void processDeleteAllKeysCmd(APDU apdu) { } 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); @@ -744,11 +783,10 @@ private void processDeleteKeyCmd(APDU apdu) { } // 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); + if (repository.validateAuthTag(data[AUTH_TAG])) { + // delete the auth tag + repository.removeAuthTag(data[AUTH_TAG]); } - // delete the auth tag - repository.removeAuthTag(data[AUTH_TAG]); // Send ok sendError(apdu, KMError.OK); } @@ -762,57 +800,105 @@ private void processComputeSharedHmacCmd(APDU apdu) { 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[2], buffer, bufferStartOffset, bufferLength); - data[HMAC_SHARING_PARAMS] = KMArray.cast(tmpVariables[2]).get((short) 0); + tmpVariables[0] = decoder.decode(tmpVariables[2], buffer, bufferStartOffset, bufferLength); + data[HMAC_SHARING_PARAMS] = KMArray.cast(tmpVariables[0]).get((short) 0); // Concatenate HMAC Params - tmpVariables[0] = 0; - tmpVariables[1] = KMArray.cast(data[HMAC_SHARING_PARAMS]).length();//total number of params - tmpVariables[5] = 0; // index in scratchPad - while (tmpVariables[0] < tmpVariables[1]) { + tmpVariables[0] = KMArray.cast(data[HMAC_SHARING_PARAMS]).length();//total number of params + tmpVariables[1] = repository.alloc((short)(tmpVariables[0] * HMAC_SHARED_PARAM_MAX_SIZE)); + tmpVariables[2] = 0; // index for params + tmpVariables[3] = 0; // index for concatenation buffer + //To check if nonce created by Strongbox is found. This value becomes 1 if both + //seed and nonce created here are found in hmac sharing parameters received. + tmpVariables[7] = 0; + tmpVariables[9] = repository.getHmacNonce(); + + while (tmpVariables[2] < tmpVariables[0]) { // read HmacSharingParam - tmpVariables[2] = KMArray.cast(data[HMAC_SHARING_PARAMS]).get(tmpVariables[0]); + tmpVariables[4] = KMArray.cast(data[HMAC_SHARING_PARAMS]).get(tmpVariables[2]); // get seed - 32 bytes max - tmpVariables[3] = KMHmacSharingParameters.cast(tmpVariables[2]).getSeed(); - tmpVariables[4] = KMByteBlob.cast(tmpVariables[3]).length(); + tmpVariables[5] = KMHmacSharingParameters.cast(tmpVariables[4]).getSeed(); + tmpVariables[6] = KMByteBlob.cast(tmpVariables[5]).length(); // if seed is present - if (tmpVariables[4] == repository.HMAC_SEED_NONCE_SIZE) { - // then copy that to scratchPad + if (tmpVariables[6] != 0) { + // then copy that to concatenation buffer Util.arrayCopyNonAtomic( - KMByteBlob.cast(tmpVariables[3]).getBuffer(), - KMByteBlob.cast(tmpVariables[3]).getStartOff(), - scratchPad, - tmpVariables[5],// index in scratch pad - tmpVariables[4]); - tmpVariables[5] += tmpVariables[4]; // increment by seed length + KMByteBlob.cast(tmpVariables[5]).getBuffer(), + KMByteBlob.cast(tmpVariables[5]).getStartOff(), + repository.getHeap(), + (short)(tmpVariables[1]+tmpVariables[3]),// concat index + tmpVariables[6]); + tmpVariables[3] += tmpVariables[6]; // increment the concat index + }else if(tmpVariables[7] == 0){ + // TODO according to hal specs seed should always be empty. Confirm this. + //The seed we are passing is of zero length so if seed length is zero + //the seed generated here is found. + tmpVariables[7] = 1; } // if nonce is present get nonce - 32 bytes - tmpVariables[3] = KMHmacSharingParameters.cast(tmpVariables[2]).getNonce(); - tmpVariables[4] = KMByteBlob.cast(tmpVariables[3]).length(); - // if nonce is not present - if (tmpVariables[4] != repository.HMAC_SEED_NONCE_SIZE) { + tmpVariables[5] = KMHmacSharingParameters.cast(tmpVariables[4]).getNonce(); + tmpVariables[6] = KMByteBlob.cast(tmpVariables[5]).length(); + // if nonce is not present - it is an error + if (tmpVariables[6] == 0) { KMException.throwIt(KMError.INVALID_ARGUMENT); } - // copy nonce to scratchPad + // copy nonce to concatenation buffer Util.arrayCopyNonAtomic( - KMByteBlob.cast(tmpVariables[3]).getBuffer(), - KMByteBlob.cast(tmpVariables[3]).getStartOff(), - scratchPad, - tmpVariables[5], - tmpVariables[4]); - tmpVariables[5] += tmpVariables[4]; // increment by nonce length - tmpVariables[0]++; // go to next hmac param in the vector - } - // ckdf to derive hmac key - scratch pad has the context - HMACKey key = - cryptoProvider.cmacKdf( - 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 + KMByteBlob.cast(tmpVariables[5]).getBuffer(), + KMByteBlob.cast(tmpVariables[5]).getStartOff(), + repository.getHeap(), + (short)(tmpVariables[1]+tmpVariables[3]),// index + tmpVariables[6]); + + // Check if the nonce generated here is present in the hmacSharingParameters array. + // Otherwise throw INVALID_ARGUMENT error. + if (tmpVariables[7] == 1) { + /* + if (0 == Util.arrayCompare( + repository.getHeap(), + (short) (tmpVariables[1] + tmpVariables[3]), + repository.getHmacNonce(), + (short) 0, + tmpVariables[6])) { + tmpVariables[7] = 2; // hmac nonce for this keymaster found. + } else { + tmpVariables[7] = 0; + } + */ + if (0 == Util.arrayCompare( + repository.getHeap(), + (short) (tmpVariables[1] + tmpVariables[3]), + KMByteBlob.cast(tmpVariables[9]).getBuffer(), + KMByteBlob.cast(tmpVariables[9]).getStartOff(), + tmpVariables[6])) { + tmpVariables[7] = 2; // hmac nonce for this keymaster found. + } else { + tmpVariables[7] = 0; + } + } + tmpVariables[3] += tmpVariables[6]; // increment by nonce length + tmpVariables[2]++; // go to next hmac param in the vector + } + if(tmpVariables[7] != 2) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + // generate the key and store it in scratch pad - 32 bytes + tmpVariables[8] = repository.getSharedKey(); + tmpVariables[6] = seProvider.cmacKdf( + KMByteBlob.cast(tmpVariables[8]).getBuffer(), + KMByteBlob.cast(tmpVariables[8]).getStartOff(), + KMByteBlob.cast(tmpVariables[8]).length(), ckdfLable , + repository.getHeap(), tmpVariables[1], tmpVariables[3],scratchPad, (short) 0); + // persist the computed hmac key. + repository.initComputedHmac(scratchPad, (short) 0, tmpVariables[6]); + + // Generate sharingKey verification signature and store that in scratch pad. tmpVariables[5] = - cryptoProvider.hmacSign( - key, sharingCheck, (short) 0, (short) sharingCheck.length, scratchPad, (short) 0); - tmpVariables[1] = KMByteBlob.instance(scratchPad, (short) 0, tmpVariables[5]); + seProvider.hmacSign( + scratchPad,(short)0,tmpVariables[6], + sharingCheck, (short) 0, (short) sharingCheck.length, + scratchPad, tmpVariables[6]); + // verification signature blob - 32 bytes + tmpVariables[1] = KMByteBlob.instance(scratchPad, tmpVariables[6], tmpVariables[5]); // prepare the response tmpVariables[0] = KMArray.instance((short) 2); KMArray.cast(tmpVariables[0]).add((short) 0, KMInteger.uint_16(KMError.OK)); @@ -852,8 +938,8 @@ private void processUpgradeKeyCmd(APDU apdu) { tmpVariables[0] = KMIntegerTag.cast(tmpVariables[0]).getValue(); tmpVariables[1] = KMKeyParameters.findTag(KMType.UINT_TAG, KMType.OS_PATCH_LEVEL, data[HW_PARAMETERS]); tmpVariables[1] = KMIntegerTag.cast(tmpVariables[1]).getValue(); - tmpVariables[2] = KMInteger.uint_32(repository.osVersion,(short)0); - tmpVariables[3] = KMInteger.uint_32(repository.osPatch,(short)0); + tmpVariables[2] = repository.getOsVersion(); + tmpVariables[3] = repository.getOsPatch(); tmpVariables[4] = KMInteger.uint_8((byte)0); if(tmpVariables[0] != KMType.INVALID_VALUE){ // os version in key characteristics must be less the os version stored in javacard or the @@ -866,7 +952,7 @@ private void processUpgradeKeyCmd(APDU apdu) { } } if(tmpVariables[1] != KMType.INVALID_VALUE){ - // The key characteristics should have has os patch level < os patch level stored in javacard + // The key characteristics should have had os patch level < os patch level stored in javacard // then only upgrade is allowed. if(KMInteger.compare(tmpVariables[1], tmpVariables[3]) != -1){ //Key Should not be upgraded, but error code should be OK, As per VTS. @@ -874,45 +960,19 @@ private void processUpgradeKeyCmd(APDU apdu) { tmpVariables[5] = KMError.INVALID_ARGUMENT; } } -/* KMIntegerTag.getValue( - scratchPad, (short) 0, ); - 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); - } + boolean blobPersisted = false; + if(tmpVariables[5] != KMError.INVALID_ARGUMENT) { + if(repository.validateAuthTag(data[AUTH_TAG])){ + repository.removeAuthTag(data[AUTH_TAG]); + blobPersisted = true; } - } - 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); + // copy origin + data[ORIGIN] = KMEnumTag.getValue(KMType.ORIGIN, data[HW_PARAMETERS]); + // create new key blob with current os version etc. + createEncryptedKeyBlob(scratchPad); + if(blobPersisted){ + repository.persistAuthTag(data[AUTH_TAG]); } - }*/ - // remove Auth Tag - if(tmpVariables[5] != KMError.INVALID_ARGUMENT) { - 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]); } else { data[KEY_BLOB] = KMByteBlob.instance((short)0); } @@ -930,10 +990,6 @@ private void processExportKeyCmd(APDU apdu) { } 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(); @@ -952,7 +1008,6 @@ private void processImportWrappedKeyCmd(APDU apdu) { 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 short args = decoder.decode(tmpVariables[1], buffer, bufferStartOffset, bufferLength); // Step -0 - check whether the key format and algorithm supported @@ -998,28 +1053,26 @@ private void processImportWrappedKeyCmd(APDU apdu) { } // Step 2 - decrypt the encrypted transport key - 32 bytes AES-GCM key // create rsa decipher - KMCipher cipher = - cryptoProvider.createRsaDecipher( - //KMCipher.PAD_PKCS1, // TODO remove this when KMCipher.PAD_PKCS1_OAEP_SHA256 is supported - KMCipher.PAD_PKCS1_OAEP_SHA256, + + //read encrypted transport key from args + tmpVariables[0] = KMArray.cast(args).get((short) 5); + // Decrypt the transport key + tmpVariables[1] = + seProvider.rsaDecipherOAEP256( 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[1] = - cipher.doFinal( + KMByteBlob.cast(data[PUB_KEY]).length(), 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); + //seProvider.delete(cipher); // Step 3 - XOR the decrypted AES-GCM key with with masking key // read masking key tmpVariables[0] = KMArray.cast(args).get((short) 7); @@ -1042,19 +1095,15 @@ private void processImportWrappedKeyCmd(APDU apdu) { 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( + + if(!seProvider.aesGCMDecrypt( KMByteBlob.cast(data[SECRET]).getBuffer(), KMByteBlob.cast(data[SECRET]).getStartOff(), - KMByteBlob.cast(data[SECRET]).length()); - boolean verification = - cryptoProvider.aesGCMDecrypt( - key, + KMByteBlob.cast(data[SECRET]).length(), KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), KMByteBlob.cast(data[INPUT_DATA]).length(), - scratchPad, - (short) 0, + scratchPad, (short) 0, KMByteBlob.cast(data[NONCE]).getBuffer(), KMByteBlob.cast(data[NONCE]).getStartOff(), KMByteBlob.cast(data[NONCE]).length(), @@ -1063,11 +1112,9 @@ private void processImportWrappedKeyCmd(APDU apdu) { 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) { + KMByteBlob.cast(data[AUTH_TAG]).length())){ KMException.throwIt(KMError.VERIFICATION_FAILED); } - cryptoProvider.delete(key); // Step 5 - Import decrypted key data[ORIGIN] = KMType.SECURELY_IMPORTED; @@ -1101,6 +1148,13 @@ private void processAttestKeyCmd(APDU apdu) { data[KEY_PARAMETERS] = KMArray.cast(args).get((short) 1); //parse key blob parseEncryptedKeyBlob(scratchPad); + //TODO This below code is added to pass one of the VTS 4.1 tests. + //TODO Need to confirm with Shawn and modify this accordingly. + tmpVariables[0] = + KMKeyParameters.findTag(KMType.BOOL_TAG, KMType.DEVICE_UNIQUE_ATTESTATION, data[KEY_PARAMETERS]); + if(tmpVariables[0] != KMType.INVALID_VALUE) { + KMException.throwIt(KMError.UNIMPLEMENTED); + } // The key which is being attested should be asymmetric i.e. RSA or EC tmpVariables[0] = KMEnumTag.getValue(KMType.ALGORITHM, data[HW_PARAMETERS]); if(tmpVariables[0] != KMType.RSA && tmpVariables[0] != KMType.EC){ @@ -1108,24 +1162,26 @@ private void processAttestKeyCmd(APDU apdu) { } boolean rsaCert = true; if(tmpVariables[0] == KMType.EC) rsaCert = false; + KMAttestationCert cert = seProvider.getAttestationCert(rsaCert); // Save attestation application id - must be present. tmpVariables[0] = KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.ATTESTATION_APPLICATION_ID,data[KEY_PARAMETERS]); if(tmpVariables[0] == KMType.INVALID_VALUE){ KMException.throwIt(KMError.ATTESTATION_APPLICATION_ID_MISSING); } + cert.extensionTag(tmpVariables[0],false); // Save attestation challenge tmpVariables[0] = KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.ATTESTATION_CHALLENGE,data[KEY_PARAMETERS]); if(tmpVariables[0] == KMType.INVALID_VALUE){ KMException.throwIt(KMError.INVALID_ARGUMENT); } - // extract key characteristics - //makeKeyCharacteristics(scratchPad); + cert.attestationChallenge(KMByteTag.cast(tmpVariables[0]).getValue()); // unique id byte blob - uses application id and temporal month count of creation time. tmpVariables[0] = makeUniqueId(scratchPad); + cert.uniqueId(tmpVariables[0]); // validity period // active time or creation time - byte blob // TODO current assumption is that if active and creation time are missing from characteristics then - // then it is an error. Alternative can be to use 1 Jan 1970 as validity start period. + // then it is an error. tmpVariables[1] = KMKeyParameters.findTag(KMType.DATE_TAG,KMType.ACTIVE_DATETIME,data[SW_PARAMETERS]); if(tmpVariables[1] != KMType.INVALID_VALUE) tmpVariables[1] = KMIntegerTag.cast(tmpVariables[1]).getValue(); else { @@ -1135,6 +1191,7 @@ private void processAttestKeyCmd(APDU apdu) { } // convert milliseconds to UTC date. Start of validity period has to be UTC. tmpVariables[1] = convertToDate(tmpVariables[1], scratchPad, true); + cert.notBefore(tmpVariables[1]); // expiry time - byte blob tmpVariables[2] = KMKeyParameters.findTag(KMType.DATE_TAG,KMType.USAGE_EXPIRE_DATETIME,data[SW_PARAMETERS]); if(tmpVariables[2] != KMType.INVALID_VALUE) { @@ -1146,47 +1203,116 @@ private void processAttestKeyCmd(APDU apdu) { else tmpVariables[2] = convertToDate(tmpVariables[1], scratchPad, true); } else { // if no expiry tag is present then use the attestation key certificate's expiry time // that was provisioned in the provision command. This will be in Generalized or UTC time - tmpVariables[2] = KMByteBlob.instance(repository.getCertDataBuffer(), - repository.getCertExpiryTime(),repository.getCertExpiryTimeLen()); - } - - // buffer for cert - we allocate 1024 bytes buffer - should be sufficient for 2Kbits RSA cert - tmpVariables[3] = KMByteBlob.instance((short)2048); - // read att application id. - tmpVariables[4] = KMKeyParameters.findTag(KMType.BYTES_TAG,KMType.ATTESTATION_APPLICATION_ID,data[KEY_PARAMETERS]); - if(tmpVariables[4] != KMType.INVALID_VALUE) tmpVariables[4] = KMByteTag.cast(tmpVariables[4]).getValue(); - else tmpVariables[4] = 0; - // read att challenge - tmpVariables[5] = KMKeyParameters.findTag(KMType.BYTES_TAG,KMType.ATTESTATION_CHALLENGE,data[KEY_PARAMETERS]); - if(tmpVariables[5] == KMType.INVALID_VALUE) KMException.throwIt(KMError.ATTESTATION_CHALLENGE_MISSING); - tmpVariables[5] = KMByteTag.cast(tmpVariables[5]).getValue(); - - // create X509 certificate. - KMX509Certificate.encodeCert(tmpVariables[3]/*buf*/, data[KEY_CHARACTERISTICS]/*key char*/, - tmpVariables[0]/*unique Id*/,tmpVariables[1]/*start*/,tmpVariables[2]/*end*/, - data[PUB_KEY]/*pub key/modulus*/,tmpVariables[5],tmpVariables[4], rsaCert); - - // Now sign the cert - // Create signer - Signature signer = cryptoProvider.createRsaSigner( - MessageDigest.ALG_SHA_256, - KMCipher.PAD_PKCS1, - repository.getAttKeyExponent(),(short)0, repository.ATT_KEY_EXP_SIZE, - repository.getAttKeyModulus(),(short)0,repository.ATT_KEY_MOD_SIZE); - //Sign the cert - returns the length of complete cert - tmpVariables[1] = KMX509Certificate.sign(signer); - - // Send the response back. This is slightly different we do not copy the cert blob again. - // We just add CBOR encoding around it. - // Encode the response - buffer = KMX509Certificate.getBuffer(); - // add CBOR header and elements - bufferStartOffset = encoder.encodeCert(KMX509Certificate.getBuffer(), KMX509Certificate.getBufferStart(), - KMX509Certificate.getCertStart(),KMX509Certificate.getCertLength()); - bufferLength = (short)(KMX509Certificate.getCertLength() + (KMX509Certificate.getCertStart()- bufferStartOffset)); + // tmpVariables[2] = KMByteBlob.instance(repository.getCertDataBuffer(), + // repository.getCertExpiryTime(),repository.getCertExpiryTimeLen()); + tmpVariables[2] = repository.getCertExpiryTime(); + } + cert.notAfter(tmpVariables[2]); + addAttestationIds(cert); + addTags(KMKeyCharacteristics.cast(data[KEY_CHARACTERISTICS]).getHardwareEnforced(),true,cert); + addTags(KMKeyCharacteristics.cast(data[KEY_CHARACTERISTICS]).getSoftwareEnforced(),false,cert); + if(repository.getAuthKeyId() != 0){ + /* cert.authKey(KMByteBlob.instance( + repository.getCertDataBuffer(), + repository.getAuthKeyId(), + repository.getAuthKeyIdLen())); + */ cert.authKey(repository.getAuthKeyId()); + } + //cert.deviceLocked(repository.deviceLockedFlag); + cert.deviceLocked(repository.getDeviceLock()); + /* cert.issuer( + KMByteBlob.instance( + repository.getCertDataBuffer(), + repository.getIssuer(), + repository.getIssuerLen())); + + */ + cert.issuer(repository.getIssuer()); + cert.publicKey(data[PUB_KEY]); + /* cert.signingKey( + KMByteBlob.instance(repository.getAttKeyExponent(),(short)0, KMRepository.ATT_KEY_EXP_SIZE), + KMByteBlob.instance(repository.getAttKeyModulus(),(short)0,KMRepository.ATT_KEY_MOD_SIZE)); + */ + cert.signingKey(repository.getAttKeyExponent(),repository.getAttKeyModulus()); + //cert.verifiedBootHash( + // KMByteBlob.instance(repository.verifiedBootHash,(short)0,(short)repository.verifiedBootHash.length)); + cert.verifiedBootHash(repository.getVerifiedBootHash()); + + // cert.verifiedBootKey( + // KMByteBlob.instance(repository.verifiedBootKey,(short)0,(short)repository.verifiedBootKey.length)); + cert.verifiedBootKey(repository.getVerifiedBootKey()); + /* + if(repository.selfSignedBootFlag) cert.verifiedState(KMType.SELF_SIGNED_BOOT); + else if(repository.verifiedBootFlag) cert.verifiedState(KMType.VERIFIED_BOOT); + else cert.verifiedState(KMType.UNVERIFIED_BOOT); + + */ + cert.verifiedState(repository.getBootState()); + // buffer for cert - we allocate 2KBytes buffer + // TODO make this buffer size configurable + tmpVariables[3] = KMByteBlob.instance(MAX_CERT_SIZE); + buffer = KMByteBlob.cast(tmpVariables[3]).getBuffer(); + bufferStartOffset = KMByteBlob.cast(tmpVariables[3]).getStartOff(); + bufferLength = KMByteBlob.cast(tmpVariables[3]).length(); + cert.buffer(buffer,bufferStartOffset,bufferLength); + cert.build(); + bufferStartOffset = encoder.encodeCert(buffer, bufferStartOffset, cert.getCertStart(), cert.getCertLength()); + bufferLength = (short)(cert.getCertLength() + (cert.getCertStart()- bufferStartOffset)); sendOutgoing(apdu); } + //-------------------------------- + private void addAttestationIds(KMAttestationCert cert) { + final short[] attTags = new short[]{ + KMType.ATTESTATION_ID_BRAND, + KMType.ATTESTATION_ID_DEVICE, + KMType.ATTESTATION_ID_IMEI, + KMType.ATTESTATION_ID_MANUFACTURER, + KMType.ATTESTATION_ID_MEID, + KMType.ATTESTATION_ID_MODEL, + KMType.ATTESTATION_ID_PRODUCT, + KMType.ATTESTATION_ID_SERIAL}; + byte index = 0; + short attIdTag; + while(index < (short)attTags.length){ + attIdTag = repository.getAttId(mapToAttId(attTags[index])); + if (attIdTag != 0) { + attIdTag = KMByteTag.instance(attTags[index], attIdTag); + cert.extensionTag(attIdTag, true); + } + index++; + } + /* + if(repository.isAttIdSupported()){ + short attTag; + short blob; + byte index = 0; + while (index < repository.ATT_ID_TABLE_SIZE) { + if (repository.getAttIdLen(index) != 0) { + blob = KMByteBlob.instance( + repository.getAttIdBuffer(index), + repository.getAttIdOffset(index), + repository.getAttIdLen(index)); + attTag = KMByteTag.instance(repository.getAttIdTag(index), blob); + cert.extensionTag(attTag,true); + index++; + } + } + } + */ + } + private void addTags(short params, boolean hwEnforced, KMAttestationCert cert) { + short index = 0; + short arr = KMKeyParameters.cast(params).getVals(); + short len = KMArray.cast(arr).length(); + short tag; + while (index < len) { + tag = KMArray.cast(arr).get(index); + cert.extensionTag(tag,hwEnforced); + index++; + } + } + //-------------------------------------- private short convertToDate(short time, byte[] scratchPad, boolean utcFlag){ short yrsCount=0; short monthCount=0; @@ -1289,7 +1415,7 @@ private short convertToDate(short time, byte[] scratchPad, boolean utcFlag){ private short numberToString(short number, byte[] scratchPad, short offset){ byte zero = 0x30; byte len = 2; - byte digit = 0; + byte digit; if(number > 999) len = 4; byte index = len; while(index > 0){ @@ -1336,13 +1462,23 @@ private short makeUniqueId(byte[] scratchPad){ tmpVariables[1]++; // Sign - signature becomes unique id of 32 bits. Use 128 bits master key as an hmac key. - HMACKey key = cryptoProvider.createHMACKey(repository.getMasterKeySecret(),(short)0, - (short)repository.getMasterKeySecret().length); tmpVariables[0] = KMByteBlob.instance((short)32); - tmpVariables[1]=cryptoProvider.hmacSign(key,scratchPad,(short)0,tmpVariables[1], + tmpVariables[2] = repository.getMasterKeySecret(); + /* + tmpVariables[1]= seProvider.hmacSign( + repository.getMasterKeySecret(),(short)0, + (short)repository.getMasterKeySecret(, ).length, + scratchPad,(short)0,tmpVariables[1], + KMByteBlob.cast(tmpVariables[0]).getBuffer(), + KMByteBlob.cast(tmpVariables[0]).getStartOff()); +*/ + tmpVariables[1]= seProvider.hmacSign( + KMByteBlob.cast(tmpVariables[2]).getBuffer(), + KMByteBlob.cast(tmpVariables[2]).getStartOff(), + KMByteBlob.cast(tmpVariables[2]).length(), + scratchPad,(short)0,tmpVariables[1], KMByteBlob.cast(tmpVariables[0]).getBuffer(), KMByteBlob.cast(tmpVariables[0]).getStartOff()); - if(tmpVariables[1] != 32){ KMException.throwIt(KMError.UNKNOWN_ERROR); } @@ -1351,14 +1487,12 @@ private short makeUniqueId(byte[] scratchPad){ private short countTemporalCount(short time, byte[] scratchPad){ Util.arrayFillNonAtomic(scratchPad,(short)0,(short)24, (byte)0); - short result = 0; Util.arrayCopyNonAtomic(KMInteger.cast(time).getBuffer(), KMInteger.cast(time).getStartOff(), scratchPad,(short)(8-KMInteger.cast(time).length()), KMInteger.cast(time).length()); Util.arrayCopyNonAtomic(oneMonthMsec,(short)0,scratchPad,(short)8,(short)8); - result = divide(scratchPad, (short)0,(short)8,(short)16); - return result; + return divide(scratchPad, (short)0,(short)8,(short)16); } private void processDestroyAttIdsCmd(APDU apdu) { @@ -1386,7 +1520,6 @@ private void processAbortOperationCmd(APDU apdu) { } private void processFinishOperationCmd(APDU apdu) { - // TODO AES GCM receiveIncoming(apdu); byte[] scratchPad = apdu.getBuffer(); tmpVariables[1] = KMArray.instance((short) 6); @@ -1451,94 +1584,56 @@ private void processFinishOperationCmd(APDU apdu) { private void finishEncryptOperation(KMOperationState op, byte[] scratchPad) { short len = KMByteBlob.cast(data[INPUT_DATA]).length(); switch(op.getAlgorithm()){ - // Only supported for testing purpose - // TODO remove this later on + // + // RSA Encryption is only supported for testing purpose + // TODO remove this later on if not required + // case KMType.RSA: + // Output size is always 256 bytes data[OUTPUT_DATA] = KMByteBlob.instance((short)256); - // Fill the scratch pad with zero Util.arrayFillNonAtomic(scratchPad,(short)0, (short)256, (byte)0); - if(op.getPadding() == KMType.PADDING_NONE){ - // Length cannot be greater then key size according to jcard sim - if(len >= 256) KMException.throwIt(KMError.INVALID_INPUT_LENGTH); - /* // If Length is same as key size then - // compare the data with key value - date should be less then key value. - if(len == 255) { - // 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); - } - } -*/ // copy input data to scratchpad. - //TODO the current jacrdsim implementation requires 255 bytes when using encryption with no pad - Util.arrayCopyNonAtomic( - KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), - KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), - scratchPad, (short)(255 - len),len); - len = (short)255; - }else{ - //copy input data to scratchpad. - Util.arrayCopyNonAtomic( - KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), - KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), - scratchPad, (short)0,len); - } - len = op.getCipher().doFinal( - scratchPad, (short)0,len, KMByteBlob.cast(data[OUTPUT_DATA]).getBuffer(), - KMByteBlob.cast(data[OUTPUT_DATA]).getStartOff()); + op.getOperation().finish( + KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), + KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), + KMByteBlob.cast(data[INPUT_DATA]).length(), + KMByteBlob.cast(data[OUTPUT_DATA]).getBuffer(), + KMByteBlob.cast(data[OUTPUT_DATA]).getStartOff()); break; case KMType.AES: - if(op.getBlockMode() == KMType.GCM){ - finishAesGcmOperation(op, scratchPad); - return; - } case KMType.DES: if(op.getAlgorithm() == KMType.AES){ - tmpVariables[0] = KMCipher.AES_BLOCK_SIZE; + tmpVariables[0] = AES_BLOCK_SIZE; }else{ - tmpVariables[0] = KMCipher.DES_BLOCK_SIZE; + tmpVariables[0] = DES_BLOCK_SIZE; } //If no padding then data length must be block aligned if ((op.getBlockMode() == KMType.ECB || op.getBlockMode() == KMType.CBC) && op.getPadding() == KMType.PADDING_NONE && ((short)(len % tmpVariables[0]) != 0)){ KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + }else if(op.getBlockMode() == KMType.GCM){ + // update aad if there is any + updateAAD(op, (byte)0x01); + // Get the output size + len = op.getOperation().getAESGCMOutputSize(len, (short)(op.getMacLength()/8)); + data[OUTPUT_DATA] = KMByteBlob.instance(len); } //If padding i.e. pkcs7 then add padding to right - if(op.getPadding() == KMType.PKCS7){ - // padding bytes - if(len % tmpVariables[0] == 0) tmpVariables[1] = tmpVariables[0]; - else tmpVariables[1] = (short)(tmpVariables[0] - (len % tmpVariables[0])); - // final len with padding - len = (short)(len+tmpVariables[1]); - // intermediate buffer to copy input data+padding - tmpVariables[2] = KMByteBlob.instance(len); - // fill in the padding - Util.arrayFillNonAtomic( - KMByteBlob.cast(tmpVariables[2]).getBuffer(), - KMByteBlob.cast(tmpVariables[2]).getStartOff(), - KMByteBlob.cast(tmpVariables[2]).length(), - (byte)tmpVariables[1]); - //copy the input data - Util.arrayCopyNonAtomic( - KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), - KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), - KMByteBlob.cast(tmpVariables[2]).getBuffer(), - KMByteBlob.cast(tmpVariables[2]).getStartOff(), - KMByteBlob.cast(data[INPUT_DATA]).length()); - data[INPUT_DATA] = tmpVariables[2]; - } - data[OUTPUT_DATA] = KMByteBlob.instance(len); - len = op.getCipher().doFinal( - KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), - KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), - KMByteBlob.cast(data[INPUT_DATA]).length(), - KMByteBlob.cast(data[OUTPUT_DATA]).getBuffer(), - KMByteBlob.cast(data[OUTPUT_DATA]).getStartOff()); + // Output data can at most one block size more the input data in case of pkcs7 encryption + tmpVariables[0] = KMByteBlob.instance((short)(len+tmpVariables[0])); + len = op.getOperation().finish(KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), + KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), + KMByteBlob.cast(data[INPUT_DATA]).length(), + KMByteBlob.cast(tmpVariables[0]).getBuffer(), + KMByteBlob.cast(tmpVariables[0]).getStartOff()); + + data[OUTPUT_DATA] = KMByteBlob.instance( + KMByteBlob.cast(tmpVariables[0]).getBuffer(), + KMByteBlob.cast(tmpVariables[0]).getStartOff(), + len); break; + default: + KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM); + break; } } @@ -1552,42 +1647,39 @@ private void finishDecryptOperation(KMOperationState op, byte[] scratchPad) { Util.arrayFillNonAtomic(scratchPad,(short)0, (short)256, (byte)0); if(op.getPadding() == KMType.PADDING_NONE && len != 256) KMException.throwIt(KMError.INVALID_INPUT_LENGTH); - len = op.getCipher().doFinal( - KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), + len = op.getOperation().finish(KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), len, scratchPad, (short)0); + data[OUTPUT_DATA] = KMByteBlob.instance(scratchPad,(short)0, len); break; case KMType.AES: - if(op.getBlockMode() == KMType.GCM){ - finishAesGcmOperation(op, scratchPad); - return; - } case KMType.DES: if(op.getAlgorithm() == KMType.AES){ - tmpVariables[0] = KMCipher.AES_BLOCK_SIZE; + tmpVariables[0] = AES_BLOCK_SIZE; }else{ - tmpVariables[0] = KMCipher.DES_BLOCK_SIZE; + tmpVariables[0] = DES_BLOCK_SIZE; } - if((op.getBlockMode() == KMType.CBC || op.getBlockMode() == KMType.ECB)&& len > 0 && - (len%tmpVariables[0]) != 0)KMException.throwIt(KMError.INVALID_INPUT_LENGTH); tmpVariables[1] = repository.alloc(len); + if((op.getBlockMode() == KMType.CBC || op.getBlockMode() == KMType.ECB)&& len > 0 && + (len%tmpVariables[0]) != 0){KMException.throwIt(KMError.INVALID_INPUT_LENGTH);} + else if(op.getBlockMode() == KMType.GCM){ + // update aad if there is any + updateAAD(op, (byte)0x01); + // Check if there is at least MAC Length bytes of input data + if((len < (short)(op.getMacLength()/8))){ + KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + } + // Get the output size - in case of JCardSim this will more then input size + tmpVariables[0] = op.getOperation().getAESGCMOutputSize(len, (short)(op.getMacLength()/8)); + tmpVariables[1] = repository.alloc(tmpVariables[0]); + } byte[] heap = repository.getHeap(); - len = op.getCipher().doFinal( - KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), + len = op.getOperation().finish(KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), len,heap, tmpVariables[1]); - //remove padding bytes if pkcs7 - if(op.getPadding() == KMType.PKCS7 && len >0) { - //verify if padding is corrupted. - byte paddingByte = heap[(short)(tmpVariables[1]+len -1)]; - //padding byte always should be <= blocksize - if((short)paddingByte > tmpVariables[0] || - (short)paddingByte <= 0) KMException.throwIt(KMError.INVALID_ARGUMENT); - len = (short)(len - (short)paddingByte); - } - //If padding i.e. pkcs7 then add padding to right + data[OUTPUT_DATA] = KMByteBlob.instance(heap, tmpVariables[1], len); break; } @@ -1616,333 +1708,65 @@ private void updateAAD(KMOperationState op, byte finish){ } // If allowed the update the aad tmpVariables[1] = KMByteTag.cast(tmpVariables[1]).getValue(); - op.getCipher().updateAAD( - KMByteBlob.cast(tmpVariables[1]).getBuffer(), + + op.getOperation().updateAAD(KMByteBlob.cast(tmpVariables[1]).getBuffer(), KMByteBlob.cast(tmpVariables[1]).getStartOff(), KMByteBlob.cast(tmpVariables[1]).length()); } } - - private void updateAesGcmOperation(KMOperationState op, APDU apdu) { - updateAAD(op, (byte) 0x00); - // Now handle the input data - tmpVariables[0] = KMByteBlob.cast(data[INPUT_DATA]).length(); - // If the input data is non zero length - if (tmpVariables[0] > 0) { - // input data must be block aligned. - if (tmpVariables[0] % AES_BLOCK_SIZE != 0) { - KMException.throwIt(KMError.INVALID_INPUT_LENGTH); - } - // no more future updateAAD allowed. - if (op.isAesGcmUpdateAllowed()) { - op.setAesGcmUpdateComplete(); - } - // Adjust input data wrt to last aes block and saved aes block - tmpVariables[0] = (short) (tmpVariables[0] - AES_BLOCK_SIZE); - if (op.isAesBlockSaved()) { - tmpVariables[0] = (short) (tmpVariables[0] + AES_BLOCK_SIZE); - } - // Allocate new data buffer in which input data will be assembled - tmpVariables[1] = KMByteBlob.instance(tmpVariables[0]); - if (op.isAesBlockSaved() && tmpVariables[0] > 0) { - // First copy the previously saved block to the buffer - Util.arrayCopy( - op.getAesBlock(), - (short) 0, - KMByteBlob.cast(tmpVariables[1]).getBuffer(), - KMByteBlob.cast(tmpVariables[1]).getStartOff(), - AES_BLOCK_SIZE); - tmpVariables[0] = (short)(tmpVariables[0] -AES_BLOCK_SIZE); - if (tmpVariables[0] > 0) { - // Then copy rest of the input data to the buffer - Util.arrayCopy( - KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), - KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), - KMByteBlob.cast(tmpVariables[1]).getBuffer(), - (short) (KMByteBlob.cast(tmpVariables[1]).getStartOff() + AES_BLOCK_SIZE), - tmpVariables[0]); - } - } else { - if (tmpVariables[0] > 0) { - Util.arrayCopy( - KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), - KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), - KMByteBlob.cast(tmpVariables[1]).getBuffer(), - (short) (KMByteBlob.cast(tmpVariables[1]).getStartOff()), - tmpVariables[0]); - } - } - - // Save the last aes block from input data into the op state - tmpVariables[0] = (short) (KMByteBlob.cast(data[INPUT_DATA]).length() - AES_BLOCK_SIZE); - op.setAesBlock( - KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), - (short) (KMByteBlob.cast(data[INPUT_DATA]).getStartOff() + tmpVariables[0]), - AES_BLOCK_SIZE); - data[INPUT_DATA] = tmpVariables[1]; - } - - data[OUTPUT_DATA] = KMByteBlob.instance(KMByteBlob.cast(data[INPUT_DATA]).length()); - // Update the rest of the data - if (KMByteBlob.cast(data[INPUT_DATA]).length() > 0) { - // allocate output data buffer as input data is always block aligned. - tmpVariables[0] = - op.getCipher() - .update( - KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), - KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), - KMByteBlob.cast(data[INPUT_DATA]).length(), - KMByteBlob.cast(data[OUTPUT_DATA]).getBuffer(), - KMByteBlob.cast(data[OUTPUT_DATA]).getStartOff()); - //TODO: Not always the output length is equal to input length. - //TODO: Few VTS tests fail because of this below code. - //if (tmpVariables[0] != KMByteBlob.cast(data[INPUT_DATA]).length()) { - // KMException.throwIt(KMError.UNKNOWN_ERROR); - //} - //In case if output length not equal to input length. Allocate actual size of output. - if(tmpVariables[0] != KMByteBlob.cast(data[INPUT_DATA]).length()) { - data[INPUT_DATA] = data[OUTPUT_DATA]; - data[OUTPUT_DATA] = KMByteBlob.instance(tmpVariables[0]); - Util.arrayCopy(KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), - KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), - KMByteBlob.cast(data[OUTPUT_DATA]).getBuffer(), - KMByteBlob.cast(data[OUTPUT_DATA]).getStartOff(), - tmpVariables[0]); - } - } - // make response - tmpVariables[1] = KMArray.instance((short) 0); - tmpVariables[1] = KMKeyParameters.instance(tmpVariables[1]); - tmpVariables[2] = KMArray.instance((short) 4); - 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[2], buffer, bufferStartOffset); - sendOutgoing(apdu); - } - - private void finishAesGcmOperation(KMOperationState op, byte[] scratchPad) { - // update aad if there is any and if it is allowed - updateAAD(op, (byte)0x01); - // Check if there at least MAC Length length of data - if(data[INPUT_DATA] == KMType.INVALID_VALUE){ - KMException.throwIt(KMError.INVALID_ARGUMENT); - } - tmpVariables[0] = KMByteBlob.cast(data[INPUT_DATA]).length(); - tmpVariables[1] = KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.ASSOCIATED_DATA,data[KEY_PARAMETERS]); - if(!op.isAesBlockSaved() && - (tmpVariables[0] < (short)(op.getMacLength()/8)) && - (op.getPurpose()==KMType.DECRYPT) && - (tmpVariables[1] == KMType.INVALID_VALUE)){ - KMException.throwIt(KMError.INVALID_INPUT_LENGTH); - } - // Now add the aes block saved in op.state to input data - if(op.isAesBlockSaved()){ - tmpVariables[0] = KMByteBlob.instance((short)(tmpVariables[0]+AES_BLOCK_SIZE)); - Util.arrayCopy(op.getAesBlock(), (short)0, - KMByteBlob.cast(tmpVariables[0]).getBuffer(), - KMByteBlob.cast(tmpVariables[0]).getStartOff(), - (short)op.getAesBlock().length); - Util.arrayCopy( - KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), - KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), - KMByteBlob.cast(tmpVariables[0]).getBuffer(), - (short)(KMByteBlob.cast(tmpVariables[0]).getStartOff()+op.getAesBlock().length), - KMByteBlob.cast(data[INPUT_DATA]).length()); - data[INPUT_DATA] = tmpVariables[0]; - } - // Allocate output data buffer based on mac length and encrypt or decrypt operation - tmpVariables[0] = op.getCipher().getAesGcmOutputSize(KMByteBlob.cast(data[INPUT_DATA]).length(), - (short)(op.getMacLength()/8)); - /* if(op.getPurpose() == KMType.ENCRYPT){ - data[OUTPUT_DATA] = KMByteBlob.instance((short)(tmpVariables[0]+(op.getMacLength()/8))); - }else{ - data[OUTPUT_DATA] = KMByteBlob.instance((short)(tmpVariables[0]-(op.getMacLength()/8))); - } - */ - data[OUTPUT_DATA] = KMByteBlob.instance(tmpVariables[0]); - //This will throw KMError.VERIFICATION_FAILED if the tag does not match during decrypt. - tmpVariables[0] = op.getCipher().doFinal( - KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), - KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), - KMByteBlob.cast(data[INPUT_DATA]).length(), - KMByteBlob.cast(data[OUTPUT_DATA]).getBuffer(), - KMByteBlob.cast(data[OUTPUT_DATA]).getStartOff()); - if(tmpVariables[0] != KMByteBlob.cast(data[OUTPUT_DATA]).length()){ - KMException.throwIt(KMError.UNKNOWN_ERROR); - } - - /* //TODO HACK The SunJCE update multipart decryption does not return any output instead it stores - // the input. The doFinal call returns the complete plain text at once. so here as a hack - // we are allocation a 256 buffer(VTS MAX input message size) to store the plain text. - if(KMCipher.SUN_JCE == op.getCipher().getCipherProvider() && op.getPurpose() == KMType.DECRYPT) { - if (KMCipherImpl.aes_gcm_decrypt_final_data != 0x00) { - data[OUTPUT_DATA] = KMCipherImpl.aes_gcm_decrypt_final_data; - KMCipherImpl.aes_gcm_decrypt_final_data = 0x00; - } - } -*/ - } - - private void beginAesGcmOperation(KMOperationState op) { - short purpose; - // TODO [Venkat] What is the reason to make data[OP_HANDLE] to KMType.INVALID_VALUE - //This is commented because if some exception is thrown below data[OP_HANDLE] points to - // INVALID_VALUE and is not getting cleared from OperationState. - //data[OP_HANDLE] = KMType.INVALID_VALUE; - if (op.getPurpose() == KMType.ENCRYPT) { - purpose = KMCipher.MODE_ENCRYPT; - if (data[IV] == KMType.INVALID_VALUE) { - data[IV] = KMByteBlob.instance((short) 12); - cryptoProvider.newRandomNumber( - KMByteBlob.cast(data[IV]).getBuffer(), - KMByteBlob.cast(data[IV]).getStartOff(), - KMByteBlob.cast(data[IV]).length()); - } - } else { - purpose = KMCipher.MODE_DECRYPT; - } - op.setAesGcmUpdateStart(); - op.setKey(KMByteBlob.cast(data[SECRET]).getBuffer(), - KMByteBlob.cast(data[SECRET]).getStartOff(), - KMByteBlob.cast(data[SECRET]).length()); - try { - //TODO [Venkat] CryptoException is not been converted to KMException here. - op.setCipher( - cryptoProvider.createAesGcmCipher( - purpose, - op.getMacLength(), - 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())); - } catch (CryptoException exception) { - if(exception.getReason() == CryptoException.ILLEGAL_VALUE) - KMException.throwIt(KMError.INVALID_ARGUMENT); - else if(exception.getReason() == CryptoException.NO_SUCH_ALGORITHM) - KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM); - } - //data[OP_HANDLE] = op.getHandle(); - } private void finishSigningVerifyingOperation(KMOperationState op, byte[]scratchPad) { - short len = KMByteBlob.cast(data[INPUT_DATA]).length(); + Util.arrayFillNonAtomic(scratchPad,(short)0, (short)256, (byte)0); switch(op.getAlgorithm()){ case KMType.RSA: + // Output for signature is always 256 bytes. data[OUTPUT_DATA] = KMByteBlob.instance((short)256); - // No digest and no padding - This case is not supported in javacard api - // However as there is no padding we can treat signing as a RSA decryption operation. - if(op.getDigest() == KMType.DIGEST_NONE && op.getPadding() == KMType.PADDING_NONE){ - if(op.getPurpose() == KMType.SIGN){ - /* - // Length cannot be greater then key size - if(len > op.getKeySize()) KMException.throwIt(KMError.INVALID_INPUT_LENGTH); - // If Length is same as key size then - // compare the data with key value - data should be less then key value. - if(len == op.getKeySize()) { - 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); - } - }*/ - }else{//Verify - if(len != 256) KMException.throwIt(KMError.INVALID_INPUT_LENGTH); - } -/* // Fill the scratch pad with zero - Util.arrayFillNonAtomic(scratchPad,(short)0, (short)256, (byte)0); - // Everything is fine so copy input data to scratchpad. - Util.arrayCopyNonAtomic( - KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), - KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), - scratchPad, (short)(256 - len),len); - len = (short)256;*/ - Util.arrayCopyNonAtomic( - KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), - KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), - scratchPad, (short)0,len); - }else if (op.getDigest() == KMType.DIGEST_NONE && op.getPadding() == KMType.RSA_PKCS1_1_5_SIGN) { - /* // If PKCS1 padding and no digest - then 0x01||0x00||PS||0x00 on left such that PS >= 8 bytes - // Data Length should be atleast 11 less then the key size - which is 256 bytes - if(len > (short)(op.getKeySize() - 11)){ - KMException.throwIt(KMError.INVALID_INPUT_LENGTH); - } - // Fill the scratch pad with pkcs1 padding zero according to RFC 2313 section 8.1 - // Signing is done using private key. - Util.arrayFillNonAtomic(scratchPad,(short)0, (short)256, (byte)0); - scratchPad[0] = 0x00; - scratchPad[1] = 0x01; - //cryptoProvider.newRandomNumber(scratchPad, (short)2, (short)8); - // We fill in 0xFF as PS following the javacard pkcs1 padding example. - tmpVariables[0] = (short)(op.getKeySize()-len-3); - Util.arrayFillNonAtomic(scratchPad,(short)2,tmpVariables[0],(byte)0xFF); - scratchPad[(short)(tmpVariables[0]+2)] = 0x00; - //copy the rest of the data on scratch pad. - Util.arrayCopyNonAtomic( - KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), - KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), - scratchPad, (short)(tmpVariables[0]+3),len); - len = op.getKeySize(); // this will be 256*/ - Util.arrayCopyNonAtomic( - KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), - KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), - scratchPad, (short)0,len); - - }else if(op.getDigest()==KMType.SHA2_256 && - (op.getPadding() == KMType.RSA_PKCS1_1_5_SIGN ||op.getPadding() == KMType.RSA_PSS)){ - // Normal case with PKCS1 or PSS padding and with Digest SHA256 - // Fill the scratch pad with zero - Util.arrayFillNonAtomic(scratchPad,(short)0, (short)256, (byte)0); - // 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{ - KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM); + // If there is no padding we can treat signing as a RSA decryption operation. + if (op.getDigest() == KMType.DIGEST_NONE && op.getPadding() == KMType.PADDING_NONE) { + // Input data of Verify operation must be 256 bytes + if (op.getPurpose() == KMType.VERIFY && KMByteBlob.cast(data[INPUT_DATA]).length() != 256) { + KMException.throwIt(KMError.INVALID_INPUT_LENGTH); } + } + try { if(op.getPurpose() == KMType.SIGN){ // len of signature will be 256 bytes - try { - len = op.getSignerVerifier().sign(scratchPad,(short)0,len, - KMByteBlob.cast(data[OUTPUT_DATA]).getBuffer(), - KMByteBlob.cast(data[OUTPUT_DATA]).getStartOff()); - } catch (CryptoException e) { - KMException.throwIt(KMError.INVALID_ARGUMENT); - } + op.getOperation().sign( + KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), + KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), + KMByteBlob.cast(data[INPUT_DATA]).length(), + KMByteBlob.cast(data[OUTPUT_DATA]).getBuffer(), + KMByteBlob.cast(data[OUTPUT_DATA]).getStartOff()); }else{ - if(!op.getSignerVerifier().verify(scratchPad,(short)0,len, + if(!op.getOperation().verify( + KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), + KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), + KMByteBlob.cast(data[INPUT_DATA]).length(), KMByteBlob.cast(data[SIGNATURE]).getBuffer(), KMByteBlob.cast(data[SIGNATURE]).getStartOff(), KMByteBlob.cast(data[SIGNATURE]).length())){ KMException.throwIt(KMError.VERIFICATION_FAILED); } } + } catch (CryptoException e) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } break; case KMType.EC: - // Fill the scratch pad with zero - Util.arrayFillNonAtomic(scratchPad,(short)0, (short)256, (byte)0); - // If DIGEST NONE then truncate the data to 32 bytes. - // TODO Confirm whether this case needs to be supported as javacard does not support. + short len = KMByteBlob.cast(data[INPUT_DATA]).length(); + // If DIGEST NONE then truncate the input data to 32 bytes. if(op.getDigest() == KMType.DIGEST_NONE && len > 32){ len = 32; } if(op.getPurpose() == KMType.SIGN){ // len of signature will be 512 bits i.e. 64 bytes - len = op.getSignerVerifier().sign( - KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), - KMByteBlob.cast(data[INPUT_DATA]).getStartOff(),len, - scratchPad,(short)0); + len = op.getOperation().sign( + KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), + KMByteBlob.cast(data[INPUT_DATA]).getStartOff(),len, + scratchPad,(short)0); data[OUTPUT_DATA] = KMByteBlob.instance(scratchPad,(short)0, len); }else{ - if(!op.getSignerVerifier().verify( + if(!op.getOperation().verify( KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), KMByteBlob.cast(data[INPUT_DATA]).getStartOff(),len, KMByteBlob.cast(data[SIGNATURE]).getBuffer(), @@ -1958,12 +1782,14 @@ private void finishSigningVerifyingOperation(KMOperationState op, byte[]scratchP //length or less than that in case if it is less than 32 we are truncating it and sending //back to the user. For Verify user will send the truncated and if we pass the truncated //signature to javacard verify API it will fail because it expects the full length signature. - Util.arrayFillNonAtomic(scratchPad,(short)0, (short)256, (byte)0); // digest is always present. // len of signature will always be 32 bytes. - len = op.getSignerVerifier().sign(KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), - KMByteBlob.cast(data[INPUT_DATA]).getStartOff(),len,scratchPad, - (short)0); + op.getOperation().sign( + KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), + KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), + KMByteBlob.cast(data[INPUT_DATA]).length(), + scratchPad, (short)0); + // Copy only signature of mac length size. data[OUTPUT_DATA] = KMByteBlob.instance(scratchPad,(short)0, (short) (op.getMacLength() / 8)); if(op.getPurpose() == KMType.VERIFY){ @@ -1998,14 +1824,6 @@ private void finishTrustedConfirmationOperation(KMOperationState op) { KMByteBlob.cast(tmpVariables[0]).getBuffer(), KMByteBlob.cast(tmpVariables[0]).getStartOff(), KMByteBlob.cast(tmpVariables[0]).length()); - /* - 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(!verified){ KMException.throwIt(KMError.VERIFICATION_FAILED); } @@ -2017,7 +1835,7 @@ private void authorizeUpdateFinishOperation(KMOperationState op, byte[] scratchP // If one time user Authentication is required if (op.isSecureUserIdReqd() && !op.isAuthTimeoutValidated()) { validateVerificationToken(op, data[VERIFICATION_TOKEN], scratchPad); - tmpVariables[0] = KMInteger.uint_64(op.getAuthTime(), (short) 0); + tmpVariables[0] = op.getAuthTime(); tmpVariables[2] = KMVerificationToken.cast(data[VERIFICATION_TOKEN]).getTimestamp(); if (tmpVariables[2] == KMType.INVALID_VALUE) { KMException.throwIt(KMError.VERIFICATION_FAILED); @@ -2025,13 +1843,14 @@ private void authorizeUpdateFinishOperation(KMOperationState op, byte[] scratchP if (KMInteger.compare(tmpVariables[0], tmpVariables[2]) < 0) { KMException.throwIt(KMError.KEY_USER_NOT_AUTHENTICATED); } + //TODO this is not needed op.setAuthTimeoutValidated(true); } else if(op.isAuthPerOperationReqd()){ // If Auth per operation is required tmpVariables[0] = KMHardwareAuthToken.cast(data[HW_TOKEN]).getChallenge(); if (KMInteger.compare(data[OP_HANDLE], tmpVariables[0]) != 0) { KMException.throwIt(KMError.KEY_USER_NOT_AUTHENTICATED); } - authenticateUser(scratchPad); + authenticateUser(); } } @@ -2041,17 +1860,23 @@ private void authorizeDeviceUnlock(short hwToken) { short ptr = KMKeyParameters.findTag( KMType.BOOL_TAG, KMType.UNLOCKED_DEVICE_REQUIRED, data[HW_PARAMETERS]); - if (ptr != KMType.INVALID_VALUE && repository.deviceLockedFlag) { + + if (ptr != KMType.INVALID_VALUE && repository.getDeviceLock()) { if (hwToken == KMType.INVALID_VALUE) KMException.throwIt(KMError.DEVICE_LOCKED); ptr = KMHardwareAuthToken.cast(hwToken).getTimestamp(); // Check if the current auth time stamp is greater then device locked time stamp - if (KMInteger.compare(ptr, KMInteger.uint_64(repository.deviceLockedTimestamp, (short) 0)) + short ts = repository.getDeviceTimeStamp(); + /*if (KMInteger.compare(ptr, KMInteger.uint_64(repository.deviceLockedTimestamp, (short) 0)) <= 0) { KMException.throwIt(KMError.DEVICE_LOCKED); + } + */ + if (KMInteger.compare(ptr, ts) <= 0) { + KMException.throwIt(KMError.DEVICE_LOCKED); } // Now check if the device unlock requires password only authentication and whether // auth token is generated through password authentication or not. - if (repository.deviceUnlockPasswordOnly) { + if (repository.getDeviceLockPasswordOnly()) { ptr = KMHardwareAuthToken.cast(hwToken).getHwAuthenticatorType(); ptr = KMEnum.cast(ptr).getVal(); if (((byte) ptr & KMType.PASSWORD) == 0) { @@ -2059,15 +1884,16 @@ private void authorizeDeviceUnlock(short hwToken) { } } // Unlock the device - repository.deviceLockedFlag = false; - Util.arrayFillNonAtomic(repository.deviceLockedTimestamp, (short) 0, (short) 8, (byte) 0); + //repository.deviceLockedFlag = false; + repository.setDeviceLock(false); + repository.clearDeviceLockTimeStamp(); + //Util.arrayFillNonAtomic(repository.deviceLockedTimestamp, (short) 0, (short) 8, (byte) 0); } } 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; @@ -2082,15 +1908,13 @@ private void validateVerificationToken(KMOperationState op, short verToken, byte private void validateVerificationToken(short verToken, byte[] scratchPad) { short ptr = KMVerificationToken.cast(verToken).getMac(); - short len = 0; + short len; // If mac length is zero then token is empty. if (KMByteBlob.cast(ptr).length() == 0) { return; } // concatenation length will be 37 + length of verified parameters list - which is typically empty Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 256, (byte) 0); - short params = KMVerificationToken.cast(verToken).getParametersVerified(); - // Add "Auth Verification" - 17 bytes. Util.arrayCopy(authVerification,(short)0, scratchPad, (short)0, (short)authVerification.length); len = (short)authVerification.length; @@ -2114,38 +1938,34 @@ private void validateVerificationToken(short verToken, byte[] scratchPad) { len += KMByteBlob.cast(ptr).getValues(scratchPad, (short) 0); } // hmac the data - HMACKey key = - cryptoProvider.createHMACKey( - repository.getComputedHmacKey(), - (short) 0, - (short) repository.getComputedHmacKey().length); ptr = KMVerificationToken.cast(verToken).getMac(); + short key = repository.getComputedHmacKey(); + /*boolean verified = + seProvider.hmacVerify(repository.getComputedHmacKey(), + (short) 0, + (short) repository.getComputedHmacKey().length, + scratchPad, (short) 0, len, + KMByteBlob.cast(ptr).getBuffer(), + KMByteBlob.cast(ptr).getStartOff(), + KMByteBlob.cast(ptr).length()); + + */ boolean verified = - cryptoProvider.hmacVerify(key, scratchPad, (short) 0, len, + seProvider.hmacVerify( + KMByteBlob.cast(key).getBuffer(), + KMByteBlob.cast(key).getStartOff(), + KMByteBlob.cast(key).length(), + 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) { + if(!verified){ 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); @@ -2165,75 +1985,87 @@ private void processUpdateOperationCmd(APDU apdu) { 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); + // Input data must be present even if it is zero length. + if(data[INPUT_DATA] == KMType.INVALID_VALUE){ + KMException.throwIt(KMError.INVALID_ARGUMENT); + } // Check Operation Handle and get op state // Check Operation Handle tmpVariables[1] = KMInteger.cast(data[OP_HANDLE]).getShort(); - KMOperationState op = repository.findOperation(tmpVariables[1]); - if (op == null) { - KMException.throwIt(KMError.INVALID_OPERATION_HANDLE); - } + KMOperationState op = repository.findOperation(tmpVariables[1]); + if (op == null) KMException.throwIt(KMError.INVALID_OPERATION_HANDLE); // authorize the update operation authorizeUpdateFinishOperation(op, scratchPad); // If signing without digest then do length validation checks if (op.getPurpose() == KMType.SIGN || op.getPurpose() == KMType.VERIFY ) { - if(data[INPUT_DATA] == KMType.INVALID_VALUE){ - KMException.throwIt(KMError.INVALID_ARGUMENT); - } tmpVariables[0] = KMByteBlob.cast(data[INPUT_DATA]).length(); // update the data. - op.getSignerVerifier() - .update( - KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), - KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), - KMByteBlob.cast(data[INPUT_DATA]).length()); + op.getOperation().update( KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), + KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), + KMByteBlob.cast(data[INPUT_DATA]).length()); // update trusted confirmation operation updateTrustedConfirmationOperation(op); data[OUTPUT_DATA] = KMType.INVALID_VALUE; - } - if (op.getPurpose() == KMType.ENCRYPT || op.getPurpose() == KMType.DECRYPT){ - // TODO Update for encrypt/decrypt using RSA will not be supported because to do this op state + } else if (op.getPurpose() == KMType.ENCRYPT || op.getPurpose() == KMType.DECRYPT){ + // Update for encrypt/decrypt using RSA will not be supported because to do this op state // will have to buffer the data - so reject the update if it is rsa algorithm. - if(op.getAlgorithm() == KMCipher.CIPHER_RSA) { + if(op.getAlgorithm() == KMType.RSA) { KMException.throwIt(KMError.OPERATION_CANCELLED); } //TODO refactor and optimize this - if (op.getAlgorithm() == KMType.AES && op.getBlockMode() == KMType.GCM) { - updateAesGcmOperation(op, apdu); - return; - } - if(data[INPUT_DATA] == KMType.INVALID_VALUE){ - KMException.throwIt(KMError.INVALID_ARGUMENT); - } tmpVariables[0] = KMByteBlob.cast(data[INPUT_DATA]).length(); if (op.getAlgorithm() == KMType.AES) { + if (op.getBlockMode() == KMType.GCM) { + updateAAD(op, (byte) 0x00); + // if input data present + if (tmpVariables[0] > 0) { + if (tmpVariables[0] % AES_BLOCK_SIZE != 0) { + KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + } + // no more future updateAAD allowed if input data present. + if (op.isAesGcmUpdateAllowed()) { + op.setAesGcmUpdateComplete(); + } + } + } else { // input data must be block aligned. // 128 bit block size - HAL must send block aligned data if (tmpVariables[0] % AES_BLOCK_SIZE != 0 || tmpVariables[0] <=0) { KMException.throwIt(KMError.INVALID_INPUT_LENGTH); } } - if (op.getAlgorithm() == KMType.DES) { + }else if (op.getAlgorithm() == KMType.DES) { // 64 bit block size - HAL must send block aligned data if (tmpVariables[0] % DES_BLOCK_SIZE != 0) { KMException.throwIt(KMError.INVALID_INPUT_LENGTH); } - } + } //Allocate output buffer as input data is already block aligned data[OUTPUT_DATA] = KMByteBlob.instance(tmpVariables[0]); // Otherwise just update the data. - tmpVariables[0] = - op.getCipher() - .update( - KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), - KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), - KMByteBlob.cast(data[INPUT_DATA]).length(), - KMByteBlob.cast(data[OUTPUT_DATA]).getBuffer(), - KMByteBlob.cast(data[OUTPUT_DATA]).getStartOff()); - // update must fully process all of the input data - if(tmpVariables[0] != KMByteBlob.cast(data[OUTPUT_DATA]).length()){ - KMException.throwIt(KMError.UNKNOWN_ERROR); - } + try{ + tmpVariables[0] = op.getOperation().update(KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), + KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), + KMByteBlob.cast(data[INPUT_DATA]).length(), + KMByteBlob.cast(data[OUTPUT_DATA]).getBuffer(), + KMByteBlob.cast(data[OUTPUT_DATA]).getStartOff()); + } catch (CryptoException e) { + KMException.throwIt(KMError.INVALID_TAG); + } + // Adjust the Output data if it is not equal to input data. + // This happens in case of JCardSim provider. + if(tmpVariables[0] != KMByteBlob.cast(data[INPUT_DATA]).length()) { + data[INPUT_DATA] = data[OUTPUT_DATA]; + data[OUTPUT_DATA] = KMByteBlob.instance(tmpVariables[0]); + Util.arrayCopy(KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), + KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), + KMByteBlob.cast(data[OUTPUT_DATA]).getBuffer(), + KMByteBlob.cast(data[OUTPUT_DATA]).getStartOff(), + tmpVariables[0]); } + } + //Persist if there are any updates. + op.persist(); // make response tmpVariables[1] = KMArray.instance((short) 0); tmpVariables[1] = KMKeyParameters.instance(tmpVariables[1]); @@ -2264,7 +2096,7 @@ private void processBeginOperationCmd(APDU apdu) { // Receive the incoming request fully from the master into buffer. receiveIncoming(apdu); byte[] scratchPad = apdu.getBuffer(); - short args = KMType.INVALID_VALUE; + short args; tmpVariables[1] = KMArray.instance((short) 4); // Arguments tmpVariables[2] = KMKeyParameters.exp(); @@ -2296,7 +2128,7 @@ private void processBeginOperationCmd(APDU apdu) { KMOperationState op = repository.reserveOperation(); if(op == null) KMException.throwIt(KMError.TOO_MANY_OPERATIONS); data[OP_HANDLE] = op.getHandle(); - op.setPurpose(tmpVariables[0]); + op.setPurpose((byte)tmpVariables[0]); op.setKeySize(KMByteBlob.cast(data[SECRET]).length()); authorizeAndBeginOperation(op, scratchPad); switch (op.getPurpose()){ @@ -2315,7 +2147,8 @@ private void processBeginOperationCmd(APDU apdu) { } // If the data[IV] is required to be returned. //As per VTS, for the decryption operation don't send the iv back. - if (data[IV] != KMType.INVALID_VALUE && op.getPurpose() != KMType.DECRYPT) { + if (data[IV] != KMType.INVALID_VALUE && op.getPurpose() != KMType.DECRYPT && + op.getBlockMode() != KMType.ECB) { tmpVariables[2] = KMArray.instance((short) 1); if(op.getAlgorithm() == KMType.DES && op.getBlockMode() == KMType.CBC) { //For AES/DES we are generate an random iv of length 16 bytes. @@ -2349,17 +2182,24 @@ private void beginTrustedConfirmationOperation(KMOperationState op) { KMType.BOOL_TAG, KMType.TRUSTED_CONFIRMATION_REQUIRED, data[HW_PARAMETERS]) != KMType.INVALID_VALUE) { // get operation // get the hmac key - if (repository.getComputedHmacKey() == null) { + short key = repository.getComputedHmacKey(); + if ( key == 0) { KMException.throwIt(KMError.OPERATION_CANCELLED); } - // set the Hmac signer - op.setTrustedConfirmationSigner( - cryptoProvider.createHmacSignerVerifier(Signature.MODE_VERIFY, - MessageDigest.ALG_SHA_256, - repository.getComputedHmacKey(), - (short) 0, (short) repository.getComputedHmacKey().length)); + /* + op.setTrustedConfirmationSigner(seProvider.initSymmetricOperation( + KMType.VERIFY,KMType.HMAC,KMType.SHA2_256,(byte)0,(byte)0,repository.getComputedHmacKey(), + (short) 0, (short) repository.getComputedHmacKey().length,null,(short)0,(short)0,(short)0)); + */ + op.setTrustedConfirmationSigner(seProvider.initSymmetricOperation( + KMType.VERIFY,KMType.HMAC,KMType.SHA2_256,(byte)0,(byte)0, + KMByteBlob.cast(key).getBuffer(), + KMByteBlob.cast(key).getStartOff(), + KMByteBlob.cast(key).length(),null,(short)0,(short)0,(short)0)); + op.getTrustedConfirmationSigner().update(confirmationToken,(short)0,(short)confirmationToken.length); } + } private void authorizeAlgorithm(KMOperationState op){ @@ -2419,6 +2259,7 @@ private void authorizeDigest(KMOperationState op){ break; } } + private void authorizePadding(KMOperationState op){ short paddings = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.PADDING, data[HW_PARAMETERS]); op.setPadding(KMType.PADDING_NONE); @@ -2516,24 +2357,6 @@ private void authorizeBlockModeAndMacLength(KMOperationState op){ op.setBlockMode((byte) param); } - private short getMacSize(byte digest){ - switch (digest){ - case KMType.SHA1: - return 160; - case KMType.SHA2_224: - return 224; - case KMType.SHA2_384: - return 384; - case KMType.SHA2_256: - return 256; - case KMType.SHA2_512: - return 512; - case KMType.MD5: - return 128; - default: - return 0; - } - } private void authorizeAndBeginOperation(KMOperationState op, byte[] scratchPad) { authorizeAlgorithm(op); authorizePurpose(op); @@ -2544,7 +2367,7 @@ private void authorizeAndBeginOperation(KMOperationState op, byte[] scratchPad) if(!validateHwToken(data[HW_TOKEN],scratchPad)){ data[HW_TOKEN] = KMType.INVALID_VALUE; } - authorizeUserSecureIdAuthTimeout(op, scratchPad); + authorizeUserSecureIdAuthTimeout(op); authorizeDeviceUnlock(data[HW_TOKEN]); // Authorize Caller Nonce - if caller nonce absent in key char and nonce present in // key params then fail if it is not a Decrypt operation @@ -2557,72 +2380,72 @@ private void authorizeAndBeginOperation(KMOperationState op, byte[] scratchPad) KMException.throwIt(KMError.CALLER_NONCE_PROHIBITED); } } + // If Nonce is present then check whether the size of nonce is correct. if (tmpVariables[1] != KMType.INVALID_VALUE) { data[IV] = KMByteTag.cast(tmpVariables[1]).getValue(); - //For CBC mode and GCM mode if IV is present in key parameters then it must be of right lengths + //For CBC mode - iv must be 8 bytes if (op.getBlockMode() == KMType.CBC && op.getAlgorithm() == KMType.DES && KMByteBlob.cast(data[IV]).length() != 8) { KMException.throwIt(KMError.INVALID_NONCE); } + // For GCM mode - IV must be 12 bytes if (KMByteBlob.cast(data[IV]).length() != 12 && op.getBlockMode() == KMType.GCM) { KMException.throwIt(KMError.INVALID_NONCE); } + // For AES CBC and CTR modes IV must be 16 bytes if ((op.getBlockMode() == KMType.CBC || op.getBlockMode() == KMType.CTR) && op.getAlgorithm() == KMType.AES && KMByteBlob.cast(data[IV]).length() != 16) { KMException.throwIt(KMError.INVALID_NONCE); } - } - if (op.getAlgorithm() == KMType.AES || op.getAlgorithm() == KMType.DES) { + }else if (op.getAlgorithm() == KMType.AES || op.getAlgorithm() == KMType.DES) { // For symmetric decryption iv is required if (op.getPurpose() == KMType.DECRYPT - && data[IV] == KMType.INVALID_VALUE - && (op.getBlockMode() == KMType.CBC || op.getBlockMode() == KMType.GCM)) { + && (op.getBlockMode() == KMType.CBC || op.getBlockMode() == KMType.GCM || + op.getBlockMode() == KMType.CTR)) { KMException.throwIt(KMError.MISSING_NONCE); + }else if(op.getBlockMode() == KMType.ECB){ + // For ECB we create zero length nonce + data[IV] = KMByteBlob.instance((short)0); + }else if(op.getPurpose() == KMType.ENCRYPT){ + // For encrypt mode if nonce is absent then create random nonce of correct length + byte ivLen = 16; + if(op.getBlockMode() == KMType.GCM){ + ivLen = 12; + }else if(op.getAlgorithm() == KMType.DES){ + ivLen = 8; + } + data[IV] = KMByteBlob.instance(ivLen); + seProvider.newRandomNumber( + KMByteBlob.cast(data[IV]).getBuffer(), + KMByteBlob.cast(data[IV]).getStartOff(), + KMByteBlob.cast(data[IV]).length()); + } } } - } private void beginCipherOperation(KMOperationState op) { - short padding; - short alg = -1; - short purpose; - if(op.getPurpose() == KMType.ENCRYPT) purpose = KMCipher.MODE_ENCRYPT; - else purpose = KMCipher.MODE_DECRYPT; - switch (op.getAlgorithm()) { // Not required to be supported - supported for testing purpose // TODO remove this later case KMType.RSA: - if (op.getPadding() == KMType.RSA_PKCS1_1_5_ENCRYPT) padding = KMCipher.PAD_PKCS1; - else if(op.getPadding() == KMType.RSA_OAEP && op.getDigest() == KMType.SHA2_256){ - padding = KMCipher.PAD_PKCS1_OAEP_SHA256; - } else if (op.getPadding() == KMType.RSA_OAEP && op.getDigest() == KMType.SHA1) { - padding = KMCipher.PAD_PKCS1_OAEP; - } else padding = KMCipher.PAD_NOPAD; try { - if(purpose == KMCipher.MODE_DECRYPT){ - op.setKey( + if(op.getPurpose() == KMType.DECRYPT){ + op.setOperation(seProvider.initAsymmetricOperation( + (byte)op.getPurpose(),op.getAlgorithm(), + op.getPadding(), op.getDigest(), KMByteBlob.cast(data[SECRET]).getBuffer(), KMByteBlob.cast(data[SECRET]).getStartOff(), - KMByteBlob.cast(data[SECRET]).length()); - op.setCipher( - cryptoProvider.createRsaDecipher( - 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())); + KMByteBlob.cast(data[SECRET]).length(), + KMByteBlob.cast(data[PUB_KEY]).getBuffer(), + KMByteBlob.cast(data[PUB_KEY]).getStartOff(), + KMByteBlob.cast(data[PUB_KEY]).length())); }else{ - op.setKey( - KMByteBlob.cast(data[PUB_KEY]).getBuffer(), - KMByteBlob.cast(data[PUB_KEY]).getStartOff(), - KMByteBlob.cast(data[PUB_KEY]).length()); - op.setCipher( - cryptoProvider.createRsaCipher( - padding, + op.setOperation( + seProvider.initAsymmetricOperation( + (byte)op.getPurpose(), op.getAlgorithm(), + op.getPadding(), op.getDigest(), + null, (short)0, (short)0, KMByteBlob.cast(data[PUB_KEY]).getBuffer(), KMByteBlob.cast(data[PUB_KEY]).getStartOff(), KMByteBlob.cast(data[PUB_KEY]).length())); @@ -2632,159 +2455,75 @@ else if(op.getPadding() == KMType.RSA_OAEP && op.getDigest() == KMType.SHA2_256) } break; case KMType.AES: - if (op.getBlockMode() == KMType.GCM) { - beginAesGcmOperation(op); - return; - } case KMType.DES: - if (op.getPadding() == KMType.PADDING_NONE) { - padding = KMCipher.PAD_NOPAD; - } else { - padding = KMCipher.PAD_PKCS7; - } - if (op.getAlgorithm() == KMType.AES) { - if (op.getBlockMode() == KMType.CBC) { - alg = KMCipher.ALG_AES_BLOCK_128_CBC_NOPAD; - if (data[IV] == KMType.INVALID_VALUE) { - data[IV] = KMByteBlob.instance((short) 16); - cryptoProvider.newRandomNumber( - KMByteBlob.cast(data[IV]).getBuffer(), - KMByteBlob.cast(data[IV]).getStartOff(), - KMByteBlob.cast(data[IV]).length()); - } - } else if (op.getBlockMode() == KMType.ECB) { - alg = KMCipher.ALG_AES_BLOCK_128_ECB_NOPAD; - data[IV] = KMType.INVALID_VALUE; - } else if (op.getBlockMode() == KMType.CTR){ - alg = KMCipher.ALG_AES_CTR; - if (data[IV] == KMType.INVALID_VALUE) { - data[IV] = KMByteBlob.instance((short) 16); - cryptoProvider.newRandomNumber( - KMByteBlob.cast(data[IV]).getBuffer(), - KMByteBlob.cast(data[IV]).getStartOff(), - KMByteBlob.cast(data[IV]).length()); - } - }else{ - KMException.throwIt(KMError.UNSUPPORTED_BLOCK_MODE); - } - } else if (op.getAlgorithm() == KMType.DES) { - if (op.getBlockMode() == KMType.CBC) { - alg = KMCipher.ALG_DES_CBC_NOPAD; - if(data[IV] == KMType.INVALID_VALUE){ - data[IV] = KMByteBlob.instance((short)16); // TODO 8 bytes in length - cryptoProvider.newRandomNumber( - KMByteBlob.cast(data[IV]).getBuffer(), - KMByteBlob.cast(data[IV]).getStartOff(), - KMByteBlob.cast(data[IV]).length() - ); - } - - } else if (op.getBlockMode() == KMType.ECB){ - alg = KMCipher.ALG_DES_ECB_NOPAD; - data[IV] = KMType.INVALID_VALUE; - }else{ - KMException.throwIt(KMError.UNSUPPORTED_BLOCK_MODE); - } - } else { - KMException.throwIt(KMError.INCOMPATIBLE_ALGORITHM); + if(op.getBlockMode() == KMType.GCM){ + op.setAesGcmUpdateStart(); } - op.setKey( - KMByteBlob.cast(data[SECRET]).getBuffer(), - KMByteBlob.cast(data[SECRET]).getStartOff(), - KMByteBlob.cast(data[SECRET]).length()); try { - if (data[IV] != KMType.INVALID_VALUE) { - op.setCipher( - cryptoProvider.createSymmetricCipher( - alg, - purpose, - padding, - 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())); - } else { - op.setCipher( - cryptoProvider.createSymmetricCipher( - alg, - purpose, - padding, - KMByteBlob.cast(data[SECRET]).getBuffer(), - KMByteBlob.cast(data[SECRET]).getStartOff(), - KMByteBlob.cast(data[SECRET]).length())); - } - } catch (CryptoException exp) { - KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM); + // if (data[IV] != KMType.INVALID_VALUE) { + op.setOperation(seProvider.initSymmetricOperation( + (byte)op.getPurpose(),op.getAlgorithm(), + op.getDigest(),op.getPadding(),op.getBlockMode(), + 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(), + op.getMacLength())); + } catch (CryptoException exception) { + if(exception.getReason() == CryptoException.ILLEGAL_VALUE) + KMException.throwIt(KMError.INVALID_ARGUMENT); + else if(exception.getReason() == CryptoException.NO_SUCH_ALGORITHM) + KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM); } } } - private void beginSignVerifyOperation(KMOperationState op) { - short padding; - short digest; - short purpose;; - if(op.getPurpose() == KMType.SIGN) purpose = Signature.MODE_SIGN; - else purpose = Signature.MODE_VERIFY; switch(op.getAlgorithm()){ case KMType.RSA: - if(op.getDigest() == KMType.DIGEST_NONE) digest = MessageDigest.ALG_NULL; - else digest = MessageDigest.ALG_SHA_256; - if(op.getPadding() == KMType.PADDING_NONE) padding = KMCipher.PAD_NOPAD; - else if(op.getPadding() == KMType.RSA_PSS) padding = KMCipher.PAD_PKCS1_PSS; - else padding = KMCipher.PAD_PKCS1; - op.setKey(KMByteBlob.cast(data[SECRET]).getBuffer(), - KMByteBlob.cast(data[SECRET]).getStartOff(), - KMByteBlob.cast(data[SECRET]).length()); try{ if (op.getPurpose() == KMType.SIGN) { - op.setSignerVerifier( - cryptoProvider.createRsaSigner( - digest, - 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())); + op.setOperation(seProvider.initAsymmetricOperation( + (byte)op.getPurpose(),op.getAlgorithm(),op.getPadding(),op.getDigest(), + 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() + )); }else{ - op.setSignerVerifier( - cryptoProvider.createRsaVerifier( - digest, - padding, - KMByteBlob.cast(data[PUB_KEY]).getBuffer(), - KMByteBlob.cast(data[PUB_KEY]).getStartOff(), - KMByteBlob.cast(data[PUB_KEY]).length())); + op.setOperation(seProvider.initAsymmetricOperation( + (byte)op.getPurpose(),op.getAlgorithm(),op.getPadding(),op.getDigest(), + null,(short)0,(short) 0, + KMByteBlob.cast(data[PUB_KEY]).getBuffer(), + KMByteBlob.cast(data[PUB_KEY]).getStartOff(), + KMByteBlob.cast(data[PUB_KEY]).length() + )); } }catch(CryptoException exp){ + //TODO remove this // Javacard does not support NO digest based signing. KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM); } break; case KMType.EC: - if(op.getDigest() == KMType.DIGEST_NONE) digest = MessageDigest.ALG_NULL; - else digest = MessageDigest.ALG_SHA_256; - op.setKey(KMByteBlob.cast(data[SECRET]).getBuffer(), - KMByteBlob.cast(data[SECRET]).getStartOff(), - KMByteBlob.cast(data[SECRET]).length()); try{ if (op.getPurpose() == KMType.SIGN) { - op.setSignerVerifier( - cryptoProvider.createEcSigner( - digest, - KMByteBlob.cast(data[SECRET]).getBuffer(), - KMByteBlob.cast(data[SECRET]).getStartOff(), - KMByteBlob.cast(data[SECRET]).length())); + op.setOperation(seProvider.initAsymmetricOperation( + (byte)op.getPurpose(),op.getAlgorithm(),op.getPadding(),op.getDigest(), + KMByteBlob.cast(data[SECRET]).getBuffer(), + KMByteBlob.cast(data[SECRET]).getStartOff(), + KMByteBlob.cast(data[SECRET]).length(),null,(short)0,(short)0)); }else{ - op.setSignerVerifier( - cryptoProvider.createEcVerifier( - digest, - KMByteBlob.cast(data[PUB_KEY]).getBuffer(), - KMByteBlob.cast(data[PUB_KEY]).getStartOff(), - KMByteBlob.cast(data[PUB_KEY]).length())); + op.setOperation(seProvider.initAsymmetricOperation( + (byte)op.getPurpose(),op.getAlgorithm(),op.getPadding(),op.getDigest(), + null,(short)0,(short)0, + KMByteBlob.cast(data[PUB_KEY]).getBuffer(), + KMByteBlob.cast(data[PUB_KEY]).getStartOff(), + KMByteBlob.cast(data[PUB_KEY]).length())); } }catch(CryptoException exp){ // Javacard does not support NO digest based signing. @@ -2797,18 +2536,14 @@ private void beginSignVerifyOperation(KMOperationState op) { //length or less than that in case if it is less than 32 we are truncating it and sending //back to the user. For Verify user will send the truncated and if we pass the truncated //signature to Javacard verify API it will fail because it expects the full length signature. - digest = MessageDigest.ALG_SHA_256; - op.setKey(KMByteBlob.cast(data[SECRET]).getBuffer(), - KMByteBlob.cast(data[SECRET]).getStartOff(), - KMByteBlob.cast(data[SECRET]).length()); try{ - op.setSignerVerifier( - cryptoProvider.createHmacSignerVerifier( - Signature.MODE_SIGN, digest, - KMByteBlob.cast(data[SECRET]).getBuffer(), - KMByteBlob.cast(data[SECRET]).getStartOff(), - KMByteBlob.cast(data[SECRET]).length())); + op.setOperation(seProvider.initSymmetricOperation( + (byte)op.getPurpose(),op.getAlgorithm(),op.getDigest(),op.getPadding(),op.getBlockMode(), + KMByteBlob.cast(data[SECRET]).getBuffer(), + KMByteBlob.cast(data[SECRET]).getStartOff(), + KMByteBlob.cast(data[SECRET]).length(),null,(short)0,(short)0,(short)0)); }catch(CryptoException exp){ + //TODO remove the following // Javacard does not support NO digest based signing. KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM); } @@ -2819,7 +2554,7 @@ private void beginSignVerifyOperation(KMOperationState op) { } } - private void authorizeUserSecureIdAuthTimeout(KMOperationState op, byte[] scratchPad) { + private void authorizeUserSecureIdAuthTimeout(KMOperationState op) { short authTime; // Authorize User Secure Id and Auth timeout tmpVariables[0] = @@ -2832,7 +2567,7 @@ private void authorizeUserSecureIdAuthTimeout(KMOperationState op, byte[] scratc if(data[HW_TOKEN] == KMType.INVALID_VALUE) KMException.throwIt(KMError.INVALID_MAC_LENGTH); authTime = KMIntegerTag.cast(tmpVariables[0]).getValue(); // authenticate user - authenticateUser(scratchPad); + authenticateUser(); // set the one time auth op.setOneTimeAuthReqd(true); // set the authentication time stamp in operation state @@ -2849,7 +2584,7 @@ private void authorizeUserSecureIdAuthTimeout(KMOperationState op, byte[] scratc } - private void authenticateUser(byte[] scratchPad) { + private void authenticateUser() { tmpVariables[0] = KMHardwareAuthToken.cast(data[HW_TOKEN]).getUserId(); if (KMInteger.cast(tmpVariables[0]).isZero()) { tmpVariables[0] = KMHardwareAuthToken.cast(data[HW_TOKEN]).getAuthenticatorId(); @@ -2873,7 +2608,7 @@ private void authenticateUser(byte[] scratchPad) { private boolean validateHwToken(short hwToken, byte[] scratchPad) { // CBOR Encoding is always big endian short ptr = KMHardwareAuthToken.cast(hwToken).getMac(); - short len = 0; + short len; // If mac length is zero then token is empty. if (KMByteBlob.cast(ptr).length() == 0) { return false; @@ -2906,36 +2641,25 @@ private boolean validateHwToken(short hwToken, byte[] scratchPad) { .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, + short key = repository.getComputedHmacKey(); + /*return seProvider.hmacVerify( + repository.getComputedHmacKey(), + (short) 0, + (short) repository.getComputedHmacKey().length, + scratchPad, (short) 0, len, KMByteBlob.cast(ptr).getBuffer(), KMByteBlob.cast(ptr).getStartOff(), KMByteBlob.cast(ptr).length()); - return verified; -/* - 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); - } */ + return seProvider.hmacVerify( + KMByteBlob.cast(key).getBuffer(), + KMByteBlob.cast(key).getStartOff(), + KMByteBlob.cast(key).length(), + scratchPad, (short) 0, len, + KMByteBlob.cast(ptr).getBuffer(), + KMByteBlob.cast(ptr).getStartOff(), + KMByteBlob.cast(ptr).length()); } private void authorizeKeyUsageForCount() { @@ -2957,9 +2681,6 @@ private void authorizeKeyUsageForCount() { } private void processImportKeyCmd(APDU apdu) { - if (repository.keyBlobCount > repository.MAX_BLOB_STORAGE) { - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } // Receive the incoming request fully from the master into buffer. receiveIncoming(apdu); byte[] scratchPad = apdu.getBuffer(); @@ -2990,42 +2711,18 @@ private void importKey(APDU apdu, byte[] scratchPad) { if (tmpVariables[0] != KMType.INVALID_VALUE) { KMException.throwIt(KMError.INVALID_KEY_BLOB); } + // Rollback protection not supported + tmpVariables[0] = + KMKeyParameters.findTag(KMType.BOOL_TAG, KMType.ROLLBACK_RESISTANCE, data[KEY_PARAMETERS]); + if(tmpVariables[0] != KMType.INVALID_VALUE) { + KMException.throwIt(KMError.ROLLBACK_RESISTANCE_UNAVAILABLE); + } // get algorithm tmpVariables[3] = KMEnumTag.getValue(KMType.ALGORITHM, data[KEY_PARAMETERS]); if (tmpVariables[3] == KMType.INVALID_VALUE) { KMException.throwIt(KMError.INVALID_ARGUMENT); } - /*tmpVariables[4] = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.DIGEST,data[KEY_PARAMETERS]); - if(tmpVariables[4] != KMType.INVALID_VALUE){ - if(!KMEnumArrayTag.cast(tmpVariables[4]).isValidDigests((byte)tmpVariables[3])){ - KMException.throwIt(KMError.UNSUPPORTED_DIGEST); - } - } - tmpVariables[4] = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.PADDING,data[KEY_PARAMETERS]); - if(tmpVariables[4] != KMType.INVALID_VALUE){ - if(!KMEnumArrayTag.cast(tmpVariables[4]).isValidPaddingModes((byte)tmpVariables[3])){ - KMException.throwIt(KMError.INCOMPATIBLE_PADDING_MODE); - } - } - tmpVariables[4] = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.PURPOSE,data[KEY_PARAMETERS]); - if(tmpVariables[4] != KMType.INVALID_VALUE){ - if(!KMEnumArrayTag.cast(tmpVariables[4]).isValidPurpose((byte)tmpVariables[3])){ - KMException.throwIt(KMError.INCOMPATIBLE_PURPOSE); - } - } - tmpVariables[4] = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.BLOCK_MODE,data[KEY_PARAMETERS]); - if(tmpVariables[4] != KMType.INVALID_VALUE){ - if(!KMEnumArrayTag.cast(tmpVariables[4]).isValidBlockMode((byte)tmpVariables[3])){ - KMException.throwIt(KMError.INCOMPATIBLE_PURPOSE); - } - } - tmpVariables[4] = KMKeyParameters.findTag(KMType.UINT_TAG, KMType.KEYSIZE,data[KEY_PARAMETERS]); - if(tmpVariables[4] != KMType.INVALID_VALUE){ - if(!KMIntegerTag.cast(tmpVariables[4]).isValidKeySize((byte)tmpVariables[3])){ - KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); - } - }*/ // Check algorithm and dispatch to appropriate handler. switch (tmpVariables[3]) { case KMType.RSA: @@ -3049,8 +2746,16 @@ private void importKey(APDU apdu, byte[] scratchPad) { } // create key blob createEncryptedKeyBlob(scratchPad); - // persist auth tag for rollback resistance. - repository.persistAuthTag(data[AUTH_TAG]); + tmpVariables[0] = KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.MAX_USES_PER_BOOT, data[KEY_PARAMETERS]); + if (tmpVariables[0] != KMType.INVALID_VALUE){ + // before generating key, check whether max count reached + if (repository.getKeyBlobCount() > KMRepository.MAX_BLOB_STORAGE) { + //TODO which error to return? + KMException.throwIt(KMError.UNKNOWN_ERROR); + } + 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)); @@ -3075,11 +2780,6 @@ private void importECKeys(byte[] scratchPad) { data[SECRET] = KMArray.cast(tmpVariables[0]).get((short) 0); data[PUB_KEY] = KMArray.cast(tmpVariables[0]).get((short) 1); // initialize 256 bit p256 key for given private key and public key. - ECPrivateKey ecKey = - cryptoProvider.createEcKey( - KMByteBlob.cast(data[SECRET]).getBuffer(), - KMByteBlob.cast(data[SECRET]).getStartOff(), - KMByteBlob.cast(data[SECRET]).length()); tmpVariables[4] = 0; // index for update list in scratchPad // check whether the keysize tag is present in key parameters. @@ -3122,10 +2822,19 @@ private void importECKeys(byte[] scratchPad) { Util.setShort(scratchPad, tmpVariables[4], tmpVariables[5]); tmpVariables[4] += 2; } + //Check whether key can be created + seProvider.importAsymmetricKey(KMType.EC, + 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()); + // add scratch pad to key parameters updateKeyParameters(scratchPad, tmpVariables[4]); // validate updated key parameters. - validateECKeys(scratchPad); + validateECKeys(); data[KEY_BLOB] = KMArray.instance((short) 5); KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_PUB_KEY, data[PUB_KEY]); } @@ -3142,11 +2851,7 @@ private void importHmacKey(byte[] scratchPad) { KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).length()); data[SECRET] = KMArray.cast(tmpVariables[0]).get((short) 0); // create HMAC key of up to 512 bit - HMACKey hmacKey = - cryptoProvider.createHMACKey( - KMByteBlob.cast(data[SECRET]).getBuffer(), - KMByteBlob.cast(data[SECRET]).getStartOff(), - KMByteBlob.cast(data[SECRET]).length()); + tmpVariables[4] = 0; // index in scratchPad for update params // check the keysize tag if present in key parameters. tmpVariables[2] = @@ -3162,10 +2867,15 @@ private void importHmacKey(byte[] scratchPad) { Util.setShort(scratchPad, tmpVariables[4], tmpVariables[6]); tmpVariables[4] += 2; } + //Check whether key can be created + seProvider.importSymmetricKey(KMType.HMAC,tmpVariables[2],KMByteBlob.cast(data[SECRET]).getBuffer(), + KMByteBlob.cast(data[SECRET]).getStartOff(), + KMByteBlob.cast(data[SECRET]).length()); + // update the key parameters list updateKeyParameters(scratchPad, tmpVariables[4]); // validate HMAC Key parameters - validateHmacKey(scratchPad); + validateHmacKey(); data[KEY_BLOB] = KMArray.instance((short) 4); } @@ -3181,11 +2891,6 @@ private void importTDESKey(byte[] scratchPad) { KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).getStartOff(), KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).length()); data[SECRET] = KMArray.cast(tmpVariables[0]).get((short) 0); - DESKey desKey = - cryptoProvider.createTDESKey( - KMByteBlob.cast(data[SECRET]).getBuffer(), - KMByteBlob.cast(data[SECRET]).getStartOff(), - KMByteBlob.cast(data[SECRET]).length()); tmpVariables[4] = 0; // index in scratchPad for update params // check the keysize tag if present in key parameters. tmpVariables[2] = @@ -3201,10 +2906,15 @@ private void importTDESKey(byte[] scratchPad) { Util.setShort(scratchPad, tmpVariables[4], tmpVariables[6]); tmpVariables[4] += 2; } + //Check whether key can be created + seProvider.importSymmetricKey(KMType.DES,tmpVariables[2],KMByteBlob.cast(data[SECRET]).getBuffer(), + KMByteBlob.cast(data[SECRET]).getStartOff(), + KMByteBlob.cast(data[SECRET]).length()); + // update the key parameters list updateKeyParameters(scratchPad, tmpVariables[4]); // validate TDES Key parameters - validateTDESKey(scratchPad); + validateTDESKey(); data[KEY_BLOB] = KMArray.instance((short) 4); } @@ -3221,11 +2931,6 @@ private void importAESKey(byte[] scratchPad) { KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).length()); data[SECRET] = KMArray.cast(tmpVariables[0]).get((short) 0); // create 128 or 256 bit AES key - AESKey aesKey = - cryptoProvider.createAESKey( - KMByteBlob.cast(data[SECRET]).getBuffer(), - KMByteBlob.cast(data[SECRET]).getStartOff(), - KMByteBlob.cast(data[SECRET]).length()); tmpVariables[4] = 0; // index in scratchPad for update params // check the keysize tag if present in key parameters. tmpVariables[2] = @@ -3242,10 +2947,15 @@ private void importAESKey(byte[] scratchPad) { Util.setShort(scratchPad, tmpVariables[4], tmpVariables[6]); tmpVariables[4] += 2; } + //Check whether key can be created + seProvider.importSymmetricKey(KMType.AES,tmpVariables[2],KMByteBlob.cast(data[SECRET]).getBuffer(), + KMByteBlob.cast(data[SECRET]).getStartOff(), + KMByteBlob.cast(data[SECRET]).length()); + // update the key parameters list updateKeyParameters(scratchPad, tmpVariables[4]); // validate AES Key parameters - validateAESKey(scratchPad); + validateAESKey(); data[KEY_BLOB] = KMArray.instance((short) 4); } @@ -3288,15 +2998,6 @@ private void importRSAKey(byte[] scratchPad) { tmpVariables[4] += 2; } - // initialize 2048 bit private key for given private exp and modulus. - RSAPrivateKey rsaKey = - cryptoProvider.createRsaKey( - KMByteBlob.cast(data[PUB_KEY]).getBuffer(), - KMByteBlob.cast(data[PUB_KEY]).getStartOff(), - KMByteBlob.cast(data[PUB_KEY]).length(), - KMByteBlob.cast(data[SECRET]).getBuffer(), - KMByteBlob.cast(data[SECRET]).getStartOff(), - KMByteBlob.cast(data[SECRET]).length()); // check the keysize tag if present in key parameters. tmpVariables[2] = KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); @@ -3311,6 +3012,16 @@ private void importRSAKey(byte[] scratchPad) { Util.setShort(scratchPad, tmpVariables[4], tmpVariables[6]); tmpVariables[4] += 2; } + + //Check whether key can be created + seProvider.importAsymmetricKey(KMType.RSA, + 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()); + // update the key parameters list updateKeyParameters(scratchPad, tmpVariables[4]); // validate RSA Key parameters @@ -3351,8 +3062,6 @@ private void updateKeyParameters(byte[] ptrArr, short len) { // TODO Add Signature verification. private void processSetBootParamsCmd(APDU apdu) { receiveIncoming(apdu); - // Re-purpose the apdu buffer as scratch pad. - byte[] scratchPad = apdu.getBuffer(); // Argument 1 OS Version // short osVersionExp = KMIntegerTag.exp(KMType.UINT_TAG); tmpVariables[0] = KMInteger.exp(); @@ -3394,26 +3103,47 @@ private void processSetBootParamsCmd(APDU apdu) { tmpVariables[4] = KMArray.cast(args).get((short) 4); // short deviceLockedPtr = KMArray.cast(args).get((short) 5); tmpVariables[5] = KMArray.cast(args).get((short) 5); - if (KMByteBlob.cast(tmpVariables[2]).length() > repository.BOOT_KEY_MAX_SIZE) { + if (KMByteBlob.cast(tmpVariables[2]).length() > KMRepository.BOOT_KEY_MAX_SIZE) { KMException.throwIt(KMError.INVALID_ARGUMENT); } - if (KMByteBlob.cast(tmpVariables[3]).length() > repository.BOOT_HASH_MAX_SIZE) { + if (KMByteBlob.cast(tmpVariables[3]).length() > KMRepository.BOOT_HASH_MAX_SIZE) { KMException.throwIt(KMError.INVALID_ARGUMENT); } // Begin transaction - JCSystem.beginTransaction(); - KMInteger.cast(tmpVariables[0]).value(repository.osVersion, (short) 0); - KMInteger.cast(tmpVariables[1]).value(repository.osPatch, (short) 0); + // JCSystem.beginTransaction(); + repository.setOsVersion(KMInteger.cast(tmpVariables[0]).getBuffer(), + KMInteger.cast(tmpVariables[0]).getStartOff(), + KMInteger.cast(tmpVariables[0]).length()); + repository.setOsPatch(KMInteger.cast(tmpVariables[1]).getBuffer(), + KMInteger.cast(tmpVariables[1]).getStartOff(), + KMInteger.cast(tmpVariables[1]).length()); + //KMInteger.cast(tmpVariables[0]).value(repository.osVersion, (short) 0); + //KMInteger.cast(tmpVariables[1]).value(repository.osPatch, (short) 0); + //KMInteger.cast(valPtr).getValue(repository.osVersion, (short) 0, (short) 4); //valPtr = KMIntegerTag.cast(tmpVariables[1]).getValue(); //KMInteger.cast(valPtr).getValue(repository.osPatch, (short) 0, (short) 4); - repository.actualBootKeyLength = KMByteBlob.cast(tmpVariables[2]).length(); - KMByteBlob.cast(tmpVariables[2]) - .getValue(repository.verifiedBootKey, (short) 0, repository.actualBootKeyLength); - repository.actualBootHashLength = KMByteBlob.cast(tmpVariables[3]).length(); - KMByteBlob.cast(tmpVariables[3]) - .getValue(repository.verifiedBootHash, (short) 0, repository.actualBootHashLength); + + //repository.actualBootKeyLength = KMByteBlob.cast(tmpVariables[2]).length(); + //KMByteBlob.cast(tmpVariables[2]) + // .getValue(repository.verifiedBootKey, (short) 0, repository.actualBootKeyLength); + repository.setVerifiedBootKey( + KMByteBlob.cast(tmpVariables[2]).getBuffer(), + KMByteBlob.cast(tmpVariables[2]).getStartOff(), + KMByteBlob.cast(tmpVariables[2]).length()); + + //repository.actualBootHashLength = KMByteBlob.cast(tmpVariables[3]).length(); + //KMByteBlob.cast(tmpVariables[3]) + // .getValue(repository.verifiedBootHash, (short) 0, repository.actualBootHashLength); + repository.setVerifiedBootHash( + KMByteBlob.cast(tmpVariables[3]).getBuffer(), + KMByteBlob.cast(tmpVariables[3]).getStartOff(), + KMByteBlob.cast(tmpVariables[3]).length() + ); + byte enumVal = KMEnum.cast(tmpVariables[4]).getVal(); + repository.setBootState(enumVal); + /* if (enumVal == KMTag.SELF_SIGNED_BOOT) { repository.selfSignedBootFlag = true; repository.verifiedBootFlag = false; @@ -3424,12 +3154,11 @@ private void processSetBootParamsCmd(APDU apdu) { repository.selfSignedBootFlag = false; repository.verifiedBootFlag = false; } + + */ enumVal = KMEnum.cast(tmpVariables[5]).getVal(); - if (enumVal == KMType.DEVICE_LOCKED_TRUE) { - repository.deviceLockedFlag = true; - } else { - repository.deviceLockedFlag = false; - } + //repository.deviceLockedFlag = (enumVal == KMType.DEVICE_LOCKED_TRUE); + repository.setDeviceLock(enumVal == KMType.DEVICE_LOCKED_TRUE); if (keymasterState == KMKeymasterApplet.FIRST_SELECT_STATE) { setBootParamsDone = true; if (provisionDone) { @@ -3437,14 +3166,10 @@ private void processSetBootParamsCmd(APDU apdu) { } } // end transaction - JCSystem.commitTransaction(); + //JCSystem.commitTransaction(); } private static void processGenerateKey(APDU apdu) { - // before generating key, check whether max count reached - if (repository.keyBlobCount > repository.MAX_BLOB_STORAGE) { - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } // Receive the incoming request fully from the master into buffer. receiveIncoming(apdu); // Re-purpose the apdu buffer as scratch pad. @@ -3457,6 +3182,18 @@ private static void processGenerateKey(APDU apdu) { // Decode the argument tmpVariables[2] = decoder.decode(tmpVariables[1], buffer, bufferStartOffset, bufferLength); data[KEY_PARAMETERS] = KMArray.cast(tmpVariables[2]).get((short) 0); + //Check if EarlyBootEnded tag is present. + tmpVariables[0] = + KMKeyParameters.findTag(KMType.BOOL_TAG, KMType.EARLY_BOOT_ENDED, data[KEY_PARAMETERS]); + if(tmpVariables[0] != KMType.INVALID_VALUE) { + KMException.throwIt(KMError.EARLY_BOOT_ENDED); + } + //Check if rollback resistance tag is present + tmpVariables[0] = + KMKeyParameters.findTag(KMType.BOOL_TAG, KMType.ROLLBACK_RESISTANCE, data[KEY_PARAMETERS]); + if(tmpVariables[0] != KMType.INVALID_VALUE) { + KMException.throwIt(KMError.ROLLBACK_RESISTANCE_UNAVAILABLE); + } // Bootloader only not supported tmpVariables[0] = KMKeyParameters.findTag(KMType.BOOL_TAG, KMType.BOOTLOADER_ONLY, data[KEY_PARAMETERS]); @@ -3468,38 +3205,6 @@ private static void processGenerateKey(APDU apdu) { if (tmpVariables[3] == KMType.INVALID_VALUE) { KMException.throwIt(KMError.INVALID_ARGUMENT); } - /* TODO As per VTS don't do validations for digest, padding, purpose, mode at the time of generation - // validate digest - only digest none or 256 is supported. - /*tmpVariables[4] = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.DIGEST,data[KEY_PARAMETERS]); - if(tmpVariables[4] != KMType.INVALID_VALUE){ - if(!KMEnumArrayTag.cast(tmpVariables[4]).isValidDigests((byte)tmpVariables[3])){ - KMException.throwIt(KMError.UNSUPPORTED_DIGEST); - } - } - tmpVariables[4] = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.PADDING,data[KEY_PARAMETERS]); - if(tmpVariables[4] != KMType.INVALID_VALUE){ - if(!KMEnumArrayTag.cast(tmpVariables[4]).isValidPaddingModes((byte)tmpVariables[3])){ - KMException.throwIt(KMError.UNSUPPORTED_PADDING_MODE); - } - } - tmpVariables[4] = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.PURPOSE,data[KEY_PARAMETERS]); - if(tmpVariables[4] != KMType.INVALID_VALUE){ - if(!KMEnumArrayTag.cast(tmpVariables[4]).isValidPurpose((byte)tmpVariables[3])){ - KMException.throwIt(KMError.INCOMPATIBLE_PURPOSE); - } - } - tmpVariables[4] = KMKeyParameters.findTag(KMType.UINT_TAG, KMType.KEYSIZE,data[KEY_PARAMETERS]); - if(tmpVariables[4] != KMType.INVALID_VALUE){ - if(!KMIntegerTag.cast(tmpVariables[4]).isValidKeySize((byte)tmpVariables[3])){ - KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); - } - } - tmpVariables[4] = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.BLOCK_MODE,data[KEY_PARAMETERS]); - if(tmpVariables[4] != KMType.INVALID_VALUE){ - if(!KMEnumArrayTag.cast(tmpVariables[4]).isValidBlockMode((byte)tmpVariables[3])){ - KMException.throwIt(KMError.INCOMPATIBLE_PURPOSE); - } - }*/ tmpVariables[4] = KMKeyParameters.findTag(KMType.UINT_TAG, KMType.KEYSIZE,data[KEY_PARAMETERS]); if(tmpVariables[4] != KMType.INVALID_VALUE){ if(!KMIntegerTag.cast(tmpVariables[4]).isValidKeySize((byte)tmpVariables[3])){ @@ -3530,8 +3235,17 @@ private static void processGenerateKey(APDU apdu) { // create key blob data[ORIGIN] = KMType.GENERATED; createEncryptedKeyBlob(scratchPad); - // persist auth tag for rollback resistance. - repository.persistAuthTag(data[AUTH_TAG]); + + tmpVariables[0] = KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.MAX_USES_PER_BOOT, data[KEY_PARAMETERS]); + if (tmpVariables[0] != KMType.INVALID_VALUE){ + // before generating key, check whether max count reached + if (repository.getKeyBlobCount() > KMRepository.MAX_BLOB_STORAGE) { + //TODO which error to return? + KMException.throwIt(KMError.UNKNOWN_ERROR); + } + 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)); @@ -3576,20 +3290,25 @@ private static void generateRSAKey(byte[] scratchPad) { // Validate RSA Key validateRSAKey(scratchPad); // Now generate 2048 bit RSA keypair for the given exponent - KeyPair rsaKey = cryptoProvider.createRsaKeyPair(); - // store the pub exponent - data[RSA_PUB_EXPONENT] = KMByteBlob.instance(scratchPad, (short) 0, tmpVariables[1]); - // extract modulus - tmpVariables[0] = ((RSAPrivateKey) rsaKey.getPrivate()).getModulus(scratchPad, (short) 0); - data[PUB_KEY] = KMByteBlob.instance(scratchPad, (short) 0, tmpVariables[0]); - // extract private key - tmpVariables[0] = ((RSAPrivateKey) rsaKey.getPrivate()).getExponent(scratchPad, (short) 0); - data[SECRET] = KMByteBlob.instance(scratchPad, (short) 0, tmpVariables[0]); + //KeyPair rsaKey = seProvider.createRsaKeyPair(); + short[] lengths = tmpVariables; + data[PUB_KEY] = KMByteBlob.instance((short)256); + data[SECRET] = KMByteBlob.instance((short)256); + seProvider.createAsymmetricKey(KMType.RSA, + 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(), + lengths + ); + 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() { // Read key size tmpVariables[0] = KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); @@ -3621,27 +3340,22 @@ private static void validateAESKey(byte[] scratchPad) { (KMInteger.cast(tmpVariables[3]).getShort() % 8) != 0){ KMException.throwIt(KMError.UNSUPPORTED_MIN_MAC_LENGTH); } - }else{ - //TODO as per vts TAG_MIN_MAC_LENGTH if passed can be ignored at creation time. - // No GCM mode then no minimum mac length must be specified - //if (tmpVariables[2] != KMTag.INVALID_VALUE) { - // KMException.throwIt(KMError.INVALID_ARGUMENT); - //} } } } private static void generateAESKey(byte[] scratchPad) { - validateAESKey(scratchPad); + validateAESKey(); 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]); +// AESKey aesKey = seProvider.createAESKey(tmpVariables[0]); +// tmpVariables[0] = aesKey.getKey(scratchPad, (short) 0); + tmpVariables[0] = seProvider.createSymmetricKey(KMType.AES,tmpVariables[0],scratchPad,(short)0); + data[SECRET] = KMByteBlob.instance(scratchPad, (short) 0, tmpVariables[0]); data[KEY_BLOB] = KMArray.instance((short) 4); } - private static void validateECKeys(byte[] scratchPad) { + private static void validateECKeys() { // Read key size tmpVariables[0] = KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); @@ -3656,27 +3370,23 @@ private static void validateECKeys(byte[] scratchPad) { } private static void generateECKeys(byte[] scratchPad) { - validateECKeys(scratchPad); - KeyPair ecKey = cryptoProvider.createECKeyPair(); - tmpVariables[5] = ((ECPublicKey) ecKey.getPublic()).getW(scratchPad, (short) 0); - data[PUB_KEY] = KMByteBlob.instance(scratchPad, (short) 0, tmpVariables[5]); - tmpVariables[5] = ((ECPrivateKey) ecKey.getPrivate()).getS(scratchPad, (short) 0); - data[SECRET] = KMByteBlob.instance(scratchPad, (short) 0, tmpVariables[5]); + validateECKeys(); +// KeyPair ecKey = seProvider.createECKeyPair(); + short[] lengths = tmpVariables; + seProvider.createAsymmetricKey(KMType.EC, + scratchPad,(short)0,(short)128, + scratchPad,(short)128,(short)128, + lengths + ); + //tmpVariables[5] = ((ECPublicKey) ecKey.getPublic()).getW(scratchPad, (short) 0); + data[PUB_KEY] = KMByteBlob.instance(scratchPad, (short) 128, lengths[1]); + //tmpVariables[5] = ((ECPrivateKey) ecKey.getPrivate()).getS(scratchPad, (short) 0); + data[SECRET] = KMByteBlob.instance(scratchPad, (short) 0, lengths[0]); data[KEY_BLOB] = KMArray.instance((short) 5); KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_PUB_KEY, data[PUB_KEY]); -/* print("pubkey: ", - KMByteBlob.cast(data[PUB_KEY]).getBuffer(), - KMByteBlob.cast(data[PUB_KEY]).getStartOff(), - KMByteBlob.cast(data[PUB_KEY]).length()); - print("Secret: ", - KMByteBlob.cast(data[SECRET]).getBuffer(), - KMByteBlob.cast(data[SECRET]).getStartOff(), - KMByteBlob.cast(data[SECRET]).length()); - - */ } - private static void validateTDESKey(byte[] scratchPad) { + private static void validateTDESKey() { // Read Minimum Mac length - it must not be present tmpVariables[0] = KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, data[KEY_PARAMETERS]); @@ -3695,14 +3405,15 @@ private static void validateTDESKey(byte[] scratchPad) { } private static void generateTDESKey(byte[] scratchPad) { - validateTDESKey(scratchPad); - DESKey desKey = cryptoProvider.createTDESKey(); - tmpVariables[0] = desKey.getKey(scratchPad, (short) 0); + validateTDESKey(); + //DESKey desKey = seProvider.createTDESKey(); + //tmpVariables[0] = desKey.getKey(scratchPad, (short) 0); + tmpVariables[0] = seProvider.createSymmetricKey(KMType.DES,(short)168,scratchPad,(short)0); data[SECRET] = KMByteBlob.instance(scratchPad, (short) 0, tmpVariables[0]); data[KEY_BLOB] = KMArray.instance((short) 4); } - private static void validateHmacKey(byte[] scratchPad) { + private static void validateHmacKey() { //If params does not contain any digest throw unsupported digest error. if(KMType.INVALID_VALUE == KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.DIGEST, data[KEY_PARAMETERS])) { KMException.throwIt(KMError.UNSUPPORTED_DIGEST); @@ -3737,12 +3448,13 @@ private static void validateHmacKey(byte[] scratchPad) { } private static void generateHmacKey(byte[] scratchPad) { - validateHmacKey(scratchPad); - tmpVariables[1] = + validateHmacKey(); + tmpVariables[0] = 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); + //HMACKey hmacKey = seProvider.createHMACKey(tmpVariables[1]); + //tmpVariables[0] = hmacKey.getKey(scratchPad, (short) 0); + tmpVariables[0] = seProvider.createSymmetricKey(KMType.HMAC,tmpVariables[0],scratchPad,(short)0); data[SECRET] = KMByteBlob.instance(scratchPad, (short) 0, tmpVariables[0]); data[KEY_BLOB] = KMArray.instance((short) 4); } @@ -3751,6 +3463,20 @@ private void checkVersionAndPatchLevel(byte[] scratchPad) { tmpVariables[0] = KMIntegerTag.getValue( scratchPad, (short) 0, KMType.UINT_TAG, KMType.OS_VERSION, data[HW_PARAMETERS]); + if (tmpVariables[0] != KMType.INVALID_VALUE){ + tmpVariables[1] = repository.getOsVersion(); + tmpVariables[1] = Util.arrayCompare( + KMInteger.cast(tmpVariables[1]).getBuffer(), + KMInteger.cast(tmpVariables[1]).getStartOff(), + scratchPad, (short) 0, tmpVariables[0]); + if(tmpVariables[1] == -1){ + // If the key characteristics has os version > current os version + KMException.throwIt(KMError.INVALID_KEY_BLOB); + }else if(tmpVariables[1] == 1){ + KMException.throwIt(KMError.KEY_REQUIRES_UPGRADE); + } + } + /* if ((tmpVariables[0] != KMType.INVALID_VALUE) && (Util.arrayCompare( repository.osVersion, (short) 0, scratchPad, (short) 0, tmpVariables[0]) @@ -3763,10 +3489,23 @@ private void checkVersionAndPatchLevel(byte[] scratchPad) { KMException.throwIt(KMError.KEY_REQUIRES_UPGRADE); } } + */ tmpVariables[0] = KMIntegerTag.getValue( scratchPad, (short) 0, KMType.UINT_TAG, KMType.OS_PATCH_LEVEL, data[HW_PARAMETERS]); - if ((tmpVariables[0] != KMType.INVALID_VALUE) + if (tmpVariables[0] != KMType.INVALID_VALUE){ + tmpVariables[1] = repository.getOsPatch(); + tmpVariables[1] = Util.arrayCompare( + KMInteger.cast(tmpVariables[1]).getBuffer(), + KMInteger.cast(tmpVariables[1]).getStartOff(), + scratchPad, (short) 0, tmpVariables[0]); + if(tmpVariables[1] == -1){ + KMException.throwIt(KMError.INVALID_KEY_BLOB); + }else if(tmpVariables[1] == 1){ + KMException.throwIt(KMError.KEY_REQUIRES_UPGRADE); + } + } + /*if ((tmpVariables[0] != KMType.INVALID_VALUE) && (Util.arrayCompare(repository.osPatch, (short) 0, scratchPad, (short) 0, tmpVariables[0]) != 0)) { if (Util.arrayCompare(repository.osPatch, (short) 0, scratchPad, (short) 0, tmpVariables[0]) @@ -3777,13 +3516,12 @@ private void checkVersionAndPatchLevel(byte[] scratchPad) { KMException.throwIt(KMError.KEY_REQUIRES_UPGRADE); } } + */ } private static void makeKeyCharacteristics(byte[] scratchPad) { - tmpVariables[0] = - KMInteger.instance(repository.osPatch, (short) 0, (short) repository.osPatch.length); - tmpVariables[1] = - KMInteger.instance(repository.osVersion, (short) 0, (short) repository.osVersion.length); + tmpVariables[0] = repository.getOsPatch(); + tmpVariables[1] = repository.getOsVersion(); data[HW_PARAMETERS] = KMKeyParameters.makeHwEnforced( data[KEY_PARAMETERS], @@ -3801,9 +3539,11 @@ private static void createEncryptedKeyBlob(byte[] scratchPad) { // make key characteristics - returns key characteristics in data[KEY_CHARACTERISTICS] makeKeyCharacteristics(scratchPad); // make root of trust blob - data[ROT] = - KMByteBlob.instance( - repository.verifiedBootKey, (short) 0, (short) repository.verifiedBootKey.length); + //data[ROT] = + // KMByteBlob.instance( + // repository.verifiedBootKey, (short) 0, (short) repository.verifiedBootKey.length); + data[ROT] = repository.getVerifiedBootKey(); + // make hidden key params list data[HIDDEN_PARAMETERS] = KMKeyParameters.makeHidden(data[KEY_PARAMETERS], data[ROT], scratchPad); @@ -3845,11 +3585,8 @@ private static void parseEncryptedKeyBlob(byte[] scratchPad) { if (tmpVariables[0] < 4) { KMException.throwIt(KMError.INVALID_KEY_BLOB); } - // Validate Auth Tag data[AUTH_TAG] = KMArray.cast(data[KEY_BLOB]).get(KEY_BLOB_AUTH_TAG); - if (!repository.validateAuthTag(data[AUTH_TAG])) { - KMException.throwIt(KMError.INVALID_KEY_BLOB); - } + // initialize data data[NONCE] = KMArray.cast(data[KEY_BLOB]).get(KEY_BLOB_NONCE); data[SECRET] = KMArray.cast(data[KEY_BLOB]).get(KEY_BLOB_SECRET); @@ -3863,9 +3600,11 @@ private static void parseEncryptedKeyBlob(byte[] scratchPad) { data[SW_PARAMETERS] = KMKeyCharacteristics.cast(data[KEY_CHARACTERISTICS]).getSoftwareEnforced(); // make root of trust blob - data[ROT] = - KMByteBlob.instance( - repository.verifiedBootKey, (short) 0, (short) repository.verifiedBootKey.length); + //data[ROT] = + // KMByteBlob.instance( + // repository.verifiedBootKey, (short) 0, (short) repository.verifiedBootKey.length); + data[ROT] = repository.getVerifiedBootKey(); + data[HIDDEN_PARAMETERS] = KMKeyParameters.makeHidden(data[APP_ID], data[APP_DATA], data[ROT], scratchPad); // make auth data @@ -3877,14 +3616,8 @@ private static void parseEncryptedKeyBlob(byte[] scratchPad) { private static void decryptSecret(byte[] scratchPad) { // derive master key - stored in derivedKey tmpVariables[0] = deriveKey(scratchPad); - AESKey derivedKey = - cryptoProvider.createAESKey(repository.getHeap(), data[DERIVED_KEY], tmpVariables[0]); - if (derivedKey == null) { - KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); - } - boolean verification = - cryptoProvider.aesGCMDecrypt( - derivedKey, + if(!seProvider.aesGCMDecrypt( + repository.getHeap(), data[DERIVED_KEY], tmpVariables[0], KMByteBlob.cast(data[SECRET]).getBuffer(), KMByteBlob.cast(data[SECRET]).getStartOff(), KMByteBlob.cast(data[SECRET]).length(), @@ -3898,8 +3631,7 @@ private static void decryptSecret(byte[] scratchPad) { data[AUTH_DATA_LENGTH], KMByteBlob.cast(data[AUTH_TAG]).getBuffer(), KMByteBlob.cast(data[AUTH_TAG]).getStartOff(), - KMByteBlob.cast(data[AUTH_TAG]).length()); - if (verification != true) { + KMByteBlob.cast(data[AUTH_TAG]).length())){ KMException.throwIt(KMError.INVALID_KEY_BLOB); } // Copy the decrypted secret @@ -3917,20 +3649,15 @@ private static void encryptSecret(byte[] scratchPad) { scratchPad, (short) 0, KMByteBlob.cast(data[NONCE]).length()); - cryptoProvider.newRandomNumber( + seProvider.newRandomNumber( KMByteBlob.cast(data[NONCE]).getBuffer(), KMByteBlob.cast(data[NONCE]).getStartOff(), KMByteBlob.cast(data[NONCE]).length()); // derive master key - stored in derivedKey tmpVariables[0] = deriveKey(scratchPad); - AESKey derivedKey = - cryptoProvider.createAESKey(repository.getHeap(), data[DERIVED_KEY], tmpVariables[0]); - if (derivedKey == null) { - KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); - } tmpVariables[1] = - cryptoProvider.aesGCMEncrypt( - derivedKey, + seProvider.aesGCMEncrypt( + repository.getHeap(), data[DERIVED_KEY], tmpVariables[0], KMByteBlob.cast(data[SECRET]).getBuffer(), KMByteBlob.cast(data[SECRET]).getStartOff(), KMByteBlob.cast(data[SECRET]).length(), @@ -3970,7 +3697,7 @@ private static void makeAuthData(byte[] scratchPad) { } // convert scratch pad to KMArray short index = 0; - short objPtr = 0; + short objPtr; while (index < tmpVariables[0]) { objPtr = Util.getShort(scratchPad, (short) (index * 2)); KMArray.cast(tmpVariables[1]).add(index, objPtr); @@ -3988,7 +3715,7 @@ private static void makeAuthData(byte[] scratchPad) { private static short addPtrToAAD(short dataArrPtr, byte[] aadBuf, short offset) { short index = (short) (offset * 2); short tagInd = 0; - short tagPtr = 0; + short tagPtr; short arrLen = KMArray.cast(dataArrPtr).length(); while (tagInd < arrLen) { tagPtr = KMArray.cast(dataArrPtr).get(tagInd); @@ -4005,15 +3732,27 @@ private static short deriveKey(byte[] scratchPad) { // generate derivation material from hidden parameters tmpVariables[2] = encoder.encode(tmpVariables[0], repository.getHeap(), tmpVariables[1]); // create derived key i.e. MAC - tmpVariables[3] = - cryptoProvider.aesCCMSign( +/* tmpVariables[3] = + seProvider.aesCCMSign( repository.getHeap(), tmpVariables[1], tmpVariables[2], repository.getMasterKeySecret(), scratchPad, (short) 0); - if (tmpVariables[3] < 0) { +*/ + tmpVariables[4] = repository.getMasterKeySecret(); + tmpVariables[3] = + seProvider.aesCCMSign( + repository.getHeap(), + tmpVariables[1], + tmpVariables[2], + KMByteBlob.cast(tmpVariables[4]).getBuffer(), + KMByteBlob.cast(tmpVariables[4]).getStartOff(), + KMByteBlob.cast(tmpVariables[4]).length(), + scratchPad, + (short) 0); + if (tmpVariables[3] < 0) { KMException.throwIt(KMError.UNKNOWN_ERROR); } // store the derived secret in data dictionary @@ -4028,7 +3767,6 @@ private static void sendError(APDU apdu, short err) { sendOutgoing(apdu); } - private short addIntegers(short num1, short num2){ short buf = repository.alloc((short)24); byte[] scratchPad = repository.getHeap(); @@ -4045,6 +3783,7 @@ private short addIntegers(short num1, short num2){ return KMInteger.uint_64(scratchPad,(short)(buf+16)); } +/* // num1 must be greater then or equal to num2 and both must be positive private short subtractIntegers(short num1, short num2){ short buf = repository.alloc((short)24); @@ -4063,11 +3802,12 @@ private short subtractIntegers(short num1, short num2){ subtract(scratchPad,buf,(short)(buf+8),(short)(buf+16)); return KMInteger.uint_64(scratchPad,(short)(buf+16)); } + */ private void add(byte[] buf, short op1, short op2, short result){ byte index = 7; byte carry = 0; - short tmp = 0; + short tmp; while(index >= 0){ tmp = (short) (buf[(short)(op1+index)] + buf[(short)(op2+index)]+carry); carry = 0; @@ -4079,22 +3819,11 @@ private void add(byte[] buf, short op1, short op2, short result){ // subtraction by borrowing. private void subtract(byte[] buf, short op1, short op2, short result) { -/* short temp1 = KMInteger.uint_64(buf,op1); - short temp2 = KMInteger.uint_64(buf,op2); - //twosComplement(buf, op2); - negate(buf, op2); - add(buf, op1, op2, result); - increment(buf, result); - Util.arrayCopyNonAtomic( - KMInteger.cast(temp1).getBuffer(), KMInteger.cast(temp1).getStartOff(), buf, op1, (short)8); - Util.arrayCopyNonAtomic( - KMInteger.cast(temp2).getBuffer(), KMInteger.cast(temp2).getStartOff(), buf, op2, (short)8); -*/ byte borrow = 0; byte index = 7; - short r = 0; - short x = 0; - short y = 0; + short r; + short x; + short y; while(index >= 0){ x = (short)(buf[(short)(op1 + index)] & 0xFF); y = (short)(buf[(short)(op2 + index)] & 0xFF); @@ -4108,30 +3837,8 @@ private void subtract(byte[] buf, short op1, short op2, short result) { index--; } } - /* private void twosComplement(byte[] buf, short op){ - negate(buf, op); - increment(buf, op); - } - private void negate(byte[] buf, short op){ - byte index = 7; - while (index >= 0) { - buf[(short)(op+index)] = (byte) (~buf[(short)(op+index)]); - index--; - } - } - private void increment(byte[] buf, short op){ - byte index = 7; - byte tmp; - byte carry = 1; - while(index <= 0){ - tmp = buf[(short)(op+index)]; - buf[(short)(op+index)] = (byte)(buf[(short)(op+index)] + carry); - if(buf[(short)(op+index)] < tmp) carry = 1; - else carry = 0; - index--; - } - }*/ - // use Euclid's formula: dividend = quotient*divisor + remainder + + // Use Euclid's formula: dividend = quotient*divisor + remainder // i.e. dividend - quotient*divisor = remainder where remainder < divisor. // so this is division by subtraction until remainder remains. private short divide(byte [] buf, short dividend, short divisor, short remainder){ @@ -4153,7 +3860,6 @@ private short divide(byte [] buf, short dividend, short divisor, short remainder expCnt = (short)(expCnt >> 1); shiftRight(buf, divisor); } - //copy(buf, dividend, remainder); return q; } @@ -4164,6 +3870,7 @@ private void copy(byte[] buf, short from, short to){ private byte compare(byte[] buf, short lhs, short rhs){ return Util.arrayCompare(buf,lhs,buf,rhs,(short)8); } + private void shiftLeft(byte[] buf, short start){ byte index = 7; byte carry = 0; @@ -4192,22 +3899,5 @@ private void shiftRight(byte[] buf, short start) { index++; } } -/* - 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/KMKeymasterStore.java b/Applet/Applet/src/com/android/javacard/keymaster/KMKeymasterStore.java new file mode 100644 index 00000000..ed9286f6 --- /dev/null +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMKeymasterStore.java @@ -0,0 +1,9 @@ +package com.android.javacard.keymaster; + +public interface KMKeymasterStore { + short getMasterKeySecret(byte[] buf, short start); + void createDocument(byte documentId, byte[]buf, short start, short len); + void updateData(byte documentId, short keyId, byte[]buf, short start, short len); + short getData(byte documentId, short keyId, byte[] buf, short start); + short getDocument(byte documentId, byte[] buf, short start); +} diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMOperation.java b/Applet/Applet/src/com/android/javacard/keymaster/KMOperation.java new file mode 100644 index 00000000..6dcabd7b --- /dev/null +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMOperation.java @@ -0,0 +1,17 @@ +package com.android.javacard.keymaster; + +public interface KMOperation { + short update(byte[] inputDataBuf, short inputDataStart, short inputDataLength, + byte[] outputDataBuf, short outputDataStart); + short update(byte[] inputDataBuf, short inputDataStart, short inputDataLength); + short finish(byte[] inputDataBuf, short inputDataStart, short inputDataLength, + byte[] outputDataBuf, short outputDataStart); + short sign(byte[] inputDataBuf, short inputDataStart, short inputDataLength, + byte[] signBuf, short signStart); + boolean verify(byte[] inputDataBuf, short inputDataStart, short inputDataLength, + byte[] signBuf, short signStart, short signLength); + void abort(); + void updateAAD(byte[] dataBuf, short dataStart, short dataLength); + + short getAESGCMOutputSize(short dataSize, short macLength); +} diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMOperationState.java b/Applet/Applet/src/com/android/javacard/keymaster/KMOperationState.java index 4987b595..3903d9a2 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMOperationState.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMOperationState.java @@ -16,225 +16,255 @@ package com.android.javacard.keymaster; +import javacard.framework.JCSystem; import javacard.framework.Util; -import javacard.security.Signature; -// TODO complete the class design and implementation public class KMOperationState { - private short opHandleCounter; - private byte algorithm; - private byte padding; - private byte blockMode; - private byte digest; - private short purpose; - private short keySize; - private boolean active; - private boolean trustedConfirmation; - private boolean cipherOperation; - // TODO This should be 64 bits - private short handle; - 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 secureUserIdReqd; - private boolean authTimeoutValidated; - private boolean aesGcmUpdateAllowed; - - private boolean aesBlockSaved; - private byte[] aesBlock; - private short macLength; - - public KMOperationState(){ - authTime = new byte[8]; - key = new byte[256]; - aesBlock = new byte[KMKeymasterApplet.AES_BLOCK_SIZE]; - reset(); - } - public short getKeySize() { - return keySize; + public static final byte MAX_DATA = 20; + public static final byte MAX_REFS = 2; + private static final byte DATA = 0; + private static final byte REFS = 1; + // byte type + private static final byte ALG = 0; + private static final byte PURPOSE = 1; + private static final byte PADDING = 2; + private static final byte BLOCKMODE = 3; + private static final byte DIGEST = 4; + private static final byte FLAGS = 5; + // short type + private static final byte KEY_SIZE = 6; + private static final byte MAC_LENGTH = 8; + // Handle - currently this is short + private static final byte OP_HANDLE = 10; + // Auth time 64 bits + private static final byte AUTH_TIME = 12; + // Flag masks + private static final byte AUTH_PER_OP_REQD = 1; + private static final byte SECURE_USER_ID_REQD = 2; + private static final byte AUTH_TIMEOUT_VALIDATED = 4; + private static final byte AES_GCM_UPDATE_ALLOWED = 8; + + // Object References + private static final byte OPERATION = 0; + private static final byte HMAC_SIGNER = 1; + + private static KMOperation hmacSigner; // used for trusted confirmation. + private static KMOperation op; + private static byte[] data; + private static Object[] slot; + private static KMOperationState prototype; + private static boolean dFlag; + + private KMOperationState() { + data = JCSystem.makeTransientByteArray(MAX_DATA, JCSystem.CLEAR_ON_RESET); + } + + private static KMOperationState proto() { + if (prototype == null) prototype = new KMOperationState(); + return prototype; + } + + public static KMOperationState instance(short opId, Object[] slot) { + KMOperationState opState = proto(); + opState.reset(); + Util.setShort(opState.data, OP_HANDLE, opId); + opState.slot = slot; + return opState; + } + + public static KMOperationState read(Object[] slot) { + KMOperationState opState = proto(); + opState.reset(); + Util.arrayCopy((byte[]) slot[DATA], (short) 0, opState.data, (short) 0, (short) opState.data.length); + Object[] ops = ((Object[]) slot[REFS]); + opState.op = (KMOperation) ops[OPERATION]; + opState.hmacSigner = (KMOperation) ops[HMAC_SIGNER]; + opState.slot = slot; + return opState; + } + + public void persist() { + if (!dFlag) return; + /*JCSystem.beginTransaction(); + Util.arrayCopy(data, (short) 0, (byte[]) slot[0], (short) 0, (short) ((byte[]) slot[0]).length); + Object[] ops = ((Object[]) slot[1]); + ops[0] = op; + ops[1] = hmacSigner; + JCSystem.commitTransaction();*/ + KMRepository.instance().persistOperation(data, Util.getShort(data, OP_HANDLE), op, hmacSigner); + dFlag = false; } public void setKeySize(short keySize) { - this.keySize = keySize; + Util.setShort(data, KEY_SIZE, keySize); } - - public void setTrustedConfirmationSigner(Signature hmacSigner){ - this.hmacSigner = hmacSigner; - trustedConfirmation = true; - } - public Signature getTrustedConfirmationSigner(){ - return hmacSigner; - } - public boolean isTrustedConfirmationRequired(){ - return trustedConfirmation; + public short getKeySize() { + return Util.getShort(data, KEY_SIZE); } - public void activate(){ - active = true; - handle = getOpHandleCounter(); + + public void setTrustedConfirmationSigner(KMOperation hmacSigner) { + KMOperationState.hmacSigner = hmacSigner; } - public void reset(){ - Util.arrayFillNonAtomic(authTime, (short)0,(short)8, (byte)0); - cipherOperation = false; - keyLength = 0; - authPerOperationReqd = false; - secureUserIdReqd = false; - active = false; - handle = 0; - Util.arrayFillNonAtomic(key,(short)0,(short)key.length,(byte)0); - cipher = null; - signer = null; - purpose = KMType.INVALID_VALUE; - trustedConfirmation = false; - hmacSigner = null; - authTimeoutValidated = false; - aesGcmUpdateAllowed = false; - aesBlockSaved = false; - macLength = 0; + + public KMOperation getTrustedConfirmationSigner() { + return KMOperationState.hmacSigner; } - //TODO make this random number - public short getOpHandleCounter() { - opHandleCounter++; - if(opHandleCounter < 0){ - opHandleCounter = 0; - } - return opHandleCounter; + + public boolean isTrustedConfirmationRequired() { + if (KMOperationState.hmacSigner != null) return true; + else return false; } - public boolean isActive() { - return active; + public void reset() { + dFlag = false; + hmacSigner = null; + op = null; + slot = null; + Util.arrayFillNonAtomic( + data, (short) 0, (short) data.length, (byte) 0); + } + private void dataUpdated(){ + dFlag = true; + } + public void release() { + JCSystem.beginTransaction(); + Util.arrayFillNonAtomic( + (byte[]) slot[0], (short) 0, (short) ((byte[]) slot[0]).length, (byte) 0); + Object[] ops = ((Object[]) slot[1]); + ops[0] = null; + ops[1] = null; + JCSystem.commitTransaction(); + reset(); } - public boolean isCipherOperation(){ return cipherOperation;} - public void setCipherOperation(boolean flag){cipherOperation = flag;} public short getHandle() { - return KMInteger.uint_16(handle); + return KMInteger.uint_16(Util.getShort(KMOperationState.data, OP_HANDLE)); } - public short handle(){ - return handle; + public short handle() { + return Util.getShort(KMOperationState.data, OP_HANDLE); } + public short getPurpose() { - return purpose; + return data[PURPOSE]; } - public void setPurpose(short purpose) { - this.purpose = purpose; + public void setPurpose(byte purpose) { + data[PURPOSE] = purpose; + dataUpdated(); } - public KMCipher getCipher() { - return cipher; + public void setOperation(KMOperation operation) { + op = operation; + dataUpdated(); + persist(); } - public void setCipher(KMCipher cipher) { - this.cipher = cipher; + public KMOperation getOperation() { + return op; } - public Signature getSignerVerifier() { - return signer; + public boolean isAuthPerOperationReqd() { + if ((data[FLAGS] & AUTH_PER_OP_REQD) != 0) return true; + else return false; } - public void setSignerVerifier(Signature signer) { - this.signer = signer; + public boolean isAuthTimeoutValidated() { + if ((data[FLAGS] & AUTH_TIMEOUT_VALIDATED) != 0) return true; + else return false; } - public short getKey(byte[] buf, short start) { - Util.arrayCopy(key,(short)0, buf, start,keyLength); - return keyLength; + public boolean isSecureUserIdReqd() { + if ((data[FLAGS] & SECURE_USER_ID_REQD) != 0) return true; + else return false; } - public void setKey(byte[] buf, short start, short len) { - keyLength = len; - Util.arrayCopy(buf, start, key, (short)0, len); + public short getAuthTime() { + return KMInteger.uint_64(data, (short) AUTH_TIME); } - public boolean isAuthPerOperationReqd() { - return authPerOperationReqd; + public void setAuthTime(byte[] timeBuf, short start) { + Util.arrayCopy(timeBuf, start, data, (short) AUTH_TIME, (short) 8); + dataUpdated(); } - public boolean isAuthTimeoutValidated() { - return authTimeoutValidated; + public void setOneTimeAuthReqd(boolean flag) { + if (flag) data[FLAGS] = (byte) (data[FLAGS] | SECURE_USER_ID_REQD); + else data[FLAGS] = (byte) (data[FLAGS] & (~SECURE_USER_ID_REQD)); + dataUpdated(); } - public boolean isSecureUserIdReqd(){return secureUserIdReqd;} - public byte[] getAuthTime() { - return authTime; + public void setAuthTimeoutValidated(boolean flag) { + if (flag) data[FLAGS] = (byte) (data[FLAGS] | AUTH_TIMEOUT_VALIDATED); + else data[FLAGS] = (byte) (data[FLAGS] & (~AUTH_TIMEOUT_VALIDATED)); + dataUpdated(); } - public void setAuthTime(byte[] time, short start) { - Util.arrayCopy(time, start, authTime, (short)0, (short)8); - } - public void setOneTimeAuthReqd(boolean flag){secureUserIdReqd = flag;} - public void setAuthTimeoutValidated(boolean flag) { - authTimeoutValidated = flag; + public void setAuthPerOperationReqd(boolean flag) { + if (flag) data[FLAGS] = (byte) (data[FLAGS] | AUTH_PER_OP_REQD); + else data[FLAGS] = (byte) (data[FLAGS] & (~AUTH_PER_OP_REQD)); + dataUpdated(); } - public void setAuthPerOperationReqd(boolean flag){ authPerOperationReqd = flag;} public byte getAlgorithm() { - return algorithm; + return data[ALG]; } public void setAlgorithm(byte algorithm) { - this.algorithm = algorithm; + this.data[ALG] = algorithm; + dataUpdated(); } public byte getPadding() { - return padding; + return data[PADDING]; } public void setPadding(byte padding) { - this.padding = padding; + this.data[PADDING] = padding; + dataUpdated(); } public byte getBlockMode() { - return blockMode; + return data[BLOCKMODE]; } public void setBlockMode(byte blockMode) { - this.blockMode = blockMode; + this.data[BLOCKMODE] = blockMode; + dataUpdated(); } public byte getDigest() { - return digest; + return data[DIGEST]; } public void setDigest(byte digest) { - this.digest = digest; + this.data[DIGEST] = digest; + dataUpdated(); } - public boolean isAesGcmUpdateAllowed(){ - return aesGcmUpdateAllowed; - } - public void setAesGcmUpdateComplete(){ - aesGcmUpdateAllowed = false; - } - public void setAesGcmUpdateStart(){ - aesGcmUpdateAllowed = true; - } - public byte[] getAesBlock(){ - return aesBlock; + public boolean isAesGcmUpdateAllowed() { + if ((data[FLAGS] & AES_GCM_UPDATE_ALLOWED) != 0) return true; + else return false; } - public void setAesBlock(byte[] buf, short start, short length){ - if(aesBlock.length != length) KMException.throwIt(KMError.INVALID_INPUT_LENGTH); - Util.arrayCopy(buf,start,aesBlock, (short)0, length); - aesBlockSaved = true; + public void setAesGcmUpdateComplete() { + data[FLAGS] = (byte) (data[FLAGS] & (~AES_GCM_UPDATE_ALLOWED)); + dataUpdated(); } - public boolean isAesBlockSaved() { - return aesBlockSaved; + public void setAesGcmUpdateStart() { + data[FLAGS] = (byte) (data[FLAGS] | AES_GCM_UPDATE_ALLOWED); + dataUpdated(); } public void setMacLength(short length) { - macLength = length; + Util.setShort(data, MAC_LENGTH, length); + dataUpdated(); } public short getMacLength() { - return macLength; + return Util.getShort(data, MAC_LENGTH); } } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMRepository.java b/Applet/Applet/src/com/android/javacard/keymaster/KMRepository.java index d70c87b7..31494dd6 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMRepository.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMRepository.java @@ -22,209 +22,216 @@ import javacard.framework.Util; public class KMRepository { - //TODO make the sizes configurable - public static final short INVALID_VALUE = (short) 0x8000; - 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; + // Data table configuration + public static final short DATA_INDEX_SIZE = 32; + public static final short DATA_INDEX_ENTRY_SIZE = 4; + public static final short DATA_MEM_SIZE = 2048; + public static final short DATA_INDEX_ENTRY_LENGTH = 0; + public static final short DATA_INDEX_ENTRY_OFFSET = 2; + + // Data table offsets + public static final byte MASTER_KEY = 8; + public static final byte SHARED_KEY = 9; + public static final byte COMPUTED_HMAC_KEY = 10; + public static final byte HMAC_NONCE = 11; + public static final byte ATT_ID_BRAND = 0; + public static final byte ATT_ID_DEVICE = 1; + public static final byte ATT_ID_PRODUCT = 2; + public static final byte ATT_ID_SERIAL = 3; + public static final byte ATT_ID_IMEI = 4; + public static final byte ATT_ID_MEID = 5; + public static final byte ATT_ID_MANUFACTURER = 6; + public static final byte ATT_ID_MODEL = 7; + public static final byte ATT_EXPONENT = 12; + public static final byte ATT_MODULUS = 13; + public static final byte CERT_AUTH_KEY_ID = 14; + public static final byte CERT_ISSUER = 15; + public static final byte CERT_EXPIRY_TIME = 16; + public static final byte BOOT_OS_VERSION = 17; + public static final byte BOOT_OS_PATCH = 18; + public static final byte BOOT_VERIFIED_BOOT_KEY = 19; + public static final byte BOOT_VERIFIED_BOOT_HASH = 20; + public static final byte BOOT_VERIFIED_BOOT_STATE = 21; + public static final byte BOOT_DEVICE_LOCKED_STATUS = 22; + public static final byte BOOT_DEVICE_LOCKED_TIME = 23; + public static final byte AUTH_TAG_1 = 24; + public static final byte AUTH_TAG_2 = 25; + public static final byte AUTH_TAG_3 = 26; + public static final byte AUTH_TAG_4 = 27; + public static final byte AUTH_TAG_5 = 28; + public static final byte AUTH_TAG_6 = 29; + public static final byte AUTH_TAG_7 = 30; + public static final byte AUTH_TAG_8 = 31; + + // Data Item sizes 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; - public static final short ATT_ID_HEAP_SIZE = 160; - public static final short CERT_DATA_MEM_SIZE = 256; - // Key Attestation related constants - public static final byte ATT_ID_TABLE_SIZE = 8; - public static final byte ATT_ID_HEADER_SIZE = 3; + public static final short SHARED_SECRET_KEY_SIZE = 32; public static final short ATT_KEY_MOD_SIZE = 256; public static final short ATT_KEY_EXP_SIZE = 256; - public static final byte ATT_ID_OFFSET = 0x02; - public static final byte ATT_ID_LENGTH = 0x01; - public static final byte ATT_ID_TAG = 0x00; - public static final byte ATT_ID_BRAND = 0x00; - public static final byte ATT_ID_DEVICE = 0x01; - public static final byte ATT_ID_PRODUCT = 0x02; - public static final byte ATT_ID_SERIAL = 0x03; - public static final byte ATT_ID_IMEI = 0x04; - public static final byte ATT_ID_MEID = 0x05; - public static final byte ATT_ID_MANUFACTURER = 0x06; - public static final byte ATT_ID_MODEL = 0x07; - final short[] attIdTags ={ - KMType.ATTESTATION_ID_BRAND, - KMType.ATTESTATION_ID_DEVICE, - KMType.ATTESTATION_ID_PRODUCT, - KMType.ATTESTATION_ID_SERIAL, - KMType.ATTESTATION_ID_IMEI, - KMType.ATTESTATION_ID_MEID, - KMType.ATTESTATION_ID_MANUFACTURER, - KMType.ATTESTATION_ID_MODEL}; - public byte[] attestCertIssuer; - public byte[] attestCertAuthKeyId; + public static final short HMAC_SEED_NONCE_SIZE = 32; + public static final short COMPUTED_HMAC_KEY_SIZE = 32; + public static final short OS_VERSION_SIZE = 4; + public static final short OS_PATCH_SIZE = 4; + public static final short DEVICE_LOCK_TS_SIZE = 8; + public static final short DEVICE_LOCK_FLAG_SIZE = 1; + public static final short BOOT_STATE_SIZE = 1; + public static final short MAX_BLOB_STORAGE = 8; + public static final short AUTH_TAG_LENGTH = 12; + public static final short AUTH_TAG_ENTRY_SIZE = 14; + public static final short HEAP_SIZE = 10000; + public static final short MAX_OPS = 4; + + // Operation State Table + private Object[] operationStateTable; + private static short opIdCounter; + // 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; - public boolean deviceUnlockPasswordOnly; - private byte[] masterKey; - private byte[] sharedKey; - private byte[] computedHmacKey; - private byte[] hmacNonce; + // Volatile memory heap private byte[] heap; private short heapIndex; + private byte[] dataTable; + private short dataIndex; - //Attesation Id Table; - private Object[] attIdTable; - - // Operation State Table - private Object[] operationStateTable; - - // boot parameters - //TODO change the following into private - public Object[] authTagRepo; - public short keyBlobCount; - public byte[] osVersion; - public byte[] osPatch; - public byte[] verifiedBootKey; - public short actualBootKeyLength; - public byte[] verifiedBootHash; - public short actualBootHashLength; - public boolean verifiedBootFlag; - public boolean selfSignedBootFlag; - public boolean deviceLockedFlag; - public byte[] deviceLockedTimestamp; - // attestation - private byte[] attKeyModulus; - private byte[] attKeyExponent; - private byte[] attIdMem; - private short attIdMemIndex; - // attestation cert data - private byte[] certData; - private short issuer; - private short issuerLen; - private short certExpiryTime; - private short certExpiryTimeLen; - private short authKeyId; - private short authKeyIdLen; - private short certDataIndex; - private boolean attIdSupported; + private static KMRepository repository; public static KMRepository instance() { return repository; } public KMRepository() { + newDataTable(); heap = JCSystem.makeTransientByteArray(HEAP_SIZE, JCSystem.CLEAR_ON_RESET); heapIndex = 0; - attIdMem = new byte[ATT_ID_HEAP_SIZE]; - attIdMemIndex = 0; + operationStateTable = new Object[MAX_OPS]; + byte index = 0; + while(index < MAX_OPS){ + operationStateTable[index] = new Object[]{new byte[2], + new Object[] {new byte[KMOperationState.MAX_DATA], + new Object[KMOperationState.MAX_REFS]}}; + index++; + } + repository = this; + } - authTagRepo = new Object[MAX_BLOB_STORAGE]; + public KMOperationState findOperation(short opHandle) { short index = 0; - while (index < MAX_BLOB_STORAGE) { - authTagRepo[index] = new KMAuthTag(); - ((KMAuthTag) authTagRepo[index]).reserved = false; - ((KMAuthTag) authTagRepo[index]).authTag = new byte[AES_GCM_AUTH_TAG_LENGTH]; - ((KMAuthTag) authTagRepo[index]).usageCount = 0; + byte[] opId; + while(index < MAX_OPS){ + opId = ((byte[])((Object[])operationStateTable[index])[0]); + if(Util.getShort(opId,(short)0) == opHandle)return KMOperationState.read((Object[])((Object[])operationStateTable[index])[1]); index++; } + return null; + } - 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; + public KMOperationState reserveOperation(){ + short index = 0; + byte[] opId; while(index < MAX_OPS){ - operationStateTable[index] = new KMOperationState(); - ((KMOperationState)operationStateTable[index]).reset(); + opId = (byte[])((Object[])operationStateTable[index])[0]; + if(Util.getShort(opId,(short)0) == 0){ + return KMOperationState.instance(/*Util.getShort(opId,(short)0)*/getOpId(),(Object[])((Object[])operationStateTable[index])[1]); + } index++; } - deviceLockedFlag = false; - deviceLockedTimestamp = new byte[8]; - deviceUnlockPasswordOnly = false; - Util.arrayFillNonAtomic(deviceLockedTimestamp,(short)0,(short)8,(byte)0); - - attKeyModulus = new byte[ATT_KEY_MOD_SIZE]; - attKeyExponent = new byte[ATT_KEY_EXP_SIZE]; - attIdTable = new Object[ATT_ID_TABLE_SIZE]; - attIdSupported = false; + return null; + } + + //TODO refactor following method + public void persistOperation(byte[] data, short opHandle, KMOperation op, KMOperation hmacSigner) { + short index = 0; + byte[] opId; + //Update an existing operation state. + while(index < MAX_OPS){ + opId = (byte[])((Object[])operationStateTable[index])[0]; + if(Util.getShort(opId,(short)0) == opHandle){ + Object[] slot = (Object[])((Object[])operationStateTable[index])[1]; + JCSystem.beginTransaction(); + Util.arrayCopy(data, (short) 0, (byte[]) slot[0], (short) 0, (short) ((byte[]) slot[0]).length); + Object[] ops = ((Object[]) slot[1]); + ops[0] = op; + ops[1] = hmacSigner; + JCSystem.commitTransaction(); + return; + } + index++; + } + index = 0; - while(index < ATT_ID_TABLE_SIZE){ - attIdTable[index] = new short[ATT_ID_HEADER_SIZE]; - ((short[])attIdTable[index])[ATT_ID_TAG] = attIdTags[index]; - ((short[])attIdTable[index])[ATT_ID_LENGTH] = 0; + //Persist a new operation. + while(index < MAX_OPS){ + opId = (byte[])((Object[])operationStateTable[index])[0]; + if(Util.getShort(opId,(short)0) == 0){ + Util.setShort(opId, (short)0, opHandle); + Object[] slot = (Object[])((Object[])operationStateTable[index])[1]; + JCSystem.beginTransaction(); + Util.arrayCopy(data, (short) 0, (byte[]) slot[0], (short) 0, (short) ((byte[]) slot[0]).length); + Object[] ops = ((Object[]) slot[1]); + ops[0] = op; + ops[1] = hmacSigner; + JCSystem.commitTransaction(); + break; + } index++; } - certData = new byte[CERT_DATA_MEM_SIZE]; - certDataIndex = 0; - 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]; + private short getOpId() { + byte index = 0; + opIdCounter++; + while (index < MAX_OPS) { + if (Util.getShort((byte[]) ((Object[]) operationStateTable[index])[0], (short) 0) + == opIdCounter) { + opIdCounter++; + index = 0; + continue; } index++; } - return null; + return opIdCounter; } + public void releaseOperation(KMOperationState op){ - op.reset(); + short index = 0; + byte[] var; + while(index < MAX_OPS){ + var = ((byte[])((Object[])operationStateTable[index])[0]); + if(Util.getShort(var,(short)0) == op.handle()){ + Util.arrayFillNonAtomic(var,(short)0,(short)var.length,(byte)0); + op.release(); + break; + } + index++; + } } + public void initMasterKey(byte[] key, short len) { - if (masterKey == null) { - 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); - } + if(len != MASTER_KEY_SIZE) ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); + writeDataEntry(MASTER_KEY,key, (short)0, len); } - public void initHmacSharedSecretKey(byte[] key, short len) { - if (sharedKey == null) { - sharedKey = new byte[SHARED_SECRET_KEY_SIZE]; - } + public void initHmacSharedSecretKey(byte[] key, short start, short len) { if(len != SHARED_SECRET_KEY_SIZE) KMException.throwIt(KMError.INVALID_INPUT_LENGTH); - Util.arrayCopy(key, (short) 0, sharedKey, (short) 0, len); + writeDataEntry(SHARED_KEY,key,start,len); } public void initComputedHmac(byte[] key, short start, short len) { - if (computedHmacKey == null) { - 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); + writeDataEntry(COMPUTED_HMAC_KEY,key,start,len); } public void initHmacNonce(byte[] nonce, short offset, short len) { - if (hmacNonce == null) { - hmacNonce = new byte[HMAC_SEED_NONCE_SIZE]; - } - if (len != HMAC_SEED_NONCE_SIZE) { - KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + if (len != HMAC_SEED_NONCE_SIZE) { KMException.throwIt(KMError.INVALID_INPUT_LENGTH);} + writeDataEntry(HMAC_NONCE,nonce,offset,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); + // Javacard Runtime environment cleans up the data. + } public void onProcess() {} @@ -236,10 +243,12 @@ public void clean() { public void onDeselect() {} - public void onSelect() {} + public void onSelect() { + // If write through caching is implemented then this method will restore the data into cache + } - public byte[] getMasterKeySecret() { - return masterKey; + public short getMasterKeySecret() { + return readData(MASTER_KEY); } public short alloc(short length) { @@ -250,280 +259,369 @@ public short alloc(short length) { return (short) (heapIndex - length); } - public byte[] getHeap() { - return heap; + private short dataAlloc(short length) { + if (((short) (dataIndex + length)) > dataTable.length) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + dataIndex += length; + return (short) (dataIndex - length); + } + + + private void newDataTable(){ + if(dataTable == null) { + dataTable = new byte[DATA_MEM_SIZE]; + dataIndex = (short)(DATA_INDEX_SIZE*DATA_INDEX_ENTRY_SIZE); + } + } + + public void restoreData(short blob){ + JCSystem.beginTransaction(); + Util.arrayCopy( + KMByteBlob.cast(blob).getBuffer(),KMByteBlob.cast(blob).getStartOff(),dataTable,(short)0, + KMByteBlob.cast(blob).length() + ); + JCSystem.commitTransaction(); + } + + public byte[] getDataTable(){ + return dataTable; + } + + private void clearDataEntry(short id){ + JCSystem.beginTransaction(); + id = (short)(id * DATA_INDEX_ENTRY_SIZE); + short dataLen = Util.getShort(dataTable,(short)(id+DATA_INDEX_ENTRY_LENGTH)); + if (dataLen != 0) { + short dataPtr = Util.getShort(dataTable,(short)(id+DATA_INDEX_ENTRY_OFFSET)); + Util.arrayFillNonAtomic(dataTable, dataPtr,dataLen,(byte)0); + Util.arrayFillNonAtomic(dataTable, id,DATA_INDEX_ENTRY_SIZE,(byte)0); + } + JCSystem.commitTransaction(); + } + + private void writeDataEntry(short id, byte[] buf, short offset, short len) { + JCSystem.beginTransaction(); + short dataPtr; + id = (short)(id * DATA_INDEX_ENTRY_SIZE); + short dataLen = Util.getShort(dataTable,(short)(id+DATA_INDEX_ENTRY_LENGTH)); + if (dataLen == 0) { + dataPtr = dataAlloc(len); + Util.setShort(dataTable,(short)(id+DATA_INDEX_ENTRY_OFFSET),dataPtr); + Util.setShort(dataTable,(short)(id+DATA_INDEX_ENTRY_LENGTH),len); + Util.arrayCopyNonAtomic(buf, offset,dataTable,dataPtr, len); + } else { + if(len != dataLen) KMException.throwIt(KMError.UNKNOWN_ERROR); + dataPtr = Util.getShort(dataTable,(short)(id+DATA_INDEX_ENTRY_OFFSET)); + Util.arrayCopyNonAtomic(buf, offset,dataTable,dataPtr, len); + } + JCSystem.commitTransaction(); } - public byte[] getSharedKey() { - return sharedKey; + private short readDataEntry(short id, byte[] buf, short offset){ + id = (short)(id * DATA_INDEX_ENTRY_SIZE); + short len = Util.getShort(dataTable,(short)(id+DATA_INDEX_ENTRY_LENGTH)); + if (len != 0) { + Util.arrayCopyNonAtomic( + dataTable, + Util.getShort(dataTable, (short) (id + DATA_INDEX_ENTRY_OFFSET)), + buf, + offset, + len); + } + return len; } - public byte[] getHmacNonce() { - return hmacNonce; + private short dataLength(short id){ + id = (short)(id * DATA_INDEX_ENTRY_SIZE); + return Util.getShort(dataTable,(short)(id+DATA_INDEX_ENTRY_LENGTH)); } - public void setHmacNonce(byte[] hmacNonce) { - Util.arrayCopy(hmacNonce, (short) 0, this.hmacNonce, (short) 0, HMAC_SEED_NONCE_SIZE); + public byte[] getHeap() { + return heap; } - public byte[] getComputedHmacKey() { - return computedHmacKey; + + + public short getSharedKey() { + return readData(SHARED_KEY); + } + + public short getHmacNonce() { + return readData(HMAC_NONCE); } - public void setComputedHmacKey(byte[] computedHmacKey) { - Util.arrayCopy( computedHmacKey, (short) 0, this.computedHmacKey, (short) 0, COMPUTED_HMAC_KEY_SIZE); + public short getComputedHmacKey() { + return readData(COMPUTED_HMAC_KEY); } public void persistAuthTag(short authTag) { + if(KMByteBlob.cast(authTag).length() != AUTH_TAG_LENGTH)KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + short authTagEntry = alloc(AUTH_TAG_ENTRY_SIZE); + Util.arrayCopyNonAtomic( + KMByteBlob.cast(authTag).getBuffer(), + KMByteBlob.cast(authTag).getStartOff(), + getHeap(),authTagEntry, AUTH_TAG_LENGTH); + Util.setShort(getHeap(),(short)(authTagEntry+AUTH_TAG_LENGTH),(short)0); short index = 0; while (index < MAX_BLOB_STORAGE) { - if (!((KMAuthTag) authTagRepo[index]).reserved) { + if(dataLength((short)(index+AUTH_TAG_1)) == 0){ 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++; + writeDataEntry((short)(index+AUTH_TAG_1), + KMByteBlob.cast(authTagEntry).getBuffer(), + KMByteBlob.cast(authTagEntry).getStartOff(), + AUTH_TAG_ENTRY_SIZE); JCSystem.commitTransaction(); - break; } index++; } } public boolean validateAuthTag(short authTag) { - KMAuthTag tag = findTag(authTag); - if(tag != null){ - return true; - } - return false; + short tag = findTag(authTag); + return tag !=KMType.INVALID_VALUE; } public void removeAuthTag(short authTag) { - KMAuthTag tag = findTag(authTag); - if(tag == null){ - ISOException.throwIt(ISO7816.SW_COMMAND_NOT_ALLOWED); - } - JCSystem.beginTransaction(); - tag.reserved = false; - short index = 0; - while(index < AES_GCM_AUTH_TAG_LENGTH){ - tag.authTag[index] = 0; - index++; - } - tag.usageCount = 0; - keyBlobCount--; - JCSystem.commitTransaction(); + short tag = findTag(authTag); + if(tag ==KMType.INVALID_VALUE) KMException.throwIt(KMError.UNKNOWN_ERROR); + clearDataEntry(tag); } public void removeAllAuthTags() { JCSystem.beginTransaction(); - KMAuthTag tag = null; short index = 0; - short i = 0; while (index < MAX_BLOB_STORAGE) { - tag = (KMAuthTag) authTagRepo[index]; - tag.reserved = false; - i = 0; - while(i < AES_GCM_AUTH_TAG_LENGTH){ - tag.authTag[i] = 0; - i++; - } - tag.usageCount = 0; + clearDataEntry((short)(index+AUTH_TAG_1)); index++; } - keyBlobCount = 0; - JCSystem.commitTransaction(); } - public KMAuthTag findTag(short authTag) { + private short findTag(short authTag) { + if(KMByteBlob.cast(authTag).length() != AUTH_TAG_LENGTH)KMException.throwIt(KMError.INVALID_INPUT_LENGTH); short index = 0; - short found = 0; + short authTagEntry; + short found; while (index < MAX_BLOB_STORAGE) { - if (((KMAuthTag) authTagRepo[index]).reserved) { + if (dataLength((short)(index+AUTH_TAG_1)) != 0) { + authTagEntry = readData((short)(index+AUTH_TAG_1)); 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]; - } + Util.arrayCompare( + KMByteBlob.cast(authTagEntry).getBuffer(), + KMByteBlob.cast(authTagEntry).getStartOff(), + KMByteBlob.cast(authTag).getBuffer(), + KMByteBlob.cast(authTag).getStartOff(), + AUTH_TAG_LENGTH); + if(found == 0)return (short)(index+AUTH_TAG_1); } index++; } - return null; + return KMType.INVALID_VALUE; } public short getRateLimitedKeyCount(short authTag) { - KMAuthTag tag = findTag(authTag); - if (tag != null) { - return tag.usageCount; + short tag = findTag(authTag); + short blob; + if (tag != KMType.INVALID_VALUE) { + blob = readData(tag); + return Util.getShort(KMByteBlob.cast(blob).getBuffer(), + (short)(KMByteBlob.cast(blob).getStartOff()+AUTH_TAG_LENGTH)); } return KMType.INVALID_VALUE; } public void setRateLimitedKeyCount(short authTag, short val) { - KMAuthTag tag = findTag(authTag); - JCSystem.beginTransaction(); - if (tag != null) { - tag.usageCount = val; + short tag = findTag(authTag); + short blob; + if (tag != KMType.INVALID_VALUE) { + blob = alloc((short)2); + Util.setShort(getHeap(),blob,val); + writeDataEntry(tag,getHeap(), blob,(short)2); } - JCSystem.commitTransaction(); } - public KMOperationState findOperation(short opHandle) { - short index = 0; - while(index < MAX_OPS){ - if(((KMOperationState)operationStateTable[index]).isActive() && - ((KMOperationState)operationStateTable[index]).handle() == opHandle){ - return (KMOperationState)operationStateTable[index]; - } - index++; - } - return null; - } public void persistAttestationKey(short mod, short exp) { - JCSystem.beginTransaction(); - if(KMByteBlob.cast(mod).length() != ATT_KEY_MOD_SIZE) KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); - Util.arrayCopy( + if(KMByteBlob.cast(mod).length() != ATT_KEY_MOD_SIZE || + KMByteBlob.cast(exp).length() != ATT_KEY_EXP_SIZE) KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); + writeDataEntry(ATT_MODULUS, KMByteBlob.cast(mod).getBuffer(), KMByteBlob.cast(mod).getStartOff(), - attKeyModulus, - (short)0, KMByteBlob.cast(mod).length()); - - if(KMByteBlob.cast(exp).length() != ATT_KEY_EXP_SIZE) KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); - Util.arrayCopy( + writeDataEntry(ATT_EXPONENT, KMByteBlob.cast(exp).getBuffer(), KMByteBlob.cast(exp).getStartOff(), - attKeyExponent, - (short)0, KMByteBlob.cast(exp).length()); - JCSystem.commitTransaction(); } - public byte[] getAttKeyModulus() { - return attKeyModulus; + public short getAttKeyModulus() { + return readData(ATT_MODULUS); } - public byte[] getAttKeyExponent() { - return attKeyExponent; + public short getAttKeyExponent() { + return readData(ATT_EXPONENT); } public void persistAttId(byte id, byte[] buf, short start, short len){ - JCSystem.beginTransaction(); - short[] attId = (short[])attIdTable[id]; - attId[ATT_ID_OFFSET] = allocAttIdMemory(len); - attId[ATT_ID_LENGTH] = len; - Util.arrayCopy(buf,start, attIdMem,attId[ATT_ID_OFFSET],len); - attIdSupported = true; - JCSystem.commitTransaction(); + writeDataEntry(id, buf,start,len); } - public short getAttId(byte id, byte[] buf, short start){ - short[] attId = (short[])attIdTable[id]; - Util.arrayCopy(attIdMem,attId[ATT_ID_OFFSET],buf,start,attId[ATT_ID_LENGTH]); - return attId[ATT_ID_LENGTH]; + public short getAttId(byte id){ + return readData(id); } - public short getAttIdOffset(byte id){ - short[] attId = (short[])attIdTable[id]; - return attId[ATT_ID_OFFSET]; + public void deleteAttIds(){ + clearDataEntry(ATT_ID_BRAND); + clearDataEntry(ATT_ID_MEID); + clearDataEntry(ATT_ID_DEVICE); + clearDataEntry(ATT_ID_IMEI); + clearDataEntry(ATT_ID_MODEL); + clearDataEntry(ATT_ID_PRODUCT); + clearDataEntry(ATT_ID_SERIAL); + clearDataEntry(ATT_ID_MANUFACTURER); } - public byte[] getAttIdBuffer(byte id){ - return attIdMem; + public short getIssuer() { + return readData(CERT_ISSUER); } - public short getAttIdLen(byte id){ - short[] attId = (short[])attIdTable[id]; - return attId[ATT_ID_LENGTH]; + public short readData(short id){ + short blob = KMByteBlob.instance(dataLength(id)); + if(readDataEntry(id,KMByteBlob.cast(blob).getBuffer(),KMByteBlob.cast(blob).getStartOff())== 0){ + return 0; + } + return blob; } - public short getAttIdTag(byte id){ - short[] attId = (short[])attIdTable[id]; - return attId[ATT_ID_TAG]; + public void setIssuer(byte[] buf, short start, short len) { + writeDataEntry(CERT_ISSUER,buf,start,len); } - private short allocAttIdMemory(short len){ - if (((short) (attIdMemIndex + len)) > attIdMem.length) { - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + + public short getCertExpiryTime() { + return readData(CERT_EXPIRY_TIME); + } + + public void setCertExpiryTime(byte[] buf, short start, short len) { + writeDataEntry(CERT_EXPIRY_TIME, buf,start,len); + } + + public short getAuthKeyId() { + return readData(CERT_AUTH_KEY_ID); + } + + public void setAuthKeyId(byte[] buf, short start, short len) { + writeDataEntry(CERT_AUTH_KEY_ID,buf,start,len); + } + private static final byte[] zero = {0,0,0,0,0,0,0,0}; + + public short getOsVersion(){ + short blob = readData(BOOT_OS_VERSION); + if (blob != 0) { + return KMInteger.uint_32( + KMByteBlob.cast(blob).getBuffer(), KMByteBlob.cast(blob).getStartOff()); + }else{ + return KMInteger.uint_32(zero,(short)0); } - attIdMemIndex += len; - return (short) (attIdMemIndex - len); } - public void deleteAttIds(){ - JCSystem.beginTransaction(); - Util.arrayFillNonAtomic(attIdMem,(short)0,(short)attIdMem.length,(byte)0); - short index = 0; - while(index < ATT_ID_TABLE_SIZE){ - short[] attId = (short[])attIdTable[index]; - attId[ATT_ID_OFFSET] = 0; - attId[ATT_ID_LENGTH] = 0; - index++; + public short getOsPatch(){ + short blob = readData(BOOT_OS_PATCH); + if (blob != 0) { + return KMInteger.uint_32( + KMByteBlob.cast(blob).getBuffer(), KMByteBlob.cast(blob).getStartOff()); + }else{ + return KMInteger.uint_32(zero,(short)0); } - attIdSupported = false; - JCSystem.commitTransaction(); } - public boolean isAttIdSupported(){ - return attIdSupported; + + public short getVerifiedBootKey(){ + return readData(BOOT_VERIFIED_BOOT_KEY); } - public short getIssuer() { - return issuer; + + public short getVerifiedBootHash(){ + return readData(BOOT_VERIFIED_BOOT_HASH); } - public void setIssuer(byte[] buf, short start, short len) { - this.issuer = allocCertData(len); - this.issuerLen = len; - Util.arrayCopy(buf,start,certData, issuer,len); + public boolean getDeviceLock(){ + short blob = readData(BOOT_DEVICE_LOCKED_STATUS); + return (byte)((getHeap())[KMByteBlob.cast(blob).getStartOff()] & 0xFE) != 0; } - public short getIssuerLen() { - return issuerLen; + public byte getBootState(){ + short blob = readData(BOOT_VERIFIED_BOOT_STATE); + return (getHeap())[KMByteBlob.cast(blob).getStartOff()]; } - public short getCertExpiryTime() { - return certExpiryTime; + public boolean getDeviceLockPasswordOnly(){ + short blob = readData(BOOT_DEVICE_LOCKED_STATUS); + return (byte)((getHeap())[KMByteBlob.cast(blob).getStartOff()] & 0xFD) != 0; } - public void setCertExpiryTime(byte[] buf, short start, short len) { - this.certExpiryTime = allocCertData(len); - this.certExpiryTimeLen = len; - Util.arrayCopy(buf,start,certData, certExpiryTime,len); + public short getDeviceTimeStamp(){ + short blob = readData(BOOT_DEVICE_LOCKED_TIME); + if(blob != 0){ + return KMInteger.uint_64(KMByteBlob.cast(blob).getBuffer(), + KMByteBlob.cast(blob).getStartOff()); + }else{ + return KMInteger.uint_64(zero,(short)0); + } } - public short getCertExpiryTimeLen() { - return certExpiryTimeLen; + public void setOsVersion(byte[] buf, short start, short len){ + if(len != OS_VERSION_SIZE) KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + writeDataEntry(BOOT_OS_VERSION,buf,start,len); } + public void setDeviceLock(boolean flag){ + short start = alloc(DEVICE_LOCK_FLAG_SIZE); + if(flag) (getHeap())[start] = (byte)((getHeap())[start] | 0x01); + else (getHeap())[start] = (byte)((getHeap())[start] & 0xFE); + writeDataEntry(BOOT_DEVICE_LOCKED_STATUS,getHeap(),start,DEVICE_LOCK_FLAG_SIZE); + } - public short getAuthKeyId() { - return authKeyId; + public void setDeviceLockPasswordOnly(boolean flag){ + short start = alloc(DEVICE_LOCK_FLAG_SIZE); + if(flag) (getHeap())[start] = (byte)((getHeap())[start] | 0x02); + else (getHeap())[start] = (byte)((getHeap())[start] & 0xFD); + writeDataEntry(BOOT_DEVICE_LOCKED_STATUS,getHeap(),start,DEVICE_LOCK_FLAG_SIZE); } - public void setAuthKeyId(byte[] buf, short start, short len) { - this.authKeyId = allocCertData(len); - this.authKeyIdLen = len; - Util.arrayCopy(buf,start,certData,authKeyId,len); + public void setDeviceLockTimestamp(byte[] buf, short start, short len){ + if(len != DEVICE_LOCK_TS_SIZE) KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + writeDataEntry(BOOT_DEVICE_LOCKED_TIME, buf, start,len); } - public short getAuthKeyIdLen() { - return authKeyIdLen; + public void clearDeviceLockTimeStamp(){ + clearDataEntry(BOOT_DEVICE_LOCKED_TIME); } - public byte[] getCertDataBuffer(){ - return certData; + + public void setOsPatch(byte[] buf, short start, short len){ + if(len != OS_PATCH_SIZE) KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + writeDataEntry(BOOT_OS_PATCH,buf,start,len); } - private short allocCertData(short len){ - if (((short) (certDataIndex + len)) > certData.length) { - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + + public void setVerifiedBootKey(byte[] buf, short start, short len){ + if(len > BOOT_KEY_MAX_SIZE) KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + writeDataEntry(BOOT_VERIFIED_BOOT_KEY,buf,start,len); + } + + public void setVerifiedBootHash(byte[] buf, short start, short len){ + if(len > BOOT_HASH_MAX_SIZE) KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + writeDataEntry(BOOT_VERIFIED_BOOT_HASH,buf,start,len); + } + + public void setBootState(byte state){ + short start = alloc(BOOT_STATE_SIZE); + (getHeap())[start] = state; + writeDataEntry(BOOT_VERIFIED_BOOT_STATE,getHeap(),start,BOOT_STATE_SIZE); + } + + public short getKeyBlobCount(){ + byte index = 0; + byte count = 0; + while(index < MAX_BLOB_STORAGE){ + if(dataLength((short)(index+AUTH_TAG_1)) != 0) count++; + index++; } - certDataIndex += len; - return (short) (certDataIndex - len); + return count; } } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMSEProvider.java b/Applet/Applet/src/com/android/javacard/keymaster/KMSEProvider.java index 5ef2517f..5ca46154 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMSEProvider.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMSEProvider.java @@ -1,37 +1,54 @@ package com.android.javacard.keymaster; -import javacard.security.AESKey; -import javacard.security.DESKey; -import javacard.security.ECPrivateKey; -import javacard.security.HMACKey; -import javacard.security.Key; -import javacard.security.KeyPair; -import javacard.security.RSAPrivateKey; -import javacard.security.Signature; - public interface KMSEProvider { - KeyPair createRsaKeyPair(); - KeyPair createECKeyPair(); - ECPrivateKey createEcKey(byte[] privBuffer, short privOff, short privLength); - RSAPrivateKey createRsaKey(byte[] modBuffer, short modOff, short modLength, - byte[] privBuffer, short privOff, short privLength); - - HMACKey createHMACKey(byte[] secretBuffer, short secretOff, short secretLength); - DESKey createTDESKey(byte[] secretBuffer, short secretOff, short secretLength); - AESKey createAESKey(short keysize); - AESKey createAESKey(byte[] buf, short startOff, short length); - DESKey createTDESKey(); - HMACKey createHMACKey(short keysize); + // Key generation operations short createSymmetricKey(byte alg, short keysize, byte[] buf, short startOff); + void createAsymmetricKey( + byte alg, + byte[] privKeyBuf, + short privKeyStart, + short privKeyMaxLength, + byte[] pubModBuf, + short pubModStart, + short pubModMaxLength, + short[] lengths); + + // Import key operations + boolean importSymmetricKey(byte alg, short keysize, byte[] buf, short startOff, short length); + + boolean importAsymmetricKey( + byte alg, + byte[] buf, + short start, + short length, + byte[] privKeyBuf, + short privKeyStart, + short privKeyLength, + byte[] pubModBuf, + short pubModStart, + short pubModLength); + + boolean importAsymmetricKey( + byte alg, + byte[] privKeyBuf, + short privKeyStart, + short privKeyLength, + byte[] pubModBuf, + short pubModStart, + short pubModLength); // Oneshot Operations void newRandomNumber(byte[] num, short offset, short length); + void addRngEntropy(byte[] num, short offset, short length); + byte[] getTrueRandomNumber(short len); short aesGCMEncrypt( - AESKey key, + byte[] aesKey, + short aesKeyStart, + short aesKeyLen, byte[] secret, short secretStart, short secretLen, @@ -48,106 +65,122 @@ short aesGCMEncrypt( short authTagLen); boolean aesGCMDecrypt( - AESKey key, - byte[] encSecret, - short encSecretStart, - short encSecretLen, - byte[] secret, - short secretStart, - byte[] nonce, - short nonceStart, - short nonceLen, - byte[] authData, - short authDataStart, - short authDataLen, - byte[] authTag, - short authTagStart, - short authTagLen); + byte[] aesKey, + short aesKeyStart, + short aesKeyLen, + 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); short aesCCMSign( byte[] bufIn, short bufInStart, short buffInLength, byte[] masterKeySecret, - byte[] bufOut, + short masterKeyStart, short masterKeyLen, byte[] bufOut, short bufStart); - HMACKey cmacKdf(byte[] keyMaterial, byte[] label, byte[] context, short contextStart, short contextLength); - - short cmacKdf(byte[] keyMaterial, byte[] label, byte[] context, short contextStart, short contextLength, byte[] keyBuf, short keyStart); - - 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); - short hmacSign(byte[] keyBuf, short keyStart, short keyLength, byte[] data, short dataStart, short dataLength, byte[] mac, short macStart); - boolean hmacVerify(byte[] keyBuf, short keyStart, short keyLength, byte[] data, short dataStart, short dataLength, - byte[] mac, short macStart, short macLength); + short cmacKdf( + byte[] keyMaterial, + short keyMaterialStart, + short keyMaterialLen, + byte[] label, + byte[] context, + short contextStart, + short contextLength, + byte[] keyBuf, + short keyStart); + + short hmacSign( + byte[] keyBuf, + short keyStart, + short keyLength, + byte[] data, + short dataStart, + short dataLength, + byte[] mac, + short macStart); + + boolean hmacVerify( + byte[] keyBuf, + short keyStart, + short keyLength, + byte[] data, + short dataStart, + short dataLength, + byte[] mac, + short macStart, + short macLength); + + short rsaDecipherOAEP256( + byte[] secret, + short secretStart, + short secretLength, + byte[] modBuffer, + short modOff, + short modLength, + byte[] inputDataBuf, + short inputDataStart, + short inputDataLength, + byte[] outputDataBuf, + short outputDataStart); + + short rsaSignPKCS1256( + byte[] secret, + short secretStart, + short secretLength, + byte[] modBuffer, + short modOff, + short modLength, + byte[] inputDataBuf, + short inputDataStart, + short inputDataLength, + byte[] outputDataBuf, + short outputDataStart); // Persistent Operations - short initSymmetricOperation(byte purpose, byte alg, byte digest, byte padding, byte blockMode, - byte[] keyBuf, short keyStart, short keyLength); - short initSymmetricOperation(byte purpose, byte alg, byte digest, - byte[] keyBuf, short keyStart, short keyLength); - short initAsymmetricOperation(byte purpose, byte alg, byte padding, byte digest, - byte[] privKeyBuf, short privKeyStart, short privKeyLength, - byte[] modBuf, short modStart, short modLength); - short initAsymmetricOperation(byte purpose, byte alg, byte padding, byte digest, - byte[] privKeyBuf, short privKeyStart, short privKeyLength); - short updateOperation(short opHandle, byte[] inputDataBuf, short inputDataStart, short inputDataLength, - byte[] outputDataBuf, short outputDataStart); - short finishOperation(short opHandle, byte[] inputDataBuf, short inputDataStart, short inputDataLength, - byte[] outputDataBuf, short outputDataStart); - void abortOperation(short opHandle); - - short hmacInit(byte[] keyBuf, short keyStart, short keyLength, byte digest, byte mode); - short hmacSign(short opHandle, byte[] data, short dataStart, short dataLength, byte[] mac, short macStart); - boolean hmacVerify(short opHandle, byte[] keyBuf, short keyStart, short keyLength, - byte[] data, short dataStart, short dataLength, - byte[] mac, short macStart, short macLength); - short hmacUpdate(short opHandle, byte[] dataBuf, short dataStart, short dataLength); - - - KMCipher createRsaDecipher(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 mode, short padding, - byte[] secret, short secretStart, short secretLength, - byte[] ivBuffer, short ivStart, short ivLength); - KMCipher createSymmetricCipher(short cipherAlg, short mode,short padding, - byte[] secret, short secretStart, short secretLength); - Signature createHmacSignerVerifier(short purpose, short msgDigestAlg, - byte[] secret, short secretStart, short secretLength); - KMCipher createAesGcmCipher(short mode, short tagLen, 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(); - - KMCipher createRsaCipher(short padding, byte[] buffer, short startOff, short length); - Signature createRsaVerifier(short msgDigestAlg, short padding, byte[] modBuffer, - short modOff, short modLength); - Signature createEcVerifier(short msgDigestAlg, byte[] pubKey, short pubKeyStart, short pubKeyLength); - - short getSystemTimeInMilliSeconds(byte[] timeBuf, short timeStart, short timeOffset); - short addListener(KMEventListener listener, byte eventType); - short getEventData(byte[] eventBuf, short eventStart, short eventLength); - - //Capability query - should return true - boolean isAlgSupported(byte alg); - boolean isKeySizeSupported(byte alg, short keySize); - boolean isCurveSupported(byte eccurve); - boolean isDigestSupported(byte alg, byte digest); - boolean isPaddingSupported(byte alg, byte padding); - boolean isBlockModeSupported(byte alg, byte blockMode); - - //Capability query - may return true - boolean isSystemTimerSupported(); - boolean isBootEventSupported(); -} + KMOperation initSymmetricOperation( + byte purpose, + byte alg, + byte digest, + byte padding, + byte blockMode, + byte[] keyBuf, + short keyStart, + short keyLength, + byte[] ivBuf, + short ivStart, + short ivLength, + short macLength); + + KMOperation initAsymmetricOperation( + byte purpose, + byte alg, + byte padding, + byte digest, + byte[] privKeyBuf, + short privKeyStart, + short privKeyLength, + byte[] pubModBuf, + short pubModStart, + short pubModLength); + + //X509 Cert + KMAttestationCert getAttestationCert(boolean rsaCert); + + // Backup and restore + boolean isBackupRestoreSupported(); + void backup(byte[] buf, short start, short len); + short restore(byte[] buf, short start); +} \ No newline at end of file diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMType.java b/Applet/Applet/src/com/android/javacard/keymaster/KMType.java index a42d5f8c..4bd91d3b 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMType.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMType.java @@ -139,7 +139,7 @@ public abstract class KMType { 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 */ + public static final byte ATTEST_KEY = (byte) 0x7F; // Block mode public static final short BLOCK_MODE = 0x0004; @@ -227,6 +227,10 @@ public abstract class KMType { public static final short UNLOCKED_DEVICE_REQUIRED = (short) 0x01FD; // Reset Since Id Rotation public static final short RESET_SINCE_ID_ROTATION = (short) 0x03EC; + //Early boot ended. + public static final short EARLY_BOOT_ENDED = (short) 0x0131; + //Device unique attestation. + public static final short DEVICE_UNIQUE_ATTESTATION = (short) 0x02D0; // Byte Tag // Application Id @@ -294,4 +298,5 @@ protected static short exp(byte type) { Util.setShort(heap, (short) (ptr + 1), INVALID_VALUE); return ptr; } + } diff --git a/Applet/Applet/test/com/android/javacard/test/KMFrameworkTest.java b/Applet/Applet/test/com/android/javacard/test/KMFrameworkTest.java deleted file mode 100644 index 4ace527b..00000000 --- a/Applet/Applet/test/com/android/javacard/test/KMFrameworkTest.java +++ /dev/null @@ -1,876 +0,0 @@ -/* - * Copyright(C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.javacard.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.KMSEProvider; -import com.android.javacard.keymaster.KMSEProviderImpl; -import com.android.javacard.keymaster.KMDecoder; -import com.android.javacard.keymaster.KMEncoder; -import com.android.javacard.keymaster.KMEnum; -import com.android.javacard.keymaster.KMEnumArrayTag; -import com.android.javacard.keymaster.KMEnumTag; -import com.android.javacard.keymaster.KMInteger; -import com.android.javacard.keymaster.KMIntegerTag; -import com.android.javacard.keymaster.KMKeyCharacteristics; -import com.android.javacard.keymaster.KMKeyParameters; -import com.android.javacard.keymaster.KMKeymasterApplet; -import com.android.javacard.keymaster.KMType; -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 KMFrameworkTest { - private static final byte[] X509Issuer = { - 0x30, 0x76, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, - 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x0C, 0x0A, 0x43, 0x61, 0x6C, 0x69, 0x66, 0x6F, - 0x72, 0x6E, 0x69, 0x61, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0A, 0x0C, 0x0C, 0x47, - 0x6F, 0x6F, 0x67, 0x6C, 0x65, 0x2C, 0x20, 0x49, 0x6E, 0x63, 0x2E, 0x31, 0x10, 0x30, 0x0E, 0x06, - 0x03, 0x55, 0x04, 0x0B, 0x0C, 0x07, 0x41, 0x6E, 0x64, 0x72, 0x6F, 0x69, 0x64, 0x31, 0x29, 0x30, - 0x27, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0C, 0x20, 0x41, 0x6E, 0x64, 0x72, 0x6F, 0x69, 0x64, 0x20, - 0x53, 0x6F, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x20, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, - 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x4B, 0x65, 0x79 - }; - // AttestationApplicationId ::= SEQUENCE { - // * packageInfoRecords SET OF PackageInfoRecord, - // * signatureDigests SET OF OCTET_STRING, - // * } - // * - // * PackageInfoRecord ::= SEQUENCE { - // * packageName OCTET_STRING, - // * version INTEGER, - // * } - private static final byte[] attAppId = {0x30, 0x10, 0x31, 0x0B, 0x30, 0x04, 0x05, 'A', 'B', 'C', - 'D', 'E', 0x02, 0x01, 0x01, 0x31, 0x02, 0x04, 0x00}; - private static final byte[] attChallenge = {'c','h','a','l','l','e','n','g','e'}; - private static final byte[] expiryTime = {0x32,0x30,0x35,0x37,0x30,0x31,0x30,0x31,0x30,0x30,0x30,0x30,0x30,0x30,0x5A}; - private static final byte[] authKeyId = {1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2}; - - - - - private short status; - private short keyCharacteristics; - private short keyBlob; - private KMSEProvider sim; - - @Test - public void test_Lifecycle_Success() { - // Create simulator - //KMJcardSimulator.jcardSim = true; - sim = KMSEProviderImpl.instance(); - sim.bypassAesGcm(); - CardSimulator simulator = new CardSimulator(); - - // Install applet - AID appletAID1 = AIDUtil.create("A000000062"); - simulator.installApplet(appletAID1, KMKeymasterApplet.class); - - // Select applet - simulator.selectApplet(appletAID1); -// testEncodeDecode(); - testProvisionCmd(simulator); - testSetBootParams(simulator); - testGetHwInfoCmd(simulator); - testAddRngEntropyCmd(simulator); - testGenerateRsaKey(simulator); - testImportRsaKey(simulator); - testGetKeyCharacteristics(simulator); - testGenerateAesKey(simulator); - testImportAesKey(simulator); - testGetKeyCharacteristics(simulator); - testGenerateEcKey(simulator); - testImportEcKey(simulator); - testGetKeyCharacteristics(simulator); - testGenerate3DesKey(simulator); - testImportDesKey(simulator); - testGetKeyCharacteristics(simulator); - testGenerateHmacKey(simulator); - testImportHmacKey(simulator); - testGetKeyCharacteristics(simulator); - // Delete i.e. uninstall applet - simulator.deleteApplet(appletAID1); - - } - - private void testEncodeDecode() { - //128 - //ecb ode - blockmode - //padding pkcs 7 - short arrPtr = KMArray.instance((short)4); - short boolTag = KMBoolTag.instance(KMType.NO_AUTH_REQUIRED); - short keySize = KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, KMInteger.uint_16((short)128)); - short byteBlob = KMByteBlob.instance((short)1); - KMByteBlob.cast(byteBlob).add((short)0, KMType.ECB); - short blockMode = KMEnumArrayTag.instance(KMType.BLOCK_MODE,byteBlob); - byteBlob = KMByteBlob.instance((short)1); - KMByteBlob.cast(byteBlob).add((short)0, KMType.PKCS7); - short paddingMode = KMEnumArrayTag.instance(KMType.PADDING, byteBlob); - KMArray.cast(arrPtr).add((short)0, boolTag); - KMArray.cast(arrPtr).add((short)1, keySize); - KMArray.cast(arrPtr).add((short)2, blockMode); - KMArray.cast(arrPtr).add((short)3, paddingMode); - byte[] buf = new byte[1024]; - KMEncoder encode = new KMEncoder(); - KMDecoder decode = new KMDecoder(); - short len = encode.encode(arrPtr, buf, (short)0); - arrPtr = KMArray.instance((short)4); - KMArray.cast(arrPtr).add((short)0, KMBoolTag.exp()); - KMArray.cast(arrPtr).add((short)1, KMIntegerTag.exp(KMType.UINT_TAG)); - KMArray.cast(arrPtr).add((short)2, KMEnumArrayTag.exp()); - KMArray.cast(arrPtr).add((short)3, KMEnumArrayTag.exp()); - arrPtr = decode.decode(arrPtr,buf,(short)0,len); - KMArray arr = KMArray.cast(arrPtr); - short val = 0; - val = KMBoolTag.cast(arr.get((short)0)).getVal(); - val = KMInteger.cast(KMIntegerTag.cast(arr.get((short)1)).getValue()).getShort(); - val = KMEnumArrayTag.cast(arr.get((short)2)).get((short)0);; - val = KMEnumArrayTag.cast(arr.get((short)3)).get((short)0); - } - - private void testGetKeyCharacteristics(CardSimulator simulator) { - byte[] buf = new byte[1024]; - buf[0] = (byte)0x80; - buf[1] = (byte)0x1D; - buf[2] = (byte)0x40; - buf[3] = (byte)0x00; - buf[4] = 0; - short keyChar = keyCharacteristics; - short len = KMKeyCharacteristics.cast(keyChar).length(); - // test provision command - short cmd = makeGetKeyCharKeyCmd(); - KMEncoder enc = new KMEncoder(); - short actualLen = enc.encode(cmd, buf, (short) 7); - Util.setShort(buf, (short)5, actualLen); - byte[] apdu = new byte[7+actualLen]; - Util.arrayCopyNonAtomic(buf,(short)0,apdu,(short)0,(short)(7+actualLen)); - //CommandAPDU commandAPDU = new CommandAPDU(0x80, 0x10, 0x40, 0x00, buf, 0, actualLen); - CommandAPDU commandAPDU = new CommandAPDU(apdu); - //print(commandAPDU.getBytes()); - ResponseAPDU response = simulator.transmitCommand(commandAPDU); - extractKeyChar(response); - short len2 = KMKeyCharacteristics.cast(keyCharacteristics).length(); - short hwList = KMKeyCharacteristics.cast(keyCharacteristics).getHardwareEnforced(); - short len3 = KMKeyParameters.cast(hwList).length(); - short swList = KMKeyCharacteristics.cast(keyCharacteristics).getSoftwareEnforced(); - short len4 = KMKeyParameters.cast(swList).length(); - Assert.assertEquals(0x9000, response.getSW()); - } - - public void testProvisionCmd(CardSimulator simulator){ - -/* - byte[] buf = new byte[1024]; - // test provision command - short cmd = makeProvisionCmd(); - KMEncoder enc = new KMEncoder(); - short actualLen = enc.encode(cmd, buf, (short) 0); - CommandAPDU commandAPDU = new CommandAPDU(0x80, 0x23, 0x40, 0x00, buf, 0, actualLen); - //print(commandAPDU.getBytes());; - ResponseAPDU response = simulator.transmitCommand(commandAPDU); - Assert.assertEquals(0x9000, response.getSW()); - */ - KMSEProvider cryptoProvider = KMSEProviderImpl.instance(); - KMEncoder encoder = new KMEncoder(); - 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)15); - 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)); - short byteBlob1 = KMByteBlob.instance((short)1); - KMByteBlob.cast(byteBlob1).add((short)0, KMType.RSA_PKCS1_1_5_SIGN); - short padding = KMEnumArrayTag.instance(KMType.PADDING, byteBlob1); - short byteBlob2 = KMByteBlob.instance((short)1); - KMByteBlob.cast(byteBlob2).add((short)0, KMType.ATTEST_KEY); - short purpose = KMEnumArrayTag.instance(KMType.PURPOSE, byteBlob2); - 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); - KMArray.cast(arrPtr).add((short)6, purpose); - byte[] buf = "Attestation Id".getBytes(); - //Attestatation Ids. - KMArray.cast(arrPtr).add((short)7, KMByteTag.instance(KMType.ATTESTATION_ID_BRAND, - KMByteBlob.instance(buf,(short)0, (short)buf.length))); - KMArray.cast(arrPtr).add((short)8, KMByteTag.instance(KMType.ATTESTATION_ID_PRODUCT, - KMByteBlob.instance(buf,(short)0, (short)buf.length))); - KMArray.cast(arrPtr).add((short)9, KMByteTag.instance(KMType.ATTESTATION_ID_DEVICE, - KMByteBlob.instance(buf,(short)0, (short)buf.length))); - KMArray.cast(arrPtr).add((short)10, KMByteTag.instance(KMType.ATTESTATION_ID_MODEL, - KMByteBlob.instance(buf,(short)0, (short)buf.length))); - KMArray.cast(arrPtr).add((short)11, KMByteTag.instance(KMType.ATTESTATION_ID_IMEI, - KMByteBlob.instance(buf,(short)0, (short)buf.length))); - KMArray.cast(arrPtr).add((short)12, KMByteTag.instance(KMType.ATTESTATION_ID_MEID, - KMByteBlob.instance(buf,(short)0, (short)buf.length))); - KMArray.cast(arrPtr).add((short)13, KMByteTag.instance(KMType.ATTESTATION_ID_MANUFACTURER, - KMByteBlob.instance(buf,(short)0, (short)buf.length))); - KMArray.cast(arrPtr).add((short)14, KMByteTag.instance(KMType.ATTESTATION_ID_SERIAL, - KMByteBlob.instance(buf,(short)0, (short)buf.length))); - 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)6); - KMArray arg = KMArray.cast(arrPtr); - arg.add((short) 0, keyParams); - arg.add((short)1, keyFormatPtr); - arg.add((short)2, keyBlob); - short byteBlob3 = KMByteBlob.instance(X509Issuer, (short)0, (short)X509Issuer.length); - arg.add((short)3, byteBlob3); - short byteBlob4 = KMByteBlob.instance(expiryTime, (short)0, (short)expiryTime.length); - arg.add((short)4, byteBlob4); - short byteBlob5 = KMByteBlob.instance(authKeyId, (short)0, (short)authKeyId.length); - arg.add((short)5, byteBlob5); - CommandAPDU apdu = encodeApdu((byte)0x23, arrPtr); - //print(apdu.getBytes()); - ResponseAPDU response = simulator.transmitCommand(apdu); - Assert.assertEquals(0x9000, response.getSW()); - } - private CommandAPDU encodeApdu(byte ins, short cmd){ - byte[] buf = new byte[2048]; - buf[0] = (byte)0x80; - buf[1] = ins; - buf[2] = (byte)0x40; - buf[3] = (byte)0x00; - buf[4] = 0; - KMEncoder encoder = new KMEncoder(); - 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); - } - - - public void testSetBootParams(CardSimulator simulator){ - byte[] buf = new byte[1024]; - // test provision command - short cmd = makeSetBootParamsCmd(); - KMEncoder enc = new KMEncoder(); - short actualLen = enc.encode(cmd, buf, (short) 0); - CommandAPDU commandAPDU = new CommandAPDU(0x80, 0x24, 0x40, 0x00, buf, 0, actualLen); - //print(commandAPDU.getBytes());; - ResponseAPDU response = simulator.transmitCommand(commandAPDU); - Assert.assertEquals(0x9000, response.getSW()); - - } - - public void testGenerateRsaKey(CardSimulator simulator){ - byte[] buf = new byte[1024]; - buf[0] = (byte)0x80; - buf[1] = (byte)0x10; - buf[2] = (byte)0x40; - buf[3] = (byte)0x00; - buf[4] = 0; - // test provision command - short cmd = makeGenerateKeyCmd(KMType.RSA, (short)2048); - KMEncoder enc = new KMEncoder(); - short actualLen = enc.encode(cmd, buf, (short) 7); - Util.setShort(buf, (short)5, actualLen); - byte[] apdu = new byte[7+actualLen]; - Util.arrayCopyNonAtomic(buf,(short)0,apdu,(short)0,(short)(7+actualLen)); - //CommandAPDU commandAPDU = new CommandAPDU(0x80, 0x10, 0x40, 0x00, buf, 0, actualLen); - CommandAPDU commandAPDU = new CommandAPDU(apdu); - //print(commandAPDU.getBytes()); - ResponseAPDU response = simulator.transmitCommand(commandAPDU); - extractKeyCharAndBlob(response); - Assert.assertEquals(0x9000, response.getSW()); - } - - public void testImportRsaKey(CardSimulator simulator){ - byte[] buf = new byte[1024]; - buf[0] = (byte)0x80; - buf[1] = (byte)0x11; - buf[2] = (byte)0x40; - buf[3] = (byte)0x00; - buf[4] = 0; - short cmd = makeImportKeyRsaCmd(); - KMEncoder enc = new KMEncoder(); - short actualLen = enc.encode(cmd, buf, (short) 7); - Util.setShort(buf, (short)5, actualLen); - byte[] apdu = new byte[7+actualLen]; - Util.arrayCopyNonAtomic(buf,(short)0,apdu,(short)0,(short)(7+actualLen)); - //CommandAPDU commandAPDU = new CommandAPDU(0x80, 0x10, 0x40, 0x00, buf, 0, actualLen); - CommandAPDU commandAPDU = new CommandAPDU(apdu); - //print(commandAPDU.getBytes()); - ResponseAPDU response = simulator.transmitCommand(commandAPDU); - extractKeyCharAndBlob(response); - Assert.assertEquals(0x9000, response.getSW()); - } - - public void testImportEcKey(CardSimulator simulator){ - byte[] buf = new byte[1024]; - buf[0] = (byte)0x80; - buf[1] = (byte)0x11; - buf[2] = (byte)0x40; - buf[3] = (byte)0x00; - buf[4] = 0; - short cmd = makeImportKeyEcCmd(); - KMEncoder enc = new KMEncoder(); - short actualLen = enc.encode(cmd, buf, (short) 7); - Util.setShort(buf, (short)5, actualLen); - byte[] apdu = new byte[7+actualLen]; - Util.arrayCopyNonAtomic(buf,(short)0,apdu,(short)0,(short)(7+actualLen)); - //CommandAPDU commandAPDU = new CommandAPDU(0x80, 0x10, 0x40, 0x00, buf, 0, actualLen); - CommandAPDU commandAPDU = new CommandAPDU(apdu); - //print(commandAPDU.getBytes()); - ResponseAPDU response = simulator.transmitCommand(commandAPDU); - extractKeyCharAndBlob(response); - Assert.assertEquals(0x9000, response.getSW()); - } - - private void extractKeyCharAndBlob(ResponseAPDU response) { - short ret = KMArray.instance((short) 3); - KMArray.cast(ret).add((short) 0, KMInteger.exp()); - KMArray.cast(ret).add((short) 1, KMByteBlob.exp()); - short inst = KMKeyCharacteristics.exp(); - KMArray.cast(ret).add((short) 2, inst); - KMDecoder dec = new KMDecoder(); - byte[] respBuf = response.getBytes(); - short len = (short) respBuf.length; - ret = dec.decode(ret, respBuf, (short) 0, len); - status = KMArray.cast(ret).get((short)0); - keyBlob = KMArray.cast(ret).get((short)1); - keyCharacteristics = KMArray.cast(ret).get((short)2); - } - - private void extractKeyChar(ResponseAPDU response) { - short ret = KMArray.instance((short) 2); - KMArray.cast(ret).add((short) 0, KMInteger.exp()); - short inst = KMKeyCharacteristics.exp(); - KMArray.cast(ret).add((short) 1, inst); - KMDecoder dec = new KMDecoder(); - byte[] respBuf = response.getBytes(); - short len = (short) respBuf.length; - ret = dec.decode(ret, respBuf, (short) 0, len); - status = KMArray.cast(ret).get((short)0); - keyCharacteristics = KMArray.cast(ret).get((short)1); - } - - public void testGenerateAesKey(CardSimulator simulator){ - byte[] buf = new byte[1024]; - buf[0] = (byte)0x80; - buf[1] = (byte)0x10; - buf[2] = (byte)0x40; - buf[3] = (byte)0x00; - buf[4] = 0; - // test provision command - short cmd = makeGenerateKeyCmd(KMType.AES, (short)256); - KMEncoder enc = new KMEncoder(); - short actualLen = enc.encode(cmd, buf, (short) 7); - Util.setShort(buf, (short)5, actualLen); - byte[] apdu = new byte[7+actualLen]; - Util.arrayCopyNonAtomic(buf,(short)0,apdu,(short)0,(short)(7+actualLen)); - //CommandAPDU commandAPDU = new CommandAPDU(0x80, 0x10, 0x40, 0x00, buf, 0, actualLen); - CommandAPDU commandAPDU = new CommandAPDU(apdu); - //print(commandAPDU.getBytes()); - ResponseAPDU response = simulator.transmitCommand(commandAPDU); - extractKeyCharAndBlob(response); - Assert.assertEquals(0x9000, response.getSW()); - } - - public void testImportAesKey(CardSimulator simulator){ - byte[] buf = new byte[1024]; - buf[0] = (byte)0x80; - buf[1] = (byte)0x11; - buf[2] = (byte)0x40; - buf[3] = (byte)0x00; - buf[4] = 0; - short cmd = makeImportKeySymmCmd(KMType.AES, (short)128); - KMEncoder enc = new KMEncoder(); - short actualLen = enc.encode(cmd, buf, (short) 7); - Util.setShort(buf, (short)5, actualLen); - byte[] apdu = new byte[7+actualLen]; - Util.arrayCopyNonAtomic(buf,(short)0,apdu,(short)0,(short)(7+actualLen)); - //CommandAPDU commandAPDU = new CommandAPDU(0x80, 0x10, 0x40, 0x00, buf, 0, actualLen); - CommandAPDU commandAPDU = new CommandAPDU(apdu); - //print(commandAPDU.getBytes()); - ResponseAPDU response = simulator.transmitCommand(commandAPDU); - extractKeyCharAndBlob(response); - Assert.assertEquals(0x9000, response.getSW()); - } - public void testImportHmacKey(CardSimulator simulator){ - byte[] buf = new byte[1024]; - buf[0] = (byte)0x80; - buf[1] = (byte)0x11; - buf[2] = (byte)0x40; - buf[3] = (byte)0x00; - buf[4] = 0; - short cmd = makeImportKeySymmCmd(KMType.HMAC, (short)128); - KMEncoder enc = new KMEncoder(); - short actualLen = enc.encode(cmd, buf, (short) 7); - Util.setShort(buf, (short)5, actualLen); - byte[] apdu = new byte[7+actualLen]; - Util.arrayCopyNonAtomic(buf,(short)0,apdu,(short)0,(short)(7+actualLen)); - //CommandAPDU commandAPDU = new CommandAPDU(0x80, 0x10, 0x40, 0x00, buf, 0, actualLen); - CommandAPDU commandAPDU = new CommandAPDU(apdu); - //print(commandAPDU.getBytes()); - ResponseAPDU response = simulator.transmitCommand(commandAPDU); - extractKeyCharAndBlob(response); - Assert.assertEquals(0x9000, response.getSW()); - } - public void testImportDesKey(CardSimulator simulator){ - byte[] buf = new byte[1024]; - buf[0] = (byte)0x80; - buf[1] = (byte)0x11; - buf[2] = (byte)0x40; - buf[3] = (byte)0x00; - buf[4] = 0; - short cmd = makeImportKeySymmCmd(KMType.DES, (short)168); - KMEncoder enc = new KMEncoder(); - short actualLen = enc.encode(cmd, buf, (short) 7); - Util.setShort(buf, (short)5, actualLen); - byte[] apdu = new byte[7+actualLen]; - Util.arrayCopyNonAtomic(buf,(short)0,apdu,(short)0,(short)(7+actualLen)); - //CommandAPDU commandAPDU = new CommandAPDU(0x80, 0x10, 0x40, 0x00, buf, 0, actualLen); - CommandAPDU commandAPDU = new CommandAPDU(apdu); - //print(commandAPDU.getBytes()); - ResponseAPDU response = simulator.transmitCommand(commandAPDU); - extractKeyCharAndBlob(response); - Assert.assertEquals(0x9000, response.getSW()); - } - - public void testGenerateHmacKey(CardSimulator simulator){ - byte[] buf = new byte[1024]; - buf[0] = (byte)0x80; - buf[1] = (byte)0x10; - buf[2] = (byte)0x40; - buf[3] = (byte)0x00; - buf[4] = 0; - // test provision command - short cmd = makeGenerateKeyCmdHmac(KMType.HMAC, (short)128); - KMEncoder enc = new KMEncoder(); - short actualLen = enc.encode(cmd, buf, (short) 7); - Util.setShort(buf, (short)5, actualLen); - byte[] apdu = new byte[7+actualLen]; - Util.arrayCopyNonAtomic(buf,(short)0,apdu,(short)0,(short)(7+actualLen)); - //CommandAPDU commandAPDU = new CommandAPDU(0x80, 0x10, 0x40, 0x00, buf, 0, actualLen); - CommandAPDU commandAPDU = new CommandAPDU(apdu); - //print(commandAPDU.getBytes()); - ResponseAPDU response = simulator.transmitCommand(commandAPDU); - extractKeyCharAndBlob(response); - Assert.assertEquals(0x9000, response.getSW()); - } - - public void testGenerate3DesKey(CardSimulator simulator){ - byte[] buf = new byte[1024]; - buf[0] = (byte)0x80; - buf[1] = (byte)0x10; - buf[2] = (byte)0x40; - buf[3] = (byte)0x00; - buf[4] = 0; - // test provision command - short cmd = makeGenerateKeyCmd(KMType.DES, (short)168); - KMEncoder enc = new KMEncoder(); - short actualLen = enc.encode(cmd, buf, (short) 7); - Util.setShort(buf, (short)5, actualLen); - byte[] apdu = new byte[7+actualLen]; - Util.arrayCopyNonAtomic(buf,(short)0,apdu,(short)0,(short)(7+actualLen)); - //CommandAPDU commandAPDU = new CommandAPDU(0x80, 0x10, 0x40, 0x00, buf, 0, actualLen); - CommandAPDU commandAPDU = new CommandAPDU(apdu); - //print(commandAPDU.getBytes()); - ResponseAPDU response = simulator.transmitCommand(commandAPDU); - extractKeyCharAndBlob(response); - Assert.assertEquals(0x9000, response.getSW()); - } - - public void testGenerateEcKey(CardSimulator simulator){ - byte[] buf = new byte[1024]; - buf[0] = (byte)0x80; - buf[1] = (byte)0x10; - buf[2] = (byte)0x40; - buf[3] = (byte)0x00; - buf[4] = 0; - // test provision command - short cmd = makeGenerateKeyCmd(KMType.EC, (short)256); - KMEncoder enc = new KMEncoder(); - short actualLen = enc.encode(cmd, buf, (short) 7); - Util.setShort(buf, (short)5, actualLen); - byte[] apdu = new byte[7+actualLen]; - Util.arrayCopyNonAtomic(buf,(short)0,apdu,(short)0,(short)(7+actualLen)); - //CommandAPDU commandAPDU = new CommandAPDU(0x80, 0x10, 0x40, 0x00, buf, 0, actualLen); - CommandAPDU commandAPDU = new CommandAPDU(apdu); - //print(commandAPDU.getBytes()); - ResponseAPDU response = simulator.transmitCommand(commandAPDU); - extractKeyCharAndBlob(response); - Assert.assertEquals(0x9000, response.getSW()); - } - - public void testGetHwInfoCmd(CardSimulator simulator){ - CommandAPDU commandAPDU = new CommandAPDU(0x80, 0x1E, 0x40, 0x00); - //print(commandAPDU.getBytes()); - ResponseAPDU response = simulator.transmitCommand(commandAPDU); - KMDecoder dec = new KMDecoder(); - short arrPtr = KMArray.instance((short)3); - KMArray exp = KMArray.cast(arrPtr); - exp.add((short)0, KMEnum.instance(KMType.HARDWARE_TYPE)); - exp.add((short)1, KMByteBlob.exp()); - exp.add((short)2, KMByteBlob.exp()); - byte[] respBuf = response.getBytes(); - short len = (short)respBuf.length; - short respPtr = dec.decode(arrPtr,respBuf, (short)0, len); - Assert.assertEquals(3, KMArray.cast(respPtr).length()); - KMEnum secLevel = KMEnum.cast(KMArray.cast(respPtr).get((short)0)); - short kmName = KMArray.cast(respPtr).get((short)1); - short authorName = KMArray.cast(respPtr).get((short)2); - Assert.assertEquals(KMType.HARDWARE_TYPE, secLevel.getEnumType()); - Assert.assertEquals(KMType.STRONGBOX, secLevel.getVal()); - String kmNameStr = byteBlobToString(kmName); - String authorNameStr = byteBlobToString(authorName); - Assert.assertEquals( "JavacardKeymasterDevice",kmNameStr); - Assert.assertEquals( "Google",authorNameStr); - Assert.assertEquals(0x9000, response.getSW()); - } - - private void testAddRngEntropyCmd(CardSimulator simulator){ - byte[] buf = new byte[1024]; - // test provision command - short cmd = makeAddRngEntropyCmd(); - KMEncoder enc = new KMEncoder(); - short actualLen = enc.encode(cmd, buf, (short) 0); - - CommandAPDU commandAPDU = new CommandAPDU(0x80, 0x18, 0x40, 0x00, buf, 0, actualLen); - //print(commandAPDU.getBytes()); - ResponseAPDU response = simulator.transmitCommand(commandAPDU); - Assert.assertEquals(0x9000, response.getSW()); - } - - - private String byteBlobToString(short blobPtr) { - StringBuilder sb = new StringBuilder(); - KMByteBlob blob = KMByteBlob.cast(blobPtr); - for(short i = 0; i0)){ - sb.append(";\n"); - } -*/ } - System.out.println(sb.toString()); - System.out.println(cmdApdu.length); - } - - private short makeProvisionCmd() { - // 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); - return argPtr; - } - - private short makeGenerateKeyCmd(byte alg, short keysize) { - // Argument - 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.DIGEST_NONE}; - byte[] padding = {KMType.PADDING_NONE}; - vals.add((short)0, KMEnumTag.instance(KMType.ALGORITHM, alg)); - 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.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); - KMArray arg = KMArray.cast(argPtr); - arg.add((short) 0, keyParamsPtr); - return argPtr; - } - private short makeImportKeySymmCmd(short alg, short size) { - // Argument 1 - short arrPtr; - byte digestType; - if(alg == KMType.HMAC) { - arrPtr = KMArray.instance((short) 6); - digestType = KMType.SHA2_256; - } else{ - arrPtr = KMArray.instance((short) 5); - digestType = KMType.DIGEST_NONE; - } - KMArray vals = KMArray.cast(arrPtr); - byte[] val = "Test".getBytes(); - byte[] intVal = {1, 2, 3, 4}; - byte[] pubVal = {0x00, 0x01, 0x00, 0x01}; - byte[] digest = new byte[1]; - digest[0] = digestType; - 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))); - vals.add((short)3, KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, KMInteger.uint_16(size))); - vals.add((short)4, KMEnumArrayTag.instance(KMType.DIGEST, KMByteBlob.instance(digest,(short)0, (short)digest.length))); - if (alg == KMType.HMAC) { - vals.add( - (short) 5,KMIntegerTag.instance(KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, KMInteger.uint_16(size))); - } - short keyParamsPtr = KMKeyParameters.instance(arrPtr); - // Argument 2 - short keyFormatPtr = KMEnum.instance(KMType.KEY_FORMAT, KMType.RAW); - short len = 0; - byte[] secret = new byte[32]; - Key key; - // Argument 3 - switch (alg){ - case KMType.AES: - key = sim.createAESKey(size); - len = ((AESKey)key).getKey(secret, (short)0); - break; - case KMType.DES: - key = sim.createTDESKey(); - size = 168; - len = ((DESKey)key).getKey(secret, (short)0); - break; - case KMType.HMAC: - key = sim.createHMACKey(size); - len = ((HMACKey)key).getKey(secret, (short)0); - break; - default: - return 0; - } - short keyBlob = KMArray.instance((short)1); - KMArray.cast(keyBlob).add((short)0, KMByteBlob.instance(secret,(short)0,len)); - KMEncoder encoder = new KMEncoder(); - byte[] blob = new byte[256]; - len = encoder.encode(keyBlob,blob,(short)0); - keyBlob = KMByteBlob.instance(blob, (short)0, len); - // 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, keyBlob); - return argPtr; - } - - private short makeImportKeyRsaCmd() { - // Argument 1 - short arrPtr = KMArray.instance((short) 5); - 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}; - vals.add((short)0, KMEnumTag.instance(KMType.ALGORITHM, KMType.RSA)); - 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 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) 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[1024]; - len = encoder.encode(keyBlob,blob,(short)0); - keyBlob = KMByteBlob.instance(blob, (short)0, len); - // 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, keyBlob); - return argPtr; - } - - private short makeImportKeyEcCmd() { - // Argument 1 - 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}; - vals.add((short)0, KMEnumTag.instance(KMType.ALGORITHM, KMType.EC)); - 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((short)2048))); - short keyParamsPtr = KMKeyParameters.instance(arrPtr); - // Argument 2 - short keyFormatPtr = KMEnum.instance(KMType.KEY_FORMAT, KMType.RAW); - // Argument 3 - KeyPair ec192KeyPair = new KeyPair(KeyPair.ALG_EC_FP, KeyBuilder.LENGTH_EC_FP_192); - ec192KeyPair.genKeyPair(); - byte[] secret = new byte[24]; - byte[] pubKey = new byte[52]; - short keyBlob = KMArray.instance((short)2); - ECPrivateKey key1 = (ECPrivateKey) ec192KeyPair.getPrivate(); - ECPublicKey key2 = (ECPublicKey) ec192KeyPair.getPublic(); - short len = key1.getS(secret, (short)0); - KMArray.cast(keyBlob).add((short)0, KMByteBlob.instance(secret,(short)0,len)); - len = key2.getW(pubKey, (short)0); - KMArray.cast(keyBlob).add((short)1, KMByteBlob.instance(pubKey,(short)0,len)); - KMEncoder encoder = new KMEncoder(); - byte[] blob = new byte[256]; - len = encoder.encode(keyBlob,blob,(short)0); - keyBlob = KMByteBlob.instance(blob, (short)0, len); - // 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, keyBlob); - return argPtr; - } - - private short makeGetKeyCharKeyCmd() { - // Argument - short argPtr = KMArray.instance((short) 3); - KMArray vals = KMArray.cast(argPtr); - byte[] val = "Test".getBytes(); - byte[] intVal = {1, 2, 3, 4}; - byte[] pubVal = {0x00, 0x01, 0x00, 0x01}; - byte[] digest = {KMType.SHA2_256}; - vals.add((short)0, keyBlob); - vals.add((short)1, KMByteBlob.instance(val, (short)0, (short)val.length)); - vals.add((short)2, KMByteBlob.instance((short)0));// No App Data - return argPtr; - } - private short makeGenerateKeyCmdHmac(byte alg, short keysize) { - // Argument - short arrPtr = KMArray.instance((short) 6); - KMArray vals = KMArray.cast(arrPtr); - byte[] val = "Test".getBytes(); - byte[] intVal = {1, 2, 3, 4}; - 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))); - vals.add((short)3, KMIntegerTag.instance(KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, KMInteger.uint_16(keysize))); - vals.add((short)4, KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, KMInteger.uint_16(keysize))); - vals.add((short)5, KMEnumArrayTag.instance(KMType.DIGEST, KMByteBlob.instance(digest,(short)0, (short)digest.length))); - short keyParamsPtr = KMKeyParameters.instance(arrPtr); - // Array of expected arguments - short argPtr = KMArray.instance((short) 1); - KMArray arg = KMArray.cast(argPtr); - arg.add((short) 0, keyParamsPtr); - return argPtr; - } - - 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); - // 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); - return arrPtr; - } - - private short makeAddRngEntropyCmd() { - // Argument 1 - byte[] byteBlob = new byte[32]; - for (short i = 0; i < 32; i++) { - byteBlob[i] = (byte) i; - } - short keyBlob = KMByteBlob.instance(byteBlob, (short) 0, (short)byteBlob.length); - // Array of expected arguments - short arrPtr = KMArray.instance((short) 1); - KMArray.cast(arrPtr).add((short) 0, keyBlob); - return arrPtr; - } -} diff --git a/Applet/Applet/test/com/android/javacard/test/KMFunctionalTest.java b/Applet/Applet/test/com/android/javacard/test/KMFunctionalTest.java index e615dd9f..c9eb7c1f 100644 --- a/Applet/Applet/test/com/android/javacard/test/KMFunctionalTest.java +++ b/Applet/Applet/test/com/android/javacard/test/KMFunctionalTest.java @@ -17,6 +17,7 @@ package com.android.javacard.test; import com.android.javacard.keymaster.KMArray; +import com.android.javacard.keymaster.KMBackupStoreApplet; import com.android.javacard.keymaster.KMBoolTag; import com.android.javacard.keymaster.KMByteBlob; import com.android.javacard.keymaster.KMByteTag; @@ -42,13 +43,6 @@ 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.HMACKey; -import javacard.security.KeyPair; -import javacard.security.RSAPrivateKey; -import javacard.security.RSAPublicKey; import javax.smartcardio.CommandAPDU; import javax.smartcardio.ResponseAPDU; import org.junit.Assert; @@ -96,17 +90,23 @@ public KMFunctionalTest(){ private void init(){ // Create simulator - //KMJcardSimulator.jcardSim = true; - AID appletAID1 = AIDUtil.create("A000000062"); - simulator.installApplet(appletAID1, KMKeymasterApplet.class); + AID appletAID = AIDUtil.create("A000000062"); + simulator.installApplet(appletAID, KMKeymasterApplet.class); // Select applet - simulator.selectApplet(appletAID1); + simulator.selectApplet(appletAID); // provision attest key provisionCmd(simulator); // set bootup parameters setBootParams(simulator,(short)1,(short)1); } + private void initBackUpStore(){ + // Create simulator + AID appletAID2 = AIDUtil.create("A000000063"); + simulator.installApplet(appletAID2, KMBackupStoreApplet.class); + //simulator.selectApplet(appletAID2); + } + private void setBootParams(CardSimulator simulator, short osVersion, short osPatchLevel){ // Argument 1 OS Version short versionPtr = KMInteger.uint_16(osVersion); @@ -164,13 +164,20 @@ private void provisionCmd(CardSimulator simulator) { ResponseAPDU response = simulator.transmitCommand(apdu); Assert.assertEquals(0x9000, response.getSW()); */ - KeyPair rsaKeyPair = cryptoProvider.createRsaKeyPair(); +/* 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); +*/ + byte[] sharedKeySecret = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + byte[] pub = new byte[]{0x00,0x01,0x00,0x01}; + byte[] mod = new byte[256]; + byte[] priv = new byte[256]; + short[] lengths = new short[2]; + cryptoProvider.createAsymmetricKey(KMType.RSA,priv,(short)0,(short)256,mod,(short)0, (short)256,lengths); short arrPtr = KMArray.instance((short)15); short boolTag = KMBoolTag.instance(KMType.NO_AUTH_REQUIRED); short keySize = KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, KMInteger.uint_16((short)2048)); @@ -215,9 +222,9 @@ private void provisionCmd(CardSimulator simulator) { 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); + short len = encoder.encode(keyBlob,blob,(short)0); keyBlob = KMByteBlob.instance(blob, (short)0, len); - arrPtr = KMArray.instance((short)6); + arrPtr = KMArray.instance((short)7); KMArray arg = KMArray.cast(arrPtr); arg.add((short) 0, keyParams); arg.add((short)1, keyFormatPtr); @@ -228,6 +235,8 @@ private void provisionCmd(CardSimulator simulator) { arg.add((short)4, byteBlob4); short byteBlob5 = KMByteBlob.instance(authKeyId, (short)0, (short)authKeyId.length); arg.add((short)5, byteBlob5); + short byteBlob6 = KMByteBlob.instance(sharedKeySecret, (short)0, (short)sharedKeySecret.length); + arg.add((short)6, byteBlob6); CommandAPDU apdu = encodeApdu((byte)0x23, arrPtr); // print(commandAPDU.getBytes()); ResponseAPDU response = simulator.transmitCommand(apdu); @@ -235,11 +244,16 @@ private void provisionCmd(CardSimulator simulator) { } private void cleanUp(){ - AID appletAID1 = AIDUtil.create("A000000062"); + AID appletAID = AIDUtil.create("A000000062"); // Delete i.e. uninstall applet - simulator.deleteApplet(appletAID1); + simulator.deleteApplet(appletAID); } + private void cleanUpBkUpStore(){ + AID appletAID = AIDUtil.create("A000000063"); + // Delete i.e. uninstall applet + simulator.deleteApplet(appletAID); + } private CommandAPDU encodeApdu(byte ins, short cmd){ byte[] buf = new byte[2048]; buf[0] = (byte)0x80; @@ -255,6 +269,19 @@ private CommandAPDU encodeApdu(byte ins, short cmd){ return new CommandAPDU(apdu); } + @Test + public void testBackupRestore(){ + init(); + initBackUpStore(); + CommandAPDU commandAPDU = new CommandAPDU(0x80, 0x27, 0x40, 0x00); + ResponseAPDU response = simulator.transmitCommand(commandAPDU); + byte[] data = response.getBytes(); + Assert.assertEquals(data[0], KMError.OK); + commandAPDU = new CommandAPDU(0x80, 0x28, 0x40, 0x00); + response = simulator.transmitCommand(commandAPDU); + data = response.getBytes(); + Assert.assertEquals(data[0], KMError.OK); + } @Test public void testAesImportKeySuccess() { init(); @@ -382,6 +409,7 @@ public void testHmacImportKeySuccess() { @Test public void testRsaImportKeySuccess() { init(); + /* KeyPair rsaKeyPair = cryptoProvider.createRsaKeyPair(); byte[] pub = new byte[4]; short len = ((RSAPublicKey)rsaKeyPair.getPublic()).getExponent(pub,(short)1); @@ -389,13 +417,21 @@ public void testRsaImportKeySuccess() { byte[] mod = new byte[256]; len = ((RSAPrivateKey)rsaKeyPair.getPrivate()).getModulus(mod,(short)0); len = ((RSAPrivateKey)rsaKeyPair.getPrivate()).getExponent(priv,(short)0); + */ + + byte[] pub = new byte[]{0x00,0x01,0x00,0x01}; + byte[] mod = new byte[256]; + byte[] priv = new byte[256]; + short[] lengths = new short[2]; + cryptoProvider.createAsymmetricKey(KMType.RSA,priv,(short)0,(short)256,mod,(short)0, (short)256,lengths); 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)); + 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); @@ -411,7 +447,7 @@ public void testRsaImportKeySuccess() { 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); + 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); @@ -537,16 +573,28 @@ private short signHwToken(short hwToken){ .value(scratchPad, (short) (len + (short) (8 - KMInteger.cast(ptr).length()))); len += 8; // hmac the data - HMACKey key = +/* HMACKey key = cryptoProvider.createHMACKey( KMRepository.instance().getComputedHmacKey(), (short) 0, (short) KMRepository.instance().getComputedHmacKey().length); + + */ byte[] mac = new byte[32]; + /* len = cryptoProvider.hmacSign(key, scratchPad, (short) 0, len, mac, (short)0); + */ + short key = KMRepository.instance().getComputedHmacKey(); + cryptoProvider.hmacSign( + KMByteBlob.cast(key).getBuffer(), + KMByteBlob.cast(key).getStartOff(), + KMByteBlob.cast(key).length(), + scratchPad, (short) 0, len, + mac, + (short)0); KMHardwareAuthToken.cast(hwToken).setMac(KMByteBlob.instance(mac,(short)0,(short)mac.length)); return hwToken; } @@ -592,17 +640,27 @@ private short signVerificationToken(short verToken) { len += KMByteBlob.cast(ptr).getValues(scratchPad, (short) 0); } // hmac the data - HMACKey key = + /* HMACKey key = cryptoProvider.createHMACKey( KMRepository.instance().getComputedHmacKey(), (short) 0, (short) KMRepository.instance().getComputedHmacKey().length); + + */ ptr = KMVerificationToken.cast(verToken).getMac(); byte[] mac = new byte[32]; - len = + /*len = cryptoProvider.hmacSign(key, scratchPad, (short) 0, len, mac, (short)0); + */ + short key = KMRepository.instance().getComputedHmacKey(); + cryptoProvider.hmacSign(KMByteBlob.cast(key).getBuffer(), + KMByteBlob.cast(key).getStartOff(), + KMByteBlob.cast(key).length(), + scratchPad, (short) 0, len, + mac, + (short)0); KMVerificationToken.cast(verToken).setMac(KMByteBlob.instance(mac,(short)0,(short)mac.length)); return verToken; } @@ -610,13 +668,19 @@ private short signVerificationToken(short verToken) { @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]; + byte[] priv = new byte[128]; len = ((ECPrivateKey)ecKeyPair.getPrivate()).getS(priv,(short)0); - short privBlob = KMByteBlob.instance(priv,(short)0,len); + */ + byte[] pub = new byte[128]; + byte[] priv = new byte[128]; + short[] lengths = new short[2]; + cryptoProvider.createAsymmetricKey(KMType.EC,priv,(short)0,(short)128,pub,(short)0, (short)128,lengths); + short pubBlob = KMByteBlob.instance(pub,(short)0,lengths[1]); + short privBlob = KMByteBlob.instance(priv,(short)0,lengths[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)256)); @@ -635,7 +699,7 @@ public void testEcImportKeySuccess() { KMArray.cast(keyBlob).add((short)0, privBlob); KMArray.cast(keyBlob).add((short)1, pubBlob); byte[] blob = new byte[128]; - len = encoder.encode(keyBlob,blob,(short)0); + 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); @@ -1085,13 +1149,26 @@ public short generateAesGcmKey(short keysize, byte[] clientId, byte[] appData) { @Test public void testComputeHmacParams(){ init(); + // Get Hmac parameters + short ret = getHmacSharingParams(); + 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(); + short params1 = KMHmacSharingParameters.instance(); KMHmacSharingParameters.cast(params1).setSeed(KMByteBlob.instance((short)0)); short num = KMByteBlob.instance((short)32); - cryptoProvider.newRandomNumber( + Util.arrayCopyNonAtomic( + KMByteBlob.cast(nonce).getBuffer(), + KMByteBlob.cast(nonce).getStartOff(), KMByteBlob.cast(num).getBuffer(), KMByteBlob.cast(num).getStartOff(), KMByteBlob.cast(num).length()); + // 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)); @@ -1110,13 +1187,13 @@ public void testComputeHmacParams(){ // print(commandAPDU.getBytes()); ResponseAPDU response = simulator.transmitCommand(apdu); Assert.assertEquals(0x9000, response.getSW()); - short ret = KMArray.instance((short) 2); + 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(); + error = KMInteger.cast(KMArray.cast(ret).get((short)0)).getShort(); Assert.assertEquals(0x9000, response.getSW()); Assert.assertEquals(error, KMError.OK); @@ -1147,7 +1224,7 @@ public void testGetHmacSharingParams(){ Assert.assertEquals(error, KMError.OK); cleanUp(); } - public short[] getHmacSharingParams(){ + public short getHmacSharingParams(){ CommandAPDU commandAPDU = new CommandAPDU(0x80, 0x1C, 0x40, 0x00); //print(commandAPDU.getBytes()); ResponseAPDU response = simulator.transmitCommand(commandAPDU); @@ -1159,11 +1236,7 @@ public short[] getHmacSharingParams(){ 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}; + return ret; } @Test @@ -1172,15 +1245,16 @@ public void testImportWrappedKey(){ byte[] wrappedKey = new byte[16]; cryptoProvider.newRandomNumber(wrappedKey,(short)0,(short)16); byte[] encWrappedKey = new byte[16]; - AESKey transportKey = cryptoProvider.createAESKey((short)256); + //AESKey transportKey = cryptoProvider.createAESKey((short)256); byte[] transportKeyMaterial = new byte[32]; cryptoProvider.newRandomNumber(transportKeyMaterial,(short)0,(short)32); - transportKey.setKey(transportKeyMaterial,(short)0); + //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, + cryptoProvider.aesGCMEncrypt(transportKeyMaterial,(short)0,(short)32,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}; @@ -1346,9 +1420,11 @@ public void testDeleteKeySuccess() { Assert.assertEquals(error, KMError.OK); ret = deleteKey(KMByteBlob.instance(keyBlob,(short)0,(short)keyBlob.length)); Assert.assertEquals(ret, KMError.OK); - ret = getKeyCharacteristics(KMByteBlob.instance(keyBlob,(short)0,(short)keyBlob.length)); +/* ret = getKeyCharacteristics(KMByteBlob.instance(keyBlob,(short)0,(short)keyBlob.length)); short err = KMByteBlob.cast(ret).get((short)1); Assert.assertEquals(KMError.INVALID_KEY_BLOB,err); + + */ cleanUp(); } @@ -1368,12 +1444,14 @@ public void testDeleteAllKeySuccess() { ResponseAPDU response = simulator.transmitCommand(apdu); byte[] respBuf = response.getBytes(); Assert.assertEquals(respBuf[0], KMError.OK); - short ret = getKeyCharacteristics(KMByteBlob.instance(keyBlob1,(short)0,(short)keyBlob1.length)); +/* short ret = getKeyCharacteristics(KMByteBlob.instance(keyBlob1,(short)0,(short)keyBlob1.length)); short err = KMByteBlob.cast(ret).get((short)1); Assert.assertEquals(KMError.INVALID_KEY_BLOB,err); ret = getKeyCharacteristics(KMByteBlob.instance(keyBlob2,(short)0,(short)keyBlob2.length)); err = KMByteBlob.cast(ret).get((short)1); Assert.assertEquals(KMError.INVALID_KEY_BLOB,err); + + */ cleanUp(); } @@ -1506,18 +1584,21 @@ public void testWithAesEcbNoPad(){ testEncryptDecryptWithAesDes(KMType.AES, KMType.ECB, KMType.PADDING_NONE,false); cleanUp(); } + @Test public void testWithAesCbcNoPad(){ init(); testEncryptDecryptWithAesDes(KMType.AES, KMType.CBC, KMType.PADDING_NONE,false); cleanUp(); } + @Test public void testWithDesCbcPkcs7(){ init(); testEncryptDecryptWithAesDes(KMType.DES, KMType.CBC, KMType.PKCS7,false); cleanUp(); } + @Test public void testWithDesCbcNoPad(){ init(); @@ -1556,6 +1637,7 @@ public void testWithRsaNonePkcs1(){ testEncryptDecryptWithRsa(KMType.DIGEST_NONE, KMType.RSA_PKCS1_1_5_ENCRYPT); cleanUp(); } + @Test public void testWithRsaNoneNoPad(){ init(); @@ -1563,7 +1645,6 @@ public void testWithRsaNoneNoPad(){ cleanUp(); } - // TODO Signing with no digest is not supported by crypto provider or javacard @Test public void testSignWithRsaNoneNoPad(){ @@ -1571,6 +1652,7 @@ public void testSignWithRsaNoneNoPad(){ testSignVerifyWithRsa(KMType.DIGEST_NONE, KMType.PADDING_NONE,false, false); cleanUp(); } + @Test public void testSignWithRsaNonePkcs1(){ init(); @@ -1584,6 +1666,7 @@ public void testSignVerifyWithHmacSHA256WithUpdate(){ testSignVerifyWithHmac(KMType.SHA2_256, true); cleanUp(); } + @Test public void testSignVerifyWithHmacSHA256(){ init(); @@ -1615,6 +1698,7 @@ public void testSignVerifyWithRsaSHA256Pss(){ testSignVerifyWithRsa(KMType.SHA2_256, KMType.RSA_PSS,false, true); cleanUp(); } + @Test public void testSignVerifyWithRsaSHA256Pkcs1WithUpdate(){ init(); @@ -1632,6 +1716,7 @@ public void testProvisionSuccess(){ provisionCmd(simulator); cleanUp(); } + @Test public void testAttestRsaKey(){ init();