diff --git a/Applet/JavaCardKeymaster.opt b/Applet/AndroidSEProvider/AndroidSE_3_0_5.opt similarity index 61% rename from Applet/JavaCardKeymaster.opt rename to Applet/AndroidSEProvider/AndroidSE_3_0_5.opt index 77c1931e..ba998254 100644 --- a/Applet/JavaCardKeymaster.opt +++ b/Applet/AndroidSEProvider/AndroidSE_3_0_5.opt @@ -1,5 +1,5 @@ -out EXP JCA CAP --exportpath api_export_files_3.1.0 --applet 0xa0:0x0:0x0:0x0:0x62:0x3:0x1:0xc:0x1:0x1 com.android.javacard.keymaster.KMKeymasterApplet +-exportpath ../../AndroidSEProvider/api_export_files_3.0.5 +-applet 0xa0:0x0:0x0:0x0:0x62:0x3:0x1:0xc:0x1:0x1 com.android.javacard.keymaster.KMAndroidSEApplet com.android.javacard.keymaster 0xa0:0x0:0x0:0x0:0x62:0x3:0x1:0xc:0x1 1.0 diff --git a/Applet/AndroidSEProvider/AndroidSE_3_1_0.opt b/Applet/AndroidSEProvider/AndroidSE_3_1_0.opt new file mode 100644 index 00000000..3de07eb5 --- /dev/null +++ b/Applet/AndroidSEProvider/AndroidSE_3_1_0.opt @@ -0,0 +1,5 @@ +-out EXP JCA CAP +-exportpath ../../AndroidSEProvider/api_export_files_3.1.0 +-applet 0xa0:0x0:0x0:0x0:0x62:0x3:0x1:0xc:0x1:0x1 com.android.javacard.keymaster.KMAndroidSEApplet +com.android.javacard.keymaster +0xa0:0x0:0x0:0x0:0x62:0x3:0x1:0xc:0x1 1.0 diff --git a/Applet/AndroidSEProvider/api_export_files_3.0.5/java/io/javacard/io.exp b/Applet/AndroidSEProvider/api_export_files_3.0.5/java/io/javacard/io.exp new file mode 100644 index 00000000..931133af Binary files /dev/null and b/Applet/AndroidSEProvider/api_export_files_3.0.5/java/io/javacard/io.exp differ diff --git a/Applet/AndroidSEProvider/api_export_files_3.0.5/java/lang/javacard/lang.exp b/Applet/AndroidSEProvider/api_export_files_3.0.5/java/lang/javacard/lang.exp new file mode 100644 index 00000000..f3498186 Binary files /dev/null and b/Applet/AndroidSEProvider/api_export_files_3.0.5/java/lang/javacard/lang.exp differ diff --git a/Applet/AndroidSEProvider/api_export_files_3.0.5/java/rmi/javacard/rmi.exp b/Applet/AndroidSEProvider/api_export_files_3.0.5/java/rmi/javacard/rmi.exp new file mode 100644 index 00000000..209cdf37 Binary files /dev/null and b/Applet/AndroidSEProvider/api_export_files_3.0.5/java/rmi/javacard/rmi.exp differ diff --git a/Applet/api_export_files_3.0.5/javacard/framework/javacard/framework.exp b/Applet/AndroidSEProvider/api_export_files_3.0.5/javacard/framework/javacard/framework.exp similarity index 76% rename from Applet/api_export_files_3.0.5/javacard/framework/javacard/framework.exp rename to Applet/AndroidSEProvider/api_export_files_3.0.5/javacard/framework/javacard/framework.exp index f604261a..fd14eac7 100644 Binary files a/Applet/api_export_files_3.0.5/javacard/framework/javacard/framework.exp and b/Applet/AndroidSEProvider/api_export_files_3.0.5/javacard/framework/javacard/framework.exp differ diff --git a/Applet/api_export_files_3.0.5/javacard/framework/service/javacard/service.exp b/Applet/AndroidSEProvider/api_export_files_3.0.5/javacard/framework/service/javacard/service.exp similarity index 76% rename from Applet/api_export_files_3.0.5/javacard/framework/service/javacard/service.exp rename to Applet/AndroidSEProvider/api_export_files_3.0.5/javacard/framework/service/javacard/service.exp index e83ae8a4..8fbef2f6 100644 Binary files a/Applet/api_export_files_3.0.5/javacard/framework/service/javacard/service.exp and b/Applet/AndroidSEProvider/api_export_files_3.0.5/javacard/framework/service/javacard/service.exp differ diff --git a/Applet/api_export_files_3.0.5/javacard/security/javacard/security.exp b/Applet/AndroidSEProvider/api_export_files_3.0.5/javacard/security/javacard/security.exp similarity index 67% rename from Applet/api_export_files_3.0.5/javacard/security/javacard/security.exp rename to Applet/AndroidSEProvider/api_export_files_3.0.5/javacard/security/javacard/security.exp index 9b2b8d44..39e57627 100644 Binary files a/Applet/api_export_files_3.0.5/javacard/security/javacard/security.exp and b/Applet/AndroidSEProvider/api_export_files_3.0.5/javacard/security/javacard/security.exp differ diff --git a/Applet/AndroidSEProvider/api_export_files_3.0.5/javacardx/apdu/javacard/apdu.exp b/Applet/AndroidSEProvider/api_export_files_3.0.5/javacardx/apdu/javacard/apdu.exp new file mode 100644 index 00000000..c9183d4f Binary files /dev/null and b/Applet/AndroidSEProvider/api_export_files_3.0.5/javacardx/apdu/javacard/apdu.exp differ diff --git a/Applet/api_export_files_3.1.0/javacardx/apdu/util/javacard/util.exp b/Applet/AndroidSEProvider/api_export_files_3.0.5/javacardx/apdu/util/javacard/util.exp similarity index 52% rename from Applet/api_export_files_3.1.0/javacardx/apdu/util/javacard/util.exp rename to Applet/AndroidSEProvider/api_export_files_3.0.5/javacardx/apdu/util/javacard/util.exp index cf2fc718..6a1e9e1e 100644 Binary files a/Applet/api_export_files_3.1.0/javacardx/apdu/util/javacard/util.exp and b/Applet/AndroidSEProvider/api_export_files_3.0.5/javacardx/apdu/util/javacard/util.exp differ diff --git a/Applet/api_export_files_3.0.5/javacardx/biometry/javacard/biometry.exp b/Applet/AndroidSEProvider/api_export_files_3.0.5/javacardx/biometry/javacard/biometry.exp similarity index 60% rename from Applet/api_export_files_3.0.5/javacardx/biometry/javacard/biometry.exp rename to Applet/AndroidSEProvider/api_export_files_3.0.5/javacardx/biometry/javacard/biometry.exp index f1779d01..fb46fa30 100644 Binary files a/Applet/api_export_files_3.0.5/javacardx/biometry/javacard/biometry.exp and b/Applet/AndroidSEProvider/api_export_files_3.0.5/javacardx/biometry/javacard/biometry.exp differ diff --git a/Applet/api_export_files_3.0.5/javacardx/biometry1toN/javacard/biometry1toN.exp b/Applet/AndroidSEProvider/api_export_files_3.0.5/javacardx/biometry1toN/javacard/biometry1toN.exp similarity index 68% rename from Applet/api_export_files_3.0.5/javacardx/biometry1toN/javacard/biometry1toN.exp rename to Applet/AndroidSEProvider/api_export_files_3.0.5/javacardx/biometry1toN/javacard/biometry1toN.exp index 057adb00..88527985 100644 Binary files a/Applet/api_export_files_3.0.5/javacardx/biometry1toN/javacard/biometry1toN.exp and b/Applet/AndroidSEProvider/api_export_files_3.0.5/javacardx/biometry1toN/javacard/biometry1toN.exp differ diff --git a/Applet/api_export_files_3.0.5/javacardx/crypto/javacard/crypto.exp b/Applet/AndroidSEProvider/api_export_files_3.0.5/javacardx/crypto/javacard/crypto.exp similarity index 92% rename from Applet/api_export_files_3.0.5/javacardx/crypto/javacard/crypto.exp rename to Applet/AndroidSEProvider/api_export_files_3.0.5/javacardx/crypto/javacard/crypto.exp index b6f6b2f8..e1ff132d 100644 Binary files a/Applet/api_export_files_3.0.5/javacardx/crypto/javacard/crypto.exp and b/Applet/AndroidSEProvider/api_export_files_3.0.5/javacardx/crypto/javacard/crypto.exp differ diff --git a/Applet/api_export_files_3.0.5/javacardx/external/javacard/external.exp b/Applet/AndroidSEProvider/api_export_files_3.0.5/javacardx/external/javacard/external.exp similarity index 67% rename from Applet/api_export_files_3.0.5/javacardx/external/javacard/external.exp rename to Applet/AndroidSEProvider/api_export_files_3.0.5/javacardx/external/javacard/external.exp index cd85799e..4af91e53 100644 Binary files a/Applet/api_export_files_3.0.5/javacardx/external/javacard/external.exp and b/Applet/AndroidSEProvider/api_export_files_3.0.5/javacardx/external/javacard/external.exp differ diff --git a/Applet/api_export_files_3.1.0/javacardx/framework/math/javacard/math.exp b/Applet/AndroidSEProvider/api_export_files_3.0.5/javacardx/framework/math/javacard/math.exp similarity index 63% rename from Applet/api_export_files_3.1.0/javacardx/framework/math/javacard/math.exp rename to Applet/AndroidSEProvider/api_export_files_3.0.5/javacardx/framework/math/javacard/math.exp index af2f608c..89a1ac6c 100644 Binary files a/Applet/api_export_files_3.1.0/javacardx/framework/math/javacard/math.exp and b/Applet/AndroidSEProvider/api_export_files_3.0.5/javacardx/framework/math/javacard/math.exp differ diff --git a/Applet/api_export_files_3.0.5/javacardx/framework/string/javacard/string.exp b/Applet/AndroidSEProvider/api_export_files_3.0.5/javacardx/framework/string/javacard/string.exp similarity index 67% rename from Applet/api_export_files_3.0.5/javacardx/framework/string/javacard/string.exp rename to Applet/AndroidSEProvider/api_export_files_3.0.5/javacardx/framework/string/javacard/string.exp index ef6c5cb7..7a3b73c3 100644 Binary files a/Applet/api_export_files_3.0.5/javacardx/framework/string/javacard/string.exp and b/Applet/AndroidSEProvider/api_export_files_3.0.5/javacardx/framework/string/javacard/string.exp differ diff --git a/Applet/api_export_files_3.0.5/javacardx/framework/tlv/javacard/tlv.exp b/Applet/AndroidSEProvider/api_export_files_3.0.5/javacardx/framework/tlv/javacard/tlv.exp similarity index 72% rename from Applet/api_export_files_3.0.5/javacardx/framework/tlv/javacard/tlv.exp rename to Applet/AndroidSEProvider/api_export_files_3.0.5/javacardx/framework/tlv/javacard/tlv.exp index 5a6d027c..58495fb0 100644 Binary files a/Applet/api_export_files_3.0.5/javacardx/framework/tlv/javacard/tlv.exp and b/Applet/AndroidSEProvider/api_export_files_3.0.5/javacardx/framework/tlv/javacard/tlv.exp differ diff --git a/Applet/AndroidSEProvider/api_export_files_3.0.5/javacardx/framework/util/intx/javacard/intx.exp b/Applet/AndroidSEProvider/api_export_files_3.0.5/javacardx/framework/util/intx/javacard/intx.exp new file mode 100644 index 00000000..881a9612 Binary files /dev/null and b/Applet/AndroidSEProvider/api_export_files_3.0.5/javacardx/framework/util/intx/javacard/intx.exp differ diff --git a/Applet/api_export_files_3.0.5/javacardx/framework/util/javacard/util.exp b/Applet/AndroidSEProvider/api_export_files_3.0.5/javacardx/framework/util/javacard/util.exp similarity index 68% rename from Applet/api_export_files_3.0.5/javacardx/framework/util/javacard/util.exp rename to Applet/AndroidSEProvider/api_export_files_3.0.5/javacardx/framework/util/javacard/util.exp index 25358d9d..a1ce7e96 100644 Binary files a/Applet/api_export_files_3.0.5/javacardx/framework/util/javacard/util.exp and b/Applet/AndroidSEProvider/api_export_files_3.0.5/javacardx/framework/util/javacard/util.exp differ diff --git a/Applet/api_export_files_3.1.0/javacardx/security/javacard/security.exp b/Applet/AndroidSEProvider/api_export_files_3.0.5/javacardx/security/javacard/security.exp similarity index 59% rename from Applet/api_export_files_3.1.0/javacardx/security/javacard/security.exp rename to Applet/AndroidSEProvider/api_export_files_3.0.5/javacardx/security/javacard/security.exp index 30b3ed6e..5153a68d 100644 Binary files a/Applet/api_export_files_3.1.0/javacardx/security/javacard/security.exp and b/Applet/AndroidSEProvider/api_export_files_3.0.5/javacardx/security/javacard/security.exp differ diff --git a/Applet/AndroidSEProvider/api_export_files_3.0.5/org/globalplatform/upgrade/javacard/upgrade.exp b/Applet/AndroidSEProvider/api_export_files_3.0.5/org/globalplatform/upgrade/javacard/upgrade.exp new file mode 100644 index 00000000..110117b2 Binary files /dev/null and b/Applet/AndroidSEProvider/api_export_files_3.0.5/org/globalplatform/upgrade/javacard/upgrade.exp differ diff --git a/Applet/AndroidSEProvider/api_export_files_3.0.5/org/globalplatform/upgrade/javacard/upgrade.jca b/Applet/AndroidSEProvider/api_export_files_3.0.5/org/globalplatform/upgrade/javacard/upgrade.jca new file mode 100644 index 00000000..9bb1e3b8 --- /dev/null +++ b/Applet/AndroidSEProvider/api_export_files_3.0.5/org/globalplatform/upgrade/javacard/upgrade.jca @@ -0,0 +1,245 @@ +// converted by version 1.3 +// on Wed Feb 14 10:43:54 CET 2018 + +.package org/globalplatform/upgrade { + .aid 0xA0:0x0:0x0:0x1:0x51:0x7; + .version 1.1; + + .imports { + 0xA0:0x0:0x0:0x0:0x62:0x0:0x1 1.0; //java/lang + } + + .constantPool { + // 0 + staticMethodRef 0.0.0()V; // java/lang/Object.()V + } + + .interface public abstract Element 0 { + + .fields { + public static final byte TYPE_SIMPLE = 1; // B + public static final byte TYPE_MAPPED = 2; // B + public static final short SIZE_BOOLEAN = 1; // S + public static final short SIZE_BYTE = 1; // S + public static final short SIZE_SHORT = 2; // S + } + + .method public abstract write(Z)Lorg/globalplatform/upgrade/Element; 0 { + } + + .method public abstract write(B)Lorg/globalplatform/upgrade/Element; 1 { + } + + .method public abstract write(S)Lorg/globalplatform/upgrade/Element; 2 { + } + + .method public abstract write(Ljava/lang/Object;)Lorg/globalplatform/upgrade/Element; 3 { + .descriptor Ljava/lang/Object; 0.0; + + } + + .method public abstract canWriteBoolean()S 4 { + } + + .method public abstract canWriteByte()S 5 { + } + + .method public abstract canWriteShort()S 6 { + } + + .method public abstract canWriteObject()S 7 { + } + + .method public abstract initRead()V 8 { + } + + .method public abstract readBoolean()Z 9 { + } + + .method public abstract readByte()B 10 { + } + + .method public abstract readShort()S 11 { + } + + .method public abstract readObject()Ljava/lang/Object; 12 { + .descriptor Ljava/lang/Object; 0.0; + + } + + .method public abstract canReadBoolean()S 13 { + } + + .method public abstract canReadByte()S 14 { + } + + .method public abstract canReadShort()S 15 { + } + + .method public abstract canReadObject()S 16 { + } + + } + + .interface public abstract MappedElement 1 { + + .superInterfaces { + Element; + } + + .method public abstract write(Z)Lorg/globalplatform/upgrade/Element; 0 { + } + + .method public abstract write(B)Lorg/globalplatform/upgrade/Element; 1 { + } + + .method public abstract write(S)Lorg/globalplatform/upgrade/Element; 2 { + } + + .method public abstract write(Ljava/lang/Object;)Lorg/globalplatform/upgrade/Element; 3 { + .descriptor Ljava/lang/Object; 0.0; + + } + + .method public abstract canWriteBoolean()S 4 { + } + + .method public abstract canWriteByte()S 5 { + } + + .method public abstract canWriteShort()S 6 { + } + + .method public abstract canWriteObject()S 7 { + } + + .method public abstract initRead()V 8 { + } + + .method public abstract readBoolean()Z 9 { + } + + .method public abstract readByte()B 10 { + } + + .method public abstract readShort()S 11 { + } + + .method public abstract readObject()Ljava/lang/Object; 12 { + .descriptor Ljava/lang/Object; 0.0; + + } + + .method public abstract canReadBoolean()S 13 { + } + + .method public abstract canReadByte()S 14 { + } + + .method public abstract canReadShort()S 15 { + } + + .method public abstract canReadObject()S 16 { + } + + .method public abstract getMappedObject()Ljava/lang/Object; 17 { + .descriptor Ljava/lang/Object; 0.0; + + } + + .method public abstract setMappedObject(Ljava/lang/Object;)Lorg/globalplatform/upgrade/Element; 18 { + .descriptor Ljava/lang/Object; 0.0; + + } + + } + + .interface public abstract OnUpgradeListener 2 { + + .method public abstract onSave()Lorg/globalplatform/upgrade/Element; 0 { + } + + .method public abstract onCleanup()V 1 { + } + + .method public abstract onRestore(Lorg/globalplatform/upgrade/Element;)V 2 { + } + + .method public abstract onConsolidate()V 3 { + } + + } + + .class public final UpgradeManager 3 extends 0.0 { // extends java/lang/Object + + .publicMethodTable 1 { + equals(Ljava/lang/Object;)Z; + } + + .packageMethodTable 0 { + } + + .method private ()V { + .stack 1; + .locals 0; + + L0: aload_0; + invokespecial 0; // java/lang/Object.()V + return; + } + + .method public static isUpgrading()Z 0 { + .stack 1; + .locals 0; + + L0: sconst_0; + sreturn; + } + + .method public static getPreviousPackageVersion()S 1 { + .stack 1; + .locals 0; + + L0: sconst_0; + sreturn; + } + + .method public static checkPreviousPackageAID([BSB)Z 2 { + .stack 1; + .locals 0; + + L0: sconst_0; + sreturn; + } + + .method public static createElement(BSS)Lorg/globalplatform/upgrade/Element; 3 { + .stack 1; + .locals 0; + + L0: aconst_null; + areturn; + } + + .method public static matchMappedElement(Ljava/lang/Object;)Lorg/globalplatform/upgrade/MappedElement; 4 { + .stack 1; + .locals 0; + + .descriptor Ljava/lang/Object; 0.0; + + L0: aconst_null; + areturn; + } + + .method public static nonNullReference()Ljava/lang/Object; 5 { + .stack 1; + .locals 0; + + .descriptor Ljava/lang/Object; 0.0; + + L0: aconst_null; + areturn; + } + + } + +} diff --git a/Applet/api_export_files_3.0.5/java/io/javacard/io.exp b/Applet/AndroidSEProvider/api_export_files_3.1.0/java/io/javacard/io.exp similarity index 100% rename from Applet/api_export_files_3.0.5/java/io/javacard/io.exp rename to Applet/AndroidSEProvider/api_export_files_3.1.0/java/io/javacard/io.exp diff --git a/Applet/api_export_files_3.0.5/java/lang/javacard/lang.exp b/Applet/AndroidSEProvider/api_export_files_3.1.0/java/lang/javacard/lang.exp similarity index 100% rename from Applet/api_export_files_3.0.5/java/lang/javacard/lang.exp rename to Applet/AndroidSEProvider/api_export_files_3.1.0/java/lang/javacard/lang.exp diff --git a/Applet/api_export_files_3.0.5/java/rmi/javacard/rmi.exp b/Applet/AndroidSEProvider/api_export_files_3.1.0/java/rmi/javacard/rmi.exp similarity index 100% rename from Applet/api_export_files_3.0.5/java/rmi/javacard/rmi.exp rename to Applet/AndroidSEProvider/api_export_files_3.1.0/java/rmi/javacard/rmi.exp diff --git a/Applet/api_export_files_3.1.0/javacard/framework/javacard/framework.exp b/Applet/AndroidSEProvider/api_export_files_3.1.0/javacard/framework/javacard/framework.exp similarity index 100% rename from Applet/api_export_files_3.1.0/javacard/framework/javacard/framework.exp rename to Applet/AndroidSEProvider/api_export_files_3.1.0/javacard/framework/javacard/framework.exp diff --git a/Applet/api_export_files_3.1.0/javacard/framework/service/javacard/service.exp b/Applet/AndroidSEProvider/api_export_files_3.1.0/javacard/framework/service/javacard/service.exp similarity index 100% rename from Applet/api_export_files_3.1.0/javacard/framework/service/javacard/service.exp rename to Applet/AndroidSEProvider/api_export_files_3.1.0/javacard/framework/service/javacard/service.exp diff --git a/Applet/api_export_files_3.1.0/javacard/security/javacard/security.exp b/Applet/AndroidSEProvider/api_export_files_3.1.0/javacard/security/javacard/security.exp similarity index 100% rename from Applet/api_export_files_3.1.0/javacard/security/javacard/security.exp rename to Applet/AndroidSEProvider/api_export_files_3.1.0/javacard/security/javacard/security.exp diff --git a/Applet/api_export_files_3.0.5/javacardx/apdu/javacard/apdu.exp b/Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/apdu/javacard/apdu.exp similarity index 100% rename from Applet/api_export_files_3.0.5/javacardx/apdu/javacard/apdu.exp rename to Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/apdu/javacard/apdu.exp diff --git a/Applet/api_export_files_3.0.5/javacardx/apdu/util/javacard/util.exp b/Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/apdu/util/javacard/util.exp similarity index 100% rename from Applet/api_export_files_3.0.5/javacardx/apdu/util/javacard/util.exp rename to Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/apdu/util/javacard/util.exp diff --git a/Applet/api_export_files_3.1.0/javacardx/biometry/javacard/biometry.exp b/Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/biometry/javacard/biometry.exp similarity index 100% rename from Applet/api_export_files_3.1.0/javacardx/biometry/javacard/biometry.exp rename to Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/biometry/javacard/biometry.exp diff --git a/Applet/api_export_files_3.1.0/javacardx/biometry1toN/javacard/biometry1toN.exp b/Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/biometry1toN/javacard/biometry1toN.exp similarity index 100% rename from Applet/api_export_files_3.1.0/javacardx/biometry1toN/javacard/biometry1toN.exp rename to Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/biometry1toN/javacard/biometry1toN.exp diff --git a/Applet/api_export_files_3.1.0/javacardx/crypto/javacard/crypto.exp b/Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/crypto/javacard/crypto.exp similarity index 100% rename from Applet/api_export_files_3.1.0/javacardx/crypto/javacard/crypto.exp rename to Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/crypto/javacard/crypto.exp diff --git a/Applet/api_export_files_3.1.0/javacardx/external/javacard/external.exp b/Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/external/javacard/external.exp similarity index 100% rename from Applet/api_export_files_3.1.0/javacardx/external/javacard/external.exp rename to Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/external/javacard/external.exp diff --git a/Applet/api_export_files_3.1.0/javacardx/framework/event/javacard/event.exp b/Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/framework/event/javacard/event.exp similarity index 100% rename from Applet/api_export_files_3.1.0/javacardx/framework/event/javacard/event.exp rename to Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/framework/event/javacard/event.exp diff --git a/Applet/api_export_files_3.0.5/javacardx/framework/math/javacard/math.exp b/Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/framework/math/javacard/math.exp similarity index 100% rename from Applet/api_export_files_3.0.5/javacardx/framework/math/javacard/math.exp rename to Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/framework/math/javacard/math.exp diff --git a/Applet/api_export_files_3.1.0/javacardx/framework/nio/javacard/nio.exp b/Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/framework/nio/javacard/nio.exp similarity index 100% rename from Applet/api_export_files_3.1.0/javacardx/framework/nio/javacard/nio.exp rename to Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/framework/nio/javacard/nio.exp diff --git a/Applet/api_export_files_3.1.0/javacardx/framework/string/javacard/string.exp b/Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/framework/string/javacard/string.exp similarity index 100% rename from Applet/api_export_files_3.1.0/javacardx/framework/string/javacard/string.exp rename to Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/framework/string/javacard/string.exp diff --git a/Applet/api_export_files_3.1.0/javacardx/framework/time/javacard/time.exp b/Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/framework/time/javacard/time.exp similarity index 100% rename from Applet/api_export_files_3.1.0/javacardx/framework/time/javacard/time.exp rename to Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/framework/time/javacard/time.exp diff --git a/Applet/api_export_files_3.1.0/javacardx/framework/tlv/javacard/tlv.exp b/Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/framework/tlv/javacard/tlv.exp similarity index 100% rename from Applet/api_export_files_3.1.0/javacardx/framework/tlv/javacard/tlv.exp rename to Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/framework/tlv/javacard/tlv.exp diff --git a/Applet/api_export_files_3.1.0/javacardx/framework/util/intx/javacard/intx.exp b/Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/framework/util/intx/javacard/intx.exp similarity index 100% rename from Applet/api_export_files_3.1.0/javacardx/framework/util/intx/javacard/intx.exp rename to Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/framework/util/intx/javacard/intx.exp diff --git a/Applet/api_export_files_3.1.0/javacardx/framework/util/javacard/util.exp b/Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/framework/util/javacard/util.exp similarity index 100% rename from Applet/api_export_files_3.1.0/javacardx/framework/util/javacard/util.exp rename to Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/framework/util/javacard/util.exp diff --git a/Applet/api_export_files_3.1.0/javacardx/security/cert/javacard/cert.exp b/Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/security/cert/javacard/cert.exp similarity index 100% rename from Applet/api_export_files_3.1.0/javacardx/security/cert/javacard/cert.exp rename to Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/security/cert/javacard/cert.exp diff --git a/Applet/api_export_files_3.1.0/javacardx/security/derivation/javacard/derivation.exp b/Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/security/derivation/javacard/derivation.exp similarity index 100% rename from Applet/api_export_files_3.1.0/javacardx/security/derivation/javacard/derivation.exp rename to Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/security/derivation/javacard/derivation.exp diff --git a/Applet/api_export_files_3.0.5/javacardx/security/javacard/security.exp b/Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/security/javacard/security.exp similarity index 100% rename from Applet/api_export_files_3.0.5/javacardx/security/javacard/security.exp rename to Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/security/javacard/security.exp diff --git a/Applet/api_export_files_3.1.0/javacardx/security/util/javacard/util.exp b/Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/security/util/javacard/util.exp similarity index 100% rename from Applet/api_export_files_3.1.0/javacardx/security/util/javacard/util.exp rename to Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/security/util/javacard/util.exp diff --git a/Applet/AndroidSEProvider/build.xml b/Applet/AndroidSEProvider/build.xml new file mode 100644 index 00000000..04b1dee6 --- /dev/null +++ b/Applet/AndroidSEProvider/build.xml @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Applet/AndroidSEProvider/lib/gpapi-upgrade.jar b/Applet/AndroidSEProvider/lib/gpapi-upgrade.jar new file mode 100644 index 00000000..e4814bde Binary files /dev/null and b/Applet/AndroidSEProvider/lib/gpapi-upgrade.jar differ diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java new file mode 100644 index 00000000..99dd47ce --- /dev/null +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java @@ -0,0 +1,71 @@ +package com.android.javacard.keymaster; + +import org.globalplatform.upgrade.Element; +import org.globalplatform.upgrade.OnUpgradeListener; +import org.globalplatform.upgrade.UpgradeManager; + +public class KMAndroidSEApplet extends KMKeymasterApplet implements OnUpgradeListener { + + KMAndroidSEApplet(){ + super(new KMAndroidSEProvider()); + } + /** + * Installs this applet. + * + * @param bArray the array containing installation parameters + * @param bOffset the starting offset in bArray + * @param bLength the length in bytes of the parameter data in bArray + */ + public static void install(byte[] bArray, short bOffset, byte bLength) { + new KMAndroidSEApplet().register(bArray, (short) (bOffset + 1), bArray[bOffset]); + } + + @Override + public void onCleanup() { + } + + @Override + public void onConsolidate() { + } + + @Override + public void onRestore(Element element) { + element.initRead(); + provisionStatus = element.readByte(); + keymasterState = element.readByte(); + repository.onRestore(element); + seProvider.onRestore(element); + } + + @Override + public Element onSave() { + // SEProvider count + short primitiveCount = seProvider.getBackupPrimitiveByteCount(); + short objectCount = seProvider.getBackupObjectCount(); + //Repository count + primitiveCount += repository.getBackupPrimitiveByteCount(); + objectCount += repository.getBackupObjectCount(); + //KMKeymasterApplet count + primitiveCount += computePrimitveDataSize(); + objectCount += computeObjectCount(); + + // Create element. + Element element = UpgradeManager.createElement(Element.TYPE_SIMPLE, + primitiveCount, objectCount); + element.write(provisionStatus); + element.write(keymasterState); + repository.onSave(element); + seProvider.onSave(element); + return element; + } + + private short computePrimitveDataSize() { + // provisionStatus + keymasterState + return (short) 2; + } + + private short computeObjectCount() { + return (short) 0; + } +} + diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEProvider.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEProvider.java new file mode 100644 index 00000000..9d9812c1 --- /dev/null +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEProvider.java @@ -0,0 +1,1213 @@ +/* + * 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" (short)0IS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.javacard.keymaster; + +import org.globalplatform.upgrade.Element; +import org.globalplatform.upgrade.UpgradeManager; + +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.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; + +public class KMAndroidSEProvider implements KMSEProvider { + // static final variables + // -------------------------------------------------------------- + // P-256 Curve Parameters + static final byte[] secp256r1_P = { + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF }; + + static final byte[] secp256r1_A = { + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFC }; + + static final byte[] secp256r1_B = { + (byte) 0x5A, (byte) 0xC6, (byte) 0x35, (byte) 0xD8, (byte) 0xAA, + (byte) 0x3A, (byte) 0x93, (byte) 0xE7, (byte) 0xB3, (byte) 0xEB, + (byte) 0xBD, (byte) 0x55, (byte) 0x76, (byte) 0x98, (byte) 0x86, + (byte) 0xBC, (byte) 0x65, (byte) 0x1D, (byte) 0x06, (byte) 0xB0, + (byte) 0xCC, (byte) 0x53, (byte) 0xB0, (byte) 0xF6, (byte) 0x3B, + (byte) 0xCE, (byte) 0x3C, (byte) 0x3E, (byte) 0x27, (byte) 0xD2, + (byte) 0x60, (byte) 0x4B }; + + static final byte[] secp256r1_S = { + (byte) 0xC4, (byte) 0x9D, (byte) 0x36, (byte) 0x08, (byte) 0x86, + (byte) 0xE7, (byte) 0x04, (byte) 0x93, (byte) 0x6A, (byte) 0x66, + (byte) 0x78, (byte) 0xE1, (byte) 0x13, (byte) 0x9D, (byte) 0x26, + (byte) 0xB7, (byte) 0x81, (byte) 0x9F, (byte) 0x7E, (byte) 0x90 }; + + // Uncompressed form + static final byte[] secp256r1_UCG = { + (byte) 0x04, (byte) 0x6B, (byte) 0x17, (byte) 0xD1, (byte) 0xF2, + (byte) 0xE1, (byte) 0x2C, (byte) 0x42, (byte) 0x47, (byte) 0xF8, + (byte) 0xBC, (byte) 0xE6, (byte) 0xE5, (byte) 0x63, (byte) 0xA4, + (byte) 0x40, (byte) 0xF2, (byte) 0x77, (byte) 0x03, (byte) 0x7D, + (byte) 0x81, (byte) 0x2D, (byte) 0xEB, (byte) 0x33, (byte) 0xA0, + (byte) 0xF4, (byte) 0xA1, (byte) 0x39, (byte) 0x45, (byte) 0xD8, + (byte) 0x98, (byte) 0xC2, (byte) 0x96, (byte) 0x4F, (byte) 0xE3, + (byte) 0x42, (byte) 0xE2, (byte) 0xFE, (byte) 0x1A, (byte) 0x7F, + (byte) 0x9B, (byte) 0x8E, (byte) 0xE7, (byte) 0xEB, (byte) 0x4A, + (byte) 0x7C, (byte) 0x0F, (byte) 0x9E, (byte) 0x16, (byte) 0x2B, + (byte) 0xCE, (byte) 0x33, (byte) 0x57, (byte) 0x6B, (byte) 0x31, + (byte) 0x5E, (byte) 0xCE, (byte) 0xCB, (byte) 0xB6, (byte) 0x40, + (byte) 0x68, (byte) 0x37, (byte) 0xBF, (byte) 0x51, (byte) 0xF5 }; + + static final byte[] secp256r1_N = { + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xBC, (byte) 0xE6, (byte) 0xFA, (byte) 0xAD, + (byte) 0xA7, (byte) 0x17, (byte) 0x9E, (byte) 0x84, (byte) 0xF3, + (byte) 0xB9, (byte) 0xCA, (byte) 0xC2, (byte) 0xFC, (byte) 0x63, + (byte) 0x25, (byte) 0x51 }; + static final short secp256r1_H = 1; + // -------------------------------------------------------------- + public static final short AES_GCM_TAG_LENGTH = 12; + public static final short AES_GCM_NONCE_LENGTH = 12; + public static final byte KEYSIZE_128_OFFSET = 0x00; + public static final byte KEYSIZE_256_OFFSET = 0x01; + public static final short TMP_ARRAY_SIZE = 256; + public static final short CERT_CHAIN_MAX_SIZE = 2050;//First 2 bytes for length. + + final byte[] CIPHER_ALGS = { + Cipher.ALG_AES_BLOCK_128_CBC_NOPAD, + Cipher.ALG_AES_BLOCK_128_ECB_NOPAD, + Cipher.ALG_DES_CBC_NOPAD, + Cipher.ALG_DES_ECB_NOPAD, + Cipher.ALG_AES_CTR, + Cipher.ALG_RSA_PKCS1, + KMRsaOAEPEncoding.ALG_RSA_PKCS1_OAEP_SHA256_MGF1_SHA1, + Cipher.ALG_RSA_NOPAD, + AEADCipher.ALG_AES_GCM }; + + final byte[] SIG_ALGS = { + Signature.ALG_RSA_SHA_256_PKCS1, + Signature.ALG_RSA_SHA_256_PKCS1_PSS, + Signature.ALG_ECDSA_SHA_256, + Signature.ALG_HMAC_SHA_256, + KMRsa2048NoDigestSignature.ALG_RSA_SIGN_NOPAD, + KMRsa2048NoDigestSignature.ALG_RSA_PKCS1_NODIGEST, + KMEcdsa256NoDigestSignature.ALG_ECDSA_NODIGEST}; + + // AESKey + private AESKey aesKeys[]; + // DES3Key + private DESKey triDesKey; + // HMACKey + private HMACKey hmacKey; + // RSA Key Pair + private KeyPair rsaKeyPair; + // EC Key Pair. + private KeyPair ecKeyPair; + // Temporary array. + public byte[] tmpArray; + // This is used for internal encryption/decryption operations. + private static AEADCipher aesGcmCipher; + // Cipher pool + private Object[] cipherPool; + // Signature pool + private Object[] sigPool; + // KMOperationImpl pool + private Object[] operationPool; + + private Signature kdf; + + private Signature hmacSignature; + //For ImportwrappedKey operations. + private KMRsaOAEPEncoding rsaOaepDecipher; + + // Entropy + private RandomData rng; + //For storing root certificate and intermediate certificates. + private byte[] certificateChain; + + private static KMAndroidSEProvider androidSEProvider = null; + + public static KMAndroidSEProvider getInstance() { + return androidSEProvider; + } + + public KMAndroidSEProvider() { + // Re-usable AES,DES and HMAC keys in persisted memory. + aesKeys = new AESKey[2]; + aesKeys[KEYSIZE_128_OFFSET] = (AESKey) KeyBuilder.buildKey( + KeyBuilder.TYPE_AES, KeyBuilder.LENGTH_AES_128, false); + aesKeys[KEYSIZE_256_OFFSET] = (AESKey) KeyBuilder.buildKey( + KeyBuilder.TYPE_AES, KeyBuilder.LENGTH_AES_256, false); + triDesKey = (DESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_DES, + KeyBuilder.LENGTH_DES3_3KEY, false); + hmacKey = (HMACKey) KeyBuilder.buildKey(KeyBuilder.TYPE_HMAC, (short) 512, + false); + rsaKeyPair = new KeyPair(KeyPair.ALG_RSA, KeyBuilder.LENGTH_RSA_2048); + initECKey(); + + // Re-usable cipher and signature instances + cipherPool = new Object[(short) (CIPHER_ALGS.length * 4)]; + sigPool = new Object[(short) (SIG_ALGS.length * 4)]; + operationPool = new Object[4]; + // Creates an instance of each cipher algorithm once. + initializeCipherPool(); + // Creates an instance of each signature algorithm once. + initializeSigPool(); + initializeOperationPool(); + //RsaOAEP Decipher + rsaOaepDecipher = new KMRsaOAEPEncoding(KMRsaOAEPEncoding.ALG_RSA_PKCS1_OAEP_SHA256_MGF1_SHA1); + + kdf = Signature.getInstance(Signature.ALG_AES_CMAC_128, false); + hmacSignature = Signature.getInstance(Signature.ALG_HMAC_SHA_256, false); + + // Temporary transient array created to use locally inside functions. + tmpArray = JCSystem.makeTransientByteArray(TMP_ARRAY_SIZE, + JCSystem.CLEAR_ON_DESELECT); + + // Random number generator initialisation. + rng = RandomData.getInstance(RandomData.ALG_KEYGENERATION); + //Allocate buffer for certificate chain. + if(!isUpgrading()) + certificateChain = new byte[CERT_CHAIN_MAX_SIZE]; + androidSEProvider = this; + } + + public void clean() { + Util.arrayFillNonAtomic(tmpArray, (short) 0, (short) 256, (byte) 0); + } + + private void initECKey() { + ecKeyPair = new KeyPair(KeyPair.ALG_EC_FP, KeyBuilder.LENGTH_EC_FP_256); + ECPrivateKey privKey = (ECPrivateKey) ecKeyPair.getPrivate(); + ECPublicKey pubkey = (ECPublicKey) ecKeyPair.getPublic(); + pubkey.setFieldFP(secp256r1_P, (short) 0, (short) secp256r1_P.length); + pubkey.setA(secp256r1_A, (short) 0, (short) secp256r1_A.length); + pubkey.setB(secp256r1_B, (short) 0, (short) secp256r1_B.length); + pubkey.setG(secp256r1_UCG, (short) 0, (short) secp256r1_UCG.length); + pubkey.setK(secp256r1_H); + pubkey.setR(secp256r1_N, (short) 0, (short) secp256r1_N.length); + + privKey.setFieldFP(secp256r1_P, (short) 0, (short) secp256r1_P.length); + privKey.setA(secp256r1_A, (short) 0, (short) secp256r1_A.length); + privKey.setB(secp256r1_B, (short) 0, (short) secp256r1_B.length); + privKey.setG(secp256r1_UCG, (short) 0, (short) secp256r1_UCG.length); + privKey.setK(secp256r1_H); + privKey.setR(secp256r1_N, (short) 0, (short) secp256r1_N.length); + } + + private boolean isCipherAlgorithm(byte alg) { + short index = 0; + while (index < CIPHER_ALGS.length) { + if (CIPHER_ALGS[index++] == alg) { + return true; + } + } + return false; + } + + private boolean isSignerAlgorithm(byte alg) { + short index = 0; + while (index < SIG_ALGS.length) { + if (SIG_ALGS[index++] == alg) { + return true; + } + } + return false; + } + + private void initializeOperationPool() { + short index = 0; + while (index < 4) { + operationPool[index] = new KMInstance(); + ((KMInstance) operationPool[index]).instanceCount = 1; + ((KMInstance) operationPool[index]).object = new KMOperationImpl(); + ((KMInstance) operationPool[index]).reserved = 0; + index++; + } + } + + // Create a signature instance of each algorithm once. + private void initializeSigPool() { + short index = 0; + while (index < SIG_ALGS.length) { + sigPool[index] = new KMInstance(); + ((KMInstance) sigPool[index]).instanceCount = 1; + ((KMInstance) sigPool[index]).object = getSignatureInstance(SIG_ALGS[index]); + ((KMInstance) sigPool[index]).reserved = 0; + index++; + } + } + + private Signature getSignatureInstance(byte alg) { + if (KMRsa2048NoDigestSignature.ALG_RSA_SIGN_NOPAD == alg + || KMRsa2048NoDigestSignature.ALG_RSA_PKCS1_NODIGEST == alg) { + return new KMRsa2048NoDigestSignature(alg); + } else if (KMEcdsa256NoDigestSignature.ALG_ECDSA_NODIGEST == alg) { + return new KMEcdsa256NoDigestSignature(alg); + } else { + return Signature.getInstance(alg, false); + } + } + + private Cipher getCipherInstance(byte alg) { + if (KMRsaOAEPEncoding.ALG_RSA_PKCS1_OAEP_SHA256_MGF1_SHA1 == alg) { + return new KMRsaOAEPEncoding(alg); + } else { + return Cipher.getInstance(alg, false); + } + } + + private byte getCipherAlgorithm(Cipher c) { + return c.getAlgorithm(); + } + + // Create a cipher instance of each algorithm once. + private void initializeCipherPool() { + short index = 0; + while (index < CIPHER_ALGS.length) { + cipherPool[index] = new KMInstance(); + ((KMInstance) cipherPool[index]).instanceCount = 1; + ((KMInstance) cipherPool[index]).object = getCipherInstance(CIPHER_ALGS[index]); + ((KMInstance) cipherPool[index]).reserved = 0; + index++; + } + } + + private KMOperationImpl getOperationInstanceFromPool() { + return (KMOperationImpl) getInstanceFromPool(operationPool, (byte) 0x00); + } + + public void releaseOperationInstance(KMOperationImpl operation) { + releaseInstance(operationPool, operation); + } + + private Signature getSignatureInstanceFromPool(byte alg) { + return (Signature) getInstanceFromPool(sigPool, alg); + } + + public void releaseSignatureInstance(Signature signer) { + releaseInstance(sigPool, signer); + } + + private Cipher getCipherInstanceFromPool(byte alg) { + return (Cipher) getInstanceFromPool(cipherPool, alg); + } + + public void releaseCipherInstance(Cipher cipher) { + releaseInstance(cipherPool, cipher); + } + + // This pool implementation can create a maximum of total 4 instances per + // algorithm. This function returns the unreserved Cipher/Signature instance + // of type algorithm from pool. If there is no unreserved cipher/signature + // instance of algorithm type in the pool and Cipher/Signature algorithm + // instance count is less than 4 then it creates and returns a new + // Cipher/Signature instance of algorithm type. If there is no unreserved + // cipher/signature and maximum instance count reaches four it throws + // exception. + private Object getInstanceFromPool(Object[] pool, byte alg) { + short index = 0; + short instanceCount = 0; + Object object = null; + boolean isCipher = isCipherAlgorithm(alg); + boolean isSigner = isSignerAlgorithm(alg); + short len = (short) pool.length; + while (index < len) { + if (null == pool[index]) { + // No instance of cipher/signature with this algorithm is found + if (instanceCount < 4) { + pool[index] = new KMInstance(); + JCSystem.beginTransaction(); + ((KMInstance) pool[index]).instanceCount = (byte) (++instanceCount); + if (isCipher) { + ((KMInstance) pool[index]).object = object = getCipherInstance(alg); + } else { + // Signature + ((KMInstance) pool[index]).object = object = getSignatureInstance(alg); + } + ((KMInstance) pool[index]).reserved = 1; + JCSystem.commitTransaction(); + break; + } else { + // Cipher/Signature instance count reached its maximum limit. + KMException.throwIt(KMError.TOO_MANY_OPERATIONS); + break; + } + } + object = ((KMInstance) pool[index]).object; + if ((isCipher && (alg == getCipherAlgorithm((Cipher) object))) + || ((isSigner && (alg == ((Signature) object).getAlgorithm())))) { + instanceCount = ((KMInstance) pool[index]).instanceCount; + if (((KMInstance) pool[index]).reserved == 0) { + JCSystem.beginTransaction(); + ((KMInstance) pool[index]).reserved = 1; + JCSystem.commitTransaction(); + break; + } + } else { + if (!isCipher && !isSigner) { + // OperationImpl + if (((KMInstance) pool[index]).reserved == 0) { + JCSystem.beginTransaction(); + ((KMInstance) pool[index]).reserved = 1; + JCSystem.commitTransaction(); + break; + } + } + } + object = null; + index++; + } + return object; + } + + private void releaseInstance(Object[] pool, Object object) { + short index = 0; + short len = (short) pool.length; + while (index < len) { + if (pool[index] != null) { + if (object == ((KMInstance) pool[index]).object) { + JCSystem.beginTransaction(); + ((KMInstance) pool[index]).reserved = 0; + JCSystem.commitTransaction(); + break; + } + } else { + // Reached end. + break; + } + index++; + } + } + + public AESKey createAESKey(short keysize) { + try { + newRandomNumber(tmpArray, (short) 0, (short) (keysize / 8)); + return createAESKey(tmpArray, (short) 0, (short) (keysize / 8)); + } finally { + clean(); + } + } + + public AESKey createAESKey(byte[] buf, short startOff, short length) { + AESKey key = null; + short keysize = (short) (length * 8); + if (keysize == 128) { + key = (AESKey) aesKeys[KEYSIZE_128_OFFSET]; + key.setKey(buf, (short) startOff); + } else if (keysize == 256) { + key = (AESKey) aesKeys[KEYSIZE_256_OFFSET]; + key.setKey(buf, (short) startOff); + } + return key; + } + + public DESKey createTDESKey() { + try { + newRandomNumber(tmpArray, (short) 0, + (short) (KeyBuilder.LENGTH_DES3_3KEY / 8)); + return createTDESKey(tmpArray, (short) 0, + (short) (KeyBuilder.LENGTH_DES3_3KEY / 8)); + } finally { + clean(); + } + } + + public DESKey createTDESKey(byte[] secretBuffer, short secretOff, + short secretLength) { + triDesKey.setKey(secretBuffer, secretOff); + return triDesKey; + } + + public HMACKey createHMACKey(short keysize) { + if ((keysize % 8 != 0) || !(keysize >= 64 && keysize <= 512)) { + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + } + try { + newRandomNumber(tmpArray, (short) 0, (short) (keysize / 8)); + return createHMACKey(tmpArray, (short) 0, (short) (keysize / 8)); + } finally { + clean(); + } + } + + public HMACKey createHMACKey(byte[] secretBuffer, short secretOff, + short secretLength) { + hmacKey.setKey(secretBuffer, secretOff, secretLength); + return hmacKey; + } + + public KeyPair createRsaKeyPair() { + rsaKeyPair.genKeyPair(); + return rsaKeyPair; + } + + public RSAPrivateKey createRsaKey(byte[] modBuffer, short modOff, + short modLength, byte[] privBuffer, short privOff, short privLength) { + RSAPrivateKey privKey = (RSAPrivateKey) rsaKeyPair.getPrivate(); + privKey.setExponent(privBuffer, privOff, privLength); + privKey.setModulus(modBuffer, modOff, modLength); + return privKey; + } + + public KeyPair createECKeyPair() { + ecKeyPair.genKeyPair(); + return ecKeyPair; + } + + public ECPrivateKey createEcKey(byte[] privBuffer, short privOff, + short privLength) { + ECPrivateKey privKey = (ECPrivateKey) ecKeyPair.getPrivate(); + privKey.setS(privBuffer, privOff, privLength); + return privKey; + } + + @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: + createAESKey(buf, startOff, length); + break; + case KMType.DES: + createTDESKey(buf, startOff, length); + break; + case KMType.HMAC: + 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: + createRsaKey(pubModBuf, pubModStart, pubModLength, privKeyBuf, + privKeyStart, privKeyLength); + break; + case KMType.EC: + createEcKey(privKeyBuf, privKeyStart, privKeyLength); + break; + default: + CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); + break; + } + return true; + } + + @Override + public void getTrueRandomNumber(byte[] buf, short start, short length) { + newRandomNumber(buf, start, length); + } + + @Override + public void newRandomNumber(byte[] num, short startOff, short length) { + rng.nextBytes(num, startOff, length); + } + + @Override + public void addRngEntropy(byte[] num, short offset, short length) { + rng.setSeed(num, offset, length); + } + + @Override + public short aesGCMEncrypt(byte[] aesKey, short aesKeyStart, short aesKeyLen, + byte[] secret, short secretStart, short secretLen, byte[] encSecret, + short encSecretStart, byte[] nonce, short nonceStart, short nonceLen, + byte[] authData, short authDataStart, short authDataLen, byte[] authTag, + short authTagStart, short authTagLen) { + + if (authTagLen != AES_GCM_TAG_LENGTH) { + KMException.throwIt(KMError.UNKNOWN_ERROR); + } + if (nonceLen != AES_GCM_NONCE_LENGTH) { + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + } + if (aesGcmCipher == null) { + aesGcmCipher = (AEADCipher) Cipher.getInstance(AEADCipher.ALG_AES_GCM, + false); + } + AESKey key = createAESKey(aesKey, aesKeyStart, aesKeyLen); + aesGcmCipher.init(key, Cipher.MODE_ENCRYPT, nonce, nonceStart, nonceLen); + aesGcmCipher.updateAAD(authData, authDataStart, authDataLen); + short ciphLen = aesGcmCipher.doFinal(secret, secretStart, secretLen, + encSecret, encSecretStart); + aesGcmCipher.retrieveTag(authTag, authTagStart, authTagLen); + return ciphLen; + } + + @Override + public boolean aesGCMDecrypt(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) { + if (aesGcmCipher == null) { + aesGcmCipher = (AEADCipher) Cipher.getInstance(AEADCipher.ALG_AES_GCM, + false); + } + boolean verification = false; + AESKey key = createAESKey(aesKey, aesKeyStart, aesKeyLen); + aesGcmCipher.init(key, Cipher.MODE_DECRYPT, nonce, nonceStart, nonceLen); + aesGcmCipher.updateAAD(authData, authDataStart, authDataLen); + // encrypt the secret + aesGcmCipher.doFinal(encSecret, encSecretStart, encSecretLen, secret, + secretStart); + verification = aesGcmCipher.verifyTag(authTag, authTagStart, (short) 12, + (short) 12); + return verification; + } + + public HMACKey cmacKdf(byte[] keyMaterial, short keyMaterialStart, + short keyMaterialLen, byte[] label, short labelStart, short labelLen, + byte[] context, short contextStart, short contextLength) { + try { + // This is hardcoded to requirement - 32 byte output with two concatenated + // 16 bytes K1 and K2. + final byte n = 2; // hardcoded + // [L] 256 bits - hardcoded 32 bits as per + // reference impl in keymaster. + final byte[] L = { + 0, 0, 1, 0 + }; + // byte + final byte[] zero = { + 0 + }; + // [i] counter - 32 bits + short iBufLen = 4; + short keyOutLen = n * 16; + Util.arrayFillNonAtomic(tmpArray, (short) 0, iBufLen, (byte) 0); + Util.arrayFillNonAtomic(tmpArray, (short) iBufLen, keyOutLen, (byte) 0); + aesKeys[KEYSIZE_256_OFFSET].setKey(keyMaterial, (short) keyMaterialStart); + kdf.init(aesKeys[KEYSIZE_256_OFFSET], Signature.MODE_SIGN); + byte i = 1; + short pos = 0; + while (i <= n) { + tmpArray[3] = i; + // 4 bytes of iBuf with counter in it + kdf.update(tmpArray, (short) 0, (short) iBufLen); + kdf.update(label, labelStart, (short) labelLen); // label + kdf.update(zero, (short) 0, (short) 1); // 1 byte of 0x00 + kdf.update(context, contextStart, contextLength); // context + // 4 bytes of L - signature of 16 bytes + pos = kdf.sign(L, (short) 0, (short) 4, tmpArray, + (short) (iBufLen + pos)); + i++; + } + return createHMACKey(tmpArray, (short) iBufLen, (short) keyOutLen); + } finally { + clean(); + } + } + + 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); + } + + public boolean hmacVerify(HMACKey key, byte[] data, short dataStart, + short dataLength, byte[] mac, short macStart, short macLength) { + hmacSignature.init(key, Signature.MODE_VERIFY); + return hmacSignature.verify(data, dataStart, dataLength, mac, macStart, + macLength); + } + + @Override + public short hmacSign(byte[] keyBuf, short keyStart, short keyLength, + byte[] data, short dataStart, short dataLength, byte[] mac, short macStart) { + 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) { + HMACKey key = createHMACKey(keyBuf, keyStart, keyLength); + return hmacVerify(key, data, dataStart, dataLength, mac, macStart, + macLength); + } + + @Override + 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) { + RSAPrivateKey key = (RSAPrivateKey) rsaKeyPair.getPrivate(); + key.setExponent(secret, (short)secretStart, (short)secretLength); + key.setModulus(modBuffer, (short)modOff, (short)modLength); + rsaOaepDecipher.init(key, Cipher.MODE_DECRYPT); + return rsaOaepDecipher.doFinal(inputDataBuf, (short)inputDataStart, (short)inputDataLength, + outputDataBuf, (short) outputDataStart); + } + + public short ecSign256(byte[] secret, short secretStart, short secretLength, + byte[] inputDataBuf, short inputDataStart, short inputDataLength, + byte[] outputDataBuf, short outputDataStart) { + Signature.OneShot signer = null; + try { + ECPrivateKey key = (ECPrivateKey) ecKeyPair.getPrivate(); + key.setS(secret, secretStart, secretLength); + + signer = Signature.OneShot.open(MessageDigest.ALG_SHA_256, + Signature.SIG_CIPHER_ECDSA, Cipher.PAD_NULL); + signer.init(key, Signature.MODE_SIGN); + return signer.sign(inputDataBuf, inputDataStart, inputDataLength, + outputDataBuf, outputDataStart); + } finally { + if (signer != null) + signer.close(); + } + } + + 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, byte digest) { + switch (alg) { + case KMType.RSA: + switch (padding) { + case KMType.RSA_PKCS1_1_5_SIGN: { + if (digest == KMType.DIGEST_NONE) + return KMRsa2048NoDigestSignature.ALG_RSA_PKCS1_NODIGEST; + else + return Signature.ALG_RSA_SHA_256_PKCS1; + } + case KMType.RSA_PSS: + return Signature.ALG_RSA_SHA_256_PKCS1_PSS; + case KMType.PADDING_NONE: + return KMRsa2048NoDigestSignature.ALG_RSA_SIGN_NOPAD; + } + break; + case KMType.EC: + if (digest == KMType.DIGEST_NONE) + return KMEcdsa256NoDigestSignature.ALG_ECDSA_NODIGEST; + else + 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, byte digest) { + 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: { + if (digest == KMType.SHA2_256) + return KMRsaOAEPEncoding.ALG_RSA_PKCS1_OAEP_SHA256_MGF1_SHA1; + else + KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM); + } + } + break; + } + return -1; + } + + public Cipher 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; + switch (secretLength) { + case 32: + key = aesKeys[KEYSIZE_256_OFFSET]; + ((AESKey) key).setKey(secret, secretStart); + break; + case 16: + key = aesKeys[KEYSIZE_128_OFFSET]; + ((AESKey) key).setKey(secret, secretStart); + break; + case 24: + key = triDesKey; + ((DESKey) key).setKey(secret, secretStart); + break; + default: + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + break; + } + short cipherAlg = mapCipherAlg((byte) alg, (byte) padding, (byte) blockMode, (byte)0); + symmCipher = getCipherInstanceFromPool((byte) cipherAlg); + switch (cipherAlg) { + case Cipher.ALG_AES_BLOCK_128_CBC_NOPAD: + case Cipher.ALG_AES_CTR: + symmCipher.init(key, mapPurpose(purpose), ivBuffer, ivStart, ivLength); + break; + case Cipher.ALG_AES_BLOCK_128_ECB_NOPAD: + case Cipher.ALG_DES_ECB_NOPAD: + symmCipher.init(key, mapPurpose(purpose)); + break; + case Cipher.ALG_DES_CBC_NOPAD: + // 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, mapPurpose(purpose), ivBuffer, ivStart, (short) 8); + break; + case AEADCipher.ALG_AES_GCM: + ((AEADCipher) symmCipher).init(key, mapPurpose(purpose), ivBuffer, + ivStart, ivLength); + break; + default:// This should never happen + CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); + break; + } + return symmCipher; + } + + public Signature createHmacSignerVerifier(short purpose, short digest, + byte[] secret, short secretStart, short secretLength) { + byte alg = Signature.ALG_HMAC_SHA_256; + if (digest != KMType.SHA2_256) + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + Signature hmacSignerVerifier = getSignatureInstanceFromPool(alg); + HMACKey key = createHMACKey(secret, secretStart, secretLength); + hmacSignerVerifier.init(key, (byte) mapPurpose(purpose)); + return hmacSignerVerifier; + } + + @Override + 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) { + KMOperationImpl opr = null; + switch (alg) { + case KMType.AES: + case KMType.DES: + Cipher cipher = createSymmetricCipher(alg, purpose, blockMode, padding, + keyBuf, keyStart, keyLength, ivBuf, ivStart, ivLength); + opr = getOperationInstanceFromPool(); + // Convert macLength to bytes + macLength = (short) (macLength / 8); + JCSystem.beginTransaction(); + opr.setCipher(cipher); + opr.setCipherAlgorithm(alg); + opr.setBlockMode(blockMode); + opr.setPaddingAlgorithm(padding); + opr.setMode(purpose); + opr.setMacLength(macLength); + JCSystem.commitTransaction(); + break; + case KMType.HMAC: + Signature signerVerifier = createHmacSignerVerifier(purpose, digest, + keyBuf, keyStart, keyLength); + opr = getOperationInstanceFromPool(); + JCSystem.beginTransaction(); + opr.setSignature(signerVerifier); + JCSystem.commitTransaction(); + break; + default: + CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); + break; + } + return opr; + } + + public Signature createRsaSigner(short digest, short padding, byte[] secret, + short secretStart, short secretLength, byte[] modBuffer, short modOff, + short modLength) { + byte alg = mapSignature256Alg(KMType.RSA, (byte) padding, (byte) digest); + byte opMode; + if (padding == KMType.PADDING_NONE + || (padding == KMType.RSA_PKCS1_1_5_SIGN && digest == KMType.DIGEST_NONE)) { + opMode = Cipher.MODE_DECRYPT; + } else { + opMode = Signature.MODE_SIGN; + } + Signature rsaSigner = getSignatureInstanceFromPool(alg); + RSAPrivateKey key = (RSAPrivateKey) rsaKeyPair.getPrivate(); + key.setExponent(secret, secretStart, secretLength); + key.setModulus(modBuffer, modOff, modLength); + rsaSigner.init(key, opMode); + return rsaSigner; + } + + public Signature createRsaVerifier(short digest, short padding, + byte[] modBuffer, short modOff, short modLength) { + try { + byte alg = mapSignature256Alg(KMType.RSA, (byte) padding, (byte) digest); + if (digest == KMType.DIGEST_NONE || padding == KMType.PADDING_NONE) + CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); + + Signature rsaVerifier = getSignatureInstanceFromPool(alg); + RSAPublicKey key = (RSAPublicKey) rsaKeyPair.getPublic(); + // setExponent + Util.setShort(tmpArray, (short) 0, (short) 0x0001); + Util.setShort(tmpArray, (short) 2, (short) 0x0001); + key.setExponent(tmpArray, (short) 0, (short) 4); + key.setModulus(modBuffer, modOff, modLength); + rsaVerifier.init(key, Signature.MODE_VERIFY); + return rsaVerifier; + } finally { + clean(); + } + } + + public Cipher createRsaCipher(short padding, short digest, byte[] modBuffer, + short modOff, short modLength) { + try { + byte cipherAlg = mapCipherAlg(KMType.RSA, (byte) padding, (byte) 0, (byte)digest); + // TODO Java Card does not support MGF1-SHA1 and digest as SHA256. + // Both digest should be SHA256 as per Java Card, but as per Keymaster + // MGF should use SHA1 and message digest should be SHA256. + if (cipherAlg == Cipher.ALG_RSA_PKCS1_OAEP) { + KMException.throwIt(KMError.UNIMPLEMENTED); + } + Cipher rsaCipher = getCipherInstanceFromPool(cipherAlg); + RSAPublicKey key = (RSAPublicKey) rsaKeyPair.getPublic(); + // setExponent + Util.setShort(tmpArray, (short) 0, (short) 0x0001); + Util.setShort(tmpArray, (short) 2, (short) 0x0001); + key.setExponent(tmpArray, (short) 0, (short) 4); + key.setModulus(modBuffer, modOff, modLength); + rsaCipher.init(key, Cipher.MODE_ENCRYPT); + return rsaCipher; + } finally { + clean(); + } + } + + public Cipher createRsaDecipher(short padding, short digest, byte[] secret, + short secretStart, short secretLength, byte[] modBuffer, short modOff, + short modLength) { + byte cipherAlg = mapCipherAlg(KMType.RSA, (byte) padding, (byte) 0, (byte)digest); + Cipher rsaCipher = getCipherInstanceFromPool(cipherAlg); + RSAPrivateKey key = (RSAPrivateKey) rsaKeyPair.getPrivate(); + key.setExponent(secret, secretStart, secretLength); + key.setModulus(modBuffer, modOff, modLength); + rsaCipher.init(key, Cipher.MODE_DECRYPT); + return rsaCipher; + } + + public Signature createEcSigner(short digest, byte[] secret, + short secretStart, short secretLength) { + byte alg = mapSignature256Alg(KMType.EC, (byte) 0, (byte) digest); + Signature ecSigner = null; + ECPrivateKey key = (ECPrivateKey) ecKeyPair.getPrivate(); + key.setS(secret, secretStart, secretLength); + ecSigner = getSignatureInstanceFromPool(alg); + ecSigner.init(key, Signature.MODE_SIGN); + return ecSigner; + } + + public Signature createEcVerifier(short digest, byte[] pubKey, + short pubKeyStart, short pubKeyLength) { + byte alg = mapSignature256Alg(KMType.EC, (byte) 0, (byte) digest); + Signature ecVerifier = null; + ECPublicKey key = (ECPublicKey) ecKeyPair.getPublic(); + key.setW(pubKey, pubKeyStart, pubKeyLength); + ecVerifier = getSignatureInstanceFromPool(alg); + ecVerifier.init(key, Signature.MODE_VERIFY); + return ecVerifier; + } + + @Override + public KMOperation initAsymmetricOperation(byte purpose, byte alg, + byte padding, byte digest, byte[] privKeyBuf, short privKeyStart, + short privKeyLength, byte[] pubModBuf, short pubModStart, + short pubModLength) { + KMOperationImpl opr = null; + if (alg == KMType.RSA) { + switch (purpose) { + case KMType.SIGN: + Signature signer = createRsaSigner(digest, padding, privKeyBuf, + privKeyStart, privKeyLength, pubModBuf, pubModStart, pubModLength); + opr = getOperationInstanceFromPool(); + JCSystem.beginTransaction(); + opr.setSignature(signer); + opr.setCipherAlgorithm(alg); + opr.setPaddingAlgorithm(padding); + opr.setMode(purpose); + JCSystem.commitTransaction(); + break; + case KMType.VERIFY: + Signature verifier = createRsaVerifier(digest, padding, pubModBuf, + pubModStart, pubModLength); + opr = getOperationInstanceFromPool(); + JCSystem.beginTransaction(); + opr.setSignature(verifier); + opr.setCipherAlgorithm(alg); + opr.setPaddingAlgorithm(padding); + opr.setMode(purpose); + JCSystem.commitTransaction(); + break; + case KMType.ENCRYPT: + Cipher cipher = createRsaCipher(padding, digest, pubModBuf, + pubModStart, pubModLength); + opr = getOperationInstanceFromPool(); + JCSystem.beginTransaction(); + opr.setCipher(cipher); + opr.setCipherAlgorithm(alg); + opr.setPaddingAlgorithm(padding); + opr.setMode(purpose); + JCSystem.commitTransaction(); + break; + case KMType.DECRYPT: + Cipher decipher = createRsaDecipher(padding, digest, privKeyBuf, + privKeyStart, privKeyLength, pubModBuf, pubModStart, pubModLength); + opr = getOperationInstanceFromPool(); + JCSystem.beginTransaction(); + opr.setCipher(decipher); + opr.setCipherAlgorithm(alg); + opr.setPaddingAlgorithm(padding); + opr.setMode(purpose); + JCSystem.commitTransaction(); + break; + default: + break; + } + } else if (alg == KMType.EC) { + switch (purpose) { + case KMType.SIGN: + Signature signer = createEcSigner(digest, privKeyBuf, privKeyStart, + privKeyLength); + opr = getOperationInstanceFromPool(); + JCSystem.beginTransaction(); + opr.setSignature(signer); + JCSystem.commitTransaction(); + break; + case KMType.VERIFY: + Signature verifier = createEcVerifier(digest, pubModBuf, pubModStart, + pubModLength); + opr = getOperationInstanceFromPool(); + JCSystem.beginTransaction(); + opr.setSignature(verifier); + JCSystem.commitTransaction(); + break; + } + } else { + CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); + } + return opr; + + } + + @Override + public KMAttestationCert getAttestationCert(boolean rsaCert) { + return KMAttestationCertImpl.instance(rsaCert); + } + + @Override + public short aesCCMSign(byte[] bufIn, short bufInStart, short buffInLength, + byte[] masterKeySecret, short masterKeyStart, short masterKeyLen, + byte[] bufOut, short bufStart) { + if (masterKeyLen > 16) { + return -1; + } + aesKeys[KEYSIZE_128_OFFSET].setKey(masterKeySecret, (short) masterKeyStart); + kdf.init(aesKeys[KEYSIZE_128_OFFSET], Signature.MODE_SIGN); + return kdf.sign(bufIn, bufInStart, buffInLength, bufOut, bufStart); + } + + @Override + public short cmacKdf(byte[] keyMaterial, short keyMaterialStart, + short keyMaterialLen, byte[] label, short labelStart, short labelLen, + byte[] context, short contextStart, short contextLength, byte[] keyBuf, + short keyStart) { + HMACKey key = cmacKdf(keyMaterial, keyMaterialStart, keyMaterialLen, label, + labelStart, labelLen, context, contextStart, contextLength); + return key.getKey(keyBuf, keyStart); + } + + //This function supports multi-part request data. + @Override + public void persistPartialCertificateChain(byte[] buf, short offset, short len, short totalLen) { + // _____________________________________________________ + // | 2 Bytes | 1 Byte | 3 Bytes | Cert1 | 3 Bytes | Cert2|... + // |_________|________|_________|_______|_________|______| + // First two bytes holds the length of the total buffer. + // CBOR format: + // Next single byte holds the array header. + // Next 3 bytes holds the Byte array header with the cert1 length. + // Next 3 bytes holds the Byte array header with the cert2 length. + short persistedLen = Util.getShort(certificateChain, (short) 0); + if (persistedLen > totalLen) { + KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + } + JCSystem.beginTransaction(); + Util.setShort(certificateChain, (short) 0, (short) (len + persistedLen)); + Util.arrayCopyNonAtomic(buf, offset, certificateChain, + (short) (persistedLen+2), len); + JCSystem.commitTransaction(); + } + + @Override + public short readCertificateChain(byte[] buf, short offset) { + short len = Util.getShort(certificateChain, (short)0); + Util.arrayCopyNonAtomic(certificateChain, (short)2, buf, offset, len); + return len; + } + + @Override + public short getCertificateChainLength() { + return Util.getShort(certificateChain, (short)0); + } + + @Override + public boolean isBootSignalEventSupported() { + return false; + } + + @Override + public boolean isDeviceRebooted() { + return false; + } + + @Override + public void clearDeviceBooted(boolean resetBootFlag) { + // To be filled + } + + @Override + public void onSave(Element element) { + element.write(certificateChain); + } + + @Override + public void onRestore(Element element) { + certificateChain = (byte[]) element.readObject(); + } + + @Override + public short getBackupPrimitiveByteCount() { + return (short) 0; + } + + @Override + public short getBackupObjectCount() { + return (short) 1; + } + + @Override + public boolean isUpgrading() { + return UpgradeManager.isUpgrading(); + } +} diff --git a/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMAttestationCertImpl.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAttestationCertImpl.java similarity index 85% rename from Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMAttestationCertImpl.java rename to Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAttestationCertImpl.java index 34a3b695..40bd751b 100644 --- a/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMAttestationCertImpl.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAttestationCertImpl.java @@ -33,24 +33,22 @@ public class KMAttestationCertImpl implements KMAttestationCert { // Authority Key Identifier Extn - 2.5.29.35 private static final byte[] authKeyIdExtn = {0x06, 0x03, 0X55, 0X1D, 0X23}; - // Signature algorithm identifier - always sha256WithRSAEncryption - 1.2.840.113549.1.1.11 - // SEQUENCE of alg OBJ ID and parameters = NULL. + private static final short ECDSA_MAX_SIG_LEN = 72; + //Signature algorithm identifier - always ecdsaWithSha256 - 1.2.840.10045.4.3.2 + //SEQUENCE of alg OBJ ID and parameters = NULL. private static final byte[] X509SignAlgIdentifier = { 0x30, - 0x0D, + 0x0A, 0x06, - 0x09, + 0x08, 0x2A, (byte) 0x86, 0x48, - (byte) 0x86, - (byte) 0xF7, - 0x0D, - 0x01, - 0x01, - 0x0B, - 0x05, - 0x00 + (byte) 0xCE, + (byte) 0x3D, + 0x04, + 0x03, + 0x02 }; // Validity is not fixed field // Subject is a fixed field with only CN= Android Keystore Key - same for all the keys @@ -100,7 +98,6 @@ public class KMAttestationCertImpl implements KMAttestationCert { private static short authKey; private static short issuer; private static short signPriv; - private static short signMod; private KMAttestationCertImpl() {} @@ -143,7 +140,6 @@ private static void init() { deviceLocked = 0; authKey = 0; signPriv = 0; - signMod = 0; } @Override @@ -170,21 +166,36 @@ public KMAttestationCert verifiedBootState(byte val) { return this; } - @Override - public KMAttestationCert uniqueId(short obj) { + private KMAttestationCert uniqueId(short obj) { uniqueId = obj; return this; } @Override - public KMAttestationCert notBefore(short obj) { - notBefore = obj; + public KMAttestationCert notBefore(short obj, byte[] scratchpad) { + // convert milliseconds to UTC date + notBefore = KMUtils.convertToDate(obj, scratchpad, true); return this; } @Override - public KMAttestationCert notAfter(short obj) { - notAfter = obj; + public KMAttestationCert notAfter(short usageExpiryTimeObj, + short certExpirtyTimeObj, byte[] scratchPad, short tmpVar) { + if (usageExpiryTimeObj != KMType.INVALID_VALUE) { + // compare if the expiry time is greater then 2051 then use generalized + // time format else use utc time format. + usageExpiryTimeObj = KMIntegerTag.cast(usageExpiryTimeObj).getValue(); + tmpVar = KMInteger.uint_64(KMUtils.firstJan2051, (short) 0); + if (KMInteger.compare(usageExpiryTimeObj, tmpVar) >= 0) + usageExpiryTimeObj = KMUtils.convertToDate(usageExpiryTimeObj, scratchPad, + false); + else + usageExpiryTimeObj = KMUtils + .convertToDate(usageExpiryTimeObj, scratchPad, true); + notAfter = usageExpiryTimeObj; + } else { + notAfter = certExpirtyTimeObj; + } return this; } @@ -357,8 +368,7 @@ private static void pushValidity() { KMByteBlob.cast(notAfter).getStartOff(), KMByteBlob.cast(notAfter).length()); } else { - // TODO move this to keymaster applet - // pushBytes(repo.getCertDataBuffer(), repo.getCertExpiryTime(), repo.getCertExpiryTimeLen()); + KMException.throwIt(KMError.INVALID_DATA); } pushTimeHeader(KMByteBlob.cast(notAfter).length()); pushBytes( @@ -472,9 +482,9 @@ private static void pushSWParams() { short last = stackPtr; // 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 - }; + KMType.ATTESTATION_APPLICATION_ID, KMType.CREATION_DATETIME, + KMType.USAGE_EXPIRE_DATETIME, KMType.ORIGINATION_EXPIRE_DATETIME, + KMType.ACTIVE_DATETIME, KMType.UNLOCKED_DEVICE_REQUIRED }; byte index = 0; do { /* @@ -492,9 +502,15 @@ private static void pushHWParams() { short last = stackPtr; // 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 - }; + KMType.BOOT_PATCH_LEVEL, KMType.VENDOR_PATCH_LEVEL, + KMType.OS_PATCH_LEVEL, KMType.OS_VERSION, KMType.ROOT_OF_TRUST, + KMType.ORIGIN, KMType.APPLICATION_ID, + KMType.TRUSTED_CONFIRMATION_REQUIRED, + KMType.TRUSTED_USER_PRESENCE_REQUIRED, KMType.ALLOW_WHILE_ON_BODY, + KMType.AUTH_TIMEOUT, KMType.USER_AUTH_TYPE, KMType.NO_AUTH_REQUIRED, + KMType.ROLLBACK_RESISTANCE, KMType.RSA_PUBLIC_EXPONENT, + KMType.ECCURVE, KMType.PADDING, KMType.DIGEST, KMType.KEYSIZE, + KMType.ALGORITHM, KMType.PURPOSE }; byte index = 0; do { // if(pushAttIds(tagIds[index])) continue; @@ -556,7 +572,7 @@ private static void pushTag(short tag) { break; case KMType.UINT_ARRAY_TAG: case KMType.ULONG_ARRAY_TAG: - // TODO According to keymaster hal only one user secure id is used but this conflicts with + // According to keymaster hal only one user secure id is used but this conflicts with // tag type which is ULONG-REP. Currently this is encoded as SET OF INTEGERS val = KMIntegerArrayTag.cast(tag).getValues(); pushIntegerArrayTag(tagId, val); @@ -591,29 +607,16 @@ private static void pushRoT() { 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; - if (repo.deviceLockedFlag) val = (byte) 0xFF; - pushBoolean(val); - */ + pushBoolean(deviceLocked); // verified boot Key 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 @@ -634,27 +637,7 @@ 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(!repo.isAttIdSupported()) return true; - byte index = 0; - while (index < repo.ATT_ID_TABLE_SIZE) { - if (repo.getAttIdLen(index) != 0) { - 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 // All of these are enum array tags i.e. byte long values private static void pushEnumArrayTag(short tagId, byte[] buf, short start, short len) { @@ -786,13 +769,7 @@ private static void pushAuthKeyId() { short last = stackPtr; // 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(), @@ -875,30 +852,6 @@ private static void decrementStackPtr(short cnt) { if (start > stackPtr) KMException.throwIt(KMError.UNKNOWN_ERROR); } - 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 seProv.rsaSignPKCS1256( - privBuf, - privStart, - privLength, - modBuf, - modStart, - modLength, - stack, - tbsOffset, - tbsLength, - stack, - signatureOffset); - } - @Override public KMAttestationCert buffer(byte[] buf, short bufStart, short maxLen) { stack = buf; @@ -909,9 +862,8 @@ public KMAttestationCert buffer(byte[] buf, short bufStart, short maxLen) { } @Override - public KMAttestationCert signingKey(short privKey, short modulus) { + public KMAttestationCert signingKey(short privKey) { signPriv = privKey; - signMod = modulus; return this; } @@ -933,10 +885,9 @@ public short getCertLength() { @Override public void build() { short last = stackPtr; - decrementStackPtr((short) 256); + decrementStackPtr((short) ECDSA_MAX_SIG_LEN); signatureOffset = stackPtr; pushBitStringHeader((byte) 0, (short) (last - stackPtr)); - // signatureOffset = pushSignature(null, (short) 0, (short) 256); pushAlgorithmId(X509SignAlgIdentifier); tbsLength = stackPtr; pushTbsCert(rsaCert); @@ -944,30 +895,59 @@ public void build() { 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){ - StringBuilder sb = new StringBuilder(); - for(int i = start; i < (start+length); i++){ - sb.append(String.format("%02X", buf[i])) ; - //if((i-start)%16 == 0 && (i-start) != 0) sb.append(String.format("\n")); + short sigLen = KMAndroidSEProvider.getInstance() + .ecSign256( + KMByteBlob.cast(signPriv).getBuffer(), + KMByteBlob.cast(signPriv).getStartOff(), + KMByteBlob.cast(signPriv).length(), + stack, + tbsOffset, + tbsLength, + stack, + signatureOffset); + if(sigLen != ECDSA_MAX_SIG_LEN) { + // Update the lengths appropriately. + stackPtr = (short)(signatureOffset - 1); + pushLength((short)(sigLen + 1)); + stackPtr = tbsOffset; + last -= (short)(ECDSA_MAX_SIG_LEN - sigLen); + pushLength((short)(last - stackPtr)); + length -= (short)(ECDSA_MAX_SIG_LEN - sigLen); } - System.out.println(sb.toString()); } - */ + @Override + public KMAttestationCert makeUniqueId(byte[] scratchPad, short scratchPadOff, + byte[] creationTime, short timeOffset, short creationTimeLen, + byte[] attestAppId, short appIdOff, short attestAppIdLen, + byte resetSinceIdRotation, byte[] key, short keyOff, short keyLen) { + // Concatenate T||C||R + // temporal count T + short temp = KMUtils.countTemporalCount(creationTime, timeOffset, + creationTimeLen, scratchPad, scratchPadOff); + Util.setShort(scratchPad, (short) scratchPadOff, temp); + temp = scratchPadOff; + scratchPadOff += 2; + + // Application Id C + Util.arrayCopyNonAtomic(attestAppId, appIdOff, scratchPad, scratchPadOff, + attestAppIdLen); + scratchPadOff += attestAppIdLen; + + // Reset After Rotation R + scratchPad[scratchPadOff] = resetSinceIdRotation; + scratchPadOff++; + + timeOffset = KMByteBlob.instance((short) 32); + appIdOff = KMAndroidSEProvider.getInstance().hmacSign(key, keyOff, keyLen, + scratchPad, /* data */ + temp, /* data start */ + scratchPadOff, /* data length */ + KMByteBlob.cast(timeOffset).getBuffer(), /* signature buffer */ + KMByteBlob.cast(timeOffset).getStartOff()); /* signature start */ + if (appIdOff != 32) { + KMException.throwIt(KMError.UNKNOWN_ERROR); + } + return uniqueId(timeOffset); + } } diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMEcdsa256NoDigestSignature.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMEcdsa256NoDigestSignature.java new file mode 100644 index 00000000..727641c0 --- /dev/null +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMEcdsa256NoDigestSignature.java @@ -0,0 +1,124 @@ +package com.android.javacard.keymaster; + +import javacard.security.CryptoException; +import javacard.framework.Util; +import javacard.security.Key; +import javacard.security.MessageDigest; +import javacard.security.Signature; +import javacardx.crypto.Cipher; + +public class KMEcdsa256NoDigestSignature extends Signature { + + public static final byte ALG_ECDSA_NODIGEST = (byte) 0x67; + public static final short MAX_NO_DIGEST_MSG_LEN = 32; + private byte algorithm; + private Signature inst; + + public KMEcdsa256NoDigestSignature(byte alg) { + algorithm = alg; + inst = Signature.getInstance(Signature.ALG_ECDSA_SHA_256, false); + } + + @Override + public void init(Key key, byte b) throws CryptoException { + inst.init(key, b); + } + + @Override + public void init(Key key, byte b, byte[] bytes, short i, short i1) + throws CryptoException { + inst.init(key, b, bytes, i, i1); + } + + @Override + public void setInitialDigest(byte[] bytes, short i, short i1, byte[] bytes1, + short i2, short i3) throws CryptoException { + + } + + @Override + public byte getAlgorithm() { + return algorithm; + } + + @Override + public byte getMessageDigestAlgorithm() { + return MessageDigest.ALG_NULL; + } + + @Override + public byte getCipherAlgorithm() { + return 0; + } + + @Override + public byte getPaddingAlgorithm() { + return Cipher.PAD_NULL; + } + + @Override + public short getLength() throws CryptoException { + return inst.getLength(); + } + + @Override + public void update(byte[] message, short msgStart, short messageLength) + throws CryptoException { + // HAL accumulates the data and send it at finish operation. + } + + @Override + public short sign(byte[] bytes, short i, short i1, byte[] bytes1, short i2) + throws CryptoException { + try { + if (i1 > MAX_NO_DIGEST_MSG_LEN) + CryptoException.throwIt(CryptoException.ILLEGAL_USE); + // add zeros to the left + if (i1 < MAX_NO_DIGEST_MSG_LEN) { + Util.arrayFillNonAtomic(KMAndroidSEProvider.getInstance().tmpArray, + (short) 0, (short) MAX_NO_DIGEST_MSG_LEN, (byte) 0); + } + Util.arrayCopyNonAtomic(bytes, i, + KMAndroidSEProvider.getInstance().tmpArray, + (short) (MAX_NO_DIGEST_MSG_LEN - i1), i1); + return inst.signPreComputedHash(KMAndroidSEProvider.getInstance().tmpArray, + (short) 0, (short) MAX_NO_DIGEST_MSG_LEN, bytes1, i2); + } finally { + KMAndroidSEProvider.getInstance().clean(); + } + } + + @Override + public short signPreComputedHash(byte[] bytes, short i, short i1, + byte[] bytes1, short i2) throws CryptoException { + return inst.sign(bytes, i, i1, bytes1, i2); + } + + @Override + public boolean verify(byte[] bytes, short i, short i1, byte[] bytes1, + short i2, short i3) throws CryptoException { + try { + if (i1 > MAX_NO_DIGEST_MSG_LEN) + CryptoException.throwIt(CryptoException.ILLEGAL_USE); + // add zeros to the left + if (i1 < MAX_NO_DIGEST_MSG_LEN) { + Util.arrayFillNonAtomic(KMAndroidSEProvider.getInstance().tmpArray, + (short) 0, (short) MAX_NO_DIGEST_MSG_LEN, (byte) 0); + } + Util.arrayCopyNonAtomic(bytes, i, + KMAndroidSEProvider.getInstance().tmpArray, + (short) (MAX_NO_DIGEST_MSG_LEN - i1), i1); + return inst.verifyPreComputedHash( + KMAndroidSEProvider.getInstance().tmpArray, (short) 0, + (short) MAX_NO_DIGEST_MSG_LEN, bytes1, i2, i3); + } finally { + KMAndroidSEProvider.getInstance().clean(); + } + } + + @Override + public boolean verifyPreComputedHash(byte[] bytes, short i, short i1, + byte[] bytes1, short i2, short i3) throws CryptoException { + return inst.verify(bytes, i, i1, bytes1, i2, i3); + } +} \ No newline at end of file diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMInstance.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMInstance.java new file mode 100644 index 00000000..3bf35e97 --- /dev/null +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMInstance.java @@ -0,0 +1,7 @@ +package com.android.javacard.keymaster; + +public class KMInstance { + public byte reserved; + public Object object; + public byte instanceCount; +} diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMOperationImpl.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMOperationImpl.java new file mode 100644 index 00000000..b1f48827 --- /dev/null +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMOperationImpl.java @@ -0,0 +1,240 @@ +package com.android.javacard.keymaster; + +import javacard.framework.JCSystem; +import javacard.framework.Util; +import javacard.security.Signature; +import javacardx.crypto.AEADCipher; +import javacardx.crypto.Cipher; + +public class KMOperationImpl implements KMOperation { + + private Cipher cipher; + private Signature signature; + private short cipherAlg; + private short padding; + private short mode; + private short blockMode; + private short macLength; + //This will hold the length of the buffer stored inside the + //Java Card after the GCM update operation. + private short aesGcmUpdatedLen; + + public KMOperationImpl() { + } + + public short getMode() { + return mode; + } + + public void setMode(short mode) { + this.mode = mode; + } + + public short getMacLength() { + return macLength; + } + + public void setMacLength(short macLength) { + this.macLength = macLength; + } + + public short getPaddingAlgorithm() { + return padding; + } + + public void setPaddingAlgorithm(short alg) { + padding = alg; + } + + public void setBlockMode(short mode) { + blockMode = mode; + } + + public short getBlockMode() { + return blockMode; + } + + public short getCipherAlgorithm() { + return cipherAlg; + } + + public void setCipherAlgorithm(short cipherAlg) { + this.cipherAlg = cipherAlg; + } + + public void setCipher(Cipher cipher) { + this.cipher = cipher; + } + + public void setSignature(Signature signer) { + this.signature = signer; + } + + private void resetCipher() { + JCSystem.beginTransaction(); + cipher = null; + macLength = 0; + aesGcmUpdatedLen = 0; + blockMode = 0; + mode = 0; + cipherAlg = 0; + JCSystem.commitTransaction(); + } + + @Override + public short update(byte[] inputDataBuf, short inputDataStart, + short inputDataLength, byte[] outputDataBuf, short outputDataStart) { + short len = cipher.update(inputDataBuf, inputDataStart, inputDataLength, + outputDataBuf, outputDataStart); + if (cipherAlg == KMType.AES && blockMode == KMType.GCM) { + // Every time Block size data is stored as intermediate result. + aesGcmUpdatedLen += (short) (inputDataLength - len); + } + return len; + } + + @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 inputDataLen, byte[] outputDataBuf, short outputDataStart) { + byte[] tmpArray = KMAndroidSEProvider.getInstance().tmpArray; + short len = 0; + try { + if (cipherAlg == KMType.AES && blockMode == KMType.GCM) { + if (mode == KMType.DECRYPT) { + inputDataLen = (short) (inputDataLen - macLength); + } + } else if (cipherAlg == KMType.RSA && padding == KMType.PADDING_NONE && + mode == KMType.ENCRYPT) { + // Length cannot be greater then key size according to Java Card + if (inputDataLen > 256) + KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + // make input equal to 255 bytes + Util.arrayFillNonAtomic(tmpArray, (short) 0, (short) 256, (byte) 0); + Util.arrayCopyNonAtomic(inputDataBuf, inputDataStart, tmpArray, + (short) (256 - inputDataLen), inputDataLen); + inputDataStart = 0; + inputDataLen = 256; + inputDataBuf = tmpArray; + + } else if ((cipherAlg == KMType.DES || cipherAlg == KMType.AES) && + padding == KMType.PKCS7 && mode == KMType.ENCRYPT) { + byte blkSize = 16; + byte paddingBytes; + short inputlen = inputDataLen; + if (cipherAlg == KMType.DES) + blkSize = 8; + // padding bytes + if (inputlen % blkSize == 0) { + paddingBytes = blkSize; + } else { + paddingBytes = (byte) (blkSize - (inputlen % blkSize)); + } + // final len with padding + inputlen = (short) (inputlen + paddingBytes); + // intermediate buffer to copy input data+padding + // fill in the padding + Util.arrayFillNonAtomic(tmpArray, (short) 0, inputlen, paddingBytes); + // copy the input data + Util.arrayCopyNonAtomic(inputDataBuf, inputDataStart, tmpArray, + (short) 0, inputDataLen); + inputDataBuf = tmpArray; + inputDataLen = inputlen; + inputDataStart = 0; + } + len = cipher.doFinal(inputDataBuf, inputDataStart, inputDataLen, + outputDataBuf, outputDataStart); + if ((cipherAlg == KMType.AES || cipherAlg == KMType.DES) && + 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 = outputDataBuf[(short) (outputDataStart + 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 + } + } else if (cipherAlg == KMType.AES && blockMode == KMType.GCM) { + if (mode == KMType.ENCRYPT) { + len += ((AEADCipher) cipher).retrieveTag(outputDataBuf, + (short) (outputDataStart + len), macLength); + } else { + boolean verified = ((AEADCipher) cipher).verifyTag(inputDataBuf, + (short) (inputDataStart + inputDataLen), macLength, macLength); + if (!verified) + KMException.throwIt(KMError.VERIFICATION_FAILED); + } + } + } finally { + KMAndroidSEProvider.getInstance().clean(); + KMAndroidSEProvider.getInstance().releaseCipherInstance(cipher); + resetCipher(); + } + return len; + } + + @Override + public short sign(byte[] inputDataBuf, short inputDataStart, + short inputDataLength, byte[] signBuf, short signStart) { + short len = 0; + try { + len = signature.sign(inputDataBuf, inputDataStart, inputDataLength, + signBuf, signStart); + } finally { + KMAndroidSEProvider.getInstance().releaseSignatureInstance(signature); + signature = null; + } + return len; + } + + @Override + public boolean verify(byte[] inputDataBuf, short inputDataStart, + short inputDataLength, byte[] signBuf, short signStart, short signLength) { + boolean ret = false; + try { + ret = signature.verify(inputDataBuf, inputDataStart, inputDataLength, + signBuf, signStart, signLength); + } finally { + KMAndroidSEProvider.getInstance().releaseSignatureInstance(signature); + signature = null; + } + return ret; + } + + @Override + public void abort() { + // do nothing + if (cipher != null) { + KMAndroidSEProvider.getInstance().releaseCipherInstance(cipher); + resetCipher(); + } + if (signature != null) { + KMAndroidSEProvider.getInstance().releaseSignatureInstance(signature); + signature = null; + } + KMAndroidSEProvider.getInstance().releaseOperationInstance(this); + } + + @Override + public void updateAAD(byte[] dataBuf, short dataStart, short dataLength) { + ((AEADCipher) cipher).updateAAD(dataBuf, dataStart, dataLength); + } + + @Override + public short getAESGCMOutputSize(short dataSize, short macLength) { + if (mode == KMType.ENCRYPT) { + return (short) (aesGcmUpdatedLen + dataSize + macLength); + } else { + return (short) (aesGcmUpdatedLen + dataSize - macLength); + } + } +} diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMRsa2048NoDigestSignature.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMRsa2048NoDigestSignature.java new file mode 100644 index 00000000..71c36a3d --- /dev/null +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMRsa2048NoDigestSignature.java @@ -0,0 +1,126 @@ +package com.android.javacard.keymaster; + +import javacard.framework.Util; +import javacard.security.CryptoException; +import javacard.security.Key; +import javacard.security.MessageDigest; +import javacard.security.Signature; +import javacardx.crypto.Cipher; + +public class KMRsa2048NoDigestSignature extends Signature { + + public static final byte ALG_RSA_SIGN_NOPAD = (byte) 0x65; + public static final byte ALG_RSA_PKCS1_NODIGEST = (byte) 0x66; + private byte algorithm; + private Cipher inst; + + public KMRsa2048NoDigestSignature(byte alg) { + algorithm = alg; + inst = Cipher.getInstance(Cipher.ALG_RSA_NOPAD, false); + } + + @Override + public void init(Key key, byte b) throws CryptoException { + inst.init(key, b); + } + + @Override + public void init(Key key, byte b, byte[] bytes, short i, short i1) + throws CryptoException { + inst.init(key, b, bytes, i, i1); + } + + @Override + public void setInitialDigest(byte[] bytes, short i, short i1, byte[] bytes1, + short i2, short i3) throws CryptoException { + } + + @Override + public byte getAlgorithm() { + return algorithm; + } + + @Override + public byte getMessageDigestAlgorithm() { + return MessageDigest.ALG_NULL; + } + + @Override + public byte getCipherAlgorithm() { + return algorithm; + } + + @Override + public byte getPaddingAlgorithm() { + return Cipher.PAD_NULL; + } + + @Override + public short getLength() throws CryptoException { + return 0; + } + + @Override + public void update(byte[] bytes, short i, short i1) throws CryptoException { + // HAL accumulates the data and send it at finish operation. + } + + @Override + public short sign(byte[] bytes, short i, short i1, byte[] bytes1, short i2) + throws CryptoException { + padData(bytes, i, i1, KMAndroidSEProvider.getInstance().tmpArray, (short) 0); + return inst.doFinal(KMAndroidSEProvider.getInstance().tmpArray, (short) 0, + (short) 256, bytes1, i2); + } + + @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 { + //Verification is handled inside HAL + return false; + } + + @Override + public boolean verifyPreComputedHash(byte[] bytes, short i, short i1, + byte[] bytes1, short i2, short i3) throws CryptoException { + //Verification is handled inside HAL + return false; + } + + private void padData(byte[] buf, short start, short len, byte[] outBuf, + short outBufStart) { + if (!isValidData(buf, start, len)) { + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + } + Util.arrayFillNonAtomic(outBuf, (short) outBufStart, (short) 256, + (byte) 0x00); + if (algorithm == ALG_RSA_SIGN_NOPAD) { // add zero to right + } else if (algorithm == ALG_RSA_PKCS1_NODIGEST) {// 0x00||0x01||PS||0x00 + outBuf[0] = 0x00; + outBuf[1] = 0x01; + Util.arrayFillNonAtomic(outBuf, (short) 2, (short) (256 - len - 3), + (byte) 0xFF); + outBuf[(short) (256 - len - 1)] = 0x00; + } else { + CryptoException.throwIt(CryptoException.ILLEGAL_USE); + } + Util.arrayCopyNonAtomic(buf, start, outBuf, (short) (256 - len), len); + } + + private boolean isValidData(byte[] buf, short start, short len) { + if (algorithm == ALG_RSA_SIGN_NOPAD) { + if (len > 256) + return false; + } else { // ALG_RSA_PKCS1_NODIGEST + if (len > 245) + return false; + } + return true; + } +} diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMRsaOAEPEncoding.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMRsaOAEPEncoding.java new file mode 100644 index 00000000..d4f4cb83 --- /dev/null +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMRsaOAEPEncoding.java @@ -0,0 +1,243 @@ +package com.android.javacard.keymaster; + +import javacard.framework.JCSystem; +import javacard.framework.Util; +import javacard.security.CryptoException; +import javacard.security.Key; +import javacard.security.MessageDigest; +import javacardx.crypto.Cipher; + +public class KMRsaOAEPEncoding extends Cipher { + + public static final byte ALG_RSA_PKCS1_OAEP_SHA256_MGF1_SHA1 = (byte) 0x1E; + public static final byte ALG_RSA_PKCS1_OAEP_SHA256_MGF1_SHA256 = (byte) 0x1F; + + final short MGF1_BUF_SIZE = 256; + static byte[] mgf1Buf; + private Cipher cipher; + private byte hash; + private byte mgf1Hash; + private byte algorithm; + + public KMRsaOAEPEncoding(byte alg) { + setDigests(alg); + cipher = Cipher.getInstance(Cipher.ALG_RSA_NOPAD, false); + algorithm = alg; + if (null == mgf1Buf) + mgf1Buf = JCSystem.makeTransientByteArray(MGF1_BUF_SIZE, + JCSystem.MEMORY_TYPE_TRANSIENT_DESELECT); + } + + private void setDigests(byte alg) { + switch (alg) { + case ALG_RSA_PKCS1_OAEP_SHA256_MGF1_SHA1: + hash = MessageDigest.ALG_SHA_256; + mgf1Hash = MessageDigest.ALG_SHA; + break; + case ALG_RSA_PKCS1_OAEP_SHA256_MGF1_SHA256: + hash = MessageDigest.ALG_SHA_256; + mgf1Hash = MessageDigest.ALG_SHA_256; + break; + default: + CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); + } + } + + private short getDigestLength() { + switch(hash) { + case MessageDigest.ALG_SHA: + return MessageDigest.LENGTH_SHA; + case MessageDigest.ALG_SHA_224: + return MessageDigest.LENGTH_SHA_224; + case MessageDigest.ALG_SHA_256: + return MessageDigest.LENGTH_SHA_256; + case MessageDigest.ALG_SHA_384: + return MessageDigest.LENGTH_SHA_384; + case MessageDigest.ALG_SHA3_512: + return MessageDigest.LENGTH_SHA_512; + default: + CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); + } + return 0; + } + + @Override + public void init(Key theKey, byte theMode) throws CryptoException { + cipher.init(theKey, theMode); + + } + + @Override + public void init(Key theKey, byte theMode, byte[] bArray, short bOff, + short bLen) throws CryptoException { + cipher.init(theKey, theMode, bArray, bOff, bLen); + } + + @Override + public byte getAlgorithm() { + return algorithm; + } + + @Override + public byte getCipherAlgorithm() { + // TODO + return 0; + } + + @Override + public byte getPaddingAlgorithm() { + // TODO + return 0; + } + + @Override + public short doFinal(byte[] inBuff, short inOffset, short inLength, + byte[] outBuff, short outOffset) throws CryptoException { + short len = cipher.doFinal(inBuff, inOffset, inLength, outBuff, outOffset); + + // https://tools.ietf.org/html/rfc8017#section-7.1 + // https://www.inf.pucrs.br/~calazans/graduate/TPVLSI_I/RSA-oaep_spec.pdf + // RSA OAEP Encoding and Decoding Mechanism for a 2048 bit RSA Key. + // Msg -> RSA-OAEP-ENCODE -> RSAEncryption -> RSADecryption -> + // RSA-OAEP-DECODE -> Msg + // RSA-OAEP-ENCODE generates an output length of 255, but RSAEncryption + // requires and input of length 256 so we pad 0 to the left of the input + // message and make the length equal to 256 and pass to RSAEncryption. + // RSADecryption takes input length equal to 256 and generates an + // output of length 256. After decryption the first byte of the output + // should be 0(left padding we did in encryption). + // RSA-OAEP-DECODE takes input of length 255 so remove the left padding of 1 + // byte. + if (len != 256 || outBuff[0] != 0) { + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + } + inBuff = outBuff; + inOffset = (short) (outOffset + 1); + return rsaOAEPDecode(inBuff, inOffset, (short) (len - 1), outBuff, + outOffset); + + } + + @Override + public short update(byte[] inBuff, short inOffset, short inLength, + byte[] outBuff, short outOffset) throws CryptoException { + return cipher.update(inBuff, inOffset, inLength, outBuff, outOffset); + } + + private void maskGenerationFunction1(byte[] input, short inputOffset, + short inputLen, short expectedOutLen, byte[] outBuf, short outOffset) { + short counter = 0; + MessageDigest.OneShot md = null; + try { + md = MessageDigest.OneShot.open(mgf1Hash); + short digestLen = md.getLength(); + + Util.arrayCopyNonAtomic(input, inputOffset, mgf1Buf, (short) 0, inputLen); + while (counter < (short) (expectedOutLen / digestLen)) { + I2OS(counter, mgf1Buf, (short) inputLen); + md.doFinal(mgf1Buf, (short) 0, (short) (4 + inputLen), outBuf, + (short) (outOffset + (counter * digestLen))); + counter++; + } + + if ((short) (counter * digestLen) < expectedOutLen) { + I2OS(counter, mgf1Buf, (short) inputLen); + md.doFinal(mgf1Buf, (short) 0, (short) (4 + inputLen), outBuf, + (short) (outOffset + (counter * digestLen))); + } + + } finally { + if (md != null) + md.close(); + Util.arrayFillNonAtomic(mgf1Buf, (short) 0, (short) MGF1_BUF_SIZE, + (byte) 0); + } + } + + // Integer to Octet String conversion. + private void I2OS(short i, byte[] out, short offset) { + Util.arrayFillNonAtomic(out, (short) offset, (short) 4, (byte) 0); + out[(short) (offset + 3)] = (byte) (i >>> 0); + out[(short) (offset + 2)] = (byte) (i >>> 8); + } + + private short rsaOAEPDecode(byte[] encodedMsg, short encodedMsgOff, + short encodedMsgLen, byte[] msg, short offset) { + MessageDigest.OneShot md = null; + byte[] tmpArray = KMAndroidSEProvider.getInstance().tmpArray; + + try { + short hLen = getDigestLength(); + + if (encodedMsgLen < (short)(2 * hLen + 1)) { + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + } + // encodedMsg will be in the format of maskedSeed||maskedDB. + // maskedSeed length is hLen and maskedDB length is (encodedMsgLen - hLen) + // Now retrieve the seedMask by calling MGF(maskedDB, hLen). The length + // of the seedMask is hLen. + // seedMask = MGF(maskedDB, hLen) + maskGenerationFunction1(encodedMsg, (short) (encodedMsgOff + hLen), + (short) (encodedMsgLen - hLen), hLen, tmpArray, (short) 0); + + // Get the seed by doing XOR of (maskedSeed ^ seedMask). + // seed = (maskedSeed ^ seedMask) + for (short i = 0; i < hLen; i++) { + // Store the seed in encodeMsg itself. + encodedMsg[(short) (encodedMsgOff + i)] ^= tmpArray[i]; + } + + // Now get the dbMask by calling MGF(seed , (emLen-hLen)). + // dbMask = MGF(seed , (emLen-hLen)). + maskGenerationFunction1(encodedMsg, (short) encodedMsgOff, hLen, + (short) (encodedMsgLen - hLen), tmpArray, (short) 0); + + // Get the DB value. DB = (maskedDB ^ dbMask) + // DB = Hash(P)||00||01||Msg, where P is encoding parameters. (P = NULL) + for (short i = 0; i < (short) (encodedMsgLen - hLen); i++) { + // Store the DB inside encodeMsg itself. + encodedMsg[(short) (encodedMsgOff + i + hLen)] ^= tmpArray[i]; + } + + // Verify Hash. + md = MessageDigest.OneShot.open(hash); + Util.arrayFillNonAtomic(tmpArray, (short) 0, (short) 256, (byte) 0); + md.doFinal(tmpArray, (short) 0, (short) 0, tmpArray, (short) 0); + if (0 != Util.arrayCompare(encodedMsg, (short) (encodedMsgOff + hLen), + tmpArray, (short) 0, hLen)) { + // Verification failed. + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + } + + // Find the Message block in DB. + // DB = Hash(P)||00||01||Msg, where P is encoding parameters. (P = NULL) + // The message will be located at the end of the Data block (DB). + // The DB block is first constructed by keeping the message at the end and + // to the message 0x01 byte is prepended. The hash of the + // encoding parameters is calculated and then copied from the + // starting of the block and a variable length of 0's are + // appended to the end of the hash till the 0x01 byte. + short start = 0; + for (short i = (short) (encodedMsgOff + 2 * hLen); i < (short) (encodedMsgOff + encodedMsgLen); i++) { + if (i == (short) ((encodedMsgOff + encodedMsgLen) - 1)) { + // Bad Padding. + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + } + if (encodedMsg[i] != 0) { + start = i; + break; + } + } + // Copy the message + Util.arrayCopyNonAtomic(encodedMsg, (short) (start + 1), msg, offset, + (short) (encodedMsgLen - ((start - encodedMsgOff) + 1))); + return (short) (encodedMsgLen - ((start - encodedMsgOff) + 1)); + + } finally { + if (md != null) + md.close(); + Util.arrayFillNonAtomic(tmpArray, (short) 0, + KMAndroidSEProvider.TMP_ARRAY_SIZE, (byte) 0); + } + } +} \ No newline at end of file diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMUtils.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMUtils.java new file mode 100644 index 00000000..d9dce03b --- /dev/null +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMUtils.java @@ -0,0 +1,321 @@ +package com.android.javacard.keymaster; + +import javacard.framework.Util; + +public class KMUtils { + // 64 bit unsigned calculations for time + public static final byte[] oneSecMsec = { + 0, 0, 0, 0, 0, 0, 0x03, (byte) 0xE8 }; // 1000 msec + public static final byte[] oneMinMsec = { + 0, 0, 0, 0, 0, 0, (byte) 0xEA, 0x60 }; // 60000 msec + public static final byte[] oneHourMsec = { + 0, 0, 0, 0, 0, 0x36, (byte) 0xEE, (byte) 0x80 }; // 3600000 msec + public static final byte[] oneDayMsec = { + 0, 0, 0, 0, 0x05, 0x26, 0x5C, 0x00 }; // 86400000 msec + public static final byte[] oneMonthMsec = { + 0, 0, 0, 0, (byte) 0x9A, 0x7E, (byte) 0xC8, 0x00 }; // 2592000000 msec + public static final byte[] oneYearMsec = { + 0, 0, 0, 0x07, 0x57, (byte) 0xB1, 0x2C, 0x00 }; // 31536000000 msec + // Leap year + 3 yrs + public static final byte[] fourYrsMsec = { + 0, 0, 0, 0x1D, 0x63, (byte) 0xEB, 0x0C, 0x00 }; // 126230400000 msec + public static final byte[] firstJan2020 = { + 0, 0, 0x01, 0x6F, 0x60, 0x1E, 0x5C, 0x00 }; // 1577865600000 msec + public static final byte[] firstJan2051 = { + 0, 0, 0x02, 0x53, 0x27, (byte) 0xC5, (byte) 0x90, 0x00 }; // 2556172800000 + // msec + + // -------------------------------------- + public static short convertToDate(short time, byte[] scratchPad, + boolean utcFlag) { + short yrsCount = 0; + short monthCount = 0; + short dayCount = 0; + short hhCount = 0; + short mmCount = 0; + short ssCount = 0; + byte Z = 0x5A; + boolean from2020 = true; + Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 256, (byte) 0); + Util.arrayCopyNonAtomic(KMInteger.cast(time).getBuffer(), + KMInteger.cast(time).getStartOff(), scratchPad, + (short) (8 - KMInteger.cast(time).length()), KMInteger.cast(time) + .length()); + // If the time is less then 1 Jan 2020 then it is an error + if (Util.arrayCompare(scratchPad, (short) 0, firstJan2020, (short) 0, + (short) 8) < 0) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + if (utcFlag + && Util.arrayCompare(scratchPad, (short) 0, firstJan2051, + (short) 0, (short) 8) >= 0) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + + if (Util.arrayCompare(scratchPad, (short) 0, firstJan2051, (short) 0, + (short) 8) < 0) { + Util.arrayCopyNonAtomic(firstJan2020, (short) 0, scratchPad, (short) 8, + (short) 8); + subtract(scratchPad, (short) 0, (short) 8, (short) 16); + Util.arrayCopyNonAtomic(scratchPad, (short) 16, scratchPad, (short) 0, + (short) 8); + } else { + from2020 = false; + Util.arrayCopyNonAtomic(firstJan2051, (short) 0, scratchPad, (short) 8, + (short) 8); + subtract(scratchPad, (short) 0, (short) 8, (short) 16); + Util.arrayCopyNonAtomic(scratchPad, (short) 16, scratchPad, (short) 0, + (short) 8); + } + // divide the given time with four yrs msec count + if (Util.arrayCompare(scratchPad, (short) 0, fourYrsMsec, (short) 0, + (short) 8) >= 0) { + Util.arrayCopyNonAtomic(fourYrsMsec, (short) 0, scratchPad, (short) 8, + (short) 8); + yrsCount = divide(scratchPad, (short) 0, (short) 8, (short) 16); // quotient + // is + // multiple + // of 4 + yrsCount = (short) (yrsCount * 4); // number of yrs. + // copy reminder as new dividend + Util.arrayCopyNonAtomic(scratchPad, (short) 16, scratchPad, (short) 0, + (short) 8); + } + // divide the given time with one yr msec count + if (Util.arrayCompare(scratchPad, (short) 0, oneYearMsec, (short) 0, + (short) 8) >= 0) { + Util.arrayCopyNonAtomic(oneYearMsec, (short) 0, scratchPad, (short) 8, + (short) 8); + yrsCount += divide(scratchPad, (short) 0, (short) 8, (short) 16); + Util.arrayCopyNonAtomic(scratchPad, (short) 16, scratchPad, (short) 0, + (short) 8); + } + // total yrs from 1970 + if (from2020) + yrsCount = (short) (2020 + yrsCount); + else + yrsCount = (short) (2051 + yrsCount); + + // divide the given time with one month msec count + if (Util.arrayCompare(scratchPad, (short) 0, oneMonthMsec, (short) 0, + (short) 8) >= 0) { + Util.arrayCopyNonAtomic(oneMonthMsec, (short) 0, scratchPad, (short) 8, + (short) 8); + monthCount = divide(scratchPad, (short) 0, (short) 8, (short) 16); + Util.arrayCopyNonAtomic(scratchPad, (short) 16, scratchPad, (short) 0, + (short) 8); + } + + // divide the given time with one day msec count + if (Util.arrayCompare(scratchPad, (short) 0, oneDayMsec, (short) 0, + (short) 8) >= 0) { + Util.arrayCopyNonAtomic(oneDayMsec, (short) 0, scratchPad, (short) 8, + (short) 8); + dayCount = divide(scratchPad, (short) 0, (short) 8, (short) 16); + Util.arrayCopyNonAtomic(scratchPad, (short) 16, scratchPad, (short) 0, + (short) 8); + } + + // divide the given time with one hour msec count + if (Util.arrayCompare(scratchPad, (short) 0, oneHourMsec, (short) 0, + (short) 8) >= 0) { + Util.arrayCopyNonAtomic(oneHourMsec, (short) 0, scratchPad, (short) 8, + (short) 8); + hhCount = divide(scratchPad, (short) 0, (short) 8, (short) 16); + Util.arrayCopyNonAtomic(scratchPad, (short) 16, scratchPad, (short) 0, + (short) 8); + } + + // divide the given time with one minute msec count + if (Util.arrayCompare(scratchPad, (short) 0, oneMinMsec, (short) 0, + (short) 8) >= 0) { + Util.arrayCopyNonAtomic(oneMinMsec, (short) 0, scratchPad, (short) 8, + (short) 8); + mmCount = divide(scratchPad, (short) 0, (short) 8, (short) 16); + Util.arrayCopyNonAtomic(scratchPad, (short) 16, scratchPad, (short) 0, + (short) 8); + } + + // divide the given time with one second msec count + if (Util.arrayCompare(scratchPad, (short) 0, oneSecMsec, (short) 0, + (short) 8) >= 0) { + Util.arrayCopyNonAtomic(oneSecMsec, (short) 0, scratchPad, (short) 8, + (short) 8); + ssCount = divide(scratchPad, (short) 0, (short) 8, (short) 16); + Util.arrayCopyNonAtomic(scratchPad, (short) 16, scratchPad, (short) 0, + (short) 8); + } + + // Now convert to ascii string YYMMDDhhmmssZ or YYYYMMDDhhmmssZ + Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 256, (byte) 0); + short len = numberToString(yrsCount, scratchPad, (short) 0); // returns YYYY + len += numberToString(monthCount, scratchPad, len); + len += numberToString(dayCount, scratchPad, len); + len += numberToString(hhCount, scratchPad, len); + len += numberToString(mmCount, scratchPad, len); + len += numberToString(ssCount, scratchPad, len); + scratchPad[len] = Z; + len++; + if (utcFlag) + return KMByteBlob.instance(scratchPad, (short) 2, (short) (len - 2)); // YY + else + return KMByteBlob.instance(scratchPad, (short) 0, len); // YYYY + } + + public static short numberToString(short number, byte[] scratchPad, + short offset) { + byte zero = 0x30; + byte len = 2; + byte digit; + if (number > 999) + len = 4; + byte index = len; + while (index > 0) { + digit = (byte) (number % 10); + number = (short) (number / 10); + scratchPad[(short) (offset + index - 1)] = (byte) (digit + zero); + index--; + } + return len; + } + + // 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. + public static short divide(byte[] buf, short dividend, short divisor, + short remainder) { + short expCnt = 1; + short q = 0; + // first increase divisor so that it becomes greater then dividend. + while (compare(buf, divisor, dividend) < 0) { + shiftLeft(buf, divisor); + expCnt = (short) (expCnt << 1); + } + // Now subtract divisor from dividend if dividend is greater then divisor. + // Copy remainder in the dividend and repeat. + while (expCnt != 0) { + if (compare(buf, dividend, divisor) >= 0) { + subtract(buf, dividend, divisor, remainder); + copy(buf, remainder, dividend); + q = (short) (q + expCnt); + } + expCnt = (short) (expCnt >> 1); + shiftRight(buf, divisor); + } + return q; + } + + public static void copy(byte[] buf, short from, short to) { + Util.arrayCopyNonAtomic(buf, from, buf, to, (short) 8); + } + + public static byte compare(byte[] buf, short lhs, short rhs) { + return Util.arrayCompare(buf, lhs, buf, rhs, (short) 8); + } + + public static void shiftLeft(byte[] buf, short start) { + byte index = 7; + byte carry = 0; + byte tmp; + while (index >= 0) { + tmp = buf[(short) (start + index)]; + buf[(short) (start + index)] = (byte) (buf[(short) (start + index)] << 1); + buf[(short) (start + index)] = (byte) (buf[(short) (start + index)] + carry); + if (tmp < 0) + carry = 1; + else + carry = 0; + index--; + } + } + + public static void shiftRight(byte[] buf, short start) { + byte index = 0; + byte carry = 0; + byte tmp; + while (index < 8) { + tmp = (byte) (buf[(short) (start + index)] & 0x01); + buf[(short) (start + index)] = (byte) (buf[(short) (start + index)] >> 1); + buf[(short) (start + index)] = (byte) (buf[(short) (start + index)] & 0x7F); + buf[(short) (start + index)] = (byte) (buf[(short) (start + index)] | carry); + if (tmp == 1) + carry = (byte) 0x80; + else + carry = 0; + index++; + } + } + + + // 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); byte[] scratchPad = repository.getHeap(); + Util.arrayFillNonAtomic(scratchPad, buf, (short) 24, (byte) 0); + Util.arrayCopyNonAtomic(KMInteger.cast(num1).getBuffer(), + KMInteger.cast(num1).getStartOff(), scratchPad, + (short) (buf + 8 - KMInteger.cast(num1).length()), + KMInteger.cast(num1).length()); + Util.arrayCopyNonAtomic(KMInteger.cast(num2).getBuffer(), + KMInteger.cast(num2).getStartOff(), scratchPad, + (short) (buf + 16 - KMInteger.cast(num2).length()), + KMInteger.cast(num2).length()); + if (scratchPad[buf] < 0 || scratchPad[(short) (buf + 8)] < 0) + return KMType.INVALID_VALUE; + if (Util.arrayCompare(scratchPad, buf, scratchPad, (short) (buf + 8), + (short) 8) < 1) + return KMType.INVALID_VALUE; + subtract(scratchPad, buf, (short) (buf + 8), (short) (buf + 16)); + return KMInteger.uint_64(scratchPad, (short) (buf + 16)); + }*/ + + public static void add(byte[] buf, short op1, short op2, short result) { + byte index = 7; + byte carry = 0; + short tmp; + while (index >= 0) { + tmp = (short) (buf[(short) (op1 + index)] + buf[(short) (op2 + index)] + carry); + carry = 0; + if (tmp > 255) + carry = 1; // max unsigned byte value is 255 + buf[(short) (result + index)] = (byte) (tmp & (byte) 0xFF); + index--; + } + } + + // subtraction by borrowing. + public static void subtract(byte[] buf, short op1, short op2, short result) { + byte borrow = 0; + byte index = 7; + short r; + short x; + short y; + while (index >= 0) { + x = (short) (buf[(short) (op1 + index)] & 0xFF); + y = (short) (buf[(short) (op2 + index)] & 0xFF); + r = (short) (x - y - borrow); + borrow = 0; + if (r < 0) { + borrow = 1; + r = (short) (r + 256); // max unsigned byte value is 255 + } + buf[(short) (result + index)] = (byte) (r & 0xFF); + index--; + } + } + + public static short countTemporalCount(byte[] bufTime, short timeOff, + short timeLen, byte[] scratchPad, short offset) { + Util.arrayFillNonAtomic(scratchPad, (short) offset, (short) 24, (byte) 0); + Util.arrayCopyNonAtomic( + bufTime, + timeOff, + scratchPad, + (short) (offset + 8 - timeLen), + timeLen); + Util.arrayCopyNonAtomic(oneMonthMsec, (short) 0, scratchPad, (short) (offset + 8), + (short) 8); + return divide(scratchPad, (short) 0, (short) 8, (short) 16); + } + +} diff --git a/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMBackupRestoreAgent.java b/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMBackupRestoreAgent.java deleted file mode 100644 index 0c483334..00000000 --- a/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMBackupRestoreAgent.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.android.javacard.keymaster; - -import javacard.framework.Shareable; - -public interface KMBackupRestoreAgent extends Shareable { - void backup(byte[] buf, short start, short len); - short restore(byte[] buf, short start); -} diff --git a/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMBackupStoreApplet.java b/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMBackupStoreApplet.java deleted file mode 100644 index 5f28c014..00000000 --- a/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMBackupStoreApplet.java +++ /dev/null @@ -1,63 +0,0 @@ -package com.android.javacard.keymaster; - -import javacard.framework.AID; -import javacard.framework.APDU; -import javacard.framework.Applet; -import javacard.framework.JCSystem; -import javacard.framework.Shareable; -import javacard.framework.Util; - -public class KMBackupStoreApplet extends Applet implements KMBackupRestoreAgent { - private static final short DATA_TABLE_MEM_SIZE = 2048; - private static final byte[] aidArr = new byte[]{ (byte)0xA0, 0x00, 0x00, 0x00, 0x62}; - - private byte[] dataTable; - private short dataTableSize; - - private KMBackupStoreApplet() { - dataTable = new byte[DATA_TABLE_MEM_SIZE]; - } - - public static void install(byte bArray[], short bOffset, byte bLength) { - new KMBackupStoreApplet().register(); - } - - @Override - public boolean select() { - return true; - } - - @Override - public void process(APDU apdu) { - - } - - @Override - public void backup(byte[] buf, short start, short len) { - // Store the data - if (len > 0) { - JCSystem.beginTransaction(); - dataTableSize = len; - Util.arrayCopy(buf, start, dataTable, (short) 0, len); - JCSystem.commitTransaction(); - } - } - - @Override - public short restore(byte[] buf, short start) { - // Restore the data - Util.arrayCopy(dataTable, (short) 0, buf, start, dataTableSize); - return dataTableSize; - } - - @Override - public Shareable getShareableInterfaceObject(AID aid, byte param){ - byte[] aidBytes = new byte[10]; - byte len = aid.getBytes(aidBytes, (short)0); - if(Util.arrayCompare(aidArr,(short)0,aidBytes,(short)0,len) == 0){ - return this; - } - return null; - } - -} diff --git a/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMSEProviderImpl.java b/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMSEProviderImpl.java deleted file mode 100644 index 34628ba7..00000000 --- a/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMSEProviderImpl.java +++ /dev/null @@ -1,8 +0,0 @@ - -package com.android.javacard.keymaster; - -public class KMSEProviderImpl { - public static KMSEProvider instance(){ - return new KMJcardSimulator(); - } -} diff --git a/Applet/Applet/OracleSimProvider/com/android/javacard/keymaster/KMSEProviderImpl.java b/Applet/Applet/OracleSimProvider/com/android/javacard/keymaster/KMSEProviderImpl.java deleted file mode 100644 index 9920c4ee..00000000 --- a/Applet/Applet/OracleSimProvider/com/android/javacard/keymaster/KMSEProviderImpl.java +++ /dev/null @@ -1,8 +0,0 @@ - -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 deleted file mode 100644 index a0f41c73..00000000 --- a/Applet/Applet/OracleSimProvider/com/android/javacard/keymaster/KMSimulator.java +++ /dev/null @@ -1,517 +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.keymaster; - -import javacard.framework.ISO7816; -import javacard.framework.ISOException; -import javacard.framework.JCSystem; -import javacard.framework.Util; -import javacard.security.AESKey; -import javacard.security.CryptoException; -import javacard.security.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.RandomData; -import javacard.security.Signature; -import javacardx.crypto.AEADCipher; -import javacardx.crypto.Cipher; - -/** - * Simulator only supports 512 bit RSA key pair, 128 AES Key, 128 bit 3Des key, less then 256 bit EC - * Key, and upto 512 bit HMAC key. Also simulator does not support TRNG, so this implementation just - * creates its own RNG using PRNG. - */ -public class 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; - public static final short ENTROPY_POOL_SIZE = 16; // simulator does not support 256 bit aes keys - public static final byte[] aesICV = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - public static boolean jcardSim = false; - - private static KeyPair rsa512KeyPair; - private static KeyPair ec192KeyPair; - private static AESKey aes128Key; - private static DESKey triDesKey; - private static HMACKey hmac128Key; - private static HMACKey hmac256Key; - private static AEADCipher aesGcmCipher; - private static AESKey derivedKey; - private static Signature kdf; - - private static byte[] rngCounter; - private static AESKey aesRngKey; - private static Cipher aesRngCipher; - private static byte[] entropyPool; - private static byte[] rndNum; - - // Implements Oracle Simulator based restricted crypto provider - public KMSimulator() { - // Various Keys - rsa512KeyPair = new KeyPair(KeyPair.ALG_RSA, KeyBuilder.LENGTH_RSA_512); - ec192KeyPair = new KeyPair(KeyPair.ALG_EC_FP, KeyBuilder.LENGTH_EC_FP_192); - aes128Key = (AESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_AES, KeyBuilder.LENGTH_AES_128, false); - triDesKey = - (DESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_DES, KeyBuilder.LENGTH_DES3_2KEY, false); - hmac128Key = (HMACKey) KeyBuilder.buildKey(KeyBuilder.TYPE_HMAC, (short) 128, false); - hmac256Key = (HMACKey) KeyBuilder.buildKey(KeyBuilder.TYPE_HMAC, (short) 256, false); - derivedKey = - (AESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_AES, KeyBuilder.LENGTH_AES_128, false); - kdf = Signature.getInstance(Signature.ALG_AES_CMAC_128, false); - - // RNG - rndNum = JCSystem.makeTransientByteArray(MAX_RND_NUM_SIZE, JCSystem.CLEAR_ON_RESET); - entropyPool = JCSystem.makeTransientByteArray(ENTROPY_POOL_SIZE, JCSystem.CLEAR_ON_RESET); - rngCounter = JCSystem.makeTransientByteArray((short) 8, JCSystem.CLEAR_ON_RESET); - initEntropyPool(entropyPool); - try { - aesRngCipher = Cipher.getInstance(Cipher.ALG_AES_BLOCK_128_CBC_NOPAD, false); - } catch (CryptoException exp) { - ISOException.throwIt(ISO7816.SW_COMMAND_NOT_ALLOWED); - } - aesRngKey = (AESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_AES, KeyBuilder.LENGTH_AES_128, false); - } - - 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 - rsa512KeyPair.genKeyPair(); - return rsa512KeyPair; - } - - public KeyPair createECKeyPair() { - // Simulator does not support 256 bit keys. - // Generate default 192 bit key pair supported by simulator. - ec192KeyPair.genKeyPair(); - return ec192KeyPair; - } - - public AESKey createAESKey(short keysize) { - // keysize is ignored as simulator only supports 128 bit aes key - newRandomNumber(rndNum, (short) 0, (short) 16); - aes128Key.setKey(rndNum, (short) 0); - return aes128Key; - } - - public AESKey createAESKey(byte[] buf, short startOff, short length) { - if (length > 16) length = 16; - else if(length < 16) return null; - aes128Key.setKey(buf, startOff); - return aes128Key; - } - - public DESKey createTDESKey() { - // only 128 bit keys are supported - newRandomNumber(rndNum, (short) 0, (short) 21); - triDesKey.setKey(rndNum, (short) 0); - return triDesKey; - } - - 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. - HMACKey key; - if (keysize == 128) { - key = hmac128Key; - keysize = 16; - } else if (keysize == 256) { - key = hmac256Key; - keysize = 32; - } else { - key = hmac128Key; // by default the simulator will return 128 bit keys for SHA1 - keysize = 16; - } - newRandomNumber(rndNum, (short) 0, keysize); - key.setKey(rndNum, (short) 0, keysize); - return key; - } - - @Override - public short createSymmetricKey(byte alg, short keysize, byte[] buf, short startOff) { - 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. - // Get existing entropy pool. - if (length > 32) length = 32; - // Create new temporary pool. - // Populate the new pool with the entropy which is derived from current entropy pool. - newRandomNumber(rndNum, (short) 0, (short) entropyPool.length); - // Copy the entropy to the current pool - updates the entropy pool. - Util.arrayCopy(rndNum, (short) 0, entropyPool, (short) 0, (short) entropyPool.length); - short index = 0; - short randIndex = 0; - // XOR the seed received from the master in the entropy pool - 16 bytes (entPool.length). - // at a time. - while (index < length) { - entropyPool[randIndex] = (byte) (entropyPool[randIndex] ^ num[(short) (offset + index)]); - randIndex++; - index++; - if (randIndex >= entropyPool.length) { - randIndex = 0; - } - } - } - - @Override - public void getTrueRandomNumber(byte[] num, short offset, short length) { - // ignore the size as simulator only supports 128 bit entropy - Util.arrayCopy(entropyPool,(short)0,num,offset,length); - } - - @Override - public short aesGCMEncrypt( - byte[] aesKey, - short keyStart, - short keyLen, - byte[] secret, - short secretStart, - short secretLen, - byte[] encSecret, - short encSecretStart, - byte[] nonce, - short nonceStart, - short nonceLen, - byte[] authData, - short authDataStart, - short authDataLen, - byte[] authTag, - short authTagStart, - short authTagLen) { - if (jcardSim) return -1; - if(authTagLen != AES_GCM_TAG_LENGTH){ - KMException.throwIt(KMError.UNKNOWN_ERROR); - } - if(nonceLen != AES_GCM_NONCE_LENGTH){ - KMException.throwIt(KMError.UNKNOWN_ERROR); - } - if (aesGcmCipher == null) { - aesGcmCipher = (AEADCipher) Cipher.getInstance(AEADCipher.ALG_AES_GCM, false); - } - 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) { - KMException.throwIt(exp.getReason()); - } - // add the auth data - try { - aesGcmCipher.updateAAD(aad, (short) 0, authDataLen); - } catch (CryptoException exp) { - KMException.throwIt(exp.getReason()); - } - // encrypt the secret - short ciphLen = aesGcmCipher.doFinal(secret, secretStart, secretLen, encSecret, encSecretStart); - // The tag buffer must be exact size otherwise simulator returns 0 tag. - byte[] tag = JCSystem.makeTransientByteArray(AES_GCM_TAG_LENGTH, JCSystem.CLEAR_ON_RESET); - aesGcmCipher.retrieveTag(tag, (short) 0, AES_GCM_TAG_LENGTH); - Util.arrayCopyNonAtomic(tag,(short)0, authTag, authTagStart,AES_GCM_TAG_LENGTH); - return ciphLen; -/* aesGcmCipher.init(key, Cipher.MODE_DECRYPT, nonce, nonceStart, nonceLen); - try { - aesGcmCipher.updateAAD(aad, (short) 0, authDataLen); - } catch (CryptoException exp) { - KMException.throwIt(exp.getReason()); - } - byte[] plain = JCSystem.makeTransientByteArray(secretLen, JCSystem.CLEAR_ON_RESET); - // encrypt the secret - ciphLen = aesGcmCipher.doFinal(encSecret, encSecretStart, ciphLen, plain, (short) 0); - boolean ver = aesGcmCipher.verifyTag(tag, (short) 0, (short) 12, (short) 12); - if (ver == true) { - KMException.throwIt((short) 10); - } else { - KMException.throwIt((short) 20); - } - return 0; - */ - } - - public boolean aesGCMDecrypt( - byte[] aesKey, - short keyStart, - short keyLen, - 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) { - if(jcardSim) return true; - if (aesGcmCipher == null) { - aesGcmCipher = (AEADCipher) Cipher.getInstance(AEADCipher.ALG_AES_GCM, false); - } - // allocate aad buffer of exact size - otherwise simulator throws exception - byte[] aad = JCSystem.makeTransientByteArray(authDataLen, JCSystem.CLEAR_ON_RESET); - Util.arrayCopyNonAtomic(authData, authDataStart, aad, (short) 0, authDataLen); - // allocate tag of exact size. - 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); - //byte[] plain = JCSystem.makeTransientByteArray(encSecretLen, JCSystem.CLEAR_ON_RESET); - // encrypt the secret - aesGcmCipher.doFinal(encSecret, encSecretStart, encSecretLen, secret, secretStart); - verification = aesGcmCipher.verifyTag(tag, (short) 0, (short) 12, (short) 12); - } catch (CryptoException exp) { - KMException.throwIt(KMError.UNKNOWN_ERROR); - } - return verification; - } - - @Override - public short aesCCMSign( - byte[] bufIn, - short bufInStart, - short buffInLength, - byte[] masterKeySecret, - short masterKeyStart, - short masterKeyLen, - byte[] bufOut, - short bufStart) { - if (masterKeyLen > 16) { - - return -1; - } - aes128Key.setKey(masterKeySecret, masterKeyStart); - kdf.init(aes128Key, Signature.MODE_SIGN); - return kdf.sign(bufIn, bufInStart, buffInLength, bufOut, bufStart); - } - - @Override - public short cmacKdf(byte[] aesKey, short aesKeyStart, short aesKeyLen, byte[] label, short labelStart, short labelLen, byte[] context, short contextStart, short contextLength, byte[] key, short keyStart) { - return 0; - } - - public ECPrivateKey createEcPrivateKey(byte[] pubBuffer, short pubOff, short pubLength, - byte[] privBuffer, short privOff, short privLength) { - // Simulator does not support NamedParameterSpec or 256 bit keys - ECPrivateKey privKey = (ECPrivateKey) ec192KeyPair.getPrivate(); - if(privLength > 24){ - privLength = 24;// simulator does not support more then 24 bytes - 192 bit key. - }else if(privLength <= 20){ - return null; - } - privKey.setS(privBuffer,privOff, privLength); - return privKey; - } - - 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. - HMACKey key; - if (secretLength == 16) { - key = hmac128Key; - key.setKey(secretBuffer, secretOff, secretLength); - } else if (secretLength == 32) { - key = hmac256Key; - key.setKey(secretBuffer, secretOff, secretLength); - } else { - key = hmac128Key; // by default the simulator will return 128 bit keys for SHA1 - key.setKey(secretBuffer, secretOff, (short)16); - } - return key; - } - public DESKey createTDESKey(byte[] secretBuffer, short secretOff, short secretLength) { - // only 128 bit keys are supported - if(secretLength < 128) return null; - triDesKey.setKey(secretBuffer, secretOff); - return triDesKey; - } - - public RSAPrivateKey createRsaKey(byte[] modBuffer, short modOff, short modLength, byte[] privBuffer, short privOff, short privLength) { - return null; - } - - public HMACKey cmacKdf(byte[] keyMaterial, byte[] label, byte[] context, short contextStart, short contextLength) { - return null; - } - - public short hmacSign(HMACKey key, byte[] data, short dataStart, short dataLength, byte[] mac, short macStart) { - return 0; - } - - public boolean hmacVerify(HMACKey key, byte[] data, short dataStart, short dataLength, byte[] mac, short macStart, short macLength) { - 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 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 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 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 KMOperation initAsymmetricOperation(byte purpose, byte alg, byte padding, byte digest, byte[] privKeyBuf, short privKeyStart, short privKeyLength, byte[] pubModBuf, short pubModStart, short pubModLength) { - return null; - } - - 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; - else if(privLength < 64)return null; - if(modLength > 64) modLength = 64; - else if( modLength < 64) return null; - privKey.setExponent(privBuffer, privOff, privLength); - privKey.setModulus(modBuffer, modOff, modLength); - return privKey; - } - - private void initEntropyPool(byte[] pool) { - byte index = 0; - RandomData trng; - while (index < rngCounter.length) { - rngCounter[index++] = 0; - } - try { - trng = RandomData.getInstance(RandomData.ALG_TRNG); - trng.nextBytes(pool, (short) 0, (short) pool.length); - } catch (CryptoException exp) { - if (exp.getReason() == CryptoException.NO_SUCH_ALGORITHM) { - // TODO change this when possible - // simulator does not support TRNG algorithm. So, PRNG algorithm (deprecated) is used. - trng = RandomData.getInstance(RandomData.ALG_PSEUDO_RANDOM); - trng.nextBytes(pool, (short) 0, (short) pool.length); - } else { - // TODO change this to proper error code - ISOException.throwIt(ISO7816.SW_UNKNOWN); - } - } - } - - // Generate a secure random number from existing entropy pool. This uses aes ecb algorithm with - // 8 byte rngCounter and 16 byte block size. - @Override - public void newRandomNumber(byte[] num, short startOff, short length) { - KMRepository repository = KMRepository.instance(); - byte[] bufPtr = repository.getHeap(); - short countBufInd = repository.alloc(KMKeymasterApplet.AES_BLOCK_SIZE); - short randBufInd = repository.alloc(KMKeymasterApplet.AES_BLOCK_SIZE); - short len = KMKeymasterApplet.AES_BLOCK_SIZE; - aesRngKey.setKey(entropyPool, (short) 0); - aesRngCipher.init(aesRngKey, Cipher.MODE_ENCRYPT, aesICV, (short) 0, (short) 16); - while (length > 0) { - if (length < len) len = length; - // increment rngCounter by one - incrementCounter(); - // copy the 8 byte rngCounter into the 16 byte rngCounter buffer. - Util.arrayCopy(rngCounter, (short) 0, bufPtr, countBufInd, (short) rngCounter.length); - // encrypt the rngCounter buffer with existing entropy which forms the aes key. - aesRngCipher.doFinal( - bufPtr, countBufInd, KMKeymasterApplet.AES_BLOCK_SIZE, bufPtr, randBufInd); - // copy the encrypted rngCounter block to buffer passed in the argument - Util.arrayCopy(bufPtr, randBufInd, num, startOff, len); - length = (short) (length - len); - startOff = (short) (startOff + len); - } - } - - // increment 8 byte rngCounter by one - private void incrementCounter() { - // start with least significant byte - short index = (short) (rngCounter.length - 1); - while (index >= 0) { - // if the msb of current byte is set then it will be negative - if (rngCounter[index] < 0) { - // then increment the rngCounter - rngCounter[index]++; - // is the msb still set? i.e. no carry over - if (rngCounter[index] < 0) break; // then break - else index--; // else go to the higher order byte - } else { - // if msb is not set then increment the rngCounter - rngCounter[index]++; - break; - } - } - } - - @Override - public boolean isBackupRestoreSupported() { - return false; - } - - @Override - public KMAttestationCert getAttestationCert(boolean rsaCert) { - return null; - } - - @Override - public void backup(byte[] buf, short start, short len) { - - } - - @Override - public short restore(byte[] buf, short start) { - return 0; - } - -} diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMKeymasterStore.java b/Applet/Applet/src/com/android/javacard/keymaster/KMKeymasterStore.java deleted file mode 100644 index ed9286f6..00000000 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMKeymasterStore.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.android.javacard.keymaster; - -public interface KMKeymasterStore { - short getMasterKeySecret(byte[] buf, short start); - void createDocument(byte documentId, byte[]buf, short start, short len); - void updateData(byte documentId, short keyId, byte[]buf, short start, short len); - short getData(byte documentId, short keyId, byte[] buf, short start); - short getDocument(byte documentId, byte[] buf, short start); -} diff --git a/Applet/JCardSimProvider/build.xml b/Applet/JCardSimProvider/build.xml new file mode 100644 index 00000000..58343402 --- /dev/null +++ b/Applet/JCardSimProvider/build.xml @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Applet/JCardSimProvider/lib/gpapi-upgrade.jar b/Applet/JCardSimProvider/lib/gpapi-upgrade.jar new file mode 100644 index 00000000..e4814bde Binary files /dev/null and b/Applet/JCardSimProvider/lib/gpapi-upgrade.jar differ diff --git a/Applet/lib/hamcrest-core-1.3.jar b/Applet/JCardSimProvider/lib/hamcrest-core-1.3.jar similarity index 100% rename from Applet/lib/hamcrest-core-1.3.jar rename to Applet/JCardSimProvider/lib/hamcrest-core-1.3.jar diff --git a/Applet/lib/jcardsim-3.0.5-SNAPSHOT.jar b/Applet/JCardSimProvider/lib/jcardsim-3.0.5-SNAPSHOT.jar similarity index 100% rename from Applet/lib/jcardsim-3.0.5-SNAPSHOT.jar rename to Applet/JCardSimProvider/lib/jcardsim-3.0.5-SNAPSHOT.jar diff --git a/Applet/lib/junit-4.13.jar b/Applet/JCardSimProvider/lib/junit-4.13.jar similarity index 100% rename from Applet/lib/junit-4.13.jar rename to Applet/JCardSimProvider/lib/junit-4.13.jar diff --git a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMAttestationCertImpl.java b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMAttestationCertImpl.java new file mode 100644 index 00000000..136ec4ba --- /dev/null +++ b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMAttestationCertImpl.java @@ -0,0 +1,947 @@ +package com.android.javacard.keymaster; + +import javacard.framework.JCSystem; +import javacard.framework.Util; + +// 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. +// Whatever fields that are fixed are added as byte arrays. The Extensions are encoded as per +// the values. +// The certificate is assembled with leafs first and then the sequences. + +public class KMAttestationCertImpl implements KMAttestationCert { + private static final byte MAX_PARAMS = 30; + // DER encoded object identifiers required by the cert. + // 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 + }; + // ecPublicKey - 1.2.840.10045.2.1 + private static final byte[] eccPubKey = { + 0x06, 0x07, 0x2A, (byte) 0x86, 0x48, (byte) 0xCE, 0x3D, 0x02, 0x01 + }; + // prime256v1 curve - 1.2.840.10045.3.1.7 + private static final byte[] prime256v1 = { + 0x06, 0x08, 0x2A, (byte) 0x86, 0x48, (byte) 0xCE, 0x3D, 0x03, 0x01, 0x07 + }; + // Key Usage Extn - 2.5.29.15 + private static final byte[] keyUsageExtn = {0x06, 0x03, 0x55, 0x1D, 0x0F}; + // Android Extn - 1.3.6.1.4.1.11129.2.1.17 + private static final byte[] androidExtn = { + 0x06, 0x0A, 0X2B, 0X06, 0X01, 0X04, 0X01, (byte) 0XD6, 0X79, 0X02, 0X01, 0X11 + }; + // Authority Key Identifier Extn - 2.5.29.35 + private static final byte[] authKeyIdExtn = {0x06, 0x03, 0X55, 0X1D, 0X23}; + + private static final short ECDSA_MAX_SIG_LEN = 72; + //Signature algorithm identifier - always ecdsaWithSha256 - 1.2.840.10045.4.3.2 + //SEQUENCE of alg OBJ ID and parameters = NULL. + private static final byte[] X509SignAlgIdentifier = { + 0x30, + 0x0A, + 0x06, + 0x08, + 0x2A, + (byte) 0x86, + 0x48, + (byte) 0xCE, + (byte) 0x3D, + 0x04, + 0x03, + 0x02 + }; + // 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 = { + 0x30, 0x1F, 0x31, 0x1D, 0x30, 0x1B, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x14, 0x41, 0x6e, 0x64, + 0x72, 0x6f, 0x69, 0x64, 0x20, 0x4B, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x20, 0x4B, 0x65, + 0x79 + }; + + private static final byte keyUsageSign = (byte) 0x80; // 0 bit + private static final byte keyUsageKeyEncipher = (byte) 0x20; // 2nd- bit + private static final byte keyUsageDataEncipher = (byte) 0x10; // 3rd- bit + + private static final byte KEYMASTER_VERSION = 4; + private static final byte ATTESTATION_VERSION = 3; + private static final byte[] pubExponent = {0x01, 0x00, 0x01}; + private static final byte SERIAL_NUM = (byte) 0x01; + private static final byte X509_VERSION = (byte) 0x02; + + private static short certStart; + private static short signatureOffset; + private static short tbsOffset; + private static short tbsLength; + + private static short stackPtr; + private static byte[] stack; + private static short start; + private static short length; + // 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 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(); + 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; + notBefore = 0; + notAfter = 0; + pubKey = 0; + uniqueId = 0; + verifiedBootKey = 0; + verifiedHash = 0; + verifiedState = 0; + rsaCert = true; + deviceLocked = 0; + authKey = 0; + signPriv = 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 verifiedBootState(byte val) { + verifiedState = val; + return this; + } + + private KMAttestationCert uniqueId(short obj) { + uniqueId = obj; + return this; + } + + @Override + public KMAttestationCert notBefore(short obj, byte[] scratchpad) { + // convert milliseconds to UTC date + notBefore = KMUtils.convertToDate(obj, scratchpad, true); + return this; + } + + @Override + public KMAttestationCert notAfter(short usageExpiryTimeObj, + short certExpirtyTimeObj, byte[] scratchPad, short tmpVar) { + if (usageExpiryTimeObj != KMType.INVALID_VALUE) { + // compare if the expiry time is greater then 2051 then use generalized + // time format else use utc time format. + usageExpiryTimeObj = KMIntegerTag.cast(usageExpiryTimeObj).getValue(); + tmpVar = KMInteger.uint_64(KMUtils.firstJan2051, (short) 0); + if (KMInteger.compare(usageExpiryTimeObj, tmpVar) >= 0) + usageExpiryTimeObj = KMUtils.convertToDate(usageExpiryTimeObj, scratchPad, + false); + else + usageExpiryTimeObj = KMUtils + .convertToDate(usageExpiryTimeObj, scratchPad, true); + notAfter = usageExpiryTimeObj; + } else { + notAfter = certExpirtyTimeObj; + } + return this; + } + + @Override + public KMAttestationCert deviceLocked(boolean val) { + if (val) deviceLocked = (byte) 0xFF; + else deviceLocked = 0; + return this; + } + + @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, + short notBefore, + short notAfter, + short pubKey, + short attChallenge, + short attAppId, + boolean rsaCert) { + init(); + stack = KMByteBlob.cast(buf).getBuffer(); + start = KMByteBlob.cast(buf).getStartOff(); + length = KMByteBlob.cast(buf).length(); + stackPtr = (short) (start + length); + /* 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; + 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)); + // print(stack, stackPtr, (short)(last - stackPtr)); + certStart = stackPtr; + } + + private static void pushTbsCert(boolean rsaCert) { + short last = stackPtr; + pushExtensions(); + // subject public key info + if (rsaCert) { + pushRsaSubjectKeyInfo(); + } else { + pushEccSubjectKeyInfo(); + } + // subject + pushBytes(X509Subject, (short) 0, (short) X509Subject.length); + pushValidity(); + // issuer - der encoded + // 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 + pushByte(SERIAL_NUM); + pushIntegerHeader((short) 1); + // Version + pushByte(X509_VERSION); + pushIntegerHeader((short) 1); + pushByte((byte) 0x03); + pushByte((byte) 0xA0); + // Finally sequence header. + pushSequenceHeader((short) (last - stackPtr)); + } + + private static void pushExtensions() { + short last = stackPtr; + // byte keyusage = 0; + // byte unusedBits = 8; + pushAuthKeyId(); + /* + if (KMEnumArrayTag.contains(KMType.PURPOSE, KMType.SIGN, hwParams)) { + keyusage = (byte) (keyusage | keyUsageSign); + unusedBits = 7; + } + if (KMEnumArrayTag.contains(KMType.PURPOSE, KMType.WRAP_KEY, hwParams)) { + keyusage = (byte) (keyusage | keyUsageKeyEncipher); + unusedBits = 5; + } + if (KMEnumArrayTag.contains(KMType.PURPOSE, KMType.DECRYPT, hwParams)) { + keyusage = (byte) (keyusage | keyUsageDataEncipher); + unusedBits = 4; + } + + */ + if (keyUsage != 0) pushKeyUsage(keyUsage, unusedBits); + pushKeyDescription(); + pushSequenceHeader((short) (last - stackPtr)); + // Extensions have explicit tag of [3] + pushLength((short) (last - stackPtr)); + pushByte((byte) 0xA3); + } + + // Time SEQUENCE{UTCTime, UTC or Generalized Time) + private static void pushValidity() { + short last = stackPtr; + if (notAfter != 0) { + pushBytes( + KMByteBlob.cast(notAfter).getBuffer(), + KMByteBlob.cast(notAfter).getStartOff(), + KMByteBlob.cast(notAfter).length()); + } else { + KMException.throwIt(KMError.INVALID_DATA); + } + pushTimeHeader(KMByteBlob.cast(notAfter).length()); + pushBytes( + KMByteBlob.cast(notBefore).getBuffer(), + KMByteBlob.cast(notBefore).getStartOff(), + KMByteBlob.cast(notBefore).length()); + pushTimeHeader(KMByteBlob.cast(notBefore).length()); + pushSequenceHeader((short) (last - stackPtr)); + } + + private static void pushTimeHeader(short len) { + if (len == 13) { // UTC Time + pushLength((short) 0x0D); + pushByte((byte) 0x17); + } else if (len == 15) { // Generalized Time + pushLength((short) 0x0F); + pushByte((byte) 0x18); + } else { + 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() { + short last = stackPtr; + pushBytes(pubExponent, (short) 0, (short) pubExponent.length); + pushIntegerHeader((short) pubExponent.length); + pushBytes( + 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) { + pushByte((byte) 0x00); + pushIntegerHeader((short) (KMByteBlob.cast(pubKey).length() + 1)); + } else { + pushIntegerHeader(KMByteBlob.cast(pubKey).length()); + } + pushSequenceHeader((short) (last - stackPtr)); + pushBitStringHeader((byte) 0x00, (short) (last - stackPtr)); + pushRsaEncryption(); + pushSequenceHeader((short) (last - stackPtr)); + } + // SEQUENCE{SEQUENCE{ecPubKey, prime256v1}, bitString{pubKey}} + private static void pushEccSubjectKeyInfo() { + short last = stackPtr; + pushBytes( + KMByteBlob.cast(pubKey).getBuffer(), + KMByteBlob.cast(pubKey).getStartOff(), + KMByteBlob.cast(pubKey).length()); + pushBitStringHeader((byte) 0x00, KMByteBlob.cast(pubKey).length()); + pushEcDsa(); + pushSequenceHeader((short) (last - stackPtr)); + } + + private static void pushEcDsa() { + short last = stackPtr; + pushBytes(prime256v1, (short) 0, (short) prime256v1.length); + pushBytes(eccPubKey, (short) 0, (short) eccPubKey.length); + pushSequenceHeader((short) (last - stackPtr)); + } + + private static void pushRsaEncryption() { + short last = stackPtr; + pushNullHeader(); + pushBytes(rsaEncryption, (short) 0, (short) rsaEncryption.length); + pushSequenceHeader((short) (last - stackPtr)); + } + // KeyDescription ::= SEQUENCE { + // attestationVersion INTEGER, # Value 3 + // attestationSecurityLevel SecurityLevel, # See below + // keymasterVersion INTEGER, # Value 4 + // keymasterSecurityLevel SecurityLevel, # See below + // attestationChallenge OCTET_STRING, # Tag::ATTESTATION_CHALLENGE from attestParams + // uniqueId OCTET_STRING, # Empty unless key has Tag::INCLUDE_UNIQUE_ID + // softwareEnforced AuthorizationList, # See below + // hardwareEnforced AuthorizationList, # See below + // } + private static void pushKeyDescription() { + short last = stackPtr; + pushHWParams(); + pushSWParams(); + if (uniqueId != 0) { + pushOctetString( + 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()); + pushEnumerated(KMType.STRONGBOX); + pushByte(KEYMASTER_VERSION); + pushIntegerHeader((short) 1); + pushEnumerated(KMType.STRONGBOX); + pushByte(ATTESTATION_VERSION); + pushIntegerHeader((short) 1); + pushSequenceHeader((short) (last - stackPtr)); + pushOctetStringHeader((short) (last - stackPtr)); + pushBytes(androidExtn, (short) 0, (short) androidExtn.length); + pushSequenceHeader((short) (last - stackPtr)); + } + + private static void pushSWParams() { + short last = stackPtr; + // Below are the allowed softwareEnforced Authorization tags inside the attestation certificate's extension. + short[] tagIds = { + KMType.ATTESTATION_APPLICATION_ID, KMType.CREATION_DATETIME, + KMType.USAGE_EXPIRE_DATETIME, KMType.ORIGINATION_EXPIRE_DATETIME, + KMType.ACTIVE_DATETIME, KMType.UNLOCKED_DEVICE_REQUIRED }; + byte index = 0; + do { + pushParams(swParams, swParamsIndex, tagIds[index]); + } while (++index < tagIds.length); + pushSequenceHeader((short) (last - stackPtr)); + } + + private static void pushHWParams() { + short last = stackPtr; + // Below are the allowed hardwareEnforced Authorization tags inside the attestation certificate's extension. + short[] tagIds = { + KMType.BOOT_PATCH_LEVEL, KMType.VENDOR_PATCH_LEVEL, + KMType.OS_PATCH_LEVEL, KMType.OS_VERSION, KMType.ROOT_OF_TRUST, + KMType.ORIGIN, KMType.APPLICATION_ID, + KMType.TRUSTED_CONFIRMATION_REQUIRED, + KMType.TRUSTED_USER_PRESENCE_REQUIRED, KMType.ALLOW_WHILE_ON_BODY, + KMType.AUTH_TIMEOUT, KMType.USER_AUTH_TYPE, KMType.NO_AUTH_REQUIRED, + KMType.ROLLBACK_RESISTANCE, KMType.RSA_PUBLIC_EXPONENT, + KMType.ECCURVE, KMType.PADDING, KMType.DIGEST, KMType.KEYSIZE, + KMType.ALGORITHM, KMType.PURPOSE }; + + byte index = 0; + do { + if (tagIds[index] == KMType.ROOT_OF_TRUST) { + pushRoT(); + continue; + } + if (pushParams(hwParams, hwParamsIndex, tagIds[index])) continue; + } while (++index < tagIds.length); + pushSequenceHeader((short) (last - stackPtr)); + } + + private static boolean pushParams(short[] params, short len, short tagId) { + short index = 0; + while (index < len) { + if (tagId == KMTag.getKey(params[index])) { + pushTag(params[index]); + return true; + } + index++; + } + return false; + } + + private static void pushTag(short tag) { + short type = KMTag.getTagType(tag); + short tagId = KMTag.getKey(tag); + short val; + switch (type) { + case KMType.BYTES_TAG: + val = KMByteTag.cast(tag).getValue(); + pushBytesTag( + tagId, + KMByteBlob.cast(val).getBuffer(), + KMByteBlob.cast(val).getStartOff(), + KMByteBlob.cast(val).length()); + break; + case KMType.ENUM_TAG: + val = KMEnumTag.cast(tag).getValue(); + pushEnumTag(tagId, (byte) val); + break; + case KMType.ENUM_ARRAY_TAG: + val = KMEnumArrayTag.cast(tag).getValues(); + pushEnumArrayTag( + tagId, + KMByteBlob.cast(val).getBuffer(), + KMByteBlob.cast(val).getStartOff(), + KMByteBlob.cast(val).length()); + break; + case KMType.UINT_TAG: + case KMType.ULONG_TAG: + case KMType.DATE_TAG: + val = KMIntegerTag.cast(tag).getValue(); + pushIntegerTag( + tagId, + KMInteger.cast(val).getBuffer(), + KMInteger.cast(val).getStartOff(), + KMInteger.cast(val).length()); + break; + case KMType.UINT_ARRAY_TAG: + case KMType.ULONG_ARRAY_TAG: + // According to keymaster hal only one user secure id is used but this conflicts with + // tag type which is ULONG-REP. Currently this is encoded as SET OF INTEGERS + val = KMIntegerArrayTag.cast(tag).getValues(); + pushIntegerArrayTag(tagId, val); + break; + case KMType.BOOL_TAG: + val = KMBoolTag.cast(tag).getVal(); + pushBoolTag(tagId); + break; + default: + KMException.throwIt(KMError.INVALID_TAG); + break; + } + } + // RootOfTrust ::= SEQUENCE { + // verifiedBootKey OCTET_STRING, + // deviceLocked BOOLEAN, + // verifiedBootState VerifiedBootState, + // verifiedBootHash OCTET_STRING, + // } + // VerifiedBootState ::= ENUMERATED { + // Verified (0), + // SelfSigned (1), + // Unverified (2), + // Failed (3), + // } + private static void pushRoT() { + short last = stackPtr; + byte val = 0x00; + // verified boot hash + // pushOctetString(repo.verifiedBootHash, (short) 0, (short) repo.verifiedBootHash.length); + pushOctetString( + KMByteBlob.cast(verifiedHash).getBuffer(), + KMByteBlob.cast(verifiedHash).getStartOff(), + KMByteBlob.cast(verifiedHash).length()); + + pushEnumerated(verifiedState); + + pushBoolean(deviceLocked); + // verified boot Key + pushOctetString( + KMByteBlob.cast(verifiedBootKey).getBuffer(), + KMByteBlob.cast(verifiedBootKey).getStartOff(), + KMByteBlob.cast(verifiedBootKey).length()); + + // Finally sequence header + pushSequenceHeader((short) (last - stackPtr)); + // ... and tag Id + pushTagIdHeader(KMType.ROOT_OF_TRUST, (short) (last - stackPtr)); + } + + private static void pushOctetString(byte[] buf, short start, short len) { + pushBytes(buf, start, len); + pushOctetStringHeader(len); + } + + private static void pushBoolean(byte val) { + pushByte(val); + pushBooleanHeader((short) 1); + } + + private static void pushBooleanHeader(short len) { + pushLength(len); + pushByte((byte) 0x01); + } + + // 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) { + short last = stackPtr; + short index = 0; + while (index < len) { + pushByte(buf[(short) (start + index)]); + pushIntegerHeader((short) 1); + index++; + } + pushSetHeader((short) (last - stackPtr)); + pushTagIdHeader(tagId, (short) (last - stackPtr)); + } + // 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 pushIntegerArrayTag(short tagId, short arr) { + short last = stackPtr; + short index = 0; + short len = KMArray.cast(arr).length(); + short ptr; + while (index < len) { + ptr = KMArray.cast(arr).get(index); + pushInteger( + KMInteger.cast(ptr).getBuffer(), + KMInteger.cast(ptr).getStartOff(), + KMInteger.cast(ptr).length()); + index++; + } + pushSetHeader((short) (last - stackPtr)); + pushTagIdHeader(tagId, (short) (last - stackPtr)); + } + + private static void pushSetHeader(short len) { + pushLength(len); + pushByte((byte) 0x31); + } + + private static void pushEnumerated(byte val) { + short last = stackPtr; + pushByte(val); + pushEnumeratedHeader((short) (last - stackPtr)); + } + + private static void pushEnumeratedHeader(short len) { + pushLength(len); + pushByte((byte) 0x0A); + } + + private static void pushBoolTag(short tagId) { + short last = stackPtr; + pushNullHeader(); + pushTagIdHeader(tagId, (short) (last - stackPtr)); + } + + private static void pushNullHeader() { + pushByte((byte) 0); + pushByte((byte) 0x05); + } + + private static void pushEnumTag(short tagId, byte val) { + short last = stackPtr; + pushByte(val); + pushIntegerHeader((short) (last - stackPtr)); + pushTagIdHeader(tagId, (short) (last - stackPtr)); + } + + private static void pushIntegerTag(short tagId, byte[] buf, short start, short len) { + short last = stackPtr; + pushInteger(buf, start, len); + // pushIntegerHeader((short) (last - stackPtr)); + pushTagIdHeader(tagId, (short) (last - stackPtr)); + } + // Ignore leading zeros. Only Unsigned Integers are required hence if MSB is set then add 0x00 + // as most significant byte. + private static void pushInteger(byte[] buf, short start, short len) { + short last = stackPtr; + byte index = 0; + while (index < (byte) len) { + if (buf[(short) (start + index)] != 0) break; + index++; + } + if (index == (byte) len) { + pushByte((byte) 0x00); + } else { + pushBytes(buf, (short) (start + index), (short) (len - index)); + if (buf[(short) (start + index)] < 0) { // MSB is 1 + pushByte((byte) 0x00); // always unsigned int + } + } + pushIntegerHeader((short) (last - stackPtr)); + } + // Bytes Tag is a octet string and tag id is added explicitly + private static void pushBytesTag(short tagId, byte[] buf, short start, short len) { + short last = stackPtr; + pushBytes(buf, start, len); + pushOctetStringHeader((short) (last - stackPtr)); + pushTagIdHeader(tagId, (short) (last - stackPtr)); + } + + // tag id <= 30 ---> 0xA0 | {tagId} + // 30 < tagId < 128 ---> 0xBF 0x{tagId} + // tagId >= 128 ---> 0xBF 0x80+(tagId/128) 0x{tagId - (128*(tagId/128))} + private static void pushTagIdHeader(short tagId, short len) { + pushLength(len); + short count = (short) (tagId / 128); + if (count > 0) { + pushByte((byte) (tagId - (128 * count))); + pushByte((byte) (0x80 + count)); + pushByte((byte) 0xBF); + } else if (tagId > 30) { + pushByte((byte) tagId); + pushByte((byte) 0xBF); + } else { + pushByte((byte) (0xA0 | (byte) tagId)); + } + } + // SEQUENCE {ObjId, OCTET STRING{BIT STRING{keyUsage}}} + private static void pushKeyUsage(byte keyUsage, byte unusedBits) { + short last = stackPtr; + pushByte(keyUsage); + pushBitStringHeader(unusedBits, (short) (last - stackPtr)); + pushOctetStringHeader((short) (last - stackPtr)); + 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 (authKey == 0) return; + + 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 + pushSequenceHeader((short) (last - stackPtr)); + } + + private static void pushKeyIdentifier(byte[] buf, short start, short len) { + pushBytes(buf, start, len); // keyIdentifier + pushLength(len); // len + pushByte((byte) 0x80); // Context specific tag [0] + } + + private static void pushAlgorithmId(byte[] algId) { + pushBytes(algId, (short) 0, (short) algId.length); + } + + private static short pushSignature(byte[] buf, short start, short len) { + pushBytes(buf, start, len); + short signatureOff = stackPtr; + pushBitStringHeader((byte) 0, len); + return signatureOff; + } + + private static void pushIntegerHeader(short len) { + pushLength(len); + pushByte((byte) 0x02); + } + + private static void pushOctetStringHeader(short len) { + pushLength(len); + pushByte((byte) 0x04); + } + + private static void pushSequenceHeader(short len) { + pushLength(len); + pushByte((byte) 0x30); + } + + private static void pushBitStringHeader(byte unusedBits, short len) { + pushByte(unusedBits); + pushLength((short) (len + 1)); // 1 extra byte for unused bits byte + pushByte((byte) 0x03); + } + + private static void pushLength(short len) { + if (len < 128) { + pushByte((byte) len); + } else if (len < 256) { + pushByte((byte) len); + pushByte((byte) 0x81); + } else { + pushShort(len); + pushByte((byte) 0x82); + } + } + + private static void pushShort(short val) { + decrementStackPtr((short) 2); + Util.setShort(stack, stackPtr, val); + } + + private static void pushByte(byte val) { + decrementStackPtr((short) 1); + stack[stackPtr] = val; + } + + private static void pushBytes(byte[] buf, short start, short len) { + decrementStackPtr(len); + if (buf != null) { + Util.arrayCopyNonAtomic(buf, start, stack, stackPtr, len); + } + } + + private static void decrementStackPtr(short cnt) { + stackPtr = (short) (stackPtr - cnt); + if (start > stackPtr) KMException.throwIt(KMError.UNKNOWN_ERROR); + } + + @Override + public KMAttestationCert buffer(byte[] buf, short bufStart, short maxLen) { + stack = buf; + start = bufStart; + length = maxLen; + stackPtr = (short) (start + length); + return this; + } + + @Override + public KMAttestationCert signingKey(short privKey) { + signPriv = privKey; + return this; + } + + @Override + public short getCertStart() { + return certStart; + } + + @Override + public short getCertEnd() { + return (short) (start + length - 1); + } + + @Override + public short getCertLength() { + return (short) (getCertEnd() - getCertStart() + 1); + } + + @Override + public void build() { + short last = stackPtr; + decrementStackPtr((short) ECDSA_MAX_SIG_LEN); + signatureOffset = stackPtr; + pushBitStringHeader((byte) 0, (short) (last - stackPtr)); + pushAlgorithmId(X509SignAlgIdentifier); + tbsLength = stackPtr; + pushTbsCert(rsaCert); + tbsOffset = stackPtr; + tbsLength = (short) (tbsLength - tbsOffset); + pushSequenceHeader((short) (last - stackPtr)); + certStart = stackPtr; + short sigLen = KMJCardSimulator.getInstance() + .ecSign256( + KMByteBlob.cast(signPriv).getBuffer(), + KMByteBlob.cast(signPriv).getStartOff(), + KMByteBlob.cast(signPriv).length(), + stack, + tbsOffset, + tbsLength, + stack, + signatureOffset); + if(sigLen != ECDSA_MAX_SIG_LEN) { + // Update the lengths appropriately. + stackPtr = (short)(signatureOffset - 1); + pushLength((short)(sigLen + 1)); + stackPtr = tbsOffset; + last -= (short)(ECDSA_MAX_SIG_LEN - sigLen); + pushLength((short)(last - stackPtr)); + length -= (short)(ECDSA_MAX_SIG_LEN - sigLen); + } + } + + @Override + public KMAttestationCert makeUniqueId(byte[] scratchPad, short scratchPadOff, + byte[] creationTime, short timeOffset, short creationTimeLen, + byte[] attestAppId, short appIdOff, short attestAppIdLen, + byte resetSinceIdRotation, byte[] key, short keyOff, short keyLen) { + // Concatenate T||C||R + // temporal count T + short temp = KMUtils.countTemporalCount(creationTime, timeOffset, + creationTimeLen, scratchPad, scratchPadOff); + Util.setShort(scratchPad, (short) scratchPadOff, temp); + temp = scratchPadOff; + scratchPadOff += 2; + + // Application Id C + Util.arrayCopyNonAtomic(attestAppId, appIdOff, scratchPad, scratchPadOff, + attestAppIdLen); + scratchPadOff += attestAppIdLen; + + // Reset After Rotation R + scratchPad[scratchPadOff] = resetSinceIdRotation; + scratchPadOff++; + + timeOffset = KMByteBlob.instance((short) 32); + appIdOff = KMJCardSimulator.getInstance().hmacSign(key, keyOff, keyLen, + scratchPad, /* data */ + temp, /* data start */ + scratchPadOff, /* data length */ + KMByteBlob.cast(timeOffset).getBuffer(), /* signature buffer */ + KMByteBlob.cast(timeOffset).getStartOff()); /* signature start */ + if (appIdOff != 32) { + KMException.throwIt(KMError.UNKNOWN_ERROR); + } + return uniqueId(timeOffset); + } +} diff --git a/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMCipher.java b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMCipher.java similarity index 97% rename from Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMCipher.java rename to Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMCipher.java index b599c003..145fea9d 100644 --- a/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMCipher.java +++ b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMCipher.java @@ -1,51 +1,51 @@ -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; - public static final byte PAD_PKCS1_OAEP_SHA256 = 14; - public static final short PAD_PKCS1_OAEP_SHA384 = 15; - public static final short PAD_PKCS1_OAEP_SHA512 = 16; - public static final short PAD_NOPAD = 1; - public static final short PAD_PKCS1_PSS = 8; - public static final short PAD_NULL = 0; - public static final short PAD_PKCS7 = 31; // Not supported in javacard - public static final short ALG_DES_CBC_NOPAD = 1; - public static final short ALG_DES_ECB_NOPAD = 5; - public static final short ALG_AES_BLOCK_128_CBC_NOPAD= 13; - public static final short ALG_AES_BLOCK_128_ECB_NOPAD = 14; - public static final short ALG_AES_GCM = -13; - public static final short MODE_ENCRYPT = 2; - public static final short MODE_DECRYPT = 1; - public static final short PAD_PKCS1 = 7; - 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); - - public abstract short update(byte[] buffer, short startOff, short length, byte[] scratchPad, short i); - - 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(); - - public abstract void setPaddingAlgorithm(short alg); - - public abstract void setCipherAlgorithm(short alg); - - public abstract short getCipherProvider(); - - public abstract short getAesGcmOutputSize(short len, short macLength); -} +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; + public static final byte PAD_PKCS1_OAEP_SHA256 = 14; + public static final short PAD_PKCS1_OAEP_SHA384 = 15; + public static final short PAD_PKCS1_OAEP_SHA512 = 16; + public static final short PAD_NOPAD = 1; + public static final short PAD_PKCS1_PSS = 8; + public static final short PAD_NULL = 0; + public static final short PAD_PKCS7 = 31; // Not supported in javacard + public static final short ALG_DES_CBC_NOPAD = 1; + public static final short ALG_DES_ECB_NOPAD = 5; + public static final short ALG_AES_BLOCK_128_CBC_NOPAD= 13; + public static final short ALG_AES_BLOCK_128_ECB_NOPAD = 14; + public static final short ALG_AES_GCM = -13; + public static final short MODE_ENCRYPT = 2; + public static final short MODE_DECRYPT = 1; + public static final short PAD_PKCS1 = 7; + 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); + + public abstract short update(byte[] buffer, short startOff, short length, byte[] scratchPad, short i); + + 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(); + + public abstract void setPaddingAlgorithm(short alg); + + public abstract void setCipherAlgorithm(short alg); + + public abstract short getCipherProvider(); + + public abstract short getAesGcmOutputSize(short len, short macLength); +} diff --git a/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMCipherImpl.java b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMCipherImpl.java similarity index 97% rename from Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMCipherImpl.java rename to Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMCipherImpl.java index 65ebfad0..ea65a94c 100644 --- a/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMCipherImpl.java +++ b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMCipherImpl.java @@ -1,220 +1,220 @@ -package com.android.javacard.keymaster; - -import javacard.framework.Util; -import javacard.security.CryptoException; -import javacardx.crypto.Cipher; -import javax.crypto.AEADBadTagException; -import javax.crypto.BadPaddingException; -import javax.crypto.IllegalBlockSizeException; -import javax.crypto.ShortBufferException; - - -public class KMCipherImpl extends KMCipher{ - private Cipher cipher; - private javax.crypto.Cipher sunCipher; - private short cipherAlg; - private short padding; - private short mode; - private boolean verificationFlag; - private short blockMode; - KMCipherImpl(Cipher c){ - cipher = c; - } - KMCipherImpl(javax.crypto.Cipher c){ - sunCipher = c; - } - - @Override - public short doFinal(byte[] buffer, short startOff, short length, byte[] scratchPad, short i){ - if(cipherAlg == KMType.RSA && padding == KMType.RSA_OAEP){ - try { - return (short)sunCipher.doFinal(buffer,startOff,length,scratchPad,i); - } catch (ShortBufferException e) { - e.printStackTrace(); - CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); - } catch (IllegalBlockSizeException e) { - e.printStackTrace(); - CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); - } catch (BadPaddingException e) { - e.printStackTrace(); - CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); - } - }else if(cipherAlg == KMType.AES && blockMode == KMType.GCM){ - try { - return (short)sunCipher.doFinal(buffer,startOff,length,scratchPad,i); - } catch (AEADBadTagException e) { - e.printStackTrace(); - verificationFlag = false; - KMException.throwIt(KMError.VERIFICATION_FAILED); - } catch (ShortBufferException e) { - e.printStackTrace(); - CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); - } catch (IllegalBlockSizeException e) { - CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); - } catch (BadPaddingException e) { - CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); - } - } else if(cipherAlg == KMType.AES && blockMode == KMType.CTR){ - try { - return (short)sunCipher.doFinal(buffer,startOff,length,scratchPad,i); - } catch (ShortBufferException e) { - e.printStackTrace(); - CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); - } catch (IllegalBlockSizeException e) { - e.printStackTrace(); - CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); - } catch (BadPaddingException e) { - e.printStackTrace(); - CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); - } - } 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 - 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; - } - return KMType.INVALID_VALUE; - } - - @Override - public short getCipherAlgorithm() { - return cipherAlg; - } - - @Override - public void setCipherAlgorithm(short alg) { - cipherAlg = alg; - } - - @Override - public short update(byte[] buffer, short startOff, short length, byte[] scratchPad, short i) { - 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); - } - return KMType.INVALID_VALUE; - } - - @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 - public short getPaddingAlgorithm() { - return padding; - } - - @Override - public void setPaddingAlgorithm(short alg) { - padding = alg; - } - - @Override - public void setBlockMode(short mode){ - blockMode = mode; - } - - @Override - public short getBlockMode(){ - return blockMode; - } - - public short getMode() { - return mode; - } - - 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); - } - } - } - -} +package com.android.javacard.keymaster; + +import javacard.framework.Util; +import javacard.security.CryptoException; +import javacardx.crypto.Cipher; +import javax.crypto.AEADBadTagException; +import javax.crypto.BadPaddingException; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.ShortBufferException; + + +public class KMCipherImpl extends KMCipher{ + private Cipher cipher; + private javax.crypto.Cipher sunCipher; + private short cipherAlg; + private short padding; + private short mode; + private boolean verificationFlag; + private short blockMode; + KMCipherImpl(Cipher c){ + cipher = c; + } + KMCipherImpl(javax.crypto.Cipher c){ + sunCipher = c; + } + + @Override + public short doFinal(byte[] buffer, short startOff, short length, byte[] scratchPad, short i){ + if(cipherAlg == KMType.RSA && padding == KMType.RSA_OAEP){ + try { + return (short)sunCipher.doFinal(buffer,startOff,length,scratchPad,i); + } catch (ShortBufferException e) { + e.printStackTrace(); + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + } catch (IllegalBlockSizeException e) { + e.printStackTrace(); + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + } catch (BadPaddingException e) { + e.printStackTrace(); + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + } + }else if(cipherAlg == KMType.AES && blockMode == KMType.GCM){ + try { + return (short)sunCipher.doFinal(buffer,startOff,length,scratchPad,i); + } catch (AEADBadTagException e) { + e.printStackTrace(); + verificationFlag = false; + KMException.throwIt(KMError.VERIFICATION_FAILED); + } catch (ShortBufferException e) { + e.printStackTrace(); + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + } catch (IllegalBlockSizeException e) { + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + } catch (BadPaddingException e) { + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + } + } else if(cipherAlg == KMType.AES && blockMode == KMType.CTR){ + try { + return (short)sunCipher.doFinal(buffer,startOff,length,scratchPad,i); + } catch (ShortBufferException e) { + e.printStackTrace(); + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + } catch (IllegalBlockSizeException e) { + e.printStackTrace(); + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + } catch (BadPaddingException e) { + e.printStackTrace(); + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + } + } 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 - 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; + } + return KMType.INVALID_VALUE; + } + + @Override + public short getCipherAlgorithm() { + return cipherAlg; + } + + @Override + public void setCipherAlgorithm(short alg) { + cipherAlg = alg; + } + + @Override + public short update(byte[] buffer, short startOff, short length, byte[] scratchPad, short i) { + 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); + } + return KMType.INVALID_VALUE; + } + + @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 + public short getPaddingAlgorithm() { + return padding; + } + + @Override + public void setPaddingAlgorithm(short alg) { + padding = alg; + } + + @Override + public void setBlockMode(short mode){ + blockMode = mode; + } + + @Override + public short getBlockMode(){ + return blockMode; + } + + public short getMode() { + return mode; + } + + 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/JCardSimProvider/src/com/android/javacard/keymaster/KMEcdsa256NoDigestSignature.java similarity index 98% rename from Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMEcdsa256NoDigestSignature.java rename to Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMEcdsa256NoDigestSignature.java index e33ebf69..242499ed 100644 --- a/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMEcdsa256NoDigestSignature.java +++ b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMEcdsa256NoDigestSignature.java @@ -146,7 +146,6 @@ public short sign(byte[] bytes, short i, short i1, byte[] bytes1, short i2) thro 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; @@ -165,7 +164,6 @@ public boolean verify(byte[] bytes, short i, short i1, byte[] bytes1, short i2, 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; diff --git a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimApplet.java b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimApplet.java new file mode 100644 index 00000000..e1ec74f8 --- /dev/null +++ b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimApplet.java @@ -0,0 +1,19 @@ +package com.android.javacard.keymaster; + +public class KMJCardSimApplet extends KMKeymasterApplet { + + KMJCardSimApplet(){ + super(new KMJCardSimulator()); + } + /** + * Installs this applet. + * + * @param bArray the array containing installation parameters + * @param bOffset the starting offset in bArray + * @param bLength the length in bytes of the parameter data in bArray + */ + public static void install(byte[] bArray, short bOffset, byte bLength) { + new KMJCardSimApplet().register(); + } + +} diff --git a/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMJcardSimulator.java b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimulator.java similarity index 93% rename from Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMJcardSimulator.java rename to Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimulator.java index 0ed1430a..70311e51 100644 --- a/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMJcardSimulator.java +++ b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimulator.java @@ -26,7 +26,7 @@ import java.security.spec.MGF1ParameterSpec; import java.security.spec.RSAPrivateKeySpec; import java.security.spec.RSAPublicKeySpec; -import javacard.framework.AID; + import javacard.framework.ISO7816; import javacard.framework.ISOException; import javacard.framework.JCSystem; @@ -46,6 +46,7 @@ import javacard.security.Signature; import javacardx.crypto.AEADCipher; import javacardx.crypto.Cipher; + import javax.crypto.AEADBadTagException; import javax.crypto.BadPaddingException; import javax.crypto.IllegalBlockSizeException; @@ -57,19 +58,20 @@ import javax.crypto.spec.PSource; import javax.crypto.spec.SecretKeySpec; +import org.globalplatform.upgrade.Element; + /** * Simulator only supports 512 bit RSA key pair, 128 AES Key, 128 bit 3Des key, less then 256 bit EC * Key, and upto 512 bit HMAC key. Also simulator does not support TRNG, so this implementation just * creates its own RNG using PRNG. */ -public class KMJcardSimulator implements KMSEProvider { +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; public static final short ENTROPY_POOL_SIZE = 16; // simulator does not support 256 bit aes keys public static final byte[] aesICV = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - private static final int AES_GCM_KEY_SIZE = 16; - private static final byte[] aidArr = new byte[]{ (byte)0xA0, 0x00, 0x00, 0x00, 0x63}; + private static final short CERT_CHAIN_MAX_SIZE = 2050;//First 2 bytes for length. public static boolean jcardSim = false; @@ -81,9 +83,16 @@ public class KMJcardSimulator implements KMSEProvider { private static Cipher aesRngCipher; private static byte[] entropyPool; private static byte[] rndNum; + private byte[] certificateChain; + + private static KMJCardSimulator jCardSimulator = null; + + public static KMJCardSimulator getInstance() { + return jCardSimulator; + } // Implements Oracle Simulator based restricted crypto provider - public KMJcardSimulator() { + public KMJCardSimulator() { // Various Keys kdf = Signature.getInstance(Signature.ALG_AES_CMAC_128, false); hmacSignature = Signature.getInstance(Signature.ALG_HMAC_SHA_256, false); @@ -99,7 +108,9 @@ public KMJcardSimulator() { } aesRngKey = (AESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_AES, KeyBuilder.LENGTH_AES_128, false); // various ciphers - + //Allocate buffer for certificate chain. + certificateChain = new byte[CERT_CHAIN_MAX_SIZE]; + jCardSimulator = this; } @@ -160,7 +171,6 @@ public AESKey createAESKey(byte[] buf, short startOff, short length) { public DESKey createTDESKey() { - // TODO check whether 168 bit or 192 bit byte[] rndNum = new byte[24]; newRandomNumber(rndNum, (short) 0, (short)rndNum.length); return createTDESKey(rndNum, (short)0, (short)rndNum.length); @@ -566,17 +576,6 @@ public short rsaDecipherOAEP256(byte[] secret, short secretStart, short secretLe inputDataBuf, inputDataStart, inputDataLength, outputDataBuf, outputDataStart); } - @Override - 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 KMOperation initSymmetricOperation(byte purpose, byte alg, byte digest, byte padding, byte blockMode, byte[] keyBuf, short keyStart, short keyLength, @@ -692,13 +691,9 @@ public void getAESGCMOutputSize(short opHandle, short dataSize, short macLength) public KMCipher createRsaDecipher(short padding, short digest, byte[] secret, short secretStart, short secretLength, byte[] modBuffer, short modOff, short modLength) { byte cipherAlg = mapCipherAlg(KMType.RSA, (byte)padding, (byte)0); - // TODO implement OAEP algorithm using SunJCE. 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); @@ -879,7 +874,6 @@ public KMCipher createSymmetricCipher(short alg, short purpose, short blockMode, 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, mapPurpose(purpose), ivBuffer, ivStart, (short)8); break; @@ -1099,12 +1093,10 @@ private void initEntropyPool(byte[] pool) { trng.nextBytes(pool, (short) 0, (short) pool.length); } catch (CryptoException exp) { if (exp.getReason() == CryptoException.NO_SUCH_ALGORITHM) { - // TODO change this when possible // simulator does not support TRNG algorithm. So, PRNG algorithm (deprecated) is used. trng = RandomData.getInstance(RandomData.ALG_PSEUDO_RANDOM); trng.nextBytes(pool, (short) 0, (short) pool.length); } else { - // TODO change this to proper error code ISOException.throwIt(ISO7816.SW_UNKNOWN); } } @@ -1187,9 +1179,6 @@ public KMCipher createRsaCipher(short padding, short digest, byte[] modBuffer, s 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}; @@ -1203,14 +1192,9 @@ public KMCipher createRsaCipher(short padding, short digest, byte[] modBuffer, s return inst; } - 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}; @@ -1220,7 +1204,6 @@ public Signature createRsaVerifier(short digest, short padding, byte[] modBuffer return rsaVerifier; } - public Signature createEcVerifier(short digest, byte[] pubKey, short pubKeyStart, short pubKeyLength) { short alg = mapSignature256Alg(KMType.EC, (byte)0); Signature ecVerifier; @@ -1236,52 +1219,103 @@ public Signature createEcVerifier(short digest, byte[] pubKey, short pubKeyStart return ecVerifier; } + @Override + public KMAttestationCert getAttestationCert(boolean rsaCert) { + //certBuilder.reset(); + return KMAttestationCertImpl.instance(rsaCert); + } + + public short readCertificateChain(byte[] buf, short offset) { + short len = Util.getShort(certificateChain, (short)0); + Util.arrayCopyNonAtomic(certificateChain, (short)2, buf, offset, len); + return len; + } @Override - public boolean isBackupRestoreSupported() { - return true; + public short getCertificateChainLength() { + return Util.getShort(certificateChain, (short)0); } @Override - public KMAttestationCert getAttestationCert(boolean rsaCert) { - //certBuilder.reset(); - return KMAttestationCertImpl.instance(rsaCert); + public short ecSign256(byte[] secret, short secretStart, short secretLength, + byte[] inputDataBuf, short inputDataStart, short inputDataLength, + byte[] outputDataBuf, short outputDataStart) { + + ECPrivateKey key = (ECPrivateKey) KeyBuilder.buildKey( + KeyBuilder.TYPE_EC_FP_PRIVATE, KeyBuilder.LENGTH_EC_FP_256, false); + key.setS(secret, secretStart, secretLength); + + Signature signer = Signature + .getInstance(Signature.ALG_ECDSA_SHA_256, false); + signer.init(key, Signature.MODE_SIGN); + return signer.sign(inputDataBuf, inputDataStart, inputDataLength, + outputDataBuf, outputDataStart); } @Override - public void backup(byte[] buf, short start, short len) { - byte[] data = new byte[len]; - AID aid = JCSystem.lookupAID(aidArr,(short)0,(byte)aidArr.length); - KMBackupRestoreAgent backupStore = (KMBackupRestoreAgent) JCSystem.getAppletShareableInterfaceObject(aid,(byte)0); - Util.arrayCopyNonAtomic(buf,start,data,(short)0,len); - backupStore.backup(data,(short)0,len); + public void persistPartialCertificateChain(byte[] buf, short offset, + short len, short totalLen) { + // _____________________________________________________ + // | 2 Bytes | 1 Byte | 3 Bytes | Cert1 | 3 Bytes | Cert2|... + // |_________|________|_________|_______|_________|______| + // First two bytes holds the length of the total buffer. + // CBOR format: + // Next single byte holds the array header. + // Next 3 bytes holds the Byte array header with the cert1 length. + // Next 3 bytes holds the Byte array header with the cert2 length. + short persistedLen = Util.getShort(certificateChain, (short) 0); + if (persistedLen > totalLen) { + KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + } + JCSystem.beginTransaction(); + Util.setShort(certificateChain, (short) 0, (short) (len + persistedLen)); + Util.arrayCopyNonAtomic(buf, offset, certificateChain, + (short) (persistedLen+2), len); + JCSystem.commitTransaction(); } @Override - public short restore(byte[] buf, short start) { - byte[] data = new byte[2200]; - AID aid = JCSystem.lookupAID(aidArr,(short)0,(byte)aidArr.length); - KMBackupRestoreAgent backupStore = (KMBackupRestoreAgent) JCSystem.getAppletShareableInterfaceObject(aid,(byte)0); - short len = backupStore.restore(data,(short)0); - Util.arrayCopyNonAtomic(data,(short)0,buf,start,len); - return len; + public boolean isBootSignalEventSupported() { + return false; } - /* - 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); + @Override + public boolean isDeviceRebooted() { + return false; + } + + @Override + public void clearDeviceBooted(boolean resetBootFlag) { + // To be filled + } + + @Override + public void onSave(Element ele) { + // TODO Auto-generated method stub + + } + + @Override + public void onRestore(Element ele) { + // TODO Auto-generated method stub + + } + + @Override + public short getBackupPrimitiveByteCount() { + // TODO Auto-generated method stub + return 0; + } + + @Override + public short getBackupObjectCount() { + // TODO Auto-generated method stub + return 0; + } + + @Override + public boolean isUpgrading() { + // TODO Auto-generated method stub + return false; } - 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/JCardSimProvider/com/android/javacard/keymaster/KMOperationImpl.java b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMOperationImpl.java similarity index 99% rename from Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMOperationImpl.java rename to Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMOperationImpl.java index 755b7134..dec071e6 100644 --- a/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMOperationImpl.java +++ b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMOperationImpl.java @@ -56,5 +56,4 @@ public void updateAAD(byte[] dataBuf, short dataStart, short dataLength) { 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/JCardSimProvider/src/com/android/javacard/keymaster/KMRsa2048NoDigestSignature.java similarity index 96% rename from Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMRsa2048NoDigestSignature.java rename to Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMRsa2048NoDigestSignature.java index 82793bfb..38e9a3bc 100644 --- a/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMRsa2048NoDigestSignature.java +++ b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMRsa2048NoDigestSignature.java @@ -1,120 +1,120 @@ -package com.android.javacard.keymaster; - -import javacard.framework.Util; -import javacard.security.CryptoException; -import javacard.security.Key; -import javacard.security.Signature; -import javacardx.crypto.Cipher; - -public class KMRsa2048NoDigestSignature extends Signature { - private Cipher inst; // ALG_RSA_NOPAD. - private byte padding; - private byte[] rsaModulus; // to compare with the data value - - public KMRsa2048NoDigestSignature(Cipher ciph, byte padding, byte[]mod, short start, short len){ - inst = ciph; - this.padding = padding; - if(len != 256) CryptoException.throwIt(CryptoException.INVALID_INIT); - rsaModulus = new byte[256]; - Util.arrayCopyNonAtomic(mod,start,rsaModulus,(short)0,len); - } - - @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[] bytes, short i, short i1) throws CryptoException { - } - - @Override - public short sign(byte[] bytes, short i, short i1, byte[] bytes1, short i2) throws CryptoException { - byte[] inputData = padData(bytes,i,i1); - return inst.doFinal(inputData,(short)0,(short)256,bytes1,i2); - } - - @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. - return false; - } - - @Override - public boolean verifyPreComputedHash(byte[] bytes, short i, short i1, byte[] bytes1, short i2, short i3) throws CryptoException { - return false; - } - - private byte[] padData(byte[] buf, short start, short len){ - byte[] inputData = new byte[256]; - if(!isValidData(buf, start,len)){ - CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); - } - Util.arrayFillNonAtomic(inputData, (short) 0, (short) 256, (byte) 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); - inputData[(short)(256-len-1)] = 0x00; - }else{ - CryptoException.throwIt(CryptoException.ILLEGAL_USE); - } - Util.arrayCopyNonAtomic(buf,start,inputData,(short)(256 -len),len); - return inputData; - } - - private boolean isValidData(byte[] buf, short start, short len) { - if (padding == KMType.PADDING_NONE) { - if (len > 256) return false; - else if (len == 256) { - short v = Util.arrayCompare(buf, start, rsaModulus, (short) 0, len); - if (v > 0) return false; - } - } else {//pkcs1 no digest - if(len > 245){ - return false; - } - } - return true; - } -} +package com.android.javacard.keymaster; + +import javacard.framework.Util; +import javacard.security.CryptoException; +import javacard.security.Key; +import javacard.security.Signature; +import javacardx.crypto.Cipher; + +public class KMRsa2048NoDigestSignature extends Signature { + private Cipher inst; // ALG_RSA_NOPAD. + private byte padding; + private byte[] rsaModulus; // to compare with the data value + + public KMRsa2048NoDigestSignature(Cipher ciph, byte padding, byte[]mod, short start, short len){ + inst = ciph; + this.padding = padding; + if(len != 256) CryptoException.throwIt(CryptoException.INVALID_INIT); + rsaModulus = new byte[256]; + Util.arrayCopyNonAtomic(mod,start,rsaModulus,(short)0,len); + } + + @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[] bytes, short i, short i1) throws CryptoException { + } + + @Override + public short sign(byte[] bytes, short i, short i1, byte[] bytes1, short i2) throws CryptoException { + byte[] inputData = padData(bytes,i,i1); + return inst.doFinal(inputData,(short)0,(short)256,bytes1,i2); + } + + @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. + return false; + } + + @Override + public boolean verifyPreComputedHash(byte[] bytes, short i, short i1, byte[] bytes1, short i2, short i3) throws CryptoException { + return false; + } + + private byte[] padData(byte[] buf, short start, short len){ + byte[] inputData = new byte[256]; + if(!isValidData(buf, start,len)){ + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + } + Util.arrayFillNonAtomic(inputData, (short) 0, (short) 256, (byte) 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); + inputData[(short)(256-len-1)] = 0x00; + }else{ + CryptoException.throwIt(CryptoException.ILLEGAL_USE); + } + Util.arrayCopyNonAtomic(buf,start,inputData,(short)(256 -len),len); + return inputData; + } + + private boolean isValidData(byte[] buf, short start, short len) { + if (padding == KMType.PADDING_NONE) { + if (len > 256) return false; + else if (len == 256) { + short v = Util.arrayCompare(buf, start, rsaModulus, (short) 0, len); + if (v > 0) return false; + } + } else {//pkcs1 no digest + if(len > 245){ + return false; + } + } + return true; + } +} diff --git a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMUtils.java b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMUtils.java new file mode 100644 index 00000000..d9dce03b --- /dev/null +++ b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMUtils.java @@ -0,0 +1,321 @@ +package com.android.javacard.keymaster; + +import javacard.framework.Util; + +public class KMUtils { + // 64 bit unsigned calculations for time + public static final byte[] oneSecMsec = { + 0, 0, 0, 0, 0, 0, 0x03, (byte) 0xE8 }; // 1000 msec + public static final byte[] oneMinMsec = { + 0, 0, 0, 0, 0, 0, (byte) 0xEA, 0x60 }; // 60000 msec + public static final byte[] oneHourMsec = { + 0, 0, 0, 0, 0, 0x36, (byte) 0xEE, (byte) 0x80 }; // 3600000 msec + public static final byte[] oneDayMsec = { + 0, 0, 0, 0, 0x05, 0x26, 0x5C, 0x00 }; // 86400000 msec + public static final byte[] oneMonthMsec = { + 0, 0, 0, 0, (byte) 0x9A, 0x7E, (byte) 0xC8, 0x00 }; // 2592000000 msec + public static final byte[] oneYearMsec = { + 0, 0, 0, 0x07, 0x57, (byte) 0xB1, 0x2C, 0x00 }; // 31536000000 msec + // Leap year + 3 yrs + public static final byte[] fourYrsMsec = { + 0, 0, 0, 0x1D, 0x63, (byte) 0xEB, 0x0C, 0x00 }; // 126230400000 msec + public static final byte[] firstJan2020 = { + 0, 0, 0x01, 0x6F, 0x60, 0x1E, 0x5C, 0x00 }; // 1577865600000 msec + public static final byte[] firstJan2051 = { + 0, 0, 0x02, 0x53, 0x27, (byte) 0xC5, (byte) 0x90, 0x00 }; // 2556172800000 + // msec + + // -------------------------------------- + public static short convertToDate(short time, byte[] scratchPad, + boolean utcFlag) { + short yrsCount = 0; + short monthCount = 0; + short dayCount = 0; + short hhCount = 0; + short mmCount = 0; + short ssCount = 0; + byte Z = 0x5A; + boolean from2020 = true; + Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 256, (byte) 0); + Util.arrayCopyNonAtomic(KMInteger.cast(time).getBuffer(), + KMInteger.cast(time).getStartOff(), scratchPad, + (short) (8 - KMInteger.cast(time).length()), KMInteger.cast(time) + .length()); + // If the time is less then 1 Jan 2020 then it is an error + if (Util.arrayCompare(scratchPad, (short) 0, firstJan2020, (short) 0, + (short) 8) < 0) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + if (utcFlag + && Util.arrayCompare(scratchPad, (short) 0, firstJan2051, + (short) 0, (short) 8) >= 0) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + + if (Util.arrayCompare(scratchPad, (short) 0, firstJan2051, (short) 0, + (short) 8) < 0) { + Util.arrayCopyNonAtomic(firstJan2020, (short) 0, scratchPad, (short) 8, + (short) 8); + subtract(scratchPad, (short) 0, (short) 8, (short) 16); + Util.arrayCopyNonAtomic(scratchPad, (short) 16, scratchPad, (short) 0, + (short) 8); + } else { + from2020 = false; + Util.arrayCopyNonAtomic(firstJan2051, (short) 0, scratchPad, (short) 8, + (short) 8); + subtract(scratchPad, (short) 0, (short) 8, (short) 16); + Util.arrayCopyNonAtomic(scratchPad, (short) 16, scratchPad, (short) 0, + (short) 8); + } + // divide the given time with four yrs msec count + if (Util.arrayCompare(scratchPad, (short) 0, fourYrsMsec, (short) 0, + (short) 8) >= 0) { + Util.arrayCopyNonAtomic(fourYrsMsec, (short) 0, scratchPad, (short) 8, + (short) 8); + yrsCount = divide(scratchPad, (short) 0, (short) 8, (short) 16); // quotient + // is + // multiple + // of 4 + yrsCount = (short) (yrsCount * 4); // number of yrs. + // copy reminder as new dividend + Util.arrayCopyNonAtomic(scratchPad, (short) 16, scratchPad, (short) 0, + (short) 8); + } + // divide the given time with one yr msec count + if (Util.arrayCompare(scratchPad, (short) 0, oneYearMsec, (short) 0, + (short) 8) >= 0) { + Util.arrayCopyNonAtomic(oneYearMsec, (short) 0, scratchPad, (short) 8, + (short) 8); + yrsCount += divide(scratchPad, (short) 0, (short) 8, (short) 16); + Util.arrayCopyNonAtomic(scratchPad, (short) 16, scratchPad, (short) 0, + (short) 8); + } + // total yrs from 1970 + if (from2020) + yrsCount = (short) (2020 + yrsCount); + else + yrsCount = (short) (2051 + yrsCount); + + // divide the given time with one month msec count + if (Util.arrayCompare(scratchPad, (short) 0, oneMonthMsec, (short) 0, + (short) 8) >= 0) { + Util.arrayCopyNonAtomic(oneMonthMsec, (short) 0, scratchPad, (short) 8, + (short) 8); + monthCount = divide(scratchPad, (short) 0, (short) 8, (short) 16); + Util.arrayCopyNonAtomic(scratchPad, (short) 16, scratchPad, (short) 0, + (short) 8); + } + + // divide the given time with one day msec count + if (Util.arrayCompare(scratchPad, (short) 0, oneDayMsec, (short) 0, + (short) 8) >= 0) { + Util.arrayCopyNonAtomic(oneDayMsec, (short) 0, scratchPad, (short) 8, + (short) 8); + dayCount = divide(scratchPad, (short) 0, (short) 8, (short) 16); + Util.arrayCopyNonAtomic(scratchPad, (short) 16, scratchPad, (short) 0, + (short) 8); + } + + // divide the given time with one hour msec count + if (Util.arrayCompare(scratchPad, (short) 0, oneHourMsec, (short) 0, + (short) 8) >= 0) { + Util.arrayCopyNonAtomic(oneHourMsec, (short) 0, scratchPad, (short) 8, + (short) 8); + hhCount = divide(scratchPad, (short) 0, (short) 8, (short) 16); + Util.arrayCopyNonAtomic(scratchPad, (short) 16, scratchPad, (short) 0, + (short) 8); + } + + // divide the given time with one minute msec count + if (Util.arrayCompare(scratchPad, (short) 0, oneMinMsec, (short) 0, + (short) 8) >= 0) { + Util.arrayCopyNonAtomic(oneMinMsec, (short) 0, scratchPad, (short) 8, + (short) 8); + mmCount = divide(scratchPad, (short) 0, (short) 8, (short) 16); + Util.arrayCopyNonAtomic(scratchPad, (short) 16, scratchPad, (short) 0, + (short) 8); + } + + // divide the given time with one second msec count + if (Util.arrayCompare(scratchPad, (short) 0, oneSecMsec, (short) 0, + (short) 8) >= 0) { + Util.arrayCopyNonAtomic(oneSecMsec, (short) 0, scratchPad, (short) 8, + (short) 8); + ssCount = divide(scratchPad, (short) 0, (short) 8, (short) 16); + Util.arrayCopyNonAtomic(scratchPad, (short) 16, scratchPad, (short) 0, + (short) 8); + } + + // Now convert to ascii string YYMMDDhhmmssZ or YYYYMMDDhhmmssZ + Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 256, (byte) 0); + short len = numberToString(yrsCount, scratchPad, (short) 0); // returns YYYY + len += numberToString(monthCount, scratchPad, len); + len += numberToString(dayCount, scratchPad, len); + len += numberToString(hhCount, scratchPad, len); + len += numberToString(mmCount, scratchPad, len); + len += numberToString(ssCount, scratchPad, len); + scratchPad[len] = Z; + len++; + if (utcFlag) + return KMByteBlob.instance(scratchPad, (short) 2, (short) (len - 2)); // YY + else + return KMByteBlob.instance(scratchPad, (short) 0, len); // YYYY + } + + public static short numberToString(short number, byte[] scratchPad, + short offset) { + byte zero = 0x30; + byte len = 2; + byte digit; + if (number > 999) + len = 4; + byte index = len; + while (index > 0) { + digit = (byte) (number % 10); + number = (short) (number / 10); + scratchPad[(short) (offset + index - 1)] = (byte) (digit + zero); + index--; + } + return len; + } + + // 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. + public static short divide(byte[] buf, short dividend, short divisor, + short remainder) { + short expCnt = 1; + short q = 0; + // first increase divisor so that it becomes greater then dividend. + while (compare(buf, divisor, dividend) < 0) { + shiftLeft(buf, divisor); + expCnt = (short) (expCnt << 1); + } + // Now subtract divisor from dividend if dividend is greater then divisor. + // Copy remainder in the dividend and repeat. + while (expCnt != 0) { + if (compare(buf, dividend, divisor) >= 0) { + subtract(buf, dividend, divisor, remainder); + copy(buf, remainder, dividend); + q = (short) (q + expCnt); + } + expCnt = (short) (expCnt >> 1); + shiftRight(buf, divisor); + } + return q; + } + + public static void copy(byte[] buf, short from, short to) { + Util.arrayCopyNonAtomic(buf, from, buf, to, (short) 8); + } + + public static byte compare(byte[] buf, short lhs, short rhs) { + return Util.arrayCompare(buf, lhs, buf, rhs, (short) 8); + } + + public static void shiftLeft(byte[] buf, short start) { + byte index = 7; + byte carry = 0; + byte tmp; + while (index >= 0) { + tmp = buf[(short) (start + index)]; + buf[(short) (start + index)] = (byte) (buf[(short) (start + index)] << 1); + buf[(short) (start + index)] = (byte) (buf[(short) (start + index)] + carry); + if (tmp < 0) + carry = 1; + else + carry = 0; + index--; + } + } + + public static void shiftRight(byte[] buf, short start) { + byte index = 0; + byte carry = 0; + byte tmp; + while (index < 8) { + tmp = (byte) (buf[(short) (start + index)] & 0x01); + buf[(short) (start + index)] = (byte) (buf[(short) (start + index)] >> 1); + buf[(short) (start + index)] = (byte) (buf[(short) (start + index)] & 0x7F); + buf[(short) (start + index)] = (byte) (buf[(short) (start + index)] | carry); + if (tmp == 1) + carry = (byte) 0x80; + else + carry = 0; + index++; + } + } + + + // 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); byte[] scratchPad = repository.getHeap(); + Util.arrayFillNonAtomic(scratchPad, buf, (short) 24, (byte) 0); + Util.arrayCopyNonAtomic(KMInteger.cast(num1).getBuffer(), + KMInteger.cast(num1).getStartOff(), scratchPad, + (short) (buf + 8 - KMInteger.cast(num1).length()), + KMInteger.cast(num1).length()); + Util.arrayCopyNonAtomic(KMInteger.cast(num2).getBuffer(), + KMInteger.cast(num2).getStartOff(), scratchPad, + (short) (buf + 16 - KMInteger.cast(num2).length()), + KMInteger.cast(num2).length()); + if (scratchPad[buf] < 0 || scratchPad[(short) (buf + 8)] < 0) + return KMType.INVALID_VALUE; + if (Util.arrayCompare(scratchPad, buf, scratchPad, (short) (buf + 8), + (short) 8) < 1) + return KMType.INVALID_VALUE; + subtract(scratchPad, buf, (short) (buf + 8), (short) (buf + 16)); + return KMInteger.uint_64(scratchPad, (short) (buf + 16)); + }*/ + + public static void add(byte[] buf, short op1, short op2, short result) { + byte index = 7; + byte carry = 0; + short tmp; + while (index >= 0) { + tmp = (short) (buf[(short) (op1 + index)] + buf[(short) (op2 + index)] + carry); + carry = 0; + if (tmp > 255) + carry = 1; // max unsigned byte value is 255 + buf[(short) (result + index)] = (byte) (tmp & (byte) 0xFF); + index--; + } + } + + // subtraction by borrowing. + public static void subtract(byte[] buf, short op1, short op2, short result) { + byte borrow = 0; + byte index = 7; + short r; + short x; + short y; + while (index >= 0) { + x = (short) (buf[(short) (op1 + index)] & 0xFF); + y = (short) (buf[(short) (op2 + index)] & 0xFF); + r = (short) (x - y - borrow); + borrow = 0; + if (r < 0) { + borrow = 1; + r = (short) (r + 256); // max unsigned byte value is 255 + } + buf[(short) (result + index)] = (byte) (r & 0xFF); + index--; + } + } + + public static short countTemporalCount(byte[] bufTime, short timeOff, + short timeLen, byte[] scratchPad, short offset) { + Util.arrayFillNonAtomic(scratchPad, (short) offset, (short) 24, (byte) 0); + Util.arrayCopyNonAtomic( + bufTime, + timeOff, + scratchPad, + (short) (offset + 8 - timeLen), + timeLen); + Util.arrayCopyNonAtomic(oneMonthMsec, (short) 0, scratchPad, (short) (offset + 8), + (short) 8); + return divide(scratchPad, (short) 0, (short) 8, (short) 16); + } + +} diff --git a/Applet/Applet/test/com/android/javacard/test/KMFunctionalTest.java b/Applet/JCardSimProvider/test/com/android/javacard/test/KMFunctionalTest.java similarity index 72% rename from Applet/Applet/test/com/android/javacard/test/KMFunctionalTest.java rename to Applet/JCardSimProvider/test/com/android/javacard/test/KMFunctionalTest.java index c9eb7c1f..fb248f2d 100644 --- a/Applet/Applet/test/com/android/javacard/test/KMFunctionalTest.java +++ b/Applet/JCardSimProvider/test/com/android/javacard/test/KMFunctionalTest.java @@ -1,2365 +1,2735 @@ -/* - * 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.KMBackupStoreApplet; -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.KMError; -import com.android.javacard.keymaster.KMHardwareAuthToken; -import com.android.javacard.keymaster.KMHmacSharingParameters; -import com.android.javacard.keymaster.KMInteger; -import com.android.javacard.keymaster.KMIntegerTag; -import com.android.javacard.keymaster.KMKeyCharacteristics; -import com.android.javacard.keymaster.KMKeyParameters; -import com.android.javacard.keymaster.KMKeymasterApplet; -import com.android.javacard.keymaster.KMRepository; -import com.android.javacard.keymaster.KMType; -import com.android.javacard.keymaster.KMVerificationToken; -import com.licel.jcardsim.smartcardio.CardSimulator; -import com.licel.jcardsim.utils.AIDUtil; -import javacard.framework.AID; -import javacard.framework.Util; -import javax.smartcardio.CommandAPDU; -import javax.smartcardio.ResponseAPDU; -import org.junit.Assert; -import org.junit.Test; - -public class KMFunctionalTest { - 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 KMSEProvider sim; - private CardSimulator simulator; - private KMEncoder encoder; - private KMDecoder decoder; - private KMSEProvider cryptoProvider; - - public KMFunctionalTest(){ - cryptoProvider = KMSEProviderImpl.instance(); - sim = KMSEProviderImpl.instance(); - simulator = new CardSimulator(); - encoder = new KMEncoder(); - decoder = new KMDecoder(); - } - - private void init(){ - // Create simulator - AID appletAID = AIDUtil.create("A000000062"); - simulator.installApplet(appletAID, KMKeymasterApplet.class); - // Select applet - simulator.selectApplet(appletAID); - // provision attest key - provisionCmd(simulator); - // set bootup parameters - setBootParams(simulator,(short)1,(short)1); - } - - private void initBackUpStore(){ - // Create simulator - AID appletAID2 = AIDUtil.create("A000000063"); - simulator.installApplet(appletAID2, KMBackupStoreApplet.class); - //simulator.selectApplet(appletAID2); - } - - private void setBootParams(CardSimulator simulator, short osVersion, short osPatchLevel){ - // Argument 1 OS Version - short versionPtr = KMInteger.uint_16(osVersion); -// short versionTagPtr = KMIntegerTag.instance(KMType.UINT_TAG, KMType.OS_VERSION,versionPatchPtr); - // Argument 2 OS Patch level - short patchPtr = KMInteger.uint_16(osPatchLevel); - // 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, versionPtr); - vals.add((short) 1, patchPtr); - vals.add((short) 2, bootKeyPtr); - vals.add((short) 3, bootHashPtr); - vals.add((short) 4, bootStatePtr); - vals.add((short) 5, deviceLockedPtr); - CommandAPDU apdu = encodeApdu((byte)0x24, arrPtr); - // print(commandAPDU.getBytes()); - ResponseAPDU response = simulator.transmitCommand(apdu); - Assert.assertEquals(0x9000, response.getSW()); - - } - - //TODO change this - private void provisionCmd(CardSimulator simulator) { -/* // Argument 1 - short arrPtr = KMArray.instance((short) 1); - KMArray vals = KMArray.cast(arrPtr); - vals.add((short) 0, KMEnumTag.instance(KMType.ALGORITHM, KMType.RSA)); - short keyparamsPtr = KMKeyParameters.instance(arrPtr); - // Argument 2 - short keyFormatPtr = KMEnum.instance(KMType.KEY_FORMAT, KMType.X509); - // Argument 3 - byte[] byteBlob = new byte[48]; - for (short i = 0; i < 48; i++) { - byteBlob[i] = (byte) i; - } - short keyBlobPtr = KMByteBlob.instance(byteBlob, (short) 0, (short)byteBlob.length); - // Array of expected arguments - short argPtr = KMArray.instance((short) 3); - KMArray arg = KMArray.cast(argPtr); - arg.add((short) 0, keyparamsPtr); - arg.add((short) 1, keyFormatPtr); - arg.add((short) 2, keyBlobPtr); - CommandAPDU apdu = encodeApdu((byte)0x23, argPtr); - // print(commandAPDU.getBytes()); - ResponseAPDU response = simulator.transmitCommand(apdu); - Assert.assertEquals(0x9000, response.getSW()); -*/ -/* 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)); - 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]; - short len = encoder.encode(keyBlob,blob,(short)0); - keyBlob = KMByteBlob.instance(blob, (short)0, len); - arrPtr = KMArray.instance((short)7); - 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); - 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); - Assert.assertEquals(0x9000, response.getSW()); - } - - private void cleanUp(){ - AID appletAID = AIDUtil.create("A000000062"); - // Delete i.e. uninstall applet - simulator.deleteApplet(appletAID); - } - - private void cleanUpBkUpStore(){ - AID appletAID = AIDUtil.create("A000000063"); - // Delete i.e. uninstall applet - simulator.deleteApplet(appletAID); - } - private CommandAPDU encodeApdu(byte ins, short cmd){ - byte[] buf = new byte[2048]; - buf[0] = (byte)0x80; - buf[1] = ins; - buf[2] = (byte)0x40; - buf[3] = (byte)0x00; - buf[4] = 0; - short len = encoder.encode(cmd, buf, (short) 7); - Util.setShort(buf, (short)5, len); - byte[] apdu = new byte[7+len]; - Util.arrayCopyNonAtomic(buf,(short)0,apdu,(short)0,(short)(7+len)); - //CommandAPDU commandAPDU = new CommandAPDU(0x80, 0x10, 0x40, 0x00, buf, 0, actualLen); - return new CommandAPDU(apdu); - } - - @Test - public void testBackupRestore(){ - init(); - initBackUpStore(); - CommandAPDU commandAPDU = new CommandAPDU(0x80, 0x27, 0x40, 0x00); - ResponseAPDU response = simulator.transmitCommand(commandAPDU); - byte[] data = response.getBytes(); - Assert.assertEquals(data[0], KMError.OK); - commandAPDU = new CommandAPDU(0x80, 0x28, 0x40, 0x00); - response = simulator.transmitCommand(commandAPDU); - data = response.getBytes(); - Assert.assertEquals(data[0], KMError.OK); - } - @Test - public void testAesImportKeySuccess() { - init(); - byte[] aesKeySecret = new byte[] {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; - short arrPtr = KMArray.instance((short)5); - short boolTag = KMBoolTag.instance(KMType.NO_AUTH_REQUIRED); - short keySize = KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, KMInteger.uint_16((short)128)); - short byteBlob = KMByteBlob.instance((short)1); - KMByteBlob.cast(byteBlob).add((short)0, KMType.ECB); - short blockMode = KMEnumArrayTag.instance(KMType.BLOCK_MODE, byteBlob); - byteBlob = KMByteBlob.instance((short)1); - KMByteBlob.cast(byteBlob).add((short)0, KMType.PKCS7); - short paddingMode = KMEnumArrayTag.instance(KMType.PADDING, byteBlob); - KMArray.cast(arrPtr).add((short)0, boolTag); - KMArray.cast(arrPtr).add((short)1, keySize); - KMArray.cast(arrPtr).add((short)2, blockMode); - KMArray.cast(arrPtr).add((short)3, paddingMode); - KMArray.cast(arrPtr).add((short)4, KMEnumTag.instance(KMType.ALGORITHM, KMType.AES)); - short keyParams = KMKeyParameters.instance(arrPtr); - short keyFormatPtr = KMEnum.instance(KMType.KEY_FORMAT, KMType.RAW); - short keyBlob = KMArray.instance((short)1); - KMArray.cast(keyBlob).add((short)0, KMByteBlob.instance(aesKeySecret,(short)0,(short)16)); - byte[] blob = new byte[256]; - short len = encoder.encode(keyBlob,blob,(short)0); - keyBlob = KMByteBlob.instance(blob, (short)0, len); - arrPtr = KMArray.instance((short)3); - KMArray arg = KMArray.cast(arrPtr); - arg.add((short) 0, keyParams); - arg.add((short)1, keyFormatPtr); - arg.add((short)2, keyBlob); - CommandAPDU apdu = encodeApdu((byte)0x11, arrPtr); - // print(commandAPDU.getBytes()); - ResponseAPDU response = simulator.transmitCommand(apdu); - short ret = KMArray.instance((short) 3); - KMArray.cast(ret).add((short) 0, KMInteger.exp()); - KMArray.cast(ret).add((short)1, KMByteBlob.exp()); - short inst = KMKeyCharacteristics.exp(); - KMArray.cast(ret).add((short) 2, inst); - byte[] respBuf = response.getBytes(); - len = (short) respBuf.length; - ret = decoder.decode(ret, respBuf, (short) 0, len); - short error = KMInteger.cast(KMArray.cast(ret).get((short)0)).getShort(); - short keyBlobLength = KMByteBlob.cast(KMArray.cast(ret).get((short)1)).length(); - short keyCharacteristics = KMArray.cast(ret).get((short)2); - short hwParams = KMKeyCharacteristics.cast(keyCharacteristics).getHardwareEnforced(); - short swParams = KMKeyCharacteristics.cast(keyCharacteristics).getSoftwareEnforced(); - Assert.assertEquals(0x9000, response.getSW()); - Assert.assertEquals(error, KMError.OK); - short tag = KMKeyParameters.findTag(KMType.BOOL_TAG, KMType.NO_AUTH_REQUIRED, hwParams); - Assert.assertEquals(KMBoolTag.cast(tag).getVal(),0x01); - tag = KMKeyParameters.findTag(KMType.UINT_TAG, KMType.KEYSIZE, hwParams); - Assert.assertEquals(KMInteger.cast(KMIntegerTag.cast(tag).getValue()).getShort(), 128); - tag = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.PADDING, hwParams); - Assert.assertTrue(KMEnumArrayTag.cast(tag).contains(KMType.PKCS7)); - tag = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.BLOCK_MODE, hwParams); - Assert.assertTrue(KMEnumArrayTag.cast(tag).contains(KMType.ECB)); - tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ALGORITHM, hwParams); - Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.AES); - tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ORIGIN, hwParams); - Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.IMPORTED); - cleanUp(); - } - - @Test - public void testHmacImportKeySuccess() { - init(); - byte[] hmacKeySecret = new byte[] {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; - short arrPtr = KMArray.instance((short)5); - short boolTag = KMBoolTag.instance(KMType.NO_AUTH_REQUIRED); - short keySize = KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, KMInteger.uint_16((short)128)); - short byteBlob = KMByteBlob.instance((short)1); - KMByteBlob.cast(byteBlob).add((short)0, KMType.SHA2_256); - short digest = KMEnumArrayTag.instance(KMType.DIGEST, byteBlob); - short minMacLength = KMIntegerTag.instance(KMType.UINT_TAG,KMType.MIN_MAC_LENGTH, KMInteger.uint_16((short)256)); - KMArray.cast(arrPtr).add((short)0, boolTag); - KMArray.cast(arrPtr).add((short)1, keySize); - KMArray.cast(arrPtr).add((short)2, digest); - KMArray.cast(arrPtr).add((short)3, minMacLength); - KMArray.cast(arrPtr).add((short)4, KMEnumTag.instance(KMType.ALGORITHM, KMType.HMAC)); - short keyParams = KMKeyParameters.instance(arrPtr); - short keyFormatPtr = KMEnum.instance(KMType.KEY_FORMAT, KMType.RAW); - short keyBlob = KMArray.instance((short)1); - KMArray.cast(keyBlob).add((short)0, KMByteBlob.instance(hmacKeySecret,(short)0,(short)16)); - byte[] blob = new byte[256]; - short len = encoder.encode(keyBlob,blob,(short)0); - keyBlob = KMByteBlob.instance(blob, (short)0, len); - arrPtr = KMArray.instance((short)3); - KMArray arg = KMArray.cast(arrPtr); - arg.add((short) 0, keyParams); - arg.add((short)1, keyFormatPtr); - arg.add((short)2, keyBlob); - CommandAPDU apdu = encodeApdu((byte)0x11, arrPtr); - // print(commandAPDU.getBytes()); - ResponseAPDU response = simulator.transmitCommand(apdu); - short ret = KMArray.instance((short) 3); - KMArray.cast(ret).add((short) 0, KMInteger.exp()); - KMArray.cast(ret).add((short)1, KMByteBlob.exp()); - short inst = KMKeyCharacteristics.exp(); - KMArray.cast(ret).add((short) 2, inst); - byte[] respBuf = response.getBytes(); - len = (short) respBuf.length; - ret = decoder.decode(ret, respBuf, (short) 0, len); - short error = KMInteger.cast(KMArray.cast(ret).get((short)0)).getShort(); - short keyBlobLength = KMByteBlob.cast(KMArray.cast(ret).get((short)1)).length(); - short keyCharacteristics = KMArray.cast(ret).get((short)2); - short hwParams = KMKeyCharacteristics.cast(keyCharacteristics).getHardwareEnforced(); - short swParams = KMKeyCharacteristics.cast(keyCharacteristics).getSoftwareEnforced(); - Assert.assertEquals(0x9000, response.getSW()); - Assert.assertEquals(error, KMError.OK); - short tag = KMKeyParameters.findTag(KMType.BOOL_TAG, KMType.NO_AUTH_REQUIRED, hwParams); - Assert.assertEquals(KMBoolTag.cast(tag).getVal(),0x01); - tag = KMKeyParameters.findTag(KMType.UINT_TAG, KMType.KEYSIZE, hwParams); - Assert.assertEquals(KMInteger.cast(KMIntegerTag.cast(tag).getValue()).getShort(), 128); - tag = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.DIGEST, hwParams); - Assert.assertTrue(KMEnumArrayTag.cast(tag).contains(KMType.SHA2_256)); - tag = KMKeyParameters.findTag(KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, hwParams); - Assert.assertEquals(KMInteger.cast(KMIntegerTag.cast(tag).getValue()).getShort(), 256); - tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ALGORITHM, hwParams); - Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.HMAC); - tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ORIGIN, hwParams); - Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.IMPORTED); - cleanUp(); - } - - @Test - public void testRsaImportKeySuccess() { - init(); - /* - KeyPair rsaKeyPair = cryptoProvider.createRsaKeyPair(); - byte[] pub = new byte[4]; - short len = ((RSAPublicKey)rsaKeyPair.getPublic()).getExponent(pub,(short)1); - byte[] priv = new byte[256]; - byte[] mod = new byte[256]; - len = ((RSAPrivateKey)rsaKeyPair.getPrivate()).getModulus(mod,(short)0); - len = ((RSAPrivateKey)rsaKeyPair.getPrivate()).getExponent(priv,(short)0); - */ - - 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)); - byteBlob = KMByteBlob.instance((short)1); - KMByteBlob.cast(byteBlob).add((short)0, KMType.RSA_PSS); - short padding = KMEnumArrayTag.instance(KMType.PADDING, byteBlob); - KMArray.cast(arrPtr).add((short)0, boolTag); - KMArray.cast(arrPtr).add((short)1, keySize); - KMArray.cast(arrPtr).add((short)2, digest); - KMArray.cast(arrPtr).add((short)3, rsaPubExpTag); - KMArray.cast(arrPtr).add((short)4, KMEnumTag.instance(KMType.ALGORITHM, KMType.RSA)); - KMArray.cast(arrPtr).add((short)5, padding); - short keyParams = KMKeyParameters.instance(arrPtr); - short keyFormatPtr = KMEnum.instance(KMType.KEY_FORMAT, KMType.RAW);// Note: VTS uses PKCS8 - short keyBlob = KMArray.instance((short)2); - KMArray.cast(keyBlob).add((short)0, KMByteBlob.instance(priv,(short)0,(short)256)); - KMArray.cast(keyBlob).add((short)1, KMByteBlob.instance(mod,(short)0,(short)256)); - byte[] blob = new byte[620]; - short len = encoder.encode(keyBlob,blob,(short)0); - keyBlob = KMByteBlob.instance(blob, (short)0, len); - arrPtr = KMArray.instance((short)3); - KMArray arg = KMArray.cast(arrPtr); - arg.add((short) 0, keyParams); - arg.add((short)1, keyFormatPtr); - arg.add((short)2, keyBlob); - CommandAPDU apdu = encodeApdu((byte)0x11, arrPtr); - // print(commandAPDU.getBytes()); - ResponseAPDU response = simulator.transmitCommand(apdu); - short ret = KMArray.instance((short) 3); - KMArray.cast(ret).add((short) 0, KMInteger.exp()); - KMArray.cast(ret).add((short)1, KMByteBlob.exp()); - short inst = KMKeyCharacteristics.exp(); - KMArray.cast(ret).add((short) 2, inst); - byte[] respBuf = response.getBytes(); - len = (short) respBuf.length; - ret = decoder.decode(ret, respBuf, (short) 0, len); - short error = KMInteger.cast(KMArray.cast(ret).get((short)0)).getShort(); - short keyBlobLength = KMByteBlob.cast(KMArray.cast(ret).get((short)1)).length(); - short keyCharacteristics = KMArray.cast(ret).get((short)2); - short hwParams = KMKeyCharacteristics.cast(keyCharacteristics).getHardwareEnforced(); - short swParams = KMKeyCharacteristics.cast(keyCharacteristics).getSoftwareEnforced(); - Assert.assertEquals(0x9000, response.getSW()); - Assert.assertEquals(error, KMError.OK); - short tag = KMKeyParameters.findTag(KMType.BOOL_TAG, KMType.NO_AUTH_REQUIRED, hwParams); - Assert.assertEquals(KMBoolTag.cast(tag).getVal(),0x01); - tag = KMKeyParameters.findTag(KMType.UINT_TAG, KMType.KEYSIZE, hwParams); - Assert.assertEquals(KMInteger.cast(KMIntegerTag.cast(tag).getValue()).getShort(), 2048); - tag = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.DIGEST, hwParams); - Assert.assertTrue(KMEnumArrayTag.cast(tag).contains(KMType.SHA2_256)); - tag = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.PADDING, hwParams); - Assert.assertTrue(KMEnumArrayTag.cast(tag).contains(KMType.RSA_PSS)); - tag = KMKeyParameters.findTag(KMType.ULONG_TAG, KMType.RSA_PUBLIC_EXPONENT, hwParams); - Assert.assertEquals(KMInteger.cast(KMIntegerTag.cast(tag).getValue()).getSignificantShort(), 0x01); - Assert.assertEquals(KMInteger.cast(KMIntegerTag.cast(tag).getValue()).getShort(), 0x01); - tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ALGORITHM, hwParams); - Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.RSA); - tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ORIGIN, hwParams); - Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.IMPORTED); - cleanUp(); - } - - @Test - public void testDeviceLocked(){ - init(); - byte[] hmacKey = new byte[32]; - cryptoProvider.newRandomNumber(hmacKey,(short)0,(short)32); - KMRepository.instance().initComputedHmac(hmacKey,(short)0,(short)32); - // generate aes key with unlocked_device_required - short aesKey = generateAesDesKey(KMType.AES,(short)128,null,null, true); - short keyBlobPtr = KMArray.cast(aesKey).get((short)1); - byte[] keyBlob= new byte[KMByteBlob.cast(keyBlobPtr).length()]; - Util.arrayCopyNonAtomic(KMByteBlob.cast(keyBlobPtr).getBuffer(), - KMByteBlob.cast(keyBlobPtr).getStartOff(), - keyBlob,(short)0, (short)keyBlob.length); - // encrypt something - short inParams = getAesDesParams(KMType.AES, KMType.ECB, KMType.PKCS7, null); - byte[] plainData= "Hello World 123!".getBytes(); - short ret = processMessage(plainData, - KMByteBlob.instance(keyBlob,(short)0, (short)keyBlob.length), - KMType.ENCRYPT, - KMKeyParameters.instance(inParams), - (short)0,null,false, false - ); - keyBlobPtr = KMArray.cast(ret).get((short)2); - byte[] cipherData = new byte[KMByteBlob.cast(keyBlobPtr).length()]; - Util.arrayCopyNonAtomic(KMByteBlob.cast(keyBlobPtr).getBuffer(), KMByteBlob.cast(keyBlobPtr).getStartOff(), - cipherData,(short)0, (short)cipherData.length); - // create verification token - short verToken = KMVerificationToken.instance(); - KMVerificationToken.cast(verToken).setTimestamp(KMInteger.uint_16((short)1)); - verToken = signVerificationToken(verToken); - // device locked request - deviceLock(verToken); - // decrypt should fail - inParams = getAesDesParams(KMType.AES, KMType.ECB, KMType.PKCS7, null); - short beginResp = begin(KMType.DECRYPT, - KMByteBlob.instance(keyBlob,(short)0, (short)keyBlob.length), KMKeyParameters.instance(inParams), (short)0); - Assert.assertEquals(beginResp,KMError.DEVICE_LOCKED); - short hwToken = KMHardwareAuthToken.instance(); - KMHardwareAuthToken.cast(hwToken).setTimestamp(KMInteger.uint_16((byte)2)); - KMHardwareAuthToken.cast(hwToken).setHwAuthenticatorType(KMEnum.instance(KMType.USER_AUTH_TYPE, (byte)KMType.PASSWORD)); - inParams = getAesDesParams(KMType.AES, KMType.ECB, KMType.PKCS7, null); - hwToken = signHwToken(hwToken); - ret = processMessage(cipherData, - KMByteBlob.instance(keyBlob,(short)0, (short)keyBlob.length), - KMType.DECRYPT, - KMKeyParameters.instance(inParams),hwToken,null,false, false - ); - ret = KMArray.cast(ret).get((short)0); - Assert.assertEquals(KMInteger.cast(ret).getShort(), KMError.OK); - cleanUp(); - } - - private short signHwToken(short hwToken){ - short len = 0; - byte[] scratchPad = new byte[256]; - // add 0 - Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 256, (byte) 0); - len = 1; - // concatenate challenge - 8 bytes - short ptr = KMHardwareAuthToken.cast(hwToken).getChallenge(); - KMInteger.cast(ptr) - .value(scratchPad, (short) (len + (short) (8 - KMInteger.cast(ptr).length()))); - len += 8; - // concatenate user id - 8 bytes - ptr = KMHardwareAuthToken.cast(hwToken).getUserId(); - KMInteger.cast(ptr) - .value(scratchPad, (short) (len + (short) (8 - KMInteger.cast(ptr).length()))); - len += 8; - // concatenate authenticator id - 8 bytes - ptr = KMHardwareAuthToken.cast(hwToken).getAuthenticatorId(); - KMInteger.cast(ptr) - .value(scratchPad, (short) (len + (short) (8 - KMInteger.cast(ptr).length()))); - len += 8; - // concatenate authenticator type - 4 bytes - ptr = KMHardwareAuthToken.cast(hwToken).getHwAuthenticatorType(); - scratchPad[(short) (len + 3)] = KMEnum.cast(ptr).getVal(); - len += 4; - // concatenate timestamp -8 bytes - ptr = KMHardwareAuthToken.cast(hwToken).getTimestamp(); - KMInteger.cast(ptr) - .value(scratchPad, (short) (len + (short) (8 - KMInteger.cast(ptr).length()))); - len += 8; - // hmac the data -/* HMACKey key = - cryptoProvider.createHMACKey( - KMRepository.instance().getComputedHmacKey(), - (short) 0, - (short) KMRepository.instance().getComputedHmacKey().length); - - */ - byte[] mac = new byte[32]; - /* - len = - cryptoProvider.hmacSign(key, scratchPad, (short) 0, len, - mac, - (short)0); - */ - short key = KMRepository.instance().getComputedHmacKey(); - cryptoProvider.hmacSign( - KMByteBlob.cast(key).getBuffer(), - KMByteBlob.cast(key).getStartOff(), - KMByteBlob.cast(key).length(), - scratchPad, (short) 0, len, - mac, - (short)0); - KMHardwareAuthToken.cast(hwToken).setMac(KMByteBlob.instance(mac,(short)0,(short)mac.length)); - return hwToken; - } - private void deviceLock(short verToken) { - short req = KMArray.instance((short)2); - KMArray.cast(req).add((short)0, KMInteger.uint_8((byte)1)); - KMArray.cast(req).add((short)1, verToken); - CommandAPDU apdu = encodeApdu((byte)0x25,req); - ResponseAPDU response = simulator.transmitCommand(apdu); - short ret = KMArray.instance((short) 1); - KMArray.cast(ret).add((short) 0, KMInteger.exp()); - byte[] respBuf = response.getBytes(); - Assert.assertEquals(respBuf[0],KMError.OK); - } - - private short signVerificationToken(short verToken) { - byte[] scratchPad = new byte[256]; - byte[] authVer = "Auth Verification".getBytes(); - //print(authVer,(short)0,(short)authVer.length); - // 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(authVer,(short)0, scratchPad, (short)0, (short)authVer.length); - short len = (short)authVer.length; - // concatenate challenge - 8 bytes - short ptr = KMVerificationToken.cast(verToken).getChallenge(); - KMInteger.cast(ptr) - .value(scratchPad, (short) (len + (short) (8 - KMInteger.cast(ptr).length()))); - len += 8; - // concatenate timestamp -8 bytes - ptr = KMVerificationToken.cast(verToken).getTimestamp(); - KMInteger.cast(ptr) - .value(scratchPad, (short) (len + (short) (8 - KMInteger.cast(ptr).length()))); - len += 8; - // concatenate security level - 4 bytes - ptr = KMVerificationToken.cast(verToken).getSecurityLevel(); - scratchPad[(short) (len + 3)] = KMEnum.cast(ptr).getVal(); - len += 4; - // concatenate Parameters verified - blob of encoded data. - ptr = KMVerificationToken.cast(verToken).getParametersVerified(); - if (KMByteBlob.cast(ptr).length() != 0) { - len += KMByteBlob.cast(ptr).getValues(scratchPad, (short) 0); - } - // hmac the data - /* 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 = - cryptoProvider.hmacSign(key, scratchPad, (short) 0, len, - mac, - (short)0); - */ - short key = KMRepository.instance().getComputedHmacKey(); - cryptoProvider.hmacSign(KMByteBlob.cast(key).getBuffer(), - KMByteBlob.cast(key).getStartOff(), - KMByteBlob.cast(key).length(), - scratchPad, (short) 0, len, - mac, - (short)0); - KMVerificationToken.cast(verToken).setMac(KMByteBlob.instance(mac,(short)0,(short)mac.length)); - return verToken; - } - - @Test - public void testEcImportKeySuccess() { - init(); - /* - KeyPair ecKeyPair = cryptoProvider.createECKeyPair(); - byte[] pub = new byte[128]; - short len = ((ECPublicKey)ecKeyPair.getPublic()).getW(pub,(short)0); - byte[] priv = new byte[128]; - len = ((ECPrivateKey)ecKeyPair.getPrivate()).getS(priv,(short)0); - */ - 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)); - short byteBlob = KMByteBlob.instance((short)1); - KMByteBlob.cast(byteBlob).add((short)0, KMType.SHA2_256); - short digest = KMEnumArrayTag.instance(KMType.DIGEST, byteBlob); - short ecCurve = KMEnumTag.instance(KMType.ECCURVE, KMType.P_256); - KMArray.cast(arrPtr).add((short)0, boolTag); - KMArray.cast(arrPtr).add((short)1, keySize); - KMArray.cast(arrPtr).add((short)2, digest); - KMArray.cast(arrPtr).add((short)3, ecCurve); - KMArray.cast(arrPtr).add((short)4, KMEnumTag.instance(KMType.ALGORITHM, KMType.EC)); - short keyParams = KMKeyParameters.instance(arrPtr); - short keyFormatPtr = KMEnum.instance(KMType.KEY_FORMAT, KMType.RAW);// Note: VTS uses PKCS8 - short keyBlob = KMArray.instance((short)2); - KMArray.cast(keyBlob).add((short)0, privBlob); - KMArray.cast(keyBlob).add((short)1, pubBlob); - byte[] blob = new byte[128]; - short len = encoder.encode(keyBlob,blob,(short)0); - keyBlob = KMByteBlob.instance(blob, (short)0, len); - arrPtr = KMArray.instance((short)3); - KMArray arg = KMArray.cast(arrPtr); - arg.add((short) 0, keyParams); - arg.add((short)1, keyFormatPtr); - arg.add((short)2, keyBlob); - CommandAPDU apdu = encodeApdu((byte)0x11, arrPtr); - // print(commandAPDU.getBytes()); - ResponseAPDU response = simulator.transmitCommand(apdu); - short ret = KMArray.instance((short) 3); - KMArray.cast(ret).add((short) 0, KMInteger.exp()); - KMArray.cast(ret).add((short)1, KMByteBlob.exp()); - short inst = KMKeyCharacteristics.exp(); - KMArray.cast(ret).add((short) 2, inst); - byte[] respBuf = response.getBytes(); - len = (short) respBuf.length; - ret = decoder.decode(ret, respBuf, (short) 0, len); - short error = KMInteger.cast(KMArray.cast(ret).get((short)0)).getShort(); - short keyBlobLength = KMByteBlob.cast(KMArray.cast(ret).get((short)1)).length(); - short blobArr = extractKeyBlobArray(KMArray.cast(ret).get((short)1)); - short keyCharacteristics = KMArray.cast(ret).get((short)2); - short hwParams = KMKeyCharacteristics.cast(keyCharacteristics).getHardwareEnforced(); - short swParams = KMKeyCharacteristics.cast(keyCharacteristics).getSoftwareEnforced(); - Assert.assertEquals(0x9000, response.getSW()); - Assert.assertEquals(error, KMError.OK); - short tag = KMKeyParameters.findTag(KMType.BOOL_TAG, KMType.NO_AUTH_REQUIRED, hwParams); - Assert.assertEquals(KMBoolTag.cast(tag).getVal(),0x01); - tag = KMKeyParameters.findTag(KMType.UINT_TAG, KMType.KEYSIZE, hwParams); - Assert.assertEquals(KMInteger.cast(KMIntegerTag.cast(tag).getValue()).getShort(), 256); - tag = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.DIGEST, hwParams); - Assert.assertTrue(KMEnumArrayTag.cast(tag).contains(KMType.SHA2_256)); - tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ECCURVE, hwParams); - Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.P_256); - tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ALGORITHM, hwParams); - Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.EC); - tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ORIGIN, hwParams); - Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.IMPORTED); - cleanUp(); - } - - private short extractKeyBlobArray(short keyBlob) { - short ret = KMArray.instance((short) 5); - KMArray.cast(ret).add(KMKeymasterApplet.KEY_BLOB_SECRET, KMByteBlob.exp()); - KMArray.cast(ret).add(KMKeymasterApplet.KEY_BLOB_AUTH_TAG, KMByteBlob.exp()); - KMArray.cast(ret).add(KMKeymasterApplet.KEY_BLOB_NONCE, KMByteBlob.exp()); - short ptr = KMKeyCharacteristics.exp(); - KMArray.cast(ret).add(KMKeymasterApplet.KEY_BLOB_KEYCHAR, ptr); - KMArray.cast(ret).add(KMKeymasterApplet.KEY_BLOB_PUB_KEY, KMByteBlob.exp()); - ret = - decoder.decodeArray( - ret, - KMByteBlob.cast(keyBlob).getBuffer(), - KMByteBlob.cast(keyBlob).getStartOff(), - KMByteBlob.cast(keyBlob).length()); - short len = KMArray.cast(ret).length(); - ptr = KMArray.cast(ret).get((short)4); -// print(KMByteBlob.cast(ptr).getBuffer(),KMByteBlob.cast(ptr).getStartOff(),KMByteBlob.cast(ptr).length()); - return ret; - } - - @Test - public void testRsaGenerateKeySuccess() { - init(); - short ret = generateRsaKey(null, null); - short error = KMInteger.cast(KMArray.cast(ret).get((short)0)).getShort(); - short keyBlobLength = KMByteBlob.cast(KMArray.cast(ret).get((short)1)).length(); - short keyCharacteristics = KMArray.cast(ret).get((short)2); - short hwParams = KMKeyCharacteristics.cast(keyCharacteristics).getHardwareEnforced(); - short swParams = KMKeyCharacteristics.cast(keyCharacteristics).getSoftwareEnforced(); - Assert.assertEquals(error, KMError.OK); - short tag = KMKeyParameters.findTag(KMType.UINT_TAG, KMType.KEYSIZE, hwParams); - Assert.assertEquals(KMInteger.cast(KMIntegerTag.cast(tag).getValue()).getShort(), 2048); - tag = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.DIGEST, hwParams); - Assert.assertTrue(KMEnumArrayTag.cast(tag).contains(KMType.DIGEST_NONE)); - tag = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.PADDING, hwParams); - Assert.assertTrue(KMEnumArrayTag.cast(tag).contains(KMType.RSA_PKCS1_1_5_ENCRYPT)); - tag = KMKeyParameters.findTag(KMType.ULONG_TAG, KMType.RSA_PUBLIC_EXPONENT, hwParams); - Assert.assertEquals(KMInteger.cast(KMIntegerTag.cast(tag).getValue()).getSignificantShort(), 0x01); - Assert.assertEquals(KMInteger.cast(KMIntegerTag.cast(tag).getValue()).getShort(), 0x01); - tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ALGORITHM, hwParams); - Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.RSA); - tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ORIGIN, hwParams); - Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.GENERATED); - cleanUp(); - } - - private short generateRsaKey(byte[] clientId, byte[] appData){ - byte[] activeAndCreationDateTime = {0,0,0x01,0x73,0x51,0x7C,(byte)0xCC,0x00}; - short tagCount = 11; - if(clientId != null) tagCount++; - if(appData != null) tagCount++; - short arrPtr = KMArray.instance(tagCount); - short keySize = KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, KMInteger.uint_16((short)2048)); - short byteBlob = KMByteBlob.instance((short)3); - KMByteBlob.cast(byteBlob).add((short)0, KMType.DIGEST_NONE); - KMByteBlob.cast(byteBlob).add((short)1, KMType.SHA2_256); - KMByteBlob.cast(byteBlob).add((short)2, KMType.SHA1); - short digest = KMEnumArrayTag.instance(KMType.DIGEST, byteBlob); - byteBlob = KMByteBlob.instance((short)5); - KMByteBlob.cast(byteBlob).add((short)0, KMType.RSA_PKCS1_1_5_ENCRYPT); - KMByteBlob.cast(byteBlob).add((short)1, KMType.RSA_PKCS1_1_5_SIGN); - KMByteBlob.cast(byteBlob).add((short)2, KMType.RSA_OAEP); - KMByteBlob.cast(byteBlob).add((short)3, KMType.RSA_PSS); - KMByteBlob.cast(byteBlob).add((short)4, KMType.PADDING_NONE); - short padding = KMEnumArrayTag.instance(KMType.PADDING, byteBlob); - byteBlob = KMByteBlob.instance((short)5); - KMByteBlob.cast(byteBlob).add((short)0, KMType.SIGN); - KMByteBlob.cast(byteBlob).add((short)1, KMType.VERIFY); - KMByteBlob.cast(byteBlob).add((short)2, KMType.ENCRYPT); - KMByteBlob.cast(byteBlob).add((short)3, KMType.DECRYPT); - KMByteBlob.cast(byteBlob).add((short)4, KMType.WRAP_KEY); - short purpose = KMEnumArrayTag.instance(KMType.PURPOSE, byteBlob); - byte[] pub = {0,1,0,1}; - short rsaPubExpTag = KMIntegerTag.instance(KMType.ULONG_TAG,KMType.RSA_PUBLIC_EXPONENT, KMInteger.uint_32(pub, (short)0)); - short boolTag = KMBoolTag.instance(KMType.NO_AUTH_REQUIRED); - short tagIndex = 0; - KMArray.cast(arrPtr).add(tagIndex++, purpose); - KMArray.cast(arrPtr).add(tagIndex++, KMBoolTag.instance(KMType.INCLUDE_UNIQUE_ID)); - KMArray.cast(arrPtr).add(tagIndex++, KMBoolTag.instance(KMType.RESET_SINCE_ID_ROTATION)); - KMArray.cast(arrPtr).add(tagIndex++, boolTag); - KMArray.cast(arrPtr).add(tagIndex++, keySize); - KMArray.cast(arrPtr).add(tagIndex++, digest); - KMArray.cast(arrPtr).add(tagIndex++, rsaPubExpTag); - KMArray.cast(arrPtr).add(tagIndex++, KMEnumTag.instance(KMType.ALGORITHM, KMType.RSA)); - KMArray.cast(arrPtr).add(tagIndex++, padding); - short dateTag = KMInteger.uint_64(activeAndCreationDateTime,(short)0); - KMArray.cast(arrPtr).add(tagIndex++, KMIntegerTag.instance(KMType.DATE_TAG,KMType.ACTIVE_DATETIME,dateTag)); - KMArray.cast(arrPtr).add(tagIndex++, KMIntegerTag.instance(KMType.DATE_TAG,KMType.CREATION_DATETIME,dateTag)); - - if(clientId != null)KMArray.cast(arrPtr).add(tagIndex++, - KMByteTag.instance(KMType.APPLICATION_ID, KMByteBlob.instance(clientId,(short)0,(short)clientId.length))); - if(appData != null)KMArray.cast(arrPtr).add(tagIndex++, - KMByteTag.instance(KMType.APPLICATION_DATA, KMByteBlob.instance(appData,(short)0,(short)appData.length))); - short keyParams = KMKeyParameters.instance(arrPtr); - arrPtr = KMArray.instance((short)1); - KMArray arg = KMArray.cast(arrPtr); - arg.add((short) 0, keyParams); - CommandAPDU apdu = encodeApdu((byte)0x10, arrPtr); - // print(commandAPDU.getBytes()); - ResponseAPDU response = simulator.transmitCommand(apdu); - Assert.assertEquals(0x9000, response.getSW()); - short ret = KMArray.instance((short) 3); - KMArray.cast(ret).add((short) 0, KMInteger.exp()); - KMArray.cast(ret).add((short)1, KMByteBlob.exp()); - short inst = KMKeyCharacteristics.exp(); - KMArray.cast(ret).add((short) 2, inst); - byte[] respBuf = response.getBytes(); - short len = (short) respBuf.length; - ret = decoder.decode(ret, respBuf, (short) 0, len); - return ret; - } - - private short generateAttestationKey(){ - // 15th July 2020 00.00.00 - byte[] activeAndCreationDateTime = {0,0,0x01,0x73,0x51,0x7C,(byte)0xCC,0x00}; - short tagCount = 11; - short arrPtr = KMArray.instance(tagCount); - short keySize = KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, KMInteger.uint_16((short)2048)); - short byteBlob = KMByteBlob.instance((short)3); - KMByteBlob.cast(byteBlob).add((short)0, KMType.DIGEST_NONE); - KMByteBlob.cast(byteBlob).add((short)1, KMType.SHA2_256); - KMByteBlob.cast(byteBlob).add((short)2, KMType.SHA1); - short digest = KMEnumArrayTag.instance(KMType.DIGEST, byteBlob); - byteBlob = KMByteBlob.instance((short)1); - KMByteBlob.cast(byteBlob).add((short)0, KMType.RSA_PKCS1_1_5_SIGN); - short padding = KMEnumArrayTag.instance(KMType.PADDING, byteBlob); - byteBlob = KMByteBlob.instance((short)1); - KMByteBlob.cast(byteBlob).add((short)0, KMType.ATTEST_KEY); - short purpose = KMEnumArrayTag.instance(KMType.PURPOSE, byteBlob); - byte[] pub = {0,1,0,1}; - short rsaPubExpTag = KMIntegerTag.instance(KMType.ULONG_TAG,KMType.RSA_PUBLIC_EXPONENT, KMInteger.uint_32(pub, (short)0)); - short boolTag = KMBoolTag.instance(KMType.NO_AUTH_REQUIRED); - short tagIndex = 0; - KMArray.cast(arrPtr).add(tagIndex++, purpose); - KMArray.cast(arrPtr).add(tagIndex++, boolTag); - KMArray.cast(arrPtr).add(tagIndex++, KMBoolTag.instance(KMType.INCLUDE_UNIQUE_ID)); - KMArray.cast(arrPtr).add(tagIndex++, KMBoolTag.instance(KMType.RESET_SINCE_ID_ROTATION)); - KMArray.cast(arrPtr).add(tagIndex++, boolTag); - KMArray.cast(arrPtr).add(tagIndex++, keySize); - KMArray.cast(arrPtr).add(tagIndex++, digest); - KMArray.cast(arrPtr).add(tagIndex++, rsaPubExpTag); - KMArray.cast(arrPtr).add(tagIndex++, KMEnumTag.instance(KMType.ALGORITHM, KMType.RSA)); - KMArray.cast(arrPtr).add(tagIndex++, padding); - short dateTag = KMInteger.uint_64(activeAndCreationDateTime,(short)0); - KMArray.cast(arrPtr).add(tagIndex++, KMIntegerTag.instance(KMType.ULONG_TAG,KMType.ACTIVE_DATETIME,dateTag)); - KMArray.cast(arrPtr).add(tagIndex++, KMIntegerTag.instance(KMType.ULONG_TAG,KMType.CREATION_DATETIME,dateTag)); - short keyParams = KMKeyParameters.instance(arrPtr); - arrPtr = KMArray.instance((short)1); - KMArray arg = KMArray.cast(arrPtr); - arg.add((short) 0, keyParams); - CommandAPDU apdu = encodeApdu((byte)0x10, arrPtr); - // print(commandAPDU.getBytes()); - ResponseAPDU response = simulator.transmitCommand(apdu); - Assert.assertEquals(0x9000, response.getSW()); - short ret = KMArray.instance((short) 3); - KMArray.cast(ret).add((short) 0, KMInteger.exp()); - KMArray.cast(ret).add((short)1, KMByteBlob.exp()); - short inst = KMKeyCharacteristics.exp(); - KMArray.cast(ret).add((short) 2, inst); - byte[] respBuf = response.getBytes(); - short len = (short) respBuf.length; - ret = decoder.decode(ret, respBuf, (short) 0, len); - return ret; - } - - @Test - public void testEcGenerateKeySuccess() { - init(); - short ret = generateEcKey(null, null); - short error = KMInteger.cast(KMArray.cast(ret).get((short)0)).getShort(); - short keyBlobLength = KMByteBlob.cast(KMArray.cast(ret).get((short)1)).length(); - short keyCharacteristics = KMArray.cast(ret).get((short)2); - short hwParams = KMKeyCharacteristics.cast(keyCharacteristics).getHardwareEnforced(); - short swParams = KMKeyCharacteristics.cast(keyCharacteristics).getSoftwareEnforced(); - Assert.assertEquals(error, KMError.OK); - short tag = KMKeyParameters.findTag(KMType.UINT_TAG, KMType.KEYSIZE, hwParams); - Assert.assertEquals(KMInteger.cast(KMIntegerTag.cast(tag).getValue()).getShort(), 256); - tag = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.DIGEST, hwParams); - Assert.assertTrue(KMEnumArrayTag.cast(tag).contains(KMType.DIGEST_NONE)); - tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ALGORITHM, hwParams); - Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.EC); - tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ORIGIN, hwParams); - Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.GENERATED); - cleanUp(); - } - public short generateEcKey(byte[] clientId, byte[] appData) { - byte[] activeAndCreationDateTime = {0,0,0x01,0x73,0x51,0x7C,(byte)0xCC,0x00}; - short tagCount = 6; - if(clientId != null) tagCount++; - if(appData != null) tagCount++; - short arrPtr = KMArray.instance(tagCount); - short keySize = KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, KMInteger.uint_16((short)256)); - short byteBlob = KMByteBlob.instance((short)2); - KMByteBlob.cast(byteBlob).add((short)0, KMType.DIGEST_NONE); - KMByteBlob.cast(byteBlob).add((short)1, KMType.SHA2_256); - short digest = KMEnumArrayTag.instance(KMType.DIGEST, byteBlob); - byteBlob = KMByteBlob.instance((short)2); - KMByteBlob.cast(byteBlob).add((short)0, KMType.SIGN); - KMByteBlob.cast(byteBlob).add((short)1, KMType.VERIFY); - short purpose = KMEnumArrayTag.instance(KMType.PURPOSE, byteBlob); - short boolTag = KMBoolTag.instance(KMType.NO_AUTH_REQUIRED); - short tagIndex = 0; - KMArray.cast(arrPtr).add(tagIndex++, purpose); - KMArray.cast(arrPtr).add(tagIndex++, boolTag); - KMArray.cast(arrPtr).add(tagIndex++, keySize); - KMArray.cast(arrPtr).add(tagIndex++, digest); - KMArray.cast(arrPtr).add(tagIndex++, KMEnumTag.instance(KMType.ALGORITHM, KMType.EC)); - short dateTag = KMInteger.uint_64(activeAndCreationDateTime,(short)0); - KMArray.cast(arrPtr).add(tagIndex++, KMIntegerTag.instance(KMType.DATE_TAG,KMType.CREATION_DATETIME,dateTag)); - if(clientId != null)KMArray.cast(arrPtr).add(tagIndex++, - KMByteTag.instance(KMType.APPLICATION_ID, KMByteBlob.instance(clientId,(short)0,(short)clientId.length))); - if(appData != null)KMArray.cast(arrPtr).add(tagIndex++, - KMByteTag.instance(KMType.APPLICATION_DATA, KMByteBlob.instance(appData,(short)0,(short)appData.length))); - short keyParams = KMKeyParameters.instance(arrPtr); - arrPtr = KMArray.instance((short)1); - KMArray arg = KMArray.cast(arrPtr); - arg.add((short) 0, keyParams); - CommandAPDU apdu = encodeApdu((byte)0x10, arrPtr); - // print(commandAPDU.getBytes()); - ResponseAPDU response = simulator.transmitCommand(apdu); - Assert.assertEquals(0x9000, response.getSW()); - short ret = KMArray.instance((short) 3); - KMArray.cast(ret).add((short) 0, KMInteger.exp()); - KMArray.cast(ret).add((short)1, KMByteBlob.exp()); - short inst = KMKeyCharacteristics.exp(); - KMArray.cast(ret).add((short) 2, inst); - byte[] respBuf = response.getBytes(); - short len = (short) respBuf.length; - ret = decoder.decode(ret, respBuf, (short) 0, len); - return ret; - } - - @Test - public void testHmacGenerateKeySuccess() { - init(); - short ret = generateHmacKey(null, null); - short error = KMInteger.cast(KMArray.cast(ret).get((short)0)).getShort(); - short keyBlobLength = KMByteBlob.cast(KMArray.cast(ret).get((short)1)).length(); - short keyCharacteristics = KMArray.cast(ret).get((short)2); - short hwParams = KMKeyCharacteristics.cast(keyCharacteristics).getHardwareEnforced(); - short swParams = KMKeyCharacteristics.cast(keyCharacteristics).getSoftwareEnforced(); - Assert.assertEquals(error, KMError.OK); - short tag = KMKeyParameters.findTag(KMType.UINT_TAG, KMType.KEYSIZE, hwParams); - Assert.assertEquals(KMInteger.cast(KMIntegerTag.cast(tag).getValue()).getShort(), 128); - tag = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.DIGEST, hwParams); - Assert.assertTrue(KMEnumArrayTag.cast(tag).contains(KMType.SHA2_256)); - tag = KMKeyParameters.findTag(KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, hwParams); - Assert.assertEquals(KMInteger.cast(KMIntegerTag.cast(tag).getValue()).getShort(), 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); - Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.GENERATED); - cleanUp(); - } - public short generateHmacKey(byte[] clientId, byte[] appData){ - short tagCount = 6; - if(clientId != null) tagCount++; - if(appData != null) tagCount++; - short arrPtr = KMArray.instance(tagCount); - short keySize = KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, KMInteger.uint_16((short)128)); - short byteBlob = KMByteBlob.instance((short)1); - KMByteBlob.cast(byteBlob).add((short)0, KMType.SHA2_256); - short digest = KMEnumArrayTag.instance(KMType.DIGEST, byteBlob); - byteBlob = KMByteBlob.instance((short)2); - KMByteBlob.cast(byteBlob).add((short)0, KMType.SIGN); - 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)/*256*/160)); - short tagIndex = 0; - KMArray.cast(arrPtr).add(tagIndex++, minMacLen); - KMArray.cast(arrPtr).add(tagIndex++, purpose); - KMArray.cast(arrPtr).add(tagIndex++, boolTag); - KMArray.cast(arrPtr).add(tagIndex++, keySize); - KMArray.cast(arrPtr).add(tagIndex++, digest); - KMArray.cast(arrPtr).add(tagIndex++, KMEnumTag.instance(KMType.ALGORITHM, KMType.HMAC)); - if(clientId != null)KMArray.cast(arrPtr).add(tagIndex++, - KMByteTag.instance(KMType.APPLICATION_ID, KMByteBlob.instance(clientId,(short)0,(short)clientId.length))); - if(appData != null)KMArray.cast(arrPtr).add(tagIndex++, - KMByteTag.instance(KMType.APPLICATION_DATA, KMByteBlob.instance(appData,(short)0,(short)appData.length))); - short keyParams = KMKeyParameters.instance(arrPtr); - arrPtr = KMArray.instance((short)1); - KMArray arg = KMArray.cast(arrPtr); - arg.add((short) 0, keyParams); - CommandAPDU apdu = encodeApdu((byte)0x10, arrPtr); - // print(commandAPDU.getBytes()); - ResponseAPDU response = simulator.transmitCommand(apdu); - short ret = KMArray.instance((short) 3); - KMArray.cast(ret).add((short) 0, KMInteger.exp()); - KMArray.cast(ret).add((short)1, KMByteBlob.exp()); - short inst = KMKeyCharacteristics.exp(); - KMArray.cast(ret).add((short) 2, inst); - byte[] respBuf = response.getBytes(); - short len = (short) respBuf.length; - ret = decoder.decode(ret, respBuf, (short) 0, len); - short error = KMInteger.cast(KMArray.cast(ret).get((short)0)).getShort(); - Assert.assertEquals(0x9000, response.getSW()); - Assert.assertEquals(error, KMError.OK); - return ret; - } - public short generateAesDesKey(byte alg, short keysize, byte[] clientId, byte[] appData, boolean unlockReqd) { - short tagCount = 7; - if(clientId != null) tagCount++; - if(appData != null) tagCount++; - if(unlockReqd)tagCount++; - short arrPtr = KMArray.instance(tagCount); - short boolTag = KMBoolTag.instance(KMType.NO_AUTH_REQUIRED); - short keySize = KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, KMInteger.uint_16(keysize)); - short byteBlob = KMByteBlob.instance((short)3); - KMByteBlob.cast(byteBlob).add((short)0, KMType.ECB); - KMByteBlob.cast(byteBlob).add((short)1, KMType.CBC); - KMByteBlob.cast(byteBlob).add((short)2, KMType.CTR); - short blockModeTag = KMEnumArrayTag.instance(KMType.BLOCK_MODE, byteBlob); - byteBlob = KMByteBlob.instance((short)2); - KMByteBlob.cast(byteBlob).add((short)0, KMType.PKCS7); - KMByteBlob.cast(byteBlob).add((short)1, KMType.PADDING_NONE); - short paddingMode = KMEnumArrayTag.instance(KMType.PADDING, byteBlob); - byteBlob = KMByteBlob.instance((short)2); - KMByteBlob.cast(byteBlob).add((short)0, KMType.ENCRYPT); - KMByteBlob.cast(byteBlob).add((short)1, KMType.DECRYPT); - short purpose = KMEnumArrayTag.instance(KMType.PURPOSE, byteBlob); - short tagIndex = 0; - KMArray.cast(arrPtr).add(tagIndex++, boolTag); - KMArray.cast(arrPtr).add(tagIndex++, keySize); - KMArray.cast(arrPtr).add(tagIndex++, blockModeTag); - KMArray.cast(arrPtr).add(tagIndex++, paddingMode); - KMArray.cast(arrPtr).add(tagIndex++, KMEnumTag.instance(KMType.ALGORITHM, alg)); - KMArray.cast(arrPtr).add(tagIndex++, purpose); - KMArray.cast(arrPtr).add(tagIndex++, KMBoolTag.instance(KMType.CALLER_NONCE)); - if(unlockReqd)KMArray.cast(arrPtr).add(tagIndex++, KMBoolTag.instance(KMType.UNLOCKED_DEVICE_REQUIRED)); - if(clientId != null)KMArray.cast(arrPtr).add(tagIndex++, - KMByteTag.instance(KMType.APPLICATION_ID, KMByteBlob.instance(clientId,(short)0,(short)clientId.length))); - if(appData != null)KMArray.cast(arrPtr).add(tagIndex++, - KMByteTag.instance(KMType.APPLICATION_DATA, KMByteBlob.instance(appData,(short)0,(short)appData.length))); - short keyParams = KMKeyParameters.instance(arrPtr); - arrPtr = KMArray.instance((short)1); - KMArray arg = KMArray.cast(arrPtr); - arg.add((short) 0, keyParams); - CommandAPDU apdu = encodeApdu((byte)0x10, arrPtr); - // print(commandAPDU.getBytes()); - ResponseAPDU response = simulator.transmitCommand(apdu); - short ret = KMArray.instance((short) 3); - KMArray.cast(ret).add((short) 0, KMInteger.exp()); - KMArray.cast(ret).add((short)1, KMByteBlob.exp()); - short inst = KMKeyCharacteristics.exp(); - KMArray.cast(ret).add((short) 2, inst); - byte[] respBuf = response.getBytes(); - short len = (short) respBuf.length; - ret = decoder.decode(ret, respBuf, (short) 0, len); - short error = KMInteger.cast(KMArray.cast(ret).get((short)0)).getShort(); - Assert.assertEquals(0x9000, response.getSW()); - Assert.assertEquals(error, KMError.OK); - return ret; - } - public short generateAesGcmKey(short keysize, byte[] clientId, byte[] appData) { - short tagCount = 8; - if(clientId != null) tagCount++; - if(appData != null) tagCount++; - short arrPtr = KMArray.instance(tagCount); - short boolTag = KMBoolTag.instance(KMType.NO_AUTH_REQUIRED); - short keySize = KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, KMInteger.uint_16(keysize)); - short macLength = KMIntegerTag.instance(KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, KMInteger.uint_16((short)96)); - short byteBlob = KMByteBlob.instance((short)1); - KMByteBlob.cast(byteBlob).add((short)0, KMType.GCM); - short blockModeTag = KMEnumArrayTag.instance(KMType.BLOCK_MODE, byteBlob); - byteBlob = KMByteBlob.instance((short)1); - KMByteBlob.cast(byteBlob).add((short)0, KMType.PADDING_NONE); - short paddingMode = KMEnumArrayTag.instance(KMType.PADDING, byteBlob); - byteBlob = KMByteBlob.instance((short)2); - KMByteBlob.cast(byteBlob).add((short)0, KMType.ENCRYPT); - KMByteBlob.cast(byteBlob).add((short)1, KMType.DECRYPT); - short purpose = KMEnumArrayTag.instance(KMType.PURPOSE, byteBlob); - short tagIndex = 0; - KMArray.cast(arrPtr).add(tagIndex++, boolTag); - KMArray.cast(arrPtr).add(tagIndex++, macLength); - KMArray.cast(arrPtr).add(tagIndex++, keySize); - KMArray.cast(arrPtr).add(tagIndex++, blockModeTag); - KMArray.cast(arrPtr).add(tagIndex++, paddingMode); - KMArray.cast(arrPtr).add(tagIndex++, KMEnumTag.instance(KMType.ALGORITHM, KMType.AES)); - KMArray.cast(arrPtr).add(tagIndex++, purpose); - KMArray.cast(arrPtr).add(tagIndex++, KMBoolTag.instance(KMType.CALLER_NONCE)); - if(clientId != null)KMArray.cast(arrPtr).add(tagIndex++, - KMByteTag.instance(KMType.APPLICATION_ID, KMByteBlob.instance(clientId,(short)0,(short)clientId.length))); - if(appData != null)KMArray.cast(arrPtr).add(tagIndex++, - KMByteTag.instance(KMType.APPLICATION_DATA, KMByteBlob.instance(appData,(short)0,(short)appData.length))); - short keyParams = KMKeyParameters.instance(arrPtr); - arrPtr = KMArray.instance((short)1); - KMArray arg = KMArray.cast(arrPtr); - arg.add((short) 0, keyParams); - CommandAPDU apdu = encodeApdu((byte)0x10, arrPtr); - // print(commandAPDU.getBytes()); - ResponseAPDU response = simulator.transmitCommand(apdu); - short ret = KMArray.instance((short) 3); - KMArray.cast(ret).add((short) 0, KMInteger.exp()); - KMArray.cast(ret).add((short)1, KMByteBlob.exp()); - short inst = KMKeyCharacteristics.exp(); - KMArray.cast(ret).add((short) 2, inst); - byte[] respBuf = response.getBytes(); - short len = (short) respBuf.length; - ret = decoder.decode(ret, respBuf, (short) 0, len); - short error = KMInteger.cast(KMArray.cast(ret).get((short)0)).getShort(); - Assert.assertEquals(0x9000, response.getSW()); - Assert.assertEquals(error, KMError.OK); - return ret; - } - - @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); - 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)); - num = KMByteBlob.instance((short)32); - cryptoProvider.newRandomNumber( - KMByteBlob.cast(num).getBuffer(), - KMByteBlob.cast(num).getStartOff(), - KMByteBlob.cast(num).length()); - KMHmacSharingParameters.cast(params2).setNonce(num); - short arr = KMArray.instance((short)2); - KMArray.cast(arr).add((short)0, params1); - KMArray.cast(arr).add((short)1,params2); - short arrPtr = KMArray.instance((short)1); - KMArray.cast(arrPtr).add((short)0,arr); - CommandAPDU apdu = encodeApdu((byte)0x19, arrPtr); - // print(commandAPDU.getBytes()); - ResponseAPDU response = simulator.transmitCommand(apdu); - Assert.assertEquals(0x9000, response.getSW()); - 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); - error = KMInteger.cast(KMArray.cast(ret).get((short)0)).getShort(); - Assert.assertEquals(0x9000, response.getSW()); - Assert.assertEquals(error, KMError.OK); - - cleanUp(); - } - @Test - public void testGetHmacSharingParams(){ - init(); - CommandAPDU commandAPDU = new CommandAPDU(0x80, 0x1C, 0x40, 0x00); - //print(commandAPDU.getBytes()); - ResponseAPDU response = simulator.transmitCommand(commandAPDU); - KMDecoder dec = new KMDecoder(); - short ret = KMArray.instance((short) 2); - KMArray.cast(ret).add((short) 0, KMInteger.exp()); - short inst = KMHmacSharingParameters.exp(); - KMArray.cast(ret).add((short) 1, inst); - byte[] respBuf = response.getBytes(); - short len = (short) respBuf.length; - ret = decoder.decode(ret, respBuf, (short) 0, len); - short error = KMInteger.cast(KMArray.cast(ret).get((short)0)).getShort(); - KMHmacSharingParameters params = KMHmacSharingParameters.cast(KMArray.cast(ret).get((short)1)); - short seed = params.getSeed(); - short nonce = params.getNonce(); - Assert.assertTrue(KMByteBlob.cast(seed).length() == 0); - Assert.assertTrue(KMByteBlob.cast(nonce).length() == 32); - //print(seed); - //print(nonce); - Assert.assertEquals(error, KMError.OK); - cleanUp(); - } - public short getHmacSharingParams(){ - CommandAPDU commandAPDU = new CommandAPDU(0x80, 0x1C, 0x40, 0x00); - //print(commandAPDU.getBytes()); - ResponseAPDU response = simulator.transmitCommand(commandAPDU); - KMDecoder dec = new KMDecoder(); - short ret = KMArray.instance((short) 2); - KMArray.cast(ret).add((short) 0, KMInteger.exp()); - short inst = KMHmacSharingParameters.exp(); - KMArray.cast(ret).add((short) 1, inst); - byte[] respBuf = response.getBytes(); - short len = (short) respBuf.length; - ret = decoder.decode(ret, respBuf, (short) 0, len); - return ret; - } - - @Test - public void testImportWrappedKey(){ - init(); - byte[] wrappedKey = new byte[16]; - cryptoProvider.newRandomNumber(wrappedKey,(short)0,(short)16); - byte[] encWrappedKey = new byte[16]; - //AESKey transportKey = cryptoProvider.createAESKey((short)256); - byte[] transportKeyMaterial = new byte[32]; - cryptoProvider.newRandomNumber(transportKeyMaterial,(short)0,(short)32); - //transportKey.setKey(transportKeyMaterial,(short)0); - byte[] nonce = new byte[12]; - cryptoProvider.newRandomNumber(nonce,(short)0,(short)12); - byte[] authData = "Auth Data".getBytes(); - byte[] authTag = new byte[12]; - cryptoProvider.aesGCMEncrypt(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}; - byte[] maskedTransportKey = new byte[32]; - for(int i=0; i< maskingKey.length;i++){ - maskedTransportKey[i] = (byte)(transportKeyMaterial[i] ^ maskingKey[i]); - } - short rsaKeyArr = generateRsaKey(null,null); - short keyBlobPtr = KMArray.cast(rsaKeyArr).get((short)1); - byte[] wrappingKeyBlob= new byte[KMByteBlob.cast(keyBlobPtr).length()]; - Util.arrayCopyNonAtomic(KMByteBlob.cast(keyBlobPtr).getBuffer(), - KMByteBlob.cast(keyBlobPtr).getStartOff(), - wrappingKeyBlob,(short)0, (short)wrappingKeyBlob.length); - short inParams = getRsaParams(KMType.SHA2_256, KMType.RSA_OAEP); - short ret = processMessage(maskedTransportKey, - KMByteBlob.instance(wrappingKeyBlob,(short)0, (short)wrappingKeyBlob.length), - KMType.ENCRYPT, - KMKeyParameters.instance(inParams), - (short)0,null,false,false - ); - keyBlobPtr = KMArray.cast(ret).get((short)2); - byte[] encTransportKey = new byte[KMByteBlob.cast(keyBlobPtr).length()]; - Util.arrayCopyNonAtomic(KMByteBlob.cast(keyBlobPtr).getBuffer(), KMByteBlob.cast(keyBlobPtr).getStartOff(), - encTransportKey,(short)0, (short)encTransportKey.length); - short tagCount = 7; - short arrPtr = KMArray.instance(tagCount); - short boolTag = KMBoolTag.instance(KMType.NO_AUTH_REQUIRED); - short keySize = KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, KMInteger.uint_16((short)128)); - short byteBlob = KMByteBlob.instance((short)2); - KMByteBlob.cast(byteBlob).add((short)0, KMType.ECB); - KMByteBlob.cast(byteBlob).add((short)1, KMType.CBC); - short blockModeTag = KMEnumArrayTag.instance(KMType.BLOCK_MODE, byteBlob); - byteBlob = KMByteBlob.instance((short)2); - KMByteBlob.cast(byteBlob).add((short)0, KMType.PKCS7); - KMByteBlob.cast(byteBlob).add((short)1, KMType.PADDING_NONE); - short paddingMode = KMEnumArrayTag.instance(KMType.PADDING, byteBlob); - byteBlob = KMByteBlob.instance((short)2); - KMByteBlob.cast(byteBlob).add((short)0, KMType.ENCRYPT); - KMByteBlob.cast(byteBlob).add((short)1, KMType.DECRYPT); - short purpose = KMEnumArrayTag.instance(KMType.PURPOSE, byteBlob); - short tagIndex = 0; - KMArray.cast(arrPtr).add(tagIndex++, boolTag); - KMArray.cast(arrPtr).add(tagIndex++, keySize); - KMArray.cast(arrPtr).add(tagIndex++, blockModeTag); - KMArray.cast(arrPtr).add(tagIndex++, paddingMode); - KMArray.cast(arrPtr).add(tagIndex++, KMEnumTag.instance(KMType.ALGORITHM, KMType.AES)); - KMArray.cast(arrPtr).add(tagIndex++, purpose); - KMArray.cast(arrPtr).add(tagIndex++, KMBoolTag.instance(KMType.CALLER_NONCE)); - short keyParams = KMKeyParameters.instance(arrPtr); - short nullParams = KMArray.instance((short)0); - nullParams = KMKeyParameters.instance(nullParams); - short arr = KMArray.instance((short)12); - KMArray.cast(arr).add((short) 0, keyParams); // Key Params of wrapped key - KMArray.cast(arr).add((short) 1, KMEnum.instance(KMType.KEY_FORMAT,KMType.RAW)); // Key Format - KMArray.cast(arr).add((short) 2, KMByteBlob.instance(encWrappedKey,(short)0,(short)encWrappedKey.length)); // Wrapped Import Key Blob - KMArray.cast(arr).add((short) 3, KMByteBlob.instance(authTag,(short)0,(short)authTag.length)); // Auth Tag - KMArray.cast(arr).add((short) 4, KMByteBlob.instance(nonce,(short)0,(short)nonce.length)); // IV - Nonce - KMArray.cast(arr).add((short) 5, KMByteBlob.instance(encTransportKey,(short)0,(short)encTransportKey.length)); // Encrypted Transport Key - KMArray.cast(arr).add((short) 6, KMByteBlob.instance(wrappingKeyBlob,(short)0, (short)wrappingKeyBlob.length)); // Wrapping Key KeyBlob - KMArray.cast(arr).add((short) 7, KMByteBlob.instance(maskingKey,(short)0,(short)maskingKey.length)); // Masking Key - KMArray.cast(arr).add((short) 8, nullParams); // Un-wrapping Params - KMArray.cast(arr).add((short) 9, KMByteBlob.instance(authData,(short)0,(short)authData.length)); // Wrapped Key ASSOCIATED AUTH DATA - KMArray.cast(arr).add((short) 10, KMInteger.uint_8((byte)0)); // Password Sid - KMArray.cast(arr).add((short) 11, KMInteger.uint_8((byte)0)); // Biometric Sid - CommandAPDU apdu = encodeApdu((byte)0x12, arr); - // print(commandAPDU.getBytes()); - ResponseAPDU response = simulator.transmitCommand(apdu); - ret = KMArray.instance((short) 3); - KMArray.cast(ret).add((short) 0, KMInteger.exp()); - KMArray.cast(ret).add((short)1, KMByteBlob.exp()); - short inst = KMKeyCharacteristics.exp(); - KMArray.cast(ret).add((short) 2, inst); - byte[] respBuf = response.getBytes(); - short len = (short) respBuf.length; - ret = decoder.decode(ret, respBuf, (short) 0, len); - short error = KMInteger.cast(KMArray.cast(ret).get((short)0)).getShort(); - short keyBlobLength = KMByteBlob.cast(KMArray.cast(ret).get((short)1)).length(); - short keyCharacteristics = KMArray.cast(ret).get((short)2); - short hwParams = KMKeyCharacteristics.cast(keyCharacteristics).getHardwareEnforced(); - short swParams = KMKeyCharacteristics.cast(keyCharacteristics).getSoftwareEnforced(); - Assert.assertEquals(0x9000, response.getSW()); - Assert.assertEquals(error, KMError.OK); - short tag = KMKeyParameters.findTag(KMType.BOOL_TAG, KMType.NO_AUTH_REQUIRED, hwParams); - Assert.assertEquals(KMBoolTag.cast(tag).getVal(),0x01); - tag = KMKeyParameters.findTag(KMType.UINT_TAG, KMType.KEYSIZE, hwParams); - Assert.assertEquals(KMInteger.cast(KMIntegerTag.cast(tag).getValue()).getShort(), 128); - tag = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.PADDING, hwParams); - Assert.assertTrue(KMEnumArrayTag.cast(tag).contains(KMType.PKCS7)); - tag = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.BLOCK_MODE, hwParams); - Assert.assertTrue(KMEnumArrayTag.cast(tag).contains(KMType.ECB)); - tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ALGORITHM, hwParams); - Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.AES); - tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ORIGIN, hwParams); - Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.SECURELY_IMPORTED); - cleanUp(); - } - - @Test - public void testGetKeyCharacteristicsWithIdDataSuccess() { - init(); - byte[] clientId = "clientId".getBytes(); - byte[] appData = "appData".getBytes(); - short ret = generateRsaKey(clientId,appData); - short error = KMInteger.cast(KMArray.cast(ret).get((short)0)).getShort(); - Assert.assertEquals(error, KMError.OK); - short keyBlob = KMArray.cast(ret).get((short)1); - - short arrPtr = KMArray.instance((short)3); - KMArray.cast(arrPtr).add((short)0, keyBlob); - KMArray.cast(arrPtr).add((short)1, KMByteBlob.instance(clientId,(short)0, (short)clientId.length)); - KMArray.cast(arrPtr).add((short)2, KMByteBlob.instance(appData,(short)0, (short)appData.length)); - CommandAPDU apdu = encodeApdu((byte)0x1D, arrPtr); - // print(commandAPDU.getBytes()); - ResponseAPDU response = simulator.transmitCommand(apdu); - ret = KMArray.instance((short) 2); - KMArray.cast(ret).add((short) 0, KMInteger.exp()); - short inst = KMKeyCharacteristics.exp(); - KMArray.cast(ret).add((short) 1, inst); - byte[] respBuf = response.getBytes(); - short len = (short) respBuf.length; - ret = decoder.decode(ret, respBuf, (short) 0, len); - error = KMInteger.cast(KMArray.cast(ret).get((short)0)).getShort(); - Assert.assertEquals(error, KMError.OK); - cleanUp(); - } - - @Test - public void testGetKeyCharacteristicsSuccess() { - init(); - short ret = generateRsaKey(null, null); - short error = KMInteger.cast(KMArray.cast(ret).get((short)0)).getShort(); - Assert.assertEquals(error, KMError.OK); - short keyBlob = KMArray.cast(ret).get((short)1); - - short arrPtr = KMArray.instance((short)3); - KMArray.cast(arrPtr).add((short)0, keyBlob); - KMArray.cast(arrPtr).add((short)1, KMByteBlob.instance((short)0)); - KMArray.cast(arrPtr).add((short)2, KMByteBlob.instance((short)0)); - CommandAPDU apdu = encodeApdu((byte)0x1D, arrPtr); - // print(commandAPDU.getBytes()); - ResponseAPDU response = simulator.transmitCommand(apdu); - ret = KMArray.instance((short) 2); - KMArray.cast(ret).add((short) 0, KMInteger.exp()); - short inst = KMKeyCharacteristics.exp(); - KMArray.cast(ret).add((short) 1, inst); - byte[] respBuf = response.getBytes(); - short len = (short) respBuf.length; - ret = decoder.decode(ret, respBuf, (short) 0, len); - error = KMInteger.cast(KMArray.cast(ret).get((short)0)).getShort(); - Assert.assertEquals(error, KMError.OK); - cleanUp(); - } - - @Test - public void testDeleteKeySuccess() { - init(); - short ret = generateRsaKey(null, null); - short keyBlobPtr = KMArray.cast(ret).get((short)1); - byte[] keyBlob = new byte[KMByteBlob.cast(keyBlobPtr).length()]; - short len = KMByteBlob.cast(keyBlobPtr).getValues(keyBlob, (short)0); - ret = getKeyCharacteristics(keyBlobPtr); - short error = KMInteger.cast(KMArray.cast(ret).get((short)0)).getShort(); - 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)); - short err = KMByteBlob.cast(ret).get((short)1); - Assert.assertEquals(KMError.INVALID_KEY_BLOB,err); - - */ - cleanUp(); - } - - @Test - public void testDeleteAllKeySuccess() { - init(); - short ret1 = generateRsaKey(null, null); - short keyBlobPtr = KMArray.cast(ret1).get((short)1); - byte[] keyBlob1 = new byte[KMByteBlob.cast(keyBlobPtr).length()]; - short len = KMByteBlob.cast(keyBlobPtr).getValues(keyBlob1, (short)0); - short ret2 = generateRsaKey(null, null); - keyBlobPtr = KMArray.cast(ret2).get((short)1); - byte[] keyBlob2 = new byte[KMByteBlob.cast(keyBlobPtr).length()]; - len = KMByteBlob.cast(keyBlobPtr).getValues(keyBlob2, (short)0); - CommandAPDU apdu = new CommandAPDU(0x80, 0x17, 0x40, 0x00); - // print(commandAPDU.getBytes()); - 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 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(); - } - - private short deleteKey(short keyBlob) { - short arrPtr = KMArray.instance((short)1); - KMArray.cast(arrPtr).add((short)0, keyBlob); - CommandAPDU apdu = encodeApdu((byte)0x16, arrPtr); - // print(commandAPDU.getBytes()); - ResponseAPDU response = simulator.transmitCommand(apdu); - byte[] respBuf = response.getBytes(); - return respBuf[0]; - } - - private short abort(short opHandle) { - short arrPtr = KMArray.instance((short)1); - KMArray.cast(arrPtr).add((short)0, opHandle); - CommandAPDU apdu = encodeApdu((byte)0x22, arrPtr); - // print(commandAPDU.getBytes()); - ResponseAPDU response = simulator.transmitCommand(apdu); - byte[] respBuf = response.getBytes(); - return respBuf[0]; - } - - public short getKeyCharacteristics(short keyBlob){ - short arrPtr = KMArray.instance((short)3); - KMArray.cast(arrPtr).add((short)0, keyBlob); - KMArray.cast(arrPtr).add((short)1, KMByteBlob.instance((short)0)); - KMArray.cast(arrPtr).add((short)2, KMByteBlob.instance((short)0)); - CommandAPDU apdu = encodeApdu((byte)0x1D, arrPtr); - // print(commandAPDU.getBytes()); - ResponseAPDU response = simulator.transmitCommand(apdu); - 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); - byte[] respBuf = response.getBytes(); - short len = (short) respBuf.length; - if( len > 5) - ret = decoder.decode(ret, respBuf, (short) 0, len); - else - ret = KMByteBlob.instance(respBuf, (short)0, len); - return ret; - } - - @Test - public void testWithAesGcmWithUpdate(){ - init(); - testEncryptDecryptWithAesDes(KMType.AES, KMType.GCM, KMType.PADDING_NONE,true); - cleanUp(); - } - @Test - public void testWithAesEcbPkcs7WithUpdate(){ - init(); - testEncryptDecryptWithAesDes(KMType.AES, KMType.ECB, KMType.PKCS7,true); - cleanUp(); - } - - @Test - public void testWithAesCtrNoPadWithUpdate(){ - init(); - testEncryptDecryptWithAesDes(KMType.AES, KMType.CTR, KMType.PADDING_NONE,true); - cleanUp(); - } - - @Test - public void testWithAesCtrNoPad(){ - init(); - testEncryptDecryptWithAesDes(KMType.AES, KMType.CTR, KMType.PADDING_NONE,false); - cleanUp(); - } - - @Test - public void testWithAesEcbNoPadWithUpdate(){ - init(); - testEncryptDecryptWithAesDes(KMType.AES, KMType.ECB, KMType.PADDING_NONE,true); - cleanUp(); - } - @Test - public void testWithDesEcbPkcs7WithUpdate(){ - init(); - testEncryptDecryptWithAesDes(KMType.DES, KMType.ECB, KMType.PKCS7,true); - cleanUp(); - } - @Test - public void testWithDesEcbNoPadWithUpdate(){ - init(); - testEncryptDecryptWithAesDes(KMType.DES, KMType.ECB, KMType.PADDING_NONE,true); - cleanUp(); - } - @Test - public void testWithAesCbcPkcs7WithUpdate(){ - init(); - testEncryptDecryptWithAesDes(KMType.AES, KMType.CBC, KMType.PKCS7,true); - cleanUp(); - } - @Test - public void testWithAesCbcNoPadWithUpdate(){ - init(); - testEncryptDecryptWithAesDes(KMType.AES, KMType.CBC, KMType.PADDING_NONE,true); - cleanUp(); - } - @Test - public void testWithDesCbcPkcs7WithUpdate(){ - init(); - testEncryptDecryptWithAesDes(KMType.DES, KMType.CBC, KMType.PKCS7,true); - cleanUp(); - } - @Test - public void testWithDesCbcNoPadWithUpdate(){ - init(); - testEncryptDecryptWithAesDes(KMType.DES, KMType.CBC, KMType.PADDING_NONE,true); - cleanUp(); - } - - @Test - public void testWithAesEcbPkcs7(){ - init(); - testEncryptDecryptWithAesDes(KMType.AES, KMType.ECB, KMType.PKCS7,false); - cleanUp(); - } - @Test - public void testWithAesCbcPkcs7(){ - init(); - testEncryptDecryptWithAesDes(KMType.AES, KMType.CBC, KMType.PKCS7,false); - cleanUp(); - } - @Test - public void testWithAesEcbNoPad(){ - init(); - 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(); - testEncryptDecryptWithAesDes(KMType.DES, KMType.CBC, KMType.PADDING_NONE,false); - cleanUp(); - } - @Test - public void testWithDesEcbNoPad(){ - init(); - testEncryptDecryptWithAesDes(KMType.DES, KMType.ECB, KMType.PADDING_NONE,false); - cleanUp(); - } - @Test - public void testWithDesEcbPkcs7(){ - init(); - testEncryptDecryptWithAesDes(KMType.DES, KMType.ECB, KMType.PKCS7,false); - cleanUp(); - } - - @Test - public void testWithRsa256Oaep(){ - init(); - testEncryptDecryptWithRsa(KMType.SHA2_256, KMType.RSA_OAEP); - cleanUp(); - } - @Test - public void testWithRsaSha1Oaep(){ - init(); - testEncryptDecryptWithRsa(KMType.SHA1, KMType.RSA_OAEP); - cleanUp(); - } - - @Test - public void testWithRsaNonePkcs1(){ - init(); - testEncryptDecryptWithRsa(KMType.DIGEST_NONE, KMType.RSA_PKCS1_1_5_ENCRYPT); - cleanUp(); - } - - @Test - public void testWithRsaNoneNoPad(){ - init(); - testEncryptDecryptWithRsa(KMType.DIGEST_NONE, KMType.PADDING_NONE); - cleanUp(); - } - - // TODO Signing with no digest is not supported by crypto provider or javacard - @Test - public void testSignWithRsaNoneNoPad(){ - init(); - testSignVerifyWithRsa(KMType.DIGEST_NONE, KMType.PADDING_NONE,false, false); - cleanUp(); - } - - @Test - public void testSignWithRsaNonePkcs1(){ - init(); - testSignVerifyWithRsa(KMType.DIGEST_NONE, KMType.RSA_PKCS1_1_5_SIGN,false, false); - cleanUp(); - } - - @Test - public void testSignVerifyWithHmacSHA256WithUpdate(){ - init(); - testSignVerifyWithHmac(KMType.SHA2_256, true); - cleanUp(); - } - - @Test - public void testSignVerifyWithHmacSHA256(){ - init(); - testSignVerifyWithHmac(KMType.SHA2_256, false); - cleanUp(); - } - - @Test - public void testSignVerifyWithEcdsaSHA256WithUpdate(){ - init(); - testSignVerifyWithEcdsa(KMType.SHA2_256, true); - cleanUp(); - } - @Test - public void testSignVerifyWithEcdsaSHA256(){ - init(); - testSignVerifyWithEcdsa(KMType.SHA2_256, false); - cleanUp(); - } - @Test - public void testSignVerifyWithRsaSHA256Pkcs1(){ - init(); - testSignVerifyWithRsa(KMType.SHA2_256, KMType.RSA_PKCS1_1_5_SIGN,false, true); - cleanUp(); - } - @Test - public void testSignVerifyWithRsaSHA256Pss(){ - init(); - testSignVerifyWithRsa(KMType.SHA2_256, KMType.RSA_PSS,false, true); - cleanUp(); - } - - @Test - public void testSignVerifyWithRsaSHA256Pkcs1WithUpdate(){ - init(); - testSignVerifyWithRsa(KMType.SHA2_256, KMType.RSA_PKCS1_1_5_SIGN,true, true); - cleanUp(); - } - - @Test - public void testProvisionSuccess(){ - AID appletAID1 = AIDUtil.create("A000000062"); - simulator.installApplet(appletAID1, KMKeymasterApplet.class); - // Select applet - simulator.selectApplet(appletAID1); - // provision attest key - provisionCmd(simulator); - cleanUp(); - } - - @Test - public void testAttestRsaKey(){ - init(); - short key = generateRsaKey(null,null); - short keyBlobPtr = KMArray.cast(key).get((short)1); - byte[] keyBlob= new byte[KMByteBlob.cast(keyBlobPtr).length()]; - Util.arrayCopyNonAtomic( - KMByteBlob.cast(keyBlobPtr).getBuffer(), - KMByteBlob.cast(keyBlobPtr).getStartOff(), - keyBlob,(short)0, (short)keyBlob.length); - testAttestKey(keyBlob); - cleanUp(); - } - - @Test - public void testAttestEcKey(){ - init(); - short key = generateEcKey(null,null); - short keyBlobPtr = KMArray.cast(key).get((short)1); - byte[] keyBlob= new byte[KMByteBlob.cast(keyBlobPtr).length()]; - Util.arrayCopyNonAtomic( - KMByteBlob.cast(keyBlobPtr).getBuffer(), - KMByteBlob.cast(keyBlobPtr).getStartOff(), - keyBlob,(short)0, (short)keyBlob.length); - testAttestKey(keyBlob); - cleanUp(); - } - - public void testAttestKey(byte[] keyBlob){ - /* - short key = generateRsaKey(null,null); - short keyBlobPtr = KMArray.cast(key).get((short)1); - 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 arrPtr = KMArray.instance((short)2); - KMArray.cast(arrPtr).add((short)0, KMByteTag.instance(KMType.ATTESTATION_APPLICATION_ID, - KMByteBlob.instance(attAppId,(short)0,(short)attAppId.length))); - KMArray.cast(arrPtr).add((short)1, KMByteTag.instance(KMType.ATTESTATION_CHALLENGE, - KMByteBlob.instance(attChallenge,(short)0,(short)attChallenge.length))); - short keyParams = KMKeyParameters.instance(arrPtr); - short args = KMArray.instance((short)2); - KMArray.cast(args).add((short)0, KMByteBlob.instance(keyBlob,(short)0,(short)keyBlob.length)); - KMArray.cast(args).add((short)1, keyParams); - CommandAPDU apdu = encodeApdu((byte)0x14, args); - //print(apdu.getBytes(),(short)0,(short)apdu.getBytes().length); - ResponseAPDU response = simulator.transmitCommand(apdu); - short ret = KMArray.instance((short) 2); - short arrBlobs = KMArray.instance((short)1); - KMArray.cast(arrBlobs).add((short)0, KMByteBlob.exp()); - KMArray.cast(ret).add((short)0, KMInteger.exp()); - KMArray.cast(ret).add((short)1, arrBlobs); - byte[] respBuf = response.getBytes(); - short len = (short) respBuf.length; - //(respBuf,(short)0,(short)respBuf.length); - ret = decoder.decode(ret, respBuf, (short) 0, len); - short error = KMInteger.cast(KMArray.cast(ret).get((short)0)).getShort(); - Assert.assertEquals(error, KMError.OK); - arrBlobs = KMArray.cast(ret).get((short)1); - short cert = KMArray.cast(arrBlobs).get((short)0); - //printCert(KMByteBlob.cast(cert).getBuffer(),KMByteBlob.cast(cert).getStartOff(),KMByteBlob.cast(cert).length()); - } - - @Test - public void testUpgradeKey(){ - init(); - short ret = generateHmacKey(null, null); - short keyBlobPtr = KMArray.cast(ret).get((short)1); - 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 keyCharacteristics = KMArray.cast(ret).get((short)2); - short hwParams = KMKeyCharacteristics.cast(keyCharacteristics).getHardwareEnforced(); - short swParams = KMKeyCharacteristics.cast(keyCharacteristics).getSoftwareEnforced(); - short osVersion = KMKeyParameters.findTag(KMType.UINT_TAG,KMType.OS_VERSION,hwParams); - osVersion = KMIntegerTag.cast(osVersion).getValue(); - short osPatch = KMKeyParameters.findTag(KMType.UINT_TAG,KMType.OS_PATCH_LEVEL,hwParams); - osPatch = KMIntegerTag.cast(osPatch).getValue(); - Assert.assertEquals(KMInteger.cast(osVersion).getShort(), 1); - Assert.assertEquals(KMInteger.cast(osPatch).getShort(), 1); - setBootParams(simulator,(short) 2,(short)2); - ret = upgradeKey(KMByteBlob.instance(keyBlob, (short)0, (short)keyBlob.length),null, null); - keyBlobPtr = KMArray.cast(ret).get((short)1); - ret = getKeyCharacteristics(keyBlobPtr); - keyCharacteristics = KMArray.cast(ret).get((short)1); - hwParams = KMKeyCharacteristics.cast(keyCharacteristics).getHardwareEnforced(); - osVersion = KMKeyParameters.findTag(KMType.UINT_TAG,KMType.OS_VERSION,hwParams); - osVersion = KMIntegerTag.cast(osVersion).getValue(); - osPatch = KMKeyParameters.findTag(KMType.UINT_TAG,KMType.OS_PATCH_LEVEL,hwParams); - osPatch = KMIntegerTag.cast(osPatch).getValue(); - Assert.assertEquals(KMInteger.cast(osVersion).getShort(), 2); - Assert.assertEquals(KMInteger.cast(osPatch).getShort(), 2); - cleanUp(); - } - - @Test - public void testDestroyAttIds(){ - init(); - CommandAPDU commandAPDU = new CommandAPDU(0x80, 0x1A, 0x40, 0x00); - ResponseAPDU response = simulator.transmitCommand(commandAPDU); - byte[] respBuf = response.getBytes(); - Assert.assertEquals(respBuf[0], 0); - cleanUp(); - } - - private short upgradeKey(short keyBlobPtr, byte[] clientId, byte[] appData){ - short tagCount = 0; - short clientIdTag = 0; - short appDataTag = 0; - if(clientId != null) tagCount++; - if(appData != null) tagCount++; - short keyParams = KMArray.instance(tagCount); - short tagIndex=0; - if(clientId != null)KMArray.cast(keyBlobPtr).add(tagIndex++, - KMByteTag.instance(KMType.APPLICATION_ID, KMByteBlob.instance(clientId,(short)0,(short)clientId.length))); - if(appData != null)KMArray.cast(keyParams).add(tagIndex++, - KMByteTag.instance(KMType.APPLICATION_DATA, KMByteBlob.instance(appData,(short)0,(short)appData.length))); - keyParams = KMKeyParameters.instance(keyParams); - short arr = KMArray.instance((short)2); - KMArray.cast(arr).add((short)0,keyBlobPtr); - KMArray.cast(arr).add((short)1,keyParams); - CommandAPDU apdu = encodeApdu((byte)0x15, arr); - // print(commandAPDU.getBytes()); - ResponseAPDU response = simulator.transmitCommand(apdu); - short ret = KMArray.instance((short) 2); - KMArray.cast(ret).add((short) 0, KMInteger.exp()); - KMArray.cast(ret).add((short)1, KMByteBlob.exp()); - byte[] respBuf = response.getBytes(); - short len = (short) respBuf.length; - ret = decoder.decode(ret, respBuf, (short) 0, len); - short error = KMInteger.cast(KMArray.cast(ret).get((short)0)).getShort(); - Assert.assertEquals(error, KMError.OK); - return ret; - } - @Test - public void testSignVerifyWithRsaSHA256PssWithUpdate(){ - init(); - testSignVerifyWithRsa(KMType.SHA2_256, KMType.RSA_PSS,true, true); - cleanUp(); - } - @Test - public void testAbortOperation(){ - init(); - short aesDesKeyArr = generateAesDesKey(KMType.AES, (short)128,null, null, false);; - short keyBlobPtr = KMArray.cast(aesDesKeyArr).get((short)1); - byte[] keyBlob= new byte[KMByteBlob.cast(keyBlobPtr).length()]; - Util.arrayCopyNonAtomic(KMByteBlob.cast(keyBlobPtr).getBuffer(), KMByteBlob.cast(keyBlobPtr).getStartOff(), - keyBlob,(short)0, (short)keyBlob.length); - byte[] nonce = new byte[16]; - cryptoProvider.newRandomNumber(nonce,(short)0,(short)16); - short inParams = getAesDesParams(KMType.AES,KMType.ECB, KMType.PKCS7, nonce); - byte[] plainData= "Hello World 123!".getBytes(); - short ret = begin(KMType.ENCRYPT, KMByteBlob.instance(keyBlob,(short)0, (short)keyBlob.length), KMKeyParameters.instance(inParams), (short)0); - short opHandle = KMArray.cast(ret).get((short) 2); - opHandle = KMInteger.cast(opHandle).getShort(); - abort(KMInteger.uint_16(opHandle)); - short dataPtr = KMByteBlob.instance(plainData, (short) 0, (short) plainData.length); - ret = update(KMInteger.uint_16(opHandle), dataPtr, (short) 0, (short) 0, (short) 0); - Assert.assertEquals(KMError.INVALID_OPERATION_HANDLE,ret); - cleanUp(); - } - - public void testEncryptDecryptWithAesDes(byte alg, byte blockMode, byte padding, boolean update){ - short aesDesKeyArr; - boolean aesGcmFlag = false; - if(alg == KMType.AES){ - if(blockMode == KMType.GCM){ - aesDesKeyArr = generateAesGcmKey((short)128,null,null); - aesGcmFlag = true; - } else { - aesDesKeyArr = generateAesDesKey(alg, (short) 128, null, null, false); - } - } else{ - aesDesKeyArr = generateAesDesKey(alg, (short)168,null, null, false); - } - short keyBlobPtr = KMArray.cast(aesDesKeyArr).get((short)1); - byte[] keyBlob= new byte[KMByteBlob.cast(keyBlobPtr).length()]; - Util.arrayCopyNonAtomic(KMByteBlob.cast(keyBlobPtr).getBuffer(), KMByteBlob.cast(keyBlobPtr).getStartOff(), - keyBlob,(short)0, (short)keyBlob.length); - byte[] nonce = new byte[16]; - cryptoProvider.newRandomNumber(nonce,(short)0,(short)16); - short inParams = getAesDesParams(alg,blockMode, padding, nonce); - byte[] plainData= "Hello World 123!".getBytes(); - if(update) plainData= "Hello World 123! Hip Hip Hoorah!".getBytes(); - //Encrypt - short ret = processMessage(plainData, - KMByteBlob.instance(keyBlob,(short)0, (short)keyBlob.length), - KMType.ENCRYPT, - KMKeyParameters.instance(inParams), - (short)0,null,update, aesGcmFlag - ); - 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); - ret = processMessage(cipherData, - KMByteBlob.instance(keyBlob,(short)0, (short)keyBlob.length), - KMType.DECRYPT, - KMKeyParameters.instance(inParams), - (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); - } - - public void testEncryptDecryptWithRsa(byte digest, byte padding){ - short rsaKeyArr = generateRsaKey(null, null); - short keyBlobPtr = KMArray.cast(rsaKeyArr).get((short)1); - 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 = getRsaParams(digest, padding); - byte[] plainData = "Hello World 123!".getBytes(); - //Encrypt - short ret = processMessage(plainData, - KMByteBlob.instance(keyBlob,(short)0, (short)keyBlob.length), - KMType.ENCRYPT, - KMKeyParameters.instance(inParams), - (short)0,null,false, false - ); - inParams = getRsaParams(digest, padding); - keyBlobPtr = KMArray.cast(ret).get((short)2); - byte[] cipherData = new byte[KMByteBlob.cast(keyBlobPtr).length()]; - Util.arrayCopyNonAtomic(KMByteBlob.cast(keyBlobPtr).getBuffer(), KMByteBlob.cast(keyBlobPtr).getStartOff(), - cipherData,(short)0, (short)cipherData.length); - ret = processMessage(cipherData, - KMByteBlob.instance(keyBlob,(short)0, (short)keyBlob.length), - KMType.DECRYPT, - KMKeyParameters.instance(inParams), - (short)0,null,false,false - ); - keyBlobPtr = KMArray.cast(ret).get((short)2); - short len = KMByteBlob.cast(keyBlobPtr).length(); - short start = KMByteBlob.cast(keyBlobPtr).getStartOff(); - short equal = Util.arrayCompare(plainData,(short)0,KMByteBlob.cast(keyBlobPtr).getBuffer(), - (short)(start+len-plainData.length),(short)plainData.length); - Assert.assertTrue(equal == 0); - } - - public void testSignVerifyWithRsa(byte digest, byte padding, boolean update, boolean verifyFlag){ - short rsaKeyArr = generateRsaKey(null, null); - short keyBlobPtr = KMArray.cast(rsaKeyArr).get((short)1); - 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 = getRsaParams(digest, padding); - byte[] plainData = "Hello World 123!".getBytes(); - if(update) plainData= "Hello World 123! Hip Hip Hoorah!".getBytes(); - //Sign - short ret = processMessage(plainData, - KMByteBlob.instance(keyBlob,(short)0, (short)keyBlob.length), - KMType.SIGN, - KMKeyParameters.instance(inParams), - (short)0,null,update,false - ); - inParams = getRsaParams(digest, padding); - 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(), - signatureData,(short)0, (short)signatureData.length); - if(verifyFlag == false) { - Assert.assertEquals(signatureData.length,256); - return; - } - ret = processMessage(plainData, - KMByteBlob.instance(keyBlob,(short)0, (short)keyBlob.length), - KMType.VERIFY, - KMKeyParameters.instance(inParams), - (short)0,signatureData,update,false - ); - short error = KMInteger.cast(KMArray.cast(ret).get((short)0)).getShort(); - Assert.assertEquals(error, KMError.OK); - } - - public void testSignVerifyWithEcdsa(byte digest, boolean update){ - short ecKeyArr = generateEcKey(null, null); - short keyBlobPtr = KMArray.cast(ecKeyArr).get((short)1); - 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 = getEcParams(digest); - byte[] plainData = "Hello World 123!".getBytes(); - if(update) plainData= "Hello World 123! Hip Hip Hoorah!".getBytes(); - //Sign - short ret = processMessage(plainData, - KMByteBlob.instance(keyBlob,(short)0, (short)keyBlob.length), - KMType.SIGN, - KMKeyParameters.instance(inParams), - (short)0,null,update,false - ); - inParams = getEcParams(digest); - 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(), - signatureData,(short)0, (short)signatureData.length); - ret = processMessage(plainData, - KMByteBlob.instance(keyBlob,(short)0, (short)keyBlob.length), - KMType.VERIFY, - KMKeyParameters.instance(inParams), - (short)0,signatureData,update,false - ); - short error = KMInteger.cast(KMArray.cast(ret).get((short)0)).getShort(); - Assert.assertEquals(error, KMError.OK); - } - public void testSignVerifyWithHmac(byte digest, boolean update){ - short hmacKeyArr = generateHmacKey(null, null); - short keyBlobPtr = KMArray.cast(hmacKeyArr).get((short)1); - 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,true); - byte[] plainData = "Hello World 123!".getBytes(); - if(update) plainData= "Hello World 123! Hip Hip Hoorah!".getBytes(); - //Sign - short ret = processMessage(plainData, - KMByteBlob.instance(keyBlob,(short)0, (short)keyBlob.length), - KMType.SIGN, - KMKeyParameters.instance(inParams), - (short)0,null,update,false - ); - 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(), - signatureData,(short)0, (short)signatureData.length); - ret = processMessage(plainData, - KMByteBlob.instance(keyBlob,(short)0, (short)keyBlob.length), - KMType.VERIFY, - KMKeyParameters.instance(inParams), - (short)0,signatureData,update,false - ); - short error = KMInteger.cast(KMArray.cast(ret).get((short)0)).getShort(); - Assert.assertEquals(error, KMError.OK); - } - - private short getAesDesParams(byte alg, byte blockMode, byte padding, byte[] nonce) { - short inParams; - if(blockMode == KMType.GCM){ - inParams = KMArray.instance((short)5); - short byteBlob = KMByteBlob.instance((short)1); - KMByteBlob.cast(byteBlob).add((short)0, blockMode); - KMArray.cast(inParams).add((short)0, KMEnumArrayTag.instance(KMType.BLOCK_MODE, byteBlob)); - byteBlob = KMByteBlob.instance((short)1); - KMByteBlob.cast(byteBlob).add((short)0, padding); - KMArray.cast(inParams).add((short)1, KMEnumArrayTag.instance(KMType.PADDING, byteBlob)); - short nonceLen = 12; - byteBlob = KMByteBlob.instance(nonce,(short)0, nonceLen); - KMArray.cast(inParams).add((short)2, KMByteTag.instance(KMType.NONCE, byteBlob)); - short macLen = KMInteger.uint_16((short)128); - macLen = KMIntegerTag.instance(KMType.UINT_TAG,KMType.MAC_LENGTH,macLen); - KMArray.cast(inParams).add((short)3, macLen); - byte[] authData = "AuthData".getBytes(); - short associatedData = KMByteBlob.instance(authData,(short)0,(short)authData.length); - associatedData = KMByteTag.instance(KMType.ASSOCIATED_DATA,associatedData); - KMArray.cast(inParams).add((short)4, associatedData); - }else if(blockMode == KMType.ECB){ - inParams = KMArray.instance((short)2); - short byteBlob = KMByteBlob.instance((short)1); - KMByteBlob.cast(byteBlob).add((short)0, blockMode); - KMArray.cast(inParams).add((short)0, KMEnumArrayTag.instance(KMType.BLOCK_MODE, byteBlob)); - byteBlob = KMByteBlob.instance((short)1); - KMByteBlob.cast(byteBlob).add((short)0, padding); - KMArray.cast(inParams).add((short)1, KMEnumArrayTag.instance(KMType.PADDING, byteBlob)); - }else{ - inParams = KMArray.instance((short)3); - short byteBlob = KMByteBlob.instance((short)1); - KMByteBlob.cast(byteBlob).add((short)0, blockMode); - KMArray.cast(inParams).add((short)0, KMEnumArrayTag.instance(KMType.BLOCK_MODE, byteBlob)); - byteBlob = KMByteBlob.instance((short)1); - KMByteBlob.cast(byteBlob).add((short)0, padding); - KMArray.cast(inParams).add((short)1, KMEnumArrayTag.instance(KMType.PADDING, byteBlob)); - short nonceLen = 16; - if(alg == KMType.DES) nonceLen = 8; - byteBlob = KMByteBlob.instance(nonce,(short)0, nonceLen); - KMArray.cast(inParams).add((short)2, KMByteTag.instance(KMType.NONCE, byteBlob)); - } - return inParams; - } - - private short getRsaParams(byte digest, byte padding) { - short inParams = KMArray.instance((short)2); - short byteBlob = KMByteBlob.instance((short)1); - KMByteBlob.cast(byteBlob).add((short)0, digest); - KMArray.cast(inParams).add((short)0, KMEnumArrayTag.instance(KMType.DIGEST, byteBlob)); - byteBlob = KMByteBlob.instance((short)1); - KMByteBlob.cast(byteBlob).add((short)0, padding); - KMArray.cast(inParams).add((short)1, KMEnumArrayTag.instance(KMType.PADDING, byteBlob)); - return inParams; - } - - private short getEcParams(byte digest) { - short inParams = KMArray.instance((short)1); - short byteBlob = KMByteBlob.instance((short)1); - KMByteBlob.cast(byteBlob).add((short)0, digest); - KMArray.cast(inParams).add((short)0, KMEnumArrayTag.instance(KMType.DIGEST, byteBlob)); - return inParams; - } - 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*/160)); - if(sign) - KMArray.cast(inParams).add((short)1, macLength); - return inParams; - } - - public short processMessage( - byte[] data, - short keyBlob, - byte keyPurpose, - short inParams, - short hwToken, - byte[] signature, - boolean updateFlag, - boolean aesGcmFlag) { - short beginResp = begin(keyPurpose, keyBlob, inParams, hwToken); - short opHandle = KMArray.cast(beginResp).get((short) 2); - opHandle = KMInteger.cast(opHandle).getShort(); - short dataPtr = KMByteBlob.instance(data, (short) 0, (short) data.length); - short ret = KMType.INVALID_VALUE; - 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*/firstDataLen); - if(aesGcmFlag){ - byte[] authData = "AuthData".getBytes(); - short associatedData = KMByteBlob.instance(authData,(short)0,(short)authData.length); - associatedData = KMByteTag.instance(KMType.ASSOCIATED_DATA,associatedData); - inParams = KMArray.instance((short)1); - KMArray.cast(inParams).add((short)0, associatedData); - inParams = KMKeyParameters.instance(inParams); - } - ret = update(KMInteger.uint_16(opHandle), dataPtr, inParams, (short) 0, (short) 0); - dataPtr = KMArray.cast(ret).get((short) 3); - if (KMByteBlob.cast(dataPtr).length() > 0) { - Util.arrayCopyNonAtomic( - KMByteBlob.cast(dataPtr).getBuffer(), - KMByteBlob.cast(dataPtr).getStartOff(), - outputData, - (short) 0, - KMByteBlob.cast(dataPtr).length()); - len = KMByteBlob.cast(dataPtr).length(); - dataPtr = KMByteBlob.instance(data, len, (short) (data.length - len)); - }else{ - dataPtr = KMByteBlob.instance(data, (short)/*16*/firstDataLen, (short) (data.length - /*16*/firstDataLen)); - } - } - - if (keyPurpose == KMType.VERIFY) { - ret = finish(KMInteger.uint_16(opHandle), dataPtr, signature, (short) 0, (short) 0, (short) 0); - } else { - ret = finish(KMInteger.uint_16(opHandle), dataPtr, null, (short) 0, (short) 0, (short) 0); - } - if(len >0){ - dataPtr = KMArray.cast(ret).get((short)2); - if(KMByteBlob.cast(dataPtr).length() >0){ - Util.arrayCopyNonAtomic( - KMByteBlob.cast(dataPtr).getBuffer(), - KMByteBlob.cast(dataPtr).getStartOff(), - outputData, - len, - KMByteBlob.cast(dataPtr).length()); - len = (short)(len + KMByteBlob.cast(dataPtr).length()); - } - KMArray.cast(ret).add((short)2, KMByteBlob.instance(outputData,(short)0,len)); - } - return ret; - } - - public short begin(byte keyPurpose, short keyBlob, short keyParmas, short hwToken) { - short arrPtr = KMArray.instance((short)4); - KMArray.cast(arrPtr).add((short)0, KMEnum.instance(KMType.PURPOSE, keyPurpose)); - KMArray.cast(arrPtr).add((short)1, keyBlob); - KMArray.cast(arrPtr).add((short)2, keyParmas); - if(hwToken == 0) { - hwToken = KMHardwareAuthToken.instance(); - } - KMArray.cast(arrPtr).add((short)3, hwToken); - CommandAPDU apdu = encodeApdu((byte)0x1F, arrPtr); - //print(apdu.getBytes(),(short)0,(short)apdu.getBytes().length); - ResponseAPDU response = simulator.transmitCommand(apdu); - short ret = KMArray.instance((short) 3); - short outParams = KMKeyParameters.exp(); - KMArray.cast(ret).add((short)0, KMInteger.exp()); - KMArray.cast(ret).add((short)1, outParams); - KMArray.cast(ret).add((short)2, KMInteger.exp()); - byte[] respBuf = response.getBytes(); - short len = (short) respBuf.length; - if(len > 5){ - ret = decoder.decode(ret, respBuf, (short) 0, len); - short error = KMInteger.cast(KMArray.cast(ret).get((short)0)).getShort(); - Assert.assertEquals(error, KMError.OK); - return ret;}else{ - if(len == 3) return respBuf[0]; - if(len == 4) return respBuf[1]; - return Util.getShort(respBuf,(short)0); - } - } - - public short finish(short operationHandle, short data, byte[] signature, short inParams, short hwToken, short verToken) { - if(hwToken == 0) { - hwToken = KMHardwareAuthToken.instance(); - } - if(verToken == 0){ - verToken = KMVerificationToken.instance(); - } - short signatureTag; - if(signature == null){ - signatureTag = KMByteBlob.instance((short)0); - }else{ - signatureTag = KMByteBlob.instance(signature,(short)0,(short)signature.length); - } - if(inParams == 0){ - short arr = KMArray.instance((short)0); - inParams = KMKeyParameters.instance(arr); - } - short arrPtr = KMArray.instance((short)6); - KMArray.cast(arrPtr).add((short)0, operationHandle); - KMArray.cast(arrPtr).add((short)1, inParams); - KMArray.cast(arrPtr).add((short)2, data); - KMArray.cast(arrPtr).add((short)3, signatureTag); - KMArray.cast(arrPtr).add((short)4, hwToken); - KMArray.cast(arrPtr).add((short)5, verToken); - CommandAPDU apdu = encodeApdu((byte)0x21, arrPtr); - // print(commandAPDU.getBytes()); - ResponseAPDU response = simulator.transmitCommand(apdu); - short ret = KMArray.instance((short) 3); - short outParams = KMKeyParameters.exp(); - KMArray.cast(ret).add((short)0, KMInteger.exp()); - KMArray.cast(ret).add((short)1, outParams); - KMArray.cast(ret).add((short)2, KMByteBlob.exp()); - byte[] respBuf = response.getBytes(); - short len = (short) respBuf.length; - ret = decoder.decode(ret, respBuf, (short) 0, len); - short error = KMInteger.cast(KMArray.cast(ret).get((short)0)).getShort(); - Assert.assertEquals(error, KMError.OK); - return ret; - } - public short update(short operationHandle, short data, short inParams, short hwToken, short verToken) { - if(hwToken == 0) { - hwToken = KMHardwareAuthToken.instance(); - } - if(verToken == 0){ - verToken = KMVerificationToken.instance(); - } - if(inParams == 0){ - short arr = KMArray.instance((short)0); - inParams = KMKeyParameters.instance(arr); - } - short arrPtr = KMArray.instance((short)5); - KMArray.cast(arrPtr).add((short)0, operationHandle); - KMArray.cast(arrPtr).add((short)1, inParams); - KMArray.cast(arrPtr).add((short)2, data); - KMArray.cast(arrPtr).add((short)3, hwToken); - KMArray.cast(arrPtr).add((short)4, verToken); - CommandAPDU apdu = encodeApdu((byte)0x20, arrPtr); - // print(commandAPDU.getBytes()); - ResponseAPDU response = simulator.transmitCommand(apdu); - short ret = KMArray.instance((short) 4); - short outParams = KMKeyParameters.exp(); - KMArray.cast(ret).add((short)0, KMInteger.exp()); - KMArray.cast(ret).add((short)1, KMInteger.exp()); - KMArray.cast(ret).add((short)2, outParams); - KMArray.cast(ret).add((short)3, KMByteBlob.exp()); - byte[] respBuf = response.getBytes(); - short len = (short) respBuf.length; - if (len > 5) { - ret = decoder.decode(ret, respBuf, (short) 0, len); - short error = KMInteger.cast(KMArray.cast(ret).get((short) 0)).getShort(); - Assert.assertEquals(error, KMError.OK); - }else{ - ret = respBuf[1]; - } - return ret; - } - - private void print(short blob){ - print(KMByteBlob.cast(blob).getBuffer(),KMByteBlob.cast(blob).getStartOff(),KMByteBlob.cast(blob).length()); - } - private void print(byte[] buf, short start, short length){ - StringBuilder sb = new StringBuilder(); - for(int i = start; i < (start+length); i++){ - sb.append(String.format(" 0x%02X", buf[i])) ; - } - System.out.println(sb.toString()); - } - private void printCert(byte[] buf, short start, short length){ - StringBuilder sb = new StringBuilder(); - for(int i = start; i < (start+length); i++){ - sb.append(String.format("%02X", buf[i])) ; - } - System.out.println(sb.toString()); - } - - -/* - @Test - public void testApdu(){ - init(); - byte[] cmd = {(byte)0x80,0x11,0x40,0x00,0x00,0x00,0x4C,(byte)0x83,(byte)0xA5,0x1A,0x70,0x00,0x01,(byte)0xF7,0x01,0x1A,0x10, - 0x00,0x00,0x02,0x03,0x1A,0x30,0x00,0x00,0x03,0x19,0x01,0x00,0x1A,0x20,0x00,0x00,0x01,0x42,0x02, - 0x03,0x1A,0x20,0x00,0x00,0x05,0x41,0x04,0x03,0x58,0x24,(byte)0x82,0x58,0x20,0x73,0x7C,0x2E,(byte)0xCD, - 0x7B,(byte)0x8D,0x19,0x40,(byte)0xBF,0x29,0x30,(byte)0xAA,(byte)0x9B,0x4E, - (byte)0xD3,(byte)0xFF,(byte)0x94,0x1E,(byte)0xED,0x09,0x36,0x6B, - (byte)0xC0,0x32,(byte)0x99,(byte)0x98,0x64,(byte)0x81,(byte)0xF3,(byte)0xA4,(byte)0xD8,0x59,0x40}; - CommandAPDU cmdApdu = new CommandAPDU(cmd); - ResponseAPDU resp = simulator.transmitCommand(cmdApdu); - short ret = KMArray.instance((short) 3); - KMArray.cast(ret).add((short) 0, KMInteger.exp()); - KMArray.cast(ret).add((short)1, KMByteBlob.exp()); - short inst = KMKeyCharacteristics.exp(); - KMArray.cast(ret).add((short) 2, inst); - byte[] respBuf = resp.getBytes(); - short len = (short) respBuf.length; - ret = decoder.decode(ret, respBuf, (short) 0, len); - short error = KMInteger.cast(KMArray.cast(ret).get((short)0)).getShort(); - short keyBlobLength = KMByteBlob.cast(KMArray.cast(ret).get((short)1)).length(); - short blobArr = extractKeyBlobArray(KMArray.cast(ret).get((short)1)); - short keyCharacteristics = KMArray.cast(ret).get((short)2); - short hwParams = KMKeyCharacteristics.cast(keyCharacteristics).getHardwareEnforced(); - short swParams = KMKeyCharacteristics.cast(keyCharacteristics).getSoftwareEnforced(); - cleanUp(); - } - */ -} +/* + * 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.KMAttestationCert; +import com.android.javacard.keymaster.KMAttestationCertImpl; +import com.android.javacard.keymaster.KMBoolTag; +import com.android.javacard.keymaster.KMByteBlob; +import com.android.javacard.keymaster.KMByteTag; +import com.android.javacard.keymaster.KMJCardSimApplet; +import com.android.javacard.keymaster.KMJCardSimulator; +import com.android.javacard.keymaster.KMSEProvider; +import com.android.javacard.keymaster.KMDecoder; +import com.android.javacard.keymaster.KMEncoder; +import com.android.javacard.keymaster.KMEnum; +import com.android.javacard.keymaster.KMEnumArrayTag; +import com.android.javacard.keymaster.KMEnumTag; +import com.android.javacard.keymaster.KMError; +import com.android.javacard.keymaster.KMHardwareAuthToken; +import com.android.javacard.keymaster.KMHmacSharingParameters; +import com.android.javacard.keymaster.KMInteger; +import com.android.javacard.keymaster.KMIntegerTag; +import com.android.javacard.keymaster.KMKeyCharacteristics; +import com.android.javacard.keymaster.KMKeyParameters; +import com.android.javacard.keymaster.KMKeymasterApplet; +import com.android.javacard.keymaster.KMRepository; +import com.android.javacard.keymaster.KMType; +import com.android.javacard.keymaster.KMVerificationToken; +import com.licel.jcardsim.smartcardio.CardSimulator; +import com.licel.jcardsim.utils.AIDUtil; +import javacard.framework.AID; +import javacard.framework.Util; + +import java.security.spec.PKCS8EncodedKeySpec; + +import javax.smartcardio.CommandAPDU; +import javax.smartcardio.ResponseAPDU; +import org.junit.Assert; +import org.junit.Test; + +public class KMFunctionalTest { + private static final byte INS_BEGIN_KM_CMD = 0x00; + private static final byte INS_PROVISION_ATTESTATION_KEY_CMD = INS_BEGIN_KM_CMD + 1; //0x01 + private static final byte INS_PROVISION_ATTESTATION_CERT_CHAIN_CMD = INS_BEGIN_KM_CMD + 2; //0x02 + private static final byte INS_PROVISION_ATTESTATION_CERT_PARAMS_CMD = INS_BEGIN_KM_CMD + 3; //0x03 + private static final byte INS_PROVISION_ATTEST_IDS_CMD = INS_BEGIN_KM_CMD + 4; //0x04 + private static final byte INS_PROVISION_SHARED_SECRET_CMD = INS_BEGIN_KM_CMD + 5; //0x05 + private static final byte INS_SET_BOOT_PARAMS_CMD = INS_BEGIN_KM_CMD + 6; //0x06 + private static final byte INS_LOCK_PROVISIONING_CMD = INS_BEGIN_KM_CMD + 7; //0x07 + private static final byte INS_GET_PROVISION_STATUS_CMD = INS_BEGIN_KM_CMD + 8; //0x08 + // Top 32 commands are reserved for provisioning. + private static final byte INS_END_KM_PROVISION_CMD = 0x20; + + private static final byte INS_GENERATE_KEY_CMD = INS_END_KM_PROVISION_CMD + 1; //0x21 + private static final byte INS_IMPORT_KEY_CMD = INS_END_KM_PROVISION_CMD + 2; //0x22 + private static final byte INS_IMPORT_WRAPPED_KEY_CMD = INS_END_KM_PROVISION_CMD + 3; //0x23 + private static final byte INS_EXPORT_KEY_CMD = INS_END_KM_PROVISION_CMD + 4; //0x24 + private static final byte INS_ATTEST_KEY_CMD = INS_END_KM_PROVISION_CMD + 5; //0x25 + private static final byte INS_UPGRADE_KEY_CMD = INS_END_KM_PROVISION_CMD + 6; //0x26 + private static final byte INS_DELETE_KEY_CMD = INS_END_KM_PROVISION_CMD + 7; //0x27 + private static final byte INS_DELETE_ALL_KEYS_CMD = INS_END_KM_PROVISION_CMD + 8; //0x28 + private static final byte INS_ADD_RNG_ENTROPY_CMD = INS_END_KM_PROVISION_CMD + 9; //0x29 + private static final byte INS_COMPUTE_SHARED_HMAC_CMD = INS_END_KM_PROVISION_CMD + 10; //0x2A + private static final byte INS_DESTROY_ATT_IDS_CMD = INS_END_KM_PROVISION_CMD + 11; //0x2B + private static final byte INS_VERIFY_AUTHORIZATION_CMD = INS_END_KM_PROVISION_CMD + 12; //0x2C + private static final byte INS_GET_HMAC_SHARING_PARAM_CMD = INS_END_KM_PROVISION_CMD + 13; //0x2D + private static final byte INS_GET_KEY_CHARACTERISTICS_CMD = INS_END_KM_PROVISION_CMD + 14; //0x2E + private static final byte INS_GET_HW_INFO_CMD = INS_END_KM_PROVISION_CMD + 15; //0x2F + private static final byte INS_BEGIN_OPERATION_CMD = INS_END_KM_PROVISION_CMD + 16; //0x30 + private static final byte INS_UPDATE_OPERATION_CMD = INS_END_KM_PROVISION_CMD + 17; //0x31 + private static final byte INS_FINISH_OPERATION_CMD = INS_END_KM_PROVISION_CMD + 18; //0x32 + private static final byte INS_ABORT_OPERATION_CMD = INS_END_KM_PROVISION_CMD + 19; //0x33 + private static final byte INS_DEVICE_LOCKED_CMD = INS_END_KM_PROVISION_CMD + 20;//0x34 + private static final byte INS_EARLY_BOOT_ENDED_CMD = INS_END_KM_PROVISION_CMD + 21; //0x35 + private static final byte INS_GET_CERT_CHAIN_CMD = INS_END_KM_PROVISION_CMD + 22; //0x36 + + private static final byte[] kEcPrivKey = { + (byte) 0x21, (byte) 0xe0, (byte) 0x86, (byte) 0x43, (byte) 0x2a, + (byte) 0x15, (byte) 0x19, (byte) 0x84, (byte) 0x59, (byte) 0xcf, + (byte) 0x36, (byte) 0x3a, (byte) 0x50, (byte) 0xfc, (byte) 0x14, + (byte) 0xc9, (byte) 0xda, (byte) 0xad, (byte) 0xf9, (byte) 0x35, + (byte) 0xf5, (byte) 0x27, (byte) 0xc2, (byte) 0xdf, (byte) 0xd7, + (byte) 0x1e, (byte) 0x4d, (byte) 0x6d, (byte) 0xbc, (byte) 0x42, + (byte) 0xe5, (byte) 0x44 }; + private static final byte[] kEcPubKey = { + (byte) 0x04, (byte) 0xeb, (byte) 0x9e, (byte) 0x79, (byte) 0xf8, + (byte) 0x42, (byte) 0x63, (byte) 0x59, (byte) 0xac, (byte) 0xcb, + (byte) 0x2a, (byte) 0x91, (byte) 0x4c, (byte) 0x89, (byte) 0x86, + (byte) 0xcc, (byte) 0x70, (byte) 0xad, (byte) 0x90, (byte) 0x66, + (byte) 0x93, (byte) 0x82, (byte) 0xa9, (byte) 0x73, (byte) 0x26, + (byte) 0x13, (byte) 0xfe, (byte) 0xac, (byte) 0xcb, (byte) 0xf8, + (byte) 0x21, (byte) 0x27, (byte) 0x4c, (byte) 0x21, (byte) 0x74, + (byte) 0x97, (byte) 0x4a, (byte) 0x2a, (byte) 0xfe, (byte) 0xa5, + (byte) 0xb9, (byte) 0x4d, (byte) 0x7f, (byte) 0x66, (byte) 0xd4, + (byte) 0xe0, (byte) 0x65, (byte) 0x10, (byte) 0x66, (byte) 0x35, + (byte) 0xbc, (byte) 0x53, (byte) 0xb7, (byte) 0xa0, (byte) 0xa3, + (byte) 0xa6, (byte) 0x71, (byte) 0x58, (byte) 0x3e, (byte) 0xdb, + (byte) 0x3e, (byte) 0x11, (byte) 0xae, (byte) 0x10, (byte) 0x14 }; + + private static final byte[] kEcAttestCert = { + 0x30, (byte) 0x82, (byte) 0x02, (byte) 0x78, (byte) 0x30, (byte) 0x82, + (byte) 0x02, (byte) 0x1e, (byte) 0xa0, (byte) 0x03, (byte) 0x02, + (byte) 0x01, (byte) 0x02, (byte) 0x02, (byte) 0x02, (byte) 0x10, 0x01, + (byte) 0x30, (byte) 0x0a, (byte) 0x06, (byte) 0x08, (byte) 0x2a, + (byte) 0x86, (byte) 0x48, (byte) 0xce, (byte) 0x3d, (byte) 0x04, + (byte) 0x03, (byte) 0x02, (byte) 0x30, (byte) 0x81, (byte) 0x98, 0x31, + (byte) 0x0b, (byte) 0x30, (byte) 0x09, (byte) 0x06, (byte) 0x03, + (byte) 0x55, (byte) 0x04, (byte) 0x06, (byte) 0x13, (byte) 0x02, + (byte) 0x55, (byte) 0x53, (byte) 0x31, (byte) 0x13, (byte) 0x30, 0x11, + (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x08, + (byte) 0x0c, (byte) 0x0a, (byte) 0x43, (byte) 0x61, (byte) 0x6c, + (byte) 0x69, (byte) 0x66, (byte) 0x6f, (byte) 0x72, (byte) 0x6e, 0x69, + (byte) 0x61, (byte) 0x31, (byte) 0x16, (byte) 0x30, (byte) 0x14, + (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x07, + (byte) 0x0c, (byte) 0x0d, (byte) 0x4d, (byte) 0x6f, (byte) 0x75, 0x6e, + (byte) 0x74, (byte) 0x61, (byte) 0x69, (byte) 0x6e, (byte) 0x20, + (byte) 0x56, (byte) 0x69, (byte) 0x65, (byte) 0x77, (byte) 0x31, + (byte) 0x15, (byte) 0x30, (byte) 0x13, (byte) 0x06, (byte) 0x03, 0x55, + (byte) 0x04, (byte) 0x0a, (byte) 0x0c, (byte) 0x0c, (byte) 0x47, + (byte) 0x6f, (byte) 0x6f, (byte) 0x67, (byte) 0x6c, (byte) 0x65, + (byte) 0x2c, (byte) 0x20, (byte) 0x49, (byte) 0x6e, (byte) 0x63, 0x2e, + (byte) 0x31, (byte) 0x10, (byte) 0x30, (byte) 0x0e, (byte) 0x06, + (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x0b, (byte) 0x0c, + (byte) 0x07, (byte) 0x41, (byte) 0x6e, (byte) 0x64, (byte) 0x72, 0x6f, + (byte) 0x69, (byte) 0x64, (byte) 0x31, (byte) 0x33, (byte) 0x30, + (byte) 0x31, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04, + (byte) 0x03, (byte) 0x0c, (byte) 0x2a, (byte) 0x41, (byte) 0x6e, 0x64, + (byte) 0x72, (byte) 0x6f, (byte) 0x69, (byte) 0x64, (byte) 0x20, + (byte) 0x4b, (byte) 0x65, (byte) 0x79, (byte) 0x73, (byte) 0x74, + (byte) 0x6f, (byte) 0x72, (byte) 0x65, (byte) 0x20, (byte) 0x53, 0x6f, + (byte) 0x66, (byte) 0x74, (byte) 0x77, (byte) 0x61, (byte) 0x72, + (byte) 0x65, (byte) 0x20, (byte) 0x41, (byte) 0x74, (byte) 0x74, + (byte) 0x65, (byte) 0x73, (byte) 0x74, (byte) 0x61, (byte) 0x74, 0x69, + (byte) 0x6f, (byte) 0x6e, (byte) 0x20, (byte) 0x52, (byte) 0x6f, + (byte) 0x6f, (byte) 0x74, (byte) 0x30, (byte) 0x1e, (byte) 0x17, + (byte) 0x0d, (byte) 0x31, (byte) 0x36, (byte) 0x30, (byte) 0x31, 0x31, + (byte) 0x31, (byte) 0x30, (byte) 0x30, (byte) 0x34, (byte) 0x36, + (byte) 0x30, (byte) 0x39, (byte) 0x5a, (byte) 0x17, (byte) 0x0d, + (byte) 0x32, (byte) 0x36, (byte) 0x30, (byte) 0x31, (byte) 0x30, 0x38, + (byte) 0x30, (byte) 0x30, (byte) 0x34, (byte) 0x36, (byte) 0x30, + (byte) 0x39, (byte) 0x5a, (byte) 0x30, (byte) 0x81, (byte) 0x88, + (byte) 0x31, (byte) 0x0b, (byte) 0x30, (byte) 0x09, (byte) 0x06, 0x03, + (byte) 0x55, (byte) 0x04, (byte) 0x06, (byte) 0x13, (byte) 0x02, + (byte) 0x55, (byte) 0x53, (byte) 0x31, (byte) 0x13, (byte) 0x30, + (byte) 0x11, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04, 0x08, + (byte) 0x0c, (byte) 0x0a, (byte) 0x43, (byte) 0x61, (byte) 0x6c, + (byte) 0x69, (byte) 0x66, (byte) 0x6f, (byte) 0x72, (byte) 0x6e, + (byte) 0x69, (byte) 0x61, (byte) 0x31, (byte) 0x15, (byte) 0x30, 0x13, + (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x0a, + (byte) 0x0c, (byte) 0x0c, (byte) 0x47, (byte) 0x6f, (byte) 0x6f, + (byte) 0x67, (byte) 0x6c, (byte) 0x65, (byte) 0x2c, (byte) 0x20, 0x49, + (byte) 0x6e, (byte) 0x63, (byte) 0x2e, (byte) 0x31, (byte) 0x10, + (byte) 0x30, (byte) 0x0e, (byte) 0x06, (byte) 0x03, (byte) 0x55, + (byte) 0x04, (byte) 0x0b, (byte) 0x0c, (byte) 0x07, (byte) 0x41, 0x6e, + (byte) 0x64, (byte) 0x72, (byte) 0x6f, (byte) 0x69, (byte) 0x64, + (byte) 0x31, (byte) 0x3b, (byte) 0x30, (byte) 0x39, (byte) 0x06, + (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x03, (byte) 0x0c, 0x32, + (byte) 0x41, (byte) 0x6e, (byte) 0x64, (byte) 0x72, (byte) 0x6f, + (byte) 0x69, (byte) 0x64, (byte) 0x20, (byte) 0x4b, (byte) 0x65, + (byte) 0x79, (byte) 0x73, (byte) 0x74, (byte) 0x6f, (byte) 0x72, 0x65, + (byte) 0x20, (byte) 0x53, (byte) 0x6f, (byte) 0x66, (byte) 0x74, + (byte) 0x77, (byte) 0x61, (byte) 0x72, (byte) 0x65, (byte) 0x20, + (byte) 0x41, (byte) 0x74, (byte) 0x74, (byte) 0x65, (byte) 0x73, 0x74, + (byte) 0x61, (byte) 0x74, (byte) 0x69, (byte) 0x6f, (byte) 0x6e, + (byte) 0x20, (byte) 0x49, (byte) 0x6e, (byte) 0x74, (byte) 0x65, + (byte) 0x72, (byte) 0x6d, (byte) 0x65, (byte) 0x64, (byte) 0x69, 0x61, + (byte) 0x74, (byte) 0x65, (byte) 0x30, (byte) 0x59, (byte) 0x30, + (byte) 0x13, (byte) 0x06, (byte) 0x07, (byte) 0x2a, (byte) 0x86, + (byte) 0x48, (byte) 0xce, (byte) 0x3d, (byte) 0x02, (byte) 0x01, 0x06, + (byte) 0x08, (byte) 0x2a, (byte) 0x86, (byte) 0x48, (byte) 0xce, + (byte) 0x3d, (byte) 0x03, (byte) 0x01, (byte) 0x07, (byte) 0x03, + (byte) 0x42, (byte) 0x00, (byte) 0x04, (byte) 0xeb, (byte) 0x9e, 0x79, + (byte) 0xf8, (byte) 0x42, (byte) 0x63, (byte) 0x59, (byte) 0xac, + (byte) 0xcb, (byte) 0x2a, (byte) 0x91, (byte) 0x4c, (byte) 0x89, + (byte) 0x86, (byte) 0xcc, (byte) 0x70, (byte) 0xad, (byte) 0x90, 0x66, + (byte) 0x93, (byte) 0x82, (byte) 0xa9, (byte) 0x73, (byte) 0x26, + (byte) 0x13, (byte) 0xfe, (byte) 0xac, (byte) 0xcb, (byte) 0xf8, + (byte) 0x21, (byte) 0x27, (byte) 0x4c, (byte) 0x21, (byte) 0x74, + (byte) 0x97, (byte) 0x4a, (byte) 0x2a, (byte) 0xfe, (byte) 0xa5, + (byte) 0xb9, (byte) 0x4d, (byte) 0x7f, (byte) 0x66, (byte) 0xd4, + (byte) 0xe0, (byte) 0x65, (byte) 0x10, (byte) 0x66, (byte) 0x35, + (byte) 0xbc, 0x53, (byte) 0xb7, (byte) 0xa0, (byte) 0xa3, (byte) 0xa6, + (byte) 0x71, (byte) 0x58, (byte) 0x3e, (byte) 0xdb, (byte) 0x3e, + (byte) 0x11, (byte) 0xae, (byte) 0x10, (byte) 0x14, (byte) 0xa3, + (byte) 0x66, 0x30, (byte) 0x64, (byte) 0x30, (byte) 0x1d, (byte) 0x06, + (byte) 0x03, (byte) 0x55, (byte) 0x1d, (byte) 0x0e, (byte) 0x04, + (byte) 0x16, (byte) 0x04, (byte) 0x14, (byte) 0x3f, (byte) 0xfc, + (byte) 0xac, (byte) 0xd6, (byte) 0x1a, (byte) 0xb1, (byte) 0x3a, + (byte) 0x9e, (byte) 0x81, (byte) 0x20, (byte) 0xb8, (byte) 0xd5, + (byte) 0x25, (byte) 0x1c, (byte) 0xc5, (byte) 0x65, (byte) 0xbb, + (byte) 0x1e, (byte) 0x91, (byte) 0xa9, (byte) 0x30, (byte) 0x1f, + (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x1d, (byte) 0x23, + (byte) 0x04, (byte) 0x18, (byte) 0x30, (byte) 0x16, (byte) 0x80, + (byte) 0x14, (byte) 0xc8, (byte) 0xad, (byte) 0xe9, (byte) 0x77, + (byte) 0x4c, (byte) 0x45, (byte) 0xc3, (byte) 0xa3, (byte) 0xcf, + (byte) 0x0d, (byte) 0x16, (byte) 0x10, (byte) 0xe4, (byte) 0x79, + (byte) 0x43, (byte) 0x3a, (byte) 0x21, (byte) 0x5a, 0x30, (byte) 0xcf, + (byte) 0x30, (byte) 0x12, (byte) 0x06, (byte) 0x03, (byte) 0x55, + (byte) 0x1d, (byte) 0x13, (byte) 0x01, (byte) 0x01, (byte) 0xff, + (byte) 0x04, (byte) 0x08, (byte) 0x30, (byte) 0x06, 0x01, (byte) 0x01, + (byte) 0xff, (byte) 0x02, (byte) 0x01, (byte) 0x00, (byte) 0x30, + (byte) 0x0e, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x1d, + (byte) 0x0f, (byte) 0x01, (byte) 0x01, (byte) 0xff, 0x04, (byte) 0x04, + (byte) 0x03, (byte) 0x02, (byte) 0x02, (byte) 0x84, (byte) 0x30, + (byte) 0x0a, (byte) 0x06, (byte) 0x08, (byte) 0x2a, (byte) 0x86, + (byte) 0x48, (byte) 0xce, (byte) 0x3d, (byte) 0x04, 0x03, (byte) 0x02, + (byte) 0x03, (byte) 0x48, (byte) 0x00, (byte) 0x30, (byte) 0x45, + (byte) 0x02, (byte) 0x20, (byte) 0x4b, (byte) 0x8a, (byte) 0x9b, + (byte) 0x7b, (byte) 0xee, (byte) 0x82, (byte) 0xbc, (byte) 0xc0, + (byte) 0x33, (byte) 0x87, (byte) 0xae, (byte) 0x2f, (byte) 0xc0, + (byte) 0x89, (byte) 0x98, (byte) 0xb4, (byte) 0xdd, (byte) 0xc3, + (byte) 0x8d, (byte) 0xab, (byte) 0x27, (byte) 0x2a, (byte) 0x45, + (byte) 0x9f, (byte) 0x69, (byte) 0x0c, (byte) 0xc7, (byte) 0xc3, + (byte) 0x92, (byte) 0xd4, (byte) 0x0f, (byte) 0x8e, (byte) 0x02, + (byte) 0x21, (byte) 0x00, (byte) 0xee, (byte) 0xda, (byte) 0x01, + (byte) 0x5d, (byte) 0xb6, (byte) 0xf4, (byte) 0x32, (byte) 0xe9, + (byte) 0xd4, (byte) 0x84, (byte) 0x3b, (byte) 0x62, (byte) 0x4c, + (byte) 0x94, (byte) 0x04, (byte) 0xef, (byte) 0x3a, (byte) 0x7c, + (byte) 0xcc, (byte) 0xbd, 0x5e, (byte) 0xfb, (byte) 0x22, (byte) 0xbb, + (byte) 0xe7, (byte) 0xfe, (byte) 0xb9, (byte) 0x77, (byte) 0x3f, + (byte) 0x59, (byte) 0x3f, (byte) 0xfb, }; + + private static final byte[] kEcAttestRootCert = { + 0x30, (byte) 0x82, (byte) 0x02, (byte) 0x8b, (byte) 0x30, + (byte) 0x82, (byte) 0x02, (byte) 0x32, (byte) 0xa0, (byte) 0x03, + (byte) 0x02, (byte) 0x01, (byte) 0x02, (byte) 0x02, (byte) 0x09, + (byte) 0x00, (byte) 0xa2, (byte) 0x05, (byte) 0x9e, (byte) 0xd1, + (byte) 0x0e, (byte) 0x43, (byte) 0x5b, (byte) 0x57, (byte) 0x30, + (byte) 0x0a, (byte) 0x06, (byte) 0x08, (byte) 0x2a, (byte) 0x86, + (byte) 0x48, (byte) 0xce, 0x3d, (byte) 0x04, (byte) 0x03, + (byte) 0x02, (byte) 0x30, (byte) 0x81, (byte) 0x98, (byte) 0x31, + (byte) 0x0b, (byte) 0x30, (byte) 0x09, (byte) 0x06, (byte) 0x03, + (byte) 0x55, (byte) 0x04, (byte) 0x06, 0x13, (byte) 0x02, + (byte) 0x55, (byte) 0x53, (byte) 0x31, (byte) 0x13, (byte) 0x30, + (byte) 0x11, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04, + (byte) 0x08, (byte) 0x0c, (byte) 0x0a, (byte) 0x43, 0x61, + (byte) 0x6c, (byte) 0x69, (byte) 0x66, (byte) 0x6f, (byte) 0x72, + (byte) 0x6e, (byte) 0x69, (byte) 0x61, (byte) 0x31, (byte) 0x16, + (byte) 0x30, (byte) 0x14, (byte) 0x06, (byte) 0x03, (byte) 0x55, + 0x04, (byte) 0x07, (byte) 0x0c, (byte) 0x0d, (byte) 0x4d, + (byte) 0x6f, (byte) 0x75, (byte) 0x6e, (byte) 0x74, (byte) 0x61, + (byte) 0x69, (byte) 0x6e, (byte) 0x20, (byte) 0x56, (byte) 0x69, + (byte) 0x65, 0x77, (byte) 0x31, (byte) 0x15, (byte) 0x30, + (byte) 0x13, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04, + (byte) 0x0a, (byte) 0x0c, (byte) 0x0c, (byte) 0x47, (byte) 0x6f, + (byte) 0x6f, (byte) 0x67, 0x6c, (byte) 0x65, (byte) 0x2c, + (byte) 0x20, (byte) 0x49, (byte) 0x6e, (byte) 0x63, (byte) 0x2e, + (byte) 0x31, (byte) 0x10, (byte) 0x30, (byte) 0x0e, (byte) 0x06, + (byte) 0x03, (byte) 0x55, (byte) 0x04, 0x0b, (byte) 0x0c, + (byte) 0x07, (byte) 0x41, (byte) 0x6e, (byte) 0x64, (byte) 0x72, + (byte) 0x6f, (byte) 0x69, (byte) 0x64, (byte) 0x31, (byte) 0x33, + (byte) 0x30, (byte) 0x31, (byte) 0x06, (byte) 0x03, 0x55, + (byte) 0x04, (byte) 0x03, (byte) 0x0c, (byte) 0x2a, (byte) 0x41, + (byte) 0x6e, (byte) 0x64, (byte) 0x72, (byte) 0x6f, (byte) 0x69, + (byte) 0x64, (byte) 0x20, (byte) 0x4b, (byte) 0x65, (byte) 0x79, + 0x73, (byte) 0x74, (byte) 0x6f, (byte) 0x72, (byte) 0x65, + (byte) 0x20, (byte) 0x53, (byte) 0x6f, (byte) 0x66, (byte) 0x74, + (byte) 0x77, (byte) 0x61, (byte) 0x72, (byte) 0x65, (byte) 0x20, + (byte) 0x41, 0x74, (byte) 0x74, (byte) 0x65, (byte) 0x73, + (byte) 0x74, (byte) 0x61, (byte) 0x74, (byte) 0x69, (byte) 0x6f, + (byte) 0x6e, (byte) 0x20, (byte) 0x52, (byte) 0x6f, (byte) 0x6f, + (byte) 0x74, (byte) 0x30, 0x1e, (byte) 0x17, (byte) 0x0d, + (byte) 0x31, (byte) 0x36, (byte) 0x30, (byte) 0x31, (byte) 0x31, + (byte) 0x31, (byte) 0x30, (byte) 0x30, (byte) 0x34, (byte) 0x33, + (byte) 0x35, (byte) 0x30, (byte) 0x5a, 0x17, (byte) 0x0d, + (byte) 0x33, (byte) 0x36, (byte) 0x30, (byte) 0x31, (byte) 0x30, + (byte) 0x36, (byte) 0x30, (byte) 0x30, (byte) 0x34, (byte) 0x33, + (byte) 0x35, (byte) 0x30, (byte) 0x5a, (byte) 0x30, (byte) 0x81, + (byte) 0x98, (byte) 0x31, (byte) 0x0b, (byte) 0x30, (byte) 0x09, + (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x06, + (byte) 0x13, (byte) 0x02, (byte) 0x55, (byte) 0x53, (byte) 0x31, + 0x13, (byte) 0x30, (byte) 0x11, (byte) 0x06, (byte) 0x03, + (byte) 0x55, (byte) 0x04, (byte) 0x08, (byte) 0x0c, (byte) 0x0a, + (byte) 0x43, (byte) 0x61, (byte) 0x6c, (byte) 0x69, (byte) 0x66, + (byte) 0x6f, 0x72, (byte) 0x6e, (byte) 0x69, (byte) 0x61, + (byte) 0x31, (byte) 0x16, (byte) 0x30, (byte) 0x14, (byte) 0x06, + (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x07, (byte) 0x0c, + (byte) 0x0d, (byte) 0x4d, 0x6f, (byte) 0x75, (byte) 0x6e, + (byte) 0x74, (byte) 0x61, (byte) 0x69, (byte) 0x6e, (byte) 0x20, + (byte) 0x56, (byte) 0x69, (byte) 0x65, (byte) 0x77, (byte) 0x31, + (byte) 0x15, (byte) 0x30, (byte) 0x13, 0x06, (byte) 0x03, + (byte) 0x55, (byte) 0x04, (byte) 0x0a, (byte) 0x0c, (byte) 0x0c, + (byte) 0x47, (byte) 0x6f, (byte) 0x6f, (byte) 0x67, (byte) 0x6c, + (byte) 0x65, (byte) 0x2c, (byte) 0x20, (byte) 0x49, 0x6e, + (byte) 0x63, (byte) 0x2e, (byte) 0x31, (byte) 0x10, (byte) 0x30, + (byte) 0x0e, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04, + (byte) 0x0b, (byte) 0x0c, (byte) 0x07, (byte) 0x41, (byte) 0x6e, + 0x64, (byte) 0x72, (byte) 0x6f, (byte) 0x69, (byte) 0x64, + (byte) 0x31, (byte) 0x33, (byte) 0x30, (byte) 0x31, (byte) 0x06, + (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x03, (byte) 0x0c, + (byte) 0x2a, 0x41, (byte) 0x6e, (byte) 0x64, (byte) 0x72, + (byte) 0x6f, (byte) 0x69, (byte) 0x64, (byte) 0x20, (byte) 0x4b, + (byte) 0x65, (byte) 0x79, (byte) 0x73, (byte) 0x74, (byte) 0x6f, + (byte) 0x72, (byte) 0x65, 0x20, (byte) 0x53, (byte) 0x6f, + (byte) 0x66, (byte) 0x74, (byte) 0x77, (byte) 0x61, (byte) 0x72, + (byte) 0x65, (byte) 0x20, (byte) 0x41, (byte) 0x74, (byte) 0x74, + (byte) 0x65, (byte) 0x73, (byte) 0x74, 0x61, (byte) 0x74, + (byte) 0x69, (byte) 0x6f, (byte) 0x6e, 0x77, (byte) 0x1f, + (byte) 0x44, (byte) 0x22, (byte) 0x6d, (byte) 0xbd, (byte) 0xb1, + (byte) 0xaf, (byte) 0xfa, (byte) 0x16, (byte) 0xcb, (byte) 0xc7, + (byte) 0xad, (byte) 0xc5, (byte) 0x77, (byte) 0xd2, (byte) 0x20, + (byte) 0x52, (byte) 0x6f, (byte) 0x6f, (byte) 0x74, (byte) 0x30, + (byte) 0x59, (byte) 0x30, (byte) 0x13, (byte) 0x06, (byte) 0x07, + 0x2a, (byte) 0x86, (byte) 0x48, (byte) 0xce, (byte) 0x3d, + (byte) 0x02, (byte) 0x01, (byte) 0x06, (byte) 0x08, (byte) 0x2a, + (byte) 0x86, (byte) 0x48, (byte) 0xce, (byte) 0x3d, (byte) 0x03, + (byte) 0x01, 0x07, (byte) 0x03, (byte) 0x42, (byte) 0x00, + (byte) 0x04, (byte) 0xee, (byte) 0x5d, (byte) 0x5e, (byte) 0xc7, + (byte) 0xe1, (byte) 0xc0, (byte) 0xdb, (byte) 0x6d, (byte) 0x03, + (byte) 0xa6, (byte) 0x7e, (byte) 0xe6, (byte) 0xb6, (byte) 0x1b, + (byte) 0xec, (byte) 0x4d, (byte) 0x6a, (byte) 0x5d, (byte) 0x6a, + (byte) 0x68, (byte) 0x2e, (byte) 0x0f, (byte) 0xff, (byte) 0x7f, + (byte) 0x49, (byte) 0x0e, (byte) 0x7d, 0x56, (byte) 0x9c, + (byte) 0xaa, (byte) 0xb7, (byte) 0xb0, (byte) 0x2d, (byte) 0x54, + (byte) 0x01, (byte) 0x5d, (byte) 0x3e, (byte) 0x43, (byte) 0x2b, + (byte) 0x2a, (byte) 0x8e, (byte) 0xd7, (byte) 0x4e, (byte) 0xec, + (byte) 0x48, (byte) 0x75, (byte) 0x41, (byte) 0xa4, (byte) 0xa3, + (byte) 0x63, (byte) 0x30, (byte) 0x61, (byte) 0x30, (byte) 0x1d, + (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x1d, (byte) 0x0e, + 0x04, (byte) 0x16, (byte) 0x04, (byte) 0x14, (byte) 0xc8, + (byte) 0xad, (byte) 0xe9, (byte) 0x77, (byte) 0x4c, (byte) 0x45, + (byte) 0xc3, (byte) 0xa3, (byte) 0xcf, (byte) 0x0d, (byte) 0x16, + (byte) 0x10, (byte) 0xe4, (byte) 0x79, (byte) 0x43, (byte) 0x3a, + (byte) 0x21, (byte) 0x5a, (byte) 0x30, (byte) 0xcf, (byte) 0x30, + (byte) 0x1f, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x1d, + (byte) 0x23, (byte) 0x04, 0x18, (byte) 0x30, (byte) 0x16, + (byte) 0x80, (byte) 0x14, (byte) 0xc8, (byte) 0xad, (byte) 0xe9, + (byte) 0x77, (byte) 0x4c, (byte) 0x45, (byte) 0xc3, (byte) 0xa3, + (byte) 0xcf, (byte) 0x0d, (byte) 0x16, 0x10, (byte) 0xe4, + (byte) 0x79, (byte) 0x43, (byte) 0x3a, (byte) 0x21, (byte) 0x5a, + (byte) 0x30, (byte) 0xcf, (byte) 0x30, (byte) 0x0f, (byte) 0x06, + (byte) 0x03, (byte) 0x55, (byte) 0x1d, (byte) 0x13, 0x01, + (byte) 0x01, (byte) 0xff, (byte) 0x04, (byte) 0x05, (byte) 0x30, + (byte) 0x03, (byte) 0x01, (byte) 0x01, (byte) 0xff, (byte) 0x30, + (byte) 0x0e, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x1d, + 0x0f, (byte) 0x01, (byte) 0x01, (byte) 0xff, (byte) 0x04, + (byte) 0x04, (byte) 0x03, (byte) 0x02, (byte) 0x02, (byte) 0x84, + (byte) 0x30, (byte) 0x0a, (byte) 0x06, (byte) 0x08, (byte) 0x2a, + (byte) 0x86, 0x48, (byte) 0xce, (byte) 0x3d, (byte) 0x04, + (byte) 0x03, (byte) 0x02, (byte) 0x03, (byte) 0x47, (byte) 0x00, + (byte) 0x30, (byte) 0x44, (byte) 0x02, (byte) 0x20, (byte) 0x35, + (byte) 0x21, (byte) 0xa3, (byte) 0xef, (byte) 0x8b, (byte) 0x34, + (byte) 0x46, (byte) 0x1e, (byte) 0x9c, (byte) 0xd5, (byte) 0x60, + (byte) 0xf3, (byte) 0x1d, (byte) 0x58, (byte) 0x89, (byte) 0x20, + (byte) 0x6a, (byte) 0xdc, (byte) 0xa3, 0x65, (byte) 0x41, + (byte) 0xf6, (byte) 0x0d, (byte) 0x9e, (byte) 0xce, (byte) 0x8a, + (byte) 0x19, (byte) 0x8c, (byte) 0x66, (byte) 0x48, (byte) 0x60, + (byte) 0x7b, (byte) 0x02, (byte) 0x20, (byte) 0x4d, 0x0b, + (byte) 0xf3, (byte) 0x51, (byte) 0xd9, (byte) 0x30, (byte) 0x7c, + (byte) 0x7d, (byte) 0x5b, (byte) 0xda, (byte) 0x35, (byte) 0x34, + (byte) 0x1d, (byte) 0xa8, (byte) 0x47, (byte) 0x1b, (byte) 0x63, + (byte) 0xa5, (byte) 0x85, (byte) 0x65, (byte) 0x3c, (byte) 0xad, + (byte) 0x4f, (byte) 0x24, (byte) 0xa7, (byte) 0xe7, (byte) 0x4d, + (byte) 0xaf, (byte) 0x41, (byte) 0x7d, (byte) 0xf1, + (byte) 0xbf, }; + + private static final byte[] X509Issuer = { + (byte) 0x30, (byte) 0x81, (byte) 0x88, (byte) 0x31, (byte) 0x0b, + (byte) 0x30, (byte) 0x09, (byte) 0x06, (byte) 0x03, (byte) 0x55, + (byte) 0x04, (byte) 0x06, (byte) 0x13, (byte) 0x02, (byte) 0x55, + (byte) 0x53, (byte) 0x31, (byte) 0x13, (byte) 0x30, (byte) 0x11, + (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x08, + (byte) 0x0c, (byte) 0x0a, (byte) 0x43, (byte) 0x61, (byte) 0x6c, + (byte) 0x69, (byte) 0x66, (byte) 0x6f, (byte) 0x72, (byte) 0x6e, + (byte) 0x69, (byte) 0x61, (byte) 0x31, (byte) 0x15, (byte) 0x30, + (byte) 0x13, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04, + (byte) 0x0a, (byte) 0x0c, (byte) 0x0c, (byte) 0x47, (byte) 0x6f, + (byte) 0x6f, (byte) 0x67, (byte) 0x6c, (byte) 0x65, (byte) 0x2c, + (byte) 0x20, (byte) 0x49, (byte) 0x6e, (byte) 0x63, (byte) 0x2e, + (byte) 0x31, (byte) 0x10, (byte) 0x30, (byte) 0x0e, (byte) 0x06, + (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x0b, (byte) 0x0c, + (byte) 0x07, (byte) 0x41, (byte) 0x6e, (byte) 0x64, (byte) 0x72, + (byte) 0x6f, (byte) 0x69, (byte) 0x64, (byte) 0x31, (byte) 0x3b, + (byte) 0x30, (byte) 0x39, (byte) 0x06, (byte) 0x03, (byte) 0x55, + (byte) 0x04, (byte) 0x03, (byte) 0x0c, (byte) 0x32, (byte) 0x41, + (byte) 0x6e, (byte) 0x64, (byte) 0x72, (byte) 0x6f, (byte) 0x69, + (byte) 0x64, (byte) 0x20, (byte) 0x4b, (byte) 0x65, (byte) 0x79, + (byte) 0x73, (byte) 0x74, (byte) 0x6f, (byte) 0x72, (byte) 0x65, + (byte) 0x20, (byte) 0x53, (byte) 0x6f, (byte) 0x66, (byte) 0x74, + (byte) 0x77, (byte) 0x61, (byte) 0x72, (byte) 0x65, (byte) 0x20, + (byte) 0x41, (byte) 0x74, (byte) 0x74, (byte) 0x65, (byte) 0x73, + (byte) 0x74, (byte) 0x61, (byte) 0x74, (byte) 0x69, (byte) 0x6f, + (byte) 0x6e, (byte) 0x20, (byte) 0x49, (byte) 0x6e, (byte) 0x74, + (byte) 0x65, (byte) 0x72, (byte) 0x6d, (byte) 0x65, (byte) 0x64, + (byte) 0x69, (byte) 0x61, (byte) 0x74, (byte) 0x65 }; + // 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 = {(byte)0x32, (byte)0x36, (byte)0x30, (byte)0x31, (byte)0x30, (byte)0x38, (byte)0x30, (byte)0x30, (byte)0x34, (byte)0x36, (byte)0x30, (byte)0x39, (byte)0x5a}; + private static final byte[] authKeyId = { (byte)0x80, (byte)0x14, (byte)0xc8, (byte)0xad, (byte)0xe9, (byte)0x77, (byte)0x4c, (byte)0x45, (byte)0xc3, (byte)0xa3, (byte)0xcf, (byte)0x0d, (byte)0x16, (byte)0x10, (byte)0xe4, (byte)0x79, (byte)0x43, (byte)0x3a, (byte)0x21, (byte)0x5a, (byte)0x30, (byte)0xcf}; + + private CardSimulator simulator; + private KMEncoder encoder; + private KMDecoder decoder; + private KMSEProvider cryptoProvider; + + public KMFunctionalTest(){ + cryptoProvider = new KMJCardSimulator(); + simulator = new CardSimulator(); + encoder = new KMEncoder(); + decoder = new KMDecoder(); + } + + private void init(){ + // Create simulator + AID appletAID = AIDUtil.create("A000000062"); + simulator.installApplet(appletAID, KMJCardSimApplet.class); + // Select applet + simulator.selectApplet(appletAID); + // provision attest key + provisionCmd(simulator); + } + + private void setBootParams(CardSimulator simulator, short osVersion, + short osPatchLevel, short vendorPatchLevel, short bootPatchLevel) { + // Argument 1 OS Version + short versionPtr = KMInteger.uint_16(osVersion); + // short versionTagPtr = KMIntegerTag.instance(KMType.UINT_TAG, + // KMType.OS_VERSION,versionPatchPtr); + // Argument 2 OS Patch level + short patchPtr = KMInteger.uint_16(osPatchLevel); + short vendorpatchPtr = KMInteger.uint_16((short) vendorPatchLevel); + short bootpatchPtr = KMInteger.uint_16((short) bootPatchLevel); + // 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) 8); + KMArray vals = KMArray.cast(arrPtr); + vals.add((short) 0, versionPtr); + vals.add((short) 1, patchPtr); + vals.add((short) 2, vendorpatchPtr); + vals.add((short) 3, bootpatchPtr); + vals.add((short) 4, bootKeyPtr); + vals.add((short) 5, bootHashPtr); + vals.add((short) 6, bootStatePtr); + vals.add((short) 7, deviceLockedPtr); + CommandAPDU apdu = encodeApdu((byte) INS_SET_BOOT_PARAMS_CMD, arrPtr); + // print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(apdu); + Assert.assertEquals(0x9000, response.getSW()); + + } + + private void provisionSigningCertificate(CardSimulator simulator) { + short byteBlobPtr = KMByteBlob.instance( + (short) (kEcAttestCert.length + kEcAttestRootCert.length)); + Util.arrayCopyNonAtomic(kEcAttestCert, (short) 0, + KMByteBlob.cast(byteBlobPtr).getBuffer(), + KMByteBlob.cast(byteBlobPtr).getStartOff(), + (short) kEcAttestCert.length); + Util.arrayCopyNonAtomic(kEcAttestRootCert, (short) 0, + KMByteBlob.cast(byteBlobPtr).getBuffer(), + (short) (KMByteBlob.cast(byteBlobPtr).getStartOff() + + kEcAttestCert.length), + (short) kEcAttestRootCert.length); + CommandAPDU apdu = encodeApdu( + (byte) INS_PROVISION_ATTESTATION_CERT_CHAIN_CMD, byteBlobPtr); + // print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(apdu); + Assert.assertEquals(0x9000, response.getSW()); + } + + private void provisionSigningKey(CardSimulator simulator) { + // KeyParameters. + short arrPtr = KMArray.instance((short) 4); + short ecCurve = KMEnumTag.instance(KMType.ECCURVE, KMType.P_256); + short byteBlob = KMByteBlob.instance((short) 1); + KMByteBlob.cast(byteBlob).add((short) 0, KMType.SHA2_256); + short digest = KMEnumArrayTag.instance(KMType.DIGEST, byteBlob); + 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, ecCurve); + KMArray.cast(arrPtr).add((short) 1, digest); + KMArray.cast(arrPtr).add((short) 2, + KMEnumTag.instance(KMType.ALGORITHM, KMType.EC)); + KMArray.cast(arrPtr).add((short) 3, purpose); + short keyParams = KMKeyParameters.instance(arrPtr); + // Note: VTS uses PKCS8 KeyFormat RAW + short keyFormatPtr = KMEnum.instance(KMType.KEY_FORMAT, KMType.RAW); + + // Key + short signKeyPtr = KMArray.instance((short) 2); + KMArray.cast(signKeyPtr).add((short) 0, KMByteBlob.instance(kEcPrivKey, + (short) 0, (short) kEcPrivKey.length)); + KMArray.cast(signKeyPtr).add((short) 1, KMByteBlob.instance(kEcPubKey, + (short) 0, (short) kEcPubKey.length)); + byte[] keyBuf = new byte[120]; + short len = encoder.encode(signKeyPtr, keyBuf, (short) 0); + short signKeyBstr = KMByteBlob.instance(keyBuf, (short) 0, len); + + short finalArrayPtr = KMArray.instance((short) 3); + KMArray.cast(finalArrayPtr).add((short) 0, keyParams); + KMArray.cast(finalArrayPtr).add((short) 1, keyFormatPtr); + KMArray.cast(finalArrayPtr).add((short) 2, signKeyBstr); + + CommandAPDU apdu = encodeApdu((byte) INS_PROVISION_ATTESTATION_KEY_CMD, + finalArrayPtr); + // print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(apdu); + Assert.assertEquals(0x9000, response.getSW()); + } + + private void provisionCertificateParams(CardSimulator simulator) { + + short arrPtr = KMArray.instance((short) 3); + short byteBlob1 = KMByteBlob.instance(X509Issuer, (short) 0, + (short) X509Issuer.length); + KMArray.cast(arrPtr).add((short) 0, byteBlob1); + short byteBlob2 = KMByteBlob.instance(expiryTime, (short) 0, + (short) expiryTime.length); + KMArray.cast(arrPtr).add((short) 1, byteBlob2); + short byteBlob3 = KMByteBlob.instance(authKeyId, (short) 0, + (short) authKeyId.length); + KMArray.cast(arrPtr).add((short) 2, byteBlob3); + + CommandAPDU apdu = encodeApdu( + (byte) INS_PROVISION_ATTESTATION_CERT_PARAMS_CMD, arrPtr); + // print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(apdu); + Assert.assertEquals(0x9000, response.getSW()); + } + + private void provisionSharedSecret(CardSimulator simulator) { + 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 }; + short arrPtr = KMArray.instance((short) 1); + short byteBlob = KMByteBlob.instance(sharedKeySecret, (short) 0, + (short) sharedKeySecret.length); + KMArray.cast(arrPtr).add((short) 0, byteBlob); + + CommandAPDU apdu = encodeApdu((byte) INS_PROVISION_SHARED_SECRET_CMD, + arrPtr); + // print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(apdu); + Assert.assertEquals(0x9000, response.getSW()); + } + + private void provisionAttestIds(CardSimulator simulator) { + short arrPtr = KMArray.instance((short) 8); + + byte[] buf = "Attestation Id".getBytes(); + + KMArray.cast(arrPtr).add((short) 0, + KMByteTag.instance(KMType.ATTESTATION_ID_BRAND, + KMByteBlob.instance(buf, (short) 0, (short) buf.length))); + KMArray.cast(arrPtr).add((short) 1, + KMByteTag.instance(KMType.ATTESTATION_ID_PRODUCT, + KMByteBlob.instance(buf, (short) 0, (short) buf.length))); + KMArray.cast(arrPtr).add((short) 2, + KMByteTag.instance(KMType.ATTESTATION_ID_DEVICE, + KMByteBlob.instance(buf, (short) 0, (short) buf.length))); + KMArray.cast(arrPtr).add((short) 3, + KMByteTag.instance(KMType.ATTESTATION_ID_MODEL, + KMByteBlob.instance(buf, (short) 0, (short) buf.length))); + KMArray.cast(arrPtr).add((short) 4, + KMByteTag.instance(KMType.ATTESTATION_ID_IMEI, + KMByteBlob.instance(buf, (short) 0, (short) buf.length))); + KMArray.cast(arrPtr).add((short) 5, + KMByteTag.instance(KMType.ATTESTATION_ID_MEID, + KMByteBlob.instance(buf, (short) 0, (short) buf.length))); + KMArray.cast(arrPtr).add((short) 6, + KMByteTag.instance(KMType.ATTESTATION_ID_MANUFACTURER, + KMByteBlob.instance(buf, (short) 0, (short) buf.length))); + KMArray.cast(arrPtr).add((short) 7, + KMByteTag.instance(KMType.ATTESTATION_ID_SERIAL, + KMByteBlob.instance(buf, (short) 0, (short) buf.length))); + short keyParams = KMKeyParameters.instance(arrPtr); + short outerArrPtr = KMArray.instance((short) 1); + KMArray.cast(outerArrPtr).add((short) 0, keyParams); + CommandAPDU apdu = encodeApdu((byte) INS_PROVISION_ATTEST_IDS_CMD, + outerArrPtr); + // print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(apdu); + Assert.assertEquals(0x9000, response.getSW()); + } + + private void provisionLocked(CardSimulator simulator) { + CommandAPDU commandAPDU = new CommandAPDU(0x80, INS_LOCK_PROVISIONING_CMD, + 0x40, 0x00); + // print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(commandAPDU); + Assert.assertEquals(0x9000, response.getSW()); + } + + private void provisionCmd(CardSimulator simulator) { + provisionSigningKey(simulator); + provisionSigningCertificate(simulator); + provisionCertificateParams(simulator); + provisionSharedSecret(simulator); + provisionAttestIds(simulator); + // set bootup parameters + setBootParams(simulator,(short)1,(short)1, (short)0, (short)0); + provisionLocked(simulator); + } + + private void cleanUp(){ + AID appletAID = AIDUtil.create("A000000062"); + // Delete i.e. uninstall applet + simulator.deleteApplet(appletAID); + } + + + private CommandAPDU encodeApdu(byte ins, short cmd){ + byte[] buf = new byte[2500]; + buf[0] = (byte)0x80; + buf[1] = ins; + buf[2] = (byte)0x40; + buf[3] = (byte)0x00; + buf[4] = 0; + short len = encoder.encode(cmd, buf, (short) 7); + Util.setShort(buf, (short)5, len); + byte[] apdu = new byte[7+len]; + Util.arrayCopyNonAtomic(buf,(short)0,apdu,(short)0,(short)(7+len)); + //CommandAPDU commandAPDU = new CommandAPDU(0x80, 0x10, 0x40, 0x00, buf, 0, actualLen); + return new CommandAPDU(apdu); + } + + @Test + public void testAesImportKeySuccess() { + init(); + byte[] aesKeySecret = new byte[] {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + short arrPtr = KMArray.instance((short)5); + short boolTag = KMBoolTag.instance(KMType.NO_AUTH_REQUIRED); + short keySize = KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, KMInteger.uint_16((short)128)); + short byteBlob = KMByteBlob.instance((short)1); + KMByteBlob.cast(byteBlob).add((short)0, KMType.ECB); + short blockMode = KMEnumArrayTag.instance(KMType.BLOCK_MODE, byteBlob); + byteBlob = KMByteBlob.instance((short)1); + KMByteBlob.cast(byteBlob).add((short)0, KMType.PKCS7); + short paddingMode = KMEnumArrayTag.instance(KMType.PADDING, byteBlob); + KMArray.cast(arrPtr).add((short)0, boolTag); + KMArray.cast(arrPtr).add((short)1, keySize); + KMArray.cast(arrPtr).add((short)2, blockMode); + KMArray.cast(arrPtr).add((short)3, paddingMode); + KMArray.cast(arrPtr).add((short)4, KMEnumTag.instance(KMType.ALGORITHM, KMType.AES)); + short keyParams = KMKeyParameters.instance(arrPtr); + short keyFormatPtr = KMEnum.instance(KMType.KEY_FORMAT, KMType.RAW); + short keyBlob = KMArray.instance((short)1); + KMArray.cast(keyBlob).add((short)0, KMByteBlob.instance(aesKeySecret,(short)0,(short)16)); + byte[] blob = new byte[256]; + short len = encoder.encode(keyBlob,blob,(short)0); + keyBlob = KMByteBlob.instance(blob, (short)0, len); + arrPtr = KMArray.instance((short)3); + KMArray arg = KMArray.cast(arrPtr); + arg.add((short) 0, keyParams); + arg.add((short)1, keyFormatPtr); + arg.add((short)2, keyBlob); + CommandAPDU apdu = encodeApdu((byte)INS_IMPORT_KEY_CMD, arrPtr); + // print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(apdu); + short ret = KMArray.instance((short) 3); + KMArray.cast(ret).add((short) 0, KMInteger.exp()); + KMArray.cast(ret).add((short)1, KMByteBlob.exp()); + short inst = KMKeyCharacteristics.exp(); + KMArray.cast(ret).add((short) 2, inst); + byte[] respBuf = response.getBytes(); + len = (short) respBuf.length; + ret = decoder.decode(ret, respBuf, (short) 0, len); + short error = KMInteger.cast(KMArray.cast(ret).get((short)0)).getShort(); + short keyBlobLength = KMByteBlob.cast(KMArray.cast(ret).get((short)1)).length(); + short keyCharacteristics = KMArray.cast(ret).get((short)2); + short hwParams = KMKeyCharacteristics.cast(keyCharacteristics).getHardwareEnforced(); + short swParams = KMKeyCharacteristics.cast(keyCharacteristics).getSoftwareEnforced(); + Assert.assertEquals(0x9000, response.getSW()); + Assert.assertEquals(error, KMError.OK); + short tag = KMKeyParameters.findTag(KMType.BOOL_TAG, KMType.NO_AUTH_REQUIRED, hwParams); + Assert.assertEquals(KMBoolTag.cast(tag).getVal(),0x01); + tag = KMKeyParameters.findTag(KMType.UINT_TAG, KMType.KEYSIZE, hwParams); + Assert.assertEquals(KMInteger.cast(KMIntegerTag.cast(tag).getValue()).getShort(), 128); + tag = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.PADDING, hwParams); + Assert.assertTrue(KMEnumArrayTag.cast(tag).contains(KMType.PKCS7)); + tag = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.BLOCK_MODE, hwParams); + Assert.assertTrue(KMEnumArrayTag.cast(tag).contains(KMType.ECB)); + tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ALGORITHM, hwParams); + Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.AES); + tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ORIGIN, hwParams); + Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.IMPORTED); + cleanUp(); + } + + @Test + public void testHmacImportKeySuccess() { + init(); + byte[] hmacKeySecret = new byte[] {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + short arrPtr = KMArray.instance((short)5); + short boolTag = KMBoolTag.instance(KMType.NO_AUTH_REQUIRED); + short keySize = KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, KMInteger.uint_16((short)128)); + short byteBlob = KMByteBlob.instance((short)1); + KMByteBlob.cast(byteBlob).add((short)0, KMType.SHA2_256); + short digest = KMEnumArrayTag.instance(KMType.DIGEST, byteBlob); + short minMacLength = KMIntegerTag.instance(KMType.UINT_TAG,KMType.MIN_MAC_LENGTH, KMInteger.uint_16((short)256)); + KMArray.cast(arrPtr).add((short)0, boolTag); + KMArray.cast(arrPtr).add((short)1, keySize); + KMArray.cast(arrPtr).add((short)2, digest); + KMArray.cast(arrPtr).add((short)3, minMacLength); + KMArray.cast(arrPtr).add((short)4, KMEnumTag.instance(KMType.ALGORITHM, KMType.HMAC)); + short keyParams = KMKeyParameters.instance(arrPtr); + short keyFormatPtr = KMEnum.instance(KMType.KEY_FORMAT, KMType.RAW); + short keyBlob = KMArray.instance((short)1); + KMArray.cast(keyBlob).add((short)0, KMByteBlob.instance(hmacKeySecret,(short)0,(short)16)); + byte[] blob = new byte[256]; + short len = encoder.encode(keyBlob,blob,(short)0); + keyBlob = KMByteBlob.instance(blob, (short)0, len); + arrPtr = KMArray.instance((short)3); + KMArray arg = KMArray.cast(arrPtr); + arg.add((short) 0, keyParams); + arg.add((short)1, keyFormatPtr); + arg.add((short)2, keyBlob); + CommandAPDU apdu = encodeApdu((byte)INS_IMPORT_KEY_CMD, arrPtr); + // print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(apdu); + short ret = KMArray.instance((short) 3); + KMArray.cast(ret).add((short) 0, KMInteger.exp()); + KMArray.cast(ret).add((short)1, KMByteBlob.exp()); + short inst = KMKeyCharacteristics.exp(); + KMArray.cast(ret).add((short) 2, inst); + byte[] respBuf = response.getBytes(); + len = (short) respBuf.length; + ret = decoder.decode(ret, respBuf, (short) 0, len); + short error = KMInteger.cast(KMArray.cast(ret).get((short)0)).getShort(); + short keyBlobLength = KMByteBlob.cast(KMArray.cast(ret).get((short)1)).length(); + short keyCharacteristics = KMArray.cast(ret).get((short)2); + short hwParams = KMKeyCharacteristics.cast(keyCharacteristics).getHardwareEnforced(); + short swParams = KMKeyCharacteristics.cast(keyCharacteristics).getSoftwareEnforced(); + Assert.assertEquals(0x9000, response.getSW()); + Assert.assertEquals(error, KMError.OK); + short tag = KMKeyParameters.findTag(KMType.BOOL_TAG, KMType.NO_AUTH_REQUIRED, hwParams); + Assert.assertEquals(KMBoolTag.cast(tag).getVal(),0x01); + tag = KMKeyParameters.findTag(KMType.UINT_TAG, KMType.KEYSIZE, hwParams); + Assert.assertEquals(KMInteger.cast(KMIntegerTag.cast(tag).getValue()).getShort(), 128); + tag = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.DIGEST, hwParams); + Assert.assertTrue(KMEnumArrayTag.cast(tag).contains(KMType.SHA2_256)); + tag = KMKeyParameters.findTag(KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, hwParams); + Assert.assertEquals(KMInteger.cast(KMIntegerTag.cast(tag).getValue()).getShort(), 256); + tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ALGORITHM, hwParams); + Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.HMAC); + tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ORIGIN, hwParams); + Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.IMPORTED); + cleanUp(); + } + + @Test + public void testRsaImportKeySuccess() { + init(); + /* + KeyPair rsaKeyPair = cryptoProvider.createRsaKeyPair(); + byte[] pub = new byte[4]; + short len = ((RSAPublicKey)rsaKeyPair.getPublic()).getExponent(pub,(short)1); + byte[] priv = new byte[256]; + byte[] mod = new byte[256]; + len = ((RSAPrivateKey)rsaKeyPair.getPrivate()).getModulus(mod,(short)0); + len = ((RSAPrivateKey)rsaKeyPair.getPrivate()).getExponent(priv,(short)0); + */ + + 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)); + byteBlob = KMByteBlob.instance((short)1); + KMByteBlob.cast(byteBlob).add((short)0, KMType.RSA_PSS); + short padding = KMEnumArrayTag.instance(KMType.PADDING, byteBlob); + KMArray.cast(arrPtr).add((short)0, boolTag); + KMArray.cast(arrPtr).add((short)1, keySize); + KMArray.cast(arrPtr).add((short)2, digest); + KMArray.cast(arrPtr).add((short)3, rsaPubExpTag); + KMArray.cast(arrPtr).add((short)4, KMEnumTag.instance(KMType.ALGORITHM, KMType.RSA)); + KMArray.cast(arrPtr).add((short)5, padding); + short keyParams = KMKeyParameters.instance(arrPtr); + short keyFormatPtr = KMEnum.instance(KMType.KEY_FORMAT, KMType.RAW);// Note: VTS uses PKCS8 + short keyBlob = KMArray.instance((short)2); + KMArray.cast(keyBlob).add((short)0, KMByteBlob.instance(priv,(short)0,(short)256)); + KMArray.cast(keyBlob).add((short)1, KMByteBlob.instance(mod,(short)0,(short)256)); + byte[] blob = new byte[620]; + short len = encoder.encode(keyBlob,blob,(short)0); + keyBlob = KMByteBlob.instance(blob, (short)0, len); + arrPtr = KMArray.instance((short)3); + KMArray arg = KMArray.cast(arrPtr); + arg.add((short) 0, keyParams); + arg.add((short)1, keyFormatPtr); + arg.add((short)2, keyBlob); + CommandAPDU apdu = encodeApdu((byte)INS_IMPORT_KEY_CMD, arrPtr); + // print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(apdu); + short ret = KMArray.instance((short) 3); + KMArray.cast(ret).add((short) 0, KMInteger.exp()); + KMArray.cast(ret).add((short)1, KMByteBlob.exp()); + short inst = KMKeyCharacteristics.exp(); + KMArray.cast(ret).add((short) 2, inst); + byte[] respBuf = response.getBytes(); + len = (short) respBuf.length; + ret = decoder.decode(ret, respBuf, (short) 0, len); + short error = KMInteger.cast(KMArray.cast(ret).get((short)0)).getShort(); + short keyBlobLength = KMByteBlob.cast(KMArray.cast(ret).get((short)1)).length(); + short keyCharacteristics = KMArray.cast(ret).get((short)2); + short hwParams = KMKeyCharacteristics.cast(keyCharacteristics).getHardwareEnforced(); + short swParams = KMKeyCharacteristics.cast(keyCharacteristics).getSoftwareEnforced(); + Assert.assertEquals(0x9000, response.getSW()); + Assert.assertEquals(error, KMError.OK); + short tag = KMKeyParameters.findTag(KMType.BOOL_TAG, KMType.NO_AUTH_REQUIRED, hwParams); + Assert.assertEquals(KMBoolTag.cast(tag).getVal(),0x01); + tag = KMKeyParameters.findTag(KMType.UINT_TAG, KMType.KEYSIZE, hwParams); + Assert.assertEquals(KMInteger.cast(KMIntegerTag.cast(tag).getValue()).getShort(), 2048); + tag = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.DIGEST, hwParams); + Assert.assertTrue(KMEnumArrayTag.cast(tag).contains(KMType.SHA2_256)); + tag = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.PADDING, hwParams); + Assert.assertTrue(KMEnumArrayTag.cast(tag).contains(KMType.RSA_PSS)); + tag = KMKeyParameters.findTag(KMType.ULONG_TAG, KMType.RSA_PUBLIC_EXPONENT, hwParams); + Assert.assertEquals(KMInteger.cast(KMIntegerTag.cast(tag).getValue()).getSignificantShort(), 0x01); + Assert.assertEquals(KMInteger.cast(KMIntegerTag.cast(tag).getValue()).getShort(), 0x01); + tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ALGORITHM, hwParams); + Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.RSA); + tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ORIGIN, hwParams); + Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.IMPORTED); + cleanUp(); + } + + @Test + public void testDeviceLocked(){ + init(); + byte[] hmacKey = new byte[32]; + cryptoProvider.newRandomNumber(hmacKey,(short)0,(short)32); + KMRepository.instance().initComputedHmac(hmacKey,(short)0,(short)32); + // generate aes key with unlocked_device_required + short aesKey = generateAesDesKey(KMType.AES,(short)128,null,null, true); + short keyBlobPtr = KMArray.cast(aesKey).get((short)1); + byte[] keyBlob= new byte[KMByteBlob.cast(keyBlobPtr).length()]; + Util.arrayCopyNonAtomic(KMByteBlob.cast(keyBlobPtr).getBuffer(), + KMByteBlob.cast(keyBlobPtr).getStartOff(), + keyBlob,(short)0, (short)keyBlob.length); + // encrypt something + short inParams = getAesDesParams(KMType.AES, KMType.ECB, KMType.PKCS7, null); + byte[] plainData= "Hello World 123!".getBytes(); + short ret = processMessage(plainData, + KMByteBlob.instance(keyBlob,(short)0, (short)keyBlob.length), + KMType.ENCRYPT, + KMKeyParameters.instance(inParams), + (short)0,null,false, false + ); + keyBlobPtr = KMArray.cast(ret).get((short)2); + byte[] cipherData = new byte[KMByteBlob.cast(keyBlobPtr).length()]; + Util.arrayCopyNonAtomic(KMByteBlob.cast(keyBlobPtr).getBuffer(), KMByteBlob.cast(keyBlobPtr).getStartOff(), + cipherData,(short)0, (short)cipherData.length); + // create verification token + short verToken = KMVerificationToken.instance(); + KMVerificationToken.cast(verToken).setTimestamp(KMInteger.uint_16((short)1)); + verToken = signVerificationToken(verToken); + // device locked request + deviceLock(verToken); + // decrypt should fail + inParams = getAesDesParams(KMType.AES, KMType.ECB, KMType.PKCS7, null); + short beginResp = begin(KMType.DECRYPT, + KMByteBlob.instance(keyBlob,(short)0, (short)keyBlob.length), KMKeyParameters.instance(inParams), (short)0); + Assert.assertEquals(beginResp,KMError.DEVICE_LOCKED); + short hwToken = KMHardwareAuthToken.instance(); + KMHardwareAuthToken.cast(hwToken).setTimestamp(KMInteger.uint_16((byte)2)); + KMHardwareAuthToken.cast(hwToken).setHwAuthenticatorType(KMEnum.instance(KMType.USER_AUTH_TYPE, (byte)KMType.PASSWORD)); + inParams = getAesDesParams(KMType.AES, KMType.ECB, KMType.PKCS7, null); + hwToken = signHwToken(hwToken); + ret = processMessage(cipherData, + KMByteBlob.instance(keyBlob,(short)0, (short)keyBlob.length), + KMType.DECRYPT, + KMKeyParameters.instance(inParams),hwToken,null,false, false + ); + ret = KMArray.cast(ret).get((short)0); + Assert.assertEquals(KMInteger.cast(ret).getShort(), KMError.OK); + cleanUp(); + } + + private short signHwToken(short hwToken){ + short len = 0; + byte[] scratchPad = new byte[256]; + // add 0 + Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 256, (byte) 0); + len = 1; + // concatenate challenge - 8 bytes + short ptr = KMHardwareAuthToken.cast(hwToken).getChallenge(); + KMInteger.cast(ptr) + .value(scratchPad, (short) (len + (short) (8 - KMInteger.cast(ptr).length()))); + len += 8; + // concatenate user id - 8 bytes + ptr = KMHardwareAuthToken.cast(hwToken).getUserId(); + KMInteger.cast(ptr) + .value(scratchPad, (short) (len + (short) (8 - KMInteger.cast(ptr).length()))); + len += 8; + // concatenate authenticator id - 8 bytes + ptr = KMHardwareAuthToken.cast(hwToken).getAuthenticatorId(); + KMInteger.cast(ptr) + .value(scratchPad, (short) (len + (short) (8 - KMInteger.cast(ptr).length()))); + len += 8; + // concatenate authenticator type - 4 bytes + ptr = KMHardwareAuthToken.cast(hwToken).getHwAuthenticatorType(); + scratchPad[(short) (len + 3)] = KMEnum.cast(ptr).getVal(); + len += 4; + // concatenate timestamp -8 bytes + ptr = KMHardwareAuthToken.cast(hwToken).getTimestamp(); + KMInteger.cast(ptr) + .value(scratchPad, (short) (len + (short) (8 - KMInteger.cast(ptr).length()))); + len += 8; + // hmac the data +/* HMACKey key = + cryptoProvider.createHMACKey( + KMRepository.instance().getComputedHmacKey(), + (short) 0, + (short) KMRepository.instance().getComputedHmacKey().length); + + */ + byte[] mac = new byte[32]; + /* + len = + cryptoProvider.hmacSign(key, scratchPad, (short) 0, len, + mac, + (short)0); + */ + short key = KMRepository.instance().getComputedHmacKey(); + cryptoProvider.hmacSign( + KMByteBlob.cast(key).getBuffer(), + KMByteBlob.cast(key).getStartOff(), + KMByteBlob.cast(key).length(), + scratchPad, (short) 0, len, + mac, + (short)0); + KMHardwareAuthToken.cast(hwToken).setMac(KMByteBlob.instance(mac,(short)0,(short)mac.length)); + return hwToken; + } + private void deviceLock(short verToken) { + short req = KMArray.instance((short)2); + KMArray.cast(req).add((short)0, KMInteger.uint_8((byte)1)); + KMArray.cast(req).add((short)1, verToken); + CommandAPDU apdu = encodeApdu((byte)INS_DEVICE_LOCKED_CMD,req); + ResponseAPDU response = simulator.transmitCommand(apdu); + short ret = KMArray.instance((short) 1); + KMArray.cast(ret).add((short) 0, KMInteger.exp()); + byte[] respBuf = response.getBytes(); + Assert.assertEquals(respBuf[0],KMError.OK); + } + + private short signVerificationToken(short verToken) { + byte[] scratchPad = new byte[256]; + byte[] authVer = "Auth Verification".getBytes(); + //print(authVer,(short)0,(short)authVer.length); + // 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(authVer,(short)0, scratchPad, (short)0, (short)authVer.length); + short len = (short)authVer.length; + // concatenate challenge - 8 bytes + short ptr = KMVerificationToken.cast(verToken).getChallenge(); + KMInteger.cast(ptr) + .value(scratchPad, (short) (len + (short) (8 - KMInteger.cast(ptr).length()))); + len += 8; + // concatenate timestamp -8 bytes + ptr = KMVerificationToken.cast(verToken).getTimestamp(); + KMInteger.cast(ptr) + .value(scratchPad, (short) (len + (short) (8 - KMInteger.cast(ptr).length()))); + len += 8; + // concatenate security level - 4 bytes + ptr = KMVerificationToken.cast(verToken).getSecurityLevel(); + scratchPad[(short) (len + 3)] = KMEnum.cast(ptr).getVal(); + len += 4; + // concatenate Parameters verified - blob of encoded data. + ptr = KMVerificationToken.cast(verToken).getParametersVerified(); + if (KMByteBlob.cast(ptr).length() != 0) { + len += KMByteBlob.cast(ptr).getValues(scratchPad, (short) 0); + } + // hmac the data + /* 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 = + cryptoProvider.hmacSign(key, scratchPad, (short) 0, len, + mac, + (short)0); + */ + short key = KMRepository.instance().getComputedHmacKey(); + cryptoProvider.hmacSign(KMByteBlob.cast(key).getBuffer(), + KMByteBlob.cast(key).getStartOff(), + KMByteBlob.cast(key).length(), + scratchPad, (short) 0, len, + mac, + (short)0); + KMVerificationToken.cast(verToken).setMac(KMByteBlob.instance(mac,(short)0,(short)mac.length)); + return verToken; + } + + @Test + public void testEcImportKeySuccess() { + init(); + /* + KeyPair ecKeyPair = cryptoProvider.createECKeyPair(); + byte[] pub = new byte[128]; + short len = ((ECPublicKey)ecKeyPair.getPublic()).getW(pub,(short)0); + byte[] priv = new byte[128]; + len = ((ECPrivateKey)ecKeyPair.getPrivate()).getS(priv,(short)0); + */ + 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)); + short byteBlob = KMByteBlob.instance((short)1); + KMByteBlob.cast(byteBlob).add((short)0, KMType.SHA2_256); + short digest = KMEnumArrayTag.instance(KMType.DIGEST, byteBlob); + short ecCurve = KMEnumTag.instance(KMType.ECCURVE, KMType.P_256); + KMArray.cast(arrPtr).add((short)0, boolTag); + KMArray.cast(arrPtr).add((short)1, keySize); + KMArray.cast(arrPtr).add((short)2, digest); + KMArray.cast(arrPtr).add((short)3, ecCurve); + KMArray.cast(arrPtr).add((short)4, KMEnumTag.instance(KMType.ALGORITHM, KMType.EC)); + short keyParams = KMKeyParameters.instance(arrPtr); + short keyFormatPtr = KMEnum.instance(KMType.KEY_FORMAT, KMType.RAW);// Note: VTS uses PKCS8 + short keyBlob = KMArray.instance((short)2); + KMArray.cast(keyBlob).add((short)0, privBlob); + KMArray.cast(keyBlob).add((short)1, pubBlob); + byte[] blob = new byte[128]; + short len = encoder.encode(keyBlob,blob,(short)0); + keyBlob = KMByteBlob.instance(blob, (short)0, len); + arrPtr = KMArray.instance((short)3); + KMArray arg = KMArray.cast(arrPtr); + arg.add((short) 0, keyParams); + arg.add((short)1, keyFormatPtr); + arg.add((short)2, keyBlob); + CommandAPDU apdu = encodeApdu((byte)INS_IMPORT_KEY_CMD, arrPtr); + // print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(apdu); + short ret = KMArray.instance((short) 3); + KMArray.cast(ret).add((short) 0, KMInteger.exp()); + KMArray.cast(ret).add((short)1, KMByteBlob.exp()); + short inst = KMKeyCharacteristics.exp(); + KMArray.cast(ret).add((short) 2, inst); + byte[] respBuf = response.getBytes(); + len = (short) respBuf.length; + ret = decoder.decode(ret, respBuf, (short) 0, len); + short error = KMInteger.cast(KMArray.cast(ret).get((short)0)).getShort(); + short keyBlobLength = KMByteBlob.cast(KMArray.cast(ret).get((short)1)).length(); + short blobArr = extractKeyBlobArray(KMArray.cast(ret).get((short)1)); + short keyCharacteristics = KMArray.cast(ret).get((short)2); + short hwParams = KMKeyCharacteristics.cast(keyCharacteristics).getHardwareEnforced(); + short swParams = KMKeyCharacteristics.cast(keyCharacteristics).getSoftwareEnforced(); + Assert.assertEquals(0x9000, response.getSW()); + Assert.assertEquals(error, KMError.OK); + short tag = KMKeyParameters.findTag(KMType.BOOL_TAG, KMType.NO_AUTH_REQUIRED, hwParams); + Assert.assertEquals(KMBoolTag.cast(tag).getVal(),0x01); + tag = KMKeyParameters.findTag(KMType.UINT_TAG, KMType.KEYSIZE, hwParams); + Assert.assertEquals(KMInteger.cast(KMIntegerTag.cast(tag).getValue()).getShort(), 256); + tag = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.DIGEST, hwParams); + Assert.assertTrue(KMEnumArrayTag.cast(tag).contains(KMType.SHA2_256)); + tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ECCURVE, hwParams); + Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.P_256); + tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ALGORITHM, hwParams); + Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.EC); + tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ORIGIN, hwParams); + Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.IMPORTED); + cleanUp(); + } + + private short extractKeyBlobArray(short keyBlob) { + short ret = KMArray.instance((short) 5); + KMArray.cast(ret).add(KMKeymasterApplet.KEY_BLOB_SECRET, KMByteBlob.exp()); + KMArray.cast(ret).add(KMKeymasterApplet.KEY_BLOB_AUTH_TAG, KMByteBlob.exp()); + KMArray.cast(ret).add(KMKeymasterApplet.KEY_BLOB_NONCE, KMByteBlob.exp()); + short ptr = KMKeyCharacteristics.exp(); + KMArray.cast(ret).add(KMKeymasterApplet.KEY_BLOB_KEYCHAR, ptr); + KMArray.cast(ret).add(KMKeymasterApplet.KEY_BLOB_PUB_KEY, KMByteBlob.exp()); + ret = + decoder.decodeArray( + ret, + KMByteBlob.cast(keyBlob).getBuffer(), + KMByteBlob.cast(keyBlob).getStartOff(), + KMByteBlob.cast(keyBlob).length()); + short len = KMArray.cast(ret).length(); + ptr = KMArray.cast(ret).get((short)4); +// print(KMByteBlob.cast(ptr).getBuffer(),KMByteBlob.cast(ptr).getStartOff(),KMByteBlob.cast(ptr).length()); + return ret; + } + + @Test + public void testRsaGenerateKeySuccess() { + init(); + short ret = generateRsaKey(null, null); + short error = KMInteger.cast(KMArray.cast(ret).get((short)0)).getShort(); + short keyBlobLength = KMByteBlob.cast(KMArray.cast(ret).get((short)1)).length(); + short keyCharacteristics = KMArray.cast(ret).get((short)2); + short hwParams = KMKeyCharacteristics.cast(keyCharacteristics).getHardwareEnforced(); + short swParams = KMKeyCharacteristics.cast(keyCharacteristics).getSoftwareEnforced(); + Assert.assertEquals(error, KMError.OK); + short tag = KMKeyParameters.findTag(KMType.UINT_TAG, KMType.KEYSIZE, hwParams); + Assert.assertEquals(KMInteger.cast(KMIntegerTag.cast(tag).getValue()).getShort(), 2048); + tag = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.DIGEST, hwParams); + Assert.assertTrue(KMEnumArrayTag.cast(tag).contains(KMType.DIGEST_NONE)); + tag = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.PADDING, hwParams); + Assert.assertTrue(KMEnumArrayTag.cast(tag).contains(KMType.RSA_PKCS1_1_5_ENCRYPT)); + tag = KMKeyParameters.findTag(KMType.ULONG_TAG, KMType.RSA_PUBLIC_EXPONENT, hwParams); + Assert.assertEquals(KMInteger.cast(KMIntegerTag.cast(tag).getValue()).getSignificantShort(), 0x01); + Assert.assertEquals(KMInteger.cast(KMIntegerTag.cast(tag).getValue()).getShort(), 0x01); + tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ALGORITHM, hwParams); + Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.RSA); + tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ORIGIN, hwParams); + Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.GENERATED); + cleanUp(); + } + + private short generateRsaKey(byte[] clientId, byte[] appData){ + byte[] activeAndCreationDateTime = {0,0,0x01,0x73,0x51,0x7C,(byte)0xCC,0x00}; + short tagCount = 11; + if(clientId != null) tagCount++; + if(appData != null) tagCount++; + short arrPtr = KMArray.instance(tagCount); + short keySize = KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, KMInteger.uint_16((short)2048)); + short byteBlob = KMByteBlob.instance((short)3); + KMByteBlob.cast(byteBlob).add((short)0, KMType.DIGEST_NONE); + KMByteBlob.cast(byteBlob).add((short)1, KMType.SHA2_256); + KMByteBlob.cast(byteBlob).add((short)2, KMType.SHA1); + short digest = KMEnumArrayTag.instance(KMType.DIGEST, byteBlob); + byteBlob = KMByteBlob.instance((short)5); + KMByteBlob.cast(byteBlob).add((short)0, KMType.RSA_PKCS1_1_5_ENCRYPT); + KMByteBlob.cast(byteBlob).add((short)1, KMType.RSA_PKCS1_1_5_SIGN); + KMByteBlob.cast(byteBlob).add((short)2, KMType.RSA_OAEP); + KMByteBlob.cast(byteBlob).add((short)3, KMType.RSA_PSS); + KMByteBlob.cast(byteBlob).add((short)4, KMType.PADDING_NONE); + short padding = KMEnumArrayTag.instance(KMType.PADDING, byteBlob); + byteBlob = KMByteBlob.instance((short)5); + KMByteBlob.cast(byteBlob).add((short)0, KMType.SIGN); + KMByteBlob.cast(byteBlob).add((short)1, KMType.VERIFY); + KMByteBlob.cast(byteBlob).add((short)2, KMType.ENCRYPT); + KMByteBlob.cast(byteBlob).add((short)3, KMType.DECRYPT); + KMByteBlob.cast(byteBlob).add((short)4, KMType.WRAP_KEY); + short purpose = KMEnumArrayTag.instance(KMType.PURPOSE, byteBlob); + byte[] pub = {0,1,0,1}; + short rsaPubExpTag = KMIntegerTag.instance(KMType.ULONG_TAG,KMType.RSA_PUBLIC_EXPONENT, KMInteger.uint_32(pub, (short)0)); + short boolTag = KMBoolTag.instance(KMType.NO_AUTH_REQUIRED); + short tagIndex = 0; + KMArray.cast(arrPtr).add(tagIndex++, purpose); + KMArray.cast(arrPtr).add(tagIndex++, KMBoolTag.instance(KMType.INCLUDE_UNIQUE_ID)); + KMArray.cast(arrPtr).add(tagIndex++, KMBoolTag.instance(KMType.RESET_SINCE_ID_ROTATION)); + KMArray.cast(arrPtr).add(tagIndex++, boolTag); + KMArray.cast(arrPtr).add(tagIndex++, keySize); + KMArray.cast(arrPtr).add(tagIndex++, digest); + KMArray.cast(arrPtr).add(tagIndex++, rsaPubExpTag); + KMArray.cast(arrPtr).add(tagIndex++, KMEnumTag.instance(KMType.ALGORITHM, KMType.RSA)); + KMArray.cast(arrPtr).add(tagIndex++, padding); + short dateTag = KMInteger.uint_64(activeAndCreationDateTime,(short)0); + KMArray.cast(arrPtr).add(tagIndex++, KMIntegerTag.instance(KMType.DATE_TAG,KMType.ACTIVE_DATETIME,dateTag)); + KMArray.cast(arrPtr).add(tagIndex++, KMIntegerTag.instance(KMType.DATE_TAG,KMType.CREATION_DATETIME,dateTag)); + + if(clientId != null)KMArray.cast(arrPtr).add(tagIndex++, + KMByteTag.instance(KMType.APPLICATION_ID, KMByteBlob.instance(clientId,(short)0,(short)clientId.length))); + if(appData != null)KMArray.cast(arrPtr).add(tagIndex++, + KMByteTag.instance(KMType.APPLICATION_DATA, KMByteBlob.instance(appData,(short)0,(short)appData.length))); + short keyParams = KMKeyParameters.instance(arrPtr); + arrPtr = KMArray.instance((short)1); + KMArray arg = KMArray.cast(arrPtr); + arg.add((short) 0, keyParams); + CommandAPDU apdu = encodeApdu((byte)INS_GENERATE_KEY_CMD, arrPtr); + // print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(apdu); + Assert.assertEquals(0x9000, response.getSW()); + short ret = KMArray.instance((short) 3); + KMArray.cast(ret).add((short) 0, KMInteger.exp()); + KMArray.cast(ret).add((short)1, KMByteBlob.exp()); + short inst = KMKeyCharacteristics.exp(); + KMArray.cast(ret).add((short) 2, inst); + byte[] respBuf = response.getBytes(); + short len = (short) respBuf.length; + ret = decoder.decode(ret, respBuf, (short) 0, len); + return ret; + } + + private short generateAttestationKey(){ + // 15th July 2020 00.00.00 + byte[] activeAndCreationDateTime = {0,0,0x01,0x73,0x51,0x7C,(byte)0xCC,0x00}; + short tagCount = 11; + short arrPtr = KMArray.instance(tagCount); + short keySize = KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, KMInteger.uint_16((short)2048)); + short byteBlob = KMByteBlob.instance((short)3); + KMByteBlob.cast(byteBlob).add((short)0, KMType.DIGEST_NONE); + KMByteBlob.cast(byteBlob).add((short)1, KMType.SHA2_256); + KMByteBlob.cast(byteBlob).add((short)2, KMType.SHA1); + short digest = KMEnumArrayTag.instance(KMType.DIGEST, byteBlob); + byteBlob = KMByteBlob.instance((short)1); + KMByteBlob.cast(byteBlob).add((short)0, KMType.RSA_PKCS1_1_5_SIGN); + short padding = KMEnumArrayTag.instance(KMType.PADDING, byteBlob); + byteBlob = KMByteBlob.instance((short)1); + KMByteBlob.cast(byteBlob).add((short)0, KMType.ATTEST_KEY); + short purpose = KMEnumArrayTag.instance(KMType.PURPOSE, byteBlob); + byte[] pub = {0,1,0,1}; + short rsaPubExpTag = KMIntegerTag.instance(KMType.ULONG_TAG,KMType.RSA_PUBLIC_EXPONENT, KMInteger.uint_32(pub, (short)0)); + short boolTag = KMBoolTag.instance(KMType.NO_AUTH_REQUIRED); + short tagIndex = 0; + KMArray.cast(arrPtr).add(tagIndex++, purpose); + KMArray.cast(arrPtr).add(tagIndex++, boolTag); + KMArray.cast(arrPtr).add(tagIndex++, KMBoolTag.instance(KMType.INCLUDE_UNIQUE_ID)); + KMArray.cast(arrPtr).add(tagIndex++, KMBoolTag.instance(KMType.RESET_SINCE_ID_ROTATION)); + KMArray.cast(arrPtr).add(tagIndex++, boolTag); + KMArray.cast(arrPtr).add(tagIndex++, keySize); + KMArray.cast(arrPtr).add(tagIndex++, digest); + KMArray.cast(arrPtr).add(tagIndex++, rsaPubExpTag); + KMArray.cast(arrPtr).add(tagIndex++, KMEnumTag.instance(KMType.ALGORITHM, KMType.RSA)); + KMArray.cast(arrPtr).add(tagIndex++, padding); + short dateTag = KMInteger.uint_64(activeAndCreationDateTime,(short)0); + KMArray.cast(arrPtr).add(tagIndex++, KMIntegerTag.instance(KMType.ULONG_TAG,KMType.ACTIVE_DATETIME,dateTag)); + KMArray.cast(arrPtr).add(tagIndex++, KMIntegerTag.instance(KMType.ULONG_TAG,KMType.CREATION_DATETIME,dateTag)); + short keyParams = KMKeyParameters.instance(arrPtr); + arrPtr = KMArray.instance((short)1); + KMArray arg = KMArray.cast(arrPtr); + arg.add((short) 0, keyParams); + CommandAPDU apdu = encodeApdu((byte)INS_GENERATE_KEY_CMD, arrPtr); + // print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(apdu); + Assert.assertEquals(0x9000, response.getSW()); + short ret = KMArray.instance((short) 3); + KMArray.cast(ret).add((short) 0, KMInteger.exp()); + KMArray.cast(ret).add((short)1, KMByteBlob.exp()); + short inst = KMKeyCharacteristics.exp(); + KMArray.cast(ret).add((short) 2, inst); + byte[] respBuf = response.getBytes(); + short len = (short) respBuf.length; + ret = decoder.decode(ret, respBuf, (short) 0, len); + return ret; + } + + @Test + public void testEcGenerateKeySuccess() { + init(); + short ret = generateEcKey(null, null); + short error = KMInteger.cast(KMArray.cast(ret).get((short)0)).getShort(); + short keyBlobLength = KMByteBlob.cast(KMArray.cast(ret).get((short)1)).length(); + short keyCharacteristics = KMArray.cast(ret).get((short)2); + short hwParams = KMKeyCharacteristics.cast(keyCharacteristics).getHardwareEnforced(); + short swParams = KMKeyCharacteristics.cast(keyCharacteristics).getSoftwareEnforced(); + Assert.assertEquals(error, KMError.OK); + short tag = KMKeyParameters.findTag(KMType.UINT_TAG, KMType.KEYSIZE, hwParams); + Assert.assertEquals(KMInteger.cast(KMIntegerTag.cast(tag).getValue()).getShort(), 256); + tag = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.DIGEST, hwParams); + Assert.assertTrue(KMEnumArrayTag.cast(tag).contains(KMType.DIGEST_NONE)); + tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ALGORITHM, hwParams); + Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.EC); + tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ORIGIN, hwParams); + Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.GENERATED); + cleanUp(); + } + public short generateEcKey(byte[] clientId, byte[] appData) { + byte[] activeAndCreationDateTime = {0,0,0x01,0x73,0x51,0x7C,(byte)0xCC,0x00}; + short tagCount = 6; + if(clientId != null) tagCount++; + if(appData != null) tagCount++; + short arrPtr = KMArray.instance(tagCount); + short keySize = KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, KMInteger.uint_16((short)256)); + short byteBlob = KMByteBlob.instance((short)2); + KMByteBlob.cast(byteBlob).add((short)0, KMType.DIGEST_NONE); + KMByteBlob.cast(byteBlob).add((short)1, KMType.SHA2_256); + short digest = KMEnumArrayTag.instance(KMType.DIGEST, byteBlob); + byteBlob = KMByteBlob.instance((short)2); + KMByteBlob.cast(byteBlob).add((short)0, KMType.SIGN); + KMByteBlob.cast(byteBlob).add((short)1, KMType.VERIFY); + short purpose = KMEnumArrayTag.instance(KMType.PURPOSE, byteBlob); + short boolTag = KMBoolTag.instance(KMType.NO_AUTH_REQUIRED); + short tagIndex = 0; + KMArray.cast(arrPtr).add(tagIndex++, purpose); + KMArray.cast(arrPtr).add(tagIndex++, boolTag); + KMArray.cast(arrPtr).add(tagIndex++, keySize); + KMArray.cast(arrPtr).add(tagIndex++, digest); + KMArray.cast(arrPtr).add(tagIndex++, KMEnumTag.instance(KMType.ALGORITHM, KMType.EC)); + short dateTag = KMInteger.uint_64(activeAndCreationDateTime,(short)0); + KMArray.cast(arrPtr).add(tagIndex++, KMIntegerTag.instance(KMType.DATE_TAG,KMType.CREATION_DATETIME,dateTag)); + if(clientId != null)KMArray.cast(arrPtr).add(tagIndex++, + KMByteTag.instance(KMType.APPLICATION_ID, KMByteBlob.instance(clientId,(short)0,(short)clientId.length))); + if(appData != null)KMArray.cast(arrPtr).add(tagIndex++, + KMByteTag.instance(KMType.APPLICATION_DATA, KMByteBlob.instance(appData,(short)0,(short)appData.length))); + short keyParams = KMKeyParameters.instance(arrPtr); + arrPtr = KMArray.instance((short)1); + KMArray arg = KMArray.cast(arrPtr); + arg.add((short) 0, keyParams); + CommandAPDU apdu = encodeApdu((byte)INS_GENERATE_KEY_CMD, arrPtr); + // print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(apdu); + Assert.assertEquals(0x9000, response.getSW()); + short ret = KMArray.instance((short) 3); + KMArray.cast(ret).add((short) 0, KMInteger.exp()); + KMArray.cast(ret).add((short)1, KMByteBlob.exp()); + short inst = KMKeyCharacteristics.exp(); + KMArray.cast(ret).add((short) 2, inst); + byte[] respBuf = response.getBytes(); + short len = (short) respBuf.length; + ret = decoder.decode(ret, respBuf, (short) 0, len); + return ret; + } + + @Test + public void testHmacGenerateKeySuccess() { + init(); + short ret = generateHmacKey(null, null); + short error = KMInteger.cast(KMArray.cast(ret).get((short)0)).getShort(); + short keyBlobLength = KMByteBlob.cast(KMArray.cast(ret).get((short)1)).length(); + short keyCharacteristics = KMArray.cast(ret).get((short)2); + short hwParams = KMKeyCharacteristics.cast(keyCharacteristics).getHardwareEnforced(); + short swParams = KMKeyCharacteristics.cast(keyCharacteristics).getSoftwareEnforced(); + Assert.assertEquals(error, KMError.OK); + short tag = KMKeyParameters.findTag(KMType.UINT_TAG, KMType.KEYSIZE, hwParams); + Assert.assertEquals(KMInteger.cast(KMIntegerTag.cast(tag).getValue()).getShort(), 128); + tag = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.DIGEST, hwParams); + Assert.assertTrue(KMEnumArrayTag.cast(tag).contains(KMType.SHA2_256)); + tag = KMKeyParameters.findTag(KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, hwParams); + Assert.assertEquals(KMInteger.cast(KMIntegerTag.cast(tag).getValue()).getShort(), 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); + Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.GENERATED); + cleanUp(); + } + public short generateHmacKey(byte[] clientId, byte[] appData){ + short tagCount = 6; + if(clientId != null) tagCount++; + if(appData != null) tagCount++; + short arrPtr = KMArray.instance(tagCount); + short keySize = KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, KMInteger.uint_16((short)128)); + short byteBlob = KMByteBlob.instance((short)1); + KMByteBlob.cast(byteBlob).add((short)0, KMType.SHA2_256); + short digest = KMEnumArrayTag.instance(KMType.DIGEST, byteBlob); + byteBlob = KMByteBlob.instance((short)2); + KMByteBlob.cast(byteBlob).add((short)0, KMType.SIGN); + 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)/*256*/160)); + short tagIndex = 0; + KMArray.cast(arrPtr).add(tagIndex++, minMacLen); + KMArray.cast(arrPtr).add(tagIndex++, purpose); + KMArray.cast(arrPtr).add(tagIndex++, boolTag); + KMArray.cast(arrPtr).add(tagIndex++, keySize); + KMArray.cast(arrPtr).add(tagIndex++, digest); + KMArray.cast(arrPtr).add(tagIndex++, KMEnumTag.instance(KMType.ALGORITHM, KMType.HMAC)); + if(clientId != null)KMArray.cast(arrPtr).add(tagIndex++, + KMByteTag.instance(KMType.APPLICATION_ID, KMByteBlob.instance(clientId,(short)0,(short)clientId.length))); + if(appData != null)KMArray.cast(arrPtr).add(tagIndex++, + KMByteTag.instance(KMType.APPLICATION_DATA, KMByteBlob.instance(appData,(short)0,(short)appData.length))); + short keyParams = KMKeyParameters.instance(arrPtr); + arrPtr = KMArray.instance((short)1); + KMArray arg = KMArray.cast(arrPtr); + arg.add((short) 0, keyParams); + CommandAPDU apdu = encodeApdu((byte)INS_GENERATE_KEY_CMD, arrPtr); + // print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(apdu); + short ret = KMArray.instance((short) 3); + KMArray.cast(ret).add((short) 0, KMInteger.exp()); + KMArray.cast(ret).add((short)1, KMByteBlob.exp()); + short inst = KMKeyCharacteristics.exp(); + KMArray.cast(ret).add((short) 2, inst); + byte[] respBuf = response.getBytes(); + short len = (short) respBuf.length; + ret = decoder.decode(ret, respBuf, (short) 0, len); + short error = KMInteger.cast(KMArray.cast(ret).get((short)0)).getShort(); + Assert.assertEquals(0x9000, response.getSW()); + Assert.assertEquals(error, KMError.OK); + return ret; + } + public short generateAesDesKey(byte alg, short keysize, byte[] clientId, byte[] appData, boolean unlockReqd) { + short tagCount = 7; + if(clientId != null) tagCount++; + if(appData != null) tagCount++; + if(unlockReqd)tagCount++; + short arrPtr = KMArray.instance(tagCount); + short boolTag = KMBoolTag.instance(KMType.NO_AUTH_REQUIRED); + short keySize = KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, KMInteger.uint_16(keysize)); + short byteBlob = KMByteBlob.instance((short)3); + KMByteBlob.cast(byteBlob).add((short)0, KMType.ECB); + KMByteBlob.cast(byteBlob).add((short)1, KMType.CBC); + KMByteBlob.cast(byteBlob).add((short)2, KMType.CTR); + short blockModeTag = KMEnumArrayTag.instance(KMType.BLOCK_MODE, byteBlob); + byteBlob = KMByteBlob.instance((short)2); + KMByteBlob.cast(byteBlob).add((short)0, KMType.PKCS7); + KMByteBlob.cast(byteBlob).add((short)1, KMType.PADDING_NONE); + short paddingMode = KMEnumArrayTag.instance(KMType.PADDING, byteBlob); + byteBlob = KMByteBlob.instance((short)2); + KMByteBlob.cast(byteBlob).add((short)0, KMType.ENCRYPT); + KMByteBlob.cast(byteBlob).add((short)1, KMType.DECRYPT); + short purpose = KMEnumArrayTag.instance(KMType.PURPOSE, byteBlob); + short tagIndex = 0; + KMArray.cast(arrPtr).add(tagIndex++, boolTag); + KMArray.cast(arrPtr).add(tagIndex++, keySize); + KMArray.cast(arrPtr).add(tagIndex++, blockModeTag); + KMArray.cast(arrPtr).add(tagIndex++, paddingMode); + KMArray.cast(arrPtr).add(tagIndex++, KMEnumTag.instance(KMType.ALGORITHM, alg)); + KMArray.cast(arrPtr).add(tagIndex++, purpose); + KMArray.cast(arrPtr).add(tagIndex++, KMBoolTag.instance(KMType.CALLER_NONCE)); + if(unlockReqd)KMArray.cast(arrPtr).add(tagIndex++, KMBoolTag.instance(KMType.UNLOCKED_DEVICE_REQUIRED)); + if(clientId != null)KMArray.cast(arrPtr).add(tagIndex++, + KMByteTag.instance(KMType.APPLICATION_ID, KMByteBlob.instance(clientId,(short)0,(short)clientId.length))); + if(appData != null)KMArray.cast(arrPtr).add(tagIndex++, + KMByteTag.instance(KMType.APPLICATION_DATA, KMByteBlob.instance(appData,(short)0,(short)appData.length))); + short keyParams = KMKeyParameters.instance(arrPtr); + arrPtr = KMArray.instance((short)1); + KMArray arg = KMArray.cast(arrPtr); + arg.add((short) 0, keyParams); + CommandAPDU apdu = encodeApdu((byte)INS_GENERATE_KEY_CMD, arrPtr); + // print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(apdu); + short ret = KMArray.instance((short) 3); + KMArray.cast(ret).add((short) 0, KMInteger.exp()); + KMArray.cast(ret).add((short)1, KMByteBlob.exp()); + short inst = KMKeyCharacteristics.exp(); + KMArray.cast(ret).add((short) 2, inst); + byte[] respBuf = response.getBytes(); + short len = (short) respBuf.length; + ret = decoder.decode(ret, respBuf, (short) 0, len); + short error = KMInteger.cast(KMArray.cast(ret).get((short)0)).getShort(); + Assert.assertEquals(0x9000, response.getSW()); + Assert.assertEquals(error, KMError.OK); + return ret; + } + public short generateAesGcmKey(short keysize, byte[] clientId, byte[] appData) { + short tagCount = 8; + if(clientId != null) tagCount++; + if(appData != null) tagCount++; + short arrPtr = KMArray.instance(tagCount); + short boolTag = KMBoolTag.instance(KMType.NO_AUTH_REQUIRED); + short keySize = KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, KMInteger.uint_16(keysize)); + short macLength = KMIntegerTag.instance(KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, KMInteger.uint_16((short)96)); + short byteBlob = KMByteBlob.instance((short)1); + KMByteBlob.cast(byteBlob).add((short)0, KMType.GCM); + short blockModeTag = KMEnumArrayTag.instance(KMType.BLOCK_MODE, byteBlob); + byteBlob = KMByteBlob.instance((short)1); + KMByteBlob.cast(byteBlob).add((short)0, KMType.PADDING_NONE); + short paddingMode = KMEnumArrayTag.instance(KMType.PADDING, byteBlob); + byteBlob = KMByteBlob.instance((short)2); + KMByteBlob.cast(byteBlob).add((short)0, KMType.ENCRYPT); + KMByteBlob.cast(byteBlob).add((short)1, KMType.DECRYPT); + short purpose = KMEnumArrayTag.instance(KMType.PURPOSE, byteBlob); + short tagIndex = 0; + KMArray.cast(arrPtr).add(tagIndex++, boolTag); + KMArray.cast(arrPtr).add(tagIndex++, macLength); + KMArray.cast(arrPtr).add(tagIndex++, keySize); + KMArray.cast(arrPtr).add(tagIndex++, blockModeTag); + KMArray.cast(arrPtr).add(tagIndex++, paddingMode); + KMArray.cast(arrPtr).add(tagIndex++, KMEnumTag.instance(KMType.ALGORITHM, KMType.AES)); + KMArray.cast(arrPtr).add(tagIndex++, purpose); + KMArray.cast(arrPtr).add(tagIndex++, KMBoolTag.instance(KMType.CALLER_NONCE)); + if(clientId != null)KMArray.cast(arrPtr).add(tagIndex++, + KMByteTag.instance(KMType.APPLICATION_ID, KMByteBlob.instance(clientId,(short)0,(short)clientId.length))); + if(appData != null)KMArray.cast(arrPtr).add(tagIndex++, + KMByteTag.instance(KMType.APPLICATION_DATA, KMByteBlob.instance(appData,(short)0,(short)appData.length))); + short keyParams = KMKeyParameters.instance(arrPtr); + arrPtr = KMArray.instance((short)1); + KMArray arg = KMArray.cast(arrPtr); + arg.add((short) 0, keyParams); + CommandAPDU apdu = encodeApdu((byte)INS_GENERATE_KEY_CMD, arrPtr); + // print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(apdu); + short ret = KMArray.instance((short) 3); + KMArray.cast(ret).add((short) 0, KMInteger.exp()); + KMArray.cast(ret).add((short)1, KMByteBlob.exp()); + short inst = KMKeyCharacteristics.exp(); + KMArray.cast(ret).add((short) 2, inst); + byte[] respBuf = response.getBytes(); + short len = (short) respBuf.length; + ret = decoder.decode(ret, respBuf, (short) 0, len); + short error = KMInteger.cast(KMArray.cast(ret).get((short)0)).getShort(); + Assert.assertEquals(0x9000, response.getSW()); + Assert.assertEquals(error, KMError.OK); + return ret; + } + + @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); + 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)); + num = KMByteBlob.instance((short)32); + cryptoProvider.newRandomNumber( + KMByteBlob.cast(num).getBuffer(), + KMByteBlob.cast(num).getStartOff(), + KMByteBlob.cast(num).length()); + KMHmacSharingParameters.cast(params2).setNonce(num); + short arr = KMArray.instance((short)2); + KMArray.cast(arr).add((short)0, params1); + KMArray.cast(arr).add((short)1,params2); + short arrPtr = KMArray.instance((short)1); + KMArray.cast(arrPtr).add((short)0,arr); + CommandAPDU apdu = encodeApdu((byte)INS_COMPUTE_SHARED_HMAC_CMD, arrPtr); + // print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(apdu); + Assert.assertEquals(0x9000, response.getSW()); + 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); + error = KMInteger.cast(KMArray.cast(ret).get((short)0)).getShort(); + Assert.assertEquals(0x9000, response.getSW()); + Assert.assertEquals(error, KMError.OK); + + cleanUp(); + } + @Test + public void testGetHmacSharingParams(){ + init(); + CommandAPDU commandAPDU = new CommandAPDU(0x80, INS_GET_HMAC_SHARING_PARAM_CMD, 0x40, 0x00); + //print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(commandAPDU); + KMDecoder dec = new KMDecoder(); + short ret = KMArray.instance((short) 2); + KMArray.cast(ret).add((short) 0, KMInteger.exp()); + short inst = KMHmacSharingParameters.exp(); + KMArray.cast(ret).add((short) 1, inst); + byte[] respBuf = response.getBytes(); + short len = (short) respBuf.length; + ret = decoder.decode(ret, respBuf, (short) 0, len); + short error = KMInteger.cast(KMArray.cast(ret).get((short)0)).getShort(); + KMHmacSharingParameters params = KMHmacSharingParameters.cast(KMArray.cast(ret).get((short)1)); + short seed = params.getSeed(); + short nonce = params.getNonce(); + Assert.assertTrue(KMByteBlob.cast(seed).length() == 0); + Assert.assertTrue(KMByteBlob.cast(nonce).length() == 32); + //print(seed); + //print(nonce); + Assert.assertEquals(error, KMError.OK); + cleanUp(); + } + public short getHmacSharingParams(){ + CommandAPDU commandAPDU = new CommandAPDU(0x80, INS_GET_HMAC_SHARING_PARAM_CMD, 0x40, 0x00); + //print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(commandAPDU); + KMDecoder dec = new KMDecoder(); + short ret = KMArray.instance((short) 2); + KMArray.cast(ret).add((short) 0, KMInteger.exp()); + short inst = KMHmacSharingParameters.exp(); + KMArray.cast(ret).add((short) 1, inst); + byte[] respBuf = response.getBytes(); + short len = (short) respBuf.length; + ret = decoder.decode(ret, respBuf, (short) 0, len); + return ret; + } + + @Test + public void testImportWrappedKey(){ + init(); + byte[] wrappedKey = new byte[16]; + cryptoProvider.newRandomNumber(wrappedKey,(short)0,(short)16); + byte[] encWrappedKey = new byte[16]; + //AESKey transportKey = cryptoProvider.createAESKey((short)256); + byte[] transportKeyMaterial = new byte[32]; + cryptoProvider.newRandomNumber(transportKeyMaterial,(short)0,(short)32); + //transportKey.setKey(transportKeyMaterial,(short)0); + byte[] nonce = new byte[12]; + cryptoProvider.newRandomNumber(nonce,(short)0,(short)12); + byte[] authData = "Auth Data".getBytes(); + byte[] authTag = new byte[12]; + cryptoProvider.aesGCMEncrypt(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}; + byte[] maskedTransportKey = new byte[32]; + for(int i=0; i< maskingKey.length;i++){ + maskedTransportKey[i] = (byte)(transportKeyMaterial[i] ^ maskingKey[i]); + } + short rsaKeyArr = generateRsaKey(null,null); + short keyBlobPtr = KMArray.cast(rsaKeyArr).get((short)1); + byte[] wrappingKeyBlob= new byte[KMByteBlob.cast(keyBlobPtr).length()]; + Util.arrayCopyNonAtomic(KMByteBlob.cast(keyBlobPtr).getBuffer(), + KMByteBlob.cast(keyBlobPtr).getStartOff(), + wrappingKeyBlob,(short)0, (short)wrappingKeyBlob.length); + short inParams = getRsaParams(KMType.SHA2_256, KMType.RSA_OAEP); + short ret = processMessage(maskedTransportKey, + KMByteBlob.instance(wrappingKeyBlob,(short)0, (short)wrappingKeyBlob.length), + KMType.ENCRYPT, + KMKeyParameters.instance(inParams), + (short)0,null,false,false + ); + keyBlobPtr = KMArray.cast(ret).get((short)2); + byte[] encTransportKey = new byte[KMByteBlob.cast(keyBlobPtr).length()]; + Util.arrayCopyNonAtomic(KMByteBlob.cast(keyBlobPtr).getBuffer(), KMByteBlob.cast(keyBlobPtr).getStartOff(), + encTransportKey,(short)0, (short)encTransportKey.length); + short tagCount = 7; + short arrPtr = KMArray.instance(tagCount); + short boolTag = KMBoolTag.instance(KMType.NO_AUTH_REQUIRED); + short keySize = KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, KMInteger.uint_16((short)128)); + short byteBlob = KMByteBlob.instance((short)2); + KMByteBlob.cast(byteBlob).add((short)0, KMType.ECB); + KMByteBlob.cast(byteBlob).add((short)1, KMType.CBC); + short blockModeTag = KMEnumArrayTag.instance(KMType.BLOCK_MODE, byteBlob); + byteBlob = KMByteBlob.instance((short)2); + KMByteBlob.cast(byteBlob).add((short)0, KMType.PKCS7); + KMByteBlob.cast(byteBlob).add((short)1, KMType.PADDING_NONE); + short paddingMode = KMEnumArrayTag.instance(KMType.PADDING, byteBlob); + byteBlob = KMByteBlob.instance((short)2); + KMByteBlob.cast(byteBlob).add((short)0, KMType.ENCRYPT); + KMByteBlob.cast(byteBlob).add((short)1, KMType.DECRYPT); + short purpose = KMEnumArrayTag.instance(KMType.PURPOSE, byteBlob); + short tagIndex = 0; + KMArray.cast(arrPtr).add(tagIndex++, boolTag); + KMArray.cast(arrPtr).add(tagIndex++, keySize); + KMArray.cast(arrPtr).add(tagIndex++, blockModeTag); + KMArray.cast(arrPtr).add(tagIndex++, paddingMode); + KMArray.cast(arrPtr).add(tagIndex++, KMEnumTag.instance(KMType.ALGORITHM, KMType.AES)); + KMArray.cast(arrPtr).add(tagIndex++, purpose); + KMArray.cast(arrPtr).add(tagIndex++, KMBoolTag.instance(KMType.CALLER_NONCE)); + short keyParams = KMKeyParameters.instance(arrPtr); + short nullParams = KMArray.instance((short)0); + nullParams = KMKeyParameters.instance(nullParams); + short arr = KMArray.instance((short)12); + KMArray.cast(arr).add((short) 0, keyParams); // Key Params of wrapped key + KMArray.cast(arr).add((short) 1, KMEnum.instance(KMType.KEY_FORMAT,KMType.RAW)); // Key Format + KMArray.cast(arr).add((short) 2, KMByteBlob.instance(encWrappedKey,(short)0,(short)encWrappedKey.length)); // Wrapped Import Key Blob + KMArray.cast(arr).add((short) 3, KMByteBlob.instance(authTag,(short)0,(short)authTag.length)); // Auth Tag + KMArray.cast(arr).add((short) 4, KMByteBlob.instance(nonce,(short)0,(short)nonce.length)); // IV - Nonce + KMArray.cast(arr).add((short) 5, KMByteBlob.instance(encTransportKey,(short)0,(short)encTransportKey.length)); // Encrypted Transport Key + KMArray.cast(arr).add((short) 6, KMByteBlob.instance(wrappingKeyBlob,(short)0, (short)wrappingKeyBlob.length)); // Wrapping Key KeyBlob + KMArray.cast(arr).add((short) 7, KMByteBlob.instance(maskingKey,(short)0,(short)maskingKey.length)); // Masking Key + KMArray.cast(arr).add((short) 8, nullParams); // Un-wrapping Params + KMArray.cast(arr).add((short) 9, KMByteBlob.instance(authData,(short)0,(short)authData.length)); // Wrapped Key ASSOCIATED AUTH DATA + KMArray.cast(arr).add((short) 10, KMInteger.uint_8((byte)0)); // Password Sid + KMArray.cast(arr).add((short) 11, KMInteger.uint_8((byte)0)); // Biometric Sid + CommandAPDU apdu = encodeApdu((byte)INS_IMPORT_WRAPPED_KEY_CMD, arr); + // print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(apdu); + ret = KMArray.instance((short) 3); + KMArray.cast(ret).add((short) 0, KMInteger.exp()); + KMArray.cast(ret).add((short)1, KMByteBlob.exp()); + short inst = KMKeyCharacteristics.exp(); + KMArray.cast(ret).add((short) 2, inst); + byte[] respBuf = response.getBytes(); + short len = (short) respBuf.length; + ret = decoder.decode(ret, respBuf, (short) 0, len); + short error = KMInteger.cast(KMArray.cast(ret).get((short)0)).getShort(); + short keyBlobLength = KMByteBlob.cast(KMArray.cast(ret).get((short)1)).length(); + short keyCharacteristics = KMArray.cast(ret).get((short)2); + short hwParams = KMKeyCharacteristics.cast(keyCharacteristics).getHardwareEnforced(); + short swParams = KMKeyCharacteristics.cast(keyCharacteristics).getSoftwareEnforced(); + Assert.assertEquals(0x9000, response.getSW()); + Assert.assertEquals(error, KMError.OK); + short tag = KMKeyParameters.findTag(KMType.BOOL_TAG, KMType.NO_AUTH_REQUIRED, hwParams); + Assert.assertEquals(KMBoolTag.cast(tag).getVal(),0x01); + tag = KMKeyParameters.findTag(KMType.UINT_TAG, KMType.KEYSIZE, hwParams); + Assert.assertEquals(KMInteger.cast(KMIntegerTag.cast(tag).getValue()).getShort(), 128); + tag = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.PADDING, hwParams); + Assert.assertTrue(KMEnumArrayTag.cast(tag).contains(KMType.PKCS7)); + tag = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.BLOCK_MODE, hwParams); + Assert.assertTrue(KMEnumArrayTag.cast(tag).contains(KMType.ECB)); + tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ALGORITHM, hwParams); + Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.AES); + tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ORIGIN, hwParams); + Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.SECURELY_IMPORTED); + cleanUp(); + } + + @Test + public void testGetKeyCharacteristicsWithIdDataSuccess() { + init(); + byte[] clientId = "clientId".getBytes(); + byte[] appData = "appData".getBytes(); + short ret = generateRsaKey(clientId,appData); + short error = KMInteger.cast(KMArray.cast(ret).get((short)0)).getShort(); + Assert.assertEquals(error, KMError.OK); + short keyBlob = KMArray.cast(ret).get((short)1); + + short arrPtr = KMArray.instance((short)3); + KMArray.cast(arrPtr).add((short)0, keyBlob); + KMArray.cast(arrPtr).add((short)1, KMByteBlob.instance(clientId,(short)0, (short)clientId.length)); + KMArray.cast(arrPtr).add((short)2, KMByteBlob.instance(appData,(short)0, (short)appData.length)); + CommandAPDU apdu = encodeApdu((byte)INS_GET_KEY_CHARACTERISTICS_CMD, arrPtr); + // print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(apdu); + ret = KMArray.instance((short) 2); + KMArray.cast(ret).add((short) 0, KMInteger.exp()); + short inst = KMKeyCharacteristics.exp(); + KMArray.cast(ret).add((short) 1, inst); + byte[] respBuf = response.getBytes(); + short len = (short) respBuf.length; + ret = decoder.decode(ret, respBuf, (short) 0, len); + error = KMInteger.cast(KMArray.cast(ret).get((short)0)).getShort(); + Assert.assertEquals(error, KMError.OK); + cleanUp(); + } + + @Test + public void testGetKeyCharacteristicsSuccess() { + init(); + short ret = generateRsaKey(null, null); + short error = KMInteger.cast(KMArray.cast(ret).get((short)0)).getShort(); + Assert.assertEquals(error, KMError.OK); + short keyBlob = KMArray.cast(ret).get((short)1); + + short arrPtr = KMArray.instance((short)3); + KMArray.cast(arrPtr).add((short)0, keyBlob); + KMArray.cast(arrPtr).add((short)1, KMByteBlob.instance((short)0)); + KMArray.cast(arrPtr).add((short)2, KMByteBlob.instance((short)0)); + CommandAPDU apdu = encodeApdu((byte)INS_GET_KEY_CHARACTERISTICS_CMD, arrPtr); + // print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(apdu); + ret = KMArray.instance((short) 2); + KMArray.cast(ret).add((short) 0, KMInteger.exp()); + short inst = KMKeyCharacteristics.exp(); + KMArray.cast(ret).add((short) 1, inst); + byte[] respBuf = response.getBytes(); + short len = (short) respBuf.length; + ret = decoder.decode(ret, respBuf, (short) 0, len); + error = KMInteger.cast(KMArray.cast(ret).get((short)0)).getShort(); + Assert.assertEquals(error, KMError.OK); + cleanUp(); + } + + @Test + public void testDeleteKeySuccess() { + init(); + short ret = generateRsaKey(null, null); + short keyBlobPtr = KMArray.cast(ret).get((short)1); + byte[] keyBlob = new byte[KMByteBlob.cast(keyBlobPtr).length()]; + short len = KMByteBlob.cast(keyBlobPtr).getValues(keyBlob, (short)0); + ret = getKeyCharacteristics(keyBlobPtr); + short error = KMInteger.cast(KMArray.cast(ret).get((short)0)).getShort(); + 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)); + short err = KMByteBlob.cast(ret).get((short)1); + Assert.assertEquals(KMError.INVALID_KEY_BLOB,err); + + */ + cleanUp(); + } + + @Test + public void testDeleteAllKeySuccess() { + init(); + short ret1 = generateRsaKey(null, null); + short keyBlobPtr = KMArray.cast(ret1).get((short)1); + byte[] keyBlob1 = new byte[KMByteBlob.cast(keyBlobPtr).length()]; + short len = KMByteBlob.cast(keyBlobPtr).getValues(keyBlob1, (short)0); + short ret2 = generateRsaKey(null, null); + keyBlobPtr = KMArray.cast(ret2).get((short)1); + byte[] keyBlob2 = new byte[KMByteBlob.cast(keyBlobPtr).length()]; + len = KMByteBlob.cast(keyBlobPtr).getValues(keyBlob2, (short)0); + CommandAPDU apdu = new CommandAPDU(0x80, INS_DELETE_ALL_KEYS_CMD, 0x40, 0x00); + // print(commandAPDU.getBytes()); + 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 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(); + } + + private short deleteKey(short keyBlob) { + short arrPtr = KMArray.instance((short)1); + KMArray.cast(arrPtr).add((short)0, keyBlob); + CommandAPDU apdu = encodeApdu((byte)INS_DELETE_KEY_CMD, arrPtr); + // print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(apdu); + byte[] respBuf = response.getBytes(); + return respBuf[0]; + } + + private short abort(short opHandle) { + short arrPtr = KMArray.instance((short)1); + KMArray.cast(arrPtr).add((short)0, opHandle); + CommandAPDU apdu = encodeApdu((byte)INS_ABORT_OPERATION_CMD, arrPtr); + // print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(apdu); + byte[] respBuf = response.getBytes(); + return respBuf[0]; + } + + public short getKeyCharacteristics(short keyBlob){ + short arrPtr = KMArray.instance((short)3); + KMArray.cast(arrPtr).add((short)0, keyBlob); + KMArray.cast(arrPtr).add((short)1, KMByteBlob.instance((short)0)); + KMArray.cast(arrPtr).add((short)2, KMByteBlob.instance((short)0)); + CommandAPDU apdu = encodeApdu((byte)INS_GET_KEY_CHARACTERISTICS_CMD, arrPtr); + // print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(apdu); + 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); + byte[] respBuf = response.getBytes(); + short len = (short) respBuf.length; + if( len > 5) + ret = decoder.decode(ret, respBuf, (short) 0, len); + else + ret = KMByteBlob.instance(respBuf, (short)0, len); + return ret; + } + + @Test + public void testWithAesGcmWithUpdate(){ + init(); + testEncryptDecryptWithAesDes(KMType.AES, KMType.GCM, KMType.PADDING_NONE,true); + cleanUp(); + } + @Test + public void testWithAesEcbPkcs7WithUpdate(){ + init(); + testEncryptDecryptWithAesDes(KMType.AES, KMType.ECB, KMType.PKCS7,true); + cleanUp(); + } + + @Test + public void testWithAesCtrNoPadWithUpdate(){ + init(); + testEncryptDecryptWithAesDes(KMType.AES, KMType.CTR, KMType.PADDING_NONE,true); + cleanUp(); + } + + @Test + public void testWithAesCtrNoPad(){ + init(); + testEncryptDecryptWithAesDes(KMType.AES, KMType.CTR, KMType.PADDING_NONE,false); + cleanUp(); + } + + @Test + public void testWithAesEcbNoPadWithUpdate(){ + init(); + testEncryptDecryptWithAesDes(KMType.AES, KMType.ECB, KMType.PADDING_NONE,true); + cleanUp(); + } + @Test + public void testWithDesEcbPkcs7WithUpdate(){ + init(); + testEncryptDecryptWithAesDes(KMType.DES, KMType.ECB, KMType.PKCS7,true); + cleanUp(); + } + @Test + public void testWithDesEcbNoPadWithUpdate(){ + init(); + testEncryptDecryptWithAesDes(KMType.DES, KMType.ECB, KMType.PADDING_NONE,true); + cleanUp(); + } + @Test + public void testWithAesCbcPkcs7WithUpdate(){ + init(); + testEncryptDecryptWithAesDes(KMType.AES, KMType.CBC, KMType.PKCS7,true); + cleanUp(); + } + @Test + public void testWithAesCbcNoPadWithUpdate(){ + init(); + testEncryptDecryptWithAesDes(KMType.AES, KMType.CBC, KMType.PADDING_NONE,true); + cleanUp(); + } + @Test + public void testWithDesCbcPkcs7WithUpdate(){ + init(); + testEncryptDecryptWithAesDes(KMType.DES, KMType.CBC, KMType.PKCS7,true); + cleanUp(); + } + @Test + public void testWithDesCbcNoPadWithUpdate(){ + init(); + testEncryptDecryptWithAesDes(KMType.DES, KMType.CBC, KMType.PADDING_NONE,true); + cleanUp(); + } + + @Test + public void testWithAesEcbPkcs7(){ + init(); + testEncryptDecryptWithAesDes(KMType.AES, KMType.ECB, KMType.PKCS7,false); + cleanUp(); + } + @Test + public void testWithAesCbcPkcs7(){ + init(); + testEncryptDecryptWithAesDes(KMType.AES, KMType.CBC, KMType.PKCS7,false); + cleanUp(); + } + @Test + public void testWithAesEcbNoPad(){ + init(); + 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(); + testEncryptDecryptWithAesDes(KMType.DES, KMType.CBC, KMType.PADDING_NONE,false); + cleanUp(); + } + @Test + public void testWithDesEcbNoPad(){ + init(); + testEncryptDecryptWithAesDes(KMType.DES, KMType.ECB, KMType.PADDING_NONE,false); + cleanUp(); + } + @Test + public void testWithDesEcbPkcs7(){ + init(); + testEncryptDecryptWithAesDes(KMType.DES, KMType.ECB, KMType.PKCS7,false); + cleanUp(); + } + + @Test + public void testWithRsa256Oaep(){ + init(); + testEncryptDecryptWithRsa(KMType.SHA2_256, KMType.RSA_OAEP); + cleanUp(); + } + @Test + public void testWithRsaSha1Oaep(){ + init(); + testEncryptDecryptWithRsa(KMType.SHA1, KMType.RSA_OAEP); + cleanUp(); + } + + @Test + public void testWithRsaNonePkcs1(){ + init(); + testEncryptDecryptWithRsa(KMType.DIGEST_NONE, KMType.RSA_PKCS1_1_5_ENCRYPT); + cleanUp(); + } + + @Test + public void testWithRsaNoneNoPad(){ + init(); + testEncryptDecryptWithRsa(KMType.DIGEST_NONE, KMType.PADDING_NONE); + cleanUp(); + } + + // TODO Signing with no digest is not supported by crypto provider or javacard + @Test + public void testSignWithRsaNoneNoPad(){ + init(); + testSignVerifyWithRsa(KMType.DIGEST_NONE, KMType.PADDING_NONE,false, false); + cleanUp(); + } + + @Test + public void testSignWithRsaNonePkcs1(){ + init(); + testSignVerifyWithRsa(KMType.DIGEST_NONE, KMType.RSA_PKCS1_1_5_SIGN,false, false); + cleanUp(); + } + + @Test + public void testSignVerifyWithHmacSHA256WithUpdate(){ + init(); + testSignVerifyWithHmac(KMType.SHA2_256, true); + cleanUp(); + } + + @Test + public void testSignVerifyWithHmacSHA256(){ + init(); + testSignVerifyWithHmac(KMType.SHA2_256, false); + cleanUp(); + } + + @Test + public void testSignVerifyWithEcdsaSHA256WithUpdate(){ + init(); + testSignVerifyWithEcdsa(KMType.SHA2_256, true); + cleanUp(); + } + @Test + public void testSignVerifyWithEcdsaSHA256(){ + init(); + testSignVerifyWithEcdsa(KMType.SHA2_256, false); + cleanUp(); + } + @Test + public void testSignVerifyWithRsaSHA256Pkcs1(){ + init(); + testSignVerifyWithRsa(KMType.SHA2_256, KMType.RSA_PKCS1_1_5_SIGN,false, true); + cleanUp(); + } + @Test + public void testSignVerifyWithRsaSHA256Pss(){ + init(); + testSignVerifyWithRsa(KMType.SHA2_256, KMType.RSA_PSS,false, true); + cleanUp(); + } + + @Test + public void testSignVerifyWithRsaSHA256Pkcs1WithUpdate(){ + init(); + testSignVerifyWithRsa(KMType.SHA2_256, KMType.RSA_PKCS1_1_5_SIGN,true, true); + cleanUp(); + } + + @Test + public void testProvisionSuccess(){ + AID appletAID1 = AIDUtil.create("A000000062"); + simulator.installApplet(appletAID1, KMJCardSimApplet.class); + // Select applet + simulator.selectApplet(appletAID1); + // provision attest key + provisionCmd(simulator); + cleanUp(); + } + + @Test + public void testAttestRsaKey(){ + init(); + short key = generateRsaKey(null,null); + short keyBlobPtr = KMArray.cast(key).get((short)1); + byte[] keyBlob= new byte[KMByteBlob.cast(keyBlobPtr).length()]; + Util.arrayCopyNonAtomic( + KMByteBlob.cast(keyBlobPtr).getBuffer(), + KMByteBlob.cast(keyBlobPtr).getStartOff(), + keyBlob,(short)0, (short)keyBlob.length); + testAttestKey(keyBlob); + cleanUp(); + } + + @Test + public void testAttestEcKey(){ + init(); + short key = generateEcKey(null,null); + short keyBlobPtr = KMArray.cast(key).get((short)1); + byte[] keyBlob= new byte[KMByteBlob.cast(keyBlobPtr).length()]; + Util.arrayCopyNonAtomic( + KMByteBlob.cast(keyBlobPtr).getBuffer(), + KMByteBlob.cast(keyBlobPtr).getStartOff(), + keyBlob,(short)0, (short)keyBlob.length); + testAttestKey(keyBlob); + cleanUp(); + } + + public void testAttestKey(byte[] keyBlob){ + /* + short key = generateRsaKey(null,null); + short keyBlobPtr = KMArray.cast(key).get((short)1); + 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 arrPtr = KMArray.instance((short)2); + KMArray.cast(arrPtr).add((short)0, KMByteTag.instance(KMType.ATTESTATION_APPLICATION_ID, + KMByteBlob.instance(attAppId,(short)0,(short)attAppId.length))); + KMArray.cast(arrPtr).add((short)1, KMByteTag.instance(KMType.ATTESTATION_CHALLENGE, + KMByteBlob.instance(attChallenge,(short)0,(short)attChallenge.length))); + short keyParams = KMKeyParameters.instance(arrPtr); + short args = KMArray.instance((short)2); + KMArray.cast(args).add((short)0, KMByteBlob.instance(keyBlob,(short)0,(short)keyBlob.length)); + KMArray.cast(args).add((short)1, keyParams); + CommandAPDU apdu = encodeApdu((byte)INS_ATTEST_KEY_CMD, args); + //print(apdu.getBytes(),(short)0,(short)apdu.getBytes().length); + ResponseAPDU response = simulator.transmitCommand(apdu); + short ret = KMArray.instance((short) 2); + short arrBlobs = KMArray.instance((short)1); + KMArray.cast(arrBlobs).add((short)0, KMByteBlob.exp()); + KMArray.cast(ret).add((short)0, KMInteger.exp()); + KMArray.cast(ret).add((short)1, arrBlobs); + byte[] respBuf = response.getBytes(); + short len = (short) respBuf.length; + //(respBuf,(short)0,(short)respBuf.length); + ret = decoder.decode(ret, respBuf, (short) 0, len); + short error = KMInteger.cast(KMArray.cast(ret).get((short)0)).getShort(); + Assert.assertEquals(error, KMError.OK); + arrBlobs = KMArray.cast(ret).get((short)1); + short cert = KMArray.cast(arrBlobs).get((short)0); + //printCert(KMByteBlob.cast(cert).getBuffer(),KMByteBlob.cast(cert).getStartOff(),KMByteBlob.cast(cert).length()); + } + + @Test + public void testUpgradeKey(){ + init(); + short ret = generateHmacKey(null, null); + short keyBlobPtr = KMArray.cast(ret).get((short)1); + 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 keyCharacteristics = KMArray.cast(ret).get((short)2); + short hwParams = KMKeyCharacteristics.cast(keyCharacteristics).getHardwareEnforced(); + short swParams = KMKeyCharacteristics.cast(keyCharacteristics).getSoftwareEnforced(); + short osVersion = KMKeyParameters.findTag(KMType.UINT_TAG,KMType.OS_VERSION,hwParams); + osVersion = KMIntegerTag.cast(osVersion).getValue(); + short osPatch = KMKeyParameters.findTag(KMType.UINT_TAG,KMType.OS_PATCH_LEVEL,hwParams); + osPatch = KMIntegerTag.cast(osPatch).getValue(); + Assert.assertEquals(KMInteger.cast(osVersion).getShort(), 1); + Assert.assertEquals(KMInteger.cast(osPatch).getShort(), 1); + setBootParams(simulator,(short) 2,(short)2, (short)1, (short)1); + ret = upgradeKey(KMByteBlob.instance(keyBlob, (short)0, (short)keyBlob.length),null, null); + keyBlobPtr = KMArray.cast(ret).get((short)1); + ret = getKeyCharacteristics(keyBlobPtr); + keyCharacteristics = KMArray.cast(ret).get((short)1); + hwParams = KMKeyCharacteristics.cast(keyCharacteristics).getHardwareEnforced(); + osVersion = KMKeyParameters.findTag(KMType.UINT_TAG,KMType.OS_VERSION,hwParams); + osVersion = KMIntegerTag.cast(osVersion).getValue(); + osPatch = KMKeyParameters.findTag(KMType.UINT_TAG,KMType.OS_PATCH_LEVEL,hwParams); + osPatch = KMIntegerTag.cast(osPatch).getValue(); + Assert.assertEquals(KMInteger.cast(osVersion).getShort(), 2); + Assert.assertEquals(KMInteger.cast(osPatch).getShort(), 2); + cleanUp(); + } + + @Test + public void testDestroyAttIds(){ + init(); + CommandAPDU commandAPDU = new CommandAPDU(0x80, INS_DESTROY_ATT_IDS_CMD, 0x40, 0x00); + ResponseAPDU response = simulator.transmitCommand(commandAPDU); + byte[] respBuf = response.getBytes(); + Assert.assertEquals(respBuf[0], 0); + cleanUp(); + } + + private short upgradeKey(short keyBlobPtr, byte[] clientId, byte[] appData){ + short tagCount = 0; + short clientIdTag = 0; + short appDataTag = 0; + if(clientId != null) tagCount++; + if(appData != null) tagCount++; + short keyParams = KMArray.instance(tagCount); + short tagIndex=0; + if(clientId != null)KMArray.cast(keyBlobPtr).add(tagIndex++, + KMByteTag.instance(KMType.APPLICATION_ID, KMByteBlob.instance(clientId,(short)0,(short)clientId.length))); + if(appData != null)KMArray.cast(keyParams).add(tagIndex++, + KMByteTag.instance(KMType.APPLICATION_DATA, KMByteBlob.instance(appData,(short)0,(short)appData.length))); + keyParams = KMKeyParameters.instance(keyParams); + short arr = KMArray.instance((short)2); + KMArray.cast(arr).add((short)0,keyBlobPtr); + KMArray.cast(arr).add((short)1,keyParams); + CommandAPDU apdu = encodeApdu((byte)INS_UPGRADE_KEY_CMD, arr); + // print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(apdu); + short ret = KMArray.instance((short) 2); + KMArray.cast(ret).add((short) 0, KMInteger.exp()); + KMArray.cast(ret).add((short)1, KMByteBlob.exp()); + byte[] respBuf = response.getBytes(); + short len = (short) respBuf.length; + ret = decoder.decode(ret, respBuf, (short) 0, len); + short error = KMInteger.cast(KMArray.cast(ret).get((short)0)).getShort(); + Assert.assertEquals(error, KMError.OK); + return ret; + } + @Test + public void testSignVerifyWithRsaSHA256PssWithUpdate(){ + init(); + testSignVerifyWithRsa(KMType.SHA2_256, KMType.RSA_PSS,true, true); + cleanUp(); + } + @Test + public void testAbortOperation(){ + init(); + short aesDesKeyArr = generateAesDesKey(KMType.AES, (short)128,null, null, false);; + short keyBlobPtr = KMArray.cast(aesDesKeyArr).get((short)1); + byte[] keyBlob= new byte[KMByteBlob.cast(keyBlobPtr).length()]; + Util.arrayCopyNonAtomic(KMByteBlob.cast(keyBlobPtr).getBuffer(), KMByteBlob.cast(keyBlobPtr).getStartOff(), + keyBlob,(short)0, (short)keyBlob.length); + byte[] nonce = new byte[16]; + cryptoProvider.newRandomNumber(nonce,(short)0,(short)16); + short inParams = getAesDesParams(KMType.AES,KMType.ECB, KMType.PKCS7, nonce); + byte[] plainData= "Hello World 123!".getBytes(); + short ret = begin(KMType.ENCRYPT, KMByteBlob.instance(keyBlob,(short)0, (short)keyBlob.length), KMKeyParameters.instance(inParams), (short)0); + short opHandle = KMArray.cast(ret).get((short) 2); + opHandle = KMInteger.cast(opHandle).getShort(); + abort(KMInteger.uint_16(opHandle)); + short dataPtr = KMByteBlob.instance(plainData, (short) 0, (short) plainData.length); + ret = update(KMInteger.uint_16(opHandle), dataPtr, (short) 0, (short) 0, (short) 0); + Assert.assertEquals(KMError.INVALID_OPERATION_HANDLE,ret); + cleanUp(); + } + + public void testEncryptDecryptWithAesDes(byte alg, byte blockMode, byte padding, boolean update){ + short aesDesKeyArr; + boolean aesGcmFlag = false; + if(alg == KMType.AES){ + if(blockMode == KMType.GCM){ + aesDesKeyArr = generateAesGcmKey((short)128,null,null); + aesGcmFlag = true; + } else { + aesDesKeyArr = generateAesDesKey(alg, (short) 128, null, null, false); + } + } else{ + aesDesKeyArr = generateAesDesKey(alg, (short)168,null, null, false); + } + short keyBlobPtr = KMArray.cast(aesDesKeyArr).get((short)1); + byte[] keyBlob= new byte[KMByteBlob.cast(keyBlobPtr).length()]; + Util.arrayCopyNonAtomic(KMByteBlob.cast(keyBlobPtr).getBuffer(), KMByteBlob.cast(keyBlobPtr).getStartOff(), + keyBlob,(short)0, (short)keyBlob.length); + byte[] nonce = new byte[16]; + cryptoProvider.newRandomNumber(nonce,(short)0,(short)16); + short inParams = getAesDesParams(alg,blockMode, padding, nonce); + byte[] plainData= "Hello World 123!".getBytes(); + if(update) plainData= "Hello World 123! Hip Hip Hoorah!".getBytes(); + //Encrypt + short ret = processMessage(plainData, + KMByteBlob.instance(keyBlob,(short)0, (short)keyBlob.length), + KMType.ENCRYPT, + KMKeyParameters.instance(inParams), + (short)0,null,update, aesGcmFlag + ); + 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); + ret = processMessage(cipherData, + KMByteBlob.instance(keyBlob,(short)0, (short)keyBlob.length), + KMType.DECRYPT, + KMKeyParameters.instance(inParams), + (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); + } + + public void testEncryptDecryptWithRsa(byte digest, byte padding){ + short rsaKeyArr = generateRsaKey(null, null); + short keyBlobPtr = KMArray.cast(rsaKeyArr).get((short)1); + 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 = getRsaParams(digest, padding); + byte[] plainData = "Hello World 123!".getBytes(); + //Encrypt + short ret = processMessage(plainData, + KMByteBlob.instance(keyBlob,(short)0, (short)keyBlob.length), + KMType.ENCRYPT, + KMKeyParameters.instance(inParams), + (short)0,null,false, false + ); + inParams = getRsaParams(digest, padding); + keyBlobPtr = KMArray.cast(ret).get((short)2); + byte[] cipherData = new byte[KMByteBlob.cast(keyBlobPtr).length()]; + Util.arrayCopyNonAtomic(KMByteBlob.cast(keyBlobPtr).getBuffer(), KMByteBlob.cast(keyBlobPtr).getStartOff(), + cipherData,(short)0, (short)cipherData.length); + ret = processMessage(cipherData, + KMByteBlob.instance(keyBlob,(short)0, (short)keyBlob.length), + KMType.DECRYPT, + KMKeyParameters.instance(inParams), + (short)0,null,false,false + ); + keyBlobPtr = KMArray.cast(ret).get((short)2); + short len = KMByteBlob.cast(keyBlobPtr).length(); + short start = KMByteBlob.cast(keyBlobPtr).getStartOff(); + short equal = Util.arrayCompare(plainData,(short)0,KMByteBlob.cast(keyBlobPtr).getBuffer(), + (short)(start+len-plainData.length),(short)plainData.length); + Assert.assertTrue(equal == 0); + } + + public void testSignVerifyWithRsa(byte digest, byte padding, boolean update, boolean verifyFlag){ + short rsaKeyArr = generateRsaKey(null, null); + short keyBlobPtr = KMArray.cast(rsaKeyArr).get((short)1); + 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 = getRsaParams(digest, padding); + byte[] plainData = "Hello World 123!".getBytes(); + if(update) plainData= "Hello World 123! Hip Hip Hoorah!".getBytes(); + //Sign + short ret = processMessage(plainData, + KMByteBlob.instance(keyBlob,(short)0, (short)keyBlob.length), + KMType.SIGN, + KMKeyParameters.instance(inParams), + (short)0,null,update,false + ); + inParams = getRsaParams(digest, padding); + 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(), + signatureData,(short)0, (short)signatureData.length); + if(verifyFlag == false) { + Assert.assertEquals(signatureData.length,256); + return; + } + ret = processMessage(plainData, + KMByteBlob.instance(keyBlob,(short)0, (short)keyBlob.length), + KMType.VERIFY, + KMKeyParameters.instance(inParams), + (short)0,signatureData,update,false + ); + short error = KMInteger.cast(KMArray.cast(ret).get((short)0)).getShort(); + Assert.assertEquals(error, KMError.OK); + } + + public void testSignVerifyWithEcdsa(byte digest, boolean update){ + short ecKeyArr = generateEcKey(null, null); + short keyBlobPtr = KMArray.cast(ecKeyArr).get((short)1); + 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 = getEcParams(digest); + byte[] plainData = "Hello World 123!".getBytes(); + if(update) plainData= "Hello World 123! Hip Hip Hoorah!".getBytes(); + //Sign + short ret = processMessage(plainData, + KMByteBlob.instance(keyBlob,(short)0, (short)keyBlob.length), + KMType.SIGN, + KMKeyParameters.instance(inParams), + (short)0,null,update,false + ); + inParams = getEcParams(digest); + 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(), + signatureData,(short)0, (short)signatureData.length); + ret = processMessage(plainData, + KMByteBlob.instance(keyBlob,(short)0, (short)keyBlob.length), + KMType.VERIFY, + KMKeyParameters.instance(inParams), + (short)0,signatureData,update,false + ); + short error = KMInteger.cast(KMArray.cast(ret).get((short)0)).getShort(); + Assert.assertEquals(error, KMError.OK); + } + public void testSignVerifyWithHmac(byte digest, boolean update){ + short hmacKeyArr = generateHmacKey(null, null); + short keyBlobPtr = KMArray.cast(hmacKeyArr).get((short)1); + 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,true); + byte[] plainData = "Hello World 123!".getBytes(); + if(update) plainData= "Hello World 123! Hip Hip Hoorah!".getBytes(); + //Sign + short ret = processMessage(plainData, + KMByteBlob.instance(keyBlob,(short)0, (short)keyBlob.length), + KMType.SIGN, + KMKeyParameters.instance(inParams), + (short)0,null,update,false + ); + 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(), + signatureData,(short)0, (short)signatureData.length); + ret = processMessage(plainData, + KMByteBlob.instance(keyBlob,(short)0, (short)keyBlob.length), + KMType.VERIFY, + KMKeyParameters.instance(inParams), + (short)0,signatureData,update,false + ); + short error = KMInteger.cast(KMArray.cast(ret).get((short)0)).getShort(); + Assert.assertEquals(error, KMError.OK); + } + + private short getAesDesParams(byte alg, byte blockMode, byte padding, byte[] nonce) { + short inParams; + if(blockMode == KMType.GCM){ + inParams = KMArray.instance((short)5); + short byteBlob = KMByteBlob.instance((short)1); + KMByteBlob.cast(byteBlob).add((short)0, blockMode); + KMArray.cast(inParams).add((short)0, KMEnumArrayTag.instance(KMType.BLOCK_MODE, byteBlob)); + byteBlob = KMByteBlob.instance((short)1); + KMByteBlob.cast(byteBlob).add((short)0, padding); + KMArray.cast(inParams).add((short)1, KMEnumArrayTag.instance(KMType.PADDING, byteBlob)); + short nonceLen = 12; + byteBlob = KMByteBlob.instance(nonce,(short)0, nonceLen); + KMArray.cast(inParams).add((short)2, KMByteTag.instance(KMType.NONCE, byteBlob)); + short macLen = KMInteger.uint_16((short)128); + macLen = KMIntegerTag.instance(KMType.UINT_TAG,KMType.MAC_LENGTH,macLen); + KMArray.cast(inParams).add((short)3, macLen); + byte[] authData = "AuthData".getBytes(); + short associatedData = KMByteBlob.instance(authData,(short)0,(short)authData.length); + associatedData = KMByteTag.instance(KMType.ASSOCIATED_DATA,associatedData); + KMArray.cast(inParams).add((short)4, associatedData); + }else if(blockMode == KMType.ECB){ + inParams = KMArray.instance((short)2); + short byteBlob = KMByteBlob.instance((short)1); + KMByteBlob.cast(byteBlob).add((short)0, blockMode); + KMArray.cast(inParams).add((short)0, KMEnumArrayTag.instance(KMType.BLOCK_MODE, byteBlob)); + byteBlob = KMByteBlob.instance((short)1); + KMByteBlob.cast(byteBlob).add((short)0, padding); + KMArray.cast(inParams).add((short)1, KMEnumArrayTag.instance(KMType.PADDING, byteBlob)); + }else{ + inParams = KMArray.instance((short)3); + short byteBlob = KMByteBlob.instance((short)1); + KMByteBlob.cast(byteBlob).add((short)0, blockMode); + KMArray.cast(inParams).add((short)0, KMEnumArrayTag.instance(KMType.BLOCK_MODE, byteBlob)); + byteBlob = KMByteBlob.instance((short)1); + KMByteBlob.cast(byteBlob).add((short)0, padding); + KMArray.cast(inParams).add((short)1, KMEnumArrayTag.instance(KMType.PADDING, byteBlob)); + short nonceLen = 16; + if(alg == KMType.DES) nonceLen = 8; + byteBlob = KMByteBlob.instance(nonce,(short)0, nonceLen); + KMArray.cast(inParams).add((short)2, KMByteTag.instance(KMType.NONCE, byteBlob)); + } + return inParams; + } + + private short getRsaParams(byte digest, byte padding) { + short inParams = KMArray.instance((short)2); + short byteBlob = KMByteBlob.instance((short)1); + KMByteBlob.cast(byteBlob).add((short)0, digest); + KMArray.cast(inParams).add((short)0, KMEnumArrayTag.instance(KMType.DIGEST, byteBlob)); + byteBlob = KMByteBlob.instance((short)1); + KMByteBlob.cast(byteBlob).add((short)0, padding); + KMArray.cast(inParams).add((short)1, KMEnumArrayTag.instance(KMType.PADDING, byteBlob)); + return inParams; + } + + private short getEcParams(byte digest) { + short inParams = KMArray.instance((short)1); + short byteBlob = KMByteBlob.instance((short)1); + KMByteBlob.cast(byteBlob).add((short)0, digest); + KMArray.cast(inParams).add((short)0, KMEnumArrayTag.instance(KMType.DIGEST, byteBlob)); + return inParams; + } + 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*/160)); + if(sign) + KMArray.cast(inParams).add((short)1, macLength); + return inParams; + } + + public short processMessage( + byte[] data, + short keyBlob, + byte keyPurpose, + short inParams, + short hwToken, + byte[] signature, + boolean updateFlag, + boolean aesGcmFlag) { + short beginResp = begin(keyPurpose, keyBlob, inParams, hwToken); + short opHandle = KMArray.cast(beginResp).get((short) 2); + opHandle = KMInteger.cast(opHandle).getShort(); + short dataPtr = KMByteBlob.instance(data, (short) 0, (short) data.length); + short ret = KMType.INVALID_VALUE; + 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*/firstDataLen); + if(aesGcmFlag){ + byte[] authData = "AuthData".getBytes(); + short associatedData = KMByteBlob.instance(authData,(short)0,(short)authData.length); + associatedData = KMByteTag.instance(KMType.ASSOCIATED_DATA,associatedData); + inParams = KMArray.instance((short)1); + KMArray.cast(inParams).add((short)0, associatedData); + inParams = KMKeyParameters.instance(inParams); + } + ret = update(KMInteger.uint_16(opHandle), dataPtr, inParams, (short) 0, (short) 0); + dataPtr = KMArray.cast(ret).get((short) 3); + if (KMByteBlob.cast(dataPtr).length() > 0) { + Util.arrayCopyNonAtomic( + KMByteBlob.cast(dataPtr).getBuffer(), + KMByteBlob.cast(dataPtr).getStartOff(), + outputData, + (short) 0, + KMByteBlob.cast(dataPtr).length()); + len = KMByteBlob.cast(dataPtr).length(); + dataPtr = KMByteBlob.instance(data, len, (short) (data.length - len)); + }else{ + dataPtr = KMByteBlob.instance(data, (short)/*16*/firstDataLen, (short) (data.length - /*16*/firstDataLen)); + } + } + + if (keyPurpose == KMType.VERIFY) { + ret = finish(KMInteger.uint_16(opHandle), dataPtr, signature, (short) 0, (short) 0, (short) 0); + } else { + ret = finish(KMInteger.uint_16(opHandle), dataPtr, null, (short) 0, (short) 0, (short) 0); + } + if(len >0){ + dataPtr = KMArray.cast(ret).get((short)2); + if(KMByteBlob.cast(dataPtr).length() >0){ + Util.arrayCopyNonAtomic( + KMByteBlob.cast(dataPtr).getBuffer(), + KMByteBlob.cast(dataPtr).getStartOff(), + outputData, + len, + KMByteBlob.cast(dataPtr).length()); + len = (short)(len + KMByteBlob.cast(dataPtr).length()); + } + KMArray.cast(ret).add((short)2, KMByteBlob.instance(outputData,(short)0,len)); + } + return ret; + } + + public short begin(byte keyPurpose, short keyBlob, short keyParmas, short hwToken) { + short arrPtr = KMArray.instance((short)4); + KMArray.cast(arrPtr).add((short)0, KMEnum.instance(KMType.PURPOSE, keyPurpose)); + KMArray.cast(arrPtr).add((short)1, keyBlob); + KMArray.cast(arrPtr).add((short)2, keyParmas); + if(hwToken == 0) { + hwToken = KMHardwareAuthToken.instance(); + } + KMArray.cast(arrPtr).add((short)3, hwToken); + CommandAPDU apdu = encodeApdu((byte)INS_BEGIN_OPERATION_CMD, arrPtr); + //print(apdu.getBytes(),(short)0,(short)apdu.getBytes().length); + ResponseAPDU response = simulator.transmitCommand(apdu); + short ret = KMArray.instance((short) 3); + short outParams = KMKeyParameters.exp(); + KMArray.cast(ret).add((short)0, KMInteger.exp()); + KMArray.cast(ret).add((short)1, outParams); + KMArray.cast(ret).add((short)2, KMInteger.exp()); + byte[] respBuf = response.getBytes(); + short len = (short) respBuf.length; + if(len > 5){ + ret = decoder.decode(ret, respBuf, (short) 0, len); + short error = KMInteger.cast(KMArray.cast(ret).get((short)0)).getShort(); + Assert.assertEquals(error, KMError.OK); + return ret;}else{ + if(len == 3) return respBuf[0]; + if(len == 4) return respBuf[1]; + return Util.getShort(respBuf,(short)0); + } + } + + public short finish(short operationHandle, short data, byte[] signature, short inParams, short hwToken, short verToken) { + if(hwToken == 0) { + hwToken = KMHardwareAuthToken.instance(); + } + if(verToken == 0){ + verToken = KMVerificationToken.instance(); + } + short signatureTag; + if(signature == null){ + signatureTag = KMByteBlob.instance((short)0); + }else{ + signatureTag = KMByteBlob.instance(signature,(short)0,(short)signature.length); + } + if(inParams == 0){ + short arr = KMArray.instance((short)0); + inParams = KMKeyParameters.instance(arr); + } + short arrPtr = KMArray.instance((short)6); + KMArray.cast(arrPtr).add((short)0, operationHandle); + KMArray.cast(arrPtr).add((short)1, inParams); + KMArray.cast(arrPtr).add((short)2, data); + KMArray.cast(arrPtr).add((short)3, signatureTag); + KMArray.cast(arrPtr).add((short)4, hwToken); + KMArray.cast(arrPtr).add((short)5, verToken); + CommandAPDU apdu = encodeApdu((byte)INS_FINISH_OPERATION_CMD, arrPtr); + // print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(apdu); + short ret = KMArray.instance((short) 3); + short outParams = KMKeyParameters.exp(); + KMArray.cast(ret).add((short)0, KMInteger.exp()); + KMArray.cast(ret).add((short)1, outParams); + KMArray.cast(ret).add((short)2, KMByteBlob.exp()); + byte[] respBuf = response.getBytes(); + short len = (short) respBuf.length; + ret = decoder.decode(ret, respBuf, (short) 0, len); + short error = KMInteger.cast(KMArray.cast(ret).get((short)0)).getShort(); + Assert.assertEquals(error, KMError.OK); + return ret; + } + public short update(short operationHandle, short data, short inParams, short hwToken, short verToken) { + if(hwToken == 0) { + hwToken = KMHardwareAuthToken.instance(); + } + if(verToken == 0){ + verToken = KMVerificationToken.instance(); + } + if(inParams == 0){ + short arr = KMArray.instance((short)0); + inParams = KMKeyParameters.instance(arr); + } + short arrPtr = KMArray.instance((short)5); + KMArray.cast(arrPtr).add((short)0, operationHandle); + KMArray.cast(arrPtr).add((short)1, inParams); + KMArray.cast(arrPtr).add((short)2, data); + KMArray.cast(arrPtr).add((short)3, hwToken); + KMArray.cast(arrPtr).add((short)4, verToken); + CommandAPDU apdu = encodeApdu((byte)INS_UPDATE_OPERATION_CMD, arrPtr); + // print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(apdu); + short ret = KMArray.instance((short) 4); + short outParams = KMKeyParameters.exp(); + KMArray.cast(ret).add((short)0, KMInteger.exp()); + KMArray.cast(ret).add((short)1, KMInteger.exp()); + KMArray.cast(ret).add((short)2, outParams); + KMArray.cast(ret).add((short)3, KMByteBlob.exp()); + byte[] respBuf = response.getBytes(); + short len = (short) respBuf.length; + if (len > 5) { + ret = decoder.decode(ret, respBuf, (short) 0, len); + short error = KMInteger.cast(KMArray.cast(ret).get((short) 0)).getShort(); + Assert.assertEquals(error, KMError.OK); + }else{ + ret = respBuf[1]; + } + return ret; + } + + private void print(short blob){ + print(KMByteBlob.cast(blob).getBuffer(),KMByteBlob.cast(blob).getStartOff(),KMByteBlob.cast(blob).length()); + } + private void print(byte[] buf, short start, short length){ + StringBuilder sb = new StringBuilder(); + for(int i = start; i < (start+length); i++){ + sb.append(String.format(" 0x%02X", buf[i])) ; + } + System.out.println(sb.toString()); + } + private void printCert(byte[] buf, short start, short length){ + StringBuilder sb = new StringBuilder(); + for(int i = start; i < (start+length); i++){ + sb.append(String.format("%02X", buf[i])) ; + } + System.out.println(sb.toString()); + } + + +/* + @Test + public void testApdu(){ + init(); + byte[] cmd = {(byte)0x80,0x11,0x40,0x00,0x00,0x00,0x4C,(byte)0x83,(byte)0xA5,0x1A,0x70,0x00,0x01,(byte)0xF7,0x01,0x1A,0x10, + 0x00,0x00,0x02,0x03,0x1A,0x30,0x00,0x00,0x03,0x19,0x01,0x00,0x1A,0x20,0x00,0x00,0x01,0x42,0x02, + 0x03,0x1A,0x20,0x00,0x00,0x05,0x41,0x04,0x03,0x58,0x24,(byte)0x82,0x58,0x20,0x73,0x7C,0x2E,(byte)0xCD, + 0x7B,(byte)0x8D,0x19,0x40,(byte)0xBF,0x29,0x30,(byte)0xAA,(byte)0x9B,0x4E, + (byte)0xD3,(byte)0xFF,(byte)0x94,0x1E,(byte)0xED,0x09,0x36,0x6B, + (byte)0xC0,0x32,(byte)0x99,(byte)0x98,0x64,(byte)0x81,(byte)0xF3,(byte)0xA4,(byte)0xD8,0x59,0x40}; + CommandAPDU cmdApdu = new CommandAPDU(cmd); + ResponseAPDU resp = simulator.transmitCommand(cmdApdu); + short ret = KMArray.instance((short) 3); + KMArray.cast(ret).add((short) 0, KMInteger.exp()); + KMArray.cast(ret).add((short)1, KMByteBlob.exp()); + short inst = KMKeyCharacteristics.exp(); + KMArray.cast(ret).add((short) 2, inst); + byte[] respBuf = resp.getBytes(); + short len = (short) respBuf.length; + ret = decoder.decode(ret, respBuf, (short) 0, len); + short error = KMInteger.cast(KMArray.cast(ret).get((short)0)).getShort(); + short keyBlobLength = KMByteBlob.cast(KMArray.cast(ret).get((short)1)).length(); + short blobArr = extractKeyBlobArray(KMArray.cast(ret).get((short)1)); + short keyCharacteristics = KMArray.cast(ret).get((short)2); + short hwParams = KMKeyCharacteristics.cast(keyCharacteristics).getHardwareEnforced(); + short swParams = KMKeyCharacteristics.cast(keyCharacteristics).getSoftwareEnforced(); + cleanUp(); + } + */ +} diff --git a/Applet/JavaCardKeymaster.scr b/Applet/JavaCardKeymaster.scr deleted file mode 100644 index 6380faa2..00000000 --- a/Applet/JavaCardKeymaster.scr +++ /dev/null @@ -1,50 +0,0 @@ -output on; - -//create applet instance -0x80 0xB8 0x00 0x00 0x0c 0x0a 0xa0 0x00 0x00 0x00 0x62 0x03 0x01 0xc 0x01 0x01 0x00 0x7F; - -// Select JavaCardKeymaster //aid/A000000062/03010C0101 - 0x00 0xa4 0x04 0x00 0x0a 0xa0 0x00 0x00 0x00 0x62 0x03 0x01 0xc 0x01 0x01 0x7F; - -// Send provision command - this will change in future - 0x80 0x23 0x40 0x00 0x3B 0x83 0xA1 0x1A 0x10 0x00 0x00 0x02 0x01 0x00 0x58 0x30 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0A 0x0B 0x0C 0x0D 0x0E 0x0F 0x10 0x11 0x12 0x13 0x14 0x15 0x16 0x17 0x18 0x19 0x1A 0x1B 0x1C 0x1D 0x1E 0x1F 0x20 0x21 0x22 0x23 0x24 0x25 0x26 0x27 0x28 0x29 0x2A 0x2B 0x2C 0x2D 0x2E 0x2F 0x7F; - -// Send Set Boot Params command - 0x80 0x24 0x40 0x00 0x49 0x86 0x01 0x01 0x58 0x20 0x30 0x30 0x30 0x31 0x31 0x31 0x32 0x32 0x32 0x33 0x33 0x33 0x34 0x34 0x34 0x35 0x35 0x35 0x36 0x36 0x36 0x37 0x37 0x37 0x38 0x38 0x38 0x39 0x39 0x39 0x30 0x30 0x58 0x20 0x30 0x30 0x30 0x31 0x31 0x31 0x32 0x32 0x32 0x33 0x33 0x33 0x34 0x34 0x34 0x35 0x35 0x35 0x36 0x36 0x36 0x37 0x37 0x37 0x38 0x38 0x38 0x39 0x39 0x39 0x30 0x30 0x02 0x00 0x7F; - -// Send getHardwareInfo command - 0x80 0x1E 0x40 0x00 0x00 0x7F; - -// Send AddRngEntropy command - 0x80 0x18 0x40 0x00 0x23 0x81 0x58 0x20 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0A 0x0B 0x0C 0x0D 0x0E 0x0F 0x10 0x11 0x12 0x13 0x14 0x15 0x16 0x17 0x18 0x19 0x1A 0x1B 0x1C 0x1D 0x1E 0x1F 0x7F; - -// Generate Key - RSA Key command - 0x80 0x10 0x40 0x00 0x22 0x81 0xA4 0x1A 0x10 0x00 0x00 0x02 0x01 0x1A 0x30 0x00 0x00 0x03 0x19 0x08 0x00 0x1A 0x50 0x00 0x00 0xC8 0x1A 0x00 0x01 0x00 0x01 0x1A 0x20 0x00 0x00 0x01 0x42 0x02 0x03 0x7F; - -// Generate Key - AES Key command - 0x80 0x10 0x40 0x00 0x23 0x81 0xA4 0x1A 0x10 0x00 0x00 0x02 0x18 0x20 0x1A 0x30 0x00 0x00 0x03 0x19 0x01 0x00 0x1A 0x50 0x00 0x00 0xC8 0x1A 0x00 0x01 0x00 0x01 0x1A 0x20 0x00 0x00 0x01 0x42 0x02 0x03 0x7F; - -// Generate Key - ECC Key command - 0x80 0x10 0x40 0x00 0x22 0x81 0xA4 0x1A 0x10 0x00 0x00 0x02 0x03 0x1A 0x30 0x00 0x00 0x03 0x19 0x01 0x00 0x1A 0x50 0x00 0x00 0xC8 0x1A 0x00 0x01 0x00 0x01 0x1A 0x20 0x00 0x00 0x01 0x42 0x02 0x03 0x7F; - -// Generate Key - DES Key command - 0x80 0x10 0x40 0x00 0x22 0x81 0xA4 0x1A 0x10 0x00 0x00 0x02 0x18 0x21 0x1A 0x30 0x00 0x00 0x03 0x18 0xA8 0x1A 0x50 0x00 0x00 0xC8 0x1A 0x00 0x01 0x00 0x01 0x1A 0x20 0x00 0x00 0x01 0x42 0x02 0x03 0x7F; - -// Generate Key - HMAC Key command - 0x80 0x10 0x40 0x00 0x32 0x81 0xA6 0x1A 0x10 0x00 0x00 0x02 0x18 0x80 0x1A 0x30 0x00 0x01 0xF5 0x1A 0x01 0x02 0x03 0x04 0x1A 0x90 0x00 0x02 0x59 0x44 0x54 0x65 0x73 0x74 0x1A 0x30 0x00 0x00 0x08 0x18 0x80 0x1A 0x30 0x00 0x00 0x03 0x18 0x80 0x1A 0x20 0x00 0x00 0x05 0x41 0x04 0x7F; - -// Import RSA Key - 0x80 0x11 0x40 0x00 0xB4 0x83 0xA5 0x1A 0x10 0x00 0x00 0x02 0x01 0x1A 0x30 0x00 0x01 0xF5 0x1A 0x01 0x02 0x03 0x04 0x1A 0x90 0x00 0x02 0x59 0x44 0x54 0x65 0x73 0x74 0x1A 0x50 0x00 0x00 0xC8 0x1A 0x00 0x01 0x00 0x01 0x1A 0x70 0x00 0x01 0xF7 0x01 0x03 0x58 0x85 0x82 0x58 0x40 0x80 0x9A 0x87 0x4C 0xE1 0xA0 0x71 0x44 0xAE 0x45 0xDD 0x8D 0x1C 0x05 0xB4 0xD0 0x44 0x23 0xDD 0x42 0xA7 0xC9 0x53 0x44 0xAC 0x31 0x4A 0x22 0x4A 0x02 0x65 0xA0 0xAA 0x21 0xA8 0x30 0x94 0x7D 0x13 0xA1 0xBC 0x89 0x81 0xB5 0x54 0xDE 0x75 0x82 0xB9 0x0B 0x1A 0x7A 0x81 0x0C 0x51 0xE0 0x2F 0x91 0x97 0xD4 0xE8 0x33 0x27 0x61 0x58 0x40 0x92 0x6C 0x79 0x17 0xBB 0x36 0x6F 0xB7 0x58 0x25 0x84 0x98 0xA9 0x56 0x07 0xE6 0x07 0xF6 0x26 0x92 0x15 0xF6 0x21 0x9F 0x6C 0xF0 0xB4 0xE7 0x20 0x42 0xAC 0xB6 0xD8 0x30 0x61 0x06 0xC9 0x3B 0x30 0x67 0x1E 0x8D 0x74 0x11 0x8B 0x06 0x98 0xAB 0x8D 0x6A 0x6C 0xCD 0xB7 0x2F 0xC3 0xA8 0x30 0xC7 0x68 0x03 0x4F 0x72 0xC7 0x5B 0x7F; - -// Import EC Key - 0x80 0x11 0x40 0x00 0x7D 0x83 0xA4 0x1A 0x10 0x00 0x00 0x02 0x03 0x1A 0x30 0x00 0x01 0xF5 0x1A 0x01 0x02 0x03 0x04 0x1A 0x90 0x00 0x02 0x59 0x44 0x54 0x65 0x73 0x74 0x1A 0x50 0x00 0x00 0xC8 0x1A 0x00 0x01 0x00 0x01 0x03 0x58 0x54 0x83 0x58 0x18 0xA6 0x68 0xDE 0xEC 0x65 0x6C 0xFB 0xEE 0xAA 0x43 0xEF 0x97 0x9D 0x10 0x82 0xF0 0x99 0x5F 0x10 0xF3 0xEE 0x9C 0x38 0x57 0x58 0x31 0x04 0x3A 0xF8 0xF4 0xFA 0x1F 0xE4 0x4D 0x62 0xA1 0xCD 0x26 0x8E 0x1A 0x5A 0xAA 0xF5 0xA8 0x94 0xE3 0x8B 0x4C 0xCE 0x49 0xA1 0x57 0x25 0x81 0x6D 0xBE 0x5C 0x3B 0x07 0x95 0xB6 0x89 0x24 0x6E 0x9D 0x25 0x22 0xE6 0x5F 0x41 0xCC 0x59 0xCE 0x25 0x0C 0x1A 0x10 0x00 0x00 0x0A 0x01 0x7F; - -// Import AES Key - 0x80 0x11 0x40 0x00 0x3F 0x83 0xA5 0x1A 0x10 0x00 0x00 0x02 0x18 0x20 0x1A 0x30 0x00 0x01 0xF5 0x1A 0x01 0x02 0x03 0x04 0x1A 0x90 0x00 0x02 0x59 0x44 0x54 0x65 0x73 0x74 0x1A 0x30 0x00 0x00 0x03 0x18 0x80 0x1A 0x20 0x00 0x00 0x05 0x41 0x04 0x03 0x52 0x81 0x50 0x95 0xE6 0x79 0x36 0x64 0xA5 0xEC 0x72 0xBF 0x01 0x4C 0x83 0x6C 0xCD 0xCF 0x51 0x7F; - -// Import Hmac Key - 0x80 0x11 0x40 0x00 0x46 0x83 0xA6 0x1A 0x10 0x00 0x00 0x02 0x18 0x80 0x1A 0x30 0x00 0x01 0xF5 0x1A 0x01 0x02 0x03 0x04 0x1A 0x90 0x00 0x02 0x59 0x44 0x54 0x65 0x73 0x74 0x1A 0x30 0x00 0x00 0x03 0x18 0x80 0x1A 0x20 0x00 0x00 0x05 0x41 0x04 0x1A 0x30 0x00 0x00 0x08 0x18 0x80 0x03 0x52 0x81 0x50 0xFC 0xA6 0x8F 0x58 0x68 0x93 0xDE 0xD0 0xC0 0x74 0x1C 0x6F 0x1D 0x39 0x2E 0x4A 0x7F; - -// Import Des Key - 0x80 0x11 0x40 0x00 0x3F 0x83 0xA5 0x1A 0x10 0x00 0x00 0x02 0x18 0x21 0x1A 0x30 0x00 0x01 0xF5 0x1A 0x01 0x02 0x03 0x04 0x1A 0x90 0x00 0x02 0x59 0x44 0x54 0x65 0x73 0x74 0x1A 0x30 0x00 0x00 0x03 0x18 0xA8 0x1A 0x20 0x00 0x00 0x05 0x41 0x04 0x03 0x52 0x81 0x50 0x8B 0xD4 0xD5 0x84 0x37 0x39 0xC0 0x1B 0xDB 0xED 0x3C 0x68 0x99 0x3A 0xDC 0x3D 0x7F; - diff --git a/Applet/README.md b/Applet/README.md index 5cc80048..76af5b05 100644 --- a/Applet/README.md +++ b/Applet/README.md @@ -1,6 +1,19 @@ -# JavaCardKeymaster Applet - -This directory contains the implementation of the Keymaster 4.1 -interface, in the form of a JavaCard 3.1 applet which runs in a secure -element. It must be deployed in conjuction with the associated HAL, -which serves to intermediate between Android Keystore and this applet. +# JavaCardKeymaster Applet + +This directory contains the implementation of the Keymaster 4.1 +interface, in the form of a JavaCard 3.0.5 applet which runs in a secure +element. It must be deployed in conjuction with the associated HAL, +which serves to intermediate between Android Keystore and this applet. + +# Supported Features! + + - Support for AndroidSEProvider, which is compliant to JavaCard platform, Classic Edition 3.0.5. + - Keymaster 4.1 supported functions for required VTS compliance. + - Support for SE Provisioning and bootup + - Support for Global platoform Amendment H in AndroidSEProvider. + - Unit test using JCardSim. + +#### Building for source +- Install Javacard 3.0.5 classic sdk. +- set JC_HOME_SIMULATOR environment variable to the installed sdk. +- Give ant build from Applet folder. diff --git a/Applet/api_export_files_3.0.5/javacardx/framework/util/intx/javacard/intx.exp b/Applet/api_export_files_3.0.5/javacardx/framework/util/intx/javacard/intx.exp deleted file mode 100644 index ed00a395..00000000 Binary files a/Applet/api_export_files_3.0.5/javacardx/framework/util/intx/javacard/intx.exp and /dev/null differ diff --git a/Applet/api_export_files_3.1.0/java/io/javacard/io.exp b/Applet/api_export_files_3.1.0/java/io/javacard/io.exp deleted file mode 100644 index 36b9d18b..00000000 Binary files a/Applet/api_export_files_3.1.0/java/io/javacard/io.exp and /dev/null differ diff --git a/Applet/api_export_files_3.1.0/java/lang/javacard/lang.exp b/Applet/api_export_files_3.1.0/java/lang/javacard/lang.exp deleted file mode 100644 index 272eebba..00000000 Binary files a/Applet/api_export_files_3.1.0/java/lang/javacard/lang.exp and /dev/null differ diff --git a/Applet/api_export_files_3.1.0/java/rmi/javacard/rmi.exp b/Applet/api_export_files_3.1.0/java/rmi/javacard/rmi.exp deleted file mode 100644 index 8c695237..00000000 Binary files a/Applet/api_export_files_3.1.0/java/rmi/javacard/rmi.exp and /dev/null differ diff --git a/Applet/api_export_files_3.1.0/javacardx/apdu/javacard/apdu.exp b/Applet/api_export_files_3.1.0/javacardx/apdu/javacard/apdu.exp deleted file mode 100644 index ce4ac0c9..00000000 Binary files a/Applet/api_export_files_3.1.0/javacardx/apdu/javacard/apdu.exp and /dev/null differ diff --git a/Applet/build.xml b/Applet/build.xml index 41181873..c08e095f 100644 --- a/Applet/build.xml +++ b/Applet/build.xml @@ -1,188 +1,26 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - - - - - - - - - - - + + + - - + + + + \ No newline at end of file diff --git a/Applet/default.output b/Applet/default.output deleted file mode 100644 index 6e4f103f..00000000 --- a/Applet/default.output +++ /dev/null @@ -1,20 +0,0 @@ -CLA: 00, INS: a4, P1: 04, P2: 00, Lc: 09, a0, 00, 00, 00, 62, 03, 01, 08, 01, Le: 00, SW1: 90, SW2: 00 -CAP file download section. Output suppressed. -OUTPUT OFF; -OUTPUT ON; -CLA: 80, INS: b8, P1: 00, P2: 00, Lc: 0c, 0a, a0, 00, 00, 00, 62, 03, 01, 0c, 01, 01, 00, Le: 0a, a0, 00, 00, 00, 62, 03, 01, 0c, 01, 01, SW1: 90, SW2: 00 -CLA: 00, INS: a4, P1: 04, P2: 00, Lc: 0a, a0, 00, 00, 00, 62, 03, 01, 0c, 01, 01, Le: 00, SW1: 90, SW2: 00 -CLA: 80, INS: 23, P1: 40, P2: 00, Lc: 3b, 83, a1, 1a, 10, 00, 00, 02, 01, 00, 58, 30, 00, 01, 02, 03, 04, 05, 06, 07, 08, 09, 0a, 0b, 0c, 0d, 0e, 0f, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 1a, 1b, 1c, 1d, 1e, 1f, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 2a, 2b, 2c, 2d, 2e, 2f, Le: 00, SW1: 69, SW2: 85 -CLA: 80, INS: 24, P1: 40, P2: 00, Lc: 49, 86, 01, 01, 58, 20, 30, 30, 30, 31, 31, 31, 32, 32, 32, 33, 33, 33, 34, 34, 34, 35, 35, 35, 36, 36, 36, 37, 37, 37, 38, 38, 38, 39, 39, 39, 30, 30, 58, 20, 30, 30, 30, 31, 31, 31, 32, 32, 32, 33, 33, 33, 34, 34, 34, 35, 35, 35, 36, 36, 36, 37, 37, 37, 38, 38, 38, 39, 39, 39, 30, 30, 02, 00, Le: 00, SW1: 90, SW2: 00 -CLA: 80, INS: 1e, P1: 40, P2: 00, Lc: 00, Le: 00, SW1: 69, SW2: 86 -CLA: 80, INS: 18, P1: 40, P2: 00, Lc: 23, 81, 58, 20, 00, 01, 02, 03, 04, 05, 06, 07, 08, 09, 0a, 0b, 0c, 0d, 0e, 0f, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 1a, 1b, 1c, 1d, 1e, 1f, Le: 00, SW1: 69, SW2: 86 -CLA: 80, INS: 10, P1: 40, P2: 00, Lc: 22, 81, a4, 1a, 10, 00, 00, 02, 01, 1a, 30, 00, 00, 03, 19, 08, 00, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 20, 00, 00, 01, 42, 02, 03, Le: 00, SW1: 69, SW2: 86 -CLA: 80, INS: 10, P1: 40, P2: 00, Lc: 23, 81, a4, 1a, 10, 00, 00, 02, 18, 20, 1a, 30, 00, 00, 03, 19, 01, 00, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 20, 00, 00, 01, 42, 02, 03, Le: 00, SW1: 69, SW2: 86 -CLA: 80, INS: 10, P1: 40, P2: 00, Lc: 22, 81, a4, 1a, 10, 00, 00, 02, 03, 1a, 30, 00, 00, 03, 19, 01, 00, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 20, 00, 00, 01, 42, 02, 03, Le: 00, SW1: 69, SW2: 86 -CLA: 80, INS: 10, P1: 40, P2: 00, Lc: 22, 81, a4, 1a, 10, 00, 00, 02, 18, 21, 1a, 30, 00, 00, 03, 18, a8, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 20, 00, 00, 01, 42, 02, 03, Le: 00, SW1: 69, SW2: 86 -CLA: 80, INS: 10, P1: 40, P2: 00, Lc: 32, 81, a6, 1a, 10, 00, 00, 02, 18, 80, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, 1a, 90, 00, 02, 59, 44, 54, 65, 73, 74, 1a, 30, 00, 00, 08, 18, 80, 1a, 30, 00, 00, 03, 18, 80, 1a, 20, 00, 00, 05, 41, 04, Le: 00, SW1: 69, SW2: 85 -CLA: 80, INS: 11, P1: 40, P2: 00, Lc: b4, 83, a5, 1a, 10, 00, 00, 02, 01, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, 1a, 90, 00, 02, 59, 44, 54, 65, 73, 74, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 70, 00, 01, f7, 01, 03, 58, 85, 82, 58, 40, 80, 9a, 87, 4c, e1, a0, 71, 44, ae, 45, dd, 8d, 1c, 05, b4, d0, 44, 23, dd, 42, a7, c9, 53, 44, ac, 31, 4a, 22, 4a, 02, 65, a0, aa, 21, a8, 30, 94, 7d, 13, a1, bc, 89, 81, b5, 54, de, 75, 82, b9, 0b, 1a, 7a, 81, 0c, 51, e0, 2f, 91, 97, d4, e8, 33, 27, 61, 58, 40, 92, 6c, 79, 17, bb, 36, 6f, b7, 58, 25, 84, 98, a9, 56, 07, e6, 07, f6, 26, 92, 15, f6, 21, 9f, 6c, f0, b4, e7, 20, 42, ac, b6, d8, 30, 61, 06, c9, 3b, 30, 67, 1e, 8d, 74, 11, 8b, 06, 98, ab, 8d, 6a, 6c, cd, b7, 2f, c3, a8, 30, c7, 68, 03, 4f, 72, c7, 5b, Le: 00, SW1: 69, SW2: 85 -CLA: 80, INS: 11, P1: 40, P2: 00, Lc: 7d, 83, a4, 1a, 10, 00, 00, 02, 03, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, 1a, 90, 00, 02, 59, 44, 54, 65, 73, 74, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 03, 58, 54, 83, 58, 18, a6, 68, de, ec, 65, 6c, fb, ee, aa, 43, ef, 97, 9d, 10, 82, f0, 99, 5f, 10, f3, ee, 9c, 38, 57, 58, 31, 04, 3a, f8, f4, fa, 1f, e4, 4d, 62, a1, cd, 26, 8e, 1a, 5a, aa, f5, a8, 94, e3, 8b, 4c, ce, 49, a1, 57, 25, 81, 6d, be, 5c, 3b, 07, 95, b6, 89, 24, 6e, 9d, 25, 22, e6, 5f, 41, cc, 59, ce, 25, 0c, 1a, 10, 00, 00, 0a, 01, Le: 00, SW1: 69, SW2: 85 -CLA: 80, INS: 11, P1: 40, P2: 00, Lc: 3f, 83, a5, 1a, 10, 00, 00, 02, 18, 20, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, 1a, 90, 00, 02, 59, 44, 54, 65, 73, 74, 1a, 30, 00, 00, 03, 18, 80, 1a, 20, 00, 00, 05, 41, 04, 03, 52, 81, 50, 95, e6, 79, 36, 64, a5, ec, 72, bf, 01, 4c, 83, 6c, cd, cf, 51, Le: 00, SW1: 69, SW2: 85 -CLA: 80, INS: 11, P1: 40, P2: 00, Lc: 46, 83, a6, 1a, 10, 00, 00, 02, 18, 80, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, 1a, 90, 00, 02, 59, 44, 54, 65, 73, 74, 1a, 30, 00, 00, 03, 18, 80, 1a, 20, 00, 00, 05, 41, 04, 1a, 30, 00, 00, 08, 18, 80, 03, 52, 81, 50, fc, a6, 8f, 58, 68, 93, de, d0, c0, 74, 1c, 6f, 1d, 39, 2e, 4a, Le: 00, SW1: 69, SW2: 85 -CLA: 80, INS: 11, P1: 40, P2: 00, Lc: 3f, 83, a5, 1a, 10, 00, 00, 02, 18, 21, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, 1a, 90, 00, 02, 59, 44, 54, 65, 73, 74, 1a, 30, 00, 00, 03, 18, a8, 1a, 20, 00, 00, 05, 41, 04, 03, 52, 81, 50, 8b, d4, d5, 84, 37, 39, c0, 1b, db, ed, 3c, 68, 99, 3a, dc, 3d, Le: 00, SW1: 69, SW2: 85 diff --git a/Applet/powerdown.scr b/Applet/powerdown.scr deleted file mode 100644 index 7dca615e..00000000 --- a/Applet/powerdown.scr +++ /dev/null @@ -1,4 +0,0 @@ - -// output on; - -powerdown; \ No newline at end of file diff --git a/Applet/powerup.scr b/Applet/powerup.scr deleted file mode 100644 index 79990dc4..00000000 --- a/Applet/powerup.scr +++ /dev/null @@ -1,9 +0,0 @@ -powerup; - -// Select the installer applet -0x00 0xA4 0x04 0x00 0x09 0xA0 0x00 0x00 0x00 0x62 0x03 0x01 0x08 0x01 0x7F; - -// Turn output off during CAP file download -echo "CAP file download section. Output suppressed."; - -output off; diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMArray.java b/Applet/src/com/android/javacard/keymaster/KMArray.java similarity index 100% rename from Applet/Applet/src/com/android/javacard/keymaster/KMArray.java rename to Applet/src/com/android/javacard/keymaster/KMArray.java diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMAttestationCert.java b/Applet/src/com/android/javacard/keymaster/KMAttestationCert.java similarity index 57% rename from Applet/Applet/src/com/android/javacard/keymaster/KMAttestationCert.java rename to Applet/src/com/android/javacard/keymaster/KMAttestationCert.java index 07c68357..f15fbfcb 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMAttestationCert.java +++ b/Applet/src/com/android/javacard/keymaster/KMAttestationCert.java @@ -11,7 +11,7 @@ public interface KMAttestationCert { /** * Set verified boot hash. * - * @param obj Ths is a KMByteBlob containing hash + * @param obj This is a KMByteBlob containing hash * @return instance of KMAttestationCert */ KMAttestationCert verifiedBootHash(short obj); @@ -19,7 +19,7 @@ public interface KMAttestationCert { /** * Set verified boot key received during booting up. * - * @param obj Ths is a KMByteBlob containing verified boot key. + * @param obj This is a KMByteBlob containing verified boot key. * @return instance of KMAttestationCert */ KMAttestationCert verifiedBootKey(short obj); @@ -27,7 +27,7 @@ public interface KMAttestationCert { /** * Set verified boot state received during booting up. * - * @param val Ths is a byte containing verified boot state value. + * @param val This is a byte containing verified boot state value. * @return instance of KMAttestationCert */ KMAttestationCert verifiedBootState(byte val); @@ -35,7 +35,7 @@ public interface KMAttestationCert { /** * Set authentication key Id from CA Certificate set during provisioning. * - * @param obj Ths is a KMByteBlob containing authentication Key Id. + * @param obj This is a KMByteBlob containing authentication Key Id. * @return instance of KMAttestationCert */ KMAttestationCert authKey(short obj); @@ -43,32 +43,52 @@ public interface KMAttestationCert { /** * Set uniqueId received from CA certificate during provisioning. * - * @param obj Ths is a KMByteBlob containing uniqueId. - * @return instance of KMAttestationCert - */ - KMAttestationCert uniqueId(short obj); + * @param scratchpad Buffer to store intermediate results. + * @param scratchPadOff Start offset of the scratchpad buffer. + * @param creationTime This buffer contains the CREATION_TIME value. + * @param creationTimeOff Start offset of creattionTime buffer. + * @param creationTimeLen Length of the creationTime buffer. + * @param attestAppId This buffer contains the ATTESTATION_APPLICATION_ID value. + * @param attestAppIdOff Start offset of the attestAppId buffer. + * @param attestAppIdLen Length of the attestAppId buffer. + * @param resetSinceIdRotation This holds the information of RESET_SINCE_ID_ROTATION. + * @param key This buffer contains the master secret. + * @param keyOff Start offset of the master key. + * @param keyLen Length of the master key. + * @return instance of KMAttestationCert. + */ + KMAttestationCert makeUniqueId(byte[] scratchpad, short scratchPadOff, byte[] creationTime, + short creationTimeOff, short creationTimeLen, byte[] attestAppId, + short attestAppIdOff, short attestAppIdLen, byte resetSinceIdRotation, + byte[] key, short keyOff, short keyLen); /** * Set start time received from creation/activation time tag. Used for certificate's valid period. * - * @param obj Ths is a KMByteBlob containing start time. - * @return instance of KMAttestationCert + * @param obj This is a KMByteBlob object containing start time. + * @param scratchpad Buffer to store intermediate results. + * @return instance of KMAttestationCert. */ - KMAttestationCert notBefore(short obj); + KMAttestationCert notBefore(short obj, byte[] scratchpad); + /** * Set expiry time received from expiry time tag or ca certificates expiry time. * Used for certificate's valid period. * - * @param obj Ths is a KMByteBlob containing expiry time. + * @param usageExpiryTimeObj This is a KMByteBlob containing expiry time. + * @param certExpirtyTimeObj This is a KMByteblob containing expirty time extracted from certificate. + * @param scratchpad Buffer to store intermediate results. + * @param offset Variable used to store intermediate results. * @return instance of KMAttestationCert */ - KMAttestationCert notAfter(short obj); + KMAttestationCert notAfter(short usageExpiryTimeObj, + short certExpirtyTimeObj, byte[] scratchPad, short offset); /** * Set device lock status received during booting time or due to device lock command. * - * @param val Ths is true if device is locked. + * @param val This is true if device is locked. * @return instance of KMAttestationCert */ KMAttestationCert deviceLocked(boolean val); @@ -76,7 +96,7 @@ public interface KMAttestationCert { /** * Set public key to be attested received from attestKey command. * - * @param obj Ths is KMByteBlob containing the public key. + * @param obj This is KMByteBlob containing the public key. * @return instance of KMAttestationCert */ KMAttestationCert publicKey(short obj); @@ -84,7 +104,7 @@ public interface KMAttestationCert { /** * Set attestation challenge received from attestKey command. * - * @param obj Ths is KMByteBlob containing the attestation challenge. + * @param obj This is KMByteBlob containing the attestation challenge. * @return instance of KMAttestationCert */ KMAttestationCert attestationChallenge(short obj); @@ -103,7 +123,7 @@ public interface KMAttestationCert { /** * Set ASN.1 encoded X509 issuer field received from attestation key CA cert. * - * @param obj Ths is KMByteBlob containing the issuer. + * @param obj This is KMByteBlob containing the issuer. * @return instance of KMAttestationCert */ KMAttestationCert issuer(short obj); @@ -111,9 +131,9 @@ public interface KMAttestationCert { /** * Set byte buffer to be used to generate certificate. * - * @param buf Ths is byte[] buffer. - * @param bufStart Ths is short start offset. - * @param maxLen Ths is short length of the buffer. + * @param buf This is byte[] buffer. + * @param bufStart This is short start offset. + * @param maxLen This is short length of the buffer. * @return instance of KMAttestationCert */ KMAttestationCert buffer(byte[] buf, short bufStart, short maxLen); @@ -121,11 +141,10 @@ public interface KMAttestationCert { /** * Set signing key to be used to sign the cert. * - * @param privateKey Ths is rsa 2048 bit private key. - * @param modulus Ths is rsa 2048 bit modulus. + * @param privateKey This is ECPrivateKey with curve P-256. * @return instance of KMAttestationCert */ - KMAttestationCert signingKey(short privateKey, short modulus); + KMAttestationCert signingKey(short privateKey); /** * Get the start of the certificate diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMBoolTag.java b/Applet/src/com/android/javacard/keymaster/KMBoolTag.java similarity index 100% rename from Applet/Applet/src/com/android/javacard/keymaster/KMBoolTag.java rename to Applet/src/com/android/javacard/keymaster/KMBoolTag.java diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMByteBlob.java b/Applet/src/com/android/javacard/keymaster/KMByteBlob.java similarity index 100% rename from Applet/Applet/src/com/android/javacard/keymaster/KMByteBlob.java rename to Applet/src/com/android/javacard/keymaster/KMByteBlob.java diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMByteTag.java b/Applet/src/com/android/javacard/keymaster/KMByteTag.java similarity index 100% rename from Applet/Applet/src/com/android/javacard/keymaster/KMByteTag.java rename to Applet/src/com/android/javacard/keymaster/KMByteTag.java diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMDecoder.java b/Applet/src/com/android/javacard/keymaster/KMDecoder.java similarity index 97% rename from Applet/Applet/src/com/android/javacard/keymaster/KMDecoder.java rename to Applet/src/com/android/javacard/keymaster/KMDecoder.java index a1ced7cd..d74e35d0 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMDecoder.java +++ b/Applet/src/com/android/javacard/keymaster/KMDecoder.java @@ -192,8 +192,6 @@ private short decodeKeyParam(short exp) { private short decodeEnumArrayTag(short exp) { readTagKey(KMEnumArrayTag.cast(exp).getTagType()); - // The value must be byte blob - // TODO check this out. return KMEnumArrayTag.instance(this.tagKey, decode(KMEnumArrayTag.cast(exp).getValues())); } @@ -250,7 +248,6 @@ private short decodeArray(short exp) { private short decodeEnumTag(short exp) { readTagKey(KMEnumTag.cast(exp).getTagType()); // Enum Tag value will always be integer with max 1 byte length. - // TODO Check this out. if ((buffer[startOff] & MAJOR_TYPE_MASK) != UINT_TYPE) { ISOException.throwIt(ISO7816.SW_DATA_INVALID); } @@ -273,7 +270,6 @@ private short decodeEnumTag(short exp) { private short decodeBoolTag(short exp) { readTagKey(KMBoolTag.cast(exp).getTagType()); // BOOL Tag is a leaf node and it must always have tiny encoded uint value = 1. - // TODO check this out. if ((buffer[startOff] & MAJOR_TYPE_MASK) != UINT_TYPE) { ISOException.throwIt(ISO7816.SW_DATA_INVALID); } @@ -407,4 +403,14 @@ private void incrementStartOff(short inc) { ISOException.throwIt(ISO7816.SW_DATA_INVALID); } } + + public short readCertificateChainLengthAndHeaderLen(byte[] buf, short bufOffset, + short bufLen) { + this.buffer = buf; + this.startOff = bufOffset; + this.length = (short) (bufOffset + bufLen); + short totalLen = readMajorTypeWithPayloadLength(BYTES_TYPE); + totalLen += (short)( startOff - bufOffset); + return totalLen; + } } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMEncoder.java b/Applet/src/com/android/javacard/keymaster/KMEncoder.java similarity index 96% rename from Applet/Applet/src/com/android/javacard/keymaster/KMEncoder.java rename to Applet/src/com/android/javacard/keymaster/KMEncoder.java index c03e7201..72f906bf 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMEncoder.java +++ b/Applet/src/com/android/javacard/keymaster/KMEncoder.java @@ -30,7 +30,6 @@ public class KMEncoder { // masks private static final byte ADDITIONAL_MASK = 0x1F; - private static final byte MAJOR_TYPE_MASK = (byte) 0xE0; // value length private static final byte UINT8_LENGTH = (byte) 0x18; @@ -39,8 +38,7 @@ public class KMEncoder { private static final byte UINT64_LENGTH = (byte) 0x1B; private static final short TINY_PAYLOAD = 0x17; private static final short SHORT_PAYLOAD = 0x100; - - // TODO move the following to transient memory. + //TODO make this static. private byte[] buffer; private short startOff; private short length; @@ -81,6 +79,16 @@ public short encode(short object, byte[] buffer, short startOff) { return (short)(this.startOff - startOff); } + // array{KMError.OK,Array{KMByteBlobs}} + public void encodeCertChain(byte[] buffer, short offset, short length) { + this.buffer = buffer; + this.startOff = offset; + this.length = (short)(offset+3); + + writeMajorTypeWithLength(ARRAY_TYPE, (short) 2); // Array of 2 elements + writeByte(UINT_TYPE); // Error.OK + } + //array{KMError.OK,Array{KMByteBlobs}} public short encodeCert(byte[] certBuffer, short bufferStart, short certStart, short certLength) { this.buffer = certBuffer; @@ -335,7 +343,7 @@ private void writeMajorTypeWithLength(byte majorType, short len) { } private void writeBytes(byte[] buf, short start, short len){ - Util.arrayCopy(buf, start, buffer, startOff, len); + Util.arrayCopyNonAtomic(buf, start, buffer, startOff, len); incrementStartOff(len); } private void writeShort(short val){ diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMEnum.java b/Applet/src/com/android/javacard/keymaster/KMEnum.java similarity index 100% rename from Applet/Applet/src/com/android/javacard/keymaster/KMEnum.java rename to Applet/src/com/android/javacard/keymaster/KMEnum.java diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMEnumArrayTag.java b/Applet/src/com/android/javacard/keymaster/KMEnumArrayTag.java similarity index 100% rename from Applet/Applet/src/com/android/javacard/keymaster/KMEnumArrayTag.java rename to Applet/src/com/android/javacard/keymaster/KMEnumArrayTag.java diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMEnumTag.java b/Applet/src/com/android/javacard/keymaster/KMEnumTag.java similarity index 100% rename from Applet/Applet/src/com/android/javacard/keymaster/KMEnumTag.java rename to Applet/src/com/android/javacard/keymaster/KMEnumTag.java diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMError.java b/Applet/src/com/android/javacard/keymaster/KMError.java similarity index 91% rename from Applet/Applet/src/com/android/javacard/keymaster/KMError.java rename to Applet/src/com/android/javacard/keymaster/KMError.java index 8a971bba..e70dc8c2 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMError.java +++ b/Applet/src/com/android/javacard/keymaster/KMError.java @@ -83,4 +83,13 @@ public class KMError { public static short UNIMPLEMENTED = 100; public static short VERSION_MISMATCH = 101; public static short UNKNOWN_ERROR = 1000; + + //Extended errors + public static short SW_CONDITIONS_NOT_SATISFIED = 1001; + public static short UNSUPPORTED_CLA = 1002; + public static short INVALID_P1P2 = 1003; + public static short UNSUPPORTED_INSTRUCTION = 1004; + public static short CMD_NOT_ALLOWED = 1005; + public static short SW_WRONG_LENGTH = 1006; + public static short INVALID_DATA = 1007; } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMException.java b/Applet/src/com/android/javacard/keymaster/KMException.java similarity index 100% rename from Applet/Applet/src/com/android/javacard/keymaster/KMException.java rename to Applet/src/com/android/javacard/keymaster/KMException.java diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMHardwareAuthToken.java b/Applet/src/com/android/javacard/keymaster/KMHardwareAuthToken.java similarity index 100% rename from Applet/Applet/src/com/android/javacard/keymaster/KMHardwareAuthToken.java rename to Applet/src/com/android/javacard/keymaster/KMHardwareAuthToken.java diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMHmacSharingParameters.java b/Applet/src/com/android/javacard/keymaster/KMHmacSharingParameters.java similarity index 100% rename from Applet/Applet/src/com/android/javacard/keymaster/KMHmacSharingParameters.java rename to Applet/src/com/android/javacard/keymaster/KMHmacSharingParameters.java diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMInteger.java b/Applet/src/com/android/javacard/keymaster/KMInteger.java similarity index 97% rename from Applet/Applet/src/com/android/javacard/keymaster/KMInteger.java rename to Applet/src/com/android/javacard/keymaster/KMInteger.java index 0ebd4c52..090f6e86 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMInteger.java +++ b/Applet/src/com/android/javacard/keymaster/KMInteger.java @@ -18,7 +18,6 @@ import javacard.framework.ISO7816; import javacard.framework.ISOException; -import javacard.framework.JCSystem; import javacard.framework.Util; /** @@ -159,10 +158,8 @@ public static short compare(short num1, short num2){ short num2Buf = repository.alloc((short)8); Util.arrayFillNonAtomic(repository.getHeap(),num1Buf,(short)8,(byte)0); Util.arrayFillNonAtomic(repository.getHeap(),num2Buf,(short)8,(byte)0); - short numPtr = KMInteger.cast(num1).getStartOff(); short len = KMInteger.cast(num1).length(); KMInteger.cast(num1).getValue(repository.getHeap(),(short)(num1Buf+(short)(8-len)),len); - numPtr = KMInteger.cast(num2).getStartOff(); len = KMInteger.cast(num2).length(); KMInteger.cast(num2).getValue(repository.getHeap(),(short)(num2Buf+(short)(8-len)),len); return Util.arrayCompare( diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMIntegerArrayTag.java b/Applet/src/com/android/javacard/keymaster/KMIntegerArrayTag.java similarity index 97% rename from Applet/Applet/src/com/android/javacard/keymaster/KMIntegerArrayTag.java rename to Applet/src/com/android/javacard/keymaster/KMIntegerArrayTag.java index 3df79a56..7faa4c00 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMIntegerArrayTag.java +++ b/Applet/src/com/android/javacard/keymaster/KMIntegerArrayTag.java @@ -125,7 +125,6 @@ private static boolean validateKey(short key) { return false; } - // TODO this should be combined with validateKey to actually isValidTag {tagType, tagKey} pair. private static boolean validateTagType(short tagType) { return (tagType == ULONG_ARRAY_TAG) || (tagType == UINT_ARRAY_TAG); } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMIntegerTag.java b/Applet/src/com/android/javacard/keymaster/KMIntegerTag.java similarity index 98% rename from Applet/Applet/src/com/android/javacard/keymaster/KMIntegerTag.java rename to Applet/src/com/android/javacard/keymaster/KMIntegerTag.java index f8fb4c1a..2700580f 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMIntegerTag.java +++ b/Applet/src/com/android/javacard/keymaster/KMIntegerTag.java @@ -133,7 +133,6 @@ private static boolean validateKey(short key) { return false; } - // TODO this should be combined with validateKey to actually isValidTag {tagType, tagKey} pair. private static boolean validateTagType(short tagType) { return (tagType == DATE_TAG) || (tagType == UINT_TAG) || (tagType == ULONG_TAG); } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMKeyCharacteristics.java b/Applet/src/com/android/javacard/keymaster/KMKeyCharacteristics.java similarity index 100% rename from Applet/Applet/src/com/android/javacard/keymaster/KMKeyCharacteristics.java rename to Applet/src/com/android/javacard/keymaster/KMKeyCharacteristics.java diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java b/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java similarity index 94% rename from Applet/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java rename to Applet/src/com/android/javacard/keymaster/KMKeyParameters.java index 64fa3db2..ae759ffc 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java @@ -102,8 +102,9 @@ public short findTag(short tagType, short tagKey){ } // KDF, ECIES_SINGLE_HASH_MODE missing from types.hal - public static short makeHwEnforced( - short keyParamsPtr, byte origin, short osVersionObjPtr, short osPatchObjPtr, byte[] scratchPad) { + public static short makeHwEnforced(short keyParamsPtr, byte origin, + short osVersionObjPtr, short osPatchObjPtr, short vendorPatchObjPtr, + short bootPatchObjPtr, byte[] scratchPad) { final short[] hwEnforcedTagArr = { // HW Enforced KMType.ENUM_TAG, KMType.ORIGIN, @@ -166,6 +167,12 @@ public static short makeHwEnforced( short osPatchTag = KMIntegerTag.instance(KMType.UINT_TAG, KMType.OS_PATCH_LEVEL, osPatchObjPtr); Util.setShort(scratchPad, arrInd, osPatchTag); arrInd += 2; + short vendorPatchTag = KMIntegerTag.instance(KMType.UINT_TAG, KMType.VENDOR_PATCH_LEVEL, vendorPatchObjPtr); + Util.setShort(scratchPad, arrInd, vendorPatchTag); + arrInd += 2; + short bootPatchTag = KMIntegerTag.instance(KMType.UINT_TAG, KMType.BOOT_PATCH_LEVEL, bootPatchObjPtr); + Util.setShort(scratchPad, arrInd, bootPatchTag); + arrInd += 2; return createKeyParameters(scratchPad, (short) (arrInd / 2)); } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java similarity index 82% rename from Applet/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java rename to Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java index 4dc5ce9a..43ada045 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java @@ -39,7 +39,6 @@ public class KMKeymasterApplet extends Applet implements AppletEvent, ExtendedLe private static final byte CLA_ISO7816_NO_SM_NO_CHAN = (byte) 0x80; private static final short KM_HAL_VERSION = (short) 0x4000; private static final short MAX_AUTH_DATA_SIZE = (short) 512; - private static final short MAX_IO_LENGTH = 0x600; // "Keymaster HMAC Verification" - used for HMAC key verification. public static final byte[] sharingCheck = { @@ -63,38 +62,60 @@ public class KMKeymasterApplet extends Applet implements AppletEvent, ExtendedLe }; // Possible states of the applet. - private static final byte ILLEGAL_STATE = 0x00; - private static final byte INSTALL_STATE = 0x01; - private static final byte FIRST_SELECT_STATE = 0x02; - private static final byte ACTIVE_STATE = 0x03; - private static final byte INACTIVE_STATE = 0x04; - private static final byte UNINSTALLED_STATE = 0x05; + private static final byte KM_BEGIN_STATE = 0x00; + private static final byte ILLEGAL_STATE = KM_BEGIN_STATE + 1; + private static final byte INIT_STATE = KM_BEGIN_STATE + 2; + private static final byte IN_PROVISION_STATE = KM_BEGIN_STATE + 3; + private static final byte ACTIVE_STATE = KM_BEGIN_STATE + 4; + // Commands - private static final byte INS_GENERATE_KEY_CMD = 0x10; - private static final byte INS_IMPORT_KEY_CMD = 0x11; - private static final byte INS_IMPORT_WRAPPED_KEY_CMD = 0x12; - private static final byte INS_EXPORT_KEY_CMD = 0x13; - private static final byte INS_ATTEST_KEY_CMD = 0x14; - private static final byte INS_UPGRADE_KEY_CMD = 0x15; - private static final byte INS_DELETE_KEY_CMD = 0x16; - private static final byte INS_DELETE_ALL_KEYS_CMD = 0x17; - private static final byte INS_ADD_RNG_ENTROPY_CMD = 0x18; - private static final byte INS_COMPUTE_SHARED_HMAC_CMD = 0x19; - private static final byte INS_DESTROY_ATT_IDS_CMD = 0x1A; - private static final byte INS_VERIFY_AUTHORIZATION_CMD = 0x1B; - private static final byte INS_GET_HMAC_SHARING_PARAM_CMD = 0x1C; - private static final byte INS_GET_KEY_CHARACTERISTICS_CMD = 0x1D; - private static final byte INS_GET_HW_INFO_CMD = 0x1E; - private static final byte INS_BEGIN_OPERATION_CMD = 0x1F; - private static final byte INS_UPDATE_OPERATION_CMD = 0x20; - private static final byte INS_FINISH_OPERATION_CMD = 0x21; - private static final byte INS_ABORT_OPERATION_CMD = 0x22; - private static final byte INS_PROVISION_CMD = 0x23; - private static final byte INS_SET_BOOT_PARAMS_CMD = 0x24; - private static final byte INS_DEVICE_LOCKED_CMD = 0x25; - private static final byte INS_EARLY_BOOT_ENDED_CMD = 0x26; - private static final byte INS_BACKUP_CMD = 0x27; - private static final byte INS_RESTORE_CMD = 0x28; + private static final byte INS_BEGIN_KM_CMD = 0x00; + // Instructions for Provision Commands. + private static final byte INS_PROVISION_ATTESTATION_KEY_CMD = INS_BEGIN_KM_CMD + 1; //0x01 + private static final byte INS_PROVISION_ATTESTATION_CERT_CHAIN_CMD = INS_BEGIN_KM_CMD + 2; //0x02 + private static final byte INS_PROVISION_ATTESTATION_CERT_PARAMS_CMD = INS_BEGIN_KM_CMD + 3; //0x03 + private static final byte INS_PROVISION_ATTEST_IDS_CMD = INS_BEGIN_KM_CMD + 4; //0x04 + private static final byte INS_PROVISION_SHARED_SECRET_CMD = INS_BEGIN_KM_CMD + 5; //0x05 + private static final byte INS_SET_BOOT_PARAMS_CMD = INS_BEGIN_KM_CMD + 6; //0x06 + private static final byte INS_LOCK_PROVISIONING_CMD = INS_BEGIN_KM_CMD + 7; //0x07 + private static final byte INS_GET_PROVISION_STATUS_CMD = INS_BEGIN_KM_CMD + 8; //0x08 + // Top 32 commands are reserved for provisioning. + private static final byte INS_END_KM_PROVISION_CMD = 0x20; + + private static final byte INS_GENERATE_KEY_CMD = INS_END_KM_PROVISION_CMD + 1; //0x21 + private static final byte INS_IMPORT_KEY_CMD = INS_END_KM_PROVISION_CMD + 2; //0x22 + private static final byte INS_IMPORT_WRAPPED_KEY_CMD = INS_END_KM_PROVISION_CMD + 3; //0x23 + private static final byte INS_EXPORT_KEY_CMD = INS_END_KM_PROVISION_CMD + 4; //0x24 + private static final byte INS_ATTEST_KEY_CMD = INS_END_KM_PROVISION_CMD + 5; //0x25 + private static final byte INS_UPGRADE_KEY_CMD = INS_END_KM_PROVISION_CMD + 6; //0x26 + private static final byte INS_DELETE_KEY_CMD = INS_END_KM_PROVISION_CMD + 7; //0x27 + private static final byte INS_DELETE_ALL_KEYS_CMD = INS_END_KM_PROVISION_CMD + 8; //0x28 + private static final byte INS_ADD_RNG_ENTROPY_CMD = INS_END_KM_PROVISION_CMD + 9; //0x29 + private static final byte INS_COMPUTE_SHARED_HMAC_CMD = INS_END_KM_PROVISION_CMD + 10; //0x2A + private static final byte INS_DESTROY_ATT_IDS_CMD = INS_END_KM_PROVISION_CMD + 11; //0x2B + private static final byte INS_VERIFY_AUTHORIZATION_CMD = INS_END_KM_PROVISION_CMD + 12; //0x2C + private static final byte INS_GET_HMAC_SHARING_PARAM_CMD = INS_END_KM_PROVISION_CMD + 13; //0x2D + private static final byte INS_GET_KEY_CHARACTERISTICS_CMD = INS_END_KM_PROVISION_CMD + 14; //0x2E + private static final byte INS_GET_HW_INFO_CMD = INS_END_KM_PROVISION_CMD + 15; //0x2F + private static final byte INS_BEGIN_OPERATION_CMD = INS_END_KM_PROVISION_CMD + 16; //0x30 + private static final byte INS_UPDATE_OPERATION_CMD = INS_END_KM_PROVISION_CMD + 17; //0x31 + private static final byte INS_FINISH_OPERATION_CMD = INS_END_KM_PROVISION_CMD + 18; //0x32 + private static final byte INS_ABORT_OPERATION_CMD = INS_END_KM_PROVISION_CMD + 19; //0x33 + private static final byte INS_DEVICE_LOCKED_CMD = INS_END_KM_PROVISION_CMD + 20;//0x34 + private static final byte INS_EARLY_BOOT_ENDED_CMD = INS_END_KM_PROVISION_CMD + 21; //0x35 + private static final byte INS_GET_CERT_CHAIN_CMD = INS_END_KM_PROVISION_CMD + 22; //0x36 + + private static final byte INS_END_KM_CMD = 0x7F; + + // Provision reporting status + private static final byte NOT_PROVISIONED = 0x00; + private static final byte PROVISION_STATUS_ATTESTATION_KEY = 0x01; + private static final byte PROVISION_STATUS_ATTESTATION_CERT_CHAIN = 0x02; + private static final byte PROVISION_STATUS_ATTESTATION_CERT_PARAMS = 0x04; + private static final byte PROVISION_STATUS_ATTEST_IDS = 0x08; + private static final byte PROVISION_STATUS_SHARED_SECRET = 0x10; + private static final byte PROVISION_STATUS_BOOT_PARAM = 0x20; + private static final byte PROVISION_STATUS_PROVISIONING_LOCKED = 0x40; // Data Dictionary items public static final byte DATA_ARRAY_SIZE = 30; @@ -128,10 +149,10 @@ public class KMKeymasterApplet extends Applet implements AppletEvent, ExtendedLe public static final byte OUTPUT_DATA = 25; public static final byte HW_TOKEN = 26; public static final byte VERIFICATION_TOKEN = 27; - private static final byte SIGNATURE = 28; + protected static final byte SIGNATURE = 28; // AddRngEntropy - private static final short MAX_SEED_SIZE = 2048; + protected static final short MAX_SEED_SIZE = 2048; // Keyblob constants public static final byte KEY_BLOB_SECRET = 0; public static final byte KEY_BLOB_NONCE = 1; @@ -143,77 +164,38 @@ public class KMKeymasterApplet extends Applet implements AppletEvent, ExtendedLe private static final byte AES_GCM_NONCE_LENGTH = 12; // ComputeHMAC constants private static final short HMAC_SHARED_PARAM_MAX_SIZE = 64; - // 64 bit unsigned calculations for time - private static final byte[] oneSecMsec = {0, 0, 0, 0, 0, 0, 0x03, (byte) 0xE8}; // 1000 msec - private static final byte[] oneMinMsec = {0, 0, 0, 0, 0, 0, (byte) 0xEA, 0x60}; // 60000 msec - private static final byte[] oneHourMsec = { - 0, 0, 0, 0, 0, 0x36, (byte) 0xEE, (byte) 0x80 - }; // 3600000 msec - private static final byte[] oneDayMsec = {0, 0, 0, 0, 0x05, 0x26, 0x5C, 0x00}; // 86400000 msec - private static final byte[] oneMonthMsec = { - 0, 0, 0, 0, (byte) 0x9A, 0x7E, (byte) 0xC8, 0x00 - }; // 2592000000 msec - private static final byte[] oneYearMsec = { - 0, 0, 0, 0x07, 0x57, (byte) 0xB1, 0x2C, 0x00 - }; // 31536000000 msec - // Leap year + 3 yrs - private static final byte[] fourYrsMsec = { - 0, 0, 0, 0x1D, 0x63, (byte) 0xEB, 0x0C, 0x00 - }; // 126230400000 msec - private static final byte[] firstJan2020 = { - 0, 0, 0x01, 0x6F, 0x60, 0x1E, 0x5C, 0x00 - }; // 1577865600000 msec - private static final byte[] firstJan2051 = { - 0, 0, 0x02, 0x53, 0x27, (byte) 0xC5, (byte) 0x90, 0x00 - }; // 2556172800000 msec // Keymaster Applet attributes - private static byte keymasterState = ILLEGAL_STATE; - private static KMEncoder encoder; - private static KMDecoder decoder; - private static KMRepository repository; - private static KMSEProvider seProvider; - private static byte[] buffer; - private static short bufferLength; - private static short bufferStartOffset; - private static boolean provisionDone; - private static boolean setBootParamsDone; - private static short[] tmpVariables; - private static short[] data; - private static final short MAX_CERT_SIZE = 2048; + protected static byte keymasterState = ILLEGAL_STATE; + protected static KMEncoder encoder; + protected static KMDecoder decoder; + protected static KMRepository repository; + protected static KMSEProvider seProvider; + protected static byte[] buffer; + protected static short bufferLength; + protected static short bufferStartOffset; + protected static short[] tmpVariables; + protected static short[] data; + protected byte provisionStatus = NOT_PROVISIONED; + protected static final short MAX_CERT_SIZE = 2048; /** Registers this applet. */ - protected KMKeymasterApplet() { - seProvider = KMSEProviderImpl.instance(); - provisionDone = false; - setBootParamsDone = false; + protected KMKeymasterApplet(KMSEProvider seImpl) { + seProvider = seImpl; + boolean isUpgrading = seImpl.isUpgrading(); + repository = new KMRepository(isUpgrading); byte[] buf = JCSystem.makeTransientByteArray((short) 32, JCSystem.CLEAR_ON_DESELECT); - keymasterState = KMKeymasterApplet.INSTALL_STATE; data = JCSystem.makeTransientShortArray((short) DATA_ARRAY_SIZE, JCSystem.CLEAR_ON_RESET); - repository = new KMRepository(); tmpVariables = JCSystem.makeTransientShortArray((short) TMP_VARIABLE_ARRAY_SIZE, JCSystem.CLEAR_ON_RESET); - seProvider.getTrueRandomNumber(buf, (short) 0, KMRepository.MASTER_KEY_SIZE); - repository.initMasterKey(buf, (short)0, KMRepository.MASTER_KEY_SIZE); - seProvider.newRandomNumber(buf, (short) 0, KMRepository.SHARED_SECRET_KEY_SIZE); - // 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); + if(!isUpgrading) { + keymasterState = KMKeymasterApplet.INIT_STATE; + seProvider.getTrueRandomNumber(buf, (short) 0, KMRepository.MASTER_KEY_SIZE); + repository.initMasterKey(buf, (short)0, KMRepository.MASTER_KEY_SIZE); + } KMType.initialize(); encoder = new KMEncoder(); decoder = new KMDecoder(); - register(); - } - - /** - * Installs this applet. - * - * @param bArray the array containing installation parameters - * @param bOffset the starting offset in bArray - * @param bLength the length in bytes of the parameter data in bArray - */ - public static void install(byte[] bArray, short bOffset, byte bLength) { - new KMKeymasterApplet(); } /** @@ -224,12 +206,8 @@ public static void install(byte[] bArray, short bOffset, byte bLength) { @Override public boolean select() { repository.onSelect(); - if (keymasterState == KMKeymasterApplet.INSTALL_STATE) { - keymasterState = KMKeymasterApplet.FIRST_SELECT_STATE; - } else if (keymasterState == KMKeymasterApplet.INACTIVE_STATE) { - keymasterState = KMKeymasterApplet.ACTIVE_STATE; - } else { - return false; + if (keymasterState == KMKeymasterApplet.INIT_STATE) { + keymasterState = KMKeymasterApplet.IN_PROVISION_STATE; } return true; } @@ -238,156 +216,232 @@ public boolean select() { @Override public void deselect() { repository.onDeselect(); - if (keymasterState == KMKeymasterApplet.ACTIVE_STATE) { - keymasterState = KMKeymasterApplet.INACTIVE_STATE; - } } /** Uninstalls the applet after cleaning the repository. */ @Override public void uninstall() { repository.onUninstall(); - if (keymasterState != KMKeymasterApplet.UNINSTALLED_STATE) { - keymasterState = KMKeymasterApplet.UNINSTALLED_STATE; - } } - /** - * Processes an incoming APDU and handles it using command objects. - * - * @param apdu the incoming APDU - */ - @Override - public void process(APDU apdu) { - repository.onProcess(); - // getRepository(); - // Verify whether applet is in correct state. - if ((keymasterState != KMKeymasterApplet.ACTIVE_STATE) - && (keymasterState != KMKeymasterApplet.FIRST_SELECT_STATE)) { - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - // If this is select applet apdu which is selecting this applet then return - if (apdu.isISOInterindustryCLA()) { - if (selectingApplet()) { - return; - } + private short mapISOErrorToKMError(short reason) { + switch (reason) { + case ISO7816.SW_CLA_NOT_SUPPORTED: + return KMError.UNSUPPORTED_CLA; + case ISO7816.SW_CONDITIONS_NOT_SATISFIED: + return KMError.SW_CONDITIONS_NOT_SATISFIED; + case ISO7816.SW_COMMAND_NOT_ALLOWED: + return KMError.CMD_NOT_ALLOWED; + case ISO7816.SW_DATA_INVALID: + return KMError.INVALID_DATA; + case ISO7816.SW_INCORRECT_P1P2: + return KMError.INVALID_P1P2; + case ISO7816.SW_INS_NOT_SUPPORTED: + return KMError.UNSUPPORTED_INSTRUCTION; + case ISO7816.SW_WRONG_LENGTH: + return KMError.SW_WRONG_LENGTH; + case ISO7816.SW_UNKNOWN: + default: + return KMError.UNKNOWN_ERROR; } + } + + protected void validateApduHeader(APDU apdu) { // Read the apdu header and buffer. byte[] apduBuffer = apdu.getBuffer(); byte apduClass = apduBuffer[ISO7816.OFFSET_CLA]; - byte apduIns = apduBuffer[ISO7816.OFFSET_INS]; short P1P2 = Util.getShort(apduBuffer, ISO7816.OFFSET_P1); - buffer = repository.getHeap(); - bufferStartOffset = repository.alloc(MAX_IO_LENGTH); + // Validate APDU Header. if ((apduClass != CLA_ISO7816_NO_SM_NO_CHAN)) { ISOException.throwIt(ISO7816.SW_CLA_NOT_SUPPORTED); - } else if (P1P2 != KMKeymasterApplet.KM_HAL_VERSION) { - ISOException.throwIt(ISO7816.SW_INCORRECT_P1P2); } - // Validate whether INS can be supported - if (!(apduIns >= INS_GENERATE_KEY_CMD && apduIns <= INS_RESTORE_CMD)) { - ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED); + + // Validate P1P2. + if (P1P2 != KMKeymasterApplet.KM_HAL_VERSION) { + ISOException.throwIt(ISO7816.SW_INCORRECT_P1P2); } - // Validate if INS is provision command if applet is in FIRST_SELECT_STATE. - if (keymasterState == KMKeymasterApplet.FIRST_SELECT_STATE) { - if ((apduIns != INS_PROVISION_CMD) && (apduIns != INS_SET_BOOT_PARAMS_CMD)) { - ISOException.throwIt(ISO7816.SW_COMMAND_NOT_ALLOWED); + } + + /** + * Processes an incoming APDU and handles it using command objects. + * + * @param apdu the incoming APDU + */ + @Override + public void process(APDU apdu) { + try { + repository.onProcess(); + // Verify whether applet is in correct state. + if ((keymasterState == KMKeymasterApplet.INIT_STATE) + || (keymasterState == KMKeymasterApplet.ILLEGAL_STATE)) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); } - if (apduIns == INS_PROVISION_CMD && provisionDone) { - ISOException.throwIt(ISO7816.SW_COMMAND_NOT_ALLOWED); + // If this is select applet apdu which is selecting this applet then + // return + if (apdu.isISOInterindustryCLA()) { + if (selectingApplet()) { + return; + } } - if (apduIns == INS_SET_BOOT_PARAMS_CMD && setBootParamsDone) { - ISOException.throwIt(ISO7816.SW_COMMAND_NOT_ALLOWED); + // Validate APDU Header. + validateApduHeader(apdu); + + byte[] apduBuffer = apdu.getBuffer(); + byte apduIns = apduBuffer[ISO7816.OFFSET_INS]; + + // Validate whether INS can be supported + if (!(apduIns > INS_BEGIN_KM_CMD && apduIns < INS_END_KM_CMD)) { + ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED); } - } - // Process the apdu - try { - // Handle the command - switch (apduIns) { - case INS_GENERATE_KEY_CMD: - processGenerateKey(apdu); - break; - case INS_IMPORT_KEY_CMD: - processImportKeyCmd(apdu); - break; - case INS_IMPORT_WRAPPED_KEY_CMD: - processImportWrappedKeyCmd(apdu); - break; - case INS_EXPORT_KEY_CMD: - processExportKeyCmd(apdu); - break; - case INS_ATTEST_KEY_CMD: - processAttestKeyCmd(apdu); - break; - case INS_UPGRADE_KEY_CMD: - processUpgradeKeyCmd(apdu); - break; - case INS_DELETE_KEY_CMD: - processDeleteKeyCmd(apdu); - break; - case INS_DELETE_ALL_KEYS_CMD: - processDeleteAllKeysCmd(apdu); - break; - case INS_ADD_RNG_ENTROPY_CMD: - processAddRngEntropyCmd(apdu); - break; - case INS_COMPUTE_SHARED_HMAC_CMD: - processComputeSharedHmacCmd(apdu); - break; - case INS_DESTROY_ATT_IDS_CMD: - processDestroyAttIdsCmd(apdu); - break; - case INS_VERIFY_AUTHORIZATION_CMD: - processVerifyAuthorizationCmd(apdu); - break; - case INS_GET_HMAC_SHARING_PARAM_CMD: - processGetHmacSharingParamCmd(apdu); - break; - case INS_GET_KEY_CHARACTERISTICS_CMD: - processGetKeyCharacteristicsCmd(apdu); - break; - case INS_GET_HW_INFO_CMD: - processGetHwInfoCmd(apdu); - break; - case INS_BEGIN_OPERATION_CMD: - processBeginOperationCmd(apdu); - break; - case INS_UPDATE_OPERATION_CMD: - processUpdateOperationCmd(apdu); - break; - case INS_FINISH_OPERATION_CMD: - processFinishOperationCmd(apdu); - break; - case INS_ABORT_OPERATION_CMD: - processAbortOperationCmd(apdu); - break; - case INS_PROVISION_CMD: - processProvisionCmd(apdu); - break; - case INS_SET_BOOT_PARAMS_CMD: - processSetBootParamsCmd(apdu); - break; - case INS_DEVICE_LOCKED_CMD: - processDeviceLockedCmd(apdu); - break; - case INS_EARLY_BOOT_ENDED_CMD: - processEarlyBootEndedCmd(apdu); - break; - case INS_BACKUP_CMD: - processBackupCmd(apdu); - break; - case INS_RESTORE_CMD: - processRestoreCmd(apdu); - break; - default: - ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED); + buffer = repository.getHeap(); + // Process the apdu + if (keymasterState == KMKeymasterApplet.IN_PROVISION_STATE) { + switch (apduIns) { + case INS_PROVISION_ATTESTATION_KEY_CMD: + processProvisionAttestationKey(apdu); + provisionStatus |= KMKeymasterApplet.PROVISION_STATUS_ATTESTATION_KEY; + sendError(apdu, KMError.OK); + return; + + case INS_PROVISION_ATTESTATION_CERT_CHAIN_CMD: + processProvisionAttestationCertChainCmd(apdu); + provisionStatus |= KMKeymasterApplet.PROVISION_STATUS_ATTESTATION_CERT_CHAIN; + sendError(apdu, KMError.OK); + return; + + case INS_PROVISION_ATTESTATION_CERT_PARAMS_CMD: + processProvisionAttestationCertParams(apdu); + provisionStatus |= KMKeymasterApplet.PROVISION_STATUS_ATTESTATION_CERT_PARAMS; + sendError(apdu, KMError.OK); + return; + + case INS_PROVISION_ATTEST_IDS_CMD: + processProvisionAttestIdsCmd(apdu); + provisionStatus |= KMKeymasterApplet.PROVISION_STATUS_ATTEST_IDS; + sendError(apdu, KMError.OK); + return; + + case INS_PROVISION_SHARED_SECRET_CMD: + processProvisionSharedSecretCmd(apdu); + provisionStatus |= KMKeymasterApplet.PROVISION_STATUS_SHARED_SECRET; + sendError(apdu, KMError.OK); + return; + + case INS_LOCK_PROVISIONING_CMD: + if (isProvisioningComplete()) { + provisionStatus |= KMKeymasterApplet.PROVISION_STATUS_PROVISIONING_LOCKED; + keymasterState = KMKeymasterApplet.ACTIVE_STATE; + sendError(apdu, KMError.OK); + } else { + ISOException.throwIt(ISO7816.SW_COMMAND_NOT_ALLOWED); + } + return; + } + } + + if ((keymasterState == KMKeymasterApplet.ACTIVE_STATE) + || (keymasterState == KMKeymasterApplet.IN_PROVISION_STATE)) { + switch (apduIns) { + case INS_SET_BOOT_PARAMS_CMD: + if (seProvider.isBootSignalEventSupported() + && (!seProvider.isDeviceRebooted())) { + ISOException.throwIt(ISO7816.SW_COMMAND_NOT_ALLOWED); + } + processSetBootParamsCmd(apdu); + provisionStatus |= KMKeymasterApplet.PROVISION_STATUS_BOOT_PARAM; + seProvider.clearDeviceBooted(false); + sendError(apdu, KMError.OK); + return; + + case INS_GET_PROVISION_STATUS_CMD: + processGetProvisionStatusCmd(apdu); + return; + } + } + + if ((keymasterState == KMKeymasterApplet.ACTIVE_STATE) + || ((keymasterState == KMKeymasterApplet.IN_PROVISION_STATE) + && isProvisioningComplete())) { + switch (apduIns) { + case INS_GENERATE_KEY_CMD: + processGenerateKey(apdu); + break; + case INS_IMPORT_KEY_CMD: + processImportKeyCmd(apdu); + break; + case INS_IMPORT_WRAPPED_KEY_CMD: + processImportWrappedKeyCmd(apdu); + break; + case INS_EXPORT_KEY_CMD: + processExportKeyCmd(apdu); + break; + case INS_ATTEST_KEY_CMD: + processAttestKeyCmd(apdu); + break; + case INS_UPGRADE_KEY_CMD: + processUpgradeKeyCmd(apdu); + break; + case INS_DELETE_KEY_CMD: + processDeleteKeyCmd(apdu); + break; + case INS_DELETE_ALL_KEYS_CMD: + processDeleteAllKeysCmd(apdu); + break; + case INS_ADD_RNG_ENTROPY_CMD: + processAddRngEntropyCmd(apdu); + break; + case INS_COMPUTE_SHARED_HMAC_CMD: + processComputeSharedHmacCmd(apdu); + break; + case INS_DESTROY_ATT_IDS_CMD: + processDestroyAttIdsCmd(apdu); + break; + case INS_VERIFY_AUTHORIZATION_CMD: + processVerifyAuthorizationCmd(apdu); + break; + case INS_GET_HMAC_SHARING_PARAM_CMD: + processGetHmacSharingParamCmd(apdu); + break; + case INS_GET_KEY_CHARACTERISTICS_CMD: + processGetKeyCharacteristicsCmd(apdu); + break; + case INS_GET_HW_INFO_CMD: + processGetHwInfoCmd(apdu); + break; + case INS_BEGIN_OPERATION_CMD: + processBeginOperationCmd(apdu); + break; + case INS_UPDATE_OPERATION_CMD: + processUpdateOperationCmd(apdu); + break; + case INS_FINISH_OPERATION_CMD: + processFinishOperationCmd(apdu); + break; + case INS_ABORT_OPERATION_CMD: + processAbortOperationCmd(apdu); + break; + case INS_DEVICE_LOCKED_CMD: + processDeviceLockedCmd(apdu); + break; + case INS_EARLY_BOOT_ENDED_CMD: + processEarlyBootEndedCmd(apdu); + break; + case INS_GET_CERT_CHAIN_CMD: + processGetCertChainCmd(apdu); + break; + default: + ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED); + } + } else { + ISOException.throwIt(ISO7816.SW_COMMAND_NOT_ALLOWED); } } catch (KMException exception) { freeOperations(); sendError(apdu, KMException.reason); exception.clear(); } catch (ISOException exp) { + sendError(apdu, mapISOErrorToKMError(exp.getReason())); freeOperations(); } finally { resetData(); @@ -395,23 +449,16 @@ public void process(APDU apdu) { } } - private void processRestoreCmd(APDU apdu) { - // No arguments - if (seProvider.isBackupRestoreSupported()) sendError(apdu, KMError.UNIMPLEMENTED); - byte[] data = repository.getDataTable(); - short buf = KMByteBlob.instance((short) data.length); - short len = - seProvider.restore(KMByteBlob.cast(buf).getBuffer(), KMByteBlob.cast(buf).getStartOff()); - repository.restoreData(buf); - sendError(apdu, KMError.OK); - } - - private void processBackupCmd(APDU apdu) { - // No arguments - if (seProvider.isBackupRestoreSupported()) sendError(apdu, KMError.UNIMPLEMENTED); - byte[] data = repository.getDataTable(); - seProvider.backup(data, (short) 0, (short) data.length); - sendError(apdu, KMError.OK); + private boolean isProvisioningComplete() { + if((0 != (provisionStatus & PROVISION_STATUS_ATTESTATION_KEY)) + && (0 != (provisionStatus & PROVISION_STATUS_ATTESTATION_CERT_CHAIN)) + && (0 != (provisionStatus & PROVISION_STATUS_ATTESTATION_CERT_PARAMS)) + && (0 != (provisionStatus & PROVISION_STATUS_SHARED_SECRET)) + && (0 != (provisionStatus & PROVISION_STATUS_BOOT_PARAM))) { + return true; + } else { + return false; + } } private void freeOperations() { @@ -436,6 +483,9 @@ private void processDeviceLockedCmd(APDU apdu) { KMArray.cast(tmpVariables[0]).add((short) 1, tmpVariables[1]); // Decode the arguments tmpVariables[0] = decoder.decode(tmpVariables[0], buffer, bufferStartOffset, bufferLength); + //reclaim memory + repository.reclaimMemory(bufferLength); + tmpVariables[1] = KMArray.cast(tmpVariables[0]).get((short) 0); tmpVariables[1] = KMInteger.cast(tmpVariables[1]).getByte(); data[VERIFICATION_TOKEN] = KMArray.cast(tmpVariables[0]).get((short) 1); @@ -465,7 +515,8 @@ private void resetData() { } /** Sends a response, may be extended response, as requested by the command. */ public static void sendOutgoing(APDU apdu) { - if (bufferLength > MAX_IO_LENGTH) { + if (((short) (bufferLength + bufferStartOffset)) > ((short) repository + .getHeap().length)) { ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); } // Send data @@ -481,11 +532,9 @@ public static void receiveIncoming(APDU apdu) { short recvLen = apdu.setIncomingAndReceive(); short srcOffset = apdu.getOffsetCdata(); bufferLength = apdu.getIncomingLength(); + bufferStartOffset = repository.allocReclaimableMemory(bufferLength); short index = bufferStartOffset; - // Receive data - if (bufferLength > MAX_IO_LENGTH) { - ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); - } + while (recvLen > 0 && ((short) (index - bufferStartOffset) < bufferLength)) { Util.arrayCopyNonAtomic(srcBuffer, srcOffset, buffer, index, recvLen); index += recvLen; @@ -510,6 +559,8 @@ private void processGetHwInfoCmd(APDU apdu) { KMByteBlob.instance( JavacardKeymasterDevice, (short) 0, (short) JavacardKeymasterDevice.length)); resp.add((short) 2, KMByteBlob.instance(Google, (short) 0, (short) Google.length)); + + bufferStartOffset = repository.allocAvailableMemory(); // Encode the response - actual bufferLength is 86 bufferLength = encoder.encode(respPtr, buffer, bufferStartOffset); // send buffer to master @@ -524,6 +575,10 @@ private void processAddRngEntropyCmd(APDU apdu) { KMArray.cast(argsProto).add((short) 0, KMByteBlob.exp()); // Decode the argument short args = decoder.decode(argsProto, buffer, bufferStartOffset, bufferLength); + //reclaim memory + repository.reclaimMemory(bufferLength); + + // Process KMByteBlob blob = KMByteBlob.cast(KMArray.cast(args).get((short) 0)); // Maximum 2KiB of seed is allowed. @@ -531,9 +586,90 @@ private void processAddRngEntropyCmd(APDU apdu) { KMException.throwIt(KMError.INVALID_ARGUMENT); } seProvider.addRngEntropy(blob.getBuffer(), blob.getStartOff(), blob.length()); + sendError(apdu, KMError.OK); } - private void processProvisionCmd(APDU apdu) { + private void processGetCertChainCmd(APDU apdu) { + // Make the response + tmpVariables[0] = seProvider.getCertificateChainLength(); + // Add arrayHeader and KMError.OK + tmpVariables[0] += 2; + tmpVariables[1] = KMByteBlob.instance(tmpVariables[0]); + buffer = KMByteBlob.cast(tmpVariables[1]).getBuffer(); + bufferStartOffset = KMByteBlob.cast(tmpVariables[1]).getStartOff(); + bufferLength = KMByteBlob.cast(tmpVariables[1]).length(); + // read the cert chain from non-volatile memory. Cert chain is already in + // CBOR format. + seProvider.readCertificateChain(buffer, (short) (bufferStartOffset + 2)); + // Encode cert chain. + encoder.encodeCertChain(buffer, bufferStartOffset, bufferLength); + sendOutgoing(apdu); + } + + + private void processProvisionAttestationCertParams(APDU apdu) { + receiveIncoming(apdu); + // Arguments + short blob = KMByteBlob.exp(); + short argsProto = KMArray.instance((short) 3); + KMArray.cast(argsProto).add((short) 0, blob); // Cert - DER encoded issuer + KMArray.cast(argsProto).add((short) 1, blob); // Cert - Expiry Time + KMArray.cast(argsProto).add((short) 2, blob); // Cert - Auth Key Id + // Decode the argument. + short args = decoder.decode(argsProto, buffer, bufferStartOffset, bufferLength); + //reclaim memory + repository.reclaimMemory(bufferLength); + + + // save issuer - DER Encoded + tmpVariables[0] = KMArray.cast(args).get((short) 0); + repository.setIssuer( + KMByteBlob.cast(tmpVariables[0]).getBuffer(), + KMByteBlob.cast(tmpVariables[0]).getStartOff(), + KMByteBlob.cast(tmpVariables[0]).length()); + + // save expiry time - UTC or General Time - YYMMDDhhmmssZ or YYYYMMDDhhmmssZ. + tmpVariables[0] = KMArray.cast(args).get((short) 1); + repository.setCertExpiryTime( + KMByteBlob.cast(tmpVariables[0]).getBuffer(), + KMByteBlob.cast(tmpVariables[0]).getStartOff(), + KMByteBlob.cast(tmpVariables[0]).length()); + + // Auth Key Id - from cert associated with imported attestation key. + tmpVariables[0] = KMArray.cast(args).get((short) 2); + repository.setAuthKeyId( + KMByteBlob.cast(tmpVariables[0]).getBuffer(), + KMByteBlob.cast(tmpVariables[0]).getStartOff(), + KMByteBlob.cast(tmpVariables[0]).length()); + } + + private void processProvisionAttestationCertChainCmd(APDU apdu) { + byte[] srcBuffer = apdu.getBuffer(); + short recvLen = apdu.setIncomingAndReceive(); + short srcOffset = apdu.getOffsetCdata(); + bufferLength = apdu.getIncomingLength(); + bufferStartOffset = repository.alloc(bufferLength); + short bytesRead = 0; + Util.arrayCopyNonAtomic(srcBuffer, srcOffset, buffer, bufferStartOffset, + recvLen); + // tmpVariables[1] holds the total length + Header length. + tmpVariables[1] = decoder.readCertificateChainLengthAndHeaderLen(buffer, + bufferStartOffset, recvLen); + while (recvLen > 0 && ((short) bytesRead <= bufferLength)) { + seProvider.persistPartialCertificateChain(buffer, bufferStartOffset, + recvLen, bufferLength); + bytesRead += recvLen; + recvLen = apdu.receiveBytes(srcOffset); + if (recvLen > 0) + Util.arrayCopyNonAtomic(srcBuffer, srcOffset, buffer, bufferStartOffset, + recvLen); + } + if (tmpVariables[1] != bytesRead) { + ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); + } + } + + private void processProvisionAttestationKey(APDU apdu) { receiveIncoming(apdu); // Re-purpose the apdu buffer as scratch pad. byte[] scratchPad = apdu.getBuffer(); @@ -541,17 +677,16 @@ 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) 7); + short argsProto = KMArray.instance((short) 3); KMArray.cast(argsProto).add((short) 0, keyparams); KMArray.cast(argsProto).add((short) 1, keyFormat); KMArray.cast(argsProto).add((short) 2, 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); + //reclaim memory + repository.reclaimMemory(bufferLength); + // key params should have os patch, os version and verified root of trust data[KEY_PARAMETERS] = KMArray.cast(args).get((short) 0); tmpVariables[0] = KMArray.cast(args).get((short) 1); @@ -563,9 +698,9 @@ private void processProvisionCmd(APDU apdu) { } data[ORIGIN] = KMType.IMPORTED; - // get algorithm - only RSA keys expected + // get algorithm - only EC keys expected tmpVariables[0] = KMEnumTag.getValue(KMType.ALGORITHM, data[KEY_PARAMETERS]); - if (tmpVariables[0] != KMType.RSA) { + if (tmpVariables[0] != KMType.EC) { KMException.throwIt(KMError.INVALID_ARGUMENT); } // get digest - only SHA256 supported @@ -579,18 +714,7 @@ private void processProvisionCmd(APDU apdu) { } else { KMException.throwIt(KMError.INVALID_ARGUMENT); } - // get padding - only PKCS1 supported - tmpVariables[0] = - KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.PADDING, data[KEY_PARAMETERS]); - if (tmpVariables[0] != KMType.INVALID_VALUE) { - if (KMEnumArrayTag.cast(tmpVariables[0]).length() != 1) - KMException.throwIt(KMError.INVALID_ARGUMENT); - tmpVariables[0] = KMEnumArrayTag.cast(tmpVariables[0]).get((short) 0); - if (tmpVariables[0] != KMType.RSA_PKCS1_1_5_SIGN) - KMException.throwIt(KMError.INCOMPATIBLE_PADDING_MODE); - } else { - KMException.throwIt(KMError.INVALID_ARGUMENT); - } + // Purpose should be ATTEST_KEY tmpVariables[0] = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.PURPOSE, data[KEY_PARAMETERS]); if (tmpVariables[0] != KMType.INVALID_VALUE) { @@ -601,45 +725,25 @@ private void processProvisionCmd(APDU apdu) { } else { KMException.throwIt(KMError.INVALID_ARGUMENT); } - tmpVariables[0] = - KMKeyParameters.findTag(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); - if (tmpVariables[0] != KMType.INVALID_VALUE) { - tmpVariables[0] = KMIntegerTag.cast(tmpVariables[0]).getValue(); - if (KMInteger.cast(tmpVariables[0]).getSignificantShort() != 0 - || KMInteger.cast(tmpVariables[0]).getShort() != (short) 2048) { - KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); - } - } else { - KMException.throwIt(KMError.INVALID_ARGUMENT); - } - - // Import Rsa Key - initializes data[PUB_KEY] and data[SECRET] - importRSAKey(scratchPad); + // Import EC Key - initializes data[SECRET] data[PUB_KEY] + importECKeys(scratchPad); // persist key - repository.persistAttestationKey(data[PUB_KEY], data[SECRET]); - - // save issuer - DER Encoded - tmpVariables[0] = KMArray.cast(args).get((short) 3); - repository.setIssuer( - KMByteBlob.cast(tmpVariables[0]).getBuffer(), - KMByteBlob.cast(tmpVariables[0]).getStartOff(), - KMByteBlob.cast(tmpVariables[0]).length()); - - // save expiry time - UTC or General Time - YYMMDDhhmmssZ or YYYYMMDDhhmmssZ. - tmpVariables[0] = KMArray.cast(args).get((short) 4); - repository.setCertExpiryTime( - KMByteBlob.cast(tmpVariables[0]).getBuffer(), - KMByteBlob.cast(tmpVariables[0]).getStartOff(), - KMByteBlob.cast(tmpVariables[0]).length()); + repository.persistAttestationKey(data[SECRET]); + } - // Auth Key Id - from cert associated with imported attestation key. - tmpVariables[0] = KMArray.cast(args).get((short) 5); - repository.setAuthKeyId( - KMByteBlob.cast(tmpVariables[0]).getBuffer(), - KMByteBlob.cast(tmpVariables[0]).getStartOff(), - KMByteBlob.cast(tmpVariables[0]).length()); + private void processProvisionAttestIdsCmd(APDU apdu) { + receiveIncoming(apdu); + // Arguments + short keyparams = KMKeyParameters.exp(); + short argsProto = KMArray.instance((short) 1); + KMArray.cast(argsProto).add((short) 0, keyparams); + // Decode the argument. + short args = decoder.decode(argsProto, buffer, bufferStartOffset, bufferLength); + //reclaim memory + repository.reclaimMemory(bufferLength); + data[KEY_PARAMETERS] = KMArray.cast(args).get((short) 0); // persist attestation Ids - if any is missing then exception occurs saveAttId(KMType.ATTESTATION_ID_BRAND); saveAttId(KMType.ATTESTATION_ID_DEVICE); @@ -649,25 +753,39 @@ private void processProvisionCmd(APDU apdu) { saveAttId(KMType.ATTESTATION_ID_IMEI); saveAttId(KMType.ATTESTATION_ID_MEID); saveAttId(KMType.ATTESTATION_ID_SERIAL); + } - // Persist Hmac Shared Key Secret - tmpVariables[0] = KMArray.cast(args).get((short) 6); - if (KMByteBlob.cast(tmpVariables[0]).length() != KMRepository.SHARED_SECRET_KEY_SIZE) { + private void processProvisionSharedSecretCmd(APDU apdu) { + receiveIncoming(apdu); + // Arguments + short blob = KMByteBlob.exp(); + short argsProto = KMArray.instance((short) 1); + KMArray.cast(argsProto).add((short) 0, blob); + // Decode the argument. + short args = decoder.decode(argsProto, buffer, bufferStartOffset, bufferLength); + //reclaim memory + repository.reclaimMemory(bufferLength); + + tmpVariables[0] = KMArray.cast(args).get((short) 0); + if (tmpVariables[0] != KMType.INVALID_VALUE + && KMByteBlob.cast(tmpVariables[0]).length() != KMRepository.SHARED_SECRET_KEY_SIZE) { KMException.throwIt(KMError.INVALID_ARGUMENT); } + // Persist shared Hmac. 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; - if (setBootParamsDone) { - keymasterState = KMKeymasterApplet.ACTIVE_STATE; - } - } - sendError(apdu, KMError.OK); + private void processGetProvisionStatusCmd(APDU apdu) { + tmpVariables[0] = KMArray.instance((short) 2); + KMArray.cast(tmpVariables[0]).add((short) 0, KMInteger.uint_16(KMError.OK)); + KMArray.cast(tmpVariables[0]).add((short) 1, KMInteger.uint_16(provisionStatus)); + + bufferStartOffset = repository.allocAvailableMemory(); + bufferLength = encoder.encode(tmpVariables[0], buffer, bufferStartOffset); + sendOutgoing(apdu); } private void saveAttId(short attTag) { @@ -719,6 +837,9 @@ private void processGetKeyCharacteristicsCmd(APDU apdu) { KMArray.cast(tmpVariables[0]).add((short) 2, KMByteBlob.exp()); // Decode the arguments tmpVariables[0] = decoder.decode(tmpVariables[0], buffer, bufferStartOffset, bufferLength); + //reclaim memory + repository.reclaimMemory(bufferLength); + data[KEY_BLOB] = KMArray.cast(tmpVariables[0]).get((short) 0); data[APP_ID] = KMArray.cast(tmpVariables[0]).get((short) 1); data[APP_DATA] = KMArray.cast(tmpVariables[0]).get((short) 2); @@ -736,6 +857,8 @@ private void processGetKeyCharacteristicsCmd(APDU apdu) { tmpVariables[0] = KMArray.instance((short) 2); KMArray.cast(tmpVariables[0]).add((short) 0, KMInteger.uint_16(KMError.OK)); KMArray.cast(tmpVariables[0]).add((short) 1, data[KEY_CHARACTERISTICS]); + + bufferStartOffset = repository.allocAvailableMemory(); // Encode the response bufferLength = encoder.encode(tmpVariables[0], buffer, bufferStartOffset); sendOutgoing(apdu); @@ -751,6 +874,8 @@ private void processGetHmacSharingParamCmd(APDU apdu) { tmpVariables[3] = KMArray.instance((short) 2); KMArray.cast(tmpVariables[3]).add((short) 0, KMInteger.uint_16(KMError.OK)); KMArray.cast(tmpVariables[3]).add((short) 1, tmpVariables[2]); + + bufferStartOffset = repository.allocAvailableMemory(); // Encode the response bufferLength = encoder.encode(tmpVariables[3], buffer, bufferStartOffset); sendOutgoing(apdu); @@ -774,6 +899,9 @@ private void processDeleteKeyCmd(APDU apdu) { KMArray.cast(argsProto).add((short) 0, KMByteBlob.exp()); // Decode the argument short args = decoder.decode(argsProto, buffer, bufferStartOffset, bufferLength); + //reclaim memory + repository.reclaimMemory(bufferLength); + // Process data[KEY_BLOB] = KMArray.cast(args).get((short) 0); tmpVariables[0] = KMByteBlob.cast(data[KEY_BLOB]).getStartOff(); @@ -814,6 +942,9 @@ private void processComputeSharedHmacCmd(APDU apdu) { KMArray.cast(tmpVariables[2]).add((short) 0, tmpVariables[0]); // Vector of hmac params // Decode the arguments tmpVariables[0] = decoder.decode(tmpVariables[2], buffer, bufferStartOffset, bufferLength); + //reclaim memory + repository.reclaimMemory(bufferLength); + data[HMAC_SHARING_PARAMS] = KMArray.cast(tmpVariables[0]).get((short) 0); // Concatenate HMAC Params tmpVariables[0] = KMArray.cast(data[HMAC_SHARING_PARAMS]).length(); // total number of params @@ -842,9 +973,6 @@ private void processComputeSharedHmacCmd(APDU apdu) { tmpVariables[6]); tmpVariables[3] += tmpVariables[6]; // increment the concat index } else if (tmpVariables[7] == 0) { - // TODO according to hal specs seed should always be empty. Confirm this. - // The seed we are passing is of zero length so if seed length is zero - // the seed generated here is found. tmpVariables[7] = 1; } // if nonce is present get nonce - 32 bytes @@ -891,7 +1019,9 @@ private void processComputeSharedHmacCmd(APDU apdu) { KMByteBlob.cast(tmpVariables[8]).getBuffer(), KMByteBlob.cast(tmpVariables[8]).getStartOff(), KMByteBlob.cast(tmpVariables[8]).length(), - ckdfLable, (short)0,(short)ckdfLable.length, + ckdfLable, + (short) 0, + (short) ckdfLable.length, repository.getHeap(), tmpVariables[1], tmpVariables[3], @@ -917,6 +1047,8 @@ private void processComputeSharedHmacCmd(APDU apdu) { tmpVariables[0] = KMArray.instance((short) 2); KMArray.cast(tmpVariables[0]).add((short) 0, KMInteger.uint_16(KMError.OK)); KMArray.cast(tmpVariables[0]).add((short) 1, tmpVariables[1]); + + bufferStartOffset = repository.allocAvailableMemory(); // Encode the response bufferLength = encoder.encode(tmpVariables[0], buffer, bufferStartOffset); sendOutgoing(apdu); @@ -932,6 +1064,9 @@ private void processUpgradeKeyCmd(APDU apdu) { KMArray.cast(tmpVariables[1]).add((short) 1, tmpVariables[2]); // Key Params // Decode the arguments tmpVariables[2] = decoder.decode(tmpVariables[1], buffer, bufferStartOffset, bufferLength); + //reclaim memory + repository.reclaimMemory(bufferLength); + data[KEY_BLOB] = KMArray.cast(tmpVariables[2]).get((short) 0); data[KEY_PARAMETERS] = KMArray.cast(tmpVariables[2]).get((short) 1); tmpVariables[0] = @@ -947,7 +1082,6 @@ private void processUpgradeKeyCmd(APDU apdu) { // parse existing key blob parseEncryptedKeyBlob(scratchPad); // validate characteristics to be upgraded. - // TODO currently only os version and os patch level are upgraded. tmpVariables[0] = KMKeyParameters.findTag(KMType.UINT_TAG, KMType.OS_VERSION, data[HW_PARAMETERS]); tmpVariables[0] = KMIntegerTag.cast(tmpVariables[0]).getValue(); @@ -974,6 +1108,33 @@ private void processUpgradeKeyCmd(APDU apdu) { tmpVariables[5] = KMError.INVALID_ARGUMENT; } } + //Compare vendor patch levels + tmpVariables[1] = + KMKeyParameters.findTag(KMType.UINT_TAG, KMType.VENDOR_PATCH_LEVEL, data[HW_PARAMETERS]); + tmpVariables[1] = KMIntegerTag.cast(tmpVariables[1]).getValue(); + tmpVariables[2] = repository.getVendorPatchLevel(); + if (tmpVariables[1] != KMType.INVALID_VALUE) { + // The key characteristics should have had vendor patch level < vendor patch level stored in javacard + // then only upgrade is allowed. + if (KMInteger.compare(tmpVariables[1], tmpVariables[2]) != -1) { + // Key Should not be upgraded, but error code should be OK, As per VTS. + tmpVariables[5] = KMError.INVALID_ARGUMENT; + } + } + //Compare boot patch levels + tmpVariables[1] = + KMKeyParameters.findTag(KMType.UINT_TAG, KMType.BOOT_PATCH_LEVEL, data[HW_PARAMETERS]); + tmpVariables[1] = KMIntegerTag.cast(tmpVariables[1]).getValue(); + tmpVariables[2] = repository.getBootPatchLevel(); + if (tmpVariables[1] != KMType.INVALID_VALUE) { + // The key characteristics should have had boot patch level < boot patch level stored in javacard + // then only upgrade is allowed. + if (KMInteger.compare(tmpVariables[1], tmpVariables[2]) != -1) { + // Key Should not be upgraded, but error code should be OK, As per VTS. + tmpVariables[5] = KMError.INVALID_ARGUMENT; + } + } + boolean blobPersisted = false; if (tmpVariables[5] != KMError.INVALID_ARGUMENT) { if (repository.validateAuthTag(data[AUTH_TAG])) { @@ -994,6 +1155,8 @@ private void processUpgradeKeyCmd(APDU apdu) { tmpVariables[0] = KMArray.instance((short) 2); KMArray.cast(tmpVariables[0]).add((short) 0, KMInteger.uint_16(KMError.OK)); KMArray.cast(tmpVariables[0]).add((short) 1, data[KEY_BLOB]); + + bufferStartOffset = repository.allocAvailableMemory(); // Encode the response bufferLength = encoder.encode(tmpVariables[0], buffer, bufferStartOffset); sendOutgoing(apdu); @@ -1025,6 +1188,8 @@ private void processImportWrappedKeyCmd(APDU apdu) { KMArray.cast(tmpVariables[1]).add((short) 11, KMInteger.exp()); // Biometric Sid // Decode the arguments short args = decoder.decode(tmpVariables[1], buffer, bufferStartOffset, bufferLength); + //reclaim memory + repository.reclaimMemory(bufferLength); // Step -0 - check whether the key format and algorithm supported // read algorithm @@ -1176,13 +1341,15 @@ private void processAttestKeyCmd(APDU apdu) { // Decode the argument short args = decoder.decode(argsProto, buffer, bufferStartOffset, bufferLength); + //reclaim memory + repository.reclaimMemory(bufferLength); + data[KEY_BLOB] = KMArray.cast(args).get((short) 0); data[KEY_PARAMETERS] = KMArray.cast(args).get((short) 1); // parse key blob parseEncryptedKeyBlob(scratchPad); - // TODO This below code is added to pass one of the VTS 4.1 tests. - // TODO Need to confirm with Shawn and modify this accordingly. + // This below code is added to pass one of the VTS 4.1 tests. tmpVariables[0] = KMKeyParameters.findTag( KMType.BOOL_TAG, KMType.DEVICE_UNIQUE_ATTESTATION, data[KEY_PARAMETERS]); @@ -1214,8 +1381,8 @@ private void processAttestKeyCmd(APDU apdu) { } cert.attestationChallenge(KMByteTag.cast(tmpVariables[0]).getValue()); // unique id byte blob - uses application id and temporal month count of creation time. - tmpVariables[0] = makeUniqueId(scratchPad); - cert.uniqueId(tmpVariables[0]); + setUniqueId(cert, scratchPad); + // validity period // active time or creation time - byte blob // TODO current assumption is that if active and creation time are missing from characteristics @@ -1232,25 +1399,12 @@ private void processAttestKeyCmd(APDU apdu) { tmpVariables[1] = KMIntegerTag.cast(tmpVariables[1]).getValue(); } // convert milliseconds to UTC date. Start of validity period has to be UTC. - tmpVariables[1] = convertToDate(tmpVariables[1], scratchPad, true); - cert.notBefore(tmpVariables[1]); + cert.notBefore(tmpVariables[1], scratchPad); // expiry time - byte blob tmpVariables[2] = KMKeyParameters.findTag(KMType.DATE_TAG, KMType.USAGE_EXPIRE_DATETIME, data[SW_PARAMETERS]); - if (tmpVariables[2] != KMType.INVALID_VALUE) { - // compare if the expiry time is greater then 2051 then use generalized time format else use - // utc time format - tmpVariables[2] = KMIntegerTag.cast(tmpVariables[1]).getValue(); - tmpVariables[3] = KMInteger.uint_64(firstJan2051, (short) 0); - if (KMInteger.compare(tmpVariables[2], tmpVariables[3]) >= 0) - tmpVariables[2] = convertToDate(tmpVariables[2], scratchPad, false); - else tmpVariables[2] = convertToDate(tmpVariables[1], scratchPad, true); - } else { - // if no expiry tag is present then use the attestation key certificate's expiry time - // that was provisioned in the provision command. This will be in Generalized or UTC time - tmpVariables[2] = repository.getCertExpiryTime(); - } - cert.notAfter(tmpVariables[2]); + cert.notAfter(tmpVariables[2], repository.getCertExpiryTime(), scratchPad, (short) 0); + addAttestationIds(cert); addTags(KMKeyCharacteristics.cast(data[KEY_CHARACTERISTICS]).getHardwareEnforced(), true, cert); addTags( @@ -1261,7 +1415,7 @@ private void processAttestKeyCmd(APDU apdu) { cert.deviceLocked(repository.getDeviceLock()); cert.issuer(repository.getIssuer()); cert.publicKey(data[PUB_KEY]); - cert.signingKey(repository.getAttKeyExponent(), repository.getAttKeyModulus()); + cert.signingKey(repository.getAttKey()); cert.verifiedBootHash(repository.getVerifiedBootHash()); cert.verifiedBootKey(repository.getVerifiedBootKey()); @@ -1279,6 +1433,7 @@ private void processAttestKeyCmd(APDU apdu) { bufferLength = (short) (cert.getCertLength() + (cert.getCertStart() - bufferStartOffset)); sendOutgoing(apdu); } + // -------------------------------- private void addAttestationIds(KMAttestationCert cert) { final short[] attTags = @@ -1333,202 +1488,51 @@ private void addTags(short params, boolean hwEnforced, KMAttestationCert cert) { index++; } } - // -------------------------------------- - private short convertToDate(short time, byte[] scratchPad, boolean utcFlag) { - short yrsCount = 0; - short monthCount = 0; - short dayCount = 0; - short hhCount = 0; - short mmCount = 0; - short ssCount = 0; - byte Z = 0x5A; - boolean from2020 = true; - Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 256, (byte) 0); - Util.arrayCopyNonAtomic( - KMInteger.cast(time).getBuffer(), - KMInteger.cast(time).getStartOff(), - scratchPad, - (short) (8 - KMInteger.cast(time).length()), - KMInteger.cast(time).length()); - // If the time is less then 1 Jan 2020 then it is an error - if (Util.arrayCompare(scratchPad, (short) 0, firstJan2020, (short) 0, (short) 8) < 0) { - KMException.throwIt(KMError.INVALID_ARGUMENT); - } - if (utcFlag - && Util.arrayCompare(scratchPad, (short) 0, firstJan2051, (short) 0, (short) 8) >= 0) { - KMException.throwIt(KMError.INVALID_ARGUMENT); - } - if (Util.arrayCompare(scratchPad, (short) 0, firstJan2051, (short) 0, (short) 8) < 0) { - Util.arrayCopyNonAtomic(firstJan2020, (short) 0, scratchPad, (short) 8, (short) 8); - subtract(scratchPad, (short) 0, (short) 8, (short) 16); - Util.arrayCopyNonAtomic(scratchPad, (short) 16, scratchPad, (short) 0, (short) 8); - } else { - from2020 = false; - Util.arrayCopyNonAtomic(firstJan2051, (short) 0, scratchPad, (short) 8, (short) 8); - subtract(scratchPad, (short) 0, (short) 8, (short) 16); - Util.arrayCopyNonAtomic(scratchPad, (short) 16, scratchPad, (short) 0, (short) 8); - } - // divide the given time with four yrs msec count - if (Util.arrayCompare(scratchPad, (short) 0, fourYrsMsec, (short) 0, (short) 8) >= 0) { - Util.arrayCopyNonAtomic(fourYrsMsec, (short) 0, scratchPad, (short) 8, (short) 8); - yrsCount = divide(scratchPad, (short) 0, (short) 8, (short) 16); // quotient is multiple of 4 - yrsCount = (short) (yrsCount * 4); // number of yrs. - // copy reminder as new dividend - Util.arrayCopyNonAtomic(scratchPad, (short) 16, scratchPad, (short) 0, (short) 8); - } - // divide the given time with one yr msec count - if (Util.arrayCompare(scratchPad, (short) 0, oneYearMsec, (short) 0, (short) 8) >= 0) { - Util.arrayCopyNonAtomic(oneYearMsec, (short) 0, scratchPad, (short) 8, (short) 8); - yrsCount += divide(scratchPad, (short) 0, (short) 8, (short) 16); - Util.arrayCopyNonAtomic(scratchPad, (short) 16, scratchPad, (short) 0, (short) 8); - } - // total yrs from 1970 - if (from2020) yrsCount = (short) (2020 + yrsCount); - else yrsCount = (short) (2051 + yrsCount); - - // divide the given time with one month msec count - if (Util.arrayCompare(scratchPad, (short) 0, oneMonthMsec, (short) 0, (short) 8) >= 0) { - Util.arrayCopyNonAtomic(oneMonthMsec, (short) 0, scratchPad, (short) 8, (short) 8); - monthCount = divide(scratchPad, (short) 0, (short) 8, (short) 16); - Util.arrayCopyNonAtomic(scratchPad, (short) 16, scratchPad, (short) 0, (short) 8); - } - - // divide the given time with one day msec count - if (Util.arrayCompare(scratchPad, (short) 0, oneDayMsec, (short) 0, (short) 8) >= 0) { - Util.arrayCopyNonAtomic(oneDayMsec, (short) 0, scratchPad, (short) 8, (short) 8); - dayCount = divide(scratchPad, (short) 0, (short) 8, (short) 16); - Util.arrayCopyNonAtomic(scratchPad, (short) 16, scratchPad, (short) 0, (short) 8); - } - - // divide the given time with one hour msec count - if (Util.arrayCompare(scratchPad, (short) 0, oneHourMsec, (short) 0, (short) 8) >= 0) { - Util.arrayCopyNonAtomic(oneHourMsec, (short) 0, scratchPad, (short) 8, (short) 8); - hhCount = divide(scratchPad, (short) 0, (short) 8, (short) 16); - Util.arrayCopyNonAtomic(scratchPad, (short) 16, scratchPad, (short) 0, (short) 8); - } - - // divide the given time with one minute msec count - if (Util.arrayCompare(scratchPad, (short) 0, oneMinMsec, (short) 0, (short) 8) >= 0) { - Util.arrayCopyNonAtomic(oneMinMsec, (short) 0, scratchPad, (short) 8, (short) 8); - mmCount = divide(scratchPad, (short) 0, (short) 8, (short) 16); - Util.arrayCopyNonAtomic(scratchPad, (short) 16, scratchPad, (short) 0, (short) 8); - } - - // divide the given time with one second msec count - if (Util.arrayCompare(scratchPad, (short) 0, oneSecMsec, (short) 0, (short) 8) >= 0) { - Util.arrayCopyNonAtomic(oneSecMsec, (short) 0, scratchPad, (short) 8, (short) 8); - ssCount = divide(scratchPad, (short) 0, (short) 8, (short) 16); - Util.arrayCopyNonAtomic(scratchPad, (short) 16, scratchPad, (short) 0, (short) 8); - } - - // Now convert to ascii string YYMMDDhhmmssZ or YYYYMMDDhhmmssZ - Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 256, (byte) 0); - short len = numberToString(yrsCount, scratchPad, (short) 0); // returns YYYY - len += numberToString(monthCount, scratchPad, len); - len += numberToString(dayCount, scratchPad, len); - len += numberToString(hhCount, scratchPad, len); - len += numberToString(mmCount, scratchPad, len); - len += numberToString(ssCount, scratchPad, len); - scratchPad[len] = Z; - len++; - if (utcFlag) return KMByteBlob.instance(scratchPad, (short) 2, (short) (len - 2)); // YY - else return KMByteBlob.instance(scratchPad, (short) 0, len); // YYYY - } - - private short numberToString(short number, byte[] scratchPad, short offset) { - byte zero = 0x30; - byte len = 2; - byte digit; - if (number > 999) len = 4; - byte index = len; - while (index > 0) { - digit = (byte) (number % 10); - number = (short) (number / 10); - scratchPad[(short) (offset + index - 1)] = (byte) (digit + zero); - index--; - } - return len; - } - - private short makeUniqueId(byte[] scratchPad) { - tmpVariables[0] = - KMKeyParameters.findTag(KMType.BOOL_TAG, KMType.INCLUDE_UNIQUE_ID, data[HW_PARAMETERS]); + private void setUniqueId(KMAttestationCert cert, byte[] scratchPad) { + tmpVariables[0] = KMKeyParameters.findTag(KMType.BOOL_TAG, + KMType.INCLUDE_UNIQUE_ID, data[HW_PARAMETERS]); if (tmpVariables[0] == KMType.INVALID_VALUE) { - return 0; + return; } - // Concatenate T||C||R + // temporal count T - tmpVariables[0] = - KMKeyParameters.findTag(KMType.DATE_TAG, KMType.CREATION_DATETIME, data[SW_PARAMETERS]); - if (tmpVariables[0] == KMType.INVALID_VALUE) KMException.throwIt(KMError.INVALID_TAG); + tmpVariables[0] = KMKeyParameters.findTag(KMType.DATE_TAG, + KMType.CREATION_DATETIME, data[SW_PARAMETERS]); + if (tmpVariables[0] == KMType.INVALID_VALUE) + KMException.throwIt(KMError.INVALID_TAG); tmpVariables[0] = KMIntegerTag.cast(tmpVariables[0]).getValue(); - tmpVariables[0] = countTemporalCount(tmpVariables[0], scratchPad); // just a short count - Util.setShort(scratchPad, (short) 0, tmpVariables[0]); - tmpVariables[1] = (short) 2; // Application Id C - tmpVariables[0] = - KMKeyParameters.findTag( - KMType.BYTES_TAG, KMType.ATTESTATION_APPLICATION_ID, data[KEY_PARAMETERS]); - if (tmpVariables[0] == KMType.INVALID_VALUE) + tmpVariables[1] = KMKeyParameters.findTag(KMType.BYTES_TAG, + KMType.ATTESTATION_APPLICATION_ID, data[KEY_PARAMETERS]); + if (tmpVariables[1] == KMType.INVALID_VALUE) KMException.throwIt(KMError.ATTESTATION_APPLICATION_ID_MISSING); - tmpVariables[0] = KMByteTag.cast(tmpVariables[0]).getValue(); - Util.arrayCopyNonAtomic( - KMByteBlob.cast(tmpVariables[0]).getBuffer(), - KMByteBlob.cast(tmpVariables[0]).getStartOff(), - scratchPad, - tmpVariables[1], - KMByteBlob.cast(tmpVariables[0]).length()); - tmpVariables[1] += KMByteBlob.cast(tmpVariables[0]).length(); + tmpVariables[1] = KMByteTag.cast(tmpVariables[1]).getValue(); - // Reset After Rotation R - it will be part of HW Enforced key characteristics - scratchPad[tmpVariables[1]] = (byte) 0; - tmpVariables[0] = - KMKeyParameters.findTag( - KMType.BOOL_TAG, KMType.RESET_SINCE_ID_ROTATION, data[HW_PARAMETERS]); - if (tmpVariables[0] != KMType.INVALID_VALUE) { - scratchPad[tmpVariables[1]] = (byte) 0x01; + // Reset After Rotation R - it will be part of HW Enforced key + // characteristics + byte resetAfterRotation = 0; + tmpVariables[2] = KMKeyParameters.findTag(KMType.BOOL_TAG, + KMType.RESET_SINCE_ID_ROTATION, data[HW_PARAMETERS]); + if (tmpVariables[2] != KMType.INVALID_VALUE) { + resetAfterRotation = 0x01; } - tmpVariables[1]++; - // Sign - signature becomes unique id of 32 bits. Use 128 bits master key as an hmac key. - tmpVariables[0] = KMByteBlob.instance((short) 32); + //master key. tmpVariables[2] = repository.getMasterKeySecret(); - /* - tmpVariables[1]= seProvider.hmacSign( - repository.getMasterKeySecret(),(short)0, - (short)repository.getMasterKeySecret(, ).length, - scratchPad,(short)0,tmpVariables[1], - KMByteBlob.cast(tmpVariables[0]).getBuffer(), - KMByteBlob.cast(tmpVariables[0]).getStartOff()); - */ - tmpVariables[1] = - seProvider.hmacSign( - KMByteBlob.cast(tmpVariables[2]).getBuffer(), - KMByteBlob.cast(tmpVariables[2]).getStartOff(), - KMByteBlob.cast(tmpVariables[2]).length(), + cert.makeUniqueId( scratchPad, (short) 0, - tmpVariables[1], - KMByteBlob.cast(tmpVariables[0]).getBuffer(), - KMByteBlob.cast(tmpVariables[0]).getStartOff()); - if (tmpVariables[1] != 32) { - KMException.throwIt(KMError.UNKNOWN_ERROR); - } - return tmpVariables[0]; - } - - private short countTemporalCount(short time, byte[] scratchPad) { - Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 24, (byte) 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); - return divide(scratchPad, (short) 0, (short) 8, (short) 16); + KMInteger.cast(tmpVariables[0]).getBuffer(), + KMInteger.cast(tmpVariables[0]).getStartOff(), + KMInteger.cast(tmpVariables[0]).length(), + KMByteBlob.cast(tmpVariables[1]).getBuffer(), + KMByteBlob.cast(tmpVariables[1]).getStartOff(), + KMByteBlob.cast(tmpVariables[1]).length(), resetAfterRotation, + KMByteBlob.cast(tmpVariables[2]).getBuffer(), + KMByteBlob.cast(tmpVariables[2]).getStartOff(), + KMByteBlob.cast(tmpVariables[2]).length()); } private void processDestroyAttIdsCmd(APDU apdu) { @@ -1545,6 +1549,9 @@ private void processAbortOperationCmd(APDU apdu) { tmpVariables[1] = KMArray.instance((short) 1); KMArray.cast(tmpVariables[1]).add((short) 0, KMInteger.exp()); tmpVariables[2] = decoder.decode(tmpVariables[1], buffer, bufferStartOffset, bufferLength); + //reclaim memory + repository.reclaimMemory(bufferLength); + data[OP_HANDLE] = KMArray.cast(tmpVariables[2]).get((short) 0); tmpVariables[1] = KMInteger.cast(data[OP_HANDLE]).getShort(); KMOperationState op = repository.findOperation(tmpVariables[1]); @@ -1571,6 +1578,9 @@ private void processFinishOperationCmd(APDU apdu) { KMArray.cast(tmpVariables[1]).add((short) 5, tmpVariables[4]); // Decode the arguments tmpVariables[2] = decoder.decode(tmpVariables[1], buffer, bufferStartOffset, bufferLength); + //reclaim memory + repository.reclaimMemory(bufferLength); + data[OP_HANDLE] = KMArray.cast(tmpVariables[2]).get((short) 0); data[KEY_PARAMETERS] = KMArray.cast(tmpVariables[2]).get((short) 1); data[INPUT_DATA] = KMArray.cast(tmpVariables[2]).get((short) 2); @@ -1612,6 +1622,8 @@ private void processFinishOperationCmd(APDU apdu) { KMArray.cast(tmpVariables[2]).add((short) 1, tmpVariables[1]); KMArray.cast(tmpVariables[2]).add((short) 2, data[OUTPUT_DATA]); + + bufferStartOffset = repository.allocAvailableMemory(); // Encode the response bufferLength = encoder.encode(tmpVariables[2], buffer, bufferStartOffset); sendOutgoing(apdu); @@ -1844,14 +1856,17 @@ private void finishSigningVerifyingOperation(KMOperationState op, byte[] scratch } 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 is always present. - // len of signature will always be 32 bytes. + // As per Keymaster HAL documentation, the length of the Hmac output can + // be decided by using TAG_MAC_LENGTH in Keyparameters. But there is no + // such provision to control the length of the Hmac output using JavaCard + // crypto APIs and the current implementation always returns 32 bytes + // length of Hmac output. So to provide support to TAG_MAC_LENGTH + // feature, we truncate the output signature to TAG_MAC_LENGTH and return + // the truncated signature back to the caller. At the time of verfication + // we again compute the signature of the plain text input, truncate it to + // TAG_MAC_LENGTH and compare it with the input signature for + // verification. So this is the reason we are using KMType.SIGN directly + // instead of using op.getPurpose(). op.getOperation() .sign( KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), @@ -2059,6 +2074,9 @@ private void processUpdateOperationCmd(APDU apdu) { KMArray.cast(tmpVariables[1]).add((short) 4, tmpVariables[4]); // Decode the arguments tmpVariables[2] = decoder.decode(tmpVariables[1], buffer, bufferStartOffset, bufferLength); + //reclaim memory + repository.reclaimMemory(bufferLength); + data[OP_HANDLE] = KMArray.cast(tmpVariables[2]).get((short) 0); data[KEY_PARAMETERS] = KMArray.cast(tmpVariables[2]).get((short) 1); data[INPUT_DATA] = KMArray.cast(tmpVariables[2]).get((short) 2); @@ -2093,8 +2111,8 @@ private void processUpdateOperationCmd(APDU apdu) { if (op.getAlgorithm() == KMType.RSA) { KMException.throwIt(KMError.OPERATION_CANCELLED); } - // TODO refactor and optimize this tmpVariables[0] = KMByteBlob.cast(data[INPUT_DATA]).length(); + short additionalExpOutLen = 0; if (op.getAlgorithm() == KMType.AES) { if (op.getBlockMode() == KMType.GCM) { updateAAD(op, (byte) 0x00); @@ -2108,6 +2126,7 @@ private void processUpdateOperationCmd(APDU apdu) { op.setAesGcmUpdateComplete(); } } + additionalExpOutLen = 16; } else { // input data must be block aligned. // 128 bit block size - HAL must send block aligned data @@ -2122,8 +2141,11 @@ private void processUpdateOperationCmd(APDU apdu) { } } // Allocate output buffer as input data is already block aligned - data[OUTPUT_DATA] = KMByteBlob.instance(tmpVariables[0]); + data[OUTPUT_DATA] = KMByteBlob.instance((short) (tmpVariables[0] + additionalExpOutLen)); // Otherwise just update the data. + // HAL consumes all the input and maintains a buffered data inside it. So the + // applet sends the inputConsumed length as same as the input length. + tmpVariables[3] = tmpVariables[0]; try { tmpVariables[0] = op.getOperation() @@ -2138,7 +2160,7 @@ private void processUpdateOperationCmd(APDU apdu) { } // 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()) { + if (tmpVariables[0] != KMByteBlob.cast(data[OUTPUT_DATA]).length()) { data[INPUT_DATA] = data[OUTPUT_DATA]; data[OUTPUT_DATA] = KMByteBlob.instance(tmpVariables[0]); Util.arrayCopy( @@ -2159,9 +2181,11 @@ private void processUpdateOperationCmd(APDU apdu) { data[OUTPUT_DATA] = KMByteBlob.instance((short) 0); } KMArray.cast(tmpVariables[2]).add((short) 0, KMInteger.uint_16(KMError.OK)); - KMArray.cast(tmpVariables[2]).add((short) 1, KMInteger.uint_16(tmpVariables[0])); + KMArray.cast(tmpVariables[2]).add((short) 1, KMInteger.uint_16(tmpVariables[3])); KMArray.cast(tmpVariables[2]).add((short) 2, tmpVariables[1]); KMArray.cast(tmpVariables[2]).add((short) 3, data[OUTPUT_DATA]); + + bufferStartOffset = repository.allocAvailableMemory(); // Encode the response bufferLength = encoder.encode(tmpVariables[2], buffer, bufferStartOffset); sendOutgoing(apdu); @@ -2192,6 +2216,9 @@ private void processBeginOperationCmd(APDU apdu) { KMArray.cast(tmpVariables[1]).add((short) 3, tmpVariables[3]); // Decode the arguments args = decoder.decode(tmpVariables[1], buffer, bufferStartOffset, bufferLength); + //reclaim memory + repository.reclaimMemory(bufferLength); + data[KEY_PARAMETERS] = KMArray.cast(args).get((short) 2); data[KEY_BLOB] = KMArray.cast(args).get((short) 1); // Check for app id and app data. @@ -2213,7 +2240,8 @@ private void processBeginOperationCmd(APDU apdu) { tmpVariables[0] = KMEnum.cast(tmpVariables[0]).getVal(); data[HW_TOKEN] = KMArray.cast(args).get((short) 3); KMOperationState op = repository.reserveOperation(); - if (op == null) KMException.throwIt(KMError.TOO_MANY_OPERATIONS); + if (op == null) + KMException.throwIt(KMError.TOO_MANY_OPERATIONS); data[OP_HANDLE] = op.getHandle(); op.setPurpose((byte) tmpVariables[0]); op.setKeySize(KMByteBlob.cast(data[SECRET]).length()); @@ -2260,6 +2288,8 @@ private void processBeginOperationCmd(APDU apdu) { KMArray.cast(tmpVariables[0]).add((short) 0, KMInteger.uint_16(KMError.OK)); KMArray.cast(tmpVariables[0]).add((short) 1, tmpVariables[1]); KMArray.cast(tmpVariables[0]).add((short) 2, data[OP_HANDLE]); + + bufferStartOffset = repository.allocAvailableMemory(); // Encode the response bufferLength = encoder.encode(tmpVariables[0], buffer, bufferStartOffset); sendOutgoing(apdu); @@ -2442,8 +2472,6 @@ private void authorizeBlockModeAndMacLength(KMOperationState op) { } op.setMacLength(macLen); } - // 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); @@ -2644,8 +2672,6 @@ private void beginSignVerifyOperation(KMOperationState op) { 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; @@ -2684,16 +2710,21 @@ 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. + // As per Keymaster HAL documentation, the length of the Hmac output can + // be decided by using TAG_MAC_LENGTH in Keyparameters. But there is no + // such provision to control the length of the Hmac output using JavaCard + // crypto APIs and the current implementation always returns 32 bytes + // length of Hmac output. So to provide support to TAG_MAC_LENGTH + // feature, we truncate the output signature to TAG_MAC_LENGTH and return + // the truncated signature back to the caller. At the time of verfication + // we again compute the signature of the plain text input, truncate it to + // TAG_MAC_LENGTH and compare it with the input signature for + // verification. So this is the reason we are using KMType.SIGN directly + // instead of using op.getPurpose(). try { op.setOperation( seProvider.initSymmetricOperation( - (byte) op.getPurpose(), + (byte) KMType.SIGN, op.getAlgorithm(), op.getDigest(), op.getPadding(), @@ -2706,8 +2737,6 @@ private void beginSignVerifyOperation(KMOperationState op) { (short) 0, (short) 0)); } catch (CryptoException exp) { - // TODO remove the following - // Javacard does not support NO digest based signing. KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM); } break; @@ -2858,6 +2887,9 @@ private void processImportKeyCmd(APDU apdu) { KMArray.cast(tmpVariables[1]).add((short) 2, KMByteBlob.exp()); // Decode the arguments tmpVariables[2] = decoder.decode(tmpVariables[1], buffer, bufferStartOffset, bufferLength); + //reclaim memory + repository.reclaimMemory(bufferLength); + data[KEY_PARAMETERS] = KMArray.cast(tmpVariables[2]).get((short) 0); tmpVariables[3] = KMArray.cast(tmpVariables[2]).get((short) 1); data[IMPORTED_KEY_BLOB] = KMArray.cast(tmpVariables[2]).get((short) 2); @@ -2917,7 +2949,6 @@ private void importKey(APDU apdu, byte[] scratchPad) { if (tmpVariables[0] != KMType.INVALID_VALUE) { // before generating key, check whether max count reached if (repository.getKeyBlobCount() > KMRepository.MAX_BLOB_STORAGE) { - // TODO which error to return? KMException.throwIt(KMError.UNKNOWN_ERROR); } repository.persistAuthTag(data[AUTH_TAG]); @@ -2928,6 +2959,8 @@ private void importKey(APDU apdu, byte[] scratchPad) { KMArray.cast(tmpVariables[0]).add((short) 0, KMInteger.uint_16(KMError.OK)); KMArray.cast(tmpVariables[0]).add((short) 1, data[KEY_BLOB]); KMArray.cast(tmpVariables[0]).add((short) 2, data[KEY_CHARACTERISTICS]); + + bufferStartOffset = repository.allocAvailableMemory(); // Encode the response bufferLength = encoder.encode(tmpVariables[0], buffer, bufferStartOffset); sendOutgoing(apdu); @@ -3238,58 +3271,71 @@ private void updateKeyParameters(byte[] ptrArr, short len) { data[KEY_PARAMETERS] = KMKeyParameters.instance(tmpVariables[1]); } - // TODO Add Signature verification. + // This command is executed to set the boot parameters. private void processSetBootParamsCmd(APDU apdu) { receiveIncoming(apdu); + byte[] scratchPad = apdu.getBuffer(); // Argument 1 OS Version // short osVersionExp = KMIntegerTag.exp(KMType.UINT_TAG); tmpVariables[0] = KMInteger.exp(); // Argument 2 OS Patch level // short osPatchExp = KMIntegerTag.exp(KMType.UINT_TAG); tmpVariables[1] = KMInteger.exp(); - // Argument 3 Verified Boot Key + // Argument 3 Vendor Patch level + tmpVariables[2] = KMInteger.exp(); + // Argument 4 Boot Patch level + tmpVariables[3] = KMInteger.exp(); + // Argument 5 Verified Boot Key // short bootKeyExp = KMByteBlob.exp(); - tmpVariables[2] = KMByteBlob.exp(); - // Argument 4 Verified Boot Hash + tmpVariables[4] = KMByteBlob.exp(); + // Argument 6 Verified Boot Hash // short bootHashExp = KMByteBlob.exp(); - tmpVariables[3] = KMByteBlob.exp(); - // Argument 5 Verified Boot State + tmpVariables[5] = KMByteBlob.exp(); + // Argument 7 Verified Boot State // short bootStateExp = KMEnum.instance(KMType.VERIFIED_BOOT_STATE); - tmpVariables[4] = KMEnum.instance(KMType.VERIFIED_BOOT_STATE); - // Argument 6 Device Locked + tmpVariables[6] = KMEnum.instance(KMType.VERIFIED_BOOT_STATE); + // Argument 8 Device Locked // short deviceLockedExp = KMEnum.instance(KMType.DEVICE_LOCKED); - tmpVariables[5] = KMEnum.instance(KMType.DEVICE_LOCKED); + tmpVariables[7] = KMEnum.instance(KMType.DEVICE_LOCKED); // Array of expected arguments - short argsProto = KMArray.instance((short) 6); + short argsProto = KMArray.instance((short) 8); KMArray.cast(argsProto).add((short) 0, tmpVariables[0]); KMArray.cast(argsProto).add((short) 1, tmpVariables[1]); KMArray.cast(argsProto).add((short) 2, tmpVariables[2]); KMArray.cast(argsProto).add((short) 3, tmpVariables[3]); KMArray.cast(argsProto).add((short) 4, tmpVariables[4]); KMArray.cast(argsProto).add((short) 5, tmpVariables[5]); + KMArray.cast(argsProto).add((short) 6, tmpVariables[6]); + KMArray.cast(argsProto).add((short) 7, tmpVariables[7]); // Decode the arguments // System.out.println("Process boot params buffer: "+byteArrayToHexString(buffer)); short args = decoder.decode(argsProto, buffer, bufferStartOffset, bufferLength); + //reclaim memory + repository.reclaimMemory(bufferLength); + // short osVersionTagPtr = KMArray.cast(args).get((short) 0); tmpVariables[0] = KMArray.cast(args).get((short) 0); // short osPatchTagPtr = KMArray.cast(args).get((short) 1); tmpVariables[1] = KMArray.cast(args).get((short) 1); - // short verifiedBootKeyPtr = KMArray.cast(args).get((short) 2); + // short vendorPatchTagPtr = KMArray.cast(args).get((short) 2); tmpVariables[2] = KMArray.cast(args).get((short) 2); - // short verifiedBootHashPtr = KMArray.cast(args).get((short) 3); + // short BootPatchTagPtr = KMArray.cast(args).get((short) 3); tmpVariables[3] = KMArray.cast(args).get((short) 3); - // short verifiedBootStatePtr = KMArray.cast(args).get((short) 4); + // short verifiedBootKeyPtr = KMArray.cast(args).get((short) 4); tmpVariables[4] = KMArray.cast(args).get((short) 4); - // short deviceLockedPtr = KMArray.cast(args).get((short) 5); + // short verifiedBootHashPtr = KMArray.cast(args).get((short) 5); tmpVariables[5] = KMArray.cast(args).get((short) 5); - if (KMByteBlob.cast(tmpVariables[2]).length() > KMRepository.BOOT_KEY_MAX_SIZE) { + // short verifiedBootStatePtr = KMArray.cast(args).get((short) 6); + tmpVariables[6] = KMArray.cast(args).get((short) 6); + // short deviceLockedPtr = KMArray.cast(args).get((short) 7); + tmpVariables[7] = KMArray.cast(args).get((short) 7); + if (KMByteBlob.cast(tmpVariables[4]).length() > KMRepository.BOOT_KEY_MAX_SIZE) { KMException.throwIt(KMError.INVALID_ARGUMENT); } - if (KMByteBlob.cast(tmpVariables[3]).length() > KMRepository.BOOT_HASH_MAX_SIZE) { + if (KMByteBlob.cast(tmpVariables[5]).length() > KMRepository.BOOT_HASH_MAX_SIZE) { KMException.throwIt(KMError.INVALID_ARGUMENT); } - // Begin transaction - // JCSystem.beginTransaction(); + repository.setOsVersion( KMInteger.cast(tmpVariables[0]).getBuffer(), KMInteger.cast(tmpVariables[0]).getStartOff(), @@ -3298,55 +3344,40 @@ private void processSetBootParamsCmd(APDU apdu) { KMInteger.cast(tmpVariables[1]).getBuffer(), KMInteger.cast(tmpVariables[1]).getStartOff(), KMInteger.cast(tmpVariables[1]).length()); - // KMInteger.cast(tmpVariables[0]).value(repository.osVersion, (short) 0); - // KMInteger.cast(tmpVariables[1]).value(repository.osPatch, (short) 0); - // KMInteger.cast(valPtr).getValue(repository.osVersion, (short) 0, (short) 4); - // valPtr = KMIntegerTag.cast(tmpVariables[1]).getValue(); - // KMInteger.cast(valPtr).getValue(repository.osPatch, (short) 0, (short) 4); + repository.setVendorPatchLevel( + KMInteger.cast(tmpVariables[2]).getBuffer(), + KMInteger.cast(tmpVariables[2]).getStartOff(), + KMInteger.cast(tmpVariables[2]).length()); + + repository.setBootPatchLevel( + KMInteger.cast(tmpVariables[3]).getBuffer(), + KMInteger.cast(tmpVariables[3]).getStartOff(), + KMInteger.cast(tmpVariables[3]).length()); - // repository.actualBootKeyLength = KMByteBlob.cast(tmpVariables[2]).length(); - // KMByteBlob.cast(tmpVariables[2]) - // .getValue(repository.verifiedBootKey, (short) 0, repository.actualBootKeyLength); repository.setVerifiedBootKey( - KMByteBlob.cast(tmpVariables[2]).getBuffer(), - KMByteBlob.cast(tmpVariables[2]).getStartOff(), - KMByteBlob.cast(tmpVariables[2]).length()); + KMByteBlob.cast(tmpVariables[4]).getBuffer(), + KMByteBlob.cast(tmpVariables[4]).getStartOff(), + KMByteBlob.cast(tmpVariables[4]).length()); - // repository.actualBootHashLength = KMByteBlob.cast(tmpVariables[3]).length(); - // KMByteBlob.cast(tmpVariables[3]) - // .getValue(repository.verifiedBootHash, (short) 0, repository.actualBootHashLength); repository.setVerifiedBootHash( - KMByteBlob.cast(tmpVariables[3]).getBuffer(), - KMByteBlob.cast(tmpVariables[3]).getStartOff(), - KMByteBlob.cast(tmpVariables[3]).length()); + KMByteBlob.cast(tmpVariables[5]).getBuffer(), + KMByteBlob.cast(tmpVariables[5]).getStartOff(), + KMByteBlob.cast(tmpVariables[5]).length()); - byte enumVal = KMEnum.cast(tmpVariables[4]).getVal(); + byte enumVal = KMEnum.cast(tmpVariables[6]).getVal(); repository.setBootState(enumVal); - /* - if (enumVal == KMTag.SELF_SIGNED_BOOT) { - repository.selfSignedBootFlag = true; - repository.verifiedBootFlag = false; - } else if(enumVal == KMType.VERIFIED_BOOT) { - repository.selfSignedBootFlag = false; - repository.verifiedBootFlag = true; - }else { - repository.selfSignedBootFlag = false; - repository.verifiedBootFlag = false; - } - */ - enumVal = KMEnum.cast(tmpVariables[5]).getVal(); - // repository.deviceLockedFlag = (enumVal == KMType.DEVICE_LOCKED_TRUE); + enumVal = KMEnum.cast(tmpVariables[7]).getVal(); repository.setDeviceLock(enumVal == KMType.DEVICE_LOCKED_TRUE); - if (keymasterState == KMKeymasterApplet.FIRST_SELECT_STATE) { - setBootParamsDone = true; - if (provisionDone) { - keymasterState = KMKeymasterApplet.ACTIVE_STATE; - } - } - // end transaction - // JCSystem.commitTransaction(); + + // Clear the Computed SharedHmac and Hmac nonce from persistent memory. + repository.clearComputedHmac(); + repository.clearHmacNonce(); + + // Hmac is cleared, so generate a new Hmac nonce. + seProvider.newRandomNumber(scratchPad, (short) 0, KMRepository.HMAC_SEED_NONCE_SIZE); + repository.initHmacNonce(scratchPad, (short) 0, KMRepository.HMAC_SEED_NONCE_SIZE); } private static void processGenerateKey(APDU apdu) { @@ -3361,6 +3392,9 @@ private static void processGenerateKey(APDU apdu) { KMArray.cast(tmpVariables[1]).add((short) 0, tmpVariables[0]); // Decode the argument tmpVariables[2] = decoder.decode(tmpVariables[1], buffer, bufferStartOffset, bufferLength); + //reclaim memory + repository.reclaimMemory(bufferLength); + data[KEY_PARAMETERS] = KMArray.cast(tmpVariables[2]).get((short) 0); // Check if EarlyBootEnded tag is present. tmpVariables[0] = @@ -3422,7 +3456,6 @@ private static void processGenerateKey(APDU apdu) { if (tmpVariables[0] != KMType.INVALID_VALUE) { // before generating key, check whether max count reached if (repository.getKeyBlobCount() > KMRepository.MAX_BLOB_STORAGE) { - // TODO which error to return? KMException.throwIt(KMError.UNKNOWN_ERROR); } repository.persistAuthTag(data[AUTH_TAG]); @@ -3433,6 +3466,8 @@ private static void processGenerateKey(APDU apdu) { KMArray.cast(tmpVariables[0]).add((short) 0, KMInteger.uint_16(KMError.OK)); KMArray.cast(tmpVariables[0]).add((short) 1, data[KEY_BLOB]); KMArray.cast(tmpVariables[0]).add((short) 2, data[KEY_CHARACTERISTICS]); + + bufferStartOffset = repository.allocAvailableMemory(); // Encode the response bufferLength = encoder.encode(tmpVariables[0], buffer, bufferStartOffset); @@ -3717,12 +3752,16 @@ private void checkVersionAndPatchLevel(byte[] scratchPad) { private static void makeKeyCharacteristics(byte[] scratchPad) { tmpVariables[0] = repository.getOsPatch(); tmpVariables[1] = repository.getOsVersion(); + tmpVariables[2] = repository.getVendorPatchLevel(); + tmpVariables[3] = repository.getBootPatchLevel(); data[HW_PARAMETERS] = KMKeyParameters.makeHwEnforced( data[KEY_PARAMETERS], (byte) data[ORIGIN], tmpVariables[1], tmpVariables[0], + tmpVariables[2], + tmpVariables[3], scratchPad); data[SW_PARAMETERS] = KMKeyParameters.makeSwEnforced(data[KEY_PARAMETERS], scratchPad); data[KEY_CHARACTERISTICS] = KMKeyCharacteristics.instance(); @@ -3734,9 +3773,6 @@ private static void createEncryptedKeyBlob(byte[] scratchPad) { // make key characteristics - returns key characteristics in data[KEY_CHARACTERISTICS] makeKeyCharacteristics(scratchPad); // make root of trust blob - // data[ROT] = - // KMByteBlob.instance( - // repository.verifiedBootKey, (short) 0, (short) repository.verifiedBootKey.length); data[ROT] = repository.getVerifiedBootKey(); // make hidden key params list @@ -3752,7 +3788,9 @@ private static void createEncryptedKeyBlob(byte[] scratchPad) { KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_AUTH_TAG, data[AUTH_TAG]); KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_NONCE, data[NONCE]); KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_KEYCHAR, data[KEY_CHARACTERISTICS]); - tmpVariables[0] = repository.alloc((short) 1024); // TODO use buffer + + // allocate reclaimable memory. + tmpVariables[0] = repository.alloc((short) 1024); tmpVariables[1] = encoder.encode(data[KEY_BLOB], repository.getHeap(), tmpVariables[0]); data[KEY_BLOB] = KMByteBlob.instance(repository.getHeap(), tmpVariables[0], tmpVariables[1]); } @@ -3930,30 +3968,57 @@ private static short deriveKey(byte[] scratchPad) { tmpVariables[1] = repository.alloc((short) 256); // generate derivation material from hidden parameters tmpVariables[2] = encoder.encode(tmpVariables[0], repository.getHeap(), tmpVariables[1]); - // create derived key i.e. MAC - /* tmpVariables[3] = - seProvider.aesCCMSign( + // KeyDerivation: + // 1. AesGCM Encryption, with below input parameters. + // authData - HIDDEN_PARAMTERS + // Key - Master Key + // InputData - AUTH_DATA + // IV - NONCE + // 2. After encryption it generates two outputs + // a. Encrypted output + // b. Auth Tag + // 3. Do HMAC Sign, with below input parameters. + // Key - Auth Tag (Generated in step 2). + // Input data - Encrypted output (Generated in step 2). + // 4. HMAC Sign generates an output of 32 bytes length. + // Consume only first 16 bytes as derived key. + tmpVariables[4] = repository.getMasterKeySecret(); + tmpVariables[5] = repository.alloc(AES_GCM_AUTH_TAG_LENGTH); + tmpVariables[3] = + seProvider.aesGCMEncrypt( + KMByteBlob.cast(tmpVariables[4]).getBuffer(), + KMByteBlob.cast(tmpVariables[4]).getStartOff(), + KMByteBlob.cast(tmpVariables[4]).length(), + repository.getHeap(), + data[AUTH_DATA], + data[AUTH_DATA_LENGTH], + scratchPad, + (short) 0, + KMByteBlob.cast(data[NONCE]).getBuffer(), + KMByteBlob.cast(data[NONCE]).getStartOff(), + KMByteBlob.cast(data[NONCE]).length(), repository.getHeap(), tmpVariables[1], tmpVariables[2], - repository.getMasterKeySecret(), - scratchPad, - (short) 0); - */ - tmpVariables[4] = repository.getMasterKeySecret(); - tmpVariables[3] = - seProvider.aesCCMSign( + repository.getHeap(), + tmpVariables[5], + AES_GCM_AUTH_TAG_LENGTH); + // Hmac sign. + tmpVariables[3] = seProvider.hmacSign( repository.getHeap(), - tmpVariables[1], - tmpVariables[2], - KMByteBlob.cast(tmpVariables[4]).getBuffer(), - KMByteBlob.cast(tmpVariables[4]).getStartOff(), - KMByteBlob.cast(tmpVariables[4]).length(), + tmpVariables[5], + AES_GCM_AUTH_TAG_LENGTH, scratchPad, - (short) 0); - if (tmpVariables[3] < 0) { + (short) 0, + tmpVariables[3], + repository.getHeap(), + tmpVariables[1]); + if (tmpVariables[3] < 16) { KMException.throwIt(KMError.UNKNOWN_ERROR); } + tmpVariables[3] = 16; + Util.arrayCopyNonAtomic(repository.getHeap(), tmpVariables[1], scratchPad, + (short) 0, tmpVariables[3]); // store the derived secret in data dictionary data[DERIVED_KEY] = repository.alloc(tmpVariables[3]); Util.arrayCopyNonAtomic( @@ -3962,6 +4027,7 @@ private static short deriveKey(byte[] scratchPad) { } private static void sendError(APDU apdu, short err) { + bufferStartOffset = repository.alloc((short) 2); bufferLength = encoder.encodeError(err, buffer, bufferStartOffset, (short) 5); sendOutgoing(apdu); } @@ -3986,27 +4052,6 @@ private short addIntegers(short num1, short num2) { return KMInteger.uint_64(scratchPad, (short) (buf + 16)); } - /* - // num1 must be greater then or equal to num2 and both must be positive - private short subtractIntegers(short num1, short num2){ - short buf = repository.alloc((short)24); - byte[] scratchPad = repository.getHeap(); - Util.arrayFillNonAtomic(scratchPad, buf, (short)24,(byte)0); - Util.arrayCopyNonAtomic( - KMInteger.cast(num1).getBuffer(), - KMInteger.cast(num1).getStartOff(),scratchPad,(short)(buf+8-KMInteger.cast(num1).length()), - KMInteger.cast(num1).length()); - Util.arrayCopyNonAtomic( - KMInteger.cast(num2).getBuffer(), - KMInteger.cast(num2).getStartOff(),scratchPad,(short)(buf+16-KMInteger.cast(num2).length()), - KMInteger.cast(num2).length()); - if(scratchPad[buf] < 0 || scratchPad[(short)(buf+8)] <0)return KMType.INVALID_VALUE; - if(Util.arrayCompare(scratchPad,buf, scratchPad,(short)(buf+8), (short)8) < 1) return KMType.INVALID_VALUE; - subtract(scratchPad,buf,(short)(buf+8),(short)(buf+16)); - return KMInteger.uint_64(scratchPad,(short)(buf+16)); - } - */ - private void add(byte[] buf, short op1, short op2, short result) { byte index = 7; byte carry = 0; @@ -4014,92 +4059,50 @@ private void add(byte[] buf, short op1, short op2, short result) { while (index >= 0) { tmp = (short) (buf[(short) (op1 + index)] + buf[(short) (op2 + index)] + carry); carry = 0; - if (tmp > 255) carry = 1; // max unsigned byte value is 255 + if (tmp > 255) + carry = 1; // max unsigned byte value is 255 buf[(short) (result + index)] = (byte) (tmp & (byte) 0xFF); index--; } } - - // subtraction by borrowing. - private void subtract(byte[] buf, short op1, short op2, short result) { - byte borrow = 0; - byte index = 7; - short r; - short x; - short y; - while (index >= 0) { - x = (short) (buf[(short) (op1 + index)] & 0xFF); - y = (short) (buf[(short) (op2 + index)] & 0xFF); - r = (short) (x - y - borrow); - borrow = 0; - if (r < 0) { - borrow = 1; - r = (short) (r + 256); // max unsigned byte value is 255 - } - buf[(short) (result + index)] = (byte) (r & 0xFF); - 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. - private short divide(byte[] buf, short dividend, short divisor, short remainder) { - short expCnt = 1; - short q = 0; - // first increase divisor so that it becomes greater then dividend. - while (compare(buf, divisor, dividend) < 0) { - shiftLeft(buf, divisor); - expCnt = (short) (expCnt << 1); - } - // Now subtract divisor from dividend if dividend is greater then divisor. - // Copy remainder in the dividend and repeat. - while (expCnt != 0) { - if (compare(buf, dividend, divisor) >= 0) { - subtract(buf, dividend, divisor, remainder); - copy(buf, remainder, dividend); - q = (short) (q + expCnt); - } - expCnt = (short) (expCnt >> 1); - shiftRight(buf, divisor); - } - return q; - } - - private void copy(byte[] buf, short from, short to) { - Util.arrayCopyNonAtomic(buf, from, buf, to, (short) 8); +/* + @Override + public void onCleanup() { } - private byte compare(byte[] buf, short lhs, short rhs) { - return Util.arrayCompare(buf, lhs, buf, rhs, (short) 8); + @Override + public void onConsolidate() { } - private void shiftLeft(byte[] buf, short start) { - byte index = 7; - byte carry = 0; - byte tmp; - while (index >= 0) { - tmp = buf[(short) (start + index)]; - buf[(short) (start + index)] = (byte) (buf[(short) (start + index)] << 1); - buf[(short) (start + index)] = (byte) (buf[(short) (start + index)] + carry); - if (tmp < 0) carry = 1; - else carry = 0; - index--; - } + @Override + public void onRestore(Element element) { + element.initRead(); + provisionStatus = element.readByte(); + keymasterState = element.readByte(); + repository.onRestore(element); + seProvider.onRestore(element); } - private void shiftRight(byte[] buf, short start) { - byte index = 0; - byte carry = 0; - byte tmp; - while (index < 8) { - tmp = (byte) (buf[(short) (start + index)] & 0x01); - buf[(short) (start + index)] = (byte) (buf[(short) (start + index)] >> 1); - buf[(short) (start + index)] = (byte) (buf[(short) (start + index)] & 0x7F); - buf[(short) (start + index)] = (byte) (buf[(short) (start + index)] | carry); - if (tmp == 1) carry = (byte) 0x80; - else carry = 0; - index++; - } + @Override + public Element onSave() { + // SEProvider count + short primitiveCount = seProvider.getBackupPrimitiveByteCount(); + short objectCount = seProvider.getBackupObjectCount(); + //Repository count + primitiveCount += repository.getBackupPrimitiveByteCount(); + objectCount += repository.getBackupObjectCount(); + //KMKeymasterApplet count + primitiveCount += computePrimitveDataSize(); + objectCount += computeObjectCount(); + + // Create element. + Element element = UpgradeManager.createElement(Element.TYPE_SIMPLE, + primitiveCount, objectCount); + element.write(provisionStatus); + element.write(keymasterState); + repository.onSave(element); + seProvider.onSave(element); + return element; } +*/ } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMOperation.java b/Applet/src/com/android/javacard/keymaster/KMOperation.java similarity index 100% rename from Applet/Applet/src/com/android/javacard/keymaster/KMOperation.java rename to Applet/src/com/android/javacard/keymaster/KMOperation.java diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMOperationState.java b/Applet/src/com/android/javacard/keymaster/KMOperationState.java similarity index 98% rename from Applet/Applet/src/com/android/javacard/keymaster/KMOperationState.java rename to Applet/src/com/android/javacard/keymaster/KMOperationState.java index 62a499f2..6647a6a8 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMOperationState.java +++ b/Applet/src/com/android/javacard/keymaster/KMOperationState.java @@ -129,12 +129,13 @@ private void dataUpdated(){ } public void release() { + Object[] ops = ((Object[]) slot[REFS]); + ((KMOperation)ops[OPERATION]).abort(); 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; + ops[OPERATION] = null; + ops[HMAC_SIGNER] = null; JCSystem.commitTransaction(); reset(); } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMRepository.java b/Applet/src/com/android/javacard/keymaster/KMRepository.java similarity index 72% rename from Applet/Applet/src/com/android/javacard/keymaster/KMRepository.java rename to Applet/src/com/android/javacard/keymaster/KMRepository.java index 04ad5f6f..b8dc8b8d 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMRepository.java +++ b/Applet/src/com/android/javacard/keymaster/KMRepository.java @@ -16,6 +16,8 @@ package com.android.javacard.keymaster; +import org.globalplatform.upgrade.Element; + import javacard.framework.ISO7816; import javacard.framework.ISOException; import javacard.framework.JCSystem; @@ -25,9 +27,9 @@ * KMRepository class manages persistent and volatile memory usage by the applet. Note the repository * is only used by applet and it is not intended to be used by seProvider. */ -public class KMRepository { +public class KMRepository implements KMUpgradable { // Data table configuration - public static final short DATA_INDEX_SIZE = 32; + public static final short DATA_INDEX_SIZE = 33; public static final short DATA_INDEX_ENTRY_SIZE = 4; public static final short DATA_MEM_SIZE = 2048; public static final short HEAP_SIZE = 10000; @@ -47,42 +49,43 @@ public class KMRepository { public static final byte ATT_ID_MEID = 5; public static final byte ATT_ID_MANUFACTURER = 6; public static final byte ATT_ID_MODEL = 7; - public static final byte ATT_EXPONENT = 12; - public static final byte ATT_MODULUS = 13; - public static final byte CERT_AUTH_KEY_ID = 14; - public static final byte CERT_ISSUER = 15; - public static final byte CERT_EXPIRY_TIME = 16; - public static final byte BOOT_OS_VERSION = 17; - public static final byte BOOT_OS_PATCH = 18; - public static final byte BOOT_VERIFIED_BOOT_KEY = 19; - public static final byte BOOT_VERIFIED_BOOT_HASH = 20; - public static final byte BOOT_VERIFIED_BOOT_STATE = 21; - public static final byte BOOT_DEVICE_LOCKED_STATUS = 22; - public static final byte BOOT_DEVICE_LOCKED_TIME = 23; - public static final byte AUTH_TAG_1 = 24; - public static final byte AUTH_TAG_2 = 25; - public static final byte AUTH_TAG_3 = 26; - public static final byte AUTH_TAG_4 = 27; - public static final byte AUTH_TAG_5 = 28; - public static final byte AUTH_TAG_6 = 29; - public static final byte AUTH_TAG_7 = 30; - public static final byte AUTH_TAG_8 = 31; + public static final byte ATT_EC_KEY = 12; + public static final byte CERT_AUTH_KEY_ID = 13; + public static final byte CERT_ISSUER = 14; + public static final byte CERT_EXPIRY_TIME = 15; + public static final byte BOOT_OS_VERSION = 16; + public static final byte BOOT_OS_PATCH = 17; + public static final byte VENDOR_PATCH_LEVEL = 18; + public static final byte BOOT_PATCH_LEVEL = 19; + public static final byte BOOT_VERIFIED_BOOT_KEY = 20; + public static final byte BOOT_VERIFIED_BOOT_HASH = 21; + public static final byte BOOT_VERIFIED_BOOT_STATE = 22; + public static final byte BOOT_DEVICE_LOCKED_STATUS = 23; + public static final byte BOOT_DEVICE_LOCKED_TIME = 24; + public static final byte AUTH_TAG_1 = 25; + public static final byte AUTH_TAG_2 = 26; + public static final byte AUTH_TAG_3 = 27; + public static final byte AUTH_TAG_4 = 28; + public static final byte AUTH_TAG_5 = 29; + public static final byte AUTH_TAG_6 = 30; + public static final byte AUTH_TAG_7 = 31; + public static final byte AUTH_TAG_8 = 32; // Data Item sizes public static final short MASTER_KEY_SIZE = 16; public static final short SHARED_SECRET_KEY_SIZE = 32; - public static final short ATT_KEY_MOD_SIZE = 256; - public static final short ATT_KEY_EXP_SIZE = 256; public static final short HMAC_SEED_NONCE_SIZE = 32; public static final short COMPUTED_HMAC_KEY_SIZE = 32; public static final short OS_VERSION_SIZE = 4; public static final short OS_PATCH_SIZE = 4; + public static final short VENDOR_PATCH_SIZE = 4; + public static final short BOOT_PATCH_SIZE = 4; public static final short DEVICE_LOCK_TS_SIZE = 8; public static final short DEVICE_LOCK_FLAG_SIZE = 1; public static final short BOOT_STATE_SIZE = 1; public static final short MAX_BLOB_STORAGE = 8; public static final short AUTH_TAG_LENGTH = 12; - public static final short AUTH_TAG_ENTRY_SIZE = 14; + public static final short AUTH_TAG_ENTRY_SIZE = 15; public static final short MAX_OPS = 4; public static final byte BOOT_KEY_MAX_SIZE = 32; public static final byte BOOT_HASH_MAX_SIZE = 32; @@ -94,6 +97,7 @@ public class KMRepository { private short heapIndex; private byte[] dataTable; private short dataIndex; + private short reclaimIndex; // Singleton instance private static KMRepository repository; @@ -102,10 +106,11 @@ public static KMRepository instance() { return repository; } - public KMRepository() { - newDataTable(); + public KMRepository(boolean isUpgrading) { + newDataTable(isUpgrading); heap = JCSystem.makeTransientByteArray(HEAP_SIZE, JCSystem.CLEAR_ON_RESET); heapIndex = 0; + reclaimIndex = HEAP_SIZE; operationStateTable = new Object[MAX_OPS]; // create and initialize operation state table. byte index = 0; @@ -229,7 +234,15 @@ public void initComputedHmac(byte[] key, short start, short len) { public void initHmacNonce(byte[] nonce, short offset, short len) { if (len != HMAC_SEED_NONCE_SIZE) { KMException.throwIt(KMError.INVALID_INPUT_LENGTH);} writeDataEntry(HMAC_NONCE,nonce,offset,len); - } + } + + public void clearHmacNonce() { + clearDataEntry(HMAC_NONCE); + } + + public void clearComputedHmac() { + clearDataEntry(COMPUTED_HMAC_KEY); + } public void onUninstall() { // Javacard Runtime environment cleans up the data. @@ -241,6 +254,7 @@ public void onProcess() {} public void clean() { Util.arrayFillNonAtomic(heap, (short) 0, heapIndex, (byte) 0); heapIndex = 0; + reclaimIndex = HEAP_SIZE; } public void onDeselect() {} @@ -253,8 +267,38 @@ public short getMasterKeySecret() { return readData(MASTER_KEY); } + // This function uses memory from the back of the heap(transient memory). Call + // reclaimMemory function immediately after the use. + public short allocReclaimableMemory(short length) { + // TODO Verify the below condition (HEAP_SIZE/2) + if ((((short) (reclaimIndex - length)) <= heapIndex) + || (length >= HEAP_SIZE / 2)) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + reclaimIndex -= length; + return reclaimIndex; + } + + // Reclaims the memory back. + public void reclaimMemory(short length) { + if (reclaimIndex < heapIndex) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + reclaimIndex += length; + } + + public short allocAvailableMemory() { + if (heapIndex >= heap.length) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + short index = heapIndex; + heapIndex = (short) heap.length; + return index; + } + public short alloc(short length) { - if (((short) (heapIndex + length)) > heap.length) { + if ((((short) (heapIndex + length)) > heap.length) || + (((short) (heapIndex + length)) > reclaimIndex)) { ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); } heapIndex += length; @@ -270,10 +314,12 @@ private short dataAlloc(short length) { } - private void newDataTable(){ - if(dataTable == null) { - dataTable = new byte[DATA_MEM_SIZE]; - dataIndex = (short)(DATA_INDEX_SIZE*DATA_INDEX_ENTRY_SIZE); + private void newDataTable(boolean isUpgrading){ + if (!isUpgrading) { + if (dataTable == null) { + dataTable = new byte[DATA_MEM_SIZE]; + dataIndex = (short) (DATA_INDEX_SIZE * DATA_INDEX_ENTRY_SIZE); + } } } @@ -297,7 +343,7 @@ private void clearDataEntry(short id){ if (dataLen != 0) { short dataPtr = Util.getShort(dataTable,(short)(id+DATA_INDEX_ENTRY_OFFSET)); Util.arrayFillNonAtomic(dataTable, dataPtr,dataLen,(byte)0); - Util.arrayFillNonAtomic(dataTable, id,DATA_INDEX_ENTRY_SIZE,(byte)0); + //Util.arrayFillNonAtomic(dataTable, id,DATA_INDEX_ENTRY_SIZE,(byte)0); } JCSystem.commitTransaction(); } @@ -355,21 +401,46 @@ public short getComputedHmacKey() { return readData(COMPUTED_HMAC_KEY); } + private byte readAuthTagState(byte[] buf, short offset) { + return buf[offset]; + } + + private void writeAuthTagState(byte[] buf, short offset, byte state) { + buf[offset] = state; + } + public void persistAuthTag(short authTag) { - if(KMByteBlob.cast(authTag).length() != AUTH_TAG_LENGTH)KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + if (KMByteBlob.cast(authTag).length() != AUTH_TAG_LENGTH) + KMException.throwIt(KMError.INVALID_INPUT_LENGTH); short authTagEntry = alloc(AUTH_TAG_ENTRY_SIZE); + short offset = alloc(AUTH_TAG_ENTRY_SIZE); + writeAuthTagState( + KMByteBlob.cast(authTagEntry).getBuffer(), + KMByteBlob.cast(authTagEntry).getStartOff(), + (byte) 1); Util.arrayCopyNonAtomic( - KMByteBlob.cast(authTag).getBuffer(), - KMByteBlob.cast(authTag).getStartOff(), - getHeap(),authTagEntry, AUTH_TAG_LENGTH); - Util.setShort(getHeap(),(short)(authTagEntry+AUTH_TAG_LENGTH),(short)0); + KMByteBlob.cast(authTag).getBuffer(), + KMByteBlob.cast(authTag).getStartOff(), + getHeap(), authTagEntry, AUTH_TAG_LENGTH); + Util.setShort(getHeap(), (short) (authTagEntry + AUTH_TAG_LENGTH +1), + (short) 0); short index = 0; while (index < MAX_BLOB_STORAGE) { - if(dataLength((short)(index+AUTH_TAG_1)) == 0){ - writeDataEntry((short)(index+AUTH_TAG_1), - KMByteBlob.cast(authTagEntry).getBuffer(), - KMByteBlob.cast(authTagEntry).getStartOff(), - AUTH_TAG_ENTRY_SIZE); + if (dataLength((short) (index + AUTH_TAG_1)) != 0) { + readDataEntry((short) (index + AUTH_TAG_1), getHeap(), offset); + if (0 == readAuthTagState(getHeap(), offset)) { + writeDataEntry((short) (index + AUTH_TAG_1), + KMByteBlob.cast(authTagEntry).getBuffer(), + KMByteBlob.cast(authTagEntry).getStartOff(), + AUTH_TAG_ENTRY_SIZE); + break; + } + } else { + writeDataEntry((short) (index + AUTH_TAG_1), + KMByteBlob.cast(authTagEntry).getBuffer(), + KMByteBlob.cast(authTagEntry).getStartOff(), + AUTH_TAG_ENTRY_SIZE); + break; } index++; } @@ -397,15 +468,16 @@ public void removeAllAuthTags() { private short findTag(short authTag) { if(KMByteBlob.cast(authTag).length() != AUTH_TAG_LENGTH)KMException.throwIt(KMError.INVALID_INPUT_LENGTH); short index = 0; - short authTagEntry; short found; + short offset = alloc(AUTH_TAG_ENTRY_SIZE); while (index < MAX_BLOB_STORAGE) { if (dataLength((short)(index+AUTH_TAG_1)) != 0) { - authTagEntry = readData((short)(index+AUTH_TAG_1)); + readDataEntry((short)(index+AUTH_TAG_1), + getHeap(), offset); found = Util.arrayCompare( - KMByteBlob.cast(authTagEntry).getBuffer(), - KMByteBlob.cast(authTagEntry).getStartOff(), + getHeap(), + (short)(offset+1), KMByteBlob.cast(authTag).getBuffer(), KMByteBlob.cast(authTag).getStartOff(), AUTH_TAG_LENGTH); @@ -422,41 +494,36 @@ public short getRateLimitedKeyCount(short authTag) { if (tag != KMType.INVALID_VALUE) { blob = readData(tag); return Util.getShort(KMByteBlob.cast(blob).getBuffer(), - (short)(KMByteBlob.cast(blob).getStartOff()+AUTH_TAG_LENGTH)); + (short)(KMByteBlob.cast(blob).getStartOff()+AUTH_TAG_LENGTH+1)); } return KMType.INVALID_VALUE; } public void setRateLimitedKeyCount(short authTag, short val) { short tag = findTag(authTag); - short blob; if (tag != KMType.INVALID_VALUE) { - blob = alloc((short)2); - Util.setShort(getHeap(),blob,val); - writeDataEntry(tag,getHeap(), blob,(short)2); + short dataPtr = readData(tag); + Util.setShort( + KMByteBlob.cast(dataPtr).getBuffer(), + (short)(KMByteBlob.cast(dataPtr).getStartOff()+AUTH_TAG_LENGTH+1), + val); + writeDataEntry(tag, + KMByteBlob.cast(dataPtr).getBuffer(), + KMByteBlob.cast(dataPtr).getStartOff(), + KMByteBlob.cast(dataPtr).length()); } } - public void persistAttestationKey(short mod, short exp) { - if(KMByteBlob.cast(mod).length() != ATT_KEY_MOD_SIZE || - KMByteBlob.cast(exp).length() != ATT_KEY_EXP_SIZE) KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); - writeDataEntry(ATT_MODULUS, - KMByteBlob.cast(mod).getBuffer(), - KMByteBlob.cast(mod).getStartOff(), - KMByteBlob.cast(mod).length()); - writeDataEntry(ATT_EXPONENT, - KMByteBlob.cast(exp).getBuffer(), - KMByteBlob.cast(exp).getStartOff(), - KMByteBlob.cast(exp).length()); + public void persistAttestationKey(short secret) { + writeDataEntry(ATT_EC_KEY, + KMByteBlob.cast(secret).getBuffer(), + KMByteBlob.cast(secret).getStartOff(), + KMByteBlob.cast(secret).length()); } - public short getAttKeyModulus() { - return readData(ATT_MODULUS); - } - - public short getAttKeyExponent() { - return readData(ATT_EXPONENT); + public short getAttKey() { + return readData(ATT_EC_KEY); } public void persistAttId(byte id, byte[] buf, short start, short len){ @@ -521,6 +588,26 @@ public short getOsVersion(){ } } + public short getVendorPatchLevel(){ + short blob = readData(VENDOR_PATCH_LEVEL); + if (blob != 0) { + return KMInteger.uint_32( + KMByteBlob.cast(blob).getBuffer(), KMByteBlob.cast(blob).getStartOff()); + }else{ + return KMInteger.uint_32(zero,(short)0); + } + } + + public short getBootPatchLevel(){ + short blob = readData(BOOT_PATCH_LEVEL); + if (blob != 0) { + return KMInteger.uint_32( + KMByteBlob.cast(blob).getBuffer(), KMByteBlob.cast(blob).getStartOff()); + }else{ + return KMInteger.uint_32(zero,(short)0); + } + } + public short getOsPatch(){ short blob = readData(BOOT_OS_PATCH); if (blob != 0) { @@ -569,6 +656,18 @@ public void setOsVersion(byte[] buf, short start, short len){ writeDataEntry(BOOT_OS_VERSION,buf,start,len); } + public void setVendorPatchLevel(byte[] buf, short start, short len) { + if (len != VENDOR_PATCH_SIZE) + KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + writeDataEntry(VENDOR_PATCH_LEVEL, buf, start, len); + } + + public void setBootPatchLevel(byte[] buf, short start, short len) { + if (len != BOOT_PATCH_SIZE) + KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + writeDataEntry(BOOT_PATCH_LEVEL, buf, start, len); + } + public void setDeviceLock(boolean flag){ short start = alloc(DEVICE_LOCK_FLAG_SIZE); if(flag) (getHeap())[start] = (byte)((getHeap())[start] | 0x01); @@ -602,6 +701,7 @@ public void setVerifiedBootKey(byte[] buf, short start, short len){ writeDataEntry(BOOT_VERIFIED_BOOT_KEY,buf,start,len); } + public void setVerifiedBootHash(byte[] buf, short start, short len){ if(len > BOOT_HASH_MAX_SIZE) KMException.throwIt(KMError.INVALID_INPUT_LENGTH); writeDataEntry(BOOT_VERIFIED_BOOT_HASH,buf,start,len); @@ -622,4 +722,28 @@ public short getKeyBlobCount(){ } return count; } + + @Override + public void onSave(Element ele) { + ele.write(dataIndex); + ele.write(dataTable); + } + + @Override + public void onRestore(Element ele) { + dataIndex = ele.readShort(); + dataTable = (byte[]) ele.readObject(); + } + + @Override + public short getBackupPrimitiveByteCount() { + // dataIndex + return (short) 2; + } + + @Override + public short getBackupObjectCount() { + // dataTable + return (short) 1; + } } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMSEProvider.java b/Applet/src/com/android/javacard/keymaster/KMSEProvider.java similarity index 89% rename from Applet/Applet/src/com/android/javacard/keymaster/KMSEProvider.java rename to Applet/src/com/android/javacard/keymaster/KMSEProvider.java index ff8c6c92..b3e9c266 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMSEProvider.java +++ b/Applet/src/com/android/javacard/keymaster/KMSEProvider.java @@ -1,12 +1,14 @@ package com.android.javacard.keymaster; +import org.globalplatform.upgrade.Element; + /** * KMSEProvider is facade to use SE specific methods. The main intention of this interface is to * abstract the cipher, signature and backup and restore related functions. The instance of this * interface is created by the singleton KMSEProviderImpl class for each provider. At a time there * can be only one provider in the applet package. */ -public interface KMSEProvider { +public interface KMSEProvider extends KMUpgradable { /** * Create a symmetric key instance. If the algorithm and/or keysize are not supported then it * should throw a CryptoException. @@ -326,15 +328,11 @@ short rsaDecipherOAEP256( short outputDataStart); /** - * This is a oneshot operation that decrypts the data using RSA algorithm with oaep256 padding. - * The public exponent is always 0x010001. + * This is a oneshot operation that signs the data using EC private key. * - * @param privExp is the private exponent (2048 bit) buffer. - * @param privExpStart is the start of the private exponent buffer. - * @param privExpLength is the length of the private exponent buffer in bytes. - * @param modBuffer is the modulus (2048 bit) buffer. - * @param modOff is the start of the modulus buffer. - * @param modLength is the length of the modulus buffer in bytes. + * @param secret is the private key of P-256 curve. + * @param secretStart is the start of the private key buffer. + * @param secretLength is the length of the private buffer in bytes. * @param inputDataBuf is the buffer of the input data. * @param inputDataStart is the start of the input data buffer. * @param inputDataLength is the length of the inpur data buffer in bytes. @@ -342,18 +340,15 @@ short rsaDecipherOAEP256( * @param outputDataStart is the start of the output data buffer. * @return length of the decrypted data. */ - short rsaSignPKCS1256( - byte[] privExp, - short privExpStart, - short privExpLength, - byte[] modBuffer, - short modOff, - short modLength, - byte[] inputDataBuf, - short inputDataStart, - short inputDataLength, - byte[] outputDataBuf, - short outputDataStart); + public short ecSign256( + byte[] secret, + short secretStart, + short secretLength, + byte[] inputDataBuf, + short inputDataStart, + short inputDataLength, + byte[] outputDataBuf, + short outputDataStart); /** * This creates a persistent operation for signing, verify, encryption and decryption using HMAC, @@ -436,29 +431,56 @@ KMOperation initAsymmetricOperation( KMAttestationCert getAttestationCert(boolean rsaCert); /** - * This operation indicates whether SE Provider supports backup and restore functionality required - * for upgrading the applet. + * This operation persists the certificate chain in the persistent memory + * in multiple requests. * - * @return true if backup and restore is supported. + * @param buf buffer containing certificate chain. + * @param offset is the start of the buffer. + * @param len is the length of the buffer. + * @param totalLen is the total length of cert chain. */ - boolean isBackupRestoreSupported(); + public void persistPartialCertificateChain(byte[] buf, short offset, short len, short totalLen); /** - * This operation passes the data that needs to be backup to SE Provider. The exact mechanism to - * backup the data is SE provider implementation specific. + * The operation reads the certificate chain from persistent memory. * - * @param buf is the data buffer. - * @param start is the start of the data. - * @param len is the length of the data. + * @param buf is the start of data buffer. + * @param offset is the start of the data. + * @return the length of the data buffer in bytes. */ - void backup(byte[] buf, short start, short len); + short readCertificateChain(byte[] buf, short offset); /** - * This operation retrieves the backed up data from SE Provider. + * This function returns the cert chain length. * - * @param buf is the data buffer. - * @param start is the start of the data. - * @return the length of the data buffer in bytes. + * @return length of the certificate chain. + */ + short getCertificateChainLength(); + + /** + * This function tells if boot signal event is supported or not. + * + * @return true if supported, false otherwise. + */ + boolean isBootSignalEventSupported(); + + /** + * This function tells if the device is booted or not. + * + * @return true if device booted, false otherwise. + */ + boolean isDeviceRebooted(); + + /** + * This function is supposed to be used to reset the device booted stated after set boot param is handled + * @param resetBootFlag is false if event has been handled + */ + void clearDeviceBooted(boolean resetBootFlag); + + /** + * This function tells if applet is upgrading or not. + * + * @return true if upgrading, otherwise false. */ - short restore(byte[] buf, short start); + boolean isUpgrading(); } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMTag.java b/Applet/src/com/android/javacard/keymaster/KMTag.java similarity index 100% rename from Applet/Applet/src/com/android/javacard/keymaster/KMTag.java rename to Applet/src/com/android/javacard/keymaster/KMTag.java diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMType.java b/Applet/src/com/android/javacard/keymaster/KMType.java similarity index 100% rename from Applet/Applet/src/com/android/javacard/keymaster/KMType.java rename to Applet/src/com/android/javacard/keymaster/KMType.java diff --git a/Applet/src/com/android/javacard/keymaster/KMUpgradable.java b/Applet/src/com/android/javacard/keymaster/KMUpgradable.java new file mode 100644 index 00000000..e3958a67 --- /dev/null +++ b/Applet/src/com/android/javacard/keymaster/KMUpgradable.java @@ -0,0 +1,14 @@ +package com.android.javacard.keymaster; + +import org.globalplatform.upgrade.Element; + +public interface KMUpgradable { + void onSave(Element ele); + + void onRestore(Element ele); + + short getBackupPrimitiveByteCount(); + + short getBackupObjectCount(); + +} diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMVerificationToken.java b/Applet/src/com/android/javacard/keymaster/KMVerificationToken.java similarity index 100% rename from Applet/Applet/src/com/android/javacard/keymaster/KMVerificationToken.java rename to Applet/src/com/android/javacard/keymaster/KMVerificationToken.java diff --git a/HAL/keymaster/4.1/CommonUtils.cpp b/HAL/keymaster/4.1/CommonUtils.cpp index 67993957..a6d2b7ab 100644 --- a/HAL/keymaster/4.1/CommonUtils.cpp +++ b/HAL/keymaster/4.1/CommonUtils.cpp @@ -27,6 +27,10 @@ #include #include +#define TAG_SEQUENCE 0x30 +#define LENGTH_MASK 0x80 +#define LENGTH_VALUE_MASK 0x7F + namespace keymaster { namespace V4_1 { namespace javacard { @@ -222,6 +226,52 @@ pubModulus) { return ErrorCode::OK; } +ErrorCode getCertificateChain(std::vector& chainBuffer, std::vector>& certChain) { + uint8_t *data = chainBuffer.data(); + int index = 0; + uint32_t length = 0; + while (index < chainBuffer.size()) { + std::vector temp; + if(data[index] == TAG_SEQUENCE) { + //read next byte + if (0 == (data[index+1] & LENGTH_MASK)) { + length = (uint32_t)data[index]; + //Add SEQ and Length fields + length += 2; + } else { + int additionalBytes = data[index+1] & LENGTH_VALUE_MASK; + if (additionalBytes == 0x01) { + length = data[index+2]; + //Add SEQ and Length fields + length += 3; + } else if (additionalBytes == 0x02) { + length = (data[index+2] << 8 | data[index+3]); + //Add SEQ and Length fields + length += 4; + } else if (additionalBytes == 0x04) { + length = data[index+2] << 24; + length |= data[index+3] << 16; + length |= data[index+4] << 8; + length |= data[index+5]; + //Add SEQ and Length fields + length += 6; + } else { + //Length is larger than uint32_t max limit. + return ErrorCode::UNKNOWN_ERROR; + } + } + temp.insert(temp.end(), (data+index), (data+index+length)); + index += length; + + certChain.push_back(std::move(temp)); + } else { + //SEQUENCE TAG MISSING. + return ErrorCode::UNKNOWN_ERROR; + } + } + return ErrorCode::OK; +} + } // namespace javacard } // namespace V4_1 diff --git a/HAL/keymaster/4.1/JavacardKeymaster4Device.cpp b/HAL/keymaster/4.1/JavacardKeymaster4Device.cpp index c2775de6..23583024 100644 --- a/HAL/keymaster/4.1/JavacardKeymaster4Device.cpp +++ b/HAL/keymaster/4.1/JavacardKeymaster4Device.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -44,16 +45,14 @@ #define APDU_P1 0x40 #define APDU_P2 0x00 #define APDU_RESP_STATUS_OK 0x9000 -#define ROOT_RSA_KEY "/data/data/rsa_key.der" -#define ROOT_RSA_CERT "/data/data/certificate_rsa.der" -/*This property is used to check if javacard is already provisioned or not */ -#define KM_JAVACARD_PROVISIONED_PROPERTY "keymaster.javacard.provisioned" + +#define INS_BEGIN_KM_CMD 0x00 +#define INS_END_KM_PROVISION_CMD 0x20 +#define INS_END_KM_CMD 0x7F namespace keymaster { namespace V4_1 { namespace javacard { -//This key is used as master key for computing Hmac shared secret. -constexpr uint8_t kFakeKeyAgreementKey[32] = {}; static std::unique_ptr pTransportFactory = nullptr; constexpr size_t kOperationTableSize = 4; @@ -63,29 +62,29 @@ struct KM_AUTH_LIST_Delete { }; enum class Instruction { - INS_GENERATE_KEY_CMD = 0x10, - INS_IMPORT_KEY_CMD = 0x11, - INS_IMPORT_WRAPPED_KEY_CMD = 0x12, - INS_EXPORT_KEY_CMD = 0x13, - INS_ATTEST_KEY_CMD = 0x14, - INS_UPGRADE_KEY_CMD = 0x15, - INS_DELETE_KEY_CMD = 0x16, - INS_DELETE_ALL_KEYS_CMD = 0x17, - INS_ADD_RNG_ENTROPY_CMD = 0x18, - INS_COMPUTE_SHARED_HMAC_CMD = 0x19, - INS_DESTROY_ATT_IDS_CMD = 0x1A, - INS_VERIFY_AUTHORIZATION_CMD = 0x1B, - INS_GET_HMAC_SHARING_PARAM_CMD = 0x1C, - INS_GET_KEY_CHARACTERISTICS_CMD = 0x1D, - INS_GET_HW_INFO_CMD = 0x1E, - INS_BEGIN_OPERATION_CMD = 0x1F, - INS_UPDATE_OPERATION_CMD = 0x20, - INS_FINISH_OPERATION_CMD = 0x21, - INS_ABORT_OPERATION_CMD = 0x22, - INS_PROVISION_CMD = 0x23, - INS_SET_BOOT_PARAMS_CMD = 0x24, - INS_DEVICE_LOCKED_CMD = 0x25, - INS_EARLY_BOOT_ENDED_CMD = 0x26, + // Keymaster commands + INS_GENERATE_KEY_CMD = INS_END_KM_PROVISION_CMD+1, + INS_IMPORT_KEY_CMD = INS_END_KM_PROVISION_CMD+2, + INS_IMPORT_WRAPPED_KEY_CMD = INS_END_KM_PROVISION_CMD+3, + INS_EXPORT_KEY_CMD = INS_END_KM_PROVISION_CMD+4, + INS_ATTEST_KEY_CMD = INS_END_KM_PROVISION_CMD+5, + INS_UPGRADE_KEY_CMD = INS_END_KM_PROVISION_CMD+6, + INS_DELETE_KEY_CMD = INS_END_KM_PROVISION_CMD+7, + INS_DELETE_ALL_KEYS_CMD = INS_END_KM_PROVISION_CMD+8, + INS_ADD_RNG_ENTROPY_CMD = INS_END_KM_PROVISION_CMD+9, + INS_COMPUTE_SHARED_HMAC_CMD = INS_END_KM_PROVISION_CMD+10, + INS_DESTROY_ATT_IDS_CMD = INS_END_KM_PROVISION_CMD+11, + INS_VERIFY_AUTHORIZATION_CMD = INS_END_KM_PROVISION_CMD+12, + INS_GET_HMAC_SHARING_PARAM_CMD = INS_END_KM_PROVISION_CMD+13, + INS_GET_KEY_CHARACTERISTICS_CMD = INS_END_KM_PROVISION_CMD+14, + INS_GET_HW_INFO_CMD = INS_END_KM_PROVISION_CMD+15, + INS_BEGIN_OPERATION_CMD = INS_END_KM_PROVISION_CMD+16, + INS_UPDATE_OPERATION_CMD = INS_END_KM_PROVISION_CMD+17, + INS_FINISH_OPERATION_CMD = INS_END_KM_PROVISION_CMD+18, + INS_ABORT_OPERATION_CMD = INS_END_KM_PROVISION_CMD+19, + INS_DEVICE_LOCKED_CMD = INS_END_KM_PROVISION_CMD+20, + INS_EARLY_BOOT_ENDED_CMD = INS_END_KM_PROVISION_CMD+21, + INS_GET_CERT_CHAIN_CMD = INS_END_KM_PROVISION_CMD+22 }; static inline std::unique_ptr& getTransportFactoryInstance() { @@ -97,29 +96,6 @@ static inline std::unique_ptr& getTransportFacto return pTransportFactory; } -static inline bool readDataFromFile(const char *filename, std::vector& data) { - FILE *fp; - bool ret = true; - fp = fopen(filename, "rb"); - if(fp == NULL) { - LOG(ERROR) << "Failed to open file: " << filename; - return false; - } - fseek(fp, 0L, SEEK_END); - long int filesize = ftell(fp); - rewind(fp); - std::unique_ptr buf(new uint8_t[filesize]); - if( 0 == fread(buf.get(), filesize, 1, fp)) { - LOG(ERROR) << "No Content in the file: " << filename; - ret = false; - } - if(true == ret) { - data.insert(data.begin(), buf.get(), buf.get() + filesize); - } - fclose(fp); - return ret; -} - static inline bool findTag(const hidl_vec& params, Tag tag) { size_t size = params.size(); for(size_t i = 0; i < size; ++i) { @@ -140,84 +116,6 @@ static inline bool getTag(const hidl_vec& params, Tag tag, KeyPara return false; } -static inline X509* parseDerCertificate(const char* filename) { - X509 *x509 = NULL; - std::vector certData; - - /* Read the Root certificate */ - if(!readDataFromFile(filename, certData)) { - LOG(ERROR) << " Failed to read the Root certificate"; - return NULL; - } - /* Create BIO instance from certificate data */ - BIO *bio = BIO_new_mem_buf(certData.data(), certData.size()); - if(bio == NULL) { - LOG(ERROR) << " Failed to create BIO from buffer."; - return NULL; - } - /* Create X509 instance from BIO */ - x509 = d2i_X509_bio(bio, NULL); - if(x509 == NULL) { - LOG(ERROR) << " Failed to get X509 instance from BIO."; - return NULL; - } - BIO_free(bio); - return x509; -} - -static inline void getDerSubjectName(X509* x509, std::vector& subject) { - uint8_t *subjectDer = NULL; - X509_NAME* asn1Subject = X509_get_subject_name(x509); - if(asn1Subject == NULL) { - LOG(ERROR) << " Failed to read the subject."; - return; - } - /* Convert X509_NAME to der encoded subject */ - int len = i2d_X509_NAME(asn1Subject, &subjectDer); - if (len < 0) { - LOG(ERROR) << " Failed to get readable name from X509_NAME."; - return; - } - subject.insert(subject.begin(), subjectDer, subjectDer+len); -} - -static inline void getAuthorityKeyIdentifier(X509* x509, std::vector& authKeyId) { - long xlen; - int tag, xclass; - - int loc = X509_get_ext_by_NID(x509, NID_authority_key_identifier, -1); - X509_EXTENSION *ext = X509_get_ext(x509, loc); - if(ext == NULL) { - LOG(ERROR) << " Failed to read authority key identifier."; - return; - } - - ASN1_OCTET_STRING *asn1AuthKeyId = X509_EXTENSION_get_data(ext); - const uint8_t *strAuthKeyId = ASN1_STRING_get0_data(asn1AuthKeyId); - int strAuthKeyIdLen = ASN1_STRING_length(asn1AuthKeyId); - int ret = ASN1_get_object(&strAuthKeyId, &xlen, &tag, &xclass, strAuthKeyIdLen); - if (ret == 0x80 || strAuthKeyId == NULL) { - LOG(ERROR) << "Failed to get the auth key identifier from ASN1 sequence."; - return; - } - authKeyId.insert(authKeyId.begin(), strAuthKeyId, strAuthKeyId + xlen); -} - -static inline void getNotAfter(X509* x509, std::vector& notAfterDate) { - const ASN1_TIME* notAfter = X509_get0_notAfter(x509); - if(notAfter == NULL) { - LOG(ERROR) << " Failed to read expiry time."; - return; - } - int strNotAfterLen = ASN1_STRING_length(notAfter); - const uint8_t *strNotAfter = ASN1_STRING_get0_data(notAfter); - if(strNotAfter == NULL) { - LOG(ERROR) << " Failed to read expiry time from ASN1 string."; - return; - } - notAfterDate.insert(notAfterDate.begin(), strNotAfter, strNotAfter + strNotAfterLen); -} - ErrorCode encodeParametersVerified(const VerificationToken& verificationToken, std::vector& asn1ParamsVerified) { if (verificationToken.parametersVerified.size() > 0) { AuthorizationSet paramSet; @@ -316,7 +214,8 @@ keyFormat, std::vector& wrappedKeyDescription) { return ErrorCode::OK; } -ErrorCode constructApduMessage(Instruction& ins, std::vector& inputData, std::vector& apduOut) { +ErrorCode constructApduMessage(Instruction& ins, std::vector& inputData, std::vector& apduOut, bool +extendedOutput=false) { apduOut.push_back(static_cast(APDU_CLS)); //CLS apduOut.push_back(static_cast(ins)); //INS apduOut.push_back(static_cast(APDU_P1)); //P1 @@ -341,6 +240,8 @@ ErrorCode constructApduMessage(Instruction& ins, std::vector& inputData apduOut.insert(apduOut.end(), inputData.begin(), inputData.end()); //Expected length of output apduOut.push_back(static_cast(0x00));//Accepting complete length of output at a time + if(extendedOutput) + apduOut.push_back(static_cast(0x00)); } else { return (ErrorCode::INSUFFICIENT_BUFFER_SPACE); @@ -354,177 +255,32 @@ uint16_t getStatus(std::vector& inputData) { return (inputData.at(inputData.size()-2) << 8) | (inputData.at(inputData.size()-1)); } -ErrorCode initiateProvision() { - /* This is just a reference implemenation */ - std::string brand("Google"); - std::string device("Pixel 3A"); - std::string product("Pixel"); - std::string serial("UGYJFDjFeRuBEH"); - std::string imei("987080543071019"); - std::string meid("27863510227963"); - std::string manufacturer("Foxconn"); - std::string model("HD1121"); - AuthorizationSet authSet(AuthorizationSetBuilder() - .Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA) - .Authorization(TAG_PADDING, KM_PAD_RSA_PKCS1_1_5_SIGN) - .Authorization(TAG_DIGEST, KM_DIGEST_SHA_2_256) - .Authorization(TAG_KEY_SIZE, 2048) - .Authorization(TAG_PURPOSE, static_cast(0x7F)) /* The value 0x7F is not present in types.hal */ - .Authorization(TAG_ATTESTATION_ID_BRAND, brand.data(), brand.size()) - .Authorization(TAG_ATTESTATION_ID_DEVICE, device.data(), device.size()) - .Authorization(TAG_ATTESTATION_ID_PRODUCT, product.data(), product.size()) - .Authorization(TAG_ATTESTATION_ID_SERIAL, serial.data(), serial.size()) - .Authorization(TAG_ATTESTATION_ID_IMEI, imei.data(), imei.size()) - .Authorization(TAG_ATTESTATION_ID_MEID, meid.data(), meid.size()) - .Authorization(TAG_ATTESTATION_ID_MANUFACTURER, manufacturer.data(), manufacturer.size()) - .Authorization(TAG_ATTESTATION_ID_MODEL, model.data(), model.size())); - - hidl_vec keyParams = kmParamSet2Hidl(authSet); - std::vector data; - if(!readDataFromFile(ROOT_RSA_KEY, data)) { - LOG(ERROR) << " Failed to read the Root rsa key"; - return ErrorCode::UNKNOWN_ERROR; - } - return JavacardKeymaster4Device::provision(keyParams, KeyFormat::PKCS8, data); -} - -Return setBootParams() { - std::vector verifiedBootKey(32, 0); - std::vector verifiedBootKeyHash(32, 0); - - return JavacardKeymaster4Device::setBootParams(GetOsVersion(), GetOsPatchlevel(), verifiedBootKey, verifiedBootKeyHash, - KM_VERIFIED_BOOT_UNVERIFIED, 0/*deviceLocked*/); -} - -ErrorCode sendData(Instruction ins, std::vector& inData, std::vector& response) { +ErrorCode sendData(Instruction ins, std::vector& inData, std::vector& response, bool + extendedOutput=false) { ErrorCode ret = ErrorCode::UNKNOWN_ERROR; std::vector apdu; - if(!android::base::GetBoolProperty(KM_JAVACARD_PROVISIONED_PROPERTY, false)) { - if(ErrorCode::OK != (ret = setBootParams())) { - LOG(ERROR) << "Failed to set boot params"; - return ret; - } - - if(ErrorCode::OK != (ret = initiateProvision())) { - LOG(ERROR) << "Failed to provision the device"; - return ret; - } - android::base::SetProperty(KM_JAVACARD_PROVISIONED_PROPERTY, "true"); + // TODO In real scenario the provision happens in the factory. In that case this + // below code is not required. This is just used for simulation. + if (ErrorCode::OK != (ret = provision(getTransportFactoryInstance()))) { + LOG(ERROR) << "Failed to provision the device"; + return ret; } - ret = constructApduMessage(ins, inData, apdu); + ret = constructApduMessage(ins, inData, apdu, extendedOutput); if(ret != ErrorCode::OK) return ret; if(!getTransportFactoryInstance()->sendData(apdu.data(), apdu.size(), response)) { return (ErrorCode::SECURE_HW_COMMUNICATION_FAILED); } - if((response.size() < 2) || (getStatus(response) != APDU_RESP_STATUS_OK)) { + // Response size should be greater than 2. Cbor output data followed by two bytes of APDU status. + if((response.size() <= 2) || (getStatus(response) != APDU_RESP_STATUS_OK)) { return (ErrorCode::UNKNOWN_ERROR); } return (ErrorCode::OK);//success } -ErrorCode JavacardKeymaster4Device::provision(const hidl_vec& keyParams, KeyFormat keyFormat, const hidl_vec& - keyData) { - cppbor::Array array; - cppbor::Array subArray; - std::unique_ptr item; - std::vector apdu; - hidl_vec keyBlob; - ErrorCode errorCode = ErrorCode::UNKNOWN_ERROR; - Instruction ins = Instruction::INS_PROVISION_CMD; - std::vector response; - CborConverter cborConverter; - X509 *x509 = NULL; - std::vector subject; - std::vector authorityKeyIdentifier; - std::vector notAfter; - std::vector masterKey(kFakeKeyAgreementKey, kFakeKeyAgreementKey + - sizeof(kFakeKeyAgreementKey)/sizeof(kFakeKeyAgreementKey[0])); - - /* Subject, AuthorityKeyIdentifier and Expirty time of the root certificate are required by javacard. */ - /* Get X509 certificate instance for the root certificate.*/ - if(NULL == (x509 = parseDerCertificate(ROOT_RSA_CERT))) { - return errorCode; - } - - if(ErrorCode::OK != (errorCode = prepareCborArrayFromKeyData(keyParams, keyFormat, keyData, subArray))) { - return errorCode; - } - /* Get subject in DER */ - getDerSubjectName(x509, subject); - /* Get AuthorityKeyIdentifier */ - getAuthorityKeyIdentifier(x509, authorityKeyIdentifier); - /* Get Expirty Time */ - getNotAfter(x509, notAfter); - /*Free X509 */ - X509_free(x509); - - /* construct cbor */ - cborConverter.addKeyparameters(array, keyParams); - array.add(static_cast(KeyFormat::RAW)); - std::vector encodedArray = subArray.encode(); - cppbor::Bstr bstr(encodedArray.begin(), encodedArray.end()); - array.add(bstr); - array.add(subject); - array.add(notAfter); - array.add(authorityKeyIdentifier); - array.add(masterKey); - std::vector cborData = array.encode(); - - if(ErrorCode::OK != (errorCode = constructApduMessage(ins, cborData, apdu))) - return errorCode; - - if(!getTransportFactoryInstance()->sendData(apdu.data(), apdu.size(), response)) { - return (ErrorCode::SECURE_HW_COMMUNICATION_FAILED); - } - - if((response.size() < 2) || (getStatus(response) != APDU_RESP_STATUS_OK)) { - return (ErrorCode::UNKNOWN_ERROR); - } - - if((response.size() > 2)) { - //Skip last 2 bytes in cborData, it contains status. - std::tie(item, errorCode) = cborConverter.decodeData(std::vector(response.begin(), response.end()-2), - true); - } - return errorCode; -} - -ErrorCode JavacardKeymaster4Device::setBootParams(uint32_t osVersion, uint32_t osPatchLevel, const std::vector& verifiedBootKey, - std::vector& verifiedBootKeyHash, keymaster_verified_boot_t kmVerifiedBoot, bool deviceLocked) { - cppbor::Array array; - std::vector apdu; - std::vector response; - Instruction ins = Instruction::INS_SET_BOOT_PARAMS_CMD; - array.add(osVersion). - add(osPatchLevel). - /* Verified Boot Key */ - add(verifiedBootKey). - /* Verified Boot Hash */ - add(verifiedBootKeyHash). - /* boot state */ - add(static_cast(kmVerifiedBoot)). - /* device locked */ - add(static_cast(deviceLocked)); - std::vector cborData = array.encode(); - - ErrorCode ret = constructApduMessage(ins, cborData, apdu); - if(ret != ErrorCode::OK) return ret; - - if(!getTransportFactoryInstance()->sendData(apdu.data(), apdu.size(), response)) { - return (ErrorCode::SECURE_HW_COMMUNICATION_FAILED); - } - - if((response.size() < 2) || (getStatus(response) != APDU_RESP_STATUS_OK)) { - return (ErrorCode::UNKNOWN_ERROR); - } - return ErrorCode::OK; - -} - JavacardKeymaster4Device::JavacardKeymaster4Device(): softKm_(new ::keymaster::AndroidKeymaster( []() -> auto { auto context = new JavaCardSoftKeymasterContext(); @@ -548,23 +304,21 @@ Return JavacardKeymaster4Device::getHardwareInfo(getHardwareInfo_cb _hidl_ hidl_string jcKeymasterAuthor; ErrorCode ret = sendData(Instruction::INS_GET_HW_INFO_CMD, input, resp); - if(ret == ErrorCode::SECURE_HW_COMMUNICATION_FAILED) { + if(ret != ErrorCode::OK) { //Socket not connected. _hidl_cb(SecurityLevel::STRONGBOX, JAVACARD_KEYMASTER_NAME, JAVACARD_KEYMASTER_AUTHOR); return Void(); } else { - if((ret == ErrorCode::OK) && (resp.size() > 2)) { - //Skip last 2 bytes in cborData, it contains status. - std::tie(item, ret) = cborConverter_.decodeData(std::vector(resp.begin(), resp.end()-2), - true); - if (item != nullptr) { - std::vector temp; - if(!cborConverter_.getUint64(item, 0, securityLevel) || - !cborConverter_.getBinaryArray(item, 1, jcKeymasterName) || - !cborConverter_.getBinaryArray(item, 2, jcKeymasterAuthor)) { - _hidl_cb(static_cast(securityLevel), jcKeymasterName, jcKeymasterAuthor); - return Void(); - } + //Skip last 2 bytes in cborData, it contains status. + std::tie(item, ret) = cborConverter_.decodeData(std::vector(resp.begin(), resp.end()-2), + true); + if (item != nullptr) { + std::vector temp; + if(!cborConverter_.getUint64(item, 0, securityLevel) || + !cborConverter_.getBinaryArray(item, 1, jcKeymasterName) || + !cborConverter_.getBinaryArray(item, 2, jcKeymasterAuthor)) { + _hidl_cb(static_cast(securityLevel), jcKeymasterName, jcKeymasterAuthor); + return Void(); } } _hidl_cb(static_cast(securityLevel), jcKeymasterName, jcKeymasterAuthor); @@ -582,9 +336,8 @@ Return JavacardKeymaster4Device::getHmacSharingParameters(getHmacSharingPa std::unique_ptr item; HmacSharingParameters hmacSharingParameters; ErrorCode errorCode = ErrorCode::UNKNOWN_ERROR; - errorCode = sendData(Instruction::INS_GET_HMAC_SHARING_PARAM_CMD, input, cborData); - if(errorCode == ErrorCode::SECURE_HW_COMMUNICATION_FAILED) { + if(errorCode != ErrorCode::OK) { auto response = softKm_->GetHmacSharingParameters(); ::android::hardware::keymaster::V4_0::HmacSharingParameters params; params.seed.setToExternal(const_cast(response.params.seed.data), @@ -594,14 +347,12 @@ Return JavacardKeymaster4Device::getHmacSharingParameters(getHmacSharingPa _hidl_cb(legacy_enum_conversion(response.error), params); return Void(); } else { - if((errorCode == ErrorCode::OK) && (cborData.size() > 2)) { - //Skip last 2 bytes in cborData, it contains status. - std::tie(item, errorCode) = cborConverter_.decodeData(std::vector(cborData.begin(), cborData.end()-2), - true); - if (item != nullptr) { - if(!cborConverter_.getHmacSharingParameters(item, 1, hmacSharingParameters)) { - errorCode = ErrorCode::UNKNOWN_ERROR; - } + //Skip last 2 bytes in cborData, it contains status. + std::tie(item, errorCode) = cborConverter_.decodeData(std::vector(cborData.begin(), cborData.end()-2), + true); + if (item != nullptr) { + if(!cborConverter_.getHmacSharingParameters(item, 1, hmacSharingParameters)) { + errorCode = ErrorCode::UNKNOWN_ERROR; } } _hidl_cb(errorCode, hmacSharingParameters); @@ -636,7 +387,7 @@ Return JavacardKeymaster4Device::computeSharedHmac(const hidl_vec cborData = array.encode(); errorCode = sendData(Instruction::INS_COMPUTE_SHARED_HMAC_CMD, cborData, cborOutData); - if(errorCode == ErrorCode::SECURE_HW_COMMUNICATION_FAILED) { + if(errorCode != ErrorCode::OK) { ComputeSharedHmacRequest request; request.params_array.params_array = new keymaster::HmacSharingParameters[params.size()]; request.params_array.num_params = params.size(); @@ -657,17 +408,15 @@ Return JavacardKeymaster4Device::computeSharedHmac(const hidl_vec 2)) { - //Skip last 2 bytes in cborData, it contains status. - std::tie(item, errorCode) = cborConverter_.decodeData(std::vector(cborOutData.begin(), cborOutData.end()-2), - true); - if (item != nullptr) { - std::vector bstr; - if(!cborConverter_.getBinaryArray(item, 1, bstr)) { - errorCode = ErrorCode::UNKNOWN_ERROR; - } else { - sharingCheck = bstr; - } + //Skip last 2 bytes in cborData, it contains status. + std::tie(item, errorCode) = cborConverter_.decodeData(std::vector(cborOutData.begin(), cborOutData.end()-2), + true); + if (item != nullptr) { + std::vector bstr; + if(!cborConverter_.getBinaryArray(item, 1, bstr)) { + errorCode = ErrorCode::UNKNOWN_ERROR; + } else { + sharingCheck = bstr; } } _hidl_cb(errorCode, sharingCheck); @@ -693,8 +442,7 @@ Return JavacardKeymaster4Device::addRngEntropy(const hidl_vec cborData = array.encode(); errorCode = sendData(Instruction::INS_ADD_RNG_ENTROPY_CMD, cborData, cborOutData); - - if((errorCode == ErrorCode::OK) && (cborOutData.size() > 2)) { + if(errorCode == ErrorCode::OK) { //Skip last 2 bytes in cborData, it contains status. std::tie(item, errorCode) = cborConverter_.decodeData(std::vector(cborOutData.begin(), cborOutData.end()-2), true); @@ -726,7 +474,7 @@ Return JavacardKeymaster4Device::generateKey(const hidl_vec& errorCode = sendData(Instruction::INS_GENERATE_KEY_CMD, cborData, cborOutData); - if((errorCode == ErrorCode::OK) && (cborOutData.size() > 2)) { + if(errorCode == ErrorCode::OK) { //Skip last 2 bytes in cborData, it contains status. std::tie(item, errorCode) = cborConverter_.decodeData(std::vector(cborOutData.begin(), cborOutData.end()-2), true); @@ -772,7 +520,7 @@ Return JavacardKeymaster4Device::importKey(const hidl_vec& k errorCode = sendData(Instruction::INS_IMPORT_KEY_CMD, cborData, cborOutData); - if((errorCode == ErrorCode::OK) && (cborOutData.size() > 2)) { + if(errorCode == ErrorCode::OK) { //Skip last 2 bytes in cborData, it contains status. std::tie(item, errorCode) = cborConverter_.decodeData(std::vector(cborOutData.begin(), cborOutData.end()-2), true); @@ -822,12 +570,12 @@ Return JavacardKeymaster4Device::importWrappedKey(const hidl_vec& cborConverter_.addKeyparameters(array, unwrappingParams); array.add(std::vector(wrappedKeyDescription)); array.add(passwordSid); - array.add(biometricSid); /* TODO if biometricSid optional if user not sent this don't encode this cbor format */ + array.add(biometricSid); std::vector cborData = array.encode(); errorCode = sendData(Instruction::INS_IMPORT_WRAPPED_KEY_CMD, cborData, cborOutData); - if((errorCode == ErrorCode::OK) && (cborOutData.size() > 2)) { + if(errorCode == ErrorCode::OK) { //Skip last 2 bytes in cborData, it contains status. std::tie(item, errorCode) = cborConverter_.decodeData(std::vector(cborOutData.begin(), cborOutData.end()-2), true); @@ -860,7 +608,7 @@ Return JavacardKeymaster4Device::getKeyCharacteristics(const hidl_vec 2)) { + if(errorCode == ErrorCode::OK) { //Skip last 2 bytes in cborData, it contains status. std::tie(item, errorCode) = cborConverter_.decodeData(std::vector(cborOutData.begin(), cborOutData.end()-2), true); @@ -909,6 +657,8 @@ Return JavacardKeymaster4Device::exportKey(KeyFormat exportFormat, const h return Void(); } + + Return JavacardKeymaster4Device::attestKey(const hidl_vec& keyToAttest, const hidl_vec& attestParams, attestKey_cb _hidl_cb) { cppbor::Array array; std::unique_ptr item; @@ -920,10 +670,9 @@ Return JavacardKeymaster4Device::attestKey(const hidl_vec& keyToA array.add(std::vector(keyToAttest)); cborConverter_.addKeyparameters(array, attestParams); std::vector cborData = array.encode(); - errorCode = sendData(Instruction::INS_ATTEST_KEY_CMD, cborData, cborOutData); - if((errorCode == ErrorCode::OK) && (cborOutData.size() > 2)) { + if(errorCode == ErrorCode::OK) { std::vector> temp; std::vector rootCert; //Skip last 2 bytes in cborData, it contains status. @@ -933,14 +682,26 @@ Return JavacardKeymaster4Device::attestKey(const hidl_vec& keyToA if(!cborConverter_.getMultiBinaryArray(item, 1, temp)) { errorCode = ErrorCode::UNKNOWN_ERROR; } else { - if(readDataFromFile(ROOT_RSA_CERT, rootCert)) { - temp.push_back(std::move(rootCert)); - certChain.resize(temp.size()); - for(int i = 0; i < temp.size(); i++) { - certChain[i] = temp[i]; + cborData.clear(); + cborOutData.clear(); + errorCode = sendData(Instruction::INS_GET_CERT_CHAIN_CMD, cborData, cborOutData, true); + if(errorCode == ErrorCode::OK) { + //Skip last 2 bytes in cborData, it contains status. + std::tie(item, errorCode) = cborConverter_.decodeData(std::vector(cborOutData.begin(), cborOutData.end()-2), + true); + if (item != nullptr) { + std::vector chain; + if(!cborConverter_.getBinaryArray(item, 1, chain)) { + errorCode = ErrorCode::UNKNOWN_ERROR; + } else { + if(ErrorCode::OK == (errorCode = getCertificateChain(chain, temp))) { + certChain.resize(temp.size()); + for(int i = 0; i < temp.size(); i++) { + certChain[i] = temp[i]; + } + } + } } - } else { - LOG(ERROR) << "No root certificate found"; } } } @@ -962,7 +723,7 @@ Return JavacardKeymaster4Device::upgradeKey(const hidl_vec& keyBl errorCode = sendData(Instruction::INS_UPGRADE_KEY_CMD, cborData, cborOutData); - if((errorCode == ErrorCode::OK) && (cborOutData.size() > 2)) { + if(errorCode == ErrorCode::OK) { //Skip last 2 bytes in cborData, it contains status. std::tie(item, errorCode) = cborConverter_.decodeData(std::vector(cborOutData.begin(), cborOutData.end()-2), true); @@ -985,7 +746,7 @@ Return JavacardKeymaster4Device::deleteKey(const hidl_vec& k std::vector cborData = array.encode(); errorCode = sendData(Instruction::INS_DELETE_KEY_CMD, cborData, cborOutData); - if((errorCode == ErrorCode::OK) && (cborOutData.size() > 2)) { + if(errorCode == ErrorCode::OK) { //Skip last 2 bytes in cborData, it contains status. std::tie(item, errorCode) = cborConverter_.decodeData(std::vector(cborOutData.begin(), cborOutData.end()-2), true); @@ -1001,7 +762,7 @@ Return JavacardKeymaster4Device::deleteAllKeys() { errorCode = sendData(Instruction::INS_DELETE_ALL_KEYS_CMD, input, cborOutData); - if((errorCode == ErrorCode::OK) && (cborOutData.size() > 2)) { + if(errorCode == ErrorCode::OK) { //Skip last 2 bytes in cborData, it contains status. std::tie(item, errorCode) = cborConverter_.decodeData(std::vector(cborOutData.begin(), cborOutData.end()-2), true); @@ -1017,7 +778,7 @@ Return JavacardKeymaster4Device::destroyAttestationIds() { errorCode = sendData(Instruction::INS_DESTROY_ATT_IDS_CMD, input, cborOutData); - if((errorCode == ErrorCode::OK) && (cborOutData.size() > 2)) { + if(errorCode == ErrorCode::OK) { //Skip last 2 bytes in cborData, it contains status. std::tie(item, errorCode) = cborConverter_.decodeData(std::vector(cborOutData.begin(), cborOutData.end()-2), true); @@ -1090,7 +851,7 @@ Return JavacardKeymaster4Device::begin(KeyPurpose purpose, const hidl_vec< errorCode = ErrorCode::UNKNOWN_ERROR; if(getTag(keyCharacteristics.hardwareEnforced, Tag::ALGORITHM, param)) { errorCode = sendData(Instruction::INS_BEGIN_OPERATION_CMD, cborData, cborOutData); - if((errorCode == ErrorCode::OK) && (cborOutData.size() > 2)) { + if(errorCode == ErrorCode::OK) { //Skip last 2 bytes in cborData, it contains status. std::tie(item, errorCode) = cborConverter_.decodeData(std::vector(cborOutData.begin(), cborOutData.end()-2), true); @@ -1164,7 +925,7 @@ Return JavacardKeymaster4Device::update(uint64_t operationHandle, const hi errorCode = sendData(Instruction::INS_UPDATE_OPERATION_CMD, cborData, cborOutData); - if((errorCode == ErrorCode::OK) && (cborOutData.size() > 2)) { + if(errorCode == ErrorCode::OK) { //Skip last 2 bytes in cborData, it contains status. std::tie(item, errorCode) = cborConverter_.decodeData(std::vector(cborOutData.begin(), cborOutData.end()-2), true); @@ -1276,7 +1037,7 @@ Return JavacardKeymaster4Device::finish(uint64_t operationHandle, const hi errorCode = sendData(ins, cborData, cborOutData); - if((errorCode == ErrorCode::OK) && (cborOutData.size() > 2)) { + if(errorCode == ErrorCode::OK) { //Skip last 2 bytes in cborData, it contains status. std::tie(item, errorCode) = cborConverter_.decodeData(std::vector(cborOutData.begin(), cborOutData.end()-2), true); @@ -1326,7 +1087,7 @@ Return JavacardKeymaster4Device::abort(uint64_t operationHandle) { errorCode = sendData(Instruction::INS_ABORT_OPERATION_CMD, cborData, cborOutData); - if((errorCode == ErrorCode::OK) && (cborOutData.size() > 2)) { + if(errorCode == ErrorCode::OK) { //Skip last 2 bytes in cborData, it contains status. std::tie(item, errorCode) = cborConverter_.decodeData(std::vector(cborOutData.begin(), cborOutData.end()-2), true); @@ -1355,10 +1116,9 @@ Return<::android::hardware::keymaster::V4_1::ErrorCode> JavacardKeymaster4Device cborConverter_.addVerificationToken(array, verificationToken, asn1ParamsVerified); std::vector cborData = array.encode(); - /* TODO DeviceLocked command handled inside HAL */ ret = sendData(Instruction::INS_DEVICE_LOCKED_CMD, cborData, cborOutData); - if((ret == ErrorCode::OK) && (cborOutData.size() > 2)) { + if(ret == ErrorCode::OK) { //Skip last 2 bytes in cborData, it contains status. std::tie(item, errorCode) = cborConverter_.decodeData<::android::hardware::keymaster::V4_1::ErrorCode>(std::vector(cborOutData.begin(), cborOutData.end()-2), true); @@ -1375,7 +1135,7 @@ Return<::android::hardware::keymaster::V4_1::ErrorCode> JavacardKeymaster4Device ErrorCode ret = sendData(Instruction::INS_EARLY_BOOT_ENDED_CMD, cborInput, cborOutData); - if((ret == ErrorCode::OK) && (cborOutData.size() > 2)) { + if(ret == ErrorCode::OK) { //Skip last 2 bytes in cborData, it contains status. std::tie(item, errorCode) = cborConverter_.decodeData<::android::hardware::keymaster::V4_1::ErrorCode>(std::vector(cborOutData.begin(), cborOutData.end()-2), true); diff --git a/HAL/keymaster/4.1/Provision.cpp b/HAL/keymaster/4.1/Provision.cpp new file mode 100644 index 00000000..34b6420d --- /dev/null +++ b/HAL/keymaster/4.1/Provision.cpp @@ -0,0 +1,558 @@ +/* + ** + ** Copyright 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ROOT_EC_KEY "/data/data/ec_key.der" +#define INTERMEDIATE_EC_CERT "/data/data/ec_cert.der" +#define ROOT_EC_CERT "/data/data/ec_root_cert.der" +#define INS_BEGIN_KM_CMD 0x00 +#define APDU_CLS 0x80 +#define APDU_P1 0x40 +#define APDU_P2 0x00 +#define APDU_RESP_STATUS_OK 0x9000 + +namespace keymaster { +namespace V4_1 { +namespace javacard { + +constexpr uint8_t kFakeKeyAgreementKey[32] = {}; +enum class Instruction { + // Provisioning commands + INS_PROVISION_ATTESTATION_KEY_CMD = INS_BEGIN_KM_CMD+1, + INS_PROVISION_CERT_CHAIN_CMD = INS_BEGIN_KM_CMD+2, + INS_PROVISION_CERT_PARAMS_CMD = INS_BEGIN_KM_CMD+3, + INS_PROVISION_ATTEST_IDS_CMD = INS_BEGIN_KM_CMD+4, + INS_PROVISION_SHARED_SECRET_CMD = INS_BEGIN_KM_CMD+5, + INS_SET_BOOT_PARAMS_CMD = INS_BEGIN_KM_CMD+6, + INS_LOCK_PROVISIONING_CMD = INS_BEGIN_KM_CMD+7, + INS_GET_PROVISION_STATUS_CMD = INS_BEGIN_KM_CMD+8, +}; + +enum ProvisionStatus { + NOT_PROVISIONED = 0x00, + PROVISION_STATUS_ATTESTATION_KEY = 0x01, + PROVISION_STATUS_ATTESTATION_CERT_CHAIN = 0x02, + PROVISION_STATUS_ATTESTATION_CERT_PARAMS = 0x04, + PROVISION_STATUS_ATTEST_IDS = 0x08, + PROVISION_STATUS_SHARED_SECRET = 0x10, + PROVISION_STATUS_BOOT_PARAM = 0x20, + PROVISION_STATUS_PROVISIONING_LOCKED = 0x40, +}; + +// Static function declarations. +static bool readDataFromFile(const char *filename, std::vector& data); +static ErrorCode constructApduMessage(Instruction& ins, std::vector& inputData, std::vector& apduOut, bool +extendedOutput=false); +static ErrorCode sendProvisionData(std::unique_ptr& transport, Instruction ins, std::vector& inData, std::vector& response, bool +extendedOutput = false); +static ErrorCode provisionAttestationKey(std::unique_ptr& transport); +static ErrorCode provisionAttestationCertificateChain(std::unique_ptr& transport); +static ErrorCode provisionAttestationCertificateParams(std::unique_ptr& transport); +static ErrorCode provisionAttestationIDs(std::unique_ptr& transport); +static ErrorCode provisionSharedSecret(std::unique_ptr& transport); +static ErrorCode getProvisionStatus(std::unique_ptr& transport, std::vector& +response); +static ErrorCode lockProvision(std::unique_ptr& transport); +static ErrorCode setBootParameters(std::unique_ptr& transport); +static uint16_t getStatus(std::vector& inputData); +static bool isSEProvisioned(uint64_t status); + +static inline X509* parseDerCertificate(const char* filename) { + X509 *x509 = NULL; + std::vector certData; + + /* Read the Root certificate */ + if(!readDataFromFile(filename, certData)) { + LOG(ERROR) << " Failed to read the Root certificate"; + return NULL; + } + /* Create BIO instance from certificate data */ + BIO *bio = BIO_new_mem_buf(certData.data(), certData.size()); + if(bio == NULL) { + LOG(ERROR) << " Failed to create BIO from buffer."; + return NULL; + } + /* Create X509 instance from BIO */ + x509 = d2i_X509_bio(bio, NULL); + if(x509 == NULL) { + LOG(ERROR) << " Failed to get X509 instance from BIO."; + return NULL; + } + BIO_free(bio); + return x509; +} + +static inline void getDerSubjectName(X509* x509, std::vector& subject) { + uint8_t *subjectDer = NULL; + X509_NAME* asn1Subject = X509_get_subject_name(x509); + if(asn1Subject == NULL) { + LOG(ERROR) << " Failed to read the subject."; + return; + } + /* Convert X509_NAME to der encoded subject */ + int len = i2d_X509_NAME(asn1Subject, &subjectDer); + if (len < 0) { + LOG(ERROR) << " Failed to get readable name from X509_NAME."; + return; + } + subject.insert(subject.begin(), subjectDer, subjectDer+len); +} + +static inline void getAuthorityKeyIdentifier(X509* x509, std::vector& authKeyId) { + long xlen; + int tag, xclass; + + int loc = X509_get_ext_by_NID(x509, NID_authority_key_identifier, -1); + X509_EXTENSION *ext = X509_get_ext(x509, loc); + if(ext == NULL) { + LOG(ERROR) << " Failed to read authority key identifier."; + return; + } + + ASN1_OCTET_STRING *asn1AuthKeyId = X509_EXTENSION_get_data(ext); + const uint8_t *strAuthKeyId = ASN1_STRING_get0_data(asn1AuthKeyId); + int strAuthKeyIdLen = ASN1_STRING_length(asn1AuthKeyId); + int ret = ASN1_get_object(&strAuthKeyId, &xlen, &tag, &xclass, strAuthKeyIdLen); + if (ret == 0x80 || strAuthKeyId == NULL) { + LOG(ERROR) << "Failed to get the auth key identifier from ASN1 sequence."; + return; + } + authKeyId.insert(authKeyId.begin(), strAuthKeyId, strAuthKeyId + xlen); +} + +static inline void getNotAfter(X509* x509, std::vector& notAfterDate) { + const ASN1_TIME* notAfter = X509_get0_notAfter(x509); + if(notAfter == NULL) { + LOG(ERROR) << " Failed to read expiry time."; + return; + } + int strNotAfterLen = ASN1_STRING_length(notAfter); + const uint8_t *strNotAfter = ASN1_STRING_get0_data(notAfter); + if(strNotAfter == NULL) { + LOG(ERROR) << " Failed to read expiry time from ASN1 string."; + return; + } + notAfterDate.insert(notAfterDate.begin(), strNotAfter, strNotAfter + strNotAfterLen); +} + +static uint16_t getStatus(std::vector& inputData) { + //Last two bytes are the status SW0SW1 + return (inputData.at(inputData.size()-2) << 8) | (inputData.at(inputData.size()-1)); +} + +static bool readDataFromFile(const char *filename, std::vector& data) { + FILE *fp; + bool ret = true; + fp = fopen(filename, "rb"); + if(fp == NULL) { + LOG(ERROR) << "Failed to open file: " << filename; + return false; + } + fseek(fp, 0L, SEEK_END); + long int filesize = ftell(fp); + rewind(fp); + std::unique_ptr buf(new uint8_t[filesize]); + if( 0 == fread(buf.get(), filesize, 1, fp)) { + LOG(ERROR) << "No Content in the file: " << filename; + ret = false; + } + if(true == ret) { + //data.insert(data.begin(), buf.get(), buf.get() + filesize); + data.insert(data.end(), buf.get(), buf.get() + filesize); + } + fclose(fp); + return ret; +} + +static ErrorCode constructApduMessage(Instruction& ins, std::vector& inputData, std::vector& apduOut, bool +extendedOutput) { + apduOut.push_back(static_cast(APDU_CLS)); //CLS + apduOut.push_back(static_cast(ins)); //INS + apduOut.push_back(static_cast(APDU_P1)); //P1 + apduOut.push_back(static_cast(APDU_P2)); //P2 + + if(UCHAR_MAX < inputData.size() && USHRT_MAX >= inputData.size()) { + //Extended length 3 bytes, starts with 0x00 + apduOut.push_back(static_cast(0x00)); + apduOut.push_back(static_cast(inputData.size() >> 8)); + apduOut.push_back(static_cast(inputData.size() & 0xFF)); + //Data + apduOut.insert(apduOut.end(), inputData.begin(), inputData.end()); + //Expected length of output + apduOut.push_back(static_cast(0x00)); + apduOut.push_back(static_cast(0x00)); + apduOut.push_back(static_cast(0x00));//Accepting complete length of output at a time + } else if(0 <= inputData.size() && UCHAR_MAX >= inputData.size()) { + //Short length + apduOut.push_back(static_cast(inputData.size())); + //Data + if(inputData.size() > 0) + apduOut.insert(apduOut.end(), inputData.begin(), inputData.end()); + //Expected length of output + apduOut.push_back(static_cast(0x00));//Accepting complete length of output at a time + if(extendedOutput) + apduOut.push_back(static_cast(0x00)); + + } else { + return (ErrorCode::INSUFFICIENT_BUFFER_SPACE); + } + + return (ErrorCode::OK);//success +} + + + +static ErrorCode sendProvisionData(std::unique_ptr& transport, Instruction ins, std::vector& inData, std::vector& response, bool +extendedOutput) { + ErrorCode ret = ErrorCode::OK; + std::vector apdu; + CborConverter cborConverter; + std::unique_ptr item; + ret = constructApduMessage(ins, inData, apdu, extendedOutput); + if(ret != ErrorCode::OK) return ret; + + if(!transport->sendData(apdu.data(), apdu.size(), response)) { + return (ErrorCode::SECURE_HW_COMMUNICATION_FAILED); + } + + if((response.size() < 2) || (getStatus(response) != APDU_RESP_STATUS_OK)) { + return (ErrorCode::UNKNOWN_ERROR); + } + + if((response.size() > 2)) { + //Skip last 2 bytes in cborData, it contains status. + std::tie(item, ret) = cborConverter.decodeData(std::vector(response.begin(), response.end()-2), + true); + } else { + ret = ErrorCode::UNKNOWN_ERROR; + } + + return ret; +} + +static ErrorCode provisionAttestationKey(std::unique_ptr& transport) { + ErrorCode errorCode = ErrorCode::OK; + CborConverter cborConverter; + cppbor::Array array; + cppbor::Array subArray; + std::vector data; + std::vector privKey; + std::vector pubKey; + Instruction ins = Instruction::INS_PROVISION_ATTESTATION_KEY_CMD; + EcCurve curve; + std::vector response; + + AuthorizationSet authSetKeyParams(AuthorizationSetBuilder() + .Authorization(TAG_ALGORITHM, KM_ALGORITHM_EC) + .Authorization(TAG_DIGEST, KM_DIGEST_SHA_2_256) + .Authorization(TAG_EC_CURVE, KM_EC_CURVE_P_256) + .Authorization(TAG_PURPOSE, static_cast(0x7F))); /* The value 0x7F is not present in types.hal */ + // Read the ECKey from the file. + hidl_vec keyParams = kmParamSet2Hidl(authSetKeyParams); + + if(!readDataFromFile(ROOT_EC_KEY, data)) { + LOG(ERROR) << " Failed to read the Root rsa key"; + return ErrorCode::UNKNOWN_ERROR; + } + if(ErrorCode::OK != (errorCode = ecRawKeyFromPKCS8(data, privKey, pubKey, curve))) { + return errorCode; + } + subArray.add(privKey); + subArray.add(pubKey); + std::vector encodedArray = subArray.encode(); + cppbor::Bstr bstr(encodedArray.begin(), encodedArray.end()); + + //Encode data. + cborConverter.addKeyparameters(array, keyParams); + array.add(static_cast(KeyFormat::RAW)); + array.add(bstr); + + std::vector cborData = array.encode(); + + if(ErrorCode::OK != (errorCode = sendProvisionData(transport, ins, cborData, response))) { + return errorCode; + } + return errorCode; +} + +static ErrorCode provisionAttestationCertificateChain(std::unique_ptr& transport) { + ErrorCode errorCode = ErrorCode::OK; + cppbor::Array array; + Instruction ins = Instruction::INS_PROVISION_CERT_CHAIN_CMD; + std::vector response; + + std::vector certData; + /* Read the Root certificate */ + if(!readDataFromFile(INTERMEDIATE_EC_CERT, certData)) { + LOG(ERROR) << " Failed to read the Root certificate"; + return (ErrorCode::UNKNOWN_ERROR); + } + if(!readDataFromFile(ROOT_EC_CERT, certData)) { + LOG(ERROR) << " Failed to read the Root certificate"; + return (ErrorCode::UNKNOWN_ERROR); + } + cppbor::Bstr certChain(certData.begin(), certData.end()); + std::vector cborData = certChain.encode(); + + if(ErrorCode::OK != (errorCode = sendProvisionData(transport, ins, cborData, response))) { + return errorCode; + } + return errorCode; +} + +static ErrorCode provisionAttestationCertificateParams(std::unique_ptr& transport) { + ErrorCode errorCode = ErrorCode::OK; + cppbor::Array array; + Instruction ins = Instruction::INS_PROVISION_CERT_PARAMS_CMD; + std::vector response; + X509 *x509 = NULL; + std::vector subject; + std::vector authorityKeyIdentifier; + std::vector notAfter; + + /* Subject, AuthorityKeyIdentifier and Expirty time of the root certificate are required by javacard. */ + /* Get X509 certificate instance for the root certificate.*/ + if(NULL == (x509 = parseDerCertificate(INTERMEDIATE_EC_CERT))) { + return errorCode; + } + + /* Get subject in DER */ + getDerSubjectName(x509, subject); + /* Get AuthorityKeyIdentifier */ + getAuthorityKeyIdentifier(x509, authorityKeyIdentifier); + /* Get Expirty Time */ + getNotAfter(x509, notAfter); + /*Free X509 */ + X509_free(x509); + + array = cppbor::Array(); + array.add(subject); + array.add(notAfter); + array.add(authorityKeyIdentifier); + std::vector cborData = array.encode(); + + if(ErrorCode::OK != (errorCode = sendProvisionData(transport, ins, cborData, response))) { + return errorCode; + } + return errorCode; +} + + +static ErrorCode provisionAttestationIDs(std::unique_ptr& transport) { + ErrorCode errorCode = ErrorCode::OK; + CborConverter cborConverter; + cppbor::Array array; + Instruction ins = Instruction::INS_PROVISION_ATTEST_IDS_CMD; + std::vector response; + + std::string brand("Google"); + std::string device("Pixel 3A"); + std::string product("Pixel"); + std::string serial("UGYJFDjFeRuBEH"); + std::string imei("987080543071019"); + std::string meid("27863510227963"); + std::string manufacturer("Foxconn"); + std::string model("HD1121"); + + AuthorizationSet authSetAttestParams(AuthorizationSetBuilder() + .Authorization(TAG_ATTESTATION_ID_BRAND, brand.data(), brand.size()) + .Authorization(TAG_ATTESTATION_ID_DEVICE, device.data(), device.size()) + .Authorization(TAG_ATTESTATION_ID_PRODUCT, product.data(), product.size()) + .Authorization(TAG_ATTESTATION_ID_SERIAL, serial.data(), serial.size()) + .Authorization(TAG_ATTESTATION_ID_IMEI, imei.data(), imei.size()) + .Authorization(TAG_ATTESTATION_ID_MEID, meid.data(), meid.size()) + .Authorization(TAG_ATTESTATION_ID_MANUFACTURER, manufacturer.data(), manufacturer.size()) + .Authorization(TAG_ATTESTATION_ID_MODEL, model.data(), model.size())); + + hidl_vec attestParams = kmParamSet2Hidl(authSetAttestParams); + + array = cppbor::Array(); + cborConverter.addKeyparameters(array, attestParams); + std::vector cborData = array.encode(); + + if(ErrorCode::OK != (errorCode = sendProvisionData(transport, ins, cborData, response))) { + return errorCode; + } + return errorCode; +} + +static ErrorCode provisionSharedSecret(std::unique_ptr& transport) { + ErrorCode errorCode = ErrorCode::OK; + cppbor::Array array; + Instruction ins = Instruction::INS_PROVISION_SHARED_SECRET_CMD; + std::vector response; + std::vector masterKey(kFakeKeyAgreementKey, kFakeKeyAgreementKey + + sizeof(kFakeKeyAgreementKey)/sizeof(kFakeKeyAgreementKey[0])); + + array = cppbor::Array(); + array.add(masterKey); + std::vector cborData = array.encode(); + + if(ErrorCode::OK != (errorCode = sendProvisionData(transport, ins, cborData, response))) { + return errorCode; + } + return errorCode; +} + +static ErrorCode getProvisionStatus(std::unique_ptr& transport, std::vector& +response) { + ErrorCode errorCode = ErrorCode::OK; + Instruction ins = Instruction::INS_GET_PROVISION_STATUS_CMD; + std::vector cborData; + + if(ErrorCode::OK != (errorCode = sendProvisionData(transport, ins, cborData, response))) { + return errorCode; + } + return errorCode; + +} + +static ErrorCode lockProvision(std::unique_ptr& transport) { + ErrorCode errorCode = ErrorCode::OK; + Instruction ins = Instruction::INS_LOCK_PROVISIONING_CMD; + std::vector cborData; + std::vector response; + + if(ErrorCode::OK != (errorCode = sendProvisionData(transport, ins, cborData, response))) { + return errorCode; + } + return errorCode; +} + +static ErrorCode setBootParameters(std::unique_ptr& transport) { + ErrorCode errorCode = ErrorCode::OK; + std::vector verifiedBootKey(32, 0); + std::vector verifiedBootKeyHash(32, 0); + uint32_t vendorPatchLevel = 0; + uint32_t bootPatchLevel = 0; + cppbor::Array array; + std::vector apdu; + std::vector response; + Instruction ins = Instruction::INS_SET_BOOT_PARAMS_CMD; + keymaster_verified_boot_t kmVerifiedBoot = KM_VERIFIED_BOOT_UNVERIFIED; + + array.add(GetOsVersion()). + add(GetOsPatchlevel()). + add(vendorPatchLevel). + add(bootPatchLevel). + /* Verified Boot Key */ + add(verifiedBootKey). + /* Verified Boot Hash */ + add(verifiedBootKeyHash). + /* boot state */ + add(static_cast(kmVerifiedBoot)). + /* device locked */ + add(0); + + std::vector cborData = array.encode(); + + if(ErrorCode::OK != (errorCode = sendProvisionData(transport, ins, cborData, response))) { + return errorCode; + } + return errorCode; +} + +static bool isSEProvisioned(uint64_t status) { + bool ret = true; + + if(status != (ProvisionStatus::PROVISION_STATUS_ATTESTATION_KEY | ProvisionStatus::PROVISION_STATUS_ATTESTATION_CERT_CHAIN | + ProvisionStatus::PROVISION_STATUS_ATTESTATION_CERT_PARAMS | ProvisionStatus::PROVISION_STATUS_ATTEST_IDS | + ProvisionStatus::PROVISION_STATUS_SHARED_SECRET | ProvisionStatus::PROVISION_STATUS_BOOT_PARAM + |ProvisionStatus::PROVISION_STATUS_PROVISIONING_LOCKED)) { + ret = false; + } + return ret; +} + +ErrorCode provision(std::unique_ptr& transport) { + std::vector response; + std::unique_ptr item; + CborConverter cborConverter; + ErrorCode errorCode = ErrorCode::OK; + + //Get Provision status. + if(ErrorCode::OK != (errorCode = getProvisionStatus(transport, response))) { + return errorCode; + } + + //Check if SE is provisioned. + std::tie(item, errorCode) = cborConverter.decodeData(std::vector(response.begin(), response.end()-2), + true); + if(item != NULL) { + uint64_t status; + + if(!cborConverter.getUint64(item, 1, status)) + return ErrorCode::UNKNOWN_ERROR; + + if(isSEProvisioned(status)) { + return ErrorCode::OK; //SE is Provisioned. + } + + } else { + return ErrorCode::UNKNOWN_ERROR; + } + + //SE not provisioned so Provision the SE. + + //Provision Attestation Key. + if(ErrorCode::OK != (errorCode = provisionAttestationKey(transport))) { + return errorCode; + } + //Provision Attestation certificate chain. + if(ErrorCode::OK != (errorCode = provisionAttestationCertificateChain(transport))) { + return errorCode; + } + //Provision certificate parameters. + if(ErrorCode::OK != (errorCode = provisionAttestationCertificateParams(transport))) { + return errorCode; + } + //Provision Attestation IDs. + if(ErrorCode::OK != (errorCode = provisionAttestationIDs(transport))) { + return errorCode; + } + //Provision Shared secret. + if(ErrorCode::OK != (errorCode = provisionSharedSecret(transport))) { + return errorCode; + } + //Set Boot parameters. + if(ErrorCode::OK != (errorCode = setBootParameters(transport))) { + return errorCode; + } + //Lock the provisioning. + if(ErrorCode::OK != (errorCode = lockProvision(transport))) { + return errorCode; + } + //return OK + return errorCode; +} + +} // namespace javacard +} // namespace V4_1 +} // namespace keymaster diff --git a/HAL/keymaster/Android.bp b/HAL/keymaster/Android.bp index ef67cb6e..8b34023d 100644 --- a/HAL/keymaster/Android.bp +++ b/HAL/keymaster/Android.bp @@ -27,6 +27,7 @@ cc_binary { "4.1/JavacardSoftKeymasterContext.cpp", "4.1/JavacardOperationContext.cpp", "4.1/CommonUtils.cpp", + "4.1/Provision.cpp", ], local_include_dirs: [ "include", @@ -42,7 +43,7 @@ cc_binary { "libsoftkeymasterdevice", "libkeymaster_messages", "libkeymaster_portable", - "libcppbor_external", + "libcppbor", "android.hardware.keymaster@4.1", "android.hardware.keymaster@4.0", "libjc_transport", @@ -73,7 +74,7 @@ cc_library { "libsoftkeymasterdevice", "libkeymaster_messages", "libkeymaster_portable", - "libcppbor_external", + "libcppbor", "android.hardware.keymaster@4.1", "android.hardware.keymaster@4.0", "libjc_transport", diff --git a/HAL/keymaster/include/CommonUtils.h b/HAL/keymaster/include/CommonUtils.h index e418fc0c..f08a60c3 100644 --- a/HAL/keymaster/include/CommonUtils.h +++ b/HAL/keymaster/include/CommonUtils.h @@ -86,6 +86,8 @@ pubModulus); ErrorCode ecRawKeyFromPKCS8(const std::vector& pkcs8Blob, std::vector& secret, std::vector& publicKey, EcCurve& eccurve); +ErrorCode getCertificateChain(std::vector& chainBuffer, std::vector>& certChain); + class KmParamSet : public keymaster_key_param_set_t { public: explicit KmParamSet(const hidl_vec& keyParams) diff --git a/HAL/keymaster/include/JavacardKeymaster4Device.h b/HAL/keymaster/include/JavacardKeymaster4Device.h index c7c06ed5..fcfdef0f 100644 --- a/HAL/keymaster/include/JavacardKeymaster4Device.h +++ b/HAL/keymaster/include/JavacardKeymaster4Device.h @@ -85,16 +85,6 @@ class JavacardKeymaster4Device : public IKeymasterDevice { Return deviceLocked(bool passwordOnly, const VerificationToken& verificationToken) override; Return earlyBootEnded() override; - //Set Boot Params - /* This method should be called at the time when HAL is initialized for the first time */ - static ErrorCode setBootParams(uint32_t osVersion, uint32_t osPatchLevel, const std::vector& verifiedBootKey, -std::vector& verifiedBootKeyHash, keymaster_verified_boot_t kmVerifiedBoot, bool deviceLocked); - - //Provision Method - /* Reference for vendor to provision the javacard. This should happen only once at the time of production.*/ - static ErrorCode provision(const hidl_vec& keyParams, KeyFormat keyformat, const hidl_vec& -keyData); - protected: CborConverter cborConverter_; diff --git a/HAL/keymaster/include/Provision.h b/HAL/keymaster/include/Provision.h new file mode 100644 index 00000000..a58c17c6 --- /dev/null +++ b/HAL/keymaster/include/Provision.h @@ -0,0 +1,36 @@ +/* + ** + ** Copyright 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. + */ + + +#ifndef KEYMASTER_V4_1_JAVACARD_PROVISION_H_ +#define KEYMASTER_V4_1_JAVACARD_PROVISION_H_ + +#include "TransportFactory.h" + +namespace keymaster { +namespace V4_1 { +namespace javacard { + +/** + * Provisions the SE. + */ +ErrorCode provision(std::unique_ptr& transport); + +} // namespace javacard +} // namespace V4_1 +} // namespace keymaster +#endif //KEYMASTER_V4_1_JAVACARD_PROVISION_H_ diff --git a/README.md b/README.md index dd532000..670e94a1 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ # JavaCardKeymaster JavaCard implementation of the [Android Keymaster 4.1 HAL](https://android.googlesource.com/platform/hardware/interfaces/+/master/keymaster/4.1/IKeymasterDevice.hal) (most of the specification is in the [Android Keymaster 4.0 HAL](https://android.googlesource.com/platform/hardware/interfaces/+/master/keymaster/4.0/IKeymasterDevice.hal)), intended for creation of StrongBox Keymaster instances to support the [Android Hardware-backed Keystore](https://source.android.com/security/keystore). -Here is the [JavaCard Applet design doc](https://docs.google.com/document/d/1mIv895E3imKfzxS9weWQxP3jZIxS-7OPmh4fxdjOPm0/edit#heading=h.xgjl2srtytjt) and the [HAL design doc](https://docs.google.com/document/d/1ExaoEOU3mkjhoMIcAhD_Z6kPp5yyfjpnfBpUQHiSomw/edit#heading=h.gjdgxs) (the content will move here when it stablizes, for now these are a limited-access links). +Here is the [JavaCard Applet design doc](https://docs.google.com/document/d/1bTAmhDqCNq1HYzChNDv8kLJEi64cwTIZ2PfdMMz3o8U/edit#heading=h.gjdgxs) and the [HAL design doc](https://docs.google.com/document/d/1-1MLJ781wAPJ2YxCdCtHMepld8F8KVAxpPtCw9J3b3o/edit#heading=h.gjdgxs) (the content will move here when it stablizes, for now these are a limited-access links).