mirror of
https://github.com/matrix-org/matrix-rust-sdk.git
synced 2026-06-11 01:44:23 -04:00
feat(qr-login): Support HPKE for the cryptographic channel
This commit is contained in:
@@ -31,6 +31,7 @@
|
||||
use vodozemac::{
|
||||
Curve25519PublicKey,
|
||||
ecies::{CheckCode, Ecies, EstablishedEcies, InboundCreationResult, InitialMessage, Message},
|
||||
hpke::{self, EstablishedHpkeChannel, HpkeRecipientChannel, RecipientCreationResult},
|
||||
};
|
||||
|
||||
use crate::authentication::oauth::qrcode::SecureChannelError as Error;
|
||||
@@ -38,6 +39,7 @@ use crate::authentication::oauth::qrcode::SecureChannelError as Error;
|
||||
/// A cryptographic communication channel.
|
||||
pub(super) enum CryptoChannel {
|
||||
Ecies(Ecies),
|
||||
Hpke(HpkeRecipientChannel),
|
||||
}
|
||||
|
||||
impl CryptoChannel {
|
||||
@@ -46,10 +48,16 @@ impl CryptoChannel {
|
||||
CryptoChannel::Ecies(Ecies::new())
|
||||
}
|
||||
|
||||
/// Create a new HPKE-based [`CryptoChannel`].
|
||||
pub(super) fn new_hpke() -> Self {
|
||||
CryptoChannel::Hpke(HpkeRecipientChannel::new())
|
||||
}
|
||||
|
||||
/// Get the [`Curve25519PublicKey`] of this cryptographic channel.
|
||||
pub(super) fn public_key(&self) -> Curve25519PublicKey {
|
||||
match self {
|
||||
CryptoChannel::Ecies(ecies) => ecies.public_key(),
|
||||
CryptoChannel::Hpke(hpke) => hpke.public_key(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,12 +71,19 @@ impl CryptoChannel {
|
||||
let message = InitialMessage::decode(message)?;
|
||||
Ok(CryptoChannelCreationResult::Ecies(ecies.establish_inbound_channel(&message)?))
|
||||
}
|
||||
CryptoChannel::Hpke(hpke) => {
|
||||
let message = hpke::InitialMessage::decode(message).unwrap();
|
||||
Ok(CryptoChannelCreationResult::Hpke(
|
||||
hpke.establish_channel(&message, &[]).unwrap(),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) enum CryptoChannelCreationResult {
|
||||
Ecies(InboundCreationResult),
|
||||
Hpke(RecipientCreationResult),
|
||||
}
|
||||
|
||||
impl CryptoChannelCreationResult {
|
||||
@@ -78,6 +93,7 @@ impl CryptoChannelCreationResult {
|
||||
CryptoChannelCreationResult::Ecies(inbound_creation_result) => {
|
||||
&inbound_creation_result.message
|
||||
}
|
||||
CryptoChannelCreationResult::Hpke(result) => &result.message,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -88,6 +104,7 @@ impl CryptoChannelCreationResult {
|
||||
/// cryptographic messages.
|
||||
pub(super) enum EstablishedCryptoChannel {
|
||||
Ecies(EstablishedEcies),
|
||||
Hpke(EstablishedHpkeChannel),
|
||||
}
|
||||
|
||||
impl EstablishedCryptoChannel {
|
||||
@@ -95,26 +112,37 @@ impl EstablishedCryptoChannel {
|
||||
pub(super) fn check_code(&self) -> &CheckCode {
|
||||
match self {
|
||||
EstablishedCryptoChannel::Ecies(established_ecies) => established_ecies.check_code(),
|
||||
EstablishedCryptoChannel::Hpke(established_hpke_channel) => {
|
||||
established_hpke_channel.check_code()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Seal the given plaintext using this [`EstablishedCryptoChannel`].
|
||||
pub(super) fn seal(&mut self, plaintext: &str) -> String {
|
||||
pub(super) fn seal(&mut self, plaintext: &str, aad: &[u8]) -> String {
|
||||
match self {
|
||||
EstablishedCryptoChannel::Ecies(channel) => {
|
||||
let message = channel.encrypt(plaintext.as_bytes());
|
||||
message.encode()
|
||||
}
|
||||
EstablishedCryptoChannel::Hpke(channel) => {
|
||||
let message = channel.seal(plaintext.as_bytes(), aad);
|
||||
message.encode()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Open the given sealed message using this [`EstablishedCryptoChannel`].
|
||||
pub(super) fn open(&mut self, message: &str) -> Result<String, Error> {
|
||||
pub(super) fn open(&mut self, message: &str, aad: &[u8]) -> Result<String, Error> {
|
||||
let plaintext = match self {
|
||||
EstablishedCryptoChannel::Ecies(channel) => {
|
||||
let message = Message::decode(message)?;
|
||||
channel.decrypt(&message)?
|
||||
}
|
||||
EstablishedCryptoChannel::Hpke(channel) => {
|
||||
let message = hpke::Message::decode(message).unwrap();
|
||||
channel.open(&message, aad).unwrap()
|
||||
}
|
||||
};
|
||||
|
||||
Ok(String::from_utf8(plaintext).map_err(|e| e.utf8_error())?)
|
||||
|
||||
@@ -19,8 +19,12 @@ use matrix_sdk_base::crypto::types::qr_login::{
|
||||
use serde::{Serialize, de::DeserializeOwned};
|
||||
use tracing::{instrument, trace};
|
||||
use url::Url;
|
||||
use vodozemac::ecies::{
|
||||
CheckCode, DigitMode, Ecies, EstablishedEcies, InboundCreationResult, OutboundCreationResult,
|
||||
use vodozemac::{
|
||||
ecies::{CheckCode, Ecies, EstablishedEcies, InboundCreationResult, OutboundCreationResult},
|
||||
hpke::{
|
||||
BidirectionalCreationResult, DigitMode, HpkeSenderChannel, InitialResponse,
|
||||
RecipientCreationResult, SenderCreationResult, UnidirectionalSenderChannel,
|
||||
},
|
||||
};
|
||||
|
||||
use super::{
|
||||
@@ -116,6 +120,15 @@ impl SecureChannel {
|
||||
secure_channel.send(LOGIN_OK_MESSAGE).await?;
|
||||
secure_channel
|
||||
}
|
||||
CryptoChannelCreationResult::Hpke(RecipientCreationResult { channel, .. }) => {
|
||||
let BidirectionalCreationResult { channel, message } =
|
||||
channel.establish_bidirectional_channel(LOGIN_OK_MESSAGE.as_bytes(), &[]);
|
||||
self.channel.send(message.encode()).await?;
|
||||
|
||||
let crypto_channel = EstablishedCryptoChannel::Hpke(channel);
|
||||
|
||||
EstablishedSecureChannel { channel: self.channel, crypto_channel }
|
||||
}
|
||||
};
|
||||
|
||||
Ok(AlmostEstablishedSecureChannel { secure_channel })
|
||||
@@ -163,6 +176,7 @@ impl EstablishedSecureChannel {
|
||||
) -> Result<Self, Error> {
|
||||
enum ChannelType {
|
||||
Ecies(EstablishedEcies),
|
||||
Hpke(UnidirectionalSenderChannel),
|
||||
}
|
||||
|
||||
if qr_code_data.intent() == expected_mode {
|
||||
@@ -176,7 +190,7 @@ impl EstablishedSecureChannel {
|
||||
// it's talking to us, the device that scanned the QR code, until it
|
||||
// receives and successfully decrypts the initial message. We're here encrypting
|
||||
// the `LOGIN_INITIATE_MESSAGE`.
|
||||
let (crypto_channel, encoded_message) = {
|
||||
let (crypto_channel, encoded_message) = if true {
|
||||
let ecies = Ecies::new();
|
||||
|
||||
let OutboundCreationResult { ecies, message } = ecies.establish_outbound_channel(
|
||||
@@ -184,6 +198,15 @@ impl EstablishedSecureChannel {
|
||||
LOGIN_INITIATE_MESSAGE.as_bytes(),
|
||||
)?;
|
||||
(ChannelType::Ecies(ecies), message.encode())
|
||||
} else {
|
||||
let SenderCreationResult { channel, message } = HpkeSenderChannel::new()
|
||||
.establish_channel(
|
||||
qr_code_data.public_key(),
|
||||
LOGIN_INITIATE_MESSAGE.as_bytes(),
|
||||
// TODO: Do we want to include some additional authenticated data here?
|
||||
&[],
|
||||
);
|
||||
(ChannelType::Hpke(channel), message.encode())
|
||||
};
|
||||
|
||||
// The other side has crated a rendezvous channel, we're going to connect to it
|
||||
@@ -213,15 +236,32 @@ impl EstablishedSecureChannel {
|
||||
trace!("Waiting for the LOGIN OK message");
|
||||
|
||||
let (response, channel) = match crypto_channel {
|
||||
ChannelType::Ecies(ecies) => {
|
||||
ChannelType::Ecies(crypto_channel) => {
|
||||
// We can create our EstablishedSecureChannel struct now and use the
|
||||
// convenient helpers which transparently decrypt on receival.
|
||||
let crypto_channel = EstablishedCryptoChannel::Ecies(ecies);
|
||||
let crypto_channel = EstablishedCryptoChannel::Ecies(crypto_channel);
|
||||
let mut channel = Self { channel, crypto_channel };
|
||||
|
||||
let response = channel.receive().await?;
|
||||
(response, channel)
|
||||
}
|
||||
ChannelType::Hpke(crypto_channel) => {
|
||||
let response = channel.receive().await?;
|
||||
let response = InitialResponse::decode(&response).unwrap();
|
||||
|
||||
let BidirectionalCreationResult { channel: crypto_channel, message } =
|
||||
crypto_channel.establish_bidirectional_channel(&response, &[]).unwrap();
|
||||
let response = String::from_utf8(message).unwrap();
|
||||
let crypto_channel = EstablishedCryptoChannel::Hpke(crypto_channel);
|
||||
|
||||
// We can create our EstablishedSecureChannel struct now and use the
|
||||
// convenient helpers which transparently decrypt on receival.
|
||||
let channel = Self { channel, crypto_channel };
|
||||
|
||||
// We can create our EstablishedSecureChannel struct now and use the
|
||||
// convenient helpers which transparently decrypt on receival.
|
||||
(response, channel)
|
||||
}
|
||||
};
|
||||
|
||||
trace!("Received the LOGIN OK message, maybe.");
|
||||
@@ -260,14 +300,13 @@ impl EstablishedSecureChannel {
|
||||
}
|
||||
|
||||
async fn send(&mut self, message: &str) -> Result<(), Error> {
|
||||
let message = self.crypto_channel.seal(message);
|
||||
|
||||
let message = self.crypto_channel.seal(message, &[]);
|
||||
Ok(self.channel.send(message).await?)
|
||||
}
|
||||
|
||||
async fn receive(&mut self) -> Result<String, Error> {
|
||||
let message = self.channel.receive().await?;
|
||||
self.crypto_channel.open(&message)
|
||||
self.crypto_channel.open(&message, &[])
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user