events: Add support for custom SecretStorageEncryptionAlgorithm
This commit is contained in:
parent
9696e9083f
commit
cf4a5d263f
@ -69,6 +69,7 @@ Improvements:
|
|||||||
- `RedactedRoomPowerLevelsEventContent`,
|
- `RedactedRoomPowerLevelsEventContent`,
|
||||||
- `RedactedRoomMemberEventContent`
|
- `RedactedRoomMemberEventContent`
|
||||||
- Add `RoomMessageEventContent::make_reply_to_raw` to build replies to any event
|
- Add `RoomMessageEventContent::make_reply_to_raw` to build replies to any event
|
||||||
|
- Add support for custom `SecretStorageEncryptionAlgorithm`
|
||||||
|
|
||||||
# 0.26.1
|
# 0.26.1
|
||||||
|
|
||||||
|
@ -2,9 +2,17 @@
|
|||||||
//!
|
//!
|
||||||
//! [`m.secret_storage.key.*`]: https://spec.matrix.org/latest/client-server-api/#key-storage
|
//! [`m.secret_storage.key.*`]: https://spec.matrix.org/latest/client-server-api/#key-storage
|
||||||
|
|
||||||
|
use std::borrow::Cow;
|
||||||
|
|
||||||
use js_int::{uint, UInt};
|
use js_int::{uint, UInt};
|
||||||
use ruma_common::{serde::Base64, KeyDerivationAlgorithm};
|
use ruma_common::{
|
||||||
|
serde::{Base64, JsonObject},
|
||||||
|
KeyDerivationAlgorithm,
|
||||||
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use serde_json::Value as JsonValue;
|
||||||
|
|
||||||
|
mod secret_encryption_algorithm_serde;
|
||||||
|
|
||||||
use crate::macros::EventContent;
|
use crate::macros::EventContent;
|
||||||
|
|
||||||
@ -77,16 +85,49 @@ impl SecretStorageKeyEventContent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// An algorithm and its properties, used to encrypt a secret.
|
/// An algorithm and its properties, used to encrypt a secret.
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
#[derive(Debug, Clone)]
|
||||||
#[serde(tag = "algorithm")]
|
|
||||||
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
||||||
pub enum SecretStorageEncryptionAlgorithm {
|
pub enum SecretStorageEncryptionAlgorithm {
|
||||||
#[serde(rename = "m.secret_storage.v1.aes-hmac-sha2")]
|
|
||||||
/// Encrypted using the `m.secret_storage.v1.aes-hmac-sha2` algorithm.
|
/// Encrypted using the `m.secret_storage.v1.aes-hmac-sha2` algorithm.
|
||||||
///
|
///
|
||||||
/// Secrets using this method are encrypted using AES-CTR-256 and authenticated using
|
/// Secrets using this method are encrypted using AES-CTR-256 and authenticated using
|
||||||
/// HMAC-SHA-256.
|
/// HMAC-SHA-256.
|
||||||
V1AesHmacSha2(SecretStorageV1AesHmacSha2Properties),
|
V1AesHmacSha2(SecretStorageV1AesHmacSha2Properties),
|
||||||
|
|
||||||
|
/// Encrypted using a custom algorithm.
|
||||||
|
#[doc(hidden)]
|
||||||
|
_Custom(CustomSecretEncryptionAlgorithm),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SecretStorageEncryptionAlgorithm {
|
||||||
|
/// The `algorithm` string.
|
||||||
|
pub fn algorithm(&self) -> &str {
|
||||||
|
match self {
|
||||||
|
Self::V1AesHmacSha2(_) => "m.secret_storage.v1.aes-hmac-sha2",
|
||||||
|
Self::_Custom(c) => &c.algorithm,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The algorithm-specific properties.
|
||||||
|
///
|
||||||
|
/// The returned JSON object won't contain the `algorithm` field, use [`Self::algorithm()`] to
|
||||||
|
/// access it.
|
||||||
|
///
|
||||||
|
/// Prefer to use the public variants of `SecretStorageEncryptionAlgorithm` where possible; this
|
||||||
|
/// method is meant to be used for custom algorithms only.
|
||||||
|
pub fn properties(&self) -> Cow<'_, JsonObject> {
|
||||||
|
fn serialize<T: Serialize>(obj: &T) -> JsonObject {
|
||||||
|
match serde_json::to_value(obj).expect("secret properties serialization to succeed") {
|
||||||
|
JsonValue::Object(obj) => obj,
|
||||||
|
_ => panic!("all secret properties must serialize to objects"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match self {
|
||||||
|
Self::V1AesHmacSha2(p) => Cow::Owned(serialize(p)),
|
||||||
|
Self::_Custom(c) => Cow::Borrowed(&c.properties),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The key properties for the `m.secret_storage.v1.aes-hmac-sha2` algorithm.
|
/// The key properties for the `m.secret_storage.v1.aes-hmac-sha2` algorithm.
|
||||||
@ -108,6 +149,18 @@ impl SecretStorageV1AesHmacSha2Properties {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The payload for a custom secret encryption algorithm.
|
||||||
|
#[doc(hidden)]
|
||||||
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||||
|
pub struct CustomSecretEncryptionAlgorithm {
|
||||||
|
/// The encryption algorithm to be used for the key.
|
||||||
|
algorithm: String,
|
||||||
|
|
||||||
|
/// Algorithm-specific properties.
|
||||||
|
#[serde(flatten)]
|
||||||
|
properties: JsonObject,
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use assert_matches2::assert_matches;
|
use assert_matches2::assert_matches;
|
||||||
@ -125,7 +178,7 @@ mod tests {
|
|||||||
use crate::{EventContentFromType, GlobalAccountDataEvent};
|
use crate::{EventContentFromType, GlobalAccountDataEvent};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_key_description_serialization() {
|
fn key_description_serialization() {
|
||||||
let mut content = SecretStorageKeyEventContent::new(
|
let mut content = SecretStorageKeyEventContent::new(
|
||||||
"my_key".into(),
|
"my_key".into(),
|
||||||
SecretStorageEncryptionAlgorithm::V1AesHmacSha2(SecretStorageV1AesHmacSha2Properties {
|
SecretStorageEncryptionAlgorithm::V1AesHmacSha2(SecretStorageV1AesHmacSha2Properties {
|
||||||
@ -146,7 +199,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_key_description_deserialization() {
|
fn key_description_deserialization() {
|
||||||
let json = to_raw_json_value(&json!({
|
let json = to_raw_json_value(&json!({
|
||||||
"name": "my_key",
|
"name": "my_key",
|
||||||
"algorithm": "m.secret_storage.v1.aes-hmac-sha2",
|
"algorithm": "m.secret_storage.v1.aes-hmac-sha2",
|
||||||
@ -172,7 +225,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_key_description_deserialization_without_name() {
|
fn key_description_deserialization_without_name() {
|
||||||
let json = to_raw_json_value(&json!({
|
let json = to_raw_json_value(&json!({
|
||||||
"algorithm": "m.secret_storage.v1.aes-hmac-sha2",
|
"algorithm": "m.secret_storage.v1.aes-hmac-sha2",
|
||||||
"iv": "YWJjZGVmZ2hpamtsbW5vcA",
|
"iv": "YWJjZGVmZ2hpamtsbW5vcA",
|
||||||
@ -197,7 +250,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_key_description_with_passphrase_serialization() {
|
fn key_description_with_passphrase_serialization() {
|
||||||
let mut content = SecretStorageKeyEventContent {
|
let mut content = SecretStorageKeyEventContent {
|
||||||
passphrase: Some(PassPhrase::new("rocksalt".into(), uint!(8))),
|
passphrase: Some(PassPhrase::new("rocksalt".into(), uint!(8))),
|
||||||
..SecretStorageKeyEventContent::new(
|
..SecretStorageKeyEventContent::new(
|
||||||
@ -228,7 +281,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_key_description_with_passphrase_deserialization() {
|
fn key_description_with_passphrase_deserialization() {
|
||||||
let json = to_raw_json_value(&json!({
|
let json = to_raw_json_value(&json!({
|
||||||
"name": "my_key",
|
"name": "my_key",
|
||||||
"algorithm": "m.secret_storage.v1.aes-hmac-sha2",
|
"algorithm": "m.secret_storage.v1.aes-hmac-sha2",
|
||||||
@ -265,7 +318,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_event_serialization() {
|
fn event_serialization() {
|
||||||
let mut content = SecretStorageKeyEventContent::new(
|
let mut content = SecretStorageKeyEventContent::new(
|
||||||
"my_key_id".into(),
|
"my_key_id".into(),
|
||||||
SecretStorageEncryptionAlgorithm::V1AesHmacSha2(SecretStorageV1AesHmacSha2Properties {
|
SecretStorageEncryptionAlgorithm::V1AesHmacSha2(SecretStorageV1AesHmacSha2Properties {
|
||||||
@ -286,7 +339,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_event_deserialization() {
|
fn event_deserialization() {
|
||||||
let json = json!({
|
let json = json!({
|
||||||
"type": "m.secret_storage.key.my_key_id",
|
"type": "m.secret_storage.key.my_key_id",
|
||||||
"content": {
|
"content": {
|
||||||
@ -313,4 +366,33 @@ mod tests {
|
|||||||
assert_eq!(iv.encode(), "YWJjZGVmZ2hpamtsbW5vcA");
|
assert_eq!(iv.encode(), "YWJjZGVmZ2hpamtsbW5vcA");
|
||||||
assert_eq!(mac.encode(), "aWRvbnRrbm93d2hhdGFtYWNsb29rc2xpa2U");
|
assert_eq!(mac.encode(), "aWRvbnRrbm93d2hhdGFtYWNsb29rc2xpa2U");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn custom_algorithm_key_description_deserialization() {
|
||||||
|
let json = to_raw_json_value(&json!({
|
||||||
|
"name": "my_key",
|
||||||
|
"algorithm": "io.ruma.custom_alg",
|
||||||
|
"io.ruma.custom_prop1": "YWJjZGVmZ2hpamtsbW5vcA",
|
||||||
|
"io.ruma.custom_prop2": "aWRvbnRrbm93d2hhdGFtYWNsb29rc2xpa2U"
|
||||||
|
}))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let content =
|
||||||
|
SecretStorageKeyEventContent::from_parts("m.secret_storage.key.test", &json).unwrap();
|
||||||
|
assert_eq!(content.name.unwrap(), "my_key");
|
||||||
|
assert_matches!(content.passphrase, None);
|
||||||
|
|
||||||
|
let algorithm = content.algorithm;
|
||||||
|
assert_eq!(algorithm.algorithm(), "io.ruma.custom_alg");
|
||||||
|
let properties = algorithm.properties();
|
||||||
|
assert_eq!(properties.len(), 2);
|
||||||
|
assert_eq!(
|
||||||
|
properties.get("io.ruma.custom_prop1").unwrap().as_str(),
|
||||||
|
Some("YWJjZGVmZ2hpamtsbW5vcA")
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
properties.get("io.ruma.custom_prop2").unwrap().as_str(),
|
||||||
|
Some("aWRvbnRrbm93d2hhdGFtYWNsb29rc2xpa2U")
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,64 @@
|
|||||||
|
use serde::{de, Deserialize, Serialize};
|
||||||
|
|
||||||
|
use super::{
|
||||||
|
CustomSecretEncryptionAlgorithm, SecretStorageEncryptionAlgorithm,
|
||||||
|
SecretStorageV1AesHmacSha2Properties,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
enum SecretStorageEncryptionAlgorithmDeHelper {
|
||||||
|
Known(KnownSecretStorageEncryptionAlgorithmDeHelper),
|
||||||
|
Unknown(CustomSecretEncryptionAlgorithm),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
#[serde(tag = "algorithm")]
|
||||||
|
enum KnownSecretStorageEncryptionAlgorithmDeHelper {
|
||||||
|
#[serde(rename = "m.secret_storage.v1.aes-hmac-sha2")]
|
||||||
|
V1AesHmacSha2(SecretStorageV1AesHmacSha2Properties),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> Deserialize<'de> for SecretStorageEncryptionAlgorithm {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: de::Deserializer<'de>,
|
||||||
|
{
|
||||||
|
let helper = SecretStorageEncryptionAlgorithmDeHelper::deserialize(deserializer)?;
|
||||||
|
|
||||||
|
Ok(match helper {
|
||||||
|
SecretStorageEncryptionAlgorithmDeHelper::Known(k) => match k {
|
||||||
|
KnownSecretStorageEncryptionAlgorithmDeHelper::V1AesHmacSha2(p) => {
|
||||||
|
Self::V1AesHmacSha2(p)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
SecretStorageEncryptionAlgorithmDeHelper::Unknown(c) => Self::_Custom(c),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
struct SecretStorageEncryptionAlgorithmSerHelper<'a, T: Serialize> {
|
||||||
|
algorithm: &'a str,
|
||||||
|
#[serde(flatten)]
|
||||||
|
properties: T,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serialize for SecretStorageEncryptionAlgorithm {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: serde::Serializer,
|
||||||
|
{
|
||||||
|
let algorithm = self.algorithm();
|
||||||
|
match self {
|
||||||
|
Self::V1AesHmacSha2(properties) => {
|
||||||
|
SecretStorageEncryptionAlgorithmSerHelper { algorithm, properties }
|
||||||
|
.serialize(serializer)
|
||||||
|
}
|
||||||
|
Self::_Custom(properties) => {
|
||||||
|
SecretStorageEncryptionAlgorithmSerHelper { algorithm, properties }
|
||||||
|
.serialize(serializer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user