Merge pull request #16 from jhoenicke/master
More robust gpg key parsing
This commit is contained in:
@@ -2,6 +2,7 @@
|
|||||||
import binascii
|
import binascii
|
||||||
import contextlib
|
import contextlib
|
||||||
import logging
|
import logging
|
||||||
|
import os
|
||||||
|
|
||||||
from . import decode, encode, keyring
|
from . import decode, encode, keyring
|
||||||
from .. import util
|
from .. import util
|
||||||
@@ -39,8 +40,9 @@ def sig_encode(r, s):
|
|||||||
def pksign(keygrip, digest, algo):
|
def pksign(keygrip, digest, algo):
|
||||||
"""Sign a message digest using a private EC key."""
|
"""Sign a message digest using a private EC key."""
|
||||||
assert algo == '8'
|
assert algo == '8'
|
||||||
|
user_id = os.environ['TREZOR_GPG_USER_ID']
|
||||||
pubkey_dict = decode.load_public_key(
|
pubkey_dict = decode.load_public_key(
|
||||||
pubkey_bytes=keyring.export_public_key(user_id=None),
|
pubkey_bytes=keyring.export_public_key(user_id=user_id),
|
||||||
use_custom=True, ecdh=False)
|
use_custom=True, ecdh=False)
|
||||||
pubkey, conn = encode.load_from_public_key(pubkey_dict=pubkey_dict)
|
pubkey, conn = encode.load_from_public_key(pubkey_dict=pubkey_dict)
|
||||||
with contextlib.closing(conn):
|
with contextlib.closing(conn):
|
||||||
@@ -83,8 +85,9 @@ def pkdecrypt(keygrip, conn):
|
|||||||
assert keyring.recvline(conn) == b'END'
|
assert keyring.recvline(conn) == b'END'
|
||||||
remote_pubkey = parse_ecdh(line)
|
remote_pubkey = parse_ecdh(line)
|
||||||
|
|
||||||
|
user_id = os.environ['TREZOR_GPG_USER_ID']
|
||||||
local_pubkey = decode.load_public_key(
|
local_pubkey = decode.load_public_key(
|
||||||
pubkey_bytes=keyring.export_public_key(user_id=None),
|
pubkey_bytes=keyring.export_public_key(user_id=user_id),
|
||||||
use_custom=True, ecdh=True)
|
use_custom=True, ecdh=True)
|
||||||
pubkey, conn = encode.load_from_public_key(pubkey_dict=local_pubkey)
|
pubkey, conn = encode.load_from_public_key(pubkey_dict=local_pubkey)
|
||||||
with contextlib.closing(conn):
|
with contextlib.closing(conn):
|
||||||
|
|||||||
@@ -241,31 +241,62 @@ def parse_packets(stream):
|
|||||||
|
|
||||||
log.debug('prefix byte: %s', bin(value))
|
log.debug('prefix byte: %s', bin(value))
|
||||||
assert util.bit(value, 7) == 1
|
assert util.bit(value, 7) == 1
|
||||||
assert util.bit(value, 6) == 0 # new format not supported yet
|
|
||||||
|
|
||||||
tag = util.low_bits(value, 6)
|
tag = util.low_bits(value, 6)
|
||||||
|
if util.bit(value, 6) == 0:
|
||||||
length_type = util.low_bits(tag, 2)
|
length_type = util.low_bits(tag, 2)
|
||||||
tag = tag >> 2
|
tag = tag >> 2
|
||||||
fmt = {0: '>B', 1: '>H', 2: '>L'}[length_type]
|
fmt = {0: '>B', 1: '>H', 2: '>L'}[length_type]
|
||||||
packet_size = stream.readfmt(fmt)
|
packet_size = stream.readfmt(fmt)
|
||||||
|
else:
|
||||||
|
first = stream.readfmt('B')
|
||||||
|
if first < 192:
|
||||||
|
packet_size = first
|
||||||
|
elif first < 224:
|
||||||
|
packet_size = ((first - 192) << 8) + stream.readfmt('B') + 192
|
||||||
|
elif first == 255:
|
||||||
|
packet_size = stream.readfmt('>L')
|
||||||
|
else:
|
||||||
|
log.error('Partial Body Lengths unsupported')
|
||||||
|
|
||||||
log.debug('packet length: %d', packet_size)
|
log.debug('packet length: %d', packet_size)
|
||||||
packet_data = stream.read(packet_size)
|
packet_data = stream.read(packet_size)
|
||||||
packet_type = PACKET_TYPES.get(tag)
|
packet_type = PACKET_TYPES.get(tag)
|
||||||
assert packet_type is not None, tag
|
|
||||||
|
|
||||||
|
if packet_type is not None:
|
||||||
p = packet_type(util.Reader(io.BytesIO(packet_data)))
|
p = packet_type(util.Reader(io.BytesIO(packet_data)))
|
||||||
p['tag'] = tag
|
p['tag'] = tag
|
||||||
|
else:
|
||||||
|
p = {'type': 'unknown', 'tag': tag, 'raw': packet_data}
|
||||||
|
|
||||||
log.debug('packet "%s": %s', p['type'], p)
|
log.debug('packet "%s": %s', p['type'], p)
|
||||||
yield p
|
yield p
|
||||||
|
|
||||||
|
|
||||||
def digest_packets(packets):
|
def digest_packets(packets, hashalg):
|
||||||
"""Compute digest on specified packets, according to '_to_hash' field."""
|
"""Compute digest on specified packets, according to '_to_hash' field."""
|
||||||
data_to_hash = io.BytesIO()
|
data_to_hash = io.BytesIO()
|
||||||
for p in packets:
|
for p in packets:
|
||||||
data_to_hash.write(p['_to_hash'])
|
data_to_hash.write(p['_to_hash'])
|
||||||
return hashlib.sha256(data_to_hash.getvalue()).digest()
|
return hashalg(data_to_hash.getvalue()).digest()
|
||||||
|
|
||||||
|
|
||||||
|
def _hash_ripemd160(msg):
|
||||||
|
"""Wrapper for ripemd160 hash."""
|
||||||
|
h = hashlib.new("ripemd160")
|
||||||
|
h.update(msg)
|
||||||
|
return h
|
||||||
|
|
||||||
|
|
||||||
|
HASH_ALGORITHMS = {
|
||||||
|
1: hashlib.md5,
|
||||||
|
2: hashlib.sha1,
|
||||||
|
3: _hash_ripemd160,
|
||||||
|
8: hashlib.sha256,
|
||||||
|
9: hashlib.sha384,
|
||||||
|
10: hashlib.sha512,
|
||||||
|
11: hashlib.sha224,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def load_public_key(pubkey_bytes, use_custom=False, ecdh=False):
|
def load_public_key(pubkey_bytes, use_custom=False, ecdh=False):
|
||||||
@@ -275,10 +306,13 @@ def load_public_key(pubkey_bytes, use_custom=False, ecdh=False):
|
|||||||
pubkey, userid, signature = packets[:3]
|
pubkey, userid, signature = packets[:3]
|
||||||
packets = packets[3:]
|
packets = packets[3:]
|
||||||
|
|
||||||
digest = digest_packets([pubkey, userid, signature])
|
hashalg = HASH_ALGORITHMS.get(signature['hash_alg'])
|
||||||
|
if hashalg is not None:
|
||||||
|
digest = digest_packets([pubkey, userid, signature], hashalg)
|
||||||
assert signature['hash_prefix'] == digest[:2]
|
assert signature['hash_prefix'] == digest[:2]
|
||||||
|
|
||||||
log.debug('loaded public key "%s"', userid['value'])
|
log.debug('loaded public key "%s"', userid['value'])
|
||||||
if pubkey.get('verifier'):
|
if hashalg is not None and pubkey.get('verifier'):
|
||||||
verify_digest(pubkey=pubkey, digest=digest,
|
verify_digest(pubkey=pubkey, digest=digest,
|
||||||
signature=signature['sig'], label='GPG public key')
|
signature=signature['sig'], label='GPG public key')
|
||||||
else:
|
else:
|
||||||
@@ -292,6 +326,8 @@ def load_public_key(pubkey_bytes, use_custom=False, ecdh=False):
|
|||||||
log.debug('found custom %s', packet['type'])
|
log.debug('found custom %s', packet['type'])
|
||||||
break
|
break
|
||||||
|
|
||||||
|
while packets[1]['type'] != 'signature':
|
||||||
|
packets = packets[1:]
|
||||||
packet, signature = packets[:2]
|
packet, signature = packets[:2]
|
||||||
packets = packets[2:]
|
packets = packets[2:]
|
||||||
|
|
||||||
@@ -303,7 +339,9 @@ def load_public_key(pubkey_bytes, use_custom=False, ecdh=False):
|
|||||||
def load_signature(stream, original_data):
|
def load_signature(stream, original_data):
|
||||||
"""Load signature from stream, and compute GPG digest for verification."""
|
"""Load signature from stream, and compute GPG digest for verification."""
|
||||||
signature, = list(parse_packets(util.Reader(stream)))
|
signature, = list(parse_packets(util.Reader(stream)))
|
||||||
digest = digest_packets([{'_to_hash': original_data}, signature])
|
hashalg = HASH_ALGORITHMS.get(signature['hash_alg'])
|
||||||
|
assert hashalg is not None
|
||||||
|
digest = digest_packets([{'_to_hash': original_data}, signature], hashalg)
|
||||||
assert signature['hash_prefix'] == digest[:2]
|
assert signature['hash_prefix'] == digest[:2]
|
||||||
return signature, digest
|
return signature, digest
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import hashlib
|
||||||
import io
|
import io
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
@@ -64,7 +65,7 @@ zpR9luXTKwMEl+mlZmwEFKZXBmir
|
|||||||
'_to_hash': b'\x04\x13\x13\x08\x00\x12\x05\x02WHH\xd6\x02\x1b\x03\x02\x15\x08\x02\x16\x00\x02\x17\x80\x04\xff\x00\x00\x00\x18' # nopep8
|
'_to_hash': b'\x04\x13\x13\x08\x00\x12\x05\x02WHH\xd6\x02\x1b\x03\x02\x15\x08\x02\x16\x00\x02\x17\x80\x04\xff\x00\x00\x00\x18' # nopep8
|
||||||
})
|
})
|
||||||
|
|
||||||
digest = decode.digest_packets(packets=[pubkey, user_id, signature])
|
digest = decode.digest_packets(packets=[pubkey, user_id, signature], hashalg=hashlib.sha256)
|
||||||
decode.verify_digest(pubkey=pubkey, digest=digest,
|
decode.verify_digest(pubkey=pubkey, digest=digest,
|
||||||
signature=signature['sig'],
|
signature=signature['sig'],
|
||||||
label='GPG primary public key')
|
label='GPG primary public key')
|
||||||
|
|||||||
Reference in New Issue
Block a user