blue-loader-python/ledgerblue/ecWrapper.py

137 lines
3.8 KiB
Python

"""
*******************************************************************************
* 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 = b"\x04"
out += self.obj.W.x.to_bytes(32, 'big')
out += self.obj.W.y.to_bytes(32, 'big')
else:
out = b"\x03" if ((self.obj.W.y & 1) != 0) else "\x02"
out += 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, 'big')
point = self.obj.W * scalar
# libsecp256k1 style secret
out = b"\x03" if ((point.y & 1) != 0) else b"\x02"
out += 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,'big')
self.obj = ECPrivateKey(privkey, CURVE_SECP256K1)
pubkey = self.obj.get_public_key().W
out = b"\x04"
out += pubkey.x.to_bytes(32, 'big')
out += pubkey.y.to_bytes(32, 'big')
self.pubkey = PublicKey(out, raw=True)
def serialize(self):
if USE_SECP:
return self.obj.serialize()
else:
return "%.64x"%self.obj.d
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)