summaryrefslogtreecommitdiffstats
path: root/openpgp/src/serialize/sexp.rs
diff options
context:
space:
mode:
authorJustus Winter <justus@sequoia-pgp.org>2019-06-02 23:03:44 +0200
committerJustus Winter <justus@sequoia-pgp.org>2019-06-06 16:41:42 +0200
commitf2e902f1b8065f0e2ab4e550a85edea18edff91c (patch)
treeb311f6e4bd81b85c45661527542db4e696f71f97 /openpgp/src/serialize/sexp.rs
parent55df865bb40b3349f911e351bf2b24cb43f9c054 (diff)
openpgp: New type representing s-expressions.
- *S-Expressions* as described in the internet draft [S-Expressions], are a way to communicate cryptographic primitives like keys, signatures, and ciphertexts between agents or implementations. [S-Expressions]: https://people.csail.mit.edu/rivest/Sexp.txt
Diffstat (limited to 'openpgp/src/serialize/sexp.rs')
-rw-r--r--openpgp/src/serialize/sexp.rs127
1 files changed, 127 insertions, 0 deletions
diff --git a/openpgp/src/serialize/sexp.rs b/openpgp/src/serialize/sexp.rs
new file mode 100644
index 00000000..808f3ce0
--- /dev/null
+++ b/openpgp/src/serialize/sexp.rs
@@ -0,0 +1,127 @@
+use Result;
+use serialize::{Serialize, SerializeInto, generic_serialize_into};
+
+use crypto::sexp::{Sexp, String_};
+
+impl Serialize for Sexp {
+ fn serialize(&self, o: &mut dyn std::io::Write) -> Result<()> {
+ match self {
+ Sexp::String(ref s) => s.serialize(o),
+ Sexp::List(ref l) => {
+ write!(o, "(")?;
+ for sexp in l {
+ sexp.serialize(o)?;
+ }
+ write!(o, ")")?;
+ Ok(())
+ },
+ }
+ }
+}
+
+impl SerializeInto for Sexp {
+ fn serialized_len(&self) -> usize {
+ match self {
+ Sexp::String(ref s) => s.serialized_len(),
+ Sexp::List(ref l) =>
+ 2 + l.iter().map(|s| s.serialized_len()).sum::<usize>(),
+ }
+ }
+
+ fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
+ generic_serialize_into(self, buf)
+ }
+}
+
+impl Serialize for String_ {
+ fn serialize(&self, o: &mut dyn std::io::Write) -> Result<()> {
+ if let Some(display) = self.display_hint() {
+ write!(o, "[{}:", display.len())?;
+ o.write_all(display)?;
+ write!(o, "]")?;
+ }
+ write!(o, "{}:", self.len())?;
+ o.write_all(self)?;
+ Ok(())
+ }
+}
+
+/// Computes the length of the size tag for a given string length.
+fn size_tag_len(len: usize) -> usize {
+ // Compute log10(self.len()).
+ let mut l = len;
+ let mut digits = 0;
+ while l > 0 {
+ l /= 10;
+ digits += 1;
+ }
+
+ std::cmp::max(1, digits) // 0 takes up 1 char, too.
+}
+
+impl SerializeInto for String_ {
+ fn serialized_len(&self) -> usize {
+ self.display_hint()
+ .map(|d| size_tag_len(d.len()) + 3 + d.len()).unwrap_or(0)
+ + size_tag_len(self.len()) + 1 + self.len()
+ }
+
+ fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
+ generic_serialize_into(self, buf)
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn sexp() {
+ assert_eq!(
+ &Sexp::List(vec![]).to_vec().unwrap(),
+ b"()");
+ assert_eq!(
+ &Sexp::List(vec![Sexp::String(b"hi"[..].into()),
+ Sexp::String(b"ho"[..].into()),
+ ]).to_vec().unwrap(),
+ b"(2:hi2:ho)");
+ assert_eq!(
+ &Sexp::List(vec![
+ Sexp::String(b"hi"[..].into()),
+ Sexp::String(String_::with_display_hint(b"ho".to_vec(),
+ b"fancy".to_vec())),
+ ]).to_vec().unwrap(),
+ b"(2:hi[5:fancy]2:ho)");
+ assert_eq!(
+ &Sexp::List(vec![
+ Sexp::String(b"hi"[..].into()),
+ Sexp::List(vec![
+ Sexp::String(b"ha"[..].into()),
+ Sexp::String(b"ho"[..].into()),
+ ]),
+ ]).to_vec().unwrap(),
+ b"(2:hi(2:ha2:ho))");
+ assert_eq!(
+ &Sexp::List(vec![
+ Sexp::String(b"sig-val"[..].into()),
+ Sexp::List(vec![
+ Sexp::String(b"rsa"[..].into()),
+ Sexp::List(vec![
+ Sexp::String(b"s"[..].into()),
+ Sexp::String(b"abc"[..].into()),
+ ]),
+ ]),
+ ]).to_vec().unwrap(),
+ b"(7:sig-val(3:rsa(1:s3:abc)))");
+ }
+
+ #[test]
+ fn string() {
+ assert_eq!(&String_::new(b"hi".to_vec()).to_vec().unwrap(),
+ b"2:hi");
+ assert_eq!(&String_::with_display_hint(b"hi".to_vec(),
+ b"fancy".to_vec())
+ .to_vec().unwrap(),
+ b"[5:fancy]2:hi");
+ }
+}