diff --git a/Applet/JCardSimProvider/test/com/android/javacard/test/KMFunctionalTest.java b/Applet/JCardSimProvider/test/com/android/javacard/test/KMFunctionalTest.java new file mode 100644 index 00000000..f703a4db --- /dev/null +++ b/Applet/JCardSimProvider/test/com/android/javacard/test/KMFunctionalTest.java @@ -0,0 +1,2970 @@ +/* + * 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.KMAsn1Parser; +import com.android.javacard.keymaster.KMBoolTag; +import com.android.javacard.keymaster.KMByteBlob; +import com.android.javacard.keymaster.KMByteTag; +import com.android.javacard.keymaster.KMConfigurations; +import com.android.javacard.keymaster.KMCose; +import com.android.javacard.keymaster.KMCoseHeaders; +import com.android.javacard.keymaster.KMDecoder; +import com.android.javacard.keymaster.KMEncoder; +import com.android.javacard.keymaster.KMEnum; +import com.android.javacard.keymaster.KMEnumArrayTag; +import com.android.javacard.keymaster.KMEnumTag; +import com.android.javacard.keymaster.KMError; +import com.android.javacard.keymaster.KMHardwareAuthToken; +import com.android.javacard.keymaster.KMHmacSharingParameters; +import com.android.javacard.keymaster.KMInteger; +import com.android.javacard.keymaster.KMIntegerTag; +import com.android.javacard.keymaster.KMJCardSimApplet; +import com.android.javacard.keymaster.KMKeyCharacteristics; +import com.android.javacard.keymaster.KMKeyParameters; +import com.android.javacard.keymaster.KMKeymasterApplet; +import com.android.javacard.keymaster.KMKeymintDataStore; +import com.android.javacard.keymaster.KMNInteger; +import com.android.javacard.keymaster.KMOperationState; +import com.android.javacard.keymaster.KMRepository; +import com.android.javacard.keymaster.KMSimpleValue; +import com.android.javacard.keymaster.KMType; +import com.android.javacard.keymaster.KMVerificationToken; +import com.android.javacard.seprovider.KMHmacKey; +import com.android.javacard.seprovider.KMJCardSimulator; +import com.android.javacard.seprovider.KMSEProvider; +import com.licel.jcardsim.bouncycastle.util.encoders.Hex; +import com.licel.jcardsim.smartcardio.CardSimulator; +import com.licel.jcardsim.smartcardio.JCardSimProvider; +import com.licel.jcardsim.utils.AIDUtil; +import java.math.BigInteger; +import java.security.AlgorithmParameters; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.KeyFactory; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.SignatureException; +import java.security.spec.ECGenParameterSpec; +import java.security.spec.ECParameterSpec; +import java.security.spec.ECPoint; +import java.security.spec.ECPublicKeySpec; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.InvalidParameterSpecException; +import java.security.spec.MGF1ParameterSpec; +import java.security.spec.RSAPublicKeySpec; +import java.util.Arrays; +import java.util.Random; +import javacard.framework.AID; +import javacard.framework.Util; +import javacard.security.ECPublicKey; +import javacard.security.KeyBuilder; +import javacard.security.KeyPair; +import javacard.security.RSAPublicKey; +import javacard.security.Signature; +import javacardx.crypto.Cipher; +import javax.crypto.BadPaddingException; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.spec.OAEPParameterSpec; +import javax.crypto.spec.PSource; +import javax.smartcardio.CommandAPDU; +import javax.smartcardio.ResponseAPDU; +import org.junit.Assert; +import org.junit.Test; + +public class KMFunctionalTest { + + // Provider specific Commands + private static final byte KEYMINT_CMD_APDU_START = 0x20; + private static final byte INS_GENERATE_KEY_CMD = KEYMINT_CMD_APDU_START + 1; //0x21 + private static final byte INS_IMPORT_KEY_CMD = KEYMINT_CMD_APDU_START + 2; //0x22 + private static final byte INS_IMPORT_WRAPPED_KEY_CMD = KEYMINT_CMD_APDU_START + 3; //0x23 + private static final byte INS_EXPORT_KEY_CMD = KEYMINT_CMD_APDU_START + 4; //0x24 + private static final byte INS_ATTEST_KEY_CMD = KEYMINT_CMD_APDU_START + 5; //0x25 + private static final byte INS_UPGRADE_KEY_CMD = KEYMINT_CMD_APDU_START + 6; //0x26 + private static final byte INS_DELETE_KEY_CMD = KEYMINT_CMD_APDU_START + 7; //0x27 + private static final byte INS_DELETE_ALL_KEYS_CMD = KEYMINT_CMD_APDU_START + 8; //0x28 + private static final byte INS_ADD_RNG_ENTROPY_CMD = KEYMINT_CMD_APDU_START + 9; //0x29 + private static final byte INS_COMPUTE_SHARED_HMAC_CMD = KEYMINT_CMD_APDU_START + 10; //0x2A + private static final byte INS_DESTROY_ATT_IDS_CMD = KEYMINT_CMD_APDU_START + 11; //0x2B + private static final byte INS_VERIFY_AUTHORIZATION_CMD = KEYMINT_CMD_APDU_START + 12; //0x2C + private static final byte INS_GET_HMAC_SHARING_PARAM_CMD = KEYMINT_CMD_APDU_START + 13; //0x2D + private static final byte INS_GET_KEY_CHARACTERISTICS_CMD = KEYMINT_CMD_APDU_START + 14; //0x2E + private static final byte INS_GET_HW_INFO_CMD = KEYMINT_CMD_APDU_START + 15; //0x2F + private static final byte INS_BEGIN_OPERATION_CMD = KEYMINT_CMD_APDU_START + 16; //0x30 + private static final byte INS_UPDATE_OPERATION_CMD = KEYMINT_CMD_APDU_START + 17; //0x31 + private static final byte INS_FINISH_OPERATION_CMD = KEYMINT_CMD_APDU_START + 18; //0x32 + private static final byte INS_ABORT_OPERATION_CMD = KEYMINT_CMD_APDU_START + 19; //0x33 + private static final byte INS_DEVICE_LOCKED_CMD = KEYMINT_CMD_APDU_START + 20;//0x34 + private static final byte INS_EARLY_BOOT_ENDED_CMD = KEYMINT_CMD_APDU_START + 21; //0x35 + private static final byte INS_GET_CERT_CHAIN_CMD = KEYMINT_CMD_APDU_START + 22; //0x36 + private static final byte INS_UPDATE_AAD_OPERATION_CMD = KEYMINT_CMD_APDU_START + 23; //0x37 + private static final byte INS_BEGIN_IMPORT_WRAPPED_KEY_CMD = KEYMINT_CMD_APDU_START + 24; //0x38 + private static final byte INS_FINISH_IMPORT_WRAPPED_KEY_CMD = KEYMINT_CMD_APDU_START + 25; //0x39 + private static final byte INS_INIT_STRONGBOX_CMD = KEYMINT_CMD_APDU_START + 26; //0x3A + // RKP + public static final byte INS_GET_RKP_HARDWARE_INFO = KEYMINT_CMD_APDU_START + 27; //0x3B + public static final byte INS_GENERATE_RKP_KEY_CMD = KEYMINT_CMD_APDU_START + 28; //0x3C + public static final byte INS_BEGIN_SEND_DATA_CMD = KEYMINT_CMD_APDU_START + 29; //0x3D + public static final byte INS_UPDATE_KEY_CMD = KEYMINT_CMD_APDU_START + 30; //0x3E + public static final byte INS_UPDATE_EEK_CHAIN_CMD = KEYMINT_CMD_APDU_START + 31; //0x3F + public static final byte INS_UPDATE_CHALLENGE_CMD = KEYMINT_CMD_APDU_START + 32; //0x40 + public static final byte INS_FINISH_SEND_DATA_CMD = KEYMINT_CMD_APDU_START + 33; //0x41 + public static final byte INS_GET_RESPONSE_CMD = KEYMINT_CMD_APDU_START + 34; //0x42 + + private static final byte KEYMINT_CMD_APDU_END = KEYMINT_CMD_APDU_START + 48; //0x50 + private static final byte INS_END_KM_CMD = 0x7F; + private static final byte[] rsa_key_pkcs8 = { + (byte) 0x30, (byte) 0x82, (byte) 0x04, (byte) 0xbc, (byte) 0x02, (byte) 0x01, (byte) 0x00, + (byte) 0x30, (byte) 0x0d, (byte) 0x06, (byte) 0x09, (byte) 0x2a, (byte) 0x86, (byte) 0x48, + (byte) 0x86, (byte) 0xf7, (byte) 0x0d, (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x05, + (byte) 0x00, (byte) 0x04, (byte) 0x82, (byte) 0x04, (byte) 0xa6, (byte) 0x30, (byte) 0x82, + (byte) 0x04, (byte) 0xa2, (byte) 0x02, (byte) 0x01, (byte) 0x00, (byte) 0x02, (byte) 0x82, + (byte) 0x01, (byte) 0x01, (byte) 0x00, (byte) 0xc5, (byte) 0x28, (byte) 0x06, (byte) 0xb1, + (byte) 0x75, (byte) 0x6c, (byte) 0x84, (byte) 0x7a, (byte) 0x61, (byte) 0x6e, (byte) 0x49, + (byte) 0x66, (byte) 0xf8, (byte) 0x60, (byte) 0x4f, (byte) 0xec, (byte) 0x17, (byte) 0x8b, + (byte) 0x34, (byte) 0xfc, (byte) 0x3f, (byte) 0xce, (byte) 0x70, (byte) 0x6a, (byte) 0x02, + (byte) 0xf2, (byte) 0xf3, (byte) 0x6b, (byte) 0xb4, (byte) 0x78, (byte) 0xac, (byte) 0x8c, + (byte) 0x7e, (byte) 0xc5, (byte) 0xf2, (byte) 0xa8, (byte) 0xea, (byte) 0xc1, (byte) 0xe5, + (byte) 0xd3, (byte) 0xa8, (byte) 0xa9, (byte) 0x4b, (byte) 0x4b, (byte) 0x5a, (byte) 0x49, + (byte) 0xc2, (byte) 0xe7, (byte) 0x85, (byte) 0xdf, (byte) 0x56, (byte) 0xa5, (byte) 0x34, + (byte) 0xb2, (byte) 0xb6, (byte) 0xfd, (byte) 0xf2, (byte) 0xbc, (byte) 0xf1, (byte) 0xca, + (byte) 0x34, (byte) 0xba, (byte) 0x60, (byte) 0x50, (byte) 0x8d, (byte) 0x0b, (byte) 0x61, + (byte) 0xca, (byte) 0xd2, (byte) 0x76, (byte) 0x7d, (byte) 0xe4, (byte) 0xff, (byte) 0xdf, + (byte) 0x39, (byte) 0x10, (byte) 0x68, (byte) 0x9c, (byte) 0x45, (byte) 0x79, (byte) 0x8c, + (byte) 0x80, (byte) 0x0b, (byte) 0x58, (byte) 0xe4, (byte) 0x30, (byte) 0x9b, (byte) 0x74, + (byte) 0xc5, (byte) 0x09, (byte) 0x5e, (byte) 0x16, (byte) 0xa1, (byte) 0x63, (byte) 0x7f, + (byte) 0x03, (byte) 0xe9, (byte) 0xb0, (byte) 0x87, (byte) 0xf9, (byte) 0x81, (byte) 0x69, + (byte) 0x35, (byte) 0xca, (byte) 0x86, (byte) 0xe6, (byte) 0xa2, (byte) 0x1d, (byte) 0x3f, + (byte) 0xb8, (byte) 0x66, (byte) 0x39, (byte) 0x35, (byte) 0xf0, (byte) 0xef, (byte) 0xe3, + (byte) 0xde, (byte) 0x11, (byte) 0xa9, (byte) 0x9d, (byte) 0x54, (byte) 0x6f, (byte) 0xa8, + (byte) 0x04, (byte) 0x67, (byte) 0x75, (byte) 0x83, (byte) 0x67, (byte) 0xfb, (byte) 0xc2, + (byte) 0x71, (byte) 0x25, (byte) 0x43, (byte) 0xbe, (byte) 0x9c, (byte) 0x8b, (byte) 0x3e, + (byte) 0x94, (byte) 0x5e, (byte) 0xc1, (byte) 0x18, (byte) 0x83, (byte) 0x48, (byte) 0x9f, + (byte) 0x4d, (byte) 0x09, (byte) 0x1c, (byte) 0x0c, (byte) 0x61, (byte) 0xc5, (byte) 0x50, + (byte) 0x47, (byte) 0x34, (byte) 0x49, (byte) 0x17, (byte) 0x51, (byte) 0x16, (byte) 0xbc, + (byte) 0x09, (byte) 0x9b, (byte) 0x14, (byte) 0xc9, (byte) 0x44, (byte) 0x68, (byte) 0x58, + (byte) 0x19, (byte) 0xac, (byte) 0xf9, (byte) 0xd5, (byte) 0xa8, (byte) 0x52, (byte) 0x1f, + (byte) 0xb2, (byte) 0xcc, (byte) 0x9a, (byte) 0x22, (byte) 0xfe, (byte) 0xa7, (byte) 0x76, + (byte) 0x12, (byte) 0xe6, (byte) 0xfa, (byte) 0x3b, (byte) 0xc8, (byte) 0xe5, (byte) 0x26, + (byte) 0x6f, (byte) 0x62, (byte) 0xd8, (byte) 0xa4, (byte) 0x20, (byte) 0x0a, (byte) 0x6b, + (byte) 0x82, (byte) 0x6e, (byte) 0x43, (byte) 0x34, (byte) 0x34, (byte) 0x00, (byte) 0x59, + (byte) 0xbb, (byte) 0x3e, (byte) 0x54, (byte) 0xc9, (byte) 0x35, (byte) 0x77, (byte) 0x14, + (byte) 0xfd, (byte) 0x8b, (byte) 0xbd, (byte) 0x4e, (byte) 0xf0, (byte) 0x82, (byte) 0x6c, + (byte) 0xd1, (byte) 0x3d, (byte) 0xc0, (byte) 0x65, (byte) 0x98, (byte) 0xe4, (byte) 0x7e, + (byte) 0x4b, (byte) 0x69, (byte) 0xe0, (byte) 0x06, (byte) 0x92, (byte) 0x69, (byte) 0xb0, + (byte) 0x77, (byte) 0x90, (byte) 0x6b, (byte) 0xaa, (byte) 0x48, (byte) 0x2b, (byte) 0xd5, + (byte) 0x27, (byte) 0x95, (byte) 0xc2, (byte) 0xa6, (byte) 0x84, (byte) 0x45, (byte) 0xe2, + (byte) 0x84, (byte) 0x18, (byte) 0x0f, (byte) 0xfe, (byte) 0xc5, (byte) 0xf9, (byte) 0xab, + (byte) 0xbd, (byte) 0x28, (byte) 0x1d, (byte) 0x33, (byte) 0xcf, (byte) 0xb3, (byte) 0xb3, + (byte) 0x02, (byte) 0x03, (byte) 0x01, (byte) 0x00, (byte) 0x01, (byte) 0x02, (byte) 0x82, + (byte) 0x01, (byte) 0x00, (byte) 0x35, (byte) 0x96, (byte) 0x54, (byte) 0x83, (byte) 0x65, + (byte) 0x6c, (byte) 0x32, (byte) 0x71, (byte) 0xe5, (byte) 0x0b, (byte) 0x89, (byte) 0xed, + (byte) 0xef, (byte) 0xf2, (byte) 0x95, (byte) 0xa6, (byte) 0x91, (byte) 0x1b, (byte) 0xa8, + (byte) 0x32, (byte) 0x2b, (byte) 0xd1, (byte) 0x9b, (byte) 0xa2, (byte) 0x64, (byte) 0xdc, + (byte) 0xce, (byte) 0x26, (byte) 0xe7, (byte) 0x2d, (byte) 0xa9, (byte) 0x90, (byte) 0xa2, + (byte) 0x60, (byte) 0x81, (byte) 0x3d, (byte) 0x42, (byte) 0x59, (byte) 0xa3, (byte) 0x73, + (byte) 0x2d, (byte) 0x33, (byte) 0x9e, (byte) 0xa0, (byte) 0x83, (byte) 0x90, (byte) 0xea, + (byte) 0xe5, (byte) 0xec, (byte) 0xf0, (byte) 0x30, (byte) 0x67, (byte) 0xc4, (byte) 0xf4, + (byte) 0x12, (byte) 0x62, (byte) 0xe1, (byte) 0xd8, (byte) 0x53, (byte) 0x4b, (byte) 0xe7, + (byte) 0x9b, (byte) 0x04, (byte) 0xd4, (byte) 0xc0, (byte) 0x11, (byte) 0x68, (byte) 0xea, + (byte) 0x2c, (byte) 0xdc, (byte) 0x42, (byte) 0x09, (byte) 0xbd, (byte) 0x36, (byte) 0x5a, + (byte) 0x17, (byte) 0x48, (byte) 0xa7, (byte) 0xb9, (byte) 0x06, (byte) 0x79, (byte) 0x96, + (byte) 0xcf, (byte) 0xfe, (byte) 0xc0, (byte) 0x3f, (byte) 0x29, (byte) 0xf1, (byte) 0xca, + (byte) 0x20, (byte) 0x6a, (byte) 0xaf, (byte) 0x71, (byte) 0xfc, (byte) 0x4e, (byte) 0x28, + (byte) 0xad, (byte) 0x1a, (byte) 0xeb, (byte) 0x4a, (byte) 0x78, (byte) 0xcf, (byte) 0x34, + (byte) 0xec, (byte) 0xb0, (byte) 0x4f, (byte) 0xfd, (byte) 0x9e, (byte) 0x3f, (byte) 0x94, + (byte) 0x8a, (byte) 0x4c, (byte) 0x60, (byte) 0x89, (byte) 0xf5, (byte) 0x5a, (byte) 0x15, + (byte) 0x20, (byte) 0xed, (byte) 0xde, (byte) 0x32, (byte) 0x76, (byte) 0x0a, (byte) 0xcf, + (byte) 0xef, (byte) 0xa2, (byte) 0xf4, (byte) 0x2d, (byte) 0x13, (byte) 0xd9, (byte) 0xea, + (byte) 0x74, (byte) 0x89, (byte) 0xe5, (byte) 0x17, (byte) 0xae, (byte) 0xbf, (byte) 0x1d, + (byte) 0xbe, (byte) 0x0a, (byte) 0xc4, (byte) 0x4b, (byte) 0xf7, (byte) 0xbb, (byte) 0xc9, + (byte) 0x33, (byte) 0xd7, (byte) 0x5b, (byte) 0xa3, (byte) 0x45, (byte) 0xf4, (byte) 0xbe, + (byte) 0x02, (byte) 0xe6, (byte) 0x77, (byte) 0xd7, (byte) 0xfa, (byte) 0xa5, (byte) 0xda, + (byte) 0x13, (byte) 0x68, (byte) 0x94, (byte) 0x9f, (byte) 0x3e, (byte) 0xff, (byte) 0x15, + (byte) 0xf4, (byte) 0xd6, (byte) 0xa8, (byte) 0x28, (byte) 0xe1, (byte) 0x3f, (byte) 0x4e, + (byte) 0xa0, (byte) 0xce, (byte) 0x38, (byte) 0xa5, (byte) 0xb5, (byte) 0x17, (byte) 0x65, + (byte) 0x14, (byte) 0x06, (byte) 0x6c, (byte) 0xca, (byte) 0xb5, (byte) 0x8f, (byte) 0x70, + (byte) 0x98, (byte) 0x4d, (byte) 0x2a, (byte) 0xda, (byte) 0xeb, (byte) 0xe9, (byte) 0x07, + (byte) 0xb8, (byte) 0x09, (byte) 0xe7, (byte) 0x29, (byte) 0x31, (byte) 0x17, (byte) 0xf6, + (byte) 0x61, (byte) 0x96, (byte) 0xbf, (byte) 0x98, (byte) 0x76, (byte) 0x0d, (byte) 0x93, + (byte) 0xe1, (byte) 0xf8, (byte) 0xc7, (byte) 0xd1, (byte) 0xc4, (byte) 0xd8, (byte) 0x3a, + (byte) 0x33, (byte) 0x66, (byte) 0x4e, (byte) 0x84, (byte) 0xbd, (byte) 0x35, (byte) 0x29, + (byte) 0x51, (byte) 0x32, (byte) 0x34, (byte) 0x02, (byte) 0xcc, (byte) 0x16, (byte) 0xc6, + (byte) 0xce, (byte) 0xfa, (byte) 0x4f, (byte) 0x11, (byte) 0x9f, (byte) 0x61, (byte) 0x19, + (byte) 0xf6, (byte) 0xb6, (byte) 0xc1, (byte) 0xa4, (byte) 0xef, (byte) 0x83, (byte) 0x17, + (byte) 0xf1, (byte) 0x1e, (byte) 0xe6, (byte) 0x08, (byte) 0x76, (byte) 0x7a, (byte) 0xf0, + (byte) 0xf7, (byte) 0xa2, (byte) 0x9d, (byte) 0xa3, (byte) 0xa5, (byte) 0x69, (byte) 0x02, + (byte) 0x81, (byte) 0x81, (byte) 0x00, (byte) 0xee, (byte) 0xb0, (byte) 0x63, (byte) 0x52, + (byte) 0x47, (byte) 0x7e, (byte) 0x94, (byte) 0x3b, (byte) 0xe5, (byte) 0x0c, (byte) 0x5c, + (byte) 0x0c, (byte) 0x5f, (byte) 0x9f, (byte) 0xec, (byte) 0xb8, (byte) 0xe6, (byte) 0x81, + (byte) 0x32, (byte) 0x7b, (byte) 0x2d, (byte) 0xf9, (byte) 0x2c, (byte) 0xa5, (byte) 0x30, + (byte) 0x86, (byte) 0x2b, (byte) 0xd0, (byte) 0x6f, (byte) 0x64, (byte) 0xfd, (byte) 0xb5, + (byte) 0xb7, (byte) 0x32, (byte) 0xe4, (byte) 0x02, (byte) 0x2f, (byte) 0x16, (byte) 0x94, + (byte) 0x95, (byte) 0xae, (byte) 0x7b, (byte) 0x57, (byte) 0xee, (byte) 0x4b, (byte) 0xf0, + (byte) 0xde, (byte) 0x9d, (byte) 0x54, (byte) 0x29, (byte) 0x99, (byte) 0xcc, (byte) 0xe0, + (byte) 0xf6, (byte) 0xb5, (byte) 0x17, (byte) 0x03, (byte) 0xfe, (byte) 0xfc, (byte) 0x56, + (byte) 0x91, (byte) 0x43, (byte) 0x22, (byte) 0xce, (byte) 0x0f, (byte) 0xfa, (byte) 0x08, + (byte) 0x88, (byte) 0x5e, (byte) 0xb6, (byte) 0x73, (byte) 0xaa, (byte) 0x82, (byte) 0xe7, + (byte) 0x4c, (byte) 0x2a, (byte) 0xaf, (byte) 0x80, (byte) 0xc6, (byte) 0x83, (byte) 0xab, + (byte) 0x2a, (byte) 0xdd, (byte) 0xd7, (byte) 0xc1, (byte) 0x15, (byte) 0xdb, (byte) 0x94, + (byte) 0x98, (byte) 0x0a, (byte) 0x97, (byte) 0x00, (byte) 0x26, (byte) 0x5b, (byte) 0x62, + (byte) 0x0b, (byte) 0x27, (byte) 0xc3, (byte) 0x64, (byte) 0x38, (byte) 0x98, (byte) 0xd7, + (byte) 0x26, (byte) 0xcf, (byte) 0x73, (byte) 0x98, (byte) 0xe4, (byte) 0x59, (byte) 0x0a, + (byte) 0xb1, (byte) 0x06, (byte) 0x1f, (byte) 0x80, (byte) 0x3c, (byte) 0x19, (byte) 0x20, + (byte) 0x1b, (byte) 0xc3, (byte) 0x47, (byte) 0xaf, (byte) 0x2b, (byte) 0x12, (byte) 0xdf, + (byte) 0xef, (byte) 0x1d, (byte) 0x4d, (byte) 0xfc, (byte) 0xbd, (byte) 0x02, (byte) 0x81, + (byte) 0x81, (byte) 0x00, (byte) 0xd3, (byte) 0x74, (byte) 0x85, (byte) 0xf6, (byte) 0xad, + (byte) 0xdf, (byte) 0x84, (byte) 0xf4, (byte) 0xde, (byte) 0x97, (byte) 0x19, (byte) 0x30, + (byte) 0xa8, (byte) 0x4a, (byte) 0xf6, (byte) 0x7f, (byte) 0x80, (byte) 0x55, (byte) 0x49, + (byte) 0xad, (byte) 0x55, (byte) 0x2c, (byte) 0x87, (byte) 0x5f, (byte) 0x29, (byte) 0xda, + (byte) 0x7a, (byte) 0x81, (byte) 0xd6, (byte) 0xe5, (byte) 0xd8, (byte) 0x8e, (byte) 0x9f, + (byte) 0xbd, (byte) 0x35, (byte) 0xfe, (byte) 0x82, (byte) 0x0b, (byte) 0x5c, (byte) 0x28, + (byte) 0x95, (byte) 0x44, (byte) 0xab, (byte) 0x8c, (byte) 0x9e, (byte) 0xa1, (byte) 0xf2, + (byte) 0x5f, (byte) 0x2a, (byte) 0x6a, (byte) 0x96, (byte) 0x35, (byte) 0xbc, (byte) 0x09, + (byte) 0x4a, (byte) 0xb1, (byte) 0x19, (byte) 0x2f, (byte) 0xc1, (byte) 0x00, (byte) 0xba, + (byte) 0x3f, (byte) 0x8b, (byte) 0x9e, (byte) 0x2b, (byte) 0xbd, (byte) 0x0a, (byte) 0x0f, + (byte) 0x2d, (byte) 0x75, (byte) 0x09, (byte) 0xb6, (byte) 0xea, (byte) 0x98, (byte) 0xb1, + (byte) 0xff, (byte) 0xd8, (byte) 0x21, (byte) 0x13, (byte) 0x5d, (byte) 0xee, (byte) 0x5b, + (byte) 0xf2, (byte) 0xad, (byte) 0x46, (byte) 0x81, (byte) 0x9d, (byte) 0x18, (byte) 0x2b, + (byte) 0x9e, (byte) 0x77, (byte) 0x78, (byte) 0x27, (byte) 0xf5, (byte) 0x3a, (byte) 0x5a, + (byte) 0xb5, (byte) 0x9b, (byte) 0x02, (byte) 0x66, (byte) 0x1b, (byte) 0xb8, (byte) 0x51, + (byte) 0x9a, (byte) 0x07, (byte) 0xb7, (byte) 0x3f, (byte) 0x41, (byte) 0x8b, (byte) 0xfe, + (byte) 0x1e, (byte) 0x85, (byte) 0xc7, (byte) 0xfe, (byte) 0x01, (byte) 0x7a, (byte) 0x7e, + (byte) 0x2e, (byte) 0xb6, (byte) 0x3b, (byte) 0x64, (byte) 0x6e, (byte) 0xdc, (byte) 0x9d, + (byte) 0x7a, (byte) 0x48, (byte) 0xd1, (byte) 0x2f, (byte) 0x02, (byte) 0x81, (byte) 0x80, + (byte) 0x36, (byte) 0x6a, (byte) 0x76, (byte) 0x2a, (byte) 0x42, (byte) 0xec, (byte) 0x63, + (byte) 0xa5, (byte) 0x08, (byte) 0x01, (byte) 0xfa, (byte) 0x56, (byte) 0x43, (byte) 0xd2, + (byte) 0xb4, (byte) 0xe8, (byte) 0x2e, (byte) 0x7c, (byte) 0xd3, (byte) 0xe2, (byte) 0x6b, + (byte) 0x47, (byte) 0xbc, (byte) 0x5a, (byte) 0xe8, (byte) 0xa6, (byte) 0x1e, (byte) 0x05, + (byte) 0x05, (byte) 0xf0, (byte) 0x53, (byte) 0x3b, (byte) 0x03, (byte) 0x4a, (byte) 0x11, + (byte) 0xdb, (byte) 0x41, (byte) 0x9a, (byte) 0xf7, (byte) 0x42, (byte) 0xec, (byte) 0xa5, + (byte) 0x68, (byte) 0x15, (byte) 0x86, (byte) 0xb0, (byte) 0xa2, (byte) 0x3f, (byte) 0xe1, + (byte) 0xf9, (byte) 0x1d, (byte) 0xfc, (byte) 0x2c, (byte) 0x69, (byte) 0x72, (byte) 0x3d, + (byte) 0x8e, (byte) 0x06, (byte) 0xaa, (byte) 0xc6, (byte) 0x9d, (byte) 0x95, (byte) 0x5d, + (byte) 0xb0, (byte) 0xf6, (byte) 0xc9, (byte) 0x7c, (byte) 0xfa, (byte) 0x82, (byte) 0x05, + (byte) 0x3c, (byte) 0x77, (byte) 0x6a, (byte) 0x22, (byte) 0x8b, (byte) 0x25, (byte) 0xcc, + (byte) 0x1f, (byte) 0x22, (byte) 0xa2, (byte) 0xcf, (byte) 0xfa, (byte) 0x14, (byte) 0xdb, + (byte) 0x64, (byte) 0x44, (byte) 0xb4, (byte) 0x6b, (byte) 0xbb, (byte) 0x01, (byte) 0xe7, + (byte) 0x0c, (byte) 0xfc, (byte) 0xb1, (byte) 0xa6, (byte) 0xb7, (byte) 0x7e, (byte) 0x58, + (byte) 0x38, (byte) 0x58, (byte) 0x02, (byte) 0xd8, (byte) 0x42, (byte) 0x1b, (byte) 0xd7, + (byte) 0x71, (byte) 0xca, (byte) 0xd5, (byte) 0x55, (byte) 0xef, (byte) 0xa7, (byte) 0xc2, + (byte) 0xb4, (byte) 0xbc, (byte) 0x7e, (byte) 0xc9, (byte) 0xe8, (byte) 0x2a, (byte) 0x6c, + (byte) 0x04, (byte) 0x4e, (byte) 0x60, (byte) 0x9e, (byte) 0x36, (byte) 0xe8, (byte) 0x4a, + (byte) 0x68, (byte) 0x4d, (byte) 0x02, (byte) 0x81, (byte) 0x80, (byte) 0x06, (byte) 0x73, + (byte) 0x24, (byte) 0x6e, (byte) 0xec, (byte) 0xc8, (byte) 0xc7, (byte) 0x96, (byte) 0x6c, + (byte) 0x7f, (byte) 0xb1, (byte) 0x5e, (byte) 0x01, (byte) 0x94, (byte) 0x1f, (byte) 0xc6, + (byte) 0xad, (byte) 0xd4, (byte) 0x6c, (byte) 0x25, (byte) 0xe4, (byte) 0x56, (byte) 0x32, + (byte) 0x5e, (byte) 0xdd, (byte) 0xb8, (byte) 0xf3, (byte) 0x49, (byte) 0xa8, (byte) 0x93, + (byte) 0x64, (byte) 0x32, (byte) 0x9d, (byte) 0x7e, (byte) 0xb8, (byte) 0xf9, (byte) 0xe5, + (byte) 0x5f, (byte) 0x91, (byte) 0x55, (byte) 0x0f, (byte) 0x90, (byte) 0x83, (byte) 0xa7, + (byte) 0x0b, (byte) 0x63, (byte) 0xa7, (byte) 0x2f, (byte) 0xed, (byte) 0xec, (byte) 0x48, + (byte) 0x5e, (byte) 0xa5, (byte) 0x38, (byte) 0xa7, (byte) 0x55, (byte) 0x95, (byte) 0x8e, + (byte) 0x16, (byte) 0x55, (byte) 0xfe, (byte) 0x58, (byte) 0x57, (byte) 0xda, (byte) 0xe0, + (byte) 0x3c, (byte) 0xa8, (byte) 0xe4, (byte) 0xe3, (byte) 0x9f, (byte) 0x11, (byte) 0x47, + (byte) 0xca, (byte) 0x0a, (byte) 0x14, (byte) 0x4b, (byte) 0xd8, (byte) 0x7c, (byte) 0xd1, + (byte) 0xc9, (byte) 0x68, (byte) 0xae, (byte) 0xd7, (byte) 0x4d, (byte) 0x1f, (byte) 0xbc, + (byte) 0x6e, (byte) 0x5d, (byte) 0x41, (byte) 0x5f, (byte) 0x59, (byte) 0x07, (byte) 0x8a, + (byte) 0x38, (byte) 0x79, (byte) 0xaa, (byte) 0x30, (byte) 0xa5, (byte) 0xe4, (byte) 0xc1, + (byte) 0xd6, (byte) 0x90, (byte) 0x9d, (byte) 0xb4, (byte) 0x94, (byte) 0x0d, (byte) 0xab, + (byte) 0xd9, (byte) 0x44, (byte) 0xfa, (byte) 0xe0, (byte) 0x55, (byte) 0x76, (byte) 0x4f, + (byte) 0x32, (byte) 0x1e, (byte) 0x59, (byte) 0x60, (byte) 0xf5, (byte) 0x60, (byte) 0x04, + (byte) 0x65, (byte) 0x39, (byte) 0x47, (byte) 0x78, (byte) 0x66, (byte) 0x66, (byte) 0x33, + (byte) 0x02, (byte) 0x81, (byte) 0x80, (byte) 0x37, (byte) 0x90, (byte) 0x1c, (byte) 0x72, + (byte) 0x46, (byte) 0xc4, (byte) 0xda, (byte) 0x2c, (byte) 0x50, (byte) 0xb8, (byte) 0x4f, + (byte) 0xdc, (byte) 0x82, (byte) 0x98, (byte) 0xbc, (byte) 0xec, (byte) 0x1d, (byte) 0x84, + (byte) 0xc1, (byte) 0x33, (byte) 0xb7, (byte) 0x60, (byte) 0x1e, (byte) 0x58, (byte) 0x81, + (byte) 0x01, (byte) 0x24, (byte) 0x4c, (byte) 0x66, (byte) 0x17, (byte) 0xbc, (byte) 0xc3, + (byte) 0x83, (byte) 0x0b, (byte) 0x10, (byte) 0x38, (byte) 0x3c, (byte) 0x3c, (byte) 0xb4, + (byte) 0x36, (byte) 0x0e, (byte) 0x1b, (byte) 0xb5, (byte) 0x93, (byte) 0xd7, (byte) 0x47, + (byte) 0x14, (byte) 0x48, (byte) 0xf1, (byte) 0xf9, (byte) 0x53, (byte) 0xb5, (byte) 0xe1, + (byte) 0xe3, (byte) 0x0b, (byte) 0x51, (byte) 0x02, (byte) 0x14, (byte) 0x24, (byte) 0x0c, + (byte) 0x37, (byte) 0xf5, (byte) 0x78, (byte) 0xac, (byte) 0x00, (byte) 0x9f, (byte) 0xb2, + (byte) 0xfb, (byte) 0x32, (byte) 0x6c, (byte) 0xef, (byte) 0x2d, (byte) 0xa1, (byte) 0x7c, + (byte) 0xaf, (byte) 0xbb, (byte) 0x53, (byte) 0x9e, (byte) 0x7a, (byte) 0xc2, (byte) 0x5f, + (byte) 0x37, (byte) 0x74, (byte) 0xe9, (byte) 0x9b, (byte) 0x2b, (byte) 0xdb, (byte) 0x48, + (byte) 0xa0, (byte) 0x62, (byte) 0xcb, (byte) 0xee, (byte) 0x80, (byte) 0x07, (byte) 0xdc, + (byte) 0x0c, (byte) 0xc5, (byte) 0xe6, (byte) 0xc5, (byte) 0xbe, (byte) 0xd8, (byte) 0x82, + (byte) 0xd1, (byte) 0xd8, (byte) 0xd0, (byte) 0xd5, (byte) 0x8c, (byte) 0x55, (byte) 0xd4, + (byte) 0xfa, (byte) 0x50, (byte) 0x05, (byte) 0x7a, (byte) 0x02, (byte) 0x6d, (byte) 0xda, + (byte) 0x56, (byte) 0xec, (byte) 0xca, (byte) 0xf4, (byte) 0x27, (byte) 0xf0, (byte) 0x8f, + (byte) 0x8f, (byte) 0xc5, (byte) 0x3c, (byte) 0x28, (byte) 0x30 + }; + + private static final byte[] ec_key_pkcs8 = { + (byte) 0x30, (byte) 0x81, (byte) 0x87, (byte) 0x02, (byte) 0x01, (byte) 0x00, + (byte) 0x30, (byte) 0x13, (byte) 0x06, (byte) 0x07, (byte) 0x2a, (byte) 0x86, + (byte) 0x48, (byte) 0xce, (byte) 0x3d, (byte) 0x02, (byte) 0x01, (byte) 0x06, + (byte) 0x08, (byte) 0x2a, (byte) 0x86, (byte) 0x48, (byte) 0xce, (byte) 0x3d, + (byte) 0x03, (byte) 0x01, (byte) 0x07, (byte) 0x04, (byte) 0x6d, (byte) 0x30, + (byte) 0x6b, (byte) 0x02, (byte) 0x01, (byte) 0x01, (byte) 0x04, (byte) 0x20, + (byte) 0xfc, (byte) 0x06, (byte) 0xed, (byte) 0x57, (byte) 0xe9, (byte) 0x03, + (byte) 0xd9, (byte) 0xfe, (byte) 0x3f, (byte) 0x32, (byte) 0x34, (byte) 0x0f, + (byte) 0xd3, (byte) 0x69, (byte) 0x0a, (byte) 0x4d, (byte) 0xe8, (byte) 0x0b, + (byte) 0x08, (byte) 0xcd, (byte) 0x17, (byte) 0x1c, (byte) 0x5f, (byte) 0xe5, + (byte) 0xd3, (byte) 0xaa, (byte) 0x34, (byte) 0xd2, (byte) 0x09, (byte) 0x0b, + (byte) 0xb2, (byte) 0x1a, (byte) 0xa1, (byte) 0x44, (byte) 0x03, (byte) 0x42, + (byte) 0x00, (byte) 0x04, (byte) 0xf7, (byte) 0x84, (byte) 0xf4, (byte) 0xae, + (byte) 0xf2, (byte) 0x80, (byte) 0xca, (byte) 0xe0, (byte) 0xe6, (byte) 0x38, + (byte) 0x63, (byte) 0x83, (byte) 0x39, (byte) 0x65, (byte) 0xd7, (byte) 0x4c, + (byte) 0x3d, (byte) 0x75, (byte) 0x13, (byte) 0x7a, (byte) 0x3b, (byte) 0xcd, + (byte) 0x1a, (byte) 0xca, (byte) 0xa1, (byte) 0x4b, (byte) 0x1d, (byte) 0xa1, + (byte) 0x6a, (byte) 0xa2, (byte) 0x13, (byte) 0xf5, (byte) 0xf5, (byte) 0xee, + (byte) 0x90, (byte) 0x92, (byte) 0xeb, (byte) 0x8f, (byte) 0x67, (byte) 0xb1, + (byte) 0xd0, (byte) 0xa2, (byte) 0x6e, (byte) 0x02, (byte) 0x1a, (byte) 0x83, + (byte) 0x12, (byte) 0x5b, (byte) 0x68, (byte) 0x8e, (byte) 0x50, (byte) 0x65, + (byte) 0x35, (byte) 0x66, (byte) 0xa1, (byte) 0xee, (byte) 0x86, (byte) 0x62, + (byte) 0x22, (byte) 0xe6, (byte) 0x00, (byte) 0x61, (byte) 0x54, (byte) 0x86 + }; + public static byte[] CSR_CHALLENGE = {0x56, 0x78, 0x65, 0x23, (byte) 0xFE, 0x32}; + + private CardSimulator simulator; + private KMEncoder encoder; + private KMDecoder decoder; + private KMSEProvider cryptoProvider; + private KMAsn1Parser asn1Parser; + + public KMFunctionalTest() { + cryptoProvider = new KMJCardSimulator(); + simulator = new CardSimulator(); + encoder = new KMEncoder(); + decoder = new KMDecoder(); + } + + private void init() { + // Create simulator + AID appletAID = AIDUtil.create("A000000062"); + simulator.installApplet(appletAID, KMJCardSimApplet.class); + // Select applet + simulator.selectApplet(appletAID); + // provision attest key + KMProvision.provisionCmd(simulator, cryptoProvider, encoder, decoder); + } + + private void cleanUp() { + AID appletAID = AIDUtil.create("A000000062"); + // Delete i.e. uninstall applet + simulator.deleteApplet(appletAID); + } + + private void resetAndSelect() { + simulator.reset(); + AID appletAID = AIDUtil.create("A000000062"); + // Select applet + simulator.selectApplet(appletAID); + } + + //------------------------------------------------------------------------------------------------ + // Import key tests + //------------------------------------------------------------------------------------------------ + @Test + public void testAesImportKeySuccess() { + init(); + byte[] aesKeySecret = new byte[]{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + short arrPtr = KMArray.instance((short) 5); + short boolTag = KMBoolTag.instance(KMType.NO_AUTH_REQUIRED); + short keySize = KMIntegerTag + .instance(KMType.UINT_TAG, KMType.KEYSIZE, KMInteger.uint_16((short) 128)); + short byteBlob = KMByteBlob.instance((short) 1); + KMByteBlob.cast(byteBlob).add((short) 0, KMType.ECB); + short blockMode = KMEnumArrayTag.instance(KMType.BLOCK_MODE, byteBlob); + byteBlob = KMByteBlob.instance((short) 1); + KMByteBlob.cast(byteBlob).add((short) 0, KMType.PKCS7); + short paddingMode = KMEnumArrayTag.instance(KMType.PADDING, byteBlob); + KMArray.cast(arrPtr).add((short) 0, boolTag); + KMArray.cast(arrPtr).add((short) 1, keySize); + KMArray.cast(arrPtr).add((short) 2, blockMode); + KMArray.cast(arrPtr).add((short) 3, paddingMode); + KMArray.cast(arrPtr).add((short) 4, KMEnumTag.instance(KMType.ALGORITHM, KMType.AES)); + short keyParams = KMKeyParameters.instance(arrPtr); + short keyFormatPtr = KMEnum.instance(KMType.KEY_FORMAT, KMType.RAW); + short keyBlob = KMByteBlob.instance(aesKeySecret, (short) 0, (short) 16); + arrPtr = importKeyNoAttestCmd(keyParams, keyFormatPtr, keyBlob); + CommandAPDU apdu = KMTestUtils.encodeApdu(encoder, (byte) INS_IMPORT_KEY_CMD, arrPtr); + // print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(apdu); + short byteBlobExp = KMByteBlob.exp(); + short certArrayExp = KMArray.exp(byteBlobExp); + short ret = KMArray.instance((short) 4); + KMArray.cast(ret).add((short) 0, KMInteger.exp()); + KMArray.cast(ret).add((short) 1, KMByteBlob.exp()); + short inst = KMKeyCharacteristics.exp(); + KMArray.cast(ret).add((short) 2, inst); + KMArray.cast(ret).add((short) 3, certArrayExp); + byte[] respBuf = response.getBytes(); + short len = (short) respBuf.length; + ret = decoder.decode(ret, respBuf, (short) 0, len); + short error = KMInteger.cast(KMArray.cast(ret).get((short) 0)).getShort(); + short keyBlobLength = KMByteBlob.cast(KMArray.cast(ret).get((short) 1)).length(); + short keyCharacteristics = KMArray.cast(ret).get((short) 2); + short sbParams = KMKeyCharacteristics.cast(keyCharacteristics).getStrongboxEnforced(); + short swParams = KMKeyCharacteristics.cast(keyCharacteristics).getKeystoreEnforced(); + short teeParams = KMKeyCharacteristics.cast(keyCharacteristics).getTeeEnforced(); + Assert.assertEquals(0x9000, response.getSW()); + Assert.assertEquals(error, KMError.OK); + short tag = KMKeyParameters.findTag(KMType.BOOL_TAG, KMType.NO_AUTH_REQUIRED, sbParams); + Assert.assertEquals(KMBoolTag.cast(tag).getVal(), 0x01); + tag = KMKeyParameters.findTag(KMType.UINT_TAG, KMType.KEYSIZE, sbParams); + Assert.assertEquals(KMInteger.cast(KMIntegerTag.cast(tag).getValue()).getShort(), 128); + tag = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.PADDING, sbParams); + Assert.assertTrue(KMEnumArrayTag.cast(tag).contains(KMType.PKCS7)); + tag = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.BLOCK_MODE, sbParams); + Assert.assertTrue(KMEnumArrayTag.cast(tag).contains(KMType.ECB)); + tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ALGORITHM, sbParams); + Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.AES); + tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ORIGIN, sbParams); + Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.IMPORTED); + cleanUp(); + } + + @Test + public void testHmacImportKeySuccess() { + init(); + byte[] hmacKeySecret = new byte[]{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + short arrPtr = KMArray.instance((short) 5); + short boolTag = KMBoolTag.instance(KMType.NO_AUTH_REQUIRED); + short keySize = KMIntegerTag + .instance(KMType.UINT_TAG, KMType.KEYSIZE, KMInteger.uint_16((short) 128)); + short byteBlob = KMByteBlob.instance((short) 1); + KMByteBlob.cast(byteBlob).add((short) 0, KMType.SHA2_256); + short digest = KMEnumArrayTag.instance(KMType.DIGEST, byteBlob); + short minMacLength = KMIntegerTag + .instance(KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, KMInteger.uint_16((short) 256)); + KMArray.cast(arrPtr).add((short) 0, boolTag); + KMArray.cast(arrPtr).add((short) 1, keySize); + KMArray.cast(arrPtr).add((short) 2, digest); + KMArray.cast(arrPtr).add((short) 3, minMacLength); + KMArray.cast(arrPtr).add((short) 4, KMEnumTag.instance(KMType.ALGORITHM, KMType.HMAC)); + short keyParams = KMKeyParameters.instance(arrPtr); + short keyFormatPtr = KMEnum.instance(KMType.KEY_FORMAT, KMType.RAW); + short keyBlob = KMByteBlob.instance(hmacKeySecret, (short) 0, (short) 16); + arrPtr = importKeyNoAttestCmd(keyParams, keyFormatPtr, keyBlob); + CommandAPDU apdu = KMTestUtils.encodeApdu(encoder, (byte) INS_IMPORT_KEY_CMD, arrPtr); + // print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(apdu); + short byteBlobExp = KMByteBlob.exp(); + short certArrayExp = KMArray.exp(byteBlobExp); + short ret = KMArray.instance((short) 4); + KMArray.cast(ret).add((short) 0, KMInteger.exp()); + KMArray.cast(ret).add((short) 1, KMByteBlob.exp()); + short inst = KMKeyCharacteristics.exp(); + KMArray.cast(ret).add((short) 2, inst); + KMArray.cast(ret).add((short) 3, certArrayExp); + byte[] respBuf = response.getBytes(); + short len = (short) respBuf.length; + ret = decoder.decode(ret, respBuf, (short) 0, len); + short error = KMInteger.cast(KMArray.cast(ret).get((short) 0)).getShort(); + short keyBlobLength = KMByteBlob.cast(KMArray.cast(ret).get((short) 1)).length(); + short keyCharacteristics = KMArray.cast(ret).get((short) 2); + short sbParams = KMKeyCharacteristics.cast(keyCharacteristics).getStrongboxEnforced(); + short swParams = KMKeyCharacteristics.cast(keyCharacteristics).getKeystoreEnforced(); + short teeParams = KMKeyCharacteristics.cast(keyCharacteristics).getTeeEnforced(); + Assert.assertEquals(0x9000, response.getSW()); + Assert.assertEquals(error, KMError.OK); + short tag = KMKeyParameters.findTag(KMType.BOOL_TAG, KMType.NO_AUTH_REQUIRED, sbParams); + Assert.assertEquals(KMBoolTag.cast(tag).getVal(), 0x01); + tag = KMKeyParameters.findTag(KMType.UINT_TAG, KMType.KEYSIZE, sbParams); + Assert.assertEquals(KMInteger.cast(KMIntegerTag.cast(tag).getValue()).getShort(), 128); + tag = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.DIGEST, sbParams); + Assert.assertTrue(KMEnumArrayTag.cast(tag).contains(KMType.SHA2_256)); + tag = KMKeyParameters.findTag(KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, sbParams); + Assert.assertEquals(KMInteger.cast(KMIntegerTag.cast(tag).getValue()).getShort(), 256); + tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ALGORITHM, sbParams); + Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.HMAC); + tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ORIGIN, sbParams); + Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.IMPORTED); + cleanUp(); + } + + @Test + public void testRsaImportKeySuccess() { + init(); + // print(commandAPDU.getBytes()); + ResponseAPDU response = importRsaKey(false); + short ret = parseImportKeyResponse(response); + short error = KMInteger.cast(KMArray.cast(ret).get((short) 0)).getShort(); + short keyBlobLength = KMByteBlob.cast(KMArray.cast(ret).get((short) 1)).length(); + short keyCharacteristics = KMArray.cast(ret).get((short) 2); + // Self-signed certificate + short certChain = KMArray.cast(ret).get((short) 3); + Assert.assertEquals(1, KMArray.cast(certChain).length()); + short byteBlob = KMArray.cast(certChain).get((short) 0); + Assert.assertTrue("Certificate length should be greater than 0", + (KMByteBlob.cast(byteBlob).length() > 0)); + short sbParams = KMKeyCharacteristics.cast(keyCharacteristics).getStrongboxEnforced(); + short swParams = KMKeyCharacteristics.cast(keyCharacteristics).getKeystoreEnforced(); + short teeParams = KMKeyCharacteristics.cast(keyCharacteristics).getTeeEnforced(); + Assert.assertEquals(0x9000, response.getSW()); + Assert.assertEquals(error, KMError.OK); + short tag = KMKeyParameters.findTag(KMType.BOOL_TAG, KMType.NO_AUTH_REQUIRED, sbParams); + Assert.assertEquals(KMBoolTag.cast(tag).getVal(), 0x01); + tag = KMKeyParameters.findTag(KMType.UINT_TAG, KMType.KEYSIZE, sbParams); + Assert.assertEquals(KMInteger.cast(KMIntegerTag.cast(tag).getValue()).getShort(), 2048); + tag = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.DIGEST, sbParams); + Assert.assertTrue(KMEnumArrayTag.cast(tag).contains(KMType.SHA2_256)); + tag = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.PADDING, sbParams); + Assert.assertTrue(KMEnumArrayTag.cast(tag).contains(KMType.RSA_PSS)); + tag = KMKeyParameters.findTag(KMType.ULONG_TAG, KMType.RSA_PUBLIC_EXPONENT, sbParams); + Assert.assertEquals(KMInteger.cast(KMIntegerTag.cast(tag).getValue()).getSignificantShort(), + 0x01); + Assert.assertEquals(KMInteger.cast(KMIntegerTag.cast(tag).getValue()).getShort(), 0x01); + tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ALGORITHM, sbParams); + Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.RSA); + tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ORIGIN, sbParams); + Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.IMPORTED); + cleanUp(); + } + + @Test + public void testEcImportKeySuccess() { + init(); + short arrPtr = KMArray.instance((short) 7); + short boolTag = KMBoolTag.instance(KMType.NO_AUTH_REQUIRED); + short keySize = KMIntegerTag + .instance(KMType.UINT_TAG, KMType.KEYSIZE, KMInteger.uint_16((short) 256)); + short byteBlob = KMByteBlob.instance((short) 1); + KMByteBlob.cast(byteBlob).add((short) 0, KMType.SHA2_256); + short digest = KMEnumArrayTag.instance(KMType.DIGEST, byteBlob); + short ecCurve = KMEnumTag.instance(KMType.ECCURVE, KMType.P_256); + KMArray.cast(arrPtr).add((short) 0, boolTag); + KMArray.cast(arrPtr).add((short) 1, keySize); + KMArray.cast(arrPtr).add((short) 2, digest); + KMArray.cast(arrPtr).add((short) 3, ecCurve); + KMArray.cast(arrPtr).add((short) 4, KMEnumTag.instance(KMType.ALGORITHM, KMType.EC)); + setDefaultValidity(arrPtr, (short) 5); + short keyParams = KMKeyParameters.instance(arrPtr); + short keyFormatPtr = KMEnum.instance(KMType.KEY_FORMAT, KMType.PKCS8); + short keyBlob = KMByteBlob.instance(ec_key_pkcs8, (short) 0, (short) ec_key_pkcs8.length); + arrPtr = importKeyNoAttestCmd(keyParams, keyFormatPtr, keyBlob); + CommandAPDU apdu = KMTestUtils.encodeApdu(encoder, (byte) INS_IMPORT_KEY_CMD, arrPtr); + ResponseAPDU response = simulator.transmitCommand(apdu); + short ret = parseImportKeyResponse(response); + short error = KMInteger.cast(KMArray.cast(ret).get((short) 0)).getShort(); + short keyBlobLength = KMByteBlob.cast(KMArray.cast(ret).get((short) 1)).length(); + short blobArr = extractKeyBlobArray(KMArray.cast(ret).get((short) 1)); + short keyCharacteristics = KMArray.cast(ret).get((short) 2); + // Self-signed certificate + short certChain = KMArray.cast(ret).get((short) 3); + Assert.assertEquals(1, KMArray.cast(certChain).length()); + byteBlob = KMArray.cast(certChain).get((short) 0); + Assert.assertTrue("Certificate length should be greater than 0", + (KMByteBlob.cast(byteBlob).length() > 0)); + short sbParams = KMKeyCharacteristics.cast(keyCharacteristics).getStrongboxEnforced(); + short swParams = KMKeyCharacteristics.cast(keyCharacteristics).getKeystoreEnforced(); + short teeParams = KMKeyCharacteristics.cast(keyCharacteristics).getTeeEnforced(); + Assert.assertEquals(0x9000, response.getSW()); + Assert.assertEquals(error, KMError.OK); + short tag = KMKeyParameters.findTag(KMType.BOOL_TAG, KMType.NO_AUTH_REQUIRED, sbParams); + Assert.assertEquals(KMBoolTag.cast(tag).getVal(), 0x01); + tag = KMKeyParameters.findTag(KMType.UINT_TAG, KMType.KEYSIZE, sbParams); + Assert.assertEquals(KMInteger.cast(KMIntegerTag.cast(tag).getValue()).getShort(), 256); + tag = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.DIGEST, sbParams); + Assert.assertTrue(KMEnumArrayTag.cast(tag).contains(KMType.SHA2_256)); + tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ECCURVE, sbParams); + Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.P_256); + tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ALGORITHM, sbParams); + Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.EC); + tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ORIGIN, sbParams); + Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.IMPORTED); + cleanUp(); + } + + //------------------------------------------------------------------------------------------------ + // Generate key tests + //------------------------------------------------------------------------------------------------ + @Test + public void testRsaGenerateKeySuccess() { + init(); + short ret = generateRsaKey(null, null); + short error = KMInteger.cast(KMArray.cast(ret).get((short) 0)).getShort(); + short keyBlobLength = KMByteBlob.cast(KMArray.cast(ret).get((short) 1)).length(); + short keyCharacteristics = KMArray.cast(ret).get((short) 2); + // Self-signed certificate + short certChain = KMArray.cast(ret).get((short) 3); + Assert.assertEquals(1, KMArray.cast(certChain).length()); + short byteBlob = KMArray.cast(certChain).get((short) 0); + Assert.assertTrue("Certificate length should be greater than 0", + (KMByteBlob.cast(byteBlob).length() > 0)); + short sbParams = KMKeyCharacteristics.cast(keyCharacteristics).getStrongboxEnforced(); + short swParams = KMKeyCharacteristics.cast(keyCharacteristics).getKeystoreEnforced(); + short teeParams = KMKeyCharacteristics.cast(keyCharacteristics).getTeeEnforced(); + Assert.assertEquals(error, KMError.OK); + short tag = KMKeyParameters.findTag(KMType.UINT_TAG, KMType.KEYSIZE, sbParams); + Assert.assertEquals(KMInteger.cast(KMIntegerTag.cast(tag).getValue()).getShort(), 2048); + tag = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.DIGEST, sbParams); + Assert.assertTrue(KMEnumArrayTag.cast(tag).contains(KMType.DIGEST_NONE)); + tag = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.PADDING, sbParams); + Assert.assertTrue(KMEnumArrayTag.cast(tag).contains(KMType.RSA_PKCS1_1_5_ENCRYPT)); + tag = KMKeyParameters.findTag(KMType.ULONG_TAG, KMType.RSA_PUBLIC_EXPONENT, sbParams); + Assert.assertEquals(KMInteger.cast(KMIntegerTag.cast(tag).getValue()).getSignificantShort(), + 0x01); + Assert.assertEquals(KMInteger.cast(KMIntegerTag.cast(tag).getValue()).getShort(), 0x01); + tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ALGORITHM, sbParams); + Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.RSA); + tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ORIGIN, sbParams); + Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.GENERATED); + cleanUp(); + } + + @Test + public void testEcGenerateKeySuccess() { + init(); + short ret = generateEcKey(null, null); + short error = KMInteger.cast(KMArray.cast(ret).get((short) 0)).getShort(); + short keyBlobLength = KMByteBlob.cast(KMArray.cast(ret).get((short) 1)).length(); + short keyCharacteristics = KMArray.cast(ret).get((short) 2); + // Self-signed certificate + short certChain = KMArray.cast(ret).get((short) 3); + Assert.assertEquals(1, KMArray.cast(certChain).length()); + short byteBlob = KMArray.cast(certChain).get((short) 0); + Assert.assertTrue("Certificate length should be greater than 0", + (KMByteBlob.cast(byteBlob).length() > 0)); + short sbParams = KMKeyCharacteristics.cast(keyCharacteristics).getStrongboxEnforced(); + short swParams = KMKeyCharacteristics.cast(keyCharacteristics).getKeystoreEnforced(); + short teeParams = KMKeyCharacteristics.cast(keyCharacteristics).getTeeEnforced(); + Assert.assertEquals(error, KMError.OK); + short tag = KMKeyParameters.findTag(KMType.UINT_TAG, KMType.KEYSIZE, sbParams); + Assert.assertEquals(KMInteger.cast(KMIntegerTag.cast(tag).getValue()).getShort(), 256); + tag = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.DIGEST, sbParams); + Assert.assertTrue(KMEnumArrayTag.cast(tag).contains(KMType.DIGEST_NONE)); + tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ALGORITHM, sbParams); + Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.EC); + tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ORIGIN, sbParams); + Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.GENERATED); + cleanUp(); + } + + @Test + public void testHmacGenerateKeySuccess() { + init(); + short ret = generateHmacKey(null, null); + short error = KMInteger.cast(KMArray.cast(ret).get((short) 0)).getShort(); + short keyBlobLength = KMByteBlob.cast(KMArray.cast(ret).get((short) 1)).length(); + short keyCharacteristics = KMArray.cast(ret).get((short) 2); + short sbParams = KMKeyCharacteristics.cast(keyCharacteristics).getStrongboxEnforced(); + short swParams = KMKeyCharacteristics.cast(keyCharacteristics).getKeystoreEnforced(); + short teeParams = KMKeyCharacteristics.cast(keyCharacteristics).getTeeEnforced(); + Assert.assertEquals(error, KMError.OK); + short tag = KMKeyParameters.findTag(KMType.UINT_TAG, KMType.KEYSIZE, sbParams); + Assert.assertEquals(KMInteger.cast(KMIntegerTag.cast(tag).getValue()).getShort(), 128); + tag = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.DIGEST, sbParams); + Assert.assertTrue(KMEnumArrayTag.cast(tag).contains(KMType.SHA2_256)); + tag = KMKeyParameters.findTag(KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, sbParams); + Assert.assertEquals(KMInteger.cast(KMIntegerTag.cast(tag).getValue()).getShort(), 160); + tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ALGORITHM, sbParams); + Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.HMAC); + tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ORIGIN, sbParams); + Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.GENERATED); + cleanUp(); + } + + @Test + public void testAesGenerateKeySuccess() { + init(); + short ret = generateAesDesKey(KMType.AES, (short) 256, null, null, false); + short error = KMInteger.cast(KMArray.cast(ret).get((short) 0)).getShort(); + short keyBlobLength = KMByteBlob.cast(KMArray.cast(ret).get((short) 1)).length(); + short keyCharacteristics = KMArray.cast(ret).get((short) 2); + short sbParams = KMKeyCharacteristics.cast(keyCharacteristics).getStrongboxEnforced(); + short swParams = KMKeyCharacteristics.cast(keyCharacteristics).getKeystoreEnforced(); + short teeParams = KMKeyCharacteristics.cast(keyCharacteristics).getTeeEnforced(); + Assert.assertEquals(error, KMError.OK); + short tag = KMKeyParameters.findTag(KMType.BOOL_TAG, KMType.NO_AUTH_REQUIRED, sbParams); + Assert.assertEquals(KMBoolTag.cast(tag).getVal(), 0x01); + tag = KMKeyParameters.findTag(KMType.UINT_TAG, KMType.KEYSIZE, sbParams); + Assert.assertEquals(KMInteger.cast(KMIntegerTag.cast(tag).getValue()).getShort(), 256); + tag = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.PADDING, sbParams); + Assert.assertTrue(KMEnumArrayTag.cast(tag).contains(KMType.PKCS7)); + tag = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.BLOCK_MODE, sbParams); + Assert.assertTrue(KMEnumArrayTag.cast(tag).contains(KMType.ECB)); + tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ALGORITHM, sbParams); + Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.AES); + tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ORIGIN, sbParams); + Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.GENERATED); + cleanUp(); + } + + //------------------------------------------------------------------------------------------------ + // ImportWrapped Key tests + //------------------------------------------------------------------------------------------------ + @Test + public void testImportWrappedKeySuccess() { + init(); + ResponseAPDU response = importWrappedKey(); + short byteBlobExp = KMByteBlob.exp(); + short certArrayExp = KMArray.exp(byteBlobExp); + short ret = KMArray.instance((short) 4); + KMArray.cast(ret).add((short) 0, KMInteger.exp()); + KMArray.cast(ret).add((short) 1, KMByteBlob.exp()); + short inst = KMKeyCharacteristics.exp(); + KMArray.cast(ret).add((short) 2, inst); + KMArray.cast(ret).add((short) 3, certArrayExp); + byte[] respBuf = response.getBytes(); + short len = (short) respBuf.length; + ret = decoder.decode(ret, respBuf, (short) 0, len); + short error = KMInteger.cast(KMArray.cast(ret).get((short) 0)).getShort(); + short keyBlobLength = KMByteBlob.cast(KMArray.cast(ret).get((short) 1)).length(); + short keyCharacteristics = KMArray.cast(ret).get((short) 2); + short sbParams = KMKeyCharacteristics.cast(keyCharacteristics).getStrongboxEnforced(); + short swParams = KMKeyCharacteristics.cast(keyCharacteristics).getKeystoreEnforced(); + Assert.assertEquals(0x9000, response.getSW()); + Assert.assertEquals(error, KMError.OK); + short tag = KMKeyParameters.findTag(KMType.BOOL_TAG, KMType.NO_AUTH_REQUIRED, sbParams); + Assert.assertEquals(KMBoolTag.cast(tag).getVal(), 0x01); + tag = KMKeyParameters.findTag(KMType.UINT_TAG, KMType.KEYSIZE, sbParams); + Assert.assertEquals(KMInteger.cast(KMIntegerTag.cast(tag).getValue()).getShort(), 128); + tag = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.PADDING, sbParams); + Assert.assertTrue(KMEnumArrayTag.cast(tag).contains(KMType.PKCS7)); + tag = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.BLOCK_MODE, sbParams); + Assert.assertTrue(KMEnumArrayTag.cast(tag).contains(KMType.ECB)); + tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ALGORITHM, sbParams); + Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.AES); + tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ORIGIN, sbParams); + Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.SECURELY_IMPORTED); + cleanUp(); + } + + //------------------------------------------------------------------------------------------------ + // Device Locked test + //------------------------------------------------------------------------------------------------ + @Test + public void testDeviceLocked() { + init(); + // generate aes key with unlocked_device_required + short aesKey = generateAesDesKey(KMType.AES, (short) 128, null, null, true); + short keyBlobPtr = KMArray.cast(aesKey).get((short) 1); + byte[] keyBlob = new byte[KMByteBlob.cast(keyBlobPtr).length()]; + Util.arrayCopyNonAtomic(KMByteBlob.cast(keyBlobPtr).getBuffer(), + KMByteBlob.cast(keyBlobPtr).getStartOff(), + keyBlob, (short) 0, (short) keyBlob.length); + // encrypt something + short inParams = getAesDesParams(KMType.AES, KMType.ECB, KMType.PKCS7, null); + byte[] plainData = "Hello World 123!".getBytes(); + short ret = processMessage(plainData, + KMByteBlob.instance(keyBlob, (short) 0, (short) keyBlob.length), + KMType.ENCRYPT, + KMKeyParameters.instance(inParams), + (short) 0, null, false, false + ); + keyBlobPtr = KMArray.cast(ret).get((short) 1); + byte[] cipherData = new byte[KMByteBlob.cast(keyBlobPtr).length()]; + Util.arrayCopyNonAtomic(KMByteBlob.cast(keyBlobPtr).getBuffer(), + KMByteBlob.cast(keyBlobPtr).getStartOff(), + cipherData, (short) 0, (short) cipherData.length); + // create verification token + short verToken = KMVerificationToken.instance(); + KMVerificationToken.cast(verToken).setTimestamp(KMInteger.uint_16((short) 1)); + verToken = signVerificationToken(verToken, KMConfigurations.TEE_MACHINE_TYPE); + // device locked request + deviceLock(verToken, KMError.OK); + inParams = getAesDesParams(KMType.AES, KMType.ECB, KMType.PKCS7, null); + short beginResp = begin(KMType.ENCRYPT, + KMByteBlob.instance(keyBlob, (short) 0, (short) keyBlob.length), + KMKeyParameters.instance(inParams), (short) 0, false); + Assert.assertEquals(KMError.DEVICE_LOCKED, + KMInteger.cast(KMArray.cast(beginResp).get((short) 0)).getShort()); + cleanUp(); + } + + //------------------------------------------------------------------------------------------------ + // MAX_USES_PER_BOOT use case tests. + //------------------------------------------------------------------------------------------------ + @Test + public void testRateLimitExceptsMaxOpsExceeded() { + init(); + short rsaKeyArr = generateRsaKey(null, null, KMInteger.uint_8((byte) 2)); + Assert.assertEquals(KMInteger.cast(KMArray.cast(rsaKeyArr).get((short) 0)).getShort(), + KMError.OK); + + // Cache keyblob + short keyBlobPtr = KMArray.cast(rsaKeyArr).get((short) 1); + byte[] keyBlob = new byte[KMByteBlob.cast(keyBlobPtr).length()]; + Util.arrayCopyNonAtomic(KMByteBlob.cast(keyBlobPtr).getBuffer(), + KMByteBlob.cast(keyBlobPtr).getStartOff(), + keyBlob, (short) 0, (short) keyBlob.length); + short inParams = getRsaParams(KMType.SHA2_256, KMType.RSA_PKCS1_1_5_SIGN); + inParams = KMKeyParameters.instance(inParams); + // Begin + begin(KMType.SIGN, keyBlobPtr, inParams, (short) 0, false); + + keyBlobPtr = KMByteBlob.instance((short) keyBlob.length); + Util.arrayCopyNonAtomic(keyBlob, (short) 0, + KMByteBlob.cast(keyBlobPtr).getBuffer(), + KMByteBlob.cast(keyBlobPtr).getStartOff(), + (short) keyBlob.length); + inParams = getRsaParams(KMType.SHA2_256, KMType.RSA_PKCS1_1_5_SIGN); + inParams = KMKeyParameters.instance(inParams); + begin(KMType.SIGN, keyBlobPtr, inParams, (short) 0, false); + + keyBlobPtr = KMByteBlob.instance((short) keyBlob.length); + Util.arrayCopyNonAtomic(keyBlob, (short) 0, + KMByteBlob.cast(keyBlobPtr).getBuffer(), + KMByteBlob.cast(keyBlobPtr).getStartOff(), + (short) keyBlob.length); + inParams = getRsaParams(KMType.SHA2_256, KMType.RSA_PKCS1_1_5_SIGN); + inParams = KMKeyParameters.instance(inParams); + short beginResp = begin(KMType.SIGN, keyBlobPtr, inParams, (short) 0, false); + Assert.assertEquals(KMError.KEY_MAX_OPS_EXCEEDED, + KMInteger.cast(KMArray.cast(beginResp).get((short) 0)).getShort()); + cleanUp(); + } + + @Test + public void testRateLimitExceptsTooManyOperations() { + init(); + byte[] plainData = "Hello World 123!".getBytes(); + for (int i = 0; i <= 8; i++) { + short rsaKeyArr = generateRsaKey(null, null, KMInteger.uint_8((byte) 1)); + Assert.assertEquals(KMInteger.cast(KMArray.cast(rsaKeyArr).get((short) 0)).getShort(), + KMError.OK); + + // Cache keyblob + short keyBlobPtr = KMArray.cast(rsaKeyArr).get((short) 1); + short inParams = getRsaParams(KMType.SHA2_256, KMType.RSA_PKCS1_1_5_SIGN); + inParams = KMKeyParameters.instance(inParams); + // Begin + short beginResp = begin(KMType.SIGN, keyBlobPtr, inParams, (short) 0, false); + if (i == 8) { + // Only 8 keys are allowed for MAX_USES_PER_BOOT + Assert.assertEquals(KMError.TOO_MANY_OPERATIONS, + KMInteger.cast(KMArray.cast(beginResp).get((short) 0)).getShort()); + return; + } + short opHandle = KMArray.cast(beginResp).get((short) 2); + finish(opHandle, + KMByteBlob.instance(plainData, (short) 0, (short) plainData.length), null, + (short) 0, (short) 0, (short) 0, (short) 0, KMError.OK, false); + } + cleanUp(); + } + + @Test + public void testRateLimitClearBufferAfterReboot() { + init(); + byte[] plainData = "Hello World 123!".getBytes(); + for (int i = 0; i <= 32; i++) { + if (i % 8 == 0) { + // Simulate reboot using set boot parameters. + // Clear the rate limited keys from the flash memory + Assert.assertEquals(KMError.OK, KMTestUtils.decodeError(decoder, + KMProvision.setBootParams(simulator, encoder, decoder, (short) KMProvision.BOOT_PATCH_LEVEL))); + KMProvision.setAndroidOSSystemProperties(simulator, encoder, decoder, + (short) KMProvision.OS_VERSION, + (short) KMProvision.OS_PATCH_LEVEL, + (short) KMProvision.VENDOR_PATCH_LEVEL); + KMProvision.computeSharedSecret(simulator, cryptoProvider, encoder, decoder); + } + short rsaKeyArr = generateRsaKey(null, null, KMInteger.uint_8((byte) 1)); + Assert.assertEquals(KMInteger.cast(KMArray.cast(rsaKeyArr).get((short) 0)).getShort(), + KMError.OK); + + // Cache keyblob + short keyBlobPtr = KMArray.cast(rsaKeyArr).get((short) 1); + short inParams = getRsaParams(KMType.SHA2_256, KMType.RSA_PKCS1_1_5_SIGN); + inParams = KMKeyParameters.instance(inParams); + // Begin + short beginResp = begin(KMType.SIGN, keyBlobPtr, inParams, (short) 0, false); + short opHandle = KMArray.cast(beginResp).get((short) 2); + // Finish + finish(opHandle, + KMByteBlob.instance(plainData, (short) 0, (short) plainData.length), null, + (short) 0, (short) 0, (short) 0, (short) 0, KMError.OK, false); + } + cleanUp(); + } + + @Test + public void testRateLimitWithHugeCount() { + init(); + short maxUsesPerBoot = 1000; + byte[] plainData = "Hello World 123!".getBytes(); + short rsaKeyArr = generateRsaKey(null, null, KMInteger.uint_16(maxUsesPerBoot)); + Assert.assertEquals(KMInteger.cast(KMArray.cast(rsaKeyArr).get((short) 0)).getShort(), + KMError.OK); + + // Cache keyblob + short keyBlobPtr = KMArray.cast(rsaKeyArr).get((short) 1); + byte[] keyBlob = new byte[KMByteBlob.cast(keyBlobPtr).length()]; + Util.arrayCopyNonAtomic(KMByteBlob.cast(keyBlobPtr).getBuffer(), + KMByteBlob.cast(keyBlobPtr).getStartOff(), + keyBlob, (short) 0, (short) keyBlob.length); + + for (int i = 0; i <= maxUsesPerBoot; i++) { + // Cache keyblob + keyBlobPtr = KMByteBlob.instance((short) keyBlob.length); + Util.arrayCopyNonAtomic(keyBlob, (short) 0, + KMByteBlob.cast(keyBlobPtr).getBuffer(), + KMByteBlob.cast(keyBlobPtr).getStartOff(), + (short) keyBlob.length); + short inParams = getRsaParams(KMType.SHA2_256, KMType.RSA_PKCS1_1_5_SIGN); + inParams = KMKeyParameters.instance(inParams); + // Begin + short beginResp = begin(KMType.SIGN, keyBlobPtr, inParams, (short) 0, false); + if (i == maxUsesPerBoot) { + Assert.assertEquals(KMError.KEY_MAX_OPS_EXCEEDED, + KMInteger.cast(KMArray.cast(beginResp).get((short) 0)).getShort()); + return; + } + short opHandle = KMArray.cast(beginResp).get((short) 2); + // Finish + finish(opHandle, + KMByteBlob.instance(plainData, (short) 0, (short) plainData.length), null, + (short) 0, (short) 0, (short) 0, (short) 0, KMError.OK, false); + } + cleanUp(); + } + + //------------------------------------------------------------------------------------------------ + // GetKeyCharacteristics tests + //------------------------------------------------------------------------------------------------ + @Test + public void testGetKeyCharacteristicsWithIdDataSuccess() { + init(); + byte[] clientId = "clientId".getBytes(); + byte[] appData = "appData".getBytes(); + short ret = generateRsaKey(clientId, appData); + short error = KMInteger.cast(KMArray.cast(ret).get((short) 0)).getShort(); + Assert.assertEquals(error, KMError.OK); + short keyBlob = KMArray.cast(ret).get((short) 1); + + short arrPtr = KMArray.instance((short) 3); + KMArray.cast(arrPtr).add((short) 0, keyBlob); + KMArray.cast(arrPtr) + .add((short) 1, KMByteBlob.instance(clientId, (short) 0, (short) clientId.length)); + KMArray.cast(arrPtr) + .add((short) 2, KMByteBlob.instance(appData, (short) 0, (short) appData.length)); + CommandAPDU apdu = KMTestUtils.encodeApdu(encoder, (byte) INS_GET_KEY_CHARACTERISTICS_CMD, + arrPtr); + ResponseAPDU response = simulator.transmitCommand(apdu); + ret = KMArray.instance((short) 2); + KMArray.cast(ret).add((short) 0, KMInteger.exp()); + short inst = KMKeyCharacteristics.exp(); + KMArray.cast(ret).add((short) 1, inst); + byte[] respBuf = response.getBytes(); + short len = (short) respBuf.length; + ret = decoder.decode(ret, respBuf, (short) 0, len); + error = KMInteger.cast(KMArray.cast(ret).get((short) 0)).getShort(); + Assert.assertEquals(error, KMError.OK); + cleanUp(); + } + + @Test + public void testGetKeyCharacteristicsSuccess() { + init(); + short ret = generateRsaKey(null, null); + short error = KMInteger.cast(KMArray.cast(ret).get((short) 0)).getShort(); + Assert.assertEquals(error, KMError.OK); + short keyBlob = KMArray.cast(ret).get((short) 1); + + short arrPtr = KMArray.instance((short) 3); + KMArray.cast(arrPtr).add((short) 0, keyBlob); + KMArray.cast(arrPtr).add((short) 1, KMByteBlob.instance((short) 0)); + KMArray.cast(arrPtr).add((short) 2, KMByteBlob.instance((short) 0)); + CommandAPDU apdu = KMTestUtils.encodeApdu(encoder, (byte) INS_GET_KEY_CHARACTERISTICS_CMD, + arrPtr); + // print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(apdu); + ret = KMArray.instance((short) 2); + KMArray.cast(ret).add((short) 0, KMInteger.exp()); + short inst = KMKeyCharacteristics.exp(); + KMArray.cast(ret).add((short) 1, inst); + byte[] respBuf = response.getBytes(); + short len = (short) respBuf.length; + ret = decoder.decode(ret, respBuf, (short) 0, len); + error = KMInteger.cast(KMArray.cast(ret).get((short) 0)).getShort(); + Assert.assertEquals(error, KMError.OK); + cleanUp(); + } + + //------------------------------------------------------------------------------------------------ + // DeleteKey tests + //------------------------------------------------------------------------------------------------ + @Test + public void testDeleteKeySuccess() { + init(); + short ret = generateRsaKey(null, null); + short keyBlobPtr = KMArray.cast(ret).get((short) 1); + byte[] keyBlob = new byte[KMByteBlob.cast(keyBlobPtr).length()]; + short len = KMByteBlob.cast(keyBlobPtr).getValues(keyBlob, (short) 0); + ret = getKeyCharacteristics(keyBlobPtr); + short error = KMInteger.cast(KMArray.cast(ret).get((short) 0)).getShort(); + Assert.assertEquals(error, KMError.OK); + deleteKey(KMByteBlob.instance(keyBlob, (short) 0, (short) keyBlob.length)); + cleanUp(); + } + + @Test + public void testDeleteAllKeySuccess() { + init(); + short ret1 = generateRsaKey(null, null); + short keyBlobPtr = KMArray.cast(ret1).get((short) 1); + byte[] keyBlob1 = new byte[KMByteBlob.cast(keyBlobPtr).length()]; + short len = KMByteBlob.cast(keyBlobPtr).getValues(keyBlob1, (short) 0); + short ret2 = generateRsaKey(null, null); + keyBlobPtr = KMArray.cast(ret2).get((short) 1); + byte[] keyBlob2 = new byte[KMByteBlob.cast(keyBlobPtr).length()]; + len = KMByteBlob.cast(keyBlobPtr).getValues(keyBlob2, (short) 0); + CommandAPDU apdu = new CommandAPDU(0x80, INS_DELETE_ALL_KEYS_CMD, 0x50, 0x00); + // print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(apdu); + Assert.assertEquals(KMError.OK, KMTestUtils.decodeError(decoder, response)); + cleanUp(); + } + + //------------------------------------------------------------------------------------------------ + // getHmacParams and computeSharedHmac + //------------------------------------------------------------------------------------------------ + @Test + public void testComputeHmacParams() { + init(); + // Get Hmac parameters + short ret = KMProvision.getHmacSharingParams(simulator, decoder); + Assert.assertEquals(KMError.OK, KMInteger.cast(KMArray.cast(ret).get((short) 0)).getShort()); + KMHmacSharingParameters params = KMHmacSharingParameters.cast(KMArray.cast(ret).get((short) 1)); + short seed = params.getSeed(); + short nonce = params.getNonce(); + + short params1 = KMHmacSharingParameters.instance(); + KMHmacSharingParameters.cast(params1).setSeed(KMByteBlob.instance((short) 0)); + short num = KMByteBlob.instance((short) 32); + Util.arrayCopyNonAtomic( + KMByteBlob.cast(nonce).getBuffer(), + KMByteBlob.cast(nonce).getStartOff(), + KMByteBlob.cast(num).getBuffer(), + KMByteBlob.cast(num).getStartOff(), + KMByteBlob.cast(num).length()); + + KMHmacSharingParameters.cast(params1).setNonce(num); + short params2 = KMHmacSharingParameters.instance(); + KMHmacSharingParameters.cast(params2).setSeed(KMByteBlob.instance((short) 0)); + num = KMByteBlob.instance((short) 32); + cryptoProvider.newRandomNumber( + KMByteBlob.cast(num).getBuffer(), + KMByteBlob.cast(num).getStartOff(), + KMByteBlob.cast(num).length()); + KMHmacSharingParameters.cast(params2).setNonce(num); + short arr = KMArray.instance((short) 2); + KMArray.cast(arr).add((short) 0, params1); + KMArray.cast(arr).add((short) 1, params2); + short arrPtr = KMArray.instance((short) 1); + KMArray.cast(arrPtr).add((short) 0, arr); + CommandAPDU apdu = KMTestUtils.encodeApdu(encoder, (byte) INS_COMPUTE_SHARED_HMAC_CMD, arrPtr); + // print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(apdu); + Assert.assertEquals(0x9000, response.getSW()); + ret = KMArray.instance((short) 2); + KMArray.cast(ret).add((short) 0, KMInteger.exp()); + KMArray.cast(ret).add((short) 1, KMByteBlob.exp()); + byte[] respBuf = response.getBytes(); + short len = (short) respBuf.length; + ret = decoder.decode(ret, respBuf, (short) 0, len); + short error = KMInteger.cast(KMArray.cast(ret).get((short) 0)).getShort(); + Assert.assertEquals(0x9000, response.getSW()); + Assert.assertEquals(error, KMError.OK); + cleanUp(); + } + + @Test + public void testGetHmacSharingParams() { + init(); + CommandAPDU commandAPDU = new CommandAPDU(0x80, INS_GET_HMAC_SHARING_PARAM_CMD, 0x50, 0x00); + //print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(commandAPDU); + Assert.assertEquals(0x9000, response.getSW()); + short ret = KMArray.instance((short) 2); + KMArray.cast(ret).add((short) 0, KMInteger.exp()); + short inst = KMHmacSharingParameters.exp(); + KMArray.cast(ret).add((short) 1, inst); + byte[] respBuf = response.getBytes(); + KMTestUtils.print(respBuf, (short) 0, (short) respBuf.length); + ret = decoder.decode(ret, respBuf, (short) 0, (short) respBuf.length); + Assert.assertEquals(KMError.OK, KMInteger.cast(KMArray.cast(ret).get((short) 0)).getShort()); + KMHmacSharingParameters params = KMHmacSharingParameters.cast(KMArray.cast(ret).get((short) 1)); + short seed = params.getSeed(); + short nonce = params.getNonce(); + Assert.assertTrue(KMByteBlob.cast(seed).length() == 0); + Assert.assertTrue(KMByteBlob.cast(nonce).length() == 32); + cleanUp(); + } + + //------------------------------------------------------------------------------------------------ + // AES/DES operations + //------------------------------------------------------------------------------------------------ + @Test + public void testWithAesGcmWithUpdate() { + init(); + testEncryptDecryptWithAesDes(KMType.AES, KMType.GCM, KMType.PADDING_NONE, true); + cleanUp(); + } + + @Test + public void testWithAesEcbPkcs7WithUpdate() { + init(); + testEncryptDecryptWithAesDes(KMType.AES, KMType.ECB, KMType.PKCS7, true); + cleanUp(); + } + + @Test + public void testWithAesCtrNoPadWithUpdate() { + init(); + testEncryptDecryptWithAesDes(KMType.AES, KMType.CTR, KMType.PADDING_NONE, true); + cleanUp(); + } + + @Test + public void testWithAesCtrNoPad() { + init(); + testEncryptDecryptWithAesDes(KMType.AES, KMType.CTR, KMType.PADDING_NONE, false); + cleanUp(); + } + + @Test + public void testWithAesEcbNoPadWithUpdate() { + init(); + testEncryptDecryptWithAesDes(KMType.AES, KMType.ECB, KMType.PADDING_NONE, true); + cleanUp(); + } + + @Test + public void testWithDesEcbPkcs7WithUpdate() { + init(); + testEncryptDecryptWithAesDes(KMType.DES, KMType.ECB, KMType.PKCS7, true); + cleanUp(); + } + + @Test + public void testWithDesEcbNoPadWithUpdate() { + init(); + testEncryptDecryptWithAesDes(KMType.DES, KMType.ECB, KMType.PADDING_NONE, true); + cleanUp(); + } + + @Test + public void testWithAesCbcPkcs7WithUpdate() { + init(); + testEncryptDecryptWithAesDes(KMType.AES, KMType.CBC, KMType.PKCS7, true); + cleanUp(); + } + + @Test + public void testWithAesCbcNoPadWithUpdate() { + init(); + testEncryptDecryptWithAesDes(KMType.AES, KMType.CBC, KMType.PADDING_NONE, true); + cleanUp(); + } + + @Test + public void testWithDesCbcPkcs7WithUpdate() { + init(); + testEncryptDecryptWithAesDes(KMType.DES, KMType.CBC, KMType.PKCS7, true); + cleanUp(); + } + + @Test + public void testWithDesCbcNoPadWithUpdate() { + init(); + testEncryptDecryptWithAesDes(KMType.DES, KMType.CBC, KMType.PADDING_NONE, true); + cleanUp(); + } + + @Test + public void testWithAesEcbPkcs7() { + init(); + testEncryptDecryptWithAesDes(KMType.AES, KMType.ECB, KMType.PKCS7, false); + cleanUp(); + } + + @Test + public void testWithAesCbcPkcs7() { + init(); + testEncryptDecryptWithAesDes(KMType.AES, KMType.CBC, KMType.PKCS7, false); + cleanUp(); + } + + @Test + public void testWithAesEcbNoPad() { + init(); + testEncryptDecryptWithAesDes(KMType.AES, KMType.ECB, KMType.PADDING_NONE, false); + cleanUp(); + } + + @Test + public void testWithAesCbcNoPad() { + init(); + testEncryptDecryptWithAesDes(KMType.AES, KMType.CBC, KMType.PADDING_NONE, false); + cleanUp(); + } + + @Test + public void testWithDesCbcPkcs7() { + init(); + testEncryptDecryptWithAesDes(KMType.DES, KMType.CBC, KMType.PKCS7, false); + cleanUp(); + } + + @Test + public void testWithDesCbcNoPad() { + init(); + testEncryptDecryptWithAesDes(KMType.DES, KMType.CBC, KMType.PADDING_NONE, false); + cleanUp(); + } + + @Test + public void testWithDesEcbNoPad() { + init(); + testEncryptDecryptWithAesDes(KMType.DES, KMType.ECB, KMType.PADDING_NONE, false); + cleanUp(); + } + + @Test + public void testWithDesEcbPkcs7() { + init(); + testEncryptDecryptWithAesDes(KMType.DES, KMType.ECB, KMType.PKCS7, false); + cleanUp(); + } + + @Test + public void testUnsupportedBlockMode() { + init(); + short desKey = generateAesDesKey(KMType.DES, (short) 168, null, null, false); + short desKeyPtr = KMArray.cast(desKey).get((short) 1); + byte[] keyBlob = new byte[KMByteBlob.cast(desKeyPtr).length()]; + Util.arrayCopyNonAtomic(KMByteBlob.cast(desKeyPtr).getBuffer(), KMByteBlob + .cast(desKeyPtr).getStartOff(), keyBlob, (short) 0, + (short) keyBlob.length); + short desPkcs7Params = getAesDesParams(KMType.DES, (byte) KMType.CTR, + KMType.PKCS7, new byte[12]); + short ret = begin(KMType.ENCRYPT, + KMByteBlob.instance(keyBlob, (short) 0, (short) keyBlob.length), + KMKeyParameters.instance(desPkcs7Params), (short) 0, false); + ret = KMInteger.cast(KMArray.cast(ret).get((short) 0)).getShort(); + Assert.assertEquals(KMError.UNSUPPORTED_BLOCK_MODE, ret); + cleanUp(); + } + + @Test + public void testDesEcbPkcs7PaddingCorrupted() { + init(); + short desKey = generateAesDesKey(KMType.DES, (short) 168, null, null, false); + short desKeyPtr = KMArray.cast(desKey).get((short) 1); + byte[] keyBlob = new byte[KMByteBlob.cast(desKeyPtr).length()]; + Util.arrayCopyNonAtomic(KMByteBlob.cast(desKeyPtr).getBuffer(), KMByteBlob + .cast(desKeyPtr).getStartOff(), keyBlob, (short) 0, + (short) keyBlob.length); + + byte[] message = { + 0x61}; + short desPkcs7Params = getAesDesParams(KMType.DES, KMType.ECB, + KMType.PKCS7, null); + byte[] cipherText1 = EncryptMessage(message, desPkcs7Params, keyBlob); + Assert.assertEquals(8, cipherText1.length); + Assert.assertFalse(Arrays.equals(message, cipherText1)); + + // Corrupt the cipher text. + ++cipherText1[(cipherText1.length / 2)]; + + // Decrypt operation + // Begin + desPkcs7Params = getAesDesParams(KMType.DES, KMType.ECB, KMType.PKCS7, null); + + short ret = begin(KMType.DECRYPT, + KMByteBlob.instance(keyBlob, (short) 0, (short) keyBlob.length), + KMKeyParameters.instance(desPkcs7Params), (short) 0, false); + // Get the operation handle. + short opHandle = KMArray.cast(ret).get((short) 2); + byte[] opHandleBuf = new byte[KMOperationState.OPERATION_HANDLE_SIZE]; + KMInteger.cast(opHandle).getValue(opHandleBuf, (short) 0, + (short) opHandleBuf.length); + opHandle = KMInteger.uint_64(opHandleBuf, (short) 0); + + // Finish + short dataPtr = KMByteBlob.instance(cipherText1, (short) 0, + (short) cipherText1.length); + opHandle = KMInteger.uint_64(opHandleBuf, (short) 0); + ret = finish(opHandle, dataPtr, null, (short) 0, (short) 0, (short) 0, + (short) 0, KMError.INVALID_ARGUMENT, false); + cleanUp(); + } + + //------------------------------------------------------------------------------------------------ + // RSA operation tests + //------------------------------------------------------------------------------------------------ + @Test + public void testWithRsa256Oaep() { + init(); + testEncryptDecryptWithRsa(KMType.SHA2_256, KMType.RSA_OAEP); + cleanUp(); + } + + @Test + public void testWithRsaNonePkcs1() { + init(); + testEncryptDecryptWithRsa(KMType.DIGEST_NONE, KMType.RSA_PKCS1_1_5_ENCRYPT); + cleanUp(); + } + + @Test + public void testWithRsaNoneNoPad() { + init(); + testEncryptDecryptWithRsa(KMType.DIGEST_NONE, KMType.PADDING_NONE); + cleanUp(); + } + + @Test + public void testSignWithRsaNoneNoPad() { + init(); + testSignVerifyWithRsa(KMType.DIGEST_NONE, KMType.PADDING_NONE, false, false); + cleanUp(); + } + + @Test + public void testSignWithRsaNonePkcs1() { + init(); + testSignVerifyWithRsa(KMType.DIGEST_NONE, KMType.RSA_PKCS1_1_5_SIGN, false, false); + cleanUp(); + } + + @Test + public void testSignVerifyWithRsaSHA256Pkcs1() { + init(); + testSignVerifyWithRsa(KMType.SHA2_256, KMType.RSA_PKCS1_1_5_SIGN, false, true); + cleanUp(); + } + + @Test + public void testSignVerifyWithRsaSHA256Pss() { + init(); + testSignVerifyWithRsa(KMType.SHA2_256, KMType.RSA_PSS, false, true); + cleanUp(); + } + + @Test + public void testSignVerifyWithRsaSHA256Pkcs1WithUpdate() { + init(); + testSignVerifyWithRsa(KMType.SHA2_256, KMType.RSA_PKCS1_1_5_SIGN, true, true); + cleanUp(); + } + + @Test + public void testVtsRsaPkcs1Success() { + init(); + byte[] message = { + 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x57, 0x6f, 0x72, 0x6c, 0x64, + 0x21}; // "Hello World!"; + for (int i = 0; i < 250; i++) { + short key = generateRsaKey(null, null); + short rsaKeyPtr = KMArray.cast(key).get((short) 1); + byte[] keyBlob = new byte[KMByteBlob.cast(rsaKeyPtr).length()]; + Util.arrayCopyNonAtomic(KMByteBlob.cast(rsaKeyPtr).getBuffer(), + KMByteBlob.cast(rsaKeyPtr).getStartOff(), keyBlob, (short) 0, + (short) keyBlob.length); + short pkcs1Params = getRsaParams(KMType.DIGEST_NONE, + KMType.RSA_PKCS1_1_5_ENCRYPT); + + byte[] cipherText1 = new byte[256]; + short cipherText1Len = rsaEncryptMessage(keyBlob, KMType.RSA_PKCS1_1_5_ENCRYPT, + KMType.DIGEST_NONE, + message, (short) 0, (short) message.length, + cipherText1, (short) 0); + Assert.assertEquals((2048 / 8), cipherText1Len); + + pkcs1Params = getRsaParams(KMType.DIGEST_NONE, + KMType.RSA_PKCS1_1_5_ENCRYPT); + byte[] cipherText2 = new byte[256]; + short cipherText2Len = rsaEncryptMessage(keyBlob, KMType.RSA_PKCS1_1_5_ENCRYPT, + KMType.DIGEST_NONE, + message, (short) 0, (short) message.length, + cipherText2, (short) 0); + Assert.assertEquals((2048 / 8), cipherText2Len); + + // PKCS1 v1.5 randomizes padding so every result should be different. + Assert.assertFalse(Arrays.equals(cipherText1, cipherText2)); + //Clean the heap. + KMRepository.instance().clean(); + pkcs1Params = getRsaParams(KMType.DIGEST_NONE, + KMType.RSA_PKCS1_1_5_ENCRYPT); + byte[] plainText = DecryptMessage(cipherText1, pkcs1Params, keyBlob); + Assert.assertTrue(Arrays.equals(message, plainText)); + + // Decrypting corrupted ciphertext should fail. + short offset_to_corrupt = generateRandom((short) cipherText1.length); + + byte corrupt_byte; + do { + corrupt_byte = (byte) generateRandom((short) 256); + } while (corrupt_byte == cipherText1[offset_to_corrupt]); + cipherText1[offset_to_corrupt] = corrupt_byte; + + pkcs1Params = getRsaParams(KMType.DIGEST_NONE, + KMType.RSA_PKCS1_1_5_ENCRYPT); + // Do Begin operation. + short ret = begin(KMType.DECRYPT, + KMByteBlob.instance(keyBlob, (short) 0, (short) keyBlob.length), + KMKeyParameters.instance(pkcs1Params), (short) 0, false); + + // Get the operation handle. + short opHandle = KMArray.cast(ret).get((short) 2); + byte[] opHandleBuf = new byte[KMOperationState.OPERATION_HANDLE_SIZE]; + KMInteger.cast(opHandle).getValue(opHandleBuf, (short) 0, + (short) opHandleBuf.length); + opHandle = KMInteger.uint_64(opHandleBuf, (short) 0); + + short dataPtr = KMByteBlob.instance(cipherText1, (short) 0, + (short) cipherText1.length); + // Finish should return UNKNOWN_ERROR. + ret = finish(opHandle, dataPtr, null, (short) 0, (short) 0, (short) 0, + (short) 0, KMError.UNKNOWN_ERROR, false); + } + cleanUp(); + } + + //------------------------------------------------------------------------------------------------ + // HMac operations + //------------------------------------------------------------------------------------------------ + @Test + public void testSignVerifyWithHmacSHA256WithUpdate() { + init(); + testSignVerifyWithHmac(KMType.SHA2_256, true); + cleanUp(); + } + + @Test + public void testSignVerifyWithHmacSHA256() { + init(); + testSignVerifyWithHmac(KMType.SHA2_256, false); + cleanUp(); + } + + //------------------------------------------------------------------------------------------------ + // ECDSA operation tests + //------------------------------------------------------------------------------------------------ + @Test + public void testSignVerifyWithEcdsaSHA256WithUpdate() { + init(); + testSignVerifyWithEcdsa(KMType.SHA2_256, true); + cleanUp(); + } + + @Test + public void testSignVerifyWithEcdsaSHA256() { + init(); + testSignVerifyWithEcdsa(KMType.SHA2_256, false); + cleanUp(); + } + + //------------------------------------------------------------------------------------------------ + // Provision tests + //------------------------------------------------------------------------------------------------ + @Test + public void testVerifyOemLockWithOutSeLockFailure() { + AID appletAID1 = AIDUtil.create("A000000062"); + simulator.installApplet(appletAID1, KMJCardSimApplet.class); + // Select applet + simulator.selectApplet(appletAID1); + Assert.assertEquals(KMError.OK, KMTestUtils.decodeError(decoder, + KMProvision.provisionDeviceUniqueKeyPair(simulator, cryptoProvider, encoder, decoder))); + Assert.assertEquals(KMError.OK, KMTestUtils.decodeError(decoder, + KMProvision.provisionAdditionalCertChain(simulator, cryptoProvider, encoder, decoder))); + Assert.assertEquals(KMError.OK, KMTestUtils.decodeError(decoder, + KMProvision.provisionSharedSecret(simulator, encoder, decoder))); + Assert.assertEquals(KMError.OK, KMTestUtils.decodeError(decoder, + KMProvision.provisionAttestIds(simulator, encoder, decoder))); + Assert.assertEquals(KMError.OK, KMTestUtils.decodeError(decoder, + KMProvision.provisionOEMRootPublicKey(simulator, encoder, decoder))); + + ResponseAPDU response = KMProvision.provisionLocked(simulator, encoder, decoder); + Assert.assertEquals(KMError.UNKNOWN_ERROR, KMTestUtils.decodeError(decoder, response)); + cleanUp(); + } + + @Test + public void testVerifyOemUnLockAfterOemLockSuccess() { + AID appletAID1 = AIDUtil.create("A000000062"); + simulator.installApplet(appletAID1, KMJCardSimApplet.class); + // Select applet + simulator.selectApplet(appletAID1); + // provision attest key + Assert.assertEquals(KMError.OK, KMTestUtils.decodeError(decoder, + KMProvision.provisionDeviceUniqueKeyPair(simulator, cryptoProvider, encoder, decoder))); + Assert.assertEquals(KMError.OK, KMTestUtils.decodeError(decoder, + KMProvision.provisionAdditionalCertChain(simulator, cryptoProvider, encoder, decoder))); + Assert.assertEquals(KMError.OK, KMTestUtils.decodeError(decoder, + KMProvision.provisionSeLocked(simulator, decoder))); + Assert.assertEquals(KMError.OK, KMTestUtils.decodeError(decoder, + KMProvision.provisionSharedSecret(simulator, encoder, decoder))); + Assert.assertEquals(KMError.OK, KMTestUtils.decodeError(decoder, + KMProvision.provisionAttestIds(simulator, encoder, decoder))); + Assert.assertEquals(KMError.OK, KMTestUtils.decodeError(decoder, + KMProvision.provisionOEMRootPublicKey(simulator, encoder, decoder))); + Assert.assertEquals(KMError.OK, KMTestUtils.decodeError(decoder, + KMProvision.provisionLocked(simulator, encoder, decoder))); + Assert.assertEquals(KMError.OK, KMTestUtils.decodeError(decoder, + KMProvision.setBootParams(simulator, encoder, decoder, (short) KMProvision.BOOT_PATCH_LEVEL))); + // set android system properties + Assert.assertEquals(KMError.OK, KMTestUtils.decodeError(decoder, + KMProvision.setAndroidOSSystemProperties(simulator, encoder, decoder, + (short) KMProvision.OS_VERSION, + (short) KMProvision.OS_PATCH_LEVEL, + (short) KMProvision.VENDOR_PATCH_LEVEL))); + // negotiate shared secret. + KMProvision.computeSharedSecret(simulator, cryptoProvider, encoder, decoder); + KMProvision.sendEarlyBootEnded(simulator, decoder); + Assert.assertEquals(KMError.OK, KMTestUtils.decodeError(decoder, + KMProvision.provisionOemUnLock(simulator, encoder, decoder))); + Assert.assertEquals(KMError.OK, KMTestUtils.decodeError(decoder, + KMProvision.provisionSharedSecret(simulator, encoder, decoder))); + Assert.assertEquals(KMError.OK, KMTestUtils.decodeError(decoder, + KMProvision.provisionAttestIds(simulator, encoder, decoder))); + Assert.assertEquals(KMError.OK, KMTestUtils.decodeError(decoder, + KMProvision.provisionLocked(simulator, encoder, decoder))); + // try generating key + generateRsaKey(null, null); + cleanUp(); + } + + @Test + public void testVerifyOemLockWithOutOemRootKeyFailure() { + AID appletAID1 = AIDUtil.create("A000000062"); + simulator.installApplet(appletAID1, KMJCardSimApplet.class); + // Select applet + simulator.selectApplet(appletAID1); + // provision attest key + Assert.assertEquals(KMError.OK, KMTestUtils.decodeError(decoder, + KMProvision.provisionDeviceUniqueKeyPair(simulator, cryptoProvider, encoder, decoder))); + Assert.assertEquals(KMError.OK, KMTestUtils.decodeError(decoder, + KMProvision.provisionAdditionalCertChain(simulator, cryptoProvider, encoder, decoder))); + Assert.assertEquals(KMError.OK, KMTestUtils.decodeError(decoder, + KMProvision.provisionSeLocked(simulator, decoder))); + Assert.assertEquals(KMError.OK, KMTestUtils.decodeError(decoder, + KMProvision.provisionSharedSecret(simulator, encoder, decoder))); + Assert.assertEquals(KMError.OK, KMTestUtils.decodeError(decoder, + KMProvision.provisionAttestIds(simulator, encoder, decoder))); + Assert.assertEquals(KMError.UNKNOWN_ERROR, KMTestUtils.decodeError(decoder, + KMProvision.provisionLocked(simulator, encoder, decoder))); + cleanUp(); + } + + @Test + public void testVerifySeLockWithOutCertDataFailure() { + AID appletAID1 = AIDUtil.create("A000000062"); + simulator.installApplet(appletAID1, KMJCardSimApplet.class); + // Select applet + simulator.selectApplet(appletAID1); + // provision attest key + Assert.assertEquals(KMError.OK, KMTestUtils.decodeError(decoder, + KMProvision.provisionDeviceUniqueKeyPair(simulator, cryptoProvider, encoder, decoder))); + Assert.assertEquals(KMError.UNKNOWN_ERROR, KMTestUtils.decodeError(decoder, + KMProvision.provisionSeLocked(simulator, decoder))); + cleanUp(); + } + + @Test + public void testVerifyProvisionSeDataAfterSeLockFailure() { + AID appletAID1 = AIDUtil.create("A000000062"); + simulator.installApplet(appletAID1, KMJCardSimApplet.class); + // Select applet + simulator.selectApplet(appletAID1); + // provision attest key + Assert.assertEquals(KMError.OK, KMTestUtils.decodeError(decoder, + KMProvision.provisionDeviceUniqueKeyPair(simulator, cryptoProvider, encoder, decoder))); + Assert.assertEquals(KMError.OK, KMTestUtils.decodeError(decoder, + KMProvision.provisionAdditionalCertChain(simulator, cryptoProvider, encoder, decoder))); + Assert.assertEquals(KMError.OK, KMTestUtils.decodeError(decoder, + KMProvision.provisionSeLocked(simulator, decoder))); + Assert.assertEquals(KMError.UNKNOWN_ERROR, KMTestUtils.decodeError(decoder, + KMProvision.provisionDeviceUniqueKeyPair(simulator, cryptoProvider, encoder, decoder))); + Assert.assertEquals(KMError.UNKNOWN_ERROR, KMTestUtils.decodeError(decoder, + KMProvision.provisionAdditionalCertChain(simulator, cryptoProvider, encoder, decoder))); + cleanUp(); + } + + @Test + public void testVerifyOemProvisionAfterOemLockFailure() { + AID appletAID1 = AIDUtil.create("A000000062"); + simulator.installApplet(appletAID1, KMJCardSimApplet.class); + // Select applet + simulator.selectApplet(appletAID1); + // provision attest key + Assert.assertEquals(KMError.OK, KMTestUtils.decodeError(decoder, + KMProvision.provisionDeviceUniqueKeyPair(simulator, cryptoProvider, encoder, decoder))); + Assert.assertEquals(KMError.OK, KMTestUtils.decodeError(decoder, + KMProvision.provisionAdditionalCertChain(simulator, cryptoProvider, encoder, decoder))); + Assert.assertEquals(KMError.OK, KMTestUtils.decodeError(decoder, + KMProvision.provisionSeLocked(simulator, decoder))); + Assert.assertEquals(KMError.OK, KMTestUtils.decodeError(decoder, + KMProvision.provisionSharedSecret(simulator, encoder, decoder))); + Assert.assertEquals(KMError.OK, KMTestUtils.decodeError(decoder, + KMProvision.provisionAttestIds(simulator, encoder, decoder))); + Assert.assertEquals(KMError.OK, KMTestUtils.decodeError(decoder, + KMProvision.provisionOEMRootPublicKey(simulator, encoder, decoder))); + Assert.assertEquals(KMError.OK, KMTestUtils.decodeError(decoder, + KMProvision.provisionLocked(simulator, encoder, decoder))); + Assert.assertEquals(KMError.UNKNOWN_ERROR, KMTestUtils.decodeError(decoder, + KMProvision.provisionSharedSecret(simulator, encoder, decoder))); + Assert.assertEquals(KMError.UNKNOWN_ERROR, KMTestUtils.decodeError(decoder, + KMProvision.provisionAttestIds(simulator, encoder, decoder))); + cleanUp(); + } + + //------------------------------------------------------------------------------------------------ + // Helper functions + //------------------------------------------------------------------------------------------------ + public short generateAesDesKey(byte alg, short keysize, byte[] clientId, byte[] appData, + boolean unlockReqd) { + short tagCount = 9; + if (clientId != null) { + tagCount++; + } + if (appData != null) { + tagCount++; + } + if (unlockReqd) { + tagCount++; + } + short arrPtr = KMArray.instance(tagCount); + short boolTag = KMBoolTag.instance(KMType.NO_AUTH_REQUIRED); + short keySize = KMIntegerTag + .instance(KMType.UINT_TAG, KMType.KEYSIZE, KMInteger.uint_16(keysize)); + short byteBlob = KMByteBlob.instance((short) 3); + KMByteBlob.cast(byteBlob).add((short) 0, KMType.ECB); + KMByteBlob.cast(byteBlob).add((short) 1, KMType.CBC); + KMByteBlob.cast(byteBlob).add((short) 2, KMType.CTR); + short blockModeTag = KMEnumArrayTag.instance(KMType.BLOCK_MODE, byteBlob); + byteBlob = KMByteBlob.instance((short) 2); + KMByteBlob.cast(byteBlob).add((short) 0, KMType.PKCS7); + KMByteBlob.cast(byteBlob).add((short) 1, KMType.PADDING_NONE); + short paddingMode = KMEnumArrayTag.instance(KMType.PADDING, byteBlob); + byteBlob = KMByteBlob.instance((short) 2); + KMByteBlob.cast(byteBlob).add((short) 0, KMType.ENCRYPT); + KMByteBlob.cast(byteBlob).add((short) 1, KMType.DECRYPT); + short purpose = KMEnumArrayTag.instance(KMType.PURPOSE, byteBlob); + short tagIndex = 0; + KMArray.cast(arrPtr).add(tagIndex++, boolTag); + KMArray.cast(arrPtr).add(tagIndex++, keySize); + KMArray.cast(arrPtr).add(tagIndex++, blockModeTag); + KMArray.cast(arrPtr).add(tagIndex++, paddingMode); + KMArray.cast(arrPtr).add(tagIndex++, KMEnumTag.instance(KMType.ALGORITHM, alg)); + KMArray.cast(arrPtr).add(tagIndex++, purpose); + KMArray.cast(arrPtr).add(tagIndex++, KMBoolTag.instance(KMType.CALLER_NONCE)); + tagIndex = setDefaultValidity(arrPtr, tagIndex); + if (unlockReqd) { + KMArray.cast(arrPtr).add(tagIndex++, KMBoolTag.instance(KMType.UNLOCKED_DEVICE_REQUIRED)); + } + if (clientId != null) { + KMArray.cast(arrPtr).add(tagIndex++, + KMByteTag.instance(KMType.APPLICATION_ID, + KMByteBlob.instance(clientId, (short) 0, (short) clientId.length))); + } + if (appData != null) { + KMArray.cast(arrPtr).add(tagIndex++, + KMByteTag.instance(KMType.APPLICATION_DATA, + KMByteBlob.instance(appData, (short) 0, (short) appData.length))); + } + short keyParams = KMKeyParameters.instance(arrPtr); + arrPtr = generateKeyNoAttestCmd(keyParams); + CommandAPDU apdu = KMTestUtils.encodeApdu(encoder, (byte) INS_GENERATE_KEY_CMD, arrPtr); + ResponseAPDU response = simulator.transmitCommand(apdu); + return parseGenerateKeyResponse(response); + } + + + public short parseImportKeyResponse(ResponseAPDU response) { + return parseGenerateKeyResponse(response); + } + + public short parseGenerateKeyResponse(ResponseAPDU response) { + Assert.assertEquals(0x9000, response.getSW()); + short byteBlobExp = KMByteBlob.exp(); + short certArrayExp = KMArray.exp(byteBlobExp); + short inst = KMKeyCharacteristics.exp(); + short ret = KMArray.instance((short) 4); + KMArray.cast(ret).add((short) 0, KMInteger.exp()); + KMArray.cast(ret).add((short) 1, KMByteBlob.exp()); + KMArray.cast(ret).add((short) 2, inst); + KMArray.cast(ret).add((short) 3, certArrayExp); + byte[] respBuf = response.getBytes(); + short len = (short) respBuf.length; + ret = decoder.decode(ret, respBuf, (short) 0, len); + Assert.assertEquals(KMError.OK, KMInteger.cast(KMArray.cast(ret).get((short) 0)).getShort()); + return ret; + } + + public ResponseAPDU importRsaKey(boolean includeAttestKeyPurpose) { + byte[] pub = new byte[]{0x00, 0x01, 0x00, 0x01}; + short arrPtr = KMArray.instance((short) 9); + short boolTag = KMBoolTag.instance(KMType.NO_AUTH_REQUIRED); + short keySize = KMIntegerTag + .instance(KMType.UINT_TAG, KMType.KEYSIZE, KMInteger.uint_16((short) 2048)); + short byteBlob = KMByteBlob.instance((short) 1); + KMByteBlob.cast(byteBlob).add((short) 0, KMType.SHA2_256); + short digest = KMEnumArrayTag.instance(KMType.DIGEST, byteBlob); + short rsaPubExpTag = KMIntegerTag.instance(KMType.ULONG_TAG, KMType.RSA_PUBLIC_EXPONENT, + KMInteger.uint_32(pub, (short) 0)); + byteBlob = KMByteBlob.instance((short) 1); + KMByteBlob.cast(byteBlob).add((short) 0, KMType.RSA_PSS); + short padding = KMEnumArrayTag.instance(KMType.PADDING, byteBlob); + short purposeLength = (short) (includeAttestKeyPurpose ? 2 : 1); + byteBlob = KMByteBlob.instance((short) purposeLength); + KMByteBlob.cast(byteBlob).add((short) 0, KMType.SIGN); + if (includeAttestKeyPurpose) { + KMByteBlob.cast(byteBlob).add((short) 1, KMType.ATTEST_KEY); + } + short purpose = KMEnumArrayTag.instance(KMType.PURPOSE, byteBlob); + KMArray.cast(arrPtr).add((short) 0, boolTag); + KMArray.cast(arrPtr).add((short) 1, keySize); + KMArray.cast(arrPtr).add((short) 2, digest); + KMArray.cast(arrPtr).add((short) 3, rsaPubExpTag); + KMArray.cast(arrPtr).add((short) 4, KMEnumTag.instance(KMType.ALGORITHM, KMType.RSA)); + KMArray.cast(arrPtr).add((short) 5, padding); + KMArray.cast(arrPtr).add((short) 6, purpose); + short nextIndex = setDefaultValidity(arrPtr, (short) 7); + short keyParams = KMKeyParameters.instance(arrPtr); + short keyFormatPtr = KMEnum.instance(KMType.KEY_FORMAT, KMType.PKCS8); + short keyBlob = KMByteBlob.instance(rsa_key_pkcs8, (short) 0, (short) rsa_key_pkcs8.length); + arrPtr = importKeyNoAttestCmd(keyParams, keyFormatPtr, keyBlob); + CommandAPDU apdu = KMTestUtils.encodeApdu(encoder, (byte) INS_IMPORT_KEY_CMD, arrPtr); + return simulator.transmitCommand(apdu); + } + + private short generateRsaKey(byte[] clientId, byte[] appData, short keyUsageLimitPtr) { + short tagCount = 11; + if (clientId != null) { + tagCount++; + } + if (appData != null) { + tagCount++; + } + if (keyUsageLimitPtr != KMType.INVALID_VALUE) { + tagCount++; + } + short arrPtr = KMArray.instance(tagCount); + short keySize = KMIntegerTag + .instance(KMType.UINT_TAG, KMType.KEYSIZE, KMInteger.uint_16((short) 2048)); + short byteBlob = KMByteBlob.instance((short) 3); + KMByteBlob.cast(byteBlob).add((short) 0, KMType.DIGEST_NONE); + KMByteBlob.cast(byteBlob).add((short) 1, KMType.SHA2_256); + KMByteBlob.cast(byteBlob).add((short) 2, KMType.SHA1); + short digest = KMEnumArrayTag.instance(KMType.DIGEST, byteBlob); + byteBlob = KMByteBlob.instance((short) 5); + KMByteBlob.cast(byteBlob).add((short) 0, KMType.RSA_PKCS1_1_5_ENCRYPT); + KMByteBlob.cast(byteBlob).add((short) 1, KMType.RSA_PKCS1_1_5_SIGN); + KMByteBlob.cast(byteBlob).add((short) 2, KMType.RSA_OAEP); + KMByteBlob.cast(byteBlob).add((short) 3, KMType.RSA_PSS); + KMByteBlob.cast(byteBlob).add((short) 4, KMType.PADDING_NONE); + short padding = KMEnumArrayTag.instance(KMType.PADDING, byteBlob); + byteBlob = KMByteBlob.instance((short) 5); + KMByteBlob.cast(byteBlob).add((short) 0, KMType.SIGN); + KMByteBlob.cast(byteBlob).add((short) 1, KMType.VERIFY); + KMByteBlob.cast(byteBlob).add((short) 2, KMType.ENCRYPT); + KMByteBlob.cast(byteBlob).add((short) 3, KMType.DECRYPT); + KMByteBlob.cast(byteBlob).add((short) 4, KMType.WRAP_KEY); + short purpose = KMEnumArrayTag.instance(KMType.PURPOSE, byteBlob); + byte[] pub = {0, 1, 0, 1}; + short rsaPubExpTag = KMIntegerTag + .instance(KMType.ULONG_TAG, KMType.RSA_PUBLIC_EXPONENT, KMInteger.uint_32(pub, (short) 0)); + short boolTag = KMBoolTag.instance(KMType.NO_AUTH_REQUIRED); + short tagIndex = 0; + KMArray.cast(arrPtr).add(tagIndex++, purpose); + KMArray.cast(arrPtr).add(tagIndex++, KMBoolTag.instance(KMType.INCLUDE_UNIQUE_ID)); + KMArray.cast(arrPtr).add(tagIndex++, KMBoolTag.instance(KMType.RESET_SINCE_ID_ROTATION)); + KMArray.cast(arrPtr).add(tagIndex++, boolTag); + KMArray.cast(arrPtr).add(tagIndex++, keySize); + KMArray.cast(arrPtr).add(tagIndex++, digest); + KMArray.cast(arrPtr).add(tagIndex++, rsaPubExpTag); + KMArray.cast(arrPtr).add(tagIndex++, KMEnumTag.instance(KMType.ALGORITHM, KMType.RSA)); + KMArray.cast(arrPtr).add(tagIndex++, padding); + tagIndex = setDefaultValidity(arrPtr, tagIndex); + + if (clientId != null) { + KMArray.cast(arrPtr).add(tagIndex++, + KMByteTag.instance(KMType.APPLICATION_ID, + KMByteBlob.instance(clientId, (short) 0, (short) clientId.length))); + } + if (appData != null) { + KMArray.cast(arrPtr).add(tagIndex++, + KMByteTag.instance(KMType.APPLICATION_DATA, + KMByteBlob.instance(appData, (short) 0, (short) appData.length))); + } + if (keyUsageLimitPtr != KMType.INVALID_VALUE) { + KMArray.cast(arrPtr).add(tagIndex++, KMIntegerTag + .instance(KMType.UINT_TAG, KMType.MAX_USES_PER_BOOT, keyUsageLimitPtr)); + } + short keyParams = KMKeyParameters.instance(arrPtr); + arrPtr = generateKeyNoAttestCmd(keyParams); + CommandAPDU apdu = KMTestUtils.encodeApdu(encoder, (byte) INS_GENERATE_KEY_CMD, arrPtr); + + ResponseAPDU response = simulator.transmitCommand(apdu); + KMTestUtils.print(response.getBytes(), (short) 0, (short) response.getBytes().length ); + return parseGenerateKeyResponse(response); + } + + private short generateRsaKey(byte[] clientId, byte[] appData) { + return generateRsaKey(clientId, appData, KMType.INVALID_VALUE); + } + + + public short generateEcKey(byte[] clientId, byte[] appData) { + short tagCount = 8; + if (clientId != null) { + tagCount++; + } + if (appData != null) { + tagCount++; + } + short arrPtr = KMArray.instance(tagCount); + short keySize = KMIntegerTag + .instance(KMType.UINT_TAG, KMType.KEYSIZE, KMInteger.uint_16((short) 256)); + short byteBlob = KMByteBlob.instance((short) 2); + KMByteBlob.cast(byteBlob).add((short) 0, KMType.DIGEST_NONE); + KMByteBlob.cast(byteBlob).add((short) 1, KMType.SHA2_256); + short digest = KMEnumArrayTag.instance(KMType.DIGEST, byteBlob); + byteBlob = KMByteBlob.instance((short) 2); + KMByteBlob.cast(byteBlob).add((short) 0, KMType.SIGN); + KMByteBlob.cast(byteBlob).add((short) 1, KMType.VERIFY); + short purpose = KMEnumArrayTag.instance(KMType.PURPOSE, byteBlob); + short boolTag = KMBoolTag.instance(KMType.NO_AUTH_REQUIRED); + short tagIndex = 0; + KMArray.cast(arrPtr).add(tagIndex++, purpose); + KMArray.cast(arrPtr).add(tagIndex++, boolTag); + KMArray.cast(arrPtr).add(tagIndex++, keySize); + KMArray.cast(arrPtr).add(tagIndex++, digest); + KMArray.cast(arrPtr).add(tagIndex++, KMEnumTag.instance(KMType.ECCURVE, KMType.P_256)); + KMArray.cast(arrPtr).add(tagIndex++, KMEnumTag.instance(KMType.ALGORITHM, KMType.EC)); + tagIndex = setDefaultValidity(arrPtr, tagIndex); + if (clientId != null) { + KMArray.cast(arrPtr).add(tagIndex++, + KMByteTag.instance(KMType.APPLICATION_ID, + KMByteBlob.instance(clientId, (short) 0, (short) clientId.length))); + } + if (appData != null) { + KMArray.cast(arrPtr).add(tagIndex++, + KMByteTag.instance(KMType.APPLICATION_DATA, + KMByteBlob.instance(appData, (short) 0, (short) appData.length))); + } + short keyParams = KMKeyParameters.instance(arrPtr); + arrPtr = generateKeyNoAttestCmd(keyParams); + CommandAPDU apdu = KMTestUtils.encodeApdu(encoder, (byte) INS_GENERATE_KEY_CMD, arrPtr); + // print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(apdu); + Assert.assertEquals(0x9000, response.getSW()); + return parseGenerateKeyResponse(response); + } + + + public short setDefaultValidity(short arrPtr, short index) { + byte[] undefinedExpirationTime = {0x00, 0x00, (byte) 0xE6, 0x77, (byte) 0xD2, 0x1F, (byte) 0xD8, + 0x18}; + short notBefore = KMInteger.uint_8((byte) 0); + short notBeforeTag = KMIntegerTag.instance(KMType.DATE_TAG, KMType.CERTIFICATE_NOT_BEFORE, + notBefore); + short notAfter = KMInteger.instance(undefinedExpirationTime, (short) 0, + (short) undefinedExpirationTime.length); + short notAfterTag = KMIntegerTag.instance(KMType.DATE_TAG, KMType.CERTIFICATE_NOT_AFTER, + notAfter); + KMArray.cast(arrPtr).add(index++, notBeforeTag); + KMArray.cast(arrPtr).add(index++, notAfterTag); + return index; + } + + private short extractKeyBlobArray(byte[] buf, short off, short buflen) { + short byteBlobExp = KMByteBlob.exp(); + short keyChar = KMKeyCharacteristics.exp(); + short keyParam = KMKeyParameters.exp(); + short keyBlob = KMArray.instance(KMKeymasterApplet.ASYM_KEY_BLOB_SIZE_V2_V3); + KMArray.cast(keyBlob).add(KMKeymasterApplet.KEY_BLOB_VERSION_OFFSET, KMInteger.exp()); + KMArray.cast(keyBlob).add(KMKeymasterApplet.KEY_BLOB_SECRET, byteBlobExp); + KMArray.cast(keyBlob).add(KMKeymasterApplet.KEY_BLOB_AUTH_TAG, byteBlobExp); + KMArray.cast(keyBlob).add(KMKeymasterApplet.KEY_BLOB_NONCE, byteBlobExp); + KMArray.cast(keyBlob).add(KMKeymasterApplet.KEY_BLOB_PARAMS, keyChar); + KMArray.cast(keyBlob).add(KMKeymasterApplet.KEY_BLOB_CUSTOM_TAGS, keyParam); + KMArray.cast(keyBlob).add(KMKeymasterApplet.KEY_BLOB_PUB_KEY, byteBlobExp); + keyBlob = decoder.decodeArray(keyBlob, buf, off, buflen); + return keyBlob; + } + + + public short generateHmacKey(byte[] clientId, byte[] appData) { + short tagCount = 8; + if (clientId != null) { + tagCount++; + } + if (appData != null) { + tagCount++; + } + short arrPtr = KMArray.instance(tagCount); + short keySize = KMIntegerTag + .instance(KMType.UINT_TAG, KMType.KEYSIZE, KMInteger.uint_16((short) 128)); + short byteBlob = KMByteBlob.instance((short) 1); + KMByteBlob.cast(byteBlob).add((short) 0, KMType.SHA2_256); + short digest = KMEnumArrayTag.instance(KMType.DIGEST, byteBlob); + byteBlob = KMByteBlob.instance((short) 2); + KMByteBlob.cast(byteBlob).add((short) 0, KMType.SIGN); + KMByteBlob.cast(byteBlob).add((short) 1, KMType.VERIFY); + short purpose = KMEnumArrayTag.instance(KMType.PURPOSE, byteBlob); + short boolTag = KMBoolTag.instance(KMType.NO_AUTH_REQUIRED); + short minMacLen = KMIntegerTag + .instance(KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, KMInteger.uint_16((short)/*256*/160)); + short tagIndex = 0; + KMArray.cast(arrPtr).add(tagIndex++, minMacLen); + KMArray.cast(arrPtr).add(tagIndex++, purpose); + KMArray.cast(arrPtr).add(tagIndex++, boolTag); + KMArray.cast(arrPtr).add(tagIndex++, keySize); + KMArray.cast(arrPtr).add(tagIndex++, digest); + KMArray.cast(arrPtr).add(tagIndex++, KMEnumTag.instance(KMType.ALGORITHM, KMType.HMAC)); + tagIndex = setDefaultValidity(arrPtr, tagIndex); + if (clientId != null) { + KMArray.cast(arrPtr).add(tagIndex++, + KMByteTag.instance(KMType.APPLICATION_ID, + KMByteBlob.instance(clientId, (short) 0, (short) clientId.length))); + } + if (appData != null) { + KMArray.cast(arrPtr).add(tagIndex++, + KMByteTag.instance(KMType.APPLICATION_DATA, + KMByteBlob.instance(appData, (short) 0, (short) appData.length))); + } + short keyParams = KMKeyParameters.instance(arrPtr); + arrPtr = generateKeyNoAttestCmd(keyParams); + CommandAPDU apdu = KMTestUtils.encodeApdu(encoder, (byte) INS_GENERATE_KEY_CMD, arrPtr); + // print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(apdu); + return parseGenerateKeyResponse(response); + } + + private short extractKeyBlobArray(short keyBlob) { + return extractKeyBlobArray(KMByteBlob.cast(keyBlob).getBuffer(), KMByteBlob + .cast(keyBlob).getStartOff(), KMByteBlob.cast(keyBlob).length()); + } + + public short importKeyNoAttestCmd(short keyParams, short keyFormat, short keyBuffer) { + short emptyBlob = KMByteBlob.instance((short) 0); + short arrPtr = KMArray.instance((short) 6); + KMArray arg = KMArray.cast(arrPtr); + arg.add((short) 0, keyParams); + arg.add((short) 1, keyFormat); + arg.add((short) 2, keyBuffer); + arg.add((short) 3, emptyBlob); + arg.add((short) 4, KMTestUtils.getEmptyKeyParams()); + arg.add((short) 5, emptyBlob); + return arrPtr; + } + + public short generateKeyNoAttestCmd(short keyParams) { + short emptyBlob = KMByteBlob.instance((short) 0); + short arrPtr = KMArray.instance((short) 4); + KMArray arg = KMArray.cast(arrPtr); + arg.add((short) 0, keyParams); + arg.add((short) 1, emptyBlob); + arg.add((short) 2, KMTestUtils.getEmptyKeyParams()); + arg.add((short) 3, emptyBlob); + return arrPtr; + } + + + public short begin(byte keyPurpose, short keyBlob, short keyParmas, short hwToken, + boolean triggerReset) { + short arrPtr = KMArray.instance((short) 4); + KMArray.cast(arrPtr).add((short) 0, KMEnum.instance(KMType.PURPOSE, keyPurpose)); + KMArray.cast(arrPtr).add((short) 1, keyBlob); + KMArray.cast(arrPtr).add((short) 2, keyParmas); + if (hwToken == 0) { + hwToken = KMHardwareAuthToken.instance(); + } + KMArray.cast(arrPtr).add((short) 3, hwToken); + CommandAPDU apdu = KMTestUtils.encodeApdu(encoder, (byte) INS_BEGIN_OPERATION_CMD, arrPtr); + KMTestUtils.print(apdu.getBytes(), (short) 0, (short) apdu.getBytes().length); + if (triggerReset) { + resetAndSelect(); + } + ResponseAPDU response = simulator.transmitCommand(apdu); + byte[] respBuf = response.getBytes(); + short arrLen = + KMTestUtils.readMajorTypeWithPayloadLength(respBuf, + (short) (KMTestUtils.CBOR_ARRAY_MAJOR_TYPE & 0x00FF)); + short ret; + if (arrLen == 5) { + ret = KMArray.instance((short) 5); + short outParams = KMKeyParameters.exp(); + KMArray.cast(ret).add((short) 0, KMInteger.exp()); + KMArray.cast(ret).add((short) 1, outParams); + KMArray.cast(ret).add((short) 2, KMInteger.exp());// opHandle + KMArray.cast(ret).add((short) 3, KMInteger.exp());// Buf Mode + KMArray.cast(ret).add((short) 4, KMInteger.exp());// MacLength + + ret = decoder.decode(ret, respBuf, (short) 0, (short) respBuf.length); + short error = KMInteger.cast(KMArray.cast(ret).get((short) 0)).getShort(); + Assert.assertEquals(error, KMError.OK); + } else { + ret = KMArray.instance((short) 1); + KMArray.cast(ret).add((short) 0, KMInteger.exp()); + ret = decoder.decode(ret, respBuf, (short) 0, (short) respBuf.length); + } + return ret; + } + + public void updateAad(short operationHandle, short data, short hwToken, short verToken) { + if (hwToken == 0) { + hwToken = KMHardwareAuthToken.instance(); + } + if (verToken == 0) { + verToken = KMVerificationToken.instance(); + } + short arrPtr = KMArray.instance((short) 4); + KMArray.cast(arrPtr).add((short) 0, operationHandle); + KMArray.cast(arrPtr).add((short) 1, data); + KMArray.cast(arrPtr).add((short) 2, hwToken); + KMArray.cast(arrPtr).add((short) 3, verToken); + CommandAPDU apdu = KMTestUtils.encodeApdu(encoder, (byte) INS_UPDATE_AAD_OPERATION_CMD, arrPtr); + ResponseAPDU response = simulator.transmitCommand(apdu); + Assert.assertEquals(KMError.OK, KMTestUtils.decodeError(decoder, response)); + } + + public short update(short operationHandle, short data, short hwToken, + short verToken, boolean triggerReset) { + if (hwToken == 0) { + hwToken = KMHardwareAuthToken.instance(); + } + if (verToken == 0) { + verToken = KMVerificationToken.instance(); + } + short arrPtr = KMArray.instance((short) 4); + KMArray.cast(arrPtr).add((short) 0, operationHandle); + KMArray.cast(arrPtr).add((short) 1, data); + KMArray.cast(arrPtr).add((short) 2, hwToken); + KMArray.cast(arrPtr).add((short) 3, verToken); + CommandAPDU apdu = KMTestUtils.encodeApdu(encoder, (byte) INS_UPDATE_OPERATION_CMD, arrPtr); + if (triggerReset) { + resetAndSelect(); + } + ResponseAPDU response = simulator.transmitCommand(apdu); + Assert.assertEquals(0x9000, response.getSW()); + byte[] respBuf = response.getBytes(); + short arrLen = KMTestUtils.readMajorTypeWithPayloadLength(respBuf, + (short) (KMTestUtils.CBOR_ARRAY_MAJOR_TYPE & 0x00FF)); + short ret; + if (arrLen == 2) { + ret = KMArray.instance((short) 2); + KMArray.cast(ret).add((short) 0, KMInteger.exp()); + KMArray.cast(ret).add((short) 1, KMByteBlob.exp()); + ret = decoder.decode(ret, respBuf, (short) 0, (short) respBuf.length); + Assert.assertEquals(KMError.OK, KMInteger.cast(KMArray.cast(ret).get((short) 0)).getShort()); + } else { + ret = KMArray.instance((short) 1); + KMArray.cast(ret).add((short) 0, KMInteger.exp()); + ret = decoder.decode(ret, respBuf, (short) 0, (short) respBuf.length); + } + return ret; + } + + public short finish(short operationHandle, short data, byte[] signature, short inParams, + short hwToken, short verToken, short confToken, short expectedErr, boolean triggerReset) { + if (hwToken == 0) { + hwToken = KMHardwareAuthToken.instance(); + } + if (verToken == 0) { + verToken = KMVerificationToken.instance(); + } + if (confToken == 0) { + confToken = KMByteBlob.instance((short) 0); + } + short signatureTag; + if (signature == null) { + signatureTag = KMByteBlob.instance((short) 0); + } else { + signatureTag = KMByteBlob.instance(signature, (short) 0, (short) signature.length); + } + if (inParams == 0) { + short arr = KMArray.instance((short) 0); + inParams = KMKeyParameters.instance(arr); + } + short arrPtr = KMArray.instance((short) 6); + KMArray.cast(arrPtr).add((short) 0, operationHandle); + KMArray.cast(arrPtr).add((short) 1, data); + KMArray.cast(arrPtr).add((short) 2, signatureTag); + KMArray.cast(arrPtr).add((short) 3, hwToken); + KMArray.cast(arrPtr).add((short) 4, verToken); + KMArray.cast(arrPtr).add((short) 5, confToken); + CommandAPDU apdu = KMTestUtils.encodeApdu(encoder, (byte) INS_FINISH_OPERATION_CMD, arrPtr); + // print(commandAPDU.getBytes()); + if (triggerReset) { + resetAndSelect(); + } + ResponseAPDU response = simulator.transmitCommand(apdu); + Assert.assertEquals(0x9000, response.getSW()); + byte[] respBuf = response.getBytes(); + short arrLen = KMTestUtils.readMajorTypeWithPayloadLength(respBuf, + (short) (KMTestUtils.CBOR_ARRAY_MAJOR_TYPE & 0x00FF)); + short ret; + short error; + if (arrLen == 2) { + ret = KMArray.instance((short) 2); + KMArray.cast(ret).add((short) 0, KMInteger.exp()); + KMArray.cast(ret).add((short) 1, KMByteBlob.exp()); + } else { + ret = KMArray.instance((short) 1); + KMArray.cast(ret).add((short) 0, KMInteger.exp()); + } + ret = decoder.decode(ret, respBuf, (short) 0, (short) respBuf.length); + error = KMInteger.cast(KMArray.cast(ret).get((short) 0)).getShort(); + Assert.assertEquals(error, expectedErr); + return ret; + } + + public short processMessage( + byte[] data, + short keyBlob, + byte keyPurpose, + short inParams, + short hwToken, + byte[] signature, + boolean updateFlag, + boolean aesGcmFlag) { + short beginResp = begin(keyPurpose, keyBlob, inParams, hwToken, false); + short opHandle = KMArray.cast(beginResp).get((short) 2); + byte[] opHandleBuf = new byte[KMOperationState.OPERATION_HANDLE_SIZE]; + KMInteger.cast(opHandle).getValue(opHandleBuf, (short) 0, (short) opHandleBuf.length); + short dataPtr = KMByteBlob.instance(data, (short) 0, (short) data.length); + short ret = KMType.INVALID_VALUE; + byte[] outputData = new byte[128]; + short len = 0; + inParams = 0; + //Test + short firstDataLen = 16; + if (keyPurpose == KMType.DECRYPT) { + firstDataLen = 32; + } + + //Test + + if (updateFlag) { + if (aesGcmFlag) { + byte[] authData = "AuthData".getBytes(); + short associatedData = KMByteBlob.instance(authData, (short) 0, (short) authData.length); + updateAad(opHandle, associatedData, (short) 0, (short) 0); + } + dataPtr = KMByteBlob.instance(data, (short) 0, (short) /*16*/firstDataLen); + opHandle = KMInteger.uint_64(opHandleBuf, (short) 0); + ret = update(opHandle, dataPtr, (short) 0, (short) 0, false); + dataPtr = KMArray.cast(ret).get((short) 1); + if (KMByteBlob.cast(dataPtr).length() > 0) { + Util.arrayCopyNonAtomic( + KMByteBlob.cast(dataPtr).getBuffer(), + KMByteBlob.cast(dataPtr).getStartOff(), + outputData, + (short) 0, + KMByteBlob.cast(dataPtr).length()); + len = KMByteBlob.cast(dataPtr).length(); + dataPtr = KMByteBlob.instance(data, len, (short) (data.length - len)); + } else { + dataPtr = KMByteBlob + .instance(data, (short)/*16*/firstDataLen, (short) (data.length - /*16*/firstDataLen)); + } + } + + opHandle = KMInteger.uint_64(opHandleBuf, (short) 0); + if (keyPurpose == KMType.VERIFY) { + ret = finish(opHandle, dataPtr, signature, (short) 0, (short) 0, (short) 0, (short) 0, + KMError.OK, + false); + } else { + ret = finish(opHandle, dataPtr, null, (short) 0, (short) 0, (short) 0, (short) 0, KMError.OK, + false); + } + if (len > 0) { + dataPtr = KMArray.cast(ret).get((short) 1); + if (KMByteBlob.cast(dataPtr).length() > 0) { + Util.arrayCopyNonAtomic( + KMByteBlob.cast(dataPtr).getBuffer(), + KMByteBlob.cast(dataPtr).getStartOff(), + outputData, + len, + KMByteBlob.cast(dataPtr).length()); + len = (short) (len + KMByteBlob.cast(dataPtr).length()); + } + KMArray.cast(ret).add((short) 1, KMByteBlob.instance(outputData, (short) 0, len)); + } + return ret; + } + + private short getAesDesParams(byte alg, byte blockMode, byte padding, byte[] nonce) { + short inParams; + if (blockMode == KMType.GCM) { + inParams = KMArray.instance((short) 4); + short byteBlob = KMByteBlob.instance((short) 1); + KMByteBlob.cast(byteBlob).add((short) 0, blockMode); + KMArray.cast(inParams).add((short) 0, KMEnumArrayTag.instance(KMType.BLOCK_MODE, byteBlob)); + byteBlob = KMByteBlob.instance((short) 1); + KMByteBlob.cast(byteBlob).add((short) 0, padding); + KMArray.cast(inParams).add((short) 1, KMEnumArrayTag.instance(KMType.PADDING, byteBlob)); + short nonceLen = 12; + byteBlob = KMByteBlob.instance(nonce, (short) 0, nonceLen); + KMArray.cast(inParams).add((short) 2, KMByteTag.instance(KMType.NONCE, byteBlob)); + short macLen = KMInteger.uint_16((short) 128); + macLen = KMIntegerTag.instance(KMType.UINT_TAG, KMType.MAC_LENGTH, macLen); + KMArray.cast(inParams).add((short) 3, macLen); + } else if (blockMode == KMType.ECB) { + inParams = KMArray.instance((short) 2); + short byteBlob = KMByteBlob.instance((short) 1); + KMByteBlob.cast(byteBlob).add((short) 0, blockMode); + KMArray.cast(inParams).add((short) 0, KMEnumArrayTag.instance(KMType.BLOCK_MODE, byteBlob)); + byteBlob = KMByteBlob.instance((short) 1); + KMByteBlob.cast(byteBlob).add((short) 0, padding); + KMArray.cast(inParams).add((short) 1, KMEnumArrayTag.instance(KMType.PADDING, byteBlob)); + } else { + inParams = KMArray.instance((short) 3); + short byteBlob = KMByteBlob.instance((short) 1); + KMByteBlob.cast(byteBlob).add((short) 0, blockMode); + KMArray.cast(inParams).add((short) 0, KMEnumArrayTag.instance(KMType.BLOCK_MODE, byteBlob)); + byteBlob = KMByteBlob.instance((short) 1); + KMByteBlob.cast(byteBlob).add((short) 0, padding); + KMArray.cast(inParams).add((short) 1, KMEnumArrayTag.instance(KMType.PADDING, byteBlob)); + short nonceLen = 16; + if (alg == KMType.DES) { + nonceLen = 8; + } + byteBlob = KMByteBlob.instance(nonce, (short) 0, nonceLen); + KMArray.cast(inParams).add((short) 2, KMByteTag.instance(KMType.NONCE, byteBlob)); + } + return inParams; + } + + private void deviceLock(short verToken, short expectedError) { + short req = KMArray.instance((short) 2); + KMArray.cast(req).add((short) 0, KMInteger.uint_8((byte) 1)); + KMArray.cast(req).add((short) 1, verToken); + CommandAPDU apdu = KMTestUtils.encodeApdu(encoder, (byte) INS_DEVICE_LOCKED_CMD, req); + ResponseAPDU response = simulator.transmitCommand(apdu); + Assert.assertEquals(0x9000, response.getSW()); + byte[] respBuf = response.getBytes(); + Assert.assertEquals(expectedError, KMTestUtils.decodeError(decoder, response)); + } + + private short signVerificationToken(short verToken, byte machineType) { + byte[] scratchPad = new byte[256]; + byte[] authVerification = { + 0x41, 0x75, 0x74, 0x68, 0x20, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, + 0x6F, + 0x6E + }; + // concatenation length will be 37 + length of verified parameters list - which + // is typically empty + Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 256, (byte) 0); + // Add "Auth Verification" - 17 bytes. + Util.arrayCopyNonAtomic(authVerification, (short) 0, scratchPad, (short) 0, + (short) authVerification.length); + short len = (short) authVerification.length; + // concatenate challenge - 8 bytes + short ptr = KMVerificationToken.cast(verToken).getChallenge(); + KMInteger.cast(ptr) + .value(scratchPad, + (short) (len + (short) (KMInteger.UINT_64 - KMInteger.cast(ptr).length()))); + len += KMInteger.UINT_64; + // concatenate timestamp -8 bytes + ptr = KMVerificationToken.cast(verToken).getTimestamp(); + KMInteger.cast(ptr) + .value(scratchPad, + (short) (len + (short) (KMInteger.UINT_64 - KMInteger.cast(ptr).length()))); + len += KMInteger.UINT_64; + // concatenate security level - 4 bytes + scratchPad[(short) (len + 3)] = 1; // TRUSTED_ENVIRONMENT + len += KMInteger.UINT_32; + // hmac the data + short signLen = cryptoProvider.hmacSign(KMKeymintDataStore.instance().getComputedHmacKey(), + scratchPad, (short) 0, len, scratchPad, len); + KMVerificationToken.cast(verToken).setMac(KMByteBlob.instance(scratchPad, len, signLen)); + return verToken; + } + + private short getRsaParams(byte digest, byte padding) { + short inParams = KMArray.instance((short) 2); + short byteBlob = KMByteBlob.instance((short) 1); + KMByteBlob.cast(byteBlob).add((short) 0, digest); + KMArray.cast(inParams).add((short) 0, KMEnumArrayTag.instance(KMType.DIGEST, byteBlob)); + byteBlob = KMByteBlob.instance((short) 1); + KMByteBlob.cast(byteBlob).add((short) 0, padding); + KMArray.cast(inParams).add((short) 1, KMEnumArrayTag.instance(KMType.PADDING, byteBlob)); + return inParams; + } + + private short getEcParams(byte digest) { + short inParams = KMArray.instance((short) 1); + short byteBlob = KMByteBlob.instance((short) 1); + KMByteBlob.cast(byteBlob).add((short) 0, digest); + KMArray.cast(inParams).add((short) 0, KMEnumArrayTag.instance(KMType.DIGEST, byteBlob)); + return inParams; + } + + public ResponseAPDU importWrappedKey() { + byte[] wrappedKey = new byte[16]; + cryptoProvider.newRandomNumber(wrappedKey, (short) 0, (short) 16); + byte[] encWrappedKey = new byte[16]; + byte[] transportKeyMaterial = new byte[32]; + cryptoProvider.newRandomNumber(transportKeyMaterial, (short) 0, (short) 32); + byte[] nonce = new byte[12]; + cryptoProvider.newRandomNumber(nonce, (short) 0, (short) 12); + byte[] authData = "Auth Data".getBytes(); + byte[] authTag = new byte[16]; + cryptoProvider.aesGCMEncrypt(transportKeyMaterial, (short) 0, (short) 32, wrappedKey, + (short) 0, (short) 16, encWrappedKey, (short) 0, + nonce, (short) 0, (short) 12, authData, (short) 0, (short) authData.length, + authTag, (short) 0, (short) 16); + byte[] maskingKey = {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, + 0, 1, 0, 1, 0, 1, 0}; + byte[] maskedTransportKey = new byte[32]; + for (int i = 0; i < maskingKey.length; i++) { + maskedTransportKey[i] = (byte) (transportKeyMaterial[i] ^ maskingKey[i]); + } + short rsaKeyArr = generateRsaKey(null, null); + short keyBlobPtr = KMArray.cast(rsaKeyArr).get((short) 1); + byte[] wrappingKeyBlob = new byte[KMByteBlob.cast(keyBlobPtr).length()]; + Util.arrayCopyNonAtomic(KMByteBlob.cast(keyBlobPtr).getBuffer(), + KMByteBlob.cast(keyBlobPtr).getStartOff(), + wrappingKeyBlob, (short) 0, (short) wrappingKeyBlob.length); + + byte[] output = new byte[256]; + short outlen = rsaOaepEncryptMessage(wrappingKeyBlob, KMType.SHA2_256, + maskedTransportKey, (short) 0, (short) maskedTransportKey.length, + output, (short) 0); + Assert.assertTrue((outlen == 256)); + byte[] encTransportKey = new byte[outlen]; + Util.arrayCopyNonAtomic(output, (short) 0, encTransportKey, (short) 0, + outlen); + // Begn Import wrapped key. + short nullParams = KMArray.instance((short) 0); + nullParams = KMKeyParameters.instance(nullParams); + short arr = KMArray.instance((short) 4); + KMArray.cast(arr).add((short) 0, KMByteBlob.instance(encTransportKey, (short) 0, + (short) encTransportKey.length)); // Encrypted Transport Key + KMArray.cast(arr).add((short) 1, KMByteBlob.instance(wrappingKeyBlob, (short) 0, + (short) wrappingKeyBlob.length)); // Wrapping Key KeyBlob + KMArray.cast(arr).add((short) 2, KMByteBlob.instance(maskingKey, (short) 0, + (short) maskingKey.length)); // Masking Key + KMArray.cast(arr).add((short) 3, nullParams); // unwrapping params + CommandAPDU apdu = KMTestUtils.encodeApdu(encoder, (byte) INS_BEGIN_IMPORT_WRAPPED_KEY_CMD, + arr); + ResponseAPDU response = simulator.transmitCommand(apdu); + Assert.assertEquals(0x9000, response.getSW()); + byte[] resBuf = response.getBytes(); + short resArr = KMArray.instance((short) 1); + KMArray.cast(resArr).add((short) 0, KMInteger.exp()); + arr = decoder.decode(resArr, resBuf, (short) 0, (short) resBuf.length); + Assert.assertEquals(KMError.OK, KMInteger.cast(KMArray.cast(arr).get((short) 0)).getShort()); + + // Finish import wrapped key. + short tagCount = 7; + short arrPtr = KMArray.instance(tagCount); + short boolTag = KMBoolTag.instance(KMType.NO_AUTH_REQUIRED); + short keySize = KMIntegerTag + .instance(KMType.UINT_TAG, KMType.KEYSIZE, KMInteger.uint_16((short) 128)); + short byteBlob = KMByteBlob.instance((short) 2); + KMByteBlob.cast(byteBlob).add((short) 0, KMType.ECB); + KMByteBlob.cast(byteBlob).add((short) 1, KMType.CBC); + short blockModeTag = KMEnumArrayTag.instance(KMType.BLOCK_MODE, byteBlob); + byteBlob = KMByteBlob.instance((short) 2); + KMByteBlob.cast(byteBlob).add((short) 0, KMType.PKCS7); + KMByteBlob.cast(byteBlob).add((short) 1, KMType.PADDING_NONE); + short paddingMode = KMEnumArrayTag.instance(KMType.PADDING, byteBlob); + byteBlob = KMByteBlob.instance((short) 2); + KMByteBlob.cast(byteBlob).add((short) 0, KMType.ENCRYPT); + KMByteBlob.cast(byteBlob).add((short) 1, KMType.DECRYPT); + short purpose = KMEnumArrayTag.instance(KMType.PURPOSE, byteBlob); + short tagIndex = 0; + KMArray.cast(arrPtr).add(tagIndex++, boolTag); + KMArray.cast(arrPtr).add(tagIndex++, keySize); + KMArray.cast(arrPtr).add(tagIndex++, blockModeTag); + KMArray.cast(arrPtr).add(tagIndex++, paddingMode); + KMArray.cast(arrPtr).add(tagIndex++, KMEnumTag.instance(KMType.ALGORITHM, KMType.AES)); + KMArray.cast(arrPtr).add(tagIndex++, purpose); + KMArray.cast(arrPtr).add(tagIndex++, KMBoolTag.instance(KMType.CALLER_NONCE)); + short keyParams = KMKeyParameters.instance(arrPtr); + arr = KMArray.instance((short) 8); + KMArray.cast(arr).add((short) 0, keyParams); // Key Params of wrapped key + KMArray.cast(arr).add((short) 1, KMEnum.instance(KMType.KEY_FORMAT, KMType.RAW)); // Key Format + KMArray.cast(arr).add((short) 2, KMByteBlob.instance(encWrappedKey, (short) 0, + (short) encWrappedKey.length)); // Wrapped Import Key Blob + KMArray.cast(arr).add((short) 3, + KMByteBlob.instance(authTag, (short) 0, (short) authTag.length)); // Auth Tag + KMArray.cast(arr) + .add((short) 4, KMByteBlob.instance(nonce, (short) 0, (short) nonce.length)); // IV - Nonce + KMArray.cast(arr).add((short) 5, KMByteBlob.instance(authData, (short) 0, + (short) authData.length)); // Wrapped Key ASSOCIATED AUTH DATA + KMArray.cast(arr).add((short) 6, KMInteger.uint_8((byte) 0)); // Password Sid + KMArray.cast(arr).add((short) 7, KMInteger.uint_8((byte) 0)); // Biometric Sid + apdu = KMTestUtils.encodeApdu(encoder, (byte) INS_FINISH_IMPORT_WRAPPED_KEY_CMD, + arr); + response = simulator.transmitCommand(apdu); + Assert.assertEquals(0x9000, response.getSW()); + return response; + } + + public short rsaOaepEncryptMessage(byte[] keyBlob, short digest, byte[] input, short inputOff, + short inputlen, + byte[] output, short outputOff) { + byte[] mod = new byte[256]; + if (0 == KMTestUtils.getPublicKey(decoder, keyBlob, (short) 0, (short) keyBlob.length, mod, + (short) 0)) { + return 0; + } + byte[] exponent = new byte[]{0x01, 0x00, 0x01}; + + // Convert byte arrays into keys + String modString = KMTestUtils.toHexString(mod); + String expString = KMTestUtils.toHexString(exponent); + BigInteger modInt = new BigInteger(modString, 16); + BigInteger expInt = new BigInteger(expString, 16); + javax.crypto.Cipher rsaCipher = null; + try { + KeyFactory kf = KeyFactory.getInstance("RSA"); + // Create cipher with oaep padding + OAEPParameterSpec oaepSpec = null; + if (digest == KMType.SHA2_256) { + oaepSpec = new OAEPParameterSpec("SHA-256", "MGF1", + MGF1ParameterSpec.SHA1, PSource.PSpecified.DEFAULT); + } else { + oaepSpec = new OAEPParameterSpec("SHA1", "MGF1", + MGF1ParameterSpec.SHA1, PSource.PSpecified.DEFAULT); + } + rsaCipher = javax.crypto.Cipher.getInstance("RSA/ECB/OAEPPadding", "SunJCE"); + + RSAPublicKeySpec pubSpec = new RSAPublicKeySpec(modInt, expInt); + java.security.interfaces.RSAPublicKey pubKey = (java.security.interfaces.RSAPublicKey) kf + .generatePublic(pubSpec); + rsaCipher.init(javax.crypto.Cipher.ENCRYPT_MODE, pubKey, oaepSpec); + byte[] cipherOut = rsaCipher.doFinal(input, inputOff, inputlen); + + if (cipherOut != null) { + Util.arrayCopyNonAtomic(cipherOut, (short) 0, output, outputOff, (short) cipherOut.length); + } + return (short) cipherOut.length; + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + } catch (InvalidKeySpecException e) { + e.printStackTrace(); + } catch (InvalidKeyException e) { + e.printStackTrace(); + } catch (InvalidAlgorithmParameterException e) { + e.printStackTrace(); + } catch (NoSuchPaddingException e) { + e.printStackTrace(); + } catch (NoSuchProviderException e) { + e.printStackTrace(); + } catch (IllegalBlockSizeException e) { + e.printStackTrace(); + } catch (BadPaddingException e) { + e.printStackTrace(); + } + return 0; + } + + public short getKeyCharacteristics(short keyBlob) { + short arrPtr = KMArray.instance((short) 3); + KMArray.cast(arrPtr).add((short) 0, keyBlob); + KMArray.cast(arrPtr).add((short) 1, KMByteBlob.instance((short) 0)); + KMArray.cast(arrPtr).add((short) 2, KMByteBlob.instance((short) 0)); + CommandAPDU apdu = KMTestUtils.encodeApdu(encoder, (byte) INS_GET_KEY_CHARACTERISTICS_CMD, + arrPtr); + // print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(apdu); + short ret = KMArray.instance((short) 2); + KMArray.cast(ret).add((short) 0, KMInteger.exp()); + short inst = KMKeyCharacteristics.exp(); + KMArray.cast(ret).add((short) 1, inst); + byte[] respBuf = response.getBytes(); + short len = (short) respBuf.length; + if (len > 5) { + ret = decoder.decode(ret, respBuf, (short) 0, len); + } else { + ret = KMByteBlob.instance(respBuf, (short) 0, len); + } + return ret; + } + + private void deleteKey(short keyBlob) { + short arrPtr = KMArray.instance((short) 1); + KMArray.cast(arrPtr).add((short) 0, keyBlob); + CommandAPDU apdu = KMTestUtils.encodeApdu(encoder, INS_DELETE_KEY_CMD, arrPtr); + // print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(apdu); + Assert.assertEquals(KMError.OK, KMTestUtils.decodeError(decoder, response)); + } + + public void testEncryptDecryptWithAesDes(byte alg, byte blockMode, byte padding, boolean update) { + short aesDesKeyArr; + boolean aesGcmFlag = false; + if (alg == KMType.AES) { + if (blockMode == KMType.GCM) { + aesDesKeyArr = generateAesGcmKey((short) 128, null, null); + aesGcmFlag = true; + } else { + aesDesKeyArr = generateAesDesKey(alg, (short) 128, null, null, false); + } + } else { + aesDesKeyArr = generateAesDesKey(alg, (short) 168, null, null, false); + } + short keyBlobPtr = KMArray.cast(aesDesKeyArr).get((short) 1); + byte[] keyBlob = new byte[KMByteBlob.cast(keyBlobPtr).length()]; + Util.arrayCopyNonAtomic(KMByteBlob.cast(keyBlobPtr).getBuffer(), + KMByteBlob.cast(keyBlobPtr).getStartOff(), + keyBlob, (short) 0, (short) keyBlob.length); + byte[] nonce = new byte[16]; + cryptoProvider.newRandomNumber(nonce, (short) 0, (short) 16); + short inParams = getAesDesParams(alg, blockMode, padding, nonce); + byte[] plainData = "Hello World 123!".getBytes(); + if (update) { + plainData = "Hello World 123! Hip Hip Hoorah!".getBytes(); + } + //Encrypt + short ret = processMessage(plainData, + KMByteBlob.instance(keyBlob, (short) 0, (short) keyBlob.length), + KMType.ENCRYPT, + KMKeyParameters.instance(inParams), + (short) 0, null, update, aesGcmFlag + ); + inParams = getAesDesParams(alg, blockMode, padding, nonce); + keyBlobPtr = KMArray.cast(ret).get((short) 1); + //print(keyBlobPtr); + byte[] cipherData = new byte[KMByteBlob.cast(keyBlobPtr).length()]; + Util.arrayCopyNonAtomic(KMByteBlob.cast(keyBlobPtr).getBuffer(), + KMByteBlob.cast(keyBlobPtr).getStartOff(), + cipherData, (short) 0, (short) cipherData.length); + ret = processMessage(cipherData, + KMByteBlob.instance(keyBlob, (short) 0, (short) keyBlob.length), + KMType.DECRYPT, + KMKeyParameters.instance(inParams), + (short) 0, null, update, aesGcmFlag + ); + keyBlobPtr = KMArray.cast(ret).get((short) 1); + //print(plainData,(short)0,(short)plainData.length); + //print(keyBlobPtr); + short equal = Util.arrayCompare(plainData, (short) 0, KMByteBlob.cast(keyBlobPtr).getBuffer(), + KMByteBlob.cast(keyBlobPtr).getStartOff(), (short) plainData.length); + Assert.assertTrue(equal == 0); + } + + public void testEncryptDecryptWithRsa(byte digest, byte padding) { + short rsaKeyArr = generateRsaKey(null, null); + short keyBlobPtr = KMArray.cast(rsaKeyArr).get((short) 1); + byte[] keyBlob = new byte[KMByteBlob.cast(keyBlobPtr).length()]; + Util.arrayCopyNonAtomic(KMByteBlob.cast(keyBlobPtr).getBuffer(), + KMByteBlob.cast(keyBlobPtr).getStartOff(), + keyBlob, (short) 0, (short) keyBlob.length); + byte[] plainData = "Hello World 123!".getBytes(); + byte[] cipherData = new byte[256]; + short cipherDataLen = 0; + //Encrypt + if (padding == KMType.RSA_OAEP) { + cipherDataLen = rsaOaepEncryptMessage(keyBlob, digest, plainData, + (short) 0, (short) plainData.length, cipherData, (short) 0); + } else { + cipherDataLen = rsaEncryptMessage(keyBlob, padding, digest, plainData, + (short) 0, (short) plainData.length, cipherData, (short) 0); + } + Assert.assertTrue((cipherDataLen == 256)); + short inParams = getRsaParams(digest, padding); + short ret = processMessage(cipherData, + KMByteBlob.instance(keyBlob, (short) 0, (short) keyBlob.length), + KMType.DECRYPT, + KMKeyParameters.instance(inParams), + (short) 0, null, false, false + ); + keyBlobPtr = KMArray.cast(ret).get((short) 1); + short len = KMByteBlob.cast(keyBlobPtr).length(); + short start = KMByteBlob.cast(keyBlobPtr).getStartOff(); + short equal = Util.arrayCompare(plainData, (short) 0, KMByteBlob.cast(keyBlobPtr).getBuffer(), + (short) (start + len - plainData.length), (short) plainData.length); + Assert.assertTrue(equal == 0); + } + + public void testSignVerifyWithRsa(byte digest, byte padding, boolean update, boolean verifyFlag) { + short rsaKeyArr = generateRsaKey(null, null); + short keyBlobPtr = KMArray.cast(rsaKeyArr).get((short) 1); + byte[] keyBlob = new byte[KMByteBlob.cast(keyBlobPtr).length()]; + Util.arrayCopyNonAtomic(KMByteBlob.cast(keyBlobPtr).getBuffer(), + KMByteBlob.cast(keyBlobPtr).getStartOff(), + keyBlob, (short) 0, (short) keyBlob.length); + short inParams = getRsaParams(digest, padding); + byte[] plainData = "Hello World 123!".getBytes(); + if (update) { + plainData = "Hello World 123! Hip Hip Hoorah!".getBytes(); + } + //Sign + short ret = processMessage(plainData, + KMByteBlob.instance(keyBlob, (short) 0, (short) keyBlob.length), + KMType.SIGN, + KMKeyParameters.instance(inParams), + (short) 0, null, update, false + ); + inParams = getRsaParams(digest, padding); + keyBlobPtr = KMArray.cast(ret).get((short) 1); + byte[] signatureData = new byte[KMByteBlob.cast(keyBlobPtr).length()]; + Util.arrayCopyNonAtomic(KMByteBlob.cast(keyBlobPtr).getBuffer(), + KMByteBlob.cast(keyBlobPtr).getStartOff(), + signatureData, (short) 0, (short) signatureData.length); + if (verifyFlag == false) { + Assert.assertEquals(signatureData.length, 256); + return; + } + boolean verify = rsaVerifyMessage(plainData, (short) 0, (short) plainData.length, + signatureData, (short) 0, (short) signatureData.length, + digest, padding, keyBlob); + Assert.assertTrue(verify); + } + + public void testSignVerifyWithEcdsa(byte digest, boolean update) { + short ecKeyArr = generateEcKey(null, null); + short keyBlobPtr = KMArray.cast(ecKeyArr).get((short) 1); + byte[] keyBlob = new byte[KMByteBlob.cast(keyBlobPtr).length()]; + Util.arrayCopyNonAtomic(KMByteBlob.cast(keyBlobPtr).getBuffer(), + KMByteBlob.cast(keyBlobPtr).getStartOff(), + keyBlob, (short) 0, (short) keyBlob.length); + short inParams = getEcParams(digest); + byte[] plainData = "Hello World 123!".getBytes(); + if (update) { + plainData = "Hello World 123! Hip Hip Hoorah!".getBytes(); + } + //Sign + short ret = processMessage(plainData, + KMByteBlob.instance(keyBlob, (short) 0, (short) keyBlob.length), + KMType.SIGN, + KMKeyParameters.instance(inParams), + (short) 0, null, update, false + ); + inParams = getEcParams(digest); + keyBlobPtr = KMArray.cast(ret).get((short) 1); + byte[] signatureData = new byte[KMByteBlob.cast(keyBlobPtr).length()]; + Util.arrayCopyNonAtomic(KMByteBlob.cast(keyBlobPtr).getBuffer(), + KMByteBlob.cast(keyBlobPtr).getStartOff(), + signatureData, (short) 0, (short) signatureData.length); + boolean verify = false; + if (digest == KMType.DIGEST_NONE) { + verify = ecNoDigestVerifyMessage(plainData, (short) 0, (short) plainData.length, + signatureData, (short) 0, (short) signatureData.length, + keyBlob); + } else { + verify = ecVerifyMessage(plainData, (short) 0, (short) plainData.length, + signatureData, (short) 0, (short) signatureData.length, + keyBlob); + } + Assert.assertTrue(verify); + } + + public void testSignVerifyWithHmac(byte digest, boolean update) { + short hmacKeyArr = generateHmacKey(null, null); + short keyBlobPtr = KMArray.cast(hmacKeyArr).get((short) 1); + byte[] keyBlob = new byte[KMByteBlob.cast(keyBlobPtr).length()]; + Util.arrayCopyNonAtomic(KMByteBlob.cast(keyBlobPtr).getBuffer(), + KMByteBlob.cast(keyBlobPtr).getStartOff(), + keyBlob, (short) 0, (short) keyBlob.length); + short inParams = getHmacParams(digest, true); + byte[] plainData = "Hello World 123!".getBytes(); + if (update) { + plainData = "Hello World 123! Hip Hip Hoorah!".getBytes(); + } + //Sign + short ret = processMessage(plainData, + KMByteBlob.instance(keyBlob, (short) 0, (short) keyBlob.length), + KMType.SIGN, + KMKeyParameters.instance(inParams), + (short) 0, null, update, false + ); + inParams = getHmacParams(digest, false); + keyBlobPtr = KMArray.cast(ret).get((short) 1); + byte[] signatureData = new byte[KMByteBlob.cast(keyBlobPtr).length()]; + Util.arrayCopyNonAtomic(KMByteBlob.cast(keyBlobPtr).getBuffer(), + KMByteBlob.cast(keyBlobPtr).getStartOff(), + signatureData, (short) 0, (short) signatureData.length); + ret = processMessage(plainData, + KMByteBlob.instance(keyBlob, (short) 0, (short) keyBlob.length), + KMType.VERIFY, + KMKeyParameters.instance(inParams), + (short) 0, signatureData, update, false + ); + short error = KMInteger.cast(KMArray.cast(ret).get((short) 0)).getShort(); + Assert.assertEquals(error, KMError.OK); + } + + private short getHmacParams(byte digest, boolean sign) { + short paramsize = (short) (sign ? 2 : 1); + short inParams = KMArray.instance((short) paramsize); + short byteBlob = KMByteBlob.instance((short) 1); + KMByteBlob.cast(byteBlob).add((short) 0, digest); + KMArray.cast(inParams).add((short) 0, KMEnumArrayTag.instance(KMType.DIGEST, byteBlob)); + short macLength = KMIntegerTag + .instance(KMType.UINT_TAG, KMType.MAC_LENGTH, KMInteger.uint_16((short)/*256*/160)); + if (sign) { + KMArray.cast(inParams).add((short) 1, macLength); + } + return inParams; + } + + public boolean ecNoDigestVerifyMessage(byte[] input, short inputOff, + short inputlen, byte[] sign, short signOff, short signLen, + byte[] keyBlob) { + KeyFactory kf; + byte[] pubKey = new byte[128]; + short keyStart = 0; + short keyLength = KMTestUtils.getPublicKey(decoder, keyBlob, (short) 0, (short) keyBlob.length, + pubKey, (short) 0); + if (keyLength == 0) { + return false; + } + try { + java.security.Signature sunSigner = java.security.Signature.getInstance( + "NONEwithECDSA", "SunEC"); + kf = KeyFactory.getInstance("EC"); + AlgorithmParameters parameters = AlgorithmParameters.getInstance("EC", + "SunEC"); + // Supported curve secp256r1 + parameters.init(new ECGenParameterSpec("secp256r1")); + ECParameterSpec ecParameters = parameters + .getParameterSpec(ECParameterSpec.class); + + // Check if the first byte is 04 and remove it. + if (pubKey[keyStart] == 0x04) { + // uncompressed format. + keyStart++; + keyLength--; + } + short i = 0; + byte[] pubx = new byte[keyLength / 2]; + for (; i < keyLength / 2; i++) { + pubx[i] = pubKey[keyStart + i]; + } + byte[] puby = new byte[keyLength / 2]; + for (i = 0; i < keyLength / 2; i++) { + puby[i] = pubKey[keyStart + keyLength / 2 + i]; + } + BigInteger bIX = new BigInteger(pubx); + BigInteger bIY = new BigInteger(puby); + ECPoint point = new ECPoint(bIX, bIY); + ECPublicKeySpec pubkeyspec = new ECPublicKeySpec(point, ecParameters); + java.security.interfaces.ECPublicKey ecPubkey = (java.security.interfaces.ECPublicKey) kf + .generatePublic(pubkeyspec); + sunSigner.initVerify(ecPubkey); + sunSigner.update(input, inputOff, inputlen); + return sunSigner.verify(sign, signOff, signLen); + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + } catch (NoSuchProviderException e) { + e.printStackTrace(); + } catch (InvalidParameterSpecException e) { + e.printStackTrace(); + } catch (InvalidKeySpecException e) { + e.printStackTrace(); + } catch (InvalidKeyException e) { + e.printStackTrace(); + } catch (SignatureException e) { + e.printStackTrace(); + } + return false; + } + + public boolean ecVerifyMessage(byte[] input, short inputOff, short inputlen, + byte[] sign, short signOff, short signLen, byte[] keyBlob) { + Signature ecVerifier; + byte[] pubKey = new byte[128]; + short len = KMTestUtils.getPublicKey(decoder, keyBlob, (short) 0, (short) keyBlob.length, + pubKey, (short) 0); + if (len == 0) { + return false; + } + ECPublicKey key = (ECPublicKey) KeyBuilder.buildKey( + KeyBuilder.TYPE_EC_FP_PUBLIC, KeyBuilder.LENGTH_EC_FP_256, false); + key.setW(pubKey, (short) 0, len); + ecVerifier = Signature.getInstance(Signature.ALG_ECDSA_SHA_256, false); + ecVerifier.init(key, Signature.MODE_VERIFY); + return ecVerifier.verify(input, inputOff, inputlen, sign, signOff, signLen); + } + + public boolean rsaVerifyMessage(byte[] input, short inputOff, short inputlen, byte[] sign, + short signOff, short signLen, + short digest, short padding, byte[] keyBlob) { + if (digest == KMType.DIGEST_NONE || padding == KMType.PADDING_NONE) { + return false; + } + byte[] pubKey = new byte[256]; + if (0 == KMTestUtils.getPublicKey(decoder, keyBlob, (short) 0, (short) keyBlob.length, pubKey, + (short) 0)) { + return false; + } + short alg = Signature.ALG_RSA_SHA_256_PKCS1_PSS; + + if (padding == KMType.RSA_PKCS1_1_5_SIGN) { + alg = Signature.ALG_RSA_SHA_256_PKCS1; + } + + Signature rsaVerifier = Signature.getInstance((byte) alg, false); + RSAPublicKey key = (RSAPublicKey) KeyBuilder + .buildKey(KeyBuilder.TYPE_RSA_PUBLIC, KeyBuilder.LENGTH_RSA_2048, false); + byte[] exponent = new byte[]{0x01, 0x00, 0x01}; + key.setExponent(exponent, (short) 0, (short) exponent.length); + key.setModulus(pubKey, (short) 0, (short) pubKey.length); + rsaVerifier.init(key, Signature.MODE_VERIFY); + return rsaVerifier.verify(input, inputOff, inputlen, sign, signOff, signLen); + } + + public byte[] EncryptMessage(byte[] input, short params, byte[] keyBlob) { + short ret = begin(KMType.ENCRYPT, + KMByteBlob.instance(keyBlob, (short) 0, (short) keyBlob.length), + KMKeyParameters.instance(params), (short) 0, false); + // Get the operation handle. + short opHandle = KMArray.cast(ret).get((short) 2); + byte[] opHandleBuf = new byte[KMOperationState.OPERATION_HANDLE_SIZE]; + KMInteger.cast(opHandle).getValue(opHandleBuf, (short) 0, + (short) opHandleBuf.length); + opHandle = KMInteger.uint_64(opHandleBuf, (short) 0); + + ret = finish(opHandle, + KMByteBlob.instance(input, (short) 0, (short) input.length), null, + (short) 0, (short) 0, (short) 0, (short) 0, KMError.OK, false); + short dataPtr = KMArray.cast(ret).get((short) 1); + byte[] output = new byte[KMByteBlob.cast(dataPtr).length()]; + if (KMByteBlob.cast(dataPtr).length() > 0) { + Util.arrayCopyNonAtomic(KMByteBlob.cast(dataPtr).getBuffer(), KMByteBlob + .cast(dataPtr).getStartOff(), output, (short) 0, + KMByteBlob.cast(dataPtr).length()); + } + return output; + } + + public byte[] DecryptMessage(byte[] input, short params, byte[] keyBlob) { + short ret = begin(KMType.DECRYPT, + KMByteBlob.instance(keyBlob, (short) 0, (short) keyBlob.length), + KMKeyParameters.instance(params), (short) 0, false); + // Get the operation handle. + short opHandle = KMArray.cast(ret).get((short) 2); + byte[] opHandleBuf = new byte[KMOperationState.OPERATION_HANDLE_SIZE]; + KMInteger.cast(opHandle).getValue(opHandleBuf, (short) 0, + (short) opHandleBuf.length); + opHandle = KMInteger.uint_64(opHandleBuf, (short) 0); + + ret = finish(opHandle, + KMByteBlob.instance(input, (short) 0, (short) input.length), null, + (short) 0, (short) 0, (short) 0, (short) 0, KMError.OK, false); + short dataPtr = KMArray.cast(ret).get((short) 1); + byte[] output = new byte[KMByteBlob.cast(dataPtr).length()]; + if (KMByteBlob.cast(dataPtr).length() > 0) { + Util.arrayCopyNonAtomic(KMByteBlob.cast(dataPtr).getBuffer(), KMByteBlob + .cast(dataPtr).getStartOff(), output, (short) 0, + KMByteBlob.cast(dataPtr).length()); + } + return output; + } + + public short generateRandom(short upperBound) { + Random rand = new Random(); + short int_random = (short) rand.nextInt(upperBound); + return int_random; + } + + public short generateAesGcmKey(short keysize, byte[] clientId, byte[] appData) { + short tagCount = 10; + if (clientId != null) { + tagCount++; + } + if (appData != null) { + tagCount++; + } + short arrPtr = KMArray.instance(tagCount); + short boolTag = KMBoolTag.instance(KMType.NO_AUTH_REQUIRED); + short keySize = KMIntegerTag + .instance(KMType.UINT_TAG, KMType.KEYSIZE, KMInteger.uint_16(keysize)); + short macLength = KMIntegerTag + .instance(KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, KMInteger.uint_16((short) 96)); + short byteBlob = KMByteBlob.instance((short) 1); + KMByteBlob.cast(byteBlob).add((short) 0, KMType.GCM); + short blockModeTag = KMEnumArrayTag.instance(KMType.BLOCK_MODE, byteBlob); + byteBlob = KMByteBlob.instance((short) 1); + KMByteBlob.cast(byteBlob).add((short) 0, KMType.PADDING_NONE); + short paddingMode = KMEnumArrayTag.instance(KMType.PADDING, byteBlob); + byteBlob = KMByteBlob.instance((short) 2); + KMByteBlob.cast(byteBlob).add((short) 0, KMType.ENCRYPT); + KMByteBlob.cast(byteBlob).add((short) 1, KMType.DECRYPT); + short purpose = KMEnumArrayTag.instance(KMType.PURPOSE, byteBlob); + short tagIndex = 0; + KMArray.cast(arrPtr).add(tagIndex++, boolTag); + KMArray.cast(arrPtr).add(tagIndex++, macLength); + KMArray.cast(arrPtr).add(tagIndex++, keySize); + KMArray.cast(arrPtr).add(tagIndex++, blockModeTag); + KMArray.cast(arrPtr).add(tagIndex++, paddingMode); + KMArray.cast(arrPtr).add(tagIndex++, KMEnumTag.instance(KMType.ALGORITHM, KMType.AES)); + KMArray.cast(arrPtr).add(tagIndex++, purpose); + KMArray.cast(arrPtr).add(tagIndex++, KMBoolTag.instance(KMType.CALLER_NONCE)); + tagIndex = setDefaultValidity(arrPtr, tagIndex); + if (clientId != null) { + KMArray.cast(arrPtr).add(tagIndex++, + KMByteTag.instance(KMType.APPLICATION_ID, + KMByteBlob.instance(clientId, (short) 0, (short) clientId.length))); + } + if (appData != null) { + KMArray.cast(arrPtr).add(tagIndex++, + KMByteTag.instance(KMType.APPLICATION_DATA, + KMByteBlob.instance(appData, (short) 0, (short) appData.length))); + } + short keyParams = KMKeyParameters.instance(arrPtr); + arrPtr = generateKeyNoAttestCmd(keyParams); + CommandAPDU apdu = KMTestUtils.encodeApdu(encoder, (byte) INS_GENERATE_KEY_CMD, arrPtr); + // print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(apdu); + return parseGenerateKeyResponse(response); + } + + public short rsaEncryptMessage(byte[] keyBlob, short padding, short digest, byte[] input, + short inputOff, short inputlen, + byte[] output, short outputOff) { + byte alg = Cipher.ALG_RSA_PKCS1; + byte[] tmp = null; + short inLen = inputlen; + if (padding == KMType.PADDING_NONE) { + alg = Cipher.ALG_RSA_NOPAD; + // Length cannot be greater then key size according to JcardSim + if (inLen >= 256) { + return 0; + } + // make input equal to 255 bytes + tmp = new byte[255]; + Util.arrayFillNonAtomic(tmp, (short) 0, (short) 255, (byte) 0); + Util.arrayCopyNonAtomic( + input, + inputOff, + tmp, (short) (255 - inLen), inLen); + inLen = 255; + inputOff = 0; + } else if (padding == KMType.RSA_PKCS1_1_5_ENCRYPT) { + tmp = input; + } else { + /*Fail */ + Assert.assertTrue(false); + } + byte[] pubKey = new byte[256]; + KeyPair rsaKeyPair = new KeyPair(KeyPair.ALG_RSA, KeyBuilder.LENGTH_RSA_2048); + RSAPublicKey rsaPubKey = (RSAPublicKey) rsaKeyPair.getPublic(); + if (0 == KMTestUtils.getPublicKey(decoder, keyBlob, (short) 0, (short) keyBlob.length, pubKey, + (short) 0)) { + return 0; + } + + byte[] exponent = new byte[]{0x01, 0x00, 0x01}; + rsaPubKey.setModulus(pubKey, (short) 0, (short) pubKey.length); + rsaPubKey.setExponent(exponent, (short) 0, (short) exponent.length); + + Cipher rsaCipher = Cipher.getInstance(alg, false); + rsaCipher.init(rsaPubKey, Cipher.MODE_ENCRYPT); + return rsaCipher.doFinal(tmp, inputOff, inLen, output, outputOff); + } +} diff --git a/Applet/JCardSimProvider/test/com/android/javacard/test/KMProvision.java b/Applet/JCardSimProvider/test/com/android/javacard/test/KMProvision.java new file mode 100644 index 00000000..77a85d3b --- /dev/null +++ b/Applet/JCardSimProvider/test/com/android/javacard/test/KMProvision.java @@ -0,0 +1,518 @@ +package com.android.javacard.test; + +import com.android.javacard.keymaster.KMArray; +import com.android.javacard.keymaster.KMByteBlob; +import com.android.javacard.keymaster.KMByteTag; +import com.android.javacard.keymaster.KMCose; +import com.android.javacard.keymaster.KMCoseHeaders; +import com.android.javacard.keymaster.KMDecoder; +import com.android.javacard.keymaster.KMEncoder; +import com.android.javacard.keymaster.KMEnum; +import com.android.javacard.keymaster.KMEnumArrayTag; +import com.android.javacard.keymaster.KMEnumTag; +import com.android.javacard.keymaster.KMError; +import com.android.javacard.keymaster.KMHmacSharingParameters; +import com.android.javacard.keymaster.KMInteger; +import com.android.javacard.keymaster.KMIntegerTag; +import com.android.javacard.keymaster.KMKeyParameters; +import com.android.javacard.keymaster.KMKeymasterApplet; +import com.android.javacard.keymaster.KMKeymintDataStore; +import com.android.javacard.keymaster.KMMap; +import com.android.javacard.keymaster.KMNInteger; +import com.android.javacard.keymaster.KMSimpleValue; +import com.android.javacard.keymaster.KMTextString; +import com.android.javacard.keymaster.KMType; +import com.android.javacard.seprovider.KMECDeviceUniqueKey; +import com.android.javacard.seprovider.KMSEProvider; +import com.licel.jcardsim.smartcardio.CardSimulator; +import javacard.framework.Util; +import javacard.security.ECPrivateKey; +import javacard.security.KeyBuilder; +import javacard.security.KeyPair; +import javacard.security.Signature; +import javax.smartcardio.Card; +import javax.smartcardio.CommandAPDU; +import javax.smartcardio.ResponseAPDU; +import org.junit.Assert; + +public class KMProvision { + + // Provision Instructions + private static final byte INS_KEYMINT_PROVIDER_APDU_START = 0x00; + private static final byte INS_PROVISION_ATTEST_IDS_CMD = INS_KEYMINT_PROVIDER_APDU_START + 1; + private static final byte INS_PROVISION_PRESHARED_SECRET_CMD = + INS_KEYMINT_PROVIDER_APDU_START + 2; + private static final byte INS_OEM_LOCK_PROVISIONING_CMD = INS_KEYMINT_PROVIDER_APDU_START + 3; + private static final byte INS_GET_PROVISION_STATUS_CMD = INS_KEYMINT_PROVIDER_APDU_START + 4; + private static final byte INS_SET_BOOT_PARAMS_CMD = INS_KEYMINT_PROVIDER_APDU_START + 5; + private static final byte INS_PROVISION_RKP_DEVICE_UNIQUE_KEYPAIR_CMD = + INS_KEYMINT_PROVIDER_APDU_START + 6; + private static final byte INS_PROVISION_RKP_ADDITIONAL_CERT_CHAIN_CMD = + INS_KEYMINT_PROVIDER_APDU_START + 7; + private static final byte INS_SET_BOOT_ENDED_CMD = + INS_KEYMINT_PROVIDER_APDU_START + 8; //unused + private static final byte INS_SE_FACTORY_PROVISIONING_LOCK_CMD = + INS_KEYMINT_PROVIDER_APDU_START + 9; + private static final byte INS_PROVISION_OEM_ROOT_PUBLIC_KEY_CMD = + INS_KEYMINT_PROVIDER_APDU_START + 10; + private static final byte INS_OEM_UNLOCK_PROVISIONING_CMD = INS_KEYMINT_PROVIDER_APDU_START + 11; + // Top 32 commands are reserved for provisioning. + private static final byte INS_END_KM_PROVISION_CMD = 0x20; + + private static final byte KEYMINT_CMD_APDU_START = 0x20; + private static final byte INS_COMPUTE_SHARED_HMAC_CMD = KEYMINT_CMD_APDU_START + 10; //0x2A + private static final byte INS_GET_HMAC_SHARING_PARAM_CMD = KEYMINT_CMD_APDU_START + 13; //0x2D + private static final byte INS_EARLY_BOOT_ENDED_CMD = KEYMINT_CMD_APDU_START + 21; //0x35 + private static final byte INS_INIT_STRONGBOX_CMD = KEYMINT_CMD_APDU_START + 26; //0x3A + // The instructions from 0x43 to 0x4C will be reserved for KeyMint 1.0 for any future use. + // KeyMint 2.0 Instructions + private static final byte INS_GET_ROT_CHALLENGE_CMD = KEYMINT_CMD_APDU_START + 45; // 0x4D + private static final byte INS_GET_ROT_DATA_CMD = KEYMINT_CMD_APDU_START + 46; // 0x4E + private static final byte INS_SEND_ROT_DATA_CMD = KEYMINT_CMD_APDU_START + 47; // 0x4F + + private static final byte[] kEcPrivKey = { + (byte) 0x21, (byte) 0xe0, (byte) 0x86, (byte) 0x43, (byte) 0x2a, + (byte) 0x15, (byte) 0x19, (byte) 0x84, (byte) 0x59, (byte) 0xcf, + (byte) 0x36, (byte) 0x3a, (byte) 0x50, (byte) 0xfc, (byte) 0x14, + (byte) 0xc9, (byte) 0xda, (byte) 0xad, (byte) 0xf9, (byte) 0x35, + (byte) 0xf5, (byte) 0x27, (byte) 0xc2, (byte) 0xdf, (byte) 0xd7, + (byte) 0x1e, (byte) 0x4d, (byte) 0x6d, (byte) 0xbc, (byte) 0x42, + (byte) 0xe5, (byte) 0x44}; + private static final byte[] kEcPubKey = { + (byte) 0x04, (byte) 0xeb, (byte) 0x9e, (byte) 0x79, (byte) 0xf8, + (byte) 0x42, (byte) 0x63, (byte) 0x59, (byte) 0xac, (byte) 0xcb, + (byte) 0x2a, (byte) 0x91, (byte) 0x4c, (byte) 0x89, (byte) 0x86, + (byte) 0xcc, (byte) 0x70, (byte) 0xad, (byte) 0x90, (byte) 0x66, + (byte) 0x93, (byte) 0x82, (byte) 0xa9, (byte) 0x73, (byte) 0x26, + (byte) 0x13, (byte) 0xfe, (byte) 0xac, (byte) 0xcb, (byte) 0xf8, + (byte) 0x21, (byte) 0x27, (byte) 0x4c, (byte) 0x21, (byte) 0x74, + (byte) 0x97, (byte) 0x4a, (byte) 0x2a, (byte) 0xfe, (byte) 0xa5, + (byte) 0xb9, (byte) 0x4d, (byte) 0x7f, (byte) 0x66, (byte) 0xd4, + (byte) 0xe0, (byte) 0x65, (byte) 0x10, (byte) 0x66, (byte) 0x35, + (byte) 0xbc, (byte) 0x53, (byte) 0xb7, (byte) 0xa0, (byte) 0xa3, + (byte) 0xa6, (byte) 0x71, (byte) 0x58, (byte) 0x3e, (byte) 0xdb, + (byte) 0x3e, (byte) 0x11, (byte) 0xae, (byte) 0x10, (byte) 0x14}; + + // OEM lock / unlock verification constants. + private static final byte[] OEM_LOCK_PROVISION_VERIFICATION_LABEL = { // "OEM Provisioning Lock" + 0x4f, 0x45, 0x4d, 0x20, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x69, 0x6e, + 0x67, 0x20, 0x4c, 0x6f, 0x63, 0x6b + }; + private static final byte[] OEM_UNLOCK_PROVISION_VERIFICATION_LABEL = { // "Enable RMA" + 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x52, 0x4d, 0x41 + }; + + public static final int OS_VERSION = 1; + public static final int OS_PATCH_LEVEL = 1; + public static final int VENDOR_PATCH_LEVEL = 1; + public static final int BOOT_PATCH_LEVEL = 1; + // RKP Device Unique Public and Private Keys. + public static byte[] RKP_DK_PUB = new byte[65]; + public static byte[] RKP_DK_PRIV = new byte[32]; + + //---------------------------------------------------------------------------------------------- + // Provision functions + //---------------------------------------------------------------------------------------------- + public static ResponseAPDU setAndroidOSSystemProperties(CardSimulator simulator, + KMEncoder encoder, + KMDecoder decoder, short osVersion, + short osPatchLevel, short vendorPatchLevel) { + // Argument 1 OS Version + short versionPtr = KMInteger.uint_16(osVersion); + // short versionTagPtr = KMIntegerTag.instance(KMType.UINT_TAG, + // KMType.OS_VERSION,versionPatchPtr); + // Argument 2 OS Patch level + short patchPtr = KMInteger.uint_16(osPatchLevel); + short vendorpatchPtr = KMInteger.uint_16((short) vendorPatchLevel); + // Arguments + short arrPtr = KMArray.instance((short) 3); + KMArray vals = KMArray.cast(arrPtr); + vals.add((short) 0, versionPtr); + vals.add((short) 1, patchPtr); + vals.add((short) 2, vendorpatchPtr); + CommandAPDU apdu = KMTestUtils.encodeApdu(encoder, (byte) INS_INIT_STRONGBOX_CMD, arrPtr); + // print(commandAPDU.getBytes()); + return simulator.transmitCommand(apdu); + } + + public static ResponseAPDU setBootParams(CardSimulator simulator, KMEncoder encoder, + KMDecoder decoder, + short bootPatchLevel) { + // Argument 0 boot patch level + short bootpatchPtr = KMInteger.uint_16((short) bootPatchLevel); + // Argument 1 Verified Boot Key + byte[] bootKeyHash = "00011122233344455566677788899900".getBytes(); + short bootKeyPtr = KMByteBlob.instance(bootKeyHash, (short) 0, + (short) bootKeyHash.length); + // Argument 2 Verified Boot Hash + short bootHashPtr = KMByteBlob.instance(bootKeyHash, (short) 0, + (short) bootKeyHash.length); + // Argument 3 Verified Boot State + short bootStatePtr = KMEnum.instance(KMType.VERIFIED_BOOT_STATE, + KMType.VERIFIED_BOOT); + // Argument 4 Device Locked + short deviceLockedPtr = KMEnum.instance(KMType.DEVICE_LOCKED, + KMType.DEVICE_LOCKED_FALSE); + // Arguments + short arrPtr = KMArray.instance((short) 5); + KMArray vals = KMArray.cast(arrPtr); + vals.add((short) 0, bootpatchPtr); + vals.add((short) 1, bootKeyPtr); + vals.add((short) 2, bootHashPtr); + vals.add((short) 3, bootStatePtr); + vals.add((short) 4, deviceLockedPtr); + CommandAPDU apdu = KMTestUtils.encodeApdu(encoder, (byte) INS_SET_BOOT_PARAMS_CMD, arrPtr); + // print(commandAPDU.getBytes()); + return simulator.transmitCommand(apdu); + } + + public static ResponseAPDU provisionAdditionalCertChain(CardSimulator simulator, + KMSEProvider cryptoProvider, KMEncoder encoder, KMDecoder decoder) { + short[] lengths = new short[2]; + byte[] rootPriv = new byte[128]; + byte[] rootPub = new byte[128]; + byte[] scratchpad = new byte[500]; + short[] coseScratchpad = new short[20]; + cryptoProvider.createAsymmetricKey(KMType.EC, rootPriv, (short) 0, (short) 128, + rootPub, (short) 0, (short) 128, lengths); + short coseKey = + KMTestUtils.constructCoseKey( + KMInteger.uint_8(KMCose.COSE_KEY_TYPE_EC2), + KMType.INVALID_VALUE, + KMNInteger.uint_8(KMCose.COSE_ALG_ES256), + KMInteger.uint_8(KMCose.COSE_KEY_OP_SIGN), + KMInteger.uint_8(KMCose.COSE_ECCURVE_256), + rootPub, (short) 0, lengths[1], + rootPriv, (short) 0, lengths[0], + false + ); + short payload = encoder.encode(coseKey, scratchpad, (short) 0, (short) 500); + payload = KMByteBlob.instance(scratchpad, (short) 0, payload); + // Protected Header + short protectedHeaderPtr = KMCose.constructHeaders(coseScratchpad, + KMNInteger.uint_8(KMCose.COSE_ALG_ES256), + KMType.INVALID_VALUE, + KMType.INVALID_VALUE, + KMType.INVALID_VALUE); + protectedHeaderPtr = encoder.encode(protectedHeaderPtr, scratchpad, (short) 0, (short) 500); + protectedHeaderPtr = KMByteBlob.instance(scratchpad, (short) 0, protectedHeaderPtr); + + // Unprotected Headers. + short emptyArr = KMArray.instance((short) 0); + short unprotectedHeader = KMCoseHeaders.instance(emptyArr); + + short aad = KMByteBlob.instance((short) 0); + KeyPair ecKeyPair = new KeyPair(KeyPair.ALG_EC_FP, KeyBuilder.LENGTH_EC_FP_256); + KMECDeviceUniqueKey ecUniqueKey = new KMECDeviceUniqueKey(ecKeyPair); + ecUniqueKey.setS(rootPriv, (short) 0, lengths[0]); + ecUniqueKey.setW(rootPub, (short) 0, lengths[1]); + short root = KMTestUtils.constructCoseSign1(cryptoProvider, encoder, + protectedHeaderPtr, unprotectedHeader, payload, aad, + ecUniqueKey); + coseKey = + KMTestUtils.constructCoseKey( + KMInteger.uint_8(KMCose.COSE_KEY_TYPE_EC2), + KMType.INVALID_VALUE, + KMNInteger.uint_8(KMCose.COSE_ALG_ES256), + KMInteger.uint_8(KMCose.COSE_KEY_OP_SIGN), + KMInteger.uint_8(KMCose.COSE_ECCURVE_256), + RKP_DK_PUB, (short) 0, (short) RKP_DK_PUB.length, + RKP_DK_PRIV, (short) 0, (short) RKP_DK_PRIV.length, + false + ); + payload = encoder.encode(coseKey, scratchpad, (short) 0, (short) 500); + payload = KMByteBlob.instance(scratchpad, (short) 0, payload); + short leaf = KMTestUtils.constructCoseSign1(cryptoProvider, encoder, protectedHeaderPtr, + unprotectedHeader, payload, aad, + ecUniqueKey); + short additionalCertChain = KMArray.instance((short) 2); + KMArray.cast(additionalCertChain).add((short) 0, root); + KMArray.cast(additionalCertChain).add((short) 1, leaf); + short map = KMMap.instance((short) 1); + byte[] signerName = "TestSigner".getBytes(); + KMMap.cast(map) + .add((short) 0, KMTextString.instance(signerName, (short) 0, (short) signerName.length), + additionalCertChain); + CommandAPDU apdu = KMTestUtils.encodeApdu(encoder, + (byte) INS_PROVISION_RKP_ADDITIONAL_CERT_CHAIN_CMD, map); + return simulator.transmitCommand(apdu); + } + + public static ResponseAPDU provisionDeviceUniqueKeyPair(CardSimulator simulator, + KMSEProvider cryptoProvider, KMEncoder encoder, + KMDecoder decoder) { + short[] lengths = new short[2]; + byte[] privKey = new byte[128]; + byte[] pubKey = new byte[128]; + cryptoProvider.createAsymmetricKey(KMType.EC, privKey, (short) 0, (short) 128, + pubKey, (short) 0, (short) 128, lengths); + short coseKey = + KMTestUtils.constructCoseKey( + KMInteger.uint_8(KMCose.COSE_KEY_TYPE_EC2), + KMType.INVALID_VALUE, + KMNInteger.uint_8(KMCose.COSE_ALG_ES256), + KMInteger.uint_8(KMCose.COSE_KEY_OP_SIGN), + KMInteger.uint_8(KMCose.COSE_ECCURVE_256), + pubKey, (short) 0, lengths[1], + privKey, (short) 0, lengths[0], + false + ); + Assert.assertEquals(lengths[1], 65); + Assert.assertTrue("Private key length should not be > 32", (lengths[0] <= 32)); + Util.arrayFillNonAtomic(RKP_DK_PRIV, (short) 0, (short) 32, (byte) 0); + Util.arrayFillNonAtomic(RKP_DK_PUB, (short) 0, (short) 65, (byte) 0); + Util.arrayCopyNonAtomic(privKey, (short) 0, RKP_DK_PRIV, (short) (32 - lengths[0]), (short) lengths[0]); + Util.arrayCopyNonAtomic(pubKey, (short) 0, RKP_DK_PUB, (short) 0, (short) lengths[1]); + short arr = KMArray.instance((short) 1); + KMArray.cast(arr).add((short) 0, coseKey); + CommandAPDU apdu = KMTestUtils.encodeApdu(encoder, + (byte) INS_PROVISION_RKP_DEVICE_UNIQUE_KEYPAIR_CMD, arr); + return simulator.transmitCommand(apdu); + } + + public static ResponseAPDU provisionOEMRootPublicKey(CardSimulator simulator, KMEncoder encoder, + KMDecoder decoder) { + // KeyParameters. + short arrPtr = KMArray.instance((short) 4); + short ecCurve = KMEnumTag.instance(KMType.ECCURVE, KMType.P_256); + short byteBlob = KMByteBlob.instance((short) 1); + KMByteBlob.cast(byteBlob).add((short) 0, KMType.SHA2_256); + short digest = KMEnumArrayTag.instance(KMType.DIGEST, byteBlob); + short byteBlob2 = KMByteBlob.instance((short) 1); + KMByteBlob.cast(byteBlob2).add((short) 0, KMType.VERIFY); + short purpose = KMEnumArrayTag.instance(KMType.PURPOSE, byteBlob2); + KMArray.cast(arrPtr).add((short) 0, ecCurve); + KMArray.cast(arrPtr).add((short) 1, digest); + KMArray.cast(arrPtr).add((short) 2, + KMEnumTag.instance(KMType.ALGORITHM, KMType.EC)); + KMArray.cast(arrPtr).add((short) 3, purpose); + short keyParams = KMKeyParameters.instance(arrPtr); + // Note: VTS uses PKCS8 KeyFormat RAW + short keyFormatPtr = KMEnum.instance(KMType.KEY_FORMAT, KMType.RAW); + + // Key + short signKeyPtr = KMByteBlob.instance(kEcPubKey, (short) 0, (short) kEcPubKey.length); + + short finalArrayPtr = KMArray.instance((short) 3); + KMArray.cast(finalArrayPtr).add((short) 0, keyParams); + KMArray.cast(finalArrayPtr).add((short) 1, keyFormatPtr); + KMArray.cast(finalArrayPtr).add((short) 2, signKeyPtr); + + CommandAPDU apdu = KMTestUtils.encodeApdu(encoder, (byte) INS_PROVISION_OEM_ROOT_PUBLIC_KEY_CMD, + finalArrayPtr); + // print(commandAPDU.getBytes()); + return simulator.transmitCommand(apdu); + } + + public static ResponseAPDU provisionSharedSecret(CardSimulator simulator, KMEncoder encoder, + KMDecoder decoder) { + byte[] sharedKeySecret = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0}; + short arrPtr = KMArray.instance((short) 1); + short byteBlob = KMByteBlob.instance(sharedKeySecret, (short) 0, + (short) sharedKeySecret.length); + KMArray.cast(arrPtr).add((short) 0, byteBlob); + + CommandAPDU apdu = KMTestUtils.encodeApdu(encoder, (byte) INS_PROVISION_PRESHARED_SECRET_CMD, + arrPtr); + // print(commandAPDU.getBytes()); + return simulator.transmitCommand(apdu); + } + + public static ResponseAPDU provisionAttestIds(CardSimulator simulator, KMEncoder encoder, + KMDecoder decoder) { + short arrPtr = KMArray.instance((short) 8); + + byte[] buf = "Attestation Id".getBytes(); + + KMArray.cast(arrPtr).add((short) 0, + KMByteTag.instance(KMType.ATTESTATION_ID_BRAND, + KMByteBlob.instance(buf, (short) 0, (short) buf.length))); + KMArray.cast(arrPtr).add((short) 1, + KMByteTag.instance(KMType.ATTESTATION_ID_PRODUCT, + KMByteBlob.instance(buf, (short) 0, (short) buf.length))); + KMArray.cast(arrPtr).add((short) 2, + KMByteTag.instance(KMType.ATTESTATION_ID_DEVICE, + KMByteBlob.instance(buf, (short) 0, (short) buf.length))); + KMArray.cast(arrPtr).add((short) 3, + KMByteTag.instance(KMType.ATTESTATION_ID_MODEL, + KMByteBlob.instance(buf, (short) 0, (short) buf.length))); + KMArray.cast(arrPtr).add((short) 4, + KMByteTag.instance(KMType.ATTESTATION_ID_IMEI, + KMByteBlob.instance(buf, (short) 0, (short) buf.length))); + KMArray.cast(arrPtr).add((short) 5, + KMByteTag.instance(KMType.ATTESTATION_ID_MEID, + KMByteBlob.instance(buf, (short) 0, (short) buf.length))); + KMArray.cast(arrPtr).add((short) 6, + KMByteTag.instance(KMType.ATTESTATION_ID_MANUFACTURER, + KMByteBlob.instance(buf, (short) 0, (short) buf.length))); + KMArray.cast(arrPtr).add((short) 7, + KMByteTag.instance(KMType.ATTESTATION_ID_SERIAL, + KMByteBlob.instance(buf, (short) 0, (short) buf.length))); + short keyParams = KMKeyParameters.instance(arrPtr); + short outerArrPtr = KMArray.instance((short) 1); + KMArray.cast(outerArrPtr).add((short) 0, keyParams); + CommandAPDU apdu = KMTestUtils.encodeApdu(encoder, (byte) INS_PROVISION_ATTEST_IDS_CMD, + outerArrPtr); + // print(commandAPDU.getBytes()); + return simulator.transmitCommand(apdu); + } + + public static ResponseAPDU provisionLocked(CardSimulator simulator, KMEncoder encoder, + KMDecoder decoder) { + // Sign the Lock message + byte[] signature = new byte[120]; + ECPrivateKey key = (ECPrivateKey) KeyBuilder.buildKey( + KeyBuilder.TYPE_EC_FP_PRIVATE, KeyBuilder.LENGTH_EC_FP_256, false); + key.setS(kEcPrivKey, (short) 0, (short) kEcPrivKey.length); + Signature ecSigner = Signature.getInstance(Signature.ALG_ECDSA_SHA_256, false); + ecSigner.init(key, Signature.MODE_SIGN); + short len = + ecSigner.sign( + OEM_LOCK_PROVISION_VERIFICATION_LABEL, + (short) 0, + (short) OEM_LOCK_PROVISION_VERIFICATION_LABEL.length, + signature, + (short) 0); + + short arr = KMArray.instance((short) 1); + KMArray.cast(arr).add((short) 0, KMByteBlob.instance(signature, (short) 0, len)); + + CommandAPDU apdu = KMTestUtils.encodeApdu(encoder, (byte) INS_OEM_LOCK_PROVISIONING_CMD, + arr); + // print(commandAPDU.getBytes()); + return simulator.transmitCommand(apdu); + } + + public static ResponseAPDU provisionOemUnLock(CardSimulator simulator, KMEncoder encoder, + KMDecoder decoder) { + // Sign the Lock message + byte[] signature = new byte[120]; + ECPrivateKey key = (ECPrivateKey) KeyBuilder.buildKey( + KeyBuilder.TYPE_EC_FP_PRIVATE, KeyBuilder.LENGTH_EC_FP_256, false); + key.setS(kEcPrivKey, (short) 0, (short) kEcPrivKey.length); + Signature ecSigner = Signature.getInstance(Signature.ALG_ECDSA_SHA_256, false); + ecSigner.init(key, Signature.MODE_SIGN); + short len = + ecSigner.sign( + OEM_UNLOCK_PROVISION_VERIFICATION_LABEL, + (short) 0, + (short) OEM_UNLOCK_PROVISION_VERIFICATION_LABEL.length, + signature, + (short) 0); + + short arr = KMArray.instance((short) 1); + KMArray.cast(arr).add((short) 0, KMByteBlob.instance(signature, (short) 0, len)); + + CommandAPDU apdu = KMTestUtils.encodeApdu(encoder, (byte) INS_OEM_UNLOCK_PROVISIONING_CMD, + arr); + return simulator.transmitCommand(apdu); + } + + public static ResponseAPDU provisionSeLocked(CardSimulator simulator, KMDecoder decoder) { + CommandAPDU commandAPDU = new CommandAPDU(0x80, INS_SE_FACTORY_PROVISIONING_LOCK_CMD, + 0x50, 0x00); + // print(commandAPDU.getBytes()); + return simulator.transmitCommand(commandAPDU); + } + + public static void computeSharedSecret(CardSimulator simulator, KMSEProvider cryptoProvider, + KMEncoder encoder, KMDecoder decoder) { + short ret = getHmacSharingParams(simulator, decoder); + short error = KMInteger.cast(KMArray.cast(ret).get((short) 0)).getShort(); + KMHmacSharingParameters params = KMHmacSharingParameters.cast(KMArray.cast(ret).get((short) 1)); + short seed = params.getSeed(); + short nonce = params.getNonce(); + + short params1 = KMHmacSharingParameters.instance(); + KMHmacSharingParameters.cast(params1).setSeed(KMByteBlob.instance((short) 0)); + short num = KMByteBlob.instance((short) 32); + Util.arrayCopyNonAtomic( + KMByteBlob.cast(nonce).getBuffer(), + KMByteBlob.cast(nonce).getStartOff(), + KMByteBlob.cast(num).getBuffer(), + KMByteBlob.cast(num).getStartOff(), + KMByteBlob.cast(num).length()); + + KMHmacSharingParameters.cast(params1).setNonce(num); + short params2 = KMHmacSharingParameters.instance(); + KMHmacSharingParameters.cast(params2).setSeed(KMByteBlob.instance((short) 0)); + num = KMByteBlob.instance((short) 32); + cryptoProvider.newRandomNumber( + KMByteBlob.cast(num).getBuffer(), + KMByteBlob.cast(num).getStartOff(), + KMByteBlob.cast(num).length()); + KMHmacSharingParameters.cast(params2).setNonce(num); + short arr = KMArray.instance((short) 2); + KMArray.cast(arr).add((short) 0, params1); + KMArray.cast(arr).add((short) 1, params2); + short arrPtr = KMArray.instance((short) 1); + KMArray.cast(arrPtr).add((short) 0, arr); + CommandAPDU apdu = KMTestUtils.encodeApdu(encoder, (byte) INS_COMPUTE_SHARED_HMAC_CMD, arrPtr); + // print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(apdu); + Assert.assertEquals(0x9000, response.getSW()); + byte[] resp = response.getBytes(); + arr = KMArray.instance((short) 2); + KMArray.cast(arr).add((short) 0, KMInteger.exp()); + KMArray.cast(arr).add((short) 1, KMByteBlob.exp()); + short ptr = decoder.decode(arr, resp, (short) 0, (short) resp.length); + Assert.assertEquals(KMError.OK, KMInteger.cast(KMArray.cast(ptr).get((short) 0)).getShort()); + } + + public static void provisionCmd(CardSimulator simulator, + KMSEProvider cryptoProvider, KMEncoder encoder, + KMDecoder decoder) { + Assert.assertEquals(KMError.OK, KMTestUtils.decodeError(decoder, + provisionDeviceUniqueKeyPair(simulator, cryptoProvider, encoder, decoder))); + Assert.assertEquals(KMError.OK, KMTestUtils.decodeError(decoder, + provisionAdditionalCertChain(simulator, cryptoProvider, encoder, decoder))); + Assert.assertEquals(KMError.OK, KMTestUtils.decodeError(decoder, + provisionSeLocked(simulator, decoder))); + Assert.assertEquals(KMError.OK, KMTestUtils.decodeError(decoder, + provisionSharedSecret(simulator, encoder, decoder))); + Assert.assertEquals(KMError.OK, KMTestUtils.decodeError(decoder, + provisionAttestIds(simulator, encoder, decoder))); + Assert.assertEquals(KMError.OK, KMTestUtils.decodeError(decoder, + provisionOEMRootPublicKey(simulator, encoder, decoder))); + setBootParams(simulator, encoder, decoder, (short) BOOT_PATCH_LEVEL); + // set android system properties + Assert.assertEquals(KMError.OK, KMTestUtils.decodeError(decoder, + setAndroidOSSystemProperties(simulator, encoder, decoder, (short) OS_VERSION, + (short) OS_PATCH_LEVEL, + (short) VENDOR_PATCH_LEVEL))); + Assert.assertEquals(KMError.OK, KMTestUtils.decodeError(decoder, + provisionLocked(simulator, encoder, decoder))); + // negotiate shared secret. + computeSharedSecret(simulator, cryptoProvider, encoder, decoder); + sendEarlyBootEnded(simulator, decoder); + } + + public static void sendEarlyBootEnded(CardSimulator simulator, KMDecoder decoder) { + CommandAPDU commandAPDU = new CommandAPDU(0x80, INS_EARLY_BOOT_ENDED_CMD, 0x50, 0x00); + //print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(commandAPDU); + Assert.assertEquals(0x9000, response.getSW()); + byte[] resp = response.getBytes(); + short arr = KMArray.instance((short) 1); + KMArray.cast(arr).add((short) 0, KMInteger.exp()); + short ptr = decoder.decode(arr, resp, (short) 0, (short) resp.length); + Assert.assertEquals(KMError.OK, KMInteger.cast(KMArray.cast(ptr).get((short) 0)).getShort()); + } + + public static short getHmacSharingParams(CardSimulator simulator, KMDecoder decoder) { + CommandAPDU commandAPDU = new CommandAPDU(0x80, INS_GET_HMAC_SHARING_PARAM_CMD, 0x50, 0x00); + //print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(commandAPDU); + KMDecoder dec = new KMDecoder(); + short ret = KMArray.instance((short) 2); + KMArray.cast(ret).add((short) 0, KMInteger.exp()); + short inst = KMHmacSharingParameters.exp(); + KMArray.cast(ret).add((short) 1, inst); + byte[] respBuf = response.getBytes(); + short len = (short) respBuf.length; + ret = decoder.decode(ret, respBuf, (short) 0, len); + return ret; + } +} diff --git a/Applet/JCardSimProvider/test/com/android/javacard/test/KMRKPFunctionalTest.java b/Applet/JCardSimProvider/test/com/android/javacard/test/KMRKPFunctionalTest.java new file mode 100644 index 00000000..8f9101d8 --- /dev/null +++ b/Applet/JCardSimProvider/test/com/android/javacard/test/KMRKPFunctionalTest.java @@ -0,0 +1,513 @@ +/* + * 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.KMAsn1Parser; +import com.android.javacard.keymaster.KMByteBlob; +import com.android.javacard.keymaster.KMCose; +import com.android.javacard.keymaster.KMCoseHeaders; +import com.android.javacard.keymaster.KMCoseKey; +import com.android.javacard.keymaster.KMCosePairByteBlobTag; +import com.android.javacard.keymaster.KMCosePairIntegerTag; +import com.android.javacard.keymaster.KMCosePairNegIntegerTag; +import com.android.javacard.keymaster.KMCosePairSimpleValueTag; +import com.android.javacard.keymaster.KMNInteger; +import com.android.javacard.keymaster.KMRepository; +import com.android.javacard.keymaster.KMSimpleValue; +import com.android.javacard.keymaster.KMJCardSimApplet; +import com.android.javacard.seprovider.KMJCardSimulator; +import com.android.javacard.seprovider.KMSEProvider; +import com.android.javacard.keymaster.KMDecoder; +import com.android.javacard.keymaster.KMEncoder; +import com.android.javacard.keymaster.KMError; +import com.android.javacard.keymaster.KMInteger; +import com.android.javacard.keymaster.KMType; +import com.licel.jcardsim.bouncycastle.util.encoders.Hex; +import com.licel.jcardsim.smartcardio.CardSimulator; +import com.licel.jcardsim.utils.AIDUtil; + +import java.util.ArrayList; +import java.util.Vector; +import javacard.framework.AID; +import javacard.framework.Util; +import javacard.security.KeyPair; + +import javax.smartcardio.CommandAPDU; +import javax.smartcardio.ResponseAPDU; + +import org.junit.Assert; +import org.junit.Test; + +public class KMRKPFunctionalTest { + + // Provider specific Commands + private static final byte KEYMINT_CMD_APDU_START = 0x20; + private static final byte INS_GENERATE_KEY_CMD = KEYMINT_CMD_APDU_START + 1; //0x21 + private static final byte INS_IMPORT_KEY_CMD = KEYMINT_CMD_APDU_START + 2; //0x22 + private static final byte INS_IMPORT_WRAPPED_KEY_CMD = KEYMINT_CMD_APDU_START + 3; //0x23 + private static final byte INS_EXPORT_KEY_CMD = KEYMINT_CMD_APDU_START + 4; //0x24 + private static final byte INS_ATTEST_KEY_CMD = KEYMINT_CMD_APDU_START + 5; //0x25 + private static final byte INS_UPGRADE_KEY_CMD = KEYMINT_CMD_APDU_START + 6; //0x26 + private static final byte INS_DELETE_KEY_CMD = KEYMINT_CMD_APDU_START + 7; //0x27 + private static final byte INS_DELETE_ALL_KEYS_CMD = KEYMINT_CMD_APDU_START + 8; //0x28 + private static final byte INS_ADD_RNG_ENTROPY_CMD = KEYMINT_CMD_APDU_START + 9; //0x29 + private static final byte INS_COMPUTE_SHARED_HMAC_CMD = KEYMINT_CMD_APDU_START + 10; //0x2A + private static final byte INS_DESTROY_ATT_IDS_CMD = KEYMINT_CMD_APDU_START + 11; //0x2B + private static final byte INS_VERIFY_AUTHORIZATION_CMD = KEYMINT_CMD_APDU_START + 12; //0x2C + private static final byte INS_GET_HMAC_SHARING_PARAM_CMD = KEYMINT_CMD_APDU_START + 13; //0x2D + private static final byte INS_GET_KEY_CHARACTERISTICS_CMD = KEYMINT_CMD_APDU_START + 14; //0x2E + private static final byte INS_GET_HW_INFO_CMD = KEYMINT_CMD_APDU_START + 15; //0x2F + private static final byte INS_BEGIN_OPERATION_CMD = KEYMINT_CMD_APDU_START + 16; //0x30 + private static final byte INS_UPDATE_OPERATION_CMD = KEYMINT_CMD_APDU_START + 17; //0x31 + private static final byte INS_FINISH_OPERATION_CMD = KEYMINT_CMD_APDU_START + 18; //0x32 + private static final byte INS_ABORT_OPERATION_CMD = KEYMINT_CMD_APDU_START + 19; //0x33 + private static final byte INS_DEVICE_LOCKED_CMD = KEYMINT_CMD_APDU_START + 20;//0x34 + private static final byte INS_EARLY_BOOT_ENDED_CMD = KEYMINT_CMD_APDU_START + 21; //0x35 + private static final byte INS_GET_CERT_CHAIN_CMD = KEYMINT_CMD_APDU_START + 22; //0x36 + private static final byte INS_UPDATE_AAD_OPERATION_CMD = KEYMINT_CMD_APDU_START + 23; //0x37 + private static final byte INS_BEGIN_IMPORT_WRAPPED_KEY_CMD = KEYMINT_CMD_APDU_START + 24; //0x38 + private static final byte INS_FINISH_IMPORT_WRAPPED_KEY_CMD = KEYMINT_CMD_APDU_START + 25; //0x39 + private static final byte INS_INIT_STRONGBOX_CMD = KEYMINT_CMD_APDU_START + 26; //0x3A + // RKP + public static final byte INS_GET_RKP_HARDWARE_INFO = KEYMINT_CMD_APDU_START + 27; //0x3B + public static final byte INS_GENERATE_RKP_KEY_CMD = KEYMINT_CMD_APDU_START + 28; //0x3C + public static final byte INS_BEGIN_SEND_DATA_CMD = KEYMINT_CMD_APDU_START + 29; //0x3D + public static final byte INS_UPDATE_KEY_CMD = KEYMINT_CMD_APDU_START + 30; //0x3E + public static final byte INS_UPDATE_EEK_CHAIN_CMD = KEYMINT_CMD_APDU_START + 31; //0x3F + public static final byte INS_UPDATE_CHALLENGE_CMD = KEYMINT_CMD_APDU_START + 32; //0x40 + public static final byte INS_FINISH_SEND_DATA_CMD = KEYMINT_CMD_APDU_START + 33; //0x41 + public static final byte INS_GET_RESPONSE_CMD = KEYMINT_CMD_APDU_START + 34; //0x42 + + private static final byte KEYMINT_CMD_APDU_END = KEYMINT_CMD_APDU_START + 48; //0x50 + private static final byte INS_END_KM_CMD = 0x7F; + + public static byte[] CSR_CHALLENGE = {0x56, 0x78, 0x65, 0x23, (byte) 0xFE, 0x32}; + + private CardSimulator simulator; + private KMEncoder encoder; + private KMDecoder decoder; + private KMSEProvider cryptoProvider; + private KMAsn1Parser asn1Parser; + + public KMRKPFunctionalTest() { + cryptoProvider = new KMJCardSimulator(); + simulator = new CardSimulator(); + encoder = new KMEncoder(); + decoder = new KMDecoder(); + } + + private void init() { + // Create simulator + AID appletAID = AIDUtil.create("A000000062"); + simulator.installApplet(appletAID, KMJCardSimApplet.class); + // Select applet + simulator.selectApplet(appletAID); + // provision attest key + KMProvision.provisionCmd(simulator, cryptoProvider, encoder, decoder); + } + + private void cleanUp() { + AID appletAID = AIDUtil.create("A000000062"); + // Delete i.e. uninstall applet + simulator.deleteApplet(appletAID); + } + + //---------------------------------------------------------------------------------------------- + // RKP Tests + //---------------------------------------------------------------------------------------------- + @Test + public void testNegativeInteger() { + init(); + short ptr = KMArray.instance((short) 3); + int a = 0xF0000056; + byte[] a_b1 = {(byte) 0xF0, 0x00, 0x00, 0x56}; + KMArray.cast(ptr).add((short) 0, KMNInteger.uint_32(a_b1, (short) 0)); + byte[] a_b2 = new byte[]{(byte) 0xF0, 0x00, 0x01, 0x56}; + KMArray.cast(ptr).add((short) 1, KMNInteger.uint_32(a_b2, (short) 0)); + byte[] a_b3 = new byte[]{(byte) 0xF0, 0x10, 0x01, 0x56}; + KMArray.cast(ptr).add((short) 2, KMNInteger.uint_32(a_b3, (short) 0)); + byte[] blob = new byte[256]; + short len = encoder.encode(ptr, blob, (short) 0, (short) 256); + + ptr = KMArray.instance((short) 3); + KMArray.cast(ptr).add((short) 0, KMNInteger.exp()); + KMArray.cast(ptr).add((short) 1, KMNInteger.exp()); + KMArray.cast(ptr).add((short) 2, KMNInteger.exp()); + ptr = decoder.decode(ptr, blob, (short) 0, len); + short a_b1_ptr = KMArray.cast(ptr).get((short) 0); + Assert.assertEquals(0, + Util.arrayCompare(a_b1, (short) 0, + KMNInteger.cast(a_b1_ptr).getBuffer(), + KMNInteger.cast(a_b1_ptr).getStartOff(), (short) 4)); + short a_b2_ptr = KMArray.cast(ptr).get((short) 1); + Assert.assertEquals(0, + Util.arrayCompare(a_b2, (short) 0, + KMNInteger.cast(a_b2_ptr).getBuffer(), + KMNInteger.cast(a_b2_ptr).getStartOff(), (short) 4)); + short a_b3_ptr = KMArray.cast(ptr).get((short) 2); + Assert.assertEquals(0, + Util.arrayCompare(a_b3, (short) 0, + KMNInteger.cast(a_b3_ptr).getBuffer(), + KMNInteger.cast(a_b3_ptr).getStartOff(), (short) 4)); + cleanUp(); + } + + @Test + public void testGetRkpHwInfo() { + init(); + short arrPtr = KMArray.instance((short) 0); + CommandAPDU apdu = KMTestUtils.encodeApdu(encoder, (byte) INS_GET_RKP_HARDWARE_INFO, arrPtr); + ResponseAPDU response = simulator.transmitCommand(apdu); + byte[] resp = response.getBytes(); + KMTestUtils.print(resp, (short) 0, (short) resp.length); + short blobExp = KMByteBlob.exp(); + arrPtr = KMArray.instance((short) 4); + KMArray.cast(arrPtr).add((short) 0, KMInteger.exp()); // ErrorCode + KMArray.cast(arrPtr).add((short) 1, KMInteger.exp()); // Version + KMArray.cast(arrPtr).add((short) 2, blobExp); // Text string + KMArray.cast(arrPtr).add((short) 3, KMInteger.exp()); // support Eek Curve. + byte[] output = new byte[100]; + arrPtr = decoder.decode(arrPtr, resp, (short) 0, (short) resp.length); + Assert.assertEquals(KMError.OK, KMInteger.cast(KMArray.cast(arrPtr).get((short) 0)).getShort()); + byte[] authorName = new byte[6]; + KMByteBlob.cast(KMArray.cast(arrPtr).get((short) 2)).getValue(authorName, (short) 0, (short) 6); + // Validate the author and Eek Curve + byte[] google = {0x47, 0x6F, 0x6F, 0x67, 0x6C, 0x65}; + Assert.assertArrayEquals(google, authorName); + Assert.assertEquals(KMType.RKP_CURVE_P256, + KMInteger.cast(KMArray.cast(arrPtr).get((short) 3)).getShort()); + Assert.assertEquals(1, KMInteger.cast(KMArray.cast(arrPtr).get((short) 1)).getShort()); + cleanUp(); + } + + @Test + public void testRkpGeneratedEcdsaKeyPair() { + init(); + // Running this test case in test mode. + byte[] testHmacKey = new byte[32]; + short ret = generateRkpEcdsaKeyPair(true); + // Prepare exp() for coseMac. + short coseMacArrPtr = KMArray.instance((short) 4); + short coseHeadersExp = KMCoseHeaders.exp(); + KMArray.cast(coseMacArrPtr).add((short) 0, KMByteBlob.exp()); + KMArray.cast(coseMacArrPtr).add((short) 1, coseHeadersExp); + KMArray.cast(coseMacArrPtr).add((short) 2, KMByteBlob.exp()); + KMArray.cast(coseMacArrPtr).add((short) 3, KMByteBlob.exp()); + short byteBlobMac = KMArray.cast(ret).get((short) 1); + short arrPtr = + decoder.decode(coseMacArrPtr, KMByteBlob.cast(byteBlobMac).getBuffer(), + KMByteBlob.cast(byteBlobMac).getStartOff(), + KMByteBlob.cast(byteBlobMac).length()); + // Decode CoseMac0 + short bstrPayloadPtr = KMArray.cast(arrPtr).get((short) 2); + short bstrTagPtr = KMArray.cast(arrPtr).get((short) 3); + short bstrProtectedHptr = KMArray.cast(arrPtr).get((short) 0); + short unprotectedHptr = KMArray.cast(arrPtr).get((short) 1); + // Verify algorithm inside protected header. + arrPtr = KMCoseHeaders.exp();//KMMap.instance((short) 1); + ret = decoder.decode(arrPtr, KMByteBlob.cast(bstrProtectedHptr).getBuffer(), + KMByteBlob.cast(bstrProtectedHptr).getStartOff(), + KMByteBlob.cast(bstrProtectedHptr).length()); + short[] scratchBuffer = new short[10]; + Assert.assertTrue(KMCoseHeaders.cast(ret) + .isDataValid(scratchBuffer, KMCose.COSE_ALG_HMAC_256, KMType.INVALID_VALUE)); + // Verify that unprotected header length is 0. + Assert.assertEquals(0, KMCoseHeaders.cast(unprotectedHptr).length()); + // Generate Cose_Mac0 structure and verify the tag. + byte[] output = new byte[256]; + short len = KMTestUtils.generateCoseMac0Mac(cryptoProvider, encoder, testHmacKey, (short) 0, + (short) testHmacKey.length, KMByteBlob.instance((short) 0), bstrPayloadPtr, + bstrProtectedHptr, output, (short) 0, (short) output.length); + if (len != 32) { + Assert.fail("Hmac sign len is not 32"); + } + // Compare the tag values. + Assert.assertEquals(0, + Util.arrayCompare(output, (short) 0, KMByteBlob.cast(bstrTagPtr).getBuffer(), + KMByteBlob.cast(bstrTagPtr).getStartOff(), KMByteBlob.cast(bstrTagPtr).length())); + cleanUp(); + } + + @Test + public void testGenerateCsrTestMode() { + init(); + short[] eekLengths = {2, 3, 9}; + short[] noOfKeys = {0, 5, 10}; + for (int i = 0; i < eekLengths.length; i++) { + testGenerateCsr(noOfKeys[i] /*no_keys*/, eekLengths[i] /*eek_chain_len*/, true /*testMode*/); + KMRepository.instance().clean(); + } + cleanUp(); + } + + @Test + public void testGenerateCsrProdMode() { + init(); + short[] noOfKeys = {0, 5, 10}; + for (int i = 0; i < noOfKeys.length; i++) { + testGenerateCsr(noOfKeys[i] /*no_keys*/, (short) 2 /*eek_chain_len*/, true /*testMode*/); + KMRepository.instance().clean(); + } + cleanUp(); + } + + //---------------------------------------------------------------------------------------------- + // Helper functions + //---------------------------------------------------------------------------------------------- + public short generateRkpEcdsaKeyPair(boolean testMode) { + short arrPtr = KMArray.instance((short) 1); + KMArray.cast(arrPtr).add((short) 0, + KMSimpleValue.instance(testMode ? KMSimpleValue.TRUE : KMSimpleValue.FALSE)); + CommandAPDU apdu = KMTestUtils.encodeApdu(encoder, (byte) INS_GENERATE_RKP_KEY_CMD, arrPtr); + ResponseAPDU response = simulator.transmitCommand(apdu); + byte[] resp = response.getBytes(); + KMTestUtils.print(resp, (short) 0, (short) resp.length); + // Prepare exp for output. + arrPtr = KMArray.instance((short) 3); + KMArray.cast(arrPtr).add((short) 0, KMInteger.exp()); + KMArray.cast(arrPtr).add((short) 1, KMByteBlob.exp()); // bstr of cose mac0 + KMArray.cast(arrPtr).add((short) 2, KMByteBlob.exp()); // keyblob + short ret = decoder.decode(arrPtr, resp, (short) 0, (short) resp.length); + Assert.assertEquals(KMError.OK, KMInteger.cast(KMArray.cast(ret).get((short) 0)).getShort()); + return ret; + } + + public void testGenerateCsr(short no_keys, short no_eek, boolean testMode) { + byte[][] mackedKeys = new byte[no_keys][]; + short ret; + short totalEncodedCoseKeysLen = 0; + for (short i = 0; i < no_keys; i++) { + // Generate RKP Key + ret = generateRkpEcdsaKeyPair(testMode); + // Store CoseMac0 in buffer. + short byteBlobCoseMac0 = KMArray.cast(ret).get((short) 1); + mackedKeys[i] = new byte[KMByteBlob.cast(byteBlobCoseMac0).length()]; + Util.arrayCopy(KMByteBlob.cast(byteBlobCoseMac0).getBuffer(), + KMByteBlob.cast(byteBlobCoseMac0).getStartOff(), mackedKeys[i], (short) 0, + KMByteBlob.cast(byteBlobCoseMac0).length()); + } + short coseKeyArr = KMArray.instance(no_keys); + short coseMacPtr; + short coseKey; + for (short i = 0; i < no_keys; i++) { + coseMacPtr = KMTestUtils.decodeCoseMac(decoder, mackedKeys[i], (short) 0, + (short) mackedKeys[i].length); + coseKey = KMTestUtils.getCoseKeyFromCoseMac(decoder, coseMacPtr); + short payload = KMArray.cast(coseMacPtr).get((short) 2); + totalEncodedCoseKeysLen += KMByteBlob.cast(payload).length(); + KMArray.cast(coseKeyArr).add(i, coseKey); + } + byte[] coseKeyArrBuf = new byte[1024]; + short coseKeyArrBufLen = encoder.encode(coseKeyArr, coseKeyArrBuf, (short) 0, (short) 1024); + byte[] encodedCoseKeysArray = new byte[coseKeyArrBufLen]; + Util.arrayCopy(coseKeyArrBuf, (short) 0, encodedCoseKeysArray, (short) 0, coseKeyArrBufLen); + + // begin send data + short arr = KMArray.instance((short) 3); + KMArray.cast(arr).add((short) 0, KMInteger.uint_8((byte) no_keys)); + KMArray.cast(arr).add((short) 1, KMInteger.uint_16(totalEncodedCoseKeysLen)); + KMArray.cast(arr).add((short) 2, + KMSimpleValue.instance(testMode ? KMSimpleValue.TRUE : KMSimpleValue.FALSE)); + CommandAPDU apdu = KMTestUtils.encodeApdu(encoder, (byte) INS_BEGIN_SEND_DATA_CMD, arr); + ResponseAPDU response = simulator.transmitCommand(apdu); + byte[] resp = response.getBytes(); + ret = decoder.decode(KMTestUtils.receiveErrorCodeExp(), resp, (short) 0, (short) resp.length); + Assert.assertEquals(KMTestUtils.getErrorCode(ret), KMError.OK); + + // update data. + for (short i = 0; i < no_keys; i++) { + coseMacPtr = KMTestUtils.decodeCoseMac(decoder, mackedKeys[i], (short) 0, + (short) mackedKeys[i].length); + short coseMacContainer = KMArray.instance((short) 1); + KMArray.cast(coseMacContainer).add((short) 0, coseMacPtr); + apdu = KMTestUtils.encodeApdu(encoder, (byte) INS_UPDATE_KEY_CMD, coseMacContainer); + response = simulator.transmitCommand(apdu); + resp = response.getBytes(); + ret = decoder.decode(KMTestUtils.receiveErrorCodeExp(), resp, (short) 0, (short) resp.length); + Assert.assertEquals(KMTestUtils.getErrorCode(ret), KMError.OK); + } + + // update EEK + byte[] pub = new byte[65]; + byte[] priv = new byte[32]; + short[] lengths = new short[2]; + KeyPair eekKey = null; + short eekArr = KMType.INVALID_VALUE; + byte[] eekId; + if (testMode) { + eekId = new byte[]{0x01, 0x02, 0x03, 0x04}; + eekKey = KMTestUtils.generateEcKeyPair(cryptoProvider, pub, priv, lengths); + eekArr = KMTestUtils.generateEEk(cryptoProvider, encoder, eekKey, eekId, no_eek); + apdu = KMTestUtils.encodeApdu(encoder, (byte) INS_UPDATE_EEK_CHAIN_CMD, eekArr); + } else { /* Production Mode */ + eekId = Hex.decode(KMTestUtils.PROD_EEK_ID); + byte[] eekPub = Hex.decode(KMTestUtils.PROD_PUB_KEY); + eekKey = + KMTestUtils.getEcKeyPair(eekPub, (short) 0, (short) eekPub.length, null, (short) 0, + (short) 0); + short length = (short) (KMTestUtils.kCoseEncodedEcdsa256GeekCert.length + + KMTestUtils.kCoseEncodedEcdsa256RootCert.length); + length += 1; // Array of 2. + byte[] encodedBuf = new byte[length]; + encodedBuf[0] = (byte) 0x82; + Util.arrayCopyNonAtomic( + KMTestUtils.kCoseEncodedEcdsa256RootCert, + (short) 0, + encodedBuf, + (short) 1, + (short) KMTestUtils.kCoseEncodedEcdsa256RootCert.length + ); + Util.arrayCopyNonAtomic( + KMTestUtils.kCoseEncodedEcdsa256GeekCert, + (short) 0, + encodedBuf, + (short) (1 + KMTestUtils.kCoseEncodedEcdsa256RootCert.length), + (short) KMTestUtils.kCoseEncodedEcdsa256GeekCert.length + ); + System.out.println(" PRODUCTION CHAIN"); + KMTestUtils.print(encodedBuf, (short) 0, (short) encodedBuf.length); + apdu = KMTestUtils.encodeApdu(encoder, INS_UPDATE_EEK_CHAIN_CMD, encodedBuf); + } + + //Clean the heap. + KMRepository.instance().clean(); + response = simulator.transmitCommand(apdu); + resp = response.getBytes(); + ret = decoder.decode(KMTestUtils.receiveErrorCodeExp(), resp, (short) 0, (short) resp.length); + Assert.assertEquals(KMTestUtils.getErrorCode(ret), KMError.OK); + + // update Challenge + short challenge = KMByteBlob.instance(CSR_CHALLENGE, (short) 0, (short) CSR_CHALLENGE.length); + short challengeArr = KMArray.instance((short) 1); + KMArray.cast(challengeArr).add((short) 0, challenge); + apdu = KMTestUtils.encodeApdu(encoder, (byte) INS_UPDATE_CHALLENGE_CMD, challengeArr); + response = simulator.transmitCommand(apdu); + resp = response.getBytes(); + ret = decoder.decode(KMTestUtils.receiveErrorCodeExp(), resp, (short) 0, (short) resp.length); + Assert.assertEquals(KMTestUtils.getErrorCode(ret), KMError.OK); + + // finish + // Extended length. + apdu = new CommandAPDU(0x80, INS_FINISH_SEND_DATA_CMD, 0x50, 0x00, (byte[]) null, 65536); + response = simulator.transmitCommand(apdu); + short coseHeadersExp = KMCoseHeaders.exp(); + arr = KMArray.instance((short) 7); + KMArray.cast(arr).add((short) 0, KMInteger.exp()); // OK + KMArray.cast(arr).add((short) 1, KMByteBlob.exp()); // pubKeysToSignMac + KMArray.cast(arr).add((short) 2, KMByteBlob.exp()); // deviceInfo + KMArray.cast(arr).add((short) 3, KMByteBlob.exp()); // CoseEncrypt ProtectedHeader + KMArray.cast(arr).add((short) 4, coseHeadersExp); // CoseEncrypt UnProtectedHeader + KMArray.cast(arr).add((short) 5, KMByteBlob.exp()); // CoseEncrypt partial cipher text. + KMArray.cast(arr).add((short) 6, KMInteger.exp()); // more data + resp = response.getBytes(); + //KMTestUtils.print(resp, (short) 0, (short) resp.length); + ret = decoder.decode(arr, resp, (short) 0, (short) resp.length); + Assert.assertEquals(KMTestUtils.getErrorCode(ret), KMError.OK); + short pubKeysToSignMac = KMArray.cast(ret).get((short) 1); + byte[] pubKeysToSignMacBytes = new byte[KMByteBlob.cast(pubKeysToSignMac).length()]; + Util.arrayCopyNonAtomic( + KMByteBlob.cast(pubKeysToSignMac).getBuffer(), + KMByteBlob.cast(pubKeysToSignMac).getStartOff(), + pubKeysToSignMacBytes, + (short) 0, + KMByteBlob.cast(pubKeysToSignMac).length() + ); + short deviceInfo = KMArray.cast(ret).get((short) 2); + byte[] deviceInfoBytes = new byte[512]; + Util.arrayCopyNonAtomic(KMByteBlob.cast(deviceInfo).getBuffer(), + KMByteBlob.cast(deviceInfo).getStartOff(), + deviceInfoBytes, + (short) 0, + KMByteBlob.cast(deviceInfo).length()); + short deviceInfoBytesLen = KMByteBlob.cast(deviceInfo).length(); + short protectedHeader = KMArray.cast(ret).get((short) 3); + byte[] protectedHeaderBytes = new byte[KMByteBlob.cast(protectedHeader).length()]; + Util.arrayCopyNonAtomic( + KMByteBlob.cast(protectedHeader).getBuffer(), + KMByteBlob.cast(protectedHeader).getStartOff(), + protectedHeaderBytes, + (short) 0, + KMByteBlob.cast(protectedHeader).length() + ); + short UnProtectedHeader = KMArray.cast(ret).get((short) 4); + byte[] unProtectedHeaderBytes = new byte[256]; + short unProtectedHeaderBytesLen = encoder.encode(UnProtectedHeader, unProtectedHeaderBytes, + (short) 0, (short) 256); + short cipherObj = KMArray.cast(ret).get((short) 5); + byte[] cipher = new byte[5000]; + short startOffset = 0; + Util.arrayCopyNonAtomic(KMByteBlob.cast(cipherObj).getBuffer(), + KMByteBlob.cast(cipherObj).getStartOff(), cipher, + (short) 0, KMByteBlob.cast(cipherObj).length()); + startOffset = KMByteBlob.cast(cipherObj).length(); + byte moreData = KMInteger.cast(KMArray.cast(ret).get((short) 6)).getByte(); + + short recipientStruct = KMType.INVALID_VALUE; + while (moreData != 0) { + apdu = new CommandAPDU(0x80, INS_GET_RESPONSE_CMD, 0x50, 0x00, (byte[]) null, 65536); + response = simulator.transmitCommand(apdu); + arr = KMArray.instance((short) 4); + // Prepare recipients expression. + short byteBlobExp = KMByteBlob.exp(); + coseHeadersExp = KMCoseHeaders.exp(); + short recipient = KMArray.instance((short) 3); + KMArray.cast(recipient).add((short) 0, byteBlobExp); + KMArray.cast(recipient).add((short) 1, coseHeadersExp); + KMArray.cast(recipient).add((short) 2, KMSimpleValue.exp()); + short recipientsExp = KMArray.exp(recipient); + KMArray.cast(arr).add((short) 0, KMInteger.exp()); // OK + KMArray.cast(arr).add((short) 1, byteBlobExp); // partial cipherText + KMArray.cast(arr).add((short) 2, recipientsExp); // recipient structure + KMArray.cast(arr).add((short) 3, KMInteger.exp()); // more data + resp = response.getBytes(); + ret = decoder.decode(arr, resp, (short) 0, (short) resp.length); + Assert.assertEquals(KMTestUtils.getErrorCode(ret), KMError.OK); + moreData = KMInteger.cast(KMArray.cast(ret).get((short) 3)).getByte(); + recipientStruct = KMArray.cast(ret).get((short) 2); + cipherObj = KMArray.cast(ret).get((short) 1); + Util.arrayCopyNonAtomic(KMByteBlob.cast(cipherObj).getBuffer(), + KMByteBlob.cast(cipherObj).getStartOff(), cipher, + startOffset, KMByteBlob.cast(cipherObj).length()); + startOffset += KMByteBlob.cast(cipherObj).length(); + } + short cipherLength = startOffset; + short cipherByteBlob = KMByteBlob.instance(cipher, (short) 0, cipherLength); + protectedHeader = KMByteBlob.instance(protectedHeaderBytes, (short) 0, + (short) protectedHeaderBytes.length); + UnProtectedHeader = decoder.decode(KMCoseHeaders.exp(), unProtectedHeaderBytes, (short) 0, + unProtectedHeaderBytesLen); + short protectedDataArrPtr = + KMCose.constructCoseEncrypt(protectedHeader, UnProtectedHeader, cipherByteBlob, + recipientStruct); + + //Verify code + KMTestUtils.print(deviceInfoBytes, (short) 0, deviceInfoBytesLen); + deviceInfo = decoder.decode(KMTestUtils.getDeviceInfoExp(), deviceInfoBytes, (short) 0, + deviceInfoBytesLen); + pubKeysToSignMac = KMByteBlob.instance(pubKeysToSignMacBytes, (short) 0, + (short) pubKeysToSignMacBytes.length); + // In Production mode we cannot validate the protected data since we don't have the + // EEK Private key. + if (testMode) { + KMTestUtils.validateProtectedData(cryptoProvider, encoder, decoder, eekId, eekKey, + CSR_CHALLENGE, encodedCoseKeysArray, + testMode, protectedDataArrPtr, deviceInfo, pubKeysToSignMac); + } + } + +} diff --git a/Applet/JCardSimProvider/test/com/android/javacard/test/KMTestUtils.java b/Applet/JCardSimProvider/test/com/android/javacard/test/KMTestUtils.java new file mode 100644 index 00000000..b653d056 --- /dev/null +++ b/Applet/JCardSimProvider/test/com/android/javacard/test/KMTestUtils.java @@ -0,0 +1,1103 @@ +package com.android.javacard.test; + +import com.android.javacard.keymaster.KMArray; +import com.android.javacard.keymaster.KMAsn1Parser; +import com.android.javacard.keymaster.KMByteBlob; +import com.android.javacard.keymaster.KMCose; +import com.android.javacard.keymaster.KMCoseCertPayload; +import com.android.javacard.keymaster.KMCoseHeaders; +import com.android.javacard.keymaster.KMCoseKey; +import com.android.javacard.keymaster.KMCosePairIntegerTag; +import com.android.javacard.keymaster.KMCosePairNegIntegerTag; +import com.android.javacard.keymaster.KMDecoder; +import com.android.javacard.keymaster.KMEncoder; +import com.android.javacard.keymaster.KMError; +import com.android.javacard.keymaster.KMInteger; +import com.android.javacard.keymaster.KMKeyCharacteristics; +import com.android.javacard.keymaster.KMKeyParameters; +import com.android.javacard.keymaster.KMKeymasterApplet; +import com.android.javacard.keymaster.KMMap; +import com.android.javacard.keymaster.KMNInteger; +import com.android.javacard.keymaster.KMSimpleValue; +import com.android.javacard.keymaster.KMTextString; +import com.android.javacard.keymaster.KMType; +import com.android.javacard.seprovider.KMDeviceUniqueKeyPair; +import com.android.javacard.seprovider.KMECPrivateKey; +import com.android.javacard.seprovider.KMException; +import com.android.javacard.seprovider.KMSEProvider; +import javacard.framework.ISO7816; +import javacard.framework.ISOException; +import javacard.framework.Util; +import javacard.security.ECPrivateKey; +import javacard.security.ECPublicKey; +import javacard.security.KeyBuilder; +import javacard.security.KeyPair; +import javax.smartcardio.CommandAPDU; +import javax.smartcardio.ResponseAPDU; +import org.junit.Assert; + +public class KMTestUtils { + + public static byte[] kCoseEncodedEcdsa256RootCert = { + (byte) 0x84, (byte) 0x43, (byte) 0xa1, (byte) 0x01, (byte) 0x26, (byte) 0xa0, (byte) 0x58, + (byte) 0x4d, (byte) 0xa5, (byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x26, (byte) 0x20, + (byte) 0x01, (byte) 0x21, + 0x58, (byte) 0x20, (byte) 0xf7, (byte) 0x14, (byte) 0x8a, (byte) 0xdb, (byte) 0x97, + (byte) 0xf4, (byte) 0xcc, (byte) 0x53, (byte) 0xef, (byte) 0xd2, (byte) 0x64, (byte) 0x11, + (byte) 0xc4, (byte) 0xe3, + 0x75, (byte) 0x1f, (byte) 0x66, (byte) 0x1f, (byte) 0xa4, (byte) 0x71, (byte) 0x0c, + (byte) 0x6c, (byte) 0xcf, (byte) 0xfa, (byte) 0x09, (byte) 0x46, (byte) 0x80, (byte) 0x74, + (byte) 0x87, (byte) 0x54, + (byte) 0xf2, (byte) 0xad, (byte) 0x22, (byte) 0x58, (byte) 0x20, (byte) 0x5e, (byte) 0x7f, + (byte) 0x5b, (byte) 0xf6, (byte) 0xec, (byte) 0xe4, (byte) 0xf6, (byte) 0x19, (byte) 0xcc, + (byte) 0xff, (byte) 0x13, + 0x37, (byte) 0xfd, (byte) 0x0f, (byte) 0xa1, (byte) 0xc8, (byte) 0x93, (byte) 0xdb, + (byte) 0x18, (byte) 0x06, (byte) 0x76, (byte) 0xc4, (byte) 0x5d, (byte) 0xe6, (byte) 0xd7, + (byte) 0x6a, (byte) 0x77, + (byte) 0x86, (byte) 0xc3, (byte) 0x2d, (byte) 0xaf, (byte) 0x8f, (byte) 0x58, (byte) 0x40, + (byte) 0x2f, (byte) 0x97, (byte) 0x8e, (byte) 0x42, (byte) 0xfb, (byte) 0xbe, (byte) 0x07, + (byte) 0x2d, (byte) 0x95, + 0x47, (byte) 0x85, (byte) 0x47, (byte) 0x93, (byte) 0x40, (byte) 0xb0, (byte) 0x1f, + (byte) 0xd4, (byte) 0x9b, (byte) 0x47, (byte) 0xa4, (byte) 0xc4, (byte) 0x44, (byte) 0xa9, + (byte) 0xf2, (byte) 0xa1, + 0x07, (byte) 0x87, (byte) 0x10, (byte) 0xc7, (byte) 0x9f, (byte) 0xcb, (byte) 0x11, + (byte) 0xf4, (byte) 0xbf, (byte) 0x9f, (byte) 0xe8, (byte) 0x3b, (byte) 0xe0, (byte) 0xe7, + (byte) 0x34, (byte) 0x4c, + 0x15, (byte) 0xfc, (byte) 0x7b, (byte) 0xc3, (byte) 0x7e, (byte) 0x33, (byte) 0x05, + (byte) 0xf4, (byte) 0xd1, (byte) 0x34, (byte) 0x3c, (byte) 0xed, (byte) 0x02, (byte) 0x04, + (byte) 0x60, (byte) 0x7a, + 0x15, (byte) 0xe0, (byte) 0x79, (byte) 0xd3, (byte) 0x8a, (byte) 0xff, (byte) 0x24}; + + // The Google ECDSA P256 Endpoint Encryption Key certificate, encoded as COSE_Sign1 + public static byte[] kCoseEncodedEcdsa256GeekCert = { + (byte) 0x84, (byte) 0x43, (byte) 0xa1, (byte) 0x01, (byte) 0x26, (byte) 0xa0, (byte) 0x58, + (byte) 0x71, (byte) 0xa6, (byte) 0x01, (byte) 0x02, (byte) 0x02, (byte) 0x58, (byte) 0x20, + (byte) 0x35, (byte) 0x73, + (byte) 0xb7, (byte) 0x3f, (byte) 0xa0, (byte) 0x8a, (byte) 0x80, (byte) 0x89, (byte) 0xb1, + (byte) 0x26, (byte) 0x67, (byte) 0xe9, (byte) 0xcb, (byte) 0x7c, (byte) 0x75, (byte) 0xa1, + (byte) 0xaf, (byte) 0x02, + 0x61, (byte) 0xfc, (byte) 0x6e, (byte) 0x65, (byte) 0x03, (byte) 0x91, (byte) 0x3b, + (byte) 0xd3, (byte) 0x4b, (byte) 0x7d, (byte) 0x14, (byte) 0x94, (byte) 0x3e, (byte) 0x46, + (byte) 0x03, (byte) 0x38, + 0x18, (byte) 0x20, (byte) 0x01, (byte) 0x21, (byte) 0x58, (byte) 0x20, (byte) 0xe0, + (byte) 0x41, (byte) 0xcf, (byte) 0x2f, (byte) 0x0f, (byte) 0x34, (byte) 0x0f, (byte) 0x1c, + (byte) 0x33, (byte) 0x2c, + 0x41, (byte) 0xb0, (byte) 0xcf, (byte) 0xd7, (byte) 0x0c, (byte) 0x30, (byte) 0x55, + (byte) 0x35, (byte) 0xd2, (byte) 0x1e, (byte) 0x6a, (byte) 0x47, (byte) 0x13, (byte) 0x4b, + (byte) 0x2e, (byte) 0xd1, + 0x48, (byte) 0x96, (byte) 0x7e, (byte) 0x24, (byte) 0x9c, (byte) 0x68, (byte) 0x22, + (byte) 0x58, (byte) 0x20, (byte) 0x1f, (byte) 0xce, (byte) 0x45, (byte) 0xc5, (byte) 0xfb, + (byte) 0x61, (byte) 0xba, + (byte) 0x81, (byte) 0x21, (byte) 0xf9, (byte) 0xe5, (byte) 0x05, (byte) 0x9b, (byte) 0x9b, + (byte) 0x39, (byte) 0x0e, (byte) 0x76, (byte) 0x86, (byte) 0x86, (byte) 0x47, (byte) 0xb8, + (byte) 0x1e, (byte) 0x2f, + 0x45, (byte) 0xf1, (byte) 0xce, (byte) 0xaf, (byte) 0xda, (byte) 0x3f, (byte) 0x80, + (byte) 0x68, (byte) 0xdb, (byte) 0x58, (byte) 0x40, (byte) 0x8c, (byte) 0xb3, (byte) 0xba, + (byte) 0x7e, (byte) 0x20, + 0x3e, (byte) 0x32, (byte) 0xb0, (byte) 0x68, (byte) 0xdf, (byte) 0x60, (byte) 0xd1, + (byte) 0x1d, (byte) 0x7d, (byte) 0xf0, (byte) 0xac, (byte) 0x38, (byte) 0x8e, (byte) 0x51, + (byte) 0xbc, (byte) 0xff, + 0x6c, (byte) 0xe1, (byte) 0x67, (byte) 0x3b, (byte) 0x4a, (byte) 0x79, (byte) 0xbc, + (byte) 0x56, (byte) 0x78, (byte) 0xb3, (byte) 0x99, (byte) 0xd8, (byte) 0x7c, (byte) 0x8a, + (byte) 0x07, (byte) 0xd8, + (byte) 0xda, (byte) 0xb5, (byte) 0xb5, (byte) 0x7f, (byte) 0x71, (byte) 0xf4, (byte) 0xd8, + (byte) 0x6b, (byte) 0xdf, (byte) 0x33, (byte) 0x27, (byte) 0x34, (byte) 0x7b, (byte) 0x65, + (byte) 0xd1, (byte) 0x2a, + (byte) 0xeb, (byte) 0x86, (byte) 0x99, (byte) 0x98, (byte) 0xab, (byte) 0x3a, (byte) 0xb4, + (byte) 0x80, (byte) 0xaa, (byte) 0xbd, (byte) 0x50}; + + public static final short ADDITIONAL_MASK = 0x1F; + private static final short UINT8_LENGTH = 0x18; + private static final short UINT16_LENGTH = 0x19; + public static final short MAJOR_TYPE_MASK = 0xE0; + public static final byte CBOR_ARRAY_MAJOR_TYPE = (byte) 0x80; + public static final byte CBOR_UINT_MAJOR_TYPE = 0x00; + public static final short SE_POWER_RESET_FLAG = (short) 0x4000; + public static final String PROD_EEK_ID = + "3573B73FA08A8089B12667E9CB7C75A1AF0261FC6E6503913BD34B7D14943E46"; + public static final String PROD_PUB_KEY = + "04E041CF2F0F340F1C332C41B0CFD70C305535D21E6A47134B2ED148967E249C681FCE45C5FB61BA812" + + "1F9E5059B9B390E76868647B81E2F45F1CEAFDA3F8068DB"; + public static final short AES_GCM_NONCE_LENGTH = 12; + public static final short AES_GCM_AUTH_TAG_LENGTH = 16; + + public static CommandAPDU encodeApdu(KMEncoder encoder, byte ins, short cmd) { + byte[] buf = new byte[2500]; + buf[0] = (byte) 0x80; + buf[1] = ins; + buf[2] = (byte) 0x50; + buf[3] = (byte) 0x00; + buf[4] = 0; + short len = encoder.encode(cmd, buf, (short) 7, (short) 2500); + Util.setShort(buf, (short) 5, len); + byte[] apdu = new byte[7 + len]; + Util.arrayCopyNonAtomic(buf, (short) 0, apdu, (short) 0, (short) (7 + len)); + print(buf, (short) 0, (short) (len + 7)); + return new CommandAPDU(apdu); + } + + public static CommandAPDU encodeApdu(KMEncoder encoder, byte ins, byte[] encodedCmd) { + byte[] buf = new byte[2500]; + buf[0] = (byte) 0x80; + buf[1] = ins; + buf[2] = (byte) 0x50; + buf[3] = (byte) 0x00; + buf[4] = 0; + Util.arrayCopyNonAtomic(encodedCmd, (short) 0, buf, (short) 7, (short) encodedCmd.length); + Util.setShort(buf, (short) 5, (short) encodedCmd.length); + byte[] apdu = new byte[7 + (short) encodedCmd.length]; + Util.arrayCopyNonAtomic(buf, (short) 0, apdu, (short) 0, + (short) (7 + (short) encodedCmd.length)); + return new CommandAPDU(apdu); + } + + public static byte readMajorType(byte[] resp) { + byte val = resp[0]; + return (byte) (val & MAJOR_TYPE_MASK); + } + + // payload length cannot be more then 16 bits. + public static short readMajorTypeWithPayloadLength(byte[] resp, short majorType) { + short cur = (short) 0; + short payloadLength = 0; + byte val = resp[cur++]; + if ((short) (val & MAJOR_TYPE_MASK) != majorType) { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + short lenType = (short) (val & ADDITIONAL_MASK); + if (lenType > UINT16_LENGTH) { + ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); + } + if (lenType < UINT8_LENGTH) { + payloadLength = lenType; + } else if (lenType == UINT8_LENGTH) { + payloadLength = (short) (resp[cur] & 0xFF); + } else { + payloadLength = Util.getShort(resp, cur); + } + return payloadLength; + } + + public static short decodeCoseMac(KMDecoder decoder, byte[] coseMac, short coseMacOff, + short coseMacLen) { + short arrPtr = KMArray.instance((short) 4); + short coseHeadersExp = KMCoseHeaders.exp(); + KMArray.cast(arrPtr).add((short) 0, KMByteBlob.exp()); + KMArray.cast(arrPtr).add((short) 1, coseHeadersExp); + KMArray.cast(arrPtr).add((short) 2, KMByteBlob.exp()); + KMArray.cast(arrPtr).add((short) 3, KMByteBlob.exp()); + short ret = decoder.decode(arrPtr, coseMac, coseMacOff, coseMacLen); + return ret; + } + + public static short getCoseKeyFromCoseMac(KMDecoder decoder, short coseMacPtr) { + short payload = KMArray.cast(coseMacPtr).get((short) 2); + return decoder.decode(KMCoseKey.exp(), KMByteBlob.cast(payload).getBuffer(), + KMByteBlob.cast(payload).getStartOff(), + KMByteBlob.cast(payload).length()); + } + + public static short getDeviceInfoExp() { + short textStrExp = KMTextString.exp(); + short byteBlobExp = KMByteBlob.exp(); + short intExp = KMInteger.exp(); + short map = KMMap.instance((short) 14); + // Canonical order is hard-coded. + //brand + KMMap.cast(map).add((short) 0, textStrExp, textStrExp); + // model + KMMap.cast(map).add((short) 1, textStrExp, textStrExp); + //product + KMMap.cast(map).add((short) 2, textStrExp, textStrExp); + // device info version + KMMap.cast(map).add((short) 3, textStrExp, intExp); + // vb state + KMMap.cast(map).add((short) 4, textStrExp, textStrExp); + // osVersion + KMMap.cast(map).add((short) 5, textStrExp, textStrExp); + // attest id state + KMMap.cast(map).add((short) 6, textStrExp, textStrExp); + //manufacturer + KMMap.cast(map).add((short) 7, textStrExp, textStrExp); + // verified boot hash + KMMap.cast(map).add((short) 8, textStrExp, byteBlobExp); + // security level + KMMap.cast(map).add((short) 9, textStrExp, textStrExp); + // boot patch level + KMMap.cast(map).add((short) 10, textStrExp, intExp); + // bootloader state + KMMap.cast(map).add((short) 11, textStrExp, textStrExp); + // system patch level + KMMap.cast(map).add((short) 12, textStrExp, intExp); + // vendor patch level + KMMap.cast(map).add((short) 13, textStrExp, intExp); + return map; + } + + public static short generateEEk(KMSEProvider cryptoProvider, KMEncoder encoder, KeyPair eekKey, + byte[] eekId, short length) { + byte[] pub = new byte[65]; // EC Public key + byte[] priv = new byte[32]; // EC Private Key + short[] lengths = new short[2]; + KeyPair signingKey = null; + short alg = KMNInteger.uint_8(KMCose.COSE_ALG_ES256); + boolean testMode = true; + short xPtr = 0; + short yPtr = 0; + short keyId = KMType.INVALID_VALUE; + short eekChainArr = KMArray.instance(length); + + for (short i = 0; i < length; i++) { + KeyPair keyPair; + if (i == (length - 1)) { + keyPair = eekKey; + getEcKeys(keyPair, pub, priv, lengths); + } else { + keyPair = generateEcKeyPair(cryptoProvider, pub, priv, lengths); + } + if (i == 0) { // First key is self signed. + signingKey = keyPair; + } + // prepare coseKey and encode it. + if (pub[0] == 0x04) { // uncompressed + short pubLen = lengths[1]; + pubLen = (short) ((pubLen - 1) / 2); + xPtr = KMByteBlob.instance(pub, (short) 1, pubLen); + yPtr = KMByteBlob.instance(pub, (short) (pubLen + 1), pubLen); + } else { + Assert.fail("Not in uncompressed form."); + } + if (i == length - 1) { + alg = KMNInteger.uint_8(KMCose.COSE_ALG_ECDH_ES_HKDF_256); + keyId = KMByteBlob.instance(eekId, (short) 0, (short) eekId.length); + } + short[] scratchBufferEncode = new short[20]; + short coseKey = + KMCose.constructCoseKey(scratchBufferEncode, + KMInteger.uint_8(KMCose.COSE_KEY_TYPE_EC2), + keyId, + alg, + KMType.INVALID_VALUE, + KMInteger.uint_8(KMCose.COSE_ECCURVE_256), + xPtr, + yPtr, + KMType.INVALID_VALUE, + testMode); + byte[] scratchpad = new byte[200]; + short coseKeyEncodedLen = encoder.encode(coseKey, scratchpad, (short) 0, (short) 200); + short payload = KMByteBlob.instance(scratchpad, (short) 0, coseKeyEncodedLen); + //print(KMByteBlob.cast(payload).getBuffer(), KMByteBlob.cast(payload).getStartOff(), + // KMByteBlob.cast(payload).length()); + + // Prepare protectedHeader + short headerPtr = KMCose.constructHeaders(scratchBufferEncode, + KMNInteger.uint_8(KMCose.COSE_ALG_ES256), + KMType.INVALID_VALUE, + KMType.INVALID_VALUE, + KMType.INVALID_VALUE); + // Encode the protected header as byte blob. + byte[] coseHeaders = new byte[200]; + short coseHeadersLen = encoder.encode(headerPtr, coseHeaders, (short) 0, (short) 200); + short protectedHeader = KMByteBlob.instance(coseHeadersLen); + Util.arrayCopyNonAtomic(coseHeaders, (short) 0, KMByteBlob.cast(protectedHeader).getBuffer(), + KMByteBlob.cast(protectedHeader).getStartOff(), coseHeadersLen); + + // prepare Cose Sign_Structure + byte[] coseSignStructureEncoded = new byte[200]; + short coseSignStructureEncodedLen; + short coseSignStructure = + KMCose.constructCoseSignStructure(protectedHeader, KMByteBlob.instance((short) 0), + payload); + coseSignStructureEncodedLen = encoder.encode(coseSignStructure, coseSignStructureEncoded, + (short) 0, (short) 200); + + // Sign the Sign_structure with signingKey. + KMECPrivateKey privateKey = new KMECPrivateKey(signingKey); + short signLen = + cryptoProvider.ecSign256(privateKey, + coseSignStructureEncoded, (short) 0, coseSignStructureEncodedLen, scratchpad, + (short) 0); + short signPtr = KMByteBlob.instance(scratchpad, (short) 0, signLen); + KMAsn1Parser asn1Parser = KMAsn1Parser.instance(); + signLen = asn1Parser.decodeEcdsa256Signature(signPtr, scratchpad, (short) 0); + KMByteBlob.cast(signPtr).setValue(scratchpad, (short) 0, signLen); + + // prepare Cose_Sign1 + short emptyArr = KMArray.instance((short) 0); + KMCoseHeaders.instance(emptyArr); + short coseSign1 = + KMCose.constructCoseSign1(protectedHeader, + KMCoseHeaders.instance(emptyArr), + payload, + signPtr); + + KMArray.cast(eekChainArr).add(i, coseSign1); + + // copy signing key + signingKey = keyPair; + } + return eekChainArr; + } + + public static short receiveErrorCodeExp() { + short arr = KMArray.instance((short) 1); + KMArray.cast(arr).add((short) 0, KMInteger.exp()); + return arr; + } + + public static short getErrorCode(short arr) { + return KMInteger.cast(KMArray.cast(arr).get((short) 0)).getShort(); + } + + public static void validateProtectedData(KMSEProvider cryptoProvider, KMEncoder encoder, + KMDecoder decoder, + byte[] EEK_KEY_ID, KeyPair eekKey, byte[] CsrChallenge, byte[] encodedCoseKeysArray, + boolean testMode, short protectedDataArrPtr, + short deviceInfoMapPtr, + short pubKeysToSignMac) { + Assert.assertEquals(4, KMArray.cast(protectedDataArrPtr).length()); + //-------------------------------------------- + // Validate recipients structure and get the public key. + //-------------------------------------------- + byte[] ephemeralPub = new byte[100]; + byte[] eekKeyId = new byte[EEK_KEY_ID.length]; + short ephemeralPubLen = getSenderPublicKeyAndKeyIdFromRecipientStructure(decoder, EEK_KEY_ID, + protectedDataArrPtr, + ephemeralPub, (short) 0, + eekKeyId, (short) 0, (short) eekKeyId.length); + //-------------------------------------------- + // Derive session key using ECDH HKDF. Alg. + //-------------------------------------------- + byte[] eekPriv = new byte[100]; + byte[] eekPub = new byte[100]; + ECPublicKey ecPublicKey = (ECPublicKey) eekKey.getPublic(); + ECPrivateKey ecPrivateKey = (ECPrivateKey) eekKey.getPrivate(); + short eekPubLen = ecPublicKey.getW(eekPub, (short) 0); + short eekPrivLen = ecPrivateKey.getS(eekPriv, (short) 0); + byte[] sessionKey = new byte[100]; + short sessionKeyLen = + ecdhHkdfDeriveKey(cryptoProvider, encoder, eekPriv, (short) 0, eekPrivLen, eekPub, + (short) 0, eekPubLen, + ephemeralPub, (short) 0, + ephemeralPubLen, sessionKey, (short) 0); + //-------------------------------------------- + // Validate Protected Data and Decrypt the Cose_Encrypt structure using session Key. + // 1. Validate protected header. + // 2. Validate unprotected header. + // 3. Decrypt the protected data. + //-------------------------------------------- + short params = KMArray.cast(protectedDataArrPtr).get((short) 0); + short protectedHeader = params; + params = + decoder.decode(KMCoseHeaders.exp(), KMByteBlob.cast(params).getBuffer(), + KMByteBlob.cast(params).getStartOff(), + KMByteBlob.cast(params).length()); + params = KMCoseHeaders.cast(params).getVals(); + // The length of the protected params is 1 and the algorithm should be AES_GCM. + Assert.assertEquals(1, KMArray.cast(params).length()); + short param = KMArray.cast(params).get((short) 0); + Assert.assertEquals(KMCose.COSE_ALG_AES_GCM_256, + KMInteger.cast(KMCosePairIntegerTag.cast(param).getValuePtr()).getByte()); + // 2. Validate unprotected header. + params = KMArray.cast(protectedDataArrPtr).get((short) 1); + short iv = KMCoseHeaders.cast(params).getIV(); + Assert.assertEquals(AES_GCM_NONCE_LENGTH, KMByteBlob.cast(iv).length()); + // 3. Decrypt the protected data. + byte[] authData = new byte[256]; + short coseEncryptStr = + KMCose.constructCoseEncryptStructure(protectedHeader, KMByteBlob.instance((short) 0)); + short authDataLen = encoder.encode(coseEncryptStr, authData, (short) 0, (short) 256); + short cipherText = KMArray.cast(protectedDataArrPtr).get((short) 2); + byte[] authTag = new byte[AES_GCM_AUTH_TAG_LENGTH]; + short encryptedDataLen = (short) (KMByteBlob.cast(cipherText).length() + - AES_GCM_AUTH_TAG_LENGTH); + byte[] encryptedData = new byte[encryptedDataLen]; + Util.arrayCopyNonAtomic(KMByteBlob.cast(cipherText).getBuffer(), + KMByteBlob.cast(cipherText).getStartOff(), + encryptedData, (short) 0, encryptedDataLen); + Util.arrayCopyNonAtomic(KMByteBlob.cast(cipherText).getBuffer(), + (short) (encryptedDataLen + KMByteBlob.cast(cipherText).getStartOff()), + authTag, (short) 0, AES_GCM_AUTH_TAG_LENGTH); + byte[] plainText = new byte[encryptedDataLen]; + boolean valid = + cryptoProvider.aesGCMDecrypt( + sessionKey, + (short) 0, + sessionKeyLen, + encryptedData, + (short) 0, + encryptedDataLen, + plainText, + (short) 0, + KMByteBlob.cast(iv).getBuffer(), + KMByteBlob.cast(iv).getStartOff(), + KMByteBlob.cast(iv).length(), + authData, + (short) 0, + authDataLen, + authTag, + (short) 0, + AES_GCM_AUTH_TAG_LENGTH + ); + Assert.assertTrue(valid); + //-------------------------------------------- + // Validate the decrypted payload. + // payload = [signedMac + bcc + ? AdditionalCertChain] + //-------------------------------------------- + short payloadLength = 3; + short additionalCertChain = 0; + short headersExp = KMCoseHeaders.exp(); + short coseKeyExp = KMCoseKey.exp(); + short signedMacArr = KMArray.instance((short) 4); + KMArray.cast(signedMacArr).add((short) 0, KMByteBlob.exp()); + KMArray.cast(signedMacArr).add((short) 1, headersExp); + KMArray.cast(signedMacArr).add((short) 2, KMByteBlob.exp()); + KMArray.cast(signedMacArr).add((short) 3, KMByteBlob.exp()); + // bcc exp + short bccArr = KMArray.instance((short) 2); + KMArray.cast(bccArr).add((short) 0, coseKeyExp); + KMArray.cast(bccArr).add((short) 1, signedMacArr); + //if (!testMode) { + short headers = KMCoseHeaders.exp(); + short arrInst = KMArray.instance((short) 4); + KMArray.cast(arrInst).add((short) 0, KMByteBlob.exp()); + KMArray.cast(arrInst).add((short) 1, headers); + KMArray.cast(arrInst).add((short) 2, KMByteBlob.exp()); + KMArray.cast(arrInst).add((short) 3, KMByteBlob.exp()); + short coseSignArr = KMArray.exp(arrInst); + additionalCertChain = KMMap.instance((short) 1); + KMMap.cast(additionalCertChain).add((short) 0, KMTextString.exp(), coseSignArr); + // protected payload exp + short payload = KMArray.instance(payloadLength); + KMArray.cast(payload).add((short) 0, signedMacArr); + KMArray.cast(payload).add((short) 1, bccArr); + if (additionalCertChain != 0) { + KMArray.cast(payload).add((short) 2, additionalCertChain); + } + short payloadPtr = decoder.decode(payload, plainText, (short) 0, encryptedDataLen); + byte[] pub = new byte[100]; + //-------------------------------------------- + // Validate BCC and get public key. + //-------------------------------------------- + short pubLen = getBccPublicKey(cryptoProvider, encoder, decoder, + KMArray.cast(payloadPtr).get((short) 1), pub, (short) 0); + //-------------------------------------------- + // Validate Signed MacPtr. + //-------------------------------------------- + validateSignedMac(cryptoProvider, encoder, decoder, CsrChallenge, encodedCoseKeysArray, + KMArray.cast(payloadPtr).get((short) 0), pub, (short) 0, pubLen, + deviceInfoMapPtr, + pubKeysToSignMac); + //-------------------------------------------- + // Validate Additional certificate chain. + //-------------------------------------------- + if (!testMode) { + short addCertChain = KMArray.cast(payloadPtr).get((short) 2); + addCertChain = KMMap.cast(addCertChain).getKeyValue((short) 0); + Assert.assertTrue( + validateCertChain(cryptoProvider, encoder, decoder, KMCose.COSE_ALG_ES256, + KMCose.COSE_ALG_ES256, addCertChain)); + } + } + + public static boolean validateCertChain(KMSEProvider cryptoProvider, KMEncoder encoder, + KMDecoder decoder, byte expCertAlg, byte expLeafCertAlg, + short certChainArr) { + byte[] scratchPad = new byte[500]; + short offset = 0; + short len = KMArray.cast(certChainArr).length(); + short coseHeadersExp = KMCoseHeaders.exp(); + //prepare exp for coseky + short coseKeyExp = KMCoseKey.exp(); + short ptr1; + short ptr2; + short signStructure; + short encodedLen; + short prevCoseKey = 0; + short keySize; + short alg = expCertAlg; + short[] shortScratchBuffer = new short[20]; + short index; + for (index = 0; index < len; index++) { + ptr1 = KMArray.cast(certChainArr).get(index); + + // validate protected Headers + ptr2 = KMArray.cast(ptr1).get(KMCose.COSE_SIGN1_PROTECTED_PARAMS_OFFSET); + ptr2 = decoder.decode(coseHeadersExp, KMByteBlob.cast(ptr2).getBuffer(), + KMByteBlob.cast(ptr2).getStartOff(), KMByteBlob.cast(ptr2).length()); + if (!KMCoseHeaders.cast(ptr2).isDataValid(shortScratchBuffer, alg, KMType.INVALID_VALUE)) { + return false; + } + + // parse and get the public key from payload. + ptr2 = KMArray.cast(ptr1).get(KMCose.COSE_SIGN1_PAYLOAD_OFFSET); + ptr2 = decoder.decode(coseKeyExp, KMByteBlob.cast(ptr2).getBuffer(), + KMByteBlob.cast(ptr2).getStartOff(), KMByteBlob.cast(ptr2).length()); + if (index == len - 1) { + alg = expLeafCertAlg; + } + if (!KMCoseKey.cast(ptr2) + .isDataValid(shortScratchBuffer, KMCose.COSE_KEY_TYPE_EC2, KMType.INVALID_VALUE, alg, + KMType.INVALID_VALUE, KMCose.COSE_ECCURVE_256)) { + return false; + } + if (prevCoseKey == 0) { + prevCoseKey = ptr2; + } + // Get the public key. + keySize = KMCoseKey.cast(prevCoseKey).getEcdsa256PublicKey(scratchPad, offset); + if (keySize != 65) { + return false; + } + // Validate signature. + signStructure = + KMCose.constructCoseSignStructure( + KMArray.cast(ptr1).get(KMCose.COSE_SIGN1_PROTECTED_PARAMS_OFFSET), + KMByteBlob.instance((short) 0), + KMArray.cast(ptr1).get(KMCose.COSE_SIGN1_PAYLOAD_OFFSET)); + encodedLen = encoder.encode(signStructure, scratchPad, (short) (offset + keySize), + (short) 500); + + if (!cryptoProvider.ecVerify256(scratchPad, offset, keySize, scratchPad, + (short) (offset + keySize), encodedLen, + KMByteBlob.cast(KMArray.cast(ptr1).get(KMCose.COSE_SIGN1_SIGNATURE_OFFSET)).getBuffer(), + KMByteBlob.cast(KMArray.cast(ptr1).get(KMCose.COSE_SIGN1_SIGNATURE_OFFSET)).getStartOff(), + KMByteBlob.cast(KMArray.cast(ptr1).get(KMCose.COSE_SIGN1_SIGNATURE_OFFSET)).length())) { + return false; + } + prevCoseKey = ptr2; + } + return true; + } + + public static boolean isSignedByte(byte b) { + return ((b & 0x0080) != 0); + } + + public static short writeIntegerHeader(short valueLen, byte[] data, short offset) { + // write length + data[offset] = (byte) valueLen; + // write INTEGER tag + offset--; + data[offset] = 0x02; + return offset; + } + + public static short writeSequenceHeader(short valueLen, byte[] data, short offset) { + // write length + data[offset] = (byte) valueLen; + // write INTEGER tag + offset--; + data[offset] = 0x30; + return offset; + } + + public static short writeSignatureData(byte[] input, short inputOff, short inputlen, + byte[] output, + short offset) { + Util.arrayCopyNonAtomic(input, inputOff, output, offset, inputlen); + if (isSignedByte(input[inputOff])) { + offset--; + output[offset] = (byte) 0; + } + return offset; + } + + public static short encodeES256CoseSignSignature(byte[] input, short offset, short len, + byte[] scratchPad, short scratchPadOff) { + // SEQ [ INTEGER(r), INTEGER(s)] + // write from bottom to the top + if (len != 64) { + KMException.throwIt(KMError.INVALID_DATA); + } + short maxTotalLen = 72; + short end = (short) (scratchPadOff + maxTotalLen); + // write s. + short start = (short) (end - 32); + start = writeSignatureData(input, (short) (offset + 32), (short) 32, scratchPad, start); + // write length and header + short length = (short) (end - start); + start--; + start = writeIntegerHeader(length, scratchPad, start); + // write r + short rEnd = start; + start = (short) (start - 32); + start = writeSignatureData(input, offset, (short) 32, scratchPad, start); + // write length and header + length = (short) (rEnd - start); + start--; + start = writeIntegerHeader(length, scratchPad, start); + // write length and sequence header + length = (short) (end - start); + start--; + start = writeSequenceHeader(length, scratchPad, start); + length = (short) (end - start); + if (start > scratchPadOff) { + // re adjust the buffer + Util.arrayCopyNonAtomic(scratchPad, start, scratchPad, scratchPadOff, length); + } + return length; + } + + public static short getBccPublicKey(KMSEProvider cryptoProvider, KMEncoder encoder, + KMDecoder decoder, short bccPtr, byte[] pub, short pubOff) { + short len = KMArray.cast(bccPtr).length(); + short pubKeyLen = 0; + short prevCoseKey = KMArray.cast(bccPtr).get((short) 0); + for (short index = 1; index < len; index++) { + //-------------------------------------------- + // Validate Cose_Sign1 + //-------------------------------------------- + short coseSign1Arr = KMArray.cast(bccPtr).get(index); + // Validate protected Header. + short headers = KMArray.cast(coseSign1Arr).get((short) 0); + short protectedHeader = headers; + headers = + decoder.decode(KMCoseHeaders.exp(), KMByteBlob.cast(headers).getBuffer(), + KMByteBlob.cast(headers).getStartOff(), KMByteBlob.cast(headers).length()); + Assert.assertEquals(KMCose.COSE_ALG_ES256, + (byte) KMNInteger.cast(KMCoseHeaders.cast(headers).getAlgorithm()).getShort()); + // Validate unprotected header. + headers = KMArray.cast(coseSign1Arr).get((short) 1); + Assert.assertEquals(0, KMCoseHeaders.cast(headers).length()); + // Get the payload. + short payload = KMArray.cast(coseSign1Arr).get((short) 2); + // Get the signature + short signature = KMArray.cast(coseSign1Arr).get((short) 3); + // Construct COSE_Struct. + short signStructure = + KMCose.constructCoseSignStructure(protectedHeader, KMByteBlob.instance((short) 0), + payload); + byte[] input = new byte[1024]; + short inputLen = encoder.encode(signStructure, input, (short) 0, (short) 1024); + //Get public key from the coseKey. + pubKeyLen = KMCoseKey.cast(prevCoseKey).getEcdsa256PublicKey(pub, pubOff); + byte[] scratchPad = new byte[80]; + short signatureLen = + encodeES256CoseSignSignature( + KMByteBlob.cast(signature).getBuffer(), + KMByteBlob.cast(signature).getStartOff(), + KMByteBlob.cast(signature).length(), + scratchPad, + (short) 0); + // Verify the signature of cose sign1. + Assert.assertTrue( + cryptoProvider.ecVerify256(pub, pubOff, pubKeyLen, input, (short) 0, inputLen, + scratchPad, (short) 0, signatureLen)); + + // Get the public key from the payload. + short certPayload = KMCoseCertPayload.exp(); + short payloadPtr = + decoder.decode(certPayload, KMByteBlob.cast(payload).getBuffer(), + KMByteBlob.cast(payload).getStartOff(), + KMByteBlob.cast(payload).length()); + short coseKeyPtr = KMCoseCertPayload.cast(payloadPtr).getSubjectPublicKey(); + coseKeyPtr = decoder.decode(KMCoseKey.exp(), KMByteBlob.cast(coseKeyPtr).getBuffer(), + KMByteBlob.cast(coseKeyPtr).getStartOff(), KMByteBlob.cast(coseKeyPtr).length()); + prevCoseKey = coseKeyPtr; + } + return pubKeyLen; + } + + public static void validateSignedMac(KMSEProvider cryptoProvider, KMEncoder encoder, + KMDecoder decoder, + byte[] csrChallenge, byte[] encodedCoseKeysArray, short signedMacPtr, byte[] pub, + short pubOff, short pubLen, + short deviceInfoMapPtr, short pubKeysToSignMac) { + //-------------------------------------------- + // Validate Cose_Sign1 + //-------------------------------------------- + short headers = KMArray.cast(signedMacPtr).get((short) 0); + short protectedHeader = headers; + headers = + decoder.decode(KMCoseHeaders.exp(), KMByteBlob.cast(headers).getBuffer(), + KMByteBlob.cast(headers).getStartOff(), KMByteBlob.cast(headers).length()); + Assert.assertEquals(KMCose.COSE_ALG_ES256, + (byte) KMNInteger.cast(KMCoseHeaders.cast(headers).getAlgorithm()).getShort()); + // Validate unprotected header. + headers = KMArray.cast(signedMacPtr).get((short) 1); + Assert.assertEquals(0, KMCoseHeaders.cast(headers).length()); + // Get the payload. + short payload = KMArray.cast(signedMacPtr).get((short) 2); + // Get the signature + short signature = KMArray.cast(signedMacPtr).get((short) 3); + // Prepare Aad [Challenge + deviceInfoMap] + short aad = KMArray.instance((short) 3); + KMArray.cast(aad).add((short) 0, + KMByteBlob.instance(csrChallenge, (short) 0, (short) csrChallenge.length)); + KMArray.cast(aad).add((short) 1, deviceInfoMapPtr); + KMArray.cast(aad).add((short) 2, pubKeysToSignMac); + byte[] aadBuf = new byte[512]; + short aadLen = encoder.encode(aad, aadBuf, (short) 0, (short) 512); + aad = KMByteBlob.instance(aadBuf, (short) 0, aadLen); + // Construct COSE_Struct. + short signStructure = + KMCose.constructCoseSignStructure(protectedHeader, aad, payload); + byte[] input = new byte[1024]; + short inputLen = encoder.encode(signStructure, input, (short) 0, (short) 1024); + byte[] signatureBuf = new byte[80]; + short signatureLen = + encodeES256CoseSignSignature(KMByteBlob.cast(signature).getBuffer(), + KMByteBlob.cast(signature).getStartOff(), + KMByteBlob.cast(signature).length(), signatureBuf, (short) 0); + // Verify the signature of cose sign1. + Assert.assertTrue(cryptoProvider.ecVerify256(pub, pubOff, pubLen, input, (short) 0, inputLen, + signatureBuf, (short) 0, signatureLen)); + //-------------------------------------------- + // Get the ephemeral mac key and verify the signed mac keys. + //-------------------------------------------- + short mac = + constructPubKeysToSignMac(cryptoProvider, encoder, + KMByteBlob.cast(payload).getBuffer(), + KMByteBlob.cast(payload).getStartOff(), + KMByteBlob.cast(payload).length(), + KMByteBlob.instance(encodedCoseKeysArray, (short) 0, + (short) encodedCoseKeysArray.length)); + Assert.assertEquals(0, + Util.arrayCompare( + KMByteBlob.cast(mac).getBuffer(), + KMByteBlob.cast(mac).getStartOff(), + KMByteBlob.cast(pubKeysToSignMac).getBuffer(), + KMByteBlob.cast(pubKeysToSignMac).getStartOff(), + KMByteBlob.cast(pubKeysToSignMac).length() + ) + ); + } + + public static short constructPubKeysToSignMac(KMSEProvider cryptoProvider, KMEncoder encoder, + byte[] ephemeralKey, + short ephemeralKeyOff, short ephemeralKeyLen, short pubKeysToSign) { + short ptr; + short len; + byte[] scratchPad = new byte[2048]; + short[] headerScratchpad = new short[15]; + short headerPtr = KMCose.constructHeaders(headerScratchpad, + KMInteger.uint_8(KMCose.COSE_ALG_HMAC_256), + KMType.INVALID_VALUE, + KMType.INVALID_VALUE, + KMType.INVALID_VALUE); + // Encode the protected header as byte blob. + len = encoder.encode(headerPtr, scratchPad, (short) 0, (short) 2048); + short protectedHeader = KMByteBlob.instance(scratchPad, (short) 0, len); + // create MAC_Structure + ptr = + KMCose.constructCoseMacStructure(protectedHeader, KMByteBlob.instance((short) 0), + pubKeysToSign); + // Encode the Mac_structure and do HMAC_Sign to produce the tag for COSE_MAC0 + len = encoder.encode(ptr, scratchPad, (short) 0, (short) 2048); + ptr = + cryptoProvider.hmacSign( + ephemeralKey, + ephemeralKeyOff, + ephemeralKeyLen, + scratchPad, + (short) 0, + len, + scratchPad, + len // offset + ); + return KMByteBlob.instance(scratchPad, len, ptr); + } + + public static short constructCoseSign1(KMSEProvider cryptoProvider, KMEncoder encoder, + short protectedHeader, short unProtectedHeader, short payload, + short aad, + KMDeviceUniqueKeyPair signingKey) { + byte[] scratchpad = new byte[500]; + short signStructure = KMCose.constructCoseSignStructure(protectedHeader, aad, payload); + signStructure = encoder.encode(signStructure, scratchpad, (short) 0, (short) 500); + short len = cryptoProvider.ecSign256(signingKey, scratchpad, (short) 0, signStructure, + scratchpad, signStructure); + signStructure = KMByteBlob.instance(scratchpad, signStructure, len); + KMAsn1Parser asn1Parser = KMAsn1Parser.instance(); + len = asn1Parser.decodeEcdsa256Signature(signStructure, scratchpad, (short) 0); + KMByteBlob.cast(signStructure).setValue(scratchpad, (short) 0, len); + return KMCose.constructCoseSign1(protectedHeader, unProtectedHeader, payload, signStructure); + } + + public static short constructCoseKey(short keyType, short keyId, short keyAlg, short keyOps, + short curve, + byte[] pubKey, short pubKeyOff, short pubKeyLen, + byte[] priv, short privKeyOff, short privKeyLen, boolean testMode) { + if (pubKey[pubKeyOff] == 0x04) { // uncompressed format + pubKeyOff += 1; + pubKeyLen -= 1; + } + pubKeyLen = (short) (pubKeyLen / 2); + short xPtr = KMByteBlob.instance(pubKey, pubKeyOff, pubKeyLen); + short yPtr = KMByteBlob.instance(pubKey, (short) (pubKeyOff + pubKeyLen), pubKeyLen); + short privPtr = KMByteBlob.instance(priv, privKeyOff, privKeyLen); + short[] scratchpad = new short[20]; + short coseKey = KMCose.constructCoseKey(scratchpad, keyType, keyId, keyAlg, keyOps, curve, xPtr, + yPtr, privPtr, testMode); + KMCoseKey.cast(coseKey).canonicalize(); + return coseKey; + } + + public static short getSenderPublicKeyAndKeyIdFromRecipientStructure(KMDecoder decoder, + byte[] EEK_KEY_ID, short protectedDataArrPtr, + byte[] pub, short pubOff, + byte[] eekId, short eekIdOff, short eekIdLen) { + //-------------------------------------------- + // Get Recipients and validate recipients + //-------------------------------------------- + short recipientsArr = KMArray.cast(protectedDataArrPtr).get((short) 3); + // recipients array should contain only 1 recipient. + Assert.assertEquals(1, KMArray.cast(recipientsArr).length()); + short recipient = KMArray.cast(recipientsArr).get((short) 0); + // The recipient should be an array of length 3. + Assert.assertEquals(3, KMArray.cast(recipient).length()); + // The 3rd element inside the recipient should be an null value of simple type. + short simplePtr = KMArray.cast(recipient).get((short) 2); + Assert.assertEquals(KMSimpleValue.NULL, KMSimpleValue.cast(simplePtr).getValue()); + //-------------------------------------------- + // Get and validate protected parameters inside the recipient structure. + //-------------------------------------------- + short params = KMArray.cast(recipient).get((short) 0); + //print(KMByteBlob.cast(params).getBuffer(), + //KMByteBlob.cast(params).getStartOff(), KMByteBlob.cast(params).length()); + params = + decoder.decode(KMCoseHeaders.exp(), KMByteBlob.cast(params).getBuffer(), + KMByteBlob.cast(params).getStartOff(), KMByteBlob.cast(params).length()); + params = KMCoseHeaders.cast(params).getVals(); + // The length of the protected params is 1 and the algorithm should be ECDH_ES_HKDF_256. + Assert.assertEquals(1, KMArray.cast(params).length()); + short param = KMArray.cast(params).get((short) 0); + Assert.assertEquals(KMCose.COSE_ALG_ECDH_ES_HKDF_256, + (byte) KMNInteger.cast(KMCosePairNegIntegerTag.cast(param).getValuePtr()).getShort()); + //-------------------------------------------- + // Get and validate unprotected parameters inside the recipient structure. + //-------------------------------------------- + params = KMArray.cast(recipient).get((short) 1); + short coseKey = KMCoseHeaders.cast(params).getCoseKey(); + //-------------------------------------------- + // Validate the COSE_Key. + //-------------------------------------------- + short[] scratchBuffer = new short[20]; + Assert.assertTrue( + KMCoseKey.cast(coseKey) + .isDataValid(scratchBuffer, KMCose.COSE_KEY_TYPE_EC2, KMType.INVALID_VALUE, + KMCose.COSE_ALG_ES256, + KMType.INVALID_VALUE, KMCose.COSE_ECCURVE_256)); + //-------------------------------------------- + // Validate the EEK Key id. + //-------------------------------------------- + short receivedEekId = KMCoseHeaders.cast(params).getKeyIdentifier(); + Assert.assertEquals(eekIdLen, KMByteBlob.cast(receivedEekId).length()); + Assert.assertEquals(0, + Util.arrayCompare(EEK_KEY_ID, (short) 0, KMByteBlob.cast(receivedEekId).getBuffer(), + KMByteBlob.cast(receivedEekId).getStartOff(), eekIdLen)); + Util.arrayCopyNonAtomic(KMByteBlob.cast(receivedEekId).getBuffer(), + KMByteBlob.cast(receivedEekId).getStartOff(), eekId, eekIdOff, eekIdLen); + return KMCoseKey.cast(coseKey).getEcdsa256PublicKey(pub, pubOff); + } + + public static short ecdhHkdfDeriveKey(KMSEProvider cryptoProvider, KMEncoder encoder, + byte[] privKeyA, short privKeyAOff, short privKeyALen, + byte[] pubKeyA, + short pubKeyAOff, short pubKeyALen, byte[] pubKeyB, short pubKeyBOff, + short pubKeyBLen, byte[] sessionKey, short sessionKeyOff) { + byte[] scratchPad = new byte[1024]; + short key = + cryptoProvider.ecdhKeyAgreement(privKeyA, privKeyAOff, privKeyALen, pubKeyB, pubKeyBOff, + pubKeyBLen, scratchPad, (short) 0); + key = KMByteBlob.instance(scratchPad, (short) 0, key); + + // ignore 0x04 for ephemerical public key as kdfContext should not include 0x04. + pubKeyAOff += 1; + pubKeyALen -= 1; + pubKeyBOff += 1; + pubKeyBLen -= 1; + short kdfContext = + KMCose.constructKdfContext(pubKeyA, pubKeyAOff, pubKeyALen, pubKeyB, pubKeyBOff, pubKeyBLen, + false); + kdfContext = encoder.encode(kdfContext, scratchPad, (short) 0, (short) 1024); + kdfContext = KMByteBlob.instance(scratchPad, (short) 0, kdfContext); + + Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 32, (byte) 0); + cryptoProvider.hkdf( + KMByteBlob.cast(key).getBuffer(), + KMByteBlob.cast(key).getStartOff(), + KMByteBlob.cast(key).length(), + scratchPad, + (short) 0, + (short) 32, + KMByteBlob.cast(kdfContext).getBuffer(), + KMByteBlob.cast(kdfContext).getStartOff(), + KMByteBlob.cast(kdfContext).length(), + scratchPad, + (short) 32, // offset + (short) 32 // Length of expected output. + ); + Util.arrayCopy(scratchPad, (short) 32, sessionKey, sessionKeyOff, (short) 32); + return (short) 32; + } + + public static KeyPair generateEcKeyPair(KMSEProvider cryptoProvider, byte[] pub, byte[] priv, + short[] lengths) { + cryptoProvider + .createAsymmetricKey(KMType.EC, priv, (short) 0, (short) priv.length, pub, (short) 0, + (short) pub.length, + lengths); + KeyPair eekKey = new KeyPair(KeyPair.ALG_EC_FP, KeyBuilder.LENGTH_EC_FP_256); + ECPrivateKey ecPrivateKey = (ECPrivateKey) eekKey.getPrivate(); + ecPrivateKey.setS(priv, (short) 0, lengths[0]); + ECPublicKey ecPublicKey = (ECPublicKey) eekKey.getPublic(); + ecPublicKey.setW(pub, (short) 0, lengths[1]); + return eekKey; + } + + public static void getEcKeys(KeyPair ecKeyPair, byte[] pub, byte[] priv, short[] lengths) { + ECPrivateKey ecPrivateKey = (ECPrivateKey) ecKeyPair.getPrivate(); + lengths[0] = ecPrivateKey.getS(priv, (short) 0); + ECPublicKey ecPublicKey = (ECPublicKey) ecKeyPair.getPublic(); + lengths[1] = ecPublicKey.getW(pub, (short) 0); + } + + public static KeyPair getEcKeyPair(byte[] pub, short pubOff, short pubLen, + byte[] priv, short privOff, short privLen) { + KeyPair ecKeyPair = new KeyPair(KeyPair.ALG_EC_FP, KeyBuilder.LENGTH_EC_FP_256); + if (privLen != 0) { + ECPrivateKey ecPrivateKey = (ECPrivateKey) ecKeyPair.getPrivate(); + ecPrivateKey.setS(priv, (short) 0, privLen); + } + if (pubLen != 0) { + ECPublicKey ecPublicKey = (ECPublicKey) ecKeyPair.getPublic(); + ecPublicKey.setW(pub, (short) 0, pubLen); + } + return ecKeyPair; + } + + public static short generateCoseMac0Mac(KMSEProvider cryptoProvider, KMEncoder encoder, + byte[] macKey, short macKeyOff, short macKeyLen, short extAad, short payload, + short protectedHeader, byte[] scratchpad, short offset, short outLength) { + if (macKeyLen == 0) { + return 0; + } + // Create MAC Structure and compute HMAC as per https://tools.ietf.org/html/rfc8152#section-6.3 + // MAC_structure = [ + // context : "MAC" / "MAC0", + // protected : empty_or_serialized_map, + // external_aad : bstr, + // payload : bstr + // ] + short arrPtr = KMArray.instance(KMCose.COSE_MAC0_ENTRY_COUNT); + // 1 - Context + KMArray.cast(arrPtr).add((short) 0, KMTextString.instance(KMCose.MAC_CONTEXT, (short) 0, + (short) KMCose.MAC_CONTEXT.length)); + // 2 - Protected headers. + KMArray.cast(arrPtr).add((short) 1, protectedHeader); + // 3 - external aad + KMArray.cast(arrPtr).add((short) 2, extAad); + // 4 - payload. + KMArray.cast(arrPtr).add((short) 3, payload); + // Do encode + short len = encoder.encode(arrPtr, scratchpad, offset, outLength); + short hmacLen = cryptoProvider.hmacSign(macKey, macKeyOff, macKeyLen, scratchpad, offset, len, + scratchpad, (short) (offset + len)); + Util.arrayCopy(scratchpad, (short) (offset + len), scratchpad, offset, hmacLen); + return hmacLen; + } + + public static short getEmptyKeyParams() { + // Empty attest key params + short emptyArr = KMArray.instance((short) 0); + return KMKeyParameters.instance(emptyArr); + } + + public static void print(short blob) { + print(KMByteBlob.cast(blob).getBuffer(), KMByteBlob.cast(blob).getStartOff(), + KMByteBlob.cast(blob).length()); + } + + public static void print(byte[] buf, short start, short length) { + StringBuilder sb = new StringBuilder(); + for (int i = start; i < (start + length); i++) { + sb.append(String.format(" 0x%02X", buf[i])); + } + System.out.println(sb.toString()); + } + + public static void printCert(byte[] buf, short start, short length) { + StringBuilder sb = new StringBuilder(); + for (int i = start; i < (start + length); i++) { + sb.append(String.format("%02X", buf[i])); + } + System.out.println(sb.toString()); + } + + public static short translateExtendedErrorCodes(short err) { + switch (err) { + case KMError.SW_CONDITIONS_NOT_SATISFIED: + case KMError.UNSUPPORTED_CLA: + case KMError.INVALID_P1P2: + case KMError.INVALID_DATA: + case KMError.CRYPTO_ILLEGAL_USE: + case KMError.CRYPTO_ILLEGAL_VALUE: + case KMError.CRYPTO_INVALID_INIT: + case KMError.CRYPTO_UNINITIALIZED_KEY: + case KMError.GENERIC_UNKNOWN_ERROR: + err = KMError.UNKNOWN_ERROR; + break; + case KMError.CRYPTO_NO_SUCH_ALGORITHM: + err = KMError.UNSUPPORTED_ALGORITHM; + break; + case KMError.UNSUPPORTED_INSTRUCTION: + case KMError.CMD_NOT_ALLOWED: + case KMError.SW_WRONG_LENGTH: + err = KMError.UNIMPLEMENTED; + break; + default: + break; + } + return err; + } + + public static short extractKeyBlobArray(KMDecoder decoder, byte[] buf, short off, short buflen) { + short byteBlobExp = KMByteBlob.exp(); + short keyChar = KMKeyCharacteristics.exp(); + short keyParam = KMKeyParameters.exp(); + short ret = KMArray.instance(KMKeymasterApplet.ASYM_KEY_BLOB_SIZE_V2_V3); + KMArray.cast(ret).add(KMKeymasterApplet.KEY_BLOB_VERSION_OFFSET, KMInteger.exp());// Version + KMArray.cast(ret).add(KMKeymasterApplet.KEY_BLOB_SECRET, byteBlobExp);// Secret + KMArray.cast(ret).add(KMKeymasterApplet.KEY_BLOB_NONCE, byteBlobExp);// Nonce + KMArray.cast(ret).add(KMKeymasterApplet.KEY_BLOB_AUTH_TAG, byteBlobExp);// AuthTag + KMArray.cast(ret).add(KMKeymasterApplet.KEY_BLOB_PARAMS, keyChar);// KeyChars + KMArray.cast(ret).add(KMKeymasterApplet.KEY_BLOB_CUSTOM_TAGS, keyParam);// KeyChars + KMArray.cast(ret).add(KMKeymasterApplet.KEY_BLOB_PUB_KEY, byteBlobExp);// PubKey + + ret = decoder.decodeArray(ret, buf, off, buflen); + return ret; + } + + public static short getPublicKey(KMDecoder decoder, byte[] keyBlob, short off, short len, + byte[] pubKey, short pubKeyOff) { + short keyBlobPtr = extractKeyBlobArray(decoder, keyBlob, off, len); + short arrayLen = KMArray.cast(keyBlobPtr).length(); + if (arrayLen < KMKeymasterApplet.ASYM_KEY_BLOB_SIZE_V2_V3) { + return 0; + } + short pubKeyPtr = KMArray.cast(keyBlobPtr).get( + KMKeymasterApplet.KEY_BLOB_PUB_KEY); + Util.arrayCopy(KMByteBlob.cast(pubKeyPtr).getBuffer(), + KMByteBlob.cast(pubKeyPtr).getStartOff(), pubKey, pubKeyOff, + KMByteBlob.cast(pubKeyPtr).length()); + return KMByteBlob.cast(pubKeyPtr).length(); + } + + public static short decodeError(KMDecoder decoder, ResponseAPDU response) { + byte[] respBuf = response.getBytes(); + short arr = KMArray.instance((short) 1); + KMArray.cast(arr).add((short) 0, KMInteger.exp()); + arr = decoder.decode(arr, respBuf, (short) 0, (short) respBuf.length); + return KMInteger.cast(KMArray.cast(arr).get((short) 0)).getShort(); + } + + public static String toHexString(byte[] num) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < num.length; i++) { + sb.append(String.format("%02X", num[i])); + } + return sb.toString(); + } +} diff --git a/Applet/src/com/android/javacard/keymaster/KMArray.java b/Applet/src/com/android/javacard/keymaster/KMArray.java index 6fa4098e..66495615 100644 --- a/Applet/src/com/android/javacard/keymaster/KMArray.java +++ b/Applet/src/com/android/javacard/keymaster/KMArray.java @@ -50,7 +50,7 @@ private static KMArray proto(short ptr) { public static short exp() { short ptr = instance(ARRAY_TYPE, ARRAY_HEADER_SIZE); - Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), (short) 0); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), KMType.INVALID_VALUE); Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), ANY_ARRAY_LENGTH); return ptr; } @@ -64,7 +64,7 @@ public static short exp(short type) { public static short instance(short length) { short ptr = KMType.instance(ARRAY_TYPE, (short) (ARRAY_HEADER_SIZE + (length * 2))); - Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), (short) 0); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), KMType.INVALID_VALUE); Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), length); return ptr; } diff --git a/Applet/src/com/android/javacard/keymaster/KMDecoder.java b/Applet/src/com/android/javacard/keymaster/KMDecoder.java index e3c7bc45..2e02274f 100644 --- a/Applet/src/com/android/javacard/keymaster/KMDecoder.java +++ b/Applet/src/com/android/javacard/keymaster/KMDecoder.java @@ -442,7 +442,7 @@ private short decodeArray(short exp) { short type; short obj; // check whether array contains one type of objects or multiple types - if (KMArray.cast(exp).containedType() == 0) {// multiple types specified by expression. + if (KMArray.cast(exp).containedType() == KMType.INVALID_VALUE) {// multiple types specified by expression. if (KMArray.cast(exp).length() != KMArray.ANY_ARRAY_LENGTH) { if (KMArray.cast(exp).length() != payloadLength) { ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); diff --git a/Applet/src/com/android/javacard/keymaster/KMError.java b/Applet/src/com/android/javacard/keymaster/KMError.java index 5e93b49b..f8c9ec4d 100644 --- a/Applet/src/com/android/javacard/keymaster/KMError.java +++ b/Applet/src/com/android/javacard/keymaster/KMError.java @@ -123,12 +123,12 @@ public static short translate(short err) { case CRYPTO_INVALID_INIT: case CRYPTO_UNINITIALIZED_KEY: case GENERIC_UNKNOWN_ERROR: + case CMD_NOT_ALLOWED: case UNKNOWN_ERROR: return UNKNOWN_ERROR; case CRYPTO_NO_SUCH_ALGORITHM: return UNSUPPORTED_ALGORITHM; case UNSUPPORTED_INSTRUCTION: - case CMD_NOT_ALLOWED: case SW_WRONG_LENGTH: return UNIMPLEMENTED; } diff --git a/Applet/src/com/android/javacard/keymaster/KMKeymintDataStore.java b/Applet/src/com/android/javacard/keymaster/KMKeymintDataStore.java index c79a0a8c..3f58ec50 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeymintDataStore.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeymintDataStore.java @@ -119,6 +119,11 @@ public class KMKeymintDataStore implements KMUpgradable { private KMRkpMacKey rkpMacKey; private byte[] oemRootPublicKey; private short provisionStatus; + private static KMKeymintDataStore kmDataStore; + + public static KMKeymintDataStore instance() { + return kmDataStore; + } public KMKeymintDataStore(KMSEProvider provider, KMRepository repo) { seProvider = provider; @@ -133,6 +138,7 @@ public KMKeymintDataStore(KMSEProvider provider, KMRepository repo) { } setDeviceLockPasswordOnly(false); setDeviceLock(false); + kmDataStore = this; } private void initDataTable() {