11package org .arkecosystem .crypto ;
22
3- import java .math .BigInteger ;
4- import org .bitcoinj .core .Sha256Hash ;
5-
6- /** Heavily inspired in https://github.com/miketwk/bip-schnorr-java/blob/master/Schnorr.java */
73public class Schnorr {
8- public static final BigInteger p =
9- new BigInteger ("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F" , 16 );
10- public static final BigInteger n =
11- new BigInteger ("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141" , 16 );
12- public static final BigInteger [] G = {
13- new BigInteger ("79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798" , 16 ),
14- new BigInteger ("483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8" , 16 )
15- };
16-
17- public static final BigInteger TWO = BigInteger .valueOf (2 );
18- public static final BigInteger THREE = BigInteger .valueOf (3 );
19-
20- private static final char [] hexArray = "0123456789ABCDEF" .toCharArray ();
21-
22- public static BigInteger [] addPoint (BigInteger [] p1 , BigInteger [] p2 ) {
23- if (p1 == null || p1 .length != 2 ) return p2 ;
24-
25- if (p2 == null || p2 .length != 2 ) return p1 ;
26-
27- if (p1 [0 ].compareTo (p2 [0 ]) == 0 && p1 [1 ].compareTo (p2 [1 ]) != 0 ) return null ;
28-
29- BigInteger lam ;
30- if (p1 [0 ].compareTo (p2 [0 ]) == 0 && p1 [1 ].compareTo (p2 [1 ]) == 0 )
31- lam =
32- (THREE .multiply (p1 [0 ])
33- .multiply (p1 [0 ])
34- .multiply (TWO .multiply (p1 [1 ]).modPow (p .subtract (TWO ), p )))
35- .mod (p );
36- else
37- lam =
38- (p2 [1 ].subtract (p1 [1 ])
39- .multiply (p2 [0 ].subtract (p1 [0 ]).modPow (p .subtract (TWO ), p )))
40- .mod (p );
41-
42- BigInteger x3 = (lam .multiply (lam ).subtract (p1 [0 ]).subtract (p2 [0 ])).mod (p );
43-
44- return new BigInteger [] {x3 , lam .multiply (p1 [0 ].subtract (x3 )).subtract (p1 [1 ]).mod (p )};
45- }
46-
47- public static BigInteger [] multiplyPoint (BigInteger [] P , BigInteger n ) {
48- BigInteger [] R = null ;
49- for (int i = 0 ; i < 256 ; i ++) {
50- if (BigInteger .ONE .compareTo (n .shiftRight (i ).and (BigInteger .ONE )) == 0 )
51- R = addPoint (R , P );
52- P = addPoint (P , P );
53- }
54- return R ;
55- }
56-
57- public static BigInteger jacobi (BigInteger x ) {
58- return x .modPow (p .subtract (BigInteger .ONE ).divide (TWO ), p );
59- }
60-
61- public static BigInteger [] bytesToPoint (byte [] b ) {
62- if (b [0 ] != 2 && b [0 ] != 3 ) return null ;
63-
64- BigInteger odd = b [0 ] == 3 ? BigInteger .ONE : BigInteger .ZERO ;
65- BigInteger x = toBigInteger (b , 1 , 32 );
66- BigInteger y_sq = x .modPow (THREE , p ).add (BigInteger .valueOf (7 )).mod (p );
67- BigInteger y0 = y_sq .modPow (p .add (BigInteger .ONE ).divide (BigInteger .valueOf (4 )), p );
68- if (y_sq .compareTo (y0 .modPow (TWO , p )) != 0 ) return null ;
69-
70- BigInteger y = y0 .and (BigInteger .ONE ).compareTo (odd ) != 0 ? p .subtract (y0 ) : y0 ;
71-
72- return new BigInteger [] {x , y };
73- }
74-
75- public static byte [] to32BytesData (BigInteger num ) {
76- String hexNum = num .toString (16 );
77- if (hexNum .length () < 64 ) {
78- StringBuilder sb = new StringBuilder ();
79- for (int i = 0 ; i < 64 - hexNum .length (); i ++) sb .append ("0" );
80-
81- hexNum = sb .append (hexNum ).toString ();
82- }
83- return hexStringToByteArray (hexNum );
84- }
85-
86- public static BigInteger toBigInteger (byte [] data , int startPos , int len ) {
87- return new BigInteger (bytesToHex (data , startPos , len ), 16 );
88- }
89-
90- public static BigInteger toBigInteger (byte [] data ) {
91- return new BigInteger (bytesToHex (data ), 16 );
92- }
93-
94- public static byte [] pointToBytes (BigInteger [] point ) {
95- byte [] res = new byte [33 ];
96- res [0 ] =
97- BigInteger .ONE .compareTo (point [1 ].and (BigInteger .ONE )) == 0
98- ? (byte ) 0x03
99- : (byte ) 0x02 ;
100- System .arraycopy (to32BytesData (point [0 ]), 0 , res , 1 , 32 );
101- return res ;
102- }
103-
104- public static byte [] schnorrSign (byte [] msg , BigInteger seckey ) {
105- if (msg .length != 32 ) throw new RuntimeException ("The message must be a 32-byte array." );
106-
107- if (BigInteger .ZERO .compareTo (seckey ) > 0
108- || seckey .compareTo (n .subtract (BigInteger .ONE )) > 0 )
109- throw new RuntimeException ("The secret key must be an integer in the range 1..n-1." );
110-
111- byte [] resultData = new byte [32 + msg .length ];
112- System .arraycopy (to32BytesData (seckey ), 0 , resultData , 0 , 32 );
113- System .arraycopy (msg , 0 , resultData , 32 , msg .length );
114-
115- BigInteger k0 = toBigInteger (Sha256Hash .hash (resultData )).mod (n );
116- if (BigInteger .ZERO .compareTo (k0 ) == 0 )
117- throw new RuntimeException ("Failure. This happens only with negligible probability." );
118-
119- BigInteger [] R = multiplyPoint (G , k0 );
120-
121- BigInteger k = BigInteger .ONE .compareTo (jacobi (R [1 ])) != 0 ? n .subtract (k0 ) : k0 ;
122- byte [] R0Bytes = to32BytesData (R [0 ]);
123- byte [] eData = new byte [32 + 33 + 32 ];
124- System .arraycopy (R0Bytes , 0 , eData , 0 , 32 );
125- System .arraycopy (pointToBytes (multiplyPoint (G , seckey )), 0 , eData , 32 , 33 );
126- System .arraycopy (msg , 0 , eData , 65 , 32 );
127- eData = Sha256Hash .hash (eData );
128- BigInteger e = toBigInteger (eData ).mod (n );
129-
130- byte [] finalData = new byte [64 ];
131- System .arraycopy (R0Bytes , 0 , finalData , 0 , 32 );
132- System .arraycopy (to32BytesData (e .multiply (seckey ).add (k ).mod (n )), 0 , finalData , 32 , 32 );
133-
134- return finalData ;
135- }
136-
137- public static boolean schnorrVerify (byte [] msg , byte [] pubkey , byte [] sig ) {
138- if (msg .length != 32 ) throw new RuntimeException ("The message must be a 32-byte array." );
139-
140- if (pubkey .length != 33 )
141- throw new RuntimeException ("The public key must be a 33-byte array." );
142-
143- if (sig .length != 64 ) throw new RuntimeException ("The signature must be a 64-byte array." );
144-
145- BigInteger [] P = bytesToPoint (pubkey );
146- if (P == null ) return false ;
147-
148- BigInteger r = toBigInteger (sig , 0 , 32 );
149- BigInteger s = toBigInteger (sig , 32 , 32 );
150-
151- if (r .compareTo (p ) >= 0 || s .compareTo (n ) >= 0 ) return false ;
152-
153- byte [] eData = new byte [32 + 33 + 32 ];
154- System .arraycopy (sig , 0 , eData , 0 , 32 );
155- System .arraycopy (pointToBytes (P ), 0 , eData , 32 , 33 );
156- System .arraycopy (msg , 0 , eData , 65 , 32 );
157- eData = Sha256Hash .hash (eData );
158- BigInteger e = toBigInteger (eData ).mod (n );
159-
160- BigInteger [] R = addPoint (multiplyPoint (G , s ), multiplyPoint (P , n .subtract (e )));
161-
162- return R != null && BigInteger .ONE .compareTo (jacobi (R [1 ])) == 0 && r .compareTo (R [0 ]) == 0 ;
163- }
1644
1655 public static byte [] hexStringToByteArray (String s ) {
1666 int len = s .length ();
@@ -173,24 +13,4 @@ public static byte[] hexStringToByteArray(String s) {
17313 }
17414 return data ;
17515 }
176-
177- public static String bytesToHex (byte [] bytes ) {
178- char [] hexChars = new char [bytes .length * 2 ];
179- for (int j = 0 ; j < bytes .length ; j ++) {
180- int v = bytes [j ] & 0xFF ;
181- hexChars [j * 2 ] = hexArray [v >>> 4 ];
182- hexChars [j * 2 + 1 ] = hexArray [v & 0x0F ];
183- }
184- return new String (hexChars );
185- }
186-
187- public static String bytesToHex (byte [] bytes , int startPos , int len ) {
188- char [] hexChars = new char [len * 2 ];
189- for (int j = 0 , i = startPos ; j < len ; j ++, i ++) {
190- int v = bytes [i ] & 0xFF ;
191- hexChars [j * 2 ] = hexArray [v >>> 4 ];
192- hexChars [j * 2 + 1 ] = hexArray [v & 0x0F ];
193- }
194- return new String (hexChars );
195- }
19616}
0 commit comments