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