mirror of https://github.com/nucypher/pyUmbral.git
Move SecretKey signing capabilities to a Signer class
parent
768ac3ae9e
commit
6545eacca2
|
@ -19,6 +19,9 @@ Keys
|
|||
:members:
|
||||
:show-inheritance:
|
||||
|
||||
.. autoclass:: Signer
|
||||
:members:
|
||||
|
||||
Intermediate objects
|
||||
--------------------
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import pytest
|
||||
|
||||
from umbral.keys import PublicKey, SecretKey
|
||||
from umbral.signing import Signature
|
||||
from umbral.signing import Signature, Signer
|
||||
from umbral.hashing import Hash
|
||||
|
||||
|
||||
|
@ -9,13 +9,14 @@ from umbral.hashing import Hash
|
|||
def test_sign_and_verify(execution_number):
|
||||
sk = SecretKey.random()
|
||||
pk = PublicKey.from_secret_key(sk)
|
||||
signer = Signer(sk)
|
||||
|
||||
message = b"peace at dawn"
|
||||
message = b"peace at dawn" + str(execution_number).encode()
|
||||
dst = b"dst"
|
||||
|
||||
digest = Hash(dst)
|
||||
digest.update(message)
|
||||
signature = sk.sign_digest(digest)
|
||||
signature = signer.sign_digest(digest)
|
||||
|
||||
digest = Hash(dst)
|
||||
digest.update(message)
|
||||
|
@ -26,13 +27,14 @@ def test_sign_and_verify(execution_number):
|
|||
def test_sign_serialize_and_verify(execution_number):
|
||||
sk = SecretKey.random()
|
||||
pk = PublicKey.from_secret_key(sk)
|
||||
signer = Signer(sk)
|
||||
|
||||
message = b"peace at dawn"
|
||||
message = b"peace at dawn" + str(execution_number).encode()
|
||||
dst = b"dst"
|
||||
|
||||
digest = Hash(dst)
|
||||
digest.update(message)
|
||||
signature = sk.sign_digest(digest)
|
||||
signature = signer.sign_digest(digest)
|
||||
|
||||
signature_bytes = bytes(signature)
|
||||
signature_restored = Signature.from_bytes(signature_bytes)
|
||||
|
@ -45,13 +47,14 @@ def test_sign_serialize_and_verify(execution_number):
|
|||
def test_verification_fail():
|
||||
sk = SecretKey.random()
|
||||
pk = PublicKey.from_secret_key(sk)
|
||||
signer = Signer(sk)
|
||||
|
||||
message = b"peace at dawn"
|
||||
dst = b"dst"
|
||||
|
||||
digest = Hash(dst)
|
||||
digest.update(message)
|
||||
signature = sk.sign_digest(digest)
|
||||
signature = signer.sign_digest(digest)
|
||||
|
||||
# wrong DST
|
||||
digest = Hash(b"other dst")
|
||||
|
@ -77,13 +80,41 @@ def test_signature_repr():
|
|||
|
||||
sk = SecretKey.random()
|
||||
pk = PublicKey.from_secret_key(sk)
|
||||
signer = Signer(sk)
|
||||
|
||||
message = b"peace at dawn"
|
||||
dst = b"dst"
|
||||
|
||||
digest = Hash(dst)
|
||||
digest.update(message)
|
||||
signature = sk.sign_digest(digest)
|
||||
signature = signer.sign_digest(digest)
|
||||
|
||||
s = repr(signature)
|
||||
assert 'Signature' in s
|
||||
|
||||
|
||||
def test_signer_str():
|
||||
signer = Signer(SecretKey.random())
|
||||
s = str(signer)
|
||||
assert s == "Signer:..."
|
||||
|
||||
|
||||
def test_signer_hash():
|
||||
signer = Signer(SecretKey.random())
|
||||
# Insecure Python hash, shouldn't be available.
|
||||
with pytest.raises(RuntimeError):
|
||||
hash(signer)
|
||||
|
||||
|
||||
def test_signer_bytes():
|
||||
signer = Signer(SecretKey.random())
|
||||
# Shouldn't be able to serialize.
|
||||
with pytest.raises(RuntimeError):
|
||||
bytes(signer)
|
||||
|
||||
|
||||
def test_signer_pubkey():
|
||||
sk = SecretKey.random()
|
||||
pk = PublicKey.from_secret_key(sk)
|
||||
signer = Signer(sk)
|
||||
assert signer.verifying_key() == pk
|
||||
|
|
|
@ -8,7 +8,7 @@ from .errors import GenericError
|
|||
from .key_frag import KeyFrag, generate_kfrags
|
||||
from .keys import SecretKey, PublicKey, SecretKeyFactory
|
||||
from .pre import encrypt, decrypt_original, decrypt_reencrypted, reencrypt
|
||||
from .signing import Signature
|
||||
from .signing import Signature, Signer
|
||||
|
||||
__all__ = [
|
||||
"__title__",
|
||||
|
@ -23,6 +23,7 @@ __all__ = [
|
|||
"PublicKey",
|
||||
"SecretKeyFactory",
|
||||
"Signature",
|
||||
"Signer",
|
||||
"Capsule",
|
||||
"KeyFrag",
|
||||
"CapsuleFrag",
|
||||
|
|
|
@ -8,7 +8,7 @@ from .curve_scalar import CurveScalar
|
|||
from .curve_point import CurvePoint
|
||||
from .keys import PublicKey, SecretKey
|
||||
from .serializable import Serializable, serialize_bool
|
||||
from .signing import Signature
|
||||
from .signing import Signature, Signer
|
||||
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
from .key_frag import KeyFragID
|
||||
|
@ -108,8 +108,8 @@ class SignatureDigest:
|
|||
def update(self, value):
|
||||
self._digest.update(value)
|
||||
|
||||
def sign(self, sk: SecretKey) -> Signature:
|
||||
return sk.sign_digest(self._digest)
|
||||
def sign(self, signer: Signer) -> Signature:
|
||||
return signer.sign_digest(self._digest)
|
||||
|
||||
def verify(self, pk: PublicKey, sig: Signature):
|
||||
return sig.verify_digest(pk, self._digest)
|
||||
|
|
|
@ -7,7 +7,7 @@ from .hashing import hash_to_shared_secret, hash_to_cfrag_signature, hash_to_pol
|
|||
from .keys import PublicKey, SecretKey
|
||||
from .params import PARAMETERS
|
||||
from .serializable import Serializable, serialize_bool, take_bool
|
||||
from .signing import Signature
|
||||
from .signing import Signature, Signer
|
||||
|
||||
|
||||
class KeyFragID(Serializable):
|
||||
|
@ -58,7 +58,7 @@ class KeyFragProof(Serializable):
|
|||
kfrag_precursor,
|
||||
delegating_pk,
|
||||
receiving_pk,
|
||||
).sign(signing_sk)
|
||||
).sign(Signer(signing_sk))
|
||||
|
||||
maybe_delegating_pk = delegating_pk if sign_delegating_key else None
|
||||
maybe_receiving_pk = receiving_pk if sign_receiving_key else None
|
||||
|
@ -67,7 +67,7 @@ class KeyFragProof(Serializable):
|
|||
kfrag_precursor,
|
||||
maybe_delegating_pk,
|
||||
maybe_receiving_pk
|
||||
).sign(signing_sk)
|
||||
).sign(Signer(signing_sk))
|
||||
|
||||
return cls(commitment,
|
||||
signature_for_proxy,
|
||||
|
|
|
@ -1,19 +1,11 @@
|
|||
import os
|
||||
from typing import TYPE_CHECKING, Tuple
|
||||
from typing import Tuple
|
||||
|
||||
from cryptography.hazmat.primitives.asymmetric import utils
|
||||
from cryptography.hazmat.primitives.asymmetric.ec import ECDSA
|
||||
|
||||
from . import openssl
|
||||
from .curve import CURVE
|
||||
from .curve_scalar import CurveScalar
|
||||
from .curve_point import CurvePoint
|
||||
from .dem import kdf
|
||||
from .serializable import Serializable
|
||||
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
from .hashing import Hash
|
||||
|
||||
|
||||
class SecretKey(Serializable):
|
||||
"""
|
||||
|
@ -57,27 +49,6 @@ class SecretKey(Serializable):
|
|||
def __bytes__(self) -> bytes:
|
||||
return bytes(self._scalar_key)
|
||||
|
||||
def sign_digest(self, digest: 'Hash') -> 'Signature':
|
||||
|
||||
signature_algorithm = ECDSA(utils.Prehashed(digest._backend_hash_algorithm))
|
||||
message = digest.finalize()
|
||||
|
||||
backend_sk = openssl.bn_to_privkey(CURVE, self._scalar_key._backend_bignum)
|
||||
signature_der_bytes = backend_sk.sign(message, signature_algorithm)
|
||||
r_int, s_int = utils.decode_dss_signature(signature_der_bytes)
|
||||
|
||||
# Normalize s
|
||||
# s is public, so no constant-timeness required here
|
||||
if s_int > (CURVE.order >> 1):
|
||||
s_int = CURVE.order - s_int
|
||||
|
||||
# Already normalized, don't waste time
|
||||
r = CurveScalar.from_int(r_int, check_normalization=False)
|
||||
s = CurveScalar.from_int(s_int, check_normalization=False)
|
||||
|
||||
from .signing import Signature
|
||||
return Signature(r, s)
|
||||
|
||||
|
||||
class PublicKey(Serializable):
|
||||
"""
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
from typing import TYPE_CHECKING
|
||||
|
||||
from cryptography.exceptions import InvalidSignature
|
||||
from cryptography.hazmat.primitives.asymmetric import utils
|
||||
from cryptography.hazmat.primitives.asymmetric.ec import ECDSA
|
||||
|
@ -5,8 +7,57 @@ from cryptography.hazmat.primitives.asymmetric.ec import ECDSA
|
|||
from . import openssl
|
||||
from .curve import CURVE
|
||||
from .curve_scalar import CurveScalar
|
||||
from .keys import SecretKey, PublicKey
|
||||
from .serializable import Serializable
|
||||
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
from .hashing import Hash
|
||||
|
||||
|
||||
class Signer:
|
||||
"""
|
||||
An object possessing the capability to create signatures.
|
||||
For safety reasons serialization is prohibited.
|
||||
"""
|
||||
|
||||
def __init__(self, secret_key: SecretKey):
|
||||
self.__secret_key = secret_key
|
||||
|
||||
def sign_digest(self, digest: 'Hash') -> 'Signature':
|
||||
|
||||
signature_algorithm = ECDSA(utils.Prehashed(digest._backend_hash_algorithm))
|
||||
message = digest.finalize()
|
||||
|
||||
backend_sk = openssl.bn_to_privkey(CURVE, self.__secret_key.secret_scalar()._backend_bignum)
|
||||
signature_der_bytes = backend_sk.sign(message, signature_algorithm)
|
||||
r_int, s_int = utils.decode_dss_signature(signature_der_bytes)
|
||||
|
||||
# Normalize s
|
||||
# s is public, so no constant-timeness required here
|
||||
if s_int > (CURVE.order >> 1):
|
||||
s_int = CURVE.order - s_int
|
||||
|
||||
# Already normalized, don't waste time
|
||||
r = CurveScalar.from_int(r_int, check_normalization=False)
|
||||
s = CurveScalar.from_int(s_int, check_normalization=False)
|
||||
|
||||
return Signature(r, s)
|
||||
|
||||
def verifying_key(self) -> PublicKey:
|
||||
"""
|
||||
Returns the public verification key corresponding to the secret key used for signing.
|
||||
"""
|
||||
return PublicKey.from_secret_key(self.__secret_key)
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.__class__.__name__}:..."
|
||||
|
||||
def __hash__(self):
|
||||
raise RuntimeError(f"{self.__class__.__name__} objects do not support hashing")
|
||||
|
||||
def __bytes__(self):
|
||||
raise RuntimeError(f"{self.__class__.__name__} objects do not support serialization")
|
||||
|
||||
|
||||
class Signature(Serializable):
|
||||
"""
|
||||
|
|
Loading…
Reference in New Issue