@@ -24,31 +24,52 @@ public class Ed25519ExpandedPrivateKey {
24
24
/**
25
25
* The prefix component of the expanded Ed25519 private key.
26
26
*
27
- * Note that because the `final` keyword only makes the reference a constant, the
28
- * contents of this byte[] could in theory be mutated (via reflection, as this field
29
- * is private). This misunderstanding was a contributor to the "final" security bug in
30
- * Google's Java implementation of Ed25519 [0]. However, the primary cause of that bug
31
- * was their reuse of the prefix buffer to hold the result of calculating S; we are
32
- * protected from that failure mode by the type-safe curve25519-elisabeth API.
27
+ * Note that because the `final` keyword only makes the reference a constant,
28
+ * the contents of this byte[] could in theory be mutated (via reflection, as
29
+ * this field is private). This misunderstanding was a contributor to the
30
+ * "final" security bug in Google's Java implementation of Ed25519 [0]. However,
31
+ * the primary cause of that bug was their reuse of the prefix buffer to hold
32
+ * the result of calculating S; we are protected from that failure mode by the
33
+ * type-safe curve25519-elisabeth API.
33
34
*
34
35
* [0] https://github.com/cryptosubtlety/final-security-bug
35
36
*/
36
37
private final byte [] prefix ;
37
38
39
+ /**
40
+ * The public key corresponding to this private key.
41
+ *
42
+ * We store the public key inside the expanded private key so that we always use
43
+ * the correct public key when creating signatures, while caching its
44
+ * computation along with the other expanded components.
45
+ *
46
+ * Version 0.1.0 of ed25519-elisabeth required the caller to provide the public
47
+ * key. This allowed the caller to control how the public key was cached in
48
+ * memory, but it created an opportunity for misuse: if two signatures were
49
+ * created using different public keys, the private scalar could be recovered
50
+ * from the signatures [0] [1]. We now always cache the public key ourselves to
51
+ * provide a safer signing API.
52
+ *
53
+ * [0] https://github.com/jedisct1/libsodium/issues/170
54
+ * [1] https://github.com/MystenLabs/ed25519-unsafe-libs
55
+ */
56
+ private final Ed25519PublicKey publicKey ;
57
+
38
58
Ed25519ExpandedPrivateKey (Scalar s , byte [] prefix ) {
39
59
this .s = s ;
40
60
this .prefix = prefix ;
61
+ EdwardsPoint A = Constants .ED25519_BASEPOINT_TABLE .multiply (this .s );
62
+ this .publicKey = new Ed25519PublicKey (A );
41
63
}
42
64
43
65
/**
44
- * Derive the Ed25519 public key corresponding to this expanded private key.
66
+ * Returns the Ed25519 public key corresponding to this expanded private key.
45
67
*
46
68
* @return the public key.
47
69
*/
48
70
@ NotNull
49
71
public Ed25519PublicKey derivePublic () {
50
- EdwardsPoint A = Constants .ED25519_BASEPOINT_TABLE .multiply (this .s );
51
- return new Ed25519PublicKey (A );
72
+ return this .publicKey ;
52
73
}
53
74
54
75
/**
@@ -57,8 +78,8 @@ public Ed25519PublicKey derivePublic() {
57
78
* @return the signature.
58
79
*/
59
80
@ NotNull
60
- public Ed25519Signature sign (@ NotNull byte [] message , @ NotNull Ed25519PublicKey publicKey ) {
61
- return this .sign (message , 0 , message .length , publicKey );
81
+ public Ed25519Signature sign (@ NotNull byte [] message ) {
82
+ return this .sign (message , 0 , message .length );
62
83
}
63
84
64
85
/**
@@ -67,7 +88,7 @@ public Ed25519Signature sign(@NotNull byte[] message, @NotNull Ed25519PublicKey
67
88
* @return the signature.
68
89
*/
69
90
@ NotNull
70
- public Ed25519Signature sign (@ NotNull byte [] message , int offset , int length , @ NotNull Ed25519PublicKey publicKey ) {
91
+ public Ed25519Signature sign (@ NotNull byte [] message , int offset , int length ) {
71
92
// @formatter:off
72
93
// RFC 8032, section 5.1:
73
94
// PH(x) | x (i.e., the identity function)
@@ -101,7 +122,7 @@ public Ed25519Signature sign(@NotNull byte[] message, int offset, int length, @N
101
122
// @formatter:on
102
123
h .reset ();
103
124
h .update (R .toByteArray ());
104
- h .update (publicKey .toByteArray ());
125
+ h .update (this . publicKey .toByteArray ());
105
126
h .update (message , offset , length );
106
127
Scalar k = Scalar .fromBytesModOrderWide (h .digest ());
107
128
0 commit comments