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.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/CHANGES.md +13 -0
- data/README.md +9 -9
- data/Rakefile +3 -3
- data/ext/ed25519_jruby/LICENSE.txt +123 -0
- data/ext/ed25519_jruby/README.md +77 -0
- data/ext/ed25519_jruby/net/i2p/crypto/eddsa/EdDSAEngine.java +491 -0
- data/ext/ed25519_jruby/net/i2p/crypto/eddsa/EdDSAKey.java +31 -0
- data/ext/ed25519_jruby/net/i2p/crypto/eddsa/EdDSAPrivateKey.java +338 -0
- data/ext/ed25519_jruby/net/i2p/crypto/eddsa/EdDSAPublicKey.java +275 -0
- data/ext/ed25519_jruby/net/i2p/crypto/eddsa/EdDSASecurityProvider.java +59 -0
- data/ext/ed25519_jruby/net/i2p/crypto/eddsa/KeyFactory.java +75 -0
- data/ext/ed25519_jruby/net/i2p/crypto/eddsa/KeyPairGenerator.java +97 -0
- data/ext/ed25519_jruby/net/i2p/crypto/eddsa/Utils.java +103 -0
- data/ext/ed25519_jruby/net/i2p/crypto/eddsa/math/Constants.java +23 -0
- data/ext/ed25519_jruby/net/i2p/crypto/eddsa/math/Curve.java +100 -0
- data/ext/ed25519_jruby/net/i2p/crypto/eddsa/math/Encoding.java +54 -0
- data/ext/ed25519_jruby/net/i2p/crypto/eddsa/math/Field.java +99 -0
- data/ext/ed25519_jruby/net/i2p/crypto/eddsa/math/FieldElement.java +76 -0
- data/ext/ed25519_jruby/net/i2p/crypto/eddsa/math/GroupElement.java +1034 -0
- data/ext/ed25519_jruby/net/i2p/crypto/eddsa/math/ScalarOps.java +34 -0
- data/ext/ed25519_jruby/net/i2p/crypto/eddsa/math/bigint/BigIntegerFieldElement.java +131 -0
- data/ext/ed25519_jruby/net/i2p/crypto/eddsa/math/bigint/BigIntegerLittleEndianEncoding.java +102 -0
- data/ext/ed25519_jruby/net/i2p/crypto/eddsa/math/bigint/BigIntegerScalarOps.java +37 -0
- data/ext/ed25519_jruby/net/i2p/crypto/eddsa/math/bigint/package.html +6 -0
- data/ext/ed25519_jruby/net/i2p/crypto/eddsa/math/ed25519/Ed25519FieldElement.java +988 -0
- data/ext/ed25519_jruby/net/i2p/crypto/eddsa/math/ed25519/Ed25519LittleEndianEncoding.java +256 -0
- data/ext/ed25519_jruby/net/i2p/crypto/eddsa/math/ed25519/Ed25519ScalarOps.java +693 -0
- data/ext/ed25519_jruby/net/i2p/crypto/eddsa/spec/EdDSAGenParameterSpec.java +32 -0
- data/ext/ed25519_jruby/net/i2p/crypto/eddsa/spec/EdDSANamedCurveSpec.java +35 -0
- data/ext/ed25519_jruby/net/i2p/crypto/eddsa/spec/EdDSANamedCurveTable.java +71 -0
- data/ext/ed25519_jruby/net/i2p/crypto/eddsa/spec/EdDSAParameterSpec.java +97 -0
- data/ext/ed25519_jruby/net/i2p/crypto/eddsa/spec/EdDSAPrivateKeySpec.java +133 -0
- data/ext/ed25519_jruby/net/i2p/crypto/eddsa/spec/EdDSAPublicKeySpec.java +61 -0
- data/ext/ed25519_jruby/org/cryptosphere/Ed25519Provider.java +95 -0
- data/lib/ed25519.rb +8 -8
- data/lib/ed25519/signing_key.rb +9 -0
- data/lib/ed25519/version.rb +1 -1
- data/lib/ed25519_java.jar +0 -0
- metadata +32 -3
- data/ext/ed25519_java/org/cryptosphere/ed25519.java +0 -228
- data/lib/ed25519/provider/jruby.rb +0 -39
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ffe9a55bb4a20efc99365fc6ed06329de947ce39
|
4
|
+
data.tar.gz: 7913e3cd83720eb011f69069512f3791f1878839
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3994f501b34ca888050eb0a7e7620a8e3570683d0207c21716c3c5ac27b063133f452fca7d0c658027cd00eeb15d7ed01cf7fcbdfa930791e7354f8c04933c46
|
7
|
+
data.tar.gz: 184956a2e7e6a414502d3b56fe814e216b622587789f1959b24d55d2c1b329740072a040b8f873385f146bd72da7d853c024a30ca100355c81b35b38b485fcc9
|
data/.gitignore
CHANGED
data/CHANGES.md
CHANGED
@@ -1,3 +1,16 @@
|
|
1
|
+
# [1.1.0] (2017-12-13)
|
2
|
+
|
3
|
+
[1.1.0]: https://github.com/cryptosphere/x25519/compare/v1.0.0...v1.1.0
|
4
|
+
|
5
|
+
* [#11](https://github.com/cryptosphere/ed25519/pull/11)
|
6
|
+
ext/ed25519_java: switch to str4d/ed25519-java implementation (fixes #4)
|
7
|
+
|
8
|
+
* [#9](https://github.com/cryptosphere/ed25519/pull/9)
|
9
|
+
Implement Java backend as a proper JRuby extension
|
10
|
+
|
11
|
+
* [#8](https://github.com/cryptosphere/ed25519/pull/8)
|
12
|
+
Use an attr_accessor for Ed25519.provider
|
13
|
+
|
1
14
|
# [1.0.0] (2017-12-12)
|
2
15
|
|
3
16
|
[1.0.0]: https://github.com/cryptosphere/x25519/compare/v0.1.0...v1.0.0
|
data/README.md
CHANGED
@@ -14,9 +14,16 @@ described in [RFC 8032].
|
|
14
14
|
|
15
15
|
Two implementations are provided: a MRI C extension which uses the "ref10"
|
16
16
|
implementation from the SUPERCOP benchmark suite, and a pure Java version
|
17
|
-
|
17
|
+
based on [str4d/ed25519-java].
|
18
|
+
|
19
|
+
Ed25519 is one of two notable algorithms implemented atop the Curve25519
|
20
|
+
elliptic curve. The [x25519 gem] is a related project of this one,
|
21
|
+
and implements the X25519 Diffie-Hellman key exchange algorithm on the
|
22
|
+
Montgomery form of Curve25519.
|
18
23
|
|
19
24
|
[RFC 8032]: https://tools.ietf.org/html/rfc8032
|
25
|
+
[str4d/ed25519-java]: https://github.com/str4d/ed25519-java
|
26
|
+
[x25519 gem]: https://github.com/cryptosphere/x25519
|
20
27
|
|
21
28
|
## What is Ed25519?
|
22
29
|
|
@@ -118,7 +125,7 @@ verify_key = Ed25519::VerifyKey.new(verify_key_bytes)
|
|
118
125
|
|
119
126
|
## Security Notes
|
120
127
|
|
121
|
-
The Ed25519 "
|
128
|
+
The Ed25519 "ref10" implementation from SUPERCOP was lovingly crafted by expert
|
122
129
|
security boffins with great care taken to prevent timing attacks. The same
|
123
130
|
cannot be said for the C code used in the **ed25519.rb** C extension or in the
|
124
131
|
entirety of the provided Java implementation.
|
@@ -130,13 +137,6 @@ by experts who can fix any potential timing vulnerabilities)
|
|
130
137
|
**ed25519.rb** relies on a strong `SecureRandom` for key generation.
|
131
138
|
Weaknesses in the random number source can potentially result in insecure keys.
|
132
139
|
|
133
|
-
## JRuby Notes
|
134
|
-
|
135
|
-
**ed25519.rb** provides a pure Java backend, however this backend is much slower
|
136
|
-
than the C-based version. While **ed25519.rb** will function on JRuby, it may be
|
137
|
-
too slow to be usable for a given use case. You should benchmark your
|
138
|
-
application to determine if it will be fast enough for your purposes.
|
139
|
-
|
140
140
|
## Contributing
|
141
141
|
|
142
142
|
Bug reports and pull requests are welcome on GitHub at https://github.com/cryptosphere/ed25519.
|
data/Rakefile
CHANGED
@@ -3,12 +3,12 @@
|
|
3
3
|
require "bundler/gem_tasks"
|
4
4
|
|
5
5
|
require "rake/clean"
|
6
|
-
CLEAN.include("**/*.o", "**/*.so", "**/*.bundle", "pkg", "tmp")
|
6
|
+
CLEAN.include("**/*.o", "**/*.so", "**/*.bundle", "*.jar", "pkg", "tmp")
|
7
7
|
|
8
8
|
if defined? JRUBY_VERSION
|
9
9
|
require "rake/javaextensiontask"
|
10
|
-
Rake::JavaExtensionTask.new("
|
11
|
-
ext.ext_dir = "ext/
|
10
|
+
Rake::JavaExtensionTask.new("ed25519_jruby") do |ext|
|
11
|
+
ext.ext_dir = "ext/ed25519_jruby"
|
12
12
|
end
|
13
13
|
else
|
14
14
|
require "rake/extensiontask"
|
@@ -0,0 +1,123 @@
|
|
1
|
+
Creative Commons Legal Code
|
2
|
+
|
3
|
+
CC0 1.0 Universal
|
4
|
+
|
5
|
+
CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
|
6
|
+
LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
|
7
|
+
ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
|
8
|
+
INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
|
9
|
+
REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
|
10
|
+
PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
|
11
|
+
THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
|
12
|
+
HEREUNDER.
|
13
|
+
|
14
|
+
Statement of Purpose
|
15
|
+
|
16
|
+
The laws of most jurisdictions throughout the world automatically confer
|
17
|
+
exclusive Copyright and Related Rights (defined below) upon the creator
|
18
|
+
and subsequent owner(s) (each and all, an "owner") of an original work of
|
19
|
+
authorship and/or a database (each, a "Work").
|
20
|
+
|
21
|
+
Certain owners wish to permanently relinquish those rights to a Work for
|
22
|
+
the purpose of contributing to a commons of creative, cultural and
|
23
|
+
scientific works ("Commons") that the public can reliably and without fear
|
24
|
+
of later claims of infringement build upon, modify, incorporate in other
|
25
|
+
works, reuse and redistribute as freely as possible in any form whatsoever
|
26
|
+
and for any purposes, including without limitation commercial purposes.
|
27
|
+
These owners may contribute to the Commons to promote the ideal of a free
|
28
|
+
culture and the further production of creative, cultural and scientific
|
29
|
+
works, or to gain reputation or greater distribution for their Work in
|
30
|
+
part through the use and efforts of others.
|
31
|
+
|
32
|
+
For these and/or other purposes and motivations, and without any
|
33
|
+
expectation of additional consideration or compensation, the person
|
34
|
+
associating CC0 with a Work (the "Affirmer"), to the extent that he or she
|
35
|
+
is an owner of Copyright and Related Rights in the Work, voluntarily
|
36
|
+
elects to apply CC0 to the Work and publicly distribute the Work under its
|
37
|
+
terms, with knowledge of his or her Copyright and Related Rights in the
|
38
|
+
Work and the meaning and intended legal effect of CC0 on those rights.
|
39
|
+
|
40
|
+
1. Copyright and Related Rights. A Work made available under CC0 may be
|
41
|
+
protected by copyright and related or neighboring rights ("Copyright and
|
42
|
+
Related Rights"). Copyright and Related Rights include, but are not
|
43
|
+
limited to, the following:
|
44
|
+
|
45
|
+
i. the right to reproduce, adapt, distribute, perform, display,
|
46
|
+
communicate, and translate a Work;
|
47
|
+
ii. moral rights retained by the original author(s) and/or performer(s);
|
48
|
+
iii. publicity and privacy rights pertaining to a person's image or
|
49
|
+
likeness depicted in a Work;
|
50
|
+
iv. rights protecting against unfair competition in regards to a Work,
|
51
|
+
subject to the limitations in paragraph 4(a), below;
|
52
|
+
v. rights protecting the extraction, dissemination, use and reuse of data
|
53
|
+
in a Work;
|
54
|
+
vi. database rights (such as those arising under Directive 96/9/EC of the
|
55
|
+
European Parliament and of the Council of 11 March 1996 on the legal
|
56
|
+
protection of databases, and under any national implementation
|
57
|
+
thereof, including any amended or successor version of such
|
58
|
+
directive); and
|
59
|
+
vii. other similar, equivalent or corresponding rights throughout the
|
60
|
+
world based on applicable law or treaty, and any national
|
61
|
+
implementations thereof.
|
62
|
+
|
63
|
+
2. Waiver. To the greatest extent permitted by, but not in contravention
|
64
|
+
of, applicable law, Affirmer hereby overtly, fully, permanently,
|
65
|
+
irrevocably and unconditionally waives, abandons, and surrenders all of
|
66
|
+
Affirmer's Copyright and Related Rights and associated claims and causes
|
67
|
+
of action, whether now known or unknown (including existing as well as
|
68
|
+
future claims and causes of action), in the Work (i) in all territories
|
69
|
+
worldwide, (ii) for the maximum duration provided by applicable law or
|
70
|
+
treaty (including future time extensions), (iii) in any current or future
|
71
|
+
medium and for any number of copies, and (iv) for any purpose whatsoever,
|
72
|
+
including without limitation commercial, advertising or promotional
|
73
|
+
purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
|
74
|
+
member of the public at large and to the detriment of Affirmer's heirs and
|
75
|
+
successors, fully intending that such Waiver shall not be subject to
|
76
|
+
revocation, rescission, cancellation, termination, or any other legal or
|
77
|
+
equitable action to disrupt the quiet enjoyment of the Work by the public
|
78
|
+
as contemplated by Affirmer's express Statement of Purpose.
|
79
|
+
|
80
|
+
3. Public License Fallback. Should any part of the Waiver for any reason
|
81
|
+
be judged legally invalid or ineffective under applicable law, then the
|
82
|
+
Waiver shall be preserved to the maximum extent permitted taking into
|
83
|
+
account Affirmer's express Statement of Purpose. In addition, to the
|
84
|
+
extent the Waiver is so judged Affirmer hereby grants to each affected
|
85
|
+
person a royalty-free, non transferable, non sublicensable, non exclusive,
|
86
|
+
irrevocable and unconditional license to exercise Affirmer's Copyright and
|
87
|
+
Related Rights in the Work (i) in all territories worldwide, (ii) for the
|
88
|
+
maximum duration provided by applicable law or treaty (including future
|
89
|
+
time extensions), (iii) in any current or future medium and for any number
|
90
|
+
of copies, and (iv) for any purpose whatsoever, including without
|
91
|
+
limitation commercial, advertising or promotional purposes (the
|
92
|
+
"License"). The License shall be deemed effective as of the date CC0 was
|
93
|
+
applied by Affirmer to the Work. Should any part of the License for any
|
94
|
+
reason be judged legally invalid or ineffective under applicable law, such
|
95
|
+
partial invalidity or ineffectiveness shall not invalidate the remainder
|
96
|
+
of the License, and in such case Affirmer hereby affirms that he or she
|
97
|
+
will not (i) exercise any of his or her remaining Copyright and Related
|
98
|
+
Rights in the Work or (ii) assert any associated claims and causes of
|
99
|
+
action with respect to the Work, in either case contrary to Affirmer's
|
100
|
+
express Statement of Purpose.
|
101
|
+
|
102
|
+
4. Limitations and Disclaimers.
|
103
|
+
|
104
|
+
a. No trademark or patent rights held by Affirmer are waived, abandoned,
|
105
|
+
surrendered, licensed or otherwise affected by this document.
|
106
|
+
b. Affirmer offers the Work as-is and makes no representations or
|
107
|
+
warranties of any kind concerning the Work, express, implied,
|
108
|
+
statutory or otherwise, including without limitation warranties of
|
109
|
+
title, merchantability, fitness for a particular purpose, non
|
110
|
+
infringement, or the absence of latent or other defects, accuracy, or
|
111
|
+
the present or absence of errors, whether or not discoverable, all to
|
112
|
+
the greatest extent permissible under applicable law.
|
113
|
+
c. Affirmer disclaims responsibility for clearing rights of other persons
|
114
|
+
that may apply to the Work or any use thereof, including without
|
115
|
+
limitation any person's Copyright and Related Rights in the Work.
|
116
|
+
Further, Affirmer disclaims responsibility for obtaining any necessary
|
117
|
+
consents, permissions or other rights required for any use of the
|
118
|
+
Work.
|
119
|
+
d. Affirmer understands and acknowledges that Creative Commons is not a
|
120
|
+
party to this document and has no duty or obligation with respect to
|
121
|
+
this CC0 or use of the Work.
|
122
|
+
|
123
|
+
For more information, please see https://creativecommons.org/publicdomain/zero/1.0/
|
@@ -0,0 +1,77 @@
|
|
1
|
+
EdDSA-Java
|
2
|
+
==========
|
3
|
+
|
4
|
+
[![Build Status](https://travis-ci.org/str4d/ed25519-java.svg?branch=master)](https://travis-ci.org/str4d/ed25519-java)
|
5
|
+
|
6
|
+
This is an implementation of EdDSA in Java. Structurally, it is based on the ref10 implementation in SUPERCOP (see https://ed25519.cr.yp.to/software.html).
|
7
|
+
|
8
|
+
There are two internal implementations:
|
9
|
+
* A port of the radix-2^51 operations in ref10 - fast and constant-time, but only useful for Ed25519.
|
10
|
+
* A generic version using BigIntegers for calculation - a bit slower and not constant-time, but compatible with any EdDSA parameter specification.
|
11
|
+
|
12
|
+
|
13
|
+
To use
|
14
|
+
------
|
15
|
+
|
16
|
+
Download the latest .jar from the releases tab and place it in your classpath.
|
17
|
+
|
18
|
+
Gradle users:
|
19
|
+
|
20
|
+
```
|
21
|
+
compile 'net.i2p.crypto:eddsa:0.2.0'
|
22
|
+
```
|
23
|
+
|
24
|
+
The code requires Java 6 (for e.g. the `Arrays.copyOfRange()` calls in `EdDSAEngine.engineVerify()`).
|
25
|
+
|
26
|
+
The JUnit4 tests require the Hamcrest library `hamcrest-all.jar`.
|
27
|
+
|
28
|
+
This code is released to the public domain and can be used for any purpose. See `LICENSE.txt` for details.
|
29
|
+
|
30
|
+
Disclaimer
|
31
|
+
----------
|
32
|
+
|
33
|
+
There are **no** guarantees that this is secure for all cases, and users should
|
34
|
+
review the code themselves before depending on it. PRs that fix bugs or improve
|
35
|
+
reviewability are very welcome. Additionally:
|
36
|
+
|
37
|
+
- The unit test suite includes tests against
|
38
|
+
[the data from the original Python implementation](https://ed25519.cr.yp.to/python/sign.input).
|
39
|
+
- The code (as of 97cea3f0d910fc627c7b57b1bc4d783cdd0c2a4a) was reviewed by
|
40
|
+
[an independent developer](https://github.com/BloodyRookie).
|
41
|
+
- The code (as of dc9f58f2c874463c15465326efc040d17a627b3a) was audited by an independent third party,
|
42
|
+
and the one issue found [was fixed](https://github.com/str4d/ed25519-java/pull/31).
|
43
|
+
|
44
|
+
Code comparison
|
45
|
+
---------------
|
46
|
+
|
47
|
+
For ease of following, here are the main methods in ref10 and their equivalents in this codebase:
|
48
|
+
|
49
|
+
| EdDSA Operation | ref10 function | Java function |
|
50
|
+
| --------------- | -------------- | ------------- |
|
51
|
+
| Generate keypair | `crypto_sign_keypair` | `EdDSAPrivateKeySpec` constructor |
|
52
|
+
| Sign message | `crypto_sign` | `EdDSAEngine.engineSign` |
|
53
|
+
| Verify signature | `crypto_sign_open` | `EdDSAEngine.engineVerify` |
|
54
|
+
|
55
|
+
| EdDSA point arithmetic | ref10 function | Java function |
|
56
|
+
| ---------------------- | -------------- | ------------- |
|
57
|
+
| `R = b * B` | `ge_scalarmult_base` | `GroupElement.scalarMultiply` |
|
58
|
+
| `R = a*A + b*B` | `ge_double_scalarmult_vartime` | `GroupElement.doubleScalarMultiplyVariableTime` |
|
59
|
+
| `R = 2 * P` | `ge_p2_dbl` | `GroupElement.dbl` |
|
60
|
+
| `R = P + Q` | `ge_madd`, `ge_add` | `GroupElement.madd`, `GroupElement.add` |
|
61
|
+
| `R = P - Q` | `ge_msub`, `ge_sub` | `GroupElement.msub`, `GroupElement.sub` |
|
62
|
+
|
63
|
+
|
64
|
+
Important changes
|
65
|
+
-----------------
|
66
|
+
|
67
|
+
### 0.2.0
|
68
|
+
|
69
|
+
- Ed25519 is now named `Ed25519` in `EdDSANamedCurveTable`, and the previous public constant
|
70
|
+
(containing the older inaccurate name) has been removed.
|
71
|
+
|
72
|
+
Credits
|
73
|
+
-------
|
74
|
+
|
75
|
+
* The Ed25519 class was originally ported by k3d3 from [the Python Ed25519 reference implementation](https://ed25519.cr.yp.to/python/ed25519.py).
|
76
|
+
* Useful comments and tweaks were found in [the GNUnet implementation of Ed25519](https://gnunet.org/svn/gnunet-java/src/main/java/org/gnunet/util/crypto/) (based on k3d3's class).
|
77
|
+
* [BloodyRookie](https://github.com/BloodyRookie) reviewed the code, adding many useful comments, unit tests and literature.
|
@@ -0,0 +1,491 @@
|
|
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.io.ByteArrayOutputStream;
|
15
|
+
import java.nio.ByteBuffer;
|
16
|
+
import java.security.InvalidAlgorithmParameterException;
|
17
|
+
import java.security.InvalidKeyException;
|
18
|
+
import java.security.MessageDigest;
|
19
|
+
import java.security.NoSuchAlgorithmException;
|
20
|
+
import java.security.PrivateKey;
|
21
|
+
import java.security.PublicKey;
|
22
|
+
import java.security.Signature;
|
23
|
+
import java.security.SignatureException;
|
24
|
+
import java.security.spec.AlgorithmParameterSpec;
|
25
|
+
import java.security.spec.InvalidKeySpecException;
|
26
|
+
import java.security.spec.X509EncodedKeySpec;
|
27
|
+
import java.util.Arrays;
|
28
|
+
|
29
|
+
import net.i2p.crypto.eddsa.math.Curve;
|
30
|
+
import net.i2p.crypto.eddsa.math.GroupElement;
|
31
|
+
import net.i2p.crypto.eddsa.math.ScalarOps;
|
32
|
+
import sun.security.x509.X509Key;
|
33
|
+
|
34
|
+
/**
|
35
|
+
* Signing and verification for EdDSA.
|
36
|
+
*<p>
|
37
|
+
* The EdDSA sign and verify algorithms do not interact well with
|
38
|
+
* the Java Signature API, as one or more update() methods must be
|
39
|
+
* called before sign() or verify(). Using the standard API,
|
40
|
+
* this implementation must copy and buffer all data passed in
|
41
|
+
* via update().
|
42
|
+
*</p><p>
|
43
|
+
* This implementation offers two ways to avoid this copying,
|
44
|
+
* but only if all data to be signed or verified is available
|
45
|
+
* in a single byte array.
|
46
|
+
*</p><p>
|
47
|
+
*Option 1:
|
48
|
+
*</p><ol>
|
49
|
+
*<li>Call initSign() or initVerify() as usual.
|
50
|
+
*</li><li>Call setParameter(ONE_SHOT_MODE)
|
51
|
+
*</li><li>Call update(byte[]) or update(byte[], int, int) exactly once
|
52
|
+
*</li><li>Call sign() or verify() as usual.
|
53
|
+
*</li><li>If doing additional one-shot signs or verifies with this object, you must
|
54
|
+
* call setParameter(ONE_SHOT_MODE) each time
|
55
|
+
*</li></ol>
|
56
|
+
*
|
57
|
+
*<p>
|
58
|
+
*Option 2:
|
59
|
+
*</p><ol>
|
60
|
+
*<li>Call initSign() or initVerify() as usual.
|
61
|
+
*</li><li>Call one of the signOneShot() or verifyOneShot() methods.
|
62
|
+
*</li><li>If doing additional one-shot signs or verifies with this object,
|
63
|
+
* just call signOneShot() or verifyOneShot() again.
|
64
|
+
*</li></ol>
|
65
|
+
*
|
66
|
+
* @author str4d
|
67
|
+
*
|
68
|
+
*/
|
69
|
+
public final class EdDSAEngine extends Signature {
|
70
|
+
public static final String SIGNATURE_ALGORITHM = "NONEwithEdDSA";
|
71
|
+
|
72
|
+
private MessageDigest digest;
|
73
|
+
private ByteArrayOutputStream baos;
|
74
|
+
private EdDSAKey key;
|
75
|
+
private boolean oneShotMode;
|
76
|
+
private byte[] oneShotBytes;
|
77
|
+
private int oneShotOffset;
|
78
|
+
private int oneShotLength;
|
79
|
+
|
80
|
+
/**
|
81
|
+
* To efficiently sign or verify data in one shot, pass this to setParameters()
|
82
|
+
* after initSign() or initVerify() but BEFORE THE FIRST AND ONLY
|
83
|
+
* update(data) or update(data, off, len). The data reference will be saved
|
84
|
+
* and then used in sign() or verify() without copying the data.
|
85
|
+
* Violate these rules and you will get a SignatureException.
|
86
|
+
*/
|
87
|
+
public static final AlgorithmParameterSpec ONE_SHOT_MODE = new OneShotSpec();
|
88
|
+
|
89
|
+
private static class OneShotSpec implements AlgorithmParameterSpec {}
|
90
|
+
|
91
|
+
/**
|
92
|
+
* No specific EdDSA-internal hash requested, allows any EdDSA key.
|
93
|
+
*/
|
94
|
+
public EdDSAEngine() {
|
95
|
+
super(SIGNATURE_ALGORITHM);
|
96
|
+
}
|
97
|
+
|
98
|
+
/**
|
99
|
+
* Specific EdDSA-internal hash requested, only matching keys will be allowed.
|
100
|
+
* @param digest the hash algorithm that keys must have to sign or verify.
|
101
|
+
*/
|
102
|
+
public EdDSAEngine(MessageDigest digest) {
|
103
|
+
this();
|
104
|
+
this.digest = digest;
|
105
|
+
}
|
106
|
+
|
107
|
+
private void reset() {
|
108
|
+
if (digest != null)
|
109
|
+
digest.reset();
|
110
|
+
if (baos != null)
|
111
|
+
baos.reset();
|
112
|
+
oneShotMode = false;
|
113
|
+
oneShotBytes = null;
|
114
|
+
}
|
115
|
+
|
116
|
+
@Override
|
117
|
+
protected void engineInitSign(PrivateKey privateKey) throws InvalidKeyException {
|
118
|
+
reset();
|
119
|
+
if (privateKey instanceof EdDSAPrivateKey) {
|
120
|
+
EdDSAPrivateKey privKey = (EdDSAPrivateKey) privateKey;
|
121
|
+
key = privKey;
|
122
|
+
|
123
|
+
if (digest == null) {
|
124
|
+
// Instantiate the digest from the key parameters
|
125
|
+
try {
|
126
|
+
digest = MessageDigest.getInstance(key.getParams().getHashAlgorithm());
|
127
|
+
} catch (NoSuchAlgorithmException e) {
|
128
|
+
throw new InvalidKeyException("cannot get required digest " + key.getParams().getHashAlgorithm() + " for private key.");
|
129
|
+
}
|
130
|
+
} else if (!key.getParams().getHashAlgorithm().equals(digest.getAlgorithm()))
|
131
|
+
throw new InvalidKeyException("Key hash algorithm does not match chosen digest");
|
132
|
+
digestInitSign(privKey);
|
133
|
+
} else {
|
134
|
+
throw new InvalidKeyException("cannot identify EdDSA private key: " + privateKey.getClass());
|
135
|
+
}
|
136
|
+
}
|
137
|
+
|
138
|
+
private void digestInitSign(EdDSAPrivateKey privKey) {
|
139
|
+
// Preparing for hash
|
140
|
+
// r = H(h_b,...,h_2b-1,M)
|
141
|
+
int b = privKey.getParams().getCurve().getField().getb();
|
142
|
+
digest.update(privKey.getH(), b/8, b/4 - b/8);
|
143
|
+
}
|
144
|
+
|
145
|
+
@Override
|
146
|
+
protected void engineInitVerify(PublicKey publicKey) throws InvalidKeyException {
|
147
|
+
reset();
|
148
|
+
if (publicKey instanceof EdDSAPublicKey) {
|
149
|
+
key = (EdDSAPublicKey) publicKey;
|
150
|
+
|
151
|
+
if (digest == null) {
|
152
|
+
// Instantiate the digest from the key parameters
|
153
|
+
try {
|
154
|
+
digest = MessageDigest.getInstance(key.getParams().getHashAlgorithm());
|
155
|
+
} catch (NoSuchAlgorithmException e) {
|
156
|
+
throw new InvalidKeyException("cannot get required digest " + key.getParams().getHashAlgorithm() + " for private key.");
|
157
|
+
}
|
158
|
+
} else if (!key.getParams().getHashAlgorithm().equals(digest.getAlgorithm()))
|
159
|
+
throw new InvalidKeyException("Key hash algorithm does not match chosen digest");
|
160
|
+
} else if (publicKey instanceof X509Key) {
|
161
|
+
// X509Certificate will sometimes contain an X509Key rather than the EdDSAPublicKey itself; the contained
|
162
|
+
// key is valid but needs to be instanced as an EdDSAPublicKey before it can be used.
|
163
|
+
EdDSAPublicKey parsedPublicKey;
|
164
|
+
try {
|
165
|
+
parsedPublicKey = new EdDSAPublicKey(new X509EncodedKeySpec(publicKey.getEncoded()));
|
166
|
+
} catch (InvalidKeySpecException ex) {
|
167
|
+
throw new InvalidKeyException("cannot handle X.509 EdDSA public key: " + publicKey.getAlgorithm());
|
168
|
+
}
|
169
|
+
engineInitVerify(parsedPublicKey);
|
170
|
+
} else {
|
171
|
+
throw new InvalidKeyException("cannot identify EdDSA public key: " + publicKey.getClass());
|
172
|
+
}
|
173
|
+
}
|
174
|
+
|
175
|
+
/**
|
176
|
+
* @throws SignatureException if in one-shot mode
|
177
|
+
*/
|
178
|
+
@Override
|
179
|
+
protected void engineUpdate(byte b) throws SignatureException {
|
180
|
+
if (oneShotMode)
|
181
|
+
throw new SignatureException("unsupported in one-shot mode");
|
182
|
+
if (baos == null)
|
183
|
+
baos = new ByteArrayOutputStream(256);
|
184
|
+
baos.write(b);
|
185
|
+
}
|
186
|
+
|
187
|
+
/**
|
188
|
+
* @throws SignatureException if one-shot rules are violated
|
189
|
+
*/
|
190
|
+
@Override
|
191
|
+
protected void engineUpdate(byte[] b, int off, int len)
|
192
|
+
throws SignatureException {
|
193
|
+
if (oneShotMode) {
|
194
|
+
if (oneShotBytes != null)
|
195
|
+
throw new SignatureException("update() already called");
|
196
|
+
oneShotBytes = b;
|
197
|
+
oneShotOffset = off;
|
198
|
+
oneShotLength = len;
|
199
|
+
} else {
|
200
|
+
if (baos == null)
|
201
|
+
baos = new ByteArrayOutputStream(256);
|
202
|
+
baos.write(b, off, len);
|
203
|
+
}
|
204
|
+
}
|
205
|
+
|
206
|
+
@Override
|
207
|
+
protected byte[] engineSign() throws SignatureException {
|
208
|
+
try {
|
209
|
+
return x_engineSign();
|
210
|
+
} finally {
|
211
|
+
reset();
|
212
|
+
// must leave the object ready to sign again with
|
213
|
+
// the same key, as required by the API
|
214
|
+
EdDSAPrivateKey privKey = (EdDSAPrivateKey) key;
|
215
|
+
digestInitSign(privKey);
|
216
|
+
}
|
217
|
+
}
|
218
|
+
|
219
|
+
private byte[] x_engineSign() throws SignatureException {
|
220
|
+
Curve curve = key.getParams().getCurve();
|
221
|
+
ScalarOps sc = key.getParams().getScalarOps();
|
222
|
+
byte[] a = ((EdDSAPrivateKey) key).geta();
|
223
|
+
|
224
|
+
byte[] message;
|
225
|
+
int offset, length;
|
226
|
+
if (oneShotMode) {
|
227
|
+
if (oneShotBytes == null)
|
228
|
+
throw new SignatureException("update() not called first");
|
229
|
+
message = oneShotBytes;
|
230
|
+
offset = oneShotOffset;
|
231
|
+
length = oneShotLength;
|
232
|
+
} else {
|
233
|
+
if (baos == null)
|
234
|
+
message = new byte[0];
|
235
|
+
else
|
236
|
+
message = baos.toByteArray();
|
237
|
+
offset = 0;
|
238
|
+
length = message.length;
|
239
|
+
}
|
240
|
+
// r = H(h_b,...,h_2b-1,M)
|
241
|
+
digest.update(message, offset, length);
|
242
|
+
byte[] r = digest.digest();
|
243
|
+
|
244
|
+
// r mod l
|
245
|
+
// Reduces r from 64 bytes to 32 bytes
|
246
|
+
r = sc.reduce(r);
|
247
|
+
|
248
|
+
// R = rB
|
249
|
+
GroupElement R = key.getParams().getB().scalarMultiply(r);
|
250
|
+
byte[] Rbyte = R.toByteArray();
|
251
|
+
|
252
|
+
// S = (r + H(Rbar,Abar,M)*a) mod l
|
253
|
+
digest.update(Rbyte);
|
254
|
+
digest.update(((EdDSAPrivateKey) key).getAbyte());
|
255
|
+
digest.update(message, offset, length);
|
256
|
+
byte[] h = digest.digest();
|
257
|
+
h = sc.reduce(h);
|
258
|
+
byte[] S = sc.multiplyAndAdd(h, a, r);
|
259
|
+
|
260
|
+
// R+S
|
261
|
+
int b = curve.getField().getb();
|
262
|
+
ByteBuffer out = ByteBuffer.allocate(b/4);
|
263
|
+
out.put(Rbyte).put(S);
|
264
|
+
return out.array();
|
265
|
+
}
|
266
|
+
|
267
|
+
@Override
|
268
|
+
protected boolean engineVerify(byte[] sigBytes) throws SignatureException {
|
269
|
+
try {
|
270
|
+
return x_engineVerify(sigBytes);
|
271
|
+
} finally {
|
272
|
+
reset();
|
273
|
+
}
|
274
|
+
}
|
275
|
+
|
276
|
+
private boolean x_engineVerify(byte[] sigBytes) throws SignatureException {
|
277
|
+
Curve curve = key.getParams().getCurve();
|
278
|
+
int b = curve.getField().getb();
|
279
|
+
if (sigBytes.length != b/4)
|
280
|
+
throw new SignatureException("signature length is wrong");
|
281
|
+
|
282
|
+
// R is first b/8 bytes of sigBytes, S is second b/8 bytes
|
283
|
+
digest.update(sigBytes, 0, b/8);
|
284
|
+
digest.update(((EdDSAPublicKey) key).getAbyte());
|
285
|
+
// h = H(Rbar,Abar,M)
|
286
|
+
byte[] message;
|
287
|
+
int offset, length;
|
288
|
+
if (oneShotMode) {
|
289
|
+
if (oneShotBytes == null)
|
290
|
+
throw new SignatureException("update() not called first");
|
291
|
+
message = oneShotBytes;
|
292
|
+
offset = oneShotOffset;
|
293
|
+
length = oneShotLength;
|
294
|
+
} else {
|
295
|
+
if (baos == null)
|
296
|
+
message = new byte[0];
|
297
|
+
else
|
298
|
+
message = baos.toByteArray();
|
299
|
+
offset = 0;
|
300
|
+
length = message.length;
|
301
|
+
}
|
302
|
+
digest.update(message, offset, length);
|
303
|
+
byte[] h = digest.digest();
|
304
|
+
|
305
|
+
// h mod l
|
306
|
+
h = key.getParams().getScalarOps().reduce(h);
|
307
|
+
|
308
|
+
byte[] Sbyte = Arrays.copyOfRange(sigBytes, b/8, b/4);
|
309
|
+
// R = SB - H(Rbar,Abar,M)A
|
310
|
+
GroupElement R = key.getParams().getB().doubleScalarMultiplyVariableTime(
|
311
|
+
((EdDSAPublicKey) key).getNegativeA(), h, Sbyte);
|
312
|
+
|
313
|
+
// Variable time. This should be okay, because there are no secret
|
314
|
+
// values used anywhere in verification.
|
315
|
+
byte[] Rcalc = R.toByteArray();
|
316
|
+
for (int i = 0; i < Rcalc.length; i++) {
|
317
|
+
if (Rcalc[i] != sigBytes[i])
|
318
|
+
return false;
|
319
|
+
}
|
320
|
+
return true;
|
321
|
+
}
|
322
|
+
|
323
|
+
/**
|
324
|
+
* To efficiently sign all the data in one shot, if it is available,
|
325
|
+
* use this method, which will avoid copying the data.
|
326
|
+
*
|
327
|
+
* Same as:
|
328
|
+
*<pre>
|
329
|
+
* setParameter(ONE_SHOT_MODE)
|
330
|
+
* update(data)
|
331
|
+
* sig = sign()
|
332
|
+
*</pre>
|
333
|
+
*
|
334
|
+
* @param data the message to be signed
|
335
|
+
* @return the signature
|
336
|
+
* @throws SignatureException if update() already called
|
337
|
+
* @see #ONE_SHOT_MODE
|
338
|
+
*/
|
339
|
+
public byte[] signOneShot(byte[] data) throws SignatureException {
|
340
|
+
return signOneShot(data, 0, data.length);
|
341
|
+
}
|
342
|
+
|
343
|
+
/**
|
344
|
+
* To efficiently sign all the data in one shot, if it is available,
|
345
|
+
* use this method, which will avoid copying the data.
|
346
|
+
*
|
347
|
+
* Same as:
|
348
|
+
*<pre>
|
349
|
+
* setParameter(ONE_SHOT_MODE)
|
350
|
+
* update(data, off, len)
|
351
|
+
* sig = sign()
|
352
|
+
*</pre>
|
353
|
+
*
|
354
|
+
* @param data byte array containing the message to be signed
|
355
|
+
* @param off the start of the message inside data
|
356
|
+
* @param len the length of the message
|
357
|
+
* @return the signature
|
358
|
+
* @throws SignatureException if update() already called
|
359
|
+
* @see #ONE_SHOT_MODE
|
360
|
+
*/
|
361
|
+
public byte[] signOneShot(byte[] data, int off, int len) throws SignatureException {
|
362
|
+
oneShotMode = true;
|
363
|
+
update(data, off, len);
|
364
|
+
return sign();
|
365
|
+
}
|
366
|
+
|
367
|
+
/**
|
368
|
+
* To efficiently verify all the data in one shot, if it is available,
|
369
|
+
* use this method, which will avoid copying the data.
|
370
|
+
*
|
371
|
+
* Same as:
|
372
|
+
*<pre>
|
373
|
+
* setParameter(ONE_SHOT_MODE)
|
374
|
+
* update(data)
|
375
|
+
* ok = verify(signature)
|
376
|
+
*</pre>
|
377
|
+
*
|
378
|
+
* @param data the message that was signed
|
379
|
+
* @param signature of the message
|
380
|
+
* @return true if the signature is valid, false otherwise
|
381
|
+
* @throws SignatureException if update() already called
|
382
|
+
* @see #ONE_SHOT_MODE
|
383
|
+
*/
|
384
|
+
public boolean verifyOneShot(byte[] data, byte[] signature) throws SignatureException {
|
385
|
+
return verifyOneShot(data, 0, data.length, signature, 0, signature.length);
|
386
|
+
}
|
387
|
+
|
388
|
+
/**
|
389
|
+
* To efficiently verify all the data in one shot, if it is available,
|
390
|
+
* use this method, which will avoid copying the data.
|
391
|
+
*
|
392
|
+
* Same as:
|
393
|
+
*<pre>
|
394
|
+
* setParameter(ONE_SHOT_MODE)
|
395
|
+
* update(data, off, len)
|
396
|
+
* ok = verify(signature)
|
397
|
+
*</pre>
|
398
|
+
*
|
399
|
+
* @param data byte array containing the message that was signed
|
400
|
+
* @param off the start of the message inside data
|
401
|
+
* @param len the length of the message
|
402
|
+
* @param signature of the message
|
403
|
+
* @return true if the signature is valid, false otherwise
|
404
|
+
* @throws SignatureException if update() already called
|
405
|
+
* @see #ONE_SHOT_MODE
|
406
|
+
*/
|
407
|
+
public boolean verifyOneShot(byte[] data, int off, int len, byte[] signature) throws SignatureException {
|
408
|
+
return verifyOneShot(data, off, len, signature, 0, signature.length);
|
409
|
+
}
|
410
|
+
|
411
|
+
/**
|
412
|
+
* To efficiently verify all the data in one shot, if it is available,
|
413
|
+
* use this method, which will avoid copying the data.
|
414
|
+
*
|
415
|
+
* Same as:
|
416
|
+
*<pre>
|
417
|
+
* setParameter(ONE_SHOT_MODE)
|
418
|
+
* update(data)
|
419
|
+
* ok = verify(signature, sigoff, siglen)
|
420
|
+
*</pre>
|
421
|
+
*
|
422
|
+
* @param data the message that was signed
|
423
|
+
* @param signature byte array containing the signature
|
424
|
+
* @param sigoff the start of the signature
|
425
|
+
* @param siglen the length of the signature
|
426
|
+
* @return true if the signature is valid, false otherwise
|
427
|
+
* @throws SignatureException if update() already called
|
428
|
+
* @see #ONE_SHOT_MODE
|
429
|
+
*/
|
430
|
+
public boolean verifyOneShot(byte[] data, byte[] signature, int sigoff, int siglen) throws SignatureException {
|
431
|
+
return verifyOneShot(data, 0, data.length, signature, sigoff, siglen);
|
432
|
+
}
|
433
|
+
|
434
|
+
/**
|
435
|
+
* To efficiently verify all the data in one shot, if it is available,
|
436
|
+
* use this method, which will avoid copying the data.
|
437
|
+
*
|
438
|
+
* Same as:
|
439
|
+
*<pre>
|
440
|
+
* setParameter(ONE_SHOT_MODE)
|
441
|
+
* update(data, off, len)
|
442
|
+
* ok = verify(signature, sigoff, siglen)
|
443
|
+
*</pre>
|
444
|
+
*
|
445
|
+
* @param data byte array containing the message that was signed
|
446
|
+
* @param off the start of the message inside data
|
447
|
+
* @param len the length of the message
|
448
|
+
* @param signature byte array containing the signature
|
449
|
+
* @param sigoff the start of the signature
|
450
|
+
* @param siglen the length of the signature
|
451
|
+
* @return true if the signature is valid, false otherwise
|
452
|
+
* @throws SignatureException if update() already called
|
453
|
+
* @see #ONE_SHOT_MODE
|
454
|
+
*/
|
455
|
+
public boolean verifyOneShot(byte[] data, int off, int len, byte[] signature, int sigoff, int siglen) throws SignatureException {
|
456
|
+
oneShotMode = true;
|
457
|
+
update(data, off, len);
|
458
|
+
return verify(signature, sigoff, siglen);
|
459
|
+
}
|
460
|
+
|
461
|
+
/**
|
462
|
+
* @throws InvalidAlgorithmParameterException if spec is ONE_SHOT_MODE and update() already called
|
463
|
+
* @see #ONE_SHOT_MODE
|
464
|
+
*/
|
465
|
+
@Override
|
466
|
+
protected void engineSetParameter(AlgorithmParameterSpec spec) throws InvalidAlgorithmParameterException {
|
467
|
+
if (spec.equals(ONE_SHOT_MODE)) {
|
468
|
+
if (oneShotBytes != null || (baos != null && baos.size() > 0))
|
469
|
+
throw new InvalidAlgorithmParameterException("update() already called");
|
470
|
+
oneShotMode = true;
|
471
|
+
} else {
|
472
|
+
super.engineSetParameter(spec);
|
473
|
+
}
|
474
|
+
}
|
475
|
+
|
476
|
+
/**
|
477
|
+
* @deprecated
|
478
|
+
*/
|
479
|
+
@Override
|
480
|
+
protected void engineSetParameter(String param, Object value) {
|
481
|
+
throw new UnsupportedOperationException("engineSetParameter unsupported");
|
482
|
+
}
|
483
|
+
|
484
|
+
/**
|
485
|
+
* @deprecated
|
486
|
+
*/
|
487
|
+
@Override
|
488
|
+
protected Object engineGetParameter(String param) {
|
489
|
+
throw new UnsupportedOperationException("engineSetParameter unsupported");
|
490
|
+
}
|
491
|
+
}
|