ed25519 1.0.0-jruby → 1.1.0-jruby

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/CHANGES.md +13 -0
  4. data/README.md +9 -9
  5. data/Rakefile +3 -3
  6. data/ext/ed25519_jruby/LICENSE.txt +123 -0
  7. data/ext/ed25519_jruby/README.md +77 -0
  8. data/ext/ed25519_jruby/net/i2p/crypto/eddsa/EdDSAEngine.java +491 -0
  9. data/ext/ed25519_jruby/net/i2p/crypto/eddsa/EdDSAKey.java +31 -0
  10. data/ext/ed25519_jruby/net/i2p/crypto/eddsa/EdDSAPrivateKey.java +338 -0
  11. data/ext/ed25519_jruby/net/i2p/crypto/eddsa/EdDSAPublicKey.java +275 -0
  12. data/ext/ed25519_jruby/net/i2p/crypto/eddsa/EdDSASecurityProvider.java +59 -0
  13. data/ext/ed25519_jruby/net/i2p/crypto/eddsa/KeyFactory.java +75 -0
  14. data/ext/ed25519_jruby/net/i2p/crypto/eddsa/KeyPairGenerator.java +97 -0
  15. data/ext/ed25519_jruby/net/i2p/crypto/eddsa/Utils.java +103 -0
  16. data/ext/ed25519_jruby/net/i2p/crypto/eddsa/math/Constants.java +23 -0
  17. data/ext/ed25519_jruby/net/i2p/crypto/eddsa/math/Curve.java +100 -0
  18. data/ext/ed25519_jruby/net/i2p/crypto/eddsa/math/Encoding.java +54 -0
  19. data/ext/ed25519_jruby/net/i2p/crypto/eddsa/math/Field.java +99 -0
  20. data/ext/ed25519_jruby/net/i2p/crypto/eddsa/math/FieldElement.java +76 -0
  21. data/ext/ed25519_jruby/net/i2p/crypto/eddsa/math/GroupElement.java +1034 -0
  22. data/ext/ed25519_jruby/net/i2p/crypto/eddsa/math/ScalarOps.java +34 -0
  23. data/ext/ed25519_jruby/net/i2p/crypto/eddsa/math/bigint/BigIntegerFieldElement.java +131 -0
  24. data/ext/ed25519_jruby/net/i2p/crypto/eddsa/math/bigint/BigIntegerLittleEndianEncoding.java +102 -0
  25. data/ext/ed25519_jruby/net/i2p/crypto/eddsa/math/bigint/BigIntegerScalarOps.java +37 -0
  26. data/ext/ed25519_jruby/net/i2p/crypto/eddsa/math/bigint/package.html +6 -0
  27. data/ext/ed25519_jruby/net/i2p/crypto/eddsa/math/ed25519/Ed25519FieldElement.java +988 -0
  28. data/ext/ed25519_jruby/net/i2p/crypto/eddsa/math/ed25519/Ed25519LittleEndianEncoding.java +256 -0
  29. data/ext/ed25519_jruby/net/i2p/crypto/eddsa/math/ed25519/Ed25519ScalarOps.java +693 -0
  30. data/ext/ed25519_jruby/net/i2p/crypto/eddsa/spec/EdDSAGenParameterSpec.java +32 -0
  31. data/ext/ed25519_jruby/net/i2p/crypto/eddsa/spec/EdDSANamedCurveSpec.java +35 -0
  32. data/ext/ed25519_jruby/net/i2p/crypto/eddsa/spec/EdDSANamedCurveTable.java +71 -0
  33. data/ext/ed25519_jruby/net/i2p/crypto/eddsa/spec/EdDSAParameterSpec.java +97 -0
  34. data/ext/ed25519_jruby/net/i2p/crypto/eddsa/spec/EdDSAPrivateKeySpec.java +133 -0
  35. data/ext/ed25519_jruby/net/i2p/crypto/eddsa/spec/EdDSAPublicKeySpec.java +61 -0
  36. data/ext/ed25519_jruby/org/cryptosphere/Ed25519Provider.java +95 -0
  37. data/lib/ed25519.rb +8 -8
  38. data/lib/ed25519/signing_key.rb +9 -0
  39. data/lib/ed25519/version.rb +1 -1
  40. data/lib/ed25519_java.jar +0 -0
  41. metadata +32 -3
  42. data/ext/ed25519_java/org/cryptosphere/ed25519.java +0 -228
  43. data/lib/ed25519/provider/jruby.rb +0 -39
