Skip to content

Add sr25519 ECIES encrypt/decrypt#2022

Open
hitchho wants to merge 1 commit into
polkadot-js:masterfrom
hitchho:ecies
Open

Add sr25519 ECIES encrypt/decrypt#2022
hitchho wants to merge 1 commit into
polkadot-js:masterfrom
hitchho:ecies

Conversation

@hitchho

@hitchho hitchho commented Mar 13, 2026

Copy link
Copy Markdown

Summary

Adds sr25519Encrypt and sr25519Decrypt to @polkadot/util-crypto, implementing ECIES (Elliptic Curve Integrated Encryption Scheme) over sr25519/Ristretto255.

  • Wire-compatible with schnorrkel's ECIES (Rust) — ciphertext produced by JS can be decrypted in Rust and vice versa
  • Uses ephemeral ECDH on Ristretto255 for key agreement
  • Merlin transcript (STROBE128/Keccak) for key derivation — same KDF as schnorrkel
  • ChaCha20-Poly1305 (RFC 8439) for authenticated encryption
  • Cross-language KDF compatibility verified with test vectors

Wire format

[version: 1 byte] [ephemeral_pk: 32 bytes] [nonce: 12 bytes] [ciphertext + tag: N + 16 bytes]

Total overhead: 61 bytes.

New API

import { sr25519Encrypt, sr25519Decrypt, sr25519PairFromSeed } from '@polkadot/util-crypto';

const alice = sr25519PairFromSeed('0x...');
const bob = sr25519PairFromSeed('0x...');

// Encrypt a message for Bob
const encrypted = sr25519Encrypt('secret message', bob.publicKey, 'my-app-v1');

// Bob decrypts
const decrypted = sr25519Decrypt(encrypted, bob, 'my-app-v1');

Why this matters

This enables wallet extensions to implement a decrypt() method in their signer interface, similar to MetaMask's eth_decrypt. The secret key never leaves the extension — the extension calls sr25519Decrypt internally. This is the missing primitive for end-to-end encrypted messaging in the Polkadot ecosystem.

New dependency

  • @noble/ciphers — for ChaCha20-Poly1305 (from the same @noble family already used in the project)

Changes

  • packages/util-crypto/src/sr25519/encrypt.tssr25519Encrypt()
  • packages/util-crypto/src/sr25519/decrypt.tssr25519Decrypt()
  • packages/util-crypto/src/sr25519/encrypt.spec.ts — 12 tests including cross-compat KDF vector
  • packages/util-crypto/src/sr25519/index.ts — exports
  • packages/util-crypto/package.json@noble/ciphers dependency

Test plan

  • All 12 ECIES tests pass (round-trip, wrong key, wrong context, empty plaintext, truncated, bad version, tampered, nondeterministic, large, string inputs, cross-compat KDF vector)
  • All 57 existing sr25519 tests still pass — zero regressions
  • KDF output verified identical between Rust (schnorrkel) and JS implementations

Wire-compatible with schnorrkel's ECIES (Rust). Uses ephemeral
ECDH on Ristretto255, Merlin transcript KDF, ChaCha20-Poly1305.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant