From 767ddb8cded07b6574133a4f3aa9ef8040a47484 Mon Sep 17 00:00:00 2001 From: Riccardo Spagni Date: Thu, 4 Feb 2016 17:44:26 +0200 Subject: [PATCH] forgot to add the source folder, my bad --- source-code/mininero-original/Keccak.py | 354 ++++++++++ source-code/mininero-original/README.md | 54 ++ source-code/mininero-original/mininero.py | 752 ++++++++++++++++++++++ 3 files changed, 1160 insertions(+) create mode 100644 source-code/mininero-original/Keccak.py create mode 100644 source-code/mininero-original/README.md create mode 100644 source-code/mininero-original/mininero.py diff --git a/source-code/mininero-original/Keccak.py b/source-code/mininero-original/Keccak.py new file mode 100644 index 0000000..cc79448 --- /dev/null +++ b/source-code/mininero-original/Keccak.py @@ -0,0 +1,354 @@ +#! /usr/bin/pythonw +# The Keccak sponge function, designed by Guido Bertoni, Joan Daemen, +# questions, please refer to our website: http://keccak.noekeon.org/ +# +# Implementation by Renaud Bauvin, +# hereby denoted as "the implementer". +# +# To the extent possible under law, the implementer has waived all copyright +# and related or neighboring rights to the source code in this file. +# http://creativecommons.org/publicdomain/zero/1.0/ + +import math + +class KeccakError(Exception): + """Class of error used in the Keccak implementation + + Use: raise KeccakError.KeccakError("Text to be displayed")""" + + def __init__(self, value): + self.value = value + def __str__(self): + return repr(self.value) + + +class Keccak: + """ + Class implementing the Keccak sponge function + """ + def __init__(self, b=1600): + """Constructor: + + b: parameter b, must be 25, 50, 100, 200, 400, 800 or 1600 (default value)""" + self.setB(b) + + def setB(self,b): + """Set the value of the parameter b (and thus w,l and nr) + + b: parameter b, must be choosen among [25, 50, 100, 200, 400, 800, 1600] + """ + + if b not in [25, 50, 100, 200, 400, 800, 1600]: + raise KeccakError.KeccakError('b value not supported - use 25, 50, 100, 200, 400, 800 or 1600') + + # Update all the parameters based on the used value of b + self.b=b + self.w=b//25 + self.l=int(math.log(self.w,2)) + self.nr=12+2*self.l + + # Constants + + ## Round constants + RC=[0x0000000000000001, + 0x0000000000008082, + 0x800000000000808A, + 0x8000000080008000, + 0x000000000000808B, + 0x0000000080000001, + 0x8000000080008081, + 0x8000000000008009, + 0x000000000000008A, + 0x0000000000000088, + 0x0000000080008009, + 0x000000008000000A, + 0x000000008000808B, + 0x800000000000008B, + 0x8000000000008089, + 0x8000000000008003, + 0x8000000000008002, + 0x8000000000000080, + 0x000000000000800A, + 0x800000008000000A, + 0x8000000080008081, + 0x8000000000008080, + 0x0000000080000001, + 0x8000000080008008] + + ## Rotation offsets + r=[[0, 36, 3, 41, 18] , + [1, 44, 10, 45, 2] , + [62, 6, 43, 15, 61] , + [28, 55, 25, 21, 56] , + [27, 20, 39, 8, 14] ] + + ## Generic utility functions + + def rot(self,x,n): + """Bitwise rotation (to the left) of n bits considering the \ + string of bits is w bits long""" + + n = n%self.w + return ((x>>(self.w-n))+(x< Table (and vice-versa) + + def convertStrToTable(self,string): + """Convert a string of bytes to its 5x5 matrix representation + + string: string of bytes of hex-coded bytes (e.g. '9A2C...')""" + + #Check that input paramaters + if self.w%8!= 0: + raise KeccakError("w is not a multiple of 8") + if len(string)!=2*(self.b)//8: + raise KeccakError.KeccakError("string can't be divided in 25 blocks of w bits\ + i.e. string must have exactly b bits") + + #Convert + output=[[0,0,0,0,0], + [0,0,0,0,0], + [0,0,0,0,0], + [0,0,0,0,0], + [0,0,0,0,0]] + for x in range(5): + for y in range(5): + offset=2*((5*y+x)*self.w)//8 + output[x][y]=self.fromHexStringToLane(string[offset:offset+(2*self.w//8)]) + return output + + def convertTableToStr(self,table): + """convert 5x5 matrix to string""" + + #Check input format + if self.w%8!= 0: + raise KeccakError.KeccakError("w is not a multiple of 8") + if (len(table)!=5) or (False in [len(row)==5 for row in table]): + raise KeccakError.KeccakError("table must be 5x5") + + #Convert + output=['']*25 + for x in range(5): + for y in range(5): + output[5*y+x]=self.fromLaneToHexString(table[x][y]) + output =''.join(output).upper() + return output + + def Round(self,A,RCfixed): + """Perform one round of computation as defined in the Keccak-f permutation + + A: current state (5x5 matrix) + RCfixed: value of round constant to use (integer) + """ + + #Initialisation of temporary variables + B=[[0,0,0,0,0], + [0,0,0,0,0], + [0,0,0,0,0], + [0,0,0,0,0], + [0,0,0,0,0]] + C= [0,0,0,0,0] + D= [0,0,0,0,0] + + #Theta step + for x in range(5): + C[x] = A[x][0]^A[x][1]^A[x][2]^A[x][3]^A[x][4] + + for x in range(5): + D[x] = C[(x-1)%5]^self.rot(C[(x+1)%5],1) + + for x in range(5): + for y in range(5): + A[x][y] = A[x][y]^D[x] + + #Rho and Pi steps + for x in range(5): + for y in range(5): + B[y][(2*x+3*y)%5] = self.rot(A[x][y], self.r[x][y]) + + #Chi step + for x in range(5): + for y in range(5): + A[x][y] = B[x][y]^((~B[(x+1)%5][y]) & B[(x+2)%5][y]) + + #Iota step + A[0][0] = A[0][0]^RCfixed + + return A + + def KeccakF(self,A, verbose=False): + """Perform Keccak-f function on the state A + + A: 5x5 matrix containing the state + verbose: a boolean flag activating the printing of intermediate computations + """ + + if verbose: + self.printState(A,"Before first round") + + for i in range(self.nr): + #NB: result is truncated to lane size + A = self.Round(A,self.RC[i]%(1<(len(my_string)//2*8): + raise KeccakError.KeccakError("the string is too short to contain the number of bits announced") + + nr_bytes_filled=my_string_length//8 + nbr_bits_filled=my_string_length%8 + l = my_string_length % n + if ((n-8) <= l <= (n-2)): + if (nbr_bits_filled == 0): + my_byte = 0 + else: + my_byte=int(my_string[nr_bytes_filled*2:nr_bytes_filled*2+2],16) + my_byte=(my_byte>>(8-nbr_bits_filled)) + my_byte=my_byte+2**(nbr_bits_filled)+2**7 + my_byte="%02X" % my_byte + my_string=my_string[0:nr_bytes_filled*2]+my_byte + else: + if (nbr_bits_filled == 0): + my_byte = 0 + else: + my_byte=int(my_string[nr_bytes_filled*2:nr_bytes_filled*2+2],16) + my_byte=(my_byte>>(8-nbr_bits_filled)) + my_byte=my_byte+2**(nbr_bits_filled) + my_byte="%02X" % my_byte + my_string=my_string[0:nr_bytes_filled*2]+my_byte + while((8*len(my_string)//2)%n < (n-8)): + my_string=my_string+'00' + my_string = my_string+'80' + + return my_string + + def Keccak(self,M,r=1024,c=576,n=1024,verbose=False): + """Compute the Keccak[r,c,d] sponge function on message M + + M: message pair (length in bits, string of hex characters ('9AFC...') + r: bitrate in bits (defautl: 1024) + c: capacity in bits (default: 576) + n: length of output in bits (default: 1024), + verbose: print the details of computations(default:False) + """ + + #Check the inputs + if (r<0) or (r%8!=0): + raise KeccakError.KeccakError('r must be a multiple of 8 in this implementation') + if (n%8!=0): + raise KeccakError.KeccakError('outputLength must be a multiple of 8') + self.setB(r+c) + + if verbose: + print("Create a Keccak function with (r=%d, c=%d (i.e. w=%d))" % (r,c,(r+c)//25)) + + #Compute lane length (in bits) + w=(r+c)//25 + + # Initialisation of state + S=[[0,0,0,0,0], + [0,0,0,0,0], + [0,0,0,0,0], + [0,0,0,0,0], + [0,0,0,0,0]] + + #Padding of messages + P = self.pad10star1(M, r) + + if verbose: + print("String ready to be absorbed: %s (will be completed by %d x '00')" % (P, c//8)) + + #Absorbing phase + for i in range((len(P)*8//2)//r): + Pi=self.convertStrToTable(P[i*(2*r//8):(i+1)*(2*r//8)]+'00'*(c//8)) + + for y in range(5): + for x in range(5): + S[x][y] = S[x][y]^Pi[x][y] + S = self.KeccakF(S, verbose) + + if verbose: + print("Value after absorption : %s" % (self.convertTableToStr(S))) + + #Squeezing phase + Z = '' + outputLength = n + while outputLength>0: + string=self.convertTableToStr(S) + Z = Z + string[:r*2//8] + outputLength -= r + if outputLength>0: + S = self.KeccakF(S, verbose) + + # NB: done by block of length r, could have to be cut if outputLength + # is not a multiple of r + + if verbose: + print("Value after squeezing : %s" % (self.convertTableToStr(S))) + + return Z[:2*n//8] diff --git a/source-code/mininero-original/README.md b/source-code/mininero-original/README.md new file mode 100644 index 0000000..ea529ed --- /dev/null +++ b/source-code/mininero-original/README.md @@ -0,0 +1,54 @@ +# MiniNero + +Copyright (c) 2014, The Monero Project + +## About this Project + +MiniNero is a reimplementation of CryptoNote one-time ring signatures in Python, and is roughly analogous with the way they are implemented in Monero. The main source that was used for creating MiniNero are the functions contained in crypto.cpp and crypto-ops.cpp in Monero. MiniNero produces working and valid ring signatures, although they differ slightly from the Monero ring signatures due to hashing and packing differences between the libraries used. + +## License + +Copyright (c) 2014, The Monero Project + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Parts of the project are originally copyright (c) 2012-2013 The Cryptonote developers + +## Using MiniNero + +Dependencies: Python 2.7+, PyCrypto 2.6+ + +Usage: ```python mininero.py ``` + +The following commands are available: + +* rs - demos MiniNero the random_scalar function +* keys - demos MiniNero key generation +* fasthash - demos MiniNero fast hash +* hashscalar - demos MiniNero H_s(P) equivalent function +* hashcurve - demos MiniNero H_p(P) +* checkkey - demos MiniNero check_key function +* secpub - demos turning a fixed secret to a public key +* keyder - demos MiniNero Key Derivation +* dersca - demos MiniNero derivation to scalar +* derpub - demos derive public key +* dersec - demos derive secret key +* testcomm - tests the helper struct for some CN functions +* gensig - testing generate_signature +* checksig - tests checking signature +* keyimage - tests creating I=xH_p(P) +* ringsig - tests creating and checking a ringsig +* conv - tests some helper conversion functions +* red - tests some of the sc_Code +* gedb - tests some of the edwards curve functions +* sck - tests sc_check \ No newline at end of file diff --git a/source-code/mininero-original/mininero.py b/source-code/mininero-original/mininero.py new file mode 100644 index 0000000..2d0c6cf --- /dev/null +++ b/source-code/mininero-original/mininero.py @@ -0,0 +1,752 @@ +# Copyright (c) 2014, The Monero Project +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, are +# permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of +# conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, this list +# of conditions and the following disclaimer in the documentation and/or other +# materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors may be +# used to endorse or promote products derived from this software without specific +# prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +# THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import hashlib +import struct +import base64 +import binascii +import sys +from Crypto.Util import number +import Crypto.Random.random as rand +import Keccak +from collections import namedtuple +import copy + +KEK=Keccak.Keccak(1600) +CURVE_P = (2**255 - 19) +b = 256 +q = 2**255 - 19 +l = 2**252 + 27742317777372353535851937790883648493 +BASEPOINT = "0900000000000000000000000000000000000000000000000000000000000000" + +##################################### +#Bernstein(?) Eddie Library in python +##################################### + +def H(m): + return hashlib.sha512(m).digest() + +def expmod(b,e,m): + if e == 0: return 1 + t = expmod(b,e/2,m)**2 % m + if e & 1: t = (t*b) % m + return t + +def inv(x): + return expmod(x,q-2,q) + +d = -121665 * inv(121666) +I = expmod(2,(q-1)/4,q) + +def xrecover(y): + xx = (y*y-1) * inv(d*y*y+1) + x = expmod(xx,(q+3)/8,q) + if (x*x - xx) % q != 0: x = (x*I) % q + if x % 2 != 0: x = q-x + return x + +By = 4 * inv(5) +Bx = xrecover(By) +B = [Bx % q,By % q] + +def edwards(P,Q): + x1 = P[0] + y1 = P[1] + x2 = Q[0] + y2 = Q[1] + x3 = (x1*y2+x2*y1) * inv(1+d*x1*x2*y1*y2) + y3 = (y1*y2+x1*x2) * inv(1-d*x1*x2*y1*y2) + return [x3 % q,y3 % q] + +def scalarmult(P, e): + if e == 0: return [0,1] + Q = scalarmult(P,e/2) + Q = edwards(Q,Q) + if e & 1: Q = edwards(Q,P) + return Q + +def encodeint(y): + bits = [(y >> i) & 1 for i in range(b)] + return ''.join([chr(sum([bits[i * 8 + j] << j for j in range(8)])) for i in range(b/8)]) + +def encodepoint(P): + x = P[0] + y = P[1] + bits = [(y >> i) & 1 for i in range(b - 1)] + [x & 1] + return ''.join([chr(sum([bits[i * 8 + j] << j for j in range(8)])) for i in range(b/8)]) + +def bit(h,i): + return (ord(h[i/8]) >> (i%8)) & 1 + +def public_key(sk): + A = scalarmult(B,sk) + return encodepoint(A) + + +def Hint(m): + h = H(m) + return sum(2**i * bit(h,i) for i in range(2*b)) + +def signature(m,sk,pk): + h = H(sk) + a = 2**(b-2) + sum(2**i * bit(h,i) for i in range(3,b-2)) + r = Hint(''.join([h[i] for i in range(b/8,b/4)]) + m) + R = scalarmult(B,r) + S = (r + Hint(encodepoint(R) + pk + m) * a) % l + return encodepoint(R) + encodeint(S) + +def isoncurve(P): + x = P[0] + y = P[1] + return (-x*x + y*y - 1 - d*x*x*y*y) % q == 0 + +def decodeint(s): + return sum(2**i * bit(s,i) for i in range(0,b)) + +def decodepoint(s): + y = sum(2**i * bit(s,i) for i in range(0,b-1)) + x = xrecover(y) + if x & 1 != bit(s,b-1): x = q-x + P = [x,y] + if not isoncurve(P): raise Exception("decoding point that is not on curve") + return P + +def checkvalid(s,m,pk): + if len(s) != b/4: raise Exception("signature length is wrong") + if len(pk) != b/8: raise Exception("public-key length is wrong") + R = decodepoint(s[0:b/8]) + A = decodepoint(pk) + S = decodeint(s[b/8:b/4]) + h = Hint(encodepoint(R) + pk + m) + if scalarmult(B,S) != edwards(R,scalarmult(A,h)): + raise Exception("signature does not pass verification") + +################################# +#curve stuff, +#mostly from https://github.com/monero-project/bitmonero/blob/1b8a68f6c1abcf481652c2cfd87300a128e3eb32/src/crypto/crypto-ops.c +#partial reference for fe things https://godoc.org/github.com/agl/ed25519/edwards25519 +#note ge is the edwards version of the curve +#fe is the monty version of the curve +################################# + + +#NOT USED IN MININERO - Use ge_scalarmult_base +def ge_fromfe_frombytesvartime(s): + #inputs something s (I assume in bytes) + #inputs into montgomery form (fe) + #then, turns it into edwards form (ge) + #then r is the edwards curve point r-> + #reference 1: http://crypto.stackexchange.com/questions/9536/converting-ed25519-public-key-to-a-curve25519-public-key?rq=1 + #reference 2: https://github.com/orlp/ed25519/blob/master/src/key_exchange.c + #best reference https://www.imperialviolet.org/2013/12/25/elligator.html + + #the point of this function is to return a ge_p2 from an int s + #whereas, the similar function ge_frombytes_vartime returns a gep3 + return + +def ge_double_scalarmult_base_vartime(aa, AA, bb): + #a very nice comment in the CN code for this one! + #r = a * A + b * B + #where a = a[0]+256*a[1]+...+256^31 a[31]. + #and b = b[0]+256*b[1]+...+256^31 b[31]. + #B is the Ed25519 base point (x,4/5) with x positive. + #cf also https://godoc.org/github.com/agl/ed25519/edwards25519 + tmpa = ge_scalarmult(aa, AA) + tmpb = ge_scalarmult(bb, BASEPOINT) + return toHex(edwards(toPoint(tmpa), toPoint(tmpb))) + +def ge_double_scalarmult_vartime(aa, AA, bb, BB): + #a very nice comment in the CN code for this one! + #r = a * A + b * B + #where a = a[0]+256*a[1]+...+256^31 a[31]. + #and b = b[0]+256*b[1]+...+256^31 b[31]. + #B is the Ed25519 base point (x,4/5) with x positive. + #cf also https://godoc.org/github.com/agl/ed25519/edwards25519 + tmpa = ge_scalarmult(aa, AA) + tmpb = ge_scalarmult(bb, BB) + return toHex(edwards(toPoint(tmpa), toPoint(tmpb))) + + +def toPoint(pubkey): + #turns hex key into x, y field coords + return decodepoint(pubkey.decode("hex")) + +def toHex(point): + #turns point into pubkey (reverse of toPoint) + return encodepoint(point).encode("hex") + +def ge_scalarmult(a, A): + #so I guess given any point A, and an integer a, this computes aA + #so the seecond arguement is definitely an EC point + # from http://cr.yp.to/highspeed/naclcrypto-20090310.pdf + # "Alice's secret key a is a uniform random 32-byte string then + #clampC(a) is a uniform random Curve25519 secret key + #i.e. n, where n/8 is a uniform random integer between + #2^251 and 2^252-1 + #Alice's public key is n/Q compressed to the x-coordinate + #so that means, ge_scalarmult is not actually doing scalar mult + #clamping makes the secret be between 2^251 and 2^252 + #and should really be done + #print(toPoint(A)) + return encodepoint(scalarmult(toPoint(A), a)).encode("hex") # now using the eddie function + +def ge_scalarmult_base(a): + #in this function in the original code, they've assumed it's already clamped ... + #c.f. also https://godoc.org/github.com/agl/ed25519/edwards25519 + #it will return h = a*B, where B is ed25519 bp (x,4/5) + #and a = a[0] + 256a[1] + ... + 256^31 a[31] + #it assumes that a[31 <= 127 already + return ge_scalarmult(8*a, BASEPOINT) + +#NOT USED IN MININERO - use ge_scalarmult_base +def ge_frombytes_vartime(key): + #https://www.imperialviolet.org/2013/12/25/elligator.html + #basically it takes some bytes of data + #converts to a point on the edwards curve + #if the bytes aren't on the curve + #also does some checking on the numbers + #ex. your secret key has to be at least >=4294967277 + #also it rejects certain curve points, i.e. "if x = 0, sign must be positive + return 0 + +#NOT USED IN MININERO - unecessary as all operations are from hex +def ge_p1p1_to_p2(p): + #there are two ways of representing the points + ##http://code.metager.de/source/xref/lib/nacl/20110221/crypto_sign/edwards25519sha512batch/ref/ge25519.c + #http://www.hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html + return + +#NOT USED IN MININERO -unnecessary as operations are from hex +def ge_p2_dbl(): + #basically it doubles a point and doubles it + #c.f. Explicit Formulas for Doubling (towards bottom) + #Explicit formulas for doubling + #http://www.hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html + return + +#NOT USED IN MININERO - unnecessary as operations are from hex +def ge_p3_to_p2(): + #basically, it copies a point in 3 coordinates to another point + #c.f. Explicit Formulas for Doubling (towards bottom) + #Explicit formulas for doubling + #http://www.hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html + return + +def ge_mul8(P): + #ok, the point of this is to double three times + #and the point is that the ge_p2_dbl returns a point in the p1p1 form + #so that's why have to convert it first and then double + return ge_scalarmult(8, P) + +def sc_reduce(s): + #inputs a 64 byte int and outputs the lowest 32 bytes + #used by hash_to_scalar, which turns cn_fast_hash to number.. + r = longToHex(s) + r = r[64::] + #print("before mod p", r) + return hexToLong(r) % CURVE_P + +def sc_reduce32(data): + #ok, the code here is exactly the same as sc_reduce + #(which is default lib sodium) + #except it is assumed that your input + #s is alread in the form: + # s[0]+256*s[1]+...+256^31*s[31] = s + #and the rest is just reducing mod l + #so basically take a 32 byte input, and reduce modulo the prime + return data % CURVE_P + +def sc_mulsub(a, b, c): + #takes in a, b, and c + #This is used by the regular sig + #i.e. in generate_signature + #returns c-ab mod l + a = number.bytes_to_long(a[::-1]) + b = number.bytes_to_long(b[::-1]) + c = number.bytes_to_long(c[::-1]) + return (c - a * b) % CURVE_P + + +########################################## +#Hashing +#this is where keccak, H_p, and H_s come in.. +###################################### + +def cn_fast_hash(key, size): + #see ReadMeKeccak.txt + return KEK.Keccak((size,key.encode("hex")),1088,512,256,False) + + +################################################### +#CryptoNote Things +#Mainly from https://github.com/monero-project/bitmonero/blob/1b8a68f6c1abcf481652c2cfd87300a128e3eb32/src/crypto/crypto.cpp +################################################### + +def random_scalar(): + #Gets a random scalar mod q and 32 bytes + tmp = rand.getrandbits(64 * 8) # 8 bits to a byte ... + tmp = sc_reduce(tmp) #-> turns 64 to 32 (note sure why don't just gt 32 in first place ... ) + return tmp + +def hash_to_scalar(data, length): + #this one is H_s(P) + #relies on cn_fast_hash and sc_reduce32 (which makes an int smaller) + #the input here is not necessarily a 64 byte thing, and that's why sc_reduce32 + res = hexToLong(cn_fast_hash(data, length)) + return sc_reduce32(res) + + +def generate_keys(): + #should return a secret key and public key pair + #once you have the secret key, + #then the public key be gotten from 25519 function + #so just need to generate random + #first generate random 32-byte(256 bit) integer, copy to result + #ok, just sc_reduce, what that does is takes 64 byte int, turns into 32 byte int... + #so sc_reduce is legit and comes from another library http://hackage.haskell.org/package/ed25519-0.0.2.0/src/src/cbits/sc_reduce.c + #as far as I can tell, sc + #basically this gets you an int which is sufficiently large + #import Crypto.Random.random as rand + rng = random_scalar() + #sec = hex(rng).rstrip("L").lstrip("0x") or "0" + sec = sc_reduce32(rng) + pub = public_key(sec).encode("hex") + #pub = ge_scalarmult_base(sec) + #print(rng.decode("hex")) + #sec = curve25519_mult(rng, basepoint) + + #the point of ge_p3_tobytes here is just store as bytes... + #and p3 is a way to store points on the ge curve + return sec, pub + +def check_key(key): + #inputs a public key, and outputs if point is on the curve + return isoncurve(toPoint(key)) + +def secret_key_to_public_key(secret_key): + #turns a secret key to a public key + #the actual function returns as bytes since they mult the fast way. + if sc_check(secret_key) != 0: + print "error in sc_check" + quit() + return public_key(secret_key) + +def hash_to_ec(key): + #takes a hash and turns into a point on the curve + #In MININERO, I'm not using the byte representation + #So this function is superfluous + h = hash_to_scalar(key, len(key)) + point = ge_scalarmult_base(h) + return ge_mul8(point) + + +def generate_key_image(public_key, secret_key): + #should return a key image as defined in whitepaper + if sc_check(secret_key) != 0: + print"sc check error in key image" + point = hash_to_ec(public_key) + point2 = ge_scalarmult(secret_key, point) + return point2 + +def generate_ring_signature(prefix, image, pubs, pubs_count, sec, sec_index): + #returns a ring signature + #prefix is whatever message + #image is key image I = xH(P) + #pubs is [pub1, pub2, pub3] + #pubs_count is number in pubs (ex 3) + #sec is your secret key x + #sec_index is which of the pubs is your pub + if sec_index >= pubs_count: + print "bad index of secret key!" + quit() + if ge_frombytes_vartime(image) != 0: + print"bad image!" + quit() + summ = 0 + aba = [0 for xx in range(pubs_count)] + abb = [0 for xx in range(pubs_count)] + sigc = [0 for xx in range(pubs_count)] #these are the c[i]'s from the whitepaper + sigr =[0 for xx in range(pubs_count)] #these are the r[i]'s from the whitepaper + for ii in range(0, pubs_count): + if (ii == sec_index): + kk = random_scalar() + tmp3 = ge_scalarmult_base(kk) #L[i] for i = s + aba[ii] = tmp3 + tmp3 = hash_to_ec(pubs[ii]) #R[i] for i = s + abb[ii] = ge_scalarmult(kk, tmp3) + else: + k1 = random_scalar() #note this generates a random scalar in the correct range... + k2 = random_scalar() + if ge_frombytes_vartime(pubs[ii]) != 0: + print "error in ring sig!!!" + quit() + tmp2 = ge_double_scalarmult_base_vartime(k1, pubs[ii], k2) #this is L[i] for i != s + aba[ii] = tmp2 + tmp3 = hash_to_ec(pubs[ii]) + abb[ii] = ge_double_scalarmult_vartime(k2, tmp3, k1, image) #R[i] for i != s + sigc[ii] = k1 #the random c[i] for i != s + sigr[ii] = k2 #the random r[i] for i != s + summ = sc_add(summ, sigc[ii]) #summing the c[i] to get the c[s] via page 9 whitepaper + + buf = struct.pack('64s', prefix) + for ii in range(0, pubs_count): + buf += struct.pack('64s', aba[ii]) + buf += struct.pack('64s', abb[ii]) + hh = hash_to_scalar(buf,len(buf)) + sigc[sec_index] = sc_sub(hh, summ) # c[s] = hash - sum c[i] mod l + sigr[sec_index] = sc_mulsub(sigc[sec_index], sec, kk) # r[s] = q[s] - sec * c[index] + return image, sigc, sigr + + + + + + +def check_ring_signature(prefix, key_image, pubs, pubs_count, sigr, sigc): + #from https://github.com/monero-project/bitmonero/blob/6a70de32bf872d97f9eebc7564f1ee41ff149c36/src/crypto/crypto.cpp + #this is the "ver" algorithm from CN + #takes the prefix (message) + #key_image is I= xH(P) + #pubs is like [pub1, pub2, pub3] + #pubs_count is length of above list (3 in this case) + #sigr is list of the r-values in the ring sig [r[0], r[1], ...] + #sigc is a list of the c-values in the ring sig [c[0], c[1], ...] + aba = [0 for xx in range(pubs_count)] + abb = [0 for xx in range(pubs_count)] + + if ge_frombytes_vartime(key_image) != 0: + print "ring image error in checking sigs" + quit() + summ = 0 + buf = struct.pack('64s', prefix) + for ii in range(0, pubs_count): + if ((sc_check(sigc[ii]) != 0) or (sc_check(sigr[ii]) != 0)): + print "failed sc_check in check ring sigs" + quit() + if ge_frombytes_vartime(pubs[ii]) != 0: + print "public key is a bad point in ring sigs" + quit() + + tmp2 = ge_double_scalarmult_base_vartime(sigc[ii], pubs[ii], sigr[ii]) + aba[ii] = tmp2 + tmp3 = hash_to_ec(pubs[ii]) + tmp2 = ge_double_scalarmult_vartime(sigr[ii], tmp3, sigc[ii], key_image) + abb[ii] = tmp2 + summ = sc_add(summ, sigc[ii]) + for ii in range(0, pubs_count): + buf += struct.pack('64s', aba[ii]) + buf += struct.pack('64s', abb[ii]) + + hh = hash_to_scalar(buf,len(buf)) + hh = sc_sub(hh, summ) + return sc_isnonzero(hh) == 0 + +def generate_key_derivation(key1, key2): + #key1 is public key of receiver Bob (see page 7) + #key2 is Alice's private + #this is a helper function for the key-derivation + #which is the generating one-time key's thingy + if sc_check(key2) != 0: + #checks that the secret key is uniform enough... + print"error in sc_check in keyder" + quit() + if ge_frombytes_vartime(key1) != 0: + print "didn't pass curve checks in keyder" + quit() + + point = key1 ## this ones the public + point2 = ge_scalarmult( key2, point) + #print("p2", encodepoint(point2).encode("hex")) + point3 = ge_mul8(point2) #This has to do with n==0 mod 8 by dedfinition, c.f. the top paragraph of page 5 of http://cr.yp.to/ecdh/curve25519-20060209.pdf + #and also c.f. middle of page 8 in same document (Bernstein) + return point3 + +def derivation_to_scalar(derivation, output_index): + #this function specifically hashes your + #output index (for the one time keys ) + #in order to get an int, so we can do ge_mult_scalar + #buf = s_comm(d = derivation, o = output_index) + buf2 = struct.pack('64sl', derivation, output_index) + #print(buf2) + return hash_to_scalar(buf2, len(buf2)) + +def derive_public_key(derivation, output_index, base ): + if ge_frombytes_vartime(base) != 0: #check some conditions on the point + print"derive pub key bad point" + quit() + point1 = base + scalar = derivation_to_scalar(derivation, output_index) + point2 = ge_scalarmult_base(scalar) + point3 = point2 #I think the cached is just for the sake of adding + #because the CN code adds using the monty curve + point4 = edwards(toPoint(point1), toPoint(point3)) + return point4 + +def sc_add(aa, bb): + return (aa + bb ) %CURVE_P +def sc_sub(aa, bb): + return (aa - bb ) %CURVE_P + +def sc_isnonzero(c): + return (c %CURVE_P != 0 ) + +def sc_mulsub(aa, bb, cc): + return (cc - aa * bb ) %CURVE_P + +def derive_secret_key(derivation, output_index, base): + #outputs a derived key... + if sc_check(base) !=0: + print"cs_check in derive_secret_key" + scalar = derivation_to_scalar(derivation, output_index) + return base + scalar + +class s_comm: + #this is a helper struct in the original CN code + #didn't actually end up using it here.. + #though originally I was doing so. + def __init__(self, **kwds): + self.__dict__.update(kwds) + +def generate_signature(prefix_hash, pub, sec): + #gets the "usual" signature (not ring sig) + #buf = s_comm(h=prefix_hash, key=pub, comm=0) #see the pack below + k = random_scalar() + tmp3 = ge_scalarmult_base(k) + buf2 = struct.pack('64s64s64s', prefix_hash, pub, tmp3) + sigc = hash_to_scalar(buf2, len(buf2)) + return sc_mulsub(sigc, sec, k), sigc + +def check_signature(prefix_hash, pub, sigr, sigc): + #checking the normal sigs, not the ring sigs... + if ge_frombytes_vartime(pub) !=0: + print "bad point, check sig!" + quit() + if (sc_check(sigc) != 0) or (sc_check(sigr) != 0): + print"sc checksig error!" + quit() + tmp2 = ge_double_scalarmult_base_vartime(sigc, pub, sigr) + buf2 = struct.pack('64s64s64s', prefix_hash, pub, tmp2) + c = hash_to_scalar(buf2, len(buf2)) + c = sc_sub(c, sigc) + return sc_isnonzero(c) == 0 + +def hexToLong(a): + return number.bytes_to_long(a.decode("hex")) + +def longToHex(a): + return number.long_to_bytes(a).encode("hex") + +def hexToBits(a): + return a.decode("hex") + +def bitsToHex(a): + return a.encode("hex") + +def sc_check(key): + #in other words, keys which are too small are rejected + return 0 + #s0, s1, s2, s3, s4, s5, s6, s7 = load_4(longToHex(key)) + #return (signum_(1559614444 - s0) + (signum_(1477600026 - s1) << 1) + (signum_(2734136534 - s2) << 2) + (signum_(350157278 - s3) << 3) + (signum_(-s4) << 4) + (signum_(-s5) << 5) + (signum_(-s6) << 6) + (signum_(268435456 - s7) << 7)) >> 8 + + +if __name__ == "__main__": + if len(sys.argv) <= 1: + print"--------------------------------------------------------" + print"MiniNero, a reimplementation of CryptoNote one-time ring" + print" signatures in Python" + print"" + print" Copyright (c) 2014, The Monero Project" + print"--------------------------------------------------------" + print"" + print"Note: MiniNero produces working and valid ring" + print"signatures, although they differ slightly from the" + print"Monero ring signatures due to hashing and packing" + print"differences between the libraries used." + print"" + print"Run mininero.py with one of the below options" + print"eg. python mininero.py ringsig" + print"" + print"rs - demos MiniNero the random_scalar function" + print"keys - demos MiniNero key generation" + print"fasthash - demos MiniNero fast hash " + print"hashscalar - demos MiniNero H_s(P) equivalent function" + print"hashcurve - demos MiniNero H_p(P)" + print"checkkey - demos MiniNero check_key function" + print"secpub - demos turning a fixed secret to a public key" + print"keyder - demos MiniNero Key Derivation" + print"dersca - demos MiniNero derivation to scalar" + print"derpub - demos derive public key" + print"dersec - demos derive secret key" + print"testcomm - tests the helper struct for some CN functions" + print"gensig - testing generate_signature" + print"checksig - tests checking signature" + print"keyimage - tests creating I=xH_p(P)" + print"ringsig - tests creating and checking a ringsig" + print"conv - tests some helper conversion functions" + print"red - tests some of the sc_Code" + print"gedb - tests some of the edwards curve functions" + print"sck - tests sc_check" + quit() + if sys.argv[1] == "rs": + #test random_scalar + print(longToHex(random_scalar())) + if sys.argv[1] == "keys": + #test generating keys + x,P = generate_keys() + print"generating keys:" + print("secret:") + print( x) + print("public:") + print( P) + print("the point P") + print(decodepoint(P.decode("hex"))) + if sys.argv[1] == "fasthash": + mysecret = "99b66345829d8c05041eea1ba1ed5b2984c3e5ec7a756ef053473c7f22b49f14" + output_index = 2 + buf2 = struct.pack('64sl', mysecret, output_index) + #buf2 = pickle(buf) + #print(buf2) + print(buf2) + print(cn_fast_hash(mysecret, len(mysecret))) + print(cn_fast_hash(buf2, len(buf2))) + + if sys.argv[1] == "hashscalar": + data = "ILOVECATS" + print(cn_fast_hash(data, len(data))) + print(hash_to_scalar(data, len(data))) + if sys.argv[1] == "hashcurve": + data = "ILOVECATS" + print(cn_fast_hash(data, len(data))) + print(hash_to_ec(data)) + + if sys.argv[1] == "checkkey": + x, P = generate_keys() + print(check_key(P)) + if sys.argv[1] == "secpub": + #testing for secret_key_to_public_key + #these test vecs were for the monty implementation + mysecret = "99b66345829d8c05041eea1ba1ed5b2984c3e5ec7a756ef053473c7f22b49f14" + mypublic = "b1c652786697a5feef36a56f36fde524a21193f4e563627977ab515f600fdb3a" + mysecret, P = generate_keys() + pub2 = secret_key_to_public_key(mysecret) + print(pub2.encode("hex")) + if sys.argv[1] == "keyder": + #testing for generate_key_derivation + x,P = generate_keys() + print(x, P) + print(generate_key_derivation(P, x)) + + if sys.argv[1] == "dersca": + #testing for derivation_to_scalar + #this is getting a scalar for one-time-keys rH_s(P) + aa, AA = generate_keys() + bb, BB = generate_keys() + for i in range(0,3): + rr, ZZ = generate_keys() + derivation = generate_key_derivation(BB, aa) + s = derivation_to_scalar(derivation, i) + print(s) + if sys.argv[1] == "derpub": + x, P = generate_keys() + output_index = 5 + keyder = generate_key_derivation(P, x) + print("keyder", keyder) + print(derive_public_key(keyder, output_index, P)) + if sys.argv[1] == "dersec": + x, P = generate_keys() + output_index = 5 + keyder = generate_key_derivation(P, x) + print("keyder", keyder) + print(derive_secret_key(keyder, output_index, x)) + if sys.argv[1] == "testcomm": + a = "99b66345829d8c05041eea1ba1ed5b2984c3e5ec7a756ef053473c7f22b49f14" + co2 = struct.pack('hhl', 1, 2, 3) + print(co2.encode("hex")) #sometimes doesn't print if your terminal doesn't have unicode + + if sys.argv[1] == "gensig": + #testing generate_signature + print"" + prefix = "destination" + sec, pub = generate_keys() # just to have some data to use .. + print(generate_signature(prefix, pub, sec)) + if sys.argv[1] == "checksig": + prefix = "destination" + sec, pub = generate_keys() # just to have some data to use .. + sir, sic = generate_signature(prefix, pub, sec) + print(sir, sic) + print(check_signature(prefix, pub, sir, sic)) + if sys.argv[1] == "keyimage": + x, P = generate_keys() + xb = 14662008266461539177776197088974240017016792645044069572180060425138978088469 + Pb = "1d0ecd1758a685d88b39567f491bc93129f59c7dae7182bddc4e6f5ad38ba462" + + I = generate_key_image(Pb, xb) + print(I) + if sys.argv[1] == "ringsig": + #these are fixed since my computer runs out of memory + xa = 54592381732429499113512315392038591381134951436395595620076310715410049314218 + Pa = "3c853b5a82912313b179e40d655003c5e3112c041fcf755c3f09d2a8c64d9062" + xb = 14662008266461539177776197088974240017016792645044069572180060425138978088469 + Pb = "1d0ecd1758a685d88b39567f491bc93129f59c7dae7182bddc4e6f5ad38ba462" + ima = "0620b888780351a3029dfbf1a5c45a89816f118aa63fa807d51b959cb3c5efc9" + ima, sic, sir = generate_ring_signature("dest", ima, [Pa, Pb],2, xb, 1) + + print("ima",ima) + print("sic", sir) + print("sir", sic) + print(check_ring_signature("dest", ima, [Pa, Pb], 2, sir, sic)) + + if sys.argv[1] == "conv": + #testing reduction + a = "99b66345829d8c05041eea1ba1ed5b2984c3e5ec7a756ef053473c7f22b49f14" + print(a) + r = hexToLong(a) + print(r) + a = longToHex(r) + print(a) + if sys.argv[1] == "red": + a = "99b66345829d8c05041eea1ba1ed5b2984c3e5ec7a756ef053473c7f22b49f14" + tmp = rand.getrandbits(64 * 8) + tmp2 = longToHex(tmp) + print(tmp2) + tmp3 = longToHex(sc_reduce(tmp)) + print(tmp3) + tmp4 = sc_reduce32(CURVE_P + 1) + print(tmp4) + tmp5 = sc_reduce(CURVE_P + 1) + print(tmp5) + if sys.argv[1] == "gedb": + x, P = generate_keys() + print(ge_double_scalarmult_base_vartime(x, P, x)) + if sys.argv[1] == "sck": + #testing sc_check + x, P = generate_keys() + print(sc_check(x)) + print("nonreduced", longToHex(x)) + print("reduced", sc_reduce32_2(x)) + print("check reduced", sc_check(hexToLong(sc_reduce32_2(x))))