1use std::io::Read;
2
3use byteorder::{BigEndian, ByteOrder};
4use chrono::{DateTime, Utc};
5use log::debug;
6use rand::{CryptoRng, Rng};
7
8use crate::crypto::hash::{HashAlgorithm, Hasher};
9use crate::crypto::public_key::PublicKeyAlgorithm;
10use crate::errors::Result;
11use crate::packet::{
12 Signature, SignatureType, SignatureVersion, Subpacket, SubpacketData, SubpacketType,
13};
14use crate::ser::Serialize;
15use crate::types::{Fingerprint, KeyId, KeyVersion, PublicKeyTrait, SecretKeyTrait, Tag};
16
17#[derive(Clone, PartialEq, Eq, Debug)]
18pub struct SignatureConfig {
19 pub typ: SignatureType,
20 pub pub_alg: PublicKeyAlgorithm,
21 pub hash_alg: HashAlgorithm,
22
23 pub unhashed_subpackets: Vec<Subpacket>,
24 pub hashed_subpackets: Vec<Subpacket>,
25
26 pub version_specific: SignatureVersionSpecific,
27}
28
29#[derive(Clone, PartialEq, Eq, Debug)]
30pub enum SignatureVersionSpecific {
31 V2 {
32 created: DateTime<Utc>,
33 issuer: KeyId,
34 },
35 V3 {
36 created: DateTime<Utc>,
37 issuer: KeyId,
38 },
39 V4,
40 V6 {
41 salt: Vec<u8>,
42 },
43}
44
45impl From<&SignatureVersionSpecific> for SignatureVersion {
46 fn from(value: &SignatureVersionSpecific) -> Self {
47 match value {
48 SignatureVersionSpecific::V2 { .. } => SignatureVersion::V2,
49 SignatureVersionSpecific::V3 { .. } => SignatureVersion::V3,
50 SignatureVersionSpecific::V4 => SignatureVersion::V4,
51 SignatureVersionSpecific::V6 { .. } => SignatureVersion::V6,
52 }
53 }
54}
55
56impl SignatureConfig {
57 pub fn v2(
61 typ: SignatureType,
62 pub_alg: PublicKeyAlgorithm,
63 hash_alg: HashAlgorithm,
64 created: DateTime<Utc>,
65 issuer: KeyId,
66 ) -> Self {
67 Self {
68 typ,
69 pub_alg,
70 hash_alg,
71 hashed_subpackets: Vec::new(),
72 unhashed_subpackets: Vec::new(),
73 version_specific: SignatureVersionSpecific::V2 { created, issuer },
74 }
75 }
76
77 pub fn v3(
81 typ: SignatureType,
82 pub_alg: PublicKeyAlgorithm,
83 hash_alg: HashAlgorithm,
84 created: DateTime<Utc>,
85 issuer: KeyId,
86 ) -> Self {
87 Self {
88 typ,
89 pub_alg,
90 hash_alg,
91 hashed_subpackets: Vec::new(),
92 unhashed_subpackets: Vec::new(),
93 version_specific: SignatureVersionSpecific::V3 { created, issuer },
94 }
95 }
96
97 pub fn v4(typ: SignatureType, pub_alg: PublicKeyAlgorithm, hash_alg: HashAlgorithm) -> Self {
102 Self {
103 typ,
104 pub_alg,
105 hash_alg,
106 unhashed_subpackets: vec![],
107 hashed_subpackets: vec![],
108 version_specific: SignatureVersionSpecific::V4,
109 }
110 }
111
112 fn v6_salt_for<R: CryptoRng + Rng>(mut rng: R, hash_alg: HashAlgorithm) -> Result<Vec<u8>> {
115 let Some(salt_len) = hash_alg.salt_len() else {
116 bail!("Unknown v6 signature salt length for hash algorithm {hash_alg:?}");
117 };
118
119 let mut salt = vec![0; salt_len];
120 rng.fill_bytes(&mut salt);
121
122 Ok(salt)
123 }
124
125 pub fn v6<R: CryptoRng + Rng>(
130 rng: R,
131 typ: SignatureType,
132 pub_alg: PublicKeyAlgorithm,
133 hash_alg: HashAlgorithm,
134 ) -> Result<Self> {
135 Ok(Self::v6_with_salt(
136 typ,
137 pub_alg,
138 hash_alg,
139 Self::v6_salt_for(rng, hash_alg)?,
140 ))
141 }
142
143 pub fn v6_with_salt(
147 typ: SignatureType,
148 pub_alg: PublicKeyAlgorithm,
149 hash_alg: HashAlgorithm,
150 salt: Vec<u8>,
151 ) -> Self {
152 Self {
153 typ,
154 pub_alg,
155 hash_alg,
156 unhashed_subpackets: Vec::new(),
157 hashed_subpackets: Vec::new(),
158 version_specific: SignatureVersionSpecific::V6 { salt },
159 }
160 }
161
162 pub fn version(&self) -> SignatureVersion {
163 (&self.version_specific).into()
164 }
165
166 pub fn sign<F, R>(self, key: &impl SecretKeyTrait, key_pw: F, data: R) -> Result<Signature>
168 where
169 F: FnOnce() -> String,
170 R: Read,
171 {
172 ensure!(
173 (self.version() == SignatureVersion::V4 && key.version() == KeyVersion::V4)
174 || (self.version() == SignatureVersion::V6 && key.version() == KeyVersion::V6),
175 "signature version {:?} not allowed for signer key version {:?}",
176 self.version(),
177 key.version()
178 );
179
180 let mut hasher = self.hash_alg.new_hasher()?;
181
182 if let SignatureVersionSpecific::V6 { salt } = &self.version_specific {
183 hasher.update(salt.as_ref())
184 }
185
186 self.hash_data_to_sign(&mut *hasher, data)?;
187 let len = self.hash_signature_data(&mut hasher)?;
188 hasher.update(&self.trailer(len)?);
189
190 let hash = &hasher.finish()[..];
191
192 let signed_hash_value = [hash[0], hash[1]];
193 let signature = key.create_signature(key_pw, self.hash_alg, hash)?;
194
195 Ok(Signature::from_config(self, signed_hash_value, signature))
196 }
197
198 pub fn sign_certification<F>(
200 self,
201 key: &impl SecretKeyTrait,
202 key_pw: F,
203 tag: Tag,
204 id: &impl Serialize,
205 ) -> Result<Signature>
206 where
207 F: FnOnce() -> String,
208 {
209 self.sign_certification_third_party(key, key_pw, key, tag, id)
210 }
211
212 pub fn sign_certification_third_party<F>(
214 self,
215 signer: &impl SecretKeyTrait,
216 signer_pw: F,
217 signee: &impl PublicKeyTrait,
218 tag: Tag,
219 id: &impl Serialize,
220 ) -> Result<Signature>
221 where
222 F: FnOnce() -> String,
223 {
224 ensure!(
225 (self.version() == SignatureVersion::V4 && signer.version() == KeyVersion::V4)
226 || (self.version() == SignatureVersion::V6 && signer.version() == KeyVersion::V6),
227 "signature version {:?} not allowed for signer key version {:?}",
228 self.version(),
229 signer.version()
230 );
231 ensure!(
232 self.is_certification(),
233 "can not sign non certification as certification"
234 );
235
236 debug!("signing certification {:#?}", self.typ);
237
238 let mut hasher = self.hash_alg.new_hasher()?;
239
240 if let SignatureVersionSpecific::V6 { salt } = &self.version_specific {
241 hasher.update(salt.as_ref())
242 }
243
244 signee.serialize_for_hashing(&mut hasher)?;
245
246 let mut packet_buf = Vec::new();
247 id.to_writer(&mut packet_buf)?;
248
249 match self.version() {
250 SignatureVersion::V2 | SignatureVersion::V3 => {
251 }
253 SignatureVersion::V4 | SignatureVersion::V6 => {
254 let prefix = match tag {
255 Tag::UserId => 0xB4,
256 Tag::UserAttribute => 0xD1,
257 _ => bail!("invalid tag for certification signature: {:?}", tag),
258 };
259
260 let mut prefix_buf = [prefix, 0u8, 0u8, 0u8, 0u8];
261 BigEndian::write_u32(&mut prefix_buf[1..], packet_buf.len().try_into()?);
262
263 hasher.update(&prefix_buf);
265 }
266 SignatureVersion::V5 => {
267 bail!("v5 signature unsupported sign tps")
268 }
269 SignatureVersion::Other(version) => {
270 bail!("unsupported signature version {}", version)
271 }
272 }
273
274 hasher.update(&packet_buf);
276
277 let len = self.hash_signature_data(&mut hasher)?;
278 hasher.update(&self.trailer(len)?);
279
280 let hash = &hasher.finish()[..];
281
282 let signed_hash_value = [hash[0], hash[1]];
283 let signature = signer.create_signature(signer_pw, self.hash_alg, hash)?;
284
285 Ok(Signature::from_config(self, signed_hash_value, signature))
286 }
287
288 pub fn sign_key_binding<F>(
290 self,
291 signing_key: &impl SecretKeyTrait,
292 key_pw: F,
293 key: &impl PublicKeyTrait,
294 ) -> Result<Signature>
295 where
296 F: FnOnce() -> String,
297 {
298 ensure!(
299 (self.version() == SignatureVersion::V4 && signing_key.version() == KeyVersion::V4)
300 || (self.version() == SignatureVersion::V6
301 && signing_key.version() == KeyVersion::V6),
302 "signature version {:?} not allowed for signer key version {:?}",
303 self.version(),
304 signing_key.version()
305 );
306 debug!(
307 "signing key binding: {:#?} - {:#?} - {:#?}",
308 self, signing_key, key
309 );
310
311 let mut hasher = self.hash_alg.new_hasher()?;
312
313 if let SignatureVersionSpecific::V6 { salt } = &self.version_specific {
314 hasher.update(salt.as_ref())
315 }
316
317 signing_key.serialize_for_hashing(&mut hasher)?;
319
320 key.serialize_for_hashing(&mut hasher)?;
322
323 let len = self.hash_signature_data(&mut hasher)?;
324 hasher.update(&self.trailer(len)?);
325
326 let hash = &hasher.finish()[..];
327 let signed_hash_value = [hash[0], hash[1]];
328 let signature = signing_key.create_signature(key_pw, self.hash_alg, hash)?;
329
330 Ok(Signature::from_config(self, signed_hash_value, signature))
331 }
332
333 pub fn sign_key<F>(
335 self,
336 signing_key: &impl SecretKeyTrait,
337 key_pw: F,
338 key: &impl PublicKeyTrait,
339 ) -> Result<Signature>
340 where
341 F: FnOnce() -> String,
342 {
343 ensure!(
344 (self.version() == SignatureVersion::V4 && signing_key.version() == KeyVersion::V4)
345 || (self.version() == SignatureVersion::V6
346 && signing_key.version() == KeyVersion::V6),
347 "signature version {:?} not allowed for signer key version {:?}",
348 self.version(),
349 signing_key.version()
350 );
351 debug!("signing key (revocation): {:#?} - {:#?}", self, key);
352
353 let mut hasher = self.hash_alg.new_hasher()?;
354
355 if let SignatureVersionSpecific::V6 { salt } = &self.version_specific {
356 hasher.update(salt.as_ref())
357 }
358
359 key.serialize_for_hashing(&mut hasher)?;
360
361 let len = self.hash_signature_data(&mut hasher)?;
362 hasher.update(&self.trailer(len)?);
363
364 let hash = &hasher.finish()[..];
365 let signed_hash_value = [hash[0], hash[1]];
366 let signature = signing_key.create_signature(key_pw, self.hash_alg, hash)?;
367
368 Ok(Signature::from_config(self, signed_hash_value, signature))
369 }
370
371 pub fn typ(&self) -> SignatureType {
373 self.typ
374 }
375
376 pub fn hash_signature_data(&self, hasher: &mut dyn std::io::Write) -> Result<usize> {
378 match self.version() {
379 SignatureVersion::V2 | SignatureVersion::V3 => {
380 let created = {
381 if let SignatureVersionSpecific::V2 { created, .. }
382 | SignatureVersionSpecific::V3 { created, .. } = self.version_specific
383 {
384 created
385 } else {
386 bail!("must exist for a v2/3 signature")
387 }
388 };
389
390 let mut buf = [0u8; 5];
391 buf[0] = self.typ.into();
392 BigEndian::write_u32(&mut buf[1..], created.timestamp().try_into()?);
393
394 hasher.write_all(&buf)?;
395
396 Ok(0)
398 }
399 SignatureVersion::V4 | SignatureVersion::V6 => {
400 let mut res = vec![
403 self.version().into(),
405 self.typ.into(),
407 self.pub_alg.into(),
409 self.hash_alg.into(),
411 ];
412
413 let mut hashed_subpackets = Vec::new();
415 for packet in &self.hashed_subpackets {
416 debug!("hashing {:#?}", packet);
417
418 if packet.is_critical && matches!(packet.typ(), SubpacketType::Other(_)) {
424 bail!("Unknown critical subpacket {:?}", packet);
431 }
432
433 if let SubpacketData::IssuerFingerprint(fp) = &packet.data {
438 match (self.version(), fp.version()) {
439 (SignatureVersion::V6, Some(KeyVersion::V6)) => {},
440 (SignatureVersion::V4, Some(KeyVersion::V4)) => {},
441 _ => bail!("IntendedRecipientFingerprint {:?} doesn't match signature version {:?}", fp, self.version())
442 }
443 }
444
445 packet.to_writer(&mut hashed_subpackets)?;
446 }
447
448 if self.version() == SignatureVersion::V4 {
450 res.extend(u16::try_from(hashed_subpackets.len())?.to_be_bytes());
451 } else if self.version() == SignatureVersion::V6 {
452 res.extend(u32::try_from(hashed_subpackets.len())?.to_be_bytes());
453 }
454
455 res.extend(hashed_subpackets);
456
457 hasher.write_all(&res)?;
458
459 Ok(res.len())
460 }
461 SignatureVersion::V5 => {
462 bail!("v5 signature unsupported hash data")
463 }
464 SignatureVersion::Other(version) => {
465 bail!("unsupported signature version {}", version)
466 }
467 }
468 }
469
470 pub fn hash_data_to_sign<R>(&self, hasher: &mut dyn Hasher, mut data: R) -> Result<usize>
471 where
472 R: Read,
473 {
474 match self.typ {
475 SignatureType::Text |
476 SignatureType::Binary => {
478 Ok(std::io::copy(&mut data, hasher)? as usize)
479 }
480 SignatureType::Timestamp |
481 SignatureType::Standalone => {
482 let mut val = [0u8;1];
483 data.read_exact(&mut val[..])?;
484 hasher.update(&val[..]);
485 Ok(1)
486 }
487 SignatureType::CertGeneric
488 | SignatureType::CertPersona
489 | SignatureType::CertCasual
490 | SignatureType::CertPositive
491 | SignatureType::CertRevocation => {
492 unimplemented_err!("{:?}", self.typ);
493 }
494 SignatureType::SubkeyBinding
495 | SignatureType::SubkeyRevocation
496 | SignatureType::KeyBinding
497 | SignatureType::Key => {
498 unimplemented_err!("{:?}", self.typ);
499 }
500 SignatureType::KeyRevocation => unimplemented_err!("KeyRevocation"),
501 SignatureType::ThirdParty => unimplemented_err!("signing ThirdParty"),
502
503 SignatureType::Other(id) => unimplemented_err!("Other ({})", id),
504 }
505 }
506
507 pub fn trailer(&self, len: usize) -> Result<Vec<u8>> {
508 match self.version() {
509 SignatureVersion::V2 | SignatureVersion::V3 => {
510 Ok(Vec::new())
512 }
513 SignatureVersion::V4 | SignatureVersion::V6 => {
514 let mut trailer = vec![self.version().into(), 0xFF, 0, 0, 0, 0];
515 BigEndian::write_u32(&mut trailer[2..], len.try_into()?);
516 Ok(trailer)
517 }
518 SignatureVersion::V5 => {
519 bail!("v5 signature unsupported")
520 }
521 SignatureVersion::Other(version) => {
522 bail!("unsupported signature version {}", version)
523 }
524 }
525 }
526
527 #[deprecated(
530 note = "Usually only hashed_subpackets should be used. unhashed_subpackets are only safe and useful to access in rare circumstances. When they are needed, unhashed_subpackets should be explicitly called."
531 )]
532 pub fn subpackets(&self) -> impl Iterator<Item = &Subpacket> {
533 self.hashed_subpackets().chain(self.unhashed_subpackets())
534 }
535
536 pub fn hashed_subpackets(&self) -> impl Iterator<Item = &Subpacket> {
538 self.hashed_subpackets.iter()
539 }
540
541 pub fn unhashed_subpackets(&self) -> impl Iterator<Item = &Subpacket> {
543 self.unhashed_subpackets.iter()
544 }
545
546 pub fn is_certification(&self) -> bool {
548 matches!(
549 self.typ,
550 SignatureType::CertGeneric
551 | SignatureType::CertPersona
552 | SignatureType::CertCasual
553 | SignatureType::CertPositive
554 | SignatureType::CertRevocation
555 )
556 }
557
558 pub fn created(&self) -> Option<&DateTime<Utc>> {
567 if let SignatureVersionSpecific::V2 { created, .. }
568 | SignatureVersionSpecific::V3 { created, .. } = &self.version_specific
569 {
570 return Some(created);
571 }
572
573 self.hashed_subpackets().find_map(|p| match p.data {
574 SubpacketData::SignatureCreationTime(ref d) => Some(d),
575 _ => None,
576 })
577 }
578
579 pub fn issuer(&self) -> Vec<&KeyId> {
587 if let SignatureVersionSpecific::V2 { issuer, .. }
589 | SignatureVersionSpecific::V3 { issuer, .. } = &self.version_specific
590 {
591 return vec![issuer];
592 }
593
594 self.hashed_subpackets()
601 .chain(self.unhashed_subpackets())
602 .filter_map(|sp| match sp.data {
603 SubpacketData::Issuer(ref id) => Some(id),
604 _ => None,
605 })
606 .collect()
607 }
608
609 pub fn issuer_fingerprint(&self) -> Vec<&Fingerprint> {
617 self.hashed_subpackets()
618 .chain(self.unhashed_subpackets())
619 .filter_map(|sp| match &sp.data {
620 SubpacketData::IssuerFingerprint(fp) => Some(fp),
621 _ => None,
622 })
623 .collect()
624 }
625}