1use crate::common;
10use crate::config::Config;
11use crate::error::Error;
12use crate::result::Result;
13
14#[derive(Debug, PartialEq)]
17pub struct Context<'a> {
18 pub config: Config<'a>,
20
21 pub lane_length: u32,
23
24 pub memory_blocks: u32,
26
27 pub pwd: &'a [u8],
29
30 pub salt: &'a [u8],
32
33 pub segment_length: u32,
35}
36
37impl<'a> Context<'a> {
38 pub fn new(config: Config<'a>, pwd: &'a [u8], salt: &'a [u8]) -> Result<Context<'a>> {
40 if config.lanes < common::MIN_LANES {
41 return Err(Error::LanesTooFew);
42 } else if config.lanes > common::MAX_LANES {
43 return Err(Error::LanesTooMany);
44 }
45
46 let lanes = config.lanes;
47 if config.mem_cost < common::MIN_MEMORY {
48 return Err(Error::MemoryTooLittle);
49 } else if config.mem_cost > common::MAX_MEMORY {
50 return Err(Error::MemoryTooMuch);
51 } else if config.mem_cost < 8 * lanes {
52 return Err(Error::MemoryTooLittle);
53 }
54
55 if config.time_cost < common::MIN_TIME {
56 return Err(Error::TimeTooSmall);
57 } else if config.time_cost > common::MAX_TIME {
58 return Err(Error::TimeTooLarge);
59 }
60
61 let pwd_len = pwd.len();
62 if pwd_len < common::MIN_PWD_LENGTH as usize {
63 return Err(Error::PwdTooShort);
64 } else if pwd_len > common::MAX_PWD_LENGTH as usize {
65 return Err(Error::PwdTooLong);
66 }
67
68 let salt_len = salt.len();
69 if salt_len < common::MIN_SALT_LENGTH as usize {
70 return Err(Error::SaltTooShort);
71 } else if salt_len > common::MAX_SALT_LENGTH as usize {
72 return Err(Error::SaltTooLong);
73 }
74
75 let secret_len = config.secret.len();
76 if secret_len < common::MIN_SECRET_LENGTH as usize {
77 return Err(Error::SecretTooShort);
78 } else if secret_len > common::MAX_SECRET_LENGTH as usize {
79 return Err(Error::SecretTooLong);
80 }
81
82 let ad_len = config.ad.len();
83 if ad_len < common::MIN_AD_LENGTH as usize {
84 return Err(Error::AdTooShort);
85 } else if ad_len > common::MAX_AD_LENGTH as usize {
86 return Err(Error::AdTooLong);
87 }
88
89 if config.hash_length < common::MIN_HASH_LENGTH {
90 return Err(Error::OutputTooShort);
91 } else if config.hash_length > common::MAX_HASH_LENGTH {
92 return Err(Error::OutputTooLong);
93 }
94
95 let mut memory_blocks = config.mem_cost;
96 if memory_blocks < 2 * common::SYNC_POINTS * lanes {
97 memory_blocks = 2 * common::SYNC_POINTS * lanes;
98 }
99
100 let segment_length = memory_blocks / (lanes * common::SYNC_POINTS);
101 let memory_blocks = segment_length * (lanes * common::SYNC_POINTS);
102 let lane_length = segment_length * common::SYNC_POINTS;
103
104 Ok(Context {
105 config,
106 lane_length,
107 memory_blocks,
108 pwd,
109 salt,
110 segment_length,
111 })
112 }
113}
114
115#[cfg(test)]
116mod tests {
117
118 use crate::config::Config;
119 use crate::context::Context;
120 use crate::error::Error;
121 use crate::variant::Variant;
122 use crate::version::Version;
123
124 #[test]
125 fn new_returns_correct_instance() {
126 let config = Config {
127 ad: b"additionaldata",
128 hash_length: 32,
129 lanes: 4,
130 mem_cost: 4096,
131 secret: b"secret",
132 time_cost: 3,
133 variant: Variant::Argon2i,
134 version: Version::Version13,
135 };
136 let pwd = b"password";
137 let salt = b"somesalt";
138 let result = Context::new(config.clone(), pwd, salt);
139 assert!(result.is_ok());
140
141 let context = result.unwrap();
142 assert_eq!(context.config, config);
143 assert_eq!(context.pwd, pwd);
144 assert_eq!(context.salt, salt);
145 assert_eq!(context.memory_blocks, 4096);
146 assert_eq!(context.segment_length, 256);
147 assert_eq!(context.lane_length, 1024);
148 }
149
150 #[test]
151 fn new_with_too_little_mem_cost_returns_correct_error() {
152 let config = Config {
153 mem_cost: 7,
154 ..Default::default()
155 };
156 assert_eq!(
157 Context::new(config, &[0u8; 8], &[0u8; 8]),
158 Err(Error::MemoryTooLittle)
159 );
160 }
161
162 #[test]
163 fn new_with_less_than_8_x_lanes_mem_cost_returns_correct_error() {
164 let config = Config {
165 lanes: 4,
166 mem_cost: 31,
167 ..Default::default()
168 };
169 assert_eq!(
170 Context::new(config, &[0u8; 8], &[0u8; 8]),
171 Err(Error::MemoryTooLittle)
172 );
173 }
174
175 #[test]
176 fn new_with_too_small_time_cost_returns_correct_error() {
177 let config = Config {
178 time_cost: 0,
179 ..Default::default()
180 };
181 assert_eq!(
182 Context::new(config, &[0u8; 8], &[0u8; 8]),
183 Err(Error::TimeTooSmall)
184 );
185 }
186
187 #[test]
188 fn new_with_too_few_lanes_returns_correct_error() {
189 let config = Config {
190 lanes: 0,
191 ..Default::default()
192 };
193 assert_eq!(
194 Context::new(config, &[0u8; 8], &[0u8; 8]),
195 Err(Error::LanesTooFew)
196 );
197 }
198
199 #[test]
200 fn new_with_too_many_lanes_returns_correct_error() {
201 let config = Config {
202 lanes: 1 << 24,
203 ..Default::default()
204 };
205 assert_eq!(
206 Context::new(config, &[0u8; 8], &[0u8; 8]),
207 Err(Error::LanesTooMany)
208 );
209 }
210
211 #[test]
212 fn new_with_too_short_salt_returns_correct_error() {
213 let config = Default::default();
214 let salt = [0u8; 7];
215 assert_eq!(
216 Context::new(config, &[0u8; 8], &salt),
217 Err(Error::SaltTooShort)
218 );
219 }
220
221 #[test]
222 fn new_with_too_short_hash_length_returns_correct_error() {
223 let config = Config {
224 hash_length: 3,
225 ..Default::default()
226 };
227 assert_eq!(
228 Context::new(config, &[0u8; 8], &[0u8; 8]),
229 Err(Error::OutputTooShort)
230 );
231 }
232}