Skip to content

Commit 0cd6a5d

Browse files
committed
Move to bouncy castle 1.50 and use RFC6979 deterministic ECDSA
signatures
1 parent 0d8b1c8 commit 0cd6a5d

5 files changed

Lines changed: 103 additions & 36 deletions

File tree

api/src/main/java/com/bitsofproof/supernode/common/ECKeyPair.java

Lines changed: 17 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -21,19 +21,20 @@
2121
import java.security.SecureRandom;
2222

2323
import org.bouncycastle.asn1.ASN1InputStream;
24-
import org.bouncycastle.asn1.DERInteger;
24+
import org.bouncycastle.asn1.ASN1Integer;
2525
import org.bouncycastle.asn1.DERSequenceGenerator;
2626
import org.bouncycastle.asn1.DLSequence;
2727
import org.bouncycastle.asn1.sec.SECNamedCurves;
2828
import org.bouncycastle.asn1.x9.X9ECParameters;
2929
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
30+
import org.bouncycastle.crypto.digests.SHA256Digest;
3031
import org.bouncycastle.crypto.generators.ECKeyPairGenerator;
3132
import org.bouncycastle.crypto.params.ECDomainParameters;
3233
import org.bouncycastle.crypto.params.ECKeyGenerationParameters;
3334
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
3435
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
3536
import org.bouncycastle.crypto.signers.ECDSASigner;
36-
import org.bouncycastle.math.ec.ECPoint;
37+
import org.bouncycastle.crypto.signers.HMacDSAKCalculator;
3738
import org.bouncycastle.util.Arrays;
3839

3940
import com.bitsofproof.supernode.api.Address;
@@ -79,15 +80,7 @@ public static ECKeyPair createNew (boolean compressed)
7980
ECKeyPair k = new ECKeyPair ();
8081
k.priv = privParams.getD ();
8182
k.compressed = compressed;
82-
if ( compressed )
83-
{
84-
ECPoint q = pubParams.getQ ();
85-
k.pub = new ECPoint.Fp (domain.getCurve (), q.getX (), q.getY (), true).getEncoded ();
86-
}
87-
else
88-
{
89-
k.pub = pubParams.getQ ().getEncoded ();
90-
}
83+
k.pub = pubParams.getQ ().getEncoded (compressed);
9184
return k;
9285
}
9386

@@ -144,30 +137,14 @@ public ECKeyPair (byte[] p, boolean compressed) throws ValidationException
144137
}
145138
this.priv = new BigInteger (1, p).mod (curve.getN ());
146139
this.compressed = compressed;
147-
if ( compressed )
148-
{
149-
ECPoint q = curve.getG ().multiply (priv);
150-
pub = new ECPoint.Fp (domain.getCurve (), q.getX (), q.getY (), true).getEncoded ();
151-
}
152-
else
153-
{
154-
pub = curve.getG ().multiply (priv).getEncoded ();
155-
}
140+
pub = curve.getG ().multiply (priv).getEncoded (compressed);
156141
}
157142

158143
public ECKeyPair (BigInteger priv, boolean compressed)
159144
{
160145
this.priv = priv;
161146
this.compressed = compressed;
162-
if ( compressed )
163-
{
164-
ECPoint q = curve.getG ().multiply (priv);
165-
pub = new ECPoint.Fp (domain.getCurve (), q.getX (), q.getY (), true).getEncoded ();
166-
}
167-
else
168-
{
169-
pub = curve.getG ().multiply (priv).getEncoded ();
170-
}
147+
pub = curve.getG ().multiply (priv).getEncoded (compressed);
171148
}
172149

173150
@Override
@@ -177,15 +154,15 @@ public byte[] sign (byte[] hash) throws ValidationException
177154
{
178155
throw new ValidationException ("Need private key to sign");
179156
}
180-
ECDSASigner signer = new ECDSASigner ();
157+
ECDSASigner signer = new ECDSASigner (new HMacDSAKCalculator (new SHA256Digest ()));
181158
signer.init (true, new ECPrivateKeyParameters (priv, domain));
182159
BigInteger[] signature = signer.generateSignature (hash);
183160
ByteArrayOutputStream s = new ByteArrayOutputStream ();
184161
try
185162
{
186163
DERSequenceGenerator seq = new DERSequenceGenerator (s);
187-
seq.addObject (new DERInteger (signature[0]));
188-
seq.addObject (new DERInteger (signature[1]));
164+
seq.addObject (new ASN1Integer (signature[0]));
165+
seq.addObject (new ASN1Integer (signature[1]));
189166
seq.close ();
190167
return s.toByteArray ();
191168
}
@@ -210,8 +187,8 @@ public static boolean verify (byte[] hash, byte[] signature, byte[] pub)
210187
signer.init (false, new ECPublicKeyParameters (curve.getCurve ().decodePoint (pub), domain));
211188

212189
DLSequence seq = (DLSequence) asn1.readObject ();
213-
BigInteger r = ((DERInteger) seq.getObjectAt (0)).getPositiveValue ();
214-
BigInteger s = ((DERInteger) seq.getObjectAt (1)).getPositiveValue ();
190+
BigInteger r = ((ASN1Integer) seq.getObjectAt (0)).getPositiveValue ();
191+
BigInteger s = ((ASN1Integer) seq.getObjectAt (1)).getPositiveValue ();
215192
return signer.verifySignature (hash, r, s);
216193
}
217194
catch ( Exception e )
@@ -231,6 +208,12 @@ public static boolean verify (byte[] hash, byte[] signature, byte[] pub)
231208
}
232209
}
233210

