diff --git a/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMCipherImpl.java b/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMCipherImpl.java index 86a9e126..67af5549 100644 --- a/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMCipherImpl.java +++ b/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMCipherImpl.java @@ -10,16 +10,20 @@ public class KMCipherImpl extends KMCipher{ - Cipher cipher; - javax.crypto.Cipher sunCipher; - short cipherAlg; - short paddingAlg; - short mode; - boolean verificationFlag; + private Cipher cipher; + private javax.crypto.Cipher sunCipher; + private short cipherAlg; + private short paddingAlg; + private short mode; + private boolean verificationFlag; + public static short aes_gcm_decrypt_final_data = 0x00; + KMCipherImpl(Cipher c){ cipher = c; } - KMCipherImpl(javax.crypto.Cipher c){sunCipher = c;} + KMCipherImpl(javax.crypto.Cipher c){ + sunCipher = c; + } @Override public short doFinal(byte[] buffer, short startOff, short length, byte[] scratchPad, short i){ @@ -39,6 +43,15 @@ public short doFinal(byte[] buffer, short startOff, short length, byte[] scratch } }else if(cipherAlg == KMCipher.ALG_AES_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(); @@ -93,6 +106,7 @@ 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){ try { return (short)sunCipher.update(buffer,startOff,length,scratchPad,i); @@ -108,7 +122,16 @@ public short update(byte[] buffer, short startOff, short length, byte[] scratchP @Override public void updateAAD(byte[] buffer, short startOff, short length) { + try { sunCipher.updateAAD(buffer,startOff,length); + } catch (IllegalArgumentException e) { + e.printStackTrace(); + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + } catch (IllegalStateException e) { + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + } catch (UnsupportedOperationException e) { + CryptoException.throwIt(CryptoException.ILLEGAL_USE); + } } @Override @@ -128,4 +151,22 @@ public short getMode() { public void setMode(short mode) { this.mode = mode; } + + @Override + public short getCipherProvider() { + return KMCipher.SUN_JCE; + } + + @Override + public short getAesGcmOutputSize(short len, short macLength) { + if (sunCipher != null) { + return (short) sunCipher.getOutputSize(len); + } else { + if (mode == KMType.ENCRYPT) { + return (short) (len + macLength); + } else { + return (short) (len - macLength); + } + } + } } diff --git a/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMEcdsa256NoDigestSignature.java b/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMEcdsa256NoDigestSignature.java new file mode 100644 index 00000000..e33ebf69 --- /dev/null +++ b/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMEcdsa256NoDigestSignature.java @@ -0,0 +1,178 @@ +package com.android.javacard.keymaster; + +import java.math.BigInteger; +import java.security.AlgorithmParameters; +import java.security.InvalidKeyException; +import java.security.KeyFactory; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.SignatureException; +import java.security.interfaces.ECPrivateKey; +import java.security.spec.ECGenParameterSpec; +import java.security.spec.ECParameterSpec; +import java.security.spec.ECPrivateKeySpec; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.InvalidParameterSpecException; +import java.security.interfaces.ECPrivateKey; +import java.security.interfaces.ECPublicKey; +import java.security.spec.ECGenParameterSpec; +import java.security.spec.ECParameterSpec; +import java.security.spec.ECPoint; +import java.security.spec.ECPrivateKeySpec; +import java.security.spec.ECPublicKeySpec; +import javacard.framework.Util; +import javacard.security.CryptoException; +import javacard.security.Key; +import javacard.security.Signature; + + +public class KMEcdsa256NoDigestSignature extends Signature { + private java.security.Signature sunSigner; + + public KMEcdsa256NoDigestSignature(byte mode, byte[] key, short keyStart, short keyLength){ + KeyFactory kf; + try { + sunSigner = java.security.Signature.getInstance("NONEwithECDSA", "SunEC"); + kf = KeyFactory.getInstance("EC"); + AlgorithmParameters parameters = AlgorithmParameters.getInstance("EC", "SunEC"); + //Supported curve secp256r1 + parameters.init(new ECGenParameterSpec("secp256r1")); + ECParameterSpec ecParameters = parameters.getParameterSpec(ECParameterSpec.class); + if(mode == Signature.MODE_SIGN) { + byte[] privKey = new byte[keyLength]; + for(short i =0; i< keyLength; i++) { + privKey[i] = key[keyStart+i]; + } + BigInteger bI = new BigInteger(privKey); + ECPrivateKeySpec prikeyspec = new ECPrivateKeySpec(bI, ecParameters); + ECPrivateKey privkey = (ECPrivateKey) kf.generatePrivate(prikeyspec); + sunSigner.initSign(privkey); + } else { + //Check if the first byte is 04 and remove it. + if(key[keyStart] == 0x04) { + //uncompressed format. + keyStart++; + keyLength--; + } + short i = 0; + byte[] pubx = new byte[keyLength/2]; + for(;i < keyLength/2; i++ ) { + pubx[i] = key[keyStart+i]; + } + byte[] puby = new byte[keyLength/2]; + for(i = 0;i < keyLength/2; i++ ) { + puby[i] = key[keyStart+keyLength/2+i]; + } + BigInteger bIX = new BigInteger(pubx); + BigInteger bIY = new BigInteger(puby); + ECPoint point = new ECPoint(bIX, bIY); + ECPublicKeySpec pubkeyspec = new ECPublicKeySpec(point, ecParameters); + ECPublicKey pubkey = (ECPublicKey) kf.generatePublic(pubkeyspec); + sunSigner.initVerify(pubkey); + } + } catch (NoSuchAlgorithmException e) { + CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); + } catch (NoSuchProviderException e) { + CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); + } catch(InvalidParameterSpecException e) { + CryptoException.throwIt(CryptoException.INVALID_INIT); + } catch(InvalidKeySpecException e) { + CryptoException.throwIt(CryptoException.INVALID_INIT); + } catch(InvalidKeyException e) { + CryptoException.throwIt(CryptoException.INVALID_INIT); + } + } + + @Override + 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 + public byte getAlgorithm() { + return 0; + } + + @Override + public byte getMessageDigestAlgorithm() { + return 0; + } + + @Override + public byte getCipherAlgorithm() { + return 0; + } + + @Override + public byte getPaddingAlgorithm() { + return 0; + } + + @Override + public short getLength() throws CryptoException { + return 0; + } + + @Override + public void update(byte[] message, short msgStart, short messageLength) throws CryptoException { + byte[] msgBytes = new byte[messageLength]; + for(int i =0; i< messageLength; i++) { + msgBytes[i] = message[msgStart+i]; + } + try { + if(messageLength > 0) + sunSigner.update(msgBytes); + } catch (SignatureException e) { + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + } + } + + @Override + public short sign(byte[] bytes, short i, short i1, byte[] bytes1, short i2) throws CryptoException { + short len = 0; + try { + update(bytes, i , i1); + byte[] sig = sunSigner.sign(); + Util.arrayCopyNonAtomic(sig, (short)0, bytes1, i2, (short)sig.length); + return (short)sig.length; + } catch (SignatureException e) { + // TODO Auto-generated catch block + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + } + return len; + } + + @Override + public short signPreComputedHash(byte[] bytes, short i, short i1, byte[] bytes1, short i2) throws CryptoException { + return 0; + } + + @Override + public boolean verify(byte[] bytes, short i, short i1, byte[] bytes1, short i2, short i3) throws CryptoException { + // Cannot support this method as javacard cipher api does not allow 256 byte for public key + // encryption without padding. It only allows 255 bytes data. + try { + update(bytes, i , i1); + return sunSigner.verify(bytes1, i2, i3); + } catch (SignatureException e) { + // TODO Auto-generated catch block + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + } + return false; + } + + @Override + public boolean verifyPreComputedHash(byte[] bytes, short i, short i1, byte[] bytes1, short i2, short i3) throws CryptoException { + return false; + } +} diff --git a/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMJcardSimulator.java b/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMJcardSimulator.java index bdd16464..72b6f5d9 100644 --- a/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMJcardSimulator.java +++ b/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMJcardSimulator.java @@ -22,7 +22,6 @@ import java.security.KeyFactory; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; -import java.security.interfaces.RSAKey; import java.security.spec.InvalidKeySpecException; import java.security.spec.MGF1ParameterSpec; import java.security.spec.RSAPrivateKeySpec; @@ -62,7 +61,7 @@ * Key, and upto 512 bit HMAC key. Also simulator does not support TRNG, so this implementation just * creates its own RNG using PRNG. */ -public class KMJcardSimulator implements KMCryptoProvider { +public class KMJcardSimulator 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; @@ -181,6 +180,11 @@ public HMACKey createHMACKey(short keysize) { return createHMACKey(rndNum, (short)0, (short)rndNum.length); } + @Override + public short createSymmetricKey(byte alg, short keysize, byte[] buf, short startOff) { + return 0; + } + @Override public HMACKey createHMACKey(byte[] secretBuffer, short secretOff, short secretLength) { HMACKey key = null; @@ -209,15 +213,17 @@ public short aesGCMEncrypt( short authTagLen) { //Create the sun jce compliant aes key byte[] keyMaterial = new byte[16]; + short keySize = 16; if(key.getSize() == 128){ keyMaterial = new byte[16]; }else if(key.getSize() == 256){ keyMaterial = new byte[32]; + 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,(short)16, "AES"); + java.security.Key aesKey = new SecretKeySpec(keyMaterial,(short)0,keySize, "AES"); // Create the cipher javax.crypto.Cipher cipher = null; try { @@ -341,7 +347,7 @@ public boolean aesGCMDecrypt( keyMaterial = new byte[16]; }else if(key.getSize() == 256){ keyMaterial = new byte[32]; - keySize = 16; + keySize = 32; } key.getKey(keyMaterial,(short)0); //print("KeyMaterial Dec", keyMaterial); @@ -466,6 +472,12 @@ public HMACKey cmacKdf(byte[] keyMaterial, byte[] label, byte[] context, short c return createHMACKey(keyOut, (short)0, (short)keyOut.length); } + @Override + public short cmacKdf(byte[] keyMaterial, byte[] label, byte[] context, short contextStart, short contextLength, byte[] keyBuf, short keyStart) { + HMACKey key = cmacKdf(keyMaterial,label,context,contextStart,contextLength); + 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); @@ -479,6 +491,71 @@ public boolean hmacVerify(HMACKey key, byte[] data, short dataStart, short dataL return hmacSignature.verify(data, dataStart, dataLength, mac, macStart, macLength); } + @Override + public short hmacSign(byte[] keyBuf, short keyStart, short keyLength, byte[] data, short dataStart, short dataLength, byte[] mac, short macStart) { + return 0; + } + + @Override + public boolean hmacVerify(byte[] keyBuf, short keyStart, short keyLength, byte[] data, short dataStart, short dataLength, byte[] mac, short macStart, short macLength) { + return false; + } + + @Override + public short initSymmetricOperation(byte purpose, byte alg, byte digest, byte padding, byte blockMode, byte[] keyBuf, short keyStart, short keyLength) { + return 0; + } + + @Override + public short initSymmetricOperation(byte purpose, byte alg, byte digest, byte[] keyBuf, short keyStart, short keyLength) { + return 0; + } + + @Override + public short initAsymmetricOperation(byte purpose, byte alg, byte padding, byte digest, byte[] privKeyBuf, short privKeyStart, short privKeyLength, byte[] modBuf, short modStart, short modLength) { + return 0; + } + + @Override + public short initAsymmetricOperation(byte purpose, byte alg, byte padding, byte digest, byte[] privKeyBuf, short privKeyStart, short privKeyLength) { + return 0; + } + + @Override + public short updateOperation(short opHandle, byte[] inputDataBuf, short inputDataStart, short inputDataLength, byte[] outputDataBuf, short outputDataStart) { + return 0; + } + + @Override + public short finishOperation(short opHandle, byte[] inputDataBuf, short inputDataStart, short inputDataLength, byte[] outputDataBuf, short outputDataStart) { + return 0; + } + + @Override + public void abortOperation(short opHandle) { + + } + + @Override + public short hmacInit(byte[] keyBuf, short keyStart, short keyLength, byte digest, byte mode) { + return 0; + } + + @Override + public short hmacSign(short opHandle, byte[] data, short dataStart, short dataLength, byte[] mac, short macStart) { + 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) { @@ -609,16 +686,21 @@ private Signature createNoDigestSigner(short padding, @Override public Signature createEcSigner(short msgDigestAlg, byte[] secret, short secretStart, short secretLength) { - short alg = Signature.ALG_ECDSA_SHA_256; - if(msgDigestAlg == MessageDigest.ALG_NULL) CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); - //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); - Signature ecSigner = Signature.getInstance((byte)alg,false); - ecSigner.init(key,Signature.MODE_SIGN); + short alg = Signature.ALG_ECDSA_SHA_256; + Signature ecSigner; + if(msgDigestAlg == MessageDigest.ALG_NULL) { + //CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); + 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); + ecSigner.init(key,Signature.MODE_SIGN); + } return ecSigner; } @@ -669,7 +751,9 @@ public KMCipher createSymmetricCipher(short cipherAlg, short mode, short padding key = (DESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_DES,len,false); ((DESKey) key).setKey(secret,secretStart); symmCipher = Cipher.getInstance((byte)cipherAlg, false); - symmCipher.init(key, (byte) mode, ivBuffer, ivStart, ivLength); + //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); break; case KMCipher.ALG_DES_ECB_NOPAD: cipherAlg = Cipher.ALG_DES_ECB_NOPAD; @@ -967,17 +1051,78 @@ public Signature createRsaVerifier(short msgDigestAlg, short padding, byte[] mod @Override public Signature createEcVerifier(short msgDigestAlg, byte[] pubKey, short pubKeyStart, short pubKeyLength) { short alg = Signature.ALG_ECDSA_SHA_256; - if(msgDigestAlg == MessageDigest.ALG_NULL) CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); -// 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); - Signature ecVerifier = Signature.getInstance((byte)alg,false); - ecVerifier.init(key,Signature.MODE_VERIFY); + Signature ecVerifier; + //if(msgDigestAlg == MessageDigest.ALG_NULL) CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); + if(msgDigestAlg == MessageDigest.ALG_NULL) { + //CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); + 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); + ecVerifier.init(key,Signature.MODE_VERIFY); + } return ecVerifier; } + @Override + public short getSystemTimeInMilliSeconds(byte[] timeBuf, short timeStart, short timeOffset) { + return 0; + } + + @Override + public short addListener(KMEventListener listener, byte eventType) { + return 0; + } + + @Override + public short getEventData(byte[] eventBuf, short eventStart, short eventLength) { + return 0; + } + + @Override + public boolean isAlgSupported(byte alg) { + return true; + } + + @Override + public boolean isKeySizeSupported(byte alg, short keySize) { + return true; + } + + @Override + public boolean isCurveSupported(byte eccurve) { + return true; + } + + @Override + public boolean isDigestSupported(byte alg, byte digest) { + return true; + } + + @Override + public boolean isPaddingSupported(byte alg, byte padding) { + return true; + } + + @Override + public boolean isBlockModeSupported(byte alg, byte blockMode) { + return true; + } + + @Override + public boolean isSystemTimerSupported() { + return false; + } + + @Override + public boolean isBootEventSupported() { + return false; + } + /* private static void print (String lab, byte[] b, short s, short l){ byte[] i = new byte[l]; diff --git a/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMCryptoProviderImpl.java b/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMSEProviderImpl.java similarity index 50% rename from Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMCryptoProviderImpl.java rename to Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMSEProviderImpl.java index 917b4911..34628ba7 100644 --- a/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMCryptoProviderImpl.java +++ b/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMSEProviderImpl.java @@ -1,8 +1,8 @@ package com.android.javacard.keymaster; -public class KMCryptoProviderImpl { - public static KMCryptoProvider instance(){ +public class KMSEProviderImpl { + public static KMSEProvider instance(){ return new KMJcardSimulator(); } } diff --git a/Applet/Applet/OracleSimProvider/com/android/javacard/keymaster/KMCipherImpl.java b/Applet/Applet/OracleSimProvider/com/android/javacard/keymaster/KMCipherImpl.java index ea8de14d..34e1851e 100644 --- a/Applet/Applet/OracleSimProvider/com/android/javacard/keymaster/KMCipherImpl.java +++ b/Applet/Applet/OracleSimProvider/com/android/javacard/keymaster/KMCipherImpl.java @@ -32,6 +32,16 @@ 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); diff --git a/Applet/Applet/OracleSimProvider/com/android/javacard/keymaster/KMCryptoProviderImpl.java b/Applet/Applet/OracleSimProvider/com/android/javacard/keymaster/KMCryptoProviderImpl.java deleted file mode 100644 index 182dda4e..00000000 --- a/Applet/Applet/OracleSimProvider/com/android/javacard/keymaster/KMCryptoProviderImpl.java +++ /dev/null @@ -1,8 +0,0 @@ - -package com.android.javacard.keymaster; - -public class KMCryptoProviderImpl { - public static KMCryptoProvider instance(){ - return new KMSimulator(); - } -} diff --git a/Applet/Applet/OracleSimProvider/com/android/javacard/keymaster/KMSEProviderImpl.java b/Applet/Applet/OracleSimProvider/com/android/javacard/keymaster/KMSEProviderImpl.java new file mode 100644 index 00000000..9920c4ee --- /dev/null +++ b/Applet/Applet/OracleSimProvider/com/android/javacard/keymaster/KMSEProviderImpl.java @@ -0,0 +1,8 @@ + +package com.android.javacard.keymaster; + +public class KMSEProviderImpl { + public static KMSEProvider instance(){ + return new KMSimulator(); + } +} diff --git a/Applet/Applet/OracleSimProvider/com/android/javacard/keymaster/KMSimulator.java b/Applet/Applet/OracleSimProvider/com/android/javacard/keymaster/KMSimulator.java index ed8750a2..f7868951 100644 --- a/Applet/Applet/OracleSimProvider/com/android/javacard/keymaster/KMSimulator.java +++ b/Applet/Applet/OracleSimProvider/com/android/javacard/keymaster/KMSimulator.java @@ -40,7 +40,7 @@ * Key, and upto 512 bit HMAC key. Also simulator does not support TRNG, so this implementation just * creates its own RNG using PRNG. */ -public class KMSimulator implements KMCryptoProvider { +public 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; @@ -151,6 +151,11 @@ public HMACKey createHMACKey(short keysize) { return key; } + @Override + public short createSymmetricKey(byte alg, short keysize, byte[] buf, short startOff) { + return 0; + } + @Override public void addRngEntropy(byte[] num, short offset, short length) { // Maximum length can be 256 bytes. But currently we support max 32 bytes seed. @@ -357,6 +362,11 @@ public HMACKey cmacKdf(byte[] keyMaterial, byte[] label, byte[] context, short c return null; } + @Override + public short cmacKdf(byte[] keyMaterial, byte[] label, byte[] context, short contextStart, short contextLength, byte[] keyBuf, short keyStart) { + return 0; + } + @Override public short hmacSign(HMACKey key, byte[] data, short dataStart, short dataLength, byte[] mac, short macStart) { return 0; @@ -367,6 +377,71 @@ public boolean hmacVerify(HMACKey key, byte[] data, short dataStart, short dataL return false; } + @Override + public short hmacSign(byte[] keyBuf, short keyStart, short keyLength, byte[] data, short dataStart, short dataLength, byte[] mac, short macStart) { + return 0; + } + + @Override + public boolean hmacVerify(byte[] keyBuf, short keyStart, short keyLength, byte[] data, short dataStart, short dataLength, byte[] mac, short macStart, short macLength) { + return false; + } + + @Override + public short initSymmetricOperation(byte purpose, byte alg, byte digest, byte padding, byte blockMode, byte[] keyBuf, short keyStart, short keyLength) { + return 0; + } + + @Override + public short initSymmetricOperation(byte purpose, byte alg, byte digest, byte[] keyBuf, short keyStart, short keyLength) { + return 0; + } + + @Override + public short initAsymmetricOperation(byte purpose, byte alg, byte padding, byte digest, byte[] privKeyBuf, short privKeyStart, short privKeyLength, byte[] modBuf, short modStart, short modLength) { + return 0; + } + + @Override + public short initAsymmetricOperation(byte purpose, byte alg, byte padding, byte digest, byte[] privKeyBuf, short privKeyStart, short privKeyLength) { + return 0; + } + + @Override + public short updateOperation(short opHandle, byte[] inputDataBuf, short inputDataStart, short inputDataLength, byte[] outputDataBuf, short outputDataStart) { + return 0; + } + + @Override + public short finishOperation(short opHandle, byte[] inputDataBuf, short inputDataStart, short inputDataLength, byte[] outputDataBuf, short outputDataStart) { + return 0; + } + + @Override + public void abortOperation(short opHandle) { + + } + + @Override + public short hmacInit(byte[] keyBuf, short keyStart, short keyLength, byte digest, byte mode) { + return 0; + } + + @Override + public short hmacSign(short opHandle, byte[] data, short dataStart, short dataLength, byte[] mac, short macStart) { + 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) { return null; @@ -521,4 +596,59 @@ public Signature createEcVerifier(short msgDigestAlg, byte[] pubKey, short pubKe return null; } + @Override + public short getSystemTimeInMilliSeconds(byte[] timeBuf, short timeStart, short timeOffset) { + return 0; + } + + @Override + public short addListener(KMEventListener listener, byte eventType) { + return 0; + } + + @Override + public short getEventData(byte[] eventBuf, short eventStart, short eventLength) { + return 0; + } + + @Override + public boolean isAlgSupported(byte alg) { + return false; + } + + @Override + public boolean isKeySizeSupported(byte alg, short keySize) { + return false; + } + + @Override + public boolean isCurveSupported(byte eccurve) { + return false; + } + + @Override + public boolean isDigestSupported(byte alg, byte digest) { + return false; + } + + @Override + public boolean isPaddingSupported(byte alg, byte padding) { + return false; + } + + @Override + public boolean isBlockModeSupported(byte alg, byte blockMode) { + return false; + } + + @Override + public boolean isSystemTimerSupported() { + return false; + } + + @Override + public boolean isBootEventSupported() { + return false; + } + } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMCipher.java b/Applet/Applet/src/com/android/javacard/keymaster/KMCipher.java index f8891fc2..a36f11ab 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMCipher.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMCipher.java @@ -22,6 +22,7 @@ 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); @@ -37,4 +38,7 @@ public abstract class KMCipher { public abstract void setCipherAlgorithm(short alg); + public abstract short getCipherProvider(); + + public abstract short getAesGcmOutputSize(short len, short macLength); } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMEventListener.java b/Applet/Applet/src/com/android/javacard/keymaster/KMEventListener.java new file mode 100644 index 00000000..e59972ab --- /dev/null +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMEventListener.java @@ -0,0 +1,5 @@ +package com.android.javacard.keymaster; + +public interface KMEventListener { + void handle(byte event); +} diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java b/Applet/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java index d16285ba..32eda210 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java @@ -165,7 +165,7 @@ public class KMKeymasterApplet extends Applet implements AppletEvent, ExtendedLe private static KMEncoder encoder; private static KMDecoder decoder; private static KMRepository repository; - private static KMCryptoProvider cryptoProvider; + private static KMSEProvider cryptoProvider; private static byte[] buffer; private static short bufferLength; private static short bufferStartOffset; @@ -177,7 +177,7 @@ public class KMKeymasterApplet extends Applet implements AppletEvent, ExtendedLe /** Registers this applet. */ protected KMKeymasterApplet() { // TODO change this to make this compile time variation. - cryptoProvider = KMCryptoProviderImpl.instance(); + cryptoProvider = KMSEProviderImpl.instance(); provisionDone = false; setBootParamsDone = false; byte[] buf = @@ -771,7 +771,7 @@ private void processComputeSharedHmacCmd(APDU apdu) { while (tmpVariables[0] < tmpVariables[1]) { // read HmacSharingParam tmpVariables[2] = KMArray.cast(data[HMAC_SHARING_PARAMS]).get(tmpVariables[0]); - // get seed + // get seed - 32 bytes max tmpVariables[3] = KMHmacSharingParameters.cast(tmpVariables[2]).getSeed(); tmpVariables[4] = KMByteBlob.cast(tmpVariables[3]).length(); // if seed is present @@ -785,7 +785,7 @@ private void processComputeSharedHmacCmd(APDU apdu) { tmpVariables[4]); tmpVariables[5] += tmpVariables[4]; // increment by seed length } - // if nonce is present get nonce + // 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 @@ -860,14 +860,18 @@ private void processUpgradeKeyCmd(APDU apdu) { // stored version must be zero. Then only upgrade is allowed else it is invalid argument. if(KMInteger.compare(tmpVariables[0], tmpVariables[2]) != -1 && KMInteger.compare(tmpVariables[2], tmpVariables[4]) != 0){ - KMException.throwIt(KMError.INVALID_ARGUMENT); + //Key Should not be upgraded, but error code should be OK, As per VTS. + //KMException.throwIt(KMError.INVALID_ARGUMENT); + tmpVariables[5] = KMError.INVALID_ARGUMENT; } } if(tmpVariables[1] != KMType.INVALID_VALUE){ // The key characteristics should have has os patch level < os patch level stored in javacard // then only upgrade is allowed. if(KMInteger.compare(tmpVariables[1], tmpVariables[3]) != -1){ - KMException.throwIt(KMError.INVALID_ARGUMENT); + //Key Should not be upgraded, but error code should be OK, As per VTS. + //KMException.throwIt(KMError.INVALID_ARGUMENT); + tmpVariables[5] = KMError.INVALID_ARGUMENT; } } /* KMIntegerTag.getValue( @@ -901,13 +905,17 @@ private void processUpgradeKeyCmd(APDU apdu) { } }*/ // remove Auth Tag - repository.removeAuthTag(data[AUTH_TAG]); - // copy origin - data[ORIGIN] = KMEnumTag.getValue(KMType.ORIGIN, data[HW_PARAMETERS]); - // create new key blob with current os version etc. - createEncryptedKeyBlob(scratchPad); - // persist new auth tag for rollback resistance. - repository.persistAuthTag(data[AUTH_TAG]); + if(tmpVariables[5] != KMError.INVALID_ARGUMENT) { + repository.removeAuthTag(data[AUTH_TAG]); + // copy origin + data[ORIGIN] = KMEnumTag.getValue(KMType.ORIGIN, data[HW_PARAMETERS]); + // create new key blob with current os version etc. + createEncryptedKeyBlob(scratchPad); + // persist new auth tag for rollback resistance. + repository.persistAuthTag(data[AUTH_TAG]); + } else { + data[KEY_BLOB] = KMByteBlob.instance((short)0); + } // prepare the response tmpVariables[0] = KMArray.instance((short) 2); KMArray.cast(tmpVariables[0]).add((short) 0, KMInteger.uint_16(KMError.OK)); @@ -1057,7 +1065,7 @@ private void processImportWrappedKeyCmd(APDU apdu) { KMByteBlob.cast(data[AUTH_TAG]).getStartOff(), KMByteBlob.cast(data[AUTH_TAG]).length()); if (verification == false) { - KMException.throwIt(KMError.IMPORTED_KEY_VERIFICATION_FAILED); + KMException.throwIt(KMError.VERIFICATION_FAILED); } cryptoProvider.delete(key); @@ -1103,7 +1111,7 @@ private void processAttestKeyCmd(APDU apdu) { // 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.INVALID_ARGUMENT); + KMException.throwIt(KMError.ATTESTATION_APPLICATION_ID_MISSING); } // Save attestation challenge tmpVariables[0] = KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.ATTESTATION_CHALLENGE,data[KEY_PARAMETERS]); @@ -1422,7 +1430,7 @@ private void processFinishOperationCmd(APDU apdu) { finishDecryptOperation(op,scratchPad); break; } - // Remove the operation handle + // Remove the operation handle repository.releaseOperation(op); // make response tmpVariables[1] = KMArray.instance((short) 0); @@ -1434,6 +1442,7 @@ private void processFinishOperationCmd(APDU apdu) { KMArray.cast(tmpVariables[2]).add((short) 0, KMInteger.uint_16(KMError.OK)); KMArray.cast(tmpVariables[2]).add((short) 1, tmpVariables[1]); KMArray.cast(tmpVariables[2]).add((short) 2, data[OUTPUT_DATA]); + // Encode the response bufferLength = encoder.encode(tmpVariables[2], buffer, bufferStartOffset); sendOutgoing(apdu); @@ -1494,7 +1503,8 @@ private void finishEncryptOperation(KMOperationState op, byte[] scratchPad) { tmpVariables[0] = KMCipher.DES_BLOCK_SIZE; } //If no padding then data length must be block aligned - if (op.getPadding() == KMType.PADDING_NONE && ((short)(len % tmpVariables[0]) != 0)){ + 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); } //If padding i.e. pkcs7 then add padding to right @@ -1569,14 +1579,23 @@ private void finishDecryptOperation(KMOperationState op, byte[] scratchPad) { KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), len,heap, tmpVariables[1]); //remove padding bytes if pkcs7 - if(op.getPadding() == KMType.PKCS7 && len >0) len = (short)(len - heap[(short)(tmpVariables[1]+len -1)]); + 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; } } - private void updateAAD(KMOperationState op){ + //update operation should send 0x00 for finish variable, where as finish operation + //should send 0x01 for finish variable. + private void updateAAD(KMOperationState op, byte finish){ // Is input data absent if(data[INPUT_DATA] == KMType.INVALID_VALUE){ KMException.throwIt(KMError.INVALID_ARGUMENT); @@ -1585,7 +1604,9 @@ private void updateAAD(KMOperationState op){ // But if it is called for neither then return error. tmpVariables[0] = KMByteBlob.cast(data[INPUT_DATA]).length(); tmpVariables[1] = KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.ASSOCIATED_DATA,data[KEY_PARAMETERS]); - if(tmpVariables[1] == KMType.INVALID_VALUE && tmpVariables[0] <=0){ + //For Finish operation the input data can be zero length and associated data can be INVALID_VALUE + //For update operation either input data or associated data should be present. + if(tmpVariables[1] == KMType.INVALID_VALUE && tmpVariables[0] <=0 && finish == 0x00){ KMException.throwIt(KMError.INVALID_INPUT_LENGTH); } // Check if associated data is present and update aad still allowed by the operation. @@ -1603,7 +1624,7 @@ private void updateAAD(KMOperationState op){ } private void updateAesGcmOperation(KMOperationState op, APDU apdu) { - updateAAD(op); + updateAAD(op, (byte) 0x00); // Now handle the input data tmpVariables[0] = KMByteBlob.cast(data[INPUT_DATA]).length(); // If the input data is non zero length @@ -1623,7 +1644,7 @@ private void updateAesGcmOperation(KMOperationState op, APDU apdu) { } // Allocate new data buffer in which input data will be assembled tmpVariables[1] = KMByteBlob.instance(tmpVariables[0]); - if (tmpVariables[0] > 0) { + if (op.isAesBlockSaved() && tmpVariables[0] > 0) { // First copy the previously saved block to the buffer Util.arrayCopy( op.getAesBlock(), @@ -1633,15 +1654,25 @@ private void updateAesGcmOperation(KMOperationState op, APDU apdu) { 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 + // 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() + AES_BLOCK_SIZE), + (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( @@ -1663,8 +1694,20 @@ private void updateAesGcmOperation(KMOperationState op, APDU apdu) { KMByteBlob.cast(data[INPUT_DATA]).length(), KMByteBlob.cast(data[OUTPUT_DATA]).getBuffer(), KMByteBlob.cast(data[OUTPUT_DATA]).getStartOff()); - if (tmpVariables[0] != KMByteBlob.cast(data[INPUT_DATA]).length()) { - KMException.throwIt(KMError.UNKNOWN_ERROR); + //TODO: Not always the output length is equal to input length. + //TODO: Few VTS tests fail because of this below code. + //if (tmpVariables[0] != KMByteBlob.cast(data[INPUT_DATA]).length()) { + // KMException.throwIt(KMError.UNKNOWN_ERROR); + //} + //In case if output length not equal to input length. Allocate actual size of output. + if(tmpVariables[0] != KMByteBlob.cast(data[INPUT_DATA]).length()) { + data[INPUT_DATA] = data[OUTPUT_DATA]; + data[OUTPUT_DATA] = KMByteBlob.instance(tmpVariables[0]); + Util.arrayCopy(KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), + KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), + KMByteBlob.cast(data[OUTPUT_DATA]).getBuffer(), + KMByteBlob.cast(data[OUTPUT_DATA]).getStartOff(), + tmpVariables[0]); } } // make response @@ -1682,13 +1725,17 @@ 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); + 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(); - if(!op.isAesBlockSaved() && tmpVariables[0] < op.getMacLength() && op.getPurpose()==KMType.DECRYPT){ + tmpVariables[1] = KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.ASSOCIATED_DATA,data[KEY_PARAMETERS]); + if(!op.isAesBlockSaved() && + (tmpVariables[0] < (short)(op.getMacLength()/8)) && + (op.getPurpose()==KMType.DECRYPT) && + (tmpVariables[1] == KMType.INVALID_VALUE)){ KMException.throwIt(KMError.INVALID_INPUT_LENGTH); } // Now add the aes block saved in op.state to input data @@ -1707,19 +1754,36 @@ private void finishAesGcmOperation(KMOperationState op, byte[] scratchPad) { data[INPUT_DATA] = tmpVariables[0]; } // Allocate output data buffer based on mac length and encrypt or decrypt operation - tmpVariables[0] = KMByteBlob.cast(data[INPUT_DATA]).length(); - if(op.getPurpose() == KMType.ENCRYPT){ + tmpVariables[0] = op.getCipher().getAesGcmOutputSize(KMByteBlob.cast(data[INPUT_DATA]).length(), + (short)(op.getMacLength()/8)); + /* if(op.getPurpose() == KMType.ENCRYPT){ data[OUTPUT_DATA] = KMByteBlob.instance((short)(tmpVariables[0]+(op.getMacLength()/8))); }else{ - data[OUTPUT_DATA] = KMByteBlob.instance((short)(tmpVariables[0]-(op.getMacLength()/8))); + data[OUTPUT_DATA] = KMByteBlob.instance((short)(tmpVariables[0]-(op.getMacLength()/8))); } + */ + data[OUTPUT_DATA] = KMByteBlob.instance(tmpVariables[0]); //This will throw KMError.VERIFICATION_FAILED if the tag does not match during decrypt. - op.getCipher().doFinal( + tmpVariables[0] = op.getCipher().doFinal( KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), KMByteBlob.cast(data[INPUT_DATA]).length(), KMByteBlob.cast(data[OUTPUT_DATA]).getBuffer(), KMByteBlob.cast(data[OUTPUT_DATA]).getStartOff()); + if(tmpVariables[0] != KMByteBlob.cast(data[OUTPUT_DATA]).length()){ + KMException.throwIt(KMError.UNKNOWN_ERROR); + } + + /* //TODO HACK The SunJCE update multipart decryption does not return any output instead it stores + // the input. The doFinal call returns the complete plain text at once. so here as a hack + // we are allocation a 256 buffer(VTS MAX input message size) to store the plain text. + if(KMCipher.SUN_JCE == op.getCipher().getCipherProvider() && op.getPurpose() == KMType.DECRYPT) { + if (KMCipherImpl.aes_gcm_decrypt_final_data != 0x00) { + data[OUTPUT_DATA] = KMCipherImpl.aes_gcm_decrypt_final_data; + KMCipherImpl.aes_gcm_decrypt_final_data = 0x00; + } + } +*/ } private void beginAesGcmOperation(KMOperationState op) { @@ -1731,7 +1795,7 @@ private void beginAesGcmOperation(KMOperationState op) { if (op.getPurpose() == KMType.ENCRYPT) { purpose = KMCipher.MODE_ENCRYPT; if (data[IV] == KMType.INVALID_VALUE) { - data[IV] = KMByteBlob.instance((short) 16); + data[IV] = KMByteBlob.instance((short) 12); cryptoProvider.newRandomNumber( KMByteBlob.cast(data[IV]).getBuffer(), KMByteBlob.cast(data[IV]).getStartOff(), @@ -1867,7 +1931,7 @@ private void finishSigningVerifyingOperation(KMOperationState op, byte[]scratchP 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. - if(op.getDigest() == KMType.DIGEST_NONE || len > 32){ + if(op.getDigest() == KMType.DIGEST_NONE && len > 32){ len = 32; } if(op.getPurpose() == KMType.SIGN){ @@ -1889,24 +1953,29 @@ private void finishSigningVerifyingOperation(KMOperationState op, byte[]scratchP } break; case KMType.HMAC: + //For HMAC, either sign or verify we do sign operation only and we compare the + //signature manually. The reason for doing this is the TAG_MAC_LENGTH can be 32 bytes + //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. - if(op.getPurpose() == KMType.SIGN){ - // len of signature will always be 32 bytes. + // len of signature will always be 32 bytes. len = op.getSignerVerifier().sign(KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), KMByteBlob.cast(data[INPUT_DATA]).getStartOff(),len,scratchPad, (short)0); - data[OUTPUT_DATA] = KMByteBlob.instance(scratchPad,(short)0, len); - }else{ - if(!op.getSignerVerifier().verify( - KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), - KMByteBlob.cast(data[INPUT_DATA]).getStartOff(),len, - KMByteBlob.cast(data[SIGNATURE]).getBuffer(), - KMByteBlob.cast(data[SIGNATURE]).getStartOff(), - KMByteBlob.cast(data[SIGNATURE]).length())){ - KMException.throwIt(KMError.VERIFICATION_FAILED); + // Copy only signature of mac length size. + data[OUTPUT_DATA] = KMByteBlob.instance(scratchPad,(short)0, (short) (op.getMacLength() / 8)); + if(op.getPurpose() == KMType.VERIFY){ + if(0 != Util.arrayCompare(KMByteBlob.cast(data[OUTPUT_DATA]).getBuffer(), + KMByteBlob.cast(data[OUTPUT_DATA]).getStartOff(), + KMByteBlob.cast(data[SIGNATURE]).getBuffer(), + KMByteBlob.cast(data[SIGNATURE]).getStartOff(), + (short)(op.getMacLength() / 8))) { + KMException.throwIt(KMError.VERIFICATION_FAILED); } - } break; + } + break; default:// This is should never happen KMException.throwIt(KMError.OPERATION_CANCELLED); break; @@ -2248,6 +2317,18 @@ private void processBeginOperationCmd(APDU apdu) { //As per VTS, for the decryption operation don't send the iv back. if (data[IV] != KMType.INVALID_VALUE && op.getPurpose() != KMType.DECRYPT) { 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. + //While sending the iv back for DES/CBC mode of opeation only send + //8 bytes back. + tmpVariables[1] = KMByteBlob.instance((short) 8); + Util.arrayCopy(KMByteBlob.cast(data[IV]).getBuffer(), + KMByteBlob.cast(data[IV]).getStartOff(), + KMByteBlob.cast(tmpVariables[1]).getBuffer(), + KMByteBlob.cast(tmpVariables[1]).getStartOff(), + (short)8); + data[IV] = tmpVariables[1]; + } KMArray.cast(tmpVariables[2]).add((short) 0, KMByteTag.instance(KMType.NONCE, data[IV])); } else { tmpVariables[2] = KMArray.instance((short) 0); @@ -2289,6 +2370,20 @@ private void authorizeAlgorithm(KMOperationState op){ op.setAlgorithm((byte)alg); } private void authorizePurpose(KMOperationState op){ + switch(op.getAlgorithm()) { + case KMType.AES: + case KMType.DES: + if(op.getPurpose() == KMType.SIGN || op.getPurpose() == KMType.VERIFY) + KMException.throwIt(KMError.UNSUPPORTED_PURPOSE); + break; + case KMType.EC: + case KMType.HMAC: + if(op.getPurpose() == KMType.ENCRYPT || op.getPurpose() == KMType.DECRYPT) + KMException.throwIt(KMError.UNSUPPORTED_PURPOSE); + break; + default: + break; + } if(!KMEnumArrayTag.contains(KMType.PURPOSE,op.getPurpose(),data[HW_PARAMETERS])){ KMException.throwIt(KMError.INCOMPATIBLE_PURPOSE); } @@ -2304,8 +2399,18 @@ private void authorizeDigest(KMOperationState op){ if(!KMEnumArrayTag.cast(digests).contains(param)) KMException.throwIt(KMError.INCOMPATIBLE_DIGEST); op.setDigest((byte)param); } + short paramPadding = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.PADDING, data[KEY_PARAMETERS]); + if(paramPadding != KMType.INVALID_VALUE){ + if(KMEnumArrayTag.cast(paramPadding).length() != 1) KMException.throwIt(KMError.INVALID_ARGUMENT); + paramPadding = KMEnumArrayTag.cast(paramPadding).get((short)0); + } switch(op.getAlgorithm()){ case KMType.RSA: + if ((paramPadding == KMType.RSA_OAEP || paramPadding == KMType.RSA_PSS) + && param == KMType.INVALID_VALUE) { + KMException.throwIt(KMError.UNSUPPORTED_DIGEST); + } + break; case KMType.EC: case KMType.HMAC: if(param == KMType.INVALID_VALUE) KMException.throwIt(KMError.UNSUPPORTED_DIGEST); @@ -2329,11 +2434,11 @@ private void authorizePadding(KMOperationState op){ if((op.getPurpose() == KMType.SIGN || op.getPurpose() == KMType.VERIFY)&& param != KMType.PADDING_NONE && param != KMType.RSA_PSS && - param != KMType.RSA_PKCS1_1_5_SIGN) KMException.throwIt(KMError.INCOMPATIBLE_PADDING_MODE); + param != KMType.RSA_PKCS1_1_5_SIGN) KMException.throwIt(KMError.UNSUPPORTED_PADDING_MODE); if((op.getPurpose() == KMType.ENCRYPT || op.getPurpose() == KMType.DECRYPT) && param != KMType.PADDING_NONE && param != KMType.RSA_OAEP && - param != KMType.RSA_PKCS1_1_5_ENCRYPT) KMException.throwIt(KMError.INCOMPATIBLE_PADDING_MODE); + param != KMType.RSA_PKCS1_1_5_ENCRYPT) KMException.throwIt(KMError.UNSUPPORTED_PADDING_MODE); if (param == KMType.PADDING_NONE && op.getDigest() != KMType.DIGEST_NONE) { KMException.throwIt(KMError.INCOMPATIBLE_DIGEST); } @@ -2358,6 +2463,11 @@ private void authorizeBlockModeAndMacLength(KMOperationState op){ if(KMEnumArrayTag.cast(param).length() != 1) KMException.throwIt(KMError.INVALID_ARGUMENT); param = KMEnumArrayTag.cast(param).get((short)0); } + if (KMType.AES == op.getAlgorithm() || KMType.DES == op.getAlgorithm()) { + if(!KMEnumArrayTag.contains(KMType.BLOCK_MODE, param, data[HW_PARAMETERS])){ + KMException.throwIt(KMError.INCOMPATIBLE_BLOCK_MODE); + } + } short macLen = KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.MAC_LENGTH, data[KEY_PARAMETERS]); switch (op.getAlgorithm()){ @@ -2375,20 +2485,30 @@ private void authorizeBlockModeAndMacLength(KMOperationState op){ KMException.throwIt(KMError.INVALID_MAC_LENGTH); } op.setMacLength(macLen); - }else if(macLen != KMType.INVALID_VALUE) KMException.throwIt(KMError.INVALID_ARGUMENT); + } + //TODO Ignore MAC_LENGTH tag for other modes of operation. + //else if(macLen != KMType.INVALID_VALUE) KMException.throwIt(KMError.INVALID_ARGUMENT); break; case KMType.DES: if(param == KMType.INVALID_VALUE) KMException.throwIt(KMError.INVALID_ARGUMENT); break; case KMType.HMAC: - if (macLen == KMType.INVALID_VALUE) { - KMException.throwIt(KMError.MISSING_MAC_LENGTH); - } - if (macLen < KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, data[HW_PARAMETERS]) || - macLen > getMacSize(op.getDigest())) { - KMException.throwIt(KMError.INVALID_MAC_LENGTH); - } - op.setMacLength(macLen); + if (macLen == KMType.INVALID_VALUE) { + if(op.getPurpose() == KMType.SIGN) { + KMException.throwIt(KMError.MISSING_MAC_LENGTH); + } + } else { + //MAC length may not be specified for verify. + if(op.getPurpose() == KMType.VERIFY) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + if (macLen < KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, data[HW_PARAMETERS])) { + KMException.throwIt(KMError.INVALID_MAC_LENGTH); + } else if (macLen > KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, data[HW_PARAMETERS])) { + KMException.throwIt(KMError.UNSUPPORTED_MAC_LENGTH); + } + op.setMacLength(macLen); + } break; default: break; @@ -2447,7 +2567,8 @@ private void authorizeAndBeginOperation(KMOperationState op, byte[] scratchPad) if (KMByteBlob.cast(data[IV]).length() != 12 && op.getBlockMode() == KMType.GCM) { KMException.throwIt(KMError.INVALID_NONCE); } - if (op.getBlockMode() == KMType.CBC && op.getAlgorithm() == KMType.AES && + if ((op.getBlockMode() == KMType.CBC || op.getBlockMode() == KMType.CTR) + && op.getAlgorithm() == KMType.AES && KMByteBlob.cast(data[IV]).length() != 16) { KMException.throwIt(KMError.INVALID_NONCE); } @@ -2671,6 +2792,11 @@ private void beginSignVerifyOperation(KMOperationState op) { } break; case KMType.HMAC: + //For HMAC, either sign or verify we do sign operation only and we compare the + //signature manually. The reason for doing this is the TAG_MAC_LENGTH can be 32 bytes + //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(), @@ -2678,7 +2804,7 @@ private void beginSignVerifyOperation(KMOperationState op) { try{ op.setSignerVerifier( cryptoProvider.createHmacSignerVerifier( - purpose, digest, + Signature.MODE_SIGN, digest, KMByteBlob.cast(data[SECRET]).getBuffer(), KMByteBlob.cast(data[SECRET]).getStartOff(), KMByteBlob.cast(data[SECRET]).length())); @@ -2870,7 +2996,7 @@ private void importKey(APDU apdu, byte[] scratchPad) { if (tmpVariables[3] == KMType.INVALID_VALUE) { KMException.throwIt(KMError.INVALID_ARGUMENT); } - tmpVariables[4] = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.DIGEST,data[KEY_PARAMETERS]); + /*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); @@ -2899,7 +3025,7 @@ private void importKey(APDU apdu, byte[] scratchPad) { 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: @@ -2955,13 +3081,21 @@ private void importECKeys(byte[] scratchPad) { KMByteBlob.cast(data[SECRET]).getStartOff(), KMByteBlob.cast(data[SECRET]).length()); tmpVariables[4] = 0; // index for update list in scratchPad + // check whether the keysize tag is present in key parameters. tmpVariables[2] = KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); if (tmpVariables[2] != KMType.INVALID_VALUE) { - if (tmpVariables[2] != 256) { + //As per NIST.SP.800-186 page 9, secret for 256 curve should be between + //256-383 + if(((256 <= (short)(KMByteBlob.cast(data[SECRET]).length()*8)) && + (383 >= (short)(KMByteBlob.cast(data[SECRET]).length()*8))) ^ + tmpVariables[2] == 256) { KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH); } + if (tmpVariables[2] != 256) { + KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); + } } else { // add the key size to scratchPad tmpVariables[5] = KMInteger.uint_16((short) 256); @@ -2972,6 +3106,13 @@ private void importECKeys(byte[] scratchPad) { // check the curve if present in key parameters. tmpVariables[3] = KMEnumTag.getValue(KMType.ECCURVE, data[KEY_PARAMETERS]); if (tmpVariables[3] != KMType.INVALID_VALUE) { + //As per NIST.SP.800-186 page 9, secret length for 256 curve should be between + //256-383 + if(((256 <= (short)(KMByteBlob.cast(data[SECRET]).length()*8)) && + (383 >= (short)(KMByteBlob.cast(data[SECRET]).length()*8))) ^ + tmpVariables[3] == KMType.P_256) { + KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH); + } if (tmpVariables[3] != KMType.P_256) { KMException.throwIt(KMError.UNSUPPORTED_EC_CURVE); } @@ -3134,7 +3275,7 @@ private void importRSAKey(byte[] scratchPad) { if (tmpVariables[2] != 4 || Util.getShort(scratchPad, (short) 10) != 0x01 || Util.getShort(scratchPad, (short) 12) != 0x01) { - KMException.throwIt(KMError.INVALID_ARGUMENT); + KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH); } } else { // add public exponent to scratchPad @@ -3160,7 +3301,7 @@ private void importRSAKey(byte[] scratchPad) { tmpVariables[2] = KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); if (tmpVariables[2] != KMType.INVALID_VALUE) { - if (tmpVariables[2] != 2048) { + if (tmpVariables[2] != 2048 || tmpVariables[2] != (short)(KMByteBlob.cast(data[SECRET]).length()*8)) { KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH); } } else { @@ -3327,8 +3468,9 @@ 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]); + /*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); @@ -3357,7 +3499,7 @@ private static void processGenerateKey(APDU apdu) { 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])){ @@ -3397,6 +3539,7 @@ private static void processGenerateKey(APDU apdu) { KMArray.cast(tmpVariables[0]).add((short) 2, data[KEY_CHARACTERISTICS]); // Encode the response bufferLength = encoder.encode(tmpVariables[0], buffer, bufferStartOffset); + sendOutgoing(apdu); } @@ -3478,10 +3621,12 @@ private static void validateAESKey(byte[] scratchPad) { (KMInteger.cast(tmpVariables[3]).getShort() % 8) != 0){ KMException.throwIt(KMError.UNSUPPORTED_MIN_MAC_LENGTH); } - }else{ // No GCM mode then no minimum mac length must be specified - if (tmpVariables[2] != KMTag.INVALID_VALUE) { - KMException.throwIt(KMError.INVALID_ARGUMENT); - } + }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); + //} } } } @@ -3519,6 +3664,16 @@ private static void generateECKeys(byte[] scratchPad) { data[SECRET] = KMByteBlob.instance(scratchPad, (short) 0, tmpVariables[5]); data[KEY_BLOB] = KMArray.instance((short) 5); KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_PUB_KEY, data[PUB_KEY]); +/* 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) { @@ -3676,12 +3831,16 @@ private static void parseEncryptedKeyBlob(byte[] scratchPad) { tmpVariables[2] = KMKeyCharacteristics.exp(); KMArray.cast(tmpVariables[1]).add(KMKeymasterApplet.KEY_BLOB_KEYCHAR, tmpVariables[2]); KMArray.cast(tmpVariables[1]).add(KMKeymasterApplet.KEY_BLOB_PUB_KEY, KMByteBlob.exp()); + try { data[KEY_BLOB] = decoder.decodeArray( tmpVariables[1], KMByteBlob.cast(data[KEY_BLOB]).getBuffer(), KMByteBlob.cast(data[KEY_BLOB]).getStartOff(), KMByteBlob.cast(data[KEY_BLOB]).length()); + } catch(ISOException e) { + KMException.throwIt(KMError.INVALID_KEY_BLOB); + } tmpVariables[0] = KMArray.cast(data[KEY_BLOB]).length(); if (tmpVariables[0] < 4) { KMException.throwIt(KMError.INVALID_KEY_BLOB); @@ -3803,8 +3962,13 @@ private static void makeAuthData(byte[] scratchPad) { tmpVariables[0] += addPtrToAAD( KMKeyParameters.cast(data[HIDDEN_PARAMETERS]).getVals(), scratchPad, tmpVariables[0]); + + if(KMArray.cast(data[KEY_BLOB]).length() == 5) { + tmpVariables[1] = KMArray.instance((short)(tmpVariables[0]+1)); + } else { + tmpVariables[1] = KMArray.instance(tmpVariables[0]); + } // convert scratch pad to KMArray - tmpVariables[1] = KMArray.instance(tmpVariables[0]); short index = 0; short objPtr = 0; while (index < tmpVariables[0]) { @@ -3812,6 +3976,10 @@ private static void makeAuthData(byte[] scratchPad) { KMArray.cast(tmpVariables[1]).add(index, objPtr); index++; } + if(KMArray.cast(data[KEY_BLOB]).length() == 5) { + KMArray.cast(tmpVariables[1]).add(index, data[PUB_KEY]); + } + data[AUTH_DATA] = repository.alloc(MAX_AUTH_DATA_SIZE); short len = encoder.encode(tmpVariables[1], repository.getHeap(), data[AUTH_DATA]); data[AUTH_DATA_LENGTH] = len; diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMOperationState.java b/Applet/Applet/src/com/android/javacard/keymaster/KMOperationState.java index 8f0cf77c..4987b595 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMOperationState.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMOperationState.java @@ -43,6 +43,7 @@ public class KMOperationState { private boolean secureUserIdReqd; private boolean authTimeoutValidated; private boolean aesGcmUpdateAllowed; + private boolean aesBlockSaved; private byte[] aesBlock; private short macLength; diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMCryptoProvider.java b/Applet/Applet/src/com/android/javacard/keymaster/KMSEProvider.java similarity index 56% rename from Applet/Applet/src/com/android/javacard/keymaster/KMCryptoProvider.java rename to Applet/Applet/src/com/android/javacard/keymaster/KMSEProvider.java index ba0ff9bc..5ef2517f 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMCryptoProvider.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMSEProvider.java @@ -9,22 +9,26 @@ import javacard.security.RSAPrivateKey; import javacard.security.Signature; -public interface KMCryptoProvider { +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); + short createSymmetricKey(byte alg, short keysize, byte[] buf, short startOff); - void newRandomNumber(byte[] num, short offset, short length); + // 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, @@ -60,8 +64,6 @@ boolean aesGCMDecrypt( short authTagStart, short authTagLen); - byte[] getTrueRandomNumber(short len); - short aesCCMSign( byte[] bufIn, short bufInStart, @@ -70,20 +72,40 @@ short aesCCMSign( byte[] bufOut, short bufStart); - ECPrivateKey createEcKey(byte[] privBuffer, short privOff, short privLength); - - HMACKey createHMACKey(byte[] secretBuffer, short secretOff, short secretLength); - - DESKey createTDESKey(byte[] secretBuffer, short secretOff, short secretLength); - - RSAPrivateKey createRsaKey(byte[] modBuffer, short modOff, short modLength, - byte[] privBuffer, short privOff, short privLength); - HMACKey cmacKdf(byte[] keyMaterial, byte[] label, byte[] context, short contextStart, short contextLength); + short 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); + 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); + + // 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, @@ -112,4 +134,20 @@ KMCipher createAesGcmCipher(short mode, short tagLen, byte[] secret, short secre Signature createRsaVerifier(short msgDigestAlg, short padding, byte[] modBuffer, short modOff, short modLength); Signature createEcVerifier(short msgDigestAlg, byte[] pubKey, short pubKeyStart, short pubKeyLength); + + short getSystemTimeInMilliSeconds(byte[] timeBuf, short timeStart, short timeOffset); + short addListener(KMEventListener listener, byte eventType); + short getEventData(byte[] eventBuf, short eventStart, short eventLength); + + //Capability query - should return true + boolean isAlgSupported(byte alg); + boolean isKeySizeSupported(byte alg, short keySize); + boolean isCurveSupported(byte eccurve); + boolean isDigestSupported(byte alg, byte digest); + boolean isPaddingSupported(byte alg, byte padding); + boolean isBlockModeSupported(byte alg, byte blockMode); + + //Capability query - may return true + boolean isSystemTimerSupported(); + boolean isBootEventSupported(); } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMType.java b/Applet/Applet/src/com/android/javacard/keymaster/KMType.java index 0aaeaaf4..a42d5f8c 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMType.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMType.java @@ -24,6 +24,9 @@ public abstract class KMType { public static final short INVALID_VALUE = (short)0x8000; protected static final byte TLV_HEADER_SIZE = 3; + //Event Type + public static final byte BOOT_EVENT = 0; + // Types public static final byte BYTE_BLOB_TYPE = 0x01; public static final byte INTEGER_TYPE = 0x02; diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMX509Certificate.java b/Applet/Applet/src/com/android/javacard/keymaster/KMX509Certificate.java index 12264793..cb5c99a2 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMX509Certificate.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMX509Certificate.java @@ -362,26 +362,52 @@ private static void pushKeyDescription() { private static void pushSWParams() { short last = stackPtr; - pushParams(swParams); + //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]); + }while(++index < tagIds.length); pushSequenceHeader((short) (last - stackPtr)); } private static void pushHWParams() { short last = stackPtr; - pushAttIds(); - pushRoT(); - pushParams(hwParams); + //Attestation ids are not included. As per VTS attestation ids are not supported currenlty. + short[] tagIds = {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(pushAttIds(tagIds[index])) continue; + if(tagIds[index] == KMType.ROOT_OF_TRUST) { + pushRoT(); + continue; + } + if(pushParams(hwParams,tagIds[index])) continue; + } while (++index < tagIds.length); pushSequenceHeader((short) (last - stackPtr)); } - private static void pushParams(short params) { + private static boolean pushParams(short params, short tagId) { short index = 0; short arr = KMKeyParameters.cast(params).getVals(); short len = KMArray.cast(arr).length(); while (index < len) { - pushTag(KMArray.cast(arr).get(index)); + short tag = KMArray.cast(arr).get(index); + if(tagId == KMTag.getKey(tag)) { + pushTag(tag); + return true; + } index++; } + return false; } private static void pushTag(short tag) { @@ -485,26 +511,31 @@ private static void pushBooleanHeader(short len) { pushByte((byte) 0x01); } // All Attestation Id tags are byte tags/octet strings - private static void pushAttIds() { - if (attAppId != 0) { + 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; + if(!repo.isAttIdSupported()) return true; byte index = 0; while (index < repo.ATT_ID_TABLE_SIZE) { if (repo.getAttIdLen(index) != 0) { - pushBytesTag( - repo.getAttIdTag(index), - repo.getAttIdBuffer(index), - repo.getAttIdOffset(index), - repo.getAttIdLen(index)); + if(tagId == repo.getAttIdTag(index)) { + pushBytesTag( + repo.getAttIdTag(index), + repo.getAttIdBuffer(index), + repo.getAttIdOffset(index), + repo.getAttIdLen(index)); + return true; + } } index++; } + return false; } // Only SET of INTEGERS supported are padding, digest, purpose and blockmode diff --git a/Applet/Applet/test/com/android/javacard/test/KMFrameworkTest.java b/Applet/Applet/test/com/android/javacard/test/KMFrameworkTest.java index ec7c4661..4ace527b 100644 --- a/Applet/Applet/test/com/android/javacard/test/KMFrameworkTest.java +++ b/Applet/Applet/test/com/android/javacard/test/KMFrameworkTest.java @@ -20,8 +20,8 @@ import com.android.javacard.keymaster.KMBoolTag; import com.android.javacard.keymaster.KMByteBlob; import com.android.javacard.keymaster.KMByteTag; -import com.android.javacard.keymaster.KMCryptoProvider; -import com.android.javacard.keymaster.KMCryptoProviderImpl; +import com.android.javacard.keymaster.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; @@ -84,13 +84,13 @@ public class KMFrameworkTest { private short status; private short keyCharacteristics; private short keyBlob; - private KMCryptoProvider sim; + private KMSEProvider sim; @Test public void test_Lifecycle_Success() { // Create simulator //KMJcardSimulator.jcardSim = true; - sim = KMCryptoProviderImpl.instance(); + sim = KMSEProviderImpl.instance(); sim.bypassAesGcm(); CardSimulator simulator = new CardSimulator(); @@ -202,7 +202,7 @@ public void testProvisionCmd(CardSimulator simulator){ ResponseAPDU response = simulator.transmitCommand(commandAPDU); Assert.assertEquals(0x9000, response.getSW()); */ - KMCryptoProvider cryptoProvider = KMCryptoProviderImpl.instance(); + KMSEProvider cryptoProvider = KMSEProviderImpl.instance(); KMEncoder encoder = new KMEncoder(); KeyPair rsaKeyPair = cryptoProvider.createRsaKeyPair(); byte[] pub = new byte[4]; diff --git a/Applet/Applet/test/com/android/javacard/test/KMFunctionalTest.java b/Applet/Applet/test/com/android/javacard/test/KMFunctionalTest.java index 6c020efd..e615dd9f 100644 --- a/Applet/Applet/test/com/android/javacard/test/KMFunctionalTest.java +++ b/Applet/Applet/test/com/android/javacard/test/KMFunctionalTest.java @@ -20,8 +20,8 @@ import com.android.javacard.keymaster.KMBoolTag; import com.android.javacard.keymaster.KMByteBlob; import com.android.javacard.keymaster.KMByteTag; -import com.android.javacard.keymaster.KMCryptoProvider; -import com.android.javacard.keymaster.KMCryptoProviderImpl; +import com.android.javacard.keymaster.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; @@ -80,15 +80,15 @@ public class KMFunctionalTest { 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 KMCryptoProvider sim; + private KMSEProvider sim; private CardSimulator simulator; private KMEncoder encoder; private KMDecoder decoder; - private KMCryptoProvider cryptoProvider; + private KMSEProvider cryptoProvider; public KMFunctionalTest(){ - cryptoProvider = KMCryptoProviderImpl.instance(); - sim = KMCryptoProviderImpl.instance(); + cryptoProvider = KMSEProviderImpl.instance(); + sim = KMSEProviderImpl.instance(); simulator = new CardSimulator(); encoder = new KMEncoder(); decoder = new KMDecoder(); @@ -923,7 +923,7 @@ public void testHmacGenerateKeySuccess() { tag = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.DIGEST, hwParams); Assert.assertTrue(KMEnumArrayTag.cast(tag).contains(KMType.SHA2_256)); tag = KMKeyParameters.findTag(KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, hwParams); - Assert.assertEquals(KMInteger.cast(KMIntegerTag.cast(tag).getValue()).getShort(), 128); + Assert.assertEquals(KMInteger.cast(KMIntegerTag.cast(tag).getValue()).getShort(), 160); tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ALGORITHM, hwParams); Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.HMAC); tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ORIGIN, hwParams); @@ -944,7 +944,7 @@ public short generateHmacKey(byte[] clientId, byte[] appData){ KMByteBlob.cast(byteBlob).add((short)1, KMType.VERIFY); short purpose = KMEnumArrayTag.instance(KMType.PURPOSE, byteBlob); short boolTag = KMBoolTag.instance(KMType.NO_AUTH_REQUIRED); - short minMacLen = KMIntegerTag.instance(KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, KMInteger.uint_16((short)128)); + short minMacLen = KMIntegerTag.instance(KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, KMInteger.uint_16((short)/*256*/160)); short tagIndex = 0; KMArray.cast(arrPtr).add(tagIndex++, minMacLen); KMArray.cast(arrPtr).add(tagIndex++, purpose); @@ -1828,6 +1828,7 @@ public void testEncryptDecryptWithAesDes(byte alg, byte blockMode, byte padding, ); inParams = getAesDesParams(alg,blockMode, padding, nonce); keyBlobPtr = KMArray.cast(ret).get((short)2); + //print(keyBlobPtr); byte[] cipherData = new byte[KMByteBlob.cast(keyBlobPtr).length()]; Util.arrayCopyNonAtomic(KMByteBlob.cast(keyBlobPtr).getBuffer(), KMByteBlob.cast(keyBlobPtr).getStartOff(), cipherData,(short)0, (short)cipherData.length); @@ -1838,6 +1839,8 @@ public void testEncryptDecryptWithAesDes(byte alg, byte blockMode, byte padding, (short)0,null,update, aesGcmFlag ); keyBlobPtr = KMArray.cast(ret).get((short)2); + //print(plainData,(short)0,(short)plainData.length); + //print(keyBlobPtr); short equal = Util.arrayCompare(plainData,(short)0,KMByteBlob.cast(keyBlobPtr).getBuffer(), KMByteBlob.cast(keyBlobPtr).getStartOff(),(short)plainData.length); Assert.assertTrue(equal == 0); @@ -1948,7 +1951,7 @@ public void testSignVerifyWithHmac(byte digest, boolean update){ byte[] keyBlob= new byte[KMByteBlob.cast(keyBlobPtr).length()]; Util.arrayCopyNonAtomic(KMByteBlob.cast(keyBlobPtr).getBuffer(), KMByteBlob.cast(keyBlobPtr).getStartOff(), keyBlob,(short)0, (short)keyBlob.length); - short inParams = getHmacParams(digest); + short inParams = getHmacParams(digest,true); byte[] plainData = "Hello World 123!".getBytes(); if(update) plainData= "Hello World 123! Hip Hip Hoorah!".getBytes(); //Sign @@ -1958,7 +1961,7 @@ public void testSignVerifyWithHmac(byte digest, boolean update){ KMKeyParameters.instance(inParams), (short)0,null,update,false ); - inParams = getHmacParams(digest); + inParams = getHmacParams(digest,false); keyBlobPtr = KMArray.cast(ret).get((short)2); byte[] signatureData = new byte[KMByteBlob.cast(keyBlobPtr).length()]; Util.arrayCopyNonAtomic(KMByteBlob.cast(keyBlobPtr).getBuffer(), KMByteBlob.cast(keyBlobPtr).getStartOff(), @@ -2035,13 +2038,15 @@ private short getEcParams(byte digest) { KMArray.cast(inParams).add((short)0, KMEnumArrayTag.instance(KMType.DIGEST, byteBlob)); return inParams; } - private short getHmacParams(byte digest) { - short inParams = KMArray.instance((short)2); + private short getHmacParams(byte digest, boolean sign) { + short paramsize = (short) (sign ? 2 : 1); + short inParams = KMArray.instance((short)paramsize); short byteBlob = KMByteBlob.instance((short)1); KMByteBlob.cast(byteBlob).add((short)0, digest); KMArray.cast(inParams).add((short)0, KMEnumArrayTag.instance(KMType.DIGEST, byteBlob)); - short macLength = KMIntegerTag.instance(KMType.UINT_TAG,KMType.MAC_LENGTH, KMInteger.uint_16((short)256)); - KMArray.cast(inParams).add((short)1, macLength); + short macLength = KMIntegerTag.instance(KMType.UINT_TAG,KMType.MAC_LENGTH, KMInteger.uint_16((short)/*256*/160)); + if(sign) + KMArray.cast(inParams).add((short)1, macLength); return inParams; } @@ -2062,8 +2067,16 @@ public short processMessage( byte[] outputData = new byte[128]; short len=0; inParams = 0; + //Test + short firstDataLen =16; + if (keyPurpose == KMType.DECRYPT) { + firstDataLen = 32; + } + + //Test + if (updateFlag) { - dataPtr = KMByteBlob.instance(data, (short) 0, (short) 16); + dataPtr = KMByteBlob.instance(data, (short) 0, (short) /*16*/firstDataLen); if(aesGcmFlag){ byte[] authData = "AuthData".getBytes(); short associatedData = KMByteBlob.instance(authData,(short)0,(short)authData.length); @@ -2084,7 +2097,7 @@ public short processMessage( len = KMByteBlob.cast(dataPtr).length(); dataPtr = KMByteBlob.instance(data, len, (short) (data.length - len)); }else{ - dataPtr = KMByteBlob.instance(data, (short)16, (short) (data.length - 16)); + dataPtr = KMByteBlob.instance(data, (short)/*16*/firstDataLen, (short) (data.length - /*16*/firstDataLen)); } }