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