diff options
author | Justus Winter <justus@sequoia-pgp.org> | 2023-11-15 11:23:54 +0100 |
---|---|---|
committer | Justus Winter <justus@sequoia-pgp.org> | 2023-11-15 11:27:45 +0100 |
commit | ebcbb15cba6d9b668a789034955e458f6f7c8177 (patch) | |
tree | 38cea618dc354e9fa0027e37ed52c3370d6669b3 | |
parent | d025a3974c20bff15c3333472bfc83841920b7c0 (diff) |
ipc: Track the offset of records in a Keybox file.
- Fixes #980.
-rw-r--r-- | ipc/src/keybox.rs | 97 |
1 files changed, 73 insertions, 24 deletions
diff --git a/ipc/src/keybox.rs b/ipc/src/keybox.rs index 9a6cb725..26c898ec 100644 --- a/ipc/src/keybox.rs +++ b/ipc/src/keybox.rs @@ -3,6 +3,8 @@ //! This implementation is based on keybox files created by GnuPG 2.2.23 and //! the way they are handled by the `kbxutil` program from that version of GnuPG. +use anyhow::Context; + use buffered_reader::BufferedReader; use openpgp::cert::Cert; @@ -46,6 +48,9 @@ use std::io::Read; /// # } /// ``` pub struct Keybox<'a> { + /// Offset into the Keybox file. + offset: usize, + reader: Box<dyn BufferedReader<()> + 'a>, } @@ -61,7 +66,12 @@ impl<'a> Keybox<'a> { let len = u32::from_be_bytes(input[..4].try_into().unwrap()) as usize; let content = self.reader.data_consume_hard(len)?; - let kbx_record = KeyboxRecord::new((&content[..len]).to_vec())?; + + // The length includes the four byte length itself. + let offset = self.offset; + self.offset += len; + + let kbx_record = KeyboxRecord::new(offset, (&content[..len]).to_vec())?; Ok(kbx_record) } } @@ -70,6 +80,7 @@ impl<'a> Parse<'a, Keybox<'a>> for Keybox<'a> { fn from_reader<R: 'a + Read + Send + Sync>(reader: R) -> Result<Self> { let bio = buffered_reader::Generic::new(reader, None); Ok(Keybox { + offset: 0, reader: Box::new(bio), }) } @@ -132,7 +143,17 @@ impl KeyboxRecord { KeyboxRecord::Header(h) => &h.bytes, KeyboxRecord::OpenPGP(o) => &o.bytes, KeyboxRecord::X509(x) => &x.bytes, - KeyboxRecord::Unknown(bytes) => bytes, + KeyboxRecord::Unknown(_, bytes) => bytes, + } + } + + /// Returns the offset in the Keybox file. + pub fn offset(&self) -> usize { + match self { + KeyboxRecord::Header(h) => h.offset(), + KeyboxRecord::OpenPGP(o) => o.offset(), + KeyboxRecord::X509(x) => x.offset(), + KeyboxRecord::Unknown(offset, _bytes) => *offset, } } @@ -151,7 +172,7 @@ impl KeyboxRecord { self.bytes()[5] } - fn new(bytes: Vec<u8>) -> Result<Self> { + fn new(offset: usize, bytes: Vec<u8>) -> Result<Self> { if bytes.len() < 6 { return Err(Error::NotEnoughData( "A keybox record requires at least 6 bytes.".to_string(), @@ -159,16 +180,16 @@ impl KeyboxRecord { .into()); } - let record = KeyboxRecord::Unknown(bytes.clone()); + let record = KeyboxRecord::Unknown(offset, bytes.clone()); match record.typ() { KeyboxRecordType::Header => { - HeaderRecord::new(bytes).map(KeyboxRecord::Header) + HeaderRecord::new(offset, bytes).map(KeyboxRecord::Header) } KeyboxRecordType::OpenPGP => { - OpenPGPRecordV1::new(&record).map(KeyboxRecord::OpenPGP) + OpenPGPRecordV1::new(offset, &record).map(KeyboxRecord::OpenPGP) } KeyboxRecordType::X509 => { - X509Record::new(bytes).map(KeyboxRecord::X509) + X509Record::new(offset, bytes).map(KeyboxRecord::X509) } KeyboxRecordType::Unknown(_) => Ok(record), } @@ -191,7 +212,7 @@ pub enum KeyboxRecord { /// X.509 record. X509(X509Record), /// Catchall. - Unknown(Vec<u8>), + Unknown(usize, Vec<u8>), } /// Keybox header record. @@ -199,13 +220,21 @@ pub enum KeyboxRecord { /// Contains general metadata of the keybox. #[derive(PartialEq, Eq, Debug)] pub struct HeaderRecord { + /// Offset into the Keybox file. + offset: usize, + bytes: Vec<u8>, } impl HeaderRecord { - fn new(bytes: Vec<u8>) -> Result<Self> { + fn new(offset: usize, bytes: Vec<u8>) -> Result<Self> { //TODO at least check length? - Ok(Self { bytes }) + Ok(Self { offset, bytes }) + } + + /// Returns the offset in the Keybox file. + pub fn offset(&self) -> usize { + self.offset } /// Flags field. @@ -238,24 +267,35 @@ impl HeaderRecord { /// Unhandled, only exists for completeness. #[derive(PartialEq, Eq, Debug)] pub struct X509Record { + /// Offset into the Keybox file. + offset: usize, + bytes: Vec<u8>, } impl X509Record { - fn new(bytes: Vec<u8>) -> Result<Self> { + fn new(offset: usize, bytes: Vec<u8>) -> Result<Self> { //TODO at least check length? - Ok(Self { bytes }) + Ok(Self { offset, bytes }) + } + + /// Returns the offset in the Keybox file. + pub fn offset(&self) -> usize { + self.offset } } /// Keybox OpenPGP record #[derive(PartialEq, Eq, Debug)] pub struct OpenPGPRecordV1 { + /// Offset into the Keybox file. + offset: usize, + bytes: Vec<u8>, } impl OpenPGPRecordV1 { - fn new(record: &KeyboxRecord) -> Result<Self> { + fn new(offset: usize, record: &KeyboxRecord) -> Result<Self> { // Check type and version if record.typ() != KeyboxRecordType::OpenPGP || record.version() != 1 { return Err( @@ -273,6 +313,7 @@ impl OpenPGPRecordV1 { }; let record = OpenPGPRecordV1 { + offset, bytes: record.bytes().to_vec(), }; @@ -284,6 +325,11 @@ impl OpenPGPRecordV1 { Ok(record) } + /// Returns the offset in the Keybox file. + pub fn offset(&self) -> usize { + self.offset + } + /// Flags field. // Semantics unknown. pub fn flags(&self) -> [u8; 2] { @@ -350,6 +396,9 @@ impl OpenPGPRecordV1 { pub fn cert(&self) -> Result<Cert> { let cert_data = &self.data_section()?; Cert::from_bytes(cert_data) + .with_context( + || format!("Parsing keybox record at offset {}", + self.offset())) } } @@ -390,26 +439,26 @@ mod tests { #[test] fn keybox_record() -> Result<()> { let header_bytes = crate::tests::keybox("header_sample"); - let header_kbx = KeyboxRecord::new(header_bytes.to_vec())?; + let header_kbx = KeyboxRecord::new(0, header_bytes.to_vec())?; assert_eq!(header_kbx.typ(), header_bytes[4].into()); let openpgp_bytes = crate::tests::keybox("testy_openpgp"); - let openpgp_kbx = KeyboxRecord::new(openpgp_bytes.to_vec())?; + let openpgp_kbx = KeyboxRecord::new(0, openpgp_bytes.to_vec())?; assert_eq!(openpgp_kbx.typ(), openpgp_bytes[4].into()); let x509_bytes = crate::tests::keybox("testy_x509"); - let x509_kbx = KeyboxRecord::new(x509_bytes.to_vec())?; + let x509_kbx = KeyboxRecord::new(0, x509_bytes.to_vec())?; assert_eq!(x509_kbx.typ(), x509_bytes[4].into()); let too_short = &[1u8; 5]; - assert!(KeyboxRecord::new(too_short.to_vec()).is_err()); + assert!(KeyboxRecord::new(0, too_short.to_vec()).is_err()); Ok(()) } #[test] fn cert_from_openpgp_record() -> Result<()> { let openpgp_bytes = crate::tests::keybox("testy_openpgp"); - let kbx_record = KeyboxRecord::new(openpgp_bytes.to_vec())?; + let kbx_record = KeyboxRecord::new(0, openpgp_bytes.to_vec())?; let openpgp_record = match kbx_record { KeyboxRecord::OpenPGP(r) => r, _ => unreachable!(), @@ -433,7 +482,7 @@ mod tests { #[test] fn openpgp_record() -> Result<()> { let openpgp_bytes = crate::tests::keybox("testy_openpgp"); - let kbx_record = KeyboxRecord::new(openpgp_bytes.to_vec())?; + let kbx_record = KeyboxRecord::new(0, openpgp_bytes.to_vec())?; assert_eq!(kbx_record.length_field(), 1428u32); assert_eq!(kbx_record.typ(), KeyboxRecordType::OpenPGP); assert_eq!(kbx_record.version(), 1u8); @@ -462,24 +511,24 @@ mod tests { #[test] fn openpgp_errors() -> Result<()> { let openpgp_too_short = [0u8, 7u8, 1u8, 1u8, 2u8, 1u8, 1u8]; - assert!(KeyboxRecord::new(openpgp_too_short.to_vec()).is_err()); + assert!(KeyboxRecord::new(0, openpgp_too_short.to_vec()).is_err()); let openpgp_unknown_version = [0u8, 7u8, 1u8, 1u8, 2u8, 7u8, 1u8]; - assert!(KeyboxRecord::new(openpgp_unknown_version.to_vec()).is_err()); + assert!(KeyboxRecord::new(0, openpgp_unknown_version.to_vec()).is_err()); let mut openpgp_wrong_checksum = crate::tests::keybox("testy_openpgp").to_vec(); // set last byte (= last byte of checksum) to 0 if let Some(last) = openpgp_wrong_checksum.last_mut() { *last = 0u8; }; - assert!(KeyboxRecord::new(openpgp_wrong_checksum.to_vec()).is_err()); + assert!(KeyboxRecord::new(0, openpgp_wrong_checksum.to_vec()).is_err()); Ok(()) } #[test] fn header_record() -> Result<()> { let header_bytes = crate::tests::keybox("header_sample"); - let kbx_record = KeyboxRecord::new(header_bytes.to_vec())?; + let kbx_record = KeyboxRecord::new(0, header_bytes.to_vec())?; assert_eq!(kbx_record.length_field(), 32u32); assert_eq!(kbx_record.typ(), KeyboxRecordType::Header); assert_eq!(kbx_record.version(), 1u8); @@ -497,7 +546,7 @@ mod tests { #[test] fn x509_record() -> Result<()> { let x509_bytes = crate::tests::keybox("testy_x509"); - let kbx_record = KeyboxRecord::new(x509_bytes.to_vec())?; + let kbx_record = KeyboxRecord::new(0, x509_bytes.to_vec())?; assert_eq!(kbx_record.length_field(), 1704u32); assert_eq!(kbx_record.typ(), KeyboxRecordType::X509); assert_eq!(kbx_record.version(), 1u8); |