1use std::sync::Arc;
77
78use ed25519_dalek::{Signature, Signer, SigningKey, VerifyingKey};
79use hmac::{Hmac, Mac};
80use sha1::Sha1;
81use sha2::{Digest, Sha256, Sha512};
82use tatara_lisp_eval::ffi::Arity;
83use tatara_lisp_eval::{EvalError, Interpreter, Value};
84
85pub const KEY_BYTES: usize = 32;
87pub const SIGNATURE_BYTES: usize = 64;
89
90#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
95pub enum CryptoError {
96 #[error("the {what} must be a hex string; got {got}")]
98 NotHexText {
99 what: &'static str,
100 got: &'static str,
101 },
102 #[error("the {what} is not hex: character {position} is `{found}`")]
104 NotHex {
105 what: &'static str,
106 position: usize,
107 found: char,
108 },
109 #[error("the {what} has an odd number of hex digits ({digits})")]
111 OddHex { what: &'static str, digits: usize },
112 #[error("the {what} is {got} bytes; an Ed25519 {what} is {expected}")]
114 WrongLength {
115 what: &'static str,
116 expected: usize,
117 got: usize,
118 },
119 #[error("a message is a string or a list of bytes; got {got}")]
121 NotAMessage { got: &'static str },
122 #[error("element {index} of the byte list is not an integer; got {got}")]
124 NotAByte { index: usize, got: &'static str },
125 #[error("element {index} of the byte list is {value}, outside 0..=255")]
127 ByteOutOfRange { index: usize, value: i64 },
128 #[error("the public key is not a point on the Ed25519 curve")]
130 NotACurvePoint,
131 #[error("the public key has small order and cannot vouch for any message")]
133 WeakPublicKey,
134 #[error("the algorithm must be a string or keyword; got {got}")]
135 NotAnAlgorithm { got: &'static str },
136 #[error("unknown algorithm `{name}`; the built ones are {known}")]
137 UnknownAlgorithm { name: String, known: &'static str },
138 #[error("{name} has no keyed form here; a keyed digest is one of {known}")]
139 NoMac {
140 name: &'static str,
141 known: &'static str,
142 },
143 #[error("the key is not usable as a keyed-digest key")]
144 BadMacKey,
145}
146
147#[derive(Debug, Clone, Copy, PartialEq, Eq)]
148pub enum Algorithm {
149 Sha1,
150 Sha256,
151 Sha512,
152 Blake3,
153}
154
155impl Algorithm {
156 pub const DIGESTS: &'static str = "sha1, sha256, sha512, blake3";
157 pub const MACS: &'static str = "sha1, sha256, sha512";
158
159 pub fn parse(name: &str) -> Result<Self, CryptoError> {
160 match name {
161 "sha1" => Ok(Algorithm::Sha1),
162 "sha256" => Ok(Algorithm::Sha256),
163 "sha512" => Ok(Algorithm::Sha512),
164 "blake3" => Ok(Algorithm::Blake3),
165 other => Err(CryptoError::UnknownAlgorithm {
166 name: other.to_string(),
167 known: Self::DIGESTS,
168 }),
169 }
170 }
171
172 pub fn from_value(v: &Value) -> Result<Self, CryptoError> {
173 match v {
174 Value::Str(s) => Self::parse(s),
175 Value::Keyword(k) => Self::parse(k),
176 other => Err(CryptoError::NotAnAlgorithm {
177 got: other.type_name(),
178 }),
179 }
180 }
181
182 pub fn name(self) -> &'static str {
183 match self {
184 Algorithm::Sha1 => "sha1",
185 Algorithm::Sha256 => "sha256",
186 Algorithm::Sha512 => "sha512",
187 Algorithm::Blake3 => "blake3",
188 }
189 }
190}
191
192pub fn message_bytes(v: &Value) -> Result<Vec<u8>, CryptoError> {
199 match v {
200 Value::Str(s) => Ok(s.as_bytes().to_vec()),
201 Value::List(items) => items
202 .iter()
203 .enumerate()
204 .map(|(index, item)| match item {
205 Value::Int(n) => {
206 u8::try_from(*n).map_err(|_| CryptoError::ByteOutOfRange { index, value: *n })
207 }
208 other => Err(CryptoError::NotAByte {
209 index,
210 got: other.type_name(),
211 }),
212 })
213 .collect(),
214 other => Err(CryptoError::NotAMessage {
215 got: other.type_name(),
216 }),
217 }
218}
219
220pub fn hex_vec(v: &Value, what: &'static str) -> Result<Vec<u8>, CryptoError> {
221 match v {
222 Value::Str(s) => decode_hex(s, what),
223 other => Err(CryptoError::NotHexText {
224 what,
225 got: other.type_name(),
226 }),
227 }
228}
229
230pub fn hex_array<const N: usize>(v: &Value, what: &'static str) -> Result<[u8; N], CryptoError> {
232 let bytes = hex_vec(v, what)?;
233 <[u8; N]>::try_from(bytes.as_slice()).map_err(|_| CryptoError::WrongLength {
234 what,
235 expected: N,
236 got: bytes.len(),
237 })
238}
239
240fn decode_hex(text: &str, what: &'static str) -> Result<Vec<u8>, CryptoError> {
242 let digits: Vec<u8> = text
243 .chars()
244 .enumerate()
245 .map(|(position, found)| {
246 found
247 .to_digit(16)
248 .map(|d| d as u8)
250 .ok_or(CryptoError::NotHex {
251 what,
252 position,
253 found,
254 })
255 })
256 .collect::<Result<_, _>>()?;
257 if !digits.len().is_multiple_of(2) {
258 return Err(CryptoError::OddHex {
259 what,
260 digits: digits.len(),
261 });
262 }
263 Ok(digits
264 .as_chunks::<2>()
265 .0
266 .iter()
267 .map(|[hi, lo]| (hi << 4) | lo)
268 .collect())
269}
270
271pub fn encode_hex(bytes: &[u8]) -> String {
273 const DIGITS: &[u8; 16] = b"0123456789abcdef";
274 let mut out = String::with_capacity(bytes.len() * 2);
275 for b in bytes {
276 out.push(char::from(DIGITS[usize::from(b >> 4)]));
277 out.push(char::from(DIGITS[usize::from(b & 0x0f)]));
278 }
279 out
280}
281
282pub fn blake3_hex(data: &[u8]) -> String {
284 blake3::hash(data).to_hex().to_string()
285}
286
287pub fn ed25519_public(secret: &[u8; KEY_BYTES]) -> [u8; KEY_BYTES] {
289 SigningKey::from_bytes(secret).verifying_key().to_bytes()
290}
291
292pub fn ed25519_sign(secret: &[u8; KEY_BYTES], message: &[u8]) -> [u8; SIGNATURE_BYTES] {
295 SigningKey::from_bytes(secret).sign(message).to_bytes()
296}
297
298pub fn ed25519_verify(
301 public: &[u8; KEY_BYTES],
302 message: &[u8],
303 signature: &[u8; SIGNATURE_BYTES],
304) -> Result<bool, CryptoError> {
305 let key = VerifyingKey::from_bytes(public).map_err(|_| CryptoError::NotACurvePoint)?;
306 if key.is_weak() {
307 return Err(CryptoError::WeakPublicKey);
308 }
309 let signature = Signature::from_bytes(signature);
310 Ok(key.verify_strict(message, &signature).is_ok())
311}
312
313pub fn digest_hex(algorithm: Algorithm, data: &[u8]) -> String {
314 match algorithm {
315 Algorithm::Sha1 => encode_hex(&Sha1::digest(data)),
316 Algorithm::Sha256 => encode_hex(&Sha256::digest(data)),
317 Algorithm::Sha512 => encode_hex(&Sha512::digest(data)),
318 Algorithm::Blake3 => blake3_hex(data),
319 }
320}
321
322fn mac_hex<M: Mac>(
323 key: &[u8],
324 data: &[u8],
325 new: fn(&[u8]) -> Result<M, hmac::digest::InvalidLength>,
326) -> Result<String, CryptoError> {
327 let mut mac = new(key).map_err(|_| CryptoError::BadMacKey)?;
328 mac.update(data);
329 Ok(encode_hex(&mac.finalize().into_bytes()))
330}
331
332pub fn hmac_hex(algorithm: Algorithm, key: &[u8], data: &[u8]) -> Result<String, CryptoError> {
333 match algorithm {
334 Algorithm::Sha1 => mac_hex(key, data, Hmac::<Sha1>::new_from_slice),
335 Algorithm::Sha256 => mac_hex(key, data, Hmac::<Sha256>::new_from_slice),
336 Algorithm::Sha512 => mac_hex(key, data, Hmac::<Sha512>::new_from_slice),
337 Algorithm::Blake3 => Err(CryptoError::NoMac {
338 name: algorithm.name(),
339 known: Algorithm::MACS,
340 }),
341 }
342}
343
344pub fn scan_differences(a: &[u8], b: &[u8]) -> (u8, usize) {
345 let width = a.len().max(b.len());
346 let mut acc = 0u8;
347 let mut visited = 0usize;
348 for i in 0..width {
349 let x = a.get(i).copied().unwrap_or(0);
350 let y = b.get(i).copied().unwrap_or(0);
351 acc |= x ^ y;
352 visited += 1;
353 }
354 (acc, visited)
355}
356
357pub fn bytes_equal(a: &[u8], b: &[u8]) -> bool {
358 let (acc, _) = scan_differences(a, b);
359 let lengths = (a.len() ^ b.len()) as u64;
360 std::hint::black_box(acc) == 0 && std::hint::black_box(lengths) == 0
361}
362
363fn refuse(name: &'static str, span: tatara_lisp::Span) -> impl Fn(CryptoError) -> EvalError {
365 move |e| EvalError::native_fn(name, e.to_string(), span)
366}
367
368fn hex_value(bytes: &[u8]) -> Value {
369 Value::Str(Arc::from(encode_hex(bytes)))
370}
371
372pub fn install_crypto_stdlib<H: 'static>(interp: &mut Interpreter<H>) {
374 interp.register_fn(
375 "blake3_hex",
376 Arity::Exact(1),
377 |a: &[Value], _h: &mut H, span| {
378 let data = message_bytes(&a[0]).map_err(refuse("blake3_hex", span))?;
379 Ok(Value::Str(Arc::from(blake3_hex(&data))))
380 },
381 );
382
383 interp.register_fn(
384 "digest_hex",
385 Arity::Exact(2),
386 |a: &[Value], _h: &mut H, span| {
387 let algorithm = Algorithm::from_value(&a[0]).map_err(refuse("digest_hex", span))?;
388 let data = message_bytes(&a[1]).map_err(refuse("digest_hex", span))?;
389 Ok(Value::Str(Arc::from(digest_hex(algorithm, &data))))
390 },
391 );
392
393 interp.register_fn(
394 "hmac_hex",
395 Arity::Exact(3),
396 |a: &[Value], _h: &mut H, span| {
397 let algorithm = Algorithm::from_value(&a[0]).map_err(refuse("hmac_hex", span))?;
398 let key = message_bytes(&a[1]).map_err(refuse("hmac_hex", span))?;
399 let data = message_bytes(&a[2]).map_err(refuse("hmac_hex", span))?;
400 let tag = hmac_hex(algorithm, &key, &data).map_err(refuse("hmac_hex", span))?;
401 Ok(Value::Str(Arc::from(tag)))
402 },
403 );
404
405 interp.register_fn(
406 "bytes_equal?",
407 Arity::Exact(2),
408 |a: &[Value], _h: &mut H, span| {
409 let left = message_bytes(&a[0]).map_err(refuse("bytes_equal?", span))?;
410 let right = message_bytes(&a[1]).map_err(refuse("bytes_equal?", span))?;
411 Ok(Value::Bool(bytes_equal(&left, &right)))
412 },
413 );
414
415 interp.register_fn(
416 "ed25519_keypair",
417 Arity::Exact(1),
418 |a: &[Value], _h: &mut H, span| {
419 let seed: [u8; KEY_BYTES] =
420 hex_array(&a[0], "seed").map_err(refuse("ed25519_keypair", span))?;
421 Ok(Value::list([
422 hex_value(&seed),
423 hex_value(&ed25519_public(&seed)),
424 ]))
425 },
426 );
427
428 interp.register_fn(
429 "ed25519_sign",
430 Arity::Exact(2),
431 |a: &[Value], _h: &mut H, span| {
432 let secret: [u8; KEY_BYTES] =
433 hex_array(&a[0], "secret key").map_err(refuse("ed25519_sign", span))?;
434 let message = message_bytes(&a[1]).map_err(refuse("ed25519_sign", span))?;
435 Ok(hex_value(&ed25519_sign(&secret, &message)))
436 },
437 );
438
439 interp.register_fn(
440 "ed25519_verify",
441 Arity::Exact(3),
442 |a: &[Value], _h: &mut H, span| {
443 let public: [u8; KEY_BYTES] =
444 hex_array(&a[0], "public key").map_err(refuse("ed25519_verify", span))?;
445 let message = message_bytes(&a[1]).map_err(refuse("ed25519_verify", span))?;
446 let signature: [u8; SIGNATURE_BYTES] =
447 hex_array(&a[2], "signature").map_err(refuse("ed25519_verify", span))?;
448 let verdict = ed25519_verify(&public, &message, &signature)
449 .map_err(refuse("ed25519_verify", span))?;
450 Ok(Value::Bool(verdict))
451 },
452 );
453}
454
455#[cfg(test)]
456mod tests {
457 use super::*;
485
486 fn eval(src: &str) -> Value {
487 crate::run(src)
488 .unwrap_or_else(|e| panic!("{src:?}: {e}"))
489 .value
490 }
491
492 fn s(src: &str) -> String {
493 match eval(src) {
494 Value::Str(v) => v.to_string(),
495 other => panic!("{src:?} produced {other:?}"),
496 }
497 }
498
499 fn b(src: &str) -> bool {
500 match eval(src) {
501 Value::Bool(v) => v,
502 other => panic!("{src:?} produced {other:?}"),
503 }
504 }
505
506 fn err(src: &str) -> String {
507 crate::run(src)
508 .map(|r| r.value)
509 .expect_err("must raise")
510 .to_string()
511 }
512
513 struct Vector {
515 secret: &'static str,
516 public: &'static str,
517 message: &'static str,
519 signature: &'static str,
520 }
521
522 const TEST_1: Vector = Vector {
523 secret: "9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60",
524 public: "d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a",
525 message: "\"\"",
526 signature: "e5564300c360ac729086e2cc806e828a84877f1eb8e5d974d873e06522490155\
527 5fb8821590a33bacc61e39701cf9b46bd25bf5f0595bbe24655141438e7a100b",
528 };
529
530 const TEST_2: Vector = Vector {
532 secret: "4ccd089b28ff96da9db6c346ec114e0f5b8a319f35aba624da8cf6ed4fb8a6fb",
533 public: "3d4017c3e843895a92b70aa74d1b7ebc9c982ccf2ec4968cc0cd55f12af4660c",
534 message: "\"r\"",
535 signature: "92a009a9f0d4cab8720e820b5f642540a2b27b5416503f8fb3762223ebdb69da\
536 085ac1e43e15996e458f3613d0f11d8c387b2eaeb4302aeeb00d291612bb0c00",
537 };
538
539 const TEST_3: Vector = Vector {
542 secret: "c5aa8df43f9f837bedb7442f31dcb7b166d38535076f094b85ce3a2e0b4458f7",
543 public: "fc51cd8e6218a1a38da47ed00230f0580816ed13ba3303ac5deb911548908025",
544 message: "[175, 130]",
545 signature: "6291d657deec24024827e69c3abe01a30ce548a284743a445e3680d7db5ac3ac\
546 18ff9b538d16f290ae67f760984dc6594a7c15e9716ed28dc027beceea1ec40a",
547 };
548
549 fn check_vector(v: &Vector) {
550 let pair = eval(&["ed25519_keypair(\"", v.secret, "\")"].concat());
551 let Value::List(pair) = pair else {
552 panic!("keypair must be a list, got {pair:?}")
553 };
554 assert!(matches!(&pair[0], Value::Str(x) if &**x == v.secret));
555 assert!(
556 matches!(&pair[1], Value::Str(x) if &**x == v.public),
557 "public key mismatch: {:?}",
558 pair[1]
559 );
560 let sig = s(&["ed25519_sign(\"", v.secret, "\", ", v.message, ")"].concat());
561 assert_eq!(sig, v.signature, "signature mismatch");
562 assert!(b(&[
563 "ed25519_verify(\"",
564 v.public,
565 "\", ",
566 v.message,
567 ", \"",
568 v.signature,
569 "\")"
570 ]
571 .concat()));
572 }
573
574 #[test]
575 fn rfc8032_test_1() {
576 check_vector(&TEST_1);
577 }
578
579 #[test]
580 fn rfc8032_test_2() {
581 check_vector(&TEST_2);
582 }
583
584 #[test]
585 fn rfc8032_test_3() {
586 check_vector(&TEST_3);
587 }
588
589 fn official_input(n: usize) -> Vec<u8> {
591 (0..n).map(|i| (i % 251) as u8).collect()
592 }
593
594 #[test]
595 fn blake3_the_empty_input() {
596 assert_eq!(
597 s("blake3_hex(\"\")"),
598 "af1349b9f5f9a1a6a0404dea36dcc9499bcb25c9adc112b7cc9a93cae41f3262"
599 );
600 assert_eq!(
602 s("blake3_hex([])"),
603 "af1349b9f5f9a1a6a0404dea36dcc9499bcb25c9adc112b7cc9a93cae41f3262"
604 );
605 }
606
607 #[test]
608 fn blake3_one_byte_through_blue() {
609 assert_eq!(
610 s("blake3_hex([0])"),
611 "2d3adedff11b61f14c886e35afa036736dcd87a74d27b5c1510225d0f592e213"
612 );
613 }
614
615 #[test]
618 fn blake3_across_the_chunk_boundary() {
619 for (n, want) in [
620 (
621 3,
622 "e1be4d7a8ab5560aa4199eea339849ba8e293d55ca0a81006726d184519e647f",
623 ),
624 (
625 1023,
626 "10108970eeda3eb932baac1428c7a2163b0e924c9a9e25b35bba72b28f70bd11",
627 ),
628 (
629 1024,
630 "42214739f095a406f3fc83deb889744ac00df831c10daa55189b5d121c855af7",
631 ),
632 (
633 1025,
634 "d00278ae47eb27b34faecf67b4fe263f82d5412916c1ffd97c8cb7fb814b8444",
635 ),
636 (
637 8193,
638 "bab6c09cb8ce8cf459261398d2e7aef35700bf488116ceb94a36d0f5f1b7bc3b",
639 ),
640 ] {
641 assert_eq!(blake3_hex(&official_input(n)), want, "input_len {n}");
642 }
643 }
644
645 #[test]
647 fn text_and_bytes_are_one_message() {
648 assert_eq!(s("blake3_hex(\"abc\")"), s("blake3_hex([97, 98, 99])"));
649 assert_eq!(s("blake3_hex(\"é\")"), s("blake3_hex([195, 169])"));
651 }
652
653 #[test]
654 fn a_signature_round_trips_through_blue() {
655 let sig = s(&["ed25519_sign(\"", TEST_2.secret, "\", \"a permit\")"].concat());
656 assert!(b(&[
657 "ed25519_verify(\"",
658 TEST_2.public,
659 "\", \"a permit\", \"",
660 &sig,
661 "\")"
662 ]
663 .concat()));
664 }
665
666 #[test]
669 fn a_tampered_message_does_not_verify() {
670 assert!(!b(&[
672 "ed25519_verify(\"",
673 TEST_2.public,
674 "\", \"s\", \"",
675 TEST_2.signature,
676 "\")"
677 ]
678 .concat()));
679 }
680
681 #[test]
682 fn a_wrong_key_does_not_verify() {
683 assert!(!b(&[
685 "ed25519_verify(\"",
686 TEST_1.public,
687 "\", \"r\", \"",
688 TEST_2.signature,
689 "\")"
690 ]
691 .concat()));
692 }
693
694 #[test]
695 fn a_tampered_signature_does_not_verify() {
696 let mut forged = TEST_1.signature.to_string();
698 forged.pop();
699 forged.push('a');
700 assert_ne!(forged, TEST_1.signature);
701 assert!(!b(&[
702 "ed25519_verify(\"",
703 TEST_1.public,
704 "\", \"\", \"",
705 &forged,
706 "\")"
707 ]
708 .concat()));
709 }
710
711 #[test]
714 fn a_short_key_is_a_named_error_not_false() {
715 let e = err("ed25519_verify(\"abcd\", \"m\", \"00\")");
716 assert!(e.contains("ed25519_verify"), "{e}");
717 assert!(e.contains("public key is 2 bytes"), "{e}");
718 }
719
720 #[test]
721 fn non_hex_is_a_named_error() {
722 let bad = ["\"zz", &TEST_1.secret[2..], "\""].concat();
723 let e = err(&["ed25519_sign(", &bad, ", \"m\")"].concat());
724 assert!(e.contains("not hex: character 0 is `z`"), "{e}");
725 }
726
727 #[test]
728 fn odd_hex_is_a_named_error() {
729 let e = err("ed25519_keypair(\"abc\")");
730 assert!(e.contains("odd number of hex digits (3)"), "{e}");
731 }
732
733 #[test]
734 fn a_key_that_is_not_a_string_is_a_named_error() {
735 let e = err("ed25519_keypair(42)");
736 assert!(e.contains("seed must be a hex string; got int"), "{e}");
737 }
738
739 #[test]
740 fn a_non_message_is_a_named_error() {
741 let e = err("blake3_hex(nil)");
742 assert!(
743 e.contains("a message is a string or a list of bytes"),
744 "{e}"
745 );
746 let e = err("blake3_hex(42)");
747 assert!(e.contains("; got int"), "{e}");
748 }
749
750 #[test]
751 fn a_byte_out_of_range_is_a_named_error() {
752 let e = err("blake3_hex([1, 256])");
753 assert!(e.contains("element 1 of the byte list is 256"), "{e}");
754 let e = err("blake3_hex([1, \"x\"])");
755 assert!(
756 e.contains("element 1 of the byte list is not an integer; got string"),
757 "{e}"
758 );
759 }
760
761 #[test]
763 fn hex_input_is_case_insensitive_and_output_is_lowercase() {
764 let upper = TEST_1.secret.to_uppercase();
765 let pair = eval(&["ed25519_keypair(\"", &upper, "\")"].concat());
766 let Value::List(pair) = pair else { panic!() };
767 assert!(matches!(&pair[0], Value::Str(x) if &**x == TEST_1.secret));
768 assert!(matches!(&pair[1], Value::Str(x) if &**x == TEST_1.public));
769 }
770
771 #[test]
775 fn a_public_key_off_the_curve_is_a_named_error() {
776 let mut off = [0u8; KEY_BYTES];
777 off[0] = 2;
778 assert!(
779 VerifyingKey::from_bytes(&off).is_err(),
780 "control: y=2 is off-curve"
781 );
782 let e = err(&[
783 "ed25519_verify(\"",
784 &encode_hex(&off),
785 "\", \"m\", \"",
786 TEST_1.signature,
787 "\")",
788 ]
789 .concat());
790 assert!(e.contains("not a point on the Ed25519 curve"), "{e}");
791 }
792
793 #[test]
796 fn a_small_order_public_key_is_a_named_error() {
797 let mut identity = [0u8; KEY_BYTES];
798 identity[0] = 1;
799 let key = VerifyingKey::from_bytes(&identity).expect("the identity decompresses");
800 assert!(key.is_weak(), "control: the identity is small-order");
801 let e = err(&[
802 "ed25519_verify(\"",
803 &encode_hex(&identity),
804 "\", \"m\", \"",
805 TEST_1.signature,
806 "\")",
807 ]
808 .concat());
809 assert!(e.contains("small order"), "{e}");
810 }
811
812 #[test]
813 fn hex_round_trips() {
814 let all: Vec<u8> = (0..=255).collect();
815 let text = encode_hex(&all);
816 assert_eq!(decode_hex(&text, "t").expect("round trip"), all);
817 assert_eq!(decode_hex("", "t").expect("empty"), Vec::<u8>::new());
818 }
819
820 const SHA: [(&str, &str, &str, &str); 3] = [
821 ("sha1", "da39a3ee5e6b4b0d3255bfef95601890afd80709", "a9993e364706816aba3e25717850c26c9cd0d89d", "6244e66451a1c8695db9731ce2c4fd5de25ccf87"),
822 ("sha256", "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad", "4cbc96099a6467ce002461f10549b4898265ebe6188b45efacc44293516e62c4"),
823 ("sha512", "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e", "ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f", "aee650458ddcbcd4557531b27f1cded4c577f576079b086f57f3c6b9e037a7c0b3cdf7ff58afa09fe279076d1107d5d554f4d34fc6f03d8fd12f93d892802ac3"),
824 ];
825
826 fn bytes_literal(byte: u8, count: usize) -> String {
827 format!("[{}]", vec![byte.to_string(); count].join(", "))
828 }
829
830 #[test]
831 fn digests_match_fips_180_for_the_empty_input_and_abc() {
832 for (name, empty, abc, _) in SHA {
833 assert_eq!(
834 s(&format!("digest_hex(\"{name}\", \"\")")),
835 empty,
836 "{name} empty"
837 );
838 assert_eq!(
839 s(&format!("digest_hex(\"{name}\", \"abc\")")),
840 abc,
841 "{name} abc"
842 );
843 }
844 }
845
846 #[test]
847 fn digests_take_text_and_bytes_and_a_keyword_name() {
848 for (name, _, abc, _) in SHA {
849 assert_eq!(
850 s(&format!("digest_hex(\"{name}\", [97, 98, 99])")),
851 abc,
852 "{name} bytes"
853 );
854 assert_eq!(
855 s(&format!("digest_hex(:{name}, \"abc\")")),
856 abc,
857 "{name} keyword"
858 );
859 }
860 assert_eq!(s("digest_hex(\"blake3\", \"\")"), s("blake3_hex(\"\")"));
861 }
862
863 #[test]
864 fn digests_differ_by_algorithm_and_by_input() {
865 let all: std::collections::BTreeSet<String> = ["sha1", "sha256", "sha512", "blake3"]
866 .iter()
867 .map(|n| s(&format!("digest_hex(\"{n}\", \"abc\")")))
868 .collect();
869 assert_eq!(all.len(), 4);
870 assert_ne!(
871 s("digest_hex(\"sha256\", \"abc\")"),
872 s("digest_hex(\"sha256\", \"abd\")")
873 );
874 }
875
876 #[test]
877 fn digest_hex_refuses_what_it_cannot_place() {
878 assert!(err("digest_hex(\"md5\", \"x\")").contains("unknown algorithm `md5`"));
879 assert!(err("digest_hex(\"SHA256\", \"x\")").contains("unknown algorithm"));
880 assert!(err("digest_hex(256, \"x\")").contains("string or keyword"));
881 assert!(err("digest_hex(nil, \"x\")").contains("string or keyword"));
882 assert!(err("digest_hex(\"sha256\", nil)").contains("string or a list of bytes"));
883 assert!(err("digest_hex(\"sha256\", [256])").contains("outside 0..=255"));
884 assert!(err("digest_hex(\"sha256\")").contains("digest_hex"));
885 }
886
887 #[test]
888 fn hmac_matches_rfc_4231_and_rfc_2202() {
889 let key = bytes_literal(0x0b, 20);
890 let long_key = bytes_literal(0xaa, 131);
891 let cases: [(&str, &str, &str, &str); 3] = [
892 ("sha1", "b617318655057264e28bc0b6fb378c8ef146be00", "effcdf6ae5eb2fa2d27416d5f184df9c259a7c79", "90d0dace1c1bdc957339307803160335bde6df2b"),
893 ("sha256", "b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7", "5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843", "60e431591ee0b67f0d8a26aacbf5b77f8e0bc6213728c5140546040f0ee37f54"),
894 ("sha512", "87aa7cdea5ef619d4ff0b4241a1d6cb02379f4e2ce4ec2787ad0b30545e17cdedaa833b7d6b8a702038b274eaea3f4e4be9d914eeb61f1702e696c203a126854", "164b7a7bfcf819e2e395fbe73b56e0a387bd64222e831fd610270cd7ea2505549758bf75c05a994a6d034f65f8f0e6fdcaeab1a34d4a6b4b636e070a38bce737", "80b24263c7c1a3ebb71493c1dd7be8b49b46d1f41b4aeec1121b013783f8f3526b56d037e05f2598bd0fd2215d6a1e5295e64f73f63f0aec8b915a985d786598"),
895 ];
896 for (name, one, two, long) in cases {
897 assert_eq!(
898 s(&format!("hmac_hex(\"{name}\", {key}, \"Hi There\")")),
899 one,
900 "{name} case 1"
901 );
902 assert_eq!(
903 s(&format!(
904 "hmac_hex(:{name}, \"Jefe\", \"what do ya want for nothing?\")"
905 )),
906 two,
907 "{name} case 2"
908 );
909 assert_eq!(
910 s(&format!("hmac_hex(\"{name}\", {long_key}, \"Test Using Larger Than Block-Size Key - Hash Key First\")")),
911 long,
912 "{name} long key"
913 );
914 }
915 }
916
917 #[test]
918 fn hmac_with_an_empty_key_is_defined_and_differs_from_the_plain_digest() {
919 for (name, _, _, empty_key) in SHA {
920 assert_eq!(
921 s(&format!("hmac_hex(\"{name}\", \"\", \"x\")")),
922 empty_key,
923 "{name}"
924 );
925 assert_ne!(
926 s(&format!("hmac_hex(\"{name}\", \"\", \"x\")")),
927 s(&format!("digest_hex(\"{name}\", \"x\")"))
928 );
929 }
930 assert_eq!(
931 s("hmac_hex(\"sha256\", [], \"x\")"),
932 s("hmac_hex(\"sha256\", \"\", \"x\")")
933 );
934 }
935
936 #[test]
937 fn hmac_depends_on_the_key_and_the_data() {
938 let base = s("hmac_hex(\"sha256\", \"k\", \"m\")");
939 assert_ne!(base, s("hmac_hex(\"sha256\", \"k2\", \"m\")"));
940 assert_ne!(base, s("hmac_hex(\"sha256\", \"k\", \"m2\")"));
941 }
942
943 #[test]
944 fn hmac_refuses_blake3_unknown_names_and_bad_arguments() {
945 assert!(err("hmac_hex(\"blake3\", \"k\", \"m\")").contains("no keyed form"));
946 assert!(err("hmac_hex(\"md5\", \"k\", \"m\")").contains("unknown algorithm"));
947 assert!(err("hmac_hex(1, \"k\", \"m\")").contains("string or keyword"));
948 assert!(err("hmac_hex(\"sha256\", nil, \"m\")").contains("string or a list of bytes"));
949 assert!(err("hmac_hex(\"sha256\", \"k\", nil)").contains("string or a list of bytes"));
950 assert!(err("hmac_hex(\"sha256\", \"k\")").contains("hmac_hex"));
951 }
952
953 #[test]
954 fn bytes_equal_answers_for_text_bytes_and_empties() {
955 assert!(b("bytes_equal?(\"abc\", \"abc\")"));
956 assert!(b("bytes_equal?(\"abc\", [97, 98, 99])"));
957 assert!(b("bytes_equal?(\"\", [])"));
958 assert!(!b("bytes_equal?(\"abc\", \"abd\")"));
959 assert!(!b("bytes_equal?(\"abc\", \"ab\")"));
960 assert!(!b("bytes_equal?(\"ab\", \"abc\")"));
961 assert!(!b("bytes_equal?(\"\", \"a\")"));
962 assert!(!b("bytes_equal?([1], [1, 0])"));
963 }
964
965 #[test]
966 fn bytes_equal_refuses_what_is_not_a_message() {
967 assert!(err("bytes_equal?(nil, \"a\")").contains("string or a list of bytes"));
968 assert!(err("bytes_equal?(\"a\", 1)").contains("string or a list of bytes"));
969 assert!(err("bytes_equal?([300], \"a\")").contains("outside 0..=255"));
970 assert!(err("bytes_equal?(\"a\")").contains("bytes_equal?"));
971 }
972
973 #[test]
974 fn the_scan_visits_every_position_whatever_the_first_difference() {
975 let reference = vec![7u8; 64];
976 for first_difference in 0..64 {
977 let mut other = reference.clone();
978 other[first_difference] ^= 0xff;
979 let (acc, visited) = scan_differences(&reference, &other);
980 assert_ne!(acc, 0, "difference at {first_difference} must register");
981 assert_eq!(
982 visited, 64,
983 "stopped early when the first difference was at {first_difference}"
984 );
985 }
986 let (acc, visited) = scan_differences(&reference, &reference);
987 assert_eq!((acc, visited), (0, 64));
988 }
989
990 #[test]
991 fn the_scan_covers_the_longer_side_when_lengths_differ() {
992 assert_eq!(scan_differences(b"", b"abcd").1, 4);
993 assert_eq!(scan_differences(b"abcd", b"").1, 4);
994 assert_eq!(scan_differences(b"x", b"yzzz").1, 4);
995 assert_eq!(scan_differences(b"", b"").1, 0);
996 }
997
998 #[test]
999 fn the_scan_accumulates_every_difference_not_just_the_last() {
1000 let (acc, _) = scan_differences(&[0b0001, 0b0000, 0b0000], &[0b0000, 0b0000, 0b0000]);
1001 assert_ne!(acc, 0, "a difference before equal tail bytes must survive");
1002 let (acc, _) = scan_differences(&[0b0001, 0b0010], &[0b0000, 0b0000]);
1003 assert_eq!(acc, 0b0011);
1004 }
1005}