Verifying attestations

For a key that is created in Cloud HSM, you can optionally request an attestation statement to provide evidence that the key is actually HSM-protected. The statement is a token that is cryptographically signed directly by the physical hardware and can be verified by the user. This topic describes how to verify the attestation statement signature and determine whether the attestation statement matches the key version.

The examples in this topic are for Linux environments.

Before you begin

Script for verifying an attestation

To help you verify an attestation, at the command line where you are going to verify the attestation, create verify.py. If you are using the Cloud Shell, you can use the Code editor to create this file. Use the following contents for this file.

#!/usr/bin/env python
from OpenSSL import crypto
import gzip,sys

_END_DELIM = "-----END CERTIFICATE-----"
def verify(blob, signature, certs_file):
  with open(certs_file, 'r') as cert_data:
    certs = [cert + _END_DELIM for cert in cert_data.read().split(_END_DELIM)
             if cert.strip(' \t\n\r')]
    for cert in certs:
      cert_obj = crypto.load_certificate(crypto.FILETYPE_PEM, cert)
      try:
        crypto.verify(cert_obj, signature, blob, 'sha256')
        return True
      except crypto.Error as er:
        continue
    return False

attest_unzipped = gzip.open(sys.argv.pop(), 'rb').read()
certs_file = sys.argv.pop()
blob, signature = attest_unzipped[:-256], attest_unzipped[-256:]
verified = verify(blob, signature, certs_file)
print('Signature verified!' if verified else 'Signature failed!')

Script for parsing an attestation

To help you parse an attestation, create a file named parse.py with the following contents.

#!/usr/bin/python
import io, gzip, sys, struct

HEADER = ">III"
TLV = ">II"
_CAVIUM_ATTESTATION_ASYM_OFFSET = 984
_CAVIUM_ATTESTATION_SYM_OFFSET = 24

def parse(attestation):
  # Parse Object, (Cavium stores its structures in big endian.)
  obj_handle, attr_count, obj_size = struct.unpack_from(HEADER, attestation, 0)
  obj_header_size = struct.calcsize(HEADER)
  attestation = attestation[obj_header_size:]
  while attr_count > 0:
    # Parse each Attribute.
    attr_type, attr_len = struct.unpack_from(TLV, attestation, 0)
    attestation = attestation[struct.calcsize(TLV):]
    print '0x%04x: %r' % (attr_type, attestation[:attr_len])
    attr_count -= 1
    attestation = attestation[attr_len:]
  return obj_header_size + obj_size

attest_unzipped = gzip.open(sys.argv.pop(), 'rb').read()
key_type = sys.argv.pop()
if key_type == 'symmetric':
  print "Private Key Attestation"
  parse(attest_unzipped[_CAVIUM_ATTESTATION_SYM_OFFSET:])
else:
  print "Public Key Attestation"
  offset = parse(attest_unzipped[_CAVIUM_ATTESTATION_ASYM_OFFSET:])
  print 'Private Key Attestation'
  parse(attest_unzipped[(_CAVIUM_ATTESTATION_ASYM_OFFSET + offset):])

Script for verifying a public key

To help you verify a public key, create a file named verify_pubkey.py with the following contents.

#!/usr/bin/env python
import sys

public_key_file = sys.argv.pop()
parsed_attest_file = sys.argv.pop()
with open(public_key_file, 'r') as public_key_data:
  # remove the "BEGIN" and "END" line.
  publickey = "".join(public_key_data.read().split('\n')[1:-2])
  publickey = base64.b64decode(publickey)
  # KCV is the first 6 bytes of the SHA1 digest of the key.
  kcv = hashlib.sha1(publickey).hexdigest()[:6]
  # EKCV is the SHA256 digest of the key
  ekcv = hashlib.sha256(publickey).hexdigest()

  with open(parsed_attest_file, 'r') as parsed_attest_data:
    if kcv in parsed_attest_data.read() and ekcv in parsed_attest_data.read():
      print 'Public key matched with attestation.'
    else:
      print 'Public key not matched with attestation.'
      attest_lines = parsed_attest_data.readlines()
      # field 0x0173 corresponds to KCV,
      # field 1003 (Custom Cavium) corresponds to EKCV
      kcv_lines = [line for line in attest_lines (if line.startswith('0x0173:') or if line.startswith('0x1003:'))]
      print(kcv_lines)

Retrieve the attestation file

You can retrieve the attestation for a cryptographic key version using the Google Cloud Platform Console or the command line.

Console

  1. Go to the Cryptographic Keys page in the GCP Console.
    Go to the Cryptographic Keys page

  2. Click the name of the key ring that contains the key whose key version you want to attest.

  3. Click the key whose key version you want to attest.

  4. For the key version that you want to attest, click the More icon (3 vertical dots).

  5. Click Get attestation.

  6. In the Get attestation dialog, click Download. The attestation file is downloaded to your computer.

