# @jscrypto/chacha20
[![CI](https://github.com/emn178/jscrypto-chacha20/actions/workflows/ci.yml/badge.svg)](https://github.com/emn178/jscrypto-chacha20/actions/workflows/ci.yml)
[![Coverage Status](https://coveralls.io/repos/emn178/jscrypto-chacha20/badge.svg?branch=main)](https://coveralls.io/r/emn178/jscrypto-chacha20?branch=main)
[![NPM](https://img.shields.io/npm/v/@jscrypto/chacha20)](https://www.npmjs.com/package/@jscrypto/chacha20)
[![CDNJS](https://img.shields.io/jsdelivr/npm/hm/@jscrypto/chacha20)](https://www.jsdelivr.com/package/npm/@jscrypto/chacha20)

## Migration Notice

`@jscrypto/chacha20` is a legacy standalone package. It remains installable with `@jscrypto/core` up to `0.9.1`, but new integrations should use the component package listed below.

Starting with `@jscrypto` `0.9.0`, the ChaCha20 family components moved into the new component architecture:

- Node / bundlers: use [`@jscrypto/ciphers`](https://github.com/emn178/jscrypto/tree/main/packages/ciphers)
- Browser bundle: use `@jscrypto/ciphers/chacha20/browser`
- Registry preset: use `chacha20Preset` from `@jscrypto/ciphers/chacha20`

This package now declares `@jscrypto/core` as `>=0.7.0 <=0.9.1` for compatibility with transition releases. Prefer the new `@jscrypto/ciphers` component layout for `0.9.x` projects.

ChaCha20 family stream cipher and AEAD components for [`@jscrypto/core`](https://www.npmjs.com/package/@jscrypto/core).

This package provides:

- `ChaCha20` and `XChaCha20` stream ciphers (IETF / extended-nonce layouts)
- `ChaCha20-Poly1305` and `XChaCha20-Poly1305` AEAD constructions
- `chacha20Preset` for registry registration

Poly1305 is a MAC, not a mode. This package does **not** support arbitrary `cipher + mac` composition such as `createCipher({ cipher: 'ChaCha20', mac: 'Poly1305' })`. Use the named AEAD constructions instead.

Use ChaCha20-Poly1305 or XChaCha20-Poly1305 for authenticated encryption. Raw ChaCha20 only encrypts bytes and does not detect tampering.

Never reuse a nonce with the same key for ChaCha20-Poly1305 or XChaCha20-Poly1305.

Algorithm primitives come from [`@noble/ciphers`](https://www.npmjs.com/package/@noble/ciphers).

## Supported Components

| Cipher | Type | Nonce | Authentication | Notes |
| --- | --- | --- | --- | --- |
| `ChaCha20` | Stream cipher | 12 bytes | No | IETF ChaCha20. `counter` defaults to `0`. |
| `XChaCha20` | Stream cipher | 24 bytes | No | Extended-nonce ChaCha20. `counter` defaults to `0`. |
| `ChaCha20-Poly1305` | AEAD | 12 bytes | 16-byte tag | Returns ciphertext with the tag appended. |
| `XChaCha20-Poly1305` | AEAD | 24 bytes | 16-byte tag | Extended-nonce AEAD. Returns ciphertext with the tag appended. |

## Demo
[ChaCha20 Encrypt Online](https://emn178.github.io/online-tools/chacha20/encrypt/)  
[ChaCha20 Decrypt Online](https://emn178.github.io/online-tools/chacha20/decrypt/)  
[ChaCha20-Poly1305 Encrypt Online](https://emn178.github.io/online-tools/chacha20-poly1305/encrypt/)  
[ChaCha20-Poly1305 Decrypt Online](https://emn178.github.io/online-tools/chacha20-poly1305/decrypt/)

## Install

```sh
npm install @jscrypto/chacha20 @jscrypto/core
```

Optional classic registry for composition tests and apps already using classic:

```sh
npm install @jscrypto/classic
```

## Quick Start

```ts
import { registry } from '@jscrypto/classic';
import { chacha20Preset } from '@jscrypto/chacha20';

registry.use(chacha20Preset);

const cipher = registry.createCipher({
  cipher: 'ChaCha20-Poly1305',
  key,
});

const sealed = cipher.encrypt(plaintext, {
  nonce, // 12 bytes
  aad,
});

const opened = cipher.decrypt(sealed, {
  nonce,
  aad,
});
```

Encryption returns ciphertext with a 16-byte authentication tag appended. Decryption also accepts a detached tag:

```ts
const opened = cipher.decrypt(ciphertext, {
  nonce,
  aad,
  tag, // 16 bytes
});
```

AEAD transforms accept chunked input, but `ChaCha20-Poly1305` and `XChaCha20-Poly1305` currently output from `finalize()`. Raw `ChaCha20` and `XChaCha20` stream ciphers output from `process()`.

## Stream Ciphers

Raw stream ciphers encrypt and decrypt with the same operation. They output bytes from `process()` and can be used for true chunked streaming.

```ts
const chacha20 = registry.createCipher({
  cipher: 'ChaCha20',
  key,
  nonce, // 12 bytes
});

const ciphertext = chacha20.encrypt(plaintext);
const decrypted = chacha20.decrypt(ciphertext);

const xchacha20 = registry.createCipher({
  cipher: 'XChaCha20',
  key,
  nonce: xnonce, // 24 bytes
});
```

## AEAD Ciphers

AEAD ciphers encrypt and authenticate data. They do not use `mode` or `padding`.

```ts
const chacha20Poly1305 = registry.createCipher({
  cipher: 'ChaCha20-Poly1305',
  key,
});

const sealed = chacha20Poly1305.encrypt(plaintext, {
  nonce, // 12 bytes
  aad,
});

const opened = chacha20Poly1305.decrypt(sealed, {
  nonce,
  aad,
});

const xchacha20Poly1305 = registry.createCipher({
  cipher: 'XChaCha20-Poly1305',
  key,
});

const xsealed = xchacha20Poly1305.encrypt(plaintext, {
  nonce: xnonce, // 24 bytes
  aad,
});
```

`XChaCha20` and `XChaCha20-Poly1305` use a 24-byte nonce. Internally the construction derives a subkey with HChaCha20 from the first 16 nonce bytes, then runs IETF ChaCha20 with a 12-byte nonce formed as 4 zero bytes followed by the last 8 nonce bytes.

## Browser

Browser builds depend on `@jscrypto/core`. Load core first, then this package:

```html
<script src="node_modules/@jscrypto/core/dist/jscrypto-core.iife.min.js"></script>
<script src="node_modules/@jscrypto/chacha20/dist/jscrypto-chacha20.iife.min.js"></script>
<script>
  const registry = jscryptoCore.createRegistry();
  registry.use(jscryptoChacha20.chacha20Preset);
</script>
```

Package export paths:

- `@jscrypto/chacha20/browser`
- `@jscrypto/chacha20/umd`

## License

MIT
