Skip to content

Commit 7a34ac6

Browse files
committed
refactor: Small update to Schnorr signatures
1 parent 165e852 commit 7a34ac6

File tree

3 files changed

+218
-2
lines changed

3 files changed

+218
-2
lines changed

src/main/java/com/danubetech/keyformats/crypto/impl/secp256k1_ES256KS_PrivateKeySigner.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.danubetech.keyformats.crypto.impl;
22

33
import com.danubetech.keyformats.crypto.PrivateKeySigner;
4+
import com.danubetech.keyformats.crypto.provider.SHA256Provider;
45
import com.danubetech.keyformats.jose.JWSAlgorithm;
56
import com.miketwk.schnorr.core.Schnorr;
67
import org.bitcoinj.crypto.ECKey;
@@ -19,7 +20,7 @@ public byte[] sign(byte[] content) throws GeneralSecurityException {
1920

2021
// sign
2122

22-
byte[] hash = Schnorr.sha256(content);
23+
byte[] hash = SHA256Provider.get().sha256(content);
2324
byte[] signatureBytes = Schnorr.schnorr_sign(hash, this.getPrivateKey().getPrivKey());
2425

2526
// done

src/main/java/com/danubetech/keyformats/crypto/impl/secp256k1_ES256KS_PublicKeyVerifier.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.danubetech.keyformats.crypto.impl;
22

33
import com.danubetech.keyformats.crypto.PublicKeyVerifier;
4+
import com.danubetech.keyformats.crypto.provider.SHA256Provider;
45
import com.danubetech.keyformats.jose.JWSAlgorithm;
56
import com.miketwk.schnorr.core.Schnorr;
67
import org.bitcoinj.crypto.ECKey;
@@ -19,7 +20,7 @@ public boolean verify(byte[] content, byte[] signature) throws GeneralSecurityEx
1920

2021
// verify
2122

22-
byte[] hash = Schnorr.sha256(content);
23+
byte[] hash = SHA256Provider.get().sha256(content);
2324
boolean verified = Schnorr.schnorr_verify(hash, this.getPublicKey().getPubKey(), signature);
2425

2526
// done
Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
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

Comments
 (0)