rust-sdk: Add initial crytpto code.

master
Damir Jelić 2020-02-24 17:19:00 +01:00
parent f8b76c6af3
commit d3903811c6
6 changed files with 220 additions and 26 deletions

View File

@ -10,6 +10,10 @@ readme = "README.md"
repository = "https://github.com/matrix-org/matrix-rust-sdk" repository = "https://github.com/matrix-org/matrix-rust-sdk"
version = "0.1.0" version = "0.1.0"
[features]
default = []
encryption = ["olm-rs", "serde/derive", "serde_json"]
[dependencies] [dependencies]
js_int = "0.1.2" js_int = "0.1.2"
futures = "0.3.4" futures = "0.3.4"
@ -22,13 +26,11 @@ ruma-events = "0.15.0"
log = "0.4.8" log = "0.4.8"
ruma-identifiers = "0.14.1" ruma-identifiers = "0.14.1"
serde_json = "1.0.48"
serde_urlencoded = "0.6.1"
url = "2.1.1" url = "2.1.1"
[dependencies.serde] olm-rs = { git = "https://gitlab.gnome.org/jhaye/olm-rs/", optional = true}
version = "1.0.104" serde = { version = "1.0.104", optional = true, features = ["derive"] }
features = ["derive"] serde_json = { version = "*", optional = true }
[dev-dependencies] [dev-dependencies]
tokio = { version = "0.2.11", features = ["full"] } tokio = { version = "0.2.11", features = ["full"] }

18
src/crypto/mod.rs Normal file
View File

@ -0,0 +1,18 @@
// Copyright 2020 The Matrix.org Foundation C.I.C.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// TODO remove this.
#[allow(dead_code)]
mod olm;

191
src/crypto/olm.rs Normal file
View File

@ -0,0 +1,191 @@
// Copyright 2020 The Matrix.org Foundation C.I.C.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use olm_rs::account::OlmAccount;
use std::collections::{hash_map::Iter, hash_map::Keys, hash_map::Values, HashMap};
use serde;
use serde::Deserialize;
/// Struct representing the parsed result of `OlmAccount::identity_keys()`.
#[derive(Deserialize, Debug, PartialEq)]
pub struct IdentityKeys {
#[serde(flatten)]
keys: HashMap<String, String>,
}
impl IdentityKeys {
/// Get the public part of the ed25519 key of the account.
pub fn ed25519(&self) -> &str {
&self.keys["ed25519"]
}
/// Get the public part of the curve25519 key of the account.
pub fn curve25519(&self) -> &str {
&self.keys["curve25519"]
}
/// Get a reference to the key of the given key type.
pub fn get(&self, key_type: &str) -> Option<&str> {
let ret = self.keys.get(key_type);
ret.map(|x| &**x)
}
/// An iterator visiting all public keys of the account.
pub fn values(&self) -> Values<String, String> {
self.keys.values()
}
/// An iterator visiting all key types of the account.
pub fn keys(&self) -> Keys<String, String> {
self.keys.keys()
}
/// An iterator visiting all key-type, key pairs of the account.
pub fn iter(&self) -> Iter<String, String> {
self.keys.iter()
}
/// Returns true if the account contains a key with the given key type.
pub fn contains_key(&self, key_type: &str) -> bool {
self.keys.contains_key(key_type)
}
}
#[derive(Deserialize, Debug, PartialEq)]
/// Struct representing the the one-time keys.
/// The keys can be accessed in a map-like fashion.
pub struct OneTimeKeys {
#[serde(flatten)]
keys: HashMap<String, HashMap<String, String>>,
}
impl OneTimeKeys {
/// Get the HashMap containing the curve25519 one-time keys.
/// This is the same as using `get("curve25519").unwrap()`
pub fn curve25519(&self) -> &HashMap<String, String> {
&self.keys["curve25519"]
}
/// Get a reference to the hashmap corresponding to given key type.
pub fn get(&self, key_type: &str) -> Option<&HashMap<String, String>> {
self.keys.get(key_type)
}
/// An iterator visiting all one-time key hashmaps in an arbitrary order.
pub fn values(&self) -> Values<String, HashMap<String, String>> {
self.keys.values()
}
/// An iterator visiting all one-time key types in an arbitrary order.
pub fn keys(&self) -> Keys<String, HashMap<String, String>> {
self.keys.keys()
}
/// An iterator visiting all one-time key types and their respective
/// key hashmaps in an arbitrary order.
pub fn iter(&self) -> Iter<String, HashMap<String, String>> {
self.keys.iter()
}
/// Returns `true` if the struct contains the given key type.
/// This does not mean that there are any keys for the given key type.
pub fn contains_key(&self, key_type: &str) -> bool {
self.keys.contains_key(key_type)
}
}
pub struct Account {
inner: OlmAccount,
shared: bool,
}
impl Account {
/// Create a new account.
pub fn new() -> Self {
Account {
inner: OlmAccount::new(),
shared: false,
}
}
/// Get the public parts of the identity keys for the account.
pub fn identity_keys(&self) -> IdentityKeys {
serde_json::from_str(&self.inner.identity_keys()).expect("Can't parse the identity keys")
}
/// Has the account been shared with the server.
pub fn shared(&self) -> bool {
self.shared
}
/// Get the one-time keys of the account.
///
/// This can be empty, keys need to be generated first.
pub fn one_time_keys(&self) -> OneTimeKeys {
serde_json::from_str(&self.inner.one_time_keys()).expect("Can't parse the one-time keys")
}
/// Generate count number of one-time keys.
pub fn generate_one_time_keys(&self, count: usize) {
self.inner.generate_one_time_keys(count);
}
/// Get the maximum number of one-time keys the account can hold.
pub fn max_one_time_keys(&self) -> usize {
self.inner.max_number_of_one_time_keys()
}
}
#[cfg(test)]
mod test {
use crate::crypto::olm::Account;
#[test]
fn account_creation() {
let account = Account::new();
let identyty_keys = account.identity_keys();
assert!(!account.shared());
assert!(!identyty_keys.ed25519().is_empty());
assert_ne!(identyty_keys.values().len(), 0);
assert_ne!(identyty_keys.keys().len(), 0);
assert_ne!(identyty_keys.iter().len(), 0);
assert!(identyty_keys.contains_key("ed25519"));
assert_eq!(identyty_keys.ed25519(), identyty_keys.get("ed25519").unwrap());
assert!(!identyty_keys.curve25519().is_empty());
}
#[test]
fn one_time_keys_creation() {
let account = Account::new();
let one_time_keys = account.one_time_keys();
assert!(one_time_keys.curve25519().is_empty());
assert_ne!(account.max_one_time_keys(), 0);
account.generate_one_time_keys(10);
let one_time_keys = account.one_time_keys();
assert!(!one_time_keys.curve25519().is_empty());
assert_ne!(one_time_keys.values().len(), 0);
assert_ne!(one_time_keys.keys().len(), 0);
assert_ne!(one_time_keys.iter().len(), 0);
assert!(one_time_keys.contains_key("curve25519"));
assert_eq!(one_time_keys.curve25519().keys().len(), 10);
assert_eq!(one_time_keys.curve25519(), one_time_keys.get("curve25519").unwrap());
}
}

