diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..378eac25 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +build diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMAbortOperationCmd.java b/Applet/Applet/src/com/android/javacard/keymaster/KMAbortOperationCmd.java new file mode 100644 index 00000000..ee3e052a --- /dev/null +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMAbortOperationCmd.java @@ -0,0 +1,36 @@ +/* + * 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 new file mode 100644 index 00000000..02fad435 --- /dev/null +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMAbstractCmd.java @@ -0,0 +1,83 @@ +/* + * 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 new file mode 100644 index 00000000..675b4f1b --- /dev/null +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMAddRngEntropyCmd.java @@ -0,0 +1,36 @@ +/* + * 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 KMAddRngEntropyCmd extends KMAbstractCmd { + public static final byte INS_ADD_RNG_ENTROPY_CMD = 0x18; + + @Override + protected KMArray getExpectedArgs() { + return null; + } + + @Override + protected KMArray process(KMArray args, KMContext context) { + 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 new file mode 100644 index 00000000..23b5afe6 --- /dev/null +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMArray.java @@ -0,0 +1,82 @@ +/* + * 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; + +public class KMArray extends KMType { + private KMType[] vals; + private short length; + private short startOff; + + private KMArray() { + init(); + } + + @Override + public void init() { + vals = null; + startOff = 0; + length = 0; + } + + @Override + public short length() { + return length; + } + + public static void create(KMArray[] arrayRefTable) { + byte index = 0; + while (index < arrayRefTable.length) { + arrayRefTable[index] = new KMArray(); + index++; + } + } + + public static KMArray instance() { + return repository.newArray(); + } + + public static KMArray instance(short length) { + + KMArray inst = repository.newArray(); + inst.startOff = repository.newTypeArray(length); + inst.vals = repository.getTypeArrayRef(); + inst.length = length; + return inst; + } + + public KMArray withLength(short length) { + this.length = length; + return this; + } + + public KMArray add(short index, KMType val) { + if (index >= length) { + throw new KMException(ISO7816.SW_WRONG_LENGTH); + } + vals[(short) (startOff + index)] = val; + return this; + } + + public KMType get(short index) { + if (index >= length) { + throw new KMException(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 new file mode 100644 index 00000000..9d51e231 --- /dev/null +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMAttestKeyCmd.java @@ -0,0 +1,36 @@ +/* + * 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 new file mode 100644 index 00000000..2ab55233 --- /dev/null +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMBeginOperationCmd.java @@ -0,0 +1,36 @@ +/* + * 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 new file mode 100644 index 00000000..6cf41894 --- /dev/null +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMBoolTag.java @@ -0,0 +1,101 @@ +/* + * 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; + +public class KMBoolTag extends KMTag { + + private static final short[] tags = { + CALLER_NONCE, + INCLUDE_UNIQUE_ID, + BOOTLOADER_ONLY, + ROLLBACK_RESISTANCE, + NO_AUTH_REQUIRED, + ALLOW_WHILE_ON_BODY, + TRUSTED_USER_PRESENCE_REQUIRED, + TRUSTED_CONFIRMATION_REQUIRED, + UNLOCKED_DEVICE_REQUIRED, + RESET_SINCE_ID_ROTATION + }; + + // Array of Tag Values. + private short key; + private byte val; + + // assignBlob constructor + private KMBoolTag() { + init(); + } + + @Override + public void init() { + key = 0; + val = 1; // always 1. + } + + public static KMBoolTag instance() { + return repository.newBoolTag(); + } + + public static void create(KMBoolTag[] boolTagRefTable) { + byte index = 0; + while (index < boolTagRefTable.length) { + boolTagRefTable[index] = new KMBoolTag(); + index++; + } + } + + @Override + public short getKey() { + return key; + } + + @Override + public short length() { + return 1; + } + + @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)) { + throw new KMException(ISO7816.SW_DATA_INVALID); + } + KMBoolTag tag = repository.newBoolTag(); + tag.key = key; + return tag; + } + + // validate the tag key + private static boolean validateKey(short key) { + short index = (short) tags.length; + while (--index >= 0) { + if (tags[index] == key) { + return true; + } + } + return false; + } +} diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMByteBlob.java b/Applet/Applet/src/com/android/javacard/keymaster/KMByteBlob.java new file mode 100644 index 00000000..fca5b947 --- /dev/null +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMByteBlob.java @@ -0,0 +1,111 @@ +/* + * Copyright(C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.javacard.keymaster; + +import javacard.framework.ISO7816; +import javacard.framework.Util; + +// Byte val represents contiguous memory buffer. +public class KMByteBlob extends KMType { + private byte[] val; + private short startOff; + private short length; + + private KMByteBlob() { + init(); + } + + @Override + public void init() { + length = 0; + startOff = 0; + val = null; + } + + @Override + public short length() { + return length; + } + + public static KMByteBlob instance() { + return repository.newByteBlob(); + } + + // 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); + } + KMByteBlob inst = instance(length); + Util.arrayCopyNonAtomic(blob, startOff, inst.val, inst.startOff, inst.length); + return inst; + } + + // returns empty blob with given length + public static KMByteBlob instance(short length) { + if (length <= 0) { + throw new KMException(ISO7816.SW_WRONG_LENGTH); + } + 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; + } + + public void add(short index, byte val) { + if (index >= this.length) { + throw new KMException(ISO7816.SW_WRONG_LENGTH); + } + if (this.val == null) { + throw new KMException(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); + } + if (this.val == null) { + throw new KMException(ISO7816.SW_DATA_INVALID); + } + return this.val[(short) (startOff + index)]; + } + + public byte[] getVal() { + return val; + } + + public short getStartOff() { + return startOff; + } +} diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMByteTag.java b/Applet/Applet/src/com/android/javacard/keymaster/KMByteTag.java new file mode 100644 index 00000000..c415abbc --- /dev/null +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMByteTag.java @@ -0,0 +1,127 @@ +/* + * 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; + +public class KMByteTag extends KMTag { + + private static final short[] tags = { + APPLICATION_ID, + APPLICATION_DATA, + ROOT_OF_TRUST, + UNIQUE_ID, + ATTESTATION_CHALLENGE, + ATTESTATION_APPLICATION_ID, + ATTESTATION_ID_BRAND, + ATTESTATION_ID_DEVICE, + ATTESTATION_ID_PRODUCT, + ATTESTATION_ID_SERIAL, + ATTESTATION_ID_IMEI, + ATTESTATION_ID_MEID, + ATTESTATION_ID_MANUFACTURER, + ATTESTATION_ID_MODEL, + ASSOCIATED_DATA, + NONCE, + CONFIRMATION_TOKEN + }; + + private short key; + private KMByteBlob val; + + private KMByteTag() { + init(); + } + + @Override + public void init() { + key = 0; + val = null; + } + + @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 KMByteTag instance(short key) { + if (!validateKey(key)) { + throw new KMException(ISO7816.SW_DATA_INVALID); + } + KMByteTag tag = repository.newByteTag(); + tag.key = key; + tag.val = null; + return tag; + } + + public static void create(KMByteTag[] byteTagRefTable) { + byte index = 0; + while (index < byteTagRefTable.length) { + byteTagRefTable[index] = new KMByteTag(); + index++; + } + } + + // create default assignBlob without any value + public static KMByteTag instance(short key, KMByteBlob array) { + if (!validateKey(key)) { + throw new KMException(ISO7816.SW_DATA_INVALID); + } + KMByteTag tag = repository.newByteTag(); + tag.key = key; + tag.val = array; + return tag; + } + + public KMByteTag withLength(short length) { + this.val.withLength(length); + return this; + } + + private static boolean validateKey(short key) { + short index = (short) tags.length; + while (--index >= 0) { + if (tags[index] == key) { + return true; + } + } + 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/KMCommand.java b/Applet/Applet/src/com/android/javacard/keymaster/KMCommand.java new file mode 100644 index 00000000..9e17dd58 --- /dev/null +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMCommand.java @@ -0,0 +1,43 @@ +/* + * 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 new file mode 100644 index 00000000..5d2e0b46 --- /dev/null +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMComputeSharedHmacCmd.java @@ -0,0 +1,36 @@ +/* + * 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 new file mode 100644 index 00000000..0d081f89 --- /dev/null +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMContext.java @@ -0,0 +1,119 @@ +/* + * 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 new file mode 100644 index 00000000..4cedfcaf --- /dev/null +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMDecoder.java @@ -0,0 +1,373 @@ +/* + * 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.Util; +// TODO Clean and refactor the code. +public class KMDecoder { + // major types + private static final short UINT_TYPE = 0x00; + private static final short BYTES_TYPE = 0x40; + private static final short ARRAY_TYPE = 0x80; + private static final short MAP_TYPE = 0xA0; + + // masks + private static final short ADDITIONAL_MASK = 0x1F; + private static final short MAJOR_TYPE_MASK = 0xE0; + + // value length + private static final short UINT8_LENGTH = 0x18; + private static final short UINT16_LENGTH = 0x19; + 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; + private short tagType; + private short tagKey; + + public KMDecoder() { + buffer = null; + startOff = 0; + length = 0; + } + + public KMArray decode(KMArray expression, byte[] buffer, short startOff, short length) { + this.buffer = buffer; + this.startOff = startOff; + this.length = length; + return decode(expression); + } + + private KMEnumArrayTag decode(KMEnumArrayTag exp) { + readTagKey(exp.getTagType()); + // The value must be byte blob + // TODO check this out. + return exp.instance(this.tagKey, decode(exp.getValues())); + } + + 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); + } + return exp.instance(this.tagKey, decode(exp.getValues(), (KMInteger) exp.getValues().getType())); + } + + private KMIntegerTag decode(KMIntegerTag exp) { + readTagKey(exp.getTagType()); + // the value is an integer + return exp.instance(this.tagKey, decode(exp.getValue())); + } + + private KMByteTag decode(KMByteTag exp) { + short key = 0; + readTagKey(exp.getTagType()); + // The value must be byte blob + return exp.instance(this.tagKey, decode(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) { + throw new KMException(ISO7816.SW_DATA_INVALID); + } + if ((byte) (buffer[startOff] & ADDITIONAL_MASK) != 0x01) { + throw new KMException(ISO7816.SW_DATA_INVALID); + } + incrementStartOff((short) 1); + return exp.instance(tagKey); + } + + private KMEnumTag decode(KMEnumTag exp) { + readTagKey(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) { + throw new KMException(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); + } + if (len < UINT8_LENGTH) { + enumVal = (byte)(len & ADDITIONAL_MASK); + incrementStartOff((short) 1); + } else if (len == UINT8_LENGTH) { + incrementStartOff((short) 1); + enumVal = buffer[startOff]; + incrementStartOff((short) 1); + } + return exp.instance(tagKey, enumVal); + } + + 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); + } + short len = (short) (buffer[startOff] & ADDITIONAL_MASK); + byte enumVal = 0; + if (len > UINT8_LENGTH) { + throw new KMException(ISO7816.SW_WRONG_LENGTH); + } + if (len < UINT8_LENGTH) { + enumVal = (byte)(len & ADDITIONAL_MASK); + incrementStartOff((short) 1); + } else { + incrementStartOff((short) 1); + enumVal = buffer[startOff]; + incrementStartOff((short) 1); + } + return exp.instance(exp.getType(), enumVal); + } + + private KMInteger decode(KMInteger exp) { + KMInteger inst; + if ((buffer[startOff] & MAJOR_TYPE_MASK) != UINT_TYPE) { + throw new KMException(ISO7816.SW_DATA_INVALID); + } + short len = (short) (buffer[startOff] & ADDITIONAL_MASK); + incrementStartOff((short) 1); + if (len < UINT8_LENGTH) { + inst = exp.uint_8((byte)(len & ADDITIONAL_MASK)); + } else if (len == UINT8_LENGTH) { + inst = exp.instance(buffer, startOff, (short) 1); + incrementStartOff((short) 1); + } else if (len == UINT16_LENGTH) { + inst = exp.instance(buffer, startOff, (short) 2); + incrementStartOff((short) 2); + } else if (len == UINT32_LENGTH) { + inst = exp.instance(buffer, startOff, (short) 4); + incrementStartOff((short) 4); + } else if (len == UINT64_LENGTH) { + inst = exp.instance(buffer, startOff, (short) 8); + incrementStartOff((short) 8); + } else { + throw new KMException(ISO7816.SW_WRONG_LENGTH); + } + return inst; + } + + private KMByteBlob decode(KMByteBlob exp) { + short payloadLength = readMajorTypeWithPayloadLength(BYTES_TYPE); + KMByteBlob inst = exp.instance(buffer, startOff, payloadLength); + incrementStartOff(payloadLength); + return inst; + } + + private KMArray decode(KMArray exp) { + short payloadLength = readMajorTypeWithPayloadLength(ARRAY_TYPE); + if (exp.length() != payloadLength) { + throw new KMException(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)) { + throw new KMException(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); + } + throw new KMException(ISO7816.SW_DATA_INVALID); + } + + private short peekTagType() { + if ((buffer[startOff] & MAJOR_TYPE_MASK) != UINT_TYPE) { + throw new KMException(ISO7816.SW_DATA_INVALID); + } + + if ((short) (buffer[startOff] & ADDITIONAL_MASK) != UINT32_LENGTH) { + throw new KMException(ISO7816.SW_WRONG_LENGTH); + } + return (short) + ((Util.makeShort(buffer[(short) (startOff + 1)], buffer[(short) (startOff + 2)])) + & KMType.TAG_TYPE_MASK); + } + + private void readTagKey(short expectedTagType) { + if ((buffer[startOff] & MAJOR_TYPE_MASK) != UINT_TYPE) { + throw new KMException(ISO7816.SW_DATA_INVALID); + } + if ((byte) (buffer[startOff] & ADDITIONAL_MASK) != UINT32_LENGTH) { + throw new KMException(ISO7816.SW_WRONG_LENGTH); + } + incrementStartOff((short) 1); + this.tagType = readShort(); + this.tagKey = readShort(); + if (tagType != expectedTagType) { + throw new KMException(ISO7816.SW_DATA_INVALID); + } + } + + // payload length cannot be more then 16 bits. + 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); + } + short lenType = (short) (val & ADDITIONAL_MASK); + if (lenType > UINT16_LENGTH) { + throw new KMException(ISO7816.SW_WRONG_LENGTH); + } + if (lenType < UINT8_LENGTH) { + payloadLength = lenType; + }else if (lenType == UINT8_LENGTH) { + payloadLength = (short)(readByte() & 0xFF); + } else { + payloadLength = readShort(); + } + return payloadLength; + } + + private short readShort() { + short val = Util.makeShort(buffer[startOff], buffer[(short) (startOff + 1)]); + incrementStartOff((short) 2); + return val; + } + + private byte readByte() { + byte val = buffer[startOff]; + incrementStartOff((short) 1); + return val; + } + + private void incrementStartOff(short inc) { + startOff += inc; + if (startOff > this.length) { + throw new KMException(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 new file mode 100644 index 00000000..5c476cd0 --- /dev/null +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMDeleteAllKeysCmd.java @@ -0,0 +1,36 @@ +/* + * 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 new file mode 100644 index 00000000..ced6e7e4 --- /dev/null +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMDeleteKeyCmd.java @@ -0,0 +1,36 @@ +/* + * 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 new file mode 100644 index 00000000..fa66ab1f --- /dev/null +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMDestroyAttestationIdsCmd.java @@ -0,0 +1,36 @@ +/* + * 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 new file mode 100644 index 00000000..a231563b --- /dev/null +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMEncoder.java @@ -0,0 +1,294 @@ +/* + * 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.Util; + +public class KMEncoder { + // major types + private static final byte UINT_TYPE = 0x00; + private static final byte BYTES_TYPE = 0x40; + private static final byte ARRAY_TYPE = (byte) 0x80; + private static final byte MAP_TYPE = (byte) 0xA0; + + // masks + private static final byte ADDITIONAL_MASK = 0x1F; + private static final byte MAJOR_TYPE_MASK = (byte) 0xE0; + + // value length + private static final byte UINT8_LENGTH = (byte) 0x18; + private static final byte UINT16_LENGTH = (byte) 0x19; + private static final byte UINT32_LENGTH = (byte) 0x1A; + private static final byte UINT64_LENGTH = (byte) 0x1B; + private static final short TINY_PAYLOAD = 0x17; + private static final short SHORT_PAYLOAD = 0x100; + + // TODO move the following to transient memory. + private byte[] buffer; + private short startOff; + private short length; + + public KMEncoder() { + buffer = null; + startOff = 0; + length = 0; + } + + public short encode(KMArray object, byte[] buffer, short startOff, short length) { + this.buffer = buffer; + this.startOff = startOff; + this.length = length; + encode(object); + this.length = this.startOff; + this.startOff = startOff; + return this.length; + } + + private void encode(KMType exp){ + if(exp instanceof KMByteBlob){ + encode((KMByteBlob) exp); + return; + } + 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; + } + throw new KMException(ISO7816.SW_DATA_INVALID); + } + + private void encode(KMKeyParameters obj) { + encodeAsMap(obj.getVals()); + } + private void encode(KMKeyCharacteristics obj) { + encode(obj.getVals()); + } + + private void encode(KMVerificationToken obj) { + encode(obj.getVals()); + } + + private void encode(KMHardwareAuthToken obj) { + encode(obj.getVals()); + } + + private void encode(KMHmacSharingParameters obj) { + encode(obj.getVals()); + } + + private void encode(KMArray obj) { + writeMajorTypeWithLength(ARRAY_TYPE, obj.length()); + short index = 0; + while(index < obj.length()){ + encode(obj.get(index)); + index++; + } + } + + private void encodeAsMap(KMArray obj){ + writeMajorTypeWithLength(MAP_TYPE, obj.length()); + short index = 0; + while(index < obj.length()){ + KMType t = obj.get(index); + encode(t); + //encode(obj.get(index)); + index++; + } + } + + private void encode(KMVector obj){ + writeMajorTypeWithLength(ARRAY_TYPE, obj.length()); + short index = 0; + while(index 0){ + break; + } + index++; // index will be equal to len if value is 0. + } + // find the difference between most significant byte and len + 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){ + writeByte((byte)(UINT_TYPE | UINT8_LENGTH)); + writeByte(val[index]); + }else if(diff == 2){ + writeByte((byte)(UINT_TYPE | UINT16_LENGTH)); + writeBytes(val, index, (short)2); + }else if(diff <= 4){ + writeByte((byte)(UINT_TYPE | UINT32_LENGTH)); + writeBytes(val, (short)(len - 4), (short)4); + }else { + writeByte((byte)(UINT_TYPE | UINT64_LENGTH)); + writeBytes(val, (short)0, (short)8); + } + } + + private void encode(KMByteBlob obj) { + writeMajorTypeWithLength(BYTES_TYPE, obj.length()); + writeBytes(obj.getVal(), obj.getStartOff(), obj.length()); + } + + private void writeByteValue(byte val){ + if(val < UINT8_LENGTH){ + writeByte((byte)(UINT_TYPE | val)); + }else{ + writeByte((byte)(UINT_TYPE | UINT8_LENGTH)); + writeByte(val); + } + } + + private void writeTag(short tagType, short tagKey){ + writeByte((byte)(UINT_TYPE | UINT32_LENGTH)); + 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))); + }else if(len < SHORT_PAYLOAD){ + writeByte((byte)(majorType | UINT8_LENGTH )); + writeByte((byte)(len & 0xFF)); + }else { + writeByte((byte)(majorType | UINT16_LENGTH )); + writeShort(len); + } + } + + private void writeBytes(byte[] buf, short start, short len){ + Util.arrayCopy(buf, start, buffer, startOff, len); + incrementStartOff(len); + } + private void writeShort(short val){ + buffer[startOff] = (byte)((val >> 8) & 0xFF); + incrementStartOff((short)1); + buffer[startOff] = (byte)((val & 0xFF)); + incrementStartOff((short)1); + } + private void writeByte(byte val){ + buffer[startOff] = val; + incrementStartOff((short)1); + } + + private void incrementStartOff(short inc){ + startOff += inc; + if (startOff >= this.length) { + throw new KMException(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 new file mode 100644 index 00000000..7dad1d27 --- /dev/null +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMEnum.java @@ -0,0 +1,125 @@ +/* + * 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; + +public class KMEnum extends KMType { + private static short[] types = {HARDWARE_TYPE, KEY_FORMAT, KEY_DERIVATION_FUNCTION}; + + private static Object[] enums = null; + + private short type; + private byte val; + + private KMEnum() { + init(); + } + + @Override + public void init() { + type = 0; + val = 0; + } + + @Override + public short length() { + return 1; + } + + public static KMEnum instance() { + return repository.newEnum(); + } + + public static KMEnum instance(short enumType, byte val) { + KMEnum inst = repository.newEnum(); + if (!validateEnum(enumType, val)) { + throw new KMException(ISO7816.SW_DATA_INVALID); + } + inst.type = enumType; + inst.val = val; + return inst; + } + + public static void create(KMEnum[] enumRefTable) { + if (enums == null) { + enums = + new Object[] { + new byte[] {SOFTWARE, TRUSTED_ENVIRONMENT,STRONGBOX}, + new byte[] {X509, PKCS8, RAW}, + new byte[] { + DERIVATION_NONE, + RFC5869_SHA256, + ISO18033_2_KDF1_SHA1, + ISO18033_2_KDF1_SHA256, + ISO18033_2_KDF2_SHA1, + ISO18033_2_KDF2_SHA256 + } + }; + } + byte index = 0; + while (index < enumRefTable.length) { + enumRefTable[index] = new KMEnum(); + index++; + } + } + + public KMEnum setVal(byte val) { + this.val = val; + return this; + } + + public byte getVal() { + return val; + } + + public KMEnum setType(short type) { + this.type = type; + return this; + } + + public short getType() { + return type; + } + // validate enumeration keys and values. + private static boolean validateEnum(short key, byte value) { + // check if key exists + short index = (short) types.length; + while (--index >= 0) { + if (types[index] == key) { + // check if value given + if (value != NO_VALUE) { + // check if the value exist + byte[] vals = (byte[]) enums[index]; + short enumInd = (short) vals.length; + while (--enumInd >= 0) { + if (vals[enumInd] == value) { + // return true if value exist + return true; + } + } + // return false if value does not exist + return false; + } + // return true if key exist and value not given + return true; + } + } + // 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 new file mode 100644 index 00000000..9c76ce66 --- /dev/null +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMEnumArrayTag.java @@ -0,0 +1,150 @@ +/* + * 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; + +public class KMEnumArrayTag extends KMTag { + + // 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(); + } + + @Override + public void init() { + key = 0; + array = null; + } + + @Override + public short getKey() { + return key; + } + + @Override + public short getTagType() { + return KMType.ENUM_ARRAY_TAG; + } + // returns the length + @Override + public short length() { + return array.length(); + } + + public static KMEnumArrayTag instance() { + return repository.newEnumArrayTag(); + } + + public static void create(KMEnumArrayTag[] enumArrayTagRefTable) { + if (enums == null) { + enums = + new Object[] { + new byte[] {ENCRYPT, DECRYPT, SIGN, VERIFY, WRAP_KEY, ATTEST_KEY}, + new byte[] {ECB, CBC, CTR}, + 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 + } + }; + } + 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) { + throw new KMException(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 + short index = (short) tags.length; + while (--index >= 0) { + if (tags[index] == key) { + return (byte[]) enums[index]; + } + } + 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) { + throw new KMException(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) { + throw new KMException(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 new file mode 100644 index 00000000..3cfa840e --- /dev/null +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMEnumTag.java @@ -0,0 +1,133 @@ +/* + * 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; + +public class KMEnumTag extends KMTag { + + private static short[] tags = { + ALGORITHM, ECCURVE, BLOB_USAGE_REQ, USER_AUTH_TYPE, ORIGIN, HARDWARE_TYPE + }; + + private static Object[] enums = null; + + private short key; + private byte val; + + // assignBlob constructor + private KMEnumTag() { + init(); + } + + @Override + public void init() { + key = 0; + val = 0; + } + + @Override + public short getKey() { + return key; + } + + @Override + public short length() { + return 1; + } + + @Override + public short getTagType() { + return KMType.ENUM_TAG; + } + + public static KMEnumTag instance() { + return repository.newEnumTag(); + } + + 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); + } + } + + public static void create(KMEnumTag[] enumTagRefTable) { + if (enums == null) { + enums = + new Object[] { + new byte[] {RSA, DES, EC, AES, HMAC}, + new byte[] {P_224, P_256, P_384, P_521}, + new byte[] {STANDALONE, REQUIRES_FILE_SYSTEM}, + new byte[] {USER_AUTH_NONE, PASSWORD, FINGERPRINT, ANY}, + new byte[] {GENERATED, DERIVED, IMPORTED, UNKNOWN, SECURELY_IMPORTED}, + 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) { + // check if key exists + short index = (short) tags.length; + while (--index >= 0) { + if (tags[index] == key) { + // check if value given + if (value != NO_VALUE) { + // check if the value exist + byte[] vals = (byte[]) enums[index]; + short enumInd = (short) vals.length; + while (--enumInd >= 0) { + if (vals[enumInd] == value) { + // return true if value exist + return true; + } + } + // return false if value does not exist + return false; + } + // return true if key exist and value not given + return true; + } + } + // 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)) { + throw new KMException(ISO7816.SW_DATA_INVALID); + } + KMEnumTag tag = instance(key); + tag.val = val; + return tag; + } +} diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMException.java b/Applet/Applet/src/com/android/javacard/keymaster/KMException.java new file mode 100644 index 00000000..3e357613 --- /dev/null +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMException.java @@ -0,0 +1,30 @@ +/* + * Copyright(C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" 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.ISOException; + +public class KMException extends ISOException { + + // 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); + } +} + + diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMExportKeyCmd.java b/Applet/Applet/src/com/android/javacard/keymaster/KMExportKeyCmd.java new file mode 100644 index 00000000..05d28ef6 --- /dev/null +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMExportKeyCmd.java @@ -0,0 +1,36 @@ +/* + * 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 new file mode 100644 index 00000000..0267e4fb --- /dev/null +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMFinishOperationCmd.java @@ -0,0 +1,36 @@ +/* + * 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 new file mode 100644 index 00000000..0c2d69d1 --- /dev/null +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMGenerateKeyCmd.java @@ -0,0 +1,36 @@ +/* + * 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 new file mode 100644 index 00000000..3f6cb8f8 --- /dev/null +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMGetHWInfoCmd.java @@ -0,0 +1,52 @@ +/* + * 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 new file mode 100644 index 00000000..19165531 --- /dev/null +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMGetHmacSharingParametersCmd.java @@ -0,0 +1,36 @@ +/* + * 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 new file mode 100644 index 00000000..20fef22b --- /dev/null +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMGetKeyCharacteristicsCmd.java @@ -0,0 +1,36 @@ +/* + * 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 new file mode 100644 index 00000000..ca3c2804 --- /dev/null +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMHardwareAuthToken.java @@ -0,0 +1,101 @@ +/* + * 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; + +public class KMHardwareAuthToken extends KMType { + public static final byte CHALLENGE = 0x00; + public static final byte USER_ID = 0x01; + public static final byte AUTHENTICATOR_ID = 0x02; + public static final byte HW_AUTHENTICATOR_TYPE = 0x03; + public static final byte TIMESTAMP = 0x04; + public static final byte MAC = 0x05; + + private KMArray vals; + + private KMHardwareAuthToken() { + init(); + } + + @Override + public void init() { + vals = null; + } + + @Override + public short length() { + return vals.length(); + } + + 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 static KMHardwareAuthToken instance(KMArray vals) { + if (vals.length() != 6) { + throw new KMException(ISO7816.SW_WRONG_LENGTH); + } + KMHardwareAuthToken inst = repository.newHwAuthToken(); + inst.vals = vals; + return inst; + } + + public static void create(KMHardwareAuthToken[] hwAuthTokenRefTable) { + byte index = 0; + while (index < hwAuthTokenRefTable.length) { + hwAuthTokenRefTable[index] = new KMHardwareAuthToken(); + index++; + } + } + + public KMInteger getChallenge() { + return (KMInteger) vals.get(CHALLENGE); + } + + public KMInteger getUserId() { + return (KMInteger) vals.get(USER_ID); + } + + public KMInteger getAuthenticatorId() { + return (KMInteger) vals.get(AUTHENTICATOR_ID); + } + + public byte getHwAuthenticatorType() { + return ((KMEnumTag) vals.get(HW_AUTHENTICATOR_TYPE)).getValue(); + } + + public KMInteger getTimestamp() { + return (KMInteger) vals.get(TIMESTAMP); + } + + public KMByteBlob getMac() { + return (KMByteBlob) vals.get(MAC); + } + + public KMArray getVals() { + return vals; + } +} diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMHmacSharingParameters.java b/Applet/Applet/src/com/android/javacard/keymaster/KMHmacSharingParameters.java new file mode 100644 index 00000000..ef08696b --- /dev/null +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMHmacSharingParameters.java @@ -0,0 +1,76 @@ +/* + * 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; + +public class KMHmacSharingParameters extends KMType { + public static final byte SEED = 0x00; + public static final byte NONCE = 0x01; + private KMArray vals; + + private KMHmacSharingParameters() { + init(); + } + + @Override + public void init() { + vals = null; + } + + @Override + public short length() { + return vals.length(); + } + + 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 static KMHmacSharingParameters instance(KMArray vals) { + if (vals.length() != 2) { + throw new KMException(ISO7816.SW_WRONG_LENGTH); + } + KMHmacSharingParameters inst = repository.newHmacSharingParameters(); + inst.vals = vals; + return inst; + } + + public static void create(KMHmacSharingParameters[] hmacSharingParamsRefTable) { + byte index = 0; + while (index < hmacSharingParamsRefTable.length) { + hmacSharingParamsRefTable[index] = new KMHmacSharingParameters(); + index++; + } + } + + public KMByteBlob getSeed() { + return (KMByteBlob) vals.get(SEED); + } + + public KMByteBlob getNonce() { + return (KMByteBlob) vals.get(NONCE); + } + + public KMArray getVals() { + return vals; + } +} diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMImportKeyCmd.java b/Applet/Applet/src/com/android/javacard/keymaster/KMImportKeyCmd.java new file mode 100644 index 00000000..6be0d0d7 --- /dev/null +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMImportKeyCmd.java @@ -0,0 +1,35 @@ +/* + * 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 new file mode 100644 index 00000000..e9896748 --- /dev/null +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMImportWrappedKeyCmd.java @@ -0,0 +1,35 @@ +/* + * 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 new file mode 100644 index 00000000..19996d18 --- /dev/null +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMInteger.java @@ -0,0 +1,132 @@ +/* + * 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.Util; + +// Represents 8 bit, 16 bit, 32 bit and 64 bit integers +public class KMInteger extends KMType { + private byte[] val; + + private KMInteger() { + init(); + } + + @Override + public void init() { + val = null; + } + + @Override + public short length() { + return (short) this.val.length; + } + + public static KMInteger instance() { + return repository.newInteger(); + } + + // 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; + } + + // 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; + } + + // 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; + } + + // 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 void create(KMInteger[] integerRefTable) { + byte index = 0; + while (index < integerRefTable.length) { + integerRefTable[index] = new KMInteger(); + index++; + } + } + + public byte[] getValue() { + return val; + } + + public KMInteger setValue(short val) { + this.val[2] = (byte) (val >> 8); + this.val[3] = (byte) (val & 0xFF); + return this; + } + + public KMInteger setValue(byte[] val) { + this.val = val; + return this; + } + + public short getShort() { + if (val == null) { + throw new KMException(ISO7816.SW_DATA_INVALID); + } else if (val.length != 4) { + throw new KMException(ISO7816.SW_WRONG_LENGTH); + } + return Util.makeShort(val[2], val[3]); + } + + public byte getByte() { + if (val == null) { + throw new KMException(ISO7816.SW_DATA_INVALID); + } else if (val.length != 4) { + throw new KMException(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 == 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); + } + } +} diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMIntegerArrayTag.java b/Applet/Applet/src/com/android/javacard/keymaster/KMIntegerArrayTag.java new file mode 100644 index 00000000..9c9258a2 --- /dev/null +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMIntegerArrayTag.java @@ -0,0 +1,124 @@ +/* + * 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; + +public class KMIntegerArrayTag extends KMTag { + private static final short[] tags = {USER_SECURE_ID}; + private short key; + private KMVector vals; + private short tagType; + + private KMIntegerArrayTag() { + init(); + } + + @Override + public void init() { + key = 0; + vals = null; + tagType = KMType.UINT_ARRAY_TAG; + } + + @Override + public short getKey() { + return key; + } + + @Override + public short length() { + return this.vals.length(); + } + + @Override + public short getTagType() { + return tagType; + } + + public static KMIntegerArrayTag instance() { + return repository.newIntegerArrayTag(); + } + + public static void create(KMIntegerArrayTag[] intArrayTagRefTable) { + byte index = 0; + while (index < intArrayTagRefTable.length) { + intArrayTagRefTable[index] = new KMIntegerArrayTag(); + index++; + } + } + + public KMIntegerArrayTag asUlongArray() { + tagType = KMType.ULONG_ARRAY_TAG; + return this; + } + + public static KMIntegerArrayTag instance(short key) { + if (!validateKey(key)) { + throw new KMException(ISO7816.SW_DATA_INVALID); + } + KMIntegerArrayTag tag = repository.newIntegerArrayTag(); + tag.key = key; + tag.vals = KMVector.instance(KMInteger.instance()); + return tag; + } + + public static KMIntegerArrayTag instance(short key, KMVector val) { + if (!(val.getType() instanceof KMInteger)) { + throw new KMException(ISO7816.SW_DATA_INVALID); + } + if (!(validateKey(key))) { + throw new KMException(ISO7816.SW_DATA_INVALID); + } + KMIntegerArrayTag tag = repository.newIntegerArrayTag(); + tag.key = key; + tag.vals = val; + return tag; + } + + private static boolean validateKey(short key) { + short index = (short) tags.length; + while (--index >= 0) { + if (tags[index] == key) { + return true; + } + } + 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); + } +} diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMIntegerTag.java b/Applet/Applet/src/com/android/javacard/keymaster/KMIntegerTag.java new file mode 100644 index 00000000..00dbf256 --- /dev/null +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMIntegerTag.java @@ -0,0 +1,134 @@ +/* + * Copyright(C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.javacard.keymaster; + +import javacard.framework.ISO7816; + +// Implements UINT, ULONG and DATE tags. +public class KMIntegerTag extends KMTag { + private static final short[] tags = { + // UINT + KEYSIZE, + MIN_MAC_LENGTH, + MIN_SEC_BETWEEN_OPS, + MAX_USES_PER_BOOT, + USERID, + AUTH_TIMEOUT, + OS_VERSION, + OS_PATCH_LEVEL, + VENDOR_PATCH_LEVEL, + BOOT_PATCH_LEVEL, + MAC_LENGTH, + // ULONG + RSA_PUBLIC_EXPONENT, + // DATE + ACTIVE_DATETIME, + ORIGINATION_EXPIRE_DATETIME, + USAGE_EXPIRE_DATETIME, + CREATION_DATETIME + }; + + private short key; + private KMInteger val; + private short tagType; + + private KMIntegerTag() { + init(); + } + + @Override + public void init() { + key = 0; + val = null; + tagType = KMType.UINT_TAG; + } + + @Override + public short getKey() { + return key; + } + + @Override + public short length() { + return (short) val.getValue().length; + } + + @Override + public short getTagType() { + return tagType; + } + + public static KMIntegerTag instance() { + return repository.newIntegerTag(); + } + + public static KMIntegerTag instance(short key) { + if (!validateKey(key)) { + throw new KMException(ISO7816.SW_DATA_INVALID); + } + KMIntegerTag tag = repository.newIntegerTag(); + tag.key = key; + tag.val = null; + return tag; + } + + 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 static void create(KMIntegerTag[] intTagRefTable) { + byte index = 0; + while (index < intTagRefTable.length) { + intTagRefTable[index] = new KMIntegerTag(); + index++; + } + } + + private static boolean validateKey(short key) { + short index = (short) tags.length; + while (--index >= 0) { + if (tags[index] == key) { + return true; + } + } + 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; + } + + 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 new file mode 100644 index 00000000..a6b5c7fe --- /dev/null +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMKeyCharacteristics.java @@ -0,0 +1,76 @@ +/* + * 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; + +public class KMKeyCharacteristics extends KMType { + public static final byte SOFTWARE_ENFORCED = 0x00; + public static final byte HARDWARE_ENFORCED = 0x01; + private KMArray vals; + + private KMKeyCharacteristics() { + init(); + } + + @Override + public void init() { + vals = null; + } + + @Override + public short length() { + return vals.length(); + } + + 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 instance(KMArray vals) { + if (vals.length() != 2) { + throw new KMException(ISO7816.SW_WRONG_LENGTH); + } + KMKeyCharacteristics inst = repository.newKeyCharacteristics(); + inst.vals = vals; + return inst; + } + + public static void create(KMKeyCharacteristics[] keyCharRefTable) { + byte index = 0; + while (index < keyCharRefTable.length) { + keyCharRefTable[index] = new KMKeyCharacteristics(); + index++; + } + } + + public KMKeyParameters getSoftwareEnforced() { + return (KMKeyParameters) vals.get(SOFTWARE_ENFORCED); + } + + public KMKeyParameters getHardwareEnforced() { + return (KMKeyParameters) vals.get(HARDWARE_ENFORCED); + } + + public KMArray getVals() { + return vals; + } +} diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java b/Applet/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java new file mode 100644 index 00000000..44e25e98 --- /dev/null +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java @@ -0,0 +1,68 @@ +/* + * Copyright(C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.javacard.keymaster; + +public class KMKeyParameters extends KMType { + private KMArray vals; + + private KMKeyParameters() { + init(); + } + + @Override + public void init() { + vals = null; + } + + @Override + public short length() { + return vals.length(); + } + + 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 KMKeyParameters instance(KMArray vals) { + KMKeyParameters inst = repository.newKeyParameters(); + inst.vals = vals; + return inst; + } + + public static void create(KMKeyParameters[] keyParametersRefTable) { + byte index = 0; + while (index < keyParametersRefTable.length) { + keyParametersRefTable[index] = new KMKeyParameters(); + index++; + } + } + + public KMArray getVals() { + return vals; + } +} diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java b/Applet/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java new file mode 100644 index 00000000..83512f92 --- /dev/null +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java @@ -0,0 +1,233 @@ +/* + * 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; +import javacard.framework.Applet; +import javacard.framework.AppletEvent; +import javacard.framework.ISO7816; +import javacard.framework.Util; +import javacardx.apdu.ExtendedLength; + +/** + * KMKeymasterApplet implements the javacard applet. It creates repository and other install time + * objects. It also implements the keymaster state machine and handles javacard applet life cycle + * events. + */ +// TODO Currently implementing ExtendedLength for ease of testing +// - 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 byte CLA_ISO7816_NO_SM_NO_CHAN = (byte) 0x80; + private static final byte KM_HAL_VERSION = (byte) 0x41; + + // 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; + + // State of the applet. + private byte keymasterState = ILLEGAL_STATE; + private KMRepository repository; + + /** + * Registers this applet. + * + * @param repo reference to the repository which manages all the NVM objects. + */ + protected KMKeymasterApplet(KMRepository repo) { + repository = repo; + register(); + } + + /** + * Installs this applet. + * + * @param bArray the array containing installation parameters + * @param bOffset the starting offset in bArray + * @param bLength the length in bytes of the parameter data in bArray + */ + public static void install(byte[] bArray, short bOffset, byte bLength) { + 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); + } + + /** + * Selects this applet. + * + * @return Returns true if the keymaster is in correct state + */ + @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); + } else { + return false; + } + return true; + } + + /** De-selects this applet. */ + @Override + public void deselect() { + repository.onDeselect(); + if (getKeymasterState() == KMKeymasterApplet.ACTIVE_STATE) { + setKeymasterState(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); + } + } + + /** + * 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(); + // 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 this is select applet apdu which is selecting this applet then return + if (apdu.isISOInterindustryCLA()) { + if (selectingApplet()) { + 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]; + + // 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); + } + + // 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); + } + // 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); + } + + // 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 + } + } + + /** + * 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(); + if (srcLength > MAX_LENGTH) { + throw new KMException(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) { + // 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); + } + while (recvLen > 0) { + Util.arrayCopyNonAtomic(srcBuffer, srcOffset, destBuffer, destOffset, recvLen); + destOffset += recvLen; + recvLen = apdu.receiveBytes(srcOffset); + } + // Update the Context + context.setBufferLength(srcLength); + } + + /** + * Getter for keymaster state. + * + * @return keymasterState - current state of the applet. + */ + private byte getKeymasterState() { + return keymasterState; + } + /** Setter for keymaster state. */ + private void setKeymasterState(byte keymasterState) { + this.keymasterState = keymasterState; + } +} diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMMessenger.java b/Applet/Applet/src/com/android/javacard/keymaster/KMMessenger.java new file mode 100644 index 00000000..3eb4b548 --- /dev/null +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMMessenger.java @@ -0,0 +1,22 @@ +/* + * 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 new file mode 100644 index 00000000..5aaa9948 --- /dev/null +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMOperationState.java @@ -0,0 +1,52 @@ +/* + * 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; + +// TODO complete the class design and implementation +public class KMOperationState { + private KMInteger operationHandle; + + 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) { + opStateRefTable[index] = new KMOperationState(); + index++; + } + } + + public KMInteger getOperationHandle() { + return operationHandle; + } + + 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 new file mode 100644 index 00000000..d1911f7e --- /dev/null +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMProvisionCmd.java @@ -0,0 +1,61 @@ +/* + * 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 new file mode 100644 index 00000000..f2de2cec --- /dev/null +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMRepository.java @@ -0,0 +1,514 @@ +/* + * 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; +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 { + private static final byte CMD_TABLE_LENGTH = 20; + private static final byte REF_TABLE_SIZE = 10; + 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 KMCommand[] commandTable = null; + private KMContext context = null; + private byte[] buffer = null; + private AESKey masterKey = null; + private boolean contextLocked = false; + 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; + + public 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 = + (AESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_AES, KeyBuilder.LENGTH_AES_256, false); + } + // Initialize types + KMType.initialize(this); + byteBlobRefTable = new KMByteBlob[REF_TABLE_SIZE]; + KMByteBlob.create(byteBlobRefTable); + integerRefTable = new KMInteger[REF_TABLE_SIZE]; + KMInteger.create(integerRefTable); + arrayRefTable = new KMArray[REF_TABLE_SIZE]; + KMArray.create(arrayRefTable); + vectorRefTable = new KMVector[REF_TABLE_SIZE]; + KMVector.create(vectorRefTable); + enumRefTable = new KMEnum[REF_TABLE_SIZE]; + 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++; + } + JCSystem.commitTransaction(); + } + + public KMEncoder getEncoder() { + return encoder; + } + + 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; + } + + public void onProcess() { + reset(); + } + + 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); + byteHeapIndex = 0; + Util.arrayFillNonAtomic(buffer, (short) 0, (short) buffer.length, (byte) 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 resetTypeObjects(KMType[] type){ + byte index = 0; + while(index < type.length){ + type[index].init(); + index++; + } + } + public void onDeselect() { + // TODO clear operation state? + } + + public void onSelect() { + // Nothing to be done currently. + } + + public byte[] getBuffer() { + return buffer; + } + + 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 + throw new KMException(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 + throw new KMException(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + byte[] ret = (byte[]) uint64Array[uint64Index]; + uint64Index++; + return ret; + } else { + throw new KMException(ISO7816.SW_WRONG_LENGTH); + } + } + + public KMByteBlob newByteBlob() { + if (blobRefIndex >= byteBlobRefTable.length) { + // TODO this is placeholder exception value. + throw new KMException(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. + throw new KMException(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. + throw new KMException(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. + throw new KMException(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. + throw new KMException(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. + throw new KMException(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. + throw new KMException(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. + throw new KMException(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. + throw new KMException(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. + throw new KMException(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. + throw new KMException(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. + throw new KMException(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. + throw new KMException(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. + throw new KMException(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + KMVerificationToken ret = verTokenRefTable[verTokenRefIndex]; + verTokenRefIndex++; + return ret; + } + + public KMOperationState newOperationState() { + if (opStateRefIndex >= opStateRefTable.length) { + // TODO this is placeholder exception value. + throw new KMException(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + KMOperationState ret = operationStateTable[opStateRefIndex]; + opStateRefIndex++; + return ret; + } + + public void releaseOperationState(KMOperationState state){ + opStateRefIndex--; + if(opStateRefIndex <0){ + throw new KMException(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); + } + KMVector ret = vectorRefTable[vectorRefIndex]; + vectorRefIndex++; + return ret; + } + + public KMEnum newEnum() { + if (enumRefIndex >= enumRefTable.length) { + // TODO this is placeholder exception value. + throw new KMException(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. + throw new KMException(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. + throw new KMException(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + byteHeapIndex += length; + return (short) (byteHeapIndex - length); + } +} diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMTag.java b/Applet/Applet/src/com/android/javacard/keymaster/KMTag.java new file mode 100644 index 00000000..c6a1b5ae --- /dev/null +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMTag.java @@ -0,0 +1,23 @@ +/* + * 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 KMTag extends KMType { + public abstract short getTagType(); + + public abstract short getKey(); +} diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMType.java b/Applet/Applet/src/com/android/javacard/keymaster/KMType.java new file mode 100644 index 00000000..2aa08271 --- /dev/null +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMType.java @@ -0,0 +1,236 @@ +/* + * 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 KMType { + public static final short TAG_TYPE_MASK = (short) 0xF000; + public static final short INVALID_TAG = 0x0000; + public static final short ENUM_TAG = 0x1000; + public static final short ENUM_ARRAY_TAG = 0x2000; + public static final short UINT_TAG = 0x3000; + public static final short UINT_ARRAY_TAG = 0x4000; + public static final short ULONG_TAG = 0x5000; + public static final short DATE_TAG = 0x6000; + public static final short BOOL_TAG = 0x7000; + public static final short BIGNUM_TAG = (short) 0x8000; + public static final short BYTES_TAG = (short) 0x9000; + public static final short ULONG_ARRAY_TAG = (short) 0xA000; + + // Enum Tag + // Algorithm Enum Tag key and values + public static final short ALGORITHM = 0x0002; + public static final byte RSA = 0x01; + public static final byte DES = 0x21; + public static final byte EC = 0x03; + public static final byte AES = 0x20; + public static final byte HMAC = (byte) 0x80; + + // EcCurve Enum Tag key and values. + public static final short ECCURVE = 0x000A; + public static final byte P_224 = 0x00; + public static final byte P_256 = 0x01; + public static final byte P_384 = 0x02; + public static final byte P_521 = 0x03; + + // KeyBlobUsageRequirements Enum Tag key and values. + public static final short BLOB_USAGE_REQ = 0x012D; + public static final byte STANDALONE = 0x00; + public static final byte REQUIRES_FILE_SYSTEM = 0x01; + + // HardwareAuthenticatorType Enum Tag key and values. + public static final short USER_AUTH_TYPE = 0x01F8; + public static final byte USER_AUTH_NONE = 0x00; + public static final byte PASSWORD = 0x01; + public static final byte FINGERPRINT = 0x02; + public static final byte ANY = (byte) 0xFF; + + // Origin Enum Tag key and values. + public static final short ORIGIN = 0x02BE; + public static final byte GENERATED = 0x00; + public static final byte DERIVED = 0x01; + public static final byte IMPORTED = 0x02; + public static final byte UNKNOWN = 0x03; + public static final byte SECURELY_IMPORTED = 0x04; + + // Hardware Type tag key and values + public static final short HARDWARE_TYPE = 0x0130; + public static final byte SOFTWARE = 0x00; + public static final byte TRUSTED_ENVIRONMENT = 0x01; + public static final byte STRONGBOX = 0x02; + + // No Tag + // Derivation Function - No Tag defined + public static final short KEY_DERIVATION_FUNCTION = (short) 0xF001; + public static final byte DERIVATION_NONE = 0x00; + public static final byte RFC5869_SHA256 = 0x01; + public static final byte ISO18033_2_KDF1_SHA1 = 0x02; + public static final byte ISO18033_2_KDF1_SHA256 = 0x03; + public static final byte ISO18033_2_KDF2_SHA1 = 0x04; + public static final byte ISO18033_2_KDF2_SHA256 = 0x05; + + // KeyFormat - No Tag defined. + public static final short KEY_FORMAT = (short) 0xF002; + public static final byte X509 = 0x00; + public static final byte PKCS8 = 0x01; + public static final byte RAW = 0x03; + + // 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; + + // Block mode + public static final short BLOCK_MODE = 0x0004; + public static final byte ECB = 0x01; + public static final byte CBC = 0x02; + public static final byte CTR = 0x04; + + // Digest + public static final short DIGEST = 0x0005; + public static final byte DIGEST_NONE = 0x00; + public static final byte MD5 = 0x01; + public static final byte SHA1 = 0x02; + public static final byte SHA2_224 = 0x03; + public static final byte SHA2_256 = 0x04; + public static final byte SHA2_384 = 0x05; + public static final byte SHA2_512 = 0x06; + + // Padding mode + public static final short PADDING = 0x0006; + public static final byte PADDING_NONE = 0x01; + public static final byte RSA_OAEP = 0x02; + public static final byte RSA_PSS = 0x03; + public static final byte RSA_PKCS1_1_5_ENCRYPT = 0x04; + public static final byte RSA_PKCS1_1_5_SIGN = 0x05; + public static final byte PKCS7 = 0x40; + + // Integer Tag - UINT, ULONG and DATE + // UINT tags + // Keysize + public static final short KEYSIZE = 0x0003; + // Min Mac Length + public static final short MIN_MAC_LENGTH = 0x0008; + // Min Seconds between OPS + public static final short MIN_SEC_BETWEEN_OPS = 0x0193; + // Max Uses per Boot + public static final short MAX_USES_PER_BOOT = 0x0194; + // UserId + public static final short USERID = 0x01F5; + // Auth Timeout + public static final short AUTH_TIMEOUT = 0x01F9; + // OS Version + public static final short OS_VERSION = 0x02C1; + // OS Patch Level + public static final short OS_PATCH_LEVEL = 0x02C2; + // Vendor Patch Level + public static final short VENDOR_PATCH_LEVEL = 0x02CE; + // Boot Patch Level + public static final short BOOT_PATCH_LEVEL = 0x02CF; + // Mac Length + public static final short MAC_LENGTH = 0x03EB; + + // ULONG tags + // RSA Public Exponent + public static final short RSA_PUBLIC_EXPONENT = 0x00C8; + + // DATE tags + public static final short ACTIVE_DATETIME = 0x0190; + public static final short ORIGINATION_EXPIRE_DATETIME = 0x0191; + public static final short USAGE_EXPIRE_DATETIME = 0x0192; + public static final short CREATION_DATETIME = 0x0193; + + // Integer Array Tags - ULONG_REP and UINT_REP. + // User Secure Id + public static final short USER_SECURE_ID = (short) 0x01F6; + + // Boolean Tag + // Caller Nonce + public static final short CALLER_NONCE = (short) 0x0007; + // Include Unique Id + public static final short INCLUDE_UNIQUE_ID = (short) 0x00CA; + // Bootloader Only + public static final short BOOTLOADER_ONLY = (short) 0x012E; + // Rollback Resistance + public static final short ROLLBACK_RESISTANCE = (short) 0x012F; + // No Auth Required + public static final short NO_AUTH_REQUIRED = (short) 0x01F7; + // Allow While On Body + public static final short ALLOW_WHILE_ON_BODY = (short) 0x01FA; + // Trusted User Presence Required + public static final short TRUSTED_USER_PRESENCE_REQUIRED = (short) 0x01FB; + // Trusted Confirmation Required + public static final short TRUSTED_CONFIRMATION_REQUIRED = (short) 0x01FC; + // Unlocked Device Required + public static final short UNLOCKED_DEVICE_REQUIRED = (short) 0x01FD; + // Reset Since Id Rotation + public static final short RESET_SINCE_ID_ROTATION = (short) 0x03EC; + + // Byte Tag + // Application Id + public static final short APPLICATION_ID = (short) 0x0259; + // Application Data + public static final short APPLICATION_DATA = (short) 0x02BC; + // Root Of Trust + public static final short ROOT_OF_TRUST = (short) 0x02C0; + // Unique Id + public static final short UNIQUE_ID = (short) 0x02C3; + // Attestation Challenge + public static final short ATTESTATION_CHALLENGE = (short) 0x02C4; + // Attestation Application Id + public static final short ATTESTATION_APPLICATION_ID = (short) 0x02C5; + // Attestation Id Brand + public static final short ATTESTATION_ID_BRAND = (short) 0x02C6; + // Attestation Id Device + public static final short ATTESTATION_ID_DEVICE = (short) 0x02C7; + // Attestation Id Product + public static final short ATTESTATION_ID_PRODUCT = (short) 0x02C8; + // Attestation Id Serial + public static final short ATTESTATION_ID_SERIAL = (short) 0x02C9; + // Attestation Id IMEI + public static final short ATTESTATION_ID_IMEI = (short) 0x02CA; + // Attestation Id MEID + public static final short ATTESTATION_ID_MEID = (short) 0x02CB; + // Attestation Id Manufacturer + public static final short ATTESTATION_ID_MANUFACTURER = (short) 0x02CC; + // Attestation Id Model + public static final short ATTESTATION_ID_MODEL = (short) 0x02CD; + // Associated Data + public static final short ASSOCIATED_DATA = (short) 0x03E8; + // Nonce + public static final short NONCE = (short) 0x03E9; + // Confirmation Token + public static final short CONFIRMATION_TOKEN = (short) 0x03ED; + + public static final short LENGTH_FROM_PDU = (short) 0xFFFF; + + public static final byte NO_VALUE = (byte) 0xff; + + protected static KMRepository repository; + + public static void initialize(KMRepository repo) { + KMType.repository = repo; + } + + public abstract void init(); + + public abstract short length(); +} diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMUpdateOperationCmd.java b/Applet/Applet/src/com/android/javacard/keymaster/KMUpdateOperationCmd.java new file mode 100644 index 00000000..5b1e58f6 --- /dev/null +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMUpdateOperationCmd.java @@ -0,0 +1,36 @@ +/* + * 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 new file mode 100644 index 00000000..d883068f --- /dev/null +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMUpgradeKeyCmd.java @@ -0,0 +1,36 @@ +/* + * 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/KMVector.java b/Applet/Applet/src/com/android/javacard/keymaster/KMVector.java new file mode 100644 index 00000000..fb9d20f5 --- /dev/null +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMVector.java @@ -0,0 +1,85 @@ +/* + * 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 new file mode 100644 index 00000000..f52fae55 --- /dev/null +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMVerificationToken.java @@ -0,0 +1,94 @@ +/* + * 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; + +public class KMVerificationToken extends KMType { + public static final byte CHALLENGE = 0x00; + public static final byte TIMESTAMP = 0x01; + public static final byte PARAMETERS_VERIFIED = 0x02; + public static final byte SECURITY_LEVEL = 0x03; + public static final byte MAC = 0x04; + private KMArray vals; + + private KMVerificationToken() { + init(); + } + + @Override + public void init() { + vals = null; + } + + @Override + public short length() { + return vals.length(); + } + + public static void create(KMVerificationToken[] verTokenRefTable) { + byte index = 0; + while (index < verTokenRefTable.length) { + verTokenRefTable[index] = new KMVerificationToken(); + index++; + } + } + + 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 static KMVerificationToken instance(KMArray vals) { + if (vals.length() != 5) { + throw new KMException(ISO7816.SW_WRONG_LENGTH); + } + KMVerificationToken inst = repository.newVerificationToken(); + inst.vals = vals; + return inst; + } + + public KMInteger getChallenge() { + return (KMInteger) vals.get(CHALLENGE); + } + + public KMInteger getTimestamp() { + return (KMInteger) vals.get(TIMESTAMP); + } + + public KMKeyParameters getParametersVerified() { + return (KMKeyParameters) vals.get(PARAMETERS_VERIFIED); + } + + public byte getSecurityLevel() { + return ((KMEnumTag) vals.get(SECURITY_LEVEL)).getValue(); + } + + public KMByteBlob getMac() { + return (KMByteBlob) vals.get(MAC); + } + + public KMArray getVals() { + return vals; + } +} diff --git a/Applet/Applet/src/com/android/javacard/keymaster/KMVerifyAuthorizationCmd.java b/Applet/Applet/src/com/android/javacard/keymaster/KMVerifyAuthorizationCmd.java new file mode 100644 index 00000000..33289b44 --- /dev/null +++ b/Applet/Applet/src/com/android/javacard/keymaster/KMVerifyAuthorizationCmd.java @@ -0,0 +1,36 @@ +/* + * 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; + } +} diff --git a/Applet/Applet/test/com/android/javacard/test/KMFrameworkTest.java b/Applet/Applet/test/com/android/javacard/test/KMFrameworkTest.java new file mode 100644 index 00000000..973f681b --- /dev/null +++ b/Applet/Applet/test/com/android/javacard/test/KMFrameworkTest.java @@ -0,0 +1,134 @@ +/* + * Copyright(C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.javacard.test; + +import com.android.javacard.keymaster.KMArray; +import com.android.javacard.keymaster.KMByteBlob; +import com.android.javacard.keymaster.KMDecoder; +import com.android.javacard.keymaster.KMEncoder; +import com.android.javacard.keymaster.KMEnum; +import com.android.javacard.keymaster.KMEnumTag; +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 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 { + + @Test + public void test_Lifecycle_Success() { + // Create simulator + CardSimulator simulator = new CardSimulator(); + + // Install applet + AID appletAID1 = AIDUtil.create("A000000062"); + simulator.installApplet(appletAID1, KMKeymasterApplet.class); + + // Select applet + simulator.selectApplet(appletAID1); + testProvisionCmd(simulator); + testGetHwInfoCmd(simulator); + // Delete i.e. uninstall applet + simulator.deleteApplet(appletAID1); + } + + public void testProvisionCmd(CardSimulator simulator){ + byte[] buf = new byte[512]; + // test provision command + KMArray 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); + //print(commandAPDU.getBytes());; + ResponseAPDU response = simulator.transmitCommand(commandAPDU); + Assert.assertEquals(0x9000, response.getSW()); + } + + public void testGetHwInfoCmd(CardSimulator simulator){ + byte[] buf = new byte[512]; + CommandAPDU commandAPDU = new CommandAPDU(0x80, 0x1E, 0x40, 0x00); + //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()); + 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()); + Assert.assertEquals(KMType.STRONGBOX, secLevel.getVal()); + String kmNameStr = byteBlobToString(kmName); + String authorNameStr = byteBlobToString(authorName); + Assert.assertEquals( "JavacardKeymasterDevice",kmNameStr); + Assert.assertEquals( "Google",authorNameStr); + Assert.assertEquals(0x9000, response.getSW()); + } + + private String byteBlobToString(KMByteBlob blob) { + StringBuilder sb = new StringBuilder(); + for(short i = 0; i0)){ + sb.append(";\n"); + } + } + System.out.println(sb.toString()); + } + + private KMArray makeProvisionCmd() { + // Argument 1 + KMArray vals = + KMArray.instance((short) 1) + .add((short) 0, KMEnumTag.instance(KMType.ALGORITHM, KMType.RSA)); + KMKeyParameters keyparams = KMKeyParameters.instance(vals); + // Argument 2 + KMEnum keyFormat = 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; + } + KMByteBlob keyBlob = KMByteBlob.instance(byteBlob, (short) 0, (short)byteBlob.length); + // 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/JavaCardKeymaster.opt b/Applet/JavaCardKeymaster.opt new file mode 100644 index 00000000..77c1931e --- /dev/null +++ b/Applet/JavaCardKeymaster.opt @@ -0,0 +1,5 @@ +-out EXP JCA CAP +-exportpath api_export_files_3.1.0 +-applet 0xa0:0x0:0x0:0x0:0x62:0x3:0x1:0xc:0x1:0x1 com.android.javacard.keymaster.KMKeymasterApplet +com.android.javacard.keymaster +0xa0:0x0:0x0:0x0:0x62:0x3:0x1:0xc:0x1 1.0 diff --git a/Applet/JavaCardKeymaster.scr b/Applet/JavaCardKeymaster.scr new file mode 100644 index 00000000..1a914761 --- /dev/null +++ b/Applet/JavaCardKeymaster.scr @@ -0,0 +1,14 @@ +output on; + +//create applet instance +0x80 0xB8 0x00 0x00 0x0c 0x0a 0xa0 0x00 0x00 0x00 0x62 0x03 0x01 0xc 0x01 0x01 0x00 0x7F; + + +// Select JavaCardKeymaster //aid/A000000062/03010C0101 +0x00 0xa4 0x04 0x00 0x0a 0xa0 0x00 0x00 0x00 0x62 0x03 0x01 0xc 0x01 0x01 0x7F; + +// Send provision command - this will change in future +0x80 0x23 0x40 0x00 0x3B 0x83 0xA1 0x1A 0x10 0x00 0x00 0x02 0x01 0x00 0x58 0x30 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0A 0x0B 0x0C 0x0D 0x0E 0x0F 0x10 0x11 0x12 0x13 0x14 0x15 0x16 0x17 0x18 0x19 0x1A 0x1B 0x1C 0x1D 0x1E 0x1F 0x20 0x21 0x22 0x23 0x24 0x25 0x26 0x27 0x28 0x29 0x2A 0x2B 0x2C 0x2D 0x2E 0x2F 0x7F; + +// Send getHardwareInfo command +0x80 0x1E 0x40 0x00 0x00 0x7F; \ No newline at end of file diff --git a/Applet/api_export_files_3.0.5/java/io/javacard/io.exp b/Applet/api_export_files_3.0.5/java/io/javacard/io.exp new file mode 100644 index 00000000..36b9d18b Binary files /dev/null and b/Applet/api_export_files_3.0.5/java/io/javacard/io.exp differ diff --git a/Applet/api_export_files_3.0.5/java/lang/javacard/lang.exp b/Applet/api_export_files_3.0.5/java/lang/javacard/lang.exp new file mode 100644 index 00000000..272eebba Binary files /dev/null and b/Applet/api_export_files_3.0.5/java/lang/javacard/lang.exp differ diff --git a/Applet/api_export_files_3.0.5/java/rmi/javacard/rmi.exp b/Applet/api_export_files_3.0.5/java/rmi/javacard/rmi.exp new file mode 100644 index 00000000..8c695237 Binary files /dev/null and b/Applet/api_export_files_3.0.5/java/rmi/javacard/rmi.exp differ diff --git a/Applet/api_export_files_3.0.5/javacard/framework/javacard/framework.exp b/Applet/api_export_files_3.0.5/javacard/framework/javacard/framework.exp new file mode 100644 index 00000000..f604261a Binary files /dev/null and b/Applet/api_export_files_3.0.5/javacard/framework/javacard/framework.exp differ diff --git a/Applet/api_export_files_3.0.5/javacard/framework/service/javacard/service.exp b/Applet/api_export_files_3.0.5/javacard/framework/service/javacard/service.exp new file mode 100644 index 00000000..e83ae8a4 Binary files /dev/null and b/Applet/api_export_files_3.0.5/javacard/framework/service/javacard/service.exp differ diff --git a/Applet/api_export_files_3.0.5/javacard/security/javacard/security.exp b/Applet/api_export_files_3.0.5/javacard/security/javacard/security.exp new file mode 100644 index 00000000..9b2b8d44 Binary files /dev/null and b/Applet/api_export_files_3.0.5/javacard/security/javacard/security.exp differ diff --git a/Applet/api_export_files_3.0.5/javacardx/apdu/javacard/apdu.exp b/Applet/api_export_files_3.0.5/javacardx/apdu/javacard/apdu.exp new file mode 100644 index 00000000..ce4ac0c9 Binary files /dev/null and b/Applet/api_export_files_3.0.5/javacardx/apdu/javacard/apdu.exp differ diff --git a/Applet/api_export_files_3.0.5/javacardx/apdu/util/javacard/util.exp b/Applet/api_export_files_3.0.5/javacardx/apdu/util/javacard/util.exp new file mode 100644 index 00000000..cf2fc718 Binary files /dev/null and b/Applet/api_export_files_3.0.5/javacardx/apdu/util/javacard/util.exp differ diff --git a/Applet/api_export_files_3.0.5/javacardx/biometry/javacard/biometry.exp b/Applet/api_export_files_3.0.5/javacardx/biometry/javacard/biometry.exp new file mode 100644 index 00000000..f1779d01 Binary files /dev/null and b/Applet/api_export_files_3.0.5/javacardx/biometry/javacard/biometry.exp differ diff --git a/Applet/api_export_files_3.0.5/javacardx/biometry1toN/javacard/biometry1toN.exp b/Applet/api_export_files_3.0.5/javacardx/biometry1toN/javacard/biometry1toN.exp new file mode 100644 index 00000000..057adb00 Binary files /dev/null and b/Applet/api_export_files_3.0.5/javacardx/biometry1toN/javacard/biometry1toN.exp differ diff --git a/Applet/api_export_files_3.0.5/javacardx/crypto/javacard/crypto.exp b/Applet/api_export_files_3.0.5/javacardx/crypto/javacard/crypto.exp new file mode 100644 index 00000000..b6f6b2f8 Binary files /dev/null and b/Applet/api_export_files_3.0.5/javacardx/crypto/javacard/crypto.exp differ diff --git a/Applet/api_export_files_3.0.5/javacardx/external/javacard/external.exp b/Applet/api_export_files_3.0.5/javacardx/external/javacard/external.exp new file mode 100644 index 00000000..cd85799e Binary files /dev/null and b/Applet/api_export_files_3.0.5/javacardx/external/javacard/external.exp differ diff --git a/Applet/api_export_files_3.0.5/javacardx/framework/math/javacard/math.exp b/Applet/api_export_files_3.0.5/javacardx/framework/math/javacard/math.exp new file mode 100644 index 00000000..af2f608c Binary files /dev/null and b/Applet/api_export_files_3.0.5/javacardx/framework/math/javacard/math.exp differ diff --git a/Applet/api_export_files_3.0.5/javacardx/framework/string/javacard/string.exp b/Applet/api_export_files_3.0.5/javacardx/framework/string/javacard/string.exp new file mode 100644 index 00000000..ef6c5cb7 Binary files /dev/null and b/Applet/api_export_files_3.0.5/javacardx/framework/string/javacard/string.exp differ diff --git a/Applet/api_export_files_3.0.5/javacardx/framework/tlv/javacard/tlv.exp b/Applet/api_export_files_3.0.5/javacardx/framework/tlv/javacard/tlv.exp new file mode 100644 index 00000000..5a6d027c Binary files /dev/null and b/Applet/api_export_files_3.0.5/javacardx/framework/tlv/javacard/tlv.exp differ diff --git a/Applet/api_export_files_3.0.5/javacardx/framework/util/intx/javacard/intx.exp b/Applet/api_export_files_3.0.5/javacardx/framework/util/intx/javacard/intx.exp new file mode 100644 index 00000000..ed00a395 Binary files /dev/null and b/Applet/api_export_files_3.0.5/javacardx/framework/util/intx/javacard/intx.exp differ diff --git a/Applet/api_export_files_3.0.5/javacardx/framework/util/javacard/util.exp b/Applet/api_export_files_3.0.5/javacardx/framework/util/javacard/util.exp new file mode 100644 index 00000000..25358d9d Binary files /dev/null and b/Applet/api_export_files_3.0.5/javacardx/framework/util/javacard/util.exp differ diff --git a/Applet/api_export_files_3.0.5/javacardx/security/javacard/security.exp b/Applet/api_export_files_3.0.5/javacardx/security/javacard/security.exp new file mode 100644 index 00000000..30b3ed6e Binary files /dev/null and b/Applet/api_export_files_3.0.5/javacardx/security/javacard/security.exp differ diff --git a/Applet/api_export_files_3.1.0/java/io/javacard/io.exp b/Applet/api_export_files_3.1.0/java/io/javacard/io.exp new file mode 100644 index 00000000..36b9d18b Binary files /dev/null and b/Applet/api_export_files_3.1.0/java/io/javacard/io.exp differ diff --git a/Applet/api_export_files_3.1.0/java/lang/javacard/lang.exp b/Applet/api_export_files_3.1.0/java/lang/javacard/lang.exp new file mode 100644 index 00000000..272eebba Binary files /dev/null and b/Applet/api_export_files_3.1.0/java/lang/javacard/lang.exp differ diff --git a/Applet/api_export_files_3.1.0/java/rmi/javacard/rmi.exp b/Applet/api_export_files_3.1.0/java/rmi/javacard/rmi.exp new file mode 100644 index 00000000..8c695237 Binary files /dev/null and b/Applet/api_export_files_3.1.0/java/rmi/javacard/rmi.exp differ diff --git a/Applet/api_export_files_3.1.0/javacard/framework/javacard/framework.exp b/Applet/api_export_files_3.1.0/javacard/framework/javacard/framework.exp new file mode 100644 index 00000000..e360b447 Binary files /dev/null and b/Applet/api_export_files_3.1.0/javacard/framework/javacard/framework.exp differ diff --git a/Applet/api_export_files_3.1.0/javacard/framework/service/javacard/service.exp b/Applet/api_export_files_3.1.0/javacard/framework/service/javacard/service.exp new file mode 100644 index 00000000..69fd1b28 Binary files /dev/null and b/Applet/api_export_files_3.1.0/javacard/framework/service/javacard/service.exp differ diff --git a/Applet/api_export_files_3.1.0/javacard/security/javacard/security.exp b/Applet/api_export_files_3.1.0/javacard/security/javacard/security.exp new file mode 100644 index 00000000..11514947 Binary files /dev/null and b/Applet/api_export_files_3.1.0/javacard/security/javacard/security.exp differ diff --git a/Applet/api_export_files_3.1.0/javacardx/apdu/javacard/apdu.exp b/Applet/api_export_files_3.1.0/javacardx/apdu/javacard/apdu.exp new file mode 100644 index 00000000..ce4ac0c9 Binary files /dev/null and b/Applet/api_export_files_3.1.0/javacardx/apdu/javacard/apdu.exp differ diff --git a/Applet/api_export_files_3.1.0/javacardx/apdu/util/javacard/util.exp b/Applet/api_export_files_3.1.0/javacardx/apdu/util/javacard/util.exp new file mode 100644 index 00000000..cf2fc718 Binary files /dev/null and b/Applet/api_export_files_3.1.0/javacardx/apdu/util/javacard/util.exp differ diff --git a/Applet/api_export_files_3.1.0/javacardx/biometry/javacard/biometry.exp b/Applet/api_export_files_3.1.0/javacardx/biometry/javacard/biometry.exp new file mode 100644 index 00000000..2bdf2c40 Binary files /dev/null and b/Applet/api_export_files_3.1.0/javacardx/biometry/javacard/biometry.exp differ diff --git a/Applet/api_export_files_3.1.0/javacardx/biometry1toN/javacard/biometry1toN.exp b/Applet/api_export_files_3.1.0/javacardx/biometry1toN/javacard/biometry1toN.exp new file mode 100644 index 00000000..3b93e625 Binary files /dev/null and b/Applet/api_export_files_3.1.0/javacardx/biometry1toN/javacard/biometry1toN.exp differ diff --git a/Applet/api_export_files_3.1.0/javacardx/crypto/javacard/crypto.exp b/Applet/api_export_files_3.1.0/javacardx/crypto/javacard/crypto.exp new file mode 100644 index 00000000..679c29d6 Binary files /dev/null and b/Applet/api_export_files_3.1.0/javacardx/crypto/javacard/crypto.exp differ diff --git a/Applet/api_export_files_3.1.0/javacardx/external/javacard/external.exp b/Applet/api_export_files_3.1.0/javacardx/external/javacard/external.exp new file mode 100644 index 00000000..aca62699 Binary files /dev/null and b/Applet/api_export_files_3.1.0/javacardx/external/javacard/external.exp differ diff --git a/Applet/api_export_files_3.1.0/javacardx/framework/event/javacard/event.exp b/Applet/api_export_files_3.1.0/javacardx/framework/event/javacard/event.exp new file mode 100644 index 00000000..bf463690 Binary files /dev/null and b/Applet/api_export_files_3.1.0/javacardx/framework/event/javacard/event.exp differ diff --git a/Applet/api_export_files_3.1.0/javacardx/framework/math/javacard/math.exp b/Applet/api_export_files_3.1.0/javacardx/framework/math/javacard/math.exp new file mode 100644 index 00000000..af2f608c Binary files /dev/null and b/Applet/api_export_files_3.1.0/javacardx/framework/math/javacard/math.exp differ diff --git a/Applet/api_export_files_3.1.0/javacardx/framework/nio/javacard/nio.exp b/Applet/api_export_files_3.1.0/javacardx/framework/nio/javacard/nio.exp new file mode 100644 index 00000000..667743fd Binary files /dev/null and b/Applet/api_export_files_3.1.0/javacardx/framework/nio/javacard/nio.exp differ diff --git a/Applet/api_export_files_3.1.0/javacardx/framework/string/javacard/string.exp b/Applet/api_export_files_3.1.0/javacardx/framework/string/javacard/string.exp new file mode 100644 index 00000000..47df6293 Binary files /dev/null and b/Applet/api_export_files_3.1.0/javacardx/framework/string/javacard/string.exp differ diff --git a/Applet/api_export_files_3.1.0/javacardx/framework/time/javacard/time.exp b/Applet/api_export_files_3.1.0/javacardx/framework/time/javacard/time.exp new file mode 100644 index 00000000..8621ad8f Binary files /dev/null and b/Applet/api_export_files_3.1.0/javacardx/framework/time/javacard/time.exp differ diff --git a/Applet/api_export_files_3.1.0/javacardx/framework/tlv/javacard/tlv.exp b/Applet/api_export_files_3.1.0/javacardx/framework/tlv/javacard/tlv.exp new file mode 100644 index 00000000..142d9923 Binary files /dev/null and b/Applet/api_export_files_3.1.0/javacardx/framework/tlv/javacard/tlv.exp differ diff --git a/Applet/api_export_files_3.1.0/javacardx/framework/util/intx/javacard/intx.exp b/Applet/api_export_files_3.1.0/javacardx/framework/util/intx/javacard/intx.exp new file mode 100644 index 00000000..39008657 Binary files /dev/null and b/Applet/api_export_files_3.1.0/javacardx/framework/util/intx/javacard/intx.exp differ diff --git a/Applet/api_export_files_3.1.0/javacardx/framework/util/javacard/util.exp b/Applet/api_export_files_3.1.0/javacardx/framework/util/javacard/util.exp new file mode 100644 index 00000000..35523adf Binary files /dev/null and b/Applet/api_export_files_3.1.0/javacardx/framework/util/javacard/util.exp differ diff --git a/Applet/api_export_files_3.1.0/javacardx/security/cert/javacard/cert.exp b/Applet/api_export_files_3.1.0/javacardx/security/cert/javacard/cert.exp new file mode 100644 index 00000000..4312b4d4 Binary files /dev/null and b/Applet/api_export_files_3.1.0/javacardx/security/cert/javacard/cert.exp differ diff --git a/Applet/api_export_files_3.1.0/javacardx/security/derivation/javacard/derivation.exp b/Applet/api_export_files_3.1.0/javacardx/security/derivation/javacard/derivation.exp new file mode 100644 index 00000000..5c740cff Binary files /dev/null and b/Applet/api_export_files_3.1.0/javacardx/security/derivation/javacard/derivation.exp differ diff --git a/Applet/api_export_files_3.1.0/javacardx/security/javacard/security.exp b/Applet/api_export_files_3.1.0/javacardx/security/javacard/security.exp new file mode 100644 index 00000000..30b3ed6e Binary files /dev/null and b/Applet/api_export_files_3.1.0/javacardx/security/javacard/security.exp differ diff --git a/Applet/api_export_files_3.1.0/javacardx/security/util/javacard/util.exp b/Applet/api_export_files_3.1.0/javacardx/security/util/javacard/util.exp new file mode 100644 index 00000000..7f443343 Binary files /dev/null and b/Applet/api_export_files_3.1.0/javacardx/security/util/javacard/util.exp differ diff --git a/Applet/build.xml b/Applet/build.xml new file mode 100644 index 00000000..17b0a546 --- /dev/null +++ b/Applet/build.xml @@ -0,0 +1,163 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Applet/default.output b/Applet/default.output new file mode 100644 index 00000000..2e85130b --- /dev/null +++ b/Applet/default.output @@ -0,0 +1,8 @@ +CLA: 00, INS: a4, P1: 04, P2: 00, Lc: 09, a0, 00, 00, 00, 62, 03, 01, 08, 01, Le: 00, SW1: 90, SW2: 00 +CAP file download section. Output suppressed. +OUTPUT OFF; +OUTPUT ON; +CLA: 80, INS: b8, P1: 00, P2: 00, Lc: 0c, 0a, a0, 00, 00, 00, 62, 03, 01, 0c, 01, 01, 00, Le: 0a, a0, 00, 00, 00, 62, 03, 01, 0c, 01, 01, SW1: 90, SW2: 00 +CLA: 00, INS: a4, P1: 04, P2: 00, Lc: 0a, a0, 00, 00, 00, 62, 03, 01, 0c, 01, 01, Le: 00, SW1: 90, SW2: 00 +CLA: 80, INS: 23, P1: 40, P2: 00, Lc: 3b, 83, a1, 1a, 10, 00, 00, 02, 01, 00, 58, 30, 00, 01, 02, 03, 04, 05, 06, 07, 08, 09, 0a, 0b, 0c, 0d, 0e, 0f, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 1a, 1b, 1c, 1d, 1e, 1f, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 2a, 2b, 2c, 2d, 2e, 2f, Le: 00, SW1: 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 diff --git a/Applet/lib/hamcrest-core-1.3.jar b/Applet/lib/hamcrest-core-1.3.jar new file mode 100644 index 00000000..9d5fe16e Binary files /dev/null and b/Applet/lib/hamcrest-core-1.3.jar differ diff --git a/Applet/lib/jcardsim-3.0.5-SNAPSHOT.jar b/Applet/lib/jcardsim-3.0.5-SNAPSHOT.jar new file mode 100644 index 00000000..d756d67b Binary files /dev/null and b/Applet/lib/jcardsim-3.0.5-SNAPSHOT.jar differ diff --git a/Applet/lib/junit-4.13.jar b/Applet/lib/junit-4.13.jar new file mode 100644 index 00000000..acc3c432 Binary files /dev/null and b/Applet/lib/junit-4.13.jar differ diff --git a/Applet/powerdown.scr b/Applet/powerdown.scr new file mode 100644 index 00000000..7dca615e --- /dev/null +++ b/Applet/powerdown.scr @@ -0,0 +1,4 @@ + +// output on; + +powerdown; \ No newline at end of file diff --git a/Applet/powerup.scr b/Applet/powerup.scr new file mode 100644 index 00000000..79990dc4 --- /dev/null +++ b/Applet/powerup.scr @@ -0,0 +1,9 @@ +powerup; + +// Select the installer applet +0x00 0xA4 0x04 0x00 0x09 0xA0 0x00 0x00 0x00 0x62 0x03 0x01 0x08 0x01 0x7F; + +// Turn output off during CAP file download +echo "CAP file download section. Output suppressed."; + +output off;