diff --git a/CHANGELOG.md b/CHANGELOG.md index 7b255ebe3..c775f84f4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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. diff --git a/README_RUST.md b/README_RUST.md index 8294021d2..1d0d4d548 100644 --- a/README_RUST.md +++ b/README_RUST.md @@ -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!")); diff --git a/cli/src/main.rs b/cli/src/main.rs index 89a46b875..249b4d17c 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -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 @@ -254,7 +255,7 @@ fn derive_key(data: String, salt: Option, iterations: Option, 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); @@ -311,7 +312,7 @@ fn decrypt_asymmetric(data: String, key: String) { } fn hash_password(password: String, iterations: Option) { - let iterations = iterations.unwrap_or(10000); + let iterations = iterations.unwrap_or(DEFAULT_PBKDF2_ITERATIONS); let hash: Vec = devolutions_crypto::password_hash::hash_password( &password.as_bytes(), diff --git a/ffi/devolutions-crypto.h b/ffi/devolutions-crypto.h index f582791c9..af2544b6d 100644 --- a/ffi/devolutions-crypto.h +++ b/ffi/devolutions-crypto.h @@ -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. diff --git a/ffi/src/lib.rs b/ffi/src/lib.rs index 3ab34b454..345d83596 100644 --- a/ffi/src/lib.rs +++ b/ffi/src/lib.rs @@ -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. diff --git a/python/PYPI_README.md b/python/PYPI_README.md index 04cf55fb3..70da05dd4 100644 --- a/python/PYPI_README.md +++ b/python/PYPI_README.md @@ -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 ) @@ -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 ) diff --git a/python/devolutions_crypto.pyi b/python/devolutions_crypto.pyi index 03eb4608b..ef851d935 100644 --- a/python/devolutions_crypto.pyi +++ b/python/devolutions_crypto.pyi @@ -127,7 +127,7 @@ def decrypt_asymmetric( def hash_password( password: bytes, - iterations: int = 10000, + iterations: int = 600000, version: int = 0 ) -> bytes: """ @@ -135,7 +135,7 @@ def hash_password( 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: @@ -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) """ ... @@ -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: """ @@ -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: @@ -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) """ ... diff --git a/python/src/lib.rs b/python/src/lib.rs index 67c68d94d..1c99a62ce 100644 --- a/python/src/lib.rs +++ b/python/src/lib.rs @@ -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], @@ -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], diff --git a/src/ciphertext/mod.rs b/src/ciphertext/mod.rs index 4d904a37f..62bcbeb53 100644 --- a/src/ciphertext/mod.rs +++ b/src/ciphertext/mod.rs @@ -9,7 +9,7 @@ //! use devolutions_crypto::utils::generate_key; //! use devolutions_crypto::ciphertext::{ encrypt, CiphertextVersion, Ciphertext }; //! -//! let key: Vec = generate_key(32).expect("generate key shoudln't fail"); +//! let key: Vec = generate_key(32).expect("generate key shouldn't fail"); //! //! let data = b"somesecretdata"; //! diff --git a/src/lib.rs b/src/lib.rs index 51e572af8..b383807ea 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -28,7 +28,7 @@ //! use devolutions_crypto::utils::generate_key; //! use devolutions_crypto::ciphertext::{ encrypt, CiphertextVersion, Ciphertext }; //! -//! let key: Vec = generate_key(32).expect("generate key shoudln't fail");; +//! let key: Vec = generate_key(32).expect("generate key shouldn't fail");; //! //! let data = b"somesecretdata"; //! @@ -59,7 +59,7 @@ //! use devolutions_crypto::utils::generate_key; //! use devolutions_crypto::ciphertext::{ encrypt, CiphertextVersion, Ciphertext }; //! -//! let key: Vec = generate_key(32).expect("generate key shoudln't fail");; +//! let key: Vec = generate_key(32).expect("generate key shouldn't fail");; //! //! let data = b"somesecretdata"; //! @@ -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!")); @@ -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); @@ -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; diff --git a/src/online_ciphertext/mod.rs b/src/online_ciphertext/mod.rs index 9125b114d..1052b249a 100644 --- a/src/online_ciphertext/mod.rs +++ b/src/online_ciphertext/mod.rs @@ -9,7 +9,7 @@ //! use devolutions_crypto::utils::generate_key; //! use devolutions_crypto::ciphertext::{ encrypt, CiphertextVersion, Ciphertext }; //! -//! let key: Vec = generate_key(32).expect("generate key shoudln't fail"); +//! let key: Vec = generate_key(32).expect("generate key shouldn't fail"); //! //! let data = b"somesecretdata"; //! diff --git a/src/password_hash/mod.rs b/src/password_hash/mod.rs index ad32ba2e1..b76ee6e85 100644 --- a/src/password_hash/mod.rs +++ b/src/password_hash/mod.rs @@ -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!")); @@ -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. @@ -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], @@ -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!")); /// ``` diff --git a/src/utils.rs b/src/utils.rs index 19a193dbf..3f41df30f 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -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> { @@ -43,14 +43,14 @@ pub fn generate_key(length: usize) -> Result> { /// * `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); diff --git a/src/wasm.rs b/src/wasm.rs index 1f3d228b2..49941d608 100644 --- a/src/wasm.rs +++ b/src/wasm.rs @@ -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}, @@ -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)] @@ -204,7 +202,7 @@ pub fn hash_password( ) -> Result, JsValue> { Ok(password_hash::hash_password( &password, - iterations.unwrap_or(10000), + iterations.unwrap_or(DEFAULT_PBKDF2_ITERATIONS), version.unwrap_or(PasswordHashVersion::Latest), )? .into()) @@ -324,7 +322,7 @@ pub fn derive_key_pbkdf2( length: Option, ) -> Vec { 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) diff --git a/tests/conformity.rs b/tests/conformity.rs index f41fd5469..5aba9326e 100644 --- a/tests/conformity.rs +++ b/tests/conformity.rs @@ -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() ); } diff --git a/uniffi/devolutions-crypto-uniffi/src/devolutions_crypto.udl b/uniffi/devolutions-crypto-uniffi/src/devolutions_crypto.udl index ffb3e16bb..020fcf849 100644 --- a/uniffi/devolutions-crypto-uniffi/src/devolutions_crypto.udl +++ b/uniffi/devolutions-crypto-uniffi/src/devolutions_crypto.udl @@ -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] diff --git a/uniffi/devolutions-crypto-uniffi/src/utils.rs b/uniffi/devolutions-crypto-uniffi/src/utils.rs index 427de4b33..bedb00722 100644 --- a/uniffi/devolutions-crypto-uniffi/src/utils.rs +++ b/uniffi/devolutions-crypto-uniffi/src/utils.rs @@ -7,7 +7,7 @@ pub fn generate_key(length: u32) -> Result> { 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>, diff --git a/wrappers/csharp/README.md b/wrappers/csharp/README.md index 0836e7f71..48b3b6db1 100644 --- a/wrappers/csharp/README.md +++ b/wrappers/csharp/README.md @@ -103,8 +103,7 @@ You can use this module to hash a password and validate it afterward. This is th using Devolutions.Cryptography; byte[] password = Utils.StringToUtf8ByteArray("somesuperstrongpa$$w0rd!"); - -byte[] hashed_password = Managed.HashPassword(password, 10000); +byte[] hashed_password = Managed.HashPassword(password, 600000); ``` ## SecretSharing @@ -168,14 +167,15 @@ byte[] key = Managed.GenerateKey(32); ### 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. ```C# using Devolutions.Cryptography; byte[] key = Utils.StringToUtf8ByteArray("this is a secret password"); byte[] salt = Managed.GenerateKey(16); -uint iterations = 10000; +uint iterations = 600000; uint length = 32; byte[] new_key = Managed.DeriveKey(key, salt, iterations, length); diff --git a/wrappers/csharp/src/Managed.cs b/wrappers/csharp/src/Managed.cs index b3abb26b9..adade3b2b 100644 --- a/wrappers/csharp/src/Managed.cs +++ b/wrappers/csharp/src/Managed.cs @@ -29,6 +29,8 @@ public static class Managed Justification = "Preprocessor directive")] private const SignatureVersion SIGNATURE_VERSION = SignatureVersion.Latest; + public const uint DEFAULT_PBKDF2_ITERATIONS = 600000; + /// /// Performs a key exchange. /// @@ -166,10 +168,10 @@ public static byte[] DeriveKeyArgon2(byte[] key, Argon2Parameters parameters) /// /// The password to derive. /// The salt. (Optional). - /// The amount of iterations. 10 000 Recommended by NIST. + /// The number of iterations (defaults to 600000). /// The resulting key length. /// Returns the derived password. - public static byte[] DeriveKey(byte[] key, byte[]? salt = null, uint iterations = 10000, uint length = 32) + public static byte[] DeriveKey(byte[] key, byte[]? salt = null, uint iterations = DEFAULT_PBKDF2_ITERATIONS, uint length = 32) { return DeriveKeyPbkdf2(key, salt, iterations, length); } @@ -179,10 +181,10 @@ public static byte[] DeriveKey(byte[] key, byte[]? salt = null, uint iterations /// /// The password to derive. /// The salt. (Optional). - /// The amount of iterations. 10 000 Recommended by NIST. + /// The number of iterations. (defaults to 600000). /// The resulting key length. /// Returns the derived password. - public static byte[] DeriveKeyPbkdf2(byte[] key, byte[]? salt = null, uint iterations = 10000, uint length = 32) + public static byte[] DeriveKeyPbkdf2(byte[] key, byte[]? salt = null, uint iterations = DEFAULT_PBKDF2_ITERATIONS, uint length = 32) { if (key == null || key.Length == 0) { @@ -208,10 +210,10 @@ public static byte[] DeriveKeyPbkdf2(byte[] key, byte[]? salt = null, uint itera /// /// The password to derive. /// The salt. (Optional). - /// The amount of iterations. 10 000 Recommended by NIST. + /// The number of iterations (defaults to 600,000). /// The resulting key length. /// Returns the derived password. - public static byte[] DerivePassword(string password, byte[]? salt = null, uint iterations = 10000, uint length = 32) + public static byte[] DerivePassword(string password, byte[]? salt = null, uint iterations = DEFAULT_PBKDF2_ITERATIONS, uint length = 32) { return DeriveKey(Utils.StringToUtf8ByteArray(password), salt, iterations, length); } @@ -221,9 +223,9 @@ public static byte[] DerivePassword(string password, byte[]? salt = null, uint i /// /// The data to decrypt. /// The salt. (Optional). - /// The amount of iterations. 10 000 Recommended by NIST. + /// The amount of iterations (defaults to 600,000). /// Returns the decryption result in a byte array. - public static byte[] DerivePassword(string password, string? salt, uint iterations = 10000) + public static byte[] DerivePassword(string password, string? salt, uint iterations = DEFAULT_PBKDF2_ITERATIONS) { return DeriveKey(Utils.StringToUtf8ByteArray(password), Utils.StringToUtf8ByteArray(salt), iterations); } @@ -343,11 +345,11 @@ public static byte[] DerivePassword(string password, string? salt, uint iteratio /// /// The data to encrypt. /// The password to use for encryption. - /// The number of iterations used to derive the password. 10 000 Recommended by NIST. + /// The number of iterations used to derive the password (defaults to 600,000). /// Additional authenticated data. (Optional). /// The cipher version to use. (Latest is recommended). /// Returns the encryption result as a base 64 encoded string. - public static string? EncryptWithPasswordAsBase64String(byte[]? data, string password, uint iterations = 10000, byte[]? aad = null, CipherTextVersion cipherTextVersion = CIPHERTEXT_VERSION) + public static string? EncryptWithPasswordAsBase64String(byte[]? data, string password, uint iterations = DEFAULT_PBKDF2_ITERATIONS, byte[]? aad = null, CipherTextVersion cipherTextVersion = CIPHERTEXT_VERSION) { if (data == null || data.Length == 0) { @@ -376,11 +378,11 @@ public static byte[] DerivePassword(string password, string? salt, uint iteratio /// /// The data to encrypt. /// The password to use for encryption. - /// The number of iterations used to derive the password. 10 000 Recommended by NIST. + /// The number of iterations used to derive the password (defaults to 600,000). /// Additional authenticated data. (Optional). /// The cipher version to use. (Latest is recommended). /// Returns the encryption result as a base 64 encoded string. - public static string? EncryptBase64WithPasswordAsString(string b64data, string password, uint iterations = 10000, byte[]? aad = null, CipherTextVersion cipherTextVersion = CIPHERTEXT_VERSION) + public static string? EncryptBase64WithPasswordAsString(string b64data, string password, uint iterations = DEFAULT_PBKDF2_ITERATIONS, byte[]? aad = null, CipherTextVersion cipherTextVersion = CIPHERTEXT_VERSION) { return EncryptBase64WithPasswordAsBase64String(b64data, password, iterations, aad, cipherTextVersion); } @@ -390,14 +392,14 @@ public static byte[] DerivePassword(string password, string? salt, uint iteratio /// /// The data to encrypt. /// The password to use for encryption. - /// The number of iterations used to derive the password. 10 000 Recommended by NIST. + /// The number of iterations used to derive the password (defaults to 600,000). /// Additional authenticated data. (Optional). /// The cipher version to use. (Latest is recommended). /// Returns the encryption result as a base 64 encoded string. public static string? EncryptBase64WithPasswordAsBase64String( string b64data, string password, - uint iterations = 10000, + uint iterations = DEFAULT_PBKDF2_ITERATIONS, byte[]? aad = null, CipherTextVersion cipherTextVersion = CIPHERTEXT_VERSION) { @@ -487,9 +489,9 @@ public static bool VerifyPassword(byte[] password, byte[] hash, ILegacyHasher? l /// Hash a password. /// /// The password to hash in bytes. - /// The number of iterations used to hash the password. 10 000 Recommended by NIST. + /// The number of iterations used to hash the password (defaults to 600,000). /// Returns the hashed password in bytes. - public static byte[] HashPassword(byte[] password, uint iterations = 10000) + public static byte[] HashPassword(byte[] password, uint iterations = DEFAULT_PBKDF2_ITERATIONS) { if (password == null || password.Length == 0) { @@ -712,11 +714,11 @@ public static SigningKeyPair GenerateSigningKeyPair(SignatureVersion version = S /// /// The data to encrypt. /// The password to use for encryption. - /// The number of iterations used to derive the password. 10 000 Recommended by NIST. + /// The number of iterations used to derive the password (defaults to 600,000). /// Additional authenticated data. (Optional). /// The cipher version to use. (Latest is recommended). /// Returns the encryption result as a base 64 encoded string. - public static string? EncryptWithPasswordAsBase64String(string data, string password, uint iterations = 10000, byte[]? aad = null, CipherTextVersion cipherTextVersion = CIPHERTEXT_VERSION) + public static string? EncryptWithPasswordAsBase64String(string data, string password, uint iterations = DEFAULT_PBKDF2_ITERATIONS, byte[]? aad = null, CipherTextVersion cipherTextVersion = CIPHERTEXT_VERSION) { if (string.IsNullOrEmpty(data)) { @@ -745,11 +747,11 @@ public static SigningKeyPair GenerateSigningKeyPair(SignatureVersion version = S /// /// The data to encrypt. /// The password to use for encryption. - /// The number of iterations used to derive the password. 10 000 Recommended by NIST. + /// The number of iterations used to derive the password (defaults to 600,000). /// Additional authenticated data. (Optional). /// The cipher version to use. (Latest is recommended). /// Returns the encryption result as a byte array. - public static byte[]? EncryptWithPassword(byte[]? data, string password, uint iterations = 10000, byte[]? aad = null, CipherTextVersion cipherTextVersion = CIPHERTEXT_VERSION) + public static byte[]? EncryptWithPassword(byte[]? data, string password, uint iterations = DEFAULT_PBKDF2_ITERATIONS, byte[]? aad = null, CipherTextVersion cipherTextVersion = CIPHERTEXT_VERSION) { if (data == null || data.Length == 0) { @@ -778,11 +780,11 @@ public static SigningKeyPair GenerateSigningKeyPair(SignatureVersion version = S /// /// The data to encrypt. /// The password to use for encryption. - /// The number of iterations used to derive the password. 10 000 Recommended by NIST. + /// The number of iterations used to derive the password. (defaults to 600,000). /// Additional authenticated data. (Optional). /// The cipher version to use. (Latest is recommended). /// Returns the encryption result as a byte array. - public static byte[]? EncryptBase64WithPassword(string? b64data, string password, uint iterations = 10000, byte[]? aad = null, CipherTextVersion cipherTextVersion = CIPHERTEXT_VERSION) + public static byte[]? EncryptBase64WithPassword(string? b64data, string password, uint iterations = DEFAULT_PBKDF2_ITERATIONS, byte[]? aad = null, CipherTextVersion cipherTextVersion = CIPHERTEXT_VERSION) { // sduquette 2025-11-12: Explicitly comparing b64data to null because the NotNullWhen directive used by string.IsNullOrEmpty // is not available in netstandard2.0. @@ -813,11 +815,11 @@ public static SigningKeyPair GenerateSigningKeyPair(SignatureVersion version = S /// /// The data to encrypt. /// The password to use for encryption. - /// The number of iterations used to derive the password. 10 000 Recommended by NIST. + /// The number of iterations used to derive the password (defaults to 600,000). /// Additional authenticated data. (Optional). /// The cipher version to use. (Latest is recommended). /// Returns the encryption result as a byte array. - public static byte[]? EncryptWithPassword(string data, string password, uint iterations = 10000, byte[]? aad = null, CipherTextVersion cipherTextVersion = CIPHERTEXT_VERSION) + public static byte[]? EncryptWithPassword(string data, string password, uint iterations = DEFAULT_PBKDF2_ITERATIONS, byte[]? aad = null, CipherTextVersion cipherTextVersion = CIPHERTEXT_VERSION) { if (string.IsNullOrEmpty(data)) { @@ -907,32 +909,15 @@ public static SigningKeyPair GenerateSigningKeyPair(SignatureVersion version = S /// The number of iterations used to derive the password. /// Additional authenticated data. (Optional). /// Returns the decryption result as a UTF8 encoded string. - public static string? DecryptWithPasswordAsUtf8String(byte[]? data, string password, uint iterations = 10000, byte[]? aad = null) + public static string? DecryptWithPasswordAsUtf8String(byte[]? data, string password, uint iterations = DEFAULT_PBKDF2_ITERATIONS, byte[]? aad = null) { if (data == null || data.Length == 0) { return null; } - // There was a bug in DeriveKey v1 where the generated key was 256 bytes instead of 256 bits, only in C#. - // This is unfortunately the best way we found to fix it while keeping backward compatibility. - // We try to decrypt with a 256 bits key, and if it doesn't work(InvalidMac means either the data or the key is invalid), - // we try with the buggy 256 bytes key. byte[] key = DeriveKey(Utils.StringToUtf8ByteArray(password), null, iterations, 32); - - byte[]? result = DecryptSafe(data, key, out DevolutionsCryptoException? exception, aad); - - if (exception is { NativeError: NativeError.InvalidMac }) - { - key = DeriveKey(Utils.StringToUtf8ByteArray(password), null, iterations, 256); - result = Decrypt(data, key, aad); - return Utils.ByteArrayToUtf8String(result); - } - - if (exception != null) - { - throw exception; - } + byte[]? result = Decrypt(data, key, aad); return Utils.ByteArrayToUtf8String(result); } @@ -997,7 +982,7 @@ public static byte[] JoinShares(byte[][] shares) /// The number of iterations used to derive the password. /// Additional authenticated data. (Optional). /// Returns the decryption result as a UTF8 encoded string. - public static string? DecryptWithPasswordAsUtf8String(string? b64data, string password, uint iterations = 10000, byte[]? aad = null) + public static string? DecryptWithPasswordAsUtf8String(string? b64data, string password, uint iterations = DEFAULT_PBKDF2_ITERATIONS, byte[]? aad = null) { // sduquette 2025-11-12: Explicitly comparing b64data to null because the NotNullWhen directive used by string.IsNullOrEmpty // is not available in netstandard2.0. @@ -1006,22 +991,8 @@ public static byte[] JoinShares(byte[][] shares) return null; } - // There was a bug in DeriveKey v1 where the generated key was 256 bytes instead of 256 bits, only in C#. - // This is unfortunately the best way we found to fix it while keeping backward compatibility. - // We try to decrypt with a 256 bits key, and if it doesn't work(InvalidMac means either the data or the key is invalid), - // we try with the buggy 256 bytes key. byte[] key = DeriveKey(Utils.StringToUtf8ByteArray(password), null, iterations, 32); - byte[]? result = DecryptSafe(Utils.Base64StringToByteArray(b64data), key, out DevolutionsCryptoException? exception, aad); - - if (exception is { NativeError: NativeError.InvalidMac }) - { - key = DeriveKey(Utils.StringToUtf8ByteArray(password), null, iterations, 256); - result = Decrypt(Utils.Base64StringToByteArray(b64data), key, aad); - } - else if (exception != null) - { - throw exception; - } + byte[]? result = Decrypt(Utils.Base64StringToByteArray(b64data), key, aad); return Utils.ByteArrayToUtf8String(result); } @@ -1079,29 +1050,15 @@ public static byte[][] GenerateSharedKey(int nbShares, int threshold, int secret /// The number of iterations used to derive the password. /// Additional authenticated data. (Optional). /// Returns the decryption result as a byte array. - public static byte[]? DecryptWithPassword(byte[]? data, string password, uint iterations = 10000, byte[]? aad = null) + public static byte[]? DecryptWithPassword(byte[]? data, string password, uint iterations = DEFAULT_PBKDF2_ITERATIONS, byte[]? aad = null) { if (data == null || data.Length == 0) { return null; } - //// There was a bug in DeriveKey v1 where the generated key was 256 bytes instead of 256 bits, only in C#. - //// This is unfortunately the best way we found to fix it while keeping backward compatibility. - //// We try to decrypt with a 256 bits key, and if it doesn't work(InvalidMac means either the data or the key is invalid), - //// we try with the buggy 256 bytes key. byte[] key = DeriveKey(Utils.StringToUtf8ByteArray(password), null, iterations, 32); - byte[]? result = DecryptSafe(data, key, out DevolutionsCryptoException? exception, aad); - - if (exception is { NativeError: NativeError.InvalidMac }) - { - key = DeriveKey(Utils.StringToUtf8ByteArray(password), null, iterations, 256); - result = Decrypt(data, key, aad); - } - else if (exception != null) - { - throw exception; - } + byte[]? result = Decrypt(data, key, aad); return result; } @@ -1114,30 +1071,15 @@ public static byte[][] GenerateSharedKey(int nbShares, int threshold, int secret /// The number of iterations used to derive the password. /// Additional authenticated data. (Optional). /// Returns the decryption result as a byte array. - public static byte[]? DecryptWithPassword(string b64data, string password, uint iterations = 10000, byte[]? aad = null) + public static byte[]? DecryptWithPassword(string b64data, string password, uint iterations = DEFAULT_PBKDF2_ITERATIONS, byte[]? aad = null) { if (string.IsNullOrEmpty(b64data)) { return null; } - // There was a bug in DeriveKey v1 where the generated key was 256 bytes instead of 256 bits, only in C#. - // This is unfortunately the best way we found to fix it while keeping backward compatibility. - // We try to decrypt with a 256 bits key, and if it doesn't work(InvalidMac means either the data or the key is invalid), - // we try with the buggy 256 bytes key. - byte[] key = DeriveKey(Utils.StringToUtf8ByteArray(password), null, iterations, 32); - - byte[]? result = DecryptSafe(Utils.Base64StringToByteArray(b64data), key, out DevolutionsCryptoException? exception, aad); - - if (exception is { NativeError: NativeError.InvalidMac }) - { - key = DeriveKey(Utils.StringToUtf8ByteArray(password), null, iterations, 256); - result = Decrypt(Utils.Base64StringToByteArray(b64data), key, aad); - } - else if (exception != null) - { - throw exception; - } + byte[] key = DeriveKey(Utils.StringToUtf8ByteArray(password), null, iterations); + byte[]? result = Decrypt(Utils.Base64StringToByteArray(b64data), key, aad); return result; } diff --git a/wrappers/csharp/src/Native.Core.cs b/wrappers/csharp/src/Native.Core.cs index a165786df..913ffd397 100644 --- a/wrappers/csharp/src/Native.Core.cs +++ b/wrappers/csharp/src/Native.Core.cs @@ -37,16 +37,16 @@ public static partial class Native internal static extern long DecodeUrlNative(string input, UIntPtr input_length, byte[] output, UIntPtr output_length); [DllImport(LibName, EntryPoint = "Decrypt", CallingConvention = CallingConvention.Cdecl)] - internal static extern long DecryptNative(byte[] data, UIntPtr dataLength, byte[] key, UIntPtr keyLength, byte[] aad, UIntPtr aadLength, byte[] result, UIntPtr resultLength); + internal static extern long DecryptNative(byte[] data, UIntPtr dataLength, byte[] key, UIntPtr keyLength, byte[]? aad, UIntPtr aadLength, byte[] result, UIntPtr resultLength); [DllImport(LibName, EntryPoint = "DecryptAsymmetric", CallingConvention = CallingConvention.Cdecl)] - internal static extern long DecryptAsymmetricNative(byte[] data, UIntPtr dataLength, byte[] privateKey, UIntPtr privateKeyLength, byte[] aad, UIntPtr aadLength, byte[] result, UIntPtr resultLength); + internal static extern long DecryptAsymmetricNative(byte[] data, UIntPtr dataLength, byte[] privateKey, UIntPtr privateKeyLength, byte[]? aad, UIntPtr aadLength, byte[] result, UIntPtr resultLength); [DllImport(LibName, EntryPoint = "DeriveKeyArgon2", CallingConvention = CallingConvention.Cdecl)] internal static extern long DeriveKeyArgon2Native(byte[] key, UIntPtr keyLength, byte[] argon2Parameters, UIntPtr argon2ParametersLength, byte[] result, UIntPtr resultLength); [DllImport(LibName, EntryPoint = "DeriveKeyPbkdf2", CallingConvention = CallingConvention.Cdecl)] - internal static extern long DeriveKeyPbkdf2Native(byte[] key, UIntPtr keyLength, byte[] salt, UIntPtr saltLength, System.UInt32 iterations, byte[] result, UIntPtr resultLength); + internal static extern long DeriveKeyPbkdf2Native(byte[] key, UIntPtr keyLength, byte[]? salt, UIntPtr saltLength, System.UInt32 iterations, byte[] result, UIntPtr resultLength); [DllImport(LibName, EntryPoint = "Encode", CallingConvention = CallingConvention.Cdecl)] internal static extern long EncodeNative(byte[] input, UIntPtr input_length, byte[] output, UIntPtr output_length); @@ -56,10 +56,10 @@ public static partial class Native internal static extern long EncodeUrlNative(byte[] input, UIntPtr input_length, byte[] output, UIntPtr output_length); [DllImport(LibName, EntryPoint = "Encrypt", CallingConvention = CallingConvention.Cdecl)] - internal static extern long EncryptNative(byte[] data, UIntPtr dataLength, byte[] key, UIntPtr keyLength, byte[] aad, UIntPtr aadLength, byte[] result, UIntPtr resultLength, UInt16 version); + internal static extern long EncryptNative(byte[] data, UIntPtr dataLength, byte[] key, UIntPtr keyLength, byte[]? aad, UIntPtr aadLength, byte[] result, UIntPtr resultLength, UInt16 version); [DllImport(LibName, EntryPoint = "EncryptAsymmetric", CallingConvention = CallingConvention.Cdecl)] - internal static extern long EncryptAsymmetricNative(byte[] data, UIntPtr dataLength, byte[] publicKey, UIntPtr publicKeyLength, byte[] aad, UIntPtr aadLength, byte[] result, UIntPtr resultLength, ushort version); + internal static extern long EncryptAsymmetricNative(byte[] data, UIntPtr dataLength, byte[] publicKey, UIntPtr publicKeyLength, byte[]? aad, UIntPtr aadLength, byte[] result, UIntPtr resultLength, ushort version); [DllImport(LibName, EntryPoint = "EncryptAsymmetricSize", CallingConvention = CallingConvention.Cdecl)] internal static extern long EncryptAsymmetricSizeNative(UIntPtr dataLength, ushort version); diff --git a/wrappers/csharp/src/Native.cs b/wrappers/csharp/src/Native.cs index 535dd91b4..020188a09 100644 --- a/wrappers/csharp/src/Native.cs +++ b/wrappers/csharp/src/Native.cs @@ -74,13 +74,13 @@ static Native() } [Obsolete("This method has been deprecated. Use Managed.DerivePassword instead.")] - public static byte[] DerivePassword(string password, string salt, uint iterations = 10000) + public static byte[] DerivePassword(string password, string salt, uint iterations = 600000) { return Managed.DerivePassword(password, salt, iterations); } [Obsolete("This method has been deprecated. Use Managed.DeriveKey instead.")] - public static byte[] DeriveKey(byte[] key, byte[] salt, uint iterations = 10000, uint length = 32) + public static byte[] DeriveKey(byte[] key, byte[] salt, uint iterations = 600000, uint length = 32) { return Managed.DeriveKey(key, salt, iterations, length); } @@ -104,7 +104,7 @@ public static KeyPair GenerateKeyPair() } [Obsolete("This method has been deprecated. Use Managed.HashPassword instead.")] - public static byte[] HashPassword(byte[] password, uint iterations = 10000) + public static byte[] HashPassword(byte[] password, uint iterations = 600000) { return Managed.HashPassword(password, iterations); } diff --git a/wrappers/csharp/tests/unit-tests/Conformity.cs b/wrappers/csharp/tests/unit-tests/Conformity.cs index eeddfed44..1fc9aafbf 100644 --- a/wrappers/csharp/tests/unit-tests/Conformity.cs +++ b/wrappers/csharp/tests/unit-tests/Conformity.cs @@ -102,7 +102,7 @@ public void DeriveKey_Default() byte[] encodedPassword = Utils.StringToUtf8ByteArray("testpassword"); byte[] derivedPassword = Managed.DeriveKey(encodedPassword); string derivedPasswordAsBase64String = Utils.EncodeToBase64String(derivedPassword)!; - Assert.AreEqual(derivedPasswordAsBase64String, "ImfGCyv6PwMYaJShGxR4MfVrjuUrsI0CSarJgOApwf8="); + Assert.AreEqual(derivedPasswordAsBase64String, "wdU+cxAOpTFddVhTQlKQTSzmVjZqPAXVx1cRrAqTGek="); } [TestMethod] diff --git a/wrappers/csharp/tests/unit-tests/TestManaged.cs b/wrappers/csharp/tests/unit-tests/TestManaged.cs index 2624523bb..6fcbafdf0 100644 --- a/wrappers/csharp/tests/unit-tests/TestManaged.cs +++ b/wrappers/csharp/tests/unit-tests/TestManaged.cs @@ -61,38 +61,20 @@ public void DecryptWithKeyAsUtf8String() } [TestMethod] - public void DecryptWithPassword2() + public void DecryptWithPassword() { - string encryptedDataAsBase64 = "DQwCAAAAAgDsQkLRs1I3054gNOYP7ifVSpOMFEV8vTfoMuZOWAzbMR2b1QLyIe0/NFNKr8rniijd8PxHv29N"; + string encryptedDataAsBase64 = "DQwCAAAAAgDutPWBLPHG0+ocNw+Yzs6xygGOeOlNPOAjbYDdbJKjPRnEP8HuDN7Y3h3dCoH81Szf3tCf3mNf"; string password = "testPa$$"; - byte[]? decryptResult = Managed.DecryptWithPassword(encryptedDataAsBase64, password); + byte[]? decryptResult = Managed.DecryptWithPassword(encryptedDataAsBase64, password, 10000); string? decryptResultString = Utils.ByteArrayToUtf8String(decryptResult); Assert.AreEqual(decryptResultString, "test Ciph3rtext"); } - [TestMethod] - public void DecryptWithPassword2_5() - { - try - { - string encryptedDataAsBase64 = "DQwCAAAAAgDutPWBLPHG0+ocNw+Yzs6xygGOeOlNPOAjbYDdbJKjPRnEP8HuDN7Y3h3dCoH81Szf3tCf3mNf"; - string password = "testPa$$"; - byte[]? decryptResult = Managed.DecryptWithPassword(encryptedDataAsBase64, password); - string? decryptResultString = Utils.ByteArrayToUtf8String(decryptResult); - Assert.AreEqual(decryptResultString, "test Ciph3rtext"); - } - catch (Exception ex) - { - Console.WriteLine(ex.InnerException?.Message); - Console.WriteLine(ex.InnerException?.StackTrace); - } - } - [TestMethod] public void DecryptWithPasswordAsUtf8String() { string encryptedDataAsBase64 = "DQwCAAAAAgCoE9Y3m06QaPSAiL2qegthcm0+zZWt4fXbdqcefkzD6y8pnWsMzLkx/32t"; - string? decryptResultString = Managed.DecryptWithPasswordAsUtf8String(encryptedDataAsBase64, TestData.TestPassword); + string? decryptResultString = Managed.DecryptWithPasswordAsUtf8String(encryptedDataAsBase64, TestData.TestPassword, 10000); Assert.AreEqual(decryptResultString, TestData.StringTestData); } diff --git a/wrappers/kotlin/lib/src/test/kotlin/org/devolutions/crypto/ConformityTest.kt b/wrappers/kotlin/lib/src/test/kotlin/org/devolutions/crypto/ConformityTest.kt index daa424aab..a8759c280 100644 --- a/wrappers/kotlin/lib/src/test/kotlin/org/devolutions/crypto/ConformityTest.kt +++ b/wrappers/kotlin/lib/src/test/kotlin/org/devolutions/crypto/ConformityTest.kt @@ -18,7 +18,7 @@ class ConformityTest { iterations = 100u ) - val expected = base64Decode("ImfGCyv6PwMYaJShGxR4MfVrjuUrsI0CSarJgOApwf8=") + val expected = base64Decode("wdU+cxAOpTFddVhTQlKQTSzmVjZqPAXVx1cRrAqTGek=") val expectedWithIterations = base64Decode("ev/GiJLvOgIkkWrnIrHSi2fdZE5qJBIrW+DLeMLIXK4=") val expectedWithSalt = base64Decode("ZaYRZeQiIPJ+Jl511AgHZjv4/HbCFq4eUP9yNa3gowI=") diff --git a/wrappers/python/tests/conformity.py b/wrappers/python/tests/conformity.py index 819e7a73c..05b4de700 100644 --- a/wrappers/python/tests/conformity.py +++ b/wrappers/python/tests/conformity.py @@ -4,7 +4,7 @@ class TestComformity(unittest.TestCase): def test_derive_pbkdf2(self): - self.assertEqual(devolutions_crypto.derive_key_pbkdf2(b'testpassword'), b64decode(b'ImfGCyv6PwMYaJShGxR4MfVrjuUrsI0CSarJgOApwf8=')) + self.assertEqual(devolutions_crypto.derive_key_pbkdf2(b'testpassword'), b64decode(b'wdU+cxAOpTFddVhTQlKQTSzmVjZqPAXVx1cRrAqTGek=')) self.assertEqual(devolutions_crypto.derive_key_pbkdf2(b'testPa$$', None, 100), b64decode(b'ev/GiJLvOgIkkWrnIrHSi2fdZE5qJBIrW+DLeMLIXK4=')) self.assertEqual(devolutions_crypto.derive_key_pbkdf2(b'testPa$$', b64decode(b'tdTt5wgeqQYLvkiXKkFirqy2hMbzadBtL+jekVeNCRA='), 100), b64decode(b'ZaYRZeQiIPJ+Jl511AgHZjv4/HbCFq4eUP9yNa3gowI=')) diff --git a/wrappers/wasm/README.md b/wrappers/wasm/README.md index c053af69a..1b6eb1ea6 100644 --- a/wrappers/wasm/README.md +++ b/wrappers/wasm/README.md @@ -1,21 +1,11 @@ # devolutions-crypto -[![Build Status](https://dev.azure.com/devolutions-net/Open%20Source/_apis/build/status/devolutions-crypto?branchName=master)](https://dev.azure.com/devolutions-net/Open%20Source/_build/latest?definitionId=170&branchName=master) [![npm version](https://img.shields.io/npm/v/devolutions-crypto.svg?style=flat)](https://npmjs.org/package/devolutions-crypto "View this project on npm") Cryptographic library used in Devolutions products. It is made to be fast, easy to use and misuse-resistant. # Usage -You can refer to the [Angular example](example/) or the [unit tests](tests/) to see how to use the library. - -# Underlying algorithms -As of the current version: - * Symmetric cryptography uses XChaCha20Poly1305 - * Asymmetric cryptography uses Curve25519. - * Asymmetric encryption uses ECIES. - * Key exchange uses x25519, or ECDH over Curve25519 - * Password Hashing uses PBKDF2-HMAC-SHA2-256 - * Secret Sharing uses Shamir Secret sharing over GF256 +You can refer to the [Angular example](demo/) or the [unit tests](tests/) to see how to use the library. # Known Issue -You will also need to import the library asynchronously(using `import().then()`). This is a browser limitation that prohibits loading WebAssembly in the main chunk. To see how to do it cleanly, please refer to the [Angular example](example/). +The WASM bundle must be loaded asynchronously. In browser applications, use a dynamic `import()` (or another async loading mechanism) and wait for the module `ready` promise before calling into the library. For a concrete integration example, refer to the [Angular demo](demo/), which uses dynamic import + `ready` -On firefox, exception shows up as `Error` in the console if not caught, but the value of `error.name` is the right one, so you can still try/catch depending on the error name. \ No newline at end of file +On Firefox, exception shows up as `Error` in the console if not caught, but the value of `error.name` is the right one, so you can still try/catch depending on the error name. \ No newline at end of file diff --git a/wrappers/wasm/demo/src/app/password/password.component.html b/wrappers/wasm/demo/src/app/password/password.component.html index 4d4c6809b..448bc2938 100644 --- a/wrappers/wasm/demo/src/app/password/password.component.html +++ b/wrappers/wasm/demo/src/app/password/password.component.html @@ -21,7 +21,7 @@

Password Hashing

- +
diff --git a/wrappers/wasm/demo/src/app/utilities/utilities.component.html b/wrappers/wasm/demo/src/app/utilities/utilities.component.html index 998d5f84e..842c30675 100644 --- a/wrappers/wasm/demo/src/app/utilities/utilities.component.html +++ b/wrappers/wasm/demo/src/app/utilities/utilities.component.html @@ -60,7 +60,7 @@

Utilities

- +
diff --git a/wrappers/wasm/tests/README.md b/wrappers/wasm/tests/README.md index e62212e3a..4c80be92e 100644 --- a/wrappers/wasm/tests/README.md +++ b/wrappers/wasm/tests/README.md @@ -1,5 +1,4 @@ -# devolutions-crypto -[![Build Status](https://dev.azure.com/devolutions-net/Open%20Source/_apis/build/status/devolutions-crypto?branchName=master)](https://dev.azure.com/devolutions-net/Open%20Source/_build/latest?definitionId=170&branchName=master) [![npm version](https://img.shields.io/npm/v/devolutions-crypto.svg?style=flat)](https://npmjs.org/package/devolutions-crypto "View this project on npm") +# devolutions-crypto webassembly tests This folder contains the TypeScript unit tests for the library. You can also use them as usage examples. diff --git a/wrappers/wasm/tests/tests/conformity.ts b/wrappers/wasm/tests/tests/conformity.ts index 81de04faf..c710f1dad 100644 --- a/wrappers/wasm/tests/tests/conformity.ts +++ b/wrappers/wasm/tests/tests/conformity.ts @@ -14,7 +14,7 @@ describe('Conformity Tests', () => { const derivedKeyWithIterations: Uint8Array = deriveKeyPbkdf2(encoder.encode('testPa$$'), null, 100) const derivedKeyWithSalt: Uint8Array = deriveKeyPbkdf2(encoder.encode('testPa$$'), base64decode('tdTt5wgeqQYLvkiXKkFirqy2hMbzadBtL+jekVeNCRA='), 100) - assert.strictEqual(base64encode(derivedKey), 'ImfGCyv6PwMYaJShGxR4MfVrjuUrsI0CSarJgOApwf8=') + assert.strictEqual(base64encode(derivedKey), 'wdU+cxAOpTFddVhTQlKQTSzmVjZqPAXVx1cRrAqTGek=') assert.strictEqual(base64encode(derivedKeyWithIterations), 'ev/GiJLvOgIkkWrnIrHSi2fdZE5qJBIrW+DLeMLIXK4=') assert.strictEqual(base64encode(derivedKeyWithSalt), 'ZaYRZeQiIPJ+Jl511AgHZjv4/HbCFq4eUP9yNa3gowI=') })