Skip to content

Commit

Permalink
signer: replace SpxSigner.new_ with raw keygen func
Browse files Browse the repository at this point in the history
* Removes previously added SpxSigner.new_ key pair generation method.
  This method may be convenient to quickly test spx signing, but does
  not fit well into the signer API usage pattern, where key generation;
  public key and signer URI loading; and signer loading (from URI) plus
  signing are separate workflows.

* Adds function to generate and return raw key pair bytes.
  * This function is "syntactic sugar" (quote @jku) over the PySPX library.
  * It is added here to provide consistent error handling, if the
    optional library is not available.
  * Otherwise, it needs additional tooling to fit well into the signer
    API, e.g. an interface that stores the created private bytes to a
    place from where they can be loaded using Signer.from_priv_key_uri.

* Adds helper factory method to create an SpxKey instance from raw bytes.

* Adopts changes in usage example and tests.
  • Loading branch information
lukpueh committed May 23, 2023
1 parent af99755 commit c0671c4
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 26 deletions.
6 changes: 5 additions & 1 deletion securesystemslib/signer/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,11 @@
SSlibSigner,
)
from securesystemslib.signer._sigstore_signer import SigstoreKey, SigstoreSigner
from securesystemslib.signer._spx_signer import SpxKey, SpxSigner
from securesystemslib.signer._spx_signer import (
SpxKey,
SpxSigner,
generate_spx_key_pair,
)

# Register supported private key uri schemes and the Signers implementing them
SIGNER_FOR_URI_SCHEME.update(
Expand Down
51 changes: 27 additions & 24 deletions securesystemslib/signer/_spx_signer.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"""
import logging
import os
from typing import Any, Dict, Optional
from typing import Any, Dict, Optional, Tuple

from securesystemslib.exceptions import (
UnsupportedLibraryError,
Expand All @@ -25,6 +25,17 @@
logger = logging.getLogger(__name__)


def generate_spx_key_pair() -> Tuple[bytes, bytes]:
"""Generate SPHINCS+ key pair and return public and private bytes."""
if SPX_IMPORT_ERROR:
raise UnsupportedLibraryError(SPX_IMPORT_ERROR)

seed = os.urandom(_SHAKE_SEED_LEN)
public, private = shake_128s.generate_keypair(seed)

return public, private


class SpxKey(Key):
"""SPHINCS+ verifier."""

Expand All @@ -36,6 +47,18 @@ def from_dict(cls, keyid: str, key_dict: Dict[str, Any]) -> "SpxKey":
keytype, scheme, keyval = cls._from_dict(key_dict)
return cls(keyid, keytype, scheme, keyval, key_dict)

@classmethod
def from_bytes(cls, public: bytes) -> "SpxKey":
"""Create SpxKey instance from public key bytes."""
keytype = cls.DEFAULT_KEY_TYPE
scheme = cls.DEFAULT_SCHEME
keyval = {"public": public.hex()}

keyid = SpxSigner._get_keyid( # pylint: disable=protected-access
keytype, scheme, keyval
)
return cls(keyid, keytype, scheme, keyval)

def to_dict(self) -> Dict[str, Any]:
return self._to_dict()

Expand Down Expand Up @@ -67,7 +90,9 @@ class SpxSigner(Signer):
Usage::
signer = SpxSigner.new_()
public_bytes, private_bytes = generate_spx_key_pair()
public_key = SpxKey.from_bytes(public_bytes)
signer = SpxSigner(private_bytes, public_key)
signature = signer.sign(b"payload")
# Use public_key.to_dict() / Key.from_dict() to transport public key data
Expand All @@ -80,28 +105,6 @@ def __init__(self, private: bytes, public: SpxKey):
self.private_key = private
self.public_key = public

@classmethod
def new_(cls) -> "SpxSigner":
"""Generate new SPHINCS+ key pair and return as SpxSigner.
NOTE: The Signer API is still experimental and key generation in
particular (see #466).
"""
if SPX_IMPORT_ERROR:
raise UnsupportedLibraryError(SPX_IMPORT_ERROR)

seed = os.urandom(_SHAKE_SEED_LEN)
public, private = shake_128s.generate_keypair(seed)

keytype = SpxKey.DEFAULT_KEY_TYPE
scheme = SpxKey.DEFAULT_SCHEME
keyval = {"public": public.hex()}

keyid = cls._get_keyid(keytype, scheme, keyval)
public_key = SpxKey(keyid, keytype, scheme, keyval)

return cls(private, public_key)

@classmethod
def from_priv_key_uri(
cls,
Expand Down
5 changes: 4 additions & 1 deletion tests/test_signer.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
SpxSigner,
SSlibKey,
SSlibSigner,
generate_spx_key_pair,
)


Expand Down Expand Up @@ -647,7 +648,9 @@ def test_sphincs(self):
"""sphincs signer smoketest."""

# Test create/sign/verify
signer = SpxSigner.new_()
public_bytes, private_bytes = generate_spx_key_pair()
public_key = SpxKey.from_bytes(public_bytes)
signer = SpxSigner(private_bytes, public_key)
sig = signer.sign(b"data")
self.assertIsNone(signer.public_key.verify_signature(sig, b"data"))
with self.assertRaises(UnverifiedSignatureError):
Expand Down

0 comments on commit c0671c4

Please sign in to comment.