red25519 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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