def ecdsa_verify_raw(m: bytes, dsasig: Signature, pubkey: PubKey) -> bool: h = int_from_hash(m, ec.n) r, s = dsasig s1 = mod_inv(s, ec.n) R = ec.pointAdd(ec.pointMultiply(r * s1 % ec.n, pubkey), ec.pointMultiply(h * s1 % ec.n, ec.G)) return R[0] % ec.n == r
def ecdsa_pubkey_recovery_raw(m: bytes, dsasig: Signature, odd1even0: int) -> PubKey: h = int_from_hash(m, ec.n) r, s = dsasig r1 = mod_inv(r, ec.n) R = (r, ec.yOdd(r, odd1even0)) return ec.pointAdd(ec.pointMultiply( s * r1 % ec.n, R), ec.pointMultiply(-h * r1 % ec.n, ec.G))
def ecssa_pubkey_recovery_raw(e: bytes, ssasig: Signature) -> PubKey: r, s = ssasig R = (r, ec.yQuadraticResidue(r, True)) e = int_from_hash(e, ec.n) assert e != 0 and e < ec.n, "invalid challenge e" e1 = mod_inv(e, ec.n) return ec.pointAdd(ec.pointMultiply((e1 * s) % ec.n, ec.G), ec.pointMultiply(ec.n - e1, R))
def ecssa_pubkey_recovery_raw(m: bytes, ssasig: Signature, hasher=sha256) -> PubKey: R_x, s = ssasig R = (R_x, ec.y(R_x, 0)) R_x = R_x.to_bytes(32, 'big') e = hasher(R_x + m).digest() e = int_from_hash(e, ec.order) assert e != 0 and e < ec.order, "invalid e value" e1 = mod_inv(e, ec.order) return ec.pointAdd(ec.pointMultiply(e1, R), ec.pointMultiply(-e1 * s % ec.order, ec.G))
def ecssa_verify_raw(m: bytes, ssasig: Signature, pub: PubKey, hasher=sha256) -> bool: R_x, s = ssasig[0].to_bytes(32, 'big'), ssasig[1] e = hasher(R_x + m).digest() e = int_from_hash(e, ec.order) if e == 0 or e >= ec.order: return False R = ec.pointAdd(ec.pointMultiply(e, pub), ec.pointMultiply(s, ec.G)) if R[1] % 2 == 1: # R.y odd return False return R[0] == ssasig[0]
def ecssa_verify_raw(m: bytes, ssasig: Signature, pub: PubKey, hasher=sha256) -> bool: r, s = ssasig e = hasher( r.to_bytes(32, byteorder="big") + bytes_from_Point(ec, pub, True) + m).digest() e = int_from_hash(e, ec.n) if e == 0 or e >= ec.n: return False # R = sG - eP R = ec.pointAdd(ec.pointMultiply(s, ec.G), ec.pointMultiply(ec.n - e, pub)) if ec.jacobi(R[1]) != 1: return False return R[0] == ssasig[0]
def test_tuple_from_point(self): prv = 0xc28fca386c7a227600b2fe50b7cae11ec86d3bf1fbe471be89827e19d72aa1d Pub = secp256k1.pointMultiply(prv, secp256k1.G) Pub_bytes = b'\x02' + Pub[0].to_bytes(32, "big") p2 = tuple_from_Point(secp256k1, Pub_bytes) self.assertEqual(p2, Pub) Pub_hex_str = Pub_bytes.hex() p2 = tuple_from_Point(secp256k1, Pub_hex_str) self.assertEqual(p2, Pub) Pub_bytes = b'\x04' + Pub[0].to_bytes(32, "big") + Pub[1].to_bytes( 32, "big") p2 = tuple_from_Point(secp256k1, Pub_bytes) self.assertEqual(p2, Pub) Pub_hex_str = Pub_bytes.hex() p2 = tuple_from_Point(secp256k1, Pub_hex_str) self.assertEqual(p2, Pub) # infinity point cannot be represented as tuple self.assertRaises(ValueError, tuple_from_Point, secp256k1, None) # scalar in point multiplication can be int, str, or bytes-like t = tuple() self.assertRaises(TypeError, pointMultiply, secp256k1, t, secp256k1.G)
def ecdsa_sign_raw(m: bytes, prvkey: int, eph_prv: int) -> Signature: R = ec.pointMultiply(eph_prv, ec.G) r = R[0] % ec.n h = int_from_hash(m, ec.n) # assert h s = mod_inv(eph_prv, ec.n) * (h + prvkey * r) % ec.n assert r != 0 and s != 0, "failed to sign" return r, s
def ecssa_sign_raw(m: bytes, prvkey: int, eph_prv: int, hasher=sha256) -> Signature: R = ec.pointMultiply(eph_prv, ec.G) # break the simmetry: any criteria could be used, jacobi is standard if ec.jacobi(R[1]) != 1: # no need to actually change R[1], as it is not used anymore # let's fix eph_prv instead, as it is used later eph_prv = ec.n - eph_prv e = hasher(R[0].to_bytes(32, byteorder="big") + bytes_from_Point(ec, ec.pointMultiply(prvkey, ec.G), True) + m).digest() e = int_from_hash(e, ec.n) assert e != 0 and e < ec.n, "sign fail" s = (eph_prv + e * prvkey) % ec.n return R[0], s
def pubkey_bytes_from_prvkey(prvkey, compressed=True): PubKey = ec.pointMultiply(prvkey, ec.G) if compressed: prefix = b'\x02' if (PubKey[1] % 2 == 0) else b'\x03' return prefix + PubKey[0].to_bytes(32, byteorder='big') else: prefix = b'\x04' return prefix + PubKey[0].to_bytes( 32, byteorder='big') + PubKey[1].to_bytes(32, byteorder='big')
def tweak(k: int, c: bytes, hasher = sha256) -> Tuple[Point, Scalar]: """tweak kG returns: - point kG to tweak - tweaked private key k + h(kG||c), the corresponding pubkey is a commitment to kG, c """ R = ec.pointMultiply(k, ec.G) e = hasher(bytes_from_Point(ec, R, True) + c).digest() e = int.from_bytes(e, 'big') return R, (e + k) % ec.n
def verify_commit(receipt: Receipt, c: Message, hasher = sha256) -> bool: w, R = receipt ec.yOdd(w, False) # receipt[0] is valid iif its y does exist tuple_from_Point(ec, R) # verify R is a good point if type(c) == str: c = hasher(c.encode()).digest() e = hasher(bytes_from_Point(ec, R, True) + c).digest() e = int.from_bytes(e, 'big') W = ec.pointAdd(R, ec.pointMultiply(e, ec.G)) # w in [1..n-1] dsa # w in [1..p-1] ssa # different verify functions? return w % ec.n == W[0] % ec.n
def ecssa_sign_raw(m: bytes, prvkey: int, eph_prv: int, hasher=sha256) -> Signature: R = ec.pointMultiply(eph_prv, ec.G) if R[1] % 2 == 1: eph_prv = ec.order - eph_prv # <=> R_y = ec_prime - R_y r = R[0] % ec.order # % ec.order ? e = hasher(R[0].to_bytes(32, 'big') + m).digest() e = int_from_hash(e, ec.order) assert e != 0 and e < ec.order, "sign fail" s = (eph_prv - e * prvkey) % ec.order return r, s
def bip32_ckd(xparentkey: bytes, index: Union[bytes, int]) -> bytes: """Child Key Derivation (CDK) Key derivation is normal if the extended parent key is public or child_index is less than 0x80000000. Key derivation is hardened if the extended parent key is private and child_index is not less than 0x80000000. """ if isinstance(index, int): index = index.to_bytes(4, 'big') elif isinstance(index, bytes): assert len(index) == 4 else: raise TypeError("a 4 bytes int is required") xparent = b58decode_check(xparentkey, 78) version = xparent[:4] # serialization data xkey = version # version xkey += (xparent[4] + 1).to_bytes(1, 'big') # (increased) depth if (version in PUBLIC): assert xparent[45] in (2, 3), \ "version/key mismatch in extended parent key" Parent_bytes = xparent[45:] Parent = tuple_from_Point(ec, Parent_bytes) xkey += h160(Parent_bytes)[:4] # parent pubkey fingerprint assert index[0] < 0x80, \ "no private/hardened derivation from pubkey" xkey += index # child index parent_chain_code = xparent[13:45] ## normal derivation # actual extended key (key + chain code) derivation h = HMAC(parent_chain_code, Parent_bytes + index, sha512).digest() offset = int.from_bytes(h[:32], 'big') Offset = ec.pointMultiply(offset, ec.G) Child = ec.pointAdd(Parent, Offset) Child_bytes = bytes_from_Point(ec, Child, True) xkey += h[32:] # chain code xkey += Child_bytes # public key elif (version in PRIVATE): assert xparent[45] == 0, "version/key mismatch in extended parent key" parent = int.from_bytes(xparent[46:], 'big') Parent = ec.pointMultiply(parent, ec.G) Parent_bytes = bytes_from_Point(ec, Parent, True) xkey += h160(Parent_bytes)[:4] # parent pubkey fingerprint xkey += index # child index # actual extended key (key + chain code) derivation parent_chain_code = xparent[13:45] if (index[0] < 0x80): ## normal derivation h = HMAC(parent_chain_code, Parent_bytes + index, sha512).digest() else: ## hardened derivation h = HMAC(parent_chain_code, xparent[45:] + index, sha512).digest() offset = int.from_bytes(h[:32], 'big') child = (parent + offset) % ec.order child_bytes = b'\x00' + child.to_bytes(32, 'big') xkey += h[32:] # chain code xkey += child_bytes # private key else: raise ValueError("invalid extended key version") return b58encode_check(xkey)
#!/usr/bin/env python3 from hashlib import sha256 from btclib.ellipticcurves import secp256k1 as ec print("\n*** EC:") print(ec) q = 0x18E14A7B6A307F426A94F8114701E7C8E774E7F9A47E2C2035DB29A206321725 q = q % ec.n print("\n*** Keys:") print("prvkey: ", hex(q)) Q = ec.pointMultiply(q, ec.G) print("PubKey:", "02" if (Q[1] % 2 == 0) else "03", hex(Q[0])) print("\n*** Message to be signed") msg1 = "Paolo is afraid of ephemeral random numbers" print(msg1) print("*** Hash of the message") h_bytes = sha256(msg1.encode()).digest() # hash(msg) must be transformed into an integer modulo ec.n: h1 = int.from_bytes(h_bytes, 'big') % ec.n assert h1 != 0 print(" h1:", hex(h1)) print("\n*** Signature") # ephemeral key k must be kept secret and never reused !!!!! # good choice: k = sha256(msg|q) # different for each msg, private because of q
#!/usr/bin/env python3 from hashlib import sha256 from btclib.ellipticcurves import secp256k1 as ec from btclib.numbertheory import mod_inv print("\n*** EC:") print(ec) q = 0x18E14A7B6A307F426A94F8114701E7C8E774E7F9A47E2C2035DB29A206321725 q = q % ec.n print("\n*** Keys:") print("prvkey: ", hex(q)) Q = ec.pointMultiply(q, ec.G) print("PubKey:", "02" if (Q[1] % 2 == 0) else "03", hex(Q[0])) print("\n*** Message to be signed") msg1 = "Paolo is afraid of ephemeral random numbers" print(msg1) print("*** Hash of the message") h_bytes = sha256(msg1.encode()).digest() # hash(msg) must be transformed into an integer modulo ec.n: h1 = int.from_bytes(h_bytes, 'big') % ec.n assert h1 != 0 print(" h1:", hex(h1)) print("\n*** Signature") # ephemeral key k must be kept secret and never reused !!!!! # good choice: k = sha256(msg|q)
# ==master ext private key== # depth: 0x00 for master nodes, 0x01 for level-1 derived keys, ... depth = b'\x00' # This is ser32(i) for i in xi = xpar/i, with xi the key being serialized. (0x00000000 if master key) child_number = b'\x00\x00\x00\x00' # the fingerprint of the parent's public key (0x00000000 if master key) fingerprint = b'\x00\x00\x00\x00' idf = depth + fingerprint + child_number # master private key, master public key, chain code hashValue = HMAC(b"Bitcoin seed", seed.to_bytes(seed_bytes, byteorder='big'), sha512).digest() p_bytes = hashValue[:32] p = int(p_bytes.hex(), 16) % ec.order p_bytes = b'\x00' + p.to_bytes(32, byteorder='big') P = ec.pointMultiply(p, ec.G) P_bytes = bytes_from_Point(ec, P, True) chain_code = hashValue[32:] #extended keys ext_prv = b58encode_check(xprv + idf + chain_code + p_bytes) print("\nm") print(ext_prv) ext_pub = b58encode_check(xpub + idf + chain_code + P_bytes) print("M") print(ext_pub) assert ext_prv == b"xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi", "failure" assert ext_pub == b"xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8", "failure" # ==first (0) hardened child== depth = b'\x01'
#!/usr/bin/env python3 import hashlib from btclib.ellipticcurves import secp256k1 as ec from btclib.base58 import b58encode_check, b58encode, b58decode_check # https://en.bitcoin.it/wiki/Technical_background_of_version_1_Bitcoin_addresses prvkey = 0x18E14A7B6A307F426A94F8114701E7C8E774E7F9A47E2C2035DB29A206321725 prvkey = prvkey % ec.n print("\n*** [0] Private ECDSA Key:") print(hex(prvkey)) PubKey = ec.pointMultiply(prvkey, ec.G) PubKey_bytes = b'\x04' + PubKey[0].to_bytes( 32, byteorder='big') + PubKey[1].to_bytes(32, byteorder='big') print("\n*** [1] Public Key (uncompressed):") print(PubKey_bytes.hex()) print("\n*** [2] SHA-256 hashing of the public key:") h1 = hashlib.sha256(PubKey_bytes).digest() print(h1.hex()) print("\n*** [3] RIPEMD-160 hashing on the result of SHA-256:") h2 = hashlib.new('ripemd160', h1).digest() print(h2.hex()) version_byte = "\x00" #for mainnet print("\n*** [4] version byte added in front of RIPEMD-160 hash:") vh160 = b'\x00' + h2 print(vh160.hex())