diff --git a/gpg/decode.py b/gpg/decode.py index 494af6c..ffb4c2f 100644 --- a/gpg/decode.py +++ b/gpg/decode.py @@ -6,6 +6,9 @@ import logging import struct import ecdsa +import ed25519 + +from trezor_agent.util import num2bytes log = logging.getLogger(__name__) @@ -86,6 +89,10 @@ def split_bits(value, *bits): assert value == 0 return reversed(result) +SUPPORTED_CURVES = { + b'\x2A\x86\x48\xCE\x3D\x03\x01\x07': 'nist256p1', + b'\x2B\x06\x01\x04\x01\xDA\x47\x0F\x01': 'ed25519', +} class Parser(object): def __init__(self, stream, to_hash=None): @@ -168,13 +175,38 @@ class Parser(object): # https://tools.ietf.org/html/rfc6637#section-11 oid_size = stream.readfmt('B') oid = stream.read(oid_size) - assert oid == b'\x2A\x86\x48\xCE\x3D\x03\x01\x07' # NIST P-256 + assert oid in SUPPORTED_CURVES + curve_name = SUPPORTED_CURVES[oid] mpi = parse_mpi(stream) - log.debug('mpi: %x', mpi) - prefix, x, y = split_bits(mpi, 4, 256, 256) - assert prefix == 4 - p['point'] = (x, y) + log.debug('mpi: %x (%d bits)', mpi, mpi.bit_length()) + if curve_name == 'nist256p1': + prefix, x, y = split_bits(mpi, 4, 256, 256) + assert prefix == 4 + point = ecdsa.ellipticcurve.Point(curve=ecdsa.NIST256p.curve, + x=x, y=y) + vk = ecdsa.VerifyingKey.from_public_point( + point=point, curve=ecdsa.curves.NIST256p, + hashfunc=hashlib.sha256) + + def _nist256p1_verify(signature, digest): + vk.verify_digest(signature=signature, + digest=digest, + sigdecode=lambda rs, order: rs) + p['verifier'] = _nist256p1_verify + elif curve_name == 'ed25519': + prefix, value = split_bits(mpi, 8, 256) + assert prefix == 0x40 + vk = ed25519.VerifyingKey(num2bytes(value, size=32)) + + def _ed25519_verify(signature, digest): + sig = b''.join(num2bytes(val, size=32) + for val in signature) + vk.verify(sig, digest) + p['verifier'] = _ed25519_verify + else: + raise ValueError('unsupported curve {}'.format(curve_name)) + assert not stream.read() # https://tools.ietf.org/html/rfc4880#section-12.2 @@ -234,16 +266,9 @@ def load_public_key(stream): def verify_digest(pubkey, digest, signature, label): - coords = pubkey['point'] - point = ecdsa.ellipticcurve.Point(curve=ecdsa.NIST256p.curve, - x=coords[0], y=coords[1]) - v = ecdsa.VerifyingKey.from_public_point(point=point, - curve=ecdsa.curves.NIST256p, - hashfunc=hashlib.sha256) + verifier = pubkey['verifier'] try: - v.verify_digest(signature=signature, - digest=digest, - sigdecode=lambda rs, order: rs) + verifier(signature, digest) log.debug('%s is OK', label) except ecdsa.keys.BadSignatureError: log.error('Bad %s!', label) diff --git a/trezor_agent/util.py b/trezor_agent/util.py index 8e0e29b..8119a82 100644 --- a/trezor_agent/util.py +++ b/trezor_agent/util.py @@ -60,7 +60,7 @@ def num2bytes(value, size): res.append(value & 0xFF) value = value >> 8 assert value == 0 - return bytearray(list(reversed(res))) + return bytes(bytearray(list(reversed(res)))) def pack(fmt, *args):