Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)

## [Unreleased] -

### Breaking Changes

- The default PBKDF2 iterations has been increased from 10,000 to 600,000. Review
uses of the following methods. Specify the numbers iterations when calling the method
if your code depends on the iterations to stay the same : `derive_key_pbkdf2`, `deriveKeyPbkdf2`, `DeriveKeyPbkdf2`,
`EncryptWithPassword`, `EncryptWithPasswordAsBase64String`, `DecryptWithPassword`, `DecryptWithPasswordAsUtf8String`, `DeriveKey`

### Changed

- Multiple functions, such as `generate_key` and `hash_password`, now return a `Result` due to the `rand` library upgrade.
Expand Down
2 changes: 1 addition & 1 deletion README_RUST.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ use devolutions_crypto::password_hash::{hash_password, PasswordHashVersion};

let password = b"somesuperstrongpa$$w0rd!";

let hashed_password = hash_password(password, 10000, PasswordHashVersion::Latest);
let hashed_password = hash_password(password, 600000, PasswordHashVersion::Latest);

assert!(hashed_password.verify_password(b"somesuperstrongpa$$w0rd!"));
assert!(!hashed_password.verify_password(b"someweakpa$$w0rd!"));
Expand Down
5 changes: 3 additions & 2 deletions cli/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use clap::{Parser, Subcommand};
use devolutions_crypto::DEFAULT_PBKDF2_ITERATIONS;
use std::{borrow::Borrow, convert::TryFrom};

/// Gives a CLI interface to Devolutions Crypto Library
Expand Down Expand Up @@ -254,7 +255,7 @@ fn derive_key(data: String, salt: Option<String>, iterations: Option<u32>, lengt
None => vec![0u8; 0],
};

let iterations = iterations.unwrap_or(10000);
let iterations = iterations.unwrap_or(DEFAULT_PBKDF2_ITERATIONS);

let length = length.unwrap_or(32);

Expand Down Expand Up @@ -311,7 +312,7 @@ fn decrypt_asymmetric(data: String, key: String) {
}

