1+ package com .danubetech .keyformats .util ;
2+
3+ import java .math .BigInteger ;
4+ import java .security .MessageDigest ;
5+ import java .security .NoSuchAlgorithmException ;
6+
7+ /**
8+ * Schnorr implementation
9+ *
10+ * @author michaeltan
11+ */
12+ public class SchnorrUtil {
13+ public static final BigInteger p =new BigInteger ("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F" ,16 );
14+ public static final BigInteger n =new BigInteger ("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141" ,16 );
15+ public static final BigInteger [] G = {
16+ new BigInteger ("79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798" ,16 ),
17+ new BigInteger ("483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8" ,16 )
18+ };
19+
20+ public static final BigInteger TWO =BigInteger .valueOf (2 );
21+ public static final BigInteger THREE =BigInteger .valueOf (3 );
22+
23+ private final static char [] hexArray = "0123456789ABCDEF" .toCharArray ();
24+
25+ public static BigInteger [] point_add (BigInteger [] p1 , BigInteger [] p2 ) {
26+ if (p1 ==null || p1 .length !=2 )
27+ return p2 ;
28+
29+ if (p2 ==null || p2 .length !=2 )
30+ return p1 ;
31+
32+ if (p1 [0 ].compareTo (p2 [0 ])==0 && p1 [1 ].compareTo (p2 [1 ])!=0 )
33+ return null ;
34+
35+ BigInteger lam ;
36+ if (p1 [0 ].compareTo (p2 [0 ])==0 && p1 [1 ].compareTo (p2 [1 ])==0 )
37+ lam =(THREE .multiply (p1 [0 ]).multiply (p1 [0 ]).multiply (TWO .multiply (p1 [1 ]).modPow (p .subtract (TWO ), p ))).mod (p );
38+ else
39+ lam =(p2 [1 ].subtract (p1 [1 ]).multiply (p2 [0 ].subtract (p1 [0 ]).modPow (p .subtract (TWO ), p ))).mod (p );
40+
41+ BigInteger x3 =(lam .multiply (lam ).subtract (p1 [0 ]).subtract (p2 [0 ])).mod (p );
42+
43+ return new BigInteger [] {x3 , lam .multiply (p1 [0 ].subtract (x3 )).subtract (p1 [1 ]).mod (p )};
44+ }
45+
46+ public static BigInteger [] point_mul (BigInteger [] P , BigInteger n ) {
47+ BigInteger [] R =null ;
48+ for (int i =0 ; i <256 ; i ++) {
49+ if (BigInteger .ONE .compareTo (n .shiftRight (i ).and (BigInteger .ONE ))==0 )
50+ R =point_add (R ,P );
51+ P =point_add (P ,P );
52+ }
53+ return R ;
54+ }
55+
56+ public static BigInteger jacobi (BigInteger x ) {
57+ return x .modPow (p .subtract (BigInteger .ONE ).divide (TWO ), p );
58+ }
59+
60+ public static BigInteger [] point_from_bytes (byte [] b ) {
61+ if (b [0 ]!=2 && b [0 ]!=3 )
62+ 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 )
69+ return null ;
70+
71+ BigInteger y =y0 .and (BigInteger .ONE ).compareTo (odd ) !=0 ? p .subtract (y0 ) : y0 ;
72+
73+ return new BigInteger [] {x ,y };
74+ }
75+
76+ public static byte [] to32BytesData (BigInteger num ) {
77+ String hexNum =num .toString (16 );
78+ if (hexNum .length ()<64 ) {
79+ StringBuilder sb =new StringBuilder ();
80+ for (int i =0 ; i <64 -hexNum .length (); i ++)
81+ sb .append ("0" );
82+
83+ hexNum =sb .append (hexNum ).toString ();
84+ }
85+ return hexStringToByteArray (hexNum );
86+ }
87+
88+ public static BigInteger toBigInteger (byte [] data , int startPos , int len ) {
89+ return new BigInteger (bytesToHex (data , startPos , len ),16 );
90+ }
91+
92+ public static BigInteger toBigInteger (byte [] data ) {
93+ return new BigInteger (bytesToHex (data ),16 );
94+ }
95+
96+ public static byte [] bytes_from_point (BigInteger [] point ) {
97+ byte [] res =new byte [33 ];
98+ res [0 ]=BigInteger .ONE .compareTo (point [1 ].and (BigInteger .ONE ))==0 ? (byte )0x03 : (byte )0x02 ;
99+ System .arraycopy (to32BytesData (point [0 ]), 0 , res , 1 , 32 );
100+ return res ;
101+ }
102+
103+ public static byte [] schnorr_sign (byte [] msg , BigInteger seckey ) {
104+ if (msg .length !=32 )
105+ throw new RuntimeException ("The message must be a 32-byte array." );
106+
107+ if (BigInteger .ZERO .compareTo (seckey )>0 || seckey .compareTo (n .subtract (BigInteger .ONE )) >0 )
108+ throw new RuntimeException ("The secret key must be an integer in the range 1..n-1." );
109+
110+ byte [] resultData =new byte [32 +msg .length ];
111+ System .arraycopy (to32BytesData (seckey ), 0 , resultData , 0 , 32 );
112+ System .arraycopy (msg , 0 , resultData , 32 , msg .length );
113+
114+ try {
115+ BigInteger k0 =toBigInteger (sha256 (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 = point_mul (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 (bytes_from_point (point_mul (G , seckey )), 0 , eData , 32 , 33 );
126+ System .arraycopy (msg , 0 , eData , 65 , 32 );
127+ eData =sha256 (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+ } catch (Exception e ) {
136+ e .printStackTrace ();
137+ throw new RuntimeException ("Error occurs during schnorr_sign, e=" +e );
138+ }
139+ }
140+
141+ public static boolean schnorr_verify (byte [] msg , byte [] pubkey , byte [] sig ) {
142+ if (msg .length !=32 )
143+ throw new RuntimeException ("The message must be a 32-byte array." );
144+
145+ if (pubkey .length !=33 )
146+ throw new RuntimeException ("The public key must be a 33-byte array." );
147+
148+ if (sig .length !=64 )
149+ throw new RuntimeException ("The signature must be a 64-byte array." );
150+
151+ BigInteger [] P = point_from_bytes (pubkey );
152+ if (P ==null )
153+ return false ;
154+
155+ BigInteger r =toBigInteger (sig ,0 ,32 );
156+ BigInteger s =toBigInteger (sig ,32 ,32 );
157+
158+ if (r .compareTo (p )>=0 || s .compareTo (n )>=0 )
159+ return false ;
160+
161+ try {
162+ byte [] eData =new byte [32 +33 +32 ];
163+ System .arraycopy (sig , 0 , eData , 0 , 32 );
164+ System .arraycopy (bytes_from_point (P ), 0 , eData , 32 , 33 );
165+ System .arraycopy (msg , 0 , eData , 65 , 32 );
166+ eData =sha256 (eData );
167+ BigInteger e =toBigInteger (eData ).mod (n );
168+
169+ BigInteger [] R =point_add (point_mul (G , s ), point_mul (P , n .subtract (e )));
170+ if (R ==null || BigInteger .ONE .compareTo (jacobi (R [1 ]))!=0 || r .compareTo (R [0 ])!=0 )
171+ return false ;
172+
173+ return true ;
174+ } catch (Exception e ) {
175+ e .printStackTrace ();
176+ throw new RuntimeException ("Error occurs during schnorr_verify, e=" +e );
177+ }
178+ }
179+
180+ public static byte [] hexStringToByteArray (String s ) {
181+ int len = s .length ();
182+ byte [] data = new byte [len / 2 ];
183+ for (int i = 0 ; i < len ; i += 2 ) {
184+ data [i / 2 ] = (byte ) ((Character .digit (s .charAt (i ), 16 ) << 4 )
185+ + Character .digit (s .charAt (i +1 ), 16 ));
186+ }
187+ return data ;
188+ }
189+
190+ public static String bytesToHex (byte [] bytes ) {
191+ char [] hexChars = new char [bytes .length * 2 ];
192+ for ( int j = 0 ; j < bytes .length ; j ++ ) {
193+ int v = bytes [j ] & 0xFF ;
194+ hexChars [j * 2 ] = hexArray [v >>> 4 ];
195+ hexChars [j * 2 + 1 ] = hexArray [v & 0x0F ];
196+ }
197+ return new String (hexChars );
198+ }
199+
200+ public static String bytesToHex (byte [] bytes , int startPos , int len ) {
201+ char [] hexChars = new char [len * 2 ];
202+ for ( int j = 0 , i =startPos ; j < len ; j ++, i ++ ) {
203+ int v = bytes [i ] & 0xFF ;
204+ hexChars [j * 2 ] = hexArray [v >>> 4 ];
205+ hexChars [j * 2 + 1 ] = hexArray [v & 0x0F ];
206+ }
207+ return new String (hexChars );
208+ }
209+
210+ public static byte [] sha256 (byte [] input ) throws NoSuchAlgorithmException {
211+ MessageDigest digest = MessageDigest .getInstance ("SHA-256" );
212+ return digest .digest (input );
213+ }
214+ }
0 commit comments