├── .gitignore
├── .idea
└── libraries
│ ├── Maven__junit_junit_maven_plugin_4_11.xml
│ ├── Maven__org_hamcrest_hamcrest_all_1_3.xml
│ └── Maven__org_hamcrest_hamcrest_core_1_3.xml
├── .travis.yml
├── LICENSE.txt
├── Literature
├── A complete set of addition laws for incomplete Edwards curves.pdf
├── Curves, Codes, and Cryptography.pdf
├── EdDSA signatures and Ed25519.pdf
├── High-speed high-security signatures.pdf
├── NaCl on 8-bit AVR Microcontrollers.pdf
├── Optimizing double-base elliptic-curve-single-scalar multiplication.pdf
├── Scalar-multiplication algorithms.pdf
├── Twisted Edwards Curves Revisited.pdf
└── Twisted Edwards Curves.pdf
├── README.md
├── eddsa.iml
├── jitpack.yml
├── pom.xml
├── src
└── net
│ └── i2p
│ └── crypto
│ └── eddsa
│ ├── EdDSAEngine.java
│ ├── EdDSAKey.java
│ ├── EdDSAPrivateKey.java
│ ├── EdDSAPublicKey.java
│ ├── EdDSASecurityProvider.java
│ ├── KeyFactory.java
│ ├── KeyPairGenerator.java
│ ├── Utils.java
│ ├── math
│ ├── Constants.java
│ ├── Curve.java
│ ├── Encoding.java
│ ├── Field.java
│ ├── FieldElement.java
│ ├── GroupElement.java
│ ├── ScalarOps.java
│ ├── bigint
│ │ ├── BigIntegerFieldElement.java
│ │ ├── BigIntegerLittleEndianEncoding.java
│ │ ├── BigIntegerScalarOps.java
│ │ └── package.html
│ ├── ed25519
│ │ ├── Ed25519FieldElement.java
│ │ ├── Ed25519LittleEndianEncoding.java
│ │ ├── Ed25519ScalarOps.java
│ │ └── package.html
│ └── package.html
│ ├── package.html
│ └── spec
│ ├── EdDSAGenParameterSpec.java
│ ├── EdDSANamedCurveSpec.java
│ ├── EdDSANamedCurveTable.java
│ ├── EdDSAParameterSpec.java
│ ├── EdDSAPrivateKeySpec.java
│ ├── EdDSAPublicKeySpec.java
│ └── package.html
└── test
└── net
└── i2p
└── crypto
└── eddsa
├── Ed25519TestVectors.java
├── EdDSAEngineTest.java
├── EdDSAPrivateKeyTest.java
├── EdDSAPublicKeyTest.java
├── EdDSASecurityProviderTest.java
├── UtilsTest.java
├── math
├── AbstractFieldElementTest.java
├── ConstantsTest.java
├── GroupElementTest.java
├── MathUtils.java
├── PrecomputationTestVectors.java
├── baseDblPrecmp
├── basePrecmp
├── bigint
│ ├── BigIntegerFieldElementTest.java
│ └── BigIntegerScalarOpsTest.java
└── ed25519
│ ├── Ed25519FieldElementTest.java
│ ├── Ed25519LittleEndianEncodingTest.java
│ └── Ed25519ScalarOpsTest.java
├── spec
├── EdDSANamedCurveTableTest.java
└── EdDSAPrivateKeySpecTest.java
└── test.data
/.gitignore:
--------------------------------------------------------------------------------
1 | /bin/
2 | /target/
3 | /.settings/
4 | *.class
5 | *.iml
6 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__junit_junit_maven_plugin_4_11.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__org_hamcrest_hamcrest_all_1_3.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Maven__org_hamcrest_hamcrest_core_1_3.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: java
2 | dist: trusty
3 |
4 | jdk:
5 | - oraclejdk11
6 | - oraclejdk9
7 | - oraclejdk8
8 | - openjdk11
9 | - openjdk10
10 | - openjdk9
11 | - openjdk8
12 |
13 | matrix:
14 | include:
15 | - jdk: openjdk7
16 | install:
17 | - mvn test -DskipTests=true -B -V
18 | - jdk_switcher use oraclejdk8
19 | - mvn install -DskipTests=true -Dmaven.javadoc.skip=true -Dgpg.skip=true -B -V
20 | - jdk_switcher use $TRAVIS_JDK_VERSION
21 |
22 | install:
23 | - mvn install -DskipTests=true -Dmaven.javadoc.skip=true -Dgpg.skip=true -B -V
24 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
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/
124 |
--------------------------------------------------------------------------------
/Literature/A complete set of addition laws for incomplete Edwards curves.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/str4d/ed25519-java/e0ac35769db8553fb714b09f0d3f3d2b001fd033/Literature/A complete set of addition laws for incomplete Edwards curves.pdf
--------------------------------------------------------------------------------
/Literature/Curves, Codes, and Cryptography.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/str4d/ed25519-java/e0ac35769db8553fb714b09f0d3f3d2b001fd033/Literature/Curves, Codes, and Cryptography.pdf
--------------------------------------------------------------------------------
/Literature/EdDSA signatures and Ed25519.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/str4d/ed25519-java/e0ac35769db8553fb714b09f0d3f3d2b001fd033/Literature/EdDSA signatures and Ed25519.pdf
--------------------------------------------------------------------------------
/Literature/High-speed high-security signatures.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/str4d/ed25519-java/e0ac35769db8553fb714b09f0d3f3d2b001fd033/Literature/High-speed high-security signatures.pdf
--------------------------------------------------------------------------------
/Literature/NaCl on 8-bit AVR Microcontrollers.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/str4d/ed25519-java/e0ac35769db8553fb714b09f0d3f3d2b001fd033/Literature/NaCl on 8-bit AVR Microcontrollers.pdf
--------------------------------------------------------------------------------
/Literature/Optimizing double-base elliptic-curve-single-scalar multiplication.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/str4d/ed25519-java/e0ac35769db8553fb714b09f0d3f3d2b001fd033/Literature/Optimizing double-base elliptic-curve-single-scalar multiplication.pdf
--------------------------------------------------------------------------------
/Literature/Scalar-multiplication algorithms.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/str4d/ed25519-java/e0ac35769db8553fb714b09f0d3f3d2b001fd033/Literature/Scalar-multiplication algorithms.pdf
--------------------------------------------------------------------------------
/Literature/Twisted Edwards Curves Revisited.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/str4d/ed25519-java/e0ac35769db8553fb714b09f0d3f3d2b001fd033/Literature/Twisted Edwards Curves Revisited.pdf
--------------------------------------------------------------------------------
/Literature/Twisted Edwards Curves.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/str4d/ed25519-java/e0ac35769db8553fb714b09f0d3f3d2b001fd033/Literature/Twisted Edwards Curves.pdf
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | EdDSA-Java
2 | ==========
3 |
4 | [](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
7 | (see https://ed25519.cr.yp.to/software.html).
8 |
9 | There are two internal implementations:
10 | - A port of the radix-2^51 operations in ref10 - fast and constant-time, but only useful for Ed25519.
11 | - A generic version using BigIntegers for calculation - a bit slower and not constant-time, but compatible
12 | with any EdDSA parameter specification.
13 |
14 |
15 | To use
16 | ------
17 |
18 | Download the latest .jar from the releases tab and place it in your classpath.
19 |
20 | Gradle users:
21 |
22 | ```
23 | compile 'net.i2p.crypto:eddsa:0.3.0'
24 | ```
25 |
26 | Java 7 and above are supported.
27 |
28 | The JUnit4 tests require the Hamcrest library `hamcrest-all.jar`.
29 |
30 | This code is released to the public domain and can be used for any purpose. See `LICENSE.txt` for details.
31 |
32 | Disclaimer
33 | ----------
34 |
35 | There are **no** guarantees that this is secure for all cases, and users should
36 | review the code themselves before depending on it. PRs that fix bugs or improve
37 | reviewability are very welcome. Additionally:
38 |
39 | - The unit test suite includes tests against
40 | [the data from the original Python implementation](https://ed25519.cr.yp.to/python/sign.input).
41 | - The code (as of 97cea3f0d910fc627c7b57b1bc4d783cdd0c2a4a) was reviewed by
42 | [an independent developer](https://github.com/BloodyRookie).
43 | - The code (as of dc9f58f2c874463c15465326efc040d17a627b3a) was audited by an independent third party,
44 | and the one issue found [was fixed](https://github.com/str4d/ed25519-java/pull/31).
45 |
46 | Code comparison
47 | ---------------
48 |
49 | For ease of following, here are the main methods in ref10 and their equivalents in this codebase:
50 |
51 | | EdDSA Operation | ref10 function | Java function |
52 | | --------------- | -------------- | ------------- |
53 | | Generate keypair | `crypto_sign_keypair` | `EdDSAPrivateKeySpec` constructor |
54 | | Sign message | `crypto_sign` | `EdDSAEngine.engineSign` |
55 | | Verify signature | `crypto_sign_open` | `EdDSAEngine.engineVerify` |
56 |
57 | | EdDSA point arithmetic | ref10 function | Java function |
58 | | ---------------------- | -------------- | ------------- |
59 | | `R = b * B` | `ge_scalarmult_base` | `GroupElement.scalarMultiply` |
60 | | `R = a*A + b*B` | `ge_double_scalarmult_vartime` | `GroupElement.doubleScalarMultiplyVariableTime` |
61 | | `R = 2 * P` | `ge_p2_dbl` | `GroupElement.dbl` |
62 | | `R = P + Q` | `ge_madd`, `ge_add` | `GroupElement.madd`, `GroupElement.add` |
63 | | `R = P - Q` | `ge_msub`, `ge_sub` | `GroupElement.msub`, `GroupElement.sub` |
64 |
65 |
66 | Important changes
67 | -----------------
68 |
69 | ### 0.3.0
70 |
71 | - The library has been extensively profiled for contention issues in a multi-threaded environment. The only
72 | remaining potential contention is in `EdDSANamedCurveTable.defineCurve()`, which will be rarely called.
73 | - The public constant for the curve name has returned as `ED_25519`, and the curve specification has a public
74 | constant `ED_25519_CURVE_SPEC` to avoid repeated lookups when converting to and from encoded form for the
75 | public or private keys.
76 | - `GroupElement` is now completely immutable, and all fields final, to avoid the need for `synchronized`
77 | blocks over mutable fields. This required some new constructors and paths to construction.
78 | - `EdDSAPublicKeySpec.getNegativeA()` and `EdDSAPublicKey.getNegativeA()` now evaluate lazily, taking
79 | advantage of the immutability of `GroupElement.negate()`. This boosts the performance of the public key
80 | constructor when the key is just being passed around rather than used.
81 | - Support for X509Key wrapped EdDSA public keys.
82 |
83 | ### 0.2.0
84 |
85 | - Ed25519 is now named `Ed25519` in `EdDSANamedCurveTable`, and the previous public constant (containing the
86 | older inaccurate name) has been removed.
87 |
88 | Credits
89 | -------
90 |
91 | - The Ed25519 class was originally ported by k3d3 from
92 | [the Python Ed25519 reference implementation](https://ed25519.cr.yp.to/python/ed25519.py).
93 | - Useful comments and tweaks were found in
94 | [the GNUnet implementation of Ed25519](https://gnunet.org/svn/gnunet-java/src/main/java/org/gnunet/util/crypto/)
95 | (based on k3d3's class).
96 | - [BloodyRookie](https://github.com/BloodyRookie) reviewed the code, adding many useful comments, unit tests
97 | and literature.
98 |
--------------------------------------------------------------------------------
/eddsa.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/jitpack.yml:
--------------------------------------------------------------------------------
1 | jdk:
2 | - oraclejdk8
3 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
3 | 4.0.0
4 | net.i2p.crypto
5 | eddsa
6 | 0.3.0
7 | EdDSA-Java
8 | bundle
9 | Implementation of EdDSA in Java
10 | https://github.com/str4d/ed25519-java
11 |
12 | UTF-8
13 |
14 |
15 |
16 | CC0 1.0 Universal
17 | https://creativecommons.org/publicdomain/zero/1.0/
18 | repo
19 |
20 |
21 |
22 |
23 | str4d
24 | Jack Grigg
25 | thestr4d@gmail.com
26 |
27 |
28 |
29 | scm:git:git://github.com/str4d/ed25519-java.git
30 | scm:git:git@github.com:str4d/ed25519-java.git
31 | https://github.com/str4d/ed25519-java
32 |
33 |
34 |
35 | ossrh
36 | https://oss.sonatype.org/content/repositories/snapshots
37 |
38 |
39 |
40 | src
41 | test
42 |
43 |
44 | test
45 |
46 | **/*.java
47 |
48 |
49 |
50 |
51 |
52 | org.apache.maven.plugins
53 | maven-compiler-plugin
54 |
55 | 1.7
56 | 1.7
57 | ${project.build.sourceEncoding}
58 |
59 | 3.8.0
60 |
61 |
62 | org.apache.maven.plugins
63 | maven-surefire-plugin
64 | 3.0.0-M3
65 |
66 |
67 | org.apache.felix
68 | maven-bundle-plugin
69 | 3.5.1
70 | true
71 |
72 |
73 |
74 | net.i2p.crypto.eddsa,
75 | net.i2p.crypto.eddsa.spec
76 |
77 |
78 | net.i2p.crypto.eddsa.math.*
79 |
80 |
81 | net.i2p.crypto.eddsa
82 |
83 |
84 |
85 |
86 |
87 | org.apache.maven.plugins
88 | maven-source-plugin
89 | 3.0.1
90 |
91 |
92 | attach-sources
93 |
94 | jar-no-fork
95 |
96 |
97 |
98 |
99 |
100 | org.apache.maven.plugins
101 | maven-javadoc-plugin
102 | 3.0.1
103 |
104 | UTF-8
105 | UTF-8
106 | UTF-8
107 | --allow-script-in-comments
108 | <script type='text/x-mathjax-config'>
109 | MathJax.Hub.Config({
110 | tex2jax: {
111 | inlineMath: [ ['$','$'] ],
112 | processEscapes: true
113 | }
114 | });
115 | </script>
116 | <script type='text/javascript' src='https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML'></script>
117 |
118 |
119 |
120 | attach-javadocs
121 |
122 | jar
123 |
124 |
125 |
126 |
127 |
128 | org.apache.maven.plugins
129 | maven-gpg-plugin
130 | 1.6
131 |
132 |
133 | sign-artifacts
134 | verify
135 |
136 | sign
137 |
138 |
139 |
140 |
141 |
142 | org.sonatype.plugins
143 | nexus-staging-maven-plugin
144 | 1.6.8
145 | true
146 |
147 | ossrh
148 | https://oss.sonatype.org/
149 | true
150 |
151 |
152 |
153 |
154 |
155 |
156 | org.hamcrest
157 | hamcrest-all
158 | 1.3
159 | test
160 |
161 |
162 | junit
163 | junit
164 | 4.12
165 | maven-plugin
166 | test
167 |
168 |
169 |
170 |
171 | zzz
172 | zzz@mail.i2p
173 |
174 |
175 | mbakkar
176 | Mohamed.b.bakkar@gmail.com
177 |
178 |
179 | Philippe Marchesseault
180 | pmarches@gmail.com
181 |
182 |
183 | BloodyRookie
184 | nemproject@gmx.de
185 |
186 |
187 | Jan Willem Janssen
188 | janwillem.janssen@luminis.eu
189 |
190 |
191 | Jim Keener
192 | jim@jimkeener.com
193 |
194 |
195 | Lyor Goldstein
196 | lyor.goldstein@gmail.com
197 |
198 |
199 | Ilya Maykov
200 | ilyam@fb.com
201 |
202 |
203 | Ross Nicoll
204 | ross.nicoll@r3.com
205 |
206 |
207 | Mark Raynsford
208 | code@io7m.com
209 |
210 |
211 | rick.parker
212 | rick.parker@r3cev.com
213 |
214 |
215 | Christian Sailer
216 | christian.sailer@r3.com
217 |
218 |
219 |
220 |
--------------------------------------------------------------------------------
/src/net/i2p/crypto/eddsa/EdDSAEngine.java:
--------------------------------------------------------------------------------
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 .
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 |
33 | /**
34 | * Signing and verification for EdDSA.
35 | *
36 | * The EdDSA sign and verify algorithms do not interact well with
37 | * the Java Signature API, as one or more update() methods must be
38 | * called before sign() or verify(). Using the standard API,
39 | * this implementation must copy and buffer all data passed in
40 | * via update().
41 | *
42 | * This implementation offers two ways to avoid this copying,
43 | * but only if all data to be signed or verified is available
44 | * in a single byte array.
45 | *
46 | *Option 1:
47 | *
48 | *
Call initSign() or initVerify() as usual.
49 | *
Call setParameter(ONE_SHOT_MODE)
50 | *
Call update(byte[]) or update(byte[], int, int) exactly once
51 | *
Call sign() or verify() as usual.
52 | *
If doing additional one-shot signs or verifies with this object, you must
53 | * call setParameter(ONE_SHOT_MODE) each time
54 | *
55 | *
56 | *
57 | *Option 2:
58 | *
59 | *
Call initSign() or initVerify() as usual.
60 | *
Call one of the signOneShot() or verifyOneShot() methods.
61 | *
If doing additional one-shot signs or verifies with this object,
62 | * just call signOneShot() or verifyOneShot() again.
63 | *
64 | *
65 | * @author str4d
66 | *
67 | */
68 | public final class EdDSAEngine extends Signature {
69 | public static final String SIGNATURE_ALGORITHM = "NONEwithEdDSA";
70 |
71 | private MessageDigest digest;
72 | private ByteArrayOutputStream baos;
73 | private EdDSAKey key;
74 | private boolean oneShotMode;
75 | private byte[] oneShotBytes;
76 | private int oneShotOffset;
77 | private int oneShotLength;
78 |
79 | /**
80 | * To efficiently sign or verify data in one shot, pass this to setParameters()
81 | * after initSign() or initVerify() but BEFORE THE FIRST AND ONLY
82 | * update(data) or update(data, off, len). The data reference will be saved
83 | * and then used in sign() or verify() without copying the data.
84 | * Violate these rules and you will get a SignatureException.
85 | */
86 | public static final AlgorithmParameterSpec ONE_SHOT_MODE = new OneShotSpec();
87 |
88 | private static class OneShotSpec implements AlgorithmParameterSpec {}
89 |
90 | /**
91 | * No specific EdDSA-internal hash requested, allows any EdDSA key.
92 | */
93 | public EdDSAEngine() {
94 | super(SIGNATURE_ALGORITHM);
95 | }
96 |
97 | /**
98 | * Specific EdDSA-internal hash requested, only matching keys will be allowed.
99 | * @param digest the hash algorithm that keys must have to sign or verify.
100 | */
101 | public EdDSAEngine(MessageDigest digest) {
102 | this();
103 | this.digest = digest;
104 | }
105 |
106 | private void reset() {
107 | if (digest != null)
108 | digest.reset();
109 | if (baos != null)
110 | baos.reset();
111 | oneShotMode = false;
112 | oneShotBytes = null;
113 | }
114 |
115 | @Override
116 | protected void engineInitSign(PrivateKey privateKey) throws InvalidKeyException {
117 | reset();
118 | if (privateKey instanceof EdDSAPrivateKey) {
119 | EdDSAPrivateKey privKey = (EdDSAPrivateKey) privateKey;
120 | key = privKey;
121 |
122 | if (digest == null) {
123 | // Instantiate the digest from the key parameters
124 | try {
125 | digest = MessageDigest.getInstance(key.getParams().getHashAlgorithm());
126 | } catch (NoSuchAlgorithmException e) {
127 | throw new InvalidKeyException("cannot get required digest " + key.getParams().getHashAlgorithm() + " for private key.");
128 | }
129 | } else if (!key.getParams().getHashAlgorithm().equals(digest.getAlgorithm()))
130 | throw new InvalidKeyException("Key hash algorithm does not match chosen digest");
131 | digestInitSign(privKey);
132 | } else {
133 | throw new InvalidKeyException("cannot identify EdDSA private key: " + privateKey.getClass());
134 | }
135 | }
136 |
137 | private void digestInitSign(EdDSAPrivateKey privKey) {
138 | // Preparing for hash
139 | // r = H(h_b,...,h_2b-1,M)
140 | int b = privKey.getParams().getCurve().getField().getb();
141 | digest.update(privKey.getH(), b/8, b/4 - b/8);
142 | }
143 |
144 | @Override
145 | protected void engineInitVerify(PublicKey publicKey) throws InvalidKeyException {
146 | reset();
147 | if (publicKey instanceof EdDSAPublicKey) {
148 | key = (EdDSAPublicKey) publicKey;
149 |
150 | if (digest == null) {
151 | // Instantiate the digest from the key parameters
152 | try {
153 | digest = MessageDigest.getInstance(key.getParams().getHashAlgorithm());
154 | } catch (NoSuchAlgorithmException e) {
155 | throw new InvalidKeyException("cannot get required digest " + key.getParams().getHashAlgorithm() + " for private key.");
156 | }
157 | } else if (!key.getParams().getHashAlgorithm().equals(digest.getAlgorithm()))
158 | throw new InvalidKeyException("Key hash algorithm does not match chosen digest");
159 | } else if (publicKey.getFormat().equals("X.509")) {
160 | // X509Certificate will sometimes contain an X509Key rather than the EdDSAPublicKey itself; the contained
161 | // key is valid but needs to be instanced as an EdDSAPublicKey before it can be used.
162 | EdDSAPublicKey parsedPublicKey;
163 | try {
164 | parsedPublicKey = new EdDSAPublicKey(new X509EncodedKeySpec(publicKey.getEncoded()));
165 | } catch (InvalidKeySpecException ex) {
166 | throw new InvalidKeyException("cannot handle X.509 EdDSA public key: " + publicKey.getAlgorithm());
167 | }
168 | engineInitVerify(parsedPublicKey);
169 | } else {
170 | throw new InvalidKeyException("cannot identify EdDSA public key: " + publicKey.getClass());
171 | }
172 | }
173 |
174 | /**
175 | * @throws SignatureException if in one-shot mode
176 | */
177 | @Override
178 | protected void engineUpdate(byte b) throws SignatureException {
179 | if (oneShotMode)
180 | throw new SignatureException("unsupported in one-shot mode");
181 | if (baos == null)
182 | baos = new ByteArrayOutputStream(256);
183 | baos.write(b);
184 | }
185 |
186 | /**
187 | * @throws SignatureException if one-shot rules are violated
188 | */
189 | @Override
190 | protected void engineUpdate(byte[] b, int off, int len)
191 | throws SignatureException {
192 | if (oneShotMode) {
193 | if (oneShotBytes != null)
194 | throw new SignatureException("update() already called");
195 | oneShotBytes = b;
196 | oneShotOffset = off;
197 | oneShotLength = len;
198 | } else {
199 | if (baos == null)
200 | baos = new ByteArrayOutputStream(256);
201 | baos.write(b, off, len);
202 | }
203 | }
204 |
205 | @Override
206 | protected byte[] engineSign() throws SignatureException {
207 | try {
208 | return x_engineSign();
209 | } finally {
210 | reset();
211 | // must leave the object ready to sign again with
212 | // the same key, as required by the API
213 | EdDSAPrivateKey privKey = (EdDSAPrivateKey) key;
214 | digestInitSign(privKey);
215 | }
216 | }
217 |
218 | private byte[] x_engineSign() throws SignatureException {
219 | Curve curve = key.getParams().getCurve();
220 | ScalarOps sc = key.getParams().getScalarOps();
221 | byte[] a = ((EdDSAPrivateKey) key).geta();
222 |
223 | byte[] message;
224 | int offset, length;
225 | if (oneShotMode) {
226 | if (oneShotBytes == null)
227 | throw new SignatureException("update() not called first");
228 | message = oneShotBytes;
229 | offset = oneShotOffset;
230 | length = oneShotLength;
231 | } else {
232 | if (baos == null)
233 | message = new byte[0];
234 | else
235 | message = baos.toByteArray();
236 | offset = 0;
237 | length = message.length;
238 | }
239 | // r = H(h_b,...,h_2b-1,M)
240 | digest.update(message, offset, length);
241 | byte[] r = digest.digest();
242 |
243 | // r mod l
244 | // Reduces r from 64 bytes to 32 bytes
245 | r = sc.reduce(r);
246 |
247 | // R = rB
248 | GroupElement R = key.getParams().getB().scalarMultiply(r);
249 | byte[] Rbyte = R.toByteArray();
250 |
251 | // S = (r + H(Rbar,Abar,M)*a) mod l
252 | digest.update(Rbyte);
253 | digest.update(((EdDSAPrivateKey) key).getAbyte());
254 | digest.update(message, offset, length);
255 | byte[] h = digest.digest();
256 | h = sc.reduce(h);
257 | byte[] S = sc.multiplyAndAdd(h, a, r);
258 |
259 | // R+S
260 | int b = curve.getField().getb();
261 | ByteBuffer out = ByteBuffer.allocate(b/4);
262 | out.put(Rbyte).put(S);
263 | return out.array();
264 | }
265 |
266 | @Override
267 | protected boolean engineVerify(byte[] sigBytes) throws SignatureException {
268 | try {
269 | return x_engineVerify(sigBytes);
270 | } finally {
271 | reset();
272 | }
273 | }
274 |
275 | private boolean x_engineVerify(byte[] sigBytes) throws SignatureException {
276 | Curve curve = key.getParams().getCurve();
277 | int b = curve.getField().getb();
278 | if (sigBytes.length != b/4)
279 | throw new SignatureException("signature length is wrong");
280 |
281 | // R is first b/8 bytes of sigBytes, S is second b/8 bytes
282 | digest.update(sigBytes, 0, b/8);
283 | digest.update(((EdDSAPublicKey) key).getAbyte());
284 | // h = H(Rbar,Abar,M)
285 | byte[] message;
286 | int offset, length;
287 | if (oneShotMode) {
288 | if (oneShotBytes == null)
289 | throw new SignatureException("update() not called first");
290 | message = oneShotBytes;
291 | offset = oneShotOffset;
292 | length = oneShotLength;
293 | } else {
294 | if (baos == null)
295 | message = new byte[0];
296 | else
297 | message = baos.toByteArray();
298 | offset = 0;
299 | length = message.length;
300 | }
301 | digest.update(message, offset, length);
302 | byte[] h = digest.digest();
303 |
304 | // h mod l
305 | h = key.getParams().getScalarOps().reduce(h);
306 |
307 | byte[] Sbyte = Arrays.copyOfRange(sigBytes, b/8, b/4);
308 | // R = SB - H(Rbar,Abar,M)A
309 | GroupElement R = key.getParams().getB().doubleScalarMultiplyVariableTime(
310 | ((EdDSAPublicKey) key).getNegativeA(), h, Sbyte);
311 |
312 | // Variable time. This should be okay, because there are no secret
313 | // values used anywhere in verification.
314 | byte[] Rcalc = R.toByteArray();
315 | for (int i = 0; i < Rcalc.length; i++) {
316 | if (Rcalc[i] != sigBytes[i])
317 | return false;
318 | }
319 | return true;
320 | }
321 |
322 | /**
323 | * To efficiently sign all the data in one shot, if it is available,
324 | * use this method, which will avoid copying the data.
325 | *
326 | * Same as:
327 | *
332 | *
333 | * @param data the message to be signed
334 | * @return the signature
335 | * @throws SignatureException if update() already called
336 | * @see #ONE_SHOT_MODE
337 | */
338 | public byte[] signOneShot(byte[] data) throws SignatureException {
339 | return signOneShot(data, 0, data.length);
340 | }
341 |
342 | /**
343 | * To efficiently sign all the data in one shot, if it is available,
344 | * use this method, which will avoid copying the data.
345 | *
346 | * Same as:
347 | *
352 | *
353 | * @param data byte array containing the message to be signed
354 | * @param off the start of the message inside data
355 | * @param len the length of the message
356 | * @return the signature
357 | * @throws SignatureException if update() already called
358 | * @see #ONE_SHOT_MODE
359 | */
360 | public byte[] signOneShot(byte[] data, int off, int len) throws SignatureException {
361 | oneShotMode = true;
362 | update(data, off, len);
363 | return sign();
364 | }
365 |
366 | /**
367 | * To efficiently verify all the data in one shot, if it is available,
368 | * use this method, which will avoid copying the data.
369 | *
370 | * Same as:
371 | *
376 | *
377 | * @param data the message that was signed
378 | * @param signature of the message
379 | * @return true if the signature is valid, false otherwise
380 | * @throws SignatureException if update() already called
381 | * @see #ONE_SHOT_MODE
382 | */
383 | public boolean verifyOneShot(byte[] data, byte[] signature) throws SignatureException {
384 | return verifyOneShot(data, 0, data.length, signature, 0, signature.length);
385 | }
386 |
387 | /**
388 | * To efficiently verify all the data in one shot, if it is available,
389 | * use this method, which will avoid copying the data.
390 | *
391 | * Same as:
392 | *
397 | *
398 | * @param data byte array containing the message that was signed
399 | * @param off the start of the message inside data
400 | * @param len the length of the message
401 | * @param signature of the message
402 | * @return true if the signature is valid, false otherwise
403 | * @throws SignatureException if update() already called
404 | * @see #ONE_SHOT_MODE
405 | */
406 | public boolean verifyOneShot(byte[] data, int off, int len, byte[] signature) throws SignatureException {
407 | return verifyOneShot(data, off, len, signature, 0, signature.length);
408 | }
409 |
410 | /**
411 | * To efficiently verify all the data in one shot, if it is available,
412 | * use this method, which will avoid copying the data.
413 | *
414 | * Same as:
415 | *
420 | *
421 | * @param data the message that was signed
422 | * @param signature byte array containing the signature
423 | * @param sigoff the start of the signature
424 | * @param siglen the length of the signature
425 | * @return true if the signature is valid, false otherwise
426 | * @throws SignatureException if update() already called
427 | * @see #ONE_SHOT_MODE
428 | */
429 | public boolean verifyOneShot(byte[] data, byte[] signature, int sigoff, int siglen) throws SignatureException {
430 | return verifyOneShot(data, 0, data.length, signature, sigoff, siglen);
431 | }
432 |
433 | /**
434 | * To efficiently verify all the data in one shot, if it is available,
435 | * use this method, which will avoid copying the data.
436 | *
437 | * Same as:
438 | *
443 | *
444 | * @param data byte array containing the message that was signed
445 | * @param off the start of the message inside data
446 | * @param len the length of the message
447 | * @param signature byte array containing the signature
448 | * @param sigoff the start of the signature
449 | * @param siglen the length of the signature
450 | * @return true if the signature is valid, false otherwise
451 | * @throws SignatureException if update() already called
452 | * @see #ONE_SHOT_MODE
453 | */
454 | public boolean verifyOneShot(byte[] data, int off, int len, byte[] signature, int sigoff, int siglen) throws SignatureException {
455 | oneShotMode = true;
456 | update(data, off, len);
457 | return verify(signature, sigoff, siglen);
458 | }
459 |
460 | /**
461 | * @throws InvalidAlgorithmParameterException if spec is ONE_SHOT_MODE and update() already called
462 | * @see #ONE_SHOT_MODE
463 | */
464 | @Override
465 | protected void engineSetParameter(AlgorithmParameterSpec spec) throws InvalidAlgorithmParameterException {
466 | if (spec.equals(ONE_SHOT_MODE)) {
467 | if (oneShotBytes != null || (baos != null && baos.size() > 0))
468 | throw new InvalidAlgorithmParameterException("update() already called");
469 | oneShotMode = true;
470 | } else {
471 | super.engineSetParameter(spec);
472 | }
473 | }
474 |
475 | /**
476 | * @deprecated
477 | */
478 | @Override
479 | protected void engineSetParameter(String param, Object value) {
480 | throw new UnsupportedOperationException("engineSetParameter unsupported");
481 | }
482 |
483 | /**
484 | * @deprecated
485 | */
486 | @Override
487 | protected Object engineGetParameter(String param) {
488 | throw new UnsupportedOperationException("engineSetParameter unsupported");
489 | }
490 | }
491 |
--------------------------------------------------------------------------------
/src/net/i2p/crypto/eddsa/EdDSAKey.java:
--------------------------------------------------------------------------------
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 .
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 | }
32 |
--------------------------------------------------------------------------------
/src/net/i2p/crypto/eddsa/EdDSAPrivateKey.java:
--------------------------------------------------------------------------------
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 .
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 | *
27 | * For compatibility with older releases, decoding supports both RFC 8410 and an
28 | * older draft specifications.
29 | *
30 | * @author str4d
31 | * @see RFC 8410
32 | * @see Older draft
34 | * specification
35 | */
36 | public class EdDSAPrivateKey implements EdDSAKey, PrivateKey {
37 | private static final long serialVersionUID = 23495873459878957L;
38 | private final byte[] seed;
39 | private final byte[] h;
40 | private final byte[] a;
41 | private final GroupElement A;
42 | private final byte[] Abyte;
43 | private final EdDSAParameterSpec edDsaSpec;
44 |
45 | // OID 1.3.101.xxx
46 | private static final int OID_OLD = 100;
47 | private static final int OID_ED25519 = 112;
48 | private static final int OID_BYTE = 11;
49 | private static final int IDLEN_BYTE = 6;
50 |
51 | public EdDSAPrivateKey(EdDSAPrivateKeySpec spec) {
52 | this.seed = spec.getSeed();
53 | this.h = spec.getH();
54 | this.a = spec.geta();
55 | this.A = spec.getA();
56 | this.Abyte = this.A.toByteArray();
57 | this.edDsaSpec = spec.getParams();
58 | }
59 |
60 | public EdDSAPrivateKey(PKCS8EncodedKeySpec spec) throws InvalidKeySpecException {
61 | this(new EdDSAPrivateKeySpec(decode(spec.getEncoded()),
62 | EdDSANamedCurveTable.ED_25519_CURVE_SPEC));
63 | }
64 |
65 | @Override
66 | public String getAlgorithm() {
67 | return KEY_ALGORITHM;
68 | }
69 |
70 | @Override
71 | public String getFormat() {
72 | return "PKCS#8";
73 | }
74 |
75 | /**
76 | * Returns the private key in its canonical encoding.
77 | *
78 | * This implements the following specs:
79 | *
80 | *
General encoding: https://tools.ietf.org/html/rfc8410
81 | *
Key encoding: https://tools.ietf.org/html/rfc8032
82 | *
83 | *
84 | * This encodes the seed. It will return null if constructed from a spec which
85 | * was directly constructed from H, in which case seed is null.
86 | *
87 | * For keys in older formats, decoding and then re-encoding is sufficient to
88 | * migrate them to the canonical encoding.
89 | *
111 | * ... when encoding a OneAsymmetricKey object, the private key is wrapped
112 | * in a CurvePrivateKey object and wrapped by the OCTET STRING of the
113 | * "privateKey" field.
114 | *
115 | * CurvePrivateKey ::= OCTET STRING
116 | *
117 | *
118 | *
119 | * AlgorithmIdentifier ::= SEQUENCE {
120 | * algorithm OBJECT IDENTIFIER,
121 | * parameters ANY DEFINED BY algorithm OPTIONAL
122 | * }
123 | *
124 | * For all of the OIDs, the parameters MUST be absent.
125 | *
176 | * This will decode data conforming to RFC 8410 or as inferred from the older
177 | * draft spec at https://tools.ietf.org/html/draft-josefsson-pkix-eddsa-04.
178 | *
179 | * Per RFC 8410 section 3, this function WILL accept a parameter value of
180 | * NULL, as it is required for interoperability with the default Java keystore.
181 | * Other implementations MUST NOT copy this behaviour from here unless they also
182 | * need to read keys from the default Java keystore.
183 | *
184 | * This is really dumb for now. It does not use a general-purpose ASN.1 decoder.
185 | * See also getEncoded().
186 | *
187 | * @return 32 bytes for Ed25519, throws for other curves
188 | */
189 | private static byte[] decode(byte[] d) throws InvalidKeySpecException {
190 | try {
191 | //
192 | // Setup and OID check
193 | //
194 | int totlen = 48;
195 | int idlen = 5;
196 | int doid = d[OID_BYTE];
197 | if (doid == OID_OLD) {
198 | totlen = 49;
199 | idlen = 8;
200 | } else if (doid == OID_ED25519) {
201 | // Detect parameter value of NULL
202 | if (d[IDLEN_BYTE] == 7) {
203 | totlen = 50;
204 | idlen = 7;
205 | }
206 | } else {
207 | throw new InvalidKeySpecException("unsupported key spec");
208 | }
209 |
210 | //
211 | // Pre-decoding check
212 | //
213 | if (d.length != totlen) {
214 | throw new InvalidKeySpecException("invalid key spec length");
215 | }
216 |
217 | //
218 | // Decoding
219 | //
220 | int idx = 0;
221 | if (d[idx++] != 0x30 ||
222 | d[idx++] != (totlen - 2) ||
223 | d[idx++] != 0x02 ||
224 | d[idx++] != 1 ||
225 | d[idx++] != 0 ||
226 | d[idx++] != 0x30 ||
227 | d[idx++] != idlen ||
228 | d[idx++] != 0x06 ||
229 | d[idx++] != 3 ||
230 | d[idx++] != (1 * 40) + 3 ||
231 | d[idx++] != 101) {
232 | throw new InvalidKeySpecException("unsupported key spec");
233 | }
234 | idx++; // OID, checked above
235 | // parameters only with old OID
236 | if (doid == OID_OLD) {
237 | if (d[idx++] != 0x0a ||
238 | d[idx++] != 1 ||
239 | d[idx++] != 1) {
240 | throw new InvalidKeySpecException("unsupported key spec");
241 | }
242 | } else {
243 | // Handle parameter value of NULL
244 | //
245 | // Quoting RFC 8410 section 3:
246 | // > For all of the OIDs, the parameters MUST be absent.
247 | // >
248 | // > It is possible to find systems that require the parameters to be
249 | // > present. This can be due to either a defect in the original 1997
250 | // > syntax or a programming error where developers never got input where
251 | // > this was not true. The optimal solution is to fix these systems;
252 | // > where this is not possible, the problem needs to be restricted to
253 | // > that subsystem and not propagated to the Internet.
254 | //
255 | // Java's default keystore puts it in (when decoding as PKCS8 and then
256 | // re-encoding to pass on), so we must accept it.
257 | if (idlen == 7) {
258 | if (d[idx++] != 0x05 ||
259 | d[idx++] != 0) {
260 | throw new InvalidKeySpecException("unsupported key spec");
261 | }
262 | }
263 | // PrivateKey wrapping the CurvePrivateKey
264 | if (d[idx++] != 0x04 ||
265 | d[idx++] != 34) {
266 | throw new InvalidKeySpecException("unsupported key spec");
267 | }
268 | }
269 | if (d[idx++] != 0x04 ||
270 | d[idx++] != 32) {
271 | throw new InvalidKeySpecException("unsupported key spec");
272 | }
273 | byte[] rv = new byte[32];
274 | System.arraycopy(d, idx, rv, 0, 32);
275 | return rv;
276 | } catch (IndexOutOfBoundsException ioobe) {
277 | throw new InvalidKeySpecException(ioobe);
278 | }
279 | }
280 |
281 | @Override
282 | public EdDSAParameterSpec getParams() {
283 | return edDsaSpec;
284 | }
285 |
286 | /**
287 | * @return will be null if constructed from a spec which was directly
288 | * constructed from H
289 | */
290 | public byte[] getSeed() {
291 | return seed;
292 | }
293 |
294 | /**
295 | * @return the hash of the seed
296 | */
297 | public byte[] getH() {
298 | return h;
299 | }
300 |
301 | /**
302 | * @return the private key
303 | */
304 | public byte[] geta() {
305 | return a;
306 | }
307 |
308 | /**
309 | * @return the public key
310 | */
311 | public GroupElement getA() {
312 | return A;
313 | }
314 |
315 | /**
316 | * @return the public key
317 | */
318 | public byte[] getAbyte() {
319 | return Abyte;
320 | }
321 |
322 | @Override
323 | public int hashCode() {
324 | return Arrays.hashCode(seed);
325 | }
326 |
327 | @Override
328 | public boolean equals(Object o) {
329 | if (o == this)
330 | return true;
331 | if (!(o instanceof EdDSAPrivateKey))
332 | return false;
333 | EdDSAPrivateKey pk = (EdDSAPrivateKey) o;
334 | return Arrays.equals(seed, pk.getSeed()) &&
335 | edDsaSpec.equals(pk.getParams());
336 | }
337 | }
338 |
--------------------------------------------------------------------------------
/src/net/i2p/crypto/eddsa/EdDSAPublicKey.java:
--------------------------------------------------------------------------------
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 .
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 | *
27 | * For compatibility with older releases, decoding supports both RFC 8410 and an
28 | * older draft specification.
29 | *
30 | * @author str4d
31 | * @see RFC 8410
32 | * @see Older draft
34 | * specification
35 | */
36 | public class EdDSAPublicKey implements EdDSAKey, PublicKey {
37 | private static final long serialVersionUID = 9837459837498475L;
38 | private final GroupElement A;
39 | private GroupElement Aneg = null;
40 | private final byte[] Abyte;
41 | private final EdDSAParameterSpec edDsaSpec;
42 |
43 | // OID 1.3.101.xxx
44 | private static final int OID_OLD = 100;
45 | private static final int OID_ED25519 = 112;
46 | private static final int OID_BYTE = 8;
47 | private static final int IDLEN_BYTE = 3;
48 |
49 | public EdDSAPublicKey(EdDSAPublicKeySpec spec) {
50 | this.A = spec.getA();
51 | this.Abyte = this.A.toByteArray();
52 | this.edDsaSpec = spec.getParams();
53 | }
54 |
55 | public EdDSAPublicKey(X509EncodedKeySpec spec) throws InvalidKeySpecException {
56 | this(new EdDSAPublicKeySpec(decode(spec.getEncoded()),
57 | EdDSANamedCurveTable.ED_25519_CURVE_SPEC));
58 | }
59 |
60 | @Override
61 | public String getAlgorithm() {
62 | return KEY_ALGORITHM;
63 | }
64 |
65 | @Override
66 | public String getFormat() {
67 | return "X.509";
68 | }
69 |
70 | /**
71 | * Returns the public key in its canonical encoding.
72 | *
73 | * This implements the following specs:
74 | *
75 | *
General encoding: https://tools.ietf.org/html/rfc8410
76 | *
Key encoding: https://tools.ietf.org/html/rfc8032
77 | *
78 | *
79 | * For keys in older formats, decoding and then re-encoding is sufficient to
80 | * migrate them to the canonical encoding.
81 | *
82 | * Relevant spec quotes:
83 | *
84 | *
85 | * In the X.509 certificate, the subjectPublicKeyInfo field has the
86 | * SubjectPublicKeyInfo type, which has the following ASN.1 syntax:
87 | *
88 | * SubjectPublicKeyInfo ::= SEQUENCE {
89 | * algorithm AlgorithmIdentifier,
90 | * subjectPublicKey BIT STRING
91 | * }
92 | *
93 | *
94 | *
95 | * AlgorithmIdentifier ::= SEQUENCE {
96 | * algorithm OBJECT IDENTIFIER,
97 | * parameters ANY DEFINED BY algorithm OPTIONAL
98 | * }
99 | *
100 | * For all of the OIDs, the parameters MUST be absent.
101 | *
142 | * This will decode data conforming to RFC 8410 or the older draft spec at
143 | * https://tools.ietf.org/html/draft-josefsson-pkix-eddsa-04.
144 | *
145 | * Per RFC 8410 section 3, this function WILL accept a parameter value of
146 | * NULL, as it is required for interoperability with the default Java keystore.
147 | * Other implementations MUST NOT copy this behaviour from here unless they also
148 | * need to read keys from the default Java keystore.
149 | *
150 | * This is really dumb for now. It does not use a general-purpose ASN.1 decoder.
151 | * See also getEncoded().
152 | *
153 | * @return 32 bytes for Ed25519, throws for other curves
154 | */
155 | private static byte[] decode(byte[] d) throws InvalidKeySpecException {
156 | try {
157 | //
158 | // Setup and OID check
159 | //
160 | int totlen = 44;
161 | int idlen = 5;
162 | int doid = d[OID_BYTE];
163 | if (doid == OID_OLD) {
164 | totlen = 47;
165 | idlen = 8;
166 | } else if (doid == OID_ED25519) {
167 | // Detect parameter value of NULL
168 | if (d[IDLEN_BYTE] == 7) {
169 | totlen = 46;
170 | idlen = 7;
171 | }
172 | } else {
173 | throw new InvalidKeySpecException("unsupported key spec");
174 | }
175 |
176 | //
177 | // Pre-decoding check
178 | //
179 | if (d.length != totlen) {
180 | throw new InvalidKeySpecException("invalid key spec length");
181 | }
182 |
183 | //
184 | // Decoding
185 | //
186 | int idx = 0;
187 | if (d[idx++] != 0x30 ||
188 | d[idx++] != (totlen - 2) ||
189 | d[idx++] != 0x30 ||
190 | d[idx++] != idlen ||
191 | d[idx++] != 0x06 ||
192 | d[idx++] != 3 ||
193 | d[idx++] != (1 * 40) + 3 ||
194 | d[idx++] != 101) {
195 | throw new InvalidKeySpecException("unsupported key spec");
196 | }
197 | idx++; // OID, checked above
198 | // parameters only with old OID
199 | if (doid == OID_OLD) {
200 | if (d[idx++] != 0x0a ||
201 | d[idx++] != 1 ||
202 | d[idx++] != 1) {
203 | throw new InvalidKeySpecException("unsupported key spec");
204 | }
205 | } else {
206 | // Handle parameter value of NULL
207 | //
208 | // Quoting RFC 8410 section 3:
209 | // > For all of the OIDs, the parameters MUST be absent.
210 | // >
211 | // > It is possible to find systems that require the parameters to be
212 | // > present. This can be due to either a defect in the original 1997
213 | // > syntax or a programming error where developers never got input where
214 | // > this was not true. The optimal solution is to fix these systems;
215 | // > where this is not possible, the problem needs to be restricted to
216 | // > that subsystem and not propagated to the Internet.
217 | //
218 | // Java's default keystore puts it in (when decoding as PKCS8 and then
219 | // re-encoding to pass on), so we must accept it.
220 | if (idlen == 7) {
221 | if (d[idx++] != 0x05 ||
222 | d[idx++] != 0) {
223 | throw new InvalidKeySpecException("unsupported key spec");
224 | }
225 | }
226 | }
227 | if (d[idx++] != 0x03 ||
228 | d[idx++] != 33 ||
229 | d[idx++] != 0) {
230 | throw new InvalidKeySpecException("unsupported key spec");
231 | }
232 | byte[] rv = new byte[32];
233 | System.arraycopy(d, idx, rv, 0, 32);
234 | return rv;
235 | } catch (IndexOutOfBoundsException ioobe) {
236 | throw new InvalidKeySpecException(ioobe);
237 | }
238 | }
239 |
240 | @Override
241 | public EdDSAParameterSpec getParams() {
242 | return edDsaSpec;
243 | }
244 |
245 | public GroupElement getA() {
246 | return A;
247 | }
248 |
249 | public GroupElement getNegativeA() {
250 | // Only read Aneg once, otherwise read re-ordering might occur between
251 | // here and return. Requires all GroupElement's fields to be final.
252 | GroupElement ourAneg = Aneg;
253 | if(ourAneg == null) {
254 | ourAneg = A.negate();
255 | Aneg = ourAneg;
256 | }
257 | return ourAneg;
258 | }
259 |
260 | public byte[] getAbyte() {
261 | return Abyte;
262 | }
263 |
264 | @Override
265 | public int hashCode() {
266 | return Arrays.hashCode(Abyte);
267 | }
268 |
269 | @Override
270 | public boolean equals(Object o) {
271 | if (o == this)
272 | return true;
273 | if (!(o instanceof EdDSAPublicKey))
274 | return false;
275 | EdDSAPublicKey pk = (EdDSAPublicKey) o;
276 | return Arrays.equals(Abyte, pk.getAbyte()) &&
277 | edDsaSpec.equals(pk.getParams());
278 | }
279 | }
280 |
--------------------------------------------------------------------------------
/src/net/i2p/crypto/eddsa/EdDSASecurityProvider.java:
--------------------------------------------------------------------------------
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 .
10 | *
11 | */
12 | package net.i2p.crypto.eddsa;
13 |
14 | import java.security.AccessController;
15 | import java.security.PrivilegedAction;
16 | import java.security.Provider;
17 | import java.security.Security;
18 |
19 | /**
20 | * A security {@link Provider} that can be registered via {@link Security#addProvider(Provider)}
21 | *
22 | * @author str4d
23 | */
24 | public class EdDSASecurityProvider extends Provider {
25 | private static final long serialVersionUID = 1210027906682292307L;
26 | public static final String PROVIDER_NAME = "EdDSA";
27 |
28 | public EdDSASecurityProvider() {
29 | super(PROVIDER_NAME, 0.3 /* should match POM major.minor version */, "str4d " + PROVIDER_NAME + " security provider wrapper");
30 |
31 | AccessController.doPrivileged(new PrivilegedAction