fn hash_password(password: String, iterations: Option<u32>) {
let iterations = iterations.unwrap_or(10000);
let iterations = iterations.unwrap_or(DEFAULT_PBKDF2_ITERATIONS);

let hash: Vec<u8> = devolutions_crypto::password_hash::hash_password(
&password.as_bytes(),
Expand Down
3 changes: 1 addition & 2 deletions ffi/devolutions-crypto.h
Original file line number Diff line number Diff line change
Expand Up @@ -419,8 +419,7 @@ int64_t GetSigningPublicKeySize(const uint8_t *_keypair, size_t _keypair_length)
* * `password` - Pointer to the password to hash.
* * `password_length` - Length of the password to hash.
* * `iterations` - Number of iterations of the password hash.
* A higher number is slower but harder to brute-force. The recommended is 10000,
* but the number can be set by the user.
* A higher number is slower but harder to brute-force. The recommended value is 600,000.
* * `result` - Pointer to the buffer to write the hash to.
* * `result_length` - Length of the buffer to write the hash to. You can get the value by
* calling HashPasswordLength() beforehand.
Expand Down
3 changes: 1 addition & 2 deletions ffi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -456,8 +456,7 @@ pub extern "C" fn SignSize(_version: u16) -> i64 {
/// * `password` - Pointer to the password to hash.
/// * `password_length` - Length of the password to hash.
/// * `iterations` - Number of iterations of the password hash.
/// A higher number is slower but harder to brute-force. The recommended is 10000,
/// but the number can be set by the user.
/// A higher number is slower but harder to brute-force. The recommended value is 600000.
/// * `result` - Pointer to the buffer to write the hash to.
/// * `result_length` - Length of the buffer to write the hash to. You can get the value by
/// calling HashPasswordLength() beforehand.
Expand Down
4 changes: 2 additions & 2 deletions python/PYPI_README.md
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ import devolutions_crypto
password = b"my_secure_password123!"
password_hash = devolutions_crypto.hash_password(
password,
iterations=100000, # Higher is more secure but slower
iterations=600000, # Higher is more secure but slower
version=0
)

Expand Down Expand Up @@ -221,7 +221,7 @@ salt = os.urandom(16) # Use a unique random salt per user
derived_key = devolutions_crypto.derive_key_pbkdf2(
password,
salt=salt,
iterations=100000,
iterations=600000,
length=32
)

Expand Down
12 changes: 6 additions & 6 deletions python/devolutions_crypto.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -127,15 +127,15 @@ def decrypt_asymmetric(

def hash_password(
password: bytes,
iterations: int = 10000,
iterations: int = 600000,
version: int = 0
) -> bytes:
"""
Hash a password using a secure password hashing algorithm (PBKDF2).

Args:
password: The password to hash
iterations: Number of iterations for the KDF (default: 10000, higher is more secure)
iterations: Number of iterations for the KDF (default: 600000, higher is more secure)
version: Password hash version (default: 0)

Returns:
Expand All @@ -146,7 +146,7 @@ def hash_password(

Example:
>>> password = b'my_secure_password'
>>> hash_value = hash_password(password, iterations=100000)
>>> hash_value = hash_password(password, iterations=600000)
"""
...

Expand Down Expand Up @@ -175,7 +175,7 @@ def verify_password(
def derive_key_pbkdf2(
key: bytes,
salt: Optional[bytes] = None,
iterations: int = 10000,
iterations: int = 600000,
length: int = 32
) -> bytes:
"""
Expand All @@ -184,7 +184,7 @@ def derive_key_pbkdf2(
Args:
key: The input key material
salt: Optional salt (default: empty bytes)
iterations: Number of iterations (default: 10000, higher is more secure)
iterations: Number of iterations (default: 600000, higher is more secure)
length: Length of the derived key in bytes (default: 32)

Returns:
Expand All @@ -194,7 +194,7 @@ def derive_key_pbkdf2(
DevolutionsCryptoException: If key derivation fails

Example:
>>> derived = derive_key_pbkdf2(b'password', b'salt', iterations=100000, length=32)
>>> derived = derive_key_pbkdf2(b'password', b'salt', iterations=600000, length=32)
"""
...

Expand Down
4 changes: 2 additions & 2 deletions python/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ fn encrypt_asymmetric(

#[pyfunction]
#[pyo3(name = "hash_password")]
#[pyo3(signature = (password, iterations=10000, version=0))]
#[pyo3(signature = (password, iterations=600000, version=0))]
fn hash_password(
py: Python,
password: &[u8],
Expand Down Expand Up @@ -160,7 +160,7 @@ fn decrypt_asymmetric(

#[pyfunction]
#[pyo3(name = "derive_key_pbkdf2")]
#[pyo3(signature = (key, salt=None, iterations=10000, length=32))]
#[pyo3(signature = (key, salt=None, iterations=600000, length=32))]
fn derive_key_pbkdf2(
py: Python,
key: &[u8],
Expand Down
2 changes: 1 addition & 1 deletion src/ciphertext/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
//! use devolutions_crypto::utils::generate_key;
//! use devolutions_crypto::ciphertext::{ encrypt, CiphertextVersion, Ciphertext };
//!
//! let key: Vec<u8> = generate_key(32).expect("generate key shoudln't fail");
//! let key: Vec<u8> = generate_key(32).expect("generate key shouldn't fail");
//!
//! let data = b"somesecretdata";
//!
Expand Down
18 changes: 10 additions & 8 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
//! use devolutions_crypto::utils::generate_key;
//! use devolutions_crypto::ciphertext::{ encrypt, CiphertextVersion, Ciphertext };
//!
//! let key: Vec<u8> = generate_key(32).expect("generate key shoudln't fail");;
//! let key: Vec<u8> = generate_key(32).expect("generate key shouldn't fail");;
//!
//! let data = b"somesecretdata";
//!
Expand Down Expand Up @@ -59,7 +59,7 @@
//! use devolutions_crypto::utils::generate_key;
//! use devolutions_crypto::ciphertext::{ encrypt, CiphertextVersion, Ciphertext };
//!
//! let key: Vec<u8> = generate_key(32).expect("generate key shoudln't fail");;
//! let key: Vec<u8> = generate_key(32).expect("generate key shouldn't fail");;
//!
//! let data = b"somesecretdata";
//!
Expand Down Expand Up @@ -139,7 +139,7 @@
//!
//! let password = b"somesuperstrongpa$$w0rd!";
//!
//! let hashed_password = hash_password(password, 10000, PasswordHashVersion::Latest).expect("hash password shoudln't fail");;
//! let hashed_password = hash_password(password, 600000, PasswordHashVersion::Latest).expect("hash password shouldn't fail");;
//!
//! assert!(hashed_password.verify_password(b"somesuperstrongpa$$w0rd!"));
//! assert!(!hashed_password.verify_password(b"someweakpa$$w0rd!"));
Expand Down Expand Up @@ -176,19 +176,21 @@
//! ```rust
//! use devolutions_crypto::utils::generate_key;
//!
//! let key = generate_key(32).expect("generate key shoudln't fail");;
//! let key = generate_key(32).expect("generate key shouldn't fail");;
//! assert_eq!(32, key.len());
//! ```
//!
//! ### Key Derivation
//!
//! This is a method used to generate a key from a password or another key. Useful for password-dependant cryptography. Salt should be a random 16 bytes array if possible and iterations should be 10000 or configurable by the user.
//! This is a method used to generate a key from a password or another key. Useful for password-dependent
//! cryptography. Salt should be a random 16 bytes array if possible and iterations should be 600,000 or configurable
//! by the user.
//!
//! ```rust
//! use devolutions_crypto::utils::{generate_key, derive_key_pbkdf2};
//! let key = b"this is a secret password";
//! let salt = generate_key(16).expect("generate key shoudln't fail");;
//! let iterations = 10000;
//! let salt = generate_key(16).expect("generate key shouldn't fail");;
//! let iterations = 600000;
//! let length = 32;
//!
//! let new_key = derive_key_pbkdf2(key, &salt, iterations, length);
Expand Down Expand Up @@ -236,7 +238,7 @@ pub use argon2parameters::Argon2ParametersBuilder;
pub use error::{Error, Result};

pub const DEFAULT_KEY_SIZE: usize = 32;
pub const DEFAULT_PBKDF2_ITERATIONS: u32 = 10000;
pub const DEFAULT_PBKDF2_ITERATIONS: u32 = 600000;

#[cfg(feature = "wbindgen")]
pub mod wasm;
2 changes: 1 addition & 1 deletion src/online_ciphertext/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
//! use devolutions_crypto::utils::generate_key;
//! use devolutions_crypto::ciphertext::{ encrypt, CiphertextVersion, Ciphertext };
//!
//! let key: Vec<u8> = generate_key(32).expect("generate key shoudln't fail");
//! let key: Vec<u8> = generate_key(32).expect("generate key shouldn't fail");
//!
//! let data = b"somesecretdata";
//!
Expand Down
9 changes: 4 additions & 5 deletions src/password_hash/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
//!
//! let password = b"somesuperstrongpa$$w0rd!";
//!
//! let hashed_password = hash_password(password, 10000, PasswordHashVersion::Latest).expect("hash password shoudln't fail");;
//! let hashed_password = hash_password(password, 600000, PasswordHashVersion::Latest).expect("hash password shouldn't fail");;
//!
//! assert!(hashed_password.verify_password(b"somesuperstrongpa$$w0rd!"));
//! assert!(!hashed_password.verify_password(b"someweakpa$$w0rd!"));
Expand Down Expand Up @@ -57,8 +57,7 @@ enum PasswordHashPayload {
/// # Arguments
/// * `password` - The password to hash.
/// * `iterations` - The number of iterations of the password hash.
/// A higher number is slower but harder to brute-force.
/// The recommended is 10000, but the number can be set by the user.
/// A higher number is slower but harder to brute-force (recommended value is 600,000 or higher)
/// * `version` - Version of the library to hash the password with. Use `PasswordHashVersion::Latest` if you're not dealing with shared data.
/// # Returns
/// Returns the `PasswordHash` containing the password verifier.
Expand All @@ -68,7 +67,7 @@ enum PasswordHashPayload {
///
/// let password = b"somesuperstrongpa$$w0rd!";
///
/// let hashed_password = hash_password(password, 10000, PasswordHashVersion::Latest);
/// let hashed_password = hash_password(password, 600000, PasswordHashVersion::Latest);
/// ```
pub fn hash_password(
password: &[u8],
Expand Down Expand Up @@ -99,7 +98,7 @@ impl PasswordHash {
///
/// let password = b"somesuperstrongpa$$w0rd!";
///
/// let hashed_password = hash_password(password, 10000, PasswordHashVersion::Latest).expect("hash password shoudln't fail");;
/// let hashed_password = hash_password(password, 600000, PasswordHashVersion::Latest).expect("hash password shouldn't fail");;
/// assert!(hashed_password.verify_password(b"somesuperstrongpa$$w0rd!"));
/// assert!(!hashed_password.verify_password(b"someweakpa$$w0rd!"));
/// ```
Expand Down
8 changes: 4 additions & 4 deletions src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ use super::Result;
/// ```
/// use devolutions_crypto::utils::generate_key;
///
/// let key = generate_key(32).expect("generate key shoudln't fail");;
/// let key = generate_key(32).expect("generate key shouldn't fail");;
/// assert_eq!(32, key.len());
/// ```
pub fn generate_key(length: usize) -> Result<Vec<u8>> {
Expand All @@ -43,14 +43,14 @@ pub fn generate_key(length: usize) -> Result<Vec<u8>> {
/// * `key` - The key or password to derive.
/// * `salt` - The cryptographic salt to be used to add randomness. Can be empty. Recommended size is 16 bytes.
/// * `iterations` - The number of time the key will be derived. A higher number is slower but harder to brute-force.
/// 10 000 iterations are recommended for a password.
/// 600,000 iterations are recommended for a password.
/// * `length` - Length of the desired key.
/// # Example
/// ```
/// use devolutions_crypto::utils::{derive_key_pbkdf2, generate_key};
/// let key = b"this is a secret password";
/// let salt = generate_key(16).expect("generate key shoudln't fail");;
/// let iterations = 10000;
/// let salt = generate_key(16).expect("generate key shouldn't fail");;
/// let iterations = 600000;
/// let length = 32;
///
/// let new_key = derive_key_pbkdf2(key, &salt, iterations, length);
Expand Down
14 changes: 6 additions & 8 deletions src/wasm.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
use std::convert::{TryFrom as _, TryInto as _};

use wasm_bindgen::prelude::*;

use js_sys::Array;
use wasm_bindgen::prelude::*;

use super::utils;

use super::Argon2Parameters;
use super::DataType;
use super::DEFAULT_PBKDF2_ITERATIONS;
use super::{
ciphertext,
ciphertext::{Ciphertext, CiphertextVersion},
Expand All @@ -31,9 +32,6 @@ use super::{
signing_key::{SigningKeyPair, SigningKeyVersion, SigningPublicKey},
};

use super::Argon2Parameters;
use super::DataType;

// Local KeyPair have private fields with getters instead of public field, for wasm_bindgen
#[wasm_bindgen(inspectable)]
#[derive(Clone)]
Expand Down Expand Up @@ -204,7 +202,7 @@ pub fn hash_password(
) -> Result<Vec<u8>, JsValue> {
Ok(password_hash::hash_password(
&password,
iterations.unwrap_or(10000),
iterations.unwrap_or(DEFAULT_PBKDF2_ITERATIONS),
version.unwrap_or(PasswordHashVersion::Latest),
)?
.into())
Expand Down Expand Up @@ -324,7 +322,7 @@ pub fn derive_key_pbkdf2(
length: Option<usize>,
) -> Vec<u8> {
let salt = salt.unwrap_or_else(|| vec![0u8; 0]);
let iterations = iterations.unwrap_or(10000);
let iterations = iterations.unwrap_or(DEFAULT_PBKDF2_ITERATIONS);
let length = length.unwrap_or(32);

utils::derive_key_pbkdf2(key, &salt, iterations, length)
Expand Down
4 changes: 2 additions & 2 deletions tests/conformity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,11 @@ fn test_derive_key_default() {
let password = b"testpassword";
let salt = b"";

let derived_password = derive_key_pbkdf2(password, salt, 10000, 32);
let derived_password = derive_key_pbkdf2(password, salt, 600000, 32);
assert_eq!(
derived_password,
general_purpose::STANDARD
.decode("ImfGCyv6PwMYaJShGxR4MfVrjuUrsI0CSarJgOApwf8=")
.decode("wdU+cxAOpTFddVhTQlKQTSzmVjZqPAXVx1cRrAqTGek=")
.unwrap()
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ namespace devolutions_crypto {

// Password Hash
[Throws=DevolutionsCryptoError]
bytes hash_password([ByRef] bytes password, optional u32 iterations = 10000, optional PasswordHashVersion version = "Latest");
bytes hash_password([ByRef] bytes password, optional u32 iterations = 600000, optional PasswordHashVersion version = "Latest");

// Secret Sharing
[Throws=DevolutionsCryptoError]
Expand Down
2 changes: 1 addition & 1 deletion uniffi/devolutions-crypto-uniffi/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ pub fn generate_key(length: u32) -> Result<Vec<u8>> {
devolutions_crypto::utils::generate_key(length as usize)
}

#[uniffi::export(default(iterations = 10000, length = 32))]
#[uniffi::export(default(iterations = 600000, length = 32))]
pub fn derive_key_pbkdf2(
key: &[u8],
salt: Option<Vec<u8>>,
Expand Down
Loading
Loading