fix(core): NFC normalize backup passphrase (audit B2)
Backup KDF was passing raw passphrase bytes to Argon2id without NFC normalization, causing cross-platform restore failures for non-ASCII passphrases (macOS NFD vs Linux NFC). Now matches derive_master_key behavior from crypto.rs. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -301,12 +301,20 @@ pub fn unpack_backup(data: &[u8], passphrase: &str) -> Result<BackupOutput> {
|
||||
}
|
||||
|
||||
fn derive_backup_key(passphrase: &[u8], salt: &[u8]) -> Result<Zeroizing<[u8; 32]>> {
|
||||
use unicode_normalization::UnicodeNormalization;
|
||||
|
||||
// NFC normalize passphrase (matches derive_master_key in crypto.rs)
|
||||
let nfc_passphrase: Vec<u8> = match std::str::from_utf8(passphrase) {
|
||||
Ok(s) => s.nfc().collect::<String>().into_bytes(),
|
||||
Err(_) => passphrase.to_vec(),
|
||||
};
|
||||
|
||||
let params = Params::new(ARGON2_M_KIB, ARGON2_T, ARGON2_P, Some(32))
|
||||
.map_err(|e| RelicarioError::Kdf(format!("argon2 params: {e}")))?;
|
||||
let argon = Argon2::new(Algorithm::Argon2id, Version::V0x13, params);
|
||||
let mut key = Zeroizing::new([0u8; 32]);
|
||||
argon
|
||||
.hash_password_into(passphrase, salt, key.as_mut_slice())
|
||||
.hash_password_into(&nfc_passphrase, salt, key.as_mut_slice())
|
||||
.map_err(|e| RelicarioError::Kdf(format!("argon2 hash: {e}")))?;
|
||||
Ok(key)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user