Optional libsecp256k1 dependency, default to pure Python cryptographic API to help Windows users

This commit is contained in:
BTChip 2016-09-02 09:53:24 +02:00
parent 4eefb8a7f7
commit b56ff7cbe1
9 changed files with 151 additions and 11 deletions

View File

@ -2,7 +2,7 @@
This package contains Python tools to communicate with Ledger Blue and Nano S and manage applications life cycle
The life cycle management requires [libsecp256k1](https://github.com/ludbb/secp256k1-py) Python bindings compiled with ECDH support. It is recommended to install this package in a [Virtual Environment](http://docs.python-guide.org/en/latest/dev/virtualenvs/) in your native environment (not a Docker image) through
It is recommended to install this package in a [Virtual Environment](http://docs.python-guide.org/en/latest/dev/virtualenvs/) in your native environment (not a Docker image) to avoid hidapi issues.
```
virtualenv ledger
@ -10,3 +10,9 @@ source ledger/bin/activate
pip install ledgerblue
```
This package can optionally work with [libsecp256k1](https://github.com/ludbb/secp256k1-py) Python bindings compiled with ECDH support. If you wish to enable libsecp256k1 bindings, make sure to install libsecp256k1 as follows
```
SECP_BUNDLED_EXPERIMENTAL=1 pip --no-cache-dir install secp256k1
```

View File

@ -17,7 +17,7 @@
********************************************************************************
"""
from secp256k1 import PrivateKey
from .ecWrapper import PrivateKey
from .comm import getDongle
from .deployed import getDeployedSecretV1, getDeployedSecretV2
from .hexLoader import HexLoader

View File

@ -17,7 +17,7 @@
********************************************************************************
"""
from secp256k1 import PrivateKey, PublicKey
from .ecWrapper import PrivateKey, PublicKey
import os
import sys
import struct

136
ledgerblue/ecWrapper.py Normal file
View File

@ -0,0 +1,136 @@
"""
*******************************************************************************
* Ledger Blue
* (c) 2016 Ledger
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
********************************************************************************
"""
import hashlib
try:
import secp256k1
USE_SECP = secp256k1.HAS_ECDH
except ImportError:
USE_SECP = False
if not USE_SECP:
import ecpy
from builtins import int
from ecpy.curves import Curve, Point
from ecpy.keys import ECPublicKey, ECPrivateKey
from ecpy.ecdsa import ECDSA
CURVE_SECP256K1 = Curve.get_curve('secp256k1')
SIGNER = ECDSA()
class PublicKey(object):
def __init__(self, pubkey=None, raw=False, flags=None, ctx=None):
if USE_SECP:
if flags == None:
flags = secp256k1.FLAG_VERIFY
self.obj = secp256k1.PublicKey(pubkey, raw, flags, ctx)
else:
if not raw:
raise Exception("Non raw init unsupported")
pubkey = pubkey[1:]
x = int.from_bytes(pubkey[0:32], 'big')
y = int.from_bytes(pubkey[32:], 'big')
self.obj = ECPublicKey(Point(x, y, CURVE_SECP256K1))
def ecdsa_deserialize(self, ser_sig):
if USE_SECP:
return self.obj.ecdsa_deserialize(ser_sig)
else:
return ser_sig
def serialize(self, compressed=True):
if USE_SECP:
return self.obj.serialize(compressed)
else:
if not compressed:
out = "\x04"
out += str(bytearray(self.obj.W.x.to_bytes(32, 'big')))
out += str(bytearray(self.obj.W.y.to_bytes(32, 'big')))
else:
out = "\x03" if ((self.obj.W.y & 1) <> 0) else "\x02"
out += str(bytearray(self.obj.W.x.to_bytes(32, 'big')))
return out
def ecdh(self, scalar):
if USE_SECP:
return self.obj.ecdh(scalar)
else:
scalar = int.from_bytes(scalar)
point = self.obj.W * scalar
# libsecp256k1 style secret
out = "\x03" if ((point.y & 1) <> 0) else "\x02"
out += str(bytearray(point.x.to_bytes(32, 'big')))
hash = hashlib.sha256()
hash.update(out)
return hash.digest()
def ecdsa_verify(self, msg, raw_sig, raw=False, digest=hashlib.sha256):
if USE_SECP:
return self.obj.ecdsa_verify(msg, raw_sig, raw, digest)
else:
if not raw:
h = digest()
h.update(msg)
msg = h.digest()
raw_sig = bytearray(raw_sig)
return SIGNER.verify(msg, raw_sig, self.obj)
class PrivateKey(object):
def __init__(self, privkey=None, raw=True, flags=None, ctx=None):
if USE_SECP:
if flags == None:
flags = secp256k1.ALL_FLAGS
self.obj = secp256k1.PrivateKey(privkey, raw, flags, ctx)
self.pubkey = self.obj.pubkey
else:
if not raw:
raise Exception("Non raw init unsupported")
if privkey == None:
privkey = ecpy.ecrand.rnd(CURVE_SECP256K1.order)
else:
privkey = int.from_bytes(privkey)
self.obj = ECPrivateKey(privkey, CURVE_SECP256K1)
pubkey = self.obj.get_public_key().W
out = "\x04"
out += str(bytearray(pubkey.x.to_bytes(32, 'big')))
out += str(bytearray(pubkey.y.to_bytes(32, 'big')))
self.pubkey = PublicKey(out, raw=True)
def serialize(self):
if USE_SECP:
return self.obj.serialize()
else:
return str(bytearray(self.obj.d.to_bytes(32, 'big'))).encode('hex')
def ecdsa_serialize(self, raw_sig):
if USE_SECP:
return self.obj.ecdsa_serialize(raw_sig)
else:
return raw_sig
def ecdsa_sign(self, msg, raw=False, digest=hashlib.sha256):
if USE_SECP:
return self.obj.ecdsa_sign(msg, raw, digest)
else:
if not raw:
h = digest()
h.update(msg)
msg = h.digest()
signature = SIGNER.sign(msg, self.obj)
return bytearray(signature)

View File

@ -17,7 +17,7 @@
********************************************************************************
"""
from secp256k1 import PrivateKey
from .ecWrapper import PrivateKey
from .comm import getDongle
from .hexParser import IntelHexParser
from .hexLoader import HexLoader

View File

@ -19,7 +19,7 @@
from .comm import getDongle
from .deployed import getDeployedSecretV2
from secp256k1 import PrivateKey
from .ecWrapper import PrivateKey
from Crypto.Cipher import AES
import argparse
import sys

View File

@ -19,7 +19,7 @@
from .hexParser import IntelHexParser
from .hexParser import IntelHexPrinter
from secp256k1 import PrivateKey
from .ecWrapper import PrivateKey
import hashlib
import binascii
import argparse

View File

@ -19,7 +19,7 @@
from .hexParser import IntelHexParser
from .hexParser import IntelHexPrinter
from secp256k1 import PublicKey
from .ecWrapper import PublicKey
import hashlib
import binascii
import argparse

View File

@ -5,19 +5,17 @@ from setuptools import setup, find_packages
from os.path import dirname, join
import os
os.environ['SECP_BUNDLED_EXPERIMENTAL'] = "1"
here = dirname(__file__)
setup(
name='ledgerblue',
version='0.1.6',
version='0.1.7',
author='Ledger',
author_email='hello@ledger.fr',
description='Python library to communicate with Ledger Blue/Nano S',
long_description=open(join(here, 'README.md')).read(),
url='https://github.com/LedgerHQ/blue-loader-python',
packages=find_packages(),
install_requires=['hidapi>=0.7.99', 'secp256k1>=0.12.1', 'pycrypto>=2.6.1'],
install_requires=['hidapi>=0.7.99', 'pycrypto>=2.6.1', 'future', 'ecpy>=0.8.1'],
extras_require = {
'smartcard': [ 'python-pyscard>=1.6.12-4build1' ]
},