crypto: Use ruma structs to represent the generated device keys.
parent
c41d3873b6
commit
003d514c14
|
@ -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();
|
||||||
|
|
Loading…
Reference in New Issue