summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJustus Winter <justus@sequoia-pgp.org>2023-11-15 11:23:54 +0100
committerJustus Winter <justus@sequoia-pgp.org>2023-11-15 11:27:45 +0100
commitebcbb15cba6d9b668a789034955e458f6f7c8177 (patch)
tree38cea618dc354e9fa0027e37ed52c3370d6669b3
parentd025a3974c20bff15c3333472bfc83841920b7c0 (diff)
ipc: Track the offset of records in a Keybox file.
- Fixes #980.
-rw-r--r--ipc/src/keybox.rs97
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);