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 writer.write_all(signed_hash_value)?;
30 signature.to_writer(writer)?;
32 }
33 SignatureVersion::V4 | SignatureVersion::V6 => {
34 config.to_writer_v4_v6(writer)?;
35
36 writer.write_all(signed_hash_value)?;
38
39 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 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(¬ation.name)?;
197 writer.write_all(¬ation.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 + 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 + 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 fn to_writer_v3<W: io::Write>(&self, writer: &mut W) -> Result<()> {
329 writer.write_u8(0x05)?; writer.write_u8(self.typ.into())?; 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())?; writer.write_u8(self.hash_alg.into())?; 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 fn to_writer_v4_v6<W: io::Write>(&self, writer: &mut W) -> Result<()> {
380 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();
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 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 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 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 #[test]
506 fn packet_signature_roundtrip_openpgp_with_unicode() {
507 test_roundtrip("unicode.sig");
508 }
509}