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,31 @@
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;
13
+
14
+ import net.i2p.crypto.eddsa.spec.EdDSAParameterSpec;
15
+
16
+ /**
17
+ * Common interface for all EdDSA keys.
18
+ * @author str4d
19
+ */
20
+ public interface EdDSAKey {
21
+ /**
22
+ * The reported key algorithm for all EdDSA keys
23
+ */
24
+ String KEY_ALGORITHM = "EdDSA";
25
+
26
+ /**
27
+ * @return a parameter specification representing the EdDSA domain
28
+ * parameters for the key.
29
+ */
30
+ EdDSAParameterSpec getParams();
31
+ }
@@ -0,0 +1,338 @@
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;
13
+
14
+ import java.security.PrivateKey;
15
+ import java.security.spec.InvalidKeySpecException;
16
+ import java.security.spec.PKCS8EncodedKeySpec;
17
+ import java.util.Arrays;
18
+
19
+ import net.i2p.crypto.eddsa.math.GroupElement;
20
+ import net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable;
21
+ import net.i2p.crypto.eddsa.spec.EdDSAParameterSpec;
22
+ import net.i2p.crypto.eddsa.spec.EdDSAPrivateKeySpec;
23
+
24
+ /**
25
+ * An EdDSA private key.
26
+ *<p>
27
+ * Warning: Private key encoding is based on the current curdle WG draft,
28
+ * and is subject to change. See getEncoded().
29
+ *</p><p>
30
+ * For compatibility with older releases, decoding supports both the old and new
31
+ * draft specifications. See decode().
32
+ *</p><p>
33
+ * Ref: https://tools.ietf.org/html/draft-ietf-curdle-pkix-04
34
+ *</p><p>
35
+ * Old Ref: https://tools.ietf.org/html/draft-josefsson-pkix-eddsa-04
36
+ *</p>
37
+ * @author str4d
38
+ *
39
+ */
40
+ public class EdDSAPrivateKey implements EdDSAKey, PrivateKey {
41
+ private static final long serialVersionUID = 23495873459878957L;
42
+ private final byte[] seed;
43
+ private final byte[] h;
44
+ private final byte[] a;
45
+ private final GroupElement A;
46
+ private final byte[] Abyte;
47
+ private final EdDSAParameterSpec edDsaSpec;
48
+
49
+ // OID 1.3.101.xxx
50
+ private static final int OID_OLD = 100;
51
+ private static final int OID_ED25519 = 112;
52
+ private static final int OID_BYTE = 11;
53
+ private static final int IDLEN_BYTE = 6;
54
+
55
+ public EdDSAPrivateKey(EdDSAPrivateKeySpec spec) {
56
+ this.seed = spec.getSeed();
57
+ this.h = spec.getH();
58
+ this.a = spec.geta();
59
+ this.A = spec.getA();
60
+ this.Abyte = this.A.toByteArray();
61
+ this.edDsaSpec = spec.getParams();
62
+ }
63
+
64
+ public EdDSAPrivateKey(PKCS8EncodedKeySpec spec) throws InvalidKeySpecException {
65
+ this(new EdDSAPrivateKeySpec(decode(spec.getEncoded()),
66
+ EdDSANamedCurveTable.getByName(EdDSANamedCurveTable.ED_25519)));
67
+ }
68
+
69
+ @Override
70
+ public String getAlgorithm() {
71
+ return KEY_ALGORITHM;
72
+ }
73
+
74
+ @Override
75
+ public String getFormat() {
76
+ return "PKCS#8";
77
+ }
78
+
79
+ /**
80
+ * Returns the public key in its canonical encoding.
81
+ *<p>
82
+ * This implements the following specs:
83
+ *<ul><li>
84
+ * General encoding: https://tools.ietf.org/html/draft-ietf-curdle-pkix-04
85
+ *</li></li>
86
+ * Key encoding: https://tools.ietf.org/html/rfc8032
87
+ *</li></ul>
88
+ *</p><p>
89
+ * This encodes the seed. It will return null if constructed from
90
+ * a spec which was directly constructed from H, in which case seed is null.
91
+ *</p><p>
92
+ * For keys in older formats, decoding and then re-encoding is sufficient to
93
+ * migrate them to the canonical encoding.
94
+ *</p>
95
+ * Relevant spec quotes:
96
+ *<pre>
97
+ * OneAsymmetricKey ::= SEQUENCE {
98
+ * version Version,
99
+ * privateKeyAlgorithm PrivateKeyAlgorithmIdentifier,
100
+ * privateKey PrivateKey,
101
+ * attributes [0] Attributes OPTIONAL,
102
+ * ...,
103
+ * [[2: publicKey [1] PublicKey OPTIONAL ]],
104
+ * ...
105
+ * }
106
+ *
107
+ * Version ::= INTEGER
108
+ * PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier
109
+ * PrivateKey ::= OCTET STRING
110
+ * PublicKey ::= OCTET STRING
111
+ * Attributes ::= SET OF Attribute
112
+ *</pre>
113
+ *
114
+ *<pre>
115
+ * ... when encoding a OneAsymmetricKey object, the private key is wrapped
116
+ * in a CurvePrivateKey object and wrapped by the OCTET STRING of the
117
+ * 'privateKey' field.
118
+ *
119
+ * CurvePrivateKey ::= OCTET STRING
120
+ *</pre>
121
+ *
122
+ *<pre>
123
+ * AlgorithmIdentifier ::= SEQUENCE {
124
+ * algorithm OBJECT IDENTIFIER,
125
+ * parameters ANY DEFINED BY algorithm OPTIONAL
126
+ * }
127
+ *
128
+ * For all of the OIDs, the parameters MUST be absent.
129
+ *</pre>
130
+ *
131
+ *<pre>
132
+ * id-Ed25519 OBJECT IDENTIFIER ::= { 1 3 101 112 }
133
+ *</pre>
134
+ *
135
+ * @return 48 bytes for Ed25519, null for other curves
136
+ */
137
+ @Override
138
+ public byte[] getEncoded() {
139
+ if (!edDsaSpec.equals(EdDSANamedCurveTable.getByName(EdDSANamedCurveTable.ED_25519)))
140
+ return null;
141
+ if (seed == null)
142
+ return null;
143
+ int totlen = 16 + seed.length;
144
+ byte[] rv = new byte[totlen];
145
+ int idx = 0;
146
+ // sequence
147
+ rv[idx++] = 0x30;
148
+ rv[idx++] = (byte) (totlen - 2);
149
+ // version
150
+ rv[idx++] = 0x02;
151
+ rv[idx++] = 1;
152
+ // v1 - no public key included
153
+ rv[idx++] = 0;
154
+ // Algorithm Identifier
155
+ // sequence
156
+ rv[idx++] = 0x30;
157
+ rv[idx++] = 5;
158
+ // OID
159
+ // https://msdn.microsoft.com/en-us/library/windows/desktop/bb540809%28v=vs.85%29.aspx
160
+ rv[idx++] = 0x06;
161
+ rv[idx++] = 3;
162
+ rv[idx++] = (1 * 40) + 3;
163
+ rv[idx++] = 101;
164
+ rv[idx++] = (byte) OID_ED25519;
165
+ // params - absent
166
+ // PrivateKey
167
+ rv[idx++] = 0x04; // octet string
168
+ rv[idx++] = (byte) (2 + seed.length);
169
+ // CurvePrivateKey
170
+ rv[idx++] = 0x04; // octet string
171
+ rv[idx++] = (byte) seed.length;
172
+ // the key
173
+ System.arraycopy(seed, 0, rv, idx, seed.length);
174
+ return rv;
175
+ }
176
+
177
+ /**
178
+ * Extracts the private key bytes from the provided encoding.
179
+ *<p>
180
+ * This will decode data conforming to the current spec at
181
+ * https://tools.ietf.org/html/draft-ietf-curdle-pkix-04
182
+ * or as inferred from the old spec at
183
+ * https://tools.ietf.org/html/draft-josefsson-pkix-eddsa-04.
184
+ *</p><p>
185
+ * Contrary to draft-ietf-curdle-pkix-04, it WILL accept a parameter value
186
+ * of NULL, as it is required for interoperability with the default Java
187
+ * keystore. Other implementations MUST NOT copy this behaviour from here
188
+ * unless they also need to read keys from the default Java keystore.
189
+ *</p><p>
190
+ * This is really dumb for now. It does not use a general-purpose ASN.1 decoder.
191
+ * See also getEncoded().
192
+ *
193
+ * @return 32 bytes for Ed25519, throws for other curves
194
+ */
195
+ private static byte[] decode(byte[] d) throws InvalidKeySpecException {
196
+ try {
197
+ //
198
+ // Setup and OID check
199
+ //
200
+ int totlen = 48;
201
+ int idlen = 5;
202
+ int doid = d[OID_BYTE];
203
+ if (doid == OID_OLD) {
204
+ totlen = 49;
205
+ idlen = 8;
206
+ } else if (doid == OID_ED25519) {
207
+ // Detect parameter value of NULL
208
+ if (d[IDLEN_BYTE] == 7) {
209
+ totlen = 50;
210
+ idlen = 7;
211
+ }
212
+ } else {
213
+ throw new InvalidKeySpecException("unsupported key spec");
214
+ }
215
+
216
+ //
217
+ // Pre-decoding check
218
+ //
219
+ if (d.length != totlen) {
220
+ throw new InvalidKeySpecException("invalid key spec length");
221
+ }
222
+
223
+ //
224
+ // Decoding
225
+ //
226
+ int idx = 0;
227
+ if (d[idx++] != 0x30 ||
228
+ d[idx++] != (totlen - 2) ||
229
+ d[idx++] != 0x02 ||
230
+ d[idx++] != 1 ||
231
+ d[idx++] != 0 ||
232
+ d[idx++] != 0x30 ||
233
+ d[idx++] != idlen ||
234
+ d[idx++] != 0x06 ||
235
+ d[idx++] != 3 ||
236
+ d[idx++] != (1 * 40) + 3 ||
237
+ d[idx++] != 101) {
238
+ throw new InvalidKeySpecException("unsupported key spec");
239
+ }
240
+ idx++; // OID, checked above
241
+ // parameters only with old OID
242
+ if (doid == OID_OLD) {
243
+ if (d[idx++] != 0x0a ||
244
+ d[idx++] != 1 ||
245
+ d[idx++] != 1) {
246
+ throw new InvalidKeySpecException("unsupported key spec");
247
+ }
248
+ } else {
249
+ // Handle parameter value of NULL
250
+ //
251
+ // Quote https://tools.ietf.org/html/draft-ietf-curdle-pkix-04 :
252
+ // For all of the OIDs, the parameters MUST be absent.
253
+ // Regardless of the defect in the original 1997 syntax,
254
+ // implementations MUST NOT accept a parameters value of NULL.
255
+ //
256
+ // But Java's default keystore puts it in (when decoding as
257
+ // PKCS8 and then re-encoding to pass on), so we must accept it.
258
+ if (idlen == 7) {
259
+ if (d[idx++] != 0x05 ||
260
+ d[idx++] != 0) {
261
+ throw new InvalidKeySpecException("unsupported key spec");
262
+ }
263
+ }
264
+ // PrivateKey wrapping the CurvePrivateKey
265
+ if (d[idx++] != 0x04 ||
266
+ d[idx++] != 34) {
267
+ throw new InvalidKeySpecException("unsupported key spec");
268
+ }
269
+ }
270
+ if (d[idx++] != 0x04 ||
271
+ d[idx++] != 32) {
272
+ throw new InvalidKeySpecException("unsupported key spec");
273
+ }
274
+ byte[] rv = new byte[32];
275
+ System.arraycopy(d, idx, rv, 0, 32);
276
+ return rv;
277
+ } catch (IndexOutOfBoundsException ioobe) {
278
+ throw new InvalidKeySpecException(ioobe);
279
+ }
280
+ }
281
+
282
+ @Override
283
+ public EdDSAParameterSpec getParams() {
284
+ return edDsaSpec;
285
+ }
286
+
287
+ /**
288
+ * @return will be null if constructed from a spec which was
289
+ * directly constructed from H
290
+ */
291
+ public byte[] getSeed() {
292
+ return seed;
293
+ }
294
+
295
+ /**
296
+ * @return the hash of the seed
297
+ */
298
+ public byte[] getH() {
299
+ return h;
300
+ }
301
+
302
+ /**
303
+ * @return the private key
304
+ */
305
+ public byte[] geta() {
306
+ return a;
307
+ }
308
+
309
+ /**
310
+ * @return the public key
311
+ */
312
+ public GroupElement getA() {
313
+ return A;
314
+ }
315
+
316
+ /**
317
+ * @return the public key
318
+ */
319
+ public byte[] getAbyte() {
320
+ return Abyte;
321
+ }
322
+
323
+ @Override
324
+ public int hashCode() {
325
+ return Arrays.hashCode(seed);
326
+ }
327
+
328
+ @Override
329
+ public boolean equals(Object o) {
330
+ if (o == this)
331
+ return true;
332
+ if (!(o instanceof EdDSAPrivateKey))
333
+ return false;
334
+ EdDSAPrivateKey pk = (EdDSAPrivateKey) o;
335
+ return Arrays.equals(seed, pk.getSeed()) &&
336
+ edDsaSpec.equals(pk.getParams());
337
+ }
338
+ }
@@ -0,0 +1,275 @@
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;
13
+
14
+ import java.security.PublicKey;
15
+ import java.security.spec.InvalidKeySpecException;
16
+ import java.security.spec.X509EncodedKeySpec;
17
+ import java.util.Arrays;
18
+
19
+ import net.i2p.crypto.eddsa.math.GroupElement;
20
+ import net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable;
21
+ import net.i2p.crypto.eddsa.spec.EdDSAParameterSpec;
22
+ import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec;
23
+
24
+ /**
25
+ * An EdDSA public key.
26
+ *<p>
27
+ * Warning: Public key encoding is is based on the current curdle WG draft,
28
+ * and is subject to change. See getEncoded().
29
+ *</p><p>
30
+ * For compatibility with older releases, decoding supports both the old and new
31
+ * draft specifications. See decode().
32
+ *</p><p>
33
+ * Ref: https://tools.ietf.org/html/draft-ietf-curdle-pkix-04
34
+ *</p><p>
35
+ * Old Ref: https://tools.ietf.org/html/draft-josefsson-pkix-eddsa-04
36
+ *</p>
37
+ * @author str4d
38
+ *
39
+ */
40
+ public class EdDSAPublicKey implements EdDSAKey, PublicKey {
41
+ private static final long serialVersionUID = 9837459837498475L;
42
+ private final GroupElement A;
43
+ private final GroupElement Aneg;
44
+ private final byte[] Abyte;
45
+ private final EdDSAParameterSpec edDsaSpec;
46
+
47
+ // OID 1.3.101.xxx
48
+ private static final int OID_OLD = 100;
49
+ private static final int OID_ED25519 = 112;
50
+ private static final int OID_BYTE = 8;
51
+ private static final int IDLEN_BYTE = 3;
52
+
53
+ public EdDSAPublicKey(EdDSAPublicKeySpec spec) {
54
+ this.A = spec.getA();
55
+ this.Aneg = spec.getNegativeA();
56
+ this.Abyte = this.A.toByteArray();
57
+ this.edDsaSpec = spec.getParams();
58
+ }
59
+
60
+ public EdDSAPublicKey(X509EncodedKeySpec spec) throws InvalidKeySpecException {
61
+ this(new EdDSAPublicKeySpec(decode(spec.getEncoded()),
62
+ EdDSANamedCurveTable.getByName(EdDSANamedCurveTable.ED_25519)));
63
+ }
64
+
65
+ @Override
66
+ public String getAlgorithm() {
67
+ return KEY_ALGORITHM;
68
+ }
69
+
70
+ @Override
71
+ public String getFormat() {
72
+ return "X.509";
73
+ }
74
+
75
+ /**
76
+ * Returns the public key in its canonical encoding.
77
+ *<p>
78
+ * This implements the following specs:
79
+ *<ul><li>
80
+ * General encoding: https://tools.ietf.org/html/draft-ietf-curdle-pkix-04
81
+ *</li></li>
82
+ * Key encoding: https://tools.ietf.org/html/rfc8032
83
+ *</li></ul>
84
+ *</p><p>
85
+ * For keys in older formats, decoding and then re-encoding is sufficient to
86
+ * migrate them to the canonical encoding.
87
+ *</p>
88
+ * Relevant spec quotes:
89
+ *<pre>
90
+ * In the X.509 certificate, the subjectPublicKeyInfo field has the
91
+ * SubjectPublicKeyInfo type, which has the following ASN.1 syntax:
92
+ *
93
+ * SubjectPublicKeyInfo ::= SEQUENCE {
94
+ * algorithm AlgorithmIdentifier,
95
+ * subjectPublicKey BIT STRING
96
+ * }
97
+ *</pre>
98
+ *
99
+ *<pre>
100
+ * AlgorithmIdentifier ::= SEQUENCE {
101
+ * algorithm OBJECT IDENTIFIER,
102
+ * parameters ANY DEFINED BY algorithm OPTIONAL
103
+ * }
104
+ *
105
+ * For all of the OIDs, the parameters MUST be absent.
106
+ *</pre>
107
+ *
108
+ *<pre>
109
+ * id-Ed25519 OBJECT IDENTIFIER ::= { 1 3 101 112 }
110
+ *</pre>
111
+ *
112
+ * @return 44 bytes for Ed25519, null for other curves
113
+ */
114
+ @Override
115
+ public byte[] getEncoded() {
116
+ if (!edDsaSpec.equals(EdDSANamedCurveTable.getByName(EdDSANamedCurveTable.ED_25519)))
117
+ return null;
118
+ int totlen = 12 + Abyte.length;
119
+ byte[] rv = new byte[totlen];
120
+ int idx = 0;
121
+ // sequence
122
+ rv[idx++] = 0x30;
123
+ rv[idx++] = (byte) (totlen - 2);
124
+ // Algorithm Identifier
125
+ // sequence
126
+ rv[idx++] = 0x30;
127
+ rv[idx++] = 5;
128
+ // OID
129
+ // https://msdn.microsoft.com/en-us/library/windows/desktop/bb540809%28v=vs.85%29.aspx
130
+ rv[idx++] = 0x06;
131
+ rv[idx++] = 3;
132
+ rv[idx++] = (1 * 40) + 3;
133
+ rv[idx++] = 101;
134
+ rv[idx++] = (byte) OID_ED25519;
135
+ // params - absent
136
+ // the key
137
+ rv[idx++] = 0x03; // bit string
138
+ rv[idx++] = (byte) (1 + Abyte.length);
139
+ rv[idx++] = 0; // number of trailing unused bits
140
+ System.arraycopy(Abyte, 0, rv, idx, Abyte.length);
141
+ return rv;
142
+ }
143
+
144
+ /**
145
+ * Extracts the public key bytes from the provided encoding.
146
+ *<p>
147
+ * This will decode data conforming to the current spec at
148
+ * https://tools.ietf.org/html/draft-ietf-curdle-pkix-04
149
+ * or the old spec at
150
+ * https://tools.ietf.org/html/draft-josefsson-pkix-eddsa-04.
151
+ *</p><p>
152
+ * Contrary to draft-ietf-curdle-pkix-04, it WILL accept a parameter value
153
+ * of NULL, as it is required for interoperability with the default Java
154
+ * keystore. Other implementations MUST NOT copy this behaviour from here
155
+ * unless they also need to read keys from the default Java keystore.
156
+ *</p><p>
157
+ * This is really dumb for now. It does not use a general-purpose ASN.1 decoder.
158
+ * See also getEncoded().
159
+ *</p>
160
+ *
161
+ * @return 32 bytes for Ed25519, throws for other curves
162
+ */
163
+ private static byte[] decode(byte[] d) throws InvalidKeySpecException {
164
+ try {
165
+ //
166
+ // Setup and OID check
167
+ //
168
+ int totlen = 44;
169
+ int idlen = 5;
170
+ int doid = d[OID_BYTE];
171
+ if (doid == OID_OLD) {
172
+ totlen = 47;
173
+ idlen = 8;
174
+ } else if (doid == OID_ED25519) {
175
+ // Detect parameter value of NULL
176
+ if (d[IDLEN_BYTE] == 7) {
177
+ totlen = 46;
178
+ idlen = 7;
179
+ }
180
+ } else {
181
+ throw new InvalidKeySpecException("unsupported key spec");
182
+ }
183
+
184
+ //
185
+ // Pre-decoding check
186
+ //
187
+ if (d.length != totlen) {
188
+ throw new InvalidKeySpecException("invalid key spec length");
189
+ }
190
+
191
+ //
192
+ // Decoding
193
+ //
194
+ int idx = 0;
195
+ if (d[idx++] != 0x30 ||
196
+ d[idx++] != (totlen - 2) ||
197
+ d[idx++] != 0x30 ||
198
+ d[idx++] != idlen ||
199
+ d[idx++] != 0x06 ||
200
+ d[idx++] != 3 ||
201
+ d[idx++] != (1 * 40) + 3 ||
202
+ d[idx++] != 101) {
203
+ throw new InvalidKeySpecException("unsupported key spec");
204
+ }
205
+ idx++; // OID, checked above
206
+ // parameters only with old OID
207
+ if (doid == OID_OLD) {
208
+ if (d[idx++] != 0x0a ||
209
+ d[idx++] != 1 ||
210
+ d[idx++] != 1) {
211
+ throw new InvalidKeySpecException("unsupported key spec");
212
+ }
213
+ } else {
214
+ // Handle parameter value of NULL
215
+ //
216
+ // Quote https://tools.ietf.org/html/draft-ietf-curdle-pkix-04 :
217
+ // For all of the OIDs, the parameters MUST be absent.
218
+ // Regardless of the defect in the original 1997 syntax,
219
+ // implementations MUST NOT accept a parameters value of NULL.
220
+ //
221
+ // But Java's default keystore puts it in (when decoding as
222
+ // PKCS8 and then re-encoding to pass on), so we must accept it.
223
+ if (idlen == 7) {
224
+ if (d[idx++] != 0x05 ||
225
+ d[idx++] != 0) {
226
+ throw new InvalidKeySpecException("unsupported key spec");
227
+ }
228
+ }
229
+ }
230
+ if (d[idx++] != 0x03 ||
231
+ d[idx++] != 33 ||
232
+ d[idx++] != 0) {
233
+ throw new InvalidKeySpecException("unsupported key spec");
234
+ }
235
+ byte[] rv = new byte[32];
236
+ System.arraycopy(d, idx, rv, 0, 32);
237
+ return rv;
238
+ } catch (IndexOutOfBoundsException ioobe) {
239
+ throw new InvalidKeySpecException(ioobe);
240
+ }
241
+ }
242
+
243
+ @Override
244
+ public EdDSAParameterSpec getParams() {
245
+ return edDsaSpec;
246
+ }
247
+
248
+ public GroupElement getA() {
249
+ return A;
250
+ }
251
+
252
+ public GroupElement getNegativeA() {
253
+ return Aneg;
254
+ }
255
+
256
+ public byte[] getAbyte() {
257
+ return Abyte;
258
+ }
259
+
260
+ @Override
261
+ public int hashCode() {
262
+ return Arrays.hashCode(Abyte);
263
+ }
264
+
265
+ @Override
266
+ public boolean equals(Object o) {
267
+ if (o == this)
268
+ return true;
269
+ if (!(o instanceof EdDSAPublicKey))
270
+ return false;
271
+ EdDSAPublicKey pk = (EdDSAPublicKey) o;
272
+ return Arrays.equals(Abyte, pk.getAbyte()) &&
273
+ edDsaSpec.equals(pk.getParams());
274
+ }
275
+ }