View File

@ -21,8 +21,6 @@ use std::fmt::{Display, Formatter, Result as FmtResult};
use reqwest::Error as ReqwestError; use reqwest::Error as ReqwestError;
use ruma_api::error::FromHttpResponseError as RumaResponseError; use ruma_api::error::FromHttpResponseError as RumaResponseError;
use ruma_api::error::IntoHttpError as RumaIntoHttpError; use ruma_api::error::IntoHttpError as RumaIntoHttpError;
use serde_json::Error as SerdeJsonError;
use serde_urlencoded::ser::Error as SerdeUrlEncodedSerializeError;
use url::ParseError; use url::ParseError;
/// An error that can occur during client operations. /// An error that can occur during client operations.
@ -37,8 +35,6 @@ impl Display for Error {
InnerError::Uri(_) => "Provided string could not be converted into a URI.", InnerError::Uri(_) => "Provided string could not be converted into a URI.",
InnerError::RumaResponseError(_) => "An error occurred converting between ruma_client_api and hyper types.", InnerError::RumaResponseError(_) => "An error occurred converting between ruma_client_api and hyper types.",
InnerError::IntoHttpError(_) => "An error occurred converting between ruma_client_api and hyper types.", InnerError::IntoHttpError(_) => "An error occurred converting between ruma_client_api and hyper types.",
InnerError::SerdeJson(_) => "A serialization error occurred.",
InnerError::SerdeUrlEncodedSerialize(_) => "An error occurred serializing data to a query string.",
}; };
write!(f, "{}", message) write!(f, "{}", message)
@ -60,10 +56,6 @@ pub(crate) enum InnerError {
RumaResponseError(RumaResponseError), RumaResponseError(RumaResponseError),
/// An error converting between ruma_client_api types and Hyper types. /// An error converting between ruma_client_api types and Hyper types.
IntoHttpError(RumaIntoHttpError), IntoHttpError(RumaIntoHttpError),
/// An error when serializing or deserializing a JSON value.
SerdeJson(SerdeJsonError),
/// An error when serializing a query string value.
SerdeUrlEncodedSerialize(SerdeUrlEncodedSerializeError),
} }
impl From<ParseError> for Error { impl From<ParseError> for Error {
@ -84,18 +76,6 @@ impl From<RumaIntoHttpError> for Error {
} }
} }
impl From<SerdeJsonError> for Error {
fn from(error: SerdeJsonError) -> Self {
Self(InnerError::SerdeJson(error))
}
}
impl From<SerdeUrlEncodedSerializeError> for Error {
fn from(error: SerdeUrlEncodedSerializeError) -> Self {
Self(InnerError::SerdeUrlEncodedSerialize(error))
}
}
impl From<ReqwestError> for Error { impl From<ReqwestError> for Error {
fn from(error: ReqwestError) -> Self { fn from(error: ReqwestError) -> Self {
Self(InnerError::Reqwest(error)) Self(InnerError::Reqwest(error))

View File

@ -27,6 +27,9 @@ mod base_client;
mod error; mod error;
mod session; mod session;
#[cfg(feature = "encryption")]
mod crypto;
pub use async_client::{AsyncClient, AsyncClientConfig, SyncSettings}; pub use async_client::{AsyncClient, AsyncClientConfig, SyncSettings};
pub use base_client::{Client, Room}; pub use base_client::{Client, Room};

View File

@ -18,7 +18,7 @@
use ruma_identifiers::UserId; use ruma_identifiers::UserId;
/// A user session, containing an access token and information about the associated user account. /// A user session, containing an access token and information about the associated user account.
#[derive(Clone, Debug, serde::Deserialize, Eq, Hash, PartialEq, serde::Serialize)] #[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub struct Session { pub struct Session {
/// The access token used for this session. /// The access token used for this session.
pub access_token: String, pub access_token: String,