211+
@Override
212+
public String toString ()
213+
{
214+
return serializeWIF (this);
215+
}
216+
234217
public static String serializeWIF (Key key)
235218
{
236219
return ByteUtils.toBase58 (bytesWIF (key));
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
package com.bitsofproof.supernode.api;
2+
3+
import static org.junit.Assert.assertTrue;
4+
5+
import java.io.IOException;
6+
import java.io.InputStream;
7+
import java.security.Security;
8+
import java.util.Arrays;
9+
10+
import org.bouncycastle.jce.provider.BouncyCastleProvider;
11+
import org.json.JSONArray;
12+
import org.json.JSONException;
13+
import org.json.JSONObject;
14+
import org.junit.BeforeClass;
15+
import org.junit.Test;
16+
17+
import com.bitsofproof.supernode.common.ByteUtils;
18+
import com.bitsofproof.supernode.common.ECKeyPair;
19+
import com.bitsofproof.supernode.common.ValidationException;
20+
21+
public class RFC6979Test
22+
{
23+
static BouncyCastleProvider provider;
24+
25+
@BeforeClass
26+
public static void init ()
27+
{
28+
Security.addProvider (provider = new BouncyCastleProvider ());
29+
}
30+
31+
private static final String TESTS = "RFC6979.json";
32+
33+
private JSONArray readArray (String resource) throws IOException, JSONException
34+
{
35+
InputStream input = this.getClass ().getResource ("/" + resource).openStream ();
36+
StringBuffer content = new StringBuffer ();
37+
byte[] buffer = new byte[1024];
38+
int len;
39+
while ( (len = input.read (buffer)) > 0 )
40+
{
41+
byte[] s = new byte[len];
42+
System.arraycopy (buffer, 0, s, 0, len);
43+
content.append (new String (buffer, "UTF-8"));
44+
}
45+
return new JSONArray (content.toString ());
46+
}
47+
48+
@Test
49+
public void rfc6979 () throws IOException, JSONException, ValidationException
50+
{
51+
JSONArray tests = readArray (TESTS);
52+
for ( int i = 0; i < tests.length (); ++i )
53+
{
54+
JSONObject test = tests.getJSONObject (i);
55+
ECKeyPair key = ECKeyPair.parseWIF (test.getString ("key"));
56+
byte[] message = test.getString ("message").getBytes ();
57+
byte[] expectedSignature = ByteUtils.fromHex (test.getString ("expectedSignature"));
58+
byte[] signature = key.sign (message);
59+
assertTrue (Arrays.equals (expectedSignature, signature));
60+
}
61+
}
62+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
[
2+
{
3+
"key": "L24GFtCcK6E8dUNGkRK7iCUcfi3JJmVQq8NPG9LzcXS594YkuFoK",
4+
"message":"Satoshi Nakamoto",
5+
"expectedSignature":"304502202eb53dd34d8c0014371ddf3c57b60cbdde5c525abd6384b6453192fac9ec180a022100fe5b29c65d400d7e48a0f9e3e450069fa87400288e78e1e9932ad44f6c03f23f"
6+
},
7+
{
8+
"key": "L1MuyFhSGEJYXZWHRLt2Ggnou5BzCMy7165eFTB2qPPU93B4fRjV",
9+
"message":"The Times 03/Jan/2009 Chancellor on brink of second bailout for banks",
10+
"expectedSignature":"30460221009d4adeca74eaea3a81f0c55c54478f6d5c652196b4d7c02d69bbf30504d9e95b02210080bc27ba00a4c75919b477b2a61d56de851a521d2ff6782b631c542e76e7d346"
11+
},
12+
{
13+
"key": "KxiYEU4ti9G4pLb832rrjGnkvHpAt8Dqx7ZBq3NA66pQN3hpRFSx",
14+
"message":"Satoshi Nakamoto",
15+
"expectedSignature":"304402203db5f02f6d96090852a31d16e44077a4ee879402a01bb9019238ec5269b976010220558817ff1534dc47f21b766fc014369158e4a471ae42b986eaaf659f81abe3ee"
16+
},
17+
{
18+
"key": "L3wadShuDYpTR1sHVsbPSE695YX1Fh5wfDvE4ztZtyyYjidSUJmX",
19+
"message":"The Times 03/Jan/2009 Chancellor on brink of second bailout for banks",
20+
"expectedSignature":"30450220290a41120f7d06d86d4d6914ea718d91111e58700b28072a951e59a2a8b1f983022100dee6880df00d72bc847929219a85d4edad83bd2ac45907371af920f3cac20bd7"
21+
}
22+
]

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@
4949
<springframework.version>3.1.2.RELEASE</springframework.version>
5050
<slf4j.version>1.6.6</slf4j.version>
5151
<java.compiler.version>1.7</java.compiler.version>
52-
<bouncycastle.version>1.48</bouncycastle.version>
52+
<bouncycastle.version>1.50</bouncycastle.version>
5353
<protobuf.version>2.4.1</protobuf.version>
5454
<json.version>20090211</json.version>
5555
<commons-cli.version>1.2</commons-cli.version>

server/src/main/java/com/bitsofproof/supernode/main/Main.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ protected interface App
3535

3636
public static void main (String[] args) throws Exception
3737
{
38-
log.info ("bitsofproof supernode (c) 2013 bits of proof zrt.");
38+
log.info ("bitsofproof supernode (c) 2013-2014 bits of proof zrt.");
3939
Security.addProvider (new BouncyCastleProvider ());
4040
log.trace ("Spring context setup");
4141

0 commit comments

Comments
 (0)