From 2c43e5e739e8192ed9cc04db598e54d96466f7b4 Mon Sep 17 00:00:00 2001 From: cpathak Date: Mon, 24 Aug 2020 13:48:27 -0700 Subject: [PATCH 1/2] Bugfixes. Refactoring and Optimizations First set of refactoring that changes KMSEProvider and KMOperationalState. Removed some redundant code from KeymasterApplet. Bug fix in processComputeHmacKey method. --- .../android/javacard/keymaster/KMCipher.java | 7 + .../javacard/keymaster/KMCipherImpl.java | 46 +- .../javacard/keymaster/KMJcardSimulator.java | 578 +++++---- .../javacard/keymaster/KMOperationImpl.java | 60 + .../keymaster/KMRsa2048NoDigestSignature.java | 9 +- .../javacard/keymaster/KMCipherImpl.java | 57 - .../javacard/keymaster/KMSimulator.java | 162 +-- .../javacard/keymaster/KMKeymasterApplet.java | 1100 ++++++----------- .../javacard/keymaster/KMOperation.java | 17 + .../javacard/keymaster/KMOperationState.java | 312 ++--- .../javacard/keymaster/KMRepository.java | 119 +- .../javacard/keymaster/KMSEProvider.java | 249 ++-- .../android/javacard/keymaster/KMType.java | 2 +- .../javacard/keymaster/KMX509Certificate.java | 8 +- .../javacard/test/KMFrameworkTest.java | 876 ------------- .../javacard/test/KMFunctionalTest.java | 111 +- 16 files changed, 1379 insertions(+), 2334 deletions(-) rename Applet/Applet/{src => JCardSimProvider}/com/android/javacard/keymaster/KMCipher.java (94%) create mode 100644 Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMOperationImpl.java delete mode 100644 Applet/Applet/OracleSimProvider/com/android/javacard/keymaster/KMCipherImpl.java create mode 100644 Applet/Applet/src/com/android/javacard/keymaster/KMOperation.java delete mode 100644 Applet/Applet/test/com/android/javacard/test/KMFrameworkTest.java 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..cf3c6759 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,11 +67,10 @@ public short doFinal(byte[] buffer, short startOff, short length, byte[] scratch e.printStackTrace(); CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); } - } - else{ + } else{ 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) { + if (cipherAlg == KMType.RSA && padding == KMType.PADDING_NONE && mode == Cipher.MODE_DECRYPT && len < 256) { byte[] tempBuf = new byte[256]; Util.arrayFillNonAtomic(tempBuf, (short) 0, (short) 256, (byte) 0); Util.arrayCopyNonAtomic(scratchPad, (short) 0, tempBuf, (short) (i + 256 - len), len); @@ -106,13 +94,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 +126,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() { diff --git a/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMJcardSimulator.java b/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMJcardSimulator.java index 72b6f5d9..05ff228c 100644 --- a/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMJcardSimulator.java +++ b/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMJcardSimulator.java @@ -39,11 +39,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; @@ -98,14 +98,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 +116,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 +131,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 +154,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 +162,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 +170,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 +182,88 @@ 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[] 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 +274,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 +292,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 +304,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 +367,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 +386,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 +398,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 { @@ -444,18 +493,16 @@ 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) { // 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); + AESKey key = (AESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_AES, KeyBuilder.LENGTH_AES_256, false); key.setKey(keyMaterial, (short) 0); prf.init(key, Signature.MODE_SIGN); byte i =1; @@ -465,7 +512,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++; } @@ -478,13 +525,12 @@ public short cmacKdf(byte[] keyMaterial, byte[] label, byte[] context, short con 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 +539,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) { - + removeOperation(opHandle); } @Override - public short hmacInit(byte[] keyBuf, short keyStart, short keyLength, byte digest, byte mode) { - 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 short hmacSign(short opHandle, byte[] data, short dataStart, short dataLength, byte[] mac, short macStart) { - return 0; + public void getAESGCMOutputSize(short opHandle, short dataSize, short macLength) { + KMCipher aesGcm = (KMCipher) findOperation(opHandle); + aesGcm.getAesGcmOutputSize(dataSize, macLength); } + */ - @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) { - 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 +727,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 +735,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 +764,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 +776,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 +813,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 +828,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 +855,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 +971,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 +998,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 +1008,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 +1026,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 +1035,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 +1064,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 +1075,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 +1175,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 +1191,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 +1213,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); 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..d07cb6ed 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,21 @@ 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[] 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 +192,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 +221,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 +260,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 +288,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); @@ -309,11 +325,6 @@ public short aesCCMSign( 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 +338,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 +354,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,12 +361,10 @@ 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; } @@ -367,12 +374,10 @@ public short cmacKdf(byte[] keyMaterial, byte[] label, byte[] context, short con 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 +393,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) { + 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 short hmacSign(short opHandle, byte[] data, short dataStart, short dataLength, byte[] mac, short macStart) { + 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 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) { - 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,24 +491,6 @@ private void incrementCounter() { } } } - @Override - public void bypassAesGcm(){ - jcardSim = true; - } - - @Override - public KMCipher createRsaCipher(short padding, byte[] modBuffer, short modOff, short modLength) { - return null; - } - - @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) { - return null; - } @Override public short getSystemTimeInMilliSeconds(byte[] timeBuf, short timeStart, short timeOffset) { diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java b/Applet/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java index 32eda210..885b7382 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java @@ -23,16 +23,7 @@ import javacard.framework.ISOException; import javacard.framework.JCSystem; import javacard.framework.Util; -import javacard.security.AESKey; import javacard.security.CryptoException; -import javacard.security.DESKey; -import javacard.security.ECPrivateKey; -import javacard.security.ECPublicKey; -import javacard.security.HMACKey; -import javacard.security.KeyPair; -import javacard.security.MessageDigest; -import javacard.security.RSAPrivateKey; -import javacard.security.Signature; import javacardx.apdu.ExtendedLength; /** @@ -148,6 +139,7 @@ public class KMKeymasterApplet extends Applet implements AppletEvent, ExtendedLe // 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 +157,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; @@ -177,7 +169,7 @@ public class KMKeymasterApplet extends Applet implements AppletEvent, ExtendedLe /** 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,18 +180,18 @@ 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), + Util.arrayCopyNonAtomic(seProvider.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); + seProvider.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); + //repository.initHmacSharedSecretKey(buf, (short) 0, 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); + seProvider.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); + //seProvider.newRandomNumber(buf, (short) 0, repository.HMAC_SEED_NONCE_SIZE); //repository.initHmacSeed(buf, repository.HMAC_SEED_NONCE_SIZE); KMType.initialize(); encoder = new KMEncoder(); @@ -273,7 +265,6 @@ public void process(APDU apdu) { return; } } - // Read the apdu header and buffer. byte[] apduBuffer = apdu.getBuffer(); byte apduClass = apduBuffer[ISO7816.OFFSET_CLA]; @@ -401,6 +392,7 @@ private void freeOperations(){ } } } + private void processEarlyBootEndedCmd(APDU apdu) { KMException.throwIt(KMError.UNIMPLEMENTED); } @@ -508,7 +500,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 +511,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 +601,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() != repository.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; @@ -654,6 +658,7 @@ private byte mapToAttId(short attTag){ 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); @@ -762,57 +767,88 @@ 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; + + 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){ + //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; + } + } + 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[6] = seProvider.cmacKdf( + repository.getSharedKey(), 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)); @@ -874,36 +910,6 @@ 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); - } - } - } - tmpVariables[0] = - KMIntegerTag.getValue( - scratchPad, (short) 0, KMType.UINT_TAG, KMType.OS_PATCH_LEVEL, data[HW_PARAMETERS]); - if ((tmpVariables[0] != KMType.INVALID_VALUE) - && (Util.arrayCompare(repository.osPatch, (short) 0, scratchPad, (short) 0, tmpVariables[0]) - != 0)) { - if (Util.arrayCompare(repository.osPatch, (short) 0, scratchPad, (short) 0, tmpVariables[0]) - < 0) { - // If the key characteristics has os patch level > current os patch - KMException.throwIt(KMError.INVALID_ARGUMENT); - } - }*/ // remove Auth Tag if(tmpVariables[5] != KMError.INVALID_ARGUMENT) { repository.removeAuthTag(data[AUTH_TAG]); @@ -998,28 +1004,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 +1046,16 @@ 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( + + boolean verification = + 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(), @@ -1067,7 +1068,7 @@ private void processImportWrappedKeyCmd(APDU apdu) { if (verification == false) { KMException.throwIt(KMError.VERIFICATION_FAILED); } - cryptoProvider.delete(key); + //seProvider.delete(key); // Step 5 - Import decrypted key data[ORIGIN] = KMType.SECURELY_IMPORTED; @@ -1168,13 +1169,10 @@ private void processAttestKeyCmd(APDU apdu) { // 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); + + //Sign the cert - returns the length of complete cert + tmpVariables[1] = KMX509Certificate.sign(seProvider,repository.getAttKeyExponent(),(short)0, KMRepository.ATT_KEY_EXP_SIZE, + repository.getAttKeyModulus(),(short)0,KMRepository.ATT_KEY_MOD_SIZE); // Send the response back. This is slightly different we do not copy the cert blob again. // We just add CBOR encoding around it. @@ -1336,10 +1334,11 @@ 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[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()); @@ -1460,20 +1459,7 @@ private void finishEncryptOperation(KMOperationState op, byte[] scratchPad) { 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. + // 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(), @@ -1487,7 +1473,7 @@ private void finishEncryptOperation(KMOperationState op, byte[] scratchPad) { KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), scratchPad, (short)0,len); } - len = op.getCipher().doFinal( + len = op.getOperation().finish( scratchPad, (short)0,len, KMByteBlob.cast(data[OUTPUT_DATA]).getBuffer(), KMByteBlob.cast(data[OUTPUT_DATA]).getStartOff()); break; @@ -1498,9 +1484,9 @@ private void finishEncryptOperation(KMOperationState op, byte[] scratchPad) { } 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) && @@ -1532,12 +1518,12 @@ private void finishEncryptOperation(KMOperationState op, byte[] scratchPad) { 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()); + + len = 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; } } @@ -1552,11 +1538,11 @@ 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: @@ -1566,16 +1552,16 @@ private void finishDecryptOperation(KMOperationState op, byte[] scratchPad) { } 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); 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 @@ -1616,8 +1602,8 @@ 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()); } @@ -1627,6 +1613,7 @@ private void updateAesGcmOperation(KMOperationState op, APDU apdu) { updateAAD(op, (byte) 0x00); // Now handle the input data tmpVariables[0] = KMByteBlob.cast(data[INPUT_DATA]).length(); + data[OUTPUT_DATA] = KMByteBlob.instance(KMByteBlob.cast(data[INPUT_DATA]).length()); // If the input data is non zero length if (tmpVariables[0] > 0) { // input data must be block aligned. @@ -1637,70 +1624,20 @@ private void updateAesGcmOperation(KMOperationState op, APDU apdu) { 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]; + try { + // allocate output data buffer as input data is always block aligned. + 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); } - 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()) { + // 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(), @@ -1710,6 +1647,8 @@ private void updateAesGcmOperation(KMOperationState op, APDU apdu) { tmpVariables[0]); } } + //Persist if there are any updates. + op.persist(); // make response tmpVariables[1] = KMArray.instance((short) 0); tmpVariables[1] = KMKeyParameters.instance(tmpVariables[1]); @@ -1726,45 +1665,16 @@ private void updateAesGcmOperation(KMOperationState op, APDU 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)){ + // Check if there at least MAC Length length of data + if((tmpVariables[0] < (short)(op.getMacLength()/8)) && (op.getPurpose()==KMType.DECRYPT)){ 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))); - } - */ + // Get the output size - in case of JCardSim this will more then input size + tmpVariables[0] = op.getOperation().getAESGCMOutputSize(KMByteBlob.cast(data[INPUT_DATA]).length(), + (short)(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( + tmpVariables[0] = op.getOperation().finish( KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), KMByteBlob.cast(data[INPUT_DATA]).length(), @@ -1773,60 +1683,47 @@ private void finishAesGcmOperation(KMOperationState op, byte[] scratchPad) { 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) { + //TODO Remove purpose instead use op.getPurpose() 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; + purpose = KMType.ENCRYPT; if (data[IV] == KMType.INVALID_VALUE) { data[IV] = KMByteBlob.instance((short) 12); - cryptoProvider.newRandomNumber( + seProvider.newRandomNumber( KMByteBlob.cast(data[IV]).getBuffer(), KMByteBlob.cast(data[IV]).getStartOff(), KMByteBlob.cast(data[IV]).length()); } } else { - purpose = KMCipher.MODE_DECRYPT; + purpose = KMType.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(), + //TODO Convert CryptoException in process method + op.setOperation(seProvider.initSymmetricOperation( + (byte)purpose,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())); + 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); } - //data[OP_HANDLE] = op.getHandle(); } private void finishSigningVerifyingOperation(KMOperationState op, byte[]scratchPad) { @@ -1837,59 +1734,14 @@ private void finishSigningVerifyingOperation(KMOperationState op, byte[]scratchP // 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(op.getPurpose() == KMType.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(), @@ -1911,14 +1763,14 @@ private void finishSigningVerifyingOperation(KMOperationState op, byte[]scratchP 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()); + len = op.getOperation().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); } }else{ - if(!op.getSignerVerifier().verify(scratchPad,(short)0,len, + if(!op.getOperation().verify(scratchPad,(short)0,len, KMByteBlob.cast(data[SIGNATURE]).getBuffer(), KMByteBlob.cast(data[SIGNATURE]).getStartOff(), KMByteBlob.cast(data[SIGNATURE]).length())){ @@ -1936,13 +1788,13 @@ private void finishSigningVerifyingOperation(KMOperationState op, byte[]scratchP } 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(), @@ -1961,9 +1813,10 @@ private void finishSigningVerifyingOperation(KMOperationState op, byte[]scratchP 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(), + len = op.getOperation().sign(KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), KMByteBlob.cast(data[INPUT_DATA]).getStartOff(),len,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 +1851,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 +1862,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,6 +1870,7 @@ 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(); @@ -2114,34 +1960,18 @@ 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(); boolean verified = - cryptoProvider.hmacVerify(key, scratchPad, (short) 0, len, + 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()); if(!verified){ KMException.throwIt(KMError.VERIFICATION_FAILED); } - /* - - // Compare mac. - ptr = KMVerificationToken.cast(verToken).getMac(); - if (macLen != KMByteBlob.cast(ptr).length()) { - KMException.throwIt(KMError.INVALID_MAC_LENGTH); - } - if (Util.arrayCompare( - scratchPad, (short) (len+1), - KMByteBlob.cast(ptr).getBuffer(), KMByteBlob.cast(ptr).getStartOff(), macLen) != 0) { - KMException.throwIt(KMError.VERIFICATION_FAILED); - } - */ - } private void processUpdateOperationCmd(APDU apdu) { @@ -2181,11 +2011,9 @@ private void processUpdateOperationCmd(APDU apdu) { } 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; @@ -2193,7 +2021,7 @@ private void processUpdateOperationCmd(APDU apdu) { 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 // 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 @@ -2221,14 +2049,11 @@ private void processUpdateOperationCmd(APDU apdu) { //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()); + 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()); // update must fully process all of the input data if(tmpVariables[0] != KMByteBlob.cast(data[OUTPUT_DATA]).length()){ KMException.throwIt(KMError.UNKNOWN_ERROR); @@ -2296,7 +2121,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()){ @@ -2352,12 +2177,9 @@ private void beginTrustedConfirmationOperation(KMOperationState op) { if (repository.getComputedHmacKey() == null) { 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.getTrustedConfirmationSigner().update(confirmationToken,(short)0,(short)confirmationToken.length); } } @@ -2419,6 +2241,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 +2339,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); @@ -2584,45 +2389,38 @@ private void authorizeAndBeginOperation(KMOperationState op, byte[] scratchPad) } private void beginCipherOperation(KMOperationState op) { + //TODO replace padding with op.getPadding short padding; short alg = -1; + //TODO replace purpose with op.getPurpose short purpose; - if(op.getPurpose() == KMType.ENCRYPT) purpose = KMCipher.MODE_ENCRYPT; - else purpose = KMCipher.MODE_DECRYPT; + if(op.getPurpose() == KMType.ENCRYPT) purpose = KMType.ENCRYPT; + else purpose = KMType.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; + if (op.getPadding() == KMType.RSA_PKCS1_1_5_ENCRYPT) padding = KMType.RSA_PKCS1_1_5_ENCRYPT; else if(op.getPadding() == KMType.RSA_OAEP && op.getDigest() == KMType.SHA2_256){ - padding = KMCipher.PAD_PKCS1_OAEP_SHA256; + padding = KMType.RSA_OAEP; } else if (op.getPadding() == KMType.RSA_OAEP && op.getDigest() == KMType.SHA1) { - padding = KMCipher.PAD_PKCS1_OAEP; - } else padding = KMCipher.PAD_NOPAD; + padding = KMType.RSA_OAEP; + } else padding = KMType.PADDING_NONE; try { - if(purpose == KMCipher.MODE_DECRYPT){ - op.setKey( + if(purpose == KMType.DECRYPT){ + op.setOperation(seProvider.initAsymmetricOperation((byte)purpose,(byte)op.getAlgorithm(), + (byte)padding, 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)purpose, op.getAlgorithm(), (byte)padding, 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())); @@ -2638,28 +2436,28 @@ else if(op.getPadding() == KMType.RSA_OAEP && op.getDigest() == KMType.SHA2_256) } case KMType.DES: if (op.getPadding() == KMType.PADDING_NONE) { - padding = KMCipher.PAD_NOPAD; + padding = KMType.PADDING_NONE; } else { - padding = KMCipher.PAD_PKCS7; + padding = KMType.PKCS7; } if (op.getAlgorithm() == KMType.AES) { if (op.getBlockMode() == KMType.CBC) { - alg = KMCipher.ALG_AES_BLOCK_128_CBC_NOPAD; + alg = KMType.AES; if (data[IV] == KMType.INVALID_VALUE) { data[IV] = KMByteBlob.instance((short) 16); - cryptoProvider.newRandomNumber( + seProvider.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; + alg = KMType.AES; data[IV] = KMType.INVALID_VALUE; } else if (op.getBlockMode() == KMType.CTR){ - alg = KMCipher.ALG_AES_CTR; + alg = KMType.AES; if (data[IV] == KMType.INVALID_VALUE) { data[IV] = KMByteBlob.instance((short) 16); - cryptoProvider.newRandomNumber( + seProvider.newRandomNumber( KMByteBlob.cast(data[IV]).getBuffer(), KMByteBlob.cast(data[IV]).getStartOff(), KMByteBlob.cast(data[IV]).length()); @@ -2669,10 +2467,10 @@ else if(op.getPadding() == KMType.RSA_OAEP && op.getDigest() == KMType.SHA2_256) } } else if (op.getAlgorithm() == KMType.DES) { if (op.getBlockMode() == KMType.CBC) { - alg = KMCipher.ALG_DES_CBC_NOPAD; + alg = KMType.DES; if(data[IV] == KMType.INVALID_VALUE){ data[IV] = KMByteBlob.instance((short)16); // TODO 8 bytes in length - cryptoProvider.newRandomNumber( + seProvider.newRandomNumber( KMByteBlob.cast(data[IV]).getBuffer(), KMByteBlob.cast(data[IV]).getStartOff(), KMByteBlob.cast(data[IV]).length() @@ -2680,7 +2478,7 @@ else if(op.getPadding() == KMType.RSA_OAEP && op.getDigest() == KMType.SHA2_256) } } else if (op.getBlockMode() == KMType.ECB){ - alg = KMCipher.ALG_DES_ECB_NOPAD; + alg = KMType.DES; data[IV] = KMType.INVALID_VALUE; }else{ KMException.throwIt(KMError.UNSUPPORTED_BLOCK_MODE); @@ -2688,103 +2486,92 @@ else if(op.getPadding() == KMType.RSA_OAEP && op.getDigest() == KMType.SHA2_256) } else { KMException.throwIt(KMError.INCOMPATIBLE_ALGORITHM); } - 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())); + op.setOperation(seProvider.initSymmetricOperation( + (byte)purpose,(byte)alg, op.getDigest(),(byte)padding,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(), + (short) 0)); } else { - op.setCipher( - cryptoProvider.createSymmetricCipher( - alg, - purpose, - padding, - KMByteBlob.cast(data[SECRET]).getBuffer(), - KMByteBlob.cast(data[SECRET]).getStartOff(), - KMByteBlob.cast(data[SECRET]).length())); + op.setOperation(seProvider.initSymmetricOperation( + (byte)purpose,(byte)alg, op.getDigest(),(byte)padding,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) { KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM); } } } - private void beginSignVerifyOperation(KMOperationState op) { + //TODO replace padding, purpose and digest with corresponding op methods short padding; short digest; - short purpose;; - if(op.getPurpose() == KMType.SIGN) purpose = Signature.MODE_SIGN; - else purpose = Signature.MODE_VERIFY; + short purpose; + if(op.getPurpose() == KMType.SIGN) purpose = KMType.SIGN; + else purpose = KMType.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()); + if(op.getDigest() == KMType.DIGEST_NONE) digest = KMType.DIGEST_NONE; + else digest = KMType.SHA2_256; + if(op.getPadding() == KMType.PADDING_NONE) padding = KMType.PADDING_NONE; + else if(op.getPadding() == KMType.RSA_PSS) padding = KMType.RSA_PSS; + else padding = KMType.RSA_PKCS1_1_5_SIGN; 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)purpose,op.getAlgorithm(),(byte)padding,(byte)digest, + 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)purpose,op.getAlgorithm(),op.getPadding(),(byte)digest, + 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()); + if(op.getDigest() == KMType.DIGEST_NONE) digest = KMType.DIGEST_NONE; + else digest = KMType.SHA2_256; + 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)purpose,op.getAlgorithm(),op.getPadding(),(byte)digest, + 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)purpose,op.getAlgorithm(),op.getPadding(),(byte)digest, + 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 +2584,16 @@ 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()); + digest = KMType.SHA2_256; + 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); } @@ -2906,36 +2691,17 @@ 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, + 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); - } - */ } private void authorizeKeyUsageForCount() { @@ -2990,42 +2756,11 @@ private void importKey(APDU apdu, byte[] scratchPad) { if (tmpVariables[0] != KMType.INVALID_VALUE) { KMException.throwIt(KMError.INVALID_KEY_BLOB); } - // 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: @@ -3075,11 +2810,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,6 +2852,15 @@ 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. @@ -3142,11 +2881,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,6 +2897,11 @@ 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 @@ -3181,11 +2921,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,6 +2936,11 @@ 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 @@ -3221,11 +2961,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,6 +2977,11 @@ 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 @@ -3288,15 +3028,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 +3042,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 @@ -3468,38 +3209,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])){ @@ -3576,15 +3285,20 @@ 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]); } @@ -3635,9 +3349,10 @@ private static void generateAESKey(byte[] scratchPad) { validateAESKey(scratchPad); tmpVariables[0] = KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); - AESKey aesKey = cryptoProvider.createAESKey(tmpVariables[0]); - tmpVariables[0] = aesKey.getKey(scratchPad, (short) 0); - data[SECRET] = KMByteBlob.instance(scratchPad, (short) 0, tmpVariables[0]); +// 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); } @@ -3657,23 +3372,19 @@ 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]); +// 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) { @@ -3696,8 +3407,9 @@ 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); + //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); } @@ -3738,11 +3450,12 @@ private static void validateHmacKey(byte[] scratchPad) { private static void generateHmacKey(byte[] scratchPad) { validateHmacKey(scratchPad); - tmpVariables[1] = + 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); } @@ -3877,14 +3590,9 @@ 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, + seProvider.aesGCMDecrypt( + repository.getHeap(), data[DERIVED_KEY], tmpVariables[0], KMByteBlob.cast(data[SECRET]).getBuffer(), KMByteBlob.cast(data[SECRET]).getStartOff(), KMByteBlob.cast(data[SECRET]).length(), @@ -3917,20 +3625,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(), @@ -4006,7 +3709,7 @@ private static short deriveKey(byte[] scratchPad) { tmpVariables[2] = encoder.encode(tmpVariables[0], repository.getHeap(), tmpVariables[1]); // create derived key i.e. MAC tmpVariables[3] = - cryptoProvider.aesCCMSign( + seProvider.aesCCMSign( repository.getHeap(), tmpVariables[1], tmpVariables[2], @@ -4079,17 +3782,6 @@ 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; @@ -4108,29 +3800,6 @@ 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 // i.e. dividend - quotient*divisor = remainder where remainder < divisor. // so this is division by subtraction until remainder remains. @@ -4153,7 +3822,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; } @@ -4192,22 +3860,4 @@ 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/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..b2685859 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMRepository.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMRepository.java @@ -28,7 +28,7 @@ public class KMRepository { public static final short MAX_BLOB_STORAGE = 32; public static final short AES_GCM_AUTH_TAG_LENGTH = 12; public static final short MASTER_KEY_SIZE = 16; - public static final short SHARED_SECRET_KEY_SIZE = 16; + public static final short SHARED_SECRET_KEY_SIZE = 32; 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; @@ -80,6 +80,7 @@ public class KMRepository { // Operation State Table private Object[] operationStateTable; + private static short opIdCounter; // boot parameters //TODO change the following into private @@ -138,8 +139,9 @@ public KMRepository() { operationStateTable = new Object[MAX_OPS]; index = 0; while(index < MAX_OPS){ - operationStateTable[index] = new KMOperationState(); - ((KMOperationState)operationStateTable[index]).reset(); + operationStateTable[index] = new Object[]{new byte[2], + new Object[] {new byte[KMOperationState.MAX_DATA], + new Object[KMOperationState.MAX_REFS]}}; index++; } deviceLockedFlag = false; @@ -163,19 +165,94 @@ public KMRepository() { repository = this; } + public KMOperationState findOperation(short opHandle) { + short index = 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; + } + public KMOperationState reserveOperation(){ short index = 0; + byte[] opId; while(index < MAX_OPS){ - if(!((KMOperationState)operationStateTable[index]).isActive()){ - ((KMOperationState)operationStateTable[index]).activate(); - return (KMOperationState)operationStateTable[index]; + opId = (byte[])((Object[])operationStateTable[index])[0]; + if(Util.getShort(opId,(short)0) == 0){ + //Util.setShort(opId, (short)0,getOpId()); + return KMOperationState.instance(/*Util.getShort(opId,(short)0)*/getOpId(),(Object[])((Object[])operationStateTable[index])[1]); } index++; } return null; } + + 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; + //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++; + } + } + + 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 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) { @@ -185,12 +262,12 @@ public void initMasterKey(byte[] key, short len) { } } - public void initHmacSharedSecretKey(byte[] key, short len) { + public void initHmacSharedSecretKey(byte[] key, short start, short len) { if (sharedKey == null) { sharedKey = new byte[SHARED_SECRET_KEY_SIZE]; } if(len != SHARED_SECRET_KEY_SIZE) KMException.throwIt(KMError.INVALID_INPUT_LENGTH); - Util.arrayCopy(key, (short) 0, sharedKey, (short) 0, len); + Util.arrayCopy(key, start, sharedKey, (short) 0, len); } @@ -209,7 +286,7 @@ public void initHmacNonce(byte[] nonce, short offset, short len) { if (len != HMAC_SEED_NONCE_SIZE) { KMException.throwIt(KMError.INVALID_INPUT_LENGTH); } - Util.arrayCopy(nonce, (short) 0, hmacNonce, (short) 0, len); + Util.arrayCopy(nonce, offset, 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 @@ -295,10 +372,7 @@ public void persistAuthTag(short authTag) { public boolean validateAuthTag(short authTag) { KMAuthTag tag = findTag(authTag); - if(tag != null){ - return true; - } - return false; + return tag != null; } public void removeAuthTag(short authTag) { @@ -320,9 +394,9 @@ public void removeAuthTag(short authTag) { public void removeAllAuthTags() { JCSystem.beginTransaction(); - KMAuthTag tag = null; + KMAuthTag tag; short index = 0; - short i = 0; + short i; while (index < MAX_BLOB_STORAGE) { tag = (KMAuthTag) authTagRepo[index]; tag.reserved = false; @@ -340,7 +414,7 @@ public void removeAllAuthTags() { public KMAuthTag findTag(short authTag) { short index = 0; - short found = 0; + short found; while (index < MAX_BLOB_STORAGE) { if (((KMAuthTag) authTagRepo[index]).reserved) { found = @@ -376,17 +450,6 @@ public void setRateLimitedKeyCount(short authTag, short val) { 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(); diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMSEProvider.java b/Applet/Applet/src/com/android/javacard/keymaster/KMSEProvider.java index 5ef2517f..46426341 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMSEProvider.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMSEProvider.java @@ -1,37 +1,41 @@ 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 privKeyLength, + byte[] pubModBuf, + short pubModStart, + short pubModLength, + short[] lengths); + // Import key operations + boolean importSymmetricKey(byte alg, short keysize, byte[] buf, short startOff, short length); + + 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 +52,137 @@ 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 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); + byte[] bufIn, + short bufInStart, + short buffInLength, + byte[] masterKeySecret, + byte[] bufOut, + short bufStart); + + short cmacKdf( + byte[] keyMaterial, + 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); - + 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); + + // Timer API short getSystemTimeInMilliSeconds(byte[] timeBuf, short timeStart, short timeOffset); + + // Events API short addListener(KMEventListener listener, byte eventType); + short getEventData(byte[] eventBuf, short eventStart, short eventLength); - //Capability query - should return true + // 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(); } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMType.java b/Applet/Applet/src/com/android/javacard/keymaster/KMType.java index a42d5f8c..7c7036e0 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; diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMX509Certificate.java b/Applet/Applet/src/com/android/javacard/keymaster/KMX509Certificate.java index cb5c99a2..8beb36ec 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMX509Certificate.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMX509Certificate.java @@ -750,10 +750,12 @@ 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; 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..24a1c2cc 100644 --- a/Applet/Applet/test/com/android/javacard/test/KMFunctionalTest.java +++ b/Applet/Applet/test/com/android/javacard/test/KMFunctionalTest.java @@ -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); @@ -382,6 +391,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 +399,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 +429,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 +555,27 @@ 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); + */ + cryptoProvider.hmacSign( + KMRepository.instance().getComputedHmacKey(), + (short) 0, + (short) KMRepository.instance().getComputedHmacKey().length, + scratchPad, (short) 0, len, + mac, + (short)0); KMHardwareAuthToken.cast(hwToken).setMac(KMByteBlob.instance(mac,(short)0,(short)mac.length)); return hwToken; } @@ -592,31 +621,46 @@ 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); + + */ + cryptoProvider.hmacSign(KMRepository.instance().getComputedHmacKey(), + (short) 0, + (short) KMRepository.instance().getComputedHmacKey().length, + scratchPad, (short) 0, len, + mac, + (short)0); KMVerificationToken.cast(verToken).setMac(KMByteBlob.instance(mac,(short)0,(short)mac.length)); return verToken; } @Test public void testEcImportKeySuccess() { - init(); + 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 +679,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 +1129,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 +1167,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 +1204,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 +1216,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 +1225,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}; @@ -1506,18 +1560,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 +1613,7 @@ public void testWithRsaNonePkcs1(){ testEncryptDecryptWithRsa(KMType.DIGEST_NONE, KMType.RSA_PKCS1_1_5_ENCRYPT); cleanUp(); } + @Test public void testWithRsaNoneNoPad(){ init(); @@ -1563,7 +1621,6 @@ public void testWithRsaNoneNoPad(){ cleanUp(); } - // TODO Signing with no digest is not supported by crypto provider or javacard @Test public void testSignWithRsaNoneNoPad(){ @@ -1571,6 +1628,7 @@ public void testSignWithRsaNoneNoPad(){ testSignVerifyWithRsa(KMType.DIGEST_NONE, KMType.PADDING_NONE,false, false); cleanUp(); } + @Test public void testSignWithRsaNonePkcs1(){ init(); @@ -1584,6 +1642,7 @@ public void testSignVerifyWithHmacSHA256WithUpdate(){ testSignVerifyWithHmac(KMType.SHA2_256, true); cleanUp(); } + @Test public void testSignVerifyWithHmacSHA256(){ init(); From d4f0a337818e630fd8f5ba67e2f8be1caf66043a Mon Sep 17 00:00:00 2001 From: cpathak Date: Thu, 3 Sep 2020 10:59:07 -0700 Subject: [PATCH 2/2] Optimizations for KMX509Certificate The certificate creation is moved to KMSEProvider. --- .../keymaster/KMAttestationCertImpl.java} | 423 ++++++--- .../javacard/keymaster/KMCipherImpl.java | 52 +- .../javacard/keymaster/KMJcardSimulator.java | 21 + .../javacard/keymaster/KMSimulator.java | 20 + .../javacard/keymaster/KMAttestationCert.java | 33 + .../javacard/keymaster/KMKeymasterApplet.java | 888 +++++++----------- .../javacard/keymaster/KMRepository.java | 6 +- .../javacard/keymaster/KMSEProvider.java | 29 +- .../javacard/test/KMFunctionalTest.java | 8 +- 9 files changed, 813 insertions(+), 667 deletions(-) rename Applet/Applet/{src/com/android/javacard/keymaster/KMX509Certificate.java => JCardSimProvider/com/android/javacard/keymaster/KMAttestationCertImpl.java} (73%) create mode 100644 Applet/Applet/src/com/android/javacard/keymaster/KMAttestationCert.java diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMX509Certificate.java b/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMAttestationCertImpl.java similarity index 73% rename from Applet/Applet/src/com/android/javacard/keymaster/KMX509Certificate.java rename to Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMAttestationCertImpl.java index 8beb36ec..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 @@ -757,23 +872,63 @@ public static short sign(KMSEProvider seProv, byte[] privBuf, short privStart, s 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){ @@ -783,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/KMCipherImpl.java b/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMCipherImpl.java index cf3c6759..65ebfad0 100644 --- a/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMCipherImpl.java +++ b/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMCipherImpl.java @@ -68,14 +68,61 @@ public short doFinal(byte[] buffer, short startOff, short length, byte[] scratch CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); } } 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 == KMType.RSA && padding == KMType.PADDING_NONE && 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; } @@ -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 05ff228c..0d4ec5d3 100644 --- a/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMJcardSimulator.java +++ b/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMJcardSimulator.java @@ -247,6 +247,11 @@ public boolean importSymmetricKey(byte alg, short keysize, byte[] buf, short sta 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){ @@ -1285,6 +1290,22 @@ public boolean isBootEventSupported() { return false; } + @Override + public boolean isPkcs8ParsingSupported() { + return false; + } + + @Override + public boolean isAttestationCertSupported() { + return true; + } + + @Override + public KMAttestationCert getAttestationCert(boolean rsaCert) { + //certBuilder.reset(); + return KMAttestationCertImpl.instance(rsaCert); + } + /* private static void print (String lab, byte[] b, short s, short l){ byte[] i = new byte[l]; diff --git a/Applet/Applet/OracleSimProvider/com/android/javacard/keymaster/KMSimulator.java b/Applet/Applet/OracleSimProvider/com/android/javacard/keymaster/KMSimulator.java index d07cb6ed..8d8d215e 100644 --- a/Applet/Applet/OracleSimProvider/com/android/javacard/keymaster/KMSimulator.java +++ b/Applet/Applet/OracleSimProvider/com/android/javacard/keymaster/KMSimulator.java @@ -161,6 +161,11 @@ public boolean importSymmetricKey(byte alg, short keysize, byte[] buf, short sta 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; @@ -547,4 +552,19 @@ public boolean isBootEventSupported() { return false; } + @Override + public boolean isPkcs8ParsingSupported() { + return false; + } + + @Override + public boolean isAttestationCertSupported() { + return false; + } + + @Override + public KMAttestationCert getAttestationCert(boolean rsaCert) { + return null; + } + } 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/KMKeymasterApplet.java b/Applet/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java index 885b7382..2d70711b 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java @@ -137,8 +137,6 @@ 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 @@ -165,6 +163,7 @@ 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() { @@ -180,19 +179,13 @@ protected KMKeymasterApplet() { repository = new KMRepository(); tmpVariables = JCSystem.makeTransientShortArray((short) TMP_VARIABLE_ARRAY_SIZE, JCSystem.CLEAR_ON_RESET); - Util.arrayCopyNonAtomic(seProvider.getTrueRandomNumber(repository.MASTER_KEY_SIZE), - (short) 0, buf, (short) 0, repository.MASTER_KEY_SIZE); - repository.initMasterKey(buf, repository.MASTER_KEY_SIZE); - seProvider.newRandomNumber(buf, (short) 0, repository.SHARED_SECRET_KEY_SIZE); - // TODO remove this when key agreement protocol is implemented. - //repository.initHmacSharedSecretKey(buf, (short) 0, 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. - seProvider.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. - //seProvider.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(); @@ -279,12 +272,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)) { 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); @@ -374,7 +365,7 @@ public void process(APDU apdu) { } } catch (KMException exception) { freeOperations(); - sendError(apdu, exception.reason); + sendError(apdu, KMException.reason); exception.clear(); } catch(ISOException exp){ freeOperations(); @@ -416,8 +407,7 @@ private void processDeviceLockedCmd(APDU apdu) { 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; + repository.deviceUnlockPasswordOnly = (tmpVariables[1] == 0x01); Util.arrayCopy(scratchPad,(short)0,repository.deviceLockedTimestamp,(short)0,(short)repository.deviceLockedTimestamp.length); } sendError(apdu,KMError.OK); @@ -486,9 +476,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()); @@ -604,7 +591,7 @@ private void processProvisionCmd(APDU apdu) { // Persist Hmac Shared Key Secret tmpVariables[0] = KMArray.cast(args).get((short)6); - if(KMByteBlob.cast(tmpVariables[0]).length() != repository.SHARED_SECRET_KEY_SIZE){ + if(KMByteBlob.cast(tmpVariables[0]).length() != KMRepository.SHARED_SECRET_KEY_SIZE){ KMException.throwIt(KMError.INVALID_ARGUMENT); } repository.initHmacSharedSecretKey( @@ -639,21 +626,21 @@ 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 @@ -695,12 +682,11 @@ 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]).setSeed(KMByteBlob.instance((short)0)); // prepare the response tmpVariables[3] = KMArray.instance((short) 2); @@ -712,6 +698,7 @@ private void processGetHmacSharingParamCmd(APDU apdu) { } private void processDeleteAllKeysCmd(APDU apdu) { + // No arguments repository.removeAllAuthTags(); // Send ok @@ -719,6 +706,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); @@ -749,11 +737,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); } @@ -902,7 +889,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. @@ -910,15 +897,19 @@ private void processUpgradeKeyCmd(APDU apdu) { tmpVariables[5] = KMError.INVALID_ARGUMENT; } } - // remove Auth Tag + boolean blobPersisted = false; 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]); + if(repository.validateAuthTag(data[AUTH_TAG])){ + repository.removeAuthTag(data[AUTH_TAG]); + blobPersisted = true; + } + // 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]); + } } else { data[KEY_BLOB] = KMByteBlob.instance((short)0); } @@ -937,7 +928,7 @@ 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) { + if (repository.keyBlobCount > KMRepository.MAX_BLOB_STORAGE) { ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); } // Receive the incoming request fully from the master into buffer. @@ -958,7 +949,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 @@ -1047,8 +1037,7 @@ private void processImportWrappedKeyCmd(APDU apdu) { data[NONCE] = KMArray.cast(args).get((short) 4); Util.arrayFillNonAtomic(scratchPad,(short)0, KMByteBlob.cast(data[INPUT_DATA]).length(),(byte)0); - boolean verification = - seProvider.aesGCMDecrypt( + if(!seProvider.aesGCMDecrypt( KMByteBlob.cast(data[SECRET]).getBuffer(), KMByteBlob.cast(data[SECRET]).getStartOff(), KMByteBlob.cast(data[SECRET]).length(), @@ -1064,11 +1053,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); } - //seProvider.delete(key); // Step 5 - Import decrypted key data[ORIGIN] = KMType.SECURELY_IMPORTED; @@ -1109,24 +1096,28 @@ 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); } + cert.attestationChallenge(KMByteTag.cast(tmpVariables[0]).getValue()); // extract key characteristics //makeKeyCharacteristics(scratchPad); // 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 { @@ -1136,6 +1127,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) { @@ -1150,41 +1142,106 @@ private void processAttestKeyCmd(APDU apdu) { 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 - - //Sign the cert - returns the length of complete cert - tmpVariables[1] = KMX509Certificate.sign(seProvider,repository.getAttKeyExponent(),(short)0, KMRepository.ATT_KEY_EXP_SIZE, - repository.getAttKeyModulus(),(short)0,KMRepository.ATT_KEY_MOD_SIZE); - - // 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)); + cert.notAfter(tmpVariables[2]); +// // 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(); + 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.deviceLocked(repository.deviceLockedFlag); + cert.issuer( + KMByteBlob.instance( + repository.getCertDataBuffer(), + repository.getIssuer(), + repository.getIssuerLen())); + 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.verifiedBootHash( + KMByteBlob.instance(repository.verifiedBootHash,(short)0,(short)repository.verifiedBootHash.length)); + cert.verifiedBootKey( + KMByteBlob.instance(repository.verifiedBootKey,(short)0,(short)repository.verifiedBootKey.length)); + if(repository.selfSignedBootFlag) cert.verifiedState(KMType.SELF_SIGNED_BOOT); + else if(repository.verifiedBootFlag) cert.verifiedState(KMType.VERIFIED_BOOT); + else cert.verifiedState(KMType.UNVERIFIED_BOOT); + // 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(); +// // create X509 certificate. +// KMAttestationCertImpl.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 +// +// //Sign the cert - returns the length of complete cert +// tmpVariables[1] = KMAttestationCertImpl.sign(seProvider,repository.getAttKeyExponent(),(short)0, KMRepository.ATT_KEY_EXP_SIZE, +// repository.getAttKeyModulus(),(short)0,KMRepository.ATT_KEY_MOD_SIZE); +// +// // 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 = KMAttestationCertImpl.getBuffer(); +// // add CBOR header and elements +// bufferStartOffset = encoder.encodeCert(KMAttestationCertImpl.getBuffer(), KMAttestationCertImpl.getBufferStart(), +// KMAttestationCertImpl.getCertStart(), KMAttestationCert.getCertLength()); +// bufferLength = (short)(KMAttestationCert.getCertLength() + (KMAttestationCertImpl.getCertStart()- bufferStartOffset)); + bufferStartOffset = encoder.encodeCert(buffer, bufferStartOffset, cert.getCertStart(), cert.getCertLength()); + bufferLength = (short)(cert.getCertLength() + (cert.getCertStart()- bufferStartOffset)); sendOutgoing(apdu); } + //-------------------------------- + private void addAttestationIds(KMAttestationCert cert) { + 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; @@ -1287,7 +1344,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){ @@ -1350,14 +1407,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) { @@ -1385,7 +1440,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); @@ -1450,38 +1504,22 @@ 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); - // 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.getOperation().finish( - 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] = AES_BLOCK_SIZE; @@ -1492,39 +1530,30 @@ private void finishEncryptOperation(KMOperationState op, byte[] scratchPad) { 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); - + // 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(data[OUTPUT_DATA]).getBuffer(), - KMByteBlob.cast(data[OUTPUT_DATA]).getStartOff()); + 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; } } @@ -1546,34 +1575,31 @@ private void finishDecryptOperation(KMOperationState op, byte[] scratchPad) { 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] = AES_BLOCK_SIZE; }else{ 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.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; } @@ -1608,181 +1634,47 @@ private void updateAAD(KMOperationState op, byte finish){ 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(); - data[OUTPUT_DATA] = KMByteBlob.instance(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(); - } - try { - // allocate output data buffer as input data is always block aligned. - 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]); - 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); - tmpVariables[0] = KMByteBlob.cast(data[INPUT_DATA]).length(); - // Check if there at least MAC Length length of data - if((tmpVariables[0] < (short)(op.getMacLength()/8)) && (op.getPurpose()==KMType.DECRYPT)){ - 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(KMByteBlob.cast(data[INPUT_DATA]).length(), - (short)(op.getMacLength()/8)); - data[OUTPUT_DATA] = KMByteBlob.instance(tmpVariables[0]); - tmpVariables[0] = 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()); - if(tmpVariables[0] != KMByteBlob.cast(data[OUTPUT_DATA]).length()){ - KMException.throwIt(KMError.UNKNOWN_ERROR); - } - } - - private void beginAesGcmOperation(KMOperationState op) { - //TODO Remove purpose instead use op.getPurpose() - 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 = KMType.ENCRYPT; - if (data[IV] == KMType.INVALID_VALUE) { - data[IV] = KMByteBlob.instance((short) 12); - seProvider.newRandomNumber( - KMByteBlob.cast(data[IV]).getBuffer(), - KMByteBlob.cast(data[IV]).getStartOff(), - KMByteBlob.cast(data[IV]).length()); - } - } else { - purpose = KMType.DECRYPT; - } - op.setAesGcmUpdateStart(); - try { - //TODO [Venkat] CryptoException is not been converted to KMException here. - //TODO Convert CryptoException in process method - op.setOperation(seProvider.initSymmetricOperation( - (byte)purpose,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 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.VERIFY){ - if(len != 256) KMException.throwIt(KMError.INVALID_INPUT_LENGTH); - } - 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) { - 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.getOperation().sign(scratchPad,(short)0,len, + 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()); - } catch (CryptoException e) { - KMException.throwIt(KMError.INVALID_ARGUMENT); - } }else{ - if(!op.getOperation().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; } @@ -1810,12 +1702,13 @@ 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.getOperation().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)); @@ -1877,7 +1770,7 @@ private void authorizeUpdateFinishOperation(KMOperationState op, byte[] scratchP if (KMInteger.compare(data[OP_HANDLE], tmpVariables[0]) != 0) { KMException.throwIt(KMError.KEY_USER_NOT_AUTHENTICATED); } - authenticateUser(scratchPad); + authenticateUser(); } } @@ -1913,7 +1806,6 @@ private void authorizeDeviceUnlock(short hwToken) { 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; @@ -1928,15 +1820,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; @@ -1975,7 +1865,6 @@ private void validateVerificationToken(short verToken, byte[] scratchPad) { } private void processUpdateOperationCmd(APDU apdu) { - // TODO Add Support for AES-GCM receiveIncoming(apdu); byte[] scratchPad = apdu.getBuffer(); tmpVariables[1] = KMArray.instance((short) 5); @@ -1995,20 +1884,19 @@ 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); - } + 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.getOperation().update( KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), @@ -2017,48 +1905,66 @@ private void processUpdateOperationCmd(APDU apdu) { // 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() == 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. + 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()); - // update must fully process all of the input data - if(tmpVariables[0] != KMByteBlob.cast(data[OUTPUT_DATA]).length()){ - KMException.throwIt(KMError.UNKNOWN_ERROR); - } + } 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]); @@ -2089,7 +1995,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(); @@ -2140,7 +2046,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,7 +2256,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 @@ -2362,55 +2269,60 @@ 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) { - //TODO replace padding with op.getPadding - short padding; - short alg = -1; - //TODO replace purpose with op.getPurpose - short purpose; - if(op.getPurpose() == KMType.ENCRYPT) purpose = KMType.ENCRYPT; - else purpose = KMType.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 = KMType.RSA_PKCS1_1_5_ENCRYPT; - else if(op.getPadding() == KMType.RSA_OAEP && op.getDigest() == KMType.SHA2_256){ - padding = KMType.RSA_OAEP; - } else if (op.getPadding() == KMType.RSA_OAEP && op.getDigest() == KMType.SHA1) { - padding = KMType.RSA_OAEP; - } else padding = KMType.PADDING_NONE; try { - if(purpose == KMType.DECRYPT){ - op.setOperation(seProvider.initAsymmetricOperation((byte)purpose,(byte)op.getAlgorithm(), - (byte)padding, op.getDigest(), + 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(), @@ -2419,7 +2331,9 @@ else if(op.getPadding() == KMType.RSA_OAEP && op.getDigest() == KMType.SHA2_256) KMByteBlob.cast(data[PUB_KEY]).length())); }else{ op.setOperation( - seProvider.initAsymmetricOperation((byte)purpose, op.getAlgorithm(), (byte)padding, op.getDigest(), + 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(), @@ -2430,105 +2344,38 @@ 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 = KMType.PADDING_NONE; - } else { - padding = KMType.PKCS7; - } - if (op.getAlgorithm() == KMType.AES) { - if (op.getBlockMode() == KMType.CBC) { - alg = KMType.AES; - if (data[IV] == KMType.INVALID_VALUE) { - data[IV] = KMByteBlob.instance((short) 16); - seProvider.newRandomNumber( - KMByteBlob.cast(data[IV]).getBuffer(), - KMByteBlob.cast(data[IV]).getStartOff(), - KMByteBlob.cast(data[IV]).length()); - } - } else if (op.getBlockMode() == KMType.ECB) { - alg = KMType.AES; - data[IV] = KMType.INVALID_VALUE; - } else if (op.getBlockMode() == KMType.CTR){ - alg = KMType.AES; - if (data[IV] == KMType.INVALID_VALUE) { - data[IV] = KMByteBlob.instance((short) 16); - seProvider.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 = KMType.DES; - if(data[IV] == KMType.INVALID_VALUE){ - data[IV] = KMByteBlob.instance((short)16); // TODO 8 bytes in length - seProvider.newRandomNumber( - KMByteBlob.cast(data[IV]).getBuffer(), - KMByteBlob.cast(data[IV]).getStartOff(), - KMByteBlob.cast(data[IV]).length() - ); - } - - } else if (op.getBlockMode() == KMType.ECB){ - alg = KMType.DES; - 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(); } try { - if (data[IV] != KMType.INVALID_VALUE) { + // if (data[IV] != KMType.INVALID_VALUE) { op.setOperation(seProvider.initSymmetricOperation( - (byte)purpose,(byte)alg, op.getDigest(),(byte)padding,op.getBlockMode(), + (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(), - (short) 0)); - } else { - op.setOperation(seProvider.initSymmetricOperation( - (byte)purpose,(byte)alg, op.getDigest(),(byte)padding,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) { - KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM); + 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) { - //TODO replace padding, purpose and digest with corresponding op methods - short padding; - short digest; - short purpose; - if(op.getPurpose() == KMType.SIGN) purpose = KMType.SIGN; - else purpose = KMType.VERIFY; switch(op.getAlgorithm()){ case KMType.RSA: - if(op.getDigest() == KMType.DIGEST_NONE) digest = KMType.DIGEST_NONE; - else digest = KMType.SHA2_256; - if(op.getPadding() == KMType.PADDING_NONE) padding = KMType.PADDING_NONE; - else if(op.getPadding() == KMType.RSA_PSS) padding = KMType.RSA_PSS; - else padding = KMType.RSA_PKCS1_1_5_SIGN; try{ if (op.getPurpose() == KMType.SIGN) { op.setOperation(seProvider.initAsymmetricOperation( - (byte)purpose,op.getAlgorithm(),(byte)padding,(byte)digest, + (byte)op.getPurpose(),op.getAlgorithm(),op.getPadding(),op.getDigest(), KMByteBlob.cast(data[SECRET]).getBuffer(), KMByteBlob.cast(data[SECRET]).getStartOff(), KMByteBlob.cast(data[SECRET]).length(), @@ -2536,16 +2383,14 @@ private void beginSignVerifyOperation(KMOperationState op) { KMByteBlob.cast(data[PUB_KEY]).getStartOff(), KMByteBlob.cast(data[PUB_KEY]).length() )); - }else{ op.setOperation(seProvider.initAsymmetricOperation( - (byte)purpose,op.getAlgorithm(),op.getPadding(),(byte)digest, + (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 @@ -2554,20 +2399,16 @@ private void beginSignVerifyOperation(KMOperationState op) { } break; case KMType.EC: - if(op.getDigest() == KMType.DIGEST_NONE) digest = KMType.DIGEST_NONE; - else digest = KMType.SHA2_256; - try{ if (op.getPurpose() == KMType.SIGN) { - op.setOperation(seProvider.initAsymmetricOperation( - (byte)purpose,op.getAlgorithm(),op.getPadding(),(byte)digest, + (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.setOperation(seProvider.initAsymmetricOperation( - (byte)purpose,op.getAlgorithm(),op.getPadding(),(byte)digest, + (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(), @@ -2584,8 +2425,6 @@ 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 = KMType.SHA2_256; - try{ op.setOperation(seProvider.initSymmetricOperation( (byte)op.getPurpose(),op.getAlgorithm(),op.getDigest(),op.getPadding(),op.getBlockMode(), @@ -2604,7 +2443,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] = @@ -2617,7 +2456,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 @@ -2634,7 +2473,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(); @@ -2658,7 +2497,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; @@ -2692,8 +2531,7 @@ private boolean validateHwToken(short hwToken, byte[] scratchPad) { len += 8; // hmac the data ptr = KMHardwareAuthToken.cast(hwToken).getMac(); - boolean verified = - seProvider.hmacVerify( + return seProvider.hmacVerify( repository.getComputedHmacKey(), (short) 0, (short) repository.getComputedHmacKey().length, @@ -2701,7 +2539,7 @@ private boolean validateHwToken(short hwToken, byte[] scratchPad) { KMByteBlob.cast(ptr).getBuffer(), KMByteBlob.cast(ptr).getStartOff(), KMByteBlob.cast(ptr).length()); - return verified; + } private void authorizeKeyUsageForCount() { @@ -2723,7 +2561,7 @@ private void authorizeKeyUsageForCount() { } private void processImportKeyCmd(APDU apdu) { - if (repository.keyBlobCount > repository.MAX_BLOB_STORAGE) { + if (repository.keyBlobCount > KMRepository.MAX_BLOB_STORAGE) { ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); } // Receive the incoming request fully from the master into buffer. @@ -2756,6 +2594,13 @@ 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) { @@ -2784,8 +2629,11 @@ 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){ + 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)); @@ -2864,7 +2712,7 @@ private void importECKeys(byte[] scratchPad) { // 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]); } @@ -2905,7 +2753,7 @@ private void importHmacKey(byte[] scratchPad) { // update the key parameters list updateKeyParameters(scratchPad, tmpVariables[4]); // validate HMAC Key parameters - validateHmacKey(scratchPad); + validateHmacKey(); data[KEY_BLOB] = KMArray.instance((short) 4); } @@ -2944,7 +2792,7 @@ private void importTDESKey(byte[] scratchPad) { // update the key parameters list updateKeyParameters(scratchPad, tmpVariables[4]); // validate TDES Key parameters - validateTDESKey(scratchPad); + validateTDESKey(); data[KEY_BLOB] = KMArray.instance((short) 4); } @@ -2985,7 +2833,7 @@ private void importAESKey(byte[] scratchPad) { // update the key parameters list updateKeyParameters(scratchPad, tmpVariables[4]); // validate AES Key parameters - validateAESKey(scratchPad); + validateAESKey(); data[KEY_BLOB] = KMArray.instance((short) 4); } @@ -3092,8 +2940,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(); @@ -3135,10 +2981,10 @@ 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 @@ -3166,11 +3012,7 @@ private void processSetBootParamsCmd(APDU apdu) { 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); if (keymasterState == KMKeymasterApplet.FIRST_SELECT_STATE) { setBootParamsDone = true; if (provisionDone) { @@ -3183,7 +3025,7 @@ private void processSetBootParamsCmd(APDU apdu) { private static void processGenerateKey(APDU apdu) { // before generating key, check whether max count reached - if (repository.keyBlobCount > repository.MAX_BLOB_STORAGE) { + if (repository.keyBlobCount > KMRepository.MAX_BLOB_STORAGE) { ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); } // Receive the incoming request fully from the master into buffer. @@ -3198,6 +3040,12 @@ 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 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]); @@ -3239,8 +3087,12 @@ 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){ + 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)); @@ -3303,7 +3155,7 @@ private static void generateRSAKey(byte[] scratchPad) { 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]); @@ -3335,18 +3187,12 @@ 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 = seProvider.createAESKey(tmpVariables[0]); @@ -3356,7 +3202,7 @@ private static void generateAESKey(byte[] scratchPad) { 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]); @@ -3371,7 +3217,7 @@ private static void validateECKeys(byte[] scratchPad) { } private static void generateECKeys(byte[] scratchPad) { - validateECKeys(scratchPad); + validateECKeys(); // KeyPair ecKey = seProvider.createECKeyPair(); short[] lengths = tmpVariables; seProvider.createAsymmetricKey(KMType.EC, @@ -3387,7 +3233,7 @@ private static void generateECKeys(byte[] scratchPad) { KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_PUB_KEY, data[PUB_KEY]); } - private static void validateTDESKey(byte[] scratchPad) { + private static void validateTDESKey() { // Read Minimum Mac length - it must not be present tmpVariables[0] = KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, data[KEY_PARAMETERS]); @@ -3406,7 +3252,7 @@ private static void validateTDESKey(byte[] scratchPad) { } private static void generateTDESKey(byte[] scratchPad) { - validateTDESKey(scratchPad); + validateTDESKey(); //DESKey desKey = seProvider.createTDESKey(); //tmpVariables[0] = desKey.getKey(scratchPad, (short) 0); tmpVariables[0] = seProvider.createSymmetricKey(KMType.DES,(short)168,scratchPad,(short)0); @@ -3414,7 +3260,7 @@ private static void generateTDESKey(byte[] scratchPad) { 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); @@ -3449,7 +3295,7 @@ private static void validateHmacKey(byte[] scratchPad) { } private static void generateHmacKey(byte[] scratchPad) { - validateHmacKey(scratchPad); + validateHmacKey(); tmpVariables[0] = KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); // generate HMAC Key @@ -3558,11 +3404,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); @@ -3590,8 +3433,7 @@ private static void parseEncryptedKeyBlob(byte[] scratchPad) { private static void decryptSecret(byte[] scratchPad) { // derive master key - stored in derivedKey tmpVariables[0] = deriveKey(scratchPad); - boolean verification = - seProvider.aesGCMDecrypt( + if(!seProvider.aesGCMDecrypt( repository.getHeap(), data[DERIVED_KEY], tmpVariables[0], KMByteBlob.cast(data[SECRET]).getBuffer(), KMByteBlob.cast(data[SECRET]).getStartOff(), @@ -3606,8 +3448,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 @@ -3673,7 +3514,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); @@ -3691,7 +3532,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); @@ -3747,7 +3588,7 @@ private short addIntegers(short num1, short num2){ add(scratchPad, buf, (short)(buf+8), (short)(buf+16)); 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); @@ -3767,10 +3608,11 @@ private short subtractIntegers(short num1, short num2){ 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; @@ -3784,9 +3626,9 @@ private void add(byte[] buf, short op1, short op2, short result){ private void subtract(byte[] buf, short op1, short op2, short result) { 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); diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMRepository.java b/Applet/Applet/src/com/android/javacard/keymaster/KMRepository.java index b2685859..28bdda45 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMRepository.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMRepository.java @@ -25,7 +25,7 @@ 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 MAX_BLOB_STORAGE = 8; public static final short AES_GCM_AUTH_TAG_LENGTH = 12; public static final short MASTER_KEY_SIZE = 16; public static final short SHARED_SECRET_KEY_SIZE = 32; @@ -75,7 +75,7 @@ public class KMRepository { private byte[] heap; private short heapIndex; - //Attesation Id Table; + //Attestation Id Table; private Object[] attIdTable; // Operation State Table @@ -412,7 +412,7 @@ public void removeAllAuthTags() { JCSystem.commitTransaction(); } - public KMAuthTag findTag(short authTag) { + private KMAuthTag findTag(short authTag) { short index = 0; short found; while (index < MAX_BLOB_STORAGE) { diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMSEProvider.java b/Applet/Applet/src/com/android/javacard/keymaster/KMSEProvider.java index 46426341..171cddc2 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMSEProvider.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMSEProvider.java @@ -8,16 +8,20 @@ void createAsymmetricKey( byte alg, byte[] privKeyBuf, short privKeyStart, - short privKeyLength, + short privKeyMaxLength, byte[] pubModBuf, short pubModStart, - short pubModLength, + 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, @@ -25,6 +29,15 @@ boolean importAsymmetricKey( 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); @@ -169,7 +182,7 @@ KMOperation initAsymmetricOperation( short getEventData(byte[] eventBuf, short eventStart, short eventLength); - // Capability query - should return true + // Capability query boolean isAlgSupported(byte alg); boolean isKeySizeSupported(byte alg, short keySize); @@ -185,4 +198,12 @@ KMOperation initAsymmetricOperation( boolean isSystemTimerSupported(); boolean isBootEventSupported(); -} + + boolean isPkcs8ParsingSupported(); + + boolean isAttestationCertSupported(); + + //X509 Cert + KMAttestationCert getAttestationCert(boolean rsaCert); + +} \ No newline at end of file diff --git a/Applet/Applet/test/com/android/javacard/test/KMFunctionalTest.java b/Applet/Applet/test/com/android/javacard/test/KMFunctionalTest.java index 24a1c2cc..93753054 100644 --- a/Applet/Applet/test/com/android/javacard/test/KMFunctionalTest.java +++ b/Applet/Applet/test/com/android/javacard/test/KMFunctionalTest.java @@ -1400,9 +1400,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(); } @@ -1422,12 +1424,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(); }