#[cfg(doc)]
use super::EstablishedHpkeChannel;
use crate::{Curve25519PublicKey, base64_decode, base64_encode, hpke::error::MessageDecodeError};
#[derive(Debug, PartialEq, Eq)]
pub struct InitialMessage {
pub encapsulated_key: Curve25519PublicKey,
pub ciphertext: Vec<u8>,
}
impl InitialMessage {
pub fn encode(&self) -> String {
let bytes = self.to_bytes();
base64_encode(bytes)
}
pub fn decode(message: &str) -> Result<Self, MessageDecodeError> {
let bytes = base64_decode(message)?;
Self::from_bytes(&bytes)
}
pub fn to_bytes(&self) -> Vec<u8> {
let Self { encapsulated_key, ciphertext } = self;
[encapsulated_key.to_bytes().as_slice(), ciphertext].concat()
}
pub fn from_bytes(bytes: &[u8]) -> Result<Self, MessageDecodeError> {
let (encapsulated_key, ciphertext) = decode_message_with_byte_prefix(bytes)?;
let encapsulated_key = Curve25519PublicKey::from_bytes(encapsulated_key);
Ok(Self { encapsulated_key, ciphertext })
}
}
#[derive(Debug)]
pub struct InitialResponse {
pub base_response_nonce: [u8; 32],
pub ciphertext: Vec<u8>,
}
impl InitialResponse {
pub fn encode(&self) -> String {
let bytes = self.to_bytes();
base64_encode(bytes)
}
pub fn decode(message: &str) -> Result<Self, MessageDecodeError> {
let bytes = base64_decode(message)?;
Self::from_bytes(&bytes)
}
pub fn from_bytes(bytes: &[u8]) -> Result<Self, MessageDecodeError> {
let (base_response_nonce, ciphertext) = decode_message_with_byte_prefix(bytes)?;
Ok(Self { base_response_nonce, ciphertext })
}
pub fn to_bytes(&self) -> Vec<u8> {
let Self { base_response_nonce, ciphertext } = self;
[base_response_nonce.as_slice(), ciphertext].concat()
}
}
fn decode_message_with_byte_prefix(
bytes: &[u8],
) -> Result<([u8; 32], Vec<u8>), MessageDecodeError> {
bytes
.split_first_chunk::<32>()
.map(|(nonce, ciphertext)| (nonce.to_owned(), ciphertext.to_owned()))
.ok_or(MessageDecodeError::MessageIncomplete)
}
#[derive(Debug)]
pub struct Message {
pub ciphertext: Vec<u8>,
}
impl Message {
pub fn encode(&self) -> String {
base64_encode(&self.ciphertext)
}
pub fn decode(message: &str) -> Result<Self, MessageDecodeError> {
let ciphertext = base64_decode(message)?;
if ciphertext.is_empty() {
Err(MessageDecodeError::MessageIncomplete)
} else {
Ok(Self { ciphertext })
}
}
}
#[cfg(test)]
mod test {
use super::*;
const INITIAL_MESSAGE: &str = "9yA/CX8pJKF02Prd75ZyBQHg3fGTVVGDNl86q1z17Uvc6ftAUnItAwASu5r0r/Ig5wkAu+4xhrHUBbSJaB/rgDC1IxlfAADTXZA";
const INITIAL_RESPONSE: &str = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADc6ftAUnItAwASu5r0r/Ig5wkAu+4xhrHUBbSJaB/rgDC1IxlfAADTXZA";
const MESSAGE: &str = "ZmtSLdzMcyjC5eV6L8xBI6amsq7gDNbCjz1W5OjX4Z8W";
const PUBLIC_KEY: &str = "9yA/CX8pJKF02Prd75ZyBQHg3fGTVVGDNl86q1z17Us";
#[test]
fn initial_message() {
let message = InitialMessage::decode(INITIAL_MESSAGE)
.expect("We should be able to decode our known-valid initial message");
assert_eq!(
message.encapsulated_key.to_base64(),
PUBLIC_KEY,
"The decoded public key should match the expected one"
);
let encoded = message.encode();
assert_eq!(INITIAL_MESSAGE, encoded);
InitialMessage::decode("").expect_err("An empty message should fail to be decoded");
}
#[test]
fn initial_response() {
let message = InitialResponse::decode(INITIAL_RESPONSE)
.expect("We should be able to decode our known-valid initial message");
assert_eq!(
message.base_response_nonce, [0u8; 32],
"The decoded nonce should match the expected one"
);
let encoded = message.encode();
assert_eq!(INITIAL_RESPONSE, encoded);
InitialResponse::decode("").expect_err("An empty message should fail to be decoded");
}
#[test]
fn message() {
let message = Message::decode(MESSAGE)
.expect("We should be able to decode our known-valid initial message");
let encoded = message.encode();
assert_eq!(MESSAGE, encoded);
Message::decode("").expect_err("An empty message should fail to be decoded");
}
}