feat(notsecrets): scrypt identity and recipient (passphrase-based)
This commit is contained in:
@@ -4,6 +4,9 @@ use crate::identities::{FileKey, Stanza};
|
||||
pub mod x25519;
|
||||
pub use x25519::X25519Recipient;
|
||||
|
||||
pub mod scrypt;
|
||||
pub use scrypt::ScryptRecipient;
|
||||
|
||||
/// Domain port: a recipient that can wrap a file key into a stanza.
|
||||
pub trait Recipient {
|
||||
fn wrap_file_key(&self, file_key: &FileKey) -> Result<Stanza, AgeError>;
|
||||
|
||||
45
crates/notsecrets/src/recipients/scrypt.rs
Normal file
45
crates/notsecrets/src/recipients/scrypt.rs
Normal file
@@ -0,0 +1,45 @@
|
||||
use crate::error::AgeError;
|
||||
use crate::identities::{FileKey, Stanza};
|
||||
use crate::identities::scrypt::derive_scrypt_key;
|
||||
use crate::recipients::Recipient;
|
||||
use base64::{Engine, engine::general_purpose::STANDARD_NO_PAD};
|
||||
use chacha20poly1305::{ChaCha20Poly1305, Key, KeyInit, Nonce, aead::Aead};
|
||||
use rand::RngCore;
|
||||
|
||||
const TAG: &str = "scrypt";
|
||||
const SALT_LEN: usize = 16;
|
||||
|
||||
pub struct ScryptRecipient {
|
||||
passphrase: String,
|
||||
work_factor: u8,
|
||||
}
|
||||
|
||||
impl ScryptRecipient {
|
||||
/// `work_factor` is log2(N). Use 18 in production, 14 in tests.
|
||||
pub fn new(passphrase: String, work_factor: u8) -> Self {
|
||||
Self { passphrase, work_factor }
|
||||
}
|
||||
}
|
||||
|
||||
impl Recipient for ScryptRecipient {
|
||||
fn wrap_file_key(&self, file_key: &FileKey) -> Result<Stanza, AgeError> {
|
||||
let mut salt = [0u8; SALT_LEN];
|
||||
rand::thread_rng().fill_bytes(&mut salt);
|
||||
|
||||
let wrap_key = derive_scrypt_key(self.passphrase.as_bytes(), &salt, self.work_factor)?;
|
||||
let cipher = ChaCha20Poly1305::new(Key::from_slice(&wrap_key));
|
||||
let nonce = Nonce::default();
|
||||
let wrapped = cipher
|
||||
.encrypt(&nonce, file_key.as_bytes().as_slice())
|
||||
.map_err(|_| AgeError::CryptoError("scrypt file key encryption failed".to_string()))?;
|
||||
|
||||
Ok(Stanza {
|
||||
tag: TAG.to_string(),
|
||||
args: vec![
|
||||
STANDARD_NO_PAD.encode(salt),
|
||||
self.work_factor.to_string(),
|
||||
],
|
||||
body: wrapped,
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user