gpg: support ed25519 public keys and signatures

nistp521
Roman Zeyde 8 years ago
parent 74f7ebf228
commit 276dec5728

@ -94,6 +94,7 @@ SUPPORTED_CURVES = {
b'\x2B\x06\x01\x04\x01\xDA\x47\x0F\x01': 'ed25519',
}
class Parser(object):
def __init__(self, stream, to_hash=None):
self.stream = stream
@ -191,11 +192,11 @@ class Parser(object):
def _nist256p1_verify(signature, digest):
vk.verify_digest(signature=signature,
digest=digest,
sigdecode=lambda rs, order: rs)
digest=digest,
sigdecode=lambda rs, order: rs)
p['verifier'] = _nist256p1_verify
elif curve_name == 'ed25519':
prefix, value = split_bits(mpi, 8, 256)
prefix, value = split_bits(mpi, 8, 256)
assert prefix == 0x40
vk = ed25519.VerifyingKey(num2bytes(value, size=32))

@ -71,43 +71,65 @@ def hexlify(blob):
return binascii.hexlify(blob).decode('ascii').upper()
class Signer(object):
def _dump_nist256(vk):
return mpi((4 << 512) |
(vk.pubkey.point.x() << 256) |
(vk.pubkey.point.y()))
def _dump_ed25519(vk):
return mpi((0x40 << 256) |
trezor_agent.util.bytes2num(vk.to_bytes()))
ecdsa_curve_name = trezor_agent.formats.CURVE_NIST256
SUPPORTED_CURVES = {
trezor_agent.formats.CURVE_NIST256: {
# https://tools.ietf.org/html/rfc6637#section-11
'oid': b'\x2A\x86\x48\xCE\x3D\x03\x01\x07',
'algo_id': 19,
'dump': _dump_nist256
},
trezor_agent.formats.CURVE_ED25519: {
'oid': b'\x2B\x06\x01\x04\x01\xDA\x47\x0F\x01',
'algo_id': 22,
'dump': _dump_ed25519
}
}
class Signer(object):
def __init__(self, user_id, created):
def __init__(self, user_id, created, curve_name):
self.user_id = user_id
assert curve_name in trezor_agent.formats.SUPPORTED_CURVES
self.curve_name = curve_name
self.client_wrapper = trezor_agent.factory.load()
# This requires the following patch to trezor-mcu in order to work correctly:
# https://github.com/trezor/trezor-mcu/pull/79
self.identity = self.client_wrapper.identity_type()
self.identity.proto = 'gpg'
self.identity.host = user_id
addr = trezor_agent.client.get_address(self.identity)
public_node = self.client_wrapper.connection.get_public_node(
n=addr, ecdsa_curve_name=self.ecdsa_curve_name)
n=addr, ecdsa_curve_name=self.curve_name)
verifying_key = trezor_agent.formats.decompress_pubkey(
self.verifying_key = trezor_agent.formats.decompress_pubkey(
pubkey=public_node.node.public_key,
curve_name=self.ecdsa_curve_name)
curve_name=self.curve_name)
self.created = int(created)
self.pubkey_point = verifying_key.pubkey.point
log.info('key %s created at %s',
self.hex_short_key_id(), time_format(self.created))
def _pubkey_data(self):
curve_info = SUPPORTED_CURVES[self.curve_name]
header = struct.pack('>BLB',
4, # version
self.created, # creation
19) # ECDSA
# https://tools.ietf.org/html/rfc6637#section-11 (NIST P-256 OID)
oid = prefix_len('>B', b'\x2A\x86\x48\xCE\x3D\x03\x01\x07')
return header + oid + mpi((4 << 512) |
(self.pubkey_point.x() << 256) |
(self.pubkey_point.y()))
curve_info['algo_id'])
oid = prefix_len('>B', curve_info['oid'])
blob = curve_info['dump'](self.verifying_key)
return header + oid + blob
def _pubkey_data_to_hash(self):
return b'\x99' + prefix_len('>H', self._pubkey_data())
@ -160,10 +182,11 @@ class Signer(object):
def _make_signature(self, visual, data_to_sign,
hashed_subpackets, sig_type=0):
curve_info = SUPPORTED_CURVES[self.curve_name]
header = struct.pack('>BBBB',
4, # version
sig_type, # rfc4880 (section-5.2.1)
19, # pubkey_alg (ECDSA)
curve_info['algo_id'],
8) # hash_alg (SHA256)
hashed = subpackets(*hashed_subpackets)
unhashed = subpackets(
@ -179,19 +202,16 @@ class Signer(object):
identity=self.identity,
challenge_hidden=digest,
challenge_visual=visual,
ecdsa_curve_name=self.ecdsa_curve_name)
ecdsa_curve_name=self.curve_name)
assert result.signature[:1] == b'\x00'
sig = result.signature[1:]
sig = [trezor_agent.util.bytes2num(sig[:32]),
trezor_agent.util.bytes2num(sig[32:])]
decode.verify_digest(pubkey={'point': (self.pubkey_point.x(),
self.pubkey_point.y())},
digest=digest,
signature=sig, label='GPG signature')
hash_prefix = digest[:2] # used for decoder's sanity check
signature = mpi(sig[0]) + mpi(sig[1]) # actual ECDSA signature
return header + hashed + unhashed + hash_prefix + signature
# TODO: add verification
def split_lines(body, size):
@ -223,22 +243,27 @@ def main():
p.add_argument('-t', '--time', type=int, default=int(time.time()))
p.add_argument('-a', '--armor', action='store_true', default=False)
p.add_argument('-v', '--verbose', action='store_true', default=False)
p.add_argument('-e', '--ecdsa-curve', default='nist256p1')
args = p.parse_args()
logging.basicConfig(level=logging.DEBUG if args.verbose else logging.INFO,
format='%(asctime)s %(levelname)-10s %(message)s')
user_id = args.user_id.encode('ascii')
if not args.filename:
s = Signer(user_id=user_id, created=args.time)
s = Signer(user_id=user_id, created=args.time,
curve_name=args.ecdsa_curve)
pubkey = s.export()
ext = '.pub'
if args.armor:
pubkey = armor(pubkey, 'PUBLIC KEY BLOCK')
ext = '.asc'
open(s.hex_short_key_id() + ext, 'wb').write(pubkey)
filename = s.hex_short_key_id() + ext
open(filename, 'wb').write(pubkey)
log.info('import to local keyring using "gpg2 --import %s"', filename)
else:
pubkey = load_from_gpg(args.user_id)
s = Signer(user_id=user_id, created=pubkey['created'])
s = Signer(user_id=user_id, created=pubkey['created'],
curve_name=args.ecdsa_curve) # TODO: deduce from existing pubkey
assert s.key_id() == pubkey['key_id']
data = open(args.filename, 'rb').read()

Loading…
Cancel
Save