Skip to main content

pgp/packet/signature/
ser.rs

1use std::io;
2
3use byteorder::{BigEndian, WriteBytesExt};
4use log::debug;
5
6use crate::{
7    errors::{bail, unimplemented_err, unsupported_err, Result},
8    packet::{
9        signature::{types::*, SignatureConfig},
10        SignatureVersionSpecific, Subpacket, SubpacketData, SubpacketType,
11    },
12    ser::Serialize,
13};
14
15impl Serialize for Signature {
16    fn to_writer<W: io::Write>(&self, writer: &mut W) -> Result<()> {
17        writer.write_u8(self.version().into())?;
18
19        match &self.inner {
20            InnerSignature::Known {
21                config,
22                signed_hash_value,
23                signature,
24                ..
25            } => match config.version() {
26                SignatureVersion::V2 | SignatureVersion::V3 => {
27                    config.to_writer_v3(writer)?;
28                    // signed hash value
29                    writer.write_all(signed_hash_value)?;
30                    // the actual cryptographic signature
31                    signature.to_writer(writer)?;
32                }
33                SignatureVersion::V4 | SignatureVersion::V6 => {
34                    config.to_writer_v4_v6(writer)?;
35
36                    // signed hash value
37                    writer.write_all(signed_hash_value)?;
38
39                    // salt, if v6
40                    if let SignatureVersionSpecific::V6 { salt } = &config.version_specific {
41                        debug!("writing salt {} bytes", salt.len());
42                        writer.write_u8(salt.len().try_into()?)?;
43                        writer.write_all(salt)?;
44                    }
45
46                    // the actual cryptographic signature
47                    signature.to_writer(writer)?;
48                }
49                SignatureVersion::V5 => {
50                    unsupported_err!("crate V5 signature")
51                }
52                _ => unreachable!(),
53            },
54            InnerSignature::Unknown { data, .. } => {
55                writer.write_all(data)?;
56            }
57        }
58        Ok(())
59    }
60
61    fn write_len(&self) -> usize {
62        let mut sum = 1;
63        match &self.inner {
64            InnerSignature::Known {
65                config,
66                signed_hash_value,
67                signature,
68            } => match config.version() {
69                SignatureVersion::V2 | SignatureVersion::V3 => {
70                    sum += config.write_len_v3();
71                    sum += signed_hash_value.len();
72                    sum += signature.write_len();
73                }
74                SignatureVersion::V4 | SignatureVersion::V6 => {
75                    sum += config.write_len_v4_v6();
76                    sum += signed_hash_value.len();
77
78                    if let SignatureVersionSpecific::V6 { salt } = &config.version_specific {
79                        sum += 1;
80                        sum += salt.len();
81                    }
82
83                    sum += signature.write_len();
84                }
85                SignatureVersion::V5 => panic!("v5 signature unsupported writer"),
86                _ => unreachable!(),
87            },
88            InnerSignature::Unknown { data, .. } => {
89                sum += data.len();
90            }
91        }
92        sum
93    }
94}
95
96impl Subpacket {
97    pub fn typ(&self) -> SubpacketType {
98        match &self.data {
99            SubpacketData::SignatureCreationTime(_) => SubpacketType::SignatureCreationTime,
100            SubpacketData::SignatureExpirationTime(_) => SubpacketType::SignatureExpirationTime,
101            SubpacketData::KeyExpirationTime(_) => SubpacketType::KeyExpirationTime,
102            SubpacketData::IssuerKeyId(_) => SubpacketType::IssuerKeyId,
103            SubpacketData::PreferredSymmetricAlgorithms(_) => {
104                SubpacketType::PreferredSymmetricAlgorithms
105            }
106            SubpacketData::PreferredHashAlgorithms(_) => SubpacketType::PreferredHashAlgorithms,
107            SubpacketData::PreferredCompressionAlgorithms(_) => {
108                SubpacketType::PreferredCompressionAlgorithms
109            }
110            SubpacketData::KeyServerPreferences(_) => SubpacketType::KeyServerPreferences,
111            SubpacketData::KeyFlags(_) => SubpacketType::KeyFlags,
112            SubpacketData::Features(_) => SubpacketType::Features,
113            SubpacketData::RevocationReason(_, _) => SubpacketType::RevocationReason,
114            SubpacketData::IsPrimary(_) => SubpacketType::PrimaryUserId,
115            SubpacketData::Revocable(_) => SubpacketType::Revocable,
116            SubpacketData::EmbeddedSignature(_) => SubpacketType::EmbeddedSignature,
117            SubpacketData::PreferredKeyServer(_) => SubpacketType::PreferredKeyServer,
118            SubpacketData::Notation(_) => SubpacketType::Notation,
119            SubpacketData::RevocationKey(_) => SubpacketType::RevocationKey,
120            SubpacketData::SignersUserID(_) => SubpacketType::SignersUserID,
121            SubpacketData::PolicyURI(_) => SubpacketType::PolicyURI,
122            SubpacketData::TrustSignature(_, _) => SubpacketType::TrustSignature,
123            SubpacketData::RegularExpression(_) => SubpacketType::RegularExpression,
124            SubpacketData::ExportableCertification(_) => SubpacketType::ExportableCertification,
125            SubpacketData::IssuerFingerprint(_) => SubpacketType::IssuerFingerprint,
126            SubpacketData::PreferredEncryptionModes(_) => SubpacketType::PreferredEncryptionModes,
127            SubpacketData::IntendedRecipientFingerprint(_) => {
128                SubpacketType::IntendedRecipientFingerprint
129            }
130            SubpacketData::PreferredAeadAlgorithms(_) => SubpacketType::PreferredAead,
131            SubpacketData::Experimental(n, _) => SubpacketType::Experimental(*n),
132            SubpacketData::Other(n, _) => SubpacketType::Other(*n),
133            SubpacketData::SignatureTarget(_, _, _) => SubpacketType::SignatureTarget,
134        }
135    }
136}
137
138impl Serialize for SubpacketData {
139    fn to_writer<W: io::Write>(&self, writer: &mut W) -> Result<()> {
140        debug!("writing subpacket: {self:?}");
141        match &self {
142            SubpacketData::SignatureCreationTime(t) => {
143                t.to_writer(writer)?;
144            }
145            SubpacketData::SignatureExpirationTime(d) => {
146                d.to_writer(writer)?;
147            }
148            SubpacketData::KeyExpirationTime(d) => {
149                d.to_writer(writer)?;
150            }
151            SubpacketData::IssuerKeyId(id) => {
152                writer.write_all(id.as_ref())?;
153            }
154            SubpacketData::PreferredSymmetricAlgorithms(algs) => {
155                writer.write_all(&algs.iter().map(|&alg| u8::from(alg)).collect::<Vec<_>>())?;
156            }
157            SubpacketData::PreferredHashAlgorithms(algs) => {
158                writer.write_all(&algs.iter().map(|&alg| alg.into()).collect::<Vec<_>>())?;
159            }
160            SubpacketData::PreferredCompressionAlgorithms(algs) => {
161                writer.write_all(&algs.iter().map(|&alg| u8::from(alg)).collect::<Vec<_>>())?;
162            }
163            SubpacketData::KeyServerPreferences(prefs) => {
164                writer.write_all(prefs)?;
165            }
166            SubpacketData::KeyFlags(flags) => {
167                flags.to_writer(writer)?;
168            }
169            SubpacketData::Features(features) => {
170                features.to_writer(writer)?;
171            }
172            SubpacketData::RevocationReason(code, reason) => {
173                writer.write_u8((*code).into())?;
174                writer.write_all(reason)?;
175            }
176            SubpacketData::IsPrimary(is_primary) => {
177                writer.write_u8((*is_primary).into())?;
178            }
179            SubpacketData::Revocable(is_revocable) => {
180                writer.write_u8((*is_revocable).into())?;
181            }
182            SubpacketData::EmbeddedSignature(inner_sig) => {
183                (*inner_sig).to_writer(writer)?;
184            }
185            SubpacketData::PreferredKeyServer(server) => {
186                writer.write_all(server.as_bytes())?;
187            }
188            SubpacketData::Notation(notation) => {
189                let is_readable = if notation.readable { 0x80 } else { 0 };
190                writer.write_all(&[is_readable, 0, 0, 0])?;
191
192                writer.write_u16::<BigEndian>(notation.name.len().try_into()?)?;
193
194                writer.write_u16::<BigEndian>(notation.value.len().try_into()?)?;
195
196                writer.write_all(&notation.name)?;
197                writer.write_all(&notation.value)?;
198            }
199            SubpacketData::RevocationKey(rev_key) => {
200                writer.write_u8(rev_key.class as u8)?;
201                writer.write_u8(rev_key.algorithm.into())?;
202                writer.write_all(&rev_key.fingerprint[..])?;
203            }
204            SubpacketData::SignersUserID(body) => {
205                writer.write_all(body.as_ref())?;
206            }
207            SubpacketData::PolicyURI(uri) => {
208                writer.write_all(uri.as_bytes())?;
209            }
210            SubpacketData::TrustSignature(depth, value) => {
211                writer.write_u8(*depth)?;
212                writer.write_u8(*value)?;
213            }
214            SubpacketData::RegularExpression(regexp) => {
215                writer.write_all(regexp)?;
216            }
217            SubpacketData::ExportableCertification(is_exportable) => {
218                writer.write_u8((*is_exportable).into())?;
219            }
220            SubpacketData::IssuerFingerprint(fp) => {
221                if let Some(version) = fp.version() {
222                    writer.write_u8(version.into())?;
223                    writer.write_all(fp.as_bytes())?;
224                } else {
225                    bail!("IssuerFingerprint: needs versioned fingerprint")
226                }
227            }
228            SubpacketData::PreferredEncryptionModes(algs) => {
229                writer.write_all(&algs.iter().map(|&alg| alg.into()).collect::<Vec<_>>())?;
230            }
231            SubpacketData::IntendedRecipientFingerprint(fp) => {
232                if let Some(version) = fp.version() {
233                    writer.write_u8(version.into())?;
234                    writer.write_all(fp.as_bytes())?;
235                } else {
236                    bail!("IntendedRecipientFingerprint: needs versioned fingerprint")
237                }
238            }
239            SubpacketData::PreferredAeadAlgorithms(algs) => {
240                writer.write_all(
241                    &algs
242                        .iter()
243                        .flat_map(|&(sym_alg, aead)| [sym_alg.into(), aead.into()])
244                        .collect::<Vec<_>>(),
245                )?;
246            }
247            SubpacketData::Experimental(_, body) => {
248                writer.write_all(body)?;
249            }
250            SubpacketData::Other(_, body) => {
251                writer.write_all(body)?;
252            }
253            SubpacketData::SignatureTarget(pub_alg, hash_alg, hash) => {
254                writer.write_u8((*pub_alg).into())?;
255                writer.write_u8((*hash_alg).into())?;
256                writer.write_all(hash)?;
257            }
258        }
259
260        Ok(())
261    }
262
263    fn write_len(&self) -> usize {
264        let len = match &self {
265            SubpacketData::SignatureCreationTime(d) => d.write_len(),
266            SubpacketData::SignatureExpirationTime(d) => d.write_len(),
267            SubpacketData::KeyExpirationTime(d) => d.write_len(),
268            SubpacketData::IssuerKeyId(_) => 8,
269            SubpacketData::PreferredSymmetricAlgorithms(algs) => algs.len(),
270            SubpacketData::PreferredHashAlgorithms(algs) => algs.len(),
271            SubpacketData::PreferredCompressionAlgorithms(algs) => algs.len(),
272            SubpacketData::KeyServerPreferences(prefs) => prefs.len(),
273            SubpacketData::KeyFlags(flags) => flags.write_len(),
274            SubpacketData::Features(features) => features.write_len(),
275
276            SubpacketData::RevocationReason(_, reason) => {
277                // 1 byte for revocation code + n for the reason
278                1 + reason.len()
279            }
280            SubpacketData::IsPrimary(_) => 1,
281            SubpacketData::Revocable(_) => 1,
282            SubpacketData::EmbeddedSignature(sig) => (*sig).write_len(),
283            SubpacketData::PreferredKeyServer(server) => server.chars().count(),
284            SubpacketData::Notation(n) => {
285                // 4 for the flags, 2 for the name length, 2 for the value length, m for the name, n for the value
286                4 + 2 + 2 + n.name.len() + n.value.len()
287            }
288            SubpacketData::RevocationKey(_) => 22,
289            SubpacketData::SignersUserID(body) => {
290                let bytes: &[u8] = body.as_ref();
291                bytes.len()
292            }
293            SubpacketData::PolicyURI(uri) => uri.len(),
294            SubpacketData::TrustSignature(_, _) => 2,
295            SubpacketData::RegularExpression(regexp) => regexp.len(),
296            SubpacketData::ExportableCertification(_) => 1,
297            SubpacketData::IssuerFingerprint(fp) => 1 + fp.len(),
298            SubpacketData::PreferredEncryptionModes(algs) => algs.len(),
299            SubpacketData::IntendedRecipientFingerprint(fp) => 1 + fp.len(),
300            SubpacketData::PreferredAeadAlgorithms(algs) => algs.len() * 2,
301            SubpacketData::Experimental(_, body) => body.len(),
302            SubpacketData::Other(_, body) => body.len(),
303            SubpacketData::SignatureTarget(_, _, hash) => 2 + hash.len(),
304        };
305
306        len
307    }
308}
309
310impl Serialize for Subpacket {
311    fn to_writer<W: io::Write>(&self, writer: &mut W) -> Result<()> {
312        self.len.to_writer(writer)?;
313        writer.write_u8(self.typ().as_u8(self.is_critical))?;
314        self.data.to_writer(writer)?;
315
316        Ok(())
317    }
318
319    fn write_len(&self) -> usize {
320        let mut sum = self.len.len();
321        sum += self.len.write_len();
322        sum
323    }
324}
325
326impl SignatureConfig {
327    /// Serializes a v2 or v3 signature.
328    fn to_writer_v3<W: io::Write>(&self, writer: &mut W) -> Result<()> {
329        writer.write_u8(0x05)?; // 1-octet length of the following hashed material; it MUST be 5
330        writer.write_u8(self.typ.into())?; // type
331
332        if let SignatureVersionSpecific::V2 {
333            created,
334            issuer_key_id: issuer,
335        }
336        | SignatureVersionSpecific::V3 {
337            created,
338            issuer_key_id: issuer,
339        } = &self.version_specific
340        {
341            created.to_writer(writer)?;
342            writer.write_all(issuer.as_ref())?;
343        } else {
344            bail!("expecting SignatureVersionSpecific::V3 for a v2/v3 signature")
345        }
346
347        writer.write_u8(self.pub_alg.into())?; // public algorithm
348        writer.write_u8(self.hash_alg.into())?; // hash algorithm
349
350        Ok(())
351    }
352
353    pub(super) fn write_len_v3(&self) -> usize {
354        let mut sum = 1 + 1;
355
356        if let SignatureVersionSpecific::V2 {
357            issuer_key_id: issuer,
358            created,
359            ..
360        }
361        | SignatureVersionSpecific::V3 {
362            issuer_key_id: issuer,
363            created,
364            ..
365        } = &self.version_specific
366        {
367            sum += created.write_len();
368            sum += issuer.as_ref().len();
369        } else {
370            panic!("expecting SignatureVersionSpecific::V3 for a v2/v3 signature")
371        }
372
373        sum += 1 + 1;
374
375        sum
376    }
377
378    /// Serializes a v4 or v6 signature.
379    fn to_writer_v4_v6<W: io::Write>(&self, writer: &mut W) -> Result<()> {
380        writer.write_u8(self.typ.into())?; // type
381
382        writer.write_u8(self.pub_alg.into())?; // public algorithm
383        writer.write_u8(self.hash_alg.into())?; // hash algorithm
384
385        // hashed subpackets
386        let hashed_sub_len = self.hashed_subpackets.write_len();
387        match self.version() {
388            SignatureVersion::V4 => writer.write_u16::<BigEndian>(hashed_sub_len.try_into()?)?,
389            SignatureVersion::V6 => writer.write_u32::<BigEndian>(hashed_sub_len.try_into()?)?,
390            v => unimplemented_err!("signature version {:?}", v),
391        }
392
393        for packet in &self.hashed_subpackets {
394            packet.to_writer(writer)?;
395        }
396
397        // unhashed subpackets
398        let unhashed_sub_len = self.unhashed_subpackets.write_len();
399
400        match self.version() {
401            SignatureVersion::V4 => writer.write_u16::<BigEndian>(unhashed_sub_len.try_into()?)?,
402            SignatureVersion::V6 => writer.write_u32::<BigEndian>(unhashed_sub_len.try_into()?)?,
403            v => unimplemented_err!("signature version {:?}", v),
404        }
405
406        for packet in &self.unhashed_subpackets {
407            packet.to_writer(writer)?;
408        }
409
410        Ok(())
411    }
412
413    pub(super) fn write_len_v4_v6(&self) -> usize {
414        let mut sum = 1 + 1 + 1;
415
416        // hashed subpackets
417        sum += self.hashed_subpackets.write_len();
418
419        match self.version() {
420            SignatureVersion::V4 => {
421                sum += 2;
422            }
423            SignatureVersion::V6 => {
424                sum += 4;
425            }
426            v => panic!("signature version {v:?}"),
427        }
428
429        // unhashed subpackets
430        sum += self.unhashed_subpackets.write_len();
431        match self.version() {
432            SignatureVersion::V4 => {
433                sum += 2;
434            }
435            SignatureVersion::V6 => {
436                sum += 4;
437            }
438            v => panic!("signature version {v:?}"),
439        }
440
441        sum
442    }
443}
444
445#[cfg(test)]
446mod tests {
447    #![allow(clippy::unwrap_used)]
448
449    use std::{
450        fs::File,
451        io::{BufReader, Read},
452        path::Path,
453    };
454
455    use super::*;
456    use crate::packet::{Packet, PacketParser};
457
458    fn test_roundtrip(name: &str) {
459        let f = BufReader::new(
460            std::fs::File::open(Path::new("./tests/openpgp/samplemsgs").join(name)).unwrap(),
461        );
462
463        let packets: Vec<Packet> = PacketParser::new(f).collect::<Result<_>>().unwrap();
464        let mut serialized = Vec::new();
465
466        for p in &packets {
467            if let Packet::Signature(_) = p {
468                p.to_writer(&mut serialized).unwrap();
469            } else {
470                panic!("unexpected packet: {p:?}");
471            };
472        }
473
474        let bytes = {
475            let mut f = File::open(Path::new("./tests/openpgp/samplemsgs").join(name)).unwrap();
476            let mut bytes = Vec::new();
477            f.read_to_end(&mut bytes).unwrap();
478            bytes
479        };
480
481        assert_eq!(bytes, serialized, "failed to roundtrip");
482    }
483
484    #[test]
485    fn packet_signature_roundtrip_openpgp_sig_1_key_1() {
486        test_roundtrip("sig-1-key-1.sig");
487    }
488
489    #[test]
490    fn packet_signature_roundtrip_openpgp_sig_1_key_2() {
491        test_roundtrip("sig-1-key-2.sig");
492    }
493
494    #[test]
495    fn packet_signature_roundtrip_openpgp_sig_2_keys_1() {
496        test_roundtrip("sig-2-keys-1.sig");
497    }
498
499    #[test]
500    fn packet_signature_roundtrip_openpgp_sig_2_keys_2() {
501        test_roundtrip("sig-2-keys-2.sig");
502    }
503
504    // Tries to roundtrip a signature containing a name + E-Mail with complicated multibyte unicode characters
505    #[test]
506    fn packet_signature_roundtrip_openpgp_with_unicode() {
507        test_roundtrip("unicode.sig");
508    }
509}