Skip to content

Commit b625bd5

Browse files
sethvargorsamborski
authored andcommitted
feat(kms): add samples for new hmac and rng apis (#161)
1 parent 47e0e8d commit b625bd5

8 files changed

+288
-3
lines changed

kms/snippets/create_key_asymmetric_decrypt.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ def create_key_asymmetric_decrypt(project_id, location_id, key_ring_id, id):
3030

3131
# Import the client library.
3232
from google.cloud import kms
33+
from google.protobuf import duration_pb2
34+
import datetime
3335

3436
# Create the client.
3537
client = kms.KeyManagementServiceClient()
@@ -44,7 +46,11 @@ def create_key_asymmetric_decrypt(project_id, location_id, key_ring_id, id):
4446
'purpose': purpose,
4547
'version_template': {
4648
'algorithm': algorithm,
47-
}
49+
},
50+
51+
# Optional: customize how long key versions should be kept before
52+
# destroying.
53+
'destroy_scheduled_duration': duration_pb2.Duration().FromTimedelta(datetime.timedelta(days=1))
4854
}
4955

5056
# Call the API.

kms/snippets/create_key_asymmetric_sign.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ def create_key_asymmetric_sign(project_id, location_id, key_ring_id, id):
3030

3131
# Import the client library.
3232
from google.cloud import kms
33+
from google.protobuf import duration_pb2
34+
import datetime
3335

3436
# Create the client.
3537
client = kms.KeyManagementServiceClient()
@@ -44,7 +46,11 @@ def create_key_asymmetric_sign(project_id, location_id, key_ring_id, id):
4446
'purpose': purpose,
4547
'version_template': {
4648
'algorithm': algorithm,
47-
}
49+
},
50+
51+
# Optional: customize how long key versions should be kept before
52+
# destroying.
53+
'destroy_scheduled_duration': duration_pb2.Duration().FromTimedelta(datetime.timedelta(days=1))
4854
}
4955

5056
# Call the API.

kms/snippets/create_key_hsm.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ def create_key_hsm(project_id, location_id, key_ring_id, id):
3030

3131
# Import the client library.
3232
from google.cloud import kms
33+
from google.protobuf import duration_pb2
34+
import datetime
3335

3436
# Create the client.
3537
client = kms.KeyManagementServiceClient()
@@ -46,7 +48,11 @@ def create_key_hsm(project_id, location_id, key_ring_id, id):
4648
'version_template': {
4749
'algorithm': algorithm,
4850
'protection_level': protection_level
49-
}
51+
},
52+
53+
# Optional: customize how long key versions should be kept before
54+
# destroying.
55+
'destroy_scheduled_duration': duration_pb2.Duration().FromTimedelta(datetime.timedelta(days=1))
5056
}
5157

5258
# Call the API.

kms/snippets/create_key_mac.py

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
# Copyright 2021 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
14+
15+
# [START kms_create_key_mac]
16+
def create_key_mac(project_id, location_id, key_ring_id, id):
17+
"""
18+
Creates a new key in Cloud KMS for HMAC operations.
19+
20+
Args:
21+
project_id (string): Google Cloud project ID (e.g. 'my-project').
22+
location_id (string): Cloud KMS location (e.g. 'us-east1').
23+
key_ring_id (string): ID of the Cloud KMS key ring (e.g. 'my-key-ring').
24+
id (string): ID of the key to create (e.g. 'my-mac-key').
25+
26+
Returns:
27+
CryptoKey: Cloud KMS key.
28+
29+
"""
30+
31+
# Import the client library.
32+
from google.cloud import kms
33+
from google.protobuf import duration_pb2
34+
import datetime
35+
36+
# Create the client.
37+
client = kms.KeyManagementServiceClient()
38+
39+
# Build the parent key ring name.
40+
key_ring_name = client.key_ring_path(project_id, location_id, key_ring_id)
41+
42+
# Build the key.
43+
purpose = kms.CryptoKey.CryptoKeyPurpose.MAC
44+
algorithm = kms.CryptoKeyVersion.CryptoKeyVersionAlgorithm.HMAC_SHA256
45+
key = {
46+
'purpose': purpose,
47+
'version_template': {
48+
'algorithm': algorithm,
49+
},
50+
51+
# Optional: customize how long key versions should be kept before
52+
# destroying.
53+
'destroy_scheduled_duration': duration_pb2.Duration().FromTimedelta(datetime.timedelta(days=1))
54+
}
55+
56+
# Call the API.
57+
created_key = client.create_crypto_key(request={'parent': key_ring_name, 'crypto_key_id': id, 'crypto_key': key})
58+
print('Created mac key: {}'.format(created_key.name))
59+
return created_key
60+
# [END kms_create_key_mac]

