crypto: Move the signature verification method under an Utility struct.

master
Damir Jelić 2020-09-04 13:14:01 +02:00
parent 22daf0d81e
commit 89efcee337
6 changed files with 124 additions and 83 deletions

View File

@ -39,9 +39,10 @@ use crate::{Account, OlmMachine};
use crate::{ use crate::{
error::{EventError, OlmError, OlmResult, SignatureError}, error::{EventError, OlmError, OlmResult, SignatureError},
identities::{OwnUserIdentity, UserIdentities}, identities::{OwnUserIdentity, UserIdentities},
olm::Utility,
store::{caches::ReadOnlyUserDevices, Result as StoreResult}, store::{caches::ReadOnlyUserDevices, Result as StoreResult},
verification::VerificationMachine, verification::VerificationMachine,
verify_json, Sas, ToDeviceRequest, Sas, ToDeviceRequest,
}; };
/// A read-only version of a `Device`. /// A read-only version of a `Device`.
@ -362,7 +363,9 @@ impl ReadOnlyDevice {
.get_key(DeviceKeyAlgorithm::Ed25519) .get_key(DeviceKeyAlgorithm::Ed25519)
.ok_or(SignatureError::MissingSigningKey)?; .ok_or(SignatureError::MissingSigningKey)?;
verify_json( let utility = Utility::new();
utility.verify_json(
&self.user_id, &self.user_id,
&DeviceKeyId::from_parts(DeviceKeyAlgorithm::Ed25519, self.device_id()), &DeviceKeyId::from_parts(DeviceKeyAlgorithm::Ed25519, self.device_id()),
signing_key, signing_key,

View File

@ -28,7 +28,7 @@ use matrix_sdk_common::{
identifiers::{DeviceKeyId, UserId}, identifiers::{DeviceKeyId, UserId},
}; };
use crate::{error::SignatureError, verify_json, ReadOnlyDevice}; use crate::{error::SignatureError, olm::Utility, ReadOnlyDevice};
/// Wrapper for a cross signing key marking it as the master key. /// Wrapper for a cross signing key marking it as the master key.
/// ///
@ -157,7 +157,8 @@ impl MasterPubkey {
return Err(SignatureError::UserIdMissmatch); return Err(SignatureError::UserIdMissmatch);
} }
verify_json( let utility = Utility::new();
utility.verify_json(
&self.0.user_id, &self.0.user_id,
&key_id, &key_id,
key, key,
@ -191,7 +192,8 @@ impl UserSigningPubkey {
// TODO check that the usage is OK. // TODO check that the usage is OK.
verify_json( let utility = Utility::new();
utility.verify_json(
&self.0.user_id, &self.0.user_id,
&DeviceKeyId::try_from(key_id.as_str())?, &DeviceKeyId::try_from(key_id.as_str())?,
key, key,
@ -224,7 +226,8 @@ impl SelfSigningPubkey {
// TODO check that the usage is OK. // TODO check that the usage is OK.
verify_json( let utility = Utility::new();
utility.verify_json(
&self.0.user_id, &self.0.user_id,
&DeviceKeyId::try_from(key_id.as_str())?, &DeviceKeyId::try_from(key_id.as_str())?,
key, key,

View File

@ -44,74 +44,3 @@ pub(crate) use olm::Account;
pub use olm::EncryptionSettings; pub use olm::EncryptionSettings;
pub use requests::{IncomingResponse, OutgoingRequest, OutgoingRequests, ToDeviceRequest}; pub use requests::{IncomingResponse, OutgoingRequest, OutgoingRequests, ToDeviceRequest};
pub use verification::Sas; pub use verification::Sas;
use error::SignatureError;
use matrix_sdk_common::identifiers::{DeviceKeyAlgorithm, DeviceKeyId, UserId};
use olm_rs::utility::OlmUtility;
use serde_json::Value;
/// Verify a signed JSON object.
///
/// The object must have a signatures key associated with an object of the
/// form `user_id: {key_id: signature}`.
///
/// Returns Ok if the signature was successfully verified, otherwise an
/// SignatureError.
///
/// # Arguments
///
/// * `user_id` - The user who signed the JSON object.
///
/// * `key_id` - The id of the key that signed the JSON object.
///
/// * `signing_key` - The public ed25519 key which was used to sign the JSON
/// object.
///
/// * `json` - The JSON object that should be verified.
pub(crate) fn verify_json(
user_id: &UserId,
key_id: &DeviceKeyId,
signing_key: &str,
json: &mut Value,
) -> Result<(), SignatureError> {
if key_id.algorithm() != DeviceKeyAlgorithm::Ed25519 {
return Err(SignatureError::UnsupportedAlgorithm);
}
let json_object = json.as_object_mut().ok_or(SignatureError::NotAnObject)?;
let unsigned = json_object.remove("unsigned");
let signatures = json_object.remove("signatures");
let canonical_json = cjson::to_string(json_object)?;
if let Some(u) = unsigned {
json_object.insert("unsigned".to_string(), u);
}
let signatures = signatures.ok_or(SignatureError::NoSignatureFound)?;
let signature_object = signatures
.as_object()
.ok_or(SignatureError::NoSignatureFound)?;
let signature = signature_object
.get(user_id.as_str())
.ok_or(SignatureError::NoSignatureFound)?;
let signature = signature
.get(key_id.to_string())
.ok_or(SignatureError::NoSignatureFound)?;
let signature = signature.as_str().ok_or(SignatureError::NoSignatureFound)?;
let utility = OlmUtility::new();
let ret = if utility
.ed25519_verify(signing_key, &canonical_json, signature)
.is_ok()
{
Ok(())
} else {
Err(SignatureError::VerificationError)
};
json_object.insert("signatures".to_string(), signatures);
ret
}

View File

@ -1548,8 +1548,9 @@ pub(crate) mod test {
use crate::{ use crate::{
machine::OlmMachine, machine::OlmMachine,
olm::Utility,
verification::test::{outgoing_request_to_event, request_to_event}, verification::test::{outgoing_request_to_event, request_to_event},
verify_json, EncryptionSettings, ReadOnlyDevice, ToDeviceRequest, EncryptionSettings, ReadOnlyDevice, ToDeviceRequest,
}; };
use matrix_sdk_common::{ use matrix_sdk_common::{
@ -1784,7 +1785,8 @@ pub(crate) mod test {
let identity_keys = machine.account.identity_keys(); let identity_keys = machine.account.identity_keys();
let ed25519_key = identity_keys.ed25519(); let ed25519_key = identity_keys.ed25519();
let ret = verify_json( let utility = Utility::new();
let ret = utility.verify_json(
&machine.user_id, &machine.user_id,
&DeviceKeyId::from_parts(DeviceKeyAlgorithm::Ed25519, machine.device_id()), &DeviceKeyId::from_parts(DeviceKeyAlgorithm::Ed25519, machine.device_id()),
ed25519_key, ed25519_key,
@ -1815,7 +1817,8 @@ pub(crate) mod test {
let mut device_keys = machine.account.device_keys().await; let mut device_keys = machine.account.device_keys().await;
let ret = verify_json( let utility = Utility::new();
let ret = utility.verify_json(
&machine.user_id, &machine.user_id,
&DeviceKeyId::from_parts(DeviceKeyAlgorithm::Ed25519, machine.device_id()), &DeviceKeyId::from_parts(DeviceKeyAlgorithm::Ed25519, machine.device_id()),
"fake_key", "fake_key",
@ -1835,7 +1838,8 @@ pub(crate) mod test {
let mut one_time_key = one_time_keys.values_mut().next().unwrap(); let mut one_time_key = one_time_keys.values_mut().next().unwrap();
let ret = verify_json( let utility = Utility::new();
let ret = utility.verify_json(
&machine.user_id, &machine.user_id,
&DeviceKeyId::from_parts(DeviceKeyAlgorithm::Ed25519, machine.device_id()), &DeviceKeyId::from_parts(DeviceKeyAlgorithm::Ed25519, machine.device_id()),
ed25519_key, ed25519_key,
@ -1857,7 +1861,8 @@ pub(crate) mod test {
.await .await
.expect("Can't prepare initial key upload"); .expect("Can't prepare initial key upload");
let ret = verify_json( let utility = Utility::new();
let ret = utility.verify_json(
&machine.user_id, &machine.user_id,
&DeviceKeyId::from_parts(DeviceKeyAlgorithm::Ed25519, machine.device_id()), &DeviceKeyId::from_parts(DeviceKeyAlgorithm::Ed25519, machine.device_id()),
ed25519_key, ed25519_key,
@ -1865,7 +1870,8 @@ pub(crate) mod test {
); );
assert!(ret.is_ok()); assert!(ret.is_ok());
let ret = verify_json( let utility = Utility::new();
let ret = utility.verify_json(
&machine.user_id, &machine.user_id,
&DeviceKeyId::from_parts(DeviceKeyAlgorithm::Ed25519, machine.device_id()), &DeviceKeyId::from_parts(DeviceKeyAlgorithm::Ed25519, machine.device_id()),
ed25519_key, ed25519_key,

View File

@ -20,6 +20,7 @@
mod account; mod account;
mod group_sessions; mod group_sessions;
mod session; mod session;
mod utility;
pub use account::{Account, AccountPickle, IdentityKeys, PickledAccount}; pub use account::{Account, AccountPickle, IdentityKeys, PickledAccount};
pub use group_sessions::{ pub use group_sessions::{
@ -29,6 +30,7 @@ pub(crate) use group_sessions::{GroupSessionKey, OutboundGroupSession};
pub use olm_rs::PicklingMode; pub use olm_rs::PicklingMode;
pub(crate) use session::OlmMessage; pub(crate) use session::OlmMessage;
pub use session::{PickledSession, Session, SessionPickle}; pub use session::{PickledSession, Session, SessionPickle};
pub(crate) use utility::Utility;
#[cfg(test)] #[cfg(test)]
pub(crate) mod test { pub(crate) mod test {

View File

@ -0,0 +1,98 @@
// 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::utility::OlmUtility;
use serde_json::Value;
use matrix_sdk_common::identifiers::{DeviceKeyAlgorithm, DeviceKeyId, UserId};
use crate::error::SignatureError;
pub(crate) struct Utility {
inner: OlmUtility,
}
impl Utility {
pub fn new() -> Self {
Self {
inner: OlmUtility::new(),
}
}
/// Verify a signed JSON object.
///
/// The object must have a signatures key associated with an object of the
/// form `user_id: {key_id: signature}`.
///
/// Returns Ok if the signature was successfully verified, otherwise an
/// SignatureError.
///
/// # Arguments
///
/// * `user_id` - The user who signed the JSON object.
///
/// * `key_id` - The id of the key that signed the JSON object.
///
/// * `signing_key` - The public ed25519 key which was used to sign the JSON
/// object.
///
/// * `json` - The JSON object that should be verified.
pub(crate) fn verify_json(
&self,
user_id: &UserId,
key_id: &DeviceKeyId,
signing_key: &str,
json: &mut Value,
) -> Result<(), SignatureError> {
if key_id.algorithm() != DeviceKeyAlgorithm::Ed25519 {
return Err(SignatureError::UnsupportedAlgorithm);
}
let json_object = json.as_object_mut().ok_or(SignatureError::NotAnObject)?;
let unsigned = json_object.remove("unsigned");
let signatures = json_object.remove("signatures");
let canonical_json = cjson::to_string(json_object)?;
if let Some(u) = unsigned {
json_object.insert("unsigned".to_string(), u);
}
let signatures = signatures.ok_or(SignatureError::NoSignatureFound)?;
let signature_object = signatures
.as_object()
.ok_or(SignatureError::NoSignatureFound)?;
let signature = signature_object
.get(user_id.as_str())
.ok_or(SignatureError::NoSignatureFound)?;
let signature = signature
.get(key_id.to_string())
.ok_or(SignatureError::NoSignatureFound)?;
let signature = signature.as_str().ok_or(SignatureError::NoSignatureFound)?;
let ret = if self
.inner
.ed25519_verify(signing_key, &canonical_json, signature)
.is_ok()
{
Ok(())
} else {
Err(SignatureError::VerificationError)
};
json_object.insert("signatures".to_string(), signatures);
ret
}
}