@enc-protocol/core
v0.9.0
Published
ENC Protocol core — app-agnostic protocol primitives (crypto, events, RBAC, SMT, CT). No app encryption (see @enc-protocol/plugin-*).
Maintainers
Readme
@enc-protocol/core
ENC Protocol core cryptographic and protocol primitives — secp256k1 signatures (Schnorr + ECDSA), hashing, ECDH, AEAD encryption, Merkle trees (SMT + CT), RBAC, events, and session management. Generated from Lean 4 DSL verified specification.
Install
npm install @enc-protocol/coreAPI
Cryptographic Hashing
sha256Hash(data: Uint8Array): Uint8Array— SHA-256 digest of bytes.sha256Str(str: string): Uint8Array— SHA-256 digest of a UTF-8 string.domainHash(prefix: number, data: Uint8Array): Uint8Array— Domain-separated SHA-256 hash with byte prefix.taggedHash(tag: string, data: Uint8Array): Uint8Array— BIP-340 style tagged hash.eip191Hash(msg: string): Uint8Array— Ethereum signed message hash (EIP-191).
Merkle Tree Hashing
ctLeafHash(eventsRoot: Uint8Array, stateHash: Uint8Array): Uint8Array— CT leaf hash (events + state).ctNodeHash(left: Uint8Array, right: Uint8Array): Uint8Array— CT inner node hash.smtLeafHash(key: Uint8Array, value: Uint8Array): Uint8Array— SMT (Poseidon2) leaf hash.smtNodeHash(left: Uint8Array, right: Uint8Array): Uint8Array— SMT (Poseidon2) inner node hash.computeEventsRoot(eventIds: string[]): Uint8Array— Merkle root from event ID hex strings.
Commitment Hashing
computeContentHash(content: string): Uint8Array— SHA-256 of content string.computeCommitHash(contentHash: string, enclave: string, from: string, type: string, exp: number, tags: string[][]): Uint8Array— Commit hash with CBOR encoding.computeEventHash(seq: number, sequencer: string, sig1: string, timestamp: number): Uint8Array— Event hash (sequencer record).computeEnclaveId(from: string, type: string, contentHash: string, tags: string[][]): Uint8Array— Enclave ID from manifest.computeEventId(sig2Hex: string): Uint8Array— Event ID from seq signature.
Key Derivation & Sessions
derivePublicKey(privateKey: Uint8Array): Uint8Array— secp256k1 compressed public key (32 bytes, x-only).derivePublicKeyCompressed(privateKey: Uint8Array): Uint8Array— secp256k1 compressed public key (33 bytes with prefix).derivePublicKeyUncompressed(privateKey: Uint8Array): Uint8Array— secp256k1 uncompressed public key (65 bytes).generateKeypair(): {privateKey: Uint8Array, publicKey: Uint8Array}— Generate random secp256k1 keypair.deriveKey(shared: Uint8Array, label: string): Uint8Array— HKDF-SHA256 with label; returns 32 bytes.generateSession(idPriv: Uint8Array, duration?: number, now: number): {session: string, sessionPriv: Uint8Array, expires: number}— Create BIP-340 delegated session.verifySession(session: string, fromPubHex: string, now: number): string | null— Verify session; returns error string or null if valid.
Signing & Verification (Schnorr)
schnorrSign(msgHash: Uint8Array, privateKey: Uint8Array): Uint8Array— secp256k1 Schnorr (BIP-340) signature.schnorrVerify(msgHash: Uint8Array, signature: Uint8Array, publicKey: Uint8Array): boolean— Verify Schnorr signature.signSTH(t: number, ts: number, rootHash: Uint8Array): string— Sign Signed Tree Head; callsglobalThis.__encSequencerSigner.verifySTH(t: number, ts: number, rootHash: Uint8Array, sigHex: string, seqPub: Uint8Array): boolean— Verify STH signature (requires sequencer public key).
Signing & Verification (ECDSA)
ecdsaSign(msgHash: Uint8Array, privateKey: Uint8Array): Uint8Array— secp256k1 ECDSA signature.ecdsaVerify(msgHash: Uint8Array, signature: Uint8Array, publicKey: Uint8Array): boolean— Verify ECDSA signature.ecdsaVerifyPrehashed(msgHash: Uint8Array, signature: Uint8Array, publicKey: Uint8Array): boolean— Verify pre-hashed ECDSA signature.ecdsaRecover(msgHash: Uint8Array, signature: Uint8Array, recovery: number): Uint8Array— Recover secp256k1 public key from ECDSA sig.
Subkey Certificates
verifySubkeyCert(cert: {subkey: string, parentId: string, expiry: number, ackSig: string, authSig: string}, commitFrom: string, now: number): string | null— Verify subkey cert; returns error string or null if valid.verifyDelegatedSession(sessionPub: Uint8Array, expires: number, authSig: string, recovery: number, fromPubHex: string, now: number): string | null— Verify delegated session authorization.deriveSignerPriv(sessionPriv: Uint8Array, sessionPub: Uint8Array, seqPub: Uint8Array, enclaveId: string): Uint8Array— Derive signer private key from session + sequencer.deriveSignerPub(sessionPub: Uint8Array, seqPub: Uint8Array, enclaveId: string): Uint8Array— Derive signer public key from session + sequencer.
ECDH & Shared Secrets
ecdh(privKey: Uint8Array, pubKey: Uint8Array): Uint8Array— secp256k1 ECDH; returns x-coordinate (32 bytes).
AEAD Encryption (XChaCha20-Poly1305)
encrypt(key: Uint8Array, plaintext: string): string— Encrypt string; returns base64 (nonce + ciphertext).decrypt(key: Uint8Array, ciphertextB64: string): string— Decrypt base64; returns plaintext string.encryptSeparated(key: Uint8Array, plaintext: string): {ciphertext: string, nonce: string}— Encrypt; returns hex nonce + ciphertext.decryptSeparated(key: Uint8Array, ciphertextHex: string, nonceHex: string): string— Decrypt hex ciphertext + nonce.encryptBytes(key: Uint8Array, plaintextBytes: Uint8Array): string— Encrypt bytes; returns hex.decryptBytes(key: Uint8Array, combinedHex: string): Uint8Array— Decrypt hex to bytes.encryptSeparatedBytes(key: Uint8Array, plaintextBytes: Uint8Array): {ciphertext: string, nonce: string}— Encrypt bytes; returns hex nonce + ciphertext.decryptSeparatedBytes(key: Uint8Array, ciphertextHex: string, nonceHex: string): Uint8Array— Decrypt hex bytes.encryptSeparatedWithNonce(key: Uint8Array, plaintext: string, nonce: Uint8Array): {ciphertext: string, nonce: string}— Deterministic encrypt with provided nonce.encryptSeparatedBytesWithNonce(key: Uint8Array, plaintextBytes: Uint8Array, nonce: Uint8Array): {ciphertext: string, nonce: string}— Deterministic byte encrypt.
AEAD Encryption (ChaCha20-Poly1305)
aeadChachaEncrypt(key: Uint8Array, plaintextBytes: Uint8Array): {ciphertext: string, nonce: string}— Encrypt bytes with 12-byte nonce; returns hex.aeadChachaDecrypt(key: Uint8Array, ciphertextHex: string, nonceHex: string): Uint8Array— Decrypt hex ciphertext + 12-byte nonce.
Stream Ciphers & MAC
chacha20Stream(key: Uint8Array, nonce: Uint8Array, data: Uint8Array): Uint8Array— ChaCha20 stream cipher (no auth).hmacSha256(key: Uint8Array, msg: Uint8Array): Uint8Array— HMAC-SHA256.
HKDF
hkdfExtract(ikm: Uint8Array, salt?: Uint8Array): Uint8Array— HKDF extract phase (SHA-256).hkdfExpand(prk: Uint8Array, info: Uint8Array, length: number): Uint8Array— HKDF expand phase.
Encoding & Random
bytesToHex(bytes: Uint8Array): string— Convert bytes to lowercase hex.hexToBytes(hex: string): Uint8Array— Parse hex (with optional 0x prefix) to bytes.base64Encode(bytes: Uint8Array): string— Base64 encode.base64Decode(str: string): Uint8Array— Base64 decode.csprngBytes(n: number): Uint8Array— Generate n random bytes.reduceScalar(bytes: Uint8Array): Uint8Array— Reduce 32 bytes to valid secp256k1 scalar.cborHead(major: number, n: number): Uint8Array— CBOR major type + value encoding.
Events & Commits
mkCommit(enclave: string, from: string, type: string, content: string, exp: number, tags: string[][]): Commit— Create unsigned commit.signCommit(commit: Commit, privateKey: Uint8Array): Commit— Add Schnorr signature to commit.mkManifestCommit(from: string, manifestContent: string, exp: number, tags: string[][]): Commit— Create + hash manifest commit.verifyCommit(commit: Commit, now: number): boolean— Verify commit signature (Schnorr or ECDSA).finalizeCommit(commit: Commit, timestamp: number, seq: number, sequencerPubHex: string): Event— Add sequencer data; callsglobalThis.__encSequencerSigner.verifyEvent(event: Event, now: number): boolean— Verify full event (commit + sequencer sig).mkReceipt(event: Event): Receipt— Extract receipt fields from event.validateCommitStructure(commit: Commit): string | null— Check commit hash matches fields.isACEvent(type: string): boolean— Check if type is AC event (Grant, Revoke, etc.).isLifecycleEvent(type: string): boolean— Check if type is lifecycle (Pause, Resume, Terminate, Migrate).canUpdateDelete(type: string): boolean— Check if type is Update or Delete.
Role-Based Access Control (RBAC)
getState(bitmask: bigint): number— Extract state bits (0–255).setState(bitmask: bigint, stateValue: number): bigint— Set state bits.isOutsider(bitmask: bigint): boolean— State is 0 (no membership).hasTrait(bitmask: bigint, traitBit: number): boolean— Check if trait bit is set.setTrait(bitmask: bigint, traitBit: number): bigint— Set trait bit.clearTrait(bitmask: bigint, traitBit: number): bigint— Clear trait bit.isOwner(mask: bigint): boolean— Check owner trait (bit 8).bestRank(mask: bigint, traitRanks: [number, number][]): number— Find best rank from trait + rank tuples.clearAllTraits(bitmask: bigint): bigint— Keep state, clear all traits.isAuthorized(schema: RBACRule[], bitmask: bigint, eventType: string, op: string, isSelf?: boolean, isSender?: boolean, stateNames?: string[], traitNames?: string[]): boolean— Check if bitmask+role permits operation.getCustomRoleNames(schema: RBACRule[]): string[]— Extract non-context roles from schema.roleBitFromName(name: string, customRoles: string[]): number | null— Get trait bit for custom role.RBACState.getRoles(identity: string): bigint— Get role bitmask for identity.RBACState.setRoles(identity: string, mask: bigint): void— Set role bitmask.RBACState.grantRole(identity: string, bit: number): void— Set trait bit.RBACState.revokeRole(identity: string, bit: number): void— Clear trait bit.RBACState.revokeAll(identity: string): void— Delete all roles.RBACState.markDeleted(eventId: string): void— Mark event as deleted.RBACState.markUpdated(targetId: string, updateId: string): void— Mark as updated by updateId.RBACState.getEventStatus(eventId: string): string— Get status: 'Active' | 'Deleted' | updateId.RBACState.applyInitialState(initialState: object, schema: RBACRule[], stateNames: string[], traitNames: string[]): void— Populate roles from initialState object.
Sparse Merkle Tree (SMT)
buildSMTKey(namespace: number, rawKey: Uint8Array): Uint8Array— Build 21-byte SMT key (1-byte namespace + 20-byte hash).buildRBACKey(identityHex: string): Uint8Array— SMT key for RBAC (namespace 0).buildEventStatusKey(eventIdHex: string): Uint8Array— SMT key for event status (namespace 1).buildKVKey(kvKey: string, identity?: string): Uint8Array— SMT key for KV state (namespace 2).encodeRoleBitmask(bitmask: bigint): Uint8Array— Encode bitmask as 32 bytes.decodeRoleBitmask(bytes: Uint8Array): bigint— Decode 32 bytes to bitmask.encodeEventStatus(status: string): Uint8Array— Encode status string/hex.proofToWire(proof: SMTProof): object— Convert proof to JSON wire format.wireToProof(wire: object): SMTProof— Parse wire format to proof.SparseMerkleTree.verify(proof: SMTProof, expectedRoot: Uint8Array): boolean— Verify 168-bit SMT inclusion proof.SparseMerkleTree.getRoot(): Uint8Array— Get current Merkle root.SparseMerkleTree.getRootHex(): string— Get root as hex.SparseMerkleTree.insert(key: Uint8Array, value: Uint8Array): void— Insert/update leaf.SparseMerkleTree.remove(key: Uint8Array): void— Remove leaf.SparseMerkleTree.get(key: Uint8Array): Uint8Array | null— Fetch leaf value.SparseMerkleTree.prove(key: Uint8Array): SMTProof— Generate inclusion proof.SparseMerkleTree.serialize(): object— Serialize to JSON.SparseMerkleTree.deserialize(data: object): void— Restore from JSON.
Certificate Transparency (CT)
verifyInclusionProof(leafHash: Uint8Array, leafIndex: number, treeSize: number, path: Uint8Array[], expectedRoot: Uint8Array): boolean— Verify RFC 9162 inclusion proof.verifyConsistencyProof(size1: number, size2: number, path: Uint8Array[], firstRoot: Uint8Array, secondRoot: Uint8Array): boolean— Verify RFC 9162 consistency proof.bundleMembershipProof(eventIds: string[], eventIndex: number): {ei: number, s: string[]}— Generate bundle proof (binary tree siblings in hex).verifyBundleMembership(eventIdHex: string, proof: {ei: number, s: string[]}, expectedRootHex: string): boolean— Verify event in bundle.CTTree.getRoot(): Uint8Array— Get current tree root.CTTree.getRootHex(): string— Get root as hex.CTTree.append(eventsRoot: Uint8Array, stateHash: Uint8Array): number— Add leaf; returns index.CTTree.inclusionProof(leafIndex: number): {ts: number, li: number, p: string[]}— Generate inclusion proof.CTTree.consistencyProof(size1: number, size2?: number): {ts1: number, ts2: number, p: string[]}— Generate consistency proof.CTTree.serialize(): string[]— Serialize to hex strings.CTTree.deserialize(data: string[]): void— Restore from hex strings.
Type Constants
Context— Object with context role values:Self,Sender,Public.Op— Enum of CRUD operations:C,R,U,D,P,Nand negations_C,_R,_U,_D,_P,_N.ACEventType— Array of AC event type strings:Manifest,Grant,Revoke,Move,Transfer,Gate,Shared,Own,AC_Bundle,Pause,Resume,Terminate,Migrate.EventStatus— Enum:Active,Deleted,Updated.SMTNamespace— Enum:RBAC(0),EventStatus(1),KVState(2).LifecycleState— Enum:Active,Paused,Terminated,Migrating.
Constants
DOMAIN_CT_LEAF,DOMAIN_CT_NODE,DOMAIN_COMMIT,DOMAIN_EVENT,DOMAIN_ENCLAVE,DOMAIN_SMT_LEAF,DOMAIN_SMT_NODE— Domain separators for hashing.SMT_EMPTY_HASH— Pre-computed empty leaf hash (SMT root for no entries).MAX_EXP_WINDOW,CLOCK_SKEW_TOLERANCE— Event timing constants (milliseconds).
Additional Modules (via subpath exports)
@enc-protocol/core/dm-ratchet.js— Double-ratchet message key derivation + NIP-44 compatibility (padding, HMAC).@enc-protocol/core/ecdh-envelope.js— One-shot sender→recipient confidentiality for personal notices and group invites.@enc-protocol/core/identity-aead.js— Deterministic identity-based AEAD for personal private content.@enc-protocol/core/mls-lazy.js— Binary ratchet tree group crypto (O(log N) epoch distribution).
Example
import {
generateKeypair,
schnorrSign,
schnorrVerify,
sha256Hash,
bytesToHex,
hexToBytes,
mkCommit,
signCommit,
verifyCommit,
deriveKey,
encrypt,
decrypt,
SparseMerkleTree,
buildRBACKey,
RBACState,
encodeRoleBitmask,
CTTree
} from '@enc-protocol/core'
// Generate a keypair
const { privateKey, publicKey } = generateKeypair()
// Create and sign a commit
const commit = mkCommit(
'enclave123',
bytesToHex(publicKey),
'Manifest',
'some policy content',
Math.floor(Date.now() / 1000) + 3600,
[['env', 'prod']]
)
const signed = signCommit(commit, privateKey)
// Verify the signature
const now = Math.floor(Date.now() / 1000)
const isValid = verifyCommit(signed, now)
console.log('Commit valid:', isValid)
// Derive an encryption key and encrypt
const shared = new Uint8Array(32)
crypto.getRandomValues(shared)
const key = deriveKey(shared, 'my-label')
const encrypted = encrypt(key, 'secret message')
const decrypted = decrypt(key, encrypted)
console.log('Decrypted:', decrypted)
// Use Sparse Merkle Tree for RBAC state
const smt = new SparseMerkleTree()
const identityKey = buildRBACKey(bytesToHex(publicKey))
const roleMask = encodeRoleBitmask(0x0103n) // state=3, owner bit set
smt.insert(identityKey, roleMask)
const proof = smt.prove(identityKey)
const isIncluded = SparseMerkleTree.verify(proof, smt.getRoot())
console.log('SMT proof verified:', isIncluded)
// Build a Certificate Transparency tree
const ct = new CTTree()
const eventsRoot = sha256Hash(new TextEncoder().encode('events'))
const stateHash = sha256Hash(new TextEncoder().encode('state'))
const leafIndex = ct.append(eventsRoot, stateHash)
console.log('CT leaf index:', leafIndex)
console.log('CT root:', ct.getRootHex())