Switch crypto deps from fastecdsa and pycrypto to cryptography

This commit is contained in:
Viktor Dragomiretskyy 2020-07-10 01:06:39 +12:00
parent 20740ea377
commit 189889bd77
4 changed files with 71 additions and 67 deletions

View file

@ -1,3 +1,2 @@
fastecdsa==1.7.4 cryptography>=2.1.4
git+https://github.com/fabiant7t/pycrypto#egg=pycrypto pyusb>=1.0.2
pyusb==1.0.2

View file

@ -1,18 +1,19 @@
from .tls import tls, hs_key, crt_hardcoded import os
from hashlib import sha256
import hmac
from struct import pack, unpack from struct import pack, unpack
from binascii import hexlify, unhexlify from binascii import hexlify, unhexlify
from hashlib import sha256
import hmac
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
from .tls import tls, hs_key, crt_hardcoded
from .usb import usb from .usb import usb
from .flash import write_flash, erase_flash, flush_changes, PartitionInfo, get_flash_info from .flash import write_flash, erase_flash, flush_changes, PartitionInfo, get_flash_info
from .sensor import reboot from .sensor import reboot
from Crypto.Random import get_random_bytes
from Crypto.Cipher import AES
from fastecdsa.encoding.der import DEREncoder
from fastecdsa.curve import P256
from fastecdsa.ecdsa import sign
from fastecdsa.keys import gen_private_key, get_public_key
from .util import assert_status, unhex from .util import assert_status, unhex
from .blobs import reset_blob from .blobs import reset_blob
@ -33,6 +34,8 @@ deb2604834e2bb62e890b0ce405b3b8ef2fec2aab3e22bff23f89a58ff0dc015fece5d3ed3f5496a
dbd0df42d534904de00b6389f68867646e9d7c3d0b1dffd74070b2d0f2049b9f1dc7b0c9651c59be3ea891674725e1f2f7a484a941615b80211105978369cf71 dbd0df42d534904de00b6389f68867646e9d7c3d0b1dffd74070b2d0f2049b9f1dc7b0c9651c59be3ea891674725e1f2f7a484a941615b80211105978369cf71
''') ''')
crypto_backend=default_backend()
def get_partition_signature(): def get_partition_signature():
if usb.usb_dev().idVendor == 0x138a: if usb.usb_dev().idVendor == 0x138a:
if usb.usb_dev().idProduct == 0x0090: if usb.usb_dev().idProduct == 0x0090:
@ -53,9 +56,11 @@ def encrypt_key(client_private, client_public):
l = 16 - (len(m) % 16) l = 16 - (len(m) % 16)
m = m + bytes([l])*l m = m + bytes([l])*l
iv = get_random_bytes(AES.block_size) iv = os.urandom(0x10)
aes = AES.new(tls.psk_encryption_key, AES.MODE_CBC, iv) cipher = Cipher(algorithms.AES(tls.psk_encryption_key), modes.CBC(iv), backend=crypto_backend)
c = iv + aes.encrypt(m) encryptor = cipher.encryptor()
c = iv + encryptor.update(m) + encryptor.finalize()
sig = hmac.new(tls.psk_validation_key, c, sha256).digest() sig = hmac.new(tls.psk_validation_key, c, sha256).digest()
return b'\x02' + c + sig return b'\x02' + c + sig
@ -65,8 +70,8 @@ def make_cert(client_public):
(b'\0'*0x24) + (b'\0'*0x24) +
unhexlify('%064x' % client_public.y)[::-1] + unhexlify('%064x' % client_public.y)[::-1] +
(b'\0'*0x4c)) (b'\0'*0x4c))
s=sign(msg, hs_key()) pk = ec.derive_private_key(hs_key(), ec.SECP256R1(), backend=crypto_backend)
s=DEREncoder().encode_signature(s[0], s[1]) s=pk.sign(msg, ec.ECDSA(hashes.SHA256()))
s=pack('<L', len(s)) + s s=pack('<L', len(s)) + s
msg = msg + s msg = msg + s
msg += b'\0'*(444 - len(msg)) # FIXME not sure this math is right msg += b'\0'*(444 - len(msg)) # FIXME not sure this math is right
@ -105,8 +110,10 @@ def partition_flash(layout, client_public):
def init_flash(): def init_flash():
assert_status(usb.cmd(reset_blob)) assert_status(usb.cmd(reset_blob))
client_private = gen_private_key(P256) skey = ec.generate_private_key(ec.SECP256R1(), crypto_backend)
client_public = get_public_key(client_private, P256) snums = skey.private_numbers()
client_private = snums.private_value
client_public = snums.public_numbers
partition_flash(flash_layout_hardcoded, client_public) partition_flash(flash_layout_hardcoded, client_public)

View file

