From 47c6de7a1ba40986bcab8082e46a9549837cccf7 Mon Sep 17 00:00:00 2001 From: Jimmy Cuadra Date: Tue, 9 Jul 2019 00:05:01 -0700 Subject: [PATCH] Add hash_and_sign_event function. --- src/functions.rs | 65 ++++++++++++++++++++++++++++++++++++++++++++++-- src/lib.rs | 4 ++- 2 files changed, 66 insertions(+), 3 deletions(-) diff --git a/src/functions.rs b/src/functions.rs index 47046642..1c659a92 100644 --- a/src/functions.rs +++ b/src/functions.rs @@ -4,9 +4,14 @@ use std::error::Error as _; use base64::{encode_config, STANDARD_NO_PAD}; use ring::digest::{digest, SHA256}; -use serde_json::{to_string, Value}; +use serde_json::{json, to_string, to_value, Value}; -use crate::{keys::KeyPair, signatures::Signature, verification::Verifier, Error}; +use crate::{ + keys::KeyPair, + signatures::{Signature, SignatureMap, SignatureSet}, + verification::Verifier, + Error, +}; /// The fields that are allowed to remain in an event during redaction. static ALLOWED_KEYS: &[&str] = &[ @@ -133,6 +138,62 @@ pub fn reference_hash(value: &Value) -> Result { Ok(encode_config(&hash, STANDARD_NO_PAD)) } +/// Hashes and signs the JSON representation of an event. +/// +/// # Parameters +/// +/// * server_name: The hostname or IP of the homeserver, e.g. `example.com`. +/// * key_pair: A cryptographic key pair used to sign the event. +/// * value: A JSON object to be hashed and signed according to the Matrix specification. +pub fn hash_and_sign_event( + server_name: &str, + key_pair: &K, + value: &Value, +) -> Result +where + K: KeyPair, +{ + let hash = content_hash(value)?; + + if !value.is_object() { + return Err(Error::new("JSON value must be a JSON object")); + } + + let mut owned_value = value.clone(); + + // Limit the scope of the mutable borrow so `owned_value` can be passed immutably to `redact` + // below. + { + let object = owned_value + .as_object_mut() + .expect("safe since we checked above"); + + let hashes = json!({ "sha256": hash }); + + object.insert("hashes".to_string(), hashes); + } + + let redacted = redact(&owned_value)?; + + let signature = sign_json(key_pair, &redacted)?; + + let mut signature_set = SignatureSet::with_capacity(1); + signature_set.insert(signature); + + let mut signature_map = SignatureMap::with_capacity(1); + signature_map.insert(server_name, signature_set)?; + + let signature_map_value = + to_value(signature_map).map_err(|error| Error::new(error.to_string()))?; + + let object = owned_value + .as_object_mut() + .expect("safe since we checked above"); + object.insert("signatures".to_string(), signature_map_value); + + Ok(owned_value) +} + /// Internal implementation detail of the canonical JSON algorithm. Allows customization of the /// fields that will be removed before serializing. fn to_canonical_json_with_fields_to_remove( diff --git a/src/lib.rs b/src/lib.rs index 36d3c8a3..9e48dac8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -150,7 +150,9 @@ use std::{ pub use url::Host; -pub use functions::{content_hash, reference_hash, sign_json, to_canonical_json, verify_json}; +pub use functions::{ + content_hash, hash_and_sign_event, reference_hash, sign_json, to_canonical_json, verify_json, +}; pub use keys::{Ed25519KeyPair, KeyPair}; pub use signatures::{Signature, SignatureMap, SignatureSet}; pub use verification::{Ed25519Verifier, Verifier};