@@ -8,6 +8,8 @@ const crypto = std.crypto;
8
8
const mem = std .mem ;
9
9
const fmt = std .fmt ;
10
10
11
+ const Sha512 = crypto .hash .sha2 .Sha512 ;
12
+
11
13
/// X25519 DH function.
12
14
pub const X25519 = struct {
13
15
/// The underlying elliptic curve.
@@ -37,33 +39,55 @@ pub const X25519 = struct {
37
39
};
38
40
var kp : KeyPair = undefined ;
39
41
mem .copy (u8 , & kp .secret_key , sk [0.. ]);
40
- try X25519 .recoverPublicKey (& kp . public_key , sk );
42
+ kp . public_key = try X25519 .recoverPublicKey (sk );
41
43
return kp ;
42
44
}
45
+
46
+ /// Create a key pair from an Ed25519 key pair
47
+ pub fn fromEd25519 (ed25519_key_pair : crypto.sign.Ed25519.KeyPair ) ! KeyPair {
48
+ const seed = ed25519_key_pair .secret_key [0.. 32];
49
+ var az : [Sha512 .digest_length ]u8 = undefined ;
50
+ Sha512 .hash (seed , & az , .{});
51
+ var sk = az [0.. 32].* ;
52
+ Curve .scalar .clamp (& sk );
53
+ const pk = try publicKeyFromEd25519 (ed25519_key_pair .public_key );
54
+ return KeyPair {
55
+ .public_key = pk ,
56
+ .secret_key = sk ,
57
+ };
58
+ }
43
59
};
44
60
45
61
/// Compute the public key for a given private key.
46
- pub fn recoverPublicKey (public_key : * [ public_length ] u8 , secret_key : [secret_length ]u8 ) ! void {
62
+ pub fn recoverPublicKey (secret_key : [secret_length ]u8 ) ! [ public_length ] u8 {
47
63
const q = try Curve .basePoint .clampedMul (secret_key );
48
- mem .copy (u8 , public_key , q .toBytes ()[0.. ]);
64
+ return q .toBytes ();
65
+ }
66
+
67
+ /// Compute the X25519 equivalent to an Ed25519 public eky.
68
+ pub fn publicKeyFromEd25519 (ed25519_public_key : [crypto .sign .Ed25519 .public_length ]u8 ) ! [public_length ]u8 {
69
+ const pk_ed = try crypto .ecc .Edwards25519 .fromBytes (ed25519_public_key );
70
+ const pk = try Curve .fromEdwards25519 (pk_ed );
71
+ return pk .toBytes ();
49
72
}
50
73
51
74
/// Compute the scalar product of a public key and a secret scalar.
52
75
/// Note that the output should not be used as a shared secret without
53
76
/// hashing it first.
54
- pub fn scalarmult (out : * [ shared_length ] u8 , secret_key : [secret_length ]u8 , public_key : [public_length ]u8 ) ! void {
77
+ pub fn scalarmult (secret_key : [secret_length ]u8 , public_key : [public_length ]u8 ) ! [ shared_length ] u8 {
55
78
const q = try Curve .fromBytes (public_key ).clampedMul (secret_key );
56
- mem . copy ( u8 , out , q .toBytes ()[0 .. ] );
79
+ return q .toBytes ();
57
80
}
58
81
};
59
82
83
+ const htest = @import ("../test.zig" );
84
+
60
85
test "x25519 public key calculation from secret key" {
61
86
var sk : [32 ]u8 = undefined ;
62
87
var pk_expected : [32 ]u8 = undefined ;
63
- var pk_calculated : [32 ]u8 = undefined ;
64
88
try fmt .hexToBytes (sk [0.. ], "8052030376d47112be7f73ed7a019293dd12ad910b654455798b4667d73de166" );
65
89
try fmt .hexToBytes (pk_expected [0.. ], "f1814f0e8ff1043d8a44d25babff3cedcae6c22c3edaa48f857ae70de2baae50" );
66
- try X25519 .recoverPublicKey (& pk_calculated , sk );
90
+ const pk_calculated = try X25519 .recoverPublicKey (sk );
67
91
std .testing .expectEqual (pk_calculated , pk_expected );
68
92
}
69
93
@@ -73,9 +97,7 @@ test "x25519 rfc7748 vector1" {
73
97
74
98
const expected_output = [32 ]u8 { 0xc3 , 0xda , 0x55 , 0x37 , 0x9d , 0xe9 , 0xc6 , 0x90 , 0x8e , 0x94 , 0xea , 0x4d , 0xf2 , 0x8d , 0x08 , 0x4f , 0x32 , 0xec , 0xcf , 0x03 , 0x49 , 0x1c , 0x71 , 0xf7 , 0x54 , 0xb4 , 0x07 , 0x55 , 0x77 , 0xa2 , 0x85 , 0x52 };
75
99
76
- var output : [32 ]u8 = undefined ;
77
-
78
- try X25519 .scalarmult (& output , secret_key , public_key );
100
+ const output = try X25519 .scalarmult (secret_key , public_key );
79
101
std .testing .expectEqual (output , expected_output );
80
102
}
81
103
@@ -85,9 +107,7 @@ test "x25519 rfc7748 vector2" {
85
107
86
108
const expected_output = [32 ]u8 { 0x95 , 0xcb , 0xde , 0x94 , 0x76 , 0xe8 , 0x90 , 0x7d , 0x7a , 0xad , 0xe4 , 0x5c , 0xb4 , 0xb8 , 0x73 , 0xf8 , 0x8b , 0x59 , 0x5a , 0x68 , 0x79 , 0x9f , 0xa1 , 0x52 , 0xe6 , 0xf8 , 0xf7 , 0x64 , 0x7a , 0xac , 0x79 , 0x57 };
87
109
88
- var output : [32 ]u8 = undefined ;
89
-
90
- try X25519 .scalarmult (& output , secret_key , public_key );
110
+ const output = try X25519 .scalarmult (secret_key , public_key );
91
111
std .testing .expectEqual (output , expected_output );
92
112
}
93
113
@@ -100,9 +120,7 @@ test "x25519 rfc7748 one iteration" {
100
120
101
121
var i : usize = 0 ;
102
122
while (i < 1 ) : (i += 1 ) {
103
- var output : [32 ]u8 = undefined ;
104
- try X25519 .scalarmult (output [0.. ], k , u );
105
-
123
+ const output = try X25519 .scalarmult (k , u );
106
124
mem .copy (u8 , u [0.. ], k [0.. ]);
107
125
mem .copy (u8 , k [0.. ], output [0.. ]);
108
126
}
@@ -124,9 +142,7 @@ test "x25519 rfc7748 1,000 iterations" {
124
142
125
143
var i : usize = 0 ;
126
144
while (i < 1000 ) : (i += 1 ) {
127
- var output : [32 ]u8 = undefined ;
128
- std .testing .expect (X25519 .scalarmult (output [0.. ], & k , & u ));
129
-
145
+ const output = try X25519 .scalarmult (& k , & u );
130
146
mem .copy (u8 , u [0.. ], k [0.. ]);
131
147
mem .copy (u8 , k [0.. ], output [0.. ]);
132
148
}
@@ -147,12 +163,17 @@ test "x25519 rfc7748 1,000,000 iterations" {
147
163
148
164
var i : usize = 0 ;
149
165
while (i < 1000000 ) : (i += 1 ) {
150
- var output : [32 ]u8 = undefined ;
151
- std .testing .expect (X25519 .scalarmult (output [0.. ], & k , & u ));
152
-
166
+ const output = try X25519 .scalarmult (& k , & u );
153
167
mem .copy (u8 , u [0.. ], k [0.. ]);
154
168
mem .copy (u8 , k [0.. ], output [0.. ]);
155
169
}
156
170
157
171
std .testing .expectEqual (k [0.. ], expected_output );
158
172
}
173
+
174
+ test "edwards25519 -> curve25519 map" {
175
+ const ed_kp = try crypto .sign .Ed25519 .KeyPair .create ([_ ]u8 {0x42 } ** 32 );
176
+ const mont_kp = try X25519 .KeyPair .fromEd25519 (ed_kp );
177
+ htest .assertEqual ("90e7595fc89e52fdfddce9c6a43d74dbf6047025ee0462d2d172e8b6a2841d6e" , & mont_kp .secret_key );
178
+ htest .assertEqual ("cc4f2cdb695dd766f34118eb67b98652fed1d8bc49c330b119bbfa8a64989378" , & mont_kp .public_key );
179
+ }
0 commit comments