kms/snippets/generate_random_bytes.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# Copyright 2021 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
14+
15+
# [START kms_generate_random_bytes]
16+
def generate_random_bytes(project_id, location_id, num_bytes):
17+
"""
18+
Generate random bytes with entropy sourced from the given location.
19+
20+
Args:
21+
project_id (string): Google Cloud project ID (e.g. 'my-project').
22+
location_id (string): Cloud KMS location (e.g. 'us-east1').
23+
num_bytes (integer): number of bytes of random data.
24+
25+
Returns:
26+
bytes: Encrypted ciphertext.
27+
28+
"""
29+
30+
# Import the client library.
31+
from google.cloud import kms
32+
33+
# Import base64 for encoding the bytes for printing.
34+
import base64
35+
36+
# Create the client.
37+
client = kms.KeyManagementServiceClient()
38+
39+
# Build the location name.
40+
location_name = client.common_location_path(project_id, location_id)
41+
42+
# Call the API.
43+
protection_level = kms.ProtectionLevel.HSM
44+
random_bytes_response = client.generate_random_bytes(
45+
request={'location': location_name, 'length_bytes': num_bytes, 'protection_level': protection_level})
46+
47+
print('Random bytes: {}'.format(base64.b64encode(random_bytes_response.data)))
48+
return random_bytes_response
49+
# [END kms_generate_random_bytes]

kms/snippets/sign_mac.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# Copyright 2021 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
14+
15+
# [START kms_sign_mac]
16+
def sign_mac(project_id, location_id, key_ring_id, key_id, version_id, data):
17+
"""
18+
Sign a message using the public key part of an asymmetric key.
19+
20+
Args:
21+
project_id (string): Google Cloud project ID (e.g. 'my-project').
22+
location_id (string): Cloud KMS location (e.g. 'us-east1').
23+
key_ring_id (string): ID of the Cloud KMS key ring (e.g. 'my-key-ring').
24+
key_id (string): ID of the key to use (e.g. 'my-key').
25+
version_id (string): Version to use (e.g. '1').
26+
data (string): Data to sign.
27+
28+
Returns:
29+
MacSignResponse: Signature.
30+
"""
31+
32+
# Import the client library.
33+
from google.cloud import kms
34+
35+
# Import base64 for printing the ciphertext.
36+
import base64
37+
38+
# Create the client.
39+
client = kms.KeyManagementServiceClient()
40+
41+
# Build the key version name.
42+
key_version_name = client.crypto_key_version_path(project_id, location_id, key_ring_id, key_id, version_id)
43+
44+
# Convert the message to bytes.
45+
data_bytes = data.encode('utf-8')
46+
47+
# Call the API
48+
sign_response = client.mac_sign(
49+
request={'name': key_version_name, 'data': data_bytes})
50+
51+
print('Signature: {}'.format(base64.b64encode(sign_response.mac)))
52+
return sign_response
53+
# [END kms_sign_mac]

