Cryptographic library used in Devolutions products. It is made to be fast, easy to use and misuse-resistant.
Documentation
- Overview
- Ciphertext Module
- Key Module
- PasswordHash Module
- SecretSharing Module
- Signature Module
- Utils Module
The library is split into multiple modules, which are explained below. When
dealing with "managed" data, that includes an header and versioning, you deal
with structures like Ciphertext
, PublicKey
, etc.
These structures all implement TryFrom<&[u8]>
and Into<Vec<u8>>
which are the implemented way to serialize and deserialize data.
use std::convert::TryFrom as _;
use devolutions_crypto::utils::generate_key;
use devolutions_crypto::ciphertext::{ encrypt, CiphertextVersion, Ciphertext };
let key: Vec<u8> = generate_key(32);
let data = b"somesecretdata";
let encrypted_data: Ciphertext = encrypt(data, &key, CiphertextVersion::Latest).expect("encryption shouldn't fail");
// The ciphertext can be serialized.
let encrypted_data_vec: Vec<u8> = encrypted_data.into();
// This data can be saved somewhere, passed to another language or over the network.
//
// When you receive the data as a byte array, you can deserialize it into a struct using TryFrom.
let ciphertext = Ciphertext::try_from(encrypted_data_vec.as_slice()).expect("deserialization shouldn't fail");
let decrypted_data = ciphertext.decrypt(&key).expect("The decryption shouldn't fail");
assert_eq!(decrypted_data, data);
This module contains everything related to encryption. You can use it to encrypt and decrypt data using either a shared key of a keypair.
Either way, the encryption will give you a Ciphertext
, which has a method to decrypt it.
use devolutions_crypto::utils::generate_key;
use devolutions_crypto::ciphertext::{ encrypt, CiphertextVersion, Ciphertext };
let key: Vec<u8> = generate_key(32);
let data = b"somesecretdata";
let encrypted_data: Ciphertext = encrypt(data, &key, CiphertextVersion::Latest).expect("encryption shouldn't fail");
let decrypted_data = encrypted_data.decrypt(&key).expect("The decryption shouldn't fail");
assert_eq!(decrypted_data, data);
Here, you will need a PublicKey
to encrypt data and the corresponding
PrivateKey
to decrypt it. You can generate them by using generate_keypair
in the Key module.
use devolutions_crypto::key::{generate_keypair, KeyVersion, KeyPair};
use devolutions_crypto::ciphertext::{ encrypt_asymmetric, CiphertextVersion, Ciphertext };
let keypair: KeyPair = generate_keypair(KeyVersion::Latest);
let data = b"somesecretdata";
let encrypted_data: Ciphertext = encrypt_asymmetric(data, &keypair.public_key, CiphertextVersion::Latest).expect("encryption shouldn't fail");
let decrypted_data = encrypted_data.decrypt_asymmetric(&keypair.private_key).expect("The decryption shouldn't fail");
assert_eq!(decrypted_data, data);
For now, this module only deal with keypairs, as the symmetric keys are not wrapped yet.
Using generate_keypair
will generate a random keypair.
Asymmetric keys have two uses. They can be used to encrypt and decrypt data and to perform a key exchange.
use devolutions_crypto::key::{generate_keypair, KeyVersion, KeyPair};
let keypair: KeyPair = generate_keypair(KeyVersion::Latest);
The goal of using a key exchange is to get a shared secret key between two parties without making it possible for users listening on the conversation to guess that shared key.
- Alice and Bob each generate a
KeyPair
. - Alice and Bob exchange their
PublicKey
. - Alice mixes her
PrivateKey
with Bob'sPublicKey
. This gives her the shared key. - Bob mixes his
PrivateKey
with Alice'sPublicKey
. This gives him the shared key. - Both Bob and Alice have the same shared key, which they can use for symmetric encryption for further communications.
use devolutions_crypto::key::{generate_keypair, mix_key_exchange, KeyVersion, KeyPair};
let bob_keypair: KeyPair = generate_keypair(KeyVersion::Latest);
let alice_keypair: KeyPair = generate_keypair(KeyVersion::Latest);
let bob_shared = mix_key_exchange(&bob_keypair.private_key, &alice_keypair.public_key).expect("key exchange should not fail");
let alice_shared = mix_key_exchange(&alice_keypair.private_key, &bob_keypair.public_key).expect("key exchange should not fail");
// They now have a shared secret!
assert_eq!(bob_shared, alice_shared);
You can use this module to hash a password and validate it afterward. This is the recommended way to verify a user password on login.
use devolutions_crypto::password_hash::{hash_password, PasswordHashVersion};
let password = b"somesuperstrongpa$$w0rd!";
let hashed_password = hash_password(password, 10000, PasswordHashVersion::Latest);
assert!(hashed_password.verify_password(b"somesuperstrongpa$$w0rd!"));
assert!(!hashed_password.verify_password(b"someweakpa$$w0rd!"));
This module is used to generate a key that is split in multiple Share
and that requires a specific amount of them to regenerate the key.
You can think of it as a "Break The Glass" scenario. You can for example
generate a key, encrypt your data and then require 3 out of the 5 administrators
to decrypt the data. That data could also be an API key or password of a super
admin account.
use devolutions_crypto::secret_sharing::{generate_shared_key, join_shares, SecretSharingVersion, Share};
// You want a key of 32 bytes, split between 5 people, and a
// minimum of 3 of these shares to regenerate the key.
let shares: Vec<Share> = generate_shared_key(5, 3, 32, SecretSharingVersion::Latest).expect("generation shouldn't fail with the right parameters");
assert_eq!(shares.len(), 5);
let key = join_shares(&shares[2..5]).expect("joining shouldn't fail with the right shares");
This module is used to sign data using a keypair to certify its authenticity.
use devolutions_crypto::signing_key::{generate_signing_keypair, SigningKeyVersion, SigningKeyPair, SigningPublicKey};
let keypair: SigningKeyPair = generate_signing_keypair(SigningKeyVersion::Latest);
use devolutions_crypto::signature::{sign, Signature, SignatureVersion};
let signature: Signature = sign(b"this is some test data", &keypair, SignatureVersion::Latest);
use devolutions_crypto::signature::{sign, Signature, SignatureVersion};
assert!(signature.verify(b"this is some test data", &public_key));
These are a bunch of functions that can be useful when dealing with the library.
This is a method used to generate a random key. In almost all cases, the length
parameter should be 32.
use devolutions_crypto::utils::generate_key;
let key = generate_key(32);
assert_eq!(32, key.len());
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.
use devolutions_crypto::utils::{generate_key, derive_key};
let key = b"this is a secret password";
let salt = generate_key(16);
let iterations = 10000;
let length = 32;
let new_key = derive_key(key, &salt, iterations, length);
assert_eq!(32, new_key.len());
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