use crate::{
key::{argon2id_hash, balloon_hash},
protected::Protected,
};
use super::primitives::{get_nonce_len, Algorithm, Mode, ENCRYPTED_MASTER_KEY_LEN, SALT_LEN};
use anyhow::{Context, Result};
use std::io::{Cursor, Read, Seek, Write};
pub const HEADER_VERSION: HeaderVersion = HeaderVersion::V5;
#[allow(clippy::module_name_repetitions)]
#[derive(PartialEq, Eq, Clone, Copy, PartialOrd)]
pub enum HeaderVersion {
V1,
V2,
V3,
V4,
V5,
}
impl std::fmt::Display for HeaderVersion {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
HeaderVersion::V1 => write!(f, "V1"),
HeaderVersion::V2 => write!(f, "V2"),
HeaderVersion::V3 => write!(f, "V3"),
HeaderVersion::V4 => write!(f, "V4"),
HeaderVersion::V5 => write!(f, "V5"),
}
}
}
#[allow(clippy::module_name_repetitions)]
pub struct HeaderType {
pub version: HeaderVersion,
pub algorithm: Algorithm,
pub mode: Mode,
}
struct HeaderTag {
pub version: [u8; 2],
pub algorithm: [u8; 2],
pub mode: [u8; 2],
}
pub struct Header {
pub header_type: HeaderType,
pub nonce: Vec<u8>,
pub salt: Option<[u8; SALT_LEN]>, pub keyslots: Option<Vec<Keyslot>>,
}
pub const ARGON2ID_LATEST: i32 = 3;
pub const BLAKE3BALLOON_LATEST: i32 = 5;
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum HashingAlgorithm {
Argon2id(i32),
Blake3Balloon(i32),
}
impl std::fmt::Display for HashingAlgorithm {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
HashingAlgorithm::Argon2id(i) => write!(f, "Argon2id (param v{})", i),
HashingAlgorithm::Blake3Balloon(i) => write!(f, "BLAKE3-Balloon (param v{})", i),
}
}
}
impl HashingAlgorithm {
pub fn hash(
&self,
raw_key: Protected<Vec<u8>>,
salt: &[u8; SALT_LEN],
) -> Result<Protected<[u8; 32]>, anyhow::Error> {
match self {
HashingAlgorithm::Argon2id(i) => match i {
1 => argon2id_hash(raw_key, salt, &HeaderVersion::V1),
2 => argon2id_hash(raw_key, salt, &HeaderVersion::V2),
3 => argon2id_hash(raw_key, salt, &HeaderVersion::V3),
_ => Err(anyhow::anyhow!(
"argon2id is not supported with the parameters provided."
)),
},
HashingAlgorithm::Blake3Balloon(i) => match i {
4 => balloon_hash(raw_key, salt, &HeaderVersion::V4),
5 => balloon_hash(raw_key, salt, &HeaderVersion::V5),
_ => Err(anyhow::anyhow!(
"Balloon hashing is not supported with the parameters provided."
)),
},
}
}
}
#[derive(Clone)]
pub struct Keyslot {
pub hash_algorithm: HashingAlgorithm,
pub encrypted_key: [u8; ENCRYPTED_MASTER_KEY_LEN],
pub nonce: Vec<u8>,
pub salt: [u8; SALT_LEN],
}
impl Keyslot {
#[must_use]
pub fn serialize(&self) -> [u8; 2] {
match self.hash_algorithm {
HashingAlgorithm::Argon2id(i) => match i {
1 => [0xDF, 0xA1],
2 => [0xDF, 0xA2],
3 => [0xDF, 0xA3],
_ => [0x00, 0x00],
},
HashingAlgorithm::Blake3Balloon(i) => match i {
4 => [0xDF, 0xB4],
5 => [0xDF, 0xB5],
_ => [0x00, 0x00],
},
}
}
}
impl Header {
fn get_tag(&self) -> HeaderTag {
let version = self.serialize_version();
let algorithm = self.serialize_algorithm();
let mode = self.serialize_mode();
HeaderTag {
version,
algorithm,
mode,
}
}
fn serialize_version(&self) -> [u8; 2] {
match self.header_type.version {
HeaderVersion::V1 => {
let info: [u8; 2] = [0xDE, 0x01];
info
}
HeaderVersion::V2 => {
let info: [u8; 2] = [0xDE, 0x02];
info
}
HeaderVersion::V3 => {
let info: [u8; 2] = [0xDE, 0x03];
info
}
HeaderVersion::V4 => {
let info: [u8; 2] = [0xDE, 0x04];
info
}
HeaderVersion::V5 => {
let info: [u8; 2] = [0xDE, 0x05];
info
}
}
}
#[allow(clippy::too_many_lines)]
pub fn deserialize(reader: &mut (impl Read + Seek)) -> Result<(Self, Vec<u8>)> {
let mut version_bytes = [0u8; 2];
reader
.read_exact(&mut version_bytes)
.context("Unable to read version from the header")?;
reader
.seek(std::io::SeekFrom::Current(-2))
.context("Unable to seek back to start of header")?;
let version = match version_bytes {
[0xDE, 0x01] => HeaderVersion::V1,
[0xDE, 0x02] => HeaderVersion::V2,
[0xDE, 0x03] => HeaderVersion::V3,
[0xDE, 0x04] => HeaderVersion::V4,
[0xDE, 0x05] => HeaderVersion::V5,
_ => return Err(anyhow::anyhow!("Error getting version from header")),
};
let header_length: usize = match version {
HeaderVersion::V1 | HeaderVersion::V2 | HeaderVersion::V3 => 64,
HeaderVersion::V4 => 128,
HeaderVersion::V5 => 416,
};
let mut full_header_bytes = vec![0u8; header_length];
reader
.read_exact(&mut full_header_bytes)
.context("Unable to read full bytes of the header")?;
let mut cursor = Cursor::new(full_header_bytes.clone());
cursor
.seek(std::io::SeekFrom::Start(2))
.context("Unable to seek past version bytes")?;
let mut algorithm_bytes = [0u8; 2];
cursor
.read_exact(&mut algorithm_bytes)
.context("Unable to read algorithm's bytes from header")?;
let algorithm = match algorithm_bytes {
[0x0E, 0x01] => Algorithm::XChaCha20Poly1305,
[0x0E, 0x02] => Algorithm::Aes256Gcm,
[0x0E, 0x03] => Algorithm::DeoxysII256,
_ => return Err(anyhow::anyhow!("Error getting encryption mode from header")),
};
let mut mode_bytes = [0u8; 2];
cursor
.read_exact(&mut mode_bytes)
.context("Unable to read encryption mode's bytes from header")?;
let mode = match mode_bytes {
[0x0C, 0x01] => Mode::StreamMode,
[0x0C, 0x02] => Mode::MemoryMode,
_ => return Err(anyhow::anyhow!("Error getting cipher mode from header")),
};
let header_type = HeaderType {
version,
algorithm,
mode,
};
let nonce_len = get_nonce_len(&header_type.algorithm, &header_type.mode);
let mut salt = [0u8; 16];
let mut nonce = vec![0u8; nonce_len];
let keyslots: Option<Vec<Keyslot>> = match header_type.version {
HeaderVersion::V1 | HeaderVersion::V3 => {
cursor
.read_exact(&mut salt)
.context("Unable to read salt from header")?;
cursor
.read_exact(&mut [0; 16])
.context("Unable to read empty bytes from header")?;
cursor
.read_exact(&mut nonce)
.context("Unable to read nonce from header")?;
cursor
.read_exact(&mut vec![0u8; 26 - nonce_len])
.context("Unable to read final padding from header")?;
None
}
HeaderVersion::V2 => {
cursor
.read_exact(&mut salt)
.context("Unable to read salt from header")?;
cursor
.read_exact(&mut nonce)
.context("Unable to read nonce from header")?;
cursor
.read_exact(&mut vec![0u8; 26 - nonce_len])
.context("Unable to read empty bytes from header")?;
cursor
.read_exact(&mut [0u8; 16])
.context("Unable to read final padding from header")?;
None
}
HeaderVersion::V4 => {
let mut master_key_encrypted = [0u8; 48];
let master_key_nonce_len = get_nonce_len(&algorithm, &Mode::MemoryMode);
let mut master_key_nonce = vec![0u8; master_key_nonce_len];
cursor
.read_exact(&mut salt)
.context("Unable to read salt from header")?;
cursor
.read_exact(&mut nonce)
.context("Unable to read nonce from header")?;
cursor
.read_exact(&mut vec![0u8; 26 - nonce_len])
.context("Unable to read padding from header")?;
cursor
.read_exact(&mut master_key_encrypted)
.context("Unable to read encrypted master key from header")?;
cursor
.read_exact(&mut master_key_nonce)
.context("Unable to read master key nonce from header")?;
cursor
.read_exact(&mut vec![0u8; 32 - master_key_nonce_len])
.context("Unable to read padding from header")?;
let keyslot = Keyslot {
encrypted_key: master_key_encrypted,
hash_algorithm: HashingAlgorithm::Blake3Balloon(4),
nonce: master_key_nonce.clone(),
salt,
};
let keyslots = vec![keyslot];
Some(keyslots)
}
HeaderVersion::V5 => {
cursor
.read_exact(&mut nonce)
.context("Unable to read nonce from header")?;
cursor
.read_exact(&mut vec![0u8; 26 - nonce_len])
.context("Unable to read padding from header")?;
let keyslot_nonce_len = get_nonce_len(&algorithm, &Mode::MemoryMode);
let mut keyslots: Vec<Keyslot> = Vec::new();
for _ in 0..4 {
let mut identifier = [0u8; 2];
cursor
.read_exact(&mut identifier)
.context("Unable to read keyslot identifier from header")?;
if identifier[..1] != [0xDF] {
continue;
}
let mut encrypted_key = [0u8; 48];
let mut nonce = vec![0u8; keyslot_nonce_len];
let mut padding = vec![0u8; 24 - keyslot_nonce_len];
let mut salt = [0u8; SALT_LEN];
cursor
.read_exact(&mut encrypted_key)
.context("Unable to read keyslot encrypted bytes from header")?;
cursor
.read_exact(&mut nonce)
.context("Unable to read keyslot nonce from header")?;
cursor
.read_exact(&mut padding)
.context("Unable to read keyslot padding from header")?;
cursor
.read_exact(&mut salt)
.context("Unable to read keyslot salt from header")?;
cursor
.read_exact(&mut [0u8; 6])
.context("Unable to read keyslot padding from header")?;
let hash_algorithm = match identifier {
[0xDF, 0xA1] => HashingAlgorithm::Argon2id(1),
[0xDF, 0xA2] => HashingAlgorithm::Argon2id(2),
[0xDF, 0xA3] => HashingAlgorithm::Argon2id(3),
[0xDF, 0xB4] => HashingAlgorithm::Blake3Balloon(4),
[0xDF, 0xB5] => HashingAlgorithm::Blake3Balloon(5),
_ => return Err(anyhow::anyhow!("Key hashing algorithm not identified")),
};
let keyslot = Keyslot {
hash_algorithm,
encrypted_key,
nonce,
salt,
};
keyslots.push(keyslot);
}
Some(keyslots)
}
};
let aad = match header_type.version {
HeaderVersion::V1 | HeaderVersion::V2 => Vec::<u8>::new(),
HeaderVersion::V3 => full_header_bytes.clone(),
HeaderVersion::V4 => {
let master_key_nonce_len = get_nonce_len(&algorithm, &Mode::MemoryMode);
let mut aad = Vec::new();
aad.extend_from_slice(&full_header_bytes[..48]);
aad.extend_from_slice(&full_header_bytes[(96 + master_key_nonce_len)..]);
aad
}
HeaderVersion::V5 => {
let mut aad = Vec::new();
aad.extend_from_slice(&full_header_bytes[..32]);
aad
}
};
Ok((
Header {
header_type,
nonce,
salt: Some(salt),
keyslots,
},
aad,
))
}
fn serialize_algorithm(&self) -> [u8; 2] {
match self.header_type.algorithm {
Algorithm::XChaCha20Poly1305 => {
let info: [u8; 2] = [0x0E, 0x01];
info
}
Algorithm::Aes256Gcm => {
let info: [u8; 2] = [0x0E, 0x02];
info
}
Algorithm::DeoxysII256 => {
let info: [u8; 2] = [0x0E, 0x03];
info
}
}
}
fn serialize_mode(&self) -> [u8; 2] {
match self.header_type.mode {
Mode::StreamMode => {
let info: [u8; 2] = [0x0C, 0x01];
info
}
Mode::MemoryMode => {
let info: [u8; 2] = [0x0C, 0x02];
info
}
}
}
fn serialize_v3(&self, tag: &HeaderTag) -> Vec<u8> {
let padding =
vec![0u8; 26 - get_nonce_len(&self.header_type.algorithm, &self.header_type.mode)];
let mut header_bytes = Vec::<u8>::new();
header_bytes.extend_from_slice(&tag.version);
header_bytes.extend_from_slice(&tag.algorithm);
header_bytes.extend_from_slice(&tag.mode);
header_bytes.extend_from_slice(&self.salt.unwrap());
header_bytes.extend_from_slice(&[0; 16]);
header_bytes.extend_from_slice(&self.nonce);
header_bytes.extend_from_slice(&padding);
header_bytes
}
fn serialize_v4(&self, tag: &HeaderTag) -> Vec<u8> {
let padding =
vec![0u8; 26 - get_nonce_len(&self.header_type.algorithm, &self.header_type.mode)];
let padding2 =
vec![0u8; 32 - get_nonce_len(&self.header_type.algorithm, &Mode::MemoryMode)];
let keyslot = self.keyslots.clone().unwrap();
let mut header_bytes = Vec::<u8>::new();
header_bytes.extend_from_slice(&tag.version);
header_bytes.extend_from_slice(&tag.algorithm);
header_bytes.extend_from_slice(&tag.mode);
header_bytes.extend_from_slice(&self.salt.unwrap_or(keyslot[0].salt));
header_bytes.extend_from_slice(&self.nonce);
header_bytes.extend_from_slice(&padding);
header_bytes.extend_from_slice(&keyslot[0].encrypted_key);
header_bytes.extend_from_slice(&keyslot[0].nonce);
header_bytes.extend_from_slice(&padding2);
header_bytes
}
fn serialize_v5(&self, tag: &HeaderTag) -> Vec<u8> {
let padding =
vec![0u8; 26 - get_nonce_len(&self.header_type.algorithm, &self.header_type.mode)];
let keyslots = self.keyslots.clone().unwrap();
let mut header_bytes = Vec::<u8>::new();
header_bytes.extend_from_slice(&tag.version);
header_bytes.extend_from_slice(&tag.algorithm);
header_bytes.extend_from_slice(&tag.mode);
header_bytes.extend_from_slice(&self.nonce);
header_bytes.extend_from_slice(&padding);
for keyslot in &keyslots {
let keyslot_nonce_len = get_nonce_len(&self.header_type.algorithm, &Mode::MemoryMode);
header_bytes.extend_from_slice(&keyslot.serialize());
header_bytes.extend_from_slice(&keyslot.encrypted_key);
header_bytes.extend_from_slice(&keyslot.nonce);
header_bytes.extend_from_slice(&vec![0u8; 24 - keyslot_nonce_len]);
header_bytes.extend_from_slice(&keyslot.salt);
header_bytes.extend_from_slice(&[0u8; 6]);
}
for _ in 0..(4 - keyslots.len()) {
header_bytes.extend_from_slice(&[0u8; 96]);
}
header_bytes
}
pub fn serialize(&self) -> Result<Vec<u8>> {
let tag = self.get_tag();
match self.header_type.version {
HeaderVersion::V1 => Err(anyhow::anyhow!(
"Serializing V1 headers has been deprecated"
)),
HeaderVersion::V2 => Err(anyhow::anyhow!(
"Serializing V2 headers has been deprecated"
)),
HeaderVersion::V3 => Ok(self.serialize_v3(&tag)),
HeaderVersion::V4 => Ok(self.serialize_v4(&tag)),
HeaderVersion::V5 => Ok(self.serialize_v5(&tag)),
}
}
#[must_use]
pub fn get_size(&self) -> u64 {
match self.header_type.version {
HeaderVersion::V1 | HeaderVersion::V2 | HeaderVersion::V3 => 64,
HeaderVersion::V4 => 128,
HeaderVersion::V5 => 416,
}
}
pub fn create_aad(&self) -> Result<Vec<u8>> {
let tag = self.get_tag();
match self.header_type.version {
HeaderVersion::V1 => Err(anyhow::anyhow!(
"Serializing V1 headers has been deprecated"
)),
HeaderVersion::V2 => Err(anyhow::anyhow!(
"Serializing V2 headers has been deprecated"
)),
HeaderVersion::V3 => Ok(self.serialize_v3(&tag)),
HeaderVersion::V4 => {
let padding =
vec![
0u8;
26 - get_nonce_len(&self.header_type.algorithm, &self.header_type.mode)
];
let master_key_nonce_len =
get_nonce_len(&self.header_type.algorithm, &Mode::MemoryMode);
let padding2 = vec![0u8; 32 - master_key_nonce_len];
let mut header_bytes = Vec::<u8>::new();
header_bytes.extend_from_slice(&tag.version);
header_bytes.extend_from_slice(&tag.algorithm);
header_bytes.extend_from_slice(&tag.mode);
header_bytes.extend_from_slice(
&self.salt.unwrap_or(
self.keyslots.as_ref().ok_or_else(|| {
anyhow::anyhow!("Cannot find a salt within the keyslot/header.")
})?[0]
.salt,
),
);
header_bytes.extend_from_slice(&self.nonce);
header_bytes.extend_from_slice(&padding);
header_bytes.extend_from_slice(&padding2);
Ok(header_bytes)
}
HeaderVersion::V5 => {
let mut header_bytes = Vec::<u8>::new();
header_bytes.extend_from_slice(&tag.version);
header_bytes.extend_from_slice(&tag.algorithm);
header_bytes.extend_from_slice(&tag.mode);
header_bytes.extend_from_slice(&self.nonce);
header_bytes.extend_from_slice(&vec![
0u8;
26 - get_nonce_len(
&self.header_type.algorithm,
&self.header_type.mode
)
]);
Ok(header_bytes)
}
}
}
pub fn write(&self, writer: &mut impl Write) -> Result<()> {
let header_bytes = self.serialize()?;
writer
.write(&header_bytes)
.context("Unable to write header")?;
Ok(())
}
}