diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEProvider.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEProvider.java index 4b0994f4..20417d0f 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEProvider.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEProvider.java @@ -105,7 +105,7 @@ public class KMAndroidSEProvider implements KMSEProvider { public static final byte KEYSIZE_128_OFFSET = 0x00; public static final byte KEYSIZE_256_OFFSET = 0x01; public static final short TMP_ARRAY_SIZE = 256; - public static final short CERT_CHAIN_MAX_SIZE = 2050;//First 2 bytes for length. + public static final short CERT_CHAIN_MAX_SIZE = 2500;//First 2 bytes for length. final byte[] CIPHER_ALGS = { Cipher.ALG_AES_BLOCK_128_CBC_NOPAD, @@ -1143,7 +1143,7 @@ public void persistPartialCertificateChain(byte[] buf, short offset, short len, // 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 > CERT_CHAIN_MAX_SIZE) { + if (totalLen > (short) (CERT_CHAIN_MAX_SIZE - 2)) { KMException.throwIt(KMError.INVALID_INPUT_LENGTH); } short persistedLen = Util.getShort(certificateChain, (short) 0); diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMUtils.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMUtils.java index d9d7e111..cb8df259 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMUtils.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMUtils.java @@ -14,23 +14,36 @@ public class KMUtils { 0, 0, 0, 0, 0x05, 0x26, 0x5C, 0x00 }; // 86400000 msec public static final byte[] oneMonthMsec = { 0, 0, 0, 0, (byte) 0x9C,(byte) 0xBE, (byte) 0xBD, 0x50}; // 2629746000 msec - public static final byte[] oneYearMsec = { - 0, 0, 0, 0x07, 0x58, (byte) 0xF0, (byte) 0xDF, (byte) 0xC0 }; // 31556952000 msec - // Leap year + 3 yrs + public static final byte[] leapYearMsec = { + 0, 0, 0, 0x07, (byte) 0x5C, (byte) 0xD7, (byte) 0x88, 0x00}; //31622400000; + public static final byte[] yearMsec = { + 0, 0, 0, 0x07, 0x57, (byte) 0xB1, 0x2C, 0x00}; //31536000000 + //Leap year(366) + 3 * 365 public static final byte[] fourYrsMsec = { - 0, 0, 0, 0x1D, 0x63, (byte) 0xC3, 0x7F, 0x00 }; // 126227808000 msec + 0, 0, 0, 0x1D, 0x63, (byte) 0xEB, 0x0C, 0x00};//126230400000 public static final byte[] firstJan2020 = { - 0, 0, 0x01, 0x6F, 0x5E, 0x66, (byte)0xE8, 0x00 }; // 1577836800000 msec + 0, 0, 0x01, 0x6F, 0x5E, 0x66, (byte)0xE8, 0x00 }; // 1577836800000 msec public static final byte[] firstJan2051 = { 0, 0, 0x02, 0x53, 0x26, (byte) 0x0E, (byte) 0x1C, 0x00 }; // 2556144000000 // msec + public static final byte[] febMonthLeapMSec = { + 0, 0, 0, 0, (byte) 0x95, 0x58, 0x6C, 0x00 }; //2505600000 + public static final byte[] febMonthMsec = { + 0, 0, 0, 0, (byte) 0x90, 0x32, 0x10, 0x00 }; //2419200000 + public static final byte[] ThirtyOneDaysMonthMsec = { + 0, 0, 0, 0, (byte) 0x9F, (byte) 0xA5, 0x24, 0x00 };//2678400000 + public static final byte[] ThirtDaysMonthMsec = { + 0, 0, 0, 0, (byte) 0x9A, 0x7E, (byte) 0xC8, 0x00 };//2592000000 + public static final short year2051 = 2051; + public static final short year2020 = 2020; // -------------------------------------- public static short convertToDate(short time, byte[] scratchPad, boolean utcFlag) { + short yrsCount = 0; short monthCount = 1; - short dayCount = 0; + short dayCount = 1; short hhCount = 0; short mmCount = 0; short ssCount = 0; @@ -42,17 +55,17 @@ public static short convertToDate(short time, byte[] scratchPad, (short) (8 - KMInteger.cast(time).length()), KMInteger.cast(time) .length()); // If the time is less then 1 Jan 2020 then it is an error - if (Util.arrayCompare(scratchPad, (short) 0, firstJan2020, (short) 0, + if (unsignedByteArrayCompare(scratchPad, (short) 0, firstJan2020, (short) 0, (short) 8) < 0) { KMException.throwIt(KMError.INVALID_ARGUMENT); } if (utcFlag - && Util.arrayCompare(scratchPad, (short) 0, firstJan2051, + && unsignedByteArrayCompare(scratchPad, (short) 0, firstJan2051, (short) 0, (short) 8) >= 0) { KMException.throwIt(KMError.INVALID_ARGUMENT); } - if (Util.arrayCompare(scratchPad, (short) 0, firstJan2051, (short) 0, + if (unsignedByteArrayCompare(scratchPad, (short) 0, firstJan2051, (short) 0, (short) 8) < 0) { Util.arrayCopyNonAtomic(firstJan2020, (short) 0, scratchPad, (short) 8, (short) 8); @@ -68,7 +81,7 @@ public static short convertToDate(short time, byte[] scratchPad, (short) 8); } // divide the given time with four yrs msec count - if (Util.arrayCompare(scratchPad, (short) 0, fourYrsMsec, (short) 0, + if (unsignedByteArrayCompare(scratchPad, (short) 0, fourYrsMsec, (short) 0, (short) 8) >= 0) { Util.arrayCopyNonAtomic(fourYrsMsec, (short) 0, scratchPad, (short) 8, (short) 8); @@ -81,44 +94,97 @@ public static short convertToDate(short time, byte[] scratchPad, Util.arrayCopyNonAtomic(scratchPad, (short) 16, scratchPad, (short) 0, (short) 8); } - // divide the given time with one yr msec count - if (Util.arrayCompare(scratchPad, (short) 0, oneYearMsec, (short) 0, - (short) 8) >= 0) { - Util.arrayCopyNonAtomic(oneYearMsec, (short) 0, scratchPad, (short) 8, - (short) 8); - yrsCount += divide(scratchPad, (short) 0, (short) 8, (short) 16); - Util.arrayCopyNonAtomic(scratchPad, (short) 16, scratchPad, (short) 0, - (short) 8); + + //Get the leap year index starting from the (base Year + yrsCount) Year. + short leapYrIdx = getLeapYrIndex(from2020, yrsCount); + + // if leap year index is 0, then the number of days for the 1st year will be 366 days. + // if leap year index is not 0, then the number of days for the 1st year will be 365 days. + if (((leapYrIdx == 0) && + (unsignedByteArrayCompare(scratchPad, (short) 0, leapYearMsec, (short) 0,(short) 8) >= 0)) || + ((leapYrIdx != 0) && + (unsignedByteArrayCompare(scratchPad, (short) 0, yearMsec, (short) 0,(short) 8) >= 0))) { + for (short i = 0; i < 4; i++) { + yrsCount++; + if (i == leapYrIdx) { + Util.arrayCopyNonAtomic(leapYearMsec, (short) 0, scratchPad, + (short) 8, (short) 8); + } else { + Util.arrayCopyNonAtomic(yearMsec, (short) 0, scratchPad, (short) 8, + (short) 8); + } + subtract(scratchPad, (short) 0, (short) 8, (short) 16); + Util.arrayCopyNonAtomic(scratchPad, (short) 16, scratchPad, (short) 0, + (short) 8); + if (((short) (i + 1) == leapYrIdx)) { + if (unsignedByteArrayCompare(scratchPad, (short) 0, leapYearMsec, + (short) 0, (short) 8) < 0) { + break; + } + } else { + if (unsignedByteArrayCompare(scratchPad, (short) 0, yearMsec, + (short) 0, (short) 8) < 0) { + break; + } + } + } } + // total yrs from 1970 if (from2020) - yrsCount = (short) (2020 + yrsCount); + yrsCount = (short) (year2020 + yrsCount); else - yrsCount = (short) (2051 + yrsCount); + yrsCount = (short) (year2051 + yrsCount); // divide the given time with one month msec count - if (Util.arrayCompare(scratchPad, (short) 0, oneMonthMsec, (short) 0, + if (unsignedByteArrayCompare(scratchPad, (short) 0, oneMonthMsec, (short) 0, (short) 8) >= 0) { - Util.arrayCopyNonAtomic(oneMonthMsec, (short) 0, scratchPad, (short) 8, - (short) 8); - monthCount = divide(scratchPad, (short) 0, (short) 8, (short) 16); - monthCount++; - Util.arrayCopyNonAtomic(scratchPad, (short) 16, scratchPad, (short) 0, - (short) 8); + for (short i = 0; i < 12; i++) { + if (i == 1) { + // Feb month + if (isLeapYear(yrsCount)) { + // Leap year 29 days + Util.arrayCopyNonAtomic(febMonthLeapMSec, (short) 0, scratchPad, + (short) 8, (short) 8); + } else { + // 28 days + Util.arrayCopyNonAtomic(febMonthMsec, (short) 0, scratchPad, + (short) 8, (short) 8); + } + } else if (((i <= 6) && ((i % 2 == 0))) || ((i > 6) && ((i % 2 == 1)))) { + Util.arrayCopyNonAtomic(ThirtyOneDaysMonthMsec, (short) 0, + scratchPad, (short) 8, (short) 8); + } else { + // 30 Days + Util.arrayCopyNonAtomic(ThirtDaysMonthMsec, (short) 0, scratchPad, + (short) 8, (short) 8); + } + + if (unsignedByteArrayCompare(scratchPad, (short) 0, scratchPad, (short) 8, + (short) 8) >= 0) { + subtract(scratchPad, (short) 0, (short) 8, (short) 16); + Util.arrayCopyNonAtomic(scratchPad, (short) 16, scratchPad, (short) 0, + (short) 8); + } else { + break; + } + monthCount++; + } } // divide the given time with one day msec count - if (Util.arrayCompare(scratchPad, (short) 0, oneDayMsec, (short) 0, + if (unsignedByteArrayCompare(scratchPad, (short) 0, oneDayMsec, (short) 0, (short) 8) >= 0) { Util.arrayCopyNonAtomic(oneDayMsec, (short) 0, scratchPad, (short) 8, (short) 8); dayCount = divide(scratchPad, (short) 0, (short) 8, (short) 16); + dayCount++; Util.arrayCopyNonAtomic(scratchPad, (short) 16, scratchPad, (short) 0, (short) 8); } // divide the given time with one hour msec count - if (Util.arrayCompare(scratchPad, (short) 0, oneHourMsec, (short) 0, + if (unsignedByteArrayCompare(scratchPad, (short) 0, oneHourMsec, (short) 0, (short) 8) >= 0) { Util.arrayCopyNonAtomic(oneHourMsec, (short) 0, scratchPad, (short) 8, (short) 8); @@ -128,7 +194,7 @@ public static short convertToDate(short time, byte[] scratchPad, } // divide the given time with one minute msec count - if (Util.arrayCompare(scratchPad, (short) 0, oneMinMsec, (short) 0, + if (unsignedByteArrayCompare(scratchPad, (short) 0, oneMinMsec, (short) 0, (short) 8) >= 0) { Util.arrayCopyNonAtomic(oneMinMsec, (short) 0, scratchPad, (short) 8, (short) 8); @@ -138,7 +204,7 @@ public static short convertToDate(short time, byte[] scratchPad, } // divide the given time with one second msec count - if (Util.arrayCompare(scratchPad, (short) 0, oneSecMsec, (short) 0, + if (unsignedByteArrayCompare(scratchPad, (short) 0, oneSecMsec, (short) 0, (short) 8) >= 0) { Util.arrayCopyNonAtomic(oneSecMsec, (short) 0, scratchPad, (short) 8, (short) 8); @@ -163,6 +229,25 @@ public static short convertToDate(short time, byte[] scratchPad, return KMByteBlob.instance(scratchPad, (short) 0, len); // YYYY } + public static 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; + } + public static short numberToString(short number, byte[] scratchPad, short offset) { byte zero = 0x30; @@ -211,7 +296,7 @@ public static void copy(byte[] buf, short from, short to) { } public static byte compare(byte[] buf, short lhs, short rhs) { - return Util.arrayCompare(buf, lhs, buf, rhs, (short) 8); + return unsignedByteArrayCompare(buf, lhs, buf, rhs, (short) 8); } public static void shiftLeft(byte[] buf, short start) { @@ -319,4 +404,25 @@ public static short countTemporalCount(byte[] bufTime, short timeOff, return divide(scratchPad, (short) 0, (short) 8, (short) 16); } -} + public static boolean isLeapYear(short year) { + if ((short)(year%4) == (short) 0) { + if (((short)(year % 100) == (short) 0) && + ((short) (year % 400)) != (short) 0) { + return false; + } + return true; + } + return false; + } + + public static short getLeapYrIndex(boolean from2020, short yrsCount) { + short newBaseYr = (short) (from2020 ? (year2020 + yrsCount) : (year2051 + yrsCount)); + for(short i = 0; i < 4; i++) { + if(isLeapYear((short)(newBaseYr + i))) { + return i; + } + } + return -1; + } + +} \ No newline at end of file diff --git a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimulator.java b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimulator.java index a9a6a937..08c3d0ea 100644 --- a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimulator.java +++ b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimulator.java @@ -71,7 +71,7 @@ public class KMJCardSimulator implements KMSEProvider { 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 = 2050;//First 2 bytes for length. + private static final short CERT_CHAIN_MAX_SIZE = 2500;//First 2 bytes for length. public static boolean jcardSim = false; @@ -1246,7 +1246,7 @@ public void persistPartialCertificateChain(byte[] buf, short offset, // 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 > CERT_CHAIN_MAX_SIZE) { + if (totalLen > (short) (CERT_CHAIN_MAX_SIZE - 2)) { KMException.throwIt(KMError.INVALID_INPUT_LENGTH); } short persistedLen = Util.getShort(certificateChain, (short) 0); diff --git a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMRsa2048NoDigestSignature.java b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMRsa2048NoDigestSignature.java index 38e9a3bc..3ebf5fe0 100644 --- a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMRsa2048NoDigestSignature.java +++ b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMRsa2048NoDigestSignature.java @@ -107,7 +107,7 @@ private boolean isValidData(byte[] buf, short start, short len) { if (padding == KMType.PADDING_NONE) { if (len > 256) return false; else if (len == 256) { - short v = Util.arrayCompare(buf, start, rsaModulus, (short) 0, len); + short v = KMUtils.unsignedByteArrayCompare(buf, start, rsaModulus, (short) 0, len); if (v > 0) return false; } } else {//pkcs1 no digest diff --git a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMUtils.java b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMUtils.java index 41bbe1ff..cb8df259 100644 --- a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMUtils.java +++ b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMUtils.java @@ -14,23 +14,36 @@ public class KMUtils { 0, 0, 0, 0, 0x05, 0x26, 0x5C, 0x00 }; // 86400000 msec public static final byte[] oneMonthMsec = { 0, 0, 0, 0, (byte) 0x9C,(byte) 0xBE, (byte) 0xBD, 0x50}; // 2629746000 msec - public static final byte[] oneYearMsec = { - 0, 0, 0, 0x07, 0x58, (byte) 0xF0, (byte) 0xDF, (byte) 0xC0 }; // 31556952000 msec - // Leap year + 3 yrs + public static final byte[] leapYearMsec = { + 0, 0, 0, 0x07, (byte) 0x5C, (byte) 0xD7, (byte) 0x88, 0x00}; //31622400000; + public static final byte[] yearMsec = { + 0, 0, 0, 0x07, 0x57, (byte) 0xB1, 0x2C, 0x00}; //31536000000 + //Leap year(366) + 3 * 365 public static final byte[] fourYrsMsec = { - 0, 0, 0, 0x1D, 0x63, (byte) 0xC3, 0x7F, 0x00 }; // 126227808000 msec + 0, 0, 0, 0x1D, 0x63, (byte) 0xEB, 0x0C, 0x00};//126230400000 public static final byte[] firstJan2020 = { 0, 0, 0x01, 0x6F, 0x5E, 0x66, (byte)0xE8, 0x00 }; // 1577836800000 msec public static final byte[] firstJan2051 = { 0, 0, 0x02, 0x53, 0x26, (byte) 0x0E, (byte) 0x1C, 0x00 }; // 2556144000000 // msec + public static final byte[] febMonthLeapMSec = { + 0, 0, 0, 0, (byte) 0x95, 0x58, 0x6C, 0x00 }; //2505600000 + public static final byte[] febMonthMsec = { + 0, 0, 0, 0, (byte) 0x90, 0x32, 0x10, 0x00 }; //2419200000 + public static final byte[] ThirtyOneDaysMonthMsec = { + 0, 0, 0, 0, (byte) 0x9F, (byte) 0xA5, 0x24, 0x00 };//2678400000 + public static final byte[] ThirtDaysMonthMsec = { + 0, 0, 0, 0, (byte) 0x9A, 0x7E, (byte) 0xC8, 0x00 };//2592000000 + public static final short year2051 = 2051; + public static final short year2020 = 2020; // -------------------------------------- public static short convertToDate(short time, byte[] scratchPad, boolean utcFlag) { + short yrsCount = 0; short monthCount = 1; - short dayCount = 0; + short dayCount = 1; short hhCount = 0; short mmCount = 0; short ssCount = 0; @@ -42,17 +55,17 @@ public static short convertToDate(short time, byte[] scratchPad, (short) (8 - KMInteger.cast(time).length()), KMInteger.cast(time) .length()); // If the time is less then 1 Jan 2020 then it is an error - if (Util.arrayCompare(scratchPad, (short) 0, firstJan2020, (short) 0, + if (unsignedByteArrayCompare(scratchPad, (short) 0, firstJan2020, (short) 0, (short) 8) < 0) { KMException.throwIt(KMError.INVALID_ARGUMENT); } if (utcFlag - && Util.arrayCompare(scratchPad, (short) 0, firstJan2051, + && unsignedByteArrayCompare(scratchPad, (short) 0, firstJan2051, (short) 0, (short) 8) >= 0) { KMException.throwIt(KMError.INVALID_ARGUMENT); } - if (Util.arrayCompare(scratchPad, (short) 0, firstJan2051, (short) 0, + if (unsignedByteArrayCompare(scratchPad, (short) 0, firstJan2051, (short) 0, (short) 8) < 0) { Util.arrayCopyNonAtomic(firstJan2020, (short) 0, scratchPad, (short) 8, (short) 8); @@ -68,7 +81,7 @@ public static short convertToDate(short time, byte[] scratchPad, (short) 8); } // divide the given time with four yrs msec count - if (Util.arrayCompare(scratchPad, (short) 0, fourYrsMsec, (short) 0, + if (unsignedByteArrayCompare(scratchPad, (short) 0, fourYrsMsec, (short) 0, (short) 8) >= 0) { Util.arrayCopyNonAtomic(fourYrsMsec, (short) 0, scratchPad, (short) 8, (short) 8); @@ -81,44 +94,97 @@ public static short convertToDate(short time, byte[] scratchPad, Util.arrayCopyNonAtomic(scratchPad, (short) 16, scratchPad, (short) 0, (short) 8); } - // divide the given time with one yr msec count - if (Util.arrayCompare(scratchPad, (short) 0, oneYearMsec, (short) 0, - (short) 8) >= 0) { - Util.arrayCopyNonAtomic(oneYearMsec, (short) 0, scratchPad, (short) 8, - (short) 8); - yrsCount += divide(scratchPad, (short) 0, (short) 8, (short) 16); - Util.arrayCopyNonAtomic(scratchPad, (short) 16, scratchPad, (short) 0, - (short) 8); + + //Get the leap year index starting from the (base Year + yrsCount) Year. + short leapYrIdx = getLeapYrIndex(from2020, yrsCount); + + // if leap year index is 0, then the number of days for the 1st year will be 366 days. + // if leap year index is not 0, then the number of days for the 1st year will be 365 days. + if (((leapYrIdx == 0) && + (unsignedByteArrayCompare(scratchPad, (short) 0, leapYearMsec, (short) 0,(short) 8) >= 0)) || + ((leapYrIdx != 0) && + (unsignedByteArrayCompare(scratchPad, (short) 0, yearMsec, (short) 0,(short) 8) >= 0))) { + for (short i = 0; i < 4; i++) { + yrsCount++; + if (i == leapYrIdx) { + Util.arrayCopyNonAtomic(leapYearMsec, (short) 0, scratchPad, + (short) 8, (short) 8); + } else { + Util.arrayCopyNonAtomic(yearMsec, (short) 0, scratchPad, (short) 8, + (short) 8); + } + subtract(scratchPad, (short) 0, (short) 8, (short) 16); + Util.arrayCopyNonAtomic(scratchPad, (short) 16, scratchPad, (short) 0, + (short) 8); + if (((short) (i + 1) == leapYrIdx)) { + if (unsignedByteArrayCompare(scratchPad, (short) 0, leapYearMsec, + (short) 0, (short) 8) < 0) { + break; + } + } else { + if (unsignedByteArrayCompare(scratchPad, (short) 0, yearMsec, + (short) 0, (short) 8) < 0) { + break; + } + } + } } + // total yrs from 1970 if (from2020) - yrsCount = (short) (2020 + yrsCount); + yrsCount = (short) (year2020 + yrsCount); else - yrsCount = (short) (2051 + yrsCount); + yrsCount = (short) (year2051 + yrsCount); // divide the given time with one month msec count - if (Util.arrayCompare(scratchPad, (short) 0, oneMonthMsec, (short) 0, + if (unsignedByteArrayCompare(scratchPad, (short) 0, oneMonthMsec, (short) 0, (short) 8) >= 0) { - Util.arrayCopyNonAtomic(oneMonthMsec, (short) 0, scratchPad, (short) 8, - (short) 8); - monthCount = divide(scratchPad, (short) 0, (short) 8, (short) 16); - monthCount++; - Util.arrayCopyNonAtomic(scratchPad, (short) 16, scratchPad, (short) 0, - (short) 8); + for (short i = 0; i < 12; i++) { + if (i == 1) { + // Feb month + if (isLeapYear(yrsCount)) { + // Leap year 29 days + Util.arrayCopyNonAtomic(febMonthLeapMSec, (short) 0, scratchPad, + (short) 8, (short) 8); + } else { + // 28 days + Util.arrayCopyNonAtomic(febMonthMsec, (short) 0, scratchPad, + (short) 8, (short) 8); + } + } else if (((i <= 6) && ((i % 2 == 0))) || ((i > 6) && ((i % 2 == 1)))) { + Util.arrayCopyNonAtomic(ThirtyOneDaysMonthMsec, (short) 0, + scratchPad, (short) 8, (short) 8); + } else { + // 30 Days + Util.arrayCopyNonAtomic(ThirtDaysMonthMsec, (short) 0, scratchPad, + (short) 8, (short) 8); + } + + if (unsignedByteArrayCompare(scratchPad, (short) 0, scratchPad, (short) 8, + (short) 8) >= 0) { + subtract(scratchPad, (short) 0, (short) 8, (short) 16); + Util.arrayCopyNonAtomic(scratchPad, (short) 16, scratchPad, (short) 0, + (short) 8); + } else { + break; + } + monthCount++; + } } // divide the given time with one day msec count - if (Util.arrayCompare(scratchPad, (short) 0, oneDayMsec, (short) 0, + if (unsignedByteArrayCompare(scratchPad, (short) 0, oneDayMsec, (short) 0, (short) 8) >= 0) { Util.arrayCopyNonAtomic(oneDayMsec, (short) 0, scratchPad, (short) 8, (short) 8); dayCount = divide(scratchPad, (short) 0, (short) 8, (short) 16); + dayCount++; Util.arrayCopyNonAtomic(scratchPad, (short) 16, scratchPad, (short) 0, (short) 8); } // divide the given time with one hour msec count - if (Util.arrayCompare(scratchPad, (short) 0, oneHourMsec, (short) 0, + if (unsignedByteArrayCompare(scratchPad, (short) 0, oneHourMsec, (short) 0, (short) 8) >= 0) { Util.arrayCopyNonAtomic(oneHourMsec, (short) 0, scratchPad, (short) 8, (short) 8); @@ -128,7 +194,7 @@ public static short convertToDate(short time, byte[] scratchPad, } // divide the given time with one minute msec count - if (Util.arrayCompare(scratchPad, (short) 0, oneMinMsec, (short) 0, + if (unsignedByteArrayCompare(scratchPad, (short) 0, oneMinMsec, (short) 0, (short) 8) >= 0) { Util.arrayCopyNonAtomic(oneMinMsec, (short) 0, scratchPad, (short) 8, (short) 8); @@ -138,7 +204,7 @@ public static short convertToDate(short time, byte[] scratchPad, } // divide the given time with one second msec count - if (Util.arrayCompare(scratchPad, (short) 0, oneSecMsec, (short) 0, + if (unsignedByteArrayCompare(scratchPad, (short) 0, oneSecMsec, (short) 0, (short) 8) >= 0) { Util.arrayCopyNonAtomic(oneSecMsec, (short) 0, scratchPad, (short) 8, (short) 8); @@ -163,6 +229,25 @@ public static short convertToDate(short time, byte[] scratchPad, return KMByteBlob.instance(scratchPad, (short) 0, len); // YYYY } + public static 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; + } + public static short numberToString(short number, byte[] scratchPad, short offset) { byte zero = 0x30; @@ -211,7 +296,7 @@ public static void copy(byte[] buf, short from, short to) { } public static byte compare(byte[] buf, short lhs, short rhs) { - return Util.arrayCompare(buf, lhs, buf, rhs, (short) 8); + return unsignedByteArrayCompare(buf, lhs, buf, rhs, (short) 8); } public static void shiftLeft(byte[] buf, short start) { @@ -319,4 +404,25 @@ public static short countTemporalCount(byte[] bufTime, short timeOff, return divide(scratchPad, (short) 0, (short) 8, (short) 16); } -} + public static boolean isLeapYear(short year) { + if ((short)(year%4) == (short) 0) { + if (((short)(year % 100) == (short) 0) && + ((short) (year % 400)) != (short) 0) { + return false; + } + return true; + } + return false; + } + + public static short getLeapYrIndex(boolean from2020, short yrsCount) { + short newBaseYr = (short) (from2020 ? (year2020 + yrsCount) : (year2051 + yrsCount)); + for(short i = 0; i < 4; i++) { + if(isLeapYear((short)(newBaseYr + i))) { + return i; + } + } + return -1; + } + +} \ No newline at end of file diff --git a/Applet/src/com/android/javacard/keymaster/KMBoolTag.java b/Applet/src/com/android/javacard/keymaster/KMBoolTag.java index d3b916e9..4396470b 100644 --- a/Applet/src/com/android/javacard/keymaster/KMBoolTag.java +++ b/Applet/src/com/android/javacard/keymaster/KMBoolTag.java @@ -42,7 +42,7 @@ public class KMBoolTag extends KMTag { TRUSTED_CONFIRMATION_REQUIRED, UNLOCKED_DEVICE_REQUIRED, RESET_SINCE_ID_ROTATION, - EARLY_BOOT_ENDED, + EARLY_BOOT_ONLY, DEVICE_UNIQUE_ATTESTATION }; diff --git a/Applet/src/com/android/javacard/keymaster/KMInteger.java b/Applet/src/com/android/javacard/keymaster/KMInteger.java index 090f6e86..18944e4d 100644 --- a/Applet/src/com/android/javacard/keymaster/KMInteger.java +++ b/Applet/src/com/android/javacard/keymaster/KMInteger.java @@ -162,7 +162,7 @@ public static short compare(short num1, short num2){ KMInteger.cast(num1).getValue(repository.getHeap(),(short)(num1Buf+(short)(8-len)),len); len = KMInteger.cast(num2).length(); KMInteger.cast(num2).getValue(repository.getHeap(),(short)(num2Buf+(short)(8-len)),len); - return Util.arrayCompare( + return KMUtils.unsignedByteArrayCompare( repository.getHeap(), num1Buf, repository.getHeap(), num2Buf, (short)8); diff --git a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java index a85a7243..1b156d53 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java @@ -3315,7 +3315,7 @@ private static void processGenerateKey(APDU apdu) { data[KEY_PARAMETERS] = KMArray.cast(tmpVariables[2]).get((short) 0); // Check if EarlyBootEnded tag is present. tmpVariables[0] = - KMKeyParameters.findTag(KMType.BOOL_TAG, KMType.EARLY_BOOT_ENDED, data[KEY_PARAMETERS]); + KMKeyParameters.findTag(KMType.BOOL_TAG, KMType.EARLY_BOOT_ONLY, data[KEY_PARAMETERS]); if (tmpVariables[0] != KMType.INVALID_VALUE) { KMException.throwIt(KMError.EARLY_BOOT_ENDED); } @@ -3611,7 +3611,7 @@ private void checkVersionAndPatchLevel(byte[] scratchPad) { if (tmpVariables[0] != KMType.INVALID_VALUE) { tmpVariables[1] = repository.getOsVersion(); tmpVariables[1] = - Util.arrayCompare( + KMUtils.unsignedByteArrayCompare( KMInteger.cast(tmpVariables[1]).getBuffer(), KMInteger.cast(tmpVariables[1]).getStartOff(), scratchPad, @@ -3644,7 +3644,7 @@ private void checkVersionAndPatchLevel(byte[] scratchPad) { if (tmpVariables[0] != KMType.INVALID_VALUE) { tmpVariables[1] = repository.getOsPatch(); tmpVariables[1] = - Util.arrayCompare( + KMUtils.unsignedByteArrayCompare( KMInteger.cast(tmpVariables[1]).getBuffer(), KMInteger.cast(tmpVariables[1]).getStartOff(), scratchPad, diff --git a/Applet/src/com/android/javacard/keymaster/KMType.java b/Applet/src/com/android/javacard/keymaster/KMType.java index bf6b12e5..4d81de45 100644 --- a/Applet/src/com/android/javacard/keymaster/KMType.java +++ b/Applet/src/com/android/javacard/keymaster/KMType.java @@ -231,7 +231,7 @@ public abstract class KMType { // Reset Since Id Rotation public static final short RESET_SINCE_ID_ROTATION = (short) 0x03EC; //Early boot ended. - public static final short EARLY_BOOT_ENDED = (short) 0x0131; + public static final short EARLY_BOOT_ONLY = (short) 0x0131; //Device unique attestation. public static final short DEVICE_UNIQUE_ATTESTATION = (short) 0x02D0; diff --git a/HAL/keymaster/4.1/JavacardKeymaster4Device.cpp b/HAL/keymaster/4.1/JavacardKeymaster4Device.cpp index f422bc26..3086e287 100644 --- a/HAL/keymaster/4.1/JavacardKeymaster4Device.cpp +++ b/HAL/keymaster/4.1/JavacardKeymaster4Device.cpp @@ -32,7 +32,6 @@ #include #include #include -#include #include #include #include @@ -91,7 +90,19 @@ enum class Instruction { 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_CERT_CHAIN_CMD = INS_END_KM_PROVISION_CMD+22, + 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, }; //Extended error codes @@ -368,18 +379,59 @@ uint16_t getStatus(std::vector& inputData) { return (inputData.at(inputData.size()-2) << 8) | (inputData.at(inputData.size()-1)); } +static bool isSEProvisioned() { + ErrorCode errorCode = ErrorCode::OK; + Instruction ins = Instruction::INS_GET_PROVISION_STATUS_CMD; + std::vector cborData; + std::vector response; + std::unique_ptr item; + CborConverter cborConverter; + std::vector apdu; + bool ret = false; + + errorCode = constructApduMessage(ins, cborData, apdu); + if(errorCode != ErrorCode::OK) return ret; + + if(!getTransportFactoryInstance()->sendData(apdu.data(), apdu.size(), response)) { + LOG(ERROR) << " Failed to send GET_PROVISION_STATUS_CMD "; + return ret; + } + + if((response.size() <= 2) || (getStatus(response) != APDU_RESP_STATUS_OK)) { + return ret; + } + //Check if SE is provisioned. + std::tie(item, errorCode) = cborConverter.decodeData(std::vector(response.begin(), response.end()-2), + true); + if(item != NULL) { + uint64_t status; + + if(!cborConverter.getUint64(item, 1, status)) { + LOG(ERROR) << "Failed to parse the status from cbor data"; + 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))) { + ret = true; + } + } + return ret; +} + + ErrorCode sendData(Instruction ins, std::vector& inData, std::vector& response, bool extendedOutput=false) { ErrorCode ret = ErrorCode::UNKNOWN_ERROR; std::vector apdu; - // TODO In real scenario the provision happens in the factory. In that case this - // below code is not required. This is just used for simulation. - if (ErrorCode::OK != (ret = provision(getTransportFactoryInstance()))) { - LOG(ERROR) << "Failed to provision the device"; + if (!isSEProvisioned()) { + LOG(ERROR) << "Javacard applet is not provisioned."; return ret; } - ret = constructApduMessage(ins, inData, apdu, extendedOutput); if(ret != ErrorCode::OK) return ret; diff --git a/HAL/keymaster/4.1/SocketTransport.cpp b/HAL/keymaster/4.1/SocketTransport.cpp index 51c28675..331a00b1 100644 --- a/HAL/keymaster/4.1/SocketTransport.cpp +++ b/HAL/keymaster/4.1/SocketTransport.cpp @@ -24,7 +24,7 @@ #define PORT 8080 #define IPADDR "10.9.40.24" //#define IPADDR "192.168.0.5" -#define MAX_RECV_BUFFER_SIZE 2048 +#define MAX_RECV_BUFFER_SIZE 2500 namespace se_transport { diff --git a/HAL/keymaster/Android.bp b/HAL/keymaster/Android.bp index d92809e8..1bcc6e2c 100644 --- a/HAL/keymaster/Android.bp +++ b/HAL/keymaster/Android.bp @@ -23,11 +23,8 @@ cc_binary { srcs: [ "4.1/service.cpp", "4.1/JavacardKeymaster4Device.cpp", - "4.1/CborConverter.cpp", "4.1/JavacardSoftKeymasterContext.cpp", "4.1/JavacardOperationContext.cpp", - "4.1/CommonUtils.cpp", - "4.1/Provision.cpp", ], local_include_dirs: [ "include", @@ -47,6 +44,7 @@ cc_binary { "android.hardware.keymaster@4.1", "android.hardware.keymaster@4.0", "libjc_transport", + "libjc_common", "libcrypto", ], product_variables: { @@ -110,3 +108,34 @@ cc_library { "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/Provision.h b/HAL/keymaster/include/Provision.h deleted file mode 100644 index a58c17c6..00000000 --- a/HAL/keymaster/include/Provision.h +++ /dev/null @@ -1,36 +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 { - -/** - * Provisions the SE. - */ -ErrorCode provision(std::unique_ptr& transport); - -} // namespace javacard -} // namespace V4_1 -} // namespace keymaster -#endif //KEYMASTER_V4_1_JAVACARD_PROVISION_H_ diff --git a/provisioning_tool/Android.bp b/provisioning_tool/Android.bp new file mode 100644 index 00000000..579d2ff4 --- /dev/null +++ b/provisioning_tool/Android.bp @@ -0,0 +1,68 @@ +// 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: [ + "provision_tool.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/HAL/keymaster/4.1/Provision.cpp b/provisioning_tool/Provision.cpp similarity index 57% rename from HAL/keymaster/4.1/Provision.cpp rename to provisioning_tool/Provision.cpp index 24f65667..85bdd241 100644 --- a/HAL/keymaster/4.1/Provision.cpp +++ b/provisioning_tool/Provision.cpp @@ -28,10 +28,8 @@ #include #include #include + #include -#define ROOT_EC_KEY "/data/data/ec_key.der" -#define INTERMEDIATE_EC_CERT "/data/data/ec_cert.der" -#define ROOT_EC_CERT "/data/data/ec_root_cert.der" #define INS_BEGIN_KM_CMD 0x00 #define APDU_CLS 0x80 #define APDU_P1 0x40 @@ -42,7 +40,6 @@ namespace keymaster { namespace V4_1 { namespace javacard { -constexpr uint8_t kFakeKeyAgreementKey[32] = {}; enum class Instruction { // Provisioning commands INS_PROVISION_ATTESTATION_KEY_CMD = INS_BEGIN_KM_CMD+1, @@ -67,32 +64,15 @@ enum ProvisionStatus { }; // Static function declarations. -static bool readDataFromFile(const char *filename, std::vector& data); static ErrorCode constructApduMessage(Instruction& ins, std::vector& inputData, std::vector& apduOut, bool extendedOutput=false); static ErrorCode sendProvisionData(std::unique_ptr& transport, Instruction ins, std::vector& inData, std::vector& response, bool extendedOutput = false); -static ErrorCode provisionAttestationKey(std::unique_ptr& transport); -static ErrorCode provisionAttestationCertificateChain(std::unique_ptr& transport); -static ErrorCode provisionAttestationCertificateParams(std::unique_ptr& transport); -static ErrorCode provisionAttestationIDs(std::unique_ptr& transport); -static ErrorCode provisionSharedSecret(std::unique_ptr& transport); -static ErrorCode getProvisionStatus(std::unique_ptr& transport, std::vector& -response); -static ErrorCode lockProvision(std::unique_ptr& transport); -static ErrorCode setBootParameters(std::unique_ptr& transport); static uint16_t getStatus(std::vector& inputData); -static bool isSEProvisioned(uint64_t status); -static inline X509* parseDerCertificate(const char* filename) { +static inline X509* parseDerCertificate(std::vector& certData) { X509 *x509 = NULL; - std::vector certData; - /* Read the Root certificate */ - if(!readDataFromFile(filename, certData)) { - LOG(ERROR) << " Failed to read the Root certificate"; - return NULL; - } /* Create BIO instance from certificate data */ BIO *bio = BIO_new_mem_buf(certData.data(), certData.size()); if(bio == NULL) { @@ -145,30 +125,6 @@ static uint16_t getStatus(std::vector& inputData) { return (inputData.at(inputData.size()-2) << 8) | (inputData.at(inputData.size()-1)); } -static bool readDataFromFile(const char *filename, std::vector& data) { - FILE *fp; - bool ret = true; - fp = fopen(filename, "rb"); - if(fp == NULL) { - LOG(ERROR) << "Failed to open file: " << filename; - return false; - } - fseek(fp, 0L, SEEK_END); - long int filesize = ftell(fp); - rewind(fp); - std::unique_ptr buf(new uint8_t[filesize]); - if( 0 == fread(buf.get(), filesize, 1, fp)) { - LOG(ERROR) << "No Content in the file: " << filename; - ret = false; - } - if(true == ret) { - //data.insert(data.begin(), buf.get(), buf.get() + filesize); - data.insert(data.end(), buf.get(), buf.get() + filesize); - } - fclose(fp); - return ret; -} - static ErrorCode constructApduMessage(Instruction& ins, std::vector& inputData, std::vector& apduOut, bool extendedOutput) { apduOut.push_back(static_cast(APDU_CLS)); //CLS @@ -235,77 +191,72 @@ extendedOutput) { return ret; } -static ErrorCode provisionAttestationKey(std::unique_ptr& transport) { +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; - CborConverter cborConverter; - cppbor::Array array; - cppbor::Array subArray; - std::vector data; std::vector privKey; std::vector pubKey; - Instruction ins = Instruction::INS_PROVISION_ATTESTATION_KEY_CMD; EcCurve curve; + 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 */ - // Read the ECKey from the file. hidl_vec keyParams = kmParamSet2Hidl(authSetKeyParams); - - if(!readDataFromFile(ROOT_EC_KEY, data)) { - LOG(ERROR) << " Failed to read the Root rsa key"; - return ErrorCode::UNKNOWN_ERROR; - } - if(ErrorCode::OK != (errorCode = ecRawKeyFromPKCS8(data, privKey, pubKey, curve))) { + 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(transport, ins, cborData, response))) { + if(ErrorCode::OK != (errorCode = sendProvisionData(pTransportFactory, ins, cborData, response))) { return errorCode; } return errorCode; } -static ErrorCode provisionAttestationCertificateChain(std::unique_ptr& transport) { +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; - /* Read the Root certificate */ - if(!readDataFromFile(INTERMEDIATE_EC_CERT, certData)) { - LOG(ERROR) << " Failed to read the Root certificate"; - return (ErrorCode::UNKNOWN_ERROR); - } - if(!readDataFromFile(ROOT_EC_CERT, certData)) { - LOG(ERROR) << " Failed to read the Root certificate"; - return (ErrorCode::UNKNOWN_ERROR); + for (auto data : certChain) { + certData.insert(certData.end(), data.begin(), data.end()); } - cppbor::Bstr certChain(certData.begin(), certData.end()); - std::vector cborData = certChain.encode(); + cppbor::Bstr bstrCertChain(certData.begin(), certData.end()); + std::vector cborData = bstrCertChain.encode(); - if(ErrorCode::OK != (errorCode = sendProvisionData(transport, ins, cborData, response))) { + if(ErrorCode::OK != (errorCode = sendProvisionData(pTransportFactory, ins, cborData, response))) { return errorCode; } return errorCode; } -static ErrorCode provisionAttestationCertificateParams(std::unique_ptr& transport) { +ErrorCode Provision::provisionAttestationCertificateParams(std::vector& batchCertificate) { ErrorCode errorCode = ErrorCode::OK; cppbor::Array array; Instruction ins = Instruction::INS_PROVISION_CERT_PARAMS_CMD; @@ -316,7 +267,7 @@ static ErrorCode provisionAttestationCertificateParams(std::unique_ptr cborData = array.encode(); - if(ErrorCode::OK != (errorCode = sendProvisionData(transport, ins, cborData, response))) { + if(ErrorCode::OK != (errorCode = sendProvisionData(pTransportFactory, ins, cborData, response))) { return errorCode; } return errorCode; } - -static ErrorCode provisionAttestationIDs(std::unique_ptr& transport) { +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; - std::string brand("Google"); - std::string device("Pixel 3A"); - std::string product("Pixel"); - std::string serial("UGYJFDjFeRuBEH"); - std::string imei("987080543071019"); - std::string meid("27863510227963"); - std::string manufacturer("Foxconn"); - std::string model("HD1121"); - AuthorizationSet authSetAttestParams(AuthorizationSetBuilder() - .Authorization(TAG_ATTESTATION_ID_BRAND, brand.data(), brand.size()) - .Authorization(TAG_ATTESTATION_ID_DEVICE, device.data(), device.size()) - .Authorization(TAG_ATTESTATION_ID_PRODUCT, product.data(), product.size()) - .Authorization(TAG_ATTESTATION_ID_SERIAL, serial.data(), serial.size()) - .Authorization(TAG_ATTESTATION_ID_IMEI, imei.data(), imei.size()) - .Authorization(TAG_ATTESTATION_ID_MEID, meid.data(), meid.size()) - .Authorization(TAG_ATTESTATION_ID_MANUFACTURER, manufacturer.data(), manufacturer.size()) - .Authorization(TAG_ATTESTATION_ID_MODEL, model.data(), model.size())); + .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 attestParams = kmParamSet2Hidl(authSetAttestParams); + hidl_vec attestKeyParams = kmParamSet2Hidl(authSetAttestParams); array = cppbor::Array(); - cborConverter.addKeyparameters(array, attestParams); + cborConverter.addKeyparameters(array, attestKeyParams); std::vector cborData = array.encode(); - if(ErrorCode::OK != (errorCode = sendProvisionData(transport, ins, cborData, response))) { + if(ErrorCode::OK != (errorCode = sendProvisionData(pTransportFactory, ins, cborData, response))) { return errorCode; } return errorCode; } -static ErrorCode provisionSharedSecret(std::unique_ptr& transport) { +ErrorCode Provision::provisionPreSharedSecret(std::vector& preSharedSecret) { ErrorCode errorCode = ErrorCode::OK; cppbor::Array array; Instruction ins = Instruction::INS_PROVISION_PRESHARED_SECRET_CMD; std::vector response; - std::vector masterKey(kFakeKeyAgreementKey, kFakeKeyAgreementKey + - sizeof(kFakeKeyAgreementKey)/sizeof(kFakeKeyAgreementKey[0])); array = cppbor::Array(); - array.add(masterKey); + array.add(preSharedSecret); std::vector cborData = array.encode(); - if(ErrorCode::OK != (errorCode = sendProvisionData(transport, ins, cborData, response))) { + if(ErrorCode::OK != (errorCode = sendProvisionData(pTransportFactory, ins, cborData, response))) { return errorCode; } return errorCode; } -static ErrorCode getProvisionStatus(std::unique_ptr& transport, std::vector& -response) { +ErrorCode Provision::provisionBootParameters(BootParams& bootParams) { ErrorCode errorCode = ErrorCode::OK; - Instruction ins = Instruction::INS_GET_PROVISION_STATUS_CMD; - std::vector cborData; - - if(ErrorCode::OK != (errorCode = sendProvisionData(transport, ins, cborData, response))) { - return errorCode; - } - return errorCode; - -} - -static ErrorCode lockProvision(std::unique_ptr& transport) { - ErrorCode errorCode = ErrorCode::OK; - Instruction ins = Instruction::INS_LOCK_PROVISIONING_CMD; - std::vector cborData; - std::vector response; - - if(ErrorCode::OK != (errorCode = sendProvisionData(transport, ins, cborData, response))) { - return errorCode; - } - return errorCode; -} - -static ErrorCode setBootParameters(std::unique_ptr& transport) { - ErrorCode errorCode = ErrorCode::OK; - std::vector verifiedBootKey(32, 0); - std::vector verifiedBootKeyHash(32, 0); - uint32_t vendorPatchLevel = 0; - uint32_t bootPatchLevel = 0; cppbor::Array array; std::vector apdu; std::vector response; Instruction ins = Instruction::INS_SET_BOOT_PARAMS_CMD; - keymaster_verified_boot_t kmVerifiedBoot = KM_VERIFIED_BOOT_UNVERIFIED; array.add(GetOsVersion()). add(GetOsPatchlevel()). - add(vendorPatchLevel). - add(bootPatchLevel). + add(bootParams.vendorPatchLevel). + add(bootParams.bootPatchLevel). /* Verified Boot Key */ - add(verifiedBootKey). + add(bootParams.verifiedBootKey). /* Verified Boot Hash */ - add(verifiedBootKeyHash). + add(bootParams.verifiedBootKeyHash). /* boot state */ - add(static_cast(kmVerifiedBoot)). + add(bootParams.verifiedBootState). /* device locked */ - add(0); + add(bootParams.deviceLocked); std::vector cborData = array.encode(); - if(ErrorCode::OK != (errorCode = sendProvisionData(transport, ins, cborData, response))) { + if(ErrorCode::OK != (errorCode = sendProvisionData(pTransportFactory, ins, cborData, response))) { return errorCode; } return errorCode; } -static bool isSEProvisioned(uint64_t status) { - bool ret = false; - 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))) { - ret = true; - } - return ret; -} - -ErrorCode provision(std::unique_ptr& transport) { +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; - ErrorCode errorCode = ErrorCode::OK; - //Get Provision status. - if(ErrorCode::OK != (errorCode = getProvisionStatus(transport, response))) { + 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) = cborConverter.decodeData(std::vector(response.begin(), response.end()-2), true); if(item != NULL) { - uint64_t status; - if(!cborConverter.getUint64(item, 1, status)) + if(!cborConverter.getUint64(item, 1, status)) { + LOG(ERROR) << "Failed to parse the status from cbor data"; return ErrorCode::UNKNOWN_ERROR; - - if(isSEProvisioned(status)) { - return ErrorCode::OK; //SE is Provisioned. } - - } else { - return ErrorCode::UNKNOWN_ERROR; } + return errorCode; +} - //SE not provisioned so Provision the SE. +ErrorCode Provision::lockProvision() { + ErrorCode errorCode = ErrorCode::OK; + Instruction ins = Instruction::INS_LOCK_PROVISIONING_CMD; + std::vector cborData; + std::vector response; - //Provision Attestation Key. - if(ErrorCode::OK != (errorCode = provisionAttestationKey(transport))) { - return errorCode; - } - //Provision Attestation certificate chain. - if(ErrorCode::OK != (errorCode = provisionAttestationCertificateChain(transport))) { - return errorCode; - } - //Provision certificate parameters. - if(ErrorCode::OK != (errorCode = provisionAttestationCertificateParams(transport))) { - return errorCode; - } - //Provision Attestation IDs. - if(ErrorCode::OK != (errorCode = provisionAttestationIDs(transport))) { + if(ErrorCode::OK != (errorCode = sendProvisionData(pTransportFactory, ins, cborData, response))) { return errorCode; } - //Provision Shared secret. - if(ErrorCode::OK != (errorCode = provisionSharedSecret(transport))) { - return errorCode; - } - //Set Boot parameters. - if(ErrorCode::OK != (errorCode = setBootParameters(transport))) { - return errorCode; - } - //Lock the provisioning. - if(ErrorCode::OK != (errorCode = lockProvision(transport))) { - return errorCode; - } - //return OK return errorCode; } +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/provisioning_tool/Provision.h b/provisioning_tool/Provision.h new file mode 100644 index 00000000..e541c496 --- /dev/null +++ b/provisioning_tool/Provision.h @@ -0,0 +1,100 @@ +/* + ** + ** 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 BootParams_ { + uint32_t osVersion; + uint32_t osPatchLevel; + uint32_t vendorPatchLevel; + 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 ); + /** + * 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/provisioning_tool/README.md b/provisioning_tool/README.md new file mode 100644 index 00000000..9f0dfbb4 --- /dev/null +++ b/provisioning_tool/README.md @@ -0,0 +1,27 @@ +# 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. A sample +json file is located in this directory with name [sample_json.txt](https://github.com/BKSSMVenkateswarlu/JavaCardKeymaster/blob/master/provisioning_tool/sample_json.txt) for +your reference. + +#### Build +This tool can be built along with aosp build. It has dependency on +[libjc_common](https://github.com/BKSSMVenkateswarlu/JavaCardKeymaster/blob/master/HAL/keymaster/Android.bp) and +libjc_provision. + +#### Usage +
+Usage: provision_tool 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.
+
diff --git a/provisioning_tool/provision_tool.cpp b/provisioning_tool/provision_tool.cpp new file mode 100644 index 00000000..0f240b3c --- /dev/null +++ b/provisioning_tool/provision_tool.cpp @@ -0,0 +1,606 @@ +/* + ** + ** 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 Provion shared secret \n"); + printf("-b, --set_boot_params jsonFile \t Provion boot parameters \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 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, "os_version", &bootParams.osVersion)) { + printf("\n Invalid value for os_version or os_version tag missing\n"); + return ret; + } + if(!getBootParameterIntValue(bootParamsObj, "os_patch_level", &bootParams.osPatchLevel)) { + printf("\n Invalid value for os_patch_level or os_patch_level tag missing\n"); + return ret; + } + if(!getBootParameterIntValue(bootParamsObj, "vendor_patch_level", &bootParams.vendorPatchLevel)) { + printf("\n Invalid value for vendor_patch_level or vendor_patch_level tag missing\n"); + return ret; + } + 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; + } + 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'}, + {"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, ":slha: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 '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/provisioning_tool/sample_json.txt b/provisioning_tool/sample_json.txt new file mode 100644 index 00000000..fbfd71de --- /dev/null +++ b/provisioning_tool/sample_json.txt @@ -0,0 +1,29 @@ +{ + "attest_ids": { + "brand": "Google", + "device": "Pixel 3A", + "product": "Pixel", + "serial": "UGYJFDjFeRuBEH", + "imei": "987080543071019", + "meid": "27863510227963", + "manufacturer": "Foxconn", + "model": "HD1121" + }, + "shared_secret": "0000000000000000000000000000000000000000000000000000000000000000", + "set_boot_params": { + "os_version": 100, + "os_patch_level": 100, + "vendor_patch_level": 0, + "boot_patch_level": 0, + "verified_boot_key": "0000000000000000000000000000000000000000000000000000000000000000", + "verified_boot_key_hash": "0000000000000000000000000000000000000000000000000000000000000000", + "boot_state": 2, + "device_locked": 0 + }, + "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" + ] +}