Documentation Index
Fetch the complete documentation index at: https://mintlify.com/bluesky-social/atproto/llms.txt
Use this file to discover all available pages before exploring further.
Overview
The @atproto/crypto package provides cryptographic primitives for the AT Protocol, including key generation, signing, verification, and encoding utilities. It supports both P-256 (secp256r1) and K-256 (secp256k1) elliptic curve cryptography.
Installation
npm install @atproto/crypto
Supported Cryptographic Systems
The package implements two elliptic curve systems:
- P-256 (ES256): NIST P-256, aka secp256r1, aka prime256v1
- K-256 (ES256K): NIST K-256, aka secp256k1
Both use SHA-256 hashing and produce “low-S” signatures as specified in the AT Protocol cryptography specification.
Keypair Classes
P256Keypair
P-256 elliptic curve keypair for ES256 signatures.
import { P256Keypair } from '@atproto/crypto'
// Generate a new random keypair
const keypair = await P256Keypair.create({ exportable: true })
// Get the public key as a did:key
const didKey = keypair.did()
console.log(didKey) // 'did:key:zDna...'
// Sign data
const data = new Uint8Array([1, 2, 3, 4, 5])
const signature = await keypair.sign(data)
// Export private key (only if exportable: true)
const privateKey = await keypair.export()
// Import from existing private key
const imported = await P256Keypair.import(privateKey, { exportable: true })
static async create(opts?: Partial<P256KeypairOptions>): Promise<P256Keypair>
Creates a new random P-256 keypair.Whether the private key can be exported. Set to true if you need to call export().
static async import(
privKey: Uint8Array | string,
opts?: Partial<P256KeypairOptions>
): Promise<P256Keypair>
Imports a keypair from an existing private key.privKey
Uint8Array | string
required
Private key bytes or hex string
Whether the imported key can be re-exported
Instance Methods:
Returns the public key formatted as a did:key identifier.
async sign(msg: Uint8Array): Promise<Uint8Array>
Signs a message. The message is hashed with SHA-256 before signing.
Returns a 64-byte compact signature (not DER-encoded).
publicKeyBytes(): Uint8Array
Returns the raw public key bytes (compressed format).
publicKeyStr(encoding?: SupportedEncodings): string
Returns the public key as an encoded string.encoding
SupportedEncodings
default:"base64pad"
Encoding format: ‘base64pad’, ‘base64’, ‘hex’, etc.
async export(): Promise<Uint8Array>
Exports the private key bytes. Throws if the keypair was not created with exportable: true.
Properties:
jwtAlg: string - Always 'ES256' for P-256 keypairs
Secp256k1Keypair
K-256 elliptic curve keypair for ES256K signatures.
import { Secp256k1Keypair } from '@atproto/crypto'
// Generate a new random keypair
const keypair = await Secp256k1Keypair.create({ exportable: true })
// Get the public key as a did:key
const didKey = keypair.did()
console.log(didKey) // 'did:key:zQ3s...'
// Sign data
const data = new Uint8Array([1, 2, 3, 4, 5])
const signature = await keypair.sign(data)
// Export and import
const privateKey = await keypair.export()
const imported = await Secp256k1Keypair.import(privateKey)
The API is identical to P256Keypair, but uses K-256 cryptography.
Secp256k1Keypair.create
Promise<Secp256k1Keypair>
static async create(opts?: Partial<Secp256k1KeypairOptions>): Promise<Secp256k1Keypair>
Creates a new random K-256 keypair.
Secp256k1Keypair.import
Promise<Secp256k1Keypair>
static async import(
privKey: Uint8Array | string,
opts?: Partial<Secp256k1KeypairOptions>
): Promise<Secp256k1Keypair>
Imports a keypair from an existing private key.
Properties:
jwtAlg: string - Always 'ES256K' for K-256 keypairs
Signature Verification
verifySignature
Verifies a signature using a public key in did:key format.
import { verifySignature } from '@atproto/crypto'
const didKey = 'did:key:zQ3s...'
const data = new Uint8Array([1, 2, 3, 4, 5])
const signature = new Uint8Array([...])
const isValid = await verifySignature(didKey, data, signature)
if (isValid) {
console.log('Signature is valid')
} else {
console.log('Signature is invalid')
}
function verifySignature(
didKey: string,
data: Uint8Array,
sig: Uint8Array,
opts?: VerifyOptions & { jwtAlg?: string }
): Promise<boolean>
Verifies a signature against a public key.Public key in did:key format
Message bytes that were signed
Signature bytes to verify
Expected JWT algorithm (‘ES256’ or ‘ES256K’). If provided, verification fails if the key type doesn’t match.
Whether to allow non-canonical (high-S) signatures
verifySignatureUtf8
Convenience function for verifying UTF-8 string signatures.
import { verifySignatureUtf8 } from '@atproto/crypto'
const didKey = 'did:key:zQ3s...'
const message = 'Hello, world!'
const sigBase64Url = 'eW91ci1zaWduYXR1cmUtaGVyZQ'
const isValid = await verifySignatureUtf8(didKey, message, sigBase64Url)
function verifySignatureUtf8(
didKey: string,
data: string,
sig: string,
opts?: VerifyOptions
): Promise<boolean>
Verifies a signature with UTF-8 string message and base64url-encoded signature.
DID Key Utilities
Formats a public key as a did:key identifier.
import { formatDidKey, P256_JWT_ALG } from '@atproto/crypto'
const publicKeyBytes = new Uint8Array([...])
const didKey = formatDidKey(P256_JWT_ALG, publicKeyBytes)
console.log(didKey) // 'did:key:zDna...'
function formatDidKey(jwtAlg: string, keyBytes: Uint8Array): `did:key:${string}`
JWT algorithm: ‘ES256’ or ‘ES256K’
Uncompressed public key bytes
parseDidKey
Parses a did:key identifier to extract key type and bytes.
import { parseDidKey } from '@atproto/crypto'
const parsed = parseDidKey('did:key:zQ3s...')
console.log(parsed.jwtAlg) // 'ES256K'
console.log(parsed.keyBytes) // Uint8Array(...)
function parseDidKey(did: string): ParsedMultikey
type ParsedMultikey = {
jwtAlg: string
keyBytes: Uint8Array
}
Extracts the algorithm and key bytes from a did:key identifier.
Multibase Utilities
multibaseToBytes
Decodes a multibase-encoded string to bytes.
import { multibaseToBytes } from '@atproto/crypto'
const multibase = 'zQ3shVRtgqTRHC7...'
const bytes = multibaseToBytes(multibase)
function multibaseToBytes(mb: string): Uint8Array
Decodes a multibase string (base58btc with ‘z’ prefix) to bytes.
bytesToMultibase
Encodes bytes as a multibase string.
import { bytesToMultibase } from '@atproto/crypto'
const bytes = new Uint8Array([...])
const multibase = bytesToMultibase(bytes)
function bytesToMultibase(bytes: Uint8Array): string
Encodes bytes as multibase (base58btc with ‘z’ prefix).
Hashing Utilities
sha256
Computes SHA-256 hash.
import { sha256 } from '@atproto/crypto'
const data = new Uint8Array([1, 2, 3, 4, 5])
const hash = await sha256(data)
function sha256(data: Uint8Array): Promise<Uint8Array>
Computes SHA-256 hash of the input data.
sha256Hex
Computes SHA-256 hash as hex string.
import { sha256Hex } from '@atproto/crypto'
const data = new Uint8Array([1, 2, 3, 4, 5])
const hashHex = await sha256Hex(data)
console.log(hashHex) // '74f81fe167d9...'
Random Utilities
randomBytes
Generates cryptographically secure random bytes.
import { randomBytes } from '@atproto/crypto'
const random = randomBytes(32)
function randomBytes(length: number): Uint8Array
Generates secure random bytes of the specified length.
Constants
import {
P256_JWT_ALG, // 'ES256'
SECP256K1_JWT_ALG, // 'ES256K'
DID_KEY_PREFIX, // 'did:key:'
BASE58_MULTIBASE_PREFIX, // 'z'
P256_DID_PREFIX,
SECP256K1_DID_PREFIX
} from '@atproto/crypto'
TypeScript Interfaces
Keypair
interface Keypair extends Signer, Didable {
jwtAlg: string
sign(msg: Uint8Array): Promise<Uint8Array>
did(): string
}
ExportableKeypair
interface ExportableKeypair extends Keypair {
export(): Promise<Uint8Array>
}
VerifyOptions
type VerifyOptions = {
allowMalleableSig?: boolean
}
Complete Example
import {
Secp256k1Keypair,
P256Keypair,
verifySignature
} from '@atproto/crypto'
// Generate keypairs
const k256Key = await Secp256k1Keypair.create({ exportable: true })
const p256Key = await P256Keypair.create({ exportable: true })
const message = new Uint8Array([1, 2, 3, 4, 5])
// Sign with K-256
const k256Sig = await k256Key.sign(message)
const k256Did = k256Key.did()
console.log('K-256 DID:', k256Did)
// Sign with P-256
const p256Sig = await p256Key.sign(message)
const p256Did = p256Key.did()
console.log('P-256 DID:', p256Did)
// Verify signatures
const k256Valid = await verifySignature(k256Did, message, k256Sig)
const p256Valid = await verifySignature(p256Did, message, p256Sig)
console.log('K-256 valid:', k256Valid) // true
console.log('P-256 valid:', p256Valid) // true
// Export and re-import keys
const k256Private = await k256Key.export()
const k256Restored = await Secp256k1Keypair.import(k256Private)
// Verify signature with restored key
const restoredSig = await k256Restored.sign(message)
const restoredValid = await verifySignature(
k256Restored.did(),
message,
restoredSig
)
console.log('Restored key works:', restoredValid) // true
Additional Resources