Crate vitaminc_encrypt

Crate vitaminc_encrypt 

Source
Expand description

§Vitamin C Encrypt

Crates.io Workflow Status

Secure, flexible and fast encryption for Rust types.

This crate is part of the Vitamin C framework to make cryptography code healthy.

§Features

  • Type-safe encryption: Encrypt and decrypt Rust types with compile-time safety
  • AES-256-GCM: Uses AES-256-GCM authenticated encryption via AWS-LC
  • 256-bit keys only: Enforces quantum-resistant key sizes for future security
  • Protected types integration: Works seamlessly with vitaminc-protected for sensitive data handling
  • Additional Authenticated Data (AAD): Support for authenticated but unencrypted data
  • Automatic nonce generation: Secure random nonce generation for each encryption operation

§Installation

Add this to your Cargo.toml:

[dependencies]
vitaminc-encrypt = "0.1.0-pre4"
vitaminc-random = "0.1.0-pre4"  # For key generation

§Quick Start

use vitaminc_encrypt::Key;
use vitaminc_random::{SafeRand, SeedableRng, Generatable};

// Generate a key
let mut rng = SafeRand::from_entropy();
let key = Key::random(&mut rng).expect("Failed to generate key");

// Encrypt a message
let ciphertext = vitaminc_encrypt::encrypt(&key, "secret message").expect("Failed to encrypt");

// Decrypt it back
let plaintext: String = vitaminc_encrypt::decrypt(&key, ciphertext).expect("Failed to decrypt");
assert_eq!(plaintext, "secret message");

§Usage

§Key Management

The Key type represents a 256-bit encryption key. Vitamin C only supports 256-bit keys to ensure quantum security and compatibility with AWS-LC.

§Generating a Key
use vitaminc_encrypt::Key;
use vitaminc_random::{SafeRand, SeedableRng, Generatable};

let mut rng = SafeRand::from_entropy();
let key = Key::random(&mut rng).expect("Failed to generate key");
§Creating a Key from Bytes
use vitaminc_encrypt::Key;

let key_bytes = [0u8; 32];  // In practice, use securely generated bytes
let key = Key::from(key_bytes);

§Encrypting Data

The encrypt function can encrypt any type that implements the Encrypt trait. Built-in support includes:

  • String
  • &str
  • Vec<u8>
  • [u8; N] (fixed-size byte arrays)
  • Protected<T> where T implements Encrypt
use vitaminc_encrypt::{encrypt, Key};
use vitaminc_random::{SafeRand, SeedableRng, Generatable};

let key = Key::random(&mut SafeRand::from_entropy()).expect("Failed to generate key");

// Encrypt a string
let ciphertext = encrypt(&key, "secret message").expect("encryption failed");

// Encrypt bytes
let data = vec![1, 2, 3, 4, 5];
let ciphertext = encrypt(&key, data).expect("encryption failed");

// Encrypt a fixed-size array
let array = [0u8; 32];
let ciphertext = encrypt(&key, array).expect("encryption failed");

§Decrypting Data

The decrypt function requires you to specify the expected type:


// Decrypt to String
let ciphertext = encrypt(&key, "secret message").expect("encryption failed");
let plaintext: String = decrypt(&key, ciphertext).expect("decryption failed");

// Decrypt to Vec<u8>
let ciphertext = encrypt(&key, vec![1, 2, 3, 4, 5]).expect("encryption failed");
let bytes: Vec<u8> = decrypt(&key, ciphertext).expect("decryption failed");

// Decrypt to fixed-size array
let ciphertext = encrypt(&key, [0u8; 32]).expect("encryption failed");
let array: [u8; 32] = decrypt(&key, ciphertext).expect("decryption failed");

§Additional Authenticated Data (AAD)

AAD allows you to authenticate additional context alongside the ciphertext without encrypting it. This is useful for binding metadata to encrypted data.

use vitaminc_encrypt::{encrypt_with_aad, decrypt_with_aad, Key};

