Seal Crypto Wrapper
This library is a high-level Rust wrapper for the underlying cryptographic library seal-crypto. Its core goal is to provide a safer and more user-friendly API by tightly binding algorithm information with the keys themselves, reducing common cryptographic misuse.
A Chinese version of this document is available here (中文).
Core Design Philosophy
When using a low-level crypto library directly, developers must manage keys and their corresponding algorithms separately. This can lead to misuse, such as using a key generated for AES-128-GCM with an AES-256-GCM cipher. seal-crypto-wrapper addresses this issue with the following design:
- Typed Keys: Provides dedicated key types for each cryptographic primitive (e.g., aead encryption, signatures,
KEM), such as
TypedAeadKey and TypedSignatureKeyPair.
- Algorithm Binding: Every typed key is mandatorily bound to the specific algorithm information used to create it (e.g.,
AeadAlgorithm::Aes128Gcm).
- Runtime Safety Checks: Before performing any cryptographic operation (like encryption or signing), the library automatically checks if the algorithm bound to the provided key matches the current operation's algorithm instance. If they don't match, the operation returns an error, preventing key misuse.
- Convenient Serialization: Key structs can be directly serialized and deserialized using
serde, simplifying storage and transmission. Upon deserialization, the algorithm information is automatically restored without extra steps.
- Unified Builder API: Offers a fluent, chainable API to select and construct the required algorithm instances.
Feature List
This library wraps the core functionalities of seal-crypto, including:
- Aead Ciphers:
- AES-GCM (128, 256-bit)
- ChaCha20Poly1305
- XChaCha20Poly1305
- Asymmetric Cryptography:
- Key Encapsulation Mechanism (KEM): Kyber (512, 768, 1024)
- Digital Signatures: Dilithium (L2, L3, L5), Ed25519, ECDSA P256
- Key Agreement: ECDH P256
- Key Derivation Functions (KDF):
- Key-based: HKDF (SHA-256, SHA-384, SHA-512)
- Password-based: PBKDF2, Argon2
- eXtendable-Output Functions (XOF):
- Hash Functions and HMAC:
- SHA-256, SHA-384, SHA-512
Installation
Add the following to your Cargo.toml file:
[dependencies]
seal-crypto-wrapper = "0.1.2"
Usage Examples
Here are some examples for common use cases.
Aead Encryption
use seal_crypto_wrapper::prelude::*;
use seal_crypto_wrapper::error::Result;
fn main() -> Result<()> {
let algorithm = AeadAlgorithm::build().aes256_gcm();
let cipher = algorithm.into_wrapper();
let key = cipher.generate_typed_key()?;
let nonce = vec![0u8; cipher.nonce_size()];
let aad = b"Authenticated but not encrypted data.";
let plaintext = b"This is a secret message.";
let ciphertext = cipher.encrypt(&key, &nonce, plaintext, Some(aad))?;
let decrypted_plaintext = cipher.decrypt(&key, &nonce, Some(aad), &ciphertext)?;
assert_eq!(plaintext.as_ref(), decrypted_plaintext.as_slice());
println!("Aead encryption/decryption successful!");
Ok(())
}
Digital Signatures
use seal_crypto_wrapper::prelude::*;
use seal_crypto_wrapper::error::Result;
fn main() -> Result<()> {
let algorithm = AsymmetricAlgorithm::build().signature().ed25519();
let signature_scheme = algorithm.into_wrapper();
let key_pair = signature_scheme.generate_keypair()?;
let (public_key, private_key) = key_pair.into_keypair();
let message = b"This is a message to be signed.";
let signature = signature_scheme.sign(message, &private_key)?;
signature_scheme.verify(message, &public_key, signature)?;
println!("Signature verification successful!");
Ok(())
}
Key Encapsulation Mechanism (KEM)
use seal_crypto_wrapper::prelude::*;
use seal_crypto_wrapper::error::Result;
fn main() -> Result<()> {
let algorithm = AsymmetricAlgorithm::build().kem().kyber512();
let kem = algorithm.into_wrapper();
let key_pair = kem.generate_keypair()?;
let (public_key, private_key) = key_pair.into_keypair();
let (shared_secret_1, ciphertext) = kem.encapsulate_key(&public_key)?;
let shared_secret_2 = kem.decapsulate_key(&private_key, &ciphertext)?;
assert_eq!(shared_secret_1, shared_secret_2);
println!("KEM successful, shared secrets match!");
Ok(())
}
Key Agreement
use seal_crypto_wrapper::prelude::*;
use seal_crypto_wrapper::error::Result;
fn main() -> Result<()> {
let algorithm = AsymmetricAlgorithm::build().key_agreement().ecdh_p256();
let key_agreement = algorithm.into_wrapper();
let key_pair_1 = key_agreement.generate_keypair()?;
let (public_key_1, private_key_1) = key_pair_1.into_keypair();
let key_pair_2 = key_agreement.generate_keypair()?;
let (public_key_2, private_key_2) = key_pair_2.into_keypair();
let shared_secret_1 = key_agreement.agree(&private_key_1, &public_key_2)?;
let shared_secret_2 = key_agreement.agree(&private_key_2, &public_key_1)?;
assert_eq!(shared_secret_1, shared_secret_2);
println!("Key agreement successful, shared secrets match!");
Ok(())
}
eXtendable-Output Function (XOF)
use seal_crypto_wrapper::prelude::*;
use seal_crypto_wrapper::error::Result;
fn main() -> Result<()> {
let algorithm = XofAlgorithm::build().shake128();
let xof = algorithm.into_wrapper();
let ikm = b"input keying material";
let salt = b"some salt";
let info = b"some info";
let mut reader = xof.reader(ikm, Some(salt), Some(info))?;
let mut output1 = [0u8; 32];
reader.read(&mut output1);
let mut output2 = [0u8; 64];
reader.read(&mut output2);
assert_ne!(&output1[..], &output2[..32]);
println!("XOF successful, outputs are different as expected!");
Ok(())
}
Key-based Key Derivation (KDF)
use seal_crypto_wrapper::prelude::*;
use seal_crypto_wrapper::error::Result;
fn main() -> Result<()> {
let algorithm = KdfAlgorithm::build().key().hkdf_sha256();
let kdf = algorithm.into_wrapper();
let ikm = b"input keying material";
let salt = b"some salt";
let info1 = b"info for key 1";
let info2 = b"info for key 2";
let key1 = kdf.derive(ikm, Some(salt), Some(info1), 32)?;
let key2 = kdf.derive(ikm, Some(salt), Some(info2), 32)?;
assert_ne!(key1, key2);
println!("Key-based KDF successful, derived keys are different!");
Ok(())
}
Password-based Key Derivation (PBKDF)
use seal_crypto_wrapper::prelude::*;
use seal_crypto_wrapper::error::Result;
fn main() -> Result<()> {
let algorithm = KdfAlgorithm::build().passwd().pbkdf2_sha256_default();
let kdf = algorithm.into_wrapper();
let password = SecretBox::new(Box::from(b"my-secret-password".as_slice()));
let salt1 = b"some salt";
let salt2 = b"another salt";
let key1 = kdf.derive(&password, salt1, 32)?;
let key2 = kdf.derive(&password, salt2, 32)?;
assert_ne!(key1, key2);
println!("Password-based KDF successful, derived keys are different!");
Ok(())
}
Hashing and HMAC
use seal_crypto_wrapper::prelude::*;
use seal_crypto_wrapper::error::Result;
fn main() -> Result<()> {
let algorithm = HashAlgorithm::build().sha256();
let hasher = algorithm.into_wrapper();
let data = b"some data to hash";
let digest = hasher.hash(data);
assert_eq!(digest.len(), 32);
let key = b"my secret key";
let message = b"a message to authenticate";
let hmac_tag = hasher.hmac(key, message)?;
let expected_tag = hasher.hmac(key, message)?;
assert_eq!(hmac_tag, expected_tag);
println!("Hashing and HMAC successful!");
Ok(())
}