Oregami
Repositories/oxedyne/fe2o3

oxedyne/fe2o3/fe2o3_ore/src/test_support.rs

4.7 KiB, 12 runs

created by r1870400018:18659, which is this file's identity for as long as the history lasts, whatever it is later renamed to

download · who wrote it · its history

1//! Stand-ins the tests bring, because the crate brings none of its own.
2//!
3//! A hash function and a signature scheme are the caller's to supply, which is
4//! what makes the crate a primitive; the price is that its own tests have to
5//! supply them too. Neither of these is cryptography and neither claims to be.
6//! What they establish is that this crate presents the right bytes to a scheme
7//! and does the right thing with what comes back, which is all it is
8//! responsible for; the strength of a real scheme is tested where it is
9//! implemented.
10//!
11//! [Written with AI entirely](https://need2know.ai/entirely-ai/code)\
12//! Anthropic Claude
13
14use oxedyne_fe2o3_core::alt::Gnomon;
15use oxedyne_fe2o3_core::prelude::*;
16use oxedyne_fe2o3_iop_crypto::{
17 InNamex,
18 NamexId,
19 keys::KeyManager,
20 sign::Signer,
21};
22use oxedyne_fe2o3_iop_hash::api::{
23 Hash,
24 HashForm,
25 Hasher,
26};
27
28
29/// A stand-in hash function: a 64-bit fold of the input, in eight bytes.
30///
31/// It is not a cryptographic hash. It is here so that the segment tests have a
32/// digest short enough to write down and stable enough to freeze, which is what
33/// a golden-bytes test needs and what the identity hasher, whose digest is the
34/// whole input again, cannot give.
35#[derive(Clone, Copy, Debug, Default)]
36pub struct Fold;
37
38impl InNamex for Fold {
39 fn name_id(&self) -> Outcome<NamexId> {
40 Ok(NamexId::default())
41 }
42}
43
44impl Hasher for Fold {
45 fn hash<const S: usize>(self, input: &[&[u8]], salt: [u8; S])
46 -> Hash<S>
47 {
48 let mut acc: u64 = 0xcbf2_9ce4_8422_2325;
49 for slice in input {
50 for b in *slice {
51 acc ^= *b as u64;
52 acc = acc.wrapping_mul(0x0000_0100_0000_01b3);
53 }
54 }
55 for b in salt.iter() {
56 acc ^= *b as u64;
57 acc = acc.wrapping_mul(0x0000_0100_0000_01b3);
58 }
59 Hash::new(HashForm::U64(acc), salt)
60 }
61
62 fn hash_length(&self) -> Gnomon<usize> {
63 Gnomon::Known(8)
64 }
65
66 fn is_identity(&self) -> bool {
67 false
68 }
69}
70
71
72/// A stand-in signature scheme, present only to exercise the marshalling of
73/// envelopes and the segments that carry them.
74///
75/// It is not cryptography and makes no claim to be: the "signature" is the
76/// secret key interleaved with a fold of the message. What these tests
77/// establish is that the envelope presents the right bytes to the scheme,
78/// carries the right public key, and refuses what the scheme rejects. The
79/// strength of a real scheme is that scheme's business, and is tested where
80/// it is implemented.
81#[derive(Clone, Debug, Default)]
82pub struct StubSigner {
83 pub pk: Vec<u8>,
84 pub sk: Vec<u8>,
85}
86
87impl StubSigner {
88 /// Constructs a key pair from a seed byte.
89 pub fn with_seed(seed: u8) -> Self {
90 Self {
91 pk: vec![seed; 40], // longer than a BU8 length would matter
92 sk: vec![seed.wrapping_add(1); 40],
93 }
94 }
95
96 /// The stand-in signature: a fold of the message under the secret key.
97 pub fn compute(sk: &[u8], msg: &[u8]) -> Vec<u8> {
98 let mut acc = vec![0u8; 32];
99 for (i, b) in msg.iter().enumerate() {
100 acc[i % 32] = acc[i % 32].wrapping_add(*b).rotate_left(1);
101 }
102 for (i, b) in sk.iter().enumerate() {
103 acc[i % 32] ^= *b;
104 }
105 acc
106 }
107
108 /// The public key a given secret key corresponds to, under the stand-in's
109 /// trivial relation.
110 pub fn public_of(sk: &[u8]) -> Vec<u8> {
111 sk.iter().map(|b| b.wrapping_sub(1)).collect()
112 }
113}
114
115impl InNamex for StubSigner {
116 fn name_id(&self) -> Outcome<NamexId> {
117 Ok(NamexId::default())
118 }
119}
120
121impl KeyManager for StubSigner {
122 fn clone_with_keys(&self, pk: Option<&[u8]>, sk: Option<&[u8]>)
123 -> Outcome<Self>
124 {
125 Ok(Self {
126 pk: match pk {
127 Some(b) => b.to_vec(),
128 None => Vec::new(),
129 },
130 sk: match sk {
131 Some(b) => b.to_vec(),
132 None => Vec::new(),
133 },
134 })
135 }
136
137 fn get_public_key(&self) -> Outcome<Option<&[u8]>> {
138 Ok(if self.pk.is_empty() { None } else { Some(&self.pk) })
139 }
140
141 fn get_secret_key(&self) -> Outcome<Option<&[u8]>> {
142 Ok(if self.sk.is_empty() { None } else { Some(&self.sk) })
143 }
144
145 fn set_public_key(mut self, pk: Option<&[u8]>) -> Outcome<Self> {
146 self.pk = match pk {
147 Some(b) => b.to_vec(),
148 None => Vec::new(),
149 };
150 Ok(self)
151 }
152
153 fn set_secret_key(mut self, sk: Option<&[u8]>) -> Outcome<Self> {
154 self.sk = match sk {
155 Some(b) => b.to_vec(),
156 None => Vec::new(),
157 };
158 Ok(self)
159 }
160}
161
162impl Signer for StubSigner {
163 fn sign(&self, msg: &[u8]) -> Outcome<Vec<u8>> {
164 if self.sk.is_empty() {
165 return Err(err!("No secret key set."; Missing, Key));
166 }
167 Ok(Self::compute(&self.sk, msg))
168 }
169
170 fn verify(&self, msg: &[u8], sig: &[u8]) -> Outcome<bool> {
171 if self.pk.is_empty() {
172 return Err(err!("No public key set."; Missing, Key));
173 }
174 // Recover the secret key the public key implies, and recompute.
175 let sk: Vec<u8> = self.pk.iter().map(|b| b.wrapping_add(1)).collect();
176 Ok(Self::compute(&sk, msg) == sig)
177 }
178}