# Copyright 2016 Cedric Mesnil <cedric.mesnil@ubinity.com>, Ubinity SAS
#
# 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.
#python 2 compatibility
from builtins import int,pow
from ecpy.curves import Curve,Point
from ecpy.keys import ECPublicKey, ECPrivateKey
from ecpy.formatters import decode_sig, encode_sig
from ecpy import ecrand
import hashlib
[docs]class ECDSA:
"""ECDSA signer.
Args:
fmt (str) : in/out signature format. See :mod:`ecpy.formatters`
"""
def __init__(self, fmt="DER"):
self.fmt=fmt
self.maxtries=10
pass
[docs] def sign(self, msg, pv_key, canonical=False):
""" Signs a message hash.
Args:
msg (bytes) : the message hash to sign
pv_key (ecpy.keys.ECPrivateKey): key to use for signing
"""
order = pv_key.curve.order
for i in range(1,self.maxtries):
k = ecrand.rnd(order)
sig = self._do_sign(msg, pv_key,k, canonical)
if sig:
return sig
return None
[docs] def sign_rfc6979(self, msg, pv_key, hasher, canonical=False):
""" Signs a message hash according to RFC6979
Args:
msg (bytes) : the message hash to sign
pv_key (ecpy.keys.ECPrivateKey): key to use for signing
hasher (hashlib) : hasher conform to hashlib interface
"""
field = pv_key.curve.field
V = None
for i in range(1,self.maxtries):
k,V = ecrand.rnd_rfc6979(msg, pv_key.d, field, hasher,V)
sig = self._do_sign(msg, pv_key, k, canonical)
if sig:
return sig
return None
[docs] def sign_k(self, msg, pv_key, k,canonical=False):
""" Signs a message hash with provided random
Args:
msg (bytes) : the hash of message to sign
pv_key (ecpy.keys.ECPrivateKey): key to use for signing
k (ecpy.keys.ECPrivateKey) : random to use for signing
"""
return self._do_sign(msg, pv_key, k, canonical)
def _do_sign(self, msg, pv_key, k, canonical=False):
if (pv_key.curve == None):
raise ECPyException('private key haz no curve')
curve = pv_key.curve
n = curve.order
G = curve.generator
k = k%n
msg = int.from_bytes(msg, 'big')
Q = G*k
kinv = pow(k,n-2,n)
r = Q.x % n
if r == 0:
return None
s = (kinv*(msg+pv_key.d*r)) %n
if s == 0:
return None
if canonical and (s > (n//2)):
s = n-s
sig = encode_sig(r,s,self.fmt)
# r = r.to_bytes((r.bit_length()+7)//8, 'big')
# s = s.to_bytes((s.bit_length()+7)//8, 'big')
# if (r[0] & 0x80) == 0x80 :
# r = b'\0'+r
# if (s[0] & 0x80) == 0x80 :
# s = b'\0'+s
# sig = (b'\x30'+int((len(r)+len(s)+4)).to_bytes(1,'big') +
# b'\x02'+int(len(r)).to_bytes(1,'big') + r +
# b'\x02'+int(len(s)).to_bytes(1,'big') + s )
return sig
[docs] def verify(self,msg,sig,pu_key):
""" Verifies a message signature.
Args:
msg (bytes) : the message hash to verify the signature
sig (bytes) : signature to verify
pu_key (ecpy.keys.ECPublicKey): key to use for verifying
"""
curve = pu_key.curve
n = curve.order
G = curve.generator
r,s = decode_sig(sig, self.fmt)
if (r == None or
r > n or
s > n ) :
return False
h = int.from_bytes(msg,'big')
c = pow(s, n-2, n)
u1 = (h*c)%n
u2 = (r*c)%n
u1G = u1*G
u2Q = u2*pu_key.W
GQ = u1G+u2Q
x = GQ.x % n
return x == r
if __name__ == "__main__":
try:
### ECDSA
cv = Curve.get_curve('secp256k1')
pu_key = ECPublicKey(Point(0x65d5b8bf9ab1801c9f168d4815994ad35f1dcb6ae6c7a1a303966b677b813b00,
0xe6b865e529b8ecbf71cf966e900477d49ced5846d7662dd2dd11ccd55c0aff7f,
cv))
pv_key = ECPrivateKey(0xfb26a4e75eec75544c0f44e937dcf5ee6355c7176600b9688c667e5c283b43c5,
cv)
#sha256("abc")
# c: 0xC03DDDA6174963AD10224BADDBCF7ED9EA5E3DAE91941CB428D2EC060B4F290A
# u1: 0x113BE17918E856E4D6EC2EE04F5E9B3CB599B82AC879C8E32A0140C290D32659
# u2: 0x2976F786AE6333E125C0DFFD6C16D37E8CED5ABEDB491BCCA21C75B307D0B318
# u1G: 0x51e4e6ed6f4b1db33b0d21b8bd30fb732f1d999c4e27bb1800eba20813ad3e86
# 0x93101a9fa0d5c7c680400b03d3becb9130dd8f9f4d9b034360a74829dc1201ab
# u2Q: 0xeaca8440897333e259d0f99165611b085d6e10a9bfd371c451bc0aea1aeb99c3
# 0x57c5c95ea9f491c0fd9029a4089a2e6df47313f915f3e39e9f12e03ab16521c2
# + : 0x0623b4159c7112125be51716d1e706d68e52f5b321da68d8b86b3c7c7019a9da
# : 0x1029094ccc466a534df3dbb7f588b283c9bef213633750aeff021c4c131b7ce5
# SIG: 3045
# 0220
# 0623b4159c7112125be51716d1e706d68e52f5b321da68d8b86b3c7c7019a9da
# 0221
# 008dffe3c592a0c7e5168dcb3d4121a60ee727082be4fbf79eae564929156305fc
msg = int(0xba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad)
sig = int(0x304502200623b4159c7112125be51716d1e706d68e52f5b321da68d8b86b3c7c7019a9da0221008dffe3c592a0c7e5168dcb3d4121a60ee727082be4fbf79eae564929156305fc)
msg = msg.to_bytes(32,'big')
sig = sig.to_bytes(0x47,'big')
signer = ECDSA()
assert(signer.verify(msg,sig,pu_key))
# k: 0xe5a8d1d529971c10ca2af378444fb544a211707892c8898f91dcb171584e3db9
# kG: 0x4f123ed9de853836447782f0a436508d34e6609083cf97c9b9cd69673d8f04a5
# 0x50b57473f987f2d7c4715827dbd7b23c3088645d5f898aa66e4ef2778591d643
# kinv: 0x0F2DD0361F61F683957CF708FB54DBC0B6B97F9EDF28604983E6F492117C154C
# kinv.d 0xFF68C89B97C63273EB5F787FBC0C33DA02BA4C883AB09E3D381197E9E3964E8C
# kinv.d.x 0x5A20058782FB81C8F98C30D9D441B7196C22939B144918CF519FB155180664B1
# kinv.h 0x112FA7E277E3AE8AE4911A05B56880F05B601CCC633A3D0084C4D5734F0C5BD1
# SIG 3044
# 0220,
# 4f123ed9de853836447782f0a436508d34e6609083cf97c9b9cd69673d8f04a5
# 0220,
# 6b4fad69fadf3053de1d4adf89aa3809c782b067778355cfd66486c86712c082
expected_sig = int(0x304402204f123ed9de853836447782f0a436508d34e6609083cf97c9b9cd69673d8f04a502206b4fad69fadf3053de1d4adf89aa3809c782b067778355cfd66486c86712c082)
expected_sig = expected_sig.to_bytes(0x46,'big')
k = int(0xe5a8d1d529971c10ca2af378444fb544a211707892c8898f91dcb171584e3db9)
sig = signer.sign_k(msg,pv_key,k)
assert(sig == expected_sig)
#Sign with k rand
sig = signer.sign(msg,pv_key)
assert(signer.verify(msg,sig,pu_key))
#sign with krfc
sig = signer.sign_rfc6979(msg,pv_key,hashlib.sha256)
assert(sig == expected_sig)
##OK!
print("All internal assert OK!")
finally:
pass