onionrblocks/onionrblocks/generators/signedby.py

56 lines
2.1 KiB
Python

from hashlib import sha3_256
from typing import Union
from nacl.signing import SigningKey, VerifyKey
from nacl.exceptions import BadSignatureError
from kasten import generator, Kasten
from kasten.exceptions import InvalidID
from kasten.types import KastenPacked, KastenChecksum
from onionrblocks.customtypes import RawEd25519PrivateKey, RawEd25519PublicKey
class Signed(generator.KastenBaseGenerator):
@classmethod
def generate(
cls,
packed_bytes: KastenPacked,
signingKey: Union[SigningKey, RawEd25519PrivateKey]
) -> Kasten:
"""Sign a digest of packed bytes, return a Kasten instance of it."""
# Use libsodium/pynacl (ed25519)
hashed = sha3_256(packed_bytes).digest()
try:
signed = signingKey.sign(hashed)
except AttributeError:
signingKey = SigningKey(signingKey)
signed = signingKey.sign(hashed)
# The KastenChecksum will be 64 bytes message then 32 bytes of the hash
# This can be fed right back into VerifyKey without splitting up
return Kasten(signed, packed_bytes, None,
auto_check_generator=False)
@staticmethod
def validate_id(
hash: KastenChecksum,
packed_bytes: KastenPacked,
verify_key: Union[VerifyKey, RawEd25519PublicKey]) -> None:
# Hash is 64 bytes message then 32 bytes of the hash of the packed_bytes
if len(hash) != 86:
raise InvalidID("Block not have proper signature length")
actual_hash = sha3_256(packed_bytes).digest()
if not isinstance(verify_key, VerifyKey):
verify_key = VerifyKey(verify_key)
# Ensure that the digest is correct
# Done in addition of the signature bc the sha3 can still ID blocks
# and to prevent swapping sigs
if actual_hash != hash[64:]:
raise InvalidID("Invalid sha3_256 digest")
# Ensure that the signature is correct
try:
verify_key.verify(hash)
except BadSignatureError as e:
raise InvalidID(repr(e))