crypto: Split out the signing module into two files.
parent
5c14910126
commit
6e83a4bbca
|
@ -20,8 +20,8 @@ js_int = "0.1.9"
|
|||
|
||||
[dependencies.ruma]
|
||||
version = "0.0.1"
|
||||
path = "/home/poljar/werk/priv/ruma/ruma"
|
||||
rev = "db2f58032953ccb6d8ae712d64d713ebdf412598"
|
||||
git = "https://github.com/ruma/ruma"
|
||||
rev = "c0eee624311c12ee9c3eb14737988e3f0a60fb3f"
|
||||
features = ["client-api", "unstable-pre-spec", "unstable-exhaustive-types"]
|
||||
|
||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||
|
|
|
@ -14,18 +14,11 @@
|
|||
|
||||
#![allow(dead_code, missing_docs)]
|
||||
|
||||
use aes_gcm::{
|
||||
aead::{generic_array::GenericArray, Aead, NewAead},
|
||||
Aes256Gcm,
|
||||
};
|
||||
use base64::{decode_config, encode_config, DecodeError, URL_SAFE_NO_PAD};
|
||||
use getrandom::getrandom;
|
||||
use matrix_sdk_common::{
|
||||
encryption::DeviceKeys,
|
||||
identifiers::{DeviceKeyAlgorithm, DeviceKeyId},
|
||||
};
|
||||
mod pk_signing;
|
||||
|
||||
use matrix_sdk_common::encryption::DeviceKeys;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::{Error as JsonError, Value};
|
||||
use serde_json::Error as JsonError;
|
||||
use std::{
|
||||
collections::BTreeMap,
|
||||
sync::{
|
||||
|
@ -33,211 +26,18 @@ use std::{
|
|||
Arc,
|
||||
},
|
||||
};
|
||||
use thiserror::Error;
|
||||
use zeroize::Zeroizing;
|
||||
|
||||
use olm_rs::{errors::OlmUtilityError, pk::OlmPkSigning, utility::OlmUtility};
|
||||
|
||||
use matrix_sdk_common::{
|
||||
api::r0::keys::{
|
||||
upload_signatures::Request as SignatureUploadRequest, CrossSigningKey, KeyUsage,
|
||||
},
|
||||
api::r0::keys::{upload_signatures::Request as SignatureUploadRequest, KeyUsage},
|
||||
identifiers::UserId,
|
||||
locks::Mutex,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
error::SignatureError,
|
||||
identities::{MasterPubkey, SelfSigningPubkey, UserSigningPubkey},
|
||||
requests::UploadSigningKeysRequest,
|
||||
UserIdentity,
|
||||
};
|
||||
use crate::{error::SignatureError, requests::UploadSigningKeysRequest};
|
||||
|
||||
use crate::ReadOnlyAccount;
|
||||
|
||||
const NONCE_SIZE: usize = 12;
|
||||
|
||||
fn encode<T: AsRef<[u8]>>(input: T) -> String {
|
||||
encode_config(input, URL_SAFE_NO_PAD)
|
||||
}
|
||||
|
||||
fn decode<T: AsRef<[u8]>>(input: T) -> Result<Vec<u8>, DecodeError> {
|
||||
decode_config(input, URL_SAFE_NO_PAD)
|
||||
}
|
||||
|
||||
/// Error type reporting failures in the Signign operations.
|
||||
#[derive(Debug, Error)]
|
||||
pub enum SigningError {
|
||||
/// Error decoding the base64 encoded pickle data.
|
||||
#[error(transparent)]
|
||||
Decode(#[from] DecodeError),
|
||||
|
||||
/// Error decrypting the pickled signing seed
|
||||
#[error("Error decrypting the pickled signign seed")]
|
||||
Decryption(String),
|
||||
|
||||
/// Error deserializing the pickle data.
|
||||
#[error(transparent)]
|
||||
Json(#[from] JsonError),
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Signing {
|
||||
inner: Arc<Mutex<OlmPkSigning>>,
|
||||
seed: Arc<Zeroizing<Vec<u8>>>,
|
||||
public_key: PublicSigningKey,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for Signing {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("Signing")
|
||||
.field("public_key", &self.public_key.as_str())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Signing {
|
||||
fn eq(&self, other: &Signing) -> bool {
|
||||
self.seed == other.seed
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Debug)]
|
||||
struct MasterSigning {
|
||||
inner: Signing,
|
||||
public_key: MasterPubkey,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
struct PickledMasterSigning {
|
||||
pickle: PickledSigning,
|
||||
public_key: CrossSigningKey,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
struct PickledUserSigning {
|
||||
pickle: PickledSigning,
|
||||
public_key: CrossSigningKey,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
struct PickledSelfSigning {
|
||||
pickle: PickledSigning,
|
||||
public_key: CrossSigningKey,
|
||||
}
|
||||
|
||||
impl MasterSigning {
|
||||
async fn pickle(&self, pickle_key: &[u8]) -> PickledMasterSigning {
|
||||
let pickle = self.inner.pickle(pickle_key).await;
|
||||
let public_key = self.public_key.clone().into();
|
||||
PickledMasterSigning { pickle, public_key }
|
||||
}
|
||||
|
||||
fn from_pickle(pickle: PickledMasterSigning, pickle_key: &[u8]) -> Result<Self, SigningError> {
|
||||
let inner = Signing::from_pickle(pickle.pickle, pickle_key)?;
|
||||
|
||||
Ok(Self {
|
||||
inner,
|
||||
public_key: pickle.public_key.into(),
|
||||
})
|
||||
}
|
||||
|
||||
async fn sign_subkey<'a>(&self, subkey: &mut CrossSigningKey) {
|
||||
// TODO create a borrowed version of a cross singing key.
|
||||
let subkey_wihtout_signatures = CrossSigningKey {
|
||||
user_id: subkey.user_id.clone(),
|
||||
keys: subkey.keys.clone(),
|
||||
usage: subkey.usage.clone(),
|
||||
signatures: BTreeMap::new(),
|
||||
};
|
||||
|
||||
let message = cjson::to_string(&subkey_wihtout_signatures)
|
||||
.expect("Can't serialize cross signing subkey");
|
||||
let signature = self.inner.sign(&message).await;
|
||||
|
||||
subkey
|
||||
.signatures
|
||||
.entry(self.public_key.user_id().to_owned())
|
||||
.or_insert_with(BTreeMap::new)
|
||||
.insert(
|
||||
format!("ed25519:{}", self.inner.public_key().as_str()),
|
||||
signature.0,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
impl UserSigning {
|
||||
async fn pickle(&self, pickle_key: &[u8]) -> PickledUserSigning {
|
||||
let pickle = self.inner.pickle(pickle_key).await;
|
||||
let public_key = self.public_key.clone().into();
|
||||
PickledUserSigning { pickle, public_key }
|
||||
}
|
||||
|
||||
async fn sign_user(&self, _: &UserIdentity) -> BTreeMap<UserId, BTreeMap<String, Value>> {
|
||||
todo!();
|
||||
}
|
||||
|
||||
fn from_pickle(pickle: PickledUserSigning, pickle_key: &[u8]) -> Result<Self, SigningError> {
|
||||
let inner = Signing::from_pickle(pickle.pickle, pickle_key)?;
|
||||
|
||||
Ok(Self {
|
||||
inner,
|
||||
public_key: pickle.public_key.into(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl SelfSigning {
|
||||
async fn pickle(&self, pickle_key: &[u8]) -> PickledSelfSigning {
|
||||
let pickle = self.inner.pickle(pickle_key).await;
|
||||
let public_key = self.public_key.clone().into();
|
||||
PickledSelfSigning { pickle, public_key }
|
||||
}
|
||||
|
||||
async fn sign_device_raw(&self, value: Value) -> Result<Signature, SignatureError> {
|
||||
self.inner.sign_json(value).await
|
||||
}
|
||||
|
||||
async fn sign_device(&self, device_keys: &mut DeviceKeys) -> Result<(), SignatureError> {
|
||||
let json_device = serde_json::to_value(&device_keys)?;
|
||||
let signature = self.sign_device_raw(json_device).await?;
|
||||
|
||||
device_keys
|
||||
.signatures
|
||||
.entry(self.public_key.user_id().to_owned())
|
||||
.or_insert_with(BTreeMap::new)
|
||||
.insert(
|
||||
DeviceKeyId::from_parts(
|
||||
DeviceKeyAlgorithm::Ed25519,
|
||||
self.inner.public_key.as_str().into(),
|
||||
),
|
||||
signature.0,
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn from_pickle(pickle: PickledSelfSigning, pickle_key: &[u8]) -> Result<Self, SigningError> {
|
||||
let inner = Signing::from_pickle(pickle.pickle, pickle_key)?;
|
||||
|
||||
Ok(Self {
|
||||
inner,
|
||||
public_key: pickle.public_key.into(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Debug)]
|
||||
struct SelfSigning {
|
||||
inner: Signing,
|
||||
public_key: SelfSigningPubkey,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Debug)]
|
||||
struct UserSigning {
|
||||
inner: Signing,
|
||||
public_key: UserSigningPubkey,
|
||||
}
|
||||
use pk_signing::{MasterSigning, PickledSignings, SelfSigning, Signing, SigningError, UserSigning};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct PrivateCrossSigningIdentity {
|
||||
|
@ -255,146 +55,6 @@ pub struct PickledCrossSigningIdentity {
|
|||
pub pickle: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
struct PickledSignings {
|
||||
master_key: Option<PickledMasterSigning>,
|
||||
user_signing_key: Option<PickledUserSigning>,
|
||||
self_signing_key: Option<PickledSelfSigning>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Signature(String);
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
struct PickledSigning(String);
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
struct InnerPickle {
|
||||
version: u8,
|
||||
nonce: String,
|
||||
ciphertext: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
struct PublicSigningKey(Arc<String>);
|
||||
|
||||
impl Signature {
|
||||
fn as_str(&self) -> &str {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl PickledSigning {
|
||||
fn as_str(&self) -> &str {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl PublicSigningKey {
|
||||
fn as_str(&self) -> &str {
|
||||
&self.0
|
||||
}
|
||||
|
||||
#[allow(clippy::inherent_to_string)]
|
||||
fn to_string(&self) -> String {
|
||||
self.0.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
impl Signing {
|
||||
fn new() -> Self {
|
||||
let seed = OlmPkSigning::generate_seed();
|
||||
Self::from_seed(seed)
|
||||
}
|
||||
|
||||
fn from_seed(seed: Vec<u8>) -> Self {
|
||||
let inner = OlmPkSigning::new(seed.clone()).expect("Unable to create pk signing object");
|
||||
let public_key = PublicSigningKey(Arc::new(inner.public_key().to_owned()));
|
||||
|
||||
Signing {
|
||||
inner: Arc::new(Mutex::new(inner)),
|
||||
seed: Arc::new(Zeroizing::from(seed)),
|
||||
public_key,
|
||||
}
|
||||
}
|
||||
|
||||
fn from_pickle(pickle: PickledSigning, pickle_key: &[u8]) -> Result<Self, SigningError> {
|
||||
let pickled: InnerPickle = serde_json::from_str(pickle.as_str())?;
|
||||
|
||||
let key = GenericArray::from_slice(pickle_key);
|
||||
let cipher = Aes256Gcm::new(key);
|
||||
|
||||
let nonce = decode(pickled.nonce)?;
|
||||
let nonce = GenericArray::from_slice(&nonce);
|
||||
let ciphertext = &decode(pickled.ciphertext)?;
|
||||
|
||||
let seed = cipher
|
||||
.decrypt(&nonce, ciphertext.as_slice())
|
||||
.map_err(|e| SigningError::Decryption(e.to_string()))?;
|
||||
|
||||
Ok(Self::from_seed(seed))
|
||||
}
|
||||
|
||||
async fn pickle(&self, pickle_key: &[u8]) -> PickledSigning {
|
||||
let key = GenericArray::from_slice(pickle_key);
|
||||
let cipher = Aes256Gcm::new(key);
|
||||
|
||||
let mut nonce = vec![0u8; NONCE_SIZE];
|
||||
getrandom(&mut nonce).expect("Can't generate nonce to pickle the signing object");
|
||||
let nonce = GenericArray::from_slice(nonce.as_slice());
|
||||
|
||||
let ciphertext = cipher
|
||||
.encrypt(nonce, self.seed.as_slice())
|
||||
.expect("Can't encrypt signing pickle");
|
||||
|
||||
let ciphertext = encode(ciphertext);
|
||||
|
||||
let pickle = InnerPickle {
|
||||
version: 1,
|
||||
nonce: encode(nonce.as_slice()),
|
||||
ciphertext,
|
||||
};
|
||||
|
||||
PickledSigning(serde_json::to_string(&pickle).expect("Can't encode pickled signing"))
|
||||
}
|
||||
|
||||
fn public_key(&self) -> &PublicSigningKey {
|
||||
&self.public_key
|
||||
}
|
||||
|
||||
fn cross_signing_key(&self, user_id: UserId, usage: KeyUsage) -> CrossSigningKey {
|
||||
let mut keys = BTreeMap::new();
|
||||
|
||||
keys.insert(
|
||||
format!("ed25519:{}", self.public_key().as_str()),
|
||||
self.public_key().to_string(),
|
||||
);
|
||||
|
||||
CrossSigningKey {
|
||||
user_id,
|
||||
usage: vec![usage],
|
||||
keys,
|
||||
signatures: BTreeMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
async fn verify(&self, message: &str, signature: &Signature) -> Result<bool, OlmUtilityError> {
|
||||
let utility = OlmUtility::new();
|
||||
utility.ed25519_verify(self.public_key.as_str(), message, signature.as_str())
|
||||
}
|
||||
|
||||
async fn sign_json(&self, mut json: Value) -> Result<Signature, SignatureError> {
|
||||
let json_object = json.as_object_mut().ok_or(SignatureError::NotAnObject)?;
|
||||
let _ = json_object.remove("signatures");
|
||||
let canonical_json = cjson::to_string(json_object)?;
|
||||
Ok(self.sign(&canonical_json).await)
|
||||
}
|
||||
|
||||
async fn sign(&self, message: &str) -> Signature {
|
||||
Signature(self.inner.lock().await.sign(message))
|
||||
}
|
||||
}
|
||||
|
||||
impl PrivateCrossSigningIdentity {
|
||||
pub fn user_id(&self) -> &UserId {
|
||||
&self.user_id
|
|
@ -0,0 +1,380 @@
|
|||
// 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 aes_gcm::{
|
||||
aead::{generic_array::GenericArray, Aead, NewAead},
|
||||
Aes256Gcm,
|
||||
};
|
||||
use base64::{decode_config, encode_config, DecodeError, URL_SAFE_NO_PAD};
|
||||
use getrandom::getrandom;
|
||||
use matrix_sdk_common::{
|
||||
encryption::DeviceKeys,
|
||||
identifiers::{DeviceKeyAlgorithm, DeviceKeyId},
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::{Error as JsonError, Value};
|
||||
use std::{collections::BTreeMap, sync::Arc};
|
||||
use thiserror::Error;
|
||||
use zeroize::Zeroizing;
|
||||
|
||||
use olm_rs::{errors::OlmUtilityError, pk::OlmPkSigning, utility::OlmUtility};
|
||||
|
||||
use matrix_sdk_common::{
|
||||
api::r0::keys::{CrossSigningKey, KeyUsage},
|
||||
identifiers::UserId,
|
||||
locks::Mutex,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
error::SignatureError,
|
||||
identities::{MasterPubkey, SelfSigningPubkey, UserSigningPubkey},
|
||||
UserIdentity,
|
||||
};
|
||||
|
||||
const NONCE_SIZE: usize = 12;
|
||||
|
||||
fn encode<T: AsRef<[u8]>>(input: T) -> String {
|
||||
encode_config(input, URL_SAFE_NO_PAD)
|
||||
}
|
||||
|
||||
fn decode<T: AsRef<[u8]>>(input: T) -> Result<Vec<u8>, DecodeError> {
|
||||
decode_config(input, URL_SAFE_NO_PAD)
|
||||
}
|
||||
|
||||
/// Error type reporting failures in the Signign operations.
|
||||
#[derive(Debug, Error)]
|
||||
pub enum SigningError {
|
||||
/// Error decoding the base64 encoded pickle data.
|
||||
#[error(transparent)]
|
||||
Decode(#[from] DecodeError),
|
||||
|
||||
/// Error decrypting the pickled signing seed
|
||||
#[error("Error decrypting the pickled signign seed")]
|
||||
Decryption(String),
|
||||
|
||||
/// Error deserializing the pickle data.
|
||||
#[error(transparent)]
|
||||
Json(#[from] JsonError),
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Signing {
|
||||
inner: Arc<Mutex<OlmPkSigning>>,
|
||||
seed: Arc<Zeroizing<Vec<u8>>>,
|
||||
public_key: PublicSigningKey,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for Signing {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("Signing")
|
||||
.field("public_key", &self.public_key.as_str())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Signing {
|
||||
fn eq(&self, other: &Signing) -> bool {
|
||||
self.seed == other.seed
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
struct InnerPickle {
|
||||
version: u8,
|
||||
nonce: String,
|
||||
ciphertext: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Debug)]
|
||||
pub struct MasterSigning {
|
||||
pub inner: Signing,
|
||||
pub public_key: MasterPubkey,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
pub struct PickledMasterSigning {
|
||||
pickle: PickledSigning,
|
||||
public_key: CrossSigningKey,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
pub struct PickledUserSigning {
|
||||
pickle: PickledSigning,
|
||||
public_key: CrossSigningKey,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
pub struct PickledSelfSigning {
|
||||
pickle: PickledSigning,
|
||||
public_key: CrossSigningKey,
|
||||
}
|
||||
|
||||
impl Signature {
|
||||
pub fn as_str(&self) -> &str {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl PickledSigning {
|
||||
pub fn as_str(&self) -> &str {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct PublicSigningKey(Arc<String>);
|
||||
|
||||
impl PublicSigningKey {
|
||||
pub fn as_str(&self) -> &str {
|
||||
&self.0
|
||||
}
|
||||
|
||||
#[allow(clippy::inherent_to_string)]
|
||||
fn to_string(&self) -> String {
|
||||
self.0.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
impl MasterSigning {
|
||||
pub async fn pickle(&self, pickle_key: &[u8]) -> PickledMasterSigning {
|
||||
let pickle = self.inner.pickle(pickle_key).await;
|
||||
let public_key = self.public_key.clone().into();
|
||||
PickledMasterSigning { pickle, public_key }
|
||||
}
|
||||
|
||||
pub fn from_pickle(
|
||||
pickle: PickledMasterSigning,
|
||||
pickle_key: &[u8],
|
||||
) -> Result<Self, SigningError> {
|
||||
let inner = Signing::from_pickle(pickle.pickle, pickle_key)?;
|
||||
|
||||
Ok(Self {
|
||||
inner,
|
||||
public_key: pickle.public_key.into(),
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn sign_subkey<'a>(&self, subkey: &mut CrossSigningKey) {
|
||||
// TODO create a borrowed version of a cross singing key.
|
||||
let subkey_wihtout_signatures = CrossSigningKey {
|
||||
user_id: subkey.user_id.clone(),
|
||||
keys: subkey.keys.clone(),
|
||||
usage: subkey.usage.clone(),
|
||||
signatures: BTreeMap::new(),
|
||||
};
|
||||
|
||||
let message = cjson::to_string(&subkey_wihtout_signatures)
|
||||
.expect("Can't serialize cross signing subkey");
|
||||
let signature = self.inner.sign(&message).await;
|
||||
|
||||
subkey
|
||||
.signatures
|
||||
.entry(self.public_key.user_id().to_owned())
|
||||
.or_insert_with(BTreeMap::new)
|
||||
.insert(
|
||||
format!("ed25519:{}", self.inner.public_key().as_str()),
|
||||
signature.0,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
impl UserSigning {
|
||||
pub async fn pickle(&self, pickle_key: &[u8]) -> PickledUserSigning {
|
||||
let pickle = self.inner.pickle(pickle_key).await;
|
||||
let public_key = self.public_key.clone().into();
|
||||
PickledUserSigning { pickle, public_key }
|
||||
}
|
||||
|
||||
pub async fn sign_user(&self, _: &UserIdentity) -> BTreeMap<UserId, BTreeMap<String, Value>> {
|
||||
todo!();
|
||||
}
|
||||
|
||||
pub fn from_pickle(
|
||||
pickle: PickledUserSigning,
|
||||
pickle_key: &[u8],
|
||||
) -> Result<Self, SigningError> {
|
||||
let inner = Signing::from_pickle(pickle.pickle, pickle_key)?;
|
||||
|
||||
Ok(Self {
|
||||
inner,
|
||||
public_key: pickle.public_key.into(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl SelfSigning {
|
||||
pub async fn pickle(&self, pickle_key: &[u8]) -> PickledSelfSigning {
|
||||
let pickle = self.inner.pickle(pickle_key).await;
|
||||
let public_key = self.public_key.clone().into();
|
||||
PickledSelfSigning { pickle, public_key }
|
||||
}
|
||||
|
||||
pub async fn sign_device_raw(&self, value: Value) -> Result<Signature, SignatureError> {
|
||||
self.inner.sign_json(value).await
|
||||
}
|
||||
|
||||
pub async fn sign_device(&self, device_keys: &mut DeviceKeys) -> Result<(), SignatureError> {
|
||||
let json_device = serde_json::to_value(&device_keys)?;
|
||||
let signature = self.sign_device_raw(json_device).await?;
|
||||
|
||||
device_keys
|
||||
.signatures
|
||||
.entry(self.public_key.user_id().to_owned())
|
||||
.or_insert_with(BTreeMap::new)
|
||||
.insert(
|
||||
DeviceKeyId::from_parts(
|
||||
DeviceKeyAlgorithm::Ed25519,
|
||||
self.inner.public_key.as_str().into(),
|
||||
),
|
||||
signature.0,
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn from_pickle(
|
||||
pickle: PickledSelfSigning,
|
||||
pickle_key: &[u8],
|
||||
) -> Result<Self, SigningError> {
|
||||
let inner = Signing::from_pickle(pickle.pickle, pickle_key)?;
|
||||
|
||||
Ok(Self {
|
||||
inner,
|
||||
public_key: pickle.public_key.into(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Debug)]
|
||||
pub struct SelfSigning {
|
||||
pub inner: Signing,
|
||||
pub public_key: SelfSigningPubkey,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Debug)]
|
||||
pub struct UserSigning {
|
||||
pub inner: Signing,
|
||||
pub public_key: UserSigningPubkey,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct PickledSignings {
|
||||
pub master_key: Option<PickledMasterSigning>,
|
||||
pub user_signing_key: Option<PickledUserSigning>,
|
||||
pub self_signing_key: Option<PickledSelfSigning>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Signature(String);
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct PickledSigning(String);
|
||||
|
||||
impl Signing {
|
||||
pub fn new() -> Self {
|
||||
let seed = OlmPkSigning::generate_seed();
|
||||
Self::from_seed(seed)
|
||||
}
|
||||
|
||||
pub fn from_seed(seed: Vec<u8>) -> Self {
|
||||
let inner = OlmPkSigning::new(seed.clone()).expect("Unable to create pk signing object");
|
||||
let public_key = PublicSigningKey(Arc::new(inner.public_key().to_owned()));
|
||||
|
||||
Signing {
|
||||
inner: Arc::new(Mutex::new(inner)),
|
||||
seed: Arc::new(Zeroizing::from(seed)),
|
||||
public_key,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_pickle(pickle: PickledSigning, pickle_key: &[u8]) -> Result<Self, SigningError> {
|
||||
let pickled: InnerPickle = serde_json::from_str(pickle.as_str())?;
|
||||
|
||||
let key = GenericArray::from_slice(pickle_key);
|
||||
let cipher = Aes256Gcm::new(key);
|
||||
|
||||
let nonce = decode(pickled.nonce)?;
|
||||
let nonce = GenericArray::from_slice(&nonce);
|
||||
let ciphertext = &decode(pickled.ciphertext)?;
|
||||
|
||||
let seed = cipher
|
||||
.decrypt(&nonce, ciphertext.as_slice())
|
||||
.map_err(|e| SigningError::Decryption(e.to_string()))?;
|
||||
|
||||
Ok(Self::from_seed(seed))
|
||||
}
|
||||
|
||||
pub async fn pickle(&self, pickle_key: &[u8]) -> PickledSigning {
|
||||
let key = GenericArray::from_slice(pickle_key);
|
||||
let cipher = Aes256Gcm::new(key);
|
||||
|
||||
let mut nonce = vec![0u8; NONCE_SIZE];
|
||||
getrandom(&mut nonce).expect("Can't generate nonce to pickle the signing object");
|
||||
let nonce = GenericArray::from_slice(nonce.as_slice());
|
||||
|
||||
let ciphertext = cipher
|
||||
.encrypt(nonce, self.seed.as_slice())
|
||||
.expect("Can't encrypt signing pickle");
|
||||
|
||||
let ciphertext = encode(ciphertext);
|
||||
|
||||
let pickle = InnerPickle {
|
||||
version: 1,
|
||||
nonce: encode(nonce.as_slice()),
|
||||
ciphertext,
|
||||
};
|
||||
|
||||
PickledSigning(serde_json::to_string(&pickle).expect("Can't encode pickled signing"))
|
||||
}
|
||||
|
||||
pub fn public_key(&self) -> &PublicSigningKey {
|
||||
&self.public_key
|
||||
}
|
||||
|
||||
pub fn cross_signing_key(&self, user_id: UserId, usage: KeyUsage) -> CrossSigningKey {
|
||||
let mut keys = BTreeMap::new();
|
||||
|
||||
keys.insert(
|
||||
format!("ed25519:{}", self.public_key().as_str()),
|
||||
self.public_key().to_string(),
|
||||
);
|
||||
|
||||
CrossSigningKey {
|
||||
user_id,
|
||||
usage: vec![usage],
|
||||
keys,
|
||||
signatures: BTreeMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn verify(
|
||||
&self,
|
||||
message: &str,
|
||||
signature: &Signature,
|
||||
) -> Result<bool, OlmUtilityError> {
|
||||
let utility = OlmUtility::new();
|
||||
utility.ed25519_verify(self.public_key.as_str(), message, signature.as_str())
|
||||
}
|
||||
|
||||
pub async fn sign_json(&self, mut json: Value) -> Result<Signature, SignatureError> {
|
||||
let json_object = json.as_object_mut().ok_or(SignatureError::NotAnObject)?;
|
||||
let _ = json_object.remove("signatures");
|
||||
let canonical_json = cjson::to_string(json_object)?;
|
||||
Ok(self.sign(&canonical_json).await)
|
||||
}
|
||||
|
||||
pub async fn sign(&self, message: &str) -> Signature {
|
||||
Signature(self.inner.lock().await.sign(message))
|
||||
}
|
||||
}
|
|
@ -190,6 +190,7 @@ impl Sas {
|
|||
};
|
||||
|
||||
let cancel = if done {
|
||||
// Pass on the signature upload request here as well.
|
||||
self.mark_as_done().await?
|
||||
} else {
|
||||
None
|
||||
|
|
Loading…
Reference in New Issue