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