1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
//! Api functions related to the argon2id password hashing algorithm.

use crate::*;

/// minimum hash length for argon2id pwhash
pub const BYTES_MIN: usize =
    libsodium_sys::crypto_pwhash_argon2id_BYTES_MIN as usize;

/// salt buffer length for argon2id pwhash
pub const SALTBYTES: usize =
    libsodium_sys::crypto_pwhash_argon2id_SALTBYTES as usize;

/// minimum password length for argon2id pwhash
pub const PASSWD_MIN: usize =
    libsodium_sys::crypto_pwhash_argon2id_PASSWD_MIN as usize;

/// maximum password length for argon2id pwhash
pub const PASSWD_MAX: usize =
    libsodium_sys::crypto_pwhash_argon2id_PASSWD_MAX as usize;

/// argon2id pwhash min ops limit
pub const OPSLIMIT_MIN: u32 =
    libsodium_sys::crypto_pwhash_argon2id_OPSLIMIT_MIN as u32;

/// argon2id pwhash interactive ops limit
pub const OPSLIMIT_INTERACTIVE: u32 =
    libsodium_sys::crypto_pwhash_argon2id_OPSLIMIT_INTERACTIVE as u32;

/// argon2id pwhash moderate ops limit
pub const OPSLIMIT_MODERATE: u32 =
    libsodium_sys::crypto_pwhash_argon2id_OPSLIMIT_MODERATE as u32;

/// argon2id pwhash sensitive ops limit
pub const OPSLIMIT_SENSITIVE: u32 =
    libsodium_sys::crypto_pwhash_argon2id_OPSLIMIT_SENSITIVE as u32;

/// argon2id pwhash max ops limit
pub const OPSLIMIT_MAX: u32 =
    libsodium_sys::crypto_pwhash_argon2id_OPSLIMIT_MAX as u32;

/// argon2id pwhash min mem limit
pub const MEMLIMIT_MIN: u32 =
    libsodium_sys::crypto_pwhash_argon2id_MEMLIMIT_MIN as u32;

/// argon2id pwhash interactive mem limit
pub const MEMLIMIT_INTERACTIVE: u32 =
    libsodium_sys::crypto_pwhash_argon2id_MEMLIMIT_INTERACTIVE as u32;

/// argon2id pwhash moderate mem limit
pub const MEMLIMIT_MODERATE: u32 =
    libsodium_sys::crypto_pwhash_argon2id_MEMLIMIT_MODERATE as u32;

/// argon2id pwhash sensitive mem limit
pub const MEMLIMIT_SENSITIVE: u32 =
    libsodium_sys::crypto_pwhash_argon2id_MEMLIMIT_SENSITIVE as u32;

/// argon2id13 password hashing scheme
pub async fn hash<H, P, S>(
    hash: H,
    passphrase: P,
    salt: S,
    ops_limit: u32,
    mem_limit: u32,
) -> SodokenResult<()>
where
    H: Into<BufWrite> + 'static + Send,
    P: Into<BufRead> + 'static + Send,
    S: Into<BufReadSized<SALTBYTES>> + 'static + Send,
{
    let hash = hash.into();
    let passphrase = passphrase.into();
    let salt = salt.into();
    tokio_exec_blocking(move || {
        // argon is designed to take a long time...
        // we don't want to hold these locks open for a long time,
        // so let's do some careful shuffling to only hold locks
        // on local single buf instances.

        let tmp_hash = BufWrite::new_mem_locked(hash.len())?;
        let tmp_passphrase = BufWrite::deep_clone_mem_locked(passphrase)?;
        let tmp_salt = BufWriteSized::deep_clone_no_lock(salt);

        let mut tmp_hash_lock = tmp_hash.write_lock();
        let tmp_passphrase_lock = tmp_passphrase.read_lock();
        let tmp_salt_lock = tmp_salt.read_lock_sized();

        safe::sodium::crypto_pwhash_argon2id(
            &mut tmp_hash_lock,
            &tmp_passphrase_lock,
            &tmp_salt_lock,
            ops_limit,
            mem_limit,
        )?;

        hash.write_lock().copy_from_slice(&tmp_hash_lock);

        Ok(())
    })
    .await
}

#[cfg(test)]
mod tests {
    use crate::*;

    #[tokio::test(flavor = "multi_thread")]
    async fn argon2id() -> SodokenResult<()> {
        let pass = BufRead::new_no_lock(b"my passphrase");
        let salt: BufReadSized<{ hash::argon2id::SALTBYTES }> =
            BufReadSized::new_no_lock([0xdb; hash::argon2id::SALTBYTES]);
        let hash = BufWrite::new_no_lock(hash::argon2id::BYTES_MIN);
        hash::argon2id::hash(
            hash.clone(),
            pass,
            salt,
            hash::argon2id::OPSLIMIT_INTERACTIVE,
            hash::argon2id::MEMLIMIT_INTERACTIVE,
        )
        .await?;
        assert_eq!(
            "[236, 88, 226, 164, 99, 188, 201, 149, 83, 218, 26, 28, 193, 215, 226, 72]",
            format!("{:?}", &*hash.read_lock()),
        );
        Ok(())
    }
}