askar_storage/protect/kdf/
mod.rs1use super::store_key::{StoreKey, PREFIX_KDF};
4use crate::{
5 crypto::{buffer::ArrayKey, generic_array::ArrayLength},
6 error::Error,
7 options::Options,
8};
9
10mod argon2;
11pub use self::argon2::Level as Argon2Level;
12use self::argon2::SaltSize as Argon2Salt;
13
14pub const METHOD_ARGON2I: &str = "argon2i";
15
16#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
18pub enum KdfMethod {
19 Argon2i(Argon2Level),
21}
22
23impl KdfMethod {
24 pub(crate) fn decode(method: &str) -> Result<(Self, String), Error> {
25 let mut method_and_detail = method.splitn(3, ':');
26 let prefix = method_and_detail.next();
27 if prefix == Some(PREFIX_KDF) {
28 let method = method_and_detail.next().unwrap_or_default();
29 let mut level_and_detail = method_and_detail.next().unwrap_or_default().splitn(2, '?');
30 let level = level_and_detail.next().unwrap_or_default();
31 let detail = level_and_detail.next().unwrap_or_default();
32 if method == METHOD_ARGON2I {
33 if let Some(level) = Argon2Level::from_str(level) {
34 return Ok((
35 Self::Argon2i(level),
36 if detail.is_empty() {
37 "".to_owned()
38 } else {
39 format!("?{}", detail)
40 },
41 ));
42 }
43 }
44 }
45 Err(err_msg!(Unsupported, "Invalid key derivation method"))
46 }
47
48 pub(crate) fn encode(&self, detail: Option<&str>) -> String {
49 match self {
50 Self::Argon2i(level) => format!(
51 "{}:{}:{}{}",
52 PREFIX_KDF,
53 METHOD_ARGON2I,
54 level.as_str(),
55 detail.unwrap_or_default()
56 ),
57 }
58 }
59
60 pub(crate) fn derive_new_key(&self, password: &str) -> Result<(StoreKey, String), Error> {
61 match self {
62 Self::Argon2i(level) => {
63 let salt = level.generate_salt();
64 let key = level.derive_key(password.as_bytes(), salt.as_ref())?;
65 let detail = format!("?salt={}", salt.as_hex());
66 Ok((key, detail))
67 }
68 }
69 }
70
71 pub(crate) fn derive_key(&self, password: &str, detail: &str) -> Result<StoreKey, Error> {
72 match self {
73 Self::Argon2i(level) => {
74 let salt = parse_salt::<Argon2Salt>(detail)?;
75 let key = level.derive_key(password.as_bytes(), salt.as_ref())?;
76 Ok(key)
77 }
78 }
79 }
80}
81
82fn parse_salt<L: ArrayLength<u8>>(detail: &str) -> Result<ArrayKey<L>, Error> {
83 let opts = Options::parse_uri(detail)?;
84 if let Some(salt) = opts.query.get("salt") {
85 ArrayKey::<L>::try_new_with(|arr| {
86 hex::decode_to_slice(salt, arr).map_err(|_| err_msg!(Input, "Invalid salt"))
87 })
88 } else {
89 Err(err_msg!(Input, "Missing salt"))
90 }
91}