hopr_crypto_types/
primitives.rs1use blake2::{Blake2s256, Blake2sMac256};
2use chacha20::cipher::KeyIvInit;
3use chacha20::cipher::{IvSizeUser, KeySizeUser, StreamCipher, StreamCipherSeek};
4use chacha20::ChaCha20;
5use digest::{FixedOutputReset, KeyInit, Output, OutputSizeUser, Update};
6use generic_array::GenericArray;
7use sha3::Keccak256;
8use typenum::Unsigned;
9use zeroize::ZeroizeOnDrop;
10
11use crate::utils::SecretValue;
12
13pub type SecretKey = SecretValue<typenum::U32>;
16
17pub trait DigestLike<T>
20where
21 T: Update + FixedOutputReset + OutputSizeUser,
22{
23 const SIZE: usize = T::OutputSize::USIZE;
25
26 fn internal_state(&mut self) -> &mut T;
28
29 fn update(&mut self, data: &[u8]) {
31 self.internal_state().update(data);
32 }
33
34 fn finalize_into(&mut self, out: &mut [u8]) {
37 assert_eq!(Self::SIZE, out.len(), "invalid output size");
38 let output = Output::<T>::from_mut_slice(out);
39 self.internal_state().finalize_into_reset(output);
40 }
41
42 fn finalize(&mut self) -> GenericArray<u8, T::OutputSize> {
45 let mut output = Output::<T>::default();
46 self.finalize_into(&mut output);
47 output
48 }
49}
50
51#[derive(Default, Clone)]
55pub struct SimpleDigest(Blake2s256);
56
57impl DigestLike<Blake2s256> for SimpleDigest {
58 fn internal_state(&mut self) -> &mut Blake2s256 {
59 &mut self.0
60 }
61}
62
63#[derive(Default, Clone)]
67pub struct EthDigest(Keccak256);
68
69impl DigestLike<Keccak256> for EthDigest {
70 fn internal_state(&mut self) -> &mut Keccak256 {
71 &mut self.0
72 }
73}
74
75pub struct SimpleMac(Blake2sMac256); impl SimpleMac {
81 pub fn new(key: &SecretKey) -> Self {
83 Self(Blake2sMac256::new(key.into()))
84 }
85}
86
87impl DigestLike<Blake2sMac256> for SimpleMac {
88 fn internal_state(&mut self) -> &mut Blake2sMac256 {
89 &mut self.0
90 }
91}
92
93#[derive(ZeroizeOnDrop)]
97pub struct SimpleStreamCipher(ChaCha20);
98
99impl SimpleStreamCipher {
100 pub const KEY_SIZE: usize = <ChaCha20 as KeySizeUser>::KeySize::USIZE;
102
103 pub const IV_SIZE: usize = <ChaCha20 as IvSizeUser>::IvSize::USIZE;
105
106 pub fn new(key: [u8; Self::KEY_SIZE], iv: [u8; Self::IV_SIZE]) -> Self {
109 Self(ChaCha20::new(&key.into(), &iv.into()))
110 }
111
112 pub fn set_block_counter(&mut self, counter: u32) {
114 self.0.seek(counter as u64 * 64u64)
115 }
116
117 pub fn apply(&mut self, data: &mut [u8]) {
119 self.0.apply_keystream(data);
120 }
121
122 pub fn apply_copy(&mut self, data: &[u8]) -> Box<[u8]> {
124 let mut ret = Vec::from(data);
125 self.0.apply_keystream(ret.as_mut_slice());
126 ret.into_boxed_slice()
127 }
128}
129
130#[cfg(test)]
131mod tests {
132 use super::*;
133 use hex_literal::hex;
134
135 #[test]
136 fn test_chacha20() {
137 let key = [0u8; 32];
138 let mut iv = [0u8; 12];
139 iv[11] = 2u8;
140
141 let mut cipher = SimpleStreamCipher::new(key, iv);
142
143 let mut data = [0u8; 64];
144 cipher.apply(&mut data);
145
146 let expected_ct = hex!("c2c64d378cd536374ae204b9ef933fcd1a8b2288b3dfa49672ab765b54ee27c78a970e0e955c14f3a88e741b97c286f75f8fc299e8148362fa198a39531bed6d");
147 assert_eq!(expected_ct, data);
148 }
149
150 #[test]
151 fn test_chacha20_iv_block_counter() {
152 let key = hex!("a9c6632c9f76e5e4dd03203196932350a47562f816cebb810c64287ff68586f3");
153 let iv = hex!("6be504b26471dea53d688c4b");
154
155 let mut cipher = SimpleStreamCipher::new(key, iv);
156
157 cipher.set_block_counter(0xa5999171u32.to_be());
158
159 let mut data = [0u8; 68];
160 cipher.apply(&mut data);
161
162 let expected_ct = hex!("abe088c198cb0a7b2591f1472fb1d0bd529a697a58a45d4ac5dc426ba6bf207deec4a5331149f93c6629d514ece8b0f49b4bc3eda74e07b78df5ac7d7f69fa75f611c926");
163 assert_eq!(expected_ct, data);
164 }
165
166 #[test]
167 fn test_mac() {
168 let data = [0u8; 60];
169
170 let mut mac = SimpleMac::new(&Default::default());
171 mac.update(&data);
172
173 let out = mac.finalize();
174 let expected_out = hex!("29a11c3c01d102143a020fb562410902ba39966ec81b706945b8ed6d83498bb7");
175 assert_eq!(expected_out, out.as_slice());
176 }
177}