Skip to content

x.crypto.chacha20 #

module chacha20

chacha20

Chacha20 (and XChacha20) stream cipher encryption algorithm in pure V. Its mostly based on RFC 8439 and inspired by Go version of the same library.

Status

This module already supports a 32-bit counter mode, and recently expanded to support a 64-bit counter mode. The implemented features at the time of writing (2025/09/22) are:- Support for standard IETF ChaCha20 with 32-bit counter and 12 bytes nonce

  • Support for eXtended ChaCha20 (XChaCha20) constructions with 24 bytes nonce,with 32 or 64-bit counter.- Support for original ChaCha20 with 8 bytes nonce and 64-bit counter.

Example

import crypto.rand
import x.crypto.chacha20

fn main() {
    // 1. Creates a standard IETF variant, supplied with 12-bytes nonce
    key0 := rand.read(32)!
    nonce0 := rand.read(12)!

    mut c0 := chacha20.new_cipher(key0, nonce0)!
    // and then, do work with the c0 that was just created

    // 2. Creates an original (DJ Bernstein) variant, supplied with 8-bytes nonce
    key1 := rand.read(32)!
    nonce1 := rand.read(8)!

    mut c1 := chacha20.new_cipher(key1, nonce1)!
    // do with yours cipher

    // 3. Creates an eXtended ChaCha20 construction with 64-bit counter
    key2 := rand.read(32)!
    nonce2 := rand.read(24)!

    mut c2 := chacha20.new_cipher(key2, nonce2, use_64bit_counter: true)!
    // do with yours cipher
}

Complete example

module main

import encoding.hex
import x.crypto.chacha20

fn main() {
    // example of random key
    // you should make sure the key (and nonce) are random enough.
    // The security guarantees of the ChaCha20 require that the same nonce
    // value is never used twice with the same key.
    key := hex.decode('bf32a829ebf86d23f6a32a74ef0333401e54a6b2900d35bfadef82c5d49da15f')!
    nonce := hex.decode('a7d7cf3405631f25cc1054bd')!

    input := 'Good of gambler'.bytes()

    // encrypt and the decrypt back
    output := chacha20.encrypt(key, nonce, input)!
    input_back := chacha20.decrypt(key, nonce, output)!

    // should true
    assert input == input_back
}

Constants #

const key_size = 32

The size of ChaCha20 key, ie 256 bits size, in bytes

const nonce_size = 12

The size of standard IETF ChaCha20 nonce, ie 96 bits size, in bytes

const x_nonce_size = 24

The size of extended variant of standard ChaCha20 (XChaCha20) nonce, 192 bits

const orig_nonce_size = 8

The size of original ChaCha20 nonce, 64 bits

fn decrypt #

fn decrypt(key []u8, nonce []u8, ciphertext []u8, opt Options) ![]u8

decrypt does reverse of encrypt operation by decrypting ciphertext with ChaCha20 cipher instance with provided key and nonce.

fn encrypt #

fn encrypt(key []u8, nonce []u8, plaintext []u8, opt Options) ![]u8

encrypt encrypts plaintext bytes with ChaCha20 cipher instance with provided key and nonce. It was a thin wrapper around two supported nonce size, ChaCha20 with 96 bits and XChaCha20 with 192 bits nonce. Internally, encrypt start with 0's counter value. If you want more control, use Cipher instance and setup the counter by your self.

fn new_cipher #

fn new_cipher(key []u8, nonce []u8, opt Options) !&Cipher

new_cipher creates a new ChaCha20 stream cipher with the given 32 bytes key and bytes of nonce with supported size, ie, 8, 12 or 24 bytes nonce. Standard IETF variant use 12 bytes nonce's, if you want create original ChaCha20 cipher with support for 64-bit counter, use 8 bytes length nonce's instead If 24 bytes of nonce was provided, the XChaCha20 construction will be used. It returns new ChaCha20 cipher instance or an error if key or nonce have any other length.

type State #

type State = [16]u32

State represents the running 64-bytes of chacha20 stream,

fn (State) clone #

fn (s State) clone() State

clone returns a new copy of this state.

fn (State) reset #

fn (mut s State) reset()

reset resets internal values of this state.

fn (State) qround #

fn (mut ws State) qround(nr int)

qround performs quarter round on the working state ws with round number specified in nr. Its responsibility the user to provide the correct round number.

struct Cipher #

@[noinit]
struct Cipher {
	Stream
mut:
	// internal buffer for storing key stream results
	block []u8 = []u8{len: block_size}
	// The last length of leftover unprocessed keystream from internal buffer
	length int
}

Cipher represents ChaCha20 stream cipher instances.

fn (Cipher) xor_key_stream #

fn (mut c Cipher) xor_key_stream(mut dst []u8, src []u8)

xor_key_stream xors each byte in the given slice in the src with a byte from the cipher's key stream. It fulfills cipher.Stream interface. It encrypts the plaintext message in src and stores the ciphertext result in dst in a key stream fashion. You must never use the same (key, nonce) pair more than once for encryption. This would void any confidentiality guarantees for the messages encrypted with the same nonce and key.

fn (Cipher) encrypt #

fn (mut c Cipher) encrypt(mut dst []u8, src []u8) !

encrypt encrypts src and stores into dst buffer. It works like xor_key_stream except its ignore key streaming process by ignoring remaining key stream in the internal buffer, so, its works in one shot of fashion. Its added to allow chacha20poly1305 modules to work without key stream fashion.

Todo: integrates it with the rest

fn (Cipher) free #

unsafe
fn (mut c Cipher) free()

free the resources taken by the Cipher c. Dont use cipher after .free call

fn (Cipher) reset #

unsafe
fn (mut c Cipher) reset()

reset quickly sets all Cipher's fields to default value. This method will be deprecated.

fn (Cipher) set_counter #

fn (mut c Cipher) set_counter(ctr u64)

set_counter sets Cipher's counter

fn (Cipher) counter #

fn (c Cipher) counter() u64

counter returns a current underlying counter value, as u64.

fn (Cipher) rekey #

fn (mut c Cipher) rekey(key []u8, nonce []u8) !

rekey resets internal Cipher's state and reinitializes state with the provided key and nonce

struct Options #

@[params]
struct Options {
pub mut:
	// currently, used for XChaCha20 construct
	use_64bit_counter bool
}

Configuration options