1use serde::{Deserialize, Serialize};
2
3use crate::error::{Error, Result};
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
12pub struct KdfParams {
13 pub m_cost_kib: u32,
16 pub t_cost: u32,
19 pub p_cost: u32,
22}
23
24impl KdfParams {
25 pub const fn new() -> Self {
28 Self {
29 m_cost_kib: 65_536,
30 t_cost: 4,
31 p_cost: 4,
32 }
33 }
34
35 pub(crate) const SERIALIZED_LEN: usize = 12;
36
37 pub(crate) fn write_to(&self, buf: &mut [u8]) {
38 buf[0..4].copy_from_slice(&self.m_cost_kib.to_le_bytes());
39 buf[4..8].copy_from_slice(&self.t_cost.to_le_bytes());
40 buf[8..12].copy_from_slice(&self.p_cost.to_le_bytes());
41 }
42
43 pub fn read_from(buf: &[u8]) -> Result<Self> {
45 if buf.len() < Self::SERIALIZED_LEN {
46 return Err(Error::InvalidHeader);
47 }
48 let params = Self {
49 m_cost_kib: u32::from_le_bytes(buf[0..4].try_into().map_err(|_| Error::InvalidHeader)?),
50 t_cost: u32::from_le_bytes(buf[4..8].try_into().map_err(|_| Error::InvalidHeader)?),
51 p_cost: u32::from_le_bytes(buf[8..12].try_into().map_err(|_| Error::InvalidHeader)?),
52 };
53 params.validate()?;
54 Ok(params)
55 }
56
57 pub fn validate(&self) -> Result<()> {
63 const MIN_M_KIB: u32 = 8 * 1024;
64 const MAX_M_KIB: u32 = 4 * 1024 * 1024; const MIN_T: u32 = 1;
66 const MAX_T: u32 = 64;
67 const MIN_P: u32 = 1;
68 const MAX_P: u32 = 16;
69
70 if !(MIN_M_KIB..=MAX_M_KIB).contains(&self.m_cost_kib) {
71 return Err(Error::InvalidKdfParams);
72 }
73 if !(MIN_T..=MAX_T).contains(&self.t_cost) {
74 return Err(Error::InvalidKdfParams);
75 }
76 if !(MIN_P..=MAX_P).contains(&self.p_cost) {
77 return Err(Error::InvalidKdfParams);
78 }
79 Ok(())
80 }
81}
82
83impl Default for KdfParams {
84 fn default() -> Self {
85 Self::new()
86 }
87}
88
89#[cfg(test)]
90mod tests {
91 use super::*;
92
93 #[test]
94 fn defaults_are_valid() {
95 assert!(KdfParams::new().validate().is_ok());
96 }
97
98 #[test]
99 fn roundtrip_serialization() {
100 let params = KdfParams {
101 m_cost_kib: 131_072,
102 t_cost: 3,
103 p_cost: 2,
104 };
105 let mut buf = [0u8; KdfParams::SERIALIZED_LEN];
106 params.write_to(&mut buf);
107 assert_eq!(KdfParams::read_from(&buf).unwrap(), params);
108 }
109
110 #[test]
111 fn rejects_out_of_bounds() {
112 assert!(KdfParams {
113 m_cost_kib: 1,
114 ..KdfParams::new()
115 }
116 .validate()
117 .is_err());
118 assert!(KdfParams {
119 m_cost_kib: u32::MAX,
120 ..KdfParams::new()
121 }
122 .validate()
123 .is_err());
124 assert!(KdfParams {
125 t_cost: 0,
126 ..KdfParams::new()
127 }
128 .validate()
129 .is_err());
130 assert!(KdfParams {
131 t_cost: 65,
132 ..KdfParams::new()
133 }
134 .validate()
135 .is_err());
136 assert!(KdfParams {
137 p_cost: 17,
138 ..KdfParams::new()
139 }
140 .validate()
141 .is_err());
142 }
143}