blob: 2854a71a581f631730bd1334f383723e7d9a183d [file] [log] [blame]
/*
* Copyright(C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.javacard.keymaster;
import javacard.framework.ISO7816;
import javacard.framework.ISOException;
/**
* This class constructs the Cose messages like CoseKey, CoseMac0, MacStructure, CoseSign1,
* SignStructure, CoseEncrypt, EncryptStructure and ReceipientStructures.
*/
public class KMCose {
// COSE SIGN1
public static final byte COSE_SIGN1_ENTRY_COUNT = 4;
public static final byte COSE_SIGN1_PROTECTED_PARAMS_OFFSET = 0;
public static final byte COSE_SIGN1_PAYLOAD_OFFSET = 2;
public static final byte COSE_SIGN1_SIGNATURE_OFFSET = 3;
// COSE MAC0
public static final byte COSE_MAC0_ENTRY_COUNT = 4;
public static final byte COSE_MAC0_PROTECTED_PARAMS_OFFSET = 0;
public static final byte COSE_MAC0_PAYLOAD_OFFSET = 2;
public static final byte COSE_MAC0_TAG_OFFSET = 3;
// COSE ENCRYPT
public static final byte COSE_ENCRYPT_ENTRY_COUNT = 4;
public static final byte COSE_ENCRYPT_STRUCTURE_ENTRY_COUNT = 3;
public static final byte COSE_ENCRYPT_RECIPIENT_ENTRY_COUNT = 3;
// COSE Labels
public static final byte COSE_LABEL_ALGORITHM = 1;
public static final byte COSE_LABEL_KEYID = 4;
public static final byte COSE_LABEL_IV = 5;
public static final byte COSE_LABEL_COSE_KEY = (byte) 0xFF; // -1
// COSE Algorithms
public static final byte COSE_ALG_AES_GCM_256 = 3; // AES-GCM mode w/ 256-bit key, 128-bit tag.
public static final byte COSE_ALG_HMAC_256 = 5; // HMAC w/ SHA-256
public static final byte COSE_ALG_ES256 = (byte) 0xF9; // ECDSA w/ SHA-256; -7
public static final byte COSE_ALG_ECDH_ES_HKDF_256 = (byte) 0xE7; // ECDH-EC+HKDF-256; -25
// COSE P256 EC Curve
public static final byte COSE_ECCURVE_256 = 1;
// COSE key types
public static final byte COSE_KEY_TYPE_EC2 = 2;
public static final byte COSE_KEY_TYPE_SYMMETRIC_KEY = 4;
// COSE Key Operations
public static final byte COSE_KEY_OP_SIGN = 1;
public static final byte COSE_KEY_OP_VERIFY = 2;
public static final byte COSE_KEY_OP_ENCRYPT = 3;
public static final byte COSE_KEY_OP_DECRYPT = 4;
// AES GCM
public static final short AES_GCM_KEY_SIZE_BITS = 256;
// Cose key parameters.
public static final byte COSE_KEY_KEY_TYPE = 1;
public static final byte COSE_KEY_KEY_ID = 2;
public static final byte COSE_KEY_ALGORITHM = 3;
public static final byte COSE_KEY_CURVE = -1;
public static final byte COSE_KEY_PUBKEY_X = -2;
public static final byte COSE_KEY_PUBKEY_Y = -3;
public static final byte COSE_KEY_PRIV_KEY = -4;
public static final byte[] COSE_TEST_KEY = {
(byte) 0xFF, (byte) 0xFE, (byte) 0xEE, (byte) 0x90
}; // -70000
public static final byte COSE_KEY_MAX_SIZE = 4;
// kdfcontext strings
public static final byte[] client = {0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74};
public static final byte[] server = {0x73, 0x65, 0x72, 0x76, 0x65, 0x72};
// Context strings
public static final byte[] MAC_CONTEXT = {0x4d, 0x41, 0x43, 0x30}; // MAC0
public static final byte[] SIGNATURE1_CONTEXT = {
0x53, 0x69, 0x67, 0x6E, 0x61, 0x74, 0x75, 0x72, 0x65, 0x31
}; // Signature1
// Certificate payload supported keys
public static final byte ISSUER = (byte) 0x01;
public static final byte SUBJECT = (byte) 0x02;
public static final byte[] SUBJECT_PUBLIC_KEY = {
(byte) 0xFF, (byte) 0xB8, (byte) 0xBB, (byte) 0xA8
};
public static final byte[] KEY_USAGE = {(byte) 0xFF, (byte) 0xB8, (byte) 0xBB, (byte) 0xA7};
// text strings
public static final byte[] TEST_ISSUER_NAME = {
(byte) 0x49, 0x73, 0x73, 0x75, 0x65, 0x72
}; // "Issuer"
public static final byte[] TEST_SUBJECT_NAME = {
0x53, 0x75, 0x62, 0x6A, 0x65, 0x63, 0x74
}; // "Subject"
public static final byte[] KEY_USAGE_SIGN = {0x20}; // Key usage sign
public static final short[] COSE_KEY_LABELS = {
KMCose.COSE_KEY_KEY_TYPE,
KMCose.COSE_KEY_KEY_ID,
KMCose.COSE_KEY_ALGORITHM,
KMCose.COSE_KEY_CURVE,
KMCose.COSE_KEY_PUBKEY_X,
KMCose.COSE_KEY_PUBKEY_Y,
KMCose.COSE_KEY_PRIV_KEY
};
public static final short[] COSE_HEADER_LABELS = {
KMCose.COSE_LABEL_ALGORITHM,
KMCose.COSE_LABEL_KEYID,
KMCose.COSE_LABEL_IV,
KMCose.COSE_LABEL_COSE_KEY
};
/**
* Constructs the Cose MAC structure.
*
* @param protectedHeader Bstr pointer which holds the protected header.
* @param extAad Bstr pointer which holds the external Aad.
* @param payload Bstr pointer which holds the payload of the MAC structure.
* @return KMArray instance of MAC structure.
*/
public static short constructCoseMacStructure(
short protectedHeader, short extAad, short payload) {
// 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);
return arrPtr;
}
/**
* Constructs the COSE_MAC0 object.
*
* @param protectedHeader Bstr pointer which holds the protected header.
* @param unprotectedHeader Bstr pointer which holds the unprotected header.
* @param payload Bstr pointer which holds the payload of the MAC structure.
* @param tag Bstr pointer which holds the tag value.
* @return KMArray instance of COSE_MAC0 object.
*/
public static short constructCoseMac0(
short protectedHeader, short unprotectedHeader, short payload, short tag) {
// Construct Cose_MAC0
// COSE_Mac0 = [
// protectedHeader,
// unprotectedHeader,
// payload : bstr / nil,
// tag : bstr,
// ]
short arrPtr = KMArray.instance(KMCose.COSE_MAC0_ENTRY_COUNT);
// 1 - protected headers
KMArray.cast(arrPtr).add((short) 0, protectedHeader);
// 2 - unprotected headers
KMArray.cast(arrPtr).add((short) 1, unprotectedHeader);
// 2 - payload
KMArray.cast(arrPtr).add((short) 2, payload);
// 3 - tag
KMArray.cast(arrPtr).add((short) 3, tag);
// Do encode.
return arrPtr;
}
/**
* Constructs the COSE_Signature structure.
*
* @param protectedHeader Bstr pointer which holds the protected header.
* @param extAad Bstr pointer which holds the aad.
* @param payload Bstr pointer which holds the payload.
* @return KMArray instance of COSE_Signature object.
*/
public static short constructCoseSignStructure(
short protectedHeader, short extAad, short payload) {
// Sig_structure = [
// context : "Signature" / "Signature1" / "CounterSignature",
// body_protected : empty_or_serialized_map,
// ? sign_protected : empty_or_serialized_map,
// external_aad : bstr,
// payload : bstr
// ]
short arrPtr = KMArray.instance(KMCose.COSE_SIGN1_ENTRY_COUNT);
// 1 - Context
KMArray.cast(arrPtr)
.add(
(short) 0,
KMTextString.instance(
KMCose.SIGNATURE1_CONTEXT, (short) 0, (short) KMCose.SIGNATURE1_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);
return arrPtr;
}
/**
* Constructs the COSE_Sign1 object.
*
* @param protectedHeader Bstr pointer which holds the protected header.
* @param unProtectedHeader Bstr pointer which holds the unprotected header.
* @param payload Bstr pointer which holds the payload.
* @param signature Bstr pointer which holds the signature.
* @return KMArray instance of COSE_Sign1 object.
*/
public static short constructCoseSign1(
short protectedHeader, short unProtectedHeader, short payload, short signature) {
// COSE_Sign = [
// protectedHeader,
// unprotectedHeader,
// payload : bstr / nil,
// signatures : [+ COSE_Signature]
// ]
short arrPtr = KMArray.instance(KMCose.COSE_SIGN1_ENTRY_COUNT);
// 1 - protected headers
KMArray.cast(arrPtr).add((short) 0, protectedHeader);
// 2 - unprotected headers
KMArray.cast(arrPtr).add((short) 1, unProtectedHeader);
// 2 - payload
KMArray.cast(arrPtr).add((short) 2, payload);
// 3 - tag
KMArray.cast(arrPtr).add((short) 3, signature);
return arrPtr;
}
/**
* Constructs array based on the tag values provided.
*
* @param tag array of tag values to be constructed.
* @return instance of KMArray.
*/
private static short handleCosePairTags(short[] tag, short[] keyValues, short valueIndex) {
short index = 0;
// var is used to calculate the length of the array.
short var = 0;
short tagLen = (short) tag.length;
// var is used to calculate the length of the array.
while (index < tagLen) {
if (keyValues[index] != KMType.INVALID_VALUE) {
keyValues[(short) (index + valueIndex)] =
buildCosePairTag((byte) tag[index], keyValues[index]);
var++;
}
index++;
}
short arrPtr = KMArray.instance(var);
index = 0;
// var is used to index the array.
var = 0;
while (index < tagLen) {
if (keyValues[(short) (index + valueIndex)] != KMType.INVALID_VALUE) {
KMArray.cast(arrPtr).add(var++, keyValues[(short) (index + valueIndex)]);
}
index++;
}
return arrPtr;
}
/**
* Constructs the COSE_sign1 payload for certificate.
*
* @param issuer instance of KMCosePairTextStringTag which contains issuer value.
* @param subject instance of KMCosePairTextStringTag which contains subject value.
* @param subPublicKey instance of KMCosePairByteBlobTag which contains encoded KMCoseKey.
* @param keyUsage instance of KMCosePairByteBlobTag which contains key usage value.
* @return instance of KMArray.
*/
public static short constructCoseCertPayload(
short issuer, short subject, short subPublicKey, short keyUsage) {
short certPayload = KMArray.instance((short) 4);
KMArray.cast(certPayload).add((short) 0, issuer);
KMArray.cast(certPayload).add((short) 1, subject);
KMArray.cast(certPayload).add((short) 2, subPublicKey);
KMArray.cast(certPayload).add((short) 3, keyUsage);
certPayload = KMCoseCertPayload.instance(certPayload);
KMCoseCertPayload.cast(certPayload).canonicalize();
return certPayload;
}
/**
* Construct headers structure. Headers can be part of COSE_Sign1, COSE_Encrypt, COSE_Mac0 and
* COSE_Key.
*
* @param alg instance of either KMNInteger or KMInteger, based on the sign of algorithm value.
* @param keyId instance of KMByteBlob which contains the key identifier.
* @param iv instance of KMByteblob which contains the iv buffer.
* @param ephemeralKey instance of KMCoseKey.
* @return instance of KMCoseHeaders.
*/
public static short constructHeaders(
short[] buff, short alg, short keyId, short iv, short ephemeralKey) {
buff[0] = alg;
buff[1] = keyId;
buff[2] = iv;
buff[3] = ephemeralKey;
for (short i = 4; i < 8; i++) {
buff[i] = KMType.INVALID_VALUE;
}
short ptr = handleCosePairTags(COSE_HEADER_LABELS, buff, (short) 4);
ptr = KMCoseHeaders.instance(ptr);
KMCoseHeaders.cast(ptr).canonicalize();
return ptr;
}
/**
* Constructs the instance of KMCosePair*Tag.
*
* @param key value of the key.
* @param valuePtr instance of one of KMType.
* @return instance of KMCosePair*Value object.
*/
public static short buildCosePairTag(byte key, short valuePtr) {
short type = KMType.getType(valuePtr);
short keyPtr;
if (key < 0) {
keyPtr = KMNInteger.uint_8(key);
} else {
keyPtr = KMInteger.uint_8(key);
}
switch (type) {
case KMType.INTEGER_TYPE:
return KMCosePairIntegerTag.instance(keyPtr, valuePtr);
case KMType.NEG_INTEGER_TYPE:
return KMCosePairNegIntegerTag.instance(keyPtr, valuePtr);
case KMType.BYTE_BLOB_TYPE:
return KMCosePairByteBlobTag.instance(keyPtr, valuePtr);
case KMType.TEXT_STRING_TYPE:
return KMCosePairTextStringTag.instance(keyPtr, valuePtr);
case KMType.COSE_KEY_TYPE:
return KMCosePairCoseKeyTag.instance(keyPtr, valuePtr);
default:
ISOException.throwIt(ISO7816.SW_DATA_INVALID);
return 0;
}
}
/**
* Constructs a CoseKey with the provided input parameters. Note that construction of the key_ops
* label is not needed to be supported. In the KeyMint3.0 specifications: The CoseKey inside
* MacedPublicKeys and DiceCertChain does not have key_ops label.
*
* @param keyType Instance of the identification of the key type.
* @param keyId Instance of key identification value.
* @param keyAlg Instance of the algorithm that is used with this key.
* @param curve Instance of the EC curve that is used with this key.
* @param pubKey Buffer containing the public key.
* @param pubKeyOff Start offset of the buffer.
* @param pubKeyLen Length of the public key.
* @param privKeyPtr Instance of the private key.
* @return Instance of the CoseKey structure.
*/
public static short constructCoseKey(
short[] buff,
short keyType,
short keyId,
short keyAlg,
short curve,
byte[] pubKey,
short pubKeyOff,
short pubKeyLen,
short privKeyPtr) {
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 coseKey = constructCoseKey(buff, keyType, keyId, keyAlg, curve, xPtr, yPtr, privKeyPtr);
KMCoseKey.cast(coseKey).canonicalize();
return coseKey;
}
/**
* Constructs the cose key based on input parameters supplied. All the parameters must be
* instantiated from either KMInteger or KMNInteger or KMByteblob types.
*
* @param keyType instance of KMInteger/KMNInteger which holds valid COSE key types.
* @param keyId instance of KMByteBlob which holds key identifier value.
* @param keyAlg instance of KMInteger/KMNInteger which holds valid COSE key algorithm.
* @param curve instance of KMInteger/KMNInteger which holds valid COSE EC curve.
* @param pubX instance of KMByteBlob which holds EC public key's x value.
* @param pubY instance of KMByteBlob which holds EC public key's y value.
* @param priv instance of KMByteBlob which holds EC private value.
* @return instance of the KMCoseKey object.
*/
public static short constructCoseKey(
short[] buff,
short keyType,
short keyId,
short keyAlg,
short curve,
short pubX,
short pubY,
short priv) {
short valueIndex = 7;
buff[0] = keyType;
buff[1] = keyId;
buff[2] = keyAlg;
buff[3] = curve;
buff[4] = pubX;
buff[5] = pubY;
buff[6] = priv;
for (short i = valueIndex; i < 16; i++) {
buff[i] = KMType.INVALID_VALUE;
}
short arrPtr = handleCosePairTags(COSE_KEY_LABELS, buff, valueIndex);
arrPtr = KMCoseKey.instance(arrPtr);
KMCoseKey.cast(arrPtr).canonicalize();
return arrPtr;
}
/**
* Constructs key derivation context which is required to compute HKDF.
*
* @param publicKeyA public key buffer from the first party.
* @param publicKeyAOff start position of the public key buffer from first party.
* @param publicKeyALen length of the public key buffer from first party.
* @param publicKeyB public key buffer from the second party.
* @param publicKeyBOff start position of the public key buffer from second party.
* @param publicKeyBLen length of the public key buffer from second party.
* @param senderIsA true if caller is first party, false if caller is second party.
* @return instance of KMArray.
*/
public static short constructKdfContext(
byte[] publicKeyA,
short publicKeyAOff,
short publicKeyALen,
byte[] publicKeyB,
short publicKeyBOff,
short publicKeyBLen,
boolean senderIsA) {
short index = 0;
// Prepare sender info
short senderInfo = KMArray.instance((short) 3);
KMArray.cast(senderInfo)
.add(index++, KMByteBlob.instance(client, (short) 0, (short) client.length));
KMArray.cast(senderInfo).add(index++, KMByteBlob.instance((short) 0));
KMArray.cast(senderInfo)
.add(
index,
senderIsA
? KMByteBlob.instance(publicKeyA, publicKeyAOff, publicKeyALen)
: KMByteBlob.instance(publicKeyB, publicKeyBOff, publicKeyBLen));
// Prepare recipient info
index = 0;
short recipientInfo = KMArray.instance((short) 3);
KMArray.cast(recipientInfo)
.add(index++, KMByteBlob.instance(server, (short) 0, (short) server.length));
KMArray.cast(recipientInfo).add(index++, KMByteBlob.instance((short) 0));
KMArray.cast(recipientInfo)
.add(
index,
senderIsA
? KMByteBlob.instance(publicKeyB, publicKeyBOff, publicKeyBLen)
: KMByteBlob.instance(publicKeyA, publicKeyAOff, publicKeyALen));
// supply public info
index = 0;
short publicInfo = KMArray.instance((short) 2);
KMArray.cast(publicInfo).add(index++, KMInteger.uint_16(AES_GCM_KEY_SIZE_BITS));
KMArray.cast(publicInfo).add(index, KMByteBlob.instance((short) 0));
// construct kdf context
index = 0;
short arrPtr = KMArray.instance((short) 4);
KMArray.cast(arrPtr).add(index++, KMInteger.uint_8(COSE_ALG_AES_GCM_256));
KMArray.cast(arrPtr).add(index++, senderInfo);
KMArray.cast(arrPtr).add(index++, recipientInfo);
KMArray.cast(arrPtr).add(index, publicInfo);
return arrPtr;
}
}