1use std::fmt::Display;
2
3use crate::{
4 util::raw_scrypt_params, Result, SError, ALG_SIZE, CHK_ALG, CHK_SIZE, COMPONENT_SIZE, KDF_ALG,
5 KDF_LIMIT_SIZE, KDF_SALT_SIZE, KEYNUM_SK_SIZE, KEY_SIG_ALG, KID_SIZE, MEMLIMIT, N_LOG2_MAX,
6 OPSLIMIT,
7};
8use base64::Engine;
9use blake2::{Blake2b, Digest};
10use ed25519_dalek::{
11 ed25519::{self, ComponentBytes},
12 Signer,
13};
14use scrypt::password_hash::rand_core::{self, RngCore};
15use zeroize::{Zeroize, ZeroizeOnDrop};
16
17#[derive(Debug, Clone, PartialEq, Eq)]
21pub struct SecretKeyBox<'s> {
22 pub(crate) untrusted_comment: Option<&'s str>,
23 pub(crate) secret_key: SecretKey,
24}
25impl Display for SecretKeyBox<'_> {
26 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
27 let mut s = String::new();
28 s.push_str("untrusted comment: ");
29 if let Some(c) = self.untrusted_comment {
30 s.push_str(c);
31 }
32 s.push('\n');
33 let encoder = base64::engine::general_purpose::STANDARD;
34 let mut sk_format = vec![];
35 sk_format.extend_from_slice(&self.secret_key.sig_alg);
36 sk_format.extend_from_slice(&self.secret_key.kdf_alg);
37 sk_format.extend_from_slice(&self.secret_key.cksum_alg);
38 sk_format.extend_from_slice(&self.secret_key.kdf_salt);
39 sk_format.extend_from_slice(&self.secret_key.kdf_opslimit.to_le_bytes());
40 sk_format.extend_from_slice(&self.secret_key.kdf_memlimit.to_le_bytes());
41 sk_format.extend_from_slice(&self.secret_key.keynum_sk);
42 let sk = encoder.encode(&sk_format);
43 s.push_str(&sk);
44 s.push('\n');
45
46 write!(f, "{}", s)
47 }
48}
49type Blake2b256 = Blake2b<blake2::digest::consts::U32>;
50impl<'s> SecretKeyBox<'s> {
51 fn new(untrusted_comment: Option<&'s str>, secret_key: SecretKey) -> Self {
52 Self {
53 untrusted_comment,
54 secret_key,
55 }
56 }
57 pub fn from_signing_key(
58 signing_key: ed25519_dalek::SigningKey,
59 kid: &[u8; KID_SIZE],
60 password: Option<&[u8]>,
61 untrusted_comment: Option<&'s str>,
62 ) -> Result<Self> {
63 let sk = signing_key.to_bytes();
64 let pk = signing_key.verifying_key().to_bytes();
65 let mut dest = [0u8; KDF_SALT_SIZE];
66 rand_core::OsRng.try_fill_bytes(&mut dest)?;
67
68 let mut hash = Blake2b256::new();
69 hash.update(KEY_SIG_ALG);
70 hash.update(kid);
71 hash.update(sk);
72 hash.update(pk);
73 let mut kdf_buf = kdf(password, &dest, OPSLIMIT, MEMLIMIT)?;
74 let keynum_sk = KeynumSK {
75 key_id: *kid,
76 sec_key: RawSk(sk),
77 pub_key: pk,
78 checksum: hash.finalize().to_vec().try_into().unwrap(),
79 };
80 kdf_buf = keynum_sk.to_bytes(kdf_buf);
81 let secret_key = SecretKey {
82 sig_alg: KEY_SIG_ALG,
83 kdf_alg: KDF_ALG,
84 cksum_alg: CHK_ALG,
85 kdf_salt: dest,
86 kdf_opslimit: OPSLIMIT,
87 kdf_memlimit: MEMLIMIT,
88 keynum_sk: kdf_buf,
89 };
90
91 Ok(Self::new(untrusted_comment, secret_key))
92 }
93 pub(crate) fn sign(
94 &self,
95 message: &[u8],
96 password: Option<&[u8]>,
97 ) -> Result<ed25519::Signature> {
98 self.secret_key.sign(message, password)
99 }
100 pub(crate) fn xor_keynum_sk(&self, password: Option<&[u8]>) -> Result<KeynumSK> {
101 self.secret_key.xor_keynum_sk(password)
102 }
103 pub fn from_raw_str(s: &'s str) -> Result<Self> {
106 let secret_key = s.trim();
107 let decoder = base64::engine::general_purpose::STANDARD;
108 let sk_format = decoder
109 .decode(secret_key.as_bytes())
110 .map_err(|e| SError::new(crate::ErrorKind::SecretKey, e))?;
111 if sk_format.len()
112 != ALG_SIZE
113 + ALG_SIZE
114 + ALG_SIZE
115 + KDF_SALT_SIZE
116 + KDF_LIMIT_SIZE
117 + KDF_LIMIT_SIZE
118 + KEYNUM_SK_SIZE
119 {
120 return Err(SError::new(
121 crate::ErrorKind::SecretKey,
122 "invalid secret key length",
123 ));
124 }
125 let sig_alg = &sk_format[..ALG_SIZE];
126 let kdf_alg = &sk_format[ALG_SIZE..ALG_SIZE + ALG_SIZE];
127 let cksum_alg = &sk_format[ALG_SIZE + ALG_SIZE..ALG_SIZE + ALG_SIZE + ALG_SIZE];
128 let kdf_salt = &sk_format
129 [ALG_SIZE + ALG_SIZE + ALG_SIZE..ALG_SIZE + ALG_SIZE + ALG_SIZE + KDF_SALT_SIZE];
130 let kdf_opslimit = u64::from_le_bytes(
131 sk_format[ALG_SIZE + ALG_SIZE + ALG_SIZE + KDF_SALT_SIZE
132 ..ALG_SIZE + ALG_SIZE + ALG_SIZE + KDF_SALT_SIZE + KDF_LIMIT_SIZE]
133 .try_into()
134 .unwrap(),
135 );
136 let kdf_memlimit = u64::from_le_bytes(
137 sk_format[ALG_SIZE + ALG_SIZE + ALG_SIZE + KDF_SALT_SIZE + KDF_LIMIT_SIZE
138 ..ALG_SIZE + ALG_SIZE + ALG_SIZE + KDF_SALT_SIZE + KDF_LIMIT_SIZE + KDF_LIMIT_SIZE]
139 .try_into()
140 .unwrap(),
141 );
142
143 let secret_key = SecretKey {
144 sig_alg: sig_alg.try_into().unwrap(),
145 kdf_alg: kdf_alg.try_into().unwrap(),
146 cksum_alg: cksum_alg.try_into().unwrap(),
147 kdf_salt: kdf_salt.try_into().unwrap(),
148 kdf_opslimit,
149 kdf_memlimit,
150 keynum_sk: sk_format[ALG_SIZE
151 + ALG_SIZE
152 + ALG_SIZE
153 + KDF_SALT_SIZE
154 + KDF_LIMIT_SIZE
155 + KDF_LIMIT_SIZE..]
156 .try_into()
157 .unwrap(),
158 };
159 Ok(SecretKeyBox::new(None, secret_key))
160 }
161 #[allow(clippy::should_implement_trait)]
165 pub fn from_str(s: &'s str) -> Result<Self> {
166 parse_secret_key(s)
167 }
168 pub fn untrusted_comment(&self) -> Option<&'s str> {
170 self.untrusted_comment
171 }
172}
173fn parse_raw_secret_key(secret_key: &str) -> Result<SecretKey> {
174 let decoder = base64::engine::general_purpose::STANDARD;
175 let sk_format = decoder
176 .decode(secret_key.as_bytes())
177 .map_err(|e| SError::new(crate::ErrorKind::SecretKey, e))?;
178 if sk_format.len()
179 != ALG_SIZE
180 + ALG_SIZE
181 + ALG_SIZE
182 + KDF_SALT_SIZE
183 + KDF_LIMIT_SIZE
184 + KDF_LIMIT_SIZE
185 + KEYNUM_SK_SIZE
186 {
187 return Err(SError::new(
188 crate::ErrorKind::SecretKey,
189 "invalid secret key length",
190 ));
191 }
192 let sig_alg = &sk_format[..ALG_SIZE];
193 let kdf_alg = &sk_format[ALG_SIZE..ALG_SIZE + ALG_SIZE];
194 let cksum_alg = &sk_format[ALG_SIZE + ALG_SIZE..ALG_SIZE + ALG_SIZE + ALG_SIZE];
195 let kdf_salt =
196 &sk_format[ALG_SIZE + ALG_SIZE + ALG_SIZE..ALG_SIZE + ALG_SIZE + ALG_SIZE + KDF_SALT_SIZE];
197 let kdf_opslimit = u64::from_le_bytes(
198 sk_format[ALG_SIZE + ALG_SIZE + ALG_SIZE + KDF_SALT_SIZE
199 ..ALG_SIZE + ALG_SIZE + ALG_SIZE + KDF_SALT_SIZE + KDF_LIMIT_SIZE]
200 .try_into()
201 .unwrap(),
202 );
203 let kdf_memlimit = u64::from_le_bytes(
204 sk_format[ALG_SIZE + ALG_SIZE + ALG_SIZE + KDF_SALT_SIZE + KDF_LIMIT_SIZE
205 ..ALG_SIZE + ALG_SIZE + ALG_SIZE + KDF_SALT_SIZE + KDF_LIMIT_SIZE + KDF_LIMIT_SIZE]
206 .try_into()
207 .unwrap(),
208 );
209
210 let secret_key = SecretKey {
211 sig_alg: sig_alg.try_into().unwrap(),
212 kdf_alg: kdf_alg.try_into().unwrap(),
213 cksum_alg: cksum_alg.try_into().unwrap(),
214 kdf_salt: kdf_salt.try_into().unwrap(),
215 kdf_opslimit,
216 kdf_memlimit,
217 keynum_sk: sk_format
218 [ALG_SIZE + ALG_SIZE + ALG_SIZE + KDF_SALT_SIZE + KDF_LIMIT_SIZE + KDF_LIMIT_SIZE..]
219 .try_into()
220 .unwrap(),
221 };
222 Ok(secret_key)
223}
224fn parse_secret_key(s: &str) -> Result<SecretKeyBox> {
225 let mut lines = s.lines();
226 if let Some(c) = lines.next() {
227 let untrusted_comment = c.strip_prefix("untrusted comment: ");
228 let secret_key = lines
229 .next()
230 .ok_or_else(|| SError::new(crate::ErrorKind::SecretKey, "missing secret key"))?;
231 Ok(SecretKeyBox::new(
232 untrusted_comment,
233 parse_raw_secret_key(secret_key)?,
234 ))
235 } else {
236 Err(SError::new(
237 crate::ErrorKind::SecretKey,
238 "missing untrusted comment",
239 ))
240 }
241}
242
243#[cfg(test)]
244#[test]
245fn test_parse_secret_key() {
246 use crate::KeyPairBox;
247 let password = b"password";
248 let k = KeyPairBox::generate(Some(password), None, None).unwrap();
249 let file = k.secret_key_box.to_string();
250 let sk = parse_secret_key(&file).unwrap();
251 assert_eq!(file, sk.to_string());
252}
253#[derive(Clone, Debug, ZeroizeOnDrop, PartialEq, Eq)]
255pub(crate) struct SecretKey {
256 pub sig_alg: [u8; ALG_SIZE],
257 kdf_alg: [u8; ALG_SIZE],
258 cksum_alg: [u8; ALG_SIZE],
259 kdf_salt: [u8; KDF_SALT_SIZE],
260 kdf_opslimit: u64,
261 kdf_memlimit: u64,
262 keynum_sk: [u8; KEYNUM_SK_SIZE],
263}
264#[derive(Clone, Debug, ZeroizeOnDrop)]
265pub struct KeynumSK {
266 pub(crate) key_id: [u8; KID_SIZE],
267 sec_key: RawSk,
268 pub pub_key: ComponentBytes,
269 checksum: [u8; CHK_SIZE],
270}
271impl KeynumSK {
272 fn to_bytes(&self, mut kdf_buf: [u8; KEYNUM_SK_SIZE]) -> [u8; KEYNUM_SK_SIZE] {
273 for (i, item) in kdf_buf.iter_mut().enumerate().take(KID_SIZE) {
274 *item ^= self.key_id[i];
275 }
276 for i in 0..COMPONENT_SIZE {
277 kdf_buf[KID_SIZE + i] ^= self.sec_key.0[i];
278 }
279 for i in 0..COMPONENT_SIZE {
280 kdf_buf[KID_SIZE + COMPONENT_SIZE + i] ^= self.pub_key[i];
281 }
282 for i in 0..CHK_SIZE {
283 kdf_buf[KID_SIZE + 2 * COMPONENT_SIZE + i] ^= self.checksum[i];
284 }
285 kdf_buf
286 }
287 fn from_bytes(keynum_sk: &[u8; KEYNUM_SK_SIZE], mut kdf_buf: [u8; KEYNUM_SK_SIZE]) -> Self {
288 for i in 0..KEYNUM_SK_SIZE {
290 kdf_buf[i] ^= keynum_sk[i];
291 }
292 Self {
293 key_id: kdf_buf[0..KID_SIZE].try_into().unwrap(),
294 sec_key: RawSk(
295 kdf_buf[KID_SIZE..KID_SIZE + COMPONENT_SIZE]
296 .try_into()
297 .unwrap(),
298 ),
299 pub_key: kdf_buf[KID_SIZE + COMPONENT_SIZE..KID_SIZE + 2 * COMPONENT_SIZE]
300 .try_into()
301 .unwrap(),
302 checksum: kdf_buf[KID_SIZE + 2 * COMPONENT_SIZE..KEYNUM_SK_SIZE]
303 .try_into()
304 .unwrap(),
305 }
306 }
307}
308#[derive(Debug, Clone, ZeroizeOnDrop, Zeroize)]
309struct RawSk(ComponentBytes);
310impl Signer<ed25519::Signature> for RawSk {
311 fn try_sign(&self, msg: &[u8]) -> std::result::Result<ed25519::Signature, ed25519::Error> {
312 let sk = ed25519_dalek::SigningKey::from_bytes(&self.0);
313 Ok(sk.sign(msg))
314 }
315}
316fn kdf(
317 password: Option<&[u8]>,
318 salt: &[u8; KDF_SALT_SIZE],
319 opslimit: u64,
320 memlimit: u64,
321) -> Result<[u8; KEYNUM_SK_SIZE]> {
322 let params = raw_scrypt_params(memlimit as usize, opslimit, N_LOG2_MAX)?;
323 let mut stream = [0u8; KEYNUM_SK_SIZE];
324 scrypt::scrypt(password.unwrap_or(&[]), salt, ¶ms, &mut stream)?;
325 Ok(stream)
326}
327impl SecretKey {
328 pub fn sign(&self, message: &[u8], password: Option<&[u8]>) -> Result<ed25519::Signature> {
329 let keynum_sk = self.xor_keynum_sk(password);
330 Ok(keynum_sk?.sec_key.sign(message))
331 }
332 pub(crate) fn xor_keynum_sk(&self, password: Option<&[u8]>) -> Result<KeynumSK> {
333 let stream = kdf(
334 password,
335 &self.kdf_salt,
336 self.kdf_opslimit,
337 self.kdf_memlimit,
338 )?;
339
340 let keynum_sk = KeynumSK::from_bytes(&self.keynum_sk, stream);
341
342 let mut hash = Blake2b256::new();
343 hash.update(self.sig_alg);
344 hash.update(&keynum_sk.key_id);
345 hash.update(&keynum_sk.sec_key.0);
346 hash.update(&keynum_sk.pub_key);
347 if hash.finalize().to_vec() != keynum_sk.checksum {
348 return Err(SError::new(
349 crate::ErrorKind::SecretKey,
350 "checksum mismatch, invalid password",
351 ));
352 }
353 Ok(keynum_sk)
354 }
355}
356#[cfg(test)]
357#[test]
358fn test_sign() {
359 use crate::{pub_key_from_sec_key, KeyPairBox};
360 let password = b"password";
361 let k = KeyPairBox::generate(Some(password), None, None).unwrap();
362 let s = k.secret_key_box.to_string();
363 let sk = parse_secret_key(&s).unwrap();
364 let msg = b"hello world";
365 let sig = sk.sign(msg, Some(password)).unwrap();
366 let pk = pub_key_from_sec_key(&sk, Some(password)).unwrap();
367 let v = pk.public_key.key.verify(msg, &sig);
368 assert_eq!(v.unwrap(), true);
369}