Files
envr/zig-vendor/age-ffi/src/passphrase.rs
2026-04-28 17:49:04 -04:00

140 lines
4.1 KiB
Rust

//! Passphrase-based encryption and decryption (scrypt).
use crate::helpers::cstr_to_string;
use crate::types::{AgeBuffer, AgeResult};
use age::secrecy::SecretString;
use std::io::{Read, Write};
use std::os::raw::c_char;
/// Encrypt data using a passphrase.
///
/// # Arguments
/// * `plaintext` - Pointer to the plaintext data
/// * `plaintext_len` - Length of the plaintext
/// * `passphrase` - The passphrase string
/// * `armor` - If true, output will be ASCII-armored
/// * `output` - Pointer to receive the encrypted buffer
///
/// # Returns
/// AgeResult indicating success or failure
#[no_mangle]
pub extern "C" fn age_encrypt_passphrase(
plaintext: *const u8,
plaintext_len: usize,
passphrase: *const c_char,
armor: bool,
output: *mut AgeBuffer,
) -> AgeResult {
if plaintext.is_null() || output.is_null() {
return AgeResult::InvalidInput;
}
let plaintext = unsafe { std::slice::from_raw_parts(plaintext, plaintext_len) };
let passphrase_str = match unsafe { cstr_to_string(passphrase) } {
Ok(s) => s,
Err(e) => return e,
};
let secret = SecretString::from(passphrase_str);
let encryptor = age::Encryptor::with_user_passphrase(secret);
let mut encrypted = Vec::new();
let result = if armor {
let armor_writer = age::armor::ArmoredWriter::wrap_output(&mut encrypted, age::armor::Format::AsciiArmor)
.map_err(|_| AgeResult::ArmorError);
match armor_writer {
Ok(armor) => {
match encryptor.wrap_output(armor) {
Ok(mut writer) => {
if writer.write_all(plaintext).is_err() {
return AgeResult::EncryptionFailed;
}
match writer.finish() {
Ok(armor) => armor.finish().map_err(|_| AgeResult::ArmorError),
Err(_) => return AgeResult::EncryptionFailed,
}
}
Err(_) => return AgeResult::EncryptionFailed,
}
}
Err(e) => return e,
}
} else {
match encryptor.wrap_output(&mut encrypted) {
Ok(mut writer) => {
if writer.write_all(plaintext).is_err() {
return AgeResult::EncryptionFailed;
}
writer.finish().map_err(|_| AgeResult::EncryptionFailed)
}
Err(_) => return AgeResult::EncryptionFailed,
}
};
if result.is_err() {
return AgeResult::EncryptionFailed;
}
unsafe {
*output = AgeBuffer::from_vec(encrypted);
}
AgeResult::Success
}
/// Decrypt data using a passphrase.
///
/// # Arguments
/// * `ciphertext` - Pointer to the encrypted data
/// * `ciphertext_len` - Length of the ciphertext
/// * `passphrase` - The passphrase string
/// * `output` - Pointer to receive the decrypted buffer
///
/// # Returns
/// AgeResult indicating success or failure
#[no_mangle]
pub extern "C" fn age_decrypt_passphrase(
ciphertext: *const u8,
ciphertext_len: usize,
passphrase: *const c_char,
output: *mut AgeBuffer,
) -> AgeResult {
if ciphertext.is_null() || output.is_null() {
return AgeResult::InvalidInput;
}
let ciphertext = unsafe { std::slice::from_raw_parts(ciphertext, ciphertext_len) };
let passphrase_str = match unsafe { cstr_to_string(passphrase) } {
Ok(s) => s,
Err(e) => return e,
};
let secret = SecretString::from(passphrase_str);
let identity = age::scrypt::Identity::new(secret);
let decryptor = match age::Decryptor::new(ciphertext) {
Ok(d) => d,
Err(_) => return AgeResult::DecryptionFailed,
};
let mut decrypted = Vec::new();
let mut reader = match decryptor.decrypt(std::iter::once(&identity as &dyn age::Identity)) {
Ok(r) => r,
Err(_) => return AgeResult::DecryptionFailed,
};
if reader.read_to_end(&mut decrypted).is_err() {
return AgeResult::DecryptionFailed;
}
unsafe {
*output = AgeBuffer::from_vec(decrypted);
}
AgeResult::Success
}