1use std::{io, ops::Deref};
2
3use chrono::{DateTime, Utc};
4use generic_array::GenericArray;
5use log::{debug, warn};
6use rand::{CryptoRng, Rng};
7
8use crate::{
9 armor,
10 composed::{
11 key::{PublicKey, PublicSubkey},
12 signed_key::{SignedKeyDetails, SignedPublicSubKey},
13 ArmorOptions, PlainSessionKey, SignedPublicKey,
14 },
15 crypto::hash::KnownDigest,
16 errors::{ensure, Result},
17 packet::{self, Packet, PacketTrait, SignatureType, SubpacketData},
18 ser::Serialize,
19 types::{EskType, Imprint, Password, PkeskBytes, PublicKeyTrait, Tag},
20};
21
22#[derive(Debug, PartialEq, Eq, Clone)]
24pub struct SignedSecretKey {
25 pub primary_key: packet::SecretKey,
26 pub details: SignedKeyDetails,
27 pub public_subkeys: Vec<SignedPublicSubKey>,
28 pub secret_subkeys: Vec<SignedSecretSubKey>,
29}
30
31pub struct SignedSecretKeyParser<
34 I: Sized + Iterator<Item = crate::errors::Result<crate::packet::Packet>>,
35> {
36 inner: std::iter::Peekable<I>,
37}
38
39impl<I: Sized + Iterator<Item = crate::errors::Result<crate::packet::Packet>>>
40 SignedSecretKeyParser<I>
41{
42 pub fn into_inner(self) -> std::iter::Peekable<I> {
43 self.inner
44 }
45
46 pub fn from_packets(packets: std::iter::Peekable<I>) -> Self {
47 SignedSecretKeyParser { inner: packets }
48 }
49}
50
51impl<I: Sized + Iterator<Item = Result<Packet>>> Iterator for SignedSecretKeyParser<I> {
52 type Item = Result<SignedSecretKey>;
53
54 fn next(&mut self) -> Option<Self::Item> {
55 match super::key_parser::next::<_, packet::SecretKey>(&mut self.inner, Tag::SecretKey, true)
56 {
57 Some(Err(err)) => Some(Err(err)),
58 None => None,
59 Some(Ok((primary_key, details, public_subkeys, secret_subkeys))) => Some(Ok(
60 SignedSecretKey::new(primary_key, details, public_subkeys, secret_subkeys),
61 )),
62 }
63 }
64}
65
66impl crate::composed::Deserializable for SignedSecretKey {
67 fn from_packets<'a, I: Iterator<Item = Result<Packet>> + 'a>(
70 packets: std::iter::Peekable<I>,
71 ) -> Box<dyn Iterator<Item = Result<Self>> + 'a> {
72 Box::new(SignedSecretKeyParser::from_packets(packets))
73 }
74
75 fn matches_block_type(typ: armor::BlockType) -> bool {
76 matches!(typ, armor::BlockType::PrivateKey | armor::BlockType::File)
77 }
78}
79
80impl SignedSecretKey {
81 pub fn new(
82 primary_key: packet::SecretKey,
83 details: SignedKeyDetails,
84 mut public_subkeys: Vec<SignedPublicSubKey>,
85 mut secret_subkeys: Vec<SignedSecretSubKey>,
86 ) -> Self {
87 public_subkeys.retain(|key| {
88 if key.signatures.is_empty() {
89 warn!("ignoring unsigned {:?}", key.key);
90 false
91 } else {
92 true
93 }
94 });
95
96 secret_subkeys.retain(|key| {
97 if key.signatures.is_empty() {
98 warn!("ignoring unsigned {:?}", key.key);
99 false
100 } else {
101 true
102 }
103 });
104
105 SignedSecretKey {
106 primary_key,
107 details,
108 public_subkeys,
109 secret_subkeys,
110 }
111 }
112
113 pub fn expires_at(&self) -> Option<DateTime<Utc>> {
115 let expiration = self.details.key_expiration_time()?;
116 Some(*self.primary_key.public_key().created_at() + expiration)
117 }
118
119 fn verify_public_subkeys(&self) -> Result<()> {
120 for subkey in &self.public_subkeys {
121 subkey.verify(self.primary_key.public_key())?;
122 }
123
124 Ok(())
125 }
126
127 fn verify_secret_subkeys(&self) -> Result<()> {
128 for subkey in &self.secret_subkeys {
129 subkey.verify(self.primary_key.public_key())?;
130 }
131
132 Ok(())
133 }
134
135 pub fn verify(&self) -> Result<()> {
136 self.details.verify(self.primary_key.public_key())?;
137 self.verify_public_subkeys()?;
138 self.verify_secret_subkeys()?;
139
140 Ok(())
141 }
142
143 pub fn to_armored_writer(
144 &self,
145 writer: &mut impl io::Write,
146 opts: ArmorOptions<'_>,
147 ) -> Result<()> {
148 armor::write(
149 self,
150 armor::BlockType::PrivateKey,
151 writer,
152 opts.headers,
153 opts.include_checksum,
154 )
155 }
156
157 pub fn to_armored_bytes(&self, opts: ArmorOptions<'_>) -> Result<Vec<u8>> {
158 let mut buf = Vec::new();
159
160 self.to_armored_writer(&mut buf, opts)?;
161
162 Ok(buf)
163 }
164
165 pub fn to_armored_string(&self, opts: ArmorOptions<'_>) -> Result<String> {
166 let res = String::from_utf8(self.to_armored_bytes(opts)?).map_err(|e| e.utf8_error())?;
167 Ok(res)
168 }
169
170 pub fn encrypt<R: Rng + CryptoRng>(
171 &self,
172 rng: R,
173 plain: &[u8],
174 typ: EskType,
175 ) -> Result<PkeskBytes> {
176 self.primary_key.encrypt(rng, plain, typ)
177 }
178
179 pub fn public_key(&self) -> PublicKey {
180 let mut subkeys: Vec<PublicSubkey> = self
181 .public_subkeys
182 .iter()
183 .map(SignedPublicSubKey::as_unsigned)
184 .collect();
185 let sec_subkeys = self.secret_subkeys.iter().map(|k| k.public_key());
186 subkeys.extend(sec_subkeys);
187
188 PublicKey::new(
189 self.primary_key.public_key().clone(),
190 self.details.as_unsigned(),
191 subkeys,
192 )
193 }
194
195 pub fn signed_public_key(&self) -> SignedPublicKey {
198 let mut public_subkeys: Vec<SignedPublicSubKey> = self.public_subkeys.to_vec();
199 let sec_subkeys: Vec<SignedPublicSubKey> = self
200 .secret_subkeys
201 .iter()
202 .map(SignedSecretSubKey::signed_public_key)
203 .collect();
204 public_subkeys.extend(sec_subkeys);
205
206 SignedPublicKey::new(
207 self.primary_key.public_key().clone(),
208 self.details.clone(),
209 public_subkeys,
210 )
211 }
212
213 pub fn decrypt_session_key(
215 &self,
216 key_pw: &Password,
217 values: &PkeskBytes,
218 typ: EskType,
219 ) -> Result<Result<PlainSessionKey>> {
220 debug!("decrypt session key");
221
222 self.unlock(key_pw, |pub_params, priv_key| {
223 priv_key.decrypt(pub_params, values, typ, self.primary_key.public_key())
224 })
225 }
226}
227
228impl Serialize for SignedSecretKey {
229 fn to_writer<W: io::Write>(&self, writer: &mut W) -> Result<()> {
230 self.primary_key.to_writer_with_header(writer)?;
231 self.details.to_writer(writer)?;
232 for ps in &self.public_subkeys {
233 ps.to_writer(writer)?;
234 }
235
236 for ps in &self.secret_subkeys {
237 ps.to_writer(writer)?;
238 }
239
240 Ok(())
241 }
242
243 fn write_len(&self) -> usize {
244 let mut sum = self.primary_key.write_len_with_header();
245 sum += self.details.write_len();
246 sum += self.public_subkeys.write_len();
247 sum += self.secret_subkeys.write_len();
248 sum
249 }
250}
251
252impl Deref for SignedSecretKey {
253 type Target = packet::SecretKey;
254
255 fn deref(&self) -> &Self::Target {
256 &self.primary_key
257 }
258}
259
260impl Imprint for SignedSecretKey {
261 fn imprint<D: KnownDigest>(&self) -> Result<GenericArray<u8, D::OutputSize>> {
262 self.primary_key.imprint::<D>()
263 }
264}
265
266#[derive(Debug, PartialEq, Eq, Clone)]
268pub struct SignedSecretSubKey {
269 pub key: packet::SecretSubkey,
270 pub signatures: Vec<packet::Signature>,
271}
272
273impl SignedSecretSubKey {
274 pub fn new(key: packet::SecretSubkey, mut signatures: Vec<packet::Signature>) -> Self {
275 signatures.retain(|sig| {
276 if sig.typ() != Some(SignatureType::SubkeyBinding)
277 && sig.typ() != Some(SignatureType::SubkeyRevocation)
278 {
279 warn!(
280 "ignoring unexpected signature {:?} after Subkey packet",
281 sig.typ()
282 );
283 false
284 } else {
285 true
286 }
287 });
288
289 SignedSecretSubKey { key, signatures }
290 }
291
292 pub fn verify<P>(&self, key: &P) -> Result<()>
293 where
294 P: PublicKeyTrait + Serialize,
295 {
296 ensure!(!self.signatures.is_empty(), "missing subkey bindings");
297
298 for sig in &self.signatures {
299 sig.verify_subkey_binding(key, self.key.public_key())?;
300 }
301
302 Ok(())
303 }
304
305 pub fn encrypt<R: Rng + CryptoRng>(
306 &self,
307 rng: R,
308 plain: &[u8],
309 typ: EskType,
310 ) -> Result<PkeskBytes> {
311 self.key.encrypt(rng, plain, typ)
312 }
313
314 pub fn signed_public_key(&self) -> SignedPublicSubKey {
317 SignedPublicSubKey::new(self.key.public_key().clone(), self.signatures.clone())
318 }
319
320 pub fn public_key(&self) -> PublicSubkey {
321 let sig = self.signatures.first().expect("invalid signed subkey");
322
323 let keyflags = sig.key_flags();
324
325 let embedded = sig.config().and_then(|c| {
326 c.hashed_subpackets().find_map(|p| match &p.data {
327 SubpacketData::EmbeddedSignature(backsig) => Some(*backsig.clone()),
328 _ => None,
329 })
330 });
331
332 PublicSubkey::new(self.key.public_key().clone(), keyflags, embedded)
333 }
334
335 pub fn decrypt_session_key(
337 &self,
338 key_pw: &Password,
339 values: &PkeskBytes,
340 typ: EskType,
341 ) -> Result<Result<PlainSessionKey>> {
342 debug!("decrypt session key");
343
344 self.unlock(key_pw, |pub_params, priv_key| {
345 priv_key.decrypt(pub_params, values, typ, self.key.public_key())
346 })
347 }
348}
349
350impl Deref for SignedSecretSubKey {
351 type Target = packet::SecretSubkey;
352
353 fn deref(&self) -> &Self::Target {
354 &self.key
355 }
356}
357
358impl Serialize for SignedSecretSubKey {
359 fn to_writer<W: io::Write>(&self, writer: &mut W) -> Result<()> {
360 self.key.to_writer_with_header(writer)?;
361 for sig in &self.signatures {
362 sig.to_writer_with_header(writer)?;
363 }
364
365 Ok(())
366 }
367
368 fn write_len(&self) -> usize {
369 let mut sum = self.key.write_len_with_header();
370 for sig in &self.signatures {
371 sum += sig.write_len_with_header()
372 }
373 sum
374 }
375}
376
377impl Imprint for SignedSecretSubKey {
378 fn imprint<D: KnownDigest>(&self) -> Result<GenericArray<u8, D::OutputSize>> {
379 self.key.imprint::<D>()
380 }
381}
382
383impl From<SignedSecretKey> for SignedPublicKey {
384 fn from(value: SignedSecretKey) -> Self {
385 let primary = value.primary_key.public_key();
386 let details = value.details;
387
388 let mut subkeys = value.public_subkeys;
389
390 value
391 .secret_subkeys
392 .into_iter()
393 .for_each(|key| subkeys.push(key.into()));
394
395 SignedPublicKey::new(primary.clone(), details, subkeys)
396 }
397}
398
399impl From<SignedSecretSubKey> for SignedPublicSubKey {
400 fn from(value: SignedSecretSubKey) -> Self {
401 SignedPublicSubKey::new(value.key.public_key().clone(), value.signatures)
402 }
403}
404
405#[cfg(test)]
406mod tests {
407 #![allow(clippy::unwrap_used)]
408
409 use rand::SeedableRng;
410 use rand_chacha::ChaCha8Rng;
411
412 use super::*;
413 use crate::{
414 composed::{shared::Deserializable, Message, MessageBuilder},
415 crypto::hash::HashAlgorithm,
416 types::{KeyVersion, Password, S2kParams},
417 };
418
419 #[test]
420 fn test_v6_annex_a_4() -> Result<()> {
421 let _ = pretty_env_logger::try_init();
422
423 let tsk = "-----BEGIN PGP PRIVATE KEY BLOCK-----
426
427xUsGY4d/4xsAAAAg+U2nu0jWCmHlZ3BqZYfQMxmZu52JGggkLq2EVD34laMAGXKB
428exK+cH6NX1hs5hNhIB00TrJmosgv3mg1ditlsLfCsQYfGwoAAABCBYJjh3/jAwsJ
429BwUVCg4IDAIWAAKbAwIeCSIhBssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce6
4302azJBScJAgcCAAAAAK0oIBA+LX0ifsDm185Ecds2v8lwgyU2kCcUmKfvBXbAf6rh
431RYWzuQOwEn7E/aLwIwRaLsdry0+VcallHhSu4RN6HWaEQsiPlR4zxP/TP7mhfVEe
4327XWPxtnMUMtf15OyA51YBMdLBmOHf+MZAAAAIIaTJINn+eUBXbki+PSAld2nhJh/
433LVmFsS+60WyvXkQ1AE1gCk95TUR3XFeibg/u/tVY6a//1q0NWC1X+yui3O24wpsG
434GBsKAAAALAWCY4d/4wKbDCIhBssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce6
4352azJAAAAAAQBIKbpGG2dWTX8j+VjFM21J0hqWlEg+bdiojWnKfA5AQpWUWtnNwDE
436M0g12vYxoWM8Y81W+bHBw805I8kWVkXU6vFOi+HWvv/ira7ofJu16NnoUkhclkUr
437k0mXubZvyl4GBg==
438-----END PGP PRIVATE KEY BLOCK-----";
439
440 let (ssk, _) = SignedSecretKey::from_armor_single(io::Cursor::new(tsk))?;
441
442 ssk.verify()?;
445
446 let mut rng = ChaCha8Rng::seed_from_u64(0);
447 let pri = &ssk.primary_key;
448
449 let mut builder = crate::composed::MessageBuilder::from_bytes("", &b"Hello world"[..]);
450 builder.sign(pri, Password::empty(), HashAlgorithm::Sha256);
451 let signed = builder.to_armored_string(&mut rng, ArmorOptions::default())?;
452
453 eprintln!("{signed}");
454
455 let (mut message, _) = Message::from_armor(signed.as_bytes())?;
456 message.verify_read(&pri.public_key())?;
457
458 Ok(())
459 }
460
461 const ANNEX_A_5: &str = "-----BEGIN PGP PRIVATE KEY BLOCK-----
463
464xYIGY4d/4xsAAAAg+U2nu0jWCmHlZ3BqZYfQMxmZu52JGggkLq2EVD34laP9JgkC
465FARdb9ccngltHraRe25uHuyuAQQVtKipJ0+r5jL4dacGWSAheCWPpITYiyfyIOPS
4663gIDyg8f7strd1OB4+LZsUhcIjOMpVHgmiY/IutJkulneoBYwrEGHxsKAAAAQgWC
467Y4d/4wMLCQcFFQoOCAwCFgACmwMCHgkiIQbLGGxPBgmml+TVLfpscisMHx4nwYpW
468cI9lJewnutmsyQUnCQIHAgAAAACtKCAQPi19In7A5tfORHHbNr/JcIMlNpAnFJin
4697wV2wH+q4UWFs7kDsBJ+xP2i8CMEWi7Ha8tPlXGpZR4UruETeh1mhELIj5UeM8T/
4700z+5oX1RHu11j8bZzFDLX9eTsgOdWATHggZjh3/jGQAAACCGkySDZ/nlAV25Ivj0
471gJXdp4SYfy1ZhbEvutFsr15ENf0mCQIUBA5hhGgp2oaavg6mFUXcFMwBBBUuE8qf
4729Ock+xwusd+GAglBr5LVyr/lup3xxQvHXFSjjA2haXfoN6xUGRdDEHI6+uevKjVR
473v5oAxgu7eJpaXNjCmwYYGwoAAAAsBYJjh3/jApsMIiEGyxhsTwYJppfk1S36bHIr
474DB8eJ8GKVnCPZSXsJ7rZrMkAAAAABAEgpukYbZ1ZNfyP5WMUzbUnSGpaUSD5t2Ki
475Nacp8DkBClZRa2c3AMQzSDXa9jGhYzxjzVb5scHDzTkjyRZWRdTq8U6L4da+/+Kt
476ruh8m7Xo2ehSSFyWRSuTSZe5tm/KXgYG
477-----END PGP PRIVATE KEY BLOCK-----";
478
479 const ANNEX_A_5_PASSPHRASE: &str = "correct horse battery staple";
480
481 #[test]
482 #[ignore] fn test_v6_annex_a_5() -> Result<()> {
484 let _ = pretty_env_logger::try_init();
485
486 let (ssk, _) = SignedSecretKey::from_armor_single(io::Cursor::new(ANNEX_A_5))?;
487 ssk.verify()?;
488
489 let mut rng = ChaCha8Rng::seed_from_u64(0);
490 let mut builder = MessageBuilder::from_bytes("", &b"Hello world"[..]);
491 builder.sign(
492 &ssk.primary_key,
493 ANNEX_A_5_PASSPHRASE.into(),
494 HashAlgorithm::Sha256,
495 );
496 let msg = builder.to_vec(&mut rng)?;
497 let mut msg = Message::from_bytes(&msg[..])?;
498 msg.verify_read(&ssk.primary_key.public_key())?;
499 Ok(())
500 }
501
502 #[test]
503 #[ignore] fn secret_key_protection_v6() -> Result<()> {
505 let _ = pretty_env_logger::try_init();
506
507 let file_name = "";
508 let text = b"Hello world";
509 let mut rng = ChaCha8Rng::seed_from_u64(0);
510
511 let (ssk, _) = SignedSecretKey::from_armor_single(io::Cursor::new(ANNEX_A_5))?;
512 ssk.verify()?;
513
514 let mut pri = ssk.primary_key;
516
517 pri.remove_password(&ANNEX_A_5_PASSPHRASE.into())?;
519
520 let mut builder = MessageBuilder::from_bytes(file_name, &text[..]);
522 builder.sign(&pri, Password::empty(), HashAlgorithm::Sha256);
523 let msg = builder.to_vec(&mut rng)?;
524
525 let mut msg = Message::from_bytes(&msg[..])?;
526 msg.verify_read(pri.public_key())?;
527
528 pri.set_password(&mut rng, &ANNEX_A_5_PASSPHRASE.into())?;
530
531 let mut builder = MessageBuilder::from_bytes(file_name, &text[..]);
533 builder.sign(&pri, ANNEX_A_5_PASSPHRASE.into(), HashAlgorithm::Sha256);
534 let msg = builder.to_vec(&mut rng)?;
535
536 let mut msg = Message::from_bytes(&msg[..])?;
537 msg.verify_read(pri.public_key())?;
538
539 pri.remove_password(&ANNEX_A_5_PASSPHRASE.into())?;
541
542 pri.set_password_with_s2k(
544 &ANNEX_A_5_PASSPHRASE.into(),
545 S2kParams::new_default(&mut rng, KeyVersion::V4),
546 )?;
547
548 let mut builder = MessageBuilder::from_bytes(file_name, &text[..]);
550 builder.sign(&pri, ANNEX_A_5_PASSPHRASE.into(), HashAlgorithm::Sha256);
551 let msg = builder.to_vec(&mut rng)?;
552
553 let mut msg = Message::from_bytes(&msg[..])?;
554 msg.verify_read(pri.public_key())?;
555 Ok(())
556 }
557}