#encryption #chacha20-poly1305 #protocols #quantum #chacha20 #crystals-kyber

kychacha_crypto

A Post-Quantum Secure Encryption Protocol using chacha20poly1305 and CRYSTALS-kyber

8 stable releases

new 2.0.3 Apr 18, 2025
2.0.2 Apr 16, 2025
1.1.6 Mar 21, 2025
1.1.3 Feb 7, 2025
1.1.2 Jan 31, 2025

#373 in Cryptography

Download history 119/week @ 2025-01-31 126/week @ 2025-02-07 17/week @ 2025-02-14 106/week @ 2025-03-07 20/week @ 2025-03-14 110/week @ 2025-03-21 3/week @ 2025-03-28 305/week @ 2025-04-11

418 downloads per month

MIT license

23KB
214 lines

Kychacha-crypto: Post-Quantum Secure Encryption Protocol

A post-quantum simple to use implementation for Kyber and ChaCha20

CI Security audit CodSpeed Badge Crates.io Version

Hybrid cryptographic implementation using:

  • Crystals-Kyber: Post-Quantum secure Key Encapsulation Mechanism (KEM) for key exchange.
  • ChaCha20-Poly1305: Authenticated symmetric encryption.

Architecture

The following diagram describes the protocol flow between the "Sender" and the "Recipient":

sequenceDiagram
    participant Sender
    participant Recipient

    Recipient-->>Sender: Recipient public key (Kyber pub key 1184 bytes)
    
    Sender->>Sender: Encapsulate secret (Kyber)
    Note right of Sender: Generates ephemeral keypair and derives shared secret
    Sender->>Sender: Derive ChaCha key (HKDF-SHA256)
    Note right of Sender: Uses shared secret to derive symmetric key
    Sender->>Sender: Encrypt message (ChaCha20-Poly1305)
    
    Sender->>Recipient: Send {ciphertext, nonce, encrypted message}
    
    Recipient->>Recipient: Decapsulate secret (Kyber)
    Note right of Recipient: Recovers shared secret
    Recipient->>Recipient: Derive ChaCha key (HKDF-SHA256)
    Note right of Recipient: Derives the same symmetric key
    Recipient->>Recipient: Decrypt message

Note: During the encapsulation process on the sender's side, an ephemeral keypair is generated.

Technical Specifications

1. Key Exchange Protocol

  • Algorithm: Kyber-1024 (NIST PQC Round 3)
  • Key Parameters:
      pub const KYBER_PUBLIC_KEY_BYTES: usize = 1184;
      pub const KYBER_SECRET_KEY_BYTES: usize = 2400;
      pub const KYBER_CIPHERTEXT_BYTES: usize = 1568;
    
  • Key Derivation: HKDF-SHA256 with specific context

2. Symetric Encryption

  • Algorithm: ChaCha20-Poly1305 (IETF variant)
  • Key Size: 256 bits
  • Nonce: 96 bits (randomly generated per message)

3. Encrypted Data Format

The encrypted data is a serialized binary structure containing:

  • Ciphertext: Kyber ciphertext (1568 bytes).
  • Nonce: ChaCha20 nonce (12 bytes).
  • Encrypted Message: Encrypted message with authentication tag.
    #[derive(Serialize, Deserialize, Debug)]
    pub struct EncryptedData {
        #[serde(with = "serde_bytes")]
        pub ciphertext: Vec<u8>,    // Kyber ciphertext (1568 bytes)
        #[serde(with = "serde_bytes")]
        pub nonce: Vec<u8>,         // ChaCha20 nonce (12 bytes)
        #[serde(with = "serde_bytes")]
        pub encrypted_msg: Vec<u8>, // Encrypted message with authentication tag
    }

Basic Usage

Key Generation and encryption

use kychacha_crypto::{generate_keypair, Keypair, decrypt, encrypt, PublicKey};

// Generate a Kyber-1024 keypair
let server_kp: Keypair = generate_keypair()?;

let message = b"Secret message";
// Encrypt the message using the server's public key
let encrypted_data: Vec<u8> = encrypt(&server_kp.public, message)?;

// Receive encrypted_data as &[u8] from the client
let decrypted_message = decrypt(&encrypted_data, &server_kp)?;
assert_eq!(decrypted_message, "Secret message");

Note: The decrypt function assumes the original message is a valid UTF-8 string and returns a String. If the message contains non-UTF-8 binary data, decryption will fail.

Key Serialization (for storage/transmission)

use kychacha_crypto::{public_key_to_bytes, secret_key_to_bytes};

// Convert keys to byte vectors
let pk_bytes: Vec<u8> = public_key_to_bytes(&server_kp.public);
let sk_bytes: Vec<u8> = secret_key_to_bytes(&server_kp.secret);

// Reconstruct keys from bytes
let public_key = PublicKey::from(pk_bytes.as_slice());
let secret_key = SecretKey::from(sk_bytes.as_slice());

Safety Considerations

  1. Randomness: Depends on the secure generator of the system.
  2. HKDF context: Used for protocol binding.
  3. Nonces: Generated with CSPRNG for each message.

Dependencies

~10–19MB
~240K SLoC