@haskou/pigeon-swarm-crypto
v2.0.0
Published
Cryptographic primitives, key handling, and wire-format compatibility for Pigeon Swarm.
Maintainers
Readme
@haskou/pigeon-swarm-crypto
Cryptographic operations and serialized key formats used by Pigeon Swarm clients
and relay nodes. The package owns signing, encryption, password-based key
protection and digest computation. Generic value types remain in
@haskou/value-objects.
Installation
yarn add @haskou/pigeon-swarm-crypto @haskou/value-objects@7Import public APIs from the package root. Internal adapters are implementation details and are not exported.
Import the MLS lifecycle API from @haskou/pigeon-swarm-crypto/mls. That entry
point provides separate ESM and bundled CommonJS builds for browser and Node.js
consumers. Import UserRootKey from the package root so every component uses
the same runtime constructor.
The package currently publishes CommonJS JavaScript and TypeScript declarations.
It requires Node.js 20.20.2 or newer. Browser applications need a bundler with
CommonJS and buffer support, and a secure context providing Web Crypto. Validate
the resulting browser bundle; successful Node.js tests alone do not establish
browser compatibility.
Signing and asymmetric encryption
import { KeyPair } from '@haskou/pigeon-swarm-crypto';
const recipient = await KeyPair.generate();
const signature = recipient.sign('message');
const verified = recipient.isValidSignature('message', signature);
const encrypted = recipient.encrypt('confidential message');
const plaintext = recipient.decrypt(encrypted).toString('utf8');Keys use Ed25519 for signatures. Asymmetric encryption converts the recipient key to X25519 and combines ephemeral key agreement, HKDF-SHA-256 and AES-256-GCM. Applications must authenticate the recipient's public key and define a canonical signed message. Encryption to a public key does not authenticate the sender.
Symmetric encryption
import { SymmetricKey } from '@haskou/pigeon-swarm-crypto';
const key = SymmetricKey.generate();
const encrypted = key.encrypt('confidential message');
const plaintext = key.decrypt(encrypted).toString('utf8');encrypt generates a fresh nonce and returns a versioned envelope. Keep the key
separate from the ciphertext. When supplying application-specific authenticated
data through aad, supply exactly the same bytes during decryption. Do not invent
a new envelope or rename its version fields without a compatibility plan.
Protecting a private key
import {
EncryptedPrivateKey,
PrivateKey,
} from '@haskou/pigeon-swarm-crypto';
async function protectPrivateKey(privateKey: PrivateKey, password: string) {
return EncryptedPrivateKey.create(privateKey, password);
}
async function unlockPrivateKey(serialized: string, password: string) {
return new EncryptedPrivateKey(serialized).decrypt(password);
}New protected keys use the v3 scrypt envelope. Existing supported envelopes remain
readable. needsReEncryption() identifies older formats; callers decide when to
replace stored data after a successful unlock. This package does not enforce an
application password policy or provide account recovery.
Public API
| Responsibility | Exports |
| --- | --- |
| Keys and signatures | Key, PrivateKey, PublicKey, KeyPair, Signature |
| Protected keys | EncryptedPrivateKey, EncryptedKeyPair, CryptoPassword |
| Encrypted envelopes | EncryptedPayload, AsymmetricEncryptedPayload, SymmetricEncryptedPayload, EncryptedPayloadScheme |
| Symmetric encryption | SymmetricKey, SymmetricKeyCryptOptions, SymmetricKeyDerivationOptions |
| Digest computation | MD5Hash, SHA256Hash, SHA512Hash, HashPayload |
| Public errors | InvalidKeyError, InvalidSignatureError, InvalidEncryptedPrivateKeyFormatError |
Digest classes expose .from(payload) for computation. The corresponding classes
in value-objects validate an already-computed digest. With version 7, equality
requires the same concrete type; use hasValue only when comparing the underlying
digest across representations intentionally. MD5 is retained for compatibility,
not for security-sensitive integrity or password storage.
Compatibility and security
See wire formats and migration before changing imports or persisted data, and SECURITY.md for security boundaries and reporting. See the key lifecycle review for current rotation gaps, the distinction between password re-encryption and key replacement, and the next integration milestone.
Moving encryption into this package does not provide forward secrecy, metadata privacy, anonymous communication or deletion of data already replicated through IPFS.
Private groups can use the MLS key lifecycle for
independent device keys, epoch rotation, device removal, authenticated daily
recipient HPKE schedules and root-key-protected persistence. The application
must still authorize each control transition and atomically adopt its verified
checkpoint with the matching MLS epoch and state commitment. Persist each
one-use join package's packageId in rollback-resistant trusted state;
restoration requires that exact checkpoint, and successful creation or joining
must replace it with a consumed tombstone atomically with the new session.
Development
yarn install --frozen-lockfile
yarn lint
yarn test:coverage
yarn buildThe test suite includes interoperability with the former UI and backend package versions, using their actual implementations. See CONTRIBUTING.md for validation and release requirements.
License
MIT. See LICENSE.txt.
Private-protocol signature verification is documented in Private operation signatures, including required application authorization and metadata-handling limits.
Private scope initialization
See private genesis signatures for the versioned, pinned-owner verification boundary shared by Node and browser clients. It does not change authorization of existing replicated data.
Subsequent private control signatures must satisfy the previous policy's quorum and sequencer before MLS processing.
