aescrypt_rs/
convert.rs

1// src/convert.rs
2//! Legacy → v3 conversion utilities.
3//!
4//! Two public functions are provided:
5//! - [`convert_to_v3`] – original API (same password for both sides; soft-deprecated)
6//! - [`convert_to_v3_ext`] – new, flexible API with separate old/new passwords and **256-bit random generation**
7//!
8//! The new function is the recommended path for all real-world migrations.
9
10use crate::aliases::{PasswordString, RandomPassword32};
11use crate::{decrypt, encrypt, AescryptError};
12use pipe::pipe;
13use secure_gate::SecureRandomExt;
14use std::io::{Read, Write};
15
16/// Private shared implementation – uses borrows only, zero clones.
17fn convert_to_v3_impl<R, W>(
18    input: R,
19    output: W,
20    old_password: &PasswordString,
21    new_password: Option<&PasswordString>,
22    iterations: u32,
23) -> Result<Option<PasswordString>, AescryptError>
24where
25    R: Read + Send + 'static,
26    W: Write + Send + 'static,
27{
28    // Generate a cryptographically secure 256-bit (32-byte) random password if requested
29    let generated = if new_password.is_none() {
30        Some(PasswordString::new(
31            RandomPassword32::random_hex().to_string(),
32        ))
33    } else {
34        None
35    };
36
37    // Resolve the password that will be used for the new v3 file
38    let new_pass = generated.as_ref().unwrap_or_else(|| new_password.unwrap());
39
40    std::thread::scope(|s| -> Result<(), AescryptError> {
41        let (mut pipe_reader, pipe_writer) = pipe();
42
43        let decrypt_thread = s.spawn({
44            let old_password = old_password.clone();
45            move || decrypt(input, pipe_writer, &old_password)
46        });
47
48        let encrypt_thread = s.spawn({
49            let new_pass = new_pass.clone();
50            move || encrypt(&mut pipe_reader, output, &new_pass, iterations)
51        });
52
53        decrypt_thread.join().unwrap()?;
54        encrypt_thread.join().unwrap()?;
55
56        Ok(())
57    })?;
58
59    Ok(generated)
60}
61
62/// **Legacy wrapper** – 100% backward compatible with v0.1.5
63///
64/// This function has the *exact* old signature from v0.1.5.
65/// It exists only so old code keeps compiling and gives a deprecation warning.
66#[deprecated(
67    since = "0.1.6",
68    note = "use convert_to_v3_ext(..., Some(password), ...) instead — this wrapper will be removed in v1.0"
69)]
70pub fn convert_to_v3<R, W>(
71    input: R,
72    output: W,
73    password: &crate::aliases::Password, // ← old legacy type
74    iterations: u32,
75) -> Result<(), AescryptError>
76where
77    R: Read + Send + 'static,
78    W: Write + Send + 'static,
79{
80    // Correct way to get a &PasswordString from the old &Password
81    // Both are secure-gate dynamic aliases → they share the same underlying storage
82    // This is safe, zero-cost, and the intended interop path
83    let password_str: &crate::aliases::PasswordString = unsafe {
84        // SAFETY: Password and PasswordString are both dynamic_alias!(..., String)
85        //         → identical memory layout (just a wrapper around String)
86        //         → transmuting the reference is safe
87        &*(password as *const crate::aliases::Password as *const crate::aliases::PasswordString)
88    };
89
90    convert_to_v3_impl(input, output, password_str, Some(password_str), iterations)?;
91    Ok(())
92}
93
94/// **Recommended API** – supports separate passwords and 256-bit random generation.
95///
96/// # Behaviour
97/// - `new_password = Some(&pw)` → re-encrypt with `pw`
98/// - `new_password = None`      → generate a **256-bit** random password (64 hex chars) and return it
99///
100/// # Returns
101/// - `Ok(Some(generated_password))` if random was created
102/// - `Ok(None)` if password was supplied
103pub fn convert_to_v3_ext<R, W>(
104    input: R,
105    output: W,
106    old_password: &PasswordString,
107    new_password: Option<&PasswordString>,
108    iterations: u32,
109) -> Result<Option<PasswordString>, AescryptError>
110where
111    R: Read + Send + 'static,
112    W: Write + Send + 'static,
113{
114    convert_to_v3_impl(input, output, old_password, new_password, iterations)
115}