crypto: Use ruma structs to represent the generated device keys.

master
Damir Jelić 2020-03-10 13:02:14 +01:00
parent c41d3873b6
commit 003d514c14
1 changed files with 66 additions and 38 deletions

View File

@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
use std::collections::HashMap;
use std::convert::TryInto; use std::convert::TryInto;
use super::error::SignatureError; use super::error::SignatureError;
@ -25,11 +26,15 @@ use olm_rs::utility::OlmUtility;
use serde_json::json; use serde_json::json;
use serde_json::Value; use serde_json::Value;
use ruma_client_api::r0::keys::{AlgorithmAndDeviceId, DeviceKeys, KeyAlgorithm};
use ruma_events::Algorithm;
use ruma_identifiers::{DeviceId, UserId};
struct OlmMachine { struct OlmMachine {
/// The unique user id that owns this account. /// The unique user id that owns this account.
user_id: String, user_id: UserId,
/// The unique device id of the device that holds this account. /// The unique device id of the device that holds this account.
device_id: String, device_id: DeviceId,
/// Our underlying Olm Account holding our identity keys. /// Our underlying Olm Account holding our identity keys.
account: Account, account: Account,
/// The number of signed one-time keys we have uploaded to the server. If /// The number of signed one-time keys we have uploaded to the server. If
@ -43,15 +48,15 @@ impl OlmMachine {
const OLM_V1_ALGORITHM: &'static str = "m.olm.v1.curve25519-aes-sha2"; const OLM_V1_ALGORITHM: &'static str = "m.olm.v1.curve25519-aes-sha2";
const MEGOLM_V1_ALGORITHM: &'static str = "m.megolm.v1.aes-sha2"; const MEGOLM_V1_ALGORITHM: &'static str = "m.megolm.v1.aes-sha2";
const ALGORITHMS: &'static [&'static str] = &[ const ALGORITHMS: &'static [&'static ruma_events::Algorithm] = &[
OlmMachine::OLM_V1_ALGORITHM, &Algorithm::OlmV1Curve25519AesSha2,
OlmMachine::MEGOLM_V1_ALGORITHM, &Algorithm::MegolmV1AesSha2,
]; ];
/// Create a new account. /// Create a new account.
pub fn new(user_id: &str, device_id: &str) -> Self { pub fn new(user_id: UserId, device_id: &str) -> Self {
OlmMachine { OlmMachine {
user_id: user_id.to_owned(), user_id,
device_id: device_id.to_owned(), device_id: device_id.to_owned(),
account: Account::new(), account: Account::new(),
uploaded_signed_key_count: None, uploaded_signed_key_count: None,
@ -125,32 +130,47 @@ impl OlmMachine {
} }
/// Sign the device keys and return a JSON Value to upload them. /// Sign the device keys and return a JSON Value to upload them.
fn device_keys(&self) -> Value { fn device_keys(&self) -> DeviceKeys {
let identity_keys = self.account.identity_keys(); let identity_keys = self.account.identity_keys();
let mut device_keys = json!({ let mut keys = HashMap::new();
keys.insert(
AlgorithmAndDeviceId(KeyAlgorithm::Curve25519, self.device_id.clone()),
identity_keys.curve25519().to_owned(),
);
keys.insert(
AlgorithmAndDeviceId(KeyAlgorithm::Ed25519, self.device_id.clone()),
identity_keys.ed25519().to_owned(),
);
let device_keys = json!({
"user_id": self.user_id, "user_id": self.user_id,
"device_id": self.device_id, "device_id": self.device_id,
"algorithms": OlmMachine::ALGORITHMS, "algorithms": OlmMachine::ALGORITHMS,
"keys": { "keys": keys,
format!("curve25519:{}", self.device_id): identity_keys.curve25519(),
format!("ed25519:{}", self.device_id): identity_keys.ed25519(),
},
}); });
let signature = json!({ let mut signatures = HashMap::new();
self.user_id.clone(): {
format!("ed25519:{}", self.device_id): self.sign_json(&device_keys), let mut signature = HashMap::new();
signature.insert(
AlgorithmAndDeviceId(KeyAlgorithm::Ed25519, self.device_id.clone()),
self.sign_json(&device_keys),
);
signatures.insert(self.user_id.clone(), signature);
DeviceKeys {
user_id: self.user_id.clone(),
device_id: self.device_id.clone(),
algorithms: vec![
Algorithm::OlmV1Curve25519AesSha2,
Algorithm::MegolmV1AesSha2,
],
keys,
signatures,
unsigned: None,
} }
});
let device_keys_object = device_keys
.as_object_mut()
.expect("Device keys json value isn't an object");
device_keys_object.insert("signatures".to_string(), signature);
device_keys
} }
/// Generate, sign and prepare one-time keys to be uploaded. /// Generate, sign and prepare one-time keys to be uploaded.
@ -174,7 +194,7 @@ impl OlmMachine {
let one_time_key = json!({ let one_time_key = json!({
"key": key, "key": key,
"signatures": { "signatures": {
self.user_id.clone(): { self.user_id.to_string(): {
format!("ed25519:{}", self.device_id): signature format!("ed25519:{}", self.device_id): signature
} }
} }
@ -219,7 +239,7 @@ impl OlmMachine {
/// * `json` - The JSON object that should be verified. /// * `json` - The JSON object that should be verified.
fn verify_json( fn verify_json(
&self, &self,
user_id: &str, user_id: &UserId,
device_id: &str, device_id: &str,
user_key: &str, user_key: &str,
json: &mut Value, json: &mut Value,
@ -234,17 +254,18 @@ impl OlmMachine {
json_object.insert("unsigned".to_string(), u); json_object.insert("unsigned".to_string(), u);
} }
let key_id = format!("ed25519:{}", device_id); // TODO this should be part of ruma-client-api.
let key_id_string = format!("{}:{}", KeyAlgorithm::Ed25519, device_id);
let signatures = signatures.ok_or(SignatureError::NoSignatureFound)?; let signatures = signatures.ok_or(SignatureError::NoSignatureFound)?;
let signature_object = signatures let signature_object = signatures
.as_object() .as_object()
.ok_or(SignatureError::NoSignatureFound)?; .ok_or(SignatureError::NoSignatureFound)?;
let signature = signature_object let signature = signature_object
.get(user_id) .get(&user_id.to_string())
.ok_or(SignatureError::NoSignatureFound)?; .ok_or(SignatureError::NoSignatureFound)?;
let signature = signature let signature = signature
.get(key_id) .get(key_id_string)
.ok_or(SignatureError::NoSignatureFound)?; .ok_or(SignatureError::NoSignatureFound)?;
let signature = signature.as_str().ok_or(SignatureError::NoSignatureFound)?; let signature = signature.as_str().ok_or(SignatureError::NoSignatureFound)?;
@ -267,7 +288,7 @@ impl OlmMachine {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
const USER_ID: &str = "@test:example.org"; static USER_ID: &str = "@test:example.org";
const DEVICE_ID: &str = "DEVICEID"; const DEVICE_ID: &str = "DEVICEID";
use js_int::UInt; use js_int::UInt;
@ -275,10 +296,17 @@ mod test {
use std::fs::File; use std::fs::File;
use std::io::prelude::*; use std::io::prelude::*;
use ruma_identifiers::UserId;
use serde_json::json;
use crate::api::r0::keys; use crate::api::r0::keys;
use crate::crypto::machine::OlmMachine; use crate::crypto::machine::OlmMachine;
use http::Response; use http::Response;
fn user_id() -> UserId {
UserId::try_from(USER_ID).unwrap()
}
fn response_from_file(path: &str) -> Response<Vec<u8>> { fn response_from_file(path: &str) -> Response<Vec<u8>> {
let mut file = File::open(path) let mut file = File::open(path)
.unwrap_or_else(|_| panic!(format!("No such data file found {}", path))); .unwrap_or_else(|_| panic!(format!("No such data file found {}", path)));
@ -296,13 +324,13 @@ mod test {
#[test] #[test]
fn create_olm_machine() { fn create_olm_machine() {
let machine = OlmMachine::new(USER_ID, DEVICE_ID); let machine = OlmMachine::new(user_id(), DEVICE_ID);
assert!(machine.should_upload_keys()); assert!(machine.should_upload_keys());
} }
#[async_std::test] #[async_std::test]
async fn receive_keys_upload_response() { async fn receive_keys_upload_response() {
let mut machine = OlmMachine::new(USER_ID, DEVICE_ID); let mut machine = OlmMachine::new(user_id(), DEVICE_ID);
let mut response = keys_upload_response(); let mut response = keys_upload_response();
response response
@ -331,7 +359,7 @@ mod test {
#[async_std::test] #[async_std::test]
async fn generate_one_time_keys() { async fn generate_one_time_keys() {
let mut machine = OlmMachine::new(USER_ID, DEVICE_ID); let mut machine = OlmMachine::new(user_id(), DEVICE_ID);
let mut response = keys_upload_response(); let mut response = keys_upload_response();
@ -352,7 +380,7 @@ mod test {
#[test] #[test]
fn test_device_key_signing() { fn test_device_key_signing() {
let machine = OlmMachine::new(USER_ID, DEVICE_ID); let machine = OlmMachine::new(user_id(), DEVICE_ID);
let mut device_keys = machine.device_keys(); let mut device_keys = machine.device_keys();
let identity_keys = machine.account.identity_keys(); let identity_keys = machine.account.identity_keys();
@ -362,14 +390,14 @@ mod test {
&machine.user_id, &machine.user_id,
&machine.device_id, &machine.device_id,
ed25519_key, ed25519_key,
&mut device_keys, &mut json!(&mut device_keys),
); );
assert!(ret.is_ok()); assert!(ret.is_ok());
} }
#[test] #[test]
fn test_invalid_signature() { fn test_invalid_signature() {
let machine = OlmMachine::new(USER_ID, DEVICE_ID); let machine = OlmMachine::new(user_id(), DEVICE_ID);
let mut device_keys = machine.device_keys(); let mut device_keys = machine.device_keys();
@ -377,14 +405,14 @@ mod test {
&machine.user_id, &machine.user_id,
&machine.device_id, &machine.device_id,
"fake_key", "fake_key",
&mut device_keys, &mut json!(&mut device_keys),
); );
assert!(ret.is_err()); assert!(ret.is_err());
} }
#[test] #[test]
fn test_one_time_key_signing() { fn test_one_time_key_signing() {
let mut machine = OlmMachine::new(USER_ID, DEVICE_ID); let mut machine = OlmMachine::new(user_id(), DEVICE_ID);
machine.uploaded_signed_key_count = Some(49); machine.uploaded_signed_key_count = Some(49);
let mut one_time_keys = machine.signed_one_time_keys().unwrap(); let mut one_time_keys = machine.signed_one_time_keys().unwrap();