use std::{collections::BTreeMap, default::Default};
use insta::{assert_json_snapshot, with_settings};
use ruma::{
CanonicalJsonValue, CrossSigningKeyId, CrossSigningOrDeviceSignatures,
CrossSigningOrDeviceSigningKeyId, DeviceId, OwnedBase64PublicKey,
OwnedBase64PublicKeyOrDeviceId, OwnedDeviceId, OwnedUserId, SigningKeyAlgorithm, UserId,
api::client::keys::get_keys::v3::Response as KeyQueryResponse,
device_id,
encryption::{CrossSigningKey, DeviceKeys, KeyUsage},
serde::Raw,
user_id,
};
use serde_json::{Value, json};
use vodozemac::{Curve25519PublicKey, Ed25519PublicKey, Ed25519SecretKey, Ed25519Signature};
use super::keys_query::{KeysQueryUser, keys_query, master_keys};
use crate::{
ruma_response_from_json, ruma_response_to_json,
test_json::keys_query::{device_keys_payload, self_signing_keys},
};
pub struct KeyQueryResponseTemplate {
user_id: OwnedUserId,
master_cross_signing_key: Option<Ed25519SecretKey>,
self_signing_key: Option<Ed25519SecretKey>,
user_signing_key: Option<Ed25519SecretKey>,
master_cross_signing_key_json: Option<CrossSigningKey>,
device_keys: BTreeMap<OwnedDeviceId, Raw<DeviceKeys>>,
}
impl KeyQueryResponseTemplate {
pub fn new(user_id: OwnedUserId) -> Self {
KeyQueryResponseTemplate {
user_id,
master_cross_signing_key: None,
self_signing_key: None,
user_signing_key: None,
master_cross_signing_key_json: None,
device_keys: Default::default(),
}
}
pub fn with_cross_signing_keys(
mut self,
master_cross_signing_key: Ed25519SecretKey,
self_signing_key: Ed25519SecretKey,
user_signing_key: Ed25519SecretKey,
) -> Self {
let master_public_key = master_cross_signing_key.public_key();
self.master_cross_signing_key = Some(master_cross_signing_key);
self.self_signing_key = Some(self_signing_key);
self.user_signing_key = Some(user_signing_key);
self.master_cross_signing_key_json =
Some(self.signed_cross_signing_key(&master_public_key, KeyUsage::Master));
self
}
pub fn with_device(
mut self,
device_id: &DeviceId,
curve25519_public_key: &Curve25519PublicKey,
ed25519_secret_key: &Ed25519SecretKey,
options: KeyQueryResponseTemplateDeviceOptions,
) -> Self {
let mut device_keys = json!({
"algorithms": [
"m.olm.v1.curve25519-aes-sha2",
"m.megolm.v1.aes-sha2"
],
"device_id": device_id.to_owned(),
"keys": {
format!("curve25519:{device_id}"): curve25519_public_key.to_base64(),
format!("ed25519:{device_id}"): ed25519_secret_key.public_key().to_base64(),
},
"signatures": {},
"user_id": self.user_id.clone(),
});
if options.dehydrated {
device_keys["dehydrated"] = Value::Bool(true);
}
sign_json(&mut device_keys, ed25519_secret_key, &self.user_id, device_id.as_str());
if options.verified {
let ssk = self
.self_signing_key
.as_ref()
.expect("must call with_cross_signing_keys() before creating cross-signed device");
sign_json(&mut device_keys, ssk, &self.user_id, &ssk.public_key().to_base64());
}
let raw_device_keys = serde_json::from_value(device_keys).unwrap();
self.device_keys.insert(device_id.to_owned(), raw_device_keys);
self
}
pub fn with_user_verification_signature(
mut self,
signing_user_id: &UserId,
signing_user_user_signing_key: &Ed25519SecretKey,
) -> Self {
let master_key = self.master_cross_signing_key_json.as_mut().expect(
"must call with_cross_signing_key() before calling 'with_user_verification_signature'",
);
sign_cross_signing_key(master_key, signing_user_user_signing_key, signing_user_id);
self
}
pub fn build_response(&self) -> KeyQueryResponse {
let mut response = KeyQueryResponse::default();
if !self.device_keys.is_empty() {
response.device_keys =
BTreeMap::from([(self.user_id.clone(), self.device_keys.clone())]);
}
if let Some(master_key) = &self.master_cross_signing_key_json {
response.master_keys.insert(
self.user_id.clone(),
Raw::new(master_key).expect("unable to serialize msk"),
);
}
if let Some(self_signing_key) = &self.self_signing_key {
let ssk = self
.signed_cross_signing_key(&self_signing_key.public_key(), KeyUsage::SelfSigning);
response
.self_signing_keys
.insert(self.user_id.clone(), Raw::new(&ssk).expect("unable to serialize ssk"));
}
if let Some(user_signing_key) = &self.user_signing_key {
let usk = self
.signed_cross_signing_key(&user_signing_key.public_key(), KeyUsage::UserSigning);
response
.user_signing_keys
.insert(self.user_id.clone(), Raw::new(&usk).expect("unable to serialize usk"));
}
response
}
fn signed_cross_signing_key(
&self,
public_key: &Ed25519PublicKey,
key_usage: KeyUsage,
) -> CrossSigningKey {
let public_key_base64 = OwnedBase64PublicKey::with_bytes(public_key.as_bytes());
let mut key = CrossSigningKey::new(
self.user_id.clone(),
vec![key_usage],
BTreeMap::from([(
CrossSigningKeyId::from_parts(SigningKeyAlgorithm::Ed25519, &public_key_base64),
public_key_base64.to_string(),
)]),
CrossSigningOrDeviceSignatures::new(),
);
let master_key = self
.master_cross_signing_key
.as_ref()
.expect("must set master key before calling `signed_cross_signing_key`");
sign_cross_signing_key(&mut key, master_key, &self.user_id);
key
}
}
#[derive(Default)]
pub struct KeyQueryResponseTemplateDeviceOptions {
verified: bool,
dehydrated: bool,
}
impl KeyQueryResponseTemplateDeviceOptions {
pub fn new() -> Self {
Self::default()
}
pub fn verified(mut self, verified: bool) -> Self {
self.verified = verified;
self
}
pub fn dehydrated(mut self, dehydrated: bool) -> Self {
self.dehydrated = dehydrated;
self
}
}
pub struct KeyDistributionTestData {}
impl KeyDistributionTestData {
pub const MASTER_KEY_PRIVATE_EXPORT: &'static str =
"9kquJqAtEUoTXljh5W2QSsCm4FH9WvWzIkDkIMUsM2k";
pub const SELF_SIGNING_KEY_PRIVATE_EXPORT: &'static str =
"QifnGfudByh/GpBgJYEMzq7/DGbp6fZjp58faQj3n1M";
pub const USER_SIGNING_KEY_PRIVATE_EXPORT: &'static str =
"zQSosK46giUFs2ACsaf32bA7drcIXbmViyEt+TLfloI";
pub fn me_private_user_signing_key() -> Ed25519SecretKey {
Ed25519SecretKey::from_base64(Self::USER_SIGNING_KEY_PRIVATE_EXPORT).unwrap()
}
pub fn me_keys_query_response() -> KeyQueryResponse {
let builder = KeyQueryResponseTemplate::new(Self::me_id().to_owned())
.with_cross_signing_keys(
Ed25519SecretKey::from_base64(Self::MASTER_KEY_PRIVATE_EXPORT).unwrap(),
Ed25519SecretKey::from_base64(Self::SELF_SIGNING_KEY_PRIVATE_EXPORT).unwrap(),
Self::me_private_user_signing_key(),
);
let response = builder.build_response();
with_settings!({sort_maps => true}, {
assert_json_snapshot!(
"KeyDistributionTestData__me_keys_query_response",
ruma_response_to_json(response.clone()),
);
});
response
}
const DAN_PRIVATE_MASTER_CROSS_SIGNING_KEY: &'static str =
"QGZo39k199RM0NYvPvFNXBspc5llftHWKKHqEi25q0U";
const DAN_PRIVATE_SELF_SIGNING_KEY: &'static str =
"0ES1HO5VXpy/BsXxadwsk6QcwH/ci99KkV9ZlPakHlU";
const DAN_PRIVATE_USER_SIGNING_KEY: &'static str =
"vSdfrHJO8sZH/54r1uCg8BE0CdcDVGkPQNOu7Ej8BBs";
pub fn dan_keys_query_response() -> KeyQueryResponse {
let response = Self::dan_keys_query_response_common();
with_settings!({sort_maps => true}, {
assert_json_snapshot!(
"KeyDistributionTestData__dan_keys_query_response",
ruma_response_to_json(response.clone()),
);
});
response
}
pub fn dan_keys_query_response_device_loggedout() -> KeyQueryResponse {
let mut response = Self::dan_keys_query_response_common();
response
.device_keys
.get_mut(Self::dan_id())
.unwrap()
.remove(Self::dan_unsigned_device_id());
with_settings!({sort_maps => true}, {
assert_json_snapshot!(
"KeyDistributionTestData__dan_keys_query_response_device_loggedout",
ruma_response_to_json(response.clone()),
);
});
response
}
fn dan_keys_query_response_common() -> KeyQueryResponse {
let builder = KeyQueryResponseTemplate::new(Self::dan_id().to_owned())
.with_cross_signing_keys(
Ed25519SecretKey::from_base64(Self::DAN_PRIVATE_MASTER_CROSS_SIGNING_KEY).unwrap(),
Ed25519SecretKey::from_base64(Self::DAN_PRIVATE_SELF_SIGNING_KEY).unwrap(),
Ed25519SecretKey::from_base64(Self::DAN_PRIVATE_USER_SIGNING_KEY).unwrap(),
)
.with_user_verification_signature(Self::me_id(), &Self::me_private_user_signing_key());
let builder = builder.with_device(
Self::dan_signed_device_id(),
&Curve25519PublicKey::from_base64("PBo2nKbink/HxgzMrBftGPogsD0d47LlIMsViTpCRn4")
.unwrap(),
&Ed25519SecretKey::from_base64("yzj53Kccfqx2yx9lcTwaRfPZX+7jU19harsDWWu5YnM").unwrap(),
KeyQueryResponseTemplateDeviceOptions::new().verified(true),
);
let builder = builder.with_device(
Self::dan_unsigned_device_id(),
&Curve25519PublicKey::from_base64("Hc/BC/xyQIEnScyZkEk+ilDMfOARxHMFoEcggPqqRw4")
.unwrap(),
&Ed25519SecretKey::from_base64("/SlFtNKxTPN+i4pHzSPWZ1Oc6ymMB33sS32GXZkaLos").unwrap(),
KeyQueryResponseTemplateDeviceOptions::new(),
);
builder.build_response()
}
pub fn dave_keys_query_response() -> KeyQueryResponse {
let data = json!({
"device_keys": {
"@dave:localhost": {
"HVCXJTHMBM": {
"algorithms": [
"m.olm.v1.curve25519-aes-sha2",
"m.megolm.v1.aes-sha2"
],
"device_id": "HVCXJTHMBM",
"keys": {
"curve25519:HVCXJTHMBM": "0GPOoQwhAGVu1lIvOZway3/XjdxVNHEi5z/4by8TzxU",
"ed25519:HVCXJTHMBM": "/4ZzD1Ou70/Ojj5aaPqBopCN8SzQpKM7itiWZ/07fXc"
},
"signatures": {
"@dave:localhost": {
"ed25519:HVCXJTHMBM": "b1DV7xN2My2oXbZVVtTeJR9hzXIg1Cx4h+W51+tVq5GAoSYtrWR31PyKPROk28CvQ9Pu++/jdomaW7/oYPxoCg",
}
},
"user_id": "@dave:localhost",
}
}
}
});
ruma_response_from_json(&data)
}
pub fn good_keys_query_response() -> KeyQueryResponse {
let data = json!({
"device_keys": {
"@good:localhost": {
"JAXGBVZYLA": {
"algorithms": [
"m.olm.v1.curve25519-aes-sha2",
"m.megolm.v1.aes-sha2"
],
"device_id": "JAXGBVZYLA",
"keys": {
"curve25519:JAXGBVZYLA": "a4vWxnHUKvELfB7WYLCW07vEbwybZReyKReWHxQhgW0",
"ed25519:JAXGBVZYLA": "m22nVxqJK72iph+FhOMqX/MDd7AoF9BJ033MlMLnDCg"
},
"signatures": {
"@good:localhost": {
"ed25519:JAXGBVZYLA": "EXKQiXNKjWSE76WxF8TUvxjCyw/qsV27gcbsgpSN1zzHzGzVdY1Qr4EB8t/76SL5rZP/9hqcAvqPSJW/N7iKCg",
"ed25519:YwQVBWn2sA5lLqp/dQsNk7fiYOuQuQhujefOPjejc+U": "sXJUXKE7hqXnsNbqlzS/1MGlGmeJU54v6/UMWAs+6bCzOFUC1+uqU1KlzfmpsVG3MKxR4r/ZLZdxoKVfUuQMAA"
}
},
"user_id": "@good:localhost"
},
"ZGLCFWEPCY": {
"algorithms": [
"m.olm.v1.curve25519-aes-sha2",
"m.megolm.v1.aes-sha2"
],
"device_id": "ZGLCFWEPCY",
"keys": {
"curve25519:ZGLCFWEPCY": "kfcIEf6ZRgTP184yuIJYabfsBFsGXiVQE/cyW9qYnQA",
"ed25519:ZGLCFWEPCY": "WLSA1tSe0eOZCeESH5WMb9cp3AgRZzm4ooSud+NwcEw"
},
"signatures": {
"@good:localhost": {
"ed25519:ZGLCFWEPCY": "AVXFgHk/QcAbOVBF5Xu4OW+03CZKBs2qAYh0fjIA49r+X+aX7QIKrbRyXU/ictPBLMpj1yXF+2J5vwR/KQYVCA",
"ed25519:YwQVBWn2sA5lLqp/dQsNk7fiYOuQuQhujefOPjejc+U": "VZk70FWiYN/YSwGykt2CygcOl1bq2D+dVSSKBL5GA5uHXxt6ypDlYvtWprM1l7re3llp5j105MevsjQ+2sWmCw"
}
},
"user_id": "@good:localhost"
}
}
},
"failures": {},
"master_keys": {
"@good:localhost": {
"keys": {
"ed25519:5vTK2S2wVXo4xGT4BhcwpINVjRLjorkkJgCjnrHgtl8": "5vTK2S2wVXo4xGT4BhcwpINVjRLjorkkJgCjnrHgtl8"
},
"signatures": {
"@good:localhost": {
"ed25519:5vTK2S2wVXo4xGT4BhcwpINVjRLjorkkJgCjnrHgtl8": "imAhrTIlPuf6hNqlbcSUnC2ndZPk5NwQLzbi9kZ8nmnPGjmv39f4U4Vh/KiweqQnI4ActGpcYyM7k9S2Ef8/CQ",
"ed25519:HPNYOQGUEE": "6w3egsvd+oVPCclef+hF1CfFMZrGTf/plFvPU5iP69WNw4w0UPAKSV1jOzh7Wv4LVGX5O3afjA9DG+O7aHZmBw"
}
},
"usage": [
"master"
],
"user_id": "@good:localhost"
}
},
"self_signing_keys": {
"@good:localhost": {
"keys": {
"ed25519:YwQVBWn2sA5lLqp/dQsNk7fiYOuQuQhujefOPjejc+U": "YwQVBWn2sA5lLqp/dQsNk7fiYOuQuQhujefOPjejc+U"
},
"signatures": {
"@good:localhost": {
"ed25519:5vTK2S2wVXo4xGT4BhcwpINVjRLjorkkJgCjnrHgtl8": "2AyR8lovFv8J1DwPwdCsAM9Tw877QhaVHmVkPopsmSokS2fst8LDQtsg/PiftVc+74NGz5tnYIMDxn4BjAisAg"
}
},
"usage": [
"self_signing"
],
"user_id": "@good:localhost"
}
},
"user_signing_keys": {
"@good:localhost": {
"keys": {
"ed25519:u1PwO3/a/HTnN9IF7BVa2dJQ7bc00J22eNS0vM4FjTA": "u1PwO3/a/HTnN9IF7BVa2dJQ7bc00J22eNS0vM4FjTA"
},
"signatures": {
"@good:localhost": {
"ed25519:5vTK2S2wVXo4xGT4BhcwpINVjRLjorkkJgCjnrHgtl8": "88v9/Z3TJeY2lsu3cFQaEuhHH5ixjJs22ALQRKY+O6VPGCT/BAzH6kUb7teinFfpvQjoXN3t5fVJxbP9mVlxDg"
}
},
"usage": [
"user_signing"
],
"user_id": "@good:localhost"
}
}
});
ruma_response_from_json(&data)
}
pub fn me_id() -> &'static UserId {
user_id!("@me:localhost")
}
pub fn me_device_id() -> &'static DeviceId {
device_id!("ABCDEFGH")
}
pub fn dan_unsigned_device_id() -> &'static DeviceId {
device_id!("FRGNMZVOKA")
}
pub fn dan_signed_device_id() -> &'static DeviceId {
device_id!("JHPUERYQUW")
}
pub fn dave_device_id() -> &'static DeviceId {
device_id!("HVCXJTHMBM")
}
pub fn dan_id() -> &'static UserId {
user_id!("@dan:localhost")
}
pub fn dave_id() -> &'static UserId {
user_id!("@dave:localhost")
}
pub fn good_id() -> &'static UserId {
user_id!("@good:localhost")
}
pub fn good_device_1_id() -> &'static DeviceId {
device_id!("JAXGBVZYLA")
}
pub fn good_device_2_id() -> &'static DeviceId {
device_id!("ZGLCFWEPCY")
}
}
pub struct IdentityChangeDataSet {}
impl IdentityChangeDataSet {
pub fn user_id() -> &'static UserId {
assert_eq!(KeysQueryUser::bob_a().user_id, KeysQueryUser::bob_b().user_id);
assert_eq!(KeysQueryUser::bob_a().user_id, KeysQueryUser::bob_c().user_id);
KeysQueryUser::bob_a().user_id
}
pub fn device_a() -> &'static DeviceId {
KeysQueryUser::bob_a().device_id
}
pub fn device_b() -> &'static DeviceId {
KeysQueryUser::bob_b().device_id
}
pub fn device_c() -> &'static DeviceId {
KeysQueryUser::bob_c().device_id
}
pub fn master_signing_keys_a() -> Value {
master_keys(&KeysQueryUser::bob_a())
}
pub fn self_signing_keys_a() -> Value {
self_signing_keys(&KeysQueryUser::bob_a())
}
pub fn key_query_with_identity_a() -> KeyQueryResponse {
keys_query(&KeysQueryUser::bob_a(), &[])
}
pub fn master_signing_keys_b() -> Value {
master_keys(&KeysQueryUser::bob_b())
}
pub fn self_signing_keys_b() -> Value {
self_signing_keys(&KeysQueryUser::bob_b())
}
pub fn device_keys_payload_2_signed_by_b() -> Value {
device_keys_payload(&KeysQueryUser::bob_b())
}
pub fn key_query_with_identity_b() -> KeyQueryResponse {
keys_query(&KeysQueryUser::bob_b(), &[KeysQueryUser::bob_a()])
}
pub fn key_query_with_identity_no_identity() -> KeyQueryResponse {
keys_query(&KeysQueryUser::bob_c(), &[KeysQueryUser::bob_a(), KeysQueryUser::bob_b()])
}
}
pub struct VerificationViolationTestData {}
impl VerificationViolationTestData {
pub const MASTER_KEY_PRIVATE_EXPORT: &'static str =
"bSa0nVTocZArMzL7OLmeFUIVF4ycp64rrkVMgqOYg6Y";
pub const SELF_SIGNING_KEY_PRIVATE_EXPORT: &'static str =
"MQ7b3MDXvOEMDvIOWkuH1XCNUyqBLqbdd1bT00p8HPU";
pub const USER_SIGNING_KEY_PRIVATE_EXPORT: &'static str =
"v77s+TlT5/NbcQym2B7Rwf20HOAhyInF2p1ZUYDPtow";
pub fn own_id() -> &'static UserId {
user_id!("@alice:localhost")
}
pub fn bob_id() -> &'static UserId {
user_id!("@bob:localhost")
}
pub fn carol_id() -> &'static UserId {
user_id!("@carol:localhost")
}
pub fn own_keys_query_response_1() -> KeyQueryResponse {
let builder = KeyQueryResponseTemplate::new(Self::own_id().to_owned())
.with_cross_signing_keys(
Ed25519SecretKey::from_base64(Self::MASTER_KEY_PRIVATE_EXPORT).unwrap(),
Ed25519SecretKey::from_base64(Self::SELF_SIGNING_KEY_PRIVATE_EXPORT).unwrap(),
Ed25519SecretKey::from_base64(Self::USER_SIGNING_KEY_PRIVATE_EXPORT).unwrap(),
);
let response = builder.build_response();
with_settings!({sort_maps => true}, {
assert_json_snapshot!(
"VerificationViolationTestData__own_keys_query_response_1",
ruma_response_to_json(response.clone()),
);
});
response
}
pub fn own_keys_query_response_2() -> KeyQueryResponse {
let data = json!({
"master_keys": {
"@alice:localhost": {
"keys": { "ed25519:J+5An10v1vzZpAXTYFokD1/PEVccFnLC61EfRXit0UY": "J+5An10v1vzZpAXTYFokD1/PEVccFnLC61EfRXit0UY" },
"user_id": "@alice:localhost",
"usage": [ "master" ]
}
},
"self_signing_keys": {
"@alice:localhost": {
"keys": { "ed25519:aU2+2CyXQTCuDcmWW0EL2bhJ6PdjFW2LbAsbHqf02AY": "aU2+2CyXQTCuDcmWW0EL2bhJ6PdjFW2LbAsbHqf02AY" },
"user_id": "@alice:localhost",
"usage": [ "self_signing" ],
"signatures": {
"@alice:localhost": {
"ed25519:J+5An10v1vzZpAXTYFokD1/PEVccFnLC61EfRXit0UY": "XfhYEhZmOs8BJdb3viatILBZ/bElsHXEW28V4tIaY5CxrBR0YOym3yZHWmRmypXessHZAKOhZn3yBMXzdajyCw"
}
}
}
},
"user_signing_keys": {
"@alice:localhost": {
"keys": { "ed25519:g5TC/zjQXyZYuDLZv7a41z5fFVrXpYPypG//AFQj8hY": "g5TC/zjQXyZYuDLZv7a41z5fFVrXpYPypG//AFQj8hY" },
"user_id": "@alice:localhost",
"usage": [ "user_signing" ],
"signatures": {
"@alice:localhost": {
"ed25519:J+5An10v1vzZpAXTYFokD1/PEVccFnLC61EfRXit0UY": "6AkD1XM2H0/ebgP9oBdMKNeft7uxsrb0XN1CsjjHgeZCvCTMmv3BHlLiT/Hzy4fe8H+S1tr484dcXN/PIdnfDA"
}
}
}
}
});
ruma_response_from_json(&data)
}
pub fn own_unsigned_device_id() -> OwnedDeviceId {
Self::own_unsigned_device_keys().0
}
pub fn own_unsigned_device_keys() -> (OwnedDeviceId, Raw<DeviceKeys>) {
let json = json!({
"algorithms": [
"m.olm.v1.curve25519-aes-sha2",
"m.megolm.v1.aes-sha2"
],
"device_id": "AHIVRZICJK",
"keys": {
"curve25519:AHIVRZICJK": "3U73fbymtt6sn/H+5UYHiQxN2HfDmxzOMYZ+3JyPT2E",
"ed25519:AHIVRZICJK": "I0NV5nJYmnH+f5py4Fz2tdCeSKUChaaXV7m4UOq9bis"
},
"signatures": {
"@alice:localhost": {
"ed25519:AHIVRZICJK": "HIs13b2GizN8gdZrYLWs9KZbcmKubXE+O4716Uow513e84JO8REy53OX4TDdoBfmVhPiZg5CIRrUDH7JxY4wAQ"
}
},
"user_id": "@alice:localhost",
"unsigned": {
"device_display_name": "Element - dbg Android"
}
});
(device_id!("AHIVRZICJK").to_owned(), serde_json::from_value(json).unwrap())
}
pub fn own_signed_device_id() -> OwnedDeviceId {
Self::own_signed_device_keys().0
}
pub fn own_signed_device_keys() -> (OwnedDeviceId, Raw<DeviceKeys>) {
let json = json!({
"algorithms": [
"m.olm.v1.curve25519-aes-sha2",
"m.megolm.v1.aes-sha2"
],
"device_id": "LCNRWQAVWK",
"keys": {
"curve25519:LCNRWQAVWK": "fULFq9I6uYmsdDwRFU76wc43RqF7TVGvlWvKXhSrsS4",
"ed25519:LCNRWQAVWK": "F7E0EF0lzVJN31cnetLdeBuNvZ8jQqkUzt8/nGD9M/E"
},
"signatures": {
"@alice:localhost": {
"ed25519:LCNRWQAVWK": "8kLsN76ytGRuHKMgIARaOds29QrPRzQ6Px+FOLsYK/ATmx5IVd65MpSh2pGjLAaPsSGWR1WLbBTq/LZtcpjTDQ",
"ed25519:WXLer0esHUanp8DCeu2Be0xB5ms9aKFFBrCFl50COjw": "lo4Vuuu+WvPt1hnOCv30iS1y/cF7DljfFZYF3ib5JH/6iPZTW4jYdlmWo4a7hDf0fb2pu3EFnghYMr7vVx41Aw"
}
},
"user_id": "@alice:localhost",
"unsigned": {
"device_display_name": "develop.element.io: Chrome on macOS"
}
});
(device_id!("LCNRWQAVWK").to_owned(), serde_json::from_value(json).unwrap())
}
pub fn bob_keys_query_response_signed() -> KeyQueryResponse {
let data = json!({
"device_keys": {
"@bob:localhost": {
"RLZGZIHKMP": {
"algorithms": [
"m.olm.v1.curve25519-aes-sha2",
"m.megolm.v1.aes-sha2"
],
"device_id": "RLZGZIHKMP",
"keys": {
"curve25519:RLZGZIHKMP": "Zd8uO9Rr1PtqNno3//ybeUZ3JuqFtm17TQTWW0f47AU",
"ed25519:RLZGZIHKMP": "kH+Zn2m7LPES/XLOyVvnf8t4Byfj3mAbngHptHZFzk0"
},
"signatures": {
"@bob:localhost": {
"ed25519:RLZGZIHKMP": "w4MOkDiD+4XatQrRzGrcaqwVmiZrAjxmaIA8aSuzQveD2SJ2eVZq3OSpqx6QRUbG/gkkZxGmY13PkS/iAOv0AA",
"ed25519:e8JFSrW8LW3UK6SSXh2ZESUzptFbapr28/+WqndD+Xk": "ki+cV0EVe5cYXnzqU078qy1qu2rnaxaBQU+KwyvPpEUotNTXjWKUOJfxast42d5tjI5vsI5aiQ6XkYfjBJ74Bw"
}
},
"user_id": "@bob:localhost",
"unsigned": {}
},
"XCYNVRMTER": {
"algorithms": [
"m.olm.v1.curve25519-aes-sha2",
"m.megolm.v1.aes-sha2"
],
"device_id": "XCYNVRMTER",
"keys": {
"curve25519:XCYNVRMTER": "xGKYkFcHGlJ+I1yiefPyZu1EY8i2h1eed5uk3PAW6GA",
"ed25519:XCYNVRMTER": "EsU8MJzTYE+/VJs1K9HkGqb8UXCByPioynGrV28WocU"
},
"signatures": {
"@bob:localhost": {
"ed25519:XCYNVRMTER": "yZ7cpaoA+0rRx+bmklsP1iAd0eGPH6gsdywC11VE98/mrcbeFuxjQVn39Ds7h+vmciu5GRzwWgDgv+6go6FHAQ",
}
},
"user_id": "@bob:localhost",
"unsigned": {},
},
}
},
"failures": {},
"master_keys": {
"@bob:localhost": {
"keys": {
"ed25519:xZPyb4hxM8zaedDFz5m8HsDpX1fknd/V/69THLhNX9I": "xZPyb4hxM8zaedDFz5m8HsDpX1fknd/V/69THLhNX9I"
},
"signatures": {
"@bob:localhost": {
"ed25519:RLZGZIHKMP": "5bHLrx0HwYsNRtd65s1a1wVGlwgJU8yb8cq/Qbq04o9nVdQuY8+woQVWq9nxk59u6QFZIpFdVjXsuTPkDJLsBA",
"ed25519:xZPyb4hxM8zaedDFz5m8HsDpX1fknd/V/69THLhNX9I": "NA+cLNIPpmECcBIcmAH5l1K4IDXI6Xss1VmU8TZ04AYQSAh/2sv7NixEBO1/Raz0nErzkOl8gpRswHbHv1p7Dw"
},
"@alice:localhost": {
"ed25519:MXob/N/bYI7U2655O1/AI9NOX1245RnE03Nl4Hvf+u0": "n3X6afWYoSywqBpPlaDfQ2BNjl3ez5AzxEVwaB5/KEAzgwsq5B2qBW9N5uZaNWEq5M3JBrh0doj1FgUg4R3yBQ"
}
},
"usage": [
"master"
],
"user_id": "@bob:localhost"
}
},
"self_signing_keys": {
"@bob:localhost": {
"keys": {
"ed25519:e8JFSrW8LW3UK6SSXh2ZESUzptFbapr28/+WqndD+Xk": "e8JFSrW8LW3UK6SSXh2ZESUzptFbapr28/+WqndD+Xk"
},
"signatures": {
"@bob:localhost": {
"ed25519:xZPyb4hxM8zaedDFz5m8HsDpX1fknd/V/69THLhNX9I": "kkGZHLY18jyqXs412VB31u6vxijbaBgVrIMR/LBAFULhTZk6HGH951N6NxMZnYHyH0sFaQhsl4DUqt7XthBHBQ"
}
},
"usage": [
"self_signing"
],
"user_id": "@bob:localhost"
}
},
"user_signing_keys": {}
});
ruma_response_from_json(&data)
}
pub fn bob_device_1_id() -> &'static DeviceId {
device_id!("RLZGZIHKMP")
}
pub fn bob_device_2_id() -> &'static DeviceId {
device_id!("XCYNVRMTER")
}
pub fn bob_keys_query_response_rotated() -> KeyQueryResponse {
let data = json!({
"device_keys": {
"@bob:localhost": {
"RLZGZIHKMP": {
"algorithms": [
"m.olm.v1.curve25519-aes-sha2",
"m.megolm.v1.aes-sha2"
],
"device_id": "RLZGZIHKMP",
"keys": {
"curve25519:RLZGZIHKMP": "Zd8uO9Rr1PtqNno3//ybeUZ3JuqFtm17TQTWW0f47AU",
"ed25519:RLZGZIHKMP": "kH+Zn2m7LPES/XLOyVvnf8t4Byfj3mAbngHptHZFzk0"
},
"signatures": {
"@bob:localhost": {
"ed25519:RLZGZIHKMP": "w4MOkDiD+4XatQrRzGrcaqwVmiZrAjxmaIA8aSuzQveD2SJ2eVZq3OSpqx6QRUbG/gkkZxGmY13PkS/iAOv0AA",
"ed25519:e8JFSrW8LW3UK6SSXh2ZESUzptFbapr28/+WqndD+Xk": "ki+cV0EVe5cYXnzqU078qy1qu2rnaxaBQU+KwyvPpEUotNTXjWKUOJfxast42d5tjI5vsI5aiQ6XkYfjBJ74Bw"
}
},
"user_id": "@bob:localhost",
"unsigned": {
"device_display_name": "develop.element.io: Chrome on macOS"
}
},
"XCYNVRMTER": {
"algorithms": [
"m.olm.v1.curve25519-aes-sha2",
"m.megolm.v1.aes-sha2"
],
"device_id": "XCYNVRMTER",
"keys": {
"curve25519:XCYNVRMTER": "xGKYkFcHGlJ+I1yiefPyZu1EY8i2h1eed5uk3PAW6GA",
"ed25519:XCYNVRMTER": "EsU8MJzTYE+/VJs1K9HkGqb8UXCByPioynGrV28WocU"
},
"signatures": {
"@bob:localhost": {
"ed25519:XCYNVRMTER": "yZ7cpaoA+0rRx+bmklsP1iAd0eGPH6gsdywC11VE98/mrcbeFuxjQVn39Ds7h+vmciu5GRzwWgDgv+6go6FHAQ",
"ed25519:e8JFSrW8LW3UK6SSXh2ZESUzptFbapr28/+WqndD+Xk": "xYnGmU9FEdoavB5P743gx3xbEy29tlfRX5lT3JO0dWhHdsP+muqBXUYMBl1RRFeZtIE0GYc9ORb6Yf88EdeoCw",
"ed25519:NWoyMF4Ox8PEj+8l1e70zuIUg0D+wL9wtcj1KhWL0Bc": "2ieX8z+oW9JhdyIIkTDsQ2o5VWxcO6dOgeyPbRwbAL6Q8J6xujzYSIi568UAlPt+wg+RkNLshneexCPNMgSiDQ"
}
},
"user_id": "@bob:localhost",
"unsigned": {
"device_display_name": "app.element.io: Chrome on mac"
}
}
}
},
"failures": {},
"master_keys": {
"@bob:localhost": {
"keys": {
"ed25519:xaFlsDqlDRRy7Idtt1dW9mdhH/gvvax34q+HxepjNWY": "xaFlsDqlDRRy7Idtt1dW9mdhH/gvvax34q+HxepjNWY"
},
"signatures": {
"@bob:localhost": {
"ed25519:XCYNVRMTER": "K1aPl+GtcNi8yDqn1zvKIJMg3PFLQkwoXJeFJMmct4SA2SiQIl1S2x1bDTC3kQ4/LA7ULiQgKlxkXdQVf2GZDw",
"ed25519:xaFlsDqlDRRy7Idtt1dW9mdhH/gvvax34q+HxepjNWY": "S5vw8moiPudKhmF1qIv3/ehbZ7uohJbcQaLcOV+DDh9iC/YX0UqnaGn1ZYWJpIN7Kxe2ZWCBwzp35DOVZKfxBw"
}
},
"usage": [
"master"
],
"user_id": "@bob:localhost"
}
},
"self_signing_keys": {
"@bob:localhost": {
"keys": {
"ed25519:NWoyMF4Ox8PEj+8l1e70zuIUg0D+wL9wtcj1KhWL0Bc": "NWoyMF4Ox8PEj+8l1e70zuIUg0D+wL9wtcj1KhWL0Bc"
},
"signatures": {
"@bob:localhost": {
"ed25519:xaFlsDqlDRRy7Idtt1dW9mdhH/gvvax34q+HxepjNWY": "rwQIkR7JbZOrwGrmkW9QzFlK+lMjRDHVcGVlYNS/zVeDyvWxD0WFHcmy4p/LSgJDyrVt+th7LH7Bj+Ed/EGvCw"
}
},
"usage": [
"self_signing"
],
"user_id": "@bob:localhost"
}
},
"user_signing_keys": {}
});
ruma_response_from_json(&data)
}
pub fn carol_signed_device_id() -> &'static DeviceId {
device_id!("JBRBCHOFDZ")
}
pub fn carol_unsigned_device_id() -> &'static DeviceId {
device_id!("BAZAPVEHGA")
}
fn device_1_keys_payload_carol() -> Value {
json!({
"algorithms": [
"m.olm.v1.curve25519-aes-sha2",
"m.megolm.v1.aes-sha2"
],
"device_id": "BAZAPVEHGA",
"keys": {
"curve25519:BAZAPVEHGA": "/mCcWJb5mtNGPC7m4iQeW8gVJB4nG8z/z2QQXzzNijw",
"ed25519:BAZAPVEHGA": "MLSoOlk27qcS/2O9Etp6XwgF8j+UT06yy/ypSeE9JRA"
},
"signatures": {
"@carol:localhost": {
"ed25519:BAZAPVEHGA": "y2+Z0ofRRoNMj864SoAcNEXRToYVeiARu39CO0Vj2GcSIxlpR7B8K1wDYV4luP4gOL1t1tPgJPXL1WO//AHHCw",
}
},
"user_id": "@carol:localhost"
})
}
fn device_2_keys_payload_carol() -> Value {
json!({
"algorithms": [
"m.olm.v1.curve25519-aes-sha2",
"m.megolm.v1.aes-sha2"
],
"device_id": "JBRBCHOFDZ",
"keys": {
"curve25519:JBRBCHOFDZ": "900HdrlfxlH8yMSmEQ3C32uVyXCuxKs5oPKS/wUgzVQ",
"ed25519:JBRBCHOFDZ": "BOINY06uroLYscHUq0e0FmUo/W0LC4/fsIPkZQe71NY"
},
"signatures": {
"@carol:localhost": {
"ed25519:JBRBCHOFDZ": "MmSJS3yEdeuseiLTDCQwImZBPNFMdhhkAFjRZZrIONoGFR0AMSzgLtx/nSgXP8RwVxpycvb6OAqvSk2toK3PDg",
"ed25519:ZOMWgk5LAogkwDEdZl9Rv7FRGu0nGbeLtMHx6anzhQs": "VtoxmPn/BQVDlpEHPEI2wPUlruUX9m2zV3FChNkRyEEWur4St27WA1He8BwjVRiiT0bdUnVH3xfmucoV9UnbDA"
}
},
"user_id": "@carol:localhost",
})
}
fn ssk_payload_carol() -> Value {
json!({
"@carol:localhost": {
"keys": {
"ed25519:ZOMWgk5LAogkwDEdZl9Rv7FRGu0nGbeLtMHx6anzhQs": "ZOMWgk5LAogkwDEdZl9Rv7FRGu0nGbeLtMHx6anzhQs"
},
"signatures": {
"@carol:localhost": {
"ed25519:itnwUCRfBPW08IrmBks9MTp/Qm5AJ2WNca13ptIZF8U": "thjR1/kxHADXqLqxc4Q3OZhAaLq7SPL96LNCGVGN64OYAJ5yG1cpqAXBiBCUaBUTdRTb0ys601RR8djPdTK/BQ"
}
},
"usage": [
"self_signing"
],
"user_id": "@carol:localhost"
}
})
}
pub fn carol_keys_query_response_unsigned() -> KeyQueryResponse {
let data = json!({
"device_keys": {
"@carol:localhost": {
"BAZAPVEHGA": Self::device_1_keys_payload_carol(),
"JBRBCHOFDZ": Self::device_2_keys_payload_carol()
}
},
"failures": {},
"master_keys": {
"@carol:localhost": {
"keys": {
"ed25519:itnwUCRfBPW08IrmBks9MTp/Qm5AJ2WNca13ptIZF8U": "itnwUCRfBPW08IrmBks9MTp/Qm5AJ2WNca13ptIZF8U"
},
"signatures": {
"@carol:localhost": {
"ed25519:JBRBCHOFDZ": "eRA4jRSszQVuYpMtHTBuWGLEzcdUojyCW4/XKHRIQ2solv7iTC/MWES6I20YrHJa7H82CVoyNxS1Y3AwttBbCg",
"ed25519:itnwUCRfBPW08IrmBks9MTp/Qm5AJ2WNca13ptIZF8U": "e3r5L+JLv6FB8+Tt4BlIbz4wk2qPeMoKL1uR079qZzYMvtKoWGK9p000cZIhA5R1Tl7buQ9ODUfizued8g3TAg"
},
},
"usage": [
"master"
],
"user_id": "@carol:localhost"
}
},
"self_signing_keys": Self::ssk_payload_carol(),
"user_signing_keys": {}
});
ruma_response_from_json(&data)
}
pub fn carol_keys_query_response_signed() -> KeyQueryResponse {
let data = json!({
"device_keys": {
"@carol:localhost": {
"BAZAPVEHGA": Self::device_1_keys_payload_carol(),
"JBRBCHOFDZ": Self::device_2_keys_payload_carol()
}
},
"failures": {},
"master_keys": {
"@carol:localhost": {
"keys": {
"ed25519:itnwUCRfBPW08IrmBks9MTp/Qm5AJ2WNca13ptIZF8U": "itnwUCRfBPW08IrmBks9MTp/Qm5AJ2WNca13ptIZF8U"
},
"signatures": {
"@carol:localhost": {
"ed25519:JBRBCHOFDZ": "eRA4jRSszQVuYpMtHTBuWGLEzcdUojyCW4/XKHRIQ2solv7iTC/MWES6I20YrHJa7H82CVoyNxS1Y3AwttBbCg",
"ed25519:itnwUCRfBPW08IrmBks9MTp/Qm5AJ2WNca13ptIZF8U": "e3r5L+JLv6FB8+Tt4BlIbz4wk2qPeMoKL1uR079qZzYMvtKoWGK9p000cZIhA5R1Tl7buQ9ODUfizued8g3TAg"
},
"@alice:localhost": {
"ed25519:MXob/N/bYI7U2655O1/AI9NOX1245RnE03Nl4Hvf+u0": "yfRUvkaVg3KizC/HDXcuP4+gtYhxgzr8X916Wt4GRXjj4qhDjsCkf8mYZ7x4lcEXzRkYql5KelabgVzP12qmAA"
}
},
"usage": [
"master"
],
"user_id": "@carol:localhost"
}
},
"self_signing_keys": Self::ssk_payload_carol(),
"user_signing_keys": {}
});
ruma_response_from_json(&data)
}
}
pub struct MaloIdentityChangeDataSet {}
impl MaloIdentityChangeDataSet {
pub fn user_id() -> &'static UserId {
user_id!("@malo:localhost")
}
pub fn device_id() -> &'static DeviceId {
device_id!("NZFSPBRLDO")
}
pub fn initial_key_query() -> KeyQueryResponse {
let data = json!({
"device_keys": {
"@malo:localhost": {
"NZFSPBRLDO": {
"algorithms": [
"m.olm.v1.curve25519-aes-sha2",
"m.megolm.v1.aes-sha2"
],
"device_id": "NZFSPBRLDO",
"keys": {
"curve25519:NZFSPBRLDO": "L3jdbw42+9i+K7LPjAY+kmqG9nr2n/U0ow8hEbLCoCs",
"ed25519:NZFSPBRLDO": "VDJt3xI4SzrgQkuE3sEIauluaXawx3wWoWOynPI8Zko"
},
"signatures": {
"@malo:localhost": {
"ed25519:NZFSPBRLDO": "lmtbdrJ5xBweo677Fg2qrSHsRi4R3x2WNlvSNJY6Zbg0R5lJS9syN2HZw/irL9PA644GYm4QM/t+DX0grnn+BQ",
"ed25519:+wbxNfSuDrch1jKuydQmEf4qlA4u4NgwqNXNuLVwug8": "Ql1fq+SvVDx+8mjNMzSaR0hBCEkdPirbs2+BK0gwsIH1zkuMADnBoNWP7LJiKo/EO9gnpiCzyQQgI4e9pIVPDA"
}
},
"user_id": "@malo:localhost",
"unsigned": {}
}
}
},
"failures": {},
"master_keys": {
"@malo:localhost": {
"keys": {
"ed25519:WBxliSP29guYr4ux0MW6otRe3V/wOLXXElpOcOmpdlE": "WBxliSP29guYr4ux0MW6otRe3V/wOLXXElpOcOmpdlE"
},
"signatures": {
"@malo:localhost": {
"ed25519:NZFSPBRLDO": "crJcXqFpEHRM8KNUw419XrVFaHoM8/kV4ebgpuuIiD9wfX0AhHE2iGRGpKzsrVCqne9k181/uN0sgDMpK2y4Aw",
"ed25519:WBxliSP29guYr4ux0MW6otRe3V/wOLXXElpOcOmpdlE": "/xwFF5AC3GhkpvJ449Srh8kNQS6CXAxQMmBpQvPEHx5BHPXJ08u2ZDd1EPYY4zk4QsePk+tEYu8gDnB0bggHCA"
}
},
"usage": [
"master"
],
"user_id": "@malo:localhost"
}
},
"self_signing_keys": {
"@malo:localhost": {
"keys": {
"ed25519:+wbxNfSuDrch1jKuydQmEf4qlA4u4NgwqNXNuLVwug8": "+wbxNfSuDrch1jKuydQmEf4qlA4u4NgwqNXNuLVwug8"
},
"signatures": {
"@malo:localhost": {
"ed25519:WBxliSP29guYr4ux0MW6otRe3V/wOLXXElpOcOmpdlE": "sSGQ6ny6aXtIvgKPGOYJzcmnNDSkbaJFVRe9wekOry7EaiWf2l28MkGTUBt4cPoRiMkNjuRBupNEARqHF72sAQ"
}
},
"usage": [
"self_signing"
],
"user_id": "@malo:localhost"
}
},
"user_signing_keys": {},
});
ruma_response_from_json(&data)
}
pub fn updated_key_query() -> KeyQueryResponse {
let data = json!({
"device_keys": {
"@malo:localhost": {
"NZFSPBRLDO": {
"algorithms": [
"m.olm.v1.curve25519-aes-sha2",
"m.megolm.v1.aes-sha2"
],
"device_id": "NZFSPBRLDO",
"keys": {
"curve25519:NZFSPBRLDO": "L3jdbw42+9i+K7LPjAY+kmqG9nr2n/U0ow8hEbLCoCs",
"ed25519:NZFSPBRLDO": "VDJt3xI4SzrgQkuE3sEIauluaXawx3wWoWOynPI8Zko"
},
"signatures": {
"@malo:localhost": {
"ed25519:NZFSPBRLDO": "lmtbdrJ5xBweo677Fg2qrSHsRi4R3x2WNlvSNJY6Zbg0R5lJS9syN2HZw/irL9PA644GYm4QM/t+DX0grnn+BQ",
"ed25519:+wbxNfSuDrch1jKuydQmEf4qlA4u4NgwqNXNuLVwug8": "Ql1fq+SvVDx+8mjNMzSaR0hBCEkdPirbs2+BK0gwsIH1zkuMADnBoNWP7LJiKo/EO9gnpiCzyQQgI4e9pIVPDA",
"ed25519:8my6+zgnzEP0ZqmQFyvscJh7isHlf8lxBmHg+fzdJkE": "OvqDE7C2mrHxjwNyMIEz+m/AO6I6lM5HoPYY2bvLjrJJDOF5sJOtw4JoYiCWyt90ZIWsbEqmfbazrblLD50tCg"
}
},
"user_id": "@malo:localhost",
"unsigned": {}
}
}
},
"failures": {},
"master_keys": {
"@malo:localhost": {
"keys": {
"ed25519:dv2Mk7bFlRtP/0oSZpB01Ouc5frCXKfG8Bn9YrFxbxU": "dv2Mk7bFlRtP/0oSZpB01Ouc5frCXKfG8Bn9YrFxbxU"
},
"signatures": {
"@malo:localhost": {
"ed25519:NZFSPBRLDO": "2Ye96l4srBSWskNQszuMpea1r97rFoUyfNqegvu/hGeP47w0OVvqYuNtZRNwqb7TMS7aPEn6l9lhWEk7v06wCg",
"ed25519:dv2Mk7bFlRtP/0oSZpB01Ouc5frCXKfG8Bn9YrFxbxU": "btkxAJpJeVtc9wgBmeHUI9QDpojd6ddLxK11E3403KoTQtP6Mnr5GsVdQr1HJToG7PG4k4eEZGWxVZr1GPndAA"
}
},
"usage": [
"master"
],
"user_id": "@malo:localhost"
}
},
"self_signing_keys": {
"@malo:localhost": {
"keys": {
"ed25519:8my6+zgnzEP0ZqmQFyvscJh7isHlf8lxBmHg+fzdJkE": "8my6+zgnzEP0ZqmQFyvscJh7isHlf8lxBmHg+fzdJkE"
},
"signatures": {
"@malo:localhost": {
"ed25519:dv2Mk7bFlRtP/0oSZpB01Ouc5frCXKfG8Bn9YrFxbxU": "KJt0y1p8v8RGLGk2wUyCMbX1irXJqup/mdRuG/cxJxs24BZhDMyIzyGrGXnWq2gx3I4fKIMtFPi/ecxf92ePAQ"
}
},
"usage": [
"self_signing"
],
"user_id": "@malo:localhost"
}
},
"user_signing_keys": {}
});
ruma_response_from_json(&data)
}
}
fn calculate_json_signature(mut value: Value, signing_key: &Ed25519SecretKey) -> Ed25519Signature {
let json_object = value.as_object_mut().expect("value must be object");
json_object.remove("signatures");
json_object.remove("unsigned");
let canonical_json: CanonicalJsonValue =
value.try_into().expect("could not convert to canonicaljson");
signing_key.sign(canonical_json.to_string().as_ref())
}
fn sign_json(
value: &mut Value,
signing_key: &Ed25519SecretKey,
user_id: &UserId,
key_identifier: &str,
) {
let signature = calculate_json_signature(value.clone(), signing_key);
let value_obj = value.as_object_mut().expect("value must be object");
let signatures_obj = value_obj
.entry("signatures")
.or_insert_with(|| serde_json::Map::new().into())
.as_object_mut()
.expect("signatures key must be object");
let user_signatures_obj = signatures_obj
.entry(user_id.to_string())
.or_insert_with(|| serde_json::Map::new().into())
.as_object_mut()
.expect("signatures keys must be object");
user_signatures_obj.insert(format!("ed25519:{key_identifier}"), signature.to_base64().into());
}
fn sign_cross_signing_key(
value: &mut CrossSigningKey,
signing_key: &Ed25519SecretKey,
user_id: &UserId,
) {
let key_json = serde_json::to_value(value.clone()).unwrap();
let signature = calculate_json_signature(key_json, signing_key);
let signing_key_id: OwnedBase64PublicKeyOrDeviceId =
OwnedBase64PublicKey::with_bytes(signing_key.public_key().as_bytes()).into();
value.signatures.insert_signature(
user_id.to_owned(),
CrossSigningOrDeviceSigningKeyId::from_parts(SigningKeyAlgorithm::Ed25519, &signing_key_id),
signature.to_base64(),
);
}