1use secrecy::{ExposeSecret, SecretString, SecretVec};
8use sha2::Digest;
9
10use crate::ocard::data::KdfDo;
11use crate::{Error, PinType};
12
13trait Hasher {
14 fn update(&mut self, _: &[u8]);
16
17 fn finish(self: Box<Self>) -> Vec<u8>;
19}
20
21#[derive(Default)]
22pub struct Sha2_256 {
23 inner: sha2::Sha256,
24}
25
26impl Hasher for Sha2_256 {
27 fn update(&mut self, data: &[u8]) {
28 self.inner.update(data);
29 }
30
31 fn finish(self: Box<Self>) -> Vec<u8> {
32 self.inner.finalize().as_slice().to_vec()
33 }
34}
35
36#[derive(Default)]
37pub struct Sha2_512 {
38 inner: sha2::Sha512,
39}
40
41impl Hasher for crate::ocard::kdf::Sha2_512 {
42 fn update(&mut self, data: &[u8]) {
43 self.inner.update(data);
44 }
45
46 fn finish(self: Box<Self>) -> Vec<u8> {
47 self.inner.finalize().as_slice().to_vec()
48 }
49}
50
51pub(crate) fn map_pin(
55 pw: SecretString,
56 pin_type: PinType,
57 kdf_do: Option<&KdfDo>,
58) -> Result<SecretVec<u8>, Error> {
59 match kdf_do {
60 None => {
61 Ok(pw.expose_secret().as_bytes().to_vec().into())
63 }
64 Some(kdf) if kdf.kdf_algo() == 0 => {
65 Ok(pw.expose_secret().as_bytes().to_vec().into())
67 }
68 Some(kdf) => {
69 match kdf.kdf_algo() {
72 3 => Ok(itersalt(
73 pw.expose_secret(),
74 kdf.hash_algo(),
75 kdf.iter_count(),
76 match pin_type {
77 PinType::Pw1 => kdf.salt_pw1(),
78 PinType::Rc => kdf.salt_rc(),
79 PinType::Pw3 => kdf.salt_pw3(),
80 },
81 )?
82 .into()),
83 _ => Err(Error::UnsupportedFeature(
84 "The KDF mode on the card is currently unsupported".to_string(),
85 )),
86 }
87 }
88 }
89}
90
91pub(crate) fn itersalt(
93 pw: &str,
94 hash_algo: Option<u8>,
95 count: Option<u32>,
96 salt: Option<&[u8]>,
97) -> Result<Vec<u8>, Error> {
98 let hash_algo = match hash_algo {
99 Some(hash_algo) => hash_algo,
100 None => {
101 return Err(Error::InternalError(
102 "No KDF hash algorithm setting found".to_string(),
103 ))
104 }
105 };
106
107 let mut count = match count {
109 Some(count) => count,
110 None => {
111 return Err(Error::InternalError(
112 "No KDF iteration count setting found".to_string(),
113 ))
114 }
115 } as usize;
116
117 let salt = match salt {
118 Some(salt) => salt,
119 None => {
120 return Err(Error::InternalError(
121 "No KDF salt setting found".to_string(),
122 ))
123 }
124 };
125
126 let mut hasher: Box<dyn Hasher> = match hash_algo {
128 0x08 => Box::<Sha2_256>::default(),
129 0x0A => Box::<Sha2_512>::default(),
130 _ => {
131 return Err(Error::InternalError(
132 "KDF: unsupported hash algorithm setting".to_string(),
133 ))
134 }
135 };
136
137 if count < salt.len() + pw.len() {
138 return Err(Error::InternalError(
139 "KDF: dubiously small count".to_string(),
140 ));
141 }
142
143 hasher.update(salt);
145 count -= salt.len();
146
147 hasher.update(pw.as_bytes());
148 count -= pw.len();
149
150 loop {
151 if count >= salt.len() {
152 hasher.update(salt);
153 count -= salt.len();
154 } else {
155 hasher.update(&salt[0..count]);
156 break;
157 }
158
159 if count >= pw.len() {
160 hasher.update(pw.as_bytes());
161 count -= pw.len();
162 } else {
163 hasher.update(&pw.as_bytes()[0..count]);
164 break;
165 }
166 }
167
168 Ok(hasher.finish())
169}
170
171#[test]
172fn test_itersalt() {
173 let user = itersalt(
176 "123456",
177 Some(0x8),
178 Some(100000),
179 Some(&[0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37]),
180 )
181 .expect("itersalt");
182 assert_eq!(
183 user,
184 vec![
185 0x77, 0x37, 0x84, 0xA6, 0x02, 0xB6, 0xC8, 0x1E, 0x3F, 0x09, 0x2F, 0x4D, 0x7D, 0x00,
186 0xE1, 0x7C, 0xC8, 0x22, 0xD8, 0x8F, 0x73, 0x60, 0xFC, 0xF2, 0xD2, 0xEF, 0x2D, 0x9D,
187 0x90, 0x1F, 0x44, 0xB6
188 ]
189 );
190
191 let admin = itersalt(
192 "12345678",
193 Some(0x8),
194 Some(100000),
195 Some(&[0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48]),
196 )
197 .expect("itersalt");
198 assert_eq!(
199 admin,
200 vec![
201 0x26, 0x75, 0xD6, 0x16, 0x4A, 0x0D, 0x48, 0x27, 0xD1, 0xD0, 0x0C, 0x7E, 0xEA, 0x62,
202 0x0D, 0x01, 0x5C, 0x00, 0x03, 0x0A, 0x1C, 0xAB, 0x38, 0xB4, 0xD0, 0xDD, 0x60, 0x0B,
203 0x27, 0xDC, 0x96, 0x30
204 ]
205 );
206}