diff --git a/Applet/AndroidSEProvider/AndroidSE_3_0_5.opt b/Applet/AndroidSEProvider/AndroidSE_3_0_5.opt deleted file mode 100644 index ba998254..00000000 --- a/Applet/AndroidSEProvider/AndroidSE_3_0_5.opt +++ /dev/null @@ -1,5 +0,0 @@ --out EXP JCA CAP --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 deleted file mode 100644 index 3de07eb5..00000000 --- a/Applet/AndroidSEProvider/AndroidSE_3_1_0.opt +++ /dev/null @@ -1,5 +0,0 @@ --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 deleted file mode 100644 index 931133af..00000000 Binary files a/Applet/AndroidSEProvider/api_export_files_3.0.5/java/io/javacard/io.exp and /dev/null 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 deleted file mode 100644 index f3498186..00000000 Binary files a/Applet/AndroidSEProvider/api_export_files_3.0.5/java/lang/javacard/lang.exp and /dev/null 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 deleted file mode 100644 index 209cdf37..00000000 Binary files a/Applet/AndroidSEProvider/api_export_files_3.0.5/java/rmi/javacard/rmi.exp and /dev/null differ diff --git a/Applet/AndroidSEProvider/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 deleted file mode 100644 index fd14eac7..00000000 Binary files a/Applet/AndroidSEProvider/api_export_files_3.0.5/javacard/framework/javacard/framework.exp and /dev/null differ diff --git a/Applet/AndroidSEProvider/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 deleted file mode 100644 index 8fbef2f6..00000000 Binary files a/Applet/AndroidSEProvider/api_export_files_3.0.5/javacard/framework/service/javacard/service.exp and /dev/null differ diff --git a/Applet/AndroidSEProvider/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 deleted file mode 100644 index 39e57627..00000000 Binary files a/Applet/AndroidSEProvider/api_export_files_3.0.5/javacard/security/javacard/security.exp and /dev/null 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 deleted file mode 100644 index c9183d4f..00000000 Binary files a/Applet/AndroidSEProvider/api_export_files_3.0.5/javacardx/apdu/javacard/apdu.exp and /dev/null differ diff --git a/Applet/AndroidSEProvider/api_export_files_3.0.5/javacardx/apdu/util/javacard/util.exp b/Applet/AndroidSEProvider/api_export_files_3.0.5/javacardx/apdu/util/javacard/util.exp deleted file mode 100644 index 6a1e9e1e..00000000 Binary files a/Applet/AndroidSEProvider/api_export_files_3.0.5/javacardx/apdu/util/javacard/util.exp and /dev/null differ diff --git a/Applet/AndroidSEProvider/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 deleted file mode 100644 index fb46fa30..00000000 Binary files a/Applet/AndroidSEProvider/api_export_files_3.0.5/javacardx/biometry/javacard/biometry.exp and /dev/null differ diff --git a/Applet/AndroidSEProvider/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 deleted file mode 100644 index 88527985..00000000 Binary files a/Applet/AndroidSEProvider/api_export_files_3.0.5/javacardx/biometry1toN/javacard/biometry1toN.exp and /dev/null differ diff --git a/Applet/AndroidSEProvider/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 deleted file mode 100644 index e1ff132d..00000000 Binary files a/Applet/AndroidSEProvider/api_export_files_3.0.5/javacardx/crypto/javacard/crypto.exp and /dev/null differ diff --git a/Applet/AndroidSEProvider/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 deleted file mode 100644 index 4af91e53..00000000 Binary files a/Applet/AndroidSEProvider/api_export_files_3.0.5/javacardx/external/javacard/external.exp and /dev/null differ diff --git a/Applet/AndroidSEProvider/api_export_files_3.0.5/javacardx/framework/math/javacard/math.exp b/Applet/AndroidSEProvider/api_export_files_3.0.5/javacardx/framework/math/javacard/math.exp deleted file mode 100644 index 89a1ac6c..00000000 Binary files a/Applet/AndroidSEProvider/api_export_files_3.0.5/javacardx/framework/math/javacard/math.exp and /dev/null differ diff --git a/Applet/AndroidSEProvider/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 deleted file mode 100644 index 7a3b73c3..00000000 Binary files a/Applet/AndroidSEProvider/api_export_files_3.0.5/javacardx/framework/string/javacard/string.exp and /dev/null differ diff --git a/Applet/AndroidSEProvider/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 deleted file mode 100644 index 58495fb0..00000000 Binary files a/Applet/AndroidSEProvider/api_export_files_3.0.5/javacardx/framework/tlv/javacard/tlv.exp and /dev/null 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 deleted file mode 100644 index 881a9612..00000000 Binary files a/Applet/AndroidSEProvider/api_export_files_3.0.5/javacardx/framework/util/intx/javacard/intx.exp and /dev/null differ diff --git a/Applet/AndroidSEProvider/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 deleted file mode 100644 index a1ce7e96..00000000 Binary files a/Applet/AndroidSEProvider/api_export_files_3.0.5/javacardx/framework/util/javacard/util.exp and /dev/null differ diff --git a/Applet/AndroidSEProvider/api_export_files_3.0.5/javacardx/security/javacard/security.exp b/Applet/AndroidSEProvider/api_export_files_3.0.5/javacardx/security/javacard/security.exp deleted file mode 100644 index 5153a68d..00000000 Binary files a/Applet/AndroidSEProvider/api_export_files_3.0.5/javacardx/security/javacard/security.exp and /dev/null 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 deleted file mode 100644 index 110117b2..00000000 Binary files a/Applet/AndroidSEProvider/api_export_files_3.0.5/org/globalplatform/upgrade/javacard/upgrade.exp and /dev/null 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 deleted file mode 100644 index 9bb1e3b8..00000000 --- a/Applet/AndroidSEProvider/api_export_files_3.0.5/org/globalplatform/upgrade/javacard/upgrade.jca +++ /dev/null @@ -1,245 +0,0 @@ -// 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/AndroidSEProvider/api_export_files_3.1.0/java/io/javacard/io.exp b/Applet/AndroidSEProvider/api_export_files_3.1.0/java/io/javacard/io.exp deleted file mode 100644 index 36b9d18b..00000000 Binary files a/Applet/AndroidSEProvider/api_export_files_3.1.0/java/io/javacard/io.exp and /dev/null differ diff --git a/Applet/AndroidSEProvider/api_export_files_3.1.0/java/lang/javacard/lang.exp b/Applet/AndroidSEProvider/api_export_files_3.1.0/java/lang/javacard/lang.exp deleted file mode 100644 index 272eebba..00000000 Binary files a/Applet/AndroidSEProvider/api_export_files_3.1.0/java/lang/javacard/lang.exp and /dev/null differ diff --git a/Applet/AndroidSEProvider/api_export_files_3.1.0/java/rmi/javacard/rmi.exp b/Applet/AndroidSEProvider/api_export_files_3.1.0/java/rmi/javacard/rmi.exp deleted file mode 100644 index 8c695237..00000000 Binary files a/Applet/AndroidSEProvider/api_export_files_3.1.0/java/rmi/javacard/rmi.exp and /dev/null differ diff --git a/Applet/AndroidSEProvider/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 deleted file mode 100644 index e360b447..00000000 Binary files a/Applet/AndroidSEProvider/api_export_files_3.1.0/javacard/framework/javacard/framework.exp and /dev/null differ diff --git a/Applet/AndroidSEProvider/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 deleted file mode 100644 index 69fd1b28..00000000 Binary files a/Applet/AndroidSEProvider/api_export_files_3.1.0/javacard/framework/service/javacard/service.exp and /dev/null differ diff --git a/Applet/AndroidSEProvider/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 deleted file mode 100644 index 11514947..00000000 Binary files a/Applet/AndroidSEProvider/api_export_files_3.1.0/javacard/security/javacard/security.exp and /dev/null differ diff --git a/Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/apdu/javacard/apdu.exp b/Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/apdu/javacard/apdu.exp deleted file mode 100644 index ce4ac0c9..00000000 Binary files a/Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/apdu/javacard/apdu.exp and /dev/null differ diff --git a/Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/apdu/util/javacard/util.exp b/Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/apdu/util/javacard/util.exp deleted file mode 100644 index cf2fc718..00000000 Binary files a/Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/apdu/util/javacard/util.exp and /dev/null differ diff --git a/Applet/AndroidSEProvider/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 deleted file mode 100644 index 2bdf2c40..00000000 Binary files a/Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/biometry/javacard/biometry.exp and /dev/null differ diff --git a/Applet/AndroidSEProvider/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 deleted file mode 100644 index 3b93e625..00000000 Binary files a/Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/biometry1toN/javacard/biometry1toN.exp and /dev/null differ diff --git a/Applet/AndroidSEProvider/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 deleted file mode 100644 index 679c29d6..00000000 Binary files a/Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/crypto/javacard/crypto.exp and /dev/null differ diff --git a/Applet/AndroidSEProvider/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 deleted file mode 100644 index aca62699..00000000 Binary files a/Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/external/javacard/external.exp and /dev/null differ diff --git a/Applet/AndroidSEProvider/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 deleted file mode 100644 index bf463690..00000000 Binary files a/Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/framework/event/javacard/event.exp and /dev/null differ diff --git a/Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/framework/math/javacard/math.exp b/Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/framework/math/javacard/math.exp deleted file mode 100644 index af2f608c..00000000 Binary files a/Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/framework/math/javacard/math.exp and /dev/null differ diff --git a/Applet/AndroidSEProvider/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 deleted file mode 100644 index 667743fd..00000000 Binary files a/Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/framework/nio/javacard/nio.exp and /dev/null differ diff --git a/Applet/AndroidSEProvider/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 deleted file mode 100644 index 47df6293..00000000 Binary files a/Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/framework/string/javacard/string.exp and /dev/null differ diff --git a/Applet/AndroidSEProvider/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 deleted file mode 100644 index 8621ad8f..00000000 Binary files a/Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/framework/time/javacard/time.exp and /dev/null differ diff --git a/Applet/AndroidSEProvider/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 deleted file mode 100644 index 142d9923..00000000 Binary files a/Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/framework/tlv/javacard/tlv.exp and /dev/null differ diff --git a/Applet/AndroidSEProvider/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 deleted file mode 100644 index 39008657..00000000 Binary files a/Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/framework/util/intx/javacard/intx.exp and /dev/null differ diff --git a/Applet/AndroidSEProvider/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 deleted file mode 100644 index 35523adf..00000000 Binary files a/Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/framework/util/javacard/util.exp and /dev/null differ diff --git a/Applet/AndroidSEProvider/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 deleted file mode 100644 index 4312b4d4..00000000 Binary files a/Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/security/cert/javacard/cert.exp and /dev/null differ diff --git a/Applet/AndroidSEProvider/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 deleted file mode 100644 index 5c740cff..00000000 Binary files a/Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/security/derivation/javacard/derivation.exp and /dev/null differ diff --git a/Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/security/javacard/security.exp b/Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/security/javacard/security.exp deleted file mode 100644 index 30b3ed6e..00000000 Binary files a/Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/security/javacard/security.exp and /dev/null differ diff --git a/Applet/AndroidSEProvider/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 deleted file mode 100644 index 7f443343..00000000 Binary files a/Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/security/util/javacard/util.exp and /dev/null differ diff --git a/Applet/AndroidSEProvider/build.xml b/Applet/AndroidSEProvider/build.xml deleted file mode 100644 index 137c169a..00000000 --- a/Applet/AndroidSEProvider/build.xml +++ /dev/null @@ -1,81 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java index 7c6f31fe..642775f1 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java @@ -1,87 +1,435 @@ -/* - * 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.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; - } -} - +/* + * 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.OnUpgradeListener; +import org.globalplatform.upgrade.UpgradeManager; + +import com.android.javacard.seprovider.KMAndroidSEProvider; +import com.android.javacard.seprovider.KMDeviceUniqueKey; +import com.android.javacard.seprovider.KMError; +import com.android.javacard.seprovider.KMException; +import com.android.javacard.seprovider.KMType; + +import javacard.framework.APDU; +import javacard.framework.ISO7816; +import javacard.framework.Util; + +public class KMAndroidSEApplet extends KMKeymasterApplet implements OnUpgradeListener { + + private static final byte KM_BEGIN_STATE = 0x00; + private static final byte ILLEGAL_STATE = KM_BEGIN_STATE + 1; + private static final short POWER_RESET_MASK_FLAG = (short) 0x4000; + + // Provider specific Commands + private static final byte INS_KEYMINT_PROVIDER_APDU_START = 0x00; + private static final byte INS_PROVISION_ATTEST_IDS_CMD = INS_KEYMINT_PROVIDER_APDU_START + 1; + private static final byte INS_PROVISION_PRESHARED_SECRET_CMD = + INS_KEYMINT_PROVIDER_APDU_START + 2; + private static final byte INS_LOCK_PROVISIONING_CMD = INS_KEYMINT_PROVIDER_APDU_START + 3; + private static final byte INS_GET_PROVISION_STATUS_CMD = INS_KEYMINT_PROVIDER_APDU_START + 4; + private static final byte INS_SET_BOOT_PARAMS_CMD = INS_KEYMINT_PROVIDER_APDU_START + 5; + private static final byte INS_PROVISION_DEVICE_UNIQUE_KEY_CMD = + INS_KEYMINT_PROVIDER_APDU_START + 6; + private static final byte INS_PROVISION_ADDITIONAL_CERT_CHAIN_CMD = + INS_KEYMINT_PROVIDER_APDU_START + 7; + + private static final byte INS_KEYMINT_PROVIDER_APDU_END = 0x1F; + public static final byte BOOT_KEY_MAX_SIZE = 32; + public static final byte BOOT_HASH_MAX_SIZE = 32; + + // 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_PRESHARED_SECRET = 0x10; + private static final byte PROVISION_STATUS_BOOT_PARAM = 0x20; + private static final byte PROVISION_STATUS_PROVISIONING_LOCKED = 0x40; + + public static final short SHARED_SECRET_KEY_SIZE = 32; + + private static byte keymasterState = ILLEGAL_STATE; + private static byte provisionStatus = NOT_PROVISIONED; + + 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 process(APDU apdu) { + try { + // If this is select applet apdu which is selecting this applet then return + if (apdu.isISOInterindustryCLA()) { + if (selectingApplet()) { + return; + } + } + short apduIns = validateApdu(apdu); + if (((KMAndroidSEProvider) seProvider).isPowerReset()) { + super.powerReset(); + } + + if (((KMAndroidSEProvider) seProvider).isProvisionLocked()) { + switch (apduIns) { + case INS_SET_BOOT_PARAMS_CMD: + processSetBootParamsCmd(apdu); + break; + default: + super.process(apdu); + break; + } + return; + } + + if (apduIns == KMType.INVALID_VALUE) { + return; + } + switch (apduIns) { + case INS_PROVISION_ATTEST_IDS_CMD: + processProvisionAttestIdsCmd(apdu); + provisionStatus |= PROVISION_STATUS_ATTEST_IDS; + sendError(apdu, KMError.OK); + break; + + case INS_PROVISION_PRESHARED_SECRET_CMD: + processProvisionPreSharedSecretCmd(apdu); + provisionStatus |= PROVISION_STATUS_PRESHARED_SECRET; + sendError(apdu, KMError.OK); + break; + + case INS_GET_PROVISION_STATUS_CMD: + processGetProvisionStatusCmd(apdu); + break; + + case INS_LOCK_PROVISIONING_CMD: + processLockProvisioningCmd(apdu); + break; + + case INS_SET_BOOT_PARAMS_CMD: + + processSetBootParamsCmd(apdu); + provisionStatus |= PROVISION_STATUS_BOOT_PARAM; + break; + + case INS_PROVISION_DEVICE_UNIQUE_KEY_CMD: + processProvisionDeviceUniqueKey(apdu); + break; + + case INS_PROVISION_ADDITIONAL_CERT_CHAIN_CMD: + processProvisionAdditionalCertChain(apdu); + break; + + default: + super.process(apdu); + break; + } + } finally { + repository.clean(); + } + } + + private static void processProvisionDeviceUniqueKey(APDU apdu) { + // Re-purpose the apdu buffer as scratch pad. + byte[] scratchPad = apdu.getBuffer(); + short arr = KMArray.instance((short) 1); + short coseKeyExp = KMCoseKey.exp(); + KMArray.cast(arr).add((short) 0, coseKeyExp); //[ CoseKey ] + arr = receiveIncoming(apdu, arr); + // Get cose key. + short coseKey = KMArray.cast(arr).get((short) 0); + short pubKeyLen = KMCoseKey.cast(coseKey).getEcdsa256PublicKey(scratchPad, (short) 0); + short privKeyLen = KMCoseKey.cast(coseKey).getPrivateKey(scratchPad, pubKeyLen); + //Store the Device unique Key. + seProvider.createDeviceUniqueKey(false, scratchPad, (short) 0, pubKeyLen, scratchPad, + pubKeyLen, privKeyLen); + short bcc = generateBcc(false, scratchPad); + short len = KMKeymasterApplet.encodeToApduBuffer(bcc, scratchPad, (short) 0, + MAX_COSE_BUF_SIZE); + ((KMAndroidSEProvider) seProvider).persistBootCertificateChain(scratchPad, (short) 0, len); + sendError(apdu, KMError.OK); + } + + private static void processProvisionAdditionalCertChain(APDU apdu) { + // Prepare the expression to decode + short headers = KMCoseHeaders.exp(); + short arrInst = KMArray.instance((short) 4); + KMArray.cast(arrInst).add((short) 0, KMByteBlob.exp()); + KMArray.cast(arrInst).add((short) 1, headers); + KMArray.cast(arrInst).add((short) 2, KMByteBlob.exp()); + KMArray.cast(arrInst).add((short) 3, KMByteBlob.exp()); + short coseSignArr = KMArray.exp(arrInst); + short map = KMMap.instance((short) 1); + KMMap.cast(map).add((short) 0, KMTextString.exp(), coseSignArr); + // receive incoming data and decode it. + byte[] srcBuffer = apdu.getBuffer(); + short recvLen = apdu.setIncomingAndReceive(); + short srcOffset = apdu.getOffsetCdata(); + short bufferLength = apdu.getIncomingLength(); + short bufferStartOffset = repository.allocReclaimableMemory(bufferLength); + short index = bufferStartOffset; + byte[] buffer = repository.getHeap(); + while (recvLen > 0 && ((short) (index - bufferStartOffset) < bufferLength)) { + Util.arrayCopyNonAtomic(srcBuffer, srcOffset, buffer, index, recvLen); + index += recvLen; + recvLen = apdu.receiveBytes(srcOffset); + } + // decode + map = decoder.decode(map, buffer, bufferStartOffset, bufferLength); + arrInst = KMMap.cast(map).getKeyValue((short) 0); + // Validate Additional certificate chain. + short leafCoseKey = + validateCertChain(false, KMCose.COSE_ALG_ES256, KMCose.COSE_ALG_ES256, arrInst, + srcBuffer, null); + // Compare the DK_Pub. + short pubKeyLen = KMCoseKey.cast(leafCoseKey).getEcdsa256PublicKey(srcBuffer, (short) 0); + KMDeviceUniqueKey uniqueKey = seProvider.getDeviceUniqueKey(false); + if (uniqueKey == null) { + KMException.throwIt(KMError.STATUS_FAILED); + } + short uniqueKeyLen = uniqueKey.getPublicKey(srcBuffer, pubKeyLen); + if ((pubKeyLen != uniqueKeyLen) || + (0 != Util.arrayCompare(srcBuffer, (short) 0, srcBuffer, pubKeyLen, pubKeyLen))) { + KMException.throwIt(KMError.STATUS_FAILED); + } + seProvider.persistAdditionalCertChain(buffer, bufferStartOffset, bufferLength); + //reclaim memory + repository.reclaimMemory(bufferLength); + sendError(apdu, KMError.OK); + } + + private void processProvisionAttestIdsCmd(APDU apdu) { + short keyparams = KMKeyParameters.exp(); + short cmd = KMArray.instance((short) 1); + KMArray.cast(cmd).add((short) 0, keyparams); + short args = receiveIncoming(apdu, cmd); + + short attData = KMArray.cast(args).get((short) 0); + // persist attestation Ids - if any is missing then exception occurs + setAttestationIds(attData); + } + + public void setAttestationIds(short attIdVals) { + KMKeyParameters instParam = KMKeyParameters.cast(attIdVals); + KMArray vals = KMArray.cast(instParam.getVals()); + short index = 0; + short length = vals.length(); + short key; + short type; + short obj; + while (index < length) { + obj = vals.get(index); + key = KMTag.getKey(obj); + type = KMTag.getTagType(obj); + + if (KMType.BYTES_TAG != type) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + obj = KMByteTag.cast(obj).getValue(); + ((KMAndroidSEProvider) seProvider).setAttestationId(key, KMByteBlob.cast(obj).getBuffer(), + KMByteBlob.cast(obj).getStartOff(), KMByteBlob.cast(obj).length()); + index++; + } + } + + private void processProvisionPreSharedSecretCmd(APDU apdu) { + short blob = KMByteBlob.exp(); + short argsProto = KMArray.instance((short) 1); + KMArray.cast(argsProto).add((short) 0, blob); + short args = receiveIncoming(apdu, argsProto); + + short val = KMArray.cast(args).get((short) 0); + + if (val != KMType.INVALID_VALUE + && KMByteBlob.cast(val).length() != SHARED_SECRET_KEY_SIZE) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + // Persist shared Hmac. + ((KMAndroidSEProvider) seProvider).createPresharedKey( + KMByteBlob.cast(val).getBuffer(), + KMByteBlob.cast(val).getStartOff(), + KMByteBlob.cast(val).length()); + + } + + //This function masks the error code with POWER_RESET_MASK_FLAG + // in case if card reset event occurred. The clients of the Applet + // has to extract the power reset status from the error code and + // process accordingly. + private static short buildErrorStatus(short err) { + short int32Ptr = KMInteger.instance((short) 4); + short powerResetStatus = 0; + if (((KMAndroidSEProvider) seProvider).isPowerReset()) { + powerResetStatus = POWER_RESET_MASK_FLAG; + } + + Util.setShort(KMInteger.cast(int32Ptr).getBuffer(), + KMInteger.cast(int32Ptr).getStartOff(), + powerResetStatus); + + Util.setShort(KMInteger.cast(int32Ptr).getBuffer(), + (short) (KMInteger.cast(int32Ptr).getStartOff() + 2), + err); + // reset power reset status flag to its default value. + //repository.restorePowerResetStatus(); //TODO + return int32Ptr; + } + + private void processGetProvisionStatusCmd(APDU apdu) { + short resp = KMArray.instance((short) 2); + KMArray.cast(resp).add((short) 0, buildErrorStatus(KMError.OK)); + KMArray.cast(resp).add((short) 1, KMInteger.uint_16(provisionStatus)); + sendOutgoing(apdu, resp); + } + + private void processSetBootParamsCmd(APDU apdu) { + short argsProto = KMArray.instance((short) 5); + + // Array of 4 expected arguments + // Argument 0 Boot Patch level + KMArray.cast(argsProto).add((short) 0, KMInteger.exp()); + // Argument 1 Verified Boot Key + KMArray.cast(argsProto).add((short) 1, KMByteBlob.exp()); + // Argument 2 Verified Boot Hash + KMArray.cast(argsProto).add((short) 2, KMByteBlob.exp()); + // Argument 3 Verified Boot State + KMArray.cast(argsProto).add((short) 3, KMEnum.instance(KMType.VERIFIED_BOOT_STATE)); + // Argument 4 Device Locked + KMArray.cast(argsProto).add((short) 4, KMEnum.instance(KMType.DEVICE_LOCKED)); + + short args = receiveIncoming(apdu, argsProto); + + short bootParam = KMArray.cast(args).get((short) 0); + + ((KMAndroidSEProvider) seProvider).setBootPatchLevel(KMInteger.cast(bootParam).getBuffer(), + KMInteger.cast(bootParam).getStartOff(), + KMInteger.cast(bootParam).length()); + + bootParam = KMArray.cast(args).get((short) 1); + if (KMByteBlob.cast(bootParam).length() > BOOT_KEY_MAX_SIZE) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + ((KMAndroidSEProvider) seProvider).setBootKey(KMByteBlob.cast(bootParam).getBuffer(), + KMByteBlob.cast(bootParam).getStartOff(), + KMByteBlob.cast(bootParam).length()); + + bootParam = KMArray.cast(args).get((short) 2); + if (KMByteBlob.cast(bootParam).length() > BOOT_HASH_MAX_SIZE) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + ((KMAndroidSEProvider) seProvider).setVerifiedBootHash(KMByteBlob.cast(bootParam).getBuffer(), + KMByteBlob.cast(bootParam).getStartOff(), + KMByteBlob.cast(bootParam).length()); + + bootParam = KMArray.cast(args).get((short) 3); + byte enumVal = KMEnum.cast(bootParam).getVal(); + ((KMAndroidSEProvider) seProvider).setBootState(enumVal); + + bootParam = KMArray.cast(args).get((short) 4); + enumVal = KMEnum.cast(bootParam).getVal(); + ((KMAndroidSEProvider) seProvider).setDeviceLocked(enumVal == KMType.DEVICE_LOCKED_TRUE); + + super.reboot(); + sendError(apdu, KMError.OK); + } + + private void processLockProvisioningCmd(APDU apdu) { + ((KMAndroidSEProvider) seProvider).setProvisionLocked(true); + sendError(apdu, KMError.OK); + } + + @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; + } + + private short validateApdu(APDU apdu) { + // Read the apdu header and buffer. + byte[] apduBuffer = apdu.getBuffer(); + byte apduClass = apduBuffer[ISO7816.OFFSET_CLA]; + short P1P2 = Util.getShort(apduBuffer, ISO7816.OFFSET_P1); + + // Validate APDU Header. + if ((apduClass != CLA_ISO7816_NO_SM_NO_CHAN)) { + sendError(apdu, KMError.UNSUPPORTED_CLA); + return KMType.INVALID_VALUE; + } + + // Validate P1P2. + if (P1P2 != KMKeymasterApplet.KM_HAL_VERSION) { + sendError(apdu, KMError.INVALID_P1P2); + return KMType.INVALID_VALUE; + } + return apduBuffer[ISO7816.OFFSET_INS]; + } +} + diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAttestationCertImpl.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAttestationCertImpl.java index 8abbd04e..c1cdb91d 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAttestationCertImpl.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAttestationCertImpl.java @@ -15,14 +15,14 @@ */ package com.android.javacard.keymaster; -import com.android.javacard.keymaster.KMAESKey; -import com.android.javacard.keymaster.KMByteBlob; -import com.android.javacard.keymaster.KMECPrivateKey; -import com.android.javacard.keymaster.KMMasterKey; +import com.android.javacard.seprovider.KMAESKey; +import com.android.javacard.seprovider.KMAttestationCert; +import com.android.javacard.seprovider.KMException; +import com.android.javacard.seprovider.KMMasterKey; +import com.android.javacard.seprovider.KMSEProvider; import javacard.framework.JCSystem; import javacard.framework.Util; -import javacard.security.AESKey; // 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. @@ -52,11 +52,11 @@ public class KMAttestationCertImpl implements KMAttestationCert { private static final byte[] androidExtn = { 0x06, 0x0A, 0X2B, 0X06, 0X01, 0X04, 0X01, (byte) 0XD6, 0X79, 0X02, 0X01, 0X11 }; - + private static final short RSA_SIG_LEN = 256; private static final short ECDSA_MAX_SIG_LEN = 72; - //Signature algorithm identifier - always ecdsaWithSha256 - 1.2.840.10045.4.3.2 + //Signature algorithm identifier - ecdsaWithSha256 - 1.2.840.10045.4.3.2 //SEQUENCE of alg OBJ ID and parameters = NULL. - private static final byte[] X509SignAlgIdentifier = { + private static final byte[] X509EcdsaSignAlgIdentifier = { 0x30, 0x0A, 0x06, @@ -70,6 +70,25 @@ public class KMAttestationCertImpl implements KMAttestationCert { 0x03, 0x02 }; + // Signature algorithm identifier - sha256WithRSAEncryption - 1.2.840.113549.1.1.11 + // SEQUENCE of alg OBJ ID and parameters = NULL. + private static final byte[] X509RsaSignAlgIdentifier = { + 0x30, + 0x0D, + 0x06, + 0x09, + 0x2A, + (byte) 0x86, + 0x48, + (byte) 0x86, + (byte) 0xF7, + 0x0D, + 0x01, + 0x01, + 0x0B, + 0x05, + 0x00 + }; // 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 = { @@ -83,26 +102,28 @@ public class KMAttestationCertImpl implements KMAttestationCert { 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 keyUsageKeyAgreement = (byte) 0x08; // 4th- bit + private static final byte keyUsageCertSign = (byte) 0x04; // 5th- bit - private static final byte KEYMASTER_VERSION = 41; - private static final byte ATTESTATION_VERSION = 4; + private static final byte KEYMASTER_VERSION = 100; + private static final byte ATTESTATION_VERSION = 100; 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 certLength; + private static short tbsStart; 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 stackPtr; + private static short bufStart; + private static short bufLength; + private static short uniqueId; private static short attChallenge; private static short notBefore; + private static short notAfter; private static short pubKey; private static short[] swParams; @@ -112,20 +133,31 @@ public class KMAttestationCertImpl implements KMAttestationCert { private static byte keyUsage; private static byte unusedBits; private static KMAttestationCert inst; + private static KMSEProvider seProvider; private static boolean rsaCert; private static byte deviceLocked; private static short verifiedBootKey; private static byte verifiedState; private static short verifiedHash; private static short issuer; + private static short subjectName; private static short signPriv; + private static short serialNum; + + private static byte certMode; + private static short certAttestKeySecret; + private static short certAttestKeyRsaPubModulus; + private static boolean certRsaSign; + private static final byte SERIAL_NUM_MAX_LEN = 20; + private static final byte SUBJECT_NAME_MAX_LEN = 32; private KMAttestationCertImpl() { } - public static KMAttestationCert instance(boolean rsaCert) { + public static KMAttestationCert instance(boolean rsaCert, KMSEProvider provider) { if (inst == null) { inst = new KMAttestationCertImpl(); + seProvider = provider; } init(); KMAttestationCertImpl.rsaCert = rsaCert; @@ -136,9 +168,9 @@ private static void init() { stack = null; stackPtr = 0; certStart = 0; - signatureOffset = 0; - start = 0; - length = 0; + certLength = 0; + bufStart = 0; + bufLength = 0; tbsLength = 0; if (swParams == null) { swParams = JCSystem.makeTransientShortArray((short) MAX_PARAMS, JCSystem.CLEAR_ON_RESET); @@ -162,6 +194,12 @@ private static void init() { rsaCert = true; deviceLocked = 0; signPriv = 0; + certMode = KMType.NO_CERT; + certAttestKeySecret = KMType.INVALID_VALUE; + certRsaSign = true; + issuer = KMType.INVALID_VALUE; + subjectName = KMType.INVALID_VALUE; + serialNum = KMType.INVALID_VALUE; } @Override @@ -188,30 +226,39 @@ private KMAttestationCert uniqueId(short obj) { } @Override - public KMAttestationCert notBefore(short obj, byte[] scratchpad) { - // convert milliseconds to UTC date - notBefore = KMUtils.convertToDate(obj, scratchpad, true); + public KMAttestationCert notBefore(short obj, boolean derEncoded, byte[] scratchpad) { + if(!derEncoded) { + // convert milliseconds to UTC date + notBefore = KMUtils.convertToDate(obj, scratchpad, true); + }else{ + notBefore = KMByteBlob.instance(KMByteBlob.cast(obj).getBuffer(), + KMByteBlob.cast(obj).getStartOff(), KMByteBlob.cast(obj).length()); + } 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); + public KMAttestationCert notAfter(short usageExpiryTimeObj, boolean derEncoded, byte[] scratchPad) { + if(!derEncoded) { + if (usageExpiryTimeObj != KMType.INVALID_VALUE) { + // compare if the expiry time is greater then 2051 then use generalized + // time format else use utc time format. + short 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 { - usageExpiryTimeObj = KMUtils - .convertToDate(usageExpiryTimeObj, scratchPad, true); + //notAfter = certExpirtyTimeObj; } - notAfter = usageExpiryTimeObj; - } else { - notAfter = certExpirtyTimeObj; + }else{ + notAfter = KMByteBlob.instance(KMByteBlob.cast(usageExpiryTimeObj).getBuffer(), + KMByteBlob.cast(usageExpiryTimeObj).getStartOff(), + KMByteBlob.cast(usageExpiryTimeObj).length()); } return this; } @@ -269,6 +316,10 @@ private void createKeyUsage(short tag) { keyUsage = (byte) (keyUsage | keyUsageKeyEncipher); } else if (KMEnumArrayTag.cast(tag).get(index) == KMType.DECRYPT) { keyUsage = (byte) (keyUsage | keyUsageDataEncipher); + } else if (KMEnumArrayTag.cast(tag).get(index) == KMType.AGREE_KEY){ + keyUsage = (byte) (keyUsage | keyUsageKeyAgreement); + }else if (KMEnumArrayTag.cast(tag).get(index) == KMType.ATTEST_KEY){ + keyUsage = (byte) (keyUsage | keyUsageCertSign); } index++; } @@ -279,9 +330,12 @@ private void createKeyUsage(short tag) { } } - private static void pushTbsCert(boolean rsaCert) { +//TODO Serial number, X509Version needa to be passed as parameter + private static void pushTbsCert(boolean rsaCert, boolean rsa) { short last = stackPtr; - pushExtensions(); + if(certMode == KMType.ATTESTATION_CERT) { + pushExtensions(); + } // subject public key info if (rsaCert) { pushRsaSubjectKeyInfo(); @@ -289,7 +343,8 @@ private static void pushTbsCert(boolean rsaCert) { pushEccSubjectKeyInfo(); } // subject - pushBytes(X509Subject, (short) 0, (short) X509Subject.length); + pushBytes(KMByteBlob.cast(subjectName).getBuffer(), KMByteBlob.cast(subjectName).getStartOff(), + KMByteBlob.cast(subjectName).length()); pushValidity(); // issuer - der encoded pushBytes( @@ -297,10 +352,15 @@ private static void pushTbsCert(boolean rsaCert) { KMByteBlob.cast(issuer).getStartOff(), KMByteBlob.cast(issuer).length()); // Algorithm Id - pushAlgorithmId(X509SignAlgIdentifier); + if(rsa) { + pushAlgorithmId(X509RsaSignAlgIdentifier); + }else{ + pushAlgorithmId(X509EcdsaSignAlgIdentifier); + } // Serial Number - pushByte(SERIAL_NUM); - pushIntegerHeader((short) 1); + pushBytes(KMByteBlob.cast(serialNum).getBuffer(), KMByteBlob.cast(serialNum).getStartOff(), + KMByteBlob.cast(serialNum).length()); + pushIntegerHeader(KMByteBlob.cast(serialNum).length()); // Version pushByte(X509_VERSION); pushIntegerHeader((short) 1); @@ -670,6 +730,7 @@ private static void pushEnumTag(short tagId, byte val) { 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)); } @@ -787,17 +848,17 @@ private static void pushBytes(byte[] buf, short start, short len) { private static void decrementStackPtr(short cnt) { stackPtr = (short) (stackPtr - cnt); - if (start > stackPtr) { + if (bufStart > stackPtr) { KMException.throwIt(KMError.UNKNOWN_ERROR); } } @Override - public KMAttestationCert buffer(byte[] buf, short bufStart, short maxLen) { + public KMAttestationCert buffer(byte[] buf, short start, short maxLen) { stack = buf; - start = bufStart; - length = maxLen; - stackPtr = (short) (start + length); + bufStart = start; + bufLength = maxLen; + stackPtr = (short) (bufStart + bufLength); return this; } @@ -806,49 +867,93 @@ public short getCertStart() { return certStart; } - @Override - public short getCertEnd() { - return (short) (start + length - 1); - } - @Override public short getCertLength() { - return (short) (getCertEnd() - getCertStart() + 1); + return certLength; } - @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; - KMAndroidSEProvider androidSeProvider = KMAndroidSEProvider.getInstance(); - short sigLen = androidSeProvider - .ecSign256( - androidSeProvider.getAttestationKey(), - 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); +public void build(short attSecret, short attMod, boolean rsaSign, boolean fakeCert) { + stackPtr = (short)(bufStart + bufLength); + short last = stackPtr; + short sigLen = 0; + if(fakeCert){ + rsaSign = true; + pushByte((byte)0); + sigLen = 1; + } + // Push placeholder signature Bit string header + // This will potentially change at the end + else if (rsaSign) { + decrementStackPtr(RSA_SIG_LEN); + } else { + decrementStackPtr(ECDSA_MAX_SIG_LEN); + } + short signatureOffset = stackPtr; + pushBitStringHeader((byte) 0, (short) (last - stackPtr)); + if (rsaSign) { + pushAlgorithmId(X509RsaSignAlgIdentifier); + } else { + pushAlgorithmId(X509EcdsaSignAlgIdentifier); + } + tbsLength = stackPtr; + pushTbsCert(rsaCert, rsaSign); + tbsStart = stackPtr; + tbsLength = (short) (tbsLength - tbsStart); + if(attSecret != KMType.INVALID_VALUE){ + // Sign with the attestation key + // The pubKey is the modulus. + if (rsaSign) { + sigLen = seProvider + .rsaSign256Pkcs1( + KMByteBlob.cast(attSecret).getBuffer(), + KMByteBlob.cast(attSecret).getStartOff(), + KMByteBlob.cast(attSecret).length(), + KMByteBlob.cast(attMod).getBuffer(), + KMByteBlob.cast(attMod).getStartOff(), + KMByteBlob.cast(attMod).length(), + stack, + tbsStart, + tbsLength, + stack, + signatureOffset); + if(sigLen > RSA_SIG_LEN) KMException.throwIt(KMError.UNKNOWN_ERROR); + } else { + sigLen = seProvider + .ecSign256( + KMByteBlob.cast(attSecret).getBuffer(), + KMByteBlob.cast(attSecret).getStartOff(), + KMByteBlob.cast(attSecret).length(), + stack, + tbsStart, + tbsLength, + stack, + signatureOffset); + if (sigLen > ECDSA_MAX_SIG_LEN) KMException.throwIt(KMError.UNKNOWN_ERROR); + } + // Adjust signature length + stackPtr = signatureOffset; + pushBitStringHeader((byte) 0, sigLen); + }else if(!fakeCert){ // no attestation key provisioned in the factory + KMException.throwIt(KMError.ATTESTATION_KEYS_NOT_PROVISIONED); + } + last = (short)(signatureOffset+sigLen); + // Add certificate sequence header + stackPtr = tbsStart; + pushSequenceHeader((short) (last - stackPtr)); + certStart = stackPtr; + certLength = (short)(last - certStart); + //print(stack, getCertStart(), getCertLength()); + } + + @Override + public void build() { + if(certMode == KMType.FAKE_CERT) { + build(KMType.INVALID_VALUE, KMType.INVALID_VALUE, true, true); + }else { + build(certAttestKeySecret, certAttestKeyRsaPubModulus, certRsaSign, false); } } - + @Override public KMAttestationCert makeUniqueId(byte[] scratchPad, short scratchPadOff, byte[] creationTime, short timeOffset, short creationTimeLen, @@ -871,14 +976,14 @@ public KMAttestationCert makeUniqueId(byte[] scratchPad, short scratchPadOff, scratchPad[scratchPadOff] = resetSinceIdRotation; scratchPadOff++; - timeOffset = KMByteBlob.instance((short) 32); - //Get the key data from the master key and use it for HMAC Sign. - AESKey aesKey = ((KMAESKey) masterKey).getKey(); - short mKeyData = KMByteBlob.instance((short) (aesKey.getSize() / 8)); + //Get the key data from the master key + KMAESKey aesKey = (KMAESKey) masterKey; + short mKeyData = KMByteBlob.instance((short) (aesKey.getKeySizeBits() / 8)); aesKey.getKey( - KMByteBlob.cast(mKeyData).getBuffer(), - KMByteBlob.cast(mKeyData).getStartOff()); - appIdOff = KMAndroidSEProvider.getInstance().hmacSign( + KMByteBlob.cast(mKeyData).getBuffer(), /* Key */ + KMByteBlob.cast(mKeyData).getStartOff()); /* Key start*/ + timeOffset = KMByteBlob.instance((short) 32); + appIdOff = seProvider.hmacSign( KMByteBlob.cast(mKeyData).getBuffer(), /* Key */ KMByteBlob.cast(mKeyData).getStartOff(), /* Key start*/ KMByteBlob.cast(mKeyData).length(), /* Key length*/ @@ -892,4 +997,60 @@ public KMAttestationCert makeUniqueId(byte[] scratchPad, short scratchPadOff, } return uniqueId(timeOffset); } + + @Override + public boolean serialNumber(short number){ + short length = KMByteBlob.cast(number).length(); + if(length > SERIAL_NUM_MAX_LEN){ + return false; + } + byte msb = KMByteBlob.cast(number).get((short)0); + if(msb < 0 && length > (SERIAL_NUM_MAX_LEN -1)){ + return false; + } + serialNum = number; + return true; + } + + @Override + public boolean subjectName(short sub){ + /* + short length = KMByteBlob.cast(sub).length(); + if(length > SUBJECT_NAME_MAX_LEN){ + return false; + } + Util.arrayCopyNonAtomic(KMByteBlob.cast(sub).getBuffer(), KMByteBlob.cast(sub).getStartOff(), + subjectName,(short)0,length); + subjectLen = length; + */ + if(sub == KMType.INVALID_VALUE || KMByteBlob.cast(sub).length() == 0) return false; + subjectName = sub; + return true; + } + + @Override + public KMAttestationCert ecAttestKey(short attestKey, byte mode){ + certMode = mode; + certAttestKeySecret = attestKey; + certAttestKeyRsaPubModulus = KMType.INVALID_VALUE; + certRsaSign = false; + return this; + } + + @Override + public KMAttestationCert rsaAttestKey(short attestPrivExp, short attestMod, byte mode){ + certMode = mode; + certAttestKeySecret = attestPrivExp; + certAttestKeyRsaPubModulus = attestMod; + certRsaSign = true; + return this; + } + + //Check + /* + * private void print(byte[] buf, short start, short length){ StringBuilder sb = + * new StringBuilder(length * 2); for(short i = start; i < (start+length); i + * ++){ sb.append(String.format("%02x", buf[i])); } System.out.println( + * sb.toString()); } + */ } diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMOperationImpl.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMOperationImpl.java deleted file mode 100644 index aa133bda..00000000 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMOperationImpl.java +++ /dev/null @@ -1,261 +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" (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 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 static final short CIPHER_ALG_OFFSET = 0x00; - private static final short PADDING_OFFSET = 0x01; - private static final short OPER_MODE_OFFSET = 0x02; - private static final short BLOCK_MODE_OFFSET = 0x03; - private static final short MAC_LENGTH_OFFSET = 0x04; - //This will hold the length of the buffer stored inside the - //Java Card after the GCM update operation. - private static final short AES_GCM_UPDATE_LEN_OFFSET = 0x05; - private short[] parameters; - // Either one of Cipher/Signature instance is stored. - private Object[] operationInst; - - public KMOperationImpl() { - parameters = JCSystem.makeTransientShortArray((short) 6, JCSystem.CLEAR_ON_RESET); - operationInst = JCSystem.makeTransientObjectArray((short) 1, JCSystem.CLEAR_ON_RESET); - } - - public short getMode() { - return parameters[OPER_MODE_OFFSET]; - } - - public void setMode(short mode) { - parameters[OPER_MODE_OFFSET] = mode; - } - - public short getMacLength() { - return parameters[MAC_LENGTH_OFFSET]; - } - - public void setMacLength(short macLength) { - parameters[MAC_LENGTH_OFFSET] = macLength; - } - - public short getPaddingAlgorithm() { - return parameters[PADDING_OFFSET]; - } - - public void setPaddingAlgorithm(short alg) { - parameters[PADDING_OFFSET] = alg; - } - - public void setBlockMode(short mode) { - parameters[BLOCK_MODE_OFFSET] = mode; - } - - public short getBlockMode() { - return parameters[BLOCK_MODE_OFFSET]; - } - - public short getCipherAlgorithm() { - return parameters[CIPHER_ALG_OFFSET]; - } - - public void setCipherAlgorithm(short cipherAlg) { - parameters[CIPHER_ALG_OFFSET] = cipherAlg; - } - - public void setCipher(Cipher cipher) { - operationInst[0] = cipher; - } - - public void setSignature(Signature signer) { - operationInst[0] = signer; - } - - private void resetCipher() { - operationInst[0] = null; - parameters[MAC_LENGTH_OFFSET] = 0; - parameters[AES_GCM_UPDATE_LEN_OFFSET] = 0; - parameters[BLOCK_MODE_OFFSET] = 0; - parameters[OPER_MODE_OFFSET] = 0; - parameters[CIPHER_ALG_OFFSET] = 0; - parameters[PADDING_OFFSET] = 0; - } - - @Override - public short update(byte[] inputDataBuf, short inputDataStart, - short inputDataLength, byte[] outputDataBuf, short outputDataStart) { - short len = ((Cipher) operationInst[0]).update(inputDataBuf, inputDataStart, inputDataLength, - outputDataBuf, outputDataStart); - if (parameters[CIPHER_ALG_OFFSET] == KMType.AES && parameters[BLOCK_MODE_OFFSET] == KMType.GCM) { - // Every time Block size data is stored as intermediate result. - parameters[AES_GCM_UPDATE_LEN_OFFSET] += (short) (inputDataLength - len); - } - return len; - } - - @Override - public short update(byte[] inputDataBuf, short inputDataStart, - short inputDataLength) { - ((Signature) operationInst[0]).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; - Cipher cipher = (Cipher) operationInst[0]; - short cipherAlg = parameters[CIPHER_ALG_OFFSET]; - short blockMode = parameters[BLOCK_MODE_OFFSET]; - short mode = parameters[OPER_MODE_OFFSET]; - short macLength = parameters[MAC_LENGTH_OFFSET]; - short padding = parameters[PADDING_OFFSET]; - short len = 0; - try { - if (cipherAlg == KMType.AES && blockMode == KMType.GCM) { - if (mode == KMType.DECRYPT) { - inputDataLen = (short) (inputDataLen - macLength); - } - } 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); - } - - for (short j = 1; j <= paddingByte; ++j) { - if (outputDataBuf[(short) (outputDataStart + len - j)] != paddingByte) { - 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) operationInst[0]).sign(inputDataBuf, inputDataStart, inputDataLength, - signBuf, signStart); - } finally { - KMAndroidSEProvider.getInstance().releaseSignatureInstance((Signature) operationInst[0]); - operationInst[0] = 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) operationInst[0]).verify(inputDataBuf, inputDataStart, inputDataLength, - signBuf, signStart, signLength); - } finally { - KMAndroidSEProvider.getInstance().releaseSignatureInstance((Signature) operationInst[0]); - operationInst[0] = null; - } - return ret; - } - - @Override - public void abort() { - if (operationInst[0] != null) { - if (parameters[OPER_MODE_OFFSET] == KMType.ENCRYPT || - parameters[OPER_MODE_OFFSET] == KMType.DECRYPT) { - KMAndroidSEProvider.getInstance().releaseCipherInstance((Cipher) operationInst[0]); - resetCipher(); - } else { - KMAndroidSEProvider.getInstance().releaseSignatureInstance((Signature) operationInst[0]); - } - operationInst[0] = null; - } - KMAndroidSEProvider.getInstance().releaseOperationInstance(this); - } - - @Override - public void updateAAD(byte[] dataBuf, short dataStart, short dataLength) { - ((AEADCipher) operationInst[0]).updateAAD(dataBuf, dataStart, dataLength); - } - - @Override - public short getAESGCMOutputSize(short dataSize, short macLength) { - if (parameters[OPER_MODE_OFFSET] == KMType.ENCRYPT) { - return (short) (parameters[AES_GCM_UPDATE_LEN_OFFSET] + dataSize + macLength); - } else { - return (short) (parameters[AES_GCM_UPDATE_LEN_OFFSET] + dataSize - macLength); - } - } -} \ 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 index c2b5c7f3..82093450 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMUtils.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMUtils.java @@ -15,6 +15,8 @@ */ package com.android.javacard.keymaster; +import com.android.javacard.seprovider.KMException; + import javacard.framework.Util; public class KMUtils { @@ -85,14 +87,14 @@ public static short convertToDate(short time, byte[] scratchPad, (short) 8) < 0) { Util.arrayCopyNonAtomic(firstJan2020, (short) 0, scratchPad, (short) 8, (short) 8); - subtract(scratchPad, (short) 0, (short) 8, (short) 16); + subtract(scratchPad, (short) 0, (short) 8, (short) 16, (byte) 8); 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); + subtract(scratchPad, (short) 0, (short) 8, (short) 16, (byte) 8); Util.arrayCopyNonAtomic(scratchPad, (short) 16, scratchPad, (short) 0, (short) 8); } @@ -133,7 +135,7 @@ public static short convertToDate(short time, byte[] scratchPad, Util.arrayCopyNonAtomic(yearMsec, (short) 0, scratchPad, (short) 8, (short) 8); } - subtract(scratchPad, (short) 0, (short) 8, (short) 16); + subtract(scratchPad, (short) 0, (short) 8, (short) 16, (byte) 8); Util.arrayCopyNonAtomic(scratchPad, (short) 16, scratchPad, (short) 0, (short) 8); if (((short) (i + 1) == leapYrIdx)) { @@ -183,7 +185,7 @@ public static short convertToDate(short time, byte[] scratchPad, if (KMInteger.unsignedByteArrayCompare(scratchPad, (short) 0, scratchPad, (short) 8, (short) 8) >= 0) { - subtract(scratchPad, (short) 0, (short) 8, (short) 16); + subtract(scratchPad, (short) 0, (short) 8, (short) 16, (byte) 8); Util.arrayCopyNonAtomic(scratchPad, (short) 16, scratchPad, (short) 0, (short) 8); } else { @@ -285,7 +287,7 @@ public static short divide(byte[] buf, short dividend, short divisor, // Copy remainder in the dividend and repeat. while (expCnt != 0) { if (compare(buf, dividend, divisor) >= 0) { - subtract(buf, dividend, divisor, remainder); + subtract(buf, dividend, divisor, remainder, (byte) 8); copy(buf, remainder, dividend); q = (short) (q + expCnt); } @@ -354,9 +356,9 @@ public static void add(byte[] buf, short op1, short op2, short result) { } // subtraction by borrowing. - public static void subtract(byte[] buf, short op1, short op2, short result) { + public static void subtract(byte[] buf, short op1, short op2, short result, byte sizeBytes) { byte borrow = 0; - byte index = 7; + byte index = (byte) (sizeBytes - 1); short r; short x; short y; @@ -409,4 +411,12 @@ public static short getLeapYrIndex(boolean from2020, short yrsCount) { return -1; } -} \ No newline at end of file + public static void computeOnesCompliment(byte[] buf, short offset, short len) { + short index = offset; + // Compute 1s compliment + while (index < (short) (len + offset)) { + buf[index] = (byte) ~buf[index]; + index++; + } + } +} diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAESKey.java b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAESKey.java similarity index 91% rename from Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAESKey.java rename to Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAESKey.java index cec6388e..7e34065e 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAESKey.java +++ b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAESKey.java @@ -13,12 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.javacard.keymaster; +package com.android.javacard.seprovider; import org.globalplatform.upgrade.Element; -import com.android.javacard.keymaster.KMMasterKey; - import javacard.security.AESKey; public class KMAESKey implements KMMasterKey { @@ -33,8 +31,8 @@ public void setKey(byte[] keyData, short kOff) { aesKey.setKey(keyData, kOff); } - public AESKey getKey() { - return aesKey; + public byte getKey(byte[] keyData, short kOff) { + return aesKey.getKey(keyData, kOff); } public short getKeySizeBits() { diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEProvider.java b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAndroidSEProvider.java similarity index 56% rename from Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEProvider.java rename to Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAndroidSEProvider.java index f64858f5..aaf93834 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEProvider.java +++ b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAndroidSEProvider.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.javacard.keymaster; +package com.android.javacard.seprovider; import org.globalplatform.upgrade.Element; import org.globalplatform.upgrade.UpgradeManager; @@ -31,112 +31,42 @@ import javacard.security.KeyPair; import javacard.security.MessageDigest; import javacard.security.RSAPrivateKey; -import javacard.security.RSAPublicKey; import javacard.security.RandomData; import javacard.security.Signature; import javacardx.crypto.AEADCipher; import javacardx.crypto.Cipher; - -import com.android.javacard.keymaster.KMAESKey; -import com.android.javacard.keymaster.KMAttestationKey; -import com.android.javacard.keymaster.KMECPrivateKey; -import com.android.javacard.keymaster.KMError; -import com.android.javacard.keymaster.KMException; -import com.android.javacard.keymaster.KMHmacKey; -import com.android.javacard.keymaster.KMMasterKey; -import com.android.javacard.keymaster.KMPreSharedKey; +import javacard.security.KeyAgreement; 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}; + static byte[] secp256r1_P; + static byte[] secp256r1_A; + + static byte[] secp256r1_B; + static byte[] secp256r1_S; // 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 byte[] secp256r1_UCG; + static byte[] secp256r1_N; static final short secp256r1_H = 1; // -------------------------------------------------------------- public static final short AES_GCM_TAG_LENGTH = 16; 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 TMP_ARRAY_SIZE = 300; private static final short RSA_KEY_SIZE = 256; public static final short CERT_CHAIN_MAX_SIZE = 2500;//First 2 bytes for length. + private static final short ADDITIONAL_CERT_CHAIN_MAX_SIZE = 512;//First 2 bytes for length. + private static final short BCC_MAX_SIZE = 512; + public static final short SHARED_SECRET_KEY_SIZE = 32; + public static final byte POWER_RESET_FALSE = (byte) 0xAA; + public static final byte POWER_RESET_TRUE = (byte) 0x00; - 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}; + private static KeyAgreement keyAgreement; // AESKey private AESKey aesKeys[]; @@ -152,18 +82,31 @@ public class KMAndroidSEProvider implements KMSEProvider { 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; + public static byte[] resetFlag; private Signature hmacSignature; //For ImportwrappedKey operations. private KMRsaOAEPEncoding rsaOaepDecipher; + private KMPoolManager poolMgr; + + // Data - originally was in repository + private byte[] attIdBrand; + private byte[] attIdDevice; + private byte[] attIdProduct; + private byte[] attIdSerial; + private byte[] attIdImei; + private byte[] attIdMeId; + private byte[] attIdManufacturer; + private byte[] attIdModel; + + // Boot parameters + private byte[] verifiedHash; + private byte[] bootKey; + private byte[] bootPatchLevel; + private boolean deviceBootLocked; + private short bootState; // Entropy private RandomData rng; @@ -171,7 +114,12 @@ public class KMAndroidSEProvider implements KMSEProvider { private byte[] certificateChain; private KMAESKey masterKey; private KMECPrivateKey attestationKey; + private KMECDeviceUniqueKey testKey; + private KMECDeviceUniqueKey deviceUniqueKey; private KMHmacKey preSharedKey; + private byte[] additionalCertChain; + private byte[] bcc; + private boolean isProvisionLocked; private static KMAndroidSEProvider androidSEProvider = null; @@ -180,6 +128,7 @@ public static KMAndroidSEProvider getInstance() { } public KMAndroidSEProvider() { + initStatics(); // Re-usable AES,DES and HMAC keys in persisted memory. aesKeys = new AESKey[2]; aesKeys[KEYSIZE_128_OFFSET] = (AESKey) KeyBuilder.buildKey( @@ -192,17 +141,9 @@ public KMAndroidSEProvider() { false); rsaKeyPair = new KeyPair(KeyPair.ALG_RSA, KeyBuilder.LENGTH_RSA_2048); ecKeyPair = new KeyPair(KeyPair.ALG_EC_FP, KeyBuilder.LENGTH_EC_FP_256); + keyAgreement = KeyAgreement.getInstance(KeyAgreement.ALG_EC_SVDP_DH_PLAIN, false); initECKey(ecKeyPair); - - // 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(); + poolMgr = KMPoolManager.getInstance(); //RsaOAEP Decipher rsaOaepDecipher = new KMRsaOAEPEncoding(KMRsaOAEPEncoding.ALG_RSA_PKCS1_OAEP_SHA256_MGF1_SHA1); @@ -218,14 +159,86 @@ public KMAndroidSEProvider() { //Allocate buffer for certificate chain. if (!isUpgrading()) { certificateChain = new byte[CERT_CHAIN_MAX_SIZE]; + additionalCertChain = new byte[ADDITIONAL_CERT_CHAIN_MAX_SIZE]; + bcc = new byte[BCC_MAX_SIZE]; // Initialize attestationKey and preShared key with zeros. Util.arrayFillNonAtomic(tmpArray, (short) 0, TMP_ARRAY_SIZE, (byte) 0); // Create attestation key of P-256 curve. - createAttestationKey(tmpArray, (short)0, (short) 32); + createAttestationKey(tmpArray, (short) 0, (short) 32); // Pre-shared secret key length is 32 bytes. - createPresharedKey(tmpArray, (short)0, (short) KMRepository.SHARED_SECRET_KEY_SIZE); + createPresharedKey(tmpArray, (short) 0, (short) SHARED_SECRET_KEY_SIZE); } androidSEProvider = this; + resetFlag = JCSystem.makeTransientByteArray((short) 1, + JCSystem.CLEAR_ON_DESELECT); + resetFlag[0] = (byte) POWER_RESET_FALSE; + } + + public static void initStatics() { + secp256r1_P = new byte[]{(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}; + + secp256r1_A = new byte[]{(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}; + + secp256r1_B = new byte[]{(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}; + + secp256r1_S = new byte[]{(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 + secp256r1_UCG = new byte[]{(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}; + + secp256r1_N = new byte[]{(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}; } public void clean() { @@ -250,194 +263,6 @@ private void initECKey(KeyPair ecKeyPair) { 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, short index) { - if (((KMInstance) pool[index]).reserved != 0) { - JCSystem.beginTransaction(); - ((KMInstance) pool[index]).reserved = 0; - JCSystem.commitTransaction(); - } - } - - 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) { - releaseInstance(pool, index); - break; - } - } - index++; - } - } - public AESKey createAESKey(short keysize) { try { newRandomNumber(tmpArray, (short) 0, (short) (keysize / 8)); @@ -786,12 +611,12 @@ public short hmacSign(byte[] keyBuf, short keyStart, short keyLength, public short hmacKDF(KMMasterKey masterkey, byte[] data, short dataStart, short dataLength, byte[] signature, short signatureStart) { try { - AESKey aesKey = ((KMAESKey) masterkey).getKey(); - aesKey.getKey(tmpArray, (short) 0); - HMACKey key = createHMACKey(tmpArray, (short) 0, - (short) (aesKey.getSize() / 8)); - return hmacSign(key, data, dataStart, dataLength, signature, - signatureStart); + KMAESKey aesKey = (KMAESKey) masterkey; + short keyLen = (short) (aesKey.getKeySizeBits() / 8); + byte[] keyData = new byte[keyLen]; + aesKey.getKey(keyData, (short) 0); + return hmacSign(keyData, (short) 0, keyLen, data, dataStart, dataLength, + signature, signatureStart); } finally { clean(); } @@ -819,38 +644,6 @@ public short rsaDecipherOAEP256(byte[] secret, short secretStart, outputDataBuf, (short) outputDataStart); } - public short ecSign256(KMAttestationKey attestationKey, - byte[] inputDataBuf, short inputDataStart, short inputDataLength, - byte[] outputDataBuf, short outputDataStart) { - Signature.OneShot signer = null; - try { - - signer = Signature.OneShot.open(MessageDigest.ALG_SHA_256, - Signature.SIG_CIPHER_ECDSA, Cipher.PAD_NULL); - signer.init(((KMECPrivateKey) attestationKey).getPrivateKey(), 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: @@ -909,8 +702,10 @@ private byte mapCipherAlg(byte alg, byte padding, byte blockmode, byte digest) { case KMType.RSA_PKCS1_1_5_ENCRYPT: return Cipher.ALG_RSA_PKCS1; case KMType.RSA_OAEP: { - if (digest == KMType.SHA2_256) { + if (digest == KMType.SHA1) { /* MGF Digest is SHA1 */ return KMRsaOAEPEncoding.ALG_RSA_PKCS1_OAEP_SHA256_MGF1_SHA1; + } else if (digest == KMType.SHA2_256) { /* MGF Digest is SHA256 */ + return KMRsaOAEPEncoding.ALG_RSA_PKCS1_OAEP_SHA256_MGF1_SHA256; } else { KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM); } @@ -921,11 +716,10 @@ private byte mapCipherAlg(byte alg, byte padding, byte blockmode, byte digest) { return -1; } - public Cipher createSymmetricCipher(short alg, short purpose, + public KMOperation createSymmetricCipher(short alg, short purpose, short macLength, 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]; @@ -944,42 +738,23 @@ public Cipher createSymmetricCipher(short alg, short purpose, 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; + KMOperation operation = + poolMgr.getOperationImpl(purpose, cipherAlg, alg, padding, blockMode, macLength); + ((KMOperationImpl) operation).init(key, KMType.INVALID_VALUE, ivBuffer, ivStart, ivLength); + return operation; } - public Signature createHmacSignerVerifier(short purpose, short digest, + public KMOperation 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); + KMOperation operation = + poolMgr.getOperationImpl(purpose, Signature.ALG_HMAC_SHA_256, + KMType.HMAC, KMType.INVALID_VALUE, KMType.INVALID_VALUE, KMType.INVALID_VALUE); HMACKey key = createHMACKey(secret, secretStart, secretLength); - hmacSignerVerifier.init(key, (byte) mapPurpose(purpose)); - return hmacSignerVerifier; + ((KMOperationImpl) operation).init(key, digest, null, (short) 0, (short) 0); + return operation; } @Override @@ -987,27 +762,17 @@ 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; + KMOperation 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); - opr.setCipher(cipher); - opr.setCipherAlgorithm(alg); - opr.setBlockMode(blockMode); - opr.setPaddingAlgorithm(padding); - opr.setMode(purpose); - opr.setMacLength(macLength); + opr = createSymmetricCipher(alg, purpose, macLength, blockMode, padding, keyBuf, keyStart, + keyLength, ivBuf, ivStart, ivLength); break; case KMType.HMAC: - Signature signerVerifier = createHmacSignerVerifier(purpose, digest, - keyBuf, keyStart, keyLength); - opr = getOperationInstanceFromPool(); - opr.setSignature(signerVerifier); + opr = createHmacSignerVerifier(purpose, digest, keyBuf, keyStart, keyLength); break; default: CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); @@ -1016,73 +781,70 @@ public KMOperation initSymmetricOperation(byte purpose, byte alg, return opr; } - public Signature createRsaSigner(short digest, short padding, byte[] secret, + public KMOperation 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); + KMOperation operation = poolMgr.getOperationImpl(KMType.SIGN, alg, KMType.RSA, padding, + KMType.INVALID_VALUE, KMType.INVALID_VALUE); RSAPrivateKey key = (RSAPrivateKey) rsaKeyPair.getPrivate(); key.setExponent(secret, secretStart, secretLength); key.setModulus(modBuffer, modOff, modLength); - rsaSigner.init(key, opMode); - return rsaSigner; + ((KMOperationImpl) operation).init(key, digest, null, (short) 0, (short) 0); + return operation; } - public Cipher createRsaDecipher(short padding, short digest, byte[] secret, + public KMOperation createRsaDecipher(short padding, short mgfDigest, 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); + byte cipherAlg = mapCipherAlg(KMType.RSA, (byte) padding, (byte) 0, (byte) mgfDigest); + KMOperation operation = poolMgr.getOperationImpl(KMType.DECRYPT, cipherAlg, KMType.RSA, padding, + KMType.INVALID_VALUE, KMType.INVALID_VALUE); RSAPrivateKey key = (RSAPrivateKey) rsaKeyPair.getPrivate(); key.setExponent(secret, secretStart, secretLength); key.setModulus(modBuffer, modOff, modLength); - rsaCipher.init(key, Cipher.MODE_DECRYPT); - return rsaCipher; + ((KMOperationImpl) operation).init(key, KMType.INVALID_VALUE, null, (short) 0, (short) 0); + return operation; } - public Signature createEcSigner(short digest, byte[] secret, + public KMOperation 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; + KMOperation operation = poolMgr + .getOperationImpl(KMType.SIGN, alg, KMType.EC, KMType.INVALID_VALUE, + KMType.INVALID_VALUE, KMType.INVALID_VALUE); + ((KMOperationImpl) operation).init(key, digest, null, (short) 0, (short) 0); + return operation; + } + + public KMOperation createKeyAgreement(byte[] secret, short secretStart, + short secretLength) { + ECPrivateKey key = (ECPrivateKey) ecKeyPair.getPrivate(); + key.setS(secret, secretStart, secretLength); + KMOperation operation = poolMgr + .getOperationImpl(KMType.AGREE_KEY, KeyAgreement.ALG_EC_SVDP_DH_PLAIN, + KMType.EC, KMType.INVALID_VALUE, KMType.INVALID_VALUE, KMType.INVALID_VALUE); + ((KMOperationImpl) operation).init(key, KMType.INVALID_VALUE, null, (short) 0, (short) 0); + return operation; } @Override public KMOperation initAsymmetricOperation(byte purpose, byte alg, - byte padding, byte digest, byte[] privKeyBuf, short privKeyStart, + byte padding, byte digest, byte mgfDigest, byte[] privKeyBuf, short privKeyStart, short privKeyLength, byte[] pubModBuf, short pubModStart, short pubModLength) { - KMOperationImpl opr = null; + KMOperation opr = null; if (alg == KMType.RSA) { switch (purpose) { case KMType.SIGN: - Signature signer = createRsaSigner(digest, padding, privKeyBuf, + opr = createRsaSigner(digest, padding, privKeyBuf, privKeyStart, privKeyLength, pubModBuf, pubModStart, pubModLength); - opr = getOperationInstanceFromPool(); - opr.setSignature(signer); - opr.setCipherAlgorithm(alg); - opr.setPaddingAlgorithm(padding); - opr.setMode(purpose); break; case KMType.DECRYPT: - Cipher decipher = createRsaDecipher(padding, digest, privKeyBuf, + opr = createRsaDecipher(padding, mgfDigest, privKeyBuf, privKeyStart, privKeyLength, pubModBuf, pubModStart, pubModLength); - opr = getOperationInstanceFromPool(); - opr.setCipher(decipher); - opr.setCipherAlgorithm(alg); - opr.setPaddingAlgorithm(padding); - opr.setMode(purpose); break; default: KMException.throwIt(KMError.UNSUPPORTED_PURPOSE); @@ -1091,10 +853,11 @@ public KMOperation initAsymmetricOperation(byte purpose, byte alg, } else if (alg == KMType.EC) { switch (purpose) { case KMType.SIGN: - Signature signer = createEcSigner(digest, privKeyBuf, privKeyStart, - privKeyLength); - opr = getOperationInstanceFromPool(); - opr.setSignature(signer); + opr = createEcSigner(digest, privKeyBuf, privKeyStart, privKeyLength); + break; + + case KMType.AGREE_KEY: + opr = createKeyAgreement(privKeyBuf, privKeyStart, privKeyLength); break; default: KMException.throwIt(KMError.UNSUPPORTED_PURPOSE); @@ -1107,11 +870,6 @@ public KMOperation initAsymmetricOperation(byte purpose, byte alg, } - @Override - public KMAttestationCert getAttestationCert(boolean rsaCert) { - return KMAttestationCertImpl.instance(rsaCert); - } - @Override public short cmacKDF(KMPreSharedKey pSharedKey, byte[] label, short labelStart, short labelLen, byte[] context, short contextStart, @@ -1121,7 +879,6 @@ public short cmacKDF(KMPreSharedKey pSharedKey, byte[] label, return key.getKey(keyBuf, keyStart); } - @Override public void clearCertificateChain() { JCSystem.beginTransaction(); Util.arrayFillNonAtomic(certificateChain, (short) 0, CERT_CHAIN_MAX_SIZE, (byte) 0); @@ -1129,7 +886,6 @@ public void clearCertificateChain() { } //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 | Cert2 |... @@ -1152,33 +908,16 @@ public void persistPartialCertificateChain(byte[] buf, short offset, short 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); @@ -1236,7 +975,6 @@ public KMMasterKey createMasterKey(short keySizeBits) { } } - @Override public KMAttestationKey createAttestationKey(byte[] keyData, short offset, short length) { if (attestationKey == null) { @@ -1249,7 +987,6 @@ public KMAttestationKey createAttestationKey(byte[] keyData, short offset, return (KMAttestationKey) attestationKey; } - @Override public KMPreSharedKey createPresharedKey(byte[] keyData, short offset, short length) { short lengthInBits = (short) (length * 8); if ((lengthInBits % 8 != 0) || !(lengthInBits >= 64 && lengthInBits <= 512)) { @@ -1269,7 +1006,6 @@ public KMMasterKey getMasterKey() { return (KMMasterKey) masterKey; } - @Override public KMAttestationKey getAttestationKey() { return (KMAttestationKey) attestationKey; } @@ -1279,21 +1015,475 @@ public KMPreSharedKey getPresharedKey() { return (KMPreSharedKey) preSharedKey; } - private void releasePool(Object[] pool) { - short index = 0; - short len = (short) pool.length; - while (index < len) { - if (pool[index] != null) { - releaseInstance(pool, index); + @Override + public short ecSign256(byte[] secret, short secretStart, short secretLength, + byte[] inputDataBuf, short inputDataStart, short inputDataLength, + byte[] outputDataBuf, short outputDataStart) { + + ECPrivateKey key = (ECPrivateKey) ecKeyPair.getPrivate(); + key.setS(secret, secretStart, secretLength); + + Signature.OneShot signer = null; + try { + + 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(); + } + } + } + + @Override + public short ecSign256(KMAttestationKey ecPrivKey, byte[] inputDataBuf, short inputDataStart, + short inputDataLength, + byte[] outputDataBuf, short outputDataStart) { + Signature.OneShot signer = null; + try { + + signer = Signature.OneShot.open(MessageDigest.ALG_SHA_256, + Signature.SIG_CIPHER_ECDSA, Cipher.PAD_NULL); + signer.init(((KMECPrivateKey) ecPrivKey).getPrivateKey(), Signature.MODE_SIGN); + return signer.sign(inputDataBuf, inputDataStart, inputDataLength, + outputDataBuf, outputDataStart); + } finally { + if (signer != null) { + signer.close(); } - index++; } } @Override - public void releaseAllOperations() { - releasePool(cipherPool); - releasePool(sigPool); - releasePool(operationPool); + public short rsaSign256Pkcs1(byte[] secret, short secretStart, short secretLength, byte[] modBuf, + short modStart, + short modLength, byte[] inputDataBuf, short inputDataStart, short inputDataLength, + byte[] outputDataBuf, + short outputDataStart) { + + Signature.OneShot signer = null; + try { + + signer = Signature.OneShot.open(MessageDigest.ALG_SHA_256, + Signature.SIG_CIPHER_RSA, Cipher.PAD_PKCS1); + + RSAPrivateKey key = (RSAPrivateKey) rsaKeyPair.getPrivate(); + ; + key.setExponent(secret, secretStart, secretLength); + key.setModulus(modBuf, modStart, modLength); + + signer.init(key, Signature.MODE_SIGN); + return signer.sign(inputDataBuf, inputDataStart, inputDataLength, + outputDataBuf, outputDataStart); + } finally { + if (signer != null) { + signer.close(); + } + } + + } + + @Override + public boolean isAttestationKeyProvisioned() { + return false; + } + + @Override + public short getAttestationKeyAlgorithm() { + return KMType.INVALID_VALUE; + } + + @Override + public short getAttestationId(short tag, byte[] buffer, short start) { + switch (tag) { + // Attestation Id Brand + case KMType.ATTESTATION_ID_BRAND: + Util.arrayCopyNonAtomic(attIdBrand, (short) 0, buffer, start, (short) attIdBrand.length); + return (short) attIdBrand.length; + // Attestation Id Device + case KMType.ATTESTATION_ID_DEVICE: + Util.arrayCopyNonAtomic(attIdDevice, (short) 0, buffer, start, (short) attIdDevice.length); + return (short) attIdDevice.length; + // Attestation Id Product + case KMType.ATTESTATION_ID_PRODUCT: + Util.arrayCopyNonAtomic(attIdProduct, (short) 0, buffer, start, + (short) attIdProduct.length); + return (short) attIdProduct.length; + // Attestation Id Serial + case KMType.ATTESTATION_ID_SERIAL: + Util.arrayCopyNonAtomic(attIdSerial, (short) 0, buffer, start, (short) attIdSerial.length); + return (short) attIdSerial.length; + // Attestation Id IMEI + case KMType.ATTESTATION_ID_IMEI: + Util.arrayCopyNonAtomic(attIdImei, (short) 0, buffer, start, (short) attIdImei.length); + return (short) attIdImei.length; + // Attestation Id MEID + case KMType.ATTESTATION_ID_MEID: + Util.arrayCopyNonAtomic(attIdMeId, (short) 0, buffer, start, (short) attIdMeId.length); + return (short) attIdMeId.length; + // Attestation Id Manufacturer + case KMType.ATTESTATION_ID_MANUFACTURER: + Util.arrayCopyNonAtomic(attIdManufacturer, (short) 0, buffer, start, + (short) attIdManufacturer.length); + return (short) attIdManufacturer.length; + // Attestation Id Model + case KMType.ATTESTATION_ID_MODEL: + Util.arrayCopyNonAtomic(attIdModel, (short) 0, buffer, start, (short) attIdModel.length); + return (short) attIdModel.length; + } + return (short) 0; + } + + public void setAttestationId(short tag, byte[] buffer, short start, short length) { + switch (tag) { + // Attestation Id Brand + case KMType.ATTESTATION_ID_BRAND: + JCSystem.beginTransaction(); + attIdBrand = new byte[length]; + Util.arrayCopyNonAtomic(buffer, (short) start, attIdBrand, (short) 0, length); + JCSystem.commitTransaction(); + break; + // Attestation Id Device + case KMType.ATTESTATION_ID_DEVICE: + JCSystem.beginTransaction(); + attIdDevice = new byte[length]; + Util.arrayCopyNonAtomic(buffer, (short) start, attIdDevice, (short) 0, length); + JCSystem.commitTransaction(); + break; + // Attestation Id Product + case KMType.ATTESTATION_ID_PRODUCT: + JCSystem.beginTransaction(); + attIdProduct = new byte[length]; + Util.arrayCopyNonAtomic(buffer, (short) start, attIdProduct, (short) 0, length); + JCSystem.commitTransaction(); + break; + // Attestation Id Serial + case KMType.ATTESTATION_ID_SERIAL: + JCSystem.beginTransaction(); + attIdSerial = new byte[length]; + Util.arrayCopyNonAtomic(buffer, (short) start, attIdSerial, (short) 0, length); + JCSystem.commitTransaction(); + break; + // Attestation Id IMEI + case KMType.ATTESTATION_ID_IMEI: + JCSystem.beginTransaction(); + attIdImei = new byte[length]; + Util.arrayCopyNonAtomic(buffer, (short) start, attIdImei, (short) 0, length); + JCSystem.commitTransaction(); + break; + // Attestation Id MEID + case KMType.ATTESTATION_ID_MEID: + JCSystem.beginTransaction(); + attIdMeId = new byte[length]; + Util.arrayCopyNonAtomic(buffer, (short) start, attIdMeId, (short) 0, length); + JCSystem.commitTransaction(); + break; + // Attestation Id Manufacturer + case KMType.ATTESTATION_ID_MANUFACTURER: + JCSystem.beginTransaction(); + attIdManufacturer = new byte[length]; + Util.arrayCopyNonAtomic(buffer, (short) start, attIdManufacturer, (short) 0, length); + JCSystem.commitTransaction(); + break; + // Attestation Id Model + case KMType.ATTESTATION_ID_MODEL: + JCSystem.beginTransaction(); + attIdModel = new byte[length]; + Util.arrayCopyNonAtomic(buffer, (short) start, attIdModel, (short) 0, length); + JCSystem.commitTransaction(); + break; + } + } + + @Override + public void deleteAttestationIds() { + attIdBrand = null; + attIdDevice = null; + attIdProduct = null; + attIdSerial = null; + attIdImei = null; + attIdMeId = null; + attIdManufacturer = null; + attIdModel = null; + } + + public boolean isPowerReset() { + boolean flag = false; + if (resetFlag[0] == POWER_RESET_TRUE) { + resetFlag[0] = POWER_RESET_FALSE; + flag = true; + if (poolMgr != null) { + poolMgr.powerReset(); + } + } + return flag; + } + + @Override + public short getVerifiedBootHash(byte[] buffer, short start) { + Util.arrayCopyNonAtomic(verifiedHash, (short) 0, buffer, start, (short) verifiedHash.length); + return (short) verifiedHash.length; + } + + @Override + public short getBootKey(byte[] buffer, short start) { + Util.arrayCopyNonAtomic(bootKey, (short) 0, buffer, start, (short) bootKey.length); + return (short) bootKey.length; + } + + @Override + public short getBootState() { + return bootState; + } + + @Override + public boolean isDeviceBootLocked() { + return deviceBootLocked; + } + + @Override + public short getBootPatchLevel(byte[] buffer, short start) { + Util.arrayCopyNonAtomic(bootPatchLevel, (short) 0, buffer, start, + (short) bootPatchLevel.length); + return (short) bootPatchLevel.length; + } + + public void setVerifiedBootHash(byte[] buffer, short start, short length) { + if (verifiedHash == null) { + verifiedHash = new byte[32]; + } + if (length != 32) { + KMException.throwIt(KMError.UNKNOWN_ERROR); + } + Util.arrayCopyNonAtomic(buffer, start, verifiedHash, (short) 0, (short) 32); + } + + public void setBootKey(byte[] buffer, short start, short length) { + if (bootKey == null) { + bootKey = new byte[32]; + } + if (length != 32) { + KMException.throwIt(KMError.UNKNOWN_ERROR); + } + Util.arrayCopyNonAtomic(buffer, start, bootKey, (short) 0, (short) 32); + } + + public void setBootState(short state) { + bootState = state; + } + + public void setDeviceLocked(boolean state) { + deviceBootLocked = state; + } + + public void setBootPatchLevel(byte[] buffer, short start, short length) { + if (bootPatchLevel == null) { + bootPatchLevel = new byte[4]; + } + if (length > 4 || length < 0) { + KMException.throwIt(KMError.UNKNOWN_ERROR); + } + Util.arrayCopyNonAtomic(buffer, start, bootPatchLevel, (short) 0, (short) 4); + } + + @Override + public short hkdf(byte[] ikm, short ikmOff, short ikmLen, byte[] salt, + short saltOff, short saltLen, byte[] info, short infoOff, short infoLen, + byte[] out, short outOff, short outLen) { + // HMAC_extract + hkdfExtract(ikm, ikmOff, ikmLen, salt, saltOff, saltLen, tmpArray, (short) 0); + //HMAC_expand + return hkdfExpand(tmpArray, (short) 0, (short) 32, info, infoOff, infoLen, out, outOff, outLen); + } + + private short hkdfExtract(byte[] ikm, short ikmOff, short ikmLen, byte[] salt, short saltOff, + short saltLen, + byte[] out, short off) { + // https://tools.ietf.org/html/rfc5869#section-2.2 + HMACKey hmacKey = createHMACKey(salt, saltOff, saltLen); + hmacSignature.init(hmacKey, Signature.MODE_SIGN); + return hmacSignature.sign(ikm, ikmOff, ikmLen, out, off); + } + + private short hkdfExpand(byte[] prk, short prkOff, short prkLen, byte[] info, short infoOff, + short infoLen, + byte[] out, short outOff, short outLen) { + // https://tools.ietf.org/html/rfc5869#section-2.3 + short digestLen = (short) 32; // SHA256 digest length. + // Calculate no of iterations N. + short n = (short) ((short) (outLen + digestLen - 1) / digestLen); + if (n > 255) { + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + } + HMACKey hmacKey = createHMACKey(prk, prkOff, prkLen); + Util.arrayFill(tmpArray, (short) 0, (short) 32, (byte) 0); + byte[] cnt = {(byte) 0}; + short bytesCopied = 0; + short len = 0; + for (short i = 0; i < n; i++) { + cnt[0]++; + hmacSignature.init(hmacKey, Signature.MODE_SIGN); + if (i != 0) { + hmacSignature.update(tmpArray, (short) 0, (short) 32); + } + hmacSignature.update(info, infoOff, infoLen); + len = hmacSignature.sign(cnt, (short) 0, (short) 1, tmpArray, (short) 0); + if ((short) (bytesCopied + len) > outLen) { + len = (short) (outLen - bytesCopied); + } + Util.arrayCopyNonAtomic(tmpArray, (short) 0, out, (short) (outOff + bytesCopied), len); + bytesCopied += len; + } + return outLen; + } + + @Override + public short ecdhKeyAgreement(byte[] privKey, short privKeyOff, + short privKeyLen, byte[] publicKey, short publicKeyOff, + short publicKeyLen, byte[] secret, short secretOff) { + keyAgreement.init(createEcKey(privKey, privKeyOff, privKeyLen)); + return keyAgreement.generateSecret(publicKey, publicKeyOff, publicKeyLen, secret, secretOff); + } + + @Override + public boolean ecVerify256(byte[] pubKey, short pubKeyOffset, short pubKeyLen, + byte[] inputDataBuf, short inputDataStart, short inputDataLength, + byte[] signatureDataBuf, short signatureDataStart, + short signatureDataLen) { + Signature.OneShot signer = null; + try { + signer = Signature.OneShot.open(MessageDigest.ALG_SHA_256, + Signature.SIG_CIPHER_ECDSA, Cipher.PAD_NULL); + ECPublicKey key = (ECPublicKey) ecKeyPair.getPublic(); + key.setW(pubKey, pubKeyOffset, pubKeyLen); + signer.init(key, Signature.MODE_VERIFY); + return signer.verify(inputDataBuf, inputDataStart, inputDataLength, + signatureDataBuf, signatureDataStart, + (short) (signatureDataBuf[(short) (signatureDataStart + 1)] + 2)); + } finally { + if (signer != null) { + signer.close(); + } + } + } + + @Override + public short ecSign256(KMDeviceUniqueKey ecPrivKey, byte[] inputDataBuf, + short inputDataStart, short inputDataLength, byte[] outputDataBuf, + short outputDataStart) { + Signature.OneShot signer = null; + try { + signer = Signature.OneShot.open(MessageDigest.ALG_SHA_256, + Signature.SIG_CIPHER_ECDSA, Cipher.PAD_NULL); + signer.init(((KMECDeviceUniqueKey) ecPrivKey).getPrivateKey(), Signature.MODE_SIGN); + return signer.sign(inputDataBuf, inputDataStart, inputDataLength, + outputDataBuf, outputDataStart); + } finally { + if (signer != null) { + signer.close(); + } + } + } + + private KMDeviceUniqueKey createDeviceUniqueKey(KMECDeviceUniqueKey key, + byte[] pubKey, short pubKeyOff, short pubKeyLen, byte[] privKey, + short privKeyOff, short privKeyLen) { + if (key == null) { + KeyPair ecKeyPair = new KeyPair(KeyPair.ALG_EC_FP, KeyBuilder.LENGTH_EC_FP_256); + initECKey(ecKeyPair); + key = new KMECDeviceUniqueKey(ecKeyPair); + } + key.setS(privKey, privKeyOff, privKeyLen); + key.setW(pubKey, pubKeyOff, pubKeyLen); + return (KMDeviceUniqueKey) key; + } + + @Override + public KMDeviceUniqueKey createDeviceUniqueKey(boolean testMode, + byte[] pubKey, short pubKeyOff, short pubKeyLen, byte[] privKey, + short privKeyOff, short privKeyLen) { + KMDeviceUniqueKey key; + if (testMode) { + key = createDeviceUniqueKey(testKey, pubKey, pubKeyOff, + pubKeyLen, privKey, privKeyOff, privKeyLen); + if (testKey == null) { + testKey = (KMECDeviceUniqueKey) key; + } + } else { + key = createDeviceUniqueKey(deviceUniqueKey, pubKey, pubKeyOff, + pubKeyLen, privKey, privKeyOff, privKeyLen); + if (deviceUniqueKey == null) { + deviceUniqueKey = (KMECDeviceUniqueKey) key; + } + } + return key; + } + + @Override + public KMDeviceUniqueKey getDeviceUniqueKey(boolean testMode) { + return ((KMDeviceUniqueKey) (testMode ? testKey : deviceUniqueKey)); + } + + @Override + public void persistAdditionalCertChain(byte[] buf, short offset, short len) { + // Input buffer contains encoded additional certificate chain as shown below. + // AdditionalDKSignatures = { + // + SignerName => DKCertChain + // } + // SignerName = tstr + // DKCertChain = [ + // 2* Certificate // Root -> Leaf. Root is the vendo r + // // self-signed cert, leaf contains DK_pu b + // ] + // Certificate = COSE_Sign1 of a public key + if ((short) (len + 2) >= ADDITIONAL_CERT_CHAIN_MAX_SIZE) { + KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + } + JCSystem.beginTransaction(); + Util.setShort(additionalCertChain, (short) 0, (short) len); + Util.arrayCopyNonAtomic(buf, offset, additionalCertChain, + (short) 2, len); + JCSystem.commitTransaction(); + + } + + @Override + public short getAdditionalCertChainLength() { + return Util.getShort(additionalCertChain, (short) 0); + } + + @Override + public byte[] getAdditionalCertChain() { + return additionalCertChain; + } + + + @Override + public byte[] getBootCertificateChain() { + return bcc; + } + + public void persistBootCertificateChain(byte[] buf, short offset, short len) { + if ((short) (len + 2) > BCC_MAX_SIZE) { + KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + } + JCSystem.beginTransaction(); + Util.setShort(bcc, (short) 0, (short) len); + Util.arrayCopyNonAtomic(buf, offset, bcc, + (short) 2, len); + JCSystem.commitTransaction(); + } + + public void setProvisionLocked(boolean locked) { + JCSystem.beginTransaction(); + isProvisionLocked = locked; + JCSystem.commitTransaction(); + } + + public boolean isProvisionLocked() { + return isProvisionLocked; } } diff --git a/Applet/src/com/android/javacard/keymaster/KMAttestationCert.java b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAttestationCert.java similarity index 82% rename from Applet/src/com/android/javacard/keymaster/KMAttestationCert.java rename to Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAttestationCert.java index 487dccc7..6cd8e7b0 100644 --- a/Applet/src/com/android/javacard/keymaster/KMAttestationCert.java +++ b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAttestationCert.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.javacard.keymaster; +package com.android.javacard.seprovider; /** * The KMAttestationCert interface represents a X509 compliant attestation certificate required to @@ -60,7 +60,7 @@ public interface KMAttestationCert { * @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 instance of the master key. + * @param masterKey * @return instance of KMAttestationCert. */ KMAttestationCert makeUniqueId(byte[] scratchpad, short scratchPadOff, byte[] creationTime, @@ -76,7 +76,7 @@ KMAttestationCert makeUniqueId(byte[] scratchpad, short scratchPadOff, byte[] cr * @param scratchpad Buffer to store intermediate results. * @return instance of KMAttestationCert. */ - KMAttestationCert notBefore(short obj, byte[] scratchpad); + KMAttestationCert notBefore(short obj, boolean derEncoded, byte[] scratchpad); /** @@ -84,14 +84,11 @@ KMAttestationCert makeUniqueId(byte[] scratchpad, short scratchPadOff, byte[] cr * certificate's valid period. * * @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. + * @param scratchPad Buffer to store intermediate results. * @return instance of KMAttestationCert */ - KMAttestationCert notAfter(short usageExpiryTimeObj, - short certExpirtyTimeObj, byte[] scratchPad, short offset); + KMAttestationCert notAfter(short usageExpiryTimeObj, boolean derEncoded, byte[] scratchPad); /** * Set device lock status received during booting time or due to device lock command. @@ -153,13 +150,6 @@ KMAttestationCert notAfter(short usageExpiryTimeObj, */ short getCertStart(); - /** - * Get the end of the certificate - * - * @return end of the attestation cert. - */ - short getCertEnd(); - /** * Get the length of the certificate * @@ -167,8 +157,38 @@ KMAttestationCert notAfter(short usageExpiryTimeObj, */ short getCertLength(); + /** - * Build the certificate. After this method the certificate is ready. + * Build a fake signed certificate. After this method executes the certificate is ready with the + * signature equal to 1 byte which is 0 and with rsa signature algorithm. */ void build(); + + /** + * Set the Serial number in the certificate. If no serial number is set then serial number is 1. + * + * @param serialNumber + */ + boolean serialNumber(short serialNumber); + + /** + * Set the Subject Name in the certificate. + * + * @param subject + */ + boolean subjectName(short subject); + + /** + * Set attestation key and mode. + * @param attestKey KMByteBlob of the key + * @param mode + */ + KMAttestationCert ecAttestKey(short attestKey, byte mode); + /** + * Set attestation key and mode. + * @param attestKey KMByteBlob of the key + * @param mode + */ + KMAttestationCert rsaAttestKey(short attestPrivExp, short attestMod, byte mode); + } diff --git a/Applet/src/com/android/javacard/keymaster/KMAttestationKey.java b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAttestationKey.java similarity index 95% rename from Applet/src/com/android/javacard/keymaster/KMAttestationKey.java rename to Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAttestationKey.java index 3d626bbf..1b8e334e 100644 --- a/Applet/src/com/android/javacard/keymaster/KMAttestationKey.java +++ b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAttestationKey.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.javacard.keymaster; +package com.android.javacard.seprovider; /** * KMAttestationKey is a marker interface and the SE Provider has to implement this interface. @@ -21,5 +21,5 @@ * additional security. The attestation key is maintained by the SEProvider. */ public interface KMAttestationKey { - } + diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMInstance.java b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMDeviceUniqueKey.java similarity index 74% rename from Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMInstance.java rename to Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMDeviceUniqueKey.java index 5178d4e2..08e60a3f 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMInstance.java +++ b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMDeviceUniqueKey.java @@ -1,5 +1,5 @@ /* - * Copyright(C) 2020 The Android Open Source Project + * Copyright(C) 2021 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. @@ -13,11 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.javacard.keymaster; +package com.android.javacard.seprovider; -public class KMInstance { +public interface KMDeviceUniqueKey { - public byte reserved; - public Object object; - public byte instanceCount; + short getPublicKey(byte[] buf, short offset); } diff --git a/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMECDeviceUniqueKey.java b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMECDeviceUniqueKey.java new file mode 100644 index 00000000..0f68de61 --- /dev/null +++ b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMECDeviceUniqueKey.java @@ -0,0 +1,53 @@ +/* + * Copyright(C) 2021 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.seprovider; +import javacard.security.ECPrivateKey; +import javacard.security.ECPublicKey; +import javacard.security.KeyPair; + +public class KMECDeviceUniqueKey implements KMDeviceUniqueKey { + + private KeyPair ecKeyPair; + + @Override + public short getPublicKey(byte[] buf, short offset) { + ECPublicKey publicKey = getPublicKey(); + return publicKey.getW(buf, offset); + } + + public KMECDeviceUniqueKey(KeyPair ecPair) { + ecKeyPair = ecPair; + } + + public void setS(byte[] buffer, short offset, short length) { + ECPrivateKey ecPriv = (ECPrivateKey) ecKeyPair.getPrivate(); + ecPriv.setS(buffer, offset, length); + } + + public void setW(byte[] buffer, short offset, short length) { + ECPublicKey ecPublicKey = (ECPublicKey) ecKeyPair.getPublic(); + ecPublicKey.setW(buffer, offset, length); + } + + public ECPrivateKey getPrivateKey() { + return (ECPrivateKey) ecKeyPair.getPrivate(); + } + + public ECPublicKey getPublicKey() { + return (ECPublicKey) ecKeyPair.getPublic(); + } + +} diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMECPrivateKey.java b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMECPrivateKey.java similarity index 90% rename from Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMECPrivateKey.java rename to Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMECPrivateKey.java index 3188ad19..33372f97 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMECPrivateKey.java +++ b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMECPrivateKey.java @@ -13,14 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.javacard.keymaster; +package com.android.javacard.seprovider; import org.globalplatform.upgrade.Element; -import com.android.javacard.keymaster.KMAESKey; -import com.android.javacard.keymaster.KMAttestationCert; -import com.android.javacard.keymaster.KMAttestationKey; - import javacard.security.AESKey; import javacard.security.ECPrivateKey; import javacard.security.KeyPair; diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMEcdsa256NoDigestSignature.java b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMEcdsa256NoDigestSignature.java similarity index 98% rename from Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMEcdsa256NoDigestSignature.java rename to Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMEcdsa256NoDigestSignature.java index f90b834f..4707f637 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMEcdsa256NoDigestSignature.java +++ b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMEcdsa256NoDigestSignature.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.javacard.keymaster; +package com.android.javacard.seprovider; import javacard.security.CryptoException; import javacard.framework.Util; diff --git a/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMError.java b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMError.java new file mode 100644 index 00000000..5754abe9 --- /dev/null +++ b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMError.java @@ -0,0 +1,134 @@ +/* + * 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.seprovider; + +/** + * KMError includes all the error codes from android keymaster hal specifications. The values are + * positive unlike negative values in keymaster hal. + */ +public class KMError { + + public static final short OK = 0; + public static final short UNSUPPORTED_PURPOSE = 2; + public static final short INCOMPATIBLE_PURPOSE = 3; + public static final short UNSUPPORTED_ALGORITHM = 4; + public static final short INCOMPATIBLE_ALGORITHM = 5; + public static final short UNSUPPORTED_KEY_SIZE = 6; + public static final short UNSUPPORTED_BLOCK_MODE = 7; + public static final short INCOMPATIBLE_BLOCK_MODE = 8; + public static final short UNSUPPORTED_MAC_LENGTH = 9; + public static final short UNSUPPORTED_PADDING_MODE = 10; + public static final short INCOMPATIBLE_PADDING_MODE = 11; + public static final short UNSUPPORTED_DIGEST = 12; + public static final short INCOMPATIBLE_DIGEST = 13; + + public static final short UNSUPPORTED_KEY_ENCRYPTION_ALGORITHM = 19; + + /** + * For PKCS8 & PKCS12 + */ + public static final short INVALID_INPUT_LENGTH = 21; + + + public static final short KEY_USER_NOT_AUTHENTICATED = 26; + public static final short INVALID_OPERATION_HANDLE = 28; + public static final short VERIFICATION_FAILED = 30; + public static final short TOO_MANY_OPERATIONS = 31; + public static final short INVALID_KEY_BLOB = 33; + + public static final short INVALID_ARGUMENT = 38; + public static final short UNSUPPORTED_TAG = 39; + public static final short INVALID_TAG = 40; + public static final short IMPORT_PARAMETER_MISMATCH = 44; + public static final short OPERATION_CANCELLED = 46; + + public static final short MISSING_NONCE = 51; + public static final short INVALID_NONCE = 52; + public static final short MISSING_MAC_LENGTH = 53; + public static final short CALLER_NONCE_PROHIBITED = 55; + public static final short INVALID_MAC_LENGTH = 57; + public static final short MISSING_MIN_MAC_LENGTH = 58; + public static final short UNSUPPORTED_MIN_MAC_LENGTH = 59; + public static final short UNSUPPORTED_EC_CURVE = 61; + public static final short KEY_REQUIRES_UPGRADE = 62; + + public static final short ATTESTATION_CHALLENGE_MISSING = 63; + public static final short ATTESTATION_APPLICATION_ID_MISSING = 65; + public static final short CANNOT_ATTEST_IDS = 66; + public static final short ROLLBACK_RESISTANCE_UNAVAILABLE = 67; + + public static final short DEVICE_LOCKED = 72; + public static final short EARLY_BOOT_ENDED = 73; + public static final short ATTESTATION_KEYS_NOT_PROVISIONED =74; + public static final short INCOMPATIBLE_MGF_DIGEST = 78; + public static final short UNSUPPORTED_MGF_DIGEST = 79; + public static final short MISSING_NOT_BEFORE = 80; + public static final short MISSING_NOT_AFTER = 81; + public static final short MISSING_ISSUER_SUBJECT_NAME = 82; + public static final short INVALID_ISSUER_SUBJECT_NAME = 83; + + public static final short UNIMPLEMENTED = 100; + public static final short UNKNOWN_ERROR = 1000; + + //Extended errors + public static final short SW_CONDITIONS_NOT_SATISFIED = 10001; + public static final short UNSUPPORTED_CLA = 10002; + public static final short INVALID_P1P2 = 10003; + public static final short UNSUPPORTED_INSTRUCTION = 10004; + public static final short CMD_NOT_ALLOWED = 10005; + public static final short SW_WRONG_LENGTH = 10006; + public static final short INVALID_DATA = 10007; + + //Crypto errors + public static final short CRYPTO_ILLEGAL_USE = 10008; + public static final short CRYPTO_ILLEGAL_VALUE = 10009; + public static final short CRYPTO_INVALID_INIT = 10010; + public static final short CRYPTO_NO_SUCH_ALGORITHM = 10011; + public static final short CRYPTO_UNINITIALIZED_KEY = 10012; + //Generic Unknown error. + public static final short GENERIC_UNKNOWN_ERROR = 10013; + + // Remote key provisioning error codes. + public static final short STATUS_FAILED = 32000; + public static final short STATUS_INVALID_MAC = 32001; + public static final short STATUS_PRODUCTION_KEY_IN_TEST_REQUEST = 32002; + public static final short STATUS_TEST_KEY_IN_PRODUCTION_REQUEST = 32003; + public static final short STATUS_INVALID_EEK = 32004; + public static final short INVALID_STATE = 32005; + + public static short translate(short err) { + switch(err) { + case SW_CONDITIONS_NOT_SATISFIED: + case UNSUPPORTED_CLA: + case INVALID_P1P2: + case INVALID_DATA: + case CRYPTO_ILLEGAL_USE: + case CRYPTO_ILLEGAL_VALUE: + case CRYPTO_INVALID_INIT: + case CRYPTO_UNINITIALIZED_KEY: + case GENERIC_UNKNOWN_ERROR: + case UNKNOWN_ERROR: + return UNKNOWN_ERROR; + case CRYPTO_NO_SUCH_ALGORITHM: + return UNSUPPORTED_ALGORITHM; + case UNSUPPORTED_INSTRUCTION: + case CMD_NOT_ALLOWED: + case SW_WRONG_LENGTH: + return UNIMPLEMENTED; + } + return err; + } +} diff --git a/Applet/src/com/android/javacard/keymaster/KMException.java b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMException.java similarity index 70% rename from Applet/src/com/android/javacard/keymaster/KMException.java rename to Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMException.java index bf588aba..c0b2431f 100644 --- a/Applet/src/com/android/javacard/keymaster/KMException.java +++ b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMException.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.javacard.keymaster; +package com.android.javacard.seprovider; import javacard.framework.JCSystem; @@ -24,33 +24,32 @@ */ public class KMException extends RuntimeException { - public short[] reason; - public static KMException exception; + private static short[] reason; + private static KMException exception; private KMException() { - reason = JCSystem.makeTransientShortArray((short) 1, JCSystem.CLEAR_ON_RESET); } - - public static void throwIt(short reason) { - instance(); - exception.reason[(short) 0] = reason; + public static short reason(){ + return reason[0]; + } + public static void throwIt(short e) { + if(reason == null) { + reason = JCSystem.makeTransientShortArray((short)1,JCSystem.CLEAR_ON_DESELECT); + } + if(exception == null){ + exception = new KMException(); + } + reason[0] = e; throw exception; } - +/* public static KMException instance() { if (exception == null) { exception = new KMException(); } return exception; } - - public void clear() { - exception.reason[(short) 0] = KMError.UNKNOWN_ERROR; - } - - public static short getReason() { - return exception.reason[0]; - } +*/ } diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMHmacKey.java b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMHmacKey.java similarity index 94% rename from Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMHmacKey.java rename to Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMHmacKey.java index b2a38b24..2eaeeeb9 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMHmacKey.java +++ b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMHmacKey.java @@ -13,12 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.javacard.keymaster; +package com.android.javacard.seprovider; import org.globalplatform.upgrade.Element; -import com.android.javacard.keymaster.KMPreSharedKey; - import javacard.security.HMACKey; public class KMHmacKey implements KMPreSharedKey { diff --git a/Applet/src/com/android/javacard/keymaster/KMMasterKey.java b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMMasterKey.java similarity index 95% rename from Applet/src/com/android/javacard/keymaster/KMMasterKey.java rename to Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMMasterKey.java index 7a88778e..6eab5e56 100644 --- a/Applet/src/com/android/javacard/keymaster/KMMasterKey.java +++ b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMMasterKey.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.javacard.keymaster; +package com.android.javacard.seprovider; /** * KMMasterKey is a marker interface and the SE Provider has to implement this interface. Internally diff --git a/Applet/src/com/android/javacard/keymaster/KMOperation.java b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMOperation.java similarity index 95% rename from Applet/src/com/android/javacard/keymaster/KMOperation.java rename to Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMOperation.java index 3132e4b3..b73d58d1 100644 --- a/Applet/src/com/android/javacard/keymaster/KMOperation.java +++ b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMOperation.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.javacard.keymaster; +package com.android.javacard.seprovider; /** * KMOperation represents a persistent operation started by keymaster hal's beginOperation function. @@ -29,7 +29,7 @@ short update(byte[] inputDataBuf, short inputDataStart, short inputDataLength, // Used for signature operations short update(byte[] inputDataBuf, short inputDataStart, short inputDataLength); - // Used for finishing cipher operations. + // Used for finishing cipher operations or ecdh keyAgreement. short finish(byte[] inputDataBuf, short inputDataStart, short inputDataLength, byte[] outputDataBuf, short outputDataStart); diff --git a/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMOperationImpl.java b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMOperationImpl.java new file mode 100644 index 00000000..2c447fd4 --- /dev/null +++ b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMOperationImpl.java @@ -0,0 +1,359 @@ +/* + * 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.seprovider; + +import com.android.javacard.seprovider.KMError; +import com.android.javacard.seprovider.KMException; +import com.android.javacard.seprovider.KMType; +import javacard.framework.JCSystem; +import javacard.framework.Util; +import javacard.security.KeyAgreement; +import javacard.security.PrivateKey; +import javacard.security.Signature; +import javacardx.crypto.AEADCipher; +import javacardx.crypto.Cipher; +import javacard.security.CryptoException; +import javacard.security.Key; + +public class KMOperationImpl implements KMOperation { + + private static final short ALG_TYPE_OFFSET = 0x00; + private static final short PADDING_OFFSET = 0x01; + private static final short PURPOSE_OFFSET = 0x02; + private static final short BLOCK_MODE_OFFSET = 0x03; + private static final short MAC_LENGTH_OFFSET = 0x04; + //This will hold the length of the buffer stored inside the + //Java Card after the GCM update operation. + private static final short AES_GCM_UPDATE_LEN_OFFSET = 0x05; + private static final short PARAMETERS_LENGTH = 6; + private short[] parameters; + // Either one of Cipher/Signature instance is stored. + private Object[] operationInst; + + public KMOperationImpl() { + parameters = JCSystem.makeTransientShortArray(PARAMETERS_LENGTH, JCSystem.CLEAR_ON_RESET); + operationInst = JCSystem.makeTransientObjectArray((short) 1, JCSystem.CLEAR_ON_RESET); + reset(); + } + + public short getPurpose() { + return parameters[PURPOSE_OFFSET]; + } + + public void setPurpose(short mode) { + parameters[PURPOSE_OFFSET] = mode; + } + + public short getMacLength() { + return parameters[MAC_LENGTH_OFFSET]; + } + + public void setMacLength(short macLength) { + parameters[MAC_LENGTH_OFFSET] = macLength; + } + + public short getPaddingAlgorithm() { + return parameters[PADDING_OFFSET]; + } + + public void setPaddingAlgorithm(short alg) { + parameters[PADDING_OFFSET] = alg; + } + + public void setBlockMode(short mode) { + parameters[BLOCK_MODE_OFFSET] = mode; + } + + public short getBlockMode() { + return parameters[BLOCK_MODE_OFFSET]; + } + + public short getAlgorithmType() { + return parameters[ALG_TYPE_OFFSET]; + } + + public void setAlgorithmType(short cipherAlg) { + parameters[ALG_TYPE_OFFSET] = cipherAlg; + } + + public void setCipher(Cipher cipher) { + operationInst[0] = cipher; + } + + public void setSignature(Signature signer) { + operationInst[0] = signer; + } + + public void setKeyAgreement(KeyAgreement keyAgreement) { + operationInst[0] = keyAgreement; + } + + public boolean isResourceMatches(Object object) { + return operationInst[0] == object; + } + + private void reset() { + operationInst[0] = null; + parameters[MAC_LENGTH_OFFSET] = KMType.INVALID_VALUE; + parameters[AES_GCM_UPDATE_LEN_OFFSET] = 0; + parameters[BLOCK_MODE_OFFSET] = KMType.INVALID_VALUE; + parameters[PURPOSE_OFFSET] = KMType.INVALID_VALUE; + parameters[ALG_TYPE_OFFSET] = KMType.INVALID_VALUE; + parameters[PADDING_OFFSET] = KMType.INVALID_VALUE; + } + + 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 void initSymmetricCipher(Key key, byte[] ivBuffer, short ivStart, short ivLength) { + Cipher symmCipher = (Cipher) operationInst[0]; + byte cipherAlg = symmCipher.getAlgorithm(); + switch (cipherAlg) { + case Cipher.ALG_AES_BLOCK_128_CBC_NOPAD: + case Cipher.ALG_AES_CTR: + symmCipher.init(key, mapPurpose(getPurpose()), ivBuffer, ivStart, ivLength); + break; + case Cipher.ALG_AES_BLOCK_128_ECB_NOPAD: + case Cipher.ALG_DES_ECB_NOPAD: + symmCipher.init(key, mapPurpose(getPurpose())); + 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(getPurpose()), ivBuffer, ivStart, (short) 8); + break; + case AEADCipher.ALG_AES_GCM: + ((AEADCipher) symmCipher).init(key, mapPurpose(getPurpose()), ivBuffer, + ivStart, ivLength); + break; + default:// This should never happen + CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); + break; + } + } + + private void initRsa(Key key, short digest) { + if (KMType.SIGN == getPurpose()) { + byte mode; + if (getPaddingAlgorithm() == KMType.PADDING_NONE || + (getPaddingAlgorithm() == KMType.RSA_PKCS1_1_5_SIGN && + digest == KMType.DIGEST_NONE)) { + mode = Cipher.MODE_DECRYPT; + } else { + mode = Signature.MODE_SIGN; + } + ((Signature) operationInst[0]).init((PrivateKey) key, mode); + } else { // RSA Cipher + ((Cipher) operationInst[0]).init((PrivateKey) key, mapPurpose(getPurpose())); + } + } + + private void initEc(Key key) { + if (KMType.AGREE_KEY == getPurpose()) { + ((KeyAgreement) operationInst[0]).init((PrivateKey) key); + } else { + ((Signature) operationInst[0]).init((PrivateKey) key, mapPurpose(getPurpose())); + } + } + + public void init(Key key, short digest, byte[] buf, short start, short length) { + switch (getAlgorithmType()) { + case KMType.AES: + case KMType.DES: + initSymmetricCipher(key, buf, start, length); + break; + case KMType.HMAC: + ((Signature) operationInst[0]).init(key, mapPurpose(getPurpose())); + break; + case KMType.RSA: + initRsa(key, digest); + break; + case KMType.EC: + initEc(key); + break; + default:// This should never happen + CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); + break; + } + } + + @Override + public short update(byte[] inputDataBuf, short inputDataStart, + short inputDataLength, byte[] outputDataBuf, short outputDataStart) { + short len = ((Cipher) operationInst[0]).update(inputDataBuf, inputDataStart, inputDataLength, + outputDataBuf, outputDataStart); + if (parameters[ALG_TYPE_OFFSET] == KMType.AES + && parameters[BLOCK_MODE_OFFSET] == KMType.GCM) { + // Every time Block size data is stored as intermediate result. + parameters[AES_GCM_UPDATE_LEN_OFFSET] += (short) (inputDataLength - len); + } + return len; + } + + @Override + public short update(byte[] inputDataBuf, short inputDataStart, + short inputDataLength) { + ((Signature) operationInst[0]).update(inputDataBuf, inputDataStart, inputDataLength); + return 0; + } + + private short finishKeyAgreement(byte[] publicKey, short start, short len, byte[] output, + short outputStart) { + return ((KeyAgreement) operationInst[0]).generateSecret(publicKey, start, len, + output, outputStart); + } + + private short finishCipher(byte[] inputDataBuf, short inputDataStart, short inputDataLen, + byte[] outputDataBuf, + short outputDataStart) { + short len = 0; + try { + byte[] tmpArray = KMAndroidSEProvider.getInstance().tmpArray; + Cipher cipher = (Cipher) operationInst[0]; + short cipherAlg = parameters[ALG_TYPE_OFFSET]; + short blockMode = parameters[BLOCK_MODE_OFFSET]; + short mode = parameters[PURPOSE_OFFSET]; + short macLength = parameters[MAC_LENGTH_OFFSET]; + short padding = parameters[PADDING_OFFSET]; + + if (cipherAlg == KMType.AES && blockMode == KMType.GCM) { + if (mode == KMType.DECRYPT) { + inputDataLen = (short) (inputDataLen - macLength); + } + } 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); + } + + for (short j = 1; j <= paddingByte; ++j) { + if (outputDataBuf[(short) (outputDataStart + len - j)] != paddingByte) { + 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(); + } + return len; + } + + @Override + public short finish(byte[] inputDataBuf, short inputDataStart, short inputDataLen, + byte[] outputDataBuf, + short outputDataStart) { + if (parameters[PURPOSE_OFFSET] == KMType.AGREE_KEY) { + return finishKeyAgreement(inputDataBuf, inputDataStart, inputDataLen, outputDataBuf, + outputDataStart); + } else { + return finishCipher(inputDataBuf, inputDataStart, inputDataLen, outputDataBuf, + outputDataStart); + } + } + + @Override + public short sign(byte[] inputDataBuf, short inputDataStart, + short inputDataLength, byte[] signBuf, short signStart) { + return ((Signature) operationInst[0]).sign(inputDataBuf, inputDataStart, inputDataLength, + signBuf, signStart); + } + + @Override + public boolean verify(byte[] inputDataBuf, short inputDataStart, + short inputDataLength, byte[] signBuf, short signStart, short signLength) { + return ((Signature) operationInst[0]).verify(inputDataBuf, inputDataStart, inputDataLength, + signBuf, signStart, signLength); + } + + @Override + public void abort() { + reset(); + } + + @Override + public void updateAAD(byte[] dataBuf, short dataStart, short dataLength) { + ((AEADCipher) operationInst[0]).updateAAD(dataBuf, dataStart, dataLength); + } + + @Override + public short getAESGCMOutputSize(short dataSize, short macLength) { + if (parameters[PURPOSE_OFFSET] == KMType.ENCRYPT) { + return (short) (parameters[AES_GCM_UPDATE_LEN_OFFSET] + dataSize + macLength); + } else { + return (short) (parameters[AES_GCM_UPDATE_LEN_OFFSET] + dataSize - macLength); + } + } +} diff --git a/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMPoolManager.java b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMPoolManager.java new file mode 100644 index 00000000..9938cd59 --- /dev/null +++ b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMPoolManager.java @@ -0,0 +1,304 @@ +/* + * Copyright(C) 2021 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.seprovider; +import javacard.security.KeyAgreement; +import javacard.security.Signature; +import javacardx.crypto.AEADCipher; +import javacardx.crypto.Cipher; + +/** + * This class manages all the pool instances. + */ +public class KMPoolManager { + + public static final short MAX_OPERATION_INSTANCES = 4; + // Cipher pool + private Object[] cipherPool; + // Signature pool + private Object[] signerPool; + // Keyagreement pool + private Object[] keyAgreementPool; + // KMOperationImpl pool + private Object[] operationPool; + + 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, + KMRsaOAEPEncoding.ALG_RSA_PKCS1_OAEP_SHA256_MGF1_SHA256, + 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}; + + final byte[] KEY_AGREE_ALGS = {KeyAgreement.ALG_EC_SVDP_DH_PLAIN}; + + + private static KMPoolManager poolManager; + + public static KMPoolManager getInstance() { + if (poolManager == null) { + poolManager = new KMPoolManager(); + } + return poolManager; + } + + private KMPoolManager() { + cipherPool = new Object[(short) (CIPHER_ALGS.length * 4)]; + signerPool = new Object[(short) (SIG_ALGS.length * 4)]; + keyAgreementPool = new Object[(short) (KEY_AGREE_ALGS.length * 4)]; + operationPool = new Object[4]; + /* Initialize pools */ + initializeOperationPool(); + initializeSignerPool(); + initializeCipherPool(); + initializeKeyAgreementPool(); + } + + private void initializeOperationPool() { + short index = 0; + while (index < MAX_OPERATION_INSTANCES) { + operationPool[index] = new KMOperationImpl(); + index++; + } + } + + // Create a signature instance of each algorithm once. + private void initializeSignerPool() { + short index = 0; + while (index < SIG_ALGS.length) { + signerPool[index] = getSignatureInstance(SIG_ALGS[index]); + index++; + } + } + + //Create a cipher instance of each algorithm once. + private void initializeCipherPool() { + short index = 0; + while (index < CIPHER_ALGS.length) { + cipherPool[index] = getCipherInstance(CIPHER_ALGS[index]); + index++; + } + } + + private void initializeKeyAgreementPool() { + short index = 0; + while (index < KEY_AGREE_ALGS.length) { + keyAgreementPool[index] = getKeyAgreementInstance(KEY_AGREE_ALGS[index]); + index++; + } + } + + private Object[] getCryptoPoolInstance(short purpose) { + switch (purpose) { + case KMType.AGREE_KEY: + return keyAgreementPool; + + case KMType.ENCRYPT: + case KMType.DECRYPT: + return cipherPool; + + case KMType.SIGN: + case KMType.VERIFY: + return signerPool; + + default: + KMException.throwIt(KMError.UNSUPPORTED_PURPOSE); + } + return null; + } + + private Object createInstance(short purpose, short alg) { + switch (purpose) { + case KMType.AGREE_KEY: + return getKeyAgreementInstance((byte) alg); + + case KMType.ENCRYPT: + case KMType.DECRYPT: + return getCipherInstance((byte) alg); + + case KMType.SIGN: + case KMType.VERIFY: + return getSignatureInstance((byte) alg); + + default: + KMException.throwIt(KMError.UNSUPPORTED_PURPOSE); + } + return null; + } + + private KeyAgreement getKeyAgreementInstance(byte alg) { + return KeyAgreement.getInstance(alg, false); + } + + 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) || + (KMRsaOAEPEncoding.ALG_RSA_PKCS1_OAEP_SHA256_MGF1_SHA256 == alg)) { + return new KMRsaOAEPEncoding(alg); + } else { + return Cipher.getInstance(alg, false); + } + } + + /** + * Returns the first available resource from operation pool. + * + * @return instance of the available resource or null if no resource is available. + */ + public KMOperation getResourceFromOperationPool() { + short index = 0; + KMOperationImpl impl; + while (index < operationPool.length) { + impl = (KMOperationImpl) operationPool[index]; + // Mode is always set. so compare using mode value. + if (impl.getPurpose() == KMType.INVALID_VALUE) { + return impl; + } + index++; + } + return null; + } + + private byte getAlgorithm(short purpose, Object object) { + switch (purpose) { + case KMType.AGREE_KEY: + return ((KeyAgreement) object).getAlgorithm(); + + case KMType.ENCRYPT: + case KMType.DECRYPT: + return ((Cipher) object).getAlgorithm(); + + case KMType.SIGN: + case KMType.VERIFY: + return ((Signature) object).getAlgorithm(); + + default: + KMException.throwIt(KMError.UNSUPPORTED_PURPOSE); + } + return 0; + } + + private boolean isResourceBusy(Object obj) { + short index = 0; + while (index < MAX_OPERATION_INSTANCES) { + if (((KMOperationImpl) operationPool[index]).isResourceMatches(obj)) { + return true; + } + index++; + } + return false; + } + + private void setObject(short purpose, KMOperation operation, Object obj) { + switch (purpose) { + case KMType.AGREE_KEY: + ((KMOperationImpl) operation).setKeyAgreement((KeyAgreement) obj); + break; + case KMType.ENCRYPT: + case KMType.DECRYPT: + ((KMOperationImpl) operation).setCipher((Cipher) obj); + break; + case KMType.SIGN: + case KMType.VERIFY: + ((KMOperationImpl) operation).setSignature((Signature) obj); + break; + default: + KMException.throwIt(KMError.UNSUPPORTED_PURPOSE); + } + } + + private void reserveOperation(KMOperation operation, short purpose, short strongboxAlgType, + short padding, short blockMode, short macLength, Object obj) { + ((KMOperationImpl) operation).setPurpose(purpose); + ((KMOperationImpl) operation).setAlgorithmType(strongboxAlgType); + ((KMOperationImpl) operation).setPaddingAlgorithm(padding); + ((KMOperationImpl) operation).setBlockMode(blockMode); + ((KMOperationImpl) operation).setMacLength(macLength); + setObject(purpose, operation, obj); + } + + + public KMOperation getOperationImpl(short purpose, short alg, short strongboxAlgType, + short padding, + short blockMode, short macLength) { + KMOperation operation; + // Throw exception if no resource from operation pool is available. + if (null == (operation = getResourceFromOperationPool())) { + KMException.throwIt(KMError.TOO_MANY_OPERATIONS); + } + // Get one of the pool instances (cipher / signer / keyAgreement) based on purpose. + Object[] pool = getCryptoPoolInstance(purpose); + short index = 0; + short usageCount = 0; + + while (index < pool.length) { + if (usageCount >= MAX_OPERATION_INSTANCES) { + KMException.throwIt(KMError.TOO_MANY_OPERATIONS); + } + if (pool[index] == null) { + // Create one of the instance (Cipher / Signer / KeyAgreement] based on purpose. + JCSystem.beginTransaction(); + pool[index] = createInstance(purpose, alg); + JCSystem.commitTransaction(); + reserveOperation(operation, purpose, strongboxAlgType, padding, blockMode, macLength, + pool[index]); + break; + } + if (alg == getAlgorithm(purpose, pool[index])) { + // Check if the crypto instance is not busy and free to use. + if (!isResourceBusy(pool[index])) { + reserveOperation(operation, purpose, strongboxAlgType, padding, blockMode, macLength, + pool[index]); + break; + } + usageCount++; + } + index++; + } + return operation; + } + + public void powerReset() { + short index = 0; + while (index < operationPool.length) { + ((KMOperationImpl) operationPool[index]).abort(); + index++; + } + } + +} diff --git a/Applet/src/com/android/javacard/keymaster/KMPreSharedKey.java b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMPreSharedKey.java similarity index 95% rename from Applet/src/com/android/javacard/keymaster/KMPreSharedKey.java rename to Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMPreSharedKey.java index 273aeb4a..86bb1df9 100644 --- a/Applet/src/com/android/javacard/keymaster/KMPreSharedKey.java +++ b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMPreSharedKey.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.javacard.keymaster; +package com.android.javacard.seprovider; /** * KMPreSharedKey is a marker interface and the SE Provider has to implement this interface. diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMRsa2048NoDigestSignature.java b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMRsa2048NoDigestSignature.java similarity index 97% rename from Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMRsa2048NoDigestSignature.java rename to Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMRsa2048NoDigestSignature.java index 08e11436..303e2b98 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMRsa2048NoDigestSignature.java +++ b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMRsa2048NoDigestSignature.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.javacard.keymaster; +package com.android.javacard.seprovider; import javacard.framework.Util; import javacard.security.CryptoException; @@ -135,6 +135,7 @@ private boolean isValidData(byte[] buf, short start, short len) { } } else { // ALG_RSA_PKCS1_NODIGEST if (len > 245) { + KMException.throwIt(KMError.INVALID_INPUT_LENGTH); return false; } } diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMRsaOAEPEncoding.java b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMRsaOAEPEncoding.java similarity index 99% rename from Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMRsaOAEPEncoding.java rename to Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMRsaOAEPEncoding.java index ac099bc5..2f2da22f 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMRsaOAEPEncoding.java +++ b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMRsaOAEPEncoding.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.javacard.keymaster; +package com.android.javacard.seprovider; import javacard.framework.JCSystem; import javacard.framework.Util; diff --git a/Applet/src/com/android/javacard/keymaster/KMSEProvider.java b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMSEProvider.java similarity index 71% rename from Applet/src/com/android/javacard/keymaster/KMSEProvider.java rename to Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMSEProvider.java index 167aa5b2..0c3051d1 100644 --- a/Applet/src/com/android/javacard/keymaster/KMSEProvider.java +++ b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMSEProvider.java @@ -13,9 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.javacard.keymaster; - -import org.globalplatform.upgrade.Element; +package com.android.javacard.seprovider; /** * KMSEProvider is facade to use SE specific methods. The main intention of this interface is to @@ -223,7 +221,7 @@ boolean aesGCMDecrypt( * This is a oneshot operation that performs key derivation function using cmac kdf (CKDF) as * defined in android keymaster hal definition. * - * @param instance of pre-shared key. + * @param hmacKey of pre-shared key. * @param label is the label to be used for ckdf. * @param labelStart is the start of label. * @param labelLen is the length of the label. @@ -272,7 +270,7 @@ short hmacSign( * This is a oneshot operation that signs the data using hmac algorithm. This is used to derive * the key, which is used to encrypt the keyblob. * - * @param instance of masterkey. + * @param masterkey of masterkey. * @param data is the buffer containing data to be signed. * @param dataStart is the start of the data. * @param dataLength is the length of the data. @@ -347,7 +345,7 @@ short rsaDecipherOAEP256( /** * This is a oneshot operation that signs the data using EC private key. * - * @param instance of KMAttestationKey. + * @param ecPrivKey of KMAttestationKey. * @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. @@ -363,6 +361,121 @@ short ecSign256( byte[] outputDataBuf, short outputDataStart); + /** + * Implementation of HKDF as per RFC5869 https://datatracker.ietf.org/doc/html/rfc5869#section-2 + * + * @param ikm is the buffer containing input key material. + * @param ikmOff is the start of the input key. + * @param ikmLen is the length of the input key. + * @param salt is the buffer containing the salt. + * @param saltOff is the start of the salt buffer. + * @param saltLen is the length of the salt buffer. + * @param info is the buffer containing the application specific information + * @param infoOff is the start of the info buffer. + * @param infoLen is the length of the info buffer. + * @param out is the output buffer. + * @param outOff is the start of the output buffer. + * @param outLen is the length of the expected out buffer. + * @return Length of the out buffer which is outLen. + */ + short hkdf( + byte[] ikm, + short ikmOff, + short ikmLen, + byte[] salt, + short saltOff, + short saltLen, + byte[] info, + short infoOff, + short infoLen, + byte[] out, + short outOff, + short outLen); + + /** + * This function performs ECDH key agreement and generates a secret. + * + * @param privKey is the buffer containing the private key from first party. + * @param privKeyOff is the offset of the private key buffer. + * @param privKeyLen is the length of the private key buffer. + * @param publicKey is the buffer containing the public key from second party. + * @param publicKeyOff is the offset of the public key buffer. + * @param publicKeyLen is the length of the public key buffer. + * @param secret is the output buffer. + * @param secretOff is the offset of the output buffer. + * @return The length of the secret. + */ + short ecdhKeyAgreement( + byte[] privKey, + short privKeyOff, + short privKeyLen, + byte[] publicKey, + short publicKeyOff, + short publicKeyLen, + byte[] secret, + short secretOff); + + /** + * This is a oneshort operation that verifies the data using EC public key + * + * @param pubKey is the public key buffer. + * @param pubKeyOffset is the start of the public key buffer. + * @param pubKeyLen is the length of the public key. + * @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 input data buffer in bytes. + * @param signatureDataBuf is the buffer the signature input data. + * @param signatureDataStart is the start of the signature input data. + * @param signatureDataLen is the length of the signature input data. + * @return true if verification is successful, otherwise false. + */ + boolean ecVerify256( + byte[] pubKey, + short pubKeyOffset, + short pubKeyLen, + byte[] inputDataBuf, + short inputDataStart, + short inputDataLength, + byte[] signatureDataBuf, + short signatureDataStart, + short signatureDataLen); + + /** + * This is a oneshot operation that signs the data using device unique key. + * + * @param ecPrivKey instance of KMECDeviceUniqueKey to sign the input data. + * @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 input data buffer in bytes. + * @param outputDataBuf is the output buffer that contains the signature. + * @param outputDataStart is the start of the output data buffer. + * @return length of the decrypted data. + */ + short ecSign256( + KMDeviceUniqueKey ecPrivKey, + byte[] inputDataBuf, + short inputDataStart, + short inputDataLength, + byte[] outputDataBuf, + short outputDataStart); + + short ecSign256(byte[] secret, short secretStart, short secretLength, + byte[] inputDataBuf, short inputDataStart, short inputDataLength, + byte[] outputDataBuf, short outputDataStart); + + short rsaSign256Pkcs1( + byte[] secret, + short secretStart, + short secretLength, + byte[] modBuf, + short modStart, + short modLength, + byte[] inputDataBuf, + short inputDataStart, + short inputDataLength, + byte[] outputDataBuf, + short outputDataStart); + /** * This creates a persistent operation for signing, verify, encryption and decryption using HMAC, * AES and DES algorithms when keymaster hal's beginOperation function is executed. The @@ -413,6 +526,7 @@ KMOperation initSymmetricOperation( * @param padding is KMType.PADDING_NONE or KMType.RSA_OAEP, KMType.RSA_PKCS1_1_5_ENCRYPT, * KMType.RSA_PKCS1_1_5_SIGN or KMType.RSA_PSS. * @param digest is KMType.DIGEST_NONE or KMType.SHA2_256. + * @param mgfDigest is the MGF digest. * @param privKeyBuf is the private key in case of EC or private key exponent is case of RSA. * @param privKeyStart is the start of the private key. * @param privKeyLength is the length of the private key. @@ -426,6 +540,7 @@ KMOperation initAsymmetricOperation( byte alg, byte padding, byte digest, + byte mgfDigest, byte[] privKeyBuf, short privKeyStart, short privKeyLength, @@ -442,127 +557,147 @@ KMOperation initAsymmetricOperation( * is for ec public key. * @return An empty instance of KMAttestationCert implementation. */ - KMAttestationCert getAttestationCert(boolean rsaCert); + //KMAttestationCert getAttestationCert(boolean rsaCert); /** - * This operation persists the certificate chain in the persistent memory in multiple requests. + * This function tells if applet is upgrading or not. * - * @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. + * @return true if upgrading, otherwise false. */ - void persistPartialCertificateChain(byte[] buf, short offset, short len, short totalLen); + boolean isUpgrading(); /** - * This operation clears the certificate chain from persistent memory. + * This function generates an AES Key of keySizeBits, which is used as an master key. This + * generated key is maintained by the SEProvider. This function should be called only once at the + * time of installation. + * + * @param keySizeBits key size in bits. + * @return An instance of KMMasterKey. */ - void clearCertificateChain(); + KMMasterKey createMasterKey(short keySizeBits); /** - * The operation reads the certificate chain from persistent memory. + * Returns the master key. * - * @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. + * @return Instance of the KMMasterKey */ - short readCertificateChain(byte[] buf, short offset); + KMMasterKey getMasterKey(); /** - * This function returns the cert chain length. - * - * @return length of the certificate chain. + * Returns true if factory provisioned attestation key is supported. */ - short getCertificateChainLength(); + boolean isAttestationKeyProvisioned(); /** - * This function tells if boot signal event is supported or not. - * - * @return true if supported, false otherwise. + * Returns algorithm type of the attestation key. It can be KMType.EC or KMType.RSA if the + * attestation key is provisioned in the factory. */ - boolean isBootSignalEventSupported(); + short getAttestationKeyAlgorithm(); /** - * This function tells if the device is booted or not. + * Returns the preshared key. * - * @return true if device booted, false otherwise. + * @return Instance of the KMPreSharedKey. */ - boolean isDeviceRebooted(); + KMPreSharedKey getPresharedKey(); /** - * This function is supposed to be used to reset the device booted stated after set boot param is - * handled + * Returns the value of the attestation id. * - * @param resetBootFlag is false if event has been handled + * @param tag - attestation id tag key as defined KMType. + * @param buffer - memorey buffer in which value of the id must be copied + * @param start - start offset in the buffer + * @return length - length of the returned attestation id value. */ - void clearDeviceBooted(boolean resetBootFlag); + short getAttestationId(short tag, byte[] buffer, short start); /** - * This function tells if applet is upgrading or not. - * - * @return true if upgrading, otherwise false. + * Delete the attestation ids permanently. */ - boolean isUpgrading(); + void deleteAttestationIds(); /** - * This function generates an AES Key of keySizeBits, which is used as an master key. This - * generated key is maintained by the SEProvider. This function should be called only once at the - * time of installation. - * - * @param keySizeBits key size in bits. - * @return An instance of KMMasterKey. + * Get Verified Boot hash. Part of RoT. Part of data sent by the aosp bootloader. */ - KMMasterKey createMasterKey(short keySizeBits); + short getVerifiedBootHash(byte[] buffer, short start); + + /** + * Get Boot Key. Part of RoT. Part of data sent by the aosp bootloader. + */ + short getBootKey(byte[] buffer, short start); + + /** + * Get Boot state. Part of RoT. Part of data sent by the aosp bootloader. + */ + short getBootState(); + + /** + * Returns true if device bootloader is locked. Part of RoT. Part of data sent by the aosp + * bootloader. + */ + boolean isDeviceBootLocked(); + + /** + * Get Boot patch level. Part of data sent by the aosp bootloader. + */ + short getBootPatchLevel(byte[] buffer, short start); /** - * This function creates an ECKey and initializes the ECPrivateKey with the provided input key - * data. The initialized Key is maintained by the SEProvider. This function should be called only - * while provisioning the attestation key. + * Creates an ECKey instance and sets the public and private keys to it. * - * @param keyData buffer containing the ec private key. - * @param offset start of the buffer. - * @param length length of the buffer. - * @return An instance of KMAttestationKey. + * @param testMode to indicate if current execution is for test or production. + * @param pubKey buffer containing the public key. + * @param pubKeyOff public key buffer start offset. + * @param pubKeyLen public key buffer length. + * @param privKey buffer containing the private key. + * @param privKeyOff private key buffer start offset. + * @param privKeyLen private key buffer length. + * @return instance of KMDeviceUniqueKey. */ - KMAttestationKey createAttestationKey(byte[] keyData, short offset, short length); + KMDeviceUniqueKey createDeviceUniqueKey(boolean testMode, + byte[] pubKey, short pubKeyOff, short pubKeyLen, + byte[] privKey, short privKeyOff, short privKeyLen); /** - * This function creates an HMACKey and initializes the key with the provided input key data. This - * created key is maintained by the SEProvider. This function should be called only while - * provisioing the pre-shared secret. + * Returns the instance KMDeviceUnique if it is created. * - * @param keyData buffer containing the key data. - * @param offset start of the buffer. - * @param length length of the buffer. - * @return An instance of KMPreSharedKey. + * @param testMode Indicates if current execution is for test or production. + * @return instance of KMDeviceUniqueKey if present; null otherwise. */ - KMPreSharedKey createPresharedKey(byte[] keyData, short offset, short length); + KMDeviceUniqueKey getDeviceUniqueKey(boolean testMode); /** - * Returns the master key. + * Persists the additional certificate chain in persistent memory. * - * @return Instance of the KMMasterKey + * @param buf buffer containing the cbor encoded additional certificate chain. + * @param offset start offset of the buffer. + * @param len length of the buffer. */ - KMMasterKey getMasterKey(); + void persistAdditionalCertChain(byte[] buf, short offset, short len); /** - * Returns the attestation key. + * Returns the additional certificate chain length. * - * @return Instance of the KMAttestationKey. + * @return length of the encoded additional certificate chain. */ - KMAttestationKey getAttestationKey(); + short getAdditionalCertChainLength(); /** - * Returns the preshared key. + * Returns the additional certificate chain. * - * @return Instance of the KMPreSharedKey. + * @return additional cert chain. */ - KMPreSharedKey getPresharedKey(); + byte[] getAdditionalCertChain(); + /** - * Releases all the instance back to pool. - * Generally this is used when card is reset. + * Returns the boot certificate chain. + * + * @return boot certificate chain. */ - void releaseAllOperations(); + byte[] getBootCertificateChain(); + + public boolean isProvisionLocked(); + } diff --git a/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMType.java b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMType.java new file mode 100644 index 00000000..d696e332 --- /dev/null +++ b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMType.java @@ -0,0 +1,354 @@ +/* + * 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.seprovider; + +import javacard.framework.ISO7816; +import javacard.framework.ISOException; +import javacard.framework.JCSystem; +import javacard.framework.Util; + +/** + * This class declares all types, tag types, and tag keys. It also establishes basic structure of + * any KMType i.e. struct{byte type, short length, value} where value can any of the KMType. Also, + * KMType refers to transient memory heap in the repository. Finally KMType's subtypes are singleton + * prototype objects which just cast the structure over contiguous memory buffer. + */ +public abstract class KMType { + + public static final short INVALID_VALUE = (short) 0x8000; + protected static final byte TLV_HEADER_SIZE = 3; + + // Types + public static final byte BYTE_BLOB_TYPE = 0x01; + public static final byte INTEGER_TYPE = 0x02; + public static final byte ENUM_TYPE = 0x03; + public static final byte TAG_TYPE = 0x04; + public static final byte ARRAY_TYPE = 0x05; + public static final byte KEY_PARAM_TYPE = 0x06; + public static final byte KEY_CHAR_TYPE = 0x07; + public static final byte HW_AUTH_TOKEN_TYPE = 0x08; + public static final byte VERIFICATION_TOKEN_TYPE = 0x09; + public static final byte HMAC_SHARING_PARAM_TYPE = 0x0A; + public static final byte X509_CERT = 0x0B; + public static final byte NEG_INTEGER_TYPE = 0x0C; + public static final byte TEXT_STRING_TYPE = 0x0D; + public static final byte MAP_TYPE = 0x0E; + public static final byte COSE_KEY_TYPE = 0x0F; + public static final byte COSE_PAIR_TAG_TYPE = 0x10; + public static final byte COSE_PAIR_INT_TAG_TYPE = 0x20; + public static final byte COSE_PAIR_NEG_INT_TAG_TYPE = 0x30; + public static final byte COSE_PAIR_BYTE_BLOB_TAG_TYPE = 0x40; + public static final byte COSE_PAIR_COSE_KEY_TAG_TYPE = 0x60; + public static final byte COSE_PAIR_SIMPLE_VALUE_TAG_TYPE = 0x70; + public static final byte COSE_PAIR_TEXT_STR_TAG_TYPE = (byte) 0x80; + public static final byte SIMPLE_VALUE_TYPE = (byte) 0x90; + public static final byte COSE_HEADERS_TYPE = (byte) 0xA0; + public static final byte COSE_CERT_PAYLOAD_TYPE = (byte) 0xB0; + // Tag Types + public static final short INVALID_TAG = 0x0000; + public static final short ENUM_TAG = 0x1000; + public static final short ENUM_ARRAY_TAG = 0x2000; + public static final short UINT_TAG = 0x3000; + public static final short UINT_ARRAY_TAG = 0x4000; + public static final short ULONG_TAG = 0x5000; + public static final short DATE_TAG = 0x6000; + public static final short BOOL_TAG = 0x7000; + public static final short BIGNUM_TAG = (short) 0x8000; + public static final short BYTES_TAG = (short) 0x9000; + public static final short ULONG_ARRAY_TAG = (short) 0xA000; + public static final short TAG_TYPE_MASK = (short) 0xF000; + + // Enum Tag + // Internal tags + public static final short RULE = 0x7FFF; + public static final byte IGNORE_INVALID_TAGS = 0x00; + public static final byte FAIL_ON_INVALID_TAGS = 0x01; + + // Algorithm Enum Tag key and values + public static final short ALGORITHM = 0x0002; + public static final byte RSA = 0x01; + public static final byte DES = 0x21; + public static final byte EC = 0x03; + public static final byte AES = 0x20; + public static final byte HMAC = (byte) 0x80; + + // EcCurve Enum Tag key and values. + public static final short ECCURVE = 0x000A; + public static final byte P_224 = 0x00; + public static final byte P_256 = 0x01; + public static final byte P_384 = 0x02; + public static final byte P_521 = 0x03; + + // KeyBlobUsageRequirements Enum Tag key and values. + public static final short BLOB_USAGE_REQ = 0x012D; + public static final byte STANDALONE = 0x00; + public static final byte REQUIRES_FILE_SYSTEM = 0x01; + + // HardwareAuthenticatorType Enum Tag key and values. + public static final short USER_AUTH_TYPE = 0x01F8; + public static final byte USER_AUTH_NONE = 0x00; + public static final byte PASSWORD = 0x01; + public static final byte FINGERPRINT = 0x02; + public static final byte BOTH = 0x03; + // have to be power of 2 + public static final byte ANY = (byte) 0xFF; + + // Origin Enum Tag key and values. + public static final short ORIGIN = 0x02BE; + public static final byte GENERATED = 0x00; + public static final byte DERIVED = 0x01; + public static final byte IMPORTED = 0x02; + public static final byte UNKNOWN = 0x03; + public static final byte SECURELY_IMPORTED = 0x04; + + // Hardware Type tag key and values + public static final short HARDWARE_TYPE = 0x0130; + public static final byte SOFTWARE = 0x00; + public static final byte TRUSTED_ENVIRONMENT = 0x01; + public static final byte STRONGBOX = 0x02; + + // No Tag + // Derivation Function - No Tag defined + public static final short KEY_DERIVATION_FUNCTION = (short) 0xF001; + public static final byte DERIVATION_NONE = 0x00; + public static final byte RFC5869_SHA256 = 0x01; + public static final byte ISO18033_2_KDF1_SHA1 = 0x02; + public static final byte ISO18033_2_KDF1_SHA256 = 0x03; + public static final byte ISO18033_2_KDF2_SHA1 = 0x04; + public static final byte ISO18033_2_KDF2_SHA256 = 0x05; + + // KeyFormat - No Tag defined. + public static final short KEY_FORMAT = (short) 0xF002; + public static final byte X509 = 0x00; + public static final byte PKCS8 = 0x01; + public static final byte RAW = 0x03; + + // Verified Boot State + public static final short VERIFIED_BOOT_STATE = (short) 0xF003; + public static final byte VERIFIED_BOOT = 0x00; + public static final byte SELF_SIGNED_BOOT = 0x01; + public static final byte UNVERIFIED_BOOT = 0x02; + public static final byte FAILED_BOOT = 0x03; + + // Verified Boot Key + public static final short VERIFIED_BOOT_KEY = (short) 0xF004; + + // Verified Boot Hash + public static final short VERIFIED_BOOT_HASH = (short) 0xF005; + + // Device Locked + public static final short DEVICE_LOCKED = (short) 0xF006; + public static final byte DEVICE_LOCKED_TRUE = 0x01; + public static final byte DEVICE_LOCKED_FALSE = 0x00; + + // Enum Array Tag + // Purpose + public static final short PURPOSE = 0x0001; + public static final byte ENCRYPT = 0x00; + public static final byte DECRYPT = 0x01; + public static final byte SIGN = 0x02; + public static final byte VERIFY = 0x03; + public static final byte DERIVE_KEY = 0x04; + public static final byte WRAP_KEY = 0x05; + public static final byte AGREE_KEY = 0x06; + public static final byte ATTEST_KEY = (byte) 0x07; + // Block mode + public static final short BLOCK_MODE = 0x0004; + public static final byte ECB = 0x01; + public static final byte CBC = 0x02; + public static final byte CTR = 0x03; + public static final byte GCM = 0x20; + + // Digest + public static final short DIGEST = 0x0005; + public static final byte DIGEST_NONE = 0x00; + public static final byte MD5 = 0x01; + public static final byte SHA1 = 0x02; + public static final byte SHA2_224 = 0x03; + public static final byte SHA2_256 = 0x04; + public static final byte SHA2_384 = 0x05; + public static final byte SHA2_512 = 0x06; + + // Padding mode + public static final short PADDING = 0x0006; + public static final byte PADDING_NONE = 0x01; + public static final byte RSA_OAEP = 0x02; + public static final byte RSA_PSS = 0x03; + public static final byte RSA_PKCS1_1_5_ENCRYPT = 0x04; + public static final byte RSA_PKCS1_1_5_SIGN = 0x05; + public static final byte PKCS7 = 0x40; + + // OAEP MGF Digests - only SHA-1 is supported in Javacard + public static final short RSA_OAEP_MGF_DIGEST = 0xCB; + + // Integer Tag - UINT, ULONG and DATE + // UINT tags + // Keysize + public static final short KEYSIZE = 0x0003; + // Min Mac Length + public static final short MIN_MAC_LENGTH = 0x0008; + // Min Seconds between OPS + public static final short MIN_SEC_BETWEEN_OPS = 0x0193; + // Max Uses per Boot + public static final short MAX_USES_PER_BOOT = 0x0194; + // UserId + public static final short USERID = 0x01F5; + // Auth Timeout + public static final short AUTH_TIMEOUT = 0x01F9; + // OS Version + public static final short OS_VERSION = 0x02C1; + // OS Patch Level + public static final short OS_PATCH_LEVEL = 0x02C2; + // Vendor Patch Level + public static final short VENDOR_PATCH_LEVEL = 0x02CE; + // Boot Patch Level + public static final short BOOT_PATCH_LEVEL = 0x02CF; + // Mac Length + public static final short MAC_LENGTH = 0x03EB; + // Usage Count Limit + public static final short USAGE_COUNT_LIMIT = 0x195; + + // ULONG tags + // RSA Public Exponent + public static final short RSA_PUBLIC_EXPONENT = 0x00C8; + + // DATE tags + public static final short ACTIVE_DATETIME = 0x0190; + public static final short ORIGINATION_EXPIRE_DATETIME = 0x0191; + public static final short USAGE_EXPIRE_DATETIME = 0x0192; + public static final short CREATION_DATETIME = 0x02BD;; + public static final short CERTIFICATE_NOT_BEFORE = 0x03F0; + public static final short CERTIFICATE_NOT_AFTER = 0x03F1; + // Integer Array Tags - ULONG_REP and UINT_REP. + // User Secure Id + public static final short USER_SECURE_ID = (short) 0x01F6; + + // Boolean Tag + // Caller Nonce + public static final short CALLER_NONCE = (short) 0x0007; + // Include Unique Id + public static final short INCLUDE_UNIQUE_ID = (short) 0x00CA; + // Bootloader Only + public static final short BOOTLOADER_ONLY = (short) 0x012E; + // Rollback Resistance + public static final short ROLLBACK_RESISTANCE = (short) 0x012F; + // No Auth Required + public static final short NO_AUTH_REQUIRED = (short) 0x01F7; + // Allow While On Body + public static final short ALLOW_WHILE_ON_BODY = (short) 0x01FA; + // Trusted User Presence Required + public static final short TRUSTED_USER_PRESENCE_REQUIRED = (short) 0x01FB; + // Trusted Confirmation Required + public static final short TRUSTED_CONFIRMATION_REQUIRED = (short) 0x01FC; + // Unlocked Device Required + public static final short UNLOCKED_DEVICE_REQUIRED = (short) 0x01FD; + // Reset Since Id Rotation + public static final short RESET_SINCE_ID_ROTATION = (short) 0x03EC; + //Early boot ended. + public static final short EARLY_BOOT_ONLY = (short) 0x0131; + //Device unique attestation. + public static final short DEVICE_UNIQUE_ATTESTATION = (short) 0x02D0; + + // Byte Tag + // Application Id + public static final short APPLICATION_ID = (short) 0x0259; + // Application Data + public static final short APPLICATION_DATA = (short) 0x02BC; + // Root Of Trust + public static final short ROOT_OF_TRUST = (short) 0x02C0; + // Unique Id + public static final short UNIQUE_ID = (short) 0x02C3; + // Attestation Challenge + public static final short ATTESTATION_CHALLENGE = (short) 0x02C4; + // Attestation Application Id + public static final short ATTESTATION_APPLICATION_ID = (short) 0x02C5; + // Attestation Id Brand + public static final short ATTESTATION_ID_BRAND = (short) 0x02C6; + // Attestation Id Device + public static final short ATTESTATION_ID_DEVICE = (short) 0x02C7; + // Attestation Id Product + public static final short ATTESTATION_ID_PRODUCT = (short) 0x02C8; + // Attestation Id Serial + public static final short ATTESTATION_ID_SERIAL = (short) 0x02C9; + // Attestation Id IMEI + public static final short ATTESTATION_ID_IMEI = (short) 0x02CA; + // Attestation Id MEID + public static final short ATTESTATION_ID_MEID = (short) 0x02CB; + // Attestation Id Manufacturer + public static final short ATTESTATION_ID_MANUFACTURER = (short) 0x02CC; + // Attestation Id Model + public static final short ATTESTATION_ID_MODEL = (short) 0x02CD; + // Associated Data + public static final short ASSOCIATED_DATA = (short) 0x03E8; + // Nonce + public static final short NONCE = (short) 0x03E9; + // Confirmation Token + public static final short CONFIRMATION_TOKEN = (short) 0x03ED; + // Serial Number - this is a big num but in applet we handle it as byte blob + public static final short CERTIFICATE_SERIAL_NUM = (short) 0x03EE; + // Subject Name + public static final short CERTIFICATE_SUBJECT_NAME = (short) 0x03EF; + + public static final short LENGTH_FROM_PDU = (short) 0xFFFF; + + public static final byte NO_VALUE = (byte) 0xff; + // Support Curves for Eek Chain validation. + public static final byte RKP_CURVE_P256 = 1; + // Type offsets. + public static final byte KM_TYPE_BASE_OFFSET = 0; + public static final byte KM_ARRAY_OFFSET = KM_TYPE_BASE_OFFSET; + public static final byte KM_BOOL_TAG_OFFSET = KM_TYPE_BASE_OFFSET + 1; + public static final byte KM_BYTE_BLOB_OFFSET = KM_TYPE_BASE_OFFSET + 2; + public static final byte KM_BYTE_TAG_OFFSET = KM_TYPE_BASE_OFFSET + 3; + public static final byte KM_ENUM_OFFSET = KM_TYPE_BASE_OFFSET + 4; + public static final byte KM_ENUM_ARRAY_TAG_OFFSET = KM_TYPE_BASE_OFFSET + 5; + public static final byte KM_ENUM_TAG_OFFSET = KM_TYPE_BASE_OFFSET + 6; + public static final byte KM_HARDWARE_AUTH_TOKEN_OFFSET = KM_TYPE_BASE_OFFSET + 7; + public static final byte KM_HMAC_SHARING_PARAMETERS_OFFSET = KM_TYPE_BASE_OFFSET + 8; + public static final byte KM_INTEGER_OFFSET = KM_TYPE_BASE_OFFSET + 9; + public static final byte KM_INTEGER_ARRAY_TAG_OFFSET = KM_TYPE_BASE_OFFSET + 10; + public static final byte KM_INTEGER_TAG_OFFSET = KM_TYPE_BASE_OFFSET + 11; + public static final byte KM_KEY_CHARACTERISTICS_OFFSET = KM_TYPE_BASE_OFFSET + 12; + public static final byte KM_KEY_PARAMETERS_OFFSET = KM_TYPE_BASE_OFFSET + 13; + public static final byte KM_VERIFICATION_TOKEN_OFFSET = KM_TYPE_BASE_OFFSET + 14; + public static final byte KM_NEG_INTEGER_OFFSET = KM_TYPE_BASE_OFFSET + 15; + public static final byte KM_TEXT_STRING_OFFSET = KM_TYPE_BASE_OFFSET + 16; + public static final byte KM_MAP_OFFSET = KM_TYPE_BASE_OFFSET + 17; + public static final byte KM_COSE_KEY_OFFSET = KM_TYPE_BASE_OFFSET + 18; + public static final byte KM_COSE_KEY_INT_VAL_OFFSET = KM_TYPE_BASE_OFFSET + 19; + public static final byte KM_COSE_KEY_NINT_VAL_OFFSET = KM_TYPE_BASE_OFFSET + 20; + public static final byte KM_COSE_KEY_BYTE_BLOB_VAL_OFFSET = KM_TYPE_BASE_OFFSET + 21; + public static final byte KM_COSE_KEY_COSE_KEY_VAL_OFFSET = KM_TYPE_BASE_OFFSET + 22; + public static final byte KM_COSE_KEY_SIMPLE_VAL_OFFSET = KM_TYPE_BASE_OFFSET + 23; + public static final byte KM_SIMPLE_VALUE_OFFSET = KM_TYPE_BASE_OFFSET + 24; + public static final byte KM_COSE_HEADERS_OFFSET = KM_TYPE_BASE_OFFSET + 25; + public static final byte KM_COSE_KEY_TXT_STR_VAL_OFFSET = KM_TYPE_BASE_OFFSET + 26; + public static final byte KM_COSE_CERT_PAYLOAD_OFFSET = KM_TYPE_BASE_OFFSET + 27; + public static final byte KM_BIGNUM_TAG_OFFSET = KM_TYPE_BASE_OFFSET + 28; + + // Attestation types + public static final byte NO_CERT = 0; + public static final byte ATTESTATION_CERT = 1; + public static final byte SELF_SIGNED_CERT = 2; + public static final byte FAKE_CERT = 3; + // Buffering Mode + public static final byte BUF_NONE = 0; + public static final byte BUF_RSA_NO_DIGEST = 1; + public static final byte BUF_EC_NO_DIGEST = 2; + public static final byte BUF_BLOCK_ALIGN = 3; + +} diff --git a/Applet/src/com/android/javacard/keymaster/KMUpgradable.java b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMUpgradable.java similarity index 95% rename from Applet/src/com/android/javacard/keymaster/KMUpgradable.java rename to Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMUpgradable.java index 0a241652..9bca1c8c 100644 --- a/Applet/src/com/android/javacard/keymaster/KMUpgradable.java +++ b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMUpgradable.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.javacard.keymaster; +package com.android.javacard.seprovider; import org.globalplatform.upgrade.Element; diff --git a/Applet/JCardSimProvider/build.xml b/Applet/JCardSimProvider/build.xml deleted file mode 100644 index 3d06a8fd..00000000 --- a/Applet/JCardSimProvider/build.xml +++ /dev/null @@ -1,60 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Applet/JCardSimProvider/lib/gpapi-upgrade.jar b/Applet/JCardSimProvider/lib/gpapi-upgrade.jar deleted file mode 100644 index e4814bde..00000000 Binary files a/Applet/JCardSimProvider/lib/gpapi-upgrade.jar and /dev/null differ diff --git a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMAttestationCertImpl.java b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMAttestationCertImpl.java index 1f242f38..b0d633b2 100644 --- a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMAttestationCertImpl.java +++ b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMAttestationCertImpl.java @@ -15,6 +15,12 @@ */ package com.android.javacard.keymaster; +import com.android.javacard.seprovider.KMAESKey; +import com.android.javacard.seprovider.KMAttestationCert; +import com.android.javacard.seprovider.KMException; +import com.android.javacard.seprovider.KMJCardSimulator; +import com.android.javacard.seprovider.KMMasterKey; +import com.android.javacard.seprovider.KMSEProvider; import javacard.framework.JCSystem; import javacard.framework.Util; @@ -46,11 +52,11 @@ public class KMAttestationCertImpl implements KMAttestationCert { private static final byte[] androidExtn = { 0x06, 0x0A, 0X2B, 0X06, 0X01, 0X04, 0X01, (byte) 0XD6, 0X79, 0X02, 0X01, 0X11 }; - + private static final short RSA_SIG_LEN = 256; private static final short ECDSA_MAX_SIG_LEN = 72; - //Signature algorithm identifier - always ecdsaWithSha256 - 1.2.840.10045.4.3.2 + //Signature algorithm identifier - ecdsaWithSha256 - 1.2.840.10045.4.3.2 //SEQUENCE of alg OBJ ID and parameters = NULL. - private static final byte[] X509SignAlgIdentifier = { + private static final byte[] X509EcdsaSignAlgIdentifier = { 0x30, 0x0A, 0x06, @@ -64,6 +70,25 @@ public class KMAttestationCertImpl implements KMAttestationCert { 0x03, 0x02 }; + // Signature algorithm identifier - sha256WithRSAEncryption - 1.2.840.113549.1.1.11 + // SEQUENCE of alg OBJ ID and parameters = NULL. + private static final byte[] X509RsaSignAlgIdentifier = { + 0x30, + 0x0D, + 0x06, + 0x09, + 0x2A, + (byte) 0x86, + 0x48, + (byte) 0x86, + (byte) 0xF7, + 0x0D, + 0x01, + 0x01, + 0x0B, + 0x05, + 0x00 + }; // 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 = { @@ -77,26 +102,29 @@ public class KMAttestationCertImpl implements KMAttestationCert { 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 keyUsageKeyAgreement = (byte) 0x08; // 4th- bit + private static final byte keyUsageCertSign = (byte) 0x04; // 5th- bit - private static final byte KEYMASTER_VERSION = 41; - private static final byte ATTESTATION_VERSION = 4; + private static final byte KEYMASTER_VERSION = 100; + private static final byte ATTESTATION_VERSION = 100; 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 certLength; + private static short tbsStart; 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 stackPtr; + private static short bufStart; + private static short bufLength; + private static KMSEProvider seProvider; + private static short uniqueId; private static short attChallenge; private static short notBefore; + private static short notAfter; private static short pubKey; private static short[] swParams; @@ -112,14 +140,24 @@ public class KMAttestationCertImpl implements KMAttestationCert { private static byte verifiedState; private static short verifiedHash; private static short issuer; + private static short subjectName; private static short signPriv; + private static short serialNum; + + private static byte certMode; + private static short certAttestKeySecret; + private static short certAttestKeyRsaPubModulus; + private static boolean certRsaSign; + private static final byte SERIAL_NUM_MAX_LEN = 20; + private static final byte SUBJECT_NAME_MAX_LEN = 32; private KMAttestationCertImpl() { } - public static KMAttestationCert instance(boolean rsaCert) { + public static KMAttestationCert instance(boolean rsaCert, KMSEProvider provider) { if (inst == null) { inst = new KMAttestationCertImpl(); + seProvider = provider; } init(); KMAttestationCertImpl.rsaCert = rsaCert; @@ -130,9 +168,9 @@ private static void init() { stack = null; stackPtr = 0; certStart = 0; - signatureOffset = 0; - start = 0; - length = 0; + certLength = 0; + bufStart = 0; + bufLength = 0; tbsLength = 0; if (swParams == null) { swParams = JCSystem.makeTransientShortArray((short) MAX_PARAMS, JCSystem.CLEAR_ON_RESET); @@ -156,6 +194,12 @@ private static void init() { rsaCert = true; deviceLocked = 0; signPriv = 0; + certMode = KMType.NO_CERT; + certAttestKeySecret = KMType.INVALID_VALUE; + certRsaSign = true; + issuer = KMType.INVALID_VALUE; + subjectName = KMType.INVALID_VALUE; + serialNum = KMType.INVALID_VALUE; } @Override @@ -182,30 +226,39 @@ private KMAttestationCert uniqueId(short obj) { } @Override - public KMAttestationCert notBefore(short obj, byte[] scratchpad) { - // convert milliseconds to UTC date - notBefore = KMUtils.convertToDate(obj, scratchpad, true); + public KMAttestationCert notBefore(short obj, boolean derEncoded, byte[] scratchpad) { + if(!derEncoded) { + // convert milliseconds to UTC date + notBefore = KMUtils.convertToDate(obj, scratchpad, true); + }else{ + notBefore = KMByteBlob.instance(KMByteBlob.cast(obj).getBuffer(), + KMByteBlob.cast(obj).getStartOff(), KMByteBlob.cast(obj).length()); + } 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); + public KMAttestationCert notAfter(short usageExpiryTimeObj, boolean derEncoded, byte[] scratchPad) { + if(!derEncoded) { + if (usageExpiryTimeObj != KMType.INVALID_VALUE) { + // compare if the expiry time is greater then 2051 then use generalized + // time format else use utc time format. + short 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 { - usageExpiryTimeObj = KMUtils - .convertToDate(usageExpiryTimeObj, scratchPad, true); + //notAfter = certExpirtyTimeObj; } - notAfter = usageExpiryTimeObj; - } else { - notAfter = certExpirtyTimeObj; + }else{ + notAfter = KMByteBlob.instance(KMByteBlob.cast(usageExpiryTimeObj).getBuffer(), + KMByteBlob.cast(usageExpiryTimeObj).getStartOff(), + KMByteBlob.cast(usageExpiryTimeObj).length()); } return this; } @@ -263,6 +316,10 @@ private void createKeyUsage(short tag) { keyUsage = (byte) (keyUsage | keyUsageKeyEncipher); } else if (KMEnumArrayTag.cast(tag).get(index) == KMType.DECRYPT) { keyUsage = (byte) (keyUsage | keyUsageDataEncipher); + } else if (KMEnumArrayTag.cast(tag).get(index) == KMType.AGREE_KEY){ + keyUsage = (byte) (keyUsage | keyUsageKeyAgreement); + }else if (KMEnumArrayTag.cast(tag).get(index) == KMType.ATTEST_KEY){ + keyUsage = (byte) (keyUsage | keyUsageCertSign); } index++; } @@ -273,9 +330,12 @@ private void createKeyUsage(short tag) { } } - private static void pushTbsCert(boolean rsaCert) { +//TODO Serial number, X509Version needa to be passed as parameter + private static void pushTbsCert(boolean rsaCert, boolean rsa) { short last = stackPtr; - pushExtensions(); + if(certMode == KMType.ATTESTATION_CERT) { + pushExtensions(); + } // subject public key info if (rsaCert) { pushRsaSubjectKeyInfo(); @@ -283,7 +343,8 @@ private static void pushTbsCert(boolean rsaCert) { pushEccSubjectKeyInfo(); } // subject - pushBytes(X509Subject, (short) 0, (short) X509Subject.length); + pushBytes(KMByteBlob.cast(subjectName).getBuffer(), KMByteBlob.cast(subjectName).getStartOff(), + KMByteBlob.cast(subjectName).length()); pushValidity(); // issuer - der encoded pushBytes( @@ -291,10 +352,15 @@ private static void pushTbsCert(boolean rsaCert) { KMByteBlob.cast(issuer).getStartOff(), KMByteBlob.cast(issuer).length()); // Algorithm Id - pushAlgorithmId(X509SignAlgIdentifier); + if(rsa) { + pushAlgorithmId(X509RsaSignAlgIdentifier); + }else{ + pushAlgorithmId(X509EcdsaSignAlgIdentifier); + } // Serial Number - pushByte(SERIAL_NUM); - pushIntegerHeader((short) 1); + pushBytes(KMByteBlob.cast(serialNum).getBuffer(), KMByteBlob.cast(serialNum).getStartOff(), + KMByteBlob.cast(serialNum).length()); + pushIntegerHeader(KMByteBlob.cast(serialNum).length()); // Version pushByte(X509_VERSION); pushIntegerHeader((short) 1); @@ -782,17 +848,17 @@ private static void pushBytes(byte[] buf, short start, short len) { private static void decrementStackPtr(short cnt) { stackPtr = (short) (stackPtr - cnt); - if (start > stackPtr) { + if (bufStart > stackPtr) { KMException.throwIt(KMError.UNKNOWN_ERROR); } } @Override - public KMAttestationCert buffer(byte[] buf, short bufStart, short maxLen) { + public KMAttestationCert buffer(byte[] buf, short start, short maxLen) { stack = buf; - start = bufStart; - length = maxLen; - stackPtr = (short) (start + length); + bufStart = start; + bufLength = maxLen; + stackPtr = (short) (bufStart + bufLength); return this; } @@ -801,46 +867,93 @@ public short getCertStart() { return certStart; } - @Override - public short getCertEnd() { - return (short) (start + length - 1); - } - @Override public short getCertLength() { - return (short) (getCertEnd() - getCertStart() + 1); + return certLength; } - @Override - public void build() { + + public void build(short attSecret, short attMod, boolean rsaSign, boolean fakeCert) { + stackPtr = (short)(bufStart + bufLength); short last = stackPtr; - decrementStackPtr((short) ECDSA_MAX_SIG_LEN); - signatureOffset = stackPtr; + short sigLen = 0; + if(fakeCert){ + rsaSign = true; + pushByte((byte)0); + sigLen = 1; + } + // Push placeholder signature Bit string header + // This will potentially change at the end + else if (rsaSign) { + decrementStackPtr(RSA_SIG_LEN); + } else { + decrementStackPtr(ECDSA_MAX_SIG_LEN); + } + short signatureOffset = stackPtr; pushBitStringHeader((byte) 0, (short) (last - stackPtr)); - pushAlgorithmId(X509SignAlgIdentifier); + if (rsaSign) { + pushAlgorithmId(X509RsaSignAlgIdentifier); + } else { + pushAlgorithmId(X509EcdsaSignAlgIdentifier); + } tbsLength = stackPtr; - pushTbsCert(rsaCert); - tbsOffset = stackPtr; - tbsLength = (short) (tbsLength - tbsOffset); + pushTbsCert(rsaCert, rsaSign); + tbsStart = stackPtr; + tbsLength = (short) (tbsLength - tbsStart); + KMJCardSimulator provider = KMJCardSimulator.getInstance(); + if(attSecret != KMType.INVALID_VALUE){ + // Sign with the attestation key + // The pubKey is the modulus. + if (rsaSign) { + sigLen = provider + .rsaSign256Pkcs1( + KMByteBlob.cast(attSecret).getBuffer(), + KMByteBlob.cast(attSecret).getStartOff(), + KMByteBlob.cast(attSecret).length(), + KMByteBlob.cast(attMod).getBuffer(), + KMByteBlob.cast(attMod).getStartOff(), + KMByteBlob.cast(attMod).length(), + stack, + tbsStart, + tbsLength, + stack, + signatureOffset); + if(sigLen > RSA_SIG_LEN) KMException.throwIt(KMError.UNKNOWN_ERROR); + } else { + sigLen = provider + .ecSign256( + KMByteBlob.cast(attSecret).getBuffer(), + KMByteBlob.cast(attSecret).getStartOff(), + KMByteBlob.cast(attSecret).length(), + stack, + tbsStart, + tbsLength, + stack, + signatureOffset); + if (sigLen > ECDSA_MAX_SIG_LEN) KMException.throwIt(KMError.UNKNOWN_ERROR); + } + // Adjust signature length + stackPtr = signatureOffset; + pushBitStringHeader((byte) 0, sigLen); + }else if(!fakeCert){ // no attestation key provisioned in the factory + KMException.throwIt(KMError.ATTESTATION_KEYS_NOT_PROVISIONED); + } + last = (short)(signatureOffset+sigLen); + // Add certificate sequence header + stackPtr = tbsStart; pushSequenceHeader((short) (last - stackPtr)); certStart = stackPtr; - KMJCardSimulator provider = KMJCardSimulator.getInstance(); - short sigLen = provider - .ecSign256( - provider.getAttestationKey(), - 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); + certLength = (short)(last - certStart); + print(stack, getCertStart(), getCertLength()); + } + + + @Override + public void build() { + if(certMode == KMType.FAKE_CERT) { + build(KMType.INVALID_VALUE, KMType.INVALID_VALUE, true, true); + }else { + build(certAttestKeySecret, certAttestKeyRsaPubModulus, certRsaSign, false); } } @@ -887,4 +1000,60 @@ public KMAttestationCert makeUniqueId(byte[] scratchPad, short scratchPadOff, } return uniqueId(timeOffset); } + + @Override + public boolean serialNumber(short number){ + short length = KMByteBlob.cast(number).length(); + if(length > SERIAL_NUM_MAX_LEN){ + return false; + } + byte msb = KMByteBlob.cast(number).get((short)0); + if(msb < 0 && length > (SERIAL_NUM_MAX_LEN -1)){ + return false; + } + serialNum = number; + return true; + } + + @Override + public boolean subjectName(short sub){ + /* + short length = KMByteBlob.cast(sub).length(); + if(length > SUBJECT_NAME_MAX_LEN){ + return false; + } + Util.arrayCopyNonAtomic(KMByteBlob.cast(sub).getBuffer(), KMByteBlob.cast(sub).getStartOff(), + subjectName,(short)0,length); + subjectLen = length; + */ + if(sub == KMType.INVALID_VALUE || KMByteBlob.cast(sub).length() == 0) return false; + subjectName = sub; + return true; + } + + @Override + public KMAttestationCert ecAttestKey(short attestKey, byte mode){ + certMode = mode; + certAttestKeySecret = attestKey; + certAttestKeyRsaPubModulus = KMType.INVALID_VALUE; + certRsaSign = false; + return this; + } + + @Override + public KMAttestationCert rsaAttestKey(short attestPrivExp, short attestMod, byte mode){ + certMode = mode; + certAttestKeySecret = attestPrivExp; + certAttestKeyRsaPubModulus = attestMod; + certRsaSign = true; + return this; + } + + private void print(byte[] buf, short start, short length){ + StringBuilder sb = new StringBuilder(length * 2); + for(short i = start; i < (start+length); i ++){ + sb.append(String.format("%02x", buf[i])); + } + System.out.println( sb.toString()); + } } diff --git a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimApplet.java b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimApplet.java index a97377ea..63c00331 100644 --- a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimApplet.java +++ b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimApplet.java @@ -15,10 +15,52 @@ */ package com.android.javacard.keymaster; +import com.android.javacard.seprovider.KMDeviceUniqueKey; +import com.android.javacard.seprovider.KMException; +import com.android.javacard.seprovider.KMJCardSimulator; +import javacard.framework.APDU; +import javacard.framework.ISO7816; +import javacard.framework.Util; + public class KMJCardSimApplet extends KMKeymasterApplet { + // Provider specific Commands + private static final byte INS_KEYMINT_PROVIDER_APDU_START = 0x00; + private static final byte INS_PROVISION_ATTEST_IDS_CMD = INS_KEYMINT_PROVIDER_APDU_START + 1; + private static final byte INS_PROVISION_PRESHARED_SECRET_CMD = + INS_KEYMINT_PROVIDER_APDU_START + 2; + private static final byte INS_LOCK_PROVISIONING_CMD = INS_KEYMINT_PROVIDER_APDU_START + 3; + private static final byte INS_GET_PROVISION_STATUS_CMD = INS_KEYMINT_PROVIDER_APDU_START + 4; + private static final byte INS_SET_BOOT_PARAMS_CMD = INS_KEYMINT_PROVIDER_APDU_START + 5; + private static final byte INS_PROVISION_DEVICE_UNIQUE_KEY_CMD = + INS_KEYMINT_PROVIDER_APDU_START + 6; + private static final byte INS_PROVISION_ADDITIONAL_CERT_CHAIN_CMD = + INS_KEYMINT_PROVIDER_APDU_START + 7; + private static final byte INS_KEYMINT_PROVIDER_APDU_END = 0x1F; + + //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_PRESHARED_SECRET = 0x10; + private static final byte PROVISION_STATUS_BOOT_PARAM = 0x20; + private static final byte PROVISION_STATUS_PROVISIONING_LOCKED = 0x40; + private static final byte PROVISION_STATUS_DEVICE_UNIQUE_KEY = 0x60; + private static final byte PROVISION_STATUS_ADDITIONAL_CERT_CHAIN = (byte) 0x80; + private static final short POWER_RESET_MASK_FLAG = (short) 0x4000; + public static final short SHARED_SECRET_KEY_SIZE = 32; + public static final byte BOOT_KEY_MAX_SIZE = 32; + public static final byte BOOT_HASH_MAX_SIZE = 32; + + private static byte provisionStatus = NOT_PROVISIONED; + KMJCardSimApplet() { super(new KMJCardSimulator()); + // setDummyBootParams(); + // setDummyPresharedKey(); + // setDummyAttestationIds(); } /** @@ -32,4 +74,372 @@ public static void install(byte[] bArray, short bOffset, byte bLength) { new KMJCardSimApplet().register(); } + @Override + public void process(APDU apdu) { + try { + // If this is select applet apdu which is selecting this applet then return + if (apdu.isISOInterindustryCLA()) { + if (selectingApplet()) { + return; + } + } + short apduIns = validateApdu(apdu); + if (((KMJCardSimulator) seProvider).isPowerReset()) { + super.powerReset(); + } + if (((KMJCardSimulator) seProvider).isProvisionLocked()) { + switch (apduIns) { + case INS_SET_BOOT_PARAMS_CMD: + processSetBootParamsCmd(apdu); + break; + default: + super.process(apdu); + break; + } + return; + } + if (apduIns == KMType.INVALID_VALUE) { + return; + } + switch (apduIns) { + case INS_PROVISION_ATTEST_IDS_CMD: + processProvisionAttestIdsCmd(apdu); + provisionStatus |= PROVISION_STATUS_ATTEST_IDS; + sendError(apdu, KMError.OK); + break; + case INS_PROVISION_PRESHARED_SECRET_CMD: + processProvisionPreSharedSecretCmd(apdu); + provisionStatus |= PROVISION_STATUS_PRESHARED_SECRET; + sendError(apdu, KMError.OK); + break; + case INS_GET_PROVISION_STATUS_CMD: + processGetProvisionStatusCmd(apdu); + break; + case INS_LOCK_PROVISIONING_CMD: + processLockProvisioningCmd(apdu); + break; + case INS_SET_BOOT_PARAMS_CMD: + processSetBootParamsCmd(apdu); + provisionStatus |= PROVISION_STATUS_BOOT_PARAM; + break; + case INS_PROVISION_DEVICE_UNIQUE_KEY_CMD: + processProvisionDeviceUniqueKey(apdu); + provisionStatus |= PROVISION_STATUS_DEVICE_UNIQUE_KEY; + break; + case INS_PROVISION_ADDITIONAL_CERT_CHAIN_CMD: + processProvisionAdditionalCertChain(apdu); + provisionStatus |= PROVISION_STATUS_ADDITIONAL_CERT_CHAIN; + break; + default: + super.process(apdu); + break; + } + } finally { + repository.clean(); + } + } + + private void processProvisionAttestIdsCmd(APDU apdu) { + + short keyparams = KMKeyParameters.exp(); + short cmd = KMArray.instance((short) 1); + KMArray.cast(cmd).add((short) 0, keyparams); + short args = receiveIncoming(apdu, cmd); + short attData = KMArray.cast(args).get((short) 0); + // persist attestation Ids - if any is missing then exception occurs + setAttestationIds(attData); + } + + public void setAttestationIds(short attIdVals) { + KMKeyParameters instParam = KMKeyParameters.cast(attIdVals); + KMArray vals = KMArray.cast(instParam.getVals()); + short index = 0; + short length = vals.length(); + short key; + short type; + short obj; + while (index < length) { + obj = vals.get(index); + key = KMTag.getKey(obj); + type = KMTag.getTagType(obj); + + if (KMType.BYTES_TAG != type) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + obj = KMByteTag.cast(obj).getValue(); + ((KMJCardSimulator) seProvider).setAttestationId(key, KMByteBlob.cast(obj).getBuffer(), + KMByteBlob.cast(obj).getStartOff(), KMByteBlob.cast(obj).length()); + index++; + } + } + + private void processProvisionPreSharedSecretCmd(APDU apdu) { + + short blob = KMByteBlob.exp(); + short argsProto = KMArray.instance((short) 1); + KMArray.cast(argsProto).add((short) 0, blob); + short args = receiveIncoming(apdu, argsProto); + + short val = KMArray.cast(args).get((short) 0); + + if (val != KMType.INVALID_VALUE + && KMByteBlob.cast(val).length() != SHARED_SECRET_KEY_SIZE) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + // Persist shared Hmac. + ((KMJCardSimulator) seProvider).createPresharedKey( + KMByteBlob.cast(val).getBuffer(), + KMByteBlob.cast(val).getStartOff(), + KMByteBlob.cast(val).length()); + } + + private void processGetProvisionStatusCmd(APDU apdu) { + short resp = KMArray.instance((short) 2); + KMArray.cast(resp).add((short) 0, buildErrorStatus(KMError.OK)); + KMArray.cast(resp).add((short) 1, KMInteger.uint_16(provisionStatus)); + sendOutgoing(apdu, resp); + + } + + private void processSetBootParamsCmd(APDU apdu) { + short argsProto = KMArray.instance((short) 5); + // Array of 4 expected arguments + // Argument 0 Boot Patch level + KMArray.cast(argsProto).add((short) 0, KMInteger.exp()); + // Argument 1 Verified Boot Key + KMArray.cast(argsProto).add((short) 1, KMByteBlob.exp()); + // Argument 2 Verified Boot Hash + KMArray.cast(argsProto).add((short) 2, KMByteBlob.exp()); + // Argument 3 Verified Boot State + KMArray.cast(argsProto).add((short) 3, KMEnum.instance(KMType.VERIFIED_BOOT_STATE)); + // Argument 4 Device Locked + KMArray.cast(argsProto).add((short) 4, KMEnum.instance(KMType.DEVICE_LOCKED)); + + short args = receiveIncoming(apdu, argsProto); + short bootParam = KMArray.cast(args).get((short) 0); + + ((KMJCardSimulator) seProvider).setBootPatchLevel(KMInteger.cast(bootParam).getBuffer(), + KMInteger.cast(bootParam).getStartOff(), + KMInteger.cast(bootParam).length()); + + bootParam = KMArray.cast(args).get((short) 1); + if (KMByteBlob.cast(bootParam).length() > BOOT_KEY_MAX_SIZE) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + ((KMJCardSimulator) seProvider).setBootKey(KMByteBlob.cast(bootParam).getBuffer(), + KMByteBlob.cast(bootParam).getStartOff(), + KMByteBlob.cast(bootParam).length()); + + bootParam = KMArray.cast(args).get((short) 2); + if (KMByteBlob.cast(bootParam).length() > BOOT_HASH_MAX_SIZE) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + ((KMJCardSimulator) seProvider).setVerifiedBootHash(KMByteBlob.cast(bootParam).getBuffer(), + KMByteBlob.cast(bootParam).getStartOff(), + KMByteBlob.cast(bootParam).length()); + + bootParam = KMArray.cast(args).get((short) 3); + byte enumVal = KMEnum.cast(bootParam).getVal(); + ((KMJCardSimulator) seProvider).setBootState(enumVal); + + bootParam = KMArray.cast(args).get((short) 4); + enumVal = KMEnum.cast(bootParam).getVal(); + ((KMJCardSimulator) seProvider).setDeviceLocked(enumVal == KMType.DEVICE_LOCKED_TRUE); + + super.reboot(); + sendError(apdu, KMError.OK); + } + + private void processLockProvisioningCmd(APDU apdu) { + ((KMJCardSimulator) seProvider).setProvisionLocked(true); + sendError(apdu, KMError.OK); + } + + private short validateApdu(APDU apdu) { + // Read the apdu header and buffer. + byte[] apduBuffer = apdu.getBuffer(); + byte apduClass = apduBuffer[ISO7816.OFFSET_CLA]; + short P1P2 = Util.getShort(apduBuffer, ISO7816.OFFSET_P1); + + // Validate APDU Header. + if ((apduClass != CLA_ISO7816_NO_SM_NO_CHAN)) { + sendError(apdu, KMError.UNSUPPORTED_CLA); + return KMType.INVALID_VALUE; + } + + // Validate P1P2. + if (P1P2 != KMKeymasterApplet.KM_HAL_VERSION) { + sendError(apdu, KMError.INVALID_P1P2); + return KMType.INVALID_VALUE; + } + return apduBuffer[ISO7816.OFFSET_INS]; + } + + private void setDummyBootParams() { + short osVersion = KMInteger.uint_16(((short) 0)); + short osPatchLevel = KMInteger.uint_16((short) 0); + short vendorPatchLevel = KMInteger.uint_16((short) 0); + short bootPatchLevel = KMInteger.uint_16((short) 0); + + super.setOsVersion(osVersion); + super.setOsPatchLevel(osPatchLevel); + super.setVendorPatchLevel(vendorPatchLevel); + + byte[] bootBlob = new byte[32]; + short bootKey = KMByteBlob.instance(bootBlob, (short) 0, + (short) bootBlob.length); + short verifiedHash = KMByteBlob.instance(bootBlob, (short) 0, + (short) bootBlob.length); + short bootState = KMType.UNVERIFIED_BOOT; + + ((KMJCardSimulator) seProvider).setBootPatchLevel( + KMInteger.cast(bootPatchLevel).getBuffer(), + KMInteger.cast(bootPatchLevel).getStartOff(), + KMInteger.cast(bootPatchLevel).length()); + + ((KMJCardSimulator) seProvider).setBootKey( + KMByteBlob.cast(bootKey).getBuffer(), + KMByteBlob.cast(bootKey).getStartOff(), + KMByteBlob.cast(bootKey).length()); + + ((KMJCardSimulator) seProvider).setVerifiedBootHash( + KMByteBlob.cast(verifiedHash).getBuffer(), + KMByteBlob.cast(verifiedHash).getStartOff(), + KMByteBlob.cast(verifiedHash).length()); + + ((KMJCardSimulator) seProvider).setBootState((byte) bootState); + ((KMJCardSimulator) seProvider).setDeviceLocked(true); + super.reboot(); + } + + private void setDummyPresharedKey() { + final byte[] presharedKey = {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}; + ((KMJCardSimulator) seProvider) + .createPresharedKey(presharedKey, (short) 0, (short) presharedKey.length); + } + + private void setDummyAttestationIds() { + final byte[] brand = {'g', 'e', 'n', 'e', 'r', 'i', 'c'}; + final byte[] device = {'v', 's', 'o', 'c', '_', 'x', '8', '6', '_', '6', '4'};//vsoc_x86_64 + final byte[] product = //aosp_cf_x86_64_phone + {'a', 'o', 's', 'p', '_', 'c', 'f', '_', 'x', '8', '6', '_', '6', '4', '_', 'p', 'h', 'o', + 'n', 'e'}; + final byte[] serial = {}; + final byte[] imei = {'0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0'}; + final byte[] meid = {'0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0'}; + final byte[] manufacturer = {'G', 'o', 'o', 'g', 'l', 'e'}; + final byte[] model = //"Cuttlefish x86_64 phone" + {'C', 'u', 't', 't', 'l', 'e', 'f', 'i', 's', 'h', ' ', 'x', '8', '6', '_', '6', '4', + ' ', 'p', 'h', 'o', 'n', 'e'}; + ((KMJCardSimulator) seProvider) + .setAttestationId(KMType.ATTESTATION_ID_BRAND, brand, (short) 0, (short) brand.length); + ((KMJCardSimulator) seProvider) + .setAttestationId(KMType.ATTESTATION_ID_IMEI, imei, (short) 0, (short) imei.length); + ((KMJCardSimulator) seProvider) + .setAttestationId(KMType.ATTESTATION_ID_DEVICE, device, (short) 0, (short) device.length); + ((KMJCardSimulator) seProvider) + .setAttestationId(KMType.ATTESTATION_ID_MEID, meid, (short) 0, (short) meid.length); + ((KMJCardSimulator) seProvider) + .setAttestationId(KMType.ATTESTATION_ID_MODEL, model, (short) 0, (short) model.length); + ((KMJCardSimulator) seProvider) + .setAttestationId(KMType.ATTESTATION_ID_MANUFACTURER, manufacturer, (short) 0, + (short) manufacturer.length); + ((KMJCardSimulator) seProvider) + .setAttestationId(KMType.ATTESTATION_ID_PRODUCT, product, (short) 0, + (short) product.length); + ((KMJCardSimulator) seProvider) + .setAttestationId(KMType.ATTESTATION_ID_SERIAL, serial, (short) 0, (short) serial.length); + } + + private static void processProvisionDeviceUniqueKey(APDU apdu) { + // Re-purpose the apdu buffer as scratch pad. + byte[] scratchPad = apdu.getBuffer(); + short arr = KMArray.instance((short) 1); + short coseKeyExp = KMCoseKey.exp(); + KMArray.cast(arr).add((short) 0, coseKeyExp); //[ CoseKey ] + arr = receiveIncoming(apdu, arr); + // Get cose key. + short coseKey = KMArray.cast(arr).get((short) 0); + short pubKeyLen = KMCoseKey.cast(coseKey).getEcdsa256PublicKey(scratchPad, (short) 0); + short privKeyLen = KMCoseKey.cast(coseKey).getPrivateKey(scratchPad, pubKeyLen); + //Store the Device unique Key. + seProvider.createDeviceUniqueKey(false, scratchPad, (short) 0, pubKeyLen, scratchPad, + pubKeyLen, privKeyLen); + // Newly added code 30/07/2021 + short bcc = generateBcc(false, scratchPad); + short len = KMKeymasterApplet.encodeToApduBuffer(bcc, scratchPad, (short) 0, + MAX_COSE_BUF_SIZE); + ((KMJCardSimulator) seProvider).persistBootCertificateChain(scratchPad, (short) 0, len); + sendError(apdu, KMError.OK); + } + + private static void processProvisionAdditionalCertChain(APDU apdu) { + // Prepare the expression to decode + short headers = KMCoseHeaders.exp(); + short arrInst = KMArray.instance((short) 4); + KMArray.cast(arrInst).add((short) 0, KMByteBlob.exp()); + KMArray.cast(arrInst).add((short) 1, headers); + KMArray.cast(arrInst).add((short) 2, KMByteBlob.exp()); + KMArray.cast(arrInst).add((short) 3, KMByteBlob.exp()); + short coseSignArr = KMArray.exp(arrInst); + short map = KMMap.instance((short) 1); + KMMap.cast(map).add((short) 0, KMTextString.exp(), coseSignArr); + // TODO duplicate code. + // receive incoming data and decode it. + byte[] srcBuffer = apdu.getBuffer(); + short recvLen = apdu.setIncomingAndReceive(); + short srcOffset = apdu.getOffsetCdata(); + short bufferLength = apdu.getIncomingLength(); + short bufferStartOffset = repository.allocReclaimableMemory(bufferLength); + short index = bufferStartOffset; + byte[] buffer = repository.getHeap(); + while (recvLen > 0 && ((short) (index - bufferStartOffset) < bufferLength)) { + Util.arrayCopyNonAtomic(srcBuffer, srcOffset, buffer, index, recvLen); + index += recvLen; + recvLen = apdu.receiveBytes(srcOffset); + } + // decode + map = decoder.decode(map, buffer, bufferStartOffset, bufferLength); + arrInst = KMMap.cast(map).getKeyValue((short) 0); + // Validate Additional certificate chain. + short leafCoseKey = + validateCertChain(false, KMCose.COSE_ALG_ES256, KMCose.COSE_ALG_ES256, arrInst, + srcBuffer, null); + // Compare the DK_Pub. + short pubKeyLen = KMCoseKey.cast(leafCoseKey).getEcdsa256PublicKey(srcBuffer, (short) 0); + KMDeviceUniqueKey uniqueKey = seProvider.getDeviceUniqueKey(false); + if (uniqueKey == null) { + KMException.throwIt(KMError.STATUS_FAILED); + } + short uniqueKeyLen = uniqueKey.getPublicKey(srcBuffer, pubKeyLen); + if ((pubKeyLen != uniqueKeyLen) || + (0 != Util.arrayCompare(srcBuffer, (short) 0, srcBuffer, pubKeyLen, pubKeyLen))) { + KMException.throwIt(KMError.STATUS_FAILED); + } + seProvider.persistAdditionalCertChain(buffer, bufferStartOffset, bufferLength); + //reclaim memory + repository.reclaimMemory(bufferLength); + sendError(apdu, KMError.OK); + } + + private static short buildErrorStatus(short err) { + short int32Ptr = KMInteger.instance((short) 4); + short powerResetStatus = 0; + if (((KMJCardSimulator) seProvider).isPowerReset()) { + powerResetStatus = POWER_RESET_MASK_FLAG; + } + + Util.setShort(KMInteger.cast(int32Ptr).getBuffer(), + KMInteger.cast(int32Ptr).getStartOff(), + powerResetStatus); + + Util.setShort(KMInteger.cast(int32Ptr).getBuffer(), + (short) (KMInteger.cast(int32Ptr).getStartOff() + 2), + err); + // reset power reset status flag to its default value. + //repository.restorePowerResetStatus(); //TODO + return int32Ptr; + } + } diff --git a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMUtils.java b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMUtils.java index c2b5c7f3..06c77ff6 100644 --- a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMUtils.java +++ b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMUtils.java @@ -15,6 +15,7 @@ */ package com.android.javacard.keymaster; +import com.android.javacard.seprovider.KMException; import javacard.framework.Util; public class KMUtils { @@ -85,14 +86,14 @@ public static short convertToDate(short time, byte[] scratchPad, (short) 8) < 0) { Util.arrayCopyNonAtomic(firstJan2020, (short) 0, scratchPad, (short) 8, (short) 8); - subtract(scratchPad, (short) 0, (short) 8, (short) 16); + subtract(scratchPad, (short) 0, (short) 8, (short) 16, (byte) 8); 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); + subtract(scratchPad, (short) 0, (short) 8, (short) 16, (byte) 8); Util.arrayCopyNonAtomic(scratchPad, (short) 16, scratchPad, (short) 0, (short) 8); } @@ -133,7 +134,7 @@ public static short convertToDate(short time, byte[] scratchPad, Util.arrayCopyNonAtomic(yearMsec, (short) 0, scratchPad, (short) 8, (short) 8); } - subtract(scratchPad, (short) 0, (short) 8, (short) 16); + subtract(scratchPad, (short) 0, (short) 8, (short) 16, (byte) 8); Util.arrayCopyNonAtomic(scratchPad, (short) 16, scratchPad, (short) 0, (short) 8); if (((short) (i + 1) == leapYrIdx)) { @@ -183,7 +184,7 @@ public static short convertToDate(short time, byte[] scratchPad, if (KMInteger.unsignedByteArrayCompare(scratchPad, (short) 0, scratchPad, (short) 8, (short) 8) >= 0) { - subtract(scratchPad, (short) 0, (short) 8, (short) 16); + subtract(scratchPad, (short) 0, (short) 8, (short) 16, (byte) 8); Util.arrayCopyNonAtomic(scratchPad, (short) 16, scratchPad, (short) 0, (short) 8); } else { @@ -285,7 +286,7 @@ public static short divide(byte[] buf, short dividend, short divisor, // Copy remainder in the dividend and repeat. while (expCnt != 0) { if (compare(buf, dividend, divisor) >= 0) { - subtract(buf, dividend, divisor, remainder); + subtract(buf, dividend, divisor, remainder, (byte) 8); copy(buf, remainder, dividend); q = (short) (q + expCnt); } @@ -354,9 +355,9 @@ public static void add(byte[] buf, short op1, short op2, short result) { } // subtraction by borrowing. - public static void subtract(byte[] buf, short op1, short op2, short result) { + public static void subtract(byte[] buf, short op1, short op2, short result, byte sizeBytes) { byte borrow = 0; - byte index = 7; + byte index = (byte) (sizeBytes - 1); short r; short x; short y; @@ -409,4 +410,12 @@ public static short getLeapYrIndex(boolean from2020, short yrsCount) { return -1; } -} \ No newline at end of file + public static void computeOnesCompliment(byte[] buf, short offset, short len) { + short index = offset; + // Compute 1s compliment + while (index < (short) (len + offset)) { + buf[index] = (byte) ~buf[index]; + index++; + } + } +} diff --git a/Applet/JCardSimProvider/test/com/android/javacard/test/KMFunctionalTest.java b/Applet/JCardSimProvider/test/com/android/javacard/test/KMFunctionalTest.java deleted file mode 100644 index 1e80f4b2..00000000 --- a/Applet/JCardSimProvider/test/com/android/javacard/test/KMFunctionalTest.java +++ /dev/null @@ -1,3615 +0,0 @@ -/* - * Copyright(C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.javacard.test; - -import com.android.javacard.keymaster.KMArray; -import com.android.javacard.keymaster.KMBoolTag; -import com.android.javacard.keymaster.KMByteBlob; -import com.android.javacard.keymaster.KMByteTag; -import com.android.javacard.keymaster.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 javacard.security.ECPublicKey; -import javacard.security.KeyBuilder; -import javacard.security.KeyPair; -import javacard.security.RSAPublicKey; -import javacard.security.Signature; -import javacardx.crypto.Cipher; - -import java.math.BigInteger; -import java.security.AlgorithmParameters; -import java.security.InvalidAlgorithmParameterException; -import java.security.InvalidKeyException; -import java.security.KeyFactory; -import java.security.NoSuchAlgorithmException; -import java.security.NoSuchProviderException; -import java.security.SignatureException; -import java.security.spec.ECGenParameterSpec; -import java.security.spec.ECParameterSpec; -import java.security.spec.ECPoint; -import java.security.spec.ECPublicKeySpec; -import java.security.spec.InvalidKeySpecException; -import java.security.spec.InvalidParameterSpecException; -import java.security.spec.MGF1ParameterSpec; -import java.security.spec.RSAPublicKeySpec; -import java.util.Arrays; -import java.util.Random; - -import javax.crypto.BadPaddingException; -import javax.crypto.IllegalBlockSizeException; -import javax.crypto.NoSuchPaddingException; -import javax.crypto.spec.OAEPParameterSpec; -import javax.crypto.spec.PSource; -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_PRESHARED_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 - private static final byte INS_SET_VERSION_PATCHLEVEL_CMD = INS_BEGIN_KM_CMD + 9; //0x09 - // 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 static final int OS_VERSION = 1; - private static final int OS_PATCH_LEVEL = 1; - private static final int VENDOR_PATCH_LEVEL = 1; - private static final int BOOT_PATCH_LEVEL = 1; - private static final short MAJOR_TYPE_MASK = 0xE0; - private static final byte CBOR_ARRAY_MAJOR_TYPE = (byte) 0x80; - private static final byte CBOR_UINT_MAJOR_TYPE = 0x00; - private static final short SE_POWER_RESET_FLAG = (short) 0x4000; - private static final boolean RESET = true; - private static final boolean NO_RESET = false; - - 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 setAndroidOSSystemProperties(CardSimulator simulator, short osVersion, - short osPatchLevel, short vendorPatchLevel) { - // 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); - // Arguments - short arrPtr = KMArray.instance((short) 3); - KMArray vals = KMArray.cast(arrPtr); - vals.add((short) 0, versionPtr); - vals.add((short) 1, patchPtr); - vals.add((short) 2, vendorpatchPtr); - CommandAPDU apdu = encodeApdu((byte) INS_SET_VERSION_PATCHLEVEL_CMD, arrPtr); - // print(commandAPDU.getBytes()); - ResponseAPDU response = simulator.transmitCommand(apdu); - Assert.assertEquals(0x9000, response.getSW()); - - } - - private void setBootParams(CardSimulator simulator, short bootPatchLevel) { - // Argument 0 boot patch level - short bootpatchPtr = KMInteger.uint_16((short) bootPatchLevel); - // Argument 1 Verified Boot Key - byte[] bootKeyHash = "00011122233344455566677788899900".getBytes(); - short bootKeyPtr = KMByteBlob.instance(bootKeyHash, (short) 0, - (short) bootKeyHash.length); - // Argument 2 Verified Boot Hash - short bootHashPtr = KMByteBlob.instance(bootKeyHash, (short) 0, - (short) bootKeyHash.length); - // Argument 3 Verified Boot State - short bootStatePtr = KMEnum.instance(KMType.VERIFIED_BOOT_STATE, - KMType.VERIFIED_BOOT); - // Argument 4 Device Locked - short deviceLockedPtr = KMEnum.instance(KMType.DEVICE_LOCKED, - KMType.DEVICE_LOCKED_FALSE); - // Arguments - short arrPtr = KMArray.instance((short) 5); - KMArray vals = KMArray.cast(arrPtr); - vals.add((short) 0, bootpatchPtr); - vals.add((short) 1, bootKeyPtr); - vals.add((short) 2, bootHashPtr); - vals.add((short) 3, bootStatePtr); - vals.add((short) 4, 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) 2); - 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); - - 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_PRESHARED_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) BOOT_PATCH_LEVEL); - // set android system properties - setAndroidOSSystemProperties(simulator, (short) OS_VERSION, (short) OS_PATCH_LEVEL, - (short) VENDOR_PATCH_LEVEL); - provisionLocked(simulator); - } - - private void cleanUp() { - AID appletAID = AIDUtil.create("A000000062"); - // Delete i.e. uninstall applet - simulator.deleteApplet(appletAID); - } - - private void resetAndSelect() { - simulator.reset(); - AID appletAID = AIDUtil.create("A000000062"); - // Select applet - simulator.selectApplet(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(); - 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, false); - 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(); - 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(byte[] buf, short off, short buflen) { - 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, - buf, off, buflen); - 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; - } - - private short extractKeyBlobArray(short keyBlob) { - return extractKeyBlobArray(KMByteBlob.cast(keyBlob).getBuffer(), KMByteBlob - .cast(keyBlob).getStartOff(), KMByteBlob.cast(keyBlob).length()); - } - - @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()); - - 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]; - byte[] transportKeyMaterial = new byte[32]; - cryptoProvider.newRandomNumber(transportKeyMaterial, (short) 0, (short) 32); - byte[] nonce = new byte[12]; - cryptoProvider.newRandomNumber(nonce, (short) 0, (short) 12); - byte[] authData = "Auth Data".getBytes(); - byte[] authTag = new byte[16]; - 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) 16); - 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); - - byte[] output = new byte[256]; - short outlen = rsaOaepEncryptMessage(wrappingKeyBlob, KMType.SHA2_256, - maskedTransportKey, (short) 0, (short) maskedTransportKey.length, - output, (short) 0); - Assert.assertTrue((outlen == 256)); - byte[] encTransportKey = new byte[outlen]; - Util.arrayCopyNonAtomic(output, (short) 0, encTransportKey, (short) 0, - outlen); - //Clean the heap. - KMRepository.instance().clean(); - 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); - 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(); - 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); - 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); - 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, boolean triggerReset) { - 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()); - if (triggerReset) { - resetAndSelect(); - } - ResponseAPDU response = simulator.transmitCommand(apdu); - byte[] respBuf = response.getBytes(); - short ret = decoder.decode(KMInteger.exp(), respBuf, (short) 0, (short) respBuf.length); - if (triggerReset) { - short error = KMInteger.cast(ret).getSignificantShort(); - Assert.assertEquals(error, SE_POWER_RESET_FLAG); - } - return ret; - } - - 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(); - } - - public short getPublicKey(byte[] keyBlob, short off, short len, - byte[] pubKey, short pubKeyOff) { - short keyBlobPtr = extractKeyBlobArray(keyBlob, off, len); - short arrayLen = KMArray.cast(keyBlobPtr).length(); - if (arrayLen < 5) { - return 0; - } - short pubKeyPtr = KMArray.cast(keyBlobPtr).get( - KMKeymasterApplet.KEY_BLOB_PUB_KEY); - Util.arrayCopy(KMByteBlob.cast(pubKeyPtr).getBuffer(), - KMByteBlob.cast(pubKeyPtr).getStartOff(), pubKey, pubKeyOff, - KMByteBlob.cast(pubKeyPtr).length()); - return KMByteBlob.cast(pubKeyPtr).length(); - } - - private String toHexString(byte[] num) { - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < num.length; i++) { - sb.append(String.format("%02X", num[i])); - } - return sb.toString(); - } - - public short rsaEncryptMessage(byte[] keyBlob, short padding, short digest, byte[] input, - short inputOff, short inputlen, - byte[] output, short outputOff) { - byte alg = Cipher.ALG_RSA_PKCS1; - byte[] tmp = null; - short inLen = inputlen; - if (padding == KMType.PADDING_NONE) { - alg = Cipher.ALG_RSA_NOPAD; - // Length cannot be greater then key size according to JcardSim - if (inLen >= 256) { - return 0; - } - // make input equal to 255 bytes - tmp = new byte[255]; - Util.arrayFillNonAtomic(tmp, (short) 0, (short) 255, (byte) 0); - Util.arrayCopyNonAtomic( - input, - inputOff, - tmp, (short) (255 - inLen), inLen); - inLen = 255; - inputOff = 0; - } else if (padding == KMType.RSA_PKCS1_1_5_ENCRYPT) { - tmp = input; - } else { - /*Fail */ - Assert.assertTrue(false); - } - byte[] pubKey = new byte[256]; - KeyPair rsaKeyPair = new KeyPair(KeyPair.ALG_RSA, KeyBuilder.LENGTH_RSA_2048); - RSAPublicKey rsaPubKey = (RSAPublicKey) rsaKeyPair.getPublic(); - if (0 == getPublicKey(keyBlob, (short) 0, (short) keyBlob.length, pubKey, (short) 0)) { - return 0; - } - - byte[] exponent = new byte[]{0x01, 0x00, 0x01}; - rsaPubKey.setModulus(pubKey, (short) 0, (short) pubKey.length); - rsaPubKey.setExponent(exponent, (short) 0, (short) exponent.length); - - Cipher rsaCipher = Cipher.getInstance(alg, false); - rsaCipher.init(rsaPubKey, Cipher.MODE_ENCRYPT); - return rsaCipher.doFinal(tmp, inputOff, inLen, output, outputOff); - } - - public short rsaOaepEncryptMessage(byte[] keyBlob, short digest, byte[] input, short inputOff, - short inputlen, - byte[] output, short outputOff) { - byte[] mod = new byte[256]; - if (0 == getPublicKey(keyBlob, (short) 0, (short) keyBlob.length, mod, (short) 0)) { - return 0; - } - byte[] exponent = new byte[]{0x01, 0x00, 0x01}; - - // Convert byte arrays into keys - String modString = toHexString(mod); - String expString = toHexString(exponent); - BigInteger modInt = new BigInteger(modString, 16); - BigInteger expInt = new BigInteger(expString, 16); - javax.crypto.Cipher rsaCipher = null; - try { - KeyFactory kf = KeyFactory.getInstance("RSA"); - // Create cipher with oaep padding - OAEPParameterSpec oaepSpec = null; - if (digest == KMType.SHA2_256) { - oaepSpec = new OAEPParameterSpec("SHA-256", "MGF1", - MGF1ParameterSpec.SHA1, PSource.PSpecified.DEFAULT); - } else { - oaepSpec = new OAEPParameterSpec("SHA1", "MGF1", - MGF1ParameterSpec.SHA1, PSource.PSpecified.DEFAULT); - } - rsaCipher = javax.crypto.Cipher.getInstance("RSA/ECB/OAEPPadding", "SunJCE"); - - RSAPublicKeySpec pubSpec = new RSAPublicKeySpec(modInt, expInt); - java.security.interfaces.RSAPublicKey pubKey = (java.security.interfaces.RSAPublicKey) kf - .generatePublic(pubSpec); - rsaCipher.init(javax.crypto.Cipher.ENCRYPT_MODE, pubKey, oaepSpec); - byte[] cipherOut = rsaCipher.doFinal(input, inputOff, inputlen); - - if (cipherOut != null) { - Util.arrayCopyNonAtomic(cipherOut, (short) 0, output, outputOff, (short) cipherOut.length); - } - return (short) cipherOut.length; - } catch (NoSuchAlgorithmException e) { - e.printStackTrace(); - } catch (InvalidKeySpecException e) { - e.printStackTrace(); - } catch (InvalidKeyException e) { - e.printStackTrace(); - } catch (InvalidAlgorithmParameterException e) { - e.printStackTrace(); - } catch (NoSuchPaddingException e) { - e.printStackTrace(); - } catch (NoSuchProviderException e) { - e.printStackTrace(); - } catch (IllegalBlockSizeException e) { - e.printStackTrace(); - } catch (BadPaddingException e) { - e.printStackTrace(); - } - return 0; - } - - public boolean ecNoDigestVerifyMessage(byte[] input, short inputOff, - short inputlen, byte[] sign, short signOff, short signLen, - byte[] keyBlob) { - KeyFactory kf; - byte[] pubKey = new byte[128]; - short keyStart = 0; - short keyLength = getPublicKey(keyBlob, (short) 0, (short) keyBlob.length, - pubKey, (short) 0); - if (keyLength == 0) { - return false; - } - try { - java.security.Signature sunSigner = java.security.Signature.getInstance( - "NONEwithECDSA", "SunEC"); - kf = KeyFactory.getInstance("EC"); - AlgorithmParameters parameters = AlgorithmParameters.getInstance("EC", - "SunEC"); - // Supported curve secp256r1 - parameters.init(new ECGenParameterSpec("secp256r1")); - ECParameterSpec ecParameters = parameters - .getParameterSpec(ECParameterSpec.class); - - // Check if the first byte is 04 and remove it. - if (pubKey[keyStart] == 0x04) { - // uncompressed format. - keyStart++; - keyLength--; - } - short i = 0; - byte[] pubx = new byte[keyLength / 2]; - for (; i < keyLength / 2; i++) { - pubx[i] = pubKey[keyStart + i]; - } - byte[] puby = new byte[keyLength / 2]; - for (i = 0; i < keyLength / 2; i++) { - puby[i] = pubKey[keyStart + keyLength / 2 + i]; - } - BigInteger bIX = new BigInteger(pubx); - BigInteger bIY = new BigInteger(puby); - ECPoint point = new ECPoint(bIX, bIY); - ECPublicKeySpec pubkeyspec = new ECPublicKeySpec(point, ecParameters); - java.security.interfaces.ECPublicKey ecPubkey = (java.security.interfaces.ECPublicKey) kf - .generatePublic(pubkeyspec); - sunSigner.initVerify(ecPubkey); - sunSigner.update(input, inputOff, inputlen); - return sunSigner.verify(sign, signOff, signLen); - } catch (NoSuchAlgorithmException e) { - e.printStackTrace(); - } catch (NoSuchProviderException e) { - e.printStackTrace(); - } catch (InvalidParameterSpecException e) { - e.printStackTrace(); - } catch (InvalidKeySpecException e) { - e.printStackTrace(); - } catch (InvalidKeyException e) { - e.printStackTrace(); - } catch (SignatureException e) { - e.printStackTrace(); - } - return false; - } - - public boolean ecVerifyMessage(byte[] input, short inputOff, short inputlen, - byte[] sign, short signOff, short signLen, byte[] keyBlob) { - Signature ecVerifier; - byte[] pubKey = new byte[128]; - short len = getPublicKey(keyBlob, (short) 0, (short) keyBlob.length, - pubKey, (short) 0); - if (len == 0) { - return false; - } - ECPublicKey key = (ECPublicKey) KeyBuilder.buildKey( - KeyBuilder.TYPE_EC_FP_PUBLIC, KeyBuilder.LENGTH_EC_FP_256, false); - key.setW(pubKey, (short) 0, len); - ecVerifier = Signature.getInstance(Signature.ALG_ECDSA_SHA_256, false); - ecVerifier.init(key, Signature.MODE_VERIFY); - return ecVerifier.verify(input, inputOff, inputlen, sign, signOff, signLen); - } - - public boolean rsaVerifyMessage(byte[] input, short inputOff, short inputlen, byte[] sign, - short signOff, short signLen, - short digest, short padding, byte[] keyBlob) { - if (digest == KMType.DIGEST_NONE || padding == KMType.PADDING_NONE) { - return false; - } - byte[] pubKey = new byte[256]; - if (0 == getPublicKey(keyBlob, (short) 0, (short) keyBlob.length, pubKey, (short) 0)) { - return false; - } - short alg = Signature.ALG_RSA_SHA_256_PKCS1_PSS; - - if (padding == KMType.RSA_PKCS1_1_5_SIGN) { - alg = Signature.ALG_RSA_SHA_256_PKCS1; - } - - 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}; - key.setExponent(exponent, (short) 0, (short) exponent.length); - key.setModulus(pubKey, (short) 0, (short) pubKey.length); - rsaVerifier.init(key, Signature.MODE_VERIFY); - return rsaVerifier.verify(input, inputOff, inputlen, sign, signOff, signLen); - } - - public byte[] EncryptMessage(byte[] input, short params, byte[] keyBlob) { - short ret = begin(KMType.ENCRYPT, - KMByteBlob.instance(keyBlob, (short) 0, (short) keyBlob.length), - KMKeyParameters.instance(params), (short) 0, false); - // Get the operation handle. - short opHandle = KMArray.cast(ret).get((short) 2); - byte[] opHandleBuf = new byte[KMRepository.OPERATION_HANDLE_SIZE]; - KMInteger.cast(opHandle).getValue(opHandleBuf, (short) 0, - (short) opHandleBuf.length); - opHandle = KMInteger.uint_64(opHandleBuf, (short) 0); - - ret = finish(opHandle, - KMByteBlob.instance(input, (short) 0, (short) input.length), null, - (short) 0, (short) 0, (short) 0, KMError.OK, false); - short dataPtr = KMArray.cast(ret).get((short) 2); - byte[] output = new byte[KMByteBlob.cast(dataPtr).length()]; - if (KMByteBlob.cast(dataPtr).length() > 0) { - Util.arrayCopyNonAtomic(KMByteBlob.cast(dataPtr).getBuffer(), KMByteBlob - .cast(dataPtr).getStartOff(), output, (short) 0, - KMByteBlob.cast(dataPtr).length()); - } - return output; - } - - public byte[] DecryptMessage(byte[] input, short params, byte[] keyBlob) { - short ret = begin(KMType.DECRYPT, - KMByteBlob.instance(keyBlob, (short) 0, (short) keyBlob.length), - KMKeyParameters.instance(params), (short) 0, false); - // Get the operation handle. - short opHandle = KMArray.cast(ret).get((short) 2); - byte[] opHandleBuf = new byte[KMRepository.OPERATION_HANDLE_SIZE]; - KMInteger.cast(opHandle).getValue(opHandleBuf, (short) 0, - (short) opHandleBuf.length); - opHandle = KMInteger.uint_64(opHandleBuf, (short) 0); - - ret = finish(opHandle, - KMByteBlob.instance(input, (short) 0, (short) input.length), null, - (short) 0, (short) 0, (short) 0, KMError.OK, false); - short dataPtr = KMArray.cast(ret).get((short) 2); - byte[] output = new byte[KMByteBlob.cast(dataPtr).length()]; - if (KMByteBlob.cast(dataPtr).length() > 0) { - Util.arrayCopyNonAtomic(KMByteBlob.cast(dataPtr).getBuffer(), KMByteBlob - .cast(dataPtr).getStartOff(), output, (short) 0, - KMByteBlob.cast(dataPtr).length()); - } - return output; - } - - public short generateRandom(short upperBound) { - Random rand = new Random(); - short int_random = (short) rand.nextInt(upperBound); - return int_random; - } - - @Test - public void testUnsupportedBlockMode() { - init(); - short desKey = generateAesDesKey(KMType.DES, (short) 168, null, null, false); - short desKeyPtr = KMArray.cast(desKey).get((short) 1); - byte[] keyBlob = new byte[KMByteBlob.cast(desKeyPtr).length()]; - Util.arrayCopyNonAtomic(KMByteBlob.cast(desKeyPtr).getBuffer(), KMByteBlob - .cast(desKeyPtr).getStartOff(), keyBlob, (short) 0, - (short) keyBlob.length); - short desPkcs7Params = getAesDesParams(KMType.DES, (byte) KMType.CTR, - KMType.PKCS7, new byte[12]); - short ret = begin(KMType.ENCRYPT, - KMByteBlob.instance(keyBlob, (short) 0, (short) keyBlob.length), - KMKeyParameters.instance(desPkcs7Params), (short) 0, false); - Assert.assertTrue(ret == KMError.UNSUPPORTED_BLOCK_MODE); - cleanUp(); - } - - @Test - public void testDesEcbPkcs7PaddingCorrupted() { - init(); - short desKey = generateAesDesKey(KMType.DES, (short) 168, null, null, false); - short desKeyPtr = KMArray.cast(desKey).get((short) 1); - byte[] keyBlob = new byte[KMByteBlob.cast(desKeyPtr).length()]; - Util.arrayCopyNonAtomic(KMByteBlob.cast(desKeyPtr).getBuffer(), KMByteBlob - .cast(desKeyPtr).getStartOff(), keyBlob, (short) 0, - (short) keyBlob.length); - - byte[] message = { - 0x61}; - short desPkcs7Params = getAesDesParams(KMType.DES, KMType.ECB, - KMType.PKCS7, null); - byte[] cipherText1 = EncryptMessage(message, desPkcs7Params, keyBlob); - Assert.assertEquals(8, cipherText1.length); - Assert.assertFalse(Arrays.equals(message, cipherText1)); - - // Corrupt the cipher text. - ++cipherText1[(cipherText1.length / 2)]; - - // Decrypt operation - // Begin - desPkcs7Params = getAesDesParams(KMType.DES, KMType.ECB, KMType.PKCS7, null); - - short ret = begin(KMType.DECRYPT, - KMByteBlob.instance(keyBlob, (short) 0, (short) keyBlob.length), - KMKeyParameters.instance(desPkcs7Params), (short) 0, false); - // Get the operation handle. - short opHandle = KMArray.cast(ret).get((short) 2); - byte[] opHandleBuf = new byte[KMRepository.OPERATION_HANDLE_SIZE]; - KMInteger.cast(opHandle).getValue(opHandleBuf, (short) 0, - (short) opHandleBuf.length); - opHandle = KMInteger.uint_64(opHandleBuf, (short) 0); - - // Finish - short dataPtr = KMByteBlob.instance(cipherText1, (short) 0, - (short) cipherText1.length); - opHandle = KMInteger.uint_64(opHandleBuf, (short) 0); - ret = finish(opHandle, dataPtr, null, (short) 0, (short) 0, (short) 0, - KMError.INVALID_ARGUMENT, false); - cleanUp(); - } - - @Test - public void testVtsRsaPkcs1Success() { - init(); - byte[] message = { - 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x57, 0x6f, 0x72, 0x6c, 0x64, - 0x21}; // "Hello World!"; - for (int i = 0; i < 250; i++) { - short key = generateRsaKey(null, null); - short rsaKeyPtr = KMArray.cast(key).get((short) 1); - byte[] keyBlob = new byte[KMByteBlob.cast(rsaKeyPtr).length()]; - Util.arrayCopyNonAtomic(KMByteBlob.cast(rsaKeyPtr).getBuffer(), - KMByteBlob.cast(rsaKeyPtr).getStartOff(), keyBlob, (short) 0, - (short) keyBlob.length); - short pkcs1Params = getRsaParams(KMType.DIGEST_NONE, - KMType.RSA_PKCS1_1_5_ENCRYPT); - - byte[] cipherText1 = new byte[256]; - short cipherText1Len = rsaEncryptMessage(keyBlob, KMType.RSA_PKCS1_1_5_ENCRYPT, - KMType.DIGEST_NONE, - message, (short) 0, (short) message.length, - cipherText1, (short) 0); - Assert.assertEquals((2048 / 8), cipherText1Len); - - pkcs1Params = getRsaParams(KMType.DIGEST_NONE, - KMType.RSA_PKCS1_1_5_ENCRYPT); - byte[] cipherText2 = new byte[256]; - short cipherText2Len = rsaEncryptMessage(keyBlob, KMType.RSA_PKCS1_1_5_ENCRYPT, - KMType.DIGEST_NONE, - message, (short) 0, (short) message.length, - cipherText2, (short) 0); - Assert.assertEquals((2048 / 8), cipherText2Len); - - // PKCS1 v1.5 randomizes padding so every result should be different. - Assert.assertFalse(Arrays.equals(cipherText1, cipherText2)); - - pkcs1Params = getRsaParams(KMType.DIGEST_NONE, - KMType.RSA_PKCS1_1_5_ENCRYPT); - byte[] plainText = DecryptMessage(cipherText1, pkcs1Params, keyBlob); - Assert.assertTrue(Arrays.equals(message, plainText)); - - // Decrypting corrupted ciphertext should fail. - short offset_to_corrupt = generateRandom((short) cipherText1.length); - - byte corrupt_byte; - do { - corrupt_byte = (byte) generateRandom((short) 256); - } while (corrupt_byte == cipherText1[offset_to_corrupt]); - cipherText1[offset_to_corrupt] = corrupt_byte; - - pkcs1Params = getRsaParams(KMType.DIGEST_NONE, - KMType.RSA_PKCS1_1_5_ENCRYPT); - // Do Begin operation. - short ret = begin(KMType.DECRYPT, - KMByteBlob.instance(keyBlob, (short) 0, (short) keyBlob.length), - KMKeyParameters.instance(pkcs1Params), (short) 0, false); - - // Get the operation handle. - short opHandle = KMArray.cast(ret).get((short) 2); - byte[] opHandleBuf = new byte[KMRepository.OPERATION_HANDLE_SIZE]; - KMInteger.cast(opHandle).getValue(opHandleBuf, (short) 0, - (short) opHandleBuf.length); - opHandle = KMInteger.uint_64(opHandleBuf, (short) 0); - - short dataPtr = KMByteBlob.instance(cipherText1, (short) 0, - (short) cipherText1.length); - // Finish should return UNKNOWN_ERROR. - ret = finish(opHandle, dataPtr, null, (short) 0, (short) 0, (short) 0, - KMError.UNKNOWN_ERROR, 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 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); - short NO_UPGRADE = 0x01; - short UPGRADE = 0x02; - short[][] test_data = { - {OS_VERSION, OS_PATCH_LEVEL, VENDOR_PATCH_LEVEL, BOOT_PATCH_LEVEL, NO_UPGRADE, KMError.OK }, - {OS_VERSION+1, OS_PATCH_LEVEL, VENDOR_PATCH_LEVEL, BOOT_PATCH_LEVEL, UPGRADE, KMError.OK }, - {OS_VERSION, OS_PATCH_LEVEL+1, VENDOR_PATCH_LEVEL, BOOT_PATCH_LEVEL, UPGRADE, KMError.OK }, - {OS_VERSION, OS_PATCH_LEVEL, VENDOR_PATCH_LEVEL+1, BOOT_PATCH_LEVEL, UPGRADE, KMError.OK }, - {OS_VERSION, OS_PATCH_LEVEL, VENDOR_PATCH_LEVEL, BOOT_PATCH_LEVEL+1, UPGRADE, KMError.OK }, - {OS_VERSION+1, OS_PATCH_LEVEL+1, VENDOR_PATCH_LEVEL+1, BOOT_PATCH_LEVEL+1, UPGRADE, KMError.OK }, - {OS_VERSION+1, OS_PATCH_LEVEL, VENDOR_PATCH_LEVEL+1, BOOT_PATCH_LEVEL, UPGRADE, KMError.OK }, - {OS_VERSION+1, OS_PATCH_LEVEL+1, VENDOR_PATCH_LEVEL, BOOT_PATCH_LEVEL, UPGRADE, KMError.OK }, - {OS_VERSION, OS_PATCH_LEVEL, VENDOR_PATCH_LEVEL, BOOT_PATCH_LEVEL-1, NO_UPGRADE, KMError.INVALID_ARGUMENT }, - {OS_VERSION-1/*0*/, OS_PATCH_LEVEL, VENDOR_PATCH_LEVEL, BOOT_PATCH_LEVEL, UPGRADE, KMError.OK }, - {OS_VERSION, OS_PATCH_LEVEL, VENDOR_PATCH_LEVEL-1, BOOT_PATCH_LEVEL, NO_UPGRADE, KMError.INVALID_ARGUMENT }, - {OS_VERSION, OS_PATCH_LEVEL+1, VENDOR_PATCH_LEVEL-1, BOOT_PATCH_LEVEL, NO_UPGRADE, KMError.INVALID_ARGUMENT }, - {0, OS_PATCH_LEVEL+1, VENDOR_PATCH_LEVEL-1, BOOT_PATCH_LEVEL+1, NO_UPGRADE, KMError.INVALID_ARGUMENT }, - }; - for (int i = 0; i < test_data.length; i++) { - setBootParams(simulator, (short) test_data[i][3]); - setAndroidOSSystemProperties(simulator, (short) test_data[i][0], (short) test_data[i][1], - (short) test_data[i][2]); - ret = upgradeKey( - KMByteBlob.instance(keyBlob, (short) 0, (short) keyBlob.length), - null, null, test_data[i][5]); - if (test_data[i][5] != KMError.OK) - continue; - keyBlobPtr = KMArray.cast(ret).get((short) 1); - if (test_data[i][4] == UPGRADE) - Assert.assertNotEquals(KMByteBlob.cast(keyBlobPtr).length(), 0); - else - Assert.assertEquals(KMByteBlob.cast(keyBlobPtr).length(), 0); - if (KMByteBlob.cast(keyBlobPtr).length() != 0) { - 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(); - short ptr = KMKeyParameters.findTag(KMType.UINT_TAG, - KMType.VENDOR_PATCH_LEVEL, hwParams); - short vendorPatchLevel = KMIntegerTag.cast(ptr).getValue(); - ptr = KMKeyParameters.findTag(KMType.UINT_TAG, KMType.BOOT_PATCH_LEVEL, - hwParams); - short bootPatchLevel = KMIntegerTag.cast(ptr).getValue(); - Assert.assertEquals(KMInteger.cast(osVersion).getShort(), - test_data[i][0]); - Assert.assertEquals(KMInteger.cast(osPatch).getShort(), - test_data[i][1]); - Assert.assertEquals(KMInteger.cast(vendorPatchLevel).getShort(), - test_data[i][2]); - Assert.assertEquals(KMInteger.cast(bootPatchLevel).getShort(), - test_data[i][3]); - } - } - cleanUp(); - } - - public void testCardRest() { - byte[] input = new byte[] {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}; - // Test different combinations of reset events happening in the ordered flow of - // begin - begin1 - update - update1 - finish - finish1 - abort - boolean[][] resetEvents = { - //begin, begin1, update, update1, finish, finish1, abort - {NO_RESET, NO_RESET, NO_RESET, NO_RESET, NO_RESET, NO_RESET, NO_RESET}, - {RESET, NO_RESET, NO_RESET, NO_RESET, NO_RESET, NO_RESET, NO_RESET}, - {NO_RESET, RESET, NO_RESET, NO_RESET, NO_RESET, NO_RESET, NO_RESET}, - {NO_RESET, NO_RESET, RESET, NO_RESET, NO_RESET, NO_RESET, NO_RESET}, - {NO_RESET, NO_RESET, NO_RESET, RESET, NO_RESET, NO_RESET, NO_RESET}, - {NO_RESET, NO_RESET, NO_RESET, NO_RESET, RESET, NO_RESET, NO_RESET}, - {NO_RESET, NO_RESET, NO_RESET, NO_RESET, NO_RESET, RESET, NO_RESET}, - {NO_RESET, NO_RESET, NO_RESET, NO_RESET, NO_RESET, NO_RESET, RESET}, - {NO_RESET, NO_RESET, NO_RESET, RESET, RESET, NO_RESET, NO_RESET}, - {NO_RESET, RESET, RESET, NO_RESET, NO_RESET, NO_RESET, NO_RESET}, - {RESET, RESET, RESET, RESET, RESET, RESET, RESET}, - }; - for(int i = 0; i < resetEvents.length; i++) { - // Generate Key---------------- - short ret = generateHmacKey(null, null); - // Store the generated key in a new byte blob. - 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 inParams = getHmacParams(KMType.SHA2_256, true); - // Generate Key---------------- - - //Call begin operation---------------- - ret = begin(KMType.SIGN, keyBlobPtr, KMKeyParameters.instance(inParams), (short) 0, resetEvents[i][0]); - // Get the operation handle. - short opHandle = KMArray.cast(ret).get((short) 2); - byte[] opHandleBuf = new byte[KMRepository.OPERATION_HANDLE_SIZE]; - KMInteger.cast(opHandle).getValue(opHandleBuf, (short) 0, (short) opHandleBuf.length); - //Get the keyblobptr again. - keyBlobPtr = KMByteBlob.instance(keyBlob, (short) 0, (short) keyBlob.length); - //Call begin end---------------- - - //Call begin1 operation---------------- - inParams = getHmacParams(KMType.SHA2_256, true); - ret = begin(KMType.SIGN, keyBlobPtr, KMKeyParameters.instance(inParams), (short) 0, resetEvents[i][1]); - // Get the operation handle. - short opHandle1 = KMArray.cast(ret).get((short) 2); - byte[] opHandleBuf1 = new byte[KMRepository.OPERATION_HANDLE_SIZE]; - KMInteger.cast(opHandle1).getValue(opHandleBuf1, (short) 0, (short) opHandleBuf1.length); - //Get the keyblobptr again. - keyBlobPtr = KMByteBlob.instance(keyBlob, (short) 0, (short) keyBlob.length); - //Call begin1 end---------------- - - //Call update operation---------------- - // Call update operation and check if the secure element power reset flag is set or not. - short dataPtr = KMByteBlob.instance(input, (short) 0, (short) input.length); - opHandle = KMInteger.instance(opHandleBuf, (short) 0, (short) opHandleBuf.length); - // update with trigger reset. - ret = update(opHandle, dataPtr, (short) 0, (short) 0, (short) 0, resetEvents[i][2]); - // If a reset event occurred then expect INVALID_OPERATION_HANDLE. - if (resetEvents[i][1] || resetEvents[i][2]) { - short err = KMInteger.cast(ret).getShort(); - Assert.assertEquals(KMError.INVALID_OPERATION_HANDLE, err); - } - //Call update end---------------- - - //Call update1 operation---------------- - // Call update1 operation and check if the secure element power reset flag is set or not. - dataPtr = KMByteBlob.instance(input, (short) 0, (short) input.length); - opHandle1 = KMInteger.instance(opHandleBuf1, (short) 0, (short) opHandleBuf1.length); - // update with trigger reset. - ret = update(opHandle1, dataPtr, (short) 0, (short) 0, (short) 0, resetEvents[i][3]); - // If a reset event occurred then expect INVALID_OPERATION_HANDLE. - if (resetEvents[i][2] || resetEvents[i][3]) { - short err = KMInteger.cast(ret).getShort(); - Assert.assertEquals(KMError.INVALID_OPERATION_HANDLE, err); - } - //Call update end---------------- - - //Call finish operation---------------- - // Call finish operation and check if the secure element power reset flag is set or not. - dataPtr = KMByteBlob.instance((short) 0); - opHandle = KMInteger.uint_64(opHandleBuf, (short) 0); - short expectedErr = KMError.OK; - // If a reset event occurred then expect INVALID_OPERATION_HANDLE. - if (resetEvents[i][1] | resetEvents[i][2] | resetEvents[i][3] | resetEvents[i][4]) - expectedErr = KMError.INVALID_OPERATION_HANDLE; - ret = finish(opHandle, dataPtr, null, (short) 0, (short) 0, (short) 0, expectedErr, resetEvents[i][4]); - //Call finish end---------------- - - //Call finish1 operation---------------- - // Call finish1 operation and check if the secure element power reset flag is set or not. - dataPtr = KMByteBlob.instance((short) 0); - opHandle1 = KMInteger.instance(opHandleBuf1, (short) 0, (short) opHandleBuf1.length); - expectedErr = KMError.OK; - // If a reset event occurred then expect INVALID_OPERATION_HANDLE. - if (resetEvents[i][2] | resetEvents[i][3] | resetEvents[i][4] | resetEvents[i][5]) - expectedErr = KMError.INVALID_OPERATION_HANDLE; - ret = finish(opHandle1, dataPtr, null, (short) 0, (short) 0, (short) 0, expectedErr, resetEvents[i][5]); - //Call finish end---------------- - - //Call abort operation---------------- - // Call abort operation and check if the secure element power reset flag is set or not. - opHandle = KMInteger.uint_64(opHandleBuf, (short) 0); - ret = abort(opHandle, resetEvents[i][6]); - if (resetEvents[i][1] || resetEvents[i][2] | resetEvents[i][3] | resetEvents[i][4] | resetEvents[i][5] | resetEvents[i][6]) { - short err = KMInteger.cast(ret).getShort(); - Assert.assertEquals(KMError.INVALID_OPERATION_HANDLE, err); - } - //Call finish end---------------- - KMRepository.instance().clean(); - } - } - - @Test - public void testCardResetFunctionality() { - init(); - testCardRest(); - 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 expectedErr) { - 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); - byte[] respBuf = response.getBytes(); - short len = (short) respBuf.length; - if (KMError.OK == expectedErr) { - short ret = KMArray.instance((short) 2); - KMArray.cast(ret).add((short) 0, KMInteger.exp()); - KMArray.cast(ret).add((short) 1, KMByteBlob.exp()); - ret = decoder.decode(ret, respBuf, (short) 0, len); - Assert.assertEquals(expectedErr, KMInteger.cast(KMArray.cast(ret).get((short) 0)).getShort()); - return ret; - } else { - short ret = KMInteger.exp(); - ret = decoder.decode(ret, respBuf, (short) 0, len); - Assert.assertEquals(expectedErr, KMInteger.cast(ret).getShort()); - 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, false); - short opHandle = KMArray.cast(ret).get((short) 2); - byte[] opHandleBuf = new byte[KMRepository.OPERATION_HANDLE_SIZE]; - KMInteger.cast(opHandle).getValue(opHandleBuf, (short) 0, (short) opHandleBuf.length); - opHandle = KMInteger.uint_64(opHandleBuf, (short) 0); - ret = abort(opHandle, false); - Assert.assertEquals(KMError.OK, KMInteger.cast(ret).getShort()); - short dataPtr = KMByteBlob.instance(plainData, (short) 0, (short) plainData.length); - opHandle = KMInteger.uint_64(opHandleBuf, (short) 0); - ret = update(opHandle, dataPtr, (short) 0, (short) 0, (short) 0, false); - ret = KMInteger.cast(ret).getShort(); - 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(); - byte[] cipherData = new byte[256]; - short cipherDataLen = 0; - //Encrypt - if (padding == KMType.RSA_OAEP) { - cipherDataLen = rsaOaepEncryptMessage(keyBlob, digest, plainData, - (short) 0, (short) plainData.length, cipherData, (short) 0); - } else { - cipherDataLen = rsaEncryptMessage(keyBlob, padding, digest, plainData, - (short) 0, (short) plainData.length, cipherData, (short) 0); - } - Assert.assertTrue((cipherDataLen == 256)); - inParams = getRsaParams(digest, padding); - short 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; - } - boolean verify = rsaVerifyMessage(plainData, (short) 0, (short) plainData.length, - signatureData, (short) 0, (short) signatureData.length, - digest, padding, keyBlob); - Assert.assertTrue(verify); - } - - 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); - boolean verify = false; - if (digest == KMType.DIGEST_NONE) { - verify = ecNoDigestVerifyMessage(plainData, (short) 0, (short) plainData.length, - signatureData, (short) 0, (short) signatureData.length, - keyBlob); - } else { - verify = ecVerifyMessage(plainData, (short) 0, (short) plainData.length, - signatureData, (short) 0, (short) signatureData.length, - keyBlob); - } - Assert.assertTrue(verify); - } - - 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, false); - short opHandle = KMArray.cast(beginResp).get((short) 2); - byte[] opHandleBuf = new byte[KMRepository.OPERATION_HANDLE_SIZE]; - KMInteger.cast(opHandle).getValue(opHandleBuf, (short) 0, (short) opHandleBuf.length); - 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); - } - opHandle = KMInteger.uint_64(opHandleBuf, (short) 0); - ret = update(opHandle, dataPtr, inParams, (short) 0, (short) 0, false); - 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)); - } - } - - opHandle = KMInteger.uint_64(opHandleBuf, (short) 0); - if (keyPurpose == KMType.VERIFY) { - ret = finish(opHandle, dataPtr, signature, (short) 0, (short) 0, (short) 0, KMError.OK, false); - } else { - ret = finish(opHandle, dataPtr, null, (short) 0, (short) 0, (short) 0, KMError.OK, false); - } - 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, boolean triggerReset) { - 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); - if (triggerReset) { - resetAndSelect(); - } - //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; - byte majorType = readMajorType(respBuf); - //if (len > 5) { - if (majorType == CBOR_ARRAY_MAJOR_TYPE) { - ret = decoder.decode(ret, respBuf, (short) 0, len); - short error = KMInteger.cast(KMArray.cast(ret).get((short) 0)).getShort(); - Assert.assertEquals(error, KMError.OK); - if (triggerReset) { - error = KMInteger.cast(KMArray.cast(ret).get((short) 0)).getSignificantShort(); - Assert.assertEquals(error, SE_POWER_RESET_FLAG); - } - return ret; - } else {//Major type UINT. - ret = decoder.decode(KMInteger.exp(), respBuf, (short) 0, len); - if (triggerReset) { - short error = KMInteger.cast(ret).getSignificantShort(); - Assert.assertEquals(error, SE_POWER_RESET_FLAG); - } - return KMInteger.cast(ret).getShort(); - /*if (len == 3) { - return respBuf[0]; - } - if (len == 4) { - return respBuf[1]; - } - return Util.getShort(respBuf, (short) 0);*/ - } - } - - public short translateExtendedErrorCodes(short err) { - switch (err) { - case KMError.SW_CONDITIONS_NOT_SATISFIED: - case KMError.UNSUPPORTED_CLA: - case KMError.INVALID_P1P2: - case KMError.INVALID_DATA: - case KMError.CRYPTO_ILLEGAL_USE: - case KMError.CRYPTO_ILLEGAL_VALUE: - case KMError.CRYPTO_INVALID_INIT: - case KMError.CRYPTO_UNINITIALIZED_KEY: - case KMError.GENERIC_UNKNOWN_ERROR: - err = KMError.UNKNOWN_ERROR; - break; - case KMError.CRYPTO_NO_SUCH_ALGORITHM: - err = KMError.UNSUPPORTED_ALGORITHM; - break; - case KMError.UNSUPPORTED_INSTRUCTION: - case KMError.CMD_NOT_ALLOWED: - case KMError.SW_WRONG_LENGTH: - err = KMError.UNIMPLEMENTED; - break; - default: - break; - } - return err; - } - - public short finish(short operationHandle, short data, byte[] signature, short inParams, - short hwToken, short verToken, short expectedErr, boolean triggerReset) { - 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()); - if (triggerReset) { - resetAndSelect(); - } - ResponseAPDU response = simulator.transmitCommand(apdu); - byte[] respBuf = response.getBytes(); - short len = (short) respBuf.length; - short ret; - short error; - if (expectedErr == KMError.OK) { - 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()); - } else { - ret = KMInteger.exp(); - } - ret = decoder.decode(ret, respBuf, (short) 0, len); - if (expectedErr == KMError.OK) { - error = KMInteger.cast(KMArray.cast(ret).get((short) 0)).getShort(); - if (triggerReset) { - short powerResetStatus = KMInteger.cast(KMArray.cast(ret).get((short) 0)).getSignificantShort(); - Assert.assertEquals(powerResetStatus, SE_POWER_RESET_FLAG); - } - } else { - error = KMInteger.cast(ret).getShort(); - error = translateExtendedErrorCodes(error); - if (triggerReset) { - short powerResetStatus = KMInteger.cast(ret).getSignificantShort(); - Assert.assertEquals(powerResetStatus, SE_POWER_RESET_FLAG); - } - } - Assert.assertEquals(error, expectedErr); - return ret; - } - - public short update(short operationHandle, short data, short inParams, short hwToken, - short verToken, boolean triggerReset) { - 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); - if (triggerReset) { - resetAndSelect(); - } - // 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; - byte majorType = readMajorType(respBuf); - if (majorType == CBOR_ARRAY_MAJOR_TYPE) { - ret = decoder.decode(ret, respBuf, (short) 0, len); - short error = KMInteger.cast(KMArray.cast(ret).get((short) 0)).getShort(); - Assert.assertEquals(error, KMError.OK); - if (triggerReset) { - error = KMInteger.cast(KMArray.cast(ret).get((short) 0)).getSignificantShort(); - Assert.assertEquals(error, SE_POWER_RESET_FLAG); - } - } else { - ret = decoder.decode(KMInteger.exp(), respBuf, (short)0, len); - if (triggerReset) { - short powerResetStatus = KMInteger.cast(ret).getSignificantShort(); - Assert.assertEquals(powerResetStatus, SE_POWER_RESET_FLAG); - } - } - return ret; - } - - private byte readMajorType(byte[] resp) { - byte val = resp[0]; - return (byte) (val & MAJOR_TYPE_MASK); - } - - 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/AndroidSEProvider/lib/gpapi-upgrade.jar b/Applet/JCardSimProviderLib/lib/gpapi-upgrade.jar similarity index 100% rename from Applet/AndroidSEProvider/lib/gpapi-upgrade.jar rename to Applet/JCardSimProviderLib/lib/gpapi-upgrade.jar diff --git a/Applet/JCardSimProvider/lib/hamcrest-core-1.3.jar b/Applet/JCardSimProviderLib/lib/hamcrest-core-1.3.jar similarity index 100% rename from Applet/JCardSimProvider/lib/hamcrest-core-1.3.jar rename to Applet/JCardSimProviderLib/lib/hamcrest-core-1.3.jar diff --git a/Applet/JCardSimProvider/lib/jcardsim-3.0.5-SNAPSHOT.jar b/Applet/JCardSimProviderLib/lib/jcardsim-3.0.5-SNAPSHOT.jar similarity index 100% rename from Applet/JCardSimProvider/lib/jcardsim-3.0.5-SNAPSHOT.jar rename to Applet/JCardSimProviderLib/lib/jcardsim-3.0.5-SNAPSHOT.jar diff --git a/Applet/JCardSimProvider/lib/junit-4.13.jar b/Applet/JCardSimProviderLib/lib/junit-4.13.jar similarity index 100% rename from Applet/JCardSimProvider/lib/junit-4.13.jar rename to Applet/JCardSimProviderLib/lib/junit-4.13.jar diff --git a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMAESKey.java b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMAESKey.java similarity index 96% rename from Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMAESKey.java rename to Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMAESKey.java index 258dc461..bbbb27f2 100644 --- a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMAESKey.java +++ b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMAESKey.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.javacard.keymaster; +package com.android.javacard.seprovider; import javacard.security.AESKey; diff --git a/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMAttestationCert.java b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMAttestationCert.java new file mode 100644 index 00000000..6cd8e7b0 --- /dev/null +++ b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMAttestationCert.java @@ -0,0 +1,194 @@ +/* + * 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.seprovider; + +/** + * The KMAttestationCert interface represents a X509 compliant attestation certificate required to + * support keymaster's attestKey function. This cert will be created according to the specifications + * given in android keymaster hal documentation. KMSeProvider has to provide the instance of this + * certificate. This interface is designed based on builder pattern and hence each method returns + * instance of cert. + */ +public interface KMAttestationCert { + + /** + * Set verified boot hash. + * + * @param obj This is a KMByteBlob containing hash + * @return instance of KMAttestationCert + */ + KMAttestationCert verifiedBootHash(short obj); + + /** + * Set verified boot key received during booting up. + * + * @param obj This is a KMByteBlob containing verified boot key. + * @return instance of KMAttestationCert + */ + KMAttestationCert verifiedBootKey(short obj); + + /** + * Set verified boot state received during booting up. + * + * @param val This is a byte containing verified boot state value. + * @return instance of KMAttestationCert + */ + KMAttestationCert verifiedBootState(byte val); + + /** + * Set uniqueId received from CA certificate during provisioning. + * + * @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 masterKey + * @return instance of KMAttestationCert. + */ + KMAttestationCert makeUniqueId(byte[] scratchpad, short scratchPadOff, byte[] creationTime, + short creationTimeOff, short creationTimeLen, byte[] attestAppId, + short attestAppIdOff, short attestAppIdLen, byte resetSinceIdRotation, + KMMasterKey masterKey); + + /** + * Set start time received from creation/activation time tag. Used for certificate's valid + * period. + * + * @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, boolean derEncoded, byte[] scratchpad); + + + /** + * Set expiry time received from expiry time tag or ca certificates expiry time. Used for + * certificate's valid period. + * + * @param usageExpiryTimeObj This is a KMByteBlob containing expiry time. + * certificate. + * @param scratchPad Buffer to store intermediate results. + * @return instance of KMAttestationCert + */ + KMAttestationCert notAfter(short usageExpiryTimeObj, boolean derEncoded, byte[] scratchPad); + + /** + * Set device lock status received during booting time or due to device lock command. + * + * @param val This is true if device is locked. + * @return instance of KMAttestationCert + */ + KMAttestationCert deviceLocked(boolean val); + + /** + * Set public key to be attested received from attestKey command. + * + * @param obj This is KMByteBlob containing the public key. + * @return instance of KMAttestationCert + */ + KMAttestationCert publicKey(short obj); + + /** + * Set attestation challenge received from attestKey command. + * + * @param obj This is KMByteBlob containing the attestation challenge. + * @return instance of KMAttestationCert + */ + KMAttestationCert attestationChallenge(short obj); + + /** + * Set extension tag received from key characteristics which needs to be added to android + * extension. This method will called once for each tag. + * + * @param tag is the KMByteBlob containing KMTag. + * @param hwEnforced is true if the tag has to be added to hw enforced list or else added to sw + * enforced list. + * @return instance of KMAttestationCert + */ + KMAttestationCert extensionTag(short tag, boolean hwEnforced); + + /** + * Set ASN.1 encoded X509 issuer field received from attestation key CA cert. + * + * @param obj This is KMByteBlob containing the issuer. + * @return instance of KMAttestationCert + */ + KMAttestationCert issuer(short obj); + + /** + * Set byte buffer to be used to generate certificate. + * + * @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); + + /** + * Get the start of the certificate + * + * @return start of the attestation cert. + */ + short getCertStart(); + + /** + * Get the length of the certificate + * + * @return length of the attestation cert. + */ + short getCertLength(); + + + /** + * Build a fake signed certificate. After this method executes the certificate is ready with the + * signature equal to 1 byte which is 0 and with rsa signature algorithm. + */ + void build(); + + /** + * Set the Serial number in the certificate. If no serial number is set then serial number is 1. + * + * @param serialNumber + */ + boolean serialNumber(short serialNumber); + + /** + * Set the Subject Name in the certificate. + * + * @param subject + */ + boolean subjectName(short subject); + + /** + * Set attestation key and mode. + * @param attestKey KMByteBlob of the key + * @param mode + */ + KMAttestationCert ecAttestKey(short attestKey, byte mode); + /** + * Set attestation key and mode. + * @param attestKey KMByteBlob of the key + * @param mode + */ + KMAttestationCert rsaAttestKey(short attestPrivExp, short attestMod, byte mode); + +} diff --git a/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMAttestationKey.java b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMAttestationKey.java new file mode 100644 index 00000000..1b8e334e --- /dev/null +++ b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMAttestationKey.java @@ -0,0 +1,25 @@ +/* + * 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.seprovider; + +/** + * KMAttestationKey is a marker interface and the SE Provider has to implement this interface. + * Internally attestation key is stored as a Javacard EC key pair object, which will provide + * additional security. The attestation key is maintained by the SEProvider. + */ +public interface KMAttestationKey { +} + diff --git a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMCipher.java b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMCipher.java similarity index 94% rename from Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMCipher.java rename to Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMCipher.java index 420a775c..31389eee 100644 --- a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMCipher.java +++ b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMCipher.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.javacard.keymaster; +package com.android.javacard.seprovider; public abstract class KMCipher { diff --git a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMCipherImpl.java b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMCipherImpl.java similarity index 96% rename from Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMCipherImpl.java rename to Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMCipherImpl.java index c4162b12..4340cd4b 100644 --- a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMCipherImpl.java +++ b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMCipherImpl.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.javacard.keymaster; +package com.android.javacard.seprovider; import javacard.framework.Util; import javacard.security.CryptoException; diff --git a/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMDeviceUniqueKey.java b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMDeviceUniqueKey.java new file mode 100644 index 00000000..08e60a3f --- /dev/null +++ b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMDeviceUniqueKey.java @@ -0,0 +1,21 @@ +/* + * Copyright(C) 2021 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.seprovider; + +public interface KMDeviceUniqueKey { + + short getPublicKey(byte[] buf, short offset); +} diff --git a/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMECDeviceUniqueKey.java b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMECDeviceUniqueKey.java new file mode 100644 index 00000000..0f68de61 --- /dev/null +++ b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMECDeviceUniqueKey.java @@ -0,0 +1,53 @@ +/* + * Copyright(C) 2021 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.seprovider; +import javacard.security.ECPrivateKey; +import javacard.security.ECPublicKey; +import javacard.security.KeyPair; + +public class KMECDeviceUniqueKey implements KMDeviceUniqueKey { + + private KeyPair ecKeyPair; + + @Override + public short getPublicKey(byte[] buf, short offset) { + ECPublicKey publicKey = getPublicKey(); + return publicKey.getW(buf, offset); + } + + public KMECDeviceUniqueKey(KeyPair ecPair) { + ecKeyPair = ecPair; + } + + public void setS(byte[] buffer, short offset, short length) { + ECPrivateKey ecPriv = (ECPrivateKey) ecKeyPair.getPrivate(); + ecPriv.setS(buffer, offset, length); + } + + public void setW(byte[] buffer, short offset, short length) { + ECPublicKey ecPublicKey = (ECPublicKey) ecKeyPair.getPublic(); + ecPublicKey.setW(buffer, offset, length); + } + + public ECPrivateKey getPrivateKey() { + return (ECPrivateKey) ecKeyPair.getPrivate(); + } + + public ECPublicKey getPublicKey() { + return (ECPublicKey) ecKeyPair.getPublic(); + } + +} diff --git a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMECPrivateKey.java b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMECPrivateKey.java similarity index 96% rename from Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMECPrivateKey.java rename to Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMECPrivateKey.java index 62b26cce..f45c55ba 100644 --- a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMECPrivateKey.java +++ b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMECPrivateKey.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.javacard.keymaster; +package com.android.javacard.seprovider; import javacard.security.ECPrivateKey; import javacard.security.KeyPair; diff --git a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMEcdsa256NoDigestSignature.java b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMEcdsa256NoDigestSignature.java similarity index 99% rename from Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMEcdsa256NoDigestSignature.java rename to Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMEcdsa256NoDigestSignature.java index c382f3cb..e53a8963 100644 --- a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMEcdsa256NoDigestSignature.java +++ b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMEcdsa256NoDigestSignature.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.javacard.keymaster; +package com.android.javacard.seprovider; import java.math.BigInteger; import java.security.AlgorithmParameters; diff --git a/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMError.java b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMError.java new file mode 100644 index 00000000..5754abe9 --- /dev/null +++ b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMError.java @@ -0,0 +1,134 @@ +/* + * 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.seprovider; + +/** + * KMError includes all the error codes from android keymaster hal specifications. The values are + * positive unlike negative values in keymaster hal. + */ +public class KMError { + + public static final short OK = 0; + public static final short UNSUPPORTED_PURPOSE = 2; + public static final short INCOMPATIBLE_PURPOSE = 3; + public static final short UNSUPPORTED_ALGORITHM = 4; + public static final short INCOMPATIBLE_ALGORITHM = 5; + public static final short UNSUPPORTED_KEY_SIZE = 6; + public static final short UNSUPPORTED_BLOCK_MODE = 7; + public static final short INCOMPATIBLE_BLOCK_MODE = 8; + public static final short UNSUPPORTED_MAC_LENGTH = 9; + public static final short UNSUPPORTED_PADDING_MODE = 10; + public static final short INCOMPATIBLE_PADDING_MODE = 11; + public static final short UNSUPPORTED_DIGEST = 12; + public static final short INCOMPATIBLE_DIGEST = 13; + + public static final short UNSUPPORTED_KEY_ENCRYPTION_ALGORITHM = 19; + + /** + * For PKCS8 & PKCS12 + */ + public static final short INVALID_INPUT_LENGTH = 21; + + + public static final short KEY_USER_NOT_AUTHENTICATED = 26; + public static final short INVALID_OPERATION_HANDLE = 28; + public static final short VERIFICATION_FAILED = 30; + public static final short TOO_MANY_OPERATIONS = 31; + public static final short INVALID_KEY_BLOB = 33; + + public static final short INVALID_ARGUMENT = 38; + public static final short UNSUPPORTED_TAG = 39; + public static final short INVALID_TAG = 40; + public static final short IMPORT_PARAMETER_MISMATCH = 44; + public static final short OPERATION_CANCELLED = 46; + + public static final short MISSING_NONCE = 51; + public static final short INVALID_NONCE = 52; + public static final short MISSING_MAC_LENGTH = 53; + public static final short CALLER_NONCE_PROHIBITED = 55; + public static final short INVALID_MAC_LENGTH = 57; + public static final short MISSING_MIN_MAC_LENGTH = 58; + public static final short UNSUPPORTED_MIN_MAC_LENGTH = 59; + public static final short UNSUPPORTED_EC_CURVE = 61; + public static final short KEY_REQUIRES_UPGRADE = 62; + + public static final short ATTESTATION_CHALLENGE_MISSING = 63; + public static final short ATTESTATION_APPLICATION_ID_MISSING = 65; + public static final short CANNOT_ATTEST_IDS = 66; + public static final short ROLLBACK_RESISTANCE_UNAVAILABLE = 67; + + public static final short DEVICE_LOCKED = 72; + public static final short EARLY_BOOT_ENDED = 73; + public static final short ATTESTATION_KEYS_NOT_PROVISIONED =74; + public static final short INCOMPATIBLE_MGF_DIGEST = 78; + public static final short UNSUPPORTED_MGF_DIGEST = 79; + public static final short MISSING_NOT_BEFORE = 80; + public static final short MISSING_NOT_AFTER = 81; + public static final short MISSING_ISSUER_SUBJECT_NAME = 82; + public static final short INVALID_ISSUER_SUBJECT_NAME = 83; + + public static final short UNIMPLEMENTED = 100; + public static final short UNKNOWN_ERROR = 1000; + + //Extended errors + public static final short SW_CONDITIONS_NOT_SATISFIED = 10001; + public static final short UNSUPPORTED_CLA = 10002; + public static final short INVALID_P1P2 = 10003; + public static final short UNSUPPORTED_INSTRUCTION = 10004; + public static final short CMD_NOT_ALLOWED = 10005; + public static final short SW_WRONG_LENGTH = 10006; + public static final short INVALID_DATA = 10007; + + //Crypto errors + public static final short CRYPTO_ILLEGAL_USE = 10008; + public static final short CRYPTO_ILLEGAL_VALUE = 10009; + public static final short CRYPTO_INVALID_INIT = 10010; + public static final short CRYPTO_NO_SUCH_ALGORITHM = 10011; + public static final short CRYPTO_UNINITIALIZED_KEY = 10012; + //Generic Unknown error. + public static final short GENERIC_UNKNOWN_ERROR = 10013; + + // Remote key provisioning error codes. + public static final short STATUS_FAILED = 32000; + public static final short STATUS_INVALID_MAC = 32001; + public static final short STATUS_PRODUCTION_KEY_IN_TEST_REQUEST = 32002; + public static final short STATUS_TEST_KEY_IN_PRODUCTION_REQUEST = 32003; + public static final short STATUS_INVALID_EEK = 32004; + public static final short INVALID_STATE = 32005; + + public static short translate(short err) { + switch(err) { + case SW_CONDITIONS_NOT_SATISFIED: + case UNSUPPORTED_CLA: + case INVALID_P1P2: + case INVALID_DATA: + case CRYPTO_ILLEGAL_USE: + case CRYPTO_ILLEGAL_VALUE: + case CRYPTO_INVALID_INIT: + case CRYPTO_UNINITIALIZED_KEY: + case GENERIC_UNKNOWN_ERROR: + case UNKNOWN_ERROR: + return UNKNOWN_ERROR; + case CRYPTO_NO_SUCH_ALGORITHM: + return UNSUPPORTED_ALGORITHM; + case UNSUPPORTED_INSTRUCTION: + case CMD_NOT_ALLOWED: + case SW_WRONG_LENGTH: + return UNIMPLEMENTED; + } + return err; + } +} diff --git a/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMException.java b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMException.java new file mode 100644 index 00000000..c0b2431f --- /dev/null +++ b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMException.java @@ -0,0 +1,55 @@ +/* + * 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.seprovider; + +import javacard.framework.JCSystem; + +/** + * KMException is shared instance of exception used for all exceptions in the applet. It is used to + * throw EMError errors. + */ +public class KMException extends RuntimeException { + + private static short[] reason; + private static KMException exception; + + private KMException() { + } + public static short reason(){ + return reason[0]; + } + public static void throwIt(short e) { + if(reason == null) { + reason = JCSystem.makeTransientShortArray((short)1,JCSystem.CLEAR_ON_DESELECT); + } + if(exception == null){ + exception = new KMException(); + } + reason[0] = e; + throw exception; + } +/* + public static KMException instance() { + if (exception == null) { + exception = new KMException(); + } + return exception; + } +*/ +} + + diff --git a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMHmacKey.java b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMHmacKey.java similarity index 96% rename from Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMHmacKey.java rename to Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMHmacKey.java index 65f1d02a..8473f1bf 100644 --- a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMHmacKey.java +++ b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMHmacKey.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.javacard.keymaster; +package com.android.javacard.seprovider; import javacard.security.HMACKey; diff --git a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimulator.java b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMJCardSimulator.java similarity index 70% rename from Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimulator.java rename to Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMJCardSimulator.java index 46bd03aa..1d0c426e 100644 --- a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimulator.java +++ b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMJCardSimulator.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.javacard.keymaster; +package com.android.javacard.seprovider; import java.math.BigInteger; import java.security.InvalidAlgorithmParameterException; @@ -26,7 +26,6 @@ import java.security.spec.MGF1ParameterSpec; import java.security.spec.RSAPrivateKeySpec; import java.security.spec.RSAPublicKeySpec; - import javacard.framework.ISO7816; import javacard.framework.ISOException; import javacard.framework.JCSystem; @@ -41,12 +40,11 @@ import javacard.security.KeyBuilder; import javacard.security.KeyPair; import javacard.security.RSAPrivateKey; -import javacard.security.RSAPublicKey; +import javacard.security.KeyAgreement; import javacard.security.RandomData; 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,7 +55,6 @@ import javax.crypto.spec.OAEPParameterSpec; import javax.crypto.spec.PSource; import javax.crypto.spec.SecretKeySpec; - import org.globalplatform.upgrade.Element; /** @@ -66,19 +63,24 @@ * creates its own RNG using PRNG. */ public class KMJCardSimulator implements KMSEProvider { - public static final short AES_GCM_TAG_LENGTH = 16; 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 short CERT_CHAIN_MAX_SIZE = 2500;//First 2 bytes for length. + private static final short ADDITIONAL_CERT_CHAIN_MAX_SIZE = 1024;//First 2 bytes for length. + private static final short BCC_MAX_SIZE = 512; private static final short RSA_KEY_SIZE = 256; + public static final byte POWER_RESET_FALSE = (byte)0xAA; + public static final byte POWER_RESET_TRUE = (byte)0x00; + public static final byte AES_BLOCK_SIZE = 16; - + public static byte[] resetFlag; public static boolean jcardSim = false; private static Signature kdf; private static Signature hmacSignature; + private static KeyAgreement keyAgreement; private static byte[] rngCounter; private static AESKey aesRngKey; @@ -86,9 +88,33 @@ public class KMJCardSimulator implements KMSEProvider { private static byte[] entropyPool; private static byte[] rndNum; private byte[] certificateChain; + private byte[] additionalCertChain; + private byte[] bcc; private KMAESKey masterKey; private KMECPrivateKey attestationKey; + private KMECDeviceUniqueKey testKey; + private KMECDeviceUniqueKey deviceUniqueKey; private KMHmacKey preSharedKey; + private boolean deviceReboot; + + // Data - originally was in repository + private byte[] attIdBrand; + private byte[] attIdDevice; + private byte[] attIdProduct; + private byte[] attIdSerial; + private byte[] attIdImei; + private byte[] attIdMeId; + private byte[] attIdManufacturer; + private byte[] attIdModel; + + // Boot parameters + private byte[] verifiedHash; + private byte[] bootKey; + private byte[] bootPatchLevel; + private boolean deviceBootLocked; + private short bootState; + private boolean isProvisionLocked; + private static KMJCardSimulator jCardSimulator = null; @@ -101,6 +127,7 @@ public KMJCardSimulator() { // Various Keys kdf = Signature.getInstance(Signature.ALG_AES_CMAC_128, false); hmacSignature = Signature.getInstance(Signature.ALG_HMAC_SHA_256, false); + keyAgreement = KeyAgreement.getInstance(KeyAgreement.ALG_EC_SVDP_DH_PLAIN, false); // RNG rndNum = JCSystem.makeTransientByteArray(MAX_RND_NUM_SIZE, JCSystem.CLEAR_ON_RESET); entropyPool = JCSystem.makeTransientByteArray(ENTROPY_POOL_SIZE, JCSystem.CLEAR_ON_RESET); @@ -115,7 +142,12 @@ public KMJCardSimulator() { // various ciphers //Allocate buffer for certificate chain. certificateChain = new byte[CERT_CHAIN_MAX_SIZE]; + additionalCertChain = new byte[ADDITIONAL_CERT_CHAIN_MAX_SIZE]; + bcc = new byte[BCC_MAX_SIZE]; jCardSimulator = this; + resetFlag = new byte[1]; + resetFlag[0] = (byte) POWER_RESET_FALSE; + deviceReboot = false; } @@ -556,6 +588,61 @@ public short hmacKDF(KMMasterKey masterkey, byte[] data, short dataStart, signature, signatureStart); } + @Override + public short hkdf(byte[] ikm, short ikmOff, short ikmLen, byte[] salt, short saltOff, short saltLen, + byte[] info, short infoOff, short infoLen, byte[] out, short outOff, short outLen) { + // HMAC_extract + byte[] prk = new byte[32]; + hkdfExtract(ikm, ikmOff, ikmLen, salt, saltOff, saltLen, prk, (short) 0); + //HMAC_expand + return hkdfExpand(prk, (short) 0, (short) 32, info, infoOff, infoLen, out, outOff, outLen); + } + + private short hkdfExtract(byte[] ikm, short ikmOff, short ikmLen, byte[] salt, short saltOff, short saltLen, + byte[] out, short off) { + // https://tools.ietf.org/html/rfc5869#section-2.2 + HMACKey hmacKey = createHMACKey(salt, saltOff, saltLen); + hmacSignature.init(hmacKey, Signature.MODE_SIGN); + return hmacSignature.sign(ikm, ikmOff, ikmLen, out, off); + } + + private short hkdfExpand(byte[] prk, short prkOff, short prkLen, byte[] info, short infoOff, short infoLen, + byte[] out, short outOff, short outLen) { + // https://tools.ietf.org/html/rfc5869#section-2.3 + short digestLen = (short) 32; // SHA256 digest length. + // Calculate no of iterations N. + short n = (short) ((outLen + digestLen - 1) / digestLen); + if (n > 255) { + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + } + HMACKey hmacKey = createHMACKey(prk, prkOff, prkLen); + byte[] previousOutput = new byte[32]; // Length of output 32. + byte[] cnt = {(byte) 0}; + short bytesCopied = 0; + short len = 0; + for (short i = 0; i < n; i++) { + cnt[0]++; + hmacSignature.init(hmacKey, Signature.MODE_SIGN); + if (i != 0) + hmacSignature.update(previousOutput, (short) 0, (short) 32); + hmacSignature.update(info, infoOff, infoLen); + len = hmacSignature.sign(cnt, (short) 0, (short) 1, previousOutput, (short) 0); + if ((short) (bytesCopied + len) > outLen) { + len = (short) (outLen - bytesCopied); + } + Util.arrayCopyNonAtomic(previousOutput, (short) 0, out, (short) (outOff + bytesCopied), len); + bytesCopied += len; + } + return outLen; + } + + @Override + public short ecdhKeyAgreement(byte[] privKey, short privKeyOff, short privKeyLen, byte[] publicKey, short publicKeyOff, + short publicKeyLen, byte[] secret, short secretOff) { + keyAgreement.init(createEcKey(privKey, privKeyOff, privKeyLen)); + return keyAgreement.generateSecret(publicKey, publicKeyOff, publicKeyLen, secret, secretOff); + } + @Override public short hmacSign(byte[] keyBuf, short keyStart, short keyLength, byte[] data, short dataStart, short dataLength, byte[] mac, short macStart) { @@ -576,7 +663,7 @@ public short rsaDecipherOAEP256(byte[] secret, short secretStart, short secretLe byte[] inputDataBuf, short inputDataStart, short inputDataLength, byte[] outputDataBuf, short outputDataStart) { KMCipher cipher = createRsaDecipher( - KMType.RSA_OAEP, KMType.SHA2_256, secret, secretStart, secretLength, modBuffer, modOff, + KMType.RSA_OAEP, KMType.SHA1, secret, secretStart, secretLength, modBuffer, modOff, modLength); return cipher.doFinal( inputDataBuf, inputDataStart, inputDataLength, outputDataBuf, outputDataStart); @@ -612,7 +699,7 @@ public KMOperation initSymmetricOperation(byte purpose, byte alg, byte digest, b @Override public KMOperation initAsymmetricOperation(byte purpose, byte alg, byte padding, byte digest, - byte[] privKeyBuf, short privKeyStart, short privKeyLength, + byte mgfDigest, byte[] privKeyBuf, short privKeyStart, short privKeyLength, byte[] pubModBuf, short pubModStart, short pubModLength) { if (alg == KMType.RSA) { switch (purpose) { @@ -631,7 +718,7 @@ public KMOperation initAsymmetricOperation(byte purpose, byte alg, byte padding, case KMType.DECRYPT: KMCipher decipher = createRsaDecipher( - padding, digest, privKeyBuf, privKeyStart, privKeyLength, pubModBuf, pubModStart, + padding, mgfDigest, privKeyBuf, privKeyStart, privKeyLength, pubModBuf, pubModStart, pubModLength); return new KMOperationImpl(decipher); default: @@ -643,6 +730,10 @@ public KMOperation initAsymmetricOperation(byte purpose, byte alg, byte padding, Signature signer = createEcSigner(digest, privKeyBuf, privKeyStart, privKeyLength); return new KMOperationImpl(signer); + case KMType.AGREE_KEY: + KeyAgreement keyAgreement = + createKeyAgreement(privKeyBuf, privKeyStart, privKeyLength); + return new KMOperationImpl(keyAgreement); default: KMException.throwIt(KMError.UNSUPPORTED_PURPOSE); } @@ -671,7 +762,25 @@ public KMCipher createRsaDecipher(short padding, short digest, byte[] secret, sh return inst; } - private KMCipher createRsaOAEP256Cipher(byte mode, byte digest, + private MGF1ParameterSpec getMGF1ParamSpec(byte mgfDigest) { + switch (mgfDigest) { + case KMType.SHA1: + return MGF1ParameterSpec.SHA1; + case KMType.SHA2_256: + return MGF1ParameterSpec.SHA256; + case KMType.SHA2_224: + return MGF1ParameterSpec.SHA224; + case KMType.SHA2_384: + return MGF1ParameterSpec.SHA384; + case KMType.SHA2_512: + return MGF1ParameterSpec.SHA512; + default: + KMException.throwIt(KMError.UNSUPPORTED_DIGEST); + } + return null; + } + + private KMCipher createRsaOAEP256Cipher(byte mode, byte mgfDigest, byte[] secret, short secretStart, short secretLen, byte[] modBuffer, short modOff, short modLength) { // Convert byte arrays into keys @@ -692,14 +801,8 @@ private KMCipher createRsaOAEP256Cipher(byte mode, byte digest, try { KeyFactory kf = KeyFactory.getInstance("RSA"); // Create cipher with oaep padding - OAEPParameterSpec oaepSpec = null; - if (digest == KMType.SHA2_256) { - oaepSpec = new OAEPParameterSpec("SHA-256", "MGF1", - MGF1ParameterSpec.SHA1, PSource.PSpecified.DEFAULT); - } else { - oaepSpec = new OAEPParameterSpec("SHA1", "MGF1", - MGF1ParameterSpec.SHA1, PSource.PSpecified.DEFAULT); - } + OAEPParameterSpec oaepSpec = new OAEPParameterSpec("SHA-256", "MGF1", + getMGF1ParamSpec(mgfDigest), PSource.PSpecified.DEFAULT); rsaCipher = javax.crypto.Cipher.getInstance("RSA/ECB/OAEPPadding", "SunJCE"); if (mode == KMType.ENCRYPT) { RSAPublicKeySpec pubSpec = new RSAPublicKeySpec(modInt, expInt); @@ -778,6 +881,17 @@ private Signature createNoDigestSigner(short padding, return inst; } + public KeyAgreement createKeyAgreement(byte[] secret, short secretStart, + short secretLength) { + ECPrivateKey key = (ECPrivateKey) KeyBuilder + .buildKey(KeyBuilder.TYPE_EC_FP_PRIVATE, KeyBuilder.LENGTH_EC_FP_256, false); + key.setS(secret, secretStart, secretLength); + + KeyAgreement keyAgreement = KeyAgreement.getInstance(KeyAgreement.ALG_EC_SVDP_DH_PLAIN, + false); + keyAgreement.init(key); + return keyAgreement; + } public Signature createEcSigner(short digest, byte[] secret, short secretStart, short secretLength) { @@ -1088,11 +1202,9 @@ private void initEntropyPool(byte[] pool) { // 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; + byte[] countBuf = new byte[AES_BLOCK_SIZE]; + byte[] randBuf = new byte[AES_BLOCK_SIZE]; + short len = AES_BLOCK_SIZE; aesRngKey.setKey(entropyPool, (short) 0); aesRngCipher.init(aesRngKey, Cipher.MODE_ENCRYPT, aesICV, (short) 0, (short) 16); while (length > 0) { @@ -1102,12 +1214,12 @@ public void newRandomNumber(byte[] num, short startOff, short 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); + Util.arrayCopy(rngCounter, (short) 0, countBuf, (short) 0, (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); + countBuf, (short) 0, AES_BLOCK_SIZE, randBuf, (short) 0); // copy the encrypted rngCounter block to buffer passed in the argument - Util.arrayCopy(bufPtr, randBufInd, num, startOff, len); + Util.arrayCopy(randBuf, (short) 0, num, startOff, len); length = (short) (length - len); startOff = (short) (startOff + len); } @@ -1162,11 +1274,13 @@ public void addRngEntropy(byte[] num, short offset, short length) { } } - @Override - public KMAttestationCert getAttestationCert(boolean rsaCert) { - return KMAttestationCertImpl.instance(rsaCert); - } - + /** + * The operation reads the certificate chain from persistent memory. + * + * @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. + */ public short readCertificateChain(byte[] buf, short offset) { short len = Util.getShort(certificateChain, (short) 0); Util.arrayCopyNonAtomic(certificateChain, (short) 2, buf, offset, len); @@ -1174,8 +1288,63 @@ public short readCertificateChain(byte[] buf, short offset) { } @Override - public short getCertificateChainLength() { - return Util.getShort(certificateChain, (short) 0); + public short getAttestationKeyAlgorithm(){ + return KMType.INVALID_VALUE; + } + + @Override + public short getAdditionalCertChainLength() { + return Util.getShort(additionalCertChain, (short) 0); + } + + @Override + public byte[] getAdditionalCertChain() { + return additionalCertChain; + // short length = Util.getShort(additionalCertChain, (short) 0); + // Util.arrayCopyNonAtomic(additionalCertChain, (short) 2, buffer, start, length); + // return length; + } + + public void persistBootCertificateChain(byte[] buf, short offset, short len) { + if ((short) (len + 2) > BCC_MAX_SIZE) { + KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + } + JCSystem.beginTransaction(); + Util.setShort(bcc, (short) 0, (short) len); + Util.arrayCopyNonAtomic(buf, offset, bcc, + (short) 2, len); + JCSystem.commitTransaction(); + } + + @Override + public byte[] getBootCertificateChain() { + return bcc; + } + + @Override + public short rsaSign256Pkcs1( + byte[] secret, + short secretStart, + short secretLength, + byte[] modBuf, + short modStart, + 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,modBuf,modStart,modLength); + return signer.sign(inputDataBuf, inputDataStart, inputDataLength, + outputDataBuf, outputDataStart); + } + + @Override + public short ecSign256(byte[] secret, short secretStart, short secretLength, + byte[] inputDataBuf, short inputDataStart, short inputDataLength, + byte[] outputDataBuf, short outputDataStart){ + Signature signer = createEcSigner(KMType.SHA2_256, secret, secretStart, secretLength); + return signer.sign(inputDataBuf, inputDataStart, inputDataLength, outputDataBuf, outputDataStart); } @Override @@ -1192,23 +1361,74 @@ public short ecSign256(KMAttestationKey attestationKey, outputDataBuf, outputDataStart); } + @Override - public void clearCertificateChain() { + public short ecSign256(KMDeviceUniqueKey deviceUniqueKey, byte[] inputDataBuf, short inputDataStart, + short inputDataLength, byte[] outputDataBuf, short outputDataStart) { + ECPrivateKey key = ((KMECDeviceUniqueKey) deviceUniqueKey).getPrivateKey(); + 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 boolean ecVerify256(byte[] pubKey, short pubKeyOffset, short pubKeyLen, byte[] inputDataBuf, + short inputDataStart, short inputDataLength, byte[] signatureDataBuf, + short signatureDataStart, short signatureDataLen) { + + KeyPair ecKeyPair = new KeyPair(KeyPair.ALG_EC_FP, KeyBuilder.LENGTH_EC_FP_256); + ECPublicKey ecPublicKey = (ECPublicKey) ecKeyPair.getPublic(); + ecPublicKey.setW(pubKey, pubKeyOffset, pubKeyLen); + + Signature signer = Signature + .getInstance(Signature.ALG_ECDSA_SHA_256, false); + signer.init(ecPublicKey, Signature.MODE_VERIFY); + return signer.verify(inputDataBuf, inputDataStart, inputDataLength, + signatureDataBuf, signatureDataStart, signatureDataLen); + } + + + @Override + public void persistAdditionalCertChain(byte[] buf, short offset, short len) { + // Input buffer contains encoded additional certificate chain as shown below. + // AdditionalDKSignatures = { + // + SignerName => DKCertChain + // } + // SignerName = tstr + // DKCertChain = [ + // 2* Certificate // Root -> Leaf. Root is the vendo r + // // self-signed cert, leaf contains DK_pu b + // ] + // Certificate = COSE_Sign1 of a public key + if (len > ADDITIONAL_CERT_CHAIN_MAX_SIZE) { + KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + } JCSystem.beginTransaction(); - Util.arrayFillNonAtomic(certificateChain, (short) 0, CERT_CHAIN_MAX_SIZE, (byte) 0); + Util.setShort(additionalCertChain, (short) 0, (short) len); + Util.arrayCopyNonAtomic(buf, offset, additionalCertChain, + (short) 2, len); JCSystem.commitTransaction(); } - @Override - public void persistPartialCertificateChain(byte[] buf, short offset, - short len, short totalLen) { - // _____________________________________________________ - // | 2 Bytes | 1 Byte | 3 Bytes | Cert1 | Cert2 |... - // |_________|________|_________|_______|________|_______ + /** + * This operation persists the certificate chain in the persistent memory in multiple requests. + * + * @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. + */ + 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 byte string header. - // Next 3 bytes holds the total length of the certificate chain. + // 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. if (totalLen > (short) (CERT_CHAIN_MAX_SIZE - 2)) { KMException.throwIt(KMError.INVALID_INPUT_LENGTH); } @@ -1223,20 +1443,6 @@ public void persistPartialCertificateChain(byte[] buf, short offset, JCSystem.commitTransaction(); } - @Override - public boolean isBootSignalEventSupported() { - return false; - } - - @Override - public boolean isDeviceRebooted() { - return false; - } - - @Override - public void clearDeviceBooted(boolean resetBootFlag) { - } - @Override public void onSave(Element ele) { } @@ -1275,8 +1481,20 @@ public KMMasterKey createMasterKey(short keySizeBits) { } @Override - public KMAttestationKey createAttestationKey(byte[] keyData, short offset, - short length) { + public boolean isAttestationKeyProvisioned(){ + return false; + } + /** + * This function creates an ECKey and initializes the ECPrivateKey with the provided input key + * data. The initialized Key is maintained by the SEProvider. This function should be called only + * while provisioning the attestation key. + * + * @param keyData buffer containing the ec private key. + * @param offset start of the buffer. + * @param length length of the buffer. + * @return An instance of KMAttestationKey. + */ + KMAttestationKey createAttestationKey(byte[] keyData, short offset, short length){ if (attestationKey == null) { // Strongbox supports only P-256 curve for EC key. KeyPair ecKeyPair = new KeyPair(KeyPair.ALG_EC_FP, KeyBuilder.LENGTH_EC_FP_256); @@ -1286,7 +1504,50 @@ public KMAttestationKey createAttestationKey(byte[] keyData, short offset, return (KMAttestationKey) attestationKey; } + private KMDeviceUniqueKey createDeviceUniqueKey(KMECDeviceUniqueKey key, + byte[] pubKey, short pubKeyOff, short pubKeyLen, byte[] privKey, + short privKeyOff, short privKeyLen) { + if (key == null) { + KeyPair ecKeyPair = new KeyPair(KeyPair.ALG_EC_FP, KeyBuilder.LENGTH_EC_FP_256); + key = new KMECDeviceUniqueKey(ecKeyPair); + } + key.setS(privKey, privKeyOff, privKeyLen); + key.setW(pubKey, pubKeyOff, pubKeyLen); + return (KMDeviceUniqueKey) key; + } + @Override + public KMDeviceUniqueKey createDeviceUniqueKey(boolean testMode, + byte[] pubKey, short pubKeyOff, short pubKeyLen, byte[] privKey, + short privKeyOff, short privKeyLen) { + KMDeviceUniqueKey key; + if (testMode) { + key = createDeviceUniqueKey(testKey, pubKey, pubKeyOff, + pubKeyLen, privKey, privKeyOff, privKeyLen); + if (testKey == null) testKey = (KMECDeviceUniqueKey) key; + } else { + key = createDeviceUniqueKey(deviceUniqueKey, pubKey, pubKeyOff, + pubKeyLen, privKey, privKeyOff, privKeyLen); + if (deviceUniqueKey == null) deviceUniqueKey = (KMECDeviceUniqueKey) key; + } + return key; + } + + @Override + public KMDeviceUniqueKey getDeviceUniqueKey(boolean testMode) { + return ((KMDeviceUniqueKey) (testMode ? testKey : deviceUniqueKey)); + } + + /** + * This function creates an HMACKey and initializes the key with the provided input key data. This + * created key is maintained by the SEProvider. This function should be called only while + * provisioing the pre-shared secret. + * + * @param keyData buffer containing the key data. + * @param offset start of the buffer. + * @param length length of the buffer. + * @return An instance of KMPreSharedKey. + */ public KMPreSharedKey createPresharedKey(byte[] keyData, short offset, short length) { short lengthInBits = (short) (length * 8); if ((lengthInBits % 8 != 0) || !(lengthInBits >= 64 && lengthInBits <= 512)) { @@ -1306,7 +1567,11 @@ public KMMasterKey getMasterKey() { return (KMMasterKey) masterKey; } - @Override + /** + * Returns the factory provisioned attestation key if any. + * + * @return Instance of the KMAttestationKey. + */ public KMAttestationKey getAttestationKey() { return (KMAttestationKey) attestationKey; } @@ -1317,7 +1582,183 @@ public KMPreSharedKey getPresharedKey() { } @Override - public void releaseAllOperations() { - //Do nothing. + public short getAttestationId(short tag, byte[] buffer, short start) { + switch(tag){ + // Attestation Id Brand + case KMType.ATTESTATION_ID_BRAND: + Util.arrayCopyNonAtomic(attIdBrand,(short)0,buffer, start,(short)attIdBrand.length); + return (short)attIdBrand.length; + // Attestation Id Device + case KMType.ATTESTATION_ID_DEVICE: + Util.arrayCopyNonAtomic(attIdDevice,(short)0,buffer, start,(short)attIdDevice.length); + return (short)attIdDevice.length; + // Attestation Id Product + case KMType.ATTESTATION_ID_PRODUCT: + Util.arrayCopyNonAtomic(attIdProduct,(short)0,buffer, start,(short)attIdProduct.length); + return (short)attIdProduct.length; + // Attestation Id Serial + case KMType.ATTESTATION_ID_SERIAL: + Util.arrayCopyNonAtomic(attIdSerial,(short)0,buffer, start,(short)attIdSerial.length); + return (short)attIdSerial.length; + // Attestation Id IMEI + case KMType.ATTESTATION_ID_IMEI: + Util.arrayCopyNonAtomic(attIdImei,(short)0,buffer, start,(short)attIdImei.length); + return (short)attIdImei.length; + // Attestation Id MEID + case KMType.ATTESTATION_ID_MEID: + Util.arrayCopyNonAtomic(attIdMeId,(short)0,buffer, start,(short)attIdMeId.length); + return (short)attIdMeId.length; + // Attestation Id Manufacturer + case KMType.ATTESTATION_ID_MANUFACTURER: + Util.arrayCopyNonAtomic(attIdManufacturer,(short)0,buffer, start,(short)attIdManufacturer.length); + return (short)attIdManufacturer.length; + // Attestation Id Model + case KMType.ATTESTATION_ID_MODEL: + Util.arrayCopyNonAtomic(attIdModel,(short)0,buffer, start,(short)attIdModel.length); + return (short)attIdModel.length; + } + return (short)0; } + + public void setAttestationId(short tag, byte[] buffer, short start, short length) { + switch(tag){ + // Attestation Id Brand + case KMType.ATTESTATION_ID_BRAND: + attIdBrand = new byte[length]; + Util.arrayCopyNonAtomic(buffer, (short)start, attIdBrand,(short)0,length); + break; + // Attestation Id Device + case KMType.ATTESTATION_ID_DEVICE: + attIdDevice = new byte[length]; + Util.arrayCopyNonAtomic(buffer, (short)start, attIdDevice,(short)0,length); + break; + // Attestation Id Product + case KMType.ATTESTATION_ID_PRODUCT: + attIdProduct = new byte[length]; + Util.arrayCopyNonAtomic(buffer, (short)start, attIdProduct,(short)0,length); + break; + // Attestation Id Serial + case KMType.ATTESTATION_ID_SERIAL: + attIdSerial = new byte[length]; + Util.arrayCopyNonAtomic(buffer, (short)start, attIdSerial,(short)0,length); + break; + // Attestation Id IMEI + case KMType.ATTESTATION_ID_IMEI: + attIdImei = new byte[length]; + Util.arrayCopyNonAtomic(buffer, (short)start, attIdImei,(short)0,length); + break; + // Attestation Id MEID + case KMType.ATTESTATION_ID_MEID: + attIdMeId = new byte[length]; + Util.arrayCopyNonAtomic(buffer, (short)start, attIdMeId,(short)0,length); + break; + // Attestation Id Manufacturer + case KMType.ATTESTATION_ID_MANUFACTURER: + attIdManufacturer = new byte[length]; + Util.arrayCopyNonAtomic(buffer, (short)start, attIdManufacturer,(short)0,length); + break; + // Attestation Id Model + case KMType.ATTESTATION_ID_MODEL: + attIdModel = new byte[length]; + Util.arrayCopyNonAtomic(buffer, (short)start, attIdModel,(short)0,length); + break; + } + } + + @Override + public void deleteAttestationIds() { + attIdBrand = null; + attIdDevice = null; + attIdProduct = null; + attIdSerial = null; + attIdImei = null; + attIdMeId = null; + attIdManufacturer = null; + attIdModel = null; + } + + + public boolean isPowerReset(){ + boolean flag = false; + if(resetFlag[0] == POWER_RESET_TRUE){ + resetFlag[0] = POWER_RESET_FALSE; + flag = true; + } + return flag; + } + + @Override + public short getVerifiedBootHash(byte[] buffer, short start) { + Util.arrayCopyNonAtomic(verifiedHash,(short)0,buffer,start,(short)verifiedHash.length); + return (short)verifiedHash.length; + } + + @Override + public short getBootKey(byte[] buffer, short start) { + Util.arrayCopyNonAtomic(bootKey,(short) 0, buffer, start, (short)bootKey.length); + return (short)bootKey.length; + } + + @Override + public short getBootState() { + return bootState; + } + + @Override + public boolean isDeviceBootLocked() { + return deviceBootLocked; + } + + @Override + public short getBootPatchLevel(byte[] buffer, short start) { + Util.arrayCopyNonAtomic(bootPatchLevel, (short)0, buffer, start, (short)bootPatchLevel.length); + return (short)bootPatchLevel.length; + } + + public void setVerifiedBootHash(byte[] buffer, short start, short length) { + if(verifiedHash == null){ + verifiedHash = new byte[32]; + } + if(length != 32){ + KMException.throwIt(KMError.UNKNOWN_ERROR); + } + Util.arrayCopyNonAtomic(buffer, start, verifiedHash, (short)0, (short)32); + } + + public void setBootKey(byte[] buffer, short start, short length) { + if(bootKey == null){ + bootKey = new byte[32]; + } + if(length != 32){ + KMException.throwIt(KMError.UNKNOWN_ERROR); + } + Util.arrayCopyNonAtomic(buffer, start, bootKey, (short)0, (short)32); + } + + public void setBootState(short state) { + bootState = state; + } + + public void setDeviceLocked(boolean state) { + deviceBootLocked = state; + } + + public void setBootPatchLevel(byte[] buffer, short start, short length) { + if(bootPatchLevel == null){ + bootPatchLevel = new byte[4]; + } + if(length > 4 || length < 0){ + KMException.throwIt(KMError.UNKNOWN_ERROR); + } + Util.arrayCopyNonAtomic(buffer, start, bootPatchLevel, (short)0, (short) 4); + } + + public void setProvisionLocked(boolean locked) { + isProvisionLocked = locked; + } + + public boolean isProvisionLocked() { + return isProvisionLocked; + } + } diff --git a/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMMasterKey.java b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMMasterKey.java new file mode 100644 index 00000000..6eab5e56 --- /dev/null +++ b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMMasterKey.java @@ -0,0 +1,25 @@ +/* + * 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.seprovider; + +/** + * KMMasterKey is a marker interface and the SE Provider has to implement this interface. Internally + * Masterkey is stored as a Javacard AES key object, which will provide additional security. The + * master key is maintained by the SEProvider. + */ +public interface KMMasterKey { + +} diff --git a/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMOperation.java b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMOperation.java new file mode 100644 index 00000000..b73d58d1 --- /dev/null +++ b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMOperation.java @@ -0,0 +1,54 @@ +/* + * 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.seprovider; + +/** + * KMOperation represents a persistent operation started by keymaster hal's beginOperation function. + * This operation is persistent i.e. it will be stored in non volatile memory of se card. It will be + * returned back to KMSEProvider for the reuse when the operation is finished. + */ +public interface KMOperation { + + // Used for cipher operations + short update(byte[] inputDataBuf, short inputDataStart, short inputDataLength, + byte[] outputDataBuf, short outputDataStart); + + // Used for signature operations + short update(byte[] inputDataBuf, short inputDataStart, short inputDataLength); + + // Used for finishing cipher operations or ecdh keyAgreement. + short finish(byte[] inputDataBuf, short inputDataStart, short inputDataLength, + byte[] outputDataBuf, short outputDataStart); + + // Used for finishing signing operations. + short sign(byte[] inputDataBuf, short inputDataStart, short inputDataLength, + byte[] signBuf, short signStart); + + // Used for finishing verifying operations. + boolean verify(byte[] inputDataBuf, short inputDataStart, short inputDataLength, + byte[] signBuf, short signStart, short signLength); + + // Used for aborting the ongoing operations. + void abort(); + + // Used for AES GCM cipher operation. + void updateAAD(byte[] dataBuf, short dataStart, short dataLength); + + // Used for getting output size before finishing a AES GCM cipher operation. For encryption this will + // include the auth tag which is appended at the end of the encrypted data. For decryption this will be + // size of the decrypted data only. + short getAESGCMOutputSize(short dataSize, short macLength); +} diff --git a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMOperationImpl.java b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMOperationImpl.java similarity index 77% rename from Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMOperationImpl.java rename to Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMOperationImpl.java index 761b388a..715092af 100644 --- a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMOperationImpl.java +++ b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMOperationImpl.java @@ -13,23 +13,33 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.javacard.keymaster; +package com.android.javacard.seprovider; +import javacard.security.KeyAgreement; import javacard.security.Signature; public class KMOperationImpl implements KMOperation { private KMCipher cipher; private Signature signature; + private KeyAgreement keyAgreement; public KMOperationImpl(KMCipher cipher) { this.cipher = cipher; this.signature = null; + this.keyAgreement = null; } public KMOperationImpl(Signature sign) { this.cipher = null; this.signature = sign; + this.keyAgreement = null; + } + + public KMOperationImpl(KeyAgreement keyAgreement) { + this.cipher = null; + this.signature = null; + this.keyAgreement = keyAgreement; } @Override @@ -48,8 +58,16 @@ public short update(byte[] inputDataBuf, short inputDataStart, short inputDataLe @Override public short finish(byte[] inputDataBuf, short inputDataStart, short inputDataLength, byte[] outputDataBuf, short outputDataStart) { - return cipher - .doFinal(inputDataBuf, inputDataStart, inputDataLength, outputDataBuf, outputDataStart); + if (cipher != null) { + return cipher + .doFinal(inputDataBuf, inputDataStart, inputDataLength, outputDataBuf, outputDataStart); + } else if (keyAgreement != null) { + return keyAgreement.generateSecret(inputDataBuf, inputDataStart, inputDataLength, + outputDataBuf, outputDataStart); + } else { + KMException.throwIt(KMError.UNKNOWN_ERROR); + } + return 0; } @Override diff --git a/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMPreSharedKey.java b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMPreSharedKey.java new file mode 100644 index 00000000..86bb1df9 --- /dev/null +++ b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMPreSharedKey.java @@ -0,0 +1,25 @@ +/* + * 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.seprovider; + +/** + * KMPreSharedKey is a marker interface and the SE Provider has to implement this interface. + * Internally Preshared key is stored as a Javacard HMac key object, which will provide additional + * security. The pre-shared key is maintained by the SEProvider. + */ +public interface KMPreSharedKey { + +} diff --git a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMRsa2048NoDigestSignature.java b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMRsa2048NoDigestSignature.java similarity index 82% rename from Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMRsa2048NoDigestSignature.java rename to Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMRsa2048NoDigestSignature.java index 573be574..f9e8875d 100644 --- a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMRsa2048NoDigestSignature.java +++ b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMRsa2048NoDigestSignature.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.javacard.keymaster; +package com.android.javacard.seprovider; import javacard.framework.Util; import javacard.security.CryptoException; @@ -130,16 +130,37 @@ private boolean isValidData(byte[] buf, short start, short len) { if (len > 256) { return false; } else if (len == 256) { - short v = KMInteger.unsignedByteArrayCompare(buf, start, rsaModulus, (short) 0, len); + short v = unsignedByteArrayCompare(buf, start, rsaModulus, (short) 0, len); if (v > 0) { return false; } } } else {//pkcs1 no digest if (len > 245) { + KMException.throwIt(KMError.INVALID_INPUT_LENGTH); return false; } } return true; } + + private byte unsignedByteArrayCompare(byte[] a1, short offset1, byte[] a2, short offset2, + short length) { + byte count = (byte) 0; + short val1 = (short) 0; + short val2 = (short) 0; + + for (; count < length; count++) { + val1 = (short) (a1[(short) (count + offset1)] & 0x00FF); + val2 = (short) (a2[(short) (count + offset2)] & 0x00FF); + + if (val1 < val2) { + return -1; + } + if (val1 > val2) { + return 1; + } + } + return 0; + } } diff --git a/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMSEProvider.java b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMSEProvider.java new file mode 100644 index 00000000..d23d7f15 --- /dev/null +++ b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMSEProvider.java @@ -0,0 +1,691 @@ +/* + * 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.seprovider; + +/** + * 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 extends KMUpgradable { + + /** + * Create a symmetric key instance. If the algorithm and/or keysize are not supported then it + * should throw a CryptoException. + * + * @param alg will be KMType.AES, KMType.DES or KMType.HMAC. + * @param keysize will be 128 or 256 for AES or DES. It can be 64 to 512 (multiple of 8) for + * HMAC. + * @param buf is the buffer in which key has to be returned + * @param startOff is the start offset. + * @return length of the data in the buf. This should match the keysize (in bytes). + */ + short createSymmetricKey(byte alg, short keysize, byte[] buf, short startOff); + + /** + * Create a asymmetric key pair. If the algorithms are not supported then it should throw a + * CryptoException. For RSA the public key exponent must always be 0x010001. The key size of RSA + * key pair must be 2048 bits and key size of EC key pair must be for p256 curve. + * + * @param alg will be KMType.RSA or KMType.EC. + * @param privKeyBuf is the buffer to return the private key exponent in case of RSA or private + * key in case of EC. + * @param privKeyStart is the start offset. + * @param privKeyMaxLength is the maximum length of this private key buffer. + * @param pubModBuf is the buffer to return the modulus in case of RSA or public key in case of + * EC. + * @param pubModStart is the start of offset. + * @param pubModMaxLength is the maximum length of this public key buffer. + * @param lengths is the actual length of the key pair - lengths[0] should be private key and + * lengths[1] should be public key. + */ + void createAsymmetricKey( + byte alg, + byte[] privKeyBuf, + short privKeyStart, + short privKeyMaxLength, + byte[] pubModBuf, + short pubModStart, + short pubModMaxLength, + short[] lengths); + + /** + * Verify that the imported key is valid. If the algorithm and/or keysize are not supported then + * it should throw a CryptoException. + * + * @param alg will be KMType.AES, KMType.DES or KMType.HMAC. + * @param keysize will be 128 or 256 for AES or DES. It can be 64 to 512 (multiple of 8) for + * HMAC. + * @param buf is the buffer that contains the symmetric key. + * @param startOff is the start offset. + * @param length of the data in the buf. This should match the keysize (in bytes). + * @return true if the symmetric key is supported and valid. + */ + boolean importSymmetricKey(byte alg, short keysize, byte[] buf, short startOff, short length); + + /** + * Validate that the imported asymmetric key pair is valid. For RSA the public key exponent must + * always be 0x010001. The key size of RSA key pair must be 2048 bits and key size of EC key pair + * must be for p256 curve. If the algorithms are not supported then it should throw a + * CryptoException. + * + * @param alg will be KMType.RSA or KMType.EC. + * @param privKeyBuf is the buffer that contains the private key exponent in case of RSA or + * private key in case of EC. + * @param privKeyStart is the start offset. + * @param privKeyLength is the length of this private key buffer. + * @param pubModBuf is the buffer that contains the modulus in case of RSA or public key in case + * of EC. + * @param pubModStart is the start of offset. + * @param pubModLength is the length of this public key buffer. + * @return true if the key pair is supported and valid. + */ + boolean importAsymmetricKey( + byte alg, + byte[] privKeyBuf, + short privKeyStart, + short privKeyLength, + byte[] pubModBuf, + short pubModStart, + short pubModLength); + + /** + * This is a oneshot operation that generates random number of desired length. + * + * @param num is the buffer in which random number is returned to the applet. + * @param offset is start of the buffer. + * @param length indicates the size of buffer and desired length of random number in bytes. + */ + void newRandomNumber(byte[] num, short offset, short length); + + /** + * This is a oneshot operation that adds the entropy to the entropy pool. This operation + * corresponds to addRndEntropy command. This method may ignore the added entropy value if the SE + * provider does not support it. + * + * @param num is the buffer in which entropy value is given. + * @param offset is start of the buffer. + * @param length length of the buffer. + */ + void addRngEntropy(byte[] num, short offset, short length); + + /** + * This is a oneshot operation that generates and returns back a true random number. + * + * @param num is the buffer in which entropy value is returned. + * @param offset is start of the buffer. + * @param length length of the buffer. + */ + void getTrueRandomNumber(byte[] num, short offset, short length); + + /** + * This is a oneshot operation that performs encryption operation using AES GCM algorithm. It + * throws CryptoException if algorithm is not supported or if tag length is not equal to 16 or + * nonce length is not equal to 12. + * + * @param aesKey is the buffer that contains 128 bit or 256 bit aes key used to encrypt. + * @param aesKeyStart is the start in aes key buffer. + * @param aesKeyLen is the length of aes key buffer in bytes (16 or 32 bytes). + * @param data is the buffer that contains data to encrypt. + * @param dataStart is the start of the data buffer. + * @param dataLen is the length of the data buffer. + * @param encData is the buffer of the output encrypted data. + * @param encDataStart is the start of the encrypted data buffer. + * @param nonce is the buffer of nonce. + * @param nonceStart is the start of the nonce buffer. + * @param nonceLen is the length of the nonce buffer. + * @param authData is the authentication data buffer. + * @param authDataStart is the start of the authentication buffer. + * @param authDataLen is the length of the authentication buffer. + * @param authTag is the buffer to output authentication tag. + * @param authTagStart is the start of the buffer. + * @param authTagLen is the length of the buffer. + * @return length of the encrypted data. + */ + short aesGCMEncrypt( + byte[] aesKey, + short aesKeyStart, + short aesKeyLen, + byte[] data, + short dataStart, + short dataLen, + byte[] encData, + short encDataStart, + byte[] nonce, + short nonceStart, + short nonceLen, + byte[] authData, + short authDataStart, + short authDataLen, + byte[] authTag, + short authTagStart, + short authTagLen); + + /** + * This is a oneshot operation that performs decryption operation using AES GCM algorithm. It + * throws CryptoException if algorithm is not supported. + * + * @param aesKey is the buffer that contains 128 bit or 256 bit aes key used to encrypt. + * @param aesKeyStart is the start in aes key buffer. + * @param aesKeyLen is the length of aes key buffer in bytes (16 or 32 bytes). + * @param encData is the buffer of the input encrypted data. + * @param encDataStart is the start of the encrypted data buffer. + * @param encDataLen is the length of the data buffer. + * @param data is the buffer that contains output decrypted data. + * @param dataStart is the start of the data buffer. + * @param nonce is the buffer of nonce. + * @param nonceStart is the start of the nonce buffer. + * @param nonceLen is the length of the nonce buffer. + * @param authData is the authentication data buffer. + * @param authDataStart is the start of the authentication buffer. + * @param authDataLen is the length of the authentication buffer. + * @param authTag is the buffer to output authentication tag. + * @param authTagStart is the start of the buffer. + * @param authTagLen is the length of the buffer. + * @return true if the authentication is valid. + */ + boolean aesGCMDecrypt( + byte[] aesKey, + short aesKeyStart, + short aesKeyLen, + byte[] encData, + short encDataStart, + short encDataLen, + byte[] data, + short dataStart, + byte[] nonce, + short nonceStart, + short nonceLen, + byte[] authData, + short authDataStart, + short authDataLen, + byte[] authTag, + short authTagStart, + short authTagLen); + + /** + * This is a oneshot operation that performs key derivation function using cmac kdf (CKDF) as + * defined in android keymaster hal definition. + * + * @param hmacKey of pre-shared key. + * @param label is the label to be used for ckdf. + * @param labelStart is the start of label. + * @param labelLen is the length of the label. + * @param context is the context to be used for ckdf. + * @param contextStart is the start of the context + * @param contextLength is the length of the context + * @param key is the output buffer to return the derived key + * @param keyStart is the start of the output buffer. + * @return length of the derived key buffer in bytes. + */ + short cmacKDF( + KMPreSharedKey hmacKey, + byte[] label, + short labelStart, + short labelLen, + byte[] context, + short contextStart, + short contextLength, + byte[] key, + short keyStart); + + /** + * This is a oneshot operation that signs the data using hmac algorithm. + * + * @param keyBuf is the buffer with hmac key. + * @param keyStart is the start of the buffer. + * @param keyLength is the length of the buffer which will be in bytes from 8 to 64. + * @param data is the buffer containing data to be signed. + * @param dataStart is the start of the data. + * @param dataLength is the length of the data. + * @param signature is the output signature buffer + * @param signatureStart is the start of the signature + * @return length of the signature buffer in bytes. + */ + short hmacSign( + byte[] keyBuf, + short keyStart, + short keyLength, + byte[] data, + short dataStart, + short dataLength, + byte[] signature, + short signatureStart); + + /** + * This is a oneshot operation that signs the data using hmac algorithm. This is used to derive + * the key, which is used to encrypt the keyblob. + * + * @param masterkey of masterkey. + * @param data is the buffer containing data to be signed. + * @param dataStart is the start of the data. + * @param dataLength is the length of the data. + * @param signature is the output signature buffer + * @param signatureStart is the start of the signature + * @return length of the signature buffer in bytes. + */ + short hmacKDF( + KMMasterKey masterkey, + byte[] data, + short dataStart, + short dataLength, + byte[] signature, + short signatureStart); + + /** + * This is a oneshot operation that verifies the signature using hmac algorithm. + * + * @param keyBuf is the buffer with hmac key. + * @param keyStart is the start of the buffer. + * @param keyLength is the length of the buffer which will be in bytes from 8 to 64. + * @param data is the buffer containing data. + * @param dataStart is the start of the data. + * @param dataLength is the length of the data. + * @param signature is the signature buffer. + * @param signatureStart is the start of the signature buffer. + * @param signatureLen is the length of the signature buffer in bytes. + * @return true if the signature matches. + */ + boolean hmacVerify( + byte[] keyBuf, + short keyStart, + short keyLength, + byte[] data, + short dataStart, + short dataLength, + byte[] signature, + short signatureStart, + short signatureLen); + + /** + * This is a oneshot operation that decrypts the data using RSA algorithm with oaep256 padding. + * The public exponent is always 0x010001. It throws CryptoException if OAEP encoding validation + * fails. + * + * @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 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 input data buffer in bytes. + * @param outputDataBuf is the output buffer that contains the decrypted data. + * @param outputDataStart is the start of the output data buffer. + * @return length of the decrypted data. + */ + short rsaDecipherOAEP256( + byte[] privExp, + short privExpStart, + short privExpLength, + byte[] modBuffer, + short modOff, + short modLength, + byte[] inputDataBuf, + short inputDataStart, + short inputDataLength, + byte[] outputDataBuf, + short outputDataStart); + + /** + * This is a oneshot operation that signs the data using EC private key. + * + * @param ecPrivKey of KMAttestationKey. + * @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. + * @param outputDataBuf is the output buffer that contains the signature. + * @param outputDataStart is the start of the output data buffer. + * @return length of the decrypted data. + */ + short ecSign256( + KMAttestationKey ecPrivKey, + byte[] inputDataBuf, + short inputDataStart, + short inputDataLength, + byte[] outputDataBuf, + short outputDataStart); + + /** + * Implementation of HKDF as per RFC5869 https://datatracker.ietf.org/doc/html/rfc5869#section-2 + * + * @param ikm is the buffer containing input key material. + * @param ikmOff is the start of the input key. + * @param ikmLen is the length of the input key. + * @param salt is the buffer containing the salt. + * @param saltOff is the start of the salt buffer. + * @param saltLen is the length of the salt buffer. + * @param info is the buffer containing the application specific information + * @param infoOff is the start of the info buffer. + * @param infoLen is the length of the info buffer. + * @param out is the output buffer. + * @param outOff is the start of the output buffer. + * @param outLen is the length of the expected out buffer. + * @return Length of the out buffer which is outLen. + */ + short hkdf( + byte[] ikm, + short ikmOff, + short ikmLen, + byte[] salt, + short saltOff, + short saltLen, + byte[] info, + short infoOff, + short infoLen, + byte[] out, + short outOff, + short outLen); + + /** + * This function performs ECDH key agreement and generates a secret. + * + * @param privKey is the buffer containing the private key from first party. + * @param privKeyOff is the offset of the private key buffer. + * @param privKeyLen is the length of the private key buffer. + * @param publicKey is the buffer containing the public key from second party. + * @param publicKeyOff is the offset of the public key buffer. + * @param publicKeyLen is the length of the public key buffer. + * @param secret is the output buffer. + * @param secretOff is the offset of the output buffer. + * @return The length of the secret. + */ + short ecdhKeyAgreement( + byte[] privKey, + short privKeyOff, + short privKeyLen, + byte[] publicKey, + short publicKeyOff, + short publicKeyLen, + byte[] secret, + short secretOff); + + /** + * This is a oneshort operation that verifies the data using EC public key + * + * @param pubKey is the public key buffer. + * @param pubKeyOffset is the start of the public key buffer. + * @param pubKeyLen is the length of the public key. + * @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 input data buffer in bytes. + * @param signatureDataBuf is the buffer the signature input data. + * @param signatureDataStart is the start of the signature input data. + * @param signatureDataLen is the length of the signature input data. + * @return true if verification is successful, otherwise false. + */ + boolean ecVerify256( + byte[] pubKey, + short pubKeyOffset, + short pubKeyLen, + byte[] inputDataBuf, + short inputDataStart, + short inputDataLength, + byte[] signatureDataBuf, + short signatureDataStart, + short signatureDataLen); + + /** + * This is a oneshot operation that signs the data using device unique key. + * + * @param ecPrivKey instance of KMECDeviceUniqueKey to sign the input data. + * @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 input data buffer in bytes. + * @param outputDataBuf is the output buffer that contains the signature. + * @param outputDataStart is the start of the output data buffer. + * @return length of the decrypted data. + */ + short ecSign256( + KMDeviceUniqueKey ecPrivKey, + byte[] inputDataBuf, + short inputDataStart, + short inputDataLength, + byte[] outputDataBuf, + short outputDataStart); + + short ecSign256(byte[] secret, short secretStart, short secretLength, + byte[] inputDataBuf, short inputDataStart, short inputDataLength, + byte[] outputDataBuf, short outputDataStart); + + short rsaSign256Pkcs1( + byte[] secret, + short secretStart, + short secretLength, + byte[] modBuf, + short modStart, + short modLength, + byte[] inputDataBuf, + short inputDataStart, + short inputDataLength, + byte[] outputDataBuf, + short outputDataStart); + + /** + * This creates a persistent operation for signing, verify, encryption and decryption using HMAC, + * AES and DES algorithms when keymaster hal's beginOperation function is executed. The + * KMOperation instance can be reclaimed by the seProvider when KMOperation is finished or + * aborted. It throws CryptoException if algorithm is not supported. + * + * @param purpose is KMType.ENCRYPT or KMType.DECRYPT for AES and DES algorithm. It will be + * KMType.SIGN and KMType.VERIFY for HMAC algorithm + * @param alg is KMType.HMAC, KMType.AES or KMType.DES. + * @param digest is KMType.SHA2_256 in case of HMAC else it will be KMType.DIGEST_NONE. + * @param padding is KMType.PADDING_NONE or KMType.PKCS7 (in case of AES and DES). + * @param blockMode is KMType.CTR, KMType.GCM. KMType.CBC or KMType.ECB for AES or DES else it is + * 0. + * @param keyBuf is aes, des or hmac key buffer. + * @param keyStart is the start of the key buffer. + * @param keyLength is the length of the key buffer. + * @param ivBuf is the iv buffer (in case on AES and DES algorithm without ECB mode) + * @param ivStart is the start of the iv buffer. + * @param ivLength is the length of the iv buffer. It will be zero in case of HMAC and AES/DES + * with ECB mode. + * @param macLength is the mac length in case of signing operation for hmac algorithm. + * @return KMOperation instance. + */ + 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); + + /** + * This creates a persistent operation for signing, verify, encryption and decryption using RSA + * and EC algorithms when keymaster hal's beginOperation function is executed. For RSA the public + * exponent is always 0x0100101. For EC the curve is always p256. The KMOperation instance can be + * reclaimed by the seProvider when KMOperation is finished or aborted. It throws CryptoException + * if algorithm is not supported. + * + * @param purpose is KMType.ENCRYPT or KMType.DECRYPT for RSA. It will be * KMType.SIGN and + * KMType.VERIFY for RSA and EC algorithms. + * @param alg is KMType.RSA or KMType.EC algorithms. + * @param padding is KMType.PADDING_NONE or KMType.RSA_OAEP, KMType.RSA_PKCS1_1_5_ENCRYPT, + * KMType.RSA_PKCS1_1_5_SIGN or KMType.RSA_PSS. + * @param digest is KMType.DIGEST_NONE or KMType.SHA2_256. + * @param mgfDigest is the MGF digest. + * @param privKeyBuf is the private key in case of EC or private key exponent is case of RSA. + * @param privKeyStart is the start of the private key. + * @param privKeyLength is the length of the private key. + * @param pubModBuf is the modulus (in case of RSA) or public key (in case of EC). + * @param pubModStart is the start of the modulus. + * @param pubModLength is the length of the modulus. + * @return KMOperation instance that can be executed. + */ + KMOperation initAsymmetricOperation( + byte purpose, + byte alg, + byte padding, + byte digest, + byte mgfDigest, + byte[] privKeyBuf, + short privKeyStart, + short privKeyLength, + byte[] pubModBuf, + short pubModStart, + short pubModLength); + + /** + * This function tells if applet is upgrading or not. + * + * @return true if upgrading, otherwise false. + */ + boolean isUpgrading(); + + /** + * This function generates an AES Key of keySizeBits, which is used as an master key. This + * generated key is maintained by the SEProvider. This function should be called only once at the + * time of installation. + * + * @param keySizeBits key size in bits. + * @return An instance of KMMasterKey. + */ + KMMasterKey createMasterKey(short keySizeBits); + + /** + * Returns the master key. + * + * @return Instance of the KMMasterKey + */ + KMMasterKey getMasterKey(); + + /** + * Returns true if factory provisioned attestation key is supported. + */ + boolean isAttestationKeyProvisioned(); + + /** + * Returns algorithm type of the attestation key. It can be KMType.EC or KMType.RSA if the + * attestation key is provisioned in the factory. + */ + short getAttestationKeyAlgorithm(); + + /** + * Returns the preshared key. + * + * @return Instance of the KMPreSharedKey. + */ + KMPreSharedKey getPresharedKey(); + + /** + * Returns the value of the attestation id. + * + * @param tag - attestation id tag key as defined KMType. + * @param buffer - memorey buffer in which value of the id must be copied + * @param start - start offset in the buffer + * @return length - length of the returned attestation id value. + */ + short getAttestationId(short tag, byte[] buffer, short start); + + /** + * Delete the attestation ids permanently. + */ + void deleteAttestationIds(); + + /** + * Get Verified Boot hash. Part of RoT. Part of data sent by the aosp bootloader. + */ + short getVerifiedBootHash(byte[] buffer, short start); + + /** + * Get Boot Key. Part of RoT. Part of data sent by the aosp bootloader. + */ + short getBootKey(byte[] buffer, short start); + + /** + * Get Boot state. Part of RoT. Part of data sent by the aosp bootloader. + */ + short getBootState(); + + /** + * Returns true if device bootloader is locked. Part of RoT. Part of data sent by the aosp + * bootloader. + */ + boolean isDeviceBootLocked(); + + /** + * Get Boot patch level. Part of data sent by the aosp bootloader. + */ + short getBootPatchLevel(byte[] buffer, short start); + + /** + * Creates an ECKey instance and sets the public and private keys to it. + * + * @param testMode to indicate if current execution is for test or production. + * @param pubKey buffer containing the public key. + * @param pubKeyOff public key buffer start offset. + * @param pubKeyLen public key buffer length. + * @param privKey buffer containing the private key. + * @param privKeyOff private key buffer start offset. + * @param privKeyLen private key buffer length. + * @return instance of KMDeviceUniqueKey. + */ + KMDeviceUniqueKey createDeviceUniqueKey(boolean testMode, + byte[] pubKey, short pubKeyOff, short pubKeyLen, + byte[] privKey, short privKeyOff, short privKeyLen); + + /** + * Returns the instance KMDeviceUnique if it is created. + * + * @param testMode Indicates if current execution is for test or production. + * @return instance of KMDeviceUniqueKey if present; null otherwise. + */ + KMDeviceUniqueKey getDeviceUniqueKey(boolean testMode); + + /** + * Persists the additional certificate chain in persistent memory. + * + * @param buf buffer containing the cbor encoded additional certificate chain. + * @param offset start offset of the buffer. + * @param len length of the buffer. + */ + void persistAdditionalCertChain(byte[] buf, short offset, short len); + + /** + * Returns the additional certificate chain length. + * + * @return length of the encoded additional certificate chain. + */ + short getAdditionalCertChainLength(); + + /** + * Returns the additional certificate chain. + * + * @return additional cert chain. + */ + byte[] getAdditionalCertChain(); + + + /** + * Returns the boot certificate chain. + * + * @return boot certificate chain. + */ + byte[] getBootCertificateChain(); + + public boolean isProvisionLocked(); + +} diff --git a/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMType.java b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMType.java new file mode 100644 index 00000000..acae1362 --- /dev/null +++ b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMType.java @@ -0,0 +1,348 @@ +/* + * 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.seprovider; + +/** + * This class declares all types, tag types, and tag keys. It also establishes basic structure of + * any KMType i.e. struct{byte type, short length, value} where value can any of the KMType. Also, + * KMType refers to transient memory heap in the repository. Finally KMType's subtypes are singleton + * prototype objects which just cast the structure over contiguous memory buffer. + */ +public abstract class KMType { + + public static final short INVALID_VALUE = (short) 0x8000; + protected static final byte TLV_HEADER_SIZE = 3; + + // Types + public static final byte BYTE_BLOB_TYPE = 0x01; + public static final byte INTEGER_TYPE = 0x02; + public static final byte ENUM_TYPE = 0x03; + public static final byte TAG_TYPE = 0x04; + public static final byte ARRAY_TYPE = 0x05; + public static final byte KEY_PARAM_TYPE = 0x06; + public static final byte KEY_CHAR_TYPE = 0x07; + public static final byte HW_AUTH_TOKEN_TYPE = 0x08; + public static final byte VERIFICATION_TOKEN_TYPE = 0x09; + public static final byte HMAC_SHARING_PARAM_TYPE = 0x0A; + public static final byte X509_CERT = 0x0B; + public static final byte NEG_INTEGER_TYPE = 0x0C; + public static final byte TEXT_STRING_TYPE = 0x0D; + public static final byte MAP_TYPE = 0x0E; + public static final byte COSE_KEY_TYPE = 0x0F; + public static final byte COSE_PAIR_TAG_TYPE = 0x10; + public static final byte COSE_PAIR_INT_TAG_TYPE = 0x20; + public static final byte COSE_PAIR_NEG_INT_TAG_TYPE = 0x30; + public static final byte COSE_PAIR_BYTE_BLOB_TAG_TYPE = 0x40; + public static final byte COSE_PAIR_COSE_KEY_TAG_TYPE = 0x60; + public static final byte COSE_PAIR_SIMPLE_VALUE_TAG_TYPE = 0x70; + public static final byte COSE_PAIR_TEXT_STR_TAG_TYPE = (byte) 0x80; + public static final byte SIMPLE_VALUE_TYPE = (byte) 0x90; + public static final byte COSE_HEADERS_TYPE = (byte) 0xA0; + public static final byte COSE_CERT_PAYLOAD_TYPE = (byte) 0xB0; + // Tag Types + public static final short INVALID_TAG = 0x0000; + public static final short ENUM_TAG = 0x1000; + public static final short ENUM_ARRAY_TAG = 0x2000; + public static final short UINT_TAG = 0x3000; + public static final short UINT_ARRAY_TAG = 0x4000; + public static final short ULONG_TAG = 0x5000; + public static final short DATE_TAG = 0x6000; + public static final short BOOL_TAG = 0x7000; + public static final short BIGNUM_TAG = (short) 0x8000; + public static final short BYTES_TAG = (short) 0x9000; + public static final short ULONG_ARRAY_TAG = (short) 0xA000; + public static final short TAG_TYPE_MASK = (short) 0xF000; + + // Enum Tag + // Internal tags + public static final short RULE = 0x7FFF; + public static final byte IGNORE_INVALID_TAGS = 0x00; + public static final byte FAIL_ON_INVALID_TAGS = 0x01; + + // Algorithm Enum Tag key and values + public static final short ALGORITHM = 0x0002; + public static final byte RSA = 0x01; + public static final byte DES = 0x21; + public static final byte EC = 0x03; + public static final byte AES = 0x20; + public static final byte HMAC = (byte) 0x80; + + // EcCurve Enum Tag key and values. + public static final short ECCURVE = 0x000A; + public static final byte P_224 = 0x00; + public static final byte P_256 = 0x01; + public static final byte P_384 = 0x02; + public static final byte P_521 = 0x03; + + // KeyBlobUsageRequirements Enum Tag key and values. + public static final short BLOB_USAGE_REQ = 0x012D; + public static final byte STANDALONE = 0x00; + public static final byte REQUIRES_FILE_SYSTEM = 0x01; + + // HardwareAuthenticatorType Enum Tag key and values. + public static final short USER_AUTH_TYPE = 0x01F8; + public static final byte USER_AUTH_NONE = 0x00; + public static final byte PASSWORD = 0x01; + public static final byte FINGERPRINT = 0x02; + public static final byte BOTH = 0x03; + // have to be power of 2 + public static final byte ANY = (byte) 0xFF; + + // Origin Enum Tag key and values. + public static final short ORIGIN = 0x02BE; + public static final byte GENERATED = 0x00; + public static final byte DERIVED = 0x01; + public static final byte IMPORTED = 0x02; + public static final byte UNKNOWN = 0x03; + public static final byte SECURELY_IMPORTED = 0x04; + + // Hardware Type tag key and values + public static final short HARDWARE_TYPE = 0x0130; + public static final byte SOFTWARE = 0x00; + public static final byte TRUSTED_ENVIRONMENT = 0x01; + public static final byte STRONGBOX = 0x02; + + // No Tag + // Derivation Function - No Tag defined + public static final short KEY_DERIVATION_FUNCTION = (short) 0xF001; + public static final byte DERIVATION_NONE = 0x00; + public static final byte RFC5869_SHA256 = 0x01; + public static final byte ISO18033_2_KDF1_SHA1 = 0x02; + public static final byte ISO18033_2_KDF1_SHA256 = 0x03; + public static final byte ISO18033_2_KDF2_SHA1 = 0x04; + public static final byte ISO18033_2_KDF2_SHA256 = 0x05; + + // KeyFormat - No Tag defined. + public static final short KEY_FORMAT = (short) 0xF002; + public static final byte X509 = 0x00; + public static final byte PKCS8 = 0x01; + public static final byte RAW = 0x03; + + // Verified Boot State + public static final short VERIFIED_BOOT_STATE = (short) 0xF003; + public static final byte VERIFIED_BOOT = 0x00; + public static final byte SELF_SIGNED_BOOT = 0x01; + public static final byte UNVERIFIED_BOOT = 0x02; + public static final byte FAILED_BOOT = 0x03; + + // Verified Boot Key + public static final short VERIFIED_BOOT_KEY = (short) 0xF004; + + // Verified Boot Hash + public static final short VERIFIED_BOOT_HASH = (short) 0xF005; + + // Device Locked + public static final short DEVICE_LOCKED = (short) 0xF006; + public static final byte DEVICE_LOCKED_TRUE = 0x01; + public static final byte DEVICE_LOCKED_FALSE = 0x00; + + // Enum Array Tag + // Purpose + public static final short PURPOSE = 0x0001; + public static final byte ENCRYPT = 0x00; + public static final byte DECRYPT = 0x01; + public static final byte SIGN = 0x02; + public static final byte VERIFY = 0x03; + public static final byte DERIVE_KEY = 0x04; + public static final byte WRAP_KEY = 0x05; + public static final byte AGREE_KEY = 0x06; + public static final byte ATTEST_KEY = (byte) 0x07; + // Block mode + public static final short BLOCK_MODE = 0x0004; + public static final byte ECB = 0x01; + public static final byte CBC = 0x02; + public static final byte CTR = 0x03; + public static final byte GCM = 0x20; + + // Digest + public static final short DIGEST = 0x0005; + public static final byte DIGEST_NONE = 0x00; + public static final byte MD5 = 0x01; + public static final byte SHA1 = 0x02; + public static final byte SHA2_224 = 0x03; + public static final byte SHA2_256 = 0x04; + public static final byte SHA2_384 = 0x05; + public static final byte SHA2_512 = 0x06; + + // Padding mode + public static final short PADDING = 0x0006; + public static final byte PADDING_NONE = 0x01; + public static final byte RSA_OAEP = 0x02; + public static final byte RSA_PSS = 0x03; + public static final byte RSA_PKCS1_1_5_ENCRYPT = 0x04; + public static final byte RSA_PKCS1_1_5_SIGN = 0x05; + public static final byte PKCS7 = 0x40; + + // OAEP MGF Digests - only SHA-1 is supported in Javacard + public static final short RSA_OAEP_MGF_DIGEST = 0xCB; + + // Integer Tag - UINT, ULONG and DATE + // UINT tags + // Keysize + public static final short KEYSIZE = 0x0003; + // Min Mac Length + public static final short MIN_MAC_LENGTH = 0x0008; + // Min Seconds between OPS + public static final short MIN_SEC_BETWEEN_OPS = 0x0193; + // Max Uses per Boot + public static final short MAX_USES_PER_BOOT = 0x0194; + // UserId + public static final short USERID = 0x01F5; + // Auth Timeout + public static final short AUTH_TIMEOUT = 0x01F9; + // OS Version + public static final short OS_VERSION = 0x02C1; + // OS Patch Level + public static final short OS_PATCH_LEVEL = 0x02C2; + // Vendor Patch Level + public static final short VENDOR_PATCH_LEVEL = 0x02CE; + // Boot Patch Level + public static final short BOOT_PATCH_LEVEL = 0x02CF; + // Mac Length + public static final short MAC_LENGTH = 0x03EB; + // Usage Count Limit + public static final short USAGE_COUNT_LIMIT = 0x195; + + // ULONG tags + // RSA Public Exponent + public static final short RSA_PUBLIC_EXPONENT = 0x00C8; + + // DATE tags + public static final short ACTIVE_DATETIME = 0x0190; + public static final short ORIGINATION_EXPIRE_DATETIME = 0x0191; + public static final short USAGE_EXPIRE_DATETIME = 0x0192; + public static final short CREATION_DATETIME = 0x02BD;; + public static final short CERTIFICATE_NOT_BEFORE = 0x03F0; + public static final short CERTIFICATE_NOT_AFTER = 0x03F1; + // Integer Array Tags - ULONG_REP and UINT_REP. + // User Secure Id + public static final short USER_SECURE_ID = (short) 0x01F6; + + // Boolean Tag + // Caller Nonce + public static final short CALLER_NONCE = (short) 0x0007; + // Include Unique Id + public static final short INCLUDE_UNIQUE_ID = (short) 0x00CA; + // Bootloader Only + public static final short BOOTLOADER_ONLY = (short) 0x012E; + // Rollback Resistance + public static final short ROLLBACK_RESISTANCE = (short) 0x012F; + // No Auth Required + public static final short NO_AUTH_REQUIRED = (short) 0x01F7; + // Allow While On Body + public static final short ALLOW_WHILE_ON_BODY = (short) 0x01FA; + // Trusted User Presence Required + public static final short TRUSTED_USER_PRESENCE_REQUIRED = (short) 0x01FB; + // Trusted Confirmation Required + public static final short TRUSTED_CONFIRMATION_REQUIRED = (short) 0x01FC; + // Unlocked Device Required + public static final short UNLOCKED_DEVICE_REQUIRED = (short) 0x01FD; + // Reset Since Id Rotation + public static final short RESET_SINCE_ID_ROTATION = (short) 0x03EC; + //Early boot ended. + public static final short EARLY_BOOT_ONLY = (short) 0x0131; + //Device unique attestation. + public static final short DEVICE_UNIQUE_ATTESTATION = (short) 0x02D0; + + // Byte Tag + // Application Id + public static final short APPLICATION_ID = (short) 0x0259; + // Application Data + public static final short APPLICATION_DATA = (short) 0x02BC; + // Root Of Trust + public static final short ROOT_OF_TRUST = (short) 0x02C0; + // Unique Id + public static final short UNIQUE_ID = (short) 0x02C3; + // Attestation Challenge + public static final short ATTESTATION_CHALLENGE = (short) 0x02C4; + // Attestation Application Id + public static final short ATTESTATION_APPLICATION_ID = (short) 0x02C5; + // Attestation Id Brand + public static final short ATTESTATION_ID_BRAND = (short) 0x02C6; + // Attestation Id Device + public static final short ATTESTATION_ID_DEVICE = (short) 0x02C7; + // Attestation Id Product + public static final short ATTESTATION_ID_PRODUCT = (short) 0x02C8; + // Attestation Id Serial + public static final short ATTESTATION_ID_SERIAL = (short) 0x02C9; + // Attestation Id IMEI + public static final short ATTESTATION_ID_IMEI = (short) 0x02CA; + // Attestation Id MEID + public static final short ATTESTATION_ID_MEID = (short) 0x02CB; + // Attestation Id Manufacturer + public static final short ATTESTATION_ID_MANUFACTURER = (short) 0x02CC; + // Attestation Id Model + public static final short ATTESTATION_ID_MODEL = (short) 0x02CD; + // Associated Data + public static final short ASSOCIATED_DATA = (short) 0x03E8; + // Nonce + public static final short NONCE = (short) 0x03E9; + // Confirmation Token + public static final short CONFIRMATION_TOKEN = (short) 0x03ED; + // Serial Number - this is a big num but in applet we handle it as byte blob + public static final short CERTIFICATE_SERIAL_NUM = (short) 0x03EE; + // Subject Name + public static final short CERTIFICATE_SUBJECT_NAME = (short) 0x03EF; + + public static final short LENGTH_FROM_PDU = (short) 0xFFFF; + + public static final byte NO_VALUE = (byte) 0xff; + // Support Curves for Eek Chain validation. + public static final byte RKP_CURVE_P256 = 1; + // Type offsets. + public static final byte KM_TYPE_BASE_OFFSET = 0; + public static final byte KM_ARRAY_OFFSET = KM_TYPE_BASE_OFFSET; + public static final byte KM_BOOL_TAG_OFFSET = KM_TYPE_BASE_OFFSET + 1; + public static final byte KM_BYTE_BLOB_OFFSET = KM_TYPE_BASE_OFFSET + 2; + public static final byte KM_BYTE_TAG_OFFSET = KM_TYPE_BASE_OFFSET + 3; + public static final byte KM_ENUM_OFFSET = KM_TYPE_BASE_OFFSET + 4; + public static final byte KM_ENUM_ARRAY_TAG_OFFSET = KM_TYPE_BASE_OFFSET + 5; + public static final byte KM_ENUM_TAG_OFFSET = KM_TYPE_BASE_OFFSET + 6; + public static final byte KM_HARDWARE_AUTH_TOKEN_OFFSET = KM_TYPE_BASE_OFFSET + 7; + public static final byte KM_HMAC_SHARING_PARAMETERS_OFFSET = KM_TYPE_BASE_OFFSET + 8; + public static final byte KM_INTEGER_OFFSET = KM_TYPE_BASE_OFFSET + 9; + public static final byte KM_INTEGER_ARRAY_TAG_OFFSET = KM_TYPE_BASE_OFFSET + 10; + public static final byte KM_INTEGER_TAG_OFFSET = KM_TYPE_BASE_OFFSET + 11; + public static final byte KM_KEY_CHARACTERISTICS_OFFSET = KM_TYPE_BASE_OFFSET + 12; + public static final byte KM_KEY_PARAMETERS_OFFSET = KM_TYPE_BASE_OFFSET + 13; + public static final byte KM_VERIFICATION_TOKEN_OFFSET = KM_TYPE_BASE_OFFSET + 14; + public static final byte KM_NEG_INTEGER_OFFSET = KM_TYPE_BASE_OFFSET + 15; + public static final byte KM_TEXT_STRING_OFFSET = KM_TYPE_BASE_OFFSET + 16; + public static final byte KM_MAP_OFFSET = KM_TYPE_BASE_OFFSET + 17; + public static final byte KM_COSE_KEY_OFFSET = KM_TYPE_BASE_OFFSET + 18; + public static final byte KM_COSE_KEY_INT_VAL_OFFSET = KM_TYPE_BASE_OFFSET + 19; + public static final byte KM_COSE_KEY_NINT_VAL_OFFSET = KM_TYPE_BASE_OFFSET + 20; + public static final byte KM_COSE_KEY_BYTE_BLOB_VAL_OFFSET = KM_TYPE_BASE_OFFSET + 21; + public static final byte KM_COSE_KEY_COSE_KEY_VAL_OFFSET = KM_TYPE_BASE_OFFSET + 22; + public static final byte KM_COSE_KEY_SIMPLE_VAL_OFFSET = KM_TYPE_BASE_OFFSET + 23; + public static final byte KM_SIMPLE_VALUE_OFFSET = KM_TYPE_BASE_OFFSET + 24; + public static final byte KM_COSE_HEADERS_OFFSET = KM_TYPE_BASE_OFFSET + 25; + public static final byte KM_COSE_KEY_TXT_STR_VAL_OFFSET = KM_TYPE_BASE_OFFSET + 26; + public static final byte KM_COSE_CERT_PAYLOAD_OFFSET = KM_TYPE_BASE_OFFSET + 27; + public static final byte KM_BIGNUM_TAG_OFFSET = KM_TYPE_BASE_OFFSET + 28; + + // Attestation types + public static final byte NO_CERT = 0; + public static final byte ATTESTATION_CERT = 1; + public static final byte SELF_SIGNED_CERT = 2; + public static final byte FAKE_CERT = 3; + // Buffering Mode + public static final byte BUF_NONE = 0; + public static final byte BUF_RSA_NO_DIGEST = 1; + public static final byte BUF_EC_NO_DIGEST = 2; + public static final byte BUF_BLOCK_ALIGN = 3; +} diff --git a/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMUpgradable.java b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMUpgradable.java new file mode 100644 index 00000000..9bca1c8c --- /dev/null +++ b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMUpgradable.java @@ -0,0 +1,30 @@ +/* + * 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.seprovider; + +import org.globalplatform.upgrade.Element; + +public interface KMUpgradable { + + void onSave(Element ele); + + void onRestore(Element ele); + + short getBackupPrimitiveByteCount(); + + short getBackupObjectCount(); + +} diff --git a/Applet/README.md b/Applet/README.md index 76af5b05..9b66ce6f 100644 --- a/Applet/README.md +++ b/Applet/README.md @@ -1,19 +1,17 @@ # JavaCardKeymaster Applet -This directory contains the implementation of the Keymaster 4.1 +This directory contains the implementation of the Keymint 1.0 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. +which mediates 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. + - Keymint 1.0 supported functions for required VTS compliance. + - SharedSecret 1.0 supported functions for required VTS compliance. -#### 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. +# Exceptions + - Generate and Import key with client supplied attestation key is not yet supported. +# Not supported features + - Factory provisioned attestation key will not be supported in this applet. + - Limited usage keys will not be supported in this applet. diff --git a/Applet/build.xml b/Applet/build.xml deleted file mode 100644 index 4a14664e..00000000 --- a/Applet/build.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Applet/src/com/android/javacard/keymaster/KMArray.java b/Applet/src/com/android/javacard/keymaster/KMArray.java index bfa09269..c1540fa7 100644 --- a/Applet/src/com/android/javacard/keymaster/KMArray.java +++ b/Applet/src/com/android/javacard/keymaster/KMArray.java @@ -44,7 +44,7 @@ private static KMArray proto(short ptr) { if (prototype == null) { prototype = new KMArray(); } - instanceTable[KM_ARRAY_OFFSET] = ptr; + KMType.instanceTable[KM_ARRAY_OFFSET] = ptr; return prototype; } @@ -88,9 +88,9 @@ public void add(short index, short objPtr) { ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); } Util.setShort( - heap, - (short) (instanceTable[KM_ARRAY_OFFSET] + TLV_HEADER_SIZE + ARRAY_HEADER_SIZE + (short) (index * 2)), - objPtr); + heap, + (short) (getStartOff() + (short) (index * 2)), + objPtr); } public short get(short index) { @@ -99,22 +99,46 @@ public short get(short index) { ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); } return Util.getShort( - heap, (short) (instanceTable[KM_ARRAY_OFFSET] + TLV_HEADER_SIZE + ARRAY_HEADER_SIZE + (short) (index * 2))); + heap, (short) (getStartOff() + (short) (index * 2))); +} + + public void swap(short index1, short index2) { + short len = length(); + if (index1 >= len || index2 >= len) { + ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); + } + short indexPtr1 = + Util.getShort( + heap, + (short) (instanceTable[KM_ARRAY_OFFSET] + TLV_HEADER_SIZE + ARRAY_HEADER_SIZE + (short) (index1 * 2))); + short indexPtr2 = + Util.getShort( + heap, + (short) (instanceTable[KM_ARRAY_OFFSET] + TLV_HEADER_SIZE + ARRAY_HEADER_SIZE + (short) (index2 * 2))); + Util.setShort( + heap, + (short) (instanceTable[KM_ARRAY_OFFSET] + TLV_HEADER_SIZE + ARRAY_HEADER_SIZE + (short) (index1 * 2)), + indexPtr2); + Util.setShort( + heap, + (short) (instanceTable[KM_ARRAY_OFFSET] + TLV_HEADER_SIZE + ARRAY_HEADER_SIZE + (short) (index2 * 2)), + indexPtr1); } public short containedType() { - return Util.getShort(heap, (short) (instanceTable[KM_ARRAY_OFFSET] + TLV_HEADER_SIZE)); + return Util.getShort(heap, (short) (KMType.instanceTable[KM_ARRAY_OFFSET] + TLV_HEADER_SIZE)); } public short getStartOff() { - return (short) (instanceTable[KM_ARRAY_OFFSET] + TLV_HEADER_SIZE + ARRAY_HEADER_SIZE); + return (short) (KMType.instanceTable[KM_ARRAY_OFFSET] + TLV_HEADER_SIZE + ARRAY_HEADER_SIZE); } public short length() { - return Util.getShort(heap, (short) (instanceTable[KM_ARRAY_OFFSET] + TLV_HEADER_SIZE + 2)); + return Util.getShort(heap, (short) (KMType.instanceTable[KM_ARRAY_OFFSET] + TLV_HEADER_SIZE + 2)); } public byte[] getBuffer() { return heap; } + } diff --git a/Applet/src/com/android/javacard/keymaster/KMBignumTag.java b/Applet/src/com/android/javacard/keymaster/KMBignumTag.java new file mode 100644 index 00000000..7b020314 --- /dev/null +++ b/Applet/src/com/android/javacard/keymaster/KMBignumTag.java @@ -0,0 +1,116 @@ +/* + * 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.Util; + +/** + * KMBignumTag represents BIGNUM Tag Type from android keymaster hal specifications. The tag value of + * this tag is the KMByteBlob pointer i.e. offset of KMByteBlob in memory heap. struct{byte + * TAG_TYPE; short length; struct{short BIGNUM_TAG; short tagKey; short blobPtr}} + */ + +public class KMBignumTag extends KMTag { + + private static KMBignumTag prototype; + + // The allowed tag keys of type bool tag + private static final short[] tags = { + CERTIFICATE_SERIAL_NUM, + }; + + private KMBignumTag() { + } + + private static KMBignumTag proto(short ptr) { + if (prototype == null) { + prototype = new KMBignumTag(); + } + KMType.instanceTable[KM_BIGNUM_TAG_OFFSET] = ptr; + return prototype; + } + + // pointer to an empty instance used as expression + public static short exp() { + short blobPtr = KMByteBlob.exp(); + short ptr = instance(TAG_TYPE, (short) 6); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), BIGNUM_TAG); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), INVALID_TAG); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), blobPtr); + return ptr; + } + + public static short instance(short key) { + if (!validateKey(key)) { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + return instance(key, KMByteBlob.exp()); + } + + public static short instance(short key, short byteBlob) { + if (!validateKey(key)) { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + if (heap[byteBlob] != BYTE_BLOB_TYPE) { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + short ptr = instance(TAG_TYPE, (short) 6); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), BIGNUM_TAG); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), key); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), byteBlob); + return ptr; + } + + public static KMBignumTag cast(short ptr) { + if (heap[ptr] != TAG_TYPE) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + if (Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE)) != BIGNUM_TAG) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + return proto(ptr); + } + + public short getKey() { + return Util.getShort(heap, (short) (KMType.instanceTable[KM_BIGNUM_TAG_OFFSET] + TLV_HEADER_SIZE + 2)); + } + + public short getTagType() { + return KMType.BIGNUM_TAG; + } + + public short getValue() { + return Util.getShort(heap, (short) (KMType.instanceTable[KM_BIGNUM_TAG_OFFSET] + TLV_HEADER_SIZE + 4)); + } + + public short length() { + short blobPtr = Util.getShort(heap, (short) (KMType.instanceTable[KM_BIGNUM_TAG_OFFSET] + TLV_HEADER_SIZE + 4)); + return KMByteBlob.cast(blobPtr).length(); + } + + private static boolean validateKey(short key) { + short index = (short) tags.length; + while (--index >= 0) { + if (tags[index] == key) { + return true; + } + } + return false; + } +} diff --git a/Applet/src/com/android/javacard/keymaster/KMBoolTag.java b/Applet/src/com/android/javacard/keymaster/KMBoolTag.java index 69619c0d..d1e277a0 100644 --- a/Applet/src/com/android/javacard/keymaster/KMBoolTag.java +++ b/Applet/src/com/android/javacard/keymaster/KMBoolTag.java @@ -16,6 +16,7 @@ package com.android.javacard.keymaster; +import com.android.javacard.seprovider.KMException; import javacard.framework.ISO7816; import javacard.framework.ISOException; import javacard.framework.Util; @@ -33,18 +34,18 @@ public class KMBoolTag extends KMTag { // The allowed tag keys of type bool tag. private static final short[] tags = { - CALLER_NONCE, - INCLUDE_UNIQUE_ID, - BOOTLOADER_ONLY, - ROLLBACK_RESISTANCE, - NO_AUTH_REQUIRED, - ALLOW_WHILE_ON_BODY, - TRUSTED_USER_PRESENCE_REQUIRED, - TRUSTED_CONFIRMATION_REQUIRED, - UNLOCKED_DEVICE_REQUIRED, - RESET_SINCE_ID_ROTATION, - EARLY_BOOT_ONLY, - DEVICE_UNIQUE_ATTESTATION + CALLER_NONCE, + INCLUDE_UNIQUE_ID, + BOOTLOADER_ONLY, + ROLLBACK_RESISTANCE, + NO_AUTH_REQUIRED, + ALLOW_WHILE_ON_BODY, + TRUSTED_USER_PRESENCE_REQUIRED, + TRUSTED_CONFIRMATION_REQUIRED, + UNLOCKED_DEVICE_REQUIRED, + RESET_SINCE_ID_ROTATION, + EARLY_BOOT_ONLY, + DEVICE_UNIQUE_ATTESTATION }; private KMBoolTag() { @@ -54,7 +55,7 @@ private static KMBoolTag proto(short ptr) { if (prototype == null) { prototype = new KMBoolTag(); } - instanceTable[KM_BOOL_TAG_OFFSET] = ptr; + KMType.instanceTable[KM_BOOL_TAG_OFFSET] = ptr; return prototype; } @@ -67,7 +68,7 @@ public static short exp() { public static short instance(short key) { if (!validateKey(key)) { - ISOException.throwIt(ISO7816.SW_DATA_INVALID); + KMException.throwIt(KMError.INVALID_TAG); } short ptr = KMType.instance(TAG_TYPE, (short) 5); Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), BOOL_TAG); @@ -88,7 +89,7 @@ public static KMBoolTag cast(short ptr) { } public short getKey() { - return Util.getShort(heap, (short) (instanceTable[KM_BOOL_TAG_OFFSET] + TLV_HEADER_SIZE + 2)); + return Util.getShort(heap, (short) (KMType.instanceTable[KM_BOOL_TAG_OFFSET] + TLV_HEADER_SIZE + 2)); } public short getTagType() { @@ -96,7 +97,7 @@ public short getTagType() { } public byte getVal() { - return heap[(short) (instanceTable[KM_BOOL_TAG_OFFSET] + TLV_HEADER_SIZE + 4)]; + return heap[(short) (KMType.instanceTable[KM_BOOL_TAG_OFFSET] + TLV_HEADER_SIZE + 4)]; } // validate the tag key. @@ -113,4 +114,5 @@ private static boolean validateKey(short key) { public static short[] getTags() { return tags; } + } diff --git a/Applet/src/com/android/javacard/keymaster/KMByteBlob.java b/Applet/src/com/android/javacard/keymaster/KMByteBlob.java index a6f4c529..0d2627d9 100644 --- a/Applet/src/com/android/javacard/keymaster/KMByteBlob.java +++ b/Applet/src/com/android/javacard/keymaster/KMByteBlob.java @@ -26,17 +26,17 @@ * BYTE_BLOB_TYPE; short length; sequence of bytes} */ public class KMByteBlob extends KMType { - + private static short OFFSET_SIZE = 2; private static KMByteBlob prototype; - private KMByteBlob() { + protected KMByteBlob() { } private static KMByteBlob proto(short ptr) { if (prototype == null) { prototype = new KMByteBlob(); } - instanceTable[KM_BYTE_BLOB_OFFSET] = ptr; + KMType.instanceTable[KM_BYTE_BLOB_OFFSET] = ptr; return prototype; } @@ -47,13 +47,16 @@ public static short exp() { // return an empty byte blob instance public static short instance(short length) { - return KMType.instance(BYTE_BLOB_TYPE, length); + short ptr = KMType.instance(BYTE_BLOB_TYPE, (short)(length+2)); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE), (short)(ptr+TLV_HEADER_SIZE+OFFSET_SIZE)); + Util.setShort(heap, (short)(ptr + 1), length); + return ptr; } // byte blob from existing buf public static short instance(byte[] buf, short startOff, short length) { short ptr = instance(length); - Util.arrayCopyNonAtomic(buf, startOff, heap, (short) (ptr + TLV_HEADER_SIZE), length); + Util.arrayCopyNonAtomic(buf, startOff, heap, (short)(ptr+TLV_HEADER_SIZE+OFFSET_SIZE), length); return ptr; } @@ -74,7 +77,7 @@ public void add(short index, byte val) { if (index >= len) { ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); } - heap[(short) (instanceTable[KM_BYTE_BLOB_OFFSET] + TLV_HEADER_SIZE + index)] = val; + heap[(short) (getStartOff() + index)] = val; } // Get the byte @@ -83,17 +86,17 @@ public byte get(short index) { if (index >= len) { ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); } - return heap[(short) (instanceTable[KM_BYTE_BLOB_OFFSET] + TLV_HEADER_SIZE + index)]; + return heap[(short) (getStartOff() + index)]; } // Get the start of blob public short getStartOff() { - return (short) (instanceTable[KM_BYTE_BLOB_OFFSET] + TLV_HEADER_SIZE); + return Util.getShort(heap, (short)(getBaseOffset() + TLV_HEADER_SIZE)); } // Get the length of the blob public short length() { - return Util.getShort(heap, (short) (instanceTable[KM_BYTE_BLOB_OFFSET] + 1)); + return Util.getShort(heap, (short) (getBaseOffset() + 1)); } // Get the buffer pointer in which blob is contained. @@ -112,22 +115,26 @@ public short getValues(byte[] destBuf, short destStart) { } public void setValue(byte[] srcBuf, short srcStart, short srcLength) { - if (length() > srcLength) { + if (length() < srcLength) { ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); } - Util.arrayCopyNonAtomic(srcBuf, srcStart, heap, getStartOff(), length()); + Util.arrayCopyNonAtomic(srcBuf, srcStart, heap, getStartOff(), srcLength); + setLength(srcLength); } public boolean isValid() { - if (length() == 0) { - return false; - } - return true; + return (length() != 0); + } + + public void setStartOff(short offset){ + Util.setShort(heap, (short)(getBaseOffset() + TLV_HEADER_SIZE), offset); + } + + protected short getBaseOffset() { + return instanceTable[KM_BYTE_BLOB_OFFSET]; } - public void decrementLength(short len) { - short length = Util.getShort(heap, (short) (instanceTable[KM_BYTE_BLOB_OFFSET] + 1)); - length = (short) (length - len); - Util.setShort(heap, (short) (instanceTable[KM_BYTE_BLOB_OFFSET] + 1), length); + public void setLength(short len){ + Util.setShort(heap, (short)(getBaseOffset() + 1), len); } } diff --git a/Applet/src/com/android/javacard/keymaster/KMByteTag.java b/Applet/src/com/android/javacard/keymaster/KMByteTag.java index 89401e4f..1677a744 100644 --- a/Applet/src/com/android/javacard/keymaster/KMByteTag.java +++ b/Applet/src/com/android/javacard/keymaster/KMByteTag.java @@ -32,25 +32,27 @@ public class KMByteTag extends KMTag { // The allowed tag keys of type bool tag private static final short[] tags = { - APPLICATION_ID, - APPLICATION_DATA, - ROOT_OF_TRUST, - UNIQUE_ID, - ATTESTATION_CHALLENGE, - ATTESTATION_APPLICATION_ID, - ATTESTATION_ID_BRAND, - ATTESTATION_ID_DEVICE, - ATTESTATION_ID_PRODUCT, - ATTESTATION_ID_SERIAL, - ATTESTATION_ID_IMEI, - ATTESTATION_ID_MEID, - ATTESTATION_ID_MANUFACTURER, - ATTESTATION_ID_MODEL, - ASSOCIATED_DATA, - NONCE, - CONFIRMATION_TOKEN, - VERIFIED_BOOT_KEY, - VERIFIED_BOOT_HASH + APPLICATION_ID, + APPLICATION_DATA, + ROOT_OF_TRUST, + UNIQUE_ID, + ATTESTATION_CHALLENGE, + ATTESTATION_APPLICATION_ID, + ATTESTATION_ID_BRAND, + ATTESTATION_ID_DEVICE, + ATTESTATION_ID_PRODUCT, + ATTESTATION_ID_SERIAL, + ATTESTATION_ID_IMEI, + ATTESTATION_ID_MEID, + ATTESTATION_ID_MANUFACTURER, + ATTESTATION_ID_MODEL, + ASSOCIATED_DATA, + NONCE, + CONFIRMATION_TOKEN, + VERIFIED_BOOT_KEY, + VERIFIED_BOOT_HASH, + CERTIFICATE_SERIAL_NUM, + CERTIFICATE_SUBJECT_NAME, }; private KMByteTag() { @@ -60,7 +62,7 @@ private static KMByteTag proto(short ptr) { if (prototype == null) { prototype = new KMByteTag(); } - instanceTable[KM_BYTE_TAG_OFFSET] = ptr; + KMType.instanceTable[KM_BYTE_TAG_OFFSET] = ptr; return prototype; } @@ -106,7 +108,7 @@ public static KMByteTag cast(short ptr) { } public short getKey() { - return Util.getShort(heap, (short) (instanceTable[KM_BYTE_TAG_OFFSET] + TLV_HEADER_SIZE + 2)); + return Util.getShort(heap, (short) (KMType.instanceTable[KM_BYTE_TAG_OFFSET] + TLV_HEADER_SIZE + 2)); } public short getTagType() { @@ -114,11 +116,11 @@ public short getTagType() { } public short getValue() { - return Util.getShort(heap, (short) (instanceTable[KM_BYTE_TAG_OFFSET] + TLV_HEADER_SIZE + 4)); + return Util.getShort(heap, (short) (KMType.instanceTable[KM_BYTE_TAG_OFFSET] + TLV_HEADER_SIZE + 4)); } public short length() { - short blobPtr = Util.getShort(heap, (short) (instanceTable[KM_BYTE_TAG_OFFSET] + TLV_HEADER_SIZE + 4)); + short blobPtr = Util.getShort(heap, (short) (KMType.instanceTable[KM_BYTE_TAG_OFFSET] + TLV_HEADER_SIZE + 4)); return KMByteBlob.cast(blobPtr).length(); } diff --git a/Applet/src/com/android/javacard/keymaster/KMCose.java b/Applet/src/com/android/javacard/keymaster/KMCose.java new file mode 100644 index 00000000..6720d8df --- /dev/null +++ b/Applet/src/com/android/javacard/keymaster/KMCose.java @@ -0,0 +1,549 @@ +/* + * Copyright(C) 2021 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; + +/** + * This class constructs the Cose messages like CoseKey, CoseMac0, MacStructure, + * CoseSign1, SignStructure, CoseEncrypt, EncryptStructure and ReceipientStructures. + */ +public class KMCose { + //COSE SIGN1 + public static final byte COSE_SIGN1_ENTRY_COUNT = 4; + public static final byte COSE_SIGN1_PROTECTED_PARAMS_OFFSET = 0; + public static final short COSE_SIGN1_UNPROTECTED_PARAMS_OFFSET = 1; + public static final short COSE_SIGN1_PAYLOAD_OFFSET = 2; + public static final short COSE_SIGN1_SIGNATURE_OFFSET = 3; + //COSE MAC0 + public static final short COSE_MAC0_ENTRY_COUNT = 4; + public static final short COSE_MAC0_PROTECTED_PARAMS_OFFSET = 0; + public static final short COSE_MAC0_UNPROTECTED_PARAMS_OFFSET = 1; + public static final short COSE_MAC0_PAYLOAD_OFFSET = 2; + public static final short COSE_MAC0_TAG_OFFSET = 3; + //COSE ENCRYPT + public static final short COSE_ENCRYPT_ENTRY_COUNT = 4; + public static final short COSE_ENCRYPT_STRUCTURE_ENTRY_COUNT = 3; + public static final short COSE_ENCRYPT_RECIPIENT_ENTRY_COUNT = 3; + public static final short COSE_ENCRYPT_PROTECTED_PARAMS_OFFSET = 0; + public static final short COSE_ENCRYPT_UNPROTECTED_PARAMS_OFFSET = 1; + public static final short COSE_ENCRYPT_PAYLOAD_OFFSET = 2; + public static final short COSE_ENCRYPT_RECIPIENTS_OFFSET = 3; + + //COSE Labels + public static final byte COSE_LABEL_ALGORITHM = 1; + public static final byte COSE_LABEL_KEYID = 4; + public static final byte COSE_LABEL_IV = 5; + public static final byte COSE_LABEL_COSE_KEY = (byte) 0xFF; // -1 + + //COSE Algorithms + public static final byte COSE_ALG_AES_GCM_256 = 3; //AES-GCM mode w/ 256-bit key, 128-bit tag. + public static final byte COSE_ALG_HMAC_256 = 5; //HMAC w/ SHA-256 + public static final byte COSE_ALG_ES256 = (byte) 0xF9; // ECDSA w/ SHA-256; -7 + public static final byte COSE_ALG_ECDH_ES_HKDF_256 = (byte) 0xE7; // ECDH-EC+HKDF-256; -25 + + //COSE P256 EC Curve + public static final byte COSE_ECCURVE_256 = 1; + + //COSE key types + public static final byte COSE_KEY_TYPE_EC2 = 2; + public static final byte COSE_KEY_TYPE_SYMMETRIC_KEY = 4; + + //COSE Key Operations + public static final byte COSE_KEY_OP_SIGN = 1; + public static final byte COSE_KEY_OP_VERIFY = 2; + public static final byte COSE_KEY_OP_ENCRYPT = 3; + public static final byte COSE_KEY_OP_DECRYPT = 4; + + // AES GCM + public static final short AES_GCM_NONCE_LENGTH = 12; + public static final short AES_GCM_TAG_SIZE = 16; + public static final short AES_GCM_KEY_SIZE = 32; + public static final short AES_GCM_KEY_SIZE_BITS = 256; + // Cose key parameters. + public static final byte COSE_KEY_KEY_TYPE = 1; + public static final byte COSE_KEY_KEY_ID = 2; + public static final byte COSE_KEY_ALGORITHM = 3; + public static final byte COSE_KEY_KEY_OPS = 4; + public static final byte COSE_KEY_CURVE = -1; + public static final byte COSE_KEY_PUBKEY_X = -2; + public static final byte COSE_KEY_PUBKEY_Y = -3; + public static final byte COSE_KEY_PRIV_KEY = -4; + public static final byte[] COSE_TEST_KEY = {(byte) 0xFF, (byte) 0xFE, (byte) 0xEE, (byte) 0x90}; // -70000 + public static final short COSE_KEY_MAX_SIZE = 4; + + // kdfcontext strings + public static final byte[] client = {0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74}; + public static final byte[] server = {0x73, 0x65, 0x72, 0x76, 0x65, 0x72}; + //Context strings + public static final byte[] MAC_CONTEXT = {0x4d, 0x41, 0x43, 0x30}; // MAC0 + public static final byte[] SIGNATURE1_CONTEXT = + {0x53, 0x69, 0x67, 0x6E, 0x61, 0x74, 0x75, 0x72, 0x65, 0x31}; // Signature1 + public static final byte[] ENCRYPT_CONTEXT = + {0x45, 0x6E, 0x63, 0x72, 0x79, 0x70, 0x74}; // Encrypt + //Empty strings + public static final byte[] EMPTY_MAC_KEY = + {0x45, 0x6d, 0x70, 0x74, 0x79, 0x20, 0x4d, 0x41, 0x43, 0x20, 0x6b, 0x65, 0x79}; // "Empty MAC key" + + // Certificate payload supported keys + public static final byte ISSUER = (byte) 0x01; + public static final byte SUBJECT = (byte) 0x02; + public static final byte[] CODE_HASH = {(byte) 0xFF, (byte) 0xB8, (byte) 0xBB, (byte) 0xAF}; + public static final byte[] CODE_DESCRIPTOR = {(byte) 0xFF, (byte) 0xB8, (byte) 0xBB, (byte) 0xAE}; + public static final byte[] CONFIG_HASH = {(byte) 0xFF, (byte) 0xB8, (byte) 0xBB, (byte) 0xAD}; + public static final byte[] CONFIG_DESCRIPTOR = {(byte) 0xFF, (byte) 0xB8, (byte) 0xBB, (byte) 0xAC}; + public static final byte[] AUTHORITY_HASH = {(byte) 0xFF, (byte) 0xB8, (byte) 0xBB, (byte) 0xAB}; + public static final byte[] AUTHORITY_DESCRIPTOR = {(byte) 0xFF, (byte) 0xB8, (byte) 0xBB, (byte) 0xAA}; + public static final byte[] MODE = {(byte) 0xFF, (byte) 0xB8, (byte) 0xBB, (byte) 0xA9}; + public static final byte[] SUBJECT_PUBLIC_KEY = {(byte) 0xFF, (byte) 0xB8, (byte) 0xBB, (byte) 0xA8}; + public static final byte[] KEY_USAGE = {(byte) 0xFF, (byte) 0xB8, (byte) 0xBB, (byte) 0xA7}; + // text strings + public static final byte[] TEST_ISSUER_NAME = {(byte) 0x49, 0x73, 0x73, 0x75, 0x65, 0x72}; // "Issuer" + public static final byte[] TEST_SUBJECT_NAME = {0x53, 0x75, 0x62, 0x6A, 0x65, 0x63, 0x74}; // "Subject" + public static final byte[] KEY_USAGE_SIGN = {0x20}; // Key usage sign + public static final byte[] MAC_DERIVE_KEY_CTX = + {0x4B, 0x65, 0x79, 0x20, 0x74, 0x6F, 0x20, 0x4D, 0x41, 0x43, 0x20, 0x70, 0x75, 0x62, 0x6C, 0x69, 0x63, + 0x20, 0x6B, 0x65, 0x79, 0x73}; // "Key to MAC public keys" + + /** + * Constructs the Cose MAC structure. + * + * @param protectedHeader Bstr pointer which holds the protected header. + * @param extAad Bstr pointer which holds the external Aad. + * @param payload Bstr pointer which holds the payload of the MAC structure. + * @return KMArray instance of MAC structure. + */ + public static short constructCoseMacStructure(short protectedHeader, short extAad, short payload) { + // Create MAC Structure and compute HMAC as per https://tools.ietf.org/html/rfc8152#section-6.3 + // MAC_structure = [ + // context : "MAC" / "MAC0", + // protected : empty_or_serialized_map, + // external_aad : bstr, + // payload : bstr + // ] + short arrPtr = KMArray.instance(KMCose.COSE_MAC0_ENTRY_COUNT); + // 1 - Context + KMArray.cast(arrPtr).add((short) 0, KMTextString.instance(KMCose.MAC_CONTEXT, (short) 0, + (short) KMCose.MAC_CONTEXT.length)); + // 2 - Protected headers. + KMArray.cast(arrPtr).add((short) 1, protectedHeader); + // 3 - external aad + KMArray.cast(arrPtr).add((short) 2, extAad); + // 4 - payload. + KMArray.cast(arrPtr).add((short) 3, payload); + return arrPtr; + } + + /** + * Constructs the COSE_MAC0 object. + * + * @param protectedHeader Bstr pointer which holds the protected header. + * @param unprotectedHeader Bstr pointer which holds the unprotected header. + * @param payload Bstr pointer which holds the payload of the MAC structure. + * @param tag Bstr pointer which holds the tag value. + * @return KMArray instance of COSE_MAC0 object. + */ + public static short constructCoseMac0(short protectedHeader, short unprotectedHeader, short payload, short tag) { + // Construct Cose_MAC0 + // COSE_Mac0 = [ + // protectedHeader, + // unprotectedHeader, + // payload : bstr / nil, + // tag : bstr, + // ] + short arrPtr = KMArray.instance(KMCose.COSE_MAC0_ENTRY_COUNT); + // 1 - protected headers + KMArray.cast(arrPtr).add((short) 0, protectedHeader); + // 2 - unprotected headers + KMArray.cast(arrPtr).add((short) 1, unprotectedHeader); + // 2 - payload + KMArray.cast(arrPtr).add((short) 2, payload); + // 3 - tag + KMArray.cast(arrPtr).add((short) 3, tag); + // Do encode. + return arrPtr; + } + + /** + * Constructs the COSE_Signature structure. + * + * @param protectedHeader Bstr pointer which holds the protected header. + * @param extAad Bstr pointer which holds the aad. + * @param payload Bstr pointer which holds the payload. + * @return KMArray instance of COSE_Signature object. + */ + public static short constructCoseSignStructure(short protectedHeader, short extAad, short payload) { + // Sig_structure = [ + // context : "Signature" / "Signature1" / "CounterSignature", + // body_protected : empty_or_serialized_map, + // ? sign_protected : empty_or_serialized_map, + // external_aad : bstr, + // payload : bstr + // ] + short arrPtr = KMArray.instance(KMCose.COSE_SIGN1_ENTRY_COUNT); + // 1 - Context + KMArray.cast(arrPtr).add((short) 0, KMTextString.instance(KMCose.SIGNATURE1_CONTEXT, (short) 0, + (short) KMCose.SIGNATURE1_CONTEXT.length)); + // 2 - Protected headers. + KMArray.cast(arrPtr).add((short) 1, protectedHeader); + // 3 - external aad + KMArray.cast(arrPtr).add((short) 2, extAad); + // 4 - payload. + KMArray.cast(arrPtr).add((short) 3, payload); + return arrPtr; + } + + /** + * Constructs the COSE_Sign1 object. + * + * @param protectedHeader Bstr pointer which holds the protected header. + * @param unProtectedHeader Bstr pointer which holds the unprotected header. + * @param payload Bstr pointer which holds the payload. + * @param signature Bstr pointer which holds the signature. + * @return KMArray instance of COSE_Sign1 object. + */ + public static short constructCoseSign1(short protectedHeader, short unProtectedHeader, short payload, + short signature) { + // COSE_Sign = [ + // protectedHeader, + // unprotectedHeader, + // payload : bstr / nil, + // signatures : [+ COSE_Signature] + // ] + short arrPtr = KMArray.instance(KMCose.COSE_SIGN1_ENTRY_COUNT); + // 1 - protected headers + KMArray.cast(arrPtr).add((short) 0, protectedHeader); + // 2 - unprotected headers + KMArray.cast(arrPtr).add((short) 1, unProtectedHeader); + // 2 - payload + KMArray.cast(arrPtr).add((short) 2, payload); + // 3 - tag + KMArray.cast(arrPtr).add((short) 3, signature); + return arrPtr; + } + + /** + * Constructs array based on the tag values provided. + * + * @param tags array of tag values to be constructed. + * @param includeTestMode flag which indicates if TEST_COSE_KEY should be included or not. + * @return instance of KMArray. + */ + private static short handleCosePairTags(short[] tags, boolean includeTestMode) { + short index = 0; + // var is used to calculate the length of the array. + short var = 0; + while (index < tags.length) { + if (tags[(short) (index + 1)] != KMType.INVALID_VALUE) { + tags[(short) (index + 2)] = + buildCosePairTag((byte) tags[index], tags[(short) (index + 1)]); + var++; + } + index += 3; + } + var += includeTestMode ? 1 : 0; + short arrPtr = KMArray.instance(var); + index = 0; + // var is used to index the array. + var = 0; + while (index < tags.length) { + if (tags[(short) (index + 2)] != KMType.INVALID_VALUE) { + KMArray.cast(arrPtr).add(var++, tags[(short) (index + 2)]); + } + index += 3; + } + return arrPtr; + } + + /** + * Constructs the COSE_sign1 payload for certificate. + * + * @param issuer instance of KMCosePairTextStringTag which contains issuer value. + * @param subject instance of KMCosePairTextStringTag which contains subject value. + * @param subPublicKey instance of KMCosePairByteBlobTag which contains encoded KMCoseKey. + * @param keyUsage instance of KMCosePairByteBlobTag which contains key usage value. + * @return instance of KMArray. + */ + public static short constructCoseCertPayload(short issuer, short subject, short subPublicKey, short keyUsage) { + short certPayload = KMArray.instance((short) 4); + KMArray.cast(certPayload).add((short) 0, issuer); + KMArray.cast(certPayload).add((short) 1, subject); + KMArray.cast(certPayload).add((short) 2, subPublicKey); + KMArray.cast(certPayload).add((short) 3, keyUsage); + certPayload = KMCoseCertPayload.instance(certPayload); + KMCoseCertPayload.cast(certPayload).canonicalize(); + return certPayload; + } + + /** + * Construct headers structure. Headers can be part of COSE_Sign1, COSE_Encrypt, + * COSE_Mac0 and COSE_Key. + * + * @param alg instance of either KMNInteger or KMInteger, based on the sign of algorithm value. + * @param keyId instance of KMByteBlob which contains the key identifier. + * @param iv instance of KMByteblob which contains the iv buffer. + * @param ephemeralKey instance of KMCoseKey. + * @return instance of KMCoseHeaders. + */ + public static short constructHeaders(short alg, short keyId, short iv, short ephemeralKey) { + short[] coseHeaderTags = { + KMCose.COSE_LABEL_ALGORITHM, alg, KMType.INVALID_VALUE, + KMCose.COSE_LABEL_KEYID, keyId, KMType.INVALID_VALUE, + KMCose.COSE_LABEL_IV, iv, KMType.INVALID_VALUE, + KMCose.COSE_LABEL_COSE_KEY, ephemeralKey, KMType.INVALID_VALUE + }; + short ptr = handleCosePairTags(coseHeaderTags, false); + ptr = KMCoseHeaders.instance(ptr); + KMCoseHeaders.cast(ptr).canonicalize(); + return ptr; + } + + /** + * Construct Recipients structure for COSE_Encrypt message. + * + * @param protectedHeaders instance of KMByteBlob which contains encoded KMCoseHeaders. + * @param unprotectedHeaders instance of KMCoseHeaders. + * @param cipherText instance of KMSimple + * @return instance of KMArray. + */ + public static short constructRecipientsStructure(short protectedHeaders, short unprotectedHeaders, + short cipherText) { + // recipients : [+COSE_recipient] + // COSE_recipient = [ + // Headers, + // ciphertext : bstr / nil, + // ? recipients : [+COSE_recipient] + // ] + short arrPtr = KMArray.instance(COSE_ENCRYPT_RECIPIENT_ENTRY_COUNT); + // 1 - protected headers + KMArray.cast(arrPtr).add((short) 0, protectedHeaders); + // 2 - unprotected headers + KMArray.cast(arrPtr).add((short) 1, unprotectedHeaders); + // 2 - payload + KMArray.cast(arrPtr).add((short) 2, cipherText); + + short recipientsArrayPtr = KMArray.instance((short) 1); + KMArray.cast(recipientsArrayPtr).add((short) 0, arrPtr); + return recipientsArrayPtr; + } + + /** + * Construct Encrypt structure required for COSE_Encrypt message. + * + * @param protectedHeader instance of KMByteBlob which wraps KMCoseHeaders. + * @param aad instance of KMByteBlob. + * @return instance of KMArray. + */ + public static short constructCoseEncryptStructure(short protectedHeader, short aad) { + // Enc_structure = [ + // context : "Encrypt" / "Encrypt0" / "Enc_Recipient" / + // "Mac_Recipient" / "Rec_Recipient", + // protected : empty_or_serialized_map, + // external_aad : bstr + // ] + short arrPtr = KMArray.instance(COSE_ENCRYPT_STRUCTURE_ENTRY_COUNT); + // 1 - protected headers + KMArray.cast(arrPtr).add((short) 0, KMTextString.instance(KMCose.ENCRYPT_CONTEXT, (short) 0, + (short) KMCose.ENCRYPT_CONTEXT.length)); + // 2 - unprotected headers + KMArray.cast(arrPtr).add((short) 1, protectedHeader); + // 2 - payload + KMArray.cast(arrPtr).add((short) 2, aad); + return arrPtr; + } + + /** + * Constructs COSE_Encrypt message. + * + * @param protectedHeader instance of KMByteBlob which wraps KMCoseHeaders. + * @param unProtectedHeader instance of KMCoseHeaders. + * @param cipherText instance of KMByteBlob containing the cipher text. + * @param recipients instance of KMArray containing the recipients instance + * @return instance of KMArray. + */ + public static short constructCoseEncrypt(short protectedHeader, short unProtectedHeader, short cipherText, + short recipients) { + // COSE_Encrypt = [ + // protectedHeader, + // unprotectedHeader, + // ciphertext : bstr / nil, + // recipients : [+COSE_recipient] + // ] + short arrPtr = KMArray.instance(KMCose.COSE_ENCRYPT_ENTRY_COUNT); + // 1 - protected headers + KMArray.cast(arrPtr).add((short) 0, protectedHeader); + // 2 - unprotected headers + KMArray.cast(arrPtr).add((short) 1, unProtectedHeader); + // 2 - payload + KMArray.cast(arrPtr).add((short) 2, cipherText); + // 3 - tag + KMArray.cast(arrPtr).add((short) 3, recipients); + return arrPtr; + } + + /** + * Constructs the instance of KMCosePair*Tag. + * + * @param key value of the key. + * @param valuePtr instance of one of KMType. + * @return instance of KMCosePair*Value object. + */ + public static short buildCosePairTag(byte key, short valuePtr) { + short type = KMType.getType(valuePtr); + short keyPtr; + if (key < 0) { + keyPtr = KMNInteger.uint_8(key); + } else { + keyPtr = KMInteger.uint_8(key); + } + switch (type) { + case KMType.INTEGER_TYPE: + return KMCosePairIntegerTag.instance(keyPtr, valuePtr); + case KMType.NEG_INTEGER_TYPE: + return KMCosePairNegIntegerTag.instance(keyPtr, valuePtr); + case KMType.BYTE_BLOB_TYPE: + return KMCosePairByteBlobTag.instance(keyPtr, valuePtr); + case KMType.TEXT_STRING_TYPE: + return KMCosePairTextStringTag.instance(keyPtr, valuePtr); + case KMType.COSE_KEY_TYPE: + return KMCosePairCoseKeyTag.instance(keyPtr, valuePtr); + default: + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + return 0; + } + } + + /** + * Constructs a CoseKey with the provided input paramters. + * + * @param keyType Instance of the identification of the key type. + * @param keyId Instance of key identification value. + * @param keyAlg Instance of the algorithm that is used with this key. + * @param keyOps Instance of the operation that this key is used for. + * @param curve Instance of the EC curve that is used with this key. + * @param pubKey Buffer containing the public key. + * @param pubKeyOff Start offset of the buffer. + * @param pubKeyLen Length of the public key. + * @param privKeyPtr Instance of the private key. + * @param testMode Represents if key is used in test mode or production mode. + * @return Instance of the CoseKey structure. + */ + public static short constructCoseKey(short keyType, short keyId, short keyAlg, short keyOps, + short curve, byte[] pubKey, short pubKeyOff, short pubKeyLen, + short privKeyPtr, boolean testMode) { + if (pubKey[pubKeyOff] == 0x04) { // uncompressed format + pubKeyOff += 1; + pubKeyLen -= 1; + } + pubKeyLen = (short) (pubKeyLen / 2); + short xPtr = KMByteBlob.instance(pubKey, pubKeyOff, pubKeyLen); + short yPtr = KMByteBlob.instance(pubKey, (short) (pubKeyOff + pubKeyLen), pubKeyLen); + short coseKey = constructCoseKey(keyType, keyId, keyAlg, keyOps, curve, xPtr, yPtr, privKeyPtr, testMode); + KMCoseKey.cast(coseKey).canonicalize(); + return coseKey; + } + + /** + * Constructs the cose key based on input parameters supplied. All the parameters must be instantiated from + * either KMInteger or KMNInteger or KMByteblob types. + * + * @param keyType instance of KMInteger/KMNInteger which holds valid COSE key types. + * @param keyId instance of KMByteBlob which holds key identifier value. + * @param keyAlg instance of KMInteger/KMNInteger which holds valid COSE key algorithm. + * @param keyOps instance of KMInteger/KMNInteger which holds valid COSE key operations. + * @param curve instance of KMInteger/KMNInteger which holds valid COSE EC curve. + * @param pubX instance of KMByteBlob which holds EC public key's x value. + * @param pubY instance of KMByteBlob which holds EC public key's y value. + * @param priv instance of KMByteBlob which holds EC private value. + * @param includeTestKey flag which identifies whether to construct test key or production key. + * @return instance of the KMCoseKey object. + */ + public static short constructCoseKey(short keyType, short keyId, short keyAlg, short keyOps, short curve, + short pubX, short pubY, short priv, boolean includeTestKey) { + short[] coseKeyTags = { + KMCose.COSE_KEY_KEY_TYPE, keyType, KMType.INVALID_VALUE, + KMCose.COSE_KEY_KEY_ID, keyId, KMType.INVALID_VALUE, + KMCose.COSE_KEY_ALGORITHM, keyAlg, KMType.INVALID_VALUE, + KMCose.COSE_KEY_KEY_OPS, keyOps, KMType.INVALID_VALUE, + KMCose.COSE_KEY_CURVE, curve, KMType.INVALID_VALUE, + KMCose.COSE_KEY_PUBKEY_X, pubX, KMType.INVALID_VALUE, + KMCose.COSE_KEY_PUBKEY_Y, pubY, KMType.INVALID_VALUE, + KMCose.COSE_KEY_PRIV_KEY, priv, KMType.INVALID_VALUE + }; + short arrPtr = handleCosePairTags(coseKeyTags, includeTestKey); + if (includeTestKey) { + short testKey = + KMCosePairSimpleValueTag.instance(KMNInteger.uint_32(KMCose.COSE_TEST_KEY, (short) 0), + KMSimpleValue.instance(KMSimpleValue.NULL)); + KMArray.cast(arrPtr).add((short) (KMArray.cast(arrPtr).length() - 1), testKey); + } + arrPtr = KMCoseKey.instance(arrPtr); + KMCoseKey.cast(arrPtr).canonicalize(); + return arrPtr; + } + + /** + * Constructs key derivation context which is required to compute HKDF. + * + * @param publicKeyA public key buffer from the first party. + * @param publicKeyAOff start position of the public key buffer from first party. + * @param publicKeyALen length of the public key buffer from first party. + * @param publicKeyB public key buffer from the second party. + * @param publicKeyBOff start position of the public key buffer from second party. + * @param publicKeyBLen length of the public key buffer from second party. + * @param senderIsA true if caller is first party, false if caller is second party. + * @return instance of KMArray. + */ + public static short constructKdfContext(byte[] publicKeyA, short publicKeyAOff, short publicKeyALen, + byte[] publicKeyB, short publicKeyBOff, short publicKeyBLen, + boolean senderIsA) { + short index = 0; + // Prepare sender info + short senderInfo = KMArray.instance((short) 3); + KMArray.cast(senderInfo).add(index++, KMByteBlob.instance(client, (short) 0, (short) client.length)); + KMArray.cast(senderInfo).add(index++, KMByteBlob.instance((short) 0)); + KMArray.cast(senderInfo).add(index, senderIsA ? + KMByteBlob.instance(publicKeyA, publicKeyAOff, publicKeyALen) : + KMByteBlob.instance(publicKeyB, publicKeyBOff, publicKeyBLen)); + + // Prepare recipient info + index = 0; + short recipientInfo = KMArray.instance((short) 3); + KMArray.cast(recipientInfo).add(index++, KMByteBlob.instance(server, (short) 0, (short) server.length)); + KMArray.cast(recipientInfo).add(index++, KMByteBlob.instance((short) 0)); + KMArray.cast(recipientInfo).add(index, senderIsA ? + KMByteBlob.instance(publicKeyB, publicKeyBOff, publicKeyBLen) : + KMByteBlob.instance(publicKeyA, publicKeyAOff, publicKeyALen)); + + // supply public info + index = 0; + short publicInfo = KMArray.instance((short) 2); + KMArray.cast(publicInfo).add(index++, KMInteger.uint_16(AES_GCM_KEY_SIZE_BITS)); + KMArray.cast(publicInfo).add(index, KMByteBlob.instance((short) 0)); + + // construct kdf context + index = 0; + short arrPtr = KMArray.instance((short) 4); + KMArray.cast(arrPtr).add(index++, KMInteger.uint_8(COSE_ALG_AES_GCM_256)); + KMArray.cast(arrPtr).add(index++, senderInfo); + KMArray.cast(arrPtr).add(index++, recipientInfo); + KMArray.cast(arrPtr).add(index, publicInfo); + + return arrPtr; + } +} \ No newline at end of file diff --git a/Applet/src/com/android/javacard/keymaster/KMCoseCertPayload.java b/Applet/src/com/android/javacard/keymaster/KMCoseCertPayload.java new file mode 100644 index 00000000..76535416 --- /dev/null +++ b/Applet/src/com/android/javacard/keymaster/KMCoseCertPayload.java @@ -0,0 +1,136 @@ +/* + * Copyright(C) 2021 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.Util; + +/** + * KMCoseCertPayload represents the COSE_Sign1 payload for each certificate in BCC. The supported key types are + * KMInteger, KMNInteger and the supported value types are KMByteBlob and KMTextString. + * It corresponds to a CBOR Map type. struct{byte TAG_TYPE; short length; short arrayPtr } where + * arrayPtr is a pointer to array with any KMCosePairTagType subtype instances. + */ +public class KMCoseCertPayload extends KMCoseMap { + + private static KMCoseCertPayload prototype; + + private KMCoseCertPayload() { + } + + private static KMCoseCertPayload proto(short ptr) { + if (prototype == null) { + prototype = new KMCoseCertPayload(); + } + instanceTable[KM_COSE_CERT_PAYLOAD_OFFSET] = ptr; + return prototype; + } + + public static short exp() { + short arrPtr = KMArray.instance((short) 2); + KMArray arr = KMArray.cast(arrPtr); + arr.add((short) 0, KMCosePairTextStringTag.exp()); + arr.add((short) 1, KMCosePairByteBlobTag.exp()); + return KMCoseCertPayload.instance(arrPtr); + } + + public static short instance(short vals) { + short ptr = KMType.instance(COSE_CERT_PAYLOAD_TYPE, (short) 2); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), vals); + return ptr; + } + + public static KMCoseCertPayload cast(short ptr) { + if (heap[ptr] != COSE_CERT_PAYLOAD_TYPE) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + short arrPtr = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE)); + if (heap[arrPtr] != ARRAY_TYPE) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + return proto(ptr); + } + + @Override + public short getVals() { + return Util.getShort(heap, (short) (instanceTable[KM_COSE_CERT_PAYLOAD_OFFSET] + TLV_HEADER_SIZE)); + } + + @Override + public short length() { + short arrPtr = getVals(); + return KMArray.cast(arrPtr).length(); + } + + @Override + public void canonicalize() { + KMCoseMap.canonicalize(getVals()); + } + + private short getValueType(short key, short significantKey) { + short arr = getVals(); + short length = length(); + short keyPtr; + short valPtr = 0; + short index = 0; + short tagType; + boolean found = false; + while (index < length) { + tagType = KMCosePairTagType.getTagValueType(KMArray.cast(arr).get(index)); + switch (tagType) { + case KMType.COSE_PAIR_BYTE_BLOB_TAG_TYPE: + keyPtr = KMCosePairByteBlobTag.cast(KMArray.cast(arr).get(index)).getKeyPtr(); + if (key == KMCosePairTagType.getKeyValueShort(keyPtr) && + significantKey == KMCosePairTagType.getKeyValueSignificantShort(keyPtr)) { + valPtr = KMCosePairByteBlobTag.cast(KMArray.cast(arr).get(index)).getValuePtr(); + found = true; + } + break; + case KMType.COSE_PAIR_TEXT_STR_TAG_TYPE: + keyPtr = KMCosePairTextStringTag.cast(KMArray.cast(arr).get(index)).getKeyPtr(); + if (key == (byte) KMCosePairTagType.getKeyValueShort(keyPtr)) { + valPtr = KMCosePairTextStringTag.cast(KMArray.cast(arr).get(index)).getValuePtr(); + found = true; + } + break; + default: + break; + + } + if (found) + break; + index++; + } + return valPtr; + } + + public short getSubjectPublicKey() { + return getValueType(Util.getShort(KMCose.SUBJECT_PUBLIC_KEY, (short) 2), // LSB + Util.getShort(KMCose.SUBJECT_PUBLIC_KEY, (short) 0) // MSB (Significant) + ); + } + + public short getSubject() { + return getValueType(KMCose.SUBJECT, KMType.INVALID_VALUE); + } + + public short getIssuer() { + return getValueType(KMCose.ISSUER, KMType.INVALID_VALUE); + } + +} diff --git a/Applet/src/com/android/javacard/keymaster/KMCoseHeaders.java b/Applet/src/com/android/javacard/keymaster/KMCoseHeaders.java new file mode 100644 index 00000000..0b82d6cb --- /dev/null +++ b/Applet/src/com/android/javacard/keymaster/KMCoseHeaders.java @@ -0,0 +1,203 @@ +/* + * Copyright(C) 2021 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 javacard.framework.ISO7816; +import javacard.framework.ISOException; +import javacard.framework.Util; + +/** + * KMCoseHeaders represents headers section from the Cose standard + * https://datatracker.ietf.org/doc/html/rfc8152#section-3. The supported key types are + * KMInteger, KMNInteger and the supported value types are KMInteger, KMNInteger, KMByteBlob, + * KMCoseKey. It corresponds to a CBOR Map type. struct{byte TAG_TYPE; short length; short arrayPtr } where + * arrayPtr is a pointer to array with any KMTag subtype instances. + */ +public class KMCoseHeaders extends KMCoseMap { + + private static KMCoseHeaders prototype; + + private KMCoseHeaders() { + } + + private static KMCoseHeaders proto(short ptr) { + if (prototype == null) { + prototype = new KMCoseHeaders(); + } + instanceTable[KM_COSE_HEADERS_OFFSET] = ptr; + return prototype; + } + + public static short exp() { + short arrPtr = KMArray.instance((short) 4); + // CoseKey is internally an Array so evaluate it separately. + short coseKeyValueExp = KMCosePairCoseKeyTag.exp(); + KMArray arr = KMArray.cast(arrPtr); + arr.add((short) 0, KMCosePairIntegerTag.exp()); + arr.add((short) 1, KMCosePairNegIntegerTag.exp()); + arr.add((short) 2, KMCosePairByteBlobTag.exp()); + arr.add((short) 3, coseKeyValueExp); + return KMCoseHeaders.instance(arrPtr); + } + + + public static short instance(short vals) { + short ptr = KMType.instance(COSE_HEADERS_TYPE, (short) 2); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), vals); + return ptr; + } + + public static KMCoseHeaders cast(short ptr) { + if (heap[ptr] != COSE_HEADERS_TYPE) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + short arrPtr = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE)); + if (heap[arrPtr] != ARRAY_TYPE) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + return proto(ptr); + } + + @Override + public short getVals() { + return Util.getShort(heap, (short) (instanceTable[KM_COSE_HEADERS_OFFSET] + TLV_HEADER_SIZE)); + } + + @Override + public short length() { + short arrPtr = getVals(); + return KMArray.cast(arrPtr).length(); + } + + @Override + public void canonicalize() { + KMCoseMap.canonicalize(getVals()); + } + + private short getValueType(short key) { + short index = 0; + short len = length(); + short arr = getVals(); + short tagType; + short valPtr = 0; + short keyPtr; + boolean found = false; + while (index < len) { + tagType = KMCosePairTagType.getTagValueType(KMArray.cast(arr).get(index)); + switch (tagType) { + case KMType.COSE_PAIR_BYTE_BLOB_TAG_TYPE: + keyPtr = KMCosePairByteBlobTag.cast(KMArray.cast(arr).get(index)).getKeyPtr(); + if (key == (byte) KMCosePairTagType.getKeyValueShort(keyPtr)) { + valPtr = KMCosePairByteBlobTag.cast(KMArray.cast(arr).get(index)).getValuePtr(); + found = true; + } + break; + case KMType.COSE_PAIR_COSE_KEY_TAG_TYPE: + keyPtr = KMCosePairCoseKeyTag.cast(KMArray.cast(arr).get(index)).getKeyPtr(); + if (key == (byte) KMCosePairTagType.getKeyValueShort(keyPtr)) { + valPtr = KMCosePairCoseKeyTag.cast(KMArray.cast(arr).get(index)).getValuePtr(); + found = true; + } + break; + case KMType.COSE_PAIR_INT_TAG_TYPE: + keyPtr = KMCosePairIntegerTag.cast(KMArray.cast(arr).get(index)).getKeyPtr(); + if (key == (byte) KMCosePairTagType.getKeyValueShort(keyPtr)) { + valPtr = KMCosePairIntegerTag.cast(KMArray.cast(arr).get(index)).getValuePtr(); + found = true; + } + break; + case KMType.COSE_PAIR_NEG_INT_TAG_TYPE: + keyPtr = KMCosePairNegIntegerTag.cast(KMArray.cast(arr).get(index)).getKeyPtr(); + if (key == (byte) KMCosePairTagType.getKeyValueShort(keyPtr)) { + valPtr = KMCosePairNegIntegerTag.cast(KMArray.cast(arr).get(index)).getValuePtr(); + found = true; + } + break; + default: + break; + } + if (found) + break; + index++; + } + return valPtr; + } + + public short getKeyIdentifier() { + return getValueType(KMCose.COSE_LABEL_KEYID); + } + + public short getCoseKey() { + return getValueType(KMCose.COSE_LABEL_COSE_KEY); + } + + public short getIV() { + return getValueType(KMCose.COSE_LABEL_IV); + } + + public short getAlgorithm() { + return getValueType(KMCose.COSE_LABEL_ALGORITHM); + } + + public boolean isDataValid(short alg, short keyIdPtr) { + short[] headerTags = { + KMCose.COSE_LABEL_ALGORITHM, alg, + KMCose.COSE_LABEL_KEYID, keyIdPtr, + }; + boolean valid = false; + short value; + short ptr; + short tagIndex = 0; + while (tagIndex < headerTags.length) { + value = headerTags[(short) (tagIndex + 1)]; + if (value != KMType.INVALID_VALUE) { + valid = false; + ptr = getValueType(headerTags[tagIndex]); + switch (KMType.getType(ptr)) { + case KMType.BYTE_BLOB_TYPE: + if ((KMByteBlob.cast(value).length() == KMByteBlob.cast(ptr).length()) && + (0 == + Util.arrayCompare(KMByteBlob.cast(value).getBuffer(), + KMByteBlob.cast(value).getStartOff(), + KMByteBlob.cast(ptr).getBuffer(), + KMByteBlob.cast(ptr).getStartOff(), + KMByteBlob.cast(ptr).length()))) { + valid = true; + } + break; + case KMType.INTEGER_TYPE: + if (value == KMInteger.cast(ptr).getShort()) { + valid = true; + } + break; + case KMType.NEG_INTEGER_TYPE: + if ((byte) value == (byte) KMNInteger.cast(ptr).getShort()) { + valid = true; + } + break; + default: + break; + } + if (!valid) + break; + } + tagIndex += 2; + } + return valid; + } + + +} diff --git a/Applet/src/com/android/javacard/keymaster/KMCoseKey.java b/Applet/src/com/android/javacard/keymaster/KMCoseKey.java new file mode 100644 index 00000000..eafa3006 --- /dev/null +++ b/Applet/src/com/android/javacard/keymaster/KMCoseKey.java @@ -0,0 +1,235 @@ +/* + * Copyright(C) 2021 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.Util; + +/** + * KMCoseKey represents COSE_Key section from the Cose standard https://datatracker.ietf.org/doc/html/rfc8152#section-7 + * The supported key types are KMNInteger, KMInteger and the supported value types are KMInteger, KMNInteger, + * KMByteBlob, KMSimpleValue. It corresponds to a CBOR Map type. struct{byte TAG_TYPE; short length; short arrayPtr } + * where arrayPtr is a pointer to array with any KMTag subtype instances. + */ +public class KMCoseKey extends KMCoseMap { + + private static KMCoseKey prototype; + + private KMCoseKey() { + } + + private static KMCoseKey proto(short ptr) { + if (prototype == null) { + prototype = new KMCoseKey(); + } + instanceTable[KM_COSE_KEY_OFFSET] = ptr; + return prototype; + } + + public static short exp() { + short arrPtr = KMArray.instance((short) 4); + KMArray arr = KMArray.cast(arrPtr); + arr.add((short) 0, KMCosePairIntegerTag.exp()); + arr.add((short) 1, KMCosePairNegIntegerTag.exp()); + arr.add((short) 2, KMCosePairByteBlobTag.exp()); + arr.add((short) 3, KMCosePairSimpleValueTag.exp()); + return KMCoseKey.instance(arrPtr); + } + + + public static short instance(short vals) { + short ptr = KMType.instance(COSE_KEY_TYPE, (short) 2); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), vals); + return ptr; + } + + public static KMCoseKey cast(short ptr) { + if (heap[ptr] != COSE_KEY_TYPE) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + short arrPtr = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE)); + if (heap[arrPtr] != ARRAY_TYPE) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + return proto(ptr); + } + + @Override + public short getVals() { + return Util.getShort(heap, (short) (instanceTable[KM_COSE_KEY_OFFSET] + TLV_HEADER_SIZE)); + } + + @Override + public short length() { + short arrPtr = getVals(); + return KMArray.cast(arrPtr).length(); + } + + private short getValueType(short key, short significantKey) { + short arr = getVals(); + short length = length(); + short keyPtr; + short valPtr = 0; + short index = 0; + short tagType; + boolean found = false; + while (index < length) { + tagType = KMCosePairTagType.getTagValueType(KMArray.cast(arr).get(index)); + switch (tagType) { + case KMType.COSE_PAIR_BYTE_BLOB_TAG_TYPE: + keyPtr = KMCosePairByteBlobTag.cast(KMArray.cast(arr).get(index)).getKeyPtr(); + if (key == (byte) KMCosePairTagType.getKeyValueShort(keyPtr)) { + valPtr = KMCosePairByteBlobTag.cast(KMArray.cast(arr).get(index)).getValuePtr(); + found = true; + } + break; + case KMType.COSE_PAIR_INT_TAG_TYPE: + keyPtr = KMCosePairIntegerTag.cast(KMArray.cast(arr).get(index)).getKeyPtr(); + if (key == (byte) KMCosePairTagType.getKeyValueShort(keyPtr)) { + valPtr = KMCosePairIntegerTag.cast(KMArray.cast(arr).get(index)).getValuePtr(); + found = true; + } + break; + case KMType.COSE_PAIR_NEG_INT_TAG_TYPE: + keyPtr = KMCosePairNegIntegerTag.cast(KMArray.cast(arr).get(index)).getKeyPtr(); + if (key == (byte) KMCosePairTagType.getKeyValueShort(keyPtr)) { + valPtr = KMCosePairNegIntegerTag.cast(KMArray.cast(arr).get(index)).getValuePtr(); + found = true; + } + break; + case KMType.COSE_PAIR_SIMPLE_VALUE_TAG_TYPE: + keyPtr = KMCosePairSimpleValueTag.cast(KMArray.cast(arr).get(index)).getKeyPtr(); + if (key == KMCosePairTagType.getKeyValueShort(keyPtr) && + significantKey == KMCosePairTagType.getKeyValueSignificantShort(keyPtr)) { + valPtr = KMCosePairSimpleValueTag.cast(KMArray.cast(arr).get(index)).getValuePtr(); + found = true; + } + break; + default: + break; + + } + if (found) + break; + index++; + } + return valPtr; + } + + public short getKeyIdentifier() { + return getValueType(KMCose.COSE_KEY_KEY_ID, KMType.INVALID_VALUE); + } + + public short getEcdsa256PublicKey(byte[] pubKey, short pubKeyOff) { + short baseOffset = pubKeyOff; + pubKey[pubKeyOff] = (byte) 0x04; // uncompressed. + pubKeyOff++; + short ptr = getValueType(KMCose.COSE_KEY_PUBKEY_X, KMType.INVALID_VALUE); + Util.arrayCopy(KMByteBlob.cast(ptr).getBuffer(), KMByteBlob.cast(ptr).getStartOff(), + pubKey, pubKeyOff, KMByteBlob.cast(ptr).length()); + pubKeyOff += KMByteBlob.cast(ptr).length(); + ptr = getValueType(KMCose.COSE_KEY_PUBKEY_Y, KMType.INVALID_VALUE); + Util.arrayCopy(KMByteBlob.cast(ptr).getBuffer(), KMByteBlob.cast(ptr).getStartOff(), + pubKey, pubKeyOff, KMByteBlob.cast(ptr).length()); + pubKeyOff += KMByteBlob.cast(ptr).length(); + return (short) (pubKeyOff - baseOffset); + } + + public short getPrivateKey(byte[] priv, short privOff) { + short ptr = getValueType(KMCose.COSE_KEY_PRIV_KEY, KMType.INVALID_VALUE); + Util.arrayCopy(KMByteBlob.cast(ptr).getBuffer(), KMByteBlob.cast(ptr).getStartOff(), + priv, privOff, KMByteBlob.cast(ptr).length()); + return KMByteBlob.cast(ptr).length(); + } + + public boolean isTestKey() { + short ptr = + getValueType( + Util.getShort(KMCose.COSE_TEST_KEY, (short) 2), // LSB + Util.getShort(KMCose.COSE_TEST_KEY, (short) 0) // MSB (Significant) + ); + boolean isTestKey = false; + if (ptr != 0) + isTestKey = (KMSimpleValue.cast(ptr).getValue() == KMSimpleValue.NULL); + return isTestKey; + } + + /** + * Verifies the KMCoseKey values against the input values. + * + * @param keyType value of the key type + * @param keyIdPtr instance of KMByteBlob containing the key id. + * @param keyAlg value of the algorithm. + * @param keyOps value of the key operations. + * @param curve value of the curve. + * @return true if valid, otherwise false. + */ + public boolean isDataValid(short keyType, short keyIdPtr, short keyAlg, short keyOps, short curve) { + short[] coseKeyTags = { + KMCose.COSE_KEY_KEY_TYPE, keyType, + KMCose.COSE_KEY_KEY_ID, keyIdPtr, + KMCose.COSE_KEY_ALGORITHM, keyAlg, + KMCose.COSE_KEY_KEY_OPS, keyOps, + KMCose.COSE_KEY_CURVE, curve, + }; + boolean valid = false; + short ptr; + short tagIndex = 0; + short value; + while (tagIndex < coseKeyTags.length) { + value = coseKeyTags[(short) (tagIndex + 1)]; + if (value != KMType.INVALID_VALUE) { + valid = false; + ptr = getValueType(coseKeyTags[tagIndex], KMType.INVALID_VALUE); + switch (KMType.getType(ptr)) { + case KMType.BYTE_BLOB_TYPE: + if ((KMByteBlob.cast(value).length() == KMByteBlob.cast(ptr).length()) && + (0 == + Util.arrayCompare(KMByteBlob.cast(value).getBuffer(), + KMByteBlob.cast(value).getStartOff(), + KMByteBlob.cast(ptr).getBuffer(), + KMByteBlob.cast(ptr).getStartOff(), + KMByteBlob.cast(ptr).length()))) { + valid = true; + } + break; + case KMType.INTEGER_TYPE: + if (value == KMInteger.cast(ptr).getShort()) { + valid = true; + } + break; + case KMType.NEG_INTEGER_TYPE: + if ((byte) value == (byte) KMNInteger.cast(ptr).getShort()) { + valid = true; + } + break; + } + if (!valid) + break; + } + tagIndex += 2; + } + return valid; + } + + @Override + public void canonicalize() { + KMCoseMap.canonicalize(getVals()); + } + +} diff --git a/Applet/src/com/android/javacard/keymaster/KMCoseMap.java b/Applet/src/com/android/javacard/keymaster/KMCoseMap.java new file mode 100644 index 00000000..ceb67fb2 --- /dev/null +++ b/Applet/src/com/android/javacard/keymaster/KMCoseMap.java @@ -0,0 +1,165 @@ +/* + * Copyright(C) 2021 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 javacard.framework.ISO7816; +import javacard.framework.ISOException; +import javacard.framework.JCSystem; +import javacard.framework.Util; + +/** + * This class represents either a Cose_key or Cose headers as defined in + * https://datatracker.ietf.org/doc/html/rfc8152 This is basically a map containing key value pairs. + * The label for the key can be (uint / int / tstr) and the value can be of any type. But this class + * is confined to support only key and value types which are required for remote key provisioning. + * So keys of type (int / uint) and values of type (int / uint / simple / bstr) only are supported. + * KMCoseHeaders and KMCoseKey implements this class. + */ +public abstract class KMCoseMap extends KMType { + + public static byte[] scratchpad; + + /** + * This function creates an instance of either KMCoseHeaders or KMCoseKey based on the type + * information provided. + * + * @param typePtr type information of the underlying KMType. + * @param arrPtr instance of KMArray. + * @return instance type of either KMCoseHeaders or KMCoseKey. + */ + public static short createInstanceFromType(short typePtr, short arrPtr) { + short mapType = KMType.getType(typePtr); + switch (mapType) { + case KMType.COSE_HEADERS_TYPE: + return KMCoseHeaders.instance(arrPtr); + case KMType.COSE_KEY_TYPE: + return KMCoseKey.instance(arrPtr); + case KMType.COSE_CERT_PAYLOAD_TYPE: + return KMCoseCertPayload.instance(arrPtr); + default: + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + return 0; + } + } + + public static short getVals(short ptr) { + short mapType = KMType.getType(ptr); + switch (mapType) { + case KMType.COSE_HEADERS_TYPE: + return KMCoseHeaders.cast(ptr).getVals(); + case KMType.COSE_KEY_TYPE: + return KMCoseKey.cast(ptr).getVals(); + case KMType.COSE_CERT_PAYLOAD_TYPE: + return KMCoseCertPayload.cast(ptr).getVals(); + default: + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + return 0; + } + } + + abstract public short getVals(); + + abstract public short length(); + + abstract public void canonicalize(); + + private static short getKey(short tagPtr) { + short tagType = KMCosePairTagType.getTagValueType(tagPtr); + switch (tagType) { + case KMType.COSE_PAIR_BYTE_BLOB_TAG_TYPE: + return KMCosePairByteBlobTag.cast(tagPtr).getKeyPtr(); + case KMType.COSE_PAIR_INT_TAG_TYPE: + return KMCosePairIntegerTag.cast(tagPtr).getKeyPtr(); + case KMType.COSE_PAIR_NEG_INT_TAG_TYPE: + return KMCosePairNegIntegerTag.cast(tagPtr).getKeyPtr(); + case KMType.COSE_PAIR_SIMPLE_VALUE_TAG_TYPE: + return KMCosePairSimpleValueTag.cast(tagPtr).getKeyPtr(); + case KMType.COSE_PAIR_COSE_KEY_TAG_TYPE: + return KMCosePairCoseKeyTag.cast(tagPtr).getKeyPtr(); + case KMType.COSE_PAIR_TEXT_STR_TAG_TYPE: + return KMCosePairTextStringTag.cast(tagPtr).getKeyPtr(); + default: + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + return 0; + } + + private static void createScratchBuffer() { + if (scratchpad == null) { + scratchpad = JCSystem.makeTransientByteArray((short) 120, JCSystem.CLEAR_ON_RESET); + } + } + + protected static void canonicalize(short arr) { + canonicalize(arr, KMArray.cast(arr).length()); + } + + private static void swap(short ptr, short firstIndex, short secondIndex) { + if (KMType.getType(ptr) == KMType.ARRAY_TYPE) { + KMArray.cast(ptr).swap(firstIndex, secondIndex); + } else { + KMMap.cast(ptr).swap(firstIndex, secondIndex); + } + } + + private static boolean compareAndSwap(short ptr, short index) { + short firstKey; + short secondKey; + short firstKeyLen; + short secondKeyLen; + if (KMType.getType(ptr) == KMType.ARRAY_TYPE) { + firstKey = getKey(KMArray.cast(ptr).get(index)); + secondKey = getKey(KMArray.cast(ptr).get((short) (index + 1))); + } else { // Map + firstKey = KMMap.cast(ptr).getKey(index); + secondKey = KMMap.cast(ptr).getKey((short) (index + 1)); + } + firstKeyLen = KMKeymasterApplet.encoder.encode(firstKey, scratchpad, (short) 0); + secondKeyLen = KMKeymasterApplet.encoder.encode(secondKey, scratchpad, firstKeyLen); + if ((firstKeyLen > secondKeyLen) || + ((firstKeyLen == secondKeyLen) && + (0 < Util.arrayCompare(scratchpad, (short) 0, scratchpad, firstKeyLen, firstKeyLen)))) { + swap(ptr, index, (short) (index + 1)); + return true; + } + return false; + } + + /** + * Canonicalizes using bubble sort. + * + * @param ptr instance pointer of either array or map. + * @param length length of the array or map instance. + */ + public static void canonicalize(short ptr, short length) { + short index = 0; + short innerIndex = 0; + createScratchBuffer(); + boolean swapped; + while (index < length) { + swapped = false; + innerIndex = 0; + while (innerIndex < (short) (length - index - 1)) { + swapped |= compareAndSwap(ptr, innerIndex); + innerIndex++; + } + if (!swapped) { + break; + } + index++; + } + } +} diff --git a/Applet/src/com/android/javacard/keymaster/KMCosePairByteBlobTag.java b/Applet/src/com/android/javacard/keymaster/KMCosePairByteBlobTag.java new file mode 100644 index 00000000..4a03295e --- /dev/null +++ b/Applet/src/com/android/javacard/keymaster/KMCosePairByteBlobTag.java @@ -0,0 +1,130 @@ +/* + * Copyright(C) 2021 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.Util; + +/** + * KMCosePairByteBlobTag represents a key-value type, where key can be KMInteger or KMNInteger and value is + * KMByteBlob type. struct{byte TAG_TYPE; short length; struct{short BYTE_BLOB_TYPE; short key; short value}}. + */ +public class KMCosePairByteBlobTag extends KMCosePairTagType { + + private static KMCosePairByteBlobTag prototype; + + public static Object[] keys; + + private KMCosePairByteBlobTag() { + } + + private static KMCosePairByteBlobTag proto(short ptr) { + if (prototype == null) { + prototype = new KMCosePairByteBlobTag(); + } + instanceTable[KM_COSE_KEY_BYTE_BLOB_VAL_OFFSET] = ptr; + return prototype; + } + + // pointer to an empty instance used as expression + public static short exp() { + short ptr = instance(COSE_PAIR_TAG_TYPE, (short) 6); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), KMType.COSE_PAIR_BYTE_BLOB_TAG_TYPE); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), KMType.INVALID_VALUE); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), KMByteBlob.exp()); + return ptr; + } + + public static short instance(short keyPtr, short valuePtr) { + if (!isKeyValueValid(keyPtr)) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + if (KMType.getType(valuePtr) != BYTE_BLOB_TYPE) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + short ptr = KMType.instance(COSE_PAIR_TAG_TYPE, (short) 6); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), KMType.COSE_PAIR_BYTE_BLOB_TAG_TYPE); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), keyPtr); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), valuePtr); + return ptr; + } + + public static KMCosePairByteBlobTag cast(short ptr) { + byte[] heap = repository.getHeap(); + if (heap[ptr] != COSE_PAIR_TAG_TYPE) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + // Validate the value pointer. + short valuePtr = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4)); + if (KMType.getType(valuePtr) != BYTE_BLOB_TYPE) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + return proto(ptr); + } + + public short getValueType() { + return BYTE_BLOB_TYPE; + } + + @Override + public short getKeyPtr() { + return Util.getShort(heap, (short) (instanceTable[KM_COSE_KEY_BYTE_BLOB_VAL_OFFSET] + TLV_HEADER_SIZE + 2)); + } + + @Override + public short getValuePtr() { + return Util.getShort(heap, (short) (instanceTable[KM_COSE_KEY_BYTE_BLOB_VAL_OFFSET] + TLV_HEADER_SIZE + 4)); + } + + private static void createKeys() { + if (keys == null) { + keys = new Object[]{ + (Object) new byte[]{(byte) 0, (byte) 0, (byte) 0, KMCose.COSE_KEY_PUBKEY_X}, + (Object) new byte[]{(byte) 0, (byte) 0, (byte) 0, KMCose.COSE_KEY_PUBKEY_Y}, + (Object) new byte[]{(byte) 0, (byte) 0, (byte) 0, KMCose.COSE_KEY_PRIV_KEY}, + (Object) new byte[]{(byte) 0, (byte) 0, (byte) 0, KMCose.COSE_LABEL_IV}, + (Object) new byte[]{(byte) 0, (byte) 0, (byte) 0, KMCose.COSE_LABEL_KEYID}, + (Object) new byte[]{(byte) 0, (byte) 0, (byte) 0, KMCose.COSE_KEY_KEY_ID}, + (Object) KMCose.SUBJECT_PUBLIC_KEY, + (Object) KMCose.KEY_USAGE + }; + } + } + + public static boolean isKeyValueValid(short keyPtr) { + createKeys(); + short type = KMType.getType(keyPtr); + short offset = 0; + if (type == INTEGER_TYPE) { + offset = KMInteger.cast(keyPtr).getStartOff(); + } else if (type == NEG_INTEGER_TYPE) { + offset = KMNInteger.cast(keyPtr).getStartOff(); + } else { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + short index = 0; + while (index < (short) keys.length) { + if (0 == Util.arrayCompare((byte[]) keys[index], (short) 0, heap, offset, (short) ((byte[]) keys[index]).length)) { + return true; + } + index++; + } + return false; + } + +} diff --git a/Applet/src/com/android/javacard/keymaster/KMCosePairCoseKeyTag.java b/Applet/src/com/android/javacard/keymaster/KMCosePairCoseKeyTag.java new file mode 100644 index 00000000..7eb1251e --- /dev/null +++ b/Applet/src/com/android/javacard/keymaster/KMCosePairCoseKeyTag.java @@ -0,0 +1,89 @@ +package com.android.javacard.keymaster; + +import javacard.framework.ISO7816; +import javacard.framework.ISOException; +import javacard.framework.Util; + +/** + * KMCosePairCoseKeyTag represents a key-value type, where key can be KMInteger or KMNInteger and value is + * KMCOseKey type. struct{byte TAG_TYPE; short length; struct{short COSE_KEY_VALUE_TYPE; short key; short value}}. + */ +public class KMCosePairCoseKeyTag extends KMCosePairTagType { + + public static final byte[] keys = { + KMCose.COSE_LABEL_COSE_KEY + }; + private static KMCosePairCoseKeyTag prototype; + + private KMCosePairCoseKeyTag() { + } + + private static KMCosePairCoseKeyTag proto(short ptr) { + if (prototype == null) { + prototype = new KMCosePairCoseKeyTag(); + } + instanceTable[KM_COSE_KEY_COSE_KEY_VAL_OFFSET] = ptr; + return prototype; + } + + // pointer to an empty instance used as expression + public static short exp() { + short ptr = instance(COSE_PAIR_TAG_TYPE, (short) 6); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), KMType.COSE_PAIR_COSE_KEY_TAG_TYPE); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), KMType.INVALID_VALUE); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), KMCoseKey.exp()); + return ptr; + } + + public static short instance(short keyPtr, short valuePtr) { + if (!isKeyValueValid(KMCosePairTagType.getKeyValueShort(keyPtr))) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + if (KMType.getType(valuePtr) != COSE_KEY_TYPE) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + short ptr = KMType.instance(COSE_PAIR_TAG_TYPE, (short) 6); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), KMType.COSE_PAIR_COSE_KEY_TAG_TYPE); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), keyPtr); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), valuePtr); + return ptr; + } + + public static KMCosePairCoseKeyTag cast(short ptr) { + byte[] heap = repository.getHeap(); + if (heap[ptr] != COSE_PAIR_TAG_TYPE) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + // Validate the value pointer. + short valuePtr = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4)); + if (KMType.getType(valuePtr) != COSE_KEY_TYPE) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + return proto(ptr); + } + + public short getValueType() { + return COSE_KEY_TYPE; + } + + @Override + public short getKeyPtr() { + return Util.getShort(heap, (short) (instanceTable[KM_COSE_KEY_COSE_KEY_VAL_OFFSET] + TLV_HEADER_SIZE + 2)); + } + + @Override + public short getValuePtr() { + return Util.getShort(heap, (short) (instanceTable[KM_COSE_KEY_COSE_KEY_VAL_OFFSET] + TLV_HEADER_SIZE + 4)); + } + + public static boolean isKeyValueValid(short keyVal) { + short index = 0; + while (index < (short) keys.length) { + if ((byte) (keyVal & 0xFF) == keys[index]) + return true; + index++; + } + return false; + } + +} diff --git a/Applet/src/com/android/javacard/keymaster/KMCosePairIntegerTag.java b/Applet/src/com/android/javacard/keymaster/KMCosePairIntegerTag.java new file mode 100644 index 00000000..d5fe47b9 --- /dev/null +++ b/Applet/src/com/android/javacard/keymaster/KMCosePairIntegerTag.java @@ -0,0 +1,93 @@ +/* + * Copyright(C) 2021 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.Util; + +/** + * KMCosePairIntegerTag represents a key-value type, where key can be KMInteger or KMNInteger and value is + * KMInteger type. struct{byte TAG_TYPE; short length; struct{short INT_VALUE_TYPE; short key; short value}}. + */ +public class KMCosePairIntegerTag extends KMCosePairTagType { + + private static KMCosePairIntegerTag prototype; + + + private KMCosePairIntegerTag() { + } + + private static KMCosePairIntegerTag proto(short ptr) { + if (prototype == null) { + prototype = new KMCosePairIntegerTag(); + } + instanceTable[KM_COSE_KEY_INT_VAL_OFFSET] = ptr; + return prototype; + } + + // pointer to an empty instance used as expression + public static short exp() { + short ptr = instance(COSE_PAIR_TAG_TYPE, (short) 6); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), KMType.COSE_PAIR_INT_TAG_TYPE); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), KMType.INVALID_VALUE); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), KMInteger.exp()); + return ptr; + } + + public static short instance(short keyPtr, short valuePtr) { + short offset = KMCosePairTagType.getKeyStartOffset(keyPtr); + if (!KMCosePairTagType.isKeyPairValid(heap, offset, KMCose.COSE_KEY_MAX_SIZE, + KMInteger.cast(valuePtr).getShort())) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + short ptr = KMType.instance(COSE_PAIR_TAG_TYPE, (short) 6); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), KMType.COSE_PAIR_INT_TAG_TYPE); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), keyPtr); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), valuePtr); + return ptr; + } + + public static KMCosePairIntegerTag cast(short ptr) { + byte[] heap = repository.getHeap(); + if (heap[ptr] != COSE_PAIR_TAG_TYPE) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + // Validate the value ptr. + short valuePtr = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4)); + if (INTEGER_TYPE != getType(valuePtr)) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + return proto(ptr); + } + + public short getValueType() { + return INTEGER_TYPE; + } + + @Override + public short getKeyPtr() { + return Util.getShort(heap, (short) (instanceTable[KM_COSE_KEY_INT_VAL_OFFSET] + TLV_HEADER_SIZE + 2)); + } + + @Override + public short getValuePtr() { + return Util.getShort(heap, (short) (instanceTable[KM_COSE_KEY_INT_VAL_OFFSET] + TLV_HEADER_SIZE + 4)); + } + + +} diff --git a/Applet/src/com/android/javacard/keymaster/KMCosePairNegIntegerTag.java b/Applet/src/com/android/javacard/keymaster/KMCosePairNegIntegerTag.java new file mode 100644 index 00000000..b2abb95d --- /dev/null +++ b/Applet/src/com/android/javacard/keymaster/KMCosePairNegIntegerTag.java @@ -0,0 +1,92 @@ +/* + * Copyright(C) 2021 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.Util; + +/** + * KMCosePairNegIntegerTag represents a key-value type, where key can be KMInteger or KMNInteger and value is + * KMNInteger type. struct{byte TAG_TYPE; short length; struct{short NINT_VALUE_TYPE; short key; short value}}. + */ +public class KMCosePairNegIntegerTag extends KMCosePairTagType { + + private static KMCosePairNegIntegerTag prototype; + + + private KMCosePairNegIntegerTag() { + } + + private static KMCosePairNegIntegerTag proto(short ptr) { + if (prototype == null) { + prototype = new KMCosePairNegIntegerTag(); + } + instanceTable[KM_COSE_KEY_NINT_VAL_OFFSET] = ptr; + return prototype; + } + + // pointer to an empty instance used as expression + public static short exp() { + short ptr = instance(COSE_PAIR_TAG_TYPE, (short) 6); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), KMType.COSE_PAIR_NEG_INT_TAG_TYPE); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), KMType.INVALID_VALUE); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), KMNInteger.exp()); + return ptr; + } + + public static KMCosePairNegIntegerTag cast(short ptr) { + byte[] heap = repository.getHeap(); + if (heap[ptr] != COSE_PAIR_TAG_TYPE) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + // Validate the value ptr. + short valuePtr = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4)); + if (NEG_INTEGER_TYPE != getType(valuePtr)) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + return proto(ptr); + } + + public static short instance(short keyPtr, short valuePtr) { + short offset = KMCosePairTagType.getKeyStartOffset(keyPtr); + if (!KMCosePairTagType.isKeyPairValid(heap, offset, KMCose.COSE_KEY_MAX_SIZE, + KMNInteger.cast(valuePtr).getShort())) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + short ptr = KMType.instance(COSE_PAIR_TAG_TYPE, (short) 6); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), KMType.COSE_PAIR_NEG_INT_TAG_TYPE); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), keyPtr); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), valuePtr); + return ptr; + } + + public short getValueType() { + return NEG_INTEGER_TYPE; + } + + @Override + public short getKeyPtr() { + return Util.getShort(heap, (short) (instanceTable[KM_COSE_KEY_NINT_VAL_OFFSET] + TLV_HEADER_SIZE + 2)); + } + + @Override + public short getValuePtr() { + return Util.getShort(heap, (short) (instanceTable[KM_COSE_KEY_NINT_VAL_OFFSET] + TLV_HEADER_SIZE + 4)); + } + +} diff --git a/Applet/src/com/android/javacard/keymaster/KMCosePairSimpleValueTag.java b/Applet/src/com/android/javacard/keymaster/KMCosePairSimpleValueTag.java new file mode 100644 index 00000000..6aea5e30 --- /dev/null +++ b/Applet/src/com/android/javacard/keymaster/KMCosePairSimpleValueTag.java @@ -0,0 +1,75 @@ +package com.android.javacard.keymaster; + +import javacard.framework.ISO7816; +import javacard.framework.ISOException; +import javacard.framework.Util; + +/** + * KMCosePairSimpleValueTag represents a key-value type, where key can be KMInteger or KMNInteger and value is + * KMSimpleValue type. struct{byte TAG_TYPE; short length; struct{short SIMPLE_VALUE_TYPE; short key; short value}}. + */ +public class KMCosePairSimpleValueTag extends KMCosePairTagType { + + private static KMCosePairSimpleValueTag prototype; + + private KMCosePairSimpleValueTag() { + } + + private static KMCosePairSimpleValueTag proto(short ptr) { + if (prototype == null) { + prototype = new KMCosePairSimpleValueTag(); + } + instanceTable[KM_COSE_KEY_SIMPLE_VAL_OFFSET] = ptr; + return prototype; + } + + // pointer to an empty instance used as expression + public static short exp() { + short ptr = instance(COSE_PAIR_TAG_TYPE, (short) 6); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), KMType.COSE_PAIR_SIMPLE_VALUE_TAG_TYPE); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), KMType.INVALID_VALUE); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), KMSimpleValue.exp()); + return ptr; + } + + public static short instance(short keyPtr, short valuePtr) { + short offset = KMCosePairTagType.getKeyStartOffset(keyPtr); + if (!KMCosePairTagType.isKeyPairValid(heap, offset, KMCose.COSE_KEY_MAX_SIZE, + KMSimpleValue.cast(valuePtr).getValue())) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + short ptr = KMType.instance(COSE_PAIR_TAG_TYPE, (short) 6); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), KMType.COSE_PAIR_SIMPLE_VALUE_TAG_TYPE); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), keyPtr); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), valuePtr); + return ptr; + } + + public static KMCosePairSimpleValueTag cast(short ptr) { + byte[] heap = repository.getHeap(); + if (heap[ptr] != COSE_PAIR_TAG_TYPE) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + // Validate the value pointer. + short valuePtr = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4)); + if (KMType.getType(valuePtr) != SIMPLE_VALUE_TYPE) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + return proto(ptr); + } + + public short getValueType() { + return SIMPLE_VALUE_TYPE; + } + + @Override + public short getKeyPtr() { + return Util.getShort(heap, (short) (instanceTable[KM_COSE_KEY_SIMPLE_VAL_OFFSET] + TLV_HEADER_SIZE + 2)); + } + + @Override + public short getValuePtr() { + return Util.getShort(heap, (short) (instanceTable[KM_COSE_KEY_SIMPLE_VAL_OFFSET] + TLV_HEADER_SIZE + 4)); + } + +} diff --git a/Applet/src/com/android/javacard/keymaster/KMCosePairTagType.java b/Applet/src/com/android/javacard/keymaster/KMCosePairTagType.java new file mode 100644 index 00000000..053fc633 --- /dev/null +++ b/Applet/src/com/android/javacard/keymaster/KMCosePairTagType.java @@ -0,0 +1,233 @@ +/* + * Copyright(C) 2021 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.Util; + +/** + * This class represents the COSE_Key as defined in https://datatracker.ietf.org/doc/html/rfc8152#section-7. + * This is basically a map containing key value pairs. The label for the key can be (uint / int / tstr) and + * the value can be of any type. But this class is confined to support only key and value types which are + * required for remote key provisioning. So keys of type (int / uint) and values of type (int / uint / simple / bstr) + * only are supported. The structure representing all the sub classes of KMCosePairTagType is as follows: + * KM_COSE_PAIR_TAG_TYPE(1byte), Length(2 bytes), COSE_PAIR_*_TAG_TYPE(2 bytes), Key(2 bytes), Value(2 bytes). + * Key can be either KMInteger or KMNInteger and Value can be either KMIntger or KMNinteger or KMSimpleValue + * or KMByteBlob or KMTextString or KMCoseKey. Each subclass of KMCosePairTagType is named after their corresponding + * value type of the Cose pair. + */ +public abstract class KMCosePairTagType extends KMType { + + /** + * Below table represents the allowed values for a key. The maximum length of the key + * can be 4 bytes so each key is represented as 4 bytes. The allowed values are + * placed next to their corresponding key. + */ + public static Object[] allowedKeyPairs; + + private static void createAllowedKeyPairs() { + if (allowedKeyPairs == null) { + allowedKeyPairs = + new Object[]{ + // Key type + (Object) new byte[]{0, 0, 0, KMCose.COSE_KEY_KEY_TYPE}, (Object) new byte[]{KMCose.COSE_KEY_TYPE_EC2, + KMCose.COSE_KEY_TYPE_SYMMETRIC_KEY}, + // Key Algorithm + (Object) new byte[]{0, 0, 0, KMCose.COSE_KEY_ALGORITHM}, + (Object) new byte[]{KMCose.COSE_ALG_AES_GCM_256, KMCose.COSE_ALG_HMAC_256, + KMCose.COSE_ALG_ECDH_ES_HKDF_256, KMCose.COSE_ALG_ES256}, + // Key operations + (Object) new byte[]{0, 0, 0, KMCose.COSE_KEY_KEY_OPS}, (Object) new byte[]{KMCose.COSE_KEY_OP_SIGN, KMCose.COSE_KEY_OP_VERIFY, + KMCose.COSE_KEY_OP_ENCRYPT, KMCose.COSE_KEY_OP_DECRYPT}, + // Key Curve + (Object) new byte[]{0, 0, 0, KMCose.COSE_KEY_CURVE}, (Object) new byte[]{KMCose.COSE_ECCURVE_256}, + // Header Label Algorithm + (Object) new byte[]{0, 0, 0, KMCose.COSE_LABEL_ALGORITHM}, (Object) new byte[]{KMCose.COSE_ALG_AES_GCM_256, + KMCose.COSE_ALG_HMAC_256, KMCose.COSE_ALG_ES256, KMCose.COSE_ALG_ECDH_ES_HKDF_256}, + // Test Key + KMCose.COSE_TEST_KEY, (Object) new byte[]{KMSimpleValue.NULL}, + }; + } + } + + + /** + * Validates the key and the values corresponding to key. + * + * @param key Buffer containing the key. + * @param keyOff Offset in the buffer from where key starts. + * @param keyLen Length of the key buffer. + * @param value Value corresponding to the key. + * @return true if key pair is valid, otherwise false. + */ + public static boolean isKeyPairValid(byte[] key, short keyOff, short keyLen, short value) { + short index = 0; + short valueIdx; + byte[] values; + boolean valid = false; + createAllowedKeyPairs(); + while (index < allowedKeyPairs.length) { + valueIdx = 0; + if (isEqual((byte[]) allowedKeyPairs[index], (short) 0, (short) ((byte[]) allowedKeyPairs[index]).length, + key, keyOff, keyLen)) { + values = (byte[]) allowedKeyPairs[(short) (index + 1)]; + while (valueIdx < values.length) { + if (values[valueIdx] == (byte) value) { + valid = true; + break; + } + valueIdx++; + } + if (valid) + break; + } + index += (short) 2; + } + return valid; + } + + /** + * Compares two key buffers. + * + * @param key1 First buffer containing the key. + * @param offset1 Offset of the first buffer. + * @param length1 Length of the first buffer. + * @param key2 Second buffer containing the key. + * @param offset2 Offset of the second buffer. + * @param length2 Length of the second buffer. + * @return true if both keys are equal, otherwise false. + */ + private static boolean isEqual(byte[] key1, short offset1, short length1, byte[] key2, short offset2, + short length2) { + if (length1 != length2) + return false; + return (0 == KMInteger.unsignedByteArrayCompare(key1, offset1, key2, offset2, length1)); + } + + /** + * Returns the short value of the key. + * + * @param keyPtr Pointer to either KMInteger or KMNInteger + * @return value of the key as short. + */ + public static short getKeyValueShort(short keyPtr) { + short type = KMType.getType(keyPtr); + short value = 0; + if (type == INTEGER_TYPE) { + value = KMInteger.cast(keyPtr).getShort(); + } else if (type == NEG_INTEGER_TYPE) { + value = KMNInteger.cast(keyPtr).getShort(); + } else { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + return value; + } + + /** + * Returns the significant short value of the key. + * + * @param keyPtr Pointer to either KMInteger or KMNInteger + * @return value of the key as short. + */ + public static short getKeyValueSignificantShort(short keyPtr) { + short type = KMType.getType(keyPtr); + short value = 0; + if (type == INTEGER_TYPE) { + value = KMInteger.cast(keyPtr).getSignificantShort(); + } else if (type == NEG_INTEGER_TYPE) { + value = KMNInteger.cast(keyPtr).getSignificantShort(); + } else { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + return value; + } + + public static void getKeyValue(short keyPtr, byte[] dest, short offset, short len) { + short type = KMType.getType(keyPtr); + if (type == INTEGER_TYPE) { + KMInteger.cast(keyPtr).getValue(dest, offset, len); + } else if (type == NEG_INTEGER_TYPE) { + KMNInteger.cast(keyPtr).getValue(dest, offset, len); + } else { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + } + + /** + * Returns the key offset from the key pointer. + * + * @param keyPtr Pointer to either KMInteger or KMNInteger + * @return offset from where the key starts. + */ + public static short getKeyStartOffset(short keyPtr) { + short type = KMType.getType(keyPtr); + short offset = 0; + if (type == INTEGER_TYPE) { + offset = KMInteger.cast(keyPtr).getStartOff(); + } else if (type == NEG_INTEGER_TYPE) { + offset = KMNInteger.cast(keyPtr).getStartOff(); + } else { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + return offset; + } + + /** + * Returns the key length. + * + * @param keyPtr pointer to either KMInteger/KMInteger. + * @return length of the key. + */ + public static short getKeyLength(short keyPtr) { + short type = KMType.getType(keyPtr); + short len = 0; + if (type == INTEGER_TYPE) { + len = KMInteger.cast(keyPtr).length(); + } else if (type == NEG_INTEGER_TYPE) { + len = KMNInteger.cast(keyPtr).length(); + } else { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + return len; + } + + /** + * This function returns one of COSE_KEY_TAG_*_VALUE_TYPE tag + * information. + * + * @param ptr Pointer to one of the KMCoseKey*Value class. + * @return Tag value type. + */ + public static short getTagValueType(short ptr) { + return Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE)); + } + + /** + * This function returns the key pointer. + * + * @return key pointer. + */ + public abstract short getKeyPtr(); + + /** + * This function returns the value pointer. + * + * @return value pointer. + */ + public abstract short getValuePtr(); +} diff --git a/Applet/src/com/android/javacard/keymaster/KMCosePairTextStringTag.java b/Applet/src/com/android/javacard/keymaster/KMCosePairTextStringTag.java new file mode 100644 index 00000000..165e3d15 --- /dev/null +++ b/Applet/src/com/android/javacard/keymaster/KMCosePairTextStringTag.java @@ -0,0 +1,91 @@ +package com.android.javacard.keymaster; + +import javacard.framework.ISO7816; +import javacard.framework.ISOException; +import javacard.framework.Util; + +/** + * KMCosePairTextStringTag represents a key-value type, where key can be KMInteger or KMNInteger and value is + * KMTextString type. struct{byte TAG_TYPE; short length; struct{short TXT_STR_VALUE_TYPE; short key; short value}}. + */ +public class KMCosePairTextStringTag extends KMCosePairTagType { + + private static KMCosePairTextStringTag prototype; + + public static final byte[] keys = { + KMCose.ISSUER, + KMCose.SUBJECT, + }; + + private KMCosePairTextStringTag() { + } + + private static KMCosePairTextStringTag proto(short ptr) { + if (prototype == null) { + prototype = new KMCosePairTextStringTag(); + } + instanceTable[KM_COSE_KEY_TXT_STR_VAL_OFFSET] = ptr; + return prototype; + } + + // pointer to an empty instance used as expression + public static short exp() { + short ptr = instance(COSE_PAIR_TAG_TYPE, (short) 6); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), KMType.COSE_PAIR_TEXT_STR_TAG_TYPE); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), KMType.INVALID_VALUE); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), KMTextString.exp()); + return ptr; + } + + public static short instance(short keyPtr, short valuePtr) { + if (!isKeyValueValid(KMCosePairTagType.getKeyValueShort(keyPtr))) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + if (KMType.getType(valuePtr) != TEXT_STRING_TYPE) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + short ptr = KMType.instance(COSE_PAIR_TAG_TYPE, (short) 6); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), KMType.COSE_PAIR_TEXT_STR_TAG_TYPE); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), keyPtr); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), valuePtr); + return ptr; + } + + public static KMCosePairTextStringTag cast(short ptr) { + byte[] heap = repository.getHeap(); + if (heap[ptr] != COSE_PAIR_TAG_TYPE) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + // Validate the value pointer. + short valuePtr = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4)); + if (KMType.getType(valuePtr) != TEXT_STRING_TYPE) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + return proto(ptr); + } + + public short getValueType() { + return TEXT_STRING_TYPE; + } + + @Override + public short getKeyPtr() { + return Util.getShort(heap, (short) (instanceTable[KM_COSE_KEY_TXT_STR_VAL_OFFSET] + TLV_HEADER_SIZE + 2)); + } + + @Override + public short getValuePtr() { + return Util.getShort(heap, (short) (instanceTable[KM_COSE_KEY_TXT_STR_VAL_OFFSET] + TLV_HEADER_SIZE + 4)); + } + + public static boolean isKeyValueValid(short keyVal) { + short index = 0; + while (index < (short) keys.length) { + if ((byte) (keyVal & 0xFF) == keys[index]) + return true; + index++; + } + return false; + } + +} diff --git a/Applet/src/com/android/javacard/keymaster/KMDecoder.java b/Applet/src/com/android/javacard/keymaster/KMDecoder.java index c2579372..e68ecaa7 100644 --- a/Applet/src/com/android/javacard/keymaster/KMDecoder.java +++ b/Applet/src/com/android/javacard/keymaster/KMDecoder.java @@ -16,18 +16,21 @@ package com.android.javacard.keymaster; +import com.android.javacard.seprovider.KMException; import javacard.framework.ISO7816; import javacard.framework.ISOException; import javacard.framework.JCSystem; import javacard.framework.Util; - public class KMDecoder { // major types private static final short UINT_TYPE = 0x00; + private static final short NEG_INT_TYPE = 0x20; private static final short BYTES_TYPE = 0x40; + private static final short TSTR_TYPE = 0x60; private static final short ARRAY_TYPE = 0x80; private static final short MAP_TYPE = 0xA0; + private static final short SIMPLE_VALUE_TYPE = 0xE0; // masks private static final short ADDITIONAL_MASK = 0x1F; @@ -48,7 +51,7 @@ public class KMDecoder { public KMDecoder() { bufferRef = JCSystem.makeTransientObjectArray((short) 1, JCSystem.CLEAR_ON_RESET); - scratchBuf = (short[]) JCSystem.makeTransientShortArray(SCRATCH_BUF_SIZE, JCSystem.CLEAR_ON_RESET); + scratchBuf = JCSystem.makeTransientShortArray(SCRATCH_BUF_SIZE, JCSystem.CLEAR_ON_RESET); bufferRef[0] = null; scratchBuf[START_OFFSET] = (short) 0; scratchBuf[LEN_OFFSET] = (short) 0; @@ -89,10 +92,18 @@ private short decode(short exp) { switch (type) { case KMType.BYTE_BLOB_TYPE: return decodeByteBlob(exp); + case KMType.TEXT_STRING_TYPE: + return decodeTstr(exp); case KMType.INTEGER_TYPE: return decodeInteger(exp); + case KMType.SIMPLE_VALUE_TYPE: + return decodeSimpleValue(exp); + case KMType.NEG_INTEGER_TYPE: + return decodeNegInteger(exp); case KMType.ARRAY_TYPE: return decodeArray(exp); + case KMType.MAP_TYPE: + return decodeMap(exp); case KMType.ENUM_TYPE: return decodeEnum(exp); case KMType.KEY_PARAM_TYPE: @@ -105,6 +116,13 @@ private short decode(short exp) { return decodeHmacSharingParam(exp); case KMType.HW_AUTH_TOKEN_TYPE: return decodeHwAuthToken(exp); + case KMType.COSE_KEY_TYPE: + case KMType.COSE_HEADERS_TYPE: + case KMType.COSE_CERT_PAYLOAD_TYPE: + return decodeCoseMap(exp); + case KMType.COSE_PAIR_TAG_TYPE: + short tagValueType = KMCosePairTagType.getTagValueType(exp); + return decodeCosePairTag(tagValueType, exp); case KMType.TAG_TYPE: short tagType = KMTag.getTagType(exp); return decodeTag(tagType, exp); @@ -116,6 +134,8 @@ private short decode(short exp) { private short decodeTag(short tagType, short exp) { switch (tagType) { + case KMType.BIGNUM_TAG: + return decodeBignumTag(exp); case KMType.BYTES_TAG: return decodeBytesTag(exp); case KMType.BOOL_TAG: @@ -157,10 +177,165 @@ private short decodeKeyChar(short exp) { return KMKeyCharacteristics.instance(vals); } + private short decodeCosePairKey(short exp) { + byte[] buffer = (byte[]) bufferRef[0]; + short startOff = scratchBuf[START_OFFSET]; + short keyPtr = (short) 0; + // Cose Key should be always either UINT or Negative int + if ((buffer[startOff] & MAJOR_TYPE_MASK) == UINT_TYPE) { + keyPtr = decodeInteger(exp); + } else if ((buffer[startOff] & MAJOR_TYPE_MASK) == NEG_INT_TYPE) { + keyPtr = decodeNegInteger(exp); + } else { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + return keyPtr; + } + + private short decodeCosePairSimpleValueTag(short exp) { + short keyPtr = decodeCosePairKey((KMCosePairSimpleValueTag.cast(exp).getKeyPtr())); + short valuePtr = decode(KMCosePairSimpleValueTag.cast(exp).getValuePtr()); + return KMCosePairSimpleValueTag.instance(keyPtr, valuePtr); + } + + private short decodeCosePairIntegerValueTag(short exp) { + short keyPtr = decodeCosePairKey((KMCosePairIntegerTag.cast(exp).getKeyPtr())); + short valuePtr = decode(KMCosePairIntegerTag.cast(exp).getValuePtr()); + return KMCosePairIntegerTag.instance(keyPtr, valuePtr); + } + + private short decodeCosePairNegIntegerTag(short exp) { + short keyPtr = decodeCosePairKey((KMCosePairNegIntegerTag.cast(exp).getKeyPtr())); + short valuePtr = decode(KMCosePairNegIntegerTag.cast(exp).getValuePtr()); + return KMCosePairNegIntegerTag.instance(keyPtr, valuePtr); + } + + private short decodeCosePairTxtStringTag(short exp) { + short keyPtr = decodeCosePairKey((KMCosePairTextStringTag.cast(exp).getKeyPtr())); + short valuePtr = decode(KMCosePairTextStringTag.cast(exp).getValuePtr()); + return KMCosePairTextStringTag.instance(keyPtr, valuePtr); + } + + private short decodeCosePairCoseKeyTag(short exp) { + short keyPtr = decodeCosePairKey((KMCosePairCoseKeyTag.cast(exp).getKeyPtr())); + short valuePtr = decode(KMCosePairCoseKeyTag.cast(exp).getValuePtr()); + return KMCosePairCoseKeyTag.instance(keyPtr, valuePtr); + } + + private short decodeCosePairByteBlobTag(short exp) { + short keyPtr = decodeCosePairKey((KMCosePairByteBlobTag.cast(exp).getKeyPtr())); + short valuePtr = decode(KMCosePairByteBlobTag.cast(exp).getValuePtr()); + return KMCosePairByteBlobTag.instance(keyPtr, valuePtr); + } + + private short peekCosePairTagType() { + byte[] buffer = (byte[]) bufferRef[0]; + short startOff = scratchBuf[START_OFFSET]; + // Cose Key should be always either UINT or Negative int + if ((buffer[startOff] & MAJOR_TYPE_MASK) != UINT_TYPE && + (buffer[startOff] & MAJOR_TYPE_MASK) != NEG_INT_TYPE) { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + + short additionalMask = (short) (buffer[startOff] & ADDITIONAL_MASK); + short increment = 0; + if (additionalMask < UINT8_LENGTH) { + increment++; + } else if (additionalMask == UINT8_LENGTH) { + increment += 2; + } else if (additionalMask == UINT16_LENGTH) { + increment += 3; + } else if (additionalMask == UINT32_LENGTH) { + increment += 5; + } else { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + short majorType = (short) (buffer[(short) (startOff + increment)] & MAJOR_TYPE_MASK); + short tagValueType = 0; + if (majorType == BYTES_TYPE) { + tagValueType = KMType.COSE_PAIR_BYTE_BLOB_TAG_TYPE; + } else if (majorType == UINT_TYPE) { + tagValueType = KMType.COSE_PAIR_INT_TAG_TYPE; + } else if (majorType == NEG_INT_TYPE) { + tagValueType = KMType.COSE_PAIR_NEG_INT_TAG_TYPE; + } else if (majorType == MAP_TYPE) { + tagValueType = KMType.COSE_PAIR_COSE_KEY_TAG_TYPE; + } else if (majorType == SIMPLE_VALUE_TYPE) { + tagValueType = KMType.COSE_PAIR_SIMPLE_VALUE_TAG_TYPE; + } else if (majorType == TSTR_TYPE) { + tagValueType = KMType.COSE_PAIR_TEXT_STR_TAG_TYPE; + }else { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + return tagValueType; + } + + private short decodeCosePairTag(short tagValueType, short exp) { + switch (tagValueType) { + case KMType.COSE_PAIR_BYTE_BLOB_TAG_TYPE: + return decodeCosePairByteBlobTag(exp); + case KMType.COSE_PAIR_NEG_INT_TAG_TYPE: + return decodeCosePairNegIntegerTag(exp); + case KMType.COSE_PAIR_INT_TAG_TYPE: + return decodeCosePairIntegerValueTag(exp); + case KMType.COSE_PAIR_SIMPLE_VALUE_TAG_TYPE: + return decodeCosePairSimpleValueTag(exp); + case KMType.COSE_PAIR_COSE_KEY_TAG_TYPE: + return decodeCosePairCoseKeyTag(exp); + case KMType.COSE_PAIR_TEXT_STR_TAG_TYPE: + return decodeCosePairTxtStringTag(exp); + default: + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + return 0; + } + } + + private short decodeCoseMap(short exp) { + short payloadLength = readMajorTypeWithPayloadLength(MAP_TYPE); + // get allowed key pairs + short allowedKeyPairs = KMCoseMap.getVals(exp); + short vals = KMArray.instance(payloadLength); + short length = KMArray.cast(allowedKeyPairs).length(); + short index = 0; + boolean tagFound; + short tagInd; + short cosePairTagType; + short tagClass; + short allowedType; + short obj; + + // For each tag in payload ... + while (index < payloadLength) { + tagFound = false; + tagInd = 0; + cosePairTagType = peekCosePairTagType(); + // Check against the allowed tags ... + while (tagInd < length) { + tagClass = KMArray.cast(allowedKeyPairs).get(tagInd); + allowedType = KMCosePairTagType.getTagValueType(tagClass); + if (allowedType == cosePairTagType) { + obj = decode(tagClass); + KMArray.cast(vals).add(index, obj); + tagFound = true; + break; + } + tagInd++; + } + if (!tagFound) { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } else { + index++; + } + } + return KMCoseMap.createInstanceFromType(exp, vals); + } + private short decodeKeyParam(short exp) { short payloadLength = readMajorTypeWithPayloadLength(MAP_TYPE); // allowed tags short allowedTags = KMKeyParameters.cast(exp).getVals(); + short tagRule = KMArray.cast(allowedTags).get((short)0); + boolean ignoreInvalidTags = KMEnum.cast(tagRule).getVal() == KMType.IGNORE_INVALID_TAGS; short vals = KMArray.instance(payloadLength); short length = KMArray.cast(allowedTags).length(); short index = 0; @@ -173,7 +348,7 @@ private short decodeKeyParam(short exp) { // For each tag in payload ... while (index < payloadLength) { tagFound = false; - tagInd = 0; + tagInd = 1; tagType = peekTagType(); // Check against the allowed tags ... while (tagInd < length) { @@ -182,15 +357,22 @@ private short decodeKeyParam(short exp) { // If it is part of allowed tags ... if (tagType == allowedType) { // then decodeByteBlob and add that to the array. - obj = decode(tagClass); - KMArray.cast(vals).add(index, obj); - tagFound = true; - break; + try { + tagFound = true; + obj = decode(tagClass); + KMArray.cast(vals).add(index, obj); + break; + }catch(KMException e){ + if(KMException.reason() == KMError.INVALID_TAG && + !ignoreInvalidTags){ + KMException.throwIt(KMError.INVALID_TAG); + } + } } tagInd++; } if (!tagFound) { - ISOException.throwIt(ISO7816.SW_DATA_INVALID); + KMException.throwIt(KMError.INVALID_TAG); } else { index++; } @@ -223,6 +405,30 @@ private short decodeBytesTag(short exp) { return KMByteTag.instance(scratchBuf[TAG_KEY_OFFSET], decode(KMByteTag.cast(exp).getValue())); } + private short decodeBignumTag(short exp) { + readTagKey(KMBignumTag.cast(exp).getTagType()); + // The value must be byte blob + return KMBignumTag.instance(scratchBuf[TAG_KEY_OFFSET], decode(KMBignumTag.cast(exp).getValue())); + } + + private short decodeMap(short exp) { + short payloadLength = readMajorTypeWithPayloadLength(MAP_TYPE); + short mapPtr = KMMap.instance(payloadLength); + short index = 0; + short type; + short keyobj; + short valueobj; + while (index < payloadLength) { + type = KMMap.cast(exp).getKey(index); + keyobj = decode(type); + type = KMMap.cast(exp).getKeyValue(index); + valueobj = decode(type); + KMMap.cast(mapPtr).add(index, keyobj, valueobj); + index++; + } + return mapPtr; + } + private short decodeArray(short exp) { short payloadLength = readMajorTypeWithPayloadLength(ARRAY_TYPE); short arrPtr = KMArray.instance(payloadLength); @@ -255,7 +461,7 @@ private short decodeArray(short exp) { private short decodeEnumTag(short exp) { readTagKey(KMEnumTag.cast(exp).getTagType()); - byte[] buffer = (byte[])bufferRef[0]; + byte[] buffer = (byte[]) bufferRef[0]; short startOff = scratchBuf[START_OFFSET]; // Enum Tag value will always be integer with max 1 byte length. if ((buffer[startOff] & MAJOR_TYPE_MASK) != UINT_TYPE) { @@ -282,7 +488,7 @@ private short decodeEnumTag(short exp) { private short decodeBoolTag(short exp) { readTagKey(KMBoolTag.cast(exp).getTagType()); - byte[] buffer = (byte[])bufferRef[0]; + byte[] buffer = (byte[]) bufferRef[0]; short startOff = scratchBuf[START_OFFSET]; // BOOL Tag is a leaf node and it must always have tiny encoded uint value = 1. if ((buffer[startOff] & MAJOR_TYPE_MASK) != UINT_TYPE) { @@ -296,7 +502,7 @@ private short decodeBoolTag(short exp) { } private short decodeEnum(short exp) { - byte[] buffer = (byte[])bufferRef[0]; + byte[] buffer = (byte[]) bufferRef[0]; short startOff = scratchBuf[START_OFFSET]; // Enum value will always be integer with max 1 byte length. if ((buffer[startOff] & MAJOR_TYPE_MASK) != UINT_TYPE) { @@ -321,10 +527,22 @@ private short decodeEnum(short exp) { return KMEnum.instance(KMEnum.cast(exp).getEnumType(), enumVal); } + private short decodeSimpleValue(short exp) { + short inst; + short startOff = scratchBuf[START_OFFSET]; + byte[] buffer = (byte[]) bufferRef[0]; + if ((buffer[startOff] & MAJOR_TYPE_MASK) != SIMPLE_VALUE_TYPE) { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + byte addInfo = (byte) (buffer[startOff] & ADDITIONAL_MASK); + incrementStartOff((short) 1); + return KMSimpleValue.instance(addInfo); + } + private short decodeInteger(short exp) { short inst; short startOff = scratchBuf[START_OFFSET]; - byte[] buffer = (byte[])bufferRef[0]; + byte[] buffer = (byte[]) bufferRef[0]; if ((buffer[startOff] & MAJOR_TYPE_MASK) != UINT_TYPE) { ISOException.throwIt(ISO7816.SW_DATA_INVALID); } @@ -354,15 +572,77 @@ private short decodeInteger(short exp) { return inst; } + private short decodeNegIntegerValue(byte addInfo, byte[] buf, short startOffset) { + short inst; + short len = 0; + short scratchpad; + if (addInfo < UINT8_LENGTH) { + addInfo = (byte) (-1 - addInfo); + inst = KMNInteger.uint_8(addInfo); + } else { + switch (addInfo) { + case UINT8_LENGTH: + len = 1; + break; + case UINT16_LENGTH: + len = 2; + break; + case UINT32_LENGTH: + len = 4; + break; + case UINT64_LENGTH: + len = 8; + break; + default: + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + // Do (-1 - N), as per cbor negative integer decoding rule. + // N is the integer value. + scratchpad = KMByteBlob.instance((short) (len * 3)); + byte[] input = KMByteBlob.cast(scratchpad).getBuffer(); + short offset = KMByteBlob.cast(scratchpad).getStartOff(); + Util.arrayFillNonAtomic(input, offset, len, (byte) -1); + Util.arrayCopyNonAtomic(buf, startOffset, input, (short) (offset + len), len); + KMUtils.subtract(input, offset, (short) (offset + len), (short) (offset + 2 * len), (byte) len); + inst = KMNInteger.instance(input, (short) (offset + 2 * len), len); + incrementStartOff(len); + } + return inst; + } + + private short decodeNegInteger(short exp) { + short startOff = scratchBuf[START_OFFSET]; + byte[] buffer = (byte[]) bufferRef[0]; + if ((buffer[startOff] & MAJOR_TYPE_MASK) != NEG_INT_TYPE) { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + short len = (short) (buffer[startOff] & ADDITIONAL_MASK); + if (len > UINT64_LENGTH) { + ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); + } + incrementStartOff((short) 1); + // startOff is incremented so update the startOff + // with latest value before using it. + startOff = scratchBuf[START_OFFSET]; + return decodeNegIntegerValue((byte) len, buffer, startOff); + } + + private short decodeTstr(short exp) { + short payloadLength = readMajorTypeWithPayloadLength(TSTR_TYPE); + short inst = KMTextString.instance((byte[]) bufferRef[0], scratchBuf[START_OFFSET], payloadLength); + incrementStartOff(payloadLength); + return inst; + } + private short decodeByteBlob(short exp) { short payloadLength = readMajorTypeWithPayloadLength(BYTES_TYPE); - short inst = KMByteBlob.instance((byte[])bufferRef[0], scratchBuf[START_OFFSET], payloadLength); + short inst = KMByteBlob.instance((byte[]) bufferRef[0], scratchBuf[START_OFFSET], payloadLength); incrementStartOff(payloadLength); return inst; } private short peekTagType() { - byte[] buffer = (byte[])bufferRef[0]; + byte[] buffer = (byte[]) bufferRef[0]; short startOff = scratchBuf[START_OFFSET]; if ((buffer[startOff] & MAJOR_TYPE_MASK) != UINT_TYPE) { ISOException.throwIt(ISO7816.SW_DATA_INVALID); @@ -377,7 +657,7 @@ private short peekTagType() { } private void readTagKey(short expectedTagType) { - byte[] buffer = (byte[])bufferRef[0]; + byte[] buffer = (byte[]) bufferRef[0]; short startOff = scratchBuf[START_OFFSET]; if ((buffer[startOff] & MAJOR_TYPE_MASK) != UINT_TYPE) { ISOException.throwIt(ISO7816.SW_DATA_INVALID); @@ -395,7 +675,7 @@ private void readTagKey(short expectedTagType) { // payload length cannot be more then 16 bits. private short readMajorTypeWithPayloadLength(short majorType) { - short payloadLength = 0; + short payloadLength; byte val = readByte(); if ((short) (val & MAJOR_TYPE_MASK) != majorType) { ISOException.throwIt(ISO7816.SW_DATA_INVALID); @@ -415,7 +695,7 @@ private short readMajorTypeWithPayloadLength(short majorType) { } private short readShort() { - byte[] buffer = (byte[])bufferRef[0]; + byte[] buffer = (byte[]) bufferRef[0]; short startOff = scratchBuf[START_OFFSET]; short val = Util.makeShort(buffer[startOff], buffer[(short) (startOff + 1)]); incrementStartOff((short) 2); @@ -424,7 +704,7 @@ private short readShort() { private byte readByte() { short startOff = scratchBuf[START_OFFSET]; - byte val = ((byte[])bufferRef[0])[startOff]; + byte val = ((byte[]) bufferRef[0])[startOff]; incrementStartOff((short) 1); return val; } @@ -437,7 +717,7 @@ private void incrementStartOff(short inc) { } public short readCertificateChainLengthAndHeaderLen(byte[] buf, short bufOffset, - short bufLen) { + short bufLen) { bufferRef[0] = buf; scratchBuf[START_OFFSET] = bufOffset; scratchBuf[LEN_OFFSET] = (short) (bufOffset + bufLen); diff --git a/Applet/src/com/android/javacard/keymaster/KMEncoder.java b/Applet/src/com/android/javacard/keymaster/KMEncoder.java index 14d8ef4c..281ba543 100644 --- a/Applet/src/com/android/javacard/keymaster/KMEncoder.java +++ b/Applet/src/com/android/javacard/keymaster/KMEncoder.java @@ -16,6 +16,7 @@ package com.android.javacard.keymaster; +import com.android.javacard.seprovider.KMException; import javacard.framework.ISO7816; import javacard.framework.ISOException; import javacard.framework.JCSystem; @@ -25,9 +26,12 @@ public class KMEncoder { // major types private static final byte UINT_TYPE = 0x00; + private static final byte NEG_INT_TYPE = 0x20; private static final byte BYTES_TYPE = 0x40; + private static final byte TSTR_TYPE = 0x60; private static final byte ARRAY_TYPE = (byte) 0x80; private static final byte MAP_TYPE = (byte) 0xA0; + private static final byte SIMPLE_VALUE_TYPE = (byte) 0xE0; // masks private static final byte ADDITIONAL_MASK = 0x1F; @@ -51,7 +55,7 @@ public class KMEncoder { public KMEncoder() { bufferRef = JCSystem.makeTransientObjectArray((short) 1, JCSystem.CLEAR_ON_RESET); - scratchBuf = JCSystem.makeTransientShortArray((short) SCRATCH_BUF_SIZE, JCSystem.CLEAR_ON_RESET); + scratchBuf = JCSystem.makeTransientShortArray(SCRATCH_BUF_SIZE, JCSystem.CLEAR_ON_RESET); stack = JCSystem.makeTransientShortArray(STACK_SIZE, JCSystem.CLEAR_ON_RESET); bufferRef[0] = null; scratchBuf[START_OFFSET] = (short) 0; @@ -77,8 +81,8 @@ public short encode(short object, byte[] buffer, short startOff) { scratchBuf[STACK_PTR_OFFSET] = 0; bufferRef[0] = buffer; scratchBuf[START_OFFSET] = startOff; - short len = (short) buffer.length; - if ((len < 0) || (len > KMKeymasterApplet.MAX_LENGTH)) { + short len = (short) (buffer.length - startOff); + if ((len < 0) || len > KMKeymasterApplet.MAX_LENGTH) { scratchBuf[LEN_OFFSET] = KMKeymasterApplet.MAX_LENGTH; } else { scratchBuf[LEN_OFFSET] = (short) buffer.length; @@ -98,7 +102,7 @@ public void encodeCertChain(byte[] buffer, short offset, short length, short err scratchBuf[LEN_OFFSET] += (short) (1 + getEncodedIntegerLength(errInt32Ptr)); writeMajorTypeWithLength(ARRAY_TYPE, (short) 2); // Array of 2 elements - encodeInteger(errInt32Ptr); + encodeUnsignedInteger(errInt32Ptr); } //array{KMError.OK,Array{KMByteBlobs}} @@ -123,7 +127,7 @@ public short encodeCert(byte[] certBuffer, short bufferStart, short certStart, s } bufferStart = scratchBuf[START_OFFSET]; writeMajorTypeWithLength(ARRAY_TYPE, (short) 2); // Array of 2 elements - encodeInteger(errInt32Ptr); //PowerResetStatus + ErrorCode + encodeUnsignedInteger(errInt32Ptr); //PowerResetStatus + ErrorCode writeMajorTypeWithLength(ARRAY_TYPE, (short) 1); // Array of 1 element writeMajorTypeWithLength(BYTES_TYPE, certLength); // Cert Byte Blob of length return bufferStart; @@ -133,7 +137,7 @@ public short encodeError(short errInt32Ptr, byte[] buffer, short startOff, short bufferRef[0] = buffer; scratchBuf[START_OFFSET] = startOff; scratchBuf[LEN_OFFSET] = (short) (startOff + length + 1); - encodeInteger(errInt32Ptr); + encodeUnsignedInteger(errInt32Ptr); return (short) (scratchBuf[START_OFFSET] - startOff); } @@ -145,18 +149,35 @@ private void encode() { case KMType.BYTE_BLOB_TYPE: encodeByteBlob(exp); break; + case KMType.TEXT_STRING_TYPE: + encodeTextString(exp); + break; case KMType.INTEGER_TYPE: - encodeInteger(exp); + encodeUnsignedInteger(exp); + break; + case KMType.SIMPLE_VALUE_TYPE: + encodeSimpleValue(exp); + break; + case KMType.NEG_INTEGER_TYPE: + encodeNegInteger(exp); break; case KMType.ARRAY_TYPE: encodeArray(exp); break; + case KMType.MAP_TYPE: + encodeMap(exp); + break; case KMType.ENUM_TYPE: encodeEnum(exp); break; case KMType.KEY_PARAM_TYPE: encodeKeyParam(exp); break; + case KMType.COSE_KEY_TYPE: + case KMType.COSE_HEADERS_TYPE: + case KMType.COSE_CERT_PAYLOAD_TYPE: + encodeCoseMap(exp); + break; case KMType.KEY_CHAR_TYPE: encodeKeyChar(exp); break; @@ -173,12 +194,83 @@ private void encode() { short tagType = KMTag.getTagType(exp); encodeTag(tagType, exp); break; + case KMType.COSE_PAIR_TAG_TYPE: + short cosePairTagType = KMCosePairTagType.getTagValueType(exp); + encodeCosePairTag(cosePairTagType, exp); + break; default: ISOException.throwIt(ISO7816.SW_DATA_INVALID); } } } + private void encodeCosePairIntegerTag(short exp) { + KMCosePairIntegerTag cosePairIntTag = KMCosePairIntegerTag.cast(exp); + // push key and value ptr in stack to get encoded. + encode(cosePairIntTag.getValuePtr()); + encode(cosePairIntTag.getKeyPtr()); + } + + private void encodeCosePairByteBlobTag(short exp) { + KMCosePairByteBlobTag cosePairByteBlobTag = KMCosePairByteBlobTag.cast(exp); + // push key and value ptr in stack to get encoded. + encode(cosePairByteBlobTag.getValuePtr()); + encode(cosePairByteBlobTag.getKeyPtr()); + } + + private void encodeCosePairCoseKeyTag(short exp) { + KMCosePairCoseKeyTag cosePairCoseKeyTag = KMCosePairCoseKeyTag.cast(exp); + // push key and value ptr in stack to get encoded. + encode(cosePairCoseKeyTag.getValuePtr()); + encode(cosePairCoseKeyTag.getKeyPtr()); + } + + private void encodeCosePairTextStringTag(short exp) { + KMCosePairTextStringTag cosePairTextStringTag = KMCosePairTextStringTag.cast(exp); + // push key and value ptr in stack to get encoded. + encode(cosePairTextStringTag.getValuePtr()); + encode(cosePairTextStringTag.getKeyPtr()); + } + + private void encodeCosePairSimpleValueTag(short exp) { + KMCosePairSimpleValueTag cosePairSimpleValueTag = KMCosePairSimpleValueTag.cast(exp); + // push key and value ptr in stack to get encoded. + encode(cosePairSimpleValueTag.getValuePtr()); + encode(cosePairSimpleValueTag.getKeyPtr()); + } + + private void encodeCosePairNegIntegerTag(short exp) { + KMCosePairNegIntegerTag cosePairNegIntegerTag = KMCosePairNegIntegerTag.cast(exp); + // push key and value ptr in stack to get encoded. + encode(cosePairNegIntegerTag.getValuePtr()); + encode(cosePairNegIntegerTag.getKeyPtr()); + } + + private void encodeCosePairTag(short tagType, short exp) { + switch (tagType) { + case KMType.COSE_PAIR_BYTE_BLOB_TAG_TYPE: + encodeCosePairByteBlobTag(exp); + return; + case KMType.COSE_PAIR_INT_TAG_TYPE: + encodeCosePairIntegerTag(exp); + return; + case KMType.COSE_PAIR_NEG_INT_TAG_TYPE: + encodeCosePairNegIntegerTag(exp); + return; + case KMType.COSE_PAIR_SIMPLE_VALUE_TAG_TYPE: + encodeCosePairSimpleValueTag(exp); + return; + case KMType.COSE_PAIR_TEXT_STR_TAG_TYPE: + encodeCosePairTextStringTag(exp); + return; + case KMType.COSE_PAIR_COSE_KEY_TAG_TYPE: + encodeCosePairCoseKeyTag(exp); + return; + default: + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + } + private void encodeTag(short tagType, short exp) { switch (tagType) { case KMType.BYTES_TAG: @@ -207,6 +299,10 @@ private void encodeTag(short tagType, short exp) { } } + private void encodeCoseMap(short obj) { + encodeAsMap(KMCoseMap.getVals(obj)); + } + private void encodeKeyParam(short obj) { encodeAsMap(KMKeyParameters.cast(obj).getVals()); } @@ -231,8 +327,29 @@ private void encodeArray(short obj) { writeMajorTypeWithLength(ARRAY_TYPE, KMArray.cast(obj).length()); short len = KMArray.cast(obj).length(); short index = (short) (len - 1); + short subObj; + while (index >= 0) { + subObj = KMArray.cast(obj).get(index); + if (subObj != KMType.INVALID_VALUE) + encode(subObj); + index--; + } + } + +public void encodeArrayOnlyLength(short arrLength, byte[] buffer, short offset, short length) { + bufferRef[0] = buffer; + scratchBuf[START_OFFSET] = offset; + scratchBuf[LEN_OFFSET] = (short) (offset + length + 1); + writeMajorTypeWithLength(ARRAY_TYPE, length); + } + + private void encodeMap(short obj) { + writeMajorTypeWithLength(MAP_TYPE, KMMap.cast(obj).length()); + short len = KMMap.cast(obj).length(); + short index = (short) (len - 1); while (index >= 0) { - encode(KMArray.cast(obj).get(index)); + encode(KMMap.cast(obj).getKeyValue(index)); + encode(KMMap.cast(obj).getKey(index)); index--; } } @@ -283,79 +400,125 @@ private void encodeEnum(short obj) { writeByteValue(KMEnum.cast(obj).getVal()); } - /* The total length of UINT Major type along with actual length of - * integer is returned. - */ - public short getEncodedIntegerLength(short obj) { - byte[] val = KMInteger.cast(obj).getBuffer(); - short len = KMInteger.cast(obj).length(); - short startOff = KMInteger.cast(obj).getStartOff(); - byte index = 0; + private void encodeInteger(byte[] val, short len, short startOff, short majorType) { // find out the most significant byte - while (index < len) { - if (val[(short) (startOff + index)] > 0) { - break; - } else if (val[(short) (startOff + index)] < 0) { - break; - } - index++; // index will be equal to len if value is 0. - } + short msbIndex = findMsb(val, startOff, len); // find the difference between most significant byte and len - short diff = (short) (len - index); - switch (diff) { - case 0: case 1: //Byte | Short - if ((val[(short) (startOff + index)] < UINT8_LENGTH) && - (val[(short) (startOff + index)] >= 0)) { - return (short) 1; - } else { - return (short) 2; - } - case 2: //Short - return (short) 3; - case 3: case 4: //Uint32 - return (short) 5; - case 5: case 6: case 7: case 8: //Uint64 - return (short) 9; - default: - ISOException.throwIt(ISO7816.SW_DATA_INVALID); + short diff = (short) (len - msbIndex); + if (diff == 0) { + writeByte((byte) (majorType | 0)); + } else if ((diff == 1) && (val[(short) (startOff + msbIndex)] < UINT8_LENGTH) + && (val[(short) (startOff + msbIndex)] >= 0)) { + writeByte((byte) (majorType | val[(short) (startOff + msbIndex)])); + } else if (diff == 1) { + writeByte((byte) (majorType | UINT8_LENGTH)); + writeByte(val[(short) (startOff + msbIndex)]); + } else if (diff == 2) { + writeByte((byte) (majorType | UINT16_LENGTH)); + writeBytes(val, (short) (startOff + msbIndex), (short) 2); + } else if (diff <= 4) { + writeByte((byte) (majorType | UINT32_LENGTH)); + writeBytes(val, (short) (startOff + len - 4), (short) 4); + } else { + writeByte((byte) (majorType | UINT64_LENGTH)); + writeBytes(val, startOff, (short) 8); } - return 0; } - - private void encodeInteger(short obj) { - byte[] val = KMInteger.cast(obj).getBuffer(); - short len = KMInteger.cast(obj).length(); - short startOff = KMInteger.cast(obj).getStartOff(); + + // find out the most significant byte + public short findMsb(byte[] buf, short offset, short len) { byte index = 0; // find out the most significant byte while (index < len) { - if (val[(short) (startOff + index)] > 0) { + if (buf[(short) (offset + index)] > 0) { break; - } else if (val[(short) (startOff + index)] < 0) { + } else if (buf[(short) (offset + index)] < 0) { break; } index++; // index will be equal to len if value is 0. } + return index; + } + + public void computeOnesCompliment(short msbIndex, byte[] buf, short offset, short len) { // find the difference between most significant byte and len - short diff = (short) (len - index); + short diff = (short) (len - msbIndex); + short correctedOffset = offset; + short correctedLen = len; + // The offset and length of the buffer for Short and Byte types should be + // corrected before computing the 1s compliment. The reason for doing this + // is to avoid computation of 1s compliment on the MSB bytes. if (diff == 0) { - writeByte((byte) (UINT_TYPE | 0)); - } else if ((diff == 1) && (val[(short) (startOff + index)] < UINT8_LENGTH) - && (val[(short) (startOff + index)] >= 0)) { - writeByte((byte) (UINT_TYPE | val[(short) (startOff + index)])); + // Fail + ISOException.throwIt(ISO7816.SW_DATA_INVALID); } else if (diff == 1) { - writeByte((byte) (UINT_TYPE | UINT8_LENGTH)); - writeByte(val[(short) (startOff + index)]); + correctedOffset = (short) (offset + 3); + correctedLen = 1; } else if (diff == 2) { - writeByte((byte) (UINT_TYPE | UINT16_LENGTH)); - writeBytes(val, (short) (startOff + index), (short) 2); - } else if (diff <= 4) { - writeByte((byte) (UINT_TYPE | UINT32_LENGTH)); - writeBytes(val, (short) (startOff + len - 4), (short) 4); - } else { - writeByte((byte) (UINT_TYPE | UINT64_LENGTH)); - writeBytes(val, startOff, (short) 8); + correctedOffset = (short) (offset + 2); + correctedLen = 2; } + // For int and long values the len and offset values are always proper. + // int - 4 bytes + // long - 8 bytes. + KMUtils.computeOnesCompliment(buf, correctedOffset, correctedLen); + } + + // Encoding rule for negative Integers is taken from + // https://datatracker.ietf.org/doc/html/rfc7049#section-2.1, Major type 1. + public short handleNegIntegerEncodingRule(byte[] buf, short offset, short len) { + short msbIndex = findMsb(buf, offset, len); + // Do -1-N, where N is the negative integer + // The value of -1-N is equal to the 1s compliment of N. + computeOnesCompliment(msbIndex, buf, offset, len); + return msbIndex; + } + + // Note: This function modifies the buffer's actual value. So after encoding, restore the original + // value by calling removeNegIntegerEncodingRule(). + public short applyNegIntegerEncodingRule(byte[] buf, short offset, short len) { + return handleNegIntegerEncodingRule(buf, offset, len); + } + + public void removeNegIntegerEncodingRule(byte[] buf, short offset, short len, short origMsbIndex) { + // Do -1-N, where N is the negative integer + // The value of -1-N is equal to the 1s compliment of N. + computeOnesCompliment(origMsbIndex, buf, offset, len); + } + + private void encodeNegInteger(short obj) { + byte[] val = KMNInteger.cast(obj).getBuffer(); + short len = KMNInteger.cast(obj).length(); + short startOff = KMNInteger.cast(obj).getStartOff(); + short msbIndex = applyNegIntegerEncodingRule(val, startOff, len); + encodeInteger(val, len, startOff, NEG_INT_TYPE); + removeNegIntegerEncodingRule(val, startOff, len, msbIndex); + } + + private void encodeUnsignedInteger(short obj) { + byte[] val = KMInteger.cast(obj).getBuffer(); + short len = KMInteger.cast(obj).length(); + short startOff = KMInteger.cast(obj).getStartOff(); + encodeInteger(val, len, startOff, UINT_TYPE); + } + + private void encodeSimpleValue(short obj) { + byte value = KMSimpleValue.cast(obj).getValue(); + writeByte((byte) (SIMPLE_VALUE_TYPE | value)); + } + + private void encodeTextString(short obj) { + writeMajorTypeWithLength(TSTR_TYPE, KMTextString.cast(obj).length()); + writeBytes(KMTextString.cast(obj).getBuffer(), KMTextString.cast(obj).getStartOff(), + KMTextString.cast(obj).length()); + } + + public short encodeByteBlobHeader(short bufLen, byte[] buffer, short startOff, short length) { + bufferRef[0] = buffer; + scratchBuf[START_OFFSET] = startOff; + scratchBuf[LEN_OFFSET] = (short) (startOff + length + 1); + writeMajorTypeWithLength(BYTES_TYPE, bufLen); + return (short) (scratchBuf[START_OFFSET] - startOff); } private void encodeByteBlob(short obj) { @@ -364,12 +527,181 @@ private void encodeByteBlob(short obj) { KMByteBlob.cast(obj).length()); } + public short getEncodedLength(short ptr) { + short len = 0; + short type = KMType.getType(ptr); + switch (type) { + case KMType.BYTE_BLOB_TYPE: + len += getEncodedByteBlobLength(ptr); + break; + case KMType.TEXT_STRING_TYPE: + len += getEncodedTextStringLength(ptr); + break; + case KMType.INTEGER_TYPE: + len += getEncodedIntegerLength(ptr); + break; + case KMType.NEG_INTEGER_TYPE: + len += getEncodedNegIntegerLength(ptr); + break; + case KMType.ARRAY_TYPE: + len += getEncodedArrayLen(ptr); + break; + case KMType.MAP_TYPE: + len += getEncodedMapLen(ptr); + break; + case KMType.COSE_PAIR_TAG_TYPE: + short cosePairTagType = KMCosePairTagType.getTagValueType(ptr); + len += getEncodedCosePairTagLen(cosePairTagType, ptr); + break; + case KMType.COSE_KEY_TYPE: + case KMType.COSE_HEADERS_TYPE: + case KMType.COSE_CERT_PAYLOAD_TYPE: + len += getEncodedArrayLen(KMCoseMap.getVals(ptr)); + break; + default: + KMException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + return len; + } + + private short getEncodedCosePairTagLen(short tagType, short exp) { + short length = 0; + switch (tagType) { + case KMType.COSE_PAIR_BYTE_BLOB_TAG_TYPE: + KMCosePairByteBlobTag cosePairByteBlobTag = KMCosePairByteBlobTag.cast(exp); + length = getEncodedLength(cosePairByteBlobTag.getKeyPtr()); + length += getEncodedLength(cosePairByteBlobTag.getValuePtr()); + break; + case KMType.COSE_PAIR_INT_TAG_TYPE: + KMCosePairIntegerTag cosePairIntTag = KMCosePairIntegerTag.cast(exp); + length = getEncodedLength(cosePairIntTag.getValuePtr()); + length += getEncodedLength(cosePairIntTag.getKeyPtr()); + break; + case KMType.COSE_PAIR_NEG_INT_TAG_TYPE: + KMCosePairNegIntegerTag cosePairNegIntegerTag = KMCosePairNegIntegerTag.cast(exp); + length = getEncodedLength(cosePairNegIntegerTag.getValuePtr()); + length += getEncodedLength(cosePairNegIntegerTag.getKeyPtr()); + break; + case KMType.COSE_PAIR_SIMPLE_VALUE_TAG_TYPE: + KMCosePairSimpleValueTag cosePairSimpleValueTag = KMCosePairSimpleValueTag.cast(exp); + length = getEncodedLength(cosePairSimpleValueTag.getValuePtr()); + length += getEncodedLength(cosePairSimpleValueTag.getKeyPtr()); + break; + case KMType.COSE_PAIR_TEXT_STR_TAG_TYPE: + KMCosePairTextStringTag cosePairTextStringTag = KMCosePairTextStringTag.cast(exp); + length = getEncodedLength(cosePairTextStringTag.getValuePtr()); + length += getEncodedLength(cosePairTextStringTag.getKeyPtr()); + break; + case KMType.COSE_PAIR_COSE_KEY_TAG_TYPE: + KMCosePairCoseKeyTag cosePairCoseKeyTag = KMCosePairCoseKeyTag.cast(exp); + length = getEncodedLength(cosePairCoseKeyTag.getValuePtr()); + length += getEncodedLength(cosePairCoseKeyTag.getKeyPtr()); + break; + default: + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + return length; + } + + private short getEncodedMapLen(short obj) { + short mapLen = KMMap.cast(obj).length(); + short len = getEncodedBytesLength(mapLen); + short index = 0; + while (index < mapLen) { + len += getEncodedLength(KMMap.cast(obj).getKey(index)); + len += getEncodedLength(KMMap.cast(obj).getKeyValue(index)); + index++; + } + return len; + } + + private short getEncodedArrayLen(short obj) { + short arrLen = KMArray.cast(obj).length(); + short len = getEncodedBytesLength(arrLen); + short index = 0; + short subObj; + while (index < arrLen) { + subObj = KMArray.cast(obj).get(index); + if (subObj != KMType.INVALID_VALUE) + len += getEncodedLength(subObj); + index++; + } + return len; + } + + private short getEncodedBytesLength(short len) { + short ret = 0; + if (len < KMEncoder.UINT8_LENGTH && len >= 0) { + ret = 1; + } else if (len >= KMEncoder.UINT8_LENGTH && len <= (short) 0x00FF) { + ret = 2; + } else if (len > (short) 0x00FF && len <= (short) 0x7FFF) { + ret = 3; + } else { + ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); + } + return ret; + } + + private short getEncodedByteBlobLength(short obj) { + short len = KMByteBlob.cast(obj).length(); + len += getEncodedBytesLength(len); + return len; + } + + private short getEncodedTextStringLength(short obj) { + short len = KMTextString.cast(obj).length(); + len += getEncodedBytesLength(len); + return len; + } + + private short getEncodedNegIntegerLength(short obj) { + byte[] buf = KMNInteger.cast(obj).getBuffer(); + short len = KMNInteger.cast(obj).length(); + short offset = KMNInteger.cast(obj).getStartOff(); + short msbIndex = applyNegIntegerEncodingRule(buf, offset, len); + short ret = getEncodedIntegerLength(buf, offset, len); + removeNegIntegerEncodingRule(buf, offset, len, msbIndex); + return ret; + } + + private short getEncodedIntegerLength(byte[] val, short startOff, short len) { + short msbIndex = findMsb(val, startOff, len); + // find the difference between most significant byte and len + short diff = (short) (len - msbIndex); + switch (diff) { + case 0: case 1: //Byte + if ((val[(short) (startOff + msbIndex)] < KMEncoder.UINT8_LENGTH) && + (val[(short) (startOff + msbIndex)] >= 0)) { + return (short) 1; + } else { + return (short) 2; + } + case 2: //Short + return (short) 3; + case 3: case 4: //UInt32 + return (short) 5; + case 5: case 6: case 7: case 8: //UInt64 + return (short) 9; + default: + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + return 0; + } + + private short getEncodedIntegerLength(short obj) { + byte[] val = KMInteger.cast(obj).getBuffer(); + short len = KMInteger.cast(obj).length(); + short startOff = KMInteger.cast(obj).getStartOff(); + return getEncodedIntegerLength(val, startOff, len); + } + private void writeByteValue(byte val) { if ((val < UINT8_LENGTH) && (val >= 0)) { writeByte((byte) (UINT_TYPE | val)); } else { writeByte((byte) (UINT_TYPE | UINT8_LENGTH)); - writeByte((byte) val); + writeByte(val); } } diff --git a/Applet/src/com/android/javacard/keymaster/KMEnum.java b/Applet/src/com/android/javacard/keymaster/KMEnum.java index 2b55a6ce..1cfe5803 100644 --- a/Applet/src/com/android/javacard/keymaster/KMEnum.java +++ b/Applet/src/com/android/javacard/keymaster/KMEnum.java @@ -31,14 +31,15 @@ public class KMEnum extends KMType { // The allowed enum types. private static short[] types = { - HARDWARE_TYPE, - KEY_FORMAT, - KEY_DERIVATION_FUNCTION, - VERIFIED_BOOT_STATE, - DEVICE_LOCKED, - USER_AUTH_TYPE, - PURPOSE, - ECCURVE + HARDWARE_TYPE, + KEY_FORMAT, + KEY_DERIVATION_FUNCTION, + VERIFIED_BOOT_STATE, + DEVICE_LOCKED, + USER_AUTH_TYPE, + PURPOSE, + ECCURVE, + RULE }; private static Object[] enums = null; @@ -50,7 +51,7 @@ private static KMEnum proto(short ptr) { if (prototype == null) { prototype = new KMEnum(); } - instanceTable[KM_ENUM_OFFSET] = ptr; + KMType.instanceTable[KM_ENUM_OFFSET] = ptr; return prototype; } @@ -60,7 +61,7 @@ public static short exp() { } public short length() { - return Util.getShort(heap, (short) (instanceTable[KM_ENUM_OFFSET] + 1)); + return Util.getShort(heap, (short) (KMType.instanceTable[KM_ENUM_OFFSET] + 1)); } public static KMEnum cast(short ptr) { @@ -96,40 +97,41 @@ private static void create() { // The allowed enum values to corresponding enum types in the types array. if (enums == null) { enums = - new Object[]{ - new byte[]{SOFTWARE, TRUSTED_ENVIRONMENT, STRONGBOX}, - new byte[]{X509, PKCS8, RAW}, - new byte[]{ - DERIVATION_NONE, - RFC5869_SHA256, - ISO18033_2_KDF1_SHA1, - ISO18033_2_KDF1_SHA256, - ISO18033_2_KDF2_SHA1, - ISO18033_2_KDF2_SHA256 - }, - new byte[]{SELF_SIGNED_BOOT, VERIFIED_BOOT, UNVERIFIED_BOOT, FAILED_BOOT}, - new byte[]{DEVICE_LOCKED_TRUE, DEVICE_LOCKED_FALSE}, - new byte[]{USER_AUTH_NONE, PASSWORD, FINGERPRINT, BOTH}, - new byte[]{ENCRYPT, DECRYPT, SIGN, VERIFY, WRAP_KEY, ATTEST_KEY}, - new byte[]{P_224, P_256, P_384, P_521} - }; + new Object[]{ + new byte[]{SOFTWARE, TRUSTED_ENVIRONMENT, STRONGBOX}, + new byte[]{X509, PKCS8, RAW}, + new byte[]{ + DERIVATION_NONE, + RFC5869_SHA256, + ISO18033_2_KDF1_SHA1, + ISO18033_2_KDF1_SHA256, + ISO18033_2_KDF2_SHA1, + ISO18033_2_KDF2_SHA256 + }, + new byte[]{SELF_SIGNED_BOOT, VERIFIED_BOOT, UNVERIFIED_BOOT, FAILED_BOOT}, + new byte[]{DEVICE_LOCKED_TRUE, DEVICE_LOCKED_FALSE}, + new byte[]{USER_AUTH_NONE, PASSWORD, FINGERPRINT, BOTH}, + new byte[]{ENCRYPT, DECRYPT, SIGN, VERIFY, WRAP_KEY, ATTEST_KEY, AGREE_KEY}, + new byte[]{P_224, P_256, P_384, P_521}, + new byte[]{IGNORE_INVALID_TAGS, FAIL_ON_INVALID_TAGS} + }; } } public void setVal(byte val) { - heap[(short) (instanceTable[KM_ENUM_OFFSET] + TLV_HEADER_SIZE + 2)] = val; + heap[(short) (KMType.instanceTable[KM_ENUM_OFFSET] + TLV_HEADER_SIZE + 2)] = val; } public byte getVal() { - return heap[(short) (instanceTable[KM_ENUM_OFFSET] + TLV_HEADER_SIZE + 2)]; + return heap[(short) (KMType.instanceTable[KM_ENUM_OFFSET] + TLV_HEADER_SIZE + 2)]; } public void setEnumType(short type) { - Util.setShort(heap, (short) (instanceTable[KM_ENUM_OFFSET] + TLV_HEADER_SIZE), type); + Util.setShort(heap, (short) (KMType.instanceTable[KM_ENUM_OFFSET] + TLV_HEADER_SIZE), type); } public short getEnumType() { - return Util.getShort(heap, (short) (instanceTable[KM_ENUM_OFFSET] + TLV_HEADER_SIZE)); + return Util.getShort(heap, (short) (KMType.instanceTable[KM_ENUM_OFFSET] + TLV_HEADER_SIZE)); } // isValidTag enumeration keys and values. diff --git a/Applet/src/com/android/javacard/keymaster/KMEnumArrayTag.java b/Applet/src/com/android/javacard/keymaster/KMEnumArrayTag.java index cd3981c4..0f932a9d 100644 --- a/Applet/src/com/android/javacard/keymaster/KMEnumArrayTag.java +++ b/Applet/src/com/android/javacard/keymaster/KMEnumArrayTag.java @@ -29,7 +29,7 @@ public class KMEnumArrayTag extends KMTag { private static KMEnumArrayTag prototype; // The allowed tag keys of enum array type. - private static final short[] tags = {PURPOSE, BLOCK_MODE, DIGEST, PADDING}; + private static short[] tags = {PURPOSE, BLOCK_MODE, DIGEST, PADDING, RSA_OAEP_MGF_DIGEST}; // Tag Values. private static Object[] enums = null; @@ -41,7 +41,7 @@ private static KMEnumArrayTag proto(short ptr) { if (prototype == null) { prototype = new KMEnumArrayTag(); } - instanceTable[KM_ENUM_ARRAY_TAG_OFFSET] = ptr; + KMType.instanceTable[KM_ENUM_ARRAY_TAG_OFFSET] = ptr; return prototype; } @@ -106,7 +106,7 @@ public static KMEnumArrayTag cast(short ptr) { } public short getKey() { - return Util.getShort(heap, (short) (instanceTable[KM_ENUM_ARRAY_TAG_OFFSET] + TLV_HEADER_SIZE + 2)); + return Util.getShort(heap, (short) (KMType.instanceTable[KM_ENUM_ARRAY_TAG_OFFSET] + TLV_HEADER_SIZE + 2)); } public short getTagType() { @@ -114,11 +114,11 @@ public short getTagType() { } public short getValues() { - return Util.getShort(heap, (short) (instanceTable[KM_ENUM_ARRAY_TAG_OFFSET] + TLV_HEADER_SIZE + 4)); + return Util.getShort(heap, (short) (KMType.instanceTable[KM_ENUM_ARRAY_TAG_OFFSET] + TLV_HEADER_SIZE + 4)); } public short length() { - short blobPtr = Util.getShort(heap, (short) (instanceTable[KM_ENUM_ARRAY_TAG_OFFSET] + TLV_HEADER_SIZE + 4)); + short blobPtr = Util.getShort(heap, (short) (KMType.instanceTable[KM_ENUM_ARRAY_TAG_OFFSET] + TLV_HEADER_SIZE + 4)); return KMByteBlob.cast(blobPtr).length(); } @@ -126,14 +126,16 @@ public static void create() { if (enums == null) { // allowed tag values. enums = - new Object[]{ - new byte[]{ENCRYPT, DECRYPT, SIGN, VERIFY, WRAP_KEY, ATTEST_KEY}, - new byte[]{ECB, CBC, CTR, GCM}, - new byte[]{DIGEST_NONE, MD5, SHA1, SHA2_224, SHA2_256, SHA2_384, SHA2_512}, - new byte[]{ - PADDING_NONE, RSA_OAEP, RSA_PSS, RSA_PKCS1_1_5_ENCRYPT, RSA_PKCS1_1_5_SIGN, PKCS7 - } - }; + new Object[]{ + new byte[]{ENCRYPT, DECRYPT, SIGN, VERIFY, WRAP_KEY, ATTEST_KEY, AGREE_KEY}, + new byte[]{ECB, CBC, CTR, GCM}, + new byte[]{DIGEST_NONE, MD5, SHA1, SHA2_224, SHA2_256, SHA2_384, SHA2_512}, + new byte[]{ + PADDING_NONE, RSA_OAEP, RSA_PSS, RSA_PKCS1_1_5_ENCRYPT, RSA_PKCS1_1_5_SIGN, PKCS7 + }, + new byte[]{DIGEST_NONE, MD5, SHA1, SHA2_224, SHA2_256, SHA2_384, SHA2_512}, + + }; } } @@ -233,10 +235,10 @@ public boolean isValidPaddingModes(byte alg) { switch (alg) { case KMType.RSA: if (padding != KMType.RSA_OAEP - && padding != KMType.PADDING_NONE - && padding != KMType.RSA_PKCS1_1_5_SIGN - && padding != KMType.RSA_PKCS1_1_5_ENCRYPT - && padding != KMType.RSA_PSS) { + && padding != KMType.PADDING_NONE + && padding != KMType.RSA_PKCS1_1_5_SIGN + && padding != KMType.RSA_PKCS1_1_5_ENCRYPT + && padding != KMType.RSA_PSS) { return false; } break; diff --git a/Applet/src/com/android/javacard/keymaster/KMEnumTag.java b/Applet/src/com/android/javacard/keymaster/KMEnumTag.java index 7493aa3d..447b1b06 100644 --- a/Applet/src/com/android/javacard/keymaster/KMEnumTag.java +++ b/Applet/src/com/android/javacard/keymaster/KMEnumTag.java @@ -29,10 +29,9 @@ public class KMEnumTag extends KMTag { private static KMEnumTag prototype; - // The allowed tag keys of type enum tag. private static short[] tags = { - ALGORITHM, ECCURVE, BLOB_USAGE_REQ, USER_AUTH_TYPE, ORIGIN, HARDWARE_TYPE + ALGORITHM, ECCURVE, BLOB_USAGE_REQ, USER_AUTH_TYPE, ORIGIN, HARDWARE_TYPE }; private static Object[] enums = null; @@ -44,7 +43,7 @@ private static KMEnumTag proto(short ptr) { if (prototype == null) { prototype = new KMEnumTag(); } - instanceTable[KM_ENUM_TAG_OFFSET] = ptr; + KMType.instanceTable[KM_ENUM_TAG_OFFSET] = ptr; return prototype; } @@ -87,7 +86,7 @@ public static KMEnumTag cast(short ptr) { } public short getKey() { - return Util.getShort(heap, (short) (instanceTable[KM_ENUM_TAG_OFFSET] + TLV_HEADER_SIZE + 2)); + return Util.getShort(heap, (short) (KMType.instanceTable[KM_ENUM_TAG_OFFSET] + TLV_HEADER_SIZE + 2)); } public short getTagType() { @@ -95,22 +94,22 @@ public short getTagType() { } public byte getValue() { - return heap[(short) (instanceTable[KM_ENUM_TAG_OFFSET] + TLV_HEADER_SIZE + 4)]; + return heap[(short) (KMType.instanceTable[KM_ENUM_TAG_OFFSET] + TLV_HEADER_SIZE + 4)]; } public static void create() { if (enums == null) { // enum tag values. enums = - new Object[]{ - new byte[]{RSA, DES, EC, AES, HMAC}, - new byte[]{P_224, P_256, P_384, P_521}, - new byte[]{STANDALONE, REQUIRES_FILE_SYSTEM}, - new byte[]{USER_AUTH_NONE, PASSWORD, FINGERPRINT, (byte) (PASSWORD & FINGERPRINT), - ANY}, - new byte[]{GENERATED, DERIVED, IMPORTED, UNKNOWN, SECURELY_IMPORTED}, - new byte[]{SOFTWARE, TRUSTED_ENVIRONMENT, STRONGBOX} - }; + new Object[]{ + new byte[]{RSA, DES, EC, AES, HMAC}, + new byte[]{P_224, P_256, P_384, P_521}, + new byte[]{STANDALONE, REQUIRES_FILE_SYSTEM}, + new byte[]{USER_AUTH_NONE, PASSWORD, FINGERPRINT, (byte) (PASSWORD & FINGERPRINT), + ANY}, + new byte[]{GENERATED, DERIVED, IMPORTED, UNKNOWN, SECURELY_IMPORTED}, + new byte[]{SOFTWARE, TRUSTED_ENVIRONMENT, STRONGBOX} + }; } } @@ -145,8 +144,8 @@ private static boolean validateEnum(short key, byte value) { return false; } - public static short getValue(short tagType, short keyParameters) { - short tagPtr = KMKeyParameters.findTag(KMType.ENUM_TAG, tagType, keyParameters); + public static short getValue(short tagKey, short keyParameters) { + short tagPtr = KMKeyParameters.findTag(KMType.ENUM_TAG, tagKey, keyParameters); if (tagPtr != KMType.INVALID_VALUE) { return heap[(short) (tagPtr + TLV_HEADER_SIZE + 4)]; } diff --git a/Applet/src/com/android/javacard/keymaster/KMError.java b/Applet/src/com/android/javacard/keymaster/KMError.java index 0b4373d3..0c5db5b5 100644 --- a/Applet/src/com/android/javacard/keymaster/KMError.java +++ b/Applet/src/com/android/javacard/keymaster/KMError.java @@ -65,12 +65,21 @@ public class KMError { public static final short UNSUPPORTED_EC_CURVE = 61; public static final short KEY_REQUIRES_UPGRADE = 62; + public static final short ATTESTATION_CHALLENGE_MISSING = 63; public static final short ATTESTATION_APPLICATION_ID_MISSING = 65; public static final short CANNOT_ATTEST_IDS = 66; public static final short ROLLBACK_RESISTANCE_UNAVAILABLE = 67; public static final short DEVICE_LOCKED = 72; public static final short EARLY_BOOT_ENDED = 73; + public static final short ATTESTATION_KEYS_NOT_PROVISIONED =74; + public static final short INCOMPATIBLE_MGF_DIGEST = 78; + public static final short UNSUPPORTED_MGF_DIGEST = 79; + public static final short MISSING_NOT_BEFORE = 80; + public static final short MISSING_NOT_AFTER = 81; + public static final short MISSING_ISSUER_SUBJECT_NAME = 82; + public static final short INVALID_ISSUER_SUBJECT_NAME = 83; + public static final short UNIMPLEMENTED = 100; public static final short UNKNOWN_ERROR = 1000; @@ -82,6 +91,7 @@ public class KMError { public static final short CMD_NOT_ALLOWED = 10005; public static final short SW_WRONG_LENGTH = 10006; public static final short INVALID_DATA = 10007; + //Crypto errors public static final short CRYPTO_ILLEGAL_USE = 10008; public static final short CRYPTO_ILLEGAL_VALUE = 10009; @@ -91,4 +101,34 @@ public class KMError { //Generic Unknown error. public static final short GENERIC_UNKNOWN_ERROR = 10013; + // Remote key provisioning error codes. + public static final short STATUS_FAILED = 32000; + public static final short STATUS_INVALID_MAC = 32001; + public static final short STATUS_PRODUCTION_KEY_IN_TEST_REQUEST = 32002; + public static final short STATUS_TEST_KEY_IN_PRODUCTION_REQUEST = 32003; + public static final short STATUS_INVALID_EEK = 32004; + public static final short INVALID_STATE = 32005; + + public static short translate(short err) { + switch(err) { + case SW_CONDITIONS_NOT_SATISFIED: + case UNSUPPORTED_CLA: + case INVALID_P1P2: + case INVALID_DATA: + case CRYPTO_ILLEGAL_USE: + case CRYPTO_ILLEGAL_VALUE: + case CRYPTO_INVALID_INIT: + case CRYPTO_UNINITIALIZED_KEY: + case GENERIC_UNKNOWN_ERROR: + case UNKNOWN_ERROR: + return UNKNOWN_ERROR; + case CRYPTO_NO_SUCH_ALGORITHM: + return UNSUPPORTED_ALGORITHM; + case UNSUPPORTED_INSTRUCTION: + case CMD_NOT_ALLOWED: + case SW_WRONG_LENGTH: + return UNIMPLEMENTED; + } + return err; + } } diff --git a/Applet/src/com/android/javacard/keymaster/KMHardwareAuthToken.java b/Applet/src/com/android/javacard/keymaster/KMHardwareAuthToken.java index 900e9069..deb53cf6 100644 --- a/Applet/src/com/android/javacard/keymaster/KMHardwareAuthToken.java +++ b/Applet/src/com/android/javacard/keymaster/KMHardwareAuthToken.java @@ -57,7 +57,7 @@ private static KMHardwareAuthToken proto(short ptr) { if (prototype == null) { prototype = new KMHardwareAuthToken(); } - instanceTable[KM_HARDWARE_AUTH_TOKEN_OFFSET] = ptr; + KMType.instanceTable[KM_HARDWARE_AUTH_TOKEN_OFFSET] = ptr; return prototype; } @@ -95,7 +95,7 @@ public static KMHardwareAuthToken cast(short ptr) { } public short getVals() { - return Util.getShort(heap, (short) (instanceTable[KM_HARDWARE_AUTH_TOKEN_OFFSET] + TLV_HEADER_SIZE)); + return Util.getShort(heap, (short) (KMType.instanceTable[KM_HARDWARE_AUTH_TOKEN_OFFSET] + TLV_HEADER_SIZE)); } public short length() { diff --git a/Applet/src/com/android/javacard/keymaster/KMHmacSharingParameters.java b/Applet/src/com/android/javacard/keymaster/KMHmacSharingParameters.java index f89ac608..49a14ebe 100644 --- a/Applet/src/com/android/javacard/keymaster/KMHmacSharingParameters.java +++ b/Applet/src/com/android/javacard/keymaster/KMHmacSharingParameters.java @@ -48,7 +48,7 @@ private static KMHmacSharingParameters proto(short ptr) { if (prototype == null) { prototype = new KMHmacSharingParameters(); } - instanceTable[KM_HMAC_SHARING_PARAMETERS_OFFSET] = ptr; + KMType.instanceTable[KM_HMAC_SHARING_PARAMETERS_OFFSET] = ptr; return prototype; } @@ -78,7 +78,7 @@ public static KMHmacSharingParameters cast(short ptr) { } public short getVals() { - return Util.getShort(heap, (short) (instanceTable[KM_HMAC_SHARING_PARAMETERS_OFFSET] + TLV_HEADER_SIZE)); + return Util.getShort(heap, (short) (KMType.instanceTable[KM_HMAC_SHARING_PARAMETERS_OFFSET] + TLV_HEADER_SIZE)); } public short length() { diff --git a/Applet/src/com/android/javacard/keymaster/KMInteger.java b/Applet/src/com/android/javacard/keymaster/KMInteger.java index aee6f9d5..8ae8c8c9 100644 --- a/Applet/src/com/android/javacard/keymaster/KMInteger.java +++ b/Applet/src/com/android/javacard/keymaster/KMInteger.java @@ -16,6 +16,7 @@ package com.android.javacard.keymaster; +import com.android.javacard.seprovider.KMException; import javacard.framework.ISO7816; import javacard.framework.ISOException; import javacard.framework.Util; @@ -30,14 +31,14 @@ public class KMInteger extends KMType { public static final short UINT_64 = 8; private static KMInteger prototype; - private KMInteger() { + protected KMInteger() { } private static KMInteger proto(short ptr) { if (prototype == null) { prototype = new KMInteger(); } - instanceTable[KM_INTEGER_OFFSET] = ptr; + KMType.instanceTable[KM_INTEGER_OFFSET] = ptr; return prototype; } @@ -114,7 +115,7 @@ public static short uint_64(byte[] num, short offset) { // Get the length of the integer public short length() { - return Util.getShort(heap, (short) (instanceTable[KM_INTEGER_OFFSET] + 1)); + return Util.getShort(heap, (short) (getBaseOffset() + 1)); } // Get the buffer pointer in which blob is contained. @@ -124,7 +125,7 @@ public byte[] getBuffer() { // Get the start of value public short getStartOff() { - return (short) (instanceTable[KM_INTEGER_OFFSET] + TLV_HEADER_SIZE); + return (short) (getBaseOffset() + TLV_HEADER_SIZE); } public void getValue(byte[] dest, short destOff, short length) { @@ -135,28 +136,28 @@ public void getValue(byte[] dest, short destOff, short length) { length = length(); destOff += length; } - Util.arrayCopyNonAtomic(heap, (short) (instanceTable[KM_INTEGER_OFFSET] + TLV_HEADER_SIZE), dest, destOff, length); + Util.arrayCopyNonAtomic(heap, getStartOff(), dest, destOff, length); } public void setValue(byte[] src, short srcOff) { - Util.arrayCopyNonAtomic(src, srcOff, heap, (short) (instanceTable[KM_INTEGER_OFFSET] + TLV_HEADER_SIZE), length()); + Util.arrayCopyNonAtomic(src, srcOff, heap, getStartOff(), length()); } public short value(byte[] dest, short destOff) { - Util.arrayCopyNonAtomic(heap, (short) (instanceTable[KM_INTEGER_OFFSET] + TLV_HEADER_SIZE), dest, destOff, length()); + Util.arrayCopyNonAtomic(heap, getStartOff(), dest, destOff, length()); return length(); } public short getShort() { - return Util.getShort(heap, (short) (instanceTable[KM_INTEGER_OFFSET] + TLV_HEADER_SIZE + 2)); + return Util.getShort(heap, (short) (getStartOff() + 2)); } public short getSignificantShort() { - return Util.getShort(heap, (short) (instanceTable[KM_INTEGER_OFFSET] + TLV_HEADER_SIZE)); + return Util.getShort(heap, getStartOff()); } public byte getByte() { - return heap[(short) (instanceTable[KM_INTEGER_OFFSET] + TLV_HEADER_SIZE + 3)]; + return heap[(short) (getStartOff() + 3)]; } public boolean isZero() { @@ -176,13 +177,13 @@ public static short compare(short num1, short num2) { len = KMInteger.cast(num2).length(); KMInteger.cast(num2).getValue(repository.getHeap(), (short) (num2Buf + (short) (8 - len)), len); return KMInteger.unsignedByteArrayCompare( - repository.getHeap(), num1Buf, - repository.getHeap(), num2Buf, - (short) 8); + repository.getHeap(), num1Buf, + repository.getHeap(), num2Buf, + (short) 8); } public static byte unsignedByteArrayCompare(byte[] a1, short offset1, byte[] a2, short offset2, - short length) { + short length) { byte count = (byte) 0; short val1 = (short) 0; short val2 = (short) 0; @@ -200,4 +201,8 @@ public static byte unsignedByteArrayCompare(byte[] a1, short offset1, byte[] a2, } return 0; } + + protected short getBaseOffset() { + return instanceTable[KM_INTEGER_OFFSET]; + } } diff --git a/Applet/src/com/android/javacard/keymaster/KMIntegerArrayTag.java b/Applet/src/com/android/javacard/keymaster/KMIntegerArrayTag.java index e292c5e6..6f7cc2ff 100644 --- a/Applet/src/com/android/javacard/keymaster/KMIntegerArrayTag.java +++ b/Applet/src/com/android/javacard/keymaster/KMIntegerArrayTag.java @@ -38,7 +38,7 @@ private static KMIntegerArrayTag proto(short ptr) { if (prototype == null) { prototype = new KMIntegerArrayTag(); } - instanceTable[KM_INTEGER_ARRAY_TAG_OFFSET] = ptr; + KMType.instanceTable[KM_INTEGER_ARRAY_TAG_OFFSET] = ptr; return prototype; } @@ -94,15 +94,15 @@ public static KMIntegerArrayTag cast(short ptr) { } public short getTagType() { - return Util.getShort(heap, (short) (instanceTable[KM_INTEGER_ARRAY_TAG_OFFSET] + TLV_HEADER_SIZE)); + return Util.getShort(heap, (short) (KMType.instanceTable[KM_INTEGER_ARRAY_TAG_OFFSET] + TLV_HEADER_SIZE)); } public short getKey() { - return Util.getShort(heap, (short) (instanceTable[KM_INTEGER_ARRAY_TAG_OFFSET] + TLV_HEADER_SIZE + 2)); + return Util.getShort(heap, (short) (KMType.instanceTable[KM_INTEGER_ARRAY_TAG_OFFSET] + TLV_HEADER_SIZE + 2)); } public short getValues() { - return Util.getShort(heap, (short) (instanceTable[KM_INTEGER_ARRAY_TAG_OFFSET] + TLV_HEADER_SIZE + 4)); + return Util.getShort(heap, (short) (KMType.instanceTable[KM_INTEGER_ARRAY_TAG_OFFSET] + TLV_HEADER_SIZE + 4)); } public short length() { @@ -136,7 +136,7 @@ private static boolean validateTagType(short tagType) { public static boolean contains(short tagId, short tagValue, short params) { short tag = - KMKeyParameters.findTag(KMType.UINT_ARRAY_TAG, tagId, params); + KMKeyParameters.findTag(KMType.UINT_ARRAY_TAG, tagId, params); if (tag != KMType.INVALID_VALUE) { short index = 0; tag = KMIntegerArrayTag.cast(tag).getValues(); diff --git a/Applet/src/com/android/javacard/keymaster/KMIntegerTag.java b/Applet/src/com/android/javacard/keymaster/KMIntegerTag.java index 6ddec4bd..2e22f183 100644 --- a/Applet/src/com/android/javacard/keymaster/KMIntegerTag.java +++ b/Applet/src/com/android/javacard/keymaster/KMIntegerTag.java @@ -28,27 +28,31 @@ public class KMIntegerTag extends KMTag { private static KMIntegerTag prototype; + // Allowed tag keys. private static final short[] tags = { - // UINT - KEYSIZE, - MIN_MAC_LENGTH, - MIN_SEC_BETWEEN_OPS, - MAX_USES_PER_BOOT, - USERID, - AUTH_TIMEOUT, - OS_VERSION, - OS_PATCH_LEVEL, - VENDOR_PATCH_LEVEL, - BOOT_PATCH_LEVEL, - MAC_LENGTH, - // ULONG - RSA_PUBLIC_EXPONENT, - // DATE - ACTIVE_DATETIME, - ORIGINATION_EXPIRE_DATETIME, - USAGE_EXPIRE_DATETIME, - CREATION_DATETIME + // UINT + KEYSIZE, + MIN_MAC_LENGTH, + MIN_SEC_BETWEEN_OPS, + MAX_USES_PER_BOOT, + USERID, + AUTH_TIMEOUT, + OS_VERSION, + OS_PATCH_LEVEL, + VENDOR_PATCH_LEVEL, + BOOT_PATCH_LEVEL, + MAC_LENGTH, + // ULONG + RSA_PUBLIC_EXPONENT, + // DATE + ACTIVE_DATETIME, + ORIGINATION_EXPIRE_DATETIME, + USAGE_EXPIRE_DATETIME, + CREATION_DATETIME, + CERTIFICATE_NOT_BEFORE, + CERTIFICATE_NOT_AFTER, + USAGE_COUNT_LIMIT, }; private KMIntegerTag() { @@ -58,7 +62,7 @@ private static KMIntegerTag proto(short ptr) { if (prototype == null) { prototype = new KMIntegerTag(); } - instanceTable[KM_INTEGER_TAG_OFFSET] = ptr; + KMType.instanceTable[KM_INTEGER_TAG_OFFSET] = ptr; return prototype; } @@ -114,15 +118,15 @@ public static KMIntegerTag cast(short ptr) { } public short getTagType() { - return Util.getShort(heap, (short) (instanceTable[KM_INTEGER_TAG_OFFSET] + TLV_HEADER_SIZE)); + return Util.getShort(heap, (short) (KMType.instanceTable[KM_INTEGER_TAG_OFFSET] + TLV_HEADER_SIZE)); } public short getKey() { - return Util.getShort(heap, (short) (instanceTable[KM_INTEGER_TAG_OFFSET] + TLV_HEADER_SIZE + 2)); + return Util.getShort(heap, (short) (KMType.instanceTable[KM_INTEGER_TAG_OFFSET] + TLV_HEADER_SIZE + 2)); } public short getValue() { - return Util.getShort(heap, (short) (instanceTable[KM_INTEGER_TAG_OFFSET] + TLV_HEADER_SIZE + 4)); + return Util.getShort(heap, (short) (KMType.instanceTable[KM_INTEGER_TAG_OFFSET] + TLV_HEADER_SIZE + 4)); } public short length() { @@ -159,7 +163,7 @@ public static short getShortValue(short tagType, short tagKey, short keyParamete } public static short getValue( - byte[] buf, short offset, short tagType, short tagKey, short keyParameters) { + byte[] buf, short offset, short tagType, short tagKey, short keyParameters) { short ptr; if ((tagType == UINT_TAG) || (tagType == ULONG_TAG) || (tagType == DATE_TAG)) { ptr = KMKeyParameters.findTag(tagType, tagKey, keyParameters); @@ -172,7 +176,7 @@ public static short getValue( } public boolean isValidKeySize(byte alg) { - short val = KMIntegerTag.cast(instanceTable[KM_INTEGER_TAG_OFFSET]).getValue(); + short val = KMIntegerTag.cast(KMType.instanceTable[KM_INTEGER_TAG_OFFSET]).getValue(); if (KMInteger.cast(val).getSignificantShort() != 0) { return false; } diff --git a/Applet/src/com/android/javacard/keymaster/KMKeyCharacteristics.java b/Applet/src/com/android/javacard/keymaster/KMKeyCharacteristics.java index 7913f6ff..be073140 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeyCharacteristics.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeyCharacteristics.java @@ -22,26 +22,30 @@ /** * KMKeyCharacteristics represents KeyCharacteristics structure from android keymaster hal - * specifications. It corresponds to CBOR array type. struct{byte KEY_CHAR_TYPE; short length=2; - * short arrayPtr} where arrayPtr is a pointer to ordered array with following elements: - * {KMKeyParameters sofEnf; KMKeyParameters hwEnf} + * specifications. It corresponds to CBOR array type. struct{byte KEY_CHAR_TYPE; short length=3; + * short arrayPtr} where arrayPtr is a pointer to ordered array with 1 or 3 following elements: + * {KMKeyParameters sb; KMKeyParameters tee; KMKeyParameters keystore} */ public class KMKeyCharacteristics extends KMType { - public static final byte SOFTWARE_ENFORCED = 0x00; - public static final byte HARDWARE_ENFORCED = 0x01; + public static final byte STRONGBOX_ENFORCED = 0x00; + public static final byte TEE_ENFORCED = 0x01; + public static final byte KEYSTORE_ENFORCED = 0x02; private static KMKeyCharacteristics prototype; private KMKeyCharacteristics() { } public static short exp() { - short softEnf = KMKeyParameters.exp(); - short hwEnf = KMKeyParameters.exp(); - short arrPtr = KMArray.instance((short) 2); + short sb = KMKeyParameters.exp(); + short tee = KMKeyParameters.exp(); + short keystore = KMKeyParameters.exp(); + short arrPtr = KMArray.instance((short) 3); + KMArray arr = KMArray.cast(arrPtr); - arr.add(SOFTWARE_ENFORCED, softEnf); - arr.add(HARDWARE_ENFORCED, hwEnf); + arr.add(STRONGBOX_ENFORCED, sb); + arr.add(TEE_ENFORCED, tee); + arr.add(KEYSTORE_ENFORCED, keystore); return instance(arrPtr); } @@ -49,18 +53,18 @@ private static KMKeyCharacteristics proto(short ptr) { if (prototype == null) { prototype = new KMKeyCharacteristics(); } - instanceTable[KM_KEY_CHARACTERISTICS_OFFSET] = ptr; + KMType.instanceTable[KM_KEY_CHARACTERISTICS_OFFSET] = ptr; return prototype; } public static short instance() { - short arrPtr = KMArray.instance((short) 2); + short arrPtr = KMArray.instance((short) 3); return instance(arrPtr); } public static short instance(short vals) { - short ptr = KMType.instance(KEY_CHAR_TYPE, (short) 2); - if (KMArray.cast(vals).length() != 2) { + short ptr = KMType.instance(KEY_CHAR_TYPE, (short) 3); + if (KMArray.cast(vals).length() != 3) { ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); } Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), vals); @@ -79,7 +83,7 @@ public static KMKeyCharacteristics cast(short ptr) { } public short getVals() { - return Util.getShort(heap, (short) (instanceTable[KM_KEY_CHARACTERISTICS_OFFSET] + TLV_HEADER_SIZE)); + return Util.getShort(heap, (short) (KMType.instanceTable[KM_KEY_CHARACTERISTICS_OFFSET] + TLV_HEADER_SIZE)); } public short length() { @@ -87,25 +91,36 @@ public short length() { return KMArray.cast(arrPtr).length(); } - public short getSoftwareEnforced() { + public short getKeystoreEnforced() { + short arrPtr = getVals(); + return KMArray.cast(arrPtr).get(KEYSTORE_ENFORCED); + } + + public short getTeeEnforced() { short arrPtr = getVals(); - return KMArray.cast(arrPtr).get(SOFTWARE_ENFORCED); + return KMArray.cast(arrPtr).get(TEE_ENFORCED); } - public short getHardwareEnforced() { + public short getStrongboxEnforced() { + short arrPtr = getVals(); + return KMArray.cast(arrPtr).get(STRONGBOX_ENFORCED); + } + + public void setKeystoreEnforced(short ptr) { + KMKeyParameters.cast(ptr); short arrPtr = getVals(); - return KMArray.cast(arrPtr).get(HARDWARE_ENFORCED); + KMArray.cast(arrPtr).add(KEYSTORE_ENFORCED, ptr); } - public void setSoftwareEnforced(short ptr) { + public void setTeeEnforced(short ptr) { KMKeyParameters.cast(ptr); short arrPtr = getVals(); - KMArray.cast(arrPtr).add(SOFTWARE_ENFORCED, ptr); + KMArray.cast(arrPtr).add(TEE_ENFORCED, ptr); } - public void setHardwareEnforced(short ptr) { + public void setStrongboxEnforced(short ptr) { KMKeyParameters.cast(ptr); short arrPtr = getVals(); - KMArray.cast(arrPtr).add(HARDWARE_ENFORCED, ptr); + KMArray.cast(arrPtr).add(STRONGBOX_ENFORCED, ptr); } } diff --git a/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java b/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java index 0ef85ae4..21dd7f5c 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java @@ -16,6 +16,7 @@ package com.android.javacard.keymaster; +import com.android.javacard.seprovider.KMException; import javacard.framework.ISO7816; import javacard.framework.ISOException; import javacard.framework.Util; @@ -36,22 +37,41 @@ private static KMKeyParameters proto(short ptr) { if (prototype == null) { prototype = new KMKeyParameters(); } - instanceTable[KM_KEY_PARAMETERS_OFFSET] = ptr; + KMType.instanceTable[KM_KEY_PARAMETERS_OFFSET] = ptr; return prototype; } public static short exp() { - short arrPtr = KMArray.instance((short) 9); + short arrPtr = KMArray.instance((short) 11); KMArray arr = KMArray.cast(arrPtr); - arr.add((short) 0, KMIntegerTag.exp(UINT_TAG)); - arr.add((short) 1, KMIntegerArrayTag.exp(UINT_ARRAY_TAG)); - arr.add((short) 2, KMIntegerTag.exp(ULONG_TAG)); - arr.add((short) 3, KMIntegerTag.exp(DATE_TAG)); - arr.add((short) 4, KMIntegerArrayTag.exp(ULONG_ARRAY_TAG)); - arr.add((short) 5, KMEnumTag.exp()); - arr.add((short) 6, KMEnumArrayTag.exp()); - arr.add((short) 7, KMByteTag.exp()); - arr.add((short) 8, KMBoolTag.exp()); + arr.add((short) 0, KMEnum.instance(KMType.RULE, KMType.FAIL_ON_INVALID_TAGS)); + arr.add((short) 1, KMIntegerTag.exp(UINT_TAG)); + arr.add((short) 2, KMIntegerArrayTag.exp(UINT_ARRAY_TAG)); + arr.add((short) 3, KMIntegerTag.exp(ULONG_TAG)); + arr.add((short) 4, KMIntegerTag.exp(DATE_TAG)); + arr.add((short) 5, KMIntegerArrayTag.exp(ULONG_ARRAY_TAG)); + arr.add((short) 6, KMEnumTag.exp()); + arr.add((short) 7, KMEnumArrayTag.exp()); + arr.add((short) 8, KMByteTag.exp()); + arr.add((short) 9, KMBoolTag.exp()); + arr.add((short) 10, KMBignumTag.exp()); + return instance(arrPtr); + } + + public static short expAny() { + short arrPtr = KMArray.instance((short) 11); + KMArray arr = KMArray.cast(arrPtr); + arr.add((short) 0, KMEnum.instance(KMType.RULE, KMType.IGNORE_INVALID_TAGS)); + arr.add((short) 1, KMIntegerTag.exp(UINT_TAG)); + arr.add((short) 2, KMIntegerArrayTag.exp(UINT_ARRAY_TAG)); + arr.add((short) 3, KMIntegerTag.exp(ULONG_TAG)); + arr.add((short) 4, KMIntegerTag.exp(DATE_TAG)); + arr.add((short) 5, KMIntegerArrayTag.exp(ULONG_ARRAY_TAG)); + arr.add((short) 6, KMEnumTag.exp()); + arr.add((short) 7, KMEnumArrayTag.exp()); + arr.add((short) 8, KMByteTag.exp()); + arr.add((short) 9, KMBoolTag.exp()); + arr.add((short) 10, KMBignumTag.exp()); return instance(arrPtr); } @@ -73,7 +93,7 @@ public static KMKeyParameters cast(short ptr) { } public short getVals() { - return Util.getShort(heap, (short) (instanceTable[KM_KEY_PARAMETERS_OFFSET] + TLV_HEADER_SIZE)); + return Util.getShort(heap, (short) (KMType.instanceTable[KM_KEY_PARAMETERS_OFFSET] + TLV_HEADER_SIZE)); } public short length() { @@ -141,7 +161,7 @@ public static boolean hasUnsupportedTags(short keyParamsPtr) { } // KDF, ECIES_SINGLE_HASH_MODE missing from types.hal - public static short makeHwEnforced(short keyParamsPtr, byte origin, + public static short makeSbEnforced(short keyParamsPtr, byte origin, short osVersionObjPtr, short osPatchObjPtr, short vendorPatchObjPtr, short bootPatchObjPtr, byte[] scratchPad) { final short[] hwEnforcedTagArr = { @@ -155,17 +175,16 @@ public static short makeHwEnforced(short keyParamsPtr, byte origin, KMType.ENUM_ARRAY_TAG, KMType.DIGEST, KMType.ENUM_ARRAY_TAG, KMType.PADDING, KMType.ENUM_ARRAY_TAG, KMType.BLOCK_MODE, - KMType.ULONG_ARRAY_TAG, KMType.USER_SECURE_ID, + KMType.ENUM_ARRAY_TAG, KMType.RSA_OAEP_MGF_DIGEST, KMType.BOOL_TAG, KMType.NO_AUTH_REQUIRED, - KMType.UINT_TAG, KMType.AUTH_TIMEOUT, KMType.BOOL_TAG, KMType.CALLER_NONCE, KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, KMType.ENUM_TAG, KMType.ECCURVE, KMType.BOOL_TAG, KMType.INCLUDE_UNIQUE_ID, KMType.BOOL_TAG, KMType.ROLLBACK_RESISTANCE, - KMType.ENUM_TAG, KMType.USER_AUTH_TYPE, KMType.BOOL_TAG, KMType.UNLOCKED_DEVICE_REQUIRED, - KMType.BOOL_TAG, KMType.RESET_SINCE_ID_ROTATION + KMType.BOOL_TAG, KMType.RESET_SINCE_ID_ROTATION, + KMType.BOOL_TAG, KMType.EARLY_BOOT_ONLY, }; byte index = 0; short tagInd; @@ -214,14 +233,98 @@ public static short makeHwEnforced(short keyParamsPtr, byte origin, return createKeyParameters(scratchPad, (short) (arrInd / 2)); } + public static short makeSbEnforced(short keyParamsPtr, byte[] scratchPad) { + final short[] hwEnforcedTagArr = { + // HW Enforced + KMType.ENUM_TAG, KMType.ORIGIN, + KMType.UINT_TAG, KMType.OS_VERSION, + KMType.UINT_TAG, KMType.OS_PATCH_LEVEL, + KMType.UINT_TAG, KMType.VENDOR_PATCH_LEVEL, + KMType.UINT_TAG, KMType.BOOT_PATCH_LEVEL, + KMType.ENUM_ARRAY_TAG, KMType.PURPOSE, + KMType.ENUM_TAG, KMType.ALGORITHM, + KMType.UINT_TAG, KMType.KEYSIZE, + KMType.ULONG_TAG, KMType.RSA_PUBLIC_EXPONENT, + KMType.ENUM_TAG, KMType.BLOB_USAGE_REQ, + KMType.ENUM_ARRAY_TAG, KMType.DIGEST, + KMType.ENUM_ARRAY_TAG, KMType.PADDING, + KMType.ENUM_ARRAY_TAG, KMType.BLOCK_MODE, + KMType.ENUM_ARRAY_TAG, KMType.RSA_OAEP_MGF_DIGEST, + KMType.BOOL_TAG, KMType.NO_AUTH_REQUIRED, + KMType.BOOL_TAG, KMType.CALLER_NONCE, + KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, + KMType.ENUM_TAG, KMType.ECCURVE, + KMType.BOOL_TAG, KMType.INCLUDE_UNIQUE_ID, + KMType.BOOL_TAG, KMType.ROLLBACK_RESISTANCE, + KMType.BOOL_TAG, KMType.UNLOCKED_DEVICE_REQUIRED, + KMType.BOOL_TAG, KMType.RESET_SINCE_ID_ROTATION, + KMType.BOOL_TAG, KMType.EARLY_BOOT_ONLY, + }; + byte index = 0; + short tagInd; + short arrInd = 0; + short tagPtr; + short tagKey; + short tagType; + short arrPtr = KMKeyParameters.cast(keyParamsPtr).getVals(); + short len = KMArray.cast(arrPtr).length(); + while (index < len) { + tagInd = 0; + tagPtr = KMArray.cast(arrPtr).get(index); + tagKey = KMTag.getKey(tagPtr); + tagType = KMTag.getTagType(tagPtr); + if (!isValidTag(tagType, tagKey)) { + KMException.throwIt(KMError.INVALID_KEY_BLOB); + } + while (tagInd < (short) hwEnforcedTagArr.length) { + if ((hwEnforcedTagArr[tagInd] == tagType) + && (hwEnforcedTagArr[(short) (tagInd + 1)] == tagKey)) { + Util.setShort(scratchPad, arrInd, tagPtr); + arrInd += 2; + break; + } + tagInd += 2; + } + index++; + } + return createKeyParameters(scratchPad, (short) (arrInd / 2)); + } + + public static short makeHwEnforced(short sb, short tee){ + short len = KMKeyParameters.cast(sb).length(); + len += KMKeyParameters.cast(tee).length(); + short hwEnf = KMArray.instance(len); + sb = KMKeyParameters.cast(sb).getVals(); + tee = KMKeyParameters.cast(tee).getVals(); + len = KMArray.cast(sb).length(); + short src = 0; + short dest =0; + short val = 0; + while(src < len){ + val = KMArray.cast(sb).get(src); + KMArray.cast(hwEnf).add(dest, val); + src++; + dest++; + } + src = 0; + len = KMArray.cast(tee).length(); + while(src < len){ + val = KMArray.cast(tee).get(src); + KMArray.cast(hwEnf).add(dest, val); + src++; + dest++; + } + return KMKeyParameters.instance(hwEnf); + } // ALL_USERS, EXPORTABLE missing from types.hal - public static short makeSwEnforced(short keyParamsPtr, byte[] scratchPad) { + public static short makeKeystoreEnforced(short keyParamsPtr, byte[] scratchPad) { final short[] swEnforcedTagsArr = { KMType.DATE_TAG, KMType.ACTIVE_DATETIME, KMType.DATE_TAG, KMType.ORIGINATION_EXPIRE_DATETIME, KMType.DATE_TAG, KMType.USAGE_EXPIRE_DATETIME, KMType.UINT_TAG, KMType.USERID, - KMType.DATE_TAG, KMType.CREATION_DATETIME + KMType.DATE_TAG, KMType.CREATION_DATETIME, + KMType.UINT_TAG, KMType.USAGE_COUNT_LIMIT }; byte index = 0; short tagInd; @@ -253,6 +356,42 @@ public static short makeSwEnforced(short keyParamsPtr, byte[] scratchPad) { return createKeyParameters(scratchPad, (short) (arrInd / 2)); } + public static short makeTeeEnforced(short keyParamsPtr, byte[] scratchPad) { + final short[] teeEnforcedTagsArr = { + KMType.ULONG_ARRAY_TAG, KMType.USER_SECURE_ID, + KMType.UINT_TAG, KMType.AUTH_TIMEOUT, + KMType.ENUM_TAG, KMType.USER_AUTH_TYPE, + }; + byte index = 0; + short tagInd; + short arrInd = 0; + short tagPtr; + short tagKey; + short tagType; + short arrPtr = KMKeyParameters.cast(keyParamsPtr).getVals(); + short len = KMArray.cast(arrPtr).length(); + while (index < len) { + tagInd = 0; + tagPtr = KMArray.cast(arrPtr).get(index); + tagKey = KMTag.getKey(tagPtr); + tagType = KMTag.getTagType(tagPtr); + if (!isValidTag(tagType, tagKey)) { + KMException.throwIt(KMError.INVALID_KEY_BLOB); + } + while (tagInd < (short) teeEnforcedTagsArr.length) { + if ((teeEnforcedTagsArr[tagInd] == tagType) + && (teeEnforcedTagsArr[(short) (tagInd + 1)] == tagKey)) { + Util.setShort(scratchPad, arrInd, tagPtr); + arrInd += 2; + break; + } + tagInd += 2; + } + index++; + } + return createKeyParameters(scratchPad, (short) (arrInd / 2)); + } + public static short makeHidden(short keyParamsPtr, short rootOfTrustBlob, byte[] scratchPad) { short appId = KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.APPLICATION_ID, keyParamsPtr); if (appId != KMTag.INVALID_VALUE) { diff --git a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java index 0949177f..b8483275 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java @@ -16,6 +16,10 @@ package com.android.javacard.keymaster; +import com.android.javacard.seprovider.KMAttestationCert; +import com.android.javacard.seprovider.KMDeviceUniqueKey; +import com.android.javacard.seprovider.KMException; +import com.android.javacard.seprovider.KMSEProvider; import javacard.framework.APDU; import javacard.framework.Applet; import javacard.framework.AppletEvent; @@ -34,14 +38,43 @@ public class KMKeymasterApplet extends Applet implements AppletEvent, ExtendedLength { // Constants. + public static final byte[] F4 = {0x01, 0x00, 0x01}; public static final byte AES_BLOCK_SIZE = 16; public static final byte DES_BLOCK_SIZE = 8; - public static final short MAX_LENGTH = (short) 0x2000; - private static final byte CLA_ISO7816_NO_SM_NO_CHAN = (byte) 0x80; - private static final short KM_HAL_VERSION = (short) 0x4000; + public static final short MAX_LENGTH = 15000; + public static final short MASTER_KEY_SIZE = 128; + public static final short WRAPPING_KEY_SIZE = 32; + public static final short MAX_OPERATIONS_COUNT = 4; + public static final short VERIFIED_BOOT_KEY_SIZE = 32; + public static final short VERIFIED_BOOT_HASH_SIZE = 32; + public static final short BOOT_PATCH_LVL_SIZE = 4; + + protected static final byte CLA_ISO7816_NO_SM_NO_CHAN = (byte) 0x80; + protected static final short KM_HAL_VERSION = (short) 0x5000; private static final short MAX_AUTH_DATA_SIZE = (short) 512; private static final short DERIVE_KEY_INPUT_SIZE = (short) 256; - private static final short POWER_RESET_MASK_FLAG = (short) 0x4000; + + // Subject is a fixed field with only CN= Android Keystore Key - same for all the keys + private static final byte[] defaultSubject = { + 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[] dec319999Ms ={(byte)0, (byte)0, (byte)0xE6, (byte)0x77, + (byte)0xD2, (byte)0x1F, (byte)0xD8, (byte)0x18}; + + private static final byte[] dec319999 = { + 0x39, 0x39, 0x39, 0x39, 0x31, 0x32, 0x33, 0x31, 0x32, 0x33, 0x35, + 0x39, 0x35, 0x39, 0x5a, + }; + + private static final byte[] jan01970 = { + 0x37, 0x30, 0x30, 0x31, 0x30, 0x31, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x5a, + }; // "Keymaster HMAC Verification" - used for HMAC key verification. public static final byte[] sharingCheck = { @@ -49,86 +82,69 @@ public class KMKeymasterApplet extends Applet implements AppletEvent, ExtendedLe 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E }; + // "KeymasterSharedMac" public static final byte[] ckdfLable = { 0x4B, 0x65, 0x79, 0x6D, 0x61, 0x73, 0x74, 0x65, 0x72, 0x53, 0x68, 0x61, 0x72, 0x65, 0x64, 0x4D, 0x61, 0x63 }; + // "Auth Verification" public static final byte[] authVerification = { 0x41, 0x75, 0x74, 0x68, 0x20, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E }; - // "confirmation token" - public static final byte[] confirmationToken = { - 0x63, 0x6F, 0x6E, 0x66, 0x69, 0x72, 0x6D, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x74, 0x6F, - 0x6B, - 0x65, 0x6E - }; - // Possible states of the applet. - 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_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_PRESHARED_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 - private static final byte INS_SET_VERSION_PATCHLEVEL_CMD = INS_BEGIN_KM_CMD + 9; //0x09 + + public static final short MAX_COSE_BUF_SIZE = (short) 1024; // 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 KEYMINT_CMD_APDU_START = 0x20; + + private static final byte INS_GENERATE_KEY_CMD = KEYMINT_CMD_APDU_START + 1; //0x21 + private static final byte INS_IMPORT_KEY_CMD = KEYMINT_CMD_APDU_START + 2; //0x22 + private static final byte INS_IMPORT_WRAPPED_KEY_CMD = KEYMINT_CMD_APDU_START + 3; //0x23 + private static final byte INS_EXPORT_KEY_CMD = KEYMINT_CMD_APDU_START + 4; //0x24 + private static final byte INS_ATTEST_KEY_CMD = KEYMINT_CMD_APDU_START + 5; //0x25 + private static final byte INS_UPGRADE_KEY_CMD = KEYMINT_CMD_APDU_START + 6; //0x26 + private static final byte INS_DELETE_KEY_CMD = KEYMINT_CMD_APDU_START + 7; //0x27 + private static final byte INS_DELETE_ALL_KEYS_CMD = KEYMINT_CMD_APDU_START + 8; //0x28 + private static final byte INS_ADD_RNG_ENTROPY_CMD = KEYMINT_CMD_APDU_START + 9; //0x29 + private static final byte INS_COMPUTE_SHARED_HMAC_CMD = KEYMINT_CMD_APDU_START + 10; //0x2A + private static final byte INS_DESTROY_ATT_IDS_CMD = KEYMINT_CMD_APDU_START + 11; //0x2B + private static final byte INS_VERIFY_AUTHORIZATION_CMD = KEYMINT_CMD_APDU_START + 12; //0x2C + private static final byte INS_GET_HMAC_SHARING_PARAM_CMD = KEYMINT_CMD_APDU_START + 13; //0x2D + private static final byte INS_GET_KEY_CHARACTERISTICS_CMD = KEYMINT_CMD_APDU_START + 14; //0x2E + private static final byte INS_GET_HW_INFO_CMD = KEYMINT_CMD_APDU_START + 15; //0x2F + private static final byte INS_BEGIN_OPERATION_CMD = KEYMINT_CMD_APDU_START + 16; //0x30 + private static final byte INS_UPDATE_OPERATION_CMD = KEYMINT_CMD_APDU_START + 17; //0x31 + private static final byte INS_FINISH_OPERATION_CMD = KEYMINT_CMD_APDU_START + 18; //0x32 + private static final byte INS_ABORT_OPERATION_CMD = KEYMINT_CMD_APDU_START + 19; //0x33 + private static final byte INS_DEVICE_LOCKED_CMD = KEYMINT_CMD_APDU_START + 20;//0x34 + private static final byte INS_EARLY_BOOT_ENDED_CMD = KEYMINT_CMD_APDU_START + 21; //0x35 + private static final byte INS_GET_CERT_CHAIN_CMD = KEYMINT_CMD_APDU_START + 22; //0x36 + private static final byte INS_UPDATE_AAD_OPERATION_CMD = KEYMINT_CMD_APDU_START + 23; //0x37 + private static final byte INS_BEGIN_IMPORT_WRAPPED_KEY_CMD = KEYMINT_CMD_APDU_START + 24; //0x38 + private static final byte INS_FINISH_IMPORT_WRAPPED_KEY_CMD = KEYMINT_CMD_APDU_START + 25; //0x39 + private static final byte INS_INIT_STRONGBOX_CMD = KEYMINT_CMD_APDU_START + 26; //0x3A + // RKP + public static final byte INS_GET_RKP_HARDWARE_INFO = KEYMINT_CMD_APDU_START + 27; //0x3B + public static final byte INS_GENERATE_RKP_KEY_CMD = KEYMINT_CMD_APDU_START + 28; //0x3C + public static final byte INS_BEGIN_SEND_DATA_CMD = KEYMINT_CMD_APDU_START + 29; //0x3D + public static final byte INS_UPDATE_KEY_CMD = KEYMINT_CMD_APDU_START + 30; //0x3E + public static final byte INS_UPDATE_EEK_CHAIN_CMD = KEYMINT_CMD_APDU_START + 31; //0x3F + public static final byte INS_UPDATE_CHALLENGE_CMD = KEYMINT_CMD_APDU_START + 32; //0x40 + public static final byte INS_FINISH_SEND_DATA_CMD = KEYMINT_CMD_APDU_START + 33; //0x41 + public static final byte INS_GET_RESPONSE_CMD = KEYMINT_CMD_APDU_START + 34; //0x42 + private static final byte KEYMINT_CMD_APDU_END = KEYMINT_CMD_APDU_START + 35; //0x43 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_PRESHARED_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; - public static final byte TMP_VARIABLE_ARRAY_SIZE = 20; - public static final byte UPDATE_PARAM_ARRAY_SIZE = 40; + public static final byte DATA_ARRAY_SIZE = 40; + public static final byte TMP_VARIABLE_ARRAY_SIZE = 5; + public static final byte KEY_PARAMETERS = 0; public static final byte KEY_CHARACTERISTICS = 1; public static final byte HIDDEN_PARAMETERS = 2; @@ -148,7 +164,7 @@ public class KMKeymasterApplet extends Applet implements AppletEvent, ExtendedLe public static final byte PUB_KEY = 16; public static final byte IMPORTED_KEY_BLOB = 17; public static final byte ORIGIN = 18; - public static final byte ENC_TRANSPORT_KEY = 19; + public static final byte NOT_USED = 19; public static final byte MASKING_KEY = 20; public static final byte HMAC_SHARING_PARAMS = 21; public static final byte OP_HANDLE = 22; @@ -158,65 +174,124 @@ public class KMKeymasterApplet extends Applet implements AppletEvent, ExtendedLe public static final byte HW_TOKEN = 26; public static final byte VERIFICATION_TOKEN = 27; public static final byte SIGNATURE = 28; + public static final byte ATTEST_KEY_BLOB = 29; + public static final byte ATTEST_KEY_PARAMS = 30; + public static final byte ATTEST_KEY_ISSUER = 31; + public static final byte CERTIFICATE = 32; + public static final byte PLAIN_SECRET = 33; + public static final byte TEE_PARAMETERS = 34; + public static final byte SB_PARAMETERS = 35; + // Constant // AddRngEntropy 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; public static final byte KEY_BLOB_AUTH_TAG = 2; - public static final byte KEY_BLOB_KEYCHAR = 3; + public static final byte KEY_BLOB_PARAMS = 3; public static final byte KEY_BLOB_PUB_KEY = 4; // AES GCM constants - private static final byte AES_GCM_AUTH_TAG_LENGTH = 16; - private static final byte AES_GCM_NONCE_LENGTH = 12; + public static final byte AES_GCM_AUTH_TAG_LENGTH = 16; + public static final byte AES_GCM_NONCE_LENGTH = 12; // ComputeHMAC constants private static final short HMAC_SHARED_PARAM_MAX_SIZE = 64; - // Maximum certificate size. - private static final short MAX_CERT_SIZE = 2048; - // Buffer constants. - private static final short BUF_START_OFFSET = 0; - private static final short BUF_LEN_OFFSET = 2; - - // Keymaster Applet attributes - protected static byte keymasterState = ILLEGAL_STATE; + protected static final short MAX_CERT_SIZE = 2048; + + protected static RemotelyProvisionedComponentDevice rkp; protected static KMEncoder encoder; protected static KMDecoder decoder; protected static KMRepository repository; protected static KMSEProvider seProvider; - protected static Object[] bufferRef; - protected static short[] bufferProp; + protected static KMOperationState[] opTable; + protected static short[] tmpVariables; protected static short[] data; - protected static byte provisionStatus = NOT_PROVISIONED; + protected static byte[] wrappingKey; /** * Registers this applet. */ protected KMKeymasterApplet(KMSEProvider seImpl) { seProvider = seImpl; - boolean isUpgrading = seImpl.isUpgrading(); - repository = new KMRepository(isUpgrading); - initializeTransientArrays(); + boolean isUpgrading = seProvider.isUpgrading(); if (!isUpgrading) { - keymasterState = KMKeymasterApplet.INIT_STATE; - seProvider.createMasterKey((short) (KMRepository.MASTER_KEY_SIZE * 8)); + seProvider.createMasterKey(MASTER_KEY_SIZE); } - KMType.initialize(); + repository = new KMRepository(isUpgrading); encoder = new KMEncoder(); decoder = new KMDecoder(); + data = JCSystem.makeTransientShortArray(DATA_ARRAY_SIZE, JCSystem.CLEAR_ON_DESELECT); + tmpVariables = + JCSystem.makeTransientShortArray(TMP_VARIABLE_ARRAY_SIZE, JCSystem.CLEAR_ON_DESELECT); + wrappingKey = JCSystem.makeTransientByteArray((short)(WRAPPING_KEY_SIZE+1), JCSystem.CLEAR_ON_RESET); + resetWrappingKey(); + opTable = new KMOperationState[MAX_OPERATIONS_COUNT]; + short index = 0; + while(index < MAX_OPERATIONS_COUNT){ + opTable[index] = new KMOperationState(); + index++; + } + KMType.initialize(); + + // initialize default values + initHmacNonceAndSeed(); + initSystemBootParams((short)0,(short)0,(short)0,(short)0); + rkp = new RemotelyProvisionedComponentDevice(encoder, decoder, repository, seProvider); } - private void initializeTransientArrays() { - data = JCSystem.makeTransientShortArray((short) DATA_ARRAY_SIZE, JCSystem.CLEAR_ON_RESET); - bufferRef = JCSystem.makeTransientObjectArray((short) 1, JCSystem.CLEAR_ON_RESET); - bufferProp = JCSystem.makeTransientShortArray((short) 4, JCSystem.CLEAR_ON_RESET); - tmpVariables = - JCSystem.makeTransientShortArray((short) TMP_VARIABLE_ARRAY_SIZE, JCSystem.CLEAR_ON_RESET); - bufferProp[BUF_START_OFFSET] = 0; - bufferProp[BUF_LEN_OFFSET] = 0; + protected void initHmacNonceAndSeed(){ + short nonce = repository.alloc((short)32); + seProvider.newRandomNumber(repository.getHeap(), nonce, KMRepository.HMAC_SEED_NONCE_SIZE); + repository.initHmacNonce(repository.getHeap(), nonce, KMRepository.HMAC_SEED_NONCE_SIZE); + } + + private void releaseAllOperations(){ + short index = 0; + while(index < MAX_OPERATIONS_COUNT) { + opTable[index].reset(); + index++; + } } + private KMOperationState reserveOperation(short algorithm, short opHandle){ + short index = 0; + while(index < MAX_OPERATIONS_COUNT) { + if (opTable[index].getAlgorithm() == KMType.INVALID_VALUE) { + opTable[index].setAlgorithm(algorithm); + opTable[index].setHandle(KMInteger.cast(opHandle).getBuffer(), + KMInteger.cast(opHandle).getStartOff(), + KMInteger.cast(opHandle).length()); + return opTable[index]; + } + index++; + } + return null; + } + + private KMOperationState findOperation(short handle){ + return findOperation(KMInteger.cast(handle).getBuffer(), + KMInteger.cast(handle).getStartOff(), + KMInteger.cast(handle).length()); + } + + private KMOperationState findOperation(byte[] opHandle, short start, short len){ + short index = 0; + while(index < MAX_OPERATIONS_COUNT) { + if(opTable[index].compare(opHandle, start, len) == 0){ + if(opTable[index].getAlgorithm() != KMType.INVALID_VALUE) { + return opTable[index]; + } + } + index++; + } + return null; + } + + private void releaseOperation(KMOperationState op){ + op.reset(); + } /** * Selects this applet. * @@ -225,9 +300,6 @@ private void initializeTransientArrays() { @Override public boolean select() { repository.onSelect(); - if (keymasterState == KMKeymasterApplet.INIT_STATE) { - keymasterState = KMKeymasterApplet.IN_PROVISION_STATE; - } return true; } @@ -286,217 +358,144 @@ private short mapCryptoErrorToKMError(short reason) { } } - protected void validateApduHeader(APDU apdu) { + protected short validateApduHeader(APDU apdu) { // Read the apdu header and buffer. byte[] apduBuffer = apdu.getBuffer(); byte apduClass = apduBuffer[ISO7816.OFFSET_CLA]; short P1P2 = Util.getShort(apduBuffer, ISO7816.OFFSET_P1); - // Validate APDU Header. if ((apduClass != CLA_ISO7816_NO_SM_NO_CHAN)) { ISOException.throwIt(ISO7816.SW_CLA_NOT_SUPPORTED); } - // Validate P1P2. if (P1P2 != KMKeymasterApplet.KM_HAL_VERSION) { ISOException.throwIt(ISO7816.SW_INCORRECT_P1P2); } + byte apduIns = apduBuffer[ISO7816.OFFSET_INS]; + // Validate whether INS can be supported + if (!(apduIns > KEYMINT_CMD_APDU_START && apduIns < KEYMINT_CMD_APDU_END)) { + ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED); + } + return apduIns; } /** * Processes an incoming APDU and handles it using command objects. - * * @param apdu the incoming APDU */ @Override public void process(APDU apdu) { try { - // Handle the card reset status before processing apdu. - if (repository.isPowerResetEventOccurred()) { - // Release all the operation instances. - seProvider.releaseAllOperations(); - } 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 this is select applet apdu which is selecting this applet then - // return + // If this is select applet apdu which is selecting this applet then return if (apdu.isISOInterindustryCLA()) { if (selectingApplet()) { return; } } // 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); - } - bufferRef[0] = 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_PRESHARED_SECRET_CMD: - processProvisionSharedSecretCmd(apdu); - provisionStatus |= KMKeymasterApplet.PROVISION_STATUS_PRESHARED_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() - && (keymasterState == KMKeymasterApplet.ACTIVE_STATE) - && (!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; - case INS_SET_VERSION_PATCHLEVEL_CMD: - processSetVersionAndPatchLevels(apdu); - break; - default: - ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED); - } - } else { - ISOException.throwIt(ISO7816.SW_COMMAND_NOT_ALLOWED); + short apduIns = validateApduHeader(apdu); + switch (apduIns) { + case INS_INIT_STRONGBOX_CMD: + processInitStrongBoxCmd(apdu); + sendError(apdu, KMError.OK); + return; + case INS_GENERATE_KEY_CMD: + processGenerateKey(apdu); + break; + case INS_IMPORT_KEY_CMD: + processImportKeyCmd(apdu); + break; + case INS_BEGIN_IMPORT_WRAPPED_KEY_CMD: + processBeginImportWrappedKeyCmd(apdu); + break; + case INS_FINISH_IMPORT_WRAPPED_KEY_CMD: + processFinishImportWrappedKeyCmd(apdu); + break; + case INS_EXPORT_KEY_CMD: + processExportKeyCmd(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_UPDATE_AAD_OPERATION_CMD: + processUpdateAadOperationCmd(apdu); + break; + case INS_GENERATE_RKP_KEY_CMD: + case INS_BEGIN_SEND_DATA_CMD: + case INS_UPDATE_CHALLENGE_CMD: + case INS_UPDATE_EEK_CHAIN_CMD: + case INS_UPDATE_KEY_CMD: + case INS_FINISH_SEND_DATA_CMD: + case INS_GET_RESPONSE_CMD: + case INS_GET_RKP_HARDWARE_INFO: + rkp.process(apduIns, apdu); + break; + default: + ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED); } } catch (KMException exception) { freeOperations(); - sendError(apdu, KMException.getReason()); - exception.clear(); + resetWrappingKey(); + sendError(apdu, KMException.reason()); } catch (ISOException exp) { sendError(apdu, mapISOErrorToKMError(exp.getReason())); freeOperations(); + resetWrappingKey(); + sendError(apdu, mapISOErrorToKMError(exp.getReason())); } catch (CryptoException e) { freeOperations(); + resetWrappingKey(); sendError(apdu, mapCryptoErrorToKMError(e.getReason())); } catch (Exception e) { + sendError(apdu, KMError.GENERIC_UNKNOWN_ERROR); freeOperations(); + resetWrappingKey(); sendError(apdu, KMError.GENERIC_UNKNOWN_ERROR); } finally { resetData(); @@ -507,25 +506,14 @@ && isProvisioningComplete())) { private void generateUniqueOperationHandle(byte[] buf, short offset, short len) { do { seProvider.newRandomNumber(buf, offset, len); - } while (null != repository.findOperation(buf, offset, len)); - } - - 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_PRESHARED_SECRET))) { - return true; - } else { - return false; - } + } while (null != findOperation(buf, offset, len)); } private void freeOperations() { if (data[OP_HANDLE] != KMType.INVALID_VALUE) { - KMOperationState op = repository.findOperation(data[OP_HANDLE]); + KMOperationState op = findOperation(data[OP_HANDLE]); if (op != null) { - repository.releaseOperation(op); + releaseOperation(op); } } } @@ -534,35 +522,58 @@ private void processEarlyBootEndedCmd(APDU apdu) { KMException.throwIt(KMError.UNIMPLEMENTED); } + private short deviceLockedCmd(APDU apdu){ + short cmd = KMArray.instance((short) 2); + // passwordOnly + KMArray.cast(cmd).add((short) 0, KMInteger.exp()); + // verification token + KMArray.cast(cmd).add((short) 1, KMVerificationToken.exp()); + return receiveIncoming(apdu, cmd); + } + private void processDeviceLockedCmd(APDU apdu) { - receiveIncoming(apdu); + short cmd = deviceLockedCmd(apdu); byte[] scratchPad = apdu.getBuffer(); - tmpVariables[0] = KMArray.instance((short) 2); - KMArray.cast(tmpVariables[0]).add((short) 0, KMInteger.exp()); - tmpVariables[1] = KMVerificationToken.exp(); - KMArray.cast(tmpVariables[0]).add((short) 1, tmpVariables[1]); - // Decode the arguments - tmpVariables[0] = decoder.decode(tmpVariables[0], (byte[]) bufferRef[0], bufferProp[BUF_START_OFFSET], bufferProp[BUF_LEN_OFFSET]); - //reclaim memory - repository.reclaimMemory(bufferProp[BUF_LEN_OFFSET]); - - 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); - validateVerificationToken(data[VERIFICATION_TOKEN], scratchPad); - short verTime = KMVerificationToken.cast(data[VERIFICATION_TOKEN]).getTimestamp(); + short passwordOnly = KMArray.cast(cmd).get((short) 0); + short verToken = KMArray.cast(cmd).get((short) 1); + passwordOnly = KMInteger.cast(passwordOnly).getByte(); + validateVerificationToken(verToken, scratchPad); + short verTime = KMVerificationToken.cast(verToken).getTimestamp(); short lastDeviceLockedTime = repository.getDeviceTimeStamp(); if (KMInteger.compare(verTime, lastDeviceLockedTime) > 0) { Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 8, (byte) 0); KMInteger.cast(verTime).getValue(scratchPad, (short) 0, (short) 8); repository.setDeviceLock(true); - repository.setDeviceLockPasswordOnly(tmpVariables[1] == 0x01); + repository.setDeviceLockPasswordOnly(passwordOnly == 0x01); repository.setDeviceLockTimestamp(scratchPad, (short) 0, (short) 8); } sendError(apdu, KMError.OK); } - private void resetData() { + private void resetWrappingKey(){ + if(!isValidWrappingKey()) return; + Util.arrayFillNonAtomic(wrappingKey,(short) 1, WRAPPING_KEY_SIZE, (byte) 0); + wrappingKey[0] = -1; + } + + private boolean isValidWrappingKey(){ + return wrappingKey[0] != -1; + } + + private void setWrappingKey(short key){ + if(KMByteBlob.cast(key).length() != WRAPPING_KEY_SIZE){ + KMException.throwIt(KMError.UNKNOWN_ERROR); + } + wrappingKey[0] = 0; + Util.arrayCopyNonAtomic(KMByteBlob.cast(key).getBuffer(), + KMByteBlob.cast(key).getStartOff(),wrappingKey,(short)1,WRAPPING_KEY_SIZE); + } + + private short getWrappingKey(){ + return KMByteBlob.instance(wrappingKey,(short)1,WRAPPING_KEY_SIZE); + } + + protected void resetData() { short index = 0; while (index < data.length) { data[index] = KMType.INVALID_VALUE; @@ -578,384 +589,106 @@ private void resetData() { /** * Sends a response, may be extended response, as requested by the command. */ - public static void sendOutgoing(APDU apdu) { - if (((short) (bufferProp[BUF_LEN_OFFSET] + bufferProp[BUF_START_OFFSET])) > ((short) repository + public static void sendOutgoing(APDU apdu, short resp) { + //TODO handle the extended buffer stuff. We can reuse this. + short bufferStartOffset = repository.allocAvailableMemory(); + byte[] buffer = repository.getHeap(); + // TODO we can change the following to incremental send. + short bufferLength = encoder.encode(resp, buffer, bufferStartOffset); + if (((short) (bufferLength + bufferStartOffset)) > ((short) repository .getHeap().length)) { ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); } // Send data apdu.setOutgoing(); - apdu.setOutgoingLength(bufferProp[BUF_LEN_OFFSET]); - apdu.sendBytesLong((byte[]) bufferRef[0], bufferProp[BUF_START_OFFSET], bufferProp[BUF_LEN_OFFSET]); + apdu.setOutgoingLength(bufferLength); + apdu.sendBytesLong(buffer, bufferStartOffset, bufferLength); } /** * Receives data, which can be extended data, as requested by the command instance. */ - public static void receiveIncoming(APDU apdu) { + public static short receiveIncoming(APDU apdu, short reqExp) { byte[] srcBuffer = apdu.getBuffer(); short recvLen = apdu.setIncomingAndReceive(); short srcOffset = apdu.getOffsetCdata(); - bufferProp[BUF_LEN_OFFSET] = apdu.getIncomingLength(); - bufferProp[BUF_START_OFFSET] = repository.allocReclaimableMemory(bufferProp[BUF_LEN_OFFSET]); - short index = bufferProp[BUF_START_OFFSET]; - - while (recvLen > 0 && ((short) (index - bufferProp[BUF_START_OFFSET]) < bufferProp[BUF_LEN_OFFSET])) { - Util.arrayCopyNonAtomic(srcBuffer, srcOffset, (byte[]) bufferRef[0], index, recvLen); + // TODO add logic to handle the extended length buffer. In this case the memory can be reused + // from extended buffer. + short bufferLength = apdu.getIncomingLength(); + short bufferStartOffset = repository.allocReclaimableMemory(bufferLength); + short index = bufferStartOffset; + byte[] buffer = repository.getHeap(); + while (recvLen > 0 && ((short) (index - bufferStartOffset) < bufferLength)) { + Util.arrayCopyNonAtomic(srcBuffer, srcOffset, buffer, index, recvLen); index += recvLen; recvLen = apdu.receiveBytes(srcOffset); } + short req = decoder.decode(reqExp, buffer, bufferStartOffset, bufferLength); + repository.reclaimMemory(bufferLength); + return req; } private void processGetHwInfoCmd(APDU apdu) { // No arguments expected - final byte[] JavacardKeymasterDevice = { - 0x4A, 0x61, 0x76, 0x61, 0x63, 0x61, 0x72, 0x64, 0x4B, 0x65, 0x79, 0x6D, 0x61, 0x73, 0x74, - 0x65, 0x72, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, + final byte version = 1; + final byte[] JavacardKeymintDevice = { + 0x4a,0x61,0x76,0x61,0x63,0x61,0x72,0x64, + 0x4b,0x65,0x79,0x6d,0x69,0x6e,0x74, + 0x44,0x65,0x76,0x69,0x63,0x65, }; final byte[] Google = {0x47, 0x6F, 0x6F, 0x67, 0x6C, 0x65}; - // Make the response - short respPtr = KMArray.instance((short) 3); + short respPtr = KMArray.instance((short) 6); KMArray resp = KMArray.cast(respPtr); - resp.add((short) 0, KMEnum.instance(KMType.HARDWARE_TYPE, KMType.STRONGBOX)); + resp.add((short) 0, KMInteger.uint_16(KMError.OK)); + resp.add((short) 1, KMInteger.uint_8(version)); + resp.add((short) 2, KMEnum.instance(KMType.HARDWARE_TYPE, KMType.STRONGBOX)); resp.add( - (short) 1, + (short) 3, KMByteBlob.instance( - JavacardKeymasterDevice, (short) 0, (short) JavacardKeymasterDevice.length)); - resp.add((short) 2, KMByteBlob.instance(Google, (short) 0, (short) Google.length)); - - bufferProp[BUF_START_OFFSET] = repository.allocAvailableMemory(); - // Encode the response - actual bufferProp[BUF_LEN_OFFSET] is 86 - bufferProp[BUF_LEN_OFFSET] = encoder.encode(respPtr, (byte[]) bufferRef[0], bufferProp[BUF_START_OFFSET]); + JavacardKeymintDevice, (short) 0, (short) JavacardKeymintDevice.length)); + resp.add((short) 4, KMByteBlob.instance(Google, (short) 0, (short) Google.length)); + resp.add((short)5, KMInteger.uint_8((byte)1)); // send buffer to master - sendOutgoing(apdu); + sendOutgoing(apdu, respPtr); + } + + private short addRngEntropyCmd(APDU apdu){ + short cmd = KMArray.instance((short) 1); + // Rng entropy + KMArray.cast(cmd).add((short) 0, KMByteBlob.exp()); + return receiveIncoming(apdu, cmd); } private void processAddRngEntropyCmd(APDU apdu) { // Receive the incoming request fully from the master. - receiveIncoming(apdu); - // Argument 1 - short argsProto = KMArray.instance((short) 1); - KMArray.cast(argsProto).add((short) 0, KMByteBlob.exp()); - // Decode the argument - short args = decoder.decode(argsProto, (byte[]) bufferRef[0], bufferProp[BUF_START_OFFSET], bufferProp[BUF_LEN_OFFSET]); - //reclaim memory - repository.reclaimMemory(bufferProp[BUF_LEN_OFFSET]); - + short cmd = addRngEntropyCmd(apdu); // Process - KMByteBlob blob = KMByteBlob.cast(KMArray.cast(args).get((short) 0)); + KMByteBlob blob = KMByteBlob.cast(KMArray.cast(cmd).get((short) 0)); // Maximum 2KiB of seed is allowed. if (blob.length() > MAX_SEED_SIZE) { - KMException.throwIt(KMError.INVALID_ARGUMENT); + KMException.throwIt(KMError.INVALID_INPUT_LENGTH); } seProvider.addRngEntropy(blob.getBuffer(), blob.getStartOff(), blob.length()); sendError(apdu, KMError.OK); } - private void processSetVersionAndPatchLevels(APDU apdu) { - receiveIncoming(apdu); - byte[] scratchPad = apdu.getBuffer(); - // Argument 1 OS Version - tmpVariables[0] = KMInteger.exp(); - // Argument 2 OS Patch level - tmpVariables[1] = KMInteger.exp(); - // Argument 3 Vendor Patch level - tmpVariables[2] = KMInteger.exp(); - // Array of expected arguments - short argsProto = KMArray.instance((short) 3); - KMArray.cast(argsProto).add((short) 0, tmpVariables[0]); - KMArray.cast(argsProto).add((short) 1, tmpVariables[1]); - KMArray.cast(argsProto).add((short) 2, tmpVariables[2]); - // Decode the arguments - short args = decoder.decode(argsProto, (byte[]) bufferRef[0], bufferProp[BUF_START_OFFSET], bufferProp[BUF_LEN_OFFSET]); - //reclaim memory - repository.reclaimMemory(bufferProp[BUF_LEN_OFFSET]); - - tmpVariables[0] = KMArray.cast(args).get((short) 0); - tmpVariables[1] = KMArray.cast(args).get((short) 1); - tmpVariables[2] = KMArray.cast(args).get((short) 2); - - repository.setOsVersion( - KMInteger.cast(tmpVariables[0]).getBuffer(), - KMInteger.cast(tmpVariables[0]).getStartOff(), - KMInteger.cast(tmpVariables[0]).length()); - - repository.setOsPatch( - KMInteger.cast(tmpVariables[1]).getBuffer(), - KMInteger.cast(tmpVariables[1]).getStartOff(), - KMInteger.cast(tmpVariables[1]).length()); - - repository.setVendorPatchLevel( - KMInteger.cast(tmpVariables[2]).getBuffer(), - KMInteger.cast(tmpVariables[2]).getStartOff(), - KMInteger.cast(tmpVariables[2]).length()); - - sendError(apdu, KMError.OK); - } - - private void processGetCertChainCmd(APDU apdu) { - // Make the response - tmpVariables[0] = seProvider.getCertificateChainLength(); - short int32Ptr = buildErrorStatus(KMError.OK); - //Total Extra length - // Add arrayHeader and (PowerResetStatus + KMError.OK) - tmpVariables[2] = (short) (1 + encoder.getEncodedIntegerLength(int32Ptr)); - tmpVariables[0] += tmpVariables[2]; - tmpVariables[1] = KMByteBlob.instance(tmpVariables[0]); - bufferRef[0] = KMByteBlob.cast(tmpVariables[1]).getBuffer(); - bufferProp[BUF_START_OFFSET] = KMByteBlob.cast(tmpVariables[1]).getStartOff(); - bufferProp[BUF_LEN_OFFSET] = KMByteBlob.cast(tmpVariables[1]).length(); - // read the cert chain from non-volatile memory. Cert chain is already in - // CBOR format. - seProvider.readCertificateChain((byte[]) bufferRef[0], (short) (bufferProp[BUF_START_OFFSET] + tmpVariables[2])); - // Encode cert chain. - encoder.encodeCertChain((byte[]) bufferRef[0], bufferProp[BUF_START_OFFSET], bufferProp[BUF_LEN_OFFSET], int32Ptr); - sendOutgoing(apdu); - } - - private void processProvisionAttestationCertParams(APDU apdu) { - receiveIncoming(apdu); - // Arguments - short blob = KMByteBlob.exp(); - short argsProto = KMArray.instance((short) 2); - KMArray.cast(argsProto).add((short) 0, blob); // Cert - DER encoded issuer - KMArray.cast(argsProto).add((short) 1, blob); // Cert - Expiry Time - // Decode the argument. - short args = decoder.decode(argsProto, (byte[]) bufferRef[0], bufferProp[BUF_START_OFFSET], bufferProp[BUF_LEN_OFFSET]); - //reclaim memory - repository.reclaimMemory(bufferProp[BUF_LEN_OFFSET]); - - // 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()); - } - - private void processProvisionAttestationCertChainCmd(APDU apdu) { - tmpVariables[0] = seProvider.getCertificateChainLength(); - if (tmpVariables[0] != 0) { - //Clear the previous certificate chain. - seProvider.clearCertificateChain(); - } - byte[] srcBuffer = apdu.getBuffer(); - short recvLen = apdu.setIncomingAndReceive(); - short srcOffset = apdu.getOffsetCdata(); - bufferProp[BUF_LEN_OFFSET] = apdu.getIncomingLength(); - bufferProp[BUF_START_OFFSET] = repository.alloc(bufferProp[BUF_LEN_OFFSET]); - short bytesRead = 0; - Util.arrayCopyNonAtomic(srcBuffer, srcOffset, (byte[]) bufferRef[0], bufferProp[BUF_START_OFFSET], - recvLen); - // tmpVariables[1] holds the total length + Header length. - tmpVariables[1] = decoder.readCertificateChainLengthAndHeaderLen((byte[]) bufferRef[0], - bufferProp[BUF_START_OFFSET], recvLen); - while (recvLen > 0 && ((short) bytesRead <= bufferProp[BUF_LEN_OFFSET])) { - seProvider.persistPartialCertificateChain((byte[]) bufferRef[0], bufferProp[BUF_START_OFFSET], - recvLen, bufferProp[BUF_LEN_OFFSET]); - bytesRead += recvLen; - recvLen = apdu.receiveBytes(srcOffset); - if (recvLen > 0) { - Util.arrayCopyNonAtomic(srcBuffer, srcOffset, (byte[]) bufferRef[0], bufferProp[BUF_START_OFFSET], - 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(); - // Arguments - short keyparams = KMKeyParameters.exp(); - short keyFormat = KMEnum.instance(KMType.KEY_FORMAT); - short blob = KMByteBlob.exp(); - 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); - - // Decode the argument - short args = decoder.decode(argsProto, (byte[]) bufferRef[0], bufferProp[BUF_START_OFFSET], bufferProp[BUF_LEN_OFFSET]); - //reclaim memory - repository.reclaimMemory(bufferProp[BUF_LEN_OFFSET]); - - // 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); - data[IMPORTED_KEY_BLOB] = KMArray.cast(args).get((short) 2); - // Key format must be RAW format - tmpVariables[0] = KMEnum.cast(tmpVariables[0]).getVal(); - if (tmpVariables[0] != KMType.RAW) { - KMException.throwIt(KMError.UNIMPLEMENTED); - } - data[ORIGIN] = KMType.IMPORTED; - - // get algorithm - only EC keys expected - tmpVariables[0] = KMEnumTag.getValue(KMType.ALGORITHM, data[KEY_PARAMETERS]); - if (tmpVariables[0] != KMType.EC) { - KMException.throwIt(KMError.INVALID_ARGUMENT); - } - // get digest - only SHA256 supported - tmpVariables[0] = - KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.DIGEST, 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.SHA2_256) { - KMException.throwIt(KMError.INCOMPATIBLE_DIGEST); - } - } 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) { - 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.ATTEST_KEY) { - KMException.throwIt(KMError.INCOMPATIBLE_PURPOSE); - } - } else { - KMException.throwIt(KMError.INVALID_ARGUMENT); - } - // Import EC Key - initializes data[SECRET] data[PUB_KEY] - importECKeys(scratchPad); - - // persist key - seProvider.createAttestationKey( - KMByteBlob.cast(data[SECRET]).getBuffer(), - KMByteBlob.cast(data[SECRET]).getStartOff(), - KMByteBlob.cast(data[SECRET]).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, (byte[]) bufferRef[0], bufferProp[BUF_START_OFFSET], bufferProp[BUF_LEN_OFFSET]); - //reclaim memory - repository.reclaimMemory(bufferProp[BUF_LEN_OFFSET]); - - 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); - saveAttId(KMType.ATTESTATION_ID_PRODUCT); - saveAttId(KMType.ATTESTATION_ID_MANUFACTURER); - saveAttId(KMType.ATTESTATION_ID_MODEL); - saveAttId(KMType.ATTESTATION_ID_IMEI); - saveAttId(KMType.ATTESTATION_ID_MEID); - saveAttId(KMType.ATTESTATION_ID_SERIAL); - } - - 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, (byte[]) bufferRef[0], bufferProp[BUF_START_OFFSET], bufferProp[BUF_LEN_OFFSET]); - //reclaim memory - repository.reclaimMemory(bufferProp[BUF_LEN_OFFSET]); - - 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. - seProvider.createPresharedKey( - KMByteBlob.cast(tmpVariables[0]).getBuffer(), - KMByteBlob.cast(tmpVariables[0]).getStartOff(), - KMByteBlob.cast(tmpVariables[0]).length()); - } - - private void processGetProvisionStatusCmd(APDU apdu) { - tmpVariables[0] = KMArray.instance((short) 2); - KMArray.cast(tmpVariables[0]).add((short) 0, buildErrorStatus(KMError.OK)); - KMArray.cast(tmpVariables[0]).add((short) 1, KMInteger.uint_16(provisionStatus)); - - bufferProp[BUF_START_OFFSET] = repository.allocAvailableMemory(); - bufferProp[BUF_LEN_OFFSET] = encoder.encode(tmpVariables[0], (byte[]) bufferRef[0], bufferProp[BUF_START_OFFSET]); - sendOutgoing(apdu); - } - - private void saveAttId(short attTag) { - tmpVariables[0] = KMKeyParameters.findTag(KMType.BYTES_TAG, attTag, data[KEY_PARAMETERS]); - if (tmpVariables[0] != KMType.INVALID_VALUE) { - tmpVariables[0] = KMByteTag.cast(tmpVariables[0]).getValue(); - repository.persistAttId( - mapToAttId(attTag), - KMByteBlob.cast(tmpVariables[0]).getBuffer(), - KMByteBlob.cast(tmpVariables[0]).getStartOff(), - KMByteBlob.cast(tmpVariables[0]).length()); - } else { - KMException.throwIt(KMError.INVALID_ARGUMENT); - } - } - - private byte mapToAttId(short attTag) { - switch (attTag) { - case KMType.ATTESTATION_ID_BRAND: - return KMRepository.ATT_ID_BRAND; - case KMType.ATTESTATION_ID_DEVICE: - return KMRepository.ATT_ID_DEVICE; - case KMType.ATTESTATION_ID_IMEI: - return KMRepository.ATT_ID_IMEI; - case KMType.ATTESTATION_ID_MANUFACTURER: - return KMRepository.ATT_ID_MANUFACTURER; - case KMType.ATTESTATION_ID_MEID: - return KMRepository.ATT_ID_MEID; - case KMType.ATTESTATION_ID_MODEL: - return KMRepository.ATT_ID_MODEL; - case KMType.ATTESTATION_ID_PRODUCT: - return KMRepository.ATT_ID_PRODUCT; - case KMType.ATTESTATION_ID_SERIAL: - return KMRepository.ATT_ID_SERIAL; - } - KMException.throwIt(KMError.INVALID_TAG); - return (byte) 0xFF; // should never happen + private short getKeyCharacteristicsCmd(APDU apdu){ + short cmd = KMArray.instance((short) 3); + KMArray.cast(cmd).add((short) 0, KMByteBlob.exp()); + KMArray.cast(cmd).add((short) 1, KMByteBlob.exp()); + KMArray.cast(cmd).add((short) 2, KMByteBlob.exp()); + return receiveIncoming(apdu, cmd); } private void processGetKeyCharacteristicsCmd(APDU apdu) { // Receive the incoming request fully from the master. - receiveIncoming(apdu); + short cmd = getKeyCharacteristicsCmd(apdu); // Re-purpose the apdu buffer as scratch pad. byte[] scratchPad = apdu.getBuffer(); - // Arguments - tmpVariables[0] = KMArray.instance((short) 3); - KMArray.cast(tmpVariables[0]).add((short) 0, KMByteBlob.exp()); - KMArray.cast(tmpVariables[0]).add((short) 1, KMByteBlob.exp()); - KMArray.cast(tmpVariables[0]).add((short) 2, KMByteBlob.exp()); - // Decode the arguments - tmpVariables[0] = decoder.decode(tmpVariables[0], (byte[]) bufferRef[0], bufferProp[BUF_START_OFFSET], bufferProp[BUF_LEN_OFFSET]); - //reclaim memory - repository.reclaimMemory(bufferProp[BUF_LEN_OFFSET]); - - 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); + data[KEY_BLOB] = KMArray.cast(cmd).get((short) 0); + data[APP_ID] = KMArray.cast(cmd).get((short) 1); + data[APP_DATA] = KMArray.cast(cmd).get((short) 2); if (!KMByteBlob.cast(data[APP_ID]).isValid()) { data[APP_ID] = KMType.INVALID_VALUE; } @@ -963,68 +696,59 @@ private void processGetKeyCharacteristicsCmd(APDU apdu) { data[APP_DATA] = KMType.INVALID_VALUE; } // Parse Key Blob - parseEncryptedKeyBlob(scratchPad); + parseEncryptedKeyBlob(data[KEY_BLOB],data[APP_ID], data[APP_DATA], scratchPad); // Check Version and Patch Level checkVersionAndPatchLevel(scratchPad); // make response. - tmpVariables[0] = KMArray.instance((short) 2); - KMArray.cast(tmpVariables[0]).add((short) 0, buildErrorStatus(KMError.OK)); - KMArray.cast(tmpVariables[0]).add((short) 1, data[KEY_CHARACTERISTICS]); - - bufferProp[BUF_START_OFFSET] = repository.allocAvailableMemory(); - // Encode the response - bufferProp[BUF_LEN_OFFSET] = encoder.encode(tmpVariables[0], (byte[]) bufferRef[0], bufferProp[BUF_START_OFFSET]); - sendOutgoing(apdu); + short resp = KMArray.instance((short) 2); + KMArray.cast(resp).add((short) 0, KMInteger.uint_16(KMError.OK)); + KMArray.cast(resp).add((short) 1, data[KEY_CHARACTERISTICS]); + sendOutgoing(apdu, resp); } private void processGetHmacSharingParamCmd(APDU apdu) { // No Arguments // Create HMAC Sharing Parameters - tmpVariables[2] = KMHmacSharingParameters.instance(); - KMHmacSharingParameters.cast(tmpVariables[2]).setNonce(repository.getHmacNonce()); - KMHmacSharingParameters.cast(tmpVariables[2]).setSeed(KMByteBlob.instance((short) 0)); + short params = KMHmacSharingParameters.instance(); + short nonce = repository.getHmacNonce(); + short seed = KMByteBlob.instance((short) 0); + KMHmacSharingParameters.cast(params).setNonce(nonce); + KMHmacSharingParameters.cast(params).setSeed(seed); // prepare the response - tmpVariables[3] = KMArray.instance((short) 2); - KMArray.cast(tmpVariables[3]).add((short) 0, buildErrorStatus(KMError.OK)); - KMArray.cast(tmpVariables[3]).add((short) 1, tmpVariables[2]); - - bufferProp[BUF_START_OFFSET] = repository.allocAvailableMemory(); - // Encode the response - bufferProp[BUF_LEN_OFFSET] = encoder.encode(tmpVariables[3], (byte[]) bufferRef[0], bufferProp[BUF_START_OFFSET]); - sendOutgoing(apdu); + short resp = KMArray.instance((short) 2); + KMArray.cast(resp).add((short) 0, KMInteger.uint_16(KMError.OK)); + KMArray.cast(resp).add((short) 1, params); + sendOutgoing(apdu, resp); } private void processDeleteAllKeysCmd(APDU apdu) { - // No arguments // Send ok sendError(apdu, KMError.OK); } - private void processDeleteKeyCmd(APDU apdu) { + private short deleteKeyCmd(APDU apdu){ + short cmd = KMArray.instance((short) 1); + KMArray.cast(cmd).add((short) 0, KMByteBlob.exp()); + return receiveIncoming(apdu, cmd); + } - // Receive the incoming request fully from the master. - receiveIncoming(apdu); - // Arguments - short argsProto = KMArray.instance((short) 1); - KMArray.cast(argsProto).add((short) 0, KMByteBlob.exp()); - // Decode the argument - short args = decoder.decode(argsProto, (byte[]) bufferRef[0], bufferProp[BUF_START_OFFSET], bufferProp[BUF_LEN_OFFSET]); - //reclaim memory - repository.reclaimMemory(bufferProp[BUF_LEN_OFFSET]); + private short keyBlob(){ + short keyBlob = KMArray.instance((short) 5); + KMArray.cast(keyBlob).add(KMKeymasterApplet.KEY_BLOB_SECRET, KMByteBlob.exp()); + KMArray.cast(keyBlob).add(KMKeymasterApplet.KEY_BLOB_AUTH_TAG, KMByteBlob.exp()); + KMArray.cast(keyBlob).add(KMKeymasterApplet.KEY_BLOB_NONCE, KMByteBlob.exp()); + short keyChar = KMKeyCharacteristics.exp(); + KMArray.cast(keyBlob).add(KMKeymasterApplet.KEY_BLOB_PARAMS, keyChar); + KMArray.cast(keyBlob).add(KMKeymasterApplet.KEY_BLOB_PUB_KEY, KMByteBlob.exp()); + return keyBlob; + } - // Process - data[KEY_BLOB] = KMArray.cast(args).get((short) 0); - tmpVariables[0] = KMByteBlob.cast(data[KEY_BLOB]).getStartOff(); - tmpVariables[1] = KMArray.instance((short) 5); - KMArray.cast(tmpVariables[1]).add(KMKeymasterApplet.KEY_BLOB_SECRET, KMByteBlob.exp()); - KMArray.cast(tmpVariables[1]).add(KMKeymasterApplet.KEY_BLOB_AUTH_TAG, KMByteBlob.exp()); - KMArray.cast(tmpVariables[1]).add(KMKeymasterApplet.KEY_BLOB_NONCE, KMByteBlob.exp()); - tmpVariables[2] = KMKeyCharacteristics.exp(); - KMArray.cast(tmpVariables[1]).add(KMKeymasterApplet.KEY_BLOB_KEYCHAR, tmpVariables[2]); - KMArray.cast(tmpVariables[1]).add(KMKeymasterApplet.KEY_BLOB_PUB_KEY, KMByteBlob.exp()); + private void processDeleteKeyCmd(APDU apdu) { + short cmd = deleteKeyCmd(apdu); + data[KEY_BLOB] = KMArray.cast(cmd).get((short) 0); try { - data[KEY_BLOB] = decoder.decodeArray(tmpVariables[1], + data[KEY_BLOB] = decoder.decodeArray(keyBlob(), KMByteBlob.cast(data[KEY_BLOB]).getBuffer(), KMByteBlob.cast(data[KEY_BLOB]).getStartOff(), KMByteBlob.cast(data[KEY_BLOB]).length()); @@ -1033,273 +757,268 @@ private void processDeleteKeyCmd(APDU apdu) { // input is empty then VTS accepts UNIMPLEMENTED errorCode as well. KMException.throwIt(KMError.UNIMPLEMENTED); } - tmpVariables[0] = KMArray.cast(data[KEY_BLOB]).length(); - if (tmpVariables[0] < 4) { + if (KMArray.cast(data[KEY_BLOB]).length() < 4) { KMException.throwIt(KMError.INVALID_KEY_BLOB); } // Send ok sendError(apdu, KMError.OK); } + private short computeSharedHmacCmd(APDU apdu){ + short params = KMHmacSharingParameters.exp(); + short paramsVec = KMArray.exp(params); + short cmd = KMArray.instance((short) 1); + KMArray.cast(cmd).add((short) 0, paramsVec); + return receiveIncoming(apdu, cmd); + } + private void processComputeSharedHmacCmd(APDU apdu) { // Receive the incoming request fully from the master into buffer. - receiveIncoming(apdu); + short cmd = computeSharedHmacCmd(apdu); byte[] scratchPad = apdu.getBuffer(); - tmpVariables[1] = KMHmacSharingParameters.exp(); - tmpVariables[0] = KMArray.exp(tmpVariables[1]); - tmpVariables[2] = KMArray.instance((short) 1); - KMArray.cast(tmpVariables[2]).add((short) 0, tmpVariables[0]); // Vector of hmac params - // Decode the arguments - tmpVariables[0] = decoder.decode(tmpVariables[2], (byte[]) bufferRef[0], bufferProp[BUF_START_OFFSET], bufferProp[BUF_LEN_OFFSET]); - //reclaim memory - repository.reclaimMemory(bufferProp[BUF_LEN_OFFSET]); - - data[HMAC_SHARING_PARAMS] = KMArray.cast(tmpVariables[0]).get((short) 0); + data[HMAC_SHARING_PARAMS] = KMArray.cast(cmd).get((short) 0); // Concatenate HMAC Params - tmpVariables[0] = KMArray.cast(data[HMAC_SHARING_PARAMS]).length(); // total number of params - tmpVariables[1] = repository.alloc((short) (tmpVariables[0] * HMAC_SHARED_PARAM_MAX_SIZE)); - tmpVariables[2] = 0; // index for params - tmpVariables[3] = 0; // index for concatenation buffer + //tmpVariables[0] + short paramsLen = KMArray.cast(data[HMAC_SHARING_PARAMS]).length(); // total number of params + //tmpVariables[1] + short concateBuffer = repository.alloc((short) (paramsLen * HMAC_SHARED_PARAM_MAX_SIZE)); + //tmpVariables[2] + short paramIndex = 0; // index for params + //tmpVariables[3] + short bufferIndex = 0; // index for concatenation buffer // To check if nonce created by Strongbox is found. This value becomes 1 if both // seed and nonce created here are found in hmac sharing parameters received. - tmpVariables[7] = 0; - tmpVariables[9] = repository.getHmacNonce(); + //tmpVariables[7] = 0; + short found = 0; + //tmpVariables[9] + short nonce = repository.getHmacNonce(); - while (tmpVariables[2] < tmpVariables[0]) { + while (paramIndex < paramsLen) { // read HmacSharingParam - tmpVariables[4] = KMArray.cast(data[HMAC_SHARING_PARAMS]).get(tmpVariables[2]); + //tmpVariables[4] + short param = KMArray.cast(data[HMAC_SHARING_PARAMS]).get(paramIndex); // get seed - 32 bytes max - tmpVariables[5] = KMHmacSharingParameters.cast(tmpVariables[4]).getSeed(); - tmpVariables[6] = KMByteBlob.cast(tmpVariables[5]).length(); + //tmpVariables[5] + short seed = KMHmacSharingParameters.cast(param).getSeed(); + //tmpVariables[6] + short seedLength = KMByteBlob.cast(seed).length(); // if seed is present - if (tmpVariables[6] != 0) { + if (seedLength != 0) { // then copy that to concatenation buffer Util.arrayCopyNonAtomic( - KMByteBlob.cast(tmpVariables[5]).getBuffer(), - KMByteBlob.cast(tmpVariables[5]).getStartOff(), + KMByteBlob.cast(seed).getBuffer(), + KMByteBlob.cast(seed).getStartOff(), repository.getHeap(), - (short) (tmpVariables[1] + tmpVariables[3]), // concat index - tmpVariables[6]); - tmpVariables[3] += tmpVariables[6]; // increment the concat index - } else if (tmpVariables[7] == 0) { - tmpVariables[7] = 1; + (short) (concateBuffer + bufferIndex), // concat index + seedLength); + bufferIndex += seedLength; // increment the concat index + } else if (found == 0) { + found = 1; // Applet does not have any seed. Potentially } // if nonce is present get nonce - 32 bytes - tmpVariables[5] = KMHmacSharingParameters.cast(tmpVariables[4]).getNonce(); - tmpVariables[6] = KMByteBlob.cast(tmpVariables[5]).length(); - // if nonce is not present - it is an error - if (tmpVariables[6] == 0) { + //tmpVariables[5] + short paramNonce = KMHmacSharingParameters.cast(param).getNonce(); + short nonceLen = KMByteBlob.cast(paramNonce).length(); + // if nonce is less then 32 - it is an error + if (nonceLen < 32) { KMException.throwIt(KMError.INVALID_ARGUMENT); } // copy nonce to concatenation buffer Util.arrayCopyNonAtomic( - KMByteBlob.cast(tmpVariables[5]).getBuffer(), - KMByteBlob.cast(tmpVariables[5]).getStartOff(), + KMByteBlob.cast(paramNonce).getBuffer(), + KMByteBlob.cast(paramNonce).getStartOff(), repository.getHeap(), - (short) (tmpVariables[1] + tmpVariables[3]), // index - tmpVariables[6]); + (short) (concateBuffer + bufferIndex), // index + nonceLen); // Check if the nonce generated here is present in the hmacSharingParameters array. // Otherwise throw INVALID_ARGUMENT error. - if (tmpVariables[7] == 1) { + if (found == 1) { if (0 == Util.arrayCompare( repository.getHeap(), - (short) (tmpVariables[1] + tmpVariables[3]), - KMByteBlob.cast(tmpVariables[9]).getBuffer(), - KMByteBlob.cast(tmpVariables[9]).getStartOff(), - tmpVariables[6])) { - tmpVariables[7] = 2; // hmac nonce for this keymaster found. + (short) (concateBuffer + bufferIndex), + KMByteBlob.cast(nonce).getBuffer(), + KMByteBlob.cast(nonce).getStartOff(), + nonceLen)) { + found = 2; // hmac nonce for this keymaster found. } else { - tmpVariables[7] = 0; + found = 0; } } - tmpVariables[3] += tmpVariables[6]; // increment by nonce length - tmpVariables[2]++; // go to next hmac param in the vector + bufferIndex += nonceLen; // increment by nonce length + paramIndex++; // go to next hmac param in the vector } - if (tmpVariables[7] != 2) { + if (found != 2) { KMException.throwIt(KMError.INVALID_ARGUMENT); } - // generate the key and store it in scratch pad - 32 bytes - tmpVariables[6] = + //tmpVariables[6] + short keyLen = seProvider.cmacKDF( seProvider.getPresharedKey(), ckdfLable, (short) 0, (short) ckdfLable.length, repository.getHeap(), - tmpVariables[1], - tmpVariables[3], + concateBuffer, + bufferIndex, scratchPad, (short) 0); // persist the computed hmac key. - repository.initComputedHmac(scratchPad, (short) 0, tmpVariables[6]); + repository.initComputedHmac(scratchPad, (short) 0, keyLen); // Generate sharingKey verification signature and store that in scratch pad. - tmpVariables[5] = + //tmpVariables[5] + short signLen = seProvider.hmacSign( scratchPad, (short) 0, - tmpVariables[6], + keyLen, sharingCheck, (short) 0, (short) sharingCheck.length, scratchPad, - tmpVariables[6]); + keyLen); // verification signature blob - 32 bytes - tmpVariables[1] = KMByteBlob.instance(scratchPad, tmpVariables[6], tmpVariables[5]); + //tmpVariables[1] + short signature = KMByteBlob.instance(scratchPad, keyLen, signLen); // prepare the response - tmpVariables[0] = KMArray.instance((short) 2); - KMArray.cast(tmpVariables[0]).add((short) 0, buildErrorStatus(KMError.OK)); - KMArray.cast(tmpVariables[0]).add((short) 1, tmpVariables[1]); - - bufferProp[BUF_START_OFFSET] = repository.allocAvailableMemory(); - // Encode the response - bufferProp[BUF_LEN_OFFSET] = encoder.encode(tmpVariables[0], (byte[]) bufferRef[0], bufferProp[BUF_START_OFFSET]); - sendOutgoing(apdu); - } - - private boolean isKeyUpgradeRequired(short tag, short systemParam) { - // validate the tag and check if key needs upgrade. - tmpVariables[0] = KMKeyParameters.findTag(KMType.UINT_TAG, tag, data[HW_PARAMETERS]); - tmpVariables[0] = KMIntegerTag.cast(tmpVariables[0]).getValue(); - tmpVariables[1] = KMInteger.uint_8((byte) 0); - if (tmpVariables[0] != KMType.INVALID_VALUE) { - // OS version in key characteristics must be less the OS version stored in Javacard or the - // stored version must be zero. Then only upgrade is allowed else it is invalid argument. - if ((tag == KMType.OS_VERSION - && KMInteger.compare(tmpVariables[0], systemParam) == 1 - && KMInteger.compare(systemParam, tmpVariables[1]) == 0)) { - // Key needs upgrade. - return true; - } else if ((KMInteger.compare(tmpVariables[0], systemParam) == -1)) { - // Each os version or patch level associated with the key must be less than it's - // corresponding value stored in Javacard, then only upgrade is allowed otherwise it - // is invalid argument. - return true; - } else if (KMInteger.compare(tmpVariables[0], systemParam) == 1) { - KMException.throwIt(KMError.INVALID_ARGUMENT); - } - } - return false; + short resp = KMArray.instance((short) 2); + KMArray.cast(resp).add((short) 0, KMInteger.uint_16(KMError.OK)); + KMArray.cast(resp).add((short) 1, signature); + sendOutgoing(apdu, resp); + } + + private short upgradeKeyCmd(APDU apdu){ + short cmd = KMArray.instance((short) 2); + short keyParams = KMKeyParameters.exp(); + KMArray.cast(cmd).add((short) 0, KMByteBlob.exp()); // Key Blob + KMArray.cast(cmd).add((short) 1, keyParams); // Key Params + return receiveIncoming(apdu, cmd); } private void processUpgradeKeyCmd(APDU apdu) { // Receive the incoming request fully from the master into buffer. - receiveIncoming(apdu); + short cmd = upgradeKeyCmd(apdu); byte[] scratchPad = apdu.getBuffer(); - tmpVariables[1] = KMArray.instance((short) 2); - tmpVariables[2] = KMKeyParameters.exp(); - KMArray.cast(tmpVariables[1]).add((short) 0, KMByteBlob.exp()); // Key Blob - KMArray.cast(tmpVariables[1]).add((short) 1, tmpVariables[2]); // Key Params - // Decode the arguments - tmpVariables[2] = decoder.decode(tmpVariables[1], (byte[]) bufferRef[0], bufferProp[BUF_START_OFFSET], bufferProp[BUF_LEN_OFFSET]); - //reclaim memory - repository.reclaimMemory(bufferProp[BUF_LEN_OFFSET]); - - data[KEY_BLOB] = KMArray.cast(tmpVariables[2]).get((short) 0); - data[KEY_PARAMETERS] = KMArray.cast(tmpVariables[2]).get((short) 1); - tmpVariables[0] = + + data[KEY_BLOB] = KMArray.cast(cmd).get((short) 0); + data[KEY_PARAMETERS] = KMArray.cast(cmd).get((short) 1); + //tmpVariables[0] + short appId = KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.APPLICATION_ID, data[KEY_PARAMETERS]); - if (tmpVariables[0] != KMTag.INVALID_VALUE) { - data[APP_ID] = KMByteTag.cast(tmpVariables[0]).getValue(); + if (appId != KMTag.INVALID_VALUE) { + data[APP_ID] = KMByteTag.cast(appId).getValue(); } - tmpVariables[0] = + short appData = KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.APPLICATION_DATA, data[KEY_PARAMETERS]); - if (tmpVariables[0] != KMTag.INVALID_VALUE) { - data[APP_DATA] = KMByteTag.cast(tmpVariables[0]).getValue(); + if (appData != KMTag.INVALID_VALUE) { + data[APP_DATA] = KMByteTag.cast(appData).getValue(); } // parse existing key blob - parseEncryptedKeyBlob(scratchPad); - boolean isKeyUpgradeRequired = false; - // Check if key requires upgrade. - isKeyUpgradeRequired |= isKeyUpgradeRequired(KMType.OS_VERSION, repository.getOsVersion()); - isKeyUpgradeRequired |= isKeyUpgradeRequired(KMType.OS_PATCH_LEVEL, repository.getOsPatch()); - isKeyUpgradeRequired |= isKeyUpgradeRequired(KMType.VENDOR_PATCH_LEVEL, repository.getVendorPatchLevel()); - isKeyUpgradeRequired |= isKeyUpgradeRequired(KMType.BOOT_PATCH_LEVEL, repository.getBootPatchLevel()); - - if (isKeyUpgradeRequired) { + parseEncryptedKeyBlob(data[KEY_BLOB], data[APP_ID], data[APP_DATA], scratchPad); + // validate characteristics to be upgraded. + short osVersion = + KMKeyParameters.findTag(KMType.UINT_TAG, KMType.OS_VERSION, data[HW_PARAMETERS]); + osVersion = KMIntegerTag.cast(osVersion).getValue(); + short osPatchLvl = + KMKeyParameters.findTag(KMType.UINT_TAG, KMType.OS_PATCH_LEVEL, data[HW_PARAMETERS]); + osPatchLvl = KMIntegerTag.cast(osPatchLvl).getValue(); + short provOsVersion = repository.getOsVersion(); + short provOsPatch = repository.getOsPatch(); + short zero = KMInteger.uint_8((byte) 0); + short error = KMError.OK; + if (osVersion != KMType.INVALID_VALUE) { + // os version in key characteristics must be less the os version stored in javacard or the + // stored version must be zero. Then only upgrade is allowed else it is invalid argument. + if (KMInteger.compare(osVersion, provOsVersion) != -1 + && KMInteger.compare(provOsVersion, zero) != 0) { + // Key Should not be upgraded, but error code should be OK, As per VTS. + error = KMError.INVALID_ARGUMENT; + } + } + if (osPatchLvl != KMType.INVALID_VALUE) { + // The key characteristics should have had os patch level < os patch level stored in javacard + // then only upgrade is allowed. + if (KMInteger.compare(osPatchLvl, provOsPatch) != -1) { + // Key Should not be upgraded, but error code should be OK, As per VTS. + error = KMError.INVALID_ARGUMENT; + } + } + + //Compare vendor patch levels + short vendorPatchLvl = + KMKeyParameters.findTag(KMType.UINT_TAG, KMType.VENDOR_PATCH_LEVEL, data[HW_PARAMETERS]); + vendorPatchLvl = KMIntegerTag.cast(vendorPatchLvl).getValue(); + short provVendorPatch = repository.getVendorPatchLevel(); + if (vendorPatchLvl != 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(vendorPatchLvl, provVendorPatch) != -1) { + // Key Should not be upgraded, but error code should be OK, As per VTS. + error = KMError.INVALID_ARGUMENT; + } + } + + //Compare boot patch levels + short bootPatch = + KMKeyParameters.findTag(KMType.UINT_TAG, KMType.BOOT_PATCH_LEVEL, data[HW_PARAMETERS]); + bootPatch = KMIntegerTag.cast(bootPatch).getValue(); + short provBootPatch = getBootPatchLevel(scratchPad); + if (bootPatch != 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(bootPatch, provBootPatch) != -1) { + // Key Should not be upgraded, but error code should be OK, As per VTS. + error = KMError.INVALID_ARGUMENT; + } + } + + if (error != KMError.INVALID_ARGUMENT) { // copy origin data[ORIGIN] = KMEnumTag.getValue(KMType.ORIGIN, data[HW_PARAMETERS]); + makeKeyCharacteristics(scratchPad); // create new key blob with current os version etc. createEncryptedKeyBlob(scratchPad); } else { data[KEY_BLOB] = KMByteBlob.instance((short) 0); } // prepare the response - tmpVariables[0] = KMArray.instance((short) 2); - KMArray.cast(tmpVariables[0]).add((short) 0, buildErrorStatus(KMError.OK)); - KMArray.cast(tmpVariables[0]).add((short) 1, data[KEY_BLOB]); - - bufferProp[BUF_START_OFFSET] = repository.allocAvailableMemory(); - // Encode the response - bufferProp[BUF_LEN_OFFSET] = encoder.encode(tmpVariables[0], (byte[]) bufferRef[0], bufferProp[BUF_START_OFFSET]); - sendOutgoing(apdu); + short resp = KMArray.instance((short) 2); + KMArray.cast(resp).add((short) 0, KMInteger.uint_16(KMError.OK)); + KMArray.cast(resp).add((short) 1, data[KEY_BLOB]); + sendOutgoing(apdu, resp); } private void processExportKeyCmd(APDU apdu) { sendError(apdu, KMError.UNIMPLEMENTED); } - - private void processImportWrappedKeyCmd(APDU apdu) { - // Receive the incoming request fully from the master into buffer. - receiveIncoming(apdu); - byte[] scratchPad = apdu.getBuffer(); - tmpVariables[1] = KMArray.instance((short) 12); - // Arguments - tmpVariables[2] = KMKeyParameters.exp(); - KMArray.cast(tmpVariables[1]).add((short) 0, tmpVariables[2]); // Key Params of wrapped key - KMArray.cast(tmpVariables[1]).add((short) 1, KMEnum.instance(KMType.KEY_FORMAT)); // Key Format - KMArray.cast(tmpVariables[1]).add((short) 2, KMByteBlob.exp()); // Wrapped Import Key Blob - KMArray.cast(tmpVariables[1]).add((short) 3, KMByteBlob.exp()); // Auth Tag - KMArray.cast(tmpVariables[1]).add((short) 4, KMByteBlob.exp()); // IV - Nonce - KMArray.cast(tmpVariables[1]).add((short) 5, KMByteBlob.exp()); // Encrypted Transport Key - KMArray.cast(tmpVariables[1]).add((short) 6, KMByteBlob.exp()); // Wrapping Key KeyBlob - KMArray.cast(tmpVariables[1]).add((short) 7, KMByteBlob.exp()); // Masking Key - KMArray.cast(tmpVariables[1]).add((short) 8, tmpVariables[2]); // Un-wrapping Params - KMArray.cast(tmpVariables[1]) - .add((short) 9, KMByteBlob.exp()); // Wrapped Key ASSOCIATED AUTH DATA - KMArray.cast(tmpVariables[1]).add((short) 10, KMInteger.exp()); // Password Sid - KMArray.cast(tmpVariables[1]).add((short) 11, KMInteger.exp()); // Biometric Sid - // Decode the arguments - short args = decoder.decode(tmpVariables[1], (byte[]) bufferRef[0], bufferProp[BUF_START_OFFSET], bufferProp[BUF_LEN_OFFSET]); - //reclaim memory - repository.reclaimMemory(bufferProp[BUF_LEN_OFFSET]); - - // Step -0 - check whether the key format and algorithm supported - // read algorithm - tmpVariables[0] = KMArray.cast(args).get((short) 0); - tmpVariables[1] = KMEnumTag.getValue(KMType.ALGORITHM, tmpVariables[0]); - // read key format - tmpVariables[2] = KMArray.cast(args).get((short) 1); - tmpVariables[2] = KMEnum.cast(tmpVariables[2]).getVal(); - // import of RSA and EC not supported with pkcs8 or x509 format - if ((tmpVariables[1] == KMType.RSA || tmpVariables[1] == KMType.EC) - && (tmpVariables[2] != KMType.RAW)) { - KMException.throwIt(KMError.UNIMPLEMENTED); - } - - // Step -1 parse the wrapping key blob - // read wrapping key blob - data[KEY_BLOB] = KMArray.cast(args).get((short) 6); - // read un wrapping key params - data[KEY_PARAMETERS] = KMArray.cast(args).get((short) 8); + private void processWrappingKeyBlob(short keyBlob, short wrapParams, byte[] scratchPad) { // Read App Id and App Data if any from un wrapping key params - data[APP_ID] = - KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.APPLICATION_ID, data[KEY_PARAMETERS]); - data[APP_DATA] = - KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.APPLICATION_DATA, data[KEY_PARAMETERS]); - if (data[APP_ID] != KMTag.INVALID_VALUE) { - data[APP_ID] = KMByteTag.cast(tmpVariables[3]).getValue(); - } - if (data[APP_DATA] != KMTag.INVALID_VALUE) { - data[APP_DATA] = KMByteTag.cast(tmpVariables[3]).getValue(); - } + short appId = + KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.APPLICATION_ID, wrapParams); + short appData = + KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.APPLICATION_DATA, wrapParams); + if (appId != KMTag.INVALID_VALUE) { + appId = KMByteTag.cast(appId).getValue(); + } + if (appData != KMTag.INVALID_VALUE) { + appData = KMByteTag.cast(appData).getValue(); + } + data[APP_ID] = appId; + data[APP_DATA] = appData; + data[KEY_PARAMETERS] = wrapParams; + data[KEY_BLOB] = keyBlob; // parse the wrapping key blob - parseEncryptedKeyBlob(scratchPad); + parseEncryptedKeyBlob(keyBlob, appId, appData, scratchPad); + validateWrappingKeyBlob(); + } + + private void validateWrappingKeyBlob(){ // check whether the wrapping key is RSA with purpose KEY_WRAP, padding RSA_OAEP and Digest // SHA2_256. + KMTag.assertPresence(data[SB_PARAMETERS],KMType.ENUM_TAG, KMType.ALGORITHM, KMError.UNSUPPORTED_KEY_ENCRYPTION_ALGORITHM); if (KMEnumTag.getValue(KMType.ALGORITHM, data[HW_PARAMETERS]) != KMType.RSA) { KMException.throwIt(KMError.UNSUPPORTED_KEY_ENCRYPTION_ALGORITHM); } @@ -1312,227 +1031,555 @@ private void processImportWrappedKeyCmd(APDU apdu) { if (!KMEnumArrayTag.contains(KMType.PURPOSE, KMType.WRAP_KEY, data[HW_PARAMETERS])) { KMException.throwIt((KMError.INCOMPATIBLE_PURPOSE)); } + } - // Step 2 - decrypt the encrypted transport key - 32 bytes AES-GCM key - // create rsa decipher - // read encrypted transport key from args - tmpVariables[0] = KMArray.cast(args).get((short) 5); - // Decrypt the transport key - tmpVariables[1] = + private short decryptTransportKey(short privExp, short modulus, short transportKey, byte[] scratchPad){ + short length = seProvider.rsaDecipherOAEP256( - KMByteBlob.cast(data[SECRET]).getBuffer(), - KMByteBlob.cast(data[SECRET]).getStartOff(), - KMByteBlob.cast(data[SECRET]).length(), - KMByteBlob.cast(data[PUB_KEY]).getBuffer(), - KMByteBlob.cast(data[PUB_KEY]).getStartOff(), - KMByteBlob.cast(data[PUB_KEY]).length(), - KMByteBlob.cast(tmpVariables[0]).getBuffer(), - KMByteBlob.cast(tmpVariables[0]).getStartOff(), - KMByteBlob.cast(tmpVariables[0]).length(), + KMByteBlob.cast(privExp).getBuffer(), + KMByteBlob.cast(privExp).getStartOff(), + KMByteBlob.cast(privExp).length(), + KMByteBlob.cast(modulus).getBuffer(), + KMByteBlob.cast(modulus).getStartOff(), + KMByteBlob.cast(modulus).length(), + KMByteBlob.cast(transportKey).getBuffer(), + KMByteBlob.cast(transportKey).getStartOff(), + KMByteBlob.cast(transportKey).length(), scratchPad, (short) 0); - data[SECRET] = KMByteBlob.instance(scratchPad, (short) 0, tmpVariables[1]); + return KMByteBlob.instance(scratchPad, (short) 0, length); - // Step 3 - XOR the decrypted AES-GCM key with with masking key - // read masking key - tmpVariables[0] = KMArray.cast(args).get((short) 7); - tmpVariables[1] = KMByteBlob.cast(tmpVariables[0]).length(); + } + + private void unmask(short data, short maskingKey){ + short dataLength = KMByteBlob.cast(data).length(); + short maskLength = KMByteBlob.cast(maskingKey).length(); // Length of masking key and transport key must be same. - if (tmpVariables[1] != KMByteBlob.cast(data[SECRET]).length()) { + if (maskLength != dataLength) { KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH); } - tmpVariables[2] = 0; // index + short index = 0; // index // Xor every byte of masking and key and store the result in data[SECRET] - while (tmpVariables[2] < tmpVariables[1]) { - tmpVariables[3] = - (short) (((short) KMByteBlob.cast(tmpVariables[0]).get(tmpVariables[2])) & 0x00FF); - tmpVariables[4] = - (short) (((short) KMByteBlob.cast(data[SECRET]).get(tmpVariables[2])) & 0x00FF); - KMByteBlob.cast(data[SECRET]) - .add(tmpVariables[2], (byte) (tmpVariables[3] ^ tmpVariables[4])); - tmpVariables[2]++; + while (index < maskLength) { + short var1 = + (short) (((short) KMByteBlob.cast(maskingKey).get(index)) & 0x00FF); + short var2 = + (short) (((short) KMByteBlob.cast(data).get(index)) & 0x00FF); + KMByteBlob.cast(data).add(index, (byte) (var1 ^ var2)); + index++; } + } + private short beginImportWrappedKeyCmd(APDU apdu){ + short cmd = KMArray.instance((short) 4); + short params = KMKeyParameters.expAny(); + KMArray.cast(cmd).add((short) 0, KMByteBlob.exp()); // Encrypted Transport Key + KMArray.cast(cmd).add((short) 1, KMByteBlob.exp()); // Wrapping Key KeyBlob + KMArray.cast(cmd).add((short) 2, KMByteBlob.exp()); // Masking Key + params = KMKeyParameters.exp(); + KMArray.cast(cmd).add((short) 3, params); // Wrapping key blob Params + return receiveIncoming(apdu, cmd); + } - // Step 4 - AES-GCM decrypt the wrapped key - data[INPUT_DATA] = KMArray.cast(args).get((short) 2); - data[AUTH_DATA] = KMArray.cast(args).get((short) 9); - data[AUTH_TAG] = KMArray.cast(args).get((short) 3); - data[NONCE] = KMArray.cast(args).get((short) 4); - Util.arrayFillNonAtomic( - scratchPad, (short) 0, KMByteBlob.cast(data[INPUT_DATA]).length(), (byte) 0); + private void processBeginImportWrappedKeyCmd(APDU apdu) { + // Receive the incoming request fully from the master into buffer. + short cmd = beginImportWrappedKeyCmd(apdu); + byte[] scratchPad = apdu.getBuffer(); + // Step -1 parse the wrapping key blob + // read wrapping key blob + short keyBlob = KMArray.cast(cmd).get((short) 1); + // read un wrapping key params + short wrappingKeyParameters = KMArray.cast(cmd).get((short) 3); + processWrappingKeyBlob(keyBlob, wrappingKeyParameters, scratchPad); + // Step 2 - decrypt the encrypted transport key - 32 bytes AES-GCM key + short transportKey = decryptTransportKey(data[SECRET], data[PUB_KEY], + KMArray.cast(cmd).get((short) 0), scratchPad); + // Step 3 - XOR the decrypted AES-GCM key with with masking key + unmask(transportKey, KMArray.cast(cmd).get((short) 2)); + if(isValidWrappingKey()){ + KMException.throwIt(KMError.UNKNOWN_ERROR); + } + setWrappingKey(transportKey); + sendError(apdu, KMError.OK); + } + private short aesGCMEncrypt(short aesSecret, short input, short nonce, short authData, short authTag,byte[] scratchPad){ + Util.arrayFillNonAtomic(scratchPad, (short) 0, KMByteBlob.cast(input).length(), (byte) 0); + short len = seProvider.aesGCMEncrypt( + KMByteBlob.cast(aesSecret).getBuffer(), + KMByteBlob.cast(aesSecret).getStartOff(), + KMByteBlob.cast(aesSecret).length(), + KMByteBlob.cast(input).getBuffer(), + KMByteBlob.cast(input).getStartOff(), + KMByteBlob.cast(input).length(), + scratchPad, + (short) 0, + KMByteBlob.cast(nonce).getBuffer(), + KMByteBlob.cast(nonce).getStartOff(), + KMByteBlob.cast(nonce).length(), + KMByteBlob.cast(authData).getBuffer(), + KMByteBlob.cast(authData).getStartOff(), + KMByteBlob.cast(authData).length(), + KMByteBlob.cast(authTag).getBuffer(), + KMByteBlob.cast(authTag).getStartOff(), + KMByteBlob.cast(authTag).length()); + return KMByteBlob.instance(scratchPad, (short) 0, len); + } + private short aesGCMDecrypt(short aesSecret, short input, short nonce, short authData, short authTag,byte[] scratchPad){ + Util.arrayFillNonAtomic(scratchPad, (short) 0, KMByteBlob.cast(input).length(), (byte) 0); if (!seProvider.aesGCMDecrypt( - KMByteBlob.cast(data[SECRET]).getBuffer(), - KMByteBlob.cast(data[SECRET]).getStartOff(), - KMByteBlob.cast(data[SECRET]).length(), - KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), - KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), - KMByteBlob.cast(data[INPUT_DATA]).length(), + KMByteBlob.cast(aesSecret).getBuffer(), + KMByteBlob.cast(aesSecret).getStartOff(), + KMByteBlob.cast(aesSecret).length(), + KMByteBlob.cast(input).getBuffer(), + KMByteBlob.cast(input).getStartOff(), + KMByteBlob.cast(input).length(), scratchPad, (short) 0, - KMByteBlob.cast(data[NONCE]).getBuffer(), - KMByteBlob.cast(data[NONCE]).getStartOff(), - KMByteBlob.cast(data[NONCE]).length(), - KMByteBlob.cast(data[AUTH_DATA]).getBuffer(), - KMByteBlob.cast(data[AUTH_DATA]).getStartOff(), - KMByteBlob.cast(data[AUTH_DATA]).length(), - KMByteBlob.cast(data[AUTH_TAG]).getBuffer(), - KMByteBlob.cast(data[AUTH_TAG]).getStartOff(), - KMByteBlob.cast(data[AUTH_TAG]).length())) { + KMByteBlob.cast(nonce).getBuffer(), + KMByteBlob.cast(nonce).getStartOff(), + KMByteBlob.cast(nonce).length(), + KMByteBlob.cast(authData).getBuffer(), + KMByteBlob.cast(authData).getStartOff(), + KMByteBlob.cast(authData).length(), + KMByteBlob.cast(authTag).getBuffer(), + KMByteBlob.cast(authTag).getStartOff(), + KMByteBlob.cast(authTag).length())) { KMException.throwIt(KMError.VERIFICATION_FAILED); } + return KMByteBlob.instance(scratchPad, (short) 0, KMByteBlob.cast(input).length()); + } + + private short finishImportWrappedKeyCmd(APDU apdu){ + short cmd = KMArray.instance((short) 8); + short params = KMKeyParameters.expAny(); + KMArray.cast(cmd).add((short) 0, params); // Key Params of wrapped key + KMArray.cast(cmd).add((short) 1, KMEnum.instance(KMType.KEY_FORMAT)); // Key Format + KMArray.cast(cmd).add((short) 2, KMByteBlob.exp()); // Wrapped Import Key Blob + KMArray.cast(cmd).add((short) 3, KMByteBlob.exp()); // Auth Tag + KMArray.cast(cmd).add((short) 4, KMByteBlob.exp()); // IV - Nonce + KMArray.cast(cmd) + .add((short) 5, KMByteBlob.exp()); // Wrapped Key ASSOCIATED AUTH DATA + KMArray.cast(cmd).add((short) 6, KMInteger.exp()); // Password Sid + KMArray.cast(cmd).add((short) 7, KMInteger.exp()); // Biometric Sid + return receiveIncoming(apdu, cmd); + } + //TODO remove cmd later on + private void processFinishImportWrappedKeyCmd(APDU apdu){ + short cmd = finishImportWrappedKeyCmd(apdu); + short keyParameters = KMArray.cast(cmd).get((short) 0); + short keyFmt = KMArray.cast(cmd).get((short) 1); + keyFmt = KMEnum.cast(keyFmt).getVal(); + validateImportKey(keyParameters, keyFmt); + byte[] scratchPad = apdu.getBuffer(); + // Step 4 - AES-GCM decrypt the wrapped key + data[INPUT_DATA] = KMArray.cast(cmd).get((short) 2); + data[AUTH_TAG] = KMArray.cast(cmd).get((short) 3); + data[NONCE] = KMArray.cast(cmd).get((short) 4); + data[AUTH_DATA] = KMArray.cast(cmd).get((short) 5); + + if(!isValidWrappingKey()){ + KMException.throwIt(KMError.UNKNOWN_ERROR); + } + data[IMPORTED_KEY_BLOB] = aesGCMDecrypt(getWrappingKey(),data[INPUT_DATA],data[NONCE],data[AUTH_DATA], data[AUTH_TAG],scratchPad); + resetWrappingKey(); // Step 5 - Import decrypted key data[ORIGIN] = KMType.SECURELY_IMPORTED; - data[KEY_PARAMETERS] = KMArray.cast(args).get((short) 0); + data[KEY_PARAMETERS] = keyParameters; // create key blob array - data[IMPORTED_KEY_BLOB] = KMArray.instance((short) 1); - // add the byte blob containing decrypted input data - KMArray.cast(data[IMPORTED_KEY_BLOB]) - .add( - (short) 0, - KMByteBlob.instance(scratchPad, (short) 0, KMByteBlob.cast(data[INPUT_DATA]).length())); - // encode the key blob - tmpVariables[0] = repository.alloc((short) (KMByteBlob.cast(data[INPUT_DATA]).length() + 16)); - tmpVariables[1] = - encoder.encode(data[IMPORTED_KEY_BLOB], repository.getHeap(), tmpVariables[0]); - data[IMPORTED_KEY_BLOB] = - KMByteBlob.instance(repository.getHeap(), tmpVariables[0], tmpVariables[1]); - importKey(apdu, scratchPad); + importKey(apdu, keyFmt, scratchPad); } - private void processAttestKeyCmd(APDU apdu) { - // Receive the incoming request fully from the master into buffer. - receiveIncoming(apdu); - - // Re-purpose the apdu buffer as scratch pad. - byte[] scratchPad = apdu.getBuffer(); + private KMAttestationCert makeCommonCert(byte[] scratchPad) { + short alg = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ALGORITHM, data[KEY_PARAMETERS]); + boolean rsaCert = KMEnumTag.cast(alg).getValue() == KMType.RSA; + KMAttestationCert cert = KMAttestationCertImpl.instance(rsaCert, seProvider); + + short subject = KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.CERTIFICATE_SUBJECT_NAME, data[KEY_PARAMETERS]); + + // If no subject name is specified then use the default subject name. + if(subject == KMType.INVALID_VALUE || KMByteTag.cast(subject).length() == 0){ + subject = KMByteBlob.instance(defaultSubject, (short) 0, (short) defaultSubject.length); + }else{ + subject = KMByteTag.cast(subject).getValue(); + } + cert.subjectName(subject); + // Validity period must be specified + short notBefore = KMKeyParameters.findTag(KMType.DATE_TAG, KMType.CERTIFICATE_NOT_BEFORE, data[KEY_PARAMETERS]); + if(notBefore == KMType.INVALID_VALUE){ + KMException.throwIt(KMError.MISSING_NOT_BEFORE); + } + notBefore = KMIntegerTag.cast(notBefore).getValue(); + short notAfter = KMKeyParameters.findTag(KMType.DATE_TAG, KMType.CERTIFICATE_NOT_AFTER, data[KEY_PARAMETERS]); + if(notAfter == KMType.INVALID_VALUE ){ + KMException.throwIt(KMError.MISSING_NOT_AFTER); + } + notAfter = KMIntegerTag.cast(notAfter).getValue(); + // VTS sends notBefore == Epoch. + Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 8, (byte) 0); + short epoch = KMInteger.instance(scratchPad, (short)0, (short)8); + short end = KMInteger.instance(dec319999Ms, (short)0, (short)dec319999Ms.length); + if(KMInteger.compare(notBefore, epoch) == 0){ + cert.notBefore(KMByteBlob.instance(jan01970, (short)0, (short)jan01970.length), + true, scratchPad); + }else { + cert.notBefore(notBefore, false, scratchPad); + } + // VTS sends notAfter == Dec 31st 9999 + if(KMInteger.compare(notAfter, end) == 0){ + cert.notAfter(KMByteBlob.instance(dec319999, (short)0, (short)dec319999.length), + true, scratchPad); + }else { + cert.notAfter(notAfter, false, scratchPad); + } + // Serial number + short serialNum = + KMKeyParameters.findTag(KMType.BIGNUM_TAG, KMType.CERTIFICATE_SERIAL_NUM, data[KEY_PARAMETERS]); + if (serialNum != KMType.INVALID_VALUE) { + serialNum = KMBignumTag.cast(serialNum).getValue(); + }else{ + serialNum= KMByteBlob.instance((short)1); + KMByteBlob.cast(serialNum).add((short)0, (byte)1); + } + cert.serialNumber(serialNum); + return cert; + } - // Arguments - short keyParams = KMKeyParameters.exp(); - short keyBlob = KMByteBlob.exp(); - short argsProto = KMArray.instance((short) 2); - KMArray.cast(argsProto).add((short) 0, keyBlob); - KMArray.cast(argsProto).add((short) 1, keyParams); - - // Decode the argument - short args = decoder.decode(argsProto, (byte[]) bufferRef[0], bufferProp[BUF_START_OFFSET], bufferProp[BUF_LEN_OFFSET]); - //reclaim memory - repository.reclaimMemory(bufferProp[BUF_LEN_OFFSET]); - - data[KEY_BLOB] = KMArray.cast(args).get((short) 0); - data[KEY_PARAMETERS] = KMArray.cast(args).get((short) 1); - - // parse key blob - parseEncryptedKeyBlob(scratchPad); - // 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]); - if (tmpVariables[0] != KMType.INVALID_VALUE) { - KMException.throwIt(KMError.CANNOT_ATTEST_IDS); - } - // The key which is being attested should be asymmetric i.e. RSA or EC - tmpVariables[0] = KMEnumTag.getValue(KMType.ALGORITHM, data[HW_PARAMETERS]); - if (tmpVariables[0] != KMType.RSA && tmpVariables[0] != KMType.EC) { - KMException.throwIt(KMError.INCOMPATIBLE_ALGORITHM); +//TODO remove hwParameters when this is refactored. + private KMAttestationCert makeAttestationCert(short attKeyBlob, short attKeyParam, + short attChallenge, short issuer, short hwParameters, short swParameters, byte[] scratchPad) { + KMAttestationCert cert = makeCommonCert(scratchPad); + + // App Id and App Data, + short appId = KMType.INVALID_VALUE; + short appData = KMType.INVALID_VALUE; + if(attKeyParam != KMType.INVALID_VALUE) { + appId = + KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.APPLICATION_ID, attKeyParam); + if (appId != KMTag.INVALID_VALUE) { + appId = KMByteTag.cast(appId).getValue(); + } + appData = + KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.APPLICATION_DATA, attKeyParam); + if (appData != KMTag.INVALID_VALUE) { + appData = KMByteTag.cast(appData).getValue(); + } } - boolean rsaCert = true; - if (tmpVariables[0] == KMType.EC) { - rsaCert = false; + //TODO remove following line + short origBlob = data[KEY_BLOB]; + short pubKey = data[PUB_KEY]; + short keyBlob = parseEncryptedKeyBlob(attKeyBlob, appId, appData, scratchPad); + short attestationKeySecret = KMArray.cast(keyBlob).get(KEY_BLOB_SECRET); + short attestParam = KMArray.cast(keyBlob).get(KEY_BLOB_PARAMS); + attestParam = KMKeyCharacteristics.cast(attestParam).getStrongboxEnforced(); + short attKeyPurpose = + KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.PURPOSE, attestParam); + // If the attest key's purpose is not "attest key" then error. + if (!KMEnumArrayTag.cast(attKeyPurpose).contains(KMType.ATTEST_KEY)) { + KMException.throwIt(KMError.INCOMPATIBLE_PURPOSE); } - KMAttestationCert cert = seProvider.getAttestationCert(rsaCert); - // Validate and add attestation ids. - addAttestationIds(cert); - // Save attestation application id - must be present. - tmpVariables[0] = - KMKeyParameters.findTag( - KMType.BYTES_TAG, KMType.ATTESTATION_APPLICATION_ID, data[KEY_PARAMETERS]); - if (tmpVariables[0] == KMType.INVALID_VALUE) { - KMException.throwIt(KMError.ATTESTATION_APPLICATION_ID_MISSING); + // If issuer is not present then it is an error + if (KMByteBlob.cast(issuer).length() <= 0) { + KMException.throwIt(KMError.MISSING_ISSUER_SUBJECT_NAME); } - cert.extensionTag(tmpVariables[0], false); - // Save attestation challenge - tmpVariables[0] = - KMKeyParameters.findTag( - KMType.BYTES_TAG, KMType.ATTESTATION_CHALLENGE, data[KEY_PARAMETERS]); - if (tmpVariables[0] == KMType.INVALID_VALUE) { - KMException.throwIt(KMError.INVALID_ARGUMENT); + short alg = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ALGORITHM, attestParam); + + if(KMEnumTag.cast(alg).getValue() == KMType.RSA) { + short attestationKeyPublic = KMArray.cast(keyBlob).get(KEY_BLOB_PUB_KEY); + cert.rsaAttestKey(attestationKeySecret, attestationKeyPublic, KMType.ATTESTATION_CERT); + }else{ + cert.ecAttestKey(attestationKeySecret, KMType.ATTESTATION_CERT); } - cert.attestationChallenge(KMByteTag.cast(tmpVariables[0]).getValue()); - // unique id byte blob - uses application id and temporal month count of creation time. - setUniqueId(cert, scratchPad); - - // validity period - // active time or creation time - byte blob - // current assumption is that if active and creation time are missing from characteristics - // then - // then it is an error. - tmpVariables[1] = - KMKeyParameters.findTag(KMType.DATE_TAG, KMType.ACTIVE_DATETIME, data[SW_PARAMETERS]); - if (tmpVariables[1] != KMType.INVALID_VALUE) { - tmpVariables[1] = KMIntegerTag.cast(tmpVariables[1]).getValue(); - } else { - tmpVariables[1] = - KMKeyParameters.findTag(KMType.DATE_TAG, KMType.CREATION_DATETIME, data[SW_PARAMETERS]); - if (tmpVariables[1] == KMType.INVALID_VALUE) { - KMException.throwIt(KMError.INVALID_KEY_BLOB); + cert.attestationChallenge(attChallenge); + cert.issuer(issuer); + //TODO remove following line + data[PUB_KEY] =pubKey; + cert.publicKey(data[PUB_KEY]); + + // Save attestation application id - must be present. + short attAppId = + KMKeyParameters.findTag( KMType.BYTES_TAG, KMType.ATTESTATION_APPLICATION_ID, data[KEY_PARAMETERS]); + if (attAppId == KMType.INVALID_VALUE) { + KMException.throwIt(KMError.ATTESTATION_APPLICATION_ID_MISSING); } - tmpVariables[1] = KMIntegerTag.cast(tmpVariables[1]).getValue(); - } - // convert milliseconds to UTC date. Start of validity period has to be UTC. - cert.notBefore(tmpVariables[1], scratchPad); - // expiry time - byte blob - tmpVariables[2] = - KMKeyParameters.findTag(KMType.DATE_TAG, KMType.USAGE_EXPIRE_DATETIME, data[SW_PARAMETERS]); - cert.notAfter(tmpVariables[2], repository.getCertExpiryTime(), scratchPad, (short) 0); + cert.extensionTag(attAppId, false); + // unique id byte blob - uses application id and temporal month count of creation time. + setUniqueId(cert, scratchPad); + // Add Attestation Ids if present + addAttestationIds(cert, scratchPad); + + // Add Tags + addTags(hwParameters, true, cert); + addTags(swParameters, false, cert); + // Add Device Boot locked status + cert.deviceLocked(seProvider.isDeviceBootLocked()); + // VB data + cert.verifiedBootHash(getVerifiedBootHash(scratchPad)); + cert.verifiedBootKey(getBootKey(scratchPad)); + cert.verifiedBootState((byte)seProvider.getBootState()); + + //TODO remove the following line + makeKeyCharacteristics(scratchPad); + data[KEY_BLOB] = origBlob; - addTags(KMKeyCharacteristics.cast(data[KEY_CHARACTERISTICS]).getHardwareEnforced(), true, cert); - addTags( - KMKeyCharacteristics.cast(data[KEY_CHARACTERISTICS]).getSoftwareEnforced(), false, cert); + return cert; + } - cert.deviceLocked(repository.getBootLoaderLock()); - cert.issuer(repository.getIssuer()); - cert.publicKey(data[PUB_KEY]); - cert.verifiedBootHash(repository.getVerifiedBootHash()); - - cert.verifiedBootKey(repository.getVerifiedBootKey()); - cert.verifiedBootState(repository.getBootState()); - // buffer for cert - we allocate 2KBytes buffer - // make this buffer size configurable - tmpVariables[3] = KMByteBlob.instance(MAX_CERT_SIZE); - bufferRef[0] = KMByteBlob.cast(tmpVariables[3]).getBuffer(); - bufferProp[BUF_START_OFFSET] = KMByteBlob.cast(tmpVariables[3]).getStartOff(); - bufferProp[BUF_LEN_OFFSET] = KMByteBlob.cast(tmpVariables[3]).length(); - cert.buffer((byte[]) bufferRef[0], bufferProp[BUF_START_OFFSET], bufferProp[BUF_LEN_OFFSET]); - cert.build(); - bufferProp[BUF_START_OFFSET] = - encoder.encodeCert((byte[]) bufferRef[0], bufferProp[BUF_START_OFFSET], cert.getCertStart(), cert.getCertLength(), - buildErrorStatus(KMError.OK)); - bufferProp[BUF_LEN_OFFSET] = (short) (cert.getCertLength() + (cert.getCertStart() - bufferProp[BUF_START_OFFSET])); - sendOutgoing(apdu); + private KMAttestationCert makeSelfSignedCert(short attPrivKey, short attPubKey, byte[] scratchPad) { + KMAttestationCert cert = makeCommonCert(scratchPad); + short alg = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ALGORITHM, data[KEY_PARAMETERS]); + byte mode = KMType.FAKE_CERT; + if(attPrivKey != KMType.INVALID_VALUE){ + mode = KMType.SELF_SIGNED_CERT; + } + short subject = KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.CERTIFICATE_SUBJECT_NAME, data[KEY_PARAMETERS]); + // If no subject name is specified then use the default subject name. + if(subject == KMType.INVALID_VALUE || KMByteTag.cast(subject).length() == 0){ + subject = KMByteBlob.instance(defaultSubject, (short) 0, (short) defaultSubject.length); + }else{ + subject = KMByteTag.cast(subject).getValue(); + } + + if(KMEnumTag.cast(alg).getValue() == KMType.RSA) { + cert.rsaAttestKey(attPrivKey, attPubKey, mode); + }else{ + cert.ecAttestKey(attPrivKey, mode); + } +// cert.attestKey(attPrivKey, rsaAttest, KMType.SELF_SIGNED_CERT); +// cert.attestKey(KMType.INVALID_VALUE, rsaAttest, KMType.FAKE_CERT); + cert.issuer(subject); + cert.subjectName(subject); + cert.publicKey(attPubKey); + return cert; } - private boolean isEmpty(byte[] buf, short offset, short len) { - boolean empty = true; - short index = 0; - while (index < len) { - if (buf[index] != 0) { - empty = false; - break; - } - index++; + // private KMAttestationCert makeFakeCert(byte[] scratchPad) { + // KMAttestationCert cert = makeCommonCert(scratchPad); + // short alg = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ALGORITHM, data[KEY_PARAMETERS]); + // boolean rsaAttest = KMEnumTag.cast(alg).getValue() == KMType.RSA; + // + // // If the generated/imported key can sign then mode is self signed + // short subject = KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.CERTIFICATE_SUBJECT_NAME, data[KEY_PARAMETERS]); + // // If no subject name is specified then use the default subject name. + // if(subject == KMType.INVALID_VALUE || KMByteTag.cast(subject).length() == 0){ + // subject = KMByteBlob.instance(defaultSubject, (short) 0, (short) defaultSubject.length); + // }else{ + // subject = KMByteTag.cast(subject).getValue(); + // } + // cert.attestKey(KMType.INVALID_VALUE, rsaAttest,KMType.FAKE_CERT); + // cert.issuer(subject); + // cert.subjectName(subject); + // cert.publicKey(data[PUB_KEY]); + // return cert; + // } + + /** + * 1) If attestation key is present and attestation challenge is absent then it is an error. + * 2) If attestation key is absent and attestation challenge is present then it is an error as + * factory provisioned attestation key is not supported. + * 3) If both are present and issuer is absent or attest key purpose is not ATTEST_KEY then it is an error. + * 4) If the generated/imported keys are RSA or EC then validity period must be specified. + * Device Unique Attestation is not supported. + */ + // private KMAttestationCert makeCert(short attKeyBlob, short attKeyParam, byte[] scratchPad) { + // short appId = KMType.INVALID_VALUE; + // short appData = KMType.INVALID_VALUE; + // if(attKeyParam != KMType.INVALID_VALUE) { + // appId = + // KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.APPLICATION_ID, attKeyParam); + // if (appId != KMTag.INVALID_VALUE) { + // appId = KMByteTag.cast(appId).getValue(); + // } + // appData = + // KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.APPLICATION_DATA, attKeyParam); + // if (appData != KMTag.INVALID_VALUE) { + // appData = KMByteTag.cast(appData).getValue(); + // } + // } + // byte mode = KMType.FAKE_CERT; + // boolean rsaCert = true; + // short attestationKey = KMType.INVALID_VALUE; + // short attestParam = KMType.INVALID_VALUE; + // boolean rsaAttest = true; + // // No attestation required for symmetric keys + // short alg = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ALGORITHM, data[KEY_PARAMETERS]); + // if(KMEnumTag.cast(alg).getValue() != KMType.RSA && + // KMEnumTag.cast(alg).getValue() != KMType.EC){ + // return null; + // } + // // Device unique attestation not supported + // short deviceUniqueAttest = KMKeyParameters.findTag(KMType.BOOL_TAG, + // KMType.DEVICE_UNIQUE_ATTESTATION, data[KEY_PARAMETERS]); + // if(deviceUniqueAttest != KMType.INVALID_VALUE){ + // KMException.throwIt(KMError.CANNOT_ATTEST_IDS); + // } + // if(KMEnumTag.cast(alg).getValue() == KMType.EC) { + // rsaCert = false; + // rsaAttest = false; + // } + // KMAttestationCert cert = seProvider.getAttestationCert(rsaCert); + // // Read attestation challenge if present + // short attChallenge = + // KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.ATTESTATION_CHALLENGE, data[KEY_PARAMETERS]); + // if(attChallenge != KMType.INVALID_VALUE){ + // attChallenge = KMByteTag.cast(attChallenge).getValue(); + // } + // short subject = KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.CERTIFICATE_SUBJECT_NAME, data[KEY_PARAMETERS]); + // // If no subject name is specified then use the default subject name. + // if(subject == KMType.INVALID_VALUE || KMByteTag.cast(subject).length() == 0){ + // subject = KMByteBlob.instance(defaultSubject, (short) 0, (short) defaultSubject.length); + // }else{ + // subject = KMByteTag.cast(subject).getValue(); + // } + // // If attestation key is given by the caller + // if(attKeyBlob != KMType.INVALID_VALUE && KMByteBlob.cast(attKeyBlob).length() > 0) { + // short keyBlob = KMType.INVALID_VALUE; + // keyBlob = parseEncryptedKeyBlob(attKeyBlob, appId, appData, scratchPad); + // + // // If no attestation challenge present then it is an error + // if (attChallenge == KMType.INVALID_VALUE || KMByteBlob.cast(attChallenge).length() <= 0) { + // KMException.throwIt(KMError.INVALID_ARGUMENT); + // } + // mode = KMType.ATTESTATION_CERT; + // attestationKey = KMArray.cast(keyBlob).get(KEY_BLOB_SECRET); + // attestParam = KMArray.cast(keyBlob).get(KEY_BLOB_PARAMS); + // attestParam = KMKeyCharacteristics.cast(attestParam).getStrongboxEnforced(); + // alg = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ALGORITHM, attestParam); + // if(KMEnumTag.cast(alg).getValue() == KMType.EC){ + // rsaAttest = false; + // } + // // If issuer is not present then it is an error + // if (KMByteBlob.cast(data[ATTEST_KEY_ISSUER]).length() <= 0) { + // KMException.throwIt(KMError.MISSING_ISSUER_SUBJECT_NAME); + // } + // // TODO KeyMint should parse issuer subject name to validate that it is valid der encoded + // // string. + // // Currently not done as X509 parsing not yet supported. + // + // short attKeyPurpose = + // KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.PURPOSE, attestParam); + // // If the attest key's purpose is not "attest key" then error. + // if (!KMEnumArrayTag.cast(attKeyPurpose).contains(KMType.ATTEST_KEY)) { + // KMException.throwIt(KMError.INCOMPATIBLE_PURPOSE); + // } + // }else if (attChallenge != KMType.INVALID_VALUE && KMByteBlob.cast(attChallenge).length() > 0) { + // // If the challenge is present and attest key is absent then it is an error. + // // Note: The keymint applet does not support factory provisioned attestation keys. + // KMException.throwIt(KMError.ATTESTATION_KEYS_NOT_PROVISIONED); + // }else { + // // Both attestation key and challenge are are absent. + // short purpose = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.PURPOSE, data[KEY_PARAMETERS]); + // if(purpose != KMType.INVALID_VALUE && KMEnumArrayTag.cast(purpose).contains(KMType.SIGN)) { + // // If the generated/imported key can sign then mode is self signed + // mode = KMType.SELF_SIGNED_CERT; + // attestationKey = data[SECRET]; + // attestParam = data[KEY_PARAMETERS]; + // data[ATTEST_KEY_ISSUER] = subject; + // }else{ + // // else a fake cert i.e. certificated without signature must be generated + // mode = KMType.FAKE_CERT; + // attestationKey = KMType.INVALID_VALUE; + // data[ATTEST_KEY_ISSUER] = subject; + // } + // } + // cert.attestKey(attestationKey, rsaAttest,mode); + // cert.attestationChallenge(attChallenge); + // cert.issuer(data[ATTEST_KEY_ISSUER]); + // cert.subjectName(subject); + // + // // Validity period must be specified + // short notBefore = KMKeyParameters.findTag(KMType.DATE_TAG, KMType.CERTIFICATE_NOT_BEFORE, data[KEY_PARAMETERS]); + // if(notBefore == KMType.INVALID_VALUE){ + // KMException.throwIt(KMError.MISSING_NOT_BEFORE); + // } + // notBefore = KMIntegerTag.cast(notBefore).getValue(); + // short notAfter = KMKeyParameters.findTag(KMType.DATE_TAG, KMType.CERTIFICATE_NOT_AFTER, data[KEY_PARAMETERS]); + // if(notAfter == KMType.INVALID_VALUE ){ + // KMException.throwIt(KMError.MISSING_NOT_AFTER); + // } + // + // notAfter = KMIntegerTag.cast(notAfter).getValue(); + // // VTS sends notBefore == Epoch. + // Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 8, (byte) 0); + // short epoch = KMInteger.instance(scratchPad, (short)0, (short)8); + // short end = KMInteger.instance(dec319999Ms, (short)0, (short)dec319999Ms.length); + // if(KMInteger.compare(notBefore, epoch) == 0){ + // cert.notBefore(KMByteBlob.instance(jan01970, (short)0, (short)jan01970.length), + // true, scratchPad); + // }else { + // cert.notBefore(notBefore, false, scratchPad); + // } + // // VTS sends notAfter == Dec 31st 9999 + // if(KMInteger.compare(notAfter, end) == 0){ + // cert.notAfter(KMByteBlob.instance(dec319999, (short)0, (short)dec319999.length), + // true, scratchPad); + // }else { + // cert.notAfter(notAfter, false, scratchPad); + // } + // // Serial number + // short serialNum = + // KMKeyParameters.findTag(KMType.BIGNUM_TAG, KMType.CERTIFICATE_SERIAL_NUM, data[KEY_PARAMETERS]); + // if (serialNum != KMType.INVALID_VALUE) { + // serialNum = KMBignumTag.cast(serialNum).getValue(); + // }else{ + // serialNum= KMByteBlob.instance((short)1); + // KMByteBlob.cast(serialNum).add((short)0, (byte)1); + // } + // cert.serialNumber(serialNum); + // + // //Extension + // if(mode == KMType.ATTESTATION_CERT){ + // // Save attestation application id - must be present. + // short attAppId = + // KMKeyParameters.findTag( KMType.BYTES_TAG, KMType.ATTESTATION_APPLICATION_ID, data[KEY_PARAMETERS]); + // if (attAppId == KMType.INVALID_VALUE) { + // KMException.throwIt(KMError.ATTESTATION_APPLICATION_ID_MISSING); + // } + // cert.extensionTag(attAppId, false); + // // unique id byte blob - uses application id and temporal month count of creation time. + // setUniqueId(cert, scratchPad); + // // Add Attestation Ids if present + // addAttestationIds(cert, scratchPad); + // // Add Tags + // addTags(data[HW_PARAMETERS], true, cert); + // addTags(data[SW_PARAMETERS], false, cert); + // // Add Device Boot locked status + // cert.deviceLocked(seProvider.isDeviceBootLocked()); + // // VB data + // cert.verifiedBootHash(getVerifiedBootHash(scratchPad)); + // cert.verifiedBootKey(getBootKey(scratchPad)); + // cert.verifiedBootState((byte)seProvider.getBootState()); + // } + // + // // Public key + // cert.publicKey(data[PUB_KEY]); + // return cert; + // } + + protected short getBootKey(byte[] scratchPad){ + Util.arrayFillNonAtomic(scratchPad, (short)0, VERIFIED_BOOT_KEY_SIZE, (byte)0); + short len = seProvider.getVerifiedBootHash(scratchPad,(short)0); + if(len != VERIFIED_BOOT_KEY_SIZE) { + KMException.throwIt(KMError.UNKNOWN_ERROR); } - return empty; + return KMByteBlob.instance(scratchPad,(short)0, VERIFIED_BOOT_KEY_SIZE); } + protected short getVerifiedBootHash(byte[] scratchPad){ + Util.arrayFillNonAtomic(scratchPad, (short)0, VERIFIED_BOOT_HASH_SIZE, (byte)0); + short len = seProvider.getVerifiedBootHash(scratchPad,(short)0); + if(len != VERIFIED_BOOT_HASH_SIZE) { + KMException.throwIt(KMError.UNKNOWN_ERROR); + } + return KMByteBlob.instance(scratchPad,(short)0, VERIFIED_BOOT_HASH_SIZE); + } // -------------------------------- // Only add the Attestation ids which are requested in the attestation parameters. // If the requested attestation ids are not provisioned or deleted then // throw CANNOT_ATTEST_IDS error. If there is mismatch in the attestation // id values of both the requested parameters and the provisioned parameters // then throw INVALID_TAG error. - private void addAttestationIds(KMAttestationCert cert) { + private void addAttestationIds(KMAttestationCert cert, byte[] scratchPad) { final short[] attTags = new short[]{ KMType.ATTESTATION_ID_BRAND, @@ -1547,36 +1594,33 @@ private void addAttestationIds(KMAttestationCert cert) { byte index = 0; short attIdTag; short attIdTagValue; - short storedAttId; + short storedAttIdLen; while (index < (short) attTags.length) { attIdTag = KMKeyParameters.findTag(KMType.BYTES_TAG, attTags[index], data[KEY_PARAMETERS]); if (attIdTag != KMType.INVALID_VALUE) { attIdTagValue = KMByteTag.cast(attIdTag).getValue(); - storedAttId = repository.getAttId(mapToAttId(attTags[index])); + storedAttIdLen = seProvider.getAttestationId(attTags[index],scratchPad, (short)0); // Return CANNOT_ATTEST_IDS if Attestation IDs are not provisioned or // Attestation IDs are deleted. - if (storedAttId == 0 || - isEmpty(KMByteBlob.cast(storedAttId).getBuffer(), - KMByteBlob.cast(storedAttId).getStartOff(), - KMByteBlob.cast(storedAttId).length())) { + if (storedAttIdLen == 0) { KMException.throwIt(KMError.CANNOT_ATTEST_IDS); } // Return INVALID_TAG if Attestation IDs does not match. - if ((KMByteBlob.cast(storedAttId).length() != KMByteBlob.cast(attIdTagValue).length()) || - (0 != Util.arrayCompare(KMByteBlob.cast(storedAttId).getBuffer(), - KMByteBlob.cast(storedAttId).getStartOff(), - KMByteBlob.cast(attIdTagValue).getBuffer(), - KMByteBlob.cast(attIdTagValue).getStartOff(), - KMByteBlob.cast(storedAttId).length()))) { + if ((storedAttIdLen != KMByteBlob.cast(attIdTagValue).length()) || + (0 != Util.arrayCompare(scratchPad, (short) 0, + KMByteBlob.cast(attIdTagValue).getBuffer(), + KMByteBlob.cast(attIdTagValue).getStartOff(), + storedAttIdLen))) { KMException.throwIt(KMError.INVALID_TAG); } - cert.extensionTag(attIdTag, true); + short blob = KMByteBlob.instance(scratchPad, (short)0, storedAttIdLen); + cert.extensionTag(KMByteTag.instance(attTags[index], blob), true); } index++; } } - private void addTags(short params, boolean hwEnforced, KMAttestationCert cert) { + private static void addTags(short params, boolean hwEnforced, KMAttestationCert cert) { short index = 0; short arr = KMKeyParameters.cast(params).getVals(); short len = KMArray.cast(arr).length(); @@ -1588,52 +1632,39 @@ private void addTags(short params, boolean hwEnforced, KMAttestationCert cert) { } } - 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) { + private static void setUniqueId(KMAttestationCert cert, byte[] scratchPad) { + if(!KMTag.isPresent(data[KEY_PARAMETERS],KMType.BOOL_TAG, KMType.INCLUDE_UNIQUE_ID)){ return; } - // temporal count T - tmpVariables[0] = KMKeyParameters.findTag(KMType.DATE_TAG, - KMType.CREATION_DATETIME, data[SW_PARAMETERS]); - if (tmpVariables[0] == KMType.INVALID_VALUE) { + short time = KMKeyParameters.findTag(KMType.DATE_TAG, KMType.CREATION_DATETIME, data[KEY_PARAMETERS]); + if(time == KMType.INVALID_VALUE){ KMException.throwIt(KMError.INVALID_TAG); } - tmpVariables[0] = KMIntegerTag.cast(tmpVariables[0]).getValue(); + time = KMIntegerTag.cast(time).getValue(); // Application Id C - tmpVariables[1] = KMKeyParameters.findTag(KMType.BYTES_TAG, - KMType.ATTESTATION_APPLICATION_ID, data[KEY_PARAMETERS]); - if (tmpVariables[1] == KMType.INVALID_VALUE) { + short appId = KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.ATTESTATION_APPLICATION_ID, data[KEY_PARAMETERS]); + if (appId == KMType.INVALID_VALUE) { KMException.throwIt(KMError.ATTESTATION_APPLICATION_ID_MISSING); } - tmpVariables[1] = KMByteTag.cast(tmpVariables[1]).getValue(); + appId = KMByteTag.cast(appId).getValue(); // 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) { + if(KMTag.isPresent(data[KEY_PARAMETERS], KMType.BOOL_TAG, KMType.RESET_SINCE_ID_ROTATION)){ resetAfterRotation = 0x01; } - cert.makeUniqueId( - scratchPad, - (short) 0, - 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, + cert.makeUniqueId(scratchPad, (short) 0, KMInteger.cast(time).getBuffer(), + KMInteger.cast(time).getStartOff(), KMInteger.cast(time).length(), + KMByteBlob.cast(appId).getBuffer(), KMByteBlob.cast(appId).getStartOff(), KMByteBlob.cast(appId).length(), resetAfterRotation, seProvider.getMasterKey()); } private void processDestroyAttIdsCmd(APDU apdu) { - repository.deleteAttIds(); + seProvider.deleteAttestationIds(); sendError(apdu, KMError.OK); } @@ -1641,50 +1672,46 @@ private void processVerifyAuthorizationCmd(APDU apdu) { sendError(apdu, KMError.UNIMPLEMENTED); } + private short abortOperationCmd(APDU apdu){ + short cmd = KMArray.instance((short) 1); + KMArray.cast(cmd).add((short) 0, KMInteger.exp()); + return receiveIncoming(apdu, cmd); + } + private void processAbortOperationCmd(APDU apdu) { - receiveIncoming(apdu); - tmpVariables[1] = KMArray.instance((short) 1); - KMArray.cast(tmpVariables[1]).add((short) 0, KMInteger.exp()); - tmpVariables[2] = decoder.decode(tmpVariables[1], (byte[]) bufferRef[0], bufferProp[BUF_START_OFFSET], bufferProp[BUF_LEN_OFFSET]); - //reclaim memory - repository.reclaimMemory(bufferProp[BUF_LEN_OFFSET]); - - data[OP_HANDLE] = KMArray.cast(tmpVariables[2]).get((short) 0); - KMOperationState op = repository.findOperation(data[OP_HANDLE]); + short cmd = abortOperationCmd(apdu); + data[OP_HANDLE] = KMArray.cast(cmd).get((short) 0); + KMOperationState op = findOperation(data[OP_HANDLE]); if (op == null) { - KMException.throwIt(KMError.INVALID_OPERATION_HANDLE); + sendError(apdu,KMError.INVALID_OPERATION_HANDLE); + }else { + releaseOperation(op); + sendError(apdu, KMError.OK); } - repository.releaseOperation(op); - sendError(apdu, KMError.OK); + } + + private short finishOperationCmd(APDU apdu){ + short cmd = KMArray.instance((short) 5); + KMArray.cast(cmd).add((short) 0, KMInteger.exp());//op handle + KMArray.cast(cmd).add((short) 1, KMByteBlob.exp());// input data + KMArray.cast(cmd).add((short) 2, KMByteBlob.exp()); // signature + short authToken = KMHardwareAuthToken.exp(); + KMArray.cast(cmd).add((short) 3, authToken); // auth token + short verToken = KMVerificationToken.exp(); + KMArray.cast(cmd).add((short) 4, verToken); // time stamp token + return receiveIncoming(apdu, cmd); } private void processFinishOperationCmd(APDU apdu) { - receiveIncoming(apdu); + short cmd = finishOperationCmd(apdu); byte[] scratchPad = apdu.getBuffer(); - tmpVariables[1] = KMArray.instance((short) 6); - // Arguments - tmpVariables[2] = KMKeyParameters.exp(); - KMArray.cast(tmpVariables[1]).add((short) 0, KMInteger.exp()); - KMArray.cast(tmpVariables[1]).add((short) 1, tmpVariables[2]); - KMArray.cast(tmpVariables[1]).add((short) 2, KMByteBlob.exp()); - KMArray.cast(tmpVariables[1]).add((short) 3, KMByteBlob.exp()); - tmpVariables[3] = KMHardwareAuthToken.exp(); - KMArray.cast(tmpVariables[1]).add((short) 4, tmpVariables[3]); - tmpVariables[4] = KMVerificationToken.exp(); - KMArray.cast(tmpVariables[1]).add((short) 5, tmpVariables[4]); - // Decode the arguments - tmpVariables[2] = decoder.decode(tmpVariables[1], (byte[]) bufferRef[0], bufferProp[BUF_START_OFFSET], bufferProp[BUF_LEN_OFFSET]); - //reclaim memory - repository.reclaimMemory(bufferProp[BUF_LEN_OFFSET]); - - 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); - data[SIGNATURE] = KMArray.cast(tmpVariables[2]).get((short) 3); - data[HW_TOKEN] = KMArray.cast(tmpVariables[2]).get((short) 4); - data[VERIFICATION_TOKEN] = KMArray.cast(tmpVariables[2]).get((short) 5); + data[OP_HANDLE] = KMArray.cast(cmd).get((short) 0); + data[INPUT_DATA] = KMArray.cast(cmd).get((short) 1); + data[SIGNATURE] = KMArray.cast(cmd).get((short) 2); + data[HW_TOKEN] = KMArray.cast(cmd).get((short) 3); + data[VERIFICATION_TOKEN] = KMArray.cast(cmd).get((short) 4); // Check Operation Handle - KMOperationState op = repository.findOperation(data[OP_HANDLE]); + KMOperationState op = findOperation(data[OP_HANDLE]); if (op == null) { KMException.throwIt(KMError.INVALID_OPERATION_HANDLE); } @@ -1701,70 +1728,28 @@ private void processFinishOperationCmd(APDU apdu) { case KMType.DECRYPT: finishDecryptOperation(op, scratchPad); break; + case KMType.AGREE_KEY: + finishKeyAgreementOperation(op, scratchPad); + break; } - // Remove the operation handle - repository.releaseOperation(op); - // make response - tmpVariables[1] = KMArray.instance((short) 0); - tmpVariables[1] = KMKeyParameters.instance(tmpVariables[1]); - tmpVariables[2] = KMArray.instance((short) 3); if (data[OUTPUT_DATA] == KMType.INVALID_VALUE) { data[OUTPUT_DATA] = KMByteBlob.instance((short) 0); } - KMArray.cast(tmpVariables[2]).add((short) 0, buildErrorStatus(KMError.OK)); - KMArray.cast(tmpVariables[2]).add((short) 1, tmpVariables[1]); - KMArray.cast(tmpVariables[2]).add((short) 2, data[OUTPUT_DATA]); + // Remove the operation handle + releaseOperation(op); - bufferProp[BUF_START_OFFSET] = repository.allocAvailableMemory(); - // Encode the response - bufferProp[BUF_LEN_OFFSET] = encoder.encode(tmpVariables[2], (byte[]) bufferRef[0], bufferProp[BUF_START_OFFSET]); - sendOutgoing(apdu); + // make response + short resp = KMArray.instance((short) 2); + KMArray.cast(resp).add((short) 0, KMInteger.uint_16(KMError.OK)); + KMArray.cast(resp).add((short) 1, data[OUTPUT_DATA]); + sendOutgoing(apdu, resp); } private void finishEncryptOperation(KMOperationState op, byte[] scratchPad) { - short len = KMByteBlob.cast(data[INPUT_DATA]).length(); - switch (op.getAlgorithm()) { - case KMType.AES: - case KMType.DES: - if (op.getAlgorithm() == KMType.AES) { - tmpVariables[0] = AES_BLOCK_SIZE; - } else { - tmpVariables[0] = DES_BLOCK_SIZE; - } - // If no padding then data length must be block aligned - if ((op.getBlockMode() == KMType.ECB || op.getBlockMode() == KMType.CBC) - && op.getPadding() == KMType.PADDING_NONE - && ((short) (len % tmpVariables[0]) != 0)) { - KMException.throwIt(KMError.INVALID_INPUT_LENGTH); - } else if (op.getBlockMode() == KMType.GCM) { - // update aad if there is any - updateAAD(op, (byte) 0x01); - // Get the output size - len = op.getOperation().getAESGCMOutputSize(len, (short) (op.getMacLength() / 8)); - data[OUTPUT_DATA] = KMByteBlob.instance(len); - } - // If padding i.e. pkcs7 then add padding to right - // Output data can at most one block size more the input data in case of pkcs7 encryption - tmpVariables[0] = KMByteBlob.instance((short) (len + tmpVariables[0])); - len = - op.getOperation() - .finish( - KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), - KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), - KMByteBlob.cast(data[INPUT_DATA]).length(), - KMByteBlob.cast(tmpVariables[0]).getBuffer(), - KMByteBlob.cast(tmpVariables[0]).getStartOff()); - - data[OUTPUT_DATA] = - KMByteBlob.instance( - KMByteBlob.cast(tmpVariables[0]).getBuffer(), - KMByteBlob.cast(tmpVariables[0]).getStartOff(), - len); - break; - default: - KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM); - break; + if(op.getAlgorithm() != KMType.AES && op.getAlgorithm() != KMType.DES){ + KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM); } + finishAesDesOperation(op); } private void finishDecryptOperation(KMOperationState op, byte[] scratchPad) { @@ -1777,86 +1762,86 @@ private void finishDecryptOperation(KMOperationState op, byte[] scratchPad) { KMException.throwIt(KMError.INVALID_INPUT_LENGTH); } len = - op.getOperation() - .finish( - KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), - KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), - len, - scratchPad, - (short) 0); + op.getOperation().finish( + KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), + len, scratchPad, (short) 0); data[OUTPUT_DATA] = KMByteBlob.instance(scratchPad, (short) 0, len); break; case KMType.AES: case KMType.DES: - if (op.getAlgorithm() == KMType.AES) { - tmpVariables[0] = AES_BLOCK_SIZE; - } else { - tmpVariables[0] = DES_BLOCK_SIZE; - } - tmpVariables[1] = repository.alloc(len); - if ((op.getBlockMode() == KMType.CBC || op.getBlockMode() == KMType.ECB) - && len > 0 - && (len % tmpVariables[0]) != 0) { - KMException.throwIt(KMError.INVALID_INPUT_LENGTH); - } else if (op.getBlockMode() == KMType.GCM) { - // update aad if there is any - updateAAD(op, (byte) 0x01); + if(op.getBlockMode() == KMType.GCM && (len < (short) (op.getMacLength() / 8))){ // Check if there is at least MAC Length bytes of input data - if ((len < (short) (op.getMacLength() / 8))) { KMException.throwIt(KMError.INVALID_INPUT_LENGTH); - } - // Get the output size - in case of JCardSim this will more then input size - tmpVariables[0] = - op.getOperation().getAESGCMOutputSize(len, (short) (op.getMacLength() / 8)); - tmpVariables[1] = repository.alloc(tmpVariables[0]); } - byte[] heap = repository.getHeap(); - len = - op.getOperation() - .finish( - KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), - KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), - len, - heap, - tmpVariables[1]); - - data[OUTPUT_DATA] = KMByteBlob.instance(heap, tmpVariables[1], len); + finishAesDesOperation(op); break; } } - // update operation should send 0x00 for finish variable, where as finish operation - // should send 0x01 for finish variable. - private void updateAAD(KMOperationState op, byte finish) { - // Is input data absent - if (data[INPUT_DATA] == KMType.INVALID_VALUE) { - KMException.throwIt(KMError.INVALID_ARGUMENT); + private void finishAesDesOperation(KMOperationState op){ + short len = KMByteBlob.cast(data[INPUT_DATA]).length(); + short blockSize = AES_BLOCK_SIZE; + if (op.getAlgorithm() == KMType.DES) { + blockSize= DES_BLOCK_SIZE; } - // Update can be called either to update auth data, update input data or both. - // But if it is called for neither then return error. - tmpVariables[0] = KMByteBlob.cast(data[INPUT_DATA]).length(); - tmpVariables[1] = - KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.ASSOCIATED_DATA, data[KEY_PARAMETERS]); - // For Finish operation the input data can be zero length and associated data can be - // INVALID_VALUE - // For update operation either input data or associated data should be present. - if (tmpVariables[1] == KMType.INVALID_VALUE && tmpVariables[0] <= 0 && finish == 0x00) { + + // If no padding then data length must be block aligned + if(op.getPadding() == KMType.PADDING_NONE && op.getPurpose() == KMType.ENCRYPT + && (op.getBlockMode() == KMType.ECB || op.getBlockMode() == KMType.CBC) + && ((short) (len % blockSize) != 0)){ KMException.throwIt(KMError.INVALID_INPUT_LENGTH); } - // Check if associated data is present and update aad still allowed by the operation. - if (tmpVariables[1] != KMType.INVALID_VALUE) { - if (!op.isAesGcmUpdateAllowed()) { - KMException.throwIt(KMError.INVALID_TAG); + + if(op.getPurpose() == KMType.DECRYPT && len > 0 + && (op.getBlockMode() == KMType.ECB || op.getBlockMode() == KMType.CBC) + && ((short) (len % blockSize) != 0)){ + KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + } + + if (op.getBlockMode() == KMType.GCM) { + if(op.isAesGcmUpdateAllowed()){ + op.setAesGcmUpdateComplete(); } - // If allowed the update the aad - tmpVariables[1] = KMByteTag.cast(tmpVariables[1]).getValue(); + //Get output size as it can be more then input size in case of GCM + len = op.getOperation().getAESGCMOutputSize(len, (short) (op.getMacLength() / 8)); + } + // If padding i.e. pkcs7 then add padding to right + // Output data can at most one block size more the input data in case of pkcs7 encryption + // In case of gcm we will allocate extra memory of the size equal to blocksize. + short outData = KMByteBlob.instance((short) (len + blockSize)); + len = + op.getOperation() + .finish( + KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), + KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), + KMByteBlob.cast(data[INPUT_DATA]).length(), + KMByteBlob.cast(outData).getBuffer(), + KMByteBlob.cast(outData).getStartOff()); + KMByteBlob.cast(outData).setLength(len); + data[OUTPUT_DATA] = outData; + } - op.getOperation() - .updateAAD( - KMByteBlob.cast(tmpVariables[1]).getBuffer(), - KMByteBlob.cast(tmpVariables[1]).getStartOff(), - KMByteBlob.cast(tmpVariables[1]).length()); + private void finishKeyAgreementOperation(KMOperationState op, byte[] scratchPad) { + try { + KMPKCS8Decoder pkcs8 = KMPKCS8Decoder.instance(); + short blob = pkcs8.decodeEcSubjectPublicKeyInfo(data[INPUT_DATA]); + short len = op.getOperation().finish( + KMByteBlob.cast(blob).getBuffer(), + KMByteBlob.cast(blob).getStartOff(), + KMByteBlob.cast(blob).length(), + scratchPad, + (short) 0 + ); + data[OUTPUT_DATA] = KMByteBlob.instance((short) 32); + Util.arrayCopyNonAtomic( + scratchPad, + (short) 0, + KMByteBlob.cast(data[OUTPUT_DATA]).getBuffer(), + KMByteBlob.cast(data[OUTPUT_DATA]).getStartOff(), + len); + } catch (CryptoException e) { + KMException.throwIt(KMError.INVALID_ARGUMENT); } } @@ -1867,20 +1852,20 @@ private void finishSigningVerifyingOperation(KMOperationState op, byte[] scratch // If there is no padding we can treat signing as a RSA decryption operation. try { if (op.getPurpose() == KMType.SIGN) { - // len of signature will be 256 bytes + // len of signature will be 256 bytes - but it can be less then 256 bytes short len = op.getOperation().sign( - KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), - KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), - KMByteBlob.cast(data[INPUT_DATA]).length(), scratchPad, - (short) 0); - // Maximum output size of signature is 256 bytes. + KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), + KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), + KMByteBlob.cast(data[INPUT_DATA]).length(), scratchPad, + (short) 0); + // Maximum output size of signature is 256 bytes. - the signature will always be positive data[OUTPUT_DATA] = KMByteBlob.instance((short) 256); Util.arrayCopyNonAtomic( - scratchPad, - (short) 0, - KMByteBlob.cast(data[OUTPUT_DATA]).getBuffer(), - (short) (KMByteBlob.cast(data[OUTPUT_DATA]).getStartOff() + 256 - len), - len); + scratchPad, + (short) 0, + KMByteBlob.cast(data[OUTPUT_DATA]).getBuffer(), + (short) (KMByteBlob.cast(data[OUTPUT_DATA]).getStartOff() + 256 - len), + len); } else { KMException.throwIt(KMError.UNSUPPORTED_PURPOSE); } @@ -1928,20 +1913,20 @@ private void finishSigningVerifyingOperation(KMOperationState op, byte[] scratch KMByteBlob.cast(data[INPUT_DATA]).length(), scratchPad, (short) 0); - - // Copy only signature of mac length size. - data[OUTPUT_DATA] = - KMByteBlob.instance(scratchPad, (short) 0, (short) (op.getMacLength() / 8)); - if (op.getPurpose() == KMType.VERIFY) { + if(op.getPurpose() == KMType.SIGN) { + // Copy only signature of mac length size. + data[OUTPUT_DATA] = + KMByteBlob.instance(scratchPad, (short) 0, (short) (op.getMacLength() / 8)); + }else if (op.getPurpose() == KMType.VERIFY) { if (0 != Util.arrayCompare( - KMByteBlob.cast(data[OUTPUT_DATA]).getBuffer(), - KMByteBlob.cast(data[OUTPUT_DATA]).getStartOff(), + scratchPad, (short)0, KMByteBlob.cast(data[SIGNATURE]).getBuffer(), KMByteBlob.cast(data[SIGNATURE]).getStartOff(), - (short) (op.getMacLength() / 8))) { + KMByteBlob.cast(data[SIGNATURE]).length())) { KMException.throwIt(KMError.VERIFICATION_FAILED); } + data[OUTPUT_DATA] = KMByteBlob.instance((short) 0); } break; default: // This is should never happen @@ -1954,18 +1939,18 @@ private void authorizeUpdateFinishOperation(KMOperationState op, byte[] scratchP // If one time user Authentication is required if (op.isSecureUserIdReqd() && !op.isAuthTimeoutValidated()) { validateVerificationToken(op, data[VERIFICATION_TOKEN], scratchPad); - tmpVariables[0] = op.getAuthTime(); - tmpVariables[2] = KMVerificationToken.cast(data[VERIFICATION_TOKEN]).getTimestamp(); - if (tmpVariables[2] == KMType.INVALID_VALUE) { + short authTime = op.getAuthTime(); + short verTime = KMVerificationToken.cast(data[VERIFICATION_TOKEN]).getTimestamp(); + if (verTime == KMType.INVALID_VALUE) { KMException.throwIt(KMError.VERIFICATION_FAILED); } - if (KMInteger.compare(tmpVariables[0], tmpVariables[2]) < 0) { + if (KMInteger.compare(authTime, verTime) < 0) { KMException.throwIt(KMError.KEY_USER_NOT_AUTHENTICATED); } op.setAuthTimeoutValidated(true); } else if (op.isAuthPerOperationReqd()) { // If Auth per operation is required - tmpVariables[0] = KMHardwareAuthToken.cast(data[HW_TOKEN]).getChallenge(); - if (KMInteger.compare(data[OP_HANDLE], tmpVariables[0]) != 0) { + short challenge = KMHardwareAuthToken.cast(data[HW_TOKEN]).getChallenge(); + if (KMInteger.compare(data[OP_HANDLE], challenge) != 0) { KMException.throwIt(KMError.KEY_USER_NOT_AUTHENTICATED); } authenticateUser(); @@ -2044,15 +2029,6 @@ private void validateVerificationToken(short verToken, byte[] scratchPad) { 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 ptr = KMVerificationToken.cast(verToken).getMac(); short key = repository.getComputedHmacKey(); @@ -2073,44 +2049,42 @@ private void validateVerificationToken(short verToken, byte[] scratchPad) { } } + private short updateOperationCmd(APDU apdu){ + short cmd = KMArray.instance((short) 4); + // Arguments + KMArray.cast(cmd).add((short) 0, KMInteger.exp()); + KMArray.cast(cmd).add((short) 1, KMByteBlob.exp()); + short authToken = KMHardwareAuthToken.exp(); + KMArray.cast(cmd).add((short) 2, authToken); + short verToken = KMVerificationToken.exp(); + KMArray.cast(cmd).add((short) 3, verToken); + return receiveIncoming(apdu, cmd); + } + private void processUpdateOperationCmd(APDU apdu) { - receiveIncoming(apdu); + short cmd = updateOperationCmd(apdu); byte[] scratchPad = apdu.getBuffer(); - tmpVariables[1] = KMArray.instance((short) 5); - // Arguments - tmpVariables[2] = KMKeyParameters.exp(); - KMArray.cast(tmpVariables[1]).add((short) 0, KMInteger.exp()); - KMArray.cast(tmpVariables[1]).add((short) 1, tmpVariables[2]); - KMArray.cast(tmpVariables[1]).add((short) 2, KMByteBlob.exp()); - tmpVariables[3] = KMHardwareAuthToken.exp(); - KMArray.cast(tmpVariables[1]).add((short) 3, tmpVariables[3]); - tmpVariables[4] = KMVerificationToken.exp(); - KMArray.cast(tmpVariables[1]).add((short) 4, tmpVariables[4]); - // Decode the arguments - tmpVariables[2] = decoder.decode(tmpVariables[1], (byte[]) bufferRef[0], bufferProp[BUF_START_OFFSET], bufferProp[BUF_LEN_OFFSET]); - //reclaim memory - repository.reclaimMemory(bufferProp[BUF_LEN_OFFSET]); - - 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); - data[HW_TOKEN] = KMArray.cast(tmpVariables[2]).get((short) 3); - data[VERIFICATION_TOKEN] = KMArray.cast(tmpVariables[2]).get((short) 4); + data[OP_HANDLE] = KMArray.cast(cmd).get((short) 0); + data[INPUT_DATA] = KMArray.cast(cmd).get((short) 1); + data[HW_TOKEN] = KMArray.cast(cmd).get((short) 2); + data[VERIFICATION_TOKEN] = KMArray.cast(cmd).get((short) 3); + // Input data must be present even if it is zero length. if (data[INPUT_DATA] == KMType.INVALID_VALUE) { KMException.throwIt(KMError.INVALID_ARGUMENT); } + // Check Operation Handle and get op state // Check Operation Handle - KMOperationState op = repository.findOperation(data[OP_HANDLE]); + KMOperationState op = findOperation(data[OP_HANDLE]); if (op == null) { KMException.throwIt(KMError.INVALID_OPERATION_HANDLE); } // authorize the update operation authorizeUpdateFinishOperation(op, scratchPad); - // If signing without digest then do length validation checks + if (op.getPurpose() == KMType.SIGN || op.getPurpose() == KMType.VERIFY) { - tmpVariables[0] = KMByteBlob.cast(data[INPUT_DATA]).length(); + short len = KMByteBlob.cast(data[INPUT_DATA]).length(); // update the data. op.getOperation() .update( @@ -2124,43 +2098,34 @@ private void processUpdateOperationCmd(APDU apdu) { if (op.getAlgorithm() == KMType.RSA) { KMException.throwIt(KMError.OPERATION_CANCELLED); } - tmpVariables[0] = KMByteBlob.cast(data[INPUT_DATA]).length(); + short len = KMByteBlob.cast(data[INPUT_DATA]).length(); short additionalExpOutLen = 0; if (op.getAlgorithm() == KMType.AES) { - if (op.getBlockMode() == KMType.GCM) { - updateAAD(op, (byte) 0x00); - // if input data present - if (tmpVariables[0] > 0) { - if (tmpVariables[0] % AES_BLOCK_SIZE != 0) { - KMException.throwIt(KMError.INVALID_INPUT_LENGTH); - } - // no more future updateAAD allowed if input data present. - if (op.isAesGcmUpdateAllowed()) { - op.setAesGcmUpdateComplete(); - } + if (op.getBlockMode() == KMType.GCM || op.getBlockMode() == KMType.CTR) { + if(op.isAesGcmUpdateAllowed()){ + op.setAesGcmUpdateComplete(); } additionalExpOutLen = 16; } else { // input data must be block aligned. // 128 bit block size - HAL must send block aligned data - if (tmpVariables[0] % AES_BLOCK_SIZE != 0 || tmpVariables[0] <= 0) { + if (len % AES_BLOCK_SIZE != 0 || len <= 0) { KMException.throwIt(KMError.INVALID_INPUT_LENGTH); } } } else if (op.getAlgorithm() == KMType.DES) { // 64 bit block size - HAL must send block aligned data - if (tmpVariables[0] % DES_BLOCK_SIZE != 0) { + if (len % DES_BLOCK_SIZE != 0) { KMException.throwIt(KMError.INVALID_INPUT_LENGTH); } } // Allocate output buffer as input data is already block aligned - data[OUTPUT_DATA] = KMByteBlob.instance((short) (tmpVariables[0] + additionalExpOutLen)); + data[OUTPUT_DATA] = KMByteBlob.instance((short) (len + 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] = + len = op.getOperation() .update( KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), @@ -2171,59 +2136,112 @@ private void processUpdateOperationCmd(APDU apdu) { } catch (CryptoException e) { KMException.throwIt(KMError.INVALID_TAG); } + // Adjust the Output data if it is not equal to input data. // This happens in case of JCardSim provider. - if (tmpVariables[0] != KMByteBlob.cast(data[OUTPUT_DATA]).length()) { + if (len != KMByteBlob.cast(data[OUTPUT_DATA]).length()) { data[INPUT_DATA] = data[OUTPUT_DATA]; - data[OUTPUT_DATA] = KMByteBlob.instance(tmpVariables[0]); + data[OUTPUT_DATA] = KMByteBlob.instance(len); Util.arrayCopy( KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), KMByteBlob.cast(data[OUTPUT_DATA]).getBuffer(), KMByteBlob.cast(data[OUTPUT_DATA]).getStartOff(), - tmpVariables[0]); + len); } } - // Persist if there are any updates. - op.persist(); - // make response - tmpVariables[1] = KMArray.instance((short) 0); - tmpVariables[1] = KMKeyParameters.instance(tmpVariables[1]); - tmpVariables[2] = KMArray.instance((short) 4); + if (data[OUTPUT_DATA] == KMType.INVALID_VALUE) { data[OUTPUT_DATA] = KMByteBlob.instance((short) 0); } - KMArray.cast(tmpVariables[2]).add((short) 0, buildErrorStatus(KMError.OK)); - 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]); + // Persist if there are any updates. + //op.persist(); + // make response + short resp = KMArray.instance((short) 2); + KMArray.cast(resp).add((short) 0, KMInteger.uint_16(KMError.OK)); + KMArray.cast(resp).add((short) 1, data[OUTPUT_DATA]); + sendOutgoing(apdu, resp); + } + + private short updateAadOperationCmd(APDU apdu){ + short cmd = KMArray.instance((short) 4); + KMArray.cast(cmd).add((short) 0, KMInteger.exp()); + KMArray.cast(cmd).add((short) 1, KMByteBlob.exp()); + short authToken = KMHardwareAuthToken.exp(); + KMArray.cast(cmd).add((short) 2, authToken); + short verToken = KMVerificationToken.exp(); + KMArray.cast(cmd).add((short) 3, verToken); + return receiveIncoming(apdu, cmd); + } + + private void processUpdateAadOperationCmd(APDU apdu) { + short cmd = updateAadOperationCmd(apdu); + byte[] scratchPad = apdu.getBuffer(); + data[OP_HANDLE] = KMArray.cast(cmd).get((short) 0); + data[INPUT_DATA] = KMArray.cast(cmd).get((short) 1); + data[HW_TOKEN] = KMArray.cast(cmd).get((short) 2); + data[VERIFICATION_TOKEN] = KMArray.cast(cmd).get((short) 3); + + // Input data must be present even if it is zero length. + if (data[INPUT_DATA] == KMType.INVALID_VALUE) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + // Check Operation Handle and get op state + // Check Operation Handle + KMOperationState op = findOperation(data[OP_HANDLE]); + if (op == null) { + KMException.throwIt(KMError.INVALID_OPERATION_HANDLE); + } + if(op.getAlgorithm() != KMType.AES ){ + KMException.throwIt(KMError.INCOMPATIBLE_ALGORITHM); + } + if(op.getBlockMode() != KMType.GCM ){ + KMException.throwIt(KMError.INCOMPATIBLE_BLOCK_MODE); + } + if(!op.isAesGcmUpdateAllowed() ){ + KMException.throwIt(KMError.INVALID_TAG); + } + if(op.getPurpose() != KMType.ENCRYPT && op.getPurpose() != KMType.DECRYPT){ + KMException.throwIt(KMError.INCOMPATIBLE_PURPOSE); + } + // authorize the update operation + authorizeUpdateFinishOperation(op, scratchPad); + try { + op.getOperation() + .updateAAD( + KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), + KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), + KMByteBlob.cast(data[INPUT_DATA]).length()); + }catch(CryptoException exp){ + KMException.throwIt(KMError.UNKNOWN_ERROR); + } + // make response + short resp = KMArray.instance((short) 1); + KMArray.cast(resp).add((short) 0, KMInteger.uint_16(KMError.OK)); + sendOutgoing(apdu, resp); + } - bufferProp[BUF_START_OFFSET] = repository.allocAvailableMemory(); - // Encode the response - bufferProp[BUF_LEN_OFFSET] = encoder.encode(tmpVariables[2], (byte[]) bufferRef[0], bufferProp[BUF_START_OFFSET]); - sendOutgoing(apdu); + private short beginOperationCmd(APDU apdu){ + short cmd = KMArray.instance((short) 4); + // Arguments + short params = KMKeyParameters.expAny(); + KMArray.cast(cmd).add((short) 0, KMEnum.instance(KMType.PURPOSE)); + KMArray.cast(cmd).add((short) 1, KMByteBlob.exp()); + KMArray.cast(cmd).add((short) 2, params); + short authToken = KMHardwareAuthToken.exp(); + KMArray.cast(cmd).add((short) 3, authToken); + return receiveIncoming(apdu, cmd); } private void processBeginOperationCmd(APDU apdu) { // Receive the incoming request fully from the master into buffer. - receiveIncoming(apdu); + short cmd = beginOperationCmd(apdu); byte[] scratchPad = apdu.getBuffer(); - short args; - tmpVariables[1] = KMArray.instance((short) 4); - // Arguments - tmpVariables[2] = KMKeyParameters.exp(); - KMArray.cast(tmpVariables[1]).add((short) 0, KMEnum.instance(KMType.PURPOSE)); - KMArray.cast(tmpVariables[1]).add((short) 1, KMByteBlob.exp()); - KMArray.cast(tmpVariables[1]).add((short) 2, tmpVariables[2]); - tmpVariables[3] = KMHardwareAuthToken.exp(); - KMArray.cast(tmpVariables[1]).add((short) 3, tmpVariables[3]); - // Decode the arguments - args = decoder.decode(tmpVariables[1], (byte[]) bufferRef[0], bufferProp[BUF_START_OFFSET], bufferProp[BUF_LEN_OFFSET]); - //reclaim memory - repository.reclaimMemory(bufferProp[BUF_LEN_OFFSET]); - - data[KEY_PARAMETERS] = KMArray.cast(args).get((short) 2); - data[KEY_BLOB] = KMArray.cast(args).get((short) 1); + short purpose = KMArray.cast(cmd).get((short) 0); + data[KEY_BLOB] = KMArray.cast(cmd).get((short) 1); + data[KEY_PARAMETERS] = KMArray.cast(cmd).get((short) 2); + data[HW_TOKEN] = KMArray.cast(cmd).get((short) 3); + purpose = KMEnum.cast(purpose).getVal(); // Check for app id and app data. data[APP_ID] = KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.APPLICATION_ID, data[KEY_PARAMETERS]); @@ -2236,14 +2254,19 @@ private void processBeginOperationCmd(APDU apdu) { data[APP_DATA] = KMByteTag.cast(data[APP_DATA]).getValue(); } // Parse the encrypted blob and decrypt it. - parseEncryptedKeyBlob(scratchPad); - // Authorize the begin operation and reserve op - data[OP_HANDLE] will have the handle. - // It will also set data[IV] field if required. - tmpVariables[0] = KMArray.cast(args).get((short) 0); - tmpVariables[0] = KMEnum.cast(tmpVariables[0]).getVal(); - data[HW_TOKEN] = KMArray.cast(args).get((short) 3); - /*Generate a random number for operation handle */ - short buf = KMByteBlob.instance(KMRepository.OPERATION_HANDLE_SIZE); + parseEncryptedKeyBlob(data[KEY_BLOB], data[APP_ID], data[APP_DATA], scratchPad); + KMTag.assertAbsence(data[SB_PARAMETERS],KMType.BOOL_TAG, KMType.EARLY_BOOT_ONLY, KMError.EARLY_BOOT_ENDED); + KMTag.assertPresence(data[SB_PARAMETERS],KMType.ENUM_TAG,KMType.ALGORITHM,KMError.UNSUPPORTED_ALGORITHM); + short algorithm = KMEnumTag.getValue(KMType.ALGORITHM,data[SB_PARAMETERS]); + // If Blob usage tag is present in key characteristics then it should be standalone. + if(KMTag.isPresent(data[SB_PARAMETERS],KMType.ENUM_TAG, KMType.BLOB_USAGE_REQ)){ + if(KMEnumTag.getValue(KMType.BLOB_USAGE_REQ, data[SB_PARAMETERS]) != KMType.STANDALONE){ + KMException.throwIt(KMError.UNSUPPORTED_TAG); + } + } + + // Generate a random number for operation handle + short buf = KMByteBlob.instance(KMOperationState.OPERATION_HANDLE_SIZE); generateUniqueOperationHandle( KMByteBlob.cast(buf).getBuffer(), KMByteBlob.cast(buf).getStartOff(), @@ -2253,12 +2276,12 @@ private void processBeginOperationCmd(APDU apdu) { KMByteBlob.cast(buf).getBuffer(), KMByteBlob.cast(buf).getStartOff(), KMByteBlob.cast(buf).length()); - KMOperationState op = repository.reserveOperation(opHandle); + KMOperationState op = reserveOperation(algorithm,opHandle); if (op == null) { KMException.throwIt(KMError.TOO_MANY_OPERATIONS); } data[OP_HANDLE] = op.getHandle(); - op.setPurpose((byte) tmpVariables[0]); + op.setPurpose((byte) purpose); op.setKeySize(KMByteBlob.cast(data[SECRET]).length()); authorizeAndBeginOperation(op, scratchPad); switch (op.getPurpose()) { @@ -2270,43 +2293,46 @@ private void processBeginOperationCmd(APDU apdu) { case KMType.DECRYPT: beginCipherOperation(op); break; + case KMType.AGREE_KEY: + beginKeyAgreementOperation(op); + break; default: KMException.throwIt(KMError.UNIMPLEMENTED); break; } + short iv = KMType.INVALID_VALUE; // If the data[IV] is required to be returned. // As per VTS, for the decryption operation don't send the iv back. if (data[IV] != KMType.INVALID_VALUE && op.getPurpose() != KMType.DECRYPT && op.getBlockMode() != KMType.ECB) { - tmpVariables[2] = KMArray.instance((short) 1); + iv = KMArray.instance((short) 1); if (op.getAlgorithm() == KMType.DES && op.getBlockMode() == KMType.CBC) { // For AES/DES we are generate an random iv of length 16 bytes. // While sending the iv back for DES/CBC mode of opeation only send // 8 bytes back. - tmpVariables[1] = KMByteBlob.instance((short) 8); + short ivBlob = KMByteBlob.instance((short) 8); Util.arrayCopy( KMByteBlob.cast(data[IV]).getBuffer(), KMByteBlob.cast(data[IV]).getStartOff(), - KMByteBlob.cast(tmpVariables[1]).getBuffer(), - KMByteBlob.cast(tmpVariables[1]).getStartOff(), + KMByteBlob.cast(ivBlob).getBuffer(), + KMByteBlob.cast(ivBlob).getStartOff(), (short) 8); - data[IV] = tmpVariables[1]; + data[IV] = ivBlob; } - KMArray.cast(tmpVariables[2]).add((short) 0, KMByteTag.instance(KMType.NONCE, data[IV])); + KMArray.cast(iv).add((short) 0, KMByteTag.instance(KMType.NONCE, data[IV])); } else { - tmpVariables[2] = KMArray.instance((short) 0); + iv = KMArray.instance((short) 0); } - tmpVariables[1] = KMKeyParameters.instance(tmpVariables[2]); - tmpVariables[0] = KMArray.instance((short) 3); - KMArray.cast(tmpVariables[0]).add((short) 0, buildErrorStatus(KMError.OK)); - KMArray.cast(tmpVariables[0]).add((short) 1, tmpVariables[1]); - KMArray.cast(tmpVariables[0]).add((short) 2, data[OP_HANDLE]); - bufferProp[BUF_START_OFFSET] = repository.allocAvailableMemory(); - // Encode the response - bufferProp[BUF_LEN_OFFSET] = encoder.encode(tmpVariables[0], (byte[]) bufferRef[0], bufferProp[BUF_START_OFFSET]); - sendOutgoing(apdu); + short params = KMKeyParameters.instance(iv); + short resp = KMArray.instance((short) 5); + KMArray.cast(resp).add((short) 0, KMInteger.uint_16(KMError.OK)); + KMArray.cast(resp).add((short) 1, params); + KMArray.cast(resp).add((short) 2, data[OP_HANDLE]); + KMArray.cast(resp).add((short) 3, KMInteger.uint_8(op.getBufferingMode())); + KMArray.cast(resp).add((short) 4, KMInteger.uint_16((short) (op.getMacLength() / 8))); + sendOutgoing(apdu, resp); } private void authorizeAlgorithm(KMOperationState op) { @@ -2321,16 +2347,26 @@ private void authorizePurpose(KMOperationState op) { switch (op.getAlgorithm()) { case KMType.AES: case KMType.DES: - if (op.getPurpose() == KMType.SIGN || op.getPurpose() == KMType.VERIFY) { + if (op.getPurpose() == KMType.SIGN || op.getPurpose() == KMType.VERIFY || + op.getPurpose() == KMType.AGREE_KEY) { KMException.throwIt(KMError.UNSUPPORTED_PURPOSE); } break; case KMType.EC: - case KMType.HMAC: if (op.getPurpose() == KMType.ENCRYPT || op.getPurpose() == KMType.DECRYPT) { KMException.throwIt(KMError.UNSUPPORTED_PURPOSE); } break; + case KMType.HMAC: + if (op.getPurpose() == KMType.ENCRYPT || op.getPurpose() == KMType.DECRYPT || + op.getPurpose() == KMType.AGREE_KEY) { + KMException.throwIt(KMError.UNSUPPORTED_PURPOSE); + } + break; + case KMType.RSA: + if (op.getPurpose() == KMType.AGREE_KEY) + KMException.throwIt(KMError.UNSUPPORTED_PURPOSE); + break; default: break; } @@ -2347,19 +2383,22 @@ private void authorizeDigest(KMOperationState op) { KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.DIGEST, data[KEY_PARAMETERS]); if (param != KMType.INVALID_VALUE) { if (KMEnumArrayTag.cast(param).length() != 1) { - KMException.throwIt(KMError.INVALID_ARGUMENT); + KMException.throwIt(KMError.UNSUPPORTED_DIGEST); } param = KMEnumArrayTag.cast(param).get((short) 0); if (!KMEnumArrayTag.cast(digests).contains(param)) { KMException.throwIt(KMError.INCOMPATIBLE_DIGEST); } op.setDigest((byte) param); + }else if(KMEnumArrayTag.contains(KMType.PADDING, KMType.RSA_PKCS1_1_5_SIGN, data[KEY_PARAMETERS])){ + KMException.throwIt(KMError.UNSUPPORTED_DIGEST); } short paramPadding = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.PADDING, data[KEY_PARAMETERS]); if (paramPadding != KMType.INVALID_VALUE) { if (KMEnumArrayTag.cast(paramPadding).length() != 1) { - KMException.throwIt(KMError.INVALID_ARGUMENT); + //TODO vts fails because it expects UNSUPPORTED_PADDING_MODE + KMException.throwIt(KMError.UNSUPPORTED_PADDING_MODE); } paramPadding = KMEnumArrayTag.cast(paramPadding).get((short) 0); } @@ -2372,7 +2411,7 @@ private void authorizeDigest(KMOperationState op) { break; case KMType.EC: case KMType.HMAC: - if (param == KMType.INVALID_VALUE) { + if (op.getPurpose() != KMType.AGREE_KEY && param == KMType.INVALID_VALUE) { KMException.throwIt(KMError.UNSUPPORTED_DIGEST); } break; @@ -2413,6 +2452,7 @@ private void authorizePadding(KMOperationState op) { && param != KMType.RSA_PKCS1_1_5_ENCRYPT) { KMException.throwIt(KMError.UNSUPPORTED_PADDING_MODE); } + if (param == KMType.PADDING_NONE && op.getDigest() != KMType.DIGEST_NONE) { KMException.throwIt(KMError.INCOMPATIBLE_DIGEST); } @@ -2420,6 +2460,29 @@ private void authorizePadding(KMOperationState op) { && op.getDigest() == KMType.DIGEST_NONE) { KMException.throwIt(KMError.INCOMPATIBLE_DIGEST); } + //TODO d to verify whether javacard support MGF1 = SHA1 or is it equal to the OAEP scheme + // digest. There is no way to define any other digest. + if(param == KMType.RSA_OAEP){ + short mgfDigest = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, + KMType.RSA_OAEP_MGF_DIGEST, data[KEY_PARAMETERS]); + if(mgfDigest != KMType.INVALID_VALUE) { + if(KMEnumArrayTag.cast(mgfDigest).length() != 1) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + mgfDigest = KMEnumArrayTag.cast(mgfDigest).get((short) 0); + if (mgfDigest == KMType.DIGEST_NONE) { + KMException.throwIt(KMError.UNSUPPORTED_MGF_DIGEST); + } + if (!KMEnumArrayTag + .contains(KMType.RSA_OAEP_MGF_DIGEST, mgfDigest, data[HW_PARAMETERS])) { + KMException.throwIt(KMError.INCOMPATIBLE_MGF_DIGEST); + } + if (mgfDigest != KMType.SHA1 && mgfDigest != KMType.SHA2_256) { + KMException.throwIt(KMError.UNSUPPORTED_MGF_DIGEST); + } + op.setMgfDigest((byte) mgfDigest); + } + } op.setPadding((byte) param); break; case KMType.DES: @@ -2439,7 +2502,7 @@ private void authorizeBlockModeAndMacLength(KMOperationState op) { KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.BLOCK_MODE, data[KEY_PARAMETERS]); if (param != KMType.INVALID_VALUE) { if (KMEnumArrayTag.cast(param).length() != 1) { - KMException.throwIt(KMError.INVALID_ARGUMENT); + KMException.throwIt(KMError.UNSUPPORTED_BLOCK_MODE); } param = KMEnumArrayTag.cast(param).get((short) 0); } @@ -2466,7 +2529,7 @@ private void authorizeBlockModeAndMacLength(KMOperationState op) { KMException.throwIt(KMError.INVALID_ARGUMENT); } if (param == KMType.GCM) { - if (op.getPadding() != KMType.PADDING_NONE) { + if (op.getPadding() != KMType.PADDING_NONE || op.getPadding() == KMType.PKCS7) { KMException.throwIt(KMError.INCOMPATIBLE_PADDING_MODE); } if (macLen == KMType.INVALID_VALUE) { @@ -2481,7 +2544,12 @@ private void authorizeBlockModeAndMacLength(KMOperationState op) { } op.setMacLength(macLen); } - break; + if (param == KMType.CTR) { + if (op.getPadding() != KMType.PADDING_NONE || op.getPadding() == KMType.PKCS7) { + KMException.throwIt(KMError.INCOMPATIBLE_PADDING_MODE); + } + } + break; case KMType.DES: //Validate the block mode. switch (param) { @@ -2524,7 +2592,6 @@ private void authorizeBlockModeAndMacLength(KMOperationState op) { } private void authorizeAndBeginOperation(KMOperationState op, byte[] scratchPad) { - authorizeAlgorithm(op); authorizePurpose(op); authorizeDigest(op); authorizePadding(op); @@ -2537,27 +2604,29 @@ private void authorizeAndBeginOperation(KMOperationState op, byte[] scratchPad) // Authorize Caller Nonce - if caller nonce absent in key char and nonce present in // key params then fail if it is not a Decrypt operation data[IV] = KMType.INVALID_VALUE; - tmpVariables[0] = - KMKeyParameters.findTag(KMType.BOOL_TAG, KMType.CALLER_NONCE, data[HW_PARAMETERS]); - tmpVariables[1] = KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.NONCE, data[KEY_PARAMETERS]); - if (tmpVariables[0] == KMType.INVALID_VALUE) { - if (tmpVariables[1] != KMType.INVALID_VALUE && op.getPurpose() != KMType.DECRYPT) { - KMException.throwIt(KMError.CALLER_NONCE_PROHIBITED); - } + + if (!KMTag.isPresent(data[HW_PARAMETERS], KMType.BOOL_TAG, KMType.CALLER_NONCE ) + && KMTag.isPresent(data[KEY_PARAMETERS], KMType.BYTES_TAG, KMType.NONCE ) + && op.getPurpose() != KMType.DECRYPT) { + KMException.throwIt(KMError.CALLER_NONCE_PROHIBITED); } + + short nonce = KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.NONCE, data[KEY_PARAMETERS]); // If Nonce is present then check whether the size of nonce is correct. - if (tmpVariables[1] != KMType.INVALID_VALUE) { - data[IV] = KMByteTag.cast(tmpVariables[1]).getValue(); + if (nonce != KMType.INVALID_VALUE) { + data[IV] = KMByteTag.cast(nonce).getValue(); // For CBC mode - iv must be 8 bytes if (op.getBlockMode() == KMType.CBC && op.getAlgorithm() == KMType.DES && KMByteBlob.cast(data[IV]).length() != 8) { KMException.throwIt(KMError.INVALID_NONCE); } + // For GCM mode - IV must be 12 bytes if (KMByteBlob.cast(data[IV]).length() != 12 && op.getBlockMode() == KMType.GCM) { KMException.throwIt(KMError.INVALID_NONCE); } + // For AES CBC and CTR modes IV must be 16 bytes if ((op.getBlockMode() == KMType.CBC || op.getBlockMode() == KMType.CTR) && op.getAlgorithm() == KMType.AES @@ -2565,6 +2634,7 @@ private void authorizeAndBeginOperation(KMOperationState op, byte[] scratchPad) KMException.throwIt(KMError.INVALID_NONCE); } } else if (op.getAlgorithm() == KMType.AES || op.getAlgorithm() == KMType.DES) { + // For symmetric decryption iv is required if (op.getPurpose() == KMType.DECRYPT && (op.getBlockMode() == KMType.CBC @@ -2575,6 +2645,7 @@ private void authorizeAndBeginOperation(KMOperationState op, byte[] scratchPad) // For ECB we create zero length nonce data[IV] = KMByteBlob.instance((short) 0); } else if (op.getPurpose() == KMType.ENCRYPT) { + // For encrypt mode if nonce is absent then create random nonce of correct length byte ivLen = 16; if (op.getBlockMode() == KMType.GCM) { @@ -2591,6 +2662,25 @@ private void authorizeAndBeginOperation(KMOperationState op, byte[] scratchPad) } } + private void beginKeyAgreementOperation(KMOperationState op) { + if (op.getAlgorithm() != KMType.EC) + KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM); + + op.setOperation( + seProvider.initAsymmetricOperation( + (byte) op.getPurpose(), + (byte)op.getAlgorithm(), + (byte)op.getPadding(), + (byte)op.getDigest(), + KMType.DIGEST_NONE, /* No MGF1 Digest */ + KMByteBlob.cast(data[SECRET]).getBuffer(), + KMByteBlob.cast(data[SECRET]).getStartOff(), + KMByteBlob.cast(data[SECRET]).length(), + null, + (short) 0, + (short) 0)); + } + private void beginCipherOperation(KMOperationState op) { switch (op.getAlgorithm()) { case KMType.RSA: @@ -2599,9 +2689,10 @@ private void beginCipherOperation(KMOperationState op) { op.setOperation( seProvider.initAsymmetricOperation( (byte) op.getPurpose(), - op.getAlgorithm(), - op.getPadding(), - op.getDigest(), + (byte)op.getAlgorithm(), + (byte)op.getPadding(), + (byte)op.getDigest(), + (byte) op.getMgfDigest(), KMByteBlob.cast(data[SECRET]).getBuffer(), KMByteBlob.cast(data[SECRET]).getStartOff(), KMByteBlob.cast(data[SECRET]).length(), @@ -2624,10 +2715,10 @@ private void beginCipherOperation(KMOperationState op) { op.setOperation( seProvider.initSymmetricOperation( (byte) op.getPurpose(), - op.getAlgorithm(), - op.getDigest(), - op.getPadding(), - op.getBlockMode(), + (byte)op.getAlgorithm(), + (byte)op.getDigest(), + (byte)op.getPadding(), + (byte)op.getBlockMode(), KMByteBlob.cast(data[SECRET]).getBuffer(), KMByteBlob.cast(data[SECRET]).getStartOff(), KMByteBlob.cast(data[SECRET]).length(), @@ -2653,9 +2744,10 @@ private void beginSignVerifyOperation(KMOperationState op) { op.setOperation( seProvider.initAsymmetricOperation( (byte) op.getPurpose(), - op.getAlgorithm(), - op.getPadding(), - op.getDigest(), + (byte)op.getAlgorithm(), + (byte)op.getPadding(), + (byte)op.getDigest(), + KMType.DIGEST_NONE, /* No MGF Digest */ KMByteBlob.cast(data[SECRET]).getBuffer(), KMByteBlob.cast(data[SECRET]).getStartOff(), KMByteBlob.cast(data[SECRET]).length(), @@ -2675,9 +2767,10 @@ private void beginSignVerifyOperation(KMOperationState op) { op.setOperation( seProvider.initAsymmetricOperation( (byte) op.getPurpose(), - op.getAlgorithm(), - op.getPadding(), - op.getDigest(), + (byte)op.getAlgorithm(), + (byte)op.getPadding(), + (byte)op.getDigest(), + KMType.DIGEST_NONE, /* No MGF Digest */ KMByteBlob.cast(data[SECRET]).getBuffer(), KMByteBlob.cast(data[SECRET]).getStartOff(), KMByteBlob.cast(data[SECRET]).length(), @@ -2708,10 +2801,10 @@ private void beginSignVerifyOperation(KMOperationState op) { op.setOperation( seProvider.initSymmetricOperation( (byte) KMType.SIGN, - op.getAlgorithm(), - op.getDigest(), - op.getPadding(), - op.getBlockMode(), + (byte)op.getAlgorithm(), + (byte)op.getDigest(), + (byte)op.getPadding(), + (byte)op.getBlockMode(), KMByteBlob.cast(data[SECRET]).getBuffer(), KMByteBlob.cast(data[SECRET]).getStartOff(), KMByteBlob.cast(data[SECRET]).length(), @@ -2732,17 +2825,15 @@ private void beginSignVerifyOperation(KMOperationState op) { private void authorizeUserSecureIdAuthTimeout(KMOperationState op) { short authTime; // Authorize User Secure Id and Auth timeout - tmpVariables[0] = - KMKeyParameters.findTag(KMType.ULONG_ARRAY_TAG, KMType.USER_SECURE_ID, data[HW_PARAMETERS]); - if (tmpVariables[0] != KMType.INVALID_VALUE) { - tmpVariables[0] = + if (KMTag.isPresent(data[HW_PARAMETERS], KMType.ULONG_ARRAY_TAG, KMType.USER_SECURE_ID)) { + authTime = KMKeyParameters.findTag(KMType.UINT_TAG, KMType.AUTH_TIMEOUT, data[HW_PARAMETERS]); - if (tmpVariables[0] != KMType.INVALID_VALUE) { + if (authTime != KMType.INVALID_VALUE) { // check if hw token is empty - mac should not be empty. if (data[HW_TOKEN] == KMType.INVALID_VALUE) { KMException.throwIt(KMError.INVALID_MAC_LENGTH); } - authTime = KMIntegerTag.cast(tmpVariables[0]).getValue(); + authTime = KMIntegerTag.cast(authTime).getValue(); // authenticate user authenticateUser(); // set the one time auth @@ -2762,22 +2853,22 @@ private void authorizeUserSecureIdAuthTimeout(KMOperationState op) { } private void authenticateUser() { - tmpVariables[0] = KMHardwareAuthToken.cast(data[HW_TOKEN]).getUserId(); - if (KMInteger.cast(tmpVariables[0]).isZero()) { - tmpVariables[0] = KMHardwareAuthToken.cast(data[HW_TOKEN]).getAuthenticatorId(); - if (KMInteger.cast(tmpVariables[0]).isZero()) { + short id = KMHardwareAuthToken.cast(data[HW_TOKEN]).getUserId(); + if (KMInteger.cast(id).isZero()) { + id = KMHardwareAuthToken.cast(data[HW_TOKEN]).getAuthenticatorId(); + if (KMInteger.cast(id).isZero()) { KMException.throwIt(KMError.KEY_USER_NOT_AUTHENTICATED); } } // check user secure id - if (!KMIntegerArrayTag.contains(KMType.USER_SECURE_ID, tmpVariables[0], data[HW_PARAMETERS])) { + if (!KMIntegerArrayTag.contains(KMType.USER_SECURE_ID, id, data[HW_PARAMETERS])) { KMException.throwIt(KMError.KEY_USER_NOT_AUTHENTICATED); } // check auth type - tmpVariables[1] = KMEnumTag.getValue(KMType.USER_AUTH_TYPE, data[HW_PARAMETERS]); - tmpVariables[2] = KMHardwareAuthToken.cast(data[HW_TOKEN]).getHwAuthenticatorType(); - tmpVariables[2] = KMEnum.cast(tmpVariables[2]).getVal(); - if (((byte) tmpVariables[2] & (byte) tmpVariables[1]) == 0) { + short authType = KMEnumTag.getValue(KMType.USER_AUTH_TYPE, data[HW_PARAMETERS]); + short authenticatorType = KMHardwareAuthToken.cast(data[HW_TOKEN]).getHwAuthenticatorType(); + authenticatorType = KMEnum.cast(authenticatorType).getVal(); + if (((byte) authenticatorType & (byte) authType) == 0) { KMException.throwIt(KMError.KEY_USER_NOT_AUTHENTICATED); } } @@ -2832,59 +2923,59 @@ private boolean validateHwToken(short hwToken, byte[] scratchPad) { KMByteBlob.cast(ptr).length()); } + private short importKeyCmd(APDU apdu){ + short cmd = KMArray.instance((short) 6); + // Arguments + short params = KMKeyParameters.expAny(); + KMArray.cast(cmd).add((short) 0, params); + KMArray.cast(cmd).add((short) 1, KMEnum.instance(KMType.KEY_FORMAT)); + KMArray.cast(cmd).add((short) 2, KMByteBlob.exp()); + KMArray.cast(cmd).add((short) 3, KMByteBlob.exp()); //attest key + KMArray.cast(cmd).add((short) 4, params); //attest key params + KMArray.cast(cmd).add((short) 5, KMByteBlob.exp()); //issuer + return receiveIncoming(apdu, cmd); + } private void processImportKeyCmd(APDU apdu) { // Receive the incoming request fully from the master into buffer. - receiveIncoming(apdu); + short cmd = importKeyCmd(apdu); byte[] scratchPad = apdu.getBuffer(); - tmpVariables[1] = KMArray.instance((short) 3); - // Arguments - tmpVariables[2] = KMKeyParameters.exp(); - KMArray.cast(tmpVariables[1]).add((short) 0, tmpVariables[2]); - KMArray.cast(tmpVariables[1]).add((short) 1, KMEnum.instance(KMType.KEY_FORMAT)); - KMArray.cast(tmpVariables[1]).add((short) 2, KMByteBlob.exp()); - // Decode the arguments - tmpVariables[2] = decoder.decode(tmpVariables[1], (byte[]) bufferRef[0], bufferProp[BUF_START_OFFSET], bufferProp[BUF_LEN_OFFSET]); - //reclaim memory - repository.reclaimMemory(bufferProp[BUF_LEN_OFFSET]); - - 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); - // Key format must be RAW format - X509 and PKCS8 not implemented. - tmpVariables[3] = KMEnum.cast(tmpVariables[3]).getVal(); - if (tmpVariables[3] != KMType.RAW) { - KMException.throwIt(KMError.UNIMPLEMENTED); - } + data[KEY_PARAMETERS] = KMArray.cast(cmd).get((short) 0); + short keyFmt = KMArray.cast(cmd).get((short) 1); + data[IMPORTED_KEY_BLOB] = KMArray.cast(cmd).get((short) 2); + data[ATTEST_KEY_BLOB] = KMArray.cast(cmd).get((short) 3); + data[ATTEST_KEY_PARAMS] = KMArray.cast(cmd).get((short) 4); + data[ATTEST_KEY_ISSUER] = KMArray.cast(cmd).get((short) 5); + keyFmt = KMEnum.cast(keyFmt).getVal(); + + data[CERTIFICATE] = KMArray.instance((short)0); //by default the cert is empty. data[ORIGIN] = KMType.IMPORTED; - importKey(apdu, scratchPad); + importKey(apdu, keyFmt, scratchPad); } - private void importKey(APDU apdu, byte[] scratchPad) { - // Bootloader only not supported - tmpVariables[0] = - KMKeyParameters.findTag(KMType.BOOL_TAG, KMType.BOOTLOADER_ONLY, data[KEY_PARAMETERS]); - if (tmpVariables[0] != KMType.INVALID_VALUE) { - KMException.throwIt(KMError.INVALID_KEY_BLOB); - } + private void validateImportKey(short params, short keyFmt){ // Rollback protection not supported - tmpVariables[0] = - KMKeyParameters.findTag(KMType.BOOL_TAG, KMType.ROLLBACK_RESISTANCE, data[KEY_PARAMETERS]); - if (tmpVariables[0] != KMType.INVALID_VALUE) { - KMException.throwIt(KMError.ROLLBACK_RESISTANCE_UNAVAILABLE); + KMTag.assertAbsence(params, KMType.BOOL_TAG, KMType.ROLLBACK_RESISTANCE, KMError.ROLLBACK_RESISTANCE_UNAVAILABLE); + // vts disallows importing EARLY_BOOT keys + KMTag.assertAbsence(params, KMType.BOOL_TAG, KMType.EARLY_BOOT_ONLY, KMError.EARLY_BOOT_ENDED); + // Importing Bootloader only keys not supported. + KMTag.assertAbsence(params, KMType.BOOL_TAG, KMType.BOOTLOADER_ONLY, KMError.INVALID_KEY_BLOB); + // Algorithm must be present + KMTag.assertPresence(params, KMType.ENUM_TAG, KMType.ALGORITHM, KMError.INVALID_ARGUMENT); + short alg = KMEnumTag.getValue(KMType.ALGORITHM, params); + // key format must be raw if aes, des or hmac and pkcs8 for rsa and ec. + if((alg == KMType.AES || alg == KMType.DES || alg == KMType.HMAC) && keyFmt != KMType.RAW ){ + KMException.throwIt(KMError.UNIMPLEMENTED); } - - // get algorithm - tmpVariables[3] = KMEnumTag.getValue(KMType.ALGORITHM, data[KEY_PARAMETERS]); - if (tmpVariables[3] == KMType.INVALID_VALUE) { - KMException.throwIt(KMError.INVALID_ARGUMENT); + if((alg == KMType.RSA || alg == KMType.EC) && keyFmt != KMType.PKCS8){ + KMException.throwIt(KMError.UNIMPLEMENTED); } + } - //Check if the tags are supported. - if (KMKeyParameters.hasUnsupportedTags(data[KEY_PARAMETERS])) { - KMException.throwIt(KMError.UNSUPPORTED_TAG); - } + private void importKey(APDU apdu, short keyFmt, byte[] scratchPad) { + validateImportKey(data[KEY_PARAMETERS], keyFmt); // Check algorithm and dispatch to appropriate handler. - switch (tmpVariables[3]) { + short alg = KMEnumTag.getValue(KMType.ALGORITHM, data[KEY_PARAMETERS]); + switch (alg) { case KMType.RSA: importRSAKey(scratchPad); break; @@ -2904,77 +2995,67 @@ private void importKey(APDU apdu, byte[] scratchPad) { KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM); break; } - // create key blob + makeKeyCharacteristics( scratchPad); + generateAttestation(data[ATTEST_KEY_BLOB], data[ATTEST_KEY_PARAMS],scratchPad); createEncryptedKeyBlob(scratchPad); - // prepare the response - tmpVariables[0] = KMArray.instance((short) 3); - KMArray.cast(tmpVariables[0]).add((short) 0, buildErrorStatus(KMError.OK)); - KMArray.cast(tmpVariables[0]).add((short) 1, data[KEY_BLOB]); - KMArray.cast(tmpVariables[0]).add((short) 2, data[KEY_CHARACTERISTICS]); - - bufferProp[BUF_START_OFFSET] = repository.allocAvailableMemory(); - // Encode the response - bufferProp[BUF_LEN_OFFSET] = encoder.encode(tmpVariables[0], (byte[]) bufferRef[0], bufferProp[BUF_START_OFFSET]); - sendOutgoing(apdu); + short resp = KMArray.instance((short) 4); + KMArray.cast(resp).add((short) 0, KMInteger.uint_16(KMError.OK)); + KMArray.cast(resp).add((short) 1, data[KEY_BLOB]); + KMArray.cast(resp).add((short) 2, data[KEY_CHARACTERISTICS]); + KMArray.cast(resp).add((short) 3, data[CERTIFICATE]); + sendOutgoing(apdu, resp); } private void importECKeys(byte[] scratchPad) { // Decode key material - tmpVariables[0] = KMArray.instance((short) 2); - KMArray.cast(tmpVariables[0]).add((short) 0, KMByteBlob.exp()); // secret - KMArray.cast(tmpVariables[0]).add((short) 1, KMByteBlob.exp()); // public key - tmpVariables[0] = - decoder.decode( - tmpVariables[0], - KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).getBuffer(), - KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).getStartOff(), - KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).length()); - data[SECRET] = KMArray.cast(tmpVariables[0]).get((short) 0); - data[PUB_KEY] = KMArray.cast(tmpVariables[0]).get((short) 1); + KMPKCS8Decoder pkcs8 = KMPKCS8Decoder.instance(); + short keyBlob = pkcs8.decodeEc(data[IMPORTED_KEY_BLOB]); + data[PUB_KEY] = KMArray.cast(keyBlob).get((short) 0); + data[SECRET] = KMArray.cast(keyBlob).get((short) 1); // initialize 256 bit p256 key for given private key and public key. - tmpVariables[4] = 0; // index for update list in scratchPad - - // check whether the keysize tag is present in key parameters. - tmpVariables[2] = + short index = 0; + // check whether the key size tag is present in key parameters. + short keySize = KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); - if (tmpVariables[2] != KMType.INVALID_VALUE) { + if (keySize != KMType.INVALID_VALUE) { // As per NIST.SP.800-186 page 9, secret for 256 curve should be between // 256-383 if (((256 <= (short) (KMByteBlob.cast(data[SECRET]).length() * 8)) && (383 >= (short) (KMByteBlob.cast(data[SECRET]).length() * 8))) - ^ tmpVariables[2] == 256) { + ^ keySize == 256) { KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH); } - if (tmpVariables[2] != 256) { + if (keySize != 256) { KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); } } else { // add the key size to scratchPad - tmpVariables[5] = KMInteger.uint_16((short) 256); - tmpVariables[6] = KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, tmpVariables[5]); - Util.setShort(scratchPad, tmpVariables[4], tmpVariables[6]); - tmpVariables[4] += 2; + keySize = KMInteger.uint_16((short) 256); + keySize = KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, keySize); + Util.setShort(scratchPad, index, keySize); + index += 2; } // check the curve if present in key parameters. - tmpVariables[3] = KMEnumTag.getValue(KMType.ECCURVE, data[KEY_PARAMETERS]); - if (tmpVariables[3] != KMType.INVALID_VALUE) { + short curve = KMEnumTag.getValue(KMType.ECCURVE, data[KEY_PARAMETERS]); + if (curve != KMType.INVALID_VALUE) { // As per NIST.SP.800-186 page 9, secret length for 256 curve should be between // 256-383 if (((256 <= (short) (KMByteBlob.cast(data[SECRET]).length() * 8)) && (383 >= (short) (KMByteBlob.cast(data[SECRET]).length() * 8))) - ^ tmpVariables[3] == KMType.P_256) { + ^ curve == KMType.P_256) { KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH); } - if (tmpVariables[3] != KMType.P_256) { + if (curve != KMType.P_256) { KMException.throwIt(KMError.UNSUPPORTED_EC_CURVE); } } else { // add the curve to scratchPad - tmpVariables[5] = KMEnumTag.instance(KMType.ECCURVE, KMType.P_256); - Util.setShort(scratchPad, tmpVariables[4], tmpVariables[5]); - tmpVariables[4] += 2; + curve = KMEnumTag.instance(KMType.ECCURVE, KMType.P_256); + Util.setShort(scratchPad, index, curve); + index += 2; } + // Check whether key can be created seProvider.importAsymmetricKey( KMType.EC, @@ -2986,7 +3067,7 @@ private void importECKeys(byte[] scratchPad) { KMByteBlob.cast(data[PUB_KEY]).length()); // add scratch pad to key parameters - updateKeyParameters(scratchPad, tmpVariables[4]); + updateKeyParameters(scratchPad, index); // validate updated key parameters. validateECKeys(); data[KEY_BLOB] = KMArray.instance((short) 5); @@ -2995,127 +3076,105 @@ private void importECKeys(byte[] scratchPad) { private void importHmacKey(byte[] scratchPad) { // Get Key - tmpVariables[0] = KMArray.instance((short) 1); - KMArray.cast(tmpVariables[0]).add((short) 0, KMByteBlob.exp()); // secret - tmpVariables[0] = - decoder.decode( - tmpVariables[0], - KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).getBuffer(), - KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).getStartOff(), - KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).length()); - data[SECRET] = KMArray.cast(tmpVariables[0]).get((short) 0); + data[SECRET] = data[IMPORTED_KEY_BLOB]; // create HMAC key of up to 512 bit - - tmpVariables[4] = 0; // index in scratchPad for update params + short index = 0; // index in scratchPad for update params // check the keysize tag if present in key parameters. - tmpVariables[2] = + short keysize = KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); - if (tmpVariables[2] != KMType.INVALID_VALUE) { - if (!(tmpVariables[2] >= 64 && tmpVariables[2] <= 512 && tmpVariables[2] % 8 == 0)) { + if (keysize != KMType.INVALID_VALUE) { + if (!(keysize >= 64 && keysize <= 512 && keysize % 8 == 0)) { KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH); } } else { // add the key size to scratchPad - tmpVariables[5] = KMInteger.uint_16((short) (KMByteBlob.cast(data[SECRET]).length() * 8)); - tmpVariables[6] = KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, tmpVariables[5]); - Util.setShort(scratchPad, tmpVariables[4], tmpVariables[6]); - tmpVariables[4] += 2; + keysize = KMInteger.uint_16((short) (KMByteBlob.cast(data[SECRET]).length() * 8)); + short keySizeTag = KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, keysize); + Util.setShort(scratchPad, index, keySizeTag); + index += 2; } // Check whether key can be created seProvider.importSymmetricKey( KMType.HMAC, - tmpVariables[2], + keysize, KMByteBlob.cast(data[SECRET]).getBuffer(), KMByteBlob.cast(data[SECRET]).getStartOff(), KMByteBlob.cast(data[SECRET]).length()); // update the key parameters list - updateKeyParameters(scratchPad, tmpVariables[4]); + updateKeyParameters(scratchPad, index); // validate HMAC Key parameters validateHmacKey(); - data[KEY_BLOB] = KMArray.instance((short) 4); } private void importTDESKey(byte[] scratchPad) { // Decode Key Material - tmpVariables[0] = KMArray.instance((short) 1); - KMArray.cast(tmpVariables[0]).add((short) 0, KMByteBlob.exp()); // secret - tmpVariables[0] = - decoder.decode( - tmpVariables[0], - KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).getBuffer(), - KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).getStartOff(), - KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).length()); - data[SECRET] = KMArray.cast(tmpVariables[0]).get((short) 0); - tmpVariables[4] = 0; // index in scratchPad for update params + data[SECRET] = data[IMPORTED_KEY_BLOB]; + short index = 0; // index in scratchPad for update params // check the keysize tag if present in key parameters. - tmpVariables[2] = + short keysize = KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); - if (tmpVariables[2] != KMType.INVALID_VALUE) { - if (tmpVariables[2] != 168) { - KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH); + if (keysize != KMType.INVALID_VALUE) { + if (keysize != 168) { + KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); } } else { // add the key size to scratchPad - tmpVariables[5] = KMInteger.uint_16((short) 168); - tmpVariables[6] = KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, tmpVariables[5]); - Util.setShort(scratchPad, tmpVariables[4], tmpVariables[6]); - tmpVariables[4] += 2; + keysize = KMInteger.uint_16((short) 168); + short keysizeTag = KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, keysize); + Util.setShort(scratchPad, index, keysizeTag); + index += 2; } // Check whether key can be created seProvider.importSymmetricKey( KMType.DES, - tmpVariables[2], + keysize, KMByteBlob.cast(data[SECRET]).getBuffer(), KMByteBlob.cast(data[SECRET]).getStartOff(), KMByteBlob.cast(data[SECRET]).length()); - // update the key parameters list - updateKeyParameters(scratchPad, tmpVariables[4]); + updateKeyParameters(scratchPad, index); // validate TDES Key parameters validateTDESKey(); - data[KEY_BLOB] = KMArray.instance((short) 4); } + private void validateAesKeySize(short keySizeBits) { + if (keySizeBits != 128 && keySizeBits != 256) { + KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); + } + } + private void importAESKey(byte[] scratchPad) { // Get Key - tmpVariables[0] = KMArray.instance((short) 1); - KMArray.cast(tmpVariables[0]).add((short) 0, KMByteBlob.exp()); // secret - tmpVariables[0] = - decoder.decode( - tmpVariables[0], - KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).getBuffer(), - KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).getStartOff(), - KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).length()); - data[SECRET] = KMArray.cast(tmpVariables[0]).get((short) 0); + data[SECRET] = data[IMPORTED_KEY_BLOB]; // create 128 or 256 bit AES key - tmpVariables[4] = 0; // index in scratchPad for update params + short index = 0; // index in scratchPad for update params // check the keysize tag if present in key parameters. - tmpVariables[2] = + short keysize = KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); - if (tmpVariables[2] != KMType.INVALID_VALUE) { - if (tmpVariables[2] != 128 && tmpVariables[2] != 256) { - KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH); - } + if (keysize != KMType.INVALID_VALUE) { + validateAesKeySize(keysize); } else { // add the key size to scratchPad - tmpVariables[5] = KMInteger.uint_16(KMByteBlob.cast(data[SECRET]).length()); - tmpVariables[6] = KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, tmpVariables[5]); - Util.setShort(scratchPad, tmpVariables[4], tmpVariables[6]); - tmpVariables[4] += 2; + keysize = (short) ( 8 * KMByteBlob.cast(data[SECRET]).length()); + validateAesKeySize(keysize); + keysize = KMInteger.uint_16(keysize); + short keysizeTag = KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, keysize); + Util.setShort(scratchPad, index, keysizeTag); + index += 2; } // Check whether key can be created seProvider.importSymmetricKey( KMType.AES, - tmpVariables[2], + keysize, KMByteBlob.cast(data[SECRET]).getBuffer(), KMByteBlob.cast(data[SECRET]).getStartOff(), KMByteBlob.cast(data[SECRET]).length()); // update the key parameters list - updateKeyParameters(scratchPad, tmpVariables[4]); + updateKeyParameters(scratchPad, index); // validate AES Key parameters validateAESKey(); data[KEY_BLOB] = KMArray.instance((short) 4); @@ -3123,28 +3182,29 @@ private void importAESKey(byte[] scratchPad) { private void importRSAKey(byte[] scratchPad) { // Decode key material - tmpVariables[0] = KMArray.instance((short) 2); - KMArray.cast(tmpVariables[0]).add((short) 0, KMByteBlob.exp()); // secret = private exponent - KMArray.cast(tmpVariables[0]).add((short) 1, KMByteBlob.exp()); // modulus - tmpVariables[0] = - decoder.decode( - tmpVariables[0], - KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).getBuffer(), - KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).getStartOff(), - KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).length()); - data[SECRET] = KMArray.cast(tmpVariables[0]).get((short) 0); - data[PUB_KEY] = KMArray.cast(tmpVariables[0]).get((short) 1); - tmpVariables[4] = 0; // index in scratchPad for update parameters. + KMPKCS8Decoder pkcs8 = KMPKCS8Decoder.instance(); + short keyblob = pkcs8.decodeRsa(data[IMPORTED_KEY_BLOB]); + data[PUB_KEY] = KMArray.cast(keyblob).get((short) 0); + short pubKeyExp = KMArray.cast(keyblob).get((short)1); + data[SECRET] = KMArray.cast(keyblob).get((short) 2); + if(F4.length != KMByteBlob.cast(pubKeyExp).length()){ + KMException.throwIt(KMError.INVALID_KEY_BLOB); + } + if(Util.arrayCompare(F4, (short)0, KMByteBlob.cast(pubKeyExp).getBuffer(), + KMByteBlob.cast(pubKeyExp).getStartOff(), (short)F4.length) != 0){ + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + short index = 0; // index in scratchPad for update parameters. // validate public exponent if present in key params - it must be 0x010001 - tmpVariables[2] = + short len = KMIntegerTag.getValue( scratchPad, (short) 10, // using offset 10 as first 10 bytes reserved for update params KMType.ULONG_TAG, KMType.RSA_PUBLIC_EXPONENT, data[KEY_PARAMETERS]); - if (tmpVariables[2] != KMTag.INVALID_VALUE) { - if (tmpVariables[2] != 4 + if (len != KMTag.INVALID_VALUE) { + if (len != 4 || Util.getShort(scratchPad, (short) 10) != 0x01 || Util.getShort(scratchPad, (short) 12) != 0x01) { KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH); @@ -3153,27 +3213,27 @@ private void importRSAKey(byte[] scratchPad) { // add public exponent to scratchPad Util.setShort(scratchPad, (short) 10, (short) 0x01); Util.setShort(scratchPad, (short) 12, (short) 0x01); - tmpVariables[5] = KMInteger.uint_32(scratchPad, (short) 10); - tmpVariables[6] = - KMIntegerTag.instance(KMType.ULONG_TAG, KMType.RSA_PUBLIC_EXPONENT, tmpVariables[5]); - Util.setShort(scratchPad, tmpVariables[4], tmpVariables[6]); - tmpVariables[4] += 2; + pubKeyExp = KMInteger.uint_32(scratchPad, (short) 10); + pubKeyExp = + KMIntegerTag.instance(KMType.ULONG_TAG, KMType.RSA_PUBLIC_EXPONENT, pubKeyExp); + Util.setShort(scratchPad, index, pubKeyExp); + index += 2; } // check the keysize tag if present in key parameters. - tmpVariables[2] = + short keysize = KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); - if (tmpVariables[2] != KMType.INVALID_VALUE) { - if (tmpVariables[2] != 2048 - || tmpVariables[2] != (short) (KMByteBlob.cast(data[SECRET]).length() * 8)) { + if (keysize != KMType.INVALID_VALUE) { + if (keysize != 2048 + || keysize != (short) (KMByteBlob.cast(data[SECRET]).length() * 8)) { KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH); } } else { // add the key size to scratchPad - tmpVariables[5] = KMInteger.uint_16((short) 2048); - tmpVariables[6] = KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, tmpVariables[5]); - Util.setShort(scratchPad, tmpVariables[4], tmpVariables[6]); - tmpVariables[4] += 2; + keysize = KMInteger.uint_16((short) 2048); + keysize = KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, keysize); + Util.setShort(scratchPad, index, keysize); + index += 2; } // Check whether key can be created @@ -3187,183 +3247,142 @@ private void importRSAKey(byte[] scratchPad) { KMByteBlob.cast(data[PUB_KEY]).length()); // update the key parameters list - updateKeyParameters(scratchPad, tmpVariables[4]); + updateKeyParameters(scratchPad, index); // validate RSA Key parameters validateRSAKey(scratchPad); data[KEY_BLOB] = KMArray.instance((short) 5); KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_PUB_KEY, data[PUB_KEY]); } - private void updateKeyParameters(byte[] ptrArr, short len) { + private void updateKeyParameters(byte[] newParams, short len) { if (len == 0) { return; // nothing to update } // Create Update Param array and copy current params - tmpVariables[0] = KMKeyParameters.cast(data[KEY_PARAMETERS]).getVals(); - tmpVariables[1] = (short) (KMArray.cast(tmpVariables[0]).length() + (short) (len / 2)); - tmpVariables[1] = KMArray.instance(tmpVariables[1]); // update params - tmpVariables[2] = KMArray.cast(tmpVariables[0]).length(); - tmpVariables[3] = 0; + short params = KMKeyParameters.cast(data[KEY_PARAMETERS]).getVals(); + len = (short) (KMArray.cast(params).length() + (short) (len / 2)); + short updatedParams = KMArray.instance(len); // update params + + len = KMArray.cast(params).length(); + short index = 0; + // copy the existing key parameters to updated array - while (tmpVariables[3] < tmpVariables[2]) { - tmpVariables[4] = KMArray.cast(tmpVariables[0]).get(tmpVariables[3]); - KMArray.cast(tmpVariables[1]).add(tmpVariables[3], tmpVariables[4]); - tmpVariables[3]++; + while (index < len) { + short tag = KMArray.cast(params).get(index); + KMArray.cast(updatedParams).add(index, tag); + index++; } + // copy new parameters to updated array - tmpVariables[2] = KMArray.cast(tmpVariables[1]).length(); - tmpVariables[5] = 0; // index in ptrArr - while (tmpVariables[3] < tmpVariables[2]) { - tmpVariables[4] = Util.getShort(ptrArr, tmpVariables[5]); - KMArray.cast(tmpVariables[1]).add(tmpVariables[3], tmpVariables[4]); - tmpVariables[3]++; - tmpVariables[5] += 2; + len = KMArray.cast(updatedParams).length(); + short newParamIndex = 0; // index in ptrArr + while (index < len) { + short tag = Util.getShort(newParams, newParamIndex); + KMArray.cast(updatedParams).add(index, tag); + index++; + newParamIndex += 2; } // replace with updated key parameters. - data[KEY_PARAMETERS] = KMKeyParameters.instance(tmpVariables[1]); + data[KEY_PARAMETERS] = KMKeyParameters.instance(updatedParams); + } + + private short initStrongBoxCmd(APDU apdu){ + short cmd = KMArray.instance((short) 3); + KMArray.cast(cmd).add((short) 0, KMInteger.exp()); //OS version + KMArray.cast(cmd).add((short) 1, KMInteger.exp()); //OS patch level + KMArray.cast(cmd).add((short) 2, KMInteger.exp()); //Vendor patch level + return receiveIncoming(apdu, cmd); } // This command is executed to set the boot parameters. // releaseAllOperations has to be called on every boot, so - // it is called from inside setBootParams. Later in future if - // setBootParams is removed, then make sure that releaseAllOperations + // it is called from inside initStrongBoxCmd. Later in future if + // initStrongBoxCmd is removed, then make sure that releaseAllOperations // is moved to a place where it is called on every boot. - private void processSetBootParamsCmd(APDU apdu) { - receiveIncoming(apdu); + private void processInitStrongBoxCmd(APDU apdu) { + short cmd = initStrongBoxCmd(apdu); byte[] scratchPad = apdu.getBuffer(); - // Argument 0 Boot Patch level - tmpVariables[0] = KMInteger.exp(); - // Argument 1 Verified Boot Key - tmpVariables[1] = KMByteBlob.exp(); - // Argument 2 Verified Boot Hash - tmpVariables[2] = KMByteBlob.exp(); - // Argument 3 Verified Boot State - tmpVariables[3] = KMEnum.instance(KMType.VERIFIED_BOOT_STATE); - // Argument 4 Device Locked - tmpVariables[4] = KMEnum.instance(KMType.DEVICE_LOCKED); - // Array of e4pected arguments - short argsProto = KMArray.instance((short) 5); - 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]); - // Decode the arguments - short args = decoder.decode(argsProto, (byte[]) bufferRef[0], bufferProp[BUF_START_OFFSET], bufferProp[BUF_LEN_OFFSET]); - //reclaim memory - repository.reclaimMemory(bufferProp[BUF_LEN_OFFSET]); - - tmpVariables[0] = KMArray.cast(args).get((short) 0); - tmpVariables[1] = KMArray.cast(args).get((short) 1); - tmpVariables[2] = KMArray.cast(args).get((short) 2); - tmpVariables[3] = KMArray.cast(args).get((short) 3); - tmpVariables[4] = KMArray.cast(args).get((short) 4); - if (KMByteBlob.cast(tmpVariables[1]).length() > KMRepository.BOOT_KEY_MAX_SIZE) { - KMException.throwIt(KMError.INVALID_ARGUMENT); - } - if (KMByteBlob.cast(tmpVariables[2]).length() > KMRepository.BOOT_HASH_MAX_SIZE) { - KMException.throwIt(KMError.INVALID_ARGUMENT); - } - - repository.setBootPatchLevel( - KMInteger.cast(tmpVariables[0]).getBuffer(), - KMInteger.cast(tmpVariables[0]).getStartOff(), - KMInteger.cast(tmpVariables[0]).length()); - repository.setVerifiedBootKey( - KMByteBlob.cast(tmpVariables[1]).getBuffer(), - KMByteBlob.cast(tmpVariables[1]).getStartOff(), - KMByteBlob.cast(tmpVariables[1]).length()); - - repository.setVerifiedBootHash( - KMByteBlob.cast(tmpVariables[2]).getBuffer(), - KMByteBlob.cast(tmpVariables[2]).getStartOff(), - KMByteBlob.cast(tmpVariables[2]).length()); + short osVersion = KMArray.cast(cmd).get((short) 0); + short osPatchLevel = KMArray.cast(cmd).get((short) 1); + short vendorPatchLevel = KMArray.cast(cmd).get((short) 2); + setOsVersion(osVersion); + setOsPatchLevel(osPatchLevel); + setVendorPatchLevel(vendorPatchLevel); + } - byte enumVal = KMEnum.cast(tmpVariables[3]).getVal(); - repository.setBootState(enumVal); + public void reboot(){ + // Clear the Computed SharedHmac and Hmac nonce from persistent memory. + repository.clearComputedHmac(); + repository.clearHmacNonce(); + //Clear all the operation state. + releaseAllOperations(); + // Hmac is cleared, so generate a new Hmac nonce. + initHmacNonceAndSeed(); + } - enumVal = KMEnum.cast(tmpVariables[4]).getVal(); - repository.setBootloaderLocked(enumVal == KMType.DEVICE_LOCKED_TRUE); + protected void initSystemBootParams(short osVersion, + short osPatchLevel, short vendorPatchLevel, short bootPatchLevel){ + osVersion = KMInteger.uint_16(osVersion); + osPatchLevel = KMInteger.uint_16(osPatchLevel); + vendorPatchLevel = KMInteger.uint_16((short) vendorPatchLevel); + setOsVersion(osVersion); + setOsPatchLevel(osPatchLevel); + setVendorPatchLevel(vendorPatchLevel); + } - // Clear Android system properties expect boot patch level as it is - // already set. - repository.clearAndroidSystemProperties(); + protected void setOsVersion(short version){ + repository.setOsVersion( + KMInteger.cast(version).getBuffer(), + KMInteger.cast(version).getStartOff(), + KMInteger.cast(version).length()); + } - // Clear the Computed SharedHmac and Hmac nonce from persistent memory. - repository.clearComputedHmac(); - repository.clearHmacNonce(); + protected void setOsPatchLevel(short patch){ + repository.setOsPatch( + KMInteger.cast(patch).getBuffer(), + KMInteger.cast(patch).getStartOff(), + KMInteger.cast(patch).length()); + } - //Clear all the operation state. - repository.releaseAllOperations(); + protected void setVendorPatchLevel(short patch){ + repository.setVendorPatchLevel( + KMInteger.cast(patch).getBuffer(), + KMInteger.cast(patch).getStartOff(), + KMInteger.cast(patch).length()); + } - // 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 short generateKeyCmd(APDU apdu){ + short params = KMKeyParameters.expAny(); + short blob = KMByteBlob.exp(); + // Array of expected arguments + short cmd = KMArray.instance((short) 4); + KMArray.cast(cmd).add((short) 0, params); //key params + KMArray.cast(cmd).add((short) 1, blob); //attest key + KMArray.cast(cmd).add((short) 2, params); //attest key params + KMArray.cast(cmd).add((short) 3, blob); //issuer + return receiveIncoming(apdu, cmd); } - private static void processGenerateKey(APDU apdu) { + private void processGenerateKey(APDU apdu) { // Receive the incoming request fully from the master into buffer. - receiveIncoming(apdu); + short cmd = generateKeyCmd(apdu); // Re-purpose the apdu buffer as scratch pad. byte[] scratchPad = apdu.getBuffer(); - // Argument - tmpVariables[0] = KMKeyParameters.exp(); - // Array of expected arguments - tmpVariables[1] = KMArray.instance((short) 1); - KMArray.cast(tmpVariables[1]).add((short) 0, tmpVariables[0]); - // Decode the argument - tmpVariables[2] = decoder.decode(tmpVariables[1], (byte[]) bufferRef[0], bufferProp[BUF_START_OFFSET], bufferProp[BUF_LEN_OFFSET]); - //reclaim memory - repository.reclaimMemory(bufferProp[BUF_LEN_OFFSET]); - - data[KEY_PARAMETERS] = KMArray.cast(tmpVariables[2]).get((short) 0); - // Check if EarlyBootEnded tag is present. - tmpVariables[0] = - KMKeyParameters.findTag(KMType.BOOL_TAG, KMType.EARLY_BOOT_ONLY, data[KEY_PARAMETERS]); - if (tmpVariables[0] != KMType.INVALID_VALUE) { - KMException.throwIt(KMError.EARLY_BOOT_ENDED); - } - // Check if rollback resistance tag is present - tmpVariables[0] = - KMKeyParameters.findTag(KMType.BOOL_TAG, KMType.ROLLBACK_RESISTANCE, data[KEY_PARAMETERS]); - if (tmpVariables[0] != KMType.INVALID_VALUE) { - KMException.throwIt(KMError.ROLLBACK_RESISTANCE_UNAVAILABLE); - } - // Bootloader only not supported - tmpVariables[0] = - KMKeyParameters.findTag(KMType.BOOL_TAG, KMType.BOOTLOADER_ONLY, data[KEY_PARAMETERS]); - if (tmpVariables[0] != KMType.INVALID_VALUE) { - KMException.throwIt(KMError.INVALID_KEY_BLOB); - } - // get algorithm - tmpVariables[3] = KMEnumTag.getValue(KMType.ALGORITHM, data[KEY_PARAMETERS]); - if (tmpVariables[3] == KMType.INVALID_VALUE) { - KMException.throwIt(KMError.INVALID_ARGUMENT); - } - tmpVariables[4] = - KMKeyParameters.findTag(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); - if (tmpVariables[4] != KMType.INVALID_VALUE) { - if (!KMIntegerTag.cast(tmpVariables[4]).isValidKeySize((byte) tmpVariables[3])) { - KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); - } - } - // Only STANDALONE is supported for BLOB_USAGE_REQ tag. - tmpVariables[0] = - KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.BLOB_USAGE_REQ, data[KEY_PARAMETERS]); - if (tmpVariables[0] != KMType.INVALID_VALUE) { - tmpVariables[0] = KMEnumTag.getValue(KMType.BLOB_USAGE_REQ, data[KEY_PARAMETERS]); - if (tmpVariables[0] != KMType.STANDALONE) { - KMException.throwIt(KMError.UNSUPPORTED_TAG); - } - } - //Check if the tags are supported. - if (KMKeyParameters.hasUnsupportedTags(data[KEY_PARAMETERS])) { - KMException.throwIt(KMError.UNSUPPORTED_TAG); - } - + data[KEY_PARAMETERS] = KMArray.cast(cmd).get((short) 0); + data[ATTEST_KEY_BLOB] = KMArray.cast(cmd).get((short) 1); + data[ATTEST_KEY_PARAMS] = KMArray.cast(cmd).get((short) 2); + data[ATTEST_KEY_ISSUER] = KMArray.cast(cmd).get((short) 3); + data[CERTIFICATE] = KMArray.instance((short)0); //by default the cert is empty. + // ROLLBACK_RESISTANCE not supported. + KMTag.assertAbsence(data[KEY_PARAMETERS], KMType.BOOL_TAG,KMType.ROLLBACK_RESISTANCE, KMError.ROLLBACK_RESISTANCE_UNAVAILABLE); + // BOOTLOADER_ONLY keys not supported. + KMTag.assertAbsence(data[KEY_PARAMETERS], KMType.BOOL_TAG, KMType.BOOTLOADER_ONLY, KMError.INVALID_KEY_BLOB); + // Algorithm must be present + KMTag.assertPresence(data[KEY_PARAMETERS], KMType.ENUM_TAG, KMType.ALGORITHM, KMError.INVALID_ARGUMENT); + short alg = KMEnumTag.getValue(KMType.ALGORITHM, data[KEY_PARAMETERS]); // Check algorithm and dispatch to appropriate handler. - switch (tmpVariables[3]) { + switch (alg) { case KMType.RSA: generateRSAKey(scratchPad); break; @@ -3383,47 +3402,150 @@ private static void processGenerateKey(APDU apdu) { KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM); break; } - // create key blob + + // create key blob and associated attestation. data[ORIGIN] = KMType.GENERATED; + makeKeyCharacteristics(scratchPad); + generateAttestation(data[ATTEST_KEY_BLOB], data[ATTEST_KEY_PARAMS],scratchPad); createEncryptedKeyBlob(scratchPad); - // prepare the response - tmpVariables[0] = KMArray.instance((short) 3); - KMArray.cast(tmpVariables[0]).add((short) 0, buildErrorStatus(KMError.OK)); - KMArray.cast(tmpVariables[0]).add((short) 1, data[KEY_BLOB]); - KMArray.cast(tmpVariables[0]).add((short) 2, data[KEY_CHARACTERISTICS]); + short resp = KMArray.instance((short) 4); + KMArray.cast(resp).add((short) 0, KMInteger.uint_16(KMError.OK)); + KMArray.cast(resp).add((short) 1, data[KEY_BLOB]); + KMArray.cast(resp).add((short) 2, data[KEY_CHARACTERISTICS]); + KMArray.cast(resp).add((short) 3, data[CERTIFICATE]); + sendOutgoing(apdu, resp); + } + + private short getAttestationMode(short attKeyBlob, short attChallenge){ + short alg = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ALGORITHM, data[KEY_PARAMETERS]); + short mode = KMType.NO_CERT; + if(KMEnumTag.cast(alg).getValue() != KMType.RSA && + KMEnumTag.cast(alg).getValue() != KMType.EC){ + return mode; + } + // If attestation keyblob preset + if (attKeyBlob != KMType.INVALID_VALUE && KMByteBlob.cast(attKeyBlob).length() > 0) { + // No attestation challenge present then it is an error + if (attChallenge == KMType.INVALID_VALUE || KMByteBlob.cast(attChallenge).length() <= 0) { + KMException.throwIt(KMError.ATTESTATION_CHALLENGE_MISSING); + } else { + mode = KMType.ATTESTATION_CERT; + } + }else{ // no attestation key blob + // Attestation challenge present then it is an error because no factory provisioned attest key + if (attChallenge != KMType.INVALID_VALUE && KMByteBlob.cast(attChallenge).length() > 0) { + KMException.throwIt(KMError.ATTESTATION_KEYS_NOT_PROVISIONED); + } else if(KMEnumArrayTag.contains(KMType.PURPOSE, KMType.SIGN, data[KEY_PARAMETERS])) { + mode = KMType.SELF_SIGNED_CERT; + }else{ + mode = KMType.FAKE_CERT; + } + } + return mode; + } + + private void generateAttestation(short attKeyBlob, short attKeyParam, byte[] scratchPad){ + // Device unique attestation not supported + KMTag.assertAbsence(data[KEY_PARAMETERS],KMType.BOOL_TAG,KMType.DEVICE_UNIQUE_ATTESTATION,KMError.CANNOT_ATTEST_IDS); + // Read attestation challenge if present + short attChallenge = + KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.ATTESTATION_CHALLENGE, data[KEY_PARAMETERS]); + if(attChallenge != KMType.INVALID_VALUE){ + attChallenge = KMByteTag.cast(attChallenge).getValue(); + } + // No attestation required for symmetric keys + short mode = getAttestationMode(attKeyBlob, attChallenge); + KMAttestationCert cert = null; + + switch (mode){ + case KMType.ATTESTATION_CERT: + cert = makeAttestationCert(attKeyBlob,attKeyParam, attChallenge, data[ATTEST_KEY_ISSUER],data[SB_PARAMETERS], + data[SW_PARAMETERS], scratchPad); + break; + case KMType.SELF_SIGNED_CERT: + //cert = makeCert(attKeyBlob, attKeyParam, scratchPad); + cert = makeSelfSignedCert(data[SECRET], data[PUB_KEY],scratchPad); + break; + case KMType.FAKE_CERT: + //cert = makeCert(attKeyBlob, attKeyParam, scratchPad); + cert = makeSelfSignedCert(KMType.INVALID_VALUE, data[PUB_KEY], scratchPad); + break; + default: + data[CERTIFICATE] = KMArray.instance((short)0); + return; + } + // Allocate memory + short certData = KMByteBlob.instance(MAX_CERT_SIZE); + + cert.buffer(KMByteBlob.cast(certData).getBuffer(), + KMByteBlob.cast(certData).getStartOff(), + KMByteBlob.cast(certData).length()); + + // Build the certificate - this will sign the cert + cert.build(); + // Adjust the start and length of the certificate in the blob + KMByteBlob.cast(certData).setStartOff(cert.getCertStart()); + KMByteBlob.cast(certData).setLength(cert.getCertLength()); + // Initialize the certificate as array of blob + data[CERTIFICATE] = KMArray.instance((short)1); + KMArray.cast(data[CERTIFICATE]).add((short)0, certData); + +/* + boolean rsaCert = KMEnumTag.cast(alg).getValue() == KMType.EC; + short appId = KMType.INVALID_VALUE; + short appData = KMType.INVALID_VALUE; + if(attKeyParam != KMType.INVALID_VALUE) { + appId = + KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.APPLICATION_ID, attKeyParam); + if (appId != KMTag.INVALID_VALUE) { + appId = KMByteTag.cast(appId).getValue(); + } + appData = + KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.APPLICATION_DATA, attKeyParam); + if (appData != KMTag.INVALID_VALUE) { + appData = KMByteTag.cast(appData).getValue(); + } + } - bufferProp[BUF_START_OFFSET] = repository.allocAvailableMemory(); - // Encode the response - bufferProp[BUF_LEN_OFFSET] = encoder.encode(tmpVariables[0], (byte[]) bufferRef[0], bufferProp[BUF_START_OFFSET]); + KMAttestationCert cert = makeCert(attKeyBlob, appId, appData, scratchPad); + if(cert == null) {// No certificate + data[CERTIFICATE] = KMArray.instance((short)0); + return; + } - sendOutgoing(apdu); + // Allocate memory + short certData = KMByteBlob.instance(MAX_CERT_SIZE); + cert.buffer(KMByteBlob.cast(certData).getBuffer(), + KMByteBlob.cast(certData).getStartOff(), + KMByteBlob.cast(certData).length()); + // Build the certificate - this will sign the cert + cert.build(); + // Adjust the start and length of the certificate in the blob + KMByteBlob.cast(certData).setStartOff(cert.getCertStart()); + KMByteBlob.cast(certData).setLength(cert.getCertLength()); + + // Initialize the certificate as array of blob + data[CERTIFICATE] = KMArray.instance((short)1); + KMArray.cast(data[CERTIFICATE]).add((short)0, certData); + */ } + /** + * 1) If attestation key is present and attestation challenge is absent then it is an error. + * 2) If attestation key is absent and attestation challenge is present then it is an error as + * factory provisioned attestation key is not supported. + * 3) If both are present and issuer is absent or attest key purpose is not ATTEST_KEY then it is an error. + * 4) If the generated/imported keys are RSA or EC then validity period must be specified. + * Device Unique Attestation is not supported. + */ + private static void validateRSAKey(byte[] scratchPad) { // Read key size - tmpVariables[0] = - KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); - if (tmpVariables[0] == KMTag.INVALID_VALUE) { - KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); - } - if (tmpVariables[0] != 2048) { + if(!KMTag.isValidKeySize(data[KEY_PARAMETERS])){ KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); } - // Read public exponent into scratch pad - tmpVariables[1] = - KMIntegerTag.getValue( - scratchPad, - (short) 0, - KMType.ULONG_TAG, - KMType.RSA_PUBLIC_EXPONENT, - data[KEY_PARAMETERS]); - if ((tmpVariables[1] == KMTag.INVALID_VALUE) || (tmpVariables[1] != 4)) { - KMException.throwIt(KMError.INVALID_ARGUMENT); - } - // Only exponent support is F4 - 65537 which is 0x00010001. - if (Util.getShort(scratchPad, (short) 0) != 0x01 - || Util.getShort(scratchPad, (short) 2) != 0x01) { + if(!KMTag.isValidPublicExponent(data[KEY_PARAMETERS])){ KMException.throwIt(KMError.INVALID_ARGUMENT); } } @@ -3452,34 +3574,26 @@ private static void generateRSAKey(byte[] scratchPad) { private static void validateAESKey() { // Read key size - tmpVariables[0] = - KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); - if (tmpVariables[0] == KMTag.INVALID_VALUE) { - KMException.throwIt(KMError.INVALID_ARGUMENT); - } - if ((tmpVariables[0] != 256) && (tmpVariables[0] != 128)) { + if(!KMTag.isValidKeySize(data[KEY_PARAMETERS])){ KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); } // Read Block mode - array of byte values - tmpVariables[1] = - KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.BLOCK_MODE, data[KEY_PARAMETERS]); - if (tmpVariables[1] != KMTag.INVALID_VALUE) { // block mode specified - // Find Minimum Mac length - tmpVariables[2] = - KMKeyParameters.findTag(KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, data[KEY_PARAMETERS]); - // If block modes contain GCM mode - if (KMEnumArrayTag.cast(tmpVariables[1]).contains(KMType.GCM)) { - // minimum mac length must be specified - if (tmpVariables[2] == KMTag.INVALID_VALUE) { - KMException.throwIt(KMError.MISSING_MIN_MAC_LENGTH); - } - tmpVariables[3] = KMIntegerTag.cast(tmpVariables[2]).getValue(); + if(KMTag.isPresent(data[KEY_PARAMETERS],KMType.ENUM_ARRAY_TAG,KMType.BLOCK_MODE)){ + short blockModes = + KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.BLOCK_MODE, data[KEY_PARAMETERS]); + // If it is a GCM mode + if (KMEnumArrayTag.cast(blockModes).contains(KMType.GCM)){ + // Min mac length must be present + KMTag.assertPresence(data[KEY_PARAMETERS],KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, KMError.MISSING_MIN_MAC_LENGTH); + short macLength = + KMKeyParameters.findTag(KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, data[KEY_PARAMETERS]); + macLength = KMIntegerTag.cast(macLength).getValue(); // Validate the MIN_MAC_LENGTH for AES - should be multiple of 8, less then 128 bits // and greater the 96 bits - if (KMInteger.cast(tmpVariables[3]).getSignificantShort() != 0 - || KMInteger.cast(tmpVariables[3]).getShort() > 128 - || KMInteger.cast(tmpVariables[3]).getShort() < 96 - || (KMInteger.cast(tmpVariables[3]).getShort() % 8) != 0) { + if (KMInteger.cast(macLength).getSignificantShort() != 0 + || KMInteger.cast(macLength).getShort() > 128 + || KMInteger.cast(macLength).getShort() < 96 + || (KMInteger.cast(macLength).getShort() % 8) != 0) { KMException.throwIt(KMError.UNSUPPORTED_MIN_MAC_LENGTH); } } @@ -3488,40 +3602,31 @@ private static void validateAESKey() { private static void generateAESKey(byte[] scratchPad) { validateAESKey(); - tmpVariables[0] = + short keysize = KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); - tmpVariables[0] = - seProvider.createSymmetricKey(KMType.AES, tmpVariables[0], scratchPad, (short) 0); - data[SECRET] = KMByteBlob.instance(scratchPad, (short) 0, tmpVariables[0]); + short len = + seProvider.createSymmetricKey(KMType.AES, keysize, scratchPad, (short) 0); + data[SECRET] = KMByteBlob.instance(scratchPad, (short) 0, len); data[KEY_BLOB] = KMArray.instance((short) 4); } private static void validateECKeys() { // Read key size - tmpVariables[0] = - KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); - tmpVariables[1] = KMEnumTag.getValue(KMType.ECCURVE, data[KEY_PARAMETERS]); - if ((tmpVariables[0] == KMTag.INVALID_VALUE) && (tmpVariables[1] == KMTag.INVALID_VALUE)) { - KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); - } else if ((tmpVariables[0] != KMTag.INVALID_VALUE) && (tmpVariables[0] != (short) 256)) { - KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); - } else if ((tmpVariables[1] != KMType.INVALID_VALUE) && (tmpVariables[1] != KMType.P_256)) { - KMException.throwIt(KMError.UNSUPPORTED_EC_CURVE); + short eccurve = KMEnumTag.getValue(KMType.ECCURVE, data[KEY_PARAMETERS]); + if(!KMTag.isValidKeySize(data[KEY_PARAMETERS])){ + if (eccurve == KMType.INVALID_VALUE) { + KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); + } else if (eccurve != KMType.P_256) { + KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); + } } } private static void generateECKeys(byte[] scratchPad) { validateECKeys(); short[] lengths = tmpVariables; - seProvider.createAsymmetricKey( - KMType.EC, - scratchPad, - (short) 0, - (short) 128, - scratchPad, - (short) 128, - (short) 128, - lengths); + seProvider.createAsymmetricKey(KMType.EC, scratchPad, (short) 0, (short) 128, scratchPad, (short) 128, + (short) 128, lengths); data[PUB_KEY] = KMByteBlob.instance(scratchPad, (short) 128, lengths[1]); data[SECRET] = KMByteBlob.instance(scratchPad, (short) 0, lengths[0]); data[KEY_BLOB] = KMArray.instance((short) 5); @@ -3529,145 +3634,124 @@ private static void generateECKeys(byte[] scratchPad) { } private static void validateTDESKey() { - // Read Minimum Mac length - it must not be present - tmpVariables[0] = - KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, data[KEY_PARAMETERS]); - if (tmpVariables[0] != KMType.INVALID_VALUE) { - KMException.throwIt(KMError.INVALID_TAG); - } - // Read keysize - tmpVariables[1] = - KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); - if (tmpVariables[1] == KMType.INVALID_VALUE) { - KMException.throwIt(KMError.INVALID_ARGUMENT); - } - if (tmpVariables[1] != 168 && tmpVariables[1] != 192) { + if(!KMTag.isValidKeySize(data[KEY_PARAMETERS])){ KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); } + // Read Minimum Mac length - it must not be present + KMTag.assertAbsence(data[KEY_PARAMETERS],KMType.UINT_TAG, KMType.MIN_MAC_LENGTH,KMError.INVALID_TAG); } private static void generateTDESKey(byte[] scratchPad) { validateTDESKey(); - tmpVariables[0] = seProvider.createSymmetricKey(KMType.DES, (short) 168, scratchPad, (short) 0); - data[SECRET] = KMByteBlob.instance(scratchPad, (short) 0, tmpVariables[0]); + short len = seProvider.createSymmetricKey(KMType.DES, (short) 168, scratchPad, (short) 0); + data[SECRET] = KMByteBlob.instance(scratchPad, (short) 0, len); data[KEY_BLOB] = KMArray.instance((short) 4); } private static void validateHmacKey() { // If params does not contain any digest throw unsupported digest error. - if (KMType.INVALID_VALUE - == KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.DIGEST, data[KEY_PARAMETERS])) { - KMException.throwIt(KMError.UNSUPPORTED_DIGEST); - } + KMTag.assertPresence(data[KEY_PARAMETERS],KMType.ENUM_ARRAY_TAG,KMType.DIGEST,KMError.UNSUPPORTED_DIGEST); + // check whether digest sizes are greater then or equal to min mac length. // Only SHA256 digest must be supported. if (KMEnumArrayTag.contains(KMType.DIGEST, KMType.DIGEST_NONE, data[KEY_PARAMETERS])) { KMException.throwIt(KMError.UNSUPPORTED_DIGEST); } // Read Minimum Mac length - tmpVariables[0] = + KMTag.assertPresence(data[KEY_PARAMETERS],KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, KMError.MISSING_MAC_LENGTH); + short minMacLength = KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, data[KEY_PARAMETERS]); - if (tmpVariables[0] == KMType.INVALID_VALUE) { - KMException.throwIt(KMError.MISSING_MIN_MAC_LENGTH); - } - if (((short) (tmpVariables[0] % 8) != 0) - || (tmpVariables[0] < (short) 64) - || tmpVariables[0] > (short) 256) { + + if (((short) (minMacLength % 8) != 0) + || minMacLength < (short) 64 + || minMacLength > (short) 256) { KMException.throwIt(KMError.UNSUPPORTED_MIN_MAC_LENGTH); } - // Read keysize - tmpVariables[1] = - KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); - if (tmpVariables[1] == KMType.INVALID_VALUE) { - KMException.throwIt(KMError.INVALID_ARGUMENT); - } - if (((short) (tmpVariables[1] % 8) != 0) - || (tmpVariables[1] < (short) 64) - || tmpVariables[1] > (short) 512) { + // Read Keysize + if(!KMTag.isValidKeySize(data[KEY_PARAMETERS])){ KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); } } private static void generateHmacKey(byte[] scratchPad) { validateHmacKey(); - tmpVariables[0] = - KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); + short keysize = KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); // generate HMAC Key - tmpVariables[0] = - seProvider.createSymmetricKey(KMType.HMAC, tmpVariables[0], scratchPad, (short) 0); - data[SECRET] = KMByteBlob.instance(scratchPad, (short) 0, tmpVariables[0]); + short len = seProvider.createSymmetricKey(KMType.HMAC, keysize, scratchPad, (short) 0); + data[SECRET] = KMByteBlob.instance(scratchPad, (short) 0, len); data[KEY_BLOB] = KMArray.instance((short) 4); } private void checkVersionAndPatchLevel(byte[] scratchPad) { - tmpVariables[0] = + short len = KMIntegerTag.getValue( scratchPad, (short) 0, KMType.UINT_TAG, KMType.OS_VERSION, data[HW_PARAMETERS]); - if (tmpVariables[0] != KMType.INVALID_VALUE) { - tmpVariables[1] = repository.getOsVersion(); - tmpVariables[1] = + if (len != KMType.INVALID_VALUE) { + short provOsVersion = repository.getOsVersion(); + short status = KMInteger.unsignedByteArrayCompare( - KMInteger.cast(tmpVariables[1]).getBuffer(), - KMInteger.cast(tmpVariables[1]).getStartOff(), + KMInteger.cast(provOsVersion).getBuffer(), + KMInteger.cast(provOsVersion).getStartOff(), scratchPad, (short) 0, - tmpVariables[0]); - if (tmpVariables[1] == -1) { + len); + if (status == -1) { // If the key characteristics has os version > current os version KMException.throwIt(KMError.INVALID_KEY_BLOB); - } else if (tmpVariables[1] == 1) { + } else if (status == 1) { KMException.throwIt(KMError.KEY_REQUIRES_UPGRADE); } } - tmpVariables[0] = + len = KMIntegerTag.getValue( scratchPad, (short) 0, KMType.UINT_TAG, KMType.OS_PATCH_LEVEL, data[HW_PARAMETERS]); - if (tmpVariables[0] != KMType.INVALID_VALUE) { - tmpVariables[1] = repository.getOsPatch(); - tmpVariables[1] = + if (len != KMType.INVALID_VALUE) { + short osPatch = repository.getOsPatch(); + short status = KMInteger.unsignedByteArrayCompare( - KMInteger.cast(tmpVariables[1]).getBuffer(), - KMInteger.cast(tmpVariables[1]).getStartOff(), + KMInteger.cast(osPatch).getBuffer(), + KMInteger.cast(osPatch).getStartOff(), scratchPad, (short) 0, - tmpVariables[0]); - if (tmpVariables[1] == -1) { + len); + if (status == -1) { KMException.throwIt(KMError.INVALID_KEY_BLOB); - } else if (tmpVariables[1] == 1) { + } else if (status == 1) { KMException.throwIt(KMError.KEY_REQUIRES_UPGRADE); } } } + protected static short getBootPatchLevel(byte[] scratchPad){ + Util.arrayFillNonAtomic(scratchPad,(short)0, BOOT_PATCH_LVL_SIZE, (byte)0); + short len = seProvider.getBootPatchLevel(scratchPad,(short)0); + if(len != BOOT_PATCH_LVL_SIZE){ + KMException.throwIt(KMError.UNKNOWN_ERROR); + } + return KMInteger.uint_32(scratchPad, (short)0); + } 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); + short osVersion = repository.getOsVersion(); + short osPatch = repository.getOsPatch(); + short vendorPatch = repository.getVendorPatchLevel(); + short bootPatch = getBootPatchLevel(scratchPad); + data[SB_PARAMETERS] = KMKeyParameters.makeSbEnforced( + data[KEY_PARAMETERS], (byte) data[ORIGIN], osVersion, osPatch, vendorPatch, bootPatch, scratchPad); + data[TEE_PARAMETERS] = KMKeyParameters.makeTeeEnforced(data[KEY_PARAMETERS],scratchPad); + data[SW_PARAMETERS] = KMKeyParameters.makeKeystoreEnforced(data[KEY_PARAMETERS],scratchPad); + data[HW_PARAMETERS] = KMKeyParameters.makeHwEnforced(data[SB_PARAMETERS], data[TEE_PARAMETERS]); data[KEY_CHARACTERISTICS] = KMKeyCharacteristics.instance(); - KMKeyCharacteristics.cast(data[KEY_CHARACTERISTICS]).setHardwareEnforced(data[HW_PARAMETERS]); - KMKeyCharacteristics.cast(data[KEY_CHARACTERISTICS]).setSoftwareEnforced(data[SW_PARAMETERS]); + KMKeyCharacteristics.cast(data[KEY_CHARACTERISTICS]).setStrongboxEnforced(data[SB_PARAMETERS]); + KMKeyCharacteristics.cast(data[KEY_CHARACTERISTICS]).setKeystoreEnforced(data[SW_PARAMETERS]); + KMKeyCharacteristics.cast(data[KEY_CHARACTERISTICS]).setTeeEnforced(data[TEE_PARAMETERS]); } 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] = repository.readROT(); + data[ROT] = readROT(scratchPad); if (data[ROT] == KMType.INVALID_VALUE) { KMException.throwIt(KMError.UNKNOWN_ERROR); } - // make hidden key params list data[HIDDEN_PARAMETERS] = KMKeyParameters.makeHidden(data[KEY_PARAMETERS], data[ROT], scratchPad); @@ -3675,91 +3759,102 @@ private static void createEncryptedKeyBlob(byte[] scratchPad) { makeAuthData(scratchPad); // encrypt the secret and cryptographically attach that to authorization data encryptSecret(scratchPad); - // create key blob array KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_SECRET, data[SECRET]); 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]); + + //TODO remove the following temporary creation of keyblob. + short tempChar = KMKeyCharacteristics.instance(); + short emptyParam = KMArray.instance((short) 0); + emptyParam = KMKeyParameters.instance(emptyParam); + KMKeyCharacteristics.cast(tempChar).setStrongboxEnforced(data[SB_PARAMETERS]); + KMKeyCharacteristics.cast(tempChar).setKeystoreEnforced(emptyParam); + KMKeyCharacteristics.cast(tempChar).setTeeEnforced(data[TEE_PARAMETERS]); + KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_PARAMS, tempChar); + //KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_PARAMS, data[KEY_CHARACTERISTICS]); // 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]); + short buffer = repository.alloc((short) 1024); + short keyBlob = encoder.encode(data[KEY_BLOB], repository.getHeap(), buffer); + data[KEY_BLOB] = KMByteBlob.instance(repository.getHeap(), buffer, keyBlob); } - private static void parseEncryptedKeyBlob(byte[] scratchPad) { - data[ROT] = repository.readROT(); - if (data[ROT] == KMType.INVALID_VALUE) { + private short parseEncryptedKeyBlob(short keyBlob, short appId, short appData, byte[] scratchPad) { + short parsedBlob = KMType.INVALID_VALUE; + short rot = readROT(scratchPad); + if (rot == KMType.INVALID_VALUE) { KMException.throwIt(KMError.UNKNOWN_ERROR); } try { - tmpVariables[0] = KMByteBlob.cast(data[KEY_BLOB]).getStartOff(); - tmpVariables[1] = KMArray.instance((short) 5); - KMArray.cast(tmpVariables[1]).add(KMKeymasterApplet.KEY_BLOB_SECRET, - KMByteBlob.exp()); - KMArray.cast(tmpVariables[1]).add(KMKeymasterApplet.KEY_BLOB_AUTH_TAG, - KMByteBlob.exp()); - KMArray.cast(tmpVariables[1]).add(KMKeymasterApplet.KEY_BLOB_NONCE, - KMByteBlob.exp()); - tmpVariables[2] = KMKeyCharacteristics.exp(); - KMArray.cast(tmpVariables[1]).add(KMKeymasterApplet.KEY_BLOB_KEYCHAR, - tmpVariables[2]); - KMArray.cast(tmpVariables[1]).add(KMKeymasterApplet.KEY_BLOB_PUB_KEY, - KMByteBlob.exp()); - data[KEY_BLOB] = decoder.decodeArray(tmpVariables[1], - KMByteBlob.cast(data[KEY_BLOB]).getBuffer(), - KMByteBlob.cast(data[KEY_BLOB]).getStartOff(), - KMByteBlob.cast(data[KEY_BLOB]).length()); - tmpVariables[0] = KMArray.cast(data[KEY_BLOB]).length(); - if (tmpVariables[0] < 4) { + parsedBlob = decoder.decodeArray(keyBlob(), + KMByteBlob.cast(keyBlob).getBuffer(), + KMByteBlob.cast(keyBlob).getStartOff(), + KMByteBlob.cast(keyBlob).length()); + if (KMArray.cast(parsedBlob).length() < 4) { KMException.throwIt(KMError.INVALID_KEY_BLOB); } - data[AUTH_TAG] = KMArray.cast(data[KEY_BLOB]).get(KEY_BLOB_AUTH_TAG); // initialize data - data[NONCE] = KMArray.cast(data[KEY_BLOB]).get(KEY_BLOB_NONCE); - data[SECRET] = KMArray.cast(data[KEY_BLOB]).get(KEY_BLOB_SECRET); - data[KEY_CHARACTERISTICS] = KMArray.cast(data[KEY_BLOB]).get( - KEY_BLOB_KEYCHAR); + data[SECRET] = KMArray.cast(parsedBlob).get(KEY_BLOB_SECRET); + data[NONCE]= KMArray.cast(parsedBlob).get(KEY_BLOB_NONCE); + data[AUTH_TAG] = KMArray.cast(parsedBlob).get(KEY_BLOB_AUTH_TAG); + data[KEY_CHARACTERISTICS] = KMArray.cast(parsedBlob).get(KEY_BLOB_PARAMS); data[PUB_KEY] = KMType.INVALID_VALUE; - if (tmpVariables[0] == 5) { - data[PUB_KEY] = KMArray.cast(data[KEY_BLOB]).get(KEY_BLOB_PUB_KEY); + if (KMArray.cast(parsedBlob).length() == 5) { + data[PUB_KEY] = KMArray.cast(parsedBlob).get(KEY_BLOB_PUB_KEY); } - data[HW_PARAMETERS] = KMKeyCharacteristics - .cast(data[KEY_CHARACTERISTICS]).getHardwareEnforced(); - data[SW_PARAMETERS] = KMKeyCharacteristics - .cast(data[KEY_CHARACTERISTICS]).getSoftwareEnforced(); - data[HIDDEN_PARAMETERS] = KMKeyParameters.makeHidden(data[APP_ID], - data[APP_DATA], data[ROT], scratchPad); + data[TEE_PARAMETERS] = KMKeyCharacteristics.cast(data[KEY_CHARACTERISTICS]).getTeeEnforced(); + data[SB_PARAMETERS] = KMKeyCharacteristics.cast(data[KEY_CHARACTERISTICS]).getStrongboxEnforced(); + data[SW_PARAMETERS] = KMKeyCharacteristics.cast(data[KEY_CHARACTERISTICS]).getKeystoreEnforced(); + data[HW_PARAMETERS] = KMKeyParameters.makeHwEnforced(data[SB_PARAMETERS], data[TEE_PARAMETERS]); + + data[HIDDEN_PARAMETERS] = KMKeyParameters.makeHidden(appId, appData, rot, scratchPad); + data[KEY_BLOB] = parsedBlob; // make auth data makeAuthData(scratchPad); // Decrypt Secret and verify auth tag decryptSecret(scratchPad); + KMArray.cast(parsedBlob).add(KEY_BLOB_SECRET, data[SECRET]); } catch (Exception e) { KMException.throwIt(KMError.INVALID_KEY_BLOB); } + return parsedBlob; + } + + // Read RoT + public static short readROT(byte[] scratchPad) { + Util.arrayFillNonAtomic(scratchPad,(short)0, (short)256,(byte)0); + short len = seProvider.getBootKey(scratchPad, (short)0); + len += seProvider.getVerifiedBootHash(scratchPad, (short)len); + short bootState = seProvider.getBootState(); + Util.setShort(scratchPad, (short)0,bootState); + len +=2; + if(seProvider.isDeviceBootLocked()){ + scratchPad[len] = (byte)1; + }else{ + scratchPad[len] = (byte)0; + } + len++; + return KMByteBlob.instance(scratchPad,(short)0,len); } - private static void decryptSecret(byte[] scratchPad) { + private void decryptSecret(byte[] scratchPad) { // derive master key - stored in derivedKey - tmpVariables[0] = deriveKey(scratchPad); - if (!seProvider.aesGCMDecrypt( - repository.getHeap(), - data[DERIVED_KEY], - tmpVariables[0], + short len = deriveKey(scratchPad); + if (!seProvider.aesGCMDecrypt( + KMByteBlob.cast(data[DERIVED_KEY]).getBuffer(), + KMByteBlob.cast(data[DERIVED_KEY]).getStartOff(), + KMByteBlob.cast(data[DERIVED_KEY]).length(), KMByteBlob.cast(data[SECRET]).getBuffer(), KMByteBlob.cast(data[SECRET]).getStartOff(), KMByteBlob.cast(data[SECRET]).length(), - scratchPad, - (short) 0, + scratchPad, (short) 0, KMByteBlob.cast(data[NONCE]).getBuffer(), KMByteBlob.cast(data[NONCE]).getStartOff(), KMByteBlob.cast(data[NONCE]).length(), - repository.getHeap(), - data[AUTH_DATA], - data[AUTH_DATA_LENGTH], + repository.getHeap(), data[AUTH_DATA], data[AUTH_DATA_LENGTH], KMByteBlob.cast(data[AUTH_TAG]).getBuffer(), KMByteBlob.cast(data[AUTH_TAG]).getStartOff(), KMByteBlob.cast(data[AUTH_TAG]).length())) { @@ -3785,12 +3880,11 @@ private static void encryptSecret(byte[] scratchPad) { KMByteBlob.cast(data[NONCE]).getStartOff(), KMByteBlob.cast(data[NONCE]).length()); // derive master key - stored in derivedKey - tmpVariables[0] = deriveKey(scratchPad); - tmpVariables[1] = - seProvider.aesGCMEncrypt( - repository.getHeap(), - data[DERIVED_KEY], - tmpVariables[0], + short len = deriveKey(scratchPad); + len = seProvider.aesGCMEncrypt( + KMByteBlob.cast(data[DERIVED_KEY]).getBuffer(), + KMByteBlob.cast(data[DERIVED_KEY]).getStartOff(), + KMByteBlob.cast(data[DERIVED_KEY]).length(), KMByteBlob.cast(data[SECRET]).getBuffer(), KMByteBlob.cast(data[SECRET]).getStartOff(), KMByteBlob.cast(data[SECRET]).length(), @@ -3805,43 +3899,39 @@ private static void encryptSecret(byte[] scratchPad) { KMByteBlob.cast(data[AUTH_TAG]).getBuffer(), KMByteBlob.cast(data[AUTH_TAG]).getStartOff(), KMByteBlob.cast(data[AUTH_TAG]).length()); - if (tmpVariables[1] > 0) { - if (tmpVariables[1] != KMByteBlob.cast(data[SECRET]).length()) { - KMException.throwIt(KMError.INVALID_KEY_BLOB); - } - KMByteBlob.cast(data[SECRET]).setValue(scratchPad, (short) 0, tmpVariables[1]); + + if (len > 0 && len != KMByteBlob.cast(data[SECRET]).length()) { + KMException.throwIt(KMError.INVALID_KEY_BLOB); } + data[SECRET] = KMByteBlob.instance(scratchPad, (short)0, len); } private static void makeAuthData(byte[] scratchPad) { - tmpVariables[0] = + short len = addPtrToAAD(KMKeyParameters.cast(data[HW_PARAMETERS]).getVals(), scratchPad, (short) 0); - tmpVariables[0] += - addPtrToAAD( - KMKeyParameters.cast(data[SW_PARAMETERS]).getVals(), scratchPad, tmpVariables[0]); - tmpVariables[0] += + len += addPtrToAAD( - KMKeyParameters.cast(data[HIDDEN_PARAMETERS]).getVals(), scratchPad, tmpVariables[0]); - + KMKeyParameters.cast(data[HIDDEN_PARAMETERS]).getVals(), scratchPad, len); + short authData; if (KMArray.cast(data[KEY_BLOB]).length() == 5) { - tmpVariables[1] = KMArray.instance((short) (tmpVariables[0] + 1)); + authData = KMArray.instance((short) (len + 1)); } else { - tmpVariables[1] = KMArray.instance(tmpVariables[0]); + authData = KMArray.instance(len); } // convert scratch pad to KMArray short index = 0; short objPtr; - while (index < tmpVariables[0]) { + while (index < len) { objPtr = Util.getShort(scratchPad, (short) (index * 2)); - KMArray.cast(tmpVariables[1]).add(index, objPtr); + KMArray.cast(authData).add(index, objPtr); index++; } + //TODO change the code below - implicitly adds the pub key. if (KMArray.cast(data[KEY_BLOB]).length() == 5) { - KMArray.cast(tmpVariables[1]).add(index, data[PUB_KEY]); + KMArray.cast(authData).add(index, data[PUB_KEY]); } - data[AUTH_DATA] = repository.alloc(MAX_AUTH_DATA_SIZE); - short len = encoder.encode(tmpVariables[1], repository.getHeap(), data[AUTH_DATA]); + len = encoder.encode(authData, repository.getHeap(), data[AUTH_DATA]); data[AUTH_DATA_LENGTH] = len; } @@ -3860,16 +3950,21 @@ private static short addPtrToAAD(short dataArrPtr, byte[] aadBuf, short offset) } private static short deriveKey(byte[] scratchPad) { - tmpVariables[0] = KMKeyParameters.cast(data[HIDDEN_PARAMETERS]).getVals(); - tmpVariables[1] = repository.alloc(DERIVE_KEY_INPUT_SIZE); + short hiddenParams = KMKeyParameters.cast(data[HIDDEN_PARAMETERS]).getVals(); + short derivationData = repository.alloc(DERIVE_KEY_INPUT_SIZE); // generate derivation material from hidden parameters - tmpVariables[2] = encoder.encode(tmpVariables[0], repository.getHeap(), tmpVariables[1]); - if (DERIVE_KEY_INPUT_SIZE > tmpVariables[2]) { + short len = encoder.encode(hiddenParams, repository.getHeap(), derivationData); + if (DERIVE_KEY_INPUT_SIZE > len) { + short start = (short)(derivationData+len); + len = (short)(DERIVE_KEY_INPUT_SIZE - len); // Copy KeyCharacteristics in the remaining space of DERIVE_KEY_INPUT_SIZE - Util.arrayCopyNonAtomic(repository.getHeap(), (short) (data[AUTH_DATA]), - repository.getHeap(), - (short) (tmpVariables[1] + tmpVariables[2]), - (short) (DERIVE_KEY_INPUT_SIZE - tmpVariables[2])); + // if the data[AUTH_DATA] length is less then DERIVE_KEY_INPUT_SIZE - len + // then add complete key characteristics. + if(data[AUTH_DATA_LENGTH] < len){ + len = data[AUTH_DATA_LENGTH]; + } + Util.arrayCopyNonAtomic(repository.getHeap(), data[AUTH_DATA], + repository.getHeap(), start, len); } // KeyDerivation: // 1. Do HMAC Sign, with below input parameters. @@ -3879,54 +3974,29 @@ private static short deriveKey(byte[] scratchPad) { // 2. HMAC Sign generates an output of 32 bytes length. // Consume only first 16 bytes as derived key. // Hmac sign. - tmpVariables[3] = seProvider.hmacKDF( + short signLen = seProvider.hmacKDF( seProvider.getMasterKey(), repository.getHeap(), - tmpVariables[1], + derivationData, DERIVE_KEY_INPUT_SIZE, scratchPad, (short) 0); - if (tmpVariables[3] < 16) { + if (signLen < 16) { KMException.throwIt(KMError.UNKNOWN_ERROR); } - tmpVariables[3] = 16; + signLen = 16; // store the derived secret in data dictionary - data[DERIVED_KEY] = tmpVariables[1]; - Util.arrayCopyNonAtomic( - scratchPad, (short) 0, repository.getHeap(), data[DERIVED_KEY], tmpVariables[3]); - return tmpVariables[3]; - } - - // This function masks the error code with POWER_RESET_MASK_FLAG - // in case if card reset event occurred. The clients of the Applet - // has to extract the power reset status from the error code and - // process accordingly. - private static short buildErrorStatus(short err) { - short int32Ptr = KMInteger.instance((short) 4); - short powerResetStatus = 0; - if (repository.isPowerResetEventOccurred()) { - powerResetStatus = POWER_RESET_MASK_FLAG; - } - - Util.setShort(KMInteger.cast(int32Ptr).getBuffer(), - KMInteger.cast(int32Ptr).getStartOff(), - powerResetStatus); - - Util.setShort(KMInteger.cast(int32Ptr).getBuffer(), - (short) (KMInteger.cast(int32Ptr).getStartOff() + 2), - err); - - // reset power reset status flag to its default value. - repository.restorePowerResetStatus(); - return int32Ptr; + data[DERIVED_KEY] = KMByteBlob.instance(scratchPad, (short)0, signLen); + //TODO do we need to return this len? + return signLen; } - private static void sendError(APDU apdu, short err) { - bufferProp[BUF_START_OFFSET] = repository.alloc((short) 5); - short int32Ptr = buildErrorStatus(err); - bufferProp[BUF_LEN_OFFSET] = encoder.encodeError(int32Ptr, (byte[]) bufferRef[0], - bufferProp[BUF_START_OFFSET], (short) 5); - sendOutgoing(apdu); + public static void sendError(APDU apdu, short err) { + short resp = KMArray.instance((short)1); + err = KMError.translate(err); + short error = KMInteger.uint_16(err); + KMArray.cast(resp).add((short)0, error); + sendOutgoing(apdu, resp); } private short addIntegers(short num1, short num2) { @@ -3963,4 +4033,208 @@ private void add(byte[] buf, short op1, short op2, short result) { index--; } } + + public void powerReset() { + //TODO handle power reset signal. + releaseAllOperations(); + resetWrappingKey(); + } + + public static void generateRkpKey(byte[] scratchPad, short keyParams) { + data[KEY_PARAMETERS] = keyParams; + generateECKeys(scratchPad); + // create key blob + data[ORIGIN] = KMType.GENERATED; + makeKeyCharacteristics(scratchPad); + createEncryptedKeyBlob(scratchPad); + } + public static short getPubKey() { + return data[PUB_KEY]; + } + + public static short getPivateKey() { + return data[KEY_BLOB]; + } + + /** + * Encodes the object to the provided apdu buffer. + * + * @param object Object to be encoded. + * @param apduBuf Buffer on which the encoded data is copied. + * @param apduOff Start offset of the buffer. + * @param maxLen Max value of the expected out length. + * @return length of the encoded buffer. + */ + public static short encodeToApduBuffer(short object, byte[] apduBuf, short apduOff, + short maxLen) { + short offset = repository.allocReclaimableMemory(maxLen); + short len = encoder.encode(object, repository.getHeap(), offset); + Util.arrayCopyNonAtomic(repository.getHeap(), offset, apduBuf, apduOff, len); + //release memory + repository.reclaimMemory(maxLen); + return len; + } + + public static short validateCertChain(boolean validateEekRoot, byte expCertAlg, + byte expLeafCertAlg, short certChainArr, byte[] scratchPad, Object[] authorizedEekRoots) { + short len = KMArray.cast(certChainArr).length(); + short coseHeadersExp = KMCoseHeaders.exp(); + //prepare exp for coseky + short coseKeyExp = KMCoseKey.exp(); + short ptr1; + short ptr2; + short signStructure; + short encodedLen; + short prevCoseKey = 0; + short keySize; + short alg = expCertAlg; + short index; + for (index = 0; index < len; index++) { + ptr1 = KMArray.cast(certChainArr).get(index); + + // validate protected Headers + ptr2 = KMArray.cast(ptr1).get(KMCose.COSE_SIGN1_PROTECTED_PARAMS_OFFSET); + ptr2 = decoder.decode(coseHeadersExp, KMByteBlob.cast(ptr2).getBuffer(), + KMByteBlob.cast(ptr2).getStartOff(), KMByteBlob.cast(ptr2).length()); + if (!KMCoseHeaders.cast(ptr2).isDataValid(alg, KMType.INVALID_VALUE)) { + KMException.throwIt(KMError.STATUS_FAILED); + } + + // parse and get the public key from payload. + ptr2 = KMArray.cast(ptr1).get(KMCose.COSE_SIGN1_PAYLOAD_OFFSET); + ptr2 = decoder.decode(coseKeyExp, KMByteBlob.cast(ptr2).getBuffer(), + KMByteBlob.cast(ptr2).getStartOff(), KMByteBlob.cast(ptr2).length()); + if ((index == (short) (len - 1)) && len > 1) { + alg = expLeafCertAlg; + } + if (!KMCoseKey.cast(ptr2).isDataValid(KMCose.COSE_KEY_TYPE_EC2, KMType.INVALID_VALUE, alg, + KMType.INVALID_VALUE, KMCose.COSE_ECCURVE_256)) { + KMException.throwIt(KMError.STATUS_FAILED); + } + if (prevCoseKey == 0) { + prevCoseKey = ptr2; + } + // Get the public key. + keySize = KMCoseKey.cast(prevCoseKey).getEcdsa256PublicKey(scratchPad, (short) 0); + if (keySize != 65) { + KMException.throwIt(KMError.STATUS_FAILED); + } + if (validateEekRoot && (index == 0)) { + boolean found = false; + // In prod mode the first pubkey should match a well-known Google public key. + for (short i = 0; i < (short) authorizedEekRoots.length; i++) { + if (0 == Util.arrayCompare(scratchPad, (short) 0, (byte[]) authorizedEekRoots[i], + (short) 0, (short) ((byte[]) authorizedEekRoots[i]).length)) { + found = true; + break; + } + } + if (!found) { + KMException.throwIt(KMError.STATUS_FAILED); + } + } + // Validate signature. + signStructure = + KMCose.constructCoseSignStructure( + KMArray.cast(ptr1).get(KMCose.COSE_SIGN1_PROTECTED_PARAMS_OFFSET), + KMByteBlob.instance((short) 0), + KMArray.cast(ptr1).get(KMCose.COSE_SIGN1_PAYLOAD_OFFSET)); + encodedLen = KMKeymasterApplet.encodeToApduBuffer(signStructure, scratchPad, + keySize, KMKeymasterApplet.MAX_COSE_BUF_SIZE); + + if (!seProvider.ecVerify256(scratchPad, (short) 0, keySize, scratchPad, keySize, encodedLen, + KMByteBlob.cast(KMArray.cast(ptr1).get(KMCose.COSE_SIGN1_SIGNATURE_OFFSET)).getBuffer(), + KMByteBlob.cast(KMArray.cast(ptr1).get(KMCose.COSE_SIGN1_SIGNATURE_OFFSET)).getStartOff(), + KMByteBlob.cast(KMArray.cast(ptr1).get(KMCose.COSE_SIGN1_SIGNATURE_OFFSET)).length())) { + KMException.throwIt(KMError.STATUS_FAILED); + } + prevCoseKey = ptr2; + } + return prevCoseKey; + } + + + public static short generateBcc(boolean testMode, byte[] scratchPad) { + if (!testMode && seProvider.isProvisionLocked()) { + KMException.throwIt(KMError.STATUS_FAILED); + } + KMDeviceUniqueKey deviceUniqueKey = seProvider.getDeviceUniqueKey(testMode); + short temp = deviceUniqueKey.getPublicKey(scratchPad, (short) 0); + short coseKey = + KMCose.constructCoseKey( + KMInteger.uint_8(KMCose.COSE_KEY_TYPE_EC2), + KMType.INVALID_VALUE, + KMNInteger.uint_8(KMCose.COSE_ALG_ES256), + KMInteger.uint_8(KMCose.COSE_KEY_OP_VERIFY), + KMInteger.uint_8(KMCose.COSE_ECCURVE_256), + scratchPad, + (short) 0, + temp, + KMType.INVALID_VALUE, + false + ); + temp = KMKeymasterApplet.encodeToApduBuffer(coseKey, scratchPad, (short) 0, + KMKeymasterApplet.MAX_COSE_BUF_SIZE); + // Construct payload. + short payload = + KMCose.constructCoseCertPayload( + KMCosePairTextStringTag.instance(KMInteger.uint_8(KMCose.ISSUER), + KMTextString.instance(KMCose.TEST_ISSUER_NAME, (short) 0, + (short) KMCose.TEST_ISSUER_NAME.length)), + KMCosePairTextStringTag.instance(KMInteger.uint_8(KMCose.SUBJECT), + KMTextString.instance(KMCose.TEST_SUBJECT_NAME, (short) 0, + (short) KMCose.TEST_SUBJECT_NAME.length)), + KMCosePairByteBlobTag.instance(KMNInteger.uint_32(KMCose.SUBJECT_PUBLIC_KEY, (short) 0), + KMByteBlob.instance(scratchPad, (short) 0, temp)), + KMCosePairByteBlobTag.instance(KMNInteger.uint_32(KMCose.KEY_USAGE, (short) 0), + KMByteBlob.instance(KMCose.KEY_USAGE_SIGN, (short) 0, + (short) KMCose.KEY_USAGE_SIGN.length)) + ); + // temp temporarily holds the length of encoded cert payload. + temp = KMKeymasterApplet.encodeToApduBuffer(payload, scratchPad, (short) 0, + KMKeymasterApplet.MAX_COSE_BUF_SIZE); + payload = KMByteBlob.instance(scratchPad, (short) 0, temp); + + // protected header + short protectedHeader = + KMCose.constructHeaders(KMNInteger.uint_8(KMCose.COSE_ALG_ES256), KMType.INVALID_VALUE, + KMType.INVALID_VALUE, KMType.INVALID_VALUE); + // temp temporarily holds the length of encoded headers. + temp = KMKeymasterApplet.encodeToApduBuffer(protectedHeader, scratchPad, (short) 0, + KMKeymasterApplet.MAX_COSE_BUF_SIZE); + protectedHeader = KMByteBlob.instance(scratchPad, (short) 0, temp); + + //unprotected headers. + short arr = KMArray.instance((short) 0); + short unprotectedHeader = KMCoseHeaders.instance(arr); + + // construct cose sign structure. + short coseSignStructure = + KMCose.constructCoseSignStructure(protectedHeader, KMByteBlob.instance((short) 0), payload); + // temp temporarily holds the length of encoded sign structure. + // Encode cose Sign_Structure. + temp = KMKeymasterApplet.encodeToApduBuffer(coseSignStructure, scratchPad, (short) 0, + KMKeymasterApplet.MAX_COSE_BUF_SIZE); + // do sign + short len = + seProvider.ecSign256( + deviceUniqueKey, + scratchPad, + (short) 0, + temp, + scratchPad, + temp + ); + coseSignStructure = KMByteBlob.instance(scratchPad, temp, len); + + // construct cose_sign1 + short coseSign1 = + KMCose.constructCoseSign1(protectedHeader, unprotectedHeader, payload, coseSignStructure); + + // [Cose_Key, Cose_Sign1] + short bcc = KMArray.instance((short) 2); + KMArray.cast(bcc).add((short) 0, coseKey); + KMArray.cast(bcc).add((short) 1, coseSign1); + return bcc; + } } diff --git a/Applet/src/com/android/javacard/keymaster/KMMap.java b/Applet/src/com/android/javacard/keymaster/KMMap.java new file mode 100644 index 00000000..4b559fa3 --- /dev/null +++ b/Applet/src/com/android/javacard/keymaster/KMMap.java @@ -0,0 +1,153 @@ +/* + * Copyright(C) 2021 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.Util; + +public class KMMap extends KMType { + public static final short ANY_MAP_LENGTH = 0x1000; + private static final short MAP_HEADER_SIZE = 4; + private static KMMap prototype; + + private KMMap() { + } + + private static KMMap proto(short ptr) { + if (prototype == null) { + prototype = new KMMap(); + } + instanceTable[KM_MAP_OFFSET] = ptr; + return prototype; + } + + public static short exp() { + short ptr = instance(MAP_TYPE, MAP_HEADER_SIZE); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), (short) 0); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), ANY_MAP_LENGTH); + return ptr; + } + + public static short instance(short length) { + short ptr = KMType.instance(MAP_TYPE, (short) (MAP_HEADER_SIZE + (length * 4))); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), (short) 0); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), length); + return ptr; + } + + public static short instance(short length, byte type) { + short ptr = instance(length); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), type); + return ptr; + } + + public static KMMap cast(short ptr) { + if (heap[ptr] != MAP_TYPE) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + return proto(ptr); + } + + public void add(short index, short keyPtr, short valPtr) { + short len = length(); + if (index >= len) { + ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); + } + short keyIndex = (short) (instanceTable[KM_MAP_OFFSET] + TLV_HEADER_SIZE + MAP_HEADER_SIZE + (short) (index * 4)); + Util.setShort(heap, keyIndex, keyPtr); + Util.setShort(heap, (short) (keyIndex + 2), valPtr); + } + + public short getKey(short index) { + short len = length(); + if (index >= len) { + ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); + } + return Util.getShort( + heap, (short) (instanceTable[KM_MAP_OFFSET] + TLV_HEADER_SIZE + MAP_HEADER_SIZE + (short) (index * 4))); + } + + public short getKeyValue(short index) { + short len = length(); + if (index >= len) { + ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); + } + return Util.getShort( + heap, (short) (instanceTable[KM_MAP_OFFSET] + TLV_HEADER_SIZE + MAP_HEADER_SIZE + (short) (index * 4 + 2))); + } + + public void swap(short index1, short index2) { + short len = length(); + if (index1 >= len || index2 >= len) { + ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); + } + // Swap keys + short indexPtr1 = + Util.getShort( + heap, (short) (instanceTable[KM_MAP_OFFSET] + TLV_HEADER_SIZE + MAP_HEADER_SIZE + (short) (index1 * 4))); + short indexPtr2 = + Util.getShort( + heap, + (short) (instanceTable[KM_MAP_OFFSET] + TLV_HEADER_SIZE + MAP_HEADER_SIZE + (short) (index2 * 4))); + Util.setShort( + heap, + (short) (instanceTable[KM_MAP_OFFSET] + TLV_HEADER_SIZE + MAP_HEADER_SIZE + (short) (index1 * 4)), + indexPtr2); + Util.setShort( + heap, + (short) (instanceTable[KM_MAP_OFFSET] + TLV_HEADER_SIZE + MAP_HEADER_SIZE + (short) (index2 * 4)), + indexPtr1); + + // Swap Values + indexPtr1 = + Util.getShort( + heap, (short) (instanceTable[KM_MAP_OFFSET] + TLV_HEADER_SIZE + MAP_HEADER_SIZE + (short) (index1 * 4 + 2))); + indexPtr2 = + Util.getShort( + heap, + (short) (instanceTable[KM_MAP_OFFSET] + TLV_HEADER_SIZE + MAP_HEADER_SIZE + (short) (index2 * 4 + 2))); + Util.setShort( + heap, + (short) (instanceTable[KM_MAP_OFFSET] + TLV_HEADER_SIZE + MAP_HEADER_SIZE + (short) (index1 * 4 + 2)), + indexPtr2); + Util.setShort( + heap, + (short) (instanceTable[KM_MAP_OFFSET] + TLV_HEADER_SIZE + MAP_HEADER_SIZE + (short) (index2 * 4 + 2)), + indexPtr1); + } + + public void canonicalize() { + KMCoseMap.canonicalize(instanceTable[KM_MAP_OFFSET], length()); + } + + public short containedType() { + return Util.getShort(heap, (short) (instanceTable[KM_MAP_OFFSET] + TLV_HEADER_SIZE)); + } + + public short getStartOff() { + return (short) (instanceTable[KM_MAP_OFFSET] + TLV_HEADER_SIZE + MAP_HEADER_SIZE); + } + + public short length() { + return Util.getShort(heap, (short) (instanceTable[KM_MAP_OFFSET] + TLV_HEADER_SIZE + 2)); + } + + public byte[] getBuffer() { + return heap; + } +} diff --git a/Applet/src/com/android/javacard/keymaster/KMNInteger.java b/Applet/src/com/android/javacard/keymaster/KMNInteger.java new file mode 100644 index 00000000..c9f8e5ab --- /dev/null +++ b/Applet/src/com/android/javacard/keymaster/KMNInteger.java @@ -0,0 +1,124 @@ +/* + * Copyright(C) 2021 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.Util; + +public class KMNInteger extends KMInteger { + private static KMNInteger prototype; + public static final byte SIGNED_MASK = (byte) 0x80; + + private KMNInteger() { + } + + private static KMNInteger proto(short ptr) { + if (prototype == null) { + prototype = new KMNInteger(); + } + instanceTable[KM_NEG_INTEGER_OFFSET] = ptr; + return prototype; + } + + public static short exp() { + return KMType.exp(NEG_INTEGER_TYPE); + } + + // return an empty integer instance + public static short instance(short length) { + if ((length <= 0) || (length > 8)) { + ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); + } + if (length > 4) { + length = KMInteger.UINT_64; + } else { + length = KMInteger.UINT_32; + } + return KMType.instance(NEG_INTEGER_TYPE, length); + } + + public static short instance(byte[] num, short srcOff, short length) { + if (length > 8) { + ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); + } + if (length == 1) { + return uint_8(num[srcOff]); + } else if (length == 2) { + return uint_16(Util.getShort(num, srcOff)); + } else if (length == 4) { + return uint_32(num, srcOff); + } else { + return uint_64(num, srcOff); + } + } + + public static KMNInteger cast(short ptr) { + byte[] heap = repository.getHeap(); + if (heap[ptr] != NEG_INTEGER_TYPE) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + if (Util.getShort(heap, (short) (ptr + 1)) == INVALID_VALUE) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + return proto(ptr); + } + + // create integer and copy byte value + public static short uint_8(byte num) { + if (num >= 0) ISOException.throwIt(ISO7816.SW_DATA_INVALID); + short ptr = instance(KMInteger.UINT_32); + heap[(short) (ptr + TLV_HEADER_SIZE + 3)] = num; + return ptr; + } + + // create integer and copy short value + public static short uint_16(short num) { + if (num >= 0) ISOException.throwIt(ISO7816.SW_DATA_INVALID); + short ptr = instance(KMInteger.UINT_32); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), num); + return ptr; + } + + // create integer and copy integer value + public static short uint_32(byte[] num, short offset) { + if (!isSignedInteger(num, offset)) + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + short ptr = instance(KMInteger.UINT_32); + Util.arrayCopy(num, offset, heap, (short) (ptr + TLV_HEADER_SIZE), KMInteger.UINT_32); + return ptr; + } + + // create integer and copy integer value + public static short uint_64(byte[] num, short offset) { + if (!isSignedInteger(num, offset)) + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + short ptr = instance(KMInteger.UINT_64); + Util.arrayCopy(num, offset, heap, (short) (ptr + TLV_HEADER_SIZE), KMInteger.UINT_64); + return ptr; + } + + @Override + protected short getBaseOffset() { + return instanceTable[KM_NEG_INTEGER_OFFSET]; + } + + public static boolean isSignedInteger(byte[] num, short offset) { + byte val = num[offset]; + return SIGNED_MASK == (val & SIGNED_MASK); + } +} diff --git a/Applet/src/com/android/javacard/keymaster/KMOperationState.java b/Applet/src/com/android/javacard/keymaster/KMOperationState.java index d8705de4..df2bcbb3 100644 --- a/Applet/src/com/android/javacard/keymaster/KMOperationState.java +++ b/Applet/src/com/android/javacard/keymaster/KMOperationState.java @@ -16,6 +16,7 @@ package com.android.javacard.keymaster; +import com.android.javacard.seprovider.KMOperation; import javacard.framework.JCSystem; import javacard.framework.Util; @@ -28,41 +29,63 @@ public class KMOperationState { public static final byte MAX_DATA = 20; - private static final byte OPERATION = 0; - private static final byte TRUE = 1; - private static final byte FALSE = 0; + public static final byte MAX_REFS = 1; + private static final byte DATA = 0; + private static final byte REFS = 1; // byte type private static final byte ALG = 0; private static final byte PURPOSE = 1; private static final byte PADDING = 2; - private static final byte BLOCKMODE = 3; + private static final byte BLOCK_MODE = 3; private static final byte DIGEST = 4; private static final byte FLAGS = 5; - // short type private static final byte KEY_SIZE = 6; - private static final byte MAC_LENGTH = 8; + private static final byte MAC_LENGTH = 7 ; + private static final byte MGF_DIGEST = 8; + public static final byte OPERATION_HANDLE_SIZE = 8; + public static final byte DATA_SIZE = 9; + public static final byte AUTH_TIME_SIZE = 8; + + // short type + + //private static final byte KEY_SIZE = 6; + //private static final byte MAC_LENGTH = 8; // Handle - currently this is short private static final byte OP_HANDLE = 10; // Auth time 64 bits private static final byte AUTH_TIME = 12; + // Flag masks - private static final byte AUTH_PER_OP_REQD = 1; - private static final byte SECURE_USER_ID_REQD = 2; - private static final byte AUTH_TIMEOUT_VALIDATED = 4; - private static final byte AES_GCM_UPDATE_ALLOWED = 8; + private static final short AUTH_PER_OP_REQD = 1; + private static final short SECURE_USER_ID_REQD = 2; + private static final short AUTH_TIMEOUT_VALIDATED = 4; + private static final short AES_GCM_UPDATE_ALLOWED = 8; + private static final byte ACTIVE = 1; + private static final byte INACTIVE = 0; // Object References - private byte[] data; - private Object[] objRefs; - private static KMOperationState prototype; - private byte[] isDataUpdated; + private byte[] opHandle; + private byte[] authTime; + private short[] data; + private Object[] operation; - private KMOperationState() { - data = JCSystem.makeTransientByteArray(MAX_DATA, JCSystem.CLEAR_ON_RESET); - objRefs = JCSystem.makeTransientObjectArray((short) 1, JCSystem.CLEAR_ON_RESET); - isDataUpdated = JCSystem.makeTransientByteArray((short) 1, JCSystem.CLEAR_ON_RESET); + /* + private static final byte OPERATION = 0; + private static KMOperation op; + private static Object[] slot; + private static KMOperationState prototype; + private static boolean dFlag; +*/ + + public KMOperationState() { + // data = JCSystem.makeTransientByteArray(MAX_DATA, JCSystem.CLEAR_ON_RESET); + opHandle = JCSystem.makeTransientByteArray(OPERATION_HANDLE_SIZE, JCSystem.CLEAR_ON_RESET); + authTime = JCSystem.makeTransientByteArray(AUTH_TIME_SIZE, JCSystem.CLEAR_ON_RESET); + data = JCSystem.makeTransientShortArray(DATA_SIZE, JCSystem.CLEAR_ON_RESET); + operation = JCSystem.makeTransientObjectArray((short)1, JCSystem.CLEAR_ON_RESET); + reset(); } - +/* private static KMOperationState proto() { if (prototype == null) { prototype = new KMOperationState(); @@ -70,78 +93,122 @@ private static KMOperationState proto() { return prototype; } - public static KMOperationState instance(short opHandle) { + public static KMOperationState instance(short opHandle, Object[] slot) { KMOperationState opState = proto(); opState.reset(); - Util.setShort(prototype.data, OP_HANDLE, opHandle); + Util.setShort(data, OP_HANDLE, opHandle); + KMOperationState.slot = slot; return opState; } - public static KMOperationState read(byte[] oprHandle, short off, byte[] data, short dataOff, Object opr) { + public static KMOperationState read(byte[] oprHandle, short off, Object[] slot) { KMOperationState opState = proto(); opState.reset(); - Util.arrayCopy(data, dataOff, prototype.data, (short) 0, (short) prototype.data.length); - prototype.objRefs[OPERATION] = opr; - Util.setShort(prototype.data, OP_HANDLE, KMInteger.uint_64(oprHandle, off)); + Util.arrayCopy((byte[]) slot[DATA], (short) 0, data, (short) 0, (short) data.length); + Object[] ops = ((Object[]) slot[REFS]); + op = (KMOperation) ops[OPERATION]; + Util.setShort(data, OP_HANDLE, KMInteger.uint_64(oprHandle, off)); + KMOperationState.slot = slot; return opState; } public void persist() { - if (FALSE == isDataUpdated[0]) { + if (!dFlag) { return; } - KMRepository.instance().persistOperation(data, - Util.getShort(data, OP_HANDLE), - (KMOperation) objRefs[OPERATION]); - isDataUpdated[0] = FALSE; + KMRepository.instance().persistOperation(data, Util.getShort(data, OP_HANDLE), op); + dFlag = false; } - public void setKeySize(short keySize) { - Util.setShort(data, KEY_SIZE, keySize); + private void dataUpdated() { + dFlag = true; } - public short getKeySize() { - return Util.getShort(data, KEY_SIZE); + 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); + ops[OPERATION] = null; + JCSystem.commitTransaction(); + reset(); } +*/ public void reset() { - isDataUpdated[0] = FALSE; - objRefs[OPERATION] = null; + byte index = 0; + while(index < DATA_SIZE){ + data[index] = KMType.INVALID_VALUE; + index++; + } + Util.arrayFillNonAtomic(opHandle, (short) 0, OPERATION_HANDLE_SIZE, (byte) 0); + Util.arrayFillNonAtomic(authTime, (short) 0, AUTH_TIME_SIZE, (byte) 0); + + if(null != operation[0]) + ((KMOperation)operation[0]).abort(); + + operation[0] = null; + /* + dFlag = false; + op = null; + slot = null; + Util.arrayFillNonAtomic( data, (short) 0, (short) data.length, (byte) 0); - } - private void dataUpdated() { - isDataUpdated[0] = TRUE; + */ +} + public short compare(byte[] handle, short start, short len){ + return Util.arrayCompare(handle, start, opHandle, (short)0, (short)opHandle.length); + } + public void setKeySize(short keySize) { + // Util.setShort(data, KEY_SIZE, keySize); + data[KEY_SIZE] = keySize; } - public void release() { - if (objRefs[OPERATION] != null) - ((KMOperation) objRefs[OPERATION]).abort(); - reset(); + public short getKeySize() { + // return Util.getShort(data, KEY_SIZE); + return data[KEY_SIZE]; } - public short getHandle() { - return Util.getShort(data, OP_HANDLE); + public short getHandle(){ + return KMInteger.uint_64(opHandle,(short)0); + } + public void setHandle(short handle){ + setHandle(KMInteger.cast(handle).getBuffer(), + KMInteger.cast(handle).getStartOff(), + KMInteger.cast(handle).length()); + } + public short getHandle(byte[] buf, short start) { + //return Util.getShort(KMOperationState.data, OP_HANDLE); + Util.arrayCopyNonAtomic(opHandle,(short)0, buf, start, (short)opHandle.length); + return (short) opHandle.length; } + public void setHandle(byte[] buf, short start, short len) { + Util.arrayCopyNonAtomic(buf,start, opHandle, (short)0, (short) opHandle.length); + //return KMByteBlob.instance(opHandle,(short)0, (short)8); + } public short getPurpose() { return data[PURPOSE]; } - public void setPurpose(byte purpose) { + public void setPurpose(short purpose) { data[PURPOSE] = purpose; - dataUpdated(); +// dataUpdated(); } - public void setOperation(KMOperation opr) { - objRefs[OPERATION] = opr; + public void setOperation(KMOperation op) { + operation[0] = op; + /*(op = operation; dataUpdated(); persist(); + */ } public KMOperation getOperation() { - return (KMOperation) objRefs[OPERATION]; + return (KMOperation) operation[0]; } public boolean isAuthPerOperationReqd() { @@ -157,21 +224,27 @@ public boolean isSecureUserIdReqd() { } public short getAuthTime() { - return KMInteger.uint_64(data, (short) AUTH_TIME); + return KMInteger.uint_64(authTime,(short) 0); + // return KMInteger.uint_64(data, (short) AUTH_TIME); } + public void setAuthTime(short time){ + setAuthTime(KMInteger.cast(time).getBuffer(), KMInteger.cast(time).getStartOff()); + } public void setAuthTime(byte[] timeBuf, short start) { - Util.arrayCopy(timeBuf, start, data, (short) AUTH_TIME, (short) 8); + Util.arrayCopyNonAtomic(timeBuf,start,authTime,(short)0, AUTH_TIME_SIZE); + /*Util.arrayCopy(timeBuf, start, data, (short) AUTH_TIME, (short) 8); dataUpdated(); + */ } public void setOneTimeAuthReqd(boolean flag) { if (flag) { - data[FLAGS] = (byte) (data[FLAGS] | SECURE_USER_ID_REQD); + data[FLAGS] = (short) (data[FLAGS] | SECURE_USER_ID_REQD); } else { - data[FLAGS] = (byte) (data[FLAGS] & (~SECURE_USER_ID_REQD)); + data[FLAGS] = (short) (data[FLAGS] & (~SECURE_USER_ID_REQD)); } - dataUpdated(); + // dataUpdated(); } public void setAuthTimeoutValidated(boolean flag) { @@ -180,52 +253,60 @@ public void setAuthTimeoutValidated(boolean flag) { } else { data[FLAGS] = (byte) (data[FLAGS] & (~AUTH_TIMEOUT_VALIDATED)); } - dataUpdated(); + //dataUpdated(); } public void setAuthPerOperationReqd(boolean flag) { if (flag) { - data[FLAGS] = (byte) (data[FLAGS] | AUTH_PER_OP_REQD); + data[FLAGS] = (short) (data[FLAGS] | AUTH_PER_OP_REQD); } else { - data[FLAGS] = (byte) (data[FLAGS] & (~AUTH_PER_OP_REQD)); + data[FLAGS] = (short) (data[FLAGS] & (~AUTH_PER_OP_REQD)); } - dataUpdated(); + //dataUpdated(); } - public byte getAlgorithm() { + public short getAlgorithm() { return data[ALG]; } - public void setAlgorithm(byte algorithm) { + public void setAlgorithm(short algorithm) { data[ALG] = algorithm; - dataUpdated(); + //dataUpdated(); } - public byte getPadding() { + public short getPadding() { return data[PADDING]; } - public void setPadding(byte padding) { + public void setPadding(short padding) { data[PADDING] = padding; - dataUpdated(); + //dataUpdated(); } - public byte getBlockMode() { - return data[BLOCKMODE]; + public short getBlockMode() { + return data[BLOCK_MODE]; } - public void setBlockMode(byte blockMode) { - data[BLOCKMODE] = blockMode; - dataUpdated(); + public void setBlockMode(short blockMode) { + data[BLOCK_MODE] = blockMode; + // dataUpdated(); } - public byte getDigest() { + public short getDigest() { return data[DIGEST]; } + public short getMgfDigest() { + return data[MGF_DIGEST]; + } + public void setDigest(byte digest) { data[DIGEST] = digest; - dataUpdated(); + // dataUpdated(); + } + + public void setMgfDigest(byte mgfDigest) { + data[MGF_DIGEST] = mgfDigest; } public boolean isAesGcmUpdateAllowed() { @@ -234,20 +315,56 @@ public boolean isAesGcmUpdateAllowed() { public void setAesGcmUpdateComplete() { data[FLAGS] = (byte) (data[FLAGS] & (~AES_GCM_UPDATE_ALLOWED)); - dataUpdated(); + //dataUpdated(); } public void setAesGcmUpdateStart() { data[FLAGS] = (byte) (data[FLAGS] | AES_GCM_UPDATE_ALLOWED); - dataUpdated(); + //dataUpdated(); } public void setMacLength(short length) { - Util.setShort(data, MAC_LENGTH, length); - dataUpdated(); + data[MAC_LENGTH] = length; + //Util.setShort(data, MAC_LENGTH, length); + //dataUpdated(); } public short getMacLength() { - return Util.getShort(data, MAC_LENGTH); + return data[MAC_LENGTH]; + //return Util.getShort(data, MAC_LENGTH); + } + public byte getBufferingMode(){ + short alg = getAlgorithm(); + short purpose = getPurpose(); + short digest = getDigest(); + short padding = getPadding(); + short blockMode = getBlockMode(); + + if(alg == KMType.RSA && digest == KMType.DIGEST_NONE && purpose == KMType.SIGN){ + return KMType.BUF_RSA_NO_DIGEST; + } + + if(alg == KMType.EC && digest == KMType.DIGEST_NONE && purpose == KMType.SIGN){ + return KMType.BUF_EC_NO_DIGEST; + } + + switch(alg) { + case KMType.AES: + if (purpose == KMType.DECRYPT && padding == KMType.PKCS7) { + return KMType.BUF_AES_PKCS7_DECRYPT_BLOCK_ALIGN; + } else if (purpose == KMType.DECRYPT && blockMode == KMType.GCM) { + return KMType.BUF_AES_GCM_DECRYPT_BLOCK_ALIGN; + } else if (blockMode == KMType.CBC || blockMode == KMType.ECB) { + return KMType.BUF_AES_BLOCK_ALIGN; + } + break; + case KMType.DES: + if (purpose == KMType.DECRYPT && padding == KMType.PKCS7) { + return KMType.BUF_DES_PKCS7_DECRYPT_BLOCK_ALIGN; + } else { + return KMType.BUF_DES_BLOCK_ALIGN; + } + } + return KMType.BUF_NONE; } } diff --git a/Applet/src/com/android/javacard/keymaster/KMPKCS8Decoder.java b/Applet/src/com/android/javacard/keymaster/KMPKCS8Decoder.java new file mode 100644 index 00000000..eb860e2d --- /dev/null +++ b/Applet/src/com/android/javacard/keymaster/KMPKCS8Decoder.java @@ -0,0 +1,210 @@ +package com.android.javacard.keymaster; + +import com.android.javacard.seprovider.KMException; +import javacard.framework.Util; + +public class KMPKCS8Decoder { + public static final byte ASN1_OCTET_STRING= 0x04; + public static final byte ASN1_SEQUENCE= 0x30; + public static final byte ASN1_INTEGER= 0x02; + public static final byte ASN1_A0_TAG = (byte) 0xA0; + public static final byte ASN1_A1_TAG = (byte) 0xA1; + public static final byte ASN1_BIT_STRING = 0x03; + public static final byte[] EC_CURVE = { + 0x06,0x08,0x2a,(byte)0x86,0x48,(byte)0xce,0x3d,0x03, + 0x01,0x07 + }; + public static final byte[] RSA_ALGORITHM = { + 0x06,0x09,0x2A,(byte)0x86,0x48,(byte)0x86, + (byte)0xF7,0x0D,0x01,0x01,0x01,0x05,0x00 + }; + public static final byte[] EC_ALGORITHM = { + 0x06,0x07,0x2a,(byte)0x86,0x48,(byte)0xce, + 0x3d,0x02,0x01,0x06,0x08,0x2a,(byte)0x86,0x48, + (byte)0xce,0x3d,0x03,0x01,0x07 + }; + private byte[] data; + private short start; + private short length; + private short cur; + private static KMPKCS8Decoder inst; + private KMPKCS8Decoder(){ + start = 0; + length = 0; + cur = 0; + } + + public short decodeRsa(short blob){ + init(blob); + decodeCommon((short)0, RSA_ALGORITHM); + return decodeRsaPrivateKey((short)0); + } + + public short decodeEc(short blob){ + init(blob); + decodeCommon((short)0, EC_ALGORITHM); + return decodeEcPrivateKey((short)1); + } + + public short decodeEcSubjectPublicKeyInfo(short blob) { + init(blob); + header(ASN1_SEQUENCE); + short len = header(ASN1_SEQUENCE); + short ecPublicInfo = KMByteBlob.instance(len); + getBytes(ecPublicInfo); + if(Util.arrayCompare( + KMByteBlob.cast(ecPublicInfo).getBuffer(), + KMByteBlob.cast(ecPublicInfo).getStartOff(), + EC_ALGORITHM, + (short)0,KMByteBlob.cast(ecPublicInfo).length()) !=0){ + KMException.throwIt(KMError.UNKNOWN_ERROR); + } + len = header(ASN1_BIT_STRING); + if(len < 1) KMException.throwIt(KMError.UNKNOWN_ERROR); + // TODO need to handle if unused bits are not zero + byte unusedBits = getByte(); + if(unusedBits != 0) KMException.throwIt(KMError.UNIMPLEMENTED); + short pubKey = KMByteBlob.instance((short)(len -1)); + getBytes(pubKey); + return pubKey; + } + + //Seq[Int,Int,Int,Int,] + public short decodeRsaPrivateKey(short version){ + short resp = KMArray.instance((short)3); + header(ASN1_OCTET_STRING); + header(ASN1_SEQUENCE); + short len =header(ASN1_INTEGER); + if(len != 1) KMException.throwIt(KMError.UNKNOWN_ERROR); + short ver = getByte(); + if(ver != version) KMException.throwIt(KMError.UNKNOWN_ERROR); + len = header(ASN1_INTEGER); + short modulus = KMByteBlob.instance(len); + getBytes(modulus); + updateModulus(modulus); + len = header(ASN1_INTEGER); + short pubKey = KMByteBlob.instance(len); + getBytes(pubKey); + len = header(ASN1_INTEGER); + short privKey = KMByteBlob.instance(len); + getBytes(privKey); + KMArray.cast(resp).add((short)0, modulus); + KMArray.cast(resp).add((short)1, pubKey); + KMArray.cast(resp).add((short)2, privKey); + return resp; + } + + private void updateModulus(short blob) { + byte[] buffer = KMByteBlob.cast(blob).getBuffer(); + short startOff = KMByteBlob.cast(blob).getStartOff(); + short len = KMByteBlob.cast(blob).length(); + if(0 == buffer[startOff] && len > 256) { + KMByteBlob.cast(blob).setStartOff(++startOff); + KMByteBlob.cast(blob).setLength(--len); + } + } + + // Seq [Int, Blob] + public void decodeCommon(short version, byte[] alg){ + short len = header(ASN1_SEQUENCE); + len = header(ASN1_INTEGER); + if(len != 1) KMException.throwIt(KMError.UNKNOWN_ERROR); + short ver = getByte(); + if(ver !=version) KMException.throwIt(KMError.UNKNOWN_ERROR); + len = header(ASN1_SEQUENCE); + short blob = KMByteBlob.instance(len); + getBytes(blob); + if(Util.arrayCompare( + KMByteBlob.cast(blob).getBuffer(), + KMByteBlob.cast(blob).getStartOff(), + alg, + (short)0,KMByteBlob.cast(blob).length()) !=0){ + KMException.throwIt(KMError.UNKNOWN_ERROR); + } + } + + //Seq[Int,blob,blob] + public short decodeEcPrivateKey(short version){ + short resp = KMArray.instance((short)2); + header(ASN1_OCTET_STRING); + header(ASN1_SEQUENCE); + short len = header(ASN1_INTEGER); + if(len != 1) KMException.throwIt(KMError.UNKNOWN_ERROR); + short ver = getByte(); + if(ver != version) KMException.throwIt(KMError.UNKNOWN_ERROR); + len = header(ASN1_OCTET_STRING); + short privKey = KMByteBlob.instance(len); + getBytes(privKey); + validateTag0IfPresent(); + header(ASN1_A1_TAG); + len = header(ASN1_BIT_STRING); + if(len < 1) KMException.throwIt(KMError.UNKNOWN_ERROR); + // TODO need to handle if unused bits are not zero + byte unusedBits = getByte(); + if(unusedBits != 0) KMException.throwIt(KMError.UNIMPLEMENTED); + short pubKey = KMByteBlob.instance((short)(len -1)); + getBytes(pubKey); + KMArray.cast(resp).add((short)0, pubKey); + KMArray.cast(resp).add((short)1, privKey); + return resp; + } + private void validateTag0IfPresent(){ + if(data[cur] != ASN1_A0_TAG) return;; + short len = header(ASN1_A0_TAG); + if(len != EC_CURVE.length) KMException.throwIt(KMError.UNKNOWN_ERROR); + if(Util.arrayCompare(data, cur, EC_CURVE, (short)0, len) != 0) KMException.throwIt(KMError.UNKNOWN_ERROR); + incrementCursor(len); + } + private short header(short tag){ + short t = getByte(); + if(t != tag) KMException.throwIt(KMError.UNKNOWN_ERROR); + return getLength(); + } + + private byte getByte(){ + byte d = data[cur]; + incrementCursor((short)1); + return d; + } + + private short getShort(){ + short d = Util.getShort(data, cur); + incrementCursor((short)2); + return d; + } + + private void getBytes(short blob){ + short len = KMByteBlob.cast(blob).length(); + Util.arrayCopyNonAtomic(data, cur, KMByteBlob.cast(blob).getBuffer(), + KMByteBlob.cast(blob).getStartOff(), len); + incrementCursor(len); + } + + private short getLength(){ + byte len = getByte(); + if(len >= 0) return len; + len = (byte)(len & 0x7F); + if(len == 1) return (short)(getByte() & 0xFF); + else if(len == 2) return getShort(); + else KMException.throwIt(KMError.UNKNOWN_ERROR); + return KMType.INVALID_VALUE; //should not come here + } + public static KMPKCS8Decoder instance() { + if (inst == null) { + inst = new KMPKCS8Decoder(); + } + return inst; + } + + public void init(short blob) { + data = KMByteBlob.cast(blob).getBuffer(); + start = KMByteBlob.cast(blob).getStartOff(); + length = KMByteBlob.cast(blob).length(); + cur = start; + } + + public void incrementCursor(short n){ + cur += n; + if(cur > ((short)(start+length))) KMException.throwIt(KMError.UNKNOWN_ERROR); + } +} diff --git a/Applet/src/com/android/javacard/keymaster/KMRepository.java b/Applet/src/com/android/javacard/keymaster/KMRepository.java index 6dfc2d0d..39340daf 100644 --- a/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 com.android.javacard.seprovider.KMException; +import com.android.javacard.seprovider.KMUpgradable; import org.globalplatform.upgrade.Element; import javacard.framework.ISO7816; @@ -29,79 +31,41 @@ */ public class KMRepository implements KMUpgradable { + public static final short HEAP_SIZE = 15000; + // Data table configuration - public static final short DATA_INDEX_SIZE = 22; + public static final short DATA_INDEX_SIZE = 12; public static final short DATA_INDEX_ENTRY_SIZE = 4; - public static final short DATA_MEM_SIZE = 2048; - public static final short HEAP_SIZE = 10000; public static final short DATA_INDEX_ENTRY_LENGTH = 0; public static final short DATA_INDEX_ENTRY_OFFSET = 2; - public static final short OPERATION_HANDLE_SIZE = 8; /* 8 bytes */ - private static final short OPERATION_HANDLE_STATUS_OFFSET = 0; - private static final short OPERATION_HANDLE_STATUS_SIZE = 1; - private static final short OPERATION_HANDLE_OFFSET = 1; - private static final short OPERATION_HANDLE_ENTRY_SIZE = - OPERATION_HANDLE_SIZE + OPERATION_HANDLE_STATUS_SIZE; - private static final byte POWER_RESET_STATUS_FLAG = (byte) 0xEF; + + //TODO reduced data table size from 2048 to 300. + public static final short DATA_MEM_SIZE = 300; // Data table offsets - public static final byte COMPUTED_HMAC_KEY = 8; - public static final byte HMAC_NONCE = 9; - public static final byte ATT_ID_BRAND = 0; - public static final byte ATT_ID_DEVICE = 1; - public static final byte ATT_ID_PRODUCT = 2; - public static final byte ATT_ID_SERIAL = 3; - public static final byte ATT_ID_IMEI = 4; - public static final byte ATT_ID_MEID = 5; - public static final byte ATT_ID_MANUFACTURER = 6; - public static final byte ATT_ID_MODEL = 7; - public static final byte CERT_ISSUER = 10; - public static final byte CERT_EXPIRY_TIME = 11; - public static final byte BOOT_OS_VERSION = 12; - public static final byte BOOT_OS_PATCH_LEVEL = 13; - public static final byte VENDOR_PATCH_LEVEL = 14; - public static final byte BOOT_PATCH_LEVEL = 15; - public static final byte BOOT_VERIFIED_BOOT_KEY = 16; - public static final byte BOOT_VERIFIED_BOOT_HASH = 17; - public static final byte BOOT_VERIFIED_BOOT_STATE = 18; - public static final byte BOOT_DEVICE_LOCKED_STATUS = 19; - public static final byte DEVICE_LOCKED_TIME = 20; - public static final byte DEVICE_LOCKED = 21; + public static final byte COMPUTED_HMAC_KEY = 0; + public static final byte HMAC_NONCE = 1; + public static final byte BOOT_OS_VERSION = 2; + public static final byte BOOT_OS_PATCH = 3; + public static final byte VENDOR_PATCH_LEVEL = 4; + public static final byte DEVICE_LOCKED_TIME = 5; + public static final byte DEVICE_LOCKED = 6; // Data Item sizes - public static final short MASTER_KEY_SIZE = 16; - public static final short SHARED_SECRET_KEY_SIZE = 32; public static final short HMAC_SEED_NONCE_SIZE = 32; public static final short 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_OPS = 4; - public static final byte BOOT_KEY_MAX_SIZE = 32; - public static final byte BOOT_HASH_MAX_SIZE = 32; // Class Attributes - private Object[] operationStateTable; private byte[] heap; private short[] heapIndex; private byte[] dataTable; private short dataIndex; - private short[] reclaimIndex; - // This variable is used to monitor the power reset status as the Applet does not get - // any power reset event. Initially the value of this variable is set to POWER_RESET_STATUS_FLAG. - // If the power reset happens then this value becomes 0. - private byte[] powerResetStatus; - - // Operation table. - private static final short OPER_TABLE_DATA_OFFSET = 0; - private static final short OPER_TABLE_OPR_OFFSET = 1; - private static final short OPER_DATA_LEN = OPERATION_HANDLE_ENTRY_SIZE + KMOperationState.MAX_DATA; - private static final short DATA_ARRAY_LENGTH = MAX_OPS * OPER_DATA_LEN; - + private short reclaimIndex; // Singleton instance private static KMRepository repository; @@ -111,203 +75,19 @@ public static KMRepository instance() { } public KMRepository(boolean isUpgrading) { - heap = JCSystem.makeTransientByteArray(HEAP_SIZE, JCSystem.CLEAR_ON_RESET); - heapIndex = JCSystem.makeTransientShortArray((short) 1, JCSystem.CLEAR_ON_RESET); - reclaimIndex = JCSystem.makeTransientShortArray((short) 1, JCSystem.CLEAR_ON_RESET); - powerResetStatus = JCSystem.makeTransientByteArray((short) 1, JCSystem.CLEAR_ON_RESET); - heapIndex[0] = (short) 0; - reclaimIndex[0] = HEAP_SIZE; - powerResetStatus[0] = POWER_RESET_STATUS_FLAG; newDataTable(isUpgrading); - - operationStateTable = new Object[2]; - operationStateTable[0] = JCSystem.makeTransientByteArray(DATA_ARRAY_LENGTH, JCSystem.CLEAR_ON_RESET); - operationStateTable[1] = JCSystem.makeTransientObjectArray(MAX_OPS, JCSystem.CLEAR_ON_RESET); + heap = JCSystem.makeTransientByteArray(HEAP_SIZE, JCSystem.CLEAR_ON_RESET); + heapIndex = JCSystem.makeTransientShortArray((short)1, JCSystem.CLEAR_ON_RESET); + reclaimIndex = HEAP_SIZE; //Initialize the device locked status if (!isUpgrading) { setDeviceLock(false); setDeviceLockPasswordOnly(false); - } else { - // In case of upgrade, the applet is deleted and installed again so all - // volatile memory is erased. so it is necessary to force the power reset flag - // to 0 so that the HAL can clear its operation state. - powerResetStatus[0] = (byte) 0; } repository = this; } - // This function checks if card reset event occurred and this function - // should only be called before processing any of the APUs. - // Transient memory is cleared in two cases: - // 1. Card reset event - // 2. Applet upgrade. - public boolean isPowerResetEventOccurred() { - if (powerResetStatus[0] == POWER_RESET_STATUS_FLAG) { - return false; - } - return true; - } - - /** - * This function sets the power reset status flag to its - * default value. - */ - public void restorePowerResetStatus() { - powerResetStatus[0] = POWER_RESET_STATUS_FLAG; - } - - public void getOperationHandle(short oprHandle, byte[] buf, short off, short len) { - if (KMInteger.cast(oprHandle).length() != OPERATION_HANDLE_SIZE) { - KMException.throwIt(KMError.INVALID_OPERATION_HANDLE); - } - KMInteger.cast(oprHandle).getValue(buf, off, len); - } - - public KMOperationState findOperation(byte[] buf, short off, short len) { - short index = 0; - byte[] oprTableData; - short offset = 0; - oprTableData = (byte[]) operationStateTable[OPER_TABLE_DATA_OFFSET]; - Object[] operations = (Object[]) operationStateTable[OPER_TABLE_OPR_OFFSET]; - while (index < MAX_OPS) { - offset = (short) (index * OPER_DATA_LEN); - if (0 == Util.arrayCompare(buf, off, oprTableData, (short) (offset + OPERATION_HANDLE_OFFSET), len)) { - return KMOperationState.read(oprTableData, (short) (offset + OPERATION_HANDLE_OFFSET), oprTableData, - (short) (offset + OPERATION_HANDLE_ENTRY_SIZE), - operations[index]); - } - index++; - } - return null; - } - - /* operationHandle is a KMInteger */ - public KMOperationState findOperation(short operationHandle) { - short buf = KMByteBlob.instance(OPERATION_HANDLE_SIZE); - getOperationHandle( - operationHandle, - KMByteBlob.cast(buf).getBuffer(), - KMByteBlob.cast(buf).getStartOff(), - KMByteBlob.cast(buf).length()); - return findOperation( - KMByteBlob.cast(buf).getBuffer(), - KMByteBlob.cast(buf).getStartOff(), - KMByteBlob.cast(buf).length()); - } - - /* opHandle is a KMInteger */ - public KMOperationState reserveOperation(short opHandle) { - short index = 0; - byte[] oprTableData = (byte[]) operationStateTable[OPER_TABLE_DATA_OFFSET]; - short offset = 0; - while (index < MAX_OPS) { - offset = (short) (index * OPER_DATA_LEN); - /* Check for unreserved operation state */ - if (oprTableData[(short) (offset + OPERATION_HANDLE_STATUS_OFFSET)] == 0) { - return KMOperationState.instance(opHandle); - } - index++; - } - return null; - } - - public void persistOperation(byte[] data, short opHandle, KMOperation op) { - short index = 0; - byte[] oprTableData = (byte[]) operationStateTable[OPER_TABLE_DATA_OFFSET]; - Object[] operations = (Object[]) operationStateTable[OPER_TABLE_OPR_OFFSET]; - short offset = 0; - short buf = KMByteBlob.instance(OPERATION_HANDLE_SIZE); - getOperationHandle( - opHandle, - KMByteBlob.cast(buf).getBuffer(), - KMByteBlob.cast(buf).getStartOff(), - KMByteBlob.cast(buf).length()); - //Update an existing operation state. - while (index < MAX_OPS) { - offset = (short) (index * OPER_DATA_LEN); - if ((1 == oprTableData[(short) (offset + OPERATION_HANDLE_STATUS_OFFSET)]) - && (0 == Util.arrayCompare( - oprTableData, - (short) (offset + OPERATION_HANDLE_OFFSET), - KMByteBlob.cast(buf).getBuffer(), - KMByteBlob.cast(buf).getStartOff(), - KMByteBlob.cast(buf).length()))) { - Util.arrayCopy(data, (short) 0, oprTableData, (short) (offset + OPERATION_HANDLE_ENTRY_SIZE), - KMOperationState.MAX_DATA); - operations[index] = op; - return; - } - index++; - } - - index = 0; - //Persist a new operation. - while (index < MAX_OPS) { - offset = (short) (index * OPER_DATA_LEN); - if (0 == oprTableData[(short) (offset + OPERATION_HANDLE_STATUS_OFFSET)]) { - oprTableData[(short) (offset + OPERATION_HANDLE_STATUS_OFFSET)] = 1;/*reserved */ - Util.arrayCopy( - KMByteBlob.cast(buf).getBuffer(), - KMByteBlob.cast(buf).getStartOff(), - oprTableData, - (short) (offset + OPERATION_HANDLE_OFFSET), - OPERATION_HANDLE_SIZE); - Util.arrayCopy(data, (short) 0, oprTableData, (short) (offset + OPERATION_HANDLE_ENTRY_SIZE), - KMOperationState.MAX_DATA); - operations[index] = op; - break; - } - index++; - } - } - - public void releaseOperation(KMOperationState op) { - short index = 0; - byte[] oprTableData = (byte[]) operationStateTable[OPER_TABLE_DATA_OFFSET]; - Object[] operations = (Object[]) operationStateTable[OPER_TABLE_OPR_OFFSET]; - short offset = 0; - short buf = KMByteBlob.instance(OPERATION_HANDLE_SIZE); - getOperationHandle( - op.getHandle(), - KMByteBlob.cast(buf).getBuffer(), - KMByteBlob.cast(buf).getStartOff(), - KMByteBlob.cast(buf).length()); - while (index < MAX_OPS) { - offset = (short) (index * OPER_DATA_LEN); - if ((oprTableData[(short) (offset + OPERATION_HANDLE_STATUS_OFFSET)] == 1) && - (0 == Util.arrayCompare(oprTableData, - (short) (offset + OPERATION_HANDLE_OFFSET), - KMByteBlob.cast(buf).getBuffer(), - KMByteBlob.cast(buf).getStartOff(), - KMByteBlob.cast(buf).length()))) { - Util.arrayFillNonAtomic(oprTableData, offset, OPER_DATA_LEN, (byte) 0); - op.release(); - operations[index] = null; - break; - } - index++; - } - } - - public void releaseAllOperations() { - short index = 0; - byte[] oprTableData = (byte[]) operationStateTable[OPER_TABLE_DATA_OFFSET]; - Object[] operations = (Object[]) operationStateTable[OPER_TABLE_OPR_OFFSET]; - short offset = 0; - while (index < MAX_OPS) { - offset = (short) (index * OPER_DATA_LEN); - if (oprTableData[(short) (offset + OPERATION_HANDLE_STATUS_OFFSET)] == 1) { - Util.arrayFillNonAtomic(oprTableData, offset, OPER_DATA_LEN, (byte) 0); - if (operations[index] != null) { - ((KMOperation) operations[index]).abort(); - operations[index] = null; - } - } - index++; - } - } - public void initComputedHmac(byte[] key, short start, short len) { if (len != COMPUTED_HMAC_KEY_SIZE) { KMException.throwIt(KMError.INVALID_INPUT_LENGTH); @@ -340,8 +120,8 @@ public void onProcess() { public void clean() { Util.arrayFillNonAtomic(heap, (short) 0, heapIndex[0], (byte) 0); - heapIndex[0] = (short) 0; - reclaimIndex[0] = HEAP_SIZE; + heapIndex[0] = 0; + reclaimIndex = HEAP_SIZE; } public void onDeselect() { @@ -354,20 +134,20 @@ public void onSelect() { // This function uses memory from the back of the heap(transient memory). Call // reclaimMemory function immediately after the use. public short allocReclaimableMemory(short length) { - if ((((short) (reclaimIndex[0] - length)) <= heapIndex[0]) + if ((((short) (reclaimIndex - length)) <= heapIndex[0]) || (length >= HEAP_SIZE / 2)) { ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); } - reclaimIndex[0] -= length; - return reclaimIndex[0]; + reclaimIndex -= length; + return reclaimIndex; } // Reclaims the memory back. public void reclaimMemory(short length) { - if (reclaimIndex[0] < heapIndex[0]) { + if (reclaimIndex < heapIndex[0]) { ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); } - reclaimIndex[0] += length; + reclaimIndex += length; } public short allocAvailableMemory() { @@ -381,7 +161,7 @@ public short allocAvailableMemory() { public short alloc(short length) { if ((((short) (heapIndex[0] + length)) > heap.length) || - (((short) (heapIndex[0] + length)) > reclaimIndex[0])) { + (((short) (heapIndex[0] + length)) > reclaimIndex)) { ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); } heapIndex[0] += length; @@ -396,7 +176,6 @@ private short dataAlloc(short length) { return (short) (dataIndex - length); } - private void newDataTable(boolean isUpgrading) { if (!isUpgrading) { if (dataTable == null) { @@ -406,20 +185,6 @@ private void newDataTable(boolean isUpgrading) { } } - public void restoreData(short blob) { - JCSystem.beginTransaction(); - Util.arrayCopy( - KMByteBlob.cast(blob).getBuffer(), KMByteBlob.cast(blob).getStartOff(), dataTable, - (short) 0, - KMByteBlob.cast(blob).length() - ); - JCSystem.commitTransaction(); - } - - public byte[] getDataTable() { - return dataTable; - } - private void clearDataEntry(short id) { JCSystem.beginTransaction(); id = (short) (id * DATA_INDEX_ENTRY_SIZE); @@ -482,29 +247,6 @@ public short getComputedHmacKey() { return readData(COMPUTED_HMAC_KEY); } - public void persistAttId(byte id, byte[] buf, short start, short len) { - writeDataEntry(id, buf, start, len); - } - - public short getAttId(byte id) { - return readData(id); - } - - public void deleteAttIds() { - clearDataEntry(ATT_ID_BRAND); - clearDataEntry(ATT_ID_MEID); - clearDataEntry(ATT_ID_DEVICE); - clearDataEntry(ATT_ID_IMEI); - clearDataEntry(ATT_ID_MODEL); - clearDataEntry(ATT_ID_PRODUCT); - clearDataEntry(ATT_ID_SERIAL); - clearDataEntry(ATT_ID_MANUFACTURER); - } - - public short getIssuer() { - return readData(CERT_ISSUER); - } - public short readData(short id) { short blob = KMByteBlob.instance(dataLength(id)); if (readDataEntry(id, KMByteBlob.cast(blob).getBuffer(), KMByteBlob.cast(blob).getStartOff()) @@ -514,19 +256,6 @@ public short readData(short id) { return blob; } - public void setIssuer(byte[] buf, short start, short len) { - writeDataEntry(CERT_ISSUER, buf, start, len); - } - - - public short getCertExpiryTime() { - return readData(CERT_EXPIRY_TIME); - } - - public void setCertExpiryTime(byte[] buf, short start, short len) { - writeDataEntry(CERT_EXPIRY_TIME, buf, start, len); - } - private static final byte[] zero = {0, 0, 0, 0, 0, 0, 0, 0}; public short getOsVersion() { @@ -549,18 +278,9 @@ public short getVendorPatchLevel() { } } - 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_LEVEL); + short blob = readData(BOOT_OS_PATCH); if (blob != 0) { return KMInteger.uint_32( KMByteBlob.cast(blob).getBuffer(), KMByteBlob.cast(blob).getStartOff()); @@ -569,62 +289,6 @@ public short getOsPatch() { } } - public short readROT() { - short totalLength = 0; - short length = dataLength(BOOT_VERIFIED_BOOT_KEY); - if (length == 0) { - return KMType.INVALID_VALUE; - } - totalLength += length; - if ((length = dataLength(BOOT_VERIFIED_BOOT_HASH)) == 0) { - return KMType.INVALID_VALUE; - } - totalLength += length; - if ((length = dataLength(BOOT_VERIFIED_BOOT_STATE)) == 0) { - return KMType.INVALID_VALUE; - } - totalLength += length; - if ((length = dataLength(BOOT_DEVICE_LOCKED_STATUS)) == 0) { - return KMType.INVALID_VALUE; - } - totalLength += length; - - short blob = KMByteBlob.instance(totalLength); - length = readDataEntry(BOOT_VERIFIED_BOOT_KEY, KMByteBlob.cast(blob) - .getBuffer(), KMByteBlob.cast(blob).getStartOff()); - - length += readDataEntry(BOOT_VERIFIED_BOOT_HASH, KMByteBlob.cast(blob) - .getBuffer(), - (short) (KMByteBlob.cast(blob).getStartOff() + length)); - - length += readDataEntry(BOOT_VERIFIED_BOOT_STATE, KMByteBlob.cast(blob) - .getBuffer(), - (short) (KMByteBlob.cast(blob).getStartOff() + length)); - - readDataEntry(BOOT_DEVICE_LOCKED_STATUS, KMByteBlob.cast(blob) - .getBuffer(), - (short) (KMByteBlob.cast(blob).getStartOff() + length)); - return blob; - } - - public short getVerifiedBootKey() { - return readData(BOOT_VERIFIED_BOOT_KEY); - } - - public short getVerifiedBootHash() { - return readData(BOOT_VERIFIED_BOOT_HASH); - } - - public boolean getBootLoaderLock() { - short blob = readData(BOOT_DEVICE_LOCKED_STATUS); - return (byte) ((getHeap())[KMByteBlob.cast(blob).getStartOff()] & 0xFE) != 0; - } - - public byte getBootState() { - short blob = readData(BOOT_VERIFIED_BOOT_STATE); - return (getHeap())[KMByteBlob.cast(blob).getStartOff()]; - } - public boolean getDeviceLock() { short blob = readData(DEVICE_LOCKED); return (byte) ((getHeap())[KMByteBlob.cast(blob).getStartOff()] & 0xFE) != 0; @@ -659,31 +323,6 @@ public void setVendorPatchLevel(byte[] buf, short start, short len) { 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 clearAndroidSystemProperties() { - clearDataEntry(BOOT_OS_VERSION); - clearDataEntry(BOOT_OS_PATCH_LEVEL); - clearDataEntry(VENDOR_PATCH_LEVEL); - // Don't clear BOOT_PATCH_LEVEL as it is part of - // boot parameters. - } - - public void setBootloaderLocked(boolean flag) { - short start = alloc(DEVICE_LOCK_FLAG_SIZE); - if (flag) { - (getHeap())[start] = (byte) ((getHeap())[start] | 0x01); - } else { - (getHeap())[start] = (byte) ((getHeap())[start] & 0xFE); - } - writeDataEntry(BOOT_DEVICE_LOCKED_STATUS, getHeap(), start, DEVICE_LOCK_FLAG_SIZE); - } - public void setDeviceLock(boolean flag) { short start = alloc(DEVICE_LOCK_FLAG_SIZE); if (flag) { @@ -719,30 +358,8 @@ public void setOsPatch(byte[] buf, short start, short len) { if (len != OS_PATCH_SIZE) { KMException.throwIt(KMError.INVALID_INPUT_LENGTH); } - writeDataEntry(BOOT_OS_PATCH_LEVEL, buf, start, len); + writeDataEntry(BOOT_OS_PATCH, buf, start, len); } - - public void setVerifiedBootKey(byte[] buf, short start, short len) { - if (len > BOOT_KEY_MAX_SIZE) { - KMException.throwIt(KMError.INVALID_INPUT_LENGTH); - } - writeDataEntry(BOOT_VERIFIED_BOOT_KEY, buf, start, len); - } - - - public void setVerifiedBootHash(byte[] buf, short start, short len) { - if (len > BOOT_HASH_MAX_SIZE) { - KMException.throwIt(KMError.INVALID_INPUT_LENGTH); - } - writeDataEntry(BOOT_VERIFIED_BOOT_HASH, buf, start, len); - } - - public void setBootState(byte state) { - short start = alloc(BOOT_STATE_SIZE); - (getHeap())[start] = state; - writeDataEntry(BOOT_VERIFIED_BOOT_STATE, getHeap(), start, BOOT_STATE_SIZE); - } - @Override public void onSave(Element ele) { ele.write(dataIndex); diff --git a/Applet/src/com/android/javacard/keymaster/KMSimpleValue.java b/Applet/src/com/android/javacard/keymaster/KMSimpleValue.java new file mode 100644 index 00000000..794f3f92 --- /dev/null +++ b/Applet/src/com/android/javacard/keymaster/KMSimpleValue.java @@ -0,0 +1,69 @@ +package com.android.javacard.keymaster; + +import javacard.framework.ISO7816; +import javacard.framework.ISOException; +import javacard.framework.Util; + +public class KMSimpleValue extends KMType { + private static KMSimpleValue prototype; + + public static final byte FALSE = (byte) 20; + public static final byte TRUE = (byte) 21; + public static final byte NULL = (byte) 22; + + + private KMSimpleValue() { + } + + private static KMSimpleValue proto(short ptr) { + if (prototype == null) { + prototype = new KMSimpleValue(); + } + instanceTable[KM_SIMPLE_VALUE_OFFSET] = ptr; + return prototype; + } + + // pointer to an empty instance used as expression + public static short exp() { + return KMType.exp(SIMPLE_VALUE_TYPE); + } + + public short length() { + return Util.getShort(heap, (short) (instanceTable[KM_SIMPLE_VALUE_OFFSET] + 1)); + } + + public static KMSimpleValue cast(short ptr) { + if (heap[ptr] != SIMPLE_VALUE_TYPE) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + if (!isSimpleValueValid(heap[(short) (ptr + 3)])) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + return proto(ptr); + } + + public static short instance(byte value) { + if (!isSimpleValueValid(value)) { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + short ptr = KMType.instance(SIMPLE_VALUE_TYPE, (short) 1); + heap[(short) (ptr + 3)] = value; + return ptr; + } + + public byte getValue() { + return heap[(short) (instanceTable[KM_SIMPLE_VALUE_OFFSET] + 3)]; + } + + private static boolean isSimpleValueValid(byte value) { + switch (value) { + case TRUE: + case FALSE: + case NULL: + break; + default: + return false; + } + return true; + } +} diff --git a/Applet/src/com/android/javacard/keymaster/KMTag.java b/Applet/src/com/android/javacard/keymaster/KMTag.java index fa9bb38e..0cac460e 100644 --- a/Applet/src/com/android/javacard/keymaster/KMTag.java +++ b/Applet/src/com/android/javacard/keymaster/KMTag.java @@ -16,6 +16,7 @@ package com.android.javacard.keymaster; +import com.android.javacard.seprovider.KMException; import javacard.framework.Util; /** @@ -28,7 +29,6 @@ * of KMType. */ public class KMTag extends KMType { - public static short getTagType(short ptr) { return Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE)); } @@ -36,4 +36,64 @@ public static short getTagType(short ptr) { public static short getKey(short ptr) { return Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2)); } + + public static void assertPresence(short params, short tagType, short tagKey, short error){ + if(!isPresent(params, tagType, tagKey)){ + KMException.throwIt(error); + } + } + public static void assertAbsence(short params, short tagType, short tagKey, short error){ + if(isPresent(params, tagType, tagKey)){ + KMException.throwIt(error); + } + } + + public static boolean isPresent(short params, short tagType, short tagKey){ + short tag = KMKeyParameters.findTag(tagType, tagKey, params); + return tag != KMType.INVALID_VALUE; + } + + public static boolean isEqual(short params, short tagType, short tagKey, short value){ + switch(tagType){ + case KMType.ENUM_TAG: + return KMEnumTag.getValue(tagKey, params) == value; + case KMType.UINT_TAG: + case KMType.DATE_TAG: + case KMType.ULONG_TAG: + return KMIntegerTag.isEqual(params, tagType, tagKey, value); + case KMType.ENUM_ARRAY_TAG: + return KMEnumArrayTag.contains(tagKey,value,params); + case KMType.UINT_ARRAY_TAG: + case KMType.ULONG_ARRAY_TAG: + return KMIntegerArrayTag.contains(tagKey, value, params); + } + return false; + } + public static void assertTrue(boolean condition, short error){ + if(!condition){ + KMException.throwIt(error); + } + } + + public static boolean isValidPublicExponent(short params) { + short pubExp = KMKeyParameters.findTag(KMType.ULONG_TAG, KMType.RSA_PUBLIC_EXPONENT, params); + if(pubExp == KMType.INVALID_VALUE){ + return false; + } + pubExp = KMIntegerTag.cast(pubExp).getValue(); + if(!(KMInteger.cast(pubExp).getShort() == 0x01 && + KMInteger.cast(pubExp).getSignificantShort() == 0x01)){ + return false; + } + return true; + } + + public static boolean isValidKeySize(short params){ + short keysize = KMKeyParameters.findTag(KMType.UINT_TAG, KMType.KEYSIZE, params); + if(keysize == KMType.INVALID_VALUE){ + return false; + } + short alg = KMEnumTag.getValue(KMType.ALGORITHM, params); + return KMIntegerTag.cast(keysize).isValidKeySize((byte)alg); + } } diff --git a/Applet/src/com/android/javacard/keymaster/KMTextString.java b/Applet/src/com/android/javacard/keymaster/KMTextString.java new file mode 100644 index 00000000..310cfa01 --- /dev/null +++ b/Applet/src/com/android/javacard/keymaster/KMTextString.java @@ -0,0 +1,80 @@ +/* + * Copyright(C) 2021 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.Util; + +/** + * KMTextString represents contiguous block of bytes. It corresponds to CBOR type of Text String. It + * extends KMByteBlob by specifying value field as zero or more sequence of bytes. struct{ byte + * TEXT_STR_TYPE; short length; sequence of bytes} + */ +public class KMTextString extends KMByteBlob { + private static short OFFSET_SIZE = 2; + + private static KMTextString prototype; + + private KMTextString() { + } + + private static KMTextString proto(short ptr) { + if (prototype == null) { + prototype = new KMTextString(); + } + instanceTable[KM_TEXT_STRING_OFFSET] = ptr; + return prototype; + } + + // pointer to an empty instance used as expression + public static short exp() { + return KMType.exp(TEXT_STRING_TYPE); + } + + // return an empty byte blob instance + public static short instance(short length) { + short ptr = KMType.instance(TEXT_STRING_TYPE, (short) (length + OFFSET_SIZE)); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), + (short) (ptr + TLV_HEADER_SIZE + OFFSET_SIZE)); + Util.setShort(heap, (short)(ptr + 1), length); + return ptr; + } + + // byte blob from existing buf + public static short instance(byte[] buf, short startOff, short length) { + short ptr = instance(length); + Util.arrayCopyNonAtomic(buf, startOff, heap, + (short) (ptr + TLV_HEADER_SIZE + OFFSET_SIZE), length); + return ptr; + } + + // cast the ptr to KMTextString + public static KMTextString cast(short ptr) { + if (heap[ptr] != TEXT_STRING_TYPE) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + if (Util.getShort(heap, (short) (ptr + 1)) == INVALID_VALUE) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + return proto(ptr); + } + + protected short getBaseOffset() { + return instanceTable[KM_TEXT_STRING_OFFSET]; + } +} diff --git a/Applet/src/com/android/javacard/keymaster/KMType.java b/Applet/src/com/android/javacard/keymaster/KMType.java index f571275c..499c7636 100644 --- a/Applet/src/com/android/javacard/keymaster/KMType.java +++ b/Applet/src/com/android/javacard/keymaster/KMType.java @@ -44,6 +44,20 @@ public abstract class KMType { public static final byte VERIFICATION_TOKEN_TYPE = 0x09; public static final byte HMAC_SHARING_PARAM_TYPE = 0x0A; public static final byte X509_CERT = 0x0B; + public static final byte NEG_INTEGER_TYPE = 0x0C; + public static final byte TEXT_STRING_TYPE = 0x0D; + public static final byte MAP_TYPE = 0x0E; + public static final byte COSE_KEY_TYPE = 0x0F; + public static final byte COSE_PAIR_TAG_TYPE = 0x10; + public static final byte COSE_PAIR_INT_TAG_TYPE = 0x20; + public static final byte COSE_PAIR_NEG_INT_TAG_TYPE = 0x30; + public static final byte COSE_PAIR_BYTE_BLOB_TAG_TYPE = 0x40; + public static final byte COSE_PAIR_COSE_KEY_TAG_TYPE = 0x60; + public static final byte COSE_PAIR_SIMPLE_VALUE_TAG_TYPE = 0x70; + public static final byte COSE_PAIR_TEXT_STR_TAG_TYPE = (byte) 0x80; + public static final byte SIMPLE_VALUE_TYPE = (byte) 0x90; + public static final byte COSE_HEADERS_TYPE = (byte) 0xA0; + public static final byte COSE_CERT_PAYLOAD_TYPE = (byte) 0xB0; // Tag Types public static final short INVALID_TAG = 0x0000; public static final short ENUM_TAG = 0x1000; @@ -59,6 +73,11 @@ public abstract class KMType { public static final short TAG_TYPE_MASK = (short) 0xF000; // Enum Tag + // Internal tags + public static final short RULE = 0x7FFF; + public static final byte IGNORE_INVALID_TAGS = 0x00; + public static final byte FAIL_ON_INVALID_TAGS = 0x01; + // Algorithm Enum Tag key and values public static final short ALGORITHM = 0x0002; public static final byte RSA = 0x01; @@ -143,9 +162,10 @@ public abstract class KMType { public static final byte DECRYPT = 0x01; public static final byte SIGN = 0x02; public static final byte VERIFY = 0x03; + public static final byte DERIVE_KEY = 0x04; public static final byte WRAP_KEY = 0x05; - public static final byte ATTEST_KEY = (byte) 0x7F; - + public static final byte AGREE_KEY = 0x06; + public static final byte ATTEST_KEY = (byte) 0x07; // Block mode public static final short BLOCK_MODE = 0x0004; public static final byte ECB = 0x01; @@ -172,6 +192,9 @@ public abstract class KMType { public static final byte RSA_PKCS1_1_5_SIGN = 0x05; public static final byte PKCS7 = 0x40; + // OAEP MGF Digests - only SHA-1 is supported in Javacard + public static final short RSA_OAEP_MGF_DIGEST = 0xCB; + // Integer Tag - UINT, ULONG and DATE // UINT tags // Keysize @@ -196,6 +219,8 @@ public abstract class KMType { public static final short BOOT_PATCH_LEVEL = 0x02CF; // Mac Length public static final short MAC_LENGTH = 0x03EB; + // Usage Count Limit + public static final short USAGE_COUNT_LIMIT = 0x195; // ULONG tags // RSA Public Exponent @@ -205,8 +230,9 @@ public abstract class KMType { public static final short ACTIVE_DATETIME = 0x0190; public static final short ORIGINATION_EXPIRE_DATETIME = 0x0191; public static final short USAGE_EXPIRE_DATETIME = 0x0192; - public static final short CREATION_DATETIME = 0x02BD;//0x0193; - + public static final short CREATION_DATETIME = 0x02BD;; + public static final short CERTIFICATE_NOT_BEFORE = 0x03F0; + public static final short CERTIFICATE_NOT_AFTER = 0x03F1; // Integer Array Tags - ULONG_REP and UINT_REP. // User Secure Id public static final short USER_SECURE_ID = (short) 0x01F6; @@ -272,10 +298,16 @@ public abstract class KMType { public static final short NONCE = (short) 0x03E9; // Confirmation Token public static final short CONFIRMATION_TOKEN = (short) 0x03ED; + // Serial Number - this is a big num but in applet we handle it as byte blob + public static final short CERTIFICATE_SERIAL_NUM = (short) 0x03EE; + // Subject Name + public static final short CERTIFICATE_SUBJECT_NAME = (short) 0x03EF; public static final short LENGTH_FROM_PDU = (short) 0xFFFF; public static final byte NO_VALUE = (byte) 0xff; + // Support Curves for Eek Chain validation. + public static final byte RKP_CURVE_P256 = 1; // Type offsets. public static final byte KM_TYPE_BASE_OFFSET = 0; public static final byte KM_ARRAY_OFFSET = KM_TYPE_BASE_OFFSET; @@ -293,11 +325,40 @@ public abstract class KMType { public static final byte KM_KEY_CHARACTERISTICS_OFFSET = KM_TYPE_BASE_OFFSET + 12; public static final byte KM_KEY_PARAMETERS_OFFSET = KM_TYPE_BASE_OFFSET + 13; public static final byte KM_VERIFICATION_TOKEN_OFFSET = KM_TYPE_BASE_OFFSET + 14; + public static final byte KM_NEG_INTEGER_OFFSET = KM_TYPE_BASE_OFFSET + 15; + public static final byte KM_TEXT_STRING_OFFSET = KM_TYPE_BASE_OFFSET + 16; + public static final byte KM_MAP_OFFSET = KM_TYPE_BASE_OFFSET + 17; + public static final byte KM_COSE_KEY_OFFSET = KM_TYPE_BASE_OFFSET + 18; + public static final byte KM_COSE_KEY_INT_VAL_OFFSET = KM_TYPE_BASE_OFFSET + 19; + public static final byte KM_COSE_KEY_NINT_VAL_OFFSET = KM_TYPE_BASE_OFFSET + 20; + public static final byte KM_COSE_KEY_BYTE_BLOB_VAL_OFFSET = KM_TYPE_BASE_OFFSET + 21; + public static final byte KM_COSE_KEY_COSE_KEY_VAL_OFFSET = KM_TYPE_BASE_OFFSET + 22; + public static final byte KM_COSE_KEY_SIMPLE_VAL_OFFSET = KM_TYPE_BASE_OFFSET + 23; + public static final byte KM_SIMPLE_VALUE_OFFSET = KM_TYPE_BASE_OFFSET + 24; + public static final byte KM_COSE_HEADERS_OFFSET = KM_TYPE_BASE_OFFSET + 25; + public static final byte KM_COSE_KEY_TXT_STR_VAL_OFFSET = KM_TYPE_BASE_OFFSET + 26; + public static final byte KM_COSE_CERT_PAYLOAD_OFFSET = KM_TYPE_BASE_OFFSET + 27; + public static final byte KM_BIGNUM_TAG_OFFSET = KM_TYPE_BASE_OFFSET + 28; + + // Attestation types + public static final byte NO_CERT = 0; + public static final byte ATTESTATION_CERT = 1; + public static final byte SELF_SIGNED_CERT = 2; + public static final byte FAKE_CERT = 3; + // Buffering Mode + public static final byte BUF_NONE = 0; + public static final byte BUF_RSA_NO_DIGEST = 1; + public static final byte BUF_EC_NO_DIGEST = 2; + public static final byte BUF_AES_BLOCK_ALIGN = 3; + public static final byte BUF_AES_PKCS7_DECRYPT_BLOCK_ALIGN = 4; + public static final byte BUF_DES_BLOCK_ALIGN = 5; + public static final byte BUF_DES_PKCS7_DECRYPT_BLOCK_ALIGN = 6; + public static final byte BUF_AES_GCM_DECRYPT_BLOCK_ALIGN = 7; protected static KMRepository repository; protected static byte[] heap; // Instance table - public static final byte INSTANCE_TABLE_SIZE = 15; + public static final byte INSTANCE_TABLE_SIZE = 29; protected static short[] instanceTable; public static void initialize() { diff --git a/Applet/src/com/android/javacard/keymaster/KMVerificationToken.java b/Applet/src/com/android/javacard/keymaster/KMVerificationToken.java index 1be88ded..65eb2dcc 100644 --- a/Applet/src/com/android/javacard/keymaster/KMVerificationToken.java +++ b/Applet/src/com/android/javacard/keymaster/KMVerificationToken.java @@ -31,9 +31,7 @@ public class KMVerificationToken extends KMType { public static final byte CHALLENGE = 0x00; public static final byte TIMESTAMP = 0x01; - public static final byte PARAMETERS_VERIFIED = 0x02; - public static final byte SECURITY_LEVEL = 0x03; - public static final byte MAC = 0x04; + public static final byte MAC = 0x02; private static KMVerificationToken prototype; @@ -41,13 +39,10 @@ private KMVerificationToken() { } public static short exp() { - short arrPtr = KMArray.instance((short) 5); + short arrPtr = KMArray.instance((short) 3); KMArray arr = KMArray.cast(arrPtr); arr.add(CHALLENGE, KMInteger.exp()); arr.add(TIMESTAMP, KMInteger.exp()); - //arr.add(PARAMETERS_VERIFIED, KMKeyParameters.exp()); - arr.add(PARAMETERS_VERIFIED, KMByteBlob.exp()); - arr.add(SECURITY_LEVEL, KMEnum.instance(KMType.HARDWARE_TYPE)); arr.add(MAC, KMByteBlob.exp()); return instance(arrPtr); } @@ -56,24 +51,22 @@ private static KMVerificationToken proto(short ptr) { if (prototype == null) { prototype = new KMVerificationToken(); } - instanceTable[KM_VERIFICATION_TOKEN_OFFSET] = ptr; + KMType.instanceTable[KM_VERIFICATION_TOKEN_OFFSET] = ptr; return prototype; } public static short instance() { - short arrPtr = KMArray.instance((short) 5); + short arrPtr = KMArray.instance((short) 3); KMArray arr = KMArray.cast(arrPtr); arr.add(CHALLENGE, KMInteger.uint_16((short) 0)); arr.add(TIMESTAMP, KMInteger.uint_16((short) 0)); - arr.add(PARAMETERS_VERIFIED, KMByteBlob.instance((short) 0)); - arr.add(SECURITY_LEVEL, KMEnum.instance(KMType.HARDWARE_TYPE, KMType.STRONGBOX)); arr.add(MAC, KMByteBlob.instance((short) 0)); return instance(arrPtr); } public static short instance(short vals) { KMArray arr = KMArray.cast(vals); - if (arr.length() != 5) { + if (arr.length() != 3) { ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); } short ptr = KMType.instance(VERIFICATION_TOKEN_TYPE, (short) 2); @@ -93,7 +86,7 @@ public static KMVerificationToken cast(short ptr) { } public short getVals() { - return Util.getShort(heap, (short) (instanceTable[KM_VERIFICATION_TOKEN_OFFSET] + TLV_HEADER_SIZE)); + return Util.getShort(heap, (short) (KMType.instanceTable[KM_VERIFICATION_TOKEN_OFFSET] + TLV_HEADER_SIZE)); } public short length() { @@ -134,27 +127,4 @@ public void setMac(short vals) { KMArray.cast(arrPtr).add(MAC, vals); } - public short getParametersVerified() { - short arrPtr = getVals(); - return KMArray.cast(arrPtr).get(PARAMETERS_VERIFIED); - } - - public void setParametersVerified(short vals) { - // KMKeyParameters.cast(vals); - KMByteBlob.cast(vals); - short arrPtr = getVals(); - KMArray.cast(arrPtr).add(PARAMETERS_VERIFIED, vals); - } - - public short getSecurityLevel() { - short arrPtr = getVals(); - return KMArray.cast(arrPtr).get(SECURITY_LEVEL); - } - - public void setSecurityLevel(short vals) { - KMEnum.cast(vals); - short arrPtr = getVals(); - KMArray.cast(arrPtr).add(SECURITY_LEVEL, vals); - } - } diff --git a/Applet/src/com/android/javacard/keymaster/RemotelyProvisionedComponentDevice.java b/Applet/src/com/android/javacard/keymaster/RemotelyProvisionedComponentDevice.java new file mode 100644 index 00000000..adc1684a --- /dev/null +++ b/Applet/src/com/android/javacard/keymaster/RemotelyProvisionedComponentDevice.java @@ -0,0 +1,1430 @@ +/* + * Copyright(C) 2021 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 com.android.javacard.seprovider.KMDeviceUniqueKey; +import com.android.javacard.seprovider.KMException; +import com.android.javacard.seprovider.KMOperation; +import com.android.javacard.seprovider.KMSEProvider; + +import javacard.framework.APDU; +import javacard.framework.ISO7816; +import javacard.framework.ISOException; +import javacard.framework.JCSystem; +import javacard.framework.Util; + +/* + * This class handles the remote key provisioning. Generates an RKP key and generates a certificate signing + * request(CSR). The generation of CSR is divided amoung multiple functions to the save the memory inside + * the Applet. The set of functions to be called sequentially in the order to complete the process of + * generating the CSR are processBeginSendData, processUpdateKey, processUpdateEekChain, + * processUpdateChallenge, processFinishSendData and getResponse. ProcessUpdateKey is called N times, where + * N is the number of keys. Similarly getResponse is called is multiple times till the client receives the + * response completely. + */ +public class RemotelyProvisionedComponentDevice { + + private static final byte TRUE = 0x01; + private static final byte FALSE = 0x00; + // RKP Version + private static final short RKP_VERSION = (short) 0x01; + // Boot params + private static final byte OS_VERSION_ID = 0x00; + private static final byte SYSTEM_PATCH_LEVEL_ID = 0x01; + private static final byte BOOT_PATCH_LEVEL_ID = 0x02; + private static final byte VENDOR_PATCH_LEVEL_ID = 0x03; + // Device Info labels + public static final byte[] BRAND = {0x62, 0x72, 0x61, 0x6E, 0x64}; + public static final byte[] MANUFACTURER = {0x6D, 0x61, 0x6E, 0x75, 0x66, 0x61, 0x63, 0x74, 0x75, + 0x72, 0x65, 0x72}; + public static final byte[] PRODUCT = {0x70, 0x72, 0x6F, 0x64, 0x75, 0x63, 0x74}; + public static final byte[] MODEL = {0x6D, 0x6F, 0x64, 0x65, 0x6C}; + public static final byte[] BOARD = {0x62, 0x6F, 0x61, 0x72, 0x64}; + public static final byte[] VB_STATE = {0x76, 0x62, 0x5F, 0x73, 0x74, 0x61, 0x74, 0x65}; + public static final byte[] BOOTLOADER_STATE = + {0x62, 0x6F, 0x6F, 0x74, 0x6C, 0x6F, 0x61, 0x64, 0x65, 0x72, 0x5F, 0x73, 0x74, 0x61, 0x74, + 0x65}; + public static final byte[] VB_META_DIGEST = + {0X76, 0X62, 0X6D, 0X65, 0X74, 0X61, 0X5F, 0X64, 0X69, 0X67, 0X65, 0X73, 0X74}; + public static final byte[] OS_VERSION = {0x6F, 0x73, 0x5F, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6F, + 0x6E}; + public static final byte[] SYSTEM_PATCH_LEVEL = + {0x73, 0x79, 0x73, 0x74, 0x65, 0x6D, 0x5F, 0x70, 0x61, 0x74, 0x63, 0x68, 0x5F, 0x6C, 0x65, + 0x76, 0x65, 0x6C}; + public static final byte[] BOOT_PATCH_LEVEL = + {0x62, 0x6F, 0x6F, 0x74, 0x5F, 0x70, 0x61, 0x74, 0x63, 0x68, 0x5F, 0x6C, 0x65, 0x76, 0x65, + 0x6C}; + public static final byte[] VENDOR_PATCH_LEVEL = + {0x76, 0x65, 0x6E, 0x64, 0x6F, 0x72, 0x5F, 0x70, 0x61, 0x74, 0x63, 0x68, 0x5F, 0x6C, 0x65, + 0x76, 0x65, 0x6C}; + public static final byte[] DEVICE_INFO_VERSION = + {0x76, 0x65, 0x72, 0x73, 0x69, 0x6F, 0x6E}; + public static final byte[] SECURITY_LEVEL = + {0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x5F, 0x6C, 0x65, 0x76, 0x65, 0x6C}; + public static final byte[] ATTEST_ID_STATE = + {0x61, 0x74, 0x74, 0x5f, 0x69, 0x64, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65}; + // Verified boot state values + public static final byte[] VB_STATE_GREEN = {0x67, 0x72, 0x65, 0x65, 0x6E}; + public static final byte[] VB_STATE_YELLOW = {0x79, 0x65, 0x6C, 0x6C, 0x6F, 0x77}; + public static final byte[] VB_STATE_ORANGE = {0x6F, 0x72, 0x61, 0x6E, 0x67, 0x65}; + public static final byte[] VB_STATE_RED = {0x72, 0x65, 0x64}; + // Boot loader state values + public static final byte[] UNLOCKED = {0x75, 0x6E, 0x6C, 0x6F, 0x63, 0x6B, 0x65, 0x64}; + public static final byte[] LOCKED = {0x6C, 0x6F, 0x63, 0x6B, 0x65, 0x64}; + // Device info CDDL schema version + public static final byte DI_SCHEMA_VERSION = 1; + public static final byte[] DI_SECURITY_LEVEL = {0x73, 0x74, 0x72, 0x6F, 0x6E, 0x67, 0x62, 0x6F, + 0x78}; + public static final byte[] ATTEST_ID_LOCKED = {0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64}; + public static final byte[] ATTEST_ID_OPEN = {0x6f, 0x70, 0x65, 0x6e}; + private static final short MAX_SEND_DATA = 1024; + // more data or no data + private static final byte MORE_DATA = 0x01; // flag to denote more data to retrieve + private static final byte NO_DATA = 0x00; + // Response processing states + private static final byte START_PROCESSING = 0x00; + private static final byte PROCESSING_BCC_IN_PROGRESS = 0x02; + private static final byte PROCESSING_BCC_COMPLETE = 0x04; + private static final byte PROCESSING_ACC_IN_PROGRESS = 0x08; // Additional certificate chain. + private static final byte PROCESSING_ACC_COMPLETE = 0x0A; + // data table + private static final short DATA_SIZE = 512; + private static final short DATA_INDEX_SIZE = 11; + public static final short DATA_INDEX_ENTRY_SIZE = 4; + public static final short DATA_INDEX_ENTRY_LENGTH = 0; + public static final short DATA_INDEX_ENTRY_OFFSET = 2; + // data offsets + private static final short EPHEMERAL_MAC_KEY = 0; + private static final short TOTAL_KEYS_TO_SIGN = 1; + private static final short KEYS_TO_SIGN_COUNT = 2; + private static final short TEST_MODE = 3; + private static final short EEK_KEY = 4; + private static final short EEK_KEY_ID = 5; + private static final short CHALLENGE = 6; + private static final short GENERATE_CSR_PHASE = 7; + private static final short EPHEMERAL_PUB_KEY = 8; + private static final short RESPONSE_PROCESSING_STATE = 9; + private static final short ACC_PROCESSED_LENGTH = 10; + + // data item sizes + private static final short MAC_KEY_SIZE = 32; + private static final short SHORT_SIZE = 2; + private static final short BYTE_SIZE = 1; + private static final short TEST_MODE_SIZE = 1; + // generate csr states + private static final byte BEGIN = 0x01; + private static final byte UPDATE = 0x02; + private static final byte FINISH = 0x04; + private static final byte GET_RESPONSE = 0x06; + // variables + private byte[] data; + private KMEncoder encoder; + private KMDecoder decoder; + private KMRepository repository; + private KMSEProvider seProvider; + private Object[] operation; + private short[] dataIndex; + public static Object[] authorizedEekRoots; + + public RemotelyProvisionedComponentDevice(KMEncoder encoder, KMDecoder decoder, + KMRepository repository, KMSEProvider seProvider) { + this.encoder = encoder; + this.decoder = decoder; + this.repository = repository; + this.seProvider = seProvider; + data = JCSystem.makeTransientByteArray(DATA_SIZE, JCSystem.CLEAR_ON_RESET); + operation = JCSystem.makeTransientObjectArray((short) 1, JCSystem.CLEAR_ON_RESET); + dataIndex = JCSystem.makeTransientShortArray((short) 1, JCSystem.CLEAR_ON_RESET); + operation[0] = null; + createAuthorizedEEKRoot(); + } + + private void createAuthorizedEEKRoot() { + if (authorizedEekRoots == null) { + authorizedEekRoots = + new Object[] + { + new byte[]{ + 0x04, + (byte)0xf7, (byte)0x14, (byte)0x8a, (byte)0xdb, (byte)0x97, (byte)0xf4, + (byte)0xcc, (byte)0x53, (byte)0xef, (byte)0xd2, (byte)0x64, (byte)0x11, + (byte)0xc4, (byte)0xe3, (byte)0x75, (byte)0x1f, (byte)0x66, (byte)0x1f, + (byte)0xa4, (byte)0x71, (byte)0x0c, (byte)0x6c, (byte)0xcf, (byte)0xfa, + (byte)0x09, (byte)0x46, (byte)0x80, (byte)0x74, (byte)0x87, (byte)0x54, + (byte)0xf2, (byte)0xad, + (byte)0x5e, (byte)0x7f, (byte)0x5b, (byte)0xf6, (byte)0xec, (byte)0xe4, + (byte)0xf6, (byte)0x19, (byte)0xcc, (byte)0xff, (byte)0x13, (byte)0x37, + (byte)0xfd, (byte)0x0f, (byte)0xa1, (byte)0xc8, (byte)0x93, (byte)0xdb, + (byte)0x18, (byte)0x06, (byte)0x76, (byte)0xc4, (byte)0x5d, (byte)0xe6, + (byte)0xd7, (byte)0x6a, (byte)0x77, (byte)0x86, (byte)0xc3, (byte)0x2d, + (byte)0xaf, (byte)0x8f + }, + }; + } + } + + private void initializeDataTable() { + if (dataIndex[0] != 0) { + KMException.throwIt(KMError.INVALID_STATE); + } + dataIndex[0] = (short) (DATA_INDEX_SIZE * DATA_INDEX_ENTRY_SIZE); + } + + private short dataAlloc(short length) { + if ((short) (dataIndex[0] + length) > (short) data.length) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + dataIndex[0] += length; + return (short) (dataIndex[0] - length); + } + + private void clearDataTable() { + Util.arrayFillNonAtomic(data, (short) 0, (short) data.length, (byte) 0x00); + dataIndex[0] = 0x00; + } + + private void releaseOperation() { + if (operation[0] != null) { + ((KMOperation) operation[0]).abort(); + operation[0] = null; + } + } + + private short createEntry(short index, short length) { + index = (short) (index * DATA_INDEX_ENTRY_SIZE); + short ptr = dataAlloc(length); + Util.setShort(data, index, length); + Util.setShort(data, (short) (index + DATA_INDEX_ENTRY_OFFSET), ptr); + return ptr; + } + + private short getEntry(short index) { + index = (short) (index * DATA_INDEX_ENTRY_SIZE); + return Util.getShort(data, (short) (index + DATA_INDEX_ENTRY_OFFSET)); + } + + private short getEntryLength(short index) { + index = (short) (index * DATA_INDEX_ENTRY_SIZE); + return Util.getShort(data, index); + } + + private void processGetRkpHwInfoCmd(APDU apdu) { + // Make the response + // Author name - Google. + final byte[] google = {0x47, 0x6F, 0x6F, 0x67, 0x6C, 0x65}; + short respPtr = KMArray.instance((short) 3); + KMArray resp = KMArray.cast(respPtr); + resp.add((short) 0, KMInteger.uint_16(RKP_VERSION)); + resp.add((short) 1, KMByteBlob.instance(google, (short) 0, (short) google.length)); + resp.add((short) 2, KMInteger.uint_8(KMType.RKP_CURVE_P256)); + KMKeymasterApplet.sendOutgoing(apdu, respPtr); + } + + /** + * This function generates an EC key pair with attest key as purpose and creates an encrypted key + * blob. It then generates a COSEMac message which includes the ECDSA public key. + */ + public void processGenerateRkpKey(APDU apdu) { + short arr = KMArray.instance((short) 1); + KMArray.cast(arr).add((short) 0, KMSimpleValue.exp()); + arr = KMKeymasterApplet.receiveIncoming(apdu, arr); + // Re-purpose the apdu buffer as scratch pad. + byte[] scratchPad = apdu.getBuffer(); + // test mode flag. + boolean testMode = + (KMSimpleValue.TRUE == KMSimpleValue.cast(KMArray.cast(arr).get((short) 0)).getValue()); + KMKeymasterApplet.generateRkpKey(scratchPad, getEcAttestKeyParameters()); + short pubKey = KMKeymasterApplet.getPubKey(); + short coseMac0 = constructCoseMacForRkpKey(testMode, scratchPad, pubKey); + // Encode the COSE_MAC0 object + arr = KMArray.instance((short) 3); + KMArray.cast(arr).add((short) 0, KMInteger.uint_16(KMError.OK)); + KMArray.cast(arr).add((short) 1, coseMac0); + KMArray.cast(arr).add((short) 2, KMKeymasterApplet.getPivateKey()); + KMKeymasterApplet.sendOutgoing(apdu, arr); + } + + public void processBeginSendData(APDU apdu) throws Exception { + try { + initializeDataTable(); + short arr = KMArray.instance((short) 3); + KMArray.cast(arr).add((short) 0, KMInteger.exp()); // Array length + KMArray.cast(arr).add((short) 1, KMInteger.exp()); // Total length of the encoded CoseKeys. + KMArray.cast(arr).add((short) 2, KMSimpleValue.exp()); + arr = KMKeymasterApplet.receiveIncoming(apdu, arr); + // Re-purpose the apdu buffer as scratch pad. + byte[] scratchPad = apdu.getBuffer(); + // Generate ephemeral mac key. + short dataEntryIndex = createEntry(EPHEMERAL_MAC_KEY, MAC_KEY_SIZE); + seProvider.newRandomNumber(data, dataEntryIndex, MAC_KEY_SIZE); + // Initialize hmac operation. + initHmacOperation(); + // Partially encode CoseMac structure with partial payload. + constructPartialPubKeysToSignMac(scratchPad, + KMInteger.cast(KMArray.cast(arr).get((short) 0)).getShort(), + KMInteger.cast(KMArray.cast(arr).get((short) 1)).getShort()); + // Store the total keys in data table. + dataEntryIndex = createEntry(TOTAL_KEYS_TO_SIGN, SHORT_SIZE); + Util.setShort(data, dataEntryIndex, + KMInteger.cast(KMArray.cast(arr).get((short) 0)).getShort()); + // Store the test mode value in data table. + dataEntryIndex = createEntry(TEST_MODE, TEST_MODE_SIZE); + data[dataEntryIndex] = + (KMSimpleValue.TRUE == KMSimpleValue.cast(KMArray.cast(arr).get((short) 2)).getValue()) ? + TRUE : FALSE; + // Store the current csr status, which is BEGIN. + createEntry(GENERATE_CSR_PHASE, BYTE_SIZE); + updateState(BEGIN); + // Send response. + KMKeymasterApplet.sendError(apdu, KMError.OK); + } catch (Exception e) { + clearDataTable(); + releaseOperation(); + throw e; + } + } + + public void processUpdateKey(APDU apdu) throws Exception { + try { + // The prior state can be BEGIN or UPDATE + validateState((byte) (BEGIN | UPDATE)); + validateKeysToSignCount(); + short headers = KMCoseHeaders.exp(); + short arrInst = KMArray.instance((short) 4); + KMArray.cast(arrInst).add((short) 0, KMByteBlob.exp()); + KMArray.cast(arrInst).add((short) 1, headers); + KMArray.cast(arrInst).add((short) 2, KMByteBlob.exp()); + KMArray.cast(arrInst).add((short) 3, KMByteBlob.exp()); + short arr = KMArray.exp(arrInst); + arr = KMKeymasterApplet.receiveIncoming(apdu, arr); + arrInst = KMArray.cast(arr).get((short) 0); + // Re-purpose the apdu buffer as scratch pad. + byte[] scratchPad = apdu.getBuffer(); + + // Validate and extract the CoseKey from CoseMac0 message. + short coseKey = validateAndExtractPublicKey(arrInst, scratchPad); + // Encode CoseKey + short length = KMKeymasterApplet.encodeToApduBuffer(coseKey, scratchPad, (short) 0, + KMKeymasterApplet.MAX_COSE_BUF_SIZE); + // Do Hmac update with input as encoded CoseKey. + ((KMOperation) operation[0]).update(scratchPad, (short) 0, length); + // Increment the count each time this function gets executed. + // Store the count in data table. + short dataEntryIndex = getEntry(KEYS_TO_SIGN_COUNT); + if (dataEntryIndex == 0) { + dataEntryIndex = createEntry(KEYS_TO_SIGN_COUNT, SHORT_SIZE); + } + length = Util.getShort(data, dataEntryIndex); + Util.setShort(data, dataEntryIndex, ++length); + // Update the csr state + updateState(UPDATE); + // Send response. + KMKeymasterApplet.sendError(apdu, KMError.OK); + } catch (Exception e) { + clearDataTable(); + releaseOperation(); + throw e; + } + } + + public void processUpdateEekChain(APDU apdu) throws Exception { + try { + // The prior state can be BEGIN or UPDATE + validateState((byte) (BEGIN | UPDATE)); + short headers = KMCoseHeaders.exp(); + short arrInst = KMArray.instance((short) 4); + KMArray.cast(arrInst).add((short) 0, KMByteBlob.exp()); + KMArray.cast(arrInst).add((short) 1, headers); + KMArray.cast(arrInst).add((short) 2, KMByteBlob.exp()); + KMArray.cast(arrInst).add((short) 3, KMByteBlob.exp()); + short arrSignPtr = KMArray.exp(arrInst); + arrInst = KMKeymasterApplet.receiveIncoming(apdu, arrSignPtr); + if (KMArray.cast(arrInst).length() == 0) { + KMException.throwIt(KMError.STATUS_INVALID_EEK); + } + // Re-purpose the apdu buffer as scratch pad. + byte[] scratchPad = apdu.getBuffer(); + // Validate eek chain. + short eekKey = validateAndExtractEekPub(arrInst, scratchPad); + // Store eek public key and eek id in the data table. + short eekKeyId = KMCoseKey.cast(eekKey).getKeyIdentifier(); + short dataEntryIndex = createEntry(EEK_KEY_ID, KMByteBlob.cast(eekKeyId).length()); + Util.arrayCopyNonAtomic( + KMByteBlob.cast(eekKeyId).getBuffer(), + KMByteBlob.cast(eekKeyId).getStartOff(), + data, + dataEntryIndex, + KMByteBlob.cast(eekKeyId).length() + ); + // Convert the coseKey to a public key. + short len = KMCoseKey.cast(eekKey).getEcdsa256PublicKey(scratchPad, (short) 0); + dataEntryIndex = createEntry(EEK_KEY, len); + Util.arrayCopyNonAtomic(scratchPad, (short) 0, data, dataEntryIndex, len); + // Update the state + updateState(UPDATE); + KMKeymasterApplet.sendError(apdu, KMError.OK); + } catch (Exception e) { + clearDataTable(); + releaseOperation(); + throw e; + } + } + + public void processUpdateChallenge(APDU apdu) throws Exception { + try { + // The prior state can be BEGIN or UPDATE + validateState((byte) (BEGIN | UPDATE)); + short arr = KMArray.instance((short) 1); + KMArray.cast(arr).add((short) 0, KMByteBlob.exp()); + arr = KMKeymasterApplet.receiveIncoming(apdu, arr); + // Store the challenge in the data table. + short challenge = KMArray.cast(arr).get((short) 0); + short dataEntryIndex = createEntry(CHALLENGE, KMByteBlob.cast(challenge).length()); + Util.arrayCopyNonAtomic( + KMByteBlob.cast(challenge).getBuffer(), + KMByteBlob.cast(challenge).getStartOff(), + data, + dataEntryIndex, + KMByteBlob.cast(challenge).length() + ); + // Update the state + updateState(UPDATE); + KMKeymasterApplet.sendError(apdu, KMError.OK); + } catch (Exception e) { + clearDataTable(); + releaseOperation(); + throw e; + } + } + + // This function returns pubKeysToSignMac, deviceInfo and partially constructed protected data + // wrapped inside byte blob. The partial protected data contains Headers and encrypted signedMac. + public void processFinishSendData(APDU apdu) throws Exception { + try { + // The prior state should be UPDATE. + validateState(UPDATE); + byte[] scratchPad = apdu.getBuffer(); + if (data[getEntry(TOTAL_KEYS_TO_SIGN)] != data[getEntry(KEYS_TO_SIGN_COUNT)]) { + // Mismatch in the number of keys sent. + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + // PubKeysToSignMac + byte[] empty = {}; + short len = + ((KMOperation) operation[0]).sign(empty, (short) 0, + (short) 0, scratchPad, (short) 0); + // release operation + releaseOperation(); + short pubKeysToSignMac = KMByteBlob.instance(scratchPad, (short) 0, len); + // Create DeviceInfo + short deviceInfo = createDeviceInfo(scratchPad); + // Generate Nonce for AES-GCM + seProvider.newRandomNumber(scratchPad, (short) 0, + KMKeymasterApplet.AES_GCM_NONCE_LENGTH); + short nonce = KMByteBlob.instance(scratchPad, (short) 0, + KMKeymasterApplet.AES_GCM_NONCE_LENGTH); + // Initializes cipher instance. + initAesGcmOperation(scratchPad, nonce); + // Encode Enc_Structure as additional data for AES-GCM. + processAesGcmUpdateAad(scratchPad); + short partialPayloadLen = processSignedMac(scratchPad, pubKeysToSignMac, deviceInfo); + short partialCipherText = KMByteBlob.instance(scratchPad, (short) 0, partialPayloadLen); + short coseEncryptProtectedHeader = getCoseEncryptProtectedHeader(scratchPad); + short coseEncryptUnProtectedHeader = getCoseEncryptUnprotectedHeader(scratchPad, nonce); + len = KMKeymasterApplet.encodeToApduBuffer(deviceInfo, scratchPad, + (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE); + short encodedDeviceInfo = KMByteBlob.instance(scratchPad, (short) 0, len); + updateState(FINISH); + short arr = KMArray.instance((short) 7); + KMArray.cast(arr).add((short) 0, KMInteger.uint_16(KMError.OK)); + KMArray.cast(arr).add((short) 1, pubKeysToSignMac); + KMArray.cast(arr).add((short) 2, encodedDeviceInfo); + KMArray.cast(arr).add((short) 3, coseEncryptProtectedHeader); + KMArray.cast(arr).add((short) 4, coseEncryptUnProtectedHeader); + KMArray.cast(arr).add((short) 5, partialCipherText); + KMArray.cast(arr).add((short) 6, KMInteger.uint_8(MORE_DATA)); + KMKeymasterApplet.sendOutgoing(apdu, arr); + } catch (Exception e) { + clearDataTable(); + releaseOperation(); + throw e; + } + } + + public void processGetResponse(APDU apdu) throws Exception { + try { + // The prior state should be FINISH. + validateState((byte) (FINISH | GET_RESPONSE)); + byte[] scratchPad = apdu.getBuffer(); + short len = 0; + short recipientStructure = KMArray.instance((short) 0); + byte moreData = MORE_DATA; + byte state = getCurrentOutputProcessingState(); + switch (state) { + case START_PROCESSING: + case PROCESSING_BCC_IN_PROGRESS: + len = processBcc(scratchPad); + updateState(GET_RESPONSE); + break; + case PROCESSING_BCC_COMPLETE: + case PROCESSING_ACC_IN_PROGRESS: + len = processAdditionalCertificateChain(scratchPad); + updateState(GET_RESPONSE); + break; + case PROCESSING_ACC_COMPLETE: + recipientStructure = processRecipientStructure(scratchPad); + len = processFinalData(scratchPad); + moreData = NO_DATA; + releaseOperation(); + clearDataTable(); + break; + default: + KMException.throwIt(KMError.INVALID_STATE); + } + short data = KMByteBlob.instance(scratchPad, (short) 0, len); + short arr = KMArray.instance((short) 4); + KMArray.cast(arr).add((short) 0, KMInteger.uint_16(KMError.OK)); + KMArray.cast(arr).add((short) 1, data); + KMArray.cast(arr).add((short) 2, recipientStructure); + // represents there is more output to retrieve + KMArray.cast(arr).add((short) 3, KMInteger.uint_8(moreData)); + KMKeymasterApplet.sendOutgoing(apdu, arr); + } catch (Exception e) { + clearDataTable(); + releaseOperation(); + throw e; + } + } + + public void process(short ins, APDU apdu) throws Exception { + switch (ins) { + case KMKeymasterApplet.INS_GET_RKP_HARDWARE_INFO: + processGetRkpHwInfoCmd(apdu); + break; + case KMKeymasterApplet.INS_GENERATE_RKP_KEY_CMD: + processGenerateRkpKey(apdu); + break; + case KMKeymasterApplet.INS_BEGIN_SEND_DATA_CMD: + processBeginSendData(apdu); + break; + case KMKeymasterApplet.INS_UPDATE_KEY_CMD: + processUpdateKey(apdu); + break; + case KMKeymasterApplet.INS_UPDATE_EEK_CHAIN_CMD: + processUpdateEekChain(apdu); + break; + case KMKeymasterApplet.INS_UPDATE_CHALLENGE_CMD: + processUpdateChallenge(apdu); + break; + case KMKeymasterApplet.INS_FINISH_SEND_DATA_CMD: + processFinishSendData(apdu); + break; + case KMKeymasterApplet.INS_GET_RESPONSE_CMD: + processGetResponse(apdu); + break; + default: + ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED); + } + } + + private boolean isAdditionalCertificateChainPresent() { + return (seProvider.getAdditionalCertChainLength() == 0 ? false : true); + } + + private short processFinalData(byte[] scratchPad) { + // Call finish on AES GCM Cipher + byte[] empty = {}; + short len = + ((KMOperation) operation[0]).finish(empty, (short) 0, (short) 0, scratchPad, (short) 0); + return len; + } + + private byte getCurrentOutputProcessingState() { + short index = getEntry(RESPONSE_PROCESSING_STATE); + if (index == 0) { + return START_PROCESSING; + } + return data[index]; + } + + private void updateOutputProcessingState(byte state) { + short dataEntryIndex = getEntry(RESPONSE_PROCESSING_STATE); + data[dataEntryIndex] = state; + } + + + private short getHmacKey(boolean testMode, byte[] scratchPad) { + short macKey = KMByteBlob.instance(MAC_KEY_SIZE); + Util.arrayFillNonAtomic(KMByteBlob.cast(macKey).getBuffer(), + KMByteBlob.cast(macKey).getStartOff(), MAC_KEY_SIZE, (byte) 0); + if (!testMode) { + Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) (2 * MAC_KEY_SIZE), (byte) 0); + short len = + seProvider.hkdf( + scratchPad, //ikm + (short) 0, // ikm offset + MAC_KEY_SIZE, // ikm size + scratchPad, // salt + MAC_KEY_SIZE, // salt offset + MAC_KEY_SIZE, // salt length + KMCose.MAC_DERIVE_KEY_CTX, + (short) 0, + (short) KMCose.MAC_DERIVE_KEY_CTX.length, + KMByteBlob.cast(macKey).getBuffer(), + KMByteBlob.cast(macKey).getStartOff(), + MAC_KEY_SIZE + ); + if (len != MAC_KEY_SIZE) { + KMException.throwIt(KMError.INVALID_MAC_LENGTH); + } + } + return macKey; + } + + /** + * Validates the CoseMac message and extracts the CoseKey from it. + * + * @param coseMacPtr CoseMac instance to be validated. + * @param scratchPad Scratch buffer used to store temp results. + * @return CoseKey instance. + */ + private short validateAndExtractPublicKey(short coseMacPtr, byte[] scratchPad) { + boolean testMode = (TRUE == data[getEntry(TEST_MODE)]) ? true : false; + // Exp for KMCoseHeaders + short coseHeadersExp = KMCoseHeaders.exp(); + // Exp for coseky + short coseKeyExp = KMCoseKey.exp(); + // Get the mackey. + short macKey = getHmacKey(testMode, scratchPad); + + // validate protected Headers + short ptr = KMArray.cast(coseMacPtr).get(KMCose.COSE_MAC0_PROTECTED_PARAMS_OFFSET); + ptr = decoder.decode(coseHeadersExp, KMByteBlob.cast(ptr).getBuffer(), + KMByteBlob.cast(ptr).getStartOff(), KMByteBlob.cast(ptr).length()); + + if (!KMCoseHeaders.cast(ptr).isDataValid(KMCose.COSE_ALG_HMAC_256, KMType.INVALID_VALUE)) { + KMException.throwIt(KMError.STATUS_FAILED); + } + + // Validate payload. + ptr = KMArray.cast(coseMacPtr).get(KMCose.COSE_MAC0_PAYLOAD_OFFSET); + ptr = decoder.decode(coseKeyExp, KMByteBlob.cast(ptr).getBuffer(), + KMByteBlob.cast(ptr).getStartOff(), KMByteBlob.cast(ptr).length()); + + if (!KMCoseKey.cast(ptr).isDataValid(KMCose.COSE_KEY_TYPE_EC2, KMType.INVALID_VALUE, + KMCose.COSE_ALG_ES256, KMType.INVALID_VALUE, KMCose.COSE_ECCURVE_256)) { + KMException.throwIt(KMError.STATUS_FAILED); + } + + boolean isTestKey = KMCoseKey.cast(ptr).isTestKey(); + if (isTestKey && !testMode) { + KMException.throwIt(KMError.STATUS_TEST_KEY_IN_PRODUCTION_REQUEST); + } else if (!isTestKey && testMode) { + KMException.throwIt(KMError.STATUS_PRODUCTION_KEY_IN_TEST_REQUEST); + } + + // Compute CoseMac Structure and compare the macs. + short macStructure = + KMCose.constructCoseMacStructure(KMArray.cast(coseMacPtr).get( + KMCose.COSE_MAC0_PROTECTED_PARAMS_OFFSET), + KMByteBlob.instance((short) 0), + KMArray.cast(coseMacPtr).get(KMCose.COSE_MAC0_PAYLOAD_OFFSET)); + short encodedLen = KMKeymasterApplet.encodeToApduBuffer(macStructure, scratchPad, (short) 0, + KMKeymasterApplet.MAX_COSE_BUF_SIZE); + + short hmacLen = seProvider.hmacSign(KMByteBlob.cast(macKey).getBuffer(), + KMByteBlob.cast(macKey).getStartOff(), + (short) 32, scratchPad, (short) 0, encodedLen, scratchPad, encodedLen); + + if (hmacLen != KMByteBlob.cast( + KMArray.cast(coseMacPtr).get(KMCose.COSE_MAC0_TAG_OFFSET)).length()) { + KMException.throwIt(KMError.STATUS_INVALID_MAC); + } + + if (0 != Util.arrayCompare(scratchPad, encodedLen, + KMByteBlob.cast(KMArray.cast(coseMacPtr).get(KMCose.COSE_MAC0_TAG_OFFSET)).getBuffer(), + KMByteBlob.cast(KMArray.cast(coseMacPtr).get(KMCose.COSE_MAC0_TAG_OFFSET)).getStartOff(), + hmacLen)) { + KMException.throwIt(KMError.STATUS_INVALID_MAC); + } + return ptr; + } + + + + /** + * This function validates the EEK Chain and extracts the leaf public key, which is used to + * generate shared secret using ECDH. + * + * @param eekArr EEK cert chain array pointer. + * @param scratchPad Scratch buffer used to store temp results. + * @return CoseKey instance. + */ + private short validateAndExtractEekPub(short eekArr, byte[] scratchPad) { + short leafPubKey = 0; + try { + leafPubKey = + KMKeymasterApplet.validateCertChain( + (TRUE == data[getEntry(TEST_MODE)]) ? false : true, // validate EEK root + KMCose.COSE_ALG_ES256, + KMCose.COSE_ALG_ECDH_ES_HKDF_256, + eekArr, + scratchPad, + authorizedEekRoots + ); + } catch (KMException e) { + KMException.throwIt(KMError.STATUS_INVALID_EEK); + } + return leafPubKey; + } + + private void validateKeysToSignCount() { + short index = getEntry(KEYS_TO_SIGN_COUNT); + short keysToSignCount = 0; + if (index != 0) { + keysToSignCount = Util.getShort(data, index); + } + if (Util.getShort(data, getEntry(TOTAL_KEYS_TO_SIGN)) <= keysToSignCount) { + // Mismatch in the number of keys sent. + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + } + + private void validateState(byte expectedState) { + short dataEntryIndex = getEntry(GENERATE_CSR_PHASE); + if (0 == (data[dataEntryIndex] & expectedState)) { + KMException.throwIt(KMError.INVALID_STATE); + } + } + + private void updateState(byte state) { + short dataEntryIndex = getEntry(GENERATE_CSR_PHASE); + if (dataEntryIndex == 0) { + KMException.throwIt(KMError.INVALID_STATE); + } + data[dataEntryIndex] = state; + } + + + /** + * This function constructs a Mac Structure, encode it and signs the encoded buffer with the + * ephemeral mac key. + */ + private void constructPartialPubKeysToSignMac(byte[] scratchPad, short arrayLength, + short encodedCoseKeysLen) { + short ptr; + short len; + short headerPtr = KMCose.constructHeaders( + KMInteger.uint_8(KMCose.COSE_ALG_HMAC_256), + KMType.INVALID_VALUE, + KMType.INVALID_VALUE, + KMType.INVALID_VALUE); + // Encode the protected header as byte blob. + len = KMKeymasterApplet.encodeToApduBuffer(headerPtr, scratchPad, (short) 0, + KMKeymasterApplet.MAX_COSE_BUF_SIZE); + short protectedHeader = KMByteBlob.instance(scratchPad, (short) 0, len); + // create MAC_Structure + ptr = + KMCose.constructCoseMacStructure(protectedHeader, + KMByteBlob.instance((short) 0), KMType.INVALID_VALUE); + // Encode the Mac_structure and do HMAC_Sign to produce the tag for COSE_MAC0 + len = KMKeymasterApplet.encodeToApduBuffer(ptr, scratchPad, (short) 0, + KMKeymasterApplet.MAX_COSE_BUF_SIZE); + // Construct partial payload - Bstr Header + Array Header + // The maximum combined length of bstr header and array header length is 6 bytes. + // The lengths will never exceed Max SHORT value. + short arrPtr = KMArray.instance(arrayLength); + for (short i = 0; i < arrayLength; i++) { + KMArray.cast(arrPtr).add(i, KMType.INVALID_VALUE); + } + arrayLength = encoder.getEncodedLength(arrPtr); + short bufIndex = repository.alloc((short) 6); + short partialPayloadLen = + encoder.encodeByteBlobHeader((short) (arrayLength + encodedCoseKeysLen), + repository.getHeap(), + bufIndex, (short) 3); + + partialPayloadLen += + encoder.encode(arrPtr, repository.getHeap(), (short) (bufIndex + partialPayloadLen)); + Util.arrayCopyNonAtomic(repository.getHeap(), bufIndex, scratchPad, len, partialPayloadLen); + ((KMOperation) operation[0]).update(scratchPad, (short) 0, (short) (len + partialPayloadLen)); + } + + private static short getAttestKeyParameters() { + short tagIndex = 0; + short arrPtr = KMArray.instance((short) 6); + // Key size - 256 + short keySize = KMIntegerTag + .instance(KMType.UINT_TAG, KMType.KEYSIZE, KMInteger.uint_16((short) 256)); + // Digest - SHA256 + short byteBlob = KMByteBlob.instance((short) 1); + KMByteBlob.cast(byteBlob).add((short) 0, KMType.SHA2_256); + short digest = KMEnumArrayTag.instance(KMType.DIGEST, byteBlob); + // Purpose - Attest + byteBlob = KMByteBlob.instance((short) 1); + KMByteBlob.cast(byteBlob).add((short) 0, KMType.ATTEST_KEY); + short purpose = KMEnumArrayTag.instance(KMType.PURPOSE, byteBlob); + + KMArray.cast(arrPtr).add(tagIndex++, purpose); + // Algorithm - EC + KMArray.cast(arrPtr).add(tagIndex++, KMEnumTag.instance(KMType.ALGORITHM, KMType.EC)); + KMArray.cast(arrPtr).add(tagIndex++, keySize); + KMArray.cast(arrPtr).add(tagIndex++, digest); + // Curve - P256 + KMArray.cast(arrPtr).add(tagIndex++, KMEnumTag.instance(KMType.ECCURVE, KMType.P_256)); + // No Authentication is required to use this key. + KMArray.cast(arrPtr).add(tagIndex, KMBoolTag.instance(KMType.NO_AUTH_REQUIRED)); + return KMKeyParameters.instance(arrPtr); + } + + private short createSignedMac(KMDeviceUniqueKey deviceUniqueKey, byte[] scratchPad, + short deviceMapPtr, short pubKeysToSign) { + // Challenge + short dataEntryIndex = getEntry(CHALLENGE); + short challengePtr = KMByteBlob.instance(data, dataEntryIndex, getEntryLength(CHALLENGE)); + // Ephemeral mac key + dataEntryIndex = getEntry(EPHEMERAL_MAC_KEY); + short ephmeralMacKey = + KMByteBlob.instance(data, dataEntryIndex, getEntryLength(EPHEMERAL_MAC_KEY)); + + /* Prepare AAD */ + short aad = KMArray.instance((short) 3); + KMArray.cast(aad).add((short) 0, challengePtr); + KMArray.cast(aad).add((short) 1, deviceMapPtr); + KMArray.cast(aad).add((short) 2, pubKeysToSign); + aad = KMKeymasterApplet.encodeToApduBuffer(aad, scratchPad, + (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE); + aad = KMByteBlob.instance(scratchPad, (short) 0, aad); + + /* construct protected header */ + short protectedHeaders = KMCose.constructHeaders( + KMNInteger.uint_8(KMCose.COSE_ALG_ES256), + KMType.INVALID_VALUE, + KMType.INVALID_VALUE, + KMType.INVALID_VALUE); + protectedHeaders = KMKeymasterApplet.encodeToApduBuffer(protectedHeaders, scratchPad, + (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE); + protectedHeaders = KMByteBlob.instance(scratchPad, (short) 0, protectedHeaders); + + /* construct cose sign structure */ + short signStructure = + KMCose.constructCoseSignStructure(protectedHeaders, aad, ephmeralMacKey); + signStructure = KMKeymasterApplet.encodeToApduBuffer(signStructure, scratchPad, + (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE); + short len = + seProvider.ecSign256( + deviceUniqueKey, + scratchPad, + (short) 0, + signStructure, + scratchPad, + signStructure + ); + signStructure = KMByteBlob.instance(scratchPad, signStructure, len); + + /* Construct unprotected headers */ + short unprotectedHeader = KMArray.instance((short) 0); + unprotectedHeader = KMCoseHeaders.instance(unprotectedHeader); + + /* construct Cose_Sign1 */ + return KMCose.constructCoseSign1(protectedHeaders, unprotectedHeader, + ephmeralMacKey, signStructure); + } + + + private KMDeviceUniqueKey createDeviceUniqueKey(boolean testMode, byte[] scratchPad) { + KMDeviceUniqueKey deviceUniqueKey; + short[] lengths = {0, 0}; + if (testMode) { + seProvider.createAsymmetricKey( + KMType.EC, + scratchPad, + (short) 0, + (short) 128, + scratchPad, + (short) 128, + (short) 128, + lengths); + deviceUniqueKey = + seProvider.createDeviceUniqueKey(true, scratchPad, (short) 128, lengths[1], + scratchPad, (short) 0, lengths[0]); + } else { + deviceUniqueKey = seProvider.getDeviceUniqueKey(false); + } + return deviceUniqueKey; + } + + /** + * DeviceInfo is a CBOR Map structure described by the following CDDL. + *

+ * DeviceInfo = { + * ? "brand" : tstr, + * ? "manufacturer" : tstr, + * ? "product" : tstr, + * ? "model" : tstr, + * ? "board" : tstr, + * ? "vb_state" : "green" / "yellow" / "orange", // Taken from the AVB values + * ? "bootloader_state" : "locked" / "unlocked", // Taken from the AVB values + * ? "vbmeta_digest": bstr, // Taken from the AVB values + * ? "os_version" : tstr, // Same as android.os.Build.VERSION.release + * ? "system_patch_level" : uint, // YYYYMMDD + * ? "boot_patch_level" : uint, //YYYYMMDD + * ? "vendor_patch_level" : uint, // YYYYMMDD + * "version" : 1, // TheCDDL schema version + * "security_level" : "tee" / "strongbox" + * "att_id_state": "locked" / "open" + * } + */ + private short createDeviceInfo(byte[] scratchpad) { + // Device Info Key Value pairs. + short[] deviceIds = { + KMType.INVALID_VALUE, KMType.INVALID_VALUE, + KMType.INVALID_VALUE, KMType.INVALID_VALUE, + KMType.INVALID_VALUE, KMType.INVALID_VALUE, + KMType.INVALID_VALUE, KMType.INVALID_VALUE, + KMType.INVALID_VALUE, KMType.INVALID_VALUE, + KMType.INVALID_VALUE, KMType.INVALID_VALUE, + KMType.INVALID_VALUE, KMType.INVALID_VALUE, + KMType.INVALID_VALUE, KMType.INVALID_VALUE, + KMType.INVALID_VALUE, KMType.INVALID_VALUE, + KMType.INVALID_VALUE, KMType.INVALID_VALUE, + KMType.INVALID_VALUE, KMType.INVALID_VALUE, + KMType.INVALID_VALUE, KMType.INVALID_VALUE, + KMType.INVALID_VALUE, KMType.INVALID_VALUE, + KMType.INVALID_VALUE, KMType.INVALID_VALUE, + KMType.INVALID_VALUE, KMType.INVALID_VALUE, + }; + short[] out = {0/* index */, 0 /* length */}; + updateItem(deviceIds, out, BRAND, getAttestationId(KMType.ATTESTATION_ID_BRAND, scratchpad)); + updateItem(deviceIds, out, MANUFACTURER, + getAttestationId(KMType.ATTESTATION_ID_MANUFACTURER, scratchpad)); + updateItem(deviceIds, out, PRODUCT, + getAttestationId(KMType.ATTESTATION_ID_PRODUCT, scratchpad)); + updateItem(deviceIds, out, MODEL, getAttestationId(KMType.ATTESTATION_ID_MODEL, scratchpad)); + updateItem(deviceIds, out, VB_STATE, getVbState()); + updateItem(deviceIds, out, BOOTLOADER_STATE, getBootloaderState()); + updateItem(deviceIds, out, VB_META_DIGEST, getVerifiedBootHash(scratchpad)); + updateItem(deviceIds, out, OS_VERSION, getBootParams(OS_VERSION_ID, scratchpad)); + updateItem(deviceIds, out, SYSTEM_PATCH_LEVEL, + getBootParams(SYSTEM_PATCH_LEVEL_ID, scratchpad)); + updateItem(deviceIds, out, BOOT_PATCH_LEVEL, getBootParams(BOOT_PATCH_LEVEL_ID, scratchpad)); + updateItem(deviceIds, out, VENDOR_PATCH_LEVEL, + getBootParams(VENDOR_PATCH_LEVEL_ID, scratchpad)); + updateItem(deviceIds, out, DEVICE_INFO_VERSION, KMInteger.uint_8(DI_SCHEMA_VERSION)); + updateItem(deviceIds, out, SECURITY_LEVEL, + KMTextString.instance(DI_SECURITY_LEVEL, (short) 0, (short) DI_SECURITY_LEVEL.length)); + //TODO Add attest_id_state + // Create device info map. + short map = KMMap.instance(out[1]); + short mapIndex = 0; + short index = 0; + while (index < (short) deviceIds.length) { + if (deviceIds[index] != KMType.INVALID_VALUE) { + KMMap.cast(map).add(mapIndex++, deviceIds[index], deviceIds[(short) (index + 1)]); + } + index += 2; + } + KMMap.cast(map).canonicalize(); + return map; + } + + // Below 6 methods are helper methods to create device info structure. + //---------------------------------------------------------------------------- + + /** + * Update the item inside the device info structure. + * + * @param deviceIds Device Info structure to be updated. + * @param meta Out parameter meta information. Offset 0 is index and Offset 1 is length. + * @param item Key info to be updated. + * @param value value to be updated. + */ + private void updateItem(short[] deviceIds, short[] meta, byte[] item, short value) { + if (KMType.INVALID_VALUE != value) { + deviceIds[meta[0]++] = + KMTextString.instance(item, (short) 0, (short) item.length); + deviceIds[meta[0]++] = value; + meta[1]++; + } + } + + private short getAttestationId(short attestId, byte[] scratchpad) { + short attIdTagLen = seProvider.getAttestationId(attestId, scratchpad, (short) 0); + if (attIdTagLen != 0) { + return KMTextString.instance(scratchpad, (short) 0, attIdTagLen); + } + return KMType.INVALID_VALUE; + } + + private short getVerifiedBootHash(byte[] scratchPad) { + short len = seProvider.getVerifiedBootHash(scratchPad, (short) 0); + if (len != 0) { + return KMByteBlob.instance(scratchPad, (short) 0, len); + } + return KMType.INVALID_VALUE; + } + + private short getBootloaderState() { + short bootloaderState; + if (seProvider.isDeviceBootLocked()) { + bootloaderState = KMTextString.instance(LOCKED, (short) 0, (short) LOCKED.length); + } else { + bootloaderState = KMTextString.instance(UNLOCKED, (short) 0, (short) UNLOCKED.length); + } + return bootloaderState; + } + + private short getVbState() { + short state = seProvider.getBootState(); + short vbState = KMType.INVALID_VALUE; + if (state == KMType.VERIFIED_BOOT) { + vbState = KMTextString.instance(VB_STATE_GREEN, (short) 0, (short) VB_STATE_GREEN.length); + } else if (state == KMType.SELF_SIGNED_BOOT) { + vbState = KMTextString.instance(VB_STATE_YELLOW, (short) 0, (short) VB_STATE_YELLOW.length); + } else if (state == KMType.UNVERIFIED_BOOT) { + vbState = KMTextString.instance(VB_STATE_ORANGE, (short) 0, (short) VB_STATE_ORANGE.length); + } else if (state == KMType.FAILED_BOOT) { + vbState = KMTextString.instance(VB_STATE_RED, (short) 0, (short) VB_STATE_RED.length); + } + return vbState; + } + + private short getBootParams(byte bootParam, byte[] scratchPad) { + short value = KMType.INVALID_VALUE; + switch (bootParam) { + case OS_VERSION_ID: + value = repository.getOsVersion(); + break; + case SYSTEM_PATCH_LEVEL_ID: + value = repository.getOsPatch(); + break; + case BOOT_PATCH_LEVEL_ID: + short len = seProvider.getBootPatchLevel(scratchPad, (short) 0); + value = KMByteBlob.instance(scratchPad, (short) 0, len); + break; + case VENDOR_PATCH_LEVEL_ID: + value = repository.getVendorPatchLevel(); + break; + default: + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + // Convert Integer to Text String for OS_VERSION. + if (bootParam == OS_VERSION_ID) { + value = + KMTextString + .instance(KMInteger.cast(value).getBuffer(), KMInteger.cast(value).getStartOff(), + KMInteger.cast(value).length()); + } + return value; + } + //---------------------------------------------------------------------------- + + //---------------------------------------------------------------------------- + // ECDH HKDF + private short ecdhHkdfDeriveKey(byte[] privKeyA, short privKeyAOff, short privKeyALen, + byte[] pubKeyA, + short pubKeyAOff, short pubKeyALen, byte[] pubKeyB, short pubKeyBOff, + short pubKeyBLen, byte[] scratchPad) { + short key = + seProvider.ecdhKeyAgreement(privKeyA, privKeyAOff, privKeyALen, pubKeyB, pubKeyBOff, + pubKeyBLen, scratchPad, (short) 0); + key = KMByteBlob.instance(scratchPad, (short) 0, key); + + short kdfContext = + KMCose.constructKdfContext(pubKeyA, pubKeyAOff, pubKeyALen, pubKeyB, pubKeyBOff, pubKeyBLen, + true); + kdfContext = KMKeymasterApplet + .encodeToApduBuffer(kdfContext, scratchPad, (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE); + kdfContext = KMByteBlob.instance(scratchPad, (short) 0, kdfContext); + + Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 32, (byte) 0); + seProvider.hkdf( + KMByteBlob.cast(key).getBuffer(), + KMByteBlob.cast(key).getStartOff(), + KMByteBlob.cast(key).length(), + scratchPad, + (short) 0, + (short) 32, + KMByteBlob.cast(kdfContext).getBuffer(), + KMByteBlob.cast(kdfContext).getStartOff(), + KMByteBlob.cast(kdfContext).length(), + scratchPad, + (short) 32, // offset + (short) 32 // Length of expected output. + ); + Util.arrayCopy(scratchPad, (short) 32, scratchPad, (short) 0, (short) 32); + return (short) 32; + } + + //---------------------------------------------------------------------------- + // This function returns the instance of private key and It stores the public key in the + // data table for later usage. + private short generateEphemeralEcKey(byte[] scratchPad) { + // Generate ephemeral ec key. + short[] lengths = {0/* Private key Length*/, 0 /* Public key length*/}; + seProvider.createAsymmetricKey( + KMType.EC, + scratchPad, + (short) 0, + (short) 128, + scratchPad, + (short) 128, + (short) 128, + lengths); + // Copy the ephemeral private key from scratch pad + short ptr = KMByteBlob.instance(lengths[0]); + Util.arrayCopyNonAtomic( + scratchPad, + (short) 0, + KMByteBlob.cast(ptr).getBuffer(), + KMByteBlob.cast(ptr).getStartOff(), + lengths[0]); + //Store ephemeral public key in data table for later usage. + short dataEntryIndex = createEntry(EPHEMERAL_PUB_KEY, lengths[1]); + Util.arrayCopyNonAtomic(scratchPad, (short) 128, data, dataEntryIndex, lengths[1]); + return ptr; + } + + private void initHmacOperation() { + short dataEntryIndex = getEntry(EPHEMERAL_MAC_KEY); + operation[0] = + seProvider.initSymmetricOperation( + KMType.SIGN, + KMType.HMAC, + KMType.SHA2_256, + KMType.PADDING_NONE, + (byte) 0, + data, + dataEntryIndex, + getEntryLength(EPHEMERAL_MAC_KEY), + null, + (short) 0, + (short) 0, + (short) 0 + ); + if (operation[0] == null) { + KMException.throwIt(KMError.STATUS_FAILED); + } + } + + private void initAesGcmOperation(byte[] scratchPad, short nonce) { + // Generate Ephemeral mac key + short privKey = generateEphemeralEcKey(scratchPad); + short pubKeyIndex = getEntry(EPHEMERAL_PUB_KEY); + // Generate session key + short eekIndex = getEntry(EEK_KEY); + // Generate session key + short sessionKeyLen = + ecdhHkdfDeriveKey( + KMByteBlob.cast(privKey).getBuffer(), /* Ephemeral Private Key */ + KMByteBlob.cast(privKey).getStartOff(), + KMByteBlob.cast(privKey).length(), + data, /* Ephemeral Public key */ + pubKeyIndex, + getEntryLength(EPHEMERAL_PUB_KEY), + data, /* EEK Public key */ + eekIndex, + getEntryLength(EEK_KEY), + scratchPad /* scratchpad */ + ); + // Initialize the Cipher object. + operation[0] = + seProvider.initSymmetricOperation( + KMType.ENCRYPT, + KMType.AES, + (byte) 0, + KMType.PADDING_NONE, + KMType.GCM, + scratchPad, /* key */ + (short) 0, + sessionKeyLen, + KMByteBlob.cast(nonce).getBuffer(), /* nonce */ + KMByteBlob.cast(nonce).getStartOff(), + KMByteBlob.cast(nonce).length(), + (short) (KMKeymasterApplet.AES_GCM_AUTH_TAG_LENGTH * 8) + ); + if (operation[0] == null) { + KMException.throwIt(KMError.STATUS_FAILED); + } + } + + private short processRecipientStructure(byte[] scratchPad) { + short protectedHeaderRecipient = KMCose.constructHeaders( + KMNInteger.uint_8(KMCose.COSE_ALG_ECDH_ES_HKDF_256), + KMType.INVALID_VALUE, + KMType.INVALID_VALUE, + KMType.INVALID_VALUE); + // Encode the protected header as byte blob. + protectedHeaderRecipient = KMKeymasterApplet + .encodeToApduBuffer(protectedHeaderRecipient, scratchPad, (short) 0, + KMKeymasterApplet.MAX_COSE_BUF_SIZE); + protectedHeaderRecipient = KMByteBlob.instance(scratchPad, (short) 0, protectedHeaderRecipient); + + /* Construct unprotected headers */ + short pubKeyIndex = getEntry(EPHEMERAL_PUB_KEY); + // prepare cosekey + short coseKey = + KMCose.constructCoseKey( + KMInteger.uint_8(KMCose.COSE_KEY_TYPE_EC2), + KMType.INVALID_VALUE, + KMNInteger.uint_8(KMCose.COSE_ALG_ES256), + KMType.INVALID_VALUE, + KMInteger.uint_8(KMCose.COSE_ECCURVE_256), + data, + pubKeyIndex, + getEntryLength(EPHEMERAL_PUB_KEY), + KMType.INVALID_VALUE, + false + ); + short keyIdentifierPtr = KMByteBlob + .instance(data, getEntry(EEK_KEY_ID), getEntryLength(EEK_KEY_ID)); + short unprotectedHeaderRecipient = + KMCose.constructHeaders(KMType.INVALID_VALUE, keyIdentifierPtr, KMType.INVALID_VALUE, + coseKey); + + // Construct recipients structure. + return KMCose.constructRecipientsStructure(protectedHeaderRecipient, unprotectedHeaderRecipient, + KMSimpleValue.instance(KMSimpleValue.NULL)); + } + + private short getAdditionalCertChainProcessedLength() { + short dataEntryIndex = getEntry(ACC_PROCESSED_LENGTH); + if (dataEntryIndex == 0) { + dataEntryIndex = createEntry(ACC_PROCESSED_LENGTH, SHORT_SIZE); + Util.setShort(data, dataEntryIndex, (short) 0); + return (short) 0; + } + return Util.getShort(data, dataEntryIndex); + } + + private void updateAdditionalCertChainProcessedLength(short processedLen) { + short dataEntryIndex = getEntry(ACC_PROCESSED_LENGTH); + Util.setShort(data, dataEntryIndex, processedLen); + } + + private short processAdditionalCertificateChain(byte[] scratchPad) { + byte[] persistedData = seProvider.getAdditionalCertChain(); + short totalAccLen = Util.getShort(persistedData, (short) 0); + if (totalAccLen == 0) { + // No Additional certificate chain present. + return 0; + } + short processedLen = getAdditionalCertChainProcessedLength(); + short lengthToSend = (short) (totalAccLen - processedLen); + if (lengthToSend > MAX_SEND_DATA) { + lengthToSend = MAX_SEND_DATA; + } + short cipherTextLen = + ((KMOperation) operation[0]).update(persistedData, (short) (2 + processedLen), lengthToSend, + scratchPad, (short) 0); + processedLen += lengthToSend; + updateAdditionalCertChainProcessedLength(processedLen); + // Update the output processing state. + updateOutputProcessingState( + (processedLen == totalAccLen) ? PROCESSING_ACC_COMPLETE : PROCESSING_ACC_IN_PROGRESS); + return cipherTextLen; + } + + // BCC for STRONGBOX has chain length of 2. So it can be returned in a single go. + private short processBcc(byte[] scratchPad) { + // Construct BCC + boolean testMode = (TRUE == data[getEntry(TEST_MODE)]) ? true : false; + short len; + if (testMode) { + short bcc = KMKeymasterApplet.generateBcc(true, scratchPad); + len = KMKeymasterApplet + .encodeToApduBuffer(bcc, scratchPad, (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE); + } else { + byte[] bcc = seProvider.getBootCertificateChain(); + len = Util.getShort(bcc, (short) 0); + Util.arrayCopyNonAtomic(bcc, (short) 2, scratchPad, (short) 0, len); + } + short cipherTextLen = ((KMOperation) operation[0]) + .update(scratchPad, (short) 0, len, scratchPad, len); + // move cipher text on scratch pad from starting position. + Util.arrayCopyNonAtomic(scratchPad, len, scratchPad, (short) 0, cipherTextLen); + createEntry(RESPONSE_PROCESSING_STATE, BYTE_SIZE); + // If there is no additional certificate chain present then put the state to + // PROCESSING_ACC_COMPLETE. + updateOutputProcessingState( + isAdditionalCertificateChainPresent() ? PROCESSING_BCC_COMPLETE : PROCESSING_ACC_COMPLETE); + return cipherTextLen; + } + + // AAD is the CoseEncrypt structure + private void processAesGcmUpdateAad(byte[] scratchPad) { + short protectedHeader = KMCose.constructHeaders( + KMInteger.uint_8(KMCose.COSE_ALG_AES_GCM_256), + KMType.INVALID_VALUE, + KMType.INVALID_VALUE, + KMType.INVALID_VALUE); + // Encode the protected header as byte blob. + protectedHeader = KMKeymasterApplet.encodeToApduBuffer(protectedHeader, scratchPad, (short) 0, + KMKeymasterApplet.MAX_COSE_BUF_SIZE); + protectedHeader = KMByteBlob.instance(scratchPad, (short) 0, protectedHeader); + short coseEncryptStr = + KMCose.constructCoseEncryptStructure(protectedHeader, KMByteBlob.instance((short) 0)); + coseEncryptStr = KMKeymasterApplet.encodeToApduBuffer(coseEncryptStr, scratchPad, (short) 0, + KMKeymasterApplet.MAX_COSE_BUF_SIZE); + ((KMOperation) operation[0]).updateAAD(scratchPad, (short) 0, coseEncryptStr); + } + + private short processSignedMac(byte[] scratchPad, short pubKeysToSignMac, short deviceInfo) { + // Construct SignedMac + KMDeviceUniqueKey deviceUniqueKey = + createDeviceUniqueKey((TRUE == data[getEntry(TEST_MODE)]) ? true : false, scratchPad); + // Create signedMac + short signedMac = createSignedMac(deviceUniqueKey, scratchPad, deviceInfo, pubKeysToSignMac); + //Prepare partial data for encryption. + short arrLength = (short) (isAdditionalCertificateChainPresent() ? 3 : 2); + short arr = KMArray.instance(arrLength); + KMArray.cast(arr).add((short) 0, signedMac); + KMArray.cast(arr).add((short) 1, KMType.INVALID_VALUE); + if (arrLength == 3) { + KMArray.cast(arr).add((short) 2, KMType.INVALID_VALUE); + } + short len = KMKeymasterApplet + .encodeToApduBuffer(arr, scratchPad, (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE); + short cipherTextLen = ((KMOperation) operation[0]) + .update(scratchPad, (short) 0, len, scratchPad, len); + Util.arrayCopyNonAtomic( + scratchPad, + len, + scratchPad, + (short) 0, + cipherTextLen + ); + return cipherTextLen; + } + + private short getCoseEncryptProtectedHeader(byte[] scratchPad) { + // CoseEncrypt protected headers. + short protectedHeader = KMCose.constructHeaders( + KMInteger.uint_8(KMCose.COSE_ALG_AES_GCM_256), + KMType.INVALID_VALUE, + KMType.INVALID_VALUE, + KMType.INVALID_VALUE); + // Encode the protected header as byte blob. + protectedHeader = KMKeymasterApplet.encodeToApduBuffer(protectedHeader, scratchPad, (short) 0, + KMKeymasterApplet.MAX_COSE_BUF_SIZE); + return KMByteBlob.instance(scratchPad, (short) 0, protectedHeader); + } + + private short getCoseEncryptUnprotectedHeader(byte[] scratchPad, short nonce) { + /* CoseEncrypt unprotected headers */ + return KMCose + .constructHeaders(KMType.INVALID_VALUE, KMType.INVALID_VALUE, nonce, KMType.INVALID_VALUE); + } + + private short constructCoseEncryptHeaders(byte[] scratchPad, short nonce) { + // CoseEncrypt protected headers. + short protectedHeader = KMCose.constructHeaders( + KMInteger.uint_8(KMCose.COSE_ALG_AES_GCM_256), + KMType.INVALID_VALUE, + KMType.INVALID_VALUE, + KMType.INVALID_VALUE); + // Encode the protected header as byte blob. + protectedHeader = KMKeymasterApplet.encodeToApduBuffer(protectedHeader, scratchPad, (short) 0, + KMKeymasterApplet.MAX_COSE_BUF_SIZE); + protectedHeader = KMByteBlob.instance(scratchPad, (short) 0, protectedHeader); + /* CoseEncrypt unprotected headers */ + short unprotectedHeader = + KMCose.constructHeaders(KMType.INVALID_VALUE, KMType.INVALID_VALUE, nonce, + KMType.INVALID_VALUE); + // Construct partial CoseEncrypt + return KMCose.constructCoseEncrypt(protectedHeader, unprotectedHeader, KMType.INVALID_VALUE, + KMType.INVALID_VALUE); + } + + + private short constructCoseMacForRkpKey(boolean testMode, byte[] scratchPad, short pubKey) { + // prepare cosekey + short coseKey = + KMCose.constructCoseKey( + KMInteger.uint_8(KMCose.COSE_KEY_TYPE_EC2), + KMType.INVALID_VALUE, + KMNInteger.uint_8(KMCose.COSE_ALG_ES256), + KMType.INVALID_VALUE, + KMInteger.uint_8(KMCose.COSE_ECCURVE_256), + KMByteBlob.cast(pubKey).getBuffer(), + KMByteBlob.cast(pubKey).getStartOff(), + KMByteBlob.cast(pubKey).length(), + KMType.INVALID_VALUE, + testMode); + // Encode the cose key and make it as payload. + short len = KMKeymasterApplet + .encodeToApduBuffer(coseKey, scratchPad, (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE); + short payload = KMByteBlob.instance(scratchPad, (short) 0, len); + // Get the mackey. + short macKey = getHmacKey(testMode, scratchPad); + // Prepare protected header, which is required to construct the COSE_MAC0 + short headerPtr = KMCose.constructHeaders( + KMInteger.uint_8(KMCose.COSE_ALG_HMAC_256), + KMType.INVALID_VALUE, + KMType.INVALID_VALUE, + KMType.INVALID_VALUE); + // Encode the protected header as byte blob. + len = KMKeymasterApplet + .encodeToApduBuffer(headerPtr, scratchPad, (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE); + short protectedHeader = KMByteBlob.instance(scratchPad, (short) 0, len); + // create MAC_Structure + short macStructure = + KMCose.constructCoseMacStructure(protectedHeader, KMByteBlob.instance((short) 0), payload); + // Encode the Mac_structure and do HMAC_Sign to produce the tag for COSE_MAC0 + len = KMKeymasterApplet.encodeToApduBuffer(macStructure, scratchPad, (short) 0, + KMKeymasterApplet.MAX_COSE_BUF_SIZE); + // HMAC Sign. + short hmacLen = seProvider + .hmacSign(KMByteBlob.cast(macKey).getBuffer(), KMByteBlob.cast(macKey).getStartOff(), + (short) 32, scratchPad, (short) 0, len, scratchPad, len); + // Create COSE_MAC0 object + short coseMac0 = + KMCose + .constructCoseMac0(protectedHeader, KMCoseHeaders.instance(KMArray.instance((short) 0)), + payload, + KMByteBlob.instance(scratchPad, len, hmacLen)); + len = KMKeymasterApplet + .encodeToApduBuffer(coseMac0, scratchPad, (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE); + return KMByteBlob.instance(scratchPad, (short) 0, len); + } + + private short getEcAttestKeyParameters() { + short tagIndex = 0; + short arrPtr = KMArray.instance((short) 6); + // Key size - 256 + short keySize = KMIntegerTag + .instance(KMType.UINT_TAG, KMType.KEYSIZE, KMInteger.uint_16((short) 256)); + // Digest - SHA256 + short byteBlob = KMByteBlob.instance((short) 1); + KMByteBlob.cast(byteBlob).add((short) 0, KMType.SHA2_256); + short digest = KMEnumArrayTag.instance(KMType.DIGEST, byteBlob); + // Purpose - Attest + byteBlob = KMByteBlob.instance((short) 1); + KMByteBlob.cast(byteBlob).add((short) 0, KMType.ATTEST_KEY); + short purpose = KMEnumArrayTag.instance(KMType.PURPOSE, byteBlob); + + KMArray.cast(arrPtr).add(tagIndex++, purpose); + // Algorithm - EC + KMArray.cast(arrPtr).add(tagIndex++, KMEnumTag.instance(KMType.ALGORITHM, KMType.EC)); + KMArray.cast(arrPtr).add(tagIndex++, keySize); + KMArray.cast(arrPtr).add(tagIndex++, digest); + // Curve - P256 + KMArray.cast(arrPtr).add(tagIndex++, KMEnumTag.instance(KMType.ECCURVE, KMType.P_256)); + // No Authentication is required to use this key. + KMArray.cast(arrPtr).add(tagIndex, KMBoolTag.instance(KMType.NO_AUTH_REQUIRED)); + return KMKeyParameters.instance(arrPtr); + } +} diff --git a/HAL/Android.bp b/HAL/Android.bp new file mode 100644 index 00000000..bf842f8b --- /dev/null +++ b/HAL/Android.bp @@ -0,0 +1,111 @@ +// 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. +// + +cc_library { + name: "libjc_keymint", + defaults: [ + "keymaster_defaults", + ], + srcs: [ + "CborConverter.cpp", + "JavacardKeyMintDevice.cpp", + "JavacardKeyMintOperation.cpp", + "JavacardRemotelyProvisionedComponentDevice.cpp", + "JavacardSecureElement.cpp", + "JavacardSharedSecret.cpp", + "keymint_utils.cpp", + ], + cflags:["-O0",], + shared_libs: [ + "android.hardware.security.keymint-V1-ndk_platform", + "android.hardware.security.secureclock-V1-ndk_platform", + "android.hardware.security.sharedsecret-V1-ndk_platform", + "lib_android_keymaster_keymint_utils", + "libbase", + "libcppbor_external", + "libkeymaster_portable", + "libkeymaster_messages", + "libsoft_attestation_cert", + "liblog", + "libcrypto", + "libcutils", + "libjc_keymint_transport", + "libbinder_ndk", + ], + export_include_dirs: [ + ".", + ], + vendor_available: true, +} + +cc_library { + name: "libjc_keymint_transport", + host_supported: true, + vendor_available: true, + srcs: [ + "SocketTransport.cpp", + ], + export_include_dirs: [ + "." + ], + shared_libs: [ + "libbase", + "liblog", + ], +} + +cc_binary { + name: "android.hardware.security.keymint-service.strongbox", + relative_install_path: "hw", + init_rc: ["android.hardware.security.keymint-service.strongbox.rc"], + vintf_fragments: [ + "android.hardware.security.keymint-service.strongbox.xml", + "android.hardware.security.sharedsecret-service.strongbox.xml", + ], + vendor: true, + cflags: [ + "-Wall", + "-Wextra", + ], + shared_libs: [ + "android.hardware.security.keymint-V1-ndk_platform", + "android.hardware.security.sharedsecret-V1-ndk_platform", + "libbase", + "libbinder_ndk", + "libcppbor_external", + "libcrypto", + "libkeymaster_portable", + "libjc_keymint", + "libjc_keymint_transport", + "liblog", + "libutils", + ], + srcs: [ + "service.cpp", + ], + required: [ + "RemoteProvisioner", + "android.hardware.strongbox_keystore.xml", + ], +} + +prebuilt_etc { + name: "android.hardware.strongbox_keystore.xml", + sub_dir: "permissions", + vendor: true, + src: "android.hardware.strongbox_keystore.xml", +} + + diff --git a/HAL/CborConverter.cpp b/HAL/CborConverter.cpp new file mode 100644 index 00000000..a97d12ad --- /dev/null +++ b/HAL/CborConverter.cpp @@ -0,0 +1,429 @@ +/* + ** + ** 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 "CborConverter.h" +#include +#include +#include +#include +#include +#include +#include + +namespace keymint::javacard { +using namespace cppbor; +using namespace aidl::android::hardware::security::keymint; +using namespace aidl::android::hardware::security::secureclock; +using namespace aidl::android::hardware::security::sharedsecret; +using std::string; +using std::unique_ptr; +using std::vector; + +bool CborConverter::addAttestationKey(Array& array, + const std::optional& attestationKey) { + if (attestationKey.has_value()) { + array.add(Bstr(attestationKey->keyBlob)); + addKeyparameters(array, attestationKey->attestKeyParams); + array.add(Bstr(attestationKey->issuerSubjectName)); + } else { + array.add(std::move(Bstr(vector(0)))); + array.add(std::move(Map())); + array.add(std::move(Bstr(vector(0)))); + } + return true; +} + +bool CborConverter::addKeyparameters(Array& array, const vector& keyParams) { + keymaster_key_param_set_t paramSet = km_utils::aidlKeyParams2Km(keyParams); + Map map; + std::map> enum_repetition; + std::map uint_repetition; + for (size_t i = 0; i < paramSet.length; i++) { + const auto& param = paramSet.params[i]; + switch (km_utils::typeFromTag(param.tag)) { + case KM_ENUM: + map.add(static_cast(param.tag), param.enumerated); + break; + case KM_UINT: + map.add(static_cast(param.tag), param.integer); + break; + case KM_UINT_REP: + uint_repetition[static_cast(param.tag)].add(param.integer); + break; + case KM_ENUM_REP: + enum_repetition[static_cast(param.tag)].push_back( + static_cast(param.enumerated)); + break; + case KM_ULONG: + map.add(static_cast(param.tag), param.long_integer); + break; + case KM_ULONG_REP: + uint_repetition[static_cast(param.tag & 0x00000000ffffffff)].add( + param.long_integer); + break; + case KM_DATE: + map.add(static_cast(param.tag), param.date_time); + break; + case KM_BOOL: + map.add(static_cast(param.tag), static_cast(param.boolean)); + break; + case KM_BIGNUM: + case KM_BYTES: + map.add(static_cast(param.tag & 0x00000000ffffffff), + km_utils::kmBlob2vector(param.blob)); + break; + default: + /* Invalid skip */ + break; + } + } + if (0 < enum_repetition.size()) { + for (auto const& [key, val] : enum_repetition) { + Bstr bstr(val); + map.add(key, std::move(bstr)); + } + } + if (0 < uint_repetition.size()) { + for (auto& [key, val] : uint_repetition) { + map.add(key, std::move(val)); + } + } + array.add(std::move(map)); + return true; +} + +// Array of three maps +bool CborConverter::getKeyCharacteristics(const unique_ptr& item, const uint32_t pos, + vector& keyCharacteristics) { + unique_ptr arrayItem(nullptr); + getItemAtPos(item, pos, arrayItem); + if ((arrayItem == nullptr) || (MajorType::ARRAY != getType(arrayItem))) return false; + + KeyCharacteristics swEnf{SecurityLevel::KEYSTORE, {}}; + KeyCharacteristics teeEnf{SecurityLevel::TRUSTED_ENVIRONMENT, {}}; + KeyCharacteristics sbEnf{SecurityLevel::STRONGBOX, {}}; + + if (!getKeyParameters(arrayItem, 0, sbEnf.authorizations) || + !getKeyParameters(arrayItem, 1, teeEnf.authorizations) || + !getKeyParameters(arrayItem, 2, swEnf.authorizations)) { + return false; + } + // VTS will fail if the authorizations list is empty. + if (!sbEnf.authorizations.empty()) keyCharacteristics.push_back(std::move(sbEnf)); + if (!teeEnf.authorizations.empty()) keyCharacteristics.push_back(std::move(teeEnf)); + if (!swEnf.authorizations.empty()) keyCharacteristics.push_back(std::move(swEnf)); + return true; +} + +bool CborConverter::getKeyParameter( + const std::pair&, const unique_ptr&> pair, + vector& keyParams) { + uint64_t key; + uint64_t value; + if (!getUint64(pair.first, key)) { + return false; + } + switch (keymaster_tag_get_type(static_cast(key))) { + case KM_ENUM_REP: { + /* ENUM_REP contains values encoded in a Binary string */ + const Bstr* bstr = pair.second.get()->asBstr(); + if (bstr == nullptr) return false; + for (auto bchar : bstr->value()) { + keymaster_key_param_t keyParam; + keyParam.tag = static_cast(key); + keyParam.enumerated = bchar; + keyParams.push_back(km_utils::kmParam2Aidl(keyParam)); + } + } break; + case KM_ENUM: { + keymaster_key_param_t keyParam; + keyParam.tag = static_cast(key); + if (!getUint64(pair.second, value)) { + return false; + } + keyParam.enumerated = static_cast(value); + keyParams.push_back(km_utils::kmParam2Aidl(keyParam)); + } break; + case KM_UINT: { + keymaster_key_param_t keyParam; + keyParam.tag = static_cast(key); + if (!getUint64(pair.second, value)) { + return false; + } + keyParam.integer = static_cast(value); + keyParams.push_back(km_utils::kmParam2Aidl(keyParam)); + } break; + case KM_ULONG: { + keymaster_key_param_t keyParam; + keyParam.tag = static_cast(key); + if (!getUint64(pair.second, value)) { + return false; + } + keyParam.long_integer = value; + keyParams.push_back(km_utils::kmParam2Aidl(keyParam)); + } break; + case KM_UINT_REP: { + /* UINT_REP contains values encoded in a Array */ + Array* array = const_cast(pair.second.get()->asArray()); + if (array == nullptr) return false; + for (int i = 0; i < array->size(); i++) { + keymaster_key_param_t keyParam; + keyParam.tag = static_cast(key); + std::unique_ptr item = std::move((*array)[i]); + if (!getUint64(item, value)) { + return false; + } + keyParam.integer = static_cast(value); + keyParams.push_back(km_utils::kmParam2Aidl(keyParam)); + } + } break; + case KM_ULONG_REP: { + /* ULONG_REP contains values encoded in a Array */ + Array* array = const_cast(pair.second.get()->asArray()); + if (array == nullptr) return false; + for (int i = 0; i < array->size(); i++) { + keymaster_key_param_t keyParam; + keyParam.tag = static_cast(key); + std::unique_ptr item = std::move((*array)[i]); + if (!getUint64(item, keyParam.long_integer)) { + return false; + } + keyParams.push_back(km_utils::kmParam2Aidl(keyParam)); + } + } break; + case KM_DATE: { + keymaster_key_param_t keyParam; + keyParam.tag = static_cast(key); + if (!getUint64(pair.second, value)) { + return false; + } + keyParam.date_time = value; + keyParams.push_back(km_utils::kmParam2Aidl(keyParam)); + } break; + case KM_BOOL: { + keymaster_key_param_t keyParam; + keyParam.tag = static_cast(key); + if (!getUint64(pair.second, value)) { + return false; + } + // TODO re-check the logic below + keyParam.boolean = static_cast(value); + keyParams.push_back(km_utils::kmParam2Aidl(keyParam)); + } break; + case KM_BYTES: { + keymaster_key_param_t keyParam; + keyParam.tag = static_cast(key); + const Bstr* bstr = pair.second.get()->asBstr(); + if (bstr == nullptr) return false; + keyParam.blob.data = bstr->value().data(); + keyParam.blob.data_length = bstr->value().size(); + keyParams.push_back(km_utils::kmParam2Aidl(keyParam)); + } break; + default: + /* Invalid - return error */ + return false; + break; + } + return true; +} + +// array of a blobs +bool CborConverter::getCertificateChain(const std::unique_ptr& item, const uint32_t pos, + vector& certChain) { + std::unique_ptr arrayItem(nullptr); + getItemAtPos(item, pos, arrayItem); + if ((arrayItem == nullptr) || (MajorType::ARRAY != getType(arrayItem))) return false; + + const Array* arr = arrayItem.get()->asArray(); + for (int i = 0; i < arr->size(); i++) { + Certificate cert; + if (!getBinaryArray(arrayItem, i, cert.encodedCertificate)) return false; + certChain.push_back(std::move(cert)); + } + return true; +} + +bool CborConverter::getMultiBinaryArray(const unique_ptr& item, const uint32_t pos, + vector>& data) { + bool ret = false; + std::unique_ptr arrayItem(nullptr); + + getItemAtPos(item, pos, arrayItem); + if ((arrayItem == nullptr) || (MajorType::ARRAY != getType(arrayItem))) return ret; + const Array* arr = arrayItem.get()->asArray(); + size_t arrSize = arr->size(); + for (int i = 0; i < arrSize; i++) { + std::vector temp; + if (!getBinaryArray(arrayItem, i, temp)) return ret; + data.push_back(std::move(temp)); + } + ret = true; // success + return ret; +} + +bool CborConverter::getBinaryArray(const unique_ptr& item, const uint32_t pos, + string& value) { + vector vec; + string str; + if (!getBinaryArray(item, pos, vec)) { + return false; + } + for (auto ch : vec) { + str += ch; + } + value = str; + return true; +} + +bool CborConverter::getBinaryArray(const unique_ptr& item, const uint32_t pos, + vector& value) { + bool ret = false; + unique_ptr strItem(nullptr); + getItemAtPos(item, pos, strItem); + if ((strItem == nullptr) || (MajorType::BSTR != getType(strItem))) return ret; + + const Bstr* bstr = strItem.get()->asBstr(); + for (auto bchar : bstr->value()) { + value.push_back(bchar); + } + ret = true; + return ret; +} + +bool CborConverter::getSharedSecretParameters(const unique_ptr& item, const uint32_t pos, + SharedSecretParameters& params) { + std::unique_ptr arrayItem(nullptr); + // Array [seed, nonce] + getItemAtPos(item, pos, arrayItem); + if ((arrayItem == nullptr) || (MajorType::ARRAY != getType(arrayItem)) || + !getBinaryArray(arrayItem, 0, params.seed) || !getBinaryArray(arrayItem, 1, params.nonce)) { + return false; + } + return true; +} + +bool CborConverter::addSharedSecretParameters(Array& array, + const vector& params) { + Array cborParamsVec; + for (auto param : params) { + Array cborParam; + cborParam.add(Bstr(param.seed)); + cborParam.add(Bstr(param.nonce)); + cborParamsVec.add(std::move(cborParam)); + } + array.add(std::move(cborParamsVec)); + return true; +} + +bool CborConverter::addTimeStampToken(Array& array, const TimeStampToken& token) { + Array vToken; + vToken.add(token.challenge); + vToken.add(token.timestamp.milliSeconds); + vToken.add((std::vector(token.mac))); + array.add(std::move(vToken)); + return true; +} + +bool CborConverter::addHardwareAuthToken(Array& array, const HardwareAuthToken& authToken) { + + Array hwAuthToken; + hwAuthToken.add(authToken.challenge); + hwAuthToken.add(authToken.userId); + hwAuthToken.add(authToken.authenticatorId); + hwAuthToken.add(static_cast(authToken.authenticatorType)); + hwAuthToken.add(authToken.timestamp.milliSeconds); + hwAuthToken.add((std::vector(authToken.mac))); + array.add(std::move(hwAuthToken)); + return true; +} + +bool CborConverter::getHardwareAuthToken(const unique_ptr& item, const uint32_t pos, + HardwareAuthToken& token) { + uint64_t authType; + // challenge, userId, AuthenticatorId, AuthType, Timestamp, MAC + if (!getUint64(item, pos, token.challenge) || + !getUint64(item, pos + 1, token.userId) || + !getUint64(item, pos + 2, token.authenticatorId) || + !getUint64(item, pos + 3, authType) || + !getUint64(item, pos + 4, token.timestamp.milliSeconds) || + !getBinaryArray(item, pos + 5, token.mac)) { + return false; + } + token.authenticatorType = static_cast(authType); + return true; +} + +bool CborConverter::getTimeStampToken(const unique_ptr& item, const uint32_t pos, + TimeStampToken& token) { + // {challenge, timestamp, Mac} + if (!getUint64(item, pos, token.challenge) || + !getUint64(item, pos + 1, token.timestamp.milliSeconds) || + !getBinaryArray(item, pos + 4, token.mac)) { + return false; + } + return true; +} + +bool CborConverter::getArrayItem(const std::unique_ptr& item, const uint32_t pos, + Array& array) { + unique_ptr arrayItem(nullptr); + getItemAtPos(item, pos, arrayItem); + if ((arrayItem == nullptr) || (MajorType::ARRAY != getType(arrayItem))) return false; + array = std::move(*arrayItem.get()->asArray()); + return true; +} + +bool CborConverter::getMapItem(const std::unique_ptr& item, const uint32_t pos, + Map& map) { + unique_ptr mapItem(nullptr); + getItemAtPos(item, pos, mapItem); + if ((mapItem == nullptr) || (MajorType::MAP != getType(mapItem))) return false; + map = std::move(*mapItem.get()->asMap()); + return true; +} + +bool CborConverter::getKeyParameters(const unique_ptr& item, const uint32_t pos, + vector& keyParams) { + bool ret = false; + unique_ptr mapItem(nullptr); + vector params; + getItemAtPos(item, pos, mapItem); + if ((mapItem == nullptr) || (MajorType::MAP != getType(mapItem))) return ret; + const Map* map = mapItem.get()->asMap(); + size_t mapSize = map->size(); + for (int i = 0; i < mapSize; i++) { + if (!getKeyParameter((*map)[i], params)) { + return ret; + } + } + keyParams.resize(params.size()); + keyParams = params; + ret = true; + return ret; +} + +std::tuple, keymaster_error_t> +CborConverter::decodeData(const std::vector& response) { + keymaster_error_t errorCode = KM_ERROR_OK; + auto [item, pos, message] = parse(response); + if (!item || MajorType::ARRAY != getType(item) || !getErrorCode(item, 0, errorCode)) { + return {nullptr, KM_ERROR_UNKNOWN_ERROR}; + } + return {std::move(item), errorCode}; +} + +} // namespace keymint::javacard diff --git a/HAL/CborConverter.h b/HAL/CborConverter.h new file mode 100644 index 00000000..ca44533f --- /dev/null +++ b/HAL/CborConverter.h @@ -0,0 +1,163 @@ +/* + ** + ** 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. + */ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace keymint::javacard { +using namespace cppbor; +using namespace aidl::android::hardware::security::keymint; +using namespace aidl::android::hardware::security::secureclock; +using namespace aidl::android::hardware::security::sharedsecret; +using std::string; +using std::unique_ptr; +using std::vector; + +class CborConverter { + public: + CborConverter() = default; + ~CborConverter() = default; + std::tuple, keymaster_error_t> + decodeData(const std::vector& response); + + template + bool getUint64(const std::unique_ptr& item, const uint32_t pos, T& value); + + template bool getUint64(const std::unique_ptr& item, T& value); + + bool getSharedSecretParameters(const std::unique_ptr& item, const uint32_t pos, + SharedSecretParameters& params); + bool getBinaryArray(const std::unique_ptr& item, const uint32_t pos, string& value); + + bool getBinaryArray(const std::unique_ptr& item, const uint32_t pos, + vector& value); + + bool getHardwareAuthToken(const std::unique_ptr& item, const uint32_t pos, + HardwareAuthToken& authType); + + bool getKeyParameters(const std::unique_ptr& item, const uint32_t pos, + vector& keyParams); + + bool addKeyparameters(Array& array, const vector& keyParams); + + bool addAttestationKey(Array& array, const std::optional& attestationKey); + + bool addHardwareAuthToken(Array& array, const HardwareAuthToken& authToken); + + bool addSharedSecretParameters(Array& array, const vector& params); + + bool getTimeStampToken(const std::unique_ptr& item, const uint32_t pos, + TimeStampToken& token); + + bool getKeyCharacteristics(const std::unique_ptr& item, const uint32_t pos, + vector& keyCharacteristics); + + bool getCertificateChain(const std::unique_ptr& item, const uint32_t pos, + vector& keyCharacteristics); + + bool getMultiBinaryArray(const std::unique_ptr& item, const uint32_t pos, + vector>& data); + + bool addTimeStampToken(Array& array, const TimeStampToken& token); + + bool getMapItem(const std::unique_ptr& item, const uint32_t pos, + Map& map); + + bool getArrayItem(const std::unique_ptr& item, const uint32_t pos, + Array& array); + + inline bool getErrorCode(const std::unique_ptr& item, const uint32_t pos, + keymaster_error_t& errorCode) { + uint64_t errorVal; + if (!getUint64(item, pos, errorVal)) { + return false; + } + errorCode = static_cast(0 - errorVal); + return true; + } + + private: + /** + * Returns the negative value of the same number. + */ + inline int32_t get2sCompliment(uint32_t value) { return static_cast(~value + 1); } + + /** + * Get the type of the Item pointer. + */ + inline MajorType getType(const unique_ptr& item) { return item.get()->type(); } + + /** + * Construct Keyparameter structure from the pair of key and value. If TagType is ENUM_REP the + * value contains binary string. If TagType is UINT_REP or ULONG_REP the value contains Array of + * unsigned integers. + */ + bool getKeyParameter(const std::pair&, const unique_ptr&> pair, + vector& keyParam); + + /** + * Get the sub item pointer from the root item pointer at the given position. + */ + inline void getItemAtPos(const unique_ptr& item, const uint32_t pos, + unique_ptr& subItem) { + Array* arr = nullptr; + + if (MajorType::ARRAY != getType(item)) { + return; + } + arr = const_cast(item.get()->asArray()); + if (arr->size() < (pos + 1)) { + return; + } + subItem = std::move((*arr)[pos]); + } +}; + +template bool CborConverter::getUint64(const unique_ptr& item, T& value) { + bool ret = false; + if ((item == nullptr) || (std::is_unsigned::value && (MajorType::UINT != getType(item))) || + ((std::is_signed::value && (MajorType::NINT != getType(item))))) { + return ret; + } + + if (std::is_unsigned::value) { + const Uint* uintVal = item.get()->asUint(); + value = static_cast(uintVal->value()); + } else { + const Nint* nintVal = item.get()->asNint(); + value = static_cast(nintVal->value()); + } + ret = true; + return ret; // success +} + +template +bool CborConverter::getUint64(const unique_ptr& item, const uint32_t pos, T& value) { + unique_ptr intItem(nullptr); + getItemAtPos(item, pos, intItem); + return getUint64(intItem, value); +} +} // namespace keymint::javacard diff --git a/HAL/ITransport.h b/HAL/ITransport.h new file mode 100644 index 00000000..672f5f50 --- /dev/null +++ b/HAL/ITransport.h @@ -0,0 +1,51 @@ +/* + ** + ** 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. + */ +#pragma once +#include +#include + +namespace keymint::javacard { +using std::shared_ptr; +using std::vector; + +/** + * ITransport is an interface with a set of virtual methods that allow communication between the + * HAL and the applet on the secure element. + */ +class ITransport { + public: + virtual ~ITransport() {} + + /** + * Opens connection. + */ + virtual bool openConnection() = 0; + /** + * Send data over communication channel and receives data back from the remote end. + */ + virtual bool sendData(const vector& inData, vector& output) = 0; + /** + * Closes the connection. + */ + virtual bool closeConnection() = 0; + /** + * Returns the state of the connection status. Returns true if the connection is active, false + * if connection is broken. + */ + virtual bool isConnected() = 0; +}; +} // namespace keymint::javacard diff --git a/HAL/JavacardKeyMintDevice.cpp b/HAL/JavacardKeyMintDevice.cpp new file mode 100644 index 00000000..402504b3 --- /dev/null +++ b/HAL/JavacardKeyMintDevice.cpp @@ -0,0 +1,398 @@ +/* + * 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. + */ + +#define LOG_TAG "javacard.keymint.device.strongbox-impl" +#include "JavacardKeyMintDevice.h" +#include "JavacardKeyMintOperation.h" +#include "JavacardSharedSecret.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace aidl::android::hardware::security::keymint { +using namespace ::keymaster; +using namespace ::keymint::javacard; + +ScopedAStatus JavacardKeyMintDevice::defaultHwInfo(KeyMintHardwareInfo* info) { + info->versionNumber = 1; + info->keyMintAuthorName = "Google"; + info->keyMintName = "JavacardKeymintDevice"; + info->securityLevel = securitylevel_; + info->timestampTokenRequired = true; + return ScopedAStatus::ok(); +} + +static inline bool findTag(const vector& params, Tag tag) { + size_t size = params.size(); + for (size_t i = 0; i < size; ++i) { + if (tag == params[i].tag) return true; + } + return false; +} + +ScopedAStatus JavacardKeyMintDevice::getHardwareInfo(KeyMintHardwareInfo* info) { + uint64_t tsRequired = 1; + auto [item, err] = card_->sendRequest(Instruction::INS_GET_HW_INFO_CMD); + uint32_t secLevel; + uint32_t version; + if (err != KM_ERROR_OK || !cbor_.getUint64(item, 1, version) || + !cbor_.getUint64(item, 2, secLevel) || + !cbor_.getBinaryArray(item, 3, info->keyMintName) || + !cbor_.getBinaryArray(item, 4, info->keyMintAuthorName) || + !cbor_.getUint64(item, 5, tsRequired)) { + // TODO should we return HARDWARE_NOT_YET_AVAILABLE instead of default Hardware Info. + LOG(ERROR) << "Error in response of getHardwareInfo."; + LOG(INFO) << "Returning defaultHwInfo in getHardwareInfo."; + return defaultHwInfo(info); + } + card_->initializeJavacard(); + info->timestampTokenRequired = (tsRequired == 1); + info->securityLevel = static_cast(secLevel); + info->versionNumber = static_cast(version); + return ScopedAStatus::ok(); +} + +ScopedAStatus JavacardKeyMintDevice::generateKey(const vector& keyParams, + const optional& attestationKey, + KeyCreationResult* creationResult) { + cppbor::Array array; + // add key params + cbor_.addKeyparameters(array, keyParams); + // add attestation key if any + cbor_.addAttestationKey(array, attestationKey); + auto [item, err] = card_->sendRequest(Instruction::INS_GENERATE_KEY_CMD, array); + if (err != KM_ERROR_OK) { + LOG(ERROR) << "Error in sending generateKey."; + return km_utils::kmError2ScopedAStatus(err); + } + if (!cbor_.getBinaryArray(item, 1, creationResult->keyBlob) || + !cbor_.getKeyCharacteristics(item, 2, creationResult->keyCharacteristics) || + !cbor_.getCertificateChain(item, 3, creationResult->certificateChain)) { + LOG(ERROR) << "Error in decoding og response in generateKey."; + return km_utils::kmError2ScopedAStatus(KM_ERROR_UNKNOWN_ERROR); + } + return ScopedAStatus::ok(); +} + +ScopedAStatus JavacardKeyMintDevice::addRngEntropy(const vector& data) { + cppbor::Array request; + // add key data + request.add(Bstr(data)); + auto [item, err] = card_->sendRequest(Instruction::INS_ADD_RNG_ENTROPY_CMD, request); + if (err != KM_ERROR_OK) { + LOG(ERROR) << "Error in sending addRngEntropy."; + return km_utils::kmError2ScopedAStatus(err); + } + return ScopedAStatus::ok(); +} + +ScopedAStatus JavacardKeyMintDevice::importKey(const vector& keyParams, + KeyFormat keyFormat, const vector& keyData, + const optional& attestationKey, + KeyCreationResult* creationResult) { + + cppbor::Array request; + vector updatedParams(keyParams); + + // Add CREATION_DATETIME if required, as secure element is not having clock. + if (!findTag(keyParams, Tag::CREATION_DATETIME) && !findTag(keyParams, Tag::ACTIVE_DATETIME)) { + updatedParams.push_back(km_utils::kmParam2Aidl( + keymaster_param_date(KM_TAG_CREATION_DATETIME, java_time(time(nullptr))))); + } + // add key params + cbor_.addKeyparameters(request, updatedParams); + // add key format + request.add(Uint(static_cast(keyFormat))); + // add key data + request.add(Bstr(keyData)); + // add attestation key if any + cbor_.addAttestationKey(request, attestationKey); + + auto [item, err] = card_->sendRequest(Instruction::INS_IMPORT_KEY_CMD, request); + if (err != KM_ERROR_OK) { + LOG(ERROR) << "Error in sending data in importKey."; + return km_utils::kmError2ScopedAStatus(err); + } + if (!cbor_.getBinaryArray(item, 1, creationResult->keyBlob) || + !cbor_.getKeyCharacteristics(item, 2, creationResult->keyCharacteristics) || + !cbor_.getCertificateChain(item, 3, creationResult->certificateChain)) { + LOG(ERROR) << "Error in decoding response in importKey."; + return km_utils::kmError2ScopedAStatus(KM_ERROR_UNKNOWN_ERROR); + } + return ScopedAStatus::ok(); +} + +// import wrapped key is divided into 2 stage operation. +ScopedAStatus JavacardKeyMintDevice::importWrappedKey(const vector& wrappedKeyData, + const vector& wrappingKeyBlob, + const vector& maskingKey, + const vector& unwrappingParams, + int64_t passwordSid, int64_t biometricSid, + KeyCreationResult* creationResult) { + cppbor::Array request; + std::unique_ptr item; + vector keyBlob; + std::vector response; + vector keyCharacteristics; + std::vector iv; + std::vector transitKey; + std::vector secureKey; + std::vector tag; + vector authList; + KeyFormat keyFormat; + std::vector wrappedKeyDescription; + + keymaster_error_t errorCode = parseWrappedKey(wrappedKeyData, iv, transitKey, secureKey, tag, + authList, keyFormat, wrappedKeyDescription); + if (errorCode != KM_ERROR_OK) { + LOG(ERROR) << "Error in parse wrapped key in importWrappedKey."; + return km_utils::kmError2ScopedAStatus(errorCode); + } + + // begin import + std::tie(item, errorCode) = + sendBeginImportWrappedKeyCmd(transitKey, wrappingKeyBlob, maskingKey, unwrappingParams); + if (errorCode != KM_ERROR_OK) { + LOG(ERROR) << "Error in send begin import wrapped key in importWrappedKey."; + return km_utils::kmError2ScopedAStatus(errorCode); + } + // Finish the import + std::tie(item, errorCode) = sendFinishImportWrappedKeyCmd( + authList, keyFormat, secureKey, tag, iv, wrappedKeyDescription, passwordSid, biometricSid); + if (errorCode != KM_ERROR_OK) { + LOG(ERROR) << "Error in send finish import wrapped key in importWrappedKey."; + return km_utils::kmError2ScopedAStatus(errorCode); + } + if (!cbor_.getBinaryArray(item, 1, creationResult->keyBlob) || + !cbor_.getKeyCharacteristics(item, 2, creationResult->keyCharacteristics) || + !cbor_.getCertificateChain(item, 3, creationResult->certificateChain)) { + LOG(ERROR) << "Error in decoding the response in importWrappedKey."; + return km_utils::kmError2ScopedAStatus(KM_ERROR_UNKNOWN_ERROR); + } + return ScopedAStatus::ok(); +} + +std::tuple, keymaster_error_t> +JavacardKeyMintDevice::sendBeginImportWrappedKeyCmd(const std::vector& transitKey, + const std::vector& wrappingKeyBlob, + const std::vector& maskingKey, + const vector& unwrappingParams) { + Array request; + request.add(std::vector(transitKey)); + request.add(std::vector(wrappingKeyBlob)); + request.add(std::vector(maskingKey)); + cbor_.addKeyparameters(request, unwrappingParams); + return card_->sendRequest(Instruction::INS_BEGIN_IMPORT_WRAPPED_KEY_CMD, request); +} + +std::tuple, keymaster_error_t> +JavacardKeyMintDevice::sendFinishImportWrappedKeyCmd( + const vector& keyParams, KeyFormat keyFormat, + const std::vector& secureKey, const std::vector& tag, + const std::vector& iv, const std::vector& wrappedKeyDescription, + int64_t passwordSid, int64_t biometricSid) { + Array request; + cbor_.addKeyparameters(request, keyParams); + request.add(static_cast(keyFormat)); + request.add(std::vector(secureKey)); + request.add(std::vector(tag)); + request.add(std::vector(iv)); + request.add(std::vector(wrappedKeyDescription)); + request.add(Uint(passwordSid)); + request.add(Uint(biometricSid)); + return card_->sendRequest(Instruction::INS_FINISH_IMPORT_WRAPPED_KEY_CMD, request); +} + +ScopedAStatus JavacardKeyMintDevice::upgradeKey(const vector& keyBlobToUpgrade, + const vector& upgradeParams, + vector* keyBlob) { + cppbor::Array request; + // add key blob + request.add(Bstr(keyBlobToUpgrade)); + // add key params + cbor_.addKeyparameters(request, upgradeParams); + auto [item, err] = card_->sendRequest(Instruction::INS_UPGRADE_KEY_CMD, request); + if (err != KM_ERROR_OK) { + LOG(ERROR) << "Error in sending in upgradeKey."; + return km_utils::kmError2ScopedAStatus(err); + } + if (!cbor_.getBinaryArray(item, 1, *keyBlob)) { + LOG(ERROR) << "Error in decoding the response in upgradeKey."; + return km_utils::kmError2ScopedAStatus(KM_ERROR_UNKNOWN_ERROR); + } + return ScopedAStatus::ok(); +} + +ScopedAStatus JavacardKeyMintDevice::deleteKey(const vector& keyBlob) { + Array request; + request.add(Bstr(keyBlob)); + auto [item, err] = card_->sendRequest(Instruction::INS_DELETE_KEY_CMD, request); + if (err != KM_ERROR_OK) { + LOG(ERROR) << "Error in sending in deleteKey."; + return km_utils::kmError2ScopedAStatus(err); + } + return ScopedAStatus::ok(); +} + +ScopedAStatus JavacardKeyMintDevice::deleteAllKeys() { + auto [item, err] = card_->sendRequest(Instruction::INS_DELETE_ALL_KEYS_CMD); + if (err != KM_ERROR_OK) { + LOG(ERROR) << "Error in sending in deleteAllKeys."; + return km_utils::kmError2ScopedAStatus(err); + } + return ScopedAStatus::ok(); +} + +ScopedAStatus JavacardKeyMintDevice::destroyAttestationIds() { + auto [item, err] = card_->sendRequest(Instruction::INS_DESTROY_ATT_IDS_CMD); + if (err != KM_ERROR_OK) { + LOG(ERROR) << "Error in sending in destroyAttestationIds."; + return km_utils::kmError2ScopedAStatus(err); + } + return ScopedAStatus::ok(); +} + +ScopedAStatus JavacardKeyMintDevice::begin(KeyPurpose purpose, const std::vector& keyBlob, + const std::vector& params, + const std::optional& authToken, + BeginResult* result) { + + cppbor::Array array; + std::vector response; + // make request + array.add(Uint(static_cast(purpose))); + array.add(Bstr(keyBlob)); + cbor_.addKeyparameters(array, params); + HardwareAuthToken token = authToken.value_or(HardwareAuthToken()); + cbor_.addHardwareAuthToken(array, token); + auto [item, err] = card_->sendRequest(Instruction::INS_BEGIN_OPERATION_CMD, array); + if (err != KM_ERROR_OK) { + LOG(ERROR) << "Error in sending in begin."; + return km_utils::kmError2ScopedAStatus(err); + } + // return the result + uint64_t opHandle; + uint8_t bufMode; + uint16_t macLength; + if (!cbor_.getKeyParameters(item, 1, result->params) || + !cbor_.getUint64(item, 2, opHandle) || + !cbor_.getUint64(item, 3, bufMode) || + !cbor_.getUint64(item, 4, macLength)) { + LOG(ERROR) << "Error in decoding the response in begin."; + return km_utils::kmError2ScopedAStatus(KM_ERROR_UNKNOWN_ERROR); + } + result->challenge = opHandle; + result->operation = ndk::SharedRefBase::make( + static_cast(opHandle), static_cast(bufMode), + macLength, card_); + return ScopedAStatus::ok(); +} + +// TODO +ScopedAStatus +JavacardKeyMintDevice::deviceLocked(bool passwordOnly, + const std::optional& timestampToken) { + Array request; + int8_t password = 1; + if (!passwordOnly) { + password = 0; + } + request.add(Uint(password)); + cbor_.addTimeStampToken(request, timestampToken.value_or(TimeStampToken())); + auto [item, err] = card_->sendRequest(Instruction::INS_DEVICE_LOCKED_CMD, request); + if (err != KM_ERROR_OK) { + return km_utils::kmError2ScopedAStatus(err); + } + return ScopedAStatus::ok(); +} + +ScopedAStatus JavacardKeyMintDevice::earlyBootEnded() { + auto [item, err] = card_->sendRequest(Instruction::INS_EARLY_BOOT_ENDED_CMD); + if (err != KM_ERROR_OK) { + return km_utils::kmError2ScopedAStatus(err); + } + return ScopedAStatus::ok(); +} + +ScopedAStatus JavacardKeyMintDevice::getKeyCharacteristics( + const std::vector& keyBlob, const std::vector& appId, + const std::vector& appData, std::vector* result) { + cppbor::Array request; + request.add(vector(keyBlob)); + request.add(vector(appId)); + request.add(vector(appData)); + auto [item, err] = card_->sendRequest(Instruction::INS_GET_KEY_CHARACTERISTICS_CMD, request); + if (err != KM_ERROR_OK) { + LOG(ERROR) << "Error in sending in getKeyCharacteristics."; + return km_utils::kmError2ScopedAStatus(err); + } + if (!cbor_.getKeyCharacteristics(item, 1, *result)) { + LOG(ERROR) << "Error in sending in upgradeKey."; + return km_utils::kmError2ScopedAStatus(KM_ERROR_UNKNOWN_ERROR); + } + return ScopedAStatus::ok(); +} + +keymaster_error_t +JavacardKeyMintDevice::parseWrappedKey(const vector& wrappedKeyData, + std::vector& iv, std::vector& transitKey, + std::vector& secureKey, std::vector& tag, + vector& authList, KeyFormat& keyFormat, + std::vector& wrappedKeyDescription) { + KeymasterBlob kmIv; + KeymasterKeyBlob kmTransitKey; + KeymasterKeyBlob kmSecureKey; + KeymasterBlob kmTag; + AuthorizationSet authSet; + keymaster_key_format_t kmKeyFormat; + KeymasterBlob kmWrappedKeyDescription; + + size_t keyDataLen = wrappedKeyData.size(); + uint8_t* keyData = dup_buffer(wrappedKeyData.data(), keyDataLen); + keymaster_key_blob_t keyMaterial = {keyData, keyDataLen}; + keymaster_error_t error = + parse_wrapped_key(KeymasterKeyBlob(keyMaterial), &kmIv, &kmTransitKey, &kmSecureKey, &kmTag, + &authSet, &kmKeyFormat, &kmWrappedKeyDescription); + if (error != KM_ERROR_OK) { + LOG(ERROR) << "Error parsing wrapped key."; + return error; + } + iv = km_utils::kmBlob2vector(kmIv); + transitKey = km_utils::kmBlob2vector(kmTransitKey); + secureKey = km_utils::kmBlob2vector(kmSecureKey); + tag = km_utils::kmBlob2vector(kmTag); + authList = km_utils::kmParamSet2Aidl(authSet); + keyFormat = static_cast(kmKeyFormat); + wrappedKeyDescription = km_utils::kmBlob2vector(kmWrappedKeyDescription); + return KM_ERROR_OK; +} + +ScopedAStatus JavacardKeyMintDevice::convertStorageKeyToEphemeral( + const std::vector& /* storageKeyBlob */, + std::vector* /* ephemeralKeyBlob */) { + return km_utils::kmError2ScopedAStatus(KM_ERROR_UNIMPLEMENTED); +} +} // namespace aidl::android::hardware::security::keymint diff --git a/HAL/JavacardKeyMintDevice.h b/HAL/JavacardKeyMintDevice.h new file mode 100644 index 00000000..51ee0c14 --- /dev/null +++ b/HAL/JavacardKeyMintDevice.h @@ -0,0 +1,114 @@ +/* + * 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. + */ + +#pragma once + +#include "CborConverter.h" +#include "JavacardSecureElement.h" +#include +#include +#include +#include + +namespace aidl::android::hardware::security::keymint { +using namespace ::keymint::javacard; +using namespace aidl::android::hardware::security::sharedsecret; +using namespace aidl::android::hardware::security::secureclock; +using ndk::ScopedAStatus; +using std::optional; +using std::shared_ptr; +using std::vector; + +class JavacardKeyMintDevice : public BnKeyMintDevice { + public: + explicit JavacardKeyMintDevice(shared_ptr card) + : securitylevel_(SecurityLevel::STRONGBOX), card_(card) { + card_->initializeJavacard(); + } + virtual ~JavacardKeyMintDevice() {} + + ScopedAStatus getHardwareInfo(KeyMintHardwareInfo* info) override; + + ScopedAStatus addRngEntropy(const vector& data) override; + + ScopedAStatus generateKey(const vector& keyParams, + const optional& attestationKey, + KeyCreationResult* creationResult) override; + + ScopedAStatus importKey(const vector& keyParams, KeyFormat keyFormat, + const vector& keyData, + const optional& attestationKey, + KeyCreationResult* creationResult) override; + + ScopedAStatus importWrappedKey(const vector& wrappedKeyData, + const vector& wrappingKeyBlob, + const vector& maskingKey, + const vector& unwrappingParams, + int64_t passwordSid, int64_t biometricSid, + KeyCreationResult* creationResult) override; + + ScopedAStatus upgradeKey(const vector& keyBlobToUpgrade, + const vector& upgradeParams, + vector* keyBlob) override; + + ScopedAStatus deleteKey(const vector& keyBlob) override; + ScopedAStatus deleteAllKeys() override; + ScopedAStatus destroyAttestationIds() override; + + virtual ScopedAStatus begin(KeyPurpose in_purpose, const std::vector& in_keyBlob, + const std::vector& in_params, + const std::optional& in_authToken, + BeginResult* _aidl_return) override; + + ScopedAStatus deviceLocked(bool passwordOnly, + const optional& timestampToken) override; + + ScopedAStatus earlyBootEnded() override; + + ScopedAStatus getKeyCharacteristics(const std::vector& in_keyBlob, + const std::vector& in_appId, + const std::vector& in_appData, + std::vector* _aidl_return) override; + + ScopedAStatus convertStorageKeyToEphemeral(const std::vector& storageKeyBlob, + std::vector* ephemeralKeyBlob) override; + + private: + keymaster_error_t parseWrappedKey(const vector& wrappedKeyData, + std::vector& iv, std::vector& transitKey, + std::vector& secureKey, std::vector& tag, + vector& authList, KeyFormat& keyFormat, + std::vector& wrappedKeyDescription); + + std::tuple, keymaster_error_t> sendBeginImportWrappedKeyCmd( + const std::vector& transitKey, const std::vector& wrappingKeyBlob, + const std::vector& maskingKey, const vector& unwrappingParams); + + std::tuple, keymaster_error_t> + sendFinishImportWrappedKeyCmd(const vector& keyParams, KeyFormat keyFormat, + const std::vector& secureKey, + const std::vector& tag, const std::vector& iv, + const std::vector& wrappedKeyDescription, + int64_t passwordSid, int64_t biometricSid); + + ScopedAStatus defaultHwInfo(KeyMintHardwareInfo* info); + + const SecurityLevel securitylevel_; + const shared_ptr card_; + CborConverter cbor_; +}; + +} // namespace aidl::android::hardware::security::keymint diff --git a/HAL/JavacardKeyMintOperation.cpp b/HAL/JavacardKeyMintOperation.cpp new file mode 100644 index 00000000..d4952aa0 --- /dev/null +++ b/HAL/JavacardKeyMintOperation.cpp @@ -0,0 +1,285 @@ +/* + * 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. + */ + +#define LOG_TAG "javacard.strongbox.keymint.operation-impl" + +#include "JavacardKeyMintOperation.h" +#include +#include +#include +#include + +namespace aidl::android::hardware::security::keymint { +using namespace ::keymint::javacard; +using secureclock::TimeStampToken; + +JavacardKeyMintOperation::~JavacardKeyMintOperation() { + if (opHandle_ != 0) { + abort(); + } +} + +ScopedAStatus JavacardKeyMintOperation::updateAad(const vector& input, + const optional& authToken, + const optional& timestampToken) { + cppbor::Array request; + request.add(Uint(opHandle_)); + request.add(Bstr(input)); + cbor_.addHardwareAuthToken(request, authToken.value_or(HardwareAuthToken())); + cbor_.addTimeStampToken(request, timestampToken.value_or(TimeStampToken())); + auto [item, err] = card_->sendRequest(Instruction::INS_UPDATE_AAD_OPERATION_CMD, request); + if (err != KM_ERROR_OK) { + return km_utils::kmError2ScopedAStatus(err); + } + return ScopedAStatus::ok(); +} + +ScopedAStatus JavacardKeyMintOperation::update(const vector& input, + const optional& authToken, + const optional& timestampToken, + vector* output) { + HardwareAuthToken aToken = authToken.value_or(HardwareAuthToken()); + TimeStampToken tToken = timestampToken.value_or(TimeStampToken()); + DataView view = {.buffer = {}, .data = input, .start = 0, .length = input.size()}; + keymaster_error_t err = bufferData(view); + if (err != KM_ERROR_OK) { + return km_utils::kmError2ScopedAStatus(err); + } + if (!(bufferingMode_ == BufferingMode::EC_NO_DIGEST || + bufferingMode_ == BufferingMode::RSA_NO_DIGEST)) { + if (view.length > MAX_CHUNK_SIZE) { + err = updateInChunks(view, aToken, tToken, output); + if (err != KM_ERROR_OK) { + return km_utils::kmError2ScopedAStatus(err); + } + } + vector remaining = popNextChunk(view, view.length); + err = sendUpdate(remaining, aToken, tToken, *output); + } + return km_utils::kmError2ScopedAStatus(err); +} + +ScopedAStatus JavacardKeyMintOperation::finish( + const optional>& input, const optional>& signature, + const optional& authToken, const optional& timestampToken, + const optional>& /*confirmationToken*/, vector* output) { + HardwareAuthToken aToken = authToken.value_or(HardwareAuthToken()); + TimeStampToken tToken = timestampToken.value_or(TimeStampToken()); + const vector inData = input.value_or(vector()); + DataView view = {.buffer = {}, .data = inData, .start = 0, .length = inData.size()}; + const vector sign = signature.value_or(vector()); + appendBufferedData(view); + if (!(bufferingMode_ == BufferingMode::EC_NO_DIGEST || + bufferingMode_ == BufferingMode::RSA_NO_DIGEST)) { + if (view.length > MAX_CHUNK_SIZE) { + auto err = updateInChunks(view, aToken, tToken, output); + if (err != KM_ERROR_OK) { + return km_utils::kmError2ScopedAStatus(err); + } + } + } + vector remaining = popNextChunk(view, view.length); + return km_utils::kmError2ScopedAStatus(sendFinish(remaining, sign, aToken, tToken, *output)); +} + +ScopedAStatus JavacardKeyMintOperation::abort() { + Array request; + request.add(Uint(opHandle_)); + auto [item, err] = card_->sendRequest(Instruction::INS_ABORT_OPERATION_CMD, request); + opHandle_ = 0; + buffer_.clear(); + return km_utils::kmError2ScopedAStatus(err); +} + +void JavacardKeyMintOperation::blockAlign(DataView& view, uint16_t blockSize) { + appendBufferedData(view); + uint16_t offset = getDataViewOffset(view, blockSize); + if (view.buffer.empty() && view.data.empty()) { + offset = 0; + } else if (view.buffer.empty()) { + buffer_.insert(buffer_.end(), view.data.begin() + offset, view.data.end()); + } else if (view.data.empty()) { + buffer_.insert(buffer_.end(), view.buffer.begin() + offset, view.buffer.end()); + } else { + if (offset < view.buffer.size()) { + buffer_.insert(buffer_.end(), view.buffer.begin() + offset, view.buffer.end()); + buffer_.insert(buffer_.end(), view.data.begin(), view.data.end()); + } else { + offset = offset - view.buffer.size(); + buffer_.insert(buffer_.end(), view.data.begin() + offset, view.data.end()); + } + } + // adjust the view length by removing the buffered data size from it. + view.length = view.length - buffer_.size(); +} + +uint16_t JavacardKeyMintOperation::getDataViewOffset(DataView& view, uint16_t blockSize) { + uint16_t offset = 0; + uint16_t remaining = 0; + switch(bufferingMode_) { + case BufferingMode::BUF_AES_BLOCK_ALIGNED: + case BufferingMode::BUF_DES_BLOCK_ALIGNED: + offset = ((view.length / blockSize)) * blockSize; + break; + case BufferingMode::BUF_AES_DECRYPT_PKCS7_BLOCK_ALIGNED: + case BufferingMode::BUF_DES_DECRYPT_PKCS7_BLOCK_ALIGNED: + offset = ((view.length / blockSize)) * blockSize; + remaining = (view.length % blockSize); + if (offset >= blockSize && remaining == 0) { + offset -= blockSize; + } + break; + case BufferingMode::BUF_AES_GCM_DECRYPT_BLOCK_ALIGNED: + if (view.length > macLength_) { + offset = (view.length - macLength_); + } + break; + default: + break; + } + return offset; +} + +keymaster_error_t JavacardKeyMintOperation::bufferData(DataView& view) { + if (view.data.empty()) return KM_ERROR_OK; // nothing to buffer + switch (bufferingMode_) { + case BufferingMode::RSA_NO_DIGEST: + buffer_.insert(buffer_.end(), view.data.begin(), view.data.end()); + if (buffer_.size() > RSA_BUFFER_SIZE) { + abort(); + return KM_ERROR_INVALID_INPUT_LENGTH; + } + view.start = 0; + view.length = 0; + break; + case BufferingMode::EC_NO_DIGEST: + if (buffer_.size() < EC_BUFFER_SIZE) { + buffer_.insert(buffer_.end(), view.data.begin(), view.data.end()); + // Truncate the buffered data if greater then allowed EC buffer size. + if (buffer_.size() > EC_BUFFER_SIZE) { + buffer_.erase(buffer_.begin() + EC_BUFFER_SIZE, buffer_.end()); + } + } + view.start = 0; + view.length = 0; + break; + case BufferingMode::BUF_AES_BLOCK_ALIGNED: + case BufferingMode::BUF_AES_DECRYPT_PKCS7_BLOCK_ALIGNED: + blockAlign(view, AES_BLOCK_SIZE); + break; + case BufferingMode::BUF_AES_GCM_DECRYPT_BLOCK_ALIGNED: + blockAlign(view, macLength_); + break; + case BufferingMode::BUF_DES_BLOCK_ALIGNED: + case BufferingMode::BUF_DES_DECRYPT_PKCS7_BLOCK_ALIGNED: + blockAlign(view, DES_BLOCK_SIZE); + break; + case BufferingMode::NONE: + break; + } + return KM_ERROR_OK; +} + +// Incermentally send the request using multiple updates. +keymaster_error_t JavacardKeyMintOperation::updateInChunks(DataView& view, + HardwareAuthToken& authToken, + TimeStampToken& timestampToken, + vector* output) { + keymaster_error_t sendError = KM_ERROR_UNKNOWN_ERROR; + while (view.length > MAX_CHUNK_SIZE) { + vector chunk = popNextChunk(view, MAX_CHUNK_SIZE); + sendError = sendUpdate(chunk, authToken, timestampToken, *output); + if (sendError != KM_ERROR_OK) { + return sendError; + } + // Clear tokens + if (!authToken.mac.empty()) authToken = HardwareAuthToken(); + if (!timestampToken.mac.empty()) timestampToken = TimeStampToken(); + } + return KM_ERROR_OK; +} + +vector JavacardKeyMintOperation::popNextChunk(DataView& view, uint32_t chunkSize) { + uint32_t start = view.start; + uint32_t end = start + ((view.length < chunkSize) ? view.length : chunkSize); + vector chunk; + if (start < view.buffer.size()) { + if (end < view.buffer.size()) { + chunk = {view.buffer.begin() + start, view.buffer.begin() + end}; + } else { + end = end - view.buffer.size(); + chunk = {view.buffer.begin() + start, view.buffer.end()}; + chunk.insert(chunk.end(), view.data.begin(), view.data.begin() + end); + } + } else { + start = start - view.buffer.size(); + end = end - view.buffer.size(); + chunk = {view.data.begin() + start, view.data.begin() + end}; + } + view.start = view.start + chunk.size(); + view.length = view.length - chunk.size(); + return chunk; +} + +keymaster_error_t JavacardKeyMintOperation::sendUpdate(const vector& input, + const HardwareAuthToken& authToken, + const TimeStampToken& timestampToken, + vector& output) { + if (input.empty()) { + return KM_ERROR_OK; + } + cppbor::Array request; + request.add(Uint(opHandle_)); + request.add(Bstr(input)); + cbor_.addHardwareAuthToken(request, authToken); + cbor_.addTimeStampToken(request, timestampToken); + auto [item, error] = card_->sendRequest(Instruction::INS_UPDATE_OPERATION_CMD, request); + if (error != KM_ERROR_OK) { + return error; + } + vector respData; + if (!cbor_.getBinaryArray(item, 1, respData)) { + return KM_ERROR_UNKNOWN_ERROR; + } + output.insert(output.end(), respData.begin(), respData.end()); + return KM_ERROR_OK; +} + +keymaster_error_t JavacardKeyMintOperation::sendFinish(const vector& data, + const vector& sign, + const HardwareAuthToken& authToken, + const TimeStampToken& timestampToken, + vector& output) { + cppbor::Array request; + request.add(Uint(opHandle_)); + request.add(Bstr(data)); + request.add(Bstr(sign)); + cbor_.addHardwareAuthToken(request, authToken); + cbor_.addTimeStampToken(request, timestampToken); + auto [item, err] = card_->sendRequest(Instruction::INS_FINISH_OPERATION_CMD, request); + if (err != KM_ERROR_OK) { + return err; + } + vector respData; + if (!cbor_.getBinaryArray(item, 1, respData)) { + return KM_ERROR_UNKNOWN_ERROR; + } + opHandle_ = 0; + output.insert(output.end(), respData.begin(), respData.end()); + return KM_ERROR_OK; +} + +} // namespace aidl::android::hardware::security::keymint diff --git a/HAL/JavacardKeyMintOperation.h b/HAL/JavacardKeyMintOperation.h new file mode 100644 index 00000000..4f5fc046 --- /dev/null +++ b/HAL/JavacardKeyMintOperation.h @@ -0,0 +1,130 @@ +/* + * 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. + */ + +#pragma once + +#include "CborConverter.h" +#include "JavacardSecureElement.h" + +#include +#include +#include +#include + +#define AES_BLOCK_SIZE 16 +#define DES_BLOCK_SIZE 8 +#define RSA_BUFFER_SIZE 256 +#define EC_BUFFER_SIZE 32 +#define MAX_CHUNK_SIZE 256 +namespace aidl::android::hardware::security::keymint { +using namespace ::keymint::javacard; +using ::ndk::ScopedAStatus; +using secureclock::TimeStampToken; +using std::optional; +using std::shared_ptr; +using std::string; +using std::vector; + +// Bufferig modes for update +enum class BufferingMode : int32_t { + NONE = 0, // Send everything to javacard - most of the assymteric operations + RSA_NO_DIGEST = 1, // Buffer everything in update upto 256 bytes and send in finish. If + // input data is greater then 256 bytes then it is an error. Javacard + // will further check according to exact key size and crypto provider. + EC_NO_DIGEST = 2, // Buffer upto 65 bytes and then truncate. Javacard will further truncate + // upto exact keysize. + BUF_AES_BLOCK_ALIGNED = 3, // Buffer 15 bytes and reminder to make input data block aligned. + BUF_AES_DECRYPT_PKCS7_BLOCK_ALIGNED = 4, // Buffer 16 bytes. + BUF_DES_BLOCK_ALIGNED = 5, // Buffer 7 bytes and reminder to make input data block aligned. + BUF_DES_DECRYPT_PKCS7_BLOCK_ALIGNED = 6, // Buffer 8 bytes. + BUF_AES_GCM_DECRYPT_BLOCK_ALIGNED = 7, // Buffer 16 bytes. + +}; + +// The is the view in the input data being processed by update/finish funcion. + +struct DataView { + vector buffer; // previously buffered data from cycle n-1 + const vector& data; // current data in cycle n. + uint32_t start; // start of the view + size_t length; // length of the view +}; + +class JavacardKeyMintOperation : public BnKeyMintOperation { + public: + explicit JavacardKeyMintOperation(keymaster_operation_handle_t opHandle, + BufferingMode bufferingMode, + uint16_t macLength, + shared_ptr card) + : buffer_(vector()), bufferingMode_(bufferingMode), macLength_(macLength), + card_(card), opHandle_(opHandle) {} + virtual ~JavacardKeyMintOperation(); + + ScopedAStatus updateAad(const vector& input, + const optional& authToken, + const optional& timestampToken) override; + + ScopedAStatus update(const vector& input, const optional& authToken, + const optional& timestampToken, + vector* output) override; + + ScopedAStatus finish(const optional>& input, + const optional>& signature, + const optional& authToken, + const optional& timestampToken, + const optional>& confirmationToken, + vector* output) override; + + ScopedAStatus abort() override; + + private: + vector popNextChunk(DataView& view, uint32_t chunkSize); + + keymaster_error_t updateInChunks(DataView& data, HardwareAuthToken& authToken, + TimeStampToken& timestampToken, vector* output); + + keymaster_error_t sendFinish(const vector& data, const vector& signature, + const HardwareAuthToken& authToken, + const TimeStampToken& timestampToken, vector& output); + + keymaster_error_t sendUpdate(const vector& data, const HardwareAuthToken& authToken, + const TimeStampToken& timestampToken, vector& output); + + inline void appendBufferedData(DataView& view) { + if (!buffer_.empty()) { + view.buffer = buffer_; + view.length = view.length + buffer_.size(); + view.start = 0; + // view.buffer = insert(data.begin(), buffer_.begin(), buffer_.end()); + buffer_.clear(); + } + } + + std::tuple, keymaster_error_t> sendRequest(Instruction ins, + Array& request); + keymaster_error_t bufferData(DataView& data); + void blockAlign(DataView& data, uint16_t blockSize); + uint16_t getDataViewOffset(DataView& view, uint16_t blockSize); + + vector buffer_; + BufferingMode bufferingMode_; + uint16_t macLength_; + const shared_ptr card_; + keymaster_operation_handle_t opHandle_; + CborConverter cbor_; +}; + +} // namespace aidl::android::hardware::security::keymint diff --git a/HAL/JavacardRemotelyProvisionedComponentDevice.cpp b/HAL/JavacardRemotelyProvisionedComponentDevice.cpp new file mode 100644 index 00000000..5657f9e2 --- /dev/null +++ b/HAL/JavacardRemotelyProvisionedComponentDevice.cpp @@ -0,0 +1,264 @@ +/* + * Copyright 2021, 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. + */ + +#define LOG_TAG "javacard.keymint.device.rkp.strongbox-impl" +#include +#include +#include +#include +#include +#include + +namespace aidl::android::hardware::security::keymint { +using namespace cppcose; +using namespace keymaster; +using namespace cppbor; +// RKP error codes defined in keymint applet. +constexpr keymaster_error_t kStatusFailed = static_cast(32000); +constexpr keymaster_error_t kStatusInvalidMac = static_cast(32001); +constexpr keymaster_error_t kStatusProductionKeyInTestRequest = static_cast(32002); +constexpr keymaster_error_t kStatusTestKeyInProductionRequest = static_cast(32003); +constexpr keymaster_error_t kStatusInvalidEek = static_cast(32004); +constexpr keymaster_error_t kStatusInvalidState = static_cast(32005); + +namespace { + +keymaster_error_t translateRkpErrorCode(keymaster_error_t error) { + switch(static_cast(-error)) { + case kStatusFailed: + case kStatusInvalidState: + return static_cast(BnRemotelyProvisionedComponent::STATUS_FAILED); + case kStatusInvalidMac: + return static_cast(BnRemotelyProvisionedComponent::STATUS_INVALID_MAC); + case kStatusProductionKeyInTestRequest: + return static_cast(BnRemotelyProvisionedComponent::STATUS_PRODUCTION_KEY_IN_TEST_REQUEST); + case kStatusTestKeyInProductionRequest: + return static_cast(BnRemotelyProvisionedComponent::STATUS_TEST_KEY_IN_PRODUCTION_REQUEST); + case kStatusInvalidEek: + return static_cast(BnRemotelyProvisionedComponent::STATUS_INVALID_EEK); + } + return error; +} + +ScopedAStatus defaultHwInfo(RpcHardwareInfo* info) { + info->versionNumber = 1; + info->rpcAuthorName = "Google"; + info->supportedEekCurve = RpcHardwareInfo::CURVE_P256; + return ScopedAStatus::ok(); +} + +uint32_t coseKeyEncodedSize(const std::vector& keysToSign) { + uint32_t size = 0; + for(auto& macKey : keysToSign) { + auto [macedKeyItem, _, coseMacErrMsg] = + cppbor::parse(macKey.macedKey); + if (!macedKeyItem || !macedKeyItem->asArray() || + macedKeyItem->asArray()->size() != kCoseMac0EntryCount) { + LOG(ERROR) << "Invalid COSE_Mac0 structure"; + return 0; + } + auto payload = macedKeyItem->asArray()->get(kCoseMac0Payload)->asBstr(); + if (!payload) return 0; + size += payload->value().size(); + } + return size; +} + +} // namespace + +ScopedAStatus +JavacardRemotelyProvisionedComponentDevice::getHardwareInfo(RpcHardwareInfo* info) { + auto [item, err] = card_->sendRequest(Instruction::INS_GET_RKP_HARDWARE_INFO); + uint32_t versionNumber; + uint32_t supportedEekCurve; + if (err != KM_ERROR_OK || + !cbor_.getUint64(item, 1, versionNumber) || + !cbor_.getBinaryArray(item, 2, info->rpcAuthorName ) || + !cbor_.getUint64(item, 3, supportedEekCurve)) { + LOG(ERROR) << "Error in response of getHardwareInfo."; + LOG(INFO) << "Returning defaultHwInfo in getHardwareInfo."; + return defaultHwInfo(info); + } + info->versionNumber = static_cast(versionNumber); + info->supportedEekCurve = static_cast(supportedEekCurve); + return ScopedAStatus::ok(); +} + +ScopedAStatus +JavacardRemotelyProvisionedComponentDevice::generateEcdsaP256KeyPair(bool testMode, + MacedPublicKey* macedPublicKey, + std::vector* privateKeyHandle) { + cppbor::Array array; + array.add(testMode); + auto [item, err] = card_->sendRequest(Instruction::INS_GENERATE_RKP_KEY_CMD, array); + if (err != KM_ERROR_OK) { + LOG(ERROR) << "Error in sending generateEcdsaP256KeyPair."; + return km_utils::kmError2ScopedAStatus(translateRkpErrorCode(err)); + } + if (!cbor_.getBinaryArray(item, 1, macedPublicKey->macedKey) || + !cbor_.getBinaryArray(item, 2, *privateKeyHandle)) { + LOG(ERROR) << "Error in decoding og response in generateEcdsaP256KeyPair."; + return km_utils::kmError2ScopedAStatus(KM_ERROR_UNKNOWN_ERROR); + } + return ScopedAStatus::ok(); +} + +ScopedAStatus +JavacardRemotelyProvisionedComponentDevice::beginSendData( + bool testMode, const std::vector& keysToSign) { + uint32_t totalEncodedSize = coseKeyEncodedSize(keysToSign); + cppbor::Array array; + array.add(keysToSign.size()); + array.add(totalEncodedSize); + array.add(testMode); + auto [_, err] = card_->sendRequest(Instruction::INS_BEGIN_SEND_DATA_CMD, array); + if (err != KM_ERROR_OK) { + LOG(ERROR) << "Error in beginSendData."; + return km_utils::kmError2ScopedAStatus(translateRkpErrorCode(err)); + } + return ScopedAStatus::ok(); +} + +ScopedAStatus +JavacardRemotelyProvisionedComponentDevice::updateMacedKey( + const std::vector& keysToSign) { + for(auto& macedPublicKey : keysToSign) { + cppbor::Array array; + array.add(EncodedItem(macedPublicKey.macedKey)); + auto [_, err] = card_->sendRequest(Instruction::INS_UPDATE_KEY_CMD, array); + if (err != KM_ERROR_OK) { + LOG(ERROR) << "Error in updateMacedKey."; + return km_utils::kmError2ScopedAStatus(translateRkpErrorCode(err)); + } + } + return ScopedAStatus::ok(); +} + +ScopedAStatus +JavacardRemotelyProvisionedComponentDevice::updateChallenge( + const std::vector& challenge) { + Array array; + array.add(challenge); + auto [_, err] = card_->sendRequest(Instruction::INS_UPDATE_CHALLENGE_CMD, array); + if (err != KM_ERROR_OK) { + LOG(ERROR) << "Error in updateChallenge."; + return km_utils::kmError2ScopedAStatus(translateRkpErrorCode(err)); + } + return ScopedAStatus::ok(); +} + +ScopedAStatus +JavacardRemotelyProvisionedComponentDevice::updateEEK( + const std::vector& endpointEncCertChain) { + std::vector eekChain = endpointEncCertChain; + auto [_, err] = card_->sendRequest(Instruction::INS_UPDATE_EEK_CHAIN_CMD, eekChain); + if (err != KM_ERROR_OK) { + LOG(ERROR) << "Error in updateEEK."; + return km_utils::kmError2ScopedAStatus(translateRkpErrorCode(err)); + } + return ScopedAStatus::ok(); +} + +ScopedAStatus +JavacardRemotelyProvisionedComponentDevice::finishSendData( + std::vector* keysToSignMac, DeviceInfo* deviceInfo, + std::vector& coseEncryptProtectedHeader, cppbor::Map& coseEncryptUnProtectedHeader, + std::vector& partialCipheredData, uint32_t& respFlag) { + + std::vector decodedKeysToSignMac; + std::vector decodedDeviceInfo; + auto [item, err] = card_->sendRequest(Instruction::INS_FINISH_SEND_DATA_CMD); + if (err != KM_ERROR_OK) { + LOG(ERROR) << "Error in finishSendData."; + return km_utils::kmError2ScopedAStatus(translateRkpErrorCode(err)); + } + if (!cbor_.getBinaryArray(item, 1, decodedKeysToSignMac) || + !cbor_.getBinaryArray(item, 2, decodedDeviceInfo) || + !cbor_.getBinaryArray(item, 3, coseEncryptProtectedHeader) || + !cbor_.getMapItem(item, 4, coseEncryptUnProtectedHeader) || + !cbor_.getBinaryArray(item, 5, partialCipheredData) || + !cbor_.getUint64(item, 6, respFlag)) { + LOG(ERROR) << "Error in decoding og response in finishSendData."; + return km_utils::kmError2ScopedAStatus(KM_ERROR_UNKNOWN_ERROR); + } + *keysToSignMac = decodedKeysToSignMac; + deviceInfo->deviceInfo = decodedDeviceInfo; + return ScopedAStatus::ok(); +} + +ScopedAStatus +JavacardRemotelyProvisionedComponentDevice::getResponse( + std::vector& partialCipheredData, cppbor::Array& recepientStructure, + uint32_t& respFlag) { + auto [item, err] = card_->sendRequest(Instruction::INS_GET_RESPONSE_CMD); + if (err != KM_ERROR_OK) { + LOG(ERROR) << "Error in getResponse."; + return km_utils::kmError2ScopedAStatus(translateRkpErrorCode(err)); + } + if (!cbor_.getBinaryArray(item, 1, partialCipheredData) || + !cbor_.getArrayItem(item, 2, recepientStructure) || + !cbor_.getUint64(item, 3, respFlag)) { + LOG(ERROR) << "Error in decoding og response in getResponse."; + return km_utils::kmError2ScopedAStatus(KM_ERROR_UNKNOWN_ERROR); + } + return ScopedAStatus::ok(); +} + +ScopedAStatus +JavacardRemotelyProvisionedComponentDevice::generateCertificateRequest(bool testMode, + const std::vector& keysToSign, + const std::vector& endpointEncCertChain, + const std::vector& challenge, + DeviceInfo* deviceInfo, ProtectedData* protectedData, + std::vector* keysToSignMac) { + std::vector coseEncryptProtectedHeader; + cppbor::Map coseEncryptUnProtectedHeader; + cppbor::Array recipients; + std::vector cipheredData; + uint32_t respFlag; + auto ret = beginSendData(testMode, keysToSign); + if (!ret.isOk()) return ret; + + ret = updateMacedKey(keysToSign); + if (!ret.isOk()) return ret; + + ret = updateChallenge(challenge); + if (!ret.isOk()) return ret; + + ret = updateEEK(endpointEncCertChain); + if (!ret.isOk()) return ret; + + ret = finishSendData(keysToSignMac, deviceInfo, coseEncryptProtectedHeader, + coseEncryptUnProtectedHeader, cipheredData, + respFlag); + if (!ret.isOk()) return ret; + + while (respFlag != 0) { // more data is pending to receive + ret = getResponse(cipheredData, recipients, respFlag); + if (!ret.isOk()) return ret; + } + // Create ConseEncrypt structure. + protectedData->protectedData = + cppbor::Array() + .add(coseEncryptProtectedHeader) // Protected + .add(std::move(coseEncryptUnProtectedHeader)) // Unprotected + .add(cipheredData) // Payload + .add(std::move(recipients)) + .encode(); + return ScopedAStatus::ok(); +} + +} // namespace aidl::android::hardware::security::keymint diff --git a/HAL/JavacardRemotelyProvisionedComponentDevice.h b/HAL/JavacardRemotelyProvisionedComponentDevice.h new file mode 100644 index 00000000..c9dd85c5 --- /dev/null +++ b/HAL/JavacardRemotelyProvisionedComponentDevice.h @@ -0,0 +1,79 @@ +/* + * Copyright 2021, 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. + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +#include "CborConverter.h" +#include "JavacardSecureElement.h" + +namespace aidl::android::hardware::security::keymint { +using namespace ::keymint::javacard; +using ndk::ScopedAStatus; + +class JavacardRemotelyProvisionedComponentDevice + : public BnRemotelyProvisionedComponent { + public: + explicit JavacardRemotelyProvisionedComponentDevice( + shared_ptr card) + : card_(card) {} + + virtual ~JavacardRemotelyProvisionedComponentDevice() = default; + + ScopedAStatus getHardwareInfo(RpcHardwareInfo* info) override; + + ScopedAStatus generateEcdsaP256KeyPair( + bool testMode, MacedPublicKey* macedPublicKey, + std::vector* privateKeyHandle) override; + + ScopedAStatus generateCertificateRequest( + bool testMode, const std::vector& keysToSign, + const std::vector& endpointEncCertChain, + const std::vector& challenge, DeviceInfo* deviceInfo, + ProtectedData* protectedData, + std::vector* keysToSignMac) override; + + private: + ScopedAStatus beginSendData(bool testMode, + const std::vector& keysToSign); + + ScopedAStatus updateMacedKey(const std::vector& keysToSign); + + ScopedAStatus updateChallenge(const std::vector& challenge); + + ScopedAStatus updateEEK(const std::vector& endpointEncCertChain); + + ScopedAStatus finishSendData(std::vector* keysToSignMac, + DeviceInfo* deviceInfo, + std::vector& coseEncryptProtectedHeader, + cppbor::Map& coseEncryptUnProtectedHeader, + std::vector& partialCipheredData, + uint32_t& respFlag); + + ScopedAStatus getResponse(std::vector& partialCipheredData, + cppbor::Array& recepientStructure, + uint32_t& respFlag); + std::shared_ptr card_; + CborConverter cbor_; +}; + +} // namespace aidl::android::hardware::security::keymint diff --git a/HAL/JavacardSecureElement.cpp b/HAL/JavacardSecureElement.cpp new file mode 100644 index 00000000..57ec6181 --- /dev/null +++ b/HAL/JavacardSecureElement.cpp @@ -0,0 +1,140 @@ +/* + * 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. + */ + +#define LOG_TAG "javacard.keymint.device.strongbox-impl" +#include "JavacardSecureElement.h" +#include "keymint_utils.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace keymint::javacard { + +using namespace ::keymaster; +keymaster_error_t JavacardSecureElement::initializeJavacard() { + Array request; + request.add(Uint(getOsVersion())); + request.add(Uint(getOsPatchlevel())); + request.add(Uint(getVendorPatchlevel())); + auto [item, err] = sendRequest(Instruction::INS_SET_BOOT_PARAMS_CMD, request); + return err; +} + +keymaster_error_t JavacardSecureElement::constructApduMessage(Instruction& ins, + std::vector& inputData, + std::vector& apduOut) { + 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 (USHRT_MAX >= inputData.size()) { + // Send extended length APDU always as response size is not known to HAL. + // Case 1: Lc > 0 CLS | INS | P1 | P2 | 00 | 2 bytes of Lc | CommandData | 2 bytes of Le + // all set to 00. Case 2: Lc = 0 CLS | INS | P1 | P2 | 3 bytes of Le all set to 00. + // Extended length 3 bytes, starts with 0x00 + apduOut.push_back(static_cast(0x00)); + if (inputData.size() > 0) { + 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. + // Accepting complete length of output every time. + apduOut.push_back(static_cast(0x00)); + apduOut.push_back(static_cast(0x00)); + } else { + LOG(ERROR) << "Error in constructApduMessage."; + return (KM_ERROR_INVALID_INPUT_LENGTH); + } + return (KM_ERROR_OK); // success +} + +keymaster_error_t JavacardSecureElement::sendData(Instruction ins, std::vector& inData, + std::vector& response) { + keymaster_error_t ret = KM_ERROR_UNKNOWN_ERROR; + std::vector apdu; + + ret = constructApduMessage(ins, inData, apdu); + + if (ret != KM_ERROR_OK) { + return ret; + } + + if (!transport_->sendData(apdu, response)) { + LOG(ERROR) << "Error in sending data in sendData."; + return (KM_ERROR_SECURE_HW_COMMUNICATION_FAILED); + } + + // Response size should be greater than 2. Cbor output data followed by two bytes of APDU + // status. + if ((response.size() <= 2) || (getApduStatus(response) != APDU_RESP_STATUS_OK)) { + LOG(ERROR) << "Response of the sendData is wrong: response size = " << response.size() + << " apdu status = " << getApduStatus(response); + return (KM_ERROR_UNKNOWN_ERROR); + } + // remove the status bytes + response.pop_back(); + response.pop_back(); + return (KM_ERROR_OK); // success +} + +std::tuple, keymaster_error_t> +JavacardSecureElement::sendRequest(Instruction ins, Array& request) { + vector response; + // encode request + std::vector command = request.encode(); + auto sendError = sendData(ins, command, response); + if (sendError != KM_ERROR_OK) { + return {unique_ptr(nullptr), sendError}; + } + // decode the response and send that back + return cbor_.decodeData(response); +} + +std::tuple, keymaster_error_t> +JavacardSecureElement::sendRequest(Instruction ins, std::vector& command) { + vector response; + auto sendError = sendData(ins, command, response); + if (sendError != KM_ERROR_OK) { + return {unique_ptr(nullptr), sendError}; + } + // decode the response and send that back + return cbor_.decodeData(response); +} + +std::tuple, keymaster_error_t> +JavacardSecureElement::sendRequest(Instruction ins) { + vector response; + vector emptyRequest; + auto sendError = sendData(ins, emptyRequest, response); + if (sendError != KM_ERROR_OK) { + return {unique_ptr(nullptr), sendError}; + } + // decode the response and send that back + return cbor_.decodeData(response); +} + +} // namespace keymint::javacard diff --git a/HAL/JavacardSecureElement.h b/HAL/JavacardSecureElement.h new file mode 100644 index 00000000..160d3d4d --- /dev/null +++ b/HAL/JavacardSecureElement.h @@ -0,0 +1,108 @@ +/* + * 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. + */ + +#pragma once + +#include "CborConverter.h" +#include + +#define APDU_CLS 0x80 +#define APDU_P1 0x50 +#define APDU_P2 0x00 +#define APDU_RESP_STATUS_OK 0x9000 + +#define KEYMINT_CMD_APDU_START 0x20 + +namespace keymint::javacard { +using ndk::ScopedAStatus; +using std::optional; +using std::shared_ptr; +using std::vector; + +enum class Instruction { + // Keymaster commands + INS_GENERATE_KEY_CMD = KEYMINT_CMD_APDU_START + 1, + INS_IMPORT_KEY_CMD = KEYMINT_CMD_APDU_START + 2, + INS_IMPORT_WRAPPED_KEY_CMD = KEYMINT_CMD_APDU_START + 3, + INS_EXPORT_KEY_CMD = KEYMINT_CMD_APDU_START + 4, + INS_ATTEST_KEY_CMD = KEYMINT_CMD_APDU_START + 5, + INS_UPGRADE_KEY_CMD = KEYMINT_CMD_APDU_START + 6, + INS_DELETE_KEY_CMD = KEYMINT_CMD_APDU_START + 7, + INS_DELETE_ALL_KEYS_CMD = KEYMINT_CMD_APDU_START + 8, + INS_ADD_RNG_ENTROPY_CMD = KEYMINT_CMD_APDU_START + 9, + INS_COMPUTE_SHARED_SECRET_CMD = KEYMINT_CMD_APDU_START + 10, + INS_DESTROY_ATT_IDS_CMD = KEYMINT_CMD_APDU_START + 11, + INS_VERIFY_AUTHORIZATION_CMD = KEYMINT_CMD_APDU_START + 12, + INS_GET_SHARED_SECRET_PARAM_CMD = KEYMINT_CMD_APDU_START + 13, + INS_GET_KEY_CHARACTERISTICS_CMD = KEYMINT_CMD_APDU_START + 14, + INS_GET_HW_INFO_CMD = KEYMINT_CMD_APDU_START + 15, + INS_BEGIN_OPERATION_CMD = KEYMINT_CMD_APDU_START + 16, + INS_UPDATE_OPERATION_CMD = KEYMINT_CMD_APDU_START + 17, + INS_FINISH_OPERATION_CMD = KEYMINT_CMD_APDU_START + 18, + INS_ABORT_OPERATION_CMD = KEYMINT_CMD_APDU_START + 19, + INS_DEVICE_LOCKED_CMD = KEYMINT_CMD_APDU_START + 20, + INS_EARLY_BOOT_ENDED_CMD = KEYMINT_CMD_APDU_START + 21, + INS_GET_CERT_CHAIN_CMD = KEYMINT_CMD_APDU_START + 22, + INS_UPDATE_AAD_OPERATION_CMD = KEYMINT_CMD_APDU_START + 23, + INS_BEGIN_IMPORT_WRAPPED_KEY_CMD = KEYMINT_CMD_APDU_START + 24, + INS_FINISH_IMPORT_WRAPPED_KEY_CMD = KEYMINT_CMD_APDU_START + 25, + INS_SET_BOOT_PARAMS_CMD = KEYMINT_CMD_APDU_START + 26, + // RKP Commands + INS_GET_RKP_HARDWARE_INFO = KEYMINT_CMD_APDU_START + 27, + INS_GENERATE_RKP_KEY_CMD = KEYMINT_CMD_APDU_START + 28, + INS_BEGIN_SEND_DATA_CMD = KEYMINT_CMD_APDU_START + 29, + INS_UPDATE_KEY_CMD = KEYMINT_CMD_APDU_START + 30, + INS_UPDATE_EEK_CHAIN_CMD = KEYMINT_CMD_APDU_START + 31, + INS_UPDATE_CHALLENGE_CMD = KEYMINT_CMD_APDU_START + 32, + INS_FINISH_SEND_DATA_CMD = KEYMINT_CMD_APDU_START + 33, + INS_GET_RESPONSE_CMD = KEYMINT_CMD_APDU_START + 34, +}; + +class JavacardSecureElement { + public: + explicit JavacardSecureElement(shared_ptr transport, uint32_t osVersion, + uint32_t osPatchLevel, uint32_t vendorPatchLevel) + : transport_(transport), osVersion_(osVersion), osPatchLevel_(osPatchLevel), + vendorPatchLevel_(vendorPatchLevel) { + transport_->openConnection(); + } + virtual ~JavacardSecureElement() { transport_->closeConnection(); } + + std::tuple, keymaster_error_t> sendRequest(Instruction ins, + Array& request); + std::tuple, keymaster_error_t> sendRequest(Instruction ins); + std::tuple, keymaster_error_t> sendRequest(Instruction ins, std::vector& command); + + keymaster_error_t sendData(Instruction ins, std::vector& inData, + std::vector& response); + + keymaster_error_t constructApduMessage(Instruction& ins, std::vector& inputData, + std::vector& apduOut); + keymaster_error_t initializeJavacard(); + inline uint16_t getApduStatus(std::vector& inputData) { + // Last two bytes are the status SW0SW1 + uint8_t SW0 = inputData.at(inputData.size() - 2); + uint8_t SW1 = inputData.at(inputData.size() - 1); + return (SW0 << 8 | SW1); + } + + shared_ptr transport_; + uint32_t osVersion_; + uint32_t osPatchLevel_; + uint32_t vendorPatchLevel_; + CborConverter cbor_; +}; +} // namespace keymint::javacard diff --git a/HAL/JavacardSharedSecret.cpp b/HAL/JavacardSharedSecret.cpp new file mode 100644 index 00000000..cc83eabd --- /dev/null +++ b/HAL/JavacardSharedSecret.cpp @@ -0,0 +1,43 @@ +#define LOG_TAG "javacard.strongbox.keymint.operation-impl" +#include + +#include "JavacardSharedSecret.h" +#include + +namespace aidl::android::hardware::security::sharedsecret { +using namespace ::keymint::javacard; +using ndk::ScopedAStatus; +using std::optional; +using std::shared_ptr; +using std::vector; + +ScopedAStatus JavacardSharedSecret::getSharedSecretParameters(SharedSecretParameters* params) { + card_->initializeJavacard(); + auto [item, err] = card_->sendRequest(Instruction::INS_GET_SHARED_SECRET_PARAM_CMD); + if (err != KM_ERROR_OK || !cbor_.getSharedSecretParameters(item, 1, *params)) { + LOG(ERROR) << "Error in sending in getSharedSecretParameters."; + return km_utils::kmError2ScopedAStatus(KM_ERROR_UNKNOWN_ERROR); + } + return ScopedAStatus::ok(); +} + +ScopedAStatus +JavacardSharedSecret::computeSharedSecret(const std::vector& params, + std::vector* secret) { + + card_->initializeJavacard(); + cppbor::Array request; + cbor_.addSharedSecretParameters(request, params); + auto [item, err] = card_->sendRequest(Instruction::INS_COMPUTE_SHARED_SECRET_CMD, request); + if (err != KM_ERROR_OK) { + LOG(ERROR) << "Error in sending in computeSharedSecret."; + return km_utils::kmError2ScopedAStatus(err); + } + if (!cbor_.getBinaryArray(item, 1, *secret)) { + LOG(ERROR) << "Error in decoding the response in computeSharedSecret."; + return km_utils::kmError2ScopedAStatus(KM_ERROR_UNKNOWN_ERROR); + } + return ScopedAStatus::ok(); +} + +} // namespace aidl::android::hardware::security::sharedsecret diff --git a/HAL/JavacardSharedSecret.h b/HAL/JavacardSharedSecret.h new file mode 100644 index 00000000..29b0e050 --- /dev/null +++ b/HAL/JavacardSharedSecret.h @@ -0,0 +1,33 @@ +#pragma once + +#include "CborConverter.h" +#include "JavacardSecureElement.h" + +#include +#include +#include +#include + +namespace aidl::android::hardware::security::sharedsecret { +using namespace ::keymint::javacard; +using ndk::ScopedAStatus; +using std::optional; +using std::shared_ptr; +using std::vector; + +class JavacardSharedSecret : public BnSharedSecret { + public: + explicit JavacardSharedSecret(shared_ptr card) : card_(card) {} + virtual ~JavacardSharedSecret() {} + + ScopedAStatus getSharedSecretParameters(SharedSecretParameters* params) override; + + ScopedAStatus computeSharedSecret(const std::vector& params, + std::vector* secret) override; + + private: + shared_ptr card_; + CborConverter cbor_; +}; + +} // namespace aidl::android::hardware::security::sharedsecret diff --git a/HAL/README.md b/HAL/README.md deleted file mode 100644 index 38e3309a..00000000 --- a/HAL/README.md +++ /dev/null @@ -1,7 +0,0 @@ -# JavaCardKeymaster HAL - -This directory contains the implementation of the Keymaster 4.1 -Hardware Abstraction Layer (HAL) interface, implemented as a Linux -binary which runs as a standalone process mediating between Keystore -(the Keymaster client) and the Applet running on a JavaCard secure -element. diff --git a/HAL/keymaster/4.1/SocketTransport.cpp b/HAL/SocketTransport.cpp similarity index 62% rename from HAL/keymaster/4.1/SocketTransport.cpp rename to HAL/SocketTransport.cpp index 331a00b1..10ceab35 100644 --- a/HAL/keymaster/4.1/SocketTransport.cpp +++ b/HAL/SocketTransport.cpp @@ -14,79 +14,81 @@ ** See the License for the specific language governing permissions and ** limitations under the License. */ -#include -#include +#include "SocketTransport.h" +#include "ITransport.h" #include -#include -#include "Transport.h" +#include #include +#include +#include +#include -#define PORT 8080 -#define IPADDR "10.9.40.24" -//#define IPADDR "192.168.0.5" +#define PORT 8080 +#define IPADDR "192.168.7.239" #define MAX_RECV_BUFFER_SIZE 2500 -namespace se_transport { +namespace keymint::javacard { +using std::shared_ptr; +using std::vector; bool SocketTransport::openConnection() { - struct sockaddr_in serv_addr; - if ((mSocket = socket(AF_INET, SOCK_STREAM, 0)) < 0) - { - LOG(ERROR) << "Socket creation failed" << " Error: "<& output) { +bool SocketTransport::sendData(const vector& inData, vector& output) { uint8_t buffer[MAX_RECV_BUFFER_SIZE]; int count = 1; - while(!socketStatus && count++ < 5 ) { + while (!socketStatus && count++ < 5) { sleep(1); LOG(ERROR) << "Trying to open socket connection... count: " << count; openConnection(); } - if(count >= 5) { + if (count >= 5) { LOG(ERROR) << "Failed to open socket connection"; return false; } - if (0 > send(mSocket, inData, inLen , 0 )) { + if (0 > send(mSocket, inData.data(), inData.size(), 0)) { static int connectionResetCnt = 0; /* To avoid loop */ if (ECONNRESET == errno && connectionResetCnt == 0) { - //Connection reset. Try open socket and then sendData. + // Connection reset. Try open socket and then sendData. socketStatus = false; connectionResetCnt++; - return sendData(inData, inLen, output); + return sendData(inData, output); } LOG(ERROR) << "Failed to send data over socket err: " << errno; connectionResetCnt = 0; return false; } - ssize_t valRead = read( mSocket , buffer, MAX_RECV_BUFFER_SIZE); - if(0 > valRead) { + + ssize_t valRead = read(mSocket, buffer, MAX_RECV_BUFFER_SIZE); + if (0 > valRead) { LOG(ERROR) << "Failed to read data from socket."; } - for(size_t i = 0; i < valRead; i++) { + for (size_t i = 0; i < valRead; i++) { output.push_back(buffer[i]); } return true; @@ -102,4 +104,4 @@ bool SocketTransport::isConnected() { return socketStatus; } -} +} // namespace keymint::javacard diff --git a/HAL/SocketTransport.h b/HAL/SocketTransport.h new file mode 100644 index 00000000..2eb134fd --- /dev/null +++ b/HAL/SocketTransport.h @@ -0,0 +1,55 @@ +/* + ** + ** 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. + */ +#pragma once +#include "ITransport.h" +#include +#include + +namespace keymint::javacard { +using std::shared_ptr; +using std::vector; + +class SocketTransport : public ITransport { + + public: + SocketTransport() : mSocket(-1), socketStatus(false) {} + /** + * Creates a socket instance and connects to the provided server IP and port. + */ + bool openConnection() override; + /** + * Sends data over socket and receives data back. + */ + bool sendData(const vector& inData, vector& output) override; + /** + * Closes the connection. + */ + bool closeConnection() override; + /** + * Returns the state of the connection status. Returns true if the connection is active, + * false if connection is broken. + */ + bool isConnected() override; + + private: + /** + * Socket instance. + */ + int mSocket; + bool socketStatus; +}; +} // namespace keymint::javacard diff --git a/HAL/keymaster/include/Transport.h b/HAL/Transport.h similarity index 100% rename from HAL/keymaster/include/Transport.h rename to HAL/Transport.h diff --git a/HAL/android.hardware.security.keymint-service.strongbox.rc b/HAL/android.hardware.security.keymint-service.strongbox.rc new file mode 100644 index 00000000..9419c350 --- /dev/null +++ b/HAL/android.hardware.security.keymint-service.strongbox.rc @@ -0,0 +1,3 @@ +service vendor.keymint-strongbox /vendor/bin/hw/android.hardware.security.keymint-service.strongbox + class early_hal + user nobody diff --git a/HAL/android.hardware.security.keymint-service.strongbox.xml b/HAL/android.hardware.security.keymint-service.strongbox.xml new file mode 100644 index 00000000..0631f129 --- /dev/null +++ b/HAL/android.hardware.security.keymint-service.strongbox.xml @@ -0,0 +1,10 @@ + + + android.hardware.security.keymint + IKeyMintDevice/strongbox + + + android.hardware.security.keymint + IRemotelyProvisionedComponent/strongbox + + diff --git a/HAL/android.hardware.security.sharedsecret-service.strongbox.xml b/HAL/android.hardware.security.sharedsecret-service.strongbox.xml new file mode 100644 index 00000000..5492100e --- /dev/null +++ b/HAL/android.hardware.security.sharedsecret-service.strongbox.xml @@ -0,0 +1,6 @@ + + + android.hardware.security.sharedsecret + ISharedSecret/strongbox + + diff --git a/HAL/android.hardware.strongbox_keystore.xml b/HAL/android.hardware.strongbox_keystore.xml new file mode 100644 index 00000000..d92d6059 --- /dev/null +++ b/HAL/android.hardware.strongbox_keystore.xml @@ -0,0 +1,17 @@ + + + + + + + diff --git a/HAL/keymaster/4.1/CborConverter.cpp b/HAL/keymaster/4.1/CborConverter.cpp deleted file mode 100644 index 4d7041fd..00000000 --- a/HAL/keymaster/4.1/CborConverter.cpp +++ /dev/null @@ -1,412 +0,0 @@ -/* - ** - ** 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 - -bool CborConverter::addKeyparameters(Array& array, const android::hardware::hidl_vec& keyParams) { - Map map; - std::map> enum_repetition; - std::map uint_repetition; - for(size_t i = 0; i < keyParams.size(); i++) { - KeyParameter param = keyParams[i]; - TagType tagType = static_cast(param.tag & (0xF << 28)); - switch(tagType) { - case TagType::ENUM: - case TagType::UINT: - map.add(static_cast(param.tag), param.f.integer); - break; - case TagType::UINT_REP: - uint_repetition[static_cast(param.tag)].add(param.f.integer); - break; - case TagType::ENUM_REP: - enum_repetition[static_cast(param.tag)].push_back(static_cast(param.f.integer)); - break; - case TagType::ULONG: - map.add(static_cast(param.tag), param.f.longInteger); - break; - case TagType::ULONG_REP: - uint_repetition[static_cast(param.tag)].add(param.f.longInteger); - break; - case TagType::DATE: - map.add(static_cast(param.tag), param.f.dateTime); - break; - case TagType::BOOL: - map.add(static_cast(param.tag), static_cast(param.f.boolValue)); - break; - case TagType::BIGNUM: - case TagType::BYTES: - map.add(static_cast(param.tag), (std::vector(param.blob))); - break; - default: - /* Invalid skip */ - break; - } - } - if(0 < enum_repetition.size()) { - for( auto const& [key, val] : enum_repetition ) { - Bstr bstr(val); - map.add(key, std::move(bstr)); - } - } - if(0 < uint_repetition.size()) { - for( auto & [key, val] : uint_repetition ) { - map.add(key, std::move(val)); - } - } - array.add(std::move(map)); - return true; -} - -bool CborConverter::getKeyCharacteristics(const std::unique_ptr &item, const uint32_t pos, - KeyCharacteristics& keyCharacteristics) { - bool ret = false; - std::unique_ptr arrayItem(nullptr); - getItemAtPos(item, pos, arrayItem); - if ((arrayItem == nullptr) || (MajorType::ARRAY != getType(arrayItem))) - return ret; - - if (!getKeyParameters(arrayItem, 0, keyCharacteristics.softwareEnforced)) { - return ret; - } - - if (!getKeyParameters(arrayItem, 1, keyCharacteristics.hardwareEnforced)) { - return ret; - } - //success - ret = true; - return ret; -} - -bool CborConverter::getKeyParameter(const std::pair&, - const std::unique_ptr&> pair, std::vector& keyParams) { - bool ret = false; - uint64_t key; - uint64_t value; - - if(!getUint64(pair.first, key)) { - return ret; - } - - /* Get the TagType from the Tag */ - TagType tagType = static_cast(key & (0xF << 28)); - switch(tagType) { - case TagType::ENUM_REP: - { - /* ENUM_REP contains values encoded in a Binary string */ - const Bstr* bstr = pair.second.get()->asBstr(); - if(bstr == nullptr) return ret; - for (auto bchar : bstr->value()) { - KeyParameter keyParam; - keyParam.tag = static_cast(key); - keyParam.f.integer = bchar; - keyParams.push_back(std::move(keyParam)); - } - return true; - } - break; - case TagType::ENUM: - case TagType::UINT: - { - KeyParameter keyParam; - keyParam.tag = static_cast(key); - if(!getUint64(pair.second, value)) { - return ret; - } - keyParam.f.integer = static_cast(value); - keyParams.push_back(std::move(keyParam)); - return true; - } - break; - case TagType::ULONG: - { - KeyParameter keyParam; - keyParam.tag = static_cast(key); - if(!getUint64(pair.second, value)) { - return ret; - } - keyParam.f.longInteger = value; - keyParams.push_back(std::move(keyParam)); - return true; - } - break; - case TagType::UINT_REP: - { - /* UINT_REP contains values encoded in a Array */ - Array* array = const_cast(pair.second.get()->asArray()); - if(array == nullptr) return ret; - for(int i = 0; i < array->size(); i++) { - KeyParameter keyParam; - keyParam.tag = static_cast(key); - std::unique_ptr item = std::move((*array)[i]); - if(!getUint64(item, value)) { - return ret; - } - keyParam.f.integer = static_cast(value); - keyParams.push_back(std::move(keyParam)); - - } - return true; - } - break; - case TagType::ULONG_REP: - { - /* ULONG_REP contains values encoded in a Array */ - Array* array = const_cast(pair.second.get()->asArray()); - if(array == nullptr) return ret; - for(int i = 0; i < array->size(); i++) { - KeyParameter keyParam; - keyParam.tag = static_cast(key); - std::unique_ptr item = std::move((*array)[i]); - if(!getUint64(item, keyParam.f.longInteger)) { - return ret; - } - keyParams.push_back(std::move(keyParam)); - - } - return true; - } - break; - case TagType::DATE: - { - KeyParameter keyParam; - keyParam.tag = static_cast(key); - if(!getUint64(pair.second, value)) { - return ret; - } - keyParam.f.dateTime = value; - keyParams.push_back(std::move(keyParam)); - return true; - } - break; - case TagType::BOOL: - { - KeyParameter keyParam; - keyParam.tag = static_cast(key); - if(!getUint64(pair.second, value)) { - return ret; - } - keyParam.f.boolValue = static_cast(value); - keyParams.push_back(std::move(keyParam)); - return true; - } - break; - case TagType::BYTES: - { - KeyParameter keyParam; - keyParam.tag = static_cast(key); - const Bstr* bstr = pair.second.get()->asBstr(); - if(bstr == nullptr) return ret; - keyParam.blob = bstr->value(); - keyParams.push_back(std::move(keyParam)); - return true; - } - break; - default: - /* Invalid skip */ - break; - } - return ret; -} - - -bool CborConverter::getMultiBinaryArray(const std::unique_ptr& item, const uint32_t pos, - std::vector>& data) { - bool ret = false; - std::unique_ptr arrayItem(nullptr); - - getItemAtPos(item, pos, arrayItem); - if ((arrayItem == nullptr) || (MajorType::ARRAY != getType(arrayItem))) - return ret; - const Array* arr = arrayItem.get()->asArray(); - size_t arrSize = arr->size(); - for (int i = 0; i < arrSize; i++) { - std::vector temp; - if (!getBinaryArray(arrayItem, i, temp)) - return ret; - data.push_back(std::move(temp)); - } - ret = true; // success - return ret; -} - -bool CborConverter::getBinaryArray(const std::unique_ptr& item, const uint32_t pos, - ::android::hardware::hidl_vec& value) { - bool ret = false; - std::unique_ptr strItem(nullptr); - getItemAtPos(item, pos, strItem); - if ((strItem == nullptr) || (MajorType::BSTR != getType(strItem))) - return ret; - - const Bstr* bstr = strItem.get()->asBstr(); - value = bstr->value(); - ret = true; - return ret; -} - -bool CborConverter::getBinaryArray(const std::unique_ptr& item, const uint32_t pos, - ::android::hardware::hidl_string& value) { - std::vector vec; - std::string str; - if(!getBinaryArray(item, pos, vec)) { - return false; - } - for(auto ch : vec) { - str += ch; - } - value = str; - return true; -} - -bool CborConverter::getBinaryArray(const std::unique_ptr& item, const uint32_t pos, std::vector& value) { - bool ret = false; - std::unique_ptr strItem(nullptr); - getItemAtPos(item, pos, strItem); - if ((strItem == nullptr) || (MajorType::BSTR != getType(strItem))) - return ret; - - const Bstr* bstr = strItem.get()->asBstr(); - for (auto bchar : bstr->value()) { - value.push_back(bchar); - } - ret = true; - return ret; -} - -bool CborConverter::getHmacSharingParameters(const std::unique_ptr& item, const uint32_t pos, HmacSharingParameters& params) { - std::vector paramValue; - bool ret = false; - std::unique_ptr arrayItem(nullptr); - - //1. Get ArrayItem - //2. First item in the array seed; second item in the array is nonce. - - getItemAtPos(item, pos, arrayItem); - if ((arrayItem == nullptr) || (MajorType::ARRAY != getType(arrayItem))) - return ret; - - //Seed - if (!getBinaryArray(arrayItem, 0, params.seed)) - return ret; - - //nonce - if (!getBinaryArray(arrayItem, 1, paramValue)) - return ret; - memcpy(params.nonce.data(), paramValue.data(), paramValue.size()); - ret = true; - return ret; -} - -bool CborConverter::addVerificationToken(Array& array, const VerificationToken& - verificationToken, std::vector& encodedParamsVerified) { - Array vToken; - vToken.add(verificationToken.challenge); - vToken.add(verificationToken.timestamp); - vToken.add(std::move(encodedParamsVerified)); - vToken.add(static_cast(verificationToken.securityLevel)); - vToken.add((std::vector(verificationToken.mac))); - array.add(std::move(vToken)); - return true; -} - -bool CborConverter::addHardwareAuthToken(Array& array, const HardwareAuthToken& - authToken) { - Array hwAuthToken; - hwAuthToken.add(authToken.challenge); - hwAuthToken.add(authToken.userId); - hwAuthToken.add(authToken.authenticatorId); - hwAuthToken.add(static_cast(authToken.authenticatorType)); - hwAuthToken.add(authToken.timestamp); - hwAuthToken.add((std::vector(authToken.mac))); - array.add(std::move(hwAuthToken)); - return true; -} - -bool CborConverter::getHardwareAuthToken(const std::unique_ptr& item, const uint32_t pos, HardwareAuthToken& token) { - bool ret = false; - //challenge - if (!getUint64(item, pos, token.challenge)) - return ret; - //userId - if (!getUint64(item, pos+1, token.userId)) - return ret; - //AuthenticatorId - if (!getUint64(item, pos+2, token.authenticatorId)) - return ret; - //AuthType - uint64_t authType; - if (!getUint64(item, pos+3, authType)) - return ret; - token.authenticatorType = static_cast(authType); - //Timestamp - if (!getUint64(item, pos+4, token.timestamp)) - return ret; - //MAC - if (!getBinaryArray(item, pos+5, token.mac)) - return ret; - ret = true; - return ret; -} - -bool CborConverter::getVerificationToken(const std::unique_ptr& item, const uint32_t pos, VerificationToken& - token) { - bool ret = false; - //challenge - if (!getUint64(item, pos, token.challenge)) - return ret; - - //timestamp - if (!getUint64(item, pos+1, token.timestamp)) - return ret; - - //List of KeyParameters - if (!getKeyParameters(item, pos+2, token.parametersVerified)) - return ret; - - //AuthenticatorId - uint64_t val; - if (!getUint64(item, pos+3, val)) - return ret; - token.securityLevel = static_cast(val); - - //MAC - if (!getBinaryArray(item, pos+4, token.mac)) - return ret; - ret = true; - return ret; - -} - -bool CborConverter::getKeyParameters(const std::unique_ptr& item, const uint32_t pos, android::hardware::hidl_vec& keyParams) { - bool ret = false; - std::unique_ptr mapItem(nullptr); - std::vector params; - getItemAtPos(item, pos, mapItem); - if ((mapItem == nullptr) || (MajorType::MAP != getType(mapItem))) - return ret; - const Map* map = mapItem.get()->asMap(); - size_t mapSize = map->size(); - for (int i = 0; i < mapSize; i++) { - if (!getKeyParameter((*map)[i], params)) { - return ret; - } - } - keyParams.resize(params.size()); - keyParams = params; - ret = true; - return ret; -} diff --git a/HAL/keymaster/4.1/CommonUtils.cpp b/HAL/keymaster/4.1/CommonUtils.cpp deleted file mode 100644 index 090c8d0d..00000000 --- a/HAL/keymaster/4.1/CommonUtils.cpp +++ /dev/null @@ -1,353 +0,0 @@ -/* - ** - ** 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 -#include -#include - -#define TAG_SEQUENCE 0x30 -#define LENGTH_MASK 0x80 -#define LENGTH_VALUE_MASK 0x7F - -namespace keymaster { -namespace V4_1 { -namespace javacard { - -constexpr char kVendorPatchlevelProp[] = "ro.vendor.build.security_patch"; -constexpr char kVendorPatchlevelRegex[] = "^([0-9]{4})-([0-9]{2})-([0-9]{2})$"; -constexpr size_t kYearMatch = 1; -constexpr size_t kMonthMatch = 2; -constexpr size_t kDayMatch = 3; -constexpr size_t kVendorPatchlevelMatchCount = kDayMatch + 1; - -hidl_vec kmParamSet2Hidl(const keymaster_key_param_set_t& set) { - hidl_vec result; - if (set.length == 0 || set.params == nullptr) - return result; - - result.resize(set.length); - keymaster_key_param_t* params = set.params; - for (size_t i = 0; i < set.length; ++i) { - auto tag = params[i].tag; - result[i].tag = legacy_enum_conversion(tag); - switch (typeFromTag(tag)) { - case KM_ENUM: - case KM_ENUM_REP: - result[i].f.integer = params[i].enumerated; - break; - case KM_UINT: - case KM_UINT_REP: - result[i].f.integer = params[i].integer; - break; - case KM_ULONG: - case KM_ULONG_REP: - result[i].f.longInteger = params[i].long_integer; - break; - case KM_DATE: - result[i].f.dateTime = params[i].date_time; - break; - case KM_BOOL: - result[i].f.boolValue = params[i].boolean; - break; - case KM_BIGNUM: - case KM_BYTES: - result[i].blob.setToExternal(const_cast(params[i].blob.data), - params[i].blob.data_length); - break; - case KM_INVALID: - default: - params[i].tag = KM_TAG_INVALID; - /* just skip */ - break; - } - } - return result; -} - -keymaster_key_param_set_t hidlKeyParams2Km(const hidl_vec& keyParams) { - keymaster_key_param_set_t set; - - set.params = new keymaster_key_param_t[keyParams.size()]; - set.length = keyParams.size(); - - for (size_t i = 0; i < keyParams.size(); ++i) { - auto tag = legacy_enum_conversion(keyParams[i].tag); - switch (typeFromTag(tag)) { - case KM_ENUM: - case KM_ENUM_REP: - set.params[i] = keymaster_param_enum(tag, keyParams[i].f.integer); - break; - case KM_UINT: - case KM_UINT_REP: - set.params[i] = keymaster_param_int(tag, keyParams[i].f.integer); - break; - case KM_ULONG: - case KM_ULONG_REP: - set.params[i] = keymaster_param_long(tag, keyParams[i].f.longInteger); - break; - case KM_DATE: - set.params[i] = keymaster_param_date(tag, keyParams[i].f.dateTime); - break; - case KM_BOOL: - if (keyParams[i].f.boolValue) - set.params[i] = keymaster_param_bool(tag); - else - set.params[i].tag = KM_TAG_INVALID; - break; - case KM_BIGNUM: - case KM_BYTES: - set.params[i] = - keymaster_param_blob(tag, &keyParams[i].blob[0], keyParams[i].blob.size()); - break; - case KM_INVALID: - default: - set.params[i].tag = KM_TAG_INVALID; - /* just skip */ - break; - } - } - - return set; -} - -ErrorCode getEcCurve(const EC_GROUP *group, EcCurve& ecCurve) { - int curve = EC_GROUP_get_curve_name(group); - switch(curve) { - case NID_secp224r1: - ecCurve = EcCurve::P_224; - break; - case NID_X9_62_prime256v1: - ecCurve = EcCurve::P_256; - break; - case NID_secp384r1: - ecCurve = EcCurve::P_384; - break; - case NID_secp521r1: - ecCurve = EcCurve::P_521; - break; - default: - return ErrorCode::UNSUPPORTED_EC_CURVE; - } - return ErrorCode::OK; -} - -ErrorCode ecRawKeyFromPKCS8(const std::vector& pkcs8Blob, std::vector& secret, std::vector& -publicKey, EcCurve& ecCurve) { - ErrorCode errorCode = ErrorCode::INVALID_KEY_BLOB; - EVP_PKEY *pkey = nullptr; - const uint8_t *data = pkcs8Blob.data(); - - d2i_PrivateKey(EVP_PKEY_EC, &pkey, &data, pkcs8Blob.size()); - if(!pkey) { - return legacy_enum_conversion(TranslateLastOpenSslError()); - } - - UniquePtr ec_key(EVP_PKEY_get1_EC_KEY(pkey)); - if(!ec_key.get()) - return legacy_enum_conversion(TranslateLastOpenSslError()); - - //Get EC Group - const EC_GROUP *group = EC_KEY_get0_group(ec_key.get()); - if(group == NULL) - return errorCode; - - if(ErrorCode::OK != (errorCode = getEcCurve(group, ecCurve))) { - return errorCode; - } - - //Extract private key. - const BIGNUM *privBn = EC_KEY_get0_private_key(ec_key.get()); - int privKeyLen = BN_num_bytes(privBn); - std::unique_ptr privKey(new uint8_t[privKeyLen]); - BN_bn2bin(privBn, privKey.get()); - secret.insert(secret.begin(), privKey.get(), privKey.get()+privKeyLen); - - //Extract public key. - const EC_POINT *point = EC_KEY_get0_public_key(ec_key.get()); - int pubKeyLen=0; - pubKeyLen = EC_POINT_point2oct(group, point, POINT_CONVERSION_UNCOMPRESSED, NULL, 0, NULL); - std::unique_ptr pubKey(new uint8_t[pubKeyLen]); - EC_POINT_point2oct(group, point, POINT_CONVERSION_UNCOMPRESSED, pubKey.get(), pubKeyLen, NULL); - publicKey.insert(publicKey.begin(), pubKey.get(), pubKey.get()+pubKeyLen); - - EVP_PKEY_free(pkey); - return ErrorCode::OK; -} - -ErrorCode rsaRawKeyFromPKCS8(const std::vector& pkcs8Blob, std::vector& privateExp, std::vector& -pubModulus) { - ErrorCode errorCode = ErrorCode::INVALID_KEY_BLOB; - const BIGNUM *n=NULL, *e=NULL, *d=NULL; - EVP_PKEY *pkey = nullptr; - const uint8_t *data = pkcs8Blob.data(); - - d2i_PrivateKey(EVP_PKEY_RSA, &pkey, &data, pkcs8Blob.size()); - if(!pkey) { - return legacy_enum_conversion(TranslateLastOpenSslError()); - } - - UniquePtr rsa_key(EVP_PKEY_get1_RSA(pkey)); - if(!rsa_key.get()) { - return legacy_enum_conversion(TranslateLastOpenSslError()); - } - - RSA_get0_key(rsa_key.get(), &n, &e, &d); - if(d != NULL && n != NULL) { - /*private exponent */ - int privExpLen = BN_num_bytes(d); - std::unique_ptr privExp(new uint8_t[privExpLen]); - BN_bn2bin(d, privExp.get()); - /* public modulus */ - int pubModLen = BN_num_bytes(n); - std::unique_ptr pubMod(new uint8_t[pubModLen]); - BN_bn2bin(n, pubMod.get()); - - privateExp.insert(privateExp.begin(), privExp.get(), privExp.get()+privExpLen); - pubModulus.insert(pubModulus.begin(), pubMod.get(), pubMod.get()+pubModLen); - } else { - return errorCode; - } - EVP_PKEY_free(pkey); - 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; -} - -uint32_t match_to_uint32(const char* expression, const regmatch_t& match) { - if (match.rm_so == -1) return 0; - - size_t len = match.rm_eo - match.rm_so; - std::string s(expression + match.rm_so, len); - return std::stoul(s); -} - -std::string wait_and_get_property(const char* prop) { - std::string prop_value; - while (!android::base::WaitForPropertyCreation(prop)) { - LOG(ERROR) << "waited 15s for %s, still waiting..." << prop; - } - prop_value = android::base::GetProperty(prop, "" /* default */); - return prop_value; -} - - -uint32_t GetVendorPatchlevel(const char* patchlevel_str) { - regex_t regex; - if (regcomp(®ex, kVendorPatchlevelRegex, REG_EXTENDED) != 0) { - LOG(ERROR) << "Failed to compile Vendor patchlevel regex! " << kVendorPatchlevelRegex; - return 0; - } - - regmatch_t matches[kVendorPatchlevelMatchCount]; - int not_match = - regexec(®ex, patchlevel_str, kVendorPatchlevelMatchCount, matches, 0 /* flags */); - regfree(®ex); - if (not_match) { - LOG(ERROR) << "Vendor patchlevel string does not match expected format. Using patchlevel 0"; - return 0; - } - - uint32_t year = match_to_uint32(patchlevel_str, matches[kYearMatch]); - uint32_t month = match_to_uint32(patchlevel_str, matches[kMonthMatch]); - uint32_t day = match_to_uint32(patchlevel_str, matches[kDayMatch]); - - if (month < 1 || month > 12) { - LOG(ERROR) << "Invalid patch month " << month; - return 0; - } - bool isLeapYear = (0 == year % 4) ? true : false; - int maxDaysForMonth = 31; - switch(month) { - case 4: case 6: case 9: case 11: - maxDaysForMonth = 30; - break; - case 2: - maxDaysForMonth = isLeapYear ? 29 : 28; - break; - } - if (day < 1 || day > maxDaysForMonth) { - LOG(ERROR) << "Invalid patch day " << day; - return 0; - } - return year * 10000 + month * 100 + day; -} - -uint32_t GetVendorPatchlevel() { - std::string patchlevel = wait_and_get_property(kVendorPatchlevelProp); - return GetVendorPatchlevel(patchlevel.c_str()); -} - - -} // namespace javacard -} // namespace V4_1 -} // namespace keymaster diff --git a/HAL/keymaster/4.1/JavacardKeymaster4Device.cpp b/HAL/keymaster/4.1/JavacardKeymaster4Device.cpp deleted file mode 100644 index 67f8f527..00000000 --- a/HAL/keymaster/4.1/JavacardKeymaster4Device.cpp +++ /dev/null @@ -1,1487 +0,0 @@ -/* - ** - ** 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 - -#include -#include -#include -#include -#include -#include -#include -#include - -#define JAVACARD_KEYMASTER_NAME "JavacardKeymaster4.1Device v1.0" -#define JAVACARD_KEYMASTER_AUTHOR "Android Open Source Project" - -#define APDU_CLS 0x80 -#define APDU_P1 0x40 -#define APDU_P2 0x00 -#define APDU_RESP_STATUS_OK 0x9000 - -#define INS_BEGIN_KM_CMD 0x00 -#define INS_END_KM_PROVISION_CMD 0x20 -#define INS_END_KM_CMD 0x7F -#define SW_KM_OPR 0UL -#define SB_KM_OPR 1UL -#define SE_POWER_RESET_STATUS_FLAG ( 1 << 30) - -namespace keymaster { -namespace V4_1 { -namespace javacard { - -static std::unique_ptr pTransportFactory = nullptr; -constexpr size_t kOperationTableSize = 4; -/* Key is the newly generated operation handle. Value is a pair with first element having - * original operation handle and second element represents SW or SB operation. - */ -std::map> operationTable; - -struct KM_AUTH_LIST_Delete { - void operator()(KM_AUTH_LIST* p) { KM_AUTH_LIST_free(p); } -}; - -enum class Instruction { - // 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, - INS_GET_PROVISION_STATUS_CMD = INS_BEGIN_KM_CMD+8, - INS_SET_VERSION_PATCHLEVEL_CMD = INS_BEGIN_KM_CMD+9, -}; - -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_PRESHARED_SECRET = 0x10, - PROVISION_STATUS_BOOT_PARAM = 0x20, - PROVISION_STATUS_PROVISIONING_LOCKED = 0x40, -}; - -//Extended error codes -enum ExtendedErrors { - SW_CONDITIONS_NOT_SATISFIED = -10001, - UNSUPPORTED_CLA = -10002, - INVALID_P1P2 = -10003, - UNSUPPORTED_INSTRUCTION = -10004, - CMD_NOT_ALLOWED = -10005, - SW_WRONG_LENGTH = -10006, - INVALID_DATA = -10007, - CRYPTO_ILLEGAL_USE = -10008, - CRYPTO_ILLEGAL_VALUE = -10009, - CRYPTO_INVALID_INIT = -10010, - CRYPTO_NO_SUCH_ALGORITHM = -10011, - CRYPTO_UNINITIALIZED_KEY = -10012, - GENERIC_UNKNOWN_ERROR = -10013 -}; - -static inline std::unique_ptr& getTransportFactoryInstance() { - if(pTransportFactory == nullptr) { - pTransportFactory = std::unique_ptr(new se_transport::TransportFactory( - android::base::GetBoolProperty("ro.kernel.qemu", false))); - pTransportFactory->openConnection(); - } - return pTransportFactory; -} - -static inline bool findTag(const hidl_vec& params, Tag tag) { - size_t size = params.size(); - for(size_t i = 0; i < size; ++i) { - if(tag == params[i].tag) - return true; - } - return false; -} - -static inline bool getTag(const hidl_vec& params, Tag tag, KeyParameter& param) { - size_t size = params.size(); - for(size_t i = 0; i < size; ++i) { - if(tag == params[i].tag) { - param = params[i]; - return true; - } - } - return false; -} - -template -static T translateExtendedErrorsToHalErrors(T& errorCode) { - T err; - switch(static_cast(errorCode)) { - case SW_CONDITIONS_NOT_SATISFIED: - case UNSUPPORTED_CLA: - case INVALID_P1P2: - case INVALID_DATA: - case CRYPTO_ILLEGAL_USE: - case CRYPTO_ILLEGAL_VALUE: - case CRYPTO_INVALID_INIT: - case CRYPTO_UNINITIALIZED_KEY: - case GENERIC_UNKNOWN_ERROR: - err = T::UNKNOWN_ERROR; - break; - case CRYPTO_NO_SUCH_ALGORITHM: - err = T::UNSUPPORTED_ALGORITHM; - break; - case UNSUPPORTED_INSTRUCTION: - case CMD_NOT_ALLOWED: - case SW_WRONG_LENGTH: - err = T::UNIMPLEMENTED; - break; - default: - err = static_cast(errorCode); - break; - } - return err; -} - -/* Generate new operation handle */ -static ErrorCode generateOperationHandle(uint64_t& oprHandle) { - std::map>::iterator it; - do { - keymaster_error_t err = GenerateRandom(reinterpret_cast(&oprHandle), (size_t)sizeof(oprHandle)); - if (err != KM_ERROR_OK) { - return legacy_enum_conversion(err); - } - it = operationTable.find(oprHandle); - } while (it != operationTable.end()); - return ErrorCode::OK; -} - -/* Create a new operation handle entry in operation table.*/ -static ErrorCode createOprHandleEntry(uint64_t origOprHandle, uint64_t keymasterSrc, uint64_t& newOperationHandle) { - ErrorCode errorCode = ErrorCode::OK; - if (ErrorCode::OK != (errorCode = generateOperationHandle(newOperationHandle))) { - return errorCode; - } - operationTable[newOperationHandle] = std::make_pair(origOprHandle, keymasterSrc); - return errorCode; -} - -/* Get original operation handle generated by softkeymaster/strongboxkeymaster. */ -static ErrorCode getOrigOperationHandle(uint64_t halGeneratedOperationHandle, uint64_t& origOprHandle) { - std::map>::iterator it = operationTable.find(halGeneratedOperationHandle); - if (it == operationTable.end()) { - return ErrorCode::INVALID_OPERATION_HANDLE; - } - origOprHandle = it->second.first; - return ErrorCode::OK; -} - -/* Tells if the operation handle belongs to strongbox keymaster. */ -static bool isStrongboxOperation(uint64_t halGeneratedOperationHandle) { - std::map>::iterator it = operationTable.find(halGeneratedOperationHandle); - if (it == operationTable.end()) { - return false; - } - return (SB_KM_OPR == it->second.second); -} - -/* Delete the operation handle entry from operation table. */ -static void deleteOprHandleEntry(uint64_t halGeneratedOperationHandle) { - operationTable.erase(halGeneratedOperationHandle); -} - -/* Clears all the strongbox operation handle entries from operation table */ -static void clearStrongboxOprHandleEntries(const std::unique_ptr& oprCtx) { - LOG(INFO) << "Secure Element reset or applet upgrade detected. Removing existing operation handles"; - auto it = operationTable.begin(); - while (it != operationTable.end()) { - if (it->second.second == SB_KM_OPR) { //Strongbox operation - LOG(INFO) << "operation handle: " << it->first << " is removed"; - oprCtx->clearOperationData(it->second.first); - it = operationTable.erase(it); - } else { - ++it; - } - } -} - -/** - * Returns the negative value of the same number. - */ -static inline int32_t get2sCompliment(uint32_t value) { - return static_cast(~value+1); -} - -/** - * Clears all the strongbox operation handle entries if secure element power reset happens. - * And also extracts the error code value after unmasking the power reset status flag. - */ -static uint32_t handleErrorCode(const std::unique_ptr& oprCtx, uint32_t errorCode) { - //Check if secure element is reset - bool isSeResetOccurred = (0 != (errorCode & SE_POWER_RESET_STATUS_FLAG)); - - if (isSeResetOccurred) { - //Clear the operation table for Strongbox operations entries. - clearStrongboxOprHandleEntries(oprCtx); - // Unmask the power reset status flag. - errorCode &= ~SE_POWER_RESET_STATUS_FLAG; - } - return errorCode; -} - -template -static std::tuple, T> decodeData(CborConverter& cb, const std::vector& response, bool - hasErrorCode, const std::unique_ptr& oprCtx) { - std::unique_ptr item(nullptr); - T errorCode = T::OK; - std::tie(item, errorCode) = cb.decodeData(response, hasErrorCode); - - uint32_t tempErrCode = handleErrorCode(oprCtx, static_cast(errorCode)); - - // SE sends errocode as unsigned value so convert the unsigned value - // into a signed value of same magnitude and copy back to errorCode. - errorCode = static_cast(get2sCompliment(tempErrCode)); - - if (T::OK != errorCode) { - LOG(ERROR) << "error in decodeData: " << (int32_t) errorCode; - errorCode = translateExtendedErrorsToHalErrors(errorCode); - } - LOG(DEBUG) << "decodeData status: " << (int32_t) errorCode; - return {std::move(item), errorCode}; -} - -ErrorCode encodeParametersVerified(const VerificationToken& verificationToken, std::vector& asn1ParamsVerified) { - if (verificationToken.parametersVerified.size() > 0) { - AuthorizationSet paramSet; - KeymasterBlob derBlob; - UniquePtr kmAuthList(KM_AUTH_LIST_new()); - - paramSet.Reinitialize(KmParamSet(verificationToken.parametersVerified)); - - auto err = build_auth_list(paramSet, kmAuthList.get()); - if (err != KM_ERROR_OK) { - return legacy_enum_conversion(err); - } - int len = i2d_KM_AUTH_LIST(kmAuthList.get(), nullptr); - if (len < 0) { - return legacy_enum_conversion(TranslateLastOpenSslError()); - } - - if (!derBlob.Reset(len)) { - return legacy_enum_conversion(KM_ERROR_MEMORY_ALLOCATION_FAILED); - } - - uint8_t* p = derBlob.writable_data(); - len = i2d_KM_AUTH_LIST(kmAuthList.get(), &p); - if (len < 0) { - return legacy_enum_conversion(TranslateLastOpenSslError()); - } - asn1ParamsVerified.insert(asn1ParamsVerified.begin(), p, p+len); - derBlob.release(); - } - return ErrorCode::OK; -} - -ErrorCode prepareCborArrayFromKeyData(const hidl_vec& keyParams, KeyFormat keyFormat, const hidl_vec& blob, cppbor::Array& - array) { - ErrorCode errorCode = ErrorCode::OK; - AuthorizationSet paramSet; - keymaster_algorithm_t algorithm; - if(keyFormat == KeyFormat::PKCS8) { - - paramSet.Reinitialize(KmParamSet(keyParams)); - paramSet.GetTagValue(TAG_ALGORITHM, &algorithm); - - if(KM_ALGORITHM_RSA == algorithm) { - std::vector privExp; - std::vector modulus; - if(ErrorCode::OK != (errorCode = rsaRawKeyFromPKCS8(std::vector(blob), privExp, modulus))) { - return errorCode; - } - array.add(privExp); - array.add(modulus); - } else if(KM_ALGORITHM_EC == algorithm) { - std::vector privKey; - std::vector pubKey; - EcCurve curve; - if(ErrorCode::OK != (errorCode = ecRawKeyFromPKCS8(std::vector(blob), privKey, pubKey, curve))) { - return errorCode; - } - array.add(privKey); - array.add(pubKey); - } else { - return ErrorCode::UNSUPPORTED_ALGORITHM; - } - } else if(keyFormat == KeyFormat::RAW) { - array.add(std::vector(blob)); - } - return errorCode; -} - -ErrorCode parseWrappedKey(const hidl_vec& wrappedKeyData, std::vector& iv, std::vector& transitKey, -std::vector& secureKey, std::vector& tag, hidl_vec& authList, KeyFormat& -keyFormat, std::vector& wrappedKeyDescription) { - KeymasterBlob kmIv; - KeymasterKeyBlob kmTransitKey; - KeymasterKeyBlob kmSecureKey; - KeymasterBlob kmTag; - AuthorizationSet authSet; - keymaster_key_format_t kmKeyFormat; - KeymasterBlob kmWrappedKeyDescription; - - size_t keyDataLen = wrappedKeyData.size(); - uint8_t *keyData = dup_buffer(wrappedKeyData.data(), keyDataLen); - keymaster_key_blob_t keyMaterial = {keyData, keyDataLen}; - - keymaster_error_t error = parse_wrapped_key(KeymasterKeyBlob(keyMaterial), &kmIv, &kmTransitKey, - &kmSecureKey, &kmTag, &authSet, - &kmKeyFormat, &kmWrappedKeyDescription); - if (error != KM_ERROR_OK) return legacy_enum_conversion(error); - blob2Vec(kmIv.data, kmIv.data_length, iv); - blob2Vec(kmTransitKey.key_material, kmTransitKey.key_material_size, transitKey); - blob2Vec(kmSecureKey.key_material, kmSecureKey.key_material_size, secureKey); - blob2Vec(kmTag.data, kmTag.data_length, tag); - authList = kmParamSet2Hidl(authSet); - keyFormat = static_cast(kmKeyFormat); - blob2Vec(kmWrappedKeyDescription.data, kmWrappedKeyDescription.data_length, wrappedKeyDescription); - - return ErrorCode::OK; -} - -ErrorCode constructApduMessage(Instruction& ins, std::vector& inputData, std::vector& apduOut) { - 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(USHRT_MAX >= inputData.size()) { - // Send extended length APDU always as response size is not known to HAL. - // Case 1: Lc > 0 CLS | INS | P1 | P2 | 00 | 2 bytes of Lc | CommandData | 2 bytes of Le all set to 00. - // Case 2: Lc = 0 CLS | INS | P1 | P2 | 3 bytes of Le all set to 00. - //Extended length 3 bytes, starts with 0x00 - apduOut.push_back(static_cast(0x00)); - if (inputData.size() > 0) { - 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. - //Accepting complete length of output every time. - apduOut.push_back(static_cast(0x00)); - apduOut.push_back(static_cast(0x00)); - } else { - return (ErrorCode::INSUFFICIENT_BUFFER_SPACE); - } - - return (ErrorCode::OK);//success -} - -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)); -} - -ErrorCode sendData(Instruction ins, std::vector& inData, std::vector& response) { - ErrorCode ret = ErrorCode::UNKNOWN_ERROR; - std::vector apdu; - - ret = constructApduMessage(ins, inData, apdu); - if(ret != ErrorCode::OK) { - LOG(ERROR) << "error in constructApduMessage cmd: " << (int32_t)ins << " status: " << (int32_t)ret; - return ret; - } - - if(!getTransportFactoryInstance()->sendData(apdu.data(), apdu.size(), response)) { - LOG(ERROR) << "error in sendData cmd: " << (int32_t)ins << " status: " - << (int32_t)ErrorCode::SECURE_HW_COMMUNICATION_FAILED; - return (ErrorCode::SECURE_HW_COMMUNICATION_FAILED); - } - - // 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)) { - LOG(ERROR) << "error in sendData cmd: " << (int32_t)ins << " status: " << getStatus(response); - return (ErrorCode::UNKNOWN_ERROR); - } - LOG(DEBUG) << "sendData cmd: " << (int32_t)ins << " status: " << (int32_t)ErrorCode::OK; - return (ErrorCode::OK);//success -} - -/** - * Sends android system properties like os_version, os_patchlevel and vendor_patchlevel to - * the Applet. - */ -static ErrorCode setAndroidSystemProperties(CborConverter& cborConverter_, const std::unique_ptr& oprCtx) { - ErrorCode errorCode = ErrorCode::UNKNOWN_ERROR; - cppbor::Array array; - std::unique_ptr item; - std::vector cborOutData; - - array.add(GetOsVersion()). - add(GetOsPatchlevel()). - add(GetVendorPatchlevel()); - - std::vector cborData = array.encode(); - errorCode = sendData(Instruction::INS_SET_VERSION_PATCHLEVEL_CMD, cborData, cborOutData); - if (ErrorCode::OK == errorCode) { - //Skip last 2 bytes in cborData, it contains status. - std::tie(item, errorCode) = decodeData(cborConverter_, std::vector(cborOutData.begin(), cborOutData.end()-2), - true, oprCtx); - } - if (ErrorCode::OK != errorCode) - LOG(ERROR) << "Failed to set os_version, os_patchlevel and vendor_patchlevel err: " << (int32_t) errorCode; - - return errorCode; -} - -JavacardKeymaster4Device::JavacardKeymaster4Device(): softKm_(new ::keymaster::AndroidKeymaster( - []() -> auto { - auto context = new JavaCardSoftKeymasterContext(); - context->SetSystemVersion(GetOsVersion(), GetOsPatchlevel()); - return context; - }(), - kOperationTableSize)), oprCtx_(new OperationContext()), isEachSystemPropertySet(false) { - // Send Android system properties like os_version, os_patchlevel and vendor_patchlevel - // to the Applet. Incase if setting system properties fails here, again try setting - // it from computeSharedHmac. - if (ErrorCode::OK == setAndroidSystemProperties(cborConverter_, oprCtx_)) { - isEachSystemPropertySet = true; - } - -} - -JavacardKeymaster4Device::~JavacardKeymaster4Device() {} - -// Methods from IKeymasterDevice follow. -Return JavacardKeymaster4Device::getHardwareInfo(getHardwareInfo_cb _hidl_cb) { - // When socket is not connected return hardware info parameters from HAL itself. - std::vector resp; - std::vector input; - std::unique_ptr item; - uint64_t securityLevel = static_cast(SecurityLevel::STRONGBOX); - hidl_string jcKeymasterName; - hidl_string jcKeymasterAuthor; - - ErrorCode ret = sendData(Instruction::INS_GET_HW_INFO_CMD, input, resp); - if (ret == ErrorCode::OK) { - //Skip last 2 bytes in cborData, it contains status. - std::tie(item, ret) = decodeData(cborConverter_, std::vector(resp.begin(), resp.end()-2), - false, oprCtx_); - if (item != nullptr) { - std::vector temp; - if(!cborConverter_.getUint64(item, 0, securityLevel) || - !cborConverter_.getBinaryArray(item, 1, jcKeymasterName) || - !cborConverter_.getBinaryArray(item, 2, jcKeymasterAuthor)) { - LOG(ERROR) << "Failed to convert cbor data of INS_GET_HW_INFO_CMD"; - _hidl_cb(static_cast(securityLevel), jcKeymasterName, jcKeymasterAuthor); - return Void(); - } - } - _hidl_cb(static_cast(securityLevel), jcKeymasterName, jcKeymasterAuthor); - return Void(); - } else { - // It should not come here, but incase if for any reason SB keymaster fails to getHardwareInfo - // return proper values from HAL. - LOG(ERROR) << "Failed to fetch getHardwareInfo from javacard"; - _hidl_cb(SecurityLevel::STRONGBOX, JAVACARD_KEYMASTER_NAME, JAVACARD_KEYMASTER_AUTHOR); - return Void(); - } -} - -Return JavacardKeymaster4Device::getHmacSharingParameters(getHmacSharingParameters_cb _hidl_cb) { - std::vector cborData; - std::vector input; - std::unique_ptr item; - HmacSharingParameters hmacSharingParameters; - ErrorCode errorCode = ErrorCode::UNKNOWN_ERROR; - errorCode = sendData(Instruction::INS_GET_HMAC_SHARING_PARAM_CMD, input, cborData); - if (ErrorCode::OK == errorCode) { - //Skip last 2 bytes in cborData, it contains status. - std::tie(item, errorCode) = decodeData(cborConverter_, std::vector(cborData.begin(), cborData.end()-2), - true, oprCtx_); - if (item != nullptr) { - if(!cborConverter_.getHmacSharingParameters(item, 1, hmacSharingParameters)) { - LOG(ERROR) << "Failed to convert cbor data of INS_GET_HMAC_SHARING_PARAM_CMD"; - errorCode = ErrorCode::UNKNOWN_ERROR; - } - } - } -#ifdef VTS_EMULATOR - /* TODO temporary fix: vold daemon calls performHmacKeyAgreement. At that time when vold calls this API there is no - * network connectivity and socket cannot be connected. So as a hack we are calling softkeymaster to getHmacSharing - * parameters. - */ - else { - auto response = softKm_->GetHmacSharingParameters(); - LOG(DEBUG) << "INS_GET_HMAC_SHARING_PARAM_CMD not succeded with javacard"; - LOG(DEBUG) << "Setting software keymaster hmac sharing parameters"; - hmacSharingParameters.seed.setToExternal(const_cast(response.params.seed.data), - response.params.seed.data_length); - static_assert(sizeof(response.params.nonce) == hmacSharingParameters.nonce.size(), "Nonce sizes don't match"); - memcpy(hmacSharingParameters.nonce.data(), response.params.nonce, hmacSharingParameters.nonce.size()); - errorCode = legacy_enum_conversion(response.error); - LOG(DEBUG) << "INS_GET_HMAC_SHARING_PARAM_CMD softkm status: " << (int32_t) errorCode; - } -#endif - _hidl_cb(errorCode, hmacSharingParameters); - return Void(); -} - -Return JavacardKeymaster4Device::computeSharedHmac(const hidl_vec& params, computeSharedHmac_cb _hidl_cb) { - cppbor::Array array; - std::unique_ptr item; - std::vector cborOutData; - hidl_vec sharingCheck; - ErrorCode errorCode = ErrorCode::UNKNOWN_ERROR; - std::vector tempVec; - cppbor::Array outerArray; -#ifndef VTS_EMULATOR - // The Android system properties like OS_VERSION, OS_PATCHLEVEL and VENDOR_PATCHLEVEL are to - // be delivered to the Applet when the HAL is first loaded. Incase if settting system properties - // failed at construction time then this is one of the ideal places to send this information - // to the Applet as computeSharedHmac is called everytime when Android device boots. - if (!isEachSystemPropertySet) { - errorCode = setAndroidSystemProperties(cborConverter_); - if (ErrorCode::OK != errorCode) { - LOG(ERROR) << " Failed to set os_version, os_patchlevel and vendor_patchlevel err: " << (int32_t)errorCode; - _hidl_cb(errorCode, sharingCheck); - return Void(); - } - isEachSystemPropertySet = true; - } -#endif - - for(size_t i = 0; i < params.size(); ++i) { - cppbor::Array innerArray; - innerArray.add(static_cast>(params[i].seed)); - for(size_t j = 0; j < params[i].nonce.size(); j++) { - tempVec.push_back(params[i].nonce[j]); - } - innerArray.add(tempVec); - tempVec.clear(); - outerArray.add(std::move(innerArray)); - } - array.add(std::move(outerArray)); - std::vector cborData = array.encode(); - - errorCode = sendData(Instruction::INS_COMPUTE_SHARED_HMAC_CMD, cborData, cborOutData); - if (ErrorCode::OK == errorCode) { - //Skip last 2 bytes in cborData, it contains status. - std::tie(item, errorCode) = decodeData(cborConverter_, std::vector(cborOutData.begin(), cborOutData.end()-2), - true, oprCtx_); - if (item != nullptr) { - std::vector bstr; - if(!cborConverter_.getBinaryArray(item, 1, bstr)) { - LOG(ERROR) << "INS_COMPUTE_SHARED_HMAC_CMD: failed to convert cbor sharing check value"; - errorCode = ErrorCode::UNKNOWN_ERROR; - } else { - sharingCheck = bstr; - } - } - } -#ifdef VTS_EMULATOR - /* TODO temporary fix: vold daemon calls performHmacKeyAgreement. At that time when vold calls this API there is no - * network connectivity and socket cannot be connected. So as a hack we are calling softkeymaster to - * computeSharedHmac. - */ - else { - ComputeSharedHmacRequest request; - request.params_array.params_array = new keymaster::HmacSharingParameters[params.size()]; - request.params_array.num_params = params.size(); - for (size_t i = 0; i < params.size(); ++i) { - request.params_array.params_array[i].seed = {params[i].seed.data(), params[i].seed.size()}; - static_assert(sizeof(request.params_array.params_array[i].nonce) == - decltype(params[i].nonce)::size(), - "Nonce sizes don't match"); - memcpy(request.params_array.params_array[i].nonce, params[i].nonce.data(), - params[i].nonce.size()); - } - - LOG(DEBUG) << "INS_COMPUTE_SHARED_HMAC_CMD failed, computing shared check data using soft-key-master" << (int32_t) errorCode; - auto response = softKm_->ComputeSharedHmac(request); - if (response.error == KM_ERROR_OK) sharingCheck = kmBlob2hidlVec(response.sharing_check); - errorCode = legacy_enum_conversion(response.error); - LOG(DEBUG) << "INS_COMPUTE_SHARED_HMAC_CMD softkm status: " << (int32_t) errorCode; - } -#endif - _hidl_cb(errorCode, sharingCheck); - return Void(); - } - -Return JavacardKeymaster4Device::verifyAuthorization(uint64_t , const hidl_vec& , const HardwareAuthToken& , verifyAuthorization_cb _hidl_cb) { - VerificationToken verificationToken; - LOG(DEBUG) << "Verify authorizations UNIMPLEMENTED"; - _hidl_cb(ErrorCode::UNIMPLEMENTED, verificationToken); - return Void(); -} - -Return JavacardKeymaster4Device::addRngEntropy(const hidl_vec& data) { - cppbor::Array array; - std::vector cborOutData; - std::unique_ptr item; - ErrorCode errorCode = ErrorCode::UNKNOWN_ERROR; - - /* Convert input data to cbor format */ - array.add(std::vector(data)); - std::vector cborData = array.encode(); - - errorCode = sendData(Instruction::INS_ADD_RNG_ENTROPY_CMD, cborData, cborOutData); - if(errorCode == ErrorCode::OK) { - //Skip last 2 bytes in cborData, it contains status. - std::tie(item, errorCode) = decodeData(cborConverter_, std::vector(cborOutData.begin(), cborOutData.end()-2), - true, oprCtx_); - } - return errorCode; -} - -Return JavacardKeymaster4Device::generateKey(const hidl_vec& keyParams, generateKey_cb _hidl_cb) { - cppbor::Array array; - std::unique_ptr item; - hidl_vec keyBlob; - std::vector cborOutData; - ErrorCode errorCode = ErrorCode::UNKNOWN_ERROR; - KeyCharacteristics keyCharacteristics; - hidl_vec updatedParams(keyParams); - - if(!findTag(keyParams, Tag::CREATION_DATETIME) && - !findTag(keyParams, Tag::ACTIVE_DATETIME)) { - //Add CREATION_DATETIME in HAL, as secure element is not having clock. - size_t size = keyParams.size(); - updatedParams.resize(size+1); - updatedParams[size].tag = Tag::CREATION_DATETIME; - updatedParams[size].f.dateTime = java_time(time(nullptr)); - } - - /* Convert to cbor format */ - cborConverter_.addKeyparameters(array, updatedParams); - std::vector cborData = array.encode(); - - errorCode = sendData(Instruction::INS_GENERATE_KEY_CMD, cborData, cborOutData); - if(errorCode == ErrorCode::OK) { - //Skip last 2 bytes in cborData, it contains status. - std::tie(item, errorCode) = decodeData(cborConverter_, std::vector(cborOutData.begin(), cborOutData.end()-2), - true, oprCtx_); - if (item != nullptr) { - if(!cborConverter_.getBinaryArray(item, 1, keyBlob) || - !cborConverter_.getKeyCharacteristics(item, 2, keyCharacteristics)) { - //Clear the buffer. - keyBlob.setToExternal(nullptr, 0); - keyCharacteristics.softwareEnforced.setToExternal(nullptr, 0); - keyCharacteristics.hardwareEnforced.setToExternal(nullptr, 0); - errorCode = ErrorCode::UNKNOWN_ERROR; - LOG(ERROR) << "INS_GENERATE_KEY_CMD: error while converting cbor data: " << (int32_t) errorCode; - } - } - } - _hidl_cb(errorCode, keyBlob, keyCharacteristics); - return Void(); -} - -Return JavacardKeymaster4Device::importKey(const hidl_vec& keyParams, KeyFormat keyFormat, const hidl_vec& keyData, importKey_cb _hidl_cb) { - cppbor::Array array; - std::unique_ptr item; - hidl_vec keyBlob; - std::vector cborOutData; - ErrorCode errorCode = ErrorCode::UNKNOWN_ERROR; - KeyCharacteristics keyCharacteristics; - cppbor::Array subArray; - - if(keyFormat != KeyFormat::PKCS8 && keyFormat != KeyFormat::RAW) { - LOG(ERROR) << "INS_IMPORT_KEY_CMD unsupported key format " << (int32_t)keyFormat; - _hidl_cb(ErrorCode::UNSUPPORTED_KEY_FORMAT, keyBlob, keyCharacteristics); - return Void(); - } - cborConverter_.addKeyparameters(array, keyParams); - array.add(static_cast(KeyFormat::RAW)); //javacard accepts only RAW. - if(ErrorCode::OK != (errorCode = prepareCborArrayFromKeyData(keyParams, keyFormat, keyData, subArray))) { - LOG(ERROR) << "INS_IMPORT_KEY_CMD Error in while creating cbor data from key data:" << (int32_t) errorCode; - _hidl_cb(errorCode, keyBlob, keyCharacteristics); - return Void(); - } - std::vector encodedArray = subArray.encode(); - cppbor::Bstr bstr(encodedArray.begin(), encodedArray.end()); - array.add(bstr); - - std::vector cborData = array.encode(); - - errorCode = sendData(Instruction::INS_IMPORT_KEY_CMD, cborData, cborOutData); - - if(errorCode == ErrorCode::OK) { - //Skip last 2 bytes in cborData, it contains status. - std::tie(item, errorCode) = decodeData(cborConverter_, std::vector(cborOutData.begin(), cborOutData.end()-2), - true, oprCtx_); - if (item != nullptr) { - if(!cborConverter_.getBinaryArray(item, 1, keyBlob) || - !cborConverter_.getKeyCharacteristics(item, 2, keyCharacteristics)) { - //Clear the buffer. - keyBlob.setToExternal(nullptr, 0); - keyCharacteristics.softwareEnforced.setToExternal(nullptr, 0); - keyCharacteristics.hardwareEnforced.setToExternal(nullptr, 0); - errorCode = ErrorCode::UNKNOWN_ERROR; - LOG(ERROR) << "INS_IMPORT_KEY_CMD: error while converting cbor data, status: " << (int32_t) errorCode; - } - } - } - _hidl_cb(errorCode, keyBlob, keyCharacteristics); - return Void(); -} - -Return JavacardKeymaster4Device::importWrappedKey(const hidl_vec& wrappedKeyData, const hidl_vec& wrappingKeyBlob, const hidl_vec& maskingKey, const hidl_vec& unwrappingParams, uint64_t passwordSid, uint64_t biometricSid, importWrappedKey_cb _hidl_cb) { - cppbor::Array array; - std::unique_ptr item; - hidl_vec keyBlob; - std::vector cborOutData; - ErrorCode errorCode = ErrorCode::UNKNOWN_ERROR; - KeyCharacteristics keyCharacteristics; - std::vector iv; - std::vector transitKey; - std::vector secureKey; - std::vector tag; - hidl_vec authList; - KeyFormat keyFormat; - std::vector wrappedKeyDescription; - - if(ErrorCode::OK != (errorCode = parseWrappedKey(wrappedKeyData, iv, transitKey, secureKey, - tag, authList, keyFormat, wrappedKeyDescription))) { - LOG(ERROR) << "INS_IMPORT_WRAPPED_KEY_CMD error while parsing wrapped key status: " << (int32_t) errorCode; - _hidl_cb(errorCode, keyBlob, keyCharacteristics); - return Void(); - } - cborConverter_.addKeyparameters(array, authList); - array.add(static_cast(keyFormat)); - array.add(secureKey); - array.add(tag); - array.add(iv); - array.add(transitKey); - array.add(std::vector(wrappingKeyBlob)); - array.add(std::vector(maskingKey)); - cborConverter_.addKeyparameters(array, unwrappingParams); - array.add(std::vector(wrappedKeyDescription)); - array.add(passwordSid); - array.add(biometricSid); - std::vector cborData = array.encode(); - - errorCode = sendData(Instruction::INS_IMPORT_WRAPPED_KEY_CMD, cborData, cborOutData); - - if(errorCode == ErrorCode::OK) { - //Skip last 2 bytes in cborData, it contains status. - std::tie(item, errorCode) = decodeData(cborConverter_, std::vector(cborOutData.begin(), cborOutData.end()-2), - true, oprCtx_); - if (item != nullptr) { - if(!cborConverter_.getBinaryArray(item, 1, keyBlob) || - !cborConverter_.getKeyCharacteristics(item, 2, keyCharacteristics)) { - //Clear the buffer. - keyBlob.setToExternal(nullptr, 0); - keyCharacteristics.softwareEnforced.setToExternal(nullptr, 0); - keyCharacteristics.hardwareEnforced.setToExternal(nullptr, 0); - errorCode = ErrorCode::UNKNOWN_ERROR; - LOG(ERROR) << "INS_IMPORT_WRAPPED_KEY_CMD: error while converting cbor data, status: " << (int32_t) errorCode; - } - } - } - _hidl_cb(errorCode, keyBlob, keyCharacteristics); - return Void(); -} - -Return JavacardKeymaster4Device::getKeyCharacteristics(const hidl_vec& keyBlob, const hidl_vec& clientId, const hidl_vec& appData, getKeyCharacteristics_cb _hidl_cb) { - cppbor::Array array; - std::unique_ptr item; - std::vector cborOutData; - ErrorCode errorCode = ErrorCode::UNKNOWN_ERROR; - KeyCharacteristics keyCharacteristics; - - array.add(std::vector(keyBlob)); - array.add(std::vector(clientId)); - array.add(std::vector(appData)); - std::vector cborData = array.encode(); - - errorCode = sendData(Instruction::INS_GET_KEY_CHARACTERISTICS_CMD, cborData, cborOutData); - if(errorCode == ErrorCode::OK) { - //Skip last 2 bytes in cborData, it contains status. - std::tie(item, errorCode) = decodeData(cborConverter_, std::vector(cborOutData.begin(), cborOutData.end()-2), - true, oprCtx_); - if (item != nullptr) { - if(!cborConverter_.getKeyCharacteristics(item, 1, keyCharacteristics)) { - keyCharacteristics.softwareEnforced.setToExternal(nullptr, 0); - keyCharacteristics.hardwareEnforced.setToExternal(nullptr, 0); - errorCode = ErrorCode::UNKNOWN_ERROR; - LOG(ERROR) << "INS_GET_KEY_CHARACTERISTICS_CMD: error while converting cbor data, status: " << (int32_t) errorCode; - } - } - } - _hidl_cb(errorCode, keyCharacteristics); - return Void(); -} - -Return JavacardKeymaster4Device::exportKey(KeyFormat exportFormat, const hidl_vec& keyBlob, const hidl_vec& clientId, const hidl_vec& appData, exportKey_cb _hidl_cb) { - ErrorCode errorCode = ErrorCode::UNKNOWN_ERROR; - hidl_vec resultKeyBlob; - - //Check if keyblob is corrupted - getKeyCharacteristics(keyBlob, clientId, appData, - [&](ErrorCode error, KeyCharacteristics /*keyCharacteristics*/) { - errorCode = error; - }); - - if(errorCode != ErrorCode::OK) { - LOG(ERROR) << "Error in exportKey: " << (int32_t) errorCode; - _hidl_cb(errorCode, resultKeyBlob); - return Void(); - } - - ExportKeyRequest request; - request.key_format = legacy_enum_conversion(exportFormat); - request.SetKeyMaterial(keyBlob.data(), keyBlob.size()); - - ExportKeyResponse response; - softKm_->ExportKey(request, &response); - - if(response.error == KM_ERROR_INCOMPATIBLE_ALGORITHM) { - //Symmetric Keys cannot be exported. - response.error = KM_ERROR_UNSUPPORTED_KEY_FORMAT; - LOG(ERROR) << "error in exportKey: unsupported algorithm or key format"; - } - if (response.error == KM_ERROR_OK) { - resultKeyBlob.setToExternal(response.key_data, response.key_data_length); - } - errorCode = legacy_enum_conversion(response.error); - LOG(DEBUG) << "exportKey status: " << (int32_t) errorCode; - _hidl_cb(errorCode, resultKeyBlob); - return Void(); -} - - - -Return JavacardKeymaster4Device::attestKey(const hidl_vec& keyToAttest, const hidl_vec& attestParams, attestKey_cb _hidl_cb) { - cppbor::Array array; - std::unique_ptr item; - hidl_vec keyBlob; - std::vector cborOutData; - hidl_vec> certChain; - ErrorCode errorCode = ErrorCode::UNKNOWN_ERROR; - - 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) { - std::vector> temp; - std::vector rootCert; - //Skip last 2 bytes in cborData, it contains status. - std::tie(item, errorCode) = decodeData(cborConverter_, std::vector(cborOutData.begin(), cborOutData.end()-2), - true, oprCtx_); - if (item != nullptr) { - if(!cborConverter_.getMultiBinaryArray(item, 1, temp)) { - errorCode = ErrorCode::UNKNOWN_ERROR; - LOG(ERROR) << "INS_ATTEST_KEY_CMD: error in converting cbor data, status: " << (int32_t) errorCode; - } else { - cborData.clear(); - cborOutData.clear(); - errorCode = sendData(Instruction::INS_GET_CERT_CHAIN_CMD, cborData, cborOutData); - if(errorCode == ErrorCode::OK) { - //Skip last 2 bytes in cborData, it contains status. - std::tie(item, errorCode) = decodeData(cborConverter_, std::vector(cborOutData.begin(), - cborOutData.end()-2), - true, oprCtx_); - if (item != nullptr) { - std::vector chain; - if(!cborConverter_.getBinaryArray(item, 1, chain)) { - errorCode = ErrorCode::UNKNOWN_ERROR; - LOG(ERROR) << "attestkey INS_GET_CERT_CHAIN_CMD: errorn in converting cbor data, status: " << (int32_t) errorCode; - } 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) << "Error in attestkey getCertificateChain: " << (int32_t) errorCode; - } - } - } - } - } - } - } - _hidl_cb(errorCode, certChain); - return Void(); -} - -Return JavacardKeymaster4Device::upgradeKey(const hidl_vec& keyBlobToUpgrade, const hidl_vec& upgradeParams, upgradeKey_cb _hidl_cb) { - cppbor::Array array; - std::unique_ptr item; - hidl_vec upgradedKeyBlob; - std::vector cborOutData; - ErrorCode errorCode = ErrorCode::UNKNOWN_ERROR; - - array.add(std::vector(keyBlobToUpgrade)); - cborConverter_.addKeyparameters(array, upgradeParams); - std::vector cborData = array.encode(); - - errorCode = sendData(Instruction::INS_UPGRADE_KEY_CMD, cborData, cborOutData); - - if(errorCode == ErrorCode::OK) { - //Skip last 2 bytes in cborData, it contains status. - std::tie(item, errorCode) = decodeData(cborConverter_, std::vector(cborOutData.begin(), cborOutData.end()-2), - true, oprCtx_); - if (item != nullptr) { - if(!cborConverter_.getBinaryArray(item, 1, upgradedKeyBlob)) { - errorCode = ErrorCode::UNKNOWN_ERROR; - LOG(ERROR) << "INS_UPGRADE_KEY_CMD: error in converting cbor data, status: " << (int32_t) errorCode; - } - } - } - _hidl_cb(errorCode, upgradedKeyBlob); - return Void(); -} - -Return JavacardKeymaster4Device::deleteKey(const hidl_vec& keyBlob) { - cppbor::Array array; - std::unique_ptr item; - std::vector cborOutData; - ErrorCode errorCode = ErrorCode::UNKNOWN_ERROR; - - array.add(std::vector(keyBlob)); - std::vector cborData = array.encode(); - errorCode = sendData(Instruction::INS_DELETE_KEY_CMD, cborData, cborOutData); - - if(errorCode == ErrorCode::OK) { - //Skip last 2 bytes in cborData, it contains status. - std::tie(item, errorCode) = decodeData(cborConverter_, std::vector(cborOutData.begin(), cborOutData.end()-2), - true, oprCtx_); - } - return errorCode; -} - -Return JavacardKeymaster4Device::deleteAllKeys() { - std::unique_ptr item; - std::vector cborOutData; - std::vector input; - ErrorCode errorCode = ErrorCode::UNKNOWN_ERROR; - - errorCode = sendData(Instruction::INS_DELETE_ALL_KEYS_CMD, input, cborOutData); - - if(errorCode == ErrorCode::OK) { - //Skip last 2 bytes in cborData, it contains status. - std::tie(item, errorCode) = decodeData(cborConverter_, std::vector(cborOutData.begin(), cborOutData.end()-2), - true, oprCtx_); - } - return errorCode; -} - -Return JavacardKeymaster4Device::destroyAttestationIds() { - std::unique_ptr item; - std::vector cborOutData; - std::vector input; - ErrorCode errorCode = ErrorCode::UNKNOWN_ERROR; - - errorCode = sendData(Instruction::INS_DESTROY_ATT_IDS_CMD, input, cborOutData); - - if(errorCode == ErrorCode::OK) { - //Skip last 2 bytes in cborData, it contains status. - std::tie(item, errorCode) = decodeData(cborConverter_, std::vector(cborOutData.begin(), cborOutData.end()-2), - true, oprCtx_); - } - return errorCode; -} - -Return JavacardKeymaster4Device::begin(KeyPurpose purpose, const hidl_vec& keyBlob, const hidl_vec& inParams, const HardwareAuthToken& authToken, begin_cb _hidl_cb) { - ErrorCode errorCode = ErrorCode::UNKNOWN_ERROR; - hidl_vec outParams; - uint64_t operationHandle = 0; - hidl_vec resultParams; - uint64_t generatedOpHandle = 0; - - if(keyBlob.size() == 0) { - LOG(ERROR) << "Error in INS_BEGIN_OPERATION_CMD, keyblob size is 0"; - _hidl_cb(ErrorCode::INVALID_ARGUMENT, resultParams, operationHandle); - return Void(); - } - /* Asymmetric public key operations like RSA Verify, RSA Encrypt, ECDSA verify - * are handled by softkeymaster. - */ - LOG(DEBUG) << "INS_BEGIN_OPERATION_CMD purpose: " << (int32_t)purpose; - if (KeyPurpose::ENCRYPT == purpose || KeyPurpose::VERIFY == purpose) { - BeginOperationRequest request; - request.purpose = legacy_enum_conversion(purpose); - request.SetKeyMaterial(keyBlob.data(), keyBlob.size()); - request.additional_params.Reinitialize(KmParamSet(inParams)); - - BeginOperationResponse response; - /* For Symmetric key operation, the BeginOperation returns KM_ERROR_INCOMPATIBLE_ALGORITHM error. */ - softKm_->BeginOperation(request, &response); - errorCode = legacy_enum_conversion(response.error); - LOG(DEBUG) << "INS_BEGIN_OPERATION_CMD softkm BeginOperation status: " << (int32_t) errorCode; - if (errorCode != ErrorCode::OK) - LOG(ERROR) << "INS_BEGIN_OPERATION_CMD error in softkm BeginOperation status: " << (int32_t) errorCode; - - if (response.error == KM_ERROR_OK) { - resultParams = kmParamSet2Hidl(response.output_params); - } - if (response.error != KM_ERROR_INCOMPATIBLE_ALGORITHM) { /*Incompatible algorithm could be handled by JavaCard*/ - errorCode = legacy_enum_conversion(response.error); - /* Create a new operation handle and add a entry inside the operation table map with - * key - new operation handle - * value - hal generated operation handle. - */ - if (errorCode == ErrorCode::OK) { - errorCode = createOprHandleEntry(response.op_handle, SW_KM_OPR, generatedOpHandle); - if (errorCode != ErrorCode::OK) - LOG(ERROR) << "INS_BEGIN_OPERATION_CMD error while creating new operation handle: " << (int32_t) errorCode; - } - _hidl_cb(errorCode, resultParams, generatedOpHandle); - return Void(); - } - } - - cppbor::Array array; - std::vector cborOutData; - std::unique_ptr item; - std::unique_ptr blobItem = nullptr; - KeyCharacteristics keyCharacteristics; - KeyParameter param; - - /* Convert input data to cbor format */ - array.add(static_cast(purpose)); - array.add(std::vector(keyBlob)); - cborConverter_.addKeyparameters(array, inParams); - cborConverter_.addHardwareAuthToken(array, authToken); - std::vector cborData = array.encode(); - - // keyCharacteristics.hardwareEnforced is required to store algorithm, digest and padding values in operationInfo - // structure. To retrieve keyCharacteristics.hardwareEnforced, call getKeyCharacateristics. - // By calling getKeyCharacateristics also helps in finding a corrupted keyblob. - hidl_vec applicationId; - hidl_vec applicationData; - if(getTag(inParams, Tag::APPLICATION_ID, param)) { - applicationId = param.blob; - } - if(getTag(inParams, Tag::APPLICATION_DATA, param)) { - applicationData = param.blob; - } - //Call to getKeyCharacteristics. - getKeyCharacteristics(keyBlob, applicationId, applicationData, - [&](ErrorCode error, KeyCharacteristics keyChars) { - errorCode = error; - keyCharacteristics = keyChars; - }); - LOG(DEBUG) << "INS_BEGIN_OPERATION_CMD getKeyCharacteristics status: " << (int32_t) errorCode; - - if(errorCode == ErrorCode::OK) { - errorCode = ErrorCode::UNKNOWN_ERROR; - if(getTag(keyCharacteristics.hardwareEnforced, Tag::ALGORITHM, param)) { - errorCode = sendData(Instruction::INS_BEGIN_OPERATION_CMD, cborData, cborOutData); - if(errorCode == ErrorCode::OK) { - //Skip last 2 bytes in cborData, it contains status. - std::tie(item, errorCode) = decodeData(cborConverter_, std::vector(cborOutData.begin(), cborOutData.end()-2), - true, oprCtx_); - if (item != nullptr) { - if(!cborConverter_.getKeyParameters(item, 1, outParams) || - !cborConverter_.getUint64(item, 2, operationHandle)) { - errorCode = ErrorCode::UNKNOWN_ERROR; - outParams.setToExternal(nullptr, 0); - operationHandle = 0; - LOG(ERROR) << "INS_BEGIN_OPERATION_CMD: error in converting cbor data, status: " << (int32_t) errorCode; - } else { - /* Store the operationInfo */ - oprCtx_->setOperationInfo(operationHandle, purpose, param.f.algorithm, inParams); - } - } - } - } else { - LOG(ERROR) << "INS_BEGIN_OPERATION_CMD couldn't find algorithm tag: " << (int32_t)Tag::ALGORITHM; - } - } else { - LOG(ERROR) << "INS_BEGIN_OPERATION_CMD error in getKeyCharacteristics status: " << (int32_t) errorCode; - } - /* Create a new operation handle and add a entry inside the operation table map with - * key - new operation handle - * value - hal generated operation handle. - */ - if (ErrorCode::OK == errorCode) - errorCode = createOprHandleEntry(operationHandle, SB_KM_OPR, generatedOpHandle); - - _hidl_cb(errorCode, outParams, generatedOpHandle); - return Void(); -} - -Return JavacardKeymaster4Device::update(uint64_t halGeneratedOprHandle, const hidl_vec& inParams, const hidl_vec& input, const HardwareAuthToken& authToken, const VerificationToken& verificationToken, update_cb _hidl_cb) { - ErrorCode errorCode = ErrorCode::UNKNOWN_ERROR; - uint32_t inputConsumed = 0; - hidl_vec outParams; - hidl_vec output; - uint64_t operationHandle; - UpdateOperationResponse response; - if (ErrorCode::OK != (errorCode = getOrigOperationHandle(halGeneratedOprHandle, operationHandle))) { - LOG(ERROR) << " Operation handle is invalid. This could happen if invalid operation handle is passed or if" - << " secure element reset occurred."; - _hidl_cb(errorCode, inputConsumed, outParams, output); - return Void(); - } - - if (!isStrongboxOperation(halGeneratedOprHandle)) { - /* SW keymaster (Public key operation) */ - LOG(DEBUG) << "INS_UPDATE_OPERATION_CMD - swkm operation "; - UpdateOperationRequest request; - request.op_handle = operationHandle; - request.input.Reinitialize(input.data(), input.size()); - request.additional_params.Reinitialize(KmParamSet(inParams)); - - softKm_->UpdateOperation(request, &response); - errorCode = legacy_enum_conversion(response.error); - LOG(DEBUG) << "INS_UPDATE_OPERATION_CMD - swkm update operation status: " - << (int32_t) errorCode; - if (response.error == KM_ERROR_OK) { - inputConsumed = response.input_consumed; - outParams = kmParamSet2Hidl(response.output_params); - output = kmBuffer2hidlVec(response.output); - } else { - LOG(ERROR) << "INS_UPDATE_OPERATION_CMD - error swkm update operation status: " - << (int32_t) errorCode; - } - } else { - /* Strongbox Keymaster operation */ - std::vector tempOut; - /* OperationContext calls this below sendDataCallback callback function. This callback - * may be called multiple times if the input data is larger than MAX_ALLOWED_INPUT_SIZE. - */ - auto sendDataCallback = [&](std::vector& data, bool) -> ErrorCode { - cppbor::Array array; - std::unique_ptr item; - std::vector cborOutData; - std::vector asn1ParamsVerified; - // For symmetic ciphers only block aligned data is send to javacard Applet to reduce the number of calls to - //javacard. If the input message is less than block size then it is buffered inside the HAL. so in case if - // after buffering there is no data to send to javacard don't call javacard applet. - //For AES GCM operations, even though the input length is 0(which is not block aligned), if there is - //ASSOCIATED_DATA present in KeyParameters. Then we need to make a call to javacard Applet. - if(data.size() == 0 && !findTag(inParams, Tag::ASSOCIATED_DATA)) { - //Return OK, since this is not error case. - LOG(DEBUG) << "sendDataCallback: data size is zero"; - return ErrorCode::OK; - } - - if(ErrorCode::OK != (errorCode = encodeParametersVerified(verificationToken, asn1ParamsVerified))) { - LOG(ERROR) << "sendDataCallback: error in encodeParametersVerified status: " - << (int32_t) errorCode; - return errorCode; - } - - // Convert input data to cbor format - array.add(operationHandle); - cborConverter_.addKeyparameters(array, inParams); - array.add(data); - cborConverter_.addHardwareAuthToken(array, authToken); - cborConverter_.addVerificationToken(array, verificationToken, asn1ParamsVerified); - std::vector cborData = array.encode(); - - errorCode = sendData(Instruction::INS_UPDATE_OPERATION_CMD, cborData, cborOutData); - - if(errorCode == ErrorCode::OK) { - //Skip last 2 bytes in cborData, it contains status. - std::tie(item, errorCode) = decodeData(cborConverter_, std::vector(cborOutData.begin(), cborOutData.end()-2), - true, oprCtx_); - if (item != nullptr) { - /*Ignore inputConsumed from javacard SE since HAL consumes all the input */ - //cborConverter_.getUint64(item, 1, inputConsumed); - //This callback function may gets called multiple times so parse and get the outParams only once. - //Otherwise there can be chance of duplicate entries in outParams. Use tempOut to collect all the - //cipher text and finally copy it to the output. getBinaryArray function appends the new cipher text - //at the end of the tempOut(std::vector). - if((outParams.size() == 0 && !cborConverter_.getKeyParameters(item, 2, outParams)) || - !cborConverter_.getBinaryArray(item, 3, tempOut)) { - outParams.setToExternal(nullptr, 0); - tempOut.clear(); - errorCode = ErrorCode::UNKNOWN_ERROR; - LOG(ERROR) << "sendDataCallback: INS_UPDATE_OPERATION_CMD: error while converting cbor data, status: " << (int32_t) errorCode; - } - } - } - return errorCode; - }; - if(ErrorCode::OK == (errorCode = oprCtx_->update(operationHandle, std::vector(input), - sendDataCallback))) { - /* Consumed all the input */ - inputConsumed = input.size(); - output = tempOut; - } - LOG(DEBUG) << "Update operation status: " << (int32_t) errorCode; - if(ErrorCode::OK != errorCode) { - LOG(ERROR) << "Error in update operation, status: " << (int32_t) errorCode; - abort(halGeneratedOprHandle); - } - } - if(ErrorCode::OK != errorCode) { - /* Delete the entry from operation table. */ - LOG(ERROR) << "Delete entry from operation table, status: " << (int32_t) errorCode; - deleteOprHandleEntry(halGeneratedOprHandle); - } - - _hidl_cb(errorCode, inputConsumed, outParams, output); - return Void(); -} - -Return JavacardKeymaster4Device::finish(uint64_t halGeneratedOprHandle, const hidl_vec& inParams, const hidl_vec& input, const hidl_vec& signature, const HardwareAuthToken& authToken, const VerificationToken& verificationToken, finish_cb _hidl_cb) { - ErrorCode errorCode = ErrorCode::UNKNOWN_ERROR; - uint64_t operationHandle; - hidl_vec outParams; - hidl_vec output; - FinishOperationResponse response; - - if (ErrorCode::OK != (errorCode = getOrigOperationHandle(halGeneratedOprHandle, operationHandle))) { - LOG(ERROR) << " Operation handle is invalid. This could happen if invalid operation handle is passed or if" - << " secure element reset occurred."; - _hidl_cb(errorCode, outParams, output); - return Void(); - } - - if (!isStrongboxOperation(halGeneratedOprHandle)) { - /* SW keymaster (Public key operation) */ - LOG(DEBUG) << "FINISH - swkm operation "; - FinishOperationRequest request; - request.op_handle = operationHandle; - request.input.Reinitialize(input.data(), input.size()); - request.signature.Reinitialize(signature.data(), signature.size()); - request.additional_params.Reinitialize(KmParamSet(inParams)); - - softKm_->FinishOperation(request, &response); - - errorCode = legacy_enum_conversion(response.error); - LOG(DEBUG) << "FINISH - swkm operation, status: " << (int32_t) errorCode; - - if (response.error == KM_ERROR_OK) { - outParams = kmParamSet2Hidl(response.output_params); - output = kmBuffer2hidlVec(response.output); - } else { - LOG(ERROR) << "Error in finish operation, status: " << (int32_t) errorCode; - } - } else { - /* Strongbox Keymaster operation */ - std::vector tempOut; - bool aadTag = false; - /* OperationContext calls this below sendDataCallback callback function. This callback - * may be called multiple times if the input data is larger than MAX_ALLOWED_INPUT_SIZE. - * This callback function decides whether to call update/finish instruction based on the - * input received from the OperationContext through finish variable. - * if finish variable is false update instruction is called, if it is true finish instruction - * is called. - */ - auto sendDataCallback = [&](std::vector& data, bool finish) -> ErrorCode { - cppbor::Array array; - Instruction ins; - std::unique_ptr item; - std::vector cborOutData; - int keyParamPos, outputPos; - std::vector asn1ParamsVerified; - - if(ErrorCode::OK != (errorCode = encodeParametersVerified(verificationToken, asn1ParamsVerified))) { - LOG(ERROR) << "sendDataCallback: Error in encodeParametersVerified, status: " << (int32_t) errorCode; - return errorCode; - } - - //In case if there is ASSOCIATED_DATA present in the keyparams, then make sure it is either passed with - //update call or finish call. Don't send ASSOCIATED_DATA in both update and finish calls. aadTag is used to - //check if ASSOCIATED_DATA is already sent in update call. If addTag is true then skip ASSOCIATED_DATA from - //keyparams in finish call. - // Convert input data to cbor format - array.add(operationHandle); - if(finish) { - std::vector finishParams; - LOG(DEBUG) << "sendDataCallback: finish operation"; - if(aadTag) { - for(int i = 0; i < inParams.size(); i++) { - if(inParams[i].tag != Tag::ASSOCIATED_DATA) - finishParams.push_back(inParams[i]); - } - } else { - finishParams = inParams; - } - cborConverter_.addKeyparameters(array, finishParams); - array.add(data); - array.add(std::vector(signature)); - ins = Instruction::INS_FINISH_OPERATION_CMD; - keyParamPos = 1; - outputPos = 2; - } else { - LOG(DEBUG) << "sendDataCallback: update operation"; - if(findTag(inParams, Tag::ASSOCIATED_DATA)) { - aadTag = true; - } - cborConverter_.addKeyparameters(array, inParams); - array.add(data); - ins = Instruction::INS_UPDATE_OPERATION_CMD; - keyParamPos = 2; - outputPos = 3; - } - cborConverter_.addHardwareAuthToken(array, authToken); - cborConverter_.addVerificationToken(array, verificationToken, asn1ParamsVerified); - std::vector cborData = array.encode(); - errorCode = sendData(ins, cborData, cborOutData); - - if(errorCode == ErrorCode::OK) { - //Skip last 2 bytes in cborData, it contains status. - std::tie(item, errorCode) = decodeData(cborConverter_, std::vector(cborOutData.begin(), cborOutData.end()-2), - true, oprCtx_); - if (item != nullptr) { - //There is a change that this finish callback may gets called multiple times if the input data size - //is larger the MAX_ALLOWED_INPUT_SIZE (Refer OperationContext) so parse and get the outParams only - //once. Otherwise there can be chance of duplicate entries in outParams. Use tempOut to collect all - //the cipher text and finally copy it to the output. getBinaryArray function appends the new cipher - //text at the end of the tempOut(std::vector). - if((outParams.size() == 0 && !cborConverter_.getKeyParameters(item, keyParamPos, outParams)) || - !cborConverter_.getBinaryArray(item, outputPos, tempOut)) { - outParams.setToExternal(nullptr, 0); - tempOut.clear(); - errorCode = ErrorCode::UNKNOWN_ERROR; - LOG(ERROR) << "sendDataCallback: error while converting cbor data in operation: " << (int32_t)ins << " decodeData, status: " << (int32_t) errorCode; - } - } - } - return errorCode; - }; - if(ErrorCode::OK == (errorCode = oprCtx_->finish(operationHandle, std::vector(input), - sendDataCallback))) { - output = tempOut; - } - if (ErrorCode::OK != errorCode) { - LOG(ERROR) << "Error in finish operation, status: " << (int32_t) errorCode; - abort(halGeneratedOprHandle); - } - } - /* Delete the entry from operation table. */ - deleteOprHandleEntry(halGeneratedOprHandle); - oprCtx_->clearOperationData(operationHandle); - LOG(DEBUG) << "finish operation, status: " << (int32_t) errorCode; - _hidl_cb(errorCode, outParams, output); - return Void(); -} - -Return JavacardKeymaster4Device::abort(uint64_t halGeneratedOprHandle) { - ErrorCode errorCode = ErrorCode::UNKNOWN_ERROR; - uint64_t operationHandle; - if (ErrorCode::OK != (errorCode = getOrigOperationHandle(halGeneratedOprHandle, operationHandle))) { - LOG(ERROR) << " Operation handle is invalid. This could happen if invalid operation handle is passed or if" - << " secure element reset occurred."; - return errorCode; - } - AbortOperationRequest request; - request.op_handle = operationHandle; - - AbortOperationResponse response; - softKm_->AbortOperation(request, &response); - - errorCode = legacy_enum_conversion(response.error); - LOG(DEBUG) << "swkm abort operation, status: " << (int32_t) errorCode; - if (response.error == KM_ERROR_INVALID_OPERATION_HANDLE) { - cppbor::Array array; - std::unique_ptr item; - std::vector cborOutData; - - /* Convert input data to cbor format */ - array.add(operationHandle); - std::vector cborData = array.encode(); - - errorCode = sendData(Instruction::INS_ABORT_OPERATION_CMD, cborData, cborOutData); - - if(errorCode == ErrorCode::OK) { - //Skip last 2 bytes in cborData, it contains status. - std::tie(item, errorCode) = decodeData(cborConverter_, std::vector(cborOutData.begin(), cborOutData.end()-2), - true, oprCtx_); - } - } - /* Delete the entry on this operationHandle */ - oprCtx_->clearOperationData(operationHandle); - deleteOprHandleEntry(halGeneratedOprHandle); - return errorCode; -} - -// Methods from ::android::hardware::keymaster::V4_1::IKeymasterDevice follow. -Return<::android::hardware::keymaster::V4_1::ErrorCode> JavacardKeymaster4Device::deviceLocked(bool passwordOnly, const VerificationToken& verificationToken) { - cppbor::Array array; - std::unique_ptr item; - std::vector cborOutData; - ::android::hardware::keymaster::V4_1::ErrorCode errorCode = ::android::hardware::keymaster::V4_1::ErrorCode::UNKNOWN_ERROR; - std::vector asn1ParamsVerified; - ErrorCode ret = ErrorCode::UNKNOWN_ERROR; - - if(ErrorCode::OK != (ret = encodeParametersVerified(verificationToken, asn1ParamsVerified))) { - LOG(DEBUG) << "INS_DEVICE_LOCKED_CMD: Error in encodeParametersVerified, status: " << (int32_t) errorCode; - return errorCode; - } - - /* Convert input data to cbor format */ - array.add(passwordOnly); - cborConverter_.addVerificationToken(array, verificationToken, asn1ParamsVerified); - std::vector cborData = array.encode(); - - ret = sendData(Instruction::INS_DEVICE_LOCKED_CMD, cborData, cborOutData); - - if(ret == ErrorCode::OK) { - //Skip last 2 bytes in cborData, it contains status. - std::tie(item, errorCode) = decodeData<::android::hardware::keymaster::V4_1::ErrorCode>( - cborConverter_, std::vector(cborOutData.begin(), cborOutData.end()-2), true, oprCtx_); - } - return errorCode; -} - -Return<::android::hardware::keymaster::V4_1::ErrorCode> JavacardKeymaster4Device::earlyBootEnded() { - std::unique_ptr item; - std::string message; - std::vector cborOutData; - std::vector cborInput; - ::android::hardware::keymaster::V4_1::ErrorCode errorCode = ::android::hardware::keymaster::V4_1::ErrorCode::UNKNOWN_ERROR; - - ErrorCode ret = sendData(Instruction::INS_EARLY_BOOT_ENDED_CMD, cborInput, cborOutData); - - if(ret == ErrorCode::OK) { - //Skip last 2 bytes in cborData, it contains status. - std::tie(item, errorCode) = decodeData<::android::hardware::keymaster::V4_1::ErrorCode>( - cborConverter_, std::vector(cborOutData.begin(), cborOutData.end()-2), true, oprCtx_); - } - return errorCode; -} - -} // javacard -} // namespace V4_1 -} // namespace keymaster diff --git a/HAL/keymaster/4.1/JavacardOperationContext.cpp b/HAL/keymaster/4.1/JavacardOperationContext.cpp deleted file mode 100644 index 7319cb3c..00000000 --- a/HAL/keymaster/4.1/JavacardOperationContext.cpp +++ /dev/null @@ -1,385 +0,0 @@ -/* - ** - ** 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 - -#define MAX_ALLOWED_INPUT_SIZE 512 -#define AES_BLOCK_SIZE 16 -#define DES_BLOCK_SIZE 8 -#define RSA_INPUT_MSG_LEN 256 -#define EC_INPUT_MSG_LEN 32 -#define MAX_EC_BUFFER_SIZE 32 - -namespace keymaster { -namespace V4_1 { -namespace javacard { - -enum class Operation { - Update = 0, - Finish = 1 -}; - -inline ErrorCode hidlParamSet2OperatinInfo(const hidl_vec& params, OperationInfo& info) { - for(int i = 0; i < params.size(); i++) { - const KeyParameter ¶m = params[i]; - switch(param.tag) { - case Tag::ALGORITHM: - info.alg = static_cast(param.f.integer); - break; - case Tag::DIGEST: - info.digest = static_cast(param.f.integer); - break; - case Tag::PADDING: - info.pad = static_cast(param.f.integer); - break; - case Tag::BLOCK_MODE: - info.mode = static_cast(param.f.integer); - break; - default: - continue; - } - } - return ErrorCode::OK; -} - -ErrorCode OperationContext::setOperationInfo(uint64_t operationHandle, KeyPurpose purpose, Algorithm alg, - const hidl_vec& params) { - ErrorCode errorCode = ErrorCode::OK; - OperationData data; - if(ErrorCode::OK != (errorCode = hidlParamSet2OperatinInfo(params, data.info))) { - return errorCode; - } - data.info.purpose = purpose; - data.info.alg = alg; - memset((void*)&(data.data), 0x00, sizeof(data.data)); - operationTable[operationHandle] = data; - return ErrorCode::OK; -} - -ErrorCode OperationContext::clearOperationData(uint64_t operHandle) { - size_t size = operationTable.erase(operHandle); - if(!size) - return ErrorCode::INVALID_OPERATION_HANDLE; - else - return ErrorCode::OK; -} - -ErrorCode OperationContext::validateInputData(uint64_t operHandle, Operation opr, - const std::vector& actualInput, std::vector& input) { - ErrorCode errorCode = ErrorCode::OK; - - OperationData& oprData = operationTable[operHandle]; - - if(KeyPurpose::SIGN == oprData.info.purpose) { - if(Algorithm::RSA == oprData.info.alg && Digest::NONE == oprData.info.digest) { - if((oprData.data.buf_len+actualInput.size()) > RSA_INPUT_MSG_LEN) - return ErrorCode::INVALID_INPUT_LENGTH; - } else if(Algorithm::EC == oprData.info.alg && Digest::NONE == oprData.info.digest) { - /* Silently truncate the input */ - if(oprData.data.buf_len >= EC_INPUT_MSG_LEN) { - return ErrorCode::OK; - } else if(actualInput.size()+oprData.data.buf_len > EC_INPUT_MSG_LEN) { - for(int i=oprData.data.buf_len,j=0; i < EC_INPUT_MSG_LEN; ++i,++j) { - input.push_back(actualInput[j]); - } - return ErrorCode::OK; - } - } - } - - if(KeyPurpose::DECRYPT == oprData.info.purpose && Algorithm::RSA == oprData.info.alg) { - if((oprData.data.buf_len+actualInput.size()) > RSA_INPUT_MSG_LEN) { - return ErrorCode::INVALID_INPUT_LENGTH; - } - } - if(opr == Operation::Finish) { - //If it is observed in finish operation that buffered data + input data exceeds the MAX_ALLOWED_INPUT_SIZE then - //combine both the data in a single buffer. This helps in making sure that no data is left out in the buffer after - //finish opertion. - if((oprData.data.buf_len+actualInput.size()) > MAX_ALLOWED_INPUT_SIZE) { - for(size_t i = 0; i < oprData.data.buf_len; ++i) { - input.push_back(oprData.data.buf[i]); - } - input.insert(input.end(), actualInput.begin(), actualInput.end()); - //As buffered data is already consumed earse the buffer. - if(oprData.data.buf_len != 0) { - memset(oprData.data.buf, 0x00, sizeof(oprData.data.buf)); - oprData.data.buf_len = 0; - } - return ErrorCode::OK; - } - } - input = actualInput; - return errorCode; -} - -ErrorCode OperationContext::update(uint64_t operHandle, const std::vector& actualInput, - sendDataToSE_cb cb) { - ErrorCode errorCode = ErrorCode::OK; - std::vector input; - - /* Validate the input data */ - if(ErrorCode::OK != (errorCode = validateInputData(operHandle, Operation::Update, actualInput, input))) { - return errorCode; - } - - if (input.size() > MAX_ALLOWED_INPUT_SIZE) { - int noOfChunks = input.size()/MAX_ALLOWED_INPUT_SIZE; - int extraData = input.size()%MAX_ALLOWED_INPUT_SIZE; - for(int i =0 ; i < noOfChunks; i++) { - auto first = input.cbegin() + (i*MAX_ALLOWED_INPUT_SIZE); - auto end = first + MAX_ALLOWED_INPUT_SIZE; - std::vector newInput(first, end); - if(ErrorCode::OK != (errorCode = handleInternalUpdate(operHandle, newInput.data(), newInput.size(), - Operation::Update, cb))) { - return errorCode; - } - } - if(extraData > 0) { - std::vector finalInput(input.cend()-extraData, input.cend()); - if(ErrorCode::OK != (errorCode = handleInternalUpdate(operHandle, finalInput.data(), finalInput.size(), - Operation::Update, cb))) { - return errorCode; - } - } - } else { - if(ErrorCode::OK != (errorCode = handleInternalUpdate(operHandle, input.data(), input.size(), - Operation::Update, cb))) { - return errorCode; - } - } - return errorCode; -} - -ErrorCode OperationContext::finish(uint64_t operHandle, const std::vector& actualInput, sendDataToSE_cb cb) { - ErrorCode errorCode = ErrorCode::OK; - std::vector input; - /* Validate the input data */ - if(ErrorCode::OK != (errorCode = validateInputData(operHandle, Operation::Finish, actualInput, input))) { - return errorCode; - } - - if (input.size() > MAX_ALLOWED_INPUT_SIZE) { - int noOfChunks = input.size()/MAX_ALLOWED_INPUT_SIZE; - int extraData = input.size()%MAX_ALLOWED_INPUT_SIZE; - for(int i =0 ; i < noOfChunks; i++) { - auto first = input.cbegin() + (i*MAX_ALLOWED_INPUT_SIZE); - auto end = first + MAX_ALLOWED_INPUT_SIZE; - std::vector newInput(first, end); - if(extraData == 0 && (i == noOfChunks - 1)) { - //Last chunk - if(ErrorCode::OK != (errorCode = handleInternalUpdate(operHandle, newInput.data(), newInput.size(), - Operation::Finish, cb, true))) { - return errorCode; - } - - } else { - if(ErrorCode::OK != (errorCode = handleInternalUpdate(operHandle, newInput.data(), newInput.size(), - Operation::Update, cb))) { - return errorCode; - } - } - } - if(extraData > 0) { - std::vector finalInput(input.cend()-extraData, input.cend()); - if(ErrorCode::OK != (errorCode = handleInternalUpdate(operHandle, finalInput.data(), finalInput.size(), - Operation::Finish, cb, true))) { - return errorCode; - } - } - } else { - if(ErrorCode::OK != (errorCode = handleInternalUpdate(operHandle, input.data(), input.size(), - Operation::Finish, cb, true))) { - return errorCode; - } - } - return errorCode; -} - - /* - * This function is called for only Symmetric operations. It calculates the length of the data to be sent to the Applet - * by considering data from both of the sources i.e. buffered data and input data. Only block aligned length of data is - * sent to the Applet i.e. multiples of 16 for AES or multiples of 8 for DES/TDES. It first Copies the data to the out - * buffer from buffered data and then the remaining from the input data. If the buffered data is empty then it copies - * data to out buffer from only input and similarly if the input is empty then it copies from only buffer. Incase if - * only a portion of the input data is consumed then the remaining portion of input data is buffered. For AES/TDES - * Decryption operations with PKCS7 padding and for AES GCM operations a block size of data is always buffered. This is - * done to make sure that there will be always a block size of data left for finish operation so that the Applet may - * remove the PKCS7 padding if any or get the tag data for AES GCM operation for authentication purpose. Once the data - * from the buffer is consumed then the buffer is cleared. - */ -ErrorCode OperationContext::getBlockAlignedData(uint64_t operHandle, uint8_t* input, size_t input_len, - Operation opr, std::vector& out) { - size_t dataToSELen = 0;/*Length of the data to be send to the Applet.*/ - size_t inputConsumed = 0;/*Length of the data consumed from input */ - size_t blockSize = 0; - BufferedData& data = operationTable[operHandle].data; - int bufIndex = data.buf_len; - if(Algorithm::AES == operationTable[operHandle].info.alg) { - blockSize = AES_BLOCK_SIZE; - } else if(Algorithm::TRIPLE_DES == operationTable[operHandle].info.alg) { - blockSize = DES_BLOCK_SIZE; - } else { - return ErrorCode::INCOMPATIBLE_ALGORITHM; - } - - if(opr == Operation::Finish) { - //Copy the buffer to be send to SE. - for(int i = 0; i < data.buf_len; i++) - { - out.push_back(data.buf[i]); - } - dataToSELen = data.buf_len + input_len; - } else { - /*Update */ - //Calculate the block sized length on combined input of both buffered data and input data. - size_t blockAlignedLen = ((data.buf_len + input_len)/blockSize) * blockSize; - //For symmetric ciphers, decryption operation and PKCS7 padding mode or AES GCM operation save the last 16 bytes - //of block and send this block in finish operation. This is done to make sure that there will be always a 16 - //bytes of data left for finish operation so that javacard Applet may remove PKCS7 padding if any or get the tag - //data for AES GCM operation for authentication purpose. - if(((operationTable[operHandle].info.alg == Algorithm::AES) || - (operationTable[operHandle].info.alg == Algorithm::TRIPLE_DES)) && - (operationTable[operHandle].info.pad == PaddingMode::PKCS7 || - operationTable[operHandle].info.mode == BlockMode::GCM) && - (operationTable[operHandle].info.purpose == KeyPurpose::DECRYPT)) { - if(blockAlignedLen >= blockSize) blockAlignedLen -= blockSize; - } - //Copy data to be send to SE from buffer, only if atleast a minimum block aligned size is available. - if(blockAlignedLen >= blockSize) { - for(size_t pos = 0; pos < std::min(blockAlignedLen, data.buf_len); pos++) { - out.push_back(data.buf[pos]); - } - } - dataToSELen = blockAlignedLen; - } - - if(dataToSELen > 0) { - //If buffer length is greater than the data length to be send to SE, then input data consumed is 0. - //That means all the data to be send to SE is consumed from the buffer. - //The buffer length might become greater than dataToSELen in the cases where we are saving the last block of - //data i.e. AES/TDES Decryption with PKC7Padding or AES GCM Decryption operations. - inputConsumed = (data.buf_len > dataToSELen) ? 0 : (dataToSELen - data.buf_len); - - //Copy the buffer to be send to SE. - for(int i = 0; i < inputConsumed; i++) - { - out.push_back(input[i]); - } - - if(data.buf_len > dataToSELen) { - //Only blockAlignedLen data is consumed from buffer so reorder the buffer data. - memcpy(data.buf, data.buf+dataToSELen, data.buf_len-dataToSELen); - memset(data.buf+dataToSELen, 0x00, data.buf_len-dataToSELen); - data.buf_len -= dataToSELen; - bufIndex = data.buf_len; - } else { - // All the data is consumed so clear buffer - if(data.buf_len != 0) { - memset(data.buf, 0x00, sizeof(data.buf)); - bufIndex = data.buf_len = 0; - } - } - } - - //Store the remaining buffer for later use. - data.buf_len += (input_len - inputConsumed); - for(int i = 0; i < (input_len - inputConsumed); i++) - { - data.buf[bufIndex+i] = input[inputConsumed+i]; - } - return ErrorCode::OK; -} - -ErrorCode OperationContext::handleInternalUpdate(uint64_t operHandle, uint8_t* data, size_t len, Operation opr, - sendDataToSE_cb cb, bool finish) { - ErrorCode errorCode = ErrorCode::OK; - std::vector out; - if(Algorithm::AES == operationTable[operHandle].info.alg || - Algorithm::TRIPLE_DES == operationTable[operHandle].info.alg) { - /*Symmetric */ - if(ErrorCode::OK != (errorCode = getBlockAlignedData(operHandle, data, len, - opr, out))) { - return errorCode; - } - //Call the callback under these condition - //1. if it is a finish operation. - //2. if there is some data to be send to Javacard.(either update or finish operation). - //3. if the operation is GCM Mode. Even though there is no data to be send there could be AAD data to be sent to - //javacard. - if(finish || out.size() > 0 || BlockMode::GCM == operationTable[operHandle].info.mode) { - if(ErrorCode::OK != (errorCode = cb(out, finish))) { - return errorCode; - } - } - } else { - /* Asymmetric */ - if(operationTable[operHandle].info.purpose == KeyPurpose::DECRYPT || - operationTable[operHandle].info.digest == Digest::NONE) { - //In case of Decrypt operation or Sign operation with no digest case, buffer the data in - //update call and send it to SE in finish call. - if(finish) { - //If finish flag is true all the data has to be sent to javacard. - size_t i = 0; - for(; i < operationTable[operHandle].data.buf_len; ++i) { - out.push_back(operationTable[operHandle].data.buf[i]); - } - for(i = 0; i < len; ++i) { - out.push_back(data[i]); - } - //As buffered data is already consumed earse the buffer. - if(operationTable[operHandle].data.buf_len != 0) { - memset(operationTable[operHandle].data.buf, 0x00, sizeof(operationTable[operHandle].data.buf)); - operationTable[operHandle].data.buf_len = 0; - } - if(ErrorCode::OK != (errorCode = cb(out, finish))) { - return errorCode; - } - } else { - //For strongbox keymaster, in NoDigest case the length of the input message for RSA should not be more than - //256 and for EC it should not be more than 32. This validation is already happening in - //validateInputData function. - size_t bufIndex = operationTable[operHandle].data.buf_len; - size_t pos = 0; - for(; pos < len; ++pos) - { - operationTable[operHandle].data.buf[bufIndex+pos] = data[pos]; - } - operationTable[operHandle].data.buf_len += pos; - } - } else { /* With Digest */ - for(size_t j=0; j < len; ++j) - { - out.push_back(data[j]); - } - //if len=0, then no need to call the callback, since there is no information to be send to javacard, - // but if finish flag is true irrespective of length the callback should be called. - if(len != 0 || finish) { - if(ErrorCode::OK != (errorCode = cb(out, finish))) { - return errorCode; - } - } - } - } - return errorCode; -} - - -} // namespace javacard -} // namespace V4_1 -} // namespace keymaster diff --git a/HAL/keymaster/4.1/JavacardSoftKeymasterContext.cpp b/HAL/keymaster/4.1/JavacardSoftKeymasterContext.cpp deleted file mode 100644 index 8f9905af..00000000 --- a/HAL/keymaster/4.1/JavacardSoftKeymasterContext.cpp +++ /dev/null @@ -1,204 +0,0 @@ -/* - * Copyright 2015 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 -#include - -using std::unique_ptr; -using ::keymaster::V4_1::javacard::KmParamSet; - -namespace keymaster { - -JavaCardSoftKeymasterContext::JavaCardSoftKeymasterContext(keymaster_security_level_t security_level) - : PureSoftKeymasterContext(security_level) {} - -JavaCardSoftKeymasterContext::~JavaCardSoftKeymasterContext() {} - -EVP_PKEY* RSA_fromMaterial(const uint8_t* modulus, size_t mod_size) { - BIGNUM *n = BN_bin2bn(modulus, mod_size, NULL); - BIGNUM *e = BN_new();//bignum_decode(exp, 5); - char exp[] = "65537"; - BN_dec2bn(&e, exp); - - if (!n || !e) - return NULL; - - if (e && n) { - EVP_PKEY* pRsaKey = EVP_PKEY_new(); - RSA* rsa = RSA_new(); - rsa->e = e; - rsa->n = n; - EVP_PKEY_assign_RSA(pRsaKey, rsa); - return pRsaKey; - } else { - if (n) BN_free(n); - if (e) BN_free(e); - return NULL; - } -} - -EC_GROUP* ChooseGroup(keymaster_ec_curve_t ec_curve) { - switch (ec_curve) { - case KM_EC_CURVE_P_224: - return EC_GROUP_new_by_curve_name(NID_secp224r1); - break; - case KM_EC_CURVE_P_256: - return EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1); - break; - case KM_EC_CURVE_P_384: - return EC_GROUP_new_by_curve_name(NID_secp384r1); - break; - case KM_EC_CURVE_P_521: - return EC_GROUP_new_by_curve_name(NID_secp521r1); - break; - default: - return nullptr; - break; - } -} - -EVP_PKEY* EC_fromMaterial(const uint8_t* pub_key, size_t key_size, keymaster_ec_curve_t ec_curve) { - - EC_GROUP *ec_group = ChooseGroup(ec_curve); - EC_POINT *p = EC_POINT_new(ec_group); - EC_KEY *ec_key = EC_KEY_new(); - EVP_PKEY *pEcKey = EVP_PKEY_new(); - - if((EC_KEY_set_group(ec_key, ec_group) != 1) || (EC_POINT_oct2point(ec_group, p, pub_key, key_size, NULL) != 1) - || (EC_KEY_set_public_key(ec_key, p) != 1) || (EVP_PKEY_set1_EC_KEY(pEcKey, ec_key) != 1)) { - return NULL; - } - - return pEcKey; -} - -keymaster_error_t JavaCardSoftKeymasterContext::LoadKey(const keymaster_algorithm_t algorithm, KeymasterKeyBlob&& key_material, - AuthorizationSet&& hw_enforced, - AuthorizationSet&& sw_enforced, - UniquePtr* key) const { - auto factory = (AsymmetricKeyFactory*)GetKeyFactory(algorithm); - UniquePtr asym_key; - keymaster_error_t error = KM_ERROR_OK; - const uint8_t* tmp = key_material.key_material; - const size_t temp_size = key_material.key_material_size; - EVP_PKEY* pkey = NULL; - - if(algorithm == KM_ALGORITHM_RSA) { - pkey = RSA_fromMaterial(tmp, temp_size); - } else if(algorithm == KM_ALGORITHM_EC) { - keymaster_ec_curve_t ec_curve; - uint32_t keySize; - if (!hw_enforced.GetTagValue(TAG_EC_CURVE, &ec_curve) && - !sw_enforced.GetTagValue(TAG_EC_CURVE, &ec_curve)) { - if(!hw_enforced.GetTagValue(TAG_KEY_SIZE, &keySize) && - !sw_enforced.GetTagValue(TAG_KEY_SIZE, &keySize)) { - return KM_ERROR_INVALID_ARGUMENT; - } - error = EcKeySizeToCurve(keySize, &ec_curve); - if(error != KM_ERROR_OK) - return error; - } - pkey = EC_fromMaterial(tmp, temp_size, ec_curve); - } - if (!pkey) - return TranslateLastOpenSslError(); - UniquePtr pkey_deleter(pkey); - - error = factory->CreateEmptyKey(move(hw_enforced), move(sw_enforced), &asym_key); - if (error != KM_ERROR_OK) - return error; - - asym_key->key_material() = move(key_material); - if (!asym_key->EvpToInternal(pkey)) - error = TranslateLastOpenSslError(); - else - key->reset(asym_key.release()); - - return error; -} - -keymaster_error_t JavaCardSoftKeymasterContext::ParseKeyBlob(const KeymasterKeyBlob& blob, - const AuthorizationSet& /*additional_params*/, - UniquePtr* key) const { - - // The JavaCardSoftKeymasterContext handle a key blob generated by JavaCard keymaster for public key operations. - // - // 1. A JavaCard keymaster key blob is a CborEncoded data of Secret, Nonce, AuthTag, KeyCharectristics and Public key. - // Here in public key operation we need only KeyCharectristics and Public key. - // Once these values extracted Public key is created based on parameters and returned. - // - - AuthorizationSet hw_enforced; - AuthorizationSet sw_enforced; - KeymasterKeyBlob key_material; - keymaster_error_t error = KM_ERROR_OK; - - auto constructKey = [&, this] () mutable -> keymaster_error_t { - keymaster_algorithm_t algorithm; - if(error != KM_ERROR_OK) { - return error; - } - if (!hw_enforced.GetTagValue(TAG_ALGORITHM, &algorithm) && - !sw_enforced.GetTagValue(TAG_ALGORITHM, &algorithm)) { - return KM_ERROR_INVALID_ARGUMENT; - } - - if (algorithm != KM_ALGORITHM_RSA && algorithm != KM_ALGORITHM_EC) { - return KM_ERROR_INCOMPATIBLE_ALGORITHM; - } - error = LoadKey(algorithm, move(key_material), move(hw_enforced), - move(sw_enforced), key); - return error; - }; - - CborConverter cc; - std::unique_ptr item; - ErrorCode errorCode = ErrorCode::UNKNOWN_ERROR; - std::vector cborKey(blob.key_material_size); - - for(size_t i = 0; i < blob.key_material_size; i++) { - cborKey[i] = blob.key_material[i]; - } - std::tie(item, errorCode) = cc.decodeData(cborKey, false); - if (item != nullptr) { - std::vector temp(0); - if(cc.getBinaryArray(item, 4, temp)) { - key_material = {temp.data(), temp.size()}; - temp.clear(); - } - KeyCharacteristics keyCharacteristics; - cc.getKeyCharacteristics(item, 3, keyCharacteristics); - - sw_enforced.Reinitialize(KmParamSet(keyCharacteristics.softwareEnforced)); - hw_enforced.Reinitialize(KmParamSet(keyCharacteristics.hardwareEnforced)); - } else { - error = KM_ERROR_INVALID_KEY_BLOB; - } - return constructKey(); -} -} // namespace keymaster diff --git a/HAL/keymaster/4.1/OmapiTransport.cpp b/HAL/keymaster/4.1/OmapiTransport.cpp deleted file mode 100644 index 5aaefc91..00000000 --- a/HAL/keymaster/4.1/OmapiTransport.cpp +++ /dev/null @@ -1,49 +0,0 @@ -/* - ** - ** 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 "Transport.h" - -#define PORT 8080 -#define IPADDR "10.9.40.24" -#define UNUSED_V(a) a=a - -namespace se_transport { - -bool OmapiTransport::openConnection() { - return true; -} - -bool OmapiTransport::sendData(const uint8_t* inData, const size_t inLen, std::vector& output) { - std::vector test(inData, inData+inLen); - output = std::move(test); - return true; -} - -bool OmapiTransport::closeConnection() { - return true; -} - -bool OmapiTransport::isConnected() { - return true; -} - -} diff --git a/HAL/keymaster/4.1/android.hardware.keymaster@4.1-javacard.service.rc b/HAL/keymaster/4.1/android.hardware.keymaster@4.1-javacard.service.rc deleted file mode 100644 index 556ffd1d..00000000 --- a/HAL/keymaster/4.1/android.hardware.keymaster@4.1-javacard.service.rc +++ /dev/null @@ -1,6 +0,0 @@ -service android.hardware.keymaster@4.1-javacard.service /vendor/bin/hw/android.hardware.keymaster@4.1-javacard.service - interface android.hardware.keymaster@4.0::IKeymasterDevice default - interface android.hardware.keymaster@4.1::IKeymasterDevice default - class early_hal - user system - group system drmrpc diff --git a/HAL/keymaster/4.1/android.hardware.keymaster@4.1-javacard.service.xml b/HAL/keymaster/4.1/android.hardware.keymaster@4.1-javacard.service.xml deleted file mode 100644 index 83fccabe..00000000 --- a/HAL/keymaster/4.1/android.hardware.keymaster@4.1-javacard.service.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - android.hardware.keymaster - hwbinder - @4.1::IKeymasterDevice/javacard - - diff --git a/HAL/keymaster/4.1/service.cpp b/HAL/keymaster/4.1/service.cpp deleted file mode 100644 index 75ed2510..00000000 --- a/HAL/keymaster/4.1/service.cpp +++ /dev/null @@ -1,35 +0,0 @@ -/* -** -** 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 - -int main() { - ::android::hardware::configureRpcThreadpool(1, true); - auto keymaster = new ::keymaster::V4_1::javacard::JavacardKeymaster4Device(); - - auto status = keymaster->registerAsService("javacard"); - if (status != android::OK) { - LOG(FATAL) << "Could not register service for Keymaster 4.1 (" << status << ")"; - return -1; - } - - android::hardware::joinRpcThreadpool(); - return -1; // Should never get here. -} diff --git a/HAL/keymaster/Android.bp b/HAL/keymaster/Android.bp deleted file mode 100644 index 92e3be8f..00000000 --- a/HAL/keymaster/Android.bp +++ /dev/null @@ -1,142 +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. -// - - -cc_binary { - name: "android.hardware.keymaster@4.1-javacard.service", - relative_install_path: "hw", - vendor: true, - init_rc: ["4.1/android.hardware.keymaster@4.1-javacard.service.rc"], - vintf_fragments: ["4.1/android.hardware.keymaster@4.1-javacard.service.xml"], - srcs: [ - "4.1/service.cpp", - "4.1/JavacardKeymaster4Device.cpp", - "4.1/JavacardSoftKeymasterContext.cpp", - "4.1/JavacardOperationContext.cpp", - ], - local_include_dirs: [ - "include", - ], - shared_libs: [ - "liblog", - "libcutils", - "libdl", - "libbase", - "libutils", - "libhardware", - "libhidlbase", - "libsoftkeymasterdevice", - "libkeymaster_messages", - "libkeymaster_portable", - "libcppbor", - "android.hardware.keymaster@4.1", - "android.hardware.keymaster@4.0", - "libjc_transport", - "libjc_common", - "libcrypto", - ], - arch: { - x86_64: { - cflags: ["-DVTS_EMULATOR"], - }, - x86: { - cflags: ["-DVTS_EMULATOR"], - }, - }, -} - -cc_library { - name: "libJavacardKeymaster41", - srcs: [ - "4.1/JavacardKeymaster4Device.cpp", - "4.1/CborConverter.cpp", - "4.1/JavacardSoftKeymasterContext.cpp", - "4.1/JavacardOperationContext.cpp", - "4.1/CommonUtils.cpp", - ], - local_include_dirs: [ - "include", - ], - shared_libs: [ - "liblog", - "libcutils", - "libdl", - "libbase", - "libutils", - "libhardware", - "libhidlbase", - "libsoftkeymasterdevice", - "libkeymaster_messages", - "libkeymaster_portable", - "libcppbor", - "android.hardware.keymaster@4.1", - "android.hardware.keymaster@4.0", - "libjc_transport", - "libcrypto", - ], -} - -cc_library { - name: "libjc_transport", - host_supported: true, - vendor_available: true, - vndk: { - enabled: true, - }, - - srcs: [ - "4.1/SocketTransport.cpp", - "4.1/OmapiTransport.cpp" - ], - export_include_dirs: [ - "include" - ], - shared_libs: [ - "libbinder", - "libbase", - "liblog", - ], -} - -cc_library { - name: "libjc_common", - vendor_available: true, - srcs: [ - "4.1/CommonUtils.cpp", - "4.1/CborConverter.cpp", - ], - local_include_dirs: [ - "include", - ], - export_include_dirs: [ - "include" - ], - shared_libs: [ - "liblog", - "libcutils", - "libdl", - "libbase", - "libutils", - "libhardware", - "libhidlbase", - "libsoftkeymasterdevice", - "libkeymaster_messages", - "libkeymaster_portable", - "libcppbor", - "android.hardware.keymaster@4.1", - "android.hardware.keymaster@4.0", - "libcrypto", - ], -} diff --git a/HAL/keymaster/include/CborConverter.h b/HAL/keymaster/include/CborConverter.h deleted file mode 100644 index 45855244..00000000 --- a/HAL/keymaster/include/CborConverter.h +++ /dev/null @@ -1,231 +0,0 @@ -/* - ** - ** 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 __CBOR_CONVERTER_H_ -#define __CBOR_CONVERTER_H_ - -#include -#include -#include -#include -#include -#include - -using namespace cppbor; - -using ::android::hardware::hidl_vec; -using ::android::hardware::keymaster::V4_0::ErrorCode; -using ::android::hardware::keymaster::V4_0::HardwareAuthToken; -using ::android::hardware::keymaster::V4_0::HardwareAuthenticatorType; -using ::android::hardware::keymaster::V4_0::HmacSharingParameters; -using ::android::hardware::keymaster::V4_0::KeyParameter; -using ::android::hardware::keymaster::V4_0::VerificationToken; -using ::android::hardware::keymaster::V4_0::KeyCharacteristics; -using ::android::hardware::keymaster::V4_0::SecurityLevel; -using ::android::hardware::keymaster::V4_0::TagType; -using ::android::hardware::keymaster::V4_0::Tag; - -class CborConverter -{ - public: - CborConverter() = default; - ~CborConverter() = default; - - /** - * Parses the input data which is in CBOR format and returns a Tuple of Item pointer and the first element in the item pointer. - */ - template - std::tuple, T> decodeData(const std::vector& response, bool - hasErrorCode) { - const uint8_t* pos; - std::unique_ptr item(nullptr); - std::string message; - T errorCode = T::OK; - - std::tie(item, pos, message) = parse(response); - - if(item != nullptr && hasErrorCode) { - if(MajorType::ARRAY == getType(item)) { - if(!getErrorCode(item, 0, errorCode)) - item = nullptr; - } else if (MajorType::UINT == getType(item)) { - uint64_t err; - if(getUint64(item, err)) { - errorCode = static_cast(err); - } - item = nullptr; /*Already read the errorCode. So no need of sending item to client */ - } - } - return {std::move(item), errorCode}; - } - - /** - * Get the signed/unsigned integer value at a given position from the item pointer. - */ - template - bool getUint64(const std::unique_ptr& item, const uint32_t pos, T& value); - - /** - * Get the signed/unsigned integer value from the item pointer. - */ - template - bool getUint64(const std::unique_ptr& item, T& value); - - /** - * Get the HmacSharingParameters structure value at the given position from the item pointer. - */ - bool getHmacSharingParameters(const std::unique_ptr& item, const uint32_t pos, HmacSharingParameters& params); - - /** - * Get the Binary string at the given position from the item pointer. - */ - bool getBinaryArray(const std::unique_ptr& item, const uint32_t pos, ::android::hardware::hidl_string& value); - - /** - * Get the Binary string at the given position from the item pointer. - */ - bool getBinaryArray(const std::unique_ptr& item, const uint32_t pos, std::vector& value); - - /** - * Get the Binary string at the given position from the item pointer. - */ - bool getBinaryArray(const std::unique_ptr& item, const uint32_t pos, - ::android::hardware::hidl_vec& value); - /** - * Get the HardwareAuthToken value at the given position from the item pointer. - */ - bool getHardwareAuthToken(const std::unique_ptr& item, const uint32_t pos, HardwareAuthToken& authType); - - /** - * Get the list of KeyParameters value at the given position from the item pointer. - */ - bool getKeyParameters(const std::unique_ptr& item, const uint32_t pos, android::hardware::hidl_vec& keyParams); - - /** - * Adds the the list of KeyParameters values to the Array item. - */ - bool addKeyparameters(Array& array, const android::hardware::hidl_vec& - keyParams); - - /** - * Add HardwareAuthToken value to the Array item. - */ - bool addHardwareAuthToken(Array& array, const HardwareAuthToken& - authToken); - - /** - * Get the VerificationToken value at the given position from the item pointer. - */ - bool getVerificationToken(const std::unique_ptr& item, const uint32_t pos, VerificationToken& - token); - - /** - * Get the KeyCharacteristics value at the given position from the item pointer. - */ - bool getKeyCharacteristics(const std::unique_ptr &item, const uint32_t pos, - KeyCharacteristics& keyCharacteristics); - - /** - * Get the list of binary arrays at the given position from the item pointer. - */ - bool getMultiBinaryArray(const std::unique_ptr& item, const uint32_t pos, - std::vector>& data); - - /** - * Add VerificationToken value to the Array item. - */ - bool addVerificationToken(Array& array, const VerificationToken& - verificationToken, std::vector& encodedParamsVerified); - - /** - * Get the ErrorCode value at the give position from the item pointer. - */ - template) || - (std::is_same_v)>> - inline bool getErrorCode(const std::unique_ptr& item, const uint32_t pos, T& errorCode) { - bool ret = false; - uint64_t errorVal; - if (!getUint64(item, pos, errorVal)) { - return ret; - } - errorCode = static_cast(errorVal); - - ret = true; - return ret; - } - - private: - /** - * Get the type of the Item pointer. - */ - inline MajorType getType(const std::unique_ptr &item) { return item.get()->type(); } - - /** - * Construct Keyparameter structure from the pair of key and value. If TagType is ENUM_REP the value contains - * binary string. If TagType is UINT_REP or ULONG_REP the value contains Array of unsigned integers. - */ - bool getKeyParameter(const std::pair&, - const std::unique_ptr&> pair, std::vector& keyParam); - - /** - * Get the sub item pointer from the root item pointer at the given position. - */ - inline void getItemAtPos(const std::unique_ptr& item, const uint32_t pos, std::unique_ptr& subItem) { - Array* arr = nullptr; - - if (MajorType::ARRAY != getType(item)) { - return; - } - arr = const_cast(item.get()->asArray()); - if (arr->size() < (pos + 1)) { - return; - } - subItem = std::move((*arr)[pos]); - } -}; - -template -bool CborConverter::getUint64(const std::unique_ptr& item, T& value) { - bool ret = false; - if ((item == nullptr) || - (std::is_unsigned::value && (MajorType::UINT != getType(item))) || - ((std::is_signed::value && (MajorType::NINT != getType(item))))) { - return ret; - } - - if (std::is_unsigned::value) { - const Uint* uintVal = item.get()->asUint(); - value = uintVal->value(); - } - else { - const Nint* nintVal = item.get()->asNint(); - value = nintVal->value(); - } - ret = true; - return ret; //success -} - -template -bool CborConverter::getUint64(const std::unique_ptr& item, const uint32_t pos, T& value) { - std::unique_ptr intItem(nullptr); - getItemAtPos(item, pos, intItem); - return getUint64(intItem, value); -} - - - -#endif diff --git a/HAL/keymaster/include/CommonUtils.h b/HAL/keymaster/include/CommonUtils.h deleted file mode 100644 index b6612741..00000000 --- a/HAL/keymaster/include/CommonUtils.h +++ /dev/null @@ -1,108 +0,0 @@ -/* - ** - ** 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_COMMONUTILS_H_ -#define KEYMASTER_V4_1_JAVACARD_COMMONUTILS_H_ - -#include -#include -#include - -namespace keymaster { -namespace V4_1 { -namespace javacard { -using ::android::hardware::hidl_vec; -using ::android::hardware::keymaster::V4_0::ErrorCode; -using ::android::hardware::keymaster::V4_0::Tag; -using ::android::hardware::keymaster::V4_0::KeyFormat; -using ::android::hardware::keymaster::V4_0::KeyParameter; -using ::android::hardware::keymaster::V4_0::KeyPurpose; -using ::android::hardware::keymaster::V4_0::EcCurve; - -inline ErrorCode legacy_enum_conversion(const keymaster_error_t value) { - return static_cast(value); -} - -inline keymaster_purpose_t legacy_enum_conversion(const KeyPurpose value) { - return static_cast(value); -} - -inline keymaster_key_format_t legacy_enum_conversion(const KeyFormat value) { - return static_cast(value); -} - -inline keymaster_tag_t legacy_enum_conversion(const Tag value) { - return keymaster_tag_t(value); -} - -inline Tag legacy_enum_conversion(const keymaster_tag_t value) { - return Tag(value); -} - -inline keymaster_tag_type_t typeFromTag(const keymaster_tag_t tag) { - return keymaster_tag_get_type(tag); -} - -inline hidl_vec kmBuffer2hidlVec(const ::keymaster::Buffer& buf) { - hidl_vec result; - result.setToExternal(const_cast(buf.peek_read()), buf.available_read()); - return result; -} - -inline void blob2Vec(const uint8_t *from, size_t size, std::vector& to) { - for(int i = 0; i < size; ++i) { - to.push_back(from[i]); - } -} - -inline hidl_vec kmBlob2hidlVec(const keymaster_blob_t& blob) { - hidl_vec result; - result.setToExternal(const_cast(blob.data), blob.data_length); - return result; -} - -keymaster_key_param_set_t hidlKeyParams2Km(const hidl_vec& keyParams); - -hidl_vec kmParamSet2Hidl(const keymaster_key_param_set_t& set); - -ErrorCode rsaRawKeyFromPKCS8(const std::vector& pkcs8Blob, std::vector& privateExp, std::vector& -pubModulus); - -ErrorCode ecRawKeyFromPKCS8(const std::vector& pkcs8Blob, std::vector& secret, std::vector& -publicKey, EcCurve& eccurve); - -ErrorCode getCertificateChain(std::vector& chainBuffer, std::vector>& certChain); - -uint32_t GetVendorPatchlevel(); - -class KmParamSet : public keymaster_key_param_set_t { - public: - explicit KmParamSet(const hidl_vec& keyParams) - : keymaster_key_param_set_t(hidlKeyParams2Km(keyParams)) {} - KmParamSet(KmParamSet&& other) : keymaster_key_param_set_t{other.params, other.length} { - other.length = 0; - other.params = nullptr; - } - KmParamSet(const KmParamSet&) = delete; - ~KmParamSet() { delete[] params; } -}; - -} // namespace javacard -} // namespace V4_1 -} // namespace keymaster -#endif //KEYMASTER_V4_1_JAVACARD_COMMONUTILS_H_ diff --git a/HAL/keymaster/include/JavacardKeymaster4Device.h b/HAL/keymaster/include/JavacardKeymaster4Device.h deleted file mode 100644 index c8ada383..00000000 --- a/HAL/keymaster/include/JavacardKeymaster4Device.h +++ /dev/null @@ -1,101 +0,0 @@ -/* - ** - ** 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_JAVACARDKEYMASTER4DEVICE_H_ -#define KEYMASTER_V4_1_JAVACARD_JAVACARDKEYMASTER4DEVICE_H_ - -#include -#include -#include -#include -#include "CborConverter.h" -#include "TransportFactory.h" -#include -#include -#include -#include - -namespace keymaster { -namespace V4_1 { -namespace javacard { - -using ::android::hardware::hidl_vec; -using ::android::hardware::hidl_string; -using ::android::hardware::Return; -using ::android::hardware::Void; - -using ::android::hardware::keymaster::V4_0::ErrorCode; -using ::android::hardware::keymaster::V4_0::HardwareAuthenticatorType; -using ::android::hardware::keymaster::V4_0::HardwareAuthToken; -using ::android::hardware::keymaster::V4_0::HmacSharingParameters; -using ::android::hardware::keymaster::V4_0::KeyCharacteristics; -using ::android::hardware::keymaster::V4_0::KeyFormat; -using ::android::hardware::keymaster::V4_0::KeyParameter; -using ::android::hardware::keymaster::V4_0::KeyPurpose; -using ::android::hardware::keymaster::V4_0::OperationHandle; -using ::android::hardware::keymaster::V4_0::SecurityLevel; -using ::android::hardware::keymaster::V4_0::VerificationToken; -using ::android::hardware::keymaster::V4_1::IKeymasterDevice; -using ::android::hardware::keymaster::V4_0::Tag; - -using V41ErrorCode = ::android::hardware::keymaster::V4_1::ErrorCode; - -class JavacardKeymaster4Device : public IKeymasterDevice { - public: - - JavacardKeymaster4Device(); - virtual ~JavacardKeymaster4Device(); - - // Methods from ::android::hardware::keymaster::V4_0::IKeymasterDevice follow. - Return getHardwareInfo(getHardwareInfo_cb _hidl_cb) override; - Return getHmacSharingParameters(getHmacSharingParameters_cb _hidl_cb) override; - Return computeSharedHmac(const hidl_vec& params, computeSharedHmac_cb _hidl_cb) override; - Return verifyAuthorization(uint64_t operationHandle, const hidl_vec& parametersToVerify, const HardwareAuthToken& authToken, verifyAuthorization_cb _hidl_cb) override; - Return addRngEntropy(const hidl_vec& data) override; - Return generateKey(const hidl_vec& keyParams, generateKey_cb _hidl_cb) override; - Return importKey(const hidl_vec& keyParams, KeyFormat keyFormat, const hidl_vec& keyData, importKey_cb _hidl_cb) override; - Return importWrappedKey(const hidl_vec& wrappedKeyData, const hidl_vec& wrappingKeyBlob, const hidl_vec& maskingKey, const hidl_vec& unwrappingParams, uint64_t passwordSid, uint64_t biometricSid, importWrappedKey_cb _hidl_cb) override; - Return getKeyCharacteristics(const hidl_vec& keyBlob, const hidl_vec& clientId, const hidl_vec& appData, getKeyCharacteristics_cb _hidl_cb) override; - Return exportKey(KeyFormat keyFormat, const hidl_vec& keyBlob, const hidl_vec& clientId, const hidl_vec& appData, exportKey_cb _hidl_cb) override; - Return attestKey(const hidl_vec& keyToAttest, const hidl_vec& attestParams, attestKey_cb _hidl_cb) override; - Return upgradeKey(const hidl_vec& keyBlobToUpgrade, const hidl_vec& upgradeParams, upgradeKey_cb _hidl_cb) override; - Return deleteKey(const hidl_vec& keyBlob) override; - Return deleteAllKeys() override; - Return destroyAttestationIds() override; - Return begin(KeyPurpose purpose, const hidl_vec& keyBlob, const hidl_vec& inParams, const HardwareAuthToken& authToken, begin_cb _hidl_cb) override; - Return update(uint64_t operationHandle, const hidl_vec& inParams, const hidl_vec& input, const HardwareAuthToken& authToken, const VerificationToken& verificationToken, update_cb _hidl_cb) override; - Return finish(uint64_t operationHandle, const hidl_vec& inParams, const hidl_vec& input, const hidl_vec& signature, const HardwareAuthToken& authToken, const VerificationToken& verificationToken, finish_cb _hidl_cb) override; - Return abort(uint64_t operationHandle) override; - - // Methods from ::android::hardware::keymaster::V4_1::IKeymasterDevice follow. - Return deviceLocked(bool passwordOnly, const VerificationToken& verificationToken) override; - Return earlyBootEnded() override; - -protected: - CborConverter cborConverter_; - -private: - std::unique_ptr<::keymaster::AndroidKeymaster> softKm_; - std::unique_ptr oprCtx_; - bool isEachSystemPropertySet; -}; - -} // namespace javacard -} // namespace V4_1 -} // namespace keymaster - -#endif // KEYMASTER_V4_1_JAVACARD_JAVACARDKEYMASTER4DEVICE_H_ diff --git a/HAL/keymaster/include/JavacardOperationContext.h b/HAL/keymaster/include/JavacardOperationContext.h deleted file mode 100644 index 53d08dd9..00000000 --- a/HAL/keymaster/include/JavacardOperationContext.h +++ /dev/null @@ -1,154 +0,0 @@ -/* - ** - ** 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_OPERATIONCONTEXT_H_ -#define KEYMASTER_V4_1_JAVACARD_OPERATIONCONTEXT_H_ - -#include -#include - -#define MAX_BUF_SIZE 256 - -namespace keymaster { -namespace V4_1 { -namespace javacard { - -using ::android::hardware::hidl_vec; -using ::android::hardware::keymaster::V4_0::ErrorCode; -using ::android::hardware::keymaster::V4_0::Algorithm; -using ::android::hardware::keymaster::V4_0::KeyPurpose; -using ::android::hardware::keymaster::V4_0::Digest; -using ::android::hardware::keymaster::V4_0::PaddingMode; -using ::android::hardware::keymaster::V4_0::KeyParameter; -using ::android::hardware::keymaster::V4_0::BlockMode; -using ::android::hardware::keymaster::V4_0::Tag; - -/** - * Callback function to send data back to the caller. - */ -using sendDataToSE_cb = std::function& data, bool finish)>; - -enum class Operation; - -/** - * This struct is used to store the buffered data. - */ -struct BufferedData { - uint8_t buf[MAX_BUF_SIZE]; - size_t buf_len; -}; - -/** - * This struct is used to store the operation info. - */ -struct OperationInfo { - Algorithm alg; - KeyPurpose purpose; - Digest digest; - PaddingMode pad; - BlockMode mode; -}; - -/** - * OperationContext uses this struct to store the buffered data and the correspoding operation info. - */ -struct OperationData { - OperationInfo info; - BufferedData data; -}; - -/** - * This class manages the data that is send for any crypto operation. - * - * For Symmetric operations, update function sends only block aligned data and stores the remaining data in the buffer - * so at any point the buffer may contain data ranging from 0 to a maximum of block size, where as finish function sends - * all the data (input data + buffered data) to the caller and clears the buffer. To support PKCS#7 padding removal, - * the last block size from the input is always buffered in update operation and this last block is sent in finish - * operation. - * - * For Asymmetric operations, if the operation is with Digest then the input data is not buffered, where as if the - * operation is with no Digest then update function buffers the input data and finish function extracts the data from - * buffer and sends to the caller. Update and finish functions does validation on the input data based on the algorithm. - * - * In General, the maximum allowed input data that is sent is limited to MAX_ALLOWED_INPUT_SIZE. If the input data - * exceeds this limit each update or finish function divides the input data into chunks of MAX_ALLOWED_INPUT_SIZE and - * sends each chunk back to the caller through update callback. - */ -class OperationContext { - -public: - OperationContext(){} - ~OperationContext() {} - /** - * In Begin operation caller has to call this function to store the operation data corresponding to the operation - * handle. - */ - ErrorCode setOperationInfo(uint64_t operationHandle, KeyPurpose purpose, Algorithm alg, const hidl_vec& params); - /** - * This function clears the operation data from the map. Caller has to call this function once the operation is done - * or if there is any error while processing the operation. - */ - ErrorCode clearOperationData(uint64_t operationHandle); - /** - * This function validaes the input data based on the algorithm and does process on the data to either store it or - * send back to the caller. The data is sent using sendDataTOSE_cb callback. - */ - ErrorCode update(uint64_t operHandle, const std::vector& input, sendDataToSE_cb cb); - /** - * This function validaes the input data based on the algorithm and send all the input data along with buffered data - * to the caller. The data is sent using sendDataTOSE_cb callback. - */ - ErrorCode finish(uint64_t operHandle, const std::vector& input, sendDataToSE_cb cb); - -private: - /** - * This is used to store the operation related info and the buffered data. Key is the operation handle and the value - * is OperationData. - */ - std::map operationTable; - - /* Helper functions */ - - /** - * This fucntion validates the input data based on the algorithm and the operation info parameters. This function - * also does a processing on the input data if either the algorithm is EC or if it is a Finish operation. For EC - * operations it truncates the input data if it exceeds 32 bytes for No Digest case. In case of finish operations - * this function combines both the buffered data and input data if both exceeds MAX_ALLOWED_INPUT_SIZE. - */ - ErrorCode validateInputData(uint64_t operHandle, Operation opr, const std::vector& actualInput, - std::vector& input); - /** - * This function is used for Symmetric operations. It extracts the block sized data from the input and buffers the - * reamining data for update calls only. For finish calls it extracts all the buffered data combines it with - * input data. - */ - ErrorCode getBlockAlignedData(uint64_t operHandle, uint8_t* input, size_t input_len, Operation opr, std::vector& - out); - /** - * This function sends the data back to the caller using callback functions. It does some processing on input data - * for Asymmetic operations. - */ - ErrorCode handleInternalUpdate(uint64_t operHandle, uint8_t* data, size_t len, Operation opr, - sendDataToSE_cb cb, bool finish=false); - -}; - -} // namespace javacard -} // namespace V4_1 -} // namespace keymaster - -#endif // KEYMASTER_V4_1_JAVACARD_OPERATIONCONTEXT_H_ diff --git a/HAL/keymaster/include/JavacardSoftKeymasterContext.h b/HAL/keymaster/include/JavacardSoftKeymasterContext.h deleted file mode 100644 index 0fa2d711..00000000 --- a/HAL/keymaster/include/JavacardSoftKeymasterContext.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2015 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 SYSTEM_KEYMASTER_JAVA_CARD_SOFT_KEYMASTER_CONTEXT_H_ -#define SYSTEM_KEYMASTER_JAVA_CARD_SOFT_KEYMASTER_CONTEXT_H_ - -#include - -namespace keymaster { - -class SoftKeymasterKeyRegistrations; -class Keymaster0Engine; -class Keymaster1Engine; -class Key; - -/** - * SoftKeymasterContext provides the context for a non-secure implementation of AndroidKeymaster. - */ -class JavaCardSoftKeymasterContext : public keymaster::PureSoftKeymasterContext { - keymaster_error_t LoadKey(const keymaster_algorithm_t algorithm, KeymasterKeyBlob&& key_material, - AuthorizationSet&& hw_enforced, - AuthorizationSet&& sw_enforced, - UniquePtr* key) const; - public: - // Security level must only be used for testing. - explicit JavaCardSoftKeymasterContext( - keymaster_security_level_t security_level = KM_SECURITY_LEVEL_SOFTWARE); - ~JavaCardSoftKeymasterContext() override; - - keymaster_error_t ParseKeyBlob(const KeymasterKeyBlob& blob, - const AuthorizationSet& additional_params, - UniquePtr* key) const override; - -}; - -} // namespace keymaster - -#endif // SYSTEM_KEYMASTER_PURE_SOFT_KEYMASTER_CONTEXT_H_ diff --git a/HAL/keymaster/include/TransportFactory.h b/HAL/keymaster/include/TransportFactory.h deleted file mode 100644 index b09e3ba9..00000000 --- a/HAL/keymaster/include/TransportFactory.h +++ /dev/null @@ -1,76 +0,0 @@ -/* - ** - ** 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 __SE_TRANSPORT_FACTORY__ -#define __SE_TRANSPORT_FACTORY__ - -#include "Transport.h" - -namespace se_transport { - -/** - * TransportFactory class decides which transport mechanism to be used to send data to secure element. In case of - * emulator the communication channel is socket and in case of device the communication channel is via OMAPI. - */ -class TransportFactory { - public: - TransportFactory(bool isEmulator) { - if (!isEmulator) - mTransport = std::unique_ptr(new OmapiTransport()); - else - mTransport = std::unique_ptr(new SocketTransport()); - } - - ~TransportFactory() {} - - /** - * Establishes a communication channel with the secure element. - */ - inline bool openConnection() { - return mTransport->openConnection(); - } - - /** - * Sends the data to the secure element and also receives back the data. - * This is a blocking call. - */ - inline bool sendData(const uint8_t* inData, const size_t inLen, std::vector& output) { - return mTransport->sendData(inData, inLen, output); - } - - /** - * Close the connection. - */ - inline bool closeConnection() { - return mTransport->closeConnection(); - } - - /** - * Returns the connection status of the communication channel. - */ - inline bool isConnected() { - return mTransport->isConnected(); - } - - private: - /** - * Holds the instance of either OmapiTransport class or SocketTransport class. - */ - std::unique_ptr mTransport; - -}; -} -#endif /* __SE_TRANSPORT_FACTORY__ */ diff --git a/HAL/keymint_utils.cpp b/HAL/keymint_utils.cpp new file mode 100644 index 00000000..a98de129 --- /dev/null +++ b/HAL/keymint_utils.cpp @@ -0,0 +1,129 @@ +/* + * 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. + */ + +#include +#include +#include + +namespace keymint::javacard { + +namespace { + +constexpr char kPlatformVersionProp[] = "ro.build.version.release"; +constexpr char kPlatformVersionRegex[] = "^([0-9]{1,2})(\\.([0-9]{1,2}))?(\\.([0-9]{1,2}))?"; +constexpr size_t kMajorVersionMatch = 1; +constexpr size_t kMinorVersionMatch = 3; +constexpr size_t kSubminorVersionMatch = 5; +constexpr size_t kPlatformVersionMatchCount = kSubminorVersionMatch + 1; + +constexpr char kPlatformPatchlevelProp[] = "ro.build.version.security_patch"; +constexpr char kVendorPatchlevelProp[] = "ro.vendor.build.security_patch"; +constexpr char kPatchlevelRegex[] = "^([0-9]{4})-([0-9]{2})-([0-9]{2})$"; +constexpr size_t kYearMatch = 1; +constexpr size_t kMonthMatch = 2; +constexpr size_t kDayMatch = 3; +constexpr size_t kPatchlevelMatchCount = kDayMatch + 1; + +uint32_t match_to_uint32(const char* expression, const regmatch_t& match) { + if (match.rm_so == -1) return 0; + + size_t len = match.rm_eo - match.rm_so; + std::string s(expression + match.rm_so, len); + return std::stoul(s); +} + +std::string wait_and_get_property(const char* prop) { + std::string prop_value; + while (!::android::base::WaitForPropertyCreation(prop)) + ; + prop_value = ::android::base::GetProperty(prop, "" /* default */); + return prop_value; +} + +uint32_t getOsVersion(const char* version_str) { + regex_t regex; + if (regcomp(®ex, kPlatformVersionRegex, REG_EXTENDED)) { + return 0; + } + + regmatch_t matches[kPlatformVersionMatchCount]; + int not_match = + regexec(®ex, version_str, kPlatformVersionMatchCount, matches, 0 /* flags */); + regfree(®ex); + if (not_match) { + return 0; + } + + uint32_t major = match_to_uint32(version_str, matches[kMajorVersionMatch]); + uint32_t minor = match_to_uint32(version_str, matches[kMinorVersionMatch]); + uint32_t subminor = match_to_uint32(version_str, matches[kSubminorVersionMatch]); + + return (major * 100 + minor) * 100 + subminor; +} + +enum class PatchlevelOutput { kYearMonthDay, kYearMonth }; + +uint32_t getPatchlevel(const char* patchlevel_str, PatchlevelOutput detail) { + regex_t regex; + if (regcomp(®ex, kPatchlevelRegex, REG_EXTENDED) != 0) { + return 0; + } + + regmatch_t matches[kPatchlevelMatchCount]; + int not_match = regexec(®ex, patchlevel_str, kPatchlevelMatchCount, matches, 0 /* flags */); + regfree(®ex); + if (not_match) { + return 0; + } + + uint32_t year = match_to_uint32(patchlevel_str, matches[kYearMatch]); + uint32_t month = match_to_uint32(patchlevel_str, matches[kMonthMatch]); + + if (month < 1 || month > 12) { + return 0; + } + + switch (detail) { + case PatchlevelOutput::kYearMonthDay: { + uint32_t day = match_to_uint32(patchlevel_str, matches[kDayMatch]); + if (day < 1 || day > 31) { + return 0; + } + return year * 10000 + month * 100 + day; + } + case PatchlevelOutput::kYearMonth: + return year * 100 + month; + } +} + +} // anonymous namespace + +uint32_t getOsVersion() { + std::string version = wait_and_get_property(kPlatformVersionProp); + return getOsVersion(version.c_str()); +} + +uint32_t getOsPatchlevel() { + std::string patchlevel = wait_and_get_property(kPlatformPatchlevelProp); + return getPatchlevel(patchlevel.c_str(), PatchlevelOutput::kYearMonth); +} + +uint32_t getVendorPatchlevel() { + std::string patchlevel = wait_and_get_property(kVendorPatchlevelProp); + return getPatchlevel(patchlevel.c_str(), PatchlevelOutput::kYearMonthDay); +} + +} // namespace keymint::javacard diff --git a/HAL/keymint_utils.h b/HAL/keymint_utils.h new file mode 100644 index 00000000..6ceb7f7d --- /dev/null +++ b/HAL/keymint_utils.h @@ -0,0 +1,45 @@ +/* + * 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. + */ + +#pragma once +#include + +//#include + +// namespace aidl::android::hardware::security::keymint { +namespace keymint::javacard { + +using std::vector; + +inline static std::vector blob2vector(const uint8_t* data, const size_t length) { + std::vector result(data, data + length); + return result; +} + +inline static std::vector blob2vector(const std::string& value) { + vector result(reinterpret_cast(value.data()), + reinterpret_cast(value.data()) + value.size()); + return result; +} + +// HardwareAuthToken vector2AuthToken(const vector& buffer); +// vector authToken2vector(const HardwareAuthToken& token); + +uint32_t getOsVersion(); +uint32_t getOsPatchlevel(); +uint32_t getVendorPatchlevel(); + +} // namespace keymint::javacard diff --git a/HAL/service.cpp b/HAL/service.cpp new file mode 100644 index 00000000..3d518770 --- /dev/null +++ b/HAL/service.cpp @@ -0,0 +1,62 @@ +/* + * 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. + */ + +#define LOG_TAG "javacard.strongbox-service" + +#include +#include +#include + +#include "JavacardKeyMintDevice.h" +#include + +#include "JavacardSecureElement.h" +#include "JavacardSharedSecret.h" +#include "keymint_utils.h" +#include "JavacardRemotelyProvisionedComponentDevice.h" +#include + +using aidl::android::hardware::security::keymint::JavacardKeyMintDevice; +using aidl::android::hardware::security::keymint::JavacardSharedSecret; +using aidl::android::hardware::security::keymint::SecurityLevel; +using namespace keymint::javacard; + +template std::shared_ptr addService(Args&&... args) { + std::shared_ptr ser = ndk::SharedRefBase::make(std::forward(args)...); + auto instanceName = std::string(T::descriptor) + "/strongbox"; + LOG(INFO) << "adding javacard strongbox service instance: " << instanceName; + binder_status_t status = + AServiceManager_addService(ser->asBinder().get(), instanceName.c_str()); + CHECK(status == STATUS_OK); + return ser; +} + +int main() { + ABinderProcess_setThreadPoolMaxThreadCount(0); + // Javacard Secure Element + std::shared_ptr card = + std::make_shared(std::make_shared(), getOsVersion(), + getOsPatchlevel(), getVendorPatchlevel()); + // Add Keymint Service + addService(card); + // Add Shared Secret Service + addService(card); + // Add Remotely Provisioned Component Service + addService(card); + + ABinderProcess_joinThreadPool(); + return EXIT_FAILURE; // should not reach +} diff --git a/ProvisioningTool/Android.bp b/ProvisioningTool/Android.bp deleted file mode 100644 index ad1ee2d1..00000000 --- a/ProvisioningTool/Android.bp +++ /dev/null @@ -1,68 +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. -// - - -cc_binary { - name: "provision_tool", - vendor: true, - relative_install_path: "hw", - srcs: [ - "ProvisionTool.cpp", - ], - shared_libs: [ - "libdl", - "libcutils", - "libutils", - "libbase", - "libhardware", - "libhidlbase", - "libkeymaster_messages", - "libkeymaster_portable", - "android.hardware.keymaster@4.1", - "android.hardware.keymaster@4.0", - "libcppbor", - "libjc_transport", - "libcrypto", - "libjsoncpp", - "libjc_common", - "libjc_provision", - ], -} - -cc_library { - name: "libjc_provision", - vendor_available: true, - srcs: [ - "Provision.cpp", - ], - shared_libs: [ - "liblog", - "libcutils", - "libdl", - "libbase", - "libutils", - "libhardware", - "libhidlbase", - "libsoftkeymasterdevice", - "libkeymaster_messages", - "libkeymaster_portable", - "libcppbor", - "android.hardware.keymaster@4.1", - "android.hardware.keymaster@4.0", - "libjc_transport", - "libcrypto", - "libjc_common", - ], -} diff --git a/ProvisioningTool/Makefile b/ProvisioningTool/Makefile new file mode 100644 index 00000000..7b992f47 --- /dev/null +++ b/ProvisioningTool/Makefile @@ -0,0 +1,58 @@ +CC = g++ +SRC_DIR = src + +CONSTRUCT_APDUS_SRC = $(SRC_DIR)/construct_apdus.cpp \ + $(SRC_DIR)/cppbor/cppbor.cpp \ + $(SRC_DIR)/cppbor/cppbor_parse.cpp \ + $(SRC_DIR)/utils.cpp \ + $(SRC_DIR)/cppcose/cppcose.cpp + +CONSTRUCT_APDUS_OBJFILES = $(CONSTRUCT_APDUS_SRC:.cpp=.o) +CONSTRUCT_APDUS_BIN = construct_keymint_apdus + +# source files for provision +PROVISION_SRC = $(SRC_DIR)/provision.cpp \ + $(SRC_DIR)/socket.cpp \ + $(SRC_DIR)/cppbor/cppbor.cpp \ + $(SRC_DIR)/cppbor/cppbor_parse.cpp \ + $(SRC_DIR)/utils.cpp \ + +#object files for keymint provision +PROVISION_OBJFILES = $(PROVISION_SRC:.cpp=.o) +PROVISION_BIN = provision_keymint + +ifeq ($(OS),Windows_NT) + uname_S := Windows +else + uname_S := $(shell uname -s) +endif + +ifeq ($(uname_S), Windows) + PLATFORM = -D__WIN32__ +endif +ifeq ($(uname_S), Linux) + PLATFORM = -D__LINUX__ +endif + +DEBUG = -g +CXXFLAGS = $(DEBUG) $(PLATFORM) -Wall -std=c++2a +CFLAGS = $(CXXFLAGS) -Iinclude +LDFLAGS = -Llib/ +LIB_JSON = -ljsoncpp +LIB_CRYPTO = -lcrypto +LDLIBS = $(LIB_JSON) $(LIB_CRYPTO) + +all: $(CONSTRUCT_APDUS_BIN) $(PROVISION_BIN) + +$(CONSTRUCT_APDUS_BIN): $(CONSTRUCT_APDUS_OBJFILES) + $(CC) $(LDFLAGS) -o $@ $^ $(LDLIBS) + +$(PROVISION_BIN): $(PROVISION_OBJFILES) + $(CC) $(LDFLAGS) -o $@ $^ $(LDLIBS) + +%.o: %.cpp + $(CC) $(CFLAGS) -c -o $@ $^ + +.PHONY: clean +clean: + rm -f $(CONSTRUCT_APDUS_OBJFILES) $(CONSTRUCT_APDUS_BIN) $(PROVISION_OBJFILES) $(PROVISION_BIN) diff --git a/ProvisioningTool/Provision.cpp b/ProvisioningTool/Provision.cpp deleted file mode 100644 index 4251cd80..00000000 --- a/ProvisioningTool/Provision.cpp +++ /dev/null @@ -1,509 +0,0 @@ -/* - ** - ** 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 - #include - -#define INS_BEGIN_KM_CMD 0x00 -#define APDU_CLS 0x80 -#define APDU_P1 0x40 -#define APDU_P2 0x00 -#define APDU_RESP_STATUS_OK 0x9000 -#define SE_POWER_RESET_STATUS_FLAG ( 1 << 30) - -namespace keymaster { -namespace V4_1 { -namespace javacard { - -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_PRESHARED_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, - INS_SET_VERSION_PATCHLEVEL_CMD = INS_BEGIN_KM_CMD+9, -}; - -//Extended error codes -enum ExtendedErrors { - SW_CONDITIONS_NOT_SATISFIED = -10001, - UNSUPPORTED_CLA = -10002, - INVALID_P1P2 = -10003, - UNSUPPORTED_INSTRUCTION = -10004, - CMD_NOT_ALLOWED = -10005, - SW_WRONG_LENGTH = -10006, - INVALID_DATA = -10007, - CRYPTO_ILLEGAL_USE = -10008, - CRYPTO_ILLEGAL_VALUE = -10009, - CRYPTO_INVALID_INIT = -10010, - CRYPTO_NO_SUCH_ALGORITHM = -10011, - CRYPTO_UNINITIALIZED_KEY = -10012, - GENERIC_UNKNOWN_ERROR = -10013 -}; - -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_PRESHARED_SECRET = 0x10, - PROVISION_STATUS_BOOT_PARAM = 0x20, - PROVISION_STATUS_PROVISIONING_LOCKED = 0x40, -}; - -// Static function declarations. -static ErrorCode constructApduMessage(Instruction& ins, std::vector& inputData, std::vector& apduOut); -static ErrorCode sendProvisionData(std::unique_ptr& transport, Instruction ins, std::vector& inData, std::vector& response); -static uint16_t getStatus(std::vector& inputData); -template -static std::tuple, T> decodeData(CborConverter& cb, const std::vector& response); -template -static T translateExtendedErrorsToHalErrors(T& errorCode); - -template -static T translateExtendedErrorsToHalErrors(T& errorCode) { - T err; - switch(static_cast(errorCode)) { - case SW_CONDITIONS_NOT_SATISFIED: - case UNSUPPORTED_CLA: - case INVALID_P1P2: - case INVALID_DATA: - case CRYPTO_ILLEGAL_USE: - case CRYPTO_ILLEGAL_VALUE: - case CRYPTO_INVALID_INIT: - case CRYPTO_UNINITIALIZED_KEY: - case GENERIC_UNKNOWN_ERROR: - err = T::UNKNOWN_ERROR; - break; - case CRYPTO_NO_SUCH_ALGORITHM: - err = T::UNSUPPORTED_ALGORITHM; - break; - case UNSUPPORTED_INSTRUCTION: - case CMD_NOT_ALLOWED: - case SW_WRONG_LENGTH: - err = T::UNIMPLEMENTED; - break; - default: - err = static_cast(errorCode); - break; - } - return err; -} - -/** - * Returns the negative value of the same number. - */ -static inline int32_t get2sCompliment(uint32_t value) { - return static_cast(~value+1); -} - -/** - * This function separates the original error code from the - * power reset flag and returns the original error code. - */ -static uint32_t extractErrorCode(uint32_t errorCode) { - //Check if secure element is reset - bool isSeResetOccurred = (0 != (errorCode & SE_POWER_RESET_STATUS_FLAG)); - - if (isSeResetOccurred) { - LOG(ERROR) << "Secure element reset happened"; - errorCode &= ~SE_POWER_RESET_STATUS_FLAG; - } - return errorCode; -} - -template -static std::tuple, T> decodeData(CborConverter& cb, const std::vector& response) { - std::unique_ptr item(nullptr); - T errorCode = T::OK; - std::tie(item, errorCode) = cb.decodeData(response, true); - - uint32_t tempErrCode = extractErrorCode(static_cast(errorCode)); - - // SE sends errocode as unsigned value so convert the unsigned value - // into a signed value of same magnitude and copy back to errorCode. - errorCode = static_cast(get2sCompliment(tempErrCode)); - - if (T::OK != errorCode) - errorCode = translateExtendedErrorsToHalErrors(errorCode); - return {std::move(item), errorCode}; -} - -static inline X509* parseDerCertificate(std::vector& certData) { - X509 *x509 = 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 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 ErrorCode constructApduMessage(Instruction& ins, std::vector& inputData, std::vector& apduOut) { - - 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(USHRT_MAX >= inputData.size()) { - // Send extended length APDU always as response size is not known to HAL. - // Case 1: Lc > 0 CLS | INS | P1 | P2 | 00 | 2 bytes of Lc | CommandData | 2 bytes of Le all set to 00. - // Case 2: Lc = 0 CLS | INS | P1 | P2 | 3 bytes of Le all set to 00. - //Extended length 3 bytes, starts with 0x00 - apduOut.push_back(static_cast(0x00)); - if (inputData.size() > 0) { - 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. - //Accepting complete length of output every time. - apduOut.push_back(static_cast(0x00)); - 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) { - ErrorCode ret = ErrorCode::OK; - std::vector apdu; - CborConverter cborConverter; - std::unique_ptr item; - ret = constructApduMessage(ins, inData, apdu); - 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) = decodeData(cborConverter, std::vector(response.begin(), response.end()-2)); - } else { - ret = ErrorCode::UNKNOWN_ERROR; - } - - return ret; -} - -ErrorCode Provision::init() { - if(pTransportFactory == nullptr) { - pTransportFactory = std::unique_ptr(new se_transport::TransportFactory( - android::base::GetBoolProperty("ro.kernel.qemu", false))); - if(!pTransportFactory->openConnection()) - return ErrorCode::UNKNOWN_ERROR; - } - return ErrorCode::OK; -} - -ErrorCode Provision::provisionAttestationKey(std::vector& batchKey) { - ErrorCode errorCode = ErrorCode::OK; - std::vector privKey; - std::vector pubKey; - EcCurve curve; - CborConverter cborConverter; - cppbor::Array array; - cppbor::Array subArray; - std::vector response; - Instruction ins = Instruction::INS_PROVISION_ATTESTATION_KEY_CMD; - - 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 */ - hidl_vec keyParams = kmParamSet2Hidl(authSetKeyParams); - if(ErrorCode::OK != (errorCode = ecRawKeyFromPKCS8(batchKey, 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(pTransportFactory, ins, cborData, response))) { - return errorCode; - } - return errorCode; -} - -ErrorCode Provision::provisionAtestationCertificateChain(std::vector>& certChain) { - ErrorCode errorCode = ErrorCode::OK; - cppbor::Array array; - Instruction ins = Instruction::INS_PROVISION_CERT_CHAIN_CMD; - std::vector response; - - std::vector certData; - for (auto data : certChain) { - certData.insert(certData.end(), data.begin(), data.end()); - } - cppbor::Bstr bstrCertChain(certData.begin(), certData.end()); - std::vector cborData = bstrCertChain.encode(); - - if(ErrorCode::OK != (errorCode = sendProvisionData(pTransportFactory, ins, cborData, response))) { - return errorCode; - } - return errorCode; -} - -ErrorCode Provision::provisionAttestationCertificateParams(std::vector& batchCertificate) { - 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 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(batchCertificate))) { - return errorCode; - } - - /* Get subject in DER */ - getDerSubjectName(x509, subject); - /* Get Expirty Time */ - getNotAfter(x509, notAfter); - /*Free X509 */ - X509_free(x509); - - array = cppbor::Array(); - array.add(subject); - array.add(notAfter); - std::vector cborData = array.encode(); - - if(ErrorCode::OK != (errorCode = sendProvisionData(pTransportFactory, ins, cborData, response))) { - return errorCode; - } - return errorCode; -} - -ErrorCode Provision::provisionAttestationID(AttestIDParams& attestParams) { - ErrorCode errorCode = ErrorCode::OK; - CborConverter cborConverter; - cppbor::Array array; - Instruction ins = Instruction::INS_PROVISION_ATTEST_IDS_CMD; - std::vector response; - - AuthorizationSet authSetAttestParams(AuthorizationSetBuilder() - .Authorization(TAG_ATTESTATION_ID_BRAND, attestParams.brand.data(), attestParams.brand.size()) - .Authorization(TAG_ATTESTATION_ID_DEVICE, attestParams.device.data(), attestParams.device.size()) - .Authorization(TAG_ATTESTATION_ID_PRODUCT, attestParams.product.data(), attestParams.product.size()) - .Authorization(TAG_ATTESTATION_ID_SERIAL, attestParams.serial.data(), attestParams.serial.size()) - .Authorization(TAG_ATTESTATION_ID_IMEI, attestParams.imei.data(), attestParams.imei.size()) - .Authorization(TAG_ATTESTATION_ID_MEID, attestParams.meid.data(), attestParams.meid.size()) - .Authorization(TAG_ATTESTATION_ID_MANUFACTURER, attestParams.manufacturer.data(), attestParams.manufacturer.size()) - .Authorization(TAG_ATTESTATION_ID_MODEL, attestParams.model.data(), attestParams.model.size())); - - hidl_vec attestKeyParams = kmParamSet2Hidl(authSetAttestParams); - - array = cppbor::Array(); - cborConverter.addKeyparameters(array, attestKeyParams); - std::vector cborData = array.encode(); - - if(ErrorCode::OK != (errorCode = sendProvisionData(pTransportFactory, ins, cborData, response))) { - return errorCode; - } - return errorCode; -} - -ErrorCode Provision::provisionPreSharedSecret(std::vector& preSharedSecret) { - ErrorCode errorCode = ErrorCode::OK; - cppbor::Array array; - Instruction ins = Instruction::INS_PROVISION_PRESHARED_SECRET_CMD; - std::vector response; - - array = cppbor::Array(); - array.add(preSharedSecret); - std::vector cborData = array.encode(); - - if(ErrorCode::OK != (errorCode = sendProvisionData(pTransportFactory, ins, cborData, response))) { - return errorCode; - } - return errorCode; -} - -ErrorCode Provision::setAndroidSystemProperties() { - ErrorCode errorCode = ErrorCode::OK; - cppbor::Array array; - std::vector apdu; - std::vector response; - Instruction ins = Instruction::INS_SET_VERSION_PATCHLEVEL_CMD; - - array.add(GetOsVersion()). - add(GetOsPatchlevel()). - add(GetVendorPatchlevel()); - std::vector cborData = array.encode(); - - if(ErrorCode::OK != (errorCode = sendProvisionData(pTransportFactory, ins, cborData, response))) { - return errorCode; - } - return errorCode; -} - -ErrorCode Provision::provisionBootParameters(BootParams& bootParams) { - ErrorCode errorCode = ErrorCode::OK; - cppbor::Array array; - std::vector apdu; - std::vector response; - Instruction ins = Instruction::INS_SET_BOOT_PARAMS_CMD; - - array.add(bootParams.bootPatchLevel). - /* Verified Boot Key */ - add(bootParams.verifiedBootKey). - /* Verified Boot Hash */ - add(bootParams.verifiedBootKeyHash). - /* boot state */ - add(bootParams.verifiedBootState). - /* device locked */ - add(bootParams.deviceLocked); - - std::vector cborData = array.encode(); - - if(ErrorCode::OK != (errorCode = sendProvisionData(pTransportFactory, ins, cborData, response))) { - return errorCode; - } - return errorCode; -} - -ErrorCode Provision::getProvisionStatus(uint64_t& status) { - ErrorCode errorCode = ErrorCode::OK; - Instruction ins = Instruction::INS_GET_PROVISION_STATUS_CMD; - std::vector cborData; - std::vector response; - std::unique_ptr item; - CborConverter cborConverter; - - if(ErrorCode::OK != (errorCode = sendProvisionData(pTransportFactory, ins, cborData, response))) { - LOG(ERROR) << "Failed to get provision status err: " << static_cast(errorCode); - return errorCode; - } - //Check if SE is provisioned. - std::tie(item, errorCode) = decodeData(cborConverter, std::vector(response.begin(), response.end()-2)); - if(item != NULL) { - - if(!cborConverter.getUint64(item, 1, status)) { - LOG(ERROR) << "Failed to parse the status from cbor data"; - return ErrorCode::UNKNOWN_ERROR; - } - } - return errorCode; -} - -ErrorCode Provision::lockProvision() { - ErrorCode errorCode = ErrorCode::OK; - Instruction ins = Instruction::INS_LOCK_PROVISIONING_CMD; - std::vector cborData; - std::vector response; - - if(ErrorCode::OK != (errorCode = sendProvisionData(pTransportFactory, ins, cborData, response))) { - return errorCode; - } - return errorCode; -} - -ErrorCode Provision::uninit() { - if(pTransportFactory != nullptr) { - if(!pTransportFactory->closeConnection()) - return ErrorCode::UNKNOWN_ERROR; - } - return ErrorCode::OK; -} -// Provision End - -} // namespace javacard -} // namespace V4_1 -} // namespace keymaster diff --git a/ProvisioningTool/Provision.h b/ProvisioningTool/Provision.h deleted file mode 100644 index 68a7f1d1..00000000 --- a/ProvisioningTool/Provision.h +++ /dev/null @@ -1,108 +0,0 @@ -/* - ** - ** 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 { - -typedef struct SystemProperties__ { - uint32_t osVersion; - uint32_t osPatchLevel; - uint32_t vendorPatchLevel; -} SystemProperties; - -typedef struct BootParams_ { - uint32_t bootPatchLevel; - std::vector verifiedBootKey; - std::vector verifiedBootKeyHash; - uint32_t verifiedBootState; - uint32_t deviceLocked; -} BootParams; - -typedef struct AttestIDParams_ { - std::string brand; - std::string device; - std::string product; - std::string serial; - std::string imei; - std::string meid; - std::string manufacturer; - std::string model; -} AttestIDParams; - -class Provision { -public: - /** - * Initalizes the transport layer. - */ - ErrorCode init(); - /** - * Provision the Attestation key. - */ - ErrorCode provisionAttestationKey(std::vector& batchKey); - /** - * Provision the Attestation certificate chain. - */ - ErrorCode provisionAtestationCertificateChain(std::vector>& CertChain); - /** - * Provision the Attestation certificate paramters. - */ - ErrorCode provisionAttestationCertificateParams(std::vector& batchCertificate); - /** - * Provision the Attestation ID. - */ - ErrorCode provisionAttestationID(AttestIDParams& attestParams); - /** - * Provision the pre-shared secret. - */ - ErrorCode provisionPreSharedSecret(std::vector& preSharedSecret); - /** - * Provision the boot parameters. - */ - ErrorCode provisionBootParameters(BootParams& bootParams ); - /** - * Set system properties. - */ - ErrorCode setAndroidSystemProperties(); - - /** - * Locks the provision. After this no more provision commanands are allowed. - */ - ErrorCode lockProvision(); - /** - * Get the provision status. - */ - ErrorCode getProvisionStatus(uint64_t&); - /** - * Uninitialize the transport layer. - */ - ErrorCode uninit(); - -private: - std::unique_ptr pTransportFactory; -}; - -} // namespace javacard -} // namespace V4_1 -} // namespace keymaster -#endif //KEYMASTER_V4_1_JAVACARD_PROVISION_H_ diff --git a/ProvisioningTool/ProvisionTool.cpp b/ProvisioningTool/ProvisionTool.cpp deleted file mode 100644 index 3ff242f5..00000000 --- a/ProvisioningTool/ProvisionTool.cpp +++ /dev/null @@ -1,616 +0,0 @@ -/* - ** - ** 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 -#include -#include -#include -#include -#include -#include -#include -#include - -#define BUFFER_MAX_LENGTH 256 -#define SB_KEYMASTER_SERVICE "javacard" -#define INS_BEGIN_KM_CMD 0x00 -#define APDU_CLS 0x80 -#define APDU_P1 0x40 -#define APDU_P2 0x00 -#define APDU_RESP_STATUS_OK 0x9000 -#define MAX_ATTEST_IDS_SIZE 8 -#define SHARED_SECRET_SIZE 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_PRESHARED_SECRET = 0x10, - PROVISION_STATUS_BOOT_PARAM = 0x20, - PROVISION_STATUS_PROVISIONING_LOCKED = 0x40, -}; - -using ::android::hardware::keymaster::V4_0::ErrorCode; - -static keymaster::V4_1::javacard::Provision mProvision; -Json::Value root; - -constexpr char hex_value[256] = {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, // - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, // '0'..'9' - 0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 'A'..'F' - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // - 0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 'a'..'f' - 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, // - 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, // - 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, // - 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, // - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - -std::string hex2str(std::string a) { - std::string b; - size_t num = a.size() / 2; - b.resize(num); - for (size_t i = 0; i < num; i++) { - b[i] = (hex_value[a[i * 2] & 0xFF] << 4) + (hex_value[a[i * 2 + 1] & 0xFF]); - } - return b; -} - -bool parseJsonFile(const char* filename); - -static bool readDataFromFile(const char *filename, std::vector& data) { - FILE *fp; - bool ret = true; - fp = fopen(filename, "rb"); - if(fp == NULL) { - printf("\nFailed to open file: \n"); - 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)) { - printf("\n No content in the file \n"); - ret = false; - } - if(true == ret) { - data.insert(data.end(), buf.get(), buf.get() + filesize); - } - fclose(fp); - return ret; -} - -void usage() { - printf("Usage: provision_tool [options]\n"); - printf("Valid options are:\n"); - printf("-h, --help show this help message and exit.\n"); - printf("-a, --all jsonFile \t Executes all the provision commands \n"); - printf("-k, --attest_key jsonFile \t Provision attestation key \n"); - printf("-c, --cert_chain jsonFile \t Provision attestation certificate chain \n"); - printf("-p, --cert_params jsonFile \t Provision attestation certificate parameters \n"); - printf("-i, --attest_ids jsonFile \t Provision attestation IDs \n"); - printf("-r, --shared_secret jsonFile \t Provision shared secret \n"); - printf("-b, --set_boot_params jsonFile \t Set boot parameters \n"); - printf("-e, --set_system_properties \t Set system properties \n"); - printf("-s, --provision_status \t Prints the provision status.\n"); - printf("-l, --lock_provision \t Locks the provision commands.\n"); -} - -bool getBootParameterIntValue(Json::Value& bootParamsObj, const char* key, uint32_t *value) { - bool ret = false; - Json::Value val = bootParamsObj[key]; - if(val.empty()) - return ret; - - if(!val.isInt()) - return ret; - - *value = (uint32_t)val.asInt(); - - return true; -} - -bool getBootParameterBlobValue(Json::Value& bootParamsObj, const char* key, std::vector& blob) { - bool ret = false; - Json::Value val = bootParamsObj[key]; - if(val.empty()) - return ret; - - if(!val.isString()) - return ret; - - std::string blobStr = hex2str(val.asString()); - - for(char ch : blobStr) { - blob.push_back((uint8_t)ch); - } - - return true; -} - -bool setAndroidSystemProperties() { - ErrorCode err = ErrorCode::OK; - bool ret = false; - if (ErrorCode::OK != (err = mProvision.setAndroidSystemProperties())) { - printf("\n set boot parameters failed with err:%d \n", (int32_t)err); - return ret; - } - printf("\n SE successfully accepted system properties.\n"); - return true; - -} - -bool setBootParameters(const char* filename) { - Json::Value bootParamsObj; - bool ret = false; - ErrorCode err = ErrorCode::OK; - keymaster::V4_1::javacard::BootParams bootParams; - - if(!parseJsonFile(filename)) - return ret; - - bootParamsObj = root.get("set_boot_params", bootParamsObj); - if (!bootParamsObj.isNull()) { - - if(!getBootParameterIntValue(bootParamsObj, "boot_patch_level", &bootParams.bootPatchLevel)) { - printf("\n Invalid value for boot_patch_level or boot_patch_level tag missing\n"); - return ret; - } - if(!getBootParameterBlobValue(bootParamsObj, "verified_boot_key", bootParams.verifiedBootKey)) { - printf("\n Invalid value for verified_boot_key or verified_boot_key tag missing\n"); - return ret; - } - if(!getBootParameterBlobValue(bootParamsObj, "verified_boot_key_hash", bootParams.verifiedBootKeyHash)) { - printf("\n Invalid value for verified_boot_key_hash or verified_boot_key_hash tag missing\n"); - return ret; - } - if(!getBootParameterIntValue(bootParamsObj, "boot_state", &bootParams.verifiedBootState)) { - printf("\n Invalid value for boot_state or boot_state tag missing\n"); - return ret; - } - if(!getBootParameterIntValue(bootParamsObj, "device_locked", &bootParams.deviceLocked)) { - printf("\n Invalid value for device_locked or device_locked tag missing\n"); - return ret; - } - - } else { - printf("\n Fail: Improper value found for set_boot_params key inside the json file\n"); - return ret; - } - - if (ErrorCode::OK != (err = mProvision.provisionBootParameters(bootParams))) { - printf("\n set boot parameters failed with err:%d \n", (int32_t)err); - return ret; - } - - printf("\n SE successfully accepted boot paramters \n"); - return true; -} - -bool provisionAttestationIds(const char *filename) { - Json::Value attestIds; - bool ret = false; - ErrorCode err = ErrorCode::OK; - keymaster::V4_1::javacard::AttestIDParams params; - - if(!parseJsonFile(filename)) - return ret; - - attestIds = root.get("attest_ids", attestIds); - if (!attestIds.isNull()) { - Json::Value value; - Json::Value::Members keys = attestIds.getMemberNames(); - for(std::string key : keys) { - value = attestIds[key]; - if(value.empty()) { - continue; - } - if (!value.isString()) { - printf("\n Fail: Value for each attest ids key should be a string in the json file \n"); - return ret; - } - - if (0 == key.compare("brand")) { - params.brand = value.asString(); - } else if(0 == key.compare("device")) { - params.device = value.asString(); - } else if(0 == key.compare("product")) { - params.product = value.asString(); - } else if(0 == key.compare("serial")) { - params.serial = value.asString(); - } else if(0 == key.compare("imei")) { - params.imei = value.asString(); - } else if(0 == key.compare("meid")) { - params.meid = value.asString(); - } else if(0 == key.compare("manufacturer")) { - params.manufacturer = value.asString(); - } else if(0 == key.compare("model")) { - params.model = value.asString(); - } else { - printf("\n unknown attestation id key:%s \n", key.c_str()); - return ret; - } - } - - if (ErrorCode::OK != (err = mProvision.provisionAttestationID(params))) { - printf("\n Provision attestationID parameters failed with err:%d \n", (int32_t)err); - return ret; - } - } else { - printf("\n Fail: Improper value found for attest_ids key inside the json file \n"); - return ret; - } - printf("\n provisioned attestation ids successfully \n"); - return true; -} - -bool lockProvision() { - ErrorCode errorCode; - bool ret = false; - - if(ErrorCode::OK != (errorCode = mProvision.lockProvision())) { - printf("\n Failed to lock provisioning error: %d\n", uint32_t(errorCode)); - return ret; - } - printf("\n Successfully locked provisioning process. Now SE doesn't accept any further provision commands. \n"); - return true; -} - -bool getProvisionStatus() { - bool ret = false; - uint64_t status; - if (ErrorCode::OK != mProvision.getProvisionStatus(status)) { - return ret; - } - if ( (0 != (status & ProvisionStatus::PROVISION_STATUS_ATTESTATION_KEY)) && - (0 != (status & ProvisionStatus::PROVISION_STATUS_ATTESTATION_CERT_CHAIN)) && - (0 != (status & ProvisionStatus::PROVISION_STATUS_ATTESTATION_CERT_PARAMS)) && - (0 != (status & ProvisionStatus::PROVISION_STATUS_PRESHARED_SECRET)) && - (0 != (status & ProvisionStatus::PROVISION_STATUS_BOOT_PARAM))) { - printf("\n SE is provisioned \n"); - } else { - if (0 == (status & ProvisionStatus::PROVISION_STATUS_ATTESTATION_KEY)) { - printf("\n Attestation key is not provisioned \n"); - } - if (0 == (status & ProvisionStatus::PROVISION_STATUS_ATTESTATION_CERT_CHAIN)) { - printf("\n Attestation certificate chain is not provisioned \n"); - } - if (0 == (status & ProvisionStatus::PROVISION_STATUS_ATTESTATION_CERT_PARAMS)) { - printf("\n Attestation certificate params are not provisioned \n"); - } - if (0 == (status & ProvisionStatus::PROVISION_STATUS_PRESHARED_SECRET)) { - printf("\n Shared secret is not provisioned \n"); - } - if (0 == (status & ProvisionStatus::PROVISION_STATUS_BOOT_PARAM)) { - printf("\n Boot params are not provisioned \n"); - } - } - return true; -} - -bool provisionSharedSecret(const char* filename) { - Json::Value sharedSecret; - bool ret = false; - ErrorCode err = ErrorCode::OK; - - if(!parseJsonFile(filename)) - return ret; - - sharedSecret = root.get("shared_secret", sharedSecret); - if (!sharedSecret.isNull()) { - - if (!sharedSecret.isString()) { - printf("\n Fail: Value for shared secret key should be string inside the json file\n"); - return ret; - } - std::string secret = hex2str(sharedSecret.asString()); - std::vector data(secret.begin(), secret.end()); - if(ErrorCode::OK != (err = mProvision.provisionPreSharedSecret(data))) { - printf("\n Provision pre-shared secret failed with err:%d \n", (int32_t)err); - return ret; - } - } else { - printf("\n Fail: Improper value for shared_secret key inside the json file\n"); - return ret; - } - printf("\n Provisioned shared secret successfully \n"); - return true; -} - -static bool provisionAttestationKey(const char* filename) { - Json::Value keyFile; - bool ret = false; - ErrorCode err = ErrorCode::OK; - - if(!parseJsonFile(filename)) - return ret; - - keyFile = root.get("attest_key", keyFile); - if (!keyFile.isNull()) { - std::vector data; - - std::string keyFileName = keyFile.asString(); - if(!readDataFromFile(keyFileName.data(), data)) { - printf("\n Failed to read the Root ec key\n"); - return ret; - } - if(ErrorCode::OK != (err = mProvision.provisionAttestationKey(data))) { - printf("\n Provision attestation key failed with error: %d\n", (int32_t)err); - return ret; - } - } else { - printf("\n Improper value for attest_key in json file \n"); - return ret; - } - printf("\n Provisioned attestation key successfully\n"); - return true; -} - -bool provisionAttestationCertificateChain(const char* filename) { - Json::Value certChainFile; - bool ret = false; - ErrorCode err = ErrorCode::OK; - - if(!parseJsonFile(filename)) - return ret; - - certChainFile = root.get("attest_cert_chain", certChainFile); - if (!certChainFile.isNull()) { - std::vector> certData; - - if(certChainFile.isArray()) { - for (int i = 0; i < certChainFile.size(); i++) { - std::vector tmp; - if(certChainFile[i].isString()) { - /* Read the certificates. */ - if(!readDataFromFile(certChainFile[i].asString().data(), tmp)) { - printf("\n Failed to read the Root certificate\n"); - return ret; - } - certData.push_back(std::move(tmp)); - } else { - printf("\n Fail: Only proper certificate paths as a string is allowed inside the json file. \n"); - return ret; - } - } - } else { - printf("\n Fail: cert chain value should be an array inside the json file. \n"); - return ret; - } - if (ErrorCode::OK != (err = mProvision.provisionAtestationCertificateChain(certData))) { - printf("\n Provision certificate chain failed with error: %d\n", (int32_t)err); - return ret; - } - } else { - printf("\n Fail: Improper value found for attest_cert_chain key inside json file \n"); - return ret; - } - printf("\n Provisioned attestation certificate chain successfully\n"); - return true; -} - -bool provisionAttestationCertificateParams(const char* filename) { - Json::Value certChainFile; - bool ret = false; - ErrorCode err = ErrorCode::OK; - - if(!parseJsonFile(filename)) - return ret; - - certChainFile = root.get("attest_cert_chain", certChainFile); - if (!certChainFile.isNull()) { - std::vector> certData; - - if(certChainFile.isArray()) { - if (certChainFile.size() == 0) { - return ret; - } - std::vector tmp; - if(!readDataFromFile(certChainFile[0].asString().data(), tmp)) { - printf("\n Failed to read the Root certificate\n"); - return ret; - } - if (ErrorCode::OK != (err = mProvision.provisionAttestationCertificateParams(tmp))) { - printf("\n Provision certificate params failed with error: %d\n", (int32_t)err); - return ret; - } - } else { - printf("\n Fail: cert chain value should be an array inside the json file. \n"); - return ret; - } - } else { - printf("\n Fail: Improper value found for attest_cert_chain key inside json file \n"); - return ret; - } - printf("\n Provisioned attestation certificate parameters successfully\n"); - return true; -} - -bool provision(const char* filename) { - - if(!provisionAttestationKey(filename)) { - return false; - } - if(!provisionAttestationCertificateChain(filename)) { - return false; - } - if(!provisionAttestationCertificateParams(filename)) { - return false; - } - if(!provisionSharedSecret(filename)) { - return false; - } - if(!provisionAttestationIds(filename)) { - return false; - } - if(!setBootParameters(filename)) { - return false; - } - if(!setAndroidSystemProperties()) { - return false; - } - return true; -} - -bool parseJsonFile(const char* filename) { - std::stringstream buffer; - Json::Reader jsonReader; - - if(!root.empty()) { - printf("\n Already parsed \n"); - return true; - } - std::ifstream stream(filename); - buffer << stream.rdbuf(); - if(jsonReader.parse(buffer.str(), root)) { - printf("\n Parsed json file successfully\n"); - return true; - } else { - printf("\n Failed to parse json file\n"); - return false; - } -} - -int main(int argc, char* argv[]) -{ - int c; - struct option longOpts[] = { - {"all", required_argument, NULL, 'a'}, - {"attest_key", required_argument, NULL, 'k'}, - {"cert_chain", required_argument, NULL, 'c'}, - {"cert_params", required_argument, NULL,'p'}, - {"attest_ids", required_argument, NULL, 'i'}, - {"shared_secret", required_argument, NULL, 'r'}, - {"set_boot_params", required_argument, NULL, 'b'}, - {"set_system_properties", no_argument, NULL, 'e'}, - {"provision_status", no_argument, NULL, 's'}, - {"lock_provision", no_argument, NULL, 'l'}, - {"help", no_argument, NULL, 'h'}, - {0,0,0,0} - }; - - if (argc <= 1) { - printf("\n Invalid command \n"); - usage(); - } - /* Initialize provision */ - mProvision.init(); - - /* getopt_long stores the option index here. */ - while ((c = getopt_long(argc, argv, ":slhea:k:c:p:i:r:b:", longOpts, NULL)) != -1) { - switch(c) { - case 'a': - //all - if(!provision(optarg)) - printf("\n Failed to provision the device \n"); - break; - case 'k': - //attest key - if(!provisionAttestationKey(optarg)) - printf("\n Failed to provision attestaion key\n"); - break; - case 'c': - //attest certchain - if(!provisionAttestationCertificateChain(optarg)) - printf("\n Failed to provision attestaion certificate chain\n"); - break; - case 'p': - //attest cert params - if(!provisionAttestationCertificateParams(optarg)) - printf("\n Failed to provision attestaion certificate paramaters\n"); - break; - case 'i': - //attestation ids. - if(!provisionAttestationIds(optarg)) - printf("\n Failed to provision attestaion ids\n"); - break; - //shared secret - case 'r': - if(!provisionSharedSecret(optarg)) - printf("\n Failed to provision shared secret\n"); - break; - case 'b': - //set boot params - if(!setBootParameters(optarg)) - printf("\n Failed to set boot parameters.\n"); - break; - case 'e': - //set Android system properties - if(!setAndroidSystemProperties()) - printf("\n Failed to set android system properties.\n"); - break; - case 's': - if(!getProvisionStatus()) - printf("\n Failed to get provision status \n"); - break; - case 'l': - lockProvision(); - break; - case 'h': - usage(); - break; - case ':': - printf("\n missing argument\n"); - usage(); - break; - case '?': - default: - printf("\n Invalid option\n"); - usage(); - break; - } - } - if(optind < argc) { - usage(); - } - /*Uninitalize */ - mProvision.uninit(); - return 0; -} diff --git a/ProvisioningTool/README.md b/ProvisioningTool/README.md index 9d65e5d1..21fd0034 100644 --- a/ProvisioningTool/README.md +++ b/ProvisioningTool/README.md @@ -1,34 +1,41 @@ # Provisioning tool -This directory contains provisioning tool which helps in provisioning -the secure element by using the APIs exposed by Provision library. -This tool takes the input parameters from json file. +This directory contains two tools. One which constructs the apdus and dumps them to a json file, Other which gets the apuds from the json file and provision them into a secure element simulator. Both the tools can be compiled and executed from a Linux machine. -#### Build -This tool can be built along with aosp build. It has dependency on -[libjc_common](../HAL/keymaster/Android.bp) and -[libjc_provision](Android.bp). +#### Build instruction +The default target generates both the executables. One construct_apdus and the other provision. +$ make +Individual targets can also be selected as shown below +$ make construct_apdus +$ make provision +Make clean will remove all the object files and binaries +$ make clean + +#### Environment setup +Before executing the binaries make sure LD_LIBRARY_PATH is set +export LD_LIBRARY_PATH=./lib:$LD_LIBRARY_PATH #### Sample resources for quick testing -Two sample json files are located in this directory with names -[sample_json_cf.txt](sample_json_cf.txt) and and [sample_json_gf.txt](sample_json_gf.txt) -for your reference. Use sample_json_cf.txt for cuttlefish target and use -sample_json_gf.txt for goldfish target. Also the required certificates and -keys can be found in [test_resources](test_resources) directory. Copy the -certificates and the key into the emulator/device filesystem in their respective -paths mentioned in the sample json file. +one sample json files is located in this directory with name +[sample_json_keymint_cf.txt](sample_json_keymint_cf.txt) +for your reference. Use sample_json_keymint_cf.txt for keymint +cuttlefish target. Also the required certificates and keys can be found in +[test_resources](test_resources) directory for your reference. -#### Usage +#### Usage for construct_apdus

-Usage: provision_tool options
+Usage: construct_keymint_apdus options
 Valid options are:
 -h, --help                        show the help message and exit.
--a, --all jsonFile                Executes all the provision commands.
--k, --attest_key jsonFile         Provision attestation key.
--c, --cert_chain jsonFile         Provision attestation certificate chain.
--p, --cert_params jsonFile        Provision attestation certificate parameters.
--i, --attest_ids jsonFile         Provision attestation IDs.
--r, --shared_secret jsonFile      Provision pre-shared secret.
--b, --set_boot_params jsonFile    Provision boot parameters.
--s, --provision_stautus           Prints the current provision status.
--l, --lock_provision              Locks the provision commands.
+-i, --input  jsonFile 	 Input json file 
+-o, --output jsonFile 	 Output json file
+
+ +#### Usage for provision +
+Usage: provision_keymint options
+Valid options are:
+-h, --help                      show the help message and exit.
+-i, --input  jsonFile 	  Input json file 
+-s, --provision_stautus   Prints the current provision status.
+-l, --lock_provision      Locks the provision state.
 
diff --git a/ProvisioningTool/include/UniquePtr.h b/ProvisioningTool/include/UniquePtr.h new file mode 100644 index 00000000..da74780b --- /dev/null +++ b/ProvisioningTool/include/UniquePtr.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2010 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. + */ + +#pragma once + +#include // for size_t + +#include + +// Default deleter for pointer types. +template struct DefaultDelete { + enum { type_must_be_complete = sizeof(T) }; + DefaultDelete() {} + void operator()(T* p) const { delete p; } +}; + +// Default deleter for array types. +template struct DefaultDelete { + enum { type_must_be_complete = sizeof(T) }; + void operator()(T* p) const { delete[] p; } +}; + +template > +using UniquePtr = std::unique_ptr; + + diff --git a/ProvisioningTool/include/constants.h b/ProvisioningTool/include/constants.h new file mode 100644 index 00000000..89854703 --- /dev/null +++ b/ProvisioningTool/include/constants.h @@ -0,0 +1,103 @@ +/* + ** + ** Copyright 2021, 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. + */ +#pragma once + +#include +#include +#include +#include +#include +#include "UniquePtr.h" + +#define SUCCESS 0 +#define FAILURE 1 +#define P1_40 0x40 +#define P1_50 0x50 +#define APDU_CLS 0x80 +#define APDU_P1 0x50 +#define APDU_P2 0x00 +#define INS_BEGIN_KM_CMD 0x00 +#define APDU_RESP_STATUS_OK 0x9000 + + + +template +struct OpenSslObjectDeleter { + void operator()(T* p) { FreeFunc(p); } +}; + +#define DEFINE_OPENSSL_OBJECT_POINTER(name) \ + typedef OpenSslObjectDeleter name##_Delete; \ + typedef UniquePtr name##_Ptr; + +DEFINE_OPENSSL_OBJECT_POINTER(EC_KEY) +DEFINE_OPENSSL_OBJECT_POINTER(EVP_PKEY) +DEFINE_OPENSSL_OBJECT_POINTER(X509) +DEFINE_OPENSSL_OBJECT_POINTER(EC_POINT) +DEFINE_OPENSSL_OBJECT_POINTER(EC_GROUP) +DEFINE_OPENSSL_OBJECT_POINTER(BN_CTX) +DEFINE_OPENSSL_OBJECT_POINTER(EVP_MD_CTX) + +typedef OpenSslObjectDeleter BIGNUM_Delete; +typedef UniquePtr BIGNUM_Ptr; + +// EC Affine point length for Nist P256. +constexpr uint32_t kAffinePointLength = 32; + +// Tags +constexpr uint64_t kTagAlgorithm = 268435458u; +constexpr uint64_t kTagDigest = 536870917u; +constexpr uint64_t kTagCurve = 268435466u; +constexpr uint64_t kTagPurpose = 536870913u; +constexpr uint64_t kTagAttestationIdBrand = 2415919814u; +constexpr uint64_t kTagAttestationIdDevice = 2415919815u; +constexpr uint64_t kTagAttestationIdProduct = 2415919816u; +constexpr uint64_t kTagAttestationIdSerial = 2415919817u; +constexpr uint64_t kTagAttestationIdImei = 2415919818u; +constexpr uint64_t kTagAttestationIdMeid = 2415919819u; +constexpr uint64_t kTagAttestationIdManufacturer = 2415919820u; +constexpr uint64_t kTagAttestationIdModel = 2415919821u; + +// Values +constexpr uint64_t kCurveP256 = 1; +constexpr uint64_t kAlgorithmEc = 3; +constexpr uint64_t kDigestSha256 = 4; +constexpr uint64_t kPurposeAttest = 0x7F; +constexpr uint64_t kKeyFormatRaw = 3; + +// json keys +constexpr char kAttestKey[] = "attest_key"; +constexpr char kAttestCertChain[] = "attest_cert_chain"; +constexpr char kAttestCertParams[] = "attest_cert_params"; +constexpr char kSharedSecret[] = "shared_secret"; +constexpr char kBootParams[] = "boot_params"; +constexpr char kAttestationIds[] = "attestation_ids"; +constexpr char kDeviceUniqueKey[] = "device_unique_key"; +constexpr char kAdditionalCertChain[] = "additional_cert_chain"; +constexpr char kSignerInfo[] = "signer_info"; +constexpr char kProvisionStatus[] = "provision_status"; +constexpr char kLockProvision[] = "lock_provision"; + +// Instruction constatnts +// TODO Modify according to keymint +constexpr int kAttestationIdsCmd = INS_BEGIN_KM_CMD + 1; +constexpr int kPresharedSecretCmd = INS_BEGIN_KM_CMD + 2; +constexpr int kLockProvisionCmd = INS_BEGIN_KM_CMD + 3; +constexpr int kGetProvisionStatusCmd = INS_BEGIN_KM_CMD + 4; +constexpr int kBootParamsCmd = INS_BEGIN_KM_CMD + 5; +constexpr int kDeviceUniqueKeyCmd = INS_BEGIN_KM_CMD + 6; +constexpr int kAdditionalCertChainCmd = INS_BEGIN_KM_CMD + 7; diff --git a/ProvisioningTool/include/cppbor/cppbor.h b/ProvisioningTool/include/cppbor/cppbor.h new file mode 100644 index 00000000..45ae67cf --- /dev/null +++ b/ProvisioningTool/include/cppbor/cppbor.h @@ -0,0 +1,1113 @@ +/* + * Copyright 2019 Google LLC + * + * 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 + * + * https://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. + */ + +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +namespace cppbor { + +enum MajorType : uint8_t { + UINT = 0 << 5, + NINT = 1 << 5, + BSTR = 2 << 5, + TSTR = 3 << 5, + ARRAY = 4 << 5, + MAP = 5 << 5, + SEMANTIC = 6 << 5, + SIMPLE = 7 << 5, +}; + +enum SimpleType { + BOOLEAN, + NULL_T, // Only two supported, as yet. +}; + +enum SpecialAddlInfoValues : uint8_t { + FALSE = 20, + TRUE = 21, + NULL_V = 22, + ONE_BYTE_LENGTH = 24, + TWO_BYTE_LENGTH = 25, + FOUR_BYTE_LENGTH = 26, + EIGHT_BYTE_LENGTH = 27, +}; + +class Item; +class Uint; +class Nint; +class Int; +class Tstr; +class Bstr; +class Simple; +class Bool; +class Array; +class Map; +class Null; +class SemanticTag; +class EncodedItem; +class ViewTstr; +class ViewBstr; + +/** + * Returns the size of a CBOR header that contains the additional info value addlInfo. + */ +size_t headerSize(uint64_t addlInfo); + +/** + * Encodes a CBOR header with the specified type and additional info into the range [pos, end). + * Returns a pointer to one past the last byte written, or nullptr if there isn't sufficient space + * to write the header. + */ +uint8_t* encodeHeader(MajorType type, uint64_t addlInfo, uint8_t* pos, const uint8_t* end); + +using EncodeCallback = std::function; + +/** + * Encodes a CBOR header with the specified type and additional info, passing each byte in turn to + * encodeCallback. + */ +void encodeHeader(MajorType type, uint64_t addlInfo, EncodeCallback encodeCallback); + +/** + * Encodes a CBOR header witht he specified type and additional info, writing each byte to the + * provided OutputIterator. + */ +template ::iterator_category>>> +void encodeHeader(MajorType type, uint64_t addlInfo, OutputIterator iter) { + return encodeHeader(type, addlInfo, [&](uint8_t v) { *iter++ = v; }); +} + +/** + * Item represents a CBOR-encodeable data item. Item is an abstract interface with a set of virtual + * methods that allow encoding of the item or conversion to the appropriate derived type. + */ +class Item { + public: + virtual ~Item() {} + + /** + * Returns the CBOR type of the item. + */ + virtual MajorType type() const = 0; + + // These methods safely downcast an Item to the appropriate subclass. + virtual Int* asInt() { return nullptr; } + const Int* asInt() const { return const_cast(this)->asInt(); } + virtual Uint* asUint() { return nullptr; } + const Uint* asUint() const { return const_cast(this)->asUint(); } + virtual Nint* asNint() { return nullptr; } + const Nint* asNint() const { return const_cast(this)->asNint(); } + virtual Tstr* asTstr() { return nullptr; } + const Tstr* asTstr() const { return const_cast(this)->asTstr(); } + virtual Bstr* asBstr() { return nullptr; } + const Bstr* asBstr() const { return const_cast(this)->asBstr(); } + virtual Simple* asSimple() { return nullptr; } + const Simple* asSimple() const { return const_cast(this)->asSimple(); } + virtual Map* asMap() { return nullptr; } + const Map* asMap() const { return const_cast(this)->asMap(); } + virtual Array* asArray() { return nullptr; } + const Array* asArray() const { return const_cast(this)->asArray(); } + + virtual ViewTstr* asViewTstr() { return nullptr; } + const ViewTstr* asViewTstr() const { return const_cast(this)->asViewTstr(); } + virtual ViewBstr* asViewBstr() { return nullptr; } + const ViewBstr* asViewBstr() const { return const_cast(this)->asViewBstr(); } + + // Like those above, these methods safely downcast an Item when it's actually a SemanticTag. + // However, if you think you want to use these methods, you probably don't. Typically, the way + // you should handle tagged Items is by calling the appropriate method above (e.g. asInt()) + // which will return a pointer to the tagged Item, rather than the tag itself. If you want to + // find out if the Item* you're holding is to something with one or more tags applied, see + // semanticTagCount() and semanticTag() below. + virtual SemanticTag* asSemanticTag() { return nullptr; } + const SemanticTag* asSemanticTag() const { return const_cast(this)->asSemanticTag(); } + + /** + * Returns the number of semantic tags prefixed to this Item. + */ + virtual size_t semanticTagCount() const { return 0; } + + /** + * Returns the semantic tag at the specified nesting level `nesting`, iff `nesting` is less than + * the value returned by semanticTagCount(). + * + * CBOR tags are "nested" by applying them in sequence. The "rightmost" tag is the "inner" tag. + * That is, given: + * + * 4(5(6("AES"))) which encodes as C1 C2 C3 63 414553 + * + * The tstr "AES" is tagged with 6. The combined entity ("AES" tagged with 6) is tagged with 5, + * etc. So in this example, semanticTagCount() would return 3, and semanticTag(0) would return + * 5 semanticTag(1) would return 5 and semanticTag(2) would return 4. For values of n > 2, + * semanticTag(n) will return 0, but this is a meaningless value. + * + * If this layering is confusing, you probably don't have to worry about it. Nested tagging does + * not appear to be common, so semanticTag(0) is the only one you'll use. + */ + virtual uint64_t semanticTag(size_t /* nesting */ = 0) const { return 0; } + + /** + * Returns true if this is a "compound" item, i.e. one that contains one or more other items. + */ + virtual bool isCompound() const { return false; } + + bool operator==(const Item& other) const&; + bool operator!=(const Item& other) const& { return !(*this == other); } + + /** + * Returns the number of bytes required to encode this Item into CBOR. Note that if this is a + * complex Item, calling this method will require walking the whole tree. + */ + virtual size_t encodedSize() const = 0; + + /** + * Encodes the Item into buffer referenced by range [*pos, end). Returns a pointer to one past + * the last position written. Returns nullptr if there isn't enough space to encode. + */ + virtual uint8_t* encode(uint8_t* pos, const uint8_t* end) const = 0; + + /** + * Encodes the Item by passing each encoded byte to encodeCallback. + */ + virtual void encode(EncodeCallback encodeCallback) const = 0; + + /** + * Clones the Item + */ + virtual std::unique_ptr clone() const = 0; + + /** + * Encodes the Item into the provided OutputIterator. + */ + template ::iterator_category> + void encode(OutputIterator i) const { + return encode([&](uint8_t v) { *i++ = v; }); + } + + /** + * Encodes the Item into a new std::vector. + */ + std::vector encode() const { + std::vector retval; + retval.reserve(encodedSize()); + encode(std::back_inserter(retval)); + return retval; + } + + /** + * Encodes the Item into a new std::string. + */ + std::string toString() const { + std::string retval; + retval.reserve(encodedSize()); + encode([&](uint8_t v) { retval.push_back(v); }); + return retval; + } + + /** + * Encodes only the header of the Item. + */ + inline uint8_t* encodeHeader(uint64_t addlInfo, uint8_t* pos, const uint8_t* end) const { + return ::cppbor::encodeHeader(type(), addlInfo, pos, end); + } + + /** + * Encodes only the header of the Item. + */ + inline void encodeHeader(uint64_t addlInfo, EncodeCallback encodeCallback) const { + ::cppbor::encodeHeader(type(), addlInfo, encodeCallback); + } +}; + +/** + * EncodedItem represents a bit of already-encoded CBOR. Caveat emptor: It does no checking to + * ensure that the provided data is a valid encoding, cannot be meaninfully-compared with other + * kinds of items and you cannot use the as*() methods to find out what's inside it. + */ +class EncodedItem : public Item { + public: + explicit EncodedItem(std::vector value) : mValue(std::move(value)) {} + + bool operator==(const EncodedItem& other) const& { return mValue == other.mValue; } + + // Type can't be meaningfully-obtained. We could extract the type from the first byte and return + // it, but you can't do any of the normal things with an EncodedItem so there's no point. + MajorType type() const override { + assert(false); + return static_cast(-1); + } + size_t encodedSize() const override { return mValue.size(); } + uint8_t* encode(uint8_t* pos, const uint8_t* end) const override { + if (end - pos < static_cast(mValue.size())) return nullptr; + return std::copy(mValue.begin(), mValue.end(), pos); + } + void encode(EncodeCallback encodeCallback) const override { + std::for_each(mValue.begin(), mValue.end(), encodeCallback); + } + std::unique_ptr clone() const override { return std::make_unique(mValue); } + + private: + std::vector mValue; +}; + +/** + * Int is an abstraction that allows Uint and Nint objects to be manipulated without caring about + * the sign. + */ +class Int : public Item { + public: + bool operator==(const Int& other) const& { return value() == other.value(); } + + virtual int64_t value() const = 0; + using Item::asInt; + Int* asInt() override { return this; } +}; + +/** + * Uint is a concrete Item that implements CBOR major type 0. + */ +class Uint : public Int { + public: + static constexpr MajorType kMajorType = UINT; + + explicit Uint(uint64_t v) : mValue(v) {} + + bool operator==(const Uint& other) const& { return mValue == other.mValue; } + + MajorType type() const override { return kMajorType; } + using Item::asUint; + Uint* asUint() override { return this; } + + size_t encodedSize() const override { return headerSize(mValue); } + + int64_t value() const override { return mValue; } + uint64_t unsignedValue() const { return mValue; } + + using Item::encode; + uint8_t* encode(uint8_t* pos, const uint8_t* end) const override { + return encodeHeader(mValue, pos, end); + } + void encode(EncodeCallback encodeCallback) const override { + encodeHeader(mValue, encodeCallback); + } + + std::unique_ptr clone() const override { return std::make_unique(mValue); } + + private: + uint64_t mValue; +}; + +/** + * Nint is a concrete Item that implements CBOR major type 1. + + * Note that it is incapable of expressing the full range of major type 1 values, becaue it can only + * express values that fall into the range [std::numeric_limits::min(), -1]. It cannot + * express values in the range [std::numeric_limits::min() - 1, + * -std::numeric_limits::max()]. + */ +class Nint : public Int { + public: + static constexpr MajorType kMajorType = NINT; + + explicit Nint(int64_t v); + + bool operator==(const Nint& other) const& { return mValue == other.mValue; } + + MajorType type() const override { return kMajorType; } + using Item::asNint; + Nint* asNint() override { return this; } + size_t encodedSize() const override { return headerSize(addlInfo()); } + + int64_t value() const override { return mValue; } + + using Item::encode; + uint8_t* encode(uint8_t* pos, const uint8_t* end) const override { + return encodeHeader(addlInfo(), pos, end); + } + void encode(EncodeCallback encodeCallback) const override { + encodeHeader(addlInfo(), encodeCallback); + } + + std::unique_ptr clone() const override { return std::make_unique(mValue); } + + private: + uint64_t addlInfo() const { return -1ll - mValue; } + + int64_t mValue; +}; + +/** + * Bstr is a concrete Item that implements major type 2. + */ +class Bstr : public Item { + public: + static constexpr MajorType kMajorType = BSTR; + + // Construct an empty Bstr + explicit Bstr() {} + + // Construct from a vector + explicit Bstr(std::vector v) : mValue(std::move(v)) {} + + // Construct from a string + explicit Bstr(const std::string& v) + : mValue(reinterpret_cast(v.data()), + reinterpret_cast(v.data()) + v.size()) {} + + // Construct from a pointer/size pair + explicit Bstr(const std::pair& buf) + : mValue(buf.first, buf.first + buf.second) {} + + // Construct from a pair of iterators + template ::iterator_category, + typename = typename std::iterator_traits::iterator_category> + explicit Bstr(const std::pair& pair) : mValue(pair.first, pair.second) {} + + // Construct from an iterator range. + template ::iterator_category, + typename = typename std::iterator_traits::iterator_category> + Bstr(I1 begin, I2 end) : mValue(begin, end) {} + + bool operator==(const Bstr& other) const& { return mValue == other.mValue; } + + MajorType type() const override { return kMajorType; } + using Item::asBstr; + Bstr* asBstr() override { return this; } + size_t encodedSize() const override { return headerSize(mValue.size()) + mValue.size(); } + using Item::encode; + uint8_t* encode(uint8_t* pos, const uint8_t* end) const override; + void encode(EncodeCallback encodeCallback) const override { + encodeHeader(mValue.size(), encodeCallback); + encodeValue(encodeCallback); + } + + const std::vector& value() const { return mValue; } + std::vector&& moveValue() { return std::move(mValue); } + + std::unique_ptr clone() const override { return std::make_unique(mValue); } + + private: + void encodeValue(EncodeCallback encodeCallback) const; + + std::vector mValue; +}; + +/** + * ViewBstr is a read-only version of Bstr backed by std::string_view + */ +class ViewBstr : public Item { + public: + static constexpr MajorType kMajorType = BSTR; + + // Construct an empty ViewBstr + explicit ViewBstr() {} + + // Construct from a string_view of uint8_t values + explicit ViewBstr(std::basic_string_view v) : mView(std::move(v)) {} + + // Construct from a string_view + explicit ViewBstr(std::string_view v) + : mView(reinterpret_cast(v.data()), v.size()) {} + + // Construct from an iterator range + template ::iterator_category, + typename = typename std::iterator_traits::iterator_category> + ViewBstr(I1 begin, I2 end) : mView(begin, end) {} + + // Construct from a uint8_t pointer pair + ViewBstr(const uint8_t* begin, const uint8_t* end) + : mView(begin, std::distance(begin, end)) {} + + bool operator==(const ViewBstr& other) const& { return mView == other.mView; } + + MajorType type() const override { return kMajorType; } + using Item::asViewBstr; + ViewBstr* asViewBstr() override { return this; } + size_t encodedSize() const override { return headerSize(mView.size()) + mView.size(); } + using Item::encode; + uint8_t* encode(uint8_t* pos, const uint8_t* end) const override; + void encode(EncodeCallback encodeCallback) const override { + encodeHeader(mView.size(), encodeCallback); + encodeValue(encodeCallback); + } + + const std::basic_string_view& view() const { return mView; } + + std::unique_ptr clone() const override { return std::make_unique(mView); } + + private: + void encodeValue(EncodeCallback encodeCallback) const; + + std::basic_string_view mView; +}; + +/** + * Tstr is a concrete Item that implements major type 3. + */ +class Tstr : public Item { + public: + static constexpr MajorType kMajorType = TSTR; + + // Construct from a string + explicit Tstr(std::string v) : mValue(std::move(v)) {} + + // Construct from a string_view + explicit Tstr(const std::string_view& v) : mValue(v) {} + + // Construct from a C string + explicit Tstr(const char* v) : mValue(std::string(v)) {} + + // Construct from a pair of iterators + template ::iterator_category, + typename = typename std::iterator_traits::iterator_category> + explicit Tstr(const std::pair& pair) : mValue(pair.first, pair.second) {} + + // Construct from an iterator range + template ::iterator_category, + typename = typename std::iterator_traits::iterator_category> + Tstr(I1 begin, I2 end) : mValue(begin, end) {} + + bool operator==(const Tstr& other) const& { return mValue == other.mValue; } + + MajorType type() const override { return kMajorType; } + using Item::asTstr; + Tstr* asTstr() override { return this; } + size_t encodedSize() const override { return headerSize(mValue.size()) + mValue.size(); } + using Item::encode; + uint8_t* encode(uint8_t* pos, const uint8_t* end) const override; + void encode(EncodeCallback encodeCallback) const override { + encodeHeader(mValue.size(), encodeCallback); + encodeValue(encodeCallback); + } + + const std::string& value() const { return mValue; } + std::string&& moveValue() { return std::move(mValue); } + + std::unique_ptr clone() const override { return std::make_unique(mValue); } + + private: + void encodeValue(EncodeCallback encodeCallback) const; + + std::string mValue; +}; + +/** + * ViewTstr is a read-only version of Tstr backed by std::string_view + */ +class ViewTstr : public Item { + public: + static constexpr MajorType kMajorType = TSTR; + + // Construct an empty ViewTstr + explicit ViewTstr() {} + + // Construct from a string_view + explicit ViewTstr(std::string_view v) : mView(std::move(v)) {} + + // Construct from an iterator range + template ::iterator_category, + typename = typename std::iterator_traits::iterator_category> + ViewTstr(I1 begin, I2 end) : mView(begin, end) {} + + // Construct from a uint8_t pointer pair + ViewTstr(const uint8_t* begin, const uint8_t* end) + : mView(reinterpret_cast(begin), + std::distance(begin, end)) {} + + bool operator==(const ViewTstr& other) const& { return mView == other.mView; } + + MajorType type() const override { return kMajorType; } + using Item::asViewTstr; + ViewTstr* asViewTstr() override { return this; } + size_t encodedSize() const override { return headerSize(mView.size()) + mView.size(); } + using Item::encode; + uint8_t* encode(uint8_t* pos, const uint8_t* end) const override; + void encode(EncodeCallback encodeCallback) const override { + encodeHeader(mView.size(), encodeCallback); + encodeValue(encodeCallback); + } + + const std::string_view& view() const { return mView; } + + std::unique_ptr clone() const override { return std::make_unique(mView); } + + private: + void encodeValue(EncodeCallback encodeCallback) const; + + std::string_view mView; +}; + +/* + * Array is a concrete Item that implements CBOR major type 4. + * + * Note that Arrays are not copyable. This is because copying them is expensive and making them + * move-only ensures that they're never copied accidentally. If you actually want to copy an Array, + * use the clone() method. + */ +class Array : public Item { + public: + static constexpr MajorType kMajorType = ARRAY; + + Array() = default; + Array(const Array& other) = delete; + Array(Array&&) = default; + Array& operator=(const Array&) = delete; + Array& operator=(Array&&) = default; + + bool operator==(const Array& other) const&; + + /** + * Construct an Array from a variable number of arguments of different types. See + * details::makeItem below for details on what types may be provided. In general, this accepts + * all of the types you'd expect and doest the things you'd expect (integral values are addes as + * Uint or Nint, std::string and char* are added as Tstr, bools are added as Bool, etc.). + */ + template + Array(Args&&... args); + + /** + * Append a single element to the Array, of any compatible type. + */ + template + Array& add(T&& v) &; + template + Array&& add(T&& v) &&; + + bool isCompound() const override { return true; } + + virtual size_t size() const { return mEntries.size(); } + + size_t encodedSize() const override { + return std::accumulate(mEntries.begin(), mEntries.end(), headerSize(size()), + [](size_t sum, auto& entry) { return sum + entry->encodedSize(); }); + } + + using Item::encode; // Make base versions visible. + uint8_t* encode(uint8_t* pos, const uint8_t* end) const override; + void encode(EncodeCallback encodeCallback) const override; + + const std::unique_ptr& operator[](size_t index) const { return get(index); } + std::unique_ptr& operator[](size_t index) { return get(index); } + + const std::unique_ptr& get(size_t index) const { return mEntries[index]; } + std::unique_ptr& get(size_t index) { return mEntries[index]; } + + MajorType type() const override { return kMajorType; } + using Item::asArray; + Array* asArray() override { return this; } + + std::unique_ptr clone() const override; + + auto begin() { return mEntries.begin(); } + auto begin() const { return mEntries.begin(); } + auto end() { return mEntries.end(); } + auto end() const { return mEntries.end(); } + + protected: + std::vector> mEntries; +}; + +/* + * Map is a concrete Item that implements CBOR major type 5. + * + * Note that Maps are not copyable. This is because copying them is expensive and making them + * move-only ensures that they're never copied accidentally. If you actually want to copy a + * Map, use the clone() method. + */ +class Map : public Item { + public: + static constexpr MajorType kMajorType = MAP; + + using entry_type = std::pair, std::unique_ptr>; + + Map() = default; + Map(const Map& other) = delete; + Map(Map&&) = default; + Map& operator=(const Map& other) = delete; + Map& operator=(Map&&) = default; + + bool operator==(const Map& other) const&; + + /** + * Construct a Map from a variable number of arguments of different types. An even number of + * arguments must be provided (this is verified statically). See details::makeItem below for + * details on what types may be provided. In general, this accepts all of the types you'd + * expect and doest the things you'd expect (integral values are addes as Uint or Nint, + * std::string and char* are added as Tstr, bools are added as Bool, etc.). + */ + template + Map(Args&&... args); + + /** + * Append a key/value pair to the Map, of any compatible types. + */ + template + Map& add(Key&& key, Value&& value) &; + template + Map&& add(Key&& key, Value&& value) &&; + + bool isCompound() const override { return true; } + + virtual size_t size() const { return mEntries.size(); } + + size_t encodedSize() const override { + return std::accumulate( + mEntries.begin(), mEntries.end(), headerSize(size()), [](size_t sum, auto& entry) { + return sum + entry.first->encodedSize() + entry.second->encodedSize(); + }); + } + + using Item::encode; // Make base versions visible. + uint8_t* encode(uint8_t* pos, const uint8_t* end) const override; + void encode(EncodeCallback encodeCallback) const override; + + /** + * Find and return the value associated with `key`, if any. + * + * If the searched-for `key` is not present, returns `nullptr`. + * + * Note that if the map is canonicalized (sorted), Map::get() peforms a binary search. If your + * map is large and you're searching in it many times, it may be worthwhile to canonicalize it + * to make Map::get() faster. Any use of a method that might modify the map disables the + * speedup. + */ + template + const std::unique_ptr& get(Key key) const; + + // Note that use of non-const operator[] marks the map as not canonicalized. + auto& operator[](size_t index) { + mCanonicalized = false; + return mEntries[index]; + } + const auto& operator[](size_t index) const { return mEntries[index]; } + + MajorType type() const override { return kMajorType; } + using Item::asMap; + Map* asMap() override { return this; } + + /** + * Sorts the map in canonical order, as defined in RFC 7049. Use this before encoding if you + * want canonicalization; cppbor does not canonicalize by default, though the integer encodings + * are always canonical and cppbor does not support indefinite-length encodings, so map order + * canonicalization is the only thing that needs to be done. + * + * @param recurse If set to true, canonicalize() will also walk the contents of the map and + * canonicalize any contained maps as well. + */ + Map& canonicalize(bool recurse = false) &; + Map&& canonicalize(bool recurse = false) && { + canonicalize(recurse); + return std::move(*this); + } + + bool isCanonical() { return mCanonicalized; } + + std::unique_ptr clone() const override; + + auto begin() { + mCanonicalized = false; + return mEntries.begin(); + } + auto begin() const { return mEntries.begin(); } + auto end() { + mCanonicalized = false; + return mEntries.end(); + } + auto end() const { return mEntries.end(); } + + // Returns true if a < b, per CBOR map key canonicalization rules. + static bool keyLess(const Item* a, const Item* b); + + protected: + std::vector mEntries; + + private: + bool mCanonicalized = false; +}; + +class SemanticTag : public Item { + public: + static constexpr MajorType kMajorType = SEMANTIC; + + template + SemanticTag(uint64_t tagValue, T&& taggedItem); + SemanticTag(const SemanticTag& other) = delete; + SemanticTag(SemanticTag&&) = default; + SemanticTag& operator=(const SemanticTag& other) = delete; + SemanticTag& operator=(SemanticTag&&) = default; + + bool operator==(const SemanticTag& other) const& { + return mValue == other.mValue && *mTaggedItem == *other.mTaggedItem; + } + + bool isCompound() const override { return true; } + + virtual size_t size() const { return 1; } + + // Encoding returns the tag + enclosed Item. + size_t encodedSize() const override { return headerSize(mValue) + mTaggedItem->encodedSize(); } + + using Item::encode; // Make base versions visible. + uint8_t* encode(uint8_t* pos, const uint8_t* end) const override; + void encode(EncodeCallback encodeCallback) const override; + + // type() is a bit special. In normal usage it should return the wrapped type, but during + // parsing when we haven't yet parsed the tagged item, it needs to return SEMANTIC. + MajorType type() const override { return mTaggedItem ? mTaggedItem->type() : SEMANTIC; } + using Item::asSemanticTag; + SemanticTag* asSemanticTag() override { return this; } + + // Type information reflects the enclosed Item. Note that if the immediately-enclosed Item is + // another tag, these methods will recurse down to the non-tag Item. + using Item::asInt; + Int* asInt() override { return mTaggedItem->asInt(); } + using Item::asUint; + Uint* asUint() override { return mTaggedItem->asUint(); } + using Item::asNint; + Nint* asNint() override { return mTaggedItem->asNint(); } + using Item::asTstr; + Tstr* asTstr() override { return mTaggedItem->asTstr(); } + using Item::asBstr; + Bstr* asBstr() override { return mTaggedItem->asBstr(); } + using Item::asSimple; + Simple* asSimple() override { return mTaggedItem->asSimple(); } + using Item::asMap; + Map* asMap() override { return mTaggedItem->asMap(); } + using Item::asArray; + Array* asArray() override { return mTaggedItem->asArray(); } + using Item::asViewTstr; + ViewTstr* asViewTstr() override { return mTaggedItem->asViewTstr(); } + using Item::asViewBstr; + ViewBstr* asViewBstr() override { return mTaggedItem->asViewBstr(); } + + std::unique_ptr clone() const override; + + size_t semanticTagCount() const override; + uint64_t semanticTag(size_t nesting = 0) const override; + + protected: + SemanticTag() = default; + SemanticTag(uint64_t value) : mValue(value) {} + uint64_t mValue; + std::unique_ptr mTaggedItem; +}; + +/** + * Simple is abstract Item that implements CBOR major type 7. It is intended to be subclassed to + * create concrete Simple types. At present only Bool is provided. + */ +class Simple : public Item { + public: + static constexpr MajorType kMajorType = SIMPLE; + + bool operator==(const Simple& other) const&; + + virtual SimpleType simpleType() const = 0; + MajorType type() const override { return kMajorType; } + + Simple* asSimple() override { return this; } + + virtual const Bool* asBool() const { return nullptr; }; + virtual const Null* asNull() const { return nullptr; }; +}; + +/** + * Bool is a concrete type that implements CBOR major type 7, with additional item values for TRUE + * and FALSE. + */ +class Bool : public Simple { + public: + static constexpr SimpleType kSimpleType = BOOLEAN; + + explicit Bool(bool v) : mValue(v) {} + + bool operator==(const Bool& other) const& { return mValue == other.mValue; } + + SimpleType simpleType() const override { return kSimpleType; } + const Bool* asBool() const override { return this; } + + size_t encodedSize() const override { return 1; } + + using Item::encode; + uint8_t* encode(uint8_t* pos, const uint8_t* end) const override { + return encodeHeader(mValue ? TRUE : FALSE, pos, end); + } + void encode(EncodeCallback encodeCallback) const override { + encodeHeader(mValue ? TRUE : FALSE, encodeCallback); + } + + bool value() const { return mValue; } + + std::unique_ptr clone() const override { return std::make_unique(mValue); } + + private: + bool mValue; +}; + +/** + * Null is a concrete type that implements CBOR major type 7, with additional item value for NULL + */ +class Null : public Simple { + public: + static constexpr SimpleType kSimpleType = NULL_T; + + explicit Null() {} + + SimpleType simpleType() const override { return kSimpleType; } + const Null* asNull() const override { return this; } + + size_t encodedSize() const override { return 1; } + + using Item::encode; + uint8_t* encode(uint8_t* pos, const uint8_t* end) const override { + return encodeHeader(NULL_V, pos, end); + } + void encode(EncodeCallback encodeCallback) const override { + encodeHeader(NULL_V, encodeCallback); + } + + std::unique_ptr clone() const override { return std::make_unique(); } +}; + +/** + * Returns pretty-printed CBOR for |item| + * + * If a byte-string is larger than |maxBStrSize| its contents will not be printed, instead the value + * of the form "" will be + * printed. Pass zero for |maxBStrSize| to disable this. + * + * The |mapKeysToNotPrint| parameter specifies the name of map values to not print. This is useful + * for unit tests. + */ +std::string prettyPrint(const Item* item, size_t maxBStrSize = 32, + const std::vector& mapKeysNotToPrint = {}); + +/** + * Returns pretty-printed CBOR for |value|. + * + * Only valid CBOR should be passed to this function. + * + * If a byte-string is larger than |maxBStrSize| its contents will not be printed, instead the value + * of the form "" will be + * printed. Pass zero for |maxBStrSize| to disable this. + * + * The |mapKeysToNotPrint| parameter specifies the name of map values to not print. This is useful + * for unit tests. + */ +std::string prettyPrint(const std::vector& encodedCbor, size_t maxBStrSize = 32, + const std::vector& mapKeysNotToPrint = {}); + +/** + * Details. Mostly you shouldn't have to look below, except perhaps at the docstring for makeItem. + */ +namespace details { + +template +struct is_iterator_pair_over : public std::false_type {}; + +template +struct is_iterator_pair_over< + std::pair, V, + typename std::enable_if_t::value_type>>> + : public std::true_type {}; + +template +struct is_unique_ptr_of_subclass_of_v : public std::false_type {}; + +template +struct is_unique_ptr_of_subclass_of_v, + typename std::enable_if_t>> + : public std::true_type {}; + +/* check if type is one of std::string (1), std::string_view (2), null-terminated char* (3) or pair + * of iterators (4)*/ +template +struct is_text_type_v : public std::false_type {}; + +template +struct is_text_type_v< + T, typename std::enable_if_t< + /* case 1 */ // + std::is_same_v>, std::string> + /* case 2 */ // + || std::is_same_v>, std::string_view> + /* case 3 */ // + || std::is_same_v>, char*> // + || std::is_same_v>, const char*> + /* case 4 */ + || details::is_iterator_pair_over::value>> : public std::true_type {}; + +/** + * Construct a unique_ptr from many argument types. Accepts: + * + * (a) booleans; + * (b) integers, all sizes and signs; + * (c) text strings, as defined by is_text_type_v above; + * (d) byte strings, as std::vector(d1), pair of iterators (d2) or pair + * (d3); and + * (e) Item subclass instances, including Array and Map. Items may be provided by naked pointer + * (e1), unique_ptr (e2), reference (e3) or value (e3). If provided by reference or value, will + * be moved if possible. If provided by pointer, ownership is taken. + * (f) null pointer; + * (g) enums, using the underlying integer value. + */ +template +std::unique_ptr makeItem(T v) { + Item* p = nullptr; + if constexpr (/* case a */ std::is_same_v) { + p = new Bool(v); + } else if constexpr (/* case b */ std::is_integral_v) { // b + if (v < 0) { + p = new Nint(v); + } else { + p = new Uint(static_cast(v)); + } + } else if constexpr (/* case c */ // + details::is_text_type_v::value) { + p = new Tstr(v); + } else if constexpr (/* case d1 */ // + std::is_same_v>, + std::vector> + /* case d2 */ // + || details::is_iterator_pair_over::value + /* case d3 */ // + || std::is_same_v>, + std::pair>) { + p = new Bstr(v); + } else if constexpr (/* case e1 */ // + std::is_pointer_v && + std::is_base_of_v>) { + p = v; + } else if constexpr (/* case e2 */ // + details::is_unique_ptr_of_subclass_of_v::value) { + p = v.release(); + } else if constexpr (/* case e3 */ // + std::is_base_of_v) { + p = new T(std::move(v)); + } else if constexpr (/* case f */ std::is_null_pointer_v) { + p = new Null(); + } else if constexpr (/* case g */ std::is_enum_v) { + return makeItem(static_cast>(v)); + } else { + // It's odd that this can't be static_assert(false), since it shouldn't be evaluated if one + // of the above ifs matches. But static_assert(false) always triggers. + static_assert(std::is_same_v, "makeItem called with unsupported type"); + } + return std::unique_ptr(p); +} + +inline void map_helper(Map& /* map */) {} + +template +inline void map_helper(Map& map, Key&& key, Value&& value, Rest&&... rest) { + map.add(std::forward(key), std::forward(value)); + map_helper(map, std::forward(rest)...); +} + +} // namespace details + +template >> || ...)>> +Array::Array(Args&&... args) { + mEntries.reserve(sizeof...(args)); + (mEntries.push_back(details::makeItem(std::forward(args))), ...); +} + +template +Array& Array::add(T&& v) & { + mEntries.push_back(details::makeItem(std::forward(v))); + return *this; +} + +template +Array&& Array::add(T&& v) && { + mEntries.push_back(details::makeItem(std::forward(v))); + return std::move(*this); +} + +template > +Map::Map(Args&&... args) { + static_assert((sizeof...(Args)) % 2 == 0, "Map must have an even number of entries"); + mEntries.reserve(sizeof...(args) / 2); + details::map_helper(*this, std::forward(args)...); +} + +template +Map& Map::add(Key&& key, Value&& value) & { + mEntries.push_back({details::makeItem(std::forward(key)), + details::makeItem(std::forward(value))}); + mCanonicalized = false; + return *this; +} + +template +Map&& Map::add(Key&& key, Value&& value) && { + this->add(std::forward(key), std::forward(value)); + return std::move(*this); +} + +static const std::unique_ptr kEmptyItemPtr; + +template || std::is_enum_v || + details::is_text_type_v::value>> +const std::unique_ptr& Map::get(Key key) const { + auto keyItem = details::makeItem(key); + + if (mCanonicalized) { + // It's sorted, so binary-search it. + auto found = std::lower_bound(begin(), end(), keyItem.get(), + [](const entry_type& entry, const Item* key) { + return keyLess(entry.first.get(), key); + }); + return (found == end() || *found->first != *keyItem) ? kEmptyItemPtr : found->second; + } else { + // Unsorted, do a linear search. + auto found = std::find_if( + begin(), end(), [&](const entry_type& entry) { return *entry.first == *keyItem; }); + return found == end() ? kEmptyItemPtr : found->second; + } +} + +template +SemanticTag::SemanticTag(uint64_t value, T&& taggedItem) + : mValue(value), mTaggedItem(details::makeItem(std::forward(taggedItem))) {} + +} // namespace cppbor diff --git a/ProvisioningTool/include/cppbor/cppbor_parse.h b/ProvisioningTool/include/cppbor/cppbor_parse.h new file mode 100644 index 00000000..22cd18d0 --- /dev/null +++ b/ProvisioningTool/include/cppbor/cppbor_parse.h @@ -0,0 +1,195 @@ +/* + * Copyright 2019 Google LLC + * + * 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 + * + * https://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. + */ + +#pragma once + +#include "cppbor.h" + +namespace cppbor { + +using ParseResult = std::tuple /* result */, const uint8_t* /* newPos */, + std::string /* errMsg */>; + +/** + * Parse the first CBOR data item (possibly compound) from the range [begin, end). + * + * Returns a tuple of Item pointer, buffer pointer and error message. If parsing is successful, the + * Item pointer is non-null, the buffer pointer points to the first byte after the + * successfully-parsed item and the error message string is empty. If parsing fails, the Item + * pointer is null, the buffer pointer points to the first byte that was unparseable (the first byte + * of a data item header that is malformed in some way, e.g. an invalid value, or a length that is + * too large for the remaining buffer, etc.) and the string contains an error message describing the + * problem encountered. + */ +ParseResult parse(const uint8_t* begin, const uint8_t* end); + +/** + * Parse the first CBOR data item (possibly compound) from the range [begin, end). + * + * Returns a tuple of Item pointer, buffer pointer and error message. If parsing is successful, the + * Item pointer is non-null, the buffer pointer points to the first byte after the + * successfully-parsed item and the error message string is empty. If parsing fails, the Item + * pointer is null, the buffer pointer points to the first byte that was unparseable (the first byte + * of a data item header that is malformed in some way, e.g. an invalid value, or a length that is + * too large for the remaining buffer, etc.) and the string contains an error message describing the + * problem encountered. + * + * The returned CBOR data item will contain View* items backed by + * std::string_view types over the input range. + * WARNING! If the input range changes underneath, the corresponding views will + * carry the same change. + */ +ParseResult parseWithViews(const uint8_t* begin, const uint8_t* end); + +/** + * Parse the first CBOR data item (possibly compound) from the byte vector. + * + * Returns a tuple of Item pointer, buffer pointer and error message. If parsing is successful, the + * Item pointer is non-null, the buffer pointer points to the first byte after the + * successfully-parsed item and the error message string is empty. If parsing fails, the Item + * pointer is null, the buffer pointer points to the first byte that was unparseable (the first byte + * of a data item header that is malformed in some way, e.g. an invalid value, or a length that is + * too large for the remaining buffer, etc.) and the string contains an error message describing the + * problem encountered. + */ +inline ParseResult parse(const std::vector& encoding) { + return parse(encoding.data(), encoding.data() + encoding.size()); +} + +/** + * Parse the first CBOR data item (possibly compound) from the range [begin, begin + size). + * + * Returns a tuple of Item pointer, buffer pointer and error message. If parsing is successful, the + * Item pointer is non-null, the buffer pointer points to the first byte after the + * successfully-parsed item and the error message string is empty. If parsing fails, the Item + * pointer is null, the buffer pointer points to the first byte that was unparseable (the first byte + * of a data item header that is malformed in some way, e.g. an invalid value, or a length that is + * too large for the remaining buffer, etc.) and the string contains an error message describing the + * problem encountered. + */ +inline ParseResult parse(const uint8_t* begin, size_t size) { + return parse(begin, begin + size); +} + +/** + * Parse the first CBOR data item (possibly compound) from the range [begin, begin + size). + * + * Returns a tuple of Item pointer, buffer pointer and error message. If parsing is successful, the + * Item pointer is non-null, the buffer pointer points to the first byte after the + * successfully-parsed item and the error message string is empty. If parsing fails, the Item + * pointer is null, the buffer pointer points to the first byte that was unparseable (the first byte + * of a data item header that is malformed in some way, e.g. an invalid value, or a length that is + * too large for the remaining buffer, etc.) and the string contains an error message describing the + * problem encountered. + * + * The returned CBOR data item will contain View* items backed by + * std::string_view types over the input range. + * WARNING! If the input range changes underneath, the corresponding views will + * carry the same change. + */ +inline ParseResult parseWithViews(const uint8_t* begin, size_t size) { + return parseWithViews(begin, begin + size); +} + +/** + * Parse the first CBOR data item (possibly compound) from the value contained in a Bstr. + * + * Returns a tuple of Item pointer, buffer pointer and error message. If parsing is successful, the + * Item pointer is non-null, the buffer pointer points to the first byte after the + * successfully-parsed item and the error message string is empty. If parsing fails, the Item + * pointer is null, the buffer pointer points to the first byte that was unparseable (the first byte + * of a data item header that is malformed in some way, e.g. an invalid value, or a length that is + * too large for the remaining buffer, etc.) and the string contains an error message describing the + * problem encountered. + */ +inline ParseResult parse(const Bstr* bstr) { + if (!bstr) + return ParseResult(nullptr, nullptr, "Null Bstr pointer"); + return parse(bstr->value()); +} + +class ParseClient; + +/** + * Parse the CBOR data in the range [begin, end) in streaming fashion, calling methods on the + * provided ParseClient when elements are found. + */ +void parse(const uint8_t* begin, const uint8_t* end, ParseClient* parseClient); + +/** + * Parse the CBOR data in the range [begin, end) in streaming fashion, calling methods on the + * provided ParseClient when elements are found. Uses the View* item types + * instead of the copying ones. + */ +void parseWithViews(const uint8_t* begin, const uint8_t* end, ParseClient* parseClient); + +/** + * Parse the CBOR data in the vector in streaming fashion, calling methods on the + * provided ParseClient when elements are found. + */ +inline void parse(const std::vector& encoding, ParseClient* parseClient) { + return parse(encoding.data(), encoding.data() + encoding.size(), parseClient); +} + +/** + * A pure interface that callers of the streaming parse functions must implement. + */ +class ParseClient { + public: + virtual ~ParseClient() {} + + /** + * Called when an item is found. The Item pointer points to the found item; use type() and + * the appropriate as*() method to examine the value. hdrBegin points to the first byte of the + * header, valueBegin points to the first byte of the value and end points one past the end of + * the item. In the case of header-only items, such as integers, and compound items (ARRAY, + * MAP or SEMANTIC) whose end has not yet been found, valueBegin and end are equal and point to + * the byte past the header. + * + * Note that for compound types (ARRAY, MAP, and SEMANTIC), the Item will have no content. For + * Map and Array items, the size() method will return a correct value, but the index operators + * are unsafe, and the object cannot be safely compared with another Array/Map. + * + * The method returns a ParseClient*. In most cases "return this;" will be the right answer, + * but a different ParseClient may be returned, which the parser will begin using. If the method + * returns nullptr, parsing will be aborted immediately. + */ + virtual ParseClient* item(std::unique_ptr& item, const uint8_t* hdrBegin, + const uint8_t* valueBegin, const uint8_t* end) = 0; + + /** + * Called when the end of a compound item (MAP or ARRAY) is found. The item argument will be + * the same one passed to the item() call -- and may be empty if item() moved its value out. + * hdrBegin, valueBegin and end point to the beginning of the item header, the beginning of the + * first contained value, and one past the end of the last contained value, respectively. + * + * Note that the Item will have no content. + * + * As with item(), itemEnd() can change the ParseClient by returning a different one, or end the + * parsing by returning nullptr; + */ + virtual ParseClient* itemEnd(std::unique_ptr& item, const uint8_t* hdrBegin, + const uint8_t* valueBegin, const uint8_t* end) = 0; + + /** + * Called when parsing encounters an error. position is set to the first unparsed byte (one + * past the last successfully-parsed byte) and errorMessage contains an message explaining what + * sort of error occurred. + */ + virtual void error(const uint8_t* position, const std::string& errorMessage) = 0; +}; + +} // namespace cppbor diff --git a/ProvisioningTool/include/cppcose/cppcose.h b/ProvisioningTool/include/cppcose/cppcose.h new file mode 100644 index 00000000..09a2d76f --- /dev/null +++ b/ProvisioningTool/include/cppcose/cppcose.h @@ -0,0 +1,218 @@ +/* + * 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. + */ + +#pragma once + +#include +#include +#include +#include +#include + +#include +#include + +namespace cppcose { + +template class ErrMsgOr; +using bytevec = std::vector; + +constexpr int kCoseSign1EntryCount = 4; +constexpr int kCoseSign1ProtectedParams = 0; +constexpr int kCoseSign1UnprotectedParams = 1; +constexpr int kCoseSign1Payload = 2; +constexpr int kCoseSign1Signature = 3; + +constexpr int kCoseMac0EntryCount = 4; +constexpr int kCoseMac0ProtectedParams = 0; +constexpr int kCoseMac0UnprotectedParams = 1; +constexpr int kCoseMac0Payload = 2; +constexpr int kCoseMac0Tag = 3; + +constexpr int kCoseEncryptEntryCount = 4; +constexpr int kCoseEncryptProtectedParams = 0; +constexpr int kCoseEncryptUnprotectedParams = 1; +constexpr int kCoseEncryptPayload = 2; +constexpr int kCoseEncryptRecipients = 3; + +enum Label : int { + ALGORITHM = 1, + KEY_ID = 4, + IV = 5, + COSE_KEY = -1, +}; + +enum CoseKeyAlgorithm : int { + AES_GCM_256 = 3, + HMAC_256 = 5, + ES256 = -7, // ECDSA with SHA-256 + EDDSA = -8, + ECDH_ES_HKDF_256 = -25, +}; + +enum CoseKeyCurve : int { P256 = 1, X25519 = 4, ED25519 = 6 }; +enum CoseKeyType : int { OCTET_KEY_PAIR = 1, EC2 = 2, SYMMETRIC_KEY = 4 }; +enum CoseKeyOps : int { SIGN = 1, VERIFY = 2, ENCRYPT = 3, DECRYPT = 4 }; + +constexpr int kAesGcmNonceLength = 12; +constexpr int kAesGcmTagSize = 16; +constexpr int kAesGcmKeySize = 32; +constexpr int kAesGcmKeySizeBits = 256; + +template class ErrMsgOr { + public: + ErrMsgOr(std::string errMsg) // NOLINT(google-explicit-constructor) + : errMsg_(std::move(errMsg)) {} + ErrMsgOr(const char* errMsg) // NOLINT(google-explicit-constructor) + : errMsg_(errMsg) {} + ErrMsgOr(T val) // NOLINT(google-explicit-constructor) + : value_(std::move(val)) {} + + explicit operator bool() const { return value_.has_value(); } + + T* operator->() & { + assert(value_); + return &value_.value(); + } + T& operator*() & { + assert(value_); + return value_.value(); + }; + T&& operator*() && { + assert(value_); + return std::move(value_).value(); + }; + + const std::string& message() { return errMsg_; } + std::string moveMessage() { return std::move(errMsg_); } + + T moveValue() { + assert(value_); + return std::move(value_).value(); + } + + private: + std::string errMsg_; + std::optional value_; +}; + +class CoseKey { + public: + CoseKey() {} + CoseKey(const CoseKey&) = delete; + CoseKey(CoseKey&&) = default; + + enum Label : int { + KEY_TYPE = 1, + KEY_ID = 2, + ALGORITHM = 3, + KEY_OPS = 4, + CURVE = -1, + PUBKEY_X = -2, + PUBKEY_Y = -3, + PRIVATE_KEY = -4, + TEST_KEY = -70000 // Application-defined + }; + + static ErrMsgOr parse(const bytevec& coseKey) { + auto [parsedKey, _, errMsg] = cppbor::parse(coseKey); + if (!parsedKey) return errMsg + " when parsing key"; + if (!parsedKey->asMap()) return "CoseKey must be a map"; + return CoseKey(static_cast(parsedKey.release())); + } + + static ErrMsgOr parse(const bytevec& coseKey, CoseKeyType expectedKeyType, + CoseKeyAlgorithm expectedAlgorithm, CoseKeyCurve expectedCurve) { + auto key = parse(coseKey); + if (!key) return key; + + if (!key->checkIntValue(CoseKey::KEY_TYPE, expectedKeyType) || + !key->checkIntValue(CoseKey::ALGORITHM, expectedAlgorithm) || + !key->checkIntValue(CoseKey::CURVE, expectedCurve)) { + return "Unexpected key type:"; + } + + return key; + } + static ErrMsgOr parseP256(const bytevec& coseKey) { + auto key = parse(coseKey, EC2, ES256, P256); + if (!key) return key; + + auto& pubkey_x = key->getMap().get(PUBKEY_X); + auto& pubkey_y = key->getMap().get(PUBKEY_Y); + if (!pubkey_x || !pubkey_y || !pubkey_x->asBstr() || !pubkey_y->asBstr() || + pubkey_x->asBstr()->value().size() != 32 || pubkey_y->asBstr()->value().size() != 32) { + return "Invalid P256 public key"; + } + + return key; + } + + std::optional getIntValue(Label label) { + const auto& value = key_->get(label); + if (!value || !value->asInt()) return {}; + return value->asInt()->value(); + } + + std::optional getBstrValue(Label label) { + const auto& value = key_->get(label); + if (!value || !value->asBstr()) return {}; + return value->asBstr()->value(); + } + + const cppbor::Map& getMap() const { return *key_; } + cppbor::Map&& moveMap() { return std::move(*key_); } + + bool checkIntValue(Label label, int expectedValue) { + const auto& value = key_->get(label); + return value && value->asInt() && value->asInt()->value() == expectedValue; + } + + void add(Label label, int value) { key_->add(label, value); } + void add(Label label, bytevec value) { key_->add(label, std::move(value)); } + + bytevec encode() { return key_->canonicalize().encode(); } + + private: + explicit CoseKey(cppbor::Map* parsedKey) : key_(parsedKey) {} + + // This is the full parsed key structure. + std::unique_ptr key_; +}; + +ErrMsgOr createCoseSign1Signature(const bytevec& key, const bytevec& protectedParams, + const bytevec& payload, const bytevec& aad); +ErrMsgOr constructCoseSign1(const bytevec& key, const bytevec& payload, + const bytevec& aad); +ErrMsgOr constructCoseSign1(const bytevec& key, cppbor::Map extraProtectedFields, + const bytevec& payload, const bytevec& aad); +/** + * Verify and parse a COSE_Sign1 message, returning the payload. + * + * @param ignoreSignature indicates whether signature verification should be skipped. If true, no + * verification of the signature will be done. + * + * @param coseSign1 is the COSE_Sign1 to verify and parse. + * + * @param signingCoseKey is a CBOR-encoded COSE_Key to use to verify the signature. The bytevec may + * be empty, in which case the function assumes that coseSign1's payload is the COSE_Key to + * use, i.e. that coseSign1 is a self-signed "certificate". + */ +ErrMsgOr verifyAndParseCoseSign1(bool ignoreSignature, + const cppbor::Array* coseSign1, + const bytevec& signingCoseKey, + const bytevec& aad); +} // namespace cppcose diff --git a/ProvisioningTool/include/json/assertions.h b/ProvisioningTool/include/json/assertions.h new file mode 100644 index 00000000..fbec7ae0 --- /dev/null +++ b/ProvisioningTool/include/json/assertions.h @@ -0,0 +1,54 @@ +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef CPPTL_JSON_ASSERTIONS_H_INCLUDED +#define CPPTL_JSON_ASSERTIONS_H_INCLUDED + +#include +#include + +#if !defined(JSON_IS_AMALGAMATION) +#include "config.h" +#endif // if !defined(JSON_IS_AMALGAMATION) + +/** It should not be possible for a maliciously designed file to + * cause an abort() or seg-fault, so these macros are used only + * for pre-condition violations and internal logic errors. + */ +#if JSON_USE_EXCEPTION + +// @todo <= add detail about condition in exception +# define JSON_ASSERT(condition) \ + {if (!(condition)) {Json::throwLogicError( "assert json failed" );}} + +# define JSON_FAIL_MESSAGE(message) \ + { \ + std::ostringstream oss; oss << message; \ + Json::throwLogicError(oss.str()); \ + abort(); \ + } + +#else // JSON_USE_EXCEPTION + +# define JSON_ASSERT(condition) assert(condition) + +// The call to assert() will show the failure message in debug builds. In +// release builds we abort, for a core-dump or debugger. +# define JSON_FAIL_MESSAGE(message) \ + { \ + std::ostringstream oss; oss << message; \ + assert(false && oss.str().c_str()); \ + abort(); \ + } + + +#endif + +#define JSON_ASSERT_MESSAGE(condition, message) \ + if (!(condition)) { \ + JSON_FAIL_MESSAGE(message); \ + } + +#endif // CPPTL_JSON_ASSERTIONS_H_INCLUDED diff --git a/ProvisioningTool/include/json/autolink.h b/ProvisioningTool/include/json/autolink.h new file mode 100644 index 00000000..6fcc8afa --- /dev/null +++ b/ProvisioningTool/include/json/autolink.h @@ -0,0 +1,25 @@ +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef JSON_AUTOLINK_H_INCLUDED +#define JSON_AUTOLINK_H_INCLUDED + +#include "config.h" + +#ifdef JSON_IN_CPPTL +#include +#endif + +#if !defined(JSON_NO_AUTOLINK) && !defined(JSON_DLL_BUILD) && \ + !defined(JSON_IN_CPPTL) +#define CPPTL_AUTOLINK_NAME "json" +#undef CPPTL_AUTOLINK_DLL +#ifdef JSON_DLL +#define CPPTL_AUTOLINK_DLL +#endif +#include "autolink.h" +#endif + +#endif // JSON_AUTOLINK_H_INCLUDED diff --git a/ProvisioningTool/include/json/config.h b/ProvisioningTool/include/json/config.h new file mode 100644 index 00000000..5ca32281 --- /dev/null +++ b/ProvisioningTool/include/json/config.h @@ -0,0 +1,119 @@ +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef JSON_CONFIG_H_INCLUDED +#define JSON_CONFIG_H_INCLUDED + +/// If defined, indicates that json library is embedded in CppTL library. +//# define JSON_IN_CPPTL 1 + +/// If defined, indicates that json may leverage CppTL library +//# define JSON_USE_CPPTL 1 +/// If defined, indicates that cpptl vector based map should be used instead of +/// std::map +/// as Value container. +//# define JSON_USE_CPPTL_SMALLMAP 1 + +// If non-zero, the library uses exceptions to report bad input instead of C +// assertion macros. The default is to use exceptions. +#ifndef JSON_USE_EXCEPTION +#define JSON_USE_EXCEPTION 1 +#endif + +/// If defined, indicates that the source file is amalgated +/// to prevent private header inclusion. +/// Remarks: it is automatically defined in the generated amalgated header. +// #define JSON_IS_AMALGAMATION + +#ifdef JSON_IN_CPPTL +#include +#ifndef JSON_USE_CPPTL +#define JSON_USE_CPPTL 1 +#endif +#endif + +#ifdef JSON_IN_CPPTL +#define JSON_API CPPTL_API +#elif defined(JSON_DLL_BUILD) +#if defined(_MSC_VER) +#define JSON_API __declspec(dllexport) +#define JSONCPP_DISABLE_DLL_INTERFACE_WARNING +#endif // if defined(_MSC_VER) +#elif defined(JSON_DLL) +#if defined(_MSC_VER) +#define JSON_API __declspec(dllimport) +#define JSONCPP_DISABLE_DLL_INTERFACE_WARNING +#endif // if defined(_MSC_VER) +#endif // ifdef JSON_IN_CPPTL +#if !defined(JSON_API) +#define JSON_API +#endif + +#if !defined(JSON_HAS_UNIQUE_PTR) +#if __cplusplus >= 201103L +#define JSON_HAS_UNIQUE_PTR (1) +#elif _MSC_VER >= 1600 +#define JSON_HAS_UNIQUE_PTR (1) +#else +#define JSON_HAS_UNIQUE_PTR (0) +#endif +#endif + +// If JSON_NO_INT64 is defined, then Json only support C++ "int" type for +// integer +// Storages, and 64 bits integer support is disabled. +// #define JSON_NO_INT64 1 + +#if defined(_MSC_VER) && _MSC_VER <= 1200 // MSVC 6 +// Microsoft Visual Studio 6 only support conversion from __int64 to double +// (no conversion from unsigned __int64). +#define JSON_USE_INT64_DOUBLE_CONVERSION 1 +// Disable warning 4786 for VS6 caused by STL (identifier was truncated to '255' +// characters in the debug information) +// All projects I've ever seen with VS6 were using this globally (not bothering +// with pragma push/pop). +#pragma warning(disable : 4786) +#endif // if defined(_MSC_VER) && _MSC_VER < 1200 // MSVC 6 + +#if defined(_MSC_VER) && _MSC_VER >= 1500 // MSVC 2008 +/// Indicates that the following function is deprecated. +#define JSONCPP_DEPRECATED(message) __declspec(deprecated(message)) +#elif defined(__clang__) && defined(__has_feature) +#if __has_feature(attribute_deprecated_with_message) +#define JSONCPP_DEPRECATED(message) __attribute__ ((deprecated(message))) +#endif +#elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)) +#define JSONCPP_DEPRECATED(message) __attribute__ ((deprecated(message))) +#elif defined(__GNUC__) && (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)) +#define JSONCPP_DEPRECATED(message) __attribute__((__deprecated__)) +#endif + +#if !defined(JSONCPP_DEPRECATED) +#define JSONCPP_DEPRECATED(message) +#endif // if !defined(JSONCPP_DEPRECATED) + +namespace Json { +typedef int Int; +typedef unsigned int UInt; +#if defined(JSON_NO_INT64) +typedef int LargestInt; +typedef unsigned int LargestUInt; +#undef JSON_HAS_INT64 +#else // if defined(JSON_NO_INT64) +// For Microsoft Visual use specific types as long long is not supported +#if defined(_MSC_VER) // Microsoft Visual Studio +typedef __int64 Int64; +typedef unsigned __int64 UInt64; +#else // if defined(_MSC_VER) // Other platforms, use long long +typedef long long int Int64; +typedef unsigned long long int UInt64; +#endif // if defined(_MSC_VER) +typedef Int64 LargestInt; +typedef UInt64 LargestUInt; +#define JSON_HAS_INT64 +#endif // if defined(JSON_NO_INT64) +} // end namespace Json + +#endif // JSON_CONFIG_H_INCLUDED diff --git a/ProvisioningTool/include/json/features.h b/ProvisioningTool/include/json/features.h new file mode 100644 index 00000000..78135478 --- /dev/null +++ b/ProvisioningTool/include/json/features.h @@ -0,0 +1,51 @@ +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef CPPTL_JSON_FEATURES_H_INCLUDED +#define CPPTL_JSON_FEATURES_H_INCLUDED + +#if !defined(JSON_IS_AMALGAMATION) +#include "forwards.h" +#endif // if !defined(JSON_IS_AMALGAMATION) + +namespace Json { + +/** \brief Configuration passed to reader and writer. + * This configuration object can be used to force the Reader or Writer + * to behave in a standard conforming way. + */ +class JSON_API Features { +public: + /** \brief A configuration that allows all features and assumes all strings + * are UTF-8. + * - C & C++ comments are allowed + * - Root object can be any JSON value + * - Assumes Value strings are encoded in UTF-8 + */ + static Features all(); + + /** \brief A configuration that is strictly compatible with the JSON + * specification. + * - Comments are forbidden. + * - Root object must be either an array or an object value. + * - Assumes Value strings are encoded in UTF-8 + */ + static Features strictMode(); + + /** \brief Initialize the configuration like JsonConfig::allFeatures; + */ + Features(); + + /// \c true if comments are allowed. Default: \c true. + bool allowComments_; + + /// \c true if root must be either an array or an object value. Default: \c + /// false. + bool strictRoot_; +}; + +} // namespace Json + +#endif // CPPTL_JSON_FEATURES_H_INCLUDED diff --git a/ProvisioningTool/include/json/forwards.h b/ProvisioningTool/include/json/forwards.h new file mode 100644 index 00000000..ccfe09ab --- /dev/null +++ b/ProvisioningTool/include/json/forwards.h @@ -0,0 +1,37 @@ +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef JSON_FORWARDS_H_INCLUDED +#define JSON_FORWARDS_H_INCLUDED + +#if !defined(JSON_IS_AMALGAMATION) +#include "config.h" +#endif // if !defined(JSON_IS_AMALGAMATION) + +namespace Json { + +// writer.h +class FastWriter; +class StyledWriter; + +// reader.h +class Reader; + +// features.h +class Features; + +// value.h +typedef unsigned int ArrayIndex; +class StaticString; +class Path; +class PathArgument; +class Value; +class ValueIteratorBase; +class ValueIterator; +class ValueConstIterator; + +} // namespace Json + +#endif // JSON_FORWARDS_H_INCLUDED diff --git a/ProvisioningTool/include/json/json.h b/ProvisioningTool/include/json/json.h new file mode 100644 index 00000000..8f10ac2b --- /dev/null +++ b/ProvisioningTool/include/json/json.h @@ -0,0 +1,15 @@ +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef JSON_JSON_H_INCLUDED +#define JSON_JSON_H_INCLUDED + +#include "autolink.h" +#include "value.h" +#include "reader.h" +#include "writer.h" +#include "features.h" + +#endif // JSON_JSON_H_INCLUDED diff --git a/ProvisioningTool/include/json/reader.h b/ProvisioningTool/include/json/reader.h new file mode 100644 index 00000000..9c9923a5 --- /dev/null +++ b/ProvisioningTool/include/json/reader.h @@ -0,0 +1,360 @@ +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef CPPTL_JSON_READER_H_INCLUDED +#define CPPTL_JSON_READER_H_INCLUDED + +#if !defined(JSON_IS_AMALGAMATION) +#include "features.h" +#include "value.h" +#endif // if !defined(JSON_IS_AMALGAMATION) +#include +#include +#include +#include +#include + +// Disable warning C4251: : needs to have dll-interface to +// be used by... +#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) +#pragma warning(push) +#pragma warning(disable : 4251) +#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) + +namespace Json { + +/** \brief Unserialize a JSON document into a + *Value. + * + * \deprecated Use CharReader and CharReaderBuilder. + */ +class JSON_API Reader { +public: + typedef char Char; + typedef const Char* Location; + + /** \brief Constructs a Reader allowing all features + * for parsing. + */ + Reader(); + + /** \brief Constructs a Reader allowing the specified feature set + * for parsing. + */ + Reader(const Features& features); + + /** \brief Read a Value from a JSON + * document. + * \param document UTF-8 encoded string containing the document to read. + * \param root [out] Contains the root value of the document if it was + * successfully parsed. + * \param collectComments \c true to collect comment and allow writing them + * back during + * serialization, \c false to discard comments. + * This parameter is ignored if + * Features::allowComments_ + * is \c false. + * \return \c true if the document was successfully parsed, \c false if an + * error occurred. + */ + bool + parse(const std::string& document, Value& root, bool collectComments = true); + + /** \brief Read a Value from a JSON + document. + * \param beginDoc Pointer on the beginning of the UTF-8 encoded string of the + document to read. + * \param endDoc Pointer on the end of the UTF-8 encoded string of the + document to read. + * Must be >= beginDoc. + * \param root [out] Contains the root value of the document if it was + * successfully parsed. + * \param collectComments \c true to collect comment and allow writing them + back during + * serialization, \c false to discard comments. + * This parameter is ignored if + Features::allowComments_ + * is \c false. + * \return \c true if the document was successfully parsed, \c false if an + error occurred. + */ + bool parse(const char* beginDoc, + const char* endDoc, + Value& root, + bool collectComments = true); + + /// \brief Parse from input stream. + /// \see Json::operator>>(std::istream&, Json::Value&). + bool parse(std::istream& is, Value& root, bool collectComments = true); + + /** \brief Returns a user friendly string that list errors in the parsed + * document. + * \return Formatted error message with the list of errors with their location + * in + * the parsed document. An empty string is returned if no error + * occurred + * during parsing. + * \deprecated Use getFormattedErrorMessages() instead (typo fix). + */ + JSONCPP_DEPRECATED("Use getFormattedErrorMessages() instead.") + std::string getFormatedErrorMessages() const; + + /** \brief Returns a user friendly string that list errors in the parsed + * document. + * \return Formatted error message with the list of errors with their location + * in + * the parsed document. An empty string is returned if no error + * occurred + * during parsing. + */ + std::string getFormattedErrorMessages() const; + +private: + enum TokenType { + tokenEndOfStream = 0, + tokenObjectBegin, + tokenObjectEnd, + tokenArrayBegin, + tokenArrayEnd, + tokenString, + tokenNumber, + tokenTrue, + tokenFalse, + tokenNull, + tokenArraySeparator, + tokenMemberSeparator, + tokenComment, + tokenError + }; + + class Token { + public: + TokenType type_; + Location start_; + Location end_; + }; + + class ErrorInfo { + public: + Token token_; + std::string message_; + Location extra_; + }; + + typedef std::deque Errors; + + bool readToken(Token& token); + void skipSpaces(); + bool match(Location pattern, int patternLength); + bool readComment(); + bool readCStyleComment(); + bool readCppStyleComment(); + bool readString(); + void readNumber(); + bool readValue(); + bool readObject(Token& token); + bool readArray(Token& token); + bool decodeNumber(Token& token); + bool decodeNumber(Token& token, Value& decoded); + bool decodeString(Token& token); + bool decodeString(Token& token, std::string& decoded); + bool decodeDouble(Token& token); + bool decodeDouble(Token& token, Value& decoded); + bool decodeUnicodeCodePoint(Token& token, + Location& current, + Location end, + unsigned int& unicode); + bool decodeUnicodeEscapeSequence(Token& token, + Location& current, + Location end, + unsigned int& unicode); + bool addError(const std::string& message, Token& token, Location extra = 0); + bool recoverFromError(TokenType skipUntilToken); + bool addErrorAndRecover(const std::string& message, + Token& token, + TokenType skipUntilToken); + void skipUntilSpace(); + Value& currentValue(); + Char getNextChar(); + void + getLocationLineAndColumn(Location location, int& line, int& column) const; + std::string getLocationLineAndColumn(Location location) const; + void addComment(Location begin, Location end, CommentPlacement placement); + void skipCommentTokens(Token& token); + + typedef std::stack Nodes; + Nodes nodes_; + Errors errors_; + std::string document_; + Location begin_; + Location end_; + Location current_; + Location lastValueEnd_; + Value* lastValue_; + std::string commentsBefore_; + Features features_; + bool collectComments_; +}; // Reader + +/** Interface for reading JSON from a char array. + */ +class JSON_API CharReader { +public: + virtual ~CharReader() {} + /** \brief Read a Value from a JSON + document. + * The document must be a UTF-8 encoded string containing the document to read. + * + * \param beginDoc Pointer on the beginning of the UTF-8 encoded string of the + document to read. + * \param endDoc Pointer on the end of the UTF-8 encoded string of the + document to read. + * Must be >= beginDoc. + * \param root [out] Contains the root value of the document if it was + * successfully parsed. + * \param errs [out] Formatted error messages (if not NULL) + * a user friendly string that lists errors in the parsed + * document. + * \return \c true if the document was successfully parsed, \c false if an + error occurred. + */ + virtual bool parse( + char const* beginDoc, char const* endDoc, + Value* root, std::string* errs) = 0; + + class Factory { + public: + virtual ~Factory() {} + /** \brief Allocate a CharReader via operator new(). + * \throw std::exception if something goes wrong (e.g. invalid settings) + */ + virtual CharReader* newCharReader() const = 0; + }; // Factory +}; // CharReader + +/** \brief Build a CharReader implementation. + +Usage: +\code + using namespace Json; + CharReaderBuilder builder; + builder["collectComments"] = false; + Value value; + std::string errs; + bool ok = parseFromStream(builder, std::cin, &value, &errs); +\endcode +*/ +class JSON_API CharReaderBuilder : public CharReader::Factory { +public: + // Note: We use a Json::Value so that we can add data-members to this class + // without a major version bump. + /** Configuration of this builder. + These are case-sensitive. + Available settings (case-sensitive): + - `"collectComments": false or true` + - true to collect comment and allow writing them + back during serialization, false to discard comments. + This parameter is ignored if allowComments is false. + - `"allowComments": false or true` + - true if comments are allowed. + - `"strictRoot": false or true` + - true if root must be either an array or an object value + - `"allowDroppedNullPlaceholders": false or true` + - true if dropped null placeholders are allowed. (See StreamWriterBuilder.) + - `"allowNumericKeys": false or true` + - true if numeric object keys are allowed. + - `"allowSingleQuotes": false or true` + - true if '' are allowed for strings (both keys and values) + - `"stackLimit": integer` + - Exceeding stackLimit (recursive depth of `readValue()`) will + cause an exception. + - This is a security issue (seg-faults caused by deeply nested JSON), + so the default is low. + - `"failIfExtra": false or true` + - If true, `parse()` returns false when extra non-whitespace trails + the JSON value in the input string. + - `"rejectDupKeys": false or true` + - If true, `parse()` returns false when a key is duplicated within an object. + - `"allowSpecialFloats": false or true` + - If true, special float values (NaNs and infinities) are allowed + and their values are lossfree restorable. + + You can examine 'settings_` yourself + to see the defaults. You can also write and read them just like any + JSON Value. + \sa setDefaults() + */ + Json::Value settings_; + + CharReaderBuilder(); + virtual ~CharReaderBuilder(); + + virtual CharReader* newCharReader() const; + + /** \return true if 'settings' are legal and consistent; + * otherwise, indicate bad settings via 'invalid'. + */ + bool validate(Json::Value* invalid) const; + + /** A simple way to update a specific setting. + */ + Value& operator[](std::string key); + + /** Called by ctor, but you can use this to reset settings_. + * \pre 'settings' != NULL (but Json::null is fine) + * \remark Defaults: + * \snippet src/lib_json/json_reader.cpp CharReaderBuilderDefaults + */ + static void setDefaults(Json::Value* settings); + /** Same as old Features::strictMode(). + * \pre 'settings' != NULL (but Json::null is fine) + * \remark Defaults: + * \snippet src/lib_json/json_reader.cpp CharReaderBuilderStrictMode + */ + static void strictMode(Json::Value* settings); +}; + +/** Consume entire stream and use its begin/end. + * Someday we might have a real StreamReader, but for now this + * is convenient. + */ +bool JSON_API parseFromStream( + CharReader::Factory const&, + std::istream&, + Value* root, std::string* errs); + +/** \brief Read from 'sin' into 'root'. + + Always keep comments from the input JSON. + + This can be used to read a file into a particular sub-object. + For example: + \code + Json::Value root; + cin >> root["dir"]["file"]; + cout << root; + \endcode + Result: + \verbatim + { + "dir": { + "file": { + // The input stream JSON would be nested here. + } + } + } + \endverbatim + \throw std::exception on parse error. + \see Json::operator<<() +*/ +JSON_API std::istream& operator>>(std::istream&, Value&); + +} // namespace Json + +#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) +#pragma warning(pop) +#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) + +#endif // CPPTL_JSON_READER_H_INCLUDED diff --git a/ProvisioningTool/include/json/value.h b/ProvisioningTool/include/json/value.h new file mode 100644 index 00000000..66433f88 --- /dev/null +++ b/ProvisioningTool/include/json/value.h @@ -0,0 +1,850 @@ +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef CPPTL_JSON_H_INCLUDED +#define CPPTL_JSON_H_INCLUDED + +#if !defined(JSON_IS_AMALGAMATION) +#include "forwards.h" +#endif // if !defined(JSON_IS_AMALGAMATION) +#include +#include +#include + +#ifndef JSON_USE_CPPTL_SMALLMAP +#include +#else +#include +#endif +#ifdef JSON_USE_CPPTL +#include +#endif + +// Disable warning C4251: : needs to have dll-interface to +// be used by... +#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) +#pragma warning(push) +#pragma warning(disable : 4251) +#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) + +//Conditional NORETURN attribute on the throw functions would: +// a) suppress false positives from static code analysis +// b) possibly improve optimization opportunities. +#if !defined(JSONCPP_NORETURN) +# if defined(_MSC_VER) +# define JSONCPP_NORETURN __declspec(noreturn) +# elif defined(__GNUC__) +# define JSONCPP_NORETURN __attribute__ ((__noreturn__)) +# else +# define JSONCPP_NORETURN +# endif +#endif + +/** \brief JSON (JavaScript Object Notation). + */ +namespace Json { + +/** Base class for all exceptions we throw. + * + * We use nothing but these internally. Of course, STL can throw others. + */ +class JSON_API Exception : public std::exception { +public: + Exception(std::string const& msg); + virtual ~Exception() throw(); + virtual char const* what() const throw(); +protected: + std::string const msg_; +}; + +/** Exceptions which the user cannot easily avoid. + * + * E.g. out-of-memory (when we use malloc), stack-overflow, malicious input + * + * \remark derived from Json::Exception + */ +class JSON_API RuntimeError : public Exception { +public: + RuntimeError(std::string const& msg); +}; + +/** Exceptions thrown by JSON_ASSERT/JSON_FAIL macros. + * + * These are precondition-violations (user bugs) and internal errors (our bugs). + * + * \remark derived from Json::Exception + */ +class JSON_API LogicError : public Exception { +public: + LogicError(std::string const& msg); +}; + +/// used internally +JSONCPP_NORETURN void throwRuntimeError(std::string const& msg); +/// used internally +JSONCPP_NORETURN void throwLogicError(std::string const& msg); + +/** \brief Type of the value held by a Value object. + */ +enum ValueType { + nullValue = 0, ///< 'null' value + intValue, ///< signed integer value + uintValue, ///< unsigned integer value + realValue, ///< double value + stringValue, ///< UTF-8 string value + booleanValue, ///< bool value + arrayValue, ///< array value (ordered list) + objectValue ///< object value (collection of name/value pairs). +}; + +enum CommentPlacement { + commentBefore = 0, ///< a comment placed on the line before a value + commentAfterOnSameLine, ///< a comment just after a value on the same line + commentAfter, ///< a comment on the line after a value (only make sense for + /// root value) + numberOfCommentPlacement +}; + +//# ifdef JSON_USE_CPPTL +// typedef CppTL::AnyEnumerator EnumMemberNames; +// typedef CppTL::AnyEnumerator EnumValues; +//# endif + +/** \brief Lightweight wrapper to tag static string. + * + * Value constructor and objectValue member assignement takes advantage of the + * StaticString and avoid the cost of string duplication when storing the + * string or the member name. + * + * Example of usage: + * \code + * Json::Value aValue( StaticString("some text") ); + * Json::Value object; + * static const StaticString code("code"); + * object[code] = 1234; + * \endcode + */ +class JSON_API StaticString { +public: + explicit StaticString(const char* czstring) : c_str_(czstring) {} + + operator const char*() const { return c_str_; } + + const char* c_str() const { return c_str_; } + +private: + const char* c_str_; +}; + +/** \brief Represents a JSON value. + * + * This class is a discriminated union wrapper that can represents a: + * - signed integer [range: Value::minInt - Value::maxInt] + * - unsigned integer (range: 0 - Value::maxUInt) + * - double + * - UTF-8 string + * - boolean + * - 'null' + * - an ordered list of Value + * - collection of name/value pairs (javascript object) + * + * The type of the held value is represented by a #ValueType and + * can be obtained using type(). + * + * Values of an #objectValue or #arrayValue can be accessed using operator[]() + * methods. + * Non-const methods will automatically create the a #nullValue element + * if it does not exist. + * The sequence of an #arrayValue will be automatically resized and initialized + * with #nullValue. resize() can be used to enlarge or truncate an #arrayValue. + * + * The get() methods can be used to obtain default value in the case the + * required element does not exist. + * + * It is possible to iterate over the list of a #objectValue values using + * the getMemberNames() method. + * + * \note #Value string-length fit in size_t, but keys must be < 2^30. + * (The reason is an implementation detail.) A #CharReader will raise an + * exception if a bound is exceeded to avoid security holes in your app, + * but the Value API does *not* check bounds. That is the responsibility + * of the caller. + */ +class JSON_API Value { + friend class ValueIteratorBase; +public: + typedef std::vector Members; + typedef ValueIterator iterator; + typedef ValueConstIterator const_iterator; + typedef Json::UInt UInt; + typedef Json::Int Int; +#if defined(JSON_HAS_INT64) + typedef Json::UInt64 UInt64; + typedef Json::Int64 Int64; +#endif // defined(JSON_HAS_INT64) + typedef Json::LargestInt LargestInt; + typedef Json::LargestUInt LargestUInt; + typedef Json::ArrayIndex ArrayIndex; + + static const Value& nullRef; +#if !defined(__ARMEL__) + /// \deprecated This exists for binary compatibility only. Use nullRef. + static const Value null; +#endif + /// Minimum signed integer value that can be stored in a Json::Value. + static const LargestInt minLargestInt; + /// Maximum signed integer value that can be stored in a Json::Value. + static const LargestInt maxLargestInt; + /// Maximum unsigned integer value that can be stored in a Json::Value. + static const LargestUInt maxLargestUInt; + + /// Minimum signed int value that can be stored in a Json::Value. + static const Int minInt; + /// Maximum signed int value that can be stored in a Json::Value. + static const Int maxInt; + /// Maximum unsigned int value that can be stored in a Json::Value. + static const UInt maxUInt; + +#if defined(JSON_HAS_INT64) + /// Minimum signed 64 bits int value that can be stored in a Json::Value. + static const Int64 minInt64; + /// Maximum signed 64 bits int value that can be stored in a Json::Value. + static const Int64 maxInt64; + /// Maximum unsigned 64 bits int value that can be stored in a Json::Value. + static const UInt64 maxUInt64; +#endif // defined(JSON_HAS_INT64) + +//MW: workaround for bug in NVIDIAs CUDA 7.5 nvcc compiler +#ifdef __NVCC__ +public: +#else +private: +#endif //__NVCC__ +#ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION + class CZString { + public: + enum DuplicationPolicy { + noDuplication = 0, + duplicate, + duplicateOnCopy + }; + CZString(ArrayIndex index); + CZString(char const* str, unsigned length, DuplicationPolicy allocate); + CZString(CZString const& other); + ~CZString(); + CZString& operator=(CZString other); + bool operator<(CZString const& other) const; + bool operator==(CZString const& other) const; + ArrayIndex index() const; + //const char* c_str() const; ///< \deprecated + char const* data() const; + unsigned length() const; + bool isStaticString() const; + + private: + void swap(CZString& other); + + struct StringStorage { + unsigned policy_: 2; + unsigned length_: 30; // 1GB max + }; + + char const* cstr_; // actually, a prefixed string, unless policy is noDup + union { + ArrayIndex index_; + StringStorage storage_; + }; + }; + +public: +#ifndef JSON_USE_CPPTL_SMALLMAP + typedef std::map ObjectValues; +#else + typedef CppTL::SmallMap ObjectValues; +#endif // ifndef JSON_USE_CPPTL_SMALLMAP +#endif // ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION + +public: + /** \brief Create a default Value of the given type. + + This is a very useful constructor. + To create an empty array, pass arrayValue. + To create an empty object, pass objectValue. + Another Value can then be set to this one by assignment. +This is useful since clear() and resize() will not alter types. + + Examples: +\code +Json::Value null_value; // null +Json::Value arr_value(Json::arrayValue); // [] +Json::Value obj_value(Json::objectValue); // {} +\endcode + */ + Value(ValueType type = nullValue); + Value(Int value); + Value(UInt value); +#if defined(JSON_HAS_INT64) + Value(Int64 value); + Value(UInt64 value); +#endif // if defined(JSON_HAS_INT64) + Value(double value); + Value(const char* value); ///< Copy til first 0. (NULL causes to seg-fault.) + Value(const char* begin, const char* end); ///< Copy all, incl zeroes. + /** \brief Constructs a value from a static string. + + * Like other value string constructor but do not duplicate the string for + * internal storage. The given string must remain alive after the call to this + * constructor. + * \note This works only for null-terminated strings. (We cannot change the + * size of this class, so we have nowhere to store the length, + * which might be computed later for various operations.) + * + * Example of usage: + * \code + * static StaticString foo("some text"); + * Json::Value aValue(foo); + * \endcode + */ + Value(const StaticString& value); + Value(const std::string& value); ///< Copy data() til size(). Embedded zeroes too. +#ifdef JSON_USE_CPPTL + Value(const CppTL::ConstString& value); +#endif + Value(bool value); + /// Deep copy. + Value(const Value& other); + ~Value(); + + /// Deep copy, then swap(other). + /// \note Over-write existing comments. To preserve comments, use #swapPayload(). + Value &operator=(const Value &other); + /// Swap everything. + void swap(Value& other); + /// Swap values but leave comments and source offsets in place. + void swapPayload(Value& other); + + ValueType type() const; + + /// Compare payload only, not comments etc. + bool operator<(const Value& other) const; + bool operator<=(const Value& other) const; + bool operator>=(const Value& other) const; + bool operator>(const Value& other) const; + bool operator==(const Value& other) const; + bool operator!=(const Value& other) const; + int compare(const Value& other) const; + + const char* asCString() const; ///< Embedded zeroes could cause you trouble! + std::string asString() const; ///< Embedded zeroes are possible. + /** Get raw char* of string-value. + * \return false if !string. (Seg-fault if str or end are NULL.) + */ + bool getString( + char const** begin, char const** end) const; +#ifdef JSON_USE_CPPTL + CppTL::ConstString asConstString() const; +#endif + Int asInt() const; + UInt asUInt() const; +#if defined(JSON_HAS_INT64) + Int64 asInt64() const; + UInt64 asUInt64() const; +#endif // if defined(JSON_HAS_INT64) + LargestInt asLargestInt() const; + LargestUInt asLargestUInt() const; + float asFloat() const; + double asDouble() const; + bool asBool() const; + + bool isNull() const; + bool isBool() const; + bool isInt() const; + bool isInt64() const; + bool isUInt() const; + bool isUInt64() const; + bool isIntegral() const; + bool isDouble() const; + bool isNumeric() const; + bool isString() const; + bool isArray() const; + bool isObject() const; + + bool isConvertibleTo(ValueType other) const; + + /// Number of values in array or object + ArrayIndex size() const; + + /// \brief Return true if empty array, empty object, or null; + /// otherwise, false. + bool empty() const; + + /// Return isNull() + bool operator!() const; + + /// Remove all object members and array elements. + /// \pre type() is arrayValue, objectValue, or nullValue + /// \post type() is unchanged + void clear(); + + /// Resize the array to size elements. + /// New elements are initialized to null. + /// May only be called on nullValue or arrayValue. + /// \pre type() is arrayValue or nullValue + /// \post type() is arrayValue + void resize(ArrayIndex size); + + /// Access an array element (zero based index ). + /// If the array contains less than index element, then null value are + /// inserted + /// in the array so that its size is index+1. + /// (You may need to say 'value[0u]' to get your compiler to distinguish + /// this from the operator[] which takes a string.) + Value& operator[](ArrayIndex index); + + /// Access an array element (zero based index ). + /// If the array contains less than index element, then null value are + /// inserted + /// in the array so that its size is index+1. + /// (You may need to say 'value[0u]' to get your compiler to distinguish + /// this from the operator[] which takes a string.) + Value& operator[](int index); + + /// Access an array element (zero based index ) + /// (You may need to say 'value[0u]' to get your compiler to distinguish + /// this from the operator[] which takes a string.) + const Value& operator[](ArrayIndex index) const; + + /// Access an array element (zero based index ) + /// (You may need to say 'value[0u]' to get your compiler to distinguish + /// this from the operator[] which takes a string.) + const Value& operator[](int index) const; + + /// If the array contains at least index+1 elements, returns the element + /// value, + /// otherwise returns defaultValue. + Value get(ArrayIndex index, const Value& defaultValue) const; + /// Return true if index < size(). + bool isValidIndex(ArrayIndex index) const; + /// \brief Append value to array at the end. + /// + /// Equivalent to jsonvalue[jsonvalue.size()] = value; + Value& append(const Value& value); + + /// Access an object value by name, create a null member if it does not exist. + /// \note Because of our implementation, keys are limited to 2^30 -1 chars. + /// Exceeding that will cause an exception. + Value& operator[](const char* key); + /// Access an object value by name, returns null if there is no member with + /// that name. + const Value& operator[](const char* key) const; + /// Access an object value by name, create a null member if it does not exist. + /// \param key may contain embedded nulls. + Value& operator[](const std::string& key); + /// Access an object value by name, returns null if there is no member with + /// that name. + /// \param key may contain embedded nulls. + const Value& operator[](const std::string& key) const; + /** \brief Access an object value by name, create a null member if it does not + exist. + + * If the object has no entry for that name, then the member name used to store + * the new entry is not duplicated. + * Example of use: + * \code + * Json::Value object; + * static const StaticString code("code"); + * object[code] = 1234; + * \endcode + */ + Value& operator[](const StaticString& key); +#ifdef JSON_USE_CPPTL + /// Access an object value by name, create a null member if it does not exist. + Value& operator[](const CppTL::ConstString& key); + /// Access an object value by name, returns null if there is no member with + /// that name. + const Value& operator[](const CppTL::ConstString& key) const; +#endif + /// Return the member named key if it exist, defaultValue otherwise. + /// \note deep copy + Value get(const char* key, const Value& defaultValue) const; + /// Return the member named key if it exist, defaultValue otherwise. + /// \note deep copy + /// \note key may contain embedded nulls. + Value get(const char* begin, const char* end, const Value& defaultValue) const; + /// Return the member named key if it exist, defaultValue otherwise. + /// \note deep copy + /// \param key may contain embedded nulls. + Value get(const std::string& key, const Value& defaultValue) const; +#ifdef JSON_USE_CPPTL + /// Return the member named key if it exist, defaultValue otherwise. + /// \note deep copy + Value get(const CppTL::ConstString& key, const Value& defaultValue) const; +#endif + /// Most general and efficient version of isMember()const, get()const, + /// and operator[]const + /// \note As stated elsewhere, behavior is undefined if (end-begin) >= 2^30 + Value const* find(char const* begin, char const* end) const; + /// Most general and efficient version of object-mutators. + /// \note As stated elsewhere, behavior is undefined if (end-begin) >= 2^30 + /// \return non-zero, but JSON_ASSERT if this is neither object nor nullValue. + Value const* demand(char const* begin, char const* end); + /// \brief Remove and return the named member. + /// + /// Do nothing if it did not exist. + /// \return the removed Value, or null. + /// \pre type() is objectValue or nullValue + /// \post type() is unchanged + /// \deprecated + Value removeMember(const char* key); + /// Same as removeMember(const char*) + /// \param key may contain embedded nulls. + /// \deprecated + Value removeMember(const std::string& key); + /// Same as removeMember(const char* begin, const char* end, Value* removed), + /// but 'key' is null-terminated. + bool removeMember(const char* key, Value* removed); + /** \brief Remove the named map member. + + Update 'removed' iff removed. + \param key may contain embedded nulls. + \return true iff removed (no exceptions) + */ + bool removeMember(std::string const& key, Value* removed); + /// Same as removeMember(std::string const& key, Value* removed) + bool removeMember(const char* begin, const char* end, Value* removed); + /** \brief Remove the indexed array element. + + O(n) expensive operations. + Update 'removed' iff removed. + \return true iff removed (no exceptions) + */ + bool removeIndex(ArrayIndex i, Value* removed); + + /// Return true if the object has a member named key. + /// \note 'key' must be null-terminated. + bool isMember(const char* key) const; + /// Return true if the object has a member named key. + /// \param key may contain embedded nulls. + bool isMember(const std::string& key) const; + /// Same as isMember(std::string const& key)const + bool isMember(const char* begin, const char* end) const; +#ifdef JSON_USE_CPPTL + /// Return true if the object has a member named key. + bool isMember(const CppTL::ConstString& key) const; +#endif + + /// \brief Return a list of the member names. + /// + /// If null, return an empty list. + /// \pre type() is objectValue or nullValue + /// \post if type() was nullValue, it remains nullValue + Members getMemberNames() const; + + //# ifdef JSON_USE_CPPTL + // EnumMemberNames enumMemberNames() const; + // EnumValues enumValues() const; + //# endif + + /// \deprecated Always pass len. + JSONCPP_DEPRECATED("Use setComment(std::string const&) instead.") + void setComment(const char* comment, CommentPlacement placement); + /// Comments must be //... or /* ... */ + void setComment(const char* comment, size_t len, CommentPlacement placement); + /// Comments must be //... or /* ... */ + void setComment(const std::string& comment, CommentPlacement placement); + bool hasComment(CommentPlacement placement) const; + /// Include delimiters and embedded newlines. + std::string getComment(CommentPlacement placement) const; + + std::string toStyledString() const; + + const_iterator begin() const; + const_iterator end() const; + + iterator begin(); + iterator end(); + +private: + void initBasic(ValueType type, bool allocated = false); + + Value& resolveReference(const char* key); + Value& resolveReference(const char* key, const char* end); + + struct CommentInfo { + CommentInfo(); + ~CommentInfo(); + + void setComment(const char* text, size_t len); + + char* comment_; + }; + + // struct MemberNamesTransform + //{ + // typedef const char *result_type; + // const char *operator()( const CZString &name ) const + // { + // return name.c_str(); + // } + //}; + + union ValueHolder { + LargestInt int_; + LargestUInt uint_; + double real_; + bool bool_; + char* string_; // actually ptr to unsigned, followed by str, unless !allocated_ + ObjectValues* map_; + } value_; + ValueType type_ : 8; + unsigned int allocated_ : 1; // Notes: if declared as bool, bitfield is useless. + // If not allocated_, string_ must be null-terminated. + CommentInfo* comments_; +}; + +/** \brief Experimental and untested: represents an element of the "path" to + * access a node. + */ +class JSON_API PathArgument { +public: + friend class Path; + + PathArgument(); + PathArgument(ArrayIndex index); + PathArgument(const char* key); + PathArgument(const std::string& key); + +private: + enum Kind { + kindNone = 0, + kindIndex, + kindKey + }; + std::string key_; + ArrayIndex index_; + Kind kind_; +}; + +/** \brief Experimental and untested: represents a "path" to access a node. + * + * Syntax: + * - "." => root node + * - ".[n]" => elements at index 'n' of root node (an array value) + * - ".name" => member named 'name' of root node (an object value) + * - ".name1.name2.name3" + * - ".[0][1][2].name1[3]" + * - ".%" => member name is provided as parameter + * - ".[%]" => index is provied as parameter + */ +class JSON_API Path { +public: + Path(const std::string& path, + const PathArgument& a1 = PathArgument(), + const PathArgument& a2 = PathArgument(), + const PathArgument& a3 = PathArgument(), + const PathArgument& a4 = PathArgument(), + const PathArgument& a5 = PathArgument()); + + const Value& resolve(const Value& root) const; + Value resolve(const Value& root, const Value& defaultValue) const; + /// Creates the "path" to access the specified node and returns a reference on + /// the node. + Value& make(Value& root) const; + +private: + typedef std::vector InArgs; + typedef std::vector Args; + + void makePath(const std::string& path, const InArgs& in); + void addPathInArg(const std::string& path, + const InArgs& in, + InArgs::const_iterator& itInArg, + PathArgument::Kind kind); + void invalidPath(const std::string& path, int location); + + Args args_; +}; + +/** \brief base class for Value iterators. + * + */ +class JSON_API ValueIteratorBase { +public: + typedef std::bidirectional_iterator_tag iterator_category; + typedef unsigned int size_t; + typedef int difference_type; + typedef ValueIteratorBase SelfType; + + bool operator==(const SelfType& other) const { return isEqual(other); } + + bool operator!=(const SelfType& other) const { return !isEqual(other); } + + difference_type operator-(const SelfType& other) const { + return other.computeDistance(*this); + } + + /// Return either the index or the member name of the referenced value as a + /// Value. + Value key() const; + + /// Return the index of the referenced Value, or -1 if it is not an arrayValue. + UInt index() const; + + /// Return the member name of the referenced Value, or "" if it is not an + /// objectValue. + /// \note Avoid `c_str()` on result, as embedded zeroes are possible. + std::string name() const; + + /// Return the member name of the referenced Value. "" if it is not an + /// objectValue. + /// \deprecated This cannot be used for UTF-8 strings, since there can be embedded nulls. + JSONCPP_DEPRECATED("Use `key = name();` instead.") + char const* memberName() const; + /// Return the member name of the referenced Value, or NULL if it is not an + /// objectValue. + /// \note Better version than memberName(). Allows embedded nulls. + char const* memberName(char const** end) const; + +protected: + Value& deref() const; + + void increment(); + + void decrement(); + + difference_type computeDistance(const SelfType& other) const; + + bool isEqual(const SelfType& other) const; + + void copy(const SelfType& other); + +private: + Value::ObjectValues::iterator current_; + // Indicates that iterator is for a null value. + bool isNull_; + +public: + // For some reason, BORLAND needs these at the end, rather + // than earlier. No idea why. + ValueIteratorBase(); + explicit ValueIteratorBase(const Value::ObjectValues::iterator& current); +}; + +/** \brief const iterator for object and array value. + * + */ +class JSON_API ValueConstIterator : public ValueIteratorBase { + friend class Value; + +public: + typedef const Value value_type; + //typedef unsigned int size_t; + //typedef int difference_type; + typedef const Value& reference; + typedef const Value* pointer; + typedef ValueConstIterator SelfType; + + ValueConstIterator(); + +private: +/*! \internal Use by Value to create an iterator. + */ + explicit ValueConstIterator(const Value::ObjectValues::iterator& current); +public: + SelfType& operator=(const ValueIteratorBase& other); + + SelfType operator++(int) { + SelfType temp(*this); + ++*this; + return temp; + } + + SelfType operator--(int) { + SelfType temp(*this); + --*this; + return temp; + } + + SelfType& operator--() { + decrement(); + return *this; + } + + SelfType& operator++() { + increment(); + return *this; + } + + reference operator*() const { return deref(); } + + pointer operator->() const { return &deref(); } +}; + +/** \brief Iterator for object and array value. + */ +class JSON_API ValueIterator : public ValueIteratorBase { + friend class Value; + +public: + typedef Value value_type; + typedef unsigned int size_t; + typedef int difference_type; + typedef Value& reference; + typedef Value* pointer; + typedef ValueIterator SelfType; + + ValueIterator(); + ValueIterator(const ValueConstIterator& other); + ValueIterator(const ValueIterator& other); + +private: +/*! \internal Use by Value to create an iterator. + */ + explicit ValueIterator(const Value::ObjectValues::iterator& current); +public: + SelfType& operator=(const SelfType& other); + + SelfType operator++(int) { + SelfType temp(*this); + ++*this; + return temp; + } + + SelfType operator--(int) { + SelfType temp(*this); + --*this; + return temp; + } + + SelfType& operator--() { + decrement(); + return *this; + } + + SelfType& operator++() { + increment(); + return *this; + } + + reference operator*() const { return deref(); } + + pointer operator->() const { return &deref(); } +}; + +} // namespace Json + + +namespace std { +/// Specialize std::swap() for Json::Value. +template<> +inline void swap(Json::Value& a, Json::Value& b) { a.swap(b); } +} + + +#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) +#pragma warning(pop) +#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) + +#endif // CPPTL_JSON_H_INCLUDED diff --git a/ProvisioningTool/include/json/version.h b/ProvisioningTool/include/json/version.h new file mode 100644 index 00000000..d0f3dcb0 --- /dev/null +++ b/ProvisioningTool/include/json/version.h @@ -0,0 +1,13 @@ +// DO NOT EDIT. This file (and "version") is generated by CMake. +// Run CMake configure step to update it. +#ifndef JSON_VERSION_H_INCLUDED +# define JSON_VERSION_H_INCLUDED + +# define JSONCPP_VERSION_STRING "0.10.7" +# define JSONCPP_VERSION_MAJOR 0 +# define JSONCPP_VERSION_MINOR 10 +# define JSONCPP_VERSION_PATCH 7 +# define JSONCPP_VERSION_QUALIFIER +# define JSONCPP_VERSION_HEXA ((JSONCPP_VERSION_MAJOR << 24) | (JSONCPP_VERSION_MINOR << 16) | (JSONCPP_VERSION_PATCH << 8)) + +#endif // JSON_VERSION_H_INCLUDED diff --git a/ProvisioningTool/include/json/writer.h b/ProvisioningTool/include/json/writer.h new file mode 100644 index 00000000..a7fd11d2 --- /dev/null +++ b/ProvisioningTool/include/json/writer.h @@ -0,0 +1,320 @@ +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef JSON_WRITER_H_INCLUDED +#define JSON_WRITER_H_INCLUDED + +#if !defined(JSON_IS_AMALGAMATION) +#include "value.h" +#endif // if !defined(JSON_IS_AMALGAMATION) +#include +#include +#include + +// Disable warning C4251: : needs to have dll-interface to +// be used by... +#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) +#pragma warning(push) +#pragma warning(disable : 4251) +#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) + +namespace Json { + +class Value; + +/** + +Usage: +\code + using namespace Json; + void writeToStdout(StreamWriter::Factory const& factory, Value const& value) { + std::unique_ptr const writer( + factory.newStreamWriter()); + writer->write(value, &std::cout); + std::cout << std::endl; // add lf and flush + } +\endcode +*/ +class JSON_API StreamWriter { +protected: + std::ostream* sout_; // not owned; will not delete +public: + StreamWriter(); + virtual ~StreamWriter(); + /** Write Value into document as configured in sub-class. + Do not take ownership of sout, but maintain a reference during function. + \pre sout != NULL + \return zero on success (For now, we always return zero, so check the stream instead.) + \throw std::exception possibly, depending on configuration + */ + virtual int write(Value const& root, std::ostream* sout) = 0; + + /** \brief A simple abstract factory. + */ + class JSON_API Factory { + public: + virtual ~Factory(); + /** \brief Allocate a CharReader via operator new(). + * \throw std::exception if something goes wrong (e.g. invalid settings) + */ + virtual StreamWriter* newStreamWriter() const = 0; + }; // Factory +}; // StreamWriter + +/** \brief Write into stringstream, then return string, for convenience. + * A StreamWriter will be created from the factory, used, and then deleted. + */ +std::string JSON_API writeString(StreamWriter::Factory const& factory, Value const& root); + + +/** \brief Build a StreamWriter implementation. + +Usage: +\code + using namespace Json; + Value value = ...; + StreamWriterBuilder builder; + builder["commentStyle"] = "None"; + builder["indentation"] = " "; // or whatever you like + std::unique_ptr writer( + builder.newStreamWriter()); + writer->write(value, &std::cout); + std::cout << std::endl; // add lf and flush +\endcode +*/ +class JSON_API StreamWriterBuilder : public StreamWriter::Factory { +public: + // Note: We use a Json::Value so that we can add data-members to this class + // without a major version bump. + /** Configuration of this builder. + Available settings (case-sensitive): + - "commentStyle": "None" or "All" + - "indentation": "" + - "enableYAMLCompatibility": false or true + - slightly change the whitespace around colons + - "dropNullPlaceholders": false or true + - Drop the "null" string from the writer's output for nullValues. + Strictly speaking, this is not valid JSON. But when the output is being + fed to a browser's Javascript, it makes for smaller output and the + browser can handle the output just fine. + - "useSpecialFloats": false or true + - If true, outputs non-finite floating point values in the following way: + NaN values as "NaN", positive infinity as "Infinity", and negative infinity + as "-Infinity". + + You can examine 'settings_` yourself + to see the defaults. You can also write and read them just like any + JSON Value. + \sa setDefaults() + */ + Json::Value settings_; + + StreamWriterBuilder(); + virtual ~StreamWriterBuilder(); + + /** + * \throw std::exception if something goes wrong (e.g. invalid settings) + */ + virtual StreamWriter* newStreamWriter() const; + + /** \return true if 'settings' are legal and consistent; + * otherwise, indicate bad settings via 'invalid'. + */ + bool validate(Json::Value* invalid) const; + /** A simple way to update a specific setting. + */ + Value& operator[](std::string key); + + /** Called by ctor, but you can use this to reset settings_. + * \pre 'settings' != NULL (but Json::null is fine) + * \remark Defaults: + * \snippet src/lib_json/json_writer.cpp StreamWriterBuilderDefaults + */ + static void setDefaults(Json::Value* settings); +}; + +/** \brief Abstract class for writers. + * \deprecated Use StreamWriter. (And really, this is an implementation detail.) + */ +class JSON_API Writer { +public: + virtual ~Writer(); + + virtual std::string write(const Value& root) = 0; +}; + +/** \brief Outputs a Value in JSON format + *without formatting (not human friendly). + * + * The JSON document is written in a single line. It is not intended for 'human' + *consumption, + * but may be usefull to support feature such as RPC where bandwith is limited. + * \sa Reader, Value + * \deprecated Use StreamWriterBuilder. + */ +class JSON_API FastWriter : public Writer { + +public: + FastWriter(); + virtual ~FastWriter() {} + + void enableYAMLCompatibility(); + +public: // overridden from Writer + virtual std::string write(const Value& root); + +private: + void writeValue(const Value& value); + + std::string document_; + bool yamlCompatiblityEnabled_; +}; + +/** \brief Writes a Value in JSON format in a + *human friendly way. + * + * The rules for line break and indent are as follow: + * - Object value: + * - if empty then print {} without indent and line break + * - if not empty the print '{', line break & indent, print one value per + *line + * and then unindent and line break and print '}'. + * - Array value: + * - if empty then print [] without indent and line break + * - if the array contains no object value, empty array or some other value + *types, + * and all the values fit on one lines, then print the array on a single + *line. + * - otherwise, it the values do not fit on one line, or the array contains + * object or non empty array, then print one value per line. + * + * If the Value have comments then they are outputed according to their + *#CommentPlacement. + * + * \sa Reader, Value, Value::setComment() + * \deprecated Use StreamWriterBuilder. + */ +class JSON_API StyledWriter : public Writer { +public: + StyledWriter(); + virtual ~StyledWriter() {} + +public: // overridden from Writer + /** \brief Serialize a Value in JSON format. + * \param root Value to serialize. + * \return String containing the JSON document that represents the root value. + */ + virtual std::string write(const Value& root); + +private: + void writeValue(const Value& value); + void writeArrayValue(const Value& value); + bool isMultineArray(const Value& value); + void pushValue(const std::string& value); + void writeIndent(); + void writeWithIndent(const std::string& value); + void indent(); + void unindent(); + void writeCommentBeforeValue(const Value& root); + void writeCommentAfterValueOnSameLine(const Value& root); + bool hasCommentForValue(const Value& value); + static std::string normalizeEOL(const std::string& text); + + typedef std::vector ChildValues; + + ChildValues childValues_; + std::string document_; + std::string indentString_; + int rightMargin_; + int indentSize_; + bool addChildValues_; +}; + +/** \brief Writes a Value in JSON format in a + human friendly way, + to a stream rather than to a string. + * + * The rules for line break and indent are as follow: + * - Object value: + * - if empty then print {} without indent and line break + * - if not empty the print '{', line break & indent, print one value per + line + * and then unindent and line break and print '}'. + * - Array value: + * - if empty then print [] without indent and line break + * - if the array contains no object value, empty array or some other value + types, + * and all the values fit on one lines, then print the array on a single + line. + * - otherwise, it the values do not fit on one line, or the array contains + * object or non empty array, then print one value per line. + * + * If the Value have comments then they are outputed according to their + #CommentPlacement. + * + * \param indentation Each level will be indented by this amount extra. + * \sa Reader, Value, Value::setComment() + * \deprecated Use StreamWriterBuilder. + */ +class JSON_API StyledStreamWriter { +public: + StyledStreamWriter(std::string indentation = "\t"); + ~StyledStreamWriter() {} + +public: + /** \brief Serialize a Value in JSON format. + * \param out Stream to write to. (Can be ostringstream, e.g.) + * \param root Value to serialize. + * \note There is no point in deriving from Writer, since write() should not + * return a value. + */ + void write(std::ostream& out, const Value& root); + +private: + void writeValue(const Value& value); + void writeArrayValue(const Value& value); + bool isMultineArray(const Value& value); + void pushValue(const std::string& value); + void writeIndent(); + void writeWithIndent(const std::string& value); + void indent(); + void unindent(); + void writeCommentBeforeValue(const Value& root); + void writeCommentAfterValueOnSameLine(const Value& root); + bool hasCommentForValue(const Value& value); + static std::string normalizeEOL(const std::string& text); + + typedef std::vector ChildValues; + + ChildValues childValues_; + std::ostream* document_; + std::string indentString_; + int rightMargin_; + std::string indentation_; + bool addChildValues_ : 1; + bool indented_ : 1; +}; + +#if defined(JSON_HAS_INT64) +std::string JSON_API valueToString(Int value); +std::string JSON_API valueToString(UInt value); +#endif // if defined(JSON_HAS_INT64) +std::string JSON_API valueToString(LargestInt value); +std::string JSON_API valueToString(LargestUInt value); +std::string JSON_API valueToString(double value); +std::string JSON_API valueToString(bool value); +std::string JSON_API valueToQuotedString(const char* value); + +/// \brief Output using the StyledStreamWriter. +/// \see Json::operator>>() +JSON_API std::ostream& operator<<(std::ostream&, const Value& root); + +} // namespace Json + +#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) +#pragma warning(pop) +#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) + +#endif // JSON_WRITER_H_INCLUDED diff --git a/ProvisioningTool/include/socket.h b/ProvisioningTool/include/socket.h new file mode 100644 index 00000000..8f47325a --- /dev/null +++ b/ProvisioningTool/include/socket.h @@ -0,0 +1,53 @@ +/* + ** + ** Copyright 2021, 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. + */ +#pragma once + +class SocketTransport +{ +public: + static inline std::shared_ptr getInstance() { + static std::shared_ptr socket = std::shared_ptr(new SocketTransport()); + return socket; + } + + ~SocketTransport(); + /** + * Creates a socket instance and connects to the provided server IP and port. + */ + bool openConnection(); + /** + * Sends data over socket and receives data back. + */ + bool sendData(const std::vector &inData, std::vector &output); + /** + * Closes the connection. + */ + bool closeConnection(); + /** + * Returns the state of the connection status. Returns true if the connection is active, + * false if connection is broken. + */ + bool isConnected(); + +private: + SocketTransport() : mSocket(-1), socketStatus(false) {} + /** + * Socket instance. + */ + int mSocket; + bool socketStatus; +}; \ No newline at end of file diff --git a/ProvisioningTool/include/utils.h b/ProvisioningTool/include/utils.h new file mode 100644 index 00000000..9eb991bd --- /dev/null +++ b/ProvisioningTool/include/utils.h @@ -0,0 +1,30 @@ +/* + ** + ** Copyright 2021, 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. + */ +#pragma once +#include +#include +#include +#include +#include + +std::string getHexString(std::vector& input); + +std::string hex2str(std::string a); + +int readJsonFile(Json::Value& root, std::string& inputFileName); + +int writeJsonFile(Json::Value& writerRoot, std::string& outputFileName); \ No newline at end of file diff --git a/ProvisioningTool/lib/README.md b/ProvisioningTool/lib/README.md new file mode 100644 index 00000000..d9ac780e --- /dev/null +++ b/ProvisioningTool/lib/README.md @@ -0,0 +1,25 @@ +# Instructions to build jsoncpp +Download the code from below opensource link: +https://github.com/open-source-parsers/jsoncpp/tree/0.y.z + +#### Unzip it +
+unzip jsoncpp-0.y.z.zip
+cd jsoncpp-0.y.z
+
+ +#### Build +
+$ mkdir -p build/debug
+$ cd build/debug
+$ cmake -DCMAKE_BUILD_TYPE=debug -DBUILD_STATIC_LIBS=ON -DBUILD_SHARED_LIBS=ON -DARCHIVE_INSTALL_DIR=. -G "Unix Makefiles" ../..
+$ make
+
+ +#### Check the generated static and dynamic link library +
+$ find . -name *.a
+./src/lib_json/libjsoncpp.a
+$ find . -name *.so
+./src/lib_json/libjsoncpp.so
+
diff --git a/ProvisioningTool/lib/libjsoncpp.a b/ProvisioningTool/lib/libjsoncpp.a new file mode 100644 index 00000000..601854f4 Binary files /dev/null and b/ProvisioningTool/lib/libjsoncpp.a differ diff --git a/ProvisioningTool/lib/libjsoncpp.so b/ProvisioningTool/lib/libjsoncpp.so new file mode 100755 index 00000000..a23ae279 Binary files /dev/null and b/ProvisioningTool/lib/libjsoncpp.so differ diff --git a/ProvisioningTool/lib/libjsoncpp.so.0 b/ProvisioningTool/lib/libjsoncpp.so.0 new file mode 100755 index 00000000..a23ae279 Binary files /dev/null and b/ProvisioningTool/lib/libjsoncpp.so.0 differ diff --git a/ProvisioningTool/lib/libjsoncpp.so.0.10.7 b/ProvisioningTool/lib/libjsoncpp.so.0.10.7 new file mode 100755 index 00000000..a23ae279 Binary files /dev/null and b/ProvisioningTool/lib/libjsoncpp.so.0.10.7 differ diff --git a/ProvisioningTool/sample_json_gf.txt b/ProvisioningTool/sample_json_gf.txt deleted file mode 100644 index 7825e693..00000000 --- a/ProvisioningTool/sample_json_gf.txt +++ /dev/null @@ -1,26 +0,0 @@ -{ - "attest_ids": { - "brand": "Android", - "device": "generic_x86_64", - "product": "aosp_x86_64", - "serial": "", - "imei": "000000000000000", - "meid": "000000000000000", - "manufacturer": "unknown", - "model": "AOSP on x86_64" - }, - "shared_secret": "0000000000000000000000000000000000000000000000000000000000000000", - "set_boot_params": { - "boot_patch_level": 0, - "verified_boot_key": "0000000000000000000000000000000000000000000000000000000000000000", - "verified_boot_key_hash": "0000000000000000000000000000000000000000000000000000000000000000", - "boot_state": 2, - "device_locked": 1 - }, - "attest_key": "/data/vendor/batch_key.der", - "attest_cert_chain": [ - "/data/vendor/batch_cert.der", - "/data/vendor/intermediate_cert.der", - "/data/vendor/ca_cert.der" - ] -} diff --git a/ProvisioningTool/sample_json_cf.txt b/ProvisioningTool/sample_json_keymint_cf.txt similarity index 75% rename from ProvisioningTool/sample_json_cf.txt rename to ProvisioningTool/sample_json_keymint_cf.txt index fd79e227..70f9c71e 100644 --- a/ProvisioningTool/sample_json_cf.txt +++ b/ProvisioningTool/sample_json_keymint_cf.txt @@ -17,10 +17,12 @@ "boot_state": 2, "device_locked": 1 }, - "attest_key": "/data/vendor/batch_key.der", - "attest_cert_chain": [ - "/data/vendor/batch_cert.der", - "/data/vendor/intermediate_cert.der", - "/data/vendor/ca_cert.der" - ] + "device_unique_key": "test_resources/batch_key.der", + "signer_info": { + "signer_name": "Google", + "signing_keys": [ + "test_resources/ca_key.der", + "test_resources/intermediate_key.der" + ] + } } diff --git a/ProvisioningTool/src/construct_apdus.cpp b/ProvisioningTool/src/construct_apdus.cpp new file mode 100644 index 00000000..190e3a78 --- /dev/null +++ b/ProvisioningTool/src/construct_apdus.cpp @@ -0,0 +1,612 @@ +/* + ** + ** Copyright 2021, 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 +#include +#include +#include +#include "cppbor/cppbor.h" +#include "cppcose/cppcose.h" + +// static globals. +static std::string inputFileName; +static std::string outputFileName; +Json::Value root; +Json::Value writerRoot; + +using namespace std; +using cppbor::Array; +using cppbor::Map; +using cppbor::Bstr; +using cppcose::CoseKey; +using cppcose::EC2; +using cppcose::ES256; +using cppcose::P256; +using cppcose::SIGN; +using cppcose::bytevec; + + +// static function declarations +static int processInputFile(); +static int ecRawKeyFromPKCS8(const std::vector& pkcs8Blob, std::vector& secret, + std::vector& pub_x, std::vector& pub_y); +static int processAttestationIds(); +static int processSharedSecret(); +static int processSetBootParameters(); +static int readDataFromFile(const char *fileName, std::vector& data); +static int addApduHeader(const int ins, std::vector& inputData); +static int getIntValue(Json::Value& Obj, const char* key, uint32_t *value); +static int getBlobValue(Json::Value& Obj, const char* key, std::vector& blob); +static int getStringValue(Json::Value& Obj, const char* key, std::string& str); +static int processDeviceUniqueKey(); +static int processAdditionalCertificateChain(); +static int getDeviceUniqueKey(bytevec& privKey, bytevec& x, bytevec& y); + + +// Print usage. +void usage() { + printf("Usage: Please give json files with values as input to generate the apdus command. Please refer to sample_json files available in the folder for reference. Sample json files are written using hardcode parameters to be used for testing setup on cuttlefilsh emulator and goldfish emulators\n"); + printf("construct_keymint_apdus [options]\n"); + printf("Valid options are:\n"); + printf("-h, --help show this help message and exit.\n"); + printf("-i, --input jsonFile \t Input json file \n"); + printf("-o, --output jsonFile \t Output json file \n"); +} + + +int ecRawKeyFromPKCS8(const std::vector& pkcs8Blob, std::vector& secret, + std::vector& pub_x, std::vector& pub_y) { + const uint8_t *data = pkcs8Blob.data(); + EVP_PKEY *evpkey = d2i_PrivateKey(EVP_PKEY_EC, nullptr, &data, pkcs8Blob.size()); + if(!evpkey) { + printf("\n Failed to decode private key from PKCS8, Error: %ld", ERR_peek_last_error()); + return FAILURE; + } + EVP_PKEY_Ptr pkey(evpkey); + + EC_KEY_Ptr ec_key(EVP_PKEY_get1_EC_KEY(pkey.get())); + if(!ec_key.get()) { + printf("\n Failed to create EC_KEY, Error: %ld", ERR_peek_last_error()); + return FAILURE; + } + + //Get EC Group + const EC_GROUP *group = EC_KEY_get0_group(ec_key.get()); + if(group == NULL) { + printf("\n Failed to get the EC_GROUP from ec_key."); + return FAILURE; + } + + //Extract private key. + const BIGNUM *privBn = EC_KEY_get0_private_key(ec_key.get()); + int privKeyLen = BN_num_bytes(privBn); + std::unique_ptr privKey(new uint8_t[privKeyLen]); + BN_bn2bin(privBn, privKey.get()); + secret.insert(secret.begin(), privKey.get(), privKey.get()+privKeyLen); + + //Extract public key. + BIGNUM_Ptr x(BN_new()); + BIGNUM_Ptr y(BN_new()); + std::vector dataX(kAffinePointLength); + std::vector dataY(kAffinePointLength); + BN_CTX_Ptr ctx(BN_CTX_new()); + if (ctx == nullptr) { + printf("\nFailed to get BN_CTX \n"); + return FAILURE; + } + const EC_POINT *point = EC_KEY_get0_public_key(ec_key.get()); + + if (!EC_POINT_get_affine_coordinates_GFp(EC_KEY_get0_group(ec_key.get()), point, x.get(), + y.get(), ctx.get())) { + printf("\nFailed to get affine coordinates\n"); + return FAILURE; + } + if (BN_bn2binpad(x.get(), dataX.data(), kAffinePointLength) != kAffinePointLength) { + printf("\nFailed to get x coordinate\n"); + return FAILURE; + } + if (BN_bn2binpad(y.get(), dataY.data(), kAffinePointLength) != kAffinePointLength) { + printf("\nFailed to get y coordinate\n"); + return FAILURE; + } + pub_x = dataX; + pub_y = dataY; + return SUCCESS; +} + +int getIntValue(Json::Value& bootParamsObj, const char* key, uint32_t *value) { + Json::Value val = bootParamsObj[key]; + if(val.empty()) + return FAILURE; + + if(!val.isInt()) + return FAILURE; + + *value = (uint32_t)val.asInt(); + + return SUCCESS; +} + +int getStringValue(Json::Value& Obj, const char* key, std::string& str) { + Json::Value val = Obj[key]; + if(val.empty()) + return FAILURE; + + if(!val.isString()) + return FAILURE; + + str = val.asString(); + + return SUCCESS; + +} + +int getBlobValue(Json::Value& bootParamsObj, const char* key, std::vector& blob) { + Json::Value val = bootParamsObj[key]; + if(val.empty()) + return FAILURE; + + if(!val.isString()) + return FAILURE; + + std::string blobStr = hex2str(val.asString()); + + for(char ch : blobStr) { + blob.push_back((uint8_t)ch); + } + + return SUCCESS; +} + + +// Parses the input json file. Prepares the apdu for each entry in the json +// file and dump all the apdus into the output json file. +int processInputFile() { + + // Parse Json file + if (0 != readJsonFile(root, inputFileName)) { + return FAILURE; + } + if (0 != processDeviceUniqueKey() || + 0 != processAdditionalCertificateChain() || + 0 != processAttestationIds() || + 0 != processSharedSecret() || + 0 != processSetBootParameters()) { + return FAILURE; + } + if (SUCCESS != writeJsonFile(writerRoot, outputFileName)) { + return FAILURE; + } + printf("\n Successfully written json to outfile: %s\n ", outputFileName.c_str()); + return SUCCESS; +} + +int processAdditionalCertificateChain() { + Json::Value signerInfo = root.get(kSignerInfo, Json::Value::nullRef); + if (!signerInfo.isNull()) { + std::string signerName; + std::string signingKeyFile; + std::vector previousKey; + Array array; + + if (SUCCESS != getStringValue(signerInfo, "signer_name", signerName)) { + printf("\n Improper value for signer_name in json file \n"); + return FAILURE; + } + + Json::Value keys = signerInfo.get("signing_keys", Json::Value::nullRef); + if (!keys.isNull()) { + if (!keys.isArray()) { + printf("\n Improper value for signing_keys in json file \n"); + return FAILURE; + } + for(uint32_t i = 0; i < keys.size(); i++) { + std::vector data; + std::vector privateKey; + std::vector x_coord; + std::vector y_coord; + + if (!keys[i].isString()) { + printf("\n Improper value for signing_keys in json file \n"); + return FAILURE; + } + + if(SUCCESS != readDataFromFile(keys[i].asString().data(), data)) { + printf("\n Failed to read the attestation key from the file.\n"); + return FAILURE; + } + if (SUCCESS != ecRawKeyFromPKCS8(data, privateKey, x_coord, y_coord)) { + return FAILURE; + } + + if (i == 0) { + // self-signed. + previousKey = privateKey; + } + + auto rootCoseSign = + cppcose::constructCoseSign1(previousKey, /* Signing key */ + cppbor::Map() /* Payload CoseKey */ + .add(CoseKey::KEY_TYPE, EC2) + .add(CoseKey::ALGORITHM, ES256) + .add(CoseKey::CURVE, P256) + .add(CoseKey::KEY_OPS, SIGN) + .add(CoseKey::PUBKEY_X, x_coord) + .add(CoseKey::PUBKEY_Y, y_coord) + .canonicalize() + .encode(), + {} /* AAD */); + if (!rootCoseSign) { + printf("\n Failed to construct CoseSign1 %s\n", rootCoseSign.moveMessage().c_str()); + return FAILURE; + } + + // Add to cbor array + array.add(rootCoseSign.moveValue()); + previousKey = privateKey; + } + } + + std::vector dk_priv; + std::vector dk_pub_x; + std::vector dk_pub_y; + if (SUCCESS == getDeviceUniqueKey(dk_priv, dk_pub_x, dk_pub_y)) { + auto dkCoseSign = + cppcose::constructCoseSign1(previousKey, /* Signing key */ + cppbor::Map() /* Payload CoseKey */ + .add(CoseKey::KEY_TYPE, EC2) + .add(CoseKey::ALGORITHM, ES256) + .add(CoseKey::CURVE, P256) + .add(CoseKey::KEY_OPS, SIGN) + .add(CoseKey::PUBKEY_X, dk_pub_x) + .add(CoseKey::PUBKEY_Y, dk_pub_y) + .canonicalize() + .encode(), + {} /* AAD */); + if (!dkCoseSign) { + printf("\n Failed to construct CoseSign1 %s\n", dkCoseSign.moveMessage().c_str()); + return FAILURE; + } + array.add(dkCoseSign.moveValue()); + std::vector cborData = Map().add(signerName, std::move(array)).encode(); + if(SUCCESS != addApduHeader(kAdditionalCertChainCmd, cborData)) { + return FAILURE; + } + // Write to json. + writerRoot[kAdditionalCertChain] = getHexString(cborData); + } else { + return FAILURE; + } + + } else { + printf("\n Improper value for signer_info in json file \n"); + return FAILURE; + } + printf("\n Constructed additional cert chain APDU successfully. \n"); + return SUCCESS; +} + +int getDeviceUniqueKey(bytevec& privKey, bytevec& x, bytevec& y) { + Json::Value keyFile = root.get(kDeviceUniqueKey, Json::Value::nullRef); + if (!keyFile.isNull()) { + std::vector data; + + std::string keyFileName = keyFile.asString(); + if(SUCCESS != readDataFromFile(keyFileName.data(), data)) { + printf("\n Failed to read the attestation key from the file.\n"); + return FAILURE; + } + if (SUCCESS != ecRawKeyFromPKCS8(data, privKey, x, y)) { + return FAILURE; + } + } else { + printf("\n Improper value for device_unique_key in json file \n"); + return FAILURE; + } + return SUCCESS; +} + +int processDeviceUniqueKey() { + std::vector privateKey; + std::vector x_coord; + std::vector y_coord; + if (SUCCESS == getDeviceUniqueKey(privateKey, x_coord, y_coord)) { + // Construct COSE_Key + cppbor::Map cose_public_key_map = cppbor::Map() + .add(CoseKey::KEY_TYPE, EC2) + .add(CoseKey::ALGORITHM, ES256) + .add(CoseKey::CURVE, P256) + .add(CoseKey::KEY_OPS, SIGN) + .add(CoseKey::PUBKEY_X, x_coord) + .add(CoseKey::PUBKEY_Y, y_coord) + .add(CoseKey::PRIVATE_KEY, privateKey); + + Array array; + array.add(std::move(cose_public_key_map.canonicalize())); + std::vector cborData = array.encode(); + + if(SUCCESS != addApduHeader(kDeviceUniqueKeyCmd, cborData)) { + return FAILURE; + } + // Write to json. + writerRoot[kDeviceUniqueKey] = getHexString(cborData); + + } else { + return FAILURE; + } + printf("\n Constructed device unique key APDU successfully. \n"); + return SUCCESS; +} + + +int processAttestationIds() { + //AttestIDParams params; + Json::Value attestIds = root.get("attest_ids", Json::Value::nullRef); + if (!attestIds.isNull()) { + Json::Value value; + Map map; + Json::Value::Members keys = attestIds.getMemberNames(); + for(std::string key : keys) { + value = attestIds[key]; + if(value.empty()) { + continue; + } + if (!value.isString()) { + printf("\n Fail: Value for each attest ids key should be a string in the json file \n"); + return FAILURE; + } + std::string idVal = value.asString(); + if (0 == key.compare("brand")) { + map.add(kTagAttestationIdBrand, std::vector(idVal.begin(), idVal.end())); + } else if(0 == key.compare("device")) { + map.add(kTagAttestationIdDevice, std::vector(idVal.begin(), idVal.end())); + } else if(0 == key.compare("product")) { + map.add(kTagAttestationIdProduct, std::vector(idVal.begin(), idVal.end())); + } else if(0 == key.compare("serial")) { + map.add(kTagAttestationIdSerial, std::vector(idVal.begin(), idVal.end())); + } else if(0 == key.compare("imei")) { + map.add(kTagAttestationIdImei, std::vector(idVal.begin(), idVal.end())); + } else if(0 == key.compare("meid")) { + map.add(kTagAttestationIdMeid, std::vector(idVal.begin(), idVal.end())); + } else if(0 == key.compare("manufacturer")) { + map.add(kTagAttestationIdManufacturer, std::vector(idVal.begin(), idVal.end())); + } else if(0 == key.compare("model")) { + map.add(kTagAttestationIdModel, std::vector(idVal.begin(), idVal.end())); + } else { + printf("\n unknown attestation id key:%s \n", key.c_str()); + return FAILURE; + } + } + + //------------------------- + // construct cbor input. + Array array; + array.add(std::move(map)); + std::vector cborData = array.encode(); + if (SUCCESS != addApduHeader(kAttestationIdsCmd, cborData)) { + return FAILURE; + } + // Write to json. + writerRoot[kAttestationIds] = getHexString(cborData); + //------------------------- + } else { + printf("\n Fail: Improper value found for attest_ids key inside the json file \n"); + return FAILURE; + } + printf("\n Constructed attestation ids APDU successfully \n"); + return SUCCESS; +} + +int processSharedSecret() { + Json::Value sharedSecret = root.get("shared_secret", Json::Value::nullRef); + if (!sharedSecret.isNull()) { + + if (!sharedSecret.isString()) { + printf("\n Fail: Value for shared secret key should be string inside the json file\n"); + return FAILURE; + } + std::string secret = hex2str(sharedSecret.asString()); + std::vector data(secret.begin(), secret.end()); + // -------------------------- + // Construct apdu. + Array array; + array.add(data); + std::vector cborData = array.encode(); + if (SUCCESS != addApduHeader(kPresharedSecretCmd, cborData)) { + return FAILURE; + } + // Write to json. + writerRoot[kSharedSecret] = getHexString(cborData); + // -------------------------- + } else { + printf("\n Fail: Improper value for shared_secret key inside the json file\n"); + return FAILURE; + } + printf("\n Constructed shared secret APDU successfully \n"); + return SUCCESS; +} + +int processSetBootParameters() { + uint32_t bootPatchLevel; + std::vector verifiedBootKey; + std::vector verifiedBootKeyHash; + uint32_t verifiedBootState; + uint32_t deviceLocked; + Json::Value bootParamsObj = root.get("set_boot_params", Json::Value::nullRef); + if (!bootParamsObj.isNull()) { + + if(SUCCESS != getIntValue(bootParamsObj, "boot_patch_level", &bootPatchLevel)) { + printf("\n Invalid value for boot_patch_level or boot_patch_level tag missing\n"); + return FAILURE; + } + if(SUCCESS != getBlobValue(bootParamsObj, "verified_boot_key", verifiedBootKey)) { + printf("\n Invalid value for verified_boot_key or verified_boot_key tag missing\n"); + return FAILURE; + } + if(SUCCESS != getBlobValue(bootParamsObj, "verified_boot_key_hash", verifiedBootKeyHash)) { + printf("\n Invalid value for verified_boot_key_hash or verified_boot_key_hash tag missing\n"); + return FAILURE; + } + if(SUCCESS != getIntValue(bootParamsObj, "boot_state", &verifiedBootState)) { + printf("\n Invalid value for boot_state or boot_state tag missing\n"); + return FAILURE; + } + if(SUCCESS != getIntValue(bootParamsObj, "device_locked", &deviceLocked)) { + printf("\n Invalid value for device_locked or device_locked tag missing\n"); + return FAILURE; + } + + } else { + printf("\n Fail: Improper value found for set_boot_params key inside the json file\n"); + return FAILURE; + } + //--------------------------------- + // prepare cbor data. + Array array; + array.add(bootPatchLevel). + add(verifiedBootKey). /* Verified Boot Key */ + add(verifiedBootKeyHash). /* Verified Boot Hash */ + add(verifiedBootState). /* boot state */ + add(deviceLocked); /* device locked */ + + std::vector cborData = array.encode(); + if (SUCCESS != addApduHeader(kBootParamsCmd, cborData)) { + return FAILURE; + } + // Write to json. + writerRoot[kBootParams] = getHexString(cborData); + + //--------------------------------- + printf("\n Constructed boot paramters APDU successfully \n"); + return SUCCESS; +} + + + +int addApduHeader(const int ins, std::vector& inputData) { + if(USHRT_MAX >= inputData.size()) { + // Send extended length APDU always as response size is not known to HAL. + // Case 1: Lc > 0 CLS | INS | P1 | P2 | 00 | 2 bytes of Lc | CommandData | 2 bytes of Le all set to 00. + // Case 2: Lc = 0 CLS | INS | P1 | P2 | 3 bytes of Le all set to 00. + //Extended length 3 bytes, starts with 0x00 + if (inputData.size() > 0) { + inputData.insert(inputData.begin(), static_cast(inputData.size() & 0xFF)); // LSB + inputData.insert(inputData.begin(), static_cast(inputData.size() >> 8)); // MSB + } + inputData.insert(inputData.begin(), static_cast(0x00)); + //Expected length of output. + //Accepting complete length of output every time. + inputData.push_back(static_cast(0x00)); + inputData.push_back(static_cast(0x00)); + } else { + printf("\n Failed to construct apdu. input data larger than USHORT_MAX.\n"); + return FAILURE; + } + + inputData.insert(inputData.begin(), static_cast(APDU_P2));//P2 + inputData.insert(inputData.begin(), static_cast(APDU_P1));//P1 + inputData.insert(inputData.begin(), static_cast(ins));//INS + inputData.insert(inputData.begin(), static_cast(APDU_CLS));//CLS + return SUCCESS; +} + +int readDataFromFile(const char *filename, std::vector& data) { + FILE *fp; + int ret = SUCCESS; + fp = fopen(filename, "rb"); + if(fp == NULL) { + printf("\nFailed to open file: \n"); + return FAILURE; + } + 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)) { + printf("\n No content in the file \n"); + ret = FAILURE; + goto exit; + } + data.insert(data.end(), buf.get(), buf.get() + filesize); +exit: + fclose(fp); + return ret; +} + +int main(int argc, char* argv[]) { + int c; + struct option longOpts[] = { + {"input", required_argument, NULL, 'i'}, + {"output", required_argument, NULL, 'o'}, + {"help", no_argument, NULL, 'h'}, + {0,0,0,0} + }; + + if (argc <= 1) { + printf("\n Invalid command \n"); + usage(); + return FAILURE; + } + + /* getopt_long stores the option index here. */ + while ((c = getopt_long(argc, argv, ":hi:o:", longOpts, NULL)) != -1) { + switch(c) { + case 'i': + // input file + inputFileName = std::string(optarg); + std::cout << "input file: " << inputFileName << std::endl; + break; + case 'o': + // output file + outputFileName = std::string(optarg); + std::cout << "output file: " << outputFileName << std::endl; + break; + case 'h': + // help + usage(); + return SUCCESS; + case ':': + printf("\n missing argument\n"); + usage(); + return FAILURE; + case '?': + default: + printf("\n Invalid option\n"); + usage(); + return FAILURE; + } + } + if (inputFileName.empty() || outputFileName.empty() || optind < argc) { + printf("\n Missing mandatory arguments \n"); + usage(); + return FAILURE; + } + // Process input file; construct apuds and store in output json file. + processInputFile(); + return SUCCESS; +} diff --git a/ProvisioningTool/src/cppbor/cppbor.cpp b/ProvisioningTool/src/cppbor/cppbor.cpp new file mode 100644 index 00000000..3414b8e5 --- /dev/null +++ b/ProvisioningTool/src/cppbor/cppbor.cpp @@ -0,0 +1,626 @@ +/* + * Copyright 2019 Google LLC + * + * 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 + * + * https://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 + +using std::string; +using std::vector; + + +#if !defined(__TRUSTY__) && !defined(__LINUX__) +#include +#define LOG_TAG "CppBor" +#else +#define CHECK(x) (void)(x) +#endif + +#ifdef __LINUX__ +#define ERROR "ERROR: " +#define LOG(x) std::cout << x +#endif + +namespace cppbor { + +namespace { + +template ::value>> +Iterator writeBigEndian(T value, Iterator pos) { + for (unsigned i = 0; i < sizeof(value); ++i) { + *pos++ = static_cast(value >> (8 * (sizeof(value) - 1))); + value = static_cast(value << 8); + } + return pos; +} + +template ::value>> +void writeBigEndian(T value, std::function& cb) { + for (unsigned i = 0; i < sizeof(value); ++i) { + cb(static_cast(value >> (8 * (sizeof(value) - 1)))); + value = static_cast(value << 8); + } +} + +bool cborAreAllElementsNonCompound(const Item* compoundItem) { + if (compoundItem->type() == ARRAY) { + const Array* array = compoundItem->asArray(); + for (size_t n = 0; n < array->size(); n++) { + const Item* entry = (*array)[n].get(); + switch (entry->type()) { + case ARRAY: + case MAP: + return false; + default: + break; + } + } + } else { + const Map* map = compoundItem->asMap(); + for (auto& [keyEntry, valueEntry] : *map) { + switch (keyEntry->type()) { + case ARRAY: + case MAP: + return false; + default: + break; + } + switch (valueEntry->type()) { + case ARRAY: + case MAP: + return false; + default: + break; + } + } + } + return true; +} + +bool prettyPrintInternal(const Item* item, string& out, size_t indent, size_t maxBStrSize, + const vector& mapKeysToNotPrint) { + if (!item) { + out.append(""); + return false; + } + + char buf[80]; + + string indentString(indent, ' '); + + size_t tagCount = item->semanticTagCount(); + while (tagCount > 0) { + --tagCount; + snprintf(buf, sizeof(buf), "tag %" PRIu64 " ", item->semanticTag(tagCount)); + out.append(buf); + } + + switch (item->type()) { + case SEMANTIC: + // Handled above. + break; + + case UINT: + snprintf(buf, sizeof(buf), "%" PRIu64, item->asUint()->unsignedValue()); + out.append(buf); + break; + + case NINT: + snprintf(buf, sizeof(buf), "%" PRId64, item->asNint()->value()); + out.append(buf); + break; + + case BSTR: { + const uint8_t* valueData; + size_t valueSize; + const Bstr* bstr = item->asBstr(); + if (bstr != nullptr) { + const vector& value = bstr->value(); + valueData = value.data(); + valueSize = value.size(); + } else { + const ViewBstr* viewBstr = item->asViewBstr(); + assert(viewBstr != nullptr); + + std::basic_string_view view = viewBstr->view(); + valueData = view.data(); + valueSize = view.size(); + } + + if (valueSize > maxBStrSize) { + unsigned char digest[SHA_DIGEST_LENGTH]; + SHA_CTX ctx; + SHA1_Init(&ctx); + SHA1_Update(&ctx, valueData, valueSize); + SHA1_Final(digest, &ctx); + char buf2[SHA_DIGEST_LENGTH * 2 + 1]; + for (size_t n = 0; n < SHA_DIGEST_LENGTH; n++) { + snprintf(buf2 + n * 2, 3, "%02x", digest[n]); + } + snprintf(buf, sizeof(buf), "", valueSize, buf2); + out.append(buf); + } else { + out.append("{"); + for (size_t n = 0; n < valueSize; n++) { + if (n > 0) { + out.append(", "); + } + snprintf(buf, sizeof(buf), "0x%02x", valueData[n]); + out.append(buf); + } + out.append("}"); + } + } break; + + case TSTR: + out.append("'"); + { + // TODO: escape "'" characters + if (item->asTstr() != nullptr) { + out.append(item->asTstr()->value().c_str()); + } else { + const ViewTstr* viewTstr = item->asViewTstr(); + assert(viewTstr != nullptr); + out.append(viewTstr->view()); + } + } + out.append("'"); + break; + + case ARRAY: { + const Array* array = item->asArray(); + if (array->size() == 0) { + out.append("[]"); + } else if (cborAreAllElementsNonCompound(array)) { + out.append("["); + for (size_t n = 0; n < array->size(); n++) { + if (!prettyPrintInternal((*array)[n].get(), out, indent + 2, maxBStrSize, + mapKeysToNotPrint)) { + return false; + } + out.append(", "); + } + out.append("]"); + } else { + out.append("[\n" + indentString); + for (size_t n = 0; n < array->size(); n++) { + out.append(" "); + if (!prettyPrintInternal((*array)[n].get(), out, indent + 2, maxBStrSize, + mapKeysToNotPrint)) { + return false; + } + out.append(",\n" + indentString); + } + out.append("]"); + } + } break; + + case MAP: { + const Map* map = item->asMap(); + + if (map->size() == 0) { + out.append("{}"); + } else { + out.append("{\n" + indentString); + for (auto& [map_key, map_value] : *map) { + out.append(" "); + + if (!prettyPrintInternal(map_key.get(), out, indent + 2, maxBStrSize, + mapKeysToNotPrint)) { + return false; + } + out.append(" : "); + if (map_key->type() == TSTR && + std::find(mapKeysToNotPrint.begin(), mapKeysToNotPrint.end(), + map_key->asTstr()->value()) != mapKeysToNotPrint.end()) { + out.append(""); + } else { + if (!prettyPrintInternal(map_value.get(), out, indent + 2, maxBStrSize, + mapKeysToNotPrint)) { + return false; + } + } + out.append(",\n" + indentString); + } + out.append("}"); + } + } break; + + case SIMPLE: + const Bool* asBool = item->asSimple()->asBool(); + const Null* asNull = item->asSimple()->asNull(); + if (asBool != nullptr) { + out.append(asBool->value() ? "true" : "false"); + } else if (asNull != nullptr) { + out.append("null"); + } else { +#ifndef __TRUSTY__ + LOG(ERROR) << "Only boolean/null is implemented for SIMPLE"; +#endif // __TRUSTY__ + return false; + } + break; + } + + return true; +} + +} // namespace + +size_t headerSize(uint64_t addlInfo) { + if (addlInfo < ONE_BYTE_LENGTH) return 1; + if (addlInfo <= std::numeric_limits::max()) return 2; + if (addlInfo <= std::numeric_limits::max()) return 3; + if (addlInfo <= std::numeric_limits::max()) return 5; + return 9; +} + +uint8_t* encodeHeader(MajorType type, uint64_t addlInfo, uint8_t* pos, const uint8_t* end) { + size_t sz = headerSize(addlInfo); + if (end - pos < static_cast(sz)) return nullptr; + switch (sz) { + case 1: + *pos++ = type | static_cast(addlInfo); + return pos; + case 2: + *pos++ = type | ONE_BYTE_LENGTH; + *pos++ = static_cast(addlInfo); + return pos; + case 3: + *pos++ = type | TWO_BYTE_LENGTH; + return writeBigEndian(static_cast(addlInfo), pos); + case 5: + *pos++ = type | FOUR_BYTE_LENGTH; + return writeBigEndian(static_cast(addlInfo), pos); + case 9: + *pos++ = type | EIGHT_BYTE_LENGTH; + return writeBigEndian(addlInfo, pos); + default: + CHECK(false); // Impossible to get here. + return nullptr; + } +} + +void encodeHeader(MajorType type, uint64_t addlInfo, EncodeCallback encodeCallback) { + size_t sz = headerSize(addlInfo); + switch (sz) { + case 1: + encodeCallback(type | static_cast(addlInfo)); + break; + case 2: + encodeCallback(type | ONE_BYTE_LENGTH); + encodeCallback(static_cast(addlInfo)); + break; + case 3: + encodeCallback(type | TWO_BYTE_LENGTH); + writeBigEndian(static_cast(addlInfo), encodeCallback); + break; + case 5: + encodeCallback(type | FOUR_BYTE_LENGTH); + writeBigEndian(static_cast(addlInfo), encodeCallback); + break; + case 9: + encodeCallback(type | EIGHT_BYTE_LENGTH); + writeBigEndian(addlInfo, encodeCallback); + break; + default: + CHECK(false); // Impossible to get here. + } +} + +bool Item::operator==(const Item& other) const& { + if (type() != other.type()) return false; + switch (type()) { + case UINT: + return *asUint() == *(other.asUint()); + case NINT: + return *asNint() == *(other.asNint()); + case BSTR: + if (asBstr() != nullptr && other.asBstr() != nullptr) { + return *asBstr() == *(other.asBstr()); + } + if (asViewBstr() != nullptr && other.asViewBstr() != nullptr) { + return *asViewBstr() == *(other.asViewBstr()); + } + // Interesting corner case: comparing a Bstr and ViewBstr with + // identical contents. The function currently returns false for + // this case. + // TODO: if it should return true, this needs a deep comparison + return false; + case TSTR: + if (asTstr() != nullptr && other.asTstr() != nullptr) { + return *asTstr() == *(other.asTstr()); + } + if (asViewTstr() != nullptr && other.asViewTstr() != nullptr) { + return *asViewTstr() == *(other.asViewTstr()); + } + // Same corner case as Bstr + return false; + case ARRAY: + return *asArray() == *(other.asArray()); + case MAP: + return *asMap() == *(other.asMap()); + case SIMPLE: + return *asSimple() == *(other.asSimple()); + case SEMANTIC: + return *asSemanticTag() == *(other.asSemanticTag()); + default: + CHECK(false); // Impossible to get here. + return false; + } +} + +Nint::Nint(int64_t v) : mValue(v) { + CHECK(v < 0); +} + +bool Simple::operator==(const Simple& other) const& { + if (simpleType() != other.simpleType()) return false; + + switch (simpleType()) { + case BOOLEAN: + return *asBool() == *(other.asBool()); + case NULL_T: + return true; + default: + CHECK(false); // Impossible to get here. + return false; + } +} + +uint8_t* Bstr::encode(uint8_t* pos, const uint8_t* end) const { + pos = encodeHeader(mValue.size(), pos, end); + if (!pos || end - pos < static_cast(mValue.size())) return nullptr; + return std::copy(mValue.begin(), mValue.end(), pos); +} + +void Bstr::encodeValue(EncodeCallback encodeCallback) const { + for (auto c : mValue) { + encodeCallback(c); + } +} + +uint8_t* ViewBstr::encode(uint8_t* pos, const uint8_t* end) const { + pos = encodeHeader(mView.size(), pos, end); + if (!pos || end - pos < static_cast(mView.size())) return nullptr; + return std::copy(mView.begin(), mView.end(), pos); +} + +void ViewBstr::encodeValue(EncodeCallback encodeCallback) const { + for (auto c : mView) { + encodeCallback(static_cast(c)); + } +} + +uint8_t* Tstr::encode(uint8_t* pos, const uint8_t* end) const { + pos = encodeHeader(mValue.size(), pos, end); + if (!pos || end - pos < static_cast(mValue.size())) return nullptr; + return std::copy(mValue.begin(), mValue.end(), pos); +} + +void Tstr::encodeValue(EncodeCallback encodeCallback) const { + for (auto c : mValue) { + encodeCallback(static_cast(c)); + } +} + +uint8_t* ViewTstr::encode(uint8_t* pos, const uint8_t* end) const { + pos = encodeHeader(mView.size(), pos, end); + if (!pos || end - pos < static_cast(mView.size())) return nullptr; + return std::copy(mView.begin(), mView.end(), pos); +} + +void ViewTstr::encodeValue(EncodeCallback encodeCallback) const { + for (auto c : mView) { + encodeCallback(static_cast(c)); + } +} + +bool Array::operator==(const Array& other) const& { + return size() == other.size() + // Can't use vector::operator== because the contents are pointers. std::equal lets us + // provide a predicate that does the dereferencing. + && std::equal(mEntries.begin(), mEntries.end(), other.mEntries.begin(), + [](auto& a, auto& b) -> bool { return *a == *b; }); +} + +uint8_t* Array::encode(uint8_t* pos, const uint8_t* end) const { + pos = encodeHeader(size(), pos, end); + if (!pos) return nullptr; + for (auto& entry : mEntries) { + pos = entry->encode(pos, end); + if (!pos) return nullptr; + } + return pos; +} + +void Array::encode(EncodeCallback encodeCallback) const { + encodeHeader(size(), encodeCallback); + for (auto& entry : mEntries) { + entry->encode(encodeCallback); + } +} + +std::unique_ptr Array::clone() const { + auto res = std::make_unique(); + for (size_t i = 0; i < mEntries.size(); i++) { + res->add(mEntries[i]->clone()); + } + return res; +} + +bool Map::operator==(const Map& other) const& { + return size() == other.size() + // Can't use vector::operator== because the contents are pairs of pointers. std::equal + // lets us provide a predicate that does the dereferencing. + && std::equal(begin(), end(), other.begin(), [](auto& a, auto& b) { + return *a.first == *b.first && *a.second == *b.second; + }); +} + +uint8_t* Map::encode(uint8_t* pos, const uint8_t* end) const { + pos = encodeHeader(size(), pos, end); + if (!pos) return nullptr; + for (auto& entry : mEntries) { + pos = entry.first->encode(pos, end); + if (!pos) return nullptr; + pos = entry.second->encode(pos, end); + if (!pos) return nullptr; + } + return pos; +} + +void Map::encode(EncodeCallback encodeCallback) const { + encodeHeader(size(), encodeCallback); + for (auto& entry : mEntries) { + entry.first->encode(encodeCallback); + entry.second->encode(encodeCallback); + } +} + +bool Map::keyLess(const Item* a, const Item* b) { + // CBOR map canonicalization rules are: + + // 1. If two keys have different lengths, the shorter one sorts earlier. + if (a->encodedSize() < b->encodedSize()) return true; + if (a->encodedSize() > b->encodedSize()) return false; + + // 2. If two keys have the same length, the one with the lower value in (byte-wise) lexical + // order sorts earlier. This requires encoding both items. + auto encodedA = a->encode(); + auto encodedB = b->encode(); + + return std::lexicographical_compare(encodedA.begin(), encodedA.end(), // + encodedB.begin(), encodedB.end()); +} + +void recursivelyCanonicalize(std::unique_ptr& item) { + switch (item->type()) { + case UINT: + case NINT: + case BSTR: + case TSTR: + case SIMPLE: + return; + + case ARRAY: + std::for_each(item->asArray()->begin(), item->asArray()->end(), + recursivelyCanonicalize); + return; + + case MAP: + item->asMap()->canonicalize(true /* recurse */); + return; + + case SEMANTIC: + // This can't happen. SemanticTags delegate their type() method to the contained Item's + // type. + assert(false); + return; + } +} + +Map& Map::canonicalize(bool recurse) & { + if (recurse) { + for (auto& entry : mEntries) { + recursivelyCanonicalize(entry.first); + recursivelyCanonicalize(entry.second); + } + } + + if (size() < 2 || mCanonicalized) { + // Trivially or already canonical; do nothing. + return *this; + } + + std::sort(begin(), end(), + [](auto& a, auto& b) { return keyLess(a.first.get(), b.first.get()); }); + mCanonicalized = true; + return *this; +} + +std::unique_ptr Map::clone() const { + auto res = std::make_unique(); + for (auto& [key, value] : *this) { + res->add(key->clone(), value->clone()); + } + res->mCanonicalized = mCanonicalized; + return res; +} + +std::unique_ptr SemanticTag::clone() const { + return std::make_unique(mValue, mTaggedItem->clone()); +} + +uint8_t* SemanticTag::encode(uint8_t* pos, const uint8_t* end) const { + // Can't use the encodeHeader() method that calls type() to get the major type, since that will + // return the tagged Item's type. + pos = ::cppbor::encodeHeader(kMajorType, mValue, pos, end); + if (!pos) return nullptr; + return mTaggedItem->encode(pos, end); +} + +void SemanticTag::encode(EncodeCallback encodeCallback) const { + // Can't use the encodeHeader() method that calls type() to get the major type, since that will + // return the tagged Item's type. + ::cppbor::encodeHeader(kMajorType, mValue, encodeCallback); + mTaggedItem->encode(encodeCallback); +} + +size_t SemanticTag::semanticTagCount() const { + size_t levelCount = 1; // Count this level. + const SemanticTag* cur = this; + while (cur->mTaggedItem && (cur = cur->mTaggedItem->asSemanticTag()) != nullptr) ++levelCount; + return levelCount; +} + +uint64_t SemanticTag::semanticTag(size_t nesting) const { + // Getting the value of a specific nested tag is a bit tricky, because we start with the outer + // tag and don't know how many are inside. We count the number of nesting levels to find out + // how many there are in total, then to get the one we want we have to walk down levelCount - + // nesting steps. + size_t levelCount = semanticTagCount(); + if (nesting >= levelCount) return 0; + + levelCount -= nesting; + const SemanticTag* cur = this; + while (--levelCount > 0) cur = cur->mTaggedItem->asSemanticTag(); + + return cur->mValue; +} + +string prettyPrint(const Item* item, size_t maxBStrSize, const vector& mapKeysToNotPrint) { + string out; + prettyPrintInternal(item, out, 0, maxBStrSize, mapKeysToNotPrint); + return out; +} +string prettyPrint(const vector& encodedCbor, size_t maxBStrSize, + const vector& mapKeysToNotPrint) { + auto [item, _, message] = parse(encodedCbor); + if (item == nullptr) { +#ifndef __TRUSTY__ + LOG(ERROR) << "Data to pretty print is not valid CBOR: " << message; +#endif // __TRUSTY__ + return ""; + } + + return prettyPrint(item.get(), maxBStrSize, mapKeysToNotPrint); +} + +} // namespace cppbor diff --git a/ProvisioningTool/src/cppbor/cppbor_parse.cpp b/ProvisioningTool/src/cppbor/cppbor_parse.cpp new file mode 100644 index 00000000..b1803310 --- /dev/null +++ b/ProvisioningTool/src/cppbor/cppbor_parse.cpp @@ -0,0 +1,389 @@ +/* + * Copyright 2019 Google LLC + * + * 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 + * + * https://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 "cppbor/cppbor_parse.h" + +#include + +#if !defined( __TRUSTY__) && !defined(__LINUX__) +#include +#define LOG_TAG "CppBor" +#else +#define CHECK(x) (void)(x) +#endif + +namespace cppbor { + +namespace { + +std::string insufficientLengthString(size_t bytesNeeded, size_t bytesAvail, + const std::string& type) { + char buf[1024]; + snprintf(buf, sizeof(buf), "Need %zu byte(s) for %s, have %zu.", bytesNeeded, type.c_str(), + bytesAvail); + return std::string(buf); +} + +template >> +std::tuple parseLength(const uint8_t* pos, const uint8_t* end, + ParseClient* parseClient) { + if (pos + sizeof(T) > end) { + parseClient->error(pos - 1, insufficientLengthString(sizeof(T), end - pos, "length field")); + return {false, 0, pos}; + } + + const uint8_t* intEnd = pos + sizeof(T); + T result = 0; + do { + result = static_cast((result << 8) | *pos++); + } while (pos < intEnd); + return {true, result, pos}; +} + +std::tuple parseRecursively(const uint8_t* begin, const uint8_t* end, + bool emitViews, ParseClient* parseClient); + +std::tuple handleUint(uint64_t value, const uint8_t* hdrBegin, + const uint8_t* hdrEnd, + ParseClient* parseClient) { + std::unique_ptr item = std::make_unique(value); + return {hdrEnd, + parseClient->item(item, hdrBegin, hdrEnd /* valueBegin */, hdrEnd /* itemEnd */)}; +} + +std::tuple handleNint(uint64_t value, const uint8_t* hdrBegin, + const uint8_t* hdrEnd, + ParseClient* parseClient) { + if (value > std::numeric_limits::max()) { + parseClient->error(hdrBegin, "NINT values that don't fit in int64_t are not supported."); + return {hdrBegin, nullptr /* end parsing */}; + } + std::unique_ptr item = std::make_unique(-1 - static_cast(value)); + return {hdrEnd, + parseClient->item(item, hdrBegin, hdrEnd /* valueBegin */, hdrEnd /* itemEnd */)}; +} + +std::tuple handleBool(uint64_t value, const uint8_t* hdrBegin, + const uint8_t* hdrEnd, + ParseClient* parseClient) { + std::unique_ptr item = std::make_unique(value == TRUE); + return {hdrEnd, + parseClient->item(item, hdrBegin, hdrEnd /* valueBegin */, hdrEnd /* itemEnd */)}; +} + +std::tuple handleNull(const uint8_t* hdrBegin, const uint8_t* hdrEnd, + ParseClient* parseClient) { + std::unique_ptr item = std::make_unique(); + return {hdrEnd, + parseClient->item(item, hdrBegin, hdrEnd /* valueBegin */, hdrEnd /* itemEnd */)}; +} + +template +std::tuple handleString(uint64_t length, const uint8_t* hdrBegin, + const uint8_t* valueBegin, const uint8_t* end, + const std::string& errLabel, + ParseClient* parseClient) { + if (end - valueBegin < static_cast(length)) { + parseClient->error(hdrBegin, insufficientLengthString(length, end - valueBegin, errLabel)); + return {hdrBegin, nullptr /* end parsing */}; + } + + std::unique_ptr item = std::make_unique(valueBegin, valueBegin + length); + return {valueBegin + length, + parseClient->item(item, hdrBegin, valueBegin, valueBegin + length)}; +} + +class IncompleteItem { + public: + virtual ~IncompleteItem() {} + virtual void add(std::unique_ptr item) = 0; +}; + +class IncompleteArray : public Array, public IncompleteItem { + public: + explicit IncompleteArray(size_t size) : mSize(size) {} + + // We return the "complete" size, rather than the actual size. + size_t size() const override { return mSize; } + + void add(std::unique_ptr item) override { + mEntries.reserve(mSize); + mEntries.push_back(std::move(item)); + } + + private: + size_t mSize; +}; + +class IncompleteMap : public Map, public IncompleteItem { + public: + explicit IncompleteMap(size_t size) : mSize(size) {} + + // We return the "complete" size, rather than the actual size. + size_t size() const override { return mSize; } + + void add(std::unique_ptr item) override { + if (mKeyHeldForAdding) { + mEntries.reserve(mSize); + mEntries.push_back({std::move(mKeyHeldForAdding), std::move(item)}); + } else { + mKeyHeldForAdding = std::move(item); + } + } + + private: + std::unique_ptr mKeyHeldForAdding; + size_t mSize; +}; + +class IncompleteSemanticTag : public SemanticTag, public IncompleteItem { + public: + explicit IncompleteSemanticTag(uint64_t value) : SemanticTag(value) {} + + // We return the "complete" size, rather than the actual size. + size_t size() const override { return 1; } + + void add(std::unique_ptr item) override { mTaggedItem = std::move(item); } +}; + +std::tuple handleEntries(size_t entryCount, const uint8_t* hdrBegin, + const uint8_t* pos, const uint8_t* end, + const std::string& typeName, + bool emitViews, + ParseClient* parseClient) { + while (entryCount > 0) { + --entryCount; + if (pos == end) { + parseClient->error(hdrBegin, "Not enough entries for " + typeName + "."); + return {hdrBegin, nullptr /* end parsing */}; + } + std::tie(pos, parseClient) = parseRecursively(pos, end, emitViews, parseClient); + if (!parseClient) return {hdrBegin, nullptr}; + } + return {pos, parseClient}; +} + +std::tuple handleCompound( + std::unique_ptr item, uint64_t entryCount, const uint8_t* hdrBegin, + const uint8_t* valueBegin, const uint8_t* end, const std::string& typeName, + bool emitViews, ParseClient* parseClient) { + parseClient = + parseClient->item(item, hdrBegin, valueBegin, valueBegin /* don't know the end yet */); + if (!parseClient) return {hdrBegin, nullptr}; + + const uint8_t* pos; + std::tie(pos, parseClient) = + handleEntries(entryCount, hdrBegin, valueBegin, end, typeName, emitViews, parseClient); + if (!parseClient) return {hdrBegin, nullptr}; + + return {pos, parseClient->itemEnd(item, hdrBegin, valueBegin, pos)}; +} + +std::tuple parseRecursively(const uint8_t* begin, const uint8_t* end, + bool emitViews, ParseClient* parseClient) { + const uint8_t* pos = begin; + + MajorType type = static_cast(*pos & 0xE0); + uint8_t tagInt = *pos & 0x1F; + ++pos; + + bool success = true; + uint64_t addlData; + if (tagInt < ONE_BYTE_LENGTH) { + addlData = tagInt; + } else if (tagInt > EIGHT_BYTE_LENGTH) { + parseClient->error( + begin, + "Reserved additional information value or unsupported indefinite length item."); + return {begin, nullptr}; + } else { + switch (tagInt) { + case ONE_BYTE_LENGTH: + std::tie(success, addlData, pos) = parseLength(pos, end, parseClient); + break; + + case TWO_BYTE_LENGTH: + std::tie(success, addlData, pos) = parseLength(pos, end, parseClient); + break; + + case FOUR_BYTE_LENGTH: + std::tie(success, addlData, pos) = parseLength(pos, end, parseClient); + break; + + case EIGHT_BYTE_LENGTH: + std::tie(success, addlData, pos) = parseLength(pos, end, parseClient); + break; + + default: + CHECK(false); // It's impossible to get here + break; + } + } + + if (!success) return {begin, nullptr}; + + switch (type) { + case UINT: + return handleUint(addlData, begin, pos, parseClient); + + case NINT: + return handleNint(addlData, begin, pos, parseClient); + + case BSTR: + if (emitViews) { + return handleString(addlData, begin, pos, end, "byte string", parseClient); + } else { + return handleString(addlData, begin, pos, end, "byte string", parseClient); + } + + case TSTR: + if (emitViews) { + return handleString(addlData, begin, pos, end, "text string", parseClient); + } else { + return handleString(addlData, begin, pos, end, "text string", parseClient); + } + + case ARRAY: + return handleCompound(std::make_unique(addlData), addlData, begin, pos, + end, "array", emitViews, parseClient); + + case MAP: + return handleCompound(std::make_unique(addlData), addlData * 2, begin, + pos, end, "map", emitViews, parseClient); + + case SEMANTIC: + return handleCompound(std::make_unique(addlData), 1, begin, pos, + end, "semantic", emitViews, parseClient); + + case SIMPLE: + switch (addlData) { + case TRUE: + case FALSE: + return handleBool(addlData, begin, pos, parseClient); + case NULL_V: + return handleNull(begin, pos, parseClient); + default: + parseClient->error(begin, "Unsupported floating-point or simple value."); + return {begin, nullptr}; + } + } + CHECK(false); // Impossible to get here. + return {}; +} + +class FullParseClient : public ParseClient { + public: + virtual ParseClient* item(std::unique_ptr& item, const uint8_t*, const uint8_t*, + const uint8_t* end) override { + if (mParentStack.empty() && !item->isCompound()) { + // This is the first and only item. + mTheItem = std::move(item); + mPosition = end; + return nullptr; // We're done. + } + + if (item->isCompound()) { + // Starting a new compound data item, i.e. a new parent. Save it on the parent stack. + // It's safe to save a raw pointer because the unique_ptr is guaranteed to stay in + // existence until the corresponding itemEnd() call. + mParentStack.push(item.get()); + return this; + } else { + appendToLastParent(std::move(item)); + return this; + } + } + + virtual ParseClient* itemEnd(std::unique_ptr& item, const uint8_t*, const uint8_t*, + const uint8_t* end) override { + CHECK(item->isCompound() && item.get() == mParentStack.top()); + mParentStack.pop(); + + if (mParentStack.empty()) { + mTheItem = std::move(item); + mPosition = end; + return nullptr; // We're done + } else { + appendToLastParent(std::move(item)); + return this; + } + } + + virtual void error(const uint8_t* position, const std::string& errorMessage) override { + mPosition = position; + mErrorMessage = errorMessage; + } + + std::tuple /* result */, const uint8_t* /* newPos */, + std::string /* errMsg */> + parseResult() { + std::unique_ptr p = std::move(mTheItem); + return {std::move(p), mPosition, std::move(mErrorMessage)}; + } + + private: + void appendToLastParent(std::unique_ptr item) { + auto parent = mParentStack.top(); +//#if __has_feature(cxx_rtti) + assert(dynamic_cast(parent)); +//#endif + + IncompleteItem* parentItem{}; + if (parent->type() == ARRAY) { + parentItem = static_cast(parent); + } else if (parent->type() == MAP) { + parentItem = static_cast(parent); + } else if (parent->asSemanticTag()) { + parentItem = static_cast(parent); + } else { + CHECK(false); // Impossible to get here. + } + parentItem->add(std::move(item)); + } + + std::unique_ptr mTheItem; + std::stack mParentStack; + const uint8_t* mPosition = nullptr; + std::string mErrorMessage; +}; + +} // anonymous namespace + +void parse(const uint8_t* begin, const uint8_t* end, ParseClient* parseClient) { + parseRecursively(begin, end, false, parseClient); +} + +std::tuple /* result */, const uint8_t* /* newPos */, + std::string /* errMsg */> +parse(const uint8_t* begin, const uint8_t* end) { + FullParseClient parseClient; + parse(begin, end, &parseClient); + return parseClient.parseResult(); +} + +void parseWithViews(const uint8_t* begin, const uint8_t* end, ParseClient* parseClient) { + parseRecursively(begin, end, true, parseClient); +} + +std::tuple /* result */, const uint8_t* /* newPos */, + std::string /* errMsg */> +parseWithViews(const uint8_t* begin, const uint8_t* end) { + FullParseClient parseClient; + parseWithViews(begin, end, &parseClient); + return parseClient.parseResult(); +} + +} // namespace cppbor diff --git a/ProvisioningTool/src/cppcose/cppcose.cpp b/ProvisioningTool/src/cppcose/cppcose.cpp new file mode 100644 index 00000000..e0c5aaab --- /dev/null +++ b/ProvisioningTool/src/cppcose/cppcose.cpp @@ -0,0 +1,210 @@ +/* + * 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. + */ + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace cppcose { + +ErrMsgOr ECDSA_sign(const bytevec& key, bytevec& input) { + EVP_PKEY_CTX* pkeyCtx = NULL; + EVP_MD_CTX_Ptr digestCtx(EVP_MD_CTX_new()); + auto bn = BIGNUM_Ptr(BN_bin2bn(key.data(), key.size(), nullptr)); + if (bn.get() == nullptr) { + return "Error creating BIGNUM for private key"; + } + auto privEcKey = EC_KEY_Ptr(EC_KEY_new_by_curve_name(NID_X9_62_prime256v1)); + if (EC_KEY_set_private_key(privEcKey.get(), bn.get()) != 1) { + return "Error setting private key from BIGNUM"; + } + auto privPkey = EVP_PKEY_Ptr(EVP_PKEY_new()); + if (EVP_PKEY_set1_EC_KEY(privPkey.get(), privEcKey.get()) != 1) { + return "Error setting private key"; + } + + if (EVP_DigestSignInit(digestCtx.get(), &pkeyCtx, EVP_sha256(), nullptr /* engine */, privPkey.get()) != + 1) { + return "Failed to do digest sign init."; + } + size_t outlen = EVP_PKEY_size(privPkey.get()); + bytevec signature(outlen); + if (!EVP_DigestSign(digestCtx.get(), signature.data(), &outlen, input.data(), input.size())) { + return "Ecdsa sign failed."; + } + return signature; +} + +bool ECDSA_verify(const bytevec& input, const bytevec& signature, const bytevec& key) { + EVP_PKEY_CTX* pkeyCtx = NULL; + EVP_MD_CTX_Ptr digestCtx(EVP_MD_CTX_new()); + auto ecGroup = EC_GROUP_Ptr(EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1)); + auto ecKey = EC_KEY_Ptr(EC_KEY_new_by_curve_name(NID_X9_62_prime256v1)); + if (ecGroup.get() == nullptr) { + return "Failed to get EC_GROUP from curve name"; + } + auto ecPoint = EC_POINT_Ptr(EC_POINT_new(ecGroup.get())); + if (ecPoint.get() == nullptr) { + return "Failed to get EC_POINT from EC_GROUP"; + } + if (EC_POINT_oct2point(ecGroup.get(), ecPoint.get(), key.data(), key.size(), nullptr) != + 1) { + return 0; + } + // set public key + if (EC_KEY_set_public_key(ecKey.get(), ecPoint.get()) != 1) { + return 0; + } + auto pkey = EVP_PKEY_Ptr(EVP_PKEY_new()); + if (EVP_PKEY_set1_EC_KEY(pkey.get(), ecKey.get()) != 1) { + return 0; + } + if (EVP_DigestVerifyInit(digestCtx.get(), &pkeyCtx, EVP_sha256(), nullptr /* engine */, pkey.get()) != + 1) { + return 0; + } + return EVP_DigestVerify(digestCtx.get(), signature.data(), signature.size(), input.data(), input.size()); +} + +ErrMsgOr getEcPointFromAffineCoordinates(const bytevec& pubx, const bytevec& puby) { + auto ecGroup = EC_GROUP_Ptr(EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1)); + if (ecGroup.get() == nullptr) { + return "Failed to get EC_GROUP from curve name"; + } + auto ecPoint = EC_POINT_Ptr(EC_POINT_new(ecGroup.get())); + if (ecPoint.get() == nullptr) { + return "Failed to get EC_POINT from EC_GROUP"; + } + auto bn_x = BIGNUM_Ptr(BN_bin2bn(pubx.data(), pubx.size(), nullptr)); + if (bn_x.get() == nullptr) { + return "Error creating BIGNUM for peer public key X coordinate"; + } + auto bn_y = BIGNUM_Ptr(BN_bin2bn(puby.data(), puby.size(), nullptr)); + if (bn_y.get() == nullptr) { + return "Error creating BIGNUM for peer public key Y coordinate"; + } + if (!EC_POINT_set_affine_coordinates(ecGroup.get(), ecPoint.get(), bn_x.get(), bn_y.get(), + nullptr)) { + return "Failed to set affine coordinates"; + } + size_t pubKeyLen; + pubKeyLen = EC_POINT_point2oct(ecGroup.get(), ecPoint.get(), POINT_CONVERSION_UNCOMPRESSED, + nullptr, 0, nullptr); + if (pubKeyLen == 0) { + return "Failed to convert EC_POINT to buffer."; + } + bytevec pubkey(pubKeyLen); + EC_POINT_point2oct(ecGroup.get(), ecPoint.get(), POINT_CONVERSION_UNCOMPRESSED, pubkey.data(), + pubKeyLen, nullptr); + return pubkey; +} + +ErrMsgOr createCoseSign1Signature(const bytevec& key, const bytevec& protectedParams, + const bytevec& payload, const bytevec& aad) { + bytevec signatureInput = cppbor::Array() + .add("Signature1") // + .add(protectedParams) + .add(aad) + .add(payload) + .encode(); + auto signature = ECDSA_sign(key, signatureInput); + if (!signature) return "Signing failed"; + return signature; +} + +ErrMsgOr constructCoseSign1(const bytevec& key, cppbor::Map protectedParams, + const bytevec& payload, const bytevec& aad) { + bytevec protParms = protectedParams.add(ALGORITHM, ES256).canonicalize().encode(); + auto signature = createCoseSign1Signature(key, protParms, payload, aad); + if (!signature) return signature.moveMessage(); + + return cppbor::Array() + .add(std::move(protParms)) + .add(cppbor::Map() /* unprotected parameters */) + .add(std::move(payload)) + .add(std::move(*signature)); +} + +ErrMsgOr constructCoseSign1(const bytevec& key, const bytevec& payload, + const bytevec& aad) { + return constructCoseSign1(key, {} /* protectedParams */, payload, aad); +} + +ErrMsgOr verifyAndParseCoseSign1(bool ignoreSignature, const cppbor::Array* coseSign1, + const bytevec& signingCoseKey, const bytevec& aad) { + if (!coseSign1 || coseSign1->size() != kCoseSign1EntryCount) { + return "Invalid COSE_Sign1"; + } + + const cppbor::Bstr* protectedParams = coseSign1->get(kCoseSign1ProtectedParams)->asBstr(); + const cppbor::Map* unprotectedParams = coseSign1->get(kCoseSign1UnprotectedParams)->asMap(); + const cppbor::Bstr* payload = coseSign1->get(kCoseSign1Payload)->asBstr(); + + if (!protectedParams || !unprotectedParams || !payload) { + return "Missing input parameters"; + } + + auto [parsedProtParams, _, errMsg] = cppbor::parse(protectedParams); + if (!parsedProtParams) { + return errMsg + " when parsing protected params."; + } + if (!parsedProtParams->asMap()) { + return "Protected params must be a map"; + } + + auto& algorithm = parsedProtParams->asMap()->get(ALGORITHM); + if (!algorithm || !algorithm->asInt() || algorithm->asInt()->value() != EDDSA) { + return "Unsupported signature algorithm"; + } + + if (!ignoreSignature) { + const cppbor::Bstr* signature = coseSign1->get(kCoseSign1Signature)->asBstr(); + if (!signature || signature->value().empty()) { + return "Missing signature input"; + } + + bool selfSigned = signingCoseKey.empty(); + + bytevec signatureInput = + cppbor::Array().add("Signature1").add(*protectedParams).add(aad).add(*payload).encode(); + + auto key = + CoseKey::parseP256(selfSigned ? payload->value() : signingCoseKey); + if (!key) return "Bad signing key: " + key.moveMessage(); + + + auto pubkey = getEcPointFromAffineCoordinates( + *key->getBstrValue(CoseKey::PUBKEY_X), *key->getBstrValue(CoseKey::PUBKEY_Y)); + if (!pubkey) return pubkey.moveMessage(); + + if (!ECDSA_verify(signatureInput, signature->value(), *pubkey)) { + return "Signature verification failed"; + } + } + + return payload->value(); +} +} // namespace cppcose diff --git a/ProvisioningTool/src/provision.cpp b/ProvisioningTool/src/provision.cpp new file mode 100644 index 00000000..e70e5ce8 --- /dev/null +++ b/ProvisioningTool/src/provision.cpp @@ -0,0 +1,329 @@ +/* + ** + ** Copyright 2021, 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 "socket.h" +#include +#include +#include +#include +#include +#include + +#define SE_POWER_RESET_STATUS_FLAG (1 << 30) +// TODO keymint provision status +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_PRESHARED_SECRET = 0x10, + PROVISION_STATUS_BOOT_PARAM = 0x20, + PROVISION_STATUS_PROVISIONING_LOCKED = 0x40, +}; + +// TODO keymint provision status and lock +std::string provisionStatusApdu = hex2str("80084000000000"); +std::string lockProvisionApdu = hex2str("80074000000000"); + +Json::Value root; +static std::string inputFileName; +using cppbor::Item; +using cppbor::Array; +using cppbor::Uint; +using cppbor::MajorType; + +// static function declarations +static uint16_t getApduStatus(std::vector& inputData); +static int sendData(std::shared_ptr& pSocket, std::string input, std::vector& response); +static int provisionData(std::shared_ptr& pSocket, std::string apdu, std::vector& response); +static int provisionData(std::shared_ptr& pSocket, const char* jsonKey); +static int getUint64(const std::unique_ptr &item, const uint32_t pos, uint64_t *value); + + +// Print usage. +void usage() { + printf("Usage: Please consturcture the apdu(s) with help of construct apdu tool and pass the output file to this utility.\n"); + printf("provision_keymint [options]\n"); + printf("Valid options are:\n"); + printf("-h, --help show this help message and exit.\n"); + printf("-i, --input jsonFile \t Input json file \n"); + printf("-s, --provision_status jsonFile \t Gets the provision status of applet. \n"); + printf("-l, --lock_provision jsonFile \t Gets the provision status of applet. \n"); + +} + +static uint16_t getApduStatus(std::vector& inputData) { + // Last two bytes are the status SW0SW1 + uint8_t SW0 = inputData.at(inputData.size() - 2); + uint8_t SW1 = inputData.at(inputData.size() - 1); + return (SW0 << 8 | SW1); +} + +static int sendData(std::shared_ptr& pSocket, std::string input, std::vector& response) { + + std::vector apdu(input.begin(), input.end()); + + if(!pSocket->sendData(apdu, response)) { + std::cout << "Failed to provision attestation key" << std::endl; + return FAILURE; + } + + // Response size should be greater than 2. Cbor output data followed by two bytes of APDU + // status. + if ((response.size() <= 2) || (getApduStatus(response) != APDU_RESP_STATUS_OK)) { + printf("\n Received error response with error: %d\n", getApduStatus(response)); + return FAILURE; + } + // remove the status bytes + response.pop_back(); + response.pop_back(); + return SUCCESS; +} + +int getUint64(const std::unique_ptr &item, const uint32_t pos, uint64_t* value) { + Array *arr = nullptr; + + if (MajorType::ARRAY != item.get()->type()) { + return FAILURE; + } + arr = const_cast(item.get()->asArray()); + if (arr->size() < (pos + 1)) { + return FAILURE; + } + *value = arr->get(pos)->asUint()->value(); + return SUCCESS; +} + + +uint64_t unmaskPowerResetFlag(uint64_t errorCode) { + bool isSeResetOccurred = (0 != (errorCode & SE_POWER_RESET_STATUS_FLAG)); + + if (isSeResetOccurred) { + printf("\n Secure element reset happened\n"); + errorCode &= ~SE_POWER_RESET_STATUS_FLAG; + } + return errorCode; +} + +int provisionData(std::shared_ptr& pSocket, std::string apdu, std::vector& response) { + if (SUCCESS != sendData(pSocket, apdu, response)) { + return FAILURE; + } + auto [item, pos, message] = cppbor::parse(response); + if(item != nullptr) { + uint64_t err; + if(MajorType::ARRAY == item.get()->type()) { + if(SUCCESS != getUint64(item, 0, &err)) { + printf("\n Failed to parse the error code \n"); + return FAILURE; + } + } else if (MajorType::UINT == item.get()->type()) { + const Uint* uintVal = item.get()->asUint(); + err = uintVal->value(); + } + err = unmaskPowerResetFlag(err); + if (err != 0) { + printf("\n Failed with error:%ld", err); + return FAILURE; + } + } else { + printf("\n Failed to parse the response\n"); + return FAILURE; + } + return SUCCESS; +} + +int provisionData(std::shared_ptr& pSocket, const char* jsonKey) { + Json::Value val = root.get(jsonKey, Json::Value::nullRef); + if (!val.isNull()) { + if (val.isString()) { + std::vector response; + if (SUCCESS != provisionData(pSocket, hex2str(val.asString()), response)) { + printf("\n Error while provisioning %s \n", jsonKey); + return FAILURE; + } + } else { + printf("\n Fail: Expected (%s) tag value is string. \n", jsonKey); + return FAILURE; + } + } + printf("\n Successfully provisioned %s \n", jsonKey); + return SUCCESS; +} + +int openConnection(std::shared_ptr& pSocket) { + if (!pSocket->isConnected()) { + if (!pSocket->openConnection()) + return FAILURE; + } else { + printf("\n Socket already opened.\n"); + } + return SUCCESS; +} + +// Parses the input json file. Sends the apdus to JCServer. +int processInputFile() { + // Parse Json file + if (0 != readJsonFile(root, inputFileName)) { + return FAILURE; + } + std::shared_ptr pSocket = SocketTransport::getInstance(); + if (SUCCESS != openConnection(pSocket)) { + printf("\n Failed to open connection \n"); + return FAILURE; + } + if (0 != provisionData(pSocket, kDeviceUniqueKey) || + 0 != provisionData(pSocket, kAdditionalCertChain) || + 0 != provisionData(pSocket, kAttestationIds) || + 0 != provisionData(pSocket, kSharedSecret) || + 0 != provisionData(pSocket, kBootParams)) { + return FAILURE; + } + return SUCCESS; +} + +int lockProvision() { + std::vector response; + std::shared_ptr pSocket = SocketTransport::getInstance(); + if (SUCCESS != openConnection(pSocket)) { + printf("\n Failed to open connection \n"); + return FAILURE; + } + if (SUCCESS != provisionData(pSocket, lockProvisionApdu, response)) { + printf("\n Failed to lock provision.\n"); + return FAILURE; + } + printf("\n Provision lock is successfull.\n"); + return SUCCESS; +} + +int getProvisionStatus() { + std::vector response; + std::shared_ptr pSocket = SocketTransport::getInstance(); + if (SUCCESS != openConnection(pSocket)) { + printf("\n Failed to open connection \n"); + return FAILURE; + } + + if (SUCCESS != provisionData(pSocket, provisionStatusApdu, response)) { + printf("\n Failed to get provision status \n"); + return FAILURE; + } + auto [item, pos, message] = cppbor::parse(response); + if(item != nullptr) { + uint64_t status; + if(SUCCESS != getUint64(item, 1, &status)) { + printf("\n Failed to get the provision status.\n"); + return FAILURE; + } + // TODO Handle Keymint Provision status once added. + if ( (0 != (status & ProvisionStatus::PROVISION_STATUS_ATTESTATION_KEY)) && + (0 != (status & ProvisionStatus::PROVISION_STATUS_ATTESTATION_CERT_CHAIN)) && + (0 != (status & ProvisionStatus::PROVISION_STATUS_ATTESTATION_CERT_PARAMS)) && + (0 != (status & ProvisionStatus::PROVISION_STATUS_PRESHARED_SECRET)) && + (0 != (status & ProvisionStatus::PROVISION_STATUS_BOOT_PARAM))) { + printf("\n SE is provisioned \n"); + } else { + if (0 == (status & ProvisionStatus::PROVISION_STATUS_ATTESTATION_KEY)) { + printf("\n Attestation key is not provisioned \n"); + } + if (0 == (status & ProvisionStatus::PROVISION_STATUS_ATTESTATION_CERT_CHAIN)) { + printf("\n Attestation certificate chain is not provisioned \n"); + } + if (0 == (status & ProvisionStatus::PROVISION_STATUS_ATTESTATION_CERT_PARAMS)) { + printf("\n Attestation certificate params are not provisioned \n"); + } + if (0 == (status & ProvisionStatus::PROVISION_STATUS_PRESHARED_SECRET)) { + printf("\n Shared secret is not provisioned \n"); + } + if (0 == (status & ProvisionStatus::PROVISION_STATUS_BOOT_PARAM)) { + printf("\n Boot params are not provisioned \n"); + } + } + } else { + printf("\n Fail to parse the response \n"); + return FAILURE; + } + return SUCCESS; +} + +int main(int argc, char* argv[]) { + int c; + bool provisionStatusSet = false; + bool lockProvisionSet = false; + + struct option longOpts[] = { + {"input", required_argument, NULL, 'i'}, + {"provision_status", no_argument, NULL, 's'}, + {"lock_provision", no_argument, NULL, 'l'}, + {"help", no_argument, NULL, 'h'}, + {0,0,0,0} + }; + + if (argc <= 1) { + printf("\n Invalid command \n"); + usage(); + return FAILURE; + } + + /* getopt_long stores the option index here. */ + while ((c = getopt_long(argc, argv, ":hls:i:", longOpts, NULL)) != -1) { + switch(c) { + case 'i': + // input file + inputFileName = std::string(optarg); + std::cout << "input file: " << inputFileName << std::endl; + break; + case 's': + provisionStatusSet = true; + break; + case 'l': + lockProvisionSet = true; + break; + case 'h': + // help + usage(); + return SUCCESS; + case ':': + printf("\n Required arguments missing.\n"); + usage(); + return FAILURE; + case '?': + default: + printf("\n Invalid option\n"); + usage(); + return FAILURE; + } + } + // Process input file; send apuds to JCServer over socket. + if (argc >= 3) { + if (SUCCESS != processInputFile()) { + return FAILURE; + } + } + if (provisionStatusSet) + getProvisionStatus(); + if (lockProvisionSet) + lockProvision(); + return SUCCESS; +} + + diff --git a/ProvisioningTool/src/socket.cpp b/ProvisioningTool/src/socket.cpp new file mode 100644 index 00000000..137cab3b --- /dev/null +++ b/ProvisioningTool/src/socket.cpp @@ -0,0 +1,109 @@ +/* + ** + ** Copyright 2021, 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 "socket.h" + +#define PORT 8080 +#define IPADDR "127.0.0.1" +#define MAX_RECV_BUFFER_SIZE 2500 + +using namespace std; + +SocketTransport::~SocketTransport() { + if (closeConnection()) + std::cout << "Socket is closed"; +} + +bool SocketTransport::openConnection() { + struct sockaddr_in serv_addr; + if ((mSocket = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + perror("Socket "); + return false; + } + + serv_addr.sin_family = AF_INET; + serv_addr.sin_port = htons(PORT); + + // Convert IPv4 and IPv6 addresses from text to binary form + if (inet_pton(AF_INET, IPADDR, &serv_addr.sin_addr) <= 0) { + std::cout << "Invalid address/ Address not supported."; + return false; + } + + if (connect(mSocket, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) { + close(mSocket); + perror("Socket "); + return false; + } + socketStatus = true; + return true; +} + +bool SocketTransport::sendData(const std::vector& inData, std::vector& output) { + uint8_t buffer[MAX_RECV_BUFFER_SIZE]; + int count = 1; + while (!socketStatus && count++ < 5) { + sleep(1); + std::cout << "Trying to open socket connection... count: " << count; + openConnection(); + } + + if (count >= 5) { + std::cout << "Failed to open socket connection"; + return false; + } + + if (0 > send(mSocket, inData.data(), inData.size(), 0)) { + static int connectionResetCnt = 0; /* To avoid loop */ + if (ECONNRESET == errno && connectionResetCnt == 0) { + // Connection reset. Try open socket and then sendData. + socketStatus = false; + connectionResetCnt++; + return sendData(inData, output); + } + std::cout << "Failed to send data over socket err: " << errno; + connectionResetCnt = 0; + return false; + } + + ssize_t valRead = read(mSocket, buffer, MAX_RECV_BUFFER_SIZE); + if (0 > valRead) { + std::cout << "Failed to read data from socket."; + } + for (ssize_t i = 0; i < valRead; i++) { + output.push_back(buffer[i]); + } + return true; +} + +bool SocketTransport::closeConnection() { + close(mSocket); + socketStatus = false; + return true; +} + +bool SocketTransport::isConnected() { + return socketStatus; +} + diff --git a/ProvisioningTool/src/utils.cpp b/ProvisioningTool/src/utils.cpp new file mode 100644 index 00000000..41ad8a6c --- /dev/null +++ b/ProvisioningTool/src/utils.cpp @@ -0,0 +1,96 @@ +/* + ** + ** Copyright 2021, 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 + + +constexpr char hex_value[256] = {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, // + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, // '0'..'9' + 0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 'A'..'F' + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // + 0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 'a'..'f' + 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, // + 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, // + 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, // + 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, // + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + +std::string getHexString(std::vector& input) { + std::stringstream ss; + for (auto b : input) { + ss << std::setw(2) << std::setfill('0') << std::hex << (int) (b & 0xFF); + } + return ss.str(); +} + + +std::string hex2str(std::string a) { + std::string b; + size_t num = a.size() / 2; + b.resize(num); + for (size_t i = 0; i < num; i++) { + b[i] = (hex_value[a[i * 2] & 0xFF] << 4) + (hex_value[a[i * 2 + 1] & 0xFF]); + } + return b; +} + + +// Parses the json file and returns 0 if success; otherwise 1. +int readJsonFile(Json::Value& root, std::string& inputFileName) { + Json::CharReaderBuilder builder; + std::string errorMessage; + + if(!root.empty()) { + printf("\n Already parsed \n"); + return 1; + } + std::ifstream stream(inputFileName); + if (Json::parseFromStream(builder, stream, &root, &errorMessage)) { + printf("\n Parsed json file successfully.\n"); + return 0; + } else { + printf("\n Failed to parse json file error:%s\n", errorMessage.c_str()); + return 1; + } +} + +// Write the json data to the output file. +int writeJsonFile(Json::Value& writerRoot, std::string& outputFileName) { + + std::ofstream ofs; + // Delete file if already exists. + std::remove(outputFileName.data()); + ofs.open(outputFileName, std::ofstream::out | std::ios_base::app); + if (ofs.fail()) { + printf("\n Fail to open the output file:%s", outputFileName.c_str()); + return FAILURE; + } + + Json::StyledWriter styledWriter; + ofs << styledWriter.write(writerRoot); + + ofs.close(); + return SUCCESS; +} \ No newline at end of file diff --git a/README.md b/README.md index 670e94a1..d87ad351 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,8 @@ -# 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). +**JavaCardKeymaster** +JavaCard implementation of the following: +1) [Android Keymint HAL](https://cs.android.com/android/platform/superproject/+/master:hardware/interfaces/security/keymint/aidl/android/hardware/security/keymint/) +2) [Android SharedSecret HAL](https://cs.android.com/android/platform/superproject/+/master:hardware/interfaces/security/sharedsecret/aidl/android/hardware/security/sharedsecret/) -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). +**Exceptions** +1) [Remote Key Provisiong HAL](https://cs.android.com/android/platform/superproject/+/master:hardware/interfaces/security/keymint/aidl/android/hardware/security/keymint/IRemotelyProvisionedComponent.aidl) +is not supported in the release. diff --git a/TestingTools/JCProxy/src/com/android/javacard/jcproxy/JCProxyMain.java b/TestingTools/JCProxy/src/com/android/javacard/jcproxy/JCProxyMain.java index 2de1feba..90832b3f 100644 --- a/TestingTools/JCProxy/src/com/android/javacard/jcproxy/JCProxyMain.java +++ b/TestingTools/JCProxy/src/com/android/javacard/jcproxy/JCProxyMain.java @@ -35,7 +35,7 @@ public static void main(String[] args) { while (true) { try { Socket socket = serverSocket.accept(); - System.out.println("\n\n\n\n\n"); + System.out.println("\n"); System.out.println("------------------------New client connected on " + socket.getPort() + "--------------------"); OutputStream output = null; diff --git a/aosp_integration_patches/device_google_cuttlefish.patch b/aosp_integration_patches/device_google_cuttlefish.patch new file mode 100644 index 00000000..825676a9 --- /dev/null +++ b/aosp_integration_patches/device_google_cuttlefish.patch @@ -0,0 +1,60 @@ +diff --git a/shared/device.mk b/shared/device.mk +index c86840539..e61e5276b 100644 +--- a/shared/device.mk ++++ b/shared/device.mk +@@ -558,6 +558,9 @@ endif + PRODUCT_PACKAGES += \ + $(LOCAL_KEYMINT_PRODUCT_PACKAGE) + ++PRODUCT_PACKAGES += \ ++ android.hardware.security.keymint-service.strongbox \ ++ + # Keymint configuration + ifneq ($(LOCAL_PREFER_VENDOR_APEX),true) + PRODUCT_COPY_FILES += \ +diff --git a/shared/sepolicy/vendor/file_contexts b/shared/sepolicy/vendor/file_contexts +index cebac258e..42d3ad6f9 100644 +--- a/shared/sepolicy/vendor/file_contexts ++++ b/shared/sepolicy/vendor/file_contexts +@@ -93,6 +93,7 @@ + /vendor/bin/hw/android\.hardware\.input\.classifier@1\.0-service.default u:object_r:hal_input_classifier_default_exec:s0 + /vendor/bin/hw/android\.hardware\.thermal@2\.0-service\.mock u:object_r:hal_thermal_default_exec:s0 + /vendor/bin/hw/android\.hardware\.security\.keymint-service\.remote u:object_r:hal_keymint_remote_exec:s0 ++/vendor/bin/hw/android\.hardware\.security\.keymint-service\.strongbox u:object_r:hal_keymint_strongbox_exec:s0 + /vendor/bin/hw/android\.hardware\.keymaster@4\.1-service.remote u:object_r:hal_keymaster_remote_exec:s0 + /vendor/bin/hw/android\.hardware\.gatekeeper@1\.0-service.remote u:object_r:hal_gatekeeper_remote_exec:s0 + /vendor/bin/hw/android\.hardware\.confirmationui@1\.0-service.cuttlefish u:object_r:hal_confirmationui_cuttlefish_exec:s0 +diff --git a/shared/sepolicy/vendor/hal_keymint_strongbox.te b/shared/sepolicy/vendor/hal_keymint_strongbox.te +new file mode 100644 +index 000000000..839fd1a6b +--- /dev/null ++++ b/shared/sepolicy/vendor/hal_keymint_strongbox.te +@@ -0,0 +1,14 @@ ++type hal_keymint_strongbox, domain; ++hal_server_domain(hal_keymint_strongbox, hal_keymint) ++ ++type hal_keymint_strongbox_exec, exec_type, vendor_file_type, file_type; ++init_daemon_domain(hal_keymint_strongbox) ++ ++vndbinder_use(hal_keymint_strongbox) ++get_prop(hal_keymint_strongbox, vendor_security_patch_level_prop); ++ ++# Allow access to sockets ++allow hal_keymint_strongbox self:tcp_socket { connect create write read getattr getopt setopt }; ++allow hal_keymint_strongbox port_type:tcp_socket name_connect; ++allow hal_keymint_strongbox port:tcp_socket { name_connect }; ++allow hal_keymint_strongbox vendor_data_file:file { open read getattr }; +diff --git a/shared/sepolicy/vendor/service_contexts b/shared/sepolicy/vendor/service_contexts +index d20d026cf..b8f0155ab 100644 +--- a/shared/sepolicy/vendor/service_contexts ++++ b/shared/sepolicy/vendor/service_contexts +@@ -4,6 +4,9 @@ android.hardware.neuralnetworks.IDevice/nnapi-sample_float_slow u:object_r:hal_n + android.hardware.neuralnetworks.IDevice/nnapi-sample_minimal u:object_r:hal_neuralnetworks_service:s0 + android.hardware.neuralnetworks.IDevice/nnapi-sample_quant u:object_r:hal_neuralnetworks_service:s0 + android.hardware.neuralnetworks.IDevice/nnapi-sample_sl_shim u:object_r:hal_neuralnetworks_service:s0 ++android.hardware.security.keymint.IKeyMintDevice/strongbox u:object_r:hal_keymint_service:s0 ++android.hardware.security.sharedsecret.ISharedSecret/strongbox u:object_r:hal_sharedsecret_service:s0 ++android.hardware.security.keymint.IRemotelyProvisionedComponent/strongbox u:object_r:hal_keymint_service:s0 + + # Binder service mappings + gce u:object_r:gce_service:s0 diff --git a/aosp_integration_patches/hardware_interfaces.patch b/aosp_integration_patches/hardware_interfaces.patch new file mode 100644 index 00000000..e76fa74c --- /dev/null +++ b/aosp_integration_patches/hardware_interfaces.patch @@ -0,0 +1,1176 @@ +diff --git a/compatibility_matrices/compatibility_matrix.current.xml b/compatibility_matrices/compatibility_matrix.current.xml +index 0b779eee4..33bd0d97d 100644 +--- a/compatibility_matrices/compatibility_matrix.current.xml ++++ b/compatibility_matrices/compatibility_matrix.current.xml +@@ -330,6 +330,13 @@ + default + + ++ ++ android.hardware.security.keymint ++ ++ IRemotelyProvisionedComponent ++ strongbox ++ ++ + + android.hardware.light + 1 +@@ -491,6 +498,14 @@ + default + + ++ ++ android.hardware.security.sharedsecret ++ 1 ++ ++ ISharedSecret ++ strongbox ++ ++ + + android.hardware.sensors + 1.0 +diff --git a/security/keymint/aidl/vts/functional/AttestKeyTest.cpp b/security/keymint/aidl/vts/functional/AttestKeyTest.cpp +index 26ed34427..2d5bc9575 100644 +--- a/security/keymint/aidl/vts/functional/AttestKeyTest.cpp ++++ b/security/keymint/aidl/vts/functional/AttestKeyTest.cpp +@@ -198,7 +198,7 @@ TEST_P(AttestKeyTest, RsaAttestedAttestKeys) { + AttestationKey attest_key; + vector attest_key_characteristics; + vector attest_key_cert_chain; +- ASSERT_EQ(ErrorCode::OK, ++ auto result = + GenerateKey(AuthorizationSetBuilder() + .RsaSigningKey(2048, 65537) + .AttestKey() +@@ -209,7 +209,13 @@ TEST_P(AttestKeyTest, RsaAttestedAttestKeys) { + .Authorization(TAG_NO_AUTH_REQUIRED) + .SetDefaultValidity(), + {} /* attestation signing key */, &attest_key.keyBlob, +- &attest_key_characteristics, &attest_key_cert_chain)); ++ &attest_key_characteristics, &attest_key_cert_chain); ++ //Strongbox does not support Factory provisioned attestation key. ++ if (SecLevel() == SecurityLevel::STRONGBOX) { ++ ASSERT_EQ(ErrorCode::ATTESTATION_KEYS_NOT_PROVISIONED, result); ++ return; ++ } ++ ASSERT_EQ(ErrorCode::OK, result); + + EXPECT_GT(attest_key_cert_chain.size(), 1); + verify_subject_and_serial(attest_key_cert_chain[0], serial_int, subject, false); +@@ -297,7 +303,7 @@ TEST_P(AttestKeyTest, RsaAttestKeyChaining) { + attest_key_opt = attest_key; + } + +- EXPECT_EQ(ErrorCode::OK, ++ auto result = + GenerateKey(AuthorizationSetBuilder() + .RsaSigningKey(2048, 65537) + .AttestKey() +@@ -308,8 +314,13 @@ TEST_P(AttestKeyTest, RsaAttestKeyChaining) { + .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der) + .SetDefaultValidity(), + attest_key_opt, &key_blob_list[i], &attested_key_characteristics, +- &cert_chain_list[i])); +- ++ &cert_chain_list[i]); ++ // Strongbox does not support Factory provisioned attestation key. ++ if (SecLevel() == SecurityLevel::STRONGBOX) { ++ ASSERT_EQ(ErrorCode::ATTESTATION_KEYS_NOT_PROVISIONED, result); ++ return; ++ } ++ ASSERT_EQ(ErrorCode::OK, result); + AuthorizationSet hw_enforced = HwEnforcedAuthorizations(attested_key_characteristics); + AuthorizationSet sw_enforced = SwEnforcedAuthorizations(attested_key_characteristics); + ASSERT_GT(cert_chain_list[i].size(), 0); +@@ -369,7 +380,7 @@ TEST_P(AttestKeyTest, EcAttestKeyChaining) { + attest_key_opt = attest_key; + } + +- EXPECT_EQ(ErrorCode::OK, ++ auto result = + GenerateKey(AuthorizationSetBuilder() + .EcdsaSigningKey(EcCurve::P_256) + .AttestKey() +@@ -380,8 +391,13 @@ TEST_P(AttestKeyTest, EcAttestKeyChaining) { + .Authorization(TAG_NO_AUTH_REQUIRED) + .SetDefaultValidity(), + attest_key_opt, &key_blob_list[i], &attested_key_characteristics, +- &cert_chain_list[i])); +- ++ &cert_chain_list[i]); ++ // Strongbox does not support Factory provisioned attestation key. ++ if (SecLevel() == SecurityLevel::STRONGBOX) { ++ ASSERT_EQ(ErrorCode::ATTESTATION_KEYS_NOT_PROVISIONED, result); ++ return; ++ } ++ ASSERT_EQ(ErrorCode::OK, result); + AuthorizationSet hw_enforced = HwEnforcedAuthorizations(attested_key_characteristics); + AuthorizationSet sw_enforced = SwEnforcedAuthorizations(attested_key_characteristics); + ASSERT_GT(cert_chain_list[i].size(), 0); +@@ -442,35 +458,40 @@ TEST_P(AttestKeyTest, AlternateAttestKeyChaining) { + attest_key.keyBlob = key_blob_list[i - 1]; + attest_key_opt = attest_key; + } +- ++ ErrorCode result; + if ((i & 0x1) == 1) { +- EXPECT_EQ(ErrorCode::OK, +- GenerateKey(AuthorizationSetBuilder() +- .EcdsaSigningKey(EcCurve::P_256) +- .AttestKey() +- .AttestationChallenge("foo") +- .AttestationApplicationId("bar") +- .Authorization(TAG_CERTIFICATE_SERIAL, serial_blob) +- .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der) +- .Authorization(TAG_NO_AUTH_REQUIRED) +- .SetDefaultValidity(), +- attest_key_opt, &key_blob_list[i], &attested_key_characteristics, +- &cert_chain_list[i])); ++ result = ++ GenerateKey(AuthorizationSetBuilder() ++ .EcdsaSigningKey(EcCurve::P_256) ++ .AttestKey() ++ .AttestationChallenge("foo") ++ .AttestationApplicationId("bar") ++ .Authorization(TAG_CERTIFICATE_SERIAL, serial_blob) ++ .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der) ++ .Authorization(TAG_NO_AUTH_REQUIRED) ++ .SetDefaultValidity(), ++ attest_key_opt, &key_blob_list[i], &attested_key_characteristics, ++ &cert_chain_list[i]); + } else { +- EXPECT_EQ(ErrorCode::OK, +- GenerateKey(AuthorizationSetBuilder() +- .RsaSigningKey(2048, 65537) +- .AttestKey() +- .AttestationChallenge("foo") +- .AttestationApplicationId("bar") +- .Authorization(TAG_CERTIFICATE_SERIAL, serial_blob) +- .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der) +- .Authorization(TAG_NO_AUTH_REQUIRED) +- .SetDefaultValidity(), +- attest_key_opt, &key_blob_list[i], &attested_key_characteristics, +- &cert_chain_list[i])); ++ result = ++ GenerateKey(AuthorizationSetBuilder() ++ .RsaSigningKey(2048, 65537) ++ .AttestKey() ++ .AttestationChallenge("foo") ++ .AttestationApplicationId("bar") ++ .Authorization(TAG_CERTIFICATE_SERIAL, serial_blob) ++ .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der) ++ .Authorization(TAG_NO_AUTH_REQUIRED) ++ .SetDefaultValidity(), ++ attest_key_opt, &key_blob_list[i], &attested_key_characteristics, ++ &cert_chain_list[i]); + } +- ++ // Strongbox does not support Factory provisioned attestation key. ++ if (SecLevel() == SecurityLevel::STRONGBOX) { ++ ASSERT_EQ(ErrorCode::ATTESTATION_KEYS_NOT_PROVISIONED, result); ++ return; ++ } ++ ASSERT_EQ(ErrorCode::OK, result); + AuthorizationSet hw_enforced = HwEnforcedAuthorizations(attested_key_characteristics); + AuthorizationSet sw_enforced = SwEnforcedAuthorizations(attested_key_characteristics); + ASSERT_GT(cert_chain_list[i].size(), 0); +diff --git a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp +index 20324117b..741bcf8f6 100644 +--- a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp ++++ b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp +@@ -1145,6 +1145,15 @@ vector KeyMintAidlTestBase::InvalidCurves() { + } + } + ++vector KeyMintAidlTestBase::ValidExponents() { ++ if (SecLevel() == SecurityLevel::STRONGBOX) { ++ return {65537}; ++ } else { ++ return {3, 65537}; ++ } ++} ++ ++ + vector KeyMintAidlTestBase::ValidDigests(bool withNone, bool withMD5) { + switch (SecLevel()) { + case SecurityLevel::SOFTWARE: +diff --git a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h +index ec3fcf6a3..0561a9b94 100644 +--- a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h ++++ b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h +@@ -250,7 +250,9 @@ class KeyMintAidlTestBase : public ::testing::TestWithParam { + .SetDefaultValidity(); + tagModifier(&rsaBuilder); + errorCode = GenerateKey(rsaBuilder, &rsaKeyData.blob, &rsaKeyData.characteristics); +- EXPECT_EQ(expectedReturn, errorCode); ++ if (!(SecLevel() == SecurityLevel::STRONGBOX && ErrorCode::ATTESTATION_KEYS_NOT_PROVISIONED == errorCode)) { ++ EXPECT_EQ(expectedReturn, errorCode); ++ } + + /* ECDSA */ + KeyData ecdsaKeyData; +@@ -262,7 +264,10 @@ class KeyMintAidlTestBase : public ::testing::TestWithParam { + .SetDefaultValidity(); + tagModifier(&ecdsaBuilder); + errorCode = GenerateKey(ecdsaBuilder, &ecdsaKeyData.blob, &ecdsaKeyData.characteristics); +- EXPECT_EQ(expectedReturn, errorCode); ++ if (!(SecLevel() == SecurityLevel::STRONGBOX && ErrorCode::ATTESTATION_KEYS_NOT_PROVISIONED == errorCode)) { ++ EXPECT_EQ(expectedReturn, errorCode); ++ } ++ + return {aesKeyData, hmacKeyData, rsaKeyData, ecdsaKeyData}; + } + bool IsSecure() const { return securityLevel_ != SecurityLevel::SOFTWARE; } +@@ -279,6 +284,7 @@ class KeyMintAidlTestBase : public ::testing::TestWithParam { + vector InvalidCurves(); + + vector ValidDigests(bool withNone, bool withMD5); ++ vector ValidExponents(); + + static vector build_params() { + auto params = ::android::getAidlHalInstanceNames(IKeyMintDevice::descriptor); +diff --git a/security/keymint/aidl/vts/functional/KeyMintTest.cpp b/security/keymint/aidl/vts/functional/KeyMintTest.cpp +index 651b21f19..0e6e882d3 100644 +--- a/security/keymint/aidl/vts/functional/KeyMintTest.cpp ++++ b/security/keymint/aidl/vts/functional/KeyMintTest.cpp +@@ -908,8 +908,8 @@ TEST_P(NewKeyGenerationTest, RsaWithAttestation) { + for (auto key_size : ValidKeySizes(Algorithm::RSA)) { + vector key_blob; + vector key_characteristics; +- ASSERT_EQ(ErrorCode::OK, +- GenerateKey(AuthorizationSetBuilder() ++ ++ auto result = GenerateKey(AuthorizationSetBuilder() + .RsaSigningKey(key_size, 65537) + .Digest(Digest::NONE) + .Padding(PaddingMode::NONE) +@@ -919,8 +919,14 @@ TEST_P(NewKeyGenerationTest, RsaWithAttestation) { + .Authorization(TAG_CERTIFICATE_SERIAL, serial_blob) + .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der) + .SetDefaultValidity(), +- &key_blob, &key_characteristics)); ++ &key_blob, &key_characteristics); + ++ // Strongbox does not support Factory provisioned attestation key ++ if (SecLevel() == SecurityLevel::STRONGBOX) { ++ ASSERT_EQ(ErrorCode::ATTESTATION_KEYS_NOT_PROVISIONED, result); ++ return; ++ } ++ ASSERT_EQ(ErrorCode::OK, result); + ASSERT_GT(key_blob.size(), 0U); + CheckBaseParams(key_characteristics); + CheckCharacteristics(key_blob, key_characteristics); +@@ -1037,8 +1043,7 @@ TEST_P(NewKeyGenerationTest, RsaEncryptionWithAttestation) { + + vector key_blob; + vector key_characteristics; +- ASSERT_EQ(ErrorCode::OK, +- GenerateKey(AuthorizationSetBuilder() ++ auto result = GenerateKey(AuthorizationSetBuilder() + .RsaEncryptionKey(key_size, 65537) + .Padding(PaddingMode::NONE) + .AttestationChallenge(challenge) +@@ -1047,8 +1052,14 @@ TEST_P(NewKeyGenerationTest, RsaEncryptionWithAttestation) { + .Authorization(TAG_CERTIFICATE_SERIAL, serial_blob) + .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der) + .SetDefaultValidity(), +- &key_blob, &key_characteristics)); ++ &key_blob, &key_characteristics); + ++ // Strongbox does not support Factory provisioned attestation key ++ if (SecLevel() == SecurityLevel::STRONGBOX) { ++ ASSERT_EQ(ErrorCode::ATTESTATION_KEYS_NOT_PROVISIONED, result); ++ return; ++ } ++ ASSERT_EQ(ErrorCode::OK, result); + ASSERT_GT(key_blob.size(), 0U); + AuthorizationSet auths; + for (auto& entry : key_characteristics) { +@@ -1149,15 +1160,21 @@ TEST_P(NewKeyGenerationTest, RsaWithAttestationMissAppId) { + vector key_blob; + vector key_characteristics; + +- ASSERT_EQ(ErrorCode::ATTESTATION_APPLICATION_ID_MISSING, +- GenerateKey(AuthorizationSetBuilder() ++ auto result = GenerateKey(AuthorizationSetBuilder() + .RsaSigningKey(2048, 65537) + .Digest(Digest::NONE) + .Padding(PaddingMode::NONE) + .AttestationChallenge(challenge) + .Authorization(TAG_NO_AUTH_REQUIRED) + .SetDefaultValidity(), +- &key_blob, &key_characteristics)); ++ &key_blob, &key_characteristics); ++ ++ // Strongbox does not support Factory provisioned attestation key ++ if (SecLevel() == SecurityLevel::STRONGBOX) { ++ ASSERT_EQ(ErrorCode::ATTESTATION_KEYS_NOT_PROVISIONED, result); ++ return; ++ } ++ ASSERT_EQ(ErrorCode::ATTESTATION_APPLICATION_ID_MISSING, result); + } + + /* +@@ -1267,8 +1284,8 @@ TEST_P(NewKeyGenerationTest, LimitedUsageRsaWithAttestation) { + for (auto key_size : ValidKeySizes(Algorithm::RSA)) { + vector key_blob; + vector key_characteristics; +- ASSERT_EQ(ErrorCode::OK, +- GenerateKey(AuthorizationSetBuilder() ++ ++ auto result = GenerateKey(AuthorizationSetBuilder() + .RsaSigningKey(key_size, 65537) + .Digest(Digest::NONE) + .Padding(PaddingMode::NONE) +@@ -1279,7 +1296,14 @@ TEST_P(NewKeyGenerationTest, LimitedUsageRsaWithAttestation) { + .Authorization(TAG_CERTIFICATE_SERIAL, serial_blob) + .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der) + .SetDefaultValidity(), +- &key_blob, &key_characteristics)); ++ &key_blob, &key_characteristics); ++ ++ //Strongbox does not support Factory provisioned attestation key ++ if (SecLevel() == SecurityLevel::STRONGBOX) { ++ ASSERT_EQ(ErrorCode::ATTESTATION_KEYS_NOT_PROVISIONED, result); ++ return; ++ } ++ ASSERT_EQ(ErrorCode::OK, result); + + ASSERT_GT(key_blob.size(), 0U); + CheckBaseParams(key_characteristics); +@@ -1410,8 +1434,8 @@ TEST_P(NewKeyGenerationTest, EcdsaAttestation) { + for (auto curve : ValidCurves()) { + vector key_blob; + vector key_characteristics; +- ASSERT_EQ(ErrorCode::OK, +- GenerateKey(AuthorizationSetBuilder() ++ ++ auto result = GenerateKey(AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .EcdsaSigningKey(curve) + .Digest(Digest::NONE) +@@ -1420,7 +1444,15 @@ TEST_P(NewKeyGenerationTest, EcdsaAttestation) { + .Authorization(TAG_CERTIFICATE_SERIAL, serial_blob) + .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der) + .SetDefaultValidity(), +- &key_blob, &key_characteristics)); ++ &key_blob, &key_characteristics); ++ ++ //Strongbox does not support Factory provisioned attestation key ++ if (SecLevel() == SecurityLevel::STRONGBOX) { ++ ASSERT_EQ(ErrorCode::ATTESTATION_KEYS_NOT_PROVISIONED, result); ++ return; ++ } ++ ASSERT_EQ(ErrorCode::OK, result); ++ + ASSERT_GT(key_blob.size(), 0U); + CheckBaseParams(key_characteristics); + CheckCharacteristics(key_blob, key_characteristics); +@@ -1497,6 +1529,12 @@ TEST_P(NewKeyGenerationTest, EcdsaAttestationTags) { + // Tag not required to be supported by all KeyMint implementations. + continue; + } ++ ++ //Strongbox does not support Factory provisioned attestation key ++ if (SecLevel() == SecurityLevel::STRONGBOX) { ++ ASSERT_EQ(ErrorCode::ATTESTATION_KEYS_NOT_PROVISIONED, result); ++ continue; ++ } + ASSERT_EQ(result, ErrorCode::OK); + ASSERT_GT(key_blob.size(), 0U); + +@@ -1546,8 +1584,14 @@ TEST_P(NewKeyGenerationTest, EcdsaAttestationTags) { + .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der) + .SetDefaultValidity(); + builder.push_back(tag); +- ASSERT_EQ(ErrorCode::CANNOT_ATTEST_IDS, +- GenerateKey(builder, &key_blob, &key_characteristics)); ++ ++ auto result = GenerateKey(builder, &key_blob, &key_characteristics); ++ //Strongbox does not support Factory provisioned attestation key ++ if (SecLevel() == SecurityLevel::STRONGBOX) { ++ ASSERT_EQ(ErrorCode::ATTESTATION_KEYS_NOT_PROVISIONED, result); ++ continue; ++ } ++ ASSERT_EQ(ErrorCode::CANNOT_ATTEST_IDS, result); + } + } + +@@ -1583,6 +1627,13 @@ TEST_P(NewKeyGenerationTest, EcdsaAttestationTagNoApplicationId) { + .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der) + .SetDefaultValidity(), + &key_blob, &key_characteristics); ++ ++ // Strongbox does not support Factory provisioned attestation key ++ if (SecLevel() == SecurityLevel::STRONGBOX) { ++ ASSERT_EQ(ErrorCode::ATTESTATION_KEYS_NOT_PROVISIONED, result); ++ return; ++ } ++ + ASSERT_EQ(result, ErrorCode::OK); + ASSERT_GT(key_blob.size(), 0U); + +@@ -1661,13 +1712,19 @@ TEST_P(NewKeyGenerationTest, EcdsaAttestationRequireAppId) { + vector key_blob; + vector key_characteristics; + +- ASSERT_EQ(ErrorCode::ATTESTATION_APPLICATION_ID_MISSING, +- GenerateKey(AuthorizationSetBuilder() ++ auto result = GenerateKey(AuthorizationSetBuilder() + .EcdsaSigningKey(EcCurve::P_256) + .Digest(Digest::NONE) + .AttestationChallenge(challenge) + .SetDefaultValidity(), +- &key_blob, &key_characteristics)); ++ &key_blob, &key_characteristics); ++ ++ // Strongbox does not support Factory provisioned attestation key ++ if (SecLevel() == SecurityLevel::STRONGBOX) { ++ ASSERT_EQ(ErrorCode::ATTESTATION_KEYS_NOT_PROVISIONED, result); ++ return; ++ } ++ ASSERT_EQ(ErrorCode::ATTESTATION_APPLICATION_ID_MISSING, result); + } + + /* +@@ -1724,14 +1781,21 @@ TEST_P(NewKeyGenerationTest, AttestationApplicationIDLengthProperlyEncoded) { + const string app_id(length, 'a'); + vector key_blob; + vector key_characteristics; +- ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() ++ auto result = GenerateKey(AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .EcdsaSigningKey(EcCurve::P_256) + .Digest(Digest::NONE) + .AttestationChallenge(challenge) + .AttestationApplicationId(app_id) + .SetDefaultValidity(), +- &key_blob, &key_characteristics)); ++ &key_blob, &key_characteristics); ++ //Strongbox does not support Factory provisioned attestation key ++ if (SecLevel() == SecurityLevel::STRONGBOX) { ++ ASSERT_EQ(ErrorCode::ATTESTATION_KEYS_NOT_PROVISIONED, result); ++ return; ++ } ++ ASSERT_EQ(ErrorCode::OK, result); ++ + ASSERT_GT(key_blob.size(), 0U); + CheckBaseParams(key_characteristics); + CheckCharacteristics(key_blob, key_characteristics); +@@ -3762,25 +3826,27 @@ typedef KeyMintAidlTestBase EncryptionOperationsTest; + * Verifies that raw RSA decryption works. + */ + TEST_P(EncryptionOperationsTest, RsaNoPaddingSuccess) { +- for (uint64_t exponent : {3, 65537}) { +- ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() +- .Authorization(TAG_NO_AUTH_REQUIRED) +- .RsaEncryptionKey(2048, exponent) +- .Padding(PaddingMode::NONE) +- .SetDefaultValidity())); + +- string message = string(2048 / 8, 'a'); +- auto params = AuthorizationSetBuilder().Padding(PaddingMode::NONE); +- string ciphertext1 = LocalRsaEncryptMessage(message, params); +- EXPECT_EQ(2048U / 8, ciphertext1.size()); ++ for (uint64_t exponent : ValidExponents()) ++ { ++ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() ++ .Authorization(TAG_NO_AUTH_REQUIRED) ++ .RsaEncryptionKey(2048, exponent) ++ .Padding(PaddingMode::NONE) ++ .SetDefaultValidity())); + +- string ciphertext2 = LocalRsaEncryptMessage(message, params); +- EXPECT_EQ(2048U / 8, ciphertext2.size()); ++ string message = string(2048 / 8, 'a'); ++ auto params = AuthorizationSetBuilder().Padding(PaddingMode::NONE); ++ string ciphertext1 = LocalRsaEncryptMessage(message, params); ++ EXPECT_EQ(2048U / 8, ciphertext1.size()); + +- // Unpadded RSA is deterministic +- EXPECT_EQ(ciphertext1, ciphertext2); ++ string ciphertext2 = LocalRsaEncryptMessage(message, params); ++ EXPECT_EQ(2048U / 8, ciphertext2.size()); + +- CheckedDeleteKey(); ++ // Unpadded RSA is deterministic ++ EXPECT_EQ(ciphertext1, ciphertext2); ++ ++ CheckedDeleteKey(); + } + } + +@@ -6297,7 +6363,7 @@ TEST_P(ClearOperationsTest, TooManyOperations) { + size_t i; + + for (i = 0; i < max_operations; i++) { +- result = Begin(KeyPurpose::ENCRYPT, key_blob_, params, &out_params, op_handles[i]); ++ result = Begin(KeyPurpose::DECRYPT, key_blob_, params, &out_params, op_handles[i]); + if (ErrorCode::OK != result) { + break; + } +@@ -6305,12 +6371,12 @@ TEST_P(ClearOperationsTest, TooManyOperations) { + EXPECT_EQ(ErrorCode::TOO_MANY_OPERATIONS, result); + // Try again just in case there's a weird overflow bug + EXPECT_EQ(ErrorCode::TOO_MANY_OPERATIONS, +- Begin(KeyPurpose::ENCRYPT, key_blob_, params, &out_params)); ++ Begin(KeyPurpose::DECRYPT, key_blob_, params, &out_params)); + for (size_t j = 0; j < i; j++) { + EXPECT_EQ(ErrorCode::OK, Abort(op_handles[j])) + << "Aboort failed for i = " << j << std::endl; + } +- EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, key_blob_, params, &out_params)); ++ EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, key_blob_, params, &out_params)); + AbortIfNeeded(); + } + +@@ -6409,7 +6475,6 @@ TEST_P(KeyAgreementTest, Ecdh) { + OPENSSL_free(p); + + // Generate EC key in KeyMint (only access to public key material) +- vector challenge = {0x41, 0x42}; + EXPECT_EQ( + ErrorCode::OK, + GenerateKey(AuthorizationSetBuilder() +@@ -6418,7 +6483,6 @@ TEST_P(KeyAgreementTest, Ecdh) { + .Authorization(TAG_PURPOSE, KeyPurpose::AGREE_KEY) + .Authorization(TAG_ALGORITHM, Algorithm::EC) + .Authorization(TAG_ATTESTATION_APPLICATION_ID, {0x61, 0x62}) +- .Authorization(TAG_ATTESTATION_CHALLENGE, challenge) + .SetDefaultValidity())) + << "Failed to generate key"; + ASSERT_GT(cert_chain_.size(), 0); +@@ -6498,14 +6562,24 @@ TEST_P(EarlyBootKeyTest, CreateEarlyBootKeys) { + CreateTestKeys(TAG_EARLY_BOOT_ONLY, ErrorCode::OK); + + for (const auto& keyData : {aesKeyData, hmacKeyData, rsaKeyData, ecdsaKeyData}) { ++ ++ if (SecLevel() == SecurityLevel::STRONGBOX && keyData.blob.size() == 0U) { ++ continue; ++ } + ASSERT_GT(keyData.blob.size(), 0U); + AuthorizationSet crypto_params = SecLevelAuthorizations(keyData.characteristics); + EXPECT_TRUE(crypto_params.Contains(TAG_EARLY_BOOT_ONLY)) << crypto_params; + } + CheckedDeleteKey(&aesKeyData.blob); + CheckedDeleteKey(&hmacKeyData.blob); +- CheckedDeleteKey(&rsaKeyData.blob); +- CheckedDeleteKey(&ecdsaKeyData.blob); ++ ++ if (rsaKeyData.blob.size() != 0U) { ++ CheckedDeleteKey(&rsaKeyData.blob); ++ } ++ if (ecdsaKeyData.blob.size() != 0U) { ++ CheckedDeleteKey(&ecdsaKeyData.blob); ++ } ++ + } + + /* +@@ -6521,14 +6595,21 @@ TEST_P(EarlyBootKeyTest, CreateAttestedEarlyBootKey) { + }); + + for (const auto& keyData : {aesKeyData, hmacKeyData, rsaKeyData, ecdsaKeyData}) { ++ if (SecLevel() == SecurityLevel::STRONGBOX && keyData.blob.size() == 0U) { ++ continue; ++ } + ASSERT_GT(keyData.blob.size(), 0U); + AuthorizationSet crypto_params = SecLevelAuthorizations(keyData.characteristics); + EXPECT_TRUE(crypto_params.Contains(TAG_EARLY_BOOT_ONLY)) << crypto_params; + } + CheckedDeleteKey(&aesKeyData.blob); + CheckedDeleteKey(&hmacKeyData.blob); +- CheckedDeleteKey(&rsaKeyData.blob); +- CheckedDeleteKey(&ecdsaKeyData.blob); ++ if (rsaKeyData.blob.size() != 0U) { ++ CheckedDeleteKey(&rsaKeyData.blob); ++ } ++ if (ecdsaKeyData.blob.size() != 0U) { ++ CheckedDeleteKey(&ecdsaKeyData.blob); ++ } + } + + /* +diff --git a/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp b/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp +index 38f358686..74e44c7b4 100644 +--- a/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp ++++ b/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp +@@ -164,6 +164,7 @@ class VtsRemotelyProvisionedComponentTests : public testing::TestWithParamgetHardwareInfo(&rpcHardwareInfo).isOk()); + } + + static vector build_params() { +@@ -173,6 +174,7 @@ class VtsRemotelyProvisionedComponentTests : public testing::TestWithParam provisionable_; ++ RpcHardwareInfo rpcHardwareInfo; + }; + + using GenerateKeyTests = VtsRemotelyProvisionedComponentTests; +@@ -273,11 +275,10 @@ TEST_P(GenerateKeyTests, generateEcdsaP256Key_testMode) { + class CertificateRequestTest : public VtsRemotelyProvisionedComponentTests { + protected: + CertificateRequestTest() : eekId_(string_to_bytevec("eekid")), challenge_(randomBytes(32)) { +- generateTestEekChain(3); + } + + void generateTestEekChain(size_t eekLength) { +- auto chain = generateEekChain(eekLength, eekId_); ++ auto chain = generateEekChain(rpcHardwareInfo.supportedEekCurve, eekLength, eekId_); + EXPECT_TRUE(chain) << chain.message(); + if (chain) testEekChain_ = chain.moveValue(); + testEekLength_ = eekLength; +@@ -298,6 +299,17 @@ class CertificateRequestTest : public VtsRemotelyProvisionedComponentTests { + } + } + ++ ErrMsgOr getSessionKey(ErrMsgOr>& senderPubkey) { ++ if (rpcHardwareInfo.supportedEekCurve == RpcHardwareInfo::CURVE_25519 || ++ rpcHardwareInfo.supportedEekCurve == RpcHardwareInfo::CURVE_NONE) { ++ return x25519_HKDF_DeriveKey(testEekChain_.last_pubkey, testEekChain_.last_privkey, ++ senderPubkey->first, false /* senderIsA */); ++ } else { ++ return ECDH_HKDF_DeriveKey(testEekChain_.last_pubkey, testEekChain_.last_privkey, ++ senderPubkey->first, false /* senderIsA */); ++ } ++ } ++ + void checkProtectedData(const DeviceInfo& deviceInfo, const cppbor::Array& keysToSign, + const bytevec& keysToSignMac, const ProtectedData& protectedData, + std::vector* bccOutput = nullptr) { +@@ -310,9 +322,7 @@ class CertificateRequestTest : public VtsRemotelyProvisionedComponentTests { + ASSERT_TRUE(senderPubkey) << senderPubkey.message(); + EXPECT_EQ(senderPubkey->second, eekId_); + +- auto sessionKey = +- x25519_HKDF_DeriveKey(testEekChain_.last_pubkey, testEekChain_.last_privkey, +- senderPubkey->first, false /* senderIsA */); ++ auto sessionKey = getSessionKey(senderPubkey); + ASSERT_TRUE(sessionKey) << sessionKey.message(); + + auto protectedDataPayload = +@@ -322,7 +332,7 @@ class CertificateRequestTest : public VtsRemotelyProvisionedComponentTests { + auto [parsedPayload, __, payloadErrMsg] = cppbor::parse(*protectedDataPayload); + ASSERT_TRUE(parsedPayload) << "Failed to parse payload: " << payloadErrMsg; + ASSERT_TRUE(parsedPayload->asArray()); +- EXPECT_EQ(parsedPayload->asArray()->size(), 2U); ++ EXPECT_LE(parsedPayload->asArray()->size(), 3U); + + auto& signedMac = parsedPayload->asArray()->get(0); + auto& bcc = parsedPayload->asArray()->get(1); +@@ -406,6 +416,7 @@ TEST_P(CertificateRequestTest, NewKeyPerCallInTestMode) { + bytevec keysToSignMac; + DeviceInfo deviceInfo; + ProtectedData protectedData; ++ generateTestEekChain(3); + auto status = provisionable_->generateCertificateRequest( + testMode, {} /* keysToSign */, testEekChain_.chain, challenge_, &deviceInfo, + &protectedData, &keysToSignMac); +@@ -445,7 +456,7 @@ TEST_P(CertificateRequestTest, DISABLED_EmptyRequest_prodMode) { + DeviceInfo deviceInfo; + ProtectedData protectedData; + auto status = provisionable_->generateCertificateRequest( +- testMode, {} /* keysToSign */, getProdEekChain(), challenge_, &deviceInfo, ++ testMode, {} /* keysToSign */, getProdEekChain(rpcHardwareInfo.supportedEekCurve), challenge_, &deviceInfo, + &protectedData, &keysToSignMac); + EXPECT_TRUE(status.isOk()); + } +@@ -486,7 +497,7 @@ TEST_P(CertificateRequestTest, DISABLED_NonEmptyRequest_prodMode) { + DeviceInfo deviceInfo; + ProtectedData protectedData; + auto status = provisionable_->generateCertificateRequest( +- testMode, keysToSign_, getProdEekChain(), challenge_, &deviceInfo, &protectedData, ++ testMode, keysToSign_, getProdEekChain(rpcHardwareInfo.supportedEekCurve), challenge_, &deviceInfo, &protectedData, + &keysToSignMac); + EXPECT_TRUE(status.isOk()); + } +@@ -502,6 +513,7 @@ TEST_P(CertificateRequestTest, NonEmptyRequestCorruptMac_testMode) { + bytevec keysToSignMac; + DeviceInfo deviceInfo; + ProtectedData protectedData; ++ generateTestEekChain(3); + auto status = provisionable_->generateCertificateRequest( + testMode, {keyWithCorruptMac}, testEekChain_.chain, challenge_, &deviceInfo, + &protectedData, &keysToSignMac); +@@ -521,7 +533,7 @@ TEST_P(CertificateRequestTest, NonEmptyRequestCorruptMac_prodMode) { + DeviceInfo deviceInfo; + ProtectedData protectedData; + auto status = provisionable_->generateCertificateRequest( +- testMode, {keyWithCorruptMac}, getProdEekChain(), challenge_, &deviceInfo, ++ testMode, {keyWithCorruptMac}, getProdEekChain(rpcHardwareInfo.supportedEekCurve), challenge_, &deviceInfo, + &protectedData, &keysToSignMac); + ASSERT_FALSE(status.isOk()) << status.getMessage(); + EXPECT_EQ(status.getServiceSpecificError(), BnRemotelyProvisionedComponent::STATUS_INVALID_MAC); +@@ -535,7 +547,7 @@ TEST_P(CertificateRequestTest, NonEmptyCorruptEekRequest_prodMode) { + bool testMode = false; + generateKeys(testMode, 4 /* numKeys */); + +- auto prodEekChain = getProdEekChain(); ++ auto prodEekChain = getProdEekChain(rpcHardwareInfo.supportedEekCurve); + auto [parsedChain, _, parseErr] = cppbor::parse(prodEekChain); + ASSERT_NE(parsedChain, nullptr) << parseErr; + ASSERT_NE(parsedChain->asArray(), nullptr); +@@ -566,7 +578,7 @@ TEST_P(CertificateRequestTest, NonEmptyIncompleteEekRequest_prodMode) { + + // Build an EEK chain that omits the first self-signed cert. + auto truncatedChain = cppbor::Array(); +- auto [chain, _, parseErr] = cppbor::parse(getProdEekChain()); ++ auto [chain, _, parseErr] = cppbor::parse(getProdEekChain(rpcHardwareInfo.supportedEekCurve)); + ASSERT_TRUE(chain); + auto eekChain = chain->asArray(); + ASSERT_NE(eekChain, nullptr); +@@ -594,6 +606,7 @@ TEST_P(CertificateRequestTest, NonEmptyRequest_prodKeyInTestCert) { + bytevec keysToSignMac; + DeviceInfo deviceInfo; + ProtectedData protectedData; ++ generateTestEekChain(3); + auto status = provisionable_->generateCertificateRequest( + true /* testMode */, keysToSign_, testEekChain_.chain, challenge_, &deviceInfo, + &protectedData, &keysToSignMac); +@@ -612,6 +625,7 @@ TEST_P(CertificateRequestTest, NonEmptyRequest_testKeyInProdCert) { + bytevec keysToSignMac; + DeviceInfo deviceInfo; + ProtectedData protectedData; ++ generateTestEekChain(3); + auto status = provisionable_->generateCertificateRequest( + false /* testMode */, keysToSign_, testEekChain_.chain, challenge_, &deviceInfo, + &protectedData, &keysToSignMac); +diff --git a/security/keymint/support/Android.bp b/security/keymint/support/Android.bp +index bdb4cdfea..e1ca68809 100644 +--- a/security/keymint/support/Android.bp ++++ b/security/keymint/support/Android.bp +@@ -62,6 +62,7 @@ cc_library { + "libcppcose_rkp", + "libcrypto", + "libjsoncpp", ++ "android.hardware.security.keymint-V1-ndk", + ], + } + +diff --git a/security/keymint/support/include/remote_prov/remote_prov_utils.h b/security/keymint/support/include/remote_prov/remote_prov_utils.h +index 406b7a9b7..4d9ed2b0c 100644 +--- a/security/keymint/support/include/remote_prov/remote_prov_utils.h ++++ b/security/keymint/support/include/remote_prov/remote_prov_utils.h +@@ -52,6 +52,20 @@ inline constexpr uint8_t kCoseEncodedGeekCert[] = { + 0x31, 0xbf, 0x6b, 0xe8, 0x1e, 0x35, 0xe2, 0xf0, 0x2d, 0xce, 0x6c, 0x2f, 0x4f, 0xf2, + 0xf5, 0x4f, 0xa5, 0xd4, 0x83, 0xad, 0x96, 0xa2, 0xf1, 0x87, 0x58, 0x04}; + ++// The Google ECDSA root key for the Endpoint Encryption Key chain, encoded as COSE_Sign1 ++inline constexpr uint8_t kCoseEncodedEcdsaRootCert[] = { ++ 0x84, 0x43, 0xa1, 0x01, 0x26, 0xa0, 0x58, 0x4d, 0xa5, 0x01, 0x02, 0x03, 0x26, 0x20, 0x01, ++ 0x21, 0x58, 0x20, 0xf7, 0x14, 0x8a, 0xdb, 0x97, 0xf4, 0xcc, 0x53, 0xef, 0xd2, 0x64, 0x11, ++ 0xc4, 0xe3, 0x75, 0x1f, 0x66, 0x1f, 0xa4, 0x71, 0x0c, 0x6c, 0xcf, 0xfa, 0x09, 0x46, 0x80, ++ 0x74, 0x87, 0x54, 0xf2, 0xad, 0x22, 0x58, 0x20, 0x5e, 0x7f, 0x5b, 0xf6, 0xec, 0xe4, 0xf6, ++ 0x19, 0xcc, 0xff, 0x13, 0x37, 0xfd, 0x0f, 0xa1, 0xc8, 0x93, 0xdb, 0x18, 0x06, 0x76, 0xc4, ++ 0x5d, 0xe6, 0xd7, 0x6a, 0x77, 0x86, 0xc3, 0x2d, 0xaf, 0x8f, 0x58, 0x47, 0x30, 0x45, 0x02, ++ 0x20, 0x2f, 0x97, 0x8e, 0x42, 0xfb, 0xbe, 0x07, 0x2d, 0x95, 0x47, 0x85, 0x47, 0x93, 0x40, ++ 0xb0, 0x1f, 0xd4, 0x9b, 0x47, 0xa4, 0xc4, 0x44, 0xa9, 0xf2, 0xa1, 0x07, 0x87, 0x10, 0xc7, ++ 0x9f, 0xcb, 0x11, 0x02, 0x21, 0x00, 0xf4, 0xbf, 0x9f, 0xe8, 0x3b, 0xe0, 0xe7, 0x34, 0x4c, ++ 0x15, 0xfc, 0x7b, 0xc3, 0x7e, 0x33, 0x05, 0xf4, 0xd1, 0x34, 0x3c, 0xed, 0x02, 0x04, 0x60, ++ 0x7a, 0x15, 0xe0, 0x79, 0xd3, 0x8a, 0xff, 0x24}; ++ + /** + * Generates random bytes. + */ +@@ -67,12 +81,12 @@ struct EekChain { + * Generates an X25518 EEK with the specified eekId and an Ed25519 chain of the + * specified length. All keys are generated randomly. + */ +-ErrMsgOr generateEekChain(size_t length, const bytevec& eekId); ++ErrMsgOr generateEekChain(int32_t supportedEekCurve, size_t length, const bytevec& eekId); + + /** + * Returns the CBOR-encoded, production Google Endpoint Encryption Key chain. + */ +-bytevec getProdEekChain(); ++bytevec getProdEekChain(int32_t supportedEekCurve); + + struct BccEntryData { + bytevec pubKey; +diff --git a/security/keymint/support/remote_prov_utils.cpp b/security/keymint/support/remote_prov_utils.cpp +index 0cbee5104..ae5120f8b 100644 +--- a/security/keymint/support/remote_prov_utils.cpp ++++ b/security/keymint/support/remote_prov_utils.cpp +@@ -17,15 +17,195 @@ + #include + #include + ++#include + #include + #include + #include ++#include ++#include ++#include ++#include + #include ++#include + #include + #include + + namespace aidl::android::hardware::security::keymint::remote_prov { + ++constexpr int kP256AffinePointSize = 32; ++ ++using EC_KEY_Ptr = bssl::UniquePtr; ++using EVP_PKEY_Ptr = bssl::UniquePtr; ++using EVP_PKEY_CTX_Ptr = bssl::UniquePtr; ++ ++ErrMsgOr ecKeyGetPrivateKey(const EC_KEY* ecKey) { ++ // Extract private key. ++ const BIGNUM* bignum = EC_KEY_get0_private_key(ecKey); ++ if (bignum == nullptr) { ++ return "Error getting bignum from private key"; ++ } ++ int size = BN_num_bytes(bignum); ++ // Pad with zeros incase the length is lesser than 32. ++ bytevec privKey(32, 0); ++ BN_bn2bin(bignum, privKey.data() + 32 - size); ++ return privKey; ++} ++ ++ErrMsgOr ecKeyGetPublicKey(const EC_KEY* ecKey) { ++ // Extract public key. ++ auto group = EC_GROUP_Ptr(EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1)); ++ if (group.get() == nullptr) { ++ return "Error creating EC group by curve name"; ++ } ++ const EC_POINT* point = EC_KEY_get0_public_key(ecKey); ++ if (point == nullptr) return "Error getting ecpoint from public key"; ++ ++ int size = EC_POINT_point2oct(group.get(), point, ++ POINT_CONVERSION_UNCOMPRESSED, nullptr, 0, ++ nullptr); ++ if (size == 0) { ++ return "Error generating public key encoding"; ++ } ++ ++ bytevec publicKey; ++ publicKey.resize(size); ++ EC_POINT_point2oct(group.get(), point, ++ POINT_CONVERSION_UNCOMPRESSED, publicKey.data(), ++ publicKey.size(), nullptr); ++ return publicKey; ++} ++ ++ErrMsgOr> getAffineCoordinates( ++ const bytevec& pubKey) { ++ auto group = EC_GROUP_Ptr( ++ EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1)); ++ if (group.get() == nullptr) { ++ return "Error creating EC group by curve name"; ++ } ++ auto point = EC_POINT_Ptr(EC_POINT_new(group.get())); ++ if (EC_POINT_oct2point(group.get(), point.get(), pubKey.data(), ++ pubKey.size(), nullptr) != 1) { ++ return "Error decoding publicKey"; ++ } ++ BIGNUM_Ptr x(BN_new()); ++ BIGNUM_Ptr y(BN_new()); ++ BN_CTX_Ptr ctx(BN_CTX_new()); ++ if (!ctx.get()) return "Failed to create BN_CTX instance"; ++ ++ if (!EC_POINT_get_affine_coordinates_GFp(group.get(), point.get(), ++ x.get(), y.get(), ++ ctx.get())) { ++ return "Failed to get affine coordinates from ECPoint"; ++ } ++ bytevec pubX(kP256AffinePointSize); ++ bytevec pubY(kP256AffinePointSize); ++ if (BN_bn2binpad(x.get(), pubX.data(), kP256AffinePointSize) != ++ kP256AffinePointSize) { ++ return "Error in converting absolute value of x cordinate to big-endian"; ++ } ++ if (BN_bn2binpad(y.get(), pubY.data(), kP256AffinePointSize) != ++ kP256AffinePointSize) { ++ return "Error in converting absolute value of y cordinate to big-endian"; ++ } ++ return std::make_tuple(std::move(pubX), std::move(pubY)); ++} ++ ++ErrMsgOr> generateEc256KeyPair() { ++ auto ec_key = EC_KEY_Ptr(EC_KEY_new()); ++ if (ec_key.get() == nullptr) { ++ return "Failed to allocate ec key"; ++ } ++ ++ auto group = EC_GROUP_Ptr(EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1)); ++ if (group.get() == nullptr) { ++ return "Error creating EC group by curve name"; ++ } ++ ++ if (EC_KEY_set_group(ec_key.get(), group.get()) != 1 || ++ EC_KEY_generate_key(ec_key.get()) != 1 || EC_KEY_check_key(ec_key.get()) < 0) { ++ return "Error generating key"; ++ } ++ ++ auto privKey = ecKeyGetPrivateKey(ec_key.get()); ++ if (!privKey) return privKey.moveMessage(); ++ ++ auto pubKey = ecKeyGetPublicKey(ec_key.get()); ++ if (!pubKey) return pubKey.moveMessage(); ++ ++ return std::make_tuple(pubKey.moveValue(), privKey.moveValue()); ++} ++ ++ErrMsgOr> generateX25519KeyPair() { ++ /* Generate X25519 key pair */ ++ bytevec pubKey(X25519_PUBLIC_VALUE_LEN); ++ bytevec privKey(X25519_PRIVATE_KEY_LEN); ++ X25519_keypair(pubKey.data(), privKey.data()); ++ return std::make_tuple(std::move(pubKey), std::move(privKey)); ++} ++ ++ErrMsgOr> generateED25519KeyPair() { ++ /* Generate ED25519 key pair */ ++ bytevec pubKey(ED25519_PUBLIC_KEY_LEN); ++ bytevec privKey(ED25519_PRIVATE_KEY_LEN); ++ ED25519_keypair(pubKey.data(), privKey.data()); ++ return std::make_tuple(std::move(pubKey), std::move(privKey)); ++} ++ ++ErrMsgOr> generateKeyPair( ++ int32_t supportedEekCurve, bool isEek) { ++ ++ switch (supportedEekCurve) { ++ case RpcHardwareInfo::CURVE_NONE: ++ case RpcHardwareInfo::CURVE_25519: ++ if (isEek) { ++ return generateX25519KeyPair(); ++ } ++ return generateED25519KeyPair(); ++ case RpcHardwareInfo::CURVE_P256: ++ return generateEc256KeyPair(); ++ default: ++ return "Unknown EEK Curve."; ++ } ++} ++ ++ErrMsgOr constructCoseKey(int32_t supportedEekCurve, const bytevec& eekId, ++ const bytevec& pubKey) { ++ CoseKeyType keyType; ++ CoseKeyAlgorithm algorithm; ++ CoseKeyCurve curve; ++ bytevec pubX; ++ bytevec pubY; ++ switch (supportedEekCurve) { ++ case RpcHardwareInfo::CURVE_NONE: ++ case RpcHardwareInfo::CURVE_25519: ++ keyType = OCTET_KEY_PAIR; ++ algorithm = (eekId.empty()) ? EDDSA : ECDH_ES_HKDF_256; ++ curve = (eekId.empty()) ? ED25519 : cppcose::X25519; ++ pubX = pubKey; ++ break; ++ case RpcHardwareInfo::CURVE_P256: { ++ keyType = EC2; ++ algorithm = (eekId.empty()) ? ES256 : ECDH_ES_HKDF_256; ++ curve = P256; ++ auto affineCoordinates = getAffineCoordinates(pubKey); ++ if (!affineCoordinates) return affineCoordinates.moveMessage(); ++ std::tie(pubX, pubY) = affineCoordinates.moveValue(); ++ } break; ++ default: ++ return "Unknown EEK Curve."; ++ } ++ cppbor::Map coseKey = cppbor::Map() ++ .add(CoseKey::KEY_TYPE, keyType) ++ .add(CoseKey::ALGORITHM, algorithm) ++ .add(CoseKey::CURVE, curve) ++ .add(CoseKey::PUBKEY_X, pubX); ++ ++ if (!pubY.empty()) coseKey.add(CoseKey::PUBKEY_Y, pubY); ++ if (!eekId.empty()) coseKey.add(CoseKey::KEY_ID, eekId); ++ ++ return coseKey.canonicalize().encode(); ++} ++ + bytevec kTestMacKey(32 /* count */, 0 /* byte value */); + + bytevec randomBytes(size_t numBytes) { +@@ -34,7 +214,17 @@ bytevec randomBytes(size_t numBytes) { + return retval; + } + +-ErrMsgOr generateEekChain(size_t length, const bytevec& eekId) { ++ErrMsgOr constructCoseSign1(int32_t supportedEekCurve, const bytevec& key, ++ const bytevec& payload, const bytevec& aad) { ++ if (supportedEekCurve == RpcHardwareInfo::CURVE_P256) { ++ return constructECDSACoseSign1(key, {} /* protectedParams */, payload, aad); ++ } else { ++ return cppcose::constructCoseSign1(key, payload, aad); ++ } ++} ++ ++ErrMsgOr generateEekChain(int32_t supportedEekCurve, size_t length, ++ const bytevec& eekId) { + if (length < 2) { + return "EEK chain must contain at least 2 certs."; + } +@@ -43,42 +233,31 @@ ErrMsgOr generateEekChain(size_t length, const bytevec& eekId) { + + bytevec prev_priv_key; + for (size_t i = 0; i < length - 1; ++i) { +- bytevec pub_key(ED25519_PUBLIC_KEY_LEN); +- bytevec priv_key(ED25519_PRIVATE_KEY_LEN); +- +- ED25519_keypair(pub_key.data(), priv_key.data()); ++ auto keyPair = generateKeyPair(supportedEekCurve, false); ++ if (!keyPair) keyPair.moveMessage(); ++ auto [pub_key, priv_key] = keyPair.moveValue(); + + // The first signing key is self-signed. + if (prev_priv_key.empty()) prev_priv_key = priv_key; + +- auto coseSign1 = constructCoseSign1(prev_priv_key, +- cppbor::Map() /* payload CoseKey */ +- .add(CoseKey::KEY_TYPE, OCTET_KEY_PAIR) +- .add(CoseKey::ALGORITHM, EDDSA) +- .add(CoseKey::CURVE, ED25519) +- .add(CoseKey::PUBKEY_X, pub_key) +- .canonicalize() +- .encode(), ++ auto coseKey = constructCoseKey(supportedEekCurve, {}, pub_key); ++ if (!coseKey) return coseKey.moveMessage(); ++ ++ auto coseSign1 = constructCoseSign1(supportedEekCurve, prev_priv_key, coseKey.moveValue(), + {} /* AAD */); + if (!coseSign1) return coseSign1.moveMessage(); + eekChain.add(coseSign1.moveValue()); + + prev_priv_key = priv_key; + } ++ auto keyPair = generateKeyPair(supportedEekCurve, true); ++ if (!keyPair) keyPair.moveMessage(); ++ auto [pub_key, priv_key] = keyPair.moveValue(); + +- bytevec pub_key(X25519_PUBLIC_VALUE_LEN); +- bytevec priv_key(X25519_PRIVATE_KEY_LEN); +- X25519_keypair(pub_key.data(), priv_key.data()); ++ auto coseKey = constructCoseKey(supportedEekCurve, eekId, pub_key); ++ if (!coseKey) return coseKey.moveMessage(); + +- auto coseSign1 = constructCoseSign1(prev_priv_key, +- cppbor::Map() /* payload CoseKey */ +- .add(CoseKey::KEY_TYPE, OCTET_KEY_PAIR) +- .add(CoseKey::KEY_ID, eekId) +- .add(CoseKey::ALGORITHM, ECDH_ES_HKDF_256) +- .add(CoseKey::CURVE, cppcose::X25519) +- .add(CoseKey::PUBKEY_X, pub_key) +- .canonicalize() +- .encode(), ++ auto coseSign1 = constructCoseSign1(supportedEekCurve, prev_priv_key, coseKey.moveValue(), + {} /* AAD */); + if (!coseSign1) return coseSign1.moveMessage(); + eekChain.add(coseSign1.moveValue()); +@@ -86,16 +265,15 @@ ErrMsgOr generateEekChain(size_t length, const bytevec& eekId) { + return EekChain{eekChain.encode(), pub_key, priv_key}; + } + +-bytevec getProdEekChain() { +- bytevec prodEek; +- prodEek.reserve(1 + sizeof(kCoseEncodedRootCert) + sizeof(kCoseEncodedGeekCert)); +- +- // In CBOR encoding, 0x82 indicates an array of two items +- prodEek.push_back(0x82); +- prodEek.insert(prodEek.end(), std::begin(kCoseEncodedRootCert), std::end(kCoseEncodedRootCert)); +- prodEek.insert(prodEek.end(), std::begin(kCoseEncodedGeekCert), std::end(kCoseEncodedGeekCert)); +- +- return prodEek; ++bytevec getProdEekChain(int32_t supportedEekCurve) { ++ cppbor::Array chain; ++ if (supportedEekCurve == RpcHardwareInfo::CURVE_P256) { ++ chain.add(cppbor::EncodedItem(bytevec(std::begin(kCoseEncodedEcdsaRootCert), std::end(kCoseEncodedEcdsaRootCert)))); ++ } else { ++ chain.add(cppbor::EncodedItem(bytevec(std::begin(kCoseEncodedRootCert), std::end(kCoseEncodedRootCert)))); ++ chain.add(cppbor::EncodedItem(bytevec(std::begin(kCoseEncodedGeekCert), std::end(kCoseEncodedGeekCert)))); ++ } ++ return chain.encode(); + } + + ErrMsgOr verifyAndParseCoseSign1Cwt(const cppbor::Array* coseSign1, +@@ -122,7 +300,8 @@ ErrMsgOr verifyAndParseCoseSign1Cwt(const cppbor::Array* coseSign1, + } + + auto& algorithm = parsedProtParams->asMap()->get(ALGORITHM); +- if (!algorithm || !algorithm->asInt() || algorithm->asInt()->value() != EDDSA) { ++ if (!algorithm || !algorithm->asInt() || (algorithm->asInt()->value() != EDDSA && ++ algorithm->asInt()->value() != ES256)) { + return "Unsupported signature algorithm"; + } + +@@ -136,16 +315,35 @@ ErrMsgOr verifyAndParseCoseSign1Cwt(const cppbor::Array* coseSign1, + if (!serializedKey || !serializedKey->asBstr()) return "Could not find key entry"; + + bool selfSigned = signingCoseKey.empty(); +- auto key = ++ bytevec key; ++ if (algorithm->asInt()->value() == EDDSA) { ++ auto key = + CoseKey::parseEd25519(selfSigned ? serializedKey->asBstr()->value() : signingCoseKey); +- if (!key) return "Bad signing key: " + key.moveMessage(); ++ if (!key) return "Bad signing key: " + key.moveMessage(); + +- bytevec signatureInput = ++ bytevec signatureInput = + cppbor::Array().add("Signature1").add(*protectedParams).add(aad).add(*payload).encode(); + +- if (!ED25519_verify(signatureInput.data(), signatureInput.size(), signature->value().data(), +- key->getBstrValue(CoseKey::PUBKEY_X)->data())) { +- return "Signature verification failed"; ++ if (!ED25519_verify(signatureInput.data(), signatureInput.size(), signature->value().data(), ++ key->getBstrValue(CoseKey::PUBKEY_X)->data())) { ++ return "Signature verification failed"; ++ } ++ } else { // P256 ++ auto key = ++ CoseKey::parseP256(selfSigned ? serializedKey->asBstr()->value() : signingCoseKey); ++ if (!key || key->getBstrValue(CoseKey::PUBKEY_X)->empty() || ++ key->getBstrValue(CoseKey::PUBKEY_Y)->empty()) { ++ return "Bad signing key: " + key.moveMessage(); ++ } ++ auto publicKey = key->getEcPublicKey(); ++ if (!publicKey) return publicKey.moveMessage(); ++ ++ bytevec signatureInput = ++ cppbor::Array().add("Signature1").add(*protectedParams).add(aad).add(*payload).encode(); ++ ++ if (!verifyEcdsaDigest(publicKey.moveValue(), sha256(signatureInput), signature->value())) { ++ return "Signature verification failed"; ++ } + } + + return serializedKey->asBstr()->value(); +diff --git a/security/keymint/support/remote_prov_utils_test.cpp b/security/keymint/support/remote_prov_utils_test.cpp +index 8697c5190..0009bf713 100644 +--- a/security/keymint/support/remote_prov_utils_test.cpp ++++ b/security/keymint/support/remote_prov_utils_test.cpp +@@ -14,6 +14,7 @@ + * limitations under the License. + */ + ++#include + #include + #include + #include +@@ -35,13 +36,13 @@ using ::keymaster::validateAndExtractEekPubAndId; + using ::testing::ElementsAreArray; + + TEST(RemoteProvUtilsTest, GenerateEekChainInvalidLength) { +- ASSERT_FALSE(generateEekChain(1, /*eekId=*/{})); ++ ASSERT_FALSE(generateEekChain(CURVE_25519, 1, /*eekId=*/{})); + } + + TEST(RemoteProvUtilsTest, GenerateEekChain) { + bytevec kTestEekId = {'t', 'e', 's', 't', 'I', 'd', 0}; + for (size_t length : {2, 3, 31}) { +- auto get_eek_result = generateEekChain(length, kTestEekId); ++ auto get_eek_result = generateEekChain(CURVE_25519, length, kTestEekId); + ASSERT_TRUE(get_eek_result) << get_eek_result.message(); + + auto& [chain, pubkey, privkey] = *get_eek_result; diff --git a/aosp_integration_patches/system_keymaster.patch b/aosp_integration_patches/system_keymaster.patch new file mode 100644 index 00000000..b994b768 --- /dev/null +++ b/aosp_integration_patches/system_keymaster.patch @@ -0,0 +1,441 @@ +diff --git a/cppcose/cppcose.cpp b/cppcose/cppcose.cpp +index bfe9928..5009bfe 100644 +--- a/cppcose/cppcose.cpp ++++ b/cppcose/cppcose.cpp +@@ -21,10 +21,17 @@ + + #include + #include ++#include + + #include + + namespace cppcose { ++constexpr int kP256AffinePointSize = 32; ++ ++using EVP_PKEY_Ptr = bssl::UniquePtr; ++using EVP_PKEY_CTX_Ptr = bssl::UniquePtr; ++using ECDSA_SIG_Ptr = bssl::UniquePtr; ++using EC_KEY_Ptr = bssl::UniquePtr; + + namespace { + +@@ -51,6 +58,92 @@ ErrMsgOr> aesGcmInitAndProcessAad(const bytevec& + return std::move(ctx); + } + ++ ++ErrMsgOr signEcdsaDigest(const bytevec& key, const bytevec& data) { ++ auto bn = BIGNUM_Ptr(BN_bin2bn(key.data(), key.size(), nullptr)); ++ if (bn.get() == nullptr) { ++ return "Error creating BIGNUM"; ++ } ++ ++ auto ec_key = EC_KEY_Ptr(EC_KEY_new_by_curve_name(NID_X9_62_prime256v1)); ++ if (EC_KEY_set_private_key(ec_key.get(), bn.get()) != 1) { ++ return "Error setting private key from BIGNUM"; ++ } ++ ++ ECDSA_SIG* sig = ECDSA_do_sign(data.data(), data.size(), ec_key.get()); ++ if (sig == nullptr) { ++ return "Error signing digest"; ++ } ++ size_t len = i2d_ECDSA_SIG(sig, nullptr); ++ bytevec signature(len); ++ unsigned char* p = (unsigned char*)signature.data(); ++ i2d_ECDSA_SIG(sig, &p); ++ ECDSA_SIG_free(sig); ++ return signature; ++} ++ ++ErrMsgOr ecdh(const bytevec& publicKey, const bytevec& privateKey) { ++ auto group = EC_GROUP_Ptr(EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1)); ++ auto point = EC_POINT_Ptr(EC_POINT_new(group.get())); ++ if (EC_POINT_oct2point(group.get(), point.get(), publicKey.data(), publicKey.size(), nullptr) != ++ 1) { ++ return "Error decoding publicKey"; ++ } ++ auto ecKey = EC_KEY_Ptr(EC_KEY_new()); ++ auto pkey = EVP_PKEY_Ptr(EVP_PKEY_new()); ++ if (ecKey.get() == nullptr || pkey.get() == nullptr) { ++ return "Memory allocation failed"; ++ } ++ if (EC_KEY_set_group(ecKey.get(), group.get()) != 1) { ++ return "Error setting group"; ++ } ++ if (EC_KEY_set_public_key(ecKey.get(), point.get()) != 1) { ++ return "Error setting point"; ++ } ++ if (EVP_PKEY_set1_EC_KEY(pkey.get(), ecKey.get()) != 1) { ++ return "Error setting key"; ++ } ++ ++ auto bn = BIGNUM_Ptr(BN_bin2bn(privateKey.data(), privateKey.size(), nullptr)); ++ if (bn.get() == nullptr) { ++ return "Error creating BIGNUM for private key"; ++ } ++ auto privEcKey = EC_KEY_Ptr(EC_KEY_new_by_curve_name(NID_X9_62_prime256v1)); ++ if (EC_KEY_set_private_key(privEcKey.get(), bn.get()) != 1) { ++ return "Error setting private key from BIGNUM"; ++ } ++ auto privPkey = EVP_PKEY_Ptr(EVP_PKEY_new()); ++ if (EVP_PKEY_set1_EC_KEY(privPkey.get(), privEcKey.get()) != 1) { ++ return "Error setting private key"; ++ } ++ ++ auto ctx = EVP_PKEY_CTX_Ptr(EVP_PKEY_CTX_new(privPkey.get(), NULL)); ++ if (ctx.get() == nullptr) { ++ return "Error creating context"; ++ } ++ ++ if (EVP_PKEY_derive_init(ctx.get()) != 1) { ++ return "Error initializing context"; ++ } ++ ++ if (EVP_PKEY_derive_set_peer(ctx.get(), pkey.get()) != 1) { ++ return "Error setting peer"; ++ } ++ ++ /* Determine buffer length for shared secret */ ++ size_t secretLen = 0; ++ if (EVP_PKEY_derive(ctx.get(), NULL, &secretLen) != 1) { ++ return "Error determing length of shared secret"; ++ } ++ bytevec sharedSecret; ++ sharedSecret.resize(secretLen); ++ ++ if (EVP_PKEY_derive(ctx.get(), sharedSecret.data(), &secretLen) != 1) { ++ return "Error deriving shared secret"; ++ } ++ return sharedSecret; ++} ++ + } // namespace + + ErrMsgOr generateHmacSha256(const bytevec& key, const bytevec& data) { +@@ -134,6 +227,17 @@ ErrMsgOr verifyAndParseCoseMac0(const cppbor::Item* macIt + return payload->value(); + } + ++ErrMsgOr createECDSACoseSign1Signature(const bytevec& key, const bytevec& protectedParams, ++ const bytevec& payload, const bytevec& aad) { ++ bytevec signatureInput = cppbor::Array() ++ .add("Signature1") // ++ .add(protectedParams) ++ .add(aad) ++ .add(payload) ++ .encode(); ++ return signEcdsaDigest(key, sha256(signatureInput)); ++} ++ + ErrMsgOr createCoseSign1Signature(const bytevec& key, const bytevec& protectedParams, + const bytevec& payload, const bytevec& aad) { + bytevec signatureInput = cppbor::Array() +@@ -152,6 +256,19 @@ ErrMsgOr createCoseSign1Signature(const bytevec& key, const bytevec& pr + return signature; + } + ++ErrMsgOr constructECDSACoseSign1(const bytevec& key, cppbor::Map protectedParams, ++ const bytevec& payload, const bytevec& aad) { ++ bytevec protParms = protectedParams.add(ALGORITHM, ES256).canonicalize().encode(); ++ auto signature = createECDSACoseSign1Signature(key, protParms, payload, aad); ++ if (!signature) return signature.moveMessage(); ++ ++ return cppbor::Array() ++ .add(std::move(protParms)) ++ .add(cppbor::Map() /* unprotected parameters */) ++ .add(std::move(payload)) ++ .add(std::move(*signature)); ++} ++ + ErrMsgOr constructCoseSign1(const bytevec& key, cppbor::Map protectedParams, + const bytevec& payload, const bytevec& aad) { + bytevec protParms = protectedParams.add(ALGORITHM, EDDSA).canonicalize().encode(); +@@ -193,7 +310,8 @@ ErrMsgOr verifyAndParseCoseSign1(const cppbor::Array* coseSign1, + } + + auto& algorithm = parsedProtParams->asMap()->get(ALGORITHM); +- if (!algorithm || !algorithm->asInt() || algorithm->asInt()->value() != EDDSA) { ++ if (!algorithm || !algorithm->asInt() || ++ !(algorithm->asInt()->value() == EDDSA || algorithm->asInt()->value() == ES256)) { + return "Unsupported signature algorithm"; + } + +@@ -203,17 +321,30 @@ ErrMsgOr verifyAndParseCoseSign1(const cppbor::Array* coseSign1, + } + + bool selfSigned = signingCoseKey.empty(); +- auto key = CoseKey::parseEd25519(selfSigned ? payload->value() : signingCoseKey); +- if (!key || key->getBstrValue(CoseKey::PUBKEY_X)->empty()) { +- return "Bad signing key: " + key.moveMessage(); +- } +- + bytevec signatureInput = + cppbor::Array().add("Signature1").add(*protectedParams).add(aad).add(*payload).encode(); +- +- if (!ED25519_verify(signatureInput.data(), signatureInput.size(), signature->value().data(), +- key->getBstrValue(CoseKey::PUBKEY_X)->data())) { +- return "Signature verification failed"; ++ if (algorithm->asInt()->value() == EDDSA) { ++ auto key = CoseKey::parseEd25519(selfSigned ? payload->value() : signingCoseKey); ++ if (!key || key->getBstrValue(CoseKey::PUBKEY_X)->empty()) { ++ return "Bad signing key: " + key.moveMessage(); ++ } ++ ++ if (!ED25519_verify(signatureInput.data(), signatureInput.size(), signature->value().data(), ++ key->getBstrValue(CoseKey::PUBKEY_X)->data())) { ++ return "Signature verification failed"; ++ } ++ } else { // P256 ++ auto key = CoseKey::parseP256(selfSigned ? payload->value() : signingCoseKey); ++ if (!key || key->getBstrValue(CoseKey::PUBKEY_X)->empty() || ++ key->getBstrValue(CoseKey::PUBKEY_Y)->empty()) { ++ return "Bad signing key: " + key.moveMessage(); ++ } ++ auto publicKey = key->getEcPublicKey(); ++ if (!publicKey) return publicKey.moveMessage(); ++ ++ if (!verifyEcdsaDigest(publicKey.moveValue(), sha256(signatureInput), signature->value())) { ++ return "Signature verification failed"; ++ } + } + + return payload->value(); +@@ -294,28 +425,47 @@ getSenderPubKeyFromCoseEncrypt(const cppbor::Item* coseEncrypt) { + if (!senderCoseKey || !senderCoseKey->asMap()) return "Invalid sender COSE_Key"; + + auto& keyType = senderCoseKey->asMap()->get(CoseKey::KEY_TYPE); +- if (!keyType || !keyType->asInt() || keyType->asInt()->value() != OCTET_KEY_PAIR) { ++ if (!keyType || !keyType->asInt() || (keyType->asInt()->value() != OCTET_KEY_PAIR && ++ keyType->asInt()->value() != EC2)) { + return "Invalid key type"; + } + + auto& curve = senderCoseKey->asMap()->get(CoseKey::CURVE); +- if (!curve || !curve->asInt() || curve->asInt()->value() != X25519) { ++ if (!curve || !curve->asInt() || ++ (keyType->asInt()->value() == OCTET_KEY_PAIR && curve->asInt()->value() != X25519) || ++ (keyType->asInt()->value() == EC2 && curve->asInt()->value() != P256)) { + return "Unsupported curve"; + } + +- auto& pubkey = senderCoseKey->asMap()->get(CoseKey::PUBKEY_X); +- if (!pubkey || !pubkey->asBstr() || +- pubkey->asBstr()->value().size() != X25519_PUBLIC_VALUE_LEN) { +- return "Invalid X25519 public key"; ++ bytevec publicKey; ++ if (keyType->asInt()->value() == EC2) { ++ auto& pubX = senderCoseKey->asMap()->get(CoseKey::PUBKEY_X); ++ if (!pubX || !pubX->asBstr() || pubX->asBstr()->value().size() != kP256AffinePointSize) { ++ return "Invalid EC public key"; ++ } ++ auto& pubY = senderCoseKey->asMap()->get(CoseKey::PUBKEY_Y); ++ if (!pubY || !pubY->asBstr() || pubY->asBstr()->value().size() != kP256AffinePointSize) { ++ return "Invalid EC public key"; ++ } ++ auto key = CoseKey::getEcPublicKey(pubX->asBstr()->value(), pubY->asBstr()->value()); ++ if (!key) return key.moveMessage(); ++ publicKey = key.moveValue(); ++ } else { ++ auto& pubkey = senderCoseKey->asMap()->get(CoseKey::PUBKEY_X); ++ if (!pubkey || !pubkey->asBstr() || ++ pubkey->asBstr()->value().size() != X25519_PUBLIC_VALUE_LEN) { ++ return "Invalid X25519 public key"; ++ } ++ publicKey = pubkey->asBstr()->value(); + } + + auto& key_id = unprotParms->asMap()->get(KEY_ID); + if (key_id && key_id->asBstr()) { +- return std::make_pair(pubkey->asBstr()->value(), key_id->asBstr()->value()); ++ return std::make_pair(publicKey, key_id->asBstr()->value()); + } + + // If no key ID, just return an empty vector. +- return std::make_pair(pubkey->asBstr()->value(), bytevec{}); ++ return std::make_pair(publicKey, bytevec{}); + } + + ErrMsgOr decryptCoseEncrypt(const bytevec& key, const cppbor::Item* coseEncrypt, +@@ -367,6 +517,43 @@ ErrMsgOr decryptCoseEncrypt(const bytevec& key, const cppbor::Item* cos + return aesGcmDecrypt(key, nonce->asBstr()->value(), aad, ciphertext->asBstr()->value()); + } + ++ErrMsgOr ECDH_HKDF_DeriveKey(const bytevec& pubKeyA, const bytevec& privKeyA, ++ const bytevec& pubKeyB, bool senderIsA) { ++ if (privKeyA.empty() || pubKeyA.empty() || pubKeyB.empty()) { ++ return "Missing input key parameters"; ++ } ++ ++ auto rawSharedKey = ecdh(pubKeyB, privKeyA); ++ if (!rawSharedKey) return rawSharedKey.moveMessage(); ++ ++ bytevec kdfContext = cppbor::Array() ++ .add(AES_GCM_256) ++ .add(cppbor::Array() // Sender Info ++ .add(cppbor::Bstr("client")) ++ .add(bytevec{} /* nonce */) ++ .add(senderIsA ? pubKeyA : pubKeyB)) ++ .add(cppbor::Array() // Recipient Info ++ .add(cppbor::Bstr("server")) ++ .add(bytevec{} /* nonce */) ++ .add(senderIsA ? pubKeyB : pubKeyA)) ++ .add(cppbor::Array() // SuppPubInfo ++ .add(kAesGcmKeySizeBits) // output key length ++ .add(bytevec{})) // protected ++ .encode(); ++ ++ bytevec retval(SHA256_DIGEST_LENGTH); ++ bytevec salt{}; ++ if (!HKDF(retval.data(), retval.size(), // ++ EVP_sha256(), // ++ rawSharedKey->data(), rawSharedKey->size(), // ++ salt.data(), salt.size(), // ++ kdfContext.data(), kdfContext.size())) { ++ return "ECDH HKDF failed"; ++ } ++ ++ return retval; ++} ++ + ErrMsgOr x25519_HKDF_DeriveKey(const bytevec& pubKeyA, const bytevec& privKeyA, + const bytevec& pubKeyB, bool senderIsA) { + if (privKeyA.empty() || pubKeyA.empty() || pubKeyB.empty()) { +@@ -460,4 +647,43 @@ ErrMsgOr aesGcmDecrypt(const bytevec& key, const bytevec& nonce, const + return plaintext; + } + ++bytevec sha256(const bytevec& data) { ++ bytevec ret(SHA256_DIGEST_LENGTH); ++ SHA256_CTX ctx; ++ SHA256_Init(&ctx); ++ SHA256_Update(&ctx, data.data(), data.size()); ++ SHA256_Final((unsigned char*)ret.data(), &ctx); ++ return ret; ++} ++ ++bool verifyEcdsaDigest(const bytevec& key, const bytevec& digest, const bytevec& signature) { ++ const unsigned char* p = (unsigned char*)signature.data(); ++ auto sig = ECDSA_SIG_Ptr(d2i_ECDSA_SIG(nullptr, &p, signature.size())); ++ if (sig.get() == nullptr) { ++ return false; ++ } ++ ++ auto group = EC_GROUP_Ptr(EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1)); ++ auto point = EC_POINT_Ptr(EC_POINT_new(group.get())); ++ if (EC_POINT_oct2point(group.get(), point.get(), key.data(), key.size(), nullptr) != 1) { ++ return false; ++ } ++ auto ecKey = EC_KEY_Ptr(EC_KEY_new()); ++ if (ecKey.get() == nullptr) { ++ return false; ++ } ++ if (EC_KEY_set_group(ecKey.get(), group.get()) != 1) { ++ return false; ++ } ++ if (EC_KEY_set_public_key(ecKey.get(), point.get()) != 1) { ++ return false; ++ } ++ ++ int rc = ECDSA_do_verify(digest.data(), digest.size(), sig.get(), ecKey.get()); ++ if (rc != 1) { ++ return false; ++ } ++ return true; ++} ++ + } // namespace cppcose +diff --git a/include/keymaster/cppcose/cppcose.h b/include/keymaster/cppcose/cppcose.h +index 0f97388..03251f1 100644 +--- a/include/keymaster/cppcose/cppcose.h ++++ b/include/keymaster/cppcose/cppcose.h +@@ -24,17 +24,25 @@ + + #include + #include +- ++#include ++#include ++#include + #include + #include + #include + #include ++#include + #include + #include + #include + + namespace cppcose { + ++using BIGNUM_Ptr = bssl::UniquePtr; ++using EC_GROUP_Ptr = bssl::UniquePtr; ++using EC_POINT_Ptr = bssl::UniquePtr; ++using BN_CTX_Ptr = bssl::UniquePtr; ++ + template class ErrMsgOr; + using bytevec = std::vector; + using HmacSha256 = std::array; +@@ -203,6 +211,41 @@ class CoseKey { + return key; + } + ++ static ErrMsgOr getEcPublicKey(const bytevec& pubX, const bytevec& pubY) { ++ auto bnX = BIGNUM_Ptr(BN_bin2bn(pubX.data(), pubX.size(), nullptr)); ++ if (bnX.get() == nullptr) { ++ return "Error creating BIGNUM X Coordinate"; ++ } ++ auto bnY = BIGNUM_Ptr(BN_bin2bn(pubY.data(), pubY.size(), nullptr)); ++ if (bnY.get() == nullptr) { ++ return "Error creating BIGNUM Y Coordinate"; ++ } ++ auto group = EC_GROUP_Ptr(EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1)); ++ auto point = EC_POINT_Ptr(EC_POINT_new(group.get())); ++ if (!point) return "Failed to create EC_POINT instance"; ++ BN_CTX_Ptr ctx(BN_CTX_new()); ++ if (!ctx.get()) return "Failed to create BN_CTX instance"; ++ if (!EC_POINT_set_affine_coordinates_GFp(group.get(), point.get(), bnX.get(), bnY.get(), ++ ctx.get())) { ++ return "Failed to set affine coordinates."; ++ } ++ int size = EC_POINT_point2oct(group.get(), point.get(), POINT_CONVERSION_UNCOMPRESSED, ++ nullptr, 0, nullptr); ++ if (size == 0) { ++ return "Error generating public key encoding"; ++ } ++ bytevec publicKey(size); ++ EC_POINT_point2oct(group.get(), point.get(), POINT_CONVERSION_UNCOMPRESSED, ++ publicKey.data(), publicKey.size(), nullptr); ++ return publicKey; ++ } ++ ++ ErrMsgOr getEcPublicKey() { ++ auto pubX = getBstrValue(PUBKEY_X).value(); ++ auto pubY = getBstrValue(PUBKEY_Y).value(); ++ return getEcPublicKey(pubX, pubY); ++ } ++ + std::optional getIntValue(Label label) { + const auto& value = key_->get(label); + if (!value || !value->asInt()) return {}; +@@ -252,6 +295,8 @@ ErrMsgOr constructCoseSign1(const bytevec& key, const bytevec& pa + const bytevec& aad); + ErrMsgOr constructCoseSign1(const bytevec& key, cppbor::Map extraProtectedFields, + const bytevec& payload, const bytevec& aad); ++ErrMsgOr constructECDSACoseSign1(const bytevec& key, cppbor::Map extraProtectedFields, ++ const bytevec& payload, const bytevec& aad); + /** + * Verify and parse a COSE_Sign1 message, returning the payload. + * +@@ -282,7 +327,10 @@ decryptCoseEncrypt(const bytevec& key, const cppbor::Item* encryptItem, const by + + ErrMsgOr x25519_HKDF_DeriveKey(const bytevec& senderPubKey, const bytevec& senderPrivKey, + const bytevec& recipientPubKey, bool senderIsA); +- ++ErrMsgOr ECDH_HKDF_DeriveKey(const bytevec& pubKeyA, const bytevec& privKeyA, ++ const bytevec& pubKeyB, bool senderIsA); ++bool verifyEcdsaDigest(const bytevec& key, const bytevec& digest, const bytevec& signature); ++bytevec sha256(const bytevec& data); + ErrMsgOr aesGcmEncrypt(const bytevec& key, const bytevec& nonce, + const bytevec& aad, + const bytevec& plaintext); diff --git a/aosp_integration_patches/system_sepolicy.patch b/aosp_integration_patches/system_sepolicy.patch new file mode 100644 index 00000000..e9240a37 --- /dev/null +++ b/aosp_integration_patches/system_sepolicy.patch @@ -0,0 +1,12 @@ +diff --git a/public/hal_neverallows.te b/public/hal_neverallows.te +index faec07420..e73a47849 100644 +--- a/public/hal_neverallows.te ++++ b/public/hal_neverallows.te +@@ -39,6 +39,7 @@ neverallow { + -hal_wifi_supplicant_server + -hal_telephony_server + -hal_uwb_server ++ -hal_keymint_server + } { + domain + userdebug_or_eng(`-su')