Add verify_event function.

This commit is contained in:
Jimmy Cuadra 2019-07-09 23:38:51 -07:00
parent b85d0fc22c
commit 490ac1c4d3
2 changed files with 114 additions and 16 deletions

View File

@ -1,8 +1,10 @@
//! Functions for signing and verifying JSON and events.
use std::{collections::HashMap, hash::BuildHasher};
use base64::{encode_config, STANDARD_NO_PAD};
use ring::digest::{digest, SHA256};
use serde_json::{from_value, map::Map, to_string, to_value, Value};
use serde_json::{from_str, from_value, map::Map, to_string, to_value, Value};
use crate::{
keys::KeyPair,
@ -134,7 +136,7 @@ pub fn to_canonical_json(value: &Value) -> Result<String, Error> {
to_canonical_json_with_fields_to_remove(value, CANONICAL_JSON_FIELDS_TO_REMOVE)
}
/// Use a public key to verify a signature of a JSON object.
/// Uses a public key to verify a signature of a JSON object.
///
/// # Parameters
///
@ -267,6 +269,94 @@ where
Ok(())
}
/// Uses a set of public keys to verify a signed JSON representation of an event.
///
/// Some room versions may require signatures from multiple homeservers, so this function takes a
/// map of servers to sets of public keys. For each homeserver present in the map, this function
/// will require a valid signature. All known public keys for a homeserver should be provided. The
/// first one found on the given event will be used.
///
/// # Parameters
///
/// * verifier: A `Verifier` appropriate for the digital signature algorithm that was used.
/// * verify_key_map: A map of server names to a map of key identifiers to public keys. Server
/// names are the hostname or IP of a homeserver (e.g. "example.com") for which a signature must be
/// verified. Key identifiers for each server (e.g. "ed25519:1") then map to their respective public
/// keys.
/// * value: The `serde_json::Value` (JSON value) of the event that was signed.
pub fn verify_event<V, S>(
verifier: &V,
verify_key_map: HashMap<&str, HashMap<&str, &[u8], S>, S>,
value: &Value,
) -> Result<(), Error>
where
V: Verifier,
S: BuildHasher,
{
let redacted = redact(value)?;
let map = match redacted {
Value::Object(ref map) => map,
_ => return Err(Error::new("JSON value must be a JSON object")),
};
let signature_map: SignatureMap = match map.get("signatures") {
Some(signatures_value) => match signatures_value.as_object() {
Some(signatures) => from_value(Value::Object(signatures.clone()))?,
None => return Err(Error::new("Field `signatures` must be a JSON object")),
},
None => return Err(Error::new("JSON object must contain a `signatures` field.")),
};
for (server_name, verify_keys) in verify_key_map {
let signature_set = match signature_map.get(server_name)? {
Some(set) => set,
None => {
return Err(Error::new(format!(
"no signatures found for server `{}`",
server_name
)))
}
};
let mut maybe_signature = None;
let mut maybe_verify_key = None;
for (key_id, verify_key) in verify_keys {
if let Some(signature) = signature_set.get(key_id) {
maybe_signature = Some(signature);
maybe_verify_key = Some(verify_key);
break;
}
}
let signature = match maybe_signature {
Some(signature) => signature,
None => {
return Err(Error::new(
"event is not signed with any of the given verify keys",
))
}
};
let verify_key = match maybe_verify_key {
Some(verify_key) => verify_key,
None => {
return Err(Error::new(
"event is not signed with any of the given verify keys",
))
}
};
let canonical_json = from_str(&to_canonical_json(&redacted)?)?;
verify_json(verifier, verify_key, signature, &canonical_json)?;
}
Ok(())
}
/// 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(

View File

@ -19,8 +19,7 @@
//! A homeserver signs JSON with a key pair:
//!
//! ```rust,no_run
//! # use ruma_signatures::{self, KeyPair};
//! # use serde_json;
//! # use ruma_signatures::KeyPair;
//! # let public_key = [0; 32];
//! # let private_key = [0; 32];
//! // Create an Ed25519 key pair.
@ -55,8 +54,7 @@
//! same:
//!
//! ```rust,no_run
//! # use ruma_signatures::{self, KeyPair};
//! # use serde_json;
//! # use ruma_signatures::KeyPair;
//! # let public_key = [0; 32];
//! # let private_key = [0; 32];
//! // Create an Ed25519 key pair.
@ -120,8 +118,6 @@
//! A client application or another homeserver can verify a signature on arbitrary JSON:
//!
//! ```rust,no_run
//! # use ruma_signatures;
//! # use serde_json;
//! # let public_key = [0; 32];
//! # let signature_bytes = [0, 32];
//! let signature = ruma_signatures::Signature::new("ed25519:1", &signature_bytes).unwrap();
@ -130,7 +126,24 @@
//! assert!(ruma_signatures::verify_json(&verifier, &public_key, &signature, &value).is_ok());
//! ```
//!
//! Verifying signatures of Matrix events is not yet implemented by ruma-signatures.
//! Verifying the signatures on a Matrix event are slightly different:
//!
//! ```rust,no_run
//! # use std::collections::HashMap;
//! # let public_key_vec = Vec::new();
//! # let public_key = public_key_vec.as_slice();
//! # let event_json = "";
//! let mut verify_key_map = HashMap::new();
//! let mut example_server_keys = HashMap::new();
//! example_server_keys.insert("ed25519:1", public_key); // public_key: &[u8]
//! verify_key_map.insert("example.com", example_server_keys);
//! let value = serde_json::from_str(event_json).unwrap();
//! let verifier = ruma_signatures::Ed25519Verifier;
//! assert!(ruma_signatures::verify_event(&verifier, verify_key_map, &value).is_ok());
//! ```
//!
//! See the documentation for `verify_event` for details on what verification entails and the
//! `verify_key_map` parameter.
//!
//! # Signature sets and maps
//!
@ -156,9 +169,6 @@
//! This inner object can be created by serializing a `SignatureSet`:
//!
//! ```rust,no_run
//! # use ruma_signatures;
//! # use serde;
//! # use serde_json;
//! # let signature_bytes = [0, 32];
//! let signature = ruma_signatures::Signature::new("ed25519:1", &signature_bytes).unwrap();
//! let mut signature_set = ruma_signatures::SignatureSet::new();
@ -173,9 +183,6 @@
//! created like this:
//!
//! ```rust,no_run
//! # use ruma_signatures;
//! # use serde;
//! # use serde_json;
//! # let signature_bytes = [0, 32];
//! let signature = ruma_signatures::Signature::new("ed25519:1", &signature_bytes).unwrap();
//! let mut signature_set = ruma_signatures::SignatureSet::new();
@ -222,7 +229,8 @@ use std::{
pub use url::Host;
pub use functions::{
content_hash, hash_and_sign_event, reference_hash, sign_json, to_canonical_json, verify_json,
content_hash, hash_and_sign_event, reference_hash, sign_json, to_canonical_json, verify_event,
verify_json,
};
pub use keys::{Ed25519KeyPair, KeyPair};
pub use signatures::{Signature, SignatureMap, SignatureSet};