Improve the error management
This commit is contained in:
parent
32eba4345f
commit
b99746bd65
5 changed files with 84 additions and 38 deletions
|
@ -18,3 +18,4 @@ hazardous-materials = []
|
||||||
[dependencies]
|
[dependencies]
|
||||||
base64ct = { version = "1.6.0", default-features = false, features = ["std"] }
|
base64ct = { version = "1.6.0", default-features = false, features = ["std"] }
|
||||||
getrandom = { version = "0.2.12", default-features = false }
|
getrandom = { version = "0.2.12", default-features = false }
|
||||||
|
thiserror = { version = "1.0.57", default-features = false }
|
||||||
|
|
35
src/error.rs
Normal file
35
src/error.rs
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
pub enum Error {
|
||||||
|
#[error("parsing error: invalid base64-urlsafe-nopadding data: {0}")]
|
||||||
|
ParsingBase64Error(base64ct::Error),
|
||||||
|
#[error("parsing error: invalid data length: {0} bytes")]
|
||||||
|
ParsingInvalidLength(usize),
|
||||||
|
#[error("parsing error: {0}: unknown scheme")]
|
||||||
|
ParsingUnknownScheme(u32),
|
||||||
|
#[error("unable to generate random values: {0}")]
|
||||||
|
RandomSourceError(getrandom::Error),
|
||||||
|
#[error("system time error: {0}")]
|
||||||
|
SystemTimeError(std::time::SystemTimeError),
|
||||||
|
#[error("system time error: {0}: unable to represent this timestamp as a system time")]
|
||||||
|
SystemTimeReprError(u64),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<base64ct::Error> for Error {
|
||||||
|
fn from(error: base64ct::Error) -> Self {
|
||||||
|
Error::ParsingBase64Error(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<getrandom::Error> for Error {
|
||||||
|
fn from(error: getrandom::Error) -> Self {
|
||||||
|
Error::RandomSourceError(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<std::time::SystemTimeError> for Error {
|
||||||
|
fn from(error: std::time::SystemTimeError) -> Self {
|
||||||
|
Error::SystemTimeError(error)
|
||||||
|
}
|
||||||
|
}
|
78
src/ikm.rs
78
src/ikm.rs
|
@ -1,4 +1,4 @@
|
||||||
use crate::Scheme;
|
use crate::{Error, Scheme};
|
||||||
use base64ct::{Base64UrlUnpadded, Encoding};
|
use base64ct::{Base64UrlUnpadded, Encoding};
|
||||||
use std::time::{Duration, SystemTime};
|
use std::time::{Duration, SystemTime};
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@ pub struct InputKeyMaterial {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InputKeyMaterial {
|
impl InputKeyMaterial {
|
||||||
fn as_bytes(&self) -> [u8; IKM_STRUCT_SIZE] {
|
fn as_bytes(&self) -> Result<[u8; IKM_STRUCT_SIZE], Error> {
|
||||||
let mut res = Vec::with_capacity(IKM_STRUCT_SIZE);
|
let mut res = Vec::with_capacity(IKM_STRUCT_SIZE);
|
||||||
res.extend_from_slice(&self.id.to_le_bytes());
|
res.extend_from_slice(&self.id.to_le_bytes());
|
||||||
res.extend_from_slice(&(self.scheme as u32).to_le_bytes());
|
res.extend_from_slice(&(self.scheme as u32).to_le_bytes());
|
||||||
|
@ -24,42 +24,38 @@ impl InputKeyMaterial {
|
||||||
res.extend_from_slice(
|
res.extend_from_slice(
|
||||||
&self
|
&self
|
||||||
.created_at
|
.created_at
|
||||||
.duration_since(SystemTime::UNIX_EPOCH)
|
.duration_since(SystemTime::UNIX_EPOCH)?
|
||||||
.unwrap()
|
|
||||||
.as_secs()
|
.as_secs()
|
||||||
.to_le_bytes(),
|
.to_le_bytes(),
|
||||||
);
|
);
|
||||||
res.extend_from_slice(
|
res.extend_from_slice(
|
||||||
&self
|
&self
|
||||||
.expire_at
|
.expire_at
|
||||||
.duration_since(SystemTime::UNIX_EPOCH)
|
.duration_since(SystemTime::UNIX_EPOCH)?
|
||||||
.unwrap()
|
|
||||||
.as_secs()
|
.as_secs()
|
||||||
.to_le_bytes(),
|
.to_le_bytes(),
|
||||||
);
|
);
|
||||||
res.push(self.is_revoked as u8);
|
res.push(self.is_revoked as u8);
|
||||||
res.try_into().unwrap()
|
Ok(res.try_into().unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_bytes(b: [u8; IKM_STRUCT_SIZE]) -> Self {
|
fn from_bytes(b: [u8; IKM_STRUCT_SIZE]) -> Result<Self, Error> {
|
||||||
Self {
|
Ok(Self {
|
||||||
id: u32::from_le_bytes(b[0..4].try_into().unwrap()),
|
id: u32::from_le_bytes(b[0..4].try_into().unwrap()),
|
||||||
scheme: u32::from_le_bytes(b[4..8].try_into().unwrap())
|
scheme: u32::from_le_bytes(b[4..8].try_into().unwrap()).try_into()?,
|
||||||
.try_into()
|
|
||||||
.unwrap(),
|
|
||||||
content: b[8..40].try_into().unwrap(),
|
content: b[8..40].try_into().unwrap(),
|
||||||
created_at: SystemTime::UNIX_EPOCH
|
created_at: InputKeyMaterial::bytes_to_system_time(&b[40..48])?,
|
||||||
.checked_add(Duration::from_secs(u64::from_le_bytes(
|
expire_at: InputKeyMaterial::bytes_to_system_time(&b[48..56])?,
|
||||||
b[40..48].try_into().unwrap(),
|
|
||||||
)))
|
|
||||||
.unwrap(),
|
|
||||||
expire_at: SystemTime::UNIX_EPOCH
|
|
||||||
.checked_add(Duration::from_secs(u64::from_le_bytes(
|
|
||||||
b[48..56].try_into().unwrap(),
|
|
||||||
)))
|
|
||||||
.unwrap(),
|
|
||||||
is_revoked: b[56] != 0,
|
is_revoked: b[56] != 0,
|
||||||
}
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bytes_to_system_time(ts_slice: &[u8]) -> Result<SystemTime, Error> {
|
||||||
|
let ts_array: [u8; 8] = ts_slice.try_into().unwrap();
|
||||||
|
let ts = u64::from_le_bytes(ts_array);
|
||||||
|
Ok(SystemTime::UNIX_EPOCH
|
||||||
|
.checked_add(Duration::from_secs(ts))
|
||||||
|
.ok_or(Error::SystemTimeReprError(ts))?)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,11 +70,11 @@ impl InputKeyMaterialList {
|
||||||
Self::default()
|
Self::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_ikm(&mut self) -> Result<(), getrandom::Error> {
|
pub fn add_ikm(&mut self) -> Result<(), Error> {
|
||||||
self.add_ikm_with_duration(Duration::from_secs(crate::DEFAULT_IKM_DURATION))
|
self.add_ikm_with_duration(Duration::from_secs(crate::DEFAULT_IKM_DURATION))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_ikm_with_duration(&mut self, duration: Duration) -> Result<(), getrandom::Error> {
|
pub fn add_ikm_with_duration(&mut self, duration: Duration) -> Result<(), Error> {
|
||||||
let mut content: [u8; 32] = [0; 32];
|
let mut content: [u8; 32] = [0; 32];
|
||||||
getrandom::getrandom(&mut content)?;
|
getrandom::getrandom(&mut content)?;
|
||||||
let created_at = SystemTime::now();
|
let created_at = SystemTime::now();
|
||||||
|
@ -94,24 +90,24 @@ impl InputKeyMaterialList {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn export(&self) -> String {
|
pub fn export(&self) -> Result<String, Error> {
|
||||||
let data_size = (self.ikm_lst.len() * IKM_STRUCT_SIZE) + 4;
|
let data_size = (self.ikm_lst.len() * IKM_STRUCT_SIZE) + 4;
|
||||||
let mut data = Vec::with_capacity(data_size);
|
let mut data = Vec::with_capacity(data_size);
|
||||||
data.extend_from_slice(&self.id_counter.to_le_bytes());
|
data.extend_from_slice(&self.id_counter.to_le_bytes());
|
||||||
for ikm in &self.ikm_lst {
|
for ikm in &self.ikm_lst {
|
||||||
data.extend_from_slice(&ikm.as_bytes());
|
data.extend_from_slice(&ikm.as_bytes()?);
|
||||||
}
|
}
|
||||||
Base64UrlUnpadded::encode_string(&data)
|
Ok(Base64UrlUnpadded::encode_string(&data))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn import(s: &str) -> Result<Self, String> {
|
pub fn import(s: &str) -> Result<Self, Error> {
|
||||||
let data = Base64UrlUnpadded::decode_vec(s).unwrap();
|
let data = Base64UrlUnpadded::decode_vec(s)?;
|
||||||
if data.len() % IKM_STRUCT_SIZE != 4 {
|
if data.len() % IKM_STRUCT_SIZE != 4 {
|
||||||
return Err("Invalid string".to_string());
|
return Err(Error::ParsingInvalidLength(data.len()));
|
||||||
}
|
}
|
||||||
let mut ikm_lst = Vec::with_capacity(data.len() / IKM_STRUCT_SIZE);
|
let mut ikm_lst = Vec::with_capacity(data.len() / IKM_STRUCT_SIZE);
|
||||||
for ikm_slice in data[4..].chunks_exact(IKM_STRUCT_SIZE) {
|
for ikm_slice in data[4..].chunks_exact(IKM_STRUCT_SIZE) {
|
||||||
ikm_lst.push(InputKeyMaterial::from_bytes(ikm_slice.try_into().unwrap()));
|
ikm_lst.push(InputKeyMaterial::from_bytes(ikm_slice.try_into().unwrap())?);
|
||||||
}
|
}
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
ikm_lst,
|
ikm_lst,
|
||||||
|
@ -163,7 +159,9 @@ mod tests {
|
||||||
assert_eq!(lst.id_counter, 0);
|
assert_eq!(lst.id_counter, 0);
|
||||||
assert_eq!(lst.ikm_lst.len(), 0);
|
assert_eq!(lst.ikm_lst.len(), 0);
|
||||||
|
|
||||||
let s = lst.export();
|
let res = lst.export();
|
||||||
|
assert!(res.is_ok());
|
||||||
|
let s = res.unwrap();
|
||||||
assert_eq!(&s, "AAAAAA");
|
assert_eq!(&s, "AAAAAA");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -172,7 +170,9 @@ mod tests {
|
||||||
let mut lst = InputKeyMaterialList::new();
|
let mut lst = InputKeyMaterialList::new();
|
||||||
let _ = lst.add_ikm();
|
let _ = lst.add_ikm();
|
||||||
|
|
||||||
let s = lst.export();
|
let res = lst.export();
|
||||||
|
assert!(res.is_ok());
|
||||||
|
let s = res.unwrap();
|
||||||
assert_eq!(s.len(), 82);
|
assert_eq!(s.len(), 82);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -202,7 +202,10 @@ mod tests {
|
||||||
fn export_import_empty() {
|
fn export_import_empty() {
|
||||||
let lst = InputKeyMaterialList::new();
|
let lst = InputKeyMaterialList::new();
|
||||||
|
|
||||||
let s = lst.export();
|
let res = lst.export();
|
||||||
|
assert!(res.is_ok());
|
||||||
|
let s = res.unwrap();
|
||||||
|
|
||||||
let res = InputKeyMaterialList::import(&s);
|
let res = InputKeyMaterialList::import(&s);
|
||||||
assert!(res.is_ok());
|
assert!(res.is_ok());
|
||||||
let lst_bis = res.unwrap();
|
let lst_bis = res.unwrap();
|
||||||
|
@ -219,7 +222,10 @@ mod tests {
|
||||||
let _ = lst.add_ikm();
|
let _ = lst.add_ikm();
|
||||||
}
|
}
|
||||||
|
|
||||||
let s = lst.export();
|
let res = lst.export();
|
||||||
|
assert!(res.is_ok());
|
||||||
|
let s = res.unwrap();
|
||||||
|
|
||||||
let res = InputKeyMaterialList::import(&s);
|
let res = InputKeyMaterialList::import(&s);
|
||||||
assert!(res.is_ok());
|
assert!(res.is_ok());
|
||||||
let lst_bis = res.unwrap();
|
let lst_bis = res.unwrap();
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
|
mod error;
|
||||||
mod ikm;
|
mod ikm;
|
||||||
mod scheme;
|
mod scheme;
|
||||||
|
|
||||||
|
pub use error::Error;
|
||||||
pub use ikm::InputKeyMaterialList;
|
pub use ikm::InputKeyMaterialList;
|
||||||
pub use scheme::Scheme;
|
pub use scheme::Scheme;
|
||||||
|
|
||||||
|
|
|
@ -1,15 +1,17 @@
|
||||||
|
use crate::Error;
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||||
pub enum Scheme {
|
pub enum Scheme {
|
||||||
XChaCha20Poly1305WithBlake3 = 1,
|
XChaCha20Poly1305WithBlake3 = 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<u32> for Scheme {
|
impl TryFrom<u32> for Scheme {
|
||||||
type Error = &'static str;
|
type Error = Error;
|
||||||
|
|
||||||
fn try_from(value: u32) -> Result<Self, Self::Error> {
|
fn try_from(value: u32) -> Result<Self, Self::Error> {
|
||||||
match value {
|
match value {
|
||||||
1 => Ok(Scheme::XChaCha20Poly1305WithBlake3),
|
1 => Ok(Scheme::XChaCha20Poly1305WithBlake3),
|
||||||
_ => Err("unknown scheme"),
|
_ => Err(Error::ParsingUnknownScheme(value)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue