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 test_second_generator(self): """ important remark on secp256-zkp prefix for compressed encoding of the second generator: https://github.com/garyyu/rust-secp256k1-zkp/wiki/Pedersen-Commitment """ H = secondGenerator(secp256k1) H = bytes_from_Point(secp256k1, H, True) self.assertEqual(H.hex(), '0250929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0') H = secondGenerator(secp256r1) H = secondGenerator(secp384r1)
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 test_ecssa_4(self): pub = tuple_from_Point(ec, "03DEFDEA4CDB677750A420FEE807EACF21EB9898AE79B9768766E4FAA04A2D4A34") msg = bytes.fromhex("4DF3C3F68FCC83B27E9D42C90431A72499F17875C81A599B566C9889B9696703") sig = (0x00000000000000000000003B78CE563F89A0ED9414F5AA28AD0D96D6795F9C63, 0x02A8DC32E64E86A333F20EF56EAC9BA30B7246D6D25E22ADB8C6BE1AEB08D49D) self.assertTrue(ecssa_verify(msg, sig, pub)) # malleability self.assertFalse(ecssa_verify(msg, (sig[0], ec.n - sig[1]), pub)) e = sha256(sig[0].to_bytes(32, byteorder="big") + bytes_from_Point(ec, pub, True) + msg).digest() self.assertEqual(ecssa_pubkey_recovery(e, sig), pub)
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 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 test_ecssa_3(self): prv = 0xC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B14E5C7 pub = ec.pointMultiply(prv, ec.G) msg = bytes.fromhex("5E2D58D8B3BCDF1ABADEC7829054F90DDA9805AAB56C77333024B9D0A508B75C") expected_sig = (0x00DA9B08172A9B6F0466A2DEFD817F2D7AB437E0D253CB5395A963866B3574BE, 0x00880371D01766935B92D2AB4CD5C8A2A5837EC57FED7660773A05F0DE142380) eph_prv = int.from_bytes(sha256(prv.to_bytes(32, byteorder="big") + msg).digest(), byteorder="big") sig = ecssa_sign(msg, prv, eph_prv) self.assertTrue(ecssa_verify(msg, sig, pub)) # malleability self.assertFalse(ecssa_verify(msg, (sig[0], ec.n - sig[1]), pub)) self.assertEqual(sig, expected_sig) e = sha256(sig[0].to_bytes(32, byteorder="big") + bytes_from_Point(ec, pub, True) + msg).digest() self.assertEqual(ecssa_pubkey_recovery(e, sig), pub)
def test_ecssa_2(self): prv = 0xB7E151628AED2A6ABF7158809CF4F3C762E7160F38B4DA56A784D9045190CFEF pub = ec.pointMultiply(prv, ec.G) msg = bytes.fromhex("243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89") expected_sig = (0x2A298DACAE57395A15D0795DDBFD1DCB564DA82B0F269BC70A74F8220429BA1D, 0x1E51A22CCEC35599B8F266912281F8365FFC2D035A230434A1A64DC59F7013FD) eph_prv = int.from_bytes(sha256(prv.to_bytes(32, byteorder="big") + msg).digest(), byteorder="big") sig = ecssa_sign(msg, prv, eph_prv) self.assertTrue(ecssa_verify(msg, sig, pub)) # malleability self.assertFalse(ecssa_verify(msg, (sig[0], ec.n - sig[1]), pub)) self.assertEqual(sig, expected_sig) e = sha256(sig[0].to_bytes(32, byteorder="big") + bytes_from_Point(ec, pub, True) + msg).digest() self.assertEqual(ecssa_pubkey_recovery(e, sig), pub)
def test_ecssa_1(self): prv = 0x1 pub = ec.pointMultiply(prv, ec.G) msg = b'\x00' * 32 expected_sig = (0x787A848E71043D280C50470E8E1532B2DD5D20EE912A45DBDD2BD1DFBF187EF6, 0x7031A98831859DC34DFFEEDDA86831842CCD0079E1F92AF177F7F22CC1DCED05) eph_prv = int.from_bytes(sha256(prv.to_bytes(32, byteorder="big") + msg).digest(), byteorder="big") sig = ecssa_sign(msg, prv, eph_prv) self.assertTrue(ecssa_verify(msg, sig, pub)) # malleability self.assertFalse(ecssa_verify(msg, (sig[0], ec.n - sig[1]), pub)) self.assertEqual(sig, expected_sig) e = sha256(sig[0].to_bytes(32, byteorder="big") + bytes_from_Point(ec, pub, True) + msg).digest() self.assertEqual(ecssa_pubkey_recovery(e, sig), pub)
def bip32_xpub_from_xprv(xprv: bytes) -> bytes: """Neutered Derivation (ND) Computation of the extended public key corresponding to an extended private key (“neutered” as it removes the ability to sign transactions) """ xprv = b58decode_check(xprv, 78) assert xprv[45] == 0, "extended key is not a private one" i = PRIVATE.index(xprv[:4]) # serialization data xpub = PUBLIC[i] # version # unchanged serialization data xpub += xprv[4:5] # depth xpub += xprv[5:9] # parent pubkey fingerprint xpub += xprv[9:13] # child index xpub += xprv[13:45] # chain code p = xprv[46:] P = pointMultiply(ec, p, ec.G) xpub += bytes_from_Point(ec, P, True) # public key return b58encode_check(xpub)
# ==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' child_number = 0 + 0x80000000 #hardened
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)
def test_ecssamusig(self): msg = 'message to sign' m = sha256(msg.encode()).digest() # first signer (is the message needed here? maybe for rfc6979?) prv1 = int_from_Scalar( ec, '0c28fca386c7a227600b2fe50b7cae11ec86d3bf1fbe471be89827e19d92ad1d') Q1 = ec.pointMultiply(prv1, ec.G) HQ1 = int_from_hash( sha256(bytes_from_Point(ec, Q1, False)).digest(), ec.order) prv1 = HQ1 * prv1 eph_prv1 = 0x012a2a833eac4e67e06611aba01345b85cdd4f5ad44f72e369ef0dd640424dbb R1 = ec.pointMultiply(eph_prv1, ec.G) if R1[1] % 2 == 1: #must be even eph_prv1 = ec.order - eph_prv1 R1 = ec.pointMultiply(eph_prv1, ec.G) R1_x = R1[0] # second signer (is the message needed here? maybe for rfc6979?) prv2 = int_from_Scalar( ec, '0c28fca386c7a227600b2fe50b7cae11ec86d3bf1fbe471be89827e19d72aa1d') Q2 = ec.pointMultiply(prv2, ec.G) HQ2 = int_from_hash( sha256(bytes_from_Point(ec, Q2, False)).digest(), ec.order) prv2 = HQ2 * prv2 eph_prv2 = 0x01a2a0d3eac4e67e06611aba01345b85cdd4f5ad44f72e369ef0dd640424dbdb R2 = ec.pointMultiply(eph_prv2, ec.G) if R2[1] % 2 == 1: #must be even eph_prv2 = ec.order - eph_prv2 R2 = ec.pointMultiply(eph_prv2, ec.G) R2_x = R2[0] ###################### # exchange Rx, compute s ###################### # first signer use R2_x R2_y_recovered = ec.y(R2_x, 0) R2_recovered = (R2_x, R2_y_recovered) R1_All = ec.pointAdd(R1, R2_recovered) if R1_All[1] % 2 == 1: # must be even eph_prv1 = ec.order - eph_prv1 R1_All_x = R1_All[0].to_bytes(32, 'big') e1 = int_from_hash(sha256(R1_All_x + m).digest(), ec.order) assert e1 != 0 and e1 < ec.order, "sign fail" s1 = (eph_prv1 - e1 * prv1) % ec.order # second signer use R1_x R1_y_recovered = ec.y(R1_x, 0) R1_recovered = (R1_x, R1_y_recovered) R2_All = ec.pointAdd(R2, R1_recovered) if R2_All[1] % 2 == 1: eph_prv2 = ec.order - eph_prv2 R2_All_x = R2_All[0].to_bytes(32, 'big') e2 = int_from_hash(sha256(R2_All_x + m).digest(), ec.order) assert e2 != 0 and e2 < ec.order, "sign fail" s2 = (eph_prv2 - e2 * prv2) % ec.order ###################### # combine signatures into a single signature ###################### # anyone can do the following assert R1_All_x == R2_All_x, "sign fail" R_All_x = R1_All[0] s_All = (s1 + s2) % ec.order ssasig = (R_All_x, s_All) Q_All = ec.pointAdd(ec.pointMultiply(HQ1, Q1), ec.pointMultiply(HQ2, Q2)) self.assertTrue(ecssa_verify(msg, ssasig, Q_All, sha256))
def pubkey_from_prvkey(prvkey: PrivateKey, compressed: bool = True) -> bytes: """Private key to (bytes) public key""" P = pointMultiply(ec, prvkey, ec.G) return bytes_from_Point(ec, P, compressed)
def test_ecssamusig(self): msg = 'message to sign' m = sha256(msg.encode()).digest() # first signer (is the message needed here? maybe for rfc6979?) prv1 = int_from_Scalar( ec, '0c28fca386c7a227600b2fe50b7cae11ec86d3bf1fbe471be89827e19d92ad1d') Q1 = ec.pointMultiply(prv1, ec.G) HQ1 = int_from_hash( sha256(bytes_from_Point(ec, Q1, False)).digest(), ec.n) prv1 = HQ1 * prv1 eph_prv1 = 0x012a2a833eac4e67e06611aba01345b85cdd4f5ad44f72e369ef0dd640424dbb R1 = ec.pointMultiply(eph_prv1, ec.G) R1_x = R1[0] # break the simmetry: any criteria could be used, jacobi is standard if ec.jacobi(R1[1]) != 1: eph_prv1 = ec.n - eph_prv1 R1 = R1_x, ec.yQuadraticResidue(R1_x, True) #R1 = ec.pointMultiply(eph_prv1, ec.G) # second signer (is the message needed here? maybe for rfc6979?) prv2 = int_from_Scalar( ec, '0c28fca386c7a227600b2fe50b7cae11ec86d3bf1fbe471be89827e19d72aa1d') Q2 = ec.pointMultiply(prv2, ec.G) HQ2 = int_from_hash( sha256(bytes_from_Point(ec, Q2, False)).digest(), ec.n) prv2 = HQ2 * prv2 eph_prv2 = 0x01a2a0d3eac4e67e06611aba01345b85cdd4f5ad44f72e369ef0dd640424dbdb R2 = ec.pointMultiply(eph_prv2, ec.G) R2_x = R2[0] # break the simmetry: any criteria could be used, jacobi is standard if ec.jacobi(R2[1]) != 1: eph_prv2 = ec.n - eph_prv2 R2 = R2_x, ec.yQuadraticResidue(R2_x, True) #R2 = ec.pointMultiply(eph_prv2, ec.G) Q_All = ec.pointAdd(ec.pointMultiply(HQ1, Q1), ec.pointMultiply(HQ2, Q2)) # joint public key ######################## # exchange Rx, compute s # first signer use R2_x # break the simmetry: any criteria could be used, jacobi is standard y = ec.yQuadraticResidue(R2_x, True) R2_recovered = (R2_x, y) R1_All = ec.pointAdd(R1, R2_recovered) # break the simmetry: any criteria could be used, jacobi is standard if ec.jacobi(R1_All[1]) != 1: # no need to actually change R1_All[1], as it is not used anymore # let's fix eph_prv1 instead, as it is used later eph_prv1 = ec.n - eph_prv1 e1 = int_from_hash( sha256(R1_All[0].to_bytes(32, byteorder="big") + bytes_from_Point(ec, Q_All, True) + m).digest(), ec.n) assert e1 != 0 and e1 < ec.n, "sign fail" s1 = (eph_prv1 + e1 * prv1) % ec.n # second signer use R1_x # break the simmetry: any criteria could be used, jacobi is standard y = ec.yQuadraticResidue(R1_x, True) R1_recovered = (R1_x, y) R2_All = ec.pointAdd(R2, R1_recovered) # break the simmetry: any criteria could be used, jacobi is standard if ec.jacobi(R2_All[1]) != 1: # no need to actually change R2_All[1], as it is not used anymore # let's fix eph_prv2 instead, as it is used later eph_prv2 = ec.n - eph_prv2 e2 = int_from_hash( sha256(R2_All[0].to_bytes(32, byteorder="big") + bytes_from_Point(ec, Q_All, True) + m).digest(), ec.n) assert e2 != 0 and e2 < ec.n, "sign fail" s2 = (eph_prv2 + e2 * prv2) % ec.n ############################################ # combine signatures into a single signature # anyone can do the following assert R1_All[0] == R2_All[0], "sign fail" s_All = (s1 + s2) % ec.n ssasig = (R1_All[0], s_All) self.assertTrue(ecssa_verify(msg, ssasig, Q_All, sha256))
def test_all_curves(self): for ec in allcurves: # the infinity point is represented by None self.assertEqual(ec.pointMultiply(0, ec.G), None) self.assertEqual(pointMultiply(ec, 0, ec.G), None) self.assertEqual(ec.pointMultiply(1, ec.G), ec.G) self.assertEqual(pointMultiply(ec, 1, ec.G), ec.G) Gy_odd = ec.y(ec.G[0], True) self.assertEqual(Gy_odd % 2, 1) Gy_even = ec.y(ec.G[0], False) self.assertEqual(Gy_even % 2, 0) self.assertTrue(ec.G[1] in (Gy_odd, Gy_even)) Gbytes = bytes_from_Point(ec, ec.G, True) Gbytes = bytes_from_Point(ec, Gbytes, True) G2 = tuple_from_Point(ec, Gbytes) G2 = tuple_from_Point(ec, G2) self.assertEqual(ec.G, G2) Gbytes = bytes_from_Point(ec, ec.G, False) Gbytes = bytes_from_Point(ec, Gbytes, False) G2 = tuple_from_Point(ec, Gbytes) G2 = tuple_from_Point(ec, G2) self.assertEqual(ec.G, G2) P = ec.pointAdd(None, ec.G) self.assertEqual(P, ec.G) P = ec.pointAdd(ec.G, None) self.assertEqual(P, ec.G) P = ec.pointAdd(None, None) self.assertEqual(P, None) P = pointAdd(ec, None, ec.G) self.assertEqual(P, ec.G) P = pointAdd(ec, ec.G, None) self.assertEqual(P, ec.G) P = pointAdd(ec, None, None) self.assertEqual(P, None) P = ec.pointDouble(ec.G) self.assertEqual(P, ec.pointMultiply(2, ec.G)) P = pointDouble(ec, ec.G) self.assertEqual(P, pointMultiply(ec, 2, ec.G)) P = ec.pointAdd(ec.G, ec.G) self.assertEqual(P, ec.pointMultiply(2, ec.G)) P = pointAdd(ec, ec.G, ec.G) self.assertEqual(P, pointMultiply(ec, 2, ec.G)) P = ec.pointMultiply(ec.order - 1, ec.G) self.assertEqual(ec.pointAdd(P, ec.G), None) self.assertEqual(ec.pointMultiply(ec.order, ec.G), None) P = pointMultiply(ec, ec.order - 1, ec.G) self.assertEqual(pointAdd(ec, P, ec.G), None) self.assertEqual(pointMultiply(ec, ec.order, ec.G), None) self.assertEqual(ec.pointMultiply(0, None), None) self.assertEqual(ec.pointMultiply(1, None), None) self.assertEqual(ec.pointMultiply(25, None), None) self.assertEqual(pointMultiply(ec, 0, None), None) self.assertEqual(pointMultiply(ec, 1, None), None) self.assertEqual(pointMultiply(ec, 25, None), None) ec2 = eval(repr(ec)) self.assertEqual(str(ec2), str(ec2)) if (ec.order % 2 == 0): P = ec.pointMultiply(ec.order // 2, ec.G) self.assertEqual(P[1], 0) self.assertEqual(ec.pointDouble(P), None) P = pointMultiply(ec, ec.order // 2, ec.G) self.assertEqual(P[1], 0) self.assertEqual(pointDouble(ec, P), None)