mirror of
https://github.com/kanidm/kanidm.git
synced 2025-05-16 05:53:55 +02:00
Add crypt formats for password import (#3458)
Adds crypt md5, sha256 and sha512 allowing import of legacy credentials from external ldap servers.
This commit is contained in:
parent
266dc77536
commit
0e0e8ff844
12
Cargo.lock
generated
12
Cargo.lock
generated
|
@ -2978,10 +2978,12 @@ dependencies = [
|
||||||
"hex",
|
"hex",
|
||||||
"kanidm-hsm-crypto",
|
"kanidm-hsm-crypto",
|
||||||
"kanidm_proto",
|
"kanidm_proto",
|
||||||
|
"md-5",
|
||||||
"openssl",
|
"openssl",
|
||||||
"openssl-sys",
|
"openssl-sys",
|
||||||
"rand",
|
"rand",
|
||||||
"serde",
|
"serde",
|
||||||
|
"sha-crypt",
|
||||||
"sha2",
|
"sha2",
|
||||||
"sketching",
|
"sketching",
|
||||||
"tracing",
|
"tracing",
|
||||||
|
@ -3528,6 +3530,16 @@ dependencies = [
|
||||||
"rand",
|
"rand",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "md-5"
|
||||||
|
version = "0.10.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"digest",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memchr"
|
name = "memchr"
|
||||||
version = "2.7.4"
|
version = "2.7.4"
|
||||||
|
|
|
@ -204,6 +204,7 @@ libsqlite3-sys = "^0.25.2"
|
||||||
lodepng = "3.11.0"
|
lodepng = "3.11.0"
|
||||||
lru = "^0.12.5"
|
lru = "^0.12.5"
|
||||||
mathru = "^0.13.0"
|
mathru = "^0.13.0"
|
||||||
|
md-5 = "0.10.6"
|
||||||
mimalloc = "0.1.43"
|
mimalloc = "0.1.43"
|
||||||
notify-debouncer-full = { version = "0.1" }
|
notify-debouncer-full = { version = "0.1" }
|
||||||
num_enum = "^0.5.11"
|
num_enum = "^0.5.11"
|
||||||
|
|
|
@ -33,6 +33,9 @@ tracing = { workspace = true }
|
||||||
uuid = { workspace = true }
|
uuid = { workspace = true }
|
||||||
x509-cert = { workspace = true, features = ["pem"] }
|
x509-cert = { workspace = true, features = ["pem"] }
|
||||||
|
|
||||||
|
md-5 = { workspace = true }
|
||||||
|
sha-crypt = { workspace = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
sketching = { workspace = true }
|
sketching = { workspace = true }
|
||||||
|
|
||||||
|
|
99
libs/crypto/src/crypt_md5.rs
Normal file
99
libs/crypto/src/crypt_md5.rs
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
use md5::{Digest, Md5};
|
||||||
|
use std::cmp::min;
|
||||||
|
|
||||||
|
/// Maximium salt length.
|
||||||
|
const MD5_MAGIC: &str = "$1$";
|
||||||
|
const MD5_TRANSPOSE: &[u8] = b"\x0c\x06\x00\x0d\x07\x01\x0e\x08\x02\x0f\x09\x03\x05\x0a\x04\x0b";
|
||||||
|
|
||||||
|
const CRYPT_HASH64: &[u8] = b"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
||||||
|
|
||||||
|
pub fn md5_sha2_hash64_encode(bs: &[u8]) -> String {
|
||||||
|
let ngroups = (bs.len() + 2) / 3;
|
||||||
|
let mut out = String::with_capacity(ngroups * 4);
|
||||||
|
for g in 0..ngroups {
|
||||||
|
let mut g_idx = g * 3;
|
||||||
|
let mut enc = 0u32;
|
||||||
|
for _ in 0..3 {
|
||||||
|
let b = (if g_idx < bs.len() { bs[g_idx] } else { 0 }) as u32;
|
||||||
|
enc >>= 8;
|
||||||
|
enc |= b << 16;
|
||||||
|
g_idx += 1;
|
||||||
|
}
|
||||||
|
for _ in 0..4 {
|
||||||
|
out.push(char::from_u32(CRYPT_HASH64[(enc & 0x3F) as usize] as u32).unwrap_or('!'));
|
||||||
|
enc >>= 6;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
match bs.len() % 3 {
|
||||||
|
1 => {
|
||||||
|
out.pop();
|
||||||
|
out.pop();
|
||||||
|
}
|
||||||
|
2 => {
|
||||||
|
out.pop();
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
out
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn do_md5_crypt(pass: &[u8], salt: &[u8]) -> Vec<u8> {
|
||||||
|
let mut dgst_b = Md5::new();
|
||||||
|
dgst_b.update(pass);
|
||||||
|
dgst_b.update(salt);
|
||||||
|
dgst_b.update(pass);
|
||||||
|
let mut hash_b = dgst_b.finalize();
|
||||||
|
|
||||||
|
let mut dgst_a = Md5::new();
|
||||||
|
dgst_a.update(pass);
|
||||||
|
dgst_a.update(MD5_MAGIC.as_bytes());
|
||||||
|
dgst_a.update(salt);
|
||||||
|
|
||||||
|
let mut plen = pass.len();
|
||||||
|
while plen > 0 {
|
||||||
|
dgst_a.update(&hash_b[..min(plen, 16)]);
|
||||||
|
if plen < 16 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
plen -= 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
plen = pass.len();
|
||||||
|
while plen > 0 {
|
||||||
|
if plen & 1 == 0 {
|
||||||
|
dgst_a.update(&pass[..1])
|
||||||
|
} else {
|
||||||
|
dgst_a.update([0u8])
|
||||||
|
}
|
||||||
|
plen >>= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut hash_a = dgst_a.finalize();
|
||||||
|
|
||||||
|
for r in 0..1000 {
|
||||||
|
let mut dgst_a = Md5::new();
|
||||||
|
if r % 2 == 1 {
|
||||||
|
dgst_a.update(pass);
|
||||||
|
} else {
|
||||||
|
dgst_a.update(hash_a);
|
||||||
|
}
|
||||||
|
if r % 3 > 0 {
|
||||||
|
dgst_a.update(salt);
|
||||||
|
}
|
||||||
|
if r % 7 > 0 {
|
||||||
|
dgst_a.update(pass);
|
||||||
|
}
|
||||||
|
if r % 2 == 0 {
|
||||||
|
dgst_a.update(pass);
|
||||||
|
} else {
|
||||||
|
dgst_a.update(hash_a);
|
||||||
|
}
|
||||||
|
hash_a = dgst_a.finalize();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i, &ti) in MD5_TRANSPOSE.iter().enumerate() {
|
||||||
|
hash_b[i] = hash_a[ti as usize];
|
||||||
|
}
|
||||||
|
|
||||||
|
md5_sha2_hash64_encode(&hash_b).into_bytes()
|
||||||
|
}
|
|
@ -11,26 +11,24 @@
|
||||||
#![deny(clippy::unreachable)]
|
#![deny(clippy::unreachable)]
|
||||||
|
|
||||||
use argon2::{Algorithm, Argon2, Params, PasswordHash, Version};
|
use argon2::{Algorithm, Argon2, Params, PasswordHash, Version};
|
||||||
|
use base64::engine::general_purpose;
|
||||||
use base64::engine::GeneralPurpose;
|
use base64::engine::GeneralPurpose;
|
||||||
use base64::{alphabet, Engine};
|
use base64::{alphabet, Engine};
|
||||||
use tracing::{debug, error, trace, warn};
|
|
||||||
|
|
||||||
use base64::engine::general_purpose;
|
|
||||||
use base64urlsafedata::Base64UrlSafeData;
|
use base64urlsafedata::Base64UrlSafeData;
|
||||||
use rand::Rng;
|
use kanidm_hsm_crypto::{HmacKey, Tpm};
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use std::fmt;
|
|
||||||
use std::time::{Duration, Instant};
|
|
||||||
|
|
||||||
use kanidm_proto::internal::OperationError;
|
use kanidm_proto::internal::OperationError;
|
||||||
use openssl::error::ErrorStack as OpenSSLErrorStack;
|
use openssl::error::ErrorStack as OpenSSLErrorStack;
|
||||||
use openssl::hash::{self, MessageDigest};
|
use openssl::hash::{self, MessageDigest};
|
||||||
use openssl::nid::Nid;
|
use openssl::nid::Nid;
|
||||||
use openssl::pkcs5::pbkdf2_hmac;
|
use openssl::pkcs5::pbkdf2_hmac;
|
||||||
use openssl::sha::{Sha1, Sha256, Sha512};
|
use openssl::sha::{Sha1, Sha256, Sha512};
|
||||||
|
use rand::Rng;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::fmt;
|
||||||
|
use std::time::{Duration, Instant};
|
||||||
|
use tracing::{debug, error, trace, warn};
|
||||||
|
|
||||||
use kanidm_hsm_crypto::{HmacKey, Tpm};
|
mod crypt_md5;
|
||||||
|
|
||||||
pub mod mtls;
|
pub mod mtls;
|
||||||
pub mod prelude;
|
pub mod prelude;
|
||||||
pub mod serialise;
|
pub mod serialise;
|
||||||
|
@ -84,6 +82,7 @@ pub enum CryptoError {
|
||||||
Argon2,
|
Argon2,
|
||||||
Argon2Version,
|
Argon2Version,
|
||||||
Argon2Parameters,
|
Argon2Parameters,
|
||||||
|
Crypt,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<OpenSSLErrorStack> for CryptoError {
|
impl From<OpenSSLErrorStack> for CryptoError {
|
||||||
|
@ -137,65 +136,15 @@ pub enum DbPasswordV1 {
|
||||||
SHA512(Vec<u8>),
|
SHA512(Vec<u8>),
|
||||||
SSHA512(Vec<u8>, Vec<u8>),
|
SSHA512(Vec<u8>, Vec<u8>),
|
||||||
NT_MD4(Vec<u8>),
|
NT_MD4(Vec<u8>),
|
||||||
}
|
CRYPT_MD5 {
|
||||||
|
s: Base64UrlSafeData,
|
||||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
|
h: Base64UrlSafeData,
|
||||||
#[allow(non_camel_case_types)]
|
|
||||||
pub enum ReplPasswordV1 {
|
|
||||||
TPM_ARGON2ID {
|
|
||||||
m_cost: u32,
|
|
||||||
t_cost: u32,
|
|
||||||
p_cost: u32,
|
|
||||||
version: u32,
|
|
||||||
salt: Base64UrlSafeData,
|
|
||||||
key: Base64UrlSafeData,
|
|
||||||
},
|
},
|
||||||
ARGON2ID {
|
CRYPT_SHA256 {
|
||||||
m_cost: u32,
|
h: String,
|
||||||
t_cost: u32,
|
|
||||||
p_cost: u32,
|
|
||||||
version: u32,
|
|
||||||
salt: Base64UrlSafeData,
|
|
||||||
key: Base64UrlSafeData,
|
|
||||||
},
|
},
|
||||||
PBKDF2 {
|
CRYPT_SHA512 {
|
||||||
cost: usize,
|
h: String,
|
||||||
salt: Base64UrlSafeData,
|
|
||||||
hash: Base64UrlSafeData,
|
|
||||||
},
|
|
||||||
PBKDF2_SHA1 {
|
|
||||||
cost: usize,
|
|
||||||
salt: Base64UrlSafeData,
|
|
||||||
hash: Base64UrlSafeData,
|
|
||||||
},
|
|
||||||
PBKDF2_SHA512 {
|
|
||||||
cost: usize,
|
|
||||||
salt: Base64UrlSafeData,
|
|
||||||
hash: Base64UrlSafeData,
|
|
||||||
},
|
|
||||||
SHA1 {
|
|
||||||
hash: Base64UrlSafeData,
|
|
||||||
},
|
|
||||||
SSHA1 {
|
|
||||||
salt: Base64UrlSafeData,
|
|
||||||
hash: Base64UrlSafeData,
|
|
||||||
},
|
|
||||||
SHA256 {
|
|
||||||
hash: Base64UrlSafeData,
|
|
||||||
},
|
|
||||||
SSHA256 {
|
|
||||||
salt: Base64UrlSafeData,
|
|
||||||
hash: Base64UrlSafeData,
|
|
||||||
},
|
|
||||||
SHA512 {
|
|
||||||
hash: Base64UrlSafeData,
|
|
||||||
},
|
|
||||||
SSHA512 {
|
|
||||||
salt: Base64UrlSafeData,
|
|
||||||
hash: Base64UrlSafeData,
|
|
||||||
},
|
|
||||||
NT_MD4 {
|
|
||||||
hash: Base64UrlSafeData,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -214,6 +163,9 @@ impl fmt::Debug for DbPasswordV1 {
|
||||||
DbPasswordV1::SHA512(_) => write!(f, "SHA512"),
|
DbPasswordV1::SHA512(_) => write!(f, "SHA512"),
|
||||||
DbPasswordV1::SSHA512(_, _) => write!(f, "SSHA512"),
|
DbPasswordV1::SSHA512(_, _) => write!(f, "SSHA512"),
|
||||||
DbPasswordV1::NT_MD4(_) => write!(f, "NT_MD4"),
|
DbPasswordV1::NT_MD4(_) => write!(f, "NT_MD4"),
|
||||||
|
DbPasswordV1::CRYPT_MD5 { .. } => write!(f, "CRYPT_MD5"),
|
||||||
|
DbPasswordV1::CRYPT_SHA256 { .. } => write!(f, "CRYPT_SHA256"),
|
||||||
|
DbPasswordV1::CRYPT_SHA512 { .. } => write!(f, "CRYPT_SHA512"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -436,6 +388,16 @@ enum Kdf {
|
||||||
SSHA512(Vec<u8>, Vec<u8>),
|
SSHA512(Vec<u8>, Vec<u8>),
|
||||||
// hash
|
// hash
|
||||||
NT_MD4(Vec<u8>),
|
NT_MD4(Vec<u8>),
|
||||||
|
CRYPT_MD5 {
|
||||||
|
s: Vec<u8>,
|
||||||
|
h: Vec<u8>,
|
||||||
|
},
|
||||||
|
CRYPT_SHA256 {
|
||||||
|
h: String,
|
||||||
|
},
|
||||||
|
CRYPT_SHA512 {
|
||||||
|
h: String,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
@ -498,78 +460,17 @@ impl TryFrom<DbPasswordV1> for Password {
|
||||||
DbPasswordV1::NT_MD4(h) => Ok(Password {
|
DbPasswordV1::NT_MD4(h) => Ok(Password {
|
||||||
material: Kdf::NT_MD4(h),
|
material: Kdf::NT_MD4(h),
|
||||||
}),
|
}),
|
||||||
}
|
DbPasswordV1::CRYPT_MD5 { s, h } => Ok(Password {
|
||||||
}
|
material: Kdf::CRYPT_MD5 {
|
||||||
}
|
s: s.into(),
|
||||||
|
h: h.into(),
|
||||||
impl TryFrom<&ReplPasswordV1> for Password {
|
|
||||||
type Error = ();
|
|
||||||
|
|
||||||
fn try_from(value: &ReplPasswordV1) -> Result<Self, Self::Error> {
|
|
||||||
match value {
|
|
||||||
ReplPasswordV1::TPM_ARGON2ID {
|
|
||||||
m_cost,
|
|
||||||
t_cost,
|
|
||||||
p_cost,
|
|
||||||
version,
|
|
||||||
salt,
|
|
||||||
key,
|
|
||||||
} => Ok(Password {
|
|
||||||
material: Kdf::TPM_ARGON2ID {
|
|
||||||
m_cost: *m_cost,
|
|
||||||
t_cost: *t_cost,
|
|
||||||
p_cost: *p_cost,
|
|
||||||
version: *version,
|
|
||||||
salt: salt.to_vec(),
|
|
||||||
key: key.to_vec(),
|
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
ReplPasswordV1::ARGON2ID {
|
DbPasswordV1::CRYPT_SHA256 { h } => Ok(Password {
|
||||||
m_cost,
|
material: Kdf::CRYPT_SHA256 { h },
|
||||||
t_cost,
|
|
||||||
p_cost,
|
|
||||||
version,
|
|
||||||
salt,
|
|
||||||
key,
|
|
||||||
} => Ok(Password {
|
|
||||||
material: Kdf::ARGON2ID {
|
|
||||||
m_cost: *m_cost,
|
|
||||||
t_cost: *t_cost,
|
|
||||||
p_cost: *p_cost,
|
|
||||||
version: *version,
|
|
||||||
salt: salt.to_vec(),
|
|
||||||
key: key.to_vec(),
|
|
||||||
},
|
|
||||||
}),
|
}),
|
||||||
ReplPasswordV1::PBKDF2 { cost, salt, hash } => Ok(Password {
|
DbPasswordV1::CRYPT_SHA512 { h } => Ok(Password {
|
||||||
material: Kdf::PBKDF2(*cost, salt.to_vec(), hash.to_vec()),
|
material: Kdf::CRYPT_SHA256 { h },
|
||||||
}),
|
|
||||||
ReplPasswordV1::PBKDF2_SHA1 { cost, salt, hash } => Ok(Password {
|
|
||||||
material: Kdf::PBKDF2_SHA1(*cost, salt.to_vec(), hash.to_vec()),
|
|
||||||
}),
|
|
||||||
ReplPasswordV1::PBKDF2_SHA512 { cost, salt, hash } => Ok(Password {
|
|
||||||
material: Kdf::PBKDF2_SHA512(*cost, salt.to_vec(), hash.to_vec()),
|
|
||||||
}),
|
|
||||||
ReplPasswordV1::SHA1 { hash } => Ok(Password {
|
|
||||||
material: Kdf::SHA1(hash.to_vec()),
|
|
||||||
}),
|
|
||||||
ReplPasswordV1::SSHA1 { salt, hash } => Ok(Password {
|
|
||||||
material: Kdf::SSHA1(salt.to_vec(), hash.to_vec()),
|
|
||||||
}),
|
|
||||||
ReplPasswordV1::SHA256 { hash } => Ok(Password {
|
|
||||||
material: Kdf::SHA256(hash.to_vec()),
|
|
||||||
}),
|
|
||||||
ReplPasswordV1::SSHA256 { salt, hash } => Ok(Password {
|
|
||||||
material: Kdf::SSHA256(salt.to_vec(), hash.to_vec()),
|
|
||||||
}),
|
|
||||||
ReplPasswordV1::SHA512 { hash } => Ok(Password {
|
|
||||||
material: Kdf::SHA512(hash.to_vec()),
|
|
||||||
}),
|
|
||||||
ReplPasswordV1::SSHA512 { salt, hash } => Ok(Password {
|
|
||||||
material: Kdf::SSHA512(salt.to_vec(), hash.to_vec()),
|
|
||||||
}),
|
|
||||||
ReplPasswordV1::NT_MD4 { hash } => Ok(Password {
|
|
||||||
material: Kdf::NT_MD4(hash.to_vec()),
|
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -665,6 +566,40 @@ impl TryFrom<&str> for Password {
|
||||||
// Test 389ds/openldap formats. Shout outs openldap which sometimes makes these
|
// Test 389ds/openldap formats. Shout outs openldap which sometimes makes these
|
||||||
// lowercase.
|
// lowercase.
|
||||||
|
|
||||||
|
if let Some(crypt) = value
|
||||||
|
.strip_prefix("{crypt}")
|
||||||
|
.or_else(|| value.strip_prefix("{CRYPT}"))
|
||||||
|
{
|
||||||
|
if let Some(crypt_md5_phc) = crypt.strip_prefix("$1$") {
|
||||||
|
let (salt, hash) = crypt_md5_phc.split_once('$').ok_or(())?;
|
||||||
|
|
||||||
|
// These are a hash64 format, so leave them as bytes, don't try
|
||||||
|
// to decode.
|
||||||
|
let s = salt.as_bytes().to_vec();
|
||||||
|
let h = hash.as_bytes().to_vec();
|
||||||
|
|
||||||
|
return Ok(Password {
|
||||||
|
material: Kdf::CRYPT_MD5 { s, h },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if crypt.starts_with("$5$") {
|
||||||
|
return Ok(Password {
|
||||||
|
material: Kdf::CRYPT_SHA256 {
|
||||||
|
h: crypt.to_string(),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if crypt.starts_with("$6$") {
|
||||||
|
return Ok(Password {
|
||||||
|
material: Kdf::CRYPT_SHA512 {
|
||||||
|
h: crypt.to_string(),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} // End crypt
|
||||||
|
|
||||||
if let Some(ds_ssha1) = value
|
if let Some(ds_ssha1) = value
|
||||||
.strip_prefix("{SHA}")
|
.strip_prefix("{SHA}")
|
||||||
.or_else(|| value.strip_prefix("{sha}"))
|
.or_else(|| value.strip_prefix("{sha}"))
|
||||||
|
@ -1242,6 +1177,20 @@ impl Password {
|
||||||
})
|
})
|
||||||
.map(|chal_key| chal_key.as_ref() == key)
|
.map(|chal_key| chal_key.as_ref() == key)
|
||||||
}
|
}
|
||||||
|
(Kdf::CRYPT_MD5 { s, h }, _) => {
|
||||||
|
let chal_key = crypt_md5::do_md5_crypt(cleartext.as_bytes(), s);
|
||||||
|
Ok(chal_key == *h)
|
||||||
|
}
|
||||||
|
(Kdf::CRYPT_SHA256 { h }, _) => {
|
||||||
|
let is_valid = sha_crypt::sha256_check(cleartext, h.as_str()).is_ok();
|
||||||
|
|
||||||
|
Ok(is_valid)
|
||||||
|
}
|
||||||
|
(Kdf::CRYPT_SHA512 { h }, _) => {
|
||||||
|
let is_valid = sha_crypt::sha512_check(cleartext, h.as_str()).is_ok();
|
||||||
|
|
||||||
|
Ok(is_valid)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1293,80 +1242,12 @@ impl Password {
|
||||||
Kdf::SHA512(hash) => DbPasswordV1::SHA512(hash.clone()),
|
Kdf::SHA512(hash) => DbPasswordV1::SHA512(hash.clone()),
|
||||||
Kdf::SSHA512(salt, hash) => DbPasswordV1::SSHA512(salt.clone(), hash.clone()),
|
Kdf::SSHA512(salt, hash) => DbPasswordV1::SSHA512(salt.clone(), hash.clone()),
|
||||||
Kdf::NT_MD4(hash) => DbPasswordV1::NT_MD4(hash.clone()),
|
Kdf::NT_MD4(hash) => DbPasswordV1::NT_MD4(hash.clone()),
|
||||||
}
|
Kdf::CRYPT_MD5 { s, h } => DbPasswordV1::CRYPT_MD5 {
|
||||||
}
|
s: s.clone().into(),
|
||||||
|
h: h.clone().into(),
|
||||||
pub fn to_repl_v1(&self) -> ReplPasswordV1 {
|
|
||||||
match &self.material {
|
|
||||||
Kdf::TPM_ARGON2ID {
|
|
||||||
m_cost,
|
|
||||||
t_cost,
|
|
||||||
p_cost,
|
|
||||||
version,
|
|
||||||
salt,
|
|
||||||
key,
|
|
||||||
} => ReplPasswordV1::TPM_ARGON2ID {
|
|
||||||
m_cost: *m_cost,
|
|
||||||
t_cost: *t_cost,
|
|
||||||
p_cost: *p_cost,
|
|
||||||
version: *version,
|
|
||||||
salt: salt.clone().into(),
|
|
||||||
key: key.clone().into(),
|
|
||||||
},
|
|
||||||
Kdf::ARGON2ID {
|
|
||||||
m_cost,
|
|
||||||
t_cost,
|
|
||||||
p_cost,
|
|
||||||
version,
|
|
||||||
salt,
|
|
||||||
key,
|
|
||||||
} => ReplPasswordV1::ARGON2ID {
|
|
||||||
m_cost: *m_cost,
|
|
||||||
t_cost: *t_cost,
|
|
||||||
p_cost: *p_cost,
|
|
||||||
version: *version,
|
|
||||||
salt: salt.clone().into(),
|
|
||||||
key: key.clone().into(),
|
|
||||||
},
|
|
||||||
Kdf::PBKDF2(cost, salt, hash) => ReplPasswordV1::PBKDF2 {
|
|
||||||
cost: *cost,
|
|
||||||
salt: salt.clone().into(),
|
|
||||||
hash: hash.clone().into(),
|
|
||||||
},
|
|
||||||
Kdf::PBKDF2_SHA1(cost, salt, hash) => ReplPasswordV1::PBKDF2_SHA1 {
|
|
||||||
cost: *cost,
|
|
||||||
salt: salt.clone().into(),
|
|
||||||
hash: hash.clone().into(),
|
|
||||||
},
|
|
||||||
Kdf::PBKDF2_SHA512(cost, salt, hash) => ReplPasswordV1::PBKDF2_SHA512 {
|
|
||||||
cost: *cost,
|
|
||||||
salt: salt.clone().into(),
|
|
||||||
hash: hash.clone().into(),
|
|
||||||
},
|
|
||||||
Kdf::SHA1(hash) => ReplPasswordV1::SHA1 {
|
|
||||||
hash: hash.clone().into(),
|
|
||||||
},
|
|
||||||
Kdf::SSHA1(salt, hash) => ReplPasswordV1::SSHA1 {
|
|
||||||
salt: salt.clone().into(),
|
|
||||||
hash: hash.clone().into(),
|
|
||||||
},
|
|
||||||
Kdf::SHA256(hash) => ReplPasswordV1::SHA256 {
|
|
||||||
hash: hash.clone().into(),
|
|
||||||
},
|
|
||||||
Kdf::SSHA256(salt, hash) => ReplPasswordV1::SSHA256 {
|
|
||||||
salt: salt.clone().into(),
|
|
||||||
hash: hash.clone().into(),
|
|
||||||
},
|
|
||||||
Kdf::SHA512(hash) => ReplPasswordV1::SHA512 {
|
|
||||||
hash: hash.clone().into(),
|
|
||||||
},
|
|
||||||
Kdf::SSHA512(salt, hash) => ReplPasswordV1::SSHA512 {
|
|
||||||
salt: salt.clone().into(),
|
|
||||||
hash: hash.clone().into(),
|
|
||||||
},
|
|
||||||
Kdf::NT_MD4(hash) => ReplPasswordV1::NT_MD4 {
|
|
||||||
hash: hash.clone().into(),
|
|
||||||
},
|
},
|
||||||
|
Kdf::CRYPT_SHA256 { h } => DbPasswordV1::CRYPT_SHA256 { h: h.clone() },
|
||||||
|
Kdf::CRYPT_SHA512 { h } => DbPasswordV1::CRYPT_SHA512 { h: h.clone() },
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1402,7 +1283,10 @@ impl Password {
|
||||||
| Kdf::SSHA256(_, _)
|
| Kdf::SSHA256(_, _)
|
||||||
| Kdf::SHA512(_)
|
| Kdf::SHA512(_)
|
||||||
| Kdf::SSHA512(_, _)
|
| Kdf::SSHA512(_, _)
|
||||||
| Kdf::NT_MD4(_) => true,
|
| Kdf::NT_MD4(_)
|
||||||
|
| Kdf::CRYPT_MD5 { .. }
|
||||||
|
| Kdf::CRYPT_SHA256 { .. }
|
||||||
|
| Kdf::CRYPT_SHA512 { .. } => true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1660,6 +1544,39 @@ mod tests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_password_from_crypt_md5() {
|
||||||
|
sketching::test_init();
|
||||||
|
let im_pw = "{crypt}$1$zaRIAsoe$7887GzjDTrst0XbDPpF5m.";
|
||||||
|
let password = "password";
|
||||||
|
let r = Password::try_from(im_pw).expect("Failed to parse");
|
||||||
|
|
||||||
|
assert!(r.requires_upgrade());
|
||||||
|
assert!(r.verify(password).unwrap_or(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_password_from_crypt_sha256() {
|
||||||
|
sketching::test_init();
|
||||||
|
let im_pw = "{crypt}$5$3UzV7Sut8EHCUxlN$41V.jtMQmFAOucqI4ImFV43r.bRLjPlN.hyfoCdmGE2";
|
||||||
|
let password = "password";
|
||||||
|
let r = Password::try_from(im_pw).expect("Failed to parse");
|
||||||
|
|
||||||
|
assert!(r.requires_upgrade());
|
||||||
|
assert!(r.verify(password).unwrap_or(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_password_from_crypt_sha512() {
|
||||||
|
sketching::test_init();
|
||||||
|
let im_pw = "{crypt}$6$aXn8azL8DXUyuMvj$9aJJC/KEUwygIpf2MTqjQa.f0MEXNg2cGFc62Fet8XpuDVDedM05CweAlxW6GWxnmHqp14CRf6zU7OQoE/bCu0";
|
||||||
|
let password = "password";
|
||||||
|
let r = Password::try_from(im_pw).expect("Failed to parse");
|
||||||
|
|
||||||
|
assert!(r.requires_upgrade());
|
||||||
|
assert!(r.verify(password).unwrap_or(false));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_password_argon2id_hsm_bind() {
|
fn test_password_argon2id_hsm_bind() {
|
||||||
sketching::test_init();
|
sketching::test_init();
|
||||||
|
|
Loading…
Reference in a new issue