red25519 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile CHANGED
@@ -2,3 +2,5 @@ source 'https://rubygems.org'
2
2
 
3
3
  # Specify your gem's dependencies in red25519.gemspec
4
4
  gemspec
5
+
6
+ gem 'jruby-openssl', :platform => :jruby
data/README.md CHANGED
@@ -3,11 +3,16 @@ Red25519
3
3
  [![Build Status](https://secure.travis-ci.org/tarcieri/red25519.png?branch=master)](http://travis-ci.org/tarcieri/red25519)
4
4
 
5
5
  Red25519 provides a Ruby binding to the Ed25519 public-key signature system
6
- based on elliptic curves and created by Dan Bernstein et al. An implementation
7
- in C is taken from the SUPERCOP benchmark suite. Ed25519 provides a 128-bit
8
- security level, that is to say, all known attacks take at least 2^128
9
- operations, providing the same security level as AES-128, NIST P-256, and
10
- RSA-3072.
6
+ based on elliptic curves and created by Dan Bernstein et al. Two
7
+ implementations are provided: a MRI C extension which uses the "ref"
8
+ implementation from the SUPERCOP benchmark suite, and a pure Java version
9
+ which is a direct port of the Python implementation.
10
+
11
+ Ed25519 provides a 128-bit security level, that is to say, all known attacks
12
+ take at least 2^128 operations, providing the same security level as AES-128,
13
+ NIST P-256, and RSA-3072.
14
+
15
+ ![Ed25519 Diagram](https://raw.github.com/tarcieri/red25519/master/ed25519.png)
11
16
 
12
17
  Ed25519 has a number of unique properties that make it one of the best-in-class
13
18
  digital signature algorithms:
@@ -107,6 +112,19 @@ The hex representation can also be passed into the constructor:
107
112
  signing_key = Ed25519::SigningKey.new(signing_key_hex)
108
113
  ```
109
114
 
115
+ JRuby Notes
116
+ -----------
117
+
118
+ red25519 provides a pure Java backend, however this backend is much slower
119
+ than the C-based version. While red25519 will function on JRuby, it may be
120
+ too slow to be usable for a given use case. You should benchmark your
121
+ application to determine if it will be fast enough for your purposes.
122
+
123
+ In the future, red25519 can use an FFI extension to provide better performance
124
+ on JRuby. Alternatively, red25519 can be abandoned for a more comprehensive
125
+ FFI binding to Dan Bernstein's NaCl library, which will soon incorporate
126
+ the SUPERCOP implementation of Ed25519.
127
+
110
128
  Contributing
111
129
  ------------
112
130
 
Binary file
@@ -0,0 +1,228 @@
1
+ package org.red25519;
2
+
3
+ import java.math.BigInteger;
4
+ import java.nio.ByteBuffer;
5
+ import java.security.MessageDigest;
6
+ import java.security.NoSuchAlgorithmException;
7
+ import java.util.Arrays;
8
+
9
+ /* Written by k3d3
10
+ * Released to the public domain
11
+ */
12
+
13
+ public class ed25519 {
14
+ static final int b = 256;
15
+ static final BigInteger q = new BigInteger("57896044618658097711785492504343953926634992332820282019728792003956564819949");
16
+ static final BigInteger qm2 = new BigInteger("57896044618658097711785492504343953926634992332820282019728792003956564819947");
17
+ static final BigInteger qp3 = new BigInteger("57896044618658097711785492504343953926634992332820282019728792003956564819952");
18
+ static final BigInteger l = new BigInteger("7237005577332262213973186563042994240857116359379907606001950938285454250989");
19
+ static final BigInteger d = new BigInteger("-4513249062541557337682894930092624173785641285191125241628941591882900924598840740");
20
+ static final BigInteger I = new BigInteger("19681161376707505956807079304988542015446066515923890162744021073123829784752");
21
+ static final BigInteger By = new BigInteger("46316835694926478169428394003475163141307993866256225615783033603165251855960");
22
+ static final BigInteger Bx = new BigInteger("15112221349535400772501151409588531511454012693041857206046113283949847762202");
23
+ static final BigInteger[] B = {Bx.mod(q),By.mod(q)};
24
+ static final BigInteger un = new BigInteger("57896044618658097711785492504343953926634992332820282019728792003956564819967");
25
+
26
+ static byte[] H(byte[] m) {
27
+ MessageDigest md;
28
+ try {
29
+ md = MessageDigest.getInstance("SHA-512");
30
+ md.reset();
31
+ return md.digest(m);
32
+ } catch (NoSuchAlgorithmException e) {
33
+ e.printStackTrace();
34
+ System.exit(1);
35
+ }
36
+ return null;
37
+ }
38
+
39
+ static BigInteger expmod(BigInteger b, BigInteger e, BigInteger m) {
40
+ //System.out.println("expmod open with b=" + b + " e=" + e + " m=" + m);
41
+ if (e.equals(BigInteger.ZERO)) {
42
+ //System.out.println("expmod close with 1z");
43
+ return BigInteger.ONE;
44
+ }
45
+ BigInteger t = expmod(b, e.divide(BigInteger.valueOf(2)), m).pow(2).mod(m);
46
+ //System.out.println("expmod 1/2 t="+t+" e="+e+" testbit="+(e.testBit(0)?1:0));
47
+ if (e.testBit(0)) {
48
+ t = t.multiply(b).mod(m);
49
+ }
50
+ //System.out.println("expmod close with " + t);
51
+ return t;
52
+ }
53
+
54
+ static BigInteger inv(BigInteger x) {
55
+ //System.out.println("inv open with " + x);
56
+ //System.out.println("inv close with " + expmod(x, qm2, q));
57
+ return expmod(x, qm2, q);
58
+ }
59
+
60
+ static BigInteger xrecover(BigInteger y) {
61
+ BigInteger y2 = y.multiply(y);
62
+ BigInteger xx = (y2.subtract(BigInteger.ONE)).multiply(inv(d.multiply(y2).add(BigInteger.ONE)));
63
+ BigInteger x = expmod(xx, qp3.divide(BigInteger.valueOf(8)), q);
64
+ if (!x.multiply(x).subtract(xx).mod(q).equals(BigInteger.ZERO)) x = (x.multiply(I).mod(q));
65
+ if (!x.mod(BigInteger.valueOf(2)).equals(BigInteger.ZERO)) x = q.subtract(x);
66
+ return x;
67
+ }
68
+
69
+ static BigInteger[] edwards(BigInteger[] P, BigInteger[] Q) {
70
+ BigInteger x1 = P[0];
71
+ BigInteger y1 = P[1];
72
+ BigInteger x2 = Q[0];
73
+ BigInteger y2 = Q[1];
74
+ BigInteger dtemp = d.multiply(x1).multiply(x2).multiply(y1).multiply(y2);
75
+ //System.out.println("edwards open with "+x1+","+x2+" "+y1+","+y2+" d="+d+" dtemp="+dtemp);
76
+ BigInteger x3 = ((x1.multiply(y2)).add((x2.multiply(y1)))).multiply(inv(BigInteger.ONE.add(dtemp)));
77
+ //System.out.println("edwards 1/2 with "+x1+","+x2+" "+y1+","+y2+" d="+d+" dtemp="+dtemp);
78
+ BigInteger y3 = ((y1.multiply(y2)).add((x1.multiply(x2)))).multiply(inv(BigInteger.ONE.subtract(dtemp)));
79
+ //System.out.println("edwards 2/2 with "+x1+","+x2+" "+y1+","+y2+" d="+d+" dtemp="+dtemp);
80
+ //System.out.println("edwards close with "+x3.mod(q)+","+y3.mod(q));
81
+ return new BigInteger[]{x3.mod(q), y3.mod(q)};
82
+ }
83
+
84
+ static BigInteger[] scalarmult(BigInteger[] P, BigInteger e) {
85
+ //System.out.println("scalarmult open with e = " + e);
86
+ if (e.equals(BigInteger.ZERO)) {
87
+ //System.out.println("scalarmult close with Q = 0,1");
88
+ return new BigInteger[]{BigInteger.ZERO, BigInteger.ONE};
89
+ }
90
+ BigInteger[] Q = scalarmult(P, e.divide(BigInteger.valueOf(2)));
91
+ //System.out.println("scalarmult asQ = " + Q[0] + "," + Q[1]);
92
+ Q = edwards(Q, Q);
93
+ //System.out.println("scalarmult aeQ = " + Q[0] + "," + Q[1] + " e="+e+" testbit="+(e.testBit(0)?1:0));
94
+ if (e.testBit(0)) Q = edwards(Q, P);
95
+ //System.out.println("scalarmult close with Q = " + Q[0] + "," + Q[1]);
96
+ return Q;
97
+ }
98
+
99
+ static byte[] encodeint(BigInteger y) {
100
+ byte[] in = y.toByteArray();
101
+ byte[] out = new byte[in.length];
102
+ for (int i=0;i<in.length;i++) {
103
+ out[i] = in[in.length-1-i];
104
+ }
105
+ return out;
106
+ }
107
+
108
+ static byte[] encodepoint(BigInteger[] P) {
109
+ BigInteger x = P[0];
110
+ BigInteger y = P[1];
111
+ byte[] out = encodeint(y);
112
+ //System.out.println("encodepoint x="+x+" testbit="+(x.testBit(0) ? 1 : 0));
113
+ out[out.length-1] |= (x.testBit(0) ? 0x80 : 0);
114
+ return out;
115
+ }
116
+
117
+ static int bit(byte[] h, int i) {
118
+ //System.out.println("bit open with i="+i);
119
+ //System.out.println("bit close with "+(h[i/8] >> (i%8) & 1));
120
+ return h[i/8] >> (i%8) & 1;
121
+ }
122
+
123
+ static byte[] publickey(byte[] sk) {
124
+ byte[] h = H(sk);
125
+ //System.out.println("publickey open with h=" + test.getHex(h));
126
+ BigInteger a = BigInteger.valueOf(2).pow(b-2);
127
+ for (int i=3;i<(b-2);i++) {
128
+ BigInteger apart = BigInteger.valueOf(2).pow(i).multiply(BigInteger.valueOf(bit(h,i)));
129
+ //System.out.println("publickey apart="+apart);
130
+ a = a.add(apart);
131
+ }
132
+ BigInteger[] A = scalarmult(B,a);
133
+ //System.out.println("publickey close with A="+A[0]+","+A[1]+" out="+test.getHex(encodepoint(A)));
134
+ return encodepoint(A);
135
+ }
136
+
137
+ static BigInteger Hint(byte[] m) {
138
+ byte[] h = H(m);
139
+ BigInteger hsum = BigInteger.ZERO;
140
+ for (int i=0;i<2*b;i++) {
141
+ hsum = hsum.add(BigInteger.valueOf(2).pow(i).multiply(BigInteger.valueOf(bit(h,i))));
142
+ }
143
+ return hsum;
144
+ }
145
+
146
+ static byte[] signature(byte[] m, byte[] sk, byte[] pk) {
147
+ byte[] h = H(sk);
148
+ //System.out.println("signature open with m="+test.getHex(m)+" h="+test.getHex(h)+" pk="+test.getHex(pk));
149
+ BigInteger a = BigInteger.valueOf(2).pow(b-2);
150
+ for (int i=3;i<(b-2);i++) {
151
+ a = a.add(BigInteger.valueOf(2).pow(i).multiply(BigInteger.valueOf(bit(h,i))));
152
+ }
153
+ //System.out.println("signature a="+a);
154
+ ByteBuffer rsub = ByteBuffer.allocate((b/8)+m.length);
155
+ rsub.put(h, b/8, b/4-b/8).put(m);
156
+ //System.out.println("signature rsub="+test.getHex(rsub.array()));
157
+ BigInteger r = Hint(rsub.array());
158
+ //System.out.println("signature r="+r);
159
+ BigInteger[] R = scalarmult(B,r);
160
+ ByteBuffer Stemp = ByteBuffer.allocate(32+pk.length+m.length);
161
+ Stemp.put(encodepoint(R)).put(pk).put(m);
162
+ BigInteger S = r.add(Hint(Stemp.array()).multiply(a)).mod(l);
163
+ ByteBuffer out = ByteBuffer.allocate(64);
164
+ out.put(encodepoint(R)).put(encodeint(S));
165
+ return out.array();
166
+ }
167
+
168
+ static boolean isoncurve(BigInteger[] P) {
169
+ BigInteger x = P[0];
170
+ BigInteger y = P[1];
171
+ //System.out.println("isoncurve open with P="+x+","+y);
172
+ BigInteger xx = x.multiply(x);
173
+ BigInteger yy = y.multiply(y);
174
+ BigInteger dxxyy = d.multiply(yy).multiply(xx);
175
+ //System.out.println("isoncurve close with "+xx.negate().add(yy).subtract(BigInteger.ONE).subtract(dxxyy).mod(q));
176
+ return xx.negate().add(yy).subtract(BigInteger.ONE).subtract(dxxyy).mod(q).equals(BigInteger.ZERO);
177
+ }
178
+
179
+ static BigInteger decodeint(byte[] s) {
180
+ byte[] out = new byte[s.length];
181
+ for (int i=0;i<s.length;i++) {
182
+ out[i] = s[s.length-1-i];
183
+ }
184
+ return new BigInteger(out).and(un);
185
+ }
186
+
187
+ static BigInteger[] decodepoint(byte[] s) throws Exception {
188
+ byte[] ybyte = new byte[s.length];
189
+ for (int i=0;i<s.length;i++) {
190
+ ybyte[i] = s[s.length-1-i];
191
+ }
192
+ //System.out.println("decodepoint open with s="+test.getHex(s)+" ybyte="+test.getHex(ybyte));
193
+ BigInteger y = new BigInteger(ybyte).and(un);
194
+ //System.out.println("decodepoint y="+y);
195
+ BigInteger x = xrecover(y);
196
+ //System.out.println("decodepoint x="+x+" testbit="+(x.testBit(0)?1:0)+" bit="+bit(s, b-1));
197
+ if ((x.testBit(0)?1:0) != bit(s, b-1)) {
198
+ x = q.subtract(x);
199
+ }
200
+ BigInteger[] P = {x,y};
201
+ if (!isoncurve(P)) throw new Exception("decoding point that is not on curve");
202
+ return P;
203
+ }
204
+
205
+ static boolean checkvalid(byte[] s, byte[] m, byte[] pk) throws Exception {
206
+ if (s.length != b/4) throw new Exception("signature length is wrong");
207
+ if (pk.length != b/8) throw new Exception("public-key length is wrong");
208
+ //System.out.println("checkvalid open with s="+test.getHex(s)+" m="+test.getHex(m)+" pk="+test.getHex(pk));
209
+ byte[] Rbyte = Arrays.copyOfRange(s, 0, b/8);
210
+ //System.out.println("checkvalid Rbyte="+test.getHex(Rbyte));
211
+ BigInteger[] R = decodepoint(Rbyte);
212
+ BigInteger[] A = decodepoint(pk);
213
+ //System.out.println("checkvalid R="+R[0]+","+R[1]+" A="+A[0]+","+A[1]);
214
+ byte[] Sbyte = Arrays.copyOfRange(s, b/8, b/4);
215
+ //System.out.println("checkvalid Sbyte="+test.getHex(Sbyte));
216
+ BigInteger S = decodeint(Sbyte);
217
+ //System.out.println("checkvalid S="+S);
218
+ ByteBuffer Stemp = ByteBuffer.allocate(32+pk.length+m.length);
219
+ Stemp.put(encodepoint(R)).put(pk).put(m);
220
+ BigInteger h = Hint(Stemp.array());
221
+ BigInteger[] ra = scalarmult(B,S);
222
+ BigInteger[] rb = edwards(R,scalarmult(A,h));
223
+ //System.out.println("checkvalid ra="+ra[0]+","+ra[1]+" rb="+rb[0]+","+rb[1]);
224
+ if (!ra[0].equals(rb[0]) || !ra[1].equals(rb[1])) // Constant time comparison
225
+ return false;
226
+ return true;
227
+ }
228
+ }
@@ -1,6 +1,7 @@
1
1
  require 'red25519/version'
2
2
  require 'red25519_engine'
3
3
  require 'red25519/keys'
4
+ require 'red25519/jruby_engine' if defined? JRUBY_VERSION
4
5
 
5
6
  module Ed25519
6
7
  SECRET_KEY_BYTES = 32
@@ -0,0 +1,27 @@
1
+ require 'java'
2
+
3
+ module Ed25519
4
+ module Engine
5
+ module_function
6
+
7
+ def create_keypair(seed)
8
+ raise ArgumentError, "seed must be 32 bytes long" unless seed.length == 32
9
+
10
+ verify_key = org.red25519.ed25519.publickey(seed.to_java_bytes)
11
+ verify_key = String.from_java_bytes(verify_key)
12
+ [verify_key, seed + verify_key]
13
+ end
14
+
15
+ def sign(signing_key, message)
16
+ verify_key = signing_key[32...64].to_java_bytes
17
+ signing_key = signing_key[0...32].to_java_bytes
18
+
19
+ signature = org.red25519.ed25519.signature(message.to_java_bytes, signing_key, verify_key)
20
+ String.from_java_bytes(signature)
21
+ end
22
+
23
+ def verify(verify_key, signature, message)
24
+ org.red25519.ed25519.checkvalid(signature.to_java_bytes, message.to_java_bytes, verify_key.to_java_bytes)
25
+ end
26
+ end
27
+ end
@@ -1,3 +1,3 @@
1
1
  module Ed25519
2
- VERSION = "1.0.0"
2
+ VERSION = "1.1.0"
3
3
  end
@@ -8,14 +8,20 @@ Gem::Specification.new do |gem|
8
8
  gem.summary = "Red25519 provides both C and Java bindings to the Ed25519 public key signature system"
9
9
  gem.homepage = "https://github.com/tarcieri/red25519"
10
10
 
11
- gem.files = `git ls-files`.split($\)
11
+ gem.files = `git ls-files`.split($\)
12
+ gem.files << "lib/red25519_engine.jar" if defined? JRUBY_VERSION
13
+
12
14
  gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
13
15
  gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
14
16
  gem.name = "red25519"
15
17
  gem.require_paths = ["lib"]
16
18
  gem.version = Ed25519::VERSION
17
-
18
- gem.extensions = "ext/red25519/extconf.rb"
19
+
20
+ if defined? JRUBY_VERSION
21
+ gem.platform = "jruby"
22
+ else
23
+ gem.extensions = "ext/red25519/extconf.rb"
24
+ end
19
25
 
20
26
  gem.add_runtime_dependency "hkdf"
21
27
 
@@ -17,7 +17,7 @@ describe Ed25519::Engine do
17
17
  privkey.length.should eq Ed25519::SECRET_KEY_BYTES * 2
18
18
  end
19
19
 
20
- it "raises ArgumentError if the seed is not #{Ed25519::SECRET_KEY_BYTES / 2} bytes long" do
20
+ it "raises ArgumentError if the seed is not #{Ed25519::SECRET_KEY_BYTES} bytes long" do
21
21
  expect { Ed25519::Engine.create_keypair("A" * (seed_length - 1)) }.to raise_exception ArgumentError
22
22
  expect { Ed25519::Engine.create_keypair("A" * (seed_length + 1)) }.to raise_exception ArgumentError
23
23
  end
@@ -1,5 +1,12 @@
1
- require 'rake/extensiontask'
1
+ if defined? JRUBY_VERSION
2
+ require 'rake/javaextensiontask'
3
+ Rake::JavaExtensionTask.new('red25519_engine') do |ext|
4
+ ext.ext_dir = 'ext/red25519'
5
+ end
6
+ else
7
+ require 'rake/extensiontask'
2
8
 
3
- Rake::ExtensionTask.new('red25519_engine') do |ext|
4
- ext.ext_dir = 'ext/red25519'
9
+ Rake::ExtensionTask.new('red25519_engine') do |ext|
10
+ ext.ext_dir = 'ext/red25519'
11
+ end
5
12
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: red25519
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.1.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-10-07 00:00:00.000000000 Z
12
+ date: 2012-10-10 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: hkdf
16
- requirement: &70274577174300 !ruby/object:Gem::Requirement
16
+ requirement: &70285633096820 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: '0'
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *70274577174300
24
+ version_requirements: *70285633096820
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: rake-compiler
27
- requirement: &70274577173840 !ruby/object:Gem::Requirement
27
+ requirement: &70285633096400 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ! '>='
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: '0'
33
33
  type: :development
34
34
  prerelease: false
35
- version_requirements: *70274577173840
35
+ version_requirements: *70285633096400
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: rake
38
- requirement: &70274577173420 !ruby/object:Gem::Requirement
38
+ requirement: &70285633095980 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ! '>='
@@ -43,10 +43,10 @@ dependencies:
43
43
  version: '0'
44
44
  type: :development
45
45
  prerelease: false
46
- version_requirements: *70274577173420
46
+ version_requirements: *70285633095980
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: rspec
49
- requirement: &70274577173000 !ruby/object:Gem::Requirement
49
+ requirement: &70285633095560 !ruby/object:Gem::Requirement
50
50
  none: false
51
51
  requirements:
52
52
  - - ! '>='
@@ -54,7 +54,7 @@ dependencies:
54
54
  version: '0'
55
55
  type: :development
56
56
  prerelease: false
57
- version_requirements: *70274577173000
57
+ version_requirements: *70285633095560
58
58
  description: Ruby wrappers for the Ed25519 public key signature system
59
59
  email:
60
60
  - tony.arcieri@gmail.com
@@ -71,6 +71,7 @@ files:
71
71
  - LICENSE
72
72
  - README.md
73
73
  - Rakefile
74
+ - ed25519.png
74
75
  - ext/red25519/api.h
75
76
  - ext/red25519/crypto_int32.h
76
77
  - ext/red25519/crypto_sign.h
@@ -83,6 +84,7 @@ files:
83
84
  - ext/red25519/ge25519.c
84
85
  - ext/red25519/ge25519.h
85
86
  - ext/red25519/ge25519_base.data
87
+ - ext/red25519/org/red25519/ed25519.java
86
88
  - ext/red25519/red25519_engine.c
87
89
  - ext/red25519/sc25519.c
88
90
  - ext/red25519/sc25519.h
@@ -91,6 +93,7 @@ files:
91
93
  - ext/red25519/sha512.h
92
94
  - ext/red25519/verify.c
93
95
  - lib/red25519.rb
96
+ - lib/red25519/jruby_engine.rb
94
97
  - lib/red25519/keys.rb
95
98
  - lib/red25519/version.rb
96
99
  - red25519.gemspec