Skip to content

Add kms/attestations/ directory with sample script to verify attestations #3262

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
May 1, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions kms/attestations/README.rst.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# This file is used to generate README.rst

product:
name: Google Cloud Key Management Service
short_name: Cloud Key Management Service
url: https://cloud.google.com/kms/docs/
description: >
The `Cloud Key Management Service`_ allows you to create, import, and manage
cryptographic keys and perform cryptographic operations in a single centralized cloud service.

setup:
- install_deps

samples:
- name: Verify attestations for keys generated by Cloud HSM
file: verify_attestation.py
show_help: True

folder: kms/attestations
1 change: 1 addition & 0 deletions kms/attestations/requirements-test.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pytest==5.3.2
2 changes: 2 additions & 0 deletions kms/attestations/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
cryptography==2.8
pem==20.1.0
71 changes: 71 additions & 0 deletions kms/attestations/verify_attestation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
#!/usr/bin/env python

# Copyright 2020 Google, LLC.
#
# 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.

"""This sample demonstrates how to verify HSM attestations using certificate
bundles obtained from Cloud HSM.

For more information, visit https://cloud.google.com/kms/docs/attest-key.
"""

# [START verify_attestations]
import gzip

from cryptography import exceptions
from cryptography import x509
from cryptography.hazmat import backends
from cryptography.hazmat.primitives.asymmetric import padding
import pem


def verify(attestation_file, bundle_file):
"""Verifies an attestation using a bundle of certificates.

Args:
attestation_file: The name of the attestation file.
bundle_file: The name of the bundle file containing the certificates
used to verify the attestation.

Returns:
True if at least one of the certificates in bundle_file can verify the
attestation data and its signature.
"""
with gzip.open(attestation_file, 'rb') as f:
# An attestation file consists of a data portion and a 256 byte
# signature portion concatenated together.
attestation = f.read()
# Separate the components.
data = attestation[:-256]
signature = attestation[-256:]

# Verify the attestation with one of the certificates in the bundle
for cert in pem.parse_file(bundle_file):
cert_obj = x509.load_pem_x509_certificate(
str(cert).encode('utf-8'), backends.default_backend())
try:
# Check if the data was signed by the private key assosicated
# with the public key in the certificate. The data should have
# been signed with PKCS1v15 padding.
cert_obj.public_key().verify(
signature, data, padding.PKCS1v15(),
cert_obj.signature_hash_algorithm)
return True
except exceptions.InvalidSignature:
# Certificate bundles contain certificates that will not be
# able to verify the attestation, so the InvalidSignature
# errors can be ignored.
continue
return False
# [END verify_attestations]
109 changes: 109 additions & 0 deletions kms/attestations/verify_attestation_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
# Copyright 2020 Google, LLC.
#
# 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.

import tempfile

import verify_attestation

# Test certificate bundles can be generated with the following steps:
# 1. Generate test key pairs.
# - openssl genrsa -out test1.key 2048
# - openssl genrsa -out test2.key 2048
# 2. Generate test certificates using the key pairs.
# - openssl req -x509 -key test1.key -days 3650 -out test1.pem
# - openssl req -x509 -key test2.key -days 3650 -out test2.pem
# 3. Create a bundle using the test certificates.
# - cat test1.pem test2.pem > bundle.pem
# For instructions on downloading certificate bundles from Cloud HSM, refer to:
# https://cloud.google.com/kms/docs/attest-key#downloading_the_certificates
TEST_CERT_BUNDLE = b"""-----BEGIN CERTIFICATE-----
MIIDZDCCAkwCFE2PSNf++wMw+Jv86m41lbsa9aUMMA0GCSqGSIb3DQEBCwUAMFkx
CzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRl
cm5ldCBXaWRnaXRzIFB0eSBMdGQxEjAQBgNVBAMMCVRlc3QgQ2FyZDAeFw0yMDAz
MzEyMTQ0MjNaFw0zMDAzMjkyMTQ0MjNaMIGDMQswCQYDVQQGEwJBVTETMBEGA1UE
CAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRk
MSMwIQYDVQQLDBpjbG91ZC1rbXMtcHJvZC11cy1jZW50cmFsMTEXMBUGA1UEAwwO
VGVzdCBQYXJ0aXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDh
cOk8hil8G4vl66jb02eXu2rKO6PmNF1GziXtzawyhx/+PDmEuGu+g/8hpivY6vDr
buAMFs8OIBBBlfED/soVANflXktDRGqWJau+rIrw9EGcclcwzlIboIi6KLPcLih0
N0TTxqRLgy2EsEQ6UKS7su4bOIxD3Z6FSeTsoq+C2sgSWXmLitO0iRYYcgRjoyCU
kdzzO/JCwPKhhQx5NUrrHseALaIltG4D0aWLuBZKyV38yA1XEMdyCGk7RedEYC+v
OzaJrNToQBCIaCdn3F0uqJd49irLNPyQ5CY3NNL8rupJSq3iVxhEIZ8ANaU8UDvo
5iaQNKV1/KiQsXfUW6fbAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAIIGB0aXhd6k
xyWgYzJ0DHpYP2ALwHRWXde5PtEkdHDguPlUTzZ5iTfqOJlEeUXlDO9RV82jc4mE
hguCaMl3Q+2JGLJLCnSDgcaY5WAVBF9FSakdbHBj4Ik9L8NDlUQB6Me4d4gKWpg1
bUD4n2KtvCZGZzA3pfRBcYyAbwC+xEi1XrITyshb0pkjsWO4Urg36W/RpkCiYAw0
Xua0jJMG/wcF+xktd7kgcsBh5Es2VCzyQwisXoOIi3EY7lMJK2+ctjQFy1GxumBU
jBlXj0VjAm3QOVLTh3mfb1XofoIjOOYkMBjXMiQhFy/Lv68u5q7qlEYe92OKxkCO
0UaAcqt8+QM=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDRjCCAi4CFBVm+eV+oRkaYq2NyuTfwxWapjFOMA0GCSqGSIb3DQEBCwUAMGYx
CzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRl
cm5ldCBXaWRnaXRzIFB0eSBMdGQxHzAdBgNVBAMMFlRlc3QgTWFudWZhY3R1cmVy
IFJvb3QwHhcNMjAwMzMxMjE0MjU1WhcNMzAwMzI5MjE0MjU1WjBZMQswCQYDVQQG
EwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lk
Z2l0cyBQdHkgTHRkMRIwEAYDVQQDDAlUZXN0IENhcmQwggEiMA0GCSqGSIb3DQEB
AQUAA4IBDwAwggEKAoIBAQC28Wu0dusN6AYkYon8RIuHlJWWwZWlTxXSMK4v/IOY
pG9F2/gUEDMQOgpyCCpTc5eLHRPa/Z2QgB0c2VSlQC8FZ1l9/YL7uBTJ0UpDoBf8
LUimIqotneXpL+7CW1kWFLZIgpm0iVuTPjV2b3frtvu0B+nYuyo4dtToebqoOKse
F3ymLsAjSqA9aoCD0XbspAkLJIvdQU28vXY4Y2y0OTGUnaQ7ZDwkNLxeAfeIdNJD
FRCcYsLRopsyptFMYLLDrI70gywAGYaGOxYG8747BIZELyT5Gnt0o7JwpuF8Mi53
T5NGiu5/wLwXnxRRhb3M5+lStdTfvbEfgK1mC0ac8ym5AgMBAAEwDQYJKoZIhvcN
AQELBQADggEBAILH0Q8WlgaGBosPBjnpAsp4eZOfq9skKZERYzFA2wBAy4B0q9/S
3oL1MIZEU6/ckiFyAm3r4/ZxMLX0UrisuRQUFQ3w+gqFccqlmGETsZGJpPtfIm+n
JQo44XXnTHndqoYPNfvfQkp0WtJQS8hSgTASP+1GYjqOn1fZaa28m39/mx9Mrw7K
xtXOtrdKqJhWCWJPprfC5sYNCYTA2HXVmBU2Y4AP4A4w+A1gCAdzvH8EEyDjnvxJ
GEa4uczhA3n+NmhLipg1aGbxJO5ZHXdyFF2rTXVVXSiX8EEasnwcTDjeXDKhdpu6
biaxW9fnsJIXirAE03FFkC/tWelkGFkmMfs=
-----END CERTIFICATE-----"""

