mod inner;
mod rev0;
mod secret;
use nuts_backend::{Backend, Binary};
use openssl::error::ErrorStack;
use std::fmt::{self, Write as FmtWrite};
use thiserror::Error;
use crate::buffer::BufferError;
use crate::cipher::{Cipher, CipherError};
use crate::header::inner::{Inner, Revision};
use crate::header::secret::PlainSecret;
use crate::kdf::{Kdf, KdfError};
use crate::options::CreateOptions;
use crate::ossl;
use crate::password::{PasswordError, PasswordStore};
use crate::svec::SecureVec;
#[derive(Debug, Error)]
pub enum HeaderError {
#[error(transparent)]
Cipher(#[from] CipherError),
#[error(transparent)]
Kdf(#[from] KdfError),
#[error(transparent)]
Password(#[from] PasswordError),
#[error("the password is wrong")]
WrongPassword,
#[error("invalid header")]
InvalidHeader,
#[error("invalid settings")]
InvalidSettings,
#[error(transparent)]
Buffer(#[from] BufferError),
#[error(transparent)]
OpenSSL(#[from] ErrorStack),
}
pub struct Header {
pub(crate) cipher: Cipher,
pub(crate) kdf: Kdf,
pub(crate) key: SecureVec,
pub(crate) iv: SecureVec,
pub(crate) userdata: SecureVec,
}
impl Header {
pub fn create(options: &CreateOptions) -> Result<Header, HeaderError> {
let cipher = options.cipher;
let mut key = vec![0; cipher.key_len()];
let mut iv = vec![0; cipher.iv_len()];
ossl::rand_bytes(&mut key)?;
ossl::rand_bytes(&mut iv)?;
let kdf = options.kdf.build()?;
Ok(Header {
cipher,
kdf,
key: key.into(),
iv: iv.into(),
userdata: vec![].into(),
})
}
pub fn read<B: Backend>(
buf: &[u8],
store: &mut PasswordStore,
) -> Result<(Header, B::Settings), HeaderError> {
let inner = Inner::get_from_buffer(&mut &buf[..])?;
let Revision::Rev0(rev0) = inner.rev;
let plain_secret = rev0
.secret
.decrypt(store, rev0.cipher, &rev0.kdf, &rev0.iv)?;
let settings =
B::Settings::from_bytes(&plain_secret.settings).ok_or(HeaderError::InvalidSettings)?;
Ok((
Header {
cipher: rev0.cipher,
kdf: rev0.kdf,
key: plain_secret.key,
iv: plain_secret.iv,
userdata: plain_secret.userdata,
},
settings,
))
}
pub fn write<B: Backend>(
&self,
settings: B::Settings,
buf: &mut [u8],
store: &mut PasswordStore,
) -> Result<(), HeaderError> {
let plain_secret = PlainSecret::generate(
self.key.clone(),
self.iv.clone(),
self.userdata.clone(),
settings.as_bytes().into(),
)?;
let mut iv = vec![0; self.cipher.iv_len()];
ossl::rand_bytes(&mut iv)?;
let secret = plain_secret.encrypt(store, self.cipher, &self.kdf, &iv)?;
let rev0 = rev0::Data {
cipher: self.cipher,
iv,
kdf: self.kdf.clone(),
secret,
};
let inner = Inner::new(Revision::Rev0(rev0));
inner.put_into_buffer(&mut &mut buf[..])?;
Ok(())
}
}
impl fmt::Debug for Header {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
let (key, iv) = if cfg!(feature = "debug-plain-keys") {
let mut key = String::with_capacity(2 * self.key.len());
let mut iv = String::with_capacity(2 * self.iv.len());
for n in self.key.iter() {
write!(key, "{:02x}", n)?;
}
for n in self.iv.iter() {
write!(iv, "{:02x}", n)?;
}
(key, iv)
} else {
(
format!("<{} bytes>", self.key.len()),
format!("<{} bytes>", self.iv.len()),
)
};
fmt.debug_struct("Header")
.field("cipher", &self.cipher)
.field("kdf", &self.kdf)
.field("key", &key)
.field("iv", &iv)
.finish()
}
}