kms/snippets/snippets_test.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
from create_key_for_import import create_key_for_import
3333
from create_key_hsm import create_key_hsm
3434
from create_key_labels import create_key_labels
35+
from create_key_mac import create_key_mac
3536
from create_key_ring import create_key_ring
3637
from create_key_rotation_schedule import create_key_rotation_schedule
3738
from create_key_symmetric_encrypt_decrypt import create_key_symmetric_encrypt_decrypt
@@ -43,6 +44,7 @@
4344
from enable_key_version import enable_key_version
4445
from encrypt_asymmetric import encrypt_asymmetric
4546
from encrypt_symmetric import encrypt_symmetric
47+
from generate_random_bytes import generate_random_bytes
4648
from get_key_labels import get_key_labels
4749
from get_key_version_attestation import get_key_version_attestation
4850
from get_public_key import get_public_key
@@ -53,13 +55,15 @@
5355
from quickstart import quickstart
5456
from restore_key_version import restore_key_version
5557
from sign_asymmetric import sign_asymmetric
58+
from sign_mac import sign_mac
5659
from update_key_add_rotation import update_key_add_rotation
5760
from update_key_remove_labels import update_key_remove_labels
5861
from update_key_remove_rotation import update_key_remove_rotation
5962
from update_key_set_primary import update_key_set_primary
6063
from update_key_update_labels import update_key_update_labels
6164
from verify_asymmetric_ec import verify_asymmetric_ec
6265
from verify_asymmetric_rsa import verify_asymmetric_rsa
66+
from verify_mac import verify_mac
6367

6468

6569
@pytest.fixture(scope="module")
@@ -167,6 +171,22 @@ def hsm_key_id(client, project_id, location_id, key_ring_id):
167171
return key_id
168172

169173

174+
@pytest.fixture(scope="module")
175+
def hmac_key_id(client, project_id, location_id, key_ring_id):
176+
key_ring_name = client.key_ring_path(project_id, location_id, key_ring_id)
177+
key_id = '{}'.format(uuid.uuid4())
178+
key = client.create_crypto_key(request={'parent': key_ring_name, 'crypto_key_id': key_id, 'crypto_key': {
179+
'purpose': kms.CryptoKey.CryptoKeyPurpose.MAC,
180+
'version_template': {
181+
'algorithm': kms.CryptoKeyVersion.CryptoKeyVersionAlgorithm.HMAC_SHA256,
182+
'protection_level': kms.ProtectionLevel.HSM
183+
},
184+
'labels': {'foo': 'bar', 'zip': 'zap'}
185+
}})
186+
wait_for_ready(client, '{}/cryptoKeyVersions/1'.format(key.name))
187+
return key_id
188+
189+
170190
@pytest.fixture(scope="module")
171191
def symmetric_key_id(client, project_id, location_id, key_ring_id):
172192
key_ring_name = client.key_ring_path(project_id, location_id, key_ring_id)
@@ -245,6 +265,13 @@ def test_create_key_labels(project_id, location_id, key_ring_id):
245265
assert key.labels == {'team': 'alpha', 'cost_center': 'cc1234'}
246266

247267

