diff options
-rw-r--r-- | Cargo.lock | 3 | ||||
-rw-r--r-- | ipc/Cargo.toml | 3 | ||||
-rw-r--r-- | ipc/build.rs | 36 | ||||
-rw-r--r-- | ipc/src/gnupg.rs | 2 | ||||
-rw-r--r-- | ipc/src/lib.rs | 26 | ||||
-rw-r--r-- | ipc/src/sexp.rs (renamed from openpgp/src/crypto/sexp.rs) | 52 | ||||
-rw-r--r-- | ipc/src/sexp/parse/grammar.lalrpop (renamed from openpgp/src/parse/sexp/grammar.lalrpop) | 4 | ||||
-rw-r--r-- | ipc/src/sexp/parse/lexer.rs (renamed from openpgp/src/parse/sexp/lexer.rs) | 0 | ||||
-rw-r--r-- | ipc/src/sexp/parse/mod.rs (renamed from openpgp/src/parse/sexp/mod.rs) | 13 | ||||
-rw-r--r-- | ipc/src/sexp/serialize.rs (renamed from openpgp/src/serialize/sexp.rs) | 54 | ||||
-rw-r--r-- | ipc/src/tests.rs | 41 | ||||
-rw-r--r-- | ipc/tests/data/sexp/dsa-signature.sexp (renamed from openpgp/tests/data/sexp/dsa-signature.sexp) | 0 | ||||
-rw-r--r-- | ipc/tests/data/sexp/ecdsa-signature.sexp (renamed from openpgp/tests/data/sexp/ecdsa-signature.sexp) | 0 | ||||
-rw-r--r-- | ipc/tests/data/sexp/eddsa-signature.sexp (renamed from openpgp/tests/data/sexp/eddsa-signature.sexp) | 0 | ||||
-rw-r--r-- | ipc/tests/data/sexp/rsa-signature.sexp (renamed from openpgp/tests/data/sexp/rsa-signature.sexp) | 0 | ||||
-rw-r--r-- | openpgp/src/crypto/mod.rs | 1 | ||||
-rw-r--r-- | openpgp/src/parse.rs | 1 | ||||
-rw-r--r-- | openpgp/src/serialize.rs | 1 |
18 files changed, 193 insertions, 44 deletions
@@ -1778,6 +1778,7 @@ name = "sequoia-ipc" version = "0.18.0" dependencies = [ "anyhow 1.0.32 (registry+https://github.com/rust-lang/crates.io-index)", + "buffered-reader 0.18.0", "capnp-rpc 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", "clap 2.33.1 (registry+https://github.com/rust-lang/crates.io-index)", "ctor 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1785,9 +1786,11 @@ dependencies = [ "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", "lalrpop 0.17.2 (registry+https://github.com/rust-lang/crates.io-index)", "lalrpop-util 0.17.2 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.74 (registry+https://github.com/rust-lang/crates.io-index)", "memsec 0.5.7 (registry+https://github.com/rust-lang/crates.io-index)", "parity-tokio-ipc 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "quickcheck 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", "sequoia-core 0.18.0", "sequoia-openpgp 0.18.0", diff --git a/ipc/Cargo.toml b/ipc/Cargo.toml index b21fcfd1..ef5ed0ea 100644 --- a/ipc/Cargo.toml +++ b/ipc/Cargo.toml @@ -24,10 +24,12 @@ sequoia-openpgp = { path = "../openpgp", version = "0.18" } sequoia-core = { path = "../core", version = "0.18" } anyhow = "1" +buffered-reader = { path = "../buffered-reader", version = "0.18", default-features = false } capnp-rpc = "0.10" fs2 = "0.4.2" futures = "0.1" lalrpop-util = "0.17" +lazy_static = "1.3" libc = "0.2.33" memsec = "0.5.6" rand = { version = "0.7", default-features = false } @@ -52,3 +54,4 @@ lalrpop = "0.17" [dev-dependencies] clap = "2.32.0" +quickcheck = { version = "0.9", default-features = false } diff --git a/ipc/build.rs b/ipc/build.rs index 23c7d3f8..5c8a9ffb 100644 --- a/ipc/build.rs +++ b/ipc/build.rs @@ -1,5 +1,41 @@ +use std::env; +use std::fs; +use std::io::{self, Write}; +use std::path::PathBuf; extern crate lalrpop; fn main() { lalrpop::process_root().unwrap(); + include_test_data().unwrap(); +} + +/// Builds the index of the test data for use with the `::tests` +/// module. +fn include_test_data() -> io::Result<()> { + let cwd = env::current_dir()?; + let mut sink = fs::File::create( + PathBuf::from(env::var_os("OUT_DIR").unwrap()) + .join("tests.index.rs.inc")).unwrap(); + + writeln!(&mut sink, "{{")?; + let mut dirs = vec![PathBuf::from("tests/data")]; + while let Some(dir) = dirs.pop() { + println!("rerun-if-changed={}", dir.to_str().unwrap()); + for entry in fs::read_dir(dir).unwrap() { + let entry = entry?; + let path = entry.path(); + if path.is_file() { + writeln!( + &mut sink, " add!({:?}, {:?});", + path.components().skip(2) + .map(|c| c.as_os_str().to_str().expect("valid UTF-8")) + .collect::<Vec<_>>().join("/"), + cwd.join(path))?; + } else if path.is_dir() { + dirs.push(path.clone()); + } + } + } + writeln!(&mut sink, "}}")?; + Ok(()) } diff --git a/ipc/src/gnupg.rs b/ipc/src/gnupg.rs index 4b565553..b81e0927 100644 --- a/ipc/src/gnupg.rs +++ b/ipc/src/gnupg.rs @@ -14,13 +14,13 @@ use sequoia_openpgp as openpgp; use openpgp::types::HashAlgorithm; use openpgp::fmt::hex; use openpgp::crypto; -use openpgp::crypto::sexp::Sexp; use openpgp::packet::prelude::*; use openpgp::parse::Parse; use openpgp::serialize::Serialize; use crate::Result; use crate::assuan; +use crate::sexp::Sexp; /// A GnuPG context. #[derive(Debug)] diff --git a/ipc/src/lib.rs b/ipc/src/lib.rs index 7e6a62ab..f0d2c2b6 100644 --- a/ipc/src/lib.rs +++ b/ipc/src/lib.rs @@ -65,9 +65,35 @@ use std::thread; use sequoia_openpgp as openpgp; use sequoia_core as core; +// Turns an `if let` into an expression so that it is possible to do +// things like: +// +// ```rust,nocompile +// if destructures_to(Foo::Bar(_) = value) +// || destructures_to(Foo::Bam(_) = value) { ... } +// ``` +// TODO: Replace with `std::matches!` once MSRV is bumped to 1.42. +#[cfg(test)] +macro_rules! destructures_to { + ( $error: pat = $expr:expr ) => { + { + let x = $expr; + if let $error = x { + true + } else { + false + } + } + }; +} + #[macro_use] mod trace; pub mod assuan; pub mod gnupg; +pub mod sexp; + +#[cfg(test)] +mod tests; macro_rules! platform { { unix => { $($unix:tt)* }, windows => { $($windows:tt)* } } => { diff --git a/openpgp/src/crypto/sexp.rs b/ipc/src/sexp.rs index 96709651..38429557 100644 --- a/openpgp/src/crypto/sexp.rs +++ b/ipc/src/sexp.rs @@ -10,14 +10,18 @@ use std::convert::TryFrom; use std::fmt; use std::ops::Deref; -#[cfg(any(test, feature = "quickcheck"))] +#[cfg(test)] use quickcheck::{Arbitrary, Gen}; -use crate::crypto::{self, mpi, SessionKey}; -use crate::crypto::mem::Protected; +use sequoia_openpgp as openpgp; +use openpgp::crypto::{mpi, SessionKey}; +use openpgp::crypto::mem::Protected; + +use openpgp::Error; +use openpgp::Result; -use crate::Error; -use crate::Result; +mod parse; +mod serialize; /// An *S-Expression*. /// @@ -47,14 +51,14 @@ impl Sexp { /// command. `padding` must be set according to the status /// messages sent. pub fn finish_decryption<R>(&self, - recipient: &crate::packet::Key< - crate::packet::key::PublicParts, R>, + recipient: &openpgp::packet::Key< + openpgp::packet::key::PublicParts, R>, ciphertext: &mpi::Ciphertext, padding: bool) -> Result<SessionKey> - where R: crate::packet::key::KeyRole + where R: openpgp::packet::key::KeyRole { - use crate::crypto::mpi::PublicKey; + use openpgp::crypto::mpi::PublicKey; let not_a_session_key = || -> anyhow::Error { Error::MalformedMPI( format!("Not a session key: {:?}", self)).into() @@ -114,7 +118,7 @@ impl Sexp { // The session key is not padded. Currently, this // happens if the session key is decrypted using // scdaemon. - assert!(! padding); + assert!(! padding); // XXX: Don't assert that. Ok(s.to_vec().into()) }, @@ -127,7 +131,7 @@ impl Sexp { // XXX: Erase shared point from s. // Now finish the decryption. - crypto::ecdh::decrypt_shared(recipient, &S, ciphertext) + openpgp::crypto::ecdh::decrypt_shared(recipient, &S, ciphertext) }, _ => @@ -250,7 +254,7 @@ impl TryFrom<&mpi::Ciphertext> for Sexp { /// The resulting expression is suitable for gpg-agent's `INQUIRE /// CIPHERTEXT` inquiry. fn try_from(ciphertext: &mpi::Ciphertext) -> Result<Self> { - use crate::crypto::mpi::Ciphertext::*; + use openpgp::crypto::mpi::Ciphertext::*; match ciphertext { RSA { ref c } => Ok(Sexp::List(vec![ @@ -400,10 +404,10 @@ impl Arbitrary for String_ { #[cfg(test)] mod tests { use super::*; - use crate::parse::Parse; - use crate::serialize::Serialize; + use openpgp::parse::Parse; + use openpgp::serialize::Serialize; - quickcheck! { + quickcheck::quickcheck! { fn roundtrip(s: Sexp) -> bool { let mut buf = Vec::new(); s.serialize(&mut buf).unwrap(); @@ -415,18 +419,18 @@ mod tests { #[test] fn to_signature() { - use crate::crypto::mpi::Signature::*; - assert_match!(DSA { .. } = Sexp::from_bytes( + use openpgp::crypto::mpi::Signature::*; + assert!(destructures_to!(DSA { .. } = Sexp::from_bytes( crate::tests::file("sexp/dsa-signature.sexp")).unwrap() - .to_signature().unwrap()); - assert_match!(ECDSA { .. } = Sexp::from_bytes( + .to_signature().unwrap())); + assert!(destructures_to!(ECDSA { .. } = Sexp::from_bytes( crate::tests::file("sexp/ecdsa-signature.sexp")).unwrap() - .to_signature().unwrap()); - assert_match!(EdDSA { .. } = Sexp::from_bytes( + .to_signature().unwrap())); + assert!(destructures_to!(EdDSA { .. } = Sexp::from_bytes( crate::tests::file("sexp/eddsa-signature.sexp")).unwrap() - .to_signature().unwrap()); - assert_match!(RSA { .. } = Sexp::from_bytes( + .to_signature().unwrap())); + assert!(destructures_to!(RSA { .. } = Sexp::from_bytes( crate::tests::file("sexp/rsa-signature.sexp")).unwrap() - .to_signature().unwrap()); + .to_signature().unwrap())); } } diff --git a/openpgp/src/parse/sexp/grammar.lalrpop b/ipc/src/sexp/parse/grammar.lalrpop index 8712e7d2..9a880361 100644 --- a/openpgp/src/parse/sexp/grammar.lalrpop +++ b/ipc/src/sexp/parse/grammar.lalrpop @@ -5,8 +5,8 @@ // // [S-Expressions]: https://people.csail.mit.edu/rivest/Sexp.txt -use crate::parse::sexp::lexer::{self, LexicalError}; -use crate::crypto::sexp::{Sexp, String_}; +use crate::sexp::parse::lexer::{self, LexicalError}; +use crate::sexp::{Sexp, String_}; grammar<'input>; diff --git a/openpgp/src/parse/sexp/lexer.rs b/ipc/src/sexp/parse/lexer.rs index 0a570475..0a570475 100644 --- a/openpgp/src/parse/sexp/lexer.rs +++ b/ipc/src/sexp/parse/lexer.rs diff --git a/openpgp/src/parse/sexp/mod.rs b/ipc/src/sexp/parse/mod.rs index 92bfd591..560f3764 100644 --- a/openpgp/src/parse/sexp/mod.rs +++ b/ipc/src/sexp/parse/mod.rs @@ -11,17 +11,18 @@ use std::path::Path; use buffered_reader::{self, BufferedReader}; use lalrpop_util::{lalrpop_mod, ParseError}; +use sequoia_openpgp as openpgp; +use openpgp::parse::Parse; -use crate::Error; +use openpgp::Error; use crate::Result; -use crate::crypto::sexp::Sexp; -use crate::parse::Parse; +use crate::sexp::Sexp; mod lexer; use self::lexer::Lexer; // Load the generated code. -lalrpop_mod!(#[allow(missing_docs, unused_parens)] grammar, "/parse/sexp/grammar.rs"); +lalrpop_mod!(#[allow(missing_docs, unused_parens)] grammar, "/sexp/parse/grammar.rs"); impl<'a> Parse<'a, Sexp> for Sexp { fn from_reader<R: 'a + Read>(reader: R) -> Result<Sexp> { @@ -79,8 +80,8 @@ impl Sexp { #[cfg(test)] mod tests { - use crate::crypto::sexp::{Sexp, String_}; - use crate::parse::Parse; + use crate::sexp::{Sexp, String_}; + use sequoia_openpgp::parse::Parse; #[test] fn basics() { diff --git a/openpgp/src/serialize/sexp.rs b/ipc/src/sexp/serialize.rs index 8c072ee0..a360c01e 100644 --- a/openpgp/src/serialize/sexp.rs +++ b/ipc/src/sexp/serialize.rs @@ -1,13 +1,18 @@ -use crate::Result; -use crate::serialize::{ - Marshal, - MarshalInto, - generic_serialize_into, +use std::io; + +use sequoia_openpgp as openpgp; +use openpgp::{ + Result, + Error, + serialize::{ + Marshal, + MarshalInto, + }, }; -use crate::crypto::sexp::{Sexp, String_}; +use crate::sexp::{Sexp, String_}; -impl crate::serialize::Serialize for Sexp {} +impl openpgp::serialize::Serialize for Sexp {} impl Marshal for Sexp { fn serialize(&self, o: &mut dyn std::io::Write) -> Result<()> { @@ -25,7 +30,7 @@ impl Marshal for Sexp { } } -impl crate::serialize::SerializeInto for Sexp {} +impl openpgp::serialize::SerializeInto for Sexp {} impl MarshalInto for Sexp { fn serialized_len(&self) -> usize { @@ -79,6 +84,39 @@ impl MarshalInto for String_ { } } +/// Provides a generic implementation for SerializeInto::serialize_into. +/// +/// For now, we express SerializeInto using Serialize. In the future, +/// we may provide implementations not relying on Serialize for a +/// no_std configuration of this crate. +fn generic_serialize_into(o: &dyn Marshal, serialized_len: usize, + buf: &mut [u8]) + -> Result<usize> { + let buf_len = buf.len(); + let mut cursor = ::std::io::Cursor::new(buf); + match o.serialize(&mut cursor) { + Ok(_) => (), + Err(e) => { + let short_write = + if let Some(ioe) = e.downcast_ref::<io::Error>() { + ioe.kind() == io::ErrorKind::WriteZero + } else { + false + }; + return if short_write { + assert!(buf_len < serialized_len, + "o.serialized_len() underestimated the required space"); + Err(Error::InvalidArgument( + format!("Invalid buffer size, expected {}, got {}", + serialized_len, buf_len)).into()) + } else { + Err(e) + } + } + }; + Ok(cursor.position() as usize) +} + #[cfg(test)] mod tests { use super::*; diff --git a/ipc/src/tests.rs b/ipc/src/tests.rs new file mode 100644 index 00000000..3a827f92 --- /dev/null +++ b/ipc/src/tests.rs @@ -0,0 +1,41 @@ +//! Test data for Sequoia. +//! +//! This module includes the test data from `ipc/tests/data` in a +//! structured way. + +use std::collections::BTreeMap; + +/// Returns the content of the given file below `ipc/tests/data`. +pub fn file(name: &str) -> &'static [u8] { + lazy_static::lazy_static! { + static ref FILES: BTreeMap<&'static str, &'static [u8]> = { + let mut m: BTreeMap<&'static str, &'static [u8]> = + Default::default(); + + macro_rules! add { + ( $key: expr, $path: expr ) => { + m.insert($key, include_bytes!($path)) + } + } + include!(concat!(env!("OUT_DIR"), "/tests.index.rs.inc")); + + // Sanity checks. + assert!(m.contains_key("sexp/rsa-signature.sexp")); + m + }; + } + + FILES.get(name).unwrap_or_else(|| panic!("No such file {:?}", name)) +} + +/// Returns the content of the given file below `ipc/tests/data/keys`. +#[allow(dead_code)] +pub fn key(name: &str) -> &'static [u8] { + file(&format!("keys/{}", name)) +} + +/// Returns the content of the given file below `ipc/tests/data/messages`. +#[allow(dead_code)] +pub fn message(name: &str) -> &'static [u8] { + file(&format!("messages/{}", name)) +} diff --git a/openpgp/tests/data/sexp/dsa-signature.sexp b/ipc/tests/data/sexp/dsa-signature.sexp index 8585774f..8585774f 100644 --- a/openpgp/tests/data/sexp/dsa-signature.sexp +++ b/ipc/tests/data/sexp/dsa-signature.sexp diff --git a/openpgp/tests/data/sexp/ecdsa-signature.sexp b/ipc/tests/data/sexp/ecdsa-signature.sexp index 03f0743d..03f0743d 100644 --- a/openpgp/tests/data/sexp/ecdsa-signature.sexp +++ b/ipc/tests/data/sexp/ecdsa-signature.sexp diff --git a/openpgp/tests/data/sexp/eddsa-signature.sexp b/ipc/tests/data/sexp/eddsa-signature.sexp index 7a67e2a0..7a67e2a0 100644 --- a/openpgp/tests/data/sexp/eddsa-signature.sexp +++ b/ipc/tests/data/sexp/eddsa-signature.sexp diff --git a/openpgp/tests/data/sexp/rsa-signature.sexp b/ipc/tests/data/sexp/rsa-signature.sexp index dc7438c0..dc7438c0 100644 --- a/openpgp/tests/data/sexp/rsa-signature.sexp +++ b/ipc/tests/data/sexp/rsa-signature.sexp diff --git a/openpgp/src/crypto/mod.rs b/openpgp/src/crypto/mod.rs index 710dc5df..75e8fe9b 100644 --- a/openpgp/src/crypto/mod.rs +++ b/openpgp/src/crypto/mod.rs @@ -35,7 +35,6 @@ pub mod mem; pub mod mpi; mod s2k; pub use s2k::S2K; -pub mod sexp; pub(crate) mod symmetric; /// Holds a session key. diff --git a/openpgp/src/parse.rs b/openpgp/src/parse.rs index 8524dbc5..e9fbcc91 100644 --- a/openpgp/src/parse.rs +++ b/openpgp/src/parse.rs @@ -258,7 +258,6 @@ pub use self::packet_parser_builder::{Dearmor, PacketParserBuilder}; pub mod map; mod mpis; -mod sexp; pub mod stream; // Whether to trace execution by default (on stderr). diff --git a/openpgp/src/serialize.rs b/openpgp/src/serialize.rs index fb80a85d..87c12cd6 100644 --- a/openpgp/src/serialize.rs +++ b/openpgp/src/serialize.rs @@ -140,7 +140,6 @@ use std::convert::{TryFrom, TryInto}; use super::*; -mod sexp; mod cert; pub use self::cert::TSK; mod cert_armored; |