let key = Key::from([0u8; 32]);

// Encrypt with context
let user_id = "user_123";
let ciphertext = encrypt_with_aad(&key, "secret message", user_id).expect("encryption failed");

// Decrypt with the same context
let plaintext: String = decrypt_with_aad(&key, ciphertext, user_id).expect("decryption failed");
assert_eq!(plaintext, "secret message");
§AAD Must Match

Decryption will fail if the AAD doesn’t match:

use vitaminc_encrypt::{encrypt_with_aad, decrypt_with_aad};

// Encrypt with one context
let ciphertext = encrypt_with_aad(&key, "secret", "context_1").expect("encryption failed");

// Try to decrypt with different context - this will fail!
let result: Result<String, _> = decrypt_with_aad(&key, ciphertext, "context_2");
assert!(result.is_err());

§Working with Protected Types

Vitamin C Encrypt integrates with vitaminc-protected to ensure sensitive data is handled securely:

use vitaminc_protected::Protected;
use vitaminc_encrypt::{encrypt, decrypt, Key};
use vitaminc_random::{SafeRand, SeedableRng, Generatable};

let key = Key::random(&mut SafeRand::from_entropy()).expect("Failed to generate key");

// Encrypt protected data
let sensitive = Protected::new("password123".to_string());
let ciphertext = encrypt(&key, sensitive).expect("encryption failed");

// Decrypt back to protected data
let decrypted: Protected<String> = decrypt(&key, ciphertext).expect("decryption failed");

§Encrypting Keys (Key Wrapping)

Keys can be encrypted with other keys, enabling key hierarchy and key wrapping:

use vitaminc_encrypt::{Key, encrypt, decrypt};
use vitaminc_random::{SafeRand, SeedableRng, Generatable};

let mut rng = SafeRand::from_entropy();

// Generate a key encryption key (KEK)
let kek = Key::random(&mut rng).expect("key generation failed");

// Generate a data encryption key (DEK)
let dek = Key::random(&mut rng).expect("key generation failed");

// Wrap the DEK with the KEK
let wrapped_dek = encrypt(&kek, dek).expect("encryption failed");

// Later, unwrap the DEK
let unwrapped_dek: Key = decrypt(&kek, wrapped_dek).expect("decryption failed");

§Convenience Functions vs Traits

This crate provides both convenience functions and traits:

Convenience functions (recommended for most use cases):

Traits (for custom implementations):

  • Encrypt - Implement to make your types encryptable
  • Decrypt - Implement to make your types decryptable
  • Cipher - Implement to create custom cipher algorithms

Example using traits directly:

use vitaminc_encrypt::{Encrypt, Decrypt, Aes256Cipher, Key};

let key = Key::from([0u8; 32]);
let cipher = Aes256Cipher::new(&key).expect("cipher creation failed");

let ciphertext = "secret".encrypt(&cipher).expect("encryption failed");
let plaintext: String = String::decrypt(ciphertext, &cipher).expect("decryption failed");

§Custom Encryptable Types

You can implement Encrypt and Decrypt for your own types to enable selective field encryption:

use vitaminc_encrypt::{Encrypt, Decrypt, Cipher, IntoAad, Unspecified, LocalCipherText};

struct User {
    id: u64,
    email: String,
    ssn: String,
}

struct EncryptedUser {
    id: u64,
    email: String,
    ssn: LocalCipherText,  // Only encrypt the SSN
}

impl Encrypt for User {
    type Encrypted = EncryptedUser;

    fn encrypt_with_aad<'a, C, A>(
        self,
        cipher: &C,
        aad: A,
    ) -> Result<Self::Encrypted, Unspecified>
    where
        C: Cipher,
        A: IntoAad<'a>,
    {
        Ok(EncryptedUser {
            id: self.id,
            email: self.email,
            ssn: self.ssn.encrypt_with_aad(cipher, aad).expect("encryption failed"),
        })
    }
}

impl Decrypt for User {
    type Encrypted = EncryptedUser;