# Test attestations can be generated with the following steps:
# 1. Create a file containing the test attestation statement.
# - echo "content" > attestation.dat
# 2. Sign the file with one the key pairs used to create the test certificates.
# - openssl dgst -sha256 -sign test1.key -out signature.dat attestation.dat
# 3. Concatenate the signature to the statement to create an attestation.
# - cat signature.dat >> attestation.dat
# 4. Compress the test attestation.
# - gzip attestation.dat
# For instructions on downloading attestations from Cloud HSM, refer to:
# https://cloud.google.com/kms/docs/attest-key#downloading_the_attestation_statement
TEST_ATTESTATION_GZ = (
b'\x1f\x8b\x08\x08\xda\xde\x84^\x00\x03attestation\x00\x01\x06\x01\xf9\xfe'
b'\x15\xa7~W\xdazHq03\x95\xd1F\xcf\x1d\n\xe0\xbbv\x11\xed\xae\x186\xc0\xcc'
b'.\xcf)\xf1?\xf7!\xf3\xd6M\x85\xfe\xb9\x84\xb2\x08V2(\xa1\x87]\xab\x01='
b'\xb5\x0f)~\x06\xee\xfa/\x94\xa6x\x96o\xb1\xcb$\x82\x90\xe03J\t\x03\xf0'
b'\xa4\xa5\xa9\xf9\xb2\xce\xdd2\xfam\x94W\x07\x00~\xa5\xc2\xcdq\xa1\x81'
b'\x18\x83\xe0\xd9\x11k]\xbd\xf8\x81@\x9c*\x80\x91R\xb0\xae\x9d\x88\xb8T'
b'\xd1>\xf6;\xe4\x83q%_\x8aw\x894\xb5:\xeab\xd2\x9a\x81\xdd\xa6\xf9\x94'
b'\xff8\xb1\xed\x7fs\x0e\xc0\xde\x89\x00]\x8fL\x82\x8a\x11\x8f\\\xe483\x9d'
b'&\x0b%\xfd\x0et\x8f\xa8\x1a\xb5K\xb4\xc7\x96\xd1}\x06\xdd\x93i\x1f\xc1@'
b'\x92\xef}(B\x0f\xd1\x03\xaaYo\x9b\xad\xa9zw#\xc8\x9a\xad\x94\xfc\x07q]x'
b'\xeb\xa2CA\xf8\xac\x96\xd9\xe5y4\xae]\x81\xb0$\x93\tx\xdb\xcc\x08:%\x1d'
b'\xe2q\xaa\xc8:\xc2<C\xb5\x9b\xd1\xb0<\x12\x8a\x99f\x11\x9a"Q\x1d]\xac'
b'\x81\xe2\x05\x06\x01\x00\x00')


def test_verify():
with tempfile.NamedTemporaryFile() as attestation_file:
with tempfile.NamedTemporaryFile() as bundle_file:
attestation_file.write(TEST_ATTESTATION_GZ)
bundle_file.write(TEST_CERT_BUNDLE)

attestation_file.seek(0)
bundle_file.seek(0)

assert verify_attestation.verify(
attestation_file.name, bundle_file.name)