268+
def test_create_key_mac(project_id, location_id, key_ring_id):
269+
key_id = '{}'.format(uuid.uuid4())
270+
key = create_key_mac(project_id, location_id, key_ring_id, key_id)
271+
assert key.purpose == kms.CryptoKey.CryptoKeyPurpose.MAC
272+
assert key.version_template.algorithm == kms.CryptoKeyVersion.CryptoKeyVersionAlgorithm.HMAC_SHA256
273+
274+
248275
def test_create_key_ring(project_id, location_id):
249276
key_ring_id = '{}'.format(uuid.uuid4())
250277
key_ring = create_key_ring(project_id, location_id, key_ring_id)
@@ -345,6 +372,11 @@ def test_encrypt_symmetric(client, project_id, location_id, key_ring_id, symmetr
345372
assert decrypt_response.plaintext == plaintext.encode('utf-8')
346373

347374

375+
def test_generate_random_bytes(client, project_id, location_id):
376+
generate_random_bytes_response = generate_random_bytes(project_id, location_id, 256)
377+
assert len(generate_random_bytes_response.data) == 256
378+
379+
348380
def test_get_key_labels(project_id, location_id, key_ring_id, symmetric_key_id):
349381
key = get_key_labels(project_id, location_id, key_ring_id, symmetric_key_id)
350382
assert key.labels == {'foo': 'bar', 'zip': 'zap'}
@@ -412,6 +444,18 @@ def test_sign_asymmetric(client, project_id, location_id, key_ring_id, asymmetri
412444
pytest.fail('invalid signature')
413445

414446

447+
def test_sign_mac(client, project_id, location_id, key_ring_id, hmac_key_id):
448+
data = 'my data'
449+
450+
sign_response = sign_mac(project_id, location_id, key_ring_id, hmac_key_id, '1', data)
451+
assert sign_response.mac
452+
453+
key_version_name = client.crypto_key_version_path(project_id, location_id, key_ring_id, hmac_key_id, '1')
454+
verify_response = client.mac_verify(request={'name': key_version_name, 'data': data.encode('utf-8'), 'mac': sign_response.mac})
455+
456+
assert verify_response.success
457+
458+
415459
def test_update_key_add_rotation(project_id, location_id, key_ring_id, symmetric_key_id):
416460
key = update_key_add_rotation(project_id, location_id, key_ring_id, symmetric_key_id)
417461
assert key.rotation_period == datetime.timedelta(seconds=60*60*24*30)
@@ -461,6 +505,16 @@ def test_verify_asymmetric_rsa(client, project_id, location_id, key_ring_id, asy
461505
assert verified
462506

463507

508+
def test_verify_mac(client, project_id, location_id, key_ring_id, hmac_key_id):
509+
data = 'my data'
510+
511+
key_version_name = client.crypto_key_version_path(project_id, location_id, key_ring_id, hmac_key_id, '1')
512+
sign_response = client.mac_sign(request={'name': key_version_name, 'data': data.encode('utf-8')})
513+
514+
verify_response = verify_mac(project_id, location_id, key_ring_id, hmac_key_id, '1', data, sign_response.mac)
515+
assert verify_response.success
516+
517+
464518
def test_quickstart(project_id, location_id):
465519
key_rings = quickstart(project_id, location_id)
466520
assert key_rings

kms/snippets/verify_mac.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# Copyright 2021 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
14+
15+
# [START kms_verify_mac]
16+
def verify_mac(project_id, location_id, key_ring_id, key_id, version_id, data, signature):
17+
"""
18+
Verify the signature of data from an HMAC key.
19+
20+
Args:
21+
project_id (string): Google Cloud project ID (e.g. 'my-project').
22+
location_id (string): Cloud KMS location (e.g. 'us-east1').
23+
key_ring_id (string): ID of the Cloud KMS key ring (e.g. 'my-key-ring').
24+
key_id (string): ID of the key to use (e.g. 'my-key').
25+
version_id (string): Version to use (e.g. '1').
26+
data (string): Data that was signed.
27+
signature (bytes): Signature bytes.
28+
29+
Returns:
30+
MacVerifyResponse: Success.
31+
"""
32+
33+
# Import the client library.
34+
from google.cloud import kms
35+
36+
# Create the client.
37+
client = kms.KeyManagementServiceClient()
38+
39+
# Build the key version name.
40+
key_version_name = client.crypto_key_version_path(project_id, location_id, key_ring_id, key_id, version_id)
41+
42+
# Convert the message to bytes.
43+
data_bytes = data.encode('utf-8')
44+
45+
# Call the API
46+
verify_response = client.mac_verify(
47+
request={'name': key_version_name, 'data': data_bytes, 'mac': signature})
48+
49+
print('Verified: {}'.format(verify_response.success))
50+
return verify_response
51+
# [END kms_verify_mac]

0 commit comments

Comments
 (0)