1#![no_std]
2
3mod curve;
4pub mod hasher;
5mod scalar;
6
7use crate::curve::multiscalar_multiply_edwards;
8use crate::hasher::Hasher;
9use crate::scalar::scalar_from_bytes_mod_order_wide_into;
10pub use solana_address::Address;
11use solana_program_error::ProgramError;
12
13pub type Signature = [u8; 64];
14
15const NEG_G: [u8; 32] = [
20 88, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102,
21 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 230,
22];
23
24#[cfg(all(
29 any(target_arch = "bpf", target_os = "solana"),
30 not(feature = "fast-sha512")
31))]
32type DefaultHasher = crate::hasher::Sha512Syscall;
33#[cfg(all(
34 any(target_arch = "bpf", target_os = "solana"),
35 feature = "fast-sha512"
36))]
37type DefaultHasher = crate::hasher::FastSha512;
38#[cfg(not(any(target_arch = "bpf", target_os = "solana")))]
39type DefaultHasher = crate::hasher::Sha512;
40
41#[inline(always)]
57pub fn verify(pubkey: &Address, sig: &Signature, messages: &[&[u8]]) -> Result<(), ProgramError> {
58 verify_with_hasher::<DefaultHasher>(pubkey, sig, messages)
59}
60
61#[inline(always)]
64pub fn verify_strict(
65 pubkey: &Address,
66 sig: &Signature,
67 messages: &[&[u8]],
68) -> Result<(), ProgramError> {
69 verify_with_hasher_strict::<DefaultHasher>(pubkey, sig, messages)
70}
71
72#[inline(always)]
75pub fn verify_with_hasher<H: Hasher>(
76 pubkey: &Address,
77 sig: &Signature,
78 messages: &[&[u8]],
79) -> Result<(), ProgramError> {
80 let sig_r: &[u8; 32] = unsafe { &*(sig.as_ptr() as *const [u8; 32]) };
82 let pubkey_bytes: &[u8; 32] = unsafe { &*(pubkey as *const Address as *const [u8; 32]) };
84
85 validate_point_encodings(pubkey_bytes, sig_r)?;
86
87 let challenge = challenge::<H>(sig_r, pubkey, messages);
88
89 verify_prehashed_unchecked(pubkey, sig, &challenge)
90}
91
92#[inline(always)]
94pub fn verify_with_hasher_strict<H: Hasher>(
95 pubkey: &Address,
96 sig: &Signature,
97 messages: &[&[u8]],
98) -> Result<(), ProgramError> {
99 let sig_r: &[u8; 32] = unsafe { &*(sig.as_ptr() as *const [u8; 32]) };
101 let pubkey_bytes: &[u8; 32] = unsafe { &*(pubkey as *const Address as *const [u8; 32]) };
103
104 validate_points_strict(pubkey_bytes, sig_r)?;
105
106 let challenge = challenge::<H>(sig_r, pubkey, messages);
107
108 verify_prehashed_unchecked(pubkey, sig, &challenge)
109}
110
111#[inline(always)]
134#[allow(non_snake_case)]
135pub fn verify_prehashed(
136 pubkey: &Address,
137 sig: &Signature,
138 challenge: &[u8; 64],
139) -> Result<(), ProgramError> {
140 let sig_r: &[u8; 32] = unsafe { &*(sig.as_ptr() as *const [u8; 32]) };
142 let pubkey_bytes: &[u8; 32] = unsafe { &*(pubkey as *const Address as *const [u8; 32]) };
144
145 validate_point_encodings(pubkey_bytes, sig_r)?;
146 verify_prehashed_unchecked(pubkey, sig, challenge)
147}
148
149#[inline(always)]
150#[allow(non_snake_case)]
151fn verify_prehashed_unchecked(
152 pubkey: &Address,
153 sig: &Signature,
154 challenge: &[u8; 64],
155) -> Result<(), ProgramError> {
156 let (sig_r, sig_s): &([u8; 32], [u8; 32]) = unsafe { &*(sig as *const [u8; 64] as *const _) };
158
159 let pubkey_bytes: &[u8; 32] = unsafe { &*(pubkey as *const Address as *const [u8; 32]) };
161
162 let mut scalars: [[u8; 32]; 2] = [*sig_s, [0u8; 32]];
166 scalar_from_bytes_mod_order_wide_into(challenge, &mut scalars[1]);
167
168 let points = [NEG_G, *pubkey_bytes];
169
170 let neg_R =
172 multiscalar_multiply_edwards(&scalars, &points).ok_or(ProgramError::InvalidArgument)?;
173
174 let mut neg_sig_R = *sig_r;
176 neg_sig_R[31] ^= 0x80;
177
178 if neg_R == neg_sig_R {
179 Ok(())
180 } else {
181 Err(ProgramError::InvalidArgument)
182 }
183}
184
185#[inline(always)]
187#[allow(non_snake_case)]
188pub fn verify_prehashed_strict(
189 pubkey: &Address,
190 sig: &Signature,
191 challenge: &[u8; 64],
192) -> Result<(), ProgramError> {
193 let sig_r: &[u8; 32] = unsafe { &*(sig.as_ptr() as *const [u8; 32]) };
195 let pubkey_bytes: &[u8; 32] = unsafe { &*(pubkey as *const Address as *const [u8; 32]) };
197
198 validate_points_strict(pubkey_bytes, sig_r)?;
199 verify_prehashed_unchecked(pubkey, sig, challenge)
200}
201
202#[inline(always)]
203fn challenge<H: Hasher>(sig_r: &[u8; 32], pubkey: &Address, messagev: &[&[u8]]) -> [u8; 64] {
204 let mut hasher = H::new();
205 hasher.update(sig_r);
206 hasher.update(pubkey.as_ref());
207
208 for message in messagev {
209 hasher.update(message);
210 }
211
212 hasher.finalize()
213}
214
215#[cfg(test)]
216const G: [u8; 32] = [
217 88, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102,
218 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102,
219];
220
221const EIGHT_TORSION: [[u8; 32]; 8] = [
223 [
224 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
225 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
226 0x00, 0x00,
227 ],
228 [
229 0xc7, 0x17, 0x6a, 0x70, 0x3d, 0x4d, 0xd8, 0x4f, 0xba, 0x3c, 0x0b, 0x76, 0x0d, 0x10, 0x67,
230 0x0f, 0x2a, 0x20, 0x53, 0xfa, 0x2c, 0x39, 0xcc, 0xc6, 0x4e, 0xc7, 0xfd, 0x77, 0x92, 0xac,
231 0x03, 0x7a,
232 ],
233 [
234 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
235 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
236 0x00, 0x80,
237 ],
238 [
239 0x26, 0xe8, 0x95, 0x8f, 0xc2, 0xb2, 0x27, 0xb0, 0x45, 0xc3, 0xf4, 0x89, 0xf2, 0xef, 0x98,
240 0xf0, 0xd5, 0xdf, 0xac, 0x05, 0xd3, 0xc6, 0x33, 0x39, 0xb1, 0x38, 0x02, 0x88, 0x6d, 0x53,
241 0xfc, 0x05,
242 ],
243 [
244 0xec, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
245 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
246 0xff, 0x7f,
247 ],
248 [
249 0x26, 0xe8, 0x95, 0x8f, 0xc2, 0xb2, 0x27, 0xb0, 0x45, 0xc3, 0xf4, 0x89, 0xf2, 0xef, 0x98,
250 0xf0, 0xd5, 0xdf, 0xac, 0x05, 0xd3, 0xc6, 0x33, 0x39, 0xb1, 0x38, 0x02, 0x88, 0x6d, 0x53,
251 0xfc, 0x85,
252 ],
253 [
254 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
255 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
256 0x00, 0x00,
257 ],
258 [
259 0xc7, 0x17, 0x6a, 0x70, 0x3d, 0x4d, 0xd8, 0x4f, 0xba, 0x3c, 0x0b, 0x76, 0x0d, 0x10, 0x67,
260 0x0f, 0x2a, 0x20, 0x53, 0xfa, 0x2c, 0x39, 0xcc, 0xc6, 0x4e, 0xc7, 0xfd, 0x77, 0x92, 0xac,
261 0x03, 0xfa,
262 ],
263];
264
265#[inline(always)]
266fn is_canonical_small_order(point: &[u8; 32]) -> bool {
267 EIGHT_TORSION.contains(point)
268}
269
270#[inline(always)]
274fn is_canonical_point_encoding(point: &[u8; 32]) -> bool {
275 const TOP: u64 = 0x7fff_ffff_ffff_ffff;
276 const LOW_P: u64 = 0xffff_ffff_ffff_ffed;
277
278 let final_byte = point[31];
281 match final_byte & 0x7f {
282 0x00 if final_byte & 0x80 != 0 => {
283 point[0] != 1 || point[1..31].iter().any(|&byte| byte != 0)
286 }
287 0x7f => {
288 let src = point.as_ptr() as *const u64;
291 let encoded_top = u64::from_le(unsafe { core::ptr::read_unaligned(src.add(3)) });
293 let top = encoded_top & TOP;
294 if top != TOP {
295 return true;
296 }
297
298 let middle_high = u64::from_le(unsafe { core::ptr::read_unaligned(src.add(2)) });
299 if middle_high != u64::MAX {
300 return true;
301 }
302
303 let middle_low = u64::from_le(unsafe { core::ptr::read_unaligned(src.add(1)) });
304 if middle_low != u64::MAX {
305 return true;
306 }
307
308 let low = u64::from_le(unsafe { core::ptr::read_unaligned(src) });
309 low < LOW_P && !(final_byte & 0x80 != 0 && low == LOW_P - 1)
310 }
311 _ => true,
312 }
313}
314
315#[inline(always)]
316fn is_negative_zero(point: &[u8; 32]) -> bool {
317 if point[31] & 0x80 == 0 {
320 return false;
321 }
322
323 match point[31] & 0x7f {
324 0x00 => point[0] == 1 && point[1..31].iter().all(|&byte| byte == 0),
325 0x7f => point[0] == 0xec && point[1..31].iter().all(|&byte| byte == 0xff),
326 _ => false,
327 }
328}
329
330#[inline(always)]
331fn validate_point_encodings(pubkey: &[u8; 32], sig_r: &[u8; 32]) -> Result<(), ProgramError> {
332 if !is_canonical_point_encoding(pubkey) || is_negative_zero(sig_r) {
338 Err(ProgramError::InvalidArgument)
339 } else {
340 Ok(())
341 }
342}
343
344#[inline(always)]
345fn validate_points_strict(pubkey: &[u8; 32], sig_r: &[u8; 32]) -> Result<(), ProgramError> {
346 validate_point_encodings(pubkey, sig_r)?;
347
348 if is_canonical_small_order(pubkey) || is_canonical_small_order(sig_r) {
349 Err(ProgramError::InvalidArgument)
350 } else {
351 Ok(())
352 }
353}
354
355#[cfg(test)]
356mod tests {
357 use super::*;
358 use crate::hasher::{Hasher, Sha512};
359 use curve25519_dalek::constants;
360
361 fn plus_p(y: u8, sign: u8) -> [u8; 32] {
362 let mut bytes = [0xffu8; 32];
363 bytes[0] = 0xed + y;
364 bytes[31] = 0x7f | sign;
365 bytes
366 }
367
368 fn forged_signature() -> [u8; 64] {
369 let mut sig = [0u8; 64];
370 sig[..32].copy_from_slice(&G);
371 sig[32] = 1;
372 sig
373 }
374
375 #[test]
376 fn test_small_order() {
377 assert!(!is_canonical_small_order(&G));
378 for (expected, point) in EIGHT_TORSION.iter().zip(constants::EIGHT_TORSION) {
379 assert_eq!(*expected, point.compress().to_bytes());
380 assert!(is_canonical_small_order(expected));
381 }
382 }
383
384 #[test]
385 fn test_canonical_point_encoding_boundaries() {
386 assert!(is_canonical_point_encoding(&G));
387 assert!(is_canonical_point_encoding(&NEG_G));
388
389 assert!(is_canonical_point_encoding(&EIGHT_TORSION[4]));
391
392 for offset in 0..19 {
395 for sign in [0x00u8, 0x80] {
396 assert!(!is_canonical_point_encoding(&plus_p(offset, sign)));
397 }
398 }
399
400 for i in [0, 4] {
403 let mut negative_zero = EIGHT_TORSION[i];
404 negative_zero[31] |= 0x80;
405 assert!(!is_canonical_point_encoding(&negative_zero));
406 }
407 }
408
409 #[test]
410 fn test_noncanonical_alias_rejected() {
411 let sig = forged_signature();
412 let pubkey = Address::from(plus_p(1, 0));
413
414 assert_eq!(
415 verify(&pubkey, &sig, &[b"anything"]),
416 Err(ProgramError::InvalidArgument)
417 );
418 assert_eq!(
419 verify_strict(&pubkey, &sig, &[b"anything"]),
420 Err(ProgramError::InvalidArgument)
421 );
422 }
423
424 #[test]
425 fn test_noncanonical_r_rejected_by_canonical_result_comparison() {
426 let pubkey = Address::from(G);
427 let challenge = [0u8; 64];
428
429 for offset in 0..19 {
430 for sign in [0x00u8, 0x80] {
431 let mut sig = [0u8; 64];
432 sig[..32].copy_from_slice(&plus_p(offset, sign));
433
434 assert_eq!(
435 verify_prehashed(&pubkey, &sig, &challenge),
436 Err(ProgramError::InvalidArgument),
437 "offset={offset}, sign={sign:#x}"
438 );
439 }
440 }
441 }
442
443 #[test]
444 fn test_strict_rejects_canonical_small_order_key() {
445 let sig = forged_signature();
446 let pubkey = Address::from(EIGHT_TORSION[0]);
447
448 assert!(verify(&pubkey, &sig, &[b"anything"]).is_ok());
451 assert_eq!(
452 verify_strict(&pubkey, &sig, &[b"anything"]),
453 Err(ProgramError::InvalidArgument)
454 );
455 }
456
457 #[test]
458 fn test_negative_zero_r_rejected() {
459 let pubkey = Address::from(G);
460 let mut r = EIGHT_TORSION[0];
461 r[31] |= 0x80;
462 let mut sig = [0u8; 64];
463 sig[..32].copy_from_slice(&r);
464 sig[32] = 1;
465 let mut challenge = [0u8; 64];
466 challenge[0] = 1;
467
468 assert_eq!(
469 verify_prehashed(&pubkey, &sig, &challenge),
470 Err(ProgramError::InvalidArgument)
471 );
472 assert_eq!(
473 verify_prehashed_strict(&pubkey, &sig, &challenge),
474 Err(ProgramError::InvalidArgument)
475 );
476 }
477
478 #[test]
479 fn test_base_point() {
480 let base_point = constants::ED25519_BASEPOINT_POINT;
481 let compressed = base_point.compress();
482 let bytes = compressed.to_bytes();
483 assert_eq!(bytes, G);
484 }
485
486 #[test]
487 fn test_hello_world() {
488 let pubkey = Address::from([
489 73, 73, 170, 112, 75, 235, 154, 81, 203, 8, 44, 245, 233, 18, 204, 136, 162, 9, 233,
490 49, 154, 201, 171, 175, 47, 6, 223, 101, 105, 80, 95, 166,
491 ]);
492 let sig: [u8; 64] = [
493 164, 121, 89, 242, 88, 29, 80, 177, 104, 20, 102, 176, 48, 133, 68, 8, 105, 33, 58, 86,
494 28, 108, 198, 140, 160, 219, 62, 184, 154, 181, 140, 33, 35, 102, 183, 203, 111, 33,
495 55, 170, 180, 138, 92, 196, 185, 201, 122, 167, 15, 112, 9, 228, 226, 112, 111, 10,
496 142, 73, 85, 43, 81, 152, 204, 13,
497 ];
498
499 assert!(verify(&pubkey, &sig, &[b"hello world"]).is_ok());
500 assert!(verify(&pubkey, &sig, &[b"not the right message"]).is_err());
501 }
502
503 #[test]
504 fn test_hello_world_with_hasher() {
505 let pubkey = Address::from([
506 73, 73, 170, 112, 75, 235, 154, 81, 203, 8, 44, 245, 233, 18, 204, 136, 162, 9, 233,
507 49, 154, 201, 171, 175, 47, 6, 223, 101, 105, 80, 95, 166,
508 ]);
509 let sig: [u8; 64] = [
510 164, 121, 89, 242, 88, 29, 80, 177, 104, 20, 102, 176, 48, 133, 68, 8, 105, 33, 58, 86,
511 28, 108, 198, 140, 160, 219, 62, 184, 154, 181, 140, 33, 35, 102, 183, 203, 111, 33,
512 55, 170, 180, 138, 92, 196, 185, 201, 122, 167, 15, 112, 9, 228, 226, 112, 111, 10,
513 142, 73, 85, 43, 81, 152, 204, 13,
514 ];
515
516 assert!(verify_with_hasher::<Sha512>(&pubkey, &sig, &[b"hello world"]).is_ok());
517 assert!(verify_with_hasher::<Sha512>(&pubkey, &sig, &[b"not the right message"]).is_err());
518 }
519
520 #[test]
521 fn test_error_invalid_public_key() {
522 let pubkey = Address::from(EIGHT_TORSION[0]);
523 let sig: [u8; 64] = [
524 164, 121, 89, 242, 88, 29, 80, 177, 104, 20, 102, 176, 48, 133, 68, 8, 105, 33, 58, 86,
525 28, 108, 198, 140, 160, 219, 62, 184, 154, 181, 140, 33, 35, 102, 183, 203, 111, 33,
526 55, 170, 180, 138, 92, 196, 185, 201, 122, 167, 15, 112, 9, 228, 226, 112, 111, 10,
527 142, 73, 85, 43, 81, 152, 204, 13,
528 ];
529
530 assert_eq!(
531 verify_strict(&pubkey, &sig, &[b"hello world"]),
532 Err(ProgramError::InvalidArgument)
533 );
534 }
535
536 #[test]
537 fn test_error_invalid_signature() {
538 let pubkey = Address::from([
539 73, 73, 170, 112, 75, 235, 154, 81, 203, 8, 44, 245, 233, 18, 204, 136, 162, 9, 233,
540 49, 154, 201, 171, 175, 47, 6, 223, 101, 105, 80, 95, 166,
541 ]);
542 let sig: [u8; 64] = [
543 164, 121, 89, 242, 88, 29, 80, 177, 104, 20, 102, 176, 48, 133, 68, 8, 105, 33, 58, 86,
544 28, 108, 198, 140, 160, 219, 62, 184, 154, 181, 140, 33, 35, 102, 183, 203, 111, 33,
545 55, 170, 180, 138, 92, 196, 185, 201, 122, 167, 15, 112, 9, 228, 226, 112, 111, 10,
546 142, 73, 85, 43, 81, 152, 204, 13,
547 ];
548
549 assert_eq!(
550 verify(&pubkey, &sig, &[b"not the right message"]),
551 Err(ProgramError::InvalidArgument)
552 );
553 }
554
555 #[test]
556 fn test_hello_worldv() {
557 let pubkey = Address::from([
558 73, 73, 170, 112, 75, 235, 154, 81, 203, 8, 44, 245, 233, 18, 204, 136, 162, 9, 233,
559 49, 154, 201, 171, 175, 47, 6, 223, 101, 105, 80, 95, 166,
560 ]);
561 let sig: [u8; 64] = [
562 164, 121, 89, 242, 88, 29, 80, 177, 104, 20, 102, 176, 48, 133, 68, 8, 105, 33, 58, 86,
563 28, 108, 198, 140, 160, 219, 62, 184, 154, 181, 140, 33, 35, 102, 183, 203, 111, 33,
564 55, 170, 180, 138, 92, 196, 185, 201, 122, 167, 15, 112, 9, 228, 226, 112, 111, 10,
565 142, 73, 85, 43, 81, 152, 204, 13,
566 ];
567
568 let messagev: &[&[u8]] = &[b"hello", b" ", b"world"];
569 let bad_messagev: &[&[u8]] = &[b"hello", b" ", b"there"];
570
571 assert!(verify(&pubkey, &sig, messagev).is_ok());
572 assert!(verify(&pubkey, &sig, bad_messagev).is_err());
573 }
574
575 #[test]
576 fn test_vector_1() {
577 let pubkey = Address::from([
578 0xd7, 0x5a, 0x98, 0x01, 0x82, 0xb1, 0x0a, 0xb7, 0xd5, 0x4b, 0xfe, 0xd3, 0xc9, 0x64,
579 0x07, 0x3a, 0x0e, 0xe1, 0x72, 0xf3, 0xda, 0xa6, 0x23, 0x25, 0xaf, 0x02, 0x1a, 0x68,
580 0xf7, 0x07, 0x51, 0x1a,
581 ]);
582
583 let sig: [u8; 64] = [
584 0xe5, 0x56, 0x43, 0x00, 0xc3, 0x60, 0xac, 0x72, 0x90, 0x86, 0xe2, 0xcc, 0x80, 0x6e,
585 0x82, 0x8a, 0x84, 0x87, 0x7f, 0x1e, 0xb8, 0xe5, 0xd9, 0x74, 0xd8, 0x73, 0xe0, 0x65,
586 0x22, 0x49, 0x01, 0x55, 0x5f, 0xb8, 0x82, 0x15, 0x90, 0xa3, 0x3b, 0xac, 0xc6, 0x1e,
587 0x39, 0x70, 0x1c, 0xf9, 0xb4, 0x6b, 0xd2, 0x5b, 0xf5, 0xf0, 0x59, 0x5b, 0xbe, 0x24,
588 0x65, 0x51, 0x41, 0x43, 0x8e, 0x7a, 0x10, 0x0b,
589 ];
590
591 assert!(verify(&pubkey, &sig, &[b""]).is_ok());
592 assert!(verify(&pubkey, &sig, &[b"not the right message"]).is_err());
593 }
594
595 #[test]
596 fn test_vector_2() {
597 let pubkey = Address::from([
598 0x3d, 0x40, 0x17, 0xc3, 0xe8, 0x43, 0x89, 0x5a, 0x92, 0xb7, 0x0a, 0xa7, 0x4d, 0x1b,
599 0x7e, 0xbc, 0x9c, 0x98, 0x2c, 0xcf, 0x2e, 0xc4, 0x96, 0x8c, 0xc0, 0xcd, 0x55, 0xf1,
600 0x2a, 0xf4, 0x66, 0x0c,
601 ]);
602
603 let sig: [u8; 64] = [
604 0x92, 0xa0, 0x09, 0xa9, 0xf0, 0xd4, 0xca, 0xb8, 0x72, 0x0e, 0x82, 0x0b, 0x5f, 0x64,
605 0x25, 0x40, 0xa2, 0xb2, 0x7b, 0x54, 0x16, 0x50, 0x3f, 0x8f, 0xb3, 0x76, 0x22, 0x23,
606 0xeb, 0xdb, 0x69, 0xda, 0x08, 0x5a, 0xc1, 0xe4, 0x3e, 0x15, 0x99, 0x6e, 0x45, 0x8f,
607 0x36, 0x13, 0xd0, 0xf1, 0x1d, 0x8c, 0x38, 0x7b, 0x2e, 0xae, 0xb4, 0x30, 0x2a, 0xee,
608 0xb0, 0x0d, 0x29, 0x16, 0x12, 0xbb, 0x0c, 0x00,
609 ];
610
611 assert!(verify(&pubkey, &sig, &[b"r"]).is_ok());
612 assert!(verify(&pubkey, &sig, &[b"not the right message"]).is_err());
613 }
614
615 #[test]
616 fn test_vector_3() {
617 let pubkey = Address::from([
618 0xfc, 0x51, 0xcd, 0x8e, 0x62, 0x18, 0xa1, 0xa3, 0x8d, 0xa4, 0x7e, 0xd0, 0x02, 0x30,
619 0xf0, 0x58, 0x08, 0x16, 0xed, 0x13, 0xba, 0x33, 0x03, 0xac, 0x5d, 0xeb, 0x91, 0x15,
620 0x48, 0x90, 0x80, 0x25,
621 ]);
622
623 let sig: [u8; 64] = [
624 0x62, 0x91, 0xd6, 0x57, 0xde, 0xec, 0x24, 0x02, 0x48, 0x27, 0xe6, 0x9c, 0x3a, 0xbe,
625 0x01, 0xa3, 0x0c, 0xe5, 0x48, 0xa2, 0x84, 0x74, 0x3a, 0x44, 0x5e, 0x36, 0x80, 0xd7,
626 0xdb, 0x5a, 0xc3, 0xac, 0x18, 0xff, 0x9b, 0x53, 0x8d, 0x16, 0xf2, 0x90, 0xae, 0x67,
627 0xf7, 0x60, 0x98, 0x4d, 0xc6, 0x59, 0x4a, 0x7c, 0x15, 0xe9, 0x71, 0x6e, 0xd2, 0x8d,
628 0xc0, 0x27, 0xbe, 0xce, 0xea, 0x1e, 0xc4, 0x0a,
629 ];
630
631 let message: &[u8] = &[0xaf, 0x82];
632
633 assert!(verify(&pubkey, &sig, &[message]).is_ok());
634 assert!(verify(&pubkey, &sig, &[b"not the right message"]).is_err());
635 }
636
637 #[test]
638 fn test_prehashed() {
639 let pubkey = Address::from([
640 0xfc, 0x51, 0xcd, 0x8e, 0x62, 0x18, 0xa1, 0xa3, 0x8d, 0xa4, 0x7e, 0xd0, 0x02, 0x30,
641 0xf0, 0x58, 0x08, 0x16, 0xed, 0x13, 0xba, 0x33, 0x03, 0xac, 0x5d, 0xeb, 0x91, 0x15,
642 0x48, 0x90, 0x80, 0x25,
643 ]);
644
645 let sig: [u8; 64] = [
646 0x62, 0x91, 0xd6, 0x57, 0xde, 0xec, 0x24, 0x02, 0x48, 0x27, 0xe6, 0x9c, 0x3a, 0xbe,
647 0x01, 0xa3, 0x0c, 0xe5, 0x48, 0xa2, 0x84, 0x74, 0x3a, 0x44, 0x5e, 0x36, 0x80, 0xd7,
648 0xdb, 0x5a, 0xc3, 0xac, 0x18, 0xff, 0x9b, 0x53, 0x8d, 0x16, 0xf2, 0x90, 0xae, 0x67,
649 0xf7, 0x60, 0x98, 0x4d, 0xc6, 0x59, 0x4a, 0x7c, 0x15, 0xe9, 0x71, 0x6e, 0xd2, 0x8d,
650 0xc0, 0x27, 0xbe, 0xce, 0xea, 0x1e, 0xc4, 0x0a,
651 ];
652
653 let message = &[0xaf, 0x82];
654 let challenge = Sha512::hashv(&[sig[..32].as_ref(), pubkey.as_ref(), message.as_ref()]);
655 let wrong_challenge = [0u8; 64];
656
657 assert!(verify_prehashed(&pubkey, &sig, &challenge).is_ok());
658 assert!(verify_prehashed(&pubkey, &sig, &wrong_challenge).is_err());
659 }
660
661 #[test]
662 fn test_challenge_hashv() {
663 let sig_r: [u8; 32] = [
664 164, 121, 89, 242, 88, 29, 80, 177, 104, 20, 102, 176, 48, 133, 68, 8, 105, 33, 58, 86,
665 28, 108, 198, 140, 160, 219, 62, 184, 154, 181, 140, 33,
666 ];
667 let pubkey = Address::from([
668 73, 73, 170, 112, 75, 235, 154, 81, 203, 8, 44, 245, 233, 18, 204, 136, 162, 9, 233,
669 49, 154, 201, 171, 175, 47, 6, 223, 101, 105, 80, 95, 166,
670 ]);
671 let messagev: &[&[u8]] = &[b"hello", b" ", b"world"];
672
673 let expected = Sha512::hashv(&[sig_r.as_ref(), pubkey.as_ref(), b"hello", b" ", b"world"]);
674
675 assert_eq!(challenge::<Sha512>(&sig_r, &pubkey, messagev), expected);
676 }
677}