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