forked from monero-project/mininero
-
Notifications
You must be signed in to change notification settings - Fork 0
/
mininero.py
337 lines (273 loc) · 9.56 KB
/
mininero.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
#"MiniNero" by Shen Noether mrl. Use at your own risk.
import hashlib #for signatures
import math
import Crypto.Random.random as rand
import Keccak #cn_fast_hash
import mnemonic #making 25 word mnemonic to remember your keys
import binascii #conversion between hex, int, and binary. Also for the crc32 thing
import ed25519 #Bernsteins python ed25519 code from cr.yp.to
import ed25519ietf # https://tools.ietf.org/html/draft-josefsson-eddsa-ed25519-02
import zlib
b = 256
q = 2**255 - 19
l = 2**252 + 27742317777372353535851937790883648493
def netVersion():
return "12"
def public_key(sk):
#returns point encoded to binary .. sk is just an int..
return ed25519.encodepoint(ed25519.scalarmultbase(sk)) #pub key is not just x coord..
def scalarmult_simple(pk, num):
#returns point encoded to hex.. num is an int, not a hex
return ed25519.encodepoint(ed25519.scalarmult(toPoint(pk), num)) #pub key is not just x coord..
def addKeys(P1, P2):
return binascii.hexlify(ed25519.encodepoint(ed25519.edwards(toPoint(P1), toPoint(P2))))
#aG + bB, G is basepoint..
def addKeys1(a, b, B):
return addKeys(scalarmultBase(a), scalarmultKey(B, b))
#aA + bB
def addKeys2(a, A, b, B):
return addKeys(scalarmultKey(A, a), scalarmultKey(B, b))
def subKeys(P1, P2):
return binascii.hexlify(ed25519.encodepoint(ed25519.edwards_Minus(toPoint(P1), toPoint(P2))))
def randomScalar():
tmp = rand.getrandbits(32 * 8) # 8 bits to a byte ...
return (tmp)
def xor(a, b):
return intToHex(hexToInt(a) ^ hexToInt(b))
def electrumChecksum(wordlist):
wl = wordlist.split(" ") #make an array
if len(wl) > 13:
wl = wl[:24]
else:
wl = wl[:12]
upl = 3 #prefix length
wl2 = ''
for a in wl:
wl2+= a[:upl]
z = ((zlib.crc32(wl2) & 0xffffffff) ^ 0xffffffff ) >> 0
z2 = ((z ^ 0xffffffff) >> 0) % len(wl)
return wl[z2]
__b58chars = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
__b58base = len(__b58chars)
def b58encode(v):
a = [reverseBytes(v[i:i+16]) for i in range(0, len(v)-16, 16)]
rr = -2*((len(v) /2 )% 16)
res = ''
for b in a:
bb = hexToInt(b)
result = ''
while bb >= __b58base:
div, mod = divmod(bb, __b58base)
result = __b58chars[mod] + result
bb = div
result = __b58chars[bb] + result
res += result
result = ''
if rr < 0:
bf = hexToInt(reverseBytes(v[rr:])) #since we only reversed the ones in the array..
result = ''
while bf >= __b58base:
div, mod = divmod(bf, __b58base)
result = __b58chars[mod] + result
bf = div
result = __b58chars[bf] + result
res += result
return res
def sc_0():
return intToHex(0)
def sc_reduce_key(a):
return intToHex(hexToInt(a) % l)
def sc_unreduce_key(a):
return intToHex(hexToInt(a) % l + l)
def sc_add_keys(a, b):
#adds two private keys mod l
return intToHex((hexToInt(a) + hexToInt(b)) % l)
def sc_add(a, exps):
#adds a vector of private keys mod l and multiplies them by an exponent
ssum = 0
for i in range(0, len(a)):
ssum = (ssum + 10 ** exps[i] * hexToInt(a[i])) % l
return intToHex(ssum)
def sc_check(a):
if hexToInt(a) % l == 0:
return False
return (a == sc_reduce_key(a))
def addScalars(a, b): #to do: remove above and rename to this (so that there is "add keys" and "add scalars")
#adds two private keys mod l
return intToHex((hexToInt(a) + hexToInt(b)) % l)
def sc_sub_keys(a, b):
#subtracts two private keys mod l
return intToHex((hexToInt(a) - hexToInt(b)) % l)
def sc_mul_keys(a, b):
return intToHex((hexToInt(a) * hexToInt(b)) % l)
def sc_sub_keys(a, b):
return intToHex((hexToInt(a) - hexToInt(b)) % l)
def sc_mulsub_keys(a, b, c):
#returns a - b * c (for use in LLW sigs - see MRL notes v 0.3)
return intToHex( (hexToInt(a)- hexToInt(b) * hexToInt(c)) % l)
def add_l(a, n):
return intToHex(hexToInt(a) +n * l )
def sc_muladd_keys(a, b, c):
#returns a + b * c (for use in LLW sigs - see MRL notes v 0.3)
return intToHex((hexToInt(a)+ hexToInt(b) * hexToInt(c) ) % l)
def mul_8(a):
return intToHex(8 * hexToInt(a))
def mul_8key(a):
return scalarmultKey(a, 8)
def fe_reduce_key(a):
return intToHex(hexToInt(a) % q)
def recoverSK(seed):
mn2 = seed.split(" ") #make array
if len(mn2) > 13:
mn2 = mn2[:24]
sk = mnemonic.mn_decode(mn2)
else:
mn2 = mn2[:12]
#mn2 += mn2[:]
sk = cn_fast_hash(mnemonic.mn_decode(mn2))
#sk = mnemonic.mn_decode(mn2)
return sk
def cn_fast_hash(s):
k = Keccak.Keccak()
return k.Keccak((len(s) * 4, s), 1088, 512, 32 * 8, False).lower() #r = bitrate = 1088, c = capacity, n = output length in bits
def getView(sk):
a = hexToInt(cn_fast_hash(sc_reduce_key(sk))) % l
return intToHex(a)
def getViewMM(sk):
a = hexToInt(cn_fast_hash(sk))
return intToHex(a)
def reverseBytes(a): #input is byte string, it reverse the endianness
b = [a[i:i+2] for i in range(0, len(a)-1, 2)]
return ''.join(b[::-1])
def encode_addr(version, spendP, viewP):
buf = version + spendP + viewP
h = cn_fast_hash(buf)
buf = buf + h[0:8]
return b58encode(buf)
def hexToInt(h):
s = binascii.unhexlify(h) #does hex to bytes
bb = len(h) * 4 #I guess 8 bits / b
return sum(2**i * ed25519.bit(s,i) for i in range(0,bb)) #does to int
def intToHex(i):
return binascii.hexlify(ed25519.encodeint(i)) #hexlify does bytes to hex
def publicFromSecret(sk):
#returns pubkey in hex, same as scalarmultBase
return binascii.hexlify(public_key(hexToInt(sk)))
def scalarmultBase(sk):
#returns pubkey in hex, expects hex sk
return binascii.hexlify(public_key(hexToInt(sk)))
def identity():
return scalarmultBase(intToHex(0))
def scalarmultKey(pk, num):
return binascii.hexlify(scalarmult_simple(pk, hexToInt(num)))
def scalarmultKeyInt(pk, num):
return binascii.hexlify(scalarmult_simple(pk, num))
def publicFromInt(i):
#returns pubkey in hex, same as scalarmultBase.. should just pick one
return binascii.hexlify(public_key(i))
def toPoint(hexVal):
aa = binascii.unhexlify(hexVal) #to binary (new)
return ed25519.decodepoint(aa) #make to point
def toPointCheck(hexVal):
aa = binascii.unhexlify(hexVal) #to binary (new)
return ed25519.decodepointcheck(aa) #make to point
def fromPoint(aa): #supposed to reverse toPoint
binvalue = ed25519.encodepoint(aa)
return binascii.hexlify(binvalue)
def basePoint():
#P = ed25519.scalarmultbase(1)
#PP = fromPoint(P)
P = ed25519ietf.basepoint()
PP = ed25519ietf.point_compress(P)
print(PP)
return PP
def hashToPointCN(hexVal):
u= hexToInt(cn_fast_hash(hexVal)) % q
A = 486662
ma = -1 * A % q
ma2 = -1 * A * A % q
sqrtm1 = ed25519.sqroot(-1)
d = ed25519.theD() #print(radix255(d))
fffb1 = -1 * ed25519.sqroot(-2 * A * (A + 2) )
#print("fffb1", ed25519.radix255(fffb1))
fffb2 = -1 * ed25519.sqroot(2 * A * (A + 2) )
#print("fffb2", ed25519.radix255(fffb2))
fffb3 = ed25519.sqroot( -1 * sqrtm1 * A * (A + 2))
#print("fffb3", ed25519.radix255(fffb3))
fffb4 = -1 * ed25519.sqroot( sqrtm1 * A * (A + 2))
#print("fffb4", ed25519.radix255(fffb4))
w = (2 * u * u + 1) % q
xp = (w * w - 2 * A * A * u * u) % q
#like sqrt (w / x) although may have to check signs..
#so, note that if a squareroot exists, then clearly a square exists..
rx = ed25519.expmod(w * ed25519.inv(xp),(q+3)/8,q)
#rx is ok.
x = rx * rx * (w * w - 2 * A * A * u * u) % q
y = (2 * u * u + 1 - x) % q #w - x, if y is zero, then x = w
negative = False
if (y != 0):
y = (w + x) % q #checking if you got the negative square root.
if (y != 0) :
negative = True
else :
rx = rx * -1 * ed25519.sqroot(-2 * A * (A + 2) ) % q
negative = False
else :
#y was 0..
rx = (rx * -1 * ed25519.sqroot(2 * A * (A + 2) ) ) % q
if not negative:
rx = (rx * u) % q
z = (-2 * A * u * u) % q
sign = 0
else:
z = -1 * A
x = x * sqrtm1 % q #..
y = (w - x) % q
if (y != 0) :
rx = rx * ed25519.sqroot( -1 * sqrtm1 * A * (A + 2)) % q
else :
rx = rx * -1 * ed25519.sqroot( sqrtm1 * A * (A + 2)) % q
sign = 1
#setsign
if ( (rx % 2) != sign ):
rx = - (rx) % q
rz = (z + w) % q
ry = (z - w) % q
rx = rx * rz % q
P = ed25519ietf.point_compress([rx, ry, rz])
P8 = mul8(P)
toPointCheck(P)
return P8
def mul8(point):
return binascii.hexlify(scalarmult_simple(point, 8))
#a simple hash function I was using to test C.T. stuff
#for the actual one, see the function just previous to this
def hashToPoint_ct(hexVal):
#however there is an alternative which will work for C.T.
#returns a hex string, not a point
a = hexVal[:]
i = 0
while True:
worked = 1
a = cn_fast_hash(a)
i += 1
try:
toPoint(a)
except:
worked = 0
if worked == 1:
break
print("found point after "+str(i)+" hashes")
return mul8(a) # needs to be in group of basepoint
def getAddrMM(sk):
vk = getViewMM(sk)
sk = sc_reduce_key(sk)
pk = publicFromSecret(sk)
pvk = publicFromSecret(vk)
return encode_addr(netVersion(), pk, pvk)
def getAddr(sk):
vk = getView(sk)
sk = sc_reduce_key(sk)
pk = publicFromSecret(sk)
pvk = publicFromSecret(vk)
return encode_addr(netVersion(), pk, pvk)