OSDN Git Service

add xpub_to_child_xpub
[bytom/bytom-kit.git] / app / model / key.py
1 import random
2 import hashlib
3 import pbkdf2
4 import hmac
5 import ed25519
6 from app.model.signature import *
7 from app.model.edwards25519 import *
8 from app.model.utils import *
9
10 # create_key create 128 bits entropy
11 def create_entropy():
12     hex_str = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"]
13     entropy_str = ""
14     for _ in range(32):
15         # create interger in range [1,15]
16         num = random.randint(0,15)
17         entropy_str += hex_str[num]
18
19     return entropy_str
20
21
22 # entropy_to_mnemonic create mnemonic from 128 bits entropy(the entropy_str length is 32)
23 # return 12 mnemonics
24 # You can get more test data from: https://gist.github.com/zcc0721/63aeb5143807950f7b7051fadc08cef0
25 # test data 1:
26 #   entropy_str: 1db8b283eb4623e749732a341396e0c9
27 #   mnemonic_str: buffalo sheriff path story giraffe victory chair grab cross original return napkin
28 # test data 2:
29 #   entropy_str: 4d33735a9e92f634d22aecbb4044038d
30 #   mnemonic_str: essay oppose stove diamond control bounce emerge frown robust acquire abstract brick
31 # test data 3:
32 #   entropy_str: 089fe9bf0cac76760bc4b131d938669e
33 #   mnemonic_str: ancient young hurt bone shuffle deposit congress normal crack six boost despair
34 def entropy_to_mnemonic(entropy_str):
35     mnemonic_str = ""
36     mnemonic_length = 12
37
38     # create a 12 elements mnemonic_list 
39     mnemonic_list = []
40     for _ in range(mnemonic_length):
41         mnemonic_list.append('')
42
43     entropy_bytes = bytes.fromhex(entropy_str)
44     checksum = hashlib.sha256(entropy_bytes).hexdigest()[:1]
45     new_entropy_str = "0" + entropy_str + checksum
46     new_entropy_bytes = bytes.fromhex(new_entropy_str)
47     new_entropy_int = int.from_bytes(new_entropy_bytes, byteorder='big')
48
49     file = open('./app/model/english_mnemonic.txt', mode='r')
50     word_list = file.readlines()
51     file.close()
52
53     for i in range(11, -1, -1):
54         word_index = new_entropy_int % 2048
55         new_entropy_int = new_entropy_int >> 11
56         mnemonic_list[i] = word_list[word_index]
57
58     for i in range(12):
59         mnemonic_str += mnemonic_list[i][:-1]
60         mnemonic_str += " "
61
62     return mnemonic_str[:-1]
63
64
65 # mnemonic_to_seed create seed from mnemonic
66 # You can find more details from: https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki#from-mnemonic-to-seed
67 # You can get more test data from: https://gist.github.com/zcc0721/4918e891073a9ca6c444ec7490298e82
68 # test data 1:
69 #   mnemonic_str: ancient young hurt bone shuffle deposit congress normal crack six boost despair
70 #   seed_str: afa3a86bbec2f40bb32833fc6324593824c4fc7821ed32eac1f762b5893e56745f66a6c6f2588b3d627680aa4e0e50efd25065097b3daa8c6a19d606838fe7d4
71 # test data 2:
72 #   mnemonic_str: rich decrease live pluck friend recipe burden minor similar agent tired horror
73 #   seed_str: b435f948bd3748ede8f9d6f59728d669939e79c6c885667a5c138e05bbabde1de0dcfcbe0c6112022fbbf0da522f4e224a9c2381016380688b51886248b3156f
74 # test data 3:
75 #   mnemonic_str: enough ginger just mutual fit trash loop mule peasant lady market hub
76 #   seed_str: ecc2bbb6c0492873cdbc81edf56bd896d3b644047879840e357be735b7fa7b6f4af1be7b8d71cc649ac4ca3816f9ccaf11bf49f4effb845f3c19e16eaf8bfcda
77 def mnemonic_to_seed(mnemonic_str):
78     password_str = mnemonic_str
79     salt_str = "mnemonic"
80     seed_str = pbkdf2.PBKDF2(password_str, salt_str, iterations=2048, digestmodule=hashlib.sha512, macmodule=hmac).hexread(64)
81
82     return seed_str
83
84
85 # s_str must be >= 32 bytes long and gets rewritten in place.
86 # This is NOT the same pruning as in Ed25519: it additionally clears the third
87 # highest bit to ensure subkeys do not overflow the second highest bit.
88 def prune_root_scalar(s_str):
89     s_0_int = int.from_bytes(bytes.fromhex(s_str[0:2]), byteorder='big') & 248
90     s_0_str = "%0.2x" % s_0_int
91     new_s_str = s_0_str + s_str[2:]
92     s_31_int = int.from_bytes(bytes.fromhex(new_s_str[62:64]), byteorder='big') & 31
93     s_31_str = "%0.2x" % s_31_int
94     new_s_str = new_s_str[:62] + s_31_str + new_s_str[64:]
95     s_31_int = int.from_bytes(bytes.fromhex(new_s_str[62:64]), byteorder='big') | 64
96     s_31_str = "%0.2x" % s_31_int
97     new_s_str = new_s_str[:62] + s_31_str + new_s_str[64:]
98     
99     return new_s_str
100
101
102 # seed_to_root_xprv create rootxprv from seed
103 # seed_str length is 512 bits.
104 # root_xprv length is 512 bits.
105 # You can get more test data from: https://gist.github.com/zcc0721/0aa1b971f4bf93d8f67e25f57b8b97ee
106 # test data 1:
107 #   seed_str: afa3a86bbec2f40bb32833fc6324593824c4fc7821ed32eac1f762b5893e56745f66a6c6f2588b3d627680aa4e0e50efd25065097b3daa8c6a19d606838fe7d4
108 #   root_xprv_str: 302a25c7c0a68a83fa043f594a2db8b44bc871fced553a8a33144b31bc7fb84887c9e75915bb6ba3fd0b9f94a60b7a5897ab9db6a48f888c2559132dba9152b0
109 # test data 2:
110 #   seed_str: b435f948bd3748ede8f9d6f59728d669939e79c6c885667a5c138e05bbabde1de0dcfcbe0c6112022fbbf0da522f4e224a9c2381016380688b51886248b3156f
111 #   root_xprv_str: 6032adeb967ac5ccbf988cf8190817bf9040c8cfd9cdfe3d5e400effb2946946d478b61cc6be936f367ae769eb1dc65c473ee73cac2eb43cf6d5e7c62b7f0062
112 # test data 3:
113 #   seed_str: ecc2bbb6c0492873cdbc81edf56bd896d3b644047879840e357be735b7fa7b6f4af1be7b8d71cc649ac4ca3816f9ccaf11bf49f4effb845f3c19e16eaf8bfcda
114 #   root_xprv_str: a01d6b741b0e74b8d0836ac22b675bbf8e108148ef018d1b000aef1a899a134bd316c0f59e7333520ae1a429504073b2773869e95aa95bb3a4fa0ec76744025c
115 def seed_to_root_xprv(seed_str):
116     hc_str = hmac.HMAC(b'Root', bytes.fromhex(seed_str), digestmod=hashlib.sha512).hexdigest()
117     root_xprv_str = prune_root_scalar(hc_str[:64]) + hc_str[64:]
118
119     return root_xprv_str
120
121
122 # xprv_to_xpub derives new xpub from xprv
123 # xprv length is 64 bytes.
124 # xpub length is 64 bytes.
125 # You can get more test data from: https://gist.github.com/zcc0721/d872a219fa91621d60357278bc62a512
126 # PLEASE ATTENTION: 
127 # xprv_bytes = bytes.fromhex(xprv_str)
128 # xprv_bytes[31] <= 127
129 # This is the precondition. Please ref: https://github.com/bytom/bytom/blob/dev/crypto/ed25519/internal/edwards25519/edwards25519.go#L958-L963
130 # test data 1:
131 #   xprv_str: c003f4bcccf9ad6f05ad2c84fa5ff98430eb8e73de5de232bc29334c7d074759d513bc370335cac51d77f0be5dfe84de024cfee562530b4d873b5f5e2ff4f57c
132 #   xpub_str: 1b0541a7664cee929edb54d9ef21996b90546918a920a77e1cd6015d97c56563d513bc370335cac51d77f0be5dfe84de024cfee562530b4d873b5f5e2ff4f57c
133 # test data 2:
134 #   xprv_str: 36667587de27eec684fc4b222276f22a24d9a82e947ee0119148bedd4dec461dd4e1b1d95dfb0f78896677ea1026af7510b41fabd3bd5771311c0cb6968337b2
135 #   xpub_str: ef0b3a8b0d66523d88f214900101ddb08a2a2a6db28bd8002e5995c1f1cbbc4cd4e1b1d95dfb0f78896677ea1026af7510b41fabd3bd5771311c0cb6968337b2
136 # test data 3:
137 #   xprv_str: 74a49c698dbd3c12e36b0b287447d833f74f3937ff132ebff7054baa18623c35a705bb18b82e2ac0384b5127db97016e63609f712bc90e3506cfbea97599f46f
138 #   xpub_str: 522940d6440fdc45363df2097e9cac29a9a8a33ac339f8b7cff848c199db5a1ca705bb18b82e2ac0384b5127db97016e63609f712bc90e3506cfbea97599f46f
139 def xprv_to_xpub(xprv_str):
140     xprv_bytes = bytes.fromhex(xprv_str)
141     scalar = decodeint(xprv_bytes[:len(xprv_bytes)//2])
142     buf = encodepoint(scalarmultbase(scalar))
143     xpub = buf + xprv_bytes[len(xprv_bytes)//2:]
144     xpub_str = xpub.hex()
145     return xpub_str
146
147
148 # xprv_to_expanded_private_key create expanded private key from xprv
149 # You can get more test data from: https://gist.github.com/zcc0721/ef0bf2e69f5e92b29d716981f2a8fe7d
150 # test data 1:
151 #   xprv_str: 406c82307bf7978d17f3ecfeea7705370e9faef2027affa86c8027c6e11a8a50e231e65bd97048850ae6c39d0f46b63ae70aa24f5aac7877727c430c2201e6d6
152 #   expanded_private_key_str_xprv: 406c82307bf7978d17f3ecfeea7705370e9faef2027affa86c8027c6e11a8a50d828bf44b1a109c2bbb4c72685858e2f2ab8b405beef1e4ecc12d1ed8511e8eb
153 # test data 2:
154 #   xprv_str: 6032adeb967ac5ccbf988cf8190817bf9040c8cfd9cdfe3d5e400effb2946946d478b61cc6be936f367ae769eb1dc65c473ee73cac2eb43cf6d5e7c62b7f0062
155 #   expanded_private_key_str_xprv: 6032adeb967ac5ccbf988cf8190817bf9040c8cfd9cdfe3d5e400effb2946946ddbb71e7a76595c6bc24937d76085d24315713764cbdf1364ab9091953009cd8
156 # test data 3:
157 #   xprv_str: 509a095ad862322641b8d66e84561aae1d4816045167e2c4dfadf464928e114300c0a162d41c0cdf196d61f4492f546e50bfff253b9d5d930d1bb89197cd333d
158 #   expanded_private_key_str_xprv: 509a095ad862322641b8d66e84561aae1d4816045167e2c4dfadf464928e11432787f5e10f9598f80fb41e4a648b609463c06e625641366f3279658b2b0f5268
159 def xprv_to_expanded_private_key(xprv_str):
160     hc_str = hmac.HMAC(b'Expand', bytes.fromhex(xprv_str), digestmod=hashlib.sha512).hexdigest()
161     expanded_private_key_str = xprv_str[:64] + hc_str[64:]
162
163     return expanded_private_key_str
164
165
166 # xpub_to_public_key create 32 bytes public key from xpub
167 # xpub length is 64 bytes.
168 # You can get more test data from: https://gist.github.com/zcc0721/9e10f2fa5bd0c8f33aa6dfc87f6aa856
169 # test data 1:
170 #   xpub_str: ecc2bbb6c0492873cdbc81edf56bd896d3b644047879840e357be735b7fa7b6f4af1be7b8d71cc649ac4ca3816f9ccaf11bf49f4effb845f3c19e16eaf8bfcda
171 #   public_key_str: ecc2bbb6c0492873cdbc81edf56bd896d3b644047879840e357be735b7fa7b6f
172 # test data 2:
173 #   xpub_str: 406c82307bf7978d17f3ecfeea7705370e9faef2027affa86c8027c6e11a8a50e231e65bd97048850ae6c39d0f46b63ae70aa24f5aac7877727c430c2201e6d6
174 #   public_key_str: 406c82307bf7978d17f3ecfeea7705370e9faef2027affa86c8027c6e11a8a50
175 # test data 3:
176 #   xpub_str: b435f948bd3748ede8f9d6f59728d669939e79c6c885667a5c138e05bbabde1de0dcfcbe0c6112022fbbf0da522f4e224a9c2381016380688b51886248b3156f
177 #   public_key_str: b435f948bd3748ede8f9d6f59728d669939e79c6c885667a5c138e05bbabde1d
178 def xpub_to_public_key(xpub_str):
179     public_key_str = xpub_str[:64]
180
181     return public_key_str
182
183
184 def prune_intermediate_scalar(f):
185     f = bytearray(f)
186     f[0] = f[0] & 248       # clear bottom 3 bits
187     f[29] = f[29] & 1       # clear 7 high bits
188     f[30] = 0               # clear 8 bits
189     f[31] = 0               # clear 8 bits
190     return f
191
192
193 # xprv_to_child_xprv create new xprv through the path
194 # xprv_str length is 64 bytes.
195 # path_list item is hex string.
196 # child_xprv length is 64 bytes.
197 # You can get more test data from: https://gist.github.com/zcc0721/3377f520954db38070e8e9c80d3a5bfd
198 # test data 1:
199 #   xprv_str: 10fdbc41a4d3b8e5a0f50dd3905c1660e7476d4db3dbd9454fa4347500a633531c487e8174ffc0cfa76c3be6833111a9b8cd94446e37a76ee18bb21a7d6ea66b
200 #   path_list: 010400000000000000
201 #   path_list: 0100000000000000
202 #   child_xprv_str: 0813a3abf814e4b4064b9b0492071176d8d98652081aced6fefe2b7363a83353f960274ff5ef195599a765e7bc24eddc2a1e6c73da0e6e0a4b47e65338bea9a6
203 # test data 2:
204 #   xprv_str: c003f4bcccf9ad6f05ad2c84fa5ff98430eb8e73de5de232bc29334c7d074759d513bc370335cac51d77f0be5dfe84de024cfee562530b4d873b5f5e2ff4f57c
205 #   path_list: 00
206 #   path_list: 00
207 #   child_xprv_str: b885ac5535c35ae45b51a84b1190f7c31b21acff552c7680413905a9c6084759e9a8f3578fe2973e37d96bad45e8d9255f3b82019f326550d24374aeafece958
208 # test data 3:
209 #   xprv_str: 0031615bdf7906a19360f08029354d12eaaedc9046806aefd672e3b93b024e495a95ba63cf47903eb742cd1843a5252118f24c0c496e9213bd42de70f649a798
210 #   path_list: 00010203
211 #   child_xprv_str: 20f86339d653bb928ad1f7456279692ac6adf89035f846c6659aaa151c034e497387952cb0dbd6c03bae6742ebe3213b7c8da5805900ab743a653dd3799793eb
212 # test data 4:
213 #   xprv_str: 0031615bdf7906a19360f08029354d12eaaedc9046806aefd672e3b93b024e495a95ba63cf47903eb742cd1843a5252118f24c0c496e9213bd42de70f649a798
214 #   path_list: 00
215 #   child_xprv_str: 883e65e6e86499bdd170c14d67e62359dd020dd63056a75ff75983a682024e49e8cc52d8e74c5dfd75b0b326c8c97ca7397b7f954ad0b655b8848bfac666f09f
216 # test data 5:
217 #   xprv_str: c003f4bcccf9ad6f05ad2c84fa5ff98430eb8e73de5de232bc29334c7d074759d513bc370335cac51d77f0be5dfe84de024cfee562530b4d873b5f5e2ff4f57c
218 #   path_list: 010203
219 #   path_list: 7906a1
220 #   child_xprv_str: 4853a0b00bdcb139e85855d9594e5f641b65218db7c50426946511397e094759bd9de7f2dcad9d7d45389bc94baecaec88aabf58f6e1d832b1f9995a93ec37ea
221 def xprv_to_child_xprv(xprv_str, path_list):
222     for i in range(len(path_list)):
223         selector_bytes = bytes.fromhex(path_list[i])
224         xpub_str = xprv_to_xpub(xprv_str)
225         xpub_bytes = bytes.fromhex(xpub_str)
226         xprv_bytes = bytes.fromhex(xprv_str)
227         hc_bytes = hmac.HMAC(xpub_bytes[32:], b'N'+xpub_bytes[:32]+selector_bytes, digestmod=hashlib.sha512).digest()
228         hc_bytearr = bytearray(hc_bytes)
229         
230         f = hc_bytearr[:32]
231         f = prune_intermediate_scalar(f)
232         hc_bytearr = f[:32] + hc_bytearr[32:]
233         
234         carry = 0
235         total = 0
236         for i in range(32):
237             total = xprv_bytes[i] + hc_bytearr[i] + carry
238             hc_bytearr[i] = total & 0xff
239             carry = total >> 8
240         if (total >> 8) != 0:
241             print("sum does not fit in 256-bit int")
242         xprv_str = hc_bytearr.hex()
243         
244     child_xprv_str = xprv_str
245     return child_xprv_str
246
247 # xpub_to_child_xpub create new xpub through the path
248 # xpub_str length is 64 bytes.
249 # path_list item is hex string.
250 # child_xpub length is 64 bytes.
251 # You can get more test data from: https://gist.github.com/zcc0721/1dea9eb1edb04f57bc01fecb867301b8
252 # test data 1:
253 #   xpub_str: cb22ce197d342d6bb440b0bf13ddd674f367275d28a00f893d7f0b10817690fd01ff37ac4a07869214c2735bba0175e001abe608db18538e083e1e44430a273b
254 #   path_list: 010400000000000000
255 #   path_list: 0100000000000000
256 #   child_xpub_str: 25405adf9bcefebaa2533631a6bdd5a93108e52ed048c7c49df21a28668768f8d15048473b96fc4d3bc041a881168b41552cabe883221a683aeddc37c1f4644c
257 # test data 2:
258 #   xpub_str: cb22ce197d342d6bb440b0bf13ddd674f367275d28a00f893d7f0b10817690fd01ff37ac4a07869214c2735bba0175e001abe608db18538e083e1e44430a273b
259 #   path_list: 00
260 #   path_list: 00
261 #   child_xpub_str: 1ff4b10aa17eb164a01bedf4f48d55c1bcbd55f28adb85e31c4bad98c070fc4ecb4228fb3f2f848384cc1a9ea82e0b351a551a035dd8ab34e198cfe64df86c79
262 # test data 3:
263 #   xpub_str: cb22ce197d342d6bb440b0bf13ddd674f367275d28a00f893d7f0b10817690fd01ff37ac4a07869214c2735bba0175e001abe608db18538e083e1e44430a273b
264 #   path_list: 00010203
265 #   child_xpub_str: 19ab025cd895705c5e2fab8d61e97bcf93670d2c2d6b4cdf06b5347a0cf0527df138d9e540093aad51ed56cf67e6a4b36e6c68327c61593707829339cc9a7f65
266 # test data 4:
267 #   xpub_str: ead6415a077b91aa7de32e1cf63350f9351d0298f5accc2cf92ef9429bd1f86c166364ce19322721b7dec84442c3665d97d0e995ba4d01c0f4b19b841379ac90
268 #   path_list: 00010203
269 #   path_list: 03ededed
270 #   path_list: 123456
271 #   child_xpub_str: c6888c31265519f59975f9fe25a4199735efbb24923648dd880dacb6ed580bdc7b79a9aa09095590175f756c1e11fcb4f8febecb67582c9fea154fd2547cd381
272 # test data 5:
273 #   xpub_str: 1b0541a7664cee929edb54d9ef21996b90546918a920a77e1cd6015d97c56563d513bc370335cac51d77f0be5dfe84de024cfee562530b4d873b5f5e2ff4f57c
274 #   path_list: 010203
275 #   path_list: 7906a1
276 #   child_xpub_str: e65c1a9714e2116c6e5d57dee188a53b98dc901a21def5a5ca46fcf78303f4f2bd9de7f2dcad9d7d45389bc94baecaec88aabf58f6e1d832b1f9995a93ec37ea
277 def xpub_to_child_xpub(xpub_str, path_list):
278     for i in range(len(path_list)):
279         selector_bytes = bytes.fromhex(path_list[i])
280         xpub_bytes = bytes.fromhex(xpub_str)
281         hc_bytes = hmac.HMAC(xpub_bytes[32:], b'N'+xpub_bytes[:32]+selector_bytes, digestmod=hashlib.sha512).digest()
282         hc_bytearr = bytearray(hc_bytes)
283
284         f = hc_bytearr[:32]
285         f = prune_intermediate_scalar(f)
286         f = bytes(f)
287         scalar = decodeint(f)
288         F = scalarmultbase(scalar)
289
290         P = decodepoint(xpub_bytes[:32])
291         P = edwards_add(P, F)
292         public_key = encodepoint(P)
293
294         xpub_bytes = public_key[:32] + hc_bytes[32:]
295         xpub_str = xpub_bytes.hex()
296
297     child_xpub_str = xpub_str
298     return child_xpub_str
299
300
301 # xprv_sign sign message
302 # xprv_str length is 64 bytes.
303 # message_str length is variable.
304 # signature_str length is 64 bytes.
305 # You can get more test data from: https://gist.github.com/zcc0721/61a26c811a632623678e274cc7e5c10b
306 # test data 1:
307 #   xprv_str: c003f4bcccf9ad6f05ad2c84fa5ff98430eb8e73de5de232bc29334c7d074759d513bc370335cac51d77f0be5dfe84de024cfee562530b4d873b5f5e2ff4f57c
308 #   xpub_str: 1b0541a7664cee929edb54d9ef21996b90546918a920a77e1cd6015d97c56563d513bc370335cac51d77f0be5dfe84de024cfee562530b4d873b5f5e2ff4f57c
309 #   message_str: a6ce34eec332b32e42ef3407e052d64ac625da6f
310 #   signature_str: f02f5bb22d8b32f14e88059a786379c26256892f45cf64770c844d0c5de2e52c00307b7bb25fcbb18be13c339a2f511a7c015a8cf81ac681052efe8e50eff00e
311 # test data 2:
312 #   xprv_str: 008ce51e3b52ee03eb0ad96c55eb5c9fe8736410518b585a0b7f35b2ab48d24c166364ce19322721b7dec84442c3665d97d0e995ba4d01c0f4b19b841379ac90
313 #   xpub_str: ead6415a077b91aa7de32e1cf63350f9351d0298f5accc2cf92ef9429bd1f86c166364ce19322721b7dec84442c3665d97d0e995ba4d01c0f4b19b841379ac90
314 #   message_str: 68656c6c6f206279746f6d      # value is: 'hello bytom'
315 #   signature_str: 1cc6b0f4031352ffd7a62540f13edddaaebf2df05db7a4926df5513129a8e85dcff1324545a024b16f958239ea67840ced3c2d57bb468dbf0e6cf1d1075f0b0f
316 # test data 3:
317 #   xprv_str: 88c0c40fb54ef9c1b90af8cce8dc4c9d54f915074dde93f79ab61cedae03444101ff37ac4a07869214c2735bba0175e001abe608db18538e083e1e44430a273b
318 #   xpub_str: cb22ce197d342d6bb440b0bf13ddd674f367275d28a00f893d7f0b10817690fd01ff37ac4a07869214c2735bba0175e001abe608db18538e083e1e44430a273b
319 #   message_str: 1246b84985e1ab5f83f4ec2bdf271114666fd3d9e24d12981a3c861b9ed523c6
320 #   signature_str: ab18f49b23d03295bc2a3f2a7d5bb53a2997bed733e1fc408b50ec834ae7e43f7da40fe5d9d50f6ef2d188e1d27f976aa2586cef1ba00dd098b5c9effa046306
321 def xprv_sign(xprv_str, message_str):
322     xprv_str = xprv_to_expanded_private_key(xprv_str)
323     xprv_bytes = bytes.fromhex(xprv_str)
324     message_bytes = bytes.fromhex(message_str)
325     data_bytes = xprv_bytes[32:64] + message_bytes
326
327     message_digest = hashlib.sha512(data_bytes).digest()
328     message_digest = sc_reduce32(message_digest.hex().encode())
329     message_digest = bytes.fromhex(message_digest.decode())
330     message_digest_reduced = message_digest[0:32]
331
332     scalar = decodeint(message_digest_reduced)
333     encoded_r = encodepoint(scalarmultbase(scalar))
334     xpub_str = xprv_to_xpub(xprv_str)
335     xpub_bytes = bytes.fromhex(xpub_str)
336     hram_digest_data = encoded_r + xpub_bytes[:32] + message_bytes
337
338     hram_digest = hashlib.sha512(hram_digest_data).digest()
339     hram_digest = sc_reduce32(hram_digest.hex().encode())
340     hram_digest = bytes.fromhex(hram_digest.decode())
341     hram_digest_reduced = hram_digest[0:32]
342
343     sk = xprv_bytes[:32]
344     s = sc_muladd(hram_digest_reduced.hex().encode(), sk.hex().encode(), message_digest_reduced.hex().encode())
345     s = bytes.fromhex(s.decode())
346
347     signature_bytes = encoded_r + s
348     signature_str = signature_bytes.hex()
349
350     return signature_str
351
352
353 # xpub_verify verify signature
354 # xpub_str length is 64 bytes.
355 # message_str length is variable.
356 # signature_str length is 64 bytes.
357 # You can get more test data from: https://gist.github.com/zcc0721/61a26c811a632623678e274cc7e5c10b
358 # test data 1:
359 #   xprv_str: c003f4bcccf9ad6f05ad2c84fa5ff98430eb8e73de5de232bc29334c7d074759d513bc370335cac51d77f0be5dfe84de024cfee562530b4d873b5f5e2ff4f57c
360 #   xpub_str: 1b0541a7664cee929edb54d9ef21996b90546918a920a77e1cd6015d97c56563d513bc370335cac51d77f0be5dfe84de024cfee562530b4d873b5f5e2ff4f57c
361 #   message_str: a6ce34eec332b32e42ef3407e052d64ac625da6f
362 #   signature_str: f02f5bb22d8b32f14e88059a786379c26256892f45cf64770c844d0c5de2e52c00307b7bb25fcbb18be13c339a2f511a7c015a8cf81ac681052efe8e50eff00e
363 # test data 2:
364 #   xprv_str: 008ce51e3b52ee03eb0ad96c55eb5c9fe8736410518b585a0b7f35b2ab48d24c166364ce19322721b7dec84442c3665d97d0e995ba4d01c0f4b19b841379ac90
365 #   xpub_str: ead6415a077b91aa7de32e1cf63350f9351d0298f5accc2cf92ef9429bd1f86c166364ce19322721b7dec84442c3665d97d0e995ba4d01c0f4b19b841379ac90
366 #   message_str: 68656c6c6f206279746f6d      # value is: 'hello bytom'
367 #   signature_str: 1cc6b0f4031352ffd7a62540f13edddaaebf2df05db7a4926df5513129a8e85dcff1324545a024b16f958239ea67840ced3c2d57bb468dbf0e6cf1d1075f0b0f
368 # test data 3:
369 #   xprv_str: 88c0c40fb54ef9c1b90af8cce8dc4c9d54f915074dde93f79ab61cedae03444101ff37ac4a07869214c2735bba0175e001abe608db18538e083e1e44430a273b
370 #   xpub_str: cb22ce197d342d6bb440b0bf13ddd674f367275d28a00f893d7f0b10817690fd01ff37ac4a07869214c2735bba0175e001abe608db18538e083e1e44430a273b
371 #   message_str: 1246b84985e1ab5f83f4ec2bdf271114666fd3d9e24d12981a3c861b9ed523c6
372 #   signature_str: ab18f49b23d03295bc2a3f2a7d5bb53a2997bed733e1fc408b50ec834ae7e43f7da40fe5d9d50f6ef2d188e1d27f976aa2586cef1ba00dd098b5c9effa046306
373 def xpub_verify(xpub_str, message_str, signature_str):
374     result = False
375     result = verify(xpub_to_public_key(xpub_str), signature_str, message_str)
376
377     return result