    fn decrypt_with_aad<'a, C, A>(
        encrypted: Self::Encrypted,
        cipher: &C,
        aad: A,
    ) -> Result<Self, Unspecified>
    where
        C: Cipher,
        A: IntoAad<'a>,
    {
        Ok(User {
            id: encrypted.id,
            email: encrypted.email,
            ssn: String::decrypt_with_aad(encrypted.ssn, cipher, aad).expect("decryption failed"),
        })
    }
}

§Security Features

§AES-256-GCM

This crate uses AES-256-GCM (Galois/Counter Mode) which provides:

  • Confidentiality: Data is encrypted with AES-256
  • Authenticity: Built-in authentication prevents tampering
  • Performance: Hardware-accelerated on most modern CPUs

§AWS-LC

Encryption is powered by AWS-LC, a FIPS-validated cryptographic library maintained by Amazon Web Services.

§256-bit Keys Only

Vitamin C enforces 256-bit keys to ensure:

  • Resistance to quantum computer attacks (via AES-256)
  • Compatibility with AWS-LC’s recommended parameters
  • No risk of accidentally using weaker key sizes

§Automatic Nonce Generation

Each encryption operation generates a unique random nonce, preventing nonce reuse which could compromise security.

§Unspecified Errors

Encryption and decryption operations return an Unspecified error type that reveals no information about failures. This prevents side-channel attacks that could leak information through error messages.

§Best Practices

  1. Generate keys securely: Always use Key::random() with a cryptographically secure RNG
  2. Protect keys in memory: The Key type uses Protected internally to zeroize keys when dropped
  3. Use AAD for context: Include relevant context (user ID, record ID, etc.) as AAD to prevent ciphertext substitution
  4. Never reuse keys across environments: Use different keys for development, staging, and production
  5. Rotate keys regularly: Implement key rotation policies for long-lived systems
  6. Store keys securely: Use key management systems (KMS) for production deployments

§Performance

Vitamin C Encrypt is designed for both security and performance:

  • Zero-copy operations where possible
  • Hardware acceleration via AWS-LC’s AES-NI support
  • Minimal allocations during encryption/decryption

§Error Handling

All encryption operations return Result<T, Unspecified> where Unspecified is an opaque error type that reveals no details about the failure. This is intentional to prevent side-channel attacks.

use vitaminc_encrypt::{Key, encrypt, Unspecified};
use vitaminc_random::{SafeRand, SeedableRng, Generatable};

let key = Key::random(&mut SafeRand::from_entropy()).expect("Failed to generate key");

match encrypt(&key, "message") {
    Ok(ciphertext) => println!("Encrypted successfully"),
    Err(Unspecified) => eprintln!("Encryption failed"),
}

§CipherStash

Vitamin C is brought to you by the team at CipherStash.

License: MIT

Structs§

Aad
Aes256Cipher
Implements the AES-256-GCM cipher using the aws-lc-rs library.
EncryptedKey
Key
256-bit key type for use with symmetric encryption algorithms like AES-256-GCM. Vitaminc does not support smaller key sizes to ensure quantum security and compatibility with AWS-LC.
LocalCipherText
Nonce
Represents a nonce used in AEAD encryption of N bytes length.
Unspecified
An error that provides no information about the failure. It is crucial when returning an error from a cipher operation that does not reveal any details about the failure as this can lead to side channel attacks.

Traits§

Cipher
Decrypt
Encrypt
IntoAad

Functions§

decrypt
Decrypt the given ciphertext using the provided key. Any type that implements the Decrypt trait can be used so long as the value was encrypted with the same key.
decrypt_with_aad
Decrypt the given ciphertext using the provided key and additional authenticated data (AAD). This is the reversed operation of encrypt_with_aad.
encrypt
Encrypt the given plaintext using the provided key. Any type that implements the Encrypt trait can be used.
encrypt_with_aad
Encrypt the given plaintext using the provided key and additional authenticated data (AAD).