Add verify_event function.
This commit is contained in:
parent
b85d0fc22c
commit
490ac1c4d3
@ -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(
|
||||
|
36
src/lib.rs
36
src/lib.rs
@ -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};
|
||||
|
Loading…
x
Reference in New Issue
Block a user