The format for the name of the attestation file is [KEYRING]-[KEY]-[KEYVERSION]-attestation.dat. When you run the example commands in the remainder of this topic, replace attestation.dat with the name of the file that you downloaded.

Command-line

  1. Click the Activate Google Cloud Shell button at the top of the console window. Activate Google Cloud Shell A Cloud Shell session opens inside a new frame at the bottom of the console and displays a command-line prompt. It can take a few seconds for the shell session to be initialized. Cloud Shell session

  2. At the Cloud Shell command-line prompt, use the gcloud alpha kms keys version describe command to retrieve the attestation for the key that you want to attest. The --attestation-file flag specifies the path and filename destination for the retrieved attestation.

    gcloud alpha kms keys versions describe [KEY_VERSION] \
      --key [KEY_NAME] \
      --location [LOCATION] \
      --keyring [KEY_RING] \
      --attestation-file attestation.dat
    

Attest a key

The signature of an attestation can be verified by two certificates. One of the certificates leads up to the Google certificate authority (CA), and the other certificate leads up to the Cavium CA.

  1. Retrieve the certificate chain for the CA that you want to use.

    curl -O https://www.gstatic.com/cloudhsm/cloud-kms-prod-[LOCATION]-[google|cavium].pem
    

    Replace [LOCATION] with one of the regions supported by Cloud HSM. Replace [google|cavium] with either google or cavium. Here's an example for the us-west location and the Google CA:

    curl -O https://www.gstatic.com/cloudhsm/cloud-kms-prod-us-west1-google.pem
    

    To display the contents of the certificate, run:

    openssl x509 -in cloud-kms-prod-[LOCATION]-[google|cavium].pem -text
    
  2. Install the Python wrapper for OpenSSL.

    pip install --user pyopenssl
    
  3. At the Cloud Shell command-line prompt, run the verification tool.

    python verify.py \
      cloud-kms-prod-[LOCATION]-[google|cavium].pem attestation.dat
    

    The verification tool verifies the signature included at the end of the attestation file with the public key provided in the HSM certificate bundle. The bundle currently contains all certificate chains from all HSMs up to but not including the certificate authority (CA) certificate.

Verifying values in the attestation file

In addition to verifying the signature for the attestation, you can verify other parts of the attestation. For example, you can verify the key version ID in the attestation with the key version ID that is viewable in Cloud KMS.

Run the following to parse the attestation file.

python parse.py [symmetric|asymmetric] attestation.dat > parsed.dat

The parsed.dat file will be used for the following sections.

To verify the key version ID

You can verify whether the SHA-256 hash of the key version resource ID is present in the attestation.

  1. Get the key version resource ID for the key version. You can use the GCP Console to get the key version resource ID or you can run the following gcloud command:

    gcloud alpha kms keys versions list \
      --location [LOCATION] \
      --keyring [KEY_RING] --key [KEY]
    
  2. At the command line, assign resource_name to the key version resource ID that you just retrieved.

    resource_name=projects/[PROJECT_ID]/locations/[LOCATION]/keyRings/[KEY_RING]/cryptoKeys/[KEY]/cryptoKeyVersions/[VERSION]
    
  3. Run the attestation script to match the SHA-256 hash of the key version resource ID that you retrieved in Step 1 with what is present in the attestation.

    echo -n $resource_name | sha256sum | cut -d ' ' -f 1 | grep -f - parsed.dat
    

To verify key properties

You can view key properties such as whether the key is not extractable, whether it can be parked, and what cryptographic operations are supported by the key. These properties correspond to fields in the PKCS #11 standard.

To determine whether a key is extractable, examine the 0x0162 field:

grep '0x0162:' parsed.dat

To determine whether a key can be parked, examine the 0x0002 field:

grep '0x0002:' parsed.dat

To determine the key type, examine the 0x0100 field. Key types are also enumerated in the PCKS#11 standard with prefix CKK_*.

grep '0x0100:' parsed.dat

To verify the public key

For asymmetric keys, you can verify that the key pair generated matches the public key. You can compare the Key Checksum Value (KCV) and Extended KCV (EKCV) of the public key and match them with what is present in the attestation.

  1. Retrieve the public key locally.

    gcloud alpha kms keys versions get-public-key \
      [KEY_VERSION] --location [LOCATION] \
      --keyring [KEY_RING] --key [KEY] \
      --output-file /tmp/key.pub
    

    In addition to using gcloud, you can retrieve the public key using the GCP Console and the Cloud KMS API.

  2. Run the script to verify the public key.

    python verify_pubkey.py parsed.dat /tmp/key.pub
    

Additional information

  • You verify an attestation to determine whether a key version was created inside an HSM. Because the verification is intentionally independent of Google, you cannot verify an attestation using the Google Cloud Platform Console, the Cloud KMS API, or the gcloud command line.
Was this page helpful? Let us know how we did:

Send feedback about...

Cloud KMS Documentation