hopr_crypto_sphinx/
prg.rs1use aes::cipher::{KeyIvInit, StreamCipher};
2use hopr_crypto_types::primitives::SecretKey;
3use zeroize::ZeroizeOnDrop;
4
5use crate::derivation::generate_key_iv;
6
7const AES_BLOCK_SIZE: usize = 16;
9const AES_KEY_SIZE: usize = 16;
10
11const PRG_KEY_LENGTH: usize = AES_KEY_SIZE;
12const PRG_COUNTER_LENGTH: usize = 4;
13const PRG_IV_LENGTH: usize = AES_BLOCK_SIZE - PRG_COUNTER_LENGTH;
14
15const HASH_KEY_PRG: &str = "HASH_KEY_PRG";
16
17type Aes128Ctr32BE = ctr::Ctr32BE<aes::Aes128>;
18
19#[derive(ZeroizeOnDrop)]
22pub struct PRGParameters {
23 key: [u8; PRG_KEY_LENGTH],
24 iv: [u8; PRG_IV_LENGTH],
25}
26
27impl Default for PRGParameters {
28 fn default() -> Self {
29 Self {
30 key: [0u8; PRG_KEY_LENGTH],
31 iv: [0u8; PRG_IV_LENGTH],
32 }
33 }
34}
35
36impl PRGParameters {
37 pub fn new(secret: &SecretKey) -> Self {
40 let mut ret = PRGParameters::default();
41 generate_key_iv(secret, HASH_KEY_PRG.as_bytes(), &mut ret.key, &mut ret.iv, true);
42 ret
43 }
44}
45
46#[allow(clippy::upper_case_acronyms)]
51#[derive(ZeroizeOnDrop)]
52pub struct PRG {
53 params: PRGParameters,
54}
55
56impl PRG {
57 pub fn from_parameters(params: PRGParameters) -> Self {
59 Self { params }
60 }
61}
62
63impl PRG {
64 pub fn digest(&self, from: usize, to: usize) -> Box<[u8]> {
67 assert!(from < to);
68
69 let first_block = from / AES_BLOCK_SIZE;
70 let start = from % AES_BLOCK_SIZE;
71
72 let last_block_end = to % AES_BLOCK_SIZE;
73 let last_block = to / AES_BLOCK_SIZE + if last_block_end != 0 { 1 } else { 0 };
74 let count_blocks = last_block - first_block;
75 let end = AES_BLOCK_SIZE * count_blocks
76 - if last_block_end > 0 {
77 AES_BLOCK_SIZE - last_block_end
78 } else {
79 0
80 };
81
82 let mut key_stream = vec![0u8; count_blocks * AES_BLOCK_SIZE];
84
85 let mut new_iv = [0u8; AES_BLOCK_SIZE];
88 let (prefix, counter) = new_iv.split_at_mut(PRG_IV_LENGTH);
89 prefix.copy_from_slice(&self.params.iv);
90 counter.copy_from_slice(&(first_block as u32).to_be_bytes());
91
92 let mut cipher = Aes128Ctr32BE::new(&self.params.key.into(), &new_iv.into());
94 cipher.apply_keystream(&mut key_stream);
95
96 let result = &key_stream.as_slice()[start..end];
98 result.into()
99 }
100}
101
102#[cfg(test)]
103mod tests {
104 use super::*;
105 use hex_literal::hex;
106
107 #[test]
108 fn test_prg_single_block() {
109 let out = PRG::from_parameters(PRGParameters {
110 key: [0u8; 16],
111 iv: [0u8; 12],
112 })
113 .digest(5, 10);
114 assert_eq!(5, out.len());
115 }
116
117 #[test]
118 fn test_prg_more_blocks() {
119 let out = PRG::from_parameters(PRGParameters {
120 key: [0u8; 16],
121 iv: [0u8; 12],
122 })
123 .digest(0, AES_BLOCK_SIZE * 2);
124
125 assert_eq!(32, out.len());
126 }
127
128 #[test]
129 fn test_prg_across_blocks() {
130 let out = PRG::from_parameters(PRGParameters {
131 key: [0u8; 16],
132 iv: [0u8; 12],
133 })
134 .digest(5, AES_KEY_SIZE * 2 + 10);
135
136 assert_eq!(AES_BLOCK_SIZE * 2 + 5, out.len());
137 }
138
139 #[test]
140 fn test_prg_parameters() {
141 let expected_key = hex!("c642ceb7af7a65308ab2dbff9f6c7132");
142 let expected_iv = hex!("a735d1806513af957dd25de7");
143
144 let params = PRGParameters::new(&SecretKey::default());
145
146 assert_eq!(expected_key, params.key);
147 assert_eq!(expected_iv, params.iv)
148 }
149}