use crate::{
BinaryRequest, BinaryRequestKind, BinaryResponse, BinaryResponseKind, GatewayRequestsError,
SharedSymmetricKey,
};
use std::iter::once;
pub struct BinaryData<'a> {
kind: u8,
encrypted: bool,
nonce: &'a [u8],
data: &'a [u8],
}
impl<'a> BinaryData<'a> {
pub fn into_raw(self) -> Vec<u8> {
once(self.kind)
.chain(once(if self.encrypted { 1 } else { 0 }))
.chain(self.nonce.iter().copied())
.chain(self.data.iter().copied())
.collect()
}
pub fn from_raw(
raw: &'a [u8],
available_key: &SharedSymmetricKey,
) -> Result<Self, GatewayRequestsError> {
if raw.len() < 2 {
return Err(GatewayRequestsError::TooShortRequest);
}
let kind = raw[0];
let encrypted = if raw[1] == 1 {
true
} else if raw[1] == 0 {
false
} else {
return Err(GatewayRequestsError::InvalidEncryptionFlag);
};
if encrypted && raw.len() < available_key.nonce_size() + 2 {
return Err(GatewayRequestsError::TooShortRequest);
}
Ok(BinaryData {
kind,
encrypted,
nonce: &raw[2..2 + available_key.nonce_size()],
data: &raw[2 + available_key.nonce_size()..],
})
}
pub fn make_encrypted_blob(
kind: u8,
plaintext: &[u8],
key: &SharedSymmetricKey,
) -> Result<Vec<u8>, GatewayRequestsError> {
let nonce = key.random_nonce();
let ciphertext = key.encrypt(plaintext, &nonce)?;
Ok(BinaryData {
kind,
encrypted: true,
nonce: &nonce,
data: &ciphertext,
}
.into_raw())
}
pub fn into_request(
self,
key: &SharedSymmetricKey,
) -> Result<BinaryRequest, GatewayRequestsError> {
let kind = BinaryRequestKind::from_repr(self.kind)
.ok_or(GatewayRequestsError::UnknownRequestKind { kind: self.kind })?;
let plaintext = if self.encrypted {
let nonce = SharedSymmetricKey::validate_aead_nonce(self.nonce)?;
&*key.decrypt(self.data, &nonce)?
} else {
self.data
};
BinaryRequest::from_plaintext(kind, plaintext)
}
pub fn into_response(
self,
key: &SharedSymmetricKey,
) -> Result<BinaryResponse, GatewayRequestsError> {
let kind = BinaryResponseKind::from_repr(self.kind)
.ok_or(GatewayRequestsError::UnknownResponseKind { kind: self.kind })?;
let plaintext = if self.encrypted {
let nonce = SharedSymmetricKey::validate_aead_nonce(self.nonce)?;
&*key.decrypt(self.data, &nonce)?
} else {
self.data
};
BinaryResponse::from_plaintext(kind, plaintext)
}
}