Skip to content

Commit

Permalink
Merge pull request #604 from lukpueh/crypto-signer-keygen-and-import
Browse files Browse the repository at this point in the history
Add replacement API for '*keys' and 'interface' key generation and import
  • Loading branch information
lukpueh committed Aug 11, 2023
2 parents b3a7c03 + 3951fed commit 5c6d0be
Show file tree
Hide file tree
Showing 25 changed files with 1,009 additions and 185 deletions.
97 changes: 97 additions & 0 deletions docs/CRYPTO_SIGNER.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@

# CryptoSigner

`CryptoSigner` is a modern replacement for the legacy `securesystemslib.keys`
module. It can be used via the `Signer.from_priv_key_uri` API to load private
*rsa*, *ecdsa* and *ed25519* keys from file. It also provides API to generate
in-memory signers for ad-hoc signing.

## Code examples

### Example 1: Ad-hoc signing

`CryptoSigner` provides `generate_{rsa, ed25519, ecdsa}` methods for ad-hoc
signing and signature verification, e.g. in tests or demos.

```python
from securesystemslib.signer import CryptoSigner

signer = CryptoSigner.generate_ed25519()
signature = signer.sign(b"data")
signer.public_key.verify_signature(signature, b"data")
```

### Example 2: Asynchronous key management and signing

The typical Signer API usage is described in
[this blog post](https://theupdateframework.github.io/python-tuf/2023/01/24/securesystemslib-signer-api.html)
and outlined below for a file-based signer.

#### 1. Generate key files
*`CryptoSigner` does not provide API to generate key files. Compatible
keys can be generated with standard tools like `openssl genpkey` (CLI) or
`pyca/cryptography` (Python).*

```python
from cryptography.hazmat.primitives import asymmetric, serialization

# Generate key pair
private_key = asymmetric.ed25519.Ed25519PrivateKey.generate()

# Serialize private key as encrypted PEM/PKCS8
private_pem = private_key.private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.PKCS8,
encryption_algorithm=serialization.BestAvailableEncryption(b"hunter2"),
)

# Serialize public key as encrypted PEM/subjectPublicKeyInfo
public_pem = private_key.public_key().public_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PublicFormat.SubjectPublicKeyInfo,
)

# Write key files
with open("private.pem", "wb") as f:
f.write(private_pem)
with open("public.pem", "wb") as f:
f.write(public_pem)
```

#### 2. Prepare signing environment

```python
import os
from securesystemslib.signer import SSlibKey

with open("public.pem", "rb") as f:
public_bytes = f.read()

# Make public key, signer URI, and key decryption password available to the
# signer, e.g. via environment variables. The private key file must also be
# available to the signer at the specified path.
os.environ.update({
"SIGNER_URI": "file:private.pem?encrypted=true",
"SIGNER_PUBLIC": public_bytes.decode(),
"SIGNER_SECRET": "hunter2"
})
```

#### 3. Load and use signer

```python
import os
from securesystemslib.signer import SSlibKey, Signer, CryptoSigner, SIGNER_FOR_URI_SCHEME

# NOTE: Registration becomes obsolete once CryptoSigner is the default file signer
SIGNER_FOR_URI_SCHEME.update({CryptoSigner.FILE_URI_SCHEME: CryptoSigner})

# Read signer details
uri = os.environ["SIGNER_URI"]
public_key = SSlibKey.from_pem(os.environ["SIGNER_PUBLIC"].encode())
secrets_handler = lambda sec: os.environ["SIGNER_SECRET"]

# Load and sign
signer = Signer.from_priv_key_uri(uri, public_key, secrets_handler)
signer.sign(b"data")
```
3 changes: 2 additions & 1 deletion securesystemslib/signer/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"""
from securesystemslib.signer._aws_signer import AWSSigner
from securesystemslib.signer._azure_signer import AzureSigner
from securesystemslib.signer._crypto_signer import CryptoSigner
from securesystemslib.signer._gcp_signer import GCPSigner
from securesystemslib.signer._gpg_signer import GPGKey, GPGSigner
from securesystemslib.signer._hsm_signer import HSMSigner
Expand All @@ -15,14 +16,14 @@
SIGNER_FOR_URI_SCHEME,
SecretsHandler,
Signer,
SSlibSigner,
)
from securesystemslib.signer._sigstore_signer import SigstoreKey, SigstoreSigner
from securesystemslib.signer._spx_signer import (
SpxKey,
SpxSigner,
generate_spx_key_pair,
)
from securesystemslib.signer._sslib_signer import SSlibSigner

# Register supported private key uri schemes and the Signers implementing them
SIGNER_FOR_URI_SCHEME.update(
Expand Down
12 changes: 4 additions & 8 deletions securesystemslib/signer/_aws_signer.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,9 @@
import securesystemslib.hash as sslib_hash
from securesystemslib import exceptions
from securesystemslib.exceptions import UnsupportedLibraryError
from securesystemslib.signer._key import Key
from securesystemslib.signer._signer import (
SecretsHandler,
Signature,
Signer,
SSlibKey,
)
from securesystemslib.signer._key import Key, SSlibKey
from securesystemslib.signer._signer import SecretsHandler, Signature, Signer
from securesystemslib.signer._utils import compute_default_keyid

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -127,7 +123,7 @@ def import_(cls, aws_key_id: str, local_scheme: str) -> Tuple[str, Key]:
) from e

keyval = {"public": public_key_pem}
keyid = cls._get_keyid(keytype, local_scheme, keyval)
keyid = compute_default_keyid(keytype, local_scheme, keyval)
public_key = SSlibKey(keyid, keytype, local_scheme, keyval)
return f"{cls.SCHEME}:{aws_key_id}", public_key

Expand Down
12 changes: 4 additions & 8 deletions securesystemslib/signer/_azure_signer.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,9 @@

import securesystemslib.hash as sslib_hash
from securesystemslib.exceptions import UnsupportedLibraryError
from securesystemslib.signer._key import Key
from securesystemslib.signer._signer import (
SecretsHandler,
Signature,
Signer,
SSlibKey,
)
from securesystemslib.signer._key import Key, SSlibKey
from securesystemslib.signer._signer import SecretsHandler, Signature, Signer
from securesystemslib.signer._utils import compute_default_keyid

AZURE_IMPORT_ERROR = None
try:
Expand Down Expand Up @@ -231,7 +227,7 @@ def import_(cls, az_vault_name: str, az_key_name: str) -> Tuple[str, Key]:

keytype, scheme = cls._get_keytype_and_scheme(key_vault_key.key.crv)
keyval = {"public": pem.decode("utf-8")}
keyid = cls._get_keyid(keytype, scheme, keyval)
keyid = compute_default_keyid(keytype, scheme, keyval)
public_key = SSlibKey(keyid, keytype, scheme, keyval)
priv_key_uri = key_vault_key.key.kid.replace("https:", "azurekms:")

Expand Down
Loading

0 comments on commit 5c6d0be

Please sign in to comment.