diff options
author | Justus Winter <justus@sequoia-pgp.org> | 2020-01-08 16:08:25 +0100 |
---|---|---|
committer | Justus Winter <justus@sequoia-pgp.org> | 2020-01-08 16:08:25 +0100 |
commit | b64d45cf84cb7baee3a88a12f1cbf52ad3fb74d3 (patch) | |
tree | 4712fed94e84a9a5f5706951d5d6f2c9919f1104 /openpgp/src/types/timestamp.rs | |
parent | ed0c5fde7ed786f0f3a99e2e2f2fa7239065be4b (diff) |
openpgp: Add convenience functions for masking timestamps.
- Timestamps can be used to identify people attending a common
event, like a key signing party. By reducing the resolution of
the time stamps, we can alleviate this problem.
- Fixes #216.
Diffstat (limited to 'openpgp/src/types/timestamp.rs')
-rw-r--r-- | openpgp/src/types/timestamp.rs | 83 |
1 files changed, 83 insertions, 0 deletions
diff --git a/openpgp/src/types/timestamp.rs b/openpgp/src/types/timestamp.rs index 318a6ba9..168625c4 100644 --- a/openpgp/src/types/timestamp.rs +++ b/openpgp/src/types/timestamp.rs @@ -56,6 +56,33 @@ impl Timestamp { SystemTime::now().try_into() .expect("representable for the next hundred years") } + + /// Rounds down to the given level of precision. + /// + /// This can be used to reduce the metadata leak resulting from + /// time stamps. For example, a group of people attending a key + /// signing event could be identified by comparing the time stamps + /// of resulting certifications. By rounding the creation time of + /// these signatures down, all of them, and others, fall into the + /// same bucket. + /// + /// The given level `p` determines the resulting resolution of + /// `2^p` seconds. The default is `21`, which results in a + /// resolution of 24 days, or roughly a month. `p` must be lower + /// than 32. + /// + /// See [`Duration::round_up`](struct.Duration.html#method.round_up). + pub fn round_down<P>(&self, precision: P) -> Result<Timestamp> + where P: Into<Option<u8>> + { + let p = precision.into().unwrap_or(21) as u32; + if p < 32 { + Ok(Self(self.0 & !((1 << p) - 1))) + } else { + Err(Error::InvalidArgument( + format!("Invalid precision {}", p)).into()) + } + } } impl Arbitrary for Timestamp { @@ -159,6 +186,37 @@ impl Duration { pub fn as_secs(self) -> u64 { self.0 as u64 } + + /// Rounds up to the given level of precision. + /// + /// If [`Timestamp::round_down`] is used to round the creation + /// timestamp of a key or signature down, then this function may + /// be used to round the corresponding expiration time up. This + /// ensures validity during the originally intended lifetime, + /// while avoiding the metadata leak associated with preserving + /// the originally intended expiration time. + /// + /// [`Timestamp::round_down`]: struct.Timestamp.html#method.round_down + /// + /// The given level `p` determines the resulting resolution of + /// `2^p` seconds. The default is `21`, which results in a + /// resolution of 24 days, or roughly a month. `p` must be lower + /// than 32. + pub fn round_up<P>(&self, precision: P) -> Result<Duration> + where P: Into<Option<u8>> + { + let p = precision.into().unwrap_or(21) as u32; + if p < 32 { + if let Some(sum) = self.0.checked_add((1 << p) - 1) { + Ok(Self(sum & !((1 << p) - 1))) + } else { + Ok(Self(std::u32::MAX)) + } + } else { + Err(Error::InvalidArgument( + format!("Invalid precision {}", p)).into()) + } + } } impl Arbitrary for Duration { @@ -166,3 +224,28 @@ impl Arbitrary for Duration { Duration(u32::arbitrary(g)) } } + +#[cfg(test)] +mod tests { + use super::*; + + quickcheck! { + fn timestamp_round_down(t: Timestamp) -> bool { + let u = t.round_down(None).unwrap(); + assert!(u <= t); + assert_eq!(u32::from(u) & 0b1_1111_1111_1111_1111, 0); + assert!(u32::from(t) - u32::from(u) < 2097152); + true + } + } + + quickcheck! { + fn duration_round_up(t: Duration) -> bool { + let u = t.round_up(None).unwrap(); + assert!(t <= u); + assert_eq!(u32::from(u) & 0b1_1111_1111_1111_1111, 0); + assert!(u32::from(u) - u32::from(t) < 2097152); + true + } + } +} |