crypto: Add initial support to encrypt messages.

master
Damir Jelić 2020-04-09 16:24:40 +02:00
parent a5f43db859
commit dc2983d9a2
1 changed files with 166 additions and 9 deletions

View File

@ -90,6 +90,8 @@ impl OlmMachine {
&Algorithm::MegolmV1AesSha2, &Algorithm::MegolmV1AesSha2,
]; ];
const MAX_TO_DEVICE_MESSAGES: usize = 20;
/// Create a new account. /// Create a new account.
pub fn new(user_id: &UserId, device_id: &str) -> Result<Self> { pub fn new(user_id: &UserId, device_id: &str) -> Result<Self> {
Ok(OlmMachine { Ok(OlmMachine {
@ -815,12 +817,115 @@ impl OlmMachine {
Ok(()) Ok(())
} }
pub async fn encrypt(
&mut self,
room_id: &RoomId,
content: MessageEventContent,
) -> Result<MegolmV1AesSha2Content> {
if !self.outbound_group_session.contains_key(room_id) {
self.create_outbound_group_session(room_id).await?
}
let session = self.outbound_group_session.get(room_id).unwrap();
if session.expired() {
todo!()
}
// if !session.shared() {
// todo!()
// }
let mut json_content = json!({
"content": content,
"room_id": room_id,
"type": EventType::RoomMessage,
});
let plaintext = cjson::to_string(&json_content).unwrap_or_else(|_| {
panic!(format!(
"Can't serialize {} to canonical JSON",
json_content
))
});
let ciphertext = session.encrypt(plaintext).await;
Ok(MegolmV1AesSha2Content {
algorithm: Algorithm::MegolmV1AesSha2,
ciphertext,
sender_key: self
.account
.lock()
.await
.identity_keys()
.curve25519()
.to_owned(),
session_id: session.session_id().to_owned(),
device_id: self.device_id.to_owned(),
})
}
async fn olm_encrypt(
&mut self,
session: Arc<Mutex<Session>>,
recipient_device: &Device,
event_type: EventType,
content: Value,
) -> Result<OlmV1Curve25519AesSha2Content> {
let identity_keys = self.account.lock().await.identity_keys();
let recipient_signing_key = recipient_device
.keys(&KeyAlgorithm::Ed25519)
.ok_or(OlmError::MissingSigningKey)?;
let recipient_sender_key = recipient_device
.keys(&KeyAlgorithm::Curve25519)
.ok_or(OlmError::MissingSigningKey)?;
let payload = json!({
"sender": self.user_id,
"sender_device": self.device_id,
"keys": {
"ed25519": identity_keys.ed25519(),
},
"recipient": recipient_device.user_id(),
"recipient_keys": {
"ed25519": recipient_signing_key,
},
"type": event_type,
"content": content,
});
let plaintext = cjson::to_string(&payload)
.unwrap_or_else(|_| panic!(format!("Can't serialize {} to canonical JSON", payload)));
let ciphertext = session.lock().await.encrypt(&plaintext).to_tuple();
self.store.save_session(session).await?;
let message_type: usize = ciphertext.0.into();
let ciphertext = CiphertextInfo {
body: ciphertext.1,
message_type: (message_type as u32).into(),
};
let mut content = HashMap::new();
content.insert(recipient_sender_key.to_owned(), ciphertext);
Ok(OlmV1Curve25519AesSha2Content {
algorithm: Algorithm::OlmV1Curve25519AesSha2,
sender_key: identity_keys.curve25519().to_owned(),
ciphertext: content,
})
}
// TODO accept an algorithm here // TODO accept an algorithm here
async fn share_megolm_session<'a, I>( pub(crate) async fn share_megolm_session<'a, I>(
&mut self, &mut self,
room_id: &RoomId, room_id: &RoomId,
users: I, users: I,
) -> Result<HashMap<UserId, HashMap<DeviceIdOrAllDevices, Event>>> ) -> Result<Vec<ToDeviceRequest>>
where where
I: IntoIterator<Item = &'a UserId>, I: IntoIterator<Item = &'a UserId>,
{ {
@ -828,16 +933,20 @@ impl OlmMachine {
self.create_outbound_group_session(room_id).await? self.create_outbound_group_session(room_id).await?
} }
let session = self.outbound_group_session.get(room_id).unwrap(); let megolm_session = self.outbound_group_session.get(room_id).unwrap();
let session_id = megolm_session.session_id().to_owned();
megolm_session.mark_as_shared();
let key_content = json!({ let key_content = json!({
"algorithm": Algorithm::MegolmV1AesSha2, "algorithm": Algorithm::MegolmV1AesSha2,
"room_id": room_id, "room_id": room_id,
"session_id": session.session_id(), "session_id": session_id.clone(),
"session_key": session.session_key().await, "session_key": megolm_session.session_key().await,
"chain_index": session.message_index().await, "chain_index": megolm_session.message_index().await,
}); });
let mut user_map = Vec::new();
for user_id in users { for user_id in users {
for device in self.store.get_user_devices(user_id).await?.devices() { for device in self.store.get_user_devices(user_id).await?.devices() {
let sender_key = if let Some(k) = device.keys(&KeyAlgorithm::Curve25519) { let sender_key = if let Some(k) = device.keys(&KeyAlgorithm::Curve25519) {
@ -851,13 +960,61 @@ impl OlmMachine {
continue; continue;
}; };
// TODO abor if the device isn't verified // TODO abort if the device isn't verified
let sessions = self.store.get_sessions(sender_key).await?;
let session = self.store.get_sessions(sender_key); if let Some(s) = sessions {
let session = &s.lock().await[0];
user_map.push((session.clone(), device.clone()));
} else {
warn!(
"Trying to encrypt a megolm session for user
{} on device {}, but no Olm session is found",
user_id,
device.device_id()
);
}
} }
} }
todo!() let mut message_vec = Vec::new();
for (i, user_map_chunk) in user_map
.chunks(OlmMachine::MAX_TO_DEVICE_MESSAGES)
.enumerate()
{
let mut messages = HashMap::new();
for (session, device) in user_map_chunk {
if !messages.contains_key(device.user_id()) {
messages.insert(device.user_id().clone(), HashMap::new());
};
let user_messages = messages.get_mut(device.user_id()).unwrap();
let encrypted_content = self
.olm_encrypt(
session.clone(),
&device,
EventType::RoomKey,
key_content.clone(),
)
.await?;
user_messages.insert(
DeviceIdOrAllDevices::DeviceId(device.device_id().clone()),
encrypted_content,
);
}
message_vec.push(ToDeviceRequest {
event_type: "m.room.encrypted".to_owned(),
txn_id: format!("{}-{}", session_id, i),
messages,
});
}
Ok(message_vec)
} }
fn add_forwarded_room_key( fn add_forwarded_room_key(