@ -1,19 +1,20 @@
import re import re
import hmac import hmac
import sys import sys
from hashlib import sha256, md5, sha1 import os
from binascii import *
from .usb import unhex, usb
from struct import pack, unpack
from Crypto.Cipher import AES
from Crypto.Random import get_random_bytes
from fastecdsa.curve import P256
from fastecdsa.point import Point
from fastecdsa.keys import gen_private_key, get_public_key
from fastecdsa.ecdsa import sign, verify
from fastecdsa.encoding.der import DEREncoder
from .util import assert_status
import pickle import pickle
from struct import pack, unpack
from binascii import *
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.primitives.asymmetric.utils import Prehashed
from cryptography.hazmat.primitives import hashes
from hashlib import sha256
from .usb import unhex, usb
from .util import assert_status
password_hardcoded=unhexlify('717cd72d0962bc4a2846138dbb2c24192512a76407065f383846139d4bec2033') password_hardcoded=unhexlify('717cd72d0962bc4a2846138dbb2c24192512a76407065f383846139d4bec2033')
@ -32,6 +33,8 @@ ae6bcffffffffffffffff00000000ffffffff0000000000000000000000000000000000000000000
fff000000000000000000000000000000000000000000000000000000000000000000000000 fff000000000000000000000000000000000000000000000000000000000000000000000000
''') ''')
crypto_backend=default_backend()
def prf(secret, seed, length): def prf(secret, seed, length):
n = (length + 0x20 - 1) // 0x20 n = (length + 0x20 - 1) // 0x20
@ -141,17 +144,11 @@ class Tls():
self.handshake_hash.update(b) self.handshake_hash.update(b)
def make_keys(self): def make_keys(self):
#self.session_private=0x2E38AFE3D563398E5962D2CDEA7FE16D3CFEA36656A9DEC412C648EE3A232D21 skey = ec.generate_private_key(ec.SECP256R1(), crypto_backend)
self.session_private = gen_private_key(P256) self.session_public = skey.private_numbers().public_numbers
self.session_public = get_public_key(self.session_private, P256) pre_master_secret = skey.exchange(ec.ECDH(), self.ecdh_q)
pre_master_secret = self.session_private*self.ecdh_q
pre_master_secret = pre_master_secret.x
pre_master_secret = to_bytes(pre_master_secret)[::-1]
seed = self.client_random + self.server_random seed = self.client_random + self.server_random
self.master_secret = prf(pre_master_secret, b'master secret'+seed, 0x30) self.master_secret = prf(pre_master_secret, b'master secret'+seed, 0x30)
key_block = prf(self.master_secret, b'key expansion'+seed, 0x120) key_block = prf(self.master_secret, b'key expansion'+seed, 0x120)
self.sign_key = key_block[0x00:0x20] self.sign_key = key_block[0x00:0x20]
self.validation_key = key_block[0x20:0x20+0x20] self.validation_key = key_block[0x20:0x20+0x20]
@ -181,17 +178,19 @@ class Tls():
def decrypt(self, c): def decrypt(self, c):
iv, c = c[:0x10], c[0x10:] iv, c = c[:0x10], c[0x10:]
aes=AES.new(self.decryption_key, AES.MODE_CBC, iv) cipher = Cipher(algorithms.AES(self.decryption_key), modes.CBC(iv), backend=crypto_backend)
m=aes.decrypt(c) decryptor = cipher.decryptor()
m = decryptor.update(c) + decryptor.finalize()
m=unpad(m) m=unpad(m)
return m return m
def encrypt(self, b): def encrypt(self, b):
#iv = unhexlify('454849acdd075174d6b9e713a957c2e7') #iv = unhexlify('454849acdd075174d6b9e713a957c2e7')
iv = get_random_bytes(0x10) iv = os.urandom(0x10)
aes=AES.new(self.encryption_key, AES.MODE_CBC, iv) cipher = Cipher(algorithms.AES(self.encryption_key), modes.CBC(iv), backend=crypto_backend)
encryptor = cipher.encryptor()
b=pad(b) b=pad(b)
c=aes.encrypt(b) c=encryptor.update(b) + encryptor.finalize()
return iv + c return iv + c
def validate(self, t, b): def validate(self, t, b):
@ -240,8 +239,7 @@ class Tls():
def make_cert_verify(self): def make_cert_verify(self):
buf=self.handshake_hash.copy().digest() buf=self.handshake_hash.copy().digest()
s=sign(hexlify(buf).decode(), self.priv_key, prehashed=True) b=self.priv_key.sign(buf, ec.ECDSA(Prehashed(hashes.SHA256())))
b=DEREncoder().encode_signature(s[0], s[1])
return self.with_neg_hdr(0x0f, b) return self.with_neg_hdr(0x0f, b)
def handle_server_hello(self, p): def handle_server_hello(self, p):
@ -369,7 +367,7 @@ class Tls():
def make_client_hello(self): def make_client_hello(self):
h = unhexlify('0303') # TLS 1.2 h = unhexlify('0303') # TLS 1.2
#self.client_random = unhexlify('bc349559ac16c8f8362191395b4d04a435d870315f519eed8777488bc2b9600c') #self.client_random = unhexlify('bc349559ac16c8f8362191395b4d04a435d870315f519eed8777488bc2b9600c')
self.client_random = get_random_bytes(0x20) self.client_random = os.urandom(0x20)
h += self.client_random # client's random h += self.client_random # client's random
h += with_1byte_size(unhexlify('00000000000000')) # session ID h += with_1byte_size(unhexlify('00000000000000')) # session ID
@ -459,10 +457,10 @@ class Tls():
x, y = [int(hexlify(i[::-1]), 0x10) for i in [x, y]] x, y = [int(hexlify(i[::-1]), 0x10) for i in [x, y]]
if not P256.is_point_on_curve( (x, y) ): # Raises ValueError unless on the curve
raise Exception('Point is not on the curve') pubkey = ec.EllipticCurvePublicNumbers(x, y, ec.SECP256R1()).public_key(crypto_backend)
self.ecdh_q = Point(x, y, P256) self.ecdh_q = pubkey
self.trace('ECDH params:') self.trace('ECDH params:')
self.trace('x=0x%x' % x) self.trace('x=0x%x' % x)
self.trace('y=0x%x' % y) self.trace('y=0x%x' % y)
@ -476,15 +474,13 @@ class Tls():
# The following pub key is hardcoded for each fw revision in the synaWudfBioUsb.dll. # The following pub key is hardcoded for each fw revision in the synaWudfBioUsb.dll.
# Corresponding private key should only be known to a genuine Synaptic device. # Corresponding private key should only be known to a genuine Synaptic device.
fwpub=Point( fwpub = ec.EllipticCurvePublicNumbers(
0xf727653b4e16ce0665a6894d7f3a30d7d0a0be310d1292a743671fdf69f6a8d3, 0xf727653b4e16ce0665a6894d7f3a30d7d0a0be310d1292a743671fdf69f6a8d3,
0xa85538f8b6bec50d6eef8bd5f4d07a886243c58b2393948df761a84721a6ca94, P256) 0xa85538f8b6bec50d6eef8bd5f4d07a886243c58b2393948df761a84721a6ca94, ec.SECP256R1()).public_key(crypto_backend)
signature=DEREncoder().decode_signature(signature) # throws InvalidSignature
fwpub.verify(signature, key, ec.ECDSA(hashes.SHA256()))
if not verify(signature, key, fwpub):
raise Exception('Untrusted device')
def handle_priv(self, body): def handle_priv(self, body):
self.priv_blob = body self.priv_blob = body
@ -497,9 +493,10 @@ class Tls():
if hs != sig: if hs != sig:
raise Exception('Signature verification failed. This device was probably paired with another computer.') raise Exception('Signature verification failed. This device was probably paired with another computer.')
iv, c = c[:AES.block_size], c[AES.block_size:] iv, c = c[:0x10], c[0x10:]
aes=AES.new(self.psk_encryption_key, AES.MODE_CBC, iv) cipher = Cipher(algorithms.AES(self.psk_encryption_key), modes.CBC(iv), backend=crypto_backend)
m=aes.decrypt(c) decryptor = cipher.decryptor()
m = decryptor.update(c) + decryptor.finalize()
m=m[:-m[-1]] # unpad (standard this time) m=m[:-m[-1]] # unpad (standard this time)
x, m = m[:0x20], m[0x20:] x, m = m[:0x20], m[0x20:]
@ -508,17 +505,16 @@ class Tls():
x, y, d = [int(hexlify(i[::-1]), 0x10) for i in [x, y, d]] x, y, d = [int(hexlify(i[::-1]), 0x10) for i in [x, y, d]]
if not P256.is_point_on_curve( (x, y) ):
raise Exception('Point is not on the curve')
# TODO check if the priv key belogs to this public key
self.trace('Private key:') self.trace('Private key:')
self.trace('x=0x%x' % x) self.trace('x=0x%x' % x)
self.trace('y=0x%x' % y) self.trace('y=0x%x' % y)
self.trace('d=0x%x' % d) self.trace('d=0x%x' % d)
self.pub_key = Point(x, y, P256) # Someone has reported that x and y are 0 after pairing with the latest windows driver,
self.priv_key = d # for compatibility we could derive x and y with a following call:
#ec.derive_private_key(d, ec.SECP256R1(), backend=crypto_backend)
pub_key = ec.EllipticCurvePublicNumbers(x, y, ec.SECP256R1())
self.priv_key = ec.EllipticCurvePrivateNumbers(d, pub_key).private_key(crypto_backend)
tls = Tls(usb) tls = Tls(usb)

View file

@ -44,6 +44,8 @@ class Usb():
try: try:
self.dev.reset() self.dev.reset()
self.dev = None self.dev = None
except:
pass
finally: finally:
self.thread.join() self.thread.join()