diff --git a/src/functions.rs b/src/functions.rs index df681a48..b96fcb5b 100644 --- a/src/functions.rs +++ b/src/functions.rs @@ -7,7 +7,7 @@ use ring::digest::{digest, SHA256}; use serde_json::{from_str, from_value, map::Map, to_string, to_value, Value}; use crate::{ - keys::KeyPair, + keys::{KeyPair, PublicKeyMap}, signatures::SignatureMap, split_id, verification::{Ed25519Verifier, Verified, Verifier}, @@ -95,7 +95,7 @@ static REFERENCE_HASH_FIELDS_TO_REMOVE: &[&str] = &["age_ts", "signatures", "uns /// let mut value = serde_json::from_str("{}").unwrap(); /// /// // Sign the JSON with the key pair. -/// assert!(ruma_signatures::sign_json("example.com", &key_pair, &mut value).is_ok()); +/// assert!(ruma_signatures::sign_json("domain", &key_pair, &mut value).is_ok()); /// ``` /// /// This will modify the JSON from an empty object to a structure like this: @@ -103,7 +103,7 @@ static REFERENCE_HASH_FIELDS_TO_REMOVE: &[&str] = &["age_ts", "signatures", "uns /// ```json /// { /// "signatures": { -/// "example.com": { +/// "domain": { /// "ed25519:1": "K8280/U9SSy9IVtjBuVeLr+HpOB4BQFWbg+UZaADMtTdGYI7Geitb76LTrr5QV/7Xg4ahLwYGYZzuHGZKM5ZAQ" /// } /// } @@ -209,23 +209,23 @@ pub fn canonical_json(value: &Value) -> Result { /// let value = serde_json::from_str( /// r#"{ /// "signatures": { -/// "example.com": { +/// "domain": { /// "ed25519:1": "K8280/U9SSy9IVtjBuVeLr+HpOB4BQFWbg+UZaADMtTdGYI7Geitb76LTrr5QV/7Xg4ahLwYGYZzuHGZKM5ZAQ" /// } /// } /// }"# /// ).unwrap(); /// -/// // Create the `SignatureMap` that will inform `verify_json` which signatures to verify. -/// let mut signature_set = HashMap::new(); -/// signature_set.insert("ed25519:1".to_string(), PUBLIC_KEY.to_string()); +/// // Create the `PublicKeyMap` that will inform `verify_json` which signatures to verify. +/// let mut public_key_set = HashMap::new(); +/// public_key_set.insert("ed25519:1".to_string(), PUBLIC_KEY.to_string()); /// let mut public_key_map = HashMap::new(); -/// public_key_map.insert("example.com".to_string(), signature_set); +/// public_key_map.insert("domain".to_string(), public_key_set); /// /// // Verify at least one signature for each entity in `public_key_map`. /// assert!(ruma_signatures::verify_json(&public_key_map, &value).is_ok()); /// ``` -pub fn verify_json(public_key_map: &SignatureMap, value: &Value) -> Result<(), Error> { +pub fn verify_json(public_key_map: &PublicKeyMap, value: &Value) -> Result<(), Error> { let map = match value { Value::Object(ref map) => map, _ => return Err(Error::new("JSON value must be a JSON object")), @@ -431,7 +431,7 @@ pub fn reference_hash(value: &Value) -> Result { /// ).unwrap(); /// /// // Hash and sign the JSON with the key pair. -/// assert!(ruma_signatures::hash_and_sign_event("example.com", &key_pair, &mut value).is_ok()); +/// assert!(ruma_signatures::hash_and_sign_event("domain", &key_pair, &mut value).is_ok()); /// ``` /// /// This will modify the JSON from the structure shown to a structure like this: @@ -549,18 +549,16 @@ where /// }"# /// ).unwrap(); /// -/// // Create a map from key ID to public key. -/// let mut example_server_keys = HashMap::new(); -/// example_server_keys.insert("ed25519:1".to_string(), PUBLIC_KEY.to_string()); -/// -/// // Insert the public keys into a map keyed by entity ID. +/// // Create the `PublicKeyMap` that will inform `verify_json` which signatures to verify. +/// let mut public_key_set = HashMap::new(); +/// public_key_set.insert("ed25519:1".to_string(), PUBLIC_KEY.to_string()); /// let mut public_key_map = HashMap::new(); -/// public_key_map.insert("domain".to_string(), example_server_keys); +/// public_key_map.insert("domain".to_string(), public_key_set); /// /// // Verify at least one signature for each entity in `public_key_map`. /// assert!(ruma_signatures::verify_event(&public_key_map, &value).is_ok()); /// ``` -pub fn verify_event(public_key_map: &SignatureMap, value: &Value) -> Result { +pub fn verify_event(public_key_map: &PublicKeyMap, value: &Value) -> Result { let redacted = redact(value)?; let map = match redacted { diff --git a/src/keys.rs b/src/keys.rs index 0fcd374d..8afaf11c 100644 --- a/src/keys.rs +++ b/src/keys.rs @@ -1,6 +1,9 @@ //! Public and private key pairs. -use std::fmt::{Debug, Formatter, Result as FmtResult}; +use std::{ + collections::HashMap, + fmt::{Debug, Formatter, Result as FmtResult}, +}; use ring::signature::Ed25519KeyPair as RingEd25519KeyPair; use untrusted::Input; @@ -87,3 +90,13 @@ impl Debug for Ed25519KeyPair { .finish() } } + +/// A map from entity names to sets of public keys for that entity. +/// +/// "Entity" is generally a homeserver, e.g. "example.com". +pub type PublicKeyMap = HashMap; + +/// A set of public keys for a single homeserver. +/// +/// This is represented as a map from key ID to Base64-encoded signature. +pub type PublicKeySet = HashMap; diff --git a/src/lib.rs b/src/lib.rs index 7b097a80..76ebad09 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -104,8 +104,8 @@ pub use functions::{ canonical_json, content_hash, hash_and_sign_event, redact, reference_hash, sign_json, verify_event, verify_json, }; -pub use keys::{Ed25519KeyPair, KeyPair}; -pub use signatures::{Signature, SignatureMap, SignatureSet}; +pub use keys::{Ed25519KeyPair, KeyPair, PublicKeyMap, PublicKeySet}; +pub use signatures::Signature; mod functions; mod keys; @@ -350,23 +350,23 @@ mod test { let mut value = from_str("{}").unwrap(); - sign_json("example.com", &key_pair, &mut value).unwrap(); + sign_json("domain", &key_pair, &mut value).unwrap(); assert_eq!( to_string(&value).unwrap(), - r#"{"signatures":{"example.com":{"ed25519:1":"K8280/U9SSy9IVtjBuVeLr+HpOB4BQFWbg+UZaADMtTdGYI7Geitb76LTrr5QV/7Xg4ahLwYGYZzuHGZKM5ZAQ"}}}"# + r#"{"signatures":{"domain":{"ed25519:1":"K8280/U9SSy9IVtjBuVeLr+HpOB4BQFWbg+UZaADMtTdGYI7Geitb76LTrr5QV/7Xg4ahLwYGYZzuHGZKM5ZAQ"}}}"# ); } #[test] fn verify_empty_json() { - let value = from_str(r#"{"signatures":{"example.com":{"ed25519:1":"K8280/U9SSy9IVtjBuVeLr+HpOB4BQFWbg+UZaADMtTdGYI7Geitb76LTrr5QV/7Xg4ahLwYGYZzuHGZKM5ZAQ"}}}"#).unwrap(); + let value = from_str(r#"{"signatures":{"domain":{"ed25519:1":"K8280/U9SSy9IVtjBuVeLr+HpOB4BQFWbg+UZaADMtTdGYI7Geitb76LTrr5QV/7Xg4ahLwYGYZzuHGZKM5ZAQ"}}}"#).unwrap(); let mut signature_set = HashMap::new(); signature_set.insert("ed25519:1".to_string(), PUBLIC_KEY.to_string()); let mut public_key_map = HashMap::new(); - public_key_map.insert("example.com".to_string(), signature_set); + public_key_map.insert("domain".to_string(), signature_set); assert!(verify_json(&public_key_map, &value).is_ok()); } @@ -407,39 +407,39 @@ mod test { }; let mut alpha_value = to_value(alpha).expect("alpha should serialize"); - sign_json("example.com", &key_pair, &mut alpha_value).unwrap(); + sign_json("domain", &key_pair, &mut alpha_value).unwrap(); assert_eq!( to_string(&alpha_value).unwrap(), - r#"{"one":1,"signatures":{"example.com":{"ed25519:1":"KqmLSbO39/Bzb0QIYE82zqLwsA+PDzYIpIRA2sRQ4sL53+sN6/fpNSoqE7BP7vBZhG6kYdD13EIMJpvhJI+6Bw"}},"two":"Two"}"# + r#"{"one":1,"signatures":{"domain":{"ed25519:1":"KqmLSbO39/Bzb0QIYE82zqLwsA+PDzYIpIRA2sRQ4sL53+sN6/fpNSoqE7BP7vBZhG6kYdD13EIMJpvhJI+6Bw"}},"two":"Two"}"# ); let mut reverse_alpha_value = to_value(reverse_alpha).expect("reverse_alpha should serialize"); - sign_json("example.com", &key_pair, &mut reverse_alpha_value).unwrap(); + sign_json("domain", &key_pair, &mut reverse_alpha_value).unwrap(); assert_eq!( to_string(&reverse_alpha_value).unwrap(), - r#"{"one":1,"signatures":{"example.com":{"ed25519:1":"KqmLSbO39/Bzb0QIYE82zqLwsA+PDzYIpIRA2sRQ4sL53+sN6/fpNSoqE7BP7vBZhG6kYdD13EIMJpvhJI+6Bw"}},"two":"Two"}"# + r#"{"one":1,"signatures":{"domain":{"ed25519:1":"KqmLSbO39/Bzb0QIYE82zqLwsA+PDzYIpIRA2sRQ4sL53+sN6/fpNSoqE7BP7vBZhG6kYdD13EIMJpvhJI+6Bw"}},"two":"Two"}"# ); } #[test] fn verify_minimal_json() { let value = from_str( - r#"{"one":1,"signatures":{"example.com":{"ed25519:1":"KqmLSbO39/Bzb0QIYE82zqLwsA+PDzYIpIRA2sRQ4sL53+sN6/fpNSoqE7BP7vBZhG6kYdD13EIMJpvhJI+6Bw"}},"two":"Two"}"# + r#"{"one":1,"signatures":{"domain":{"ed25519:1":"KqmLSbO39/Bzb0QIYE82zqLwsA+PDzYIpIRA2sRQ4sL53+sN6/fpNSoqE7BP7vBZhG6kYdD13EIMJpvhJI+6Bw"}},"two":"Two"}"# ).unwrap(); let mut signature_set = HashMap::new(); signature_set.insert("ed25519:1".to_string(), PUBLIC_KEY.to_string()); let mut public_key_map = HashMap::new(); - public_key_map.insert("example.com".to_string(), signature_set); + public_key_map.insert("domain".to_string(), signature_set); assert!(verify_json(&public_key_map, &value).is_ok()); let reverse_value = from_str( - r#"{"two":"Two","signatures":{"example.com":{"ed25519:1":"KqmLSbO39/Bzb0QIYE82zqLwsA+PDzYIpIRA2sRQ4sL53+sN6/fpNSoqE7BP7vBZhG6kYdD13EIMJpvhJI+6Bw"}},"one":1}"# + r#"{"two":"Two","signatures":{"domain":{"ed25519:1":"KqmLSbO39/Bzb0QIYE82zqLwsA+PDzYIpIRA2sRQ4sL53+sN6/fpNSoqE7BP7vBZhG6kYdD13EIMJpvhJI+6Bw"}},"one":1}"# ).unwrap(); assert!(verify_json(&public_key_map, &reverse_value).is_ok()); @@ -447,13 +447,13 @@ mod test { #[test] fn fail_verify_json() { - let value = from_str(r#"{"not":"empty","signatures":{"example.com":"K8280/U9SSy9IVtjBuVeLr+HpOB4BQFWbg+UZaADMtTdGYI7Geitb76LTrr5QV/7Xg4ahLwYGYZzuHGZKM5ZAQ"}}"#).unwrap(); + let value = from_str(r#"{"not":"empty","signatures":{"domain":"K8280/U9SSy9IVtjBuVeLr+HpOB4BQFWbg+UZaADMtTdGYI7Geitb76LTrr5QV/7Xg4ahLwYGYZzuHGZKM5ZAQ"}}"#).unwrap(); let mut signature_set = HashMap::new(); signature_set.insert("ed25519:1".to_string(), PUBLIC_KEY.to_string()); let mut public_key_map = HashMap::new(); - public_key_map.insert("example.com".to_string(), signature_set); + public_key_map.insert("domain".to_string(), signature_set); assert!(verify_json(&public_key_map, &value).is_err()); }