Prerequisites
Before you can parse the attestation, you need to retrieve the following required items:
You can use either the Cloud HSM Provider or the Marvell tools to accomplish these tasks.
Verifying an Attestation
After completing the Prerequisites, run the verify_attest.py script to verify whether the attestation is successful or not. The script takes the certificate file and the attestation file as arguments.
python verify_attest.py part.crt attest.dat
If the attestation is successful, the output will look as below.
python verify_attest.py part.crt attest.dat
************************************************************
Usage: ./verify_attest.py <partition.cert> <attestation.dat>
************************************************************
Signature verified!:signature verification passed
If attestation fails, the following output is returned:
python verify_attest.py part.crt attest.dat
************************************************************
Usage: ./verify_attest.py <partition.cert> <attestation.dat>
************************************************************
Signature failed!
Parsing an Attestation
There are two different scripts for parsing attestations corresponding to the two attestation formats:
Version_1 format (parse_v1.py) – Used when the firmware version is earlier than 3.2.
Version_2 format (parse_v2.py) – Used when the firmware version is 3.2 or later.
Note: The script can only parse an attestation blob that contains one format. You must use the script for your firmware version.
python parse_v2.py attest.dat
If the attestation file is for an RSA key, the output will be similar to the following:
Public Key Attestation |
To verify key properties, send the output to a file; for example:
python parse_v2_attest.py attest.dat > parsed.dat
NUMBER | ATTRIBUTE NAME | 特性 |
---|---|---|
0X0000 | OBJ_ATTR_CLASS | Class type of the key. |
0X0001 | OBJ_ATTR_TOKEN | Identifies the key as a token key. |
0X0002 | OBJ_ATTR_PRIVATE | Indicates if this is a shared key or a private key (for symmetric or asymmetric keys). |
0X0003 | OBJ_ATTR_LABEL | Key description. |
0X0086 | OBJ_ATTR_TRUSTED | The key can be trusted for the application that it was created. |
0X0100 | OBJ_ATTR_KEY_TYPE | Subclass type of the key. |
0X0102 | OBJ_ATTR_ID | Key identifier. |
0X0103 | OBJ_ATTR_SENSITIVE | Always true for keys generated on HSM. |
0X0104 | OBJ_ATTR_ENCRYPT | |
0X0105 | OBJ_ATTR_DECRYPT | |
0X0106 | OBJ_ATTR_WRAP | Indicates if key can be used to wrap other keys. |
0X0107 | OBJ_ATTR_UNWRAP | Indicates if key can be used to unwrap other keys. |
0X0108 | OBJ_ATTR_SIGN | Indicates if key can be used for signing operations. |
0x010A | OBJ_ATTR_VERIFY | Indicates if key can be used for verifying operations. |
0x010C | OBJ_ATTR_DERIVE | Indicates if key supports key derivation (i.e. if other keys can be derived from this one). |
0X0120 | OBJ_ATTR_MODULUS | RSA key modulus value. |
0X0121 | OBJ_ATTR_MODULUS_BITS | RSA key size in bits. |
0X0122 | OBJ_ATTR_PUBLIC_EXPONENT | RSA key public exponent value. |
0X0161 | OBJ_ATTR_VALUE_LEN | Length in bytes of any value. |
0X0162 | OBJ_ATTR_EXTRACTABLE | Indicates if key can be extracted. |
0X0163 | OBJ_ATTR_LOCAL | Indicates if key was generated locally |
0X0164 | OBJ_ATTR_NEVER_EXTRACTABLE | Indicates if key can never be extracted. |
0X0165 | OBJ_ATTR_ALWAYS_SENSITIVE | Indicates if key has always had the OBJ_ATTR_SENSITIVE attribute set. |
0X0173 | OBJ_ATTR_KCV | Key Check Value. |
0X1000 | OBJ_EXT_ATTR1 | Extended Attribute #1 |
0X1003 | OBJ_ATTR_EKCV | Extended Key Check Value. |
0X0210 | OBJ_ATTR_WRAP_WITH_TRUSTED | Indicates if key can only be wrapped with a wrapping key that has OBJ_ATTR_TRUSTED set. |
0X80000002 | OBJ_ATTR_SPLITTABLE | Indicates if key can be split into multiple parts. |
0X80000003 | OBJ_ATTR_IS_SPLIT | Indicate if it is part of the key split. |
0X80000174 | OBJ_ATTR_ENCRYPT_KEY_MECHANISMS | Indicate if key supports encryption. |
0X80000175 | OBJ_ATTR_DECRYPT_KEY_MECHANISMS | Indicate if key supports decryption. |
0X80000176 | OBJ_ATTR_SIGN_KEY_MECHANISMS | Indicate if key supports signing. |
0X80000177 | OBJ_ATTR_VERIFY_KEY_MECHANISMS | Indicate if key supports signature verification. |
0X80000178 | OBJ_ATTR_WRAP_KEY_MECHANISMS | Indicate if key supports key wrapping. |
0X80000179 | OBJ_ATTR_UNWAP_KEY_MECHANISMS | Indicate if key supports key unwrapping. |
0X80000180 | OBJ_ATTR_DERIVE_KEY_MECHANISMS | Indicate if key supports key derivation. |
grep '0x0162:' parsed.dat
grep '0x0100:' parsed.dat
grep '0x0163:' parsed.dat
Verifying a Public Key
1.
./Cfm2Util -p PARTITION_1 singlecmd loginHSM -u CU -p user123 -s crypto_user exportPubKey -k 55 -out public.pem
Application is bound to the partition with name: PARTITION_1
SDK Version: 3.02
Session handle: 28181408 : 0x1ae03a0
Current FIPS mode is: 2
Cfm2LoginHSM returned: 0x00 : HSM Return: SUCCESS
Command: exportPubKey -k 55 -out public.pem
PEM formatted public key is written to public.pem
Cfm2ExportPubKey returned: 0x00 : HSM Return: SUCCESS
2. Parse the attestation data and run the verify_pubkey.py script to verify the public key.
python parse_v2.py attestation.dat > parse.dat
python verify_pubkey.py parse.dat public_key.pem
Result
****************************************
Public key matched with attestation.
****************************************
This section describes the attestation format and how it is used by the application.
Attestation Response Format
When attestation is enabled, after the response the attestation response buffer for a command has two parts: the data and the signature.
Header | Attributes (optional) | RSA Sign |
---|
Attestation Response Format
Attributes and Attestation Flags
KEY_GEN_FLAG_GET_ATTR – Fetches the attributes as part of the response.
KEY_GEN_FLAG_GET_ATTEST – Gets RSA sign of the data as part of the response. This sign is obtained by signing the response header along with attributes (if fetched).
KEY_GEN_FLAG_EXCLUDE_HEADER – Excludes the response header and gets only the sign of the attributes. This flag is valid only if KEY_GEN_FLAG_GET_ATTEST is set. If attribute flag (KEY_GEN_FLAG_GET_ATTR) is not specified and KEY_GEN_FLAG_EXCLUDE_HEADER flag is set, attestation will fail as there is no data to sign.
Flag Usage
In the genKeyArgs structure, the flags field can be set with flags as shown below.
genKeyArgs keyArgs;
keyArgs.flags = KEY_GEN_FLAG_GET_ATTR;
Only attributes will be fetched, along with response header.
keyArgs.flags = KEY_GEN_FLAG_GET_ATTEST;
Only attestation of response header will be performed as there are no attributes.
keyArgs.flags = KEY_GEN_FLAG_GET_ATTR | KEY_GEN_FLAG_GET_ATTEST;
Attributes are fetched along with response header, and both of them will be attested together.
keyArgs.flags = KEY_GEN_FLAG_GET_ATTR | KEY_GEN_FLAG_GET_ATTEST | KEY_GEN_FLAG_EXCLUDE_HEADER;
Attributes are fetched as part of response, but only attributes will be attested, excluding the response header.
keyArgs.flags = KEY_GEN_FLAG_GET_ATTEST | KEY_GEN_FLAG_EXCLUDE_HEADER;
This is invalid, and attestation will fail as there is no data to attest.
keyArgs.flags = KEY_GEN_FLAG_EXCLUDE_HEADER; //
This won’t impact on the response because the attestation flag is not set. EXCLUDE_HEADER is valid only when the ATTEST flag is set.
genKeyArgs is passed to the key generation API, and then inside the API, the attest_flags field of the request header will be assigned with the genKeyArgs flags field.
Parsing the Attribute Buffer
To fetch attributes, the genKeyArgs flags field should be set to KEY_GEN_FLAG_GET_ATTR. The attribute buffer, which should have a size MTU/2 (4500 bytes), must be passed.
genKeyArgs has keyArgs structure. The pAttrObj field of this structure should point to the attribute buffer, and the ulAttrLen field should be assigned with the buffer size.
The response contains the response header, key handles as described below, and attributes that are in pAttrObj.
Interpreting the Attributes in pAttrBuf
Asymmetric key-pair generation example
The attribute buffer data format is as follows:
typedef struct {
Uint16 usObjectVersion;
Uint16 usFlags;
Uint16 usKey1Offset;
Uint16 usKey2Offset;
Object key1_tlv[0]; //place holder for key 1
Object key2_tlv[0]; //place holder for key 2
} TLVKeyInfo;
usObjectVersion = Object version. It will be 1 because this is the first version.
usFlags = Flags set during the request in the request header.
usKey1Offset = Offset to the attributes of first key in attribute buffer (pAttrObj).
usKey2Offset = Offset to attributes of second key in attribute buffer (pAttObj).
TLVKeyInfo *keyinfo = (TLVKeyInfo *) (pAttrObj); // Typecasting pAttrBuf to TLVKeyInfo
uint8_t * key_1 = pAttrObj + betoh46(keyinfo->usKey1Offset); // Pointing to key1 attributes.
Betoh46 is required because the response is received from firmware in big-endian format.
Instead of the above steps, you can use the GET_KEY1_ATTR macro (defined in cavium_wrappers.h).
uint8_t * key1 = GET_KEY1_ATTR(pAttrObj);
Similarly, to get key2 attributes, you can add usKey2Offset to pAttrObj buffer, or use the GET_KEY2_ATTR macro.
uint8_t *key2 = GET_KEY2_ATTR(pAttrObj);
注:
ParkObject
The structure parkOpArgs is used as input structure to parkObject (see Structures Used in Attestation).
The following fields must be populated and passed as part of the input to handle the parkObject response.
flags, input flags, which are discussed above (see Flag Usage).
pParkedObject should point to attribute buffer of size 6000 bytes, after getting the response this buffer contains encrypted parked key data and its attributes (auth data).
ulParkObjectLen is size of attribute buffer
ParkObject response
The response structure of parkObject is shown below:
typedef struct {
ResponseHeader header;
Uint32 ulObjectLen;
Uint8 ucOptionalKeyInfo[0];
Uint8 ucOptionalAttestation[0];
} ParkObjectResponse_v2;
ulObjectLen is the total length of the parked blob, which is the same as ulParkObjLen (see Structures Used in Attestation).
To get the attributes from pParkedObject, follow the procedure described for key generation (see the genKeyArgs).
Uint8_t * parked_blob = GET_KEY1_ATTR (pParkedObject);
This parked blob will contain encrypted key data and auth data (attributes) in TLV format.
With attestation
The attestation response buffer size must be equal to MTU (9000 bytes) and must be passed in the request along with its size. The pAttestResponse field should point to the attestation buffer, and the attestedLength field should be equal to buffer size.
The flags field of the input structure should be set with a flag based on the requirement.
Case 1
flags = KEY_GEN_FLAG_GET_ATTEST
Attest header; attestation response buffer contains header and sign, as shown in the figure below.
Header | Signature |
---|
Attestation Response Format
Parsing the attestation response buffer using external fields:
attestedLength field of input structure will be updated with total attestation response buffer length. To get the sign, add (attestedLength – RSA_SIGN_SIZE) to attestResponse buffer. RSA_SIGN_SIZE is 256.
uint8_t * sign = (Uint8 *) attestedResponse + attestedLength – RSA_SIGN_SIZE.
Parsing the attestation response buffer using fields in header:
In the case of generateKeyPair, the header included in the above diagram is GenerateKeyPairResponse_v2.
typedef struct {
uint32_t ulResponseCode;
uint32_t ulFlags;
uint32_t ulTotalSize;
uint32_t ulBufferSize;
} ResponseHeader;
ResponseHeader is a field in the GenerateKeyPairResponse_v2 structure (see Structures Used in Attestation).
If you look at the above structure, it includes the fields ulTotalSize and ulBufferSize. These fields can be used as offset within the attestation response buffer to get the attributes and sign. Typecast the attestation buffer to GenerateKeyPairResponse_v2 and get the values of ulTotalSize and ulBufferSize.
ulTotalSize is total size of attestation response buffer. This is equal to attestedLength value which was mentioned above.
ulBufferSIze is total size of attributes. In this case as attributes are not fetched, it will be 0.
GenerateKeyPairResponse_v2 * resp = ( GenerateKeyPairResponse_v2 *) attestedResponse;
Uint32 ulTotalSize = betoh32(resp->header.ulTotalSize);
Uint8_t * sign = (Uint8 *)attestedResponse + ulTotalSize - RSA_SIGN_SIZE;
Case 2
flags = KEY_GEN_FLAG_GET_ATTR | KEY_GEN_FLAG_GET_ATTEST
If these flags are set, along with attestedResponse and attestLength fields, you need to set the pAttrObj and ulAttrLen fields of the input structure. pAttrObj has an attribute buffer of size 4500 bytes, and ulAttrLen is the size of the buffer.
Header | Attributes | RSA Sign |
---|
Attestation Response Format
Parsing the attestation response format:
Attributes are present in both the attestedResponse and pAttrObj buffers. You can parse any of them to get the attributes. To parse the attributes from pAttrObj, use the method described in Parsing the Attribute Buffer because pAttrObj itself can be parsed directly. To fetch the signature, use attestedLength or ulTotalSize, as detailed in Case 1.
To get the attribute buffer from attestedResponse buffer, follow the below steps.
Example for key pair generation:
GenerateKeyPairResponse_v2 * resp = ( GenerateKeyPairResponse_v2 *) attestedResponse;
Uint32 ulTotalSize = betoh32(resp->header.ulTotalSize);
Uint32 ulBufferSize = betoh32(resp->header.ulBufferSize);
uint8_t * pAttrObj = (uint8_t*)attestedResponse + sizeof (GenerateKeyPairResponse_v2);
The obtained attribute buffer can be parsed as described in Parsing the Attribute Buffer.
Similarly, sign can be fetched as:
uint8_t * sign = (uint8_t *) attestedResponse + sizeof (GenerateKeyPairResponse_v2) + ulBufferSIze;
Case 3
flags = KEY_GEN_FLAG_GET_ATTR | KEY_GEN_FLAG_GET_ATTEST |
KEY_GEN_FLAG_EXCLUDE_HEADER;
Attributes | Signature |
---|
Attestation Response Format
Parsing the attestation response buffer:
As the header is not available, header fields cannot be used as offset. The only way to parse the buffer is by using the external fields of attestedLength of attestedResponseand ulAttrLen of pAttrObj.
uint8_t *Obj = (uint8_t *) attestedResponse or pAttrObj can be directly used as it also has the attributes.
Getting the sign:
uint8_t * sign = (uint8_t *) attestedResponse + ulAttrLen;
typedef struct {
Uint32 flags;
Uint32 ulSessionHandle;
Uint32 ulMech;
Uint8 *pAttestedResponse;
Uint32 attestedLength;
…..
…...
union {
keyArgs key;
struct {
keyArgs pubkey;
keyArgs privkey;
};
};
} genKeyArgs;
typedef struct {
Uint64 ulKeyHandle;
…....
Uint8 * pAttrObj; ///< key attributes
Uint32 ulAttrLen; ///< key attributes total length
} keyArgs;
ResponseHeader header;
Uint64 ulPublicKey;
Uint64 ulPrivateKey;
Uint8 ucOptionalKeyInfo[0];
Uint8 ucOptionalAttestation[0];
} GenerateKeyPairResponse_v2;
typedef struct {
Uint32 flags;
Uint32 ulSessionHandle;
union {
Uint32 ulFlags;
struct {
Uint32 ulForceInsert:1; ///< Force override the existing key handle if
it matches the object handle on unpark operation
Uint32 ephemeralStorage:1;
};
};
Uint64 ulObjectHandle; ///< key handle of parkable key on park operation and object handle to be assigned
for unparked object on unpark operation
Uint64 ulParkingKeyHandle;
Uint8 * pParkedObject; ///< parked object data
Uint32 ulParkedObjectLen; ///< length of parked object data
Uint8 * pAttestedResponse; ///< attestation response
Uint32 attestedLength; ///< length of attestation response
Uint32 request_id;
} parkOpArgs, unparkOpArgs;