mod check_code;
mod error;
mod messages;
mod recipient;
mod response_context;
mod sender;
pub use check_code::*;
pub use error::*;
use hpke::{
aead::{AeadCtxR, AeadCtxS, ChaCha20Poly1305},
kdf::HkdfSha256,
kem::X25519HkdfSha256,
};
pub use messages::*;
pub use recipient::*;
use response_context::CreateResponseContext;
pub use sender::*;
use crate::Curve25519PublicKey;
const MATRIX_QR_LOGIN_INFO_PREFIX: &str = "MATRIX_QR_CODE_LOGIN";
type Kem = X25519HkdfSha256;
type Aead = ChaCha20Poly1305;
type Kdf = HkdfSha256;
type SenderContext = AeadCtxS<Aead, Kdf, Kem>;
type RecipientContext = AeadCtxR<Aead, Kdf, Kem>;
type SenderResponseContext = AeadCtxR<Aead, Kdf, Kem>;
type RecipientResponseContext = AeadCtxS<Aead, Kdf, Kem>;
enum Role {
Sender {
sender_context: SenderContext,
response_context: SenderResponseContext,
},
Recipient {
sender_context: RecipientContext,
response_context: RecipientResponseContext,
},
}
impl std::fmt::Debug for Role {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Role::Sender { .. } => f.write_str("Sender"),
Role::Recipient { .. } => f.write_str("Recipient"),
}
}
}
impl Role {
fn construct_info_string(
&self,
partial_info: &str,
our_public_key: Curve25519PublicKey,
their_public_key: Curve25519PublicKey,
) -> String {
match self {
Role::Recipient { .. } => {
format!(
"{partial_info}|{}|{}",
our_public_key.to_base64(),
their_public_key.to_base64(),
)
}
Role::Sender { .. } => {
format!(
"{partial_info}|{}|{}",
their_public_key.to_base64(),
our_public_key.to_base64(),
)
}
}
}
fn check_code_info(
&self,
app_info: &str,
our_public_key: Curve25519PublicKey,
their_public_key: Curve25519PublicKey,
) -> String {
let partial_info = format!("{app_info}_CHECKCODE");
self.construct_info_string(&partial_info, our_public_key, their_public_key)
}
fn check_code(
&self,
app_info: &str,
our_public_key: Curve25519PublicKey,
their_public_key: Curve25519PublicKey,
) -> CheckCode {
let mut bytes = [0u8; 2];
let info = self.check_code_info(app_info, our_public_key, their_public_key);
let ret = match self {
Role::Sender { sender_context, .. } => {
sender_context.export(info.as_bytes(), &mut bytes)
}
Role::Recipient { sender_context, .. } => {
sender_context.export(info.as_bytes(), &mut bytes)
}
};
#[allow(clippy::expect_used)]
ret.expect("We should be able to generate a check code, as it's just two bytes");
CheckCode { bytes }
}
}
struct UnidirectionalHkpeChannel<T> {
sender_context: T,
application_info_prefix: String,
our_public_key: Curve25519PublicKey,
their_public_key: Curve25519PublicKey,
}
pub struct BidirectionalCreationResult<T> {
pub channel: EstablishedHpkeChannel,
pub message: T,
}
pub struct EstablishedHpkeChannel {
our_public_key: Curve25519PublicKey,
their_public_key: Curve25519PublicKey,
role: Role,
check_code: CheckCode,
}
impl std::fmt::Debug for EstablishedHpkeChannel {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("EstablishedHpkeChannel")
.field("our_public_key", &self.our_public_key)
.field("their_public_key", &self.their_public_key)
.field("check_code", &self.check_code)
.field("role", &self.role)
.finish()
}
}
impl EstablishedHpkeChannel {
pub const fn public_key(&self) -> Curve25519PublicKey {
self.our_public_key
}
pub const fn their_public_key(&self) -> Curve25519PublicKey {
self.their_public_key
}
pub fn check_code(&self) -> &CheckCode {
&self.check_code
}
pub fn seal(&mut self, plaintext: &[u8], aad: &[u8]) -> Message {
let ret = match &mut self.role {
Role::Sender { sender_context, .. } => sender_context.seal(plaintext, aad),
Role::Recipient { response_context, .. } => response_context.seal(plaintext, aad),
};
#[allow(clippy::expect_used)]
let ciphertext = ret.expect(
"We should be able to seal a plaintext, unless we're overflowed the sequence counter",
);
Message { ciphertext }
}
pub fn open(&mut self, message: &Message, aad: &[u8]) -> Result<Vec<u8>, Error> {
let ret = match &mut self.role {
Role::Sender { response_context, .. } => {
response_context.open(&message.ciphertext, aad)
}
Role::Recipient { sender_context, .. } => sender_context.open(&message.ciphertext, aad),
};
ret.map_err(|_| Error::Decryption)
}
}
#[cfg(test)]
mod tests {
use insta::assert_debug_snapshot;
use super::*;
use crate::Curve25519SecretKey;
#[test]
fn test_channel_creation() {
let alice = HpkeSenderChannel::new();
let bob = HpkeRecipientChannel::new();
let plaintext = b"It's a secret to everybody";
let SenderCreationResult { message, .. } = alice
.establish_channel(bob.public_key(), plaintext, &[])
.expect("We should be able to create the sender channel");
assert_ne!(message.ciphertext, plaintext);
let RecipientCreationResult { message, .. } = bob
.establish_channel(&message, &[])
.expect("We should be able to establish the recipient channel");
assert_eq!(message, plaintext);
}
#[test]
fn test_channel_roundtrip() {
let alice = HpkeSenderChannel::new();
let bob = HpkeRecipientChannel::new();
let bob_public_key = bob.public_key();
let plaintext = b"It's a secret to everybody";
let SenderCreationResult { channel: alice, message, .. } = alice
.establish_channel(bob_public_key, plaintext, &[])
.expect("We should be able to create the sender channel");
assert_ne!(message.ciphertext, plaintext);
let RecipientCreationResult { channel: bob, message } = bob
.establish_channel(&message, &[])
.expect("We should be able to establish the recipient channel");
assert_eq!(message, plaintext);
let plaintext = b"Not a secret to me!";
let BidirectionalCreationResult { message: initial_response, channel: mut bob } =
bob.establish_bidirectional_channel(plaintext, &[]);
assert_ne!(initial_response.ciphertext, plaintext);
let BidirectionalCreationResult { message: decrypted, channel: mut alice } = alice
.establish_bidirectional_channel(&initial_response, &[])
.expect("We should be able to decrypt the initial response");
assert_eq!(decrypted, plaintext);
assert_eq!(
alice.check_code(),
bob.check_code(),
"Alice and Bob should derive the same check code"
);
assert_eq!(
bob_public_key,
bob.public_key(),
"The public key should stay the same even after the bidirectional channel has been established"
);
assert_eq!(bob.their_public_key(), alice.public_key());
let plaintext = b"Fully";
let message = bob.seal(plaintext, &[]);
let decrypted =
alice.open(&message, &[]).expect("Alice should be able to open Bob's latest message");
assert_eq!(plaintext.as_slice(), decrypted);
alice.open(&message, &[]).expect_err("Replaying a message should not be possible");
let message = alice.seal(plaintext, b"some additional data");
bob.open(&message, &[]).expect_err(
"Bob should not be able to decrypt a message without providing the same AAD",
);
let message = bob
.open(&message, b"some additional data")
.expect("Bob should be able to decrypt Alice's final message");
assert_eq!(message, plaintext);
}
#[test]
fn invalid_public_key() {
let plaintext = b"It's a secret to everybody";
let alice = HpkeSenderChannel::new();
let bob = HpkeRecipientChannel::new();
let malory = Curve25519SecretKey::new();
let SenderCreationResult { mut message, .. } = alice
.establish_channel(bob.public_key(), plaintext, &[])
.expect("We should be able to create the sender channel");
message.encapsulated_key = Curve25519PublicKey::from(&malory);
bob.establish_channel(&message, &[]).expect_err(
"The decryption should fail since Malory inserted the \
wrong public key into the message",
);
}
#[test]
fn test_info_construction() {
use crate::types::Curve25519Keypair;
let app_info = "foobar";
let our_public_key = Curve25519Keypair::new().public_key;
let their_public_key = Curve25519Keypair::new().public_key;
let alice = HpkeSenderChannel::new();
let bob = HpkeRecipientChannel::new();
let SenderCreationResult { channel: alice, message } = alice
.establish_channel(bob.public_key(), b"", &[])
.expect("We should be able to create the sender channel");
let RecipientCreationResult { channel: bob, message: _ } = bob
.establish_channel(&message, &[])
.expect("We should be able to establish the recipient channel");
let BidirectionalCreationResult { channel: bob, message: initial_response } =
bob.establish_bidirectional_channel(b"My response", &[]);
let BidirectionalCreationResult { channel: alice, .. } = alice
.establish_bidirectional_channel(&initial_response, &[])
.expect("We should be able to establish the bidirectional channel for Alice");
let check_code_info1 =
alice.role.check_code_info(app_info, our_public_key, their_public_key);
assert_eq!(
check_code_info1,
format!("foobar_CHECKCODE|{their_public_key}|{our_public_key}")
);
let check_code_info2 = bob.role.check_code_info(app_info, our_public_key, their_public_key);
assert_eq!(
check_code_info2,
format!("foobar_CHECKCODE|{our_public_key}|{their_public_key}")
);
}
#[test]
fn snapshot_debug() {
let key = Curve25519PublicKey::from_bytes([0; 32]);
let alice = HpkeSenderChannel::new();
let bob = HpkeRecipientChannel::new();
let SenderCreationResult { channel: alice, message } = alice
.establish_channel(bob.public_key(), b"", &[])
.expect("We should be able to create the sender channel");
let RecipientCreationResult { channel: bob, .. } =
bob.establish_channel(&message, &[]).unwrap();
let BidirectionalCreationResult { message, .. } =
bob.establish_bidirectional_channel(b"", &[]);
let BidirectionalCreationResult { mut channel, .. } =
alice.establish_bidirectional_channel(&message, &[]).unwrap();
channel.our_public_key = key;
channel.their_public_key = key;
channel.check_code = CheckCode { bytes: [0, 1] };
assert_debug_snapshot!(channel);
}
}