From cc5c9130bc0283182f0e6f39df1a1b623b8ae6da Mon Sep 17 00:00:00 2001 From: "avinash.hedage" Date: Wed, 24 Nov 2021 18:42:13 +0000 Subject: [PATCH 1/3] Auth data optimization and PKCS8 interface changes --- .../keymaster/KMPKCS8DecoderImpl.java | 208 +++++++++++++++ .../javacard/keymaster/KMKeymasterApplet.java | 95 ++++--- .../javacard/keymaster/KMPKCS8Decoder.java | 239 +++--------------- 3 files changed, 301 insertions(+), 241 deletions(-) create mode 100644 Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMPKCS8DecoderImpl.java diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMPKCS8DecoderImpl.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMPKCS8DecoderImpl.java new file mode 100644 index 00000000..1d2812a5 --- /dev/null +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMPKCS8DecoderImpl.java @@ -0,0 +1,208 @@ +package com.android.javacard.keymaster; + +import javacard.framework.Util; + +public class KMPKCS8DecoderImpl implements 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 KMPKCS8DecoderImpl inst; + private KMPKCS8DecoderImpl(){ + start = 0; + length = 0; + cur = 0; + } + + @Override + public short decodeRsa(short blob){ + init(blob); + decodeCommon((short)0, RSA_ALGORITHM); + return decodeRsaPrivateKey((short)0); + } + + @Override + public short decodeEc(short blob){ + init(blob); + decodeCommon((short)0, EC_ALGORITHM); + return decodeEcPrivateKey((short)1); + } + + @Override + 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); + 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 = getModulus(len); + 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; + } + + // 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); + 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 short getModulus(short modulusLen) { + if(0 == data[cur] && modulusLen == 257) { + incrementCursor((short) 1); + modulusLen--; + } + short blob = KMByteBlob.instance(modulusLen); + getBytes(blob); + return blob; + } + + 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 KMPKCS8DecoderImpl instance() { + if (inst == null) { + inst = new KMPKCS8DecoderImpl(); + } + 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/KMKeymasterApplet.java b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java index b2df2690..5b209a23 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java @@ -40,7 +40,7 @@ public class KMKeymasterApplet extends Applet implements AppletEvent, ExtendedLe 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; - private static final short MAX_AUTH_DATA_SIZE = (short) 512; + private static final short MAX_AUTH_DATA_SIZE = (short) 256; private static final short DERIVE_KEY_INPUT_SIZE = (short) 256; private static final short POWER_RESET_MASK_FLAG = (short) 0x4000; @@ -3081,7 +3081,7 @@ private void decodeRawECKey() { private void decodePKCS8ECKeys() { // Decode key material - KMPKCS8Decoder pkcs8 = KMPKCS8Decoder.instance(); + KMPKCS8DecoderImpl pkcs8 = KMPKCS8DecoderImpl.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); @@ -3287,7 +3287,7 @@ private void importAESKey(byte[] scratchPad) { private void importRSAKey(byte[] scratchPad) { // Decode key material - KMPKCS8Decoder pkcs8 = KMPKCS8Decoder.instance(); + KMPKCS8DecoderImpl pkcs8 = KMPKCS8DecoderImpl.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); @@ -3989,35 +3989,44 @@ private static void encryptSecret(byte[] scratchPad) { } private static void makeAuthData(byte[] scratchPad) { - tmpVariables[0] = - addPtrToAAD(KMKeyParameters.cast(data[HW_PARAMETERS]).getVals(), scratchPad, (short) 0); - tmpVariables[0] += - addPtrToAAD( - KMKeyParameters.cast(data[SW_PARAMETERS]).getVals(), scratchPad, tmpVariables[0]); - tmpVariables[0] += - addPtrToAAD( - KMKeyParameters.cast(data[HIDDEN_PARAMETERS]).getVals(), scratchPad, tmpVariables[0]); - - if (KMArray.cast(data[KEY_BLOB]).length() == 5) { - tmpVariables[1] = KMArray.instance((short) (tmpVariables[0] + 1)); - } else { - tmpVariables[1] = KMArray.instance(tmpVariables[0]); - } - // convert scratch pad to KMArray - short index = 0; - short objPtr; - while (index < tmpVariables[0]) { - objPtr = Util.getShort(scratchPad, (short) (index * 2)); - KMArray.cast(tmpVariables[1]).add(index, objPtr); - index++; - } - if (KMArray.cast(data[KEY_BLOB]).length() == 5) { - KMArray.cast(tmpVariables[1]).add(index, data[PUB_KEY]); - } - - data[AUTH_DATA] = repository.alloc(MAX_AUTH_DATA_SIZE); - short len = encoder.encode(tmpVariables[1], repository.getHeap(), data[AUTH_DATA]); - data[AUTH_DATA_LENGTH] = len; + + final byte[] oneBuffer = { (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF }; + + short arrayLen = 3; + if (KMArray.cast(data[KEY_BLOB]).length() == 5) { + arrayLen = 4; + } + short params = KMArray.instance((short) arrayLen); + KMArray.cast(params).add((short) 0, KMKeyParameters.cast(data[HW_PARAMETERS]).getVals()); + KMArray.cast(params).add((short) 1, KMKeyParameters.cast(data[SW_PARAMETERS]).getVals()); + KMArray.cast(params).add((short) 2, KMKeyParameters.cast(data[HIDDEN_PARAMETERS]).getVals()); + if (4 == arrayLen) { + KMArray.cast(params).add((short) 3, data[PUB_KEY]); + } + + short authIndex = repository.alloc(MAX_AUTH_DATA_SIZE); + short index = 0; + short len = 0; + short paramsLen = KMArray.cast(params).length(); + Util.arrayFillNonAtomic(repository.getHeap(), authIndex, (short) MAX_AUTH_DATA_SIZE, (byte) 0); + while (index < paramsLen) { + short tag = KMArray.cast(params).get(index); + len = encoder.encode(tag, repository.getHeap(), (short) (authIndex + 32)); + Util.arrayCopyNonAtomic(repository.getHeap(), (short) authIndex, repository.getHeap(), + (short) (authIndex + len + 32), (short) 32); + len = seProvider.hmacSign(oneBuffer, (short) 0, (short) oneBuffer.length, repository.getHeap(), + (short) (authIndex + 32), (short) (len + 32), repository.getHeap(), (short) authIndex); + if (len != 32) { + KMException.throwIt(KMError.UNKNOWN_ERROR); + } + index++; + } + + data[AUTH_DATA] = authIndex; + data[AUTH_DATA_LENGTH] = len; } private static short addPtrToAAD(short dataArrPtr, byte[] aadBuf, short offset) { @@ -4035,17 +4044,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[0] = KMKeyParameters.cast(data[HW_PARAMETERS]).getVals(); tmpVariables[1] = 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]) { - // 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 (DERIVE_KEY_INPUT_SIZE > tmpVariables[2]) { + // Copy KeyCharacteristics in the remaining space of DERIVE_KEY_INPUT_SIZE + if ((short) (tmpVariables[2] + data[AUTH_DATA_LENGTH]) < DERIVE_KEY_INPUT_SIZE) { + tmpVariables[2] = (short) (tmpVariables[2] + data[AUTH_DATA_LENGTH]); + Util.arrayCopyNonAtomic(repository.getHeap(), data[AUTH_DATA], repository.getHeap(), + (short) (tmpVariables[1] + tmpVariables[2]), data[AUTH_DATA_LENGTH]); + } + } else { + tmpVariables[2] = DERIVE_KEY_INPUT_SIZE; + } // KeyDerivation: // 1. Do HMAC Sign, with below input parameters. // Key - 128 bit master key @@ -4058,7 +4071,7 @@ private static short deriveKey(byte[] scratchPad) { seProvider.getMasterKey(), repository.getHeap(), tmpVariables[1], - DERIVE_KEY_INPUT_SIZE, + tmpVariables[2], scratchPad, (short) 0); if (tmpVariables[3] < 16) { diff --git a/Applet/src/com/android/javacard/keymaster/KMPKCS8Decoder.java b/Applet/src/com/android/javacard/keymaster/KMPKCS8Decoder.java index 5e67eb83..d22cbed7 100644 --- a/Applet/src/com/android/javacard/keymaster/KMPKCS8Decoder.java +++ b/Applet/src/com/android/javacard/keymaster/KMPKCS8Decoder.java @@ -1,205 +1,44 @@ +/* + * 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.Util; +public interface KMPKCS8Decoder { -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); - 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 = getModulus(len); - 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; - } - - // 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); - 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; - } + /** + * Decode the PKCS 7 encoded RSA KeyBlob + * + * @param Keyblob. + * @return Instance of KMArray holding RSA public key, RSA private key and modulus. + */ + short decodeRsa(short blob); - private short getModulus(short modulusLen) { - if(0 == data[cur] && modulusLen == 257) { - incrementCursor((short) 1); - modulusLen--; - } - short blob = KMByteBlob.instance(modulusLen); - getBytes(blob); - return blob; - } - - 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); - } + /** + * Decode the PKCS 7 encoded EC KeyBlob + * + * @param Keyblob. + * @return Instance of KMArray holding EC public key and EC private key. + */ + short decodeEc(short blob); + + /** + * Decode the PKCS 7 encoded EC Public key info + * + * @param Keyblob. + * @return EC public key. + */ + short decodeEcSubjectPublicKeyInfo(short blob); + } From 4172a94a2cbe2727cdd2366edbee0ec4eea4f4fb Mon Sep 17 00:00:00 2001 From: "avinash.hedage" Date: Thu, 25 Nov 2021 17:01:56 +0000 Subject: [PATCH 2/3] Auth data and derive key changes --- .../keymaster/KMAndroidSEProvider.java | 18 ++++++ .../keymaster/KMPKCS8DecoderImpl.java | 23 ------- .../javacard/keymaster/KMKeyParameters.java | 1 - .../javacard/keymaster/KMKeymasterApplet.java | 61 ++++--------------- .../javacard/keymaster/KMPKCS8Decoder.java | 16 ++--- .../javacard/keymaster/KMSEProvider.java | 7 ++- 6 files changed, 40 insertions(+), 86 deletions(-) diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEProvider.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEProvider.java index 985d87fb..10552081 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEProvider.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEProvider.java @@ -1350,4 +1350,22 @@ public void releaseAllOperations() { public KMComputedHmacKey getComputedHmacKey() { return computedHmacKey; } + + @Override + public short messageDigest256(byte[] inBuff, short inOffset, + short inLength, byte[] outBuff, short outOffset) { + MessageDigest.OneShot mDigest = null; + short len = 0; + try { + mDigest = MessageDigest.OneShot.open(MessageDigest.ALG_SHA_256); + len = mDigest.doFinal(inBuff, inOffset, inLength, outBuff, outOffset); + } finally { + if (mDigest != null) { + mDigest.close(); + mDigest = null; + } + } + return len; + } + } diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMPKCS8DecoderImpl.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMPKCS8DecoderImpl.java index 1d2812a5..8585587f 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMPKCS8DecoderImpl.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMPKCS8DecoderImpl.java @@ -47,29 +47,6 @@ public short decodeEc(short blob){ return decodeEcPrivateKey((short)1); } - @Override - 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); - 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); diff --git a/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java b/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java index 3592e32b..04accde6 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java @@ -114,7 +114,6 @@ public static boolean hasUnsupportedTags(short keyParamsPtr) { final short[] tagArr = { // Unsupported tags. KMType.BOOL_TAG, KMType.TRUSTED_USER_PRESENCE_REQUIRED, - KMType.BOOL_TAG, KMType.ALLOW_WHILE_ON_BODY, KMType.UINT_TAG, KMType.MIN_SEC_BETWEEN_OPS }; byte index = 0; diff --git a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java index 5b209a23..043cadc8 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java @@ -3233,6 +3233,7 @@ private void importTDESKey(byte[] scratchPad) { // update the key parameters list updateKeyParameters(scratchPad, tmpVariables[4]); // Read Minimum Mac length - it must not be present + //Added this error check based on default reference implementation. tmpVariables[0] = KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, data[KEY_PARAMETERS]); if (tmpVariables[0] != KMType.INVALID_VALUE) { @@ -3990,11 +3991,6 @@ private static void encryptSecret(byte[] scratchPad) { private static void makeAuthData(byte[] scratchPad) { - final byte[] oneBuffer = { (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, - (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, - (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, - (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF }; - short arrayLen = 3; if (KMArray.cast(data[KEY_BLOB]).length() == 5) { arrayLen = 4; @@ -4017,7 +4013,7 @@ private static void makeAuthData(byte[] scratchPad) { len = encoder.encode(tag, repository.getHeap(), (short) (authIndex + 32)); Util.arrayCopyNonAtomic(repository.getHeap(), (short) authIndex, repository.getHeap(), (short) (authIndex + len + 32), (short) 32); - len = seProvider.hmacSign(oneBuffer, (short) 0, (short) oneBuffer.length, repository.getHeap(), + len = seProvider.messageDigest256(repository.getHeap(), (short) (authIndex + 32), (short) (len + 32), repository.getHeap(), (short) authIndex); if (len != 32) { KMException.throwIt(KMError.UNKNOWN_ERROR); @@ -4029,60 +4025,29 @@ private static void makeAuthData(byte[] scratchPad) { data[AUTH_DATA_LENGTH] = len; } - private static short addPtrToAAD(short dataArrPtr, byte[] aadBuf, short offset) { - short index = (short) (offset * 2); - short tagInd = 0; - short tagPtr; - short arrLen = KMArray.cast(dataArrPtr).length(); - while (tagInd < arrLen) { - tagPtr = KMArray.cast(dataArrPtr).get(tagInd); - Util.setShort(aadBuf, index, tagPtr); - index += 2; - tagInd++; - } - return tagInd; - } - private static short deriveKey(byte[] scratchPad) { - - tmpVariables[0] = KMKeyParameters.cast(data[HW_PARAMETERS]).getVals(); - tmpVariables[1] = 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]) { - // Copy KeyCharacteristics in the remaining space of DERIVE_KEY_INPUT_SIZE - if ((short) (tmpVariables[2] + data[AUTH_DATA_LENGTH]) < DERIVE_KEY_INPUT_SIZE) { - tmpVariables[2] = (short) (tmpVariables[2] + data[AUTH_DATA_LENGTH]); - Util.arrayCopyNonAtomic(repository.getHeap(), data[AUTH_DATA], repository.getHeap(), - (short) (tmpVariables[1] + tmpVariables[2]), data[AUTH_DATA_LENGTH]); - } - } else { - tmpVariables[2] = DERIVE_KEY_INPUT_SIZE; - } + // KeyDerivation: - // 1. Do HMAC Sign, with below input parameters. - // Key - 128 bit master key - // Input data - HIDDEN_PARAMETERS + KeyCharacateristics - // - Truncate beyond 256 bytes. + // 1. Do HMAC Sign, Auth data. // 2. HMAC Sign generates an output of 32 bytes length. - // Consume only first 16 bytes as derived key. + // Consume only first 16 bytes as derived key. // Hmac sign. - tmpVariables[3] = seProvider.hmacKDF( + short len = seProvider.hmacKDF( seProvider.getMasterKey(), repository.getHeap(), - tmpVariables[1], - tmpVariables[2], + data[AUTH_DATA], + data[AUTH_DATA_LENGTH], scratchPad, (short) 0); - if (tmpVariables[3] < 16) { + if (len < 16) { KMException.throwIt(KMError.UNKNOWN_ERROR); } - tmpVariables[3] = 16; + len = 16; + data[DERIVED_KEY] = repository.alloc(len); // 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]; + scratchPad, (short) 0, repository.getHeap(), data[DERIVED_KEY], len); + return len; } // This function masks the error code with POWER_RESET_MASK_FLAG diff --git a/Applet/src/com/android/javacard/keymaster/KMPKCS8Decoder.java b/Applet/src/com/android/javacard/keymaster/KMPKCS8Decoder.java index d22cbed7..d8d472e2 100644 --- a/Applet/src/com/android/javacard/keymaster/KMPKCS8Decoder.java +++ b/Applet/src/com/android/javacard/keymaster/KMPKCS8Decoder.java @@ -18,27 +18,19 @@ public interface KMPKCS8Decoder { /** - * Decode the PKCS 7 encoded RSA KeyBlob + * Decodes the PKCS8 encoded RSA Key and extracts the private and public key * - * @param Keyblob. + * @param Instance of the PKCS8 encoded data * @return Instance of KMArray holding RSA public key, RSA private key and modulus. */ short decodeRsa(short blob); /** - * Decode the PKCS 7 encoded EC KeyBlob + * Decodes the PKCS8 encoded EC Key and extracts the private and public key * - * @param Keyblob. + * @param Instance of the PKCS8 encoded data. * @return Instance of KMArray holding EC public key and EC private key. */ short decodeEc(short blob); - - /** - * Decode the PKCS 7 encoded EC Public key info - * - * @param Keyblob. - * @return EC public key. - */ - short decodeEcSubjectPublicKeyInfo(short blob); } diff --git a/Applet/src/com/android/javacard/keymaster/KMSEProvider.java b/Applet/src/com/android/javacard/keymaster/KMSEProvider.java index da57e3ee..332f0cc4 100644 --- a/Applet/src/com/android/javacard/keymaster/KMSEProvider.java +++ b/Applet/src/com/android/javacard/keymaster/KMSEProvider.java @@ -15,8 +15,6 @@ */ package com.android.javacard.keymaster; -import org.globalplatform.upgrade.Element; - /** * KMSEProvider is facade to use SE specific methods. The main intention of this interface is to * abstract the cipher, signature and backup and restore related functions. The instance of this @@ -590,5 +588,10 @@ void persistProvisionData(byte[] buf, short certChainOff, short certChainLen, * Releases all the instance back to pool. Generally this is used when card is reset. */ void releaseAllOperations(); + + /** + * Digest message + */ + short messageDigest256(byte[] inBuff, short inOffset, short inLength, byte[] outBuff, short outOffset); } From 45e92c23e1c09279122915214dcf7303ff0729e8 Mon Sep 17 00:00:00 2001 From: "avinash.hedage" Date: Fri, 26 Nov 2021 16:09:45 +0000 Subject: [PATCH 3/3] added pkcs8 decoder in jcard --- .../javacard/keymaster/KMJCardSimulator.java | 16 ++ .../keymaster/KMPKCS8DecoderImpl.java | 185 ++++++++++++++++++ 2 files changed, 201 insertions(+) create mode 100644 Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMPKCS8DecoderImpl.java diff --git a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimulator.java b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimulator.java index e0d2b546..4b4ec852 100644 --- a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimulator.java +++ b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimulator.java @@ -40,6 +40,7 @@ import javacard.security.Key; import javacard.security.KeyBuilder; import javacard.security.KeyPair; +import javacard.security.MessageDigest; import javacard.security.RSAPrivateKey; import javacard.security.RSAPublicKey; import javacard.security.RandomData; @@ -1383,4 +1384,19 @@ public KMComputedHmacKey getComputedHmacKey() { public void releaseAllOperations() { //Do nothing. } + +@Override +public short messageDigest256(byte[] inBuff, short inOffset, + short inLength, byte[] outBuff, short outOffset) { + MessageDigest mDigest = null; + short len = 0; + try { + mDigest = MessageDigest.getInitializedMessageDigestInstance(MessageDigest.ALG_SHA_256, false); + len = mDigest.doFinal(inBuff, inOffset, inLength, outBuff, outOffset); + } catch (Exception e) { + + } + return len; +} + } diff --git a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMPKCS8DecoderImpl.java b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMPKCS8DecoderImpl.java new file mode 100644 index 00000000..8585587f --- /dev/null +++ b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMPKCS8DecoderImpl.java @@ -0,0 +1,185 @@ +package com.android.javacard.keymaster; + +import javacard.framework.Util; + +public class KMPKCS8DecoderImpl implements 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 KMPKCS8DecoderImpl inst; + private KMPKCS8DecoderImpl(){ + start = 0; + length = 0; + cur = 0; + } + + @Override + public short decodeRsa(short blob){ + init(blob); + decodeCommon((short)0, RSA_ALGORITHM); + return decodeRsaPrivateKey((short)0); + } + + @Override + public short decodeEc(short blob){ + init(blob); + decodeCommon((short)0, EC_ALGORITHM); + return decodeEcPrivateKey((short)1); + } + + //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 = getModulus(len); + 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; + } + + // 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); + 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 short getModulus(short modulusLen) { + if(0 == data[cur] && modulusLen == 257) { + incrementCursor((short) 1); + modulusLen--; + } + short blob = KMByteBlob.instance(modulusLen); + getBytes(blob); + return blob; + } + + 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 KMPKCS8DecoderImpl instance() { + if (inst == null) { + inst = new KMPKCS8DecoderImpl(); + } + 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); + } +}