Skip to content

Commit

Permalink
Initial GCP Signer implementation
Browse files Browse the repository at this point in the history
Very bare bones Signer for Google Cloud KMS: Private keys live in KMS,
signing happens in KMS (although payloading hash happens in Signer).

This is not super usable without issue secure-systems-lab#447 but demonstrates the simplicity.
With 447, the usage pattern nwould be

    signer = Signer(
        "gcpkms://projects/openssf/locations/global/keyRings/securesystemslib-test-keyring/cryptoKeys/securesystemslib-test-key/cryptoKeyVersions/1",
        pubkey
    )

so all GCP specific details are in the private key URI.

Key creation is not supported at this point.
  • Loading branch information
jku committed Nov 2, 2022
1 parent 600aca1 commit 92c7d20
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 0 deletions.
29 changes: 29 additions & 0 deletions .github/workflows/test-gcp-kms.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@

name: Test GCP KMS

on:
workflow_dispatch:

jobs:
test:
runs-on: ubuntu-20.04
permissions:
id-token: 'write'
steps:
- uses: 'actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8'
- name: 'Authenticate to Google Cloud'
id: 'auth'
uses: 'google-github-actions/auth@c4799db9111fba4461e9f9da8732e5057b394f72'
with:
token_format: 'access_token'
workload_identity_provider: 'projects/367732848534/locations/global/workloadIdentityPools/securesystemslib-test-pool/providers/securesystemslib-test-provider'
service_account: '[email protected]'

- name: 'Install google-cloud-kms'
run: pip install google-cloud-kms

- name: 'Sign with KMSSigner'
run: |
python3 test-signer.py
46 changes: 46 additions & 0 deletions securesystemslib/signer.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,23 @@
"""

import abc
import logging
from typing import Any, Dict, Mapping, Optional

import securesystemslib.gpg.functions as gpg
import securesystemslib.hash as sslib_hash
import securesystemslib.keys as sslib_keys
from securesystemslib import exceptions

logger = logging.getLogger(__name__)

GCP_IMPORT_ERROR = None
try:
from google.cloud import kms
except ImportError:
GCP_IMPORT_ERROR = (
"google-cloud-kms library required to sign with Google Cloud keys."
)


class Signature:
Expand Down Expand Up @@ -266,3 +279,36 @@ def sign(self, payload: bytes) -> GPGSignature:

sig_dict = gpg.create_signature(payload, self.keyid, self.homedir)
return GPGSignature(**sig_dict)


class GCPSigner(Signer):
"""Google Cloud KMS Signer
There is no way to input Google Cloud credentials: assumption is that they will
be found in the runtime environment by google.cloud.kms, see
https://cloud.google.com/docs/authentication/getting-started
"""

def __init__(self, gcp_keyid: str, hash_algo: str, keyid: str):
if GCP_IMPORT_ERROR:
raise exceptions.UnsupportedLibraryError(GCP_IMPORT_ERROR)

self.gcp_keyid = gcp_keyid
self.hash_algo = hash_algo
self.keyid = keyid
self.client = kms.KeyManagementServiceClient()

def sign(self, payload: bytes) -> Signature:
# NOTE: request and response can contain CRC32C of the digest/sig:
# This could be useful but would require another dependency...

hasher = sslib_hash.digest(self.hash_algo)
hasher.update(payload)
digest = {self.hash_algo: hasher.digest()}
request = {"name": self.gcp_keyid, "digest": digest}

logger.debug("signing request %s", request)
response = self.client.asymmetric_sign(request)
logger.debug("signing response %s", response)

return Signature(self.keyid, response.signature.hex())
27 changes: 27 additions & 0 deletions test-signer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
from securesystemslib import keys
from securesystemslib.signer import GCPSigner

data = "data".encode("utf-8")

pubkey = {
"keyid": "abcd",
"keytype": "ecdsa",
"scheme": "ecdsa-sha2-nistp256",
"keyval": {
"public": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEDJchWswdXOBpMqXkekAzwuWjL+Hx\ncw2ZonDbixh/wTf1FkpxmT8Aq6/WN6NNXOW4Rw9Lua2aKLZo2ZeNrk2VLA==\n-----END PUBLIC KEY-----\n"
},
}


gcp_id = "projects/openssf/locations/global/keyRings/securesystemslib-test-keyring/cryptoKeys/securesystemslib-test-key/cryptoKeyVersions/1"
# This should be parsed from pubkey
hash_algo = "sha256"

signer = GCPSigner(gcp_id, hash_algo, pubkey["keyid"])
sig = signer.sign(data)

if not keys.verify_signature(pubkey, sig.to_dict(), data):
raise RuntimeError(
f"Failed to verify signature by {pubkey['keyid']}: sig was {sig.to_dict()}"
)
print("OK")

0 comments on commit 92c7d20

Please sign in to comment.