Skip to content

Zero-Key Roundtrip Failure in Sealed Event Encryption #104

@ryanleecode

Description

@ryanleecode

Summary

When using an all-zero 32-byte key for both sealEvent() and unsealEvent(), the seal operation succeeds but unseal fails. This affects both Ed25519 private keys and symmetric channel keys.

Expected Behavior

Any 32-byte value should roundtrip successfully:

  • Ed25519 internally clamps keys (masks bits, handles edge cases)
  • Zero is a valid scalar on the curve (produces identity point as public key)
  • Symmetric keys have no mathematical constraints

Actual Behavior

Counterexample from PBT:

{
  channelKey: Uint8Array(32).fill(0),  // all zeros
  senderPrivateKey: Uint8Array(32).fill(0)  // all zeros
}

Flow:

  1. sealEvent(params)Right({ data: Uint8Array, messageId: string }) ✅ succeeds
  2. unsealEvent(data, channelKey)Left(SealedEventError) ❌ fails

Reproduction

import { sealEvent, unsealEvent } from './sealed-event.fn.js'
import { SealedEventParams } from './sealed-event.schema.js'

const zeroKey = new Uint8Array(32)  // all zeros
const params = new SealedEventParams({
  content: 'test',
  channelKey: zeroKey,
  senderPrivateKey: zeroKey,
})

const sealed = sealEvent(params)
// sealed is Right

const unsealed = unsealEvent(sealed.right.data, zeroKey)
// unsealed is Left - decryption/signature verification fails

Code Path

sealEvent:

const ed25519Key = BctsEd25519PrivateKey.from(senderPrivateKey)  // zero key
const derivedPublicKey = ed25519Key.publicKey().toData()  // identity point
const privKey = SigningPrivateKey.newEd25519(ed25519Key)
const signed = envelope.sign(privKey)  // succeeds
const salted = signed.addSalt()
const symmetricKey = SymmetricKey.fromData(channelKey)  // zero key
const encrypted = salted.encrypt(symmetricKey)  // succeeds

unsealEvent:

const encrypted = EnvelopeDecoder.tryFromCborData(data)
const symmetricKey = SymmetricKey.fromData(channelKey)  // same zero key
const decrypted = encrypted.decrypt(symmetricKey)  // likely fails here
const unwrapped = decrypted.unwrap()  // or fails here on signature

Hypothesis

Either:

  1. SymmetricKey decryption with zero key fails (AES/ChaCha edge case)
  2. Signature verification fails because zero-derived public key (identity point) doesn't verify signatures correctly

The library may have assertions or checks that reject zero keys internally, even though they're mathematically valid.

Environment

  • Library: @bcts/components (Blockchain Commons Gordian Envelope)
  • Ed25519 + X25519 + ECIES encryption
  • Symmetric encryption (likely AES-256-GCM or ChaCha20-Poly1305)

Suggested Fix

Either:

  1. Accept zero keys and handle edge case correctly (preferred)
  2. Reject zero keys explicitly at from() with clear error message (documented limitation)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions