Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add const initializer to argon2::Block #427

Merged
merged 1 commit into from
Jun 22, 2023

Conversation

rlee287
Copy link
Contributor

@rlee287 rlee287 commented Jun 22, 2023

For #![no_std] environments, adding the const initializer makes it possible to write

static mut ARGON2_MEMBLOCK: [argon2::Block; MEM_BLOCK_COUNT] = [argon2::Block::new(); MEM_BLOCK_COUNT];

Previously, without a const initializer for argon2::Block, doing the same would require code like the following:

static mut MEM_BLOCKS: [MaybeUninit<argon2::Block>; MEM_BLOCK_COUNT] = unsafe {MaybeUninit::uninit().assume_init()};

// Initialize blocks when they are actually used

Unfortunately, the compiler is not able to optimize the initialization properly, resulting in an extra 1KB of space wasted on the stack, which becomes an issue in memory-constrained embedded systems. The following program is an illustration of this:

#![no_std]

use core::mem::MaybeUninit;
use core::panic::PanicInfo;

// Arbitrary values
const SALTED_PWD_LEN: usize = 32;
const MEM_BLOCK_COUNT: usize = 81;

static mut MEM_BLOCKS: [MaybeUninit<argon2::Block>; MEM_BLOCK_COUNT] = unsafe {MaybeUninit::uninit().assume_init()};

#[panic_handler]
fn panic_handler(_panic: &PanicInfo<'_>) -> ! {
    loop {}
}

pub fn argon2_wrapper(pwd: &[u8], salt: &[u8]) {
    let argon2 = argon2::Argon2::new(
        argon2::Algorithm::Argon2id,
        argon2::Version::V0x13,
        argon2::Params::new(MEM_BLOCK_COUNT.try_into().unwrap(), 47, 1, Some(SALTED_PWD_LEN)).unwrap()
    );
    let mut out = [0x00; SALTED_PWD_LEN];
    let mem_blocks_ref = unsafe {
        for maybeuninit_block in &mut MEM_BLOCKS {
            maybeuninit_block.write(argon2::Block::default());
        }
        // We are transmuting a live reference (-> NonNull), so as_mut() is Some()
        (&mut MEM_BLOCKS as *mut [MaybeUninit<argon2::Block>; MEM_BLOCK_COUNT]
            as *mut [argon2::Block; MEM_BLOCK_COUNT]).as_mut().unwrap()
    };
    argon2.hash_password_into_with_memory(pwd, salt, &mut out, mem_blocks_ref).unwrap();
}

When compiled with cargo build --release --target thumbv7em-none-eabi, the resulting assembly initializes each argon2::Block on the stack and them memcpys it into the global array:

image

Thus, adding a const initializer for argon2::Block makes using the crate in embedded environments easier, as well as saving stack space. The particular project that I extracted this code from was very tight on memory space, almost needing to account for every single available RAM byte that the particular microcontroller we were using had available.

The argon2 crate was the only instance I found of a custom argon2::Block type, but if there are others, I think they should all get a const initializer as well.

@tarcieri tarcieri merged commit 765ed9b into RustCrypto:master Jun 22, 2023
13 checks passed
@tarcieri tarcieri mentioned this pull request Jul 13, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants