From 5e28d93bed62630bdd8191d968dba77af6a99304 Mon Sep 17 00:00:00 2001 From: cpathak Date: Fri, 27 Mar 2020 17:07:20 -0700 Subject: [PATCH 1/6] Optimization changes - removed command related classes and combined them into KMKeymasterApplet class - removed context class as it is no longer needed. - changed the random number generation logic to use aes cbc with aes 128 bit key encryption as simulator does not support aes ecb and aes 256 bit key. - changed exception throwing to use ISOException.throwit() to use runtime environment's instance. --- .../keymaster/KMAbortOperationCmd.java | 36 -- .../javacard/keymaster/KMAbstractCmd.java | 83 ---- .../keymaster/KMAddRngEntropyCmd.java | 70 --- .../android/javacard/keymaster/KMArray.java | 5 +- .../javacard/keymaster/KMAttestKeyCmd.java | 36 -- .../keymaster/KMBeginOperationCmd.java | 36 -- .../android/javacard/keymaster/KMBoolTag.java | 3 +- .../javacard/keymaster/KMByteBlob.java | 13 +- .../android/javacard/keymaster/KMByteTag.java | 5 +- .../android/javacard/keymaster/KMCommand.java | 43 -- .../keymaster/KMComputeSharedHmacCmd.java | 36 -- .../android/javacard/keymaster/KMContext.java | 119 ----- .../android/javacard/keymaster/KMDecoder.java | 47 +- .../keymaster/KMDeleteAllKeysCmd.java | 36 -- .../javacard/keymaster/KMDeleteKeyCmd.java | 36 -- .../keymaster/KMDestroyAttestationIdsCmd.java | 36 -- .../android/javacard/keymaster/KMEncoder.java | 5 +- .../android/javacard/keymaster/KMEnum.java | 3 +- .../javacard/keymaster/KMEnumArrayTag.java | 7 +- .../android/javacard/keymaster/KMEnumTag.java | 14 +- .../javacard/keymaster/KMException.java | 9 +- .../javacard/keymaster/KMExportKeyCmd.java | 36 -- .../keymaster/KMFinishOperationCmd.java | 36 -- .../javacard/keymaster/KMGenerateKeyCmd.java | 36 -- .../javacard/keymaster/KMGetHWInfoCmd.java | 52 --- .../KMGetHmacSharingParametersCmd.java | 36 -- .../keymaster/KMGetKeyCharacteristicsCmd.java | 36 -- .../keymaster/KMHardwareAuthToken.java | 3 +- .../keymaster/KMHmacSharingParameters.java | 3 +- .../javacard/keymaster/KMImportKeyCmd.java | 35 -- .../keymaster/KMImportWrappedKeyCmd.java | 35 -- .../android/javacard/keymaster/KMInteger.java | 16 +- .../javacard/keymaster/KMIntegerArrayTag.java | 7 +- .../javacard/keymaster/KMIntegerTag.java | 3 +- .../keymaster/KMKeyCharacteristics.java | 3 +- .../javacard/keymaster/KMKeymasterApplet.java | 429 +++++++++++++----- .../javacard/keymaster/KMMessenger.java | 22 - .../javacard/keymaster/KMOperationState.java | 8 +- .../javacard/keymaster/KMProvisionCmd.java | 61 --- .../javacard/keymaster/KMRepository.java | 130 ++---- .../keymaster/KMUpdateOperationCmd.java | 36 -- .../javacard/keymaster/KMUpgradeKeyCmd.java | 36 -- .../android/javacard/keymaster/KMUtil.java | 45 +- .../keymaster/KMVerificationToken.java | 3 +- .../keymaster/KMVerifyAuthorizationCmd.java | 36 -- 45 files changed, 480 insertions(+), 1341 deletions(-) delete mode 100644 Applet/Applet/src/com/android/javacard/keymaster/KMAbortOperationCmd.java delete mode 100644 Applet/Applet/src/com/android/javacard/keymaster/KMAbstractCmd.java delete mode 100644 Applet/Applet/src/com/android/javacard/keymaster/KMAddRngEntropyCmd.java delete mode 100644 Applet/Applet/src/com/android/javacard/keymaster/KMAttestKeyCmd.java delete mode 100644 Applet/Applet/src/com/android/javacard/keymaster/KMBeginOperationCmd.java delete mode 100644 Applet/Applet/src/com/android/javacard/keymaster/KMCommand.java delete mode 100644 Applet/Applet/src/com/android/javacard/keymaster/KMComputeSharedHmacCmd.java delete mode 100644 Applet/Applet/src/com/android/javacard/keymaster/KMContext.java delete mode 100644 Applet/Applet/src/com/android/javacard/keymaster/KMDeleteAllKeysCmd.java delete mode 100644 Applet/Applet/src/com/android/javacard/keymaster/KMDeleteKeyCmd.java delete mode 100644 Applet/Applet/src/com/android/javacard/keymaster/KMDestroyAttestationIdsCmd.java delete mode 100644 Applet/Applet/src/com/android/javacard/keymaster/KMExportKeyCmd.java delete mode 100644 Applet/Applet/src/com/android/javacard/keymaster/KMFinishOperationCmd.java delete mode 100644 Applet/Applet/src/com/android/javacard/keymaster/KMGenerateKeyCmd.java delete mode 100644 Applet/Applet/src/com/android/javacard/keymaster/KMGetHWInfoCmd.java delete mode 100644 Applet/Applet/src/com/android/javacard/keymaster/KMGetHmacSharingParametersCmd.java delete mode 100644 Applet/Applet/src/com/android/javacard/keymaster/KMGetKeyCharacteristicsCmd.java delete mode 100644 Applet/Applet/src/com/android/javacard/keymaster/KMImportKeyCmd.java delete mode 100644 Applet/Applet/src/com/android/javacard/keymaster/KMImportWrappedKeyCmd.java delete mode 100644 Applet/Applet/src/com/android/javacard/keymaster/KMMessenger.java delete mode 100644 Applet/Applet/src/com/android/javacard/keymaster/KMProvisionCmd.java delete mode 100644 Applet/Applet/src/com/android/javacard/keymaster/KMUpdateOperationCmd.java delete mode 100644 Applet/Applet/src/com/android/javacard/keymaster/KMUpgradeKeyCmd.java delete mode 100644 Applet/Applet/src/com/android/javacard/keymaster/KMVerifyAuthorizationCmd.java diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMAbortOperationCmd.java b/Applet/Applet/src/com/android/javacard/keymaster/KMAbortOperationCmd.java deleted file mode 100644 index ee3e052a..00000000 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMAbortOperationCmd.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright(C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.javacard.keymaster; - -public class KMAbortOperationCmd extends KMAbstractCmd { - public static final byte INS_ABORT_OPERATION_CMD = 0x22; - - @Override - protected KMArray getExpectedArgs() { - return null; - } - - @Override - protected KMArray process(KMArray args, KMContext context) { - return null; - } - - @Override - public byte getIns() { - return INS_ABORT_OPERATION_CMD; - } -} diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMAbstractCmd.java b/Applet/Applet/src/com/android/javacard/keymaster/KMAbstractCmd.java deleted file mode 100644 index 02fad435..00000000 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMAbstractCmd.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright(C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.javacard.keymaster; - -public abstract class KMAbstractCmd implements KMCommand { - - /** - * Implements the KMCommand interface. - * - * @param context provides information required to execute the command. - */ - @Override - public void execute(KMContext context) { - // Assert the command's operational state - if (!this.validateState(context.getKeymasterState())) { - throw new KMException(KMException.CMD_NOT_ACCEPTED_WRONG_STATE); - } - KMEncoder encoder = context.getRepository().getEncoder(); - KMDecoder decoder = context.getRepository().getDecoder(); - // Get getExpectedArgs if expected - KMArray args = null; - if (hasArguments()) { - // Deserialize the getExpectedArgs - KMArray argsProto = getExpectedArgs(); - args = decoder.decode(argsProto, context.getBuffer(), (short) 0, context.getBufferLength()); - } - // Pass control to concrete command subclass - KMArray resp = this.process(args, context); - context.setBufferLength((short)0); - // If there is resp then serialize and send - if (resp != null) { - // set outgoing buffer - short len = encoder.encode(resp, context.getBuffer(), (short) 0, (short)context.getBuffer().length); - context.setBufferLength(len); - } - } - - /** - * Get the getExpectedArgs prototype expression from the concrete subclass. - * - * @return KMArray of KMType objects which provides expression for the command's getExpectedArgs.. - */ - protected abstract KMArray getExpectedArgs(); - - /** - * Implemented by the subclass to execute the command specific functionality. - * - * @param args which are decoded from the the apdu. - * @param context within which the command should be executed. - * @return Null or response having the result of the command's execution. - */ - protected abstract KMArray process(KMArray args, KMContext context); - - /** - * Validate the state required by the command to execute. By default all the commands can execute - * in active state. - * - * @param state is the current state of the applet - * @return true if the state is valid for command's execution else false is returned. - */ - protected boolean validateState(byte state) { - return (KMKeymasterApplet.ACTIVE_STATE == state); - } - - @Override - public boolean hasArguments(){ - return true; - } -} diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMAddRngEntropyCmd.java b/Applet/Applet/src/com/android/javacard/keymaster/KMAddRngEntropyCmd.java deleted file mode 100644 index cb431b8f..00000000 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMAddRngEntropyCmd.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright(C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.javacard.keymaster; - -import javacard.framework.ISO7816; -import javacard.framework.JCSystem; -import javacard.framework.Util; - -// This command adds entropy into the current entropy pool as per hal specifications. -public class KMAddRngEntropyCmd extends KMAbstractCmd { - public static final byte INS_ADD_RNG_ENTROPY_CMD = 0x18; - public static final short MAX_SEED_SIZE = 2048; - - @Override - protected KMArray getExpectedArgs() { - return KMArray.instance((short) 1).add((short) 0, KMByteBlob.instance()); - } - - @Override - protected KMArray process(KMArray args, KMContext context) { - KMByteBlob blob = (KMByteBlob) args.get((short) 0); - // maximum 2KiB of seed is allowed. - if (blob.length() > MAX_SEED_SIZE) { - throw new KMException(ISO7816.SW_WRONG_LENGTH); - } - KMUtil.init(context.getRepository()); - // Get existing entropy pool. - byte[] entPool = context.getRepository().getEntropyPool(); - // Create new temporary pool. - byte[] heapRef = context.getRepository().getByteHeapRef(); - short poolStart = context.getRepository().newByteArray((short) entPool.length); - // Populate the new pool with the entropy which is derived from current entropy pool. - KMUtil.newRandomNumber(heapRef, poolStart, (short) entPool.length); - // Copy the entropy to the current pool - updates the entropy pool. - Util.arrayCopy(heapRef, poolStart, entPool, (short) 0, (short) entPool.length); - short index = 0; - short randIndex = 0; - // Mix (XOR) the seed received from the master in the entropy pool - 32 bytes (entPool.length). - // at a time. - while (index < blob.length()) { - entPool[randIndex] = (byte) (entPool[randIndex] ^ blob.get(index)); - randIndex++; - index++; - if (randIndex >= entPool.length) { - randIndex = 0; - } - } - // TODO return success error code. - return null; - } - - @Override - public byte getIns() { - return INS_ADD_RNG_ENTROPY_CMD; - } -} diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMArray.java b/Applet/Applet/src/com/android/javacard/keymaster/KMArray.java index 23b5afe6..d2eed688 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMArray.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMArray.java @@ -17,6 +17,7 @@ package com.android.javacard.keymaster; import javacard.framework.ISO7816; +import javacard.framework.ISOException; public class KMArray extends KMType { private KMType[] vals; @@ -67,7 +68,7 @@ public KMArray withLength(short length) { public KMArray add(short index, KMType val) { if (index >= length) { - throw new KMException(ISO7816.SW_WRONG_LENGTH); + ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); } vals[(short) (startOff + index)] = val; return this; @@ -75,7 +76,7 @@ public KMArray add(short index, KMType val) { public KMType get(short index) { if (index >= length) { - throw new KMException(ISO7816.SW_WRONG_LENGTH); + ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); } return vals[(short) (startOff + index)]; } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMAttestKeyCmd.java b/Applet/Applet/src/com/android/javacard/keymaster/KMAttestKeyCmd.java deleted file mode 100644 index 9d51e231..00000000 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMAttestKeyCmd.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright(C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.javacard.keymaster; - -public class KMAttestKeyCmd extends KMAbstractCmd { - public static final byte INS_ATTEST_KEY_CMD = 0x14; - - @Override - protected KMArray getExpectedArgs() { - return null; - } - - @Override - protected KMArray process(KMArray args, KMContext context) { - return null; - } - - @Override - public byte getIns() { - return INS_ATTEST_KEY_CMD; - } -} diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMBeginOperationCmd.java b/Applet/Applet/src/com/android/javacard/keymaster/KMBeginOperationCmd.java deleted file mode 100644 index 2ab55233..00000000 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMBeginOperationCmd.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright(C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.javacard.keymaster; - -public class KMBeginOperationCmd extends KMAbstractCmd { - public static final byte INS_BEGIN_OPERATION_CMD = 0x1F; - - @Override - protected KMArray getExpectedArgs() { - return null; - } - - @Override - protected KMArray process(KMArray args, KMContext context) { - return null; - } - - @Override - public byte getIns() { - return INS_BEGIN_OPERATION_CMD; - } -} diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMBoolTag.java b/Applet/Applet/src/com/android/javacard/keymaster/KMBoolTag.java index 6cf41894..2e088893 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMBoolTag.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMBoolTag.java @@ -17,6 +17,7 @@ package com.android.javacard.keymaster; import javacard.framework.ISO7816; +import javacard.framework.ISOException; public class KMBoolTag extends KMTag { @@ -81,7 +82,7 @@ public byte getVal() { // create default assignBlob without any value public static KMBoolTag instance(short key) { if (!validateKey(key)) { - throw new KMException(ISO7816.SW_DATA_INVALID); + ISOException.throwIt(ISO7816.SW_DATA_INVALID); } KMBoolTag tag = repository.newBoolTag(); tag.key = key; diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMByteBlob.java b/Applet/Applet/src/com/android/javacard/keymaster/KMByteBlob.java index fca5b947..54b0e9e4 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMByteBlob.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMByteBlob.java @@ -17,6 +17,7 @@ package com.android.javacard.keymaster; import javacard.framework.ISO7816; +import javacard.framework.ISOException; import javacard.framework.Util; // Byte val represents contiguous memory buffer. @@ -48,7 +49,7 @@ public static KMByteBlob instance() { // copy the blob public static KMByteBlob instance(byte[] blob, short startOff, short length) { if ((length <= 0) || ((short)(startOff+length) > blob.length)) { - throw new KMException(ISO7816.SW_WRONG_LENGTH); + ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); } KMByteBlob inst = instance(length); Util.arrayCopyNonAtomic(blob, startOff, inst.val, inst.startOff, inst.length); @@ -58,7 +59,7 @@ public static KMByteBlob instance(byte[] blob, short startOff, short length) { // returns empty blob with given length public static KMByteBlob instance(short length) { if (length <= 0) { - throw new KMException(ISO7816.SW_WRONG_LENGTH); + ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); } KMByteBlob inst = instance(); inst.startOff = repository.newByteArray(length); @@ -83,20 +84,20 @@ public KMByteBlob withLength(short len) { public void add(short index, byte val) { if (index >= this.length) { - throw new KMException(ISO7816.SW_WRONG_LENGTH); + ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); } if (this.val == null) { - throw new KMException(ISO7816.SW_DATA_INVALID); + ISOException.throwIt(ISO7816.SW_DATA_INVALID); } this.val[(short) (startOff + index)] = val; } public byte get(short index) { if (index >= this.length) { - throw new KMException(ISO7816.SW_WRONG_LENGTH); + ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); } if (this.val == null) { - throw new KMException(ISO7816.SW_DATA_INVALID); + ISOException.throwIt(ISO7816.SW_DATA_INVALID); } return this.val[(short) (startOff + index)]; } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMByteTag.java b/Applet/Applet/src/com/android/javacard/keymaster/KMByteTag.java index c415abbc..1569394c 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMByteTag.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMByteTag.java @@ -17,6 +17,7 @@ package com.android.javacard.keymaster; import javacard.framework.ISO7816; +import javacard.framework.ISOException; public class KMByteTag extends KMTag { @@ -74,7 +75,7 @@ public static KMByteTag instance() { public static KMByteTag instance(short key) { if (!validateKey(key)) { - throw new KMException(ISO7816.SW_DATA_INVALID); + ISOException.throwIt(ISO7816.SW_DATA_INVALID); } KMByteTag tag = repository.newByteTag(); tag.key = key; @@ -93,7 +94,7 @@ public static void create(KMByteTag[] byteTagRefTable) { // create default assignBlob without any value public static KMByteTag instance(short key, KMByteBlob array) { if (!validateKey(key)) { - throw new KMException(ISO7816.SW_DATA_INVALID); + ISOException.throwIt(ISO7816.SW_DATA_INVALID); } KMByteTag tag = repository.newByteTag(); tag.key = key; diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMCommand.java b/Applet/Applet/src/com/android/javacard/keymaster/KMCommand.java deleted file mode 100644 index 9e17dd58..00000000 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMCommand.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright(C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.javacard.keymaster; - -import javacard.framework.APDU; - -/** This interface declares methods to be implemented by the command instances. */ -public interface KMCommand { - /** - * Execute this command within given context. If the command fails then it throws an exception - * - * @param context provides information required to execute the command. - */ - void execute(KMContext context); - - /** - * Return the instruction code associated with this command. The implementations will provide this - * code. - * - * @return instruction code which is related APDU INS. - */ - byte getIns(); - - /** - * Indicates whether command has arguments. - * @ return true if the command has arguments - */ - boolean hasArguments(); -} diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMComputeSharedHmacCmd.java b/Applet/Applet/src/com/android/javacard/keymaster/KMComputeSharedHmacCmd.java deleted file mode 100644 index 5d2e0b46..00000000 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMComputeSharedHmacCmd.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright(C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.javacard.keymaster; - -public class KMComputeSharedHmacCmd extends KMAbstractCmd { - public static final byte INS_COMPUTE_SHARED_HMAC_CMD = 0x19; - - @Override - protected KMArray getExpectedArgs() { - return null; - } - - @Override - protected KMArray process(KMArray args, KMContext context) { - return null; - } - - @Override - public byte getIns() { - return INS_COMPUTE_SHARED_HMAC_CMD; - } -} diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMContext.java b/Applet/Applet/src/com/android/javacard/keymaster/KMContext.java deleted file mode 100644 index 0d081f89..00000000 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMContext.java +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright(C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.javacard.keymaster; - -/** - * This class provides data structure for information which is passed between the Keymaster Applet - * and the commands. It is created by applet and initialized for the process request. Applet sets - * repository, apdu and keymasterState. Command sets and uses incoming buffer information, outgoing - * buffer information and operation state (if command is an operation). - */ -public class KMContext { - private KMRepository repository; - private byte keymasterState; - private KMOperationState opState; - private byte[] buffer; - private short bufferLength; - /** - * Setter for the keymasterState. Set by the applet. - * - * @param keymasterState represents current applet state. - */ - public void setKeymasterState(byte keymasterState) { - this.keymasterState = keymasterState; - } - - /** - * Getter for keymasterState. Used by the commands. - * - * @return keymasterState represents current applets state. - */ - public byte getKeymasterState() { - return keymasterState; - } - - - /** - * Getter for buffer used for receiving or sending data to or from the master. Used by the - * messenger. - * - * @return buffer which is used to copying data to and from apdu's buffer. Start offset is always - * 0. - */ - public byte[] getBuffer() { - return buffer; - } - - /** - * Setter for buffer. Used by the repository. - * - * @param buffer which is used to copying data to and from apdu's buffer. - */ - public void setBuffer(byte[] buffer) { - this.buffer = buffer; - } - - /** - * Getter for buffer length. Used by the messenger and commands. - * - * @return buffer length. - */ - public short getBufferLength() { - return bufferLength; - } - - /** - * Setter for buffer length. Used by the messenger commands. - * - * @param length of buffer. - */ - public void setBufferLength(short length) { - this.bufferLength = length; - } - - /** - * Getter for repository instance. Used by commands. - * - * @return repository - */ - public KMRepository getRepository() { - return repository; - } - - /** - * Setter for the repository instance. Used by the applet. - * - * @param repository is repository of the KMType objects and other objects. - */ - public void setRepository(KMRepository repository) { - this.repository = repository; - } - - /** - * Getter for the OperationState for operation specific commands. Used by commands. - * - * @return Operation state associated with the command. - */ - public KMOperationState getOpState() { - return opState; - } - - /** Setter for the OperationState for operation specific commands. Used by commands. */ - public void setOpState(KMOperationState opState) { - this.opState = opState; - } -} diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMDecoder.java b/Applet/Applet/src/com/android/javacard/keymaster/KMDecoder.java index 4cedfcaf..a1c43aa3 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMDecoder.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMDecoder.java @@ -17,6 +17,7 @@ package com.android.javacard.keymaster; import javacard.framework.ISO7816; +import javacard.framework.ISOException; import javacard.framework.Util; // TODO Clean and refactor the code. public class KMDecoder { @@ -67,7 +68,7 @@ private KMIntegerArrayTag decode(KMIntegerArrayTag exp) { readTagKey(exp.getTagType()); // the values are array of integers. if (!(exp.getValues().getType() instanceof KMInteger)) { - throw new KMException(ISO7816.SW_DATA_INVALID); + ISOException.throwIt(ISO7816.SW_DATA_INVALID); } return exp.instance(this.tagKey, decode(exp.getValues(), (KMInteger) exp.getValues().getType())); } @@ -90,10 +91,10 @@ private KMBoolTag decode(KMBoolTag exp) { // BOOL Tag is a leaf node and it must always have tiny encoded uint value = 1. // TODO check this out. if ((buffer[startOff] & MAJOR_TYPE_MASK) != UINT_TYPE) { - throw new KMException(ISO7816.SW_DATA_INVALID); + ISOException.throwIt(ISO7816.SW_DATA_INVALID); } if ((byte) (buffer[startOff] & ADDITIONAL_MASK) != 0x01) { - throw new KMException(ISO7816.SW_DATA_INVALID); + ISOException.throwIt(ISO7816.SW_DATA_INVALID); } incrementStartOff((short) 1); return exp.instance(tagKey); @@ -104,12 +105,12 @@ private KMEnumTag decode(KMEnumTag exp) { // Enum Tag value will always be integer with max 1 byte length. // TODO Check this out. if ((buffer[startOff] & MAJOR_TYPE_MASK) != UINT_TYPE) { - throw new KMException(ISO7816.SW_DATA_INVALID); + ISOException.throwIt(ISO7816.SW_DATA_INVALID); } short len = (short) (buffer[startOff] & ADDITIONAL_MASK); byte enumVal = 0; if (len > UINT8_LENGTH) { - throw new KMException(ISO7816.SW_WRONG_LENGTH); + ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); } if (len < UINT8_LENGTH) { enumVal = (byte)(len & ADDITIONAL_MASK); @@ -126,12 +127,12 @@ private KMEnum decode(KMEnum exp) { // Enum value will always be integer with max 1 byte length. if ((buffer[startOff] & MAJOR_TYPE_MASK) != UINT_TYPE) { - throw new KMException(ISO7816.SW_DATA_INVALID); + ISOException.throwIt(ISO7816.SW_DATA_INVALID); } short len = (short) (buffer[startOff] & ADDITIONAL_MASK); byte enumVal = 0; if (len > UINT8_LENGTH) { - throw new KMException(ISO7816.SW_WRONG_LENGTH); + ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); } if (len < UINT8_LENGTH) { enumVal = (byte)(len & ADDITIONAL_MASK); @@ -147,9 +148,12 @@ private KMEnum decode(KMEnum exp) { private KMInteger decode(KMInteger exp) { KMInteger inst; if ((buffer[startOff] & MAJOR_TYPE_MASK) != UINT_TYPE) { - throw new KMException(ISO7816.SW_DATA_INVALID); + ISOException.throwIt(ISO7816.SW_DATA_INVALID); } short len = (short) (buffer[startOff] & ADDITIONAL_MASK); + if(len > UINT64_LENGTH){ + ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); + } incrementStartOff((short) 1); if (len < UINT8_LENGTH) { inst = exp.uint_8((byte)(len & ADDITIONAL_MASK)); @@ -162,11 +166,9 @@ private KMInteger decode(KMInteger exp) { } else if (len == UINT32_LENGTH) { inst = exp.instance(buffer, startOff, (short) 4); incrementStartOff((short) 4); - } else if (len == UINT64_LENGTH) { + } else { inst = exp.instance(buffer, startOff, (short) 8); incrementStartOff((short) 8); - } else { - throw new KMException(ISO7816.SW_WRONG_LENGTH); } return inst; } @@ -181,7 +183,7 @@ private KMByteBlob decode(KMByteBlob exp) { private KMArray decode(KMArray exp) { short payloadLength = readMajorTypeWithPayloadLength(ARRAY_TYPE); if (exp.length() != payloadLength) { - throw new KMException(ISO7816.SW_WRONG_LENGTH); + ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); } KMArray inst = exp.instance(payloadLength); short index = 0; @@ -260,7 +262,7 @@ private KMType decode(KMType exp) { } if (exp instanceof KMVector) { if (!((((KMVector) exp).getType()) instanceof KMInteger)) { - throw new KMException(ISO7816.SW_DATA_INVALID); + ISOException.throwIt(ISO7816.SW_DATA_INVALID); } return decode((KMVector) exp, (KMInteger) ((KMVector) exp).getType()); } @@ -300,16 +302,17 @@ private KMType decode(KMType exp) { if (exp instanceof KMHardwareAuthToken) { return decode((KMHardwareAuthToken) exp); } - throw new KMException(ISO7816.SW_DATA_INVALID); + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + return null; } private short peekTagType() { if ((buffer[startOff] & MAJOR_TYPE_MASK) != UINT_TYPE) { - throw new KMException(ISO7816.SW_DATA_INVALID); + ISOException.throwIt(ISO7816.SW_DATA_INVALID); } if ((short) (buffer[startOff] & ADDITIONAL_MASK) != UINT32_LENGTH) { - throw new KMException(ISO7816.SW_WRONG_LENGTH); + ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); } return (short) ((Util.makeShort(buffer[(short) (startOff + 1)], buffer[(short) (startOff + 2)])) @@ -318,16 +321,16 @@ private short peekTagType() { private void readTagKey(short expectedTagType) { if ((buffer[startOff] & MAJOR_TYPE_MASK) != UINT_TYPE) { - throw new KMException(ISO7816.SW_DATA_INVALID); + ISOException.throwIt(ISO7816.SW_DATA_INVALID); } if ((byte) (buffer[startOff] & ADDITIONAL_MASK) != UINT32_LENGTH) { - throw new KMException(ISO7816.SW_WRONG_LENGTH); + ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); } incrementStartOff((short) 1); this.tagType = readShort(); this.tagKey = readShort(); if (tagType != expectedTagType) { - throw new KMException(ISO7816.SW_DATA_INVALID); + ISOException.throwIt(ISO7816.SW_DATA_INVALID); } } @@ -336,11 +339,11 @@ private short readMajorTypeWithPayloadLength(short majorType) { short payloadLength = 0; byte val = readByte(); if ((short) (val & MAJOR_TYPE_MASK) != majorType) { - throw new KMException(ISO7816.SW_DATA_INVALID); + ISOException.throwIt(ISO7816.SW_DATA_INVALID); } short lenType = (short) (val & ADDITIONAL_MASK); if (lenType > UINT16_LENGTH) { - throw new KMException(ISO7816.SW_WRONG_LENGTH); + ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); } if (lenType < UINT8_LENGTH) { payloadLength = lenType; @@ -367,7 +370,7 @@ private byte readByte() { private void incrementStartOff(short inc) { startOff += inc; if (startOff > this.length) { - throw new KMException(ISO7816.SW_DATA_INVALID); + ISOException.throwIt(ISO7816.SW_DATA_INVALID); } } } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMDeleteAllKeysCmd.java b/Applet/Applet/src/com/android/javacard/keymaster/KMDeleteAllKeysCmd.java deleted file mode 100644 index 5c476cd0..00000000 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMDeleteAllKeysCmd.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright(C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.javacard.keymaster; - -public class KMDeleteAllKeysCmd extends KMAbstractCmd { - public static final byte INS_DELETE_ALL_KEYS_CMD = 0x17; - - @Override - protected KMArray getExpectedArgs() { - return null; - } - - @Override - protected KMArray process(KMArray args, KMContext context) { - return null; - } - - @Override - public byte getIns() { - return INS_DELETE_ALL_KEYS_CMD; - } -} diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMDeleteKeyCmd.java b/Applet/Applet/src/com/android/javacard/keymaster/KMDeleteKeyCmd.java deleted file mode 100644 index ced6e7e4..00000000 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMDeleteKeyCmd.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright(C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.javacard.keymaster; - -public class KMDeleteKeyCmd extends KMAbstractCmd { - public static final byte INS_DELETE_KEY_CMD = 0x16; - - @Override - protected KMArray getExpectedArgs() { - return null; - } - - @Override - protected KMArray process(KMArray args, KMContext context) { - return null; - } - - @Override - public byte getIns() { - return INS_DELETE_KEY_CMD; - } -} diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMDestroyAttestationIdsCmd.java b/Applet/Applet/src/com/android/javacard/keymaster/KMDestroyAttestationIdsCmd.java deleted file mode 100644 index fa66ab1f..00000000 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMDestroyAttestationIdsCmd.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright(C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.javacard.keymaster; - -public class KMDestroyAttestationIdsCmd extends KMAbstractCmd { - public static final byte INS_DESTROY_ATT_IDS_CMD = 0x1A; - - @Override - protected KMArray getExpectedArgs() { - return null; - } - - @Override - protected KMArray process(KMArray args, KMContext context) { - return null; - } - - @Override - public byte getIns() { - return INS_DESTROY_ATT_IDS_CMD; - } -} diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMEncoder.java b/Applet/Applet/src/com/android/javacard/keymaster/KMEncoder.java index a231563b..ec736de4 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMEncoder.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMEncoder.java @@ -17,6 +17,7 @@ package com.android.javacard.keymaster; import javacard.framework.ISO7816; +import javacard.framework.ISOException; import javacard.framework.Util; public class KMEncoder { @@ -124,7 +125,7 @@ private void encode(KMType exp){ encode((KMHardwareAuthToken) exp); return; } - throw new KMException(ISO7816.SW_DATA_INVALID); + ISOException.throwIt(ISO7816.SW_DATA_INVALID); } private void encode(KMKeyParameters obj) { @@ -288,7 +289,7 @@ private void writeByte(byte val){ private void incrementStartOff(short inc){ startOff += inc; if (startOff >= this.length) { - throw new KMException(ISO7816.SW_DATA_INVALID); + ISOException.throwIt(ISO7816.SW_DATA_INVALID); } } } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMEnum.java b/Applet/Applet/src/com/android/javacard/keymaster/KMEnum.java index 7dad1d27..441fd833 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMEnum.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMEnum.java @@ -17,6 +17,7 @@ package com.android.javacard.keymaster; import javacard.framework.ISO7816; +import javacard.framework.ISOException; public class KMEnum extends KMType { private static short[] types = {HARDWARE_TYPE, KEY_FORMAT, KEY_DERIVATION_FUNCTION}; @@ -48,7 +49,7 @@ public static KMEnum instance() { public static KMEnum instance(short enumType, byte val) { KMEnum inst = repository.newEnum(); if (!validateEnum(enumType, val)) { - throw new KMException(ISO7816.SW_DATA_INVALID); + ISOException.throwIt(ISO7816.SW_DATA_INVALID); } inst.type = enumType; inst.val = val; diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMEnumArrayTag.java b/Applet/Applet/src/com/android/javacard/keymaster/KMEnumArrayTag.java index 9c76ce66..30c40bad 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMEnumArrayTag.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMEnumArrayTag.java @@ -17,6 +17,7 @@ package com.android.javacard.keymaster; import javacard.framework.ISO7816; +import javacard.framework.ISOException; public class KMEnumArrayTag extends KMTag { @@ -84,7 +85,7 @@ public static KMEnumArrayTag instance(short key) { // check if key is valid. byte[] vals = getAllowedEnumValues(key); if (vals == null) { - throw new KMException(ISO7816.SW_DATA_INVALID); + ISOException.throwIt(ISO7816.SW_DATA_INVALID); } KMEnumArrayTag tag = repository.newEnumArrayTag(); tag.key = key; @@ -124,7 +125,7 @@ public static KMEnumArrayTag instance(short key, KMByteBlob blob) { // validate key byte[] allowedVals = getAllowedEnumValues(key); if (allowedVals == null) { - throw new KMException(ISO7816.SW_DATA_INVALID); + ISOException.throwIt(ISO7816.SW_DATA_INVALID); } short byteIndex = 0; while (byteIndex < blob.length()) { @@ -138,7 +139,7 @@ public static KMEnumArrayTag instance(short key, KMByteBlob blob) { enumIndex++; } if (!validValue) { - throw new KMException(ISO7816.SW_DATA_INVALID); + ISOException.throwIt(ISO7816.SW_DATA_INVALID); } byteIndex++; } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMEnumTag.java b/Applet/Applet/src/com/android/javacard/keymaster/KMEnumTag.java index 3cfa840e..9a641425 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMEnumTag.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMEnumTag.java @@ -17,6 +17,7 @@ package com.android.javacard.keymaster; import javacard.framework.ISO7816; +import javacard.framework.ISOException; public class KMEnumTag extends KMTag { @@ -60,13 +61,12 @@ public static KMEnumTag instance() { } public static KMEnumTag instance(short key) { - if (validateEnum(key, NO_VALUE)) { - KMEnumTag tag = repository.newEnumTag(); - tag.key = key; - return tag; - } else { - throw new KMException(ISO7816.SW_DATA_INVALID); + if(!validateEnum(key, NO_VALUE)){ + ISOException.throwIt(ISO7816.SW_DATA_INVALID); } + KMEnumTag tag = repository.newEnumTag(); + tag.key = key; + return tag; } public static void create(KMEnumTag[] enumTagRefTable) { @@ -124,7 +124,7 @@ public byte getValue() { // instantiate enum tag. public static KMEnumTag instance(short key, byte val) { if (!validateEnum(key, val)) { - throw new KMException(ISO7816.SW_DATA_INVALID); + ISOException.throwIt(ISO7816.SW_DATA_INVALID); } KMEnumTag tag = instance(key); tag.val = val; diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMException.java b/Applet/Applet/src/com/android/javacard/keymaster/KMException.java index 3e357613..3a26b047 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMException.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMException.java @@ -16,14 +16,13 @@ package com.android.javacard.keymaster; -import javacard.framework.ISOException; - -public class KMException extends ISOException { +public class KMException { // The Applet is not in a correct state in order to execute the command. public static final short CMD_NOT_ACCEPTED_WRONG_STATE = (short) 0x6901; - public KMException(short i) { - super(i); + + public static boolean handle(short reason) { + return false; } } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMExportKeyCmd.java b/Applet/Applet/src/com/android/javacard/keymaster/KMExportKeyCmd.java deleted file mode 100644 index 05d28ef6..00000000 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMExportKeyCmd.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright(C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.javacard.keymaster; - -public class KMExportKeyCmd extends KMAbstractCmd { - public static final byte INS_EXPORT_KEY_CMD = 0x13; - - @Override - protected KMArray getExpectedArgs() { - return null; - } - - @Override - protected KMArray process(KMArray args, KMContext context) { - return null; - } - - @Override - public byte getIns() { - return INS_EXPORT_KEY_CMD; - } -} diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMFinishOperationCmd.java b/Applet/Applet/src/com/android/javacard/keymaster/KMFinishOperationCmd.java deleted file mode 100644 index 0267e4fb..00000000 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMFinishOperationCmd.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright(C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.javacard.keymaster; - -public class KMFinishOperationCmd extends KMAbstractCmd { - public static final byte INS_FINISH_OPERATION_CMD = 0x21; - - @Override - protected KMArray getExpectedArgs() { - return null; - } - - @Override - protected KMArray process(KMArray args, KMContext context) { - return null; - } - - @Override - public byte getIns() { - return INS_FINISH_OPERATION_CMD; - } -} diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMGenerateKeyCmd.java b/Applet/Applet/src/com/android/javacard/keymaster/KMGenerateKeyCmd.java deleted file mode 100644 index 0c2d69d1..00000000 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMGenerateKeyCmd.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright(C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.javacard.keymaster; - -public class KMGenerateKeyCmd extends KMAbstractCmd { - public static final byte INS_GENERATE_KEY_CMD = 0x10; - - @Override - protected KMArray getExpectedArgs() { - return null; - } - - @Override - protected KMArray process(KMArray args, KMContext context) { - return null; - } - - @Override - public byte getIns() { - return INS_GENERATE_KEY_CMD; - } -} diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMGetHWInfoCmd.java b/Applet/Applet/src/com/android/javacard/keymaster/KMGetHWInfoCmd.java deleted file mode 100644 index 3f6cb8f8..00000000 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMGetHWInfoCmd.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright(C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.javacard.keymaster; - -public class KMGetHWInfoCmd extends KMAbstractCmd { - public static final byte INS_GET_HW_INFO_CMD = 0x1E; - public static final byte[] JavacardKeymasterDevice = { - 0x4A, 0x61, 0x76, 0x61, 0x63, 0x61, 0x72, 0x64, 0x4B, 0x65, 0x79, 0x6D, 0x61, 0x73, 0x74, 0x65, - 0x72, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, - }; - public static final byte[] Google = {0x47, 0x6F, 0x6F, 0x67, 0x6C, 0x65}; - - @Override - protected KMArray getExpectedArgs() { - return null; - } - - @Override - protected KMArray process(KMArray args, KMContext context) { - return KMArray.instance((short) 3) - .add((short) 0, KMEnum.instance(KMType.HARDWARE_TYPE, KMType.STRONGBOX)) - .add( - (short) 1, - KMByteBlob.instance( - JavacardKeymasterDevice, (short) 0, (short) JavacardKeymasterDevice.length)) - .add((short) 2, KMByteBlob.instance(Google, (short) 0, (short) Google.length)); - } - - @Override - public byte getIns() { - return INS_GET_HW_INFO_CMD; - } - - @Override - public boolean hasArguments() { - return false; - } -} diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMGetHmacSharingParametersCmd.java b/Applet/Applet/src/com/android/javacard/keymaster/KMGetHmacSharingParametersCmd.java deleted file mode 100644 index 19165531..00000000 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMGetHmacSharingParametersCmd.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright(C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.javacard.keymaster; - -public class KMGetHmacSharingParametersCmd extends KMAbstractCmd { - public static final byte INS_GET_HMAC_SHARING_PARAM_CMD = 0x1C; - - @Override - protected KMArray getExpectedArgs() { - return null; - } - - @Override - protected KMArray process(KMArray args, KMContext context) { - return null; - } - - @Override - public byte getIns() { - return INS_GET_HMAC_SHARING_PARAM_CMD; - } -} diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMGetKeyCharacteristicsCmd.java b/Applet/Applet/src/com/android/javacard/keymaster/KMGetKeyCharacteristicsCmd.java deleted file mode 100644 index 20fef22b..00000000 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMGetKeyCharacteristicsCmd.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright(C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.javacard.keymaster; - -public class KMGetKeyCharacteristicsCmd extends KMAbstractCmd { - public static final byte INS_GET_KEY_CHARACTERISTICS_CMD = 0x1D; - - @Override - protected KMArray getExpectedArgs() { - return null; - } - - @Override - protected KMArray process(KMArray args, KMContext context) { - return null; - } - - @Override - public byte getIns() { - return INS_GET_KEY_CHARACTERISTICS_CMD; - } -} diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMHardwareAuthToken.java b/Applet/Applet/src/com/android/javacard/keymaster/KMHardwareAuthToken.java index ca3c2804..9c188c67 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMHardwareAuthToken.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMHardwareAuthToken.java @@ -17,6 +17,7 @@ package com.android.javacard.keymaster; import javacard.framework.ISO7816; +import javacard.framework.ISOException; public class KMHardwareAuthToken extends KMType { public static final byte CHALLENGE = 0x00; @@ -56,7 +57,7 @@ public static KMHardwareAuthToken instance() { public static KMHardwareAuthToken instance(KMArray vals) { if (vals.length() != 6) { - throw new KMException(ISO7816.SW_WRONG_LENGTH); + ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); } KMHardwareAuthToken inst = repository.newHwAuthToken(); inst.vals = vals; diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMHmacSharingParameters.java b/Applet/Applet/src/com/android/javacard/keymaster/KMHmacSharingParameters.java index ef08696b..a1fca4d1 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMHmacSharingParameters.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMHmacSharingParameters.java @@ -17,6 +17,7 @@ package com.android.javacard.keymaster; import javacard.framework.ISO7816; +import javacard.framework.ISOException; public class KMHmacSharingParameters extends KMType { public static final byte SEED = 0x00; @@ -47,7 +48,7 @@ public static KMHmacSharingParameters instance() { public static KMHmacSharingParameters instance(KMArray vals) { if (vals.length() != 2) { - throw new KMException(ISO7816.SW_WRONG_LENGTH); + ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); } KMHmacSharingParameters inst = repository.newHmacSharingParameters(); inst.vals = vals; diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMImportKeyCmd.java b/Applet/Applet/src/com/android/javacard/keymaster/KMImportKeyCmd.java deleted file mode 100644 index 6be0d0d7..00000000 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMImportKeyCmd.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright(C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.javacard.keymaster; - -public class KMImportKeyCmd extends KMAbstractCmd { - public static final byte INS_IMPORT_KEY_CMD = 0x11; - @Override - protected KMArray getExpectedArgs() { - return null; - } - - @Override - protected KMArray process(KMArray args, KMContext context) { - return null; - } - - @Override - public byte getIns() { - return INS_IMPORT_KEY_CMD; - } -} diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMImportWrappedKeyCmd.java b/Applet/Applet/src/com/android/javacard/keymaster/KMImportWrappedKeyCmd.java deleted file mode 100644 index e9896748..00000000 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMImportWrappedKeyCmd.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright(C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.javacard.keymaster; - -public class KMImportWrappedKeyCmd extends KMAbstractCmd { - public static final byte INS_IMPORT_WRAPPED_KEY_CMD = 0x12; - @Override - protected KMArray getExpectedArgs() { - return null; - } - - @Override - protected KMArray process(KMArray args, KMContext context) { - return null; - } - - @Override - public byte getIns() { - return INS_IMPORT_WRAPPED_KEY_CMD; - } -} diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMInteger.java b/Applet/Applet/src/com/android/javacard/keymaster/KMInteger.java index 19996d18..987a0858 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMInteger.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMInteger.java @@ -17,6 +17,7 @@ package com.android.javacard.keymaster; import javacard.framework.ISO7816; +import javacard.framework.ISOException; import javacard.framework.Util; // Represents 8 bit, 16 bit, 32 bit and 64 bit integers @@ -99,34 +100,35 @@ public KMInteger setValue(byte[] val) { public short getShort() { if (val == null) { - throw new KMException(ISO7816.SW_DATA_INVALID); + ISOException.throwIt(ISO7816.SW_DATA_INVALID); } else if (val.length != 4) { - throw new KMException(ISO7816.SW_WRONG_LENGTH); + ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); } return Util.makeShort(val[2], val[3]); } public byte getByte() { if (val == null) { - throw new KMException(ISO7816.SW_DATA_INVALID); + ISOException.throwIt(ISO7816.SW_DATA_INVALID); } else if (val.length != 4) { - throw new KMException(ISO7816.SW_WRONG_LENGTH); + ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); } return val[3]; } // copy the integer value from bytes public static KMInteger instance(byte[] num, short srcOff, short length) { + if(length > 8){ + ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); + } if (length == 1) { return uint_8(num[srcOff]); } else if (length == 2) { return uint_16(Util.makeShort(num[srcOff], num[(short) (srcOff + 1)])); } else if (length == 4) { return uint_32(num, srcOff); - } else if (length == 8) { - return uint_64(num, srcOff); } else { - throw new KMException(ISO7816.SW_WRONG_LENGTH); + return uint_64(num, srcOff); } } } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMIntegerArrayTag.java b/Applet/Applet/src/com/android/javacard/keymaster/KMIntegerArrayTag.java index 9c9258a2..df96e114 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMIntegerArrayTag.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMIntegerArrayTag.java @@ -17,6 +17,7 @@ package com.android.javacard.keymaster; import javacard.framework.ISO7816; +import javacard.framework.ISOException; public class KMIntegerArrayTag extends KMTag { private static final short[] tags = {USER_SECURE_ID}; @@ -69,7 +70,7 @@ public KMIntegerArrayTag asUlongArray() { public static KMIntegerArrayTag instance(short key) { if (!validateKey(key)) { - throw new KMException(ISO7816.SW_DATA_INVALID); + ISOException.throwIt(ISO7816.SW_DATA_INVALID); } KMIntegerArrayTag tag = repository.newIntegerArrayTag(); tag.key = key; @@ -79,10 +80,10 @@ public static KMIntegerArrayTag instance(short key) { public static KMIntegerArrayTag instance(short key, KMVector val) { if (!(val.getType() instanceof KMInteger)) { - throw new KMException(ISO7816.SW_DATA_INVALID); + ISOException.throwIt(ISO7816.SW_DATA_INVALID); } if (!(validateKey(key))) { - throw new KMException(ISO7816.SW_DATA_INVALID); + ISOException.throwIt(ISO7816.SW_DATA_INVALID); } KMIntegerArrayTag tag = repository.newIntegerArrayTag(); tag.key = key; diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMIntegerTag.java b/Applet/Applet/src/com/android/javacard/keymaster/KMIntegerTag.java index 00dbf256..d583f6b2 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMIntegerTag.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMIntegerTag.java @@ -17,6 +17,7 @@ package com.android.javacard.keymaster; import javacard.framework.ISO7816; +import javacard.framework.ISOException; // Implements UINT, ULONG and DATE tags. public class KMIntegerTag extends KMTag { @@ -78,7 +79,7 @@ public static KMIntegerTag instance() { public static KMIntegerTag instance(short key) { if (!validateKey(key)) { - throw new KMException(ISO7816.SW_DATA_INVALID); + ISOException.throwIt(ISO7816.SW_DATA_INVALID); } KMIntegerTag tag = repository.newIntegerTag(); tag.key = key; diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMKeyCharacteristics.java b/Applet/Applet/src/com/android/javacard/keymaster/KMKeyCharacteristics.java index a6b5c7fe..d4c1a9b5 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMKeyCharacteristics.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMKeyCharacteristics.java @@ -17,6 +17,7 @@ package com.android.javacard.keymaster; import javacard.framework.ISO7816; +import javacard.framework.ISOException; public class KMKeyCharacteristics extends KMType { public static final byte SOFTWARE_ENFORCED = 0x00; @@ -47,7 +48,7 @@ public static KMKeyCharacteristics instance() { public static KMKeyCharacteristics instance(KMArray vals) { if (vals.length() != 2) { - throw new KMException(ISO7816.SW_WRONG_LENGTH); + ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); } KMKeyCharacteristics inst = repository.newKeyCharacteristics(); inst.vals = vals; diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java b/Applet/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java index 83512f92..0ee85ff1 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java @@ -19,7 +19,10 @@ import javacard.framework.APDU; import javacard.framework.Applet; import javacard.framework.AppletEvent; +import javacard.framework.CardRuntimeException; import javacard.framework.ISO7816; +import javacard.framework.ISOException; +import javacard.framework.JCSystem; import javacard.framework.Util; import javacardx.apdu.ExtendedLength; @@ -32,29 +35,63 @@ // - remove this in future. public class KMKeymasterApplet extends Applet implements AppletEvent, ExtendedLength { // Constants. - public static final short MAX_LENGTH = (short) 0x04ff; // TODO: make this value configurable. + private static final short MAX_LENGTH = (short) 0x1000; // TODO: make this value configurable. private static final byte CLA_ISO7816_NO_SM_NO_CHAN = (byte) 0x80; - private static final byte KM_HAL_VERSION = (byte) 0x41; + private static final short KM_HAL_VERSION = (short) 0x4000; // Possible states of the applet. - public static final byte ILLEGAL_STATE = 0x00; - public static final byte INSTALL_STATE = 0x01; - public static final byte FIRST_SELECT_STATE = 0x02; - public static final byte ACTIVE_STATE = 0x03; - public static final byte INACTIVE_STATE = 0x04; - public static final byte UNINSTALLED_STATE = 0x05; + private static final byte ILLEGAL_STATE = 0x00; + private static final byte INSTALL_STATE = 0x01; + private static final byte FIRST_SELECT_STATE = 0x02; + private static final byte ACTIVE_STATE = 0x03; + private static final byte INACTIVE_STATE = 0x04; + private static final byte UNINSTALLED_STATE = 0x05; + + // Commands + private static final byte INS_GENERATE_KEY_CMD = 0x10; + private static final byte INS_IMPORT_KEY_CMD = 0x11; + private static final byte INS_IMPORT_WRAPPED_KEY_CMD = 0x12; + private static final byte INS_EXPORT_KEY_CMD = 0x13; + private static final byte INS_ATTEST_KEY_CMD = 0x14; + private static final byte INS_UPGRADE_KEY_CMD = 0x15; + private static final byte INS_DELETE_KEY_CMD = 0x16; + private static final byte INS_DELETE_ALL_KEYS_CMD = 0x17; + private static final byte INS_ADD_RNG_ENTROPY_CMD = 0x18; + private static final byte INS_COMPUTE_SHARED_HMAC_CMD = 0x19; + private static final byte INS_DESTROY_ATT_IDS_CMD = 0x1A; + private static final byte INS_VERIFY_AUTHORIZATION_CMD = 0x1B; + private static final byte INS_GET_HMAC_SHARING_PARAM_CMD = 0x1C; + private static final byte INS_GET_KEY_CHARACTERISTICS_CMD = 0x1D; + private static final byte INS_GET_HW_INFO_CMD = 0x1E; + private static final byte INS_BEGIN_OPERATION_CMD = 0x1F; + private static final byte INS_UPDATE_OPERATION_CMD = 0x20; + private static final byte INS_FINISH_OPERATION_CMD = 0x21; + private static final byte INS_ABORT_OPERATION_CMD = 0x22; + private static final byte INS_PROVISION_CMD = 0x23; + + // GetHwInfo information + // TODO change this to just filling the buffer + private static final byte[] JavacardKeymasterDevice = { + 0x4A, 0x61, 0x76, 0x61, 0x63, 0x61, 0x72, 0x64, 0x4B, 0x65, 0x79, 0x6D, 0x61, 0x73, 0x74, 0x65, + 0x72, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, + }; + private static final byte[] Google = {0x47, 0x6F, 0x6F, 0x67, 0x6C, 0x65}; + private static final short MAX_SEED_SIZE = 2048; // State of the applet. - private byte keymasterState = ILLEGAL_STATE; + private KMEncoder encoder; + private KMDecoder decoder; private KMRepository repository; + private byte keymasterState = ILLEGAL_STATE; + private byte[] buffer; - /** - * Registers this applet. - * - * @param repo reference to the repository which manages all the NVM objects. - */ - protected KMKeymasterApplet(KMRepository repo) { - repository = repo; + /** Registers this applet. */ + protected KMKeymasterApplet() { + keymasterState = KMKeymasterApplet.INSTALL_STATE; + repository = KMRepository.instance(); + KMUtil.init(); + encoder = new KMEncoder(); + decoder = new KMDecoder(); register(); } @@ -66,11 +103,7 @@ protected KMKeymasterApplet(KMRepository repo) { * @param bLength the length in bytes of the parameter data in bArray */ public static void install(byte[] bArray, short bOffset, byte bLength) { - KMRepository repo = new KMRepository(); - // TODO: Read the configuration from the package and pass the data in initialize method. - repo.initialize(); - KMKeymasterApplet keymaster = new KMKeymasterApplet(repo); - keymaster.setKeymasterState(KMKeymasterApplet.INSTALL_STATE); + KMKeymasterApplet keymaster = new KMKeymasterApplet(); } /** @@ -80,11 +113,11 @@ public static void install(byte[] bArray, short bOffset, byte bLength) { */ @Override public boolean select() { - repository.onSelect(); - if (getKeymasterState() == KMKeymasterApplet.INSTALL_STATE) { - setKeymasterState(KMKeymasterApplet.FIRST_SELECT_STATE); - } else if (getKeymasterState() == KMKeymasterApplet.INACTIVE_STATE) { - setKeymasterState(KMKeymasterApplet.ACTIVE_STATE); + KMRepository.instance().onSelect(); + if (keymasterState == KMKeymasterApplet.INSTALL_STATE) { + keymasterState = KMKeymasterApplet.FIRST_SELECT_STATE; + } else if (keymasterState == KMKeymasterApplet.INACTIVE_STATE) { + keymasterState = KMKeymasterApplet.ACTIVE_STATE; } else { return false; } @@ -94,34 +127,36 @@ public boolean select() { /** De-selects this applet. */ @Override public void deselect() { - repository.onDeselect(); - if (getKeymasterState() == KMKeymasterApplet.ACTIVE_STATE) { - setKeymasterState(KMKeymasterApplet.INACTIVE_STATE); + KMRepository.instance().onDeselect(); + if (keymasterState == KMKeymasterApplet.ACTIVE_STATE) { + keymasterState = KMKeymasterApplet.INACTIVE_STATE; } } /** Uninstalls the applet after cleaning the repository. */ @Override public void uninstall() { - repository.onUninstall(); - if (getKeymasterState() != KMKeymasterApplet.UNINSTALLED_STATE) { - setKeymasterState(KMKeymasterApplet.UNINSTALLED_STATE); + KMRepository.instance().onUninstall(); + if (keymasterState != KMKeymasterApplet.UNINSTALLED_STATE) { + keymasterState = KMKeymasterApplet.UNINSTALLED_STATE; } } /** * Processes an incoming APDU and handles it using command objects. * - * @see APDU * @param apdu the incoming APDU */ @Override public void process(APDU apdu) { - repository.onProcess(); + KMRepository.instance().onProcess(); + if (buffer == null) { + buffer = JCSystem.makeTransientByteArray(MAX_LENGTH, JCSystem.CLEAR_ON_RESET); + } // Verify whether applet is in correct state. - if ((getKeymasterState() != KMKeymasterApplet.ACTIVE_STATE) - && (getKeymasterState() != KMKeymasterApplet.FIRST_SELECT_STATE)) { - throw new KMException(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + if ((keymasterState != KMKeymasterApplet.ACTIVE_STATE) + && (keymasterState != KMKeymasterApplet.FIRST_SELECT_STATE)) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); } // If this is select applet apdu which is selecting this applet then return if (apdu.isISOInterindustryCLA()) { @@ -129,105 +164,295 @@ public void process(APDU apdu) { return; } } - // Read the apdu header and buffer. - byte[] buffer = apdu.getBuffer(); - byte apduClass = buffer[ISO7816.OFFSET_CLA]; - byte apduIns = buffer[ISO7816.OFFSET_INS]; - byte halVersion = buffer[ISO7816.OFFSET_P1]; - byte apduP2 = buffer[ISO7816.OFFSET_P2]; - + byte[] apduBuffer = apdu.getBuffer(); + byte apduClass = apduBuffer[ISO7816.OFFSET_CLA]; + byte apduIns = apduBuffer[ISO7816.OFFSET_INS]; + short P1P2 = Util.getShort(apduBuffer, ISO7816.OFFSET_P1); // Validate APDU Header. if ((apduClass != CLA_ISO7816_NO_SM_NO_CHAN)) { - throw new KMException(ISO7816.SW_CLA_NOT_SUPPORTED); - } else if ((halVersion != KMKeymasterApplet.KM_HAL_VERSION) && (apduP2 != (byte) 0x00)) { - throw new KMException(ISO7816.SW_INCORRECT_P1P2); + ISOException.throwIt(ISO7816.SW_CLA_NOT_SUPPORTED); + } else if (P1P2 != KMKeymasterApplet.KM_HAL_VERSION) { + ISOException.throwIt(ISO7816.SW_INCORRECT_P1P2); } - - // Process the APDU. - try { - // Get the command object for specific INS from the repository. - KMCommand command = repository.getCommand(apduIns); - // Get the empty context object from the repository. - KMContext context = repository.getContext(); - // Initialize context - context.setKeymasterState(getKeymasterState()); - context.setBuffer(repository.getBuffer()); - if(command.hasArguments()){ - receiveIncoming(context, apdu); + // Validate whether INS can be supported + if (!(apduIns >= INS_GENERATE_KEY_CMD && apduIns <= INS_PROVISION_CMD)) { + ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED); + } + // Validate if INS is provision command if applet is in FIRST_SELECT_STATE. + if (keymasterState == KMKeymasterApplet.FIRST_SELECT_STATE) { + if (apduIns != INS_PROVISION_CMD) { + ISOException.throwIt(ISO7816.SW_COMMAND_NOT_ALLOWED); } - // Execute the command. If the execution fails then an exception is thrown. - command.execute(context); - - // context has data that needs to be sent - if(context.getBufferLength() >0 ){ - sendOutgoing(context, apdu); + } + // Process the apdu + try { + // Handle the command + handle(apduIns, apdu); + } catch (CardRuntimeException exception) { + if (!(KMException.handle(exception.getReason()))) { + CardRuntimeException.throwIt(exception.getReason()); } - - // Update the Keymaster state according to the context. - setKeymasterState(context.getKeymasterState()); - } catch (KMException exception) { - // TODO: error handling for command related error. - // TODO: This should result in ISOException or exception with keymaster specific error codes + } finally { + // Reset the buffer. + Util.arrayFillNonAtomic(buffer, (short) 0, MAX_LENGTH, (byte) 0); } } - /** - * Sends a response, may be extended response, as requested by the command. - * - * @param context of current command. - */ - public void sendOutgoing(KMContext context, APDU apdu) { - // Initialize source - short srcLength = context.getBufferLength(); + /** Sends a response, may be extended response, as requested by the command. */ + private void sendOutgoing(byte[] srcBuffer, short srcLength, APDU apdu) { if (srcLength > MAX_LENGTH) { - throw new KMException(ISO7816.SW_WRONG_LENGTH); + ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); } // Send data - byte[] srcBuffer = context.getBuffer(); apdu.setOutgoing(); apdu.setOutgoingLength(srcLength); apdu.sendBytesLong(srcBuffer, (short) 0, srcLength); } - /** - * Receives data, which can be extended data, as requested by the command instance. - * - * @param context of current command. - */ - public void receiveIncoming(KMContext context, APDU apdu) { + /** Receives data, which can be extended data, as requested by the command instance. */ + private short receiveIncoming(byte[] destBuffer, APDU apdu) { // Initialize source byte[] srcBuffer = apdu.getBuffer(); // Initialize destination - byte[] destBuffer = context.getBuffer(); short destOffset = (short) 0; - // Receive data short recvLen = apdu.setIncomingAndReceive(); short srcOffset = apdu.getOffsetCdata(); short srcLength = apdu.getIncomingLength(); if (srcLength > MAX_LENGTH) { - throw new KMException(ISO7816.SW_WRONG_LENGTH); + ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); } while (recvLen > 0) { Util.arrayCopyNonAtomic(srcBuffer, srcOffset, destBuffer, destOffset, recvLen); destOffset += recvLen; recvLen = apdu.receiveBytes(srcOffset); } - // Update the Context - context.setBufferLength(srcLength); + return srcLength; } - /** - * Getter for keymaster state. - * - * @return keymasterState - current state of the applet. - */ - private byte getKeymasterState() { - return keymasterState; + // Commands + private void handle(byte ins, APDU apdu) { + switch (ins) { + case INS_GENERATE_KEY_CMD: + processGenerateKey(apdu); + break; + case INS_IMPORT_KEY_CMD: + processImportKeyCmd(apdu); + break; + case INS_IMPORT_WRAPPED_KEY_CMD: + processImportWrappedKeyCmd(apdu); + break; + case INS_EXPORT_KEY_CMD: + processExportKeyCmd(apdu); + break; + case INS_ATTEST_KEY_CMD: + processAttestKeyCmd(apdu); + break; + case INS_UPGRADE_KEY_CMD: + processUpgradeKeyCmd(apdu); + break; + case INS_DELETE_KEY_CMD: + processDeleteKeyCmd(apdu); + break; + case INS_DELETE_ALL_KEYS_CMD: + processDeleteAllKeysCmd(apdu); + break; + case INS_ADD_RNG_ENTROPY_CMD: + processAddRngEntropyCmd(apdu); + break; + case INS_COMPUTE_SHARED_HMAC_CMD: + processComputeSharedHmacCmd(apdu); + break; + case INS_DESTROY_ATT_IDS_CMD: + processDestroyAttIdsCmd(apdu); + break; + case INS_VERIFY_AUTHORIZATION_CMD: + processVerifyAuthenticationCmd(apdu); + break; + case INS_GET_HMAC_SHARING_PARAM_CMD: + processGetHmacSharingParamCmd(apdu); + break; + case INS_GET_KEY_CHARACTERISTICS_CMD: + processGetKeyCharacteristicsCmd(apdu); + break; + case INS_GET_HW_INFO_CMD: + processGetHwInfoCmd(apdu); + break; + case INS_BEGIN_OPERATION_CMD: + processBeginOperationCmd(apdu); + break; + case INS_UPDATE_OPERATION_CMD: + processUpdateOperationCmd(apdu); + break; + case INS_FINISH_OPERATION_CMD: + processFinishOperationCmd(apdu); + break; + case INS_ABORT_OPERATION_CMD: + processAbortOperationCmd(apdu); + break; + case INS_PROVISION_CMD: + processProvisionCmd(apdu); + break; + default: + ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED); + } } - /** Setter for keymaster state. */ - private void setKeymasterState(byte keymasterState) { - this.keymasterState = keymasterState; + + private short processProvisionCmd(APDU apdu) { + // Receive the incoming request fully from the master. + short length = receiveIncoming(buffer, apdu); + // Re-purpose the apdu buffer as scratch pad. + byte[] scratchPad = apdu.getBuffer(); + Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) apdu.getBuffer().length, (byte) 0); + // Argument 1 + KMKeyParameters keyparams = KMKeyParameters.instance(); + // Argument 2 + KMEnum keyFormat = KMEnum.instance().setType(KMType.KEY_FORMAT); + // Argument 3 + KMByteBlob keyBlob = KMByteBlob.instance(); + // Array of expected arguments + KMArray argsProto = + KMArray.instance((short) 3) + .add((short) 0, keyparams) + .add((short) 1, keyFormat) + .add((short) 2, keyBlob); + // Decode the argument + KMArray args = decoder.decode(argsProto, buffer, (short) 0, length); + // TODO execute the function + // Change the state to ACTIVE + if (keymasterState == KMKeymasterApplet.FIRST_SELECT_STATE) { + keymasterState = KMKeymasterApplet.ACTIVE_STATE; + } + // nothing to return + return 0; + } + + private void processGetHwInfoCmd(APDU apdu) { + // No arguments expected + // Make the response + KMArray resp = + KMArray.instance((short) 3) + .add((short) 0, KMEnum.instance(KMType.HARDWARE_TYPE, KMType.STRONGBOX)) + .add( + (short) 1, + KMByteBlob.instance( + JavacardKeymasterDevice, (short) 0, (short) JavacardKeymasterDevice.length)) + .add((short) 2, KMByteBlob.instance(Google, (short) 0, (short) Google.length)); + // Reuse the buffer + Util.arrayFillNonAtomic(buffer, (short) 0, MAX_LENGTH, (byte) 0); + // Encode the response + short len = encoder.encode(resp, buffer, (short) 0, MAX_LENGTH); + sendOutgoing(buffer, len, apdu); + } + + private short processAddRngEntropyCmd(APDU apdu) { + // Receive the incoming request fully from the master. + short length = receiveIncoming(buffer, apdu); + // Re-purpose the apdu buffer as scratch pad. + byte[] scratchPad = apdu.getBuffer(); + Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) apdu.getBuffer().length, (byte) 0); + // Argument 1 + KMArray argsProto = KMArray.instance((short) 1).add((short) 0, KMByteBlob.instance()); + + // Decode the argument + KMArray args = decoder.decode(argsProto, buffer, (short) 0, length); + // Process + KMByteBlob blob = (KMByteBlob) args.get((short) 0); + // Maximum 2KiB of seed is allowed. + if (blob.length() > MAX_SEED_SIZE) { + ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); + } + // Get existing entropy pool. + byte[] entPool = KMUtil.getEntropyPool(); + // Create new temporary pool. + byte[] heapRef = repository.getByteHeapRef(); + short poolStart = repository.newByteArray((short) entPool.length); + // Populate the new pool with the entropy which is derived from current entropy pool. + KMUtil.newRandomNumber(heapRef, poolStart, (short) entPool.length); + // Copy the entropy to the current pool - updates the entropy pool. + Util.arrayCopy(heapRef, poolStart, entPool, (short) 0, (short) entPool.length); + short index = 0; + short randIndex = 0; + // Mix (XOR) the seed received from the master in the entropy pool - 32 bytes (entPool.length). + // at a time. + while (index < blob.length()) { + entPool[randIndex] = (byte) (entPool[randIndex] ^ blob.get(index)); + randIndex++; + index++; + if (randIndex >= entPool.length) { + randIndex = 0; + } + } + // Nothing to return + return 0; + } + + private short processAbortOperationCmd(APDU apdu) { + return 0; + } + + private short processFinishOperationCmd(APDU apdu) { + return 0; + } + + private short processUpdateOperationCmd(APDU apdu) { + return 0; + } + + private short processBeginOperationCmd(APDU apdu) { + return 0; + } + + private short processGetKeyCharacteristicsCmd(APDU apdu) { + return 0; + } + + private short processGetHmacSharingParamCmd(APDU apdu) { + return 0; + } + + private short processVerifyAuthenticationCmd(APDU apdu) { + return 0; + } + + private short processDestroyAttIdsCmd(APDU apdu) { + return 0; + } + + private short processComputeSharedHmacCmd(APDU apdu) { + return 0; + } + + private short processDeleteAllKeysCmd(APDU apdu) { + return 0; + } + + private short processDeleteKeyCmd(APDU apdu) { + return 0; + } + + private short processUpgradeKeyCmd(APDU apdu) { + return 0; + } + + private short processAttestKeyCmd(APDU apdu) { + return 0; + } + + private short processExportKeyCmd(APDU apdu) { + return 0; + } + + private short processImportWrappedKeyCmd(APDU apdu) { + return 0; + } + + private short processImportKeyCmd(APDU apdu) { + return 0; + } + + private short processGenerateKey(APDU apdu) { + return 0; } } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMMessenger.java b/Applet/Applet/src/com/android/javacard/keymaster/KMMessenger.java deleted file mode 100644 index 3eb4b548..00000000 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMMessenger.java +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright(C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.javacard.keymaster; - -public interface KMMessenger { - void receiveIncoming(KMContext context); - void sendOutgoing(KMContext context); -} diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMOperationState.java b/Applet/Applet/src/com/android/javacard/keymaster/KMOperationState.java index 5aaa9948..d1671328 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMOperationState.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMOperationState.java @@ -23,12 +23,12 @@ public class KMOperationState { private KMOperationState() { operationHandle = null; } - +/* public static KMOperationState instance(KMContext context) { // TODO make operation handle return context.getRepository().newOperationState(); } - +*/ public static void create(KMOperationState[] opStateRefTable) { byte index = 0; while (index < opStateRefTable.length) { @@ -44,9 +44,11 @@ public KMInteger getOperationHandle() { public void setOperationHandle(KMInteger operationHandle) { this.operationHandle = operationHandle; } - +/* public void release(KMContext context) { // TODO release handle context.getRepository().releaseOperationState(this); } + + */ } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMProvisionCmd.java b/Applet/Applet/src/com/android/javacard/keymaster/KMProvisionCmd.java deleted file mode 100644 index d1911f7e..00000000 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMProvisionCmd.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright(C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.javacard.keymaster; - -public class KMProvisionCmd extends KMAbstractCmd { - public static final byte INS_PROVISION_CMD = 0x23; - - @Override - public byte getIns() { - return INS_PROVISION_CMD; - } - - @Override - public KMArray process(KMArray args, KMContext context) { - KMKeyParameters arg1 = (KMKeyParameters)args.get((short)0); - KMEnum arg2 = (KMEnum)args.get((short)1); - KMByteBlob arg3 = (KMByteBlob)args.get((short)2); - provision(arg1, arg2.getVal(),arg3); - context.setKeymasterState(KMKeymasterApplet.ACTIVE_STATE); - //nothing to return - return null; - } - - // TODO implement functionality - private void provision(KMKeyParameters params, byte keyFormat, KMByteBlob keyBlob){ - } - - @Override - protected boolean validateState(byte state) { - return (KMKeymasterApplet.FIRST_SELECT_STATE == state); - } - - // Uses import key command signature but does not return anything back. - protected KMArray getExpectedArgs() { - // Argument 1 - KMKeyParameters keyparams = KMKeyParameters.instance(); - // Argument 2 - KMEnum keyFormat = KMEnum.instance().setType(KMType.KEY_FORMAT); - // Argument 3 - KMByteBlob keyBlob = KMByteBlob.instance(); - // Array of expected arguments - return KMArray.instance((short) 3) - .add((short) 0, keyparams) - .add((short) 1, keyFormat) - .add((short) 2, keyBlob); - } -} diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMRepository.java b/Applet/Applet/src/com/android/javacard/keymaster/KMRepository.java index 36d8f6f7..b71aea01 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMRepository.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMRepository.java @@ -17,6 +17,7 @@ package com.android.javacard.keymaster; import javacard.framework.ISO7816; +import javacard.framework.ISOException; import javacard.framework.JCSystem; import javacard.framework.Util; import javacard.security.AESKey; @@ -27,20 +28,14 @@ // to handle onInstall and onSelect. public class KMRepository { - private static final byte CMD_TABLE_LENGTH = 20; private static final byte REF_TABLE_SIZE = 5; private static final short HEAP_SIZE = 0x1000; private static final byte INT_TABLE_SIZE = 10; private static final byte TYPE_ARRAY_SIZE = 100; private static final byte INT_SIZE = 4; private static final byte LONG_SIZE = 8; - private static final short ENTROPY_POOL_SIZE = 32; - - private KMCommand[] commandTable = null; - private KMContext context = null; - private byte[] buffer = null; + private static KMRepository repository; private AESKey masterKey = null; - private boolean contextLocked = false; private KMEncoder encoder = null; private KMDecoder decoder = null; @@ -91,38 +86,23 @@ public class KMRepository { private byte[] entropyPool = null; private byte[] counter; - public void initialize() { + public static KMRepository instance() { + if (repository == null) { + repository = new KMRepository(); + } + return repository; + } + + public KMRepository(){ + initialize(); + } + + private void initialize() { // Initialize buffers and context. JCSystem.beginTransaction(); encoder = new KMEncoder(); decoder = new KMDecoder(); - buffer = new byte[KMKeymasterApplet.MAX_LENGTH]; - context = new KMContext(); - context.setRepository(this); - contextLocked = false; operationStateTable = new KMOperationState[4]; - // Initialize command table. - commandTable = new KMCommand[CMD_TABLE_LENGTH]; - commandTable[0] = new KMProvisionCmd(); - commandTable[1] = new KMGenerateKeyCmd(); - commandTable[2] = new KMImportKeyCmd(); - commandTable[3] = new KMExportKeyCmd(); - commandTable[4] = new KMComputeSharedHmacCmd(); - commandTable[5] = new KMBeginOperationCmd(); - commandTable[6] = new KMUpdateOperationCmd(); - commandTable[7] = new KMFinishOperationCmd(); - commandTable[8] = new KMAbortOperationCmd(); - commandTable[9] = new KMVerifyAuthorizationCmd(); - commandTable[10] = new KMAddRngEntropyCmd(); - commandTable[11] = new KMImportWrappedKeyCmd(); - commandTable[12] = new KMAttestKeyCmd(); - commandTable[13] = new KMUpgradeKeyCmd(); - commandTable[14] = new KMDeleteKeyCmd(); - commandTable[15] = new KMDeleteAllKeysCmd(); - commandTable[16] = new KMDestroyAttestationIdsCmd(); - commandTable[17] = new KMGetHWInfoCmd(); - commandTable[18] = new KMGetKeyCharacteristicsCmd(); - commandTable[19] = new KMGetHmacSharingParametersCmd(); // Initialize masterkey - AES 256 bit key. if (masterKey == null) { masterKey = @@ -176,8 +156,6 @@ public void initialize() { uint64Array[index] = new byte[LONG_SIZE]; index++; } - entropyPool = new byte[ENTROPY_POOL_SIZE]; - counter = new byte[8]; JCSystem.commitTransaction(); } @@ -190,26 +168,6 @@ public KMDecoder getDecoder() { return decoder; } - public KMCommand getCommand(byte ins) throws KMException { - short cmdIndex = 0; - while (cmdIndex < CMD_TABLE_LENGTH) { - if (commandTable[cmdIndex].getIns() == ins) { - return commandTable[cmdIndex]; - } - cmdIndex++; - } - throw new KMException(ISO7816.SW_INS_NOT_SUPPORTED); - } - - public KMContext getContext() throws KMException { - if (!contextLocked) { - contextLocked = true; - return context; - } else { - throw new KMException(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - } - public void onUninstall() { masterKey = null; } @@ -219,11 +177,8 @@ public void onProcess() { } private void reset() { - contextLocked = false; - Util.arrayFillNonAtomic(buffer, (short) 0, (short) buffer.length, (byte) 0); - Util.arrayFillNonAtomic(byteHeap, (short) 0, (short) buffer.length, (byte) 0); + Util.arrayFillNonAtomic(byteHeap, (short) 0, (short) byteHeap.length, (byte) 0); byteHeapIndex = 0; - Util.arrayFillNonAtomic(buffer, (short) 0, (short) buffer.length, (byte) 0); short index = 0; while (index < typeRefTable.length) { typeRefTable[index] = null; @@ -285,10 +240,6 @@ public void onSelect() { // Nothing to be done currently. } - public byte[] getBuffer() { - return buffer; - } - public AESKey getMasterKey() { return masterKey; } @@ -298,7 +249,7 @@ public byte[] newIntegerArray(short length) { if (length == 4) { if (uint32Index >= uint32Array.length) { // TODO this is placeholder exception value. This needs to be replaced by 910E, 91A1 or 9210 - throw new KMException(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); } byte[] ret = (byte[]) uint32Array[uint32Index]; uint32Index++; @@ -306,20 +257,21 @@ public byte[] newIntegerArray(short length) { } else if (length == 8) { if (uint64Index >= uint64Array.length) { // TODO this is placeholder exception value. This needs to be replaced by 910E, 91A1 or 9210 - throw new KMException(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); } byte[] ret = (byte[]) uint64Array[uint64Index]; uint64Index++; return ret; } else { - throw new KMException(ISO7816.SW_WRONG_LENGTH); + ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); } + return null;// this will never be executed. } public KMByteBlob newByteBlob() { if (blobRefIndex >= byteBlobRefTable.length) { // TODO this is placeholder exception value. - throw new KMException(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); } KMByteBlob ret = byteBlobRefTable[blobRefIndex]; blobRefIndex++; @@ -329,7 +281,7 @@ public KMByteBlob newByteBlob() { public KMInteger newInteger() { if (intRefIndex >= integerRefTable.length) { // TODO this is placeholder exception value. - throw new KMException(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); } KMInteger ret = integerRefTable[intRefIndex]; intRefIndex++; @@ -339,7 +291,7 @@ public KMInteger newInteger() { public KMEnumTag newEnumTag() { if (enumTagRefIndex >= enumTagRefTable.length) { // TODO this is placeholder exception value. - throw new KMException(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); } KMEnumTag ret = enumTagRefTable[enumTagRefIndex]; enumTagRefIndex++; @@ -349,7 +301,7 @@ public KMEnumTag newEnumTag() { public KMEnumArrayTag newEnumArrayTag() { if (enumArrayTagRefIndex >= enumArrayTagRefTable.length) { // TODO this is placeholder exception value. - throw new KMException(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); } KMEnumArrayTag ret = enumArrayTagRefTable[enumArrayTagRefIndex]; enumArrayTagRefIndex++; @@ -359,7 +311,7 @@ public KMEnumArrayTag newEnumArrayTag() { public KMIntegerTag newIntegerTag() { if (intTagRefIndex >= intTagRefTable.length) { // TODO this is placeholder exception value. - throw new KMException(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); } KMIntegerTag ret = intTagRefTable[intTagRefIndex]; intTagRefIndex++; @@ -369,7 +321,7 @@ public KMIntegerTag newIntegerTag() { public KMIntegerArrayTag newIntegerArrayTag() { if (intArrayTagRefIndex >= intArrayTagRefTable.length) { // TODO this is placeholder exception value. - throw new KMException(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); } KMIntegerArrayTag ret = intArrayTagRefTable[intArrayTagRefIndex]; intArrayTagRefIndex++; @@ -379,7 +331,7 @@ public KMIntegerArrayTag newIntegerArrayTag() { public KMBoolTag newBoolTag() { if (boolTagRefIndex >= boolTagRefTable.length) { // TODO this is placeholder exception value. - throw new KMException(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); } KMBoolTag ret = boolTagRefTable[boolTagRefIndex]; boolTagRefIndex++; @@ -389,7 +341,7 @@ public KMBoolTag newBoolTag() { public KMByteTag newByteTag() { if (byteTagRefIndex >= byteTagRefTable.length) { // TODO this is placeholder exception value. - throw new KMException(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); } KMByteTag ret = byteTagRefTable[byteTagRefIndex]; byteTagRefIndex++; @@ -399,7 +351,7 @@ public KMByteTag newByteTag() { public KMKeyParameters newKeyParameters() { if (keyParametersRefIndex >= keyParametersRefTable.length) { // TODO this is placeholder exception value. - throw new KMException(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); } KMKeyParameters ret = keyParametersRefTable[keyParametersRefIndex]; keyParametersRefIndex++; @@ -409,7 +361,7 @@ public KMKeyParameters newKeyParameters() { public KMArray newArray() { if (arrayRefIndex >= arrayRefTable.length) { // TODO this is placeholder exception value. - throw new KMException(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); } KMArray ret = arrayRefTable[arrayRefIndex]; arrayRefIndex++; @@ -419,7 +371,7 @@ public KMArray newArray() { public KMKeyCharacteristics newKeyCharacteristics() { if (keyCharRefIndex >= keyCharRefTable.length) { // TODO this is placeholder exception value. - throw new KMException(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); } KMKeyCharacteristics ret = keyCharRefTable[keyCharRefIndex]; keyCharRefIndex++; @@ -429,7 +381,7 @@ public KMKeyCharacteristics newKeyCharacteristics() { public KMHardwareAuthToken newHwAuthToken() { if (hwAuthTokenRefIndex >= hwAuthTokenRefTable.length) { // TODO this is placeholder exception value. - throw new KMException(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); } KMHardwareAuthToken ret = hwAuthTokenRefTable[hwAuthTokenRefIndex]; hwAuthTokenRefIndex++; @@ -439,7 +391,7 @@ public KMHardwareAuthToken newHwAuthToken() { public KMHmacSharingParameters newHmacSharingParameters() { if (hmacSharingParamsRefIndex >= hmacSharingParamsRefTable.length) { // TODO this is placeholder exception value. - throw new KMException(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); } KMHmacSharingParameters ret = hmacSharingParamsRefTable[hmacSharingParamsRefIndex]; hmacSharingParamsRefIndex++; @@ -449,7 +401,7 @@ public KMHmacSharingParameters newHmacSharingParameters() { public KMVerificationToken newVerificationToken() { if (verTokenRefIndex >= verTokenRefTable.length) { // TODO this is placeholder exception value. - throw new KMException(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); } KMVerificationToken ret = verTokenRefTable[verTokenRefIndex]; verTokenRefIndex++; @@ -459,7 +411,7 @@ public KMVerificationToken newVerificationToken() { public KMOperationState newOperationState() { if (opStateRefIndex >= opStateRefTable.length) { // TODO this is placeholder exception value. - throw new KMException(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); } KMOperationState ret = operationStateTable[opStateRefIndex]; opStateRefIndex++; @@ -469,14 +421,14 @@ public KMOperationState newOperationState() { public void releaseOperationState(KMOperationState state){ opStateRefIndex--; if(opStateRefIndex <0){ - throw new KMException(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); } opStateRefTable[opStateRefIndex] = state; } public KMVector newVector() { if (vectorRefIndex >= vectorRefTable.length) { // TODO this is placeholder exception value. - throw new KMException(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); } KMVector ret = vectorRefTable[vectorRefIndex]; vectorRefIndex++; @@ -486,7 +438,7 @@ public KMVector newVector() { public KMEnum newEnum() { if (enumRefIndex >= enumRefTable.length) { // TODO this is placeholder exception value. - throw new KMException(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); } KMEnum ret = enumRefTable[enumRefIndex]; enumRefIndex++; @@ -504,7 +456,7 @@ public byte[] getByteHeapRef(){ public short newTypeArray(short length) { if (((short) (typeRefIndex + length)) >= typeRefTable.length) { // TODO this is placeholder exception value. - throw new KMException(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); } typeRefIndex += length; return (short) (typeRefIndex - length); @@ -513,13 +465,9 @@ public short newTypeArray(short length) { public short newByteArray(short length) { if (((short) (byteHeapIndex + length)) >= byteHeap.length) { // TODO this is placeholder exception value. - throw new KMException(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); } byteHeapIndex += length; return (short) (byteHeapIndex - length); } - - public byte[] getEntropyPool() { - return entropyPool; - } } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMUpdateOperationCmd.java b/Applet/Applet/src/com/android/javacard/keymaster/KMUpdateOperationCmd.java deleted file mode 100644 index 5b1e58f6..00000000 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMUpdateOperationCmd.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright(C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.javacard.keymaster; - -public class KMUpdateOperationCmd extends KMAbstractCmd { - public static final byte INS_UPDATE_OPERATION_CMD = 0x20; - - @Override - protected KMArray getExpectedArgs() { - return null; - } - - @Override - protected KMArray process(KMArray args, KMContext context) { - return null; - } - - @Override - public byte getIns() { - return INS_UPDATE_OPERATION_CMD; - } -} diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMUpgradeKeyCmd.java b/Applet/Applet/src/com/android/javacard/keymaster/KMUpgradeKeyCmd.java deleted file mode 100644 index d883068f..00000000 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMUpgradeKeyCmd.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright(C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.javacard.keymaster; - -public class KMUpgradeKeyCmd extends KMAbstractCmd { - public static final byte INS_UPGRADE_KEY_CMD = 0x15; - - @Override - protected KMArray getExpectedArgs() { - return null; - } - - @Override - protected KMArray process(KMArray args, KMContext context) { - return null; - } - - @Override - public byte getIns() { - return INS_UPGRADE_KEY_CMD; - } -} diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMUtil.java b/Applet/Applet/src/com/android/javacard/keymaster/KMUtil.java index 5b42ae9d..0d1080df 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMUtil.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMUtil.java @@ -1,6 +1,7 @@ package com.android.javacard.keymaster; import javacard.framework.ISO7816; +import javacard.framework.ISOException; import javacard.framework.Util; import javacard.security.AESKey; import javacard.security.CryptoException; @@ -9,27 +10,25 @@ import javacardx.crypto.Cipher; public class KMUtil { + private static final short ENTROPY_POOL_SIZE = 16; // simulator does not support 256 bit aes keys public static final byte AES_BLOCK_SIZE = 16; - private static KMRepository repository = null; + public static final byte[] aesICV = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; private static byte[] counter; - private static boolean initialized = false; private static AESKey aesKey; - private static Cipher aesEcb; - - public static void init(KMRepository repo) { - if (!initialized) { - KMUtil.repository = repo; - counter = repo.newIntegerArray((short) 8); - KMUtil.initEntropyPool(repo.getEntropyPool()); + private static Cipher aesCbc; + private static byte[] entropyPool; + public static void init() { + entropyPool = new byte[ENTROPY_POOL_SIZE]; + counter = KMRepository.instance().newIntegerArray((short) 8); + KMUtil.initEntropyPool(entropyPool); try { - aesEcb = Cipher.getInstance(Cipher.ALG_AES_BLOCK_128_ECB_NOPAD, false); + //Note: ALG_AES_BLOCK_128_CBC_NOPAD not supported by simulator. + aesCbc = Cipher.getInstance(Cipher.ALG_AES_BLOCK_128_CBC_NOPAD, false); } catch (CryptoException exp) { // TODO change this to proper error code - throw new KMException(ISO7816.SW_UNKNOWN); + ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); } - aesKey = (AESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_AES, KeyBuilder.LENGTH_AES_256, false); - initialized = true; - } + aesKey = (AESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_AES, KeyBuilder.LENGTH_AES_128, false); } public static void initEntropyPool(byte[] pool) { @@ -40,26 +39,31 @@ public static void initEntropyPool(byte[] pool) { } try { trng = RandomData.getInstance(RandomData.ALG_TRNG); + trng.nextBytes(pool, (short) 0, (short) pool.length); } catch (CryptoException exp) { if (exp.getReason() == CryptoException.NO_SUCH_ALGORITHM) { //TODO change this when possible // simulator does not support TRNG algorithm. So, PRNG algorithm (deprecated) is used. trng = RandomData.getInstance(RandomData.ALG_PSEUDO_RANDOM); + trng.nextBytes(pool, (short) 0, (short) pool.length); } else { // TODO change this to proper error code - throw new KMException(ISO7816.SW_UNKNOWN); + ISOException.throwIt(ISO7816.SW_UNKNOWN); } } - trng.nextBytes(pool, (short) 0, (short) pool.length); + } // Generate a secure random number from existing entropy pool. This uses aes ecb algorithm with // 8 byte counter and 16 byte block size. public static void newRandomNumber(byte[] num, short startOff, short length) { + KMRepository repository = KMRepository.instance(); byte[] bufPtr = repository.getByteHeapRef(); short countBufInd = repository.newByteArray(AES_BLOCK_SIZE); short randBufInd = repository.newByteArray(AES_BLOCK_SIZE); short len = AES_BLOCK_SIZE; + aesKey.setKey(entropyPool, (short) 0); + aesCbc.init(aesKey, Cipher.MODE_ENCRYPT, aesICV, (short)0, (short)16); while (length > 0) { if (length < len ) len = length; // increment counter by one @@ -67,9 +71,7 @@ public static void newRandomNumber(byte[] num, short startOff, short length) { // copy the 8 byte counter into the 16 byte counter buffer. Util.arrayCopy(counter, (short) 0, bufPtr, countBufInd, (short) counter.length); // encrypt the counter buffer with existing entropy which forms the aes key. - aesKey.setKey(repository.getEntropyPool(), (short) 0); - aesEcb.init(aesKey, Cipher.MODE_ENCRYPT); - aesEcb.doFinal(bufPtr, countBufInd, AES_BLOCK_SIZE, bufPtr, randBufInd); + aesCbc.doFinal(bufPtr, countBufInd, AES_BLOCK_SIZE, bufPtr, randBufInd); // copy the encrypted counter block to buffer passed in the argument Util.arrayCopy(bufPtr, randBufInd, num, startOff, len); length = (short) (length - len); @@ -98,4 +100,9 @@ private static void incrementCounter() { } } } + + public static byte[] getEntropyPool() { + return entropyPool; + } + } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMVerificationToken.java b/Applet/Applet/src/com/android/javacard/keymaster/KMVerificationToken.java index f52fae55..d47c80e2 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMVerificationToken.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMVerificationToken.java @@ -17,6 +17,7 @@ package com.android.javacard.keymaster; import javacard.framework.ISO7816; +import javacard.framework.ISOException; public class KMVerificationToken extends KMType { public static final byte CHALLENGE = 0x00; @@ -61,7 +62,7 @@ public static KMVerificationToken instance() { public static KMVerificationToken instance(KMArray vals) { if (vals.length() != 5) { - throw new KMException(ISO7816.SW_WRONG_LENGTH); + ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); } KMVerificationToken inst = repository.newVerificationToken(); inst.vals = vals; diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMVerifyAuthorizationCmd.java b/Applet/Applet/src/com/android/javacard/keymaster/KMVerifyAuthorizationCmd.java deleted file mode 100644 index 33289b44..00000000 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMVerifyAuthorizationCmd.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright(C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.javacard.keymaster; - -public class KMVerifyAuthorizationCmd extends KMAbstractCmd { - public static final byte INS_VERIFY_AUTHORIZATION_CMD = 0x1B; - - @Override - protected KMArray getExpectedArgs() { - return null; - } - - @Override - protected KMArray process(KMArray args, KMContext context) { - return null; - } - - @Override - public byte getIns() { - return INS_VERIFY_AUTHORIZATION_CMD; - } -} From 6f9748fe084e6c5ecc9ece54014df676ea45db8b Mon Sep 17 00:00:00 2001 From: cpathak Date: Mon, 6 Apr 2020 22:45:50 -0700 Subject: [PATCH 2/6] Optimization of the code Implemented transient memory based code. --- .../android/javacard/keymaster/KMArray.java | 92 ++-- .../android/javacard/keymaster/KMBoolTag.java | 64 ++- .../javacard/keymaster/KMByteBlob.java | 110 ++--- .../android/javacard/keymaster/KMByteTag.java | 104 ++--- .../android/javacard/keymaster/KMDecoder.java | 346 +++++++------- .../android/javacard/keymaster/KMEncoder.java | 245 +++++----- .../android/javacard/keymaster/KMEnum.java | 77 ++-- .../javacard/keymaster/KMEnumArrayTag.java | 159 +++---- .../android/javacard/keymaster/KMEnumTag.java | 94 ++-- .../keymaster/KMHardwareAuthToken.java | 137 ++++-- .../keymaster/KMHmacSharingParameters.java | 86 ++-- .../android/javacard/keymaster/KMInteger.java | 148 +++--- .../javacard/keymaster/KMIntegerArrayTag.java | 141 +++--- .../javacard/keymaster/KMIntegerTag.java | 124 ++--- .../keymaster/KMKeyCharacteristics.java | 87 ++-- .../javacard/keymaster/KMKeyParameters.java | 75 +-- .../javacard/keymaster/KMKeymasterApplet.java | 329 +++++++------- .../javacard/keymaster/KMRepository.java | 426 +----------------- .../com/android/javacard/keymaster/KMTag.java | 7 +- .../android/javacard/keymaster/KMType.java | 47 +- .../android/javacard/keymaster/KMUtil.java | 11 +- .../android/javacard/keymaster/KMVector.java | 85 ---- .../keymaster/KMVerificationToken.java | 127 ++++-- .../javacard/test/KMFrameworkTest.java | 62 +-- Applet/default.output | 2 +- 25 files changed, 1428 insertions(+), 1757 deletions(-) delete mode 100644 Applet/Applet/src/com/android/javacard/keymaster/KMVector.java diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMArray.java b/Applet/Applet/src/com/android/javacard/keymaster/KMArray.java index d2eed688..04ee7cfe 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMArray.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMArray.java @@ -18,66 +18,76 @@ import javacard.framework.ISO7816; import javacard.framework.ISOException; +import javacard.framework.Util; public class KMArray extends KMType { - private KMType[] vals; - private short length; - private short startOff; + private static final short ARRAY_HEADER_SIZE = 3; + private static KMArray prototype; + private static short instPtr; - private KMArray() { - init(); + private KMArray() {} + + private static KMArray proto(short ptr) { + if (prototype == null) prototype = new KMArray(); + instPtr = ptr; + return prototype; } - @Override - public void init() { - vals = null; - startOff = 0; - length = 0; + public static short exp() { + short ptr = instance(ARRAY_TYPE, ARRAY_HEADER_SIZE); + heap[(short)(ptr + TLV_HEADER_SIZE)] = 0; + Util.setShort(heap,(short)(ptr + TLV_HEADER_SIZE + 1),(short)0 ); + return ptr; } - @Override - public short length() { - return length; + public static short exp(byte type) { + short ptr = instance(ARRAY_TYPE, ARRAY_HEADER_SIZE); + heap[(short)(ptr + TLV_HEADER_SIZE)] = type; + Util.setShort(heap,(short)(ptr + TLV_HEADER_SIZE + 1),(short)0 ); + return ptr; } - public static void create(KMArray[] arrayRefTable) { - byte index = 0; - while (index < arrayRefTable.length) { - arrayRefTable[index] = new KMArray(); - index++; - } + public static short instance(short length) { + short ptr = KMType.instance(ARRAY_TYPE, (short)(ARRAY_HEADER_SIZE + (length*2))); + heap[(short)(ptr + TLV_HEADER_SIZE)] = 0; + Util.setShort(heap,(short)(ptr + TLV_HEADER_SIZE + 1),length); + return ptr; } - public static KMArray instance() { - return repository.newArray(); + public static short instance(short length, byte type) { + short ptr = instance(length); + heap[(short)(ptr + TLV_HEADER_SIZE)] = type; + return ptr; } - public static KMArray instance(short length) { + public static KMArray cast(short ptr) { + if (heap[ptr] != ARRAY_TYPE) ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + return proto(ptr); + } - KMArray inst = repository.newArray(); - inst.startOff = repository.newTypeArray(length); - inst.vals = repository.getTypeArrayRef(); - inst.length = length; - return inst; + public void add(short index, short objPtr) { + short len = length(); + if (index >= len) ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); + Util.setShort(heap, (short) (instPtr + TLV_HEADER_SIZE + ARRAY_HEADER_SIZE + (short)(index*2)), objPtr) ; } - public KMArray withLength(short length) { - this.length = length; - return this; + public short get(short index) { + short len = length(); + if (index >= len) ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); + return Util.getShort(heap,(short) (instPtr + TLV_HEADER_SIZE + 3 + (short)(index*2))); } - public KMArray add(short index, KMType val) { - if (index >= length) { - ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); - } - vals[(short) (startOff + index)] = val; - return this; + public byte containedType(){ return heap[(short)(instPtr + TLV_HEADER_SIZE)];} + + public short getStartOff() { + return (short) (instPtr + TLV_HEADER_SIZE + 3); + } + + public short length() { + return Util.getShort(heap, (short) (instPtr + TLV_HEADER_SIZE + 1)); } - public KMType get(short index) { - if (index >= length) { - ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); - } - return vals[(short) (startOff + index)]; + public byte[] getBuffer() { + return heap; } } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMBoolTag.java b/Applet/Applet/src/com/android/javacard/keymaster/KMBoolTag.java index 2e088893..5154cd44 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMBoolTag.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMBoolTag.java @@ -18,8 +18,11 @@ import javacard.framework.ISO7816; import javacard.framework.ISOException; +import javacard.framework.Util; public class KMBoolTag extends KMTag { + private static KMBoolTag prototype; + private static short instPtr; private static final short[] tags = { CALLER_NONCE, @@ -34,59 +37,50 @@ public class KMBoolTag extends KMTag { RESET_SINCE_ID_ROTATION }; - // Array of Tag Values. - private short key; - private byte val; + private KMBoolTag() {} - // assignBlob constructor - private KMBoolTag() { - init(); + private static KMBoolTag proto(short ptr) { + if (prototype == null) prototype = new KMBoolTag(); + instPtr = ptr; + return prototype; } - @Override - public void init() { - key = 0; - val = 1; // always 1. + // pointer to an empty instance used as expression + public static short exp() { + short ptr = KMType.exp(TAG_TYPE); + Util.setShort(heap, (short)(ptr+1), BOOL_TAG); + return ptr; } - public static KMBoolTag instance() { - return repository.newBoolTag(); + public static short instance(short key) { + if (!validateKey(key)) { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + short ptr = KMType.instance(TAG_TYPE, (short)5); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE), BOOL_TAG); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE+2), key); + heap[(short)(ptr+TLV_HEADER_SIZE+4)] = 0x01; + return ptr; } - public static void create(KMBoolTag[] boolTagRefTable) { - byte index = 0; - while (index < boolTagRefTable.length) { - boolTagRefTable[index] = new KMBoolTag(); - index++; + public static KMBoolTag cast(short ptr) { + if (heap[ptr] != TAG_TYPE) ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + if (Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE)) != BOOL_TAG) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); } + return proto(ptr); } - @Override public short getKey() { - return key; - } - - @Override - public short length() { - return 1; + return Util.getShort(heap, (short)(instPtr+TLV_HEADER_SIZE+2)); } - @Override public short getTagType() { return KMType.BOOL_TAG; } public byte getVal() { - return val; - } - // create default assignBlob without any value - public static KMBoolTag instance(short key) { - if (!validateKey(key)) { - ISOException.throwIt(ISO7816.SW_DATA_INVALID); - } - KMBoolTag tag = repository.newBoolTag(); - tag.key = key; - return tag; + return heap[(short)(instPtr+TLV_HEADER_SIZE+4)]; } // validate the tag key diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMByteBlob.java b/Applet/Applet/src/com/android/javacard/keymaster/KMByteBlob.java index 54b0e9e4..a112ce6c 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMByteBlob.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMByteBlob.java @@ -20,93 +20,71 @@ import javacard.framework.ISOException; import javacard.framework.Util; -// Byte val represents contiguous memory buffer. +// Byte blob represents contiguous memory buffer. public class KMByteBlob extends KMType { - private byte[] val; - private short startOff; - private short length; + private static KMByteBlob prototype; + private static short instPtr; - private KMByteBlob() { - init(); - } + private KMByteBlob() {} - @Override - public void init() { - length = 0; - startOff = 0; - val = null; + private static KMByteBlob proto(short ptr) { + if (prototype == null) prototype = new KMByteBlob(); + instPtr = ptr; + return prototype; } - @Override - public short length() { - return length; + // pointer to an empty instance used as expression + public static short exp() { + return KMType.exp(BYTE_BLOB_TYPE); } - public static KMByteBlob instance() { - return repository.newByteBlob(); + // return an empty byte blob instance + public static short instance(short length) { + return KMType.instance(BYTE_BLOB_TYPE, length); } - // copy the blob - public static KMByteBlob instance(byte[] blob, short startOff, short length) { - if ((length <= 0) || ((short)(startOff+length) > blob.length)) { - ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); - } - KMByteBlob inst = instance(length); - Util.arrayCopyNonAtomic(blob, startOff, inst.val, inst.startOff, inst.length); - return inst; + // byte blob from existing buf + public static short instance(byte[] buf, short startOff, short length) { + short ptr = instance(length); + Util.arrayCopyNonAtomic(buf, startOff, heap, (short) (ptr + TLV_HEADER_SIZE), length); + return ptr; } - // returns empty blob with given length - public static KMByteBlob instance(short length) { - if (length <= 0) { - ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); + // cast the ptr to KMByteBlob + public static KMByteBlob cast(short ptr) { + if (heap[ptr] != BYTE_BLOB_TYPE) ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + if (Util.getShort(heap, (short) (ptr + 1)) == INVALID_VALUE) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); } - KMByteBlob inst = instance(); - inst.startOff = repository.newByteArray(length); - inst.val = repository.getByteHeapRef(); - inst.length = length; - return inst; - } - - public static void create(KMByteBlob[] byteBlobRefTable) { - byte index = 0; - while (index < byteBlobRefTable.length) { - byteBlobRefTable[index] = new KMByteBlob(); - index++; - } - } - - // sets the expected length for prototype byte val. - public KMByteBlob withLength(short len) { - this.length = len; - return this; + return proto(ptr); } + // Add the byte public void add(short index, byte val) { - if (index >= this.length) { - ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); - } - if (this.val == null) { - ISOException.throwIt(ISO7816.SW_DATA_INVALID); - } - this.val[(short) (startOff + index)] = val; + short len = length(); + if (index >= len) ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); + heap[(short) (instPtr + TLV_HEADER_SIZE + index)] = val; } + // Get the byte public byte get(short index) { - if (index >= this.length) { - ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); - } - if (this.val == null) { - ISOException.throwIt(ISO7816.SW_DATA_INVALID); - } - return this.val[(short) (startOff + index)]; + short len = length(); + if (index >= len) ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); + return heap[(short) (instPtr + TLV_HEADER_SIZE + index)]; + } + + // Get the start of blob + public short getStartOff() { + return (short) (instPtr + TLV_HEADER_SIZE); } - public byte[] getVal() { - return val; + // Get the length of the blob + public short length() { + return Util.getShort(heap, (short) (instPtr + 1)); } - public short getStartOff() { - return startOff; + // Get the buffer pointer in which blob is contained. + public byte[] getBuffer() { + return heap; } } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMByteTag.java b/Applet/Applet/src/com/android/javacard/keymaster/KMByteTag.java index 1569394c..6740e9ba 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMByteTag.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMByteTag.java @@ -18,8 +18,11 @@ import javacard.framework.ISO7816; import javacard.framework.ISOException; +import javacard.framework.Util; public class KMByteTag extends KMTag { + private static KMByteTag prototype; + private static short instPtr; private static final short[] tags = { APPLICATION_ID, @@ -41,70 +44,68 @@ public class KMByteTag extends KMTag { CONFIRMATION_TOKEN }; - private short key; - private KMByteBlob val; + private KMByteTag() {} - private KMByteTag() { - init(); + private static KMByteTag proto(short ptr) { + if (prototype == null) prototype = new KMByteTag(); + instPtr = ptr; + return prototype; } - @Override - public void init() { - key = 0; - val = null; + // pointer to an empty instance used as expression + public static short exp() { + short blobPtr = KMByteBlob.exp(); + short ptr = instance(TAG_TYPE, (short)6); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE), BYTES_TAG); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE+2), INVALID_TAG); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE+4), blobPtr); + return ptr; } - @Override - public short getKey() { - return key; - } - - @Override - public short length() { - return val.length(); - } - - @Override - public short getTagType() { - return KMType.BYTES_TAG; - } - - public static KMByteTag instance() { - return repository.newByteTag(); + public static short instance(short key) { + if (!validateKey(key)) { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + return instance(key, KMByteBlob.exp()); } - public static KMByteTag instance(short key) { + public static short instance(short key, short byteBlob) { if (!validateKey(key)) { ISOException.throwIt(ISO7816.SW_DATA_INVALID); } - KMByteTag tag = repository.newByteTag(); - tag.key = key; - tag.val = null; - return tag; + if(heap[byteBlob] != BYTE_BLOB_TYPE) { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + short ptr = instance(TAG_TYPE, (short)6); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE), BYTES_TAG); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE+2), key); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE+4), byteBlob); + return ptr; } - public static void create(KMByteTag[] byteTagRefTable) { - byte index = 0; - while (index < byteTagRefTable.length) { - byteTagRefTable[index] = new KMByteTag(); - index++; + public static KMByteTag cast(short ptr) { + if (heap[ptr] != TAG_TYPE) ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + if (Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE)) != BYTES_TAG) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); } + return proto(ptr); } - // create default assignBlob without any value - public static KMByteTag instance(short key, KMByteBlob array) { - if (!validateKey(key)) { - ISOException.throwIt(ISO7816.SW_DATA_INVALID); - } - KMByteTag tag = repository.newByteTag(); - tag.key = key; - tag.val = array; - return tag; + public short getKey() { + return Util.getShort(heap, (short)(instPtr+TLV_HEADER_SIZE+2)); + } + + public short getTagType() { + return KMType.BYTES_TAG; } - public KMByteTag withLength(short length) { - this.val.withLength(length); - return this; + public short getValue() { + return Util.getShort(heap, (short)(instPtr+TLV_HEADER_SIZE+4)); + } + + public short length() { + short blobPtr = Util.getShort(heap, (short)(instPtr+TLV_HEADER_SIZE+4)); + return KMByteBlob.cast(blobPtr).length(); } private static boolean validateKey(short key) { @@ -116,13 +117,4 @@ private static boolean validateKey(short key) { } return false; } - - public KMByteBlob getValue() { - return val; - } - - public KMByteTag setValue(KMByteBlob val) { - this.val = val; - return this; - } } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMDecoder.java b/Applet/Applet/src/com/android/javacard/keymaster/KMDecoder.java index a1c43aa3..b4679a3c 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMDecoder.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMDecoder.java @@ -19,7 +19,7 @@ import javacard.framework.ISO7816; import javacard.framework.ISOException; import javacard.framework.Util; -// TODO Clean and refactor the code. + public class KMDecoder { // major types private static final short UINT_TYPE = 0x00; @@ -37,7 +37,6 @@ public class KMDecoder { private static final short UINT32_LENGTH = 0x1A; private static final short UINT64_LENGTH = 0x1B; - // TODO move the following to transient memory. private byte[] buffer; private short startOff; private short length; @@ -50,58 +49,176 @@ public KMDecoder() { length = 0; } - public KMArray decode(KMArray expression, byte[] buffer, short startOff, short length) { + public short decode(short expression, byte[] buffer, short startOff, short length) { this.buffer = buffer; this.startOff = startOff; - this.length = length; + this.length = (short)(startOff+length); return decode(expression); } - private KMEnumArrayTag decode(KMEnumArrayTag exp) { - readTagKey(exp.getTagType()); + private short decode(short exp){ + byte type = KMType.getType(exp); + switch(type){ + case KMType.BYTE_BLOB_TYPE: + return decodeByteBlob(exp); + case KMType.INTEGER_TYPE: + return decodeInteger(exp); + case KMType.ARRAY_TYPE: + return decodeArray(exp); + case KMType.ENUM_TYPE: + return decodeEnum(exp); + case KMType.KEY_PARAM_TYPE: + return decodeKeyParam(exp); + case KMType.KEY_CHAR_TYPE: + return decodeKeyChar(exp); + case KMType.VERIFICATION_TOKEN_TYPE: + return decodeVeriToken(exp); + case KMType.HMAC_SHARING_PARAM_TYPE: + return decodeHmacSharingParam(exp); + case KMType.HW_AUTH_TOKEN_TYPE: + return decodeHwAuthToken(exp); + case KMType.TAG_TYPE: + short tagType = KMTag.getTagType(exp); + return decodeTag(tagType, exp); + default: + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + return 0; + } + } + private short decodeTag(short tagType, short exp){ + switch(tagType){ + case KMType.BYTES_TAG: + return decodeBytesTag(exp); + case KMType.BOOL_TAG: + return decodeBoolTag(exp); + case KMType.UINT_TAG: + case KMType.ULONG_TAG: + case KMType.DATE_TAG: + return decodeIntegerTag(exp); + case KMType.ULONG_ARRAY_TAG: + case KMType.UINT_ARRAY_TAG: + return decodeIntegerArrayTag(exp); + case KMType.ENUM_TAG: + return decodeEnumTag(exp); + case KMType.ENUM_ARRAY_TAG: + return decodeEnumArrayTag(exp); + default: + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + return 0; + } + } + + private short decodeVeriToken(short exp) { + short vals = decode(KMVerificationToken.cast(exp).getVals()); + return KMVerificationToken.instance(vals); + } + + private short decodeHwAuthToken(short exp) { + short vals = decode(KMHardwareAuthToken.cast(exp).getVals()); + return KMHardwareAuthToken.cast(exp).instance(vals); + } + + private short decodeHmacSharingParam(short exp) { + short vals = decode(KMHmacSharingParameters.cast(exp).getVals()); + return KMHmacSharingParameters.instance(vals); + } + + private short decodeKeyChar(short exp) { + short vals = decode(KMKeyCharacteristics.cast(exp).getVals()); + return KMKeyCharacteristics.instance(vals); + } + + private short decodeKeyParam(short exp) { + short payloadLength = readMajorTypeWithPayloadLength(MAP_TYPE); + // allowed tags + short allowedTags = KMKeyParameters.cast(exp).getVals(); + short vals = KMArray.instance(payloadLength); + short length = KMArray.cast(allowedTags).length(); + short index = 0; + boolean tagFound; + short tagInd; + short tagType; + // For each tag in payload ... + while (index < payloadLength) { + tagFound = false; + tagInd = 0; + tagType = peekTagType(); + // Check against the allowed tags ... + while (tagInd < length) { + short tagClass = KMArray.cast(allowedTags).get(tagInd); + short allowedType = KMTag.getTagType(tagClass); + // If it is part of allowed tags ... + if (tagType == allowedType) { + // then decodeByteBlob and add that to the array. + KMArray.cast(vals).add(index, decode(tagClass)); + tagFound = true; + break; + } + tagInd++; + } + if(!tagFound){ + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + else { + index++; + } + } + return KMKeyParameters.instance(vals); + } + + private short decodeEnumArrayTag(short exp) { + readTagKey(KMEnumArrayTag.cast(exp).getTagType()); // The value must be byte blob // TODO check this out. - return exp.instance(this.tagKey, decode(exp.getValues())); + return KMEnumArrayTag.instance(this.tagKey, decode(KMEnumArrayTag.cast(exp).getValues())); } - private KMIntegerArrayTag decode(KMIntegerArrayTag exp) { - readTagKey(exp.getTagType()); + private short decodeIntegerArrayTag(short exp) { + readTagKey(KMIntegerArrayTag.cast(exp).getTagType()); // the values are array of integers. - if (!(exp.getValues().getType() instanceof KMInteger)) { - ISOException.throwIt(ISO7816.SW_DATA_INVALID); - } - return exp.instance(this.tagKey, decode(exp.getValues(), (KMInteger) exp.getValues().getType())); + return KMIntegerArrayTag.instance(KMIntegerArrayTag.cast(exp).getTagType(), + this.tagKey, decode(KMIntegerArrayTag.cast(exp).getValues())); } - private KMIntegerTag decode(KMIntegerTag exp) { - readTagKey(exp.getTagType()); + private short decodeIntegerTag(short exp) { + readTagKey(KMIntegerTag.cast(exp).getTagType()); // the value is an integer - return exp.instance(this.tagKey, decode(exp.getValue())); + return KMIntegerTag.instance(KMIntegerTag.cast(exp).getTagType(), + this.tagKey, decode(KMIntegerTag.cast(exp).getValue())); } - private KMByteTag decode(KMByteTag exp) { - short key = 0; - readTagKey(exp.getTagType()); + private short decodeBytesTag(short exp) { + readTagKey(KMByteTag.cast(exp).getTagType()); // The value must be byte blob - return exp.instance(this.tagKey, decode(exp.getValue())); + return KMByteTag.instance(this.tagKey, decode(KMByteTag.cast(exp).getValue())); } - private KMBoolTag decode(KMBoolTag exp) { - readTagKey(exp.getTagType()); - // BOOL Tag is a leaf node and it must always have tiny encoded uint value = 1. - // TODO check this out. - if ((buffer[startOff] & MAJOR_TYPE_MASK) != UINT_TYPE) { - ISOException.throwIt(ISO7816.SW_DATA_INVALID); + private short decodeArray(short exp) { + short payloadLength = readMajorTypeWithPayloadLength(ARRAY_TYPE); + if (KMArray.cast(exp).length() != payloadLength) { + ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); } - if ((byte) (buffer[startOff] & ADDITIONAL_MASK) != 0x01) { - ISOException.throwIt(ISO7816.SW_DATA_INVALID); + short arrPtr = KMArray.cast(exp).instance(payloadLength); + short index = 0; + // check whether array contains one type of objects or multiple types + if( KMArray.cast(exp).containedType() == 0){// multiple types specified by expression. + while (index < payloadLength) { + short type = KMArray.cast(exp).get(index); + KMArray.cast(arrPtr).add(index, decode(type)); + index++; + } + }else{ // Array is a Vector containing objects of one type + short type = KMArray.cast(exp).containedType(); + while(index < payloadLength){ + KMArray.cast(arrPtr).add(index, decode(type)); + index++; + } } - incrementStartOff((short) 1); - return exp.instance(tagKey); + return arrPtr; } - private KMEnumTag decode(KMEnumTag exp) { - readTagKey(exp.getTagType()); + private short decodeEnumTag(short exp) { + readTagKey(KMEnumTag.cast(exp).getTagType()); // Enum Tag value will always be integer with max 1 byte length. // TODO Check this out. if ((buffer[startOff] & MAJOR_TYPE_MASK) != UINT_TYPE) { @@ -120,17 +237,30 @@ private KMEnumTag decode(KMEnumTag exp) { enumVal = buffer[startOff]; incrementStartOff((short) 1); } - return exp.instance(tagKey, enumVal); + return KMEnumTag.instance(tagKey, enumVal); } - private KMEnum decode(KMEnum exp) { + private short decodeBoolTag(short exp) { + readTagKey(KMBoolTag.cast(exp).getTagType()); + // BOOL Tag is a leaf node and it must always have tiny encoded uint value = 1. + // TODO check this out. + if ((buffer[startOff] & MAJOR_TYPE_MASK) != UINT_TYPE) { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + if ((byte) (buffer[startOff] & ADDITIONAL_MASK) != 0x01) { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + incrementStartOff((short) 1); + return KMBoolTag.instance(tagKey); + } + private short decodeEnum(short exp) { // Enum value will always be integer with max 1 byte length. if ((buffer[startOff] & MAJOR_TYPE_MASK) != UINT_TYPE) { ISOException.throwIt(ISO7816.SW_DATA_INVALID); } short len = (short) (buffer[startOff] & ADDITIONAL_MASK); - byte enumVal = 0; + byte enumVal; if (len > UINT8_LENGTH) { ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); } @@ -142,11 +272,11 @@ private KMEnum decode(KMEnum exp) { enumVal = buffer[startOff]; incrementStartOff((short) 1); } - return exp.instance(exp.getType(), enumVal); + return KMEnum.instance(KMEnum.cast(exp).getEnumType(), enumVal); } - private KMInteger decode(KMInteger exp) { - KMInteger inst; + private short decodeInteger(short exp) { + short inst; if ((buffer[startOff] & MAJOR_TYPE_MASK) != UINT_TYPE) { ISOException.throwIt(ISO7816.SW_DATA_INVALID); } @@ -156,156 +286,30 @@ private KMInteger decode(KMInteger exp) { } incrementStartOff((short) 1); if (len < UINT8_LENGTH) { - inst = exp.uint_8((byte)(len & ADDITIONAL_MASK)); + inst = KMInteger.uint_8((byte)(len & ADDITIONAL_MASK)); } else if (len == UINT8_LENGTH) { - inst = exp.instance(buffer, startOff, (short) 1); + inst = KMInteger.instance(buffer, startOff, (short) 1); incrementStartOff((short) 1); } else if (len == UINT16_LENGTH) { - inst = exp.instance(buffer, startOff, (short) 2); + inst = KMInteger.instance(buffer, startOff, (short) 2); incrementStartOff((short) 2); } else if (len == UINT32_LENGTH) { - inst = exp.instance(buffer, startOff, (short) 4); + inst = KMInteger.instance(buffer, startOff, (short) 4); incrementStartOff((short) 4); } else { - inst = exp.instance(buffer, startOff, (short) 8); + inst = KMInteger.instance(buffer, startOff, (short) 8); incrementStartOff((short) 8); } return inst; } - private KMByteBlob decode(KMByteBlob exp) { + private short decodeByteBlob(short exp) { short payloadLength = readMajorTypeWithPayloadLength(BYTES_TYPE); - KMByteBlob inst = exp.instance(buffer, startOff, payloadLength); + short inst = KMByteBlob.instance(buffer, startOff, payloadLength); incrementStartOff(payloadLength); return inst; } - private KMArray decode(KMArray exp) { - short payloadLength = readMajorTypeWithPayloadLength(ARRAY_TYPE); - if (exp.length() != payloadLength) { - ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); - } - KMArray inst = exp.instance(payloadLength); - short index = 0; - while (index < payloadLength) { - KMType type = exp.get(index); - inst.add(index, decode(type)); - index++; - } - return inst; - } - - private KMVector decode(KMVector exp, KMInteger type) { - short payloadLength = readMajorTypeWithPayloadLength(ARRAY_TYPE); - KMVector inst = exp.instance(type, payloadLength); - short index = 0; - while (index < payloadLength) { - inst.add(index, decode(type)); - index++; - } - return inst; - } - - private KMVerificationToken decode(KMVerificationToken exp) { - KMArray vals = decode(exp.getVals()); - return exp.instance(vals); - } - - private KMHardwareAuthToken decode(KMHardwareAuthToken exp) { - KMArray vals = decode(exp.getVals()); - return exp.instance(vals); - } - - private KMHmacSharingParameters decode(KMHmacSharingParameters exp) { - KMArray vals = decode(exp.getVals()); - return exp.instance(vals); - } - - private KMKeyParameters decode(KMKeyParameters exp) { - short payloadLength = readMajorTypeWithPayloadLength(MAP_TYPE); - // allowed tags - // TODO expand the logic to handle prototypes with tag values also. - KMArray allowedTags = exp.getVals(); - KMArray vals = KMArray.instance(payloadLength); - short index = 0; - while (index < payloadLength) { - short tagInd = 0; - short tagType = peekTagType(); - while (tagInd < allowedTags.length()) { - KMTag tagClass = ((KMTag) allowedTags.get(tagInd)); - short allowedType = ((KMTag) allowedTags.get(tagInd)).getTagType(); - if (tagType == allowedType) { - vals.add(index, decode(tagClass)); - break; - } - tagInd++; - } - index++; - } - return KMKeyParameters.instance(vals); - } - - private KMKeyCharacteristics decode(KMKeyCharacteristics exp) { - KMArray vals = decode(exp.getVals()); - return exp.instance(vals); - } - - private KMType decode(KMType exp) { - if (exp instanceof KMByteBlob) { - return decode((KMByteBlob) exp); - } - if (exp instanceof KMInteger) { - return decode((KMInteger) exp); - } - if (exp instanceof KMArray) { - return decode((KMArray) exp); - } - if (exp instanceof KMVector) { - if (!((((KMVector) exp).getType()) instanceof KMInteger)) { - ISOException.throwIt(ISO7816.SW_DATA_INVALID); - } - return decode((KMVector) exp, (KMInteger) ((KMVector) exp).getType()); - } - if (exp instanceof KMByteTag) { - return decode((KMByteTag) exp); - } - if (exp instanceof KMBoolTag) { - return decode((KMBoolTag) exp); - } - if (exp instanceof KMIntegerTag) { - return decode((KMIntegerTag) exp); - } - if (exp instanceof KMIntegerArrayTag) { - return decode((KMIntegerArrayTag) exp); - } - if (exp instanceof KMEnumTag) { - return decode((KMEnumTag) exp); - } - if (exp instanceof KMEnum) { - return decode((KMEnum) exp); - } - if (exp instanceof KMEnumArrayTag) { - return decode((KMEnumArrayTag) exp); - } - if (exp instanceof KMKeyParameters) { - return decode((KMKeyParameters) exp); - } - if (exp instanceof KMKeyCharacteristics) { - return decode((KMKeyCharacteristics) exp); - } - if (exp instanceof KMVerificationToken) { - return decode((KMVerificationToken) exp); - } - if (exp instanceof KMHmacSharingParameters) { - return decode((KMHmacSharingParameters) exp); - } - if (exp instanceof KMHardwareAuthToken) { - return decode((KMHardwareAuthToken) exp); - } - ISOException.throwIt(ISO7816.SW_DATA_INVALID); - return null; - } - private short peekTagType() { if ((buffer[startOff] & MAJOR_TYPE_MASK) != UINT_TYPE) { ISOException.throwIt(ISO7816.SW_DATA_INVALID); diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMEncoder.java b/Applet/Applet/src/com/android/javacard/keymaster/KMEncoder.java index ec736de4..5c96387a 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMEncoder.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMEncoder.java @@ -50,7 +50,7 @@ public KMEncoder() { length = 0; } - public short encode(KMArray object, byte[] buffer, short startOff, short length) { + public short encode(short object, byte[] buffer, short startOff, short length) { this.buffer = buffer; this.startOff = startOff; this.length = length; @@ -60,160 +60,153 @@ public short encode(KMArray object, byte[] buffer, short startOff, short length) return this.length; } - private void encode(KMType exp){ - if(exp instanceof KMByteBlob){ - encode((KMByteBlob) exp); - return; + private void encode(short exp){ + byte type = KMType.getType(exp); + switch(type){ + case KMType.BYTE_BLOB_TYPE: + encodeByteBlob(exp); + return; + case KMType.INTEGER_TYPE: + encodeInteger(exp); + return; + case KMType.ARRAY_TYPE: + encodeArray(exp); + return; + case KMType.ENUM_TYPE: + encodeEnum(exp); + return; + case KMType.KEY_PARAM_TYPE: + encodeKeyParam(exp); + return; + case KMType.KEY_CHAR_TYPE: + encodeKeyChar(exp); + return; + case KMType.VERIFICATION_TOKEN_TYPE: + encodeVeriToken(exp); + return; + case KMType.HMAC_SHARING_PARAM_TYPE: + encodeHmacSharingParam(exp); + return; + case KMType.HW_AUTH_TOKEN_TYPE: + encodeHwAuthToken(exp); + return; + case KMType.TAG_TYPE: + short tagType = KMTag.getTagType(exp); + encodeTag(tagType, exp); + return; + default: + ISOException.throwIt(ISO7816.SW_DATA_INVALID); } - if(exp instanceof KMEnum){ - encode((KMEnum) exp); - return; - } - if(exp instanceof KMInteger){ - encode((KMInteger)exp); - return; - } - if(exp instanceof KMArray){ - encode((KMArray)exp); - return; - } - if(exp instanceof KMVector){ - encode((KMVector)exp); - return; - } - if(exp instanceof KMByteTag){ - encode((KMByteTag)exp); - return; - } - if(exp instanceof KMBoolTag){ - encode((KMBoolTag) exp); - return; - } - if(exp instanceof KMIntegerTag){ - encode((KMIntegerTag)exp); - return; - } - if(exp instanceof KMIntegerArrayTag){ - encode((KMIntegerArrayTag)exp); - return; - } - if(exp instanceof KMEnumTag){ - encode((KMEnumTag) exp); - return; - } - if(exp instanceof KMEnumArrayTag){ - encode((KMEnumArrayTag) exp); - return; - } - if(exp instanceof KMKeyParameters){ - encode((KMKeyParameters) exp); - return; - } - if(exp instanceof KMKeyCharacteristics){ - encode((KMKeyCharacteristics) exp); - return; - } - if(exp instanceof KMVerificationToken){ - encode((KMVerificationToken) exp); - return; - } - if(exp instanceof KMHmacSharingParameters){ - encode((KMHmacSharingParameters) exp); - return; - } - if(exp instanceof KMHardwareAuthToken){ - encode((KMHardwareAuthToken) exp); - return; + } + private void encodeTag(short tagType, short exp){ + switch(tagType){ + case KMType.BYTES_TAG: + encodeBytesTag(exp); + return; + case KMType.BOOL_TAG: + encodeBoolTag(exp); + return; + case KMType.UINT_TAG: + case KMType.ULONG_TAG: + case KMType.DATE_TAG: + encodeIntegerTag(exp); + return; + case KMType.ULONG_ARRAY_TAG: + case KMType.UINT_ARRAY_TAG: + encodeIntegerArrayTag(exp); + return; + case KMType.ENUM_TAG: + encodeEnumTag(exp); + return; + case KMType.ENUM_ARRAY_TAG: + encodeEnumArrayTag(exp); + return; + default: + ISOException.throwIt(ISO7816.SW_DATA_INVALID); } - ISOException.throwIt(ISO7816.SW_DATA_INVALID); } - private void encode(KMKeyParameters obj) { - encodeAsMap(obj.getVals()); + private void encodeKeyParam(short obj) { + encodeAsMap(KMKeyParameters.cast(obj).getVals()); } - private void encode(KMKeyCharacteristics obj) { - encode(obj.getVals()); + private void encodeKeyChar(short obj) { + encode(KMKeyCharacteristics.cast(obj).getVals()); } - private void encode(KMVerificationToken obj) { - encode(obj.getVals()); + private void encodeVeriToken(short obj) { + encode(KMVerificationToken.cast(obj).getVals()); } - private void encode(KMHardwareAuthToken obj) { - encode(obj.getVals()); + private void encodeHwAuthToken(short obj) { + encode(KMHardwareAuthToken.cast(obj).getVals()); } - private void encode(KMHmacSharingParameters obj) { - encode(obj.getVals()); + private void encodeHmacSharingParam(short obj) { + encode(KMHmacSharingParameters.cast(obj).getVals()); } - private void encode(KMArray obj) { - writeMajorTypeWithLength(ARRAY_TYPE, obj.length()); + private void encodeArray(short obj) { + writeMajorTypeWithLength(ARRAY_TYPE, KMArray.cast(obj).length()); short index = 0; - while(index < obj.length()){ - encode(obj.get(index)); + short len = KMArray.cast(obj).length(); + while(index < len){ + encode(KMArray.cast(obj).get(index)); index++; } } - private void encodeAsMap(KMArray obj){ - writeMajorTypeWithLength(MAP_TYPE, obj.length()); + private void encodeAsMap(short obj){ + writeMajorTypeWithLength(MAP_TYPE, KMArray.cast(obj).length()); short index = 0; - while(index < obj.length()){ - KMType t = obj.get(index); - encode(t); - //encode(obj.get(index)); + short len = KMArray.cast(obj).length(); + while(index < len){ + short inst = KMArray.cast(obj).get(index); + encode(inst); index++; } } - private void encode(KMVector obj){ - writeMajorTypeWithLength(ARRAY_TYPE, obj.length()); - short index = 0; - while(index 0){ + if(val[(short)(startOff + index)] > 0){ break; } index++; // index will be equal to len if value is 0. @@ -222,26 +215,27 @@ private void encode(KMInteger obj) { short diff = (short)(len - index); if(diff == 0){ writeByte((byte)(UINT_TYPE | 0)); - }else if((diff == 1) && val[index] < UINT8_LENGTH){ - writeByte((byte)(UINT_TYPE | val[index])); + }else if((diff == 1) && val[(short)(startOff + index)] < UINT8_LENGTH){ + writeByte((byte)(UINT_TYPE | val[(short)(startOff + index)])); }else if (diff == 1){ writeByte((byte)(UINT_TYPE | UINT8_LENGTH)); - writeByte(val[index]); + writeByte(val[(short)(startOff + index)]); }else if(diff == 2){ writeByte((byte)(UINT_TYPE | UINT16_LENGTH)); - writeBytes(val, index, (short)2); + writeBytes(val, (short)(startOff + index), (short)2); }else if(diff <= 4){ writeByte((byte)(UINT_TYPE | UINT32_LENGTH)); - writeBytes(val, (short)(len - 4), (short)4); + writeBytes(val, (short)(startOff + len - 4), (short)4); }else { writeByte((byte)(UINT_TYPE | UINT64_LENGTH)); - writeBytes(val, (short)0, (short)8); + writeBytes(val, startOff, (short)8); } } - private void encode(KMByteBlob obj) { - writeMajorTypeWithLength(BYTES_TYPE, obj.length()); - writeBytes(obj.getVal(), obj.getStartOff(), obj.length()); + private void encodeByteBlob(short obj) { + writeMajorTypeWithLength(BYTES_TYPE, KMByteBlob.cast(obj).length()); + writeBytes(KMByteBlob.cast(obj).getBuffer(), KMByteBlob.cast(obj).getStartOff(), + KMByteBlob.cast(obj).length()); } private void writeByteValue(byte val){ @@ -258,7 +252,6 @@ private void writeTag(short tagType, short tagKey){ writeShort(tagType); writeShort(tagKey); } - // TODO bug here private void writeMajorTypeWithLength(byte majorType, short len) { if(len <= TINY_PAYLOAD){ writeByte((byte)(majorType | (byte) (len & ADDITIONAL_MASK))); diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMEnum.java b/Applet/Applet/src/com/android/javacard/keymaster/KMEnum.java index 441fd833..67196a81 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMEnum.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMEnum.java @@ -18,45 +18,63 @@ import javacard.framework.ISO7816; import javacard.framework.ISOException; +import javacard.framework.Util; public class KMEnum extends KMType { + private static KMEnum prototype; + private static short instPtr; + private static short[] types = {HARDWARE_TYPE, KEY_FORMAT, KEY_DERIVATION_FUNCTION}; private static Object[] enums = null; - private short type; - private byte val; + private KMEnum() {} - private KMEnum() { - init(); + private static KMEnum proto(short ptr) { + if (prototype == null) prototype = new KMEnum(); + instPtr = ptr; + return prototype; } - @Override - public void init() { - type = 0; - val = 0; + // pointer to an empty instance used as expression + public static short exp() { + return KMType.exp(ENUM_TYPE); } - @Override + public short length() { - return 1; + return Util.getShort(heap, (short) (instPtr + 1)); } - public static KMEnum instance() { - return repository.newEnum(); + // cast the ptr to KMByteBlob + public static KMEnum cast(short ptr) { + if (heap[ptr] != ENUM_TYPE) ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + if (Util.getShort(heap, (short) (ptr + 1)) == INVALID_VALUE) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + return proto(ptr); } - public static KMEnum instance(short enumType, byte val) { - KMEnum inst = repository.newEnum(); + public static short instance(short enumType) { + if (!validateEnum(enumType, NO_VALUE)) { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + short ptr = KMType.instance(ENUM_TYPE, (short)2); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE), enumType); + return ptr; + } + + public static short instance(short enumType, byte val) { if (!validateEnum(enumType, val)) { ISOException.throwIt(ISO7816.SW_DATA_INVALID); } - inst.type = enumType; - inst.val = val; - return inst; + short ptr = KMType.instance(ENUM_TYPE, (short)3); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE), enumType); + heap[(short)(ptr+TLV_HEADER_SIZE+2)] = val; + return ptr; } - public static void create(KMEnum[] enumRefTable) { + private static void create() { if (enums == null) { enums = new Object[] { @@ -72,32 +90,27 @@ public static void create(KMEnum[] enumRefTable) { } }; } - byte index = 0; - while (index < enumRefTable.length) { - enumRefTable[index] = new KMEnum(); - index++; - } } - public KMEnum setVal(byte val) { - this.val = val; - return this; + public void setVal(byte val) { + heap[(short)(instPtr+TLV_HEADER_SIZE+2)] = val; } public byte getVal() { - return val; + return heap[(short)(instPtr+TLV_HEADER_SIZE+2)]; } - public KMEnum setType(short type) { - this.type = type; - return this; + public void setEnumType(short type) { + Util.setShort(heap, (short)(instPtr+TLV_HEADER_SIZE),type); } - public short getType() { - return type; + public short getEnumType() { + return Util.getShort(heap, (short)(instPtr+TLV_HEADER_SIZE)); } + // validate enumeration keys and values. private static boolean validateEnum(short key, byte value) { + create(); // check if key exists short index = (short) types.length; while (--index >= 0) { diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMEnumArrayTag.java b/Applet/Applet/src/com/android/javacard/keymaster/KMEnumArrayTag.java index 30c40bad..c0601267 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMEnumArrayTag.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMEnumArrayTag.java @@ -18,50 +18,101 @@ import javacard.framework.ISO7816; import javacard.framework.ISOException; +import javacard.framework.Util; public class KMEnumArrayTag extends KMTag { + private static KMEnumArrayTag prototype; + private static short instPtr; + // Arrays given below, together they form multi dimensional array. // Tag private static short[] tags = {PURPOSE, BLOCK_MODE, DIGEST, PADDING}; // Tag Values. private static Object[] enums = null; - // Tag Key - private short key; - // Byte Array of Tag Values. - private KMByteBlob array; - - // assignBlob constructor - private KMEnumArrayTag() { - init(); + + private KMEnumArrayTag() {} + + private static KMEnumArrayTag proto(short ptr) { + if (prototype == null) prototype = new KMEnumArrayTag(); + instPtr = ptr; + return prototype; + } + + // pointer to an empty instance used as expression + public static short exp() { + short blobPtr = KMByteBlob.exp(); + short ptr = instance(TAG_TYPE, (short)6); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE), ENUM_ARRAY_TAG); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE+2), INVALID_TAG); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE+4), blobPtr); + return ptr; } - @Override - public void init() { - key = 0; - array = null; + public static short instance(short key) { + byte[] vals = getAllowedEnumValues(key); + if (vals == null) { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + short blobPtr = KMByteBlob.exp(); + return instance(key, blobPtr); + } + + public static short instance(short key, short byteBlob) { + byte[] allowedVals = getAllowedEnumValues(key); + if (allowedVals == null) { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + KMByteBlob blob = KMByteBlob.cast(byteBlob); + short byteIndex = 0; + while (byteIndex < blob.length()) { + short enumIndex = 0; + boolean validValue = false; + while (enumIndex < allowedVals.length) { + if (blob.get(byteIndex) == allowedVals[enumIndex]) { + validValue = true; + break; + } + enumIndex++; + } + if (!validValue) { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + byteIndex++; + } + short ptr = instance(TAG_TYPE, (short)6); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE), ENUM_ARRAY_TAG); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE+2), key); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE+4), byteBlob); + return ptr; + } + + public static KMEnumArrayTag cast(short ptr) { + if (heap[ptr] != TAG_TYPE) ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + if (Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE)) != ENUM_ARRAY_TAG) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + return proto(ptr); } - @Override public short getKey() { - return key; + return Util.getShort(heap, (short)(instPtr+TLV_HEADER_SIZE+2)); } - @Override public short getTagType() { return KMType.ENUM_ARRAY_TAG; } - // returns the length - @Override - public short length() { - return array.length(); + + public short getValues() { + return Util.getShort(heap, (short)(instPtr+TLV_HEADER_SIZE+4)); } - public static KMEnumArrayTag instance() { - return repository.newEnumArrayTag(); + public short length() { + short blobPtr = Util.getShort(heap, (short)(instPtr+TLV_HEADER_SIZE+4)); + return KMByteBlob.cast(blobPtr).length(); } - public static void create(KMEnumArrayTag[] enumArrayTagRefTable) { + public static void create() { if (enums == null) { enums = new Object[] { @@ -73,35 +124,10 @@ public static void create(KMEnumArrayTag[] enumArrayTagRefTable) { } }; } - byte index = 0; - while (index < enumArrayTagRefTable.length) { - enumArrayTagRefTable[index] = new KMEnumArrayTag(); - index++; - } - } - - // create default assignBlob without any value array - public static KMEnumArrayTag instance(short key) { - // check if key is valid. - byte[] vals = getAllowedEnumValues(key); - if (vals == null) { - ISOException.throwIt(ISO7816.SW_DATA_INVALID); - } - KMEnumArrayTag tag = repository.newEnumArrayTag(); - tag.key = key; - tag.array = null; - return tag; } - // Set the expected length for the prototype. - public KMEnumArrayTag withLength(short length) { - array.withLength(length); - return this; - } - - // get the allowed enum values for given tag key private static byte[] getAllowedEnumValues(short key) { - // check if key is allowed + create(); short index = (short) tags.length; while (--index >= 0) { if (tags[index] == key) { @@ -111,41 +137,4 @@ private static byte[] getAllowedEnumValues(short key) { return null; } - // get value array of this tag assignBlob. - public KMByteBlob getValues() { - return this.array; - } - - public KMEnumArrayTag setValues(KMByteBlob val) { - this.array = val; - return this; - } - // instantiate enum array pointing to existing array. - public static KMEnumArrayTag instance(short key, KMByteBlob blob) { - // validate key - byte[] allowedVals = getAllowedEnumValues(key); - if (allowedVals == null) { - ISOException.throwIt(ISO7816.SW_DATA_INVALID); - } - short byteIndex = 0; - while (byteIndex < blob.length()) { - short enumIndex = 0; - boolean validValue = false; - while (enumIndex < allowedVals.length) { - if (blob.get(byteIndex) == allowedVals[enumIndex]) { - validValue = true; - break; - } - enumIndex++; - } - if (!validValue) { - ISOException.throwIt(ISO7816.SW_DATA_INVALID); - } - byteIndex++; - } - KMEnumArrayTag tag = repository.newEnumArrayTag(); - tag.key = key; - tag.array = blob; - return tag; - } } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMEnumTag.java b/Applet/Applet/src/com/android/javacard/keymaster/KMEnumTag.java index 9a641425..8f66e5e3 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMEnumTag.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMEnumTag.java @@ -18,8 +18,11 @@ import javacard.framework.ISO7816; import javacard.framework.ISOException; +import javacard.framework.Util; public class KMEnumTag extends KMTag { + private static KMEnumTag prototype; + private static short instPtr; private static short[] tags = { ALGORITHM, ECCURVE, BLOB_USAGE_REQ, USER_AUTH_TYPE, ORIGIN, HARDWARE_TYPE @@ -27,49 +30,63 @@ public class KMEnumTag extends KMTag { private static Object[] enums = null; - private short key; - private byte val; + private KMEnumTag() {} - // assignBlob constructor - private KMEnumTag() { - init(); + private static KMEnumTag proto(short ptr) { + if (prototype == null) prototype = new KMEnumTag(); + instPtr = ptr; + return prototype; } - @Override - public void init() { - key = 0; - val = 0; + // pointer to an empty instance used as expression + public static short exp() { + short ptr = instance(TAG_TYPE, (short)2); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE), ENUM_TAG); + return ptr; } - @Override - public short getKey() { - return key; + public static short instance(short key) { + if(!validateEnum(key, NO_VALUE)){ + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + short ptr = KMType.instance(TAG_TYPE, (short)4); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE), ENUM_TAG); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE+2), key); + return ptr; } - @Override - public short length() { - return 1; + public static short instance(short key, byte val) { + if(!validateEnum(key, val)){ + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + short ptr = instance(TAG_TYPE, (short)5); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE), ENUM_TAG); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE+2), key); + heap[(short)(ptr+TLV_HEADER_SIZE+4)]= val; + return ptr; } - @Override - public short getTagType() { - return KMType.ENUM_TAG; + public static KMEnumTag cast(short ptr) { + if (heap[ptr] != TAG_TYPE) ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + if (Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE)) != ENUM_TAG) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + return proto(ptr); } - public static KMEnumTag instance() { - return repository.newEnumTag(); + public short getKey() { + return Util.getShort(heap, (short)(instPtr+TLV_HEADER_SIZE+2)); } - public static KMEnumTag instance(short key) { - if(!validateEnum(key, NO_VALUE)){ - ISOException.throwIt(ISO7816.SW_DATA_INVALID); - } - KMEnumTag tag = repository.newEnumTag(); - tag.key = key; - return tag; + public short getTagType() { + return KMType.ENUM_TAG; } - public static void create(KMEnumTag[] enumTagRefTable) { + public byte getValue() { + return heap[(short)(instPtr+TLV_HEADER_SIZE+4)]; + } + + public static void create() { if (enums == null) { enums = new Object[] { @@ -81,15 +98,11 @@ public static void create(KMEnumTag[] enumTagRefTable) { new byte[] {SOFTWARE, TRUSTED_ENVIRONMENT, STRONGBOX} }; } - byte index = 0; - while (index < enumTagRefTable.length) { - enumTagRefTable[index] = new KMEnumTag(); - index++; - } } // validate enumeration keys and values. private static boolean validateEnum(short key, byte value) { + create(); // check if key exists short index = (short) tags.length; while (--index >= 0) { @@ -115,19 +128,4 @@ private static boolean validateEnum(short key, byte value) { // return false if key does not exist return false; } - - // get value of this tag assignBlob. - public byte getValue() { - return val; - } - - // instantiate enum tag. - public static KMEnumTag instance(short key, byte val) { - if (!validateEnum(key, val)) { - ISOException.throwIt(ISO7816.SW_DATA_INVALID); - } - KMEnumTag tag = instance(key); - tag.val = val; - return tag; - } } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMHardwareAuthToken.java b/Applet/Applet/src/com/android/javacard/keymaster/KMHardwareAuthToken.java index 9c188c67..c5c92d86 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMHardwareAuthToken.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMHardwareAuthToken.java @@ -18,6 +18,7 @@ import javacard.framework.ISO7816; import javacard.framework.ISOException; +import javacard.framework.Util; public class KMHardwareAuthToken extends KMType { public static final byte CHALLENGE = 0x00; @@ -27,76 +28,122 @@ public class KMHardwareAuthToken extends KMType { public static final byte TIMESTAMP = 0x04; public static final byte MAC = 0x05; - private KMArray vals; + private static KMHardwareAuthToken prototype; + private static short instPtr; + + private KMHardwareAuthToken() {} + + public static short exp() { + short arrPtr = KMArray.instance((short)6); + KMArray arr = KMArray.cast(arrPtr); + arr.add(CHALLENGE, KMInteger.exp()); + arr.add(USER_ID, KMInteger.exp()); + arr.add(AUTHENTICATOR_ID, KMInteger.exp()); + arr.add(HW_AUTHENTICATOR_TYPE, KMEnumTag.instance(KMType.USER_AUTH_TYPE)); + arr.add(TIMESTAMP, KMInteger.exp()); + arr.add(MAC, KMByteBlob.exp()); + return instance(arrPtr); + } + + private static KMHardwareAuthToken proto(short ptr) { + if (prototype == null) prototype = new KMHardwareAuthToken(); + instPtr = ptr; + return prototype; + } - private KMHardwareAuthToken() { - init(); + public static short instance() { + short arrPtr = KMArray.instance((short)6); + return instance(arrPtr); } - @Override - public void init() { - vals = null; + public static short instance(short vals) { + KMArray arr = KMArray.cast(vals); + if(arr.length() != 6)ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); + short ptr = KMType.instance(HW_AUTH_TOKEN_TYPE, (short)2); + Util.setShort(heap, (short)(ptr + TLV_HEADER_SIZE), vals); + return ptr; + } + + public static KMHardwareAuthToken cast(short ptr) { + if (heap[ptr] != HW_AUTH_TOKEN_TYPE) ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + short arrPtr = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE)); + if(heap[arrPtr] != ARRAY_TYPE) ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + return proto(ptr); + } + + public short getVals() { + return Util.getShort(heap, (short) (instPtr + TLV_HEADER_SIZE)); } - @Override public short length() { - return vals.length(); + short arrPtr = getVals(); + return KMArray.cast(arrPtr).length(); + } + + public short getChallenge() { + short arrPtr = getVals(); + return KMArray.cast(arrPtr).get(CHALLENGE); + } + + public void setChallenge(short vals) { + KMInteger.cast(vals); + short arrPtr = getVals(); + KMArray.cast(arrPtr).add(CHALLENGE, vals); } - public static KMHardwareAuthToken instance() { - KMHardwareAuthToken inst = repository.newHwAuthToken(); - inst.vals = KMArray.instance((short) 6); - inst.vals.add(CHALLENGE, KMInteger.instance()); - inst.vals.add(USER_ID, KMInteger.instance()); - inst.vals.add(AUTHENTICATOR_ID, KMInteger.instance()); - inst.vals.add(HW_AUTHENTICATOR_TYPE, KMEnumTag.instance(KMType.USER_AUTH_TYPE)); - inst.vals.add(TIMESTAMP, KMInteger.instance()); - inst.vals.add(MAC, KMByteBlob.instance()); - return inst; + public short getUserId() { + short arrPtr = getVals(); + return KMArray.cast(arrPtr).get(USER_ID); } - public static KMHardwareAuthToken instance(KMArray vals) { - if (vals.length() != 6) { - ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); - } - KMHardwareAuthToken inst = repository.newHwAuthToken(); - inst.vals = vals; - return inst; + public void setUserId(short vals) { + KMInteger.cast(vals); + short arrPtr = getVals(); + KMArray.cast(arrPtr).add(USER_ID, vals); } - public static void create(KMHardwareAuthToken[] hwAuthTokenRefTable) { - byte index = 0; - while (index < hwAuthTokenRefTable.length) { - hwAuthTokenRefTable[index] = new KMHardwareAuthToken(); - index++; - } + public short getAuthenticatorId() { + short arrPtr = getVals(); + return KMArray.cast(arrPtr).get(AUTHENTICATOR_ID); } - public KMInteger getChallenge() { - return (KMInteger) vals.get(CHALLENGE); + public void setAuthenticatorId(short vals) { + KMInteger.cast(vals); + short arrPtr = getVals(); + KMArray.cast(arrPtr).add(AUTHENTICATOR_ID, vals); } - public KMInteger getUserId() { - return (KMInteger) vals.get(USER_ID); + public short getHwAuthenticatorType() { + short arrPtr = getVals(); + return KMArray.cast(arrPtr).get(HW_AUTHENTICATOR_TYPE); } - public KMInteger getAuthenticatorId() { - return (KMInteger) vals.get(AUTHENTICATOR_ID); + public void setHwAuthenticatorType(short vals) { + short key = KMEnumTag.cast(vals).getKey(); + if(key != USER_AUTH_TYPE) ISOException.throwIt(ISO7816.SW_DATA_INVALID); + short arrPtr = getVals(); + KMArray.cast(arrPtr).add(HW_AUTHENTICATOR_TYPE, vals); } - public byte getHwAuthenticatorType() { - return ((KMEnumTag) vals.get(HW_AUTHENTICATOR_TYPE)).getValue(); + public short getTimestamp() { + short arrPtr = getVals(); + return KMArray.cast(arrPtr).get(TIMESTAMP); } - public KMInteger getTimestamp() { - return (KMInteger) vals.get(TIMESTAMP); + public void setTimestamp(short vals) { + KMInteger.cast(vals); + short arrPtr = getVals(); + KMArray.cast(arrPtr).add(TIMESTAMP, vals); } - public KMByteBlob getMac() { - return (KMByteBlob) vals.get(MAC); + public short getMac() { + short arrPtr = getVals(); + return KMArray.cast(arrPtr).get(MAC); } - public KMArray getVals() { - return vals; + public void setMac(short vals) { + KMByteBlob.cast(vals); + short arrPtr = getVals(); + KMArray.cast(arrPtr).add(MAC, vals); } } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMHmacSharingParameters.java b/Applet/Applet/src/com/android/javacard/keymaster/KMHmacSharingParameters.java index a1fca4d1..bc34c217 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMHmacSharingParameters.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMHmacSharingParameters.java @@ -18,60 +18,78 @@ import javacard.framework.ISO7816; import javacard.framework.ISOException; +import javacard.framework.Util; public class KMHmacSharingParameters extends KMType { public static final byte SEED = 0x00; public static final byte NONCE = 0x01; - private KMArray vals; - private KMHmacSharingParameters() { - init(); + private static KMHmacSharingParameters prototype; + private static short instPtr; + + private KMHmacSharingParameters() {} + + public static short exp() { + short arrPtr = KMArray.instance((short)2); + KMArray arr = KMArray.cast(arrPtr); + arr.add(SEED, KMByteBlob.exp()); + arr.add(NONCE, KMByteBlob.exp()); + return instance(arrPtr); } - @Override - public void init() { - vals = null; + private static KMHmacSharingParameters proto(short ptr) { + if (prototype == null) prototype = new KMHmacSharingParameters(); + instPtr = ptr; + return prototype; } - @Override - public short length() { - return vals.length(); + public static short instance() { + short arrPtr = KMArray.instance((short)2); + return instance(arrPtr); + } + + public static short instance(short vals) { + short ptr = KMType.instance(HMAC_SHARING_PARAM_TYPE, (short)2); + if(KMArray.cast(vals).length() != 2) ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); + Util.setShort(heap, (short)(ptr + TLV_HEADER_SIZE), vals); + return ptr; + } + + public static KMHmacSharingParameters cast(short ptr) { + if (heap[ptr] != HMAC_SHARING_PARAM_TYPE) ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + short arrPtr = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE)); + if(heap[arrPtr] != ARRAY_TYPE) ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + return proto(ptr); } - public static KMHmacSharingParameters instance() { - KMHmacSharingParameters inst = repository.newHmacSharingParameters(); - inst.vals = KMArray.instance((short) 2); - inst.vals.add(SEED, KMByteBlob.instance()); - inst.vals.add(NONCE, KMByteBlob.instance()); - return inst; + public short getVals() { + return Util.getShort(heap, (short) (instPtr + TLV_HEADER_SIZE)); } - public static KMHmacSharingParameters instance(KMArray vals) { - if (vals.length() != 2) { - ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); - } - KMHmacSharingParameters inst = repository.newHmacSharingParameters(); - inst.vals = vals; - return inst; + public short length() { + short arrPtr = getVals(); + return KMArray.cast(arrPtr).length(); } - public static void create(KMHmacSharingParameters[] hmacSharingParamsRefTable) { - byte index = 0; - while (index < hmacSharingParamsRefTable.length) { - hmacSharingParamsRefTable[index] = new KMHmacSharingParameters(); - index++; - } + public void setSeed(short vals) { + KMByteBlob.cast(vals); + short arrPtr = getVals(); + KMArray.cast(arrPtr).add(SEED, vals); } - public KMByteBlob getSeed() { - return (KMByteBlob) vals.get(SEED); + public void setNonce(short vals) { + KMByteBlob.cast(vals); + short arrPtr = getVals(); + KMArray.cast(arrPtr).add(NONCE, vals); } - public KMByteBlob getNonce() { - return (KMByteBlob) vals.get(NONCE); + public short getNonce() { + short arrPtr = getVals(); + return KMArray.cast(arrPtr).get(NONCE); } - public KMArray getVals() { - return vals; + public short getSeed() { + short arrPtr = getVals(); + return KMArray.cast(arrPtr).get(SEED); } } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMInteger.java b/Applet/Applet/src/com/android/javacard/keymaster/KMInteger.java index 987a0858..d4efe987 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMInteger.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMInteger.java @@ -18,117 +18,115 @@ import javacard.framework.ISO7816; import javacard.framework.ISOException; +import javacard.framework.JCSystem; import javacard.framework.Util; // Represents 8 bit, 16 bit, 32 bit and 64 bit integers public class KMInteger extends KMType { - private byte[] val; + private static final short UINT_32 = 4; + private static final short UINT_64 = 8; + private static KMInteger prototype; + private static short instPtr; - private KMInteger() { - init(); + private KMInteger() {} + + private static KMInteger proto(short ptr) { + if (prototype == null) prototype = new KMInteger(); + instPtr = ptr; + return prototype; } - @Override - public void init() { - val = null; + public static short exp() { + return KMType.exp(INTEGER_TYPE); } - @Override - public short length() { - return (short) this.val.length; + // return an empty integer instance + public static short instance(short length) { + if ((length <= 0) || (length > 8)) ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); + if (length > 4) { + length = UINT_64; + } else { + length = UINT_32; + } + return KMType.instance(INTEGER_TYPE, length); + } + + public static short instance(byte[] num, short srcOff, short length) { + if (length > 8) { + ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); + } + if (length == 1) { + return uint_8(num[srcOff]); + } else if (length == 2) { + return uint_16(Util.getShort(num, srcOff)); + } else if (length == 4) { + return uint_32(num, srcOff); + } else { + return uint_64(num, srcOff); + } } - public static KMInteger instance() { - return repository.newInteger(); + public static KMInteger cast(short ptr) { + byte[] heap = repository.getHeap(); + if (heap[ptr] != INTEGER_TYPE) ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + if (Util.getShort(heap, (short) (ptr + 1)) == INVALID_VALUE) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + return proto(ptr); } // create integer and copy byte value - public static KMInteger uint_8(byte num) { - KMInteger inst = repository.newInteger(); - inst.val = repository.newIntegerArray((short) 4); - inst.val[3] = num; - return inst; + public static short uint_8(byte num) { + short ptr = instance(UINT_32); + heap[(short) (ptr + TLV_HEADER_SIZE + 3)] = num; + return ptr; } // create integer and copy short value - public static KMInteger uint_16(short num) { - KMInteger inst = repository.newInteger(); - inst.val = repository.newIntegerArray((short) 4); - inst.val[2] = (byte) ((num >> 8) & 0xff); - inst.val[3] = (byte) (num & 0xff); - return inst; + public static short uint_16(short num) { + short ptr = instance(UINT_32); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), num); + return ptr; } // create integer and copy integer value - public static KMInteger uint_32(byte[] num, short offset) { - KMInteger inst = repository.newInteger(); - inst.val = repository.newIntegerArray((short) 4); - Util.arrayCopy(num, offset, inst.val, (short) 0, (short) 4); - return inst; + public static short uint_32(byte[] num, short offset) { + short ptr = instance(UINT_32); + Util.arrayCopy(num, offset, heap, (short) (ptr + TLV_HEADER_SIZE), UINT_32); + return ptr; } // create integer and copy integer value - public static KMInteger uint_64(byte[] num, short offset) { - KMInteger inst = repository.newInteger(); - inst.val = repository.newIntegerArray((short) 8); - Util.arrayCopy(num, offset, inst.val, (short) 0, (short) 8); - return inst; + public static short uint_64(byte[] num, short offset) { + short ptr = instance(UINT_64); + Util.arrayCopy(num, offset, heap, (short) (ptr + TLV_HEADER_SIZE), UINT_64); + return ptr; } - public static void create(KMInteger[] integerRefTable) { - byte index = 0; - while (index < integerRefTable.length) { - integerRefTable[index] = new KMInteger(); - index++; - } + // Get the length of the integer + public short length() { + return Util.getShort(heap, (short) (instPtr + 1)); } - public byte[] getValue() { - return val; + // Get the buffer pointer in which blob is contained. + public byte[] getBuffer() { + return heap; } - public KMInteger setValue(short val) { - this.val[2] = (byte) (val >> 8); - this.val[3] = (byte) (val & 0xFF); - return this; + // Get the start of value + public short getStartOff() { + return (short) (instPtr + TLV_HEADER_SIZE); } - public KMInteger setValue(byte[] val) { - this.val = val; - return this; + public void getValue(byte[] dest, short destOff, short length){ + Util.arrayCopyNonAtomic(heap, (short)(instPtr+TLV_HEADER_SIZE), dest, destOff, length); } public short getShort() { - if (val == null) { - ISOException.throwIt(ISO7816.SW_DATA_INVALID); - } else if (val.length != 4) { - ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); - } - return Util.makeShort(val[2], val[3]); + return Util.getShort(heap, (short) (instPtr + TLV_HEADER_SIZE + 2)); } public byte getByte() { - if (val == null) { - ISOException.throwIt(ISO7816.SW_DATA_INVALID); - } else if (val.length != 4) { - ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); - } - return val[3]; - } - - // copy the integer value from bytes - public static KMInteger instance(byte[] num, short srcOff, short length) { - if(length > 8){ - ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); - } - if (length == 1) { - return uint_8(num[srcOff]); - } else if (length == 2) { - return uint_16(Util.makeShort(num[srcOff], num[(short) (srcOff + 1)])); - } else if (length == 4) { - return uint_32(num, srcOff); - } else { - return uint_64(num, srcOff); - } + return heap[(short) (instPtr + TLV_HEADER_SIZE + 3)]; } } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMIntegerArrayTag.java b/Applet/Applet/src/com/android/javacard/keymaster/KMIntegerArrayTag.java index df96e114..775ebfd2 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMIntegerArrayTag.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMIntegerArrayTag.java @@ -18,77 +18,96 @@ import javacard.framework.ISO7816; import javacard.framework.ISOException; +import javacard.framework.Util; public class KMIntegerArrayTag extends KMTag { + private static KMIntegerArrayTag prototype; + private static short instPtr; + private static final short[] tags = {USER_SECURE_ID}; - private short key; - private KMVector vals; - private short tagType; - private KMIntegerArrayTag() { - init(); + private KMIntegerArrayTag() {} + + private static KMIntegerArrayTag proto(short ptr) { + if (prototype == null) prototype = new KMIntegerArrayTag(); + instPtr = ptr; + return prototype; } - @Override - public void init() { - key = 0; - vals = null; - tagType = KMType.UINT_ARRAY_TAG; + public static short exp(short tagType) { + if (!validateTagType(tagType)) { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + short arrPtr = KMArray.exp(KMType.INTEGER_TYPE); + short ptr = instance(TAG_TYPE, (short)6); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE), tagType); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE+2), INVALID_TAG); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE+4), arrPtr); + return ptr; } - @Override - public short getKey() { - return key; + public static short instance(short tagType, short key) { + if (!validateTagType(tagType)) { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + if (!validateKey(key)) { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + short arrPtr = KMArray.exp(); + return instance(tagType, key, arrPtr); } - @Override - public short length() { - return this.vals.length(); + public static short instance(short tagType, short key, short arrObj) { + if (!validateTagType(tagType)) { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + if (!validateKey(key)) { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + if(heap[arrObj] != ARRAY_TYPE) { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + short ptr = instance(TAG_TYPE, (short)6); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE), tagType); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE+2), key); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE+4), arrObj); + return ptr; + } + + public static KMIntegerArrayTag cast(short ptr) { + if (heap[ptr] != TAG_TYPE) ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + short tagType = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE)); + if (!validateTagType(tagType)) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + return proto(ptr); } - @Override public short getTagType() { - return tagType; + return Util.getShort(heap, (short)(instPtr+TLV_HEADER_SIZE)); } - public static KMIntegerArrayTag instance() { - return repository.newIntegerArrayTag(); + public short getKey() { + return Util.getShort(heap, (short)(instPtr+TLV_HEADER_SIZE+2)); } - public static void create(KMIntegerArrayTag[] intArrayTagRefTable) { - byte index = 0; - while (index < intArrayTagRefTable.length) { - intArrayTagRefTable[index] = new KMIntegerArrayTag(); - index++; - } + public short getValues() { + return Util.getShort(heap, (short)(instPtr+TLV_HEADER_SIZE+4)); } - public KMIntegerArrayTag asUlongArray() { - tagType = KMType.ULONG_ARRAY_TAG; - return this; + public short length() { + short ptr = getValues(); + return KMIntegerArrayTag.cast(ptr).length(); } - public static KMIntegerArrayTag instance(short key) { - if (!validateKey(key)) { - ISOException.throwIt(ISO7816.SW_DATA_INVALID); - } - KMIntegerArrayTag tag = repository.newIntegerArrayTag(); - tag.key = key; - tag.vals = KMVector.instance(KMInteger.instance()); - return tag; + public void add(short index, short val) { + KMArray arr = KMArray.cast(getValues()); + arr.add(index, val); } - public static KMIntegerArrayTag instance(short key, KMVector val) { - if (!(val.getType() instanceof KMInteger)) { - ISOException.throwIt(ISO7816.SW_DATA_INVALID); - } - if (!(validateKey(key))) { - ISOException.throwIt(ISO7816.SW_DATA_INVALID); - } - KMIntegerArrayTag tag = repository.newIntegerArrayTag(); - tag.key = key; - tag.vals = val; - return tag; + public short get(short index) { + KMArray arr = KMArray.cast(getValues()); + return arr.get(index); } private static boolean validateKey(short key) { @@ -101,25 +120,11 @@ private static boolean validateKey(short key) { return false; } - public KMIntegerArrayTag withLength(short length) { - this.vals.withLength(length); - return this; - } - - public KMVector getValues() { - return this.vals; - } - - public KMIntegerArrayTag setValues(KMVector vals) { - this.vals = vals; - return this; - } - - public void add(short index, KMInteger val) { - this.vals.add(index, val); - } - - public KMInteger get(short index) { - return (KMInteger) this.vals.get(index); + // TODO this should be combined with validateKey to actually validate {tagType, tagKey} pair. + private static boolean validateTagType(short tagType) { + if ((tagType == ULONG_ARRAY_TAG) || (tagType == UINT_ARRAY_TAG)) { + return true; + } + return false; } } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMIntegerTag.java b/Applet/Applet/src/com/android/javacard/keymaster/KMIntegerTag.java index d583f6b2..6aa3a1b4 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMIntegerTag.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMIntegerTag.java @@ -18,9 +18,13 @@ import javacard.framework.ISO7816; import javacard.framework.ISOException; +import javacard.framework.Util; // Implements UINT, ULONG and DATE tags. public class KMIntegerTag extends KMTag { + private static KMIntegerTag prototype; + private static short instPtr; + private static final short[] tags = { // UINT KEYSIZE, @@ -43,67 +47,81 @@ public class KMIntegerTag extends KMTag { CREATION_DATETIME }; - private short key; - private KMInteger val; - private short tagType; + private KMIntegerTag() {} - private KMIntegerTag() { - init(); + private static KMIntegerTag proto(short ptr) { + if (prototype == null) prototype = new KMIntegerTag(); + instPtr = ptr; + return prototype; } - @Override - public void init() { - key = 0; - val = null; - tagType = KMType.UINT_TAG; + public static short exp(short tagType) { + if (!validateTagType(tagType)) { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + short intPtr = KMInteger.exp(); + short ptr = instance(TAG_TYPE, (short)6); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE), tagType); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE+2), INVALID_TAG); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE+4), intPtr); + return ptr; } - @Override - public short getKey() { - return key; + public static short instance(short tagType, short key) { + if (!validateTagType(tagType)) { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + if (!validateKey(key)) { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + short intPtr = KMInteger.exp(); + return instance(tagType, key, intPtr); } - @Override - public short length() { - return (short) val.getValue().length; + public static short instance(short tagType, short key, short intObj) { + if (!validateTagType(tagType)) { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + if (!validateKey(key)) { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + if(heap[intObj] != INTEGER_TYPE) { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + short ptr = instance(TAG_TYPE, (short)6); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE), tagType); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE+2), key); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE+4), intObj); + return ptr; } - @Override - public short getTagType() { - return tagType; + public static KMIntegerTag cast(short ptr) { + if (heap[ptr] != TAG_TYPE) ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + short tagType = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE)); + if (!validateTagType(tagType)) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + return proto(ptr); } - public static KMIntegerTag instance() { - return repository.newIntegerTag(); + public short getTagType() { + return Util.getShort(heap, (short)(instPtr+TLV_HEADER_SIZE)); } - public static KMIntegerTag instance(short key) { - if (!validateKey(key)) { - ISOException.throwIt(ISO7816.SW_DATA_INVALID); - } - KMIntegerTag tag = repository.newIntegerTag(); - tag.key = key; - tag.val = null; - return tag; + public short getKey() { + return Util.getShort(heap, (short)(instPtr+TLV_HEADER_SIZE+2)); } - public static KMIntegerTag instance(short givenKey, KMInteger val) { - KMIntegerTag tag = KMIntegerTag.instance(givenKey); - tag.val = val; - if (val.length() == 8) { - tag.tagType = KMType.ULONG_TAG; - } - return tag; + public short getValue() { + return Util.getShort(heap, (short)(instPtr+TLV_HEADER_SIZE+4)); } - public static void create(KMIntegerTag[] intTagRefTable) { - byte index = 0; - while (index < intTagRefTable.length) { - intTagRefTable[index] = new KMIntegerTag(); - index++; - } + public short length() { + KMInteger obj = KMInteger.cast(getValue()); + return obj.length(); } + private static boolean validateKey(short key) { short index = (short) tags.length; while (--index >= 0) { @@ -114,22 +132,12 @@ private static boolean validateKey(short key) { return false; } - public KMInteger getValue() { - return this.val; - } - - public KMIntegerTag setValue(KMInteger val) { - this.val = val; - return this; - } - - public KMIntegerTag asULong() { - tagType = KMType.ULONG_TAG; - return this; + // TODO this should be combined with validateKey to actually validate {tagType, tagKey} pair. + private static boolean validateTagType(short tagType) { + if ((tagType == DATE_TAG) || (tagType == UINT_TAG) || (tagType == ULONG_TAG)) { + return true; + } + return false; } - public KMIntegerTag asDate() { - tagType = KMType.DATE_TAG; - return this; - } } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMKeyCharacteristics.java b/Applet/Applet/src/com/android/javacard/keymaster/KMKeyCharacteristics.java index d4c1a9b5..0421e6da 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMKeyCharacteristics.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMKeyCharacteristics.java @@ -18,60 +18,79 @@ import javacard.framework.ISO7816; import javacard.framework.ISOException; +import javacard.framework.Util; public class KMKeyCharacteristics extends KMType { public static final byte SOFTWARE_ENFORCED = 0x00; public static final byte HARDWARE_ENFORCED = 0x01; - private KMArray vals; + private static KMKeyCharacteristics prototype; + private static short instPtr; - private KMKeyCharacteristics() { - init(); + private KMKeyCharacteristics() {} + + public static short exp() { + short softEnf = KMKeyParameters.exp(); + short hwEnf = KMKeyParameters.exp(); + short arrPtr = KMArray.instance((short)2); + KMArray arr = KMArray.cast(arrPtr); + arr.add(SOFTWARE_ENFORCED, softEnf); + arr.add(HARDWARE_ENFORCED, hwEnf); + return instance(arrPtr); } - @Override - public void init() { - vals = null; + private static KMKeyCharacteristics proto(short ptr) { + if (prototype == null) prototype = new KMKeyCharacteristics(); + instPtr = ptr; + return prototype; } - @Override - public short length() { - return vals.length(); + public static short instance() { + short arrPtr = KMArray.instance((short)2); + return instance(arrPtr); + } + + public static short instance(short vals) { + short ptr = KMType.instance(KEY_CHAR_TYPE, (short)2); + if(KMArray.cast(vals).length() != 2) ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); + Util.setShort(heap, (short)(ptr + TLV_HEADER_SIZE), vals); + return ptr; } - public static KMKeyCharacteristics instance() { - KMKeyCharacteristics inst = repository.newKeyCharacteristics(); - inst.vals = KMArray.instance((short) 2); - inst.vals.add(SOFTWARE_ENFORCED, KMKeyParameters.instance()); - inst.vals.add(HARDWARE_ENFORCED, KMKeyParameters.instance()); - return inst; + public static KMKeyCharacteristics cast(short ptr) { + if (heap[ptr] != KEY_CHAR_TYPE) ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + short arrPtr = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE)); + if(heap[arrPtr] != ARRAY_TYPE) ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + return proto(ptr); } - public static KMKeyCharacteristics instance(KMArray vals) { - if (vals.length() != 2) { - ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); - } - KMKeyCharacteristics inst = repository.newKeyCharacteristics(); - inst.vals = vals; - return inst; + public short getVals() { + return Util.getShort(heap, (short) (instPtr + TLV_HEADER_SIZE)); + } + + public short length() { + short arrPtr = getVals(); + return KMArray.cast(arrPtr).length(); } - public static void create(KMKeyCharacteristics[] keyCharRefTable) { - byte index = 0; - while (index < keyCharRefTable.length) { - keyCharRefTable[index] = new KMKeyCharacteristics(); - index++; - } + public short getSoftwareEnforced() { + short arrPtr = getVals(); + return KMArray.cast(arrPtr).get(SOFTWARE_ENFORCED); } - public KMKeyParameters getSoftwareEnforced() { - return (KMKeyParameters) vals.get(SOFTWARE_ENFORCED); + public short getHardwareEnforced() { + short arrPtr = getVals(); + return KMArray.cast(arrPtr).get(HARDWARE_ENFORCED); } - public KMKeyParameters getHardwareEnforced() { - return (KMKeyParameters) vals.get(HARDWARE_ENFORCED); + public void setSoftwareEnforced(short ptr) { + KMKeyParameters.cast(ptr); + short arrPtr = getVals(); + KMArray.cast(arrPtr).add(SOFTWARE_ENFORCED, ptr); } - public KMArray getVals() { - return vals; + public void setHardwareEnforced(short ptr) { + KMKeyParameters.cast(ptr); + short arrPtr = getVals(); + KMArray.cast(arrPtr).add(HARDWARE_ENFORCED, ptr); } } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java b/Applet/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java index 44e25e98..428ef78c 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java @@ -16,53 +16,58 @@ package com.android.javacard.keymaster; + +import javacard.framework.ISO7816; +import javacard.framework.ISOException; +import javacard.framework.Util; + public class KMKeyParameters extends KMType { - private KMArray vals; + private static KMKeyParameters prototype; + private static short instPtr; - private KMKeyParameters() { - init(); - } + private KMKeyParameters() {} - @Override - public void init() { - vals = null; + private static KMKeyParameters proto(short ptr) { + if (prototype == null) prototype = new KMKeyParameters(); + instPtr = ptr; + return prototype; } - @Override - public short length() { - return vals.length(); + public static short exp() { + short arrPtr = KMArray.instance((short)9); + KMArray arr = KMArray.cast(arrPtr); + arr.add((short) 0, KMIntegerTag.exp(UINT_TAG)); + arr.add((short) 1, KMIntegerArrayTag.exp(UINT_ARRAY_TAG)); + arr.add((short) 2, KMIntegerTag.exp(ULONG_TAG)); + arr.add((short) 3, KMIntegerTag.exp(DATE_TAG)); + arr.add((short) 4, KMIntegerArrayTag.exp(ULONG_ARRAY_TAG)); + arr.add((short) 5, KMEnumTag.exp()); + arr.add((short) 6, KMEnumArrayTag.exp()); + arr.add((short) 7, KMByteTag.exp()); + arr.add((short) 8, KMBoolTag.exp()); + return instance(arrPtr); } - public static KMKeyParameters instance() { - KMKeyParameters inst = repository.newKeyParameters(); - inst.vals = KMArray.instance((short) 9); - inst.vals.add((short) 0, KMIntegerTag.instance()); - inst.vals.add((short) 1, KMIntegerArrayTag.instance()); - inst.vals.add((short) 2, KMIntegerTag.instance().asULong()); - inst.vals.add((short) 3, KMIntegerTag.instance().asDate()); - inst.vals.add((short) 4, KMIntegerArrayTag.instance().asUlongArray()); - inst.vals.add((short) 5, KMEnumTag.instance()); - inst.vals.add((short) 6, KMEnumArrayTag.instance()); - inst.vals.add((short) 7, KMByteTag.instance()); - inst.vals.add((short) 8, KMBoolTag.instance()); - return inst; + public static short instance(short vals) { + short ptr = KMType.instance(KEY_PARAM_TYPE, (short)2); + Util.setShort(heap, (short)(ptr + TLV_HEADER_SIZE), vals); + return ptr; } - public static KMKeyParameters instance(KMArray vals) { - KMKeyParameters inst = repository.newKeyParameters(); - inst.vals = vals; - return inst; + public static KMKeyParameters cast(short ptr) { + if (heap[ptr] != KEY_PARAM_TYPE) ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + short arrPtr = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE)); + if(heap[arrPtr] != ARRAY_TYPE) ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + return proto(ptr); } - public static void create(KMKeyParameters[] keyParametersRefTable) { - byte index = 0; - while (index < keyParametersRefTable.length) { - keyParametersRefTable[index] = new KMKeyParameters(); - index++; - } + public short getVals() { + return Util.getShort(heap, (short) (instPtr + TLV_HEADER_SIZE)); } - public KMArray getVals() { - return vals; + public short length() { + short arrPtr = getVals(); + return KMArray.cast(arrPtr).length(); } + } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java b/Applet/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java index 0ee85ff1..427296df 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java @@ -22,7 +22,6 @@ import javacard.framework.CardRuntimeException; import javacard.framework.ISO7816; import javacard.framework.ISOException; -import javacard.framework.JCSystem; import javacard.framework.Util; import javacardx.apdu.ExtendedLength; @@ -71,11 +70,6 @@ public class KMKeymasterApplet extends Applet implements AppletEvent, ExtendedLe // GetHwInfo information // TODO change this to just filling the buffer - private static final byte[] JavacardKeymasterDevice = { - 0x4A, 0x61, 0x76, 0x61, 0x63, 0x61, 0x72, 0x64, 0x4B, 0x65, 0x79, 0x6D, 0x61, 0x73, 0x74, 0x65, - 0x72, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, - }; - private static final byte[] Google = {0x47, 0x6F, 0x6F, 0x67, 0x6C, 0x65}; private static final short MAX_SEED_SIZE = 2048; // State of the applet. @@ -84,12 +78,15 @@ public class KMKeymasterApplet extends Applet implements AppletEvent, ExtendedLe private KMRepository repository; private byte keymasterState = ILLEGAL_STATE; private byte[] buffer; + private short bufferStartOffset; + private short bufferLength; /** Registers this applet. */ protected KMKeymasterApplet() { keymasterState = KMKeymasterApplet.INSTALL_STATE; repository = KMRepository.instance(); KMUtil.init(); + KMType.initialize(); encoder = new KMEncoder(); decoder = new KMDecoder(); register(); @@ -113,7 +110,7 @@ public static void install(byte[] bArray, short bOffset, byte bLength) { */ @Override public boolean select() { - KMRepository.instance().onSelect(); + repository.onSelect(); if (keymasterState == KMKeymasterApplet.INSTALL_STATE) { keymasterState = KMKeymasterApplet.FIRST_SELECT_STATE; } else if (keymasterState == KMKeymasterApplet.INACTIVE_STATE) { @@ -127,7 +124,7 @@ public boolean select() { /** De-selects this applet. */ @Override public void deselect() { - KMRepository.instance().onDeselect(); + repository.onDeselect(); if (keymasterState == KMKeymasterApplet.ACTIVE_STATE) { keymasterState = KMKeymasterApplet.INACTIVE_STATE; } @@ -136,7 +133,7 @@ public void deselect() { /** Uninstalls the applet after cleaning the repository. */ @Override public void uninstall() { - KMRepository.instance().onUninstall(); + repository.onUninstall(); if (keymasterState != KMKeymasterApplet.UNINSTALLED_STATE) { keymasterState = KMKeymasterApplet.UNINSTALLED_STATE; } @@ -149,10 +146,7 @@ public void uninstall() { */ @Override public void process(APDU apdu) { - KMRepository.instance().onProcess(); - if (buffer == null) { - buffer = JCSystem.makeTransientByteArray(MAX_LENGTH, JCSystem.CLEAR_ON_RESET); - } + repository.onProcess(); // Verify whether applet is in correct state. if ((keymasterState != KMKeymasterApplet.ACTIVE_STATE) && (keymasterState != KMKeymasterApplet.FIRST_SELECT_STATE)) { @@ -188,177 +182,174 @@ public void process(APDU apdu) { // Process the apdu try { // Handle the command - handle(apduIns, apdu); + switch (apduIns) { + case INS_GENERATE_KEY_CMD: + processGenerateKey(apdu); + break; + case INS_IMPORT_KEY_CMD: + processImportKeyCmd(apdu); + break; + case INS_IMPORT_WRAPPED_KEY_CMD: + processImportWrappedKeyCmd(apdu); + break; + case INS_EXPORT_KEY_CMD: + processExportKeyCmd(apdu); + break; + case INS_ATTEST_KEY_CMD: + processAttestKeyCmd(apdu); + break; + case INS_UPGRADE_KEY_CMD: + processUpgradeKeyCmd(apdu); + break; + case INS_DELETE_KEY_CMD: + processDeleteKeyCmd(apdu); + break; + case INS_DELETE_ALL_KEYS_CMD: + processDeleteAllKeysCmd(apdu); + break; + case INS_ADD_RNG_ENTROPY_CMD: + processAddRngEntropyCmd(apdu); + break; + case INS_COMPUTE_SHARED_HMAC_CMD: + processComputeSharedHmacCmd(apdu); + break; + case INS_DESTROY_ATT_IDS_CMD: + processDestroyAttIdsCmd(apdu); + break; + case INS_VERIFY_AUTHORIZATION_CMD: + processVerifyAuthenticationCmd(apdu); + break; + case INS_GET_HMAC_SHARING_PARAM_CMD: + processGetHmacSharingParamCmd(apdu); + break; + case INS_GET_KEY_CHARACTERISTICS_CMD: + processGetKeyCharacteristicsCmd(apdu); + break; + case INS_GET_HW_INFO_CMD: + processGetHwInfoCmd(apdu); + break; + case INS_BEGIN_OPERATION_CMD: + processBeginOperationCmd(apdu); + break; + case INS_UPDATE_OPERATION_CMD: + processUpdateOperationCmd(apdu); + break; + case INS_FINISH_OPERATION_CMD: + processFinishOperationCmd(apdu); + break; + case INS_ABORT_OPERATION_CMD: + processAbortOperationCmd(apdu); + break; + case INS_PROVISION_CMD: + processProvisionCmd(apdu); + break; + default: + ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED); + } + } catch (CardRuntimeException exception) { if (!(KMException.handle(exception.getReason()))) { CardRuntimeException.throwIt(exception.getReason()); } } finally { - // Reset the buffer. - Util.arrayFillNonAtomic(buffer, (short) 0, MAX_LENGTH, (byte) 0); + repository.clean(); } } /** Sends a response, may be extended response, as requested by the command. */ - private void sendOutgoing(byte[] srcBuffer, short srcLength, APDU apdu) { - if (srcLength > MAX_LENGTH) { + private void sendOutgoing(APDU apdu) { + if (bufferLength > MAX_LENGTH) { ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); } // Send data apdu.setOutgoing(); - apdu.setOutgoingLength(srcLength); - apdu.sendBytesLong(srcBuffer, (short) 0, srcLength); + apdu.setOutgoingLength(bufferLength); + apdu.sendBytesLong(buffer, bufferStartOffset, bufferLength); } /** Receives data, which can be extended data, as requested by the command instance. */ - private short receiveIncoming(byte[] destBuffer, APDU apdu) { - // Initialize source + private void receiveIncoming(APDU apdu) { byte[] srcBuffer = apdu.getBuffer(); - // Initialize destination - short destOffset = (short) 0; - // Receive data short recvLen = apdu.setIncomingAndReceive(); short srcOffset = apdu.getOffsetCdata(); - short srcLength = apdu.getIncomingLength(); - if (srcLength > MAX_LENGTH) { + bufferLength = apdu.getIncomingLength(); + bufferStartOffset = repository.alloc(bufferLength); + buffer = repository.getHeap(); + short index = bufferStartOffset; + // Receive data + if (bufferLength > MAX_LENGTH) { ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); } - while (recvLen > 0) { - Util.arrayCopyNonAtomic(srcBuffer, srcOffset, destBuffer, destOffset, recvLen); - destOffset += recvLen; + while (recvLen > 0 && ((short)(index - bufferStartOffset) < bufferLength)) { + Util.arrayCopyNonAtomic(srcBuffer, srcOffset, buffer, index, recvLen); + index += recvLen; recvLen = apdu.receiveBytes(srcOffset); } - return srcLength; } - // Commands - private void handle(byte ins, APDU apdu) { - switch (ins) { - case INS_GENERATE_KEY_CMD: - processGenerateKey(apdu); - break; - case INS_IMPORT_KEY_CMD: - processImportKeyCmd(apdu); - break; - case INS_IMPORT_WRAPPED_KEY_CMD: - processImportWrappedKeyCmd(apdu); - break; - case INS_EXPORT_KEY_CMD: - processExportKeyCmd(apdu); - break; - case INS_ATTEST_KEY_CMD: - processAttestKeyCmd(apdu); - break; - case INS_UPGRADE_KEY_CMD: - processUpgradeKeyCmd(apdu); - break; - case INS_DELETE_KEY_CMD: - processDeleteKeyCmd(apdu); - break; - case INS_DELETE_ALL_KEYS_CMD: - processDeleteAllKeysCmd(apdu); - break; - case INS_ADD_RNG_ENTROPY_CMD: - processAddRngEntropyCmd(apdu); - break; - case INS_COMPUTE_SHARED_HMAC_CMD: - processComputeSharedHmacCmd(apdu); - break; - case INS_DESTROY_ATT_IDS_CMD: - processDestroyAttIdsCmd(apdu); - break; - case INS_VERIFY_AUTHORIZATION_CMD: - processVerifyAuthenticationCmd(apdu); - break; - case INS_GET_HMAC_SHARING_PARAM_CMD: - processGetHmacSharingParamCmd(apdu); - break; - case INS_GET_KEY_CHARACTERISTICS_CMD: - processGetKeyCharacteristicsCmd(apdu); - break; - case INS_GET_HW_INFO_CMD: - processGetHwInfoCmd(apdu); - break; - case INS_BEGIN_OPERATION_CMD: - processBeginOperationCmd(apdu); - break; - case INS_UPDATE_OPERATION_CMD: - processUpdateOperationCmd(apdu); - break; - case INS_FINISH_OPERATION_CMD: - processFinishOperationCmd(apdu); - break; - case INS_ABORT_OPERATION_CMD: - processAbortOperationCmd(apdu); - break; - case INS_PROVISION_CMD: - processProvisionCmd(apdu); - break; - default: - ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED); - } - } - - private short processProvisionCmd(APDU apdu) { - // Receive the incoming request fully from the master. - short length = receiveIncoming(buffer, apdu); + private void processProvisionCmd(APDU apdu) { + // Receive the incoming request fully from the master into buffer. + receiveIncoming(apdu); // Re-purpose the apdu buffer as scratch pad. byte[] scratchPad = apdu.getBuffer(); Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) apdu.getBuffer().length, (byte) 0); // Argument 1 - KMKeyParameters keyparams = KMKeyParameters.instance(); + short keyparams = KMKeyParameters.exp(); // Argument 2 - KMEnum keyFormat = KMEnum.instance().setType(KMType.KEY_FORMAT); + short keyFormat = KMEnum.instance(KMType.KEY_FORMAT); // Argument 3 - KMByteBlob keyBlob = KMByteBlob.instance(); + short keyBlob = KMByteBlob.exp(); // Array of expected arguments - KMArray argsProto = - KMArray.instance((short) 3) - .add((short) 0, keyparams) - .add((short) 1, keyFormat) - .add((short) 2, keyBlob); + short argsProto = KMArray.instance((short) 3); + KMArray.cast(argsProto).add((short) 0, keyparams); + KMArray.cast(argsProto).add((short) 1, keyFormat); + KMArray.cast(argsProto).add((short) 2, keyBlob); // Decode the argument - KMArray args = decoder.decode(argsProto, buffer, (short) 0, length); + short args = decoder.decode(argsProto, buffer, bufferStartOffset, bufferLength); // TODO execute the function // Change the state to ACTIVE if (keymasterState == KMKeymasterApplet.FIRST_SELECT_STATE) { keymasterState = KMKeymasterApplet.ACTIVE_STATE; } - // nothing to return - return 0; } private void processGetHwInfoCmd(APDU apdu) { // No arguments expected + final byte[] JavacardKeymasterDevice = { + 0x4A, 0x61, 0x76, 0x61, 0x63, 0x61, 0x72, 0x64, 0x4B, 0x65, 0x79, 0x6D, 0x61, 0x73, 0x74, 0x65, + 0x72, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, + }; + final byte[] Google = {0x47, 0x6F, 0x6F, 0x67, 0x6C, 0x65}; + // Make the response - KMArray resp = - KMArray.instance((short) 3) - .add((short) 0, KMEnum.instance(KMType.HARDWARE_TYPE, KMType.STRONGBOX)) - .add( - (short) 1, - KMByteBlob.instance( - JavacardKeymasterDevice, (short) 0, (short) JavacardKeymasterDevice.length)) - .add((short) 2, KMByteBlob.instance(Google, (short) 0, (short) Google.length)); - // Reuse the buffer - Util.arrayFillNonAtomic(buffer, (short) 0, MAX_LENGTH, (byte) 0); - // Encode the response - short len = encoder.encode(resp, buffer, (short) 0, MAX_LENGTH); - sendOutgoing(buffer, len, apdu); + short respPtr = KMArray.instance((short) 3); + KMArray resp = KMArray.cast(respPtr); + resp.add((short) 0, KMEnum.instance(KMType.HARDWARE_TYPE, KMType.STRONGBOX)); + resp.add((short) 1, KMByteBlob.instance( + JavacardKeymasterDevice, (short) 0, (short) JavacardKeymasterDevice.length)); + resp.add((short) 2, KMByteBlob.instance(Google, (short) 0, (short) Google.length)); + //TODO change from MAX_LENGTH to actual length. + buffer = repository.getHeap(); + bufferStartOffset = repository.alloc((short)128); + // Encode the response - actual bufferLength is 86 + bufferLength = encoder.encode(respPtr, buffer, bufferStartOffset, MAX_LENGTH); + // send buffer to master + sendOutgoing(apdu); } - private short processAddRngEntropyCmd(APDU apdu) { + private void processAddRngEntropyCmd(APDU apdu) { // Receive the incoming request fully from the master. - short length = receiveIncoming(buffer, apdu); + receiveIncoming(apdu); // Re-purpose the apdu buffer as scratch pad. byte[] scratchPad = apdu.getBuffer(); Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) apdu.getBuffer().length, (byte) 0); // Argument 1 - KMArray argsProto = KMArray.instance((short) 1).add((short) 0, KMByteBlob.instance()); - + short argsProto = KMArray.instance((short) 1); + KMArray.cast(argsProto).add((short) 0, KMByteBlob.exp()); // Decode the argument - KMArray args = decoder.decode(argsProto, buffer, (short) 0, length); + short args = decoder.decode(argsProto, buffer, bufferStartOffset, bufferLength); // Process - KMByteBlob blob = (KMByteBlob) args.get((short) 0); + KMByteBlob blob = KMByteBlob.cast(KMArray.cast(args).get((short) 0)); // Maximum 2KiB of seed is allowed. if (blob.length() > MAX_SEED_SIZE) { ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); @@ -366,8 +357,8 @@ private short processAddRngEntropyCmd(APDU apdu) { // Get existing entropy pool. byte[] entPool = KMUtil.getEntropyPool(); // Create new temporary pool. - byte[] heapRef = repository.getByteHeapRef(); - short poolStart = repository.newByteArray((short) entPool.length); + byte[] heapRef = repository.getHeap(); + short poolStart = repository.alloc((short) entPool.length); // Populate the new pool with the entropy which is derived from current entropy pool. KMUtil.newRandomNumber(heapRef, poolStart, (short) entPool.length); // Copy the entropy to the current pool - updates the entropy pool. @@ -384,75 +375,73 @@ private short processAddRngEntropyCmd(APDU apdu) { randIndex = 0; } } - // Nothing to return - return 0; } - private short processAbortOperationCmd(APDU apdu) { - return 0; + private void processAbortOperationCmd(APDU apdu) { + } - private short processFinishOperationCmd(APDU apdu) { - return 0; + private void processFinishOperationCmd(APDU apdu) { + } - private short processUpdateOperationCmd(APDU apdu) { - return 0; + private void processUpdateOperationCmd(APDU apdu) { + } - private short processBeginOperationCmd(APDU apdu) { - return 0; + private void processBeginOperationCmd(APDU apdu) { + } - private short processGetKeyCharacteristicsCmd(APDU apdu) { - return 0; + private void processGetKeyCharacteristicsCmd(APDU apdu) { + } - private short processGetHmacSharingParamCmd(APDU apdu) { - return 0; + private void processGetHmacSharingParamCmd(APDU apdu) { + } - private short processVerifyAuthenticationCmd(APDU apdu) { - return 0; + private void processVerifyAuthenticationCmd(APDU apdu) { + } - private short processDestroyAttIdsCmd(APDU apdu) { - return 0; + private void processDestroyAttIdsCmd(APDU apdu) { + } - private short processComputeSharedHmacCmd(APDU apdu) { - return 0; + private void processComputeSharedHmacCmd(APDU apdu) { + } - private short processDeleteAllKeysCmd(APDU apdu) { - return 0; + private void processDeleteAllKeysCmd(APDU apdu) { + } - private short processDeleteKeyCmd(APDU apdu) { - return 0; + private void processDeleteKeyCmd(APDU apdu) { + } - private short processUpgradeKeyCmd(APDU apdu) { - return 0; + private void processUpgradeKeyCmd(APDU apdu) { + } - private short processAttestKeyCmd(APDU apdu) { - return 0; + private void processAttestKeyCmd(APDU apdu) { + } - private short processExportKeyCmd(APDU apdu) { - return 0; + private void processExportKeyCmd(APDU apdu) { + } - private short processImportWrappedKeyCmd(APDU apdu) { - return 0; + private void processImportWrappedKeyCmd(APDU apdu) { + } - private short processImportKeyCmd(APDU apdu) { - return 0; + private void processImportKeyCmd(APDU apdu) { + } - private short processGenerateKey(APDU apdu) { - return 0; + private void processGenerateKey(APDU apdu) { + } } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMRepository.java b/Applet/Applet/src/com/android/javacard/keymaster/KMRepository.java index b71aea01..3492d4b5 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMRepository.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMRepository.java @@ -28,63 +28,11 @@ // to handle onInstall and onSelect. public class KMRepository { - private static final byte REF_TABLE_SIZE = 5; - private static final short HEAP_SIZE = 0x1000; - private static final byte INT_TABLE_SIZE = 10; - private static final byte TYPE_ARRAY_SIZE = 100; - private static final byte INT_SIZE = 4; - private static final byte LONG_SIZE = 8; + public static final short HEAP_SIZE = 0x1000; private static KMRepository repository; - private AESKey masterKey = null; - private KMEncoder encoder = null; - private KMDecoder decoder = null; - - private KMByteBlob[] byteBlobRefTable = null; - private byte blobRefIndex = 0; - private KMInteger[] integerRefTable = null; - private byte intRefIndex = 0; - private KMArray[] arrayRefTable = null; - private byte arrayRefIndex = 0; - private KMVector[] vectorRefTable = null; - private byte vectorRefIndex = 0; - private KMEnum[] enumRefTable = null; - private byte enumRefIndex = 0; - private KMByteTag[] byteTagRefTable = null; - private byte byteTagRefIndex = 0; - private KMIntegerTag[] intTagRefTable = null; - private byte intTagRefIndex = 0; - private KMIntegerArrayTag[] intArrayTagRefTable = null; - private byte intArrayTagRefIndex = 0; - private KMEnumTag[] enumTagRefTable = null; - private byte enumTagRefIndex = 0; - private KMEnumArrayTag[] enumArrayTagRefTable = null; - private byte enumArrayTagRefIndex = 0; - private KMBoolTag[] boolTagRefTable = null; - private byte boolTagRefIndex = 0; - private KMKeyParameters[] keyParametersRefTable = null; - private byte keyParametersRefIndex = 0; - private KMKeyCharacteristics[] keyCharRefTable = null; - private byte keyCharRefIndex = 0; - private KMVerificationToken[] verTokenRefTable = null; - private byte verTokenRefIndex = 0; - private KMHmacSharingParameters[] hmacSharingParamsRefTable = null; - private byte hmacSharingParamsRefIndex = 0; - private KMHardwareAuthToken[] hwAuthTokenRefTable = null; - private byte hwAuthTokenRefIndex = 0; - private KMOperationState[] opStateRefTable = null; - private byte opStateRefIndex = 0; - private KMType[] typeRefTable = null; - private byte typeRefIndex = 0; - - private byte[] byteHeap = null; - private short byteHeapIndex = 0; - private Object[] uint32Array = null; - private byte uint32Index = 0; - private Object[] uint64Array = null; - private byte uint64Index = 0; - private KMOperationState[] operationStateTable = null; - private byte[] entropyPool = null; - private byte[] counter; + private AESKey masterKey; + private byte[] heap; + private short heapIndex; public static KMRepository instance() { if (repository == null) { @@ -94,380 +42,42 @@ public static KMRepository instance() { } public KMRepository(){ - initialize(); - } - - private void initialize() { - // Initialize buffers and context. - JCSystem.beginTransaction(); - encoder = new KMEncoder(); - decoder = new KMDecoder(); - operationStateTable = new KMOperationState[4]; + heap = JCSystem.makeTransientByteArray(HEAP_SIZE, JCSystem.CLEAR_ON_RESET); // Initialize masterkey - AES 256 bit key. if (masterKey == null) { masterKey = - (AESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_AES, KeyBuilder.LENGTH_AES_256, false); - } - // Initialize types - KMType.initialize(this); - byteBlobRefTable = new KMByteBlob[(short)(REF_TABLE_SIZE*4)]; - KMByteBlob.create(byteBlobRefTable); - integerRefTable = new KMInteger[REF_TABLE_SIZE]; - KMInteger.create(integerRefTable); - arrayRefTable = new KMArray[(short)(REF_TABLE_SIZE*4)]; - KMArray.create(arrayRefTable); - vectorRefTable = new KMVector[REF_TABLE_SIZE]; - KMVector.create(vectorRefTable); - enumRefTable = new KMEnum[(short)(REF_TABLE_SIZE*2)]; - KMEnum.create(enumRefTable); - byteTagRefTable = new KMByteTag[REF_TABLE_SIZE]; - KMByteTag.create(byteTagRefTable); - intTagRefTable = new KMIntegerTag[REF_TABLE_SIZE]; - KMIntegerTag.create(intTagRefTable); - intArrayTagRefTable = new KMIntegerArrayTag[REF_TABLE_SIZE]; - KMIntegerArrayTag.create(intArrayTagRefTable); - enumTagRefTable = new KMEnumTag[REF_TABLE_SIZE]; - KMEnumTag.create(enumTagRefTable); - enumArrayTagRefTable = new KMEnumArrayTag[REF_TABLE_SIZE]; - KMEnumArrayTag.create(enumArrayTagRefTable); - boolTagRefTable = new KMBoolTag[REF_TABLE_SIZE]; - KMBoolTag.create(boolTagRefTable); - keyParametersRefTable = new KMKeyParameters[REF_TABLE_SIZE]; - KMKeyParameters.create(keyParametersRefTable); - keyCharRefTable = new KMKeyCharacteristics[REF_TABLE_SIZE]; - KMKeyCharacteristics.create(keyCharRefTable); - verTokenRefTable = new KMVerificationToken[REF_TABLE_SIZE]; - KMVerificationToken.create(verTokenRefTable); - hmacSharingParamsRefTable = new KMHmacSharingParameters[REF_TABLE_SIZE]; - KMHmacSharingParameters.create(hmacSharingParamsRefTable); - hwAuthTokenRefTable = new KMHardwareAuthToken[REF_TABLE_SIZE]; - KMHardwareAuthToken.create(hwAuthTokenRefTable); - opStateRefTable = new KMOperationState[REF_TABLE_SIZE]; - KMOperationState.create(opStateRefTable); - - byteHeap = new byte[HEAP_SIZE]; - uint32Array = new Object[INT_TABLE_SIZE]; - uint64Array = new Object[INT_TABLE_SIZE]; - typeRefTable = new KMType[TYPE_ARRAY_SIZE]; - - short index = 0; - while (index < INT_TABLE_SIZE) { - uint32Array[index] = new byte[INT_SIZE]; - uint64Array[index] = new byte[LONG_SIZE]; - index++; + (AESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_AES, KeyBuilder.LENGTH_AES_256, false); } - - JCSystem.commitTransaction(); - } - - public KMEncoder getEncoder() { - return encoder; - } - - public KMDecoder getDecoder() { - return decoder; } public void onUninstall() { masterKey = null; } - public void onProcess() { - reset(); - } + public void onProcess() {} - private void reset() { - Util.arrayFillNonAtomic(byteHeap, (short) 0, (short) byteHeap.length, (byte) 0); - byteHeapIndex = 0; - short index = 0; - while (index < typeRefTable.length) { - typeRefTable[index] = null; - index++; - } - typeRefIndex = 0; - index = 0; - while (index < uint32Array.length) { - byte[] num = (byte[]) uint32Array[index]; - byte numIndex = 0; - while (numIndex < INT_SIZE) { - num[numIndex] = 0; - numIndex++; - } - index++; - } - uint32Index = 0; - index = 0; - while (index < uint64Array.length) { - byte[] num = (byte[]) uint64Array[index]; - byte numIndex = 0; - while (numIndex < LONG_SIZE) { - num[numIndex] = 0; - numIndex++; - } - index++; - } - uint64Index = 0; - resetTypeObjects(byteBlobRefTable); - resetTypeObjects(integerRefTable); - resetTypeObjects(enumRefTable); - resetTypeObjects(byteTagRefTable); - resetTypeObjects(boolTagRefTable); - resetTypeObjects(arrayRefTable); - resetTypeObjects(enumTagRefTable); - resetTypeObjects(enumArrayTagRefTable); - resetTypeObjects(intTagRefTable); - resetTypeObjects(intArrayTagRefTable); - resetTypeObjects(vectorRefTable); - resetTypeObjects(keyCharRefTable); - resetTypeObjects(keyParametersRefTable); - resetTypeObjects(hmacSharingParamsRefTable); - resetTypeObjects(hwAuthTokenRefTable); - resetTypeObjects(verTokenRefTable); + public void clean(){ + Util.arrayFillNonAtomic(heap, (short) 0, heapIndex, (byte) 0); + heapIndex = 0; } - public void resetTypeObjects(KMType[] type){ - byte index = 0; - while(index < type.length){ - type[index].init(); - index++; - } - } - public void onDeselect() { - // TODO clear operation state? - } + public void onDeselect() {} - public void onSelect() { - // Nothing to be done currently. - } + public void onSelect() {} public AESKey getMasterKey() { return masterKey; } - // Allocate 4 bytes or 8 bytes buffer - public byte[] newIntegerArray(short length) { - if (length == 4) { - if (uint32Index >= uint32Array.length) { - // TODO this is placeholder exception value. This needs to be replaced by 910E, 91A1 or 9210 - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - byte[] ret = (byte[]) uint32Array[uint32Index]; - uint32Index++; - return ret; - } else if (length == 8) { - if (uint64Index >= uint64Array.length) { - // TODO this is placeholder exception value. This needs to be replaced by 910E, 91A1 or 9210 - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - byte[] ret = (byte[]) uint64Array[uint64Index]; - uint64Index++; - return ret; - } else { - ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); - } - return null;// this will never be executed. - } - - public KMByteBlob newByteBlob() { - if (blobRefIndex >= byteBlobRefTable.length) { - // TODO this is placeholder exception value. - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - KMByteBlob ret = byteBlobRefTable[blobRefIndex]; - blobRefIndex++; - return ret; - } - - public KMInteger newInteger() { - if (intRefIndex >= integerRefTable.length) { - // TODO this is placeholder exception value. - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - KMInteger ret = integerRefTable[intRefIndex]; - intRefIndex++; - return ret; - } - - public KMEnumTag newEnumTag() { - if (enumTagRefIndex >= enumTagRefTable.length) { - // TODO this is placeholder exception value. - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - KMEnumTag ret = enumTagRefTable[enumTagRefIndex]; - enumTagRefIndex++; - return ret; - } - - public KMEnumArrayTag newEnumArrayTag() { - if (enumArrayTagRefIndex >= enumArrayTagRefTable.length) { - // TODO this is placeholder exception value. - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - KMEnumArrayTag ret = enumArrayTagRefTable[enumArrayTagRefIndex]; - enumArrayTagRefIndex++; - return ret; - } - - public KMIntegerTag newIntegerTag() { - if (intTagRefIndex >= intTagRefTable.length) { - // TODO this is placeholder exception value. - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - KMIntegerTag ret = intTagRefTable[intTagRefIndex]; - intTagRefIndex++; - return ret; - } - - public KMIntegerArrayTag newIntegerArrayTag() { - if (intArrayTagRefIndex >= intArrayTagRefTable.length) { - // TODO this is placeholder exception value. - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - KMIntegerArrayTag ret = intArrayTagRefTable[intArrayTagRefIndex]; - intArrayTagRefIndex++; - return ret; - } - - public KMBoolTag newBoolTag() { - if (boolTagRefIndex >= boolTagRefTable.length) { - // TODO this is placeholder exception value. - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - KMBoolTag ret = boolTagRefTable[boolTagRefIndex]; - boolTagRefIndex++; - return ret; - } - - public KMByteTag newByteTag() { - if (byteTagRefIndex >= byteTagRefTable.length) { - // TODO this is placeholder exception value. - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - KMByteTag ret = byteTagRefTable[byteTagRefIndex]; - byteTagRefIndex++; - return ret; - } - - public KMKeyParameters newKeyParameters() { - if (keyParametersRefIndex >= keyParametersRefTable.length) { - // TODO this is placeholder exception value. - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - KMKeyParameters ret = keyParametersRefTable[keyParametersRefIndex]; - keyParametersRefIndex++; - return ret; - } - - public KMArray newArray() { - if (arrayRefIndex >= arrayRefTable.length) { - // TODO this is placeholder exception value. - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - KMArray ret = arrayRefTable[arrayRefIndex]; - arrayRefIndex++; - return ret; - } - - public KMKeyCharacteristics newKeyCharacteristics() { - if (keyCharRefIndex >= keyCharRefTable.length) { - // TODO this is placeholder exception value. - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - KMKeyCharacteristics ret = keyCharRefTable[keyCharRefIndex]; - keyCharRefIndex++; - return ret; - } - - public KMHardwareAuthToken newHwAuthToken() { - if (hwAuthTokenRefIndex >= hwAuthTokenRefTable.length) { - // TODO this is placeholder exception value. - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - KMHardwareAuthToken ret = hwAuthTokenRefTable[hwAuthTokenRefIndex]; - hwAuthTokenRefIndex++; - return ret; - } - - public KMHmacSharingParameters newHmacSharingParameters() { - if (hmacSharingParamsRefIndex >= hmacSharingParamsRefTable.length) { - // TODO this is placeholder exception value. - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - KMHmacSharingParameters ret = hmacSharingParamsRefTable[hmacSharingParamsRefIndex]; - hmacSharingParamsRefIndex++; - return ret; - } - - public KMVerificationToken newVerificationToken() { - if (verTokenRefIndex >= verTokenRefTable.length) { - // TODO this is placeholder exception value. + public short alloc(short length) { + if (((short) (heapIndex + length)) > heap.length) { ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); } - KMVerificationToken ret = verTokenRefTable[verTokenRefIndex]; - verTokenRefIndex++; - return ret; + heapIndex += length; + return (short) (heapIndex - length); } - public KMOperationState newOperationState() { - if (opStateRefIndex >= opStateRefTable.length) { - // TODO this is placeholder exception value. - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - KMOperationState ret = operationStateTable[opStateRefIndex]; - opStateRefIndex++; - return ret; - } - - public void releaseOperationState(KMOperationState state){ - opStateRefIndex--; - if(opStateRefIndex <0){ - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - opStateRefTable[opStateRefIndex] = state; - } - public KMVector newVector() { - if (vectorRefIndex >= vectorRefTable.length) { - // TODO this is placeholder exception value. - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - KMVector ret = vectorRefTable[vectorRefIndex]; - vectorRefIndex++; - return ret; - } - - public KMEnum newEnum() { - if (enumRefIndex >= enumRefTable.length) { - // TODO this is placeholder exception value. - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - KMEnum ret = enumRefTable[enumRefIndex]; - enumRefIndex++; - return ret; - } - - public KMType[] getTypeArrayRef(){ - return typeRefTable; - } - - public byte[] getByteHeapRef(){ - return byteHeap; - } - - public short newTypeArray(short length) { - if (((short) (typeRefIndex + length)) >= typeRefTable.length) { - // TODO this is placeholder exception value. - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - typeRefIndex += length; - return (short) (typeRefIndex - length); - } - - public short newByteArray(short length) { - if (((short) (byteHeapIndex + length)) >= byteHeap.length) { - // TODO this is placeholder exception value. - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - byteHeapIndex += length; - return (short) (byteHeapIndex - length); + public byte[] getHeap(){ + return heap; } } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMTag.java b/Applet/Applet/src/com/android/javacard/keymaster/KMTag.java index c6a1b5ae..709f56f8 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMTag.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMTag.java @@ -16,8 +16,9 @@ package com.android.javacard.keymaster; -public abstract class KMTag extends KMType { - public abstract short getTagType(); +import javacard.framework.Util; - public abstract short getKey(); +public class KMTag extends KMType { + public static short getTagType(short ptr){return Util.getShort(heap, (short)(ptr+TLV_HEADER_SIZE));} + public static short getKey(short ptr){return Util.getShort(heap, (short)(ptr+TLV_HEADER_SIZE+2));} } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMType.java b/Applet/Applet/src/com/android/javacard/keymaster/KMType.java index 2aa08271..2dc74dfd 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMType.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMType.java @@ -16,8 +16,27 @@ package com.android.javacard.keymaster; +import javacard.framework.ISO7816; +import javacard.framework.ISOException; +import javacard.framework.Util; + public abstract class KMType { - public static final short TAG_TYPE_MASK = (short) 0xF000; + public static final short INVALID_VALUE = (short)0x8000; + protected static final byte TLV_HEADER_SIZE = 3; + + // Types + public static final byte BYTE_BLOB_TYPE = 0x01; + public static final byte INTEGER_TYPE = 0x02; + public static final byte ENUM_TYPE = 0x03; + public static final byte TAG_TYPE = 0x04; + public static final byte ARRAY_TYPE = 0x05; + public static final byte KEY_PARAM_TYPE = 0x06; + public static final byte KEY_CHAR_TYPE = 0x07; + public static final byte HW_AUTH_TOKEN_TYPE = 0x08; + public static final byte VERIFICATION_TOKEN_TYPE = 0x09; + public static final byte HMAC_SHARING_PARAM_TYPE = 0x0A; + + // Tags public static final short INVALID_TAG = 0x0000; public static final short ENUM_TAG = 0x1000; public static final short ENUM_ARRAY_TAG = 0x2000; @@ -29,6 +48,7 @@ public abstract class KMType { public static final short BIGNUM_TAG = (short) 0x8000; public static final short BYTES_TAG = (short) 0x9000; public static final short ULONG_ARRAY_TAG = (short) 0xA000; + public static final short TAG_TYPE_MASK = (short) 0xF000; // Enum Tag // Algorithm Enum Tag key and values @@ -225,12 +245,29 @@ public abstract class KMType { public static final byte NO_VALUE = (byte) 0xff; protected static KMRepository repository; + protected static byte[] heap; - public static void initialize(KMRepository repo) { - KMType.repository = repo; + public static void initialize() { + KMType.repository = KMRepository.instance(); + KMType.heap = repository.getHeap(); } - public abstract void init(); + public static byte getType(short ptr){return heap[ptr];} + public static short length(short ptr){return Util.getShort(heap, (short)(ptr+1));} + public static short getValue(short ptr){return Util.getShort(heap, (short)(ptr+TLV_HEADER_SIZE));} + + protected static short instance(byte type, short length){ + if (length <= 0) ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); + short ptr = repository.alloc((short) (length + TLV_HEADER_SIZE)); + heap[ptr] = type; + Util.setShort(heap, (short) (ptr + 1), length); + return ptr; + } - public abstract short length(); + protected static short exp(byte type) { + short ptr = repository.alloc(TLV_HEADER_SIZE); + heap[ptr] = type; + Util.setShort(heap, (short) (ptr + 1), INVALID_VALUE); + return ptr; + } } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMUtil.java b/Applet/Applet/src/com/android/javacard/keymaster/KMUtil.java index 0d1080df..599da84e 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMUtil.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMUtil.java @@ -2,6 +2,7 @@ import javacard.framework.ISO7816; import javacard.framework.ISOException; +import javacard.framework.JCSystem; import javacard.framework.Util; import javacard.security.AESKey; import javacard.security.CryptoException; @@ -18,8 +19,8 @@ public class KMUtil { private static Cipher aesCbc; private static byte[] entropyPool; public static void init() { - entropyPool = new byte[ENTROPY_POOL_SIZE]; - counter = KMRepository.instance().newIntegerArray((short) 8); + entropyPool = JCSystem.makeTransientByteArray(ENTROPY_POOL_SIZE, JCSystem.CLEAR_ON_RESET); + counter = JCSystem.makeTransientByteArray((short)8, JCSystem.CLEAR_ON_RESET); KMUtil.initEntropyPool(entropyPool); try { //Note: ALG_AES_BLOCK_128_CBC_NOPAD not supported by simulator. @@ -58,9 +59,9 @@ public static void initEntropyPool(byte[] pool) { // 8 byte counter and 16 byte block size. public static void newRandomNumber(byte[] num, short startOff, short length) { KMRepository repository = KMRepository.instance(); - byte[] bufPtr = repository.getByteHeapRef(); - short countBufInd = repository.newByteArray(AES_BLOCK_SIZE); - short randBufInd = repository.newByteArray(AES_BLOCK_SIZE); + byte[] bufPtr = repository.getHeap(); + short countBufInd = repository.alloc(AES_BLOCK_SIZE); + short randBufInd = repository.alloc(AES_BLOCK_SIZE); short len = AES_BLOCK_SIZE; aesKey.setKey(entropyPool, (short) 0); aesCbc.init(aesKey, Cipher.MODE_ENCRYPT, aesICV, (short)0, (short)16); diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMVector.java b/Applet/Applet/src/com/android/javacard/keymaster/KMVector.java deleted file mode 100644 index fb9d20f5..00000000 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMVector.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright(C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.javacard.keymaster; - -public class KMVector extends KMType { - private KMType type; - private KMArray vals; - - private KMVector() { - init(); - } - - @Override - public void init() { - vals = null; - type = null; - } - - @Override - public short length() { - return vals.length(); - } - - public static KMVector instance(KMType type) { - KMVector inst = repository.newVector(); - inst.type = type; - inst.vals = KMArray.instance(); - return inst; - } - - public static KMVector instance(KMType type, short length) { - KMVector inst = repository.newVector(); - inst.type = type; - inst.vals = KMArray.instance(length); - return inst; - } - - public static void create(KMVector[] vectorRefTable) { - byte index = 0; - while (index < vectorRefTable.length) { - vectorRefTable[index] = new KMVector(); - index++; - } - } - - public KMArray getVals() { - return vals; - } - - public KMVector withLength(short length) { - this.vals.withLength(length); - return this; - } - - public KMVector add(short index, KMType val) { - vals.add(index, val); - return this; - } - - public KMType get(short index) { - return vals.get(index); - } - - public void setVals(KMArray vals) { - this.vals = vals; - } - - public KMType getType() { - return type; - } -} diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMVerificationToken.java b/Applet/Applet/src/com/android/javacard/keymaster/KMVerificationToken.java index d47c80e2..eefa9e60 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMVerificationToken.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMVerificationToken.java @@ -18,6 +18,7 @@ import javacard.framework.ISO7816; import javacard.framework.ISOException; +import javacard.framework.Util; public class KMVerificationToken extends KMType { public static final byte CHALLENGE = 0x00; @@ -25,71 +26,113 @@ public class KMVerificationToken extends KMType { public static final byte PARAMETERS_VERIFIED = 0x02; public static final byte SECURITY_LEVEL = 0x03; public static final byte MAC = 0x04; - private KMArray vals; - private KMVerificationToken() { - init(); + private static KMVerificationToken prototype; + private static short instPtr; + + private KMVerificationToken() {} + + public static short exp() { + short arrPtr = KMArray.instance((short)5); + KMArray arr = KMArray.cast(arrPtr); + arr.add(CHALLENGE, KMInteger.exp()); + arr.add(TIMESTAMP, KMInteger.exp()); + arr.add(PARAMETERS_VERIFIED, KMKeyParameters.exp()); + arr.add(SECURITY_LEVEL, KMEnumTag.instance(KMType.HARDWARE_TYPE)); + arr.add(MAC, KMByteBlob.exp()); + return instance(arrPtr); + } + + private static KMVerificationToken proto(short ptr) { + if (prototype == null) prototype = new KMVerificationToken(); + instPtr = ptr; + return prototype; + } + + + public static short instance() { + short arrPtr = KMArray.instance((short)5); + return instance(arrPtr); } - @Override - public void init() { - vals = null; + public static short instance(short vals) { + KMArray arr = KMArray.cast(vals); + if(arr.length() != 5)ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); + short ptr = KMType.instance(VERIFICATION_TOKEN_TYPE, (short)2); + Util.setShort(heap, (short)(ptr + TLV_HEADER_SIZE), vals); + return ptr; + } + + public static KMVerificationToken cast(short ptr) { + if (heap[ptr] != VERIFICATION_TOKEN_TYPE) ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + short arrPtr = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE)); + if(heap[arrPtr] != ARRAY_TYPE) ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + return proto(ptr); + } + + public short getVals() { + return Util.getShort(heap, (short) (instPtr + TLV_HEADER_SIZE)); } - @Override public short length() { - return vals.length(); + short arrPtr = getVals(); + return KMArray.cast(arrPtr).length(); } - public static void create(KMVerificationToken[] verTokenRefTable) { - byte index = 0; - while (index < verTokenRefTable.length) { - verTokenRefTable[index] = new KMVerificationToken(); - index++; - } + public short getChallenge() { + short arrPtr = getVals(); + return KMArray.cast(arrPtr).get(CHALLENGE); } - public static KMVerificationToken instance() { - KMVerificationToken inst = repository.newVerificationToken(); - inst.vals = KMArray.instance((short) 5); - inst.vals.add(CHALLENGE, KMInteger.instance()); - inst.vals.add(TIMESTAMP, KMInteger.instance()); - inst.vals.add(PARAMETERS_VERIFIED, KMKeyParameters.instance()); - inst.vals.add(SECURITY_LEVEL, KMEnumTag.instance(KMType.HARDWARE_TYPE)); - inst.vals.add(MAC, KMByteBlob.instance()); - return inst; + public void setChallenge(short vals) { + KMInteger.cast(vals); + short arrPtr = getVals(); + KMArray.cast(arrPtr).add(CHALLENGE, vals); } - public static KMVerificationToken instance(KMArray vals) { - if (vals.length() != 5) { - ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); - } - KMVerificationToken inst = repository.newVerificationToken(); - inst.vals = vals; - return inst; + public short getTimestamp() { + short arrPtr = getVals(); + return KMArray.cast(arrPtr).get(TIMESTAMP); } - public KMInteger getChallenge() { - return (KMInteger) vals.get(CHALLENGE); + public void setTimestamp(short vals) { + KMInteger.cast(vals); + short arrPtr = getVals(); + KMArray.cast(arrPtr).add(TIMESTAMP, vals); } - public KMInteger getTimestamp() { - return (KMInteger) vals.get(TIMESTAMP); + public short getMac() { + short arrPtr = getVals(); + return KMArray.cast(arrPtr).get(MAC); } - public KMKeyParameters getParametersVerified() { - return (KMKeyParameters) vals.get(PARAMETERS_VERIFIED); + public void setMac(short vals) { + KMByteBlob.cast(vals); + short arrPtr = getVals(); + KMArray.cast(arrPtr).add(MAC, vals); } - public byte getSecurityLevel() { - return ((KMEnumTag) vals.get(SECURITY_LEVEL)).getValue(); + public short getParametersVerified() { + short arrPtr = getVals(); + return KMArray.cast(arrPtr).get(PARAMETERS_VERIFIED); } - public KMByteBlob getMac() { - return (KMByteBlob) vals.get(MAC); + public void setParametersVerified(short vals) { + KMKeyParameters.cast(vals); + short arrPtr = getVals(); + KMArray.cast(arrPtr).add(PARAMETERS_VERIFIED, vals); } - public KMArray getVals() { - return vals; + public short getSecurityLevel() { + short arrPtr = getVals(); + return KMArray.cast(arrPtr).get(SECURITY_LEVEL); } + + public void setSecurityLevel(short vals) { + short key = KMEnumTag.cast(vals).getKey(); + if(key != HARDWARE_TYPE) ISOException.throwIt(ISO7816.SW_DATA_INVALID); + short arrPtr = getVals(); + KMArray.cast(arrPtr).add(SECURITY_LEVEL, vals); + } + } diff --git a/Applet/Applet/test/com/android/javacard/test/KMFrameworkTest.java b/Applet/Applet/test/com/android/javacard/test/KMFrameworkTest.java index 15e61109..c34ac845 100644 --- a/Applet/Applet/test/com/android/javacard/test/KMFrameworkTest.java +++ b/Applet/Applet/test/com/android/javacard/test/KMFrameworkTest.java @@ -60,7 +60,7 @@ public void test_Lifecycle_Success() { public void testProvisionCmd(CardSimulator simulator){ byte[] buf = new byte[512]; // test provision command - KMArray cmd = makeProvisionCmd(); + short cmd = makeProvisionCmd(); KMEncoder enc = new KMEncoder(); short actualLen = enc.encode(cmd, buf, (short) 0, (short)buf.length); CommandAPDU commandAPDU = new CommandAPDU(0x80, 0x23, 0x40, 0x00, buf, 0, actualLen); @@ -74,18 +74,19 @@ public void testGetHwInfoCmd(CardSimulator simulator){ //print(commandAPDU.getBytes()); ResponseAPDU response = simulator.transmitCommand(commandAPDU); KMDecoder dec = new KMDecoder(); - KMArray exp = KMArray.instance((short)3) - .add((short)0, KMEnum.instance().setType(KMType.HARDWARE_TYPE)) - .add((short)1, KMByteBlob.instance()) - .add((short)2, KMByteBlob.instance()); + short arrPtr = KMArray.instance((short)3); + KMArray exp = KMArray.cast(arrPtr); + exp.add((short)0, KMEnum.instance(KMType.HARDWARE_TYPE)); + exp.add((short)1, KMByteBlob.exp()); + exp.add((short)2, KMByteBlob.exp()); byte[] respBuf = response.getBytes(); short len = (short)respBuf.length; - KMArray resp = dec.decode(exp,respBuf, (short)0, len); - Assert.assertEquals(3, resp.length()); - KMEnum secLevel = (KMEnum)resp.get((short)0); - KMByteBlob kmName = (KMByteBlob) resp.get((short)1); - KMByteBlob authorName = (KMByteBlob) resp.get((short)2); - Assert.assertEquals(KMType.HARDWARE_TYPE, secLevel.getType()); + short respPtr = dec.decode(arrPtr,respBuf, (short)0, len); + Assert.assertEquals(3, KMArray.cast(respPtr).length()); + KMEnum secLevel = KMEnum.cast(KMArray.cast(respPtr).get((short)0)); + short kmName = KMArray.cast(respPtr).get((short)1); + short authorName = KMArray.cast(respPtr).get((short)2); + Assert.assertEquals(KMType.HARDWARE_TYPE, secLevel.getEnumType()); Assert.assertEquals(KMType.STRONGBOX, secLevel.getVal()); String kmNameStr = byteBlobToString(kmName); String authorNameStr = byteBlobToString(authorName); @@ -96,7 +97,7 @@ public void testGetHwInfoCmd(CardSimulator simulator){ private void testAddRngEntropyCmd(CardSimulator simulator){ byte[] buf = new byte[512]; // test provision command - KMArray cmd = makeAddRngEntropyCmd(); + short cmd = makeAddRngEntropyCmd(); KMEncoder enc = new KMEncoder(); short actualLen = enc.encode(cmd, buf, (short) 0, (short)buf.length); @@ -107,8 +108,9 @@ private void testAddRngEntropyCmd(CardSimulator simulator){ } - private String byteBlobToString(KMByteBlob blob) { + private String byteBlobToString(short blobPtr) { StringBuilder sb = new StringBuilder(); + KMByteBlob blob = KMByteBlob.cast(blobPtr); for(short i = 0; i Date: Wed, 13 May 2020 20:38:50 -0700 Subject: [PATCH 3/6] ImportKey, GenerateKey and GetKeyCharacteristics - Some Code optimizations. - ImportKey, GenerateKey and GetKeyCharacteristics functionality. - GetKeyCharacteristics only tested on jcardSim. --- .../android/javacard/keymaster/KMArray.java | 2 +- .../android/javacard/keymaster/KMBoolTag.java | 5 +- .../javacard/keymaster/KMByteBlob.java | 12 + .../android/javacard/keymaster/KMByteTag.java | 4 +- .../javacard/keymaster/KMCryptoProvider.java | 80 ++ .../android/javacard/keymaster/KMDecoder.java | 44 +- .../android/javacard/keymaster/KMEncoder.java | 149 +- .../android/javacard/keymaster/KMEnum.java | 16 +- .../javacard/keymaster/KMEnumArrayTag.java | 8 +- .../android/javacard/keymaster/KMEnumTag.java | 18 +- .../android/javacard/keymaster/KMError.java | 78 ++ .../javacard/keymaster/KMException.java | 21 +- .../android/javacard/keymaster/KMInteger.java | 15 +- .../javacard/keymaster/KMIntegerArrayTag.java | 2 +- .../javacard/keymaster/KMIntegerTag.java | 27 +- .../javacard/keymaster/KMKeyParameters.java | 212 ++- .../javacard/keymaster/KMKeymasterApplet.java | 1244 +++++++++++++++-- .../javacard/keymaster/KMOperationState.java | 7 - .../javacard/keymaster/KMRepository.java | 143 +- .../com/android/javacard/keymaster/KMRng.java | 104 ++ .../javacard/keymaster/KMSimulator.java | 424 ++++++ .../android/javacard/keymaster/KMType.java | 18 + .../javacard/test/KMFrameworkTest.java | 551 +++++++- Applet/JavaCardKeymaster.scr | 43 +- Applet/default.output | 13 +- 25 files changed, 3027 insertions(+), 213 deletions(-) create mode 100644 Applet/Applet/src/com/android/javacard/keymaster/KMCryptoProvider.java create mode 100644 Applet/Applet/src/com/android/javacard/keymaster/KMError.java create mode 100644 Applet/Applet/src/com/android/javacard/keymaster/KMRng.java create mode 100644 Applet/Applet/src/com/android/javacard/keymaster/KMSimulator.java diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMArray.java b/Applet/Applet/src/com/android/javacard/keymaster/KMArray.java index 04ee7cfe..2935aa2b 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMArray.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMArray.java @@ -80,7 +80,7 @@ public short get(short index) { public byte containedType(){ return heap[(short)(instPtr + TLV_HEADER_SIZE)];} public short getStartOff() { - return (short) (instPtr + TLV_HEADER_SIZE + 3); + return (short) (instPtr + TLV_HEADER_SIZE + ARRAY_HEADER_SIZE); } public short length() { diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMBoolTag.java b/Applet/Applet/src/com/android/javacard/keymaster/KMBoolTag.java index 5154cd44..e56dc9f3 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMBoolTag.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMBoolTag.java @@ -83,7 +83,7 @@ public byte getVal() { return heap[(short)(instPtr+TLV_HEADER_SIZE+4)]; } - // validate the tag key + // isValidTag the tag key private static boolean validateKey(short key) { short index = (short) tags.length; while (--index >= 0) { @@ -93,4 +93,7 @@ private static boolean validateKey(short key) { } return false; } + public static short[] getTags(){ + return tags; + } } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMByteBlob.java b/Applet/Applet/src/com/android/javacard/keymaster/KMByteBlob.java index a112ce6c..423220fa 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMByteBlob.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMByteBlob.java @@ -87,4 +87,16 @@ public short length() { public byte[] getBuffer() { return heap; } + + public void getValue(byte[] destBuf, short destStart, short destLength){ + Util.arrayCopyNonAtomic(heap, getStartOff(), destBuf, destStart, destLength); + } + + public void setValue(byte[] srcBuf, short srcStart, short srcLength){ + if(length() > srcLength){ + ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); + } + Util.arrayCopyNonAtomic(srcBuf, srcStart, heap, getStartOff(), length()); + } + } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMByteTag.java b/Applet/Applet/src/com/android/javacard/keymaster/KMByteTag.java index 6740e9ba..a00d6c6f 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMByteTag.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMByteTag.java @@ -41,7 +41,9 @@ public class KMByteTag extends KMTag { ATTESTATION_ID_MODEL, ASSOCIATED_DATA, NONCE, - CONFIRMATION_TOKEN + CONFIRMATION_TOKEN, + VERIFIED_BOOT_KEY, + VERIFIED_BOOT_HASH }; private KMByteTag() {} diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMCryptoProvider.java b/Applet/Applet/src/com/android/javacard/keymaster/KMCryptoProvider.java new file mode 100644 index 00000000..8c44d28f --- /dev/null +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMCryptoProvider.java @@ -0,0 +1,80 @@ +package com.android.javacard.keymaster; + +import javacard.security.AESKey; +import javacard.security.DESKey; +import javacard.security.ECPrivateKey; +import javacard.security.HMACKey; +import javacard.security.KeyPair; +import javacard.security.RSAPrivateKey; + +public interface KMCryptoProvider { + KeyPair createRsaKeyPair(); + + KeyPair createECKeyPair(); + + AESKey createAESKey(short keysize); + + AESKey createAESKey(byte[] buf, short startOff, short length); + + DESKey createTDESKey(); + + HMACKey createHMACKey(short keysize); + + void newRandomNumber(byte[] num, short offset, short length); + + void addRngEntropy(byte[] num, short offset, short length); + + short aesGCMEncrypt( + AESKey key, + byte[] secret, + short secretStart, + short secretLen, + byte[] encSecret, + short encSecretStart, + byte[] nonce, + short nonceStart, + short nonceLen, + byte[] authData, + short authDataStart, + short authDataLen, + byte[] authTag, + short authTagStart, + short authTagLen); + + boolean aesGCMDecrypt( + AESKey key, + byte[] encSecret, + short encSecretStart, + short encSecretLen, + byte[] secret, + short secretStart, + byte[] nonce, + short nonceStart, + short nonceLen, + byte[] authData, + short authDataStart, + short authDataLen, + byte[] authTag, + short authTagStart, + short authTagLen); + + byte[] getTrueRandomNumber(short len); + + short aesCCMSign( + byte[] bufIn, + short bufInStart, + short buffInLength, + byte[] masterKeySecret, + byte[] bufOut, + short bufStart); + + ECPrivateKey createEcPrivateKey(byte[] pubBuffer, short pubOff, short pubLength, + byte[] privBuffer, short privOff, short privLength); + + HMACKey createHMACKey(byte[] secretBuffer, short secretOff, short secretLength); + + DESKey createTDESKey(byte[] secretBuffer, short secretOff, short secretLength); + + RSAPrivateKey createRsaPrivateKey(byte[] modBuffer, short modOff, short modLength, + byte[] privBuffer, short privOff, short privLength); +} diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMDecoder.java b/Applet/Applet/src/com/android/javacard/keymaster/KMDecoder.java index b4679a3c..25c1c1b1 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMDecoder.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMDecoder.java @@ -55,7 +55,27 @@ public short decode(short expression, byte[] buffer, short startOff, short lengt this.length = (short)(startOff+length); return decode(expression); } - + public short decodeArray(short exp, byte[] buffer, short startOff, short length){ + this.buffer = buffer; + this.startOff = startOff; + this.length = (short)(startOff+length); + short payloadLength = readMajorTypeWithPayloadLength(ARRAY_TYPE); + short expLength = KMArray.cast(exp).length(); + if(payloadLength > expLength){ + ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); + } + short index = 0; + short obj; + short type; + short arrPtr = KMArray.instance(payloadLength); + while (index < payloadLength) { + type = KMArray.cast(exp).get(index); + obj = decode(type); + KMArray.cast(arrPtr).add(index, obj); + index++; + } + return arrPtr; + } private short decode(short exp){ byte type = KMType.getType(exp); switch(type){ @@ -138,6 +158,9 @@ private short decodeKeyParam(short exp) { boolean tagFound; short tagInd; short tagType; + short tagClass; + short allowedType; + short obj; // For each tag in payload ... while (index < payloadLength) { tagFound = false; @@ -145,12 +168,13 @@ private short decodeKeyParam(short exp) { tagType = peekTagType(); // Check against the allowed tags ... while (tagInd < length) { - short tagClass = KMArray.cast(allowedTags).get(tagInd); - short allowedType = KMTag.getTagType(tagClass); + tagClass = KMArray.cast(allowedTags).get(tagInd); + allowedType = KMTag.getTagType(tagClass); // If it is part of allowed tags ... if (tagType == allowedType) { // then decodeByteBlob and add that to the array. - KMArray.cast(vals).add(index, decode(tagClass)); + obj = decode(tagClass); + KMArray.cast(vals).add(index,obj); tagFound = true; break; } @@ -200,17 +224,21 @@ private short decodeArray(short exp) { } short arrPtr = KMArray.cast(exp).instance(payloadLength); short index = 0; + short type; + short obj; // check whether array contains one type of objects or multiple types if( KMArray.cast(exp).containedType() == 0){// multiple types specified by expression. while (index < payloadLength) { - short type = KMArray.cast(exp).get(index); - KMArray.cast(arrPtr).add(index, decode(type)); + type = KMArray.cast(exp).get(index); + obj = decode(type); + KMArray.cast(arrPtr).add(index, obj); index++; } }else{ // Array is a Vector containing objects of one type - short type = KMArray.cast(exp).containedType(); + type = KMArray.cast(exp).containedType(); while(index < payloadLength){ - KMArray.cast(arrPtr).add(index, decode(type)); + obj = decode(type); + KMArray.cast(arrPtr).add(index, obj); index++; } } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMEncoder.java b/Applet/Applet/src/com/android/javacard/keymaster/KMEncoder.java index 5c96387a..01fb137a 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMEncoder.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMEncoder.java @@ -18,6 +18,8 @@ import javacard.framework.ISO7816; import javacard.framework.ISOException; +import javacard.framework.JCSystem; +import javacard.framework.SystemException; import javacard.framework.Util; public class KMEncoder { @@ -43,59 +45,100 @@ public class KMEncoder { private byte[] buffer; private short startOff; private short length; + private static short[] stack; + private static byte stackPtr; public KMEncoder() { buffer = null; startOff = 0; length = 0; + stack = JCSystem.makeTransientShortArray((short)50, JCSystem.CLEAR_ON_RESET); } - public short encode(short object, byte[] buffer, short startOff, short length) { + private static void push (short objPtr){ + stack[stackPtr] = objPtr; + stackPtr++; + } + private static short pop(){ + stackPtr--; + return stack[stackPtr]; + } + private void encode(short obj){ + push(obj); + } + public short encode(short object, byte[] buffer, short startOff) { + stackPtr = 0; this.buffer = buffer; this.startOff = startOff; - this.length = length; - encode(object); - this.length = this.startOff; + short len = (short) buffer.length; + if((len <0) || (len > KMKeymasterApplet.MAX_LENGTH)){ + this.length = KMKeymasterApplet.MAX_LENGTH; + }else{ + this.length = (short)buffer.length; + } + //this.length = (short)(startOff + length); + push(object); + encode(); + return (short)(this.startOff - startOff); + } + + + public short encodeError(short err, byte[] buffer, short startOff, short length) { + this.buffer = buffer; this.startOff = startOff; - return this.length; + this.length = (short)(startOff + length); + // encode the err as UINT with value in err - should not be greater then 5 bytes. + if(err < UINT8_LENGTH){ + writeByte((byte)(UINT_TYPE | err )); + }else if(err < 0x100){ + writeByte((byte)(UINT_TYPE | UINT8_LENGTH)); + writeByte((byte)err); + }else { + writeByte((byte)(UINT_TYPE | UINT16_LENGTH)); + writeShort(err); + } + return (short)(this.startOff - startOff); } - private void encode(short exp){ - byte type = KMType.getType(exp); - switch(type){ - case KMType.BYTE_BLOB_TYPE: - encodeByteBlob(exp); - return; - case KMType.INTEGER_TYPE: - encodeInteger(exp); - return; - case KMType.ARRAY_TYPE: - encodeArray(exp); - return; - case KMType.ENUM_TYPE: - encodeEnum(exp); - return; - case KMType.KEY_PARAM_TYPE: - encodeKeyParam(exp); - return; - case KMType.KEY_CHAR_TYPE: - encodeKeyChar(exp); - return; - case KMType.VERIFICATION_TOKEN_TYPE: - encodeVeriToken(exp); - return; - case KMType.HMAC_SHARING_PARAM_TYPE: - encodeHmacSharingParam(exp); - return; - case KMType.HW_AUTH_TOKEN_TYPE: - encodeHwAuthToken(exp); - return; - case KMType.TAG_TYPE: - short tagType = KMTag.getTagType(exp); - encodeTag(tagType, exp); - return; - default: - ISOException.throwIt(ISO7816.SW_DATA_INVALID); + private void encode(){ + while (stackPtr > 0) { + short exp = pop(); + byte type = KMType.getType(exp); + switch (type) { + case KMType.BYTE_BLOB_TYPE: + encodeByteBlob(exp); + break; + case KMType.INTEGER_TYPE: + encodeInteger(exp); + break; + case KMType.ARRAY_TYPE: + encodeArray(exp); + break; + case KMType.ENUM_TYPE: + encodeEnum(exp); + break; + case KMType.KEY_PARAM_TYPE: + encodeKeyParam(exp); + break; + case KMType.KEY_CHAR_TYPE: + encodeKeyChar(exp); + break; + case KMType.VERIFICATION_TOKEN_TYPE: + encodeVeriToken(exp); + break; + case KMType.HMAC_SHARING_PARAM_TYPE: + encodeHmacSharingParam(exp); + break; + case KMType.HW_AUTH_TOKEN_TYPE: + encodeHwAuthToken(exp); + break; + case KMType.TAG_TYPE: + short tagType = KMTag.getTagType(exp); + encodeTag(tagType, exp); + break; + default: + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } } } private void encodeTag(short tagType, short exp){ @@ -147,22 +190,23 @@ private void encodeHmacSharingParam(short obj) { private void encodeArray(short obj) { writeMajorTypeWithLength(ARRAY_TYPE, KMArray.cast(obj).length()); - short index = 0; short len = KMArray.cast(obj).length(); - while(index < len){ + short index = (short)(len-1); + while(index >= 0){ encode(KMArray.cast(obj).get(index)); - index++; + index--; } } private void encodeAsMap(short obj){ writeMajorTypeWithLength(MAP_TYPE, KMArray.cast(obj).length()); - short index = 0; short len = KMArray.cast(obj).length(); - while(index < len){ - short inst = KMArray.cast(obj).get(index); + short index = (short)(len-1); + short inst; + while(index >= 0){ + inst = KMArray.cast(obj).get(index); encode(inst); - index++; + index--; } } @@ -208,6 +252,8 @@ private void encodeInteger(short obj) { while(index < len){ if(val[(short)(startOff + index)] > 0){ break; + }else if(val[(short)(startOff + index)] < 0){ + break; } index++; // index will be equal to len if value is 0. } @@ -215,7 +261,8 @@ private void encodeInteger(short obj) { short diff = (short)(len - index); if(diff == 0){ writeByte((byte)(UINT_TYPE | 0)); - }else if((diff == 1) && val[(short)(startOff + index)] < UINT8_LENGTH){ + }else if((diff == 1) && (val[(short)(startOff + index)] < UINT8_LENGTH) + &&(val[(short)(startOff + index)] >= 0)){ writeByte((byte)(UINT_TYPE | val[(short)(startOff + index)])); }else if (diff == 1){ writeByte((byte)(UINT_TYPE | UINT8_LENGTH)); @@ -239,11 +286,11 @@ private void encodeByteBlob(short obj) { } private void writeByteValue(byte val){ - if(val < UINT8_LENGTH){ + if((val < UINT8_LENGTH) && (val >=0)){ writeByte((byte)(UINT_TYPE | val)); }else{ writeByte((byte)(UINT_TYPE | UINT8_LENGTH)); - writeByte(val); + writeByte((byte)val); } } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMEnum.java b/Applet/Applet/src/com/android/javacard/keymaster/KMEnum.java index 67196a81..00ba0cd1 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMEnum.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMEnum.java @@ -24,7 +24,8 @@ public class KMEnum extends KMType { private static KMEnum prototype; private static short instPtr; - private static short[] types = {HARDWARE_TYPE, KEY_FORMAT, KEY_DERIVATION_FUNCTION}; + private static short[] types = {HARDWARE_TYPE, KEY_FORMAT, KEY_DERIVATION_FUNCTION, + VERIFIED_BOOT_STATE, DEVICE_LOCKED}; private static Object[] enums = null; @@ -87,7 +88,9 @@ private static void create() { ISO18033_2_KDF1_SHA256, ISO18033_2_KDF2_SHA1, ISO18033_2_KDF2_SHA256 - } + }, + new byte[] {SELF_SIGNED_BOOT, VERIFIED_BOOT}, + new byte[] {DEVICE_LOCKED_TRUE, DEVICE_LOCKED_FALSE} }; } } @@ -108,9 +111,11 @@ public short getEnumType() { return Util.getShort(heap, (short)(instPtr+TLV_HEADER_SIZE)); } - // validate enumeration keys and values. + // isValidTag enumeration keys and values. private static boolean validateEnum(short key, byte value) { create(); + byte[] vals; + short enumInd; // check if key exists short index = (short) types.length; while (--index >= 0) { @@ -118,8 +123,8 @@ private static boolean validateEnum(short key, byte value) { // check if value given if (value != NO_VALUE) { // check if the value exist - byte[] vals = (byte[]) enums[index]; - short enumInd = (short) vals.length; + vals = (byte[]) enums[index]; + enumInd = (short) vals.length; while (--enumInd >= 0) { if (vals[enumInd] == value) { // return true if value exist @@ -136,4 +141,5 @@ private static boolean validateEnum(short key, byte value) { // return false if key does not exist return false; } + } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMEnumArrayTag.java b/Applet/Applet/src/com/android/javacard/keymaster/KMEnumArrayTag.java index c0601267..d29903e1 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMEnumArrayTag.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMEnumArrayTag.java @@ -65,9 +65,11 @@ public static short instance(short key, short byteBlob) { } KMByteBlob blob = KMByteBlob.cast(byteBlob); short byteIndex = 0; + short enumIndex; + boolean validValue; while (byteIndex < blob.length()) { - short enumIndex = 0; - boolean validValue = false; + enumIndex = 0; + validValue = false; while (enumIndex < allowedVals.length) { if (blob.get(byteIndex) == allowedVals[enumIndex]) { validValue = true; @@ -117,7 +119,7 @@ public static void create() { enums = new Object[] { new byte[] {ENCRYPT, DECRYPT, SIGN, VERIFY, WRAP_KEY, ATTEST_KEY}, - new byte[] {ECB, CBC, CTR}, + new byte[] {ECB, CBC, CTR, GCM}, new byte[] {DIGEST_NONE, MD5, SHA1, SHA2_224, SHA2_256, SHA2_384, SHA2_512}, new byte[] { PADDING_NONE, RSA_OAEP, RSA_PSS, RSA_PKCS1_1_5_ENCRYPT, RSA_PKCS1_1_5_SIGN, PKCS7 diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMEnumTag.java b/Applet/Applet/src/com/android/javacard/keymaster/KMEnumTag.java index 8f66e5e3..b75563d6 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMEnumTag.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMEnumTag.java @@ -93,16 +93,18 @@ public static void create() { new byte[] {RSA, DES, EC, AES, HMAC}, new byte[] {P_224, P_256, P_384, P_521}, new byte[] {STANDALONE, REQUIRES_FILE_SYSTEM}, - new byte[] {USER_AUTH_NONE, PASSWORD, FINGERPRINT, ANY}, + new byte[] {USER_AUTH_NONE, PASSWORD, FINGERPRINT, (byte)(PASSWORD & FINGERPRINT),ANY}, new byte[] {GENERATED, DERIVED, IMPORTED, UNKNOWN, SECURELY_IMPORTED}, new byte[] {SOFTWARE, TRUSTED_ENVIRONMENT, STRONGBOX} }; } } - // validate enumeration keys and values. + // isValidTag enumeration keys and values. private static boolean validateEnum(short key, byte value) { create(); + byte[] vals; + short enumInd; // check if key exists short index = (short) tags.length; while (--index >= 0) { @@ -110,8 +112,8 @@ private static boolean validateEnum(short key, byte value) { // check if value given if (value != NO_VALUE) { // check if the value exist - byte[] vals = (byte[]) enums[index]; - short enumInd = (short) vals.length; + vals = (byte[]) enums[index]; + enumInd = (short) vals.length; while (--enumInd >= 0) { if (vals[enumInd] == value) { // return true if value exist @@ -128,4 +130,12 @@ private static boolean validateEnum(short key, byte value) { // return false if key does not exist return false; } + + public static short getValue(short tagType, short keyParameters){ + short tagPtr = KMKeyParameters.findTag(KMType.ENUM_TAG, tagType, keyParameters); + if(tagPtr != KMType.INVALID_VALUE){ + return heap[(short)(tagPtr+TLV_HEADER_SIZE+4)]; + } + return KMType.INVALID_VALUE; + } } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMError.java b/Applet/Applet/src/com/android/javacard/keymaster/KMError.java new file mode 100644 index 00000000..25557d27 --- /dev/null +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMError.java @@ -0,0 +1,78 @@ +package com.android.javacard.keymaster; + +public class KMError { + public static short OK = 0 ; + public static short ROOT_OF_TRUST_ALREADY_SET = 1; + public static short UNSUPPORTED_PURPOSE = 2; + public static short INCOMPATIBLE_PURPOSE = 3; + public static short UNSUPPORTED_ALGORITHM = 4; + public static short INCOMPATIBLE_ALGORITHM = 5; + public static short UNSUPPORTED_KEY_SIZE = 6; + public static short UNSUPPORTED_BLOCK_MODE = 7; + public static short INCOMPATIBLE_BLOCK_MODE = 8; + public static short UNSUPPORTED_MAC_LENGTH = 9; + public static short UNSUPPORTED_PADDING_MODE = 10; + public static short INCOMPATIBLE_PADDING_MODE = 11; + public static short UNSUPPORTED_DIGEST = 12; + public static short INCOMPATIBLE_DIGEST = 13; + public static short INVALID_EXPIRATION_TIME = 14; + public static short INVALID_USER_ID = 15; + public static short INVALID_AUTHORIZATION_TIMEOUT = 16; + public static short UNSUPPORTED_KEY_FORMAT = 17; + public static short INCOMPATIBLE_KEY_FORMAT = 18; + public static short UNSUPPORTED_KEY_ENCRYPTION_ALGORITHM = 19; /** For PKCS8 & PKCS12 */ + public static short UNSUPPORTED_KEY_VERIFICATION_ALGORITHM = 20; /** For PKCS8 & PKCS12 */ + public static short INVALID_INPUT_LENGTH = 21; + public static short KEY_EXPORT_OPTIONS_INVALID = 22; + public static short DELEGATION_NOT_ALLOWED = 23; + public static short KEY_NOT_YET_VALID = 24; + public static short KEY_EXPIRED = 25; + public static short KEY_USER_NOT_AUTHENTICATED = 26; + public static short OUTPUT_PARAMETER_NULL = 27; + public static short INVALID_OPERATION_HANDLE = 28; + public static short INSUFFICIENT_BUFFER_SPACE = 29; + public static short VERIFICATION_FAILED = 30; + public static short TOO_MANY_OPERATIONS = 31; + public static short UNEXPECTED_NULL_POINTER = 32; + public static short INVALID_KEY_BLOB = 33; + public static short IMPORTED_KEY_NOT_ENCRYPTED = 34; + public static short IMPORTED_KEY_DECRYPTION_FAILED = 35; + public static short IMPORTED_KEY_NOT_SIGNED = 36; + public static short IMPORTED_KEY_VERIFICATION_FAILED = 37; + public static short INVALID_ARGUMENT = 38; + public static short UNSUPPORTED_TAG = 39; + public static short INVALID_TAG = 40; + public static short MEMORY_ALLOCATION_FAILED = 41; + public static short IMPORT_PARAMETER_MISMATCH = 44; + public static short SECURE_HW_ACCESS_DENIED = 45; + public static short OPERATION_CANCELLED = 46; + public static short CONCURRENT_ACCESS_CONFLICT = 47; + public static short SECURE_HW_BUSY = 48; + public static short SECURE_HW_COMMUNICATION_FAILED = 49; + public static short UNSUPPORTED_EC_FIELD = 50; + public static short MISSING_NONCE = 51; + public static short INVALID_NONCE = 52; + public static short MISSING_MAC_LENGTH = 53; + public static short KEY_RATE_LIMIT_EXCEEDED = 54; + public static short CALLER_NONCE_PROHIBITED = 55; + public static short KEY_MAX_OPS_EXCEEDED = 56; + public static short INVALID_MAC_LENGTH = 57; + public static short MISSING_MIN_MAC_LENGTH = 58; + public static short UNSUPPORTED_MIN_MAC_LENGTH = 59; + public static short UNSUPPORTED_KDF = 60; + public static short UNSUPPORTED_EC_CURVE = 61; + public static short KEY_REQUIRES_UPGRADE = 62; + public static short ATTESTATION_CHALLENGE_MISSING = 63; + public static short KEYMASTER_NOT_CONFIGURED = 64; + public static short ATTESTATION_APPLICATION_ID_MISSING = 65; + public static short CANNOT_ATTEST_IDS = 66; + public static short ROLLBACK_RESISTANCE_UNAVAILABLE = 67; + public static short HARDWARE_TYPE_UNAVAILABLE = 68; + public static short PROOF_OF_PRESENCE_REQUIRED = 69; + public static short CONCURRENT_PROOF_OF_PRESENCE_REQUESTED = 70; + public static short NO_USER_CONFIRMATION = 71; + public static short DEVICE_LOCKED = 72; + public static short UNIMPLEMENTED = 100; + public static short VERSION_MISMATCH = 101; + public static short UNKNOWN_ERROR = 1000; +} diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMException.java b/Applet/Applet/src/com/android/javacard/keymaster/KMException.java index 3a26b047..1805a911 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMException.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMException.java @@ -16,13 +16,22 @@ package com.android.javacard.keymaster; -public class KMException { - - // The Applet is not in a correct state in order to execute the command. - public static final short CMD_NOT_ACCEPTED_WRONG_STATE = (short) 0x6901; +public class KMException extends RuntimeException { + public static short reason; + public static KMException exception; + private KMException(){ + } + public static void throwIt(short reason){ + KMException.reason = reason; + throw instance(); + } + public static KMException instance(){ + if(exception == null ) exception = new KMException(); + return exception; + } - public static boolean handle(short reason) { - return false; + public void clear(){ + reason = KMError.UNKNOWN_ERROR; } } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMInteger.java b/Applet/Applet/src/com/android/javacard/keymaster/KMInteger.java index d4efe987..994a94cc 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMInteger.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMInteger.java @@ -23,8 +23,8 @@ // Represents 8 bit, 16 bit, 32 bit and 64 bit integers public class KMInteger extends KMType { - private static final short UINT_32 = 4; - private static final short UINT_64 = 8; + public static final short UINT_32 = 4; + public static final short UINT_64 = 8; private static KMInteger prototype; private static short instPtr; @@ -121,11 +121,20 @@ public short getStartOff() { public void getValue(byte[] dest, short destOff, short length){ Util.arrayCopyNonAtomic(heap, (short)(instPtr+TLV_HEADER_SIZE), dest, destOff, length); } + public void setValue(byte[] src, short srcOff){ + Util.arrayCopyNonAtomic(src, srcOff, heap, (short)(instPtr+TLV_HEADER_SIZE), length()); + } + public short value(byte[] dest, short destOff){ + Util.arrayCopyNonAtomic(heap, (short)(instPtr+TLV_HEADER_SIZE), dest, destOff, length()); + return length(); + } public short getShort() { return Util.getShort(heap, (short) (instPtr + TLV_HEADER_SIZE + 2)); } - + public short getSignificantShort(){ + return Util.getShort(heap, (short) (instPtr + TLV_HEADER_SIZE)); + } public byte getByte() { return heap[(short) (instPtr + TLV_HEADER_SIZE + 3)]; } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMIntegerArrayTag.java b/Applet/Applet/src/com/android/javacard/keymaster/KMIntegerArrayTag.java index 775ebfd2..5f0e6731 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMIntegerArrayTag.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMIntegerArrayTag.java @@ -120,7 +120,7 @@ private static boolean validateKey(short key) { return false; } - // TODO this should be combined with validateKey to actually validate {tagType, tagKey} pair. + // TODO this should be combined with validateKey to actually isValidTag {tagType, tagKey} pair. private static boolean validateTagType(short tagType) { if ((tagType == ULONG_ARRAY_TAG) || (tagType == UINT_ARRAY_TAG)) { return true; diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMIntegerTag.java b/Applet/Applet/src/com/android/javacard/keymaster/KMIntegerTag.java index 6aa3a1b4..0ac49e06 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMIntegerTag.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMIntegerTag.java @@ -132,7 +132,7 @@ private static boolean validateKey(short key) { return false; } - // TODO this should be combined with validateKey to actually validate {tagType, tagKey} pair. + // TODO this should be combined with validateKey to actually isValidTag {tagType, tagKey} pair. private static boolean validateTagType(short tagType) { if ((tagType == DATE_TAG) || (tagType == UINT_TAG) || (tagType == ULONG_TAG)) { return true; @@ -140,4 +140,29 @@ private static boolean validateTagType(short tagType) { return false; } + public static short getShortValue(short tagType, short tagKey, short keyParameters){ + short ptr; + if(tagType == UINT_TAG){ + ptr = KMKeyParameters.findTag(KMType.UINT_TAG, tagKey, keyParameters); + if (ptr != KMType.INVALID_VALUE) { + ptr = KMIntegerTag.cast(ptr).getValue(); + if(KMInteger.cast(ptr).getSignificantShort() == 0){ + return KMInteger.cast(ptr).getShort(); + } + } + } + return KMType.INVALID_VALUE; + } + + public static short getValue(byte[] buf, short offset, short tagType, short tagKey, short keyParameters){ + short ptr; + if((tagType == UINT_TAG) || (tagType == ULONG_TAG) || (tagType == DATE_TAG)){ + ptr = KMKeyParameters.findTag(tagType, tagKey, keyParameters); + if (ptr != KMType.INVALID_VALUE) { + ptr = KMIntegerTag.cast(ptr).getValue(); + return KMInteger.cast(ptr).value(buf,offset); + } + } + return KMType.INVALID_VALUE; + } } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java b/Applet/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java index 428ef78c..d3bf5af1 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java @@ -16,12 +16,24 @@ package com.android.javacard.keymaster; - import javacard.framework.ISO7816; import javacard.framework.ISOException; import javacard.framework.Util; public class KMKeyParameters extends KMType { + + private static final short[] swEnforcedTags = {}; + + private static final short[] ignoredTags = { + KMType.ROOT_OF_TRUST, + KMType.RESET_SINCE_ID_ROTATION, + KMType.ALLOW_WHILE_ON_BODY, + KMType.ATTESTATION_CHALLENGE, + KMType.OS_VERSION, // not in hidden + KMType.OS_PATCH_LEVEL // not in hidden + // ALL_APPLICATIONS missing from types.hal + }; + private static KMKeyParameters prototype; private static short instPtr; @@ -34,7 +46,7 @@ private static KMKeyParameters proto(short ptr) { } public static short exp() { - short arrPtr = KMArray.instance((short)9); + short arrPtr = KMArray.instance((short) 9); KMArray arr = KMArray.cast(arrPtr); arr.add((short) 0, KMIntegerTag.exp(UINT_TAG)); arr.add((short) 1, KMIntegerArrayTag.exp(UINT_ARRAY_TAG)); @@ -49,15 +61,15 @@ public static short exp() { } public static short instance(short vals) { - short ptr = KMType.instance(KEY_PARAM_TYPE, (short)2); - Util.setShort(heap, (short)(ptr + TLV_HEADER_SIZE), vals); + short ptr = KMType.instance(KEY_PARAM_TYPE, (short) 2); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), vals); return ptr; } public static KMKeyParameters cast(short ptr) { if (heap[ptr] != KEY_PARAM_TYPE) ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); short arrPtr = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE)); - if(heap[arrPtr] != ARRAY_TYPE) ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + if (heap[arrPtr] != ARRAY_TYPE) ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); return proto(ptr); } @@ -70,4 +82,194 @@ public short length() { return KMArray.cast(arrPtr).length(); } + public static short findTag(short tagType, short tagKey, short keyParam) { + KMKeyParameters instParam = KMKeyParameters.cast(keyParam); + KMArray vals = KMArray.cast(instParam.getVals()); + short index = 0; + short length = vals.length(); + short key; + short type; + short ret = KMType.INVALID_VALUE; + short obj; + while (index < length) { + obj = vals.get(index); + key = KMTag.getKey(obj); + type = KMTag.getTagType(obj); + if ((tagKey == key) && (tagType == type)) { + ret = obj; + break; + } + index++; + } + return ret; + } + + // KDF, ECIES_SINGLE_HASH_MODE missing from types.hal + public static short makeHwEnforced( + short keyParamsPtr, byte origin, short osVersionObjPtr, short osPatchObjPtr, byte[] scratchPad) { + final short[] hwEnforcedTagArr = { + // HW Enforced + KMType.ENUM_TAG, KMType.ORIGIN, + KMType.ENUM_ARRAY_TAG, KMType.PURPOSE, + KMType.ENUM_TAG, KMType.ALGORITHM, + KMType.UINT_TAG, KMType.KEYSIZE, + KMType.ULONG_TAG, KMType.RSA_PUBLIC_EXPONENT, + KMType.ENUM_TAG, KMType.BLOB_USAGE_REQ, + KMType.ENUM_ARRAY_TAG, KMType.DIGEST, + KMType.ENUM_ARRAY_TAG, KMType.PADDING, + KMType.ENUM_ARRAY_TAG, KMType.BLOCK_MODE, + KMType.UINT_TAG, KMType.MIN_SEC_BETWEEN_OPS, + KMType.UINT_TAG, KMType.MAX_USES_PER_BOOT, + KMType.ULONG_ARRAY_TAG, KMType.USER_SECURE_ID, + KMType.BOOL_TAG, KMType.NO_AUTH_REQUIRED, + KMType.UINT_TAG, KMType.AUTH_TIMEOUT, + KMType.BOOL_TAG, KMType.CALLER_NONCE, + KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, + KMType.ENUM_TAG, KMType.ECCURVE, + KMType.BOOL_TAG, KMType.TRUSTED_CONFIRMATION_REQUIRED, + KMType.BOOL_TAG, KMType.INCLUDE_UNIQUE_ID, + KMType.BOOL_TAG, KMType.ROLLBACK_RESISTANCE, + KMType.ENUM_TAG, KMType.USER_AUTH_TYPE + }; + byte index = 0; + short tagInd = 0; + short arrInd = 0; + short tagPtr = 0; + short tagKey = 0; + short tagType = 0; + short arrPtr = KMKeyParameters.cast(keyParamsPtr).getVals(); + short len = KMArray.cast(arrPtr).length(); + while (index < len) { + tagInd = 0; + tagPtr = KMArray.cast(arrPtr).get(index); + tagKey = KMTag.getKey(tagPtr); + tagType = KMTag.getTagType(tagPtr); + if (!isValidTag(tagType, tagKey)) { + KMException.throwIt(KMError.INVALID_KEY_BLOB); + } + while (tagInd < (short) hwEnforcedTagArr.length) { + if ((hwEnforcedTagArr[tagInd] == tagType) + && (hwEnforcedTagArr[(short) (tagInd + 1)] == tagKey)) { + Util.setShort(scratchPad, arrInd, tagPtr); + arrInd += 2; + break; + } + tagInd += 2; + } + index++; + } + short originTag = KMEnumTag.instance(KMType.ORIGIN, origin); + Util.setShort(scratchPad, arrInd, originTag); + arrInd += 2; + short osVersionTag = KMIntegerTag.instance(KMType.UINT_TAG, KMType.OS_VERSION, osVersionObjPtr); + Util.setShort(scratchPad, arrInd, osVersionTag); + arrInd += 2; + short osPatchTag = KMIntegerTag.instance(KMType.UINT_TAG, KMType.OS_PATCH_LEVEL, osPatchObjPtr); + Util.setShort(scratchPad, arrInd, osPatchTag); + arrInd += 2; + return createKeyParameters(scratchPad, (short) (arrInd / 2)); + } + + // ALL_USERS, EXPORTABLE missing from types.hal + public static short makeSwEnforced(short keyParamsPtr, byte[] scratchPad) { + final short[] swEnforcedTagsArr = { + KMType.DATE_TAG, KMType.ACTIVE_DATETIME, + KMType.DATE_TAG, KMType.ORIGINATION_EXPIRE_DATETIME, + KMType.DATE_TAG, KMType.USAGE_EXPIRE_DATETIME, + KMType.UINT_TAG, KMType.USERID, + KMType.DATE_TAG, KMType.CREATION_DATETIME + }; + byte index = 0; + short tagInd = 0; + short arrInd = 0; + short tagPtr = 0; + short tagKey = 0; + short tagType = 0; + short arrPtr = KMKeyParameters.cast(keyParamsPtr).getVals(); + short len = KMArray.cast(arrPtr).length(); + while (index < len) { + tagInd = 0; + tagPtr = KMArray.cast(arrPtr).get(index); + tagKey = KMTag.getKey(tagPtr); + tagType = KMTag.getTagType(tagPtr); + if (!isValidTag(tagType, tagKey)){ + KMException.throwIt(KMError.INVALID_KEY_BLOB); + } + while (tagInd < (short) swEnforcedTagsArr.length) { + if ((swEnforcedTagsArr[tagInd] == tagType) + && (swEnforcedTagsArr[(short) (tagInd + 1)] == tagKey)) { + Util.setShort(scratchPad, arrInd, tagPtr); + arrInd += 2; + break; + } + tagInd += 2; + } + index++; + } + return createKeyParameters(scratchPad, (short) (arrInd / 2)); + } + + public static short makeHidden(short keyParamsPtr, short rootOfTrustBlob, byte[] scratchPad) { + short appId = KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.APPLICATION_ID, keyParamsPtr); + if (appId != KMTag.INVALID_VALUE) { + appId = KMByteTag.cast(appId).getValue(); + } + short appData = + KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.APPLICATION_DATA, keyParamsPtr); + if (appData != KMTag.INVALID_VALUE) { + appData = KMByteTag.cast(appData).getValue(); + } + return makeHidden(appId, appData, rootOfTrustBlob, scratchPad); + } + + public static short makeHidden(short appIdBlob, short appDataBlob, short rootOfTrustBlob, byte[] scratchPad){ + // Order in which the hidden array is created should not change. + short index = 0; + KMByteBlob.cast(rootOfTrustBlob); + Util.setShort(scratchPad, index, rootOfTrustBlob); + index += 2; + if (appIdBlob != KMTag.INVALID_VALUE) { + KMByteBlob.cast(appIdBlob); + Util.setShort(scratchPad, index, appIdBlob); + index += 2; + } + if (appDataBlob != KMTag.INVALID_VALUE) { + Util.setShort(scratchPad, index, appDataBlob); + index += 2; + } + return createKeyParameters(scratchPad, (short)(index/2)); + + } + public static boolean isValidTag(short tagType, short tagKey) { + short[] invalidTagsArr = { + KMType.BYTES_TAG, KMType.NONCE, + KMType.BYTES_TAG, KMType.ASSOCIATED_DATA, + KMType.BYTES_TAG, KMType.UNIQUE_ID, + KMType.UINT_TAG, KMType.MAC_LENGTH, + KMType.BOOL_TAG, KMType.BOOTLOADER_ONLY + }; + short index = 0; + if (tagKey == KMType.INVALID_TAG) { + return false; + } + while (index < invalidTagsArr.length) { + if ((tagType == invalidTagsArr[index]) && (tagKey == invalidTagsArr[(short) (index + 1)])) { + return false; + } + index += 2; + } + return true; + } + + public static short createKeyParameters(byte[] ptrArr, short len) { + short arrPtr = KMArray.instance(len); + short index = 0; + short ptr = 0; + while (index < len) { + KMArray.cast(arrPtr).add(index, Util.getShort(ptrArr, ptr)); + index++; + ptr += 2; + } + return KMKeyParameters.instance(arrPtr); + } } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java b/Applet/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java index 427296df..8ea7ad74 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java @@ -19,10 +19,17 @@ import javacard.framework.APDU; import javacard.framework.Applet; import javacard.framework.AppletEvent; -import javacard.framework.CardRuntimeException; import javacard.framework.ISO7816; import javacard.framework.ISOException; +import javacard.framework.JCSystem; import javacard.framework.Util; +import javacard.security.AESKey; +import javacard.security.DESKey; +import javacard.security.ECPrivateKey; +import javacard.security.ECPublicKey; +import javacard.security.HMACKey; +import javacard.security.KeyPair; +import javacard.security.RSAPrivateKey; import javacardx.apdu.ExtendedLength; /** @@ -34,10 +41,12 @@ // - remove this in future. public class KMKeymasterApplet extends Applet implements AppletEvent, ExtendedLength { // Constants. - private static final short MAX_LENGTH = (short) 0x1000; // TODO: make this value configurable. + public static final byte AES_BLOCK_SIZE = 16; + public static final short MAX_LENGTH = (short) 0x1000; 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) 128; + private static final short MAX_IO_LENGTH = 0x400; // Possible states of the applet. private static final byte ILLEGAL_STATE = 0x00; private static final byte INSTALL_STATE = 0x01; @@ -45,7 +54,6 @@ public class KMKeymasterApplet extends Applet implements AppletEvent, ExtendedLe private static final byte ACTIVE_STATE = 0x03; private static final byte INACTIVE_STATE = 0x04; private static final byte UNINSTALLED_STATE = 0x05; - // Commands private static final byte INS_GENERATE_KEY_CMD = 0x10; private static final byte INS_IMPORT_KEY_CMD = 0x11; @@ -67,25 +75,63 @@ public class KMKeymasterApplet extends Applet implements AppletEvent, ExtendedLe private static final byte INS_FINISH_OPERATION_CMD = 0x21; private static final byte INS_ABORT_OPERATION_CMD = 0x22; private static final byte INS_PROVISION_CMD = 0x23; - - // GetHwInfo information - // TODO change this to just filling the buffer + private static final byte INS_SET_BOOT_PARAMS_CMD = 0x24; + // Data Dictionary items + public static final byte DATA_ARRAY_SIZE = 25; + public static final byte TMP_VARIABLE_ARRAY_SIZE = 20; + public static final byte UPDATE_PARAM_ARRAY_SIZE = 40; + public static final byte KEY_PARAMETERS = 0; + public static final byte KEY_CHARACTERISTICS = 1; + public static final byte HIDDEN_PARAMETERS = 2; + public static final byte HW_PARAMETERS = 3; + public static final byte SW_PARAMETERS = 4; + public static final byte AUTH_DATA = 5; + public static final byte AUTH_TAG = 6; + public static final byte NONCE = 7; + public static final byte KEY_BLOB = 8; + public static final byte AUTH_DATA_LENGTH = 9; + public static final byte SECRET = 10; + public static final byte ROT = 11; + public static final byte DERIVED_KEY = 12; + public static final byte RSA_PUB_EXPONENT = 13; + public static final byte APP_ID = 14; + public static final byte APP_DATA = 15; + public static final byte PUB_KEY = 16; + public static final byte IMPORTED_KEY_BLOB = 17; + public static final byte ORIGIN = 18; + // AddRngEntropy private static final short MAX_SEED_SIZE = 2048; - - // State of the applet. - private KMEncoder encoder; - private KMDecoder decoder; - private KMRepository repository; - private byte keymasterState = ILLEGAL_STATE; - private byte[] buffer; - private short bufferStartOffset; - private short bufferLength; + // Keyblob constants + public static final byte KEY_BLOB_SECRET = 0; + public static final byte KEY_BLOB_NONCE = 1; + public static final byte KEY_BLOB_AUTH_TAG = 2; + public static final byte KEY_BLOB_KEYCHAR = 3; + public static final byte KEY_BLOB_PUB_KEY = 4; + // AES GCM constants + private static final byte AES_GCM_AUTH_TAG_LENGTH = 12; + private static final byte AES_GCM_NONCE_LENGTH = 12; + // Keymaster Applet attributes + private static byte keymasterState = ILLEGAL_STATE; + private static KMEncoder encoder; + private static KMDecoder decoder; + private static KMRepository repository; + private static KMCryptoProvider cryptoProvider; + private static byte[] buffer; + private static short bufferLength; + private static short bufferStartOffset; + private static boolean provisionDone; + private static boolean setBootParamsDone; + private static short[] tmpVariables; + private static short[] data; /** Registers this applet. */ protected KMKeymasterApplet() { + // TODO change this to make this compile time variation. + cryptoProvider = new KMSimulator(); keymasterState = KMKeymasterApplet.INSTALL_STATE; - repository = KMRepository.instance(); - KMUtil.init(); + data = JCSystem.makeTransientShortArray((short) DATA_ARRAY_SIZE, JCSystem.CLEAR_ON_RESET); + tmpVariables = JCSystem.makeTransientShortArray((short) TMP_VARIABLE_ARRAY_SIZE, JCSystem.CLEAR_ON_RESET); + repository = new KMRepository(cryptoProvider.getTrueRandomNumber((short) 256)); KMType.initialize(); encoder = new KMEncoder(); decoder = new KMDecoder(); @@ -100,7 +146,7 @@ protected KMKeymasterApplet() { * @param bLength the length in bytes of the parameter data in bArray */ public static void install(byte[] bArray, short bOffset, byte bLength) { - KMKeymasterApplet keymaster = new KMKeymasterApplet(); + new KMKeymasterApplet(); } /** @@ -163,6 +209,8 @@ public void process(APDU apdu) { byte apduClass = apduBuffer[ISO7816.OFFSET_CLA]; byte apduIns = apduBuffer[ISO7816.OFFSET_INS]; short P1P2 = Util.getShort(apduBuffer, ISO7816.OFFSET_P1); + buffer = repository.getHeap(); + bufferStartOffset = repository.alloc(MAX_IO_LENGTH); // Validate APDU Header. if ((apduClass != CLA_ISO7816_NO_SM_NO_CHAN)) { ISOException.throwIt(ISO7816.SW_CLA_NOT_SUPPORTED); @@ -170,12 +218,18 @@ public void process(APDU apdu) { ISOException.throwIt(ISO7816.SW_INCORRECT_P1P2); } // Validate whether INS can be supported - if (!(apduIns >= INS_GENERATE_KEY_CMD && apduIns <= INS_PROVISION_CMD)) { + if (!(apduIns >= INS_GENERATE_KEY_CMD && apduIns <= INS_SET_BOOT_PARAMS_CMD)) { ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED); } // Validate if INS is provision command if applet is in FIRST_SELECT_STATE. if (keymasterState == KMKeymasterApplet.FIRST_SELECT_STATE) { - if (apduIns != INS_PROVISION_CMD) { + if ((apduIns != INS_PROVISION_CMD) && (apduIns != INS_SET_BOOT_PARAMS_CMD)) { + ISOException.throwIt(ISO7816.SW_COMMAND_NOT_ALLOWED); + } + if (apduIns == INS_PROVISION_CMD && provisionDone) { + ISOException.throwIt(ISO7816.SW_COMMAND_NOT_ALLOWED); + } + if (apduIns == INS_SET_BOOT_PARAMS_CMD && setBootParamsDone) { ISOException.throwIt(ISO7816.SW_COMMAND_NOT_ALLOWED); } } @@ -243,44 +297,44 @@ public void process(APDU apdu) { case INS_PROVISION_CMD: processProvisionCmd(apdu); break; + case INS_SET_BOOT_PARAMS_CMD: + processSetBootParamsCmd(apdu); + break; default: ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED); } - - } catch (CardRuntimeException exception) { - if (!(KMException.handle(exception.getReason()))) { - CardRuntimeException.throwIt(exception.getReason()); - } + } catch (KMException exception) { + sendError(apdu, exception.reason); + exception.clear(); } finally { repository.clean(); } } /** Sends a response, may be extended response, as requested by the command. */ - private void sendOutgoing(APDU apdu) { - if (bufferLength > MAX_LENGTH) { + public static void sendOutgoing(APDU apdu) { + if (bufferLength > MAX_IO_LENGTH) { ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); } // Send data apdu.setOutgoing(); + // short currentBlockSize = apdu.getOutBlockSize(); apdu.setOutgoingLength(bufferLength); apdu.sendBytesLong(buffer, bufferStartOffset, bufferLength); } /** Receives data, which can be extended data, as requested by the command instance. */ - private void receiveIncoming(APDU apdu) { + public static void receiveIncoming(APDU apdu) { byte[] srcBuffer = apdu.getBuffer(); short recvLen = apdu.setIncomingAndReceive(); short srcOffset = apdu.getOffsetCdata(); - bufferLength = apdu.getIncomingLength(); - bufferStartOffset = repository.alloc(bufferLength); - buffer = repository.getHeap(); + bufferLength = apdu.getIncomingLength(); short index = bufferStartOffset; // Receive data - if (bufferLength > MAX_LENGTH) { + if (bufferLength > MAX_IO_LENGTH) { ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); } - while (recvLen > 0 && ((short)(index - bufferStartOffset) < bufferLength)) { + while (recvLen > 0 && ((short) (index - bufferStartOffset) < bufferLength)) { Util.arrayCopyNonAtomic(srcBuffer, srcOffset, buffer, index, recvLen); index += recvLen; recvLen = apdu.receiveBytes(srcOffset); @@ -293,31 +347,33 @@ private void processProvisionCmd(APDU apdu) { // Re-purpose the apdu buffer as scratch pad. byte[] scratchPad = apdu.getBuffer(); Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) apdu.getBuffer().length, (byte) 0); - // Argument 1 + // Arguments short keyparams = KMKeyParameters.exp(); - // Argument 2 short keyFormat = KMEnum.instance(KMType.KEY_FORMAT); - // Argument 3 short keyBlob = KMByteBlob.exp(); - // Array of expected arguments short argsProto = KMArray.instance((short) 3); KMArray.cast(argsProto).add((short) 0, keyparams); KMArray.cast(argsProto).add((short) 1, keyFormat); KMArray.cast(argsProto).add((short) 2, keyBlob); // Decode the argument short args = decoder.decode(argsProto, buffer, bufferStartOffset, bufferLength); + // key params should have os patch, os version and verified root of trust + // TODO execute the function // Change the state to ACTIVE if (keymasterState == KMKeymasterApplet.FIRST_SELECT_STATE) { - keymasterState = KMKeymasterApplet.ACTIVE_STATE; + provisionDone = true; + if (setBootParamsDone) { + keymasterState = KMKeymasterApplet.ACTIVE_STATE; + } } } private void processGetHwInfoCmd(APDU apdu) { // No arguments expected final byte[] JavacardKeymasterDevice = { - 0x4A, 0x61, 0x76, 0x61, 0x63, 0x61, 0x72, 0x64, 0x4B, 0x65, 0x79, 0x6D, 0x61, 0x73, 0x74, 0x65, - 0x72, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, + 0x4A, 0x61, 0x76, 0x61, 0x63, 0x61, 0x72, 0x64, 0x4B, 0x65, 0x79, 0x6D, 0x61, 0x73, 0x74, + 0x65, 0x72, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, }; final byte[] Google = {0x47, 0x6F, 0x6F, 0x67, 0x6C, 0x65}; @@ -325,14 +381,13 @@ private void processGetHwInfoCmd(APDU apdu) { short respPtr = KMArray.instance((short) 3); KMArray resp = KMArray.cast(respPtr); resp.add((short) 0, KMEnum.instance(KMType.HARDWARE_TYPE, KMType.STRONGBOX)); - resp.add((short) 1, KMByteBlob.instance( - JavacardKeymasterDevice, (short) 0, (short) JavacardKeymasterDevice.length)); + resp.add( + (short) 1, + KMByteBlob.instance( + JavacardKeymasterDevice, (short) 0, (short) JavacardKeymasterDevice.length)); resp.add((short) 2, KMByteBlob.instance(Google, (short) 0, (short) Google.length)); - //TODO change from MAX_LENGTH to actual length. - buffer = repository.getHeap(); - bufferStartOffset = repository.alloc((short)128); // Encode the response - actual bufferLength is 86 - bufferLength = encoder.encode(respPtr, buffer, bufferStartOffset, MAX_LENGTH); + bufferLength = encoder.encode(respPtr, buffer, bufferStartOffset); // send buffer to master sendOutgoing(apdu); } @@ -352,96 +407,1109 @@ private void processAddRngEntropyCmd(APDU apdu) { KMByteBlob blob = KMByteBlob.cast(KMArray.cast(args).get((short) 0)); // Maximum 2KiB of seed is allowed. if (blob.length() > MAX_SEED_SIZE) { - ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); + KMException.throwIt(KMError.INVALID_ARGUMENT); } - // Get existing entropy pool. - byte[] entPool = KMUtil.getEntropyPool(); - // Create new temporary pool. - byte[] heapRef = repository.getHeap(); - short poolStart = repository.alloc((short) entPool.length); - // Populate the new pool with the entropy which is derived from current entropy pool. - KMUtil.newRandomNumber(heapRef, poolStart, (short) entPool.length); - // Copy the entropy to the current pool - updates the entropy pool. - Util.arrayCopy(heapRef, poolStart, entPool, (short) 0, (short) entPool.length); - short index = 0; - short randIndex = 0; - // Mix (XOR) the seed received from the master in the entropy pool - 32 bytes (entPool.length). - // at a time. - while (index < blob.length()) { - entPool[randIndex] = (byte) (entPool[randIndex] ^ blob.get(index)); - randIndex++; - index++; - if (randIndex >= entPool.length) { - randIndex = 0; + cryptoProvider.addRngEntropy(blob.getBuffer(), blob.getStartOff(), blob.length()); + } + + private void processAbortOperationCmd(APDU apdu) {} + + private void processFinishOperationCmd(APDU apdu) {} + + private void processUpdateOperationCmd(APDU apdu) {} + + private void processBeginOperationCmd(APDU apdu) {} + + private void processGetKeyCharacteristicsCmd(APDU apdu) { + // Receive the incoming request fully from the master. + receiveIncoming(apdu); + // Re-purpose the apdu buffer as scratch pad. + byte[] scratchPad = apdu.getBuffer(); + // Arguments + tmpVariables[0] = KMArray.instance((short) 3); + KMArray.cast(tmpVariables[0]).add((short) 0, KMByteBlob.exp()); + KMArray.cast(tmpVariables[0]).add((short) 1, KMByteBlob.exp()); + KMArray.cast(tmpVariables[0]).add((short) 2, KMByteBlob.exp()); + // Decode the arguments + tmpVariables[0] = decoder.decode(tmpVariables[0], buffer, bufferStartOffset, bufferLength); + data[KEY_BLOB] = KMArray.cast(tmpVariables[0]).get((short) 0); + data[APP_ID] = KMArray.cast(tmpVariables[0]).get((short) 1); + data[APP_DATA] = KMArray.cast(tmpVariables[0]).get((short) 2); + if(KMByteBlob.cast(data[APP_ID]).length() == 2){ + if(Util.getShort(repository.getHeap(),KMByteBlob.cast(data[APP_ID]).getStartOff()) == KMType.INVALID_VALUE){ + data[APP_ID] = KMType.INVALID_VALUE; } } + if(KMByteBlob.cast(data[APP_DATA]).length() == 2){ + if(Util.getShort(repository.getHeap(),KMByteBlob.cast(data[APP_DATA]).getStartOff()) == KMType.INVALID_VALUE){ + data[APP_DATA] = KMType.INVALID_VALUE; + } + } + // Parse Key Blob + parseEncryptedKeyBlob(scratchPad); + // Check Version and Patch Level + checkVersionAndPatchLevel(scratchPad); + // make response. + tmpVariables[0] = KMArray.instance((short) 2); + KMArray.cast(tmpVariables[0]).add((short) 0, KMInteger.uint_16(KMError.OK)); + KMArray.cast(tmpVariables[0]).add((short) 1, data[KEY_CHARACTERISTICS]); + // Encode the response + bufferLength = encoder.encode(tmpVariables[0], buffer, bufferStartOffset); + sendOutgoing(apdu); } - private void processAbortOperationCmd(APDU apdu) { + private void processGetHmacSharingParamCmd(APDU apdu) {} - } + private void processVerifyAuthenticationCmd(APDU apdu) {} - private void processFinishOperationCmd(APDU apdu) { + private void processDestroyAttIdsCmd(APDU apdu) {} - } + private void processComputeSharedHmacCmd(APDU apdu) {} - private void processUpdateOperationCmd(APDU apdu) { + private void processDeleteAllKeysCmd(APDU apdu) {} - } + private void processDeleteKeyCmd(APDU apdu) {} - private void processBeginOperationCmd(APDU apdu) { + private void processUpgradeKeyCmd(APDU apdu) {} - } + private void processAttestKeyCmd(APDU apdu) {} + + private void processExportKeyCmd(APDU apdu) {} - private void processGetKeyCharacteristicsCmd(APDU apdu) { + private void processImportWrappedKeyCmd(APDU apdu) {} + private void processImportKeyCmd(APDU apdu) { + if (repository.keyBlobCount > repository.MAX_BLOB_STORAGE) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + // Receive the incoming request fully from the master into buffer. + receiveIncoming(apdu); + byte[] scratchPad = apdu.getBuffer(); + tmpVariables[1] = KMArray.instance((short)3); + // Arguments + tmpVariables[2] = KMKeyParameters.exp(); + KMArray.cast(tmpVariables[1]).add((short) 0, tmpVariables[2]); + KMArray.cast(tmpVariables[1]).add((short) 1, KMEnum.instance(KMType.KEY_FORMAT)); + KMArray.cast(tmpVariables[1]).add((short) 2, KMByteBlob.exp()); + // Decode the arguments + tmpVariables[2] = decoder.decode(tmpVariables[1], buffer, bufferStartOffset, bufferLength); + data[KEY_PARAMETERS] = KMArray.cast(tmpVariables[2]).get((short) 0); + tmpVariables[3] = KMArray.cast(tmpVariables[2]).get((short) 1); + data[IMPORTED_KEY_BLOB] = KMArray.cast(tmpVariables[2]).get((short) 2); + // Keyformat must be RAW format - X509 and PKCS8 not implemented. + tmpVariables[3] = KMEnum.cast(tmpVariables[3]).getVal(); + if (tmpVariables[3] != KMType.RAW) { + KMException.throwIt(KMError.UNIMPLEMENTED); + } + // get algorithm + tmpVariables[3] = KMEnumTag.getValue(KMType.ALGORITHM, data[KEY_PARAMETERS]); + if (tmpVariables[3] == KMType.INVALID_VALUE) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + // Check algorithm and dispatch to appropriate handler. + switch (tmpVariables[3]) { + case KMType.RSA: + importRSAKey(scratchPad); + break; + case KMType.AES: + importAESKey(scratchPad); + break; + case KMType.DES: + importTDESKey(scratchPad); + break; + case KMType.HMAC: + importHmacKey(scratchPad); + break; + case KMType.EC: + importECKeys(scratchPad); + break; + default: + KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM); + break; + } + // create key blob + data[ORIGIN] = KMType.IMPORTED; + createEncryptedKeyBlob(scratchPad); + // persist auth tag for rollback resistance. + repository.persistAuthTag(data[AUTH_TAG]); + // prepare the response + tmpVariables[0] = KMArray.instance((short) 3); + KMArray.cast(tmpVariables[0]).add((short) 0, KMInteger.uint_16(KMError.OK)); + KMArray.cast(tmpVariables[0]).add((short) 1, data[KEY_BLOB]); + KMArray.cast(tmpVariables[0]).add((short) 2, data[KEY_CHARACTERISTICS]); + // Encode the response + bufferLength = encoder.encode(tmpVariables[0], buffer, bufferStartOffset); + sendOutgoing(apdu); + } + + private void importECKeys(byte[] scratchPad) { + // Decode key material + tmpVariables[0] = KMArray.instance((short) 3); + KMArray.cast(tmpVariables[0]).add((short) 0, KMByteBlob.exp()); // secret + KMArray.cast(tmpVariables[0]).add((short) 1, KMByteBlob.exp()); // public key + KMArray.cast(tmpVariables[0]).add((short) 2, KMEnumTag.exp()); // curve + tmpVariables[0] = + decoder.decode( + tmpVariables[0], + KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).getBuffer(), + KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).getStartOff(), + KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).length()); + data[SECRET] = KMArray.cast(tmpVariables[0]).get((short) 0); + data[PUB_KEY] = KMArray.cast(tmpVariables[0]).get((short) 1); + tmpVariables[1] = KMArray.cast(tmpVariables[0]).get((short) 2); + tmpVariables[1] = KMEnumTag.cast(tmpVariables[1]).getValue(); + // curve must be P_256 + if(tmpVariables[1] != KMType.P_256){ + KMException.throwIt(KMError.UNSUPPORTED_EC_CURVE); + } + // initialize 256 bit p256 key for given private key and public key. + ECPrivateKey ecKey = + cryptoProvider.createEcPrivateKey( + KMByteBlob.cast(data[PUB_KEY]).getBuffer(), + KMByteBlob.cast(data[PUB_KEY]).getStartOff(), + KMByteBlob.cast(data[PUB_KEY]).length(), + KMByteBlob.cast(data[SECRET]).getBuffer(), + KMByteBlob.cast(data[SECRET]).getStartOff(), + KMByteBlob.cast(data[SECRET]).length() + ); + tmpVariables[4] = 0; // index for update list in scratchPad + // check whether the keysize tag is present in key parameters. + tmpVariables[2] = + KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); + if (tmpVariables[2] != KMType.INVALID_VALUE) { + if (tmpVariables[2] != 256) { + KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH); + } + }else{ + // add the key size to scratchPad + tmpVariables[5] = KMInteger.uint_16((short)256); + tmpVariables[6] = KMIntegerTag.instance(KMType.UINT_TAG,KMType.KEYSIZE, tmpVariables[5]); + Util.setShort(scratchPad,tmpVariables[4],tmpVariables[6]); + tmpVariables[4] +=2; + } + // check the curve if present in key parameters. + tmpVariables[3] = KMEnumTag.getValue(KMType.ECCURVE,data[KEY_PARAMETERS]); + if(tmpVariables[3] != KMType.INVALID_VALUE){ + if(tmpVariables[3] != tmpVariables[1]){ + KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH); + } + }else{ + // add the curve to scratchPad + tmpVariables[5] = KMEnumTag.instance(KMType.ECCURVE,KMType.P_256); + Util.setShort(scratchPad,tmpVariables[4],tmpVariables[5]); + tmpVariables[4] +=2; + } + // add scratch pad to key parameters + updateKeyParameters(scratchPad, tmpVariables[4]); + // validate updated key parameters. + validateECKeys(scratchPad); + data[KEY_BLOB] = KMArray.instance((short)5); + KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_PUB_KEY, data[PUB_KEY]); } - private void processGetHmacSharingParamCmd(APDU apdu) { + private void importHmacKey(byte[] scratchPad) { + // Get Key + tmpVariables[0] = KMArray.instance((short) 1); + KMArray.cast(tmpVariables[0]).add((short) 0, KMByteBlob.exp()); // secret + tmpVariables[0] = + decoder.decode( + tmpVariables[0], + KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).getBuffer(), + KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).getStartOff(), + KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).length()); + data[SECRET] = KMArray.cast(tmpVariables[0]).get((short) 0); + // create HMAC key of up to 512 bit + HMACKey hmacKey = cryptoProvider.createHMACKey( + KMByteBlob.cast(data[SECRET]).getBuffer(), + KMByteBlob.cast(data[SECRET]).getStartOff(), + KMByteBlob.cast(data[SECRET]).length() + ); + tmpVariables[4] = 0; // index in scratchPad for update params + // check the keysize tag if present in key parameters. + tmpVariables[2] = + KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); + if (tmpVariables[2] != KMType.INVALID_VALUE) { + if (!(tmpVariables[2] > 64 && tmpVariables[2] <= 512 && tmpVariables[2]%8 ==0)) { + KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH); + } + }else{ + // add the key size to scratchPad + tmpVariables[5] = KMInteger.uint_16(KMByteBlob.cast(data[SECRET]).length()); + tmpVariables[6] = KMIntegerTag.instance(KMType.UINT_TAG,KMType.KEYSIZE, tmpVariables[5]); + Util.setShort(scratchPad,tmpVariables[4],tmpVariables[6]); + tmpVariables[4] +=2; + } + // update the key parameters list + updateKeyParameters(scratchPad, tmpVariables[4]); + // validate HMAC Key parameters + validateHmacKey(scratchPad); + data[KEY_BLOB] = KMArray.instance((short)4); } - private void processVerifyAuthenticationCmd(APDU apdu) { + private void importTDESKey(byte[] scratchPad) { + // Decode Key Material + tmpVariables[0] = KMArray.instance((short) 1); + KMArray.cast(tmpVariables[0]).add((short) 0, KMByteBlob.exp()); // secret + tmpVariables[0] = + decoder.decode( + tmpVariables[0], + KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).getBuffer(), + KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).getStartOff(), + KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).length()); + data[SECRET] = KMArray.cast(tmpVariables[0]).get((short) 0); + DESKey desKey = cryptoProvider.createTDESKey( + KMByteBlob.cast(data[SECRET]).getBuffer(), + KMByteBlob.cast(data[SECRET]).getStartOff(), + KMByteBlob.cast(data[SECRET]).length() + ); + tmpVariables[4] = 0; // index in scratchPad for update params + // check the keysize tag if present in key parameters. + tmpVariables[2] = + KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); + if (tmpVariables[2] != KMType.INVALID_VALUE) { + if (tmpVariables[2] != 168) { + KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH); + } + }else{ + // add the key size to scratchPad + tmpVariables[5] = KMInteger.uint_16((short)168); + tmpVariables[6] = KMIntegerTag.instance(KMType.UINT_TAG,KMType.KEYSIZE, tmpVariables[5]); + Util.setShort(scratchPad,tmpVariables[4],tmpVariables[6]); + tmpVariables[4] +=2; + } + // update the key parameters list + updateKeyParameters(scratchPad, tmpVariables[4]); + // validate TDES Key parameters + validateTDESKey(scratchPad); + data[KEY_BLOB] = KMArray.instance((short)4); } - private void processDestroyAttIdsCmd(APDU apdu) { + private void importAESKey(byte[] scratchPad) { + // Get Key + tmpVariables[0] = KMArray.instance((short) 1); + KMArray.cast(tmpVariables[0]).add((short) 0, KMByteBlob.exp()); // secret + tmpVariables[0] = + decoder.decode( + tmpVariables[0], + KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).getBuffer(), + KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).getStartOff(), + KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).length()); + data[SECRET] = KMArray.cast(tmpVariables[0]).get((short) 0); + // create 128 or 256 bit AES key + AESKey aesKey = cryptoProvider.createAESKey( + KMByteBlob.cast(data[SECRET]).getBuffer(), + KMByteBlob.cast(data[SECRET]).getStartOff(), + KMByteBlob.cast(data[SECRET]).length() + ); + tmpVariables[4] = 0; // index in scratchPad for update params + // check the keysize tag if present in key parameters. + tmpVariables[2] = + KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); + if (tmpVariables[2] != KMType.INVALID_VALUE) { + if (tmpVariables[2] != 128 && tmpVariables[2] != 256) { + KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH); + } + }else{ + // add the key size to scratch pad + // add the key size to scratchPad + tmpVariables[5] = KMInteger.uint_16(KMByteBlob.cast(data[SECRET]).length()); + tmpVariables[6] = KMIntegerTag.instance(KMType.UINT_TAG,KMType.KEYSIZE, tmpVariables[5]); + Util.setShort(scratchPad,tmpVariables[4],tmpVariables[6]); + tmpVariables[4] +=2; + } + // update the key parameters list + updateKeyParameters(scratchPad, tmpVariables[4]); + // validate AES Key parameters + validateAESKey(scratchPad); + data[KEY_BLOB] = KMArray.instance((short)4); + } + private void importRSAKey(byte[] scratchPad) { + // Decode key material + tmpVariables[0] = KMArray.instance((short) 2); + KMArray.cast(tmpVariables[0]).add((short) 0, KMByteBlob.exp()); // secret = private exponent + KMArray.cast(tmpVariables[0]).add((short) 1, KMByteBlob.exp()); // modulus + tmpVariables[0] = + decoder.decode( + tmpVariables[0], + KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).getBuffer(), + KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).getStartOff(), + KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).length()); + data[SECRET] = KMArray.cast(tmpVariables[0]).get((short) 0); + data[PUB_KEY] = KMArray.cast(tmpVariables[0]).get((short) 1); + tmpVariables[4] = 0; // index in scratchPad for update parameters. + // validate public exponent if present in key params - it must be 0x010001 + tmpVariables[2] = + KMIntegerTag.getValue( + scratchPad, + (short) 10, // using offset 10 as first 10 bytes reserved for update params + KMType.ULONG_TAG, + KMType.RSA_PUBLIC_EXPONENT, + data[KEY_PARAMETERS]); + if (tmpVariables[2] != KMTag.INVALID_VALUE) { + if ( tmpVariables[2] != 4 || Util.getShort(scratchPad, (short) 10) != 0x01 + || Util.getShort(scratchPad, (short) 12) != 0x01) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + }else{ + // add public exponent to scratchPad + Util.setShort(scratchPad,(short)10, (short)0x01); + Util.setShort(scratchPad,(short)12, (short)0x01); + tmpVariables[5] = KMInteger.uint_32(scratchPad,(short)10); + tmpVariables[6] = KMIntegerTag.instance(KMType.ULONG_TAG,KMType.RSA_PUBLIC_EXPONENT, tmpVariables[5]); + Util.setShort(scratchPad,tmpVariables[4],tmpVariables[6]); + tmpVariables[4] +=2; + } + + // initialize 2048 bit private key for given private exp and modulus. + RSAPrivateKey rsaKey = + cryptoProvider.createRsaPrivateKey( + KMByteBlob.cast(data[PUB_KEY]).getBuffer(), + KMByteBlob.cast(data[PUB_KEY]).getStartOff(), + KMByteBlob.cast(data[PUB_KEY]).length(), + KMByteBlob.cast(data[SECRET]).getBuffer(), + KMByteBlob.cast(data[SECRET]).getStartOff(), + KMByteBlob.cast(data[SECRET]).length() + ); + // check the keysize tag if present in key parameters. + tmpVariables[2] = + KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); + if (tmpVariables[2] != KMType.INVALID_VALUE) { + if (tmpVariables[2] != 2048) { + KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH); + } + }else{ + // add the key size to scratchPad + tmpVariables[5] = KMInteger.uint_16((short)2048); + tmpVariables[6] = KMIntegerTag.instance(KMType.UINT_TAG,KMType.KEYSIZE, tmpVariables[5]); + Util.setShort(scratchPad,tmpVariables[4],tmpVariables[6]); + tmpVariables[4] +=2; + } + // update the key parameters list + updateKeyParameters(scratchPad, tmpVariables[4]); + // validate RSA Key parameters + validateRSAKey(scratchPad); + data[KEY_BLOB] = KMArray.instance((short)5); + KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_PUB_KEY, data[PUB_KEY]); } - private void processComputeSharedHmacCmd(APDU apdu) { + private void updateKeyParameters(byte[] ptrArr, short len){ + if(len == 0) { + return; // nothing to update + } + // Create Update Param array and copy current params + tmpVariables[0] = KMKeyParameters.cast(data[KEY_PARAMETERS]).getVals(); + tmpVariables[1] = (short)(KMArray.cast(tmpVariables[0]).length()+(short)(len/2)); + tmpVariables[1] = KMArray.instance(tmpVariables[1]);// update params + tmpVariables[2] = KMArray.cast(tmpVariables[0]).length(); + tmpVariables[3] = 0; + // copy the existing key parameters to updated array + while(tmpVariables[3] < tmpVariables[2]){ + tmpVariables[4] = KMArray.cast(tmpVariables[0]).get(tmpVariables[3]); + KMArray.cast(tmpVariables[1]).add(tmpVariables[3],tmpVariables[4]); + tmpVariables[3]++; + } + // copy new parameters to updated array + tmpVariables[2] = KMArray.cast(tmpVariables[1]).length(); + tmpVariables[5] = 0; // index in ptrArr + while(tmpVariables[3] < tmpVariables[2]){ + tmpVariables[4] = Util.getShort(ptrArr,tmpVariables[5]); + KMArray.cast(tmpVariables[1]).add(tmpVariables[3],tmpVariables[4]); + tmpVariables[3]++; + tmpVariables[5] +=2; + } + // replace with updated key parameters. + data[KEY_PARAMETERS] = KMKeyParameters.instance(tmpVariables[1]); + } + // TODO Add Signature verification. + private void processSetBootParamsCmd(APDU apdu) { + receiveIncoming(apdu); + // Re-purpose the apdu buffer as scratch pad. + byte[] scratchPad = apdu.getBuffer(); + Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) apdu.getBuffer().length, (byte) 0); + // Argument 1 OS Version + // short osVersionExp = KMIntegerTag.exp(KMType.UINT_TAG); + tmpVariables[0] = KMIntegerTag.exp(KMType.UINT_TAG); + // Argument 2 OS Patch level + // short osPatchExp = KMIntegerTag.exp(KMType.UINT_TAG); + tmpVariables[1] = KMIntegerTag.exp(KMType.UINT_TAG); + // Argument 3 Verified Boot Key + // short bootKeyExp = KMByteBlob.exp(); + tmpVariables[2] = KMByteBlob.exp(); + // Argument 4 Verified Boot Hash + // short bootHashExp = KMByteBlob.exp(); + tmpVariables[3] = KMByteBlob.exp(); + // Argument 5 Verified Boot State + // short bootStateExp = KMEnum.instance(KMType.VERIFIED_BOOT_STATE); + tmpVariables[4] = KMEnum.instance(KMType.VERIFIED_BOOT_STATE); + // Argument 6 Device Locked + // short deviceLockedExp = KMEnum.instance(KMType.DEVICE_LOCKED); + tmpVariables[5] = KMEnum.instance(KMType.DEVICE_LOCKED); + // Array of expected arguments + short argsProto = KMArray.instance((short) 6); + KMArray.cast(argsProto).add((short) 0, tmpVariables[0]); + KMArray.cast(argsProto).add((short) 1, tmpVariables[1]); + KMArray.cast(argsProto).add((short) 2, tmpVariables[2]); + KMArray.cast(argsProto).add((short) 3, tmpVariables[3]); + KMArray.cast(argsProto).add((short) 4, tmpVariables[4]); + KMArray.cast(argsProto).add((short) 5, tmpVariables[5]); + // Decode the arguments + short args = decoder.decode(argsProto, buffer, bufferStartOffset, bufferLength); + // short osVersionTagPtr = KMArray.cast(args).get((short) 0); + tmpVariables[0] = KMArray.cast(args).get((short) 0); + // short osPatchTagPtr = KMArray.cast(args).get((short) 1); + tmpVariables[1] = KMArray.cast(args).get((short) 1); + // short verifiedBootKeyPtr = KMArray.cast(args).get((short) 2); + tmpVariables[2] = KMArray.cast(args).get((short) 2); + // short verifiedBootHashPtr = KMArray.cast(args).get((short) 3); + tmpVariables[3] = KMArray.cast(args).get((short) 3); + // short verifiedBootStatePtr = KMArray.cast(args).get((short) 4); + tmpVariables[4] = KMArray.cast(args).get((short) 4); + // short deviceLockedPtr = KMArray.cast(args).get((short) 5); + tmpVariables[5] = KMArray.cast(args).get((short) 5); + if (KMByteBlob.cast(tmpVariables[2]).length() > repository.BOOT_KEY_MAX_SIZE) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + if (KMByteBlob.cast(tmpVariables[3]).length() > repository.BOOT_HASH_MAX_SIZE) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + // Begin transaction + JCSystem.beginTransaction(); + short valPtr = KMIntegerTag.cast(tmpVariables[0]).getValue(); + KMInteger.cast(valPtr).getValue(repository.osVersion, (short) 0, (short) 4); + valPtr = KMIntegerTag.cast(tmpVariables[1]).getValue(); + KMInteger.cast(valPtr).getValue(repository.osPatch, (short) 0, (short) 4); + repository.actualBootKeyLength = KMByteBlob.cast(tmpVariables[2]).length(); + KMByteBlob.cast(tmpVariables[2]) + .getValue(repository.verifiedBootKey, (short) 0, repository.actualBootKeyLength); + repository.actualBootHashLength = KMByteBlob.cast(tmpVariables[3]).length(); + KMByteBlob.cast(tmpVariables[3]) + .getValue(repository.verifiedBootHash, (short) 0, repository.actualBootHashLength); + byte enumVal = KMEnum.cast(tmpVariables[4]).getVal(); + if (enumVal == KMTag.SELF_SIGNED_BOOT) { + repository.selfSignedBootFlag = true; + repository.verifiedBootFlag = false; + } else { + repository.selfSignedBootFlag = false; + repository.verifiedBootFlag = true; + } + enumVal = KMEnum.cast(tmpVariables[5]).getVal(); + if (enumVal == KMType.DEVICE_LOCKED_TRUE) { + repository.deviceLockedFlag = true; + } else { + repository.deviceLockedFlag = false; + } + if (keymasterState == KMKeymasterApplet.FIRST_SELECT_STATE) { + setBootParamsDone = true; + if (provisionDone) { + keymasterState = KMKeymasterApplet.ACTIVE_STATE; + } + } + // end transaction + JCSystem.commitTransaction(); } - private void processDeleteAllKeysCmd(APDU apdu) { + private static void processGenerateKey(APDU apdu) { + // before generating key, check whether max count reached + if (repository.keyBlobCount > repository.MAX_BLOB_STORAGE) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + // Receive the incoming request fully from the master into buffer. + receiveIncoming(apdu); + // Re-purpose the apdu buffer as scratch pad. + byte[] scratchPad = apdu.getBuffer(); + // Argument + tmpVariables[0] = KMKeyParameters.exp(); + // Array of expected arguments + tmpVariables[1] = KMArray.instance((short) 1); + KMArray.cast(tmpVariables[1]).add((short) 0, tmpVariables[0]); + // Decode the argument + tmpVariables[2] = decoder.decode(tmpVariables[1], buffer, bufferStartOffset, bufferLength); + data[KEY_PARAMETERS] = KMArray.cast(tmpVariables[2]).get((short) 0); + // get algorithm + tmpVariables[3] = KMEnumTag.getValue(KMType.ALGORITHM, data[KEY_PARAMETERS]); + if (tmpVariables[3] == KMType.INVALID_VALUE) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + // Check algorithm and dispatch to appropriate handler. + switch (tmpVariables[3]) { + case KMType.RSA: + generateRSAKey(scratchPad); + break; + case KMType.AES: + generateAESKey(scratchPad); + break; + case KMType.DES: + generateTDESKey(scratchPad); + break; + case KMType.HMAC: + generateHmacKey(scratchPad); + break; + case KMType.EC: + generateECKeys(scratchPad); + break; + default: + KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM); + break; + } + // create key blob + data[ORIGIN] = KMType.GENERATED; + createEncryptedKeyBlob(scratchPad); + // persist auth tag for rollback resistance. + repository.persistAuthTag(data[AUTH_TAG]); + // prepare the response + tmpVariables[0] = KMArray.instance((short) 3); + KMArray.cast(tmpVariables[0]).add((short) 0, KMInteger.uint_16(KMError.OK)); + KMArray.cast(tmpVariables[0]).add((short) 1, data[KEY_BLOB]); + KMArray.cast(tmpVariables[0]).add((short) 2, data[KEY_CHARACTERISTICS]); + // Encode the response + bufferLength = encoder.encode(tmpVariables[0], buffer, bufferStartOffset); + sendOutgoing(apdu); + } + private static void validateRSAKey(byte[] scratchPad){ + // Read key size + tmpVariables[0] = + KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); + if (tmpVariables[0] == KMTag.INVALID_VALUE) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + if (tmpVariables[0] != 2048) { + KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); + } + // Read public exponent into scratch pad + tmpVariables[1] = + KMIntegerTag.getValue( + scratchPad, + (short) 0, + KMType.ULONG_TAG, + KMType.RSA_PUBLIC_EXPONENT, + data[KEY_PARAMETERS]); + if ((tmpVariables[1] == KMTag.INVALID_VALUE) || (tmpVariables[1] != 4)) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + // Only exponent support is F4 - 65537 which is 0x00010001. + if (Util.getShort(scratchPad, (short) 0) != 0x01 + || Util.getShort(scratchPad, (short) 2) != 0x01) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } } - private void processDeleteKeyCmd(APDU apdu) { + // Generate key handlers + private static void generateRSAKey(byte[] scratchPad) { + // Validate RSA Key + validateRSAKey(scratchPad); + // Now generate 2048 bit RSA keypair for the given exponent + KeyPair rsaKey = cryptoProvider.createRsaKeyPair(); + // store the pub exponent + data[RSA_PUB_EXPONENT] = KMByteBlob.instance(scratchPad, (short) 0, tmpVariables[1]); + // extract modulus + tmpVariables[0] = ((RSAPrivateKey) rsaKey.getPrivate()).getModulus(scratchPad, (short) 0); + data[PUB_KEY] = KMByteBlob.instance(scratchPad, (short) 0, tmpVariables[0]); + // extract private key + tmpVariables[0] = ((RSAPrivateKey) rsaKey.getPrivate()).getExponent(scratchPad, (short) 0); + data[SECRET] = KMByteBlob.instance(scratchPad, (short) 0, tmpVariables[0]); + data[KEY_BLOB] = KMArray.instance((short) 5); + KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_PUB_KEY, data[PUB_KEY]); + } + private static void validateAESKey(byte[] scratchPad){ + // Read key size + tmpVariables[0] = + KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); + if (tmpVariables[0] == KMTag.INVALID_VALUE) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + if ((tmpVariables[0] != 256) && (tmpVariables[0] != 128)) { + KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); + } + // Read Block mode - array of byte values + tmpVariables[1] = + KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.BLOCK_MODE, data[KEY_PARAMETERS]); + if (tmpVariables[1] != KMTag.INVALID_VALUE) { // block mode specified + tmpVariables[2] = KMEnumArrayTag.cast(tmpVariables[0]).getValues(); // byte blob + tmpVariables[3] = KMByteBlob.cast(tmpVariables[2]).length(); // length + tmpVariables[4] = 0; // index + tmpVariables[5] = AES_BLOCK_SIZE; // block size + tmpVariables[5] = + KMKeyParameters.findTag( + KMType.UINT_TAG, + KMType.MIN_MAC_LENGTH, + data[KEY_PARAMETERS]); // Find Minimum Mac length + while (tmpVariables[4] < tmpVariables[3]) { // for each value in block mode array + if (KMByteBlob.cast(tmpVariables[2]).get(tmpVariables[4]) == KMType.GCM) { // if GCM mode + if (tmpVariables[5] == KMTag.INVALID_VALUE) { // minimum mac length must be specified + KMException.throwIt(KMError.MISSING_MAC_LENGTH); + } + tmpVariables[6] = KMInteger.cast(KMIntegerTag.cast(tmpVariables[5]).getValue()).getByte(); + if (tmpVariables[6] < 12 || tmpVariables[6] > 16) { + KMException.throwIt(KMError.UNSUPPORTED_MAC_LENGTH); + } + tmpVariables[6] = 12; // simulator supports only 12 bits tag for GCM. + } else { // if not GCM mode + if (tmpVariables[5] != KMTag.INVALID_VALUE) { // no mac length should be specified + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + } + tmpVariables[4]++; + } + } + } + private static void generateAESKey(byte[] scratchPad) { + validateAESKey(scratchPad); + AESKey aesKey = cryptoProvider.createAESKey(tmpVariables[0]); + tmpVariables[0] = aesKey.getKey(scratchPad, (short) 0); + data[SECRET] = KMByteBlob.instance(scratchPad, (short) 0, tmpVariables[0]); + data[KEY_BLOB] = KMArray.instance((short) 4); + } + private static void validateECKeys(byte[] scratchPad){ + // Read key size + tmpVariables[0] = + KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); + if (tmpVariables[0] == KMTag.INVALID_VALUE) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + if (tmpVariables[0] != 256) { + KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); + } + // Read EC_CURVE + tmpVariables[1] = KMEnumTag.getValue(KMType.ECCURVE, data[KEY_PARAMETERS]); + if (tmpVariables[1] != KMType.INVALID_VALUE) { + if (tmpVariables[1] != KMType.P_256) { + KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); + } + } } - private void processUpgradeKeyCmd(APDU apdu) { + private static void generateECKeys(byte[] scratchPad) { + validateECKeys(scratchPad); + KeyPair ecKey = cryptoProvider.createECKeyPair(); + tmpVariables[5] = ((ECPublicKey) ecKey.getPublic()).getW(scratchPad, (short) 0); + data[PUB_KEY] = KMByteBlob.instance(scratchPad, (short) 0, tmpVariables[5]); + tmpVariables[5] = ((ECPrivateKey) ecKey.getPrivate()).getS(scratchPad, (short) 0); + data[SECRET] = KMByteBlob.instance(scratchPad, (short) 0, tmpVariables[5]); + data[KEY_BLOB] = KMArray.instance((short) 5); + KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_PUB_KEY, data[PUB_KEY]); + } + private static void validateTDESKey(byte[] scratchPad){ + // Read Minimum Mac length - it must not be present + tmpVariables[0] = + KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, data[KEY_PARAMETERS]); + if (tmpVariables[0] != KMType.INVALID_VALUE) { + KMException.throwIt(KMError.INVALID_TAG); + } + // Read keysize + tmpVariables[1] = + KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); + if (tmpVariables[1] == KMType.INVALID_VALUE) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + if (tmpVariables[1] != 168) { + KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); + } + } + private static void generateTDESKey(byte[] scratchPad) { + validateTDESKey(scratchPad); + DESKey desKey = cryptoProvider.createTDESKey(); + tmpVariables[0] = desKey.getKey(scratchPad, (short) 0); + data[SECRET] = KMByteBlob.instance(scratchPad, (short) 0, tmpVariables[0]); + data[KEY_BLOB] = KMArray.instance((short) 4); + } + + private static void validateHmacKey(byte[] scratchPad){ + // Read Minimum Mac length + tmpVariables[0] = + KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, data[KEY_PARAMETERS]); + if (tmpVariables[0] == KMType.INVALID_VALUE) { + KMException.throwIt(KMError.MISSING_MIN_MAC_LENGTH); + } + if (((short) (tmpVariables[0] % 8) != 0) || (tmpVariables[0] < (short) 64)) { + KMException.throwIt(KMError.UNSUPPORTED_MIN_MAC_LENGTH); + } + // Read keysize + tmpVariables[1] = + KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); + if (tmpVariables[1] == KMType.INVALID_VALUE) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + if ((tmpVariables[1] > 512) || ((short) (tmpVariables[1] % 8) != 0)) { + KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); + } + // Read digests + tmpVariables[2] = + KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.DIGEST, data[KEY_PARAMETERS]); + if (tmpVariables[2] == KMType.INVALID_VALUE) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + tmpVariables[3] = KMEnumArrayTag.cast(tmpVariables[2]).getValues(); + tmpVariables[4] = KMByteBlob.cast(tmpVariables[3]).length(); + tmpVariables[5] = 0; + // check whether digest sizes are greater then or equal to min mac length. + while (tmpVariables[5] < tmpVariables[4]) { + tmpVariables[6] = KMByteBlob.cast(tmpVariables[3]).get(tmpVariables[5]); + switch (tmpVariables[6]) { + case KMType.DIGEST_NONE: + KMException.throwIt(KMError.UNSUPPORTED_DIGEST); + break; + case KMType.MD5: + tmpVariables[7] = 128; + break; + case KMType.SHA1: + tmpVariables[7] = 160; + break; + case KMType.SHA2_224: + tmpVariables[7] = 224; + break; + case KMType.SHA2_256: + tmpVariables[7] = 256; + break; + case KMType.SHA2_384: + tmpVariables[7] = 384; + break; + case KMType.SHA2_512: + tmpVariables[7] = 512; + break; + default: + tmpVariables[7] = 0; + break; + } + if (tmpVariables[7] == 0) { + KMException.throwIt(KMError.UNSUPPORTED_DIGEST); + } + if (tmpVariables[0] > tmpVariables[7]) { + KMException.throwIt(KMError.UNSUPPORTED_MIN_MAC_LENGTH); + } + tmpVariables[5]++; + } + } + private static void generateHmacKey(byte[] scratchPad) { + validateHmacKey(scratchPad); + // Read Minimum Mac length + tmpVariables[0] = + KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, data[KEY_PARAMETERS]); + if (tmpVariables[0] == KMType.INVALID_VALUE) { + KMException.throwIt(KMError.MISSING_MIN_MAC_LENGTH); + } + if (((short) (tmpVariables[0] % 8) != 0) || (tmpVariables[0] < (short) 64)) { + KMException.throwIt(KMError.UNSUPPORTED_MIN_MAC_LENGTH); + } + // Read keysize + tmpVariables[1] = + KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); + if (tmpVariables[1] == KMType.INVALID_VALUE) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + if ((tmpVariables[1] > 512) || ((short) (tmpVariables[1] % 8) != 0)) { + KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); + } + // Read digests + tmpVariables[2] = + KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.DIGEST, data[KEY_PARAMETERS]); + if (tmpVariables[2] == KMType.INVALID_VALUE) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + tmpVariables[3] = KMEnumArrayTag.cast(tmpVariables[2]).getValues(); + tmpVariables[4] = KMByteBlob.cast(tmpVariables[3]).length(); + tmpVariables[5] = 0; + // check whether digest sizes are greater then or equal to min mac length. + while (tmpVariables[5] < tmpVariables[4]) { + tmpVariables[6] = KMByteBlob.cast(tmpVariables[3]).get(tmpVariables[5]); + switch (tmpVariables[6]) { + case KMType.DIGEST_NONE: + KMException.throwIt(KMError.UNSUPPORTED_DIGEST); + break; + case KMType.MD5: + tmpVariables[7] = 128; + break; + case KMType.SHA1: + tmpVariables[7] = 160; + break; + case KMType.SHA2_224: + tmpVariables[7] = 224; + break; + case KMType.SHA2_256: + tmpVariables[7] = 256; + break; + case KMType.SHA2_384: + tmpVariables[7] = 384; + break; + case KMType.SHA2_512: + tmpVariables[7] = 512; + break; + default: + tmpVariables[7] = 0; + break; + } + if (tmpVariables[7] == 0) { + KMException.throwIt(KMError.UNSUPPORTED_DIGEST); + } + if (tmpVariables[0] > tmpVariables[7]) { + KMException.throwIt(KMError.UNSUPPORTED_MIN_MAC_LENGTH); + } + tmpVariables[5]++; + } + // generate HMAC Key + HMACKey hmacKey = cryptoProvider.createHMACKey(tmpVariables[1]); + tmpVariables[0] = hmacKey.getKey(scratchPad, (short) 0); + data[SECRET] = KMByteBlob.instance(scratchPad, (short) 0, tmpVariables[0]); + data[KEY_BLOB] = KMArray.instance((short) 4); } - private void processAttestKeyCmd(APDU apdu) { + private void checkVersionAndPatchLevel(byte[] scratchPad) { + tmpVariables[0] = + KMIntegerTag.getValue( + scratchPad, (short) 0, KMType.UINT_TAG, KMType.OS_VERSION, data[HW_PARAMETERS]); + if ((tmpVariables[0] != KMType.INVALID_VALUE) + && (Util.arrayCompare( + repository.osVersion, (short) 0, scratchPad, (short) 0, tmpVariables[0]) + != 0)) { + if (Util.arrayCompare(repository.osVersion, (short) 0, scratchPad, (short) 0, tmpVariables[0]) + == -1) { + // If the key characteristics has os version > current os version + KMException.throwIt(KMError.INVALID_KEY_BLOB); + } else { + KMException.throwIt(KMError.KEY_REQUIRES_UPGRADE); + } + } + tmpVariables[0] = + KMIntegerTag.getValue( + scratchPad, (short) 0, KMType.UINT_TAG, KMType.OS_PATCH_LEVEL, data[HW_PARAMETERS]); + if ((tmpVariables[0] != KMType.INVALID_VALUE) + && (Util.arrayCompare(repository.osPatch, (short) 0, scratchPad, (short) 0, tmpVariables[0]) + != 0)) { + if (Util.arrayCompare(repository.osPatch, (short) 0, scratchPad, (short) 0, tmpVariables[0]) + == -1) { + // If the key characteristics has os patch level > current os patch + KMException.throwIt(KMError.INVALID_KEY_BLOB); + } else { + KMException.throwIt(KMError.KEY_REQUIRES_UPGRADE); + } + } + } + private static void makeKeyCharacteristics(byte[] scratchPad) { + tmpVariables[0] = + KMInteger.instance(repository.osPatch, (short) 0, (short) repository.osPatch.length); + tmpVariables[1] = + KMInteger.instance(repository.osVersion, (short) 0, (short) repository.osVersion.length); + data[HW_PARAMETERS] = + KMKeyParameters.makeHwEnforced( + data[KEY_PARAMETERS], (byte)data[ORIGIN], tmpVariables[1], tmpVariables[0], scratchPad); + data[SW_PARAMETERS] = KMKeyParameters.makeSwEnforced(data[KEY_PARAMETERS], scratchPad); + data[KEY_CHARACTERISTICS] = KMKeyCharacteristics.instance(); + KMKeyCharacteristics.cast(data[KEY_CHARACTERISTICS]).setHardwareEnforced(data[HW_PARAMETERS]); + KMKeyCharacteristics.cast(data[KEY_CHARACTERISTICS]).setSoftwareEnforced(data[SW_PARAMETERS]); } - private void processExportKeyCmd(APDU apdu) { + private static void createEncryptedKeyBlob(byte[] scratchPad) { + // make key characteristics - returns key characteristics in data[KEY_CHARACTERISTICS] + makeKeyCharacteristics(scratchPad); + // make root of trust blob + data[ROT] = + KMByteBlob.instance( + repository.verifiedBootKey, (short) 0, (short) repository.verifiedBootKey.length); + // make hidden key params list + data[HIDDEN_PARAMETERS] = + KMKeyParameters.makeHidden(data[KEY_PARAMETERS], data[ROT], scratchPad); + // make authorization data + makeAuthData(scratchPad); + // encrypt the secret and cryptographically attach that to authorization data + encryptSecret(scratchPad); + // create key blob array + KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_SECRET, data[SECRET]); + KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_AUTH_TAG, data[AUTH_TAG]); + KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_NONCE, data[NONCE]); + KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_KEYCHAR, data[KEY_CHARACTERISTICS]); + tmpVariables[0] = repository.alloc((short) 256); // TODO use buffer + tmpVariables[1] = encoder.encode(data[KEY_BLOB], repository.getHeap(), tmpVariables[0]); + data[KEY_BLOB] = KMByteBlob.instance(repository.getHeap(), tmpVariables[0], tmpVariables[1]); + } + private static void parseEncryptedKeyBlob(byte[] scratchPad) { + tmpVariables[0] = KMByteBlob.cast(data[KEY_BLOB]).getStartOff(); + tmpVariables[1] = KMArray.instance((short)5); + KMArray.cast(tmpVariables[1]).add(KMKeymasterApplet.KEY_BLOB_SECRET, KMByteBlob.exp()); + KMArray.cast(tmpVariables[1]).add(KMKeymasterApplet.KEY_BLOB_AUTH_TAG, KMByteBlob.exp()); + KMArray.cast(tmpVariables[1]).add(KMKeymasterApplet.KEY_BLOB_NONCE, KMByteBlob.exp()); + tmpVariables[2] = KMKeyCharacteristics.exp(); + KMArray.cast(tmpVariables[1]).add(KMKeymasterApplet.KEY_BLOB_KEYCHAR, tmpVariables[2]); + KMArray.cast(tmpVariables[1]).add(KMKeymasterApplet.KEY_BLOB_PUB_KEY, KMByteBlob.exp()); + data[KEY_BLOB]=decoder.decodeArray(tmpVariables[1], + KMByteBlob.cast(data[KEY_BLOB]).getBuffer(), + KMByteBlob.cast(data[KEY_BLOB]).getStartOff(), + KMByteBlob.cast(data[KEY_BLOB]).length()); + tmpVariables[0] = KMArray.cast(data[KEY_BLOB]).length(); + if (tmpVariables[0] < 4) { + KMException.throwIt(KMError.INVALID_KEY_BLOB); + } + // Validate Auth Tag + data[AUTH_TAG] = KMArray.cast(data[KEY_BLOB]).get(KEY_BLOB_AUTH_TAG); + if (!KMRepository.validateAuthTag(data[AUTH_TAG])) { + KMException.throwIt(KMError.INVALID_KEY_BLOB); + } + // initialize data + data[NONCE] = KMArray.cast(data[KEY_BLOB]).get(KEY_BLOB_NONCE); + data[SECRET] = KMArray.cast(data[KEY_BLOB]).get(KEY_BLOB_SECRET); + data[KEY_CHARACTERISTICS] = KMArray.cast(data[KEY_BLOB]).get(KEY_BLOB_KEYCHAR); + data[PUB_KEY] = KMType.INVALID_VALUE; + if (tmpVariables[0] == 5) { + data[PUB_KEY] = KMArray.cast(data[KEY_BLOB]).get(KEY_BLOB_PUB_KEY); + } + data[HW_PARAMETERS] = KMKeyCharacteristics.cast(data[KEY_CHARACTERISTICS]).getHardwareEnforced(); + data[SW_PARAMETERS] = KMKeyCharacteristics.cast(data[KEY_CHARACTERISTICS]).getSoftwareEnforced(); + // make root of trust blob + data[ROT] = + KMByteBlob.instance( + repository.verifiedBootKey, (short) 0, (short) repository.verifiedBootKey.length); + data[HIDDEN_PARAMETERS] = + KMKeyParameters.makeHidden(data[APP_ID], data[APP_DATA], data[ROT], scratchPad); + // Decrypt Secret and verify auth tag + decryptSecret(scratchPad); } - private void processImportWrappedKeyCmd(APDU apdu) { + private static void decryptSecret(byte[] scratchPad) { + // derive master key - stored in derivedKey + tmpVariables[0] = deriveKey(scratchPad); + AESKey derivedKey = + cryptoProvider.createAESKey(repository.getHeap(), data[DERIVED_KEY], tmpVariables[0]); + if (derivedKey == null) { + KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); + } + boolean verification = + cryptoProvider.aesGCMDecrypt( + derivedKey, + KMByteBlob.cast(data[SECRET]).getBuffer(), + KMByteBlob.cast(data[SECRET]).getStartOff(), + KMByteBlob.cast(data[SECRET]).length(), + scratchPad, + (short) 0, + KMByteBlob.cast(data[NONCE]).getBuffer(), + KMByteBlob.cast(data[NONCE]).getStartOff(), + KMByteBlob.cast(data[NONCE]).length(), + repository.getHeap(), + data[AUTH_DATA], + data[AUTH_DATA_LENGTH], + KMByteBlob.cast(data[AUTH_TAG]).getBuffer(), + KMByteBlob.cast(data[AUTH_TAG]).getStartOff(), + KMByteBlob.cast(data[AUTH_TAG]).length()); + if (verification != true) { + KMException.throwIt(KMError.INVALID_KEY_BLOB); + } + } + private static void encryptSecret(byte[] scratchPad) { + // make nonce + data[NONCE] = KMByteBlob.instance((short) AES_GCM_NONCE_LENGTH); + data[AUTH_TAG] = KMByteBlob.instance(AES_GCM_AUTH_TAG_LENGTH); + Util.arrayCopy( + KMByteBlob.cast(data[NONCE]).getBuffer(), + KMByteBlob.cast(data[NONCE]).getStartOff(), + scratchPad, + (short) 0, + KMByteBlob.cast(data[NONCE]).length()); + cryptoProvider.newRandomNumber( + KMByteBlob.cast(data[NONCE]).getBuffer(), + KMByteBlob.cast(data[NONCE]).getStartOff(), + KMByteBlob.cast(data[NONCE]).length()); + // derive master key - stored in derivedKey + tmpVariables[0] = deriveKey(scratchPad); + AESKey derivedKey = + cryptoProvider.createAESKey(repository.getHeap(), data[DERIVED_KEY], tmpVariables[0]); + if (derivedKey == null) { + KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); + } + tmpVariables[1] = + cryptoProvider.aesGCMEncrypt( + derivedKey, + KMByteBlob.cast(data[SECRET]).getBuffer(), + KMByteBlob.cast(data[SECRET]).getStartOff(), + KMByteBlob.cast(data[SECRET]).length(), + scratchPad, + (short) 0, + KMByteBlob.cast(data[NONCE]).getBuffer(), + KMByteBlob.cast(data[NONCE]).getStartOff(), + KMByteBlob.cast(data[NONCE]).length(), + repository.getHeap(), + data[AUTH_DATA], + data[AUTH_DATA_LENGTH], + KMByteBlob.cast(data[AUTH_TAG]).getBuffer(), + KMByteBlob.cast(data[AUTH_TAG]).getStartOff(), + KMByteBlob.cast(data[AUTH_TAG]).length()); + if (tmpVariables[1] > 0) { + if (tmpVariables[1] != KMByteBlob.cast(data[SECRET]).length()) { + KMException.throwIt(KMError.INVALID_KEY_BLOB); + } + KMByteBlob.cast(data[SECRET]).setValue(scratchPad, (short) 0, tmpVariables[1]); + } } - private void processImportKeyCmd(APDU apdu) { + 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]); + // convert scratch pad to KMArray + tmpVariables[1] = KMArray.instance(tmpVariables[0]); + short index = 0; + short objPtr = 0; + while (index < tmpVariables[0]) { + objPtr = Util.getShort(scratchPad, (short) (index * 2)); + KMArray.cast(tmpVariables[1]).add(index, objPtr); + index++; + } + 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; + } + private static short addPtrToAAD(short dataArrPtr, byte[] aadBuf, short offset) { + short index = (short) (offset * 2); + short tagInd = 0; + short tagPtr = 0; + 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 void processGenerateKey(APDU apdu) { + private static short deriveKey(byte[] scratchPad) { + tmpVariables[0] = KMKeyParameters.cast(data[HIDDEN_PARAMETERS]).getVals(); + tmpVariables[1] = repository.alloc((short) 256); + // generate derivation material from hidden parameters + tmpVariables[2] = encoder.encode(tmpVariables[0], repository.getHeap(), tmpVariables[1]); + // create derived key i.e. MAC + tmpVariables[3] = + cryptoProvider.aesCCMSign( + repository.getHeap(), + tmpVariables[1], + tmpVariables[2], + repository.getMasterKeySecret(), + scratchPad, + (short) 0); + if (tmpVariables[3] < 0) { + KMException.throwIt(KMError.UNKNOWN_ERROR); + } + // store the derived secret in data dictionary + data[DERIVED_KEY] = repository.alloc(tmpVariables[3]); + Util.arrayCopyNonAtomic( + scratchPad, (short) 0, repository.getHeap(), data[DERIVED_KEY], tmpVariables[3]); + return tmpVariables[3]; + } + private static void sendError(APDU apdu, short err) { + bufferLength = encoder.encodeError(err, buffer, bufferStartOffset, (short) 5); + sendOutgoing(apdu); } } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMOperationState.java b/Applet/Applet/src/com/android/javacard/keymaster/KMOperationState.java index d1671328..47cdabf8 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMOperationState.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMOperationState.java @@ -44,11 +44,4 @@ public KMInteger getOperationHandle() { public void setOperationHandle(KMInteger operationHandle) { this.operationHandle = operationHandle; } -/* - public void release(KMContext context) { - // TODO release handle - context.getRepository().releaseOperationState(this); - } - - */ } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMRepository.java b/Applet/Applet/src/com/android/javacard/keymaster/KMRepository.java index 3492d4b5..8e0f01c3 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMRepository.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMRepository.java @@ -20,43 +20,62 @@ import javacard.framework.ISOException; import javacard.framework.JCSystem; import javacard.framework.Util; -import javacard.security.AESKey; -import javacard.security.KeyBuilder; - -// TODO cleanup, move most of the buffers to transient memory with "clear on deselect". Only -// exception may be OperationState - TBD. The initialize and reset functions will be refactored -// to handle onInstall and onSelect. public class KMRepository { public static final short HEAP_SIZE = 0x1000; + public static final short MAX_BLOB_STORAGE = 32; + public static final short AES_GCM_AUTH_TAG_LENGTH = 12; + // Boot params constants + public static final byte BOOT_KEY_MAX_SIZE = 32; + public static final byte BOOT_HASH_MAX_SIZE = 32; + // Repository attributes private static KMRepository repository; - private AESKey masterKey; + private byte[] masterKey; private byte[] heap; private short heapIndex; + // boot parameters + public Object[] authTagRepo; + public short keyBlobCount; + public byte[] osVersion; + public byte[] osPatch; + public byte[] verifiedBootKey; + public short actualBootKeyLength; + public byte[] verifiedBootHash; + public short actualBootHashLength; + public boolean verifiedBootFlag; + public boolean selfSignedBootFlag; + public boolean deviceLockedFlag ; public static KMRepository instance() { - if (repository == null) { - repository = new KMRepository(); - } return repository; } - public KMRepository(){ + public KMRepository(byte[] masterKey) { heap = JCSystem.makeTransientByteArray(HEAP_SIZE, JCSystem.CLEAR_ON_RESET); - // Initialize masterkey - AES 256 bit key. - if (masterKey == null) { - masterKey = - (AESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_AES, KeyBuilder.LENGTH_AES_256, false); + this.masterKey = new byte[(short)masterKey.length]; + // Initialize masterkey + Util.arrayCopy(masterKey, (short)0, this.masterKey, (short)0, (short)masterKey.length); + authTagRepo = new Object[MAX_BLOB_STORAGE]; + short index = 0; + while (index < MAX_BLOB_STORAGE) { + authTagRepo[index] = new byte[AES_GCM_AUTH_TAG_LENGTH]; + index++; } + osVersion = new byte[4]; + osPatch = new byte[4]; + verifiedBootKey = new byte[BOOT_KEY_MAX_SIZE]; + verifiedBootHash = new byte[BOOT_HASH_MAX_SIZE]; + repository = this; } public void onUninstall() { - masterKey = null; + //TODO change this + Util.arrayFillNonAtomic(masterKey,(short)0,(short)masterKey.length,(byte) 0); } public void onProcess() {} - public void clean(){ + public void clean() { Util.arrayFillNonAtomic(heap, (short) 0, heapIndex, (byte) 0); heapIndex = 0; } @@ -65,7 +84,7 @@ public void onDeselect() {} public void onSelect() {} - public AESKey getMasterKey() { + public byte[] getMasterKeySecret() { return masterKey; } @@ -77,7 +96,93 @@ public short alloc(short length) { return (short) (heapIndex - length); } - public byte[] getHeap(){ + public byte[] getHeap() { return heap; } + + public static void persistAuthTag(short authTag) { + final byte[] compare = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; // length equal to AES_GCM_AUTH_TAG_LENGTH. + short index = 0; + byte ret = 0; + while (index < MAX_BLOB_STORAGE) { + ret = + Util.arrayCompare( + (byte[]) (repository.authTagRepo[index]), + (short) 0, + compare, + (short) 0, + AES_GCM_AUTH_TAG_LENGTH); + if (ret == 0) { + break; + } + index++; + } + // This should never happen + if (index >= repository.MAX_BLOB_STORAGE){ + ISOException.throwIt(ISO7816.SW_BYTES_REMAINING_00); + } + JCSystem.beginTransaction(); + Util.arrayCopy( + KMByteBlob.cast(authTag).getBuffer(), + KMByteBlob.cast(authTag).getStartOff(), + (byte[]) (repository.authTagRepo[index]), + (short) 0, + AES_GCM_AUTH_TAG_LENGTH); + repository.keyBlobCount++; + JCSystem.commitTransaction(); + } + + public static void removeAuthTag(short authTag) { + final byte[] zeroTag = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; // length equal to AES_GCM_AUTH_TAG_LENGTH. + short index = 0; + byte ret = 0; + while (index < repository.MAX_BLOB_STORAGE) { + ret = + Util.arrayCompare( + (byte[]) (repository.authTagRepo[index]), + (short) 0, + KMByteBlob.cast(authTag).getBuffer(), + KMByteBlob.cast(authTag).getStartOff(), + AES_GCM_AUTH_TAG_LENGTH); + if (ret == 0) { + break; + } + index++; + } + if (index >= MAX_BLOB_STORAGE) { + ISOException.throwIt(ISO7816.SW_COMMAND_NOT_ALLOWED); + } + JCSystem.beginTransaction(); + Util.arrayCopy( + zeroTag, (short) 0, (byte[]) (repository.authTagRepo[index]), (short) 0, AES_GCM_AUTH_TAG_LENGTH); + repository.keyBlobCount--; + JCSystem.commitTransaction(); + } + + public static boolean validateAuthTag(short authTag) { + short index = 0; + byte ret = 0; + while (index < MAX_BLOB_STORAGE) { + ret = + Util.arrayCompare( + (byte[]) (repository.authTagRepo[index]), + (short) 0, + KMByteBlob.cast(authTag).getBuffer(), + KMByteBlob.cast(authTag).getStartOff(), + AES_GCM_AUTH_TAG_LENGTH); + if (ret == 0) { + break; + } + index++; + } + if (index >= MAX_BLOB_STORAGE) { + return false; + } + return true; + } + } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMRng.java b/Applet/Applet/src/com/android/javacard/keymaster/KMRng.java new file mode 100644 index 00000000..a2e79901 --- /dev/null +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMRng.java @@ -0,0 +1,104 @@ +package com.android.javacard.keymaster; + +import javacard.framework.ISO7816; +import javacard.framework.ISOException; +import javacard.framework.JCSystem; +import javacard.framework.Util; +import javacard.security.AESKey; +import javacard.security.CryptoException; +import javacard.security.KeyBuilder; +import javacard.security.RandomData; +import javacardx.crypto.Cipher; + +public class KMRng { + 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 byte[] counter; + private static AESKey aesKey; + private static Cipher aesCbc; + private static byte[] entropyPool; + public static void init() { + entropyPool = JCSystem.makeTransientByteArray(ENTROPY_POOL_SIZE, JCSystem.CLEAR_ON_RESET); + counter = JCSystem.makeTransientByteArray((short)8, JCSystem.CLEAR_ON_RESET); + KMRng.initEntropyPool(entropyPool); + try { + aesCbc = Cipher.getInstance(Cipher.ALG_AES_BLOCK_128_CBC_NOPAD, false); + } catch (CryptoException exp) { + ISOException.throwIt(ISO7816.SW_COMMAND_NOT_ALLOWED); + } + aesKey = (AESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_AES, KeyBuilder.LENGTH_AES_128, false); + } + + public static void initEntropyPool(byte[] pool) { + byte index = 0; + RandomData trng; + while (index < counter.length) { + counter[index++] = 0; + } + try { + trng = RandomData.getInstance(RandomData.ALG_TRNG); + trng.nextBytes(pool, (short) 0, (short) pool.length); + } catch (CryptoException exp) { + if (exp.getReason() == CryptoException.NO_SUCH_ALGORITHM) { + //TODO change this when possible + // simulator does not support TRNG algorithm. So, PRNG algorithm (deprecated) is used. + trng = RandomData.getInstance(RandomData.ALG_PSEUDO_RANDOM); + trng.nextBytes(pool, (short) 0, (short) pool.length); + } else { + // TODO change this to proper error code + ISOException.throwIt(ISO7816.SW_UNKNOWN); + } + } + + } + + // Generate a secure random number from existing entropy pool. This uses aes ecb algorithm with + // 8 byte counter and 16 byte block size. + public static void newRandomNumber(byte[] num, short startOff, short length) { + KMRepository repository = KMRepository.instance(); + byte[] bufPtr = repository.getHeap(); + short countBufInd = repository.alloc(KMKeymasterApplet.AES_BLOCK_SIZE); + short randBufInd = repository.alloc(KMKeymasterApplet.AES_BLOCK_SIZE); + short len = KMKeymasterApplet.AES_BLOCK_SIZE; + aesKey.setKey(entropyPool, (short) 0); + aesCbc.init(aesKey, Cipher.MODE_ENCRYPT, aesICV, (short)0, (short)16); + while (length > 0) { + if (length < len ) len = length; + // increment counter by one + incrementCounter(); + // copy the 8 byte counter into the 16 byte counter buffer. + Util.arrayCopy(counter, (short) 0, bufPtr, countBufInd, (short) counter.length); + // encrypt the counter buffer with existing entropy which forms the aes key. + aesCbc.doFinal(bufPtr, countBufInd, KMKeymasterApplet.AES_BLOCK_SIZE, bufPtr, randBufInd); + // copy the encrypted counter block to buffer passed in the argument + Util.arrayCopy(bufPtr, randBufInd, num, startOff, len); + length = (short) (length - len); + startOff = (short)(startOff + len); + } + } + + // increment 8 byte counter by one + private static void incrementCounter() { + // start with least significant byte + short index = (short) (counter.length - 1); + while (index >= 0) { + // if the msb of current byte is set then it will be negative + if (counter[index] < 0) { + // then increment the counter + counter[index]++; + // is the msb still set? i.e. no carry over + if (counter[index] < 0) break; // then break + else index--; // else go to the higher order byte + } else { + // if msb is not set then increment the counter + counter[index]++; + break; + } + } + } + + public static byte[] getEntropyPool() { + return entropyPool; + } + +} diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMSimulator.java b/Applet/Applet/src/com/android/javacard/keymaster/KMSimulator.java new file mode 100644 index 00000000..0d0c1394 --- /dev/null +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMSimulator.java @@ -0,0 +1,424 @@ +/* + * Copyright(C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.javacard.keymaster; + +import javacard.framework.ISO7816; +import javacard.framework.ISOException; +import javacard.framework.JCSystem; +import javacard.framework.Util; +import javacard.security.AESKey; +import javacard.security.CryptoException; +import javacard.security.DESKey; +import javacard.security.ECPrivateKey; +import javacard.security.ECPublicKey; +import javacard.security.HMACKey; +import javacard.security.KeyBuilder; +import javacard.security.KeyPair; +import javacard.security.RSAPrivateKey; +import javacard.security.RandomData; +import javacard.security.Signature; +import javacardx.crypto.AEADCipher; +import javacardx.crypto.Cipher; + +/** + * Simulator only supports 512 bit RSA key pair, 128 AES Key, 128 bit 3Des key, less then 256 bit EC + * Key, and upto 512 bit HMAC key. Also simulator does not support TRNG, so this implementation just + * creates its own RNG using PRNG. + */ +public class KMSimulator implements KMCryptoProvider { + public static final short AES_GCM_TAG_LENGTH = 12; + public static final short AES_GCM_NONCE_LENGTH = 12; + public static final short MAX_RND_NUM_SIZE = 64; + public static final short ENTROPY_POOL_SIZE = 16; // simulator does not support 256 bit aes keys + public static final byte[] aesICV = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + public static boolean jcardSim = false; + + private static KeyPair rsa512KeyPair; + private static KeyPair ec192KeyPair; + private static AESKey aes128Key; + private static DESKey triDesKey; + private static HMACKey hmac128Key; + private static HMACKey hmac256Key; + private static AEADCipher aesGcmCipher; + private static AESKey derivedKey; + private static Signature kdf; + + private static byte[] rngCounter; + private static AESKey aesRngKey; + private static Cipher aesRngCipher; + private static byte[] entropyPool; + private static byte[] rndNum; + + // Implements Oracle Simulator based restricted crypto provider + public KMSimulator() { + // Various Keys + rsa512KeyPair = new KeyPair(KeyPair.ALG_RSA, KeyBuilder.LENGTH_RSA_512); + ec192KeyPair = new KeyPair(KeyPair.ALG_EC_FP, KeyBuilder.LENGTH_EC_FP_192); + aes128Key = (AESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_AES, KeyBuilder.LENGTH_AES_128, false); + triDesKey = + (DESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_DES, KeyBuilder.LENGTH_DES3_2KEY, false); + hmac128Key = (HMACKey) KeyBuilder.buildKey(KeyBuilder.TYPE_HMAC, (short) 128, false); + hmac256Key = (HMACKey) KeyBuilder.buildKey(KeyBuilder.TYPE_HMAC, (short) 256, false); + derivedKey = + (AESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_AES, KeyBuilder.LENGTH_AES_128, false); + kdf = Signature.getInstance(Signature.ALG_AES_CMAC_128, false); + + // RNG + rndNum = JCSystem.makeTransientByteArray(MAX_RND_NUM_SIZE, JCSystem.CLEAR_ON_RESET); + entropyPool = JCSystem.makeTransientByteArray(ENTROPY_POOL_SIZE, JCSystem.CLEAR_ON_RESET); + rngCounter = JCSystem.makeTransientByteArray((short) 8, JCSystem.CLEAR_ON_RESET); + initEntropyPool(entropyPool); + try { + aesRngCipher = Cipher.getInstance(Cipher.ALG_AES_BLOCK_128_CBC_NOPAD, false); + } catch (CryptoException exp) { + ISOException.throwIt(ISO7816.SW_COMMAND_NOT_ALLOWED); + } + aesRngKey = (AESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_AES, KeyBuilder.LENGTH_AES_128, false); + } + + @Override + public KeyPair createRsaKeyPair() { + // By default 65537 is used as public exponent no need to set the public exponent. Now generate + // 512 bit RSA keypair for the given exponent + rsa512KeyPair.genKeyPair(); + return rsa512KeyPair; + } + + @Override + public KeyPair createECKeyPair() { + // Simulator does not support 256 bit keys. + // Generate default 192 bit key pair supported by simulator. + ec192KeyPair.genKeyPair(); + return ec192KeyPair; + } + + @Override + public AESKey createAESKey(short keysize) { + // keysize is ignored as simulator only supports 128 bit aes key + newRandomNumber(rndNum, (short) 0, (short) 16); + aes128Key.setKey(rndNum, (short) 0); + return aes128Key; + } + + @Override + public AESKey createAESKey(byte[] buf, short startOff, short length) { + if (length > 16) length = 16; + else if(length < 16) return null; + aes128Key.setKey(buf, startOff); + return aes128Key; + } + + @Override + public DESKey createTDESKey() { + // only 128 bit keys are supported + newRandomNumber(rndNum, (short) 0, (short) 21); + triDesKey.setKey(rndNum, (short) 0); + return triDesKey; + } + + @Override + public HMACKey createHMACKey(short keysize) { + // simulator only supports HMAC keys for SHA1 and SHA256 with block size 64. + // So only 128 and 256 bit HMAC keys are supported. + HMACKey key; + if (keysize == 128) { + key = hmac128Key; + keysize = 16; + } else if (keysize == 256) { + key = hmac256Key; + keysize = 32; + } else { + key = hmac128Key; // by default the simulator will return 128 bit keys for SHA1 + keysize = 16; + } + newRandomNumber(rndNum, (short) 0, keysize); + key.setKey(rndNum, (short) 0, keysize); + return key; + } + + @Override + public void addRngEntropy(byte[] num, short offset, short length) { + // Maximum length can be 256 bytes. But currently we support max 32 bytes seed. + // Get existing entropy pool. + if (length > 32) length = 32; + // Create new temporary pool. + // Populate the new pool with the entropy which is derived from current entropy pool. + newRandomNumber(rndNum, (short) 0, (short) entropyPool.length); + // Copy the entropy to the current pool - updates the entropy pool. + Util.arrayCopy(rndNum, (short) 0, entropyPool, (short) 0, (short) entropyPool.length); + short index = 0; + short randIndex = 0; + // XOR the seed received from the master in the entropy pool - 16 bytes (entPool.length). + // at a time. + while (index < length) { + entropyPool[randIndex] = (byte) (entropyPool[randIndex] ^ num[(short) (offset + index)]); + randIndex++; + index++; + if (randIndex >= entropyPool.length) { + randIndex = 0; + } + } + } + + @Override + public short aesGCMEncrypt( + AESKey key, + byte[] secret, + short secretStart, + short secretLen, + byte[] encSecret, + short encSecretStart, + byte[] nonce, + short nonceStart, + short nonceLen, + byte[] authData, + short authDataStart, + short authDataLen, + byte[] authTag, + short authTagStart, + short authTagLen) { + if (jcardSim) return -1; + if(authTagLen != AES_GCM_TAG_LENGTH){ + KMException.throwIt(KMError.UNKNOWN_ERROR); + } + if(nonceLen != AES_GCM_NONCE_LENGTH){ + KMException.throwIt(KMError.UNKNOWN_ERROR); + } + if (aesGcmCipher == null) { + aesGcmCipher = (AEADCipher) Cipher.getInstance(AEADCipher.ALG_AES_GCM, false); + } + byte[] aad = JCSystem.makeTransientByteArray(authDataLen, JCSystem.CLEAR_ON_RESET); + Util.arrayCopyNonAtomic(authData, authDataStart, aad, (short) 0, authDataLen); + try { + aesGcmCipher.init(key, Cipher.MODE_ENCRYPT, nonce, nonceStart, nonceLen); + } catch (CryptoException exp) { + KMException.throwIt(exp.getReason()); + } + // add the auth data + try { + aesGcmCipher.updateAAD(aad, (short) 0, authDataLen); + } catch (CryptoException exp) { + KMException.throwIt(exp.getReason()); + } + // encrypt the secret + short ciphLen = aesGcmCipher.doFinal(secret, secretStart, secretLen, encSecret, encSecretStart); + // The tag buffer must be exact size otherwise simulator returns 0 tag. + byte[] tag = JCSystem.makeTransientByteArray(AES_GCM_TAG_LENGTH, JCSystem.CLEAR_ON_RESET); + aesGcmCipher.retrieveTag(tag, (short) 0, AES_GCM_TAG_LENGTH); + Util.arrayCopyNonAtomic(tag,(short)0, authTag, authTagStart,AES_GCM_TAG_LENGTH); + return ciphLen; +/* aesGcmCipher.init(key, Cipher.MODE_DECRYPT, nonce, nonceStart, nonceLen); + try { + aesGcmCipher.updateAAD(aad, (short) 0, authDataLen); + } catch (CryptoException exp) { + KMException.throwIt(exp.getReason()); + } + byte[] plain = JCSystem.makeTransientByteArray(secretLen, JCSystem.CLEAR_ON_RESET); + // encrypt the secret + ciphLen = aesGcmCipher.doFinal(encSecret, encSecretStart, ciphLen, plain, (short) 0); + boolean ver = aesGcmCipher.verifyTag(tag, (short) 0, (short) 12, (short) 12); + if (ver == true) { + KMException.throwIt((short) 10); + } else { + KMException.throwIt((short) 20); + } + return 0; + */ + } + + public boolean aesGCMDecrypt( + AESKey key, + byte[] encSecret, + short encSecretStart, + short encSecretLen, + byte[] secret, + short secretStart, + byte[] nonce, + short nonceStart, + short nonceLen, + byte[] authData, + short authDataStart, + short authDataLen, + byte[] authTag, + short authTagStart, + short authTagLen) { + if(jcardSim) return true; + if (aesGcmCipher == null) { + aesGcmCipher = (AEADCipher) Cipher.getInstance(AEADCipher.ALG_AES_GCM, false); + } + // allocate aad buffer of exact size - otherwise simulator throws exception + byte[] aad = JCSystem.makeTransientByteArray(authDataLen, JCSystem.CLEAR_ON_RESET); + Util.arrayCopyNonAtomic(authData, authDataStart, aad, (short) 0, authDataLen); + // allocate tag of exact size. + byte[] tag = JCSystem.makeTransientByteArray(AES_GCM_TAG_LENGTH, JCSystem.CLEAR_ON_RESET); + Util.arrayCopyNonAtomic(authTag, authTagStart, tag, (short) 0, authTagLen); + boolean verification = false; + try { + aesGcmCipher.init(key, Cipher.MODE_DECRYPT, nonce, nonceStart, nonceLen); + aesGcmCipher.updateAAD(aad, (short) 0, authDataLen); + //byte[] plain = JCSystem.makeTransientByteArray(encSecretLen, JCSystem.CLEAR_ON_RESET); + // encrypt the secret + aesGcmCipher.doFinal(encSecret, encSecretStart, encSecretLen, secret, secretStart); + verification = aesGcmCipher.verifyTag(tag, (short) 0, (short) 12, (short) 12); + } catch (CryptoException exp) { + KMException.throwIt(KMError.UNKNOWN_ERROR); + } + return verification; + } + + @Override + public byte[] getTrueRandomNumber(short i) { + // ignore the size as simulator only supports 128 bit entropy + return entropyPool; + } + + @Override + public short aesCCMSign( + byte[] bufIn, + short bufInStart, + short buffInLength, + byte[] masterKeySecret, + byte[] bufOut, + short bufStart) { + if (masterKeySecret.length > 16) { + return -1; + } + aes128Key.setKey(masterKeySecret, (short) 0); + kdf.init(aes128Key, Signature.MODE_SIGN); + return kdf.sign(bufIn, bufInStart, buffInLength, bufOut, bufStart); + } + + @Override + public ECPrivateKey createEcPrivateKey(byte[] pubBuffer, short pubOff, short pubLength, + byte[] privBuffer, short privOff, short privLength) { + // Simulator does not support NamedParameterSpec or 256 bit keys + ECPrivateKey privKey = (ECPrivateKey) ec192KeyPair.getPrivate(); + if(privLength > 24){ + privLength = 24;// simulator does not support more then 24 bytes - 192 bit key. + }else if(privLength <= 20){ + return null; + } + privKey.setS(privBuffer,privOff, privLength); + return privKey; + } + + @Override + public HMACKey createHMACKey(byte[] secretBuffer, short secretOff, short secretLength) { + // simulator only supports HMAC keys for SHA1 and SHA256 with block size 64. + // So only 128 and 256 bit HMAC keys are supported. + HMACKey key; + if (secretLength == 16) { + key = hmac128Key; + key.setKey(secretBuffer, secretOff, secretLength); + } else if (secretLength == 32) { + key = hmac256Key; + key.setKey(secretBuffer, secretOff, secretLength); + } else { + key = hmac128Key; // by default the simulator will return 128 bit keys for SHA1 + key.setKey(secretBuffer, secretOff, (short)16); + } + return key; + } + @Override + public DESKey createTDESKey(byte[] secretBuffer, short secretOff, short secretLength) { + // only 128 bit keys are supported + if(secretLength < 128) return null; + triDesKey.setKey(secretBuffer, secretOff); + return triDesKey; + } + + @Override + public RSAPrivateKey createRsaPrivateKey(byte[] modBuffer, short modOff, short modLength, byte[] privBuffer, short privOff, short privLength) { + RSAPrivateKey privKey = (RSAPrivateKey) rsa512KeyPair.getPrivate(); + if(privLength > 64) privLength = 64; + else if(privLength < 64)return null; + if(modLength > 64) modLength = 64; + else if( modLength < 64) return null; + privKey.setExponent(privBuffer, privOff, privLength); + privKey.setModulus(modBuffer, modOff, modLength); + return privKey; + } + + private void initEntropyPool(byte[] pool) { + byte index = 0; + RandomData trng; + while (index < rngCounter.length) { + rngCounter[index++] = 0; + } + try { + trng = RandomData.getInstance(RandomData.ALG_TRNG); + trng.nextBytes(pool, (short) 0, (short) pool.length); + } catch (CryptoException exp) { + if (exp.getReason() == CryptoException.NO_SUCH_ALGORITHM) { + // TODO change this when possible + // simulator does not support TRNG algorithm. So, PRNG algorithm (deprecated) is used. + trng = RandomData.getInstance(RandomData.ALG_PSEUDO_RANDOM); + trng.nextBytes(pool, (short) 0, (short) pool.length); + } else { + // TODO change this to proper error code + ISOException.throwIt(ISO7816.SW_UNKNOWN); + } + } + } + + // Generate a secure random number from existing entropy pool. This uses aes ecb algorithm with + // 8 byte rngCounter and 16 byte block size. + @Override + public void newRandomNumber(byte[] num, short startOff, short length) { + KMRepository repository = KMRepository.instance(); + byte[] bufPtr = repository.getHeap(); + short countBufInd = repository.alloc(KMKeymasterApplet.AES_BLOCK_SIZE); + short randBufInd = repository.alloc(KMKeymasterApplet.AES_BLOCK_SIZE); + short len = KMKeymasterApplet.AES_BLOCK_SIZE; + aesRngKey.setKey(entropyPool, (short) 0); + aesRngCipher.init(aesRngKey, Cipher.MODE_ENCRYPT, aesICV, (short) 0, (short) 16); + while (length > 0) { + if (length < len) len = length; + // increment rngCounter by one + incrementCounter(); + // copy the 8 byte rngCounter into the 16 byte rngCounter buffer. + Util.arrayCopy(rngCounter, (short) 0, bufPtr, countBufInd, (short) rngCounter.length); + // encrypt the rngCounter buffer with existing entropy which forms the aes key. + aesRngCipher.doFinal( + bufPtr, countBufInd, KMKeymasterApplet.AES_BLOCK_SIZE, bufPtr, randBufInd); + // copy the encrypted rngCounter block to buffer passed in the argument + Util.arrayCopy(bufPtr, randBufInd, num, startOff, len); + length = (short) (length - len); + startOff = (short) (startOff + len); + } + } + + // increment 8 byte rngCounter by one + private void incrementCounter() { + // start with least significant byte + short index = (short) (rngCounter.length - 1); + while (index >= 0) { + // if the msb of current byte is set then it will be negative + if (rngCounter[index] < 0) { + // then increment the rngCounter + rngCounter[index]++; + // is the msb still set? i.e. no carry over + if (rngCounter[index] < 0) break; // then break + else index--; // else go to the higher order byte + } else { + // if msb is not set then increment the rngCounter + rngCounter[index]++; + break; + } + } + } +} diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMType.java b/Applet/Applet/src/com/android/javacard/keymaster/KMType.java index 2dc74dfd..1e023647 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMType.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMType.java @@ -76,6 +76,7 @@ public abstract class KMType { public static final byte USER_AUTH_NONE = 0x00; public static final byte PASSWORD = 0x01; public static final byte FINGERPRINT = 0x02; + // have to be power of 2 public static final byte ANY = (byte) 0xFF; // Origin Enum Tag key and values. @@ -108,6 +109,22 @@ public abstract class KMType { public static final byte PKCS8 = 0x01; public static final byte RAW = 0x03; + // Verified Boot State + public static final short VERIFIED_BOOT_STATE = (short) 0xF003; + public static final byte SELF_SIGNED_BOOT = 0x01; + public static final byte VERIFIED_BOOT = 0x02; + + // Verified Boot Key + public static final short VERIFIED_BOOT_KEY = (short) 0xF004; + + // Verified Boot Hash + public static final short VERIFIED_BOOT_HASH = (short) 0xF005; + + // Device Locked + public static final short DEVICE_LOCKED = (short) 0xF006; + public static final byte DEVICE_LOCKED_TRUE = 0x01; + public static final byte DEVICE_LOCKED_FALSE = 0x00; + // Enum Array Tag // Purpose public static final short PURPOSE = 0x0002; @@ -123,6 +140,7 @@ public abstract class KMType { public static final byte ECB = 0x01; public static final byte CBC = 0x02; public static final byte CTR = 0x04; + public static final byte GCM = 0x20; // Digest public static final short DIGEST = 0x0005; diff --git a/Applet/Applet/test/com/android/javacard/test/KMFrameworkTest.java b/Applet/Applet/test/com/android/javacard/test/KMFrameworkTest.java index c34ac845..5f43ebfa 100644 --- a/Applet/Applet/test/com/android/javacard/test/KMFrameworkTest.java +++ b/Applet/Applet/test/com/android/javacard/test/KMFrameworkTest.java @@ -18,17 +18,34 @@ import com.android.javacard.keymaster.KMArray; import com.android.javacard.keymaster.KMByteBlob; +import com.android.javacard.keymaster.KMByteTag; import com.android.javacard.keymaster.KMDecoder; import com.android.javacard.keymaster.KMEncoder; import com.android.javacard.keymaster.KMEnum; +import com.android.javacard.keymaster.KMEnumArrayTag; import com.android.javacard.keymaster.KMEnumTag; +import com.android.javacard.keymaster.KMError; +import com.android.javacard.keymaster.KMInteger; +import com.android.javacard.keymaster.KMIntegerArrayTag; +import com.android.javacard.keymaster.KMIntegerTag; +import com.android.javacard.keymaster.KMKeyCharacteristics; import com.android.javacard.keymaster.KMKeyParameters; import com.android.javacard.keymaster.KMKeymasterApplet; +import com.android.javacard.keymaster.KMSimulator; import com.android.javacard.keymaster.KMType; import com.licel.jcardsim.smartcardio.CardSimulator; import com.licel.jcardsim.utils.AIDUtil; import javacard.framework.AID; import javacard.framework.Util; +import javacard.security.AESKey; +import javacard.security.DESKey; +import javacard.security.ECPrivateKey; +import javacard.security.ECPublicKey; +import javacard.security.HMACKey; +import javacard.security.Key; +import javacard.security.KeyBuilder; +import javacard.security.KeyPair; +import javacard.security.RSAPrivateKey; import javacard.security.RandomData; import javax.smartcardio.CommandAPDU; import javax.smartcardio.ResponseAPDU; @@ -37,10 +54,16 @@ import org.junit.experimental.theories.suppliers.TestedOn; public class KMFrameworkTest { + private short status; + private short keyCharacteristics; + private short keyBlob; + private KMSimulator sim; @Test public void test_Lifecycle_Success() { // Create simulator + KMSimulator.jcardSim = true; + sim = new KMSimulator(); CardSimulator simulator = new CardSimulator(); // Install applet @@ -50,24 +73,320 @@ public void test_Lifecycle_Success() { // Select applet simulator.selectApplet(appletAID1); testProvisionCmd(simulator); + testSetBootParams(simulator); testGetHwInfoCmd(simulator); testAddRngEntropyCmd(simulator); - + testGenerateRsaKey(simulator); + testImportRsaKey(simulator); + testGetKeyCharacteristics(simulator); + testGenerateAesKey(simulator); + testImportAesKey(simulator); + testGetKeyCharacteristics(simulator); + testGenerateEcKey(simulator); + testImportEcKey(simulator); + testGetKeyCharacteristics(simulator); + testGenerate3DesKey(simulator); + testImportDesKey(simulator); + testGetKeyCharacteristics(simulator); + testGenerateHmacKey(simulator); + testImportHmacKey(simulator); + testGetKeyCharacteristics(simulator); // Delete i.e. uninstall applet simulator.deleteApplet(appletAID1); } + private void testGetKeyCharacteristics(CardSimulator simulator) { + byte[] buf = new byte[512]; + buf[0] = (byte)0x80; + buf[1] = (byte)0x1D; + buf[2] = (byte)0x40; + buf[3] = (byte)0x00; + buf[4] = 0; + short keyChar = keyCharacteristics; + short len = KMKeyCharacteristics.cast(keyChar).length(); + // test provision command + short cmd = makeGetKeyCharKeyCmd(); + KMEncoder enc = new KMEncoder(); + short actualLen = enc.encode(cmd, buf, (short) 7); + Util.setShort(buf, (short)5, actualLen); + byte[] apdu = new byte[7+actualLen]; + Util.arrayCopyNonAtomic(buf,(short)0,apdu,(short)0,(short)(7+actualLen)); + //CommandAPDU commandAPDU = new CommandAPDU(0x80, 0x10, 0x40, 0x00, buf, 0, actualLen); + CommandAPDU commandAPDU = new CommandAPDU(apdu); + //print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(commandAPDU); + extractKeyChar(response); + short len2 = KMKeyCharacteristics.cast(keyCharacteristics).length(); + short hwList = KMKeyCharacteristics.cast(keyCharacteristics).getHardwareEnforced(); + short len3 = KMKeyParameters.cast(hwList).length(); + short swList = KMKeyCharacteristics.cast(keyCharacteristics).getSoftwareEnforced(); + short len4 = KMKeyParameters.cast(swList).length(); + Assert.assertEquals(0x9000, response.getSW()); + } + public void testProvisionCmd(CardSimulator simulator){ byte[] buf = new byte[512]; // test provision command short cmd = makeProvisionCmd(); KMEncoder enc = new KMEncoder(); - short actualLen = enc.encode(cmd, buf, (short) 0, (short)buf.length); + short actualLen = enc.encode(cmd, buf, (short) 0); CommandAPDU commandAPDU = new CommandAPDU(0x80, 0x23, 0x40, 0x00, buf, 0, actualLen); //print(commandAPDU.getBytes());; ResponseAPDU response = simulator.transmitCommand(commandAPDU); Assert.assertEquals(0x9000, response.getSW()); } + public void testSetBootParams(CardSimulator simulator){ + byte[] buf = new byte[512]; + // test provision command + short cmd = makeSetBootParamsCmd(); + KMEncoder enc = new KMEncoder(); + short actualLen = enc.encode(cmd, buf, (short) 0); + CommandAPDU commandAPDU = new CommandAPDU(0x80, 0x24, 0x40, 0x00, buf, 0, actualLen); + //print(commandAPDU.getBytes());; + ResponseAPDU response = simulator.transmitCommand(commandAPDU); + Assert.assertEquals(0x9000, response.getSW()); + } + + public void testGenerateRsaKey(CardSimulator simulator){ + byte[] buf = new byte[512]; + buf[0] = (byte)0x80; + buf[1] = (byte)0x10; + buf[2] = (byte)0x40; + buf[3] = (byte)0x00; + buf[4] = 0; + // test provision command + short cmd = makeGenerateKeyCmd(KMType.RSA, (short)2048); + KMEncoder enc = new KMEncoder(); + short actualLen = enc.encode(cmd, buf, (short) 7); + Util.setShort(buf, (short)5, actualLen); + byte[] apdu = new byte[7+actualLen]; + Util.arrayCopyNonAtomic(buf,(short)0,apdu,(short)0,(short)(7+actualLen)); + //CommandAPDU commandAPDU = new CommandAPDU(0x80, 0x10, 0x40, 0x00, buf, 0, actualLen); + CommandAPDU commandAPDU = new CommandAPDU(apdu); + //print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(commandAPDU); + extractKeyCharAndBlob(response); + Assert.assertEquals(0x9000, response.getSW()); + } + + public void testImportRsaKey(CardSimulator simulator){ + byte[] buf = new byte[512]; + buf[0] = (byte)0x80; + buf[1] = (byte)0x11; + buf[2] = (byte)0x40; + buf[3] = (byte)0x00; + buf[4] = 0; + short cmd = makeImportKeyRsaCmd(); + KMEncoder enc = new KMEncoder(); + short actualLen = enc.encode(cmd, buf, (short) 7); + Util.setShort(buf, (short)5, actualLen); + byte[] apdu = new byte[7+actualLen]; + Util.arrayCopyNonAtomic(buf,(short)0,apdu,(short)0,(short)(7+actualLen)); + //CommandAPDU commandAPDU = new CommandAPDU(0x80, 0x10, 0x40, 0x00, buf, 0, actualLen); + CommandAPDU commandAPDU = new CommandAPDU(apdu); + // print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(commandAPDU); + extractKeyCharAndBlob(response); + Assert.assertEquals(0x9000, response.getSW()); + } + + public void testImportEcKey(CardSimulator simulator){ + byte[] buf = new byte[512]; + buf[0] = (byte)0x80; + buf[1] = (byte)0x11; + buf[2] = (byte)0x40; + buf[3] = (byte)0x00; + buf[4] = 0; + short cmd = makeImportKeyEcCmd(); + KMEncoder enc = new KMEncoder(); + short actualLen = enc.encode(cmd, buf, (short) 7); + Util.setShort(buf, (short)5, actualLen); + byte[] apdu = new byte[7+actualLen]; + Util.arrayCopyNonAtomic(buf,(short)0,apdu,(short)0,(short)(7+actualLen)); + //CommandAPDU commandAPDU = new CommandAPDU(0x80, 0x10, 0x40, 0x00, buf, 0, actualLen); + CommandAPDU commandAPDU = new CommandAPDU(apdu); + // print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(commandAPDU); + extractKeyCharAndBlob(response); + Assert.assertEquals(0x9000, response.getSW()); + } + + private void extractKeyCharAndBlob(ResponseAPDU response) { + short ret = KMArray.instance((short) 3); + KMArray.cast(ret).add((short) 0, KMInteger.exp()); + KMArray.cast(ret).add((short) 1, KMByteBlob.exp()); + short inst = KMKeyCharacteristics.exp(); + KMArray.cast(ret).add((short) 2, inst); + KMDecoder dec = new KMDecoder(); + byte[] respBuf = response.getBytes(); + short len = (short) respBuf.length; + ret = dec.decode(ret, respBuf, (short) 0, len); + status = KMArray.cast(ret).get((short)0); + keyBlob = KMArray.cast(ret).get((short)1); + keyCharacteristics = KMArray.cast(ret).get((short)2); + } + + private void extractKeyChar(ResponseAPDU response) { + short ret = KMArray.instance((short) 2); + KMArray.cast(ret).add((short) 0, KMInteger.exp()); + short inst = KMKeyCharacteristics.exp(); + KMArray.cast(ret).add((short) 1, inst); + KMDecoder dec = new KMDecoder(); + byte[] respBuf = response.getBytes(); + short len = (short) respBuf.length; + ret = dec.decode(ret, respBuf, (short) 0, len); + status = KMArray.cast(ret).get((short)0); + keyCharacteristics = KMArray.cast(ret).get((short)1); + } + + public void testGenerateAesKey(CardSimulator simulator){ + byte[] buf = new byte[512]; + buf[0] = (byte)0x80; + buf[1] = (byte)0x10; + buf[2] = (byte)0x40; + buf[3] = (byte)0x00; + buf[4] = 0; + // test provision command + short cmd = makeGenerateKeyCmd(KMType.AES, (short)256); + KMEncoder enc = new KMEncoder(); + short actualLen = enc.encode(cmd, buf, (short) 7); + Util.setShort(buf, (short)5, actualLen); + byte[] apdu = new byte[7+actualLen]; + Util.arrayCopyNonAtomic(buf,(short)0,apdu,(short)0,(short)(7+actualLen)); + //CommandAPDU commandAPDU = new CommandAPDU(0x80, 0x10, 0x40, 0x00, buf, 0, actualLen); + CommandAPDU commandAPDU = new CommandAPDU(apdu); + //print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(commandAPDU); + extractKeyCharAndBlob(response); + Assert.assertEquals(0x9000, response.getSW()); + } + + public void testImportAesKey(CardSimulator simulator){ + byte[] buf = new byte[512]; + buf[0] = (byte)0x80; + buf[1] = (byte)0x11; + buf[2] = (byte)0x40; + buf[3] = (byte)0x00; + buf[4] = 0; + short cmd = makeImportKeySymmCmd(KMType.AES, (short)256); + KMEncoder enc = new KMEncoder(); + short actualLen = enc.encode(cmd, buf, (short) 7); + Util.setShort(buf, (short)5, actualLen); + byte[] apdu = new byte[7+actualLen]; + Util.arrayCopyNonAtomic(buf,(short)0,apdu,(short)0,(short)(7+actualLen)); + //CommandAPDU commandAPDU = new CommandAPDU(0x80, 0x10, 0x40, 0x00, buf, 0, actualLen); + CommandAPDU commandAPDU = new CommandAPDU(apdu); + // print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(commandAPDU); + extractKeyCharAndBlob(response); + Assert.assertEquals(0x9000, response.getSW()); + } + public void testImportHmacKey(CardSimulator simulator){ + byte[] buf = new byte[512]; + buf[0] = (byte)0x80; + buf[1] = (byte)0x11; + buf[2] = (byte)0x40; + buf[3] = (byte)0x00; + buf[4] = 0; + short cmd = makeImportKeySymmCmd(KMType.HMAC, (short)128); + KMEncoder enc = new KMEncoder(); + short actualLen = enc.encode(cmd, buf, (short) 7); + Util.setShort(buf, (short)5, actualLen); + byte[] apdu = new byte[7+actualLen]; + Util.arrayCopyNonAtomic(buf,(short)0,apdu,(short)0,(short)(7+actualLen)); + //CommandAPDU commandAPDU = new CommandAPDU(0x80, 0x10, 0x40, 0x00, buf, 0, actualLen); + CommandAPDU commandAPDU = new CommandAPDU(apdu); + // print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(commandAPDU); + extractKeyCharAndBlob(response); + Assert.assertEquals(0x9000, response.getSW()); + } + public void testImportDesKey(CardSimulator simulator){ + byte[] buf = new byte[512]; + buf[0] = (byte)0x80; + buf[1] = (byte)0x11; + buf[2] = (byte)0x40; + buf[3] = (byte)0x00; + buf[4] = 0; + short cmd = makeImportKeySymmCmd(KMType.DES, (short)168); + KMEncoder enc = new KMEncoder(); + short actualLen = enc.encode(cmd, buf, (short) 7); + Util.setShort(buf, (short)5, actualLen); + byte[] apdu = new byte[7+actualLen]; + Util.arrayCopyNonAtomic(buf,(short)0,apdu,(short)0,(short)(7+actualLen)); + //CommandAPDU commandAPDU = new CommandAPDU(0x80, 0x10, 0x40, 0x00, buf, 0, actualLen); + CommandAPDU commandAPDU = new CommandAPDU(apdu); + print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(commandAPDU); + extractKeyCharAndBlob(response); + Assert.assertEquals(0x9000, response.getSW()); + } + + public void testGenerateHmacKey(CardSimulator simulator){ + byte[] buf = new byte[512]; + buf[0] = (byte)0x80; + buf[1] = (byte)0x10; + buf[2] = (byte)0x40; + buf[3] = (byte)0x00; + buf[4] = 0; + // test provision command + short cmd = makeGenerateKeyCmdHmac(KMType.HMAC, (short)128); + KMEncoder enc = new KMEncoder(); + short actualLen = enc.encode(cmd, buf, (short) 7); + Util.setShort(buf, (short)5, actualLen); + byte[] apdu = new byte[7+actualLen]; + Util.arrayCopyNonAtomic(buf,(short)0,apdu,(short)0,(short)(7+actualLen)); + //CommandAPDU commandAPDU = new CommandAPDU(0x80, 0x10, 0x40, 0x00, buf, 0, actualLen); + CommandAPDU commandAPDU = new CommandAPDU(apdu); + //print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(commandAPDU); + extractKeyCharAndBlob(response); + Assert.assertEquals(0x9000, response.getSW()); + } + + public void testGenerate3DesKey(CardSimulator simulator){ + byte[] buf = new byte[512]; + buf[0] = (byte)0x80; + buf[1] = (byte)0x10; + buf[2] = (byte)0x40; + buf[3] = (byte)0x00; + buf[4] = 0; + // test provision command + short cmd = makeGenerateKeyCmd(KMType.DES, (short)168); + KMEncoder enc = new KMEncoder(); + short actualLen = enc.encode(cmd, buf, (short) 7); + Util.setShort(buf, (short)5, actualLen); + byte[] apdu = new byte[7+actualLen]; + Util.arrayCopyNonAtomic(buf,(short)0,apdu,(short)0,(short)(7+actualLen)); + //CommandAPDU commandAPDU = new CommandAPDU(0x80, 0x10, 0x40, 0x00, buf, 0, actualLen); + CommandAPDU commandAPDU = new CommandAPDU(apdu); + //print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(commandAPDU); + extractKeyCharAndBlob(response); + Assert.assertEquals(0x9000, response.getSW()); + } + + public void testGenerateEcKey(CardSimulator simulator){ + byte[] buf = new byte[512]; + buf[0] = (byte)0x80; + buf[1] = (byte)0x10; + buf[2] = (byte)0x40; + buf[3] = (byte)0x00; + buf[4] = 0; + // test provision command + short cmd = makeGenerateKeyCmd(KMType.EC, (short)256); + KMEncoder enc = new KMEncoder(); + short actualLen = enc.encode(cmd, buf, (short) 7); + Util.setShort(buf, (short)5, actualLen); + byte[] apdu = new byte[7+actualLen]; + Util.arrayCopyNonAtomic(buf,(short)0,apdu,(short)0,(short)(7+actualLen)); + //CommandAPDU commandAPDU = new CommandAPDU(0x80, 0x10, 0x40, 0x00, buf, 0, actualLen); + CommandAPDU commandAPDU = new CommandAPDU(apdu); + //print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(commandAPDU); + extractKeyCharAndBlob(response); + Assert.assertEquals(0x9000, response.getSW()); + } public void testGetHwInfoCmd(CardSimulator simulator){ CommandAPDU commandAPDU = new CommandAPDU(0x80, 0x1E, 0x40, 0x00); @@ -99,7 +418,7 @@ private void testAddRngEntropyCmd(CardSimulator simulator){ // test provision command short cmd = makeAddRngEntropyCmd(); KMEncoder enc = new KMEncoder(); - short actualLen = enc.encode(cmd, buf, (short) 0, (short)buf.length); + short actualLen = enc.encode(cmd, buf, (short) 0); CommandAPDU commandAPDU = new CommandAPDU(0x80, 0x18, 0x40, 0x00, buf, 0, actualLen); //print(commandAPDU.getBytes()); @@ -150,6 +469,232 @@ private short makeProvisionCmd() { arg.add((short) 2, keyBlobPtr); return argPtr; } + + private short makeGenerateKeyCmd(byte alg, short keysize) { + // Argument + short arrPtr = KMArray.instance((short) 5); + KMArray vals = KMArray.cast(arrPtr); + byte[] val = "Test".getBytes(); + byte[] intVal = {1, 2, 3, 4}; + byte[] pubVal = {0x00, 0x01, 0x00, 0x01}; + byte[] digest = {KMType.SHA1, KMType.SHA2_256}; + vals.add((short)0, KMEnumTag.instance(KMType.ALGORITHM, alg)); + vals.add((short)1, KMIntegerTag.instance(KMType.UINT_TAG, KMType.USERID, KMInteger.uint_32(intVal, (short)0))); + vals.add((short)2, KMByteTag.instance(KMType.APPLICATION_ID, KMByteBlob.instance(val, (short)0, (short)val.length))); + vals.add((short)3, KMIntegerTag.instance(KMType.ULONG_TAG, KMType.RSA_PUBLIC_EXPONENT, KMInteger.uint_32(pubVal,(short)0))); + vals.add((short)4, KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, KMInteger.uint_16(keysize))); + short keyParamsPtr = KMKeyParameters.instance(arrPtr); + // Array of expected arguments + short argPtr = KMArray.instance((short) 1); + KMArray arg = KMArray.cast(argPtr); + arg.add((short) 0, keyParamsPtr); + return argPtr; + } + private short makeImportKeySymmCmd(short alg, short size) { + // Argument 1 + short arrPtr; + if(alg == KMType.HMAC) { + arrPtr = KMArray.instance((short) 6); + } else{ + arrPtr = KMArray.instance((short) 5); + } + KMArray vals = KMArray.cast(arrPtr); + byte[] val = "Test".getBytes(); + byte[] intVal = {1, 2, 3, 4}; + byte[] pubVal = {0x00, 0x01, 0x00, 0x01}; + byte[] digest = {KMType.SHA1, KMType.SHA2_256}; + vals.add((short)0, KMEnumTag.instance(KMType.ALGORITHM, (byte)alg)); + vals.add((short)1, KMIntegerTag.instance(KMType.UINT_TAG, KMType.USERID, KMInteger.uint_32(intVal, (short)0))); + vals.add((short)2, KMByteTag.instance(KMType.APPLICATION_ID, KMByteBlob.instance(val, (short)0, (short)val.length))); + vals.add((short)3, KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, KMInteger.uint_16(size))); + vals.add((short)4, KMEnumArrayTag.instance(KMType.DIGEST, KMByteBlob.instance(digest,(short)0, (short)digest.length))); + if (alg == KMType.HMAC) { + vals.add( + (short) 5,KMIntegerTag.instance(KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, KMInteger.uint_16(size))); + } + short keyParamsPtr = KMKeyParameters.instance(arrPtr); + // Argument 2 + short keyFormatPtr = KMEnum.instance(KMType.KEY_FORMAT, KMType.RAW); + short len = 0; + byte[] secret = new byte[32]; + Key key; + // Argument 3 + switch (alg){ + case KMType.AES: + key = sim.createAESKey(size); + len = ((AESKey)key).getKey(secret, (short)0); + break; + case KMType.DES: + key = sim.createTDESKey(); + size = 168; + len = ((DESKey)key).getKey(secret, (short)0); + break; + case KMType.HMAC: + key = sim.createHMACKey(size); + len = ((HMACKey)key).getKey(secret, (short)0); + break; + default: + return 0; + } + short keyBlob = KMArray.instance((short)1); + KMArray.cast(keyBlob).add((short)0, KMByteBlob.instance(secret,(short)0,len)); + KMEncoder encoder = new KMEncoder(); + byte[] blob = new byte[256]; + len = encoder.encode(keyBlob,blob,(short)0); + keyBlob = KMByteBlob.instance(blob, (short)0, len); + // Array of expected arguments + short argPtr = KMArray.instance((short) 3); + KMArray arg = KMArray.cast(argPtr); + arg.add((short) 0, keyParamsPtr); + arg.add((short)1, keyFormatPtr); + arg.add((short)2, keyBlob); + return argPtr; + } + + private short makeImportKeyRsaCmd() { + // Argument 1 + short arrPtr = KMArray.instance((short) 4); + KMArray vals = KMArray.cast(arrPtr); + byte[] val = "Test".getBytes(); + byte[] intVal = {1, 2, 3, 4}; + byte[] pubVal = {0x00, 0x01, 0x00, 0x01}; + byte[] digest = {KMType.SHA1, KMType.SHA2_256}; + vals.add((short)0, KMEnumTag.instance(KMType.ALGORITHM, KMType.RSA)); + vals.add((short)1, KMIntegerTag.instance(KMType.UINT_TAG, KMType.USERID, KMInteger.uint_32(intVal, (short)0))); + vals.add((short)2, KMByteTag.instance(KMType.APPLICATION_ID, KMByteBlob.instance(val, (short)0, (short)val.length))); + vals.add((short)3, KMIntegerTag.instance(KMType.ULONG_TAG, KMType.RSA_PUBLIC_EXPONENT, KMInteger.uint_32(pubVal,(short)0))); + //vals.add((short)4, KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, KMInteger.uint_16((short)2048))); + short keyParamsPtr = KMKeyParameters.instance(arrPtr); + // Argument 2 + short keyFormatPtr = KMEnum.instance(KMType.KEY_FORMAT, KMType.RAW); + // Argument 3 + KeyPair rsa512KeyPair = new KeyPair(KeyPair.ALG_RSA, KeyBuilder.LENGTH_RSA_512); + rsa512KeyPair.genKeyPair(); + byte[] secret = new byte[64]; + byte[] modulus = new byte[64]; + short keyBlob = KMArray.instance((short)2); + RSAPrivateKey key = (RSAPrivateKey) rsa512KeyPair.getPrivate(); + short len = key.getExponent(secret, (short)0); + KMArray.cast(keyBlob).add((short)0, KMByteBlob.instance(secret,(short)0,len)); + len = key.getModulus(modulus, (short)0); + KMArray.cast(keyBlob).add((short)1, KMByteBlob.instance(modulus,(short)0,len)); + KMEncoder encoder = new KMEncoder(); + byte[] blob = new byte[256]; + len = encoder.encode(keyBlob,blob,(short)0); + keyBlob = KMByteBlob.instance(blob, (short)0, len); + // Array of expected arguments + short argPtr = KMArray.instance((short) 3); + KMArray arg = KMArray.cast(argPtr); + arg.add((short) 0, keyParamsPtr); + arg.add((short)1, keyFormatPtr); + arg.add((short)2, keyBlob); + return argPtr; + } + + private short makeImportKeyEcCmd() { + // Argument 1 + short arrPtr = KMArray.instance((short) 4); + KMArray vals = KMArray.cast(arrPtr); + byte[] val = "Test".getBytes(); + byte[] intVal = {1, 2, 3, 4}; + byte[] pubVal = {0x00, 0x01, 0x00, 0x01}; + byte[] digest = {KMType.SHA1, KMType.SHA2_256}; + vals.add((short)0, KMEnumTag.instance(KMType.ALGORITHM, KMType.EC)); + vals.add((short)1, KMIntegerTag.instance(KMType.UINT_TAG, KMType.USERID, KMInteger.uint_32(intVal, (short)0))); + vals.add((short)2, KMByteTag.instance(KMType.APPLICATION_ID, KMByteBlob.instance(val, (short)0, (short)val.length))); + vals.add((short)3, KMIntegerTag.instance(KMType.ULONG_TAG, KMType.RSA_PUBLIC_EXPONENT, KMInteger.uint_32(pubVal,(short)0))); + //vals.add((short)4, KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, KMInteger.uint_16((short)2048))); + short keyParamsPtr = KMKeyParameters.instance(arrPtr); + // Argument 2 + short keyFormatPtr = KMEnum.instance(KMType.KEY_FORMAT, KMType.RAW); + // Argument 3 + KeyPair ec192KeyPair = new KeyPair(KeyPair.ALG_EC_FP, KeyBuilder.LENGTH_EC_FP_192); + ec192KeyPair.genKeyPair(); + byte[] secret = new byte[24]; + byte[] pubKey = new byte[52]; + short keyBlob = KMArray.instance((short)3); + ECPrivateKey key1 = (ECPrivateKey) ec192KeyPair.getPrivate(); + ECPublicKey key2 = (ECPublicKey) ec192KeyPair.getPublic(); + short len = key1.getS(secret, (short)0); + KMArray.cast(keyBlob).add((short)0, KMByteBlob.instance(secret,(short)0,len)); + len = key2.getW(pubKey, (short)0); + KMArray.cast(keyBlob).add((short)1, KMByteBlob.instance(pubKey,(short)0,len)); + KMArray.cast(keyBlob).add((short)2, KMEnumTag.instance(KMType.ECCURVE, KMType.P_256)); + KMEncoder encoder = new KMEncoder(); + byte[] blob = new byte[256]; + len = encoder.encode(keyBlob,blob,(short)0); + keyBlob = KMByteBlob.instance(blob, (short)0, len); + // Array of expected arguments + short argPtr = KMArray.instance((short) 3); + KMArray arg = KMArray.cast(argPtr); + arg.add((short) 0, keyParamsPtr); + arg.add((short)1, keyFormatPtr); + arg.add((short)2, keyBlob); + return argPtr; + } + + private short makeGetKeyCharKeyCmd() { + // Argument + short argPtr = KMArray.instance((short) 3); + KMArray vals = KMArray.cast(argPtr); + byte[] val = "Test".getBytes(); + byte[] intVal = {1, 2, 3, 4}; + byte[] pubVal = {0x00, 0x01, 0x00, 0x01}; + byte[] digest = {KMType.SHA1, KMType.SHA2_256}; + vals.add((short)0, keyBlob); + vals.add((short)1, KMByteBlob.instance(val, (short)0, (short)val.length)); + Util.setShort(val,(short)0,KMType.INVALID_VALUE); + vals.add((short)2, KMByteBlob.instance(val, (short)0, (short)2));// No App Data + return argPtr; + } + private short makeGenerateKeyCmdHmac(byte alg, short keysize) { + // Argument + short arrPtr = KMArray.instance((short) 6); + KMArray vals = KMArray.cast(arrPtr); + byte[] val = "Test".getBytes(); + byte[] intVal = {1, 2, 3, 4}; + byte[] digest = {KMType.SHA1, KMType.SHA2_256}; + vals.add((short)0, KMEnumTag.instance(KMType.ALGORITHM, alg)); + vals.add((short)1, KMIntegerTag.instance(KMType.UINT_TAG, KMType.USERID, KMInteger.uint_32(intVal, (short)0))); + vals.add((short)2, KMByteTag.instance(KMType.APPLICATION_ID, KMByteBlob.instance(val, (short)0, (short)val.length))); + vals.add((short)3, KMIntegerTag.instance(KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, KMInteger.uint_16(keysize))); + vals.add((short)4, KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, KMInteger.uint_16(keysize))); + vals.add((short)5, KMEnumArrayTag.instance(KMType.DIGEST, KMByteBlob.instance(digest,(short)0, (short)digest.length))); + short keyParamsPtr = KMKeyParameters.instance(arrPtr); + // Array of expected arguments + short argPtr = KMArray.instance((short) 1); + KMArray arg = KMArray.cast(argPtr); + arg.add((short) 0, keyParamsPtr); + return argPtr; + } + + private short makeSetBootParamsCmd() { + // Argument 1 OS Version + short versionPatchPtr = KMInteger.uint_16((short)1); + short versionTagPtr = KMIntegerTag.instance(KMType.UINT_TAG, KMType.OS_VERSION,versionPatchPtr); + // Argument 2 OS Patch level + short patchTagPtr = KMIntegerTag.instance(KMType.UINT_TAG, KMType.OS_PATCH_LEVEL, versionPatchPtr); + // Argument 3 Verified Boot Key + byte[] bootKeyHash = "00011122233344455566677788899900".getBytes(); + short bootKeyPtr = KMByteBlob.instance(bootKeyHash,(short)0, (short)bootKeyHash.length); + // Argument 4 Verified Boot Hash + short bootHashPtr = KMByteBlob.instance(bootKeyHash,(short)0, (short)bootKeyHash.length); + // Argument 5 Verified Boot State + short bootStatePtr = KMEnum.instance(KMType.VERIFIED_BOOT_STATE,KMType.VERIFIED_BOOT); + // Argument 6 Device Locked + short deviceLockedPtr = KMEnum.instance(KMType.DEVICE_LOCKED, KMType.DEVICE_LOCKED_FALSE); + // Arguments + short arrPtr = KMArray.instance((short) 6); + KMArray vals = KMArray.cast(arrPtr); + vals.add((short)0, versionTagPtr); + vals.add((short) 1, patchTagPtr); + vals.add((short) 2, bootKeyPtr); + vals.add((short) 3, bootHashPtr); + vals.add((short) 4, bootStatePtr); + vals.add((short) 5, deviceLockedPtr); + return arrPtr; + } + private short makeAddRngEntropyCmd() { // Argument 1 byte[] byteBlob = new byte[32]; diff --git a/Applet/JavaCardKeymaster.scr b/Applet/JavaCardKeymaster.scr index ce15b5c9..b1a669be 100644 --- a/Applet/JavaCardKeymaster.scr +++ b/Applet/JavaCardKeymaster.scr @@ -3,15 +3,48 @@ output on; //create applet instance 0x80 0xB8 0x00 0x00 0x0c 0x0a 0xa0 0x00 0x00 0x00 0x62 0x03 0x01 0xc 0x01 0x01 0x00 0x7F; - // Select JavaCardKeymaster //aid/A000000062/03010C0101 -0x00 0xa4 0x04 0x00 0x0a 0xa0 0x00 0x00 0x00 0x62 0x03 0x01 0xc 0x01 0x01 0x7F; + 0x00 0xa4 0x04 0x00 0x0a 0xa0 0x00 0x00 0x00 0x62 0x03 0x01 0xc 0x01 0x01 0x7F; // Send provision command - this will change in future -0x80 0x23 0x40 0x00 0x3B 0x83 0xA1 0x1A 0x10 0x00 0x00 0x02 0x01 0x00 0x58 0x30 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0A 0x0B 0x0C 0x0D 0x0E 0x0F 0x10 0x11 0x12 0x13 0x14 0x15 0x16 0x17 0x18 0x19 0x1A 0x1B 0x1C 0x1D 0x1E 0x1F 0x20 0x21 0x22 0x23 0x24 0x25 0x26 0x27 0x28 0x29 0x2A 0x2B 0x2C 0x2D 0x2E 0x2F 0x7F; + 0x80 0x23 0x40 0x00 0x3B 0x83 0xA1 0x1A 0x10 0x00 0x00 0x02 0x01 0x00 0x58 0x30 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0A 0x0B 0x0C 0x0D 0x0E 0x0F 0x10 0x11 0x12 0x13 0x14 0x15 0x16 0x17 0x18 0x19 0x1A 0x1B 0x1C 0x1D 0x1E 0x1F 0x20 0x21 0x22 0x23 0x24 0x25 0x26 0x27 0x28 0x29 0x2A 0x2B 0x2C 0x2D 0x2E 0x2F 0x7F; + +// Send Set Boot Params command + 0x80 0x24 0x40 0x00 0x53 0x86 0x1A 0x30 0x00 0x02 0xC1 0x01 0x1A 0x30 0x00 0x02 0xC2 0x01 0x58 0x20 0x30 0x30 0x30 0x31 0x31 0x31 0x32 0x32 0x32 0x33 0x33 0x33 0x34 0x34 0x34 0x35 0x35 0x35 0x36 0x36 0x36 0x37 0x37 0x37 0x38 0x38 0x38 0x39 0x39 0x39 0x30 0x30 0x58 0x20 0x30 0x30 0x30 0x31 0x31 0x31 0x32 0x32 0x32 0x33 0x33 0x33 0x34 0x34 0x34 0x35 0x35 0x35 0x36 0x36 0x36 0x37 0x37 0x37 0x38 0x38 0x38 0x39 0x39 0x39 0x30 0x30 0x02 0x00 0x7F; // Send getHardwareInfo command -0x80 0x1E 0x40 0x00 0x00 0x7F; + 0x80 0x1E 0x40 0x00 0x00 0x7F; // Send AddRngEntropy command -0x80 0x18 0x40 0x00 0x23 0x81 0x58 0x20 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0A 0x0B 0x0C 0x0D 0x0E 0x0F 0x10 0x11 0x12 0x13 0x14 0x15 0x16 0x17 0x18 0x19 0x1A 0x1B 0x1C 0x1D 0x1E 0x1F 0x7F; \ No newline at end of file + 0x80 0x18 0x40 0x00 0x23 0x81 0x58 0x20 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0A 0x0B 0x0C 0x0D 0x0E 0x0F 0x10 0x11 0x12 0x13 0x14 0x15 0x16 0x17 0x18 0x19 0x1A 0x1B 0x1C 0x1D 0x1E 0x1F 0x7F; + +// Generate Key - RSA Key command + 0x80 0x10 0x40 0x00 0x2E 0x81 0xA5 0x1A 0x10 0x00 0x00 0x02 0x01 0x1A 0x30 0x00 0x01 0xF5 0x1A 0x01 0x02 0x03 0x04 0x1A 0x90 0x00 0x02 0x59 0x44 0x54 0x65 0x73 0x74 0x1A 0x50 0x00 0x00 0xC8 0x1A 0x00 0x01 0x00 0x01 0x1A 0x30 0x00 0x00 0x03 0x19 0x08 0x00 0x7F; + +// Generate Key - AES Key command + 0x80 0x10 0x40 0x00 0x2F 0x81 0xA5 0x1A 0x10 0x00 0x00 0x02 0x18 0x20 0x1A 0x30 0x00 0x01 0xF5 0x1A 0x01 0x02 0x03 0x04 0x1A 0x90 0x00 0x02 0x59 0x44 0x54 0x65 0x73 0x74 0x1A 0x50 0x00 0x00 0xC8 0x1A 0x00 0x01 0x00 0x01 0x1A 0x30 0x00 0x00 0x03 0x19 0x01 0x00 0x7F; + +// Generate Key - ECC Key command + 0x80 0x10 0x40 0x00 0x2E 0x81 0xA5 0x1A 0x10 0x00 0x00 0x02 0x03 0x1A 0x30 0x00 0x01 0xF5 0x1A 0x01 0x02 0x03 0x04 0x1A 0x90 0x00 0x02 0x59 0x44 0x54 0x65 0x73 0x74 0x1A 0x50 0x00 0x00 0xC8 0x1A 0x00 0x01 0x00 0x01 0x1A 0x30 0x00 0x00 0x03 0x19 0x01 0x00 0x7F; + +// Generate Key - DES Key command + 0x80 0x10 0x40 0x00 0x2E 0x81 0xA5 0x1A 0x10 0x00 0x00 0x02 0x18 0x21 0x1A 0x30 0x00 0x01 0xF5 0x1A 0x01 0x02 0x03 0x04 0x1A 0x90 0x00 0x02 0x59 0x44 0x54 0x65 0x73 0x74 0x1A 0x50 0x00 0x00 0xC8 0x1A 0x00 0x01 0x00 0x01 0x1A 0x30 0x00 0x00 0x03 0x18 0xA8 0x7F; + +// Generate Key - HMAC Key command + 0x80 0x10 0x40 0x00 0x33 0x81 0xA6 0x1A 0x10 0x00 0x00 0x02 0x18 0x80 0x1A 0x30 0x00 0x01 0xF5 0x1A 0x01 0x02 0x03 0x04 0x1A 0x90 0x00 0x02 0x59 0x44 0x54 0x65 0x73 0x74 0x1A 0x30 0x00 0x00 0x08 0x18 0x80 0x1A 0x30 0x00 0x00 0x03 0x18 0x80 0x1A 0x20 0x00 0x00 0x05 0x42 0x02 0x04 0x7F; + +// Import RSA Key + 0x80 0x11 0x40 0x00 0xAE 0x83 0xA4 0x1A 0x10 0x00 0x00 0x02 0x01 0x1A 0x30 0x00 0x01 0xF5 0x1A 0x01 0x02 0x03 0x04 0x1A 0x90 0x00 0x02 0x59 0x44 0x54 0x65 0x73 0x74 0x1A 0x50 0x00 0x00 0xC8 0x1A 0x00 0x01 0x00 0x01 0x03 0x58 0x85 0x82 0x58 0x40 0x1F 0x1E 0xBD 0xB0 0xE0 0x3A 0x42 0xD7 0x7A 0x2C 0xBE 0xB7 0x00 0x7F 0x98 0x3C 0x32 0xC3 0x43 0x80 0x16 0x75 0x09 0xF4 0xAA 0x4E 0x45 0x85 0xE0 0x8B 0x34 0xC3 0x5B 0x3C 0x03 0x4A 0x2D 0x35 0x7D 0xFC 0x31 0x6A 0xB6 0x49 0xB1 0x3F 0x92 0x90 0x21 0x4C 0x99 0x5B 0x8B 0x4E 0xD6 0x00 0x7D 0x01 0x5D 0xEC 0x1F 0x87 0x6E 0x81 0x58 0x40 0x80 0x94 0x2B 0xC3 0x69 0x81 0x10 0xF9 0x66 0x3A 0xB2 0x4D 0x43 0x05 0x03 0xD6 0x79 0x4B 0x25 0xFB 0x30 0x98 0xE0 0x8E 0x5A 0x1A 0xA8 0xC0 0xFE 0x5F 0x0C 0x79 0x66 0x2C 0x0B 0x9D 0xDA 0xA5 0x45 0x4F 0x29 0x2C 0x80 0x9E 0xAF 0xB0 0x0A 0x6C 0xEA 0x5B 0xAE 0x22 0xF2 0x6C 0x37 0xFB 0x69 0x5F 0xED 0xDF 0xDB 0x3F 0x17 0xE5 0x7F; + +// Import EC Key + 0x80 0x11 0x40 0x00 0x7D 0x83 0xA4 0x1A 0x10 0x00 0x00 0x02 0x03 0x1A 0x30 0x00 0x01 0xF5 0x1A 0x01 0x02 0x03 0x04 0x1A 0x90 0x00 0x02 0x59 0x44 0x54 0x65 0x73 0x74 0x1A 0x50 0x00 0x00 0xC8 0x1A 0x00 0x01 0x00 0x01 0x03 0x58 0x54 0x83 0x58 0x18 0xA8 0x90 0x6D 0x38 0x3A 0x25 0x67 0x08 0x85 0xD3 0x07 0x17 0x3A 0x7C 0x0E 0x81 0x96 0x46 0xA0 0xDA 0x2C 0xBD 0xD2 0xEA 0x58 0x31 0x04 0x95 0xCE 0xB7 0x75 0x21 0xD8 0xAE 0xC9 0xA2 0x99 0xC0 0x00 0x6C 0x2E 0xC2 0x11 0x7D 0x79 0x52 0x56 0xB7 0x3D 0xC0 0xC8 0xE7 0x07 0x6D 0xD1 0xBD 0xCD 0xF6 0x05 0xD3 0xB3 0xF4 0xD1 0x56 0x86 0x90 0xED 0xD3 0x4F 0xA2 0x85 0xC2 0x3B 0xCE 0x45 0x1A 0x10 0x00 0x00 0x0A 0x01 0x7F; + +// Import AES Key + 0x80 0x11 0x40 0x00 0x41 0x83 0xA5 0x1A 0x10 0x00 0x00 0x02 0x18 0x20 0x1A 0x30 0x00 0x01 0xF5 0x1A 0x01 0x02 0x03 0x04 0x1A 0x90 0x00 0x02 0x59 0x44 0x54 0x65 0x73 0x74 0x1A 0x30 0x00 0x00 0x03 0x19 0x01 0x00 0x1A 0x20 0x00 0x00 0x05 0x42 0x02 0x04 0x03 0x52 0x81 0x50 0xF6 0x16 0x03 0x1C 0x26 0xB5 0x6F 0x2E 0x42 0xCB 0x7C 0x37 0x96 0x1C 0x27 0xBA 0x7F; + +// Import Hmac Key + 0x80 0x11 0x40 0x00 0x47 0x83 0xA6 0x1A 0x10 0x00 0x00 0x02 0x18 0x80 0x1A 0x30 0x00 0x01 0xF5 0x1A 0x01 0x02 0x03 0x04 0x1A 0x90 0x00 0x02 0x59 0x44 0x54 0x65 0x73 0x74 0x1A 0x30 0x00 0x00 0x03 0x18 0x80 0x1A 0x20 0x00 0x00 0x05 0x42 0x02 0x04 0x1A 0x30 0x00 0x00 0x08 0x18 0x80 0x03 0x52 0x81 0x50 0xA8 0x66 0xA3 0xE5 0xBA 0x53 0xE9 0x93 0x00 0xE9 0x1A 0xEC 0xBF 0x8F 0xEC 0x90 0x7F; + +// Import Des Key + 0x80 0x11 0x40 0x00 0x40 0x83 0xA5 0x1A 0x10 0x00 0x00 0x02 0x18 0x21 0x1A 0x30 0x00 0x01 0xF5 0x1A 0x01 0x02 0x03 0x04 0x1A 0x90 0x00 0x02 0x59 0x44 0x54 0x65 0x73 0x74 0x1A 0x30 0x00 0x00 0x03 0x18 0xA8 0x1A 0x20 0x00 0x00 0x05 0x42 0x02 0x04 0x03 0x52 0x81 0x50 0x80 0x5B 0x6F 0xCB 0xBB 0x1B 0x07 0xD3 0xB6 0xE1 0x67 0xAB 0x51 0xC6 0x34 0x29 0x7F; + diff --git a/Applet/default.output b/Applet/default.output index 81fa92ba..3763ffa7 100644 --- a/Applet/default.output +++ b/Applet/default.output @@ -5,5 +5,16 @@ OUTPUT ON; CLA: 80, INS: b8, P1: 00, P2: 00, Lc: 0c, 0a, a0, 00, 00, 00, 62, 03, 01, 0c, 01, 01, 00, Le: 0a, a0, 00, 00, 00, 62, 03, 01, 0c, 01, 01, SW1: 90, SW2: 00 CLA: 00, INS: a4, P1: 04, P2: 00, Lc: 0a, a0, 00, 00, 00, 62, 03, 01, 0c, 01, 01, Le: 00, SW1: 90, SW2: 00 CLA: 80, INS: 23, P1: 40, P2: 00, Lc: 3b, 83, a1, 1a, 10, 00, 00, 02, 01, 00, 58, 30, 00, 01, 02, 03, 04, 05, 06, 07, 08, 09, 0a, 0b, 0c, 0d, 0e, 0f, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 1a, 1b, 1c, 1d, 1e, 1f, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 2a, 2b, 2c, 2d, 2e, 2f, Le: 00, SW1: 90, SW2: 00 -CLA: 80, INS: 1e, P1: 40, P2: 00, Lc: 00, Le: 56, 83, 02, 57, 4a, 61, 76, 61, 63, 61, 72, 64, 4b, 65, 79, 6d, 61, 73, 74, 65, 72, 44, 65, 76, 69, 63, 65, 46, 47, 6f, 6f, 67, 6c, 65, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, SW1: 90, SW2: 00 +CLA: 80, INS: 24, P1: 40, P2: 00, Lc: 53, 86, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, 58, 20, 30, 30, 30, 31, 31, 31, 32, 32, 32, 33, 33, 33, 34, 34, 34, 35, 35, 35, 36, 36, 36, 37, 37, 37, 38, 38, 38, 39, 39, 39, 30, 30, 58, 20, 30, 30, 30, 31, 31, 31, 32, 32, 32, 33, 33, 33, 34, 34, 34, 35, 35, 35, 36, 36, 36, 37, 37, 37, 38, 38, 38, 39, 39, 39, 30, 30, 02, 00, Le: 00, SW1: 90, SW2: 00 +CLA: 80, INS: 1e, P1: 40, P2: 00, Lc: 00, Le: 21, 83, 02, 57, 4a, 61, 76, 61, 63, 61, 72, 64, 4b, 65, 79, 6d, 61, 73, 74, 65, 72, 44, 65, 76, 69, 63, 65, 46, 47, 6f, 6f, 67, 6c, 65, SW1: 90, SW2: 00 CLA: 80, INS: 18, P1: 40, P2: 00, Lc: 23, 81, 58, 20, 00, 01, 02, 03, 04, 05, 06, 07, 08, 09, 0a, 0b, 0c, 0d, 0e, 0f, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 1a, 1b, 1c, 1d, 1e, 1f, Le: 00, SW1: 90, SW2: 00 +CLA: 80, INS: 10, P1: 40, P2: 00, Lc: 2e, 81, a5, 1a, 10, 00, 00, 02, 01, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, 1a, 90, 00, 02, 59, 44, 54, 65, 73, 74, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 30, 00, 00, 03, 19, 08, 00, Le: 11, 83, 00, 58, d6, 85, 58, 40, 11, 8c, a2, 01, 8d, 5d, d0, d5, 79, dc, fc, 71, a5, a0, ea, fc, e5, 11, 32, e7, 8a, bd, d1, 5c, a0, d8, 5f, 01, 8e, 4d, b1, f0, 09, 79, 5c, b9, e3, 26, 09, 26, dc, 7c, 21, b4, ee, 9b, 28, aa, c2, 73, 0c, ca, e4, 9b, 29, 5d, a0, f4, aa, 87, 7a, 51, 31, 7a, 4c, 62, 92, 2f, 4c, 7d, da, 2a, 3d, 6d, 92, d2, 0a, 4c, 63, 8e, 40, 1f, 03, a1, b1, 1c, 32, 52, 22, 73, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a6, 1a, 10, 00, 00, 02, 01, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 30, 00, 00, 03, 19, 08, 00, 1a, 10, 00, 02, be, 00, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, 58, 40, b4, 05, c4, 0e, 08, 15, 0e, a5, 86, 84, b2, 03, 00, 2c, cb, d7, 23, 85, c9, c1, 0b, 74, cb, 20, 10, b1, 25, 8e, 4b, 38, d4, 72, da, 2c, 2c, 64, 0e, e9, 2f, b2, d6, 74, 2e, 02, 77, 69, 74, 9f, a9, 98, e7, 7a, 4f, 9e, d4, 06, 62, 0c, 33, 6e, 80, 3a, 3f, e3, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a6, 1a, 10, 00, 00, 02, 01, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 30, 00, 00, 03, 19, 08, 00, 1a, 10, 00, 02, be, 00, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, SW1: 90, SW2: 00 +CLA: 80, INS: 10, P1: 40, P2: 00, Lc: 2f, 81, a5, 1a, 10, 00, 00, 02, 18, 20, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, 1a, 90, 00, 02, 59, 44, 54, 65, 73, 74, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 30, 00, 00, 03, 19, 01, 00, Le: a0, 83, 00, 58, 64, 84, 50, 4d, eb, 33, dc, 4d, 36, 74, 3a, 38, d4, b6, 1d, 0c, 48, 70, a6, 4c, fa, 0f, 3b, 66, 89, 7a, 20, 81, 20, c6, f5, f3, 4c, 14, cc, a8, 4b, 27, 04, 49, a0, 4f, 95, 93, 07, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a6, 1a, 10, 00, 00, 02, 18, 20, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 30, 00, 00, 03, 19, 01, 00, 1a, 10, 00, 02, be, 00, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a6, 1a, 10, 00, 00, 02, 18, 20, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 30, 00, 00, 03, 19, 01, 00, 1a, 10, 00, 02, be, 00, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, SW1: 90, SW2: 00 +CLA: 80, INS: 10, P1: 40, P2: 00, Lc: 2e, 81, a5, 1a, 10, 00, 00, 02, 03, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, 1a, 90, 00, 02, 59, 44, 54, 65, 73, 74, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 30, 00, 00, 03, 19, 01, 00, Le: da, 83, 00, 58, 9f, 85, 58, 18, a7, 1b, 67, ff, 9e, ef, 1a, 20, 88, ce, dc, 29, 49, f1, 69, 0a, c7, 99, 3f, 68, cf, c1, 12, 2a, 4c, ee, 2c, 52, c6, 4d, 6f, e7, 9e, 60, 6c, 96, 50, 4c, 86, 14, 3a, 79, 7a, c6, 2e, cb, 70, 33, 83, c2, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a6, 1a, 10, 00, 00, 02, 03, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 30, 00, 00, 03, 19, 01, 00, 1a, 10, 00, 02, be, 00, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, 58, 31, 04, 5a, 10, b8, d7, e9, be, 0a, e9, 47, 6c, 79, e4, 64, 15, 9c, 9d, 32, c6, 74, dd, 17, 11, 61, b7, 9a, 0c, 57, 8e, a1, 5f, 64, 1e, 8d, 51, 5b, 1e, 19, 4e, cc, 0e, 33, 15, d0, 9e, f6, f5, 8d, 2e, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a6, 1a, 10, 00, 00, 02, 03, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 30, 00, 00, 03, 19, 01, 00, 1a, 10, 00, 02, be, 00, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, SW1: 90, SW2: 00 +CLA: 80, INS: 10, P1: 40, P2: 00, Lc: 2e, 81, a5, 1a, 10, 00, 00, 02, 18, 21, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, 1a, 90, 00, 02, 59, 44, 54, 65, 73, 74, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 30, 00, 00, 03, 18, a8, Le: 9e, 83, 00, 58, 63, 84, 50, 6d, fd, 69, 01, 75, 25, d1, 54, 24, f1, 2c, c2, 6e, 6d, 8c, d3, 4c, e6, a1, 6a, dd, 67, 55, 62, df, f2, ed, 1c, e9, 4c, 42, ef, f8, a4, 56, 52, 52, 03, 73, d7, d7, f2, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a6, 1a, 10, 00, 00, 02, 18, 21, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 30, 00, 00, 03, 18, a8, 1a, 10, 00, 02, be, 00, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a6, 1a, 10, 00, 00, 02, 18, 21, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 30, 00, 00, 03, 18, a8, 1a, 10, 00, 02, be, 00, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, SW1: 90, SW2: 00 +CLA: 80, INS: 10, P1: 40, P2: 00, Lc: 33, 81, a6, 1a, 10, 00, 00, 02, 18, 80, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, 1a, 90, 00, 02, 59, 44, 54, 65, 73, 74, 1a, 30, 00, 00, 08, 18, 80, 1a, 30, 00, 00, 03, 18, 80, 1a, 20, 00, 00, 05, 42, 02, 04, Le: a8, 83, 00, 58, 68, 84, 50, af, ee, 95, 3a, d3, f1, 6a, 0a, e6, 88, 30, 63, fa, 8d, e5, 27, 4c, de, af, d7, 29, c4, 08, 59, 9d, 3b, 55, 59, 4e, 4c, 11, 1f, 0b, 47, 4e, a9, e5, d5, 74, 5a, 14, 20, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a7, 1a, 10, 00, 00, 02, 18, 80, 1a, 30, 00, 00, 08, 18, 80, 1a, 30, 00, 00, 03, 18, 80, 1a, 20, 00, 00, 05, 42, 02, 04, 1a, 10, 00, 02, be, 00, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a7, 1a, 10, 00, 00, 02, 18, 80, 1a, 30, 00, 00, 08, 18, 80, 1a, 30, 00, 00, 03, 18, 80, 1a, 20, 00, 00, 05, 42, 02, 04, 1a, 10, 00, 02, be, 00, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, SW1: 90, SW2: 00 +CLA: 80, INS: 11, P1: 40, P2: 00, Lc: ae, 83, a4, 1a, 10, 00, 00, 02, 01, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, 1a, 90, 00, 02, 59, 44, 54, 65, 73, 74, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 03, 58, 85, 82, 58, 40, 1f, 1e, bd, b0, e0, 3a, 42, d7, 7a, 2c, be, b7, 00, 7f, 98, 3c, 32, c3, 43, 80, 16, 75, 09, f4, aa, 4e, 45, 85, e0, 8b, 34, c3, 5b, 3c, 03, 4a, 2d, 35, 7d, fc, 31, 6a, b6, 49, b1, 3f, 92, 90, 21, 4c, 99, 5b, 8b, 4e, d6, 00, 7d, 01, 5d, ec, 1f, 87, 6e, 81, 58, 40, 80, 94, 2b, c3, 69, 81, 10, f9, 66, 3a, b2, 4d, 43, 05, 03, d6, 79, 4b, 25, fb, 30, 98, e0, 8e, 5a, 1a, a8, c0, fe, 5f, 0c, 79, 66, 2c, 0b, 9d, da, a5, 45, 4f, 29, 2c, 80, 9e, af, b0, 0a, 6c, ea, 5b, ae, 22, f2, 6c, 37, fb, 69, 5f, ed, df, db, 3f, 17, e5, Le: 11, 83, 00, 58, d6, 85, 58, 40, 3b, 5a, c7, 40, 44, 8e, b4, 9b, 73, 68, 11, bc, d7, 6b, 14, fb, 2f, 76, ea, 91, d1, 92, ba, 6c, 70, ce, 13, 5d, f9, 96, b0, 17, 6d, 4a, 6d, 16, fc, 60, 17, cd, 08, 8a, 47, 3a, a9, 7b, e5, 36, 70, 87, 2f, af, 65, cd, 9e, bc, 62, c2, a0, fe, 47, 6b, fa, 73, 4c, f4, b4, 76, af, 25, 7d, eb, 88, 6e, ca, f9, b8, 4c, a4, b9, a1, 20, 29, 43, 74, ac, 06, 8f, 61, 62, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a6, 1a, 10, 00, 00, 02, 01, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 30, 00, 00, 03, 19, 08, 00, 1a, 10, 00, 02, be, 02, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, 58, 40, 80, 94, 2b, c3, 69, 81, 10, f9, 66, 3a, b2, 4d, 43, 05, 03, d6, 79, 4b, 25, fb, 30, 98, e0, 8e, 5a, 1a, a8, c0, fe, 5f, 0c, 79, 66, 2c, 0b, 9d, da, a5, 45, 4f, 29, 2c, 80, 9e, af, b0, 0a, 6c, ea, 5b, ae, 22, f2, 6c, 37, fb, 69, 5f, ed, df, db, 3f, 17, e5, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a6, 1a, 10, 00, 00, 02, 01, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 30, 00, 00, 03, 19, 08, 00, 1a, 10, 00, 02, be, 02, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, SW1: 90, SW2: 00 +CLA: 80, INS: 11, P1: 40, P2: 00, Lc: 7d, 83, a4, 1a, 10, 00, 00, 02, 03, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, 1a, 90, 00, 02, 59, 44, 54, 65, 73, 74, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 03, 58, 54, 83, 58, 18, a8, 90, 6d, 38, 3a, 25, 67, 08, 85, d3, 07, 17, 3a, 7c, 0e, 81, 96, 46, a0, da, 2c, bd, d2, ea, 58, 31, 04, 95, ce, b7, 75, 21, d8, ae, c9, a2, 99, c0, 00, 6c, 2e, c2, 11, 7d, 79, 52, 56, b7, 3d, c0, c8, e7, 07, 6d, d1, bd, cd, f6, 05, d3, b3, f4, d1, 56, 86, 90, ed, d3, 4f, a2, 85, c2, 3b, ce, 45, 1a, 10, 00, 00, 0a, 01, Le: e6, 83, 00, 58, a5, 85, 58, 18, e7, 0d, ce, 40, c4, 39, a6, 32, 09, 23, 63, 1d, cf, 03, 2c, c3, b4, 84, 8a, 1e, ab, d6, 3b, ab, 4c, 6b, 1d, 1e, 6d, 24, 79, 22, 9a, fe, 81, 37, 7c, 4c, 0f, 4a, 14, 9a, 91, 31, 39, 94, ea, a5, 2d, 14, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a7, 1a, 10, 00, 00, 02, 03, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 30, 00, 00, 03, 19, 01, 00, 1a, 10, 00, 00, 0a, 01, 1a, 10, 00, 02, be, 02, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, 58, 31, 04, 95, ce, b7, 75, 21, d8, ae, c9, a2, 99, c0, 00, 6c, 2e, c2, 11, 7d, 79, 52, 56, b7, 3d, c0, c8, e7, 07, 6d, d1, bd, cd, f6, 05, d3, b3, f4, d1, 56, 86, 90, ed, d3, 4f, a2, 85, c2, 3b, ce, 45, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a7, 1a, 10, 00, 00, 02, 03, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 30, 00, 00, 03, 19, 01, 00, 1a, 10, 00, 00, 0a, 01, 1a, 10, 00, 02, be, 02, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, SW1: 90, SW2: 00 +CLA: 80, INS: 11, P1: 40, P2: 00, Lc: 41, 83, a5, 1a, 10, 00, 00, 02, 18, 20, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, 1a, 90, 00, 02, 59, 44, 54, 65, 73, 74, 1a, 30, 00, 00, 03, 19, 01, 00, 1a, 20, 00, 00, 05, 42, 02, 04, 03, 52, 81, 50, f6, 16, 03, 1c, 26, b5, 6f, 2e, 42, cb, 7c, 37, 96, 1c, 27, ba, Le: 9c, 83, 00, 58, 62, 84, 50, b8, 4e, b2, a0, 85, 11, 94, 9a, e3, f6, 13, fe, f9, a1, b3, dd, 4c, f6, 10, 1c, 18, 2b, 4f, 80, 8d, e3, 76, ed, 92, 4c, 41, ab, d8, 54, 0c, 54, 18, 01, 33, 45, 09, 1e, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a6, 1a, 10, 00, 00, 02, 18, 20, 1a, 30, 00, 00, 03, 19, 01, 00, 1a, 20, 00, 00, 05, 42, 02, 04, 1a, 10, 00, 02, be, 02, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a6, 1a, 10, 00, 00, 02, 18, 20, 1a, 30, 00, 00, 03, 19, 01, 00, 1a, 20, 00, 00, 05, 42, 02, 04, 1a, 10, 00, 02, be, 02, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, SW1: 90, SW2: 00 +CLA: 80, INS: 11, P1: 40, P2: 00, Lc: 47, 83, a6, 1a, 10, 00, 00, 02, 18, 80, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, 1a, 90, 00, 02, 59, 44, 54, 65, 73, 74, 1a, 30, 00, 00, 03, 18, 80, 1a, 20, 00, 00, 05, 42, 02, 04, 1a, 30, 00, 00, 08, 18, 80, 03, 52, 81, 50, a8, 66, a3, e5, ba, 53, e9, 93, 00, e9, 1a, ec, bf, 8f, ec, 90, Le: a8, 83, 00, 58, 68, 84, 50, 63, 2d, d8, 58, c8, 5b, 66, 98, da, de, 51, 5f, e5, c8, 90, 99, 4c, 8a, 7e, 7c, 1e, 4c, ba, 19, e5, 66, d5, 27, 2d, 4c, 3d, ed, 1d, 79, c8, d8, b7, 3b, 93, 1e, fd, ad, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a7, 1a, 10, 00, 00, 02, 18, 80, 1a, 30, 00, 00, 03, 18, 80, 1a, 20, 00, 00, 05, 42, 02, 04, 1a, 30, 00, 00, 08, 18, 80, 1a, 10, 00, 02, be, 02, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a7, 1a, 10, 00, 00, 02, 18, 80, 1a, 30, 00, 00, 03, 18, 80, 1a, 20, 00, 00, 05, 42, 02, 04, 1a, 30, 00, 00, 08, 18, 80, 1a, 10, 00, 02, be, 02, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, SW1: 90, SW2: 00 +CLA: 80, INS: 11, P1: 40, P2: 00, Lc: 40, 83, a5, 1a, 10, 00, 00, 02, 18, 21, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, 1a, 90, 00, 02, 59, 44, 54, 65, 73, 74, 1a, 30, 00, 00, 03, 18, a8, 1a, 20, 00, 00, 05, 42, 02, 04, 03, 52, 81, 50, 80, 5b, 6f, cb, bb, 1b, 07, d3, b6, e1, 67, ab, 51, c6, 34, 29, Le: 9a, 83, 00, 58, 61, 84, 50, c0, ec, 75, 64, dd, a9, 3f, 81, 56, aa, 95, c5, 60, c7, 7f, 5b, 4c, ac, 0a, 0f, 27, 80, f4, 2e, 25, 76, 68, 62, 21, 4c, ae, c2, 77, b3, f7, 43, e6, f5, 19, ac, 77, 12, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a6, 1a, 10, 00, 00, 02, 18, 21, 1a, 30, 00, 00, 03, 18, a8, 1a, 20, 00, 00, 05, 42, 02, 04, 1a, 10, 00, 02, be, 02, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a6, 1a, 10, 00, 00, 02, 18, 21, 1a, 30, 00, 00, 03, 18, a8, 1a, 20, 00, 00, 05, 42, 02, 04, 1a, 10, 00, 02, be, 02, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, SW1: 90, SW2: 00 From 1c4ee23cd2d232df61d4af5202055f2fcab412e6 Mon Sep 17 00:00:00 2001 From: cpathak Date: Mon, 8 Jun 2020 21:18:40 -0700 Subject: [PATCH 4/6] Changes with crypto provider Code changes to crypto provider. New feature implemented but not tested. --- .../javacard/keymaster/KMCipherImpl.java | 31 + .../keymaster/KMCryptoProviderImpl.java | 8 + .../javacard/keymaster/KMJcardSimulator.java | 672 +++++ .../javacard/keymaster/KMCipherImpl.java | 31 + .../keymaster/KMCryptoProviderImpl.java | 8 + .../javacard/keymaster/KMSimulator.java | 81 + .../android/javacard/keymaster/KMArray.java | 26 +- .../android/javacard/keymaster/KMAuthTag.java | 7 + .../android/javacard/keymaster/KMBoolTag.java | 4 +- .../javacard/keymaster/KMByteBlob.java | 12 +- .../android/javacard/keymaster/KMCipher.java | 30 + .../javacard/keymaster/KMCryptoProvider.java | 34 +- .../android/javacard/keymaster/KMDecoder.java | 8 +- .../android/javacard/keymaster/KMEnum.java | 6 +- .../javacard/keymaster/KMEnumArrayTag.java | 46 + .../keymaster/KMHardwareAuthToken.java | 5 +- .../android/javacard/keymaster/KMInteger.java | 20 + .../javacard/keymaster/KMIntegerArrayTag.java | 19 +- .../javacard/keymaster/KMKeymasterApplet.java | 2196 ++++++++++++++--- .../javacard/keymaster/KMOperationState.java | 132 +- .../javacard/keymaster/KMRepository.java | 247 +- .../android/javacard/keymaster/KMType.java | 21 +- .../android/javacard/keymaster/KMUtil.java | 109 - .../keymaster/KMVerificationToken.java | 11 +- .../javacard/test/KMFrameworkTest.java | 142 +- .../com/android/javacard/test/KMVTSTest.java | 646 +++++ Applet/JavaCardKeymaster.scr | 22 +- Applet/build.xml | 29 +- Applet/default.output | 22 +- 29 files changed, 3914 insertions(+), 711 deletions(-) create mode 100644 Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMCipherImpl.java create mode 100644 Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMCryptoProviderImpl.java create mode 100644 Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMJcardSimulator.java create mode 100644 Applet/Applet/OracleSimProvider/com/android/javacard/keymaster/KMCipherImpl.java create mode 100644 Applet/Applet/OracleSimProvider/com/android/javacard/keymaster/KMCryptoProviderImpl.java rename Applet/Applet/{src => OracleSimProvider}/com/android/javacard/keymaster/KMSimulator.java (88%) create mode 100644 Applet/Applet/src/com/android/javacard/keymaster/KMAuthTag.java create mode 100644 Applet/Applet/src/com/android/javacard/keymaster/KMCipher.java delete mode 100644 Applet/Applet/src/com/android/javacard/keymaster/KMUtil.java create mode 100644 Applet/Applet/test/com/android/javacard/test/KMVTSTest.java diff --git a/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMCipherImpl.java b/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMCipherImpl.java new file mode 100644 index 00000000..66d988d4 --- /dev/null +++ b/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMCipherImpl.java @@ -0,0 +1,31 @@ +package com.android.javacard.keymaster; + +import com.android.javacard.keymaster.KMCipher; +import javacardx.crypto.Cipher; + +public class KMCipherImpl extends KMCipher{ + Cipher cipher; + KMCipherImpl(Cipher c){ + cipher = c; + } + + @Override + public short doFinal(byte[] buffer, short startOff, short length, byte[] scratchPad, short i) { + return cipher.doFinal(buffer, startOff, length, scratchPad, i); + } + + @Override + public short getCipherAlgorithm() { + return cipher.getCipherAlgorithm(); + } + + @Override + public short update(byte[] buffer, short startOff, short length, byte[] scratchPad, short i) { + return cipher.update(buffer,startOff,length,scratchPad,i); + } + + @Override + public short getPaddingAlgorithm() { + return cipher.getPaddingAlgorithm(); + } +} diff --git a/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMCryptoProviderImpl.java b/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMCryptoProviderImpl.java new file mode 100644 index 00000000..917b4911 --- /dev/null +++ b/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMCryptoProviderImpl.java @@ -0,0 +1,8 @@ + +package com.android.javacard.keymaster; + +public class KMCryptoProviderImpl { + public static KMCryptoProvider instance(){ + return new KMJcardSimulator(); + } +} diff --git a/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMJcardSimulator.java b/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMJcardSimulator.java new file mode 100644 index 00000000..2e12de18 --- /dev/null +++ b/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMJcardSimulator.java @@ -0,0 +1,672 @@ +/* + * Copyright(C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.javacard.keymaster; + +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import javacard.framework.ISO7816; +import javacard.framework.ISOException; +import javacard.framework.JCSystem; +import javacard.framework.Util; +import javacard.security.AESKey; +import javacard.security.CryptoException; +import javacard.security.DESKey; +import javacard.security.ECPrivateKey; +import javacard.security.HMACKey; +import javacard.security.Key; +import javacard.security.KeyBuilder; +import javacard.security.KeyPair; +import javacard.security.RSAPrivateKey; +import javacard.security.RandomData; +import javacard.security.Signature; +import javacardx.crypto.Cipher; +import javax.crypto.AEADBadTagException; +import javax.crypto.BadPaddingException; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.ShortBufferException; +import javax.crypto.spec.GCMParameterSpec; +import javax.crypto.spec.SecretKeySpec; + +/** + * Simulator only supports 512 bit RSA key pair, 128 AES Key, 128 bit 3Des key, less then 256 bit EC + * Key, and upto 512 bit HMAC key. Also simulator does not support TRNG, so this implementation just + * creates its own RNG using PRNG. + */ +public class KMJcardSimulator implements KMCryptoProvider { + public static final short AES_GCM_TAG_LENGTH = 12; + public static final short AES_GCM_NONCE_LENGTH = 12; + public static final short MAX_RND_NUM_SIZE = 64; + public static final short ENTROPY_POOL_SIZE = 16; // simulator does not support 256 bit aes keys + public static final byte[] aesICV = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + private static final int AES_GCM_KEY_SIZE = 16; + public static boolean jcardSim = false; + private static Signature kdf; + private static Signature hmacSignature; + + private static byte[] rngCounter; + private static AESKey aesRngKey; + private static Cipher aesRngCipher; + private static byte[] entropyPool; + private static byte[] rndNum; + + // Implements Oracle Simulator based restricted crypto provider + public KMJcardSimulator() { + // Various Keys + kdf = Signature.getInstance(Signature.ALG_AES_CMAC_128, false); + hmacSignature = Signature.getInstance(Signature.ALG_HMAC_SHA_256, false); + // RNG + rndNum = JCSystem.makeTransientByteArray(MAX_RND_NUM_SIZE, JCSystem.CLEAR_ON_RESET); + entropyPool = JCSystem.makeTransientByteArray(ENTROPY_POOL_SIZE, JCSystem.CLEAR_ON_RESET); + rngCounter = JCSystem.makeTransientByteArray((short) 8, JCSystem.CLEAR_ON_RESET); + initEntropyPool(entropyPool); + try { + aesRngCipher = Cipher.getInstance(Cipher.ALG_AES_BLOCK_128_CBC_NOPAD, false); + } catch (CryptoException exp) { + ISOException.throwIt(ISO7816.SW_COMMAND_NOT_ALLOWED); + } + aesRngKey = (AESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_AES, KeyBuilder.LENGTH_AES_128, false); + // various ciphers + + } + + @Override + public KeyPair createRsaKeyPair() { + KeyPair rsaKeyPair = new KeyPair(KeyPair.ALG_RSA, KeyBuilder.LENGTH_RSA_2048); + rsaKeyPair.genKeyPair(); + return rsaKeyPair; + } + + @Override + public RSAPrivateKey createRsaKey(byte[] modBuffer, short modOff, short modLength, + byte[] privBuffer, short privOff, short privLength) { + KeyPair rsaKeyPair = new KeyPair(KeyPair.ALG_RSA, KeyBuilder.LENGTH_RSA_2048); + RSAPrivateKey privKey = (RSAPrivateKey) rsaKeyPair.getPrivate(); + privKey.setExponent(privBuffer, privOff, privLength); + privKey.setModulus(modBuffer, modOff, modLength); + return privKey; + + } + + @Override + public KeyPair createECKeyPair() { + KeyPair ecKeyPair = new KeyPair(KeyPair.ALG_EC_FP, KeyBuilder.LENGTH_EC_FP_256); + ecKeyPair.genKeyPair(); + return ecKeyPair; + } + + @Override + public ECPrivateKey createEcKey(byte[] privBuffer, short privOff, short privLength) { + KeyPair ecKeyPair = new KeyPair(KeyPair.ALG_EC_FP, KeyBuilder.LENGTH_EC_FP_256); + ECPrivateKey privKey = (ECPrivateKey) ecKeyPair.getPrivate(); + if(privLength > 24){ + privLength = 24;// simulator does not support more then 24 bytes - 192 bit key. + }else if(privLength <= 20){ + return null; + } + privKey.setS(privBuffer,privOff, privLength); + return privKey; + } + + @Override + public AESKey createAESKey(short keysize) { + byte[] rndNum = new byte[(short) (keysize/8)]; + return createAESKey(rndNum, (short)0, (short)rndNum.length); + } + + @Override + public AESKey createAESKey(byte[] buf, short startOff, short length) { + AESKey key = null; + short keysize = (short)(length * 8); + if (keysize == 128) { + key = (AESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_AES, KeyBuilder.LENGTH_AES_128, false); + key.setKey(buf, (short) startOff); + }else if (keysize == 256){ + key = (AESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_AES, KeyBuilder.LENGTH_AES_256, false); + key.setKey(buf, (short) startOff); + } + // byte[] buffer = new byte[length]; + // Util.arrayCopyNonAtomic(buf, startOff, buffer, (short)0,length); + // print("AES Key", buffer); + return key; + } + + @Override + public DESKey createTDESKey() { + // TODO check whether 168 bit or 192 bit + byte[] rndNum = new byte[24]; + newRandomNumber(rndNum, (short) 0, (short)rndNum.length); + return createTDESKey(rndNum, (short)0, (short)rndNum.length); + } + + @Override + public DESKey createTDESKey(byte[] secretBuffer, short secretOff, short secretLength) { + DESKey triDesKey = + (DESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_DES, KeyBuilder.LENGTH_DES3_3KEY, false); + triDesKey.setKey(secretBuffer, secretOff); + return triDesKey; + } + + @Override + public HMACKey createHMACKey(short keysize) { + if((keysize % 8 != 0) || !(keysize >= 64 && keysize <= 512)){ + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + } + byte[] rndNum = new byte[(short) (keysize/8)]; + newRandomNumber(rndNum, (short) 0, (short)(keysize/8)); + return createHMACKey(rndNum, (short)0, (short)rndNum.length); + } + + @Override + public HMACKey createHMACKey(byte[] secretBuffer, short secretOff, short secretLength) { + HMACKey key = null; + key = (HMACKey) KeyBuilder.buildKey(KeyBuilder.TYPE_HMAC, + KeyBuilder.LENGTH_HMAC_SHA_256_BLOCK_64, false); + key.setKey(secretBuffer,secretOff,secretLength); + return key; + } + + @Override + public short aesGCMEncrypt( + AESKey key, + byte[] secret, + short secretStart, + short secretLen, + byte[] encSecret, + short encSecretStart, + byte[] nonce, + short nonceStart, + short nonceLen, + byte[] authData, + short authDataStart, + short authDataLen, + byte[] authTag, + short authTagStart, + short authTagLen) { + //Create the sun jce compliant aes key + if(key.getSize() != 128){ + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + } + byte[] keyMaterial = new byte[16]; + key.getKey(keyMaterial,(short)0); + // print("KeyMaterial", keyMaterial); + java.security.Key aesKey = new SecretKeySpec(keyMaterial,(short)0,(short)16, "AES"); + // Create the cipher + javax.crypto.Cipher cipher = null; + try { + cipher = javax.crypto.Cipher.getInstance("AES/GCM/NoPadding", "SunJCE"); + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); + } catch (NoSuchProviderException e) { + e.printStackTrace(); + CryptoException.throwIt(CryptoException.INVALID_INIT); + } catch (NoSuchPaddingException e) { + e.printStackTrace(); + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + } + // Copy nonce + if(nonceLen != AES_GCM_NONCE_LENGTH){ + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + } + byte[] iv = new byte[AES_GCM_NONCE_LENGTH]; + Util.arrayCopyNonAtomic(nonce,nonceStart,iv,(short)0,AES_GCM_NONCE_LENGTH); + // Init Cipher + GCMParameterSpec spec = new GCMParameterSpec(AES_GCM_TAG_LENGTH * 8, nonce,nonceStart,AES_GCM_NONCE_LENGTH); + try { + cipher.init(javax.crypto.Cipher.ENCRYPT_MODE, aesKey, spec); + } catch (InvalidKeyException e) { + e.printStackTrace(); + CryptoException.throwIt(CryptoException.INVALID_INIT); + } catch (InvalidAlgorithmParameterException e) { + e.printStackTrace(); + CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); + } + // Create auth data + byte[] aad = new byte[authDataLen]; + Util.arrayCopyNonAtomic(authData,authDataStart,aad,(short)0,authDataLen); + // print("AAD", aad); + cipher.updateAAD(aad); + // Encrypt secret + short len = 0; + byte[] outputBuf = new byte[cipher.getOutputSize(secretLen)]; + try { + len = (short)(cipher.doFinal(secret,secretStart,secretLen,outputBuf,(short)0)); + } catch (ShortBufferException e) { + e.printStackTrace(); + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + } catch (IllegalBlockSizeException e) { + e.printStackTrace(); + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + } catch (BadPaddingException e) { + e.printStackTrace(); + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + } + // Extract Tag appended at the end. + Util.arrayCopyNonAtomic(outputBuf, (short)(len - AES_GCM_TAG_LENGTH),authTag,authTagStart,AES_GCM_TAG_LENGTH); + //Copy the encrypted data + Util.arrayCopyNonAtomic(outputBuf, (short)0,encSecret,encSecretStart,(short)(len - AES_GCM_TAG_LENGTH)); + return (short)(len - AES_GCM_TAG_LENGTH); + } + +/* + // Decrypt; nonce is shared implicitly + cipher.init(Cipher.DECRYPT_MODE, key, spec); + + // EXPECTED: Uncommenting this will cause an AEADBadTagException when decrypting + // because AAD value is altered + if (testNum == 1) aad[1]++; + + cipher.updateAAD(aad); + + // EXPECTED: Uncommenting this will cause an AEADBadTagException when decrypting + // because the encrypted data has been altered + if (testNum == 2) cipherText[10]++; + + // EXPECTED: Uncommenting this will cause an AEADBadTagException when decrypting + // because the tag has been altered + if (testNum == 3) cipherText[cipherText.length - 2]++; + + try { + byte[] plainText = cipher.doFinal(cipherText); + if (testNum != 0) { + System.out.println("Test Failed: expected AEADBadTagException not thrown"); + } else { + // check if the decryption result matches + if (Arrays.equals(input, plainText)) { + System.out.println("Test Passed: match!"); + } else { + System.out.println("Test Failed: result mismatch!"); + System.out.println(new String(plainText)); + } + } + } catch(AEADBadTagException ex) { + if (testNum == 0) { + System.out.println("Test Failed: unexpected ex " + ex); + ex.printStackTrace(); + } else { + System.out.println("Test Passed: expected ex " + ex); + } + } + } + }*/ + + public boolean aesGCMDecrypt( + AESKey key, + byte[] encSecret, + short encSecretStart, + short encSecretLen, + byte[] secret, + short secretStart, + byte[] nonce, + short nonceStart, + short nonceLen, + byte[] authData, + short authDataStart, + short authDataLen, + byte[] authTag, + short authTagStart, + short authTagLen) { + //Create the sun jce compliant aes key + if(key.getSize() != 128){ + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + } + byte[] keyMaterial = new byte[16]; + key.getKey(keyMaterial,(short)0); + java.security.Key aesKey = new SecretKeySpec(keyMaterial,(short)0,(short)16, "AES"); + // Create the cipher + javax.crypto.Cipher cipher = null; + try { + cipher = javax.crypto.Cipher.getInstance("AES/GCM/NoPadding", "SunJCE"); + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); + } catch (NoSuchProviderException e) { + e.printStackTrace(); + CryptoException.throwIt(CryptoException.INVALID_INIT); + } catch (NoSuchPaddingException e) { + e.printStackTrace(); + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + } + // Copy nonce + if(nonceLen != AES_GCM_NONCE_LENGTH){ + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + } + byte[] iv = new byte[AES_GCM_NONCE_LENGTH]; + Util.arrayCopyNonAtomic(nonce,nonceStart,iv,(short)0,AES_GCM_NONCE_LENGTH); + // Init Cipher + GCMParameterSpec spec = new GCMParameterSpec(AES_GCM_TAG_LENGTH * 8, nonce,nonceStart,AES_GCM_NONCE_LENGTH); + try { + cipher.init(javax.crypto.Cipher.DECRYPT_MODE, aesKey, spec); + } catch (InvalidKeyException e) { + e.printStackTrace(); + CryptoException.throwIt(CryptoException.INVALID_INIT); + } catch (InvalidAlgorithmParameterException e) { + e.printStackTrace(); + CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); + } + // Create auth data + byte[] aad = new byte[authDataLen]; + Util.arrayCopyNonAtomic(authData,authDataStart,aad,(short)0,authDataLen); + cipher.updateAAD(aad); + // Append the auth tag at the end of data + byte[] inputBuf = new byte[(short)(encSecretLen + AES_GCM_TAG_LENGTH)]; + Util.arrayCopyNonAtomic(encSecret,encSecretStart,inputBuf,(short)0,encSecretLen); + Util.arrayCopyNonAtomic(authTag,authTagStart,inputBuf,encSecretLen,AES_GCM_TAG_LENGTH); + // Decrypt + short len = 0; + byte[] outputBuf = new byte[cipher.getOutputSize((short)inputBuf.length)]; + try { + len = (short)(cipher.doFinal(inputBuf,(short)0,(short)inputBuf.length,outputBuf,(short)0)); + }catch(AEADBadTagException e){ + e.printStackTrace(); + return false; + }catch (ShortBufferException e) { + e.printStackTrace(); + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + } catch (IllegalBlockSizeException e) { + e.printStackTrace(); + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + } catch (BadPaddingException e) { + e.printStackTrace(); + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + } + //Copy the decrypted data + Util.arrayCopyNonAtomic(outputBuf, (short)0,secret,secretStart,len); + return true; + } + + @Override + public byte[] getTrueRandomNumber(short i) { + // ignore the size as simulator only supports 128 bit entropy + return entropyPool; + } + + @Override + public short aesCCMSign( + byte[] bufIn, + short bufInStart, + short buffInLength, + byte[] masterKeySecret, + byte[] bufOut, + short bufStart) { + if (masterKeySecret.length > 16) { + return -1; + } + + AESKey key = (AESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_AES, KeyBuilder.LENGTH_AES_128, false); + key.setKey(masterKeySecret, (short) 0); + byte[] in = new byte[buffInLength]; + Util.arrayCopyNonAtomic(bufIn, bufInStart,in,(short)0,buffInLength); + kdf.init(key, Signature.MODE_SIGN); + short len = kdf.sign(bufIn, bufInStart, buffInLength, bufOut, bufStart); + byte[] out = new byte[len]; + Util.arrayCopyNonAtomic(bufOut, bufStart,out,(short)0,len); + return len; + } + + + @Override + public HMACKey cmacKdf(byte[] keyMaterial, byte[] label, byte[] context, short contextStart, short contextLength) { + return null; + } + + @Override + public short hmacSign(HMACKey key, byte[] data, short dataStart, short dataLength, byte[] mac, short macStart) { + hmacSignature.init(key, Signature.MODE_SIGN); + return hmacSignature.sign(data, dataStart, dataLength, mac, macStart); + } + + @Override + public boolean hmacVerify(HMACKey key, byte[] data, short dataStart, short dataLength, + byte[] mac, short macStart, short macLength) { + hmacSignature.init(key, Signature.MODE_VERIFY); + return hmacSignature.verify(data, dataStart, dataLength, mac, macStart, macLength); + } + + @Override + public KMCipher createRsaDecrypt(short cipherAlg, short padding, byte[] secret, short secretStart, + short secretLength, byte[] modBuffer, short modOff, short modLength) { + Cipher rsaCipher = Cipher.getInstance((byte)cipherAlg, + (byte)padding,false); + RSAPrivateKey key = (RSAPrivateKey) KeyBuilder.buildKey(KeyBuilder.TYPE_RSA_PRIVATE, KeyBuilder.LENGTH_RSA_2048, false); + key.setExponent(secret,secretStart,secretLength); + key.setModulus(modBuffer, modOff, modLength); + rsaCipher.init(key,Cipher.MODE_DECRYPT); + return new KMCipherImpl(rsaCipher); + } + + @Override + public Signature createRsaSigner(short msgDigestAlg, short padding, byte[] secret, short secretStart, short secretLength, byte[] modBuffer, short modOff, short modLength) { + Signature rsaSigner = Signature.getInstance((byte)msgDigestAlg, Signature.SIG_CIPHER_RSA,(byte)padding,false); + RSAPrivateKey key = (RSAPrivateKey) KeyBuilder.buildKey(KeyBuilder.TYPE_RSA_PRIVATE, KeyBuilder.LENGTH_RSA_2048, false); + key.setExponent(secret,secretStart,secretLength); + key.setModulus(modBuffer, modOff, modLength); + rsaSigner.init(key,Signature.MODE_SIGN); + return rsaSigner; + } + + @Override + public Signature createEcSigner(short msgDigestAlg, byte[] secret, short secretStart, short secretLength) { + Signature ecSigner = Signature.getInstance((byte)msgDigestAlg, Signature.SIG_CIPHER_ECDSA,Cipher.PAD_NOPAD,false); + ECPrivateKey key = (ECPrivateKey) KeyBuilder.buildKey(KeyBuilder.TYPE_EC_FP_PRIVATE, KeyBuilder.LENGTH_EC_FP_256, false); + key.setS(secret,secretStart,secretLength); + ecSigner.init(key,Signature.MODE_SIGN); + return ecSigner; + } + + @Override + public KMCipher createSymmetricCipher(short cipherAlg, short padding, short mode, byte[] secret, + short secretStart, short secretLength, + byte[] ivBuffer, short ivStart, short ivLength) { + Key key = null; + short len = 0; + if(cipherAlg == Cipher.CIPHER_AES_CBC || cipherAlg == Cipher.CIPHER_AES_CBC){ + if(secretLength == 32){ + len = KeyBuilder.LENGTH_AES_256; + }else if(secretLength == 16){ + len = KeyBuilder.LENGTH_AES_128; + }else{ + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + } + + //TODO + }else if(secretLength != 21){ // DES Key + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + }else{ //DES Key + len = KeyBuilder.LENGTH_DES3_3KEY; + } + switch(cipherAlg){ + case Cipher.CIPHER_AES_CBC: + case Cipher.CIPHER_AES_ECB: + key = KeyBuilder.buildKey(KeyBuilder.TYPE_AES,len,false); + ((AESKey) key).setKey(secret,secretStart); + break; + case Cipher.CIPHER_DES_CBC: + case Cipher.CIPHER_DES_ECB: + key = (DESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_DES,len,false); + ((DESKey) key).setKey(secret,secretStart); + break; + default://This should never happen + CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); + break; + } + + //TODO + Cipher symmCipher = Cipher.getInstance((byte)cipherAlg, Cipher.PAD_NOPAD,false); + if (ivBuffer != null) { + symmCipher.init(key, (byte) mode, ivBuffer, ivStart, ivLength); + }else{ + symmCipher.init(key, (byte) mode); + } + return new KMCipherImpl(symmCipher); + } + + @Override + public Signature createHmacSigner(short msgDigestAlg, byte[] secret, short secretStart, short secretLength) { + Signature hmacSigner = Signature.getInstance((byte)msgDigestAlg, Signature.SIG_CIPHER_HMAC,Cipher.PAD_NOPAD,false); + HMACKey key = (HMACKey) KeyBuilder.buildKey(KeyBuilder.TYPE_HMAC, (short)(secretLength*8), false); + key.setKey(secret,secretStart,secretLength); + hmacSigner.init(key,Signature.MODE_SIGN); + return hmacSigner; + } + + @Override + public KMCipher createGCMCipher(short mode, byte[] secret, short secretStart, short secretLength, byte[] ivBuffer, short ivStart, short ivLength) { + //TODO + short len = KeyBuilder.LENGTH_AES_128; + if(secretLength == 32){ + len = KeyBuilder.LENGTH_AES_256; + } + return new KMCipherImpl(null); + } + + @Override + public void delete(KMCipher cipher) { + //Don't do anything as we don't pool the objects. + } + + @Override + public void delete(Signature signature) { + //Don't do anything as we don't pool the objects. + } + + @Override + public void delete(Key key) { + // Don't do anything as we don't pool the objects. + } + + @Override + public void delete(KeyPair keyPair) { + // Don't do anything as we don't pool the objects. + } + + private void initEntropyPool(byte[] pool) { + byte index = 0; + RandomData trng; + while (index < rngCounter.length) { + rngCounter[index++] = 0; + } + try { + trng = RandomData.getInstance(RandomData.ALG_TRNG); + trng.nextBytes(pool, (short) 0, (short) pool.length); + } catch (CryptoException exp) { + if (exp.getReason() == CryptoException.NO_SUCH_ALGORITHM) { + // TODO change this when possible + // simulator does not support TRNG algorithm. So, PRNG algorithm (deprecated) is used. + trng = RandomData.getInstance(RandomData.ALG_PSEUDO_RANDOM); + trng.nextBytes(pool, (short) 0, (short) pool.length); + } else { + // TODO change this to proper error code + ISOException.throwIt(ISO7816.SW_UNKNOWN); + } + } + } + + // Generate a secure random number from existing entropy pool. This uses aes ecb algorithm with + // 8 byte rngCounter and 16 byte block size. + @Override + public void newRandomNumber(byte[] num, short startOff, short length) { + KMRepository repository = KMRepository.instance(); + byte[] bufPtr = repository.getHeap(); + short countBufInd = repository.alloc(KMKeymasterApplet.AES_BLOCK_SIZE); + short randBufInd = repository.alloc(KMKeymasterApplet.AES_BLOCK_SIZE); + short len = KMKeymasterApplet.AES_BLOCK_SIZE; + aesRngKey.setKey(entropyPool, (short) 0); + aesRngCipher.init(aesRngKey, Cipher.MODE_ENCRYPT, aesICV, (short) 0, (short) 16); + while (length > 0) { + if (length < len) len = length; + // increment rngCounter by one + incrementCounter(); + // copy the 8 byte rngCounter into the 16 byte rngCounter buffer. + Util.arrayCopy(rngCounter, (short) 0, bufPtr, countBufInd, (short) rngCounter.length); + // encrypt the rngCounter buffer with existing entropy which forms the aes key. + aesRngCipher.doFinal( + bufPtr, countBufInd, KMKeymasterApplet.AES_BLOCK_SIZE, bufPtr, randBufInd); + // copy the encrypted rngCounter block to buffer passed in the argument + Util.arrayCopy(bufPtr, randBufInd, num, startOff, len); + length = (short) (length - len); + startOff = (short) (startOff + len); + } + } + + // increment 8 byte rngCounter by one + private void incrementCounter() { + // start with least significant byte + short index = (short) (rngCounter.length - 1); + while (index >= 0) { + // if the msb of current byte is set then it will be negative + if (rngCounter[index] < 0) { + // then increment the rngCounter + rngCounter[index]++; + // is the msb still set? i.e. no carry over + if (rngCounter[index] < 0) break; // then break + else index--; // else go to the higher order byte + } else { + // if msb is not set then increment the rngCounter + rngCounter[index]++; + break; + } + } + } + + @Override + public void addRngEntropy(byte[] num, short offset, short length) { + // Maximum length can be 256 bytes. But currently we support max 32 bytes seed. + // Get existing entropy pool. + if (length > 32) length = 32; + // Create new temporary pool. + // Populate the new pool with the entropy which is derived from current entropy pool. + newRandomNumber(rndNum, (short) 0, (short) entropyPool.length); + // Copy the entropy to the current pool - updates the entropy pool. + Util.arrayCopy(rndNum, (short) 0, entropyPool, (short) 0, (short) entropyPool.length); + short index = 0; + short randIndex = 0; + // XOR the seed received from the master in the entropy pool - 16 bytes (entPool.length). + // at a time. + while (index < length) { + entropyPool[randIndex] = (byte) (entropyPool[randIndex] ^ num[(short) (offset + index)]); + randIndex++; + index++; + if (randIndex >= entropyPool.length) { + randIndex = 0; + } + } + } + private void print (String lab, byte[] b, short s, short l){ + byte[] i = new byte[l]; + Util.arrayCopyNonAtomic(b,s,i,(short)0,l); + print(lab,i); + } + private void print(String label, byte[] buf){ + System.out.println(label+": "); + StringBuilder sb = new StringBuilder(); + for(int i = 0; i < buf.length; i++){ + sb.append(String.format(" 0x%02X", buf[i])) ; + if(((i-1)%38 == 0) && ((i-1) >0)){ + sb.append(";\n"); + } + } + System.out.println(sb.toString()); + } + @Override + public void bypassAesGcm(){ + //ignore + } +} diff --git a/Applet/Applet/OracleSimProvider/com/android/javacard/keymaster/KMCipherImpl.java b/Applet/Applet/OracleSimProvider/com/android/javacard/keymaster/KMCipherImpl.java new file mode 100644 index 00000000..66d988d4 --- /dev/null +++ b/Applet/Applet/OracleSimProvider/com/android/javacard/keymaster/KMCipherImpl.java @@ -0,0 +1,31 @@ +package com.android.javacard.keymaster; + +import com.android.javacard.keymaster.KMCipher; +import javacardx.crypto.Cipher; + +public class KMCipherImpl extends KMCipher{ + Cipher cipher; + KMCipherImpl(Cipher c){ + cipher = c; + } + + @Override + public short doFinal(byte[] buffer, short startOff, short length, byte[] scratchPad, short i) { + return cipher.doFinal(buffer, startOff, length, scratchPad, i); + } + + @Override + public short getCipherAlgorithm() { + return cipher.getCipherAlgorithm(); + } + + @Override + public short update(byte[] buffer, short startOff, short length, byte[] scratchPad, short i) { + return cipher.update(buffer,startOff,length,scratchPad,i); + } + + @Override + public short getPaddingAlgorithm() { + return cipher.getPaddingAlgorithm(); + } +} diff --git a/Applet/Applet/OracleSimProvider/com/android/javacard/keymaster/KMCryptoProviderImpl.java b/Applet/Applet/OracleSimProvider/com/android/javacard/keymaster/KMCryptoProviderImpl.java new file mode 100644 index 00000000..182dda4e --- /dev/null +++ b/Applet/Applet/OracleSimProvider/com/android/javacard/keymaster/KMCryptoProviderImpl.java @@ -0,0 +1,8 @@ + +package com.android.javacard.keymaster; + +public class KMCryptoProviderImpl { + public static KMCryptoProvider instance(){ + return new KMSimulator(); + } +} diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMSimulator.java b/Applet/Applet/OracleSimProvider/com/android/javacard/keymaster/KMSimulator.java similarity index 88% rename from Applet/Applet/src/com/android/javacard/keymaster/KMSimulator.java rename to Applet/Applet/OracleSimProvider/com/android/javacard/keymaster/KMSimulator.java index 0d0c1394..51c87142 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMSimulator.java +++ b/Applet/Applet/OracleSimProvider/com/android/javacard/keymaster/KMSimulator.java @@ -26,6 +26,7 @@ import javacard.security.ECPrivateKey; import javacard.security.ECPublicKey; import javacard.security.HMACKey; +import javacard.security.Key; import javacard.security.KeyBuilder; import javacard.security.KeyPair; import javacard.security.RSAPrivateKey; @@ -295,6 +296,7 @@ public short aesCCMSign( byte[] bufOut, short bufStart) { if (masterKeySecret.length > 16) { + return -1; } aes128Key.setKey(masterKeySecret, (short) 0); @@ -303,6 +305,10 @@ public short aesCCMSign( } @Override + public ECPrivateKey createEcKey(byte[] privBuffer, short privOff, short privLength) { + return null; + } + public ECPrivateKey createEcPrivateKey(byte[] pubBuffer, short pubOff, short pubLength, byte[] privBuffer, short privOff, short privLength) { // Simulator does not support NamedParameterSpec or 256 bit keys @@ -342,6 +348,77 @@ public DESKey createTDESKey(byte[] secretBuffer, short secretOff, short secretLe } @Override + public RSAPrivateKey createRsaKey(byte[] modBuffer, short modOff, short modLength, byte[] privBuffer, short privOff, short privLength) { + return null; + } + + @Override + public HMACKey cmacKdf(byte[] keyMaterial, byte[] label, byte[] context, short contextStart, short contextLength) { + return null; + } + + @Override + public short hmacSign(HMACKey key, byte[] data, short dataStart, short dataLength, byte[] mac, short macStart) { + return 0; + } + + @Override + public boolean hmacVerify(HMACKey key, byte[] data, short dataStart, short dataLength, byte[] mac, short macStart, short macLength) { + return false; + } + + + @Override + public KMCipher createRsaDecrypt(short cipherAlg, short padding, byte[] secret, short secretStart, short secretLength, byte[] modBuffer, short modOff, short modLength) { + return null; + } + + @Override + public Signature createRsaSigner(short msgDigestAlg, short padding, byte[] secret, short secretStart, short secretLength, byte[] modBuffer, short modOff, short modLength) { + return null; + } + + @Override + public Signature createEcSigner(short msgDigestAlg, byte[] secret, short secretStart, short secretLength) { + return null; + } + + @Override + public KMCipher createSymmetricCipher(short cipherAlg, short padding, short mode, byte[] secret, short secretStart, short secretLength, byte[] ivBuffer, short ivStart, short ivLength) { + return null; + } + + @Override + public Signature createHmacSigner(short msgDigestAlg, byte[] secret, short secretStart, short secretLength) { + return null; + } + + @Override + public KMCipher createGCMCipher(short mode, byte[] secret, short secretStart, short secretLength, byte[] ivBuffer, short ivStart, short ivLength) { + return null; + } + + @Override + public void delete(KMCipher cipher) { + + } + + + @Override + public void delete(Signature signature) { + + } + + @Override + public void delete(Key key) { + + } + + @Override + public void delete(KeyPair keyPair) { + + } + public RSAPrivateKey createRsaPrivateKey(byte[] modBuffer, short modOff, short modLength, byte[] privBuffer, short privOff, short privLength) { RSAPrivateKey privKey = (RSAPrivateKey) rsa512KeyPair.getPrivate(); if(privLength > 64) privLength = 64; @@ -421,4 +498,8 @@ private void incrementCounter() { } } } + @Override + public void bypassAesGcm(){ + jcardSim = true; + } } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMArray.java b/Applet/Applet/src/com/android/javacard/keymaster/KMArray.java index 2935aa2b..4646f46e 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMArray.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMArray.java @@ -21,7 +21,9 @@ import javacard.framework.Util; public class KMArray extends KMType { - private static final short ARRAY_HEADER_SIZE = 3; + public static final short ANY_ARRAY_LENGTH = 0x1000; + // short Type + short Length + private static final short ARRAY_HEADER_SIZE = 4; private static KMArray prototype; private static short instPtr; @@ -35,28 +37,28 @@ private static KMArray proto(short ptr) { public static short exp() { short ptr = instance(ARRAY_TYPE, ARRAY_HEADER_SIZE); - heap[(short)(ptr + TLV_HEADER_SIZE)] = 0; - Util.setShort(heap,(short)(ptr + TLV_HEADER_SIZE + 1),(short)0 ); + Util.setShort(heap,(short)(ptr + TLV_HEADER_SIZE),(short)0 ); + Util.setShort(heap,(short)(ptr + TLV_HEADER_SIZE + 2),ANY_ARRAY_LENGTH ); return ptr; } - public static short exp(byte type) { + public static short exp(short type) { short ptr = instance(ARRAY_TYPE, ARRAY_HEADER_SIZE); - heap[(short)(ptr + TLV_HEADER_SIZE)] = type; - Util.setShort(heap,(short)(ptr + TLV_HEADER_SIZE + 1),(short)0 ); + Util.setShort(heap,(short)(ptr + TLV_HEADER_SIZE),type); + Util.setShort(heap,(short)(ptr + TLV_HEADER_SIZE + 2),ANY_ARRAY_LENGTH ); return ptr; } public static short instance(short length) { short ptr = KMType.instance(ARRAY_TYPE, (short)(ARRAY_HEADER_SIZE + (length*2))); - heap[(short)(ptr + TLV_HEADER_SIZE)] = 0; - Util.setShort(heap,(short)(ptr + TLV_HEADER_SIZE + 1),length); + Util.setShort(heap,(short)(ptr + TLV_HEADER_SIZE),(short)0); + Util.setShort(heap,(short)(ptr + TLV_HEADER_SIZE + 2),length); return ptr; } public static short instance(short length, byte type) { short ptr = instance(length); - heap[(short)(ptr + TLV_HEADER_SIZE)] = type; + Util.setShort(heap,(short)(ptr + TLV_HEADER_SIZE),type); return ptr; } @@ -74,17 +76,17 @@ public void add(short index, short objPtr) { public short get(short index) { short len = length(); if (index >= len) ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); - return Util.getShort(heap,(short) (instPtr + TLV_HEADER_SIZE + 3 + (short)(index*2))); + return Util.getShort(heap,(short) (instPtr + TLV_HEADER_SIZE + ARRAY_HEADER_SIZE + (short)(index*2))); } - public byte containedType(){ return heap[(short)(instPtr + TLV_HEADER_SIZE)];} + public short containedType(){ return Util.getShort(heap, (short)(instPtr + TLV_HEADER_SIZE));} public short getStartOff() { return (short) (instPtr + TLV_HEADER_SIZE + ARRAY_HEADER_SIZE); } public short length() { - return Util.getShort(heap, (short) (instPtr + TLV_HEADER_SIZE + 1)); + return Util.getShort(heap, (short) (instPtr + TLV_HEADER_SIZE + 2)); } public byte[] getBuffer() { diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMAuthTag.java b/Applet/Applet/src/com/android/javacard/keymaster/KMAuthTag.java new file mode 100644 index 00000000..23d4f801 --- /dev/null +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMAuthTag.java @@ -0,0 +1,7 @@ +package com.android.javacard.keymaster; + +public class KMAuthTag { + public boolean reserved; + public byte[] authTag; + public short usageCount; +} diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMBoolTag.java b/Applet/Applet/src/com/android/javacard/keymaster/KMBoolTag.java index e56dc9f3..ae0f75ac 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMBoolTag.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMBoolTag.java @@ -47,8 +47,8 @@ private static KMBoolTag proto(short ptr) { // pointer to an empty instance used as expression public static short exp() { - short ptr = KMType.exp(TAG_TYPE); - Util.setShort(heap, (short)(ptr+1), BOOL_TAG); + short ptr = instance(TAG_TYPE, (short)2); + Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE), BOOL_TAG); return ptr; } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMByteBlob.java b/Applet/Applet/src/com/android/javacard/keymaster/KMByteBlob.java index 423220fa..7f598eaa 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMByteBlob.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMByteBlob.java @@ -91,6 +91,11 @@ public byte[] getBuffer() { public void getValue(byte[] destBuf, short destStart, short destLength){ Util.arrayCopyNonAtomic(heap, getStartOff(), destBuf, destStart, destLength); } + public short getValues(byte[] destBuf, short destStart){ + short destLength = length(); + Util.arrayCopyNonAtomic(heap, getStartOff(), destBuf, destStart, destLength); + return destLength; + } public void setValue(byte[] srcBuf, short srcStart, short srcLength){ if(length() > srcLength){ @@ -98,5 +103,10 @@ public void setValue(byte[] srcBuf, short srcStart, short srcLength){ } Util.arrayCopyNonAtomic(srcBuf, srcStart, heap, getStartOff(), length()); } - + public boolean isValid(){ + if (length() == 0) { + return false; + } + return true; + } } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMCipher.java b/Applet/Applet/src/com/android/javacard/keymaster/KMCipher.java new file mode 100644 index 00000000..922f9a43 --- /dev/null +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMCipher.java @@ -0,0 +1,30 @@ +package com.android.javacard.keymaster; + +public abstract class KMCipher { + + public static final byte CIPHER_RSA = 7; + + public static final short PAD_PKCS1_OAEP_SHA224 = 13; + public static final byte PAD_PKCS1_OAEP_SHA256 = 14; + public static final short PAD_PKCS1_OAEP_SHA384 = 15; + public static final short PAD_PKCS1_OAEP_SHA512 = 16; + public static final short PAD_NOPAD = 1; + public static final short PAD_NULL = 0; + public static final short PAD_PKCS7 = 31; // Not supported in javacard + public static final short CIPHER_DES_CBC = 3; + public static final short CIPHER_DES_ECB = 4; + public static final short CIPHER_AES_CBC = 1; + public static final short CIPHER_AES_ECB = 2; + public static final short MODE_ENCRYPT = 2; + public static final short MODE_DECRYPT = 1; + public static final short PAD_PKCS1 = 7; + + public abstract short doFinal(byte[] buffer, short startOff, short length, byte[] scratchPad, short i); + + public abstract short getCipherAlgorithm(); + + public abstract short update(byte[] buffer, short startOff, short length, byte[] scratchPad, short i); + + public abstract short getPaddingAlgorithm(); + +} diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMCryptoProvider.java b/Applet/Applet/src/com/android/javacard/keymaster/KMCryptoProvider.java index 8c44d28f..055822e5 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMCryptoProvider.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMCryptoProvider.java @@ -4,8 +4,10 @@ import javacard.security.DESKey; import javacard.security.ECPrivateKey; import javacard.security.HMACKey; +import javacard.security.Key; import javacard.security.KeyPair; import javacard.security.RSAPrivateKey; +import javacard.security.Signature; public interface KMCryptoProvider { KeyPair createRsaKeyPair(); @@ -68,13 +70,39 @@ short aesCCMSign( byte[] bufOut, short bufStart); - ECPrivateKey createEcPrivateKey(byte[] pubBuffer, short pubOff, short pubLength, - byte[] privBuffer, short privOff, short privLength); + ECPrivateKey createEcKey(byte[] privBuffer, short privOff, short privLength); HMACKey createHMACKey(byte[] secretBuffer, short secretOff, short secretLength); DESKey createTDESKey(byte[] secretBuffer, short secretOff, short secretLength); - RSAPrivateKey createRsaPrivateKey(byte[] modBuffer, short modOff, short modLength, + RSAPrivateKey createRsaKey(byte[] modBuffer, short modOff, short modLength, byte[] privBuffer, short privOff, short privLength); + + HMACKey cmacKdf(byte[] keyMaterial, byte[] label, byte[] context, short contextStart, short contextLength); + + short hmacSign(HMACKey key, byte[] data, short dataStart, short dataLength, byte[] mac, short macStart); + boolean hmacVerify(HMACKey key, byte[] data, short dataStart, short dataLength, + byte[] mac, short macStart, short macLength); + + KMCipher createRsaDecrypt(short cipherAlg, short padding, + byte[] secret, short secretStart, short secretLength, + byte[] modBuffer, short modOff, short modLength); + Signature createRsaSigner(short msgDigestAlg, short padding, byte[] secret, short secretStart, + short secretLength,byte[] modBuffer, short modOff, short modLength); + Signature createEcSigner(short msgDigestAlg, byte[] secret, short secretStart, + short secretLength); + KMCipher createSymmetricCipher(short cipherAlg, short padding, short mode, + byte[] secret, short secretStart, short secretLength, + byte[] ivBuffer, short ivStart, short ivLength); + Signature createHmacSigner(short msgDigestAlg, + byte[] secret, short secretStart, short secretLength); + KMCipher createGCMCipher(short mode, byte[] secret, short secretStart, short secretLength, + byte[] ivBuffer, short ivStart, short ivLength); + void delete(KMCipher cipher); + void delete(Signature signature); + void delete(Key key); + void delete(KeyPair keyPair); + //TODO remove this later + void bypassAesGcm(); } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMDecoder.java b/Applet/Applet/src/com/android/javacard/keymaster/KMDecoder.java index 25c1c1b1..daefb282 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMDecoder.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMDecoder.java @@ -219,15 +219,17 @@ private short decodeBytesTag(short exp) { private short decodeArray(short exp) { short payloadLength = readMajorTypeWithPayloadLength(ARRAY_TYPE); - if (KMArray.cast(exp).length() != payloadLength) { - ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); - } short arrPtr = KMArray.cast(exp).instance(payloadLength); short index = 0; short type; short obj; // check whether array contains one type of objects or multiple types if( KMArray.cast(exp).containedType() == 0){// multiple types specified by expression. + if (KMArray.cast(exp).length() != KMArray.ANY_ARRAY_LENGTH) { + if (KMArray.cast(exp).length() != payloadLength) { + ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); + } + } while (index < payloadLength) { type = KMArray.cast(exp).get(index); obj = decode(type); diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMEnum.java b/Applet/Applet/src/com/android/javacard/keymaster/KMEnum.java index 00ba0cd1..d5a69270 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMEnum.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMEnum.java @@ -25,7 +25,7 @@ public class KMEnum extends KMType { private static short instPtr; private static short[] types = {HARDWARE_TYPE, KEY_FORMAT, KEY_DERIVATION_FUNCTION, - VERIFIED_BOOT_STATE, DEVICE_LOCKED}; + VERIFIED_BOOT_STATE, DEVICE_LOCKED, USER_AUTH_TYPE, PURPOSE}; private static Object[] enums = null; @@ -90,7 +90,9 @@ private static void create() { ISO18033_2_KDF2_SHA256 }, new byte[] {SELF_SIGNED_BOOT, VERIFIED_BOOT}, - new byte[] {DEVICE_LOCKED_TRUE, DEVICE_LOCKED_FALSE} + new byte[] {DEVICE_LOCKED_TRUE, DEVICE_LOCKED_FALSE}, + new byte[] {USER_AUTH_NONE,PASSWORD,FINGERPRINT, BOTH}, + new byte[] {ENCRYPT, DECRYPT, SIGN, VERIFY, WRAP_KEY, ATTEST_KEY} }; } } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMEnumArrayTag.java b/Applet/Applet/src/com/android/javacard/keymaster/KMEnumArrayTag.java index d29903e1..1b1a56ee 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMEnumArrayTag.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMEnumArrayTag.java @@ -139,4 +139,50 @@ private static byte[] getAllowedEnumValues(short key) { return null; } + public static short getValues(short tagId, short params, byte[] buf, short start) { + short tag = + KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, tagId, params); + if (tag == KMType.INVALID_VALUE) { + return KMType.INVALID_VALUE; + } + tag = KMEnumArrayTag.cast(tag).getValues(); + return KMByteBlob.cast(tag).getValues(buf, start); + } + + public short get(short index){ + return KMByteBlob.cast(getValues()).get(index); + } + + public static boolean contains(short tagId, short tagValue, short params) { + short tag = + KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, tagId, params); + if (tag != KMType.INVALID_VALUE) { + short index = 0; + while (index < KMEnumArrayTag.cast(tag).length()) { + if (tagValue == KMEnumArrayTag.cast(tag).get(index)) { + return true; + } + index++; + } + } + return false; + } + public static short length(short tagId, short params) { + short tag = + KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, tagId, params); + if (tag != KMType.INVALID_VALUE) { + return KMEnumArrayTag.cast(tag).length(); + } + return KMType.INVALID_VALUE; + } + public boolean contains(short tagValue){ + short index = 0; + while(index < length()){ + if(get(index) == (byte)tagValue){ + return true; + } + index++; + } + return false; + } } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMHardwareAuthToken.java b/Applet/Applet/src/com/android/javacard/keymaster/KMHardwareAuthToken.java index c5c92d86..23245352 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMHardwareAuthToken.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMHardwareAuthToken.java @@ -39,7 +39,7 @@ public static short exp() { arr.add(CHALLENGE, KMInteger.exp()); arr.add(USER_ID, KMInteger.exp()); arr.add(AUTHENTICATOR_ID, KMInteger.exp()); - arr.add(HW_AUTHENTICATOR_TYPE, KMEnumTag.instance(KMType.USER_AUTH_TYPE)); + arr.add(HW_AUTHENTICATOR_TYPE, KMEnum.instance(KMType.USER_AUTH_TYPE)); arr.add(TIMESTAMP, KMInteger.exp()); arr.add(MAC, KMByteBlob.exp()); return instance(arrPtr); @@ -119,8 +119,7 @@ public short getHwAuthenticatorType() { } public void setHwAuthenticatorType(short vals) { - short key = KMEnumTag.cast(vals).getKey(); - if(key != USER_AUTH_TYPE) ISOException.throwIt(ISO7816.SW_DATA_INVALID); + KMEnum.cast(vals); short arrPtr = getVals(); KMArray.cast(arrPtr).add(HW_AUTHENTICATOR_TYPE, vals); } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMInteger.java b/Applet/Applet/src/com/android/javacard/keymaster/KMInteger.java index 994a94cc..6fff9f04 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMInteger.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMInteger.java @@ -138,4 +138,24 @@ public short getSignificantShort(){ public byte getByte() { return heap[(short) (instPtr + TLV_HEADER_SIZE + 3)]; } + + public boolean isZero() { + if(getShort() == 0 && getSignificantShort() == 0){ + return true; + } + return false; + } + + public static short compare(short num1, short num2){ + short num1Ptr = KMInteger.cast(num1).getStartOff(); + short num2Ptr = KMInteger.cast(num2).getStartOff(); + short len = KMInteger.cast(num2).length(); + if(KMInteger.cast(num1).length() > KMInteger.cast(num2).length()){ + len = KMInteger.cast(num1).length(); + } + return Util.arrayCompare( + repository.getHeap(), num1Ptr, + repository.getHeap(), num2Ptr, + len); + } } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMIntegerArrayTag.java b/Applet/Applet/src/com/android/javacard/keymaster/KMIntegerArrayTag.java index 5f0e6731..c154f5a0 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMIntegerArrayTag.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMIntegerArrayTag.java @@ -97,7 +97,7 @@ public short getValues() { public short length() { short ptr = getValues(); - return KMIntegerArrayTag.cast(ptr).length(); + return KMArray.cast(ptr).length(); } public void add(short index, short val) { @@ -127,4 +127,21 @@ private static boolean validateTagType(short tagType) { } return false; } + + public static boolean contains(short tagId, short tagValue, short params) { + short tag = + KMKeyParameters.findTag(KMType.UINT_ARRAY_TAG, tagId, params); + if (tag != KMType.INVALID_VALUE) { + short index = 0; + tag = KMIntegerArrayTag.cast(tag).getValues(); + while (index < KMArray.cast(tag).length()) { + if (KMInteger.compare(tagValue, KMArray.cast(tag).get(index)) == 0) { + return true; + } + index++; + } + } + return false; + } + } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java b/Applet/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java index 8ea7ad74..e8b102dd 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java @@ -29,7 +29,9 @@ import javacard.security.ECPublicKey; import javacard.security.HMACKey; import javacard.security.KeyPair; +import javacard.security.MessageDigest; import javacard.security.RSAPrivateKey; +import javacard.security.Signature; import javacardx.apdu.ExtendedLength; /** @@ -42,11 +44,25 @@ public class KMKeymasterApplet extends Applet implements AppletEvent, ExtendedLength { // Constants. public static final byte AES_BLOCK_SIZE = 16; - public static final short MAX_LENGTH = (short) 0x1000; + public static final byte DES_BLOCK_SIZE = 8; + public static final short MAX_LENGTH = (short) 0x2000; private static final byte CLA_ISO7816_NO_SM_NO_CHAN = (byte) 0x80; private static final short KM_HAL_VERSION = (short) 0x4000; private static final short MAX_AUTH_DATA_SIZE = (short) 128; private static final short MAX_IO_LENGTH = 0x400; + // "Keymaster HMAC Verification" - used for HMAC key verification. + public static final byte[] sharingCheck = { + 0x4B, 0x65, 0x79, 0x6D, 0x61, 0x73, 0x74, 0x65, 0x72, 0x20, 0x48, 0x4D, 0x41, 0x43, 0x20, 0x56, + 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E + }; + // "KeymasterSharedMac" + public static final byte[] ckdfLable = { + 0x4B, 0x65, 0x79, 0x6D, 0x61, 0x73, 0x74, 0x65, 0x72, 0x53, 0x68, 0x61, 0x72, 0x65, 0x64, 0x4D, + 0x61, 0x63 + }; + // "Auth Verification" + public static final byte[] authVerification = {0x41, 0x75, 0x74, 0x68, 0x20, 0x56, 0x65, 0x72, 0x69, + 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E}; // Possible states of the applet. private static final byte ILLEGAL_STATE = 0x00; private static final byte INSTALL_STATE = 0x01; @@ -77,7 +93,7 @@ public class KMKeymasterApplet extends Applet implements AppletEvent, ExtendedLe private static final byte INS_PROVISION_CMD = 0x23; private static final byte INS_SET_BOOT_PARAMS_CMD = 0x24; // Data Dictionary items - public static final byte DATA_ARRAY_SIZE = 25; + public static final byte DATA_ARRAY_SIZE = 30; public static final byte TMP_VARIABLE_ARRAY_SIZE = 20; public static final byte UPDATE_PARAM_ARRAY_SIZE = 40; public static final byte KEY_PARAMETERS = 0; @@ -99,6 +115,16 @@ public class KMKeymasterApplet extends Applet implements AppletEvent, ExtendedLe public static final byte PUB_KEY = 16; public static final byte IMPORTED_KEY_BLOB = 17; public static final byte ORIGIN = 18; + public static final byte ENC_TRANSPORT_KEY = 19; + public static final byte MASKING_KEY = 20; + public static final byte HMAC_SHARING_PARAMS = 21; + public static final byte OP_HANDLE = 22; + public static final byte IV = 23; + public static final byte INPUT_DATA = 24; + public static final byte OUTPUT_DATA = 25; + public static final byte HW_TOKEN = 26; + public static final byte VERIFICATION_TOKEN = 27; + // AddRngEntropy private static final short MAX_SEED_SIZE = 2048; // Keyblob constants @@ -110,6 +136,9 @@ public class KMKeymasterApplet extends Applet implements AppletEvent, ExtendedLe // AES GCM constants private static final byte AES_GCM_AUTH_TAG_LENGTH = 12; private static final byte AES_GCM_NONCE_LENGTH = 12; + // ComputeHMAC constants + private static final short HMAC_SEED_SIZE = 32; + private static final short HMAC_NONCE_SIZE = 32; // Keymaster Applet attributes private static byte keymasterState = ILLEGAL_STATE; private static KMEncoder encoder; @@ -127,11 +156,29 @@ public class KMKeymasterApplet extends Applet implements AppletEvent, ExtendedLe /** Registers this applet. */ protected KMKeymasterApplet() { // TODO change this to make this compile time variation. - cryptoProvider = new KMSimulator(); + cryptoProvider = KMCryptoProviderImpl.instance(); + provisionDone = false; + setBootParamsDone = false; + byte[] buf = + JCSystem.makeTransientByteArray( + repository.HMAC_SEED_NONCE_SIZE, JCSystem.CLEAR_ON_DESELECT); keymasterState = KMKeymasterApplet.INSTALL_STATE; data = JCSystem.makeTransientShortArray((short) DATA_ARRAY_SIZE, JCSystem.CLEAR_ON_RESET); - tmpVariables = JCSystem.makeTransientShortArray((short) TMP_VARIABLE_ARRAY_SIZE, JCSystem.CLEAR_ON_RESET); - repository = new KMRepository(cryptoProvider.getTrueRandomNumber((short) 256)); + repository = new KMRepository(); + tmpVariables = + JCSystem.makeTransientShortArray((short) TMP_VARIABLE_ARRAY_SIZE, JCSystem.CLEAR_ON_RESET); + Util.arrayCopyNonAtomic( + cryptoProvider.getTrueRandomNumber(repository.HMAC_SEED_NONCE_SIZE), + (short) 0, + buf, + (short) 0, + repository.HMAC_SEED_NONCE_SIZE); + repository.initMasterKey(buf, repository.HMAC_SEED_NONCE_SIZE); + cryptoProvider.newRandomNumber(buf, (short) 0, repository.HMAC_SEED_NONCE_SIZE); + // TODO remove this when key agreement protocol is implemented. + repository.initHmacKey(buf, repository.HMAC_SEED_NONCE_SIZE); + cryptoProvider.newRandomNumber(buf, (short) 0, repository.HMAC_SEED_NONCE_SIZE); + repository.initHmacSeed(buf, repository.HMAC_SEED_NONCE_SIZE); KMType.initialize(); encoder = new KMEncoder(); decoder = new KMDecoder(); @@ -271,7 +318,7 @@ public void process(APDU apdu) { processDestroyAttIdsCmd(apdu); break; case INS_VERIFY_AUTHORIZATION_CMD: - processVerifyAuthenticationCmd(apdu); + processVerifyAuthorizationCmd(apdu); break; case INS_GET_HMAC_SHARING_PARAM_CMD: processGetHmacSharingParamCmd(apdu); @@ -304,13 +351,26 @@ public void process(APDU apdu) { ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED); } } catch (KMException exception) { + if(data[OP_HANDLE] != KMType.INVALID_VALUE){ + KMOperationState op = repository.findOperation(KMInteger.cast(data[OP_HANDLE]).getShort()); + if(op != null){ + repository.releaseOperation(op); + } + } sendError(apdu, exception.reason); exception.clear(); } finally { + resetData(); repository.clean(); } } - + private void resetData(){ + short index = 0; + while (index < data.length){ + data[index] = KMType.INVALID_VALUE; + index++; + } + } /** Sends a response, may be extended response, as requested by the command. */ public static void sendOutgoing(APDU apdu) { if (bufferLength > MAX_IO_LENGTH) { @@ -341,34 +401,6 @@ public static void receiveIncoming(APDU apdu) { } } - private void processProvisionCmd(APDU apdu) { - // Receive the incoming request fully from the master into buffer. - receiveIncoming(apdu); - // Re-purpose the apdu buffer as scratch pad. - byte[] scratchPad = apdu.getBuffer(); - Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) apdu.getBuffer().length, (byte) 0); - // Arguments - short keyparams = KMKeyParameters.exp(); - short keyFormat = KMEnum.instance(KMType.KEY_FORMAT); - short keyBlob = KMByteBlob.exp(); - short argsProto = KMArray.instance((short) 3); - KMArray.cast(argsProto).add((short) 0, keyparams); - KMArray.cast(argsProto).add((short) 1, keyFormat); - KMArray.cast(argsProto).add((short) 2, keyBlob); - // Decode the argument - short args = decoder.decode(argsProto, buffer, bufferStartOffset, bufferLength); - // key params should have os patch, os version and verified root of trust - - // TODO execute the function - // Change the state to ACTIVE - if (keymasterState == KMKeymasterApplet.FIRST_SELECT_STATE) { - provisionDone = true; - if (setBootParamsDone) { - keymasterState = KMKeymasterApplet.ACTIVE_STATE; - } - } - } - private void processGetHwInfoCmd(APDU apdu) { // No arguments expected final byte[] JavacardKeymasterDevice = { @@ -409,74 +441,1507 @@ private void processAddRngEntropyCmd(APDU apdu) { if (blob.length() > MAX_SEED_SIZE) { KMException.throwIt(KMError.INVALID_ARGUMENT); } - cryptoProvider.addRngEntropy(blob.getBuffer(), blob.getStartOff(), blob.length()); + cryptoProvider.addRngEntropy(blob.getBuffer(), blob.getStartOff(), blob.length()); + } + + private void processProvisionCmd(APDU apdu) { + // Receive the incoming request fully from the master into buffer. + receiveIncoming(apdu); + // Re-purpose the apdu buffer as scratch pad. + byte[] scratchPad = apdu.getBuffer(); + // Arguments + short keyparams = KMKeyParameters.exp(); + short keyFormat = KMEnum.instance(KMType.KEY_FORMAT); + short keyBlob = KMByteBlob.exp(); + short argsProto = KMArray.instance((short) 3); + KMArray.cast(argsProto).add((short) 0, keyparams); + KMArray.cast(argsProto).add((short) 1, keyFormat); + KMArray.cast(argsProto).add((short) 2, keyBlob); + // Decode the argument + short args = decoder.decode(argsProto, buffer, bufferStartOffset, bufferLength); + // key params should have os patch, os version and verified root of trust + + // TODO execute the function + // Change the state to ACTIVE + if (keymasterState == KMKeymasterApplet.FIRST_SELECT_STATE) { + provisionDone = true; + if (setBootParamsDone) { + keymasterState = KMKeymasterApplet.ACTIVE_STATE; + } + } + } + + private void processGetKeyCharacteristicsCmd(APDU apdu) { + // Receive the incoming request fully from the master. + receiveIncoming(apdu); + // Re-purpose the apdu buffer as scratch pad. + byte[] scratchPad = apdu.getBuffer(); + // Arguments + tmpVariables[0] = KMArray.instance((short) 3); + KMArray.cast(tmpVariables[0]).add((short) 0, KMByteBlob.exp()); + KMArray.cast(tmpVariables[0]).add((short) 1, KMByteBlob.exp()); + KMArray.cast(tmpVariables[0]).add((short) 2, KMByteBlob.exp()); + // Decode the arguments + tmpVariables[0] = decoder.decode(tmpVariables[0], buffer, bufferStartOffset, bufferLength); + data[KEY_BLOB] = KMArray.cast(tmpVariables[0]).get((short) 0); + data[APP_ID] = KMArray.cast(tmpVariables[0]).get((short) 1); + data[APP_DATA] = KMArray.cast(tmpVariables[0]).get((short) 2); + if (!KMByteBlob.cast(data[APP_ID]).isValid()) { + data[APP_ID] = KMType.INVALID_VALUE; + } + if (!KMByteBlob.cast(data[APP_DATA]).isValid()) { + data[APP_DATA] = KMType.INVALID_VALUE; + } + // Parse Key Blob + parseEncryptedKeyBlob(scratchPad); + // Check Version and Patch Level + checkVersionAndPatchLevel(scratchPad); + // make response. + tmpVariables[0] = KMArray.instance((short) 2); + KMArray.cast(tmpVariables[0]).add((short) 0, KMInteger.uint_16(KMError.OK)); + KMArray.cast(tmpVariables[0]).add((short) 1, data[KEY_CHARACTERISTICS]); + // Encode the response + bufferLength = encoder.encode(tmpVariables[0], buffer, bufferStartOffset); + sendOutgoing(apdu); + } + + private void processGetHmacSharingParamCmd(APDU apdu) { + // No Arguments + byte[] scratchPad = apdu.getBuffer(); + // Create blob containing seed + tmpVariables[0] = + KMByteBlob.instance(repository.getHmacSeed(), (short) 0, repository.HMAC_SEED_NONCE_SIZE); + // Create blob containing nonce + cryptoProvider.newRandomNumber(scratchPad, (short) 0, repository.HMAC_SEED_NONCE_SIZE); + tmpVariables[1] = KMByteBlob.instance(scratchPad, (short) 0, repository.HMAC_SEED_NONCE_SIZE); + // Create HMAC Sharing Parameters + tmpVariables[2] = KMHmacSharingParameters.instance(); + KMHmacSharingParameters.cast(tmpVariables[2]).setNonce(tmpVariables[1]); + KMHmacSharingParameters.cast(tmpVariables[2]).setSeed(tmpVariables[0]); + // prepare the response + tmpVariables[3] = KMArray.instance((short) 2); + KMArray.cast(tmpVariables[3]).add((short) 0, KMInteger.uint_16(KMError.OK)); + KMArray.cast(tmpVariables[3]).add((short) 1, tmpVariables[2]); + // Encode the response + bufferLength = encoder.encode(tmpVariables[0], buffer, bufferStartOffset); + sendOutgoing(apdu); + } + + private void processDeleteAllKeysCmd(APDU apdu) { + // No arguments + repository.removeAllAuthTags(); + // Send ok + sendError(apdu, KMError.OK); + } + + private void processDeleteKeyCmd(APDU apdu) { + // Receive the incoming request fully from the master. + receiveIncoming(apdu); + // Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) apdu.getBuffer().length, (byte) 0); + // Arguments + short argsProto = KMArray.instance((short) 1); + KMArray.cast(argsProto).add((short) 0, KMByteBlob.exp()); + // Decode the argument + short args = decoder.decode(argsProto, buffer, bufferStartOffset, bufferLength); + // Process + data[KEY_BLOB] = KMArray.cast(args).get((short) 0); + tmpVariables[0] = KMByteBlob.cast(data[KEY_BLOB]).getStartOff(); + tmpVariables[1] = KMArray.instance((short) 5); + KMArray.cast(tmpVariables[1]).add(KMKeymasterApplet.KEY_BLOB_SECRET, KMByteBlob.exp()); + KMArray.cast(tmpVariables[1]).add(KMKeymasterApplet.KEY_BLOB_AUTH_TAG, KMByteBlob.exp()); + KMArray.cast(tmpVariables[1]).add(KMKeymasterApplet.KEY_BLOB_NONCE, KMByteBlob.exp()); + tmpVariables[2] = KMKeyCharacteristics.exp(); + KMArray.cast(tmpVariables[1]).add(KMKeymasterApplet.KEY_BLOB_KEYCHAR, tmpVariables[2]); + KMArray.cast(tmpVariables[1]).add(KMKeymasterApplet.KEY_BLOB_PUB_KEY, KMByteBlob.exp()); + data[KEY_BLOB] = + decoder.decodeArray( + tmpVariables[1], + KMByteBlob.cast(data[KEY_BLOB]).getBuffer(), + KMByteBlob.cast(data[KEY_BLOB]).getStartOff(), + KMByteBlob.cast(data[KEY_BLOB]).length()); + tmpVariables[0] = KMArray.cast(data[KEY_BLOB]).length(); + if (tmpVariables[0] < 4) { + KMException.throwIt(KMError.INVALID_KEY_BLOB); + } + // Validate Auth Tag + data[AUTH_TAG] = KMArray.cast(data[KEY_BLOB]).get(KEY_BLOB_AUTH_TAG); + if (!repository.validateAuthTag(data[AUTH_TAG])) { + KMException.throwIt(KMError.INVALID_KEY_BLOB); + } + // delete the auth tag + repository.removeAuthTag(data[AUTH_TAG]); + // Send ok + sendError(apdu, KMError.OK); + } + + private void processComputeSharedHmacCmd(APDU apdu) { + // Receive the incoming request fully from the master into buffer. + receiveIncoming(apdu); + byte[] scratchPad = apdu.getBuffer(); + tmpVariables[1] = KMArray.instance((short) 1); + tmpVariables[2] = KMKeyParameters.exp(); + tmpVariables[3] = KMHmacSharingParameters.exp(); + KMArray.cast(tmpVariables[1]).add((short) 0, KMArray.exp(tmpVariables[3])); // Vector + KMArray.cast(tmpVariables[1]).add((short) 1, tmpVariables[2]); // Key Params + // Decode the arguments + tmpVariables[2] = decoder.decode(tmpVariables[1], buffer, bufferStartOffset, bufferLength); + data[KEY_PARAMETERS] = KMArray.cast(tmpVariables[2]).get((short) 1); + data[HMAC_SHARING_PARAMS] = KMArray.cast(tmpVariables[2]).get((short) 0); + // Concatenate HMAC Params + tmpVariables[0] = 0; + tmpVariables[1] = KMArray.cast(data[HMAC_SHARING_PARAMS]).length(); + tmpVariables[5] = 0; // index in scratchPad + while (tmpVariables[0] < tmpVariables[1]) { + // read HmacSharingParam + tmpVariables[2] = KMArray.cast(data[HMAC_SHARING_PARAMS]).get(tmpVariables[0]); + // get seed + tmpVariables[3] = KMHmacSharingParameters.cast(tmpVariables[2]).getSeed(); + tmpVariables[4] = KMByteBlob.cast(tmpVariables[3]).length(); + // if seed is present + if (tmpVariables[4] == HMAC_SEED_SIZE /*32*/) { + // then copy that to scratchPad + Util.arrayCopyNonAtomic( + KMByteBlob.cast(tmpVariables[3]).getBuffer(), + KMByteBlob.cast(tmpVariables[3]).getStartOff(), + scratchPad, + tmpVariables[5], + tmpVariables[4]); + tmpVariables[5] += tmpVariables[4]; + } + // get nonce + tmpVariables[3] = KMHmacSharingParameters.cast(tmpVariables[2]).getNonce(); + tmpVariables[4] = KMByteBlob.cast(tmpVariables[3]).length(); + // if nonce is not present + if (tmpVariables[4] != HMAC_NONCE_SIZE /*32*/) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + // copy nonce to scratchPad + Util.arrayCopyNonAtomic( + KMByteBlob.cast(tmpVariables[3]).getBuffer(), + KMByteBlob.cast(tmpVariables[3]).getStartOff(), + scratchPad, + tmpVariables[5], + tmpVariables[4]); + tmpVariables[5] += tmpVariables[4]; + } + // ckdf to derive hmac key + HMACKey key = + cryptoProvider.cmacKdf( + repository.getHmacKey(), ckdfLable, scratchPad, (short) 0, tmpVariables[5]); + tmpVariables[5] = key.getKey(scratchPad, (short) 0); + repository.initComputedHmac(scratchPad, (short) 0, tmpVariables[5]); + // Generate sharingKey verification + tmpVariables[5] = + cryptoProvider.hmacSign( + key, sharingCheck, (short) 0, (short) sharingCheck.length, scratchPad, (short) 0); + tmpVariables[1] = KMByteBlob.instance(scratchPad, (short) 0, tmpVariables[5]); + // prepare the response + tmpVariables[0] = KMArray.instance((short) 2); + KMArray.cast(tmpVariables[0]).add((short) 0, KMInteger.uint_16(KMError.OK)); + KMArray.cast(tmpVariables[0]).add((short) 1, tmpVariables[1]); + // Encode the response + bufferLength = encoder.encode(tmpVariables[0], buffer, bufferStartOffset); + sendOutgoing(apdu); + } + + private void processUpgradeKeyCmd(APDU apdu) { + // Receive the incoming request fully from the master into buffer. + receiveIncoming(apdu); + byte[] scratchPad = apdu.getBuffer(); + tmpVariables[1] = KMArray.instance((short) 2); + tmpVariables[2] = KMKeyParameters.exp(); + KMArray.cast(tmpVariables[1]).add((short) 0, KMByteBlob.exp()); // Key Blob + KMArray.cast(tmpVariables[1]).add((short) 1, tmpVariables[2]); // Key Params + // Decode the arguments + tmpVariables[2] = decoder.decode(tmpVariables[1], buffer, bufferStartOffset, bufferLength); + data[KEY_BLOB] = KMArray.cast(tmpVariables[2]).get((short) 0); + data[KEY_PARAMETERS] = KMArray.cast(tmpVariables[2]).get((short) 1); + tmpVariables[0] = + KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.APPLICATION_ID, data[KEY_PARAMETERS]); + if (tmpVariables[0] != KMTag.INVALID_VALUE) { + data[APP_ID] = KMByteTag.cast(tmpVariables[0]).getValue(); + } + tmpVariables[0] = + KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.APPLICATION_DATA, data[KEY_PARAMETERS]); + if (tmpVariables[0] != KMTag.INVALID_VALUE) { + data[APP_DATA] = KMByteTag.cast(tmpVariables[0]).getValue(); + } + // parse existing key blob + parseEncryptedKeyBlob(scratchPad); + // validate characteristics to be upgraded. + tmpVariables[0] = + KMIntegerTag.getValue( + scratchPad, (short) 0, KMType.UINT_TAG, KMType.OS_VERSION, data[HW_PARAMETERS]); + if ((tmpVariables[0] != KMType.INVALID_VALUE) + && (Util.arrayCompare( + repository.osVersion, (short) 0, scratchPad, (short) 0, tmpVariables[0]) + != 0)) { + if (Util.arrayCompare(repository.osVersion, (short) 0, scratchPad, (short) 0, tmpVariables[0]) + == -1) { + // If the key characteristics has os version > current os version + Util.arrayFillNonAtomic(scratchPad, (short) 0, tmpVariables[0], (byte) 0); + // If the os version is not zero + if (Util.arrayCompare( + repository.osVersion, (short) 0, scratchPad, (short) 0, tmpVariables[0]) + != 0) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + } + } + tmpVariables[0] = + KMIntegerTag.getValue( + scratchPad, (short) 0, KMType.UINT_TAG, KMType.OS_PATCH_LEVEL, data[HW_PARAMETERS]); + if ((tmpVariables[0] != KMType.INVALID_VALUE) + && (Util.arrayCompare(repository.osPatch, (short) 0, scratchPad, (short) 0, tmpVariables[0]) + != 0)) { + if (Util.arrayCompare(repository.osPatch, (short) 0, scratchPad, (short) 0, tmpVariables[0]) + < 0) { + // If the key characteristics has os patch level > current os patch + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + } + // remove Auth Tag + repository.removeAuthTag(data[AUTH_TAG]); + // copy origin + data[ORIGIN] = KMEnumTag.getValue(KMType.ORIGIN, data[HW_PARAMETERS]); + // create new key blob with current os version etc. + createEncryptedKeyBlob(scratchPad); + // persist new auth tag for rollback resistance. + repository.persistAuthTag(data[AUTH_TAG]); + // prepare the response + tmpVariables[0] = KMArray.instance((short) 3); + KMArray.cast(tmpVariables[0]).add((short) 0, KMInteger.uint_16(KMError.OK)); + KMArray.cast(tmpVariables[0]).add((short) 1, data[KEY_BLOB]); + KMArray.cast(tmpVariables[0]).add((short) 2, data[KEY_CHARACTERISTICS]); + // Encode the response + bufferLength = encoder.encode(tmpVariables[0], buffer, bufferStartOffset); + sendOutgoing(apdu); + } + + private void processExportKeyCmd(APDU apdu) { + sendError(apdu, KMError.UNIMPLEMENTED); + } + + private void processImportWrappedKeyCmd(APDU apdu) { + // Currently only RAW formatted import key blob are supported + if (repository.keyBlobCount > repository.MAX_BLOB_STORAGE) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + // Receive the incoming request fully from the master into buffer. + receiveIncoming(apdu); + byte[] scratchPad = apdu.getBuffer(); + tmpVariables[1] = KMArray.instance((short) 11); + // Arguments + tmpVariables[2] = KMKeyParameters.exp(); + KMArray.cast(tmpVariables[1]).add((short) 0, tmpVariables[2]); // Key Params + KMArray.cast(tmpVariables[1]).add((short) 1, KMEnum.instance(KMType.KEY_FORMAT)); // Key Format + KMArray.cast(tmpVariables[1]).add((short) 2, KMByteBlob.exp()); // Wrapped Import Key Blob + KMArray.cast(tmpVariables[1]).add((short) 3, KMByteBlob.exp()); // Auth Tag + KMArray.cast(tmpVariables[1]).add((short) 4, KMByteBlob.exp()); // IV - Nonce + KMArray.cast(tmpVariables[1]).add((short) 5, KMByteBlob.exp()); // Encrypted Transport Key + KMArray.cast(tmpVariables[1]).add((short) 6, KMByteBlob.exp()); // Wrapping Key KeyBlob + KMArray.cast(tmpVariables[1]).add((short) 7, KMByteBlob.exp()); // Masking Key + KMArray.cast(tmpVariables[1]).add((short) 8, tmpVariables[2]); // Un-wrapping Params + KMArray.cast(tmpVariables[1]).add((short) 9, KMInteger.exp()); // Password Sid + KMArray.cast(tmpVariables[1]).add((short) 10, KMInteger.exp()); // Biometric Sid + // Decode the arguments + tmpVariables[2] = decoder.decode(tmpVariables[1], buffer, bufferStartOffset, bufferLength); + tmpVariables[3] = KMArray.cast(tmpVariables[2]).get((short) 0); + // get algorithm + tmpVariables[3] = KMEnumTag.getValue(KMType.ALGORITHM, tmpVariables[3]); + if (tmpVariables[3] == KMType.INVALID_VALUE) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + tmpVariables[3] = KMEnumTag.getValue(KMType.ALGORITHM, data[KEY_PARAMETERS]); + if (tmpVariables[3] == KMType.RSA + || tmpVariables[3] == KMType.EC) { // RSA and EC not implemented + KMException.throwIt(KMError.UNIMPLEMENTED); + } + // Key format must be RAW format - X509 and PKCS8 not implemented. + tmpVariables[3] = KMArray.cast(tmpVariables[2]).get((short) 1); + tmpVariables[3] = KMEnum.cast(tmpVariables[3]).getVal(); + if (tmpVariables[3] != KMType.RAW) { + KMException.throwIt(KMError.UNIMPLEMENTED); + } + data[AUTH_DATA] = KMArray.cast(tmpVariables[2]).get((short) 3); + data[AUTH_TAG] = KMArray.cast(tmpVariables[2]).get((short) 4); + data[NONCE] = KMArray.cast(tmpVariables[2]).get((short) 5); + data[ENC_TRANSPORT_KEY] = KMArray.cast(tmpVariables[2]).get((short) 6); + data[MASKING_KEY] = KMArray.cast(tmpVariables[2]).get((short) 8); + // Step 1 - parse wrapping key blob + data[KEY_PARAMETERS] = KMArray.cast(tmpVariables[2]).get((short) 9); // wrapping key parameters + // Check for app id and app data. + data[APP_ID] = KMType.INVALID_VALUE; + data[APP_DATA] = KMType.INVALID_VALUE; + tmpVariables[3] = + KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.APPLICATION_ID, data[KEY_PARAMETERS]); + if (tmpVariables[3] != KMTag.INVALID_VALUE) { + data[APP_ID] = KMByteTag.cast(tmpVariables[3]).getValue(); + } + tmpVariables[3] = + KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.APPLICATION_DATA, data[KEY_PARAMETERS]); + if (tmpVariables[3] != KMTag.INVALID_VALUE) { + data[APP_DATA] = KMByteTag.cast(tmpVariables[3]).getValue(); + } + // wrapping key blob + data[KEY_BLOB] = KMArray.cast(tmpVariables[2]).get((short) 7); + parseEncryptedKeyBlob(scratchPad); + + // Step 2 - Decrypt the encrypted transport key + // enforce authorization for WRAP_KEY operation using RSA algorithm according to javacard caps. + if (KMEnumTag.getValue(KMType.ALGORITHM, data[HW_PARAMETERS]) != KMType.RSA) { + KMException.throwIt(KMError.INCOMPATIBLE_ALGORITHM); + } + if (!(KMEnumArrayTag.contains(KMType.DIGEST, KMType.SHA2_256, data[HW_PARAMETERS]))) { + KMException.throwIt(KMError.INCOMPATIBLE_DIGEST); + } + if (!(KMEnumArrayTag.contains(KMType.PADDING, KMType.RSA_OAEP, data[HW_PARAMETERS]))) { + KMException.throwIt(KMError.INCOMPATIBLE_PADDING_MODE); + } + KMCipher cipher = + cryptoProvider.createRsaDecrypt( + KMCipher.CIPHER_RSA, + KMCipher.PAD_PKCS1_OAEP_SHA256, + KMByteBlob.cast(data[SECRET]).getBuffer(), + KMByteBlob.cast(data[SECRET]).getStartOff(), + KMByteBlob.cast(data[SECRET]).length(), + KMByteBlob.cast(data[PUB_KEY]).getBuffer(), + KMByteBlob.cast(data[PUB_KEY]).getStartOff(), + KMByteBlob.cast(data[PUB_KEY]).length()); + // Decrypt the transport key + tmpVariables[3] = + cipher.doFinal( + KMByteBlob.cast(data[ENC_TRANSPORT_KEY]).getBuffer(), + KMByteBlob.cast(data[ENC_TRANSPORT_KEY]).getStartOff(), + KMByteBlob.cast(data[ENC_TRANSPORT_KEY]).length(), + scratchPad, + (short) 0); + data[SECRET] = KMByteBlob.instance(scratchPad, (short) 0, tmpVariables[3]); + cryptoProvider.delete(cipher); + + // Step 3 - XOR with masking key + tmpVariables[4] = KMByteBlob.cast(data[MASKING_KEY]).length(); + if (tmpVariables[3] != tmpVariables[4]) { + KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH); + } + tmpVariables[3] = 0; // index in scratchPad + byte[] buf = KMByteBlob.cast(MASKING_KEY).getBuffer(); + tmpVariables[5] = KMByteBlob.cast(MASKING_KEY).getStartOff(); + while (tmpVariables[3] < tmpVariables[4]) { + scratchPad[tmpVariables[3]] = + (byte) (scratchPad[tmpVariables[3]] ^ buf[(short) (tmpVariables[3] + tmpVariables[5])]); + scratchPad[3]++; + } + data[SECRET] = KMByteBlob.instance(scratchPad, (short) 0, tmpVariables[3]); + + // Step 4 - AES-GCM decrypt + data[IMPORTED_KEY_BLOB] = KMArray.cast(tmpVariables[2]).get((short) 2); + data[AUTH_DATA] = KMArray.cast(tmpVariables[2]).get((short) 3); + data[AUTH_TAG] = KMArray.cast(tmpVariables[2]).get((short) 4); + data[NONCE] = KMArray.cast(tmpVariables[2]).get((short) 5); + data[ENC_TRANSPORT_KEY] = KMArray.cast(tmpVariables[2]).get((short) 6); + data[MASKING_KEY] = KMArray.cast(tmpVariables[2]).get((short) 8); + AESKey key = + cryptoProvider.createAESKey( + KMByteBlob.cast(data[SECRET]).getBuffer(), + KMByteBlob.cast(data[SECRET]).getStartOff(), + KMByteBlob.cast(data[SECRET]).length()); + boolean verification = + cryptoProvider.aesGCMDecrypt( + key, + KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).getBuffer(), + KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).getStartOff(), + KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).length(), + scratchPad, + (short) 0, + KMByteBlob.cast(data[NONCE]).getBuffer(), + KMByteBlob.cast(data[NONCE]).getStartOff(), + KMByteBlob.cast(data[NONCE]).length(), + KMByteBlob.cast(data[AUTH_DATA]).getBuffer(), + KMByteBlob.cast(data[AUTH_DATA]).getStartOff(), + KMByteBlob.cast(data[AUTH_DATA]).length(), + KMByteBlob.cast(data[AUTH_TAG]).getBuffer(), + KMByteBlob.cast(data[AUTH_TAG]).getStartOff(), + KMByteBlob.cast(data[AUTH_TAG]).length()); + if (verification == false) { + KMException.throwIt(KMError.IMPORTED_KEY_VERIFICATION_FAILED); + } + cryptoProvider.delete(key); + + // Step 5 - Import Decrypted Key. + data[ORIGIN] = KMType.SECURELY_IMPORTED; + data[KEY_PARAMETERS] = KMArray.cast(tmpVariables[2]).get((short) 0); + importKey(apdu, scratchPad); + } + + private void processAttestKeyCmd(APDU apdu) {} + + private void processDestroyAttIdsCmd(APDU apdu) {} + + private void processVerifyAuthorizationCmd(APDU apdu) { + sendError(apdu, KMError.UNIMPLEMENTED); + } + + private void processAbortOperationCmd(APDU apdu) {} + + private void processFinishOperationCmd(APDU apdu) { + // TODO AES GCM + receiveIncoming(apdu); + byte[] scratchPad = apdu.getBuffer(); + Util.arrayFill(scratchPad, (short)0,(short)256, (byte)0); + tmpVariables[1] = KMArray.instance((short) 6); + // Arguments + tmpVariables[2] = KMKeyParameters.exp(); + KMArray.cast(tmpVariables[1]).add((short) 0, KMInteger.exp()); + KMArray.cast(tmpVariables[1]).add((short) 1, tmpVariables[2]); + KMArray.cast(tmpVariables[1]).add((short) 2, KMByteBlob.exp()); + KMArray.cast(tmpVariables[1]).add((short) 3, KMByteBlob.exp()); + tmpVariables[3] = KMHardwareAuthToken.exp(); + KMArray.cast(tmpVariables[1]).add((short) 4, tmpVariables[3]); + tmpVariables[4] = KMVerificationToken.exp(); + KMArray.cast(tmpVariables[1]).add((short) 5, tmpVariables[4]); + // Decode the arguments + tmpVariables[2] = decoder.decode(tmpVariables[1], buffer, bufferStartOffset, bufferLength); + data[OP_HANDLE] = KMArray.cast(tmpVariables[2]).get((short) 0); + data[KEY_PARAMETERS] = KMArray.cast(tmpVariables[2]).get((short) 1); + data[INPUT_DATA] = KMArray.cast(tmpVariables[2]).get((short) 2); + data[HW_TOKEN] = KMArray.cast(tmpVariables[2]).get((short) 4); + data[VERIFICATION_TOKEN] = KMArray.cast(tmpVariables[2]).get((short) 5); + // Check Operation Handle + tmpVariables[1] = KMInteger.cast(data[OP_HANDLE]).getShort(); + KMOperationState op = repository.findOperation(tmpVariables[1]); + if (KMInteger.compare(data[OP_HANDLE], KMInteger.uint_16(op.getHandle())) != 0) { + KMException.throwIt(KMError.INVALID_OPERATION_HANDLE); + } + //Authorize the final operation + authorizeUpdateFinalOperation(op, scratchPad); + short len = 0; + // If the operation is signing + if(op.getPurpose() == KMType.SIGN){ + // Perform trusted confirmation if required + if (op.isTrustedConfirmationRequired()) { + tmpVariables[0] = KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.CONFIRMATION_TOKEN, data[KEY_PARAMETERS]); + if(tmpVariables[0] == KMType.INVALID_VALUE){ + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + tmpVariables[0] = KMByteTag.cast(tmpVariables[0]).getValue(); + tmpVariables[1] = op.getTrustedConfirmationSigner() + .sign( + KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), + KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), + KMByteBlob.cast(data[INPUT_DATA]).length(), scratchPad, (short)0); + if(tmpVariables[1] != KMByteBlob.cast(tmpVariables[0]).length() ){ + KMException.throwIt(KMError.VERIFICATION_FAILED); + } + tmpVariables[0]=Util.arrayCompare(scratchPad,(short)0, + KMByteBlob.cast(tmpVariables[0]).getBuffer(), + KMByteBlob.cast(tmpVariables[0]).getStartOff(), + tmpVariables[1]); + if(tmpVariables[0] != 0){ + KMException.throwIt(KMError.VERIFICATION_FAILED); + } + } + tmpVariables[1] = op.getSigner().getCipherAlgorithm(); + tmpVariables[2] = op.getSigner().getMessageDigestAlgorithm(); + tmpVariables[3] = op.getSigner().getPaddingAlgorithm(); + len = KMByteBlob.cast(data[INPUT_DATA]).length(); + //For RSA Signing algorithm + if(tmpVariables[1] == Signature.SIG_CIPHER_RSA){ + //If no padding and no digest - then zero padding up to 256 on left + if(tmpVariables[2] == MessageDigest.ALG_NULL && tmpVariables[3] == KMCipher.PAD_NOPAD){ + // If data length is greater then key length + if(len > 256){ + KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + }else if(len == 256){ // if data length is same as key length + // Compare the data with key value - date should be less then key value. + // TODO the assumption is that private key exponent value is considered here. + tmpVariables[0]= op.getKey(scratchPad,(short)0); + tmpVariables[0] = Util.arrayCompare( + KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), + KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), + scratchPad, (short)0, tmpVariables[0]); + if(tmpVariables[0] >= 0){ + KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + } + } + Util.arrayCopyNonAtomic( + KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), + KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), + scratchPad, (short)(256 - len),len); + len = (short)256; + } else if (tmpVariables[2] == MessageDigest.ALG_NULL + && tmpVariables[3] == KMCipher.PAD_PKCS1) { + // If PKCS1 padding and no digest - then 0x01||0x00||PS||0x00 on left such that PS = 8 bytes + if(len > 245){ // 256 -11 bytes + KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + } + scratchPad[0] = 0x00; + scratchPad[1] = 0x01; + cryptoProvider.newRandomNumber(scratchPad, (short)2, (short)8); + scratchPad[10] = 0x00; + Util.arrayCopyNonAtomic( + KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), + KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), + scratchPad, (short)11,len); + len += (short)11; + }else if (tmpVariables[2] != MessageDigest.ALG_NULL && tmpVariables[3] == KMCipher.PAD_PKCS1){ + //If PKCS1 padding and digest != ALG_NULL - just copy the data on the scratch pad + Util.arrayCopyNonAtomic( + KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), + KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), + scratchPad, (short)0,len); + } + }else if(tmpVariables[1] == Signature.SIG_CIPHER_ECDSA){ // For ECDSA algorithm + //If no digest then truncate the data to 32 byte if required + if(tmpVariables[2] == MessageDigest.ALG_NULL){ + if(len > 32){ + Util.arrayCopyNonAtomic( + KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), + KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), + scratchPad, (short)0,(short)32); + len = 32; + } + }else{ + //If digest is present then copy the data to scratchpad + Util.arrayCopyNonAtomic( + KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), + KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), + scratchPad, (short)0,len); + } + }else if(tmpVariables[1] == Signature.SIG_CIPHER_HMAC){ // For HMAC algorithm + // Just copy the data as digest is always present. + Util.arrayCopyNonAtomic( + KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), + KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), + scratchPad, (short)0,len); + }else{ // This is should never happen + KMException.throwIt(KMError.OPERATION_CANCELLED); + } + // Sign the data and also complete the trusted verification. + tmpVariables[0]= op.getSigner() + .sign( + KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), + KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), + KMByteBlob.cast(data[INPUT_DATA]).length(),scratchPad, (short)0); + data[OUTPUT_DATA] = KMByteBlob.instance(scratchPad, (short)0, tmpVariables[0]); + } else{ //If decrypt or encrypt operation + tmpVariables[1] = op.getCipher().getCipherAlgorithm(); + tmpVariables[2] = op.getCipher().getPaddingAlgorithm(); + len = KMByteBlob.cast(data[INPUT_DATA]).length(); + if(tmpVariables[1] == KMCipher.CIPHER_RSA){ // For RSA algorithm + // If no padding and no digest - then zero padding up to 256 on left + if (tmpVariables[2] == KMCipher.PAD_NOPAD) { + if(len > 256){ + KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + } + if(len < 256){ + Util.arrayCopyNonAtomic( + KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), + KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), + scratchPad, (short)(256 - len),len); + len = (short)256; + } + } else { + // If OAEP padding with digest - just copy the data to scratchpad and continue. + Util.arrayCopyNonAtomic( + KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), + KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), + scratchPad, (short)0,len); + } + }else if(tmpVariables[1] == KMCipher.CIPHER_DES_CBC || tmpVariables[1] == KMCipher.CIPHER_DES_ECB + || tmpVariables[1] == KMCipher.CIPHER_AES_CBC || + tmpVariables[1] == KMCipher.CIPHER_AES_ECB){ + if(tmpVariables[1] == KMCipher.CIPHER_AES_CBC || + tmpVariables[1] == KMCipher.CIPHER_AES_ECB){ // For AES algorithm + tmpVariables[5] = AES_BLOCK_SIZE; + }else{ + tmpVariables[5] = DES_BLOCK_SIZE; + } + //If no padding then data length must be block aligned + if (tmpVariables[2] == KMCipher.PAD_NOPAD && ((short)(len % tmpVariables[5]) != 0)){ + KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + } + //If padding i.e. pkcs7 then add padding to right + if(tmpVariables[2] != KMCipher.PAD_NOPAD){ + tmpVariables[3] = (short)(len % tmpVariables[5]); + if(tmpVariables[3] != 0){ + // If not block aligned then pkcs7 padding on right + tmpVariables[4] = (short)((len / tmpVariables[5])+tmpVariables[5]); + Util.arrayFillNonAtomic(scratchPad, (short)0, tmpVariables[4], (byte)tmpVariables[3]); + }else{ + // If block aligned then one complete block of pkcs7 padding of block length value + // on the right. + tmpVariables[4] = (short)(len + tmpVariables[5]); + Util.arrayFillNonAtomic(scratchPad, (short)0, tmpVariables[4], (byte)tmpVariables[5]); + } + Util.arrayCopyNonAtomic( KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), + KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), + scratchPad, (short)0,len); + len = tmpVariables[4]; + } + // AES / DES Cipher + tmpVariables[0]= op.getCipher() + .doFinal(scratchPad, (short)0,len, scratchPad, (short)len); + data[OUTPUT_DATA] = KMByteBlob.instance(scratchPad, (short)len, tmpVariables[0]); + } else{ // This should never happen + KMException.throwIt(KMError.OPERATION_CANCELLED); + } + } + // Remove the operation handle + repository.releaseOperation(op); + // Make response + // make response + tmpVariables[1] = KMArray.instance((short) 0); + tmpVariables[1] = KMKeyParameters.instance(tmpVariables[1]); + tmpVariables[2] = KMArray.instance((short) 4); + if (data[OUTPUT_DATA] == KMType.INVALID_VALUE) { + data[OUTPUT_DATA] = KMByteBlob.instance((short) 0); + } + KMArray.cast(tmpVariables[2]).add((short) 0, KMInteger.uint_16(KMError.OK)); + KMArray.cast(tmpVariables[2]).add((short) 1, tmpVariables[1]); + KMArray.cast(tmpVariables[2]).add((short) 2, data[OUTPUT_DATA]); + // Encode the response + bufferLength = encoder.encode(tmpVariables[0], buffer, bufferStartOffset); + sendOutgoing(apdu); + } + + private void authorizeUpdateFinalOperation(KMOperationState op, byte[] scratchPad) { + // User Authentication + if (!op.isAuthPerOperation()) { + if (!op.isAuthTimeoutValidated()) { + validateVerificationToken(op, data[VERIFICATION_TOKEN], scratchPad); + tmpVariables[0] = KMInteger.uint_64(op.getAuthTime(), (short) 0); + tmpVariables[2] = KMVerificationToken.cast(data[VERIFICATION_TOKEN]).getTimestamp(); + if (tmpVariables[3] == KMType.INVALID_VALUE) { + KMException.throwIt(KMError.VERIFICATION_FAILED); + } + if (KMInteger.compare(tmpVariables[0], tmpVariables[3]) >= 0) { + KMException.throwIt(KMError.KEY_USER_NOT_AUTHENTICATED); + } + op.setAuthTimeoutValidated(true); + } + } else { // Auth per operation + authorizeUserIdPerKeyOperation(data[HW_TOKEN], scratchPad); + } + } + + private void validateVerificationToken(KMOperationState op, short verToken, byte[] scratchPad) { + // CBOR Encoding is always big endian and Java is big endian + short ptr = KMVerificationToken.cast(verToken).getMac(); + short len = 0; + // If mac length is zero then token is empty. + if (KMByteBlob.cast(ptr).length() == 0) { + return; + } + // validate operation handle. + ptr = KMVerificationToken.cast(verToken).getChallenge(); + if(op.getHandle() != KMInteger.cast(ptr).getShort()){ + KMException.throwIt(KMError.VERIFICATION_FAILED); + } + // concatenation length will be 37 + length of verified parameters list. + short params = KMVerificationToken.cast(verToken).getParametersVerified(); + Util.arrayFillNonAtomic(scratchPad, (short) 0, + (short) (37+KMByteBlob.cast(params).length()), (byte) 0); + // Add "Auth Verification" - 17 bytes. + Util.arrayCopy(authVerification,(short)0, scratchPad, (short)0, (short)authVerification.length); + len = (short)authVerification.length; + // concatenate challenge - 8 bytes + ptr = KMVerificationToken.cast(verToken).getChallenge(); + KMInteger.cast(ptr) + .value(scratchPad, (short) (len + (short) (8 - KMInteger.cast(ptr).length()))); + len += 8; + // concatenate timestamp -8 bytes + ptr = KMVerificationToken.cast(verToken).getTimestamp(); + KMInteger.cast(ptr) + .value(scratchPad, (short) (len + (short) (8 - KMInteger.cast(ptr).length()))); + len += 8; + // concatenate security level - 4 bytes + ptr = KMVerificationToken.cast(verToken).getSecurityLevel(); + scratchPad[(short) (len + 3)] = KMEnum.cast(ptr).getVal(); + len += 4; + // concatenate Parameters verified - blob of encoded data. + ptr = KMVerificationToken.cast(verToken).getParametersVerified(); + len += KMByteBlob.cast(ptr).getValues(scratchPad, (short)0); + len += 4; + // hmac the data + HMACKey key = + cryptoProvider.createHMACKey( + repository.getComputedHmacKey(), + (short) 0, + (short) repository.getComputedHmacKey().length); + ptr = KMVerificationToken.cast(verToken).getMac(); + boolean verified = + cryptoProvider.hmacVerify(key, scratchPad, (short) 0, len, + KMByteBlob.cast(ptr).getBuffer(), + KMByteBlob.cast(ptr).getStartOff(), + KMByteBlob.cast(ptr).length()); + if(!verified){ + KMException.throwIt(KMError.VERIFICATION_FAILED); + } + /* + + // Compare mac. + ptr = KMVerificationToken.cast(verToken).getMac(); + if (macLen != KMByteBlob.cast(ptr).length()) { + KMException.throwIt(KMError.INVALID_MAC_LENGTH); + } + if (Util.arrayCompare( + scratchPad, (short) (len+1), + KMByteBlob.cast(ptr).getBuffer(), KMByteBlob.cast(ptr).getStartOff(), macLen) != 0) { + KMException.throwIt(KMError.VERIFICATION_FAILED); + } + */ + + } + + private void processUpdateOperationCmd(APDU apdu) { + // TODO Add Support for AES-GCM + receiveIncoming(apdu); + byte[] scratchPad = apdu.getBuffer(); + tmpVariables[1] = KMArray.instance((short) 5); + // Arguments + tmpVariables[2] = KMKeyParameters.exp(); + KMArray.cast(tmpVariables[1]).add((short) 0, KMInteger.exp()); + KMArray.cast(tmpVariables[1]).add((short) 1, tmpVariables[2]); + KMArray.cast(tmpVariables[1]).add((short) 2, KMByteBlob.exp()); + tmpVariables[3] = KMHardwareAuthToken.exp(); + KMArray.cast(tmpVariables[1]).add((short) 3, tmpVariables[3]); + tmpVariables[4] = KMVerificationToken.exp(); + KMArray.cast(tmpVariables[1]).add((short) 4, tmpVariables[4]); + // Decode the arguments + tmpVariables[2] = decoder.decode(tmpVariables[1], buffer, bufferStartOffset, bufferLength); + data[OP_HANDLE] = KMArray.cast(tmpVariables[2]).get((short) 0); + data[KEY_PARAMETERS] = KMArray.cast(tmpVariables[2]).get((short) 1); + data[INPUT_DATA] = KMArray.cast(tmpVariables[2]).get((short) 2); + data[HW_TOKEN] = KMArray.cast(tmpVariables[2]).get((short) 3); + data[VERIFICATION_TOKEN] = KMArray.cast(tmpVariables[2]).get((short) 4); + // Check Operation Handle and get op state + tmpVariables[1] = KMInteger.cast(data[OP_HANDLE]).getShort(); + KMOperationState op = repository.findOperation(tmpVariables[1]); + if (KMInteger.compare(data[OP_HANDLE], KMInteger.uint_16(op.getHandle())) != 0) { + KMException.throwIt(KMError.INVALID_OPERATION_HANDLE); + } + // authorize the update operation + authorizeUpdateFinalOperation(op, scratchPad); + // If signing without digest then do length validation checks + tmpVariables[0] = KMByteBlob.cast(data[INPUT_DATA]).length(); + if (op.getPurpose() == KMType.SIGN) { + // If signing without digest then update should not be called by HAL only final must be + // called + if (op.getSigner().getMessageDigestAlgorithm() == MessageDigest.ALG_NULL) { + KMException.throwIt(KMError.OPERATION_CANCELLED); + } + op.getSigner() + .update( + KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), + KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), + KMByteBlob.cast(data[INPUT_DATA]).length()); + + if (op.isTrustedConfirmationRequired()) { + op.getTrustedConfirmationSigner() + .update( + KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), + KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), + KMByteBlob.cast(data[INPUT_DATA]).length()); + } + data[OUTPUT_DATA] = KMType.INVALID_VALUE; + } else { + // purpose is Encrypt or Decrypt - input data must be block aligned. + tmpVariables[1] = op.getCipher().getCipherAlgorithm(); + // TODO Update for decrypt for RSA may not be necessary - confirm this + if (tmpVariables[1] == KMCipher.CIPHER_RSA) { + KMException.throwIt(KMError.OPERATION_CANCELLED); + } + if (tmpVariables[1] == KMCipher.CIPHER_AES_CBC + || op.getCipher().getCipherAlgorithm() == KMCipher.CIPHER_AES_ECB) { + // 128 bit block size - HAL must send block aligned data + if (tmpVariables[0] % 16 != 0) { + KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + } + } else if (op.getCipher().getCipherAlgorithm() == KMCipher.CIPHER_DES_CBC + || op.getCipher().getCipherAlgorithm() == KMCipher.CIPHER_DES_ECB) { + // 64 bit block size - HAL must send block aligned data + if (tmpVariables[0] % 8 != 0) { + KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + } + } + tmpVariables[1] = + op.getCipher() + .update( + KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), + KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), + KMByteBlob.cast(data[INPUT_DATA]).length(), + scratchPad, + (short) 0); + data[OUTPUT_DATA] = KMByteBlob.instance(scratchPad, (short) 0, tmpVariables[1]); + } + // make response + tmpVariables[1] = KMArray.instance((short) 0); + tmpVariables[1] = KMKeyParameters.instance(tmpVariables[1]); + tmpVariables[2] = KMArray.instance((short) 4); + if (data[OUTPUT_DATA] == KMType.INVALID_VALUE) { + data[OUTPUT_DATA] = KMByteBlob.instance((short) 0); + } + KMArray.cast(tmpVariables[2]).add((short) 0, KMInteger.uint_16(KMError.OK)); + KMArray.cast(tmpVariables[2]).add((short) 1, KMInteger.uint_16(tmpVariables[0])); + KMArray.cast(tmpVariables[2]).add((short) 2, tmpVariables[1]); + KMArray.cast(tmpVariables[2]).add((short) 3, data[OUTPUT_DATA]); + // Encode the response + bufferLength = encoder.encode(tmpVariables[0], buffer, bufferStartOffset); + sendOutgoing(apdu); + } + + private void processBeginOperationCmd(APDU apdu) { + // Receive the incoming request fully from the master into buffer. + receiveIncoming(apdu); + byte[] scratchPad = apdu.getBuffer(); + tmpVariables[1] = KMArray.instance((short) 4); + // Arguments + tmpVariables[2] = KMKeyParameters.exp(); + KMArray.cast(tmpVariables[1]).add((short) 0, KMEnum.instance(KMType.PURPOSE)); + KMArray.cast(tmpVariables[1]).add((short) 1, KMByteBlob.exp()); + KMArray.cast(tmpVariables[1]).add((short) 2, tmpVariables[2]); + tmpVariables[3] = KMHardwareAuthToken.exp(); + KMArray.cast(tmpVariables[1]).add((short) 3, tmpVariables[3]); + // Decode the arguments + tmpVariables[2] = decoder.decode(tmpVariables[1], buffer, bufferStartOffset, bufferLength); + data[KEY_PARAMETERS] = KMArray.cast(tmpVariables[2]).get((short) 2); + data[KEY_BLOB] = KMArray.cast(tmpVariables[2]).get((short) 1); + tmpVariables[0] = KMArray.cast(tmpVariables[2]).get((short) 0); + tmpVariables[0] = KMEnum.cast(tmpVariables[0]).getVal(); + tmpVariables[4] = KMArray.cast(tmpVariables[2]).get((short) 3); + // Check for app id and app data. + data[APP_ID] = KMType.INVALID_VALUE; + data[APP_DATA] = KMType.INVALID_VALUE; + tmpVariables[3] = + KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.APPLICATION_ID, data[KEY_PARAMETERS]); + if (tmpVariables[3] != KMTag.INVALID_VALUE) { + data[APP_ID] = KMByteTag.cast(tmpVariables[3]).getValue(); + } + tmpVariables[3] = + KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.APPLICATION_DATA, data[KEY_PARAMETERS]); + if (tmpVariables[3] != KMTag.INVALID_VALUE) { + data[APP_DATA] = KMByteTag.cast(tmpVariables[3]).getValue(); + } + // Parse the encrypted blob and decrypt it. + parseEncryptedKeyBlob(scratchPad); + // Authorize the begin operation and reserve op - data[OP_HANDLE] will have the handle. + // It will also set data[IV] field if required. + authorizeBeginOperation(tmpVariables[4], scratchPad); + // Check for trusted confirmation - if required then set the signer in op state. + tmpVariables[0] = + KMKeyParameters.findTag( + KMType.BOOL_TAG, KMType.TRUSTED_CONFIRMATION_REQUIRED, data[HW_PARAMETERS]); + if (tmpVariables[0] != KMType.INVALID_VALUE) { + // get operation + KMOperationState op = repository.findOperation(data[OP_HANDLE]); + // get the hmac key + if (repository.getComputedHmacKey() == null) { + KMException.throwIt(KMError.OPERATION_CANCELLED); + } + // set the Hmac signer + op.setTrustedConfirmationSigner( + cryptoProvider.createHmacSigner( + MessageDigest.ALG_SHA_256, + repository.getComputedHmacKey(), + (short) 0, + (short) repository.getComputedHmacKey().length)); + } + // If the data[IV] is required to be returned. + if (data[IV] != KMType.INVALID_VALUE) { + // TODO confirm why this is needed + tmpVariables[2] = KMArray.instance((short) 1); + KMArray.cast(tmpVariables[2]).add((short) 0, data[IV]); + } else { + tmpVariables[2] = KMArray.instance((short) 0); + } + tmpVariables[1] = KMKeyParameters.instance(tmpVariables[2]); + tmpVariables[0] = KMArray.instance((short) 3); + KMArray.cast(tmpVariables[0]).add((short) 0, KMInteger.uint_16(KMError.OK)); + KMArray.cast(tmpVariables[0]).add((short) 1, tmpVariables[1]); + KMArray.cast(tmpVariables[0]).add((short) 2, data[OP_HANDLE]); + // Encode the response + bufferLength = encoder.encode(tmpVariables[0], buffer, bufferStartOffset); + sendOutgoing(apdu); + } + + private void authorizeBeginOperation(short hwToken, byte[] scratchPad) { + // Read purpose from key parameters - cannot be null. + short purpose = + KMEnumArrayTag.getValues(KMType.PURPOSE, data[KEY_PARAMETERS], scratchPad, (short) 0); + if (purpose == KMType.INVALID_VALUE) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + if (purpose != 1) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + purpose = scratchPad[0]; + if (!(KMEnumArrayTag.contains(KMType.PURPOSE, purpose, data[HW_PARAMETERS]))) { + KMException.throwIt(KMError.UNSUPPORTED_PURPOSE); + } + // Read digest from key parameters - can be null for EC. + short digest = + KMEnumArrayTag.getValues(KMType.DIGEST, data[KEY_PARAMETERS], scratchPad, (short) 0); + if (digest != KMType.INVALID_VALUE && digest != 1) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + digest = scratchPad[0]; + // Read padding from key parameters - can be null for AES/DES. + short padding = + KMEnumArrayTag.getValues(KMType.PADDING, data[KEY_PARAMETERS], scratchPad, (short) 0); + if (padding != KMType.INVALID_VALUE && padding != 1) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + padding = scratchPad[0]; + // Read Blockmode + short blockmode = + KMEnumArrayTag.getValues(KMType.BLOCK_MODE, data[KEY_PARAMETERS], scratchPad, (short) 0); + if (blockmode != KMType.INVALID_VALUE && blockmode != 1) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + blockmode = scratchPad[0]; + + // Max uses per boot + tmpVariables[0] = + KMKeyParameters.findTag(KMType.UINT_TAG, KMType.MAX_USES_PER_BOOT, data[HW_PARAMETERS]); + if (tmpVariables[0] != KMType.INVALID_VALUE) { + // get prescribed limit + tmpVariables[0] = KMIntegerTag.cast(tmpVariables[0]).getValue(); + authorizeKeyUsageForCount(tmpVariables[0]); + } + // Authorize UserId - auth timeout check cannot be done in javacard + tmpVariables[0] = + KMKeyParameters.findTag(KMType.ULONG_ARRAY_TAG, KMType.USER_SECURE_ID, data[HW_PARAMETERS]); + if (tmpVariables[0] != KMType.INVALID_VALUE) { + tmpVariables[0] = + KMKeyParameters.findTag(KMType.UINT_TAG, KMType.AUTH_TIMEOUT, data[HW_PARAMETERS]); + if (tmpVariables[0] != KMType.INVALID_VALUE) { + // check if hw token is empty - mac should not be empty. + tmpVariables[1] = KMHardwareAuthToken.cast(hwToken).getMac(); + if (KMByteBlob.cast(tmpVariables[1]).length() == 0) { + KMException.throwIt(KMError.INVALID_MAC_LENGTH); + } + authorizeUserId(hwToken, scratchPad); + } + } + // Authorize Caller Nonce - if caller nonce absent in key char and nonce present in + // key params then fail. + tmpVariables[2] = KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.NONCE, data[KEY_PARAMETERS]); + tmpVariables[0] = + KMKeyParameters.findTag(KMType.BOOL_TAG, KMType.CALLER_NONCE, data[HW_PARAMETERS]); + if (tmpVariables[0] == KMType.INVALID_VALUE) { + if (tmpVariables[2] != KMType.INVALID_VALUE) { + KMException.throwIt(KMError.CALLER_NONCE_PROHIBITED); + } + } + // Authorize Bootloader Only - assumption is that if this is is present then always fail. + tmpVariables[0] = + KMKeyParameters.findTag(KMType.BOOL_TAG, KMType.BOOTLOADER_ONLY, data[HW_PARAMETERS]); + if (tmpVariables[1] != KMType.INVALID_VALUE) { + KMException.throwIt(KMError.INVALID_KEY_BLOB); + } + tmpVariables[0] = KMEnumTag.getValue(KMType.ALGORITHM, data[HW_PARAMETERS]); + switch (tmpVariables[0]) { + case KMType.RSA: + authorizeRsa(purpose, digest, padding); + break; + case KMType.EC: + authorizeEC(purpose, digest); + break; + case KMType.DES: + case KMType.AES: + if (tmpVariables[2] == KMType.INVALID_VALUE) { + tmpVariables[2] = KMByteBlob.instance((short) 16); + cryptoProvider.newRandomNumber( + KMByteBlob.cast(tmpVariables[2]).getBuffer(), + KMByteBlob.cast(tmpVariables[2]).getStartOff(), + KMByteBlob.cast(tmpVariables[2]).length()); + } + data[IV] = tmpVariables[2]; + authorizeAesDes(tmpVariables[0], purpose, blockmode, padding); + break; + case KMType.HMAC: + authorizeHmac(purpose, digest); + break; + default: + KMException.throwIt(KMError.UNIMPLEMENTED); + break; + } + } + + private void authorizeGCM(short purpose, short padding) { + data[OP_HANDLE] = KMType.INVALID_VALUE; + if (purpose == KMType.SIGN || purpose == KMType.VERIFY) { + KMException.throwIt(KMError.UNSUPPORTED_PURPOSE); + } + if (purpose == KMType.ENCRYPT) { + purpose = KMCipher.MODE_ENCRYPT; + } else { + purpose = KMCipher.MODE_DECRYPT; + } + if (padding != KMType.PADDING_NONE) { + KMException.throwIt(KMError.INCOMPATIBLE_PADDING_MODE); + } + // Read and authorizeBeginOperation mac length + tmpVariables[0] = + KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.MAC_LENGTH, data[KEY_PARAMETERS]); + if (tmpVariables[0] == KMType.INVALID_VALUE) { + KMException.throwIt(KMError.MISSING_MAC_LENGTH); + } + if (tmpVariables[0] % 8 != 0) { + KMException.throwIt(KMError.INVALID_MAC_LENGTH); + } + tmpVariables[1] = + KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, data[HW_PARAMETERS]); + if (tmpVariables[0] < tmpVariables[1]) { + KMException.throwIt(KMError.INVALID_MAC_LENGTH); + } + if (tmpVariables[0] > 128) { + KMException.throwIt(KMError.INVALID_MAC_LENGTH); + } + KMOperationState op = repository.reserveOperation(); + if (op == null) { + KMException.throwIt(KMError.TOO_MANY_OPERATIONS); + } + op.setPurpose(purpose); + op.setKey(KMByteBlob.cast(data[SECRET]).getBuffer(), + KMByteBlob.cast(data[SECRET]).getStartOff(), + KMByteBlob.cast(data[SECRET]).length()); + op.setCipher( + cryptoProvider.createGCMCipher( + purpose, + KMByteBlob.cast(data[SECRET]).getBuffer(), + KMByteBlob.cast(data[SECRET]).getStartOff(), + KMByteBlob.cast(data[SECRET]).length(), + KMByteBlob.cast(data[IV]).getBuffer(), + KMByteBlob.cast(data[IV]).getStartOff(), + KMByteBlob.cast(data[IV]).length())); + data[OP_HANDLE] = op.getHandle(); + } + + private void authorizeHmac(short purpose, short digest) { + data[OP_HANDLE] = KMType.INVALID_VALUE; + if (purpose == KMType.ENCRYPT || purpose == KMType.DECRYPT) { + KMException.throwIt(KMError.UNSUPPORTED_PURPOSE); + } + if (digest == KMType.INVALID_VALUE) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + if (!(KMEnumArrayTag.contains(KMType.DIGEST, digest, data[HW_PARAMETERS]))) { + KMException.throwIt(KMError.UNSUPPORTED_DIGEST); + } + // Read and authorizeBeginOperation mac length + tmpVariables[0] = + KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.MAC_LENGTH, data[KEY_PARAMETERS]); + if (tmpVariables[0] == KMType.INVALID_VALUE) { + KMException.throwIt(KMError.MISSING_MAC_LENGTH); + } + if (tmpVariables[0] % 8 != 0) { + KMException.throwIt(KMError.INVALID_MAC_LENGTH); + } + tmpVariables[1] = + KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, data[HW_PARAMETERS]); + if (tmpVariables[0] < tmpVariables[1]) { + KMException.throwIt(KMError.INVALID_MAC_LENGTH); + } + + switch (digest) { + case KMType.MD5: + tmpVariables[2] = MessageDigest.ALG_MD5; + tmpVariables[1] = 128; + break; + case KMType.SHA1: + tmpVariables[2] = MessageDigest.ALG_SHA; + tmpVariables[1] = 160; + break; + case KMType.SHA2_224: + tmpVariables[2] = MessageDigest.ALG_SHA_224; + tmpVariables[1] = 224; + break; + case KMType.SHA2_256: + tmpVariables[2] = MessageDigest.ALG_SHA_256; + tmpVariables[1] = 256; + break; + case KMType.SHA2_384: + tmpVariables[2] = MessageDigest.ALG_SHA_384; + tmpVariables[1] = 384; + break; + case KMType.SHA2_512: + tmpVariables[2] = MessageDigest.ALG_SHA_512; + tmpVariables[1] = 512; + break; + default: + tmpVariables[1] = 512; + break; + } + if (tmpVariables[0] > tmpVariables[1]) { + KMException.throwIt(KMError.INVALID_MAC_LENGTH); + } + KMOperationState op = repository.reserveOperation(); + if (op == null) { + KMException.throwIt(KMError.TOO_MANY_OPERATIONS); + } + op.setPurpose(purpose); + op.setKey(KMByteBlob.cast(data[SECRET]).getBuffer(), + KMByteBlob.cast(data[SECRET]).getStartOff(), + KMByteBlob.cast(data[SECRET]).length()); + op.setSigner( + cryptoProvider.createHmacSigner( + tmpVariables[0], + KMByteBlob.cast(data[SECRET]).getBuffer(), + KMByteBlob.cast(data[SECRET]).getStartOff(), + KMByteBlob.cast(data[SECRET]).length())); + data[OP_HANDLE] = op.getHandle(); + } + + private void authorizeAesDes(short alg, short purpose, short blockmode, short padding) { + data[OP_HANDLE] = KMType.INVALID_VALUE; + if (purpose == KMType.SIGN || purpose == KMType.VERIFY) { + KMException.throwIt(KMError.UNSUPPORTED_PURPOSE); + } + if (blockmode == KMType.GCM) { + authorizeGCM(purpose, padding); + } + if (purpose == KMType.ENCRYPT) { + purpose = KMCipher.MODE_ENCRYPT; + } else { + purpose = KMCipher.MODE_DECRYPT; + } + KMOperationState op = null; + // padding must be no pad - PKCS7 is not supported in javacard + // TODO implement PCKS7 in cryptoProvider + if (padding == KMType.PADDING_NONE) { + padding = KMCipher.PAD_NULL; + } else if (padding == KMType.PKCS7) { + padding = KMCipher.PAD_PKCS7; + } else { + KMException.throwIt(KMError.INCOMPATIBLE_PADDING_MODE); + } + if (alg == KMType.AES) { + if (blockmode == KMType.CBC) { + tmpVariables[0] = KMCipher.CIPHER_AES_CBC; + } else if (blockmode == KMType.ECB) { + tmpVariables[0] = KMCipher.CIPHER_AES_ECB; + } else { + // data[CIPHER_ALGORITHM] = Cipher.CIPHER_AES_CTR; // Not supported in 3.0.5 + // TODO change this once we can test. + KMException.throwIt(KMError.UNSUPPORTED_BLOCK_MODE); + } + op = repository.reserveOperation(); + } else if (alg == KMType.DES) { + if (blockmode == KMType.CBC) { + tmpVariables[0] = KMCipher.CIPHER_DES_CBC; + } else if (blockmode == KMType.ECB) { + tmpVariables[0] = KMCipher.CIPHER_DES_ECB; + } else { + // data[CIPHER_ALGORITHM] = Cipher.CIPHER_DES_CTR; // Not supported in 3.0.5 + // TODO change this once we can test. + KMException.throwIt(KMError.UNSUPPORTED_BLOCK_MODE); + } + op = repository.reserveOperation(); + } else { + KMException.throwIt(KMError.INCOMPATIBLE_ALGORITHM); + } + if (op == null) { + KMException.throwIt(KMError.TOO_MANY_OPERATIONS); + } + op.setPurpose(purpose); + op.setKey(KMByteBlob.cast(data[SECRET]).getBuffer(), + KMByteBlob.cast(data[SECRET]).getStartOff(), + KMByteBlob.cast(data[SECRET]).length()); + op.setCipher( + cryptoProvider.createSymmetricCipher( + tmpVariables[0], + padding, + purpose, + KMByteBlob.cast(data[SECRET]).getBuffer(), + KMByteBlob.cast(data[SECRET]).getStartOff(), + KMByteBlob.cast(data[SECRET]).length(), + KMByteBlob.cast(data[IV]).getBuffer(), + KMByteBlob.cast(data[IV]).getStartOff(), + KMByteBlob.cast(data[IV]).length())); + data[OP_HANDLE] = op.getHandle(); } - private void processAbortOperationCmd(APDU apdu) {} - - private void processFinishOperationCmd(APDU apdu) {} - - private void processUpdateOperationCmd(APDU apdu) {} + private void authorizeEC(short purpose, short digest) { + data[OP_HANDLE] = KMType.INVALID_VALUE; + // purpose will be always sign. + // Only ECDSA signing supported + if (purpose == KMType.ENCRYPT || purpose == KMType.VERIFY || purpose == KMType.DECRYPT) { + KMException.throwIt(KMError.UNSUPPORTED_PURPOSE); + } + switch (digest) { + case KMType.DIGEST_NONE: + tmpVariables[0] = MessageDigest.ALG_NULL; + break; + case KMType.SHA1: + tmpVariables[0] = MessageDigest.ALG_SHA; + break; + case KMType.SHA2_224: + tmpVariables[0] = MessageDigest.ALG_SHA_224; + break; + case KMType.SHA2_256: + tmpVariables[0] = MessageDigest.ALG_SHA_256; + break; + case KMType.SHA2_384: + tmpVariables[0] = MessageDigest.ALG_SHA_384; + break; + case KMType.SHA2_512: + tmpVariables[0] = MessageDigest.ALG_SHA_512; + break; + default: + KMException.throwIt(KMError.INCOMPATIBLE_DIGEST); + break; + } + KMOperationState op = repository.reserveOperation(); + if (op == null) { + KMException.throwIt(KMError.TOO_MANY_OPERATIONS); + } + op.setPurpose(purpose); + op.setKey(KMByteBlob.cast(data[SECRET]).getBuffer(), + KMByteBlob.cast(data[SECRET]).getStartOff(), + KMByteBlob.cast(data[SECRET]).length()); + op.setSigner( + cryptoProvider.createEcSigner( + tmpVariables[0], + KMByteBlob.cast(data[SECRET]).getBuffer(), + KMByteBlob.cast(data[SECRET]).getStartOff(), + KMByteBlob.cast(data[SECRET]).length())); + data[OP_HANDLE] = op.getHandle(); + } - private void processBeginOperationCmd(APDU apdu) {} + private void authorizeRsa(short purpose, short digest, short padding) { + KMOperationState op = null; + data[OP_HANDLE] = KMType.INVALID_VALUE; + if (purpose == KMType.ENCRYPT || purpose == KMType.VERIFY) { + KMException.throwIt(KMError.UNSUPPORTED_PURPOSE); + } + switch (purpose) { + case KMType.DECRYPT: + tmpVariables[0] = KMCipher.CIPHER_RSA; + if (padding == KMType.PADDING_NONE) { + // There is no way to select digest with no padding. Digest is also none. + padding = KMCipher.PAD_NOPAD; + } else if (padding != KMType.RSA_OAEP) { + KMException.throwIt(KMError.INCOMPATIBLE_PADDING_MODE); + } else { + // There is no way to ascertain MGF1 and SHA1 in javacard - this should be part of PKCS1. + switch (digest) { + case KMType.DIGEST_NONE: + KMException.throwIt(KMError.UNSUPPORTED_DIGEST); + break; + case KMType.SHA2_224: + padding = KMCipher.PAD_PKCS1_OAEP_SHA224; + break; + case KMType.SHA2_256: + padding = KMCipher.PAD_PKCS1_OAEP_SHA256; + break; + case KMType.SHA2_384: + padding = KMCipher.PAD_PKCS1_OAEP_SHA384; + break; + case KMType.SHA2_512: + padding = KMCipher.PAD_PKCS1_OAEP_SHA512; + break; + default: + KMException.throwIt(KMError.INCOMPATIBLE_DIGEST); + break; + } + } + op = repository.reserveOperation(); + if (op == null) { + KMException.throwIt(KMError.TOO_MANY_OPERATIONS); + } + op.setPurpose(purpose); + op.setKey(KMByteBlob.cast(data[SECRET]).getBuffer(), + KMByteBlob.cast(data[SECRET]).getStartOff(), + KMByteBlob.cast(data[SECRET]).length()); + op.setCipher( + cryptoProvider.createRsaDecrypt( + tmpVariables[0], + padding, + KMByteBlob.cast(data[SECRET]).getBuffer(), + KMByteBlob.cast(data[SECRET]).getStartOff(), + KMByteBlob.cast(data[SECRET]).length(), + KMByteBlob.cast(data[PUB_KEY]).getBuffer(), + KMByteBlob.cast(data[PUB_KEY]).getStartOff(), + KMByteBlob.cast(data[PUB_KEY]).length())); + break; + case KMType.SIGN: + if (padding == KMType.PADDING_NONE) { + if(digest == KMType.DIGEST_NONE){ + tmpVariables[0] = MessageDigest.ALG_NULL; + padding = KMCipher.PAD_NOPAD; + }else{ + KMException.throwIt(KMError.INCOMPATIBLE_DIGEST); + } + } else if (padding != KMType.RSA_PKCS1_1_5_SIGN) { + KMException.throwIt(KMError.INCOMPATIBLE_PADDING_MODE); + } else { + padding = KMCipher.PAD_PKCS1; + switch (digest) { // TODO No digest not supported at this moment + case KMType.DIGEST_NONE: + tmpVariables[0] = MessageDigest.ALG_NULL; + break; + case KMType.SHA2_224: + tmpVariables[0] = MessageDigest.ALG_SHA_224; + break; + case KMType.SHA2_256: + tmpVariables[0] = MessageDigest.ALG_SHA_256; + break; + case KMType.SHA2_384: + tmpVariables[0] = MessageDigest.ALG_SHA_384; + break; + case KMType.SHA2_512: + tmpVariables[0] = MessageDigest.ALG_SHA_512; + break; + default: + KMException.throwIt(KMError.INCOMPATIBLE_DIGEST); + break; + } + } + op = repository.reserveOperation(); + if (op == null) { + KMException.throwIt(KMError.TOO_MANY_OPERATIONS); + } + op.setPurpose(purpose); + op.setKey(KMByteBlob.cast(data[SECRET]).getBuffer(), + KMByteBlob.cast(data[SECRET]).getStartOff(), + KMByteBlob.cast(data[SECRET]).length()); + op.setSigner( + cryptoProvider.createRsaSigner( + tmpVariables[0], + padding, + KMByteBlob.cast(data[SECRET]).getBuffer(), + KMByteBlob.cast(data[SECRET]).getStartOff(), + KMByteBlob.cast(data[SECRET]).length(), + KMByteBlob.cast(data[PUB_KEY]).getBuffer(), + KMByteBlob.cast(data[PUB_KEY]).getStartOff(), + KMByteBlob.cast(data[PUB_KEY]).length())); + break; + default: + KMException.throwIt(KMError.UNSUPPORTED_PURPOSE); + break; + } + data[OP_HANDLE] = op.getHandle(); + } - private void processGetKeyCharacteristicsCmd(APDU apdu) { - // Receive the incoming request fully from the master. - receiveIncoming(apdu); - // Re-purpose the apdu buffer as scratch pad. - byte[] scratchPad = apdu.getBuffer(); - // Arguments - tmpVariables[0] = KMArray.instance((short) 3); - KMArray.cast(tmpVariables[0]).add((short) 0, KMByteBlob.exp()); - KMArray.cast(tmpVariables[0]).add((short) 1, KMByteBlob.exp()); - KMArray.cast(tmpVariables[0]).add((short) 2, KMByteBlob.exp()); - // Decode the arguments - tmpVariables[0] = decoder.decode(tmpVariables[0], buffer, bufferStartOffset, bufferLength); - data[KEY_BLOB] = KMArray.cast(tmpVariables[0]).get((short) 0); - data[APP_ID] = KMArray.cast(tmpVariables[0]).get((short) 1); - data[APP_DATA] = KMArray.cast(tmpVariables[0]).get((short) 2); - if(KMByteBlob.cast(data[APP_ID]).length() == 2){ - if(Util.getShort(repository.getHeap(),KMByteBlob.cast(data[APP_ID]).getStartOff()) == KMType.INVALID_VALUE){ - data[APP_ID] = KMType.INVALID_VALUE; + private void authorizeUserId(short hwToken, byte[] scratchPad) { + validateHwToken(hwToken, scratchPad); + tmpVariables[0] = KMHardwareAuthToken.cast(hwToken).getUserId(); + if (KMInteger.cast(tmpVariables[0]).isZero()) { + tmpVariables[0] = KMHardwareAuthToken.cast(hwToken).getAuthenticatorId(); + if (KMInteger.cast(tmpVariables[0]).isZero()) { + KMException.throwIt(KMError.KEY_USER_NOT_AUTHENTICATED); } } - if(KMByteBlob.cast(data[APP_DATA]).length() == 2){ - if(Util.getShort(repository.getHeap(),KMByteBlob.cast(data[APP_DATA]).getStartOff()) == KMType.INVALID_VALUE){ - data[APP_DATA] = KMType.INVALID_VALUE; - } + // check user secure id + if (!KMIntegerArrayTag.contains(KMType.USER_SECURE_ID, tmpVariables[0], data[HW_PARAMETERS])) { + KMException.throwIt(KMError.KEY_USER_NOT_AUTHENTICATED); + } + // check auth type + tmpVariables[1] = KMEnumTag.getValue(KMType.USER_AUTH_TYPE, data[HW_PARAMETERS]); + tmpVariables[2] = KMHardwareAuthToken.cast(hwToken).getHwAuthenticatorType(); + tmpVariables[2] = KMEnum.cast(tmpVariables[2]).getVal(); + if (((byte) tmpVariables[2] & (byte) tmpVariables[1]) == 0) { + KMException.throwIt(KMError.KEY_USER_NOT_AUTHENTICATED); } - // Parse Key Blob - parseEncryptedKeyBlob(scratchPad); - // Check Version and Patch Level - checkVersionAndPatchLevel(scratchPad); - // make response. - tmpVariables[0] = KMArray.instance((short) 2); - KMArray.cast(tmpVariables[0]).add((short) 0, KMInteger.uint_16(KMError.OK)); - KMArray.cast(tmpVariables[0]).add((short) 1, data[KEY_CHARACTERISTICS]); - // Encode the response - bufferLength = encoder.encode(tmpVariables[0], buffer, bufferStartOffset); - sendOutgoing(apdu); } - private void processGetHmacSharingParamCmd(APDU apdu) {} - - private void processVerifyAuthenticationCmd(APDU apdu) {} - - private void processDestroyAttIdsCmd(APDU apdu) {} - - private void processComputeSharedHmacCmd(APDU apdu) {} - - private void processDeleteAllKeysCmd(APDU apdu) {} - - private void processDeleteKeyCmd(APDU apdu) {} - - private void processUpgradeKeyCmd(APDU apdu) {} - - private void processAttestKeyCmd(APDU apdu) {} + private void validateHwToken(short hwToken, byte[] scratchPad) { + // CBOR Encoding is always big endian + short ptr = KMHardwareAuthToken.cast(hwToken).getMac(); + short len = 0; + // If mac length is zero then token is empty. + if (KMByteBlob.cast(ptr).length() == 0) { + return; + } + // add 0 + Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 37, (byte) 0); + len = 1; + // concatenate challenge - 8 bytes + ptr = KMHardwareAuthToken.cast(hwToken).getChallenge(); + KMInteger.cast(ptr) + .value(scratchPad, (short) (len + (short) (8 - KMInteger.cast(ptr).length()))); + len += 8; + // concatenate user id - 8 bytes + ptr = KMHardwareAuthToken.cast(hwToken).getUserId(); + KMInteger.cast(tmpVariables[0]) + .value(scratchPad, (short) (len + (short) (8 - KMInteger.cast(ptr).length()))); + len += 8; + // concatenate authenticator id - 8 bytes + ptr = KMHardwareAuthToken.cast(hwToken).getAuthenticatorId(); + KMInteger.cast(tmpVariables[0]) + .value(scratchPad, (short) (len + (short) (8 - KMInteger.cast(ptr).length()))); + len += 8; + // concatenate authenticator type - 4 bytes + ptr = KMHardwareAuthToken.cast(hwToken).getHwAuthenticatorType(); + scratchPad[(short) (len + 3)] = KMEnum.cast(ptr).getVal(); + len += 4; + // concatenate timestamp -8 bytes + ptr = KMHardwareAuthToken.cast(hwToken).getTimestamp(); + KMInteger.cast(tmpVariables[0]) + .value(scratchPad, (short) (len + (short) (8 - KMInteger.cast(ptr).length()))); + len += 8; + // hmac the data + HMACKey key = + cryptoProvider.createHMACKey( + repository.getComputedHmacKey(), + (short) 0, + (short) repository.getComputedHmacKey().length); + ptr = KMHardwareAuthToken.cast(hwToken).getMac(); + boolean verified = + cryptoProvider.hmacVerify(key, scratchPad, (short) 0, len, + KMByteBlob.cast(ptr).getBuffer(), + KMByteBlob.cast(ptr).getStartOff(), + KMByteBlob.cast(ptr).length()); + if(!verified){ + KMException.throwIt(KMError.VERIFICATION_FAILED); + } +/* + len = + cryptoProvider.hmac(key, scratchPad, (short) 0, len, scratchPad, (short) (len + 1) ); + // Compare mac. + ptr = KMHardwareAuthToken.cast(hwToken).getMac(); + if (len != KMByteBlob.cast(ptr).length()) { + KMException.throwIt(KMError.INVALID_MAC_LENGTH); + } + if (Util.arrayCompare( + scratchPad, + (short) 38, + KMByteBlob.cast(ptr).getBuffer(), + KMByteBlob.cast(ptr).getStartOff(), + len) + != 0) { + KMException.throwIt(KMError.VERIFICATION_FAILED); + } + */ + } - private void processExportKeyCmd(APDU apdu) {} + private void authorizeUserIdPerKeyOperation(short hwToken, byte[] scratchPad) { + tmpVariables[0] = KMHardwareAuthToken.cast(hwToken).getChallenge(); + if (KMInteger.compare(data[OP_HANDLE], tmpVariables[0]) != 0) { + KMException.throwIt(KMError.KEY_USER_NOT_AUTHENTICATED); + } + authorizeUserId(hwToken, scratchPad); + } - private void processImportWrappedKeyCmd(APDU apdu) {} + private void authorizeKeyUsageForCount(short limit) { + // get current counter + // TODO currently only short counter supported - max count 32K. + short val = repository.getRateLimitedKeyCount(data[AUTH_TAG]); + if (val != KMType.INVALID_VALUE) { + short count = KMInteger.uint_16(val); + // compare 32 bit values - is current counter less then prescribed limit + if (KMInteger.compare(count, limit) != -1) { + KMException.throwIt(KMError.KEY_MAX_OPS_EXCEEDED); + } + // increment the counter and store it back. + val++; + repository.setRateLimitedKeyCount(data[AUTH_TAG], val); + } else { + KMException.throwIt(KMError.UNKNOWN_ERROR); + } + } private void processImportKeyCmd(APDU apdu) { if (repository.keyBlobCount > repository.MAX_BLOB_STORAGE) { @@ -485,7 +1950,7 @@ private void processImportKeyCmd(APDU apdu) { // Receive the incoming request fully from the master into buffer. receiveIncoming(apdu); byte[] scratchPad = apdu.getBuffer(); - tmpVariables[1] = KMArray.instance((short)3); + tmpVariables[1] = KMArray.instance((short) 3); // Arguments tmpVariables[2] = KMKeyParameters.exp(); KMArray.cast(tmpVariables[1]).add((short) 0, tmpVariables[2]); @@ -496,11 +1961,16 @@ private void processImportKeyCmd(APDU apdu) { data[KEY_PARAMETERS] = KMArray.cast(tmpVariables[2]).get((short) 0); tmpVariables[3] = KMArray.cast(tmpVariables[2]).get((short) 1); data[IMPORTED_KEY_BLOB] = KMArray.cast(tmpVariables[2]).get((short) 2); - // Keyformat must be RAW format - X509 and PKCS8 not implemented. + // Key format must be RAW format - X509 and PKCS8 not implemented. tmpVariables[3] = KMEnum.cast(tmpVariables[3]).getVal(); if (tmpVariables[3] != KMType.RAW) { KMException.throwIt(KMError.UNIMPLEMENTED); } + data[ORIGIN] = KMType.IMPORTED; + importKey(apdu, scratchPad); + } + + private void importKey(APDU apdu, byte[] scratchPad) { // get algorithm tmpVariables[3] = KMEnumTag.getValue(KMType.ALGORITHM, data[KEY_PARAMETERS]); if (tmpVariables[3] == KMType.INVALID_VALUE) { @@ -528,7 +1998,6 @@ private void processImportKeyCmd(APDU apdu) { break; } // create key blob - data[ORIGIN] = KMType.IMPORTED; createEncryptedKeyBlob(scratchPad); // persist auth tag for rollback resistance. repository.persistAuthTag(data[AUTH_TAG]); @@ -549,61 +2018,57 @@ private void importECKeys(byte[] scratchPad) { KMArray.cast(tmpVariables[0]).add((short) 1, KMByteBlob.exp()); // public key KMArray.cast(tmpVariables[0]).add((short) 2, KMEnumTag.exp()); // curve tmpVariables[0] = - decoder.decode( - tmpVariables[0], - KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).getBuffer(), - KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).getStartOff(), - KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).length()); + decoder.decode( + tmpVariables[0], + KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).getBuffer(), + KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).getStartOff(), + KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).length()); data[SECRET] = KMArray.cast(tmpVariables[0]).get((short) 0); data[PUB_KEY] = KMArray.cast(tmpVariables[0]).get((short) 1); tmpVariables[1] = KMArray.cast(tmpVariables[0]).get((short) 2); tmpVariables[1] = KMEnumTag.cast(tmpVariables[1]).getValue(); // curve must be P_256 - if(tmpVariables[1] != KMType.P_256){ + if (tmpVariables[1] != KMType.P_256) { KMException.throwIt(KMError.UNSUPPORTED_EC_CURVE); } // initialize 256 bit p256 key for given private key and public key. ECPrivateKey ecKey = - cryptoProvider.createEcPrivateKey( - KMByteBlob.cast(data[PUB_KEY]).getBuffer(), - KMByteBlob.cast(data[PUB_KEY]).getStartOff(), - KMByteBlob.cast(data[PUB_KEY]).length(), - KMByteBlob.cast(data[SECRET]).getBuffer(), - KMByteBlob.cast(data[SECRET]).getStartOff(), - KMByteBlob.cast(data[SECRET]).length() - ); + cryptoProvider.createEcKey( + KMByteBlob.cast(data[SECRET]).getBuffer(), + KMByteBlob.cast(data[SECRET]).getStartOff(), + KMByteBlob.cast(data[SECRET]).length()); tmpVariables[4] = 0; // index for update list in scratchPad // check whether the keysize tag is present in key parameters. tmpVariables[2] = - KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); + KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); if (tmpVariables[2] != KMType.INVALID_VALUE) { if (tmpVariables[2] != 256) { KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH); } - }else{ + } else { // add the key size to scratchPad - tmpVariables[5] = KMInteger.uint_16((short)256); - tmpVariables[6] = KMIntegerTag.instance(KMType.UINT_TAG,KMType.KEYSIZE, tmpVariables[5]); - Util.setShort(scratchPad,tmpVariables[4],tmpVariables[6]); - tmpVariables[4] +=2; + tmpVariables[5] = KMInteger.uint_16((short) 256); + tmpVariables[6] = KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, tmpVariables[5]); + Util.setShort(scratchPad, tmpVariables[4], tmpVariables[6]); + tmpVariables[4] += 2; } // check the curve if present in key parameters. - tmpVariables[3] = KMEnumTag.getValue(KMType.ECCURVE,data[KEY_PARAMETERS]); - if(tmpVariables[3] != KMType.INVALID_VALUE){ - if(tmpVariables[3] != tmpVariables[1]){ + tmpVariables[3] = KMEnumTag.getValue(KMType.ECCURVE, data[KEY_PARAMETERS]); + if (tmpVariables[3] != KMType.INVALID_VALUE) { + if (tmpVariables[3] != tmpVariables[1]) { KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH); } - }else{ + } else { // add the curve to scratchPad - tmpVariables[5] = KMEnumTag.instance(KMType.ECCURVE,KMType.P_256); - Util.setShort(scratchPad,tmpVariables[4],tmpVariables[5]); - tmpVariables[4] +=2; + tmpVariables[5] = KMEnumTag.instance(KMType.ECCURVE, KMType.P_256); + Util.setShort(scratchPad, tmpVariables[4], tmpVariables[5]); + tmpVariables[4] += 2; } // add scratch pad to key parameters updateKeyParameters(scratchPad, tmpVariables[4]); // validate updated key parameters. validateECKeys(scratchPad); - data[KEY_BLOB] = KMArray.instance((short)5); + data[KEY_BLOB] = KMArray.instance((short) 5); KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_PUB_KEY, data[PUB_KEY]); } @@ -612,39 +2077,39 @@ private void importHmacKey(byte[] scratchPad) { tmpVariables[0] = KMArray.instance((short) 1); KMArray.cast(tmpVariables[0]).add((short) 0, KMByteBlob.exp()); // secret tmpVariables[0] = - decoder.decode( - tmpVariables[0], - KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).getBuffer(), - KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).getStartOff(), - KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).length()); + decoder.decode( + tmpVariables[0], + KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).getBuffer(), + KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).getStartOff(), + KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).length()); data[SECRET] = KMArray.cast(tmpVariables[0]).get((short) 0); // create HMAC key of up to 512 bit - HMACKey hmacKey = cryptoProvider.createHMACKey( - KMByteBlob.cast(data[SECRET]).getBuffer(), - KMByteBlob.cast(data[SECRET]).getStartOff(), - KMByteBlob.cast(data[SECRET]).length() - ); + HMACKey hmacKey = + cryptoProvider.createHMACKey( + KMByteBlob.cast(data[SECRET]).getBuffer(), + KMByteBlob.cast(data[SECRET]).getStartOff(), + KMByteBlob.cast(data[SECRET]).length()); tmpVariables[4] = 0; // index in scratchPad for update params // check the keysize tag if present in key parameters. tmpVariables[2] = - KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); + KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); if (tmpVariables[2] != KMType.INVALID_VALUE) { - if (!(tmpVariables[2] > 64 && tmpVariables[2] <= 512 && tmpVariables[2]%8 ==0)) { + if (!(tmpVariables[2] >= 64 && tmpVariables[2] <= 512 && tmpVariables[2] % 8 == 0)) { KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH); } - }else{ + } else { // add the key size to scratchPad tmpVariables[5] = KMInteger.uint_16(KMByteBlob.cast(data[SECRET]).length()); - tmpVariables[6] = KMIntegerTag.instance(KMType.UINT_TAG,KMType.KEYSIZE, tmpVariables[5]); - Util.setShort(scratchPad,tmpVariables[4],tmpVariables[6]); - tmpVariables[4] +=2; + tmpVariables[6] = KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, tmpVariables[5]); + Util.setShort(scratchPad, tmpVariables[4], tmpVariables[6]); + tmpVariables[4] += 2; } // update the key parameters list updateKeyParameters(scratchPad, tmpVariables[4]); // validate HMAC Key parameters validateHmacKey(scratchPad); - data[KEY_BLOB] = KMArray.instance((short)4); + data[KEY_BLOB] = KMArray.instance((short) 4); } private void importTDESKey(byte[] scratchPad) { @@ -652,38 +2117,38 @@ private void importTDESKey(byte[] scratchPad) { tmpVariables[0] = KMArray.instance((short) 1); KMArray.cast(tmpVariables[0]).add((short) 0, KMByteBlob.exp()); // secret tmpVariables[0] = - decoder.decode( - tmpVariables[0], - KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).getBuffer(), - KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).getStartOff(), - KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).length()); + decoder.decode( + tmpVariables[0], + KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).getBuffer(), + KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).getStartOff(), + KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).length()); data[SECRET] = KMArray.cast(tmpVariables[0]).get((short) 0); - DESKey desKey = cryptoProvider.createTDESKey( - KMByteBlob.cast(data[SECRET]).getBuffer(), - KMByteBlob.cast(data[SECRET]).getStartOff(), - KMByteBlob.cast(data[SECRET]).length() - ); + DESKey desKey = + cryptoProvider.createTDESKey( + KMByteBlob.cast(data[SECRET]).getBuffer(), + KMByteBlob.cast(data[SECRET]).getStartOff(), + KMByteBlob.cast(data[SECRET]).length()); tmpVariables[4] = 0; // index in scratchPad for update params // check the keysize tag if present in key parameters. tmpVariables[2] = - KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); + KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); if (tmpVariables[2] != KMType.INVALID_VALUE) { if (tmpVariables[2] != 168) { KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH); } - }else{ + } else { // add the key size to scratchPad - tmpVariables[5] = KMInteger.uint_16((short)168); - tmpVariables[6] = KMIntegerTag.instance(KMType.UINT_TAG,KMType.KEYSIZE, tmpVariables[5]); - Util.setShort(scratchPad,tmpVariables[4],tmpVariables[6]); - tmpVariables[4] +=2; + tmpVariables[5] = KMInteger.uint_16((short) 168); + tmpVariables[6] = KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, tmpVariables[5]); + Util.setShort(scratchPad, tmpVariables[4], tmpVariables[6]); + tmpVariables[4] += 2; } // update the key parameters list updateKeyParameters(scratchPad, tmpVariables[4]); // validate TDES Key parameters validateTDESKey(scratchPad); - data[KEY_BLOB] = KMArray.instance((short)4); + data[KEY_BLOB] = KMArray.instance((short) 4); } private void importAESKey(byte[] scratchPad) { @@ -691,39 +2156,39 @@ private void importAESKey(byte[] scratchPad) { tmpVariables[0] = KMArray.instance((short) 1); KMArray.cast(tmpVariables[0]).add((short) 0, KMByteBlob.exp()); // secret tmpVariables[0] = - decoder.decode( - tmpVariables[0], - KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).getBuffer(), - KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).getStartOff(), - KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).length()); + decoder.decode( + tmpVariables[0], + KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).getBuffer(), + KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).getStartOff(), + KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).length()); data[SECRET] = KMArray.cast(tmpVariables[0]).get((short) 0); // create 128 or 256 bit AES key - AESKey aesKey = cryptoProvider.createAESKey( - KMByteBlob.cast(data[SECRET]).getBuffer(), - KMByteBlob.cast(data[SECRET]).getStartOff(), - KMByteBlob.cast(data[SECRET]).length() - ); + AESKey aesKey = + cryptoProvider.createAESKey( + KMByteBlob.cast(data[SECRET]).getBuffer(), + KMByteBlob.cast(data[SECRET]).getStartOff(), + KMByteBlob.cast(data[SECRET]).length()); tmpVariables[4] = 0; // index in scratchPad for update params // check the keysize tag if present in key parameters. tmpVariables[2] = - KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); + KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); if (tmpVariables[2] != KMType.INVALID_VALUE) { if (tmpVariables[2] != 128 && tmpVariables[2] != 256) { KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH); } - }else{ + } else { // add the key size to scratch pad // add the key size to scratchPad tmpVariables[5] = KMInteger.uint_16(KMByteBlob.cast(data[SECRET]).length()); - tmpVariables[6] = KMIntegerTag.instance(KMType.UINT_TAG,KMType.KEYSIZE, tmpVariables[5]); - Util.setShort(scratchPad,tmpVariables[4],tmpVariables[6]); - tmpVariables[4] +=2; + tmpVariables[6] = KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, tmpVariables[5]); + Util.setShort(scratchPad, tmpVariables[4], tmpVariables[6]); + tmpVariables[4] += 2; } // update the key parameters list updateKeyParameters(scratchPad, tmpVariables[4]); // validate AES Key parameters validateAESKey(scratchPad); - data[KEY_BLOB] = KMArray.instance((short)4); + data[KEY_BLOB] = KMArray.instance((short) 4); } private void importRSAKey(byte[] scratchPad) { @@ -742,83 +2207,84 @@ private void importRSAKey(byte[] scratchPad) { tmpVariables[4] = 0; // index in scratchPad for update parameters. // validate public exponent if present in key params - it must be 0x010001 tmpVariables[2] = - KMIntegerTag.getValue( - scratchPad, - (short) 10, // using offset 10 as first 10 bytes reserved for update params - KMType.ULONG_TAG, - KMType.RSA_PUBLIC_EXPONENT, - data[KEY_PARAMETERS]); + KMIntegerTag.getValue( + scratchPad, + (short) 10, // using offset 10 as first 10 bytes reserved for update params + KMType.ULONG_TAG, + KMType.RSA_PUBLIC_EXPONENT, + data[KEY_PARAMETERS]); if (tmpVariables[2] != KMTag.INVALID_VALUE) { - if ( tmpVariables[2] != 4 || Util.getShort(scratchPad, (short) 10) != 0x01 - || Util.getShort(scratchPad, (short) 12) != 0x01) { + if (tmpVariables[2] != 4 + || Util.getShort(scratchPad, (short) 10) != 0x01 + || Util.getShort(scratchPad, (short) 12) != 0x01) { KMException.throwIt(KMError.INVALID_ARGUMENT); } - }else{ + } else { // add public exponent to scratchPad - Util.setShort(scratchPad,(short)10, (short)0x01); - Util.setShort(scratchPad,(short)12, (short)0x01); - tmpVariables[5] = KMInteger.uint_32(scratchPad,(short)10); - tmpVariables[6] = KMIntegerTag.instance(KMType.ULONG_TAG,KMType.RSA_PUBLIC_EXPONENT, tmpVariables[5]); - Util.setShort(scratchPad,tmpVariables[4],tmpVariables[6]); - tmpVariables[4] +=2; + Util.setShort(scratchPad, (short) 10, (short) 0x01); + Util.setShort(scratchPad, (short) 12, (short) 0x01); + tmpVariables[5] = KMInteger.uint_32(scratchPad, (short) 10); + tmpVariables[6] = + KMIntegerTag.instance(KMType.ULONG_TAG, KMType.RSA_PUBLIC_EXPONENT, tmpVariables[5]); + Util.setShort(scratchPad, tmpVariables[4], tmpVariables[6]); + tmpVariables[4] += 2; } // initialize 2048 bit private key for given private exp and modulus. RSAPrivateKey rsaKey = - cryptoProvider.createRsaPrivateKey( - KMByteBlob.cast(data[PUB_KEY]).getBuffer(), - KMByteBlob.cast(data[PUB_KEY]).getStartOff(), - KMByteBlob.cast(data[PUB_KEY]).length(), - KMByteBlob.cast(data[SECRET]).getBuffer(), - KMByteBlob.cast(data[SECRET]).getStartOff(), - KMByteBlob.cast(data[SECRET]).length() - ); + cryptoProvider.createRsaKey( + KMByteBlob.cast(data[PUB_KEY]).getBuffer(), + KMByteBlob.cast(data[PUB_KEY]).getStartOff(), + KMByteBlob.cast(data[PUB_KEY]).length(), + KMByteBlob.cast(data[SECRET]).getBuffer(), + KMByteBlob.cast(data[SECRET]).getStartOff(), + KMByteBlob.cast(data[SECRET]).length()); // check the keysize tag if present in key parameters. tmpVariables[2] = - KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); + KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); if (tmpVariables[2] != KMType.INVALID_VALUE) { if (tmpVariables[2] != 2048) { KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH); } - }else{ + } else { // add the key size to scratchPad - tmpVariables[5] = KMInteger.uint_16((short)2048); - tmpVariables[6] = KMIntegerTag.instance(KMType.UINT_TAG,KMType.KEYSIZE, tmpVariables[5]); - Util.setShort(scratchPad,tmpVariables[4],tmpVariables[6]); - tmpVariables[4] +=2; + tmpVariables[5] = KMInteger.uint_16((short) 2048); + tmpVariables[6] = KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, tmpVariables[5]); + Util.setShort(scratchPad, tmpVariables[4], tmpVariables[6]); + tmpVariables[4] += 2; } // update the key parameters list updateKeyParameters(scratchPad, tmpVariables[4]); // validate RSA Key parameters validateRSAKey(scratchPad); - data[KEY_BLOB] = KMArray.instance((short)5); + data[KEY_BLOB] = KMArray.instance((short) 5); KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_PUB_KEY, data[PUB_KEY]); } - private void updateKeyParameters(byte[] ptrArr, short len){ - if(len == 0) { + private void updateKeyParameters(byte[] ptrArr, short len) { + if (len == 0) { return; // nothing to update } // Create Update Param array and copy current params tmpVariables[0] = KMKeyParameters.cast(data[KEY_PARAMETERS]).getVals(); - tmpVariables[1] = (short)(KMArray.cast(tmpVariables[0]).length()+(short)(len/2)); - tmpVariables[1] = KMArray.instance(tmpVariables[1]);// update params + tmpVariables[1] = (short) (KMArray.cast(tmpVariables[0]).length() + (short) (len / 2)); + tmpVariables[1] = KMArray.instance(tmpVariables[1]); // update params tmpVariables[2] = KMArray.cast(tmpVariables[0]).length(); tmpVariables[3] = 0; // copy the existing key parameters to updated array - while(tmpVariables[3] < tmpVariables[2]){ + while (tmpVariables[3] < tmpVariables[2]) { tmpVariables[4] = KMArray.cast(tmpVariables[0]).get(tmpVariables[3]); - KMArray.cast(tmpVariables[1]).add(tmpVariables[3],tmpVariables[4]); + KMArray.cast(tmpVariables[1]).add(tmpVariables[3], tmpVariables[4]); tmpVariables[3]++; } // copy new parameters to updated array tmpVariables[2] = KMArray.cast(tmpVariables[1]).length(); tmpVariables[5] = 0; // index in ptrArr - while(tmpVariables[3] < tmpVariables[2]){ - tmpVariables[4] = Util.getShort(ptrArr,tmpVariables[5]); - KMArray.cast(tmpVariables[1]).add(tmpVariables[3],tmpVariables[4]); + while (tmpVariables[3] < tmpVariables[2]) { + tmpVariables[4] = Util.getShort(ptrArr, tmpVariables[5]); + KMArray.cast(tmpVariables[1]).add(tmpVariables[3], tmpVariables[4]); tmpVariables[3]++; - tmpVariables[5] +=2; + tmpVariables[5] += 2; } // replace with updated key parameters. data[KEY_PARAMETERS] = KMKeyParameters.instance(tmpVariables[1]); @@ -829,13 +2295,12 @@ private void processSetBootParamsCmd(APDU apdu) { receiveIncoming(apdu); // Re-purpose the apdu buffer as scratch pad. byte[] scratchPad = apdu.getBuffer(); - Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) apdu.getBuffer().length, (byte) 0); // Argument 1 OS Version // short osVersionExp = KMIntegerTag.exp(KMType.UINT_TAG); - tmpVariables[0] = KMIntegerTag.exp(KMType.UINT_TAG); + tmpVariables[0] = KMInteger.exp(); // Argument 2 OS Patch level // short osPatchExp = KMIntegerTag.exp(KMType.UINT_TAG); - tmpVariables[1] = KMIntegerTag.exp(KMType.UINT_TAG); + tmpVariables[1] = KMInteger.exp(); // Argument 3 Verified Boot Key // short bootKeyExp = KMByteBlob.exp(); tmpVariables[2] = KMByteBlob.exp(); @@ -857,6 +2322,7 @@ private void processSetBootParamsCmd(APDU apdu) { KMArray.cast(argsProto).add((short) 4, tmpVariables[4]); KMArray.cast(argsProto).add((short) 5, tmpVariables[5]); // Decode the arguments + //System.out.println("Process boot params buffer: "+byteArrayToHexString(buffer)); short args = decoder.decode(argsProto, buffer, bufferStartOffset, bufferLength); // short osVersionTagPtr = KMArray.cast(args).get((short) 0); tmpVariables[0] = KMArray.cast(args).get((short) 0); @@ -878,10 +2344,11 @@ private void processSetBootParamsCmd(APDU apdu) { } // Begin transaction JCSystem.beginTransaction(); - short valPtr = KMIntegerTag.cast(tmpVariables[0]).getValue(); - KMInteger.cast(valPtr).getValue(repository.osVersion, (short) 0, (short) 4); - valPtr = KMIntegerTag.cast(tmpVariables[1]).getValue(); - KMInteger.cast(valPtr).getValue(repository.osPatch, (short) 0, (short) 4); + KMInteger.cast(tmpVariables[0]).value(repository.osVersion, (short) 0); + KMInteger.cast(tmpVariables[1]).value(repository.osPatch, (short) 0); + //KMInteger.cast(valPtr).getValue(repository.osVersion, (short) 0, (short) 4); + //valPtr = KMIntegerTag.cast(tmpVariables[1]).getValue(); + //KMInteger.cast(valPtr).getValue(repository.osPatch, (short) 0, (short) 4); repository.actualBootKeyLength = KMByteBlob.cast(tmpVariables[2]).length(); KMByteBlob.cast(tmpVariables[2]) .getValue(repository.verifiedBootKey, (short) 0, repository.actualBootKeyLength); @@ -970,30 +2437,30 @@ private static void processGenerateKey(APDU apdu) { sendOutgoing(apdu); } - private static void validateRSAKey(byte[] scratchPad){ + private static void validateRSAKey(byte[] scratchPad) { // Read key size tmpVariables[0] = - KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); + KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); if (tmpVariables[0] == KMTag.INVALID_VALUE) { - KMException.throwIt(KMError.INVALID_ARGUMENT); + KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); } if (tmpVariables[0] != 2048) { KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); } // Read public exponent into scratch pad tmpVariables[1] = - KMIntegerTag.getValue( - scratchPad, - (short) 0, - KMType.ULONG_TAG, - KMType.RSA_PUBLIC_EXPONENT, - data[KEY_PARAMETERS]); + KMIntegerTag.getValue( + scratchPad, + (short) 0, + KMType.ULONG_TAG, + KMType.RSA_PUBLIC_EXPONENT, + data[KEY_PARAMETERS]); if ((tmpVariables[1] == KMTag.INVALID_VALUE) || (tmpVariables[1] != 4)) { KMException.throwIt(KMError.INVALID_ARGUMENT); } // Only exponent support is F4 - 65537 which is 0x00010001. if (Util.getShort(scratchPad, (short) 0) != 0x01 - || Util.getShort(scratchPad, (short) 2) != 0x01) { + || Util.getShort(scratchPad, (short) 2) != 0x01) { KMException.throwIt(KMError.INVALID_ARGUMENT); } } @@ -1015,10 +2482,11 @@ private static void generateRSAKey(byte[] scratchPad) { data[KEY_BLOB] = KMArray.instance((short) 5); KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_PUB_KEY, data[PUB_KEY]); } - private static void validateAESKey(byte[] scratchPad){ + + private static void validateAESKey(byte[] scratchPad) { // Read key size tmpVariables[0] = - KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); + KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); if (tmpVariables[0] == KMTag.INVALID_VALUE) { KMException.throwIt(KMError.INVALID_ARGUMENT); } @@ -1027,61 +2495,56 @@ private static void validateAESKey(byte[] scratchPad){ } // Read Block mode - array of byte values tmpVariables[1] = - KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.BLOCK_MODE, data[KEY_PARAMETERS]); + KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.BLOCK_MODE, data[KEY_PARAMETERS]); if (tmpVariables[1] != KMTag.INVALID_VALUE) { // block mode specified - tmpVariables[2] = KMEnumArrayTag.cast(tmpVariables[0]).getValues(); // byte blob - tmpVariables[3] = KMByteBlob.cast(tmpVariables[2]).length(); // length - tmpVariables[4] = 0; // index - tmpVariables[5] = AES_BLOCK_SIZE; // block size - tmpVariables[5] = - KMKeyParameters.findTag( - KMType.UINT_TAG, - KMType.MIN_MAC_LENGTH, - data[KEY_PARAMETERS]); // Find Minimum Mac length - while (tmpVariables[4] < tmpVariables[3]) { // for each value in block mode array - if (KMByteBlob.cast(tmpVariables[2]).get(tmpVariables[4]) == KMType.GCM) { // if GCM mode - if (tmpVariables[5] == KMTag.INVALID_VALUE) { // minimum mac length must be specified - KMException.throwIt(KMError.MISSING_MAC_LENGTH); - } - tmpVariables[6] = KMInteger.cast(KMIntegerTag.cast(tmpVariables[5]).getValue()).getByte(); - if (tmpVariables[6] < 12 || tmpVariables[6] > 16) { - KMException.throwIt(KMError.UNSUPPORTED_MAC_LENGTH); - } - tmpVariables[6] = 12; // simulator supports only 12 bits tag for GCM. - } else { // if not GCM mode - if (tmpVariables[5] != KMTag.INVALID_VALUE) { // no mac length should be specified - KMException.throwIt(KMError.INVALID_ARGUMENT); - } + // Find Minimum Mac length + tmpVariables[2] = + KMKeyParameters.findTag(KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, data[KEY_PARAMETERS]); + // If block modes contain GCM mode + if(KMEnumArrayTag.cast(tmpVariables[1]).contains(KMType.GCM)){ + // minimum mac length must be specified + if (tmpVariables[2] == KMTag.INVALID_VALUE) { + KMException.throwIt(KMError.MISSING_MIN_MAC_LENGTH); + } + tmpVariables[3] = KMIntegerTag.cast(tmpVariables[2]).getValue(); + // Validate the MIN_MAC_LENGTH for AES - should be multiple of 8, less then 128 bits + // and greater the 96 bits + if(KMInteger.cast(tmpVariables[3]).getSignificantShort() != 0 || + KMInteger.cast(tmpVariables[3]).getShort() > 128 || + KMInteger.cast(tmpVariables[3]).getShort() < 96 || + (KMInteger.cast(tmpVariables[3]).getShort() % 8) != 0){ + KMException.throwIt(KMError.UNSUPPORTED_MIN_MAC_LENGTH); + } + }else{ // No GCM mode then no minimum mac length must be specified + if (tmpVariables[2] != KMTag.INVALID_VALUE) { + KMException.throwIt(KMError.INVALID_ARGUMENT); } - tmpVariables[4]++; } } } + private static void generateAESKey(byte[] scratchPad) { validateAESKey(scratchPad); + tmpVariables[0] = + KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); AESKey aesKey = cryptoProvider.createAESKey(tmpVariables[0]); tmpVariables[0] = aesKey.getKey(scratchPad, (short) 0); data[SECRET] = KMByteBlob.instance(scratchPad, (short) 0, tmpVariables[0]); data[KEY_BLOB] = KMArray.instance((short) 4); } - private static void validateECKeys(byte[] scratchPad){ + private static void validateECKeys(byte[] scratchPad) { // Read key size tmpVariables[0] = - KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); - if (tmpVariables[0] == KMTag.INVALID_VALUE) { + KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); + tmpVariables[1] = KMEnumTag.getValue(KMType.ECCURVE, data[KEY_PARAMETERS]); + if ((tmpVariables[0] == KMTag.INVALID_VALUE) && (tmpVariables[1] == KMType.INVALID_VALUE)){ KMException.throwIt(KMError.INVALID_ARGUMENT); - } - if (tmpVariables[0] != 256) { + }else if((tmpVariables[1] != KMTag.INVALID_VALUE) && (tmpVariables[1] != KMType.P_256)){ + KMException.throwIt(KMError.UNSUPPORTED_EC_CURVE); + }else if ((tmpVariables[0] != KMTag.INVALID_VALUE) && (tmpVariables[0] != (short)256)){ KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); } - // Read EC_CURVE - tmpVariables[1] = KMEnumTag.getValue(KMType.ECCURVE, data[KEY_PARAMETERS]); - if (tmpVariables[1] != KMType.INVALID_VALUE) { - if (tmpVariables[1] != KMType.P_256) { - KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); - } - } } private static void generateECKeys(byte[] scratchPad) { @@ -1095,16 +2558,16 @@ private static void generateECKeys(byte[] scratchPad) { KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_PUB_KEY, data[PUB_KEY]); } - private static void validateTDESKey(byte[] scratchPad){ + private static void validateTDESKey(byte[] scratchPad) { // Read Minimum Mac length - it must not be present tmpVariables[0] = - KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, data[KEY_PARAMETERS]); + KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, data[KEY_PARAMETERS]); if (tmpVariables[0] != KMType.INVALID_VALUE) { KMException.throwIt(KMError.INVALID_TAG); } // Read keysize tmpVariables[1] = - KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); + KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); if (tmpVariables[1] == KMType.INVALID_VALUE) { KMException.throwIt(KMError.INVALID_ARGUMENT); } @@ -1112,6 +2575,7 @@ private static void validateTDESKey(byte[] scratchPad){ KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); } } + private static void generateTDESKey(byte[] scratchPad) { validateTDESKey(scratchPad); DESKey desKey = cryptoProvider.createTDESKey(); @@ -1120,81 +2584,24 @@ private static void generateTDESKey(byte[] scratchPad) { data[KEY_BLOB] = KMArray.instance((short) 4); } - private static void validateHmacKey(byte[] scratchPad){ - // Read Minimum Mac length - tmpVariables[0] = - KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, data[KEY_PARAMETERS]); - if (tmpVariables[0] == KMType.INVALID_VALUE) { - KMException.throwIt(KMError.MISSING_MIN_MAC_LENGTH); - } - if (((short) (tmpVariables[0] % 8) != 0) || (tmpVariables[0] < (short) 64)) { - KMException.throwIt(KMError.UNSUPPORTED_MIN_MAC_LENGTH); - } - // Read keysize - tmpVariables[1] = - KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); - if (tmpVariables[1] == KMType.INVALID_VALUE) { - KMException.throwIt(KMError.INVALID_ARGUMENT); - } - if ((tmpVariables[1] > 512) || ((short) (tmpVariables[1] % 8) != 0)) { - KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); - } - // Read digests - tmpVariables[2] = - KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.DIGEST, data[KEY_PARAMETERS]); - if (tmpVariables[2] == KMType.INVALID_VALUE) { - KMException.throwIt(KMError.INVALID_ARGUMENT); - } - tmpVariables[3] = KMEnumArrayTag.cast(tmpVariables[2]).getValues(); - tmpVariables[4] = KMByteBlob.cast(tmpVariables[3]).length(); - tmpVariables[5] = 0; + private static void validateHmacKey(byte[] scratchPad) { // check whether digest sizes are greater then or equal to min mac length. - while (tmpVariables[5] < tmpVariables[4]) { - tmpVariables[6] = KMByteBlob.cast(tmpVariables[3]).get(tmpVariables[5]); - switch (tmpVariables[6]) { - case KMType.DIGEST_NONE: - KMException.throwIt(KMError.UNSUPPORTED_DIGEST); - break; - case KMType.MD5: - tmpVariables[7] = 128; - break; - case KMType.SHA1: - tmpVariables[7] = 160; - break; - case KMType.SHA2_224: - tmpVariables[7] = 224; - break; - case KMType.SHA2_256: - tmpVariables[7] = 256; - break; - case KMType.SHA2_384: - tmpVariables[7] = 384; - break; - case KMType.SHA2_512: - tmpVariables[7] = 512; - break; - default: - tmpVariables[7] = 0; - break; - } - if (tmpVariables[7] == 0) { - KMException.throwIt(KMError.UNSUPPORTED_DIGEST); - } - if (tmpVariables[0] > tmpVariables[7]) { - KMException.throwIt(KMError.UNSUPPORTED_MIN_MAC_LENGTH); - } - tmpVariables[5]++; + // Only SHA256 digest must be supported. + if(!KMEnumArrayTag.contains(KMType.DIGEST, KMType.SHA2_256, data[KEY_PARAMETERS])){ + KMException.throwIt(KMError.INCOMPATIBLE_DIGEST); + } + if(KMEnumArrayTag.length(KMType.DIGEST,data[KEY_PARAMETERS]) != 1){ + KMException.throwIt(KMError.INCOMPATIBLE_DIGEST); } - } - private static void generateHmacKey(byte[] scratchPad) { - validateHmacKey(scratchPad); // Read Minimum Mac length tmpVariables[0] = KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, data[KEY_PARAMETERS]); if (tmpVariables[0] == KMType.INVALID_VALUE) { KMException.throwIt(KMError.MISSING_MIN_MAC_LENGTH); } - if (((short) (tmpVariables[0] % 8) != 0) || (tmpVariables[0] < (short) 64)) { + if (((short) (tmpVariables[0] % 8) != 0) || + (tmpVariables[0] < (short) 64)|| + tmpVariables[0] > (short)256) { KMException.throwIt(KMError.UNSUPPORTED_MIN_MAC_LENGTH); } // Read keysize @@ -1203,55 +2610,15 @@ private static void generateHmacKey(byte[] scratchPad) { if (tmpVariables[1] == KMType.INVALID_VALUE) { KMException.throwIt(KMError.INVALID_ARGUMENT); } - if ((tmpVariables[1] > 512) || ((short) (tmpVariables[1] % 8) != 0)) { + if (!(tmpVariables[1] >= 64 && tmpVariables[1] <= 512 && tmpVariables[1] % 8 == 0)) { KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); } - // Read digests - tmpVariables[2] = - KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.DIGEST, data[KEY_PARAMETERS]); - if (tmpVariables[2] == KMType.INVALID_VALUE) { - KMException.throwIt(KMError.INVALID_ARGUMENT); - } - tmpVariables[3] = KMEnumArrayTag.cast(tmpVariables[2]).getValues(); - tmpVariables[4] = KMByteBlob.cast(tmpVariables[3]).length(); - tmpVariables[5] = 0; - // check whether digest sizes are greater then or equal to min mac length. - while (tmpVariables[5] < tmpVariables[4]) { - tmpVariables[6] = KMByteBlob.cast(tmpVariables[3]).get(tmpVariables[5]); - switch (tmpVariables[6]) { - case KMType.DIGEST_NONE: - KMException.throwIt(KMError.UNSUPPORTED_DIGEST); - break; - case KMType.MD5: - tmpVariables[7] = 128; - break; - case KMType.SHA1: - tmpVariables[7] = 160; - break; - case KMType.SHA2_224: - tmpVariables[7] = 224; - break; - case KMType.SHA2_256: - tmpVariables[7] = 256; - break; - case KMType.SHA2_384: - tmpVariables[7] = 384; - break; - case KMType.SHA2_512: - tmpVariables[7] = 512; - break; - default: - tmpVariables[7] = 0; - break; - } - if (tmpVariables[7] == 0) { - KMException.throwIt(KMError.UNSUPPORTED_DIGEST); - } - if (tmpVariables[0] > tmpVariables[7]) { - KMException.throwIt(KMError.UNSUPPORTED_MIN_MAC_LENGTH); - } - tmpVariables[5]++; - } + } + + private static void generateHmacKey(byte[] scratchPad) { + validateHmacKey(scratchPad); + tmpVariables[1] = + KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); // generate HMAC Key HMACKey hmacKey = cryptoProvider.createHMACKey(tmpVariables[1]); tmpVariables[0] = hmacKey.getKey(scratchPad, (short) 0); @@ -1298,7 +2665,11 @@ private static void makeKeyCharacteristics(byte[] scratchPad) { KMInteger.instance(repository.osVersion, (short) 0, (short) repository.osVersion.length); data[HW_PARAMETERS] = KMKeyParameters.makeHwEnforced( - data[KEY_PARAMETERS], (byte)data[ORIGIN], tmpVariables[1], tmpVariables[0], scratchPad); + data[KEY_PARAMETERS], + (byte) data[ORIGIN], + tmpVariables[1], + tmpVariables[0], + scratchPad); data[SW_PARAMETERS] = KMKeyParameters.makeSwEnforced(data[KEY_PARAMETERS], scratchPad); data[KEY_CHARACTERISTICS] = KMKeyCharacteristics.instance(); KMKeyCharacteristics.cast(data[KEY_CHARACTERISTICS]).setHardwareEnforced(data[HW_PARAMETERS]); @@ -1319,36 +2690,39 @@ private static void createEncryptedKeyBlob(byte[] scratchPad) { makeAuthData(scratchPad); // encrypt the secret and cryptographically attach that to authorization data encryptSecret(scratchPad); + // create key blob array KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_SECRET, data[SECRET]); KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_AUTH_TAG, data[AUTH_TAG]); KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_NONCE, data[NONCE]); KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_KEYCHAR, data[KEY_CHARACTERISTICS]); - tmpVariables[0] = repository.alloc((short) 256); // TODO use buffer + tmpVariables[0] = repository.alloc((short) 1024); // TODO use buffer tmpVariables[1] = encoder.encode(data[KEY_BLOB], repository.getHeap(), tmpVariables[0]); data[KEY_BLOB] = KMByteBlob.instance(repository.getHeap(), tmpVariables[0], tmpVariables[1]); } private static void parseEncryptedKeyBlob(byte[] scratchPad) { tmpVariables[0] = KMByteBlob.cast(data[KEY_BLOB]).getStartOff(); - tmpVariables[1] = KMArray.instance((short)5); + tmpVariables[1] = KMArray.instance((short) 5); KMArray.cast(tmpVariables[1]).add(KMKeymasterApplet.KEY_BLOB_SECRET, KMByteBlob.exp()); KMArray.cast(tmpVariables[1]).add(KMKeymasterApplet.KEY_BLOB_AUTH_TAG, KMByteBlob.exp()); KMArray.cast(tmpVariables[1]).add(KMKeymasterApplet.KEY_BLOB_NONCE, KMByteBlob.exp()); tmpVariables[2] = KMKeyCharacteristics.exp(); KMArray.cast(tmpVariables[1]).add(KMKeymasterApplet.KEY_BLOB_KEYCHAR, tmpVariables[2]); KMArray.cast(tmpVariables[1]).add(KMKeymasterApplet.KEY_BLOB_PUB_KEY, KMByteBlob.exp()); - data[KEY_BLOB]=decoder.decodeArray(tmpVariables[1], - KMByteBlob.cast(data[KEY_BLOB]).getBuffer(), - KMByteBlob.cast(data[KEY_BLOB]).getStartOff(), - KMByteBlob.cast(data[KEY_BLOB]).length()); + data[KEY_BLOB] = + decoder.decodeArray( + tmpVariables[1], + KMByteBlob.cast(data[KEY_BLOB]).getBuffer(), + KMByteBlob.cast(data[KEY_BLOB]).getStartOff(), + KMByteBlob.cast(data[KEY_BLOB]).length()); tmpVariables[0] = KMArray.cast(data[KEY_BLOB]).length(); if (tmpVariables[0] < 4) { KMException.throwIt(KMError.INVALID_KEY_BLOB); } // Validate Auth Tag data[AUTH_TAG] = KMArray.cast(data[KEY_BLOB]).get(KEY_BLOB_AUTH_TAG); - if (!KMRepository.validateAuthTag(data[AUTH_TAG])) { + if (!repository.validateAuthTag(data[AUTH_TAG])) { KMException.throwIt(KMError.INVALID_KEY_BLOB); } // initialize data @@ -1359,14 +2733,18 @@ private static void parseEncryptedKeyBlob(byte[] scratchPad) { if (tmpVariables[0] == 5) { data[PUB_KEY] = KMArray.cast(data[KEY_BLOB]).get(KEY_BLOB_PUB_KEY); } - data[HW_PARAMETERS] = KMKeyCharacteristics.cast(data[KEY_CHARACTERISTICS]).getHardwareEnforced(); - data[SW_PARAMETERS] = KMKeyCharacteristics.cast(data[KEY_CHARACTERISTICS]).getSoftwareEnforced(); + data[HW_PARAMETERS] = + KMKeyCharacteristics.cast(data[KEY_CHARACTERISTICS]).getHardwareEnforced(); + data[SW_PARAMETERS] = + KMKeyCharacteristics.cast(data[KEY_CHARACTERISTICS]).getSoftwareEnforced(); // make root of trust blob data[ROT] = - KMByteBlob.instance( - repository.verifiedBootKey, (short) 0, (short) repository.verifiedBootKey.length); + KMByteBlob.instance( + repository.verifiedBootKey, (short) 0, (short) repository.verifiedBootKey.length); data[HIDDEN_PARAMETERS] = KMKeyParameters.makeHidden(data[APP_ID], data[APP_DATA], data[ROT], scratchPad); + // make auth data + makeAuthData(scratchPad); // Decrypt Secret and verify auth tag decryptSecret(scratchPad); } @@ -1399,13 +2777,16 @@ private static void decryptSecret(byte[] scratchPad) { if (verification != true) { KMException.throwIt(KMError.INVALID_KEY_BLOB); } + // Copy the decrypted secret + data[SECRET] = + KMByteBlob.instance(scratchPad, (short) 0, KMByteBlob.cast(data[SECRET]).length()); } private static void encryptSecret(byte[] scratchPad) { // make nonce data[NONCE] = KMByteBlob.instance((short) AES_GCM_NONCE_LENGTH); data[AUTH_TAG] = KMByteBlob.instance(AES_GCM_AUTH_TAG_LENGTH); - Util.arrayCopy( + Util.arrayCopyNonAtomic( KMByteBlob.cast(data[NONCE]).getBuffer(), KMByteBlob.cast(data[NONCE]).getStartOff(), scratchPad, @@ -1512,4 +2893,21 @@ private static void sendError(APDU apdu, short err) { bufferLength = encoder.encodeError(err, buffer, bufferStartOffset, (short) 5); sendOutgoing(apdu); } + /* + private static void print (String lab, byte[] b, short s, short l){ + byte[] i = new byte[l]; + Util.arrayCopyNonAtomic(b,s,i,(short)0,l); + print(lab,i); + } + private static void print(String label, byte[] buf){ + System.out.println(label+": "); + StringBuilder sb = new StringBuilder(); + for(int i = 0; i < buf.length; i++){ + sb.append(String.format(" 0x%02X", buf[i])) ; + if(((i-1)%38 == 0) && ((i-1) >0)){ + sb.append(";\n"); + } + } + System.out.println(sb.toString()); + }*/ } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMOperationState.java b/Applet/Applet/src/com/android/javacard/keymaster/KMOperationState.java index 47cdabf8..2684efb3 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMOperationState.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMOperationState.java @@ -16,32 +16,128 @@ package com.android.javacard.keymaster; +import javacard.framework.Util; +import javacard.security.Signature; + // TODO complete the class design and implementation public class KMOperationState { - private KMInteger operationHandle; + private short opHandleCounter; + private boolean active; + private boolean trustedConfirmation; + // TODO This should be 64 bits + private short handle; + private short purpose; + private KMCipher cipher; + private Signature hmacSigner; // used for trusted confirmation. + private Signature signer; + private byte[] key; + private short keyLength; + private byte[] authTime; + private boolean authPerOperationReqd; + private boolean authTimeoutValidated; - private KMOperationState() { - operationHandle = null; + public KMOperationState(){ + authTime = new byte[8]; + key = new byte[256]; + reset(); } -/* - public static KMOperationState instance(KMContext context) { - // TODO make operation handle - return context.getRepository().newOperationState(); - } -*/ - public static void create(KMOperationState[] opStateRefTable) { - byte index = 0; - while (index < opStateRefTable.length) { - opStateRefTable[index] = new KMOperationState(); - index++; + + public void setTrustedConfirmationSigner(Signature hmacSigner){ + this.hmacSigner = hmacSigner; + trustedConfirmation = true; + } + public Signature getTrustedConfirmationSigner(){ + return hmacSigner; + } + public boolean isTrustedConfirmationRequired(){ + return trustedConfirmation; + } + public void activate(){ + active = true; + handle = getOpHandleCounter(); + } + public void reset(){ + Util.arrayFillNonAtomic(authTime, (short)0,(short)8, (byte)0); + keyLength = 0; + authPerOperationReqd = false; + active = false; + handle = 0; + key = null; + cipher = null; + signer = null; + purpose = KMType.INVALID_VALUE; + trustedConfirmation = false; + hmacSigner = null; + authTimeoutValidated = false; + } + //TODO make this random number + public short getOpHandleCounter() { + opHandleCounter++; + if(opHandleCounter < 0){ + opHandleCounter = 0; } + return opHandleCounter; + } + + public boolean isActive() { + return active; + } + + public short getHandle() { + return handle; + } + + public short getPurpose() { + return purpose; + } + + public void setPurpose(short purpose) { + this.purpose = purpose; + } + + public KMCipher getCipher() { + return cipher; + } + + public void setCipher(KMCipher cipher) { + this.cipher = cipher; + } + + public Signature getSigner() { + return signer; + } + + public void setSigner(Signature signer) { + this.signer = signer; + } + + public short getKey(byte[] buf, short start) { + Util.arrayCopy(key,(short)0, buf, start,keyLength); + return keyLength; + } + + public void setKey(byte[] buf, short start, short len) { + keyLength = len; + Util.arrayCopy(buf, start, key, (short)0, len); + } + + public boolean isAuthPerOperation() { + return authPerOperationReqd; + } + + public boolean isAuthTimeoutValidated() { + return authTimeoutValidated; + } + + public byte[] getAuthTime() { + return authTime; } - public KMInteger getOperationHandle() { - return operationHandle; + public void setAuthTime(byte[] time, short start) { + Util.arrayCopy(time, start, authTime, (short)0, (short)8); } - public void setOperationHandle(KMInteger operationHandle) { - this.operationHandle = operationHandle; + public void setAuthTimeoutValidated(boolean flag) { + authTimeoutValidated = flag; } } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMRepository.java b/Applet/Applet/src/com/android/javacard/keymaster/KMRepository.java index 8e0f01c3..421065ab 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMRepository.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMRepository.java @@ -22,16 +22,25 @@ import javacard.framework.Util; public class KMRepository { - public static final short HEAP_SIZE = 0x1000; + //TODO make the sizes configurable + public static final short HEAP_SIZE = 0x2000; public static final short MAX_BLOB_STORAGE = 32; public static final short AES_GCM_AUTH_TAG_LENGTH = 12; + public static final short HMAC_SEED_NONCE_SIZE = 16; + public static final short MAX_OPS = 4; + public static final short COMPUTED_HMAC_KEY_SIZE = 32; // Boot params constants public static final byte BOOT_KEY_MAX_SIZE = 32; public static final byte BOOT_HASH_MAX_SIZE = 32; // Repository attributes private static KMRepository repository; private byte[] masterKey; + private byte[] hmacSeed; + private byte[] hmacKey; + private byte[] computedHmacKey; + private byte[] hmacNonce; private byte[] heap; + private Object[] operationStateTable; private short heapIndex; // boot parameters public Object[] authTagRepo; @@ -44,33 +53,91 @@ public class KMRepository { public short actualBootHashLength; public boolean verifiedBootFlag; public boolean selfSignedBootFlag; - public boolean deviceLockedFlag ; + public boolean deviceLockedFlag; public static KMRepository instance() { return repository; } - public KMRepository(byte[] masterKey) { + public KMRepository() { heap = JCSystem.makeTransientByteArray(HEAP_SIZE, JCSystem.CLEAR_ON_RESET); - this.masterKey = new byte[(short)masterKey.length]; - // Initialize masterkey - Util.arrayCopy(masterKey, (short)0, this.masterKey, (short)0, (short)masterKey.length); authTagRepo = new Object[MAX_BLOB_STORAGE]; short index = 0; while (index < MAX_BLOB_STORAGE) { - authTagRepo[index] = new byte[AES_GCM_AUTH_TAG_LENGTH]; + authTagRepo[index] = new KMAuthTag(); + ((KMAuthTag) authTagRepo[index]).reserved = false; + ((KMAuthTag) authTagRepo[index]).authTag = new byte[AES_GCM_AUTH_TAG_LENGTH]; + ((KMAuthTag) authTagRepo[index]).usageCount = 0; index++; } osVersion = new byte[4]; osPatch = new byte[4]; verifiedBootKey = new byte[BOOT_KEY_MAX_SIZE]; verifiedBootHash = new byte[BOOT_HASH_MAX_SIZE]; + operationStateTable = new Object[MAX_OPS]; + index = 0; + while(index < MAX_OPS){ + operationStateTable[index] = new KMOperationState(); + ((KMOperationState)operationStateTable[index]).reset(); + index++; + } repository = this; } + public KMOperationState reserveOperation(){ + short index = 0; + while(index < MAX_OPS){ + if(!((KMOperationState)operationStateTable[index]).isActive()){ + ((KMOperationState)operationStateTable[index]).activate(); + return (KMOperationState)operationStateTable[index]; + } + index++; + } + return null; + } + public void releaseOperation(KMOperationState op){ + op.reset(); + } + public void initMasterKey(byte[] key, short len) { + if (masterKey == null) { + masterKey = new byte[len]; + Util.arrayCopy(key, (short) 0, masterKey, (short) 0, len); + } + } + + public void initHmacKey(byte[] key, short len) { + if (hmacKey == null) { + hmacKey = new byte[len]; + Util.arrayCopy(key, (short) 0, hmacKey, (short) 0, len); + } + } + + public void initHmacSeed(byte[] seed, short len) { + if (hmacSeed == null) { + hmacSeed = new byte[len]; + Util.arrayCopy(seed, (short) 0, hmacSeed, (short) 0, len); + } + } + + public void initComputedHmac(byte[] key, short start, short len) { + if (computedHmacKey == null) { + computedHmacKey = new byte[len]; + Util.arrayCopy(key, (short) 0, computedHmacKey, start, len); + } + } + + public void initHmacNonce(byte[] nonce, short offset, short len) { + if (hmacNonce == null) { + hmacNonce = new byte[len]; + } else if (len != hmacNonce.length) { + KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + } + Util.arrayCopy(nonce, (short) 0, hmacSeed, (short) 0, len); + } + public void onUninstall() { - //TODO change this - Util.arrayFillNonAtomic(masterKey,(short)0,(short)masterKey.length,(byte) 0); + // TODO change this + Util.arrayFillNonAtomic(masterKey, (short) 0, (short) masterKey.length, (byte) 0); } public void onProcess() {} @@ -100,89 +167,131 @@ public byte[] getHeap() { return heap; } - public static void persistAuthTag(short authTag) { - final byte[] compare = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - }; // length equal to AES_GCM_AUTH_TAG_LENGTH. + public byte[] getHmacSeed() { + return hmacSeed; + } + + public byte[] getHmacKey() { + return hmacKey; + } + + public byte[] getHmacNonce() { + return hmacNonce; + } + + public void setHmacNonce(byte[] hmacNonce) { + Util.arrayCopy(hmacNonce, (short) 0, this.hmacNonce, (short) 0, HMAC_SEED_NONCE_SIZE); + } + public byte[] getComputedHmacKey() { + return computedHmacKey; + } + + public void setComputedHmacKey(byte[] computedHmacKey) { + Util.arrayCopy( computedHmacKey, (short) 0, this.computedHmacKey, (short) 0, COMPUTED_HMAC_KEY_SIZE); + } + + public void persistAuthTag(short authTag) { short index = 0; - byte ret = 0; while (index < MAX_BLOB_STORAGE) { - ret = - Util.arrayCompare( - (byte[]) (repository.authTagRepo[index]), - (short) 0, - compare, - (short) 0, - AES_GCM_AUTH_TAG_LENGTH); - if (ret == 0) { + if (!((KMAuthTag) authTagRepo[index]).reserved) { + JCSystem.beginTransaction(); + ((KMAuthTag) authTagRepo[index]).reserved = true; + Util.arrayCopy( + KMByteBlob.cast(authTag).getBuffer(), + KMByteBlob.cast(authTag).getStartOff(), + ((KMAuthTag) authTagRepo[index]).authTag , + (short) 0, + AES_GCM_AUTH_TAG_LENGTH); + keyBlobCount++; + JCSystem.commitTransaction(); break; } index++; } - // This should never happen - if (index >= repository.MAX_BLOB_STORAGE){ - ISOException.throwIt(ISO7816.SW_BYTES_REMAINING_00); + } + + public boolean validateAuthTag(short authTag) { + KMAuthTag tag = findTag(authTag); + if(tag != null){ + return true; + } + return false; + } + + public void removeAuthTag(short authTag) { + KMAuthTag tag = findTag(authTag); + if(tag == null){ + ISOException.throwIt(ISO7816.SW_COMMAND_NOT_ALLOWED); } JCSystem.beginTransaction(); - Util.arrayCopy( - KMByteBlob.cast(authTag).getBuffer(), - KMByteBlob.cast(authTag).getStartOff(), - (byte[]) (repository.authTagRepo[index]), - (short) 0, - AES_GCM_AUTH_TAG_LENGTH); - repository.keyBlobCount++; + tag.reserved = false; + Util.arrayFill(tag.authTag, (short) 0, AES_GCM_AUTH_TAG_LENGTH, (byte) 0); + tag.usageCount = 0; + keyBlobCount--; JCSystem.commitTransaction(); } - public static void removeAuthTag(short authTag) { - final byte[] zeroTag = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - }; // length equal to AES_GCM_AUTH_TAG_LENGTH. + public void removeAllAuthTags() { + JCSystem.beginTransaction(); short index = 0; - byte ret = 0; - while (index < repository.MAX_BLOB_STORAGE) { - ret = - Util.arrayCompare( - (byte[]) (repository.authTagRepo[index]), - (short) 0, - KMByteBlob.cast(authTag).getBuffer(), - KMByteBlob.cast(authTag).getStartOff(), - AES_GCM_AUTH_TAG_LENGTH); - if (ret == 0) { - break; - } + while (index < MAX_BLOB_STORAGE) { + ((KMAuthTag) authTagRepo[index]).reserved = false; + Util.arrayFill( + ((KMAuthTag) authTagRepo[index]).authTag, (short) 0, AES_GCM_AUTH_TAG_LENGTH, (byte) 0); + ((KMAuthTag) authTagRepo[index]).usageCount = 0; index++; } - if (index >= MAX_BLOB_STORAGE) { - ISOException.throwIt(ISO7816.SW_COMMAND_NOT_ALLOWED); - } - JCSystem.beginTransaction(); - Util.arrayCopy( - zeroTag, (short) 0, (byte[]) (repository.authTagRepo[index]), (short) 0, AES_GCM_AUTH_TAG_LENGTH); - repository.keyBlobCount--; + keyBlobCount = 0; JCSystem.commitTransaction(); } - public static boolean validateAuthTag(short authTag) { + public KMAuthTag findTag(short authTag) { short index = 0; - byte ret = 0; + short found = 0; while (index < MAX_BLOB_STORAGE) { - ret = - Util.arrayCompare( - (byte[]) (repository.authTagRepo[index]), - (short) 0, - KMByteBlob.cast(authTag).getBuffer(), - KMByteBlob.cast(authTag).getStartOff(), - AES_GCM_AUTH_TAG_LENGTH); - if (ret == 0) { - break; + if (((KMAuthTag) authTagRepo[index]).reserved) { + found = + Util.arrayCompare( + ((KMAuthTag) authTagRepo[index]).authTag, + (short) 0, + KMByteBlob.cast(authTag).getBuffer(), + KMByteBlob.cast(authTag).getStartOff(), + AES_GCM_AUTH_TAG_LENGTH); + if (found == 0) { + return (KMAuthTag) authTagRepo[index]; + } } index++; } - if (index >= MAX_BLOB_STORAGE) { - return false; + return null; + } + + public short getRateLimitedKeyCount(short authTag) { + KMAuthTag tag = findTag(authTag); + if (tag != null) { + return tag.usageCount; } - return true; + return KMType.INVALID_VALUE; } + public void setRateLimitedKeyCount(short authTag, short val) { + KMAuthTag tag = findTag(authTag); + JCSystem.beginTransaction(); + if (tag != null) { + tag.usageCount = val; + } + JCSystem.commitTransaction(); + } + + public KMOperationState findOperation(short opHandle) { + short index = 0; + while(index < MAX_OPS){ + if(((KMOperationState)operationStateTable[index]).isActive() && + ((KMOperationState)operationStateTable[index]).getHandle() == opHandle){ + return (KMOperationState)operationStateTable[index]; + } + index++; + } + return null; + } } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMType.java b/Applet/Applet/src/com/android/javacard/keymaster/KMType.java index 1e023647..f136bd83 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMType.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMType.java @@ -36,7 +36,7 @@ public abstract class KMType { public static final byte VERIFICATION_TOKEN_TYPE = 0x09; public static final byte HMAC_SHARING_PARAM_TYPE = 0x0A; - // Tags + // Tag Types public static final short INVALID_TAG = 0x0000; public static final short ENUM_TAG = 0x1000; public static final short ENUM_ARRAY_TAG = 0x2000; @@ -76,6 +76,7 @@ public abstract class KMType { public static final byte USER_AUTH_NONE = 0x00; public static final byte PASSWORD = 0x01; public static final byte FINGERPRINT = 0x02; + public static final byte BOTH = 0x03; // have to be power of 2 public static final byte ANY = (byte) 0xFF; @@ -127,13 +128,13 @@ public abstract class KMType { // Enum Array Tag // Purpose - public static final short PURPOSE = 0x0002; - public static final byte ENCRYPT = 0x01; - public static final byte DECRYPT = 0x02; - public static final byte SIGN = 0x04; - public static final byte VERIFY = 0x05; - public static final byte WRAP_KEY = 0x06; - public static final byte ATTEST_KEY = (byte) 0x7F; + public static final short PURPOSE = 0x0001; + public static final byte ENCRYPT = 0x00; + public static final byte DECRYPT = 0x01; + public static final byte SIGN = 0x02; + public static final byte VERIFY = 0x03; + public static final byte WRAP_KEY = 0x05; + public static final byte ATTEST_KEY = (byte) 0x7F; /* TODO This is not present in types.hal */ // Block mode public static final short BLOCK_MODE = 0x0004; @@ -194,7 +195,7 @@ public abstract class KMType { public static final short ACTIVE_DATETIME = 0x0190; public static final short ORIGINATION_EXPIRE_DATETIME = 0x0191; public static final short USAGE_EXPIRE_DATETIME = 0x0192; - public static final short CREATION_DATETIME = 0x0193; + public static final short CREATION_DATETIME = 0x02BD;//0x0193; // Integer Array Tags - ULONG_REP and UINT_REP. // User Secure Id @@ -275,7 +276,7 @@ public static void initialize() { public static short getValue(short ptr){return Util.getShort(heap, (short)(ptr+TLV_HEADER_SIZE));} protected static short instance(byte type, short length){ - if (length <= 0) ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); + if (length < 0) ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); short ptr = repository.alloc((short) (length + TLV_HEADER_SIZE)); heap[ptr] = type; Util.setShort(heap, (short) (ptr + 1), length); diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMUtil.java b/Applet/Applet/src/com/android/javacard/keymaster/KMUtil.java deleted file mode 100644 index 599da84e..00000000 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMUtil.java +++ /dev/null @@ -1,109 +0,0 @@ -package com.android.javacard.keymaster; - -import javacard.framework.ISO7816; -import javacard.framework.ISOException; -import javacard.framework.JCSystem; -import javacard.framework.Util; -import javacard.security.AESKey; -import javacard.security.CryptoException; -import javacard.security.KeyBuilder; -import javacard.security.RandomData; -import javacardx.crypto.Cipher; - -public class KMUtil { - private static final short ENTROPY_POOL_SIZE = 16; // simulator does not support 256 bit aes keys - public static final byte AES_BLOCK_SIZE = 16; - public static final byte[] aesICV = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; - private static byte[] counter; - private static AESKey aesKey; - private static Cipher aesCbc; - private static byte[] entropyPool; - public static void init() { - entropyPool = JCSystem.makeTransientByteArray(ENTROPY_POOL_SIZE, JCSystem.CLEAR_ON_RESET); - counter = JCSystem.makeTransientByteArray((short)8, JCSystem.CLEAR_ON_RESET); - KMUtil.initEntropyPool(entropyPool); - try { - //Note: ALG_AES_BLOCK_128_CBC_NOPAD not supported by simulator. - aesCbc = Cipher.getInstance(Cipher.ALG_AES_BLOCK_128_CBC_NOPAD, false); - } catch (CryptoException exp) { - // TODO change this to proper error code - ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); - } - aesKey = (AESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_AES, KeyBuilder.LENGTH_AES_128, false); - } - - public static void initEntropyPool(byte[] pool) { - byte index = 0; - RandomData trng; - while (index < counter.length) { - counter[index++] = 0; - } - try { - trng = RandomData.getInstance(RandomData.ALG_TRNG); - trng.nextBytes(pool, (short) 0, (short) pool.length); - } catch (CryptoException exp) { - if (exp.getReason() == CryptoException.NO_SUCH_ALGORITHM) { - //TODO change this when possible - // simulator does not support TRNG algorithm. So, PRNG algorithm (deprecated) is used. - trng = RandomData.getInstance(RandomData.ALG_PSEUDO_RANDOM); - trng.nextBytes(pool, (short) 0, (short) pool.length); - } else { - // TODO change this to proper error code - ISOException.throwIt(ISO7816.SW_UNKNOWN); - } - } - - } - - // Generate a secure random number from existing entropy pool. This uses aes ecb algorithm with - // 8 byte counter and 16 byte block size. - public static void newRandomNumber(byte[] num, short startOff, short length) { - KMRepository repository = KMRepository.instance(); - byte[] bufPtr = repository.getHeap(); - short countBufInd = repository.alloc(AES_BLOCK_SIZE); - short randBufInd = repository.alloc(AES_BLOCK_SIZE); - short len = AES_BLOCK_SIZE; - aesKey.setKey(entropyPool, (short) 0); - aesCbc.init(aesKey, Cipher.MODE_ENCRYPT, aesICV, (short)0, (short)16); - while (length > 0) { - if (length < len ) len = length; - // increment counter by one - incrementCounter(); - // copy the 8 byte counter into the 16 byte counter buffer. - Util.arrayCopy(counter, (short) 0, bufPtr, countBufInd, (short) counter.length); - // encrypt the counter buffer with existing entropy which forms the aes key. - aesCbc.doFinal(bufPtr, countBufInd, AES_BLOCK_SIZE, bufPtr, randBufInd); - // copy the encrypted counter block to buffer passed in the argument - Util.arrayCopy(bufPtr, randBufInd, num, startOff, len); - length = (short) (length - len); - startOff = (short)(startOff + len); - } - } - - // increment 8 byte counter by one - private static void incrementCounter() { - // start with least significant byte - short index = (short) (counter.length - 1); - while (index >= 0) { - // if the msb of current byte is set then it will be negative - if (counter[index] < 0) { - // then increment the counter - counter[index]++; - // is the msb still set? i.e. no carry over - if (counter[index] < 0) break; // then break - else index--; // else go to the higher order byte - } else { - // if msb is not set then increment the counter - counter[index]++; - // is the msb still not set i.e. no carry over - if (counter[index] >= 0) break; // then break - else index--; // else go to the higher order byte - } - } - } - - public static byte[] getEntropyPool() { - return entropyPool; - } - -} diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMVerificationToken.java b/Applet/Applet/src/com/android/javacard/keymaster/KMVerificationToken.java index eefa9e60..168971c3 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMVerificationToken.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMVerificationToken.java @@ -37,8 +37,9 @@ public static short exp() { KMArray arr = KMArray.cast(arrPtr); arr.add(CHALLENGE, KMInteger.exp()); arr.add(TIMESTAMP, KMInteger.exp()); - arr.add(PARAMETERS_VERIFIED, KMKeyParameters.exp()); - arr.add(SECURITY_LEVEL, KMEnumTag.instance(KMType.HARDWARE_TYPE)); + //arr.add(PARAMETERS_VERIFIED, KMKeyParameters.exp()); + arr.add(PARAMETERS_VERIFIED, KMByteBlob.exp()); + arr.add(SECURITY_LEVEL, KMEnum.instance(KMType.HARDWARE_TYPE)); arr.add(MAC, KMByteBlob.exp()); return instance(arrPtr); } @@ -118,7 +119,8 @@ public short getParametersVerified() { } public void setParametersVerified(short vals) { - KMKeyParameters.cast(vals); + // KMKeyParameters.cast(vals); + KMByteBlob.cast(vals); short arrPtr = getVals(); KMArray.cast(arrPtr).add(PARAMETERS_VERIFIED, vals); } @@ -129,8 +131,7 @@ public short getSecurityLevel() { } public void setSecurityLevel(short vals) { - short key = KMEnumTag.cast(vals).getKey(); - if(key != HARDWARE_TYPE) ISOException.throwIt(ISO7816.SW_DATA_INVALID); + KMEnum.cast(vals); short arrPtr = getVals(); KMArray.cast(arrPtr).add(SECURITY_LEVEL, vals); } diff --git a/Applet/Applet/test/com/android/javacard/test/KMFrameworkTest.java b/Applet/Applet/test/com/android/javacard/test/KMFrameworkTest.java index 5f43ebfa..0b2ae651 100644 --- a/Applet/Applet/test/com/android/javacard/test/KMFrameworkTest.java +++ b/Applet/Applet/test/com/android/javacard/test/KMFrameworkTest.java @@ -17,21 +17,21 @@ package com.android.javacard.test; import com.android.javacard.keymaster.KMArray; +import com.android.javacard.keymaster.KMBoolTag; import com.android.javacard.keymaster.KMByteBlob; import com.android.javacard.keymaster.KMByteTag; +import com.android.javacard.keymaster.KMCryptoProvider; +import com.android.javacard.keymaster.KMCryptoProviderImpl; import com.android.javacard.keymaster.KMDecoder; import com.android.javacard.keymaster.KMEncoder; import com.android.javacard.keymaster.KMEnum; import com.android.javacard.keymaster.KMEnumArrayTag; import com.android.javacard.keymaster.KMEnumTag; -import com.android.javacard.keymaster.KMError; import com.android.javacard.keymaster.KMInteger; -import com.android.javacard.keymaster.KMIntegerArrayTag; import com.android.javacard.keymaster.KMIntegerTag; import com.android.javacard.keymaster.KMKeyCharacteristics; import com.android.javacard.keymaster.KMKeyParameters; import com.android.javacard.keymaster.KMKeymasterApplet; -import com.android.javacard.keymaster.KMSimulator; import com.android.javacard.keymaster.KMType; import com.licel.jcardsim.smartcardio.CardSimulator; import com.licel.jcardsim.utils.AIDUtil; @@ -46,24 +46,23 @@ import javacard.security.KeyBuilder; import javacard.security.KeyPair; import javacard.security.RSAPrivateKey; -import javacard.security.RandomData; import javax.smartcardio.CommandAPDU; import javax.smartcardio.ResponseAPDU; import org.junit.Assert; import org.junit.Test; -import org.junit.experimental.theories.suppliers.TestedOn; public class KMFrameworkTest { private short status; private short keyCharacteristics; private short keyBlob; - private KMSimulator sim; + private KMCryptoProvider sim; @Test public void test_Lifecycle_Success() { // Create simulator - KMSimulator.jcardSim = true; - sim = new KMSimulator(); + //KMJcardSimulator.jcardSim = true; + sim = KMCryptoProviderImpl.instance(); + sim.bypassAesGcm(); CardSimulator simulator = new CardSimulator(); // Install applet @@ -72,6 +71,7 @@ public void test_Lifecycle_Success() { // Select applet simulator.selectApplet(appletAID1); +// testEncodeDecode(); testProvisionCmd(simulator); testSetBootParams(simulator); testGetHwInfoCmd(simulator); @@ -95,8 +95,43 @@ public void test_Lifecycle_Success() { simulator.deleteApplet(appletAID1); } + private void testEncodeDecode() { + //128 + //ecb ode - blockmode + //padding pkcs 7 + short arrPtr = KMArray.instance((short)4); + short boolTag = KMBoolTag.instance(KMType.NO_AUTH_REQUIRED); + short keySize = KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, KMInteger.uint_16((short)128)); + short byteBlob = KMByteBlob.instance((short)1); + KMByteBlob.cast(byteBlob).add((short)0, KMType.ECB); + short blockMode = KMEnumArrayTag.instance(KMType.BLOCK_MODE,byteBlob); + byteBlob = KMByteBlob.instance((short)1); + KMByteBlob.cast(byteBlob).add((short)0, KMType.PKCS7); + short paddingMode = KMEnumArrayTag.instance(KMType.PADDING, byteBlob); + KMArray.cast(arrPtr).add((short)0, boolTag); + KMArray.cast(arrPtr).add((short)1, keySize); + KMArray.cast(arrPtr).add((short)2, blockMode); + KMArray.cast(arrPtr).add((short)3, paddingMode); + byte[] buf = new byte[1024]; + KMEncoder encode = new KMEncoder(); + KMDecoder decode = new KMDecoder(); + short len = encode.encode(arrPtr, buf, (short)0); + arrPtr = KMArray.instance((short)4); + KMArray.cast(arrPtr).add((short)0, KMBoolTag.exp()); + KMArray.cast(arrPtr).add((short)1, KMIntegerTag.exp(KMType.UINT_TAG)); + KMArray.cast(arrPtr).add((short)2, KMEnumArrayTag.exp()); + KMArray.cast(arrPtr).add((short)3, KMEnumArrayTag.exp()); + arrPtr = decode.decode(arrPtr,buf,(short)0,len); + KMArray arr = KMArray.cast(arrPtr); + short val = 0; + val = KMBoolTag.cast(arr.get((short)0)).getVal(); + val = KMInteger.cast(KMIntegerTag.cast(arr.get((short)1)).getValue()).getShort(); + val = KMEnumArrayTag.cast(arr.get((short)2)).get((short)0);; + val = KMEnumArrayTag.cast(arr.get((short)3)).get((short)0); + } + private void testGetKeyCharacteristics(CardSimulator simulator) { - byte[] buf = new byte[512]; + byte[] buf = new byte[1024]; buf[0] = (byte)0x80; buf[1] = (byte)0x1D; buf[2] = (byte)0x40; @@ -125,7 +160,7 @@ private void testGetKeyCharacteristics(CardSimulator simulator) { } public void testProvisionCmd(CardSimulator simulator){ - byte[] buf = new byte[512]; + byte[] buf = new byte[1024]; // test provision command short cmd = makeProvisionCmd(); KMEncoder enc = new KMEncoder(); @@ -136,7 +171,7 @@ public void testProvisionCmd(CardSimulator simulator){ Assert.assertEquals(0x9000, response.getSW()); } public void testSetBootParams(CardSimulator simulator){ - byte[] buf = new byte[512]; + byte[] buf = new byte[1024]; // test provision command short cmd = makeSetBootParamsCmd(); KMEncoder enc = new KMEncoder(); @@ -148,7 +183,7 @@ public void testSetBootParams(CardSimulator simulator){ } public void testGenerateRsaKey(CardSimulator simulator){ - byte[] buf = new byte[512]; + byte[] buf = new byte[1024]; buf[0] = (byte)0x80; buf[1] = (byte)0x10; buf[2] = (byte)0x40; @@ -170,7 +205,7 @@ public void testGenerateRsaKey(CardSimulator simulator){ } public void testImportRsaKey(CardSimulator simulator){ - byte[] buf = new byte[512]; + byte[] buf = new byte[1024]; buf[0] = (byte)0x80; buf[1] = (byte)0x11; buf[2] = (byte)0x40; @@ -184,14 +219,14 @@ public void testImportRsaKey(CardSimulator simulator){ Util.arrayCopyNonAtomic(buf,(short)0,apdu,(short)0,(short)(7+actualLen)); //CommandAPDU commandAPDU = new CommandAPDU(0x80, 0x10, 0x40, 0x00, buf, 0, actualLen); CommandAPDU commandAPDU = new CommandAPDU(apdu); - // print(commandAPDU.getBytes()); + //print(commandAPDU.getBytes()); ResponseAPDU response = simulator.transmitCommand(commandAPDU); extractKeyCharAndBlob(response); Assert.assertEquals(0x9000, response.getSW()); } public void testImportEcKey(CardSimulator simulator){ - byte[] buf = new byte[512]; + byte[] buf = new byte[1024]; buf[0] = (byte)0x80; buf[1] = (byte)0x11; buf[2] = (byte)0x40; @@ -205,7 +240,7 @@ public void testImportEcKey(CardSimulator simulator){ Util.arrayCopyNonAtomic(buf,(short)0,apdu,(short)0,(short)(7+actualLen)); //CommandAPDU commandAPDU = new CommandAPDU(0x80, 0x10, 0x40, 0x00, buf, 0, actualLen); CommandAPDU commandAPDU = new CommandAPDU(apdu); - // print(commandAPDU.getBytes()); + //print(commandAPDU.getBytes()); ResponseAPDU response = simulator.transmitCommand(commandAPDU); extractKeyCharAndBlob(response); Assert.assertEquals(0x9000, response.getSW()); @@ -240,7 +275,7 @@ private void extractKeyChar(ResponseAPDU response) { } public void testGenerateAesKey(CardSimulator simulator){ - byte[] buf = new byte[512]; + byte[] buf = new byte[1024]; buf[0] = (byte)0x80; buf[1] = (byte)0x10; buf[2] = (byte)0x40; @@ -262,13 +297,13 @@ public void testGenerateAesKey(CardSimulator simulator){ } public void testImportAesKey(CardSimulator simulator){ - byte[] buf = new byte[512]; + byte[] buf = new byte[1024]; buf[0] = (byte)0x80; buf[1] = (byte)0x11; buf[2] = (byte)0x40; buf[3] = (byte)0x00; buf[4] = 0; - short cmd = makeImportKeySymmCmd(KMType.AES, (short)256); + short cmd = makeImportKeySymmCmd(KMType.AES, (short)128); KMEncoder enc = new KMEncoder(); short actualLen = enc.encode(cmd, buf, (short) 7); Util.setShort(buf, (short)5, actualLen); @@ -276,13 +311,13 @@ public void testImportAesKey(CardSimulator simulator){ Util.arrayCopyNonAtomic(buf,(short)0,apdu,(short)0,(short)(7+actualLen)); //CommandAPDU commandAPDU = new CommandAPDU(0x80, 0x10, 0x40, 0x00, buf, 0, actualLen); CommandAPDU commandAPDU = new CommandAPDU(apdu); - // print(commandAPDU.getBytes()); + //print(commandAPDU.getBytes()); ResponseAPDU response = simulator.transmitCommand(commandAPDU); extractKeyCharAndBlob(response); Assert.assertEquals(0x9000, response.getSW()); } public void testImportHmacKey(CardSimulator simulator){ - byte[] buf = new byte[512]; + byte[] buf = new byte[1024]; buf[0] = (byte)0x80; buf[1] = (byte)0x11; buf[2] = (byte)0x40; @@ -296,13 +331,13 @@ public void testImportHmacKey(CardSimulator simulator){ Util.arrayCopyNonAtomic(buf,(short)0,apdu,(short)0,(short)(7+actualLen)); //CommandAPDU commandAPDU = new CommandAPDU(0x80, 0x10, 0x40, 0x00, buf, 0, actualLen); CommandAPDU commandAPDU = new CommandAPDU(apdu); - // print(commandAPDU.getBytes()); + //print(commandAPDU.getBytes()); ResponseAPDU response = simulator.transmitCommand(commandAPDU); extractKeyCharAndBlob(response); Assert.assertEquals(0x9000, response.getSW()); } public void testImportDesKey(CardSimulator simulator){ - byte[] buf = new byte[512]; + byte[] buf = new byte[1024]; buf[0] = (byte)0x80; buf[1] = (byte)0x11; buf[2] = (byte)0x40; @@ -316,14 +351,14 @@ public void testImportDesKey(CardSimulator simulator){ Util.arrayCopyNonAtomic(buf,(short)0,apdu,(short)0,(short)(7+actualLen)); //CommandAPDU commandAPDU = new CommandAPDU(0x80, 0x10, 0x40, 0x00, buf, 0, actualLen); CommandAPDU commandAPDU = new CommandAPDU(apdu); - print(commandAPDU.getBytes()); + //print(commandAPDU.getBytes()); ResponseAPDU response = simulator.transmitCommand(commandAPDU); extractKeyCharAndBlob(response); Assert.assertEquals(0x9000, response.getSW()); } public void testGenerateHmacKey(CardSimulator simulator){ - byte[] buf = new byte[512]; + byte[] buf = new byte[1024]; buf[0] = (byte)0x80; buf[1] = (byte)0x10; buf[2] = (byte)0x40; @@ -345,7 +380,7 @@ public void testGenerateHmacKey(CardSimulator simulator){ } public void testGenerate3DesKey(CardSimulator simulator){ - byte[] buf = new byte[512]; + byte[] buf = new byte[1024]; buf[0] = (byte)0x80; buf[1] = (byte)0x10; buf[2] = (byte)0x40; @@ -367,7 +402,7 @@ public void testGenerate3DesKey(CardSimulator simulator){ } public void testGenerateEcKey(CardSimulator simulator){ - byte[] buf = new byte[512]; + byte[] buf = new byte[1024]; buf[0] = (byte)0x80; buf[1] = (byte)0x10; buf[2] = (byte)0x40; @@ -414,7 +449,7 @@ public void testGetHwInfoCmd(CardSimulator simulator){ Assert.assertEquals(0x9000, response.getSW()); } private void testAddRngEntropyCmd(CardSimulator simulator){ - byte[] buf = new byte[512]; + byte[] buf = new byte[1024]; // test provision command short cmd = makeAddRngEntropyCmd(); KMEncoder enc = new KMEncoder(); @@ -472,17 +507,23 @@ private short makeProvisionCmd() { private short makeGenerateKeyCmd(byte alg, short keysize) { // Argument - short arrPtr = KMArray.instance((short) 5); + short arrPtr = KMArray.instance((short) 4); KMArray vals = KMArray.cast(arrPtr); byte[] val = "Test".getBytes(); byte[] intVal = {1, 2, 3, 4}; byte[] pubVal = {0x00, 0x01, 0x00, 0x01}; - byte[] digest = {KMType.SHA1, KMType.SHA2_256}; + //byte[] digest = {KMType.SHA1, KMType.SHA2_256}; + byte[] digest = {KMType.DIGEST_NONE}; + byte[] padding = {KMType.PADDING_NONE}; + byte[] purpose = {0x02, 0x03}; vals.add((short)0, KMEnumTag.instance(KMType.ALGORITHM, alg)); - vals.add((short)1, KMIntegerTag.instance(KMType.UINT_TAG, KMType.USERID, KMInteger.uint_32(intVal, (short)0))); - vals.add((short)2, KMByteTag.instance(KMType.APPLICATION_ID, KMByteBlob.instance(val, (short)0, (short)val.length))); - vals.add((short)3, KMIntegerTag.instance(KMType.ULONG_TAG, KMType.RSA_PUBLIC_EXPONENT, KMInteger.uint_32(pubVal,(short)0))); - vals.add((short)4, KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, KMInteger.uint_16(keysize))); + vals.add((short)1, KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, KMInteger.uint_16(keysize))); + //vals.add((short)1, KMIntegerTag.instance(KMType.UINT_TAG, KMType.USERID, KMInteger.uint_32(intVal, (short)0))); + //vals.add((short)2, KMByteTag.instance(KMType.APPLICATION_ID, KMByteBlob.instance(val, (short)0, (short)val.length))); + vals.add((short)2, KMIntegerTag.instance(KMType.ULONG_TAG, KMType.RSA_PUBLIC_EXPONENT, KMInteger.uint_32(pubVal,(short)0))); + vals.add((short)3, KMEnumArrayTag.instance(KMType.PURPOSE, KMByteBlob.instance(purpose,(short)0, (short)purpose.length))); + // vals.add((short)4, KMEnumArrayTag.instance(KMType.DIGEST, KMByteBlob.instance(digest,(short)0, (short)digest.length))); + //vals.add((short)5, KMEnumArrayTag.instance(KMType.PADDING, KMByteBlob.instance(padding,(short)0, (short)padding.length))); short keyParamsPtr = KMKeyParameters.instance(arrPtr); // Array of expected arguments short argPtr = KMArray.instance((short) 1); @@ -502,7 +543,7 @@ private short makeImportKeySymmCmd(short alg, short size) { byte[] val = "Test".getBytes(); byte[] intVal = {1, 2, 3, 4}; byte[] pubVal = {0x00, 0x01, 0x00, 0x01}; - byte[] digest = {KMType.SHA1, KMType.SHA2_256}; + byte[] digest = {KMType.SHA2_256}; vals.add((short)0, KMEnumTag.instance(KMType.ALGORITHM, (byte)alg)); vals.add((short)1, KMIntegerTag.instance(KMType.UINT_TAG, KMType.USERID, KMInteger.uint_32(intVal, (short)0))); vals.add((short)2, KMByteTag.instance(KMType.APPLICATION_ID, KMByteBlob.instance(val, (short)0, (short)val.length))); @@ -553,7 +594,7 @@ private short makeImportKeySymmCmd(short alg, short size) { private short makeImportKeyRsaCmd() { // Argument 1 - short arrPtr = KMArray.instance((short) 4); + short arrPtr = KMArray.instance((short) 5); KMArray vals = KMArray.cast(arrPtr); byte[] val = "Test".getBytes(); byte[] intVal = {1, 2, 3, 4}; @@ -563,23 +604,25 @@ private short makeImportKeyRsaCmd() { vals.add((short)1, KMIntegerTag.instance(KMType.UINT_TAG, KMType.USERID, KMInteger.uint_32(intVal, (short)0))); vals.add((short)2, KMByteTag.instance(KMType.APPLICATION_ID, KMByteBlob.instance(val, (short)0, (short)val.length))); vals.add((short)3, KMIntegerTag.instance(KMType.ULONG_TAG, KMType.RSA_PUBLIC_EXPONENT, KMInteger.uint_32(pubVal,(short)0))); + vals.add((short)4, KMBoolTag.instance(KMType.NO_AUTH_REQUIRED)); + //vals.add((short)4, KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, KMInteger.uint_16((short)2048))); short keyParamsPtr = KMKeyParameters.instance(arrPtr); // Argument 2 short keyFormatPtr = KMEnum.instance(KMType.KEY_FORMAT, KMType.RAW); // Argument 3 - KeyPair rsa512KeyPair = new KeyPair(KeyPair.ALG_RSA, KeyBuilder.LENGTH_RSA_512); - rsa512KeyPair.genKeyPair(); - byte[] secret = new byte[64]; - byte[] modulus = new byte[64]; + KeyPair rsaKeyPair = new KeyPair(KeyPair.ALG_RSA, KeyBuilder.LENGTH_RSA_2048); + rsaKeyPair.genKeyPair(); + byte[] secret = new byte[256]; + byte[] modulus = new byte[256]; short keyBlob = KMArray.instance((short)2); - RSAPrivateKey key = (RSAPrivateKey) rsa512KeyPair.getPrivate(); + RSAPrivateKey key = (RSAPrivateKey) rsaKeyPair.getPrivate(); short len = key.getExponent(secret, (short)0); KMArray.cast(keyBlob).add((short)0, KMByteBlob.instance(secret,(short)0,len)); len = key.getModulus(modulus, (short)0); KMArray.cast(keyBlob).add((short)1, KMByteBlob.instance(modulus,(short)0,len)); KMEncoder encoder = new KMEncoder(); - byte[] blob = new byte[256]; + byte[] blob = new byte[1024]; len = encoder.encode(keyBlob,blob,(short)0); keyBlob = KMByteBlob.instance(blob, (short)0, len); // Array of expected arguments @@ -640,11 +683,10 @@ private short makeGetKeyCharKeyCmd() { byte[] val = "Test".getBytes(); byte[] intVal = {1, 2, 3, 4}; byte[] pubVal = {0x00, 0x01, 0x00, 0x01}; - byte[] digest = {KMType.SHA1, KMType.SHA2_256}; + byte[] digest = {KMType.SHA2_256}; vals.add((short)0, keyBlob); vals.add((short)1, KMByteBlob.instance(val, (short)0, (short)val.length)); - Util.setShort(val,(short)0,KMType.INVALID_VALUE); - vals.add((short)2, KMByteBlob.instance(val, (short)0, (short)2));// No App Data + vals.add((short)2, KMByteBlob.instance((short)0));// No App Data return argPtr; } private short makeGenerateKeyCmdHmac(byte alg, short keysize) { @@ -653,7 +695,7 @@ private short makeGenerateKeyCmdHmac(byte alg, short keysize) { KMArray vals = KMArray.cast(arrPtr); byte[] val = "Test".getBytes(); byte[] intVal = {1, 2, 3, 4}; - byte[] digest = {KMType.SHA1, KMType.SHA2_256}; + byte[] digest = {KMType.SHA2_256}; vals.add((short)0, KMEnumTag.instance(KMType.ALGORITHM, alg)); vals.add((short)1, KMIntegerTag.instance(KMType.UINT_TAG, KMType.USERID, KMInteger.uint_32(intVal, (short)0))); vals.add((short)2, KMByteTag.instance(KMType.APPLICATION_ID, KMByteBlob.instance(val, (short)0, (short)val.length))); @@ -671,9 +713,9 @@ private short makeGenerateKeyCmdHmac(byte alg, short keysize) { private short makeSetBootParamsCmd() { // Argument 1 OS Version short versionPatchPtr = KMInteger.uint_16((short)1); - short versionTagPtr = KMIntegerTag.instance(KMType.UINT_TAG, KMType.OS_VERSION,versionPatchPtr); +// short versionTagPtr = KMIntegerTag.instance(KMType.UINT_TAG, KMType.OS_VERSION,versionPatchPtr); // Argument 2 OS Patch level - short patchTagPtr = KMIntegerTag.instance(KMType.UINT_TAG, KMType.OS_PATCH_LEVEL, versionPatchPtr); +// short patchTagPtr = KMIntegerTag.instance(KMType.UINT_TAG, KMType.OS_PATCH_LEVEL, versionPatchPtr); // Argument 3 Verified Boot Key byte[] bootKeyHash = "00011122233344455566677788899900".getBytes(); short bootKeyPtr = KMByteBlob.instance(bootKeyHash,(short)0, (short)bootKeyHash.length); @@ -686,8 +728,8 @@ private short makeSetBootParamsCmd() { // Arguments short arrPtr = KMArray.instance((short) 6); KMArray vals = KMArray.cast(arrPtr); - vals.add((short)0, versionTagPtr); - vals.add((short) 1, patchTagPtr); + vals.add((short)0, versionPatchPtr); + vals.add((short) 1, versionPatchPtr); vals.add((short) 2, bootKeyPtr); vals.add((short) 3, bootHashPtr); vals.add((short) 4, bootStatePtr); diff --git a/Applet/Applet/test/com/android/javacard/test/KMVTSTest.java b/Applet/Applet/test/com/android/javacard/test/KMVTSTest.java new file mode 100644 index 00000000..c5ef65c9 --- /dev/null +++ b/Applet/Applet/test/com/android/javacard/test/KMVTSTest.java @@ -0,0 +1,646 @@ +/* + * Copyright(C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.javacard.test; + +import com.android.javacard.keymaster.KMArray; +import com.android.javacard.keymaster.KMBoolTag; +import com.android.javacard.keymaster.KMByteBlob; +import com.android.javacard.keymaster.KMByteTag; +import com.android.javacard.keymaster.KMCryptoProvider; +import com.android.javacard.keymaster.KMCryptoProviderImpl; +import com.android.javacard.keymaster.KMDecoder; +import com.android.javacard.keymaster.KMEncoder; +import com.android.javacard.keymaster.KMEnum; +import com.android.javacard.keymaster.KMEnumArrayTag; +import com.android.javacard.keymaster.KMEnumTag; +import com.android.javacard.keymaster.KMError; +import com.android.javacard.keymaster.KMInteger; +import com.android.javacard.keymaster.KMIntegerTag; +import com.android.javacard.keymaster.KMKeyCharacteristics; +import com.android.javacard.keymaster.KMKeyParameters; +import com.android.javacard.keymaster.KMKeymasterApplet; +import com.android.javacard.keymaster.KMType; +import com.licel.jcardsim.smartcardio.CardSimulator; +import com.licel.jcardsim.utils.AIDUtil; +import javacard.framework.AID; +import javacard.framework.Util; +import javacard.security.AESKey; +import javacard.security.DESKey; +import javacard.security.ECPrivateKey; +import javacard.security.ECPublicKey; +import javacard.security.HMACKey; +import javacard.security.Key; +import javacard.security.KeyBuilder; +import javacard.security.KeyPair; +import javacard.security.RSAPrivateKey; +import javacard.security.RSAPublicKey; +import javax.smartcardio.CommandAPDU; +import javax.smartcardio.ResponseAPDU; +import org.junit.Assert; +import org.junit.Test; + +public class KMVTSTest { + private KMCryptoProvider sim; + private CardSimulator simulator; + private KMEncoder encoder; + private KMDecoder decoder; + private KMCryptoProvider cryptoProvider; + + public KMVTSTest(){ + cryptoProvider = KMCryptoProviderImpl.instance(); + sim = KMCryptoProviderImpl.instance(); + simulator = new CardSimulator(); + encoder = new KMEncoder(); + decoder = new KMDecoder(); + } + + private void init(){ + // Create simulator + //KMJcardSimulator.jcardSim = true; + AID appletAID1 = AIDUtil.create("A000000062"); + simulator.installApplet(appletAID1, KMKeymasterApplet.class); + // Select applet + simulator.selectApplet(appletAID1); + // provision attest key + provisionCmd(simulator); + // set bootup parameters + setBootParams(simulator); + } + + private void setBootParams(CardSimulator simulator){ + // Argument 1 OS Version + short versionPatchPtr = KMInteger.uint_16((short)1); +// short versionTagPtr = KMIntegerTag.instance(KMType.UINT_TAG, KMType.OS_VERSION,versionPatchPtr); + // Argument 2 OS Patch level +// short patchTagPtr = KMIntegerTag.instance(KMType.UINT_TAG, KMType.OS_PATCH_LEVEL, versionPatchPtr); + // Argument 3 Verified Boot Key + byte[] bootKeyHash = "00011122233344455566677788899900".getBytes(); + short bootKeyPtr = KMByteBlob.instance(bootKeyHash,(short)0, (short)bootKeyHash.length); + // Argument 4 Verified Boot Hash + short bootHashPtr = KMByteBlob.instance(bootKeyHash,(short)0, (short)bootKeyHash.length); + // Argument 5 Verified Boot State + short bootStatePtr = KMEnum.instance(KMType.VERIFIED_BOOT_STATE,KMType.VERIFIED_BOOT); + // Argument 6 Device Locked + short deviceLockedPtr = KMEnum.instance(KMType.DEVICE_LOCKED, KMType.DEVICE_LOCKED_FALSE); + // Arguments + short arrPtr = KMArray.instance((short) 6); + KMArray vals = KMArray.cast(arrPtr); + vals.add((short)0, versionPatchPtr); + vals.add((short) 1, versionPatchPtr); + vals.add((short) 2, bootKeyPtr); + vals.add((short) 3, bootHashPtr); + vals.add((short) 4, bootStatePtr); + vals.add((short) 5, deviceLockedPtr); + CommandAPDU apdu = encodeApdu((byte)0x24, arrPtr); + // print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(apdu); + Assert.assertEquals(0x9000, response.getSW()); + + } + + private void provisionCmd(CardSimulator simulator) { + // Argument 1 + short arrPtr = KMArray.instance((short) 1); + KMArray vals = KMArray.cast(arrPtr); + vals.add((short) 0, KMEnumTag.instance(KMType.ALGORITHM, KMType.RSA)); + short keyparamsPtr = KMKeyParameters.instance(arrPtr); + // Argument 2 + short keyFormatPtr = KMEnum.instance(KMType.KEY_FORMAT, KMType.X509); + // Argument 3 + byte[] byteBlob = new byte[48]; + for (short i = 0; i < 48; i++) { + byteBlob[i] = (byte) i; + } + short keyBlobPtr = KMByteBlob.instance(byteBlob, (short) 0, (short)byteBlob.length); + // Array of expected arguments + short argPtr = KMArray.instance((short) 3); + KMArray arg = KMArray.cast(argPtr); + arg.add((short) 0, keyparamsPtr); + arg.add((short) 1, keyFormatPtr); + arg.add((short) 2, keyBlobPtr); + CommandAPDU apdu = encodeApdu((byte)0x23, argPtr); + // print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(apdu); + Assert.assertEquals(0x9000, response.getSW()); + } + + private void cleanUp(){ + AID appletAID1 = AIDUtil.create("A000000062"); + // Delete i.e. uninstall applet + simulator.deleteApplet(appletAID1); + } + + private CommandAPDU encodeApdu(byte ins, short cmd){ + byte[] buf = new byte[1024]; + buf[0] = (byte)0x80; + buf[1] = ins; + buf[2] = (byte)0x40; + buf[3] = (byte)0x00; + buf[4] = 0; + short len = encoder.encode(cmd, buf, (short) 7); + Util.setShort(buf, (short)5, len); + byte[] apdu = new byte[7+len]; + Util.arrayCopyNonAtomic(buf,(short)0,apdu,(short)0,(short)(7+len)); + //CommandAPDU commandAPDU = new CommandAPDU(0x80, 0x10, 0x40, 0x00, buf, 0, actualLen); + return new CommandAPDU(apdu); + } + + @Test + public void testAesImportKeySuccess() { + init(); + byte[] aesKeySecret = new byte[] {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + short arrPtr = KMArray.instance((short)5); + short boolTag = KMBoolTag.instance(KMType.NO_AUTH_REQUIRED); + short keySize = KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, KMInteger.uint_16((short)128)); + short byteBlob = KMByteBlob.instance((short)1); + KMByteBlob.cast(byteBlob).add((short)0, KMType.ECB); + short blockMode = KMEnumArrayTag.instance(KMType.BLOCK_MODE, byteBlob); + byteBlob = KMByteBlob.instance((short)1); + KMByteBlob.cast(byteBlob).add((short)0, KMType.PKCS7); + short paddingMode = KMEnumArrayTag.instance(KMType.PADDING, byteBlob); + KMArray.cast(arrPtr).add((short)0, boolTag); + KMArray.cast(arrPtr).add((short)1, keySize); + KMArray.cast(arrPtr).add((short)2, blockMode); + KMArray.cast(arrPtr).add((short)3, paddingMode); + KMArray.cast(arrPtr).add((short)4, KMEnumTag.instance(KMType.ALGORITHM, KMType.AES)); + short keyParams = KMKeyParameters.instance(arrPtr); + short keyFormatPtr = KMEnum.instance(KMType.KEY_FORMAT, KMType.RAW); + short keyBlob = KMArray.instance((short)1); + KMArray.cast(keyBlob).add((short)0, KMByteBlob.instance(aesKeySecret,(short)0,(short)16)); + byte[] blob = new byte[256]; + short len = encoder.encode(keyBlob,blob,(short)0); + keyBlob = KMByteBlob.instance(blob, (short)0, len); + arrPtr = KMArray.instance((short)3); + KMArray arg = KMArray.cast(arrPtr); + arg.add((short) 0, keyParams); + arg.add((short)1, keyFormatPtr); + arg.add((short)2, keyBlob); + CommandAPDU apdu = encodeApdu((byte)0x11, arrPtr); + // print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(apdu); + short ret = KMArray.instance((short) 3); + KMArray.cast(ret).add((short) 0, KMInteger.exp()); + KMArray.cast(ret).add((short)1, KMByteBlob.exp()); + short inst = KMKeyCharacteristics.exp(); + KMArray.cast(ret).add((short) 2, inst); + byte[] respBuf = response.getBytes(); + len = (short) respBuf.length; + ret = decoder.decode(ret, respBuf, (short) 0, len); + short error = KMInteger.cast(KMArray.cast(ret).get((short)0)).getShort(); + short keyBlobLength = KMByteBlob.cast(KMArray.cast(ret).get((short)1)).length(); + short keyCharacteristics = KMArray.cast(ret).get((short)2); + short hwParams = KMKeyCharacteristics.cast(keyCharacteristics).getHardwareEnforced(); + short swParams = KMKeyCharacteristics.cast(keyCharacteristics).getSoftwareEnforced(); + Assert.assertEquals(0x9000, response.getSW()); + Assert.assertEquals(error, KMError.OK); + short tag = KMKeyParameters.findTag(KMType.BOOL_TAG, KMType.NO_AUTH_REQUIRED, hwParams); + Assert.assertEquals(KMBoolTag.cast(tag).getVal(),0x01); + tag = KMKeyParameters.findTag(KMType.UINT_TAG, KMType.KEYSIZE, hwParams); + Assert.assertEquals(KMInteger.cast(KMIntegerTag.cast(tag).getValue()).getShort(), 128); + tag = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.PADDING, hwParams); + Assert.assertTrue(KMEnumArrayTag.cast(tag).contains(KMType.PKCS7)); + tag = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.BLOCK_MODE, hwParams); + Assert.assertTrue(KMEnumArrayTag.cast(tag).contains(KMType.ECB)); + tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ALGORITHM, hwParams); + Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.AES); + tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ORIGIN, hwParams); + Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.IMPORTED); + cleanUp(); + } + + @Test + public void testHmacImportKeySuccess() { + init(); + byte[] hmacKeySecret = new byte[] {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + short arrPtr = KMArray.instance((short)5); + short boolTag = KMBoolTag.instance(KMType.NO_AUTH_REQUIRED); + short keySize = KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, KMInteger.uint_16((short)128)); + short byteBlob = KMByteBlob.instance((short)1); + KMByteBlob.cast(byteBlob).add((short)0, KMType.SHA2_256); + short digest = KMEnumArrayTag.instance(KMType.DIGEST, byteBlob); + short minMacLength = KMIntegerTag.instance(KMType.UINT_TAG,KMType.MIN_MAC_LENGTH, KMInteger.uint_16((short)256)); + KMArray.cast(arrPtr).add((short)0, boolTag); + KMArray.cast(arrPtr).add((short)1, keySize); + KMArray.cast(arrPtr).add((short)2, digest); + KMArray.cast(arrPtr).add((short)3, minMacLength); + KMArray.cast(arrPtr).add((short)4, KMEnumTag.instance(KMType.ALGORITHM, KMType.HMAC)); + short keyParams = KMKeyParameters.instance(arrPtr); + short keyFormatPtr = KMEnum.instance(KMType.KEY_FORMAT, KMType.RAW); + short keyBlob = KMArray.instance((short)1); + KMArray.cast(keyBlob).add((short)0, KMByteBlob.instance(hmacKeySecret,(short)0,(short)16)); + byte[] blob = new byte[256]; + short len = encoder.encode(keyBlob,blob,(short)0); + keyBlob = KMByteBlob.instance(blob, (short)0, len); + arrPtr = KMArray.instance((short)3); + KMArray arg = KMArray.cast(arrPtr); + arg.add((short) 0, keyParams); + arg.add((short)1, keyFormatPtr); + arg.add((short)2, keyBlob); + CommandAPDU apdu = encodeApdu((byte)0x11, arrPtr); + // print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(apdu); + short ret = KMArray.instance((short) 3); + KMArray.cast(ret).add((short) 0, KMInteger.exp()); + KMArray.cast(ret).add((short)1, KMByteBlob.exp()); + short inst = KMKeyCharacteristics.exp(); + KMArray.cast(ret).add((short) 2, inst); + byte[] respBuf = response.getBytes(); + len = (short) respBuf.length; + ret = decoder.decode(ret, respBuf, (short) 0, len); + short error = KMInteger.cast(KMArray.cast(ret).get((short)0)).getShort(); + short keyBlobLength = KMByteBlob.cast(KMArray.cast(ret).get((short)1)).length(); + short keyCharacteristics = KMArray.cast(ret).get((short)2); + short hwParams = KMKeyCharacteristics.cast(keyCharacteristics).getHardwareEnforced(); + short swParams = KMKeyCharacteristics.cast(keyCharacteristics).getSoftwareEnforced(); + Assert.assertEquals(0x9000, response.getSW()); + Assert.assertEquals(error, KMError.OK); + short tag = KMKeyParameters.findTag(KMType.BOOL_TAG, KMType.NO_AUTH_REQUIRED, hwParams); + Assert.assertEquals(KMBoolTag.cast(tag).getVal(),0x01); + tag = KMKeyParameters.findTag(KMType.UINT_TAG, KMType.KEYSIZE, hwParams); + Assert.assertEquals(KMInteger.cast(KMIntegerTag.cast(tag).getValue()).getShort(), 128); + tag = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.DIGEST, hwParams); + Assert.assertTrue(KMEnumArrayTag.cast(tag).contains(KMType.SHA2_256)); + tag = KMKeyParameters.findTag(KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, hwParams); + Assert.assertEquals(KMInteger.cast(KMIntegerTag.cast(tag).getValue()).getShort(), 256); + tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ALGORITHM, hwParams); + Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.HMAC); + tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ORIGIN, hwParams); + Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.IMPORTED); + cleanUp(); + } + + @Test + public void testRsaImportKeySuccess() { + init(); + KeyPair rsaKeyPair = cryptoProvider.createRsaKeyPair(); + byte[] pub = new byte[4]; + short len = ((RSAPublicKey)rsaKeyPair.getPublic()).getExponent(pub,(short)1); + byte[] priv = new byte[256]; + byte[] mod = new byte[256]; + len = ((RSAPrivateKey)rsaKeyPair.getPrivate()).getModulus(mod,(short)0); + len = ((RSAPrivateKey)rsaKeyPair.getPrivate()).getExponent(priv,(short)0); + short arrPtr = KMArray.instance((short)6); + short boolTag = KMBoolTag.instance(KMType.NO_AUTH_REQUIRED); + short keySize = KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, KMInteger.uint_16((short)2048)); + short byteBlob = KMByteBlob.instance((short)1); + KMByteBlob.cast(byteBlob).add((short)0, KMType.SHA2_256); + short digest = KMEnumArrayTag.instance(KMType.DIGEST, byteBlob); + short rsaPubExpTag = KMIntegerTag.instance(KMType.ULONG_TAG,KMType.RSA_PUBLIC_EXPONENT, KMInteger.uint_32(pub, (short)0)); + byteBlob = KMByteBlob.instance((short)1); + KMByteBlob.cast(byteBlob).add((short)0, KMType.RSA_PSS); + short padding = KMEnumArrayTag.instance(KMType.PADDING, byteBlob); + KMArray.cast(arrPtr).add((short)0, boolTag); + KMArray.cast(arrPtr).add((short)1, keySize); + KMArray.cast(arrPtr).add((short)2, digest); + KMArray.cast(arrPtr).add((short)3, rsaPubExpTag); + KMArray.cast(arrPtr).add((short)4, KMEnumTag.instance(KMType.ALGORITHM, KMType.RSA)); + KMArray.cast(arrPtr).add((short)5, padding); + short keyParams = KMKeyParameters.instance(arrPtr); + short keyFormatPtr = KMEnum.instance(KMType.KEY_FORMAT, KMType.RAW);// Note: VTS uses PKCS8 + short keyBlob = KMArray.instance((short)2); + KMArray.cast(keyBlob).add((short)0, KMByteBlob.instance(priv,(short)0,(short)256)); + KMArray.cast(keyBlob).add((short)1, KMByteBlob.instance(mod,(short)0,(short)256)); + byte[] blob = new byte[620]; + len = encoder.encode(keyBlob,blob,(short)0); + keyBlob = KMByteBlob.instance(blob, (short)0, len); + arrPtr = KMArray.instance((short)3); + KMArray arg = KMArray.cast(arrPtr); + arg.add((short) 0, keyParams); + arg.add((short)1, keyFormatPtr); + arg.add((short)2, keyBlob); + CommandAPDU apdu = encodeApdu((byte)0x11, arrPtr); + // print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(apdu); + short ret = KMArray.instance((short) 3); + KMArray.cast(ret).add((short) 0, KMInteger.exp()); + KMArray.cast(ret).add((short)1, KMByteBlob.exp()); + short inst = KMKeyCharacteristics.exp(); + KMArray.cast(ret).add((short) 2, inst); + byte[] respBuf = response.getBytes(); + len = (short) respBuf.length; + ret = decoder.decode(ret, respBuf, (short) 0, len); + short error = KMInteger.cast(KMArray.cast(ret).get((short)0)).getShort(); + short keyBlobLength = KMByteBlob.cast(KMArray.cast(ret).get((short)1)).length(); + short keyCharacteristics = KMArray.cast(ret).get((short)2); + short hwParams = KMKeyCharacteristics.cast(keyCharacteristics).getHardwareEnforced(); + short swParams = KMKeyCharacteristics.cast(keyCharacteristics).getSoftwareEnforced(); + Assert.assertEquals(0x9000, response.getSW()); + Assert.assertEquals(error, KMError.OK); + short tag = KMKeyParameters.findTag(KMType.BOOL_TAG, KMType.NO_AUTH_REQUIRED, hwParams); + Assert.assertEquals(KMBoolTag.cast(tag).getVal(),0x01); + tag = KMKeyParameters.findTag(KMType.UINT_TAG, KMType.KEYSIZE, hwParams); + Assert.assertEquals(KMInteger.cast(KMIntegerTag.cast(tag).getValue()).getShort(), 2048); + tag = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.DIGEST, hwParams); + Assert.assertTrue(KMEnumArrayTag.cast(tag).contains(KMType.SHA2_256)); + tag = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.PADDING, hwParams); + Assert.assertTrue(KMEnumArrayTag.cast(tag).contains(KMType.RSA_PSS)); + tag = KMKeyParameters.findTag(KMType.ULONG_TAG, KMType.RSA_PUBLIC_EXPONENT, hwParams); + Assert.assertEquals(KMInteger.cast(KMIntegerTag.cast(tag).getValue()).getSignificantShort(), 0x01); + Assert.assertEquals(KMInteger.cast(KMIntegerTag.cast(tag).getValue()).getShort(), 0x01); + tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ALGORITHM, hwParams); + Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.RSA); + tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ORIGIN, hwParams); + Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.IMPORTED); + cleanUp(); + } + + @Test + public void testEcImportKeySuccess() { + init(); + KeyPair ecKeyPair = cryptoProvider.createECKeyPair(); + byte[] pub = new byte[128]; + short len = ((ECPublicKey)ecKeyPair.getPublic()).getW(pub,(short)0); + short pubBlob = KMByteBlob.instance(pub,(short)0,len); + byte[] priv = new byte[32]; + len = ((ECPrivateKey)ecKeyPair.getPrivate()).getS(priv,(short)0); + short privBlob = KMByteBlob.instance(priv,(short)0,len); + short arrPtr = KMArray.instance((short)5); + short boolTag = KMBoolTag.instance(KMType.NO_AUTH_REQUIRED); + short keySize = KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, KMInteger.uint_16((short)256)); + short byteBlob = KMByteBlob.instance((short)1); + KMByteBlob.cast(byteBlob).add((short)0, KMType.SHA2_256); + short digest = KMEnumArrayTag.instance(KMType.DIGEST, byteBlob); + short ecCurve = KMEnumTag.instance(KMType.ECCURVE, KMType.P_256); + KMArray.cast(arrPtr).add((short)0, boolTag); + KMArray.cast(arrPtr).add((short)1, keySize); + KMArray.cast(arrPtr).add((short)2, digest); + KMArray.cast(arrPtr).add((short)3, ecCurve); + KMArray.cast(arrPtr).add((short)4, KMEnumTag.instance(KMType.ALGORITHM, KMType.EC)); + short keyParams = KMKeyParameters.instance(arrPtr); + short keyFormatPtr = KMEnum.instance(KMType.KEY_FORMAT, KMType.RAW);// Note: VTS uses PKCS8 + short keyBlob = KMArray.instance((short)3); + KMArray.cast(keyBlob).add((short)0, privBlob); + KMArray.cast(keyBlob).add((short)1, pubBlob); + KMArray.cast(keyBlob).add((short)2, ecCurve); + byte[] blob = new byte[128]; + len = encoder.encode(keyBlob,blob,(short)0); + keyBlob = KMByteBlob.instance(blob, (short)0, len); + arrPtr = KMArray.instance((short)3); + KMArray arg = KMArray.cast(arrPtr); + arg.add((short) 0, keyParams); + arg.add((short)1, keyFormatPtr); + arg.add((short)2, keyBlob); + CommandAPDU apdu = encodeApdu((byte)0x11, arrPtr); + // print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(apdu); + short ret = KMArray.instance((short) 3); + KMArray.cast(ret).add((short) 0, KMInteger.exp()); + KMArray.cast(ret).add((short)1, KMByteBlob.exp()); + short inst = KMKeyCharacteristics.exp(); + KMArray.cast(ret).add((short) 2, inst); + byte[] respBuf = response.getBytes(); + len = (short) respBuf.length; + ret = decoder.decode(ret, respBuf, (short) 0, len); + short error = KMInteger.cast(KMArray.cast(ret).get((short)0)).getShort(); + short keyBlobLength = KMByteBlob.cast(KMArray.cast(ret).get((short)1)).length(); + short keyCharacteristics = KMArray.cast(ret).get((short)2); + short hwParams = KMKeyCharacteristics.cast(keyCharacteristics).getHardwareEnforced(); + short swParams = KMKeyCharacteristics.cast(keyCharacteristics).getSoftwareEnforced(); + Assert.assertEquals(0x9000, response.getSW()); + Assert.assertEquals(error, KMError.OK); + short tag = KMKeyParameters.findTag(KMType.BOOL_TAG, KMType.NO_AUTH_REQUIRED, hwParams); + Assert.assertEquals(KMBoolTag.cast(tag).getVal(),0x01); + tag = KMKeyParameters.findTag(KMType.UINT_TAG, KMType.KEYSIZE, hwParams); + Assert.assertEquals(KMInteger.cast(KMIntegerTag.cast(tag).getValue()).getShort(), 256); + tag = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.DIGEST, hwParams); + Assert.assertTrue(KMEnumArrayTag.cast(tag).contains(KMType.SHA2_256)); + tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ECCURVE, hwParams); + Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.P_256); + tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ALGORITHM, hwParams); + Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.EC); + tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ORIGIN, hwParams); + Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.IMPORTED); + cleanUp(); + } + + @Test + public void testRsaGenerateKeySuccess() { + init(); + short ret = generateRsaKey(null, null); + short error = KMInteger.cast(KMArray.cast(ret).get((short)0)).getShort(); + short keyBlobLength = KMByteBlob.cast(KMArray.cast(ret).get((short)1)).length(); + short keyCharacteristics = KMArray.cast(ret).get((short)2); + short hwParams = KMKeyCharacteristics.cast(keyCharacteristics).getHardwareEnforced(); + short swParams = KMKeyCharacteristics.cast(keyCharacteristics).getSoftwareEnforced(); + Assert.assertEquals(error, KMError.OK); + short tag = KMKeyParameters.findTag(KMType.UINT_TAG, KMType.KEYSIZE, hwParams); + Assert.assertEquals(KMInteger.cast(KMIntegerTag.cast(tag).getValue()).getShort(), 2048); + tag = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.DIGEST, hwParams); + Assert.assertTrue(KMEnumArrayTag.cast(tag).contains(KMType.DIGEST_NONE)); + tag = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.PADDING, hwParams); + Assert.assertTrue(KMEnumArrayTag.cast(tag).contains(KMType.PADDING_NONE)); + tag = KMKeyParameters.findTag(KMType.ULONG_TAG, KMType.RSA_PUBLIC_EXPONENT, hwParams); + Assert.assertEquals(KMInteger.cast(KMIntegerTag.cast(tag).getValue()).getSignificantShort(), 0x01); + Assert.assertEquals(KMInteger.cast(KMIntegerTag.cast(tag).getValue()).getShort(), 0x01); + tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ALGORITHM, hwParams); + Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.RSA); + tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ORIGIN, hwParams); + Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.GENERATED); + cleanUp(); + } + + private short generateRsaKey(byte[] clientId, byte[] appData){ + short tagCount = 5; + if(clientId != null) tagCount++; + if(appData != null) tagCount++; + short arrPtr = KMArray.instance(tagCount); + short keySize = KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, KMInteger.uint_16((short)2048)); + short byteBlob = KMByteBlob.instance((short)1); + KMByteBlob.cast(byteBlob).add((short)0, KMType.DIGEST_NONE); + short digest = KMEnumArrayTag.instance(KMType.DIGEST, byteBlob); + byteBlob = KMByteBlob.instance((short)1); + KMByteBlob.cast(byteBlob).add((short)0, KMType.PADDING_NONE); + short padding = KMEnumArrayTag.instance(KMType.PADDING, byteBlob); + byte[] pub = {0,1,0,1}; + short rsaPubExpTag = KMIntegerTag.instance(KMType.ULONG_TAG,KMType.RSA_PUBLIC_EXPONENT, KMInteger.uint_32(pub, (short)0)); + short tagIndex = 0; + KMArray.cast(arrPtr).add(tagIndex++, keySize); + KMArray.cast(arrPtr).add(tagIndex++, digest); + KMArray.cast(arrPtr).add(tagIndex++, rsaPubExpTag); + KMArray.cast(arrPtr).add(tagIndex++, KMEnumTag.instance(KMType.ALGORITHM, KMType.RSA)); + KMArray.cast(arrPtr).add(tagIndex++, padding); + if(clientId != null)KMArray.cast(arrPtr).add(tagIndex++, + KMByteTag.instance(KMType.APPLICATION_ID, KMByteBlob.instance(clientId,(short)0,(short)clientId.length))); + if(appData != null)KMArray.cast(arrPtr).add(tagIndex++, + KMByteTag.instance(KMType.APPLICATION_DATA, KMByteBlob.instance(appData,(short)0,(short)appData.length))); + short keyParams = KMKeyParameters.instance(arrPtr); + arrPtr = KMArray.instance((short)1); + KMArray arg = KMArray.cast(arrPtr); + arg.add((short) 0, keyParams); + CommandAPDU apdu = encodeApdu((byte)0x10, arrPtr); + // print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(apdu); + Assert.assertEquals(0x9000, response.getSW()); + short ret = KMArray.instance((short) 3); + KMArray.cast(ret).add((short) 0, KMInteger.exp()); + KMArray.cast(ret).add((short)1, KMByteBlob.exp()); + short inst = KMKeyCharacteristics.exp(); + KMArray.cast(ret).add((short) 2, inst); + byte[] respBuf = response.getBytes(); + short len = (short) respBuf.length; + ret = decoder.decode(ret, respBuf, (short) 0, len); + return ret; + } + + @Test + public void testEcGenerateKeySuccess() { + init(); + short arrPtr = KMArray.instance((short)3); + short keySize = KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, KMInteger.uint_16((short)256)); + short byteBlob = KMByteBlob.instance((short)1); + KMByteBlob.cast(byteBlob).add((short)0, KMType.DIGEST_NONE); + short digest = KMEnumArrayTag.instance(KMType.DIGEST, byteBlob); + KMArray.cast(arrPtr).add((short)0, keySize); + KMArray.cast(arrPtr).add((short)1, digest); + KMArray.cast(arrPtr).add((short)2, KMEnumTag.instance(KMType.ALGORITHM, KMType.EC)); + short keyParams = KMKeyParameters.instance(arrPtr); + arrPtr = KMArray.instance((short)1); + KMArray arg = KMArray.cast(arrPtr); + arg.add((short) 0, keyParams); + CommandAPDU apdu = encodeApdu((byte)0x10, arrPtr); + // print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(apdu); + short ret = KMArray.instance((short) 3); + KMArray.cast(ret).add((short) 0, KMInteger.exp()); + KMArray.cast(ret).add((short)1, KMByteBlob.exp()); + short inst = KMKeyCharacteristics.exp(); + KMArray.cast(ret).add((short) 2, inst); + byte[] respBuf = response.getBytes(); + short len = (short) respBuf.length; + ret = decoder.decode(ret, respBuf, (short) 0, len); + short error = KMInteger.cast(KMArray.cast(ret).get((short)0)).getShort(); + short keyBlobLength = KMByteBlob.cast(KMArray.cast(ret).get((short)1)).length(); + short keyCharacteristics = KMArray.cast(ret).get((short)2); + short hwParams = KMKeyCharacteristics.cast(keyCharacteristics).getHardwareEnforced(); + short swParams = KMKeyCharacteristics.cast(keyCharacteristics).getSoftwareEnforced(); + Assert.assertEquals(0x9000, response.getSW()); + Assert.assertEquals(error, KMError.OK); + short tag = KMKeyParameters.findTag(KMType.UINT_TAG, KMType.KEYSIZE, hwParams); + Assert.assertEquals(KMInteger.cast(KMIntegerTag.cast(tag).getValue()).getShort(), 256); + tag = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.DIGEST, hwParams); + Assert.assertTrue(KMEnumArrayTag.cast(tag).contains(KMType.DIGEST_NONE)); + tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ALGORITHM, hwParams); + Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.EC); + tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ORIGIN, hwParams); + Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.GENERATED); + cleanUp(); + } + + @Test + public void testHmacGenerateKeySuccess() { + init(); + short arrPtr = KMArray.instance((short)4); + short keySize = KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, KMInteger.uint_16((short)128)); + short byteBlob = KMByteBlob.instance((short)1); + KMByteBlob.cast(byteBlob).add((short)0, KMType.SHA2_256); + short digest = KMEnumArrayTag.instance(KMType.DIGEST, byteBlob); + short minMacLength = KMIntegerTag.instance(KMType.UINT_TAG,KMType.MIN_MAC_LENGTH, KMInteger.uint_16((short)128)); + KMArray.cast(arrPtr).add((short)0, keySize); + KMArray.cast(arrPtr).add((short)1, digest); + KMArray.cast(arrPtr).add((short)2, minMacLength); + KMArray.cast(arrPtr).add((short)3, KMEnumTag.instance(KMType.ALGORITHM, KMType.HMAC)); + short keyParams = KMKeyParameters.instance(arrPtr); + arrPtr = KMArray.instance((short)1); + KMArray arg = KMArray.cast(arrPtr); + arg.add((short) 0, keyParams); + CommandAPDU apdu = encodeApdu((byte)0x10, arrPtr); + // print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(apdu); + short ret = KMArray.instance((short) 3); + KMArray.cast(ret).add((short) 0, KMInteger.exp()); + KMArray.cast(ret).add((short)1, KMByteBlob.exp()); + short inst = KMKeyCharacteristics.exp(); + KMArray.cast(ret).add((short) 2, inst); + byte[] respBuf = response.getBytes(); + short len = (short) respBuf.length; + ret = decoder.decode(ret, respBuf, (short) 0, len); + short error = KMInteger.cast(KMArray.cast(ret).get((short)0)).getShort(); + short keyBlobLength = KMByteBlob.cast(KMArray.cast(ret).get((short)1)).length(); + short keyCharacteristics = KMArray.cast(ret).get((short)2); + short hwParams = KMKeyCharacteristics.cast(keyCharacteristics).getHardwareEnforced(); + short swParams = KMKeyCharacteristics.cast(keyCharacteristics).getSoftwareEnforced(); + Assert.assertEquals(0x9000, response.getSW()); + Assert.assertEquals(error, KMError.OK); + short tag = KMKeyParameters.findTag(KMType.UINT_TAG, KMType.KEYSIZE, hwParams); + Assert.assertEquals(KMInteger.cast(KMIntegerTag.cast(tag).getValue()).getShort(), 128); + tag = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.DIGEST, hwParams); + Assert.assertTrue(KMEnumArrayTag.cast(tag).contains(KMType.SHA2_256)); + tag = KMKeyParameters.findTag(KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, hwParams); + Assert.assertEquals(KMInteger.cast(KMIntegerTag.cast(tag).getValue()).getShort(), 128); + tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ALGORITHM, hwParams); + Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.HMAC); + tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ORIGIN, hwParams); + Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.GENERATED); + cleanUp(); + } + + @Test + public void testGetKeyCharacteristicsWithIdDataSuccess() { + init(); + byte[] clientId = "clientId".getBytes(); + byte[] appData = "appData".getBytes(); + short ret = generateRsaKey(clientId,appData); + short error = KMInteger.cast(KMArray.cast(ret).get((short)0)).getShort(); + Assert.assertEquals(error, KMError.OK); + short keyBlob = KMArray.cast(ret).get((short)1); + + short arrPtr = KMArray.instance((short)3); + KMArray.cast(arrPtr).add((short)0, keyBlob); + KMArray.cast(arrPtr).add((short)1, KMByteBlob.instance(clientId,(short)0, (short)clientId.length)); + KMArray.cast(arrPtr).add((short)2, KMByteBlob.instance(appData,(short)0, (short)appData.length)); + CommandAPDU apdu = encodeApdu((byte)0x1D, arrPtr); + // print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(apdu); + ret = KMArray.instance((short) 2); + KMArray.cast(ret).add((short) 0, KMInteger.exp()); + short inst = KMKeyCharacteristics.exp(); + KMArray.cast(ret).add((short) 1, inst); + byte[] respBuf = response.getBytes(); + short len = (short) respBuf.length; + ret = decoder.decode(ret, respBuf, (short) 0, len); + error = KMInteger.cast(KMArray.cast(ret).get((short)0)).getShort(); + Assert.assertEquals(error, KMError.OK); + cleanUp(); + } + + @Test + public void testGetKeyCharacteristicsSuccess() { + init(); + short ret = generateRsaKey(null, null); + short error = KMInteger.cast(KMArray.cast(ret).get((short)0)).getShort(); + Assert.assertEquals(error, KMError.OK); + short keyBlob = KMArray.cast(ret).get((short)1); + + short arrPtr = KMArray.instance((short)3); + KMArray.cast(arrPtr).add((short)0, keyBlob); + KMArray.cast(arrPtr).add((short)1, KMByteBlob.instance((short)0)); + KMArray.cast(arrPtr).add((short)2, KMByteBlob.instance((short)0)); + CommandAPDU apdu = encodeApdu((byte)0x1D, arrPtr); + // print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(apdu); + ret = KMArray.instance((short) 2); + KMArray.cast(ret).add((short) 0, KMInteger.exp()); + short inst = KMKeyCharacteristics.exp(); + KMArray.cast(ret).add((short) 1, inst); + byte[] respBuf = response.getBytes(); + short len = (short) respBuf.length; + ret = decoder.decode(ret, respBuf, (short) 0, len); + error = KMInteger.cast(KMArray.cast(ret).get((short)0)).getShort(); + Assert.assertEquals(error, KMError.OK); + cleanUp(); + } +} diff --git a/Applet/JavaCardKeymaster.scr b/Applet/JavaCardKeymaster.scr index b1a669be..6380faa2 100644 --- a/Applet/JavaCardKeymaster.scr +++ b/Applet/JavaCardKeymaster.scr @@ -10,7 +10,7 @@ output on; 0x80 0x23 0x40 0x00 0x3B 0x83 0xA1 0x1A 0x10 0x00 0x00 0x02 0x01 0x00 0x58 0x30 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0A 0x0B 0x0C 0x0D 0x0E 0x0F 0x10 0x11 0x12 0x13 0x14 0x15 0x16 0x17 0x18 0x19 0x1A 0x1B 0x1C 0x1D 0x1E 0x1F 0x20 0x21 0x22 0x23 0x24 0x25 0x26 0x27 0x28 0x29 0x2A 0x2B 0x2C 0x2D 0x2E 0x2F 0x7F; // Send Set Boot Params command - 0x80 0x24 0x40 0x00 0x53 0x86 0x1A 0x30 0x00 0x02 0xC1 0x01 0x1A 0x30 0x00 0x02 0xC2 0x01 0x58 0x20 0x30 0x30 0x30 0x31 0x31 0x31 0x32 0x32 0x32 0x33 0x33 0x33 0x34 0x34 0x34 0x35 0x35 0x35 0x36 0x36 0x36 0x37 0x37 0x37 0x38 0x38 0x38 0x39 0x39 0x39 0x30 0x30 0x58 0x20 0x30 0x30 0x30 0x31 0x31 0x31 0x32 0x32 0x32 0x33 0x33 0x33 0x34 0x34 0x34 0x35 0x35 0x35 0x36 0x36 0x36 0x37 0x37 0x37 0x38 0x38 0x38 0x39 0x39 0x39 0x30 0x30 0x02 0x00 0x7F; + 0x80 0x24 0x40 0x00 0x49 0x86 0x01 0x01 0x58 0x20 0x30 0x30 0x30 0x31 0x31 0x31 0x32 0x32 0x32 0x33 0x33 0x33 0x34 0x34 0x34 0x35 0x35 0x35 0x36 0x36 0x36 0x37 0x37 0x37 0x38 0x38 0x38 0x39 0x39 0x39 0x30 0x30 0x58 0x20 0x30 0x30 0x30 0x31 0x31 0x31 0x32 0x32 0x32 0x33 0x33 0x33 0x34 0x34 0x34 0x35 0x35 0x35 0x36 0x36 0x36 0x37 0x37 0x37 0x38 0x38 0x38 0x39 0x39 0x39 0x30 0x30 0x02 0x00 0x7F; // Send getHardwareInfo command 0x80 0x1E 0x40 0x00 0x00 0x7F; @@ -19,32 +19,32 @@ output on; 0x80 0x18 0x40 0x00 0x23 0x81 0x58 0x20 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0A 0x0B 0x0C 0x0D 0x0E 0x0F 0x10 0x11 0x12 0x13 0x14 0x15 0x16 0x17 0x18 0x19 0x1A 0x1B 0x1C 0x1D 0x1E 0x1F 0x7F; // Generate Key - RSA Key command - 0x80 0x10 0x40 0x00 0x2E 0x81 0xA5 0x1A 0x10 0x00 0x00 0x02 0x01 0x1A 0x30 0x00 0x01 0xF5 0x1A 0x01 0x02 0x03 0x04 0x1A 0x90 0x00 0x02 0x59 0x44 0x54 0x65 0x73 0x74 0x1A 0x50 0x00 0x00 0xC8 0x1A 0x00 0x01 0x00 0x01 0x1A 0x30 0x00 0x00 0x03 0x19 0x08 0x00 0x7F; + 0x80 0x10 0x40 0x00 0x22 0x81 0xA4 0x1A 0x10 0x00 0x00 0x02 0x01 0x1A 0x30 0x00 0x00 0x03 0x19 0x08 0x00 0x1A 0x50 0x00 0x00 0xC8 0x1A 0x00 0x01 0x00 0x01 0x1A 0x20 0x00 0x00 0x01 0x42 0x02 0x03 0x7F; // Generate Key - AES Key command - 0x80 0x10 0x40 0x00 0x2F 0x81 0xA5 0x1A 0x10 0x00 0x00 0x02 0x18 0x20 0x1A 0x30 0x00 0x01 0xF5 0x1A 0x01 0x02 0x03 0x04 0x1A 0x90 0x00 0x02 0x59 0x44 0x54 0x65 0x73 0x74 0x1A 0x50 0x00 0x00 0xC8 0x1A 0x00 0x01 0x00 0x01 0x1A 0x30 0x00 0x00 0x03 0x19 0x01 0x00 0x7F; + 0x80 0x10 0x40 0x00 0x23 0x81 0xA4 0x1A 0x10 0x00 0x00 0x02 0x18 0x20 0x1A 0x30 0x00 0x00 0x03 0x19 0x01 0x00 0x1A 0x50 0x00 0x00 0xC8 0x1A 0x00 0x01 0x00 0x01 0x1A 0x20 0x00 0x00 0x01 0x42 0x02 0x03 0x7F; // Generate Key - ECC Key command - 0x80 0x10 0x40 0x00 0x2E 0x81 0xA5 0x1A 0x10 0x00 0x00 0x02 0x03 0x1A 0x30 0x00 0x01 0xF5 0x1A 0x01 0x02 0x03 0x04 0x1A 0x90 0x00 0x02 0x59 0x44 0x54 0x65 0x73 0x74 0x1A 0x50 0x00 0x00 0xC8 0x1A 0x00 0x01 0x00 0x01 0x1A 0x30 0x00 0x00 0x03 0x19 0x01 0x00 0x7F; + 0x80 0x10 0x40 0x00 0x22 0x81 0xA4 0x1A 0x10 0x00 0x00 0x02 0x03 0x1A 0x30 0x00 0x00 0x03 0x19 0x01 0x00 0x1A 0x50 0x00 0x00 0xC8 0x1A 0x00 0x01 0x00 0x01 0x1A 0x20 0x00 0x00 0x01 0x42 0x02 0x03 0x7F; // Generate Key - DES Key command - 0x80 0x10 0x40 0x00 0x2E 0x81 0xA5 0x1A 0x10 0x00 0x00 0x02 0x18 0x21 0x1A 0x30 0x00 0x01 0xF5 0x1A 0x01 0x02 0x03 0x04 0x1A 0x90 0x00 0x02 0x59 0x44 0x54 0x65 0x73 0x74 0x1A 0x50 0x00 0x00 0xC8 0x1A 0x00 0x01 0x00 0x01 0x1A 0x30 0x00 0x00 0x03 0x18 0xA8 0x7F; + 0x80 0x10 0x40 0x00 0x22 0x81 0xA4 0x1A 0x10 0x00 0x00 0x02 0x18 0x21 0x1A 0x30 0x00 0x00 0x03 0x18 0xA8 0x1A 0x50 0x00 0x00 0xC8 0x1A 0x00 0x01 0x00 0x01 0x1A 0x20 0x00 0x00 0x01 0x42 0x02 0x03 0x7F; // Generate Key - HMAC Key command - 0x80 0x10 0x40 0x00 0x33 0x81 0xA6 0x1A 0x10 0x00 0x00 0x02 0x18 0x80 0x1A 0x30 0x00 0x01 0xF5 0x1A 0x01 0x02 0x03 0x04 0x1A 0x90 0x00 0x02 0x59 0x44 0x54 0x65 0x73 0x74 0x1A 0x30 0x00 0x00 0x08 0x18 0x80 0x1A 0x30 0x00 0x00 0x03 0x18 0x80 0x1A 0x20 0x00 0x00 0x05 0x42 0x02 0x04 0x7F; + 0x80 0x10 0x40 0x00 0x32 0x81 0xA6 0x1A 0x10 0x00 0x00 0x02 0x18 0x80 0x1A 0x30 0x00 0x01 0xF5 0x1A 0x01 0x02 0x03 0x04 0x1A 0x90 0x00 0x02 0x59 0x44 0x54 0x65 0x73 0x74 0x1A 0x30 0x00 0x00 0x08 0x18 0x80 0x1A 0x30 0x00 0x00 0x03 0x18 0x80 0x1A 0x20 0x00 0x00 0x05 0x41 0x04 0x7F; // Import RSA Key - 0x80 0x11 0x40 0x00 0xAE 0x83 0xA4 0x1A 0x10 0x00 0x00 0x02 0x01 0x1A 0x30 0x00 0x01 0xF5 0x1A 0x01 0x02 0x03 0x04 0x1A 0x90 0x00 0x02 0x59 0x44 0x54 0x65 0x73 0x74 0x1A 0x50 0x00 0x00 0xC8 0x1A 0x00 0x01 0x00 0x01 0x03 0x58 0x85 0x82 0x58 0x40 0x1F 0x1E 0xBD 0xB0 0xE0 0x3A 0x42 0xD7 0x7A 0x2C 0xBE 0xB7 0x00 0x7F 0x98 0x3C 0x32 0xC3 0x43 0x80 0x16 0x75 0x09 0xF4 0xAA 0x4E 0x45 0x85 0xE0 0x8B 0x34 0xC3 0x5B 0x3C 0x03 0x4A 0x2D 0x35 0x7D 0xFC 0x31 0x6A 0xB6 0x49 0xB1 0x3F 0x92 0x90 0x21 0x4C 0x99 0x5B 0x8B 0x4E 0xD6 0x00 0x7D 0x01 0x5D 0xEC 0x1F 0x87 0x6E 0x81 0x58 0x40 0x80 0x94 0x2B 0xC3 0x69 0x81 0x10 0xF9 0x66 0x3A 0xB2 0x4D 0x43 0x05 0x03 0xD6 0x79 0x4B 0x25 0xFB 0x30 0x98 0xE0 0x8E 0x5A 0x1A 0xA8 0xC0 0xFE 0x5F 0x0C 0x79 0x66 0x2C 0x0B 0x9D 0xDA 0xA5 0x45 0x4F 0x29 0x2C 0x80 0x9E 0xAF 0xB0 0x0A 0x6C 0xEA 0x5B 0xAE 0x22 0xF2 0x6C 0x37 0xFB 0x69 0x5F 0xED 0xDF 0xDB 0x3F 0x17 0xE5 0x7F; + 0x80 0x11 0x40 0x00 0xB4 0x83 0xA5 0x1A 0x10 0x00 0x00 0x02 0x01 0x1A 0x30 0x00 0x01 0xF5 0x1A 0x01 0x02 0x03 0x04 0x1A 0x90 0x00 0x02 0x59 0x44 0x54 0x65 0x73 0x74 0x1A 0x50 0x00 0x00 0xC8 0x1A 0x00 0x01 0x00 0x01 0x1A 0x70 0x00 0x01 0xF7 0x01 0x03 0x58 0x85 0x82 0x58 0x40 0x80 0x9A 0x87 0x4C 0xE1 0xA0 0x71 0x44 0xAE 0x45 0xDD 0x8D 0x1C 0x05 0xB4 0xD0 0x44 0x23 0xDD 0x42 0xA7 0xC9 0x53 0x44 0xAC 0x31 0x4A 0x22 0x4A 0x02 0x65 0xA0 0xAA 0x21 0xA8 0x30 0x94 0x7D 0x13 0xA1 0xBC 0x89 0x81 0xB5 0x54 0xDE 0x75 0x82 0xB9 0x0B 0x1A 0x7A 0x81 0x0C 0x51 0xE0 0x2F 0x91 0x97 0xD4 0xE8 0x33 0x27 0x61 0x58 0x40 0x92 0x6C 0x79 0x17 0xBB 0x36 0x6F 0xB7 0x58 0x25 0x84 0x98 0xA9 0x56 0x07 0xE6 0x07 0xF6 0x26 0x92 0x15 0xF6 0x21 0x9F 0x6C 0xF0 0xB4 0xE7 0x20 0x42 0xAC 0xB6 0xD8 0x30 0x61 0x06 0xC9 0x3B 0x30 0x67 0x1E 0x8D 0x74 0x11 0x8B 0x06 0x98 0xAB 0x8D 0x6A 0x6C 0xCD 0xB7 0x2F 0xC3 0xA8 0x30 0xC7 0x68 0x03 0x4F 0x72 0xC7 0x5B 0x7F; // Import EC Key - 0x80 0x11 0x40 0x00 0x7D 0x83 0xA4 0x1A 0x10 0x00 0x00 0x02 0x03 0x1A 0x30 0x00 0x01 0xF5 0x1A 0x01 0x02 0x03 0x04 0x1A 0x90 0x00 0x02 0x59 0x44 0x54 0x65 0x73 0x74 0x1A 0x50 0x00 0x00 0xC8 0x1A 0x00 0x01 0x00 0x01 0x03 0x58 0x54 0x83 0x58 0x18 0xA8 0x90 0x6D 0x38 0x3A 0x25 0x67 0x08 0x85 0xD3 0x07 0x17 0x3A 0x7C 0x0E 0x81 0x96 0x46 0xA0 0xDA 0x2C 0xBD 0xD2 0xEA 0x58 0x31 0x04 0x95 0xCE 0xB7 0x75 0x21 0xD8 0xAE 0xC9 0xA2 0x99 0xC0 0x00 0x6C 0x2E 0xC2 0x11 0x7D 0x79 0x52 0x56 0xB7 0x3D 0xC0 0xC8 0xE7 0x07 0x6D 0xD1 0xBD 0xCD 0xF6 0x05 0xD3 0xB3 0xF4 0xD1 0x56 0x86 0x90 0xED 0xD3 0x4F 0xA2 0x85 0xC2 0x3B 0xCE 0x45 0x1A 0x10 0x00 0x00 0x0A 0x01 0x7F; + 0x80 0x11 0x40 0x00 0x7D 0x83 0xA4 0x1A 0x10 0x00 0x00 0x02 0x03 0x1A 0x30 0x00 0x01 0xF5 0x1A 0x01 0x02 0x03 0x04 0x1A 0x90 0x00 0x02 0x59 0x44 0x54 0x65 0x73 0x74 0x1A 0x50 0x00 0x00 0xC8 0x1A 0x00 0x01 0x00 0x01 0x03 0x58 0x54 0x83 0x58 0x18 0xA6 0x68 0xDE 0xEC 0x65 0x6C 0xFB 0xEE 0xAA 0x43 0xEF 0x97 0x9D 0x10 0x82 0xF0 0x99 0x5F 0x10 0xF3 0xEE 0x9C 0x38 0x57 0x58 0x31 0x04 0x3A 0xF8 0xF4 0xFA 0x1F 0xE4 0x4D 0x62 0xA1 0xCD 0x26 0x8E 0x1A 0x5A 0xAA 0xF5 0xA8 0x94 0xE3 0x8B 0x4C 0xCE 0x49 0xA1 0x57 0x25 0x81 0x6D 0xBE 0x5C 0x3B 0x07 0x95 0xB6 0x89 0x24 0x6E 0x9D 0x25 0x22 0xE6 0x5F 0x41 0xCC 0x59 0xCE 0x25 0x0C 0x1A 0x10 0x00 0x00 0x0A 0x01 0x7F; // Import AES Key - 0x80 0x11 0x40 0x00 0x41 0x83 0xA5 0x1A 0x10 0x00 0x00 0x02 0x18 0x20 0x1A 0x30 0x00 0x01 0xF5 0x1A 0x01 0x02 0x03 0x04 0x1A 0x90 0x00 0x02 0x59 0x44 0x54 0x65 0x73 0x74 0x1A 0x30 0x00 0x00 0x03 0x19 0x01 0x00 0x1A 0x20 0x00 0x00 0x05 0x42 0x02 0x04 0x03 0x52 0x81 0x50 0xF6 0x16 0x03 0x1C 0x26 0xB5 0x6F 0x2E 0x42 0xCB 0x7C 0x37 0x96 0x1C 0x27 0xBA 0x7F; + 0x80 0x11 0x40 0x00 0x3F 0x83 0xA5 0x1A 0x10 0x00 0x00 0x02 0x18 0x20 0x1A 0x30 0x00 0x01 0xF5 0x1A 0x01 0x02 0x03 0x04 0x1A 0x90 0x00 0x02 0x59 0x44 0x54 0x65 0x73 0x74 0x1A 0x30 0x00 0x00 0x03 0x18 0x80 0x1A 0x20 0x00 0x00 0x05 0x41 0x04 0x03 0x52 0x81 0x50 0x95 0xE6 0x79 0x36 0x64 0xA5 0xEC 0x72 0xBF 0x01 0x4C 0x83 0x6C 0xCD 0xCF 0x51 0x7F; // Import Hmac Key - 0x80 0x11 0x40 0x00 0x47 0x83 0xA6 0x1A 0x10 0x00 0x00 0x02 0x18 0x80 0x1A 0x30 0x00 0x01 0xF5 0x1A 0x01 0x02 0x03 0x04 0x1A 0x90 0x00 0x02 0x59 0x44 0x54 0x65 0x73 0x74 0x1A 0x30 0x00 0x00 0x03 0x18 0x80 0x1A 0x20 0x00 0x00 0x05 0x42 0x02 0x04 0x1A 0x30 0x00 0x00 0x08 0x18 0x80 0x03 0x52 0x81 0x50 0xA8 0x66 0xA3 0xE5 0xBA 0x53 0xE9 0x93 0x00 0xE9 0x1A 0xEC 0xBF 0x8F 0xEC 0x90 0x7F; + 0x80 0x11 0x40 0x00 0x46 0x83 0xA6 0x1A 0x10 0x00 0x00 0x02 0x18 0x80 0x1A 0x30 0x00 0x01 0xF5 0x1A 0x01 0x02 0x03 0x04 0x1A 0x90 0x00 0x02 0x59 0x44 0x54 0x65 0x73 0x74 0x1A 0x30 0x00 0x00 0x03 0x18 0x80 0x1A 0x20 0x00 0x00 0x05 0x41 0x04 0x1A 0x30 0x00 0x00 0x08 0x18 0x80 0x03 0x52 0x81 0x50 0xFC 0xA6 0x8F 0x58 0x68 0x93 0xDE 0xD0 0xC0 0x74 0x1C 0x6F 0x1D 0x39 0x2E 0x4A 0x7F; // Import Des Key - 0x80 0x11 0x40 0x00 0x40 0x83 0xA5 0x1A 0x10 0x00 0x00 0x02 0x18 0x21 0x1A 0x30 0x00 0x01 0xF5 0x1A 0x01 0x02 0x03 0x04 0x1A 0x90 0x00 0x02 0x59 0x44 0x54 0x65 0x73 0x74 0x1A 0x30 0x00 0x00 0x03 0x18 0xA8 0x1A 0x20 0x00 0x00 0x05 0x42 0x02 0x04 0x03 0x52 0x81 0x50 0x80 0x5B 0x6F 0xCB 0xBB 0x1B 0x07 0xD3 0xB6 0xE1 0x67 0xAB 0x51 0xC6 0x34 0x29 0x7F; + 0x80 0x11 0x40 0x00 0x3F 0x83 0xA5 0x1A 0x10 0x00 0x00 0x02 0x18 0x21 0x1A 0x30 0x00 0x01 0xF5 0x1A 0x01 0x02 0x03 0x04 0x1A 0x90 0x00 0x02 0x59 0x44 0x54 0x65 0x73 0x74 0x1A 0x30 0x00 0x00 0x03 0x18 0xA8 0x1A 0x20 0x00 0x00 0x05 0x41 0x04 0x03 0x52 0x81 0x50 0x8B 0xD4 0xD5 0x84 0x37 0x39 0xC0 0x1B 0xDB 0xED 0x3C 0x68 0x99 0x3A 0xDC 0x3D 0x7F; diff --git a/Applet/build.xml b/Applet/build.xml index 17b0a546..41181873 100644 --- a/Applet/build.xml +++ b/Applet/build.xml @@ -4,6 +4,8 @@ + + @@ -60,7 +62,7 @@ - + + + + + + + + @@ -130,13 +146,22 @@ + + + + + + + + + diff --git a/Applet/default.output b/Applet/default.output index 3763ffa7..465ff419 100644 --- a/Applet/default.output +++ b/Applet/default.output @@ -5,16 +5,16 @@ OUTPUT ON; CLA: 80, INS: b8, P1: 00, P2: 00, Lc: 0c, 0a, a0, 00, 00, 00, 62, 03, 01, 0c, 01, 01, 00, Le: 0a, a0, 00, 00, 00, 62, 03, 01, 0c, 01, 01, SW1: 90, SW2: 00 CLA: 00, INS: a4, P1: 04, P2: 00, Lc: 0a, a0, 00, 00, 00, 62, 03, 01, 0c, 01, 01, Le: 00, SW1: 90, SW2: 00 CLA: 80, INS: 23, P1: 40, P2: 00, Lc: 3b, 83, a1, 1a, 10, 00, 00, 02, 01, 00, 58, 30, 00, 01, 02, 03, 04, 05, 06, 07, 08, 09, 0a, 0b, 0c, 0d, 0e, 0f, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 1a, 1b, 1c, 1d, 1e, 1f, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 2a, 2b, 2c, 2d, 2e, 2f, Le: 00, SW1: 90, SW2: 00 -CLA: 80, INS: 24, P1: 40, P2: 00, Lc: 53, 86, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, 58, 20, 30, 30, 30, 31, 31, 31, 32, 32, 32, 33, 33, 33, 34, 34, 34, 35, 35, 35, 36, 36, 36, 37, 37, 37, 38, 38, 38, 39, 39, 39, 30, 30, 58, 20, 30, 30, 30, 31, 31, 31, 32, 32, 32, 33, 33, 33, 34, 34, 34, 35, 35, 35, 36, 36, 36, 37, 37, 37, 38, 38, 38, 39, 39, 39, 30, 30, 02, 00, Le: 00, SW1: 90, SW2: 00 +CLA: 80, INS: 24, P1: 40, P2: 00, Lc: 49, 86, 01, 01, 58, 20, 30, 30, 30, 31, 31, 31, 32, 32, 32, 33, 33, 33, 34, 34, 34, 35, 35, 35, 36, 36, 36, 37, 37, 37, 38, 38, 38, 39, 39, 39, 30, 30, 58, 20, 30, 30, 30, 31, 31, 31, 32, 32, 32, 33, 33, 33, 34, 34, 34, 35, 35, 35, 36, 36, 36, 37, 37, 37, 38, 38, 38, 39, 39, 39, 30, 30, 02, 00, Le: 00, SW1: 90, SW2: 00 CLA: 80, INS: 1e, P1: 40, P2: 00, Lc: 00, Le: 21, 83, 02, 57, 4a, 61, 76, 61, 63, 61, 72, 64, 4b, 65, 79, 6d, 61, 73, 74, 65, 72, 44, 65, 76, 69, 63, 65, 46, 47, 6f, 6f, 67, 6c, 65, SW1: 90, SW2: 00 CLA: 80, INS: 18, P1: 40, P2: 00, Lc: 23, 81, 58, 20, 00, 01, 02, 03, 04, 05, 06, 07, 08, 09, 0a, 0b, 0c, 0d, 0e, 0f, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 1a, 1b, 1c, 1d, 1e, 1f, Le: 00, SW1: 90, SW2: 00 -CLA: 80, INS: 10, P1: 40, P2: 00, Lc: 2e, 81, a5, 1a, 10, 00, 00, 02, 01, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, 1a, 90, 00, 02, 59, 44, 54, 65, 73, 74, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 30, 00, 00, 03, 19, 08, 00, Le: 11, 83, 00, 58, d6, 85, 58, 40, 11, 8c, a2, 01, 8d, 5d, d0, d5, 79, dc, fc, 71, a5, a0, ea, fc, e5, 11, 32, e7, 8a, bd, d1, 5c, a0, d8, 5f, 01, 8e, 4d, b1, f0, 09, 79, 5c, b9, e3, 26, 09, 26, dc, 7c, 21, b4, ee, 9b, 28, aa, c2, 73, 0c, ca, e4, 9b, 29, 5d, a0, f4, aa, 87, 7a, 51, 31, 7a, 4c, 62, 92, 2f, 4c, 7d, da, 2a, 3d, 6d, 92, d2, 0a, 4c, 63, 8e, 40, 1f, 03, a1, b1, 1c, 32, 52, 22, 73, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a6, 1a, 10, 00, 00, 02, 01, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 30, 00, 00, 03, 19, 08, 00, 1a, 10, 00, 02, be, 00, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, 58, 40, b4, 05, c4, 0e, 08, 15, 0e, a5, 86, 84, b2, 03, 00, 2c, cb, d7, 23, 85, c9, c1, 0b, 74, cb, 20, 10, b1, 25, 8e, 4b, 38, d4, 72, da, 2c, 2c, 64, 0e, e9, 2f, b2, d6, 74, 2e, 02, 77, 69, 74, 9f, a9, 98, e7, 7a, 4f, 9e, d4, 06, 62, 0c, 33, 6e, 80, 3a, 3f, e3, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a6, 1a, 10, 00, 00, 02, 01, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 30, 00, 00, 03, 19, 08, 00, 1a, 10, 00, 02, be, 00, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, SW1: 90, SW2: 00 -CLA: 80, INS: 10, P1: 40, P2: 00, Lc: 2f, 81, a5, 1a, 10, 00, 00, 02, 18, 20, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, 1a, 90, 00, 02, 59, 44, 54, 65, 73, 74, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 30, 00, 00, 03, 19, 01, 00, Le: a0, 83, 00, 58, 64, 84, 50, 4d, eb, 33, dc, 4d, 36, 74, 3a, 38, d4, b6, 1d, 0c, 48, 70, a6, 4c, fa, 0f, 3b, 66, 89, 7a, 20, 81, 20, c6, f5, f3, 4c, 14, cc, a8, 4b, 27, 04, 49, a0, 4f, 95, 93, 07, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a6, 1a, 10, 00, 00, 02, 18, 20, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 30, 00, 00, 03, 19, 01, 00, 1a, 10, 00, 02, be, 00, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a6, 1a, 10, 00, 00, 02, 18, 20, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 30, 00, 00, 03, 19, 01, 00, 1a, 10, 00, 02, be, 00, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, SW1: 90, SW2: 00 -CLA: 80, INS: 10, P1: 40, P2: 00, Lc: 2e, 81, a5, 1a, 10, 00, 00, 02, 03, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, 1a, 90, 00, 02, 59, 44, 54, 65, 73, 74, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 30, 00, 00, 03, 19, 01, 00, Le: da, 83, 00, 58, 9f, 85, 58, 18, a7, 1b, 67, ff, 9e, ef, 1a, 20, 88, ce, dc, 29, 49, f1, 69, 0a, c7, 99, 3f, 68, cf, c1, 12, 2a, 4c, ee, 2c, 52, c6, 4d, 6f, e7, 9e, 60, 6c, 96, 50, 4c, 86, 14, 3a, 79, 7a, c6, 2e, cb, 70, 33, 83, c2, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a6, 1a, 10, 00, 00, 02, 03, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 30, 00, 00, 03, 19, 01, 00, 1a, 10, 00, 02, be, 00, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, 58, 31, 04, 5a, 10, b8, d7, e9, be, 0a, e9, 47, 6c, 79, e4, 64, 15, 9c, 9d, 32, c6, 74, dd, 17, 11, 61, b7, 9a, 0c, 57, 8e, a1, 5f, 64, 1e, 8d, 51, 5b, 1e, 19, 4e, cc, 0e, 33, 15, d0, 9e, f6, f5, 8d, 2e, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a6, 1a, 10, 00, 00, 02, 03, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 30, 00, 00, 03, 19, 01, 00, 1a, 10, 00, 02, be, 00, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, SW1: 90, SW2: 00 -CLA: 80, INS: 10, P1: 40, P2: 00, Lc: 2e, 81, a5, 1a, 10, 00, 00, 02, 18, 21, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, 1a, 90, 00, 02, 59, 44, 54, 65, 73, 74, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 30, 00, 00, 03, 18, a8, Le: 9e, 83, 00, 58, 63, 84, 50, 6d, fd, 69, 01, 75, 25, d1, 54, 24, f1, 2c, c2, 6e, 6d, 8c, d3, 4c, e6, a1, 6a, dd, 67, 55, 62, df, f2, ed, 1c, e9, 4c, 42, ef, f8, a4, 56, 52, 52, 03, 73, d7, d7, f2, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a6, 1a, 10, 00, 00, 02, 18, 21, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 30, 00, 00, 03, 18, a8, 1a, 10, 00, 02, be, 00, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a6, 1a, 10, 00, 00, 02, 18, 21, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 30, 00, 00, 03, 18, a8, 1a, 10, 00, 02, be, 00, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, SW1: 90, SW2: 00 -CLA: 80, INS: 10, P1: 40, P2: 00, Lc: 33, 81, a6, 1a, 10, 00, 00, 02, 18, 80, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, 1a, 90, 00, 02, 59, 44, 54, 65, 73, 74, 1a, 30, 00, 00, 08, 18, 80, 1a, 30, 00, 00, 03, 18, 80, 1a, 20, 00, 00, 05, 42, 02, 04, Le: a8, 83, 00, 58, 68, 84, 50, af, ee, 95, 3a, d3, f1, 6a, 0a, e6, 88, 30, 63, fa, 8d, e5, 27, 4c, de, af, d7, 29, c4, 08, 59, 9d, 3b, 55, 59, 4e, 4c, 11, 1f, 0b, 47, 4e, a9, e5, d5, 74, 5a, 14, 20, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a7, 1a, 10, 00, 00, 02, 18, 80, 1a, 30, 00, 00, 08, 18, 80, 1a, 30, 00, 00, 03, 18, 80, 1a, 20, 00, 00, 05, 42, 02, 04, 1a, 10, 00, 02, be, 00, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a7, 1a, 10, 00, 00, 02, 18, 80, 1a, 30, 00, 00, 08, 18, 80, 1a, 30, 00, 00, 03, 18, 80, 1a, 20, 00, 00, 05, 42, 02, 04, 1a, 10, 00, 02, be, 00, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, SW1: 90, SW2: 00 -CLA: 80, INS: 11, P1: 40, P2: 00, Lc: ae, 83, a4, 1a, 10, 00, 00, 02, 01, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, 1a, 90, 00, 02, 59, 44, 54, 65, 73, 74, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 03, 58, 85, 82, 58, 40, 1f, 1e, bd, b0, e0, 3a, 42, d7, 7a, 2c, be, b7, 00, 7f, 98, 3c, 32, c3, 43, 80, 16, 75, 09, f4, aa, 4e, 45, 85, e0, 8b, 34, c3, 5b, 3c, 03, 4a, 2d, 35, 7d, fc, 31, 6a, b6, 49, b1, 3f, 92, 90, 21, 4c, 99, 5b, 8b, 4e, d6, 00, 7d, 01, 5d, ec, 1f, 87, 6e, 81, 58, 40, 80, 94, 2b, c3, 69, 81, 10, f9, 66, 3a, b2, 4d, 43, 05, 03, d6, 79, 4b, 25, fb, 30, 98, e0, 8e, 5a, 1a, a8, c0, fe, 5f, 0c, 79, 66, 2c, 0b, 9d, da, a5, 45, 4f, 29, 2c, 80, 9e, af, b0, 0a, 6c, ea, 5b, ae, 22, f2, 6c, 37, fb, 69, 5f, ed, df, db, 3f, 17, e5, Le: 11, 83, 00, 58, d6, 85, 58, 40, 3b, 5a, c7, 40, 44, 8e, b4, 9b, 73, 68, 11, bc, d7, 6b, 14, fb, 2f, 76, ea, 91, d1, 92, ba, 6c, 70, ce, 13, 5d, f9, 96, b0, 17, 6d, 4a, 6d, 16, fc, 60, 17, cd, 08, 8a, 47, 3a, a9, 7b, e5, 36, 70, 87, 2f, af, 65, cd, 9e, bc, 62, c2, a0, fe, 47, 6b, fa, 73, 4c, f4, b4, 76, af, 25, 7d, eb, 88, 6e, ca, f9, b8, 4c, a4, b9, a1, 20, 29, 43, 74, ac, 06, 8f, 61, 62, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a6, 1a, 10, 00, 00, 02, 01, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 30, 00, 00, 03, 19, 08, 00, 1a, 10, 00, 02, be, 02, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, 58, 40, 80, 94, 2b, c3, 69, 81, 10, f9, 66, 3a, b2, 4d, 43, 05, 03, d6, 79, 4b, 25, fb, 30, 98, e0, 8e, 5a, 1a, a8, c0, fe, 5f, 0c, 79, 66, 2c, 0b, 9d, da, a5, 45, 4f, 29, 2c, 80, 9e, af, b0, 0a, 6c, ea, 5b, ae, 22, f2, 6c, 37, fb, 69, 5f, ed, df, db, 3f, 17, e5, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a6, 1a, 10, 00, 00, 02, 01, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 30, 00, 00, 03, 19, 08, 00, 1a, 10, 00, 02, be, 02, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, SW1: 90, SW2: 00 -CLA: 80, INS: 11, P1: 40, P2: 00, Lc: 7d, 83, a4, 1a, 10, 00, 00, 02, 03, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, 1a, 90, 00, 02, 59, 44, 54, 65, 73, 74, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 03, 58, 54, 83, 58, 18, a8, 90, 6d, 38, 3a, 25, 67, 08, 85, d3, 07, 17, 3a, 7c, 0e, 81, 96, 46, a0, da, 2c, bd, d2, ea, 58, 31, 04, 95, ce, b7, 75, 21, d8, ae, c9, a2, 99, c0, 00, 6c, 2e, c2, 11, 7d, 79, 52, 56, b7, 3d, c0, c8, e7, 07, 6d, d1, bd, cd, f6, 05, d3, b3, f4, d1, 56, 86, 90, ed, d3, 4f, a2, 85, c2, 3b, ce, 45, 1a, 10, 00, 00, 0a, 01, Le: e6, 83, 00, 58, a5, 85, 58, 18, e7, 0d, ce, 40, c4, 39, a6, 32, 09, 23, 63, 1d, cf, 03, 2c, c3, b4, 84, 8a, 1e, ab, d6, 3b, ab, 4c, 6b, 1d, 1e, 6d, 24, 79, 22, 9a, fe, 81, 37, 7c, 4c, 0f, 4a, 14, 9a, 91, 31, 39, 94, ea, a5, 2d, 14, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a7, 1a, 10, 00, 00, 02, 03, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 30, 00, 00, 03, 19, 01, 00, 1a, 10, 00, 00, 0a, 01, 1a, 10, 00, 02, be, 02, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, 58, 31, 04, 95, ce, b7, 75, 21, d8, ae, c9, a2, 99, c0, 00, 6c, 2e, c2, 11, 7d, 79, 52, 56, b7, 3d, c0, c8, e7, 07, 6d, d1, bd, cd, f6, 05, d3, b3, f4, d1, 56, 86, 90, ed, d3, 4f, a2, 85, c2, 3b, ce, 45, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a7, 1a, 10, 00, 00, 02, 03, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 30, 00, 00, 03, 19, 01, 00, 1a, 10, 00, 00, 0a, 01, 1a, 10, 00, 02, be, 02, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, SW1: 90, SW2: 00 -CLA: 80, INS: 11, P1: 40, P2: 00, Lc: 41, 83, a5, 1a, 10, 00, 00, 02, 18, 20, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, 1a, 90, 00, 02, 59, 44, 54, 65, 73, 74, 1a, 30, 00, 00, 03, 19, 01, 00, 1a, 20, 00, 00, 05, 42, 02, 04, 03, 52, 81, 50, f6, 16, 03, 1c, 26, b5, 6f, 2e, 42, cb, 7c, 37, 96, 1c, 27, ba, Le: 9c, 83, 00, 58, 62, 84, 50, b8, 4e, b2, a0, 85, 11, 94, 9a, e3, f6, 13, fe, f9, a1, b3, dd, 4c, f6, 10, 1c, 18, 2b, 4f, 80, 8d, e3, 76, ed, 92, 4c, 41, ab, d8, 54, 0c, 54, 18, 01, 33, 45, 09, 1e, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a6, 1a, 10, 00, 00, 02, 18, 20, 1a, 30, 00, 00, 03, 19, 01, 00, 1a, 20, 00, 00, 05, 42, 02, 04, 1a, 10, 00, 02, be, 02, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a6, 1a, 10, 00, 00, 02, 18, 20, 1a, 30, 00, 00, 03, 19, 01, 00, 1a, 20, 00, 00, 05, 42, 02, 04, 1a, 10, 00, 02, be, 02, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, SW1: 90, SW2: 00 -CLA: 80, INS: 11, P1: 40, P2: 00, Lc: 47, 83, a6, 1a, 10, 00, 00, 02, 18, 80, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, 1a, 90, 00, 02, 59, 44, 54, 65, 73, 74, 1a, 30, 00, 00, 03, 18, 80, 1a, 20, 00, 00, 05, 42, 02, 04, 1a, 30, 00, 00, 08, 18, 80, 03, 52, 81, 50, a8, 66, a3, e5, ba, 53, e9, 93, 00, e9, 1a, ec, bf, 8f, ec, 90, Le: a8, 83, 00, 58, 68, 84, 50, 63, 2d, d8, 58, c8, 5b, 66, 98, da, de, 51, 5f, e5, c8, 90, 99, 4c, 8a, 7e, 7c, 1e, 4c, ba, 19, e5, 66, d5, 27, 2d, 4c, 3d, ed, 1d, 79, c8, d8, b7, 3b, 93, 1e, fd, ad, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a7, 1a, 10, 00, 00, 02, 18, 80, 1a, 30, 00, 00, 03, 18, 80, 1a, 20, 00, 00, 05, 42, 02, 04, 1a, 30, 00, 00, 08, 18, 80, 1a, 10, 00, 02, be, 02, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a7, 1a, 10, 00, 00, 02, 18, 80, 1a, 30, 00, 00, 03, 18, 80, 1a, 20, 00, 00, 05, 42, 02, 04, 1a, 30, 00, 00, 08, 18, 80, 1a, 10, 00, 02, be, 02, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, SW1: 90, SW2: 00 -CLA: 80, INS: 11, P1: 40, P2: 00, Lc: 40, 83, a5, 1a, 10, 00, 00, 02, 18, 21, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, 1a, 90, 00, 02, 59, 44, 54, 65, 73, 74, 1a, 30, 00, 00, 03, 18, a8, 1a, 20, 00, 00, 05, 42, 02, 04, 03, 52, 81, 50, 80, 5b, 6f, cb, bb, 1b, 07, d3, b6, e1, 67, ab, 51, c6, 34, 29, Le: 9a, 83, 00, 58, 61, 84, 50, c0, ec, 75, 64, dd, a9, 3f, 81, 56, aa, 95, c5, 60, c7, 7f, 5b, 4c, ac, 0a, 0f, 27, 80, f4, 2e, 25, 76, 68, 62, 21, 4c, ae, c2, 77, b3, f7, 43, e6, f5, 19, ac, 77, 12, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a6, 1a, 10, 00, 00, 02, 18, 21, 1a, 30, 00, 00, 03, 18, a8, 1a, 20, 00, 00, 05, 42, 02, 04, 1a, 10, 00, 02, be, 02, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a6, 1a, 10, 00, 00, 02, 18, 21, 1a, 30, 00, 00, 03, 18, a8, 1a, 20, 00, 00, 05, 42, 02, 04, 1a, 10, 00, 02, be, 02, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, SW1: 90, SW2: 00 +CLA: 80, INS: 10, P1: 40, P2: 00, Lc: 22, 81, a4, 1a, 10, 00, 00, 02, 01, 1a, 30, 00, 00, 03, 19, 08, 00, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 20, 00, 00, 01, 42, 02, 03, Le: 0d, 83, 00, 58, d4, 85, 58, 40, dc, 1d, ed, 50, 93, 56, 0c, 97, de, e7, 37, 82, 41, be, cc, 97, 19, 46, 6f, 18, 3e, 55, 1d, 95, b5, a2, 32, d6, 8d, c3, f9, ab, 16, e5, 8d, 7f, 8a, fe, 09, 86, f0, 83, 04, 50, ef, e1, 8d, 4e, d3, a9, 53, 99, b8, d4, bc, ad, 8f, a2, 5e, 8a, 84, 1a, ea, 29, 4c, 00, 47, 73, 4c, ad, c1, 48, a8, 52, e8, 13, dd, 4c, 48, c7, 53, a4, 74, a5, 3d, 80, 5c, 1a, ab, 92, 82, a0, a7, 1a, 10, 00, 00, 02, 01, 1a, 30, 00, 00, 03, 19, 08, 00, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 20, 00, 00, 01, 42, 02, 03, 1a, 10, 00, 02, be, 00, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, 58, 40, b4, 05, c4, 0e, 08, 15, 0e, a5, 86, 84, b2, 03, 00, 2c, cb, d7, 23, 85, c9, c1, 0b, 74, cb, 20, 10, b1, 25, 8e, 4b, 38, d4, 72, da, 2c, 2c, 64, 0e, e9, 2f, b2, d6, 74, 2e, 02, 77, 69, 74, 9f, a9, 98, e7, 7a, 4f, 9e, d4, 06, 62, 0c, 33, 6e, 80, 3a, 3f, e3, 82, a0, a7, 1a, 10, 00, 00, 02, 01, 1a, 30, 00, 00, 03, 19, 08, 00, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 20, 00, 00, 01, 42, 02, 03, 1a, 10, 00, 02, be, 00, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, SW1: 90, SW2: 00 +CLA: 80, INS: 10, P1: 40, P2: 00, Lc: 23, 81, a4, 1a, 10, 00, 00, 02, 18, 20, 1a, 30, 00, 00, 03, 19, 01, 00, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 20, 00, 00, 01, 42, 02, 03, Le: 9c, 83, 00, 58, 62, 84, 50, c9, a3, b8, 91, fe, 89, d9, f0, 6e, 62, 84, 1e, 4c, 60, eb, e3, 4c, e3, 82, 9d, 1c, ad, 27, 96, 78, 29, 11, 7e, 2e, 4c, cd, 00, 01, 7c, aa, 9d, 76, 28, 1f, 0a, e0, a0, 82, a0, a7, 1a, 10, 00, 00, 02, 18, 20, 1a, 30, 00, 00, 03, 19, 01, 00, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 20, 00, 00, 01, 42, 02, 03, 1a, 10, 00, 02, be, 00, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, 82, a0, a7, 1a, 10, 00, 00, 02, 18, 20, 1a, 30, 00, 00, 03, 19, 01, 00, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 20, 00, 00, 01, 42, 02, 03, 1a, 10, 00, 02, be, 00, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, SW1: 90, SW2: 00 +CLA: 80, INS: 10, P1: 40, P2: 00, Lc: 22, 81, a4, 1a, 10, 00, 00, 02, 03, 1a, 30, 00, 00, 03, 19, 01, 00, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 20, 00, 00, 01, 42, 02, 03, Le: d6, 83, 00, 58, 9d, 85, 58, 18, ba, 56, b4, 85, 20, 57, a9, b9, 77, 65, ba, e3, f4, 43, e6, 97, 70, fc, 3f, 49, 40, 93, 4e, 36, 4c, ae, 8e, 3e, 17, 1d, 67, 15, cc, 16, e8, f2, c4, 4c, 62, 66, 94, d1, 12, 0f, b0, 59, e4, c4, 1b, 91, 82, a0, a7, 1a, 10, 00, 00, 02, 03, 1a, 30, 00, 00, 03, 19, 01, 00, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 20, 00, 00, 01, 42, 02, 03, 1a, 10, 00, 02, be, 00, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, 58, 31, 04, 5a, 10, b8, d7, e9, be, 0a, e9, 47, 6c, 79, e4, 64, 15, 9c, 9d, 32, c6, 74, dd, 17, 11, 61, b7, 9a, 0c, 57, 8e, a1, 5f, 64, 1e, 8d, 51, 5b, 1e, 19, 4e, cc, 0e, 33, 15, d0, 9e, f6, f5, 8d, 2e, 82, a0, a7, 1a, 10, 00, 00, 02, 03, 1a, 30, 00, 00, 03, 19, 01, 00, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 20, 00, 00, 01, 42, 02, 03, 1a, 10, 00, 02, be, 00, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, SW1: 90, SW2: 00 +CLA: 80, INS: 10, P1: 40, P2: 00, Lc: 22, 81, a4, 1a, 10, 00, 00, 02, 18, 21, 1a, 30, 00, 00, 03, 18, a8, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 20, 00, 00, 01, 42, 02, 03, Le: 9a, 83, 00, 58, 61, 84, 50, ac, 61, 52, d1, e0, b1, 60, 86, 83, 04, f0, 27, 26, 82, 33, 5d, 4c, 70, dd, 86, 23, 76, 5a, 88, cb, f1, 89, 32, 38, 4c, 81, fb, d7, 08, c6, b7, 3d, c3, 4c, 4d, f2, 7f, 82, a0, a7, 1a, 10, 00, 00, 02, 18, 21, 1a, 30, 00, 00, 03, 18, a8, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 20, 00, 00, 01, 42, 02, 03, 1a, 10, 00, 02, be, 00, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, 82, a0, a7, 1a, 10, 00, 00, 02, 18, 21, 1a, 30, 00, 00, 03, 18, a8, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 20, 00, 00, 01, 42, 02, 03, 1a, 10, 00, 02, be, 00, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, SW1: 90, SW2: 00 +CLA: 80, INS: 10, P1: 40, P2: 00, Lc: 32, 81, a6, 1a, 10, 00, 00, 02, 18, 80, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, 1a, 90, 00, 02, 59, 44, 54, 65, 73, 74, 1a, 30, 00, 00, 08, 18, 80, 1a, 30, 00, 00, 03, 18, 80, 1a, 20, 00, 00, 05, 41, 04, Le: a6, 83, 00, 58, 67, 84, 50, b2, 44, 46, 9e, 6a, 14, 04, 41, a6, 58, 9c, 6f, 8b, ae, ed, 82, 4c, 5e, eb, 90, 37, 7a, 7b, f8, 61, 77, 30, 6c, 79, 4c, 0e, b8, 7e, 23, 1c, 0f, d8, af, b1, 97, 98, d6, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a7, 1a, 10, 00, 00, 02, 18, 80, 1a, 30, 00, 00, 08, 18, 80, 1a, 30, 00, 00, 03, 18, 80, 1a, 20, 00, 00, 05, 41, 04, 1a, 10, 00, 02, be, 00, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a7, 1a, 10, 00, 00, 02, 18, 80, 1a, 30, 00, 00, 08, 18, 80, 1a, 30, 00, 00, 03, 18, 80, 1a, 20, 00, 00, 05, 41, 04, 1a, 10, 00, 02, be, 00, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, SW1: 90, SW2: 00 +CLA: 80, INS: 11, P1: 40, P2: 00, Lc: b4, 83, a5, 1a, 10, 00, 00, 02, 01, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, 1a, 90, 00, 02, 59, 44, 54, 65, 73, 74, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 70, 00, 01, f7, 01, 03, 58, 85, 82, 58, 40, 80, 9a, 87, 4c, e1, a0, 71, 44, ae, 45, dd, 8d, 1c, 05, b4, d0, 44, 23, dd, 42, a7, c9, 53, 44, ac, 31, 4a, 22, 4a, 02, 65, a0, aa, 21, a8, 30, 94, 7d, 13, a1, bc, 89, 81, b5, 54, de, 75, 82, b9, 0b, 1a, 7a, 81, 0c, 51, e0, 2f, 91, 97, d4, e8, 33, 27, 61, 58, 40, 92, 6c, 79, 17, bb, 36, 6f, b7, 58, 25, 84, 98, a9, 56, 07, e6, 07, f6, 26, 92, 15, f6, 21, 9f, 6c, f0, b4, e7, 20, 42, ac, b6, d8, 30, 61, 06, c9, 3b, 30, 67, 1e, 8d, 74, 11, 8b, 06, 98, ab, 8d, 6a, 6c, cd, b7, 2f, c3, a8, 30, c7, 68, 03, 4f, 72, c7, 5b, Le: 1d, 83, 00, 58, dc, 85, 58, 40, ee, 9c, 17, 33, 10, 19, 8c, 42, 68, 14, dc, e7, d4, ac, 6e, 86, 74, af, 8c, 02, 9d, 9c, fb, f7, be, 3c, d4, bd, de, 9d, d8, fd, 51, eb, d8, df, fd, ce, a0, 9b, ff, 44, a9, a2, e8, eb, 44, 78, eb, 15, 04, ca, 6a, 98, 07, 7c, bb, ab, 07, be, 72, a0, f9, 1f, 4c, 74, b7, f8, 50, 81, 15, 16, c2, e9, 63, c2, 92, 4c, 75, ca, 0d, 0e, 29, 2e, 75, 05, 64, cf, a6, 91, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a7, 1a, 10, 00, 00, 02, 01, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 70, 00, 01, f7, 01, 1a, 30, 00, 00, 03, 19, 08, 00, 1a, 10, 00, 02, be, 02, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, 58, 40, 92, 6c, 79, 17, bb, 36, 6f, b7, 58, 25, 84, 98, a9, 56, 07, e6, 07, f6, 26, 92, 15, f6, 21, 9f, 6c, f0, b4, e7, 20, 42, ac, b6, d8, 30, 61, 06, c9, 3b, 30, 67, 1e, 8d, 74, 11, 8b, 06, 98, ab, 8d, 6a, 6c, cd, b7, 2f, c3, a8, 30, c7, 68, 03, 4f, 72, c7, 5b, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a7, 1a, 10, 00, 00, 02, 01, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 70, 00, 01, f7, 01, 1a, 30, 00, 00, 03, 19, 08, 00, 1a, 10, 00, 02, be, 02, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, SW1: 90, SW2: 00 +CLA: 80, INS: 11, P1: 40, P2: 00, Lc: 7d, 83, a4, 1a, 10, 00, 00, 02, 03, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, 1a, 90, 00, 02, 59, 44, 54, 65, 73, 74, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 03, 58, 54, 83, 58, 18, a6, 68, de, ec, 65, 6c, fb, ee, aa, 43, ef, 97, 9d, 10, 82, f0, 99, 5f, 10, f3, ee, 9c, 38, 57, 58, 31, 04, 3a, f8, f4, fa, 1f, e4, 4d, 62, a1, cd, 26, 8e, 1a, 5a, aa, f5, a8, 94, e3, 8b, 4c, ce, 49, a1, 57, 25, 81, 6d, be, 5c, 3b, 07, 95, b6, 89, 24, 6e, 9d, 25, 22, e6, 5f, 41, cc, 59, ce, 25, 0c, 1a, 10, 00, 00, 0a, 01, Le: e6, 83, 00, 58, a5, 85, 58, 18, dd, 87, 3a, 89, 4c, b4, 3b, b3, 7a, 02, dd, ac, 10, a9, 06, 27, b7, 86, bb, 06, 90, b8, 89, 86, 4c, 0d, ac, e8, c8, 2a, c9, 2a, e6, 8e, 4d, 7c, 5e, 4c, 5a, 32, 10, 2d, 01, 5a, 76, 62, 0f, e6, 62, 6c, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a7, 1a, 10, 00, 00, 02, 03, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 30, 00, 00, 03, 19, 01, 00, 1a, 10, 00, 00, 0a, 01, 1a, 10, 00, 02, be, 02, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, 58, 31, 04, 3a, f8, f4, fa, 1f, e4, 4d, 62, a1, cd, 26, 8e, 1a, 5a, aa, f5, a8, 94, e3, 8b, 4c, ce, 49, a1, 57, 25, 81, 6d, be, 5c, 3b, 07, 95, b6, 89, 24, 6e, 9d, 25, 22, e6, 5f, 41, cc, 59, ce, 25, 0c, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a7, 1a, 10, 00, 00, 02, 03, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 30, 00, 00, 03, 19, 01, 00, 1a, 10, 00, 00, 0a, 01, 1a, 10, 00, 02, be, 02, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, SW1: 90, SW2: 00 +CLA: 80, INS: 11, P1: 40, P2: 00, Lc: 3f, 83, a5, 1a, 10, 00, 00, 02, 18, 20, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, 1a, 90, 00, 02, 59, 44, 54, 65, 73, 74, 1a, 30, 00, 00, 03, 18, 80, 1a, 20, 00, 00, 05, 41, 04, 03, 52, 81, 50, 95, e6, 79, 36, 64, a5, ec, 72, bf, 01, 4c, 83, 6c, cd, cf, 51, Le: 98, 83, 00, 58, 60, 84, 50, 4c, 88, 06, bc, 20, cd, ba, 26, 2e, d1, 10, af, 70, 6e, 5f, 3a, 4c, 17, 80, e7, 2c, 13, ef, df, 3b, 09, e2, 0a, ff, 4c, b4, d5, 1d, 48, 47, a3, e1, e0, 20, 10, 05, 1c, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a6, 1a, 10, 00, 00, 02, 18, 20, 1a, 30, 00, 00, 03, 18, 80, 1a, 20, 00, 00, 05, 41, 04, 1a, 10, 00, 02, be, 02, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a6, 1a, 10, 00, 00, 02, 18, 20, 1a, 30, 00, 00, 03, 18, 80, 1a, 20, 00, 00, 05, 41, 04, 1a, 10, 00, 02, be, 02, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, SW1: 90, SW2: 00 +CLA: 80, INS: 11, P1: 40, P2: 00, Lc: 46, 83, a6, 1a, 10, 00, 00, 02, 18, 80, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, 1a, 90, 00, 02, 59, 44, 54, 65, 73, 74, 1a, 30, 00, 00, 03, 18, 80, 1a, 20, 00, 00, 05, 41, 04, 1a, 30, 00, 00, 08, 18, 80, 03, 52, 81, 50, fc, a6, 8f, 58, 68, 93, de, d0, c0, 74, 1c, 6f, 1d, 39, 2e, 4a, Le: a6, 83, 00, 58, 67, 84, 50, 0b, 7d, d5, e0, de, 79, f0, ce, 6c, f5, 6d, 5a, f9, f0, 3e, aa, 4c, 36, 7b, 2d, e5, cb, b7, 4a, 8e, 97, 1f, 44, d3, 4c, 4e, 09, 34, d3, 7f, d1, 23, 8b, 51, 70, fe, e8, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a7, 1a, 10, 00, 00, 02, 18, 80, 1a, 30, 00, 00, 03, 18, 80, 1a, 20, 00, 00, 05, 41, 04, 1a, 30, 00, 00, 08, 18, 80, 1a, 10, 00, 02, be, 02, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a7, 1a, 10, 00, 00, 02, 18, 80, 1a, 30, 00, 00, 03, 18, 80, 1a, 20, 00, 00, 05, 41, 04, 1a, 30, 00, 00, 08, 18, 80, 1a, 10, 00, 02, be, 02, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, SW1: 90, SW2: 00 +CLA: 80, INS: 11, P1: 40, P2: 00, Lc: 3f, 83, a5, 1a, 10, 00, 00, 02, 18, 21, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, 1a, 90, 00, 02, 59, 44, 54, 65, 73, 74, 1a, 30, 00, 00, 03, 18, a8, 1a, 20, 00, 00, 05, 41, 04, 03, 52, 81, 50, 8b, d4, d5, 84, 37, 39, c0, 1b, db, ed, 3c, 68, 99, 3a, dc, 3d, Le: 98, 83, 00, 58, 60, 84, 50, ad, 9d, 57, aa, 21, e9, c7, 97, b7, eb, 25, 14, c6, ad, f2, d1, 4c, b4, 12, 76, 4f, b3, 85, 6e, 0f, 79, 87, fc, 40, 4c, fc, 62, 73, 9c, 1b, 64, df, 15, 76, 28, 17, 43, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a6, 1a, 10, 00, 00, 02, 18, 21, 1a, 30, 00, 00, 03, 18, a8, 1a, 20, 00, 00, 05, 41, 04, 1a, 10, 00, 02, be, 02, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a6, 1a, 10, 00, 00, 02, 18, 21, 1a, 30, 00, 00, 03, 18, a8, 1a, 20, 00, 00, 05, 41, 04, 1a, 10, 00, 02, be, 02, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, SW1: 90, SW2: 00 From e8a1546fa8e6fa5b3daa6ba8371cb670fe74e699 Mon Sep 17 00:00:00 2001 From: cpathak Date: Tue, 9 Jun 2020 10:03:47 -0700 Subject: [PATCH 5/6] Removed untested commands from KeymasterApplet Removed untested code from KeymasterApplet. --- .../javacard/keymaster/KMKeymasterApplet.java | 1374 +---------------- 1 file changed, 2 insertions(+), 1372 deletions(-) diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java b/Applet/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java index e8b102dd..bfd769df 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java @@ -506,25 +506,6 @@ private void processGetKeyCharacteristicsCmd(APDU apdu) { } private void processGetHmacSharingParamCmd(APDU apdu) { - // No Arguments - byte[] scratchPad = apdu.getBuffer(); - // Create blob containing seed - tmpVariables[0] = - KMByteBlob.instance(repository.getHmacSeed(), (short) 0, repository.HMAC_SEED_NONCE_SIZE); - // Create blob containing nonce - cryptoProvider.newRandomNumber(scratchPad, (short) 0, repository.HMAC_SEED_NONCE_SIZE); - tmpVariables[1] = KMByteBlob.instance(scratchPad, (short) 0, repository.HMAC_SEED_NONCE_SIZE); - // Create HMAC Sharing Parameters - tmpVariables[2] = KMHmacSharingParameters.instance(); - KMHmacSharingParameters.cast(tmpVariables[2]).setNonce(tmpVariables[1]); - KMHmacSharingParameters.cast(tmpVariables[2]).setSeed(tmpVariables[0]); - // prepare the response - tmpVariables[3] = KMArray.instance((short) 2); - KMArray.cast(tmpVariables[3]).add((short) 0, KMInteger.uint_16(KMError.OK)); - KMArray.cast(tmpVariables[3]).add((short) 1, tmpVariables[2]); - // Encode the response - bufferLength = encoder.encode(tmpVariables[0], buffer, bufferStartOffset); - sendOutgoing(apdu); } private void processDeleteAllKeysCmd(APDU apdu) { @@ -575,303 +556,15 @@ private void processDeleteKeyCmd(APDU apdu) { } private void processComputeSharedHmacCmd(APDU apdu) { - // Receive the incoming request fully from the master into buffer. - receiveIncoming(apdu); - byte[] scratchPad = apdu.getBuffer(); - tmpVariables[1] = KMArray.instance((short) 1); - tmpVariables[2] = KMKeyParameters.exp(); - tmpVariables[3] = KMHmacSharingParameters.exp(); - KMArray.cast(tmpVariables[1]).add((short) 0, KMArray.exp(tmpVariables[3])); // Vector - KMArray.cast(tmpVariables[1]).add((short) 1, tmpVariables[2]); // Key Params - // Decode the arguments - tmpVariables[2] = decoder.decode(tmpVariables[1], buffer, bufferStartOffset, bufferLength); - data[KEY_PARAMETERS] = KMArray.cast(tmpVariables[2]).get((short) 1); - data[HMAC_SHARING_PARAMS] = KMArray.cast(tmpVariables[2]).get((short) 0); - // Concatenate HMAC Params - tmpVariables[0] = 0; - tmpVariables[1] = KMArray.cast(data[HMAC_SHARING_PARAMS]).length(); - tmpVariables[5] = 0; // index in scratchPad - while (tmpVariables[0] < tmpVariables[1]) { - // read HmacSharingParam - tmpVariables[2] = KMArray.cast(data[HMAC_SHARING_PARAMS]).get(tmpVariables[0]); - // get seed - tmpVariables[3] = KMHmacSharingParameters.cast(tmpVariables[2]).getSeed(); - tmpVariables[4] = KMByteBlob.cast(tmpVariables[3]).length(); - // if seed is present - if (tmpVariables[4] == HMAC_SEED_SIZE /*32*/) { - // then copy that to scratchPad - Util.arrayCopyNonAtomic( - KMByteBlob.cast(tmpVariables[3]).getBuffer(), - KMByteBlob.cast(tmpVariables[3]).getStartOff(), - scratchPad, - tmpVariables[5], - tmpVariables[4]); - tmpVariables[5] += tmpVariables[4]; - } - // get nonce - tmpVariables[3] = KMHmacSharingParameters.cast(tmpVariables[2]).getNonce(); - tmpVariables[4] = KMByteBlob.cast(tmpVariables[3]).length(); - // if nonce is not present - if (tmpVariables[4] != HMAC_NONCE_SIZE /*32*/) { - KMException.throwIt(KMError.INVALID_ARGUMENT); - } - // copy nonce to scratchPad - Util.arrayCopyNonAtomic( - KMByteBlob.cast(tmpVariables[3]).getBuffer(), - KMByteBlob.cast(tmpVariables[3]).getStartOff(), - scratchPad, - tmpVariables[5], - tmpVariables[4]); - tmpVariables[5] += tmpVariables[4]; - } - // ckdf to derive hmac key - HMACKey key = - cryptoProvider.cmacKdf( - repository.getHmacKey(), ckdfLable, scratchPad, (short) 0, tmpVariables[5]); - tmpVariables[5] = key.getKey(scratchPad, (short) 0); - repository.initComputedHmac(scratchPad, (short) 0, tmpVariables[5]); - // Generate sharingKey verification - tmpVariables[5] = - cryptoProvider.hmacSign( - key, sharingCheck, (short) 0, (short) sharingCheck.length, scratchPad, (short) 0); - tmpVariables[1] = KMByteBlob.instance(scratchPad, (short) 0, tmpVariables[5]); - // prepare the response - tmpVariables[0] = KMArray.instance((short) 2); - KMArray.cast(tmpVariables[0]).add((short) 0, KMInteger.uint_16(KMError.OK)); - KMArray.cast(tmpVariables[0]).add((short) 1, tmpVariables[1]); - // Encode the response - bufferLength = encoder.encode(tmpVariables[0], buffer, bufferStartOffset); - sendOutgoing(apdu); } private void processUpgradeKeyCmd(APDU apdu) { - // Receive the incoming request fully from the master into buffer. - receiveIncoming(apdu); - byte[] scratchPad = apdu.getBuffer(); - tmpVariables[1] = KMArray.instance((short) 2); - tmpVariables[2] = KMKeyParameters.exp(); - KMArray.cast(tmpVariables[1]).add((short) 0, KMByteBlob.exp()); // Key Blob - KMArray.cast(tmpVariables[1]).add((short) 1, tmpVariables[2]); // Key Params - // Decode the arguments - tmpVariables[2] = decoder.decode(tmpVariables[1], buffer, bufferStartOffset, bufferLength); - data[KEY_BLOB] = KMArray.cast(tmpVariables[2]).get((short) 0); - data[KEY_PARAMETERS] = KMArray.cast(tmpVariables[2]).get((short) 1); - tmpVariables[0] = - KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.APPLICATION_ID, data[KEY_PARAMETERS]); - if (tmpVariables[0] != KMTag.INVALID_VALUE) { - data[APP_ID] = KMByteTag.cast(tmpVariables[0]).getValue(); - } - tmpVariables[0] = - KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.APPLICATION_DATA, data[KEY_PARAMETERS]); - if (tmpVariables[0] != KMTag.INVALID_VALUE) { - data[APP_DATA] = KMByteTag.cast(tmpVariables[0]).getValue(); - } - // parse existing key blob - parseEncryptedKeyBlob(scratchPad); - // validate characteristics to be upgraded. - tmpVariables[0] = - KMIntegerTag.getValue( - scratchPad, (short) 0, KMType.UINT_TAG, KMType.OS_VERSION, data[HW_PARAMETERS]); - if ((tmpVariables[0] != KMType.INVALID_VALUE) - && (Util.arrayCompare( - repository.osVersion, (short) 0, scratchPad, (short) 0, tmpVariables[0]) - != 0)) { - if (Util.arrayCompare(repository.osVersion, (short) 0, scratchPad, (short) 0, tmpVariables[0]) - == -1) { - // If the key characteristics has os version > current os version - Util.arrayFillNonAtomic(scratchPad, (short) 0, tmpVariables[0], (byte) 0); - // If the os version is not zero - if (Util.arrayCompare( - repository.osVersion, (short) 0, scratchPad, (short) 0, tmpVariables[0]) - != 0) { - KMException.throwIt(KMError.INVALID_ARGUMENT); - } - } - } - tmpVariables[0] = - KMIntegerTag.getValue( - scratchPad, (short) 0, KMType.UINT_TAG, KMType.OS_PATCH_LEVEL, data[HW_PARAMETERS]); - if ((tmpVariables[0] != KMType.INVALID_VALUE) - && (Util.arrayCompare(repository.osPatch, (short) 0, scratchPad, (short) 0, tmpVariables[0]) - != 0)) { - if (Util.arrayCompare(repository.osPatch, (short) 0, scratchPad, (short) 0, tmpVariables[0]) - < 0) { - // If the key characteristics has os patch level > current os patch - KMException.throwIt(KMError.INVALID_ARGUMENT); - } - } - // remove Auth Tag - repository.removeAuthTag(data[AUTH_TAG]); - // copy origin - data[ORIGIN] = KMEnumTag.getValue(KMType.ORIGIN, data[HW_PARAMETERS]); - // create new key blob with current os version etc. - createEncryptedKeyBlob(scratchPad); - // persist new auth tag for rollback resistance. - repository.persistAuthTag(data[AUTH_TAG]); - // prepare the response - tmpVariables[0] = KMArray.instance((short) 3); - KMArray.cast(tmpVariables[0]).add((short) 0, KMInteger.uint_16(KMError.OK)); - KMArray.cast(tmpVariables[0]).add((short) 1, data[KEY_BLOB]); - KMArray.cast(tmpVariables[0]).add((short) 2, data[KEY_CHARACTERISTICS]); - // Encode the response - bufferLength = encoder.encode(tmpVariables[0], buffer, bufferStartOffset); - sendOutgoing(apdu); } private void processExportKeyCmd(APDU apdu) { - sendError(apdu, KMError.UNIMPLEMENTED); } private void processImportWrappedKeyCmd(APDU apdu) { - // Currently only RAW formatted import key blob are supported - if (repository.keyBlobCount > repository.MAX_BLOB_STORAGE) { - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - // Receive the incoming request fully from the master into buffer. - receiveIncoming(apdu); - byte[] scratchPad = apdu.getBuffer(); - tmpVariables[1] = KMArray.instance((short) 11); - // Arguments - tmpVariables[2] = KMKeyParameters.exp(); - KMArray.cast(tmpVariables[1]).add((short) 0, tmpVariables[2]); // Key Params - KMArray.cast(tmpVariables[1]).add((short) 1, KMEnum.instance(KMType.KEY_FORMAT)); // Key Format - KMArray.cast(tmpVariables[1]).add((short) 2, KMByteBlob.exp()); // Wrapped Import Key Blob - KMArray.cast(tmpVariables[1]).add((short) 3, KMByteBlob.exp()); // Auth Tag - KMArray.cast(tmpVariables[1]).add((short) 4, KMByteBlob.exp()); // IV - Nonce - KMArray.cast(tmpVariables[1]).add((short) 5, KMByteBlob.exp()); // Encrypted Transport Key - KMArray.cast(tmpVariables[1]).add((short) 6, KMByteBlob.exp()); // Wrapping Key KeyBlob - KMArray.cast(tmpVariables[1]).add((short) 7, KMByteBlob.exp()); // Masking Key - KMArray.cast(tmpVariables[1]).add((short) 8, tmpVariables[2]); // Un-wrapping Params - KMArray.cast(tmpVariables[1]).add((short) 9, KMInteger.exp()); // Password Sid - KMArray.cast(tmpVariables[1]).add((short) 10, KMInteger.exp()); // Biometric Sid - // Decode the arguments - tmpVariables[2] = decoder.decode(tmpVariables[1], buffer, bufferStartOffset, bufferLength); - tmpVariables[3] = KMArray.cast(tmpVariables[2]).get((short) 0); - // get algorithm - tmpVariables[3] = KMEnumTag.getValue(KMType.ALGORITHM, tmpVariables[3]); - if (tmpVariables[3] == KMType.INVALID_VALUE) { - KMException.throwIt(KMError.INVALID_ARGUMENT); - } - tmpVariables[3] = KMEnumTag.getValue(KMType.ALGORITHM, data[KEY_PARAMETERS]); - if (tmpVariables[3] == KMType.RSA - || tmpVariables[3] == KMType.EC) { // RSA and EC not implemented - KMException.throwIt(KMError.UNIMPLEMENTED); - } - // Key format must be RAW format - X509 and PKCS8 not implemented. - tmpVariables[3] = KMArray.cast(tmpVariables[2]).get((short) 1); - tmpVariables[3] = KMEnum.cast(tmpVariables[3]).getVal(); - if (tmpVariables[3] != KMType.RAW) { - KMException.throwIt(KMError.UNIMPLEMENTED); - } - data[AUTH_DATA] = KMArray.cast(tmpVariables[2]).get((short) 3); - data[AUTH_TAG] = KMArray.cast(tmpVariables[2]).get((short) 4); - data[NONCE] = KMArray.cast(tmpVariables[2]).get((short) 5); - data[ENC_TRANSPORT_KEY] = KMArray.cast(tmpVariables[2]).get((short) 6); - data[MASKING_KEY] = KMArray.cast(tmpVariables[2]).get((short) 8); - // Step 1 - parse wrapping key blob - data[KEY_PARAMETERS] = KMArray.cast(tmpVariables[2]).get((short) 9); // wrapping key parameters - // Check for app id and app data. - data[APP_ID] = KMType.INVALID_VALUE; - data[APP_DATA] = KMType.INVALID_VALUE; - tmpVariables[3] = - KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.APPLICATION_ID, data[KEY_PARAMETERS]); - if (tmpVariables[3] != KMTag.INVALID_VALUE) { - data[APP_ID] = KMByteTag.cast(tmpVariables[3]).getValue(); - } - tmpVariables[3] = - KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.APPLICATION_DATA, data[KEY_PARAMETERS]); - if (tmpVariables[3] != KMTag.INVALID_VALUE) { - data[APP_DATA] = KMByteTag.cast(tmpVariables[3]).getValue(); - } - // wrapping key blob - data[KEY_BLOB] = KMArray.cast(tmpVariables[2]).get((short) 7); - parseEncryptedKeyBlob(scratchPad); - - // Step 2 - Decrypt the encrypted transport key - // enforce authorization for WRAP_KEY operation using RSA algorithm according to javacard caps. - if (KMEnumTag.getValue(KMType.ALGORITHM, data[HW_PARAMETERS]) != KMType.RSA) { - KMException.throwIt(KMError.INCOMPATIBLE_ALGORITHM); - } - if (!(KMEnumArrayTag.contains(KMType.DIGEST, KMType.SHA2_256, data[HW_PARAMETERS]))) { - KMException.throwIt(KMError.INCOMPATIBLE_DIGEST); - } - if (!(KMEnumArrayTag.contains(KMType.PADDING, KMType.RSA_OAEP, data[HW_PARAMETERS]))) { - KMException.throwIt(KMError.INCOMPATIBLE_PADDING_MODE); - } - KMCipher cipher = - cryptoProvider.createRsaDecrypt( - KMCipher.CIPHER_RSA, - KMCipher.PAD_PKCS1_OAEP_SHA256, - KMByteBlob.cast(data[SECRET]).getBuffer(), - KMByteBlob.cast(data[SECRET]).getStartOff(), - KMByteBlob.cast(data[SECRET]).length(), - KMByteBlob.cast(data[PUB_KEY]).getBuffer(), - KMByteBlob.cast(data[PUB_KEY]).getStartOff(), - KMByteBlob.cast(data[PUB_KEY]).length()); - // Decrypt the transport key - tmpVariables[3] = - cipher.doFinal( - KMByteBlob.cast(data[ENC_TRANSPORT_KEY]).getBuffer(), - KMByteBlob.cast(data[ENC_TRANSPORT_KEY]).getStartOff(), - KMByteBlob.cast(data[ENC_TRANSPORT_KEY]).length(), - scratchPad, - (short) 0); - data[SECRET] = KMByteBlob.instance(scratchPad, (short) 0, tmpVariables[3]); - cryptoProvider.delete(cipher); - - // Step 3 - XOR with masking key - tmpVariables[4] = KMByteBlob.cast(data[MASKING_KEY]).length(); - if (tmpVariables[3] != tmpVariables[4]) { - KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH); - } - tmpVariables[3] = 0; // index in scratchPad - byte[] buf = KMByteBlob.cast(MASKING_KEY).getBuffer(); - tmpVariables[5] = KMByteBlob.cast(MASKING_KEY).getStartOff(); - while (tmpVariables[3] < tmpVariables[4]) { - scratchPad[tmpVariables[3]] = - (byte) (scratchPad[tmpVariables[3]] ^ buf[(short) (tmpVariables[3] + tmpVariables[5])]); - scratchPad[3]++; - } - data[SECRET] = KMByteBlob.instance(scratchPad, (short) 0, tmpVariables[3]); - - // Step 4 - AES-GCM decrypt - data[IMPORTED_KEY_BLOB] = KMArray.cast(tmpVariables[2]).get((short) 2); - data[AUTH_DATA] = KMArray.cast(tmpVariables[2]).get((short) 3); - data[AUTH_TAG] = KMArray.cast(tmpVariables[2]).get((short) 4); - data[NONCE] = KMArray.cast(tmpVariables[2]).get((short) 5); - data[ENC_TRANSPORT_KEY] = KMArray.cast(tmpVariables[2]).get((short) 6); - data[MASKING_KEY] = KMArray.cast(tmpVariables[2]).get((short) 8); - AESKey key = - cryptoProvider.createAESKey( - KMByteBlob.cast(data[SECRET]).getBuffer(), - KMByteBlob.cast(data[SECRET]).getStartOff(), - KMByteBlob.cast(data[SECRET]).length()); - boolean verification = - cryptoProvider.aesGCMDecrypt( - key, - KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).getBuffer(), - KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).getStartOff(), - KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).length(), - scratchPad, - (short) 0, - KMByteBlob.cast(data[NONCE]).getBuffer(), - KMByteBlob.cast(data[NONCE]).getStartOff(), - KMByteBlob.cast(data[NONCE]).length(), - KMByteBlob.cast(data[AUTH_DATA]).getBuffer(), - KMByteBlob.cast(data[AUTH_DATA]).getStartOff(), - KMByteBlob.cast(data[AUTH_DATA]).length(), - KMByteBlob.cast(data[AUTH_TAG]).getBuffer(), - KMByteBlob.cast(data[AUTH_TAG]).getStartOff(), - KMByteBlob.cast(data[AUTH_TAG]).length()); - if (verification == false) { - KMException.throwIt(KMError.IMPORTED_KEY_VERIFICATION_FAILED); - } - cryptoProvider.delete(key); - - // Step 5 - Import Decrypted Key. - data[ORIGIN] = KMType.SECURELY_IMPORTED; - data[KEY_PARAMETERS] = KMArray.cast(tmpVariables[2]).get((short) 0); - importKey(apdu, scratchPad); } private void processAttestKeyCmd(APDU apdu) {} @@ -886,1062 +579,15 @@ private void processAbortOperationCmd(APDU apdu) {} private void processFinishOperationCmd(APDU apdu) { // TODO AES GCM - receiveIncoming(apdu); - byte[] scratchPad = apdu.getBuffer(); - Util.arrayFill(scratchPad, (short)0,(short)256, (byte)0); - tmpVariables[1] = KMArray.instance((short) 6); - // Arguments - tmpVariables[2] = KMKeyParameters.exp(); - KMArray.cast(tmpVariables[1]).add((short) 0, KMInteger.exp()); - KMArray.cast(tmpVariables[1]).add((short) 1, tmpVariables[2]); - KMArray.cast(tmpVariables[1]).add((short) 2, KMByteBlob.exp()); - KMArray.cast(tmpVariables[1]).add((short) 3, KMByteBlob.exp()); - tmpVariables[3] = KMHardwareAuthToken.exp(); - KMArray.cast(tmpVariables[1]).add((short) 4, tmpVariables[3]); - tmpVariables[4] = KMVerificationToken.exp(); - KMArray.cast(tmpVariables[1]).add((short) 5, tmpVariables[4]); - // Decode the arguments - tmpVariables[2] = decoder.decode(tmpVariables[1], buffer, bufferStartOffset, bufferLength); - data[OP_HANDLE] = KMArray.cast(tmpVariables[2]).get((short) 0); - data[KEY_PARAMETERS] = KMArray.cast(tmpVariables[2]).get((short) 1); - data[INPUT_DATA] = KMArray.cast(tmpVariables[2]).get((short) 2); - data[HW_TOKEN] = KMArray.cast(tmpVariables[2]).get((short) 4); - data[VERIFICATION_TOKEN] = KMArray.cast(tmpVariables[2]).get((short) 5); - // Check Operation Handle - tmpVariables[1] = KMInteger.cast(data[OP_HANDLE]).getShort(); - KMOperationState op = repository.findOperation(tmpVariables[1]); - if (KMInteger.compare(data[OP_HANDLE], KMInteger.uint_16(op.getHandle())) != 0) { - KMException.throwIt(KMError.INVALID_OPERATION_HANDLE); - } - //Authorize the final operation - authorizeUpdateFinalOperation(op, scratchPad); - short len = 0; - // If the operation is signing - if(op.getPurpose() == KMType.SIGN){ - // Perform trusted confirmation if required - if (op.isTrustedConfirmationRequired()) { - tmpVariables[0] = KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.CONFIRMATION_TOKEN, data[KEY_PARAMETERS]); - if(tmpVariables[0] == KMType.INVALID_VALUE){ - KMException.throwIt(KMError.INVALID_ARGUMENT); - } - tmpVariables[0] = KMByteTag.cast(tmpVariables[0]).getValue(); - tmpVariables[1] = op.getTrustedConfirmationSigner() - .sign( - KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), - KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), - KMByteBlob.cast(data[INPUT_DATA]).length(), scratchPad, (short)0); - if(tmpVariables[1] != KMByteBlob.cast(tmpVariables[0]).length() ){ - KMException.throwIt(KMError.VERIFICATION_FAILED); - } - tmpVariables[0]=Util.arrayCompare(scratchPad,(short)0, - KMByteBlob.cast(tmpVariables[0]).getBuffer(), - KMByteBlob.cast(tmpVariables[0]).getStartOff(), - tmpVariables[1]); - if(tmpVariables[0] != 0){ - KMException.throwIt(KMError.VERIFICATION_FAILED); - } - } - tmpVariables[1] = op.getSigner().getCipherAlgorithm(); - tmpVariables[2] = op.getSigner().getMessageDigestAlgorithm(); - tmpVariables[3] = op.getSigner().getPaddingAlgorithm(); - len = KMByteBlob.cast(data[INPUT_DATA]).length(); - //For RSA Signing algorithm - if(tmpVariables[1] == Signature.SIG_CIPHER_RSA){ - //If no padding and no digest - then zero padding up to 256 on left - if(tmpVariables[2] == MessageDigest.ALG_NULL && tmpVariables[3] == KMCipher.PAD_NOPAD){ - // If data length is greater then key length - if(len > 256){ - KMException.throwIt(KMError.INVALID_INPUT_LENGTH); - }else if(len == 256){ // if data length is same as key length - // Compare the data with key value - date should be less then key value. - // TODO the assumption is that private key exponent value is considered here. - tmpVariables[0]= op.getKey(scratchPad,(short)0); - tmpVariables[0] = Util.arrayCompare( - KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), - KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), - scratchPad, (short)0, tmpVariables[0]); - if(tmpVariables[0] >= 0){ - KMException.throwIt(KMError.INVALID_INPUT_LENGTH); - } - } - Util.arrayCopyNonAtomic( - KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), - KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), - scratchPad, (short)(256 - len),len); - len = (short)256; - } else if (tmpVariables[2] == MessageDigest.ALG_NULL - && tmpVariables[3] == KMCipher.PAD_PKCS1) { - // If PKCS1 padding and no digest - then 0x01||0x00||PS||0x00 on left such that PS = 8 bytes - if(len > 245){ // 256 -11 bytes - KMException.throwIt(KMError.INVALID_INPUT_LENGTH); - } - scratchPad[0] = 0x00; - scratchPad[1] = 0x01; - cryptoProvider.newRandomNumber(scratchPad, (short)2, (short)8); - scratchPad[10] = 0x00; - Util.arrayCopyNonAtomic( - KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), - KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), - scratchPad, (short)11,len); - len += (short)11; - }else if (tmpVariables[2] != MessageDigest.ALG_NULL && tmpVariables[3] == KMCipher.PAD_PKCS1){ - //If PKCS1 padding and digest != ALG_NULL - just copy the data on the scratch pad - Util.arrayCopyNonAtomic( - KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), - KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), - scratchPad, (short)0,len); - } - }else if(tmpVariables[1] == Signature.SIG_CIPHER_ECDSA){ // For ECDSA algorithm - //If no digest then truncate the data to 32 byte if required - if(tmpVariables[2] == MessageDigest.ALG_NULL){ - if(len > 32){ - Util.arrayCopyNonAtomic( - KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), - KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), - scratchPad, (short)0,(short)32); - len = 32; - } - }else{ - //If digest is present then copy the data to scratchpad - Util.arrayCopyNonAtomic( - KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), - KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), - scratchPad, (short)0,len); - } - }else if(tmpVariables[1] == Signature.SIG_CIPHER_HMAC){ // For HMAC algorithm - // Just copy the data as digest is always present. - Util.arrayCopyNonAtomic( - KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), - KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), - scratchPad, (short)0,len); - }else{ // This is should never happen - KMException.throwIt(KMError.OPERATION_CANCELLED); - } - // Sign the data and also complete the trusted verification. - tmpVariables[0]= op.getSigner() - .sign( - KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), - KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), - KMByteBlob.cast(data[INPUT_DATA]).length(),scratchPad, (short)0); - data[OUTPUT_DATA] = KMByteBlob.instance(scratchPad, (short)0, tmpVariables[0]); - } else{ //If decrypt or encrypt operation - tmpVariables[1] = op.getCipher().getCipherAlgorithm(); - tmpVariables[2] = op.getCipher().getPaddingAlgorithm(); - len = KMByteBlob.cast(data[INPUT_DATA]).length(); - if(tmpVariables[1] == KMCipher.CIPHER_RSA){ // For RSA algorithm - // If no padding and no digest - then zero padding up to 256 on left - if (tmpVariables[2] == KMCipher.PAD_NOPAD) { - if(len > 256){ - KMException.throwIt(KMError.INVALID_INPUT_LENGTH); - } - if(len < 256){ - Util.arrayCopyNonAtomic( - KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), - KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), - scratchPad, (short)(256 - len),len); - len = (short)256; - } - } else { - // If OAEP padding with digest - just copy the data to scratchpad and continue. - Util.arrayCopyNonAtomic( - KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), - KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), - scratchPad, (short)0,len); - } - }else if(tmpVariables[1] == KMCipher.CIPHER_DES_CBC || tmpVariables[1] == KMCipher.CIPHER_DES_ECB - || tmpVariables[1] == KMCipher.CIPHER_AES_CBC || - tmpVariables[1] == KMCipher.CIPHER_AES_ECB){ - if(tmpVariables[1] == KMCipher.CIPHER_AES_CBC || - tmpVariables[1] == KMCipher.CIPHER_AES_ECB){ // For AES algorithm - tmpVariables[5] = AES_BLOCK_SIZE; - }else{ - tmpVariables[5] = DES_BLOCK_SIZE; - } - //If no padding then data length must be block aligned - if (tmpVariables[2] == KMCipher.PAD_NOPAD && ((short)(len % tmpVariables[5]) != 0)){ - KMException.throwIt(KMError.INVALID_INPUT_LENGTH); - } - //If padding i.e. pkcs7 then add padding to right - if(tmpVariables[2] != KMCipher.PAD_NOPAD){ - tmpVariables[3] = (short)(len % tmpVariables[5]); - if(tmpVariables[3] != 0){ - // If not block aligned then pkcs7 padding on right - tmpVariables[4] = (short)((len / tmpVariables[5])+tmpVariables[5]); - Util.arrayFillNonAtomic(scratchPad, (short)0, tmpVariables[4], (byte)tmpVariables[3]); - }else{ - // If block aligned then one complete block of pkcs7 padding of block length value - // on the right. - tmpVariables[4] = (short)(len + tmpVariables[5]); - Util.arrayFillNonAtomic(scratchPad, (short)0, tmpVariables[4], (byte)tmpVariables[5]); - } - Util.arrayCopyNonAtomic( KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), - KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), - scratchPad, (short)0,len); - len = tmpVariables[4]; - } - // AES / DES Cipher - tmpVariables[0]= op.getCipher() - .doFinal(scratchPad, (short)0,len, scratchPad, (short)len); - data[OUTPUT_DATA] = KMByteBlob.instance(scratchPad, (short)len, tmpVariables[0]); - } else{ // This should never happen - KMException.throwIt(KMError.OPERATION_CANCELLED); - } - } - // Remove the operation handle - repository.releaseOperation(op); - // Make response - // make response - tmpVariables[1] = KMArray.instance((short) 0); - tmpVariables[1] = KMKeyParameters.instance(tmpVariables[1]); - tmpVariables[2] = KMArray.instance((short) 4); - if (data[OUTPUT_DATA] == KMType.INVALID_VALUE) { - data[OUTPUT_DATA] = KMByteBlob.instance((short) 0); - } - KMArray.cast(tmpVariables[2]).add((short) 0, KMInteger.uint_16(KMError.OK)); - KMArray.cast(tmpVariables[2]).add((short) 1, tmpVariables[1]); - KMArray.cast(tmpVariables[2]).add((short) 2, data[OUTPUT_DATA]); - // Encode the response - bufferLength = encoder.encode(tmpVariables[0], buffer, bufferStartOffset); - sendOutgoing(apdu); - } - private void authorizeUpdateFinalOperation(KMOperationState op, byte[] scratchPad) { - // User Authentication - if (!op.isAuthPerOperation()) { - if (!op.isAuthTimeoutValidated()) { - validateVerificationToken(op, data[VERIFICATION_TOKEN], scratchPad); - tmpVariables[0] = KMInteger.uint_64(op.getAuthTime(), (short) 0); - tmpVariables[2] = KMVerificationToken.cast(data[VERIFICATION_TOKEN]).getTimestamp(); - if (tmpVariables[3] == KMType.INVALID_VALUE) { - KMException.throwIt(KMError.VERIFICATION_FAILED); - } - if (KMInteger.compare(tmpVariables[0], tmpVariables[3]) >= 0) { - KMException.throwIt(KMError.KEY_USER_NOT_AUTHENTICATED); - } - op.setAuthTimeoutValidated(true); - } - } else { // Auth per operation - authorizeUserIdPerKeyOperation(data[HW_TOKEN], scratchPad); - } } - private void validateVerificationToken(KMOperationState op, short verToken, byte[] scratchPad) { - // CBOR Encoding is always big endian and Java is big endian - short ptr = KMVerificationToken.cast(verToken).getMac(); - short len = 0; - // If mac length is zero then token is empty. - if (KMByteBlob.cast(ptr).length() == 0) { - return; - } - // validate operation handle. - ptr = KMVerificationToken.cast(verToken).getChallenge(); - if(op.getHandle() != KMInteger.cast(ptr).getShort()){ - KMException.throwIt(KMError.VERIFICATION_FAILED); - } - // concatenation length will be 37 + length of verified parameters list. - short params = KMVerificationToken.cast(verToken).getParametersVerified(); - Util.arrayFillNonAtomic(scratchPad, (short) 0, - (short) (37+KMByteBlob.cast(params).length()), (byte) 0); - // Add "Auth Verification" - 17 bytes. - Util.arrayCopy(authVerification,(short)0, scratchPad, (short)0, (short)authVerification.length); - len = (short)authVerification.length; - // concatenate challenge - 8 bytes - ptr = KMVerificationToken.cast(verToken).getChallenge(); - KMInteger.cast(ptr) - .value(scratchPad, (short) (len + (short) (8 - KMInteger.cast(ptr).length()))); - len += 8; - // concatenate timestamp -8 bytes - ptr = KMVerificationToken.cast(verToken).getTimestamp(); - KMInteger.cast(ptr) - .value(scratchPad, (short) (len + (short) (8 - KMInteger.cast(ptr).length()))); - len += 8; - // concatenate security level - 4 bytes - ptr = KMVerificationToken.cast(verToken).getSecurityLevel(); - scratchPad[(short) (len + 3)] = KMEnum.cast(ptr).getVal(); - len += 4; - // concatenate Parameters verified - blob of encoded data. - ptr = KMVerificationToken.cast(verToken).getParametersVerified(); - len += KMByteBlob.cast(ptr).getValues(scratchPad, (short)0); - len += 4; - // hmac the data - HMACKey key = - cryptoProvider.createHMACKey( - repository.getComputedHmacKey(), - (short) 0, - (short) repository.getComputedHmacKey().length); - ptr = KMVerificationToken.cast(verToken).getMac(); - boolean verified = - cryptoProvider.hmacVerify(key, scratchPad, (short) 0, len, - KMByteBlob.cast(ptr).getBuffer(), - KMByteBlob.cast(ptr).getStartOff(), - KMByteBlob.cast(ptr).length()); - if(!verified){ - KMException.throwIt(KMError.VERIFICATION_FAILED); - } - /* - - // Compare mac. - ptr = KMVerificationToken.cast(verToken).getMac(); - if (macLen != KMByteBlob.cast(ptr).length()) { - KMException.throwIt(KMError.INVALID_MAC_LENGTH); - } - if (Util.arrayCompare( - scratchPad, (short) (len+1), - KMByteBlob.cast(ptr).getBuffer(), KMByteBlob.cast(ptr).getStartOff(), macLen) != 0) { - KMException.throwIt(KMError.VERIFICATION_FAILED); - } - */ - - } private void processUpdateOperationCmd(APDU apdu) { - // TODO Add Support for AES-GCM - receiveIncoming(apdu); - byte[] scratchPad = apdu.getBuffer(); - tmpVariables[1] = KMArray.instance((short) 5); - // Arguments - tmpVariables[2] = KMKeyParameters.exp(); - KMArray.cast(tmpVariables[1]).add((short) 0, KMInteger.exp()); - KMArray.cast(tmpVariables[1]).add((short) 1, tmpVariables[2]); - KMArray.cast(tmpVariables[1]).add((short) 2, KMByteBlob.exp()); - tmpVariables[3] = KMHardwareAuthToken.exp(); - KMArray.cast(tmpVariables[1]).add((short) 3, tmpVariables[3]); - tmpVariables[4] = KMVerificationToken.exp(); - KMArray.cast(tmpVariables[1]).add((short) 4, tmpVariables[4]); - // Decode the arguments - tmpVariables[2] = decoder.decode(tmpVariables[1], buffer, bufferStartOffset, bufferLength); - data[OP_HANDLE] = KMArray.cast(tmpVariables[2]).get((short) 0); - data[KEY_PARAMETERS] = KMArray.cast(tmpVariables[2]).get((short) 1); - data[INPUT_DATA] = KMArray.cast(tmpVariables[2]).get((short) 2); - data[HW_TOKEN] = KMArray.cast(tmpVariables[2]).get((short) 3); - data[VERIFICATION_TOKEN] = KMArray.cast(tmpVariables[2]).get((short) 4); - // Check Operation Handle and get op state - tmpVariables[1] = KMInteger.cast(data[OP_HANDLE]).getShort(); - KMOperationState op = repository.findOperation(tmpVariables[1]); - if (KMInteger.compare(data[OP_HANDLE], KMInteger.uint_16(op.getHandle())) != 0) { - KMException.throwIt(KMError.INVALID_OPERATION_HANDLE); - } - // authorize the update operation - authorizeUpdateFinalOperation(op, scratchPad); - // If signing without digest then do length validation checks - tmpVariables[0] = KMByteBlob.cast(data[INPUT_DATA]).length(); - if (op.getPurpose() == KMType.SIGN) { - // If signing without digest then update should not be called by HAL only final must be - // called - if (op.getSigner().getMessageDigestAlgorithm() == MessageDigest.ALG_NULL) { - KMException.throwIt(KMError.OPERATION_CANCELLED); - } - op.getSigner() - .update( - KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), - KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), - KMByteBlob.cast(data[INPUT_DATA]).length()); - - if (op.isTrustedConfirmationRequired()) { - op.getTrustedConfirmationSigner() - .update( - KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), - KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), - KMByteBlob.cast(data[INPUT_DATA]).length()); - } - data[OUTPUT_DATA] = KMType.INVALID_VALUE; - } else { - // purpose is Encrypt or Decrypt - input data must be block aligned. - tmpVariables[1] = op.getCipher().getCipherAlgorithm(); - // TODO Update for decrypt for RSA may not be necessary - confirm this - if (tmpVariables[1] == KMCipher.CIPHER_RSA) { - KMException.throwIt(KMError.OPERATION_CANCELLED); - } - if (tmpVariables[1] == KMCipher.CIPHER_AES_CBC - || op.getCipher().getCipherAlgorithm() == KMCipher.CIPHER_AES_ECB) { - // 128 bit block size - HAL must send block aligned data - if (tmpVariables[0] % 16 != 0) { - KMException.throwIt(KMError.INVALID_INPUT_LENGTH); - } - } else if (op.getCipher().getCipherAlgorithm() == KMCipher.CIPHER_DES_CBC - || op.getCipher().getCipherAlgorithm() == KMCipher.CIPHER_DES_ECB) { - // 64 bit block size - HAL must send block aligned data - if (tmpVariables[0] % 8 != 0) { - KMException.throwIt(KMError.INVALID_INPUT_LENGTH); - } - } - tmpVariables[1] = - op.getCipher() - .update( - KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), - KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), - KMByteBlob.cast(data[INPUT_DATA]).length(), - scratchPad, - (short) 0); - data[OUTPUT_DATA] = KMByteBlob.instance(scratchPad, (short) 0, tmpVariables[1]); - } - // make response - tmpVariables[1] = KMArray.instance((short) 0); - tmpVariables[1] = KMKeyParameters.instance(tmpVariables[1]); - tmpVariables[2] = KMArray.instance((short) 4); - if (data[OUTPUT_DATA] == KMType.INVALID_VALUE) { - data[OUTPUT_DATA] = KMByteBlob.instance((short) 0); - } - KMArray.cast(tmpVariables[2]).add((short) 0, KMInteger.uint_16(KMError.OK)); - KMArray.cast(tmpVariables[2]).add((short) 1, KMInteger.uint_16(tmpVariables[0])); - KMArray.cast(tmpVariables[2]).add((short) 2, tmpVariables[1]); - KMArray.cast(tmpVariables[2]).add((short) 3, data[OUTPUT_DATA]); - // Encode the response - bufferLength = encoder.encode(tmpVariables[0], buffer, bufferStartOffset); - sendOutgoing(apdu); - } - - private void processBeginOperationCmd(APDU apdu) { - // Receive the incoming request fully from the master into buffer. - receiveIncoming(apdu); - byte[] scratchPad = apdu.getBuffer(); - tmpVariables[1] = KMArray.instance((short) 4); - // Arguments - tmpVariables[2] = KMKeyParameters.exp(); - KMArray.cast(tmpVariables[1]).add((short) 0, KMEnum.instance(KMType.PURPOSE)); - KMArray.cast(tmpVariables[1]).add((short) 1, KMByteBlob.exp()); - KMArray.cast(tmpVariables[1]).add((short) 2, tmpVariables[2]); - tmpVariables[3] = KMHardwareAuthToken.exp(); - KMArray.cast(tmpVariables[1]).add((short) 3, tmpVariables[3]); - // Decode the arguments - tmpVariables[2] = decoder.decode(tmpVariables[1], buffer, bufferStartOffset, bufferLength); - data[KEY_PARAMETERS] = KMArray.cast(tmpVariables[2]).get((short) 2); - data[KEY_BLOB] = KMArray.cast(tmpVariables[2]).get((short) 1); - tmpVariables[0] = KMArray.cast(tmpVariables[2]).get((short) 0); - tmpVariables[0] = KMEnum.cast(tmpVariables[0]).getVal(); - tmpVariables[4] = KMArray.cast(tmpVariables[2]).get((short) 3); - // Check for app id and app data. - data[APP_ID] = KMType.INVALID_VALUE; - data[APP_DATA] = KMType.INVALID_VALUE; - tmpVariables[3] = - KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.APPLICATION_ID, data[KEY_PARAMETERS]); - if (tmpVariables[3] != KMTag.INVALID_VALUE) { - data[APP_ID] = KMByteTag.cast(tmpVariables[3]).getValue(); - } - tmpVariables[3] = - KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.APPLICATION_DATA, data[KEY_PARAMETERS]); - if (tmpVariables[3] != KMTag.INVALID_VALUE) { - data[APP_DATA] = KMByteTag.cast(tmpVariables[3]).getValue(); - } - // Parse the encrypted blob and decrypt it. - parseEncryptedKeyBlob(scratchPad); - // Authorize the begin operation and reserve op - data[OP_HANDLE] will have the handle. - // It will also set data[IV] field if required. - authorizeBeginOperation(tmpVariables[4], scratchPad); - // Check for trusted confirmation - if required then set the signer in op state. - tmpVariables[0] = - KMKeyParameters.findTag( - KMType.BOOL_TAG, KMType.TRUSTED_CONFIRMATION_REQUIRED, data[HW_PARAMETERS]); - if (tmpVariables[0] != KMType.INVALID_VALUE) { - // get operation - KMOperationState op = repository.findOperation(data[OP_HANDLE]); - // get the hmac key - if (repository.getComputedHmacKey() == null) { - KMException.throwIt(KMError.OPERATION_CANCELLED); - } - // set the Hmac signer - op.setTrustedConfirmationSigner( - cryptoProvider.createHmacSigner( - MessageDigest.ALG_SHA_256, - repository.getComputedHmacKey(), - (short) 0, - (short) repository.getComputedHmacKey().length)); - } - // If the data[IV] is required to be returned. - if (data[IV] != KMType.INVALID_VALUE) { - // TODO confirm why this is needed - tmpVariables[2] = KMArray.instance((short) 1); - KMArray.cast(tmpVariables[2]).add((short) 0, data[IV]); - } else { - tmpVariables[2] = KMArray.instance((short) 0); - } - tmpVariables[1] = KMKeyParameters.instance(tmpVariables[2]); - tmpVariables[0] = KMArray.instance((short) 3); - KMArray.cast(tmpVariables[0]).add((short) 0, KMInteger.uint_16(KMError.OK)); - KMArray.cast(tmpVariables[0]).add((short) 1, tmpVariables[1]); - KMArray.cast(tmpVariables[0]).add((short) 2, data[OP_HANDLE]); - // Encode the response - bufferLength = encoder.encode(tmpVariables[0], buffer, bufferStartOffset); - sendOutgoing(apdu); - } - - private void authorizeBeginOperation(short hwToken, byte[] scratchPad) { - // Read purpose from key parameters - cannot be null. - short purpose = - KMEnumArrayTag.getValues(KMType.PURPOSE, data[KEY_PARAMETERS], scratchPad, (short) 0); - if (purpose == KMType.INVALID_VALUE) { - KMException.throwIt(KMError.INVALID_ARGUMENT); - } - if (purpose != 1) { - KMException.throwIt(KMError.INVALID_ARGUMENT); - } - purpose = scratchPad[0]; - if (!(KMEnumArrayTag.contains(KMType.PURPOSE, purpose, data[HW_PARAMETERS]))) { - KMException.throwIt(KMError.UNSUPPORTED_PURPOSE); - } - // Read digest from key parameters - can be null for EC. - short digest = - KMEnumArrayTag.getValues(KMType.DIGEST, data[KEY_PARAMETERS], scratchPad, (short) 0); - if (digest != KMType.INVALID_VALUE && digest != 1) { - KMException.throwIt(KMError.INVALID_ARGUMENT); - } - digest = scratchPad[0]; - // Read padding from key parameters - can be null for AES/DES. - short padding = - KMEnumArrayTag.getValues(KMType.PADDING, data[KEY_PARAMETERS], scratchPad, (short) 0); - if (padding != KMType.INVALID_VALUE && padding != 1) { - KMException.throwIt(KMError.INVALID_ARGUMENT); - } - padding = scratchPad[0]; - // Read Blockmode - short blockmode = - KMEnumArrayTag.getValues(KMType.BLOCK_MODE, data[KEY_PARAMETERS], scratchPad, (short) 0); - if (blockmode != KMType.INVALID_VALUE && blockmode != 1) { - KMException.throwIt(KMError.INVALID_ARGUMENT); - } - blockmode = scratchPad[0]; - - // Max uses per boot - tmpVariables[0] = - KMKeyParameters.findTag(KMType.UINT_TAG, KMType.MAX_USES_PER_BOOT, data[HW_PARAMETERS]); - if (tmpVariables[0] != KMType.INVALID_VALUE) { - // get prescribed limit - tmpVariables[0] = KMIntegerTag.cast(tmpVariables[0]).getValue(); - authorizeKeyUsageForCount(tmpVariables[0]); - } - // Authorize UserId - auth timeout check cannot be done in javacard - tmpVariables[0] = - KMKeyParameters.findTag(KMType.ULONG_ARRAY_TAG, KMType.USER_SECURE_ID, data[HW_PARAMETERS]); - if (tmpVariables[0] != KMType.INVALID_VALUE) { - tmpVariables[0] = - KMKeyParameters.findTag(KMType.UINT_TAG, KMType.AUTH_TIMEOUT, data[HW_PARAMETERS]); - if (tmpVariables[0] != KMType.INVALID_VALUE) { - // check if hw token is empty - mac should not be empty. - tmpVariables[1] = KMHardwareAuthToken.cast(hwToken).getMac(); - if (KMByteBlob.cast(tmpVariables[1]).length() == 0) { - KMException.throwIt(KMError.INVALID_MAC_LENGTH); - } - authorizeUserId(hwToken, scratchPad); - } - } - // Authorize Caller Nonce - if caller nonce absent in key char and nonce present in - // key params then fail. - tmpVariables[2] = KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.NONCE, data[KEY_PARAMETERS]); - tmpVariables[0] = - KMKeyParameters.findTag(KMType.BOOL_TAG, KMType.CALLER_NONCE, data[HW_PARAMETERS]); - if (tmpVariables[0] == KMType.INVALID_VALUE) { - if (tmpVariables[2] != KMType.INVALID_VALUE) { - KMException.throwIt(KMError.CALLER_NONCE_PROHIBITED); - } - } - // Authorize Bootloader Only - assumption is that if this is is present then always fail. - tmpVariables[0] = - KMKeyParameters.findTag(KMType.BOOL_TAG, KMType.BOOTLOADER_ONLY, data[HW_PARAMETERS]); - if (tmpVariables[1] != KMType.INVALID_VALUE) { - KMException.throwIt(KMError.INVALID_KEY_BLOB); - } - tmpVariables[0] = KMEnumTag.getValue(KMType.ALGORITHM, data[HW_PARAMETERS]); - switch (tmpVariables[0]) { - case KMType.RSA: - authorizeRsa(purpose, digest, padding); - break; - case KMType.EC: - authorizeEC(purpose, digest); - break; - case KMType.DES: - case KMType.AES: - if (tmpVariables[2] == KMType.INVALID_VALUE) { - tmpVariables[2] = KMByteBlob.instance((short) 16); - cryptoProvider.newRandomNumber( - KMByteBlob.cast(tmpVariables[2]).getBuffer(), - KMByteBlob.cast(tmpVariables[2]).getStartOff(), - KMByteBlob.cast(tmpVariables[2]).length()); - } - data[IV] = tmpVariables[2]; - authorizeAesDes(tmpVariables[0], purpose, blockmode, padding); - break; - case KMType.HMAC: - authorizeHmac(purpose, digest); - break; - default: - KMException.throwIt(KMError.UNIMPLEMENTED); - break; - } - } - - private void authorizeGCM(short purpose, short padding) { - data[OP_HANDLE] = KMType.INVALID_VALUE; - if (purpose == KMType.SIGN || purpose == KMType.VERIFY) { - KMException.throwIt(KMError.UNSUPPORTED_PURPOSE); - } - if (purpose == KMType.ENCRYPT) { - purpose = KMCipher.MODE_ENCRYPT; - } else { - purpose = KMCipher.MODE_DECRYPT; - } - if (padding != KMType.PADDING_NONE) { - KMException.throwIt(KMError.INCOMPATIBLE_PADDING_MODE); - } - // Read and authorizeBeginOperation mac length - tmpVariables[0] = - KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.MAC_LENGTH, data[KEY_PARAMETERS]); - if (tmpVariables[0] == KMType.INVALID_VALUE) { - KMException.throwIt(KMError.MISSING_MAC_LENGTH); - } - if (tmpVariables[0] % 8 != 0) { - KMException.throwIt(KMError.INVALID_MAC_LENGTH); - } - tmpVariables[1] = - KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, data[HW_PARAMETERS]); - if (tmpVariables[0] < tmpVariables[1]) { - KMException.throwIt(KMError.INVALID_MAC_LENGTH); - } - if (tmpVariables[0] > 128) { - KMException.throwIt(KMError.INVALID_MAC_LENGTH); - } - KMOperationState op = repository.reserveOperation(); - if (op == null) { - KMException.throwIt(KMError.TOO_MANY_OPERATIONS); - } - op.setPurpose(purpose); - op.setKey(KMByteBlob.cast(data[SECRET]).getBuffer(), - KMByteBlob.cast(data[SECRET]).getStartOff(), - KMByteBlob.cast(data[SECRET]).length()); - op.setCipher( - cryptoProvider.createGCMCipher( - purpose, - KMByteBlob.cast(data[SECRET]).getBuffer(), - KMByteBlob.cast(data[SECRET]).getStartOff(), - KMByteBlob.cast(data[SECRET]).length(), - KMByteBlob.cast(data[IV]).getBuffer(), - KMByteBlob.cast(data[IV]).getStartOff(), - KMByteBlob.cast(data[IV]).length())); - data[OP_HANDLE] = op.getHandle(); - } - - private void authorizeHmac(short purpose, short digest) { - data[OP_HANDLE] = KMType.INVALID_VALUE; - if (purpose == KMType.ENCRYPT || purpose == KMType.DECRYPT) { - KMException.throwIt(KMError.UNSUPPORTED_PURPOSE); - } - if (digest == KMType.INVALID_VALUE) { - KMException.throwIt(KMError.INVALID_ARGUMENT); - } - if (!(KMEnumArrayTag.contains(KMType.DIGEST, digest, data[HW_PARAMETERS]))) { - KMException.throwIt(KMError.UNSUPPORTED_DIGEST); - } - // Read and authorizeBeginOperation mac length - tmpVariables[0] = - KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.MAC_LENGTH, data[KEY_PARAMETERS]); - if (tmpVariables[0] == KMType.INVALID_VALUE) { - KMException.throwIt(KMError.MISSING_MAC_LENGTH); - } - if (tmpVariables[0] % 8 != 0) { - KMException.throwIt(KMError.INVALID_MAC_LENGTH); - } - tmpVariables[1] = - KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, data[HW_PARAMETERS]); - if (tmpVariables[0] < tmpVariables[1]) { - KMException.throwIt(KMError.INVALID_MAC_LENGTH); - } - - switch (digest) { - case KMType.MD5: - tmpVariables[2] = MessageDigest.ALG_MD5; - tmpVariables[1] = 128; - break; - case KMType.SHA1: - tmpVariables[2] = MessageDigest.ALG_SHA; - tmpVariables[1] = 160; - break; - case KMType.SHA2_224: - tmpVariables[2] = MessageDigest.ALG_SHA_224; - tmpVariables[1] = 224; - break; - case KMType.SHA2_256: - tmpVariables[2] = MessageDigest.ALG_SHA_256; - tmpVariables[1] = 256; - break; - case KMType.SHA2_384: - tmpVariables[2] = MessageDigest.ALG_SHA_384; - tmpVariables[1] = 384; - break; - case KMType.SHA2_512: - tmpVariables[2] = MessageDigest.ALG_SHA_512; - tmpVariables[1] = 512; - break; - default: - tmpVariables[1] = 512; - break; - } - if (tmpVariables[0] > tmpVariables[1]) { - KMException.throwIt(KMError.INVALID_MAC_LENGTH); - } - KMOperationState op = repository.reserveOperation(); - if (op == null) { - KMException.throwIt(KMError.TOO_MANY_OPERATIONS); - } - op.setPurpose(purpose); - op.setKey(KMByteBlob.cast(data[SECRET]).getBuffer(), - KMByteBlob.cast(data[SECRET]).getStartOff(), - KMByteBlob.cast(data[SECRET]).length()); - op.setSigner( - cryptoProvider.createHmacSigner( - tmpVariables[0], - KMByteBlob.cast(data[SECRET]).getBuffer(), - KMByteBlob.cast(data[SECRET]).getStartOff(), - KMByteBlob.cast(data[SECRET]).length())); - data[OP_HANDLE] = op.getHandle(); - } - - private void authorizeAesDes(short alg, short purpose, short blockmode, short padding) { - data[OP_HANDLE] = KMType.INVALID_VALUE; - if (purpose == KMType.SIGN || purpose == KMType.VERIFY) { - KMException.throwIt(KMError.UNSUPPORTED_PURPOSE); - } - if (blockmode == KMType.GCM) { - authorizeGCM(purpose, padding); - } - if (purpose == KMType.ENCRYPT) { - purpose = KMCipher.MODE_ENCRYPT; - } else { - purpose = KMCipher.MODE_DECRYPT; - } - KMOperationState op = null; - // padding must be no pad - PKCS7 is not supported in javacard - // TODO implement PCKS7 in cryptoProvider - if (padding == KMType.PADDING_NONE) { - padding = KMCipher.PAD_NULL; - } else if (padding == KMType.PKCS7) { - padding = KMCipher.PAD_PKCS7; - } else { - KMException.throwIt(KMError.INCOMPATIBLE_PADDING_MODE); - } - if (alg == KMType.AES) { - if (blockmode == KMType.CBC) { - tmpVariables[0] = KMCipher.CIPHER_AES_CBC; - } else if (blockmode == KMType.ECB) { - tmpVariables[0] = KMCipher.CIPHER_AES_ECB; - } else { - // data[CIPHER_ALGORITHM] = Cipher.CIPHER_AES_CTR; // Not supported in 3.0.5 - // TODO change this once we can test. - KMException.throwIt(KMError.UNSUPPORTED_BLOCK_MODE); - } - op = repository.reserveOperation(); - } else if (alg == KMType.DES) { - if (blockmode == KMType.CBC) { - tmpVariables[0] = KMCipher.CIPHER_DES_CBC; - } else if (blockmode == KMType.ECB) { - tmpVariables[0] = KMCipher.CIPHER_DES_ECB; - } else { - // data[CIPHER_ALGORITHM] = Cipher.CIPHER_DES_CTR; // Not supported in 3.0.5 - // TODO change this once we can test. - KMException.throwIt(KMError.UNSUPPORTED_BLOCK_MODE); - } - op = repository.reserveOperation(); - } else { - KMException.throwIt(KMError.INCOMPATIBLE_ALGORITHM); - } - if (op == null) { - KMException.throwIt(KMError.TOO_MANY_OPERATIONS); - } - op.setPurpose(purpose); - op.setKey(KMByteBlob.cast(data[SECRET]).getBuffer(), - KMByteBlob.cast(data[SECRET]).getStartOff(), - KMByteBlob.cast(data[SECRET]).length()); - op.setCipher( - cryptoProvider.createSymmetricCipher( - tmpVariables[0], - padding, - purpose, - KMByteBlob.cast(data[SECRET]).getBuffer(), - KMByteBlob.cast(data[SECRET]).getStartOff(), - KMByteBlob.cast(data[SECRET]).length(), - KMByteBlob.cast(data[IV]).getBuffer(), - KMByteBlob.cast(data[IV]).getStartOff(), - KMByteBlob.cast(data[IV]).length())); - data[OP_HANDLE] = op.getHandle(); - } - - private void authorizeEC(short purpose, short digest) { - data[OP_HANDLE] = KMType.INVALID_VALUE; - // purpose will be always sign. - // Only ECDSA signing supported - if (purpose == KMType.ENCRYPT || purpose == KMType.VERIFY || purpose == KMType.DECRYPT) { - KMException.throwIt(KMError.UNSUPPORTED_PURPOSE); - } - switch (digest) { - case KMType.DIGEST_NONE: - tmpVariables[0] = MessageDigest.ALG_NULL; - break; - case KMType.SHA1: - tmpVariables[0] = MessageDigest.ALG_SHA; - break; - case KMType.SHA2_224: - tmpVariables[0] = MessageDigest.ALG_SHA_224; - break; - case KMType.SHA2_256: - tmpVariables[0] = MessageDigest.ALG_SHA_256; - break; - case KMType.SHA2_384: - tmpVariables[0] = MessageDigest.ALG_SHA_384; - break; - case KMType.SHA2_512: - tmpVariables[0] = MessageDigest.ALG_SHA_512; - break; - default: - KMException.throwIt(KMError.INCOMPATIBLE_DIGEST); - break; - } - KMOperationState op = repository.reserveOperation(); - if (op == null) { - KMException.throwIt(KMError.TOO_MANY_OPERATIONS); - } - op.setPurpose(purpose); - op.setKey(KMByteBlob.cast(data[SECRET]).getBuffer(), - KMByteBlob.cast(data[SECRET]).getStartOff(), - KMByteBlob.cast(data[SECRET]).length()); - op.setSigner( - cryptoProvider.createEcSigner( - tmpVariables[0], - KMByteBlob.cast(data[SECRET]).getBuffer(), - KMByteBlob.cast(data[SECRET]).getStartOff(), - KMByteBlob.cast(data[SECRET]).length())); - data[OP_HANDLE] = op.getHandle(); } - private void authorizeRsa(short purpose, short digest, short padding) { - KMOperationState op = null; - data[OP_HANDLE] = KMType.INVALID_VALUE; - if (purpose == KMType.ENCRYPT || purpose == KMType.VERIFY) { - KMException.throwIt(KMError.UNSUPPORTED_PURPOSE); - } - switch (purpose) { - case KMType.DECRYPT: - tmpVariables[0] = KMCipher.CIPHER_RSA; - if (padding == KMType.PADDING_NONE) { - // There is no way to select digest with no padding. Digest is also none. - padding = KMCipher.PAD_NOPAD; - } else if (padding != KMType.RSA_OAEP) { - KMException.throwIt(KMError.INCOMPATIBLE_PADDING_MODE); - } else { - // There is no way to ascertain MGF1 and SHA1 in javacard - this should be part of PKCS1. - switch (digest) { - case KMType.DIGEST_NONE: - KMException.throwIt(KMError.UNSUPPORTED_DIGEST); - break; - case KMType.SHA2_224: - padding = KMCipher.PAD_PKCS1_OAEP_SHA224; - break; - case KMType.SHA2_256: - padding = KMCipher.PAD_PKCS1_OAEP_SHA256; - break; - case KMType.SHA2_384: - padding = KMCipher.PAD_PKCS1_OAEP_SHA384; - break; - case KMType.SHA2_512: - padding = KMCipher.PAD_PKCS1_OAEP_SHA512; - break; - default: - KMException.throwIt(KMError.INCOMPATIBLE_DIGEST); - break; - } - } - op = repository.reserveOperation(); - if (op == null) { - KMException.throwIt(KMError.TOO_MANY_OPERATIONS); - } - op.setPurpose(purpose); - op.setKey(KMByteBlob.cast(data[SECRET]).getBuffer(), - KMByteBlob.cast(data[SECRET]).getStartOff(), - KMByteBlob.cast(data[SECRET]).length()); - op.setCipher( - cryptoProvider.createRsaDecrypt( - tmpVariables[0], - padding, - KMByteBlob.cast(data[SECRET]).getBuffer(), - KMByteBlob.cast(data[SECRET]).getStartOff(), - KMByteBlob.cast(data[SECRET]).length(), - KMByteBlob.cast(data[PUB_KEY]).getBuffer(), - KMByteBlob.cast(data[PUB_KEY]).getStartOff(), - KMByteBlob.cast(data[PUB_KEY]).length())); - break; - case KMType.SIGN: - if (padding == KMType.PADDING_NONE) { - if(digest == KMType.DIGEST_NONE){ - tmpVariables[0] = MessageDigest.ALG_NULL; - padding = KMCipher.PAD_NOPAD; - }else{ - KMException.throwIt(KMError.INCOMPATIBLE_DIGEST); - } - } else if (padding != KMType.RSA_PKCS1_1_5_SIGN) { - KMException.throwIt(KMError.INCOMPATIBLE_PADDING_MODE); - } else { - padding = KMCipher.PAD_PKCS1; - switch (digest) { // TODO No digest not supported at this moment - case KMType.DIGEST_NONE: - tmpVariables[0] = MessageDigest.ALG_NULL; - break; - case KMType.SHA2_224: - tmpVariables[0] = MessageDigest.ALG_SHA_224; - break; - case KMType.SHA2_256: - tmpVariables[0] = MessageDigest.ALG_SHA_256; - break; - case KMType.SHA2_384: - tmpVariables[0] = MessageDigest.ALG_SHA_384; - break; - case KMType.SHA2_512: - tmpVariables[0] = MessageDigest.ALG_SHA_512; - break; - default: - KMException.throwIt(KMError.INCOMPATIBLE_DIGEST); - break; - } - } - op = repository.reserveOperation(); - if (op == null) { - KMException.throwIt(KMError.TOO_MANY_OPERATIONS); - } - op.setPurpose(purpose); - op.setKey(KMByteBlob.cast(data[SECRET]).getBuffer(), - KMByteBlob.cast(data[SECRET]).getStartOff(), - KMByteBlob.cast(data[SECRET]).length()); - op.setSigner( - cryptoProvider.createRsaSigner( - tmpVariables[0], - padding, - KMByteBlob.cast(data[SECRET]).getBuffer(), - KMByteBlob.cast(data[SECRET]).getStartOff(), - KMByteBlob.cast(data[SECRET]).length(), - KMByteBlob.cast(data[PUB_KEY]).getBuffer(), - KMByteBlob.cast(data[PUB_KEY]).getStartOff(), - KMByteBlob.cast(data[PUB_KEY]).length())); - break; - default: - KMException.throwIt(KMError.UNSUPPORTED_PURPOSE); - break; - } - data[OP_HANDLE] = op.getHandle(); - } + private void processBeginOperationCmd(APDU apdu) {} - private void authorizeUserId(short hwToken, byte[] scratchPad) { - validateHwToken(hwToken, scratchPad); - tmpVariables[0] = KMHardwareAuthToken.cast(hwToken).getUserId(); - if (KMInteger.cast(tmpVariables[0]).isZero()) { - tmpVariables[0] = KMHardwareAuthToken.cast(hwToken).getAuthenticatorId(); - if (KMInteger.cast(tmpVariables[0]).isZero()) { - KMException.throwIt(KMError.KEY_USER_NOT_AUTHENTICATED); - } - } - // check user secure id - if (!KMIntegerArrayTag.contains(KMType.USER_SECURE_ID, tmpVariables[0], data[HW_PARAMETERS])) { - KMException.throwIt(KMError.KEY_USER_NOT_AUTHENTICATED); - } - // check auth type - tmpVariables[1] = KMEnumTag.getValue(KMType.USER_AUTH_TYPE, data[HW_PARAMETERS]); - tmpVariables[2] = KMHardwareAuthToken.cast(hwToken).getHwAuthenticatorType(); - tmpVariables[2] = KMEnum.cast(tmpVariables[2]).getVal(); - if (((byte) tmpVariables[2] & (byte) tmpVariables[1]) == 0) { - KMException.throwIt(KMError.KEY_USER_NOT_AUTHENTICATED); - } - } - - private void validateHwToken(short hwToken, byte[] scratchPad) { - // CBOR Encoding is always big endian - short ptr = KMHardwareAuthToken.cast(hwToken).getMac(); - short len = 0; - // If mac length is zero then token is empty. - if (KMByteBlob.cast(ptr).length() == 0) { - return; - } - // add 0 - Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 37, (byte) 0); - len = 1; - // concatenate challenge - 8 bytes - ptr = KMHardwareAuthToken.cast(hwToken).getChallenge(); - KMInteger.cast(ptr) - .value(scratchPad, (short) (len + (short) (8 - KMInteger.cast(ptr).length()))); - len += 8; - // concatenate user id - 8 bytes - ptr = KMHardwareAuthToken.cast(hwToken).getUserId(); - KMInteger.cast(tmpVariables[0]) - .value(scratchPad, (short) (len + (short) (8 - KMInteger.cast(ptr).length()))); - len += 8; - // concatenate authenticator id - 8 bytes - ptr = KMHardwareAuthToken.cast(hwToken).getAuthenticatorId(); - KMInteger.cast(tmpVariables[0]) - .value(scratchPad, (short) (len + (short) (8 - KMInteger.cast(ptr).length()))); - len += 8; - // concatenate authenticator type - 4 bytes - ptr = KMHardwareAuthToken.cast(hwToken).getHwAuthenticatorType(); - scratchPad[(short) (len + 3)] = KMEnum.cast(ptr).getVal(); - len += 4; - // concatenate timestamp -8 bytes - ptr = KMHardwareAuthToken.cast(hwToken).getTimestamp(); - KMInteger.cast(tmpVariables[0]) - .value(scratchPad, (short) (len + (short) (8 - KMInteger.cast(ptr).length()))); - len += 8; - // hmac the data - HMACKey key = - cryptoProvider.createHMACKey( - repository.getComputedHmacKey(), - (short) 0, - (short) repository.getComputedHmacKey().length); - ptr = KMHardwareAuthToken.cast(hwToken).getMac(); - boolean verified = - cryptoProvider.hmacVerify(key, scratchPad, (short) 0, len, - KMByteBlob.cast(ptr).getBuffer(), - KMByteBlob.cast(ptr).getStartOff(), - KMByteBlob.cast(ptr).length()); - if(!verified){ - KMException.throwIt(KMError.VERIFICATION_FAILED); - } -/* - len = - cryptoProvider.hmac(key, scratchPad, (short) 0, len, scratchPad, (short) (len + 1) ); - // Compare mac. - ptr = KMHardwareAuthToken.cast(hwToken).getMac(); - if (len != KMByteBlob.cast(ptr).length()) { - KMException.throwIt(KMError.INVALID_MAC_LENGTH); - } - if (Util.arrayCompare( - scratchPad, - (short) 38, - KMByteBlob.cast(ptr).getBuffer(), - KMByteBlob.cast(ptr).getStartOff(), - len) - != 0) { - KMException.throwIt(KMError.VERIFICATION_FAILED); - } - */ - } - - private void authorizeUserIdPerKeyOperation(short hwToken, byte[] scratchPad) { - tmpVariables[0] = KMHardwareAuthToken.cast(hwToken).getChallenge(); - if (KMInteger.compare(data[OP_HANDLE], tmpVariables[0]) != 0) { - KMException.throwIt(KMError.KEY_USER_NOT_AUTHENTICATED); - } - authorizeUserId(hwToken, scratchPad); - } - - private void authorizeKeyUsageForCount(short limit) { - // get current counter - // TODO currently only short counter supported - max count 32K. - short val = repository.getRateLimitedKeyCount(data[AUTH_TAG]); - if (val != KMType.INVALID_VALUE) { - short count = KMInteger.uint_16(val); - // compare 32 bit values - is current counter less then prescribed limit - if (KMInteger.compare(count, limit) != -1) { - KMException.throwIt(KMError.KEY_MAX_OPS_EXCEEDED); - } - // increment the counter and store it back. - val++; - repository.setRateLimitedKeyCount(data[AUTH_TAG], val); - } else { - KMException.throwIt(KMError.UNKNOWN_ERROR); - } - } private void processImportKeyCmd(APDU apdu) { if (repository.keyBlobCount > repository.MAX_BLOB_STORAGE) { @@ -2893,21 +1539,5 @@ private static void sendError(APDU apdu, short err) { bufferLength = encoder.encodeError(err, buffer, bufferStartOffset, (short) 5); sendOutgoing(apdu); } - /* - private static void print (String lab, byte[] b, short s, short l){ - byte[] i = new byte[l]; - Util.arrayCopyNonAtomic(b,s,i,(short)0,l); - print(lab,i); - } - private static void print(String label, byte[] buf){ - System.out.println(label+": "); - StringBuilder sb = new StringBuilder(); - for(int i = 0; i < buf.length; i++){ - sb.append(String.format(" 0x%02X", buf[i])) ; - if(((i-1)%38 == 0) && ((i-1) >0)){ - sb.append(";\n"); - } - } - System.out.println(sb.toString()); - }*/ + } From be4e3036d5379c3eb40ca47d4b7610269920f859 Mon Sep 17 00:00:00 2001 From: cpathak Date: Tue, 16 Jun 2020 02:06:29 -0700 Subject: [PATCH 6/6] Added Operations and commands - Tested only with jcard sim. - Basic testing with Begin, Update, Finish and Abort. - Basic testing with Upgrade Key, Delete Key, DeleteAllKeys. --- .../javacard/keymaster/KMCipherImpl.java | 15 +- .../javacard/keymaster/KMJcardSimulator.java | 207 ++- .../javacard/keymaster/KMCipherImpl.java | 13 + .../javacard/keymaster/KMSimulator.java | 25 +- .../android/javacard/keymaster/KMCipher.java | 21 +- .../javacard/keymaster/KMCryptoProvider.java | 21 +- .../android/javacard/keymaster/KMDecoder.java | 4 +- .../javacard/keymaster/KMEnumArrayTag.java | 93 + .../keymaster/KMHardwareAuthToken.java | 7 + .../android/javacard/keymaster/KMInteger.java | 23 +- .../javacard/keymaster/KMIntegerTag.java | 66 +- .../javacard/keymaster/KMKeyParameters.java | 120 +- .../javacard/keymaster/KMKeymasterApplet.java | 1631 ++++++++++++++++- .../javacard/keymaster/KMOperationState.java | 60 +- .../javacard/keymaster/KMRepository.java | 22 +- .../keymaster/KMVerificationToken.java | 7 + .../javacard/test/KMFrameworkTest.java | 11 +- .../com/android/javacard/test/KMVTSTest.java | 931 +++++++++- Applet/default.output | 18 +- 19 files changed, 3112 insertions(+), 183 deletions(-) diff --git a/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMCipherImpl.java b/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMCipherImpl.java index 66d988d4..ec34fd32 100644 --- a/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMCipherImpl.java +++ b/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMCipherImpl.java @@ -5,6 +5,8 @@ public class KMCipherImpl extends KMCipher{ Cipher cipher; + short cipherAlg; + short paddingAlg; KMCipherImpl(Cipher c){ cipher = c; } @@ -16,9 +18,13 @@ public short doFinal(byte[] buffer, short startOff, short length, byte[] scratch @Override public short getCipherAlgorithm() { - return cipher.getCipherAlgorithm(); + return cipherAlg; } + @Override + public void setCipherAlgorithm(short alg) { + cipherAlg = alg; + } @Override public short update(byte[] buffer, short startOff, short length, byte[] scratchPad, short i) { return cipher.update(buffer,startOff,length,scratchPad,i); @@ -26,6 +32,11 @@ public short update(byte[] buffer, short startOff, short length, byte[] scratchP @Override public short getPaddingAlgorithm() { - return cipher.getPaddingAlgorithm(); + return paddingAlg; + } + + @Override + public void setPaddingAlgorithm(short alg) { + paddingAlg = alg; } } diff --git a/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMJcardSimulator.java b/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMJcardSimulator.java index 2e12de18..3a4a57b5 100644 --- a/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMJcardSimulator.java +++ b/Applet/Applet/JCardSimProvider/com/android/javacard/keymaster/KMJcardSimulator.java @@ -28,11 +28,14 @@ import javacard.security.CryptoException; import javacard.security.DESKey; import javacard.security.ECPrivateKey; +import javacard.security.ECPublicKey; import javacard.security.HMACKey; import javacard.security.Key; import javacard.security.KeyBuilder; import javacard.security.KeyPair; +import javacard.security.MessageDigest; import javacard.security.RSAPrivateKey; +import javacard.security.RSAPublicKey; import javacard.security.RandomData; import javacard.security.Signature; import javacardx.crypto.Cipher; @@ -115,11 +118,6 @@ public KeyPair createECKeyPair() { public ECPrivateKey createEcKey(byte[] privBuffer, short privOff, short privLength) { KeyPair ecKeyPair = new KeyPair(KeyPair.ALG_EC_FP, KeyBuilder.LENGTH_EC_FP_256); ECPrivateKey privKey = (ECPrivateKey) ecKeyPair.getPrivate(); - if(privLength > 24){ - privLength = 24;// simulator does not support more then 24 bytes - 192 bit key. - }else if(privLength <= 20){ - return null; - } privKey.setS(privBuffer,privOff, privLength); return privKey; } @@ -205,7 +203,8 @@ public short aesGCMEncrypt( } byte[] keyMaterial = new byte[16]; key.getKey(keyMaterial,(short)0); - // print("KeyMaterial", keyMaterial); + //print("KeyMaterial Enc", keyMaterial); + //print("Authdata Enc", authData, authDataStart, authDataLen); java.security.Key aesKey = new SecretKeySpec(keyMaterial,(short)0,(short)16, "AES"); // Create the cipher javax.crypto.Cipher cipher = null; @@ -329,6 +328,9 @@ public boolean aesGCMDecrypt( } byte[] keyMaterial = new byte[16]; key.getKey(keyMaterial,(short)0); + //print("KeyMaterial Dec", keyMaterial); + //print("Authdata Dec", authData, authDataStart, authDataLen); + java.security.Key aesKey = new SecretKeySpec(keyMaterial,(short)0,(short)16, "AES"); // Create the cipher javax.crypto.Cipher cipher = null; @@ -424,7 +426,28 @@ public short aesCCMSign( @Override public HMACKey cmacKdf(byte[] keyMaterial, byte[] label, byte[] context, short contextStart, short contextLength) { - return null; + // This is hardcoded to requirement - 32 byte output with two concatenated 16 bytes K1 and K2. + final byte n = 2; // hardcoded - L is 32 bytes and h is 16 byte i.e. CMAC output of AES 128 bit key. + final byte[] L = {0,0,1,0}; // [L] 256 bits - hardcoded 32 bits as per reference impl in keymaster. + final byte[] zero = {0}; // + byte[] iBuf = new byte[]{0,0,0,0}; // [i] counter - 32 bits + byte[] keyOut = new byte[(short)(n*16)]; + Signature prf = Signature.getInstance(Signature.ALG_AES_CMAC_128, false); + AESKey key = (AESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_AES, KeyBuilder.LENGTH_AES_128, false); + key.setKey(keyMaterial, (short) 0); + prf.init(key, Signature.MODE_SIGN); + byte i =1; + short pos = 0; + while (i <= n) { + iBuf[3] = i; + prf.update(iBuf, (short) 0, (short) 4); // 4 bytes of iBuf with counter in it + prf.update(label, (short) 0, (short) label.length); // label + prf.update(zero, (short) 0, (short) 1); // 1 byte of 0x00 + prf.update(context, contextLength, contextLength); // context + pos = prf.sign(L, (short) 0, (short) 4, keyOut, pos); // 4 bytes of L - signature of 16 bytes + i++; + } + return createHMACKey(keyOut, (short)0, (short)keyOut.length); } @Override @@ -441,10 +464,14 @@ public boolean hmacVerify(HMACKey key, byte[] data, short dataStart, short dataL } @Override - public KMCipher createRsaDecrypt(short cipherAlg, short padding, byte[] secret, short secretStart, - short secretLength, byte[] modBuffer, short modOff, short modLength) { - Cipher rsaCipher = Cipher.getInstance((byte)cipherAlg, - (byte)padding,false); + public KMCipher createRsaDecipher(short padding, byte[] secret, short secretStart, + short secretLength, byte[] modBuffer, short modOff, short modLength) { + byte cipherAlg = Cipher.ALG_RSA_NOPAD; + //TODO implement OAEP algorithm using SunJCE. + if(padding == KMCipher.PAD_PKCS1_OAEP_SHA256) CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); + else if(padding == KMCipher.PAD_PKCS1) cipherAlg = Cipher.ALG_RSA_PKCS1; + else cipherAlg = Cipher.ALG_RSA_NOPAD; + Cipher rsaCipher = Cipher.getInstance(cipherAlg,false); RSAPrivateKey key = (RSAPrivateKey) KeyBuilder.buildKey(KeyBuilder.TYPE_RSA_PRIVATE, KeyBuilder.LENGTH_RSA_2048, false); key.setExponent(secret,secretStart,secretLength); key.setModulus(modBuffer, modOff, modLength); @@ -454,7 +481,13 @@ public KMCipher createRsaDecrypt(short cipherAlg, short padding, byte[] secret, @Override public Signature createRsaSigner(short msgDigestAlg, short padding, byte[] secret, short secretStart, short secretLength, byte[] modBuffer, short modOff, short modLength) { - Signature rsaSigner = Signature.getInstance((byte)msgDigestAlg, Signature.SIG_CIPHER_RSA,(byte)padding,false); + short alg = Signature.ALG_RSA_SHA_256_PKCS1; + if(msgDigestAlg == MessageDigest.ALG_NULL) CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); + else if(padding == KMCipher.PAD_NOPAD) CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); + else if(padding == KMCipher.PAD_PKCS1_PSS) alg = Signature.ALG_RSA_SHA_256_PKCS1_PSS; + else if(padding == KMCipher.PAD_PKCS1) alg = Signature.ALG_RSA_SHA_256_PKCS1; + else CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); + Signature rsaSigner = Signature.getInstance((byte)alg, false); RSAPrivateKey key = (RSAPrivateKey) KeyBuilder.buildKey(KeyBuilder.TYPE_RSA_PRIVATE, KeyBuilder.LENGTH_RSA_2048, false); key.setExponent(secret,secretStart,secretLength); key.setModulus(modBuffer, modOff, modLength); @@ -464,67 +497,94 @@ public Signature createRsaSigner(short msgDigestAlg, short padding, byte[] secre @Override public Signature createEcSigner(short msgDigestAlg, byte[] secret, short secretStart, short secretLength) { - Signature ecSigner = Signature.getInstance((byte)msgDigestAlg, Signature.SIG_CIPHER_ECDSA,Cipher.PAD_NOPAD,false); + short alg = Signature.ALG_ECDSA_SHA_256; + if(msgDigestAlg == MessageDigest.ALG_NULL) CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); + //KeyPair ecKeyPair = new KeyPair(KeyPair.ALG_EC_FP, KeyBuilder.LENGTH_EC_FP_256); + //ecKeyPair.genKeyPair(); + //ECPrivateKey privKey = (ECPrivateKey) ecKeyPair.getPrivate(); + //privKey.setS(secret,secretStart, secretLength); ECPrivateKey key = (ECPrivateKey) KeyBuilder.buildKey(KeyBuilder.TYPE_EC_FP_PRIVATE, KeyBuilder.LENGTH_EC_FP_256, false); key.setS(secret,secretStart,secretLength); + Signature ecSigner = Signature.getInstance((byte)alg,false); ecSigner.init(key,Signature.MODE_SIGN); return ecSigner; } @Override - public KMCipher createSymmetricCipher(short cipherAlg, short padding, short mode, byte[] secret, + public KMCipher createSymmetricCipher( + short cipherAlg, short mode, short padding, byte[] secret, short secretStart, short secretLength) { + return createSymmetricCipher(cipherAlg, mode, padding, secret,secretStart,secretLength,null,(short)0,(short)0); + } + + @Override + public KMCipher createSymmetricCipher(short cipherAlg, short mode, short padding, byte[] secret, short secretStart, short secretLength, byte[] ivBuffer, short ivStart, short ivLength) { Key key = null; + Cipher symmCipher = null; short len = 0; - if(cipherAlg == Cipher.CIPHER_AES_CBC || cipherAlg == Cipher.CIPHER_AES_CBC){ - if(secretLength == 32){ + switch (secretLength){ + case 32: len = KeyBuilder.LENGTH_AES_256; - }else if(secretLength == 16){ + break; + case 16: len = KeyBuilder.LENGTH_AES_128; - }else{ + break; + case 24: + len = KeyBuilder.LENGTH_DES3_3KEY; + break; + default: CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); - } - - //TODO - }else if(secretLength != 21){ // DES Key - CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); - }else{ //DES Key - len = KeyBuilder.LENGTH_DES3_3KEY; + break; } switch(cipherAlg){ - case Cipher.CIPHER_AES_CBC: - case Cipher.CIPHER_AES_ECB: + case KMCipher.ALG_AES_BLOCK_128_CBC_NOPAD: + cipherAlg = Cipher.ALG_AES_BLOCK_128_CBC_NOPAD; + key = KeyBuilder.buildKey(KeyBuilder.TYPE_AES,len,false); + ((AESKey) key).setKey(secret,secretStart); + symmCipher = Cipher.getInstance((byte)cipherAlg, false); + symmCipher.init(key, (byte) mode, ivBuffer, ivStart, ivLength); + break; + case KMCipher.ALG_AES_BLOCK_128_ECB_NOPAD: + cipherAlg = Cipher.ALG_AES_BLOCK_128_ECB_NOPAD; key = KeyBuilder.buildKey(KeyBuilder.TYPE_AES,len,false); ((AESKey) key).setKey(secret,secretStart); + symmCipher = Cipher.getInstance((byte)cipherAlg, false); + symmCipher.init(key, (byte) mode); break; - case Cipher.CIPHER_DES_CBC: - case Cipher.CIPHER_DES_ECB: + case KMCipher.ALG_DES_CBC_NOPAD: + cipherAlg = Cipher.ALG_DES_CBC_NOPAD; key = (DESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_DES,len,false); ((DESKey) key).setKey(secret,secretStart); + symmCipher = Cipher.getInstance((byte)cipherAlg, false); + symmCipher.init(key, (byte) mode, ivBuffer, ivStart, ivLength); + break; + case KMCipher.ALG_DES_ECB_NOPAD: + cipherAlg = Cipher.ALG_DES_ECB_NOPAD; + key = (DESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_DES,len,false); + ((DESKey) key).setKey(secret,secretStart); + symmCipher = Cipher.getInstance((byte)cipherAlg, false); + symmCipher.init(key, (byte) mode); break; default://This should never happen CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); break; } - - //TODO - Cipher symmCipher = Cipher.getInstance((byte)cipherAlg, Cipher.PAD_NOPAD,false); - if (ivBuffer != null) { - symmCipher.init(key, (byte) mode, ivBuffer, ivStart, ivLength); - }else{ - symmCipher.init(key, (byte) mode); - } - return new KMCipherImpl(symmCipher); + KMCipher cipher = new KMCipherImpl(symmCipher); + cipher.setCipherAlgorithm(cipherAlg); + cipher.setPaddingAlgorithm(padding); + return cipher; } @Override - public Signature createHmacSigner(short msgDigestAlg, byte[] secret, short secretStart, short secretLength) { - Signature hmacSigner = Signature.getInstance((byte)msgDigestAlg, Signature.SIG_CIPHER_HMAC,Cipher.PAD_NOPAD,false); + public Signature createHmacSignerVerifier(short purpose, short msgDigestAlg, byte[] secret, short secretStart, short secretLength) { + short alg = Signature.ALG_HMAC_SHA_256; + if(msgDigestAlg != MessageDigest.ALG_SHA_256) CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + Signature hmacSignerVerifier = Signature.getInstance((byte)alg,false); HMACKey key = (HMACKey) KeyBuilder.buildKey(KeyBuilder.TYPE_HMAC, (short)(secretLength*8), false); key.setKey(secret,secretStart,secretLength); - hmacSigner.init(key,Signature.MODE_SIGN); - return hmacSigner; + hmacSignerVerifier.init(key,(byte)purpose); + return hmacSignerVerifier; } @Override @@ -649,12 +709,65 @@ public void addRngEntropy(byte[] num, short offset, short length) { } } } - private void print (String lab, byte[] b, short s, short l){ + + @Override + public void bypassAesGcm(){ + //ignore + } + + @Override + public KMCipher createRsaCipher(short padding, byte[] modBuffer, short modOff, short modLength) { + byte cipherAlg = Cipher.ALG_RSA_NOPAD; + //TODO implement OAEP algorithm using SunJCE. + //TODO for no pad the buffer length must be 255 max. + if(padding == KMCipher.PAD_PKCS1_OAEP_SHA256) CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); + else if(padding == KMCipher.PAD_PKCS1) cipherAlg = Cipher.ALG_RSA_PKCS1; + else cipherAlg = Cipher.ALG_RSA_NOPAD; + Cipher rsaCipher = Cipher.getInstance(cipherAlg,false); + RSAPublicKey key = (RSAPublicKey) KeyBuilder.buildKey(KeyBuilder.TYPE_RSA_PUBLIC, KeyBuilder.LENGTH_RSA_2048, false); + byte[] exponent = new byte[]{0x01,0x00,0x01}; + key.setExponent(exponent,(short)0,(short)3); + key.setModulus(modBuffer, modOff, modLength); + rsaCipher.init(key,Cipher.MODE_ENCRYPT); + return new KMCipherImpl(rsaCipher); + } + + @Override + public Signature createRsaVerifier(short msgDigestAlg, short padding, byte[] modBuffer, short modOff, short modLength) { + short alg = Signature.ALG_RSA_SHA_256_PKCS1; + if(msgDigestAlg == MessageDigest.ALG_NULL) CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); + else if(padding == KMCipher.PAD_NOPAD) CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); + else if(padding == KMCipher.PAD_PKCS1_PSS) alg = Signature.ALG_RSA_SHA_256_PKCS1_PSS; + else if(padding == KMCipher.PAD_PKCS1) alg = Signature.ALG_RSA_SHA_256_PKCS1; + else CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); + Signature rsaVerifier = Signature.getInstance((byte)alg, false); + RSAPublicKey key = (RSAPublicKey) KeyBuilder.buildKey(KeyBuilder.TYPE_RSA_PUBLIC, KeyBuilder.LENGTH_RSA_2048, false); + byte[] exponent = new byte[]{0x01,0x00,0x01}; + key.setExponent(exponent,(short)0,(short)3); + key.setModulus(modBuffer, modOff, modLength); + rsaVerifier.init(key,Signature.MODE_VERIFY); + return rsaVerifier; + } + @Override + public Signature createEcVerifier(short msgDigestAlg, byte[] pubKey, short pubKeyStart, short pubKeyLength) { + short alg = Signature.ALG_ECDSA_SHA_256; + if(msgDigestAlg == MessageDigest.ALG_NULL) CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); +// KeyPair ecKeyPair = new KeyPair(KeyPair.ALG_EC_FP, KeyBuilder.LENGTH_EC_FP_256); +// ecKeyPair.genKeyPair(); +// ECPublicKey key = (ECPublicKey) ecKeyPair.getPublic(); + ECPublicKey key = (ECPublicKey) KeyBuilder.buildKey(KeyBuilder.TYPE_EC_FP_PUBLIC, KeyBuilder.LENGTH_EC_FP_256, false); + key.setW(pubKey,pubKeyStart,pubKeyLength); + Signature ecVerifier = Signature.getInstance((byte)alg,false); + ecVerifier.init(key,Signature.MODE_VERIFY); + return ecVerifier; + } + /* + private static void print (String lab, byte[] b, short s, short l){ byte[] i = new byte[l]; Util.arrayCopyNonAtomic(b,s,i,(short)0,l); print(lab,i); } - private void print(String label, byte[] buf){ + private static void print(String label, byte[] buf){ System.out.println(label+": "); StringBuilder sb = new StringBuilder(); for(int i = 0; i < buf.length; i++){ @@ -664,9 +777,5 @@ private void print(String label, byte[] buf){ } } System.out.println(sb.toString()); - } - @Override - public void bypassAesGcm(){ - //ignore - } + }*/ } diff --git a/Applet/Applet/OracleSimProvider/com/android/javacard/keymaster/KMCipherImpl.java b/Applet/Applet/OracleSimProvider/com/android/javacard/keymaster/KMCipherImpl.java index 66d988d4..ccc1dc7e 100644 --- a/Applet/Applet/OracleSimProvider/com/android/javacard/keymaster/KMCipherImpl.java +++ b/Applet/Applet/OracleSimProvider/com/android/javacard/keymaster/KMCipherImpl.java @@ -5,6 +5,8 @@ public class KMCipherImpl extends KMCipher{ Cipher cipher; + short cipherAlg; + short paddingAlg; KMCipherImpl(Cipher c){ cipher = c; } @@ -19,6 +21,17 @@ public short getCipherAlgorithm() { return cipher.getCipherAlgorithm(); } + + @Override + public void setPaddingAlgorithm(short alg) { + paddingAlg = alg; + } + + @Override + public void setCipherAlgorithm(short alg) { + cipherAlg = alg; + } + @Override public short update(byte[] buffer, short startOff, short length, byte[] scratchPad, short i) { return cipher.update(buffer,startOff,length,scratchPad,i); diff --git a/Applet/Applet/OracleSimProvider/com/android/javacard/keymaster/KMSimulator.java b/Applet/Applet/OracleSimProvider/com/android/javacard/keymaster/KMSimulator.java index 51c87142..12fb7699 100644 --- a/Applet/Applet/OracleSimProvider/com/android/javacard/keymaster/KMSimulator.java +++ b/Applet/Applet/OracleSimProvider/com/android/javacard/keymaster/KMSimulator.java @@ -367,9 +367,8 @@ public boolean hmacVerify(HMACKey key, byte[] data, short dataStart, short dataL return false; } - @Override - public KMCipher createRsaDecrypt(short cipherAlg, short padding, byte[] secret, short secretStart, short secretLength, byte[] modBuffer, short modOff, short modLength) { + public KMCipher createRsaDecipher(short padding, byte[] secret, short secretStart, short secretLength, byte[] modBuffer, short modOff, short modLength) { return null; } @@ -389,7 +388,12 @@ public KMCipher createSymmetricCipher(short cipherAlg, short padding, short mode } @Override - public Signature createHmacSigner(short msgDigestAlg, byte[] secret, short secretStart, short secretLength) { + public KMCipher createSymmetricCipher(short cipherAlg, short mode, short padding, byte[] secret, short secretStart, short secretLength) { + return null; + } + + @Override + public Signature createHmacSignerVerifier(short purpose, short msgDigestAlg, byte[] secret, short secretStart, short secretLength) { return null; } @@ -502,4 +506,19 @@ private void incrementCounter() { public void bypassAesGcm(){ jcardSim = true; } + + @Override + public KMCipher createRsaCipher(short padding, byte[] modBuffer, short modOff, short modLength) { + return null; + } + + @Override + public Signature createRsaVerifier(short msgDigestAlg, short padding, byte[] modBuffer, short modOff, short modLength) { + return null; + } + @Override + public Signature createEcVerifier(short msgDigestAlg, byte[] pubKey, short pubKeyStart, short pubKeyLength) { + return null; + } + } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMCipher.java b/Applet/Applet/src/com/android/javacard/keymaster/KMCipher.java index 922f9a43..08cdf789 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMCipher.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMCipher.java @@ -1,30 +1,35 @@ package com.android.javacard.keymaster; public abstract class KMCipher { - public static final byte CIPHER_RSA = 7; - public static final short PAD_PKCS1_OAEP_SHA224 = 13; public static final byte PAD_PKCS1_OAEP_SHA256 = 14; public static final short PAD_PKCS1_OAEP_SHA384 = 15; public static final short PAD_PKCS1_OAEP_SHA512 = 16; public static final short PAD_NOPAD = 1; + public static final short PAD_PKCS1_PSS = 8; public static final short PAD_NULL = 0; public static final short PAD_PKCS7 = 31; // Not supported in javacard - public static final short CIPHER_DES_CBC = 3; - public static final short CIPHER_DES_ECB = 4; - public static final short CIPHER_AES_CBC = 1; - public static final short CIPHER_AES_ECB = 2; + public static final short ALG_DES_CBC_NOPAD = 1; + public static final short ALG_DES_ECB_NOPAD = 5; + public static final short ALG_AES_BLOCK_128_CBC_NOPAD= 13; + public static final short ALG_AES_BLOCK_128_ECB_NOPAD = 14; public static final short MODE_ENCRYPT = 2; public static final short MODE_DECRYPT = 1; public static final short PAD_PKCS1 = 7; + public static final short AES_BLOCK_SIZE = 16; + public static final short DES_BLOCK_SIZE = 8; public abstract short doFinal(byte[] buffer, short startOff, short length, byte[] scratchPad, short i); - public abstract short getCipherAlgorithm(); - public abstract short update(byte[] buffer, short startOff, short length, byte[] scratchPad, short i); public abstract short getPaddingAlgorithm(); + public abstract short getCipherAlgorithm(); + + public abstract void setPaddingAlgorithm(short alg); + + public abstract void setCipherAlgorithm(short alg); + } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMCryptoProvider.java b/Applet/Applet/src/com/android/javacard/keymaster/KMCryptoProvider.java index 055822e5..44fd05e7 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMCryptoProvider.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMCryptoProvider.java @@ -85,18 +85,20 @@ RSAPrivateKey createRsaKey(byte[] modBuffer, short modOff, short modLength, boolean hmacVerify(HMACKey key, byte[] data, short dataStart, short dataLength, byte[] mac, short macStart, short macLength); - KMCipher createRsaDecrypt(short cipherAlg, short padding, - byte[] secret, short secretStart, short secretLength, - byte[] modBuffer, short modOff, short modLength); + KMCipher createRsaDecipher(short padding, + byte[] secret, short secretStart, short secretLength, + byte[] modBuffer, short modOff, short modLength); Signature createRsaSigner(short msgDigestAlg, short padding, byte[] secret, short secretStart, - short secretLength,byte[] modBuffer, short modOff, short modLength); + short secretLength, byte[] modBuffer, short modOff, short modLength); Signature createEcSigner(short msgDigestAlg, byte[] secret, short secretStart, short secretLength); - KMCipher createSymmetricCipher(short cipherAlg, short padding, short mode, + KMCipher createSymmetricCipher(short cipherAlg, short mode, short padding, byte[] secret, short secretStart, short secretLength, byte[] ivBuffer, short ivStart, short ivLength); - Signature createHmacSigner(short msgDigestAlg, - byte[] secret, short secretStart, short secretLength); + KMCipher createSymmetricCipher(short cipherAlg, short mode,short padding, + byte[] secret, short secretStart, short secretLength); + Signature createHmacSignerVerifier(short purpose, short msgDigestAlg, + byte[] secret, short secretStart, short secretLength); KMCipher createGCMCipher(short mode, byte[] secret, short secretStart, short secretLength, byte[] ivBuffer, short ivStart, short ivLength); void delete(KMCipher cipher); @@ -105,4 +107,9 @@ KMCipher createGCMCipher(short mode, byte[] secret, short secretStart, short sec void delete(KeyPair keyPair); //TODO remove this later void bypassAesGcm(); + + KMCipher createRsaCipher(short padding, byte[] buffer, short startOff, short length); + Signature createRsaVerifier(short msgDigestAlg, short padding, byte[] modBuffer, + short modOff, short modLength); + Signature createEcVerifier(short msgDigestAlg, byte[] pubKey, short pubKeyStart, short pubKeyLength); } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMDecoder.java b/Applet/Applet/src/com/android/javacard/keymaster/KMDecoder.java index daefb282..a1ced7cd 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMDecoder.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMDecoder.java @@ -92,7 +92,7 @@ private short decode(short exp){ case KMType.KEY_CHAR_TYPE: return decodeKeyChar(exp); case KMType.VERIFICATION_TOKEN_TYPE: - return decodeVeriToken(exp); + return decodeVerificationToken(exp); case KMType.HMAC_SHARING_PARAM_TYPE: return decodeHmacSharingParam(exp); case KMType.HW_AUTH_TOKEN_TYPE: @@ -128,7 +128,7 @@ private short decodeTag(short tagType, short exp){ } } - private short decodeVeriToken(short exp) { + private short decodeVerificationToken(short exp) { short vals = decode(KMVerificationToken.cast(exp).getVals()); return KMVerificationToken.instance(vals); } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMEnumArrayTag.java b/Applet/Applet/src/com/android/javacard/keymaster/KMEnumArrayTag.java index 1b1a56ee..6304d7a2 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMEnumArrayTag.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMEnumArrayTag.java @@ -185,4 +185,97 @@ public boolean contains(short tagValue){ } return false; } + + public boolean isValidDigests(byte alg){ + short index = 0; + short digest; + while(index < length()){ + digest = get(index); + switch (alg) { + case KMType.EC: + case KMType.RSA: + if (digest != KMType.DIGEST_NONE && digest != KMType.SHA2_256) return false; + break; + case KMType.HMAC: + if (digest != KMType.SHA2_256) return false; + break; + case KMType.AES: + case KMType.DES: + if (digest != KMType.DIGEST_NONE) return false; + break; + default: + return false; + } + index++; + } + return true; + } + + public boolean isValidPaddingModes(byte alg){ + short index = 0; + short padding; + while(index < length()){ + padding = get(index); + switch(alg){ + case KMType.RSA: + if(padding != KMType.RSA_OAEP && padding != KMType.PADDING_NONE && + padding != KMType.RSA_PKCS1_1_5_SIGN && padding != KMType.RSA_PKCS1_1_5_ENCRYPT && + padding != KMType.RSA_PSS){ + return false; + } + break; + case KMType.AES: + case KMType.DES: + if(padding != KMType.PKCS7 && padding != KMType.PADDING_NONE){ + return false; + } + break; + case KMType.EC: + case KMType.HMAC: + if(padding != PADDING_NONE){ + return false; + } + break; + default: + return false; + } + index++; + } + return true; + } + public boolean isValidPurpose(byte alg){ + short index = 0; + short purpose; + while(index < length()){ + purpose = get(index); + switch(purpose){ + case KMType.DECRYPT: + case KMType.ENCRYPT: + if(alg != KMType.RSA && alg != KMType.AES && + alg != KMType.DES){ + return false; + } + break; + case KMType.SIGN: + case KMType.VERIFY: + if(alg != KMType.HMAC && alg != KMType.RSA && + alg != KMType.EC){ + return false; + } + break; + default: + return false; + } + index++; + } + return true; + } + + public boolean isValidBlockMode(byte alg) { + if (alg == KMType.AES || alg == KMType.DES) { + return true; + } else { + return false; + } + } } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMHardwareAuthToken.java b/Applet/Applet/src/com/android/javacard/keymaster/KMHardwareAuthToken.java index 23245352..96b1ed9d 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMHardwareAuthToken.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMHardwareAuthToken.java @@ -53,6 +53,13 @@ private static KMHardwareAuthToken proto(short ptr) { public static short instance() { short arrPtr = KMArray.instance((short)6); + KMArray arr = KMArray.cast(arrPtr); + arr.add(CHALLENGE, KMInteger.uint_16((short)0)); + arr.add(USER_ID, KMInteger.uint_16((short)0)); + arr.add(AUTHENTICATOR_ID, KMInteger.uint_16((short)0)); + arr.add(HW_AUTHENTICATOR_TYPE, KMEnum.instance(KMType.USER_AUTH_TYPE, KMType.USER_AUTH_NONE)); + arr.add(TIMESTAMP, KMInteger.uint_16((short)0)); + arr.add(MAC, KMByteBlob.instance((short)0)); return instance(arrPtr); } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMInteger.java b/Applet/Applet/src/com/android/javacard/keymaster/KMInteger.java index 6fff9f04..4d7e33b1 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMInteger.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMInteger.java @@ -147,15 +147,20 @@ public boolean isZero() { } public static short compare(short num1, short num2){ - short num1Ptr = KMInteger.cast(num1).getStartOff(); - short num2Ptr = KMInteger.cast(num2).getStartOff(); - short len = KMInteger.cast(num2).length(); - if(KMInteger.cast(num1).length() > KMInteger.cast(num2).length()){ - len = KMInteger.cast(num1).length(); - } + short num1Buf = repository.alloc((short)8); + short num2Buf = repository.alloc((short)8); + Util.arrayFillNonAtomic(repository.getHeap(),num1Buf,(short)8,(byte)0); + Util.arrayFillNonAtomic(repository.getHeap(),num2Buf,(short)8,(byte)0); + short numPtr = KMInteger.cast(num1).getStartOff(); + short len = KMInteger.cast(num1).length(); + KMInteger.cast(num1).getValue(repository.getHeap(),(short)(num1Buf+(short)(8-len)),len); + numPtr = KMInteger.cast(num2).getStartOff(); + len = KMInteger.cast(num2).length(); + KMInteger.cast(num2).getValue(repository.getHeap(),(short)(num2Buf+(short)(8-len)),len); return Util.arrayCompare( - repository.getHeap(), num1Ptr, - repository.getHeap(), num2Ptr, - len); + repository.getHeap(), num1Buf, + repository.getHeap(), num2Buf, + (short)8); } + } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMIntegerTag.java b/Applet/Applet/src/com/android/javacard/keymaster/KMIntegerTag.java index 0ac49e06..e3736793 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMIntegerTag.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMIntegerTag.java @@ -60,10 +60,10 @@ public static short exp(short tagType) { ISOException.throwIt(ISO7816.SW_DATA_INVALID); } short intPtr = KMInteger.exp(); - short ptr = instance(TAG_TYPE, (short)6); - Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE), tagType); - Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE+2), INVALID_TAG); - Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE+4), intPtr); + short ptr = instance(TAG_TYPE, (short) 6); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), tagType); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), INVALID_TAG); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), intPtr); return ptr; } @@ -85,13 +85,13 @@ public static short instance(short tagType, short key, short intObj) { if (!validateKey(key)) { ISOException.throwIt(ISO7816.SW_DATA_INVALID); } - if(heap[intObj] != INTEGER_TYPE) { + if (heap[intObj] != INTEGER_TYPE) { ISOException.throwIt(ISO7816.SW_DATA_INVALID); } - short ptr = instance(TAG_TYPE, (short)6); - Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE), tagType); - Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE+2), key); - Util.setShort(heap, (short)(ptr+TLV_HEADER_SIZE+4), intObj); + short ptr = instance(TAG_TYPE, (short) 6); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), tagType); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), key); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), intObj); return ptr; } @@ -105,15 +105,15 @@ public static KMIntegerTag cast(short ptr) { } public short getTagType() { - return Util.getShort(heap, (short)(instPtr+TLV_HEADER_SIZE)); + return Util.getShort(heap, (short) (instPtr + TLV_HEADER_SIZE)); } public short getKey() { - return Util.getShort(heap, (short)(instPtr+TLV_HEADER_SIZE+2)); + return Util.getShort(heap, (short) (instPtr + TLV_HEADER_SIZE + 2)); } public short getValue() { - return Util.getShort(heap, (short)(instPtr+TLV_HEADER_SIZE+4)); + return Util.getShort(heap, (short) (instPtr + TLV_HEADER_SIZE + 4)); } public short length() { @@ -121,7 +121,6 @@ public short length() { return obj.length(); } - private static boolean validateKey(short key) { short index = (short) tags.length; while (--index >= 0) { @@ -140,13 +139,13 @@ private static boolean validateTagType(short tagType) { return false; } - public static short getShortValue(short tagType, short tagKey, short keyParameters){ + public static short getShortValue(short tagType, short tagKey, short keyParameters) { short ptr; - if(tagType == UINT_TAG){ + if (tagType == UINT_TAG) { ptr = KMKeyParameters.findTag(KMType.UINT_TAG, tagKey, keyParameters); if (ptr != KMType.INVALID_VALUE) { ptr = KMIntegerTag.cast(ptr).getValue(); - if(KMInteger.cast(ptr).getSignificantShort() == 0){ + if (KMInteger.cast(ptr).getSignificantShort() == 0) { return KMInteger.cast(ptr).getShort(); } } @@ -154,15 +153,44 @@ public static short getShortValue(short tagType, short tagKey, short keyParamete return KMType.INVALID_VALUE; } - public static short getValue(byte[] buf, short offset, short tagType, short tagKey, short keyParameters){ + public static short getValue( + byte[] buf, short offset, short tagType, short tagKey, short keyParameters) { short ptr; - if((tagType == UINT_TAG) || (tagType == ULONG_TAG) || (tagType == DATE_TAG)){ + if ((tagType == UINT_TAG) || (tagType == ULONG_TAG) || (tagType == DATE_TAG)) { ptr = KMKeyParameters.findTag(tagType, tagKey, keyParameters); if (ptr != KMType.INVALID_VALUE) { ptr = KMIntegerTag.cast(ptr).getValue(); - return KMInteger.cast(ptr).value(buf,offset); + return KMInteger.cast(ptr).value(buf, offset); } } return KMType.INVALID_VALUE; } + + public boolean isValidKeySize(byte alg) { + short val = KMIntegerTag.cast(instPtr).getValue(); + if (KMInteger.cast(val).getSignificantShort() != 0) { + return false; + } + val = KMInteger.cast(val).getShort(); + switch (alg) { + case KMType.RSA: + if (val == 2048) return true; + break; + case KMType.AES: + if (val == 128 || val == 256) return true; + break; + case KMType.DES: + if (val == 192 || val == 168) return true; + break; + case KMType.EC: + if (val == 256) return true; + break; + case KMType.HMAC: + if (val % 8 == 0 && val >= 64 && val <= 512) return true; + break; + default: + break; + } + return false; + } } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java b/Applet/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java index d3bf5af1..69164fce 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java @@ -84,7 +84,11 @@ public short length() { public static short findTag(short tagType, short tagKey, short keyParam) { KMKeyParameters instParam = KMKeyParameters.cast(keyParam); - KMArray vals = KMArray.cast(instParam.getVals()); + return instParam.findTag(tagType, tagKey); + } + + public short findTag(short tagType, short tagKey){ + KMArray vals = KMArray.cast(getVals()); short index = 0; short length = vals.length(); short key; @@ -272,4 +276,118 @@ public static short createKeyParameters(byte[] ptrArr, short len) { } return KMKeyParameters.instance(arrPtr); } +/* + public void validateKeyCreation(){ + short alg = KMEnumTag.getValue(KMType.ALGORITHM,instPtr); + if(alg == KMType.INVALID_VALUE){ + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + short keySize = KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, instPtr); + short padding = KMEnumArrayTag. + validateKeySize(keySize, alg); + + if(padding != KMType.INVALID_VALUE){ + validatePadding(padding, alg); + } + if(digest != KMType.INVALID_VALUE){ + validateDigest(digest, alg); + } + if(purpose != KMType.INVALID_VALUE){ + validatePurpose(purpose, alg); + } + switch(alg){ + case KMType.RSA: + validateRsa(keySize, padding, digest, purpose, arrPtr); + break; + case KMType.AES: + validateAes(keySize, padding, digest, purpose); + break; + case KMType.DES: + validateDes(keySize, padding, digest, purpose); + break; + case KMType.EC: + validateEc(keySize, padding, digest, purpose); + break; + case KMType.HMAC: + validateHmac(keySize, padding, digest, purpose); + break; + default: + KMException.throwIt(KMError.INVALID_ARGUMENT); + break; + } + final byte[] alg = {, KMType.AES, KMType.DES, KMType.EC, KMType.HMAC}; + final Object[] keySizes = { + new short[]{2048}, + new short[]{128, 256}, + new short[]{192}, + new short[]{256}, + new short[]{128, 256, 512} + }; + final Object[] digests = { + new byte[]{KMType.DIGEST_NONE,KMType.SHA2_256}, + new byte[]{KMType.DIGEST_NONE}, + new byte[]{KMType.DIGEST_NONE}, + new byte[]{KMType.DIGEST_NONE,KMType.SHA2_256}, + new byte[]{KMType.SHA2_256} + }; + final Object[] paddings = { + new byte[]{KMType.PADDING_NONE, KMType.RSA_OAEP, KMType.RSA_PKCS1_1_5_ENCRYPT, KMType.RSA_PKCS1_1_5_SIGN, KMType.RSA_PSS}, + new byte[]{KMType.PADDING_NONE, KMType.PKCS7}, + new byte[]{KMType.PADDING_NONE, KMType.PKCS7}, + new byte[]{PADDING_NONE}, + new byte[]{PADDING_NONE} + }; + final Object[] purposes ={ + new byte[]{KMType.ENCRYPT, KMType.DECRYPT, KMType.SIGN, KMType.VERIFY}, + new byte[]{KMType.ENCRYPT, KMType.DECRYPT}, + new byte[]{KMType.ENCRYPT, KMType.DECRYPT}, + new byte[]{KMType.SIGN, KMType.VERIFY}, + new byte[]{KMType.SIGN, KMType.VERIFY} + }; + + } + short keySize = findTag(KMType.UINT_TAG, KMType.KEYSIZE, this); + short padding = findTag(KMType.ENUM_ARRAY_TAG, KMType.PADDING, this); + short digest = findTag(KMType.ENUM_ARRAY_TAG, KMType.DIGEST, ); + short purpose = findTag(KMType.ENUM_ARRAY_TAG, KMType.PURPOSE, keyParams); + + private void validateKeySize(short keySize, short alg){ + if() + } + private static void validateRsa(short keySize, short padding, short digest, short purpose, short arr) { + short ptr = + if(KMIntegerTag.cast(keySize).) + + } + + public boolean validateDigest(short digest){ + short index = 0; + while(index < KMEnumArrayTag.cast(digest).length()){ + if(KMEnumArrayTag.cast(digest).get(index) != KMType.DIGEST_NONE && + KMEnumArrayTag.cast(digest).get(index) != KMType.SHA2_256){ + return false; + } + index++; + } + return true; + } +*/ +/* + private static void print (String lab, byte[] b, short s, short l){ + byte[] i = new byte[l]; + Util.arrayCopyNonAtomic(b,s,i,(short)0,l); + print(lab,i); + } + private static void print(String label, byte[] buf){ + System.out.println(label+": "); + StringBuilder sb = new StringBuilder(); + for(int i = 0; i < buf.length; i++){ + sb.append(String.format(" 0x%02X", buf[i])) ; + if(((i-1)%38 == 0) && ((i-1) >0)){ + sb.append(";\n"); + } + } + System.out.println(sb.toString()); + } +*/ } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java b/Applet/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java index bfd769df..c65b8fb9 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java @@ -24,6 +24,7 @@ import javacard.framework.JCSystem; import javacard.framework.Util; import javacard.security.AESKey; +import javacard.security.CryptoException; import javacard.security.DESKey; import javacard.security.ECPrivateKey; import javacard.security.ECPublicKey; @@ -48,7 +49,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) 128; + private static final short MAX_AUTH_DATA_SIZE = (short) 512; private static final short MAX_IO_LENGTH = 0x400; // "Keymaster HMAC Verification" - used for HMAC key verification. public static final byte[] sharingCheck = { @@ -63,6 +64,10 @@ public class KMKeymasterApplet extends Applet implements AppletEvent, ExtendedLe // "Auth Verification" public static final byte[] authVerification = {0x41, 0x75, 0x74, 0x68, 0x20, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E}; + // "confirmation token" + public static final byte[] confirmationToken = {0x63, 0x6F, 0x6E, 0x66, 0x69, 0x72, 0x6D, 0x61, 0x74, + 0x69, 0x6F, 0x6E, 0x20, 0x74, 0x6F, 0x6B, 0x65, 0x6E}; + // Possible states of the applet. private static final byte ILLEGAL_STATE = 0x00; private static final byte INSTALL_STATE = 0x01; @@ -124,6 +129,7 @@ public class KMKeymasterApplet extends Applet implements AppletEvent, ExtendedLe public static final byte OUTPUT_DATA = 25; public static final byte HW_TOKEN = 26; public static final byte VERIFICATION_TOKEN = 27; + private static final byte SIGNATURE = 28; // AddRngEntropy private static final short MAX_SEED_SIZE = 2048; @@ -506,6 +512,25 @@ private void processGetKeyCharacteristicsCmd(APDU apdu) { } private void processGetHmacSharingParamCmd(APDU apdu) { + // No Arguments + byte[] scratchPad = apdu.getBuffer(); + // Create blob containing seed + tmpVariables[0] = + KMByteBlob.instance(repository.getHmacSeed(), (short) 0, repository.HMAC_SEED_NONCE_SIZE); + // Create blob containing nonce + cryptoProvider.newRandomNumber(scratchPad, (short) 0, repository.HMAC_SEED_NONCE_SIZE); + tmpVariables[1] = KMByteBlob.instance(scratchPad, (short) 0, repository.HMAC_SEED_NONCE_SIZE); + // Create HMAC Sharing Parameters + tmpVariables[2] = KMHmacSharingParameters.instance(); + KMHmacSharingParameters.cast(tmpVariables[2]).setNonce(tmpVariables[1]); + KMHmacSharingParameters.cast(tmpVariables[2]).setSeed(tmpVariables[0]); + // prepare the response + tmpVariables[3] = KMArray.instance((short) 2); + KMArray.cast(tmpVariables[3]).add((short) 0, KMInteger.uint_16(KMError.OK)); + KMArray.cast(tmpVariables[3]).add((short) 1, tmpVariables[2]); + // Encode the response + bufferLength = encoder.encode(tmpVariables[0], buffer, bufferStartOffset); + sendOutgoing(apdu); } private void processDeleteAllKeysCmd(APDU apdu) { @@ -556,15 +581,324 @@ private void processDeleteKeyCmd(APDU apdu) { } private void processComputeSharedHmacCmd(APDU apdu) { + // Receive the incoming request fully from the master into buffer. + receiveIncoming(apdu); + byte[] scratchPad = apdu.getBuffer(); + tmpVariables[1] = KMArray.instance((short) 1); + tmpVariables[2] = KMKeyParameters.exp(); + tmpVariables[3] = KMHmacSharingParameters.exp(); + KMArray.cast(tmpVariables[1]).add((short) 0, KMArray.exp(tmpVariables[3])); // Vector of hmac params + KMArray.cast(tmpVariables[1]).add((short) 1, tmpVariables[2]); // Key Params + // Decode the arguments + tmpVariables[2] = decoder.decode(tmpVariables[1], buffer, bufferStartOffset, bufferLength); + data[KEY_PARAMETERS] = KMArray.cast(tmpVariables[2]).get((short) 1); + data[HMAC_SHARING_PARAMS] = KMArray.cast(tmpVariables[2]).get((short) 0); + // Concatenate HMAC Params + tmpVariables[0] = 0; + tmpVariables[1] = KMArray.cast(data[HMAC_SHARING_PARAMS]).length();//total number of params + tmpVariables[5] = 0; // index in scratchPad + while (tmpVariables[0] < tmpVariables[1]) { + // read HmacSharingParam + tmpVariables[2] = KMArray.cast(data[HMAC_SHARING_PARAMS]).get(tmpVariables[0]); + // get seed + tmpVariables[3] = KMHmacSharingParameters.cast(tmpVariables[2]).getSeed(); + tmpVariables[4] = KMByteBlob.cast(tmpVariables[3]).length(); + // if seed is present + if (tmpVariables[4] == HMAC_SEED_SIZE /*32*/) { + // then copy that to scratchPad + Util.arrayCopyNonAtomic( + KMByteBlob.cast(tmpVariables[3]).getBuffer(), + KMByteBlob.cast(tmpVariables[3]).getStartOff(), + scratchPad, + tmpVariables[5],// index in scratch pad + tmpVariables[4]); + tmpVariables[5] += tmpVariables[4]; // increment by seed length + } + // if nonce is present get nonce + tmpVariables[3] = KMHmacSharingParameters.cast(tmpVariables[2]).getNonce(); + tmpVariables[4] = KMByteBlob.cast(tmpVariables[3]).length(); + // if nonce is not present + if (tmpVariables[4] != HMAC_NONCE_SIZE /*32*/) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + // copy nonce to scratchPad + Util.arrayCopyNonAtomic( + KMByteBlob.cast(tmpVariables[3]).getBuffer(), + KMByteBlob.cast(tmpVariables[3]).getStartOff(), + scratchPad, + tmpVariables[5], + tmpVariables[4]); + tmpVariables[5] += tmpVariables[4]; // increment by nonce length + tmpVariables[0]++; // go to next hmac param in the vector + } + // ckdf to derive hmac key - scratch pad has the context + HMACKey key = + cryptoProvider.cmacKdf( + repository.getHmacKey(), ckdfLable , scratchPad, (short) 0, tmpVariables[5]); + tmpVariables[5] = key.getKey(scratchPad, (short) 0); + repository.initComputedHmac(scratchPad, (short) 0, tmpVariables[5]); + // Generate sharingKey verification + tmpVariables[5] = + cryptoProvider.hmacSign( + key, sharingCheck, (short) 0, (short) sharingCheck.length, scratchPad, (short) 0); + tmpVariables[1] = KMByteBlob.instance(scratchPad, (short) 0, tmpVariables[5]); + // prepare the response + tmpVariables[0] = KMArray.instance((short) 2); + KMArray.cast(tmpVariables[0]).add((short) 0, KMInteger.uint_16(KMError.OK)); + KMArray.cast(tmpVariables[0]).add((short) 1, tmpVariables[1]); + // Encode the response + bufferLength = encoder.encode(tmpVariables[0], buffer, bufferStartOffset); + sendOutgoing(apdu); } private void processUpgradeKeyCmd(APDU apdu) { + // Receive the incoming request fully from the master into buffer. + receiveIncoming(apdu); + byte[] scratchPad = apdu.getBuffer(); + tmpVariables[1] = KMArray.instance((short) 2); + tmpVariables[2] = KMKeyParameters.exp(); + KMArray.cast(tmpVariables[1]).add((short) 0, KMByteBlob.exp()); // Key Blob + KMArray.cast(tmpVariables[1]).add((short) 1, tmpVariables[2]); // Key Params + // Decode the arguments + tmpVariables[2] = decoder.decode(tmpVariables[1], buffer, bufferStartOffset, bufferLength); + data[KEY_BLOB] = KMArray.cast(tmpVariables[2]).get((short) 0); + data[KEY_PARAMETERS] = KMArray.cast(tmpVariables[2]).get((short) 1); + tmpVariables[0] = + KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.APPLICATION_ID, data[KEY_PARAMETERS]); + if (tmpVariables[0] != KMTag.INVALID_VALUE) { + data[APP_ID] = KMByteTag.cast(tmpVariables[0]).getValue(); + } + tmpVariables[0] = + KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.APPLICATION_DATA, data[KEY_PARAMETERS]); + if (tmpVariables[0] != KMTag.INVALID_VALUE) { + data[APP_DATA] = KMByteTag.cast(tmpVariables[0]).getValue(); + } + // parse existing key blob + parseEncryptedKeyBlob(scratchPad); + // validate characteristics to be upgraded. + // TODO currently only os version and os patch level are upgraded. + tmpVariables[0] = KMKeyParameters.findTag(KMType.UINT_TAG, KMType.OS_VERSION, data[HW_PARAMETERS]); + tmpVariables[0] = KMIntegerTag.cast(tmpVariables[0]).getValue(); + tmpVariables[1] = KMKeyParameters.findTag(KMType.UINT_TAG, KMType.OS_PATCH_LEVEL, data[HW_PARAMETERS]); + tmpVariables[1] = KMIntegerTag.cast(tmpVariables[1]).getValue(); + tmpVariables[2] = KMInteger.uint_32(repository.osVersion,(short)0); + tmpVariables[3] = KMInteger.uint_32(repository.osPatch,(short)0); + tmpVariables[4] = KMInteger.uint_8((byte)0); + if(tmpVariables[0] != KMType.INVALID_VALUE){ + // os version in key characteristics must be less the os version stored in javacard or the + // stored version must be zero. Then only upgrade is allowed else it is invalid argument. + if(KMInteger.compare(tmpVariables[0], tmpVariables[2]) != -1 && + KMInteger.compare(tmpVariables[2], tmpVariables[4]) != 0){ + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + } + if(tmpVariables[1] != KMType.INVALID_VALUE){ + // The key characteristics should have has os patch level < os patch level stored in javacard + // then only upgrade is allowed. + if(KMInteger.compare(tmpVariables[1], tmpVariables[3]) != -1){ + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + } +/* KMIntegerTag.getValue( + scratchPad, (short) 0, ); + if ((tmpVariables[0] != KMType.INVALID_VALUE) + && (Util.arrayCompare( + repository.osVersion, (short) 0, scratchPad, (short) 0, tmpVariables[0]) + != 0)) { + if (Util.arrayCompare(repository.osVersion, (short) 0, scratchPad, (short) 0, tmpVariables[0]) + == -1) { + // If the key characteristics has os version > current os version + Util.arrayFillNonAtomic(scratchPad, (short) 0, tmpVariables[0], (byte) 0); + // If the os version is not zero + if (Util.arrayCompare( + repository.osVersion, (short) 0, scratchPad, (short) 0, tmpVariables[0]) + != 0) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + } + } + tmpVariables[0] = + KMIntegerTag.getValue( + scratchPad, (short) 0, KMType.UINT_TAG, KMType.OS_PATCH_LEVEL, data[HW_PARAMETERS]); + if ((tmpVariables[0] != KMType.INVALID_VALUE) + && (Util.arrayCompare(repository.osPatch, (short) 0, scratchPad, (short) 0, tmpVariables[0]) + != 0)) { + if (Util.arrayCompare(repository.osPatch, (short) 0, scratchPad, (short) 0, tmpVariables[0]) + < 0) { + // If the key characteristics has os patch level > current os patch + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + }*/ + // remove Auth Tag + repository.removeAuthTag(data[AUTH_TAG]); + // copy origin + data[ORIGIN] = KMEnumTag.getValue(KMType.ORIGIN, data[HW_PARAMETERS]); + // create new key blob with current os version etc. + createEncryptedKeyBlob(scratchPad); + // persist new auth tag for rollback resistance. + repository.persistAuthTag(data[AUTH_TAG]); + // prepare the response + tmpVariables[0] = KMArray.instance((short) 2); + KMArray.cast(tmpVariables[0]).add((short) 0, KMInteger.uint_16(KMError.OK)); + KMArray.cast(tmpVariables[0]).add((short) 1, data[KEY_BLOB]); + // Encode the response + bufferLength = encoder.encode(tmpVariables[0], buffer, bufferStartOffset); + sendOutgoing(apdu); } private void processExportKeyCmd(APDU apdu) { + sendError(apdu, KMError.UNIMPLEMENTED); } private void processImportWrappedKeyCmd(APDU apdu) { + // Currently only RAW formatted import key blob are supported + if (repository.keyBlobCount > repository.MAX_BLOB_STORAGE) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + // Receive the incoming request fully from the master into buffer. + receiveIncoming(apdu); + byte[] scratchPad = apdu.getBuffer(); + tmpVariables[1] = KMArray.instance((short) 11); + // Arguments + tmpVariables[2] = KMKeyParameters.exp(); + KMArray.cast(tmpVariables[1]).add((short) 0, tmpVariables[2]); // Key Params + KMArray.cast(tmpVariables[1]).add((short) 1, KMEnum.instance(KMType.KEY_FORMAT)); // Key Format + KMArray.cast(tmpVariables[1]).add((short) 2, KMByteBlob.exp()); // Wrapped Import Key Blob + KMArray.cast(tmpVariables[1]).add((short) 3, KMByteBlob.exp()); // Auth Tag + KMArray.cast(tmpVariables[1]).add((short) 4, KMByteBlob.exp()); // IV - Nonce + KMArray.cast(tmpVariables[1]).add((short) 5, KMByteBlob.exp()); // Encrypted Transport Key + KMArray.cast(tmpVariables[1]).add((short) 6, KMByteBlob.exp()); // Wrapping Key KeyBlob + KMArray.cast(tmpVariables[1]).add((short) 7, KMByteBlob.exp()); // Masking Key + KMArray.cast(tmpVariables[1]).add((short) 8, tmpVariables[2]); // Un-wrapping Params + KMArray.cast(tmpVariables[1]).add((short) 9, KMInteger.exp()); // Password Sid + KMArray.cast(tmpVariables[1]).add((short) 10, KMInteger.exp()); // Biometric Sid + // Decode the arguments + tmpVariables[2] = decoder.decode(tmpVariables[1], buffer, bufferStartOffset, bufferLength); + tmpVariables[3] = KMArray.cast(tmpVariables[2]).get((short) 0); + // get algorithm + tmpVariables[3] = KMEnumTag.getValue(KMType.ALGORITHM, tmpVariables[3]); + if (tmpVariables[3] == KMType.INVALID_VALUE) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + tmpVariables[3] = KMEnumTag.getValue(KMType.ALGORITHM, data[KEY_PARAMETERS]); + if (tmpVariables[3] == KMType.RSA + || tmpVariables[3] == KMType.EC) { // RSA and EC not implemented + KMException.throwIt(KMError.UNIMPLEMENTED); + } + // Key format must be RAW format - X509 and PKCS8 not implemented. + tmpVariables[3] = KMArray.cast(tmpVariables[2]).get((short) 1); + tmpVariables[3] = KMEnum.cast(tmpVariables[3]).getVal(); + if (tmpVariables[3] != KMType.RAW) { + KMException.throwIt(KMError.UNIMPLEMENTED); + } + data[AUTH_DATA] = KMArray.cast(tmpVariables[2]).get((short) 3); + data[AUTH_TAG] = KMArray.cast(tmpVariables[2]).get((short) 4); + data[NONCE] = KMArray.cast(tmpVariables[2]).get((short) 5); + data[ENC_TRANSPORT_KEY] = KMArray.cast(tmpVariables[2]).get((short) 6); + data[MASKING_KEY] = KMArray.cast(tmpVariables[2]).get((short) 8); + // Step 1 - parse wrapping key blob + data[KEY_PARAMETERS] = KMArray.cast(tmpVariables[2]).get((short) 9); // wrapping key parameters + // Check for app id and app data. + data[APP_ID] = KMType.INVALID_VALUE; + data[APP_DATA] = KMType.INVALID_VALUE; + tmpVariables[3] = + KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.APPLICATION_ID, data[KEY_PARAMETERS]); + if (tmpVariables[3] != KMTag.INVALID_VALUE) { + data[APP_ID] = KMByteTag.cast(tmpVariables[3]).getValue(); + } + tmpVariables[3] = + KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.APPLICATION_DATA, data[KEY_PARAMETERS]); + if (tmpVariables[3] != KMTag.INVALID_VALUE) { + data[APP_DATA] = KMByteTag.cast(tmpVariables[3]).getValue(); + } + // wrapping key blob + data[KEY_BLOB] = KMArray.cast(tmpVariables[2]).get((short) 7); + parseEncryptedKeyBlob(scratchPad); + + // Step 2 - Decrypt the encrypted transport key + // enforce authorization for WRAP_KEY operation using RSA algorithm according to javacard caps. + if (KMEnumTag.getValue(KMType.ALGORITHM, data[HW_PARAMETERS]) != KMType.RSA) { + KMException.throwIt(KMError.INCOMPATIBLE_ALGORITHM); + } + if (!(KMEnumArrayTag.contains(KMType.DIGEST, KMType.SHA2_256, data[HW_PARAMETERS]))) { + KMException.throwIt(KMError.INCOMPATIBLE_DIGEST); + } + if (!(KMEnumArrayTag.contains(KMType.PADDING, KMType.RSA_OAEP, data[HW_PARAMETERS]))) { + KMException.throwIt(KMError.INCOMPATIBLE_PADDING_MODE); + } + KMCipher cipher = + cryptoProvider.createRsaDecipher( + KMCipher.PAD_PKCS1_OAEP_SHA256, + KMByteBlob.cast(data[SECRET]).getBuffer(), + KMByteBlob.cast(data[SECRET]).getStartOff(), + KMByteBlob.cast(data[SECRET]).length(), + KMByteBlob.cast(data[PUB_KEY]).getBuffer(), + KMByteBlob.cast(data[PUB_KEY]).getStartOff(), + KMByteBlob.cast(data[PUB_KEY]).length()); + // Decrypt the transport key + tmpVariables[3] = + cipher.doFinal( + KMByteBlob.cast(data[ENC_TRANSPORT_KEY]).getBuffer(), + KMByteBlob.cast(data[ENC_TRANSPORT_KEY]).getStartOff(), + KMByteBlob.cast(data[ENC_TRANSPORT_KEY]).length(), + scratchPad, + (short) 0); + data[SECRET] = KMByteBlob.instance(scratchPad, (short) 0, tmpVariables[3]); + cryptoProvider.delete(cipher); + + // Step 3 - XOR with masking key + tmpVariables[4] = KMByteBlob.cast(data[MASKING_KEY]).length(); + if (tmpVariables[3] != tmpVariables[4]) { + KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH); + } + tmpVariables[3] = 0; // index in scratchPad + byte[] buf = KMByteBlob.cast(MASKING_KEY).getBuffer(); + tmpVariables[5] = KMByteBlob.cast(MASKING_KEY).getStartOff(); + while (tmpVariables[3] < tmpVariables[4]) { + scratchPad[tmpVariables[3]] = + (byte) (scratchPad[tmpVariables[3]] ^ buf[(short) (tmpVariables[3] + tmpVariables[5])]); + scratchPad[3]++; + } + data[SECRET] = KMByteBlob.instance(scratchPad, (short) 0, tmpVariables[3]); + + // Step 4 - AES-GCM decrypt + data[IMPORTED_KEY_BLOB] = KMArray.cast(tmpVariables[2]).get((short) 2); + data[AUTH_DATA] = KMArray.cast(tmpVariables[2]).get((short) 3); + data[AUTH_TAG] = KMArray.cast(tmpVariables[2]).get((short) 4); + data[NONCE] = KMArray.cast(tmpVariables[2]).get((short) 5); + data[ENC_TRANSPORT_KEY] = KMArray.cast(tmpVariables[2]).get((short) 6); + data[MASKING_KEY] = KMArray.cast(tmpVariables[2]).get((short) 8); + AESKey key = + cryptoProvider.createAESKey( + KMByteBlob.cast(data[SECRET]).getBuffer(), + KMByteBlob.cast(data[SECRET]).getStartOff(), + KMByteBlob.cast(data[SECRET]).length()); + boolean verification = + cryptoProvider.aesGCMDecrypt( + key, + KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).getBuffer(), + KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).getStartOff(), + KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).length(), + scratchPad, + (short) 0, + KMByteBlob.cast(data[NONCE]).getBuffer(), + KMByteBlob.cast(data[NONCE]).getStartOff(), + KMByteBlob.cast(data[NONCE]).length(), + KMByteBlob.cast(data[AUTH_DATA]).getBuffer(), + KMByteBlob.cast(data[AUTH_DATA]).getStartOff(), + KMByteBlob.cast(data[AUTH_DATA]).length(), + KMByteBlob.cast(data[AUTH_TAG]).getBuffer(), + KMByteBlob.cast(data[AUTH_TAG]).getStartOff(), + KMByteBlob.cast(data[AUTH_TAG]).length()); + if (verification == false) { + KMException.throwIt(KMError.IMPORTED_KEY_VERIFICATION_FAILED); + } + cryptoProvider.delete(key); + + // Step 5 - Import Decrypted Key. + data[ORIGIN] = KMType.SECURELY_IMPORTED; + data[KEY_PARAMETERS] = KMArray.cast(tmpVariables[2]).get((short) 0); + importKey(apdu, scratchPad); } private void processAttestKeyCmd(APDU apdu) {} @@ -575,19 +909,1183 @@ private void processVerifyAuthorizationCmd(APDU apdu) { sendError(apdu, KMError.UNIMPLEMENTED); } - private void processAbortOperationCmd(APDU apdu) {} + private void processAbortOperationCmd(APDU apdu) { + receiveIncoming(apdu); + tmpVariables[1] = KMArray.instance((short) 1); + KMArray.cast(tmpVariables[1]).add((short) 0, KMInteger.exp()); + tmpVariables[2] = decoder.decode(tmpVariables[1], buffer, bufferStartOffset, bufferLength); + data[OP_HANDLE] = KMArray.cast(tmpVariables[2]).get((short) 0); + tmpVariables[1] = KMInteger.cast(data[OP_HANDLE]).getShort(); + KMOperationState op = repository.findOperation(tmpVariables[1]); + if (op == null) { + KMException.throwIt(KMError.INVALID_OPERATION_HANDLE); + } + repository.releaseOperation(op); + sendError(apdu, KMError.OK); + } private void processFinishOperationCmd(APDU apdu) { // TODO AES GCM + receiveIncoming(apdu); + byte[] scratchPad = apdu.getBuffer(); + tmpVariables[1] = KMArray.instance((short) 6); + // Arguments + tmpVariables[2] = KMKeyParameters.exp(); + KMArray.cast(tmpVariables[1]).add((short) 0, KMInteger.exp()); + KMArray.cast(tmpVariables[1]).add((short) 1, tmpVariables[2]); + KMArray.cast(tmpVariables[1]).add((short) 2, KMByteBlob.exp()); + KMArray.cast(tmpVariables[1]).add((short) 3, KMByteBlob.exp()); + tmpVariables[3] = KMHardwareAuthToken.exp(); + KMArray.cast(tmpVariables[1]).add((short) 4, tmpVariables[3]); + tmpVariables[4] = KMVerificationToken.exp(); + KMArray.cast(tmpVariables[1]).add((short) 5, tmpVariables[4]); + // Decode the arguments + tmpVariables[2] = decoder.decode(tmpVariables[1], buffer, bufferStartOffset, bufferLength); + data[OP_HANDLE] = KMArray.cast(tmpVariables[2]).get((short) 0); + data[KEY_PARAMETERS] = KMArray.cast(tmpVariables[2]).get((short) 1); + data[INPUT_DATA] = KMArray.cast(tmpVariables[2]).get((short) 2); + data[SIGNATURE] = KMArray.cast(tmpVariables[2]).get((short) 3); + data[HW_TOKEN] = KMArray.cast(tmpVariables[2]).get((short) 4); + data[VERIFICATION_TOKEN] = KMArray.cast(tmpVariables[2]).get((short) 5); + // Check Operation Handle + tmpVariables[1] = KMInteger.cast(data[OP_HANDLE]).getShort(); + KMOperationState op = repository.findOperation(tmpVariables[1]); + if (op == null) { + KMException.throwIt(KMError.INVALID_OPERATION_HANDLE); + } + //Authorize the finish operation + authorizeUpdateFinishOperation(op, scratchPad); + //Finish trusted Confirmation operation + switch(op.getPurpose()){ + case KMType.SIGN: + finishTrustedConfirmationOperation(op); + case KMType.VERIFY: + finishSigningVerifyingOperation(op,scratchPad); + break; + case KMType.ENCRYPT: + finishEncryptOperation(op, scratchPad); + break; + case KMType.DECRYPT: + finishDecryptOperation(op,scratchPad); + break; + } + // Remove the operation handle + repository.releaseOperation(op); + // make response + tmpVariables[1] = KMArray.instance((short) 0); + tmpVariables[1] = KMKeyParameters.instance(tmpVariables[1]); + tmpVariables[2] = KMArray.instance((short) 3); + if (data[OUTPUT_DATA] == KMType.INVALID_VALUE) { + data[OUTPUT_DATA] = KMByteBlob.instance((short) 0); + } + KMArray.cast(tmpVariables[2]).add((short) 0, KMInteger.uint_16(KMError.OK)); + KMArray.cast(tmpVariables[2]).add((short) 1, tmpVariables[1]); + KMArray.cast(tmpVariables[2]).add((short) 2, data[OUTPUT_DATA]); + // Encode the response + bufferLength = encoder.encode(tmpVariables[2], buffer, bufferStartOffset); + sendOutgoing(apdu); + } + private void finishEncryptOperation(KMOperationState op, byte[] scratchPad) { + short len = KMByteBlob.cast(data[INPUT_DATA]).length(); + switch(op.getAlgorithm()){ + // Only supported for testing purpose + // TODO remove this later on + case KMType.RSA: + data[OUTPUT_DATA] = KMByteBlob.instance((short)256); + // Fill the scratch pad with zero + Util.arrayFillNonAtomic(scratchPad,(short)0, (short)256, (byte)0); + if(op.getPadding() == KMType.PADDING_NONE){ + // Length cannot be greater then key size - we restrict this to 255 that ensures + // that it will never be greater then numerical value of the key also. + if(len >= 256) KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + /* // If Length is same as key size then + // compare the data with key value - date should be less then key value. + if(len == 255) { + // TODO the assumption is that private key exponent value is considered here. + tmpVariables[0]= op.getKey(scratchPad,(short)0); + tmpVariables[0] = Util.arrayCompare( + KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), + KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), + scratchPad, (short)0, tmpVariables[0]); + if(tmpVariables[0] >= 0){ + KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + } + } +*/ // copy input data to scratchpad. + //TODO the current jacrdsim implementation requires 255 bytes when using encryption with no pad + Util.arrayCopyNonAtomic( + KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), + KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), + scratchPad, (short)(255 - len),len); + len = (short)255; + }else{ + //copy input data to scratchpad. + Util.arrayCopyNonAtomic( + KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), + KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), + scratchPad, (short)0,len); + } + len = op.getCipher().doFinal( + scratchPad, (short)0,len, KMByteBlob.cast(data[OUTPUT_DATA]).getBuffer(), + KMByteBlob.cast(data[OUTPUT_DATA]).getStartOff()); + break; + case KMType.AES: + if(op.getBlockMode() == KMType.GCM){ + finishAesGCMOperation(op, scratchPad); + return; + } + case KMType.DES: + if(op.getAlgorithm() == KMType.AES){ + tmpVariables[0] = KMCipher.AES_BLOCK_SIZE; + }else{ + tmpVariables[0] = KMCipher.DES_BLOCK_SIZE; + } + //If no padding then data length must be block aligned + if (op.getPadding() == KMType.PADDING_NONE && ((short)(len % tmpVariables[0]) != 0)){ + KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + } + //If padding i.e. pkcs7 then add padding to right + if(op.getPadding() == KMType.PKCS7){ + // padding bytes + if(len % tmpVariables[0] == 0) tmpVariables[1] = tmpVariables[0]; + else tmpVariables[1] = (short)(tmpVariables[0] - (len % tmpVariables[0])); + // final len with padding + len = (short)(len+tmpVariables[1]); + // intermediate buffer to copy input data+padding + tmpVariables[2] = KMByteBlob.instance(len); + // fill in the padding + Util.arrayFillNonAtomic( + KMByteBlob.cast(tmpVariables[2]).getBuffer(), + KMByteBlob.cast(tmpVariables[2]).getStartOff(), + KMByteBlob.cast(tmpVariables[2]).length(), + (byte)tmpVariables[1]); + //copy the input data + Util.arrayCopyNonAtomic( + KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), + KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), + KMByteBlob.cast(tmpVariables[2]).getBuffer(), + KMByteBlob.cast(tmpVariables[2]).getStartOff(), + KMByteBlob.cast(data[INPUT_DATA]).length()); + data[INPUT_DATA] = tmpVariables[2]; + } + data[OUTPUT_DATA] = KMByteBlob.instance(len); + len = op.getCipher().doFinal( + KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), + KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), + KMByteBlob.cast(data[INPUT_DATA]).length(), + KMByteBlob.cast(data[OUTPUT_DATA]).getBuffer(), + KMByteBlob.cast(data[OUTPUT_DATA]).getStartOff()); + break; + } } + private void finishDecryptOperation(KMOperationState op, byte[] scratchPad) { + short len = KMByteBlob.cast(data[INPUT_DATA]).length(); + switch(op.getAlgorithm()){ + // Only supported for testing purpose + // TODO remove this later on + case KMType.RSA: + // Fill the scratch pad with zero + Util.arrayFillNonAtomic(scratchPad,(short)0, (short)256, (byte)0); + if(op.getPadding() == KMType.PADDING_NONE && + len != 256) KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + len = op.getCipher().doFinal( + KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), + KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), + len, scratchPad, + (short)0); + data[OUTPUT_DATA] = KMByteBlob.instance(scratchPad,(short)0, len); + break; + case KMType.AES: + if(op.getBlockMode() == KMType.GCM){ + finishAesGCMOperation(op, scratchPad); + return; + } + case KMType.DES: + if(op.getAlgorithm() == KMType.AES){ + tmpVariables[0] = KMCipher.AES_BLOCK_SIZE; + }else{ + tmpVariables[0] = KMCipher.DES_BLOCK_SIZE; + } + if((op.getBlockMode() == KMType.CBC || op.getBlockMode() == KMType.ECB)&& len > 0 && + (len%tmpVariables[0]) != 0)KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + tmpVariables[1] = repository.alloc(len); + byte[] heap = repository.getHeap(); + len = op.getCipher().doFinal( + KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), + KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), len,heap, + tmpVariables[1]); + //remove padding bytes if pkcs7 + if(op.getPadding() == KMType.PKCS7 && len >0) len = (short)(len - heap[(short)(tmpVariables[1]+len -1)]); + //If padding i.e. pkcs7 then add padding to right + data[OUTPUT_DATA] = KMByteBlob.instance(heap, tmpVariables[1], len); + break; + } + } + + private void finishAesGCMOperation(KMOperationState op, byte[] scratchPad) { + } + + private void finishSigningVerifyingOperation(KMOperationState op, byte[]scratchPad) { + short len = KMByteBlob.cast(data[INPUT_DATA]).length(); + switch(op.getAlgorithm()){ + case KMType.RSA: + data[OUTPUT_DATA] = KMByteBlob.instance((short)256); + // No digest and no padding - This case is not supported in javacard api + // TODO confirm whether this case should be handled as it cannot be supported or tested + if(op.getDigest() == KMType.DIGEST_NONE && op.getPadding() == KMType.PADDING_NONE){ + if(op.getPurpose() == KMType.SIGN){ + // Length cannot be greater then key size + if(len > 256) KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + // If Length is same as key size then + // compare the data with key value - date should be less then key value. + if(len == 256) { + // TODO the assumption is that private key exponent value is considered here. + tmpVariables[0]= op.getKey(scratchPad,(short)0); + tmpVariables[0] = Util.arrayCompare( + KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), + KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), + scratchPad, (short)0, tmpVariables[0]); + if(tmpVariables[0] >= 0){ + KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + } + } + }else{//Verify + if(len != 256) KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + } + // Fill the scratch pad with zero + Util.arrayFillNonAtomic(scratchPad,(short)0, (short)256, (byte)0); + // Everything is fine so copy input data to scratchpad. + Util.arrayCopyNonAtomic( + KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), + KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), + scratchPad, (short)(256 - len),len); + len = (short)256; + } + // If PKCS1 padding and no digest - then 0x01||0x00||PS||0x00 on left such that PS = 8 bytes + // TODO confirm whether this case should be handled as it cannot be supported or tested + if (op.getDigest() == KMType.DIGEST_NONE && op.getPadding() == KMType.RSA_PKCS1_1_5_SIGN) { + // Length cannot be greater then 256 -11 = 245 bytes + if(len > 245){ + KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + } + // Fill the scratch pad with zero + Util.arrayFillNonAtomic(scratchPad,(short)0, (short)256, (byte)0); + scratchPad[0] = 0x00; + scratchPad[1] = 0x01; + cryptoProvider.newRandomNumber(scratchPad, (short)2, (short)8); + scratchPad[10] = 0x00; + //copy the rest of the data on scratch pad. + Util.arrayCopyNonAtomic( + KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), + KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), + scratchPad, (short)11,len); + len += (short)11; + } + // Normal case with PKCS1 or PSS padding and with Digest SHA256 + if(op.getDigest()==KMType.SHA2_256 && + (op.getPadding() == KMType.RSA_PKCS1_1_5_SIGN ||op.getPadding() == KMType.RSA_PSS)){ + // Fill the scratch pad with zero + Util.arrayFillNonAtomic(scratchPad,(short)0, (short)256, (byte)0); + // Copy the data on the scratch pad. + Util.arrayCopyNonAtomic( + KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), + KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), + scratchPad, (short)0,len); + } + if(op.getPurpose() == KMType.SIGN){ + // len of signature will be 256 bytes + len = op.getSignerVerifier().sign(scratchPad,(short)0,len, + KMByteBlob.cast(data[OUTPUT_DATA]).getBuffer(), + KMByteBlob.cast(data[OUTPUT_DATA]).getStartOff()); + }else{ + if(!op.getSignerVerifier().verify(scratchPad,(short)0,len, + KMByteBlob.cast(data[SIGNATURE]).getBuffer(), + KMByteBlob.cast(data[SIGNATURE]).getStartOff(), + KMByteBlob.cast(data[SIGNATURE]).length())){ + KMException.throwIt(KMError.VERIFICATION_FAILED); + } + } + break; + case KMType.EC: + // Fill the scratch pad with zero + Util.arrayFillNonAtomic(scratchPad,(short)0, (short)256, (byte)0); + // If DIGEST NONE then truncate the data to 32 bytes. + // TODO Confirm whether this case needs to be supported as javacard does not support. + if(op.getDigest() == KMType.DIGEST_NONE || len > 32){ + len = 32; + } + if(op.getPurpose() == KMType.SIGN){ + // len of signature will be 512 bits i.e. 64 bytes + len = op.getSignerVerifier().sign( + KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), + KMByteBlob.cast(data[INPUT_DATA]).getStartOff(),len, + scratchPad,(short)0); + data[OUTPUT_DATA] = KMByteBlob.instance(scratchPad,(short)0, len); + }else{ + if(!op.getSignerVerifier().verify( + KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), + KMByteBlob.cast(data[INPUT_DATA]).getStartOff(),len, + KMByteBlob.cast(data[SIGNATURE]).getBuffer(), + KMByteBlob.cast(data[SIGNATURE]).getStartOff(), + KMByteBlob.cast(data[SIGNATURE]).length())){ + KMException.throwIt(KMError.VERIFICATION_FAILED); + } + } + break; + case KMType.HMAC: + Util.arrayFillNonAtomic(scratchPad,(short)0, (short)256, (byte)0); + // digest is always present. + if(op.getPurpose() == KMType.SIGN){ + // len of signature will always be 32 bytes. + len = op.getSignerVerifier().sign(KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), + KMByteBlob.cast(data[INPUT_DATA]).getStartOff(),len,scratchPad, + (short)0); + data[OUTPUT_DATA] = KMByteBlob.instance(scratchPad,(short)0, len); + }else{ + if(!op.getSignerVerifier().verify( + KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), + KMByteBlob.cast(data[INPUT_DATA]).getStartOff(),len, + KMByteBlob.cast(data[SIGNATURE]).getBuffer(), + KMByteBlob.cast(data[SIGNATURE]).getStartOff(), + KMByteBlob.cast(data[SIGNATURE]).length())){ + KMException.throwIt(KMError.VERIFICATION_FAILED); + } + } break; + default:// This is should never happen + KMException.throwIt(KMError.OPERATION_CANCELLED); + break; + } + } + + private void finishTrustedConfirmationOperation(KMOperationState op) { + // Perform trusted confirmation if required + if (op.isTrustedConfirmationRequired()) { + tmpVariables[0] = KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.CONFIRMATION_TOKEN, data[KEY_PARAMETERS]); + if(tmpVariables[0] == KMType.INVALID_VALUE){ + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + tmpVariables[0] = KMByteTag.cast(tmpVariables[0]).getValue(); + boolean verified = op.getTrustedConfirmationSigner() + .verify( + KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), + KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), + KMByteBlob.cast(data[INPUT_DATA]).length(), + KMByteBlob.cast(tmpVariables[0]).getBuffer(), + KMByteBlob.cast(tmpVariables[0]).getStartOff(), + KMByteBlob.cast(tmpVariables[0]).length()); + /* + if(tmpVariables[1] != KMByteBlob.cast(tmpVariables[0]).length() ){ + KMException.throwIt(KMError.VERIFICATION_FAILED); + } + tmpVariables[0]=Util.arrayCompare(scratchPad,(short)0, + KMByteBlob.cast(tmpVariables[0]).getBuffer(), + KMByteBlob.cast(tmpVariables[0]).getStartOff(), + tmpVariables[1]);*/ + if(!verified){ + KMException.throwIt(KMError.VERIFICATION_FAILED); + } + } + + } + + private void authorizeUpdateFinishOperation(KMOperationState op, byte[] scratchPad) { + // User Authentication + if (op.isSecureUserIdReqd() && !op.isAuthTimeoutValidated()) { + validateVerificationToken(op, data[VERIFICATION_TOKEN], scratchPad); + tmpVariables[0] = KMInteger.uint_64(op.getAuthTime(), (short) 0); + tmpVariables[2] = KMVerificationToken.cast(data[VERIFICATION_TOKEN]).getTimestamp(); + if (tmpVariables[2] == KMType.INVALID_VALUE) { + KMException.throwIt(KMError.VERIFICATION_FAILED); + } + if (KMInteger.compare(tmpVariables[0], tmpVariables[2]) < 0) { + KMException.throwIt(KMError.KEY_USER_NOT_AUTHENTICATED); + } + op.setAuthTimeoutValidated(true); + } else if(op.isAuthPerOperationReqd()){ // Auth per operation + tmpVariables[0] = KMHardwareAuthToken.cast(data[HW_TOKEN]).getChallenge(); + if (KMInteger.compare(data[OP_HANDLE], tmpVariables[0]) != 0) { + KMException.throwIt(KMError.KEY_USER_NOT_AUTHENTICATED); + } + authorizeHwAuthToken(scratchPad); + } + } + + private void validateVerificationToken(KMOperationState op, short verToken, byte[] scratchPad) { + // CBOR Encoding is always big endian and Java is big endian + short ptr = KMVerificationToken.cast(verToken).getMac(); + short len = 0; + // If mac length is zero then token is empty. + if (KMByteBlob.cast(ptr).length() == 0) { + return; + } + // validate operation handle. + ptr = KMVerificationToken.cast(verToken).getChallenge(); + if(op.getHandle() != KMInteger.cast(ptr).getShort()){ + KMException.throwIt(KMError.VERIFICATION_FAILED); + } + // concatenation length will be 37 + length of verified parameters list. + short params = KMVerificationToken.cast(verToken).getParametersVerified(); + Util.arrayFillNonAtomic(scratchPad, (short) 0, + (short) (37+KMByteBlob.cast(params).length()), (byte) 0); + // Add "Auth Verification" - 17 bytes. + Util.arrayCopy(authVerification,(short)0, scratchPad, (short)0, (short)authVerification.length); + len = (short)authVerification.length; + // concatenate challenge - 8 bytes + ptr = KMVerificationToken.cast(verToken).getChallenge(); + KMInteger.cast(ptr) + .value(scratchPad, (short) (len + (short) (8 - KMInteger.cast(ptr).length()))); + len += 8; + // concatenate timestamp -8 bytes + ptr = KMVerificationToken.cast(verToken).getTimestamp(); + KMInteger.cast(ptr) + .value(scratchPad, (short) (len + (short) (8 - KMInteger.cast(ptr).length()))); + len += 8; + // concatenate security level - 4 bytes + ptr = KMVerificationToken.cast(verToken).getSecurityLevel(); + scratchPad[(short) (len + 3)] = KMEnum.cast(ptr).getVal(); + len += 4; + // concatenate Parameters verified - blob of encoded data. + ptr = KMVerificationToken.cast(verToken).getParametersVerified(); + len += KMByteBlob.cast(ptr).getValues(scratchPad, (short)0); + len += 4; + // hmac the data + HMACKey key = + cryptoProvider.createHMACKey( + repository.getComputedHmacKey(), + (short) 0, + (short) repository.getComputedHmacKey().length); + ptr = KMVerificationToken.cast(verToken).getMac(); + boolean verified = + cryptoProvider.hmacVerify(key, scratchPad, (short) 0, len, + KMByteBlob.cast(ptr).getBuffer(), + KMByteBlob.cast(ptr).getStartOff(), + KMByteBlob.cast(ptr).length()); + if(!verified){ + KMException.throwIt(KMError.VERIFICATION_FAILED); + } + /* + + // Compare mac. + ptr = KMVerificationToken.cast(verToken).getMac(); + if (macLen != KMByteBlob.cast(ptr).length()) { + KMException.throwIt(KMError.INVALID_MAC_LENGTH); + } + if (Util.arrayCompare( + scratchPad, (short) (len+1), + KMByteBlob.cast(ptr).getBuffer(), KMByteBlob.cast(ptr).getStartOff(), macLen) != 0) { + KMException.throwIt(KMError.VERIFICATION_FAILED); + } + */ + + } private void processUpdateOperationCmd(APDU apdu) { + // TODO Add Support for AES-GCM + receiveIncoming(apdu); + byte[] scratchPad = apdu.getBuffer(); + tmpVariables[1] = KMArray.instance((short) 5); + // Arguments + tmpVariables[2] = KMKeyParameters.exp(); + KMArray.cast(tmpVariables[1]).add((short) 0, KMInteger.exp()); + KMArray.cast(tmpVariables[1]).add((short) 1, tmpVariables[2]); + KMArray.cast(tmpVariables[1]).add((short) 2, KMByteBlob.exp()); + tmpVariables[3] = KMHardwareAuthToken.exp(); + KMArray.cast(tmpVariables[1]).add((short) 3, tmpVariables[3]); + tmpVariables[4] = KMVerificationToken.exp(); + KMArray.cast(tmpVariables[1]).add((short) 4, tmpVariables[4]); + // Decode the arguments + tmpVariables[2] = decoder.decode(tmpVariables[1], buffer, bufferStartOffset, bufferLength); + data[OP_HANDLE] = KMArray.cast(tmpVariables[2]).get((short) 0); + data[KEY_PARAMETERS] = KMArray.cast(tmpVariables[2]).get((short) 1); + data[INPUT_DATA] = KMArray.cast(tmpVariables[2]).get((short) 2); + data[HW_TOKEN] = KMArray.cast(tmpVariables[2]).get((short) 3); + data[VERIFICATION_TOKEN] = KMArray.cast(tmpVariables[2]).get((short) 4); + // Check Operation Handle and get op state + // Check Operation Handle + tmpVariables[1] = KMInteger.cast(data[OP_HANDLE]).getShort(); + KMOperationState op = repository.findOperation(tmpVariables[1]); + if (op == null) { + KMException.throwIt(KMError.INVALID_OPERATION_HANDLE); + } + // authorize the update operation + authorizeUpdateFinishOperation(op, scratchPad); + // If signing without digest then do length validation checks + tmpVariables[0] = KMByteBlob.cast(data[INPUT_DATA]).length(); + if (op.getPurpose() == KMType.SIGN || op.getPurpose() == KMType.VERIFY ) { + // update the data. + op.getSignerVerifier() + .update( + KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), + KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), + KMByteBlob.cast(data[INPUT_DATA]).length()); + // update trusted confirmation operation + updateTrustedConfirmationOperation(op); + data[OUTPUT_DATA] = KMType.INVALID_VALUE; + } + if (op.getPurpose() == KMType.ENCRYPT || op.getPurpose() == KMType.DECRYPT){ + // TODO Update for encrypt/decrypt using RSA will not be supported because to do this op state + // will have to buffer the data - so reject the update if it is rsa algorithm. + if(op.getAlgorithm() == KMCipher.CIPHER_RSA) { + KMException.throwIt(KMError.OPERATION_CANCELLED); + } + if (op.getAlgorithm() == KMType.AES) { + // input data must be block aligned. + // 128 bit block size - HAL must send block aligned data + if (tmpVariables[0] % AES_BLOCK_SIZE != 0 || tmpVariables[0] <=0) { + KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + } + } + if (op.getAlgorithm() == KMType.DES) { + // 64 bit block size - HAL must send block aligned data + if (tmpVariables[0] % DES_BLOCK_SIZE != 0) { + KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + } + } + // Otherwise just update the data. + tmpVariables[0] = + op.getCipher() + .update( + KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), + KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), + KMByteBlob.cast(data[INPUT_DATA]).length(), + scratchPad, + (short) 0); + data[OUTPUT_DATA] = KMByteBlob.instance(scratchPad, (short) 0, tmpVariables[0]); + } + // make response + tmpVariables[1] = KMArray.instance((short) 0); + tmpVariables[1] = KMKeyParameters.instance(tmpVariables[1]); + tmpVariables[2] = KMArray.instance((short) 4); + if (data[OUTPUT_DATA] == KMType.INVALID_VALUE) { + data[OUTPUT_DATA] = KMByteBlob.instance((short) 0); + } + KMArray.cast(tmpVariables[2]).add((short) 0, KMInteger.uint_16(KMError.OK)); + KMArray.cast(tmpVariables[2]).add((short) 1, KMInteger.uint_16(tmpVariables[0])); + KMArray.cast(tmpVariables[2]).add((short) 2, tmpVariables[1]); + KMArray.cast(tmpVariables[2]).add((short) 3, data[OUTPUT_DATA]); + // Encode the response + bufferLength = encoder.encode(tmpVariables[2], buffer, bufferStartOffset); + sendOutgoing(apdu); + } + + private void updateTrustedConfirmationOperation(KMOperationState op) { + if (op.isTrustedConfirmationRequired()) { + op.getTrustedConfirmationSigner() + .update( + KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), + KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), + KMByteBlob.cast(data[INPUT_DATA]).length()); + } + } + + private void processBeginOperationCmd(APDU apdu) { + // Receive the incoming request fully from the master into buffer. + receiveIncoming(apdu); + byte[] scratchPad = apdu.getBuffer(); + short args = KMType.INVALID_VALUE; + tmpVariables[1] = KMArray.instance((short) 4); + // Arguments + tmpVariables[2] = KMKeyParameters.exp(); + KMArray.cast(tmpVariables[1]).add((short) 0, KMEnum.instance(KMType.PURPOSE)); + KMArray.cast(tmpVariables[1]).add((short) 1, KMByteBlob.exp()); + KMArray.cast(tmpVariables[1]).add((short) 2, tmpVariables[2]); + tmpVariables[3] = KMHardwareAuthToken.exp(); + KMArray.cast(tmpVariables[1]).add((short) 3, tmpVariables[3]); + // Decode the arguments + args = decoder.decode(tmpVariables[1], buffer, bufferStartOffset, bufferLength); + data[KEY_PARAMETERS] = KMArray.cast(args).get((short) 2); + data[KEY_BLOB] = KMArray.cast(args).get((short) 1); + // Check for app id and app data. + data[APP_ID] = KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.APPLICATION_ID, data[KEY_PARAMETERS]); + data[APP_DATA] = KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.APPLICATION_DATA, data[KEY_PARAMETERS]); + if (data[APP_ID] != KMTag.INVALID_VALUE) { + data[APP_ID] = KMByteTag.cast(data[APP_ID]).getValue(); + } + if (data[APP_DATA] != KMTag.INVALID_VALUE) { + data[APP_DATA] = KMByteTag.cast(data[APP_DATA]).getValue(); + } + // Parse the encrypted blob and decrypt it. + parseEncryptedKeyBlob(scratchPad); + // Authorize the begin operation and reserve op - data[OP_HANDLE] will have the handle. + // It will also set data[IV] field if required. + tmpVariables[0] = KMArray.cast(args).get((short) 0); + tmpVariables[0] = KMEnum.cast(tmpVariables[0]).getVal(); + data[HW_TOKEN] = KMArray.cast(args).get((short) 3); + KMOperationState op = repository.reserveOperation(); + if(op == null) KMException.throwIt(KMError.TOO_MANY_OPERATIONS); + data[OP_HANDLE] = op.getHandle(); + op.setPurpose(tmpVariables[0]); + authorizeAndBeginOperation(op, scratchPad); + switch (op.getPurpose()){ + case KMType.SIGN: + beginTrustedConfirmationOperation(op); + case KMType.VERIFY: + beginSignVerifyOperation(op); + break; + case KMType.ENCRYPT: + case KMType.DECRYPT: + beginCipherOperation(op); + break; + default: + KMException.throwIt(KMError.UNIMPLEMENTED); + break; + } + // If the data[IV] is required to be returned. + if (data[IV] != KMType.INVALID_VALUE) { + // TODO confirm why this is needed + tmpVariables[2] = KMArray.instance((short) 1); + KMArray.cast(tmpVariables[2]).add((short) 0, KMByteTag.instance(KMType.NONCE, data[IV])); + } else { + tmpVariables[2] = KMArray.instance((short) 0); + } + tmpVariables[1] = KMKeyParameters.instance(tmpVariables[2]); + tmpVariables[0] = KMArray.instance((short) 3); + KMArray.cast(tmpVariables[0]).add((short) 0, KMInteger.uint_16(KMError.OK)); + KMArray.cast(tmpVariables[0]).add((short) 1, tmpVariables[1]); + KMArray.cast(tmpVariables[0]).add((short) 2, data[OP_HANDLE]); + // Encode the response + bufferLength = encoder.encode(tmpVariables[0], buffer, bufferStartOffset); + sendOutgoing(apdu); + } + + private void beginTrustedConfirmationOperation(KMOperationState op) { + // Check for trusted confirmation - if required then set the signer in op state. + if (KMKeyParameters.findTag( + KMType.BOOL_TAG, KMType.TRUSTED_CONFIRMATION_REQUIRED, data[HW_PARAMETERS]) != KMType.INVALID_VALUE) { + // get operation + // get the hmac key + if (repository.getComputedHmacKey() == null) { + KMException.throwIt(KMError.OPERATION_CANCELLED); + } + // set the Hmac signer + op.setTrustedConfirmationSigner( + cryptoProvider.createHmacSignerVerifier(Signature.MODE_VERIFY, + MessageDigest.ALG_SHA_256, + repository.getComputedHmacKey(), + (short) 0, (short) repository.getComputedHmacKey().length)); + op.getTrustedConfirmationSigner().update(confirmationToken,(short)0,(short)confirmationToken.length); + } + } + + private void authorizeAlgorithm(KMOperationState op){ + short alg = KMEnumTag.getValue(KMType.ALGORITHM, data[HW_PARAMETERS]); + if(alg == KMType.INVALID_VALUE){ + KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM); + } + op.setAlgorithm((byte)alg); + } + private void authorizePurpose(KMOperationState op){ + if(!KMEnumArrayTag.contains(KMType.PURPOSE,op.getPurpose(),data[HW_PARAMETERS])){ + KMException.throwIt(KMError.UNSUPPORTED_PURPOSE); + } + } + + private void authorizeDigest(KMOperationState op){ + short digests = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.DIGEST, data[HW_PARAMETERS]); + op.setDigest(KMType.DIGEST_NONE); + short param = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.DIGEST, data[KEY_PARAMETERS]); + if(param != KMType.INVALID_VALUE){ + if(KMEnumArrayTag.cast(param).length() != 1) KMException.throwIt(KMError.INVALID_ARGUMENT); + param = KMEnumArrayTag.cast(param).get((short)0); + if(!KMEnumArrayTag.cast(digests).contains(param)) KMException.throwIt(KMError.UNSUPPORTED_DIGEST); + op.setDigest((byte)param); + } + switch(op.getAlgorithm()){ + case KMType.EC: + case KMType.HMAC: + if(param == KMType.INVALID_VALUE) KMException.throwIt(KMError.INVALID_ARGUMENT); + break; + default: + break; + } + } + private void authorizePadding(KMOperationState op){ + short paddings = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.PADDING, data[HW_PARAMETERS]); + op.setPadding(KMType.PADDING_NONE); + short param = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.PADDING, data[KEY_PARAMETERS]); + if(param != KMType.INVALID_VALUE){ + if(KMEnumArrayTag.cast(param).length() != 1) KMException.throwIt(KMError.INVALID_ARGUMENT); + param = KMEnumArrayTag.cast(param).get((short)0); + if(!KMEnumArrayTag.cast(paddings).contains(param)) KMException.throwIt(KMError.UNSUPPORTED_PADDING_MODE); + } + switch (op.getAlgorithm()){ + case KMType.RSA: + if(param == KMType.INVALID_VALUE) KMException.throwIt(KMError.INVALID_ARGUMENT); + if((op.getPurpose() == KMType.SIGN || op.getPurpose() == KMType.VERIFY)&& + param != KMType.PADDING_NONE && + param != KMType.RSA_PSS && + param != KMType.RSA_PKCS1_1_5_SIGN) KMException.throwIt(KMError.INCOMPATIBLE_PADDING_MODE); + if((op.getPurpose() == KMType.ENCRYPT || op.getPurpose() == KMType.DECRYPT) && + param != KMType.PADDING_NONE && + param != KMType.RSA_OAEP && + param != KMType.RSA_PKCS1_1_5_ENCRYPT) KMException.throwIt(KMError.INCOMPATIBLE_PADDING_MODE); + if (param == KMType.PADDING_NONE && op.getDigest() != KMType.DIGEST_NONE) { + KMException.throwIt(KMError.INCOMPATIBLE_DIGEST); + } + if ((param == KMType.RSA_OAEP || param == KMType.RSA_PSS) + && op.getDigest() == KMType.DIGEST_NONE) { + KMException.throwIt(KMError.INCOMPATIBLE_DIGEST); + } + op.setPadding((byte)param); + break; + case KMType.DES: + case KMType.AES: + if(param == KMType.INVALID_VALUE) KMException.throwIt(KMError.INVALID_ARGUMENT); + op.setPadding((byte)param); + break; + default: + break; + } + } + private void authorizeBlockModeAndMacLength(KMOperationState op){ + short param = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.BLOCK_MODE, data[KEY_PARAMETERS]); + if(param != KMType.INVALID_VALUE){ + if(KMEnumArrayTag.cast(param).length() != 1) KMException.throwIt(KMError.INVALID_ARGUMENT); + param = KMEnumArrayTag.cast(param).get((short)0); + } + short macLen = + KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.MAC_LENGTH, data[KEY_PARAMETERS]); + switch (op.getAlgorithm()){ + case KMType.AES: + if(param == KMType.INVALID_VALUE) KMException.throwIt(KMError.INVALID_ARGUMENT); + if (param == KMType.GCM){ + if(op.getPadding() != KMType.PADDING_NONE) { + KMException.throwIt(KMError.INCOMPATIBLE_PADDING_MODE); + } + if (macLen == KMType.INVALID_VALUE) { + KMException.throwIt(KMError.MISSING_MAC_LENGTH); + } + if (macLen % 8 != 0 || macLen > 128 || + macLen < KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, data[HW_PARAMETERS])) { + KMException.throwIt(KMError.INVALID_MAC_LENGTH); + } + }else if(macLen != KMType.INVALID_VALUE) KMException.throwIt(KMError.INVALID_ARGUMENT); + break; + case KMType.DES: + if(param == KMType.INVALID_VALUE) KMException.throwIt(KMError.INVALID_ARGUMENT); + break; + case KMType.HMAC: + if (macLen == KMType.INVALID_VALUE) { + KMException.throwIt(KMError.MISSING_MAC_LENGTH); + } + if (macLen != 256 || + macLen < KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, data[HW_PARAMETERS])) { + KMException.throwIt(KMError.INVALID_MAC_LENGTH); + } + break; + default: + break; + } + op.setBlockMode((byte) param); + } + + private void authorizeAndBeginOperation(KMOperationState op, byte[] scratchPad) { + authorizeAlgorithm(op); + authorizePurpose(op); + authorizeDigest(op); + authorizePadding(op); + authorizeBlockModeAndMacLength(op); + authorizeKeyUsageForCount(); + authorizeUserSecureIdAuthTimeout(op, scratchPad); + // Authorize Caller Nonce - if caller nonce absent in key char and nonce present in + // key params then fail. + data[IV] = KMType.INVALID_VALUE; + tmpVariables[0] = + KMKeyParameters.findTag(KMType.BOOL_TAG, KMType.CALLER_NONCE, data[HW_PARAMETERS]); + tmpVariables[1] = KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.NONCE, data[KEY_PARAMETERS]); + if (tmpVariables[0] == KMType.INVALID_VALUE) { + if (tmpVariables[1] != KMType.INVALID_VALUE) { + KMException.throwIt(KMError.CALLER_NONCE_PROHIBITED); + } + } + if (tmpVariables[1] != KMType.INVALID_VALUE) { + data[IV] = KMByteTag.cast(tmpVariables[1]).getValue(); + } + // For symmetric decryption iv is required + if(op.getPurpose() == KMType.DECRYPT && data[IV] == KMType.INVALID_VALUE && + (op.getBlockMode() == KMType.CBC || op.getBlockMode() == KMType.GCM)){ + KMException.throwIt(KMError.MISSING_NONCE); + } + } + + private void beginCipherOperation(KMOperationState op) { + short padding; + short alg = -1; + short purpose; + if(op.getPurpose() == KMType.ENCRYPT) purpose = KMCipher.MODE_ENCRYPT; + else purpose = KMCipher.MODE_DECRYPT; + + switch (op.getAlgorithm()) { + // Not required to be supported - supported for testing purpose + // TODO remove this later + case KMType.RSA: + if (op.getPadding() == KMType.RSA_PKCS1_1_5_ENCRYPT) padding = KMCipher.PAD_PKCS1; + else if(op.getPadding() == KMType.RSA_OAEP){ + padding = KMCipher.PAD_PKCS1_OAEP_SHA256; + } + else padding = KMCipher.PAD_NOPAD; + try { + if(purpose == KMCipher.MODE_DECRYPT){ + op.setKey( + KMByteBlob.cast(data[SECRET]).getBuffer(), + KMByteBlob.cast(data[SECRET]).getStartOff(), + KMByteBlob.cast(data[SECRET]).length()); + op.setCipher( + cryptoProvider.createRsaDecipher( + padding, + KMByteBlob.cast(data[SECRET]).getBuffer(), + KMByteBlob.cast(data[SECRET]).getStartOff(), + KMByteBlob.cast(data[SECRET]).length(), + KMByteBlob.cast(data[PUB_KEY]).getBuffer(), + KMByteBlob.cast(data[PUB_KEY]).getStartOff(), + KMByteBlob.cast(data[PUB_KEY]).length())); + }else{ + op.setKey( + KMByteBlob.cast(data[PUB_KEY]).getBuffer(), + KMByteBlob.cast(data[PUB_KEY]).getStartOff(), + KMByteBlob.cast(data[PUB_KEY]).length()); + op.setCipher( + cryptoProvider.createRsaCipher( + padding, + KMByteBlob.cast(data[PUB_KEY]).getBuffer(), + KMByteBlob.cast(data[PUB_KEY]).getStartOff(), + KMByteBlob.cast(data[PUB_KEY]).length())); + } + } catch (CryptoException exp) { + KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM); + } + break; + case KMType.AES: + if (op.getBlockMode() == KMType.GCM) { + beginAesGCMOperation(op); + return; + } + case KMType.DES: + if (op.getPadding() == KMType.PADDING_NONE) { + padding = KMCipher.PAD_NOPAD; + } else { + padding = KMCipher.PAD_PKCS7; + } + if (op.getAlgorithm() == KMType.AES) { + if (op.getBlockMode() == KMType.CBC) { + alg = KMCipher.ALG_AES_BLOCK_128_CBC_NOPAD; + } else if (op.getBlockMode() == KMType.ECB) { + alg = KMCipher.ALG_AES_BLOCK_128_ECB_NOPAD; + data[IV] = KMType.INVALID_VALUE; + } else { + // data[CIPHER_ALGORITHM] = Cipher.CIPHER_AES_CTR; // Not supported in 3.0.5 + // TODO change this once we can test. + KMException.throwIt(KMError.UNSUPPORTED_BLOCK_MODE); + } + } else if (op.getAlgorithm() == KMType.DES) { + if (op.getBlockMode() == KMType.CBC) { + alg = KMCipher.ALG_DES_CBC_NOPAD; + if(data[IV] == KMType.INVALID_VALUE){ + data[IV] = KMByteBlob.instance((short)16); + cryptoProvider.newRandomNumber( + KMByteBlob.cast(data[IV]).getBuffer(), + KMByteBlob.cast(data[IV]).getStartOff(), + KMByteBlob.cast(data[IV]).length() + ); + } + + } else { + alg = KMCipher.ALG_DES_ECB_NOPAD; + data[IV] = KMType.INVALID_VALUE; + } + } else { + KMException.throwIt(KMError.INCOMPATIBLE_ALGORITHM); + } + op.setKey( + KMByteBlob.cast(data[SECRET]).getBuffer(), + KMByteBlob.cast(data[SECRET]).getStartOff(), + KMByteBlob.cast(data[SECRET]).length()); + try { + if (data[IV] != KMType.INVALID_VALUE) { + op.setCipher( + cryptoProvider.createSymmetricCipher( + alg, + purpose, + padding, + KMByteBlob.cast(data[SECRET]).getBuffer(), + KMByteBlob.cast(data[SECRET]).getStartOff(), + KMByteBlob.cast(data[SECRET]).length(), + KMByteBlob.cast(data[IV]).getBuffer(), + KMByteBlob.cast(data[IV]).getStartOff(), + KMByteBlob.cast(data[IV]).length())); + } else { + op.setCipher( + cryptoProvider.createSymmetricCipher( + alg, + purpose, + padding, + KMByteBlob.cast(data[SECRET]).getBuffer(), + KMByteBlob.cast(data[SECRET]).getStartOff(), + KMByteBlob.cast(data[SECRET]).length())); + } + } catch (CryptoException exp) { + KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM); + } + } + } + + + private void beginSignVerifyOperation(KMOperationState op) { + short padding; + short digest; + short purpose;; + if(op.getPurpose() == KMType.SIGN) purpose = Signature.MODE_SIGN; + else purpose = Signature.MODE_VERIFY; + switch(op.getAlgorithm()){ + case KMType.RSA: + if(op.getDigest() == KMType.DIGEST_NONE) digest = MessageDigest.ALG_NULL; + else digest = MessageDigest.ALG_SHA_256; + if(op.getPadding() == KMType.PADDING_NONE) padding = KMCipher.PAD_NOPAD; + else if(op.getPadding() == KMType.RSA_PSS) padding = KMCipher.PAD_PKCS1_PSS; + else padding = KMCipher.PAD_PKCS1; + op.setKey(KMByteBlob.cast(data[SECRET]).getBuffer(), + KMByteBlob.cast(data[SECRET]).getStartOff(), + KMByteBlob.cast(data[SECRET]).length()); + try{ + if (op.getPurpose() == KMType.SIGN) { + op.setSignerVerifier( + cryptoProvider.createRsaSigner( + digest, + padding, + KMByteBlob.cast(data[SECRET]).getBuffer(), + KMByteBlob.cast(data[SECRET]).getStartOff(), + KMByteBlob.cast(data[SECRET]).length(), + KMByteBlob.cast(data[PUB_KEY]).getBuffer(), + KMByteBlob.cast(data[PUB_KEY]).getStartOff(), + KMByteBlob.cast(data[PUB_KEY]).length())); + }else{ + op.setSignerVerifier( + cryptoProvider.createRsaVerifier( + digest, + padding, + KMByteBlob.cast(data[PUB_KEY]).getBuffer(), + KMByteBlob.cast(data[PUB_KEY]).getStartOff(), + KMByteBlob.cast(data[PUB_KEY]).length())); + } + }catch(CryptoException exp){ + // Javacard does not support NO digest based signing. + KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM); + } + break; + case KMType.EC: + if(op.getDigest() == KMType.DIGEST_NONE) digest = MessageDigest.ALG_NULL; + else digest = MessageDigest.ALG_SHA_256; + op.setKey(KMByteBlob.cast(data[SECRET]).getBuffer(), + KMByteBlob.cast(data[SECRET]).getStartOff(), + KMByteBlob.cast(data[SECRET]).length()); + try{ + if (op.getPurpose() == KMType.SIGN) { + op.setSignerVerifier( + cryptoProvider.createEcSigner( + digest, + KMByteBlob.cast(data[SECRET]).getBuffer(), + KMByteBlob.cast(data[SECRET]).getStartOff(), + KMByteBlob.cast(data[SECRET]).length())); + }else{ + op.setSignerVerifier( + cryptoProvider.createEcVerifier( + digest, + KMByteBlob.cast(data[PUB_KEY]).getBuffer(), + KMByteBlob.cast(data[PUB_KEY]).getStartOff(), + KMByteBlob.cast(data[PUB_KEY]).length())); + } + }catch(CryptoException exp){ + // Javacard does not support NO digest based signing. + KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM); + } + break; + case KMType.HMAC: + digest = MessageDigest.ALG_SHA_256; + op.setKey(KMByteBlob.cast(data[SECRET]).getBuffer(), + KMByteBlob.cast(data[SECRET]).getStartOff(), + KMByteBlob.cast(data[SECRET]).length()); + try{ + op.setSignerVerifier( + cryptoProvider.createHmacSignerVerifier( + purpose, digest, + KMByteBlob.cast(data[SECRET]).getBuffer(), + KMByteBlob.cast(data[SECRET]).getStartOff(), + KMByteBlob.cast(data[SECRET]).length())); + }catch(CryptoException exp){ + // Javacard does not support NO digest based signing. + KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM); + } + break; + default: + KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM); + break; + } + } + + private void authorizeUserSecureIdAuthTimeout(KMOperationState op, byte[] scratchPad) { + short authTime; + // Authorize User Secure Id and Auth timeout + tmpVariables[0] = + KMKeyParameters.findTag(KMType.ULONG_ARRAY_TAG, KMType.USER_SECURE_ID, data[HW_PARAMETERS]); + if (tmpVariables[0] != KMType.INVALID_VALUE) { + tmpVariables[0] = + KMKeyParameters.findTag(KMType.UINT_TAG, KMType.AUTH_TIMEOUT, data[HW_PARAMETERS]); + if (tmpVariables[0] != KMType.INVALID_VALUE) { + // check if hw token is empty - mac should not be empty. + tmpVariables[1] = KMHardwareAuthToken.cast(data[HW_TOKEN]).getMac(); + if (KMByteBlob.cast(tmpVariables[1]).length() == 0) { + KMException.throwIt(KMError.INVALID_MAC_LENGTH); + } + authTime = KMIntegerTag.cast(tmpVariables[0]).getValue(); + authorizeHwAuthToken(scratchPad); + op.setOneTimeAuthReqd(true); + authTime = addIntegers(authTime, KMHardwareAuthToken.cast(data[HW_TOKEN]).getTimestamp()); + op.setAuthTime(KMInteger.cast(authTime).getBuffer(), KMInteger.cast(authTime).getStartOff()); + op.setAuthTimeoutValidated(false); // auth time validation will happen in update or finish + } else { // auth per operation required + op.setOneTimeAuthReqd(false); + op.setAuthPerOperationReqd(true); + } + } } - private void processBeginOperationCmd(APDU apdu) {} + private void beginAesGCMOperation(KMOperationState op) { + short purpose; + data[OP_HANDLE] = KMType.INVALID_VALUE; + if (op.getPurpose() == KMType.ENCRYPT) { + purpose = KMCipher.MODE_ENCRYPT; + } else { + purpose = KMCipher.MODE_DECRYPT; + } + if (op.getPadding() != KMType.PADDING_NONE) { + KMException.throwIt(KMError.INCOMPATIBLE_PADDING_MODE); + } + op.setKey(KMByteBlob.cast(data[SECRET]).getBuffer(), + KMByteBlob.cast(data[SECRET]).getStartOff(), + KMByteBlob.cast(data[SECRET]).length()); + op.setCipher( + cryptoProvider.createGCMCipher( + purpose, + KMByteBlob.cast(data[SECRET]).getBuffer(), + KMByteBlob.cast(data[SECRET]).getStartOff(), + KMByteBlob.cast(data[SECRET]).length(), + KMByteBlob.cast(data[IV]).getBuffer(), + KMByteBlob.cast(data[IV]).getStartOff(), + KMByteBlob.cast(data[IV]).length())); + data[OP_HANDLE] = op.getHandle(); + } + private void authorizeHwAuthToken(byte[] scratchPad) { + validateHwToken(data[HW_TOKEN], scratchPad); + tmpVariables[0] = KMHardwareAuthToken.cast(data[HW_TOKEN]).getUserId(); + if (KMInteger.cast(tmpVariables[0]).isZero()) { + tmpVariables[0] = KMHardwareAuthToken.cast(data[HW_TOKEN]).getAuthenticatorId(); + if (KMInteger.cast(tmpVariables[0]).isZero()) { + KMException.throwIt(KMError.KEY_USER_NOT_AUTHENTICATED); + } + } + // check user secure id + if (!KMIntegerArrayTag.contains(KMType.USER_SECURE_ID, tmpVariables[0], data[HW_PARAMETERS])) { + KMException.throwIt(KMError.KEY_USER_NOT_AUTHENTICATED); + } + // check auth type + tmpVariables[1] = KMEnumTag.getValue(KMType.USER_AUTH_TYPE, data[HW_PARAMETERS]); + tmpVariables[2] = KMHardwareAuthToken.cast(data[HW_TOKEN]).getHwAuthenticatorType(); + tmpVariables[2] = KMEnum.cast(tmpVariables[2]).getVal(); + if (((byte) tmpVariables[2] & (byte) tmpVariables[1]) == 0) { + KMException.throwIt(KMError.KEY_USER_NOT_AUTHENTICATED); + } + } + + private void validateHwToken(short hwToken, byte[] scratchPad) { + // CBOR Encoding is always big endian + short ptr = KMHardwareAuthToken.cast(hwToken).getMac(); + short len = 0; + // If mac length is zero then token is empty. + if (KMByteBlob.cast(ptr).length() == 0) { + return; + } + // add 0 + Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 37, (byte) 0); + len = 1; + // concatenate challenge - 8 bytes + ptr = KMHardwareAuthToken.cast(hwToken).getChallenge(); + KMInteger.cast(ptr) + .value(scratchPad, (short) (len + (short) (8 - KMInteger.cast(ptr).length()))); + len += 8; + // concatenate user id - 8 bytes + ptr = KMHardwareAuthToken.cast(hwToken).getUserId(); + KMInteger.cast(tmpVariables[0]) + .value(scratchPad, (short) (len + (short) (8 - KMInteger.cast(ptr).length()))); + len += 8; + // concatenate authenticator id - 8 bytes + ptr = KMHardwareAuthToken.cast(hwToken).getAuthenticatorId(); + KMInteger.cast(tmpVariables[0]) + .value(scratchPad, (short) (len + (short) (8 - KMInteger.cast(ptr).length()))); + len += 8; + // concatenate authenticator type - 4 bytes + ptr = KMHardwareAuthToken.cast(hwToken).getHwAuthenticatorType(); + scratchPad[(short) (len + 3)] = KMEnum.cast(ptr).getVal(); + len += 4; + // concatenate timestamp -8 bytes + ptr = KMHardwareAuthToken.cast(hwToken).getTimestamp(); + KMInteger.cast(tmpVariables[0]) + .value(scratchPad, (short) (len + (short) (8 - KMInteger.cast(ptr).length()))); + len += 8; + // hmac the data + HMACKey key = + cryptoProvider.createHMACKey( + repository.getComputedHmacKey(), + (short) 0, + (short) repository.getComputedHmacKey().length); + ptr = KMHardwareAuthToken.cast(hwToken).getMac(); + boolean verified = + cryptoProvider.hmacVerify(key, scratchPad, (short) 0, len, + KMByteBlob.cast(ptr).getBuffer(), + KMByteBlob.cast(ptr).getStartOff(), + KMByteBlob.cast(ptr).length()); + if(!verified){ + KMException.throwIt(KMError.VERIFICATION_FAILED); + } +/* + len = + cryptoProvider.hmac(key, scratchPad, (short) 0, len, scratchPad, (short) (len + 1) ); + // Compare mac. + ptr = KMHardwareAuthToken.cast(hwToken).getMac(); + if (len != KMByteBlob.cast(ptr).length()) { + KMException.throwIt(KMError.INVALID_MAC_LENGTH); + } + if (Util.arrayCompare( + scratchPad, + (short) 38, + KMByteBlob.cast(ptr).getBuffer(), + KMByteBlob.cast(ptr).getStartOff(), + len) + != 0) { + KMException.throwIt(KMError.VERIFICATION_FAILED); + } + */ + } + + private void authorizeKeyUsageForCount() { + // TODO currently only short usageLimit supported - max count 32K. + short usageLimit = KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.MAX_USES_PER_BOOT, data[HW_PARAMETERS]); + if (usageLimit == KMType.INVALID_VALUE) return; + // get current counter + short usage = repository.getRateLimitedKeyCount(data[AUTH_TAG]); + if (usage != KMType.INVALID_VALUE) { + if(usage < usageLimit){ + KMException.throwIt(KMError.KEY_MAX_OPS_EXCEEDED); + } + // increment the counter and store it back. + usage++; + repository.setRateLimitedKeyCount(data[AUTH_TAG], usage); + } else { + KMException.throwIt(KMError.UNKNOWN_ERROR); + } + } private void processImportKeyCmd(APDU apdu) { if (repository.keyBlobCount > repository.MAX_BLOB_STORAGE) { @@ -617,11 +2115,48 @@ private void processImportKeyCmd(APDU apdu) { } private void importKey(APDU apdu, byte[] scratchPad) { + // Bootloader only not supported + tmpVariables[0] = + KMKeyParameters.findTag(KMType.BOOL_TAG, KMType.BOOTLOADER_ONLY, data[KEY_PARAMETERS]); + if (tmpVariables[0] != KMType.INVALID_VALUE) { + KMException.throwIt(KMError.INVALID_KEY_BLOB); + } + // get algorithm tmpVariables[3] = KMEnumTag.getValue(KMType.ALGORITHM, data[KEY_PARAMETERS]); if (tmpVariables[3] == KMType.INVALID_VALUE) { KMException.throwIt(KMError.INVALID_ARGUMENT); } + tmpVariables[4] = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.DIGEST,data[KEY_PARAMETERS]); + if(tmpVariables[4] != KMType.INVALID_VALUE){ + if(!KMEnumArrayTag.cast(tmpVariables[4]).isValidDigests((byte)tmpVariables[3])){ + KMException.throwIt(KMError.UNSUPPORTED_DIGEST); + } + } + tmpVariables[4] = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.PADDING,data[KEY_PARAMETERS]); + if(tmpVariables[4] != KMType.INVALID_VALUE){ + if(!KMEnumArrayTag.cast(tmpVariables[4]).isValidPaddingModes((byte)tmpVariables[3])){ + KMException.throwIt(KMError.INCOMPATIBLE_PADDING_MODE); + } + } + tmpVariables[4] = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.PURPOSE,data[KEY_PARAMETERS]); + if(tmpVariables[4] != KMType.INVALID_VALUE){ + if(!KMEnumArrayTag.cast(tmpVariables[4]).isValidPurpose((byte)tmpVariables[3])){ + KMException.throwIt(KMError.INCOMPATIBLE_PURPOSE); + } + } + tmpVariables[4] = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.BLOCK_MODE,data[KEY_PARAMETERS]); + if(tmpVariables[4] != KMType.INVALID_VALUE){ + if(!KMEnumArrayTag.cast(tmpVariables[4]).isValidBlockMode((byte)tmpVariables[3])){ + KMException.throwIt(KMError.INCOMPATIBLE_PURPOSE); + } + } + tmpVariables[4] = KMKeyParameters.findTag(KMType.UINT_TAG, KMType.KEYSIZE,data[KEY_PARAMETERS]); + if(tmpVariables[4] != KMType.INVALID_VALUE){ + if(!KMIntegerTag.cast(tmpVariables[4]).isValidKeySize((byte)tmpVariables[3])){ + KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); + } + } // Check algorithm and dispatch to appropriate handler. switch (tmpVariables[3]) { case KMType.RSA: @@ -1042,11 +2577,54 @@ private static void processGenerateKey(APDU apdu) { // Decode the argument tmpVariables[2] = decoder.decode(tmpVariables[1], buffer, bufferStartOffset, bufferLength); data[KEY_PARAMETERS] = KMArray.cast(tmpVariables[2]).get((short) 0); + // Bootloader only not supported + tmpVariables[0] = + KMKeyParameters.findTag(KMType.BOOL_TAG, KMType.BOOTLOADER_ONLY, data[KEY_PARAMETERS]); + if (tmpVariables[0] != KMType.INVALID_VALUE) { + KMException.throwIt(KMError.INVALID_KEY_BLOB); + } // get algorithm tmpVariables[3] = KMEnumTag.getValue(KMType.ALGORITHM, data[KEY_PARAMETERS]); if (tmpVariables[3] == KMType.INVALID_VALUE) { KMException.throwIt(KMError.INVALID_ARGUMENT); } + // validate digest - only digest none or 256 is supported. + tmpVariables[4] = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.DIGEST,data[KEY_PARAMETERS]); + if(tmpVariables[4] != KMType.INVALID_VALUE){ + if(!KMEnumArrayTag.cast(tmpVariables[4]).isValidDigests((byte)tmpVariables[3])){ + KMException.throwIt(KMError.UNSUPPORTED_DIGEST); + } + } + tmpVariables[4] = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.PADDING,data[KEY_PARAMETERS]); + if(tmpVariables[4] != KMType.INVALID_VALUE){ + if(!KMEnumArrayTag.cast(tmpVariables[4]).isValidPaddingModes((byte)tmpVariables[3])){ + KMException.throwIt(KMError.INCOMPATIBLE_PADDING_MODE); + } + } + tmpVariables[4] = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.PURPOSE,data[KEY_PARAMETERS]); + if(tmpVariables[4] != KMType.INVALID_VALUE){ + if(!KMEnumArrayTag.cast(tmpVariables[4]).isValidPurpose((byte)tmpVariables[3])){ + KMException.throwIt(KMError.INCOMPATIBLE_PURPOSE); + } + } + tmpVariables[4] = KMKeyParameters.findTag(KMType.UINT_TAG, KMType.KEYSIZE,data[KEY_PARAMETERS]); + if(tmpVariables[4] != KMType.INVALID_VALUE){ + if(!KMIntegerTag.cast(tmpVariables[4]).isValidKeySize((byte)tmpVariables[3])){ + KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); + } + } + tmpVariables[4] = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.BLOCK_MODE,data[KEY_PARAMETERS]); + if(tmpVariables[4] != KMType.INVALID_VALUE){ + if(!KMEnumArrayTag.cast(tmpVariables[4]).isValidBlockMode((byte)tmpVariables[3])){ + KMException.throwIt(KMError.INCOMPATIBLE_PURPOSE); + } + } + tmpVariables[4] = KMKeyParameters.findTag(KMType.UINT_TAG, KMType.KEYSIZE,data[KEY_PARAMETERS]); + if(tmpVariables[4] != KMType.INVALID_VALUE){ + if(!KMIntegerTag.cast(tmpVariables[4]).isValidKeySize((byte)tmpVariables[3])){ + KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); + } + } // Check algorithm and dispatch to appropriate handler. switch (tmpVariables[3]) { case KMType.RSA: @@ -1217,7 +2795,7 @@ private static void validateTDESKey(byte[] scratchPad) { if (tmpVariables[1] == KMType.INVALID_VALUE) { KMException.throwIt(KMError.INVALID_ARGUMENT); } - if (tmpVariables[1] != 168) { + if (tmpVariables[1] != 168 && tmpVariables[1] != 192) { KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); } } @@ -1233,10 +2811,7 @@ private static void generateTDESKey(byte[] scratchPad) { private static void validateHmacKey(byte[] scratchPad) { // check whether digest sizes are greater then or equal to min mac length. // Only SHA256 digest must be supported. - if(!KMEnumArrayTag.contains(KMType.DIGEST, KMType.SHA2_256, data[KEY_PARAMETERS])){ - KMException.throwIt(KMError.INCOMPATIBLE_DIGEST); - } - if(KMEnumArrayTag.length(KMType.DIGEST,data[KEY_PARAMETERS]) != 1){ + if(KMEnumArrayTag.contains(KMType.DIGEST, KMType.DIGEST_NONE, data[KEY_PARAMETERS])){ KMException.throwIt(KMError.INCOMPATIBLE_DIGEST); } // Read Minimum Mac length @@ -1540,4 +3115,44 @@ private static void sendError(APDU apdu, short err) { sendOutgoing(apdu); } + private short addIntegers(short num1, short num2){ + short buf = repository.alloc((short)24); + byte[] scratchPad = repository.getHeap(); + Util.arrayFillNonAtomic(scratchPad, buf, (short)24,(byte)0); + Util.arrayCopyNonAtomic( + KMInteger.cast(num1).getBuffer(), + KMInteger.cast(num1).getStartOff(),scratchPad,(short)(buf+8-KMInteger.cast(num1).length()), + KMInteger.cast(num1).length()); + Util.arrayCopyNonAtomic( + KMInteger.cast(num2).getBuffer(), + KMInteger.cast(num2).getStartOff(),scratchPad,(short)(buf+16-KMInteger.cast(num2).length()), + KMInteger.cast(num2).length()); + short index = 1; + short result = 0; + while(index <= 8){ + result = (short)(scratchPad[(short)(buf+8-index)] + scratchPad[(short)(buf+16-index)]+result); + scratchPad[(short)(buf+24-index)] = (byte)(result & 0xFF); + result = (short)(result >> 8); + index++; + } + return KMInteger.uint_64(scratchPad,(short)(buf+16)); + } +/* + private static void print (String lab, byte[] b, short s, short l){ + byte[] i = new byte[l]; + Util.arrayCopyNonAtomic(b,s,i,(short)0,l); + print(lab,i); + } + private static void print(String label, byte[] buf){ + System.out.println(label+": "); + StringBuilder sb = new StringBuilder(); + for(int i = 0; i < buf.length; i++){ + sb.append(String.format(" 0x%02X", buf[i])) ; + if(((i-1)%38 == 0) && ((i-1) >0)){ + sb.append(";\n"); + } + } + System.out.println(sb.toString()); + } +*/ } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMOperationState.java b/Applet/Applet/src/com/android/javacard/keymaster/KMOperationState.java index 2684efb3..892c5d18 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMOperationState.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMOperationState.java @@ -22,8 +22,14 @@ // TODO complete the class design and implementation public class KMOperationState { private short opHandleCounter; + private byte algorithm; + private byte padding; + private byte blockMode; + private byte digest; + private boolean active; private boolean trustedConfirmation; + private boolean cipherOperation; // TODO This should be 64 bits private short handle; private short purpose; @@ -34,6 +40,7 @@ public class KMOperationState { private short keyLength; private byte[] authTime; private boolean authPerOperationReqd; + private boolean secureUserIdReqd; private boolean authTimeoutValidated; public KMOperationState(){ @@ -58,11 +65,13 @@ public void activate(){ } public void reset(){ Util.arrayFillNonAtomic(authTime, (short)0,(short)8, (byte)0); + cipherOperation = false; keyLength = 0; authPerOperationReqd = false; + secureUserIdReqd = false; active = false; handle = 0; - key = null; + Util.arrayFillNonAtomic(key,(short)0,(short)key.length,(byte)0); cipher = null; signer = null; purpose = KMType.INVALID_VALUE; @@ -82,11 +91,16 @@ public short getOpHandleCounter() { public boolean isActive() { return active; } + public boolean isCipherOperation(){ return cipherOperation;} + public void setCipherOperation(boolean flag){cipherOperation = flag;} public short getHandle() { - return handle; + return KMInteger.uint_16(handle); } + public short handle(){ + return handle; + } public short getPurpose() { return purpose; } @@ -103,11 +117,11 @@ public void setCipher(KMCipher cipher) { this.cipher = cipher; } - public Signature getSigner() { + public Signature getSignerVerifier() { return signer; } - public void setSigner(Signature signer) { + public void setSignerVerifier(Signature signer) { this.signer = signer; } @@ -121,13 +135,14 @@ public void setKey(byte[] buf, short start, short len) { Util.arrayCopy(buf, start, key, (short)0, len); } - public boolean isAuthPerOperation() { + public boolean isAuthPerOperationReqd() { return authPerOperationReqd; } public boolean isAuthTimeoutValidated() { return authTimeoutValidated; } + public boolean isSecureUserIdReqd(){return secureUserIdReqd;} public byte[] getAuthTime() { return authTime; @@ -136,8 +151,41 @@ public byte[] getAuthTime() { public void setAuthTime(byte[] time, short start) { Util.arrayCopy(time, start, authTime, (short)0, (short)8); } - + public void setOneTimeAuthReqd(boolean flag){secureUserIdReqd = flag;} public void setAuthTimeoutValidated(boolean flag) { authTimeoutValidated = flag; } + public void setAuthPerOperationReqd(boolean flag){ authPerOperationReqd = flag;} + + public byte getAlgorithm() { + return algorithm; + } + + public void setAlgorithm(byte algorithm) { + this.algorithm = algorithm; + } + + public byte getPadding() { + return padding; + } + + public void setPadding(byte padding) { + this.padding = padding; + } + + public byte getBlockMode() { + return blockMode; + } + + public void setBlockMode(byte blockMode) { + this.blockMode = blockMode; + } + + public byte getDigest() { + return digest; + } + + public void setDigest(byte digest) { + this.digest = digest; + } } diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMRepository.java b/Applet/Applet/src/com/android/javacard/keymaster/KMRepository.java index 421065ab..696b06f0 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMRepository.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMRepository.java @@ -225,7 +225,11 @@ public void removeAuthTag(short authTag) { } JCSystem.beginTransaction(); tag.reserved = false; - Util.arrayFill(tag.authTag, (short) 0, AES_GCM_AUTH_TAG_LENGTH, (byte) 0); + short index = 0; + while(index < AES_GCM_AUTH_TAG_LENGTH){ + tag.authTag[index] = 0; + index++; + } tag.usageCount = 0; keyBlobCount--; JCSystem.commitTransaction(); @@ -233,12 +237,18 @@ public void removeAuthTag(short authTag) { public void removeAllAuthTags() { JCSystem.beginTransaction(); + KMAuthTag tag = null; short index = 0; + short i = 0; while (index < MAX_BLOB_STORAGE) { - ((KMAuthTag) authTagRepo[index]).reserved = false; - Util.arrayFill( - ((KMAuthTag) authTagRepo[index]).authTag, (short) 0, AES_GCM_AUTH_TAG_LENGTH, (byte) 0); - ((KMAuthTag) authTagRepo[index]).usageCount = 0; + tag = (KMAuthTag) authTagRepo[index]; + tag.reserved = false; + i = 0; + while(i < AES_GCM_AUTH_TAG_LENGTH){ + tag.authTag[i] = 0; + i++; + } + tag.usageCount = 0; index++; } keyBlobCount = 0; @@ -287,7 +297,7 @@ public KMOperationState findOperation(short opHandle) { short index = 0; while(index < MAX_OPS){ if(((KMOperationState)operationStateTable[index]).isActive() && - ((KMOperationState)operationStateTable[index]).getHandle() == opHandle){ + ((KMOperationState)operationStateTable[index]).handle() == opHandle){ return (KMOperationState)operationStateTable[index]; } index++; diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMVerificationToken.java b/Applet/Applet/src/com/android/javacard/keymaster/KMVerificationToken.java index 168971c3..c1c6d23c 100644 --- a/Applet/Applet/src/com/android/javacard/keymaster/KMVerificationToken.java +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMVerificationToken.java @@ -53,6 +53,13 @@ private static KMVerificationToken proto(short ptr) { public static short instance() { short arrPtr = KMArray.instance((short)5); + KMArray arr = KMArray.cast(arrPtr); + arr.add(CHALLENGE, KMInteger.uint_16((short)0)); + arr.add(TIMESTAMP, KMInteger.uint_16((short)0)); + //arr.add(PARAMETERS_VERIFIED, KMKeyParameters.exp()); + arr.add(PARAMETERS_VERIFIED, KMByteBlob.instance((short)0)); + arr.add(SECURITY_LEVEL, KMEnum.instance(KMType.HARDWARE_TYPE, KMType.STRONGBOX)); + arr.add(MAC, KMByteBlob.instance((short)0)); return instance(arrPtr); } diff --git a/Applet/Applet/test/com/android/javacard/test/KMFrameworkTest.java b/Applet/Applet/test/com/android/javacard/test/KMFrameworkTest.java index 0b2ae651..52bcf435 100644 --- a/Applet/Applet/test/com/android/javacard/test/KMFrameworkTest.java +++ b/Applet/Applet/test/com/android/javacard/test/KMFrameworkTest.java @@ -448,6 +448,7 @@ public void testGetHwInfoCmd(CardSimulator simulator){ Assert.assertEquals( "Google",authorNameStr); Assert.assertEquals(0x9000, response.getSW()); } + private void testAddRngEntropyCmd(CardSimulator simulator){ byte[] buf = new byte[1024]; // test provision command @@ -515,14 +516,12 @@ private short makeGenerateKeyCmd(byte alg, short keysize) { //byte[] digest = {KMType.SHA1, KMType.SHA2_256}; byte[] digest = {KMType.DIGEST_NONE}; byte[] padding = {KMType.PADDING_NONE}; - byte[] purpose = {0x02, 0x03}; vals.add((short)0, KMEnumTag.instance(KMType.ALGORITHM, alg)); vals.add((short)1, KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, KMInteger.uint_16(keysize))); //vals.add((short)1, KMIntegerTag.instance(KMType.UINT_TAG, KMType.USERID, KMInteger.uint_32(intVal, (short)0))); //vals.add((short)2, KMByteTag.instance(KMType.APPLICATION_ID, KMByteBlob.instance(val, (short)0, (short)val.length))); vals.add((short)2, KMIntegerTag.instance(KMType.ULONG_TAG, KMType.RSA_PUBLIC_EXPONENT, KMInteger.uint_32(pubVal,(short)0))); - vals.add((short)3, KMEnumArrayTag.instance(KMType.PURPOSE, KMByteBlob.instance(purpose,(short)0, (short)purpose.length))); - // vals.add((short)4, KMEnumArrayTag.instance(KMType.DIGEST, KMByteBlob.instance(digest,(short)0, (short)digest.length))); + vals.add((short)3, KMEnumArrayTag.instance(KMType.DIGEST, KMByteBlob.instance(digest,(short)0, (short)digest.length))); //vals.add((short)5, KMEnumArrayTag.instance(KMType.PADDING, KMByteBlob.instance(padding,(short)0, (short)padding.length))); short keyParamsPtr = KMKeyParameters.instance(arrPtr); // Array of expected arguments @@ -534,16 +533,20 @@ private short makeGenerateKeyCmd(byte alg, short keysize) { private short makeImportKeySymmCmd(short alg, short size) { // Argument 1 short arrPtr; + byte digestType; if(alg == KMType.HMAC) { arrPtr = KMArray.instance((short) 6); + digestType = KMType.SHA2_256; } else{ arrPtr = KMArray.instance((short) 5); + digestType = KMType.DIGEST_NONE; } KMArray vals = KMArray.cast(arrPtr); byte[] val = "Test".getBytes(); byte[] intVal = {1, 2, 3, 4}; byte[] pubVal = {0x00, 0x01, 0x00, 0x01}; - byte[] digest = {KMType.SHA2_256}; + byte[] digest = new byte[1]; + digest[0] = digestType; vals.add((short)0, KMEnumTag.instance(KMType.ALGORITHM, (byte)alg)); vals.add((short)1, KMIntegerTag.instance(KMType.UINT_TAG, KMType.USERID, KMInteger.uint_32(intVal, (short)0))); vals.add((short)2, KMByteTag.instance(KMType.APPLICATION_ID, KMByteBlob.instance(val, (short)0, (short)val.length))); diff --git a/Applet/Applet/test/com/android/javacard/test/KMVTSTest.java b/Applet/Applet/test/com/android/javacard/test/KMVTSTest.java index c5ef65c9..5ceb6aab 100644 --- a/Applet/Applet/test/com/android/javacard/test/KMVTSTest.java +++ b/Applet/Applet/test/com/android/javacard/test/KMVTSTest.java @@ -28,23 +28,20 @@ import com.android.javacard.keymaster.KMEnumArrayTag; import com.android.javacard.keymaster.KMEnumTag; import com.android.javacard.keymaster.KMError; +import com.android.javacard.keymaster.KMHardwareAuthToken; import com.android.javacard.keymaster.KMInteger; import com.android.javacard.keymaster.KMIntegerTag; import com.android.javacard.keymaster.KMKeyCharacteristics; import com.android.javacard.keymaster.KMKeyParameters; import com.android.javacard.keymaster.KMKeymasterApplet; import com.android.javacard.keymaster.KMType; +import com.android.javacard.keymaster.KMVerificationToken; import com.licel.jcardsim.smartcardio.CardSimulator; import com.licel.jcardsim.utils.AIDUtil; import javacard.framework.AID; import javacard.framework.Util; -import javacard.security.AESKey; -import javacard.security.DESKey; import javacard.security.ECPrivateKey; import javacard.security.ECPublicKey; -import javacard.security.HMACKey; -import javacard.security.Key; -import javacard.security.KeyBuilder; import javacard.security.KeyPair; import javacard.security.RSAPrivateKey; import javacard.security.RSAPublicKey; @@ -78,15 +75,15 @@ private void init(){ // provision attest key provisionCmd(simulator); // set bootup parameters - setBootParams(simulator); + setBootParams(simulator,(short)1,(short)1); } - private void setBootParams(CardSimulator simulator){ + private void setBootParams(CardSimulator simulator, short osVersion, short osPatchLevel){ // Argument 1 OS Version - short versionPatchPtr = KMInteger.uint_16((short)1); + short versionPtr = KMInteger.uint_16(osVersion); // short versionTagPtr = KMIntegerTag.instance(KMType.UINT_TAG, KMType.OS_VERSION,versionPatchPtr); // Argument 2 OS Patch level -// short patchTagPtr = KMIntegerTag.instance(KMType.UINT_TAG, KMType.OS_PATCH_LEVEL, versionPatchPtr); + short patchPtr = KMInteger.uint_16(osPatchLevel); // Argument 3 Verified Boot Key byte[] bootKeyHash = "00011122233344455566677788899900".getBytes(); short bootKeyPtr = KMByteBlob.instance(bootKeyHash,(short)0, (short)bootKeyHash.length); @@ -99,8 +96,8 @@ private void setBootParams(CardSimulator simulator){ // Arguments short arrPtr = KMArray.instance((short) 6); KMArray vals = KMArray.cast(arrPtr); - vals.add((short)0, versionPatchPtr); - vals.add((short) 1, versionPatchPtr); + vals.add((short)0, versionPtr); + vals.add((short) 1, patchPtr); vals.add((short) 2, bootKeyPtr); vals.add((short) 3, bootHashPtr); vals.add((short) 4, bootStatePtr); @@ -442,7 +439,7 @@ public void testRsaGenerateKeySuccess() { tag = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.DIGEST, hwParams); Assert.assertTrue(KMEnumArrayTag.cast(tag).contains(KMType.DIGEST_NONE)); tag = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.PADDING, hwParams); - Assert.assertTrue(KMEnumArrayTag.cast(tag).contains(KMType.PADDING_NONE)); + Assert.assertTrue(KMEnumArrayTag.cast(tag).contains(KMType.RSA_PKCS1_1_5_ENCRYPT)); tag = KMKeyParameters.findTag(KMType.ULONG_TAG, KMType.RSA_PUBLIC_EXPONENT, hwParams); Assert.assertEquals(KMInteger.cast(KMIntegerTag.cast(tag).getValue()).getSignificantShort(), 0x01); Assert.assertEquals(KMInteger.cast(KMIntegerTag.cast(tag).getValue()).getShort(), 0x01); @@ -454,20 +451,34 @@ public void testRsaGenerateKeySuccess() { } private short generateRsaKey(byte[] clientId, byte[] appData){ - short tagCount = 5; + short tagCount = 7; if(clientId != null) tagCount++; if(appData != null) tagCount++; short arrPtr = KMArray.instance(tagCount); short keySize = KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, KMInteger.uint_16((short)2048)); - short byteBlob = KMByteBlob.instance((short)1); + short byteBlob = KMByteBlob.instance((short)2); KMByteBlob.cast(byteBlob).add((short)0, KMType.DIGEST_NONE); + KMByteBlob.cast(byteBlob).add((short)1, KMType.SHA2_256); short digest = KMEnumArrayTag.instance(KMType.DIGEST, byteBlob); - byteBlob = KMByteBlob.instance((short)1); - KMByteBlob.cast(byteBlob).add((short)0, KMType.PADDING_NONE); + byteBlob = KMByteBlob.instance((short)5); + KMByteBlob.cast(byteBlob).add((short)0, KMType.RSA_PKCS1_1_5_ENCRYPT); + KMByteBlob.cast(byteBlob).add((short)1, KMType.RSA_PKCS1_1_5_SIGN); + KMByteBlob.cast(byteBlob).add((short)2, KMType.RSA_OAEP); + KMByteBlob.cast(byteBlob).add((short)3, KMType.RSA_PSS); + KMByteBlob.cast(byteBlob).add((short)4, KMType.PADDING_NONE); short padding = KMEnumArrayTag.instance(KMType.PADDING, byteBlob); + byteBlob = KMByteBlob.instance((short)4); + KMByteBlob.cast(byteBlob).add((short)0, KMType.SIGN); + KMByteBlob.cast(byteBlob).add((short)1, KMType.VERIFY); + KMByteBlob.cast(byteBlob).add((short)2, KMType.ENCRYPT); + KMByteBlob.cast(byteBlob).add((short)3, KMType.DECRYPT); + short purpose = KMEnumArrayTag.instance(KMType.PURPOSE, byteBlob); byte[] pub = {0,1,0,1}; short rsaPubExpTag = KMIntegerTag.instance(KMType.ULONG_TAG,KMType.RSA_PUBLIC_EXPONENT, KMInteger.uint_32(pub, (short)0)); + short boolTag = KMBoolTag.instance(KMType.NO_AUTH_REQUIRED); short tagIndex = 0; + KMArray.cast(arrPtr).add(tagIndex++, purpose); + KMArray.cast(arrPtr).add(tagIndex++, boolTag); KMArray.cast(arrPtr).add(tagIndex++, keySize); KMArray.cast(arrPtr).add(tagIndex++, digest); KMArray.cast(arrPtr).add(tagIndex++, rsaPubExpTag); @@ -499,14 +510,48 @@ private short generateRsaKey(byte[] clientId, byte[] appData){ @Test public void testEcGenerateKeySuccess() { init(); - short arrPtr = KMArray.instance((short)3); + short ret = generateEcKey(null, null); + short error = KMInteger.cast(KMArray.cast(ret).get((short)0)).getShort(); + short keyBlobLength = KMByteBlob.cast(KMArray.cast(ret).get((short)1)).length(); + short keyCharacteristics = KMArray.cast(ret).get((short)2); + short hwParams = KMKeyCharacteristics.cast(keyCharacteristics).getHardwareEnforced(); + short swParams = KMKeyCharacteristics.cast(keyCharacteristics).getSoftwareEnforced(); + Assert.assertEquals(error, KMError.OK); + short tag = KMKeyParameters.findTag(KMType.UINT_TAG, KMType.KEYSIZE, hwParams); + Assert.assertEquals(KMInteger.cast(KMIntegerTag.cast(tag).getValue()).getShort(), 256); + tag = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.DIGEST, hwParams); + Assert.assertTrue(KMEnumArrayTag.cast(tag).contains(KMType.DIGEST_NONE)); + tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ALGORITHM, hwParams); + Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.EC); + tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ORIGIN, hwParams); + Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.GENERATED); + cleanUp(); + } + public short generateEcKey(byte[] clientId, byte[] appData) { + short tagCount = 5; + if(clientId != null) tagCount++; + if(appData != null) tagCount++; + short arrPtr = KMArray.instance(tagCount); short keySize = KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, KMInteger.uint_16((short)256)); - short byteBlob = KMByteBlob.instance((short)1); + short byteBlob = KMByteBlob.instance((short)2); KMByteBlob.cast(byteBlob).add((short)0, KMType.DIGEST_NONE); + KMByteBlob.cast(byteBlob).add((short)1, KMType.SHA2_256); short digest = KMEnumArrayTag.instance(KMType.DIGEST, byteBlob); - KMArray.cast(arrPtr).add((short)0, keySize); - KMArray.cast(arrPtr).add((short)1, digest); - KMArray.cast(arrPtr).add((short)2, KMEnumTag.instance(KMType.ALGORITHM, KMType.EC)); + byteBlob = KMByteBlob.instance((short)2); + KMByteBlob.cast(byteBlob).add((short)0, KMType.SIGN); + KMByteBlob.cast(byteBlob).add((short)1, KMType.VERIFY); + short purpose = KMEnumArrayTag.instance(KMType.PURPOSE, byteBlob); + short boolTag = KMBoolTag.instance(KMType.NO_AUTH_REQUIRED); + short tagIndex = 0; + KMArray.cast(arrPtr).add(tagIndex++, purpose); + KMArray.cast(arrPtr).add(tagIndex++, boolTag); + KMArray.cast(arrPtr).add(tagIndex++, keySize); + KMArray.cast(arrPtr).add(tagIndex++, digest); + KMArray.cast(arrPtr).add(tagIndex++, KMEnumTag.instance(KMType.ALGORITHM, KMType.EC)); + if(clientId != null)KMArray.cast(arrPtr).add(tagIndex++, + KMByteTag.instance(KMType.APPLICATION_ID, KMByteBlob.instance(clientId,(short)0,(short)clientId.length))); + if(appData != null)KMArray.cast(arrPtr).add(tagIndex++, + KMByteTag.instance(KMType.APPLICATION_DATA, KMByteBlob.instance(appData,(short)0,(short)appData.length))); short keyParams = KMKeyParameters.instance(arrPtr); arrPtr = KMArray.instance((short)1); KMArray arg = KMArray.cast(arrPtr); @@ -514,6 +559,7 @@ public void testEcGenerateKeySuccess() { CommandAPDU apdu = encodeApdu((byte)0x10, arrPtr); // print(commandAPDU.getBytes()); ResponseAPDU response = simulator.transmitCommand(apdu); + Assert.assertEquals(0x9000, response.getSW()); short ret = KMArray.instance((short) 3); KMArray.cast(ret).add((short) 0, KMInteger.exp()); KMArray.cast(ret).add((short)1, KMByteBlob.exp()); @@ -522,37 +568,57 @@ public void testEcGenerateKeySuccess() { byte[] respBuf = response.getBytes(); short len = (short) respBuf.length; ret = decoder.decode(ret, respBuf, (short) 0, len); + return ret; + } + + @Test + public void testHmacGenerateKeySuccess() { + init(); + short ret = generateHmacKey(null, null); short error = KMInteger.cast(KMArray.cast(ret).get((short)0)).getShort(); short keyBlobLength = KMByteBlob.cast(KMArray.cast(ret).get((short)1)).length(); short keyCharacteristics = KMArray.cast(ret).get((short)2); short hwParams = KMKeyCharacteristics.cast(keyCharacteristics).getHardwareEnforced(); short swParams = KMKeyCharacteristics.cast(keyCharacteristics).getSoftwareEnforced(); - Assert.assertEquals(0x9000, response.getSW()); Assert.assertEquals(error, KMError.OK); short tag = KMKeyParameters.findTag(KMType.UINT_TAG, KMType.KEYSIZE, hwParams); - Assert.assertEquals(KMInteger.cast(KMIntegerTag.cast(tag).getValue()).getShort(), 256); + Assert.assertEquals(KMInteger.cast(KMIntegerTag.cast(tag).getValue()).getShort(), 128); tag = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.DIGEST, hwParams); - Assert.assertTrue(KMEnumArrayTag.cast(tag).contains(KMType.DIGEST_NONE)); + Assert.assertTrue(KMEnumArrayTag.cast(tag).contains(KMType.SHA2_256)); + tag = KMKeyParameters.findTag(KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, hwParams); + Assert.assertEquals(KMInteger.cast(KMIntegerTag.cast(tag).getValue()).getShort(), 128); tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ALGORITHM, hwParams); - Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.EC); + Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.HMAC); tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ORIGIN, hwParams); Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.GENERATED); cleanUp(); } - - @Test - public void testHmacGenerateKeySuccess() { - init(); - short arrPtr = KMArray.instance((short)4); + public short generateHmacKey(byte[] clientId, byte[] appData){ + short tagCount = 6; + if(clientId != null) tagCount++; + if(appData != null) tagCount++; + short arrPtr = KMArray.instance(tagCount); short keySize = KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, KMInteger.uint_16((short)128)); short byteBlob = KMByteBlob.instance((short)1); KMByteBlob.cast(byteBlob).add((short)0, KMType.SHA2_256); short digest = KMEnumArrayTag.instance(KMType.DIGEST, byteBlob); - short minMacLength = KMIntegerTag.instance(KMType.UINT_TAG,KMType.MIN_MAC_LENGTH, KMInteger.uint_16((short)128)); - KMArray.cast(arrPtr).add((short)0, keySize); - KMArray.cast(arrPtr).add((short)1, digest); - KMArray.cast(arrPtr).add((short)2, minMacLength); - KMArray.cast(arrPtr).add((short)3, KMEnumTag.instance(KMType.ALGORITHM, KMType.HMAC)); + byteBlob = KMByteBlob.instance((short)2); + KMByteBlob.cast(byteBlob).add((short)0, KMType.SIGN); + KMByteBlob.cast(byteBlob).add((short)1, KMType.VERIFY); + short purpose = KMEnumArrayTag.instance(KMType.PURPOSE, byteBlob); + short boolTag = KMBoolTag.instance(KMType.NO_AUTH_REQUIRED); + short minMacLen = KMIntegerTag.instance(KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, KMInteger.uint_16((short)128)); + short tagIndex = 0; + KMArray.cast(arrPtr).add(tagIndex++, minMacLen); + KMArray.cast(arrPtr).add(tagIndex++, purpose); + KMArray.cast(arrPtr).add(tagIndex++, boolTag); + KMArray.cast(arrPtr).add(tagIndex++, keySize); + KMArray.cast(arrPtr).add(tagIndex++, digest); + KMArray.cast(arrPtr).add(tagIndex++, KMEnumTag.instance(KMType.ALGORITHM, KMType.HMAC)); + if(clientId != null)KMArray.cast(arrPtr).add(tagIndex++, + KMByteTag.instance(KMType.APPLICATION_ID, KMByteBlob.instance(clientId,(short)0,(short)clientId.length))); + if(appData != null)KMArray.cast(arrPtr).add(tagIndex++, + KMByteTag.instance(KMType.APPLICATION_DATA, KMByteBlob.instance(appData,(short)0,(short)appData.length))); short keyParams = KMKeyParameters.instance(arrPtr); arrPtr = KMArray.instance((short)1); KMArray arg = KMArray.cast(arrPtr); @@ -569,23 +635,60 @@ public void testHmacGenerateKeySuccess() { short len = (short) respBuf.length; ret = decoder.decode(ret, respBuf, (short) 0, len); short error = KMInteger.cast(KMArray.cast(ret).get((short)0)).getShort(); - short keyBlobLength = KMByteBlob.cast(KMArray.cast(ret).get((short)1)).length(); - short keyCharacteristics = KMArray.cast(ret).get((short)2); - short hwParams = KMKeyCharacteristics.cast(keyCharacteristics).getHardwareEnforced(); - short swParams = KMKeyCharacteristics.cast(keyCharacteristics).getSoftwareEnforced(); Assert.assertEquals(0x9000, response.getSW()); Assert.assertEquals(error, KMError.OK); - short tag = KMKeyParameters.findTag(KMType.UINT_TAG, KMType.KEYSIZE, hwParams); - Assert.assertEquals(KMInteger.cast(KMIntegerTag.cast(tag).getValue()).getShort(), 128); - tag = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.DIGEST, hwParams); - Assert.assertTrue(KMEnumArrayTag.cast(tag).contains(KMType.SHA2_256)); - tag = KMKeyParameters.findTag(KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, hwParams); - Assert.assertEquals(KMInteger.cast(KMIntegerTag.cast(tag).getValue()).getShort(), 128); - tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ALGORITHM, hwParams); - Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.HMAC); - tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ORIGIN, hwParams); - Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.GENERATED); - cleanUp(); + return ret; + } + public short generateAesDesKey(byte alg, short keysize, byte[] clientId, byte[] appData) { + short tagCount = 7; + if(clientId != null) tagCount++; + if(appData != null) tagCount++; + short arrPtr = KMArray.instance(tagCount); + short boolTag = KMBoolTag.instance(KMType.NO_AUTH_REQUIRED); + short keySize = KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, KMInteger.uint_16(keysize)); + short byteBlob = KMByteBlob.instance((short)2); + KMByteBlob.cast(byteBlob).add((short)0, KMType.ECB); + KMByteBlob.cast(byteBlob).add((short)1, KMType.CBC); + short blockModeTag = KMEnumArrayTag.instance(KMType.BLOCK_MODE, byteBlob); + byteBlob = KMByteBlob.instance((short)2); + KMByteBlob.cast(byteBlob).add((short)0, KMType.PKCS7); + KMByteBlob.cast(byteBlob).add((short)1, KMType.PADDING_NONE); + short paddingMode = KMEnumArrayTag.instance(KMType.PADDING, byteBlob); + byteBlob = KMByteBlob.instance((short)2); + KMByteBlob.cast(byteBlob).add((short)0, KMType.ENCRYPT); + KMByteBlob.cast(byteBlob).add((short)1, KMType.DECRYPT); + short purpose = KMEnumArrayTag.instance(KMType.PURPOSE, byteBlob); + short tagIndex = 0; + KMArray.cast(arrPtr).add(tagIndex++, boolTag); + KMArray.cast(arrPtr).add(tagIndex++, keySize); + KMArray.cast(arrPtr).add(tagIndex++, blockModeTag); + KMArray.cast(arrPtr).add(tagIndex++, paddingMode); + KMArray.cast(arrPtr).add(tagIndex++, KMEnumTag.instance(KMType.ALGORITHM, alg)); + KMArray.cast(arrPtr).add(tagIndex++, purpose); + KMArray.cast(arrPtr).add(tagIndex++, KMBoolTag.instance(KMType.CALLER_NONCE)); + if(clientId != null)KMArray.cast(arrPtr).add(tagIndex++, + KMByteTag.instance(KMType.APPLICATION_ID, KMByteBlob.instance(clientId,(short)0,(short)clientId.length))); + if(appData != null)KMArray.cast(arrPtr).add(tagIndex++, + KMByteTag.instance(KMType.APPLICATION_DATA, KMByteBlob.instance(appData,(short)0,(short)appData.length))); + short keyParams = KMKeyParameters.instance(arrPtr); + arrPtr = KMArray.instance((short)1); + KMArray arg = KMArray.cast(arrPtr); + arg.add((short) 0, keyParams); + CommandAPDU apdu = encodeApdu((byte)0x10, arrPtr); + // print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(apdu); + short ret = KMArray.instance((short) 3); + KMArray.cast(ret).add((short) 0, KMInteger.exp()); + KMArray.cast(ret).add((short)1, KMByteBlob.exp()); + short inst = KMKeyCharacteristics.exp(); + KMArray.cast(ret).add((short) 2, inst); + byte[] respBuf = response.getBytes(); + short len = (short) respBuf.length; + ret = decoder.decode(ret, respBuf, (short) 0, len); + short error = KMInteger.cast(KMArray.cast(ret).get((short)0)).getShort(); + Assert.assertEquals(0x9000, response.getSW()); + Assert.assertEquals(error, KMError.OK); + return ret; } @Test @@ -643,4 +746,732 @@ public void testGetKeyCharacteristicsSuccess() { Assert.assertEquals(error, KMError.OK); cleanUp(); } + + @Test + public void testDeleteKeySuccess() { + init(); + short ret = generateRsaKey(null, null); + short keyBlobPtr = KMArray.cast(ret).get((short)1); + byte[] keyBlob = new byte[KMByteBlob.cast(keyBlobPtr).length()]; + short len = KMByteBlob.cast(keyBlobPtr).getValues(keyBlob, (short)0); + ret = getKeyCharacteristics(keyBlobPtr); + short error = KMInteger.cast(KMArray.cast(ret).get((short)0)).getShort(); + Assert.assertEquals(error, KMError.OK); + ret = deleteKey(KMByteBlob.instance(keyBlob,(short)0,(short)keyBlob.length)); + Assert.assertEquals(ret, KMError.OK); + ret = getKeyCharacteristics(KMByteBlob.instance(keyBlob,(short)0,(short)keyBlob.length)); + short err = KMByteBlob.cast(ret).get((short)1); + Assert.assertEquals(KMError.INVALID_KEY_BLOB,err); + cleanUp(); + } + + @Test + public void testDeleteAllKeySuccess() { + init(); + short ret1 = generateRsaKey(null, null); + short keyBlobPtr = KMArray.cast(ret1).get((short)1); + byte[] keyBlob1 = new byte[KMByteBlob.cast(keyBlobPtr).length()]; + short len = KMByteBlob.cast(keyBlobPtr).getValues(keyBlob1, (short)0); + short ret2 = generateRsaKey(null, null); + keyBlobPtr = KMArray.cast(ret2).get((short)1); + byte[] keyBlob2 = new byte[KMByteBlob.cast(keyBlobPtr).length()]; + len = KMByteBlob.cast(keyBlobPtr).getValues(keyBlob2, (short)0); + CommandAPDU apdu = new CommandAPDU(0x80, 0x17, 0x40, 0x00); + // print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(apdu); + byte[] respBuf = response.getBytes(); + Assert.assertEquals(respBuf[0], KMError.OK); + short ret = getKeyCharacteristics(KMByteBlob.instance(keyBlob1,(short)0,(short)keyBlob1.length)); + short err = KMByteBlob.cast(ret).get((short)1); + Assert.assertEquals(KMError.INVALID_KEY_BLOB,err); + ret = getKeyCharacteristics(KMByteBlob.instance(keyBlob2,(short)0,(short)keyBlob2.length)); + err = KMByteBlob.cast(ret).get((short)1); + Assert.assertEquals(KMError.INVALID_KEY_BLOB,err); + cleanUp(); + } + + private short deleteKey(short keyBlob) { + short arrPtr = KMArray.instance((short)1); + KMArray.cast(arrPtr).add((short)0, keyBlob); + CommandAPDU apdu = encodeApdu((byte)0x16, arrPtr); + // print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(apdu); + byte[] respBuf = response.getBytes(); + return respBuf[0]; + } + + private short abort(short opHandle) { + short arrPtr = KMArray.instance((short)1); + KMArray.cast(arrPtr).add((short)0, opHandle); + CommandAPDU apdu = encodeApdu((byte)0x22, arrPtr); + // print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(apdu); + byte[] respBuf = response.getBytes(); + return respBuf[0]; + } + + public short getKeyCharacteristics(short keyBlob){ + short arrPtr = KMArray.instance((short)3); + KMArray.cast(arrPtr).add((short)0, keyBlob); + KMArray.cast(arrPtr).add((short)1, KMByteBlob.instance((short)0)); + KMArray.cast(arrPtr).add((short)2, KMByteBlob.instance((short)0)); + CommandAPDU apdu = encodeApdu((byte)0x1D, arrPtr); + // print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(apdu); + short ret = KMArray.instance((short) 2); + KMArray.cast(ret).add((short) 0, KMInteger.exp()); + short inst = KMKeyCharacteristics.exp(); + KMArray.cast(ret).add((short) 1, inst); + byte[] respBuf = response.getBytes(); + short len = (short) respBuf.length; + if( len > 5) + ret = decoder.decode(ret, respBuf, (short) 0, len); + else + ret = KMByteBlob.instance(respBuf, (short)0, len); + return ret; + } + + + @Test + public void testWithAesEcbPkcs7WithUpdate(){ + init(); + testEncryptDecryptWithAesDes(KMType.AES, KMType.ECB, KMType.PKCS7,true); + cleanUp(); + } + @Test + public void testWithAesEcbNoPadWithUpdate(){ + init(); + testEncryptDecryptWithAesDes(KMType.AES, KMType.ECB, KMType.PADDING_NONE,true); + cleanUp(); + } + @Test + public void testWithDesEcbPkcs7WithUpdate(){ + init(); + testEncryptDecryptWithAesDes(KMType.DES, KMType.ECB, KMType.PKCS7,true); + cleanUp(); + } + @Test + public void testWithDesEcbNoPadWithUpdate(){ + init(); + testEncryptDecryptWithAesDes(KMType.DES, KMType.ECB, KMType.PADDING_NONE,true); + cleanUp(); + } + @Test + public void testWithAesCbcPkcs7WithUpdate(){ + init(); + testEncryptDecryptWithAesDes(KMType.AES, KMType.CBC, KMType.PKCS7,true); + cleanUp(); + } + @Test + public void testWithAesCbcNoPadWithUpdate(){ + init(); + testEncryptDecryptWithAesDes(KMType.AES, KMType.CBC, KMType.PADDING_NONE,true); + cleanUp(); + } + @Test + public void testWithDesCbcPkcs7WithUpdate(){ + init(); + testEncryptDecryptWithAesDes(KMType.DES, KMType.CBC, KMType.PKCS7,true); + cleanUp(); + } + @Test + public void testWithDesCbcNoPadWithUpdate(){ + init(); + testEncryptDecryptWithAesDes(KMType.DES, KMType.CBC, KMType.PADDING_NONE,true); + cleanUp(); + } + + @Test + public void testWithAesEcbPkcs7(){ + init(); + testEncryptDecryptWithAesDes(KMType.AES, KMType.ECB, KMType.PKCS7,false); + cleanUp(); + } + @Test + public void testWithAesCbcPkcs7(){ + init(); + testEncryptDecryptWithAesDes(KMType.AES, KMType.CBC, KMType.PKCS7,false); + cleanUp(); + } + @Test + public void testWithAesEcbNoPad(){ + init(); + testEncryptDecryptWithAesDes(KMType.AES, KMType.ECB, KMType.PADDING_NONE,false); + cleanUp(); + } + @Test + public void testWithAesCbcNoPad(){ + init(); + testEncryptDecryptWithAesDes(KMType.AES, KMType.CBC, KMType.PADDING_NONE,false); + cleanUp(); + } + @Test + public void testWithDesCbcPkcs7(){ + init(); + testEncryptDecryptWithAesDes(KMType.DES, KMType.CBC, KMType.PKCS7,false); + cleanUp(); + } + @Test + public void testWithDesCbcNoPad(){ + init(); + testEncryptDecryptWithAesDes(KMType.DES, KMType.CBC, KMType.PADDING_NONE,false); + cleanUp(); + } + @Test + public void testWithDesEcbNoPad(){ + init(); + testEncryptDecryptWithAesDes(KMType.DES, KMType.ECB, KMType.PADDING_NONE,false); + cleanUp(); + } + @Test + public void testWithDesEcbPkcs7(){ + init(); + testEncryptDecryptWithAesDes(KMType.DES, KMType.ECB, KMType.PKCS7,false); + cleanUp(); + } + + //TODO currently cannot test OAEP SHA256 based encryption/decryption as it is not supported by + // crypto provider +/* @Test + public void testWithRsa256Oaep(){ + init(); + testEncryptDecryptWithRsa(KMType.SHA2_256, KMType.RSA_OAEP); + cleanUp(); + } +*/ + @Test + public void testWithRsaNonePkcs1(){ + init(); + testEncryptDecryptWithRsa(KMType.DIGEST_NONE, KMType.RSA_PKCS1_1_5_ENCRYPT); + cleanUp(); + } + @Test + public void testWithRsaNoneNoPad(){ + init(); + testEncryptDecryptWithRsa(KMType.DIGEST_NONE, KMType.PADDING_NONE); + cleanUp(); + } + + /* + // TODO Signing with no digest is not supported by crypto provider or javacard + @Test + public void testSignVerifyWithRsaNoneNoPad(){ + init(); + testSignVerifyWithRsa(KMType.DIGEST_NONE, KMType.RSA_PKCS1_1_5_SIGN); + cleanUp(); + } + @Test + public void testSignVerifyWithRsaNonePkcs1(){ + init(); + testSignVerifyWithRsa(KMType.DIGEST_NONE, KMType.RSA_PKCS1_1_5_SIGN); + cleanUp(); + }*/ + + @Test + public void testSignVerifyWithHmacSHA256WithUpdate(){ + init(); + testSignVerifyWithHmac(KMType.SHA2_256, true); + cleanUp(); + } + @Test + public void testSignVerifyWithHmacSHA256(){ + init(); + testSignVerifyWithHmac(KMType.SHA2_256, false); + cleanUp(); + } + + @Test + public void testSignVerifyWithEcdsaSHA256WithUpdate(){ + init(); + testSignVerifyWithEcdsa(KMType.SHA2_256, true); + cleanUp(); + } + @Test + public void testSignVerifyWithEcdsaSHA256(){ + init(); + testSignVerifyWithEcdsa(KMType.SHA2_256, false); + cleanUp(); + } + @Test + public void testSignVerifyWithRsaSHA256Pkcs1(){ + init(); + testSignVerifyWithRsa(KMType.SHA2_256, KMType.RSA_PKCS1_1_5_SIGN,false); + cleanUp(); + } + @Test + public void testSignVerifyWithRsaSHA256Pss(){ + init(); + testSignVerifyWithRsa(KMType.SHA2_256, KMType.RSA_PSS,false); + cleanUp(); + } + @Test + public void testSignVerifyWithRsaSHA256Pkcs1WithUpdate(){ + init(); + testSignVerifyWithRsa(KMType.SHA2_256, KMType.RSA_PKCS1_1_5_SIGN,true); + cleanUp(); + } + + @Test + public void testUpgradeKey(){ + init(); + short ret = generateHmacKey(null, null); + short keyBlobPtr = KMArray.cast(ret).get((short)1); + byte[] keyBlob= new byte[KMByteBlob.cast(keyBlobPtr).length()]; + Util.arrayCopyNonAtomic(KMByteBlob.cast(keyBlobPtr).getBuffer(), KMByteBlob.cast(keyBlobPtr).getStartOff(), + keyBlob,(short)0, (short)keyBlob.length); + short keyCharacteristics = KMArray.cast(ret).get((short)2); + short hwParams = KMKeyCharacteristics.cast(keyCharacteristics).getHardwareEnforced(); + short swParams = KMKeyCharacteristics.cast(keyCharacteristics).getSoftwareEnforced(); + short osVersion = KMKeyParameters.findTag(KMType.UINT_TAG,KMType.OS_VERSION,hwParams); + osVersion = KMIntegerTag.cast(osVersion).getValue(); + short osPatch = KMKeyParameters.findTag(KMType.UINT_TAG,KMType.OS_PATCH_LEVEL,hwParams); + osPatch = KMIntegerTag.cast(osPatch).getValue(); + Assert.assertEquals(KMInteger.cast(osVersion).getShort(), 1); + Assert.assertEquals(KMInteger.cast(osPatch).getShort(), 1); + setBootParams(simulator,(short) 2,(short)2); + ret = upgradeKey(KMByteBlob.instance(keyBlob, (short)0, (short)keyBlob.length),null, null); + keyBlobPtr = KMArray.cast(ret).get((short)1); + ret = getKeyCharacteristics(keyBlobPtr); + keyCharacteristics = KMArray.cast(ret).get((short)1); + hwParams = KMKeyCharacteristics.cast(keyCharacteristics).getHardwareEnforced(); + osVersion = KMKeyParameters.findTag(KMType.UINT_TAG,KMType.OS_VERSION,hwParams); + osVersion = KMIntegerTag.cast(osVersion).getValue(); + osPatch = KMKeyParameters.findTag(KMType.UINT_TAG,KMType.OS_PATCH_LEVEL,hwParams); + osPatch = KMIntegerTag.cast(osPatch).getValue(); + Assert.assertEquals(KMInteger.cast(osVersion).getShort(), 2); + Assert.assertEquals(KMInteger.cast(osPatch).getShort(), 2); + cleanUp(); + } + private short upgradeKey(short keyBlobPtr, byte[] clientId, byte[] appData){ + short tagCount = 0; + short clientIdTag = 0; + short appDataTag = 0; + if(clientId != null) tagCount++; + if(appData != null) tagCount++; + short keyParams = KMArray.instance(tagCount); + short tagIndex=0; + if(clientId != null)KMArray.cast(keyBlobPtr).add(tagIndex++, + KMByteTag.instance(KMType.APPLICATION_ID, KMByteBlob.instance(clientId,(short)0,(short)clientId.length))); + if(appData != null)KMArray.cast(keyParams).add(tagIndex++, + KMByteTag.instance(KMType.APPLICATION_DATA, KMByteBlob.instance(appData,(short)0,(short)appData.length))); + keyParams = KMKeyParameters.instance(keyParams); + short arr = KMArray.instance((short)2); + KMArray.cast(arr).add((short)0,keyBlobPtr); + KMArray.cast(arr).add((short)1,keyParams); + CommandAPDU apdu = encodeApdu((byte)0x15, arr); + // print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(apdu); + short ret = KMArray.instance((short) 2); + KMArray.cast(ret).add((short) 0, KMInteger.exp()); + KMArray.cast(ret).add((short)1, KMByteBlob.exp()); + byte[] respBuf = response.getBytes(); + short len = (short) respBuf.length; + ret = decoder.decode(ret, respBuf, (short) 0, len); + short error = KMInteger.cast(KMArray.cast(ret).get((short)0)).getShort(); + Assert.assertEquals(error, KMError.OK); + return ret; + } + @Test + public void testSignVerifyWithRsaSHA256PssWithUpdate(){ + init(); + testSignVerifyWithRsa(KMType.SHA2_256, KMType.RSA_PSS,true); + cleanUp(); + } + @Test + public void testAbortOperation(){ + init(); + short aesDesKeyArr = generateAesDesKey(KMType.AES, (short)128,null, null);; + short keyBlobPtr = KMArray.cast(aesDesKeyArr).get((short)1); + byte[] keyBlob= new byte[KMByteBlob.cast(keyBlobPtr).length()]; + Util.arrayCopyNonAtomic(KMByteBlob.cast(keyBlobPtr).getBuffer(), KMByteBlob.cast(keyBlobPtr).getStartOff(), + keyBlob,(short)0, (short)keyBlob.length); + byte[] nonce = new byte[16]; + cryptoProvider.newRandomNumber(nonce,(short)0,(short)16); + short inParams = getAesDesParams(KMType.AES,KMType.ECB, KMType.PKCS7, nonce); + byte[] plainData= "Hello World 123!".getBytes(); + short ret = begin(KMType.ENCRYPT, KMByteBlob.instance(keyBlob,(short)0, (short)keyBlob.length), KMKeyParameters.instance(inParams), (short)0); + short opHandle = KMArray.cast(ret).get((short) 2); + opHandle = KMInteger.cast(opHandle).getShort(); + abort(KMInteger.uint_16(opHandle)); + short dataPtr = KMByteBlob.instance(plainData, (short) 0, (short) plainData.length); + ret = KMType.INVALID_VALUE; + ret = update(KMInteger.uint_16(opHandle), dataPtr, (short) 0, (short) 0, (short) 0); + Assert.assertEquals(KMError.INVALID_OPERATION_HANDLE,ret); + cleanUp(); + } + + public void testEncryptDecryptWithAesDes(byte alg, byte blockMode, byte padding, boolean update){ + short aesDesKeyArr; + if(alg == KMType.AES){ + aesDesKeyArr = generateAesDesKey(alg, (short)128,null, null); + } else{ + aesDesKeyArr = generateAesDesKey(alg, (short)168,null, null); + } + short keyBlobPtr = KMArray.cast(aesDesKeyArr).get((short)1); + byte[] keyBlob= new byte[KMByteBlob.cast(keyBlobPtr).length()]; + Util.arrayCopyNonAtomic(KMByteBlob.cast(keyBlobPtr).getBuffer(), KMByteBlob.cast(keyBlobPtr).getStartOff(), + keyBlob,(short)0, (short)keyBlob.length); + byte[] nonce = new byte[16]; + cryptoProvider.newRandomNumber(nonce,(short)0,(short)16); + short inParams = getAesDesParams(alg,blockMode, padding, nonce); + byte[] plainData= "Hello World 123!".getBytes(); + if(update) plainData= "Hello World 123! Hip Hip Hoorah!".getBytes(); + //Encrypt + short ret = processMessage(plainData, + KMByteBlob.instance(keyBlob,(short)0, (short)keyBlob.length), + KMType.ENCRYPT, + KMKeyParameters.instance(inParams), + (short)0,null,update + ); + inParams = getAesDesParams(alg,blockMode, padding, nonce); + keyBlobPtr = KMArray.cast(ret).get((short)2); + byte[] cipherData = new byte[KMByteBlob.cast(keyBlobPtr).length()]; + Util.arrayCopyNonAtomic(KMByteBlob.cast(keyBlobPtr).getBuffer(), KMByteBlob.cast(keyBlobPtr).getStartOff(), + cipherData,(short)0, (short)cipherData.length); + ret = processMessage(cipherData, + KMByteBlob.instance(keyBlob,(short)0, (short)keyBlob.length), + KMType.DECRYPT, + KMKeyParameters.instance(inParams), + (short)0,null,update + ); + keyBlobPtr = KMArray.cast(ret).get((short)2); + short equal = Util.arrayCompare(plainData,(short)0,KMByteBlob.cast(keyBlobPtr).getBuffer(), + KMByteBlob.cast(keyBlobPtr).getStartOff(),(short)plainData.length); + Assert.assertTrue(equal == 0); + } + + public void testEncryptDecryptWithRsa(byte digest, byte padding){ + short rsaKeyArr = generateRsaKey(null, null); + short keyBlobPtr = KMArray.cast(rsaKeyArr).get((short)1); + byte[] keyBlob= new byte[KMByteBlob.cast(keyBlobPtr).length()]; + Util.arrayCopyNonAtomic(KMByteBlob.cast(keyBlobPtr).getBuffer(), KMByteBlob.cast(keyBlobPtr).getStartOff(), + keyBlob,(short)0, (short)keyBlob.length); + short inParams = getRsaParams(digest, padding); + byte[] plainData = "Hello World 123!".getBytes(); + //Encrypt + short ret = processMessage(plainData, + KMByteBlob.instance(keyBlob,(short)0, (short)keyBlob.length), + KMType.ENCRYPT, + KMKeyParameters.instance(inParams), + (short)0,null,false + ); + inParams = getRsaParams(digest, padding); + keyBlobPtr = KMArray.cast(ret).get((short)2); + byte[] cipherData = new byte[KMByteBlob.cast(keyBlobPtr).length()]; + Util.arrayCopyNonAtomic(KMByteBlob.cast(keyBlobPtr).getBuffer(), KMByteBlob.cast(keyBlobPtr).getStartOff(), + cipherData,(short)0, (short)cipherData.length); + ret = processMessage(cipherData, + KMByteBlob.instance(keyBlob,(short)0, (short)keyBlob.length), + KMType.DECRYPT, + KMKeyParameters.instance(inParams), + (short)0,null,false + ); + keyBlobPtr = KMArray.cast(ret).get((short)2); + short equal = Util.arrayCompare(plainData,(short)0,KMByteBlob.cast(keyBlobPtr).getBuffer(), + KMByteBlob.cast(keyBlobPtr).getStartOff(),(short)plainData.length); + Assert.assertTrue(equal == 0); + } + + public void testSignVerifyWithRsa(byte digest, byte padding, boolean update){ + short rsaKeyArr = generateRsaKey(null, null); + short keyBlobPtr = KMArray.cast(rsaKeyArr).get((short)1); + byte[] keyBlob= new byte[KMByteBlob.cast(keyBlobPtr).length()]; + Util.arrayCopyNonAtomic(KMByteBlob.cast(keyBlobPtr).getBuffer(), KMByteBlob.cast(keyBlobPtr).getStartOff(), + keyBlob,(short)0, (short)keyBlob.length); + short inParams = getRsaParams(digest, padding); + byte[] plainData = "Hello World 123!".getBytes(); + if(update) plainData= "Hello World 123! Hip Hip Hoorah!".getBytes(); + //Sign + short ret = processMessage(plainData, + KMByteBlob.instance(keyBlob,(short)0, (short)keyBlob.length), + KMType.SIGN, + KMKeyParameters.instance(inParams), + (short)0,null,update + ); + inParams = getRsaParams(digest, padding); + keyBlobPtr = KMArray.cast(ret).get((short)2); + byte[] signatureData = new byte[KMByteBlob.cast(keyBlobPtr).length()]; + Util.arrayCopyNonAtomic(KMByteBlob.cast(keyBlobPtr).getBuffer(), KMByteBlob.cast(keyBlobPtr).getStartOff(), + signatureData,(short)0, (short)signatureData.length); + ret = processMessage(plainData, + KMByteBlob.instance(keyBlob,(short)0, (short)keyBlob.length), + KMType.VERIFY, + KMKeyParameters.instance(inParams), + (short)0,signatureData,update + ); + short error = KMInteger.cast(KMArray.cast(ret).get((short)0)).getShort(); + Assert.assertEquals(error, KMError.OK); + } + + public void testSignVerifyWithEcdsa(byte digest, boolean update){ + short ecKeyArr = generateEcKey(null, null); + short keyBlobPtr = KMArray.cast(ecKeyArr).get((short)1); + byte[] keyBlob= new byte[KMByteBlob.cast(keyBlobPtr).length()]; + Util.arrayCopyNonAtomic(KMByteBlob.cast(keyBlobPtr).getBuffer(), KMByteBlob.cast(keyBlobPtr).getStartOff(), + keyBlob,(short)0, (short)keyBlob.length); + short inParams = getEcParams(digest); + byte[] plainData = "Hello World 123!".getBytes(); + if(update) plainData= "Hello World 123! Hip Hip Hoorah!".getBytes(); + //Sign + short ret = processMessage(plainData, + KMByteBlob.instance(keyBlob,(short)0, (short)keyBlob.length), + KMType.SIGN, + KMKeyParameters.instance(inParams), + (short)0,null,update + ); + inParams = getEcParams(digest); + keyBlobPtr = KMArray.cast(ret).get((short)2); + byte[] signatureData = new byte[KMByteBlob.cast(keyBlobPtr).length()]; + Util.arrayCopyNonAtomic(KMByteBlob.cast(keyBlobPtr).getBuffer(), KMByteBlob.cast(keyBlobPtr).getStartOff(), + signatureData,(short)0, (short)signatureData.length); + ret = processMessage(plainData, + KMByteBlob.instance(keyBlob,(short)0, (short)keyBlob.length), + KMType.VERIFY, + KMKeyParameters.instance(inParams), + (short)0,signatureData,update + ); + short error = KMInteger.cast(KMArray.cast(ret).get((short)0)).getShort(); + Assert.assertEquals(error, KMError.OK); + } + public void testSignVerifyWithHmac(byte digest, boolean update){ + short hmacKeyArr = generateHmacKey(null, null); + short keyBlobPtr = KMArray.cast(hmacKeyArr).get((short)1); + byte[] keyBlob= new byte[KMByteBlob.cast(keyBlobPtr).length()]; + Util.arrayCopyNonAtomic(KMByteBlob.cast(keyBlobPtr).getBuffer(), KMByteBlob.cast(keyBlobPtr).getStartOff(), + keyBlob,(short)0, (short)keyBlob.length); + short inParams = getHmacParams(digest); + byte[] plainData = "Hello World 123!".getBytes(); + if(update) plainData= "Hello World 123! Hip Hip Hoorah!".getBytes(); + //Sign + short ret = processMessage(plainData, + KMByteBlob.instance(keyBlob,(short)0, (short)keyBlob.length), + KMType.SIGN, + KMKeyParameters.instance(inParams), + (short)0,null,update + ); + inParams = getHmacParams(digest); + keyBlobPtr = KMArray.cast(ret).get((short)2); + byte[] signatureData = new byte[KMByteBlob.cast(keyBlobPtr).length()]; + Util.arrayCopyNonAtomic(KMByteBlob.cast(keyBlobPtr).getBuffer(), KMByteBlob.cast(keyBlobPtr).getStartOff(), + signatureData,(short)0, (short)signatureData.length); + ret = processMessage(plainData, + KMByteBlob.instance(keyBlob,(short)0, (short)keyBlob.length), + KMType.VERIFY, + KMKeyParameters.instance(inParams), + (short)0,signatureData,update + ); + short error = KMInteger.cast(KMArray.cast(ret).get((short)0)).getShort(); + Assert.assertEquals(error, KMError.OK); + } + + private short getAesDesParams(byte alg, byte blockMode, byte padding, byte[] nonce) { + short inParams; + if(blockMode == KMType.ECB){ + inParams = KMArray.instance((short)2); + short byteBlob = KMByteBlob.instance((short)1); + KMByteBlob.cast(byteBlob).add((short)0, blockMode); + KMArray.cast(inParams).add((short)0, KMEnumArrayTag.instance(KMType.BLOCK_MODE, byteBlob)); + byteBlob = KMByteBlob.instance((short)1); + KMByteBlob.cast(byteBlob).add((short)0, padding); + KMArray.cast(inParams).add((short)1, KMEnumArrayTag.instance(KMType.PADDING, byteBlob)); + }else{ + inParams = KMArray.instance((short)3); + short byteBlob = KMByteBlob.instance((short)1); + KMByteBlob.cast(byteBlob).add((short)0, blockMode); + KMArray.cast(inParams).add((short)0, KMEnumArrayTag.instance(KMType.BLOCK_MODE, byteBlob)); + byteBlob = KMByteBlob.instance((short)1); + KMByteBlob.cast(byteBlob).add((short)0, padding); + KMArray.cast(inParams).add((short)1, KMEnumArrayTag.instance(KMType.PADDING, byteBlob)); + short nonceLen = 16; + if(alg == KMType.DES) nonceLen = 8; + byteBlob = KMByteBlob.instance(nonce,(short)0, nonceLen); + KMArray.cast(inParams).add((short)2, KMByteTag.instance(KMType.NONCE, byteBlob)); + } + return inParams; + } + + private short getRsaParams(byte digest, byte padding) { + short inParams = KMArray.instance((short)2); + short byteBlob = KMByteBlob.instance((short)1); + KMByteBlob.cast(byteBlob).add((short)0, digest); + KMArray.cast(inParams).add((short)0, KMEnumArrayTag.instance(KMType.DIGEST, byteBlob)); + byteBlob = KMByteBlob.instance((short)1); + KMByteBlob.cast(byteBlob).add((short)0, padding); + KMArray.cast(inParams).add((short)1, KMEnumArrayTag.instance(KMType.PADDING, byteBlob)); + return inParams; + } + + private short getEcParams(byte digest) { + short inParams = KMArray.instance((short)1); + short byteBlob = KMByteBlob.instance((short)1); + KMByteBlob.cast(byteBlob).add((short)0, digest); + KMArray.cast(inParams).add((short)0, KMEnumArrayTag.instance(KMType.DIGEST, byteBlob)); + return inParams; + } + private short getHmacParams(byte digest) { + short inParams = KMArray.instance((short)2); + short byteBlob = KMByteBlob.instance((short)1); + KMByteBlob.cast(byteBlob).add((short)0, digest); + KMArray.cast(inParams).add((short)0, KMEnumArrayTag.instance(KMType.DIGEST, byteBlob)); + short macLength = KMIntegerTag.instance(KMType.UINT_TAG,KMType.MAC_LENGTH, KMInteger.uint_16((short)256)); + KMArray.cast(inParams).add((short)1, macLength); + return inParams; + } + + public short processMessage( + byte[] data, + short keyBlob, + byte keyPurpose, + short inParams, + short hwToken, + byte[] signature, + boolean updateFlag) { + short beginResp = begin(keyPurpose, keyBlob, inParams, hwToken); + short opHandle = KMArray.cast(beginResp).get((short) 2); + opHandle = KMInteger.cast(opHandle).getShort(); + short dataPtr = KMByteBlob.instance(data, (short) 0, (short) data.length); + short ret = KMType.INVALID_VALUE; + byte[] outputData = new byte[128]; + short len=0; + if (updateFlag) { + dataPtr = KMByteBlob.instance(data, (short) 0, (short) 16); + ret = update(KMInteger.uint_16(opHandle), dataPtr, (short) 0, (short) 0, (short) 0); + dataPtr = KMArray.cast(ret).get((short) 3); + if (KMByteBlob.cast(dataPtr).length() > 0) { + Util.arrayCopyNonAtomic( + KMByteBlob.cast(dataPtr).getBuffer(), + KMByteBlob.cast(dataPtr).getStartOff(), + outputData, + (short) 0, + KMByteBlob.cast(dataPtr).length()); + len = KMByteBlob.cast(dataPtr).length(); + dataPtr = KMByteBlob.instance(data, len, (short) (data.length - len)); + }else{ + dataPtr = KMByteBlob.instance(data, (short)16, (short) (data.length - 16)); + } + } + + if (keyPurpose == KMType.VERIFY) { + ret = finish(KMInteger.uint_16(opHandle), dataPtr, signature, (short) 0, (short) 0, (short) 0); + } else { + ret = finish(KMInteger.uint_16(opHandle), dataPtr, null, (short) 0, (short) 0, (short) 0); + } + if(len >0){ + dataPtr = KMArray.cast(ret).get((short)2); + if(KMByteBlob.cast(dataPtr).length() >0){ + Util.arrayCopyNonAtomic( + KMByteBlob.cast(dataPtr).getBuffer(), + KMByteBlob.cast(dataPtr).getStartOff(), + outputData, + len, + KMByteBlob.cast(dataPtr).length()); + len = (short)(len + KMByteBlob.cast(dataPtr).length()); + } + KMArray.cast(ret).add((short)2, KMByteBlob.instance(outputData,(short)0,len)); + } + return ret; + } + + public short begin(byte keyPurpose, short keyBlob, short keyParmas, short hwToken) { + short arrPtr = KMArray.instance((short)4); + KMArray.cast(arrPtr).add((short)0, KMEnum.instance(KMType.PURPOSE, keyPurpose)); + KMArray.cast(arrPtr).add((short)1, keyBlob); + KMArray.cast(arrPtr).add((short)2, keyParmas); + if(hwToken == 0) { + hwToken = KMHardwareAuthToken.instance(); + } + KMArray.cast(arrPtr).add((short)3, hwToken); + CommandAPDU apdu = encodeApdu((byte)0x1F, arrPtr); + // print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(apdu); + short ret = KMArray.instance((short) 3); + short outParams = KMKeyParameters.exp(); + KMArray.cast(ret).add((short)0, KMInteger.exp()); + KMArray.cast(ret).add((short)1, outParams); + KMArray.cast(ret).add((short)2, KMInteger.exp()); + byte[] respBuf = response.getBytes(); + short len = (short) respBuf.length; + ret = decoder.decode(ret, respBuf, (short) 0, len); + short error = KMInteger.cast(KMArray.cast(ret).get((short)0)).getShort(); + Assert.assertEquals(error, KMError.OK); + return ret; + } + + public short finish(short operationHandle, short data, byte[] signature, short inParams, short hwToken, short verToken) { + if(hwToken == 0) { + hwToken = KMHardwareAuthToken.instance(); + } + if(verToken == 0){ + verToken = KMVerificationToken.instance(); + } + short signatureTag; + if(signature == null){ + signatureTag = KMByteBlob.instance((short)0); + }else{ + signatureTag = KMByteBlob.instance(signature,(short)0,(short)signature.length); + } + if(inParams == 0){ + short arr = KMArray.instance((short)0); + inParams = KMKeyParameters.instance(arr); + } + short arrPtr = KMArray.instance((short)6); + KMArray.cast(arrPtr).add((short)0, operationHandle); + KMArray.cast(arrPtr).add((short)1, inParams); + KMArray.cast(arrPtr).add((short)2, data); + KMArray.cast(arrPtr).add((short)3, signatureTag); + KMArray.cast(arrPtr).add((short)4, hwToken); + KMArray.cast(arrPtr).add((short)5, verToken); + CommandAPDU apdu = encodeApdu((byte)0x21, arrPtr); + // print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(apdu); + short ret = KMArray.instance((short) 3); + short outParams = KMKeyParameters.exp(); + KMArray.cast(ret).add((short)0, KMInteger.exp()); + KMArray.cast(ret).add((short)1, outParams); + KMArray.cast(ret).add((short)2, KMByteBlob.exp()); + byte[] respBuf = response.getBytes(); + short len = (short) respBuf.length; + ret = decoder.decode(ret, respBuf, (short) 0, len); + short error = KMInteger.cast(KMArray.cast(ret).get((short)0)).getShort(); + Assert.assertEquals(error, KMError.OK); + return ret; + } + public short update(short operationHandle, short data, short inParams, short hwToken, short verToken) { + if(hwToken == 0) { + hwToken = KMHardwareAuthToken.instance(); + } + if(verToken == 0){ + verToken = KMVerificationToken.instance(); + } + if(inParams == 0){ + short arr = KMArray.instance((short)0); + inParams = KMKeyParameters.instance(arr); + } + short arrPtr = KMArray.instance((short)5); + KMArray.cast(arrPtr).add((short)0, operationHandle); + KMArray.cast(arrPtr).add((short)1, inParams); + KMArray.cast(arrPtr).add((short)2, data); + KMArray.cast(arrPtr).add((short)3, hwToken); + KMArray.cast(arrPtr).add((short)4, verToken); + CommandAPDU apdu = encodeApdu((byte)0x20, arrPtr); + // print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(apdu); + short ret = KMArray.instance((short) 4); + short outParams = KMKeyParameters.exp(); + KMArray.cast(ret).add((short)0, KMInteger.exp()); + KMArray.cast(ret).add((short)1, KMInteger.exp()); + KMArray.cast(ret).add((short)2, outParams); + KMArray.cast(ret).add((short)3, KMByteBlob.exp()); + byte[] respBuf = response.getBytes(); + short len = (short) respBuf.length; + if (len > 5) { + ret = decoder.decode(ret, respBuf, (short) 0, len); + short error = KMInteger.cast(KMArray.cast(ret).get((short) 0)).getShort(); + Assert.assertEquals(error, KMError.OK); + }else{ + ret = respBuf[1]; + } + return ret; + } + } diff --git a/Applet/default.output b/Applet/default.output index 465ff419..6d9f7ea9 100644 --- a/Applet/default.output +++ b/Applet/default.output @@ -9,12 +9,12 @@ CLA: 80, INS: 24, P1: 40, P2: 00, Lc: 49, 86, 01, 01, 58, 20, 30, 30, 30, 31, 31 CLA: 80, INS: 1e, P1: 40, P2: 00, Lc: 00, Le: 21, 83, 02, 57, 4a, 61, 76, 61, 63, 61, 72, 64, 4b, 65, 79, 6d, 61, 73, 74, 65, 72, 44, 65, 76, 69, 63, 65, 46, 47, 6f, 6f, 67, 6c, 65, SW1: 90, SW2: 00 CLA: 80, INS: 18, P1: 40, P2: 00, Lc: 23, 81, 58, 20, 00, 01, 02, 03, 04, 05, 06, 07, 08, 09, 0a, 0b, 0c, 0d, 0e, 0f, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 1a, 1b, 1c, 1d, 1e, 1f, Le: 00, SW1: 90, SW2: 00 CLA: 80, INS: 10, P1: 40, P2: 00, Lc: 22, 81, a4, 1a, 10, 00, 00, 02, 01, 1a, 30, 00, 00, 03, 19, 08, 00, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 20, 00, 00, 01, 42, 02, 03, Le: 0d, 83, 00, 58, d4, 85, 58, 40, dc, 1d, ed, 50, 93, 56, 0c, 97, de, e7, 37, 82, 41, be, cc, 97, 19, 46, 6f, 18, 3e, 55, 1d, 95, b5, a2, 32, d6, 8d, c3, f9, ab, 16, e5, 8d, 7f, 8a, fe, 09, 86, f0, 83, 04, 50, ef, e1, 8d, 4e, d3, a9, 53, 99, b8, d4, bc, ad, 8f, a2, 5e, 8a, 84, 1a, ea, 29, 4c, 00, 47, 73, 4c, ad, c1, 48, a8, 52, e8, 13, dd, 4c, 48, c7, 53, a4, 74, a5, 3d, 80, 5c, 1a, ab, 92, 82, a0, a7, 1a, 10, 00, 00, 02, 01, 1a, 30, 00, 00, 03, 19, 08, 00, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 20, 00, 00, 01, 42, 02, 03, 1a, 10, 00, 02, be, 00, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, 58, 40, b4, 05, c4, 0e, 08, 15, 0e, a5, 86, 84, b2, 03, 00, 2c, cb, d7, 23, 85, c9, c1, 0b, 74, cb, 20, 10, b1, 25, 8e, 4b, 38, d4, 72, da, 2c, 2c, 64, 0e, e9, 2f, b2, d6, 74, 2e, 02, 77, 69, 74, 9f, a9, 98, e7, 7a, 4f, 9e, d4, 06, 62, 0c, 33, 6e, 80, 3a, 3f, e3, 82, a0, a7, 1a, 10, 00, 00, 02, 01, 1a, 30, 00, 00, 03, 19, 08, 00, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 20, 00, 00, 01, 42, 02, 03, 1a, 10, 00, 02, be, 00, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, SW1: 90, SW2: 00 -CLA: 80, INS: 10, P1: 40, P2: 00, Lc: 23, 81, a4, 1a, 10, 00, 00, 02, 18, 20, 1a, 30, 00, 00, 03, 19, 01, 00, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 20, 00, 00, 01, 42, 02, 03, Le: 9c, 83, 00, 58, 62, 84, 50, c9, a3, b8, 91, fe, 89, d9, f0, 6e, 62, 84, 1e, 4c, 60, eb, e3, 4c, e3, 82, 9d, 1c, ad, 27, 96, 78, 29, 11, 7e, 2e, 4c, cd, 00, 01, 7c, aa, 9d, 76, 28, 1f, 0a, e0, a0, 82, a0, a7, 1a, 10, 00, 00, 02, 18, 20, 1a, 30, 00, 00, 03, 19, 01, 00, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 20, 00, 00, 01, 42, 02, 03, 1a, 10, 00, 02, be, 00, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, 82, a0, a7, 1a, 10, 00, 00, 02, 18, 20, 1a, 30, 00, 00, 03, 19, 01, 00, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 20, 00, 00, 01, 42, 02, 03, 1a, 10, 00, 02, be, 00, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, SW1: 90, SW2: 00 -CLA: 80, INS: 10, P1: 40, P2: 00, Lc: 22, 81, a4, 1a, 10, 00, 00, 02, 03, 1a, 30, 00, 00, 03, 19, 01, 00, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 20, 00, 00, 01, 42, 02, 03, Le: d6, 83, 00, 58, 9d, 85, 58, 18, ba, 56, b4, 85, 20, 57, a9, b9, 77, 65, ba, e3, f4, 43, e6, 97, 70, fc, 3f, 49, 40, 93, 4e, 36, 4c, ae, 8e, 3e, 17, 1d, 67, 15, cc, 16, e8, f2, c4, 4c, 62, 66, 94, d1, 12, 0f, b0, 59, e4, c4, 1b, 91, 82, a0, a7, 1a, 10, 00, 00, 02, 03, 1a, 30, 00, 00, 03, 19, 01, 00, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 20, 00, 00, 01, 42, 02, 03, 1a, 10, 00, 02, be, 00, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, 58, 31, 04, 5a, 10, b8, d7, e9, be, 0a, e9, 47, 6c, 79, e4, 64, 15, 9c, 9d, 32, c6, 74, dd, 17, 11, 61, b7, 9a, 0c, 57, 8e, a1, 5f, 64, 1e, 8d, 51, 5b, 1e, 19, 4e, cc, 0e, 33, 15, d0, 9e, f6, f5, 8d, 2e, 82, a0, a7, 1a, 10, 00, 00, 02, 03, 1a, 30, 00, 00, 03, 19, 01, 00, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 20, 00, 00, 01, 42, 02, 03, 1a, 10, 00, 02, be, 00, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, SW1: 90, SW2: 00 -CLA: 80, INS: 10, P1: 40, P2: 00, Lc: 22, 81, a4, 1a, 10, 00, 00, 02, 18, 21, 1a, 30, 00, 00, 03, 18, a8, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 20, 00, 00, 01, 42, 02, 03, Le: 9a, 83, 00, 58, 61, 84, 50, ac, 61, 52, d1, e0, b1, 60, 86, 83, 04, f0, 27, 26, 82, 33, 5d, 4c, 70, dd, 86, 23, 76, 5a, 88, cb, f1, 89, 32, 38, 4c, 81, fb, d7, 08, c6, b7, 3d, c3, 4c, 4d, f2, 7f, 82, a0, a7, 1a, 10, 00, 00, 02, 18, 21, 1a, 30, 00, 00, 03, 18, a8, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 20, 00, 00, 01, 42, 02, 03, 1a, 10, 00, 02, be, 00, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, 82, a0, a7, 1a, 10, 00, 00, 02, 18, 21, 1a, 30, 00, 00, 03, 18, a8, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 20, 00, 00, 01, 42, 02, 03, 1a, 10, 00, 02, be, 00, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, SW1: 90, SW2: 00 -CLA: 80, INS: 10, P1: 40, P2: 00, Lc: 32, 81, a6, 1a, 10, 00, 00, 02, 18, 80, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, 1a, 90, 00, 02, 59, 44, 54, 65, 73, 74, 1a, 30, 00, 00, 08, 18, 80, 1a, 30, 00, 00, 03, 18, 80, 1a, 20, 00, 00, 05, 41, 04, Le: a6, 83, 00, 58, 67, 84, 50, b2, 44, 46, 9e, 6a, 14, 04, 41, a6, 58, 9c, 6f, 8b, ae, ed, 82, 4c, 5e, eb, 90, 37, 7a, 7b, f8, 61, 77, 30, 6c, 79, 4c, 0e, b8, 7e, 23, 1c, 0f, d8, af, b1, 97, 98, d6, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a7, 1a, 10, 00, 00, 02, 18, 80, 1a, 30, 00, 00, 08, 18, 80, 1a, 30, 00, 00, 03, 18, 80, 1a, 20, 00, 00, 05, 41, 04, 1a, 10, 00, 02, be, 00, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a7, 1a, 10, 00, 00, 02, 18, 80, 1a, 30, 00, 00, 08, 18, 80, 1a, 30, 00, 00, 03, 18, 80, 1a, 20, 00, 00, 05, 41, 04, 1a, 10, 00, 02, be, 00, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, SW1: 90, SW2: 00 -CLA: 80, INS: 11, P1: 40, P2: 00, Lc: b4, 83, a5, 1a, 10, 00, 00, 02, 01, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, 1a, 90, 00, 02, 59, 44, 54, 65, 73, 74, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 70, 00, 01, f7, 01, 03, 58, 85, 82, 58, 40, 80, 9a, 87, 4c, e1, a0, 71, 44, ae, 45, dd, 8d, 1c, 05, b4, d0, 44, 23, dd, 42, a7, c9, 53, 44, ac, 31, 4a, 22, 4a, 02, 65, a0, aa, 21, a8, 30, 94, 7d, 13, a1, bc, 89, 81, b5, 54, de, 75, 82, b9, 0b, 1a, 7a, 81, 0c, 51, e0, 2f, 91, 97, d4, e8, 33, 27, 61, 58, 40, 92, 6c, 79, 17, bb, 36, 6f, b7, 58, 25, 84, 98, a9, 56, 07, e6, 07, f6, 26, 92, 15, f6, 21, 9f, 6c, f0, b4, e7, 20, 42, ac, b6, d8, 30, 61, 06, c9, 3b, 30, 67, 1e, 8d, 74, 11, 8b, 06, 98, ab, 8d, 6a, 6c, cd, b7, 2f, c3, a8, 30, c7, 68, 03, 4f, 72, c7, 5b, Le: 1d, 83, 00, 58, dc, 85, 58, 40, ee, 9c, 17, 33, 10, 19, 8c, 42, 68, 14, dc, e7, d4, ac, 6e, 86, 74, af, 8c, 02, 9d, 9c, fb, f7, be, 3c, d4, bd, de, 9d, d8, fd, 51, eb, d8, df, fd, ce, a0, 9b, ff, 44, a9, a2, e8, eb, 44, 78, eb, 15, 04, ca, 6a, 98, 07, 7c, bb, ab, 07, be, 72, a0, f9, 1f, 4c, 74, b7, f8, 50, 81, 15, 16, c2, e9, 63, c2, 92, 4c, 75, ca, 0d, 0e, 29, 2e, 75, 05, 64, cf, a6, 91, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a7, 1a, 10, 00, 00, 02, 01, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 70, 00, 01, f7, 01, 1a, 30, 00, 00, 03, 19, 08, 00, 1a, 10, 00, 02, be, 02, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, 58, 40, 92, 6c, 79, 17, bb, 36, 6f, b7, 58, 25, 84, 98, a9, 56, 07, e6, 07, f6, 26, 92, 15, f6, 21, 9f, 6c, f0, b4, e7, 20, 42, ac, b6, d8, 30, 61, 06, c9, 3b, 30, 67, 1e, 8d, 74, 11, 8b, 06, 98, ab, 8d, 6a, 6c, cd, b7, 2f, c3, a8, 30, c7, 68, 03, 4f, 72, c7, 5b, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a7, 1a, 10, 00, 00, 02, 01, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 70, 00, 01, f7, 01, 1a, 30, 00, 00, 03, 19, 08, 00, 1a, 10, 00, 02, be, 02, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, SW1: 90, SW2: 00 -CLA: 80, INS: 11, P1: 40, P2: 00, Lc: 7d, 83, a4, 1a, 10, 00, 00, 02, 03, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, 1a, 90, 00, 02, 59, 44, 54, 65, 73, 74, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 03, 58, 54, 83, 58, 18, a6, 68, de, ec, 65, 6c, fb, ee, aa, 43, ef, 97, 9d, 10, 82, f0, 99, 5f, 10, f3, ee, 9c, 38, 57, 58, 31, 04, 3a, f8, f4, fa, 1f, e4, 4d, 62, a1, cd, 26, 8e, 1a, 5a, aa, f5, a8, 94, e3, 8b, 4c, ce, 49, a1, 57, 25, 81, 6d, be, 5c, 3b, 07, 95, b6, 89, 24, 6e, 9d, 25, 22, e6, 5f, 41, cc, 59, ce, 25, 0c, 1a, 10, 00, 00, 0a, 01, Le: e6, 83, 00, 58, a5, 85, 58, 18, dd, 87, 3a, 89, 4c, b4, 3b, b3, 7a, 02, dd, ac, 10, a9, 06, 27, b7, 86, bb, 06, 90, b8, 89, 86, 4c, 0d, ac, e8, c8, 2a, c9, 2a, e6, 8e, 4d, 7c, 5e, 4c, 5a, 32, 10, 2d, 01, 5a, 76, 62, 0f, e6, 62, 6c, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a7, 1a, 10, 00, 00, 02, 03, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 30, 00, 00, 03, 19, 01, 00, 1a, 10, 00, 00, 0a, 01, 1a, 10, 00, 02, be, 02, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, 58, 31, 04, 3a, f8, f4, fa, 1f, e4, 4d, 62, a1, cd, 26, 8e, 1a, 5a, aa, f5, a8, 94, e3, 8b, 4c, ce, 49, a1, 57, 25, 81, 6d, be, 5c, 3b, 07, 95, b6, 89, 24, 6e, 9d, 25, 22, e6, 5f, 41, cc, 59, ce, 25, 0c, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a7, 1a, 10, 00, 00, 02, 03, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 30, 00, 00, 03, 19, 01, 00, 1a, 10, 00, 00, 0a, 01, 1a, 10, 00, 02, be, 02, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, SW1: 90, SW2: 00 -CLA: 80, INS: 11, P1: 40, P2: 00, Lc: 3f, 83, a5, 1a, 10, 00, 00, 02, 18, 20, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, 1a, 90, 00, 02, 59, 44, 54, 65, 73, 74, 1a, 30, 00, 00, 03, 18, 80, 1a, 20, 00, 00, 05, 41, 04, 03, 52, 81, 50, 95, e6, 79, 36, 64, a5, ec, 72, bf, 01, 4c, 83, 6c, cd, cf, 51, Le: 98, 83, 00, 58, 60, 84, 50, 4c, 88, 06, bc, 20, cd, ba, 26, 2e, d1, 10, af, 70, 6e, 5f, 3a, 4c, 17, 80, e7, 2c, 13, ef, df, 3b, 09, e2, 0a, ff, 4c, b4, d5, 1d, 48, 47, a3, e1, e0, 20, 10, 05, 1c, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a6, 1a, 10, 00, 00, 02, 18, 20, 1a, 30, 00, 00, 03, 18, 80, 1a, 20, 00, 00, 05, 41, 04, 1a, 10, 00, 02, be, 02, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a6, 1a, 10, 00, 00, 02, 18, 20, 1a, 30, 00, 00, 03, 18, 80, 1a, 20, 00, 00, 05, 41, 04, 1a, 10, 00, 02, be, 02, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, SW1: 90, SW2: 00 -CLA: 80, INS: 11, P1: 40, P2: 00, Lc: 46, 83, a6, 1a, 10, 00, 00, 02, 18, 80, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, 1a, 90, 00, 02, 59, 44, 54, 65, 73, 74, 1a, 30, 00, 00, 03, 18, 80, 1a, 20, 00, 00, 05, 41, 04, 1a, 30, 00, 00, 08, 18, 80, 03, 52, 81, 50, fc, a6, 8f, 58, 68, 93, de, d0, c0, 74, 1c, 6f, 1d, 39, 2e, 4a, Le: a6, 83, 00, 58, 67, 84, 50, 0b, 7d, d5, e0, de, 79, f0, ce, 6c, f5, 6d, 5a, f9, f0, 3e, aa, 4c, 36, 7b, 2d, e5, cb, b7, 4a, 8e, 97, 1f, 44, d3, 4c, 4e, 09, 34, d3, 7f, d1, 23, 8b, 51, 70, fe, e8, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a7, 1a, 10, 00, 00, 02, 18, 80, 1a, 30, 00, 00, 03, 18, 80, 1a, 20, 00, 00, 05, 41, 04, 1a, 30, 00, 00, 08, 18, 80, 1a, 10, 00, 02, be, 02, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a7, 1a, 10, 00, 00, 02, 18, 80, 1a, 30, 00, 00, 03, 18, 80, 1a, 20, 00, 00, 05, 41, 04, 1a, 30, 00, 00, 08, 18, 80, 1a, 10, 00, 02, be, 02, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, SW1: 90, SW2: 00 -CLA: 80, INS: 11, P1: 40, P2: 00, Lc: 3f, 83, a5, 1a, 10, 00, 00, 02, 18, 21, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, 1a, 90, 00, 02, 59, 44, 54, 65, 73, 74, 1a, 30, 00, 00, 03, 18, a8, 1a, 20, 00, 00, 05, 41, 04, 03, 52, 81, 50, 8b, d4, d5, 84, 37, 39, c0, 1b, db, ed, 3c, 68, 99, 3a, dc, 3d, Le: 98, 83, 00, 58, 60, 84, 50, ad, 9d, 57, aa, 21, e9, c7, 97, b7, eb, 25, 14, c6, ad, f2, d1, 4c, b4, 12, 76, 4f, b3, 85, 6e, 0f, 79, 87, fc, 40, 4c, fc, 62, 73, 9c, 1b, 64, df, 15, 76, 28, 17, 43, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a6, 1a, 10, 00, 00, 02, 18, 21, 1a, 30, 00, 00, 03, 18, a8, 1a, 20, 00, 00, 05, 41, 04, 1a, 10, 00, 02, be, 02, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a6, 1a, 10, 00, 00, 02, 18, 21, 1a, 30, 00, 00, 03, 18, a8, 1a, 20, 00, 00, 05, 41, 04, 1a, 10, 00, 02, be, 02, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, SW1: 90, SW2: 00 +CLA: 80, INS: 10, P1: 40, P2: 00, Lc: 23, 81, a4, 1a, 10, 00, 00, 02, 18, 20, 1a, 30, 00, 00, 03, 19, 01, 00, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 20, 00, 00, 01, 42, 02, 03, Le: 01, 03, SW1: 90, SW2: 00 +CLA: 80, INS: 10, P1: 40, P2: 00, Lc: 22, 81, a4, 1a, 10, 00, 00, 02, 03, 1a, 30, 00, 00, 03, 19, 01, 00, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 20, 00, 00, 01, 42, 02, 03, Le: d6, 83, 00, 58, 9d, 85, 58, 18, ac, 08, ac, 35, c5, b0, 8e, f4, 8a, 74, 5a, b7, ce, 25, 5e, 1e, 65, 8f, 17, ad, 86, 5f, 58, 4a, 4c, 7d, 9f, c1, 98, e9, 17, 75, c2, ca, c2, 4a, 74, 4c, 38, 78, 4e, 7e, 14, 6c, 55, dc, 46, 1b, da, 0e, 82, a0, a7, 1a, 10, 00, 00, 02, 03, 1a, 30, 00, 00, 03, 19, 01, 00, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 20, 00, 00, 01, 42, 02, 03, 1a, 10, 00, 02, be, 00, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, 58, 31, 04, 5a, 10, b8, d7, e9, be, 0a, e9, 47, 6c, 79, e4, 64, 15, 9c, 9d, 32, c6, 74, dd, 17, 11, 61, b7, 9a, 0c, 57, 8e, a1, 5f, 64, 1e, 8d, 51, 5b, 1e, 19, 4e, cc, 0e, 33, 15, d0, 9e, f6, f5, 8d, 2e, 82, a0, a7, 1a, 10, 00, 00, 02, 03, 1a, 30, 00, 00, 03, 19, 01, 00, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 20, 00, 00, 01, 42, 02, 03, 1a, 10, 00, 02, be, 00, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, SW1: 90, SW2: 00 +CLA: 80, INS: 10, P1: 40, P2: 00, Lc: 22, 81, a4, 1a, 10, 00, 00, 02, 18, 21, 1a, 30, 00, 00, 03, 18, a8, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 20, 00, 00, 01, 42, 02, 03, Le: 01, 03, SW1: 90, SW2: 00 +CLA: 80, INS: 10, P1: 40, P2: 00, Lc: 32, 81, a6, 1a, 10, 00, 00, 02, 18, 80, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, 1a, 90, 00, 02, 59, 44, 54, 65, 73, 74, 1a, 30, 00, 00, 08, 18, 80, 1a, 30, 00, 00, 03, 18, 80, 1a, 20, 00, 00, 05, 41, 04, Le: a6, 83, 00, 58, 67, 84, 50, c9, 2e, be, 94, f0, 2b, e7, 57, 9d, 90, 5b, b0, 08, 1c, b7, 93, 4c, ae, 8e, 3e, 17, 1d, 67, 15, cc, 16, e8, f2, c4, 4c, de, 14, d8, 8e, 94, 0e, 50, 63, ee, 34, 4e, 4f, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a7, 1a, 10, 00, 00, 02, 18, 80, 1a, 30, 00, 00, 08, 18, 80, 1a, 30, 00, 00, 03, 18, 80, 1a, 20, 00, 00, 05, 41, 04, 1a, 10, 00, 02, be, 00, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a7, 1a, 10, 00, 00, 02, 18, 80, 1a, 30, 00, 00, 08, 18, 80, 1a, 30, 00, 00, 03, 18, 80, 1a, 20, 00, 00, 05, 41, 04, 1a, 10, 00, 02, be, 00, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, SW1: 90, SW2: 00 +CLA: 80, INS: 11, P1: 40, P2: 00, Lc: b4, 83, a5, 1a, 10, 00, 00, 02, 01, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, 1a, 90, 00, 02, 59, 44, 54, 65, 73, 74, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 70, 00, 01, f7, 01, 03, 58, 85, 82, 58, 40, 80, 9a, 87, 4c, e1, a0, 71, 44, ae, 45, dd, 8d, 1c, 05, b4, d0, 44, 23, dd, 42, a7, c9, 53, 44, ac, 31, 4a, 22, 4a, 02, 65, a0, aa, 21, a8, 30, 94, 7d, 13, a1, bc, 89, 81, b5, 54, de, 75, 82, b9, 0b, 1a, 7a, 81, 0c, 51, e0, 2f, 91, 97, d4, e8, 33, 27, 61, 58, 40, 92, 6c, 79, 17, bb, 36, 6f, b7, 58, 25, 84, 98, a9, 56, 07, e6, 07, f6, 26, 92, 15, f6, 21, 9f, 6c, f0, b4, e7, 20, 42, ac, b6, d8, 30, 61, 06, c9, 3b, 30, 67, 1e, 8d, 74, 11, 8b, 06, 98, ab, 8d, 6a, 6c, cd, b7, 2f, c3, a8, 30, c7, 68, 03, 4f, 72, c7, 5b, Le: 1d, 83, 00, 58, dc, 85, 58, 40, 47, 58, 70, e8, fd, 81, ea, a4, eb, 96, 3b, 69, 1b, 6d, fe, b2, 9d, 99, 4c, dd, 42, 4c, 49, dc, 8d, 98, bc, ca, 2a, d1, 51, 2b, ad, ab, f1, 3f, 8e, 8e, db, f1, 6a, ec, 87, 36, af, c3, b2, 9e, ba, 14, 92, c9, 84, d2, 96, 5c, fa, 73, 02, 98, 49, 08, 7e, 33, 4c, 11, 7d, e1, 4b, ab, 5e, d5, 21, e2, d5, d0, b9, 4c, a6, 49, cb, d7, 6f, 01, 98, cd, 8e, c4, 74, 8a, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a7, 1a, 10, 00, 00, 02, 01, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 70, 00, 01, f7, 01, 1a, 30, 00, 00, 03, 19, 08, 00, 1a, 10, 00, 02, be, 02, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, 58, 40, 92, 6c, 79, 17, bb, 36, 6f, b7, 58, 25, 84, 98, a9, 56, 07, e6, 07, f6, 26, 92, 15, f6, 21, 9f, 6c, f0, b4, e7, 20, 42, ac, b6, d8, 30, 61, 06, c9, 3b, 30, 67, 1e, 8d, 74, 11, 8b, 06, 98, ab, 8d, 6a, 6c, cd, b7, 2f, c3, a8, 30, c7, 68, 03, 4f, 72, c7, 5b, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a7, 1a, 10, 00, 00, 02, 01, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 70, 00, 01, f7, 01, 1a, 30, 00, 00, 03, 19, 08, 00, 1a, 10, 00, 02, be, 02, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, SW1: 90, SW2: 00 +CLA: 80, INS: 11, P1: 40, P2: 00, Lc: 7d, 83, a4, 1a, 10, 00, 00, 02, 03, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, 1a, 90, 00, 02, 59, 44, 54, 65, 73, 74, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 03, 58, 54, 83, 58, 18, a6, 68, de, ec, 65, 6c, fb, ee, aa, 43, ef, 97, 9d, 10, 82, f0, 99, 5f, 10, f3, ee, 9c, 38, 57, 58, 31, 04, 3a, f8, f4, fa, 1f, e4, 4d, 62, a1, cd, 26, 8e, 1a, 5a, aa, f5, a8, 94, e3, 8b, 4c, ce, 49, a1, 57, 25, 81, 6d, be, 5c, 3b, 07, 95, b6, 89, 24, 6e, 9d, 25, 22, e6, 5f, 41, cc, 59, ce, 25, 0c, 1a, 10, 00, 00, 0a, 01, Le: e6, 83, 00, 58, a5, 85, 58, 18, a3, b5, 4b, b2, 54, 26, 92, ba, 13, e7, 2a, 8c, 04, fa, 7e, 5c, 74, 1e, 12, fb, 6f, 59, 7a, 27, 4c, c7, dd, 10, 77, 31, 33, 35, 0a, 83, 1d, 8b, f1, 4c, 13, 9b, 59, 6d, 06, 55, 6e, e4, a8, c2, bf, 69, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a7, 1a, 10, 00, 00, 02, 03, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 30, 00, 00, 03, 19, 01, 00, 1a, 10, 00, 00, 0a, 01, 1a, 10, 00, 02, be, 02, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, 58, 31, 04, 3a, f8, f4, fa, 1f, e4, 4d, 62, a1, cd, 26, 8e, 1a, 5a, aa, f5, a8, 94, e3, 8b, 4c, ce, 49, a1, 57, 25, 81, 6d, be, 5c, 3b, 07, 95, b6, 89, 24, 6e, 9d, 25, 22, e6, 5f, 41, cc, 59, ce, 25, 0c, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a7, 1a, 10, 00, 00, 02, 03, 1a, 50, 00, 00, c8, 1a, 00, 01, 00, 01, 1a, 30, 00, 00, 03, 19, 01, 00, 1a, 10, 00, 00, 0a, 01, 1a, 10, 00, 02, be, 02, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, SW1: 90, SW2: 00 +CLA: 80, INS: 11, P1: 40, P2: 00, Lc: 3f, 83, a5, 1a, 10, 00, 00, 02, 18, 20, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, 1a, 90, 00, 02, 59, 44, 54, 65, 73, 74, 1a, 30, 00, 00, 03, 18, 80, 1a, 20, 00, 00, 05, 41, 04, 03, 52, 81, 50, 95, e6, 79, 36, 64, a5, ec, 72, bf, 01, 4c, 83, 6c, cd, cf, 51, Le: 01, 0c, SW1: 90, SW2: 00 +CLA: 80, INS: 11, P1: 40, P2: 00, Lc: 46, 83, a6, 1a, 10, 00, 00, 02, 18, 80, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, 1a, 90, 00, 02, 59, 44, 54, 65, 73, 74, 1a, 30, 00, 00, 03, 18, 80, 1a, 20, 00, 00, 05, 41, 04, 1a, 30, 00, 00, 08, 18, 80, 03, 52, 81, 50, fc, a6, 8f, 58, 68, 93, de, d0, c0, 74, 1c, 6f, 1d, 39, 2e, 4a, Le: a6, 83, 00, 58, 67, 84, 50, 89, ba, 5c, 77, e1, 0d, 5f, a8, 79, 30, 46, 0a, 77, 68, f7, 95, 4c, 70, dd, 86, 23, 76, 5a, 88, cb, f1, 89, 32, 38, 4c, e2, 41, 13, e8, 3e, 7a, 31, f9, dc, 2c, ae, af, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a7, 1a, 10, 00, 00, 02, 18, 80, 1a, 30, 00, 00, 03, 18, 80, 1a, 20, 00, 00, 05, 41, 04, 1a, 30, 00, 00, 08, 18, 80, 1a, 10, 00, 02, be, 02, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, 82, a1, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, a7, 1a, 10, 00, 00, 02, 18, 80, 1a, 30, 00, 00, 03, 18, 80, 1a, 20, 00, 00, 05, 41, 04, 1a, 30, 00, 00, 08, 18, 80, 1a, 10, 00, 02, be, 02, 1a, 30, 00, 02, c1, 01, 1a, 30, 00, 02, c2, 01, SW1: 90, SW2: 00 +CLA: 80, INS: 11, P1: 40, P2: 00, Lc: 3f, 83, a5, 1a, 10, 00, 00, 02, 18, 21, 1a, 30, 00, 01, f5, 1a, 01, 02, 03, 04, 1a, 90, 00, 02, 59, 44, 54, 65, 73, 74, 1a, 30, 00, 00, 03, 18, a8, 1a, 20, 00, 00, 05, 41, 04, 03, 52, 81, 50, 8b, d4, d5, 84, 37, 39, c0, 1b, db, ed, 3c, 68, 99, 3a, dc, 3d, Le: 01, 0c, SW1: 90, SW2: 00