-
Notifications
You must be signed in to change notification settings - Fork 50
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
signer: move spx keygen, sign and verify API
This was previously part of the `securesystemslib.keys` interface. To prepare for deprecation of this interface, and use of the new signer API as replacement, spx-related functionality and test are moved. This is a backwards incompatible change and a slight feature degradation: - spx is now no longer available via the keys module, and thus no longer available SSlibSigner/SSlibKey (i.e. the legacy API bridge). Given that spx was added recently, support via the legacy API does not seem necessary. The advantage is more modular code and less case-handling, also wrt different optional dependencies. - Currently, SpxSigner does not implement from_priv_key_uri, which was previously available via SSlibSigner, and supported loading signers from file or envvar. Support may be added back later. - The default keyid is computed differently than before (see 6c29cae) See usage example in docstring. Signed-off-by: Lukas Puehringer <[email protected]>
- Loading branch information
Showing
9 changed files
with
182 additions
and
191 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
"""Signer implementation for project SPHINCS+ post-quantum signature support. | ||
""" | ||
import logging | ||
import os | ||
from typing import Any, Dict, Optional | ||
|
||
from securesystemslib.exceptions import ( | ||
UnsupportedLibraryError, | ||
UnverifiedSignatureError, | ||
VerificationError, | ||
) | ||
from securesystemslib.signer._key import Key | ||
from securesystemslib.signer._signature import Signature | ||
from securesystemslib.signer._signer import SecretsHandler, Signer | ||
from securesystemslib.signer._sigstore_signer import SigstoreSigner | ||
|
||
SPX_IMPORT_ERROR = None | ||
try: | ||
from pyspx import shake_128s | ||
except ImportError: | ||
SPX_IMPORT_ERROR = "spinhcs+ key support requires the pyspx library" | ||
|
||
_SHAKE_SEED_LEN = 48 | ||
|
||
logger = logging.getLogger(__name__) | ||
|
||
|
||
class SpxKey(Key): | ||
"""SPHINCS+ verifier.""" | ||
|
||
DEFAULT_KEY_TYPE = "sphincs" | ||
DEFAULT_SCHEME = "sphincs-shake-128s" | ||
|
||
@classmethod | ||
def from_dict(cls, keyid: str, key_dict: Dict[str, Any]) -> "SpxKey": | ||
keytype = key_dict.pop("keytype") | ||
scheme = key_dict.pop("scheme") | ||
keyval = key_dict.pop("keyval") | ||
|
||
return cls(keyid, keytype, scheme, keyval, key_dict) | ||
|
||
def to_dict(self) -> Dict[str, Any]: | ||
return { | ||
"keytype": self.keytype, | ||
"scheme": self.scheme, | ||
"keyval": self.keyval, | ||
**self.unrecognized_fields, | ||
} | ||
|
||
def verify_signature(self, signature: Signature, data: bytes) -> None: | ||
valid = None | ||
try: | ||
if SPX_IMPORT_ERROR: | ||
raise UnsupportedLibraryError(SPX_IMPORT_ERROR) | ||
|
||
key = bytes.fromhex(self.keyval["public"]) | ||
sig = bytes.fromhex(signature.signature) | ||
|
||
valid = shake_128s.verify(data, sig, key) | ||
|
||
except Exception as e: | ||
logger.info("Key %s failed to verify sig: %s", self.keyid, str(e)) | ||
raise VerificationError( | ||
f"Unknown failure to verify signature by {self.keyid}" | ||
) from e | ||
|
||
if not valid: | ||
raise UnverifiedSignatureError( | ||
f"Failed to verify signature by {self.keyid}" | ||
) | ||
|
||
|
||
class SpxSigner(Signer): | ||
"""SPHINCS+ signer. | ||
Usage:: | ||
signer = SpxSigner.new_() | ||
signature = signer.sign(b"payload") | ||
# Use public_key.to_dict() / Key.from_dict() to transport public key data | ||
public_key = signer.public_key | ||
public_key.verify_signature(signature, b"payload") | ||
""" | ||
|
||
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()} | ||
# TODO: Create generic helper | ||
keyid = SigstoreSigner._get_keyid(keytype, scheme, keyval) | ||
public_key = SpxKey(keyid, keytype, scheme, keyval) | ||
|
||
return cls(private, public_key) | ||
|
||
@classmethod | ||
def from_priv_key_uri( | ||
cls, | ||
priv_key_uri: str, | ||
public_key: Key, | ||
secrets_handler: Optional[SecretsHandler] = None, | ||
) -> "SpxSigner": | ||
raise NotImplementedError | ||
|
||
def sign(self, payload: bytes) -> Signature: | ||
"""Signs payload with SPHINCS+ private key on the instance. | ||
Arguments: | ||
payload: bytes to be signed. | ||
Raises: | ||
UnsupportedLibraryError: PySPX is not available. | ||
Returns: | ||
Signature. | ||
""" | ||
if SPX_IMPORT_ERROR: | ||
raise UnsupportedLibraryError(SPX_IMPORT_ERROR) | ||
|
||
raw = shake_128s.sign(payload, self.private_key) | ||
return Signature(self.public_key.keyid, raw.hex()) |
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.