1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
use super::ge25519::*;
use super::sc25519::*;
use crate::hash::sha512::{ hash_sha512, Sha512 };
use crate::util::{ make_conf_error, make_signature_verification_error, Error,
Resetable, verify::compare_v32 };
pub struct Keypair {
pub skey: [u8; SECRET_KEY_LENGTH],
pub pkey: [u8; PUBLIC_KEY_LENGTH],
}
pub const JWK_ALG_NAME: &str = "NaCl-sign-Ed25519";
pub const SEED_LENGTH: usize = 32;
pub const PUBLIC_KEY_LENGTH: usize = 32;
pub const SECRET_KEY_LENGTH: usize = 64;
fn make_keypair() -> Keypair {
Keypair {
skey: [0; SECRET_KEY_LENGTH],
pkey: [0; PUBLIC_KEY_LENGTH],
}
}
pub fn generate_keypair(seed: &[u8]) -> Keypair {
let mut az: [u8; 64] = [0; 64];
let mut scsk = make_sc25519();
let mut gepk = make_ge25519();
hash_sha512(&mut az, &seed);
az[0] &= 248;
az[31] &= 127;
az[31] |= 64;
sc25519_from32bytes(&mut scsk, &az[0..32]);
ge25519_scalarmult_base(&mut gepk, &scsk);
let mut pair = make_keypair();
ge25519_pack(&mut pair.pkey, &gepk);
pair.skey[0..32].copy_from_slice(seed);
pair.skey[32..].copy_from_slice(&pair.pkey);
pair
}
pub fn extract_pkey(sk: &[u8]) -> Result<Vec<u8>, Error> {
if sk.len() != SECRET_KEY_LENGTH { return Err(make_conf_error(format!(
"Length of given sk array is {} instead of {}",
sk.len(), SECRET_KEY_LENGTH))); }
let mut pk: Vec<u8> = vec![0; 32];
pk[..].copy_from_slice(&sk[32..]);
Ok(pk)
}
pub fn sign(m: &[u8], sk: &[u8]) -> Result<Vec<u8>, Error> {
if sk.len() != 64 { return Err(make_conf_error(format!(
"Secret key array is {} bytes long instead of 64", sk.len()))); }
if m.len() == 0 { return Err(make_conf_error(format!(
"Message array m is empty"))); }
let mut pk: [u8; 32] = [0; 32];
pk.copy_from_slice(&sk[32..]);
let mut az: [u8; 64] = [0; 64];
hash_sha512(&mut az, &sk[0..32]);
az[0] &= 248;
az[31] &= 127;
az[31] |= 64;
let mut sm: Vec<u8> = vec![0; m.len()+64];
sm[64..].copy_from_slice(m);
sm[32..64].copy_from_slice(&az[32..64]);
let mut nonce: [u8; 64] = [0; 64];
hash_sha512(&mut nonce, &sm[32..]);
let mut sck = make_sc25519();
let mut ger = make_ge25519();
sc25519_from64bytes(&mut sck, &nonce);
ge25519_scalarmult_base(&mut ger, &sck);
ge25519_pack(&mut sm[0..32], &ger);
sm[32..64].copy_from_slice(&pk);
let mut hram: [u8; 64] = [0; 64];
hash_sha512(&mut hram, &sm);
let mut scs = make_sc25519();
let mut scs_temp = make_sc25519();
let mut scsk = make_sc25519();
sc25519_from64bytes(&mut scs, &hram);
sc25519_from32bytes(&mut scsk, &az[0..32]);
sc25519_mul(&mut scs_temp, &scs, &scsk);
sc25519_add(&mut scs, &scs_temp, &sck);
sc25519_to32bytes(&mut sm[32..64], &scs);
az.reset();
scs_temp.v.reset();
scsk.v.reset();
nonce.reset();
hram.reset();
sck.v.reset();
ger.x.v.reset();
Ok(sm)
}
pub fn signature(m: &[u8], sk: &[u8]) -> Result<Vec<u8>, Error> {
if sk.len() != 64 { return Err(make_conf_error(format!(
"Secret key array is {} bytes long instead of 64", sk.len()))); }
if m.len() == 0 { return Err(make_conf_error(format!(
"Message array m is empty"))); }
let mut pk: [u8; 32] = [0; 32];
pk.copy_from_slice(&sk[32..]);
let mut az: [u8; 64] = [0; 64];
hash_sha512(&mut az, &sk[0..32]);
az[0] &= 248;
az[31] &= 127;
az[31] |= 64;
let mut sig: Vec<u8> = vec![0; 64];
sig[32..64].copy_from_slice(&az[32..]);
let mut hasher = Sha512::new();
hasher.update(&sig[32..]);
hasher.update(m);
let nonce = hasher.digest();
let mut sck = make_sc25519();
let mut ger = make_ge25519();
sc25519_from64bytes(&mut sck, &nonce);
ge25519_scalarmult_base(&mut ger, &sck);
ge25519_pack(&mut sig[0..32], &ger);
hasher.update(&sig[0..32]);
hasher.update(&pk);
hasher.update(m);
let hram = hasher.digest();
let mut scs = make_sc25519();
let mut scs_temp = make_sc25519();
let mut scsk = make_sc25519();
sc25519_from64bytes(&mut scs, &hram);
sc25519_from32bytes(&mut scsk, &az[0..32]);
sc25519_mul(&mut scs_temp, &scs, &scsk);
sc25519_add(&mut scs, &scs_temp, &sck);
sc25519_to32bytes(&mut sig[32..64], &scs);
Ok(sig)
}
pub fn open(sm: &[u8], pk: &[u8]) -> Result<Vec<u8>, Error> {
if pk.len() != 32 { return Err(make_conf_error(format!(
"Public key array is {} bytes long instead of 32", pk.len()))); }
let mut get1 = make_ge25519();
if (sm.len() < 64)
|| ((sm[63] & 224) != 0)
|| !ge25519_unpackneg_vartime(&mut get1, pk) {
return Err(make_signature_verification_error());
}
let mut rcopy: [u8; 32] = [0; 32];
rcopy.copy_from_slice(&sm[0..32]);
let mut scs = make_sc25519();
sc25519_from32bytes(&mut scs, &sm[32..64]);
let mut m: Vec<u8> = sm.to_vec();
m[32..64].copy_from_slice(pk);
let mut hram: [u8; 64] = [0; 64];
hash_sha512(&mut hram, &m);
let mut schram = make_sc25519();
sc25519_from64bytes(&mut schram, &hram);
let mut get2 = make_ge25519();
ge25519_double_scalarmult_vartime(
&mut get2, &get1, &schram, &ge25519_base, &scs);
let mut rcheck: [u8; 32] = [0; 32];
ge25519_pack(&mut rcheck, &get2);
if compare_v32(&rcopy, &rcheck) {
Ok(m[64..].to_vec())
} else {
Err(make_signature_verification_error())
}
}
pub fn verify(sig: &[u8], m: &[u8], pk: &[u8]) -> Result<bool, Error> {
if pk.len() != 32 { return Err(make_conf_error(format!(
"Public key array is {} bytes long instead of 32", pk.len()))); }
let mut get1 = make_ge25519();
if (sig.len() < 64)
|| ((sig[63] & 224) != 0)
|| !ge25519_unpackneg_vartime(&mut get1, pk) {
return Ok(false);
}
let mut rcopy: [u8; 32] = [0; 32];
rcopy.copy_from_slice(&sig[0..32]);
let mut scs = make_sc25519();
sc25519_from32bytes(&mut scs, &sig[32..64]);
let mut hasher = Sha512::new();
hasher.update(&sig[0..32]);
hasher.update(pk);
hasher.update(m);
let hram = hasher.digest();
let mut schram = make_sc25519();
sc25519_from64bytes(&mut schram, &hram);
let mut get2 = make_ge25519();
ge25519_double_scalarmult_vartime(
&mut get2, &get1, &schram, &ge25519_base, &scs);
let mut rcheck: [u8; 32] = [0; 32];
ge25519_pack(&mut rcheck, &get2);
Ok(compare_v32(&rcopy, &rcheck))
}
#[cfg(test)]
mod tests {
use super::{ generate_keypair, sign, open, signature, verify, SEED_LENGTH, PUBLIC_KEY_LENGTH };
use crate::util::verify::compare;
use crate::util::ops::incr;
#[test]
fn test1() {
let key_seed: [u8; SEED_LENGTH] = [
0xae, 0x38, 0x86, 0x7b, 0xd2, 0x65, 0xcb, 0x86, 0x57, 0x0e,
0x90, 0x0e, 0x24, 0xa1, 0x75, 0x03, 0x2f, 0x74, 0xab, 0x4d,
0xa1, 0xbd, 0xf5, 0xc9, 0x12, 0x3e, 0x4c, 0x98, 0x12, 0xaa,
0x0c, 0x95 ];
let expected_pkey: [u8; 32] = [
0xd0, 0xa5, 0xe8, 0xca, 0xeb, 0xff, 0xb8, 0x2a, 0x5e, 0x6d,
0x24, 0x4a, 0x94, 0x94, 0x3c, 0xd5, 0x34, 0x03, 0x68, 0x0d,
0x93, 0x02, 0x82, 0xb2, 0xc0, 0x7b, 0x1f, 0xfd, 0xbd, 0x21,
0x39, 0xd0 ];
let pair = generate_keypair(&key_seed);
assert!(compare(&pair.pkey, &expected_pkey));
let m = "testing\n".as_bytes();
let expected_signed_m = [
0x74, 0xff, 0xb4, 0x4b, 0xf0, 0x46, 0xf9, 0xe9, 0x86, 0x87,
0xa4, 0x6b, 0x28, 0xc3, 0x38, 0x6e, 0x78, 0xb0, 0x62, 0x53,
0x2f, 0xf6, 0x45, 0x39, 0x97, 0x24, 0x0b, 0xa0, 0xab, 0xee,
0x5d, 0x7e, 0x44, 0xc8, 0x80, 0x2f, 0x86, 0xf5, 0x34, 0x21,
0x32, 0x7f, 0xb4, 0x3f, 0xa6, 0xd8, 0x9c, 0x0a, 0xdf, 0x5b,
0x91, 0x04, 0x9a, 0x67, 0xba, 0x3b, 0xf0, 0xdd, 0x7c, 0xd1,
0x5d, 0xbd, 0x89, 0x0a, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e,
0x67, 0x0a ];
let signed_m = sign(&m, &pair.skey).unwrap();
assert!(compare(&signed_m, &expected_signed_m));
let result = open(&signed_m, &pair.pkey).unwrap();
assert!(compare(&result, &m));
let sig = signature(&m, &pair.skey).unwrap();
assert!(compare(&sig, &expected_signed_m[0..64]));
assert!(verify(&sig, &m, &pair.pkey).unwrap());
}
#[test]
fn test2() {
struct SeedAndPKey {
seed: [u8; SEED_LENGTH],
pkey: [u8; PUBLIC_KEY_LENGTH]
}
let seed_and_pkeys = [
SeedAndPKey {
seed: [ 0x13, 0xca, 0x75, 0xbe, 0x97, 0x13, 0x61, 0x62, 0xb4, 0x36,
0x95, 0xfa, 0xd2, 0xa2, 0xb2, 0xcb, 0xb4, 0x35, 0xc9, 0xad,
0x0a, 0x0f, 0xf5, 0xb6, 0x58, 0x7e, 0xd9, 0xd0, 0xcf, 0xfb,
0x59, 0xec ],
pkey: [ 0xed, 0xef, 0xc9, 0x54, 0x05, 0xce, 0x9f, 0x81, 0x7d, 0x2b,
0xd8, 0xb9, 0x48, 0x0c, 0x3f, 0xfb, 0xa8, 0xd9, 0x6a, 0x6e,
0x00, 0x87, 0x90, 0x6a, 0xe2, 0xe9, 0xa9, 0x2f, 0xf5, 0xa9,
0xdc, 0xe7 ] },
SeedAndPKey {
seed: [ 0xee, 0xdd, 0xec, 0xfa, 0x96, 0x70, 0x23, 0x6b, 0xdd, 0x4b,
0xba, 0x59, 0xae, 0x69, 0x65, 0x7a, 0x83, 0xb9, 0x74, 0x9a,
0xd7, 0xd7, 0x68, 0x21, 0xe8, 0x64, 0x1a, 0x4b, 0xe3, 0x1a,
0x5b, 0x74 ],
pkey: [ 0x20, 0x95, 0x60, 0x39, 0xa6, 0x6f, 0x66, 0x63, 0xe0, 0x08,
0xa3, 0xac, 0xd2, 0x96, 0x76, 0x5e, 0xea, 0x21, 0xe5, 0x6c,
0x3d, 0x2f, 0xea, 0xb7, 0xc7, 0x4d, 0x0c, 0x9d, 0x2f, 0x6e,
0xc5, 0xe4 ] },
SeedAndPKey {
seed: [ 0x9a, 0xae, 0xe7, 0xc6, 0xf9, 0xd7, 0xe4, 0x9c, 0x64, 0x05,
0xa9, 0x81, 0xa6, 0xe3, 0xa6, 0x52, 0x5b, 0x62, 0x5f, 0xa1,
0xae, 0x92, 0x5c, 0xec, 0x12, 0x2f, 0x2d, 0xe3, 0x3d, 0x4d,
0x30, 0x3c ],
pkey: [ 0xe3, 0x43, 0x33, 0xb1, 0x42, 0xc5, 0xc5, 0x86, 0x14, 0x86,
0x46, 0x37, 0x0d, 0xfc, 0xf7, 0x21, 0x48, 0x50, 0x24, 0x6d,
0x69, 0x7f, 0x6d, 0x32, 0x60, 0x47, 0xdf, 0xa7, 0x85, 0xd6,
0xee, 0xd5 ] } ];
let m = "<From https://doc.rust-lang.org/book/second-edition/ch04-03-slices.html>
We’ll discuss iterators in more detail in Chapter 13. For now, know that iter is a method that returns each element in a collection and that enumerate wraps the result of iter and returns each element as part of a tuple instead. The first element of the tuple returned from enumerate is the index, and the second element is a reference to the element. This is a bit more convenient than calculating the index ourselves.
Because the enumerate method returns a tuple, we can use patterns to destructure that tuple, just like everywhere else in Rust. So in the for loop, we specify a pattern that has i for the index in the tuple and &item for the single byte in the tuple. Because we get a reference to the element from .iter().enumerate(), we use & in the pattern.
Inside the for loop, we search for the byte that represents the space by using the byte literal syntax. If we find a space, we return the position. Otherwise, we return the length of the string by using s.len():
if item == b' ' {
return i;
}
}
s.len()
We now have a way to find out the index of the end of the first word in the string, but there’s a problem. We’re returning a usize on its own, but it’s only a meaningful number in the context of the &String. In other words, because it’s a separate value from the String, there’s no guarantee that it will still be valid in the future. Consider the program in Listing 4-8 that uses the first_word function from Listing 4-7:
...".as_bytes();
for sp in seed_and_pkeys.iter() {
let pair = generate_keypair(&sp.seed);
assert!(compare(&pair.pkey, &sp.pkey));
let signed_m = sign(&m, &pair.skey).unwrap();
let mut sig = signature(&m, &pair.skey).unwrap();
assert!(compare(&sig, &signed_m[0..64]));
let result = open(&signed_m, &pair.pkey).unwrap();
assert!(compare(&result, &m));
assert!(verify(&sig, &m, &pair.pkey).unwrap());
incr!(sig[5], 1 as u8);
assert!(!verify(&sig, &m, &pair.pkey).unwrap());
}
}
}