@@ -0,0 +1,32 @@
1
+ /**
2
+ * EdDSA-Java by str4d
3
+ *
4
+ * To the extent possible under law, the person who associated CC0 with
5
+ * EdDSA-Java has waived all copyright and related or neighboring rights
6
+ * to EdDSA-Java.
7
+ *
8
+ * You should have received a copy of the CC0 legalcode along with this
9
+ * work. If not, see <https://creativecommons.org/publicdomain/zero/1.0/>.
10
+ *
11
+ */
12
+ package net.i2p.crypto.eddsa.spec;
13
+
14
+ import java.security.spec.AlgorithmParameterSpec;
15
+
16
+ /**
17
+ * Implementation of AlgorithmParameterSpec that holds the name of a named
18
+ * EdDSA curve specification.
19
+ * @author str4d
20
+ *
21
+ */
22
+ public class EdDSAGenParameterSpec implements AlgorithmParameterSpec {
23
+ private final String name;
24
+
25
+ public EdDSAGenParameterSpec(String stdName) {
26
+ name = stdName;
27
+ }
28
+
29
+ public String getName() {
30
+ return name;
31
+ }
32
+ }
@@ -0,0 +1,35 @@
1
+ /**
2
+ * EdDSA-Java by str4d
3
+ *
4
+ * To the extent possible under law, the person who associated CC0 with
5
+ * EdDSA-Java has waived all copyright and related or neighboring rights
6
+ * to EdDSA-Java.
7
+ *
8
+ * You should have received a copy of the CC0 legalcode along with this
9
+ * work. If not, see <https://creativecommons.org/publicdomain/zero/1.0/>.
10
+ *
11
+ */
12
+ package net.i2p.crypto.eddsa.spec;
13
+
14
+ import net.i2p.crypto.eddsa.math.Curve;
15
+ import net.i2p.crypto.eddsa.math.GroupElement;
16
+ import net.i2p.crypto.eddsa.math.ScalarOps;
17
+
18
+ /**
19
+ * EdDSA Curve specification that can also be referred to by name.
20
+ * @author str4d
21
+ *
22
+ */
23
+ public class EdDSANamedCurveSpec extends EdDSAParameterSpec {
24
+ private final String name;
25
+
26
+ public EdDSANamedCurveSpec(String name, Curve curve,
27
+ String hashAlgo, ScalarOps sc, GroupElement B) {
28
+ super(curve, hashAlgo, sc, B);
29
+ this.name = name;
30
+ }
31
+
32
+ public String getName() {
33
+ return name;
34
+ }
35
+ }
@@ -0,0 +1,71 @@
1
+ /**
2
+ * EdDSA-Java by str4d
3
+ *
4
+ * To the extent possible under law, the person who associated CC0 with
5
+ * EdDSA-Java has waived all copyright and related or neighboring rights
6
+ * to EdDSA-Java.
7
+ *
8
+ * You should have received a copy of the CC0 legalcode along with this
9
+ * work. If not, see <https://creativecommons.org/publicdomain/zero/1.0/>.
10
+ *
11
+ */
12
+ package net.i2p.crypto.eddsa.spec;
13
+
14
+ import java.util.Hashtable;
15
+ import java.util.Locale;
16
+
17
+ import net.i2p.crypto.eddsa.Utils;
18
+ import net.i2p.crypto.eddsa.math.Curve;
19
+ import net.i2p.crypto.eddsa.math.Field;
20
+ import net.i2p.crypto.eddsa.math.ed25519.Ed25519LittleEndianEncoding;
21
+ import net.i2p.crypto.eddsa.math.ed25519.Ed25519ScalarOps;
22
+
23
+ /**
24
+ * The named EdDSA curves.
25
+ * @author str4d
26
+ *
27
+ */
28
+ public class EdDSANamedCurveTable {
29
+ public static final String ED_25519 = "Ed25519";
30
+
31
+ private static final Field ed25519field = new Field(
32
+ 256, // b
33
+ Utils.hexToBytes("edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f"), // q
34
+ new Ed25519LittleEndianEncoding());
35
+
36
+ private static final Curve ed25519curve = new Curve(ed25519field,
37
+ Utils.hexToBytes("a3785913ca4deb75abd841414d0a700098e879777940c78c73fe6f2bee6c0352"), // d
38
+ ed25519field.fromByteArray(Utils.hexToBytes("b0a00e4a271beec478e42fad0618432fa7d7fb3d99004d2b0bdfc14f8024832b"))); // I
39
+
40
+ private static final EdDSANamedCurveSpec ed25519 = new EdDSANamedCurveSpec(
41
+ ED_25519,
42
+ ed25519curve,
43
+ "SHA-512", // H
44
+ new Ed25519ScalarOps(), // l
45
+ ed25519curve.createPoint( // B
46
+ Utils.hexToBytes("5866666666666666666666666666666666666666666666666666666666666666"),
47
+ true)); // Precompute tables for B
48
+
49
+ private static final Hashtable<String, EdDSANamedCurveSpec> curves = new Hashtable<String, EdDSANamedCurveSpec>();
50
+
51
+ public static void defineCurve(EdDSANamedCurveSpec curve) {
52
+ curves.put(curve.getName().toLowerCase(Locale.ENGLISH), curve);
53
+ }
54
+
55
+ static void defineCurveAlias(String name, String alias) {
56
+ EdDSANamedCurveSpec curve = curves.get(name.toLowerCase(Locale.ENGLISH));
57
+ if (curve == null) {
58
+ throw new IllegalStateException();
59
+ }
60
+ curves.put(alias.toLowerCase(Locale.ENGLISH), curve);
61
+ }
62
+
63
+ static {
64
+ // RFC 8032
65
+ defineCurve(ed25519);
66
+ }
67
+
68
+ public static EdDSANamedCurveSpec getByName(String name) {
69
+ return curves.get(name.toLowerCase(Locale.ENGLISH));
70
+ }
71
+ }
@@ -0,0 +1,97 @@
1
+ /**
2
+ * EdDSA-Java by str4d
3
+ *
4
+ * To the extent possible under law, the person who associated CC0 with
5
+ * EdDSA-Java has waived all copyright and related or neighboring rights
6
+ * to EdDSA-Java.
7
+ *
8
+ * You should have received a copy of the CC0 legalcode along with this
9
+ * work. If not, see <https://creativecommons.org/publicdomain/zero/1.0/>.
10
+ *
11
+ */
12
+ package net.i2p.crypto.eddsa.spec;
13
+
14
+ import java.security.MessageDigest;
15
+ import java.security.NoSuchAlgorithmException;
16
+ import java.security.spec.AlgorithmParameterSpec;
17
+
18
+ import net.i2p.crypto.eddsa.math.Curve;
19
+ import net.i2p.crypto.eddsa.math.GroupElement;
20
+ import net.i2p.crypto.eddsa.math.ScalarOps;
21
+
22
+ import java.io.Serializable;
23
+
24
+ /**
25
+ * Parameter specification for an EdDSA algorithm.
26
+ * @author str4d
27
+ *
28
+ */
29
+ public class EdDSAParameterSpec implements AlgorithmParameterSpec, Serializable {
30
+ private static final long serialVersionUID = 8274987108472012L;
31
+ private final Curve curve;
32
+ private final String hashAlgo;
33
+ private final ScalarOps sc;
34
+ private final GroupElement B;
35
+
36
+ /**
37
+ * @param curve the curve
38
+ * @param hashAlgo the JCA string for the hash algorithm
39
+ * @param sc the parameter L represented as ScalarOps
40
+ * @param B the parameter B
41
+ * @throws IllegalArgumentException if hash algorithm is unsupported or length is wrong
42
+ */
43
+ public EdDSAParameterSpec(Curve curve, String hashAlgo,
44
+ ScalarOps sc, GroupElement B) {
45
+ try {
46
+ MessageDigest hash = MessageDigest.getInstance(hashAlgo);
47
+ // EdDSA hash function must produce 2b-bit output
48
+ if (curve.getField().getb()/4 != hash.getDigestLength())
49
+ throw new IllegalArgumentException("Hash output is not 2b-bit");
50
+ } catch (NoSuchAlgorithmException e) {
51
+ throw new IllegalArgumentException("Unsupported hash algorithm");
52
+ }
53
+
54
+ this.curve = curve;
55
+ this.hashAlgo = hashAlgo;
56
+ this.sc = sc;
57
+ this.B = B;
58
+ }
59
+
60
+ public Curve getCurve() {
61
+ return curve;
62
+ }
63
+
64
+ public String getHashAlgorithm() {
65
+ return hashAlgo;
66
+ }
67
+
68
+ public ScalarOps getScalarOps() {
69
+ return sc;
70
+ }
71
+
72
+ /**
73
+ * @return the base (generator)
74
+ */
75
+ public GroupElement getB() {
76
+ return B;
77
+ }
78
+
79
+ @Override
80
+ public int hashCode() {
81
+ return hashAlgo.hashCode() ^
82
+ curve.hashCode() ^
83
+ B.hashCode();
84
+ }
85
+
86
+ @Override
87
+ public boolean equals(Object o) {
88
+ if (o == this)
89
+ return true;
90
+ if (!(o instanceof EdDSAParameterSpec))
91
+ return false;
92
+ EdDSAParameterSpec s = (EdDSAParameterSpec) o;
93
+ return hashAlgo.equals(s.getHashAlgorithm()) &&
94
+ curve.equals(s.getCurve()) &&
95
+ B.equals(s.getB());
96
+ }
97
+ }
@@ -0,0 +1,133 @@
1
+ /**
2
+ * EdDSA-Java by str4d
3
+ *
4
+ * To the extent possible under law, the person who associated CC0 with
5
+ * EdDSA-Java has waived all copyright and related or neighboring rights
6
+ * to EdDSA-Java.
7
+ *
8
+ * You should have received a copy of the CC0 legalcode along with this
9
+ * work. If not, see <https://creativecommons.org/publicdomain/zero/1.0/>.
10
+ *
11
+ */
12
+ package net.i2p.crypto.eddsa.spec;
13
+
14
+ import java.security.MessageDigest;
15
+ import java.security.NoSuchAlgorithmException;
16
+ import java.security.spec.KeySpec;
17
+ import java.util.Arrays;
18
+
19
+ import net.i2p.crypto.eddsa.math.GroupElement;
20
+
21
+ /**
22
+ * @author str4d
23
+ *
24
+ */
25
+ public class EdDSAPrivateKeySpec implements KeySpec {
26
+ private final byte[] seed;
27
+ private final byte[] h;
28
+ private final byte[] a;
29
+ private final GroupElement A;
30
+ private final EdDSAParameterSpec spec;
31
+
32
+ /**
33
+ * @param seed the private key
34
+ * @param spec the parameter specification for this key
35
+ * @throws IllegalArgumentException if seed length is wrong or hash algorithm is unsupported
36
+ */
37
+ public EdDSAPrivateKeySpec(byte[] seed, EdDSAParameterSpec spec) {
38
+ if (seed.length != spec.getCurve().getField().getb()/8)
39
+ throw new IllegalArgumentException("seed length is wrong");
40
+
41
+ this.spec = spec;
42
+ this.seed = seed;
43
+
44
+ try {
45
+ MessageDigest hash = MessageDigest.getInstance(spec.getHashAlgorithm());
46
+ int b = spec.getCurve().getField().getb();
47
+
48
+ // H(k)
49
+ h = hash.digest(seed);
50
+
51
+ /*a = BigInteger.valueOf(2).pow(b-2);
52
+ for (int i=3;i<(b-2);i++) {
53
+ a = a.add(BigInteger.valueOf(2).pow(i).multiply(BigInteger.valueOf(Utils.bit(h,i))));
54
+ }*/
55
+ // Saves ~0.4ms per key when running signing tests.
56
+ // TODO: are these bitflips the same for any hash function?
57
+ h[0] &= 248;
58
+ h[(b/8)-1] &= 63;
59
+ h[(b/8)-1] |= 64;
60
+ a = Arrays.copyOfRange(h, 0, b/8);
61
+
62
+ A = spec.getB().scalarMultiply(a);
63
+ } catch (NoSuchAlgorithmException e) {
64
+ throw new IllegalArgumentException("Unsupported hash algorithm");
65
+ }
66
+ }
67
+
68
+ /**
69
+ * Initialize directly from the hash.
70
+ * getSeed() will return null if this constructor is used.
71
+ *
72
+ * @param spec the parameter specification for this key
73
+ * @param h the private key
74
+ * @throws IllegalArgumentException if hash length is wrong
75
+ * @since 0.1.1
76
+ */
77
+ public EdDSAPrivateKeySpec(EdDSAParameterSpec spec, byte[] h) {
78
+ if (h.length != spec.getCurve().getField().getb()/4)
79
+ throw new IllegalArgumentException("hash length is wrong");
80
+
81
+ this.seed = null;
82
+ this.h = h;
83
+ this.spec = spec;
84
+ int b = spec.getCurve().getField().getb();
85
+
86
+ h[0] &= 248;
87
+ h[(b/8)-1] &= 63;
88
+ h[(b/8)-1] |= 64;
89
+ a = Arrays.copyOfRange(h, 0, b/8);
90
+
91
+ A = spec.getB().scalarMultiply(a);
92
+ }
93
+
94
+ public EdDSAPrivateKeySpec(byte[] seed, byte[] h, byte[] a, GroupElement A, EdDSAParameterSpec spec) {
95
+ this.seed = seed;
96
+ this.h = h;
97
+ this.a = a;
98
+ this.A = A;
99
+ this.spec = spec;
100
+ }
101
+
102
+ /**
103
+ * @return will be null if constructed directly from the private key
104
+ */
105
+ public byte[] getSeed() {
106
+ return seed;
107
+ }
108
+
109
+ /**
110
+ * @return the hash
111
+ */
112
+ public byte[] getH() {
113
+ return h;
114
+ }
115
+
116
+ /**
117
+ * @return the private key
118
+ */
119
+ public byte[] geta() {
120
+ return a;
121
+ }
122
+
123
+ /**
124
+ * @return the public key
125
+ */
126
+ public GroupElement getA() {
127
+ return A;
128
+ }
129
+
130
+ public EdDSAParameterSpec getParams() {
131
+ return spec;
132
+ }
133
+ }
@@ -0,0 +1,61 @@
1
+ /**
2
+ * EdDSA-Java by str4d
3
+ *
4
+ * To the extent possible under law, the person who associated CC0 with
5
+ * EdDSA-Java has waived all copyright and related or neighboring rights
6
+ * to EdDSA-Java.
7
+ *
8
+ * You should have received a copy of the CC0 legalcode along with this
9
+ * work. If not, see <https://creativecommons.org/publicdomain/zero/1.0/>.
10
+ *
11
+ */
12
+ package net.i2p.crypto.eddsa.spec;
13
+
14
+ import java.security.spec.KeySpec;
15
+
16
+ import net.i2p.crypto.eddsa.math.GroupElement;
17
+
18
+ /**
19
+ * @author str4d
20
+ *
21
+ */
22
+ public class EdDSAPublicKeySpec implements KeySpec {
23
+ private final GroupElement A;
24
+ private final GroupElement Aneg;
25
+ private final EdDSAParameterSpec spec;
26
+
27
+ /**
28
+ * @param pk the public key
29
+ * @param spec the parameter specification for this key
30
+ * @throws IllegalArgumentException if key length is wrong
31
+ */
32
+ public EdDSAPublicKeySpec(byte[] pk, EdDSAParameterSpec spec) {
33
+ if (pk.length != spec.getCurve().getField().getb()/8)
34
+ throw new IllegalArgumentException("public-key length is wrong");
35
+
36
+ this.A = new GroupElement(spec.getCurve(), pk);
37
+ // Precompute -A for use in verification.
38
+ this.Aneg = A.negate();
39
+ Aneg.precompute(false);
40
+ this.spec = spec;
41
+ }
42
+
43
+ public EdDSAPublicKeySpec(GroupElement A, EdDSAParameterSpec spec) {
44
+ this.A = A;
45
+ this.Aneg = A.negate();
46
+ Aneg.precompute(false);
47
+ this.spec = spec;
48
+ }
49
+
50
+ public GroupElement getA() {
51
+ return A;
52
+ }
53
+
54
+ public GroupElement getNegativeA() {
55
+ return Aneg;
56
+ }
57
+
58
+ public EdDSAParameterSpec getParams() {
59
+ return spec;
60
+ }
61
+ }
@@ -0,0 +1,95 @@
1
+ package org.cryptosphere;
2
+
3
+ import java.security.MessageDigest;
4
+ import java.security.Signature;
5
+ import java.util.Arrays;
6
+ import net.i2p.crypto.eddsa.EdDSAEngine;
7
+ import net.i2p.crypto.eddsa.EdDSAPrivateKey;
8
+ import net.i2p.crypto.eddsa.EdDSAPublicKey;
9
+ import net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable;
10
+ import net.i2p.crypto.eddsa.spec.EdDSAParameterSpec;
11
+ import net.i2p.crypto.eddsa.spec.EdDSAPrivateKeySpec;
12
+ import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec;
13
+ import org.jruby.Ruby;
14
+ import org.jruby.RubyModule;
15
+ import org.jruby.RubyString;
16
+ import org.jruby.anno.JRubyMethod;
17
+ import org.jruby.anno.JRubyModule;
18
+ import org.jruby.runtime.ThreadContext;
19
+ import org.jruby.runtime.builtin.IRubyObject;
20
+
21
+ @JRubyModule(name="Ed25519::Provider::JRuby")
22
+ public class Ed25519Provider {
23
+ public static RubyModule createEd25519Module(Ruby runtime) {
24
+ RubyModule mEd25519 = runtime.defineModule("Ed25519");
25
+ RubyModule mEd25519Provider = mEd25519.defineModuleUnder("Provider");
26
+ RubyModule mEd25519ProviderJRuby = mEd25519Provider.defineOrGetModuleUnder("JRuby");
27
+ mEd25519ProviderJRuby.defineAnnotatedMethods(Ed25519Provider.class);
28
+
29
+ return mEd25519ProviderJRuby;
30
+ }
31
+
32
+ @JRubyMethod(name = "create_keypair", module = true)
33
+ public static IRubyObject create_keypair(ThreadContext context, IRubyObject self, IRubyObject seed) {
34
+ byte[] seedBytes = seed.convertToString().getByteList().bytes();
35
+
36
+ if (seedBytes.length != 32) {
37
+ throw context.runtime.newArgumentError("expected 32-byte seed value, got " + seedBytes.length);
38
+ }
39
+
40
+ EdDSAParameterSpec edParams = EdDSANamedCurveTable.getByName(EdDSANamedCurveTable.ED_25519);
41
+ EdDSAPrivateKeySpec signingKey = new EdDSAPrivateKeySpec(seedBytes, edParams);
42
+ EdDSAPublicKeySpec verifyKey = new EdDSAPublicKeySpec(signingKey.getA(), edParams);
43
+
44
+ byte[] keypair = new byte[64];
45
+
46
+ System.arraycopy(seedBytes, 0, keypair, 0, 32);
47
+ System.arraycopy(verifyKey.getA().toByteArray(), 0, keypair, 32, 32);
48
+
49
+ return RubyString.newString(context.getRuntime(), keypair);
50
+ }
51
+
52
+ @JRubyMethod(name = "sign", module = true)
53
+ public static IRubyObject sign(ThreadContext context, IRubyObject self, IRubyObject keypair, IRubyObject msg) throws Exception {
54
+ byte[] keypairBytes = keypair.convertToString().getByteList().bytes();
55
+
56
+ if (keypairBytes.length != 64) {
57
+ throw context.runtime.newArgumentError("expected 64-byte keypair value, got " + keypairBytes.length);
58
+ }
59
+
60
+ EdDSAParameterSpec edParams = EdDSANamedCurveTable.getByName(EdDSANamedCurveTable.ED_25519);
61
+ Signature signer = new EdDSAEngine(MessageDigest.getInstance(edParams.getHashAlgorithm()));
62
+
63
+ byte[] seedBytes = Arrays.copyOfRange(keypairBytes, 0, 32);
64
+ EdDSAPrivateKeySpec signingKey = new EdDSAPrivateKeySpec(seedBytes, edParams);
65
+
66
+ signer.initSign(new EdDSAPrivateKey(signingKey));
67
+ signer.update(msg.convertToString().getByteList().bytes());
68
+
69
+ return RubyString.newString(context.getRuntime(), signer.sign());
70
+ }
71
+
72
+ @JRubyMethod(name = "verify", module = true)
73
+ public static IRubyObject verify(ThreadContext context, IRubyObject self, IRubyObject verify_key, IRubyObject signature, IRubyObject msg) throws Exception {
74
+ byte[] verifyKeyBytes = verify_key.convertToString().getByteList().bytes();
75
+ byte[] signatureBytes = signature.convertToString().getByteList().bytes();
76
+
77
+ if (verifyKeyBytes.length != 32) {
78
+ throw context.runtime.newArgumentError("expected 32-byte verify key, got " + verifyKeyBytes.length);
79
+ }
80
+
81
+ if (signatureBytes.length != 64) {
82
+ throw context.runtime.newArgumentError("expected 64-byte signature, got " + signatureBytes.length);
83
+ }
84
+
85
+ EdDSAParameterSpec edParams = EdDSANamedCurveTable.getByName(EdDSANamedCurveTable.ED_25519);
86
+ Signature signer = new EdDSAEngine(MessageDigest.getInstance(edParams.getHashAlgorithm()));
87
+ EdDSAPublicKeySpec verifyKey = new EdDSAPublicKeySpec(verifyKeyBytes, edParams);
88
+
89
+ signer.initVerify(new EdDSAPublicKey(verifyKey));
90
+ signer.update(msg.convertToString().getByteList().bytes());
91
+
92
+ boolean isValid = signer.verify(signatureBytes);
93
+ return context.runtime.newBoolean(isValid);
94
+ }
95
+ }