diff --git a/Cargo.lock b/Cargo.lock index 15ecf0a9f..c5ee93f04 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -145,6 +145,12 @@ dependencies = [ "serde", ] +[[package]] +name = "bumpalo" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e8c087f005730276d1096a652e92a8bacee2e2472bcc9715a74d2bec38b5820" + [[package]] name = "byteorder" version = "1.3.4" @@ -350,6 +356,15 @@ version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6" +[[package]] +name = "js-sys" +version = "0.3.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca059e81d9486668f12d455a4ea6daa600bd408134cd17e3d3fb5a32d1f016f8" +dependencies = [ + "wasm-bindgen", +] + [[package]] name = "kv" version = "0.10.0" @@ -398,6 +413,15 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "log" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b" +dependencies = [ + "cfg-if 0.1.10", +] + [[package]] name = "memchr" version = "2.3.4" @@ -445,6 +469,12 @@ version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d3b63360ec3cb337817c2dbd47ab4a0f170d285d8e5a2064600f3def1402397" +[[package]] +name = "once_cell" +version = "1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13bd41f508810a131401606d54ac32a467c97172d74ba7662562ebba5ad07fa0" + [[package]] name = "pkg-config" version = "0.3.19" @@ -766,6 +796,21 @@ dependencies = [ "winapi", ] +[[package]] +name = "ring" +version = "0.16.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5911690c9b773bab7e657471afc207f3827b249a657241327e3544d79bcabdd" +dependencies = [ + "cc", + "libc", + "once_cell", + "spin", + "untrusted", + "web-sys", + "winapi", +] + [[package]] name = "rmp" version = "0.8.9" @@ -854,6 +899,12 @@ dependencies = [ "serde", ] +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + [[package]] name = "strsim" version = "0.8.0" @@ -896,6 +947,7 @@ dependencies = [ "kv", "lmdb-rkv", "proptest", + "ring", "serde", "serde_json", "tempdir", @@ -1025,6 +1077,12 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + [[package]] name = "uuid" version = "0.8.1" @@ -1062,6 +1120,70 @@ version = "0.10.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" +[[package]] +name = "wasm-bindgen" +version = "0.2.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ac64ead5ea5f05873d7c12b545865ca2b8d28adfc50a49b84770a3a97265d42" +dependencies = [ + "cfg-if 0.1.10", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f22b422e2a757c35a73774860af8e112bff612ce6cb604224e8e47641a9e4f68" +dependencies = [ + "bumpalo", + "lazy_static", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b13312a745c08c469f0b292dd2fcd6411dba5f7160f593da6ef69b64e407038" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f249f06ef7ee334cc3b8ff031bfc11ec99d00f34d86da7498396dc1e3b1498fe" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d649a3145108d7d3fbcde896a468d1bd636791823c9921135218ad89be08307" + +[[package]] +name = "web-sys" +version = "0.3.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bf6ef87ad7ae8008e15a355ce696bed26012b7caa21605188cfd8214ab51e2d" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "winapi" version = "0.3.9" diff --git a/taskchampion/Cargo.toml b/taskchampion/Cargo.toml index 1f6681454..509a7db8b 100644 --- a/taskchampion/Cargo.toml +++ b/taskchampion/Cargo.toml @@ -12,6 +12,7 @@ chrono = { version = "0.4.10", features = ["serde"] } failure = {version = "0.1.5", features = ["derive"] } kv = {version = "0.10.0", features = ["msgpack-value"]} lmdb-rkv = {version = "0.12.3"} +ring = { version = "0.16.17", features = ["std"] } [dev-dependencies] proptest = "0.9.4" diff --git a/taskchampion/src/server/mod.rs b/taskchampion/src/server/mod.rs index e73c7f74a..9a6214425 100644 --- a/taskchampion/src/server/mod.rs +++ b/taskchampion/src/server/mod.rs @@ -1,6 +1,7 @@ #[cfg(test)] pub(crate) mod test; +mod signing; mod types; pub use types::{Blob, Server, VersionAdd}; diff --git a/taskchampion/src/server/signing.rs b/taskchampion/src/server/signing.rs new file mode 100644 index 000000000..e1e49501a --- /dev/null +++ b/taskchampion/src/server/signing.rs @@ -0,0 +1,87 @@ +#![allow(dead_code)] // TODO: temporary until this module is used +//! This is a general wrapper around an asymmetric-key signature system. + +use failure::Fallible; +use ring::{ + rand, + signature::{Ed25519KeyPair, KeyPair, Signature, UnparsedPublicKey, ED25519}, +}; + +type PublicKey = Vec; +type PrivateKey = Vec; + +/// Generate a pair of (public, private) key material (in fact the private key is a keypair) +pub fn new_keypair() -> Fallible<(PublicKey, PrivateKey)> { + let rng = rand::SystemRandom::new(); + let key_pkcs8 = Ed25519KeyPair::generate_pkcs8(&rng)?; + let key_pair = Ed25519KeyPair::from_pkcs8(key_pkcs8.as_ref())?; + let pub_key = key_pair.public_key(); + Ok(( + pub_key.as_ref().to_vec() as PublicKey, + key_pkcs8.as_ref().to_vec() as PrivateKey, + )) +} + +pub struct Signer { + key_pair: Ed25519KeyPair, +} + +impl Signer { + /// Create a new signer, given a pkcs#8 v2 document containing the keypair. + fn new(priv_key: PrivateKey) -> Fallible { + Ok(Self { + key_pair: Ed25519KeyPair::from_pkcs8(&priv_key)?, + }) + } + + pub fn sign>(&self, message: B) -> Fallible { + Ok(self.key_pair.sign(message.as_ref())) + } +} + +pub struct Verifier { + pub_key: PublicKey, +} + +impl Verifier { + fn new(pub_key: PublicKey) -> Fallible { + Ok(Self { pub_key }) + } + + pub fn verify, B2: AsRef<[u8]>>( + &self, + message: B1, + signature: B2, + ) -> Fallible<()> { + let pub_key = UnparsedPublicKey::new(&ED25519, &self.pub_key); + Ok(pub_key.verify(message.as_ref(), signature.as_ref())?) + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_verify_ok() -> Fallible<()> { + let (public, private) = new_keypair()?; + let signer = Signer::new(private)?; + let verifier = Verifier::new(public)?; + + let message = b"Hello, world"; + let signature = signer.sign(message)?; + verifier.verify(message, signature) + } + + #[test] + fn test_verify_bad_message() -> Fallible<()> { + let (public, private) = new_keypair()?; + let signer = Signer::new(private)?; + let verifier = Verifier::new(public)?; + + let message = b"Hello, world"; + let signature = signer.sign(message)?; + assert!(verifier.verify(b"Hello, cruel world", signature).is_err()); + Ok(()) + } +}