Skip to main content

Encryption & Key Management

Every cryptographic operation in the Specter data protocol happens client-side. The chain stores commitments, nullifiers, and encrypted key fragments. It never sees plaintext data, unencrypted private keys, or user passphrases. This page documents every cryptographic primitive used in the protocol, how they compose, and why each was chosen.

Cryptographic Primitives

PrimitiveAlgorithmUseSecurity Level
Commitment hashingPoseidon (BN254)Merkle tree commitments, nullifier derivation, accessTag computation~127-bit (BN254 field)
Zero-knowledge proofsGroth16 on BN254Proof of commitment knowledge + Merkle membership128-bit (BN254 pairing)
Symmetric encryptionAES-256-GCMPassphrase protection of PNG metadata, voucher encryption256-bit
Password-based KDFPBKDF2-SHA256Passphrase to AES key derivationConfigurable (iterations)
Seed-based KDFHKDF-SHA256Seed to commitment secrets derivation256-bit (SHA-256)
Split-key encryptionXOR (one-time pad)Private key splitting for Phantom IdentityInformation-theoretic
Quantum commitmentkeccak256Post-quantum commitment rotation~128-bit post-quantum
Key bindingkeccak256keyId derivation (commitment + issuer binding)256-bit
NFC encryptionAES-128 (NTAG 424 DNA)On-chip tag encryption128-bit
NFC authenticationAES-CMAC (SUN)Tag authenticity verification128-bit

AES-256-GCM: Symmetric Encryption

AES-256-GCM is used wherever data needs to be encrypted at rest with a known key: passphrase-protected PNG metadata and encrypted voucher payloads.

Parameters

ParameterValueNotes
Key size256 bitsDerived via PBKDF2 (passphrase) or HKDF (seed)
IV size96 bits (12 bytes)Random, unique per encryption
Auth tag size128 bitsAppended to ciphertext
ModeGCM (Galois/Counter Mode)Authenticated encryption (confidentiality + integrity)
ImplementationBrowser WebCrypto APIcrypto.subtle.encrypt({name: "AES-GCM", ...})

Why GCM?

GCM provides authenticated encryption — it protects both confidentiality (the data is unreadable without the key) and integrity (any modification of the ciphertext is detected). This prevents an attacker from tampering with encrypted PNG metadata without detection. A non-authenticated mode like AES-CTR would encrypt the data but would not detect if an attacker flipped bits in the ciphertext.

Security Properties

PropertyGuarantee
IND-CCA2Ciphertext is indistinguishable from random data under adaptive chosen-ciphertext attack.
IntegrityAny modification to the ciphertext (including truncation) is detected via the auth tag.
IV uniquenessRandom 96-bit IV. Probability of collision is negligible for practical usage volumes (<232< 2^{32} encryptions per key).
IV Reuse

AES-GCM is catastrophically broken if the same IV is used twice with the same key. The protocol generates a fresh random IV for every encryption operation and stores it alongside the ciphertext. Never reuse an IV.

PBKDF2-SHA256: Password-Based Key Derivation

PBKDF2 transforms a user-chosen passphrase into a 256-bit AES key suitable for encryption. It is used exclusively for Phantom Identity passphrase protection.

Parameters

ParameterValue
Hash functionSHA-256
Iterations100,000
Salt16 bytes (random, stored in PNG metadata)
Output length256 bits (32 bytes)
ImplementationBrowser WebCrypto API (crypto.subtle.deriveKey)

Why 100,000 Iterations?

The iteration count is a trade-off between user experience (derivation time) and brute-force resistance. At 100,000 iterations:

Attacker CapabilityTime to Crack (40-bit passphrase entropy)Time to Crack (80-bit passphrase entropy)
Single CPU core~35 years~101210^{12} years
GPU cluster (10,000 cores)~1.3 days~10810^{8} years
ASIC farmHours~10610^{6} years

For high-value identities, users should choose passphrases with at least 80 bits of entropy (approximately 6+ random words from a large dictionary). The iteration count provides a speed bump, not a wall — passphrase strength is the primary defense.

Salt Purpose

The random salt ensures that identical passphrases produce different AES keys. Without a salt, an attacker could precompute a rainbow table mapping common passphrases to AES keys. With a 16-byte random salt, each PNG has a unique derivation path, forcing the attacker to brute-force each PNG independently.

HKDF-SHA256: Seed-Based Key Derivation

HKDF (HMAC-based Key Derivation Function) deterministically derives multiple independent secrets from a single 128-bit seed. It is the core derivation function for Phantom Keys.

Derivation Table

OutputHKDF Info StringOutput SizeReduction
secretghostcoin-secret-v132 bytesmod BN254 field prime
nullifierSecretghostcoin-nullifier-v132 bytesmod BN254 field prime
blindingghostcoin-blinding-v132 bytesmod BN254 field prime
changeBlindingghostcoin-change-blinding-v132 bytesmod BN254 field prime
quantumSecretghostcoin-quantum-v132 bytesNone (raw 256-bit value)

Why HKDF?

HKDF is a standardized KDF (RFC 5869) with formal security proofs. Each derivation uses a unique info string, which acts as a domain separator — the outputs for different info strings are cryptographically independent even from the same seed. This means compromising secret reveals nothing about nullifierSecret, and vice versa.

BN254 Field Reduction

HKDF outputs 32 bytes (256 bits), but BN254 field elements must be less than the scalar field prime (r2254r \approx 2^{254}). The 256-bit output is reduced modulo rr. Because the output is approximately uniformly distributed over 256 bits, the bias introduced by modular reduction is negligible (<2252< 2^{-252}).

Split-Key XOR Architecture

The split-key architecture protects the secp256k1 private key in Phantom Identity by splitting it across two storage locations.

Construction

encKeyPartA = random(256 bits)         // stored in PNG
encKeyPartB = privateKey XOR encKeyPartA // stored on-chain

Recovery

privateKey = encKeyPartA XOR encKeyPartB

Security Analysis

PropertyGuarantee
Information-theoretic securityWhen encKeyPartA is uniformly random, encKeyPartB is also uniformly random regardless of privateKey. Neither part alone leaks any information.
No computational assumptionsXOR security does not depend on the hardness of any mathematical problem. It is unconditionally secure for the split — no quantum computer or future algorithm can break it.
Both parts requiredRecovery requires both parts. An attacker with only one part has zero bits of information about the private key.

The XOR split is the simplest possible secret sharing scheme. It is a degenerate case of a 2-of-2 secret sharing scheme where the shares are exactly the same size as the secret. The simplicity eliminates implementation risk — there are no parameters to misconfigure, no threshold to set, and no reconstruction algorithm beyond a single XOR.

Quantum-Resistant keccak256 Commitments

The quantum commitment layer provides defense against a future adversary with a quantum computer capable of breaking the BN254 pairing (and thus forging Groth16 proofs).

Mechanism

StepOperationWhen
InitialquantumCommitment0=keccak256(quantumSecret0)\text{quantumCommitment}_0 = \text{keccak256}(\text{quantumSecret}_0)Identity creation (seal)
Access nnVerify: keccak256(quantumSecretn)==quantumCommitmentn\text{keccak256}(\text{quantumSecret}_n) == \text{quantumCommitment}_nEach connect
RotationquantumCommitmentn+1=keccak256(quantumSecretn+1)\text{quantumCommitment}_{n+1} = \text{keccak256}(\text{quantumSecret}_{n+1})After verification

Why keccak256?

keccak256 (SHA-3 family) is a hash function based on the sponge construction. Its preimage resistance relies on the security of the Keccak permutation, which is unrelated to the discrete logarithm problem or elliptic curve pairings. Grover's algorithm provides at most a quadratic speedup for hash preimage search, reducing 256-bit security to approximately 128-bit equivalent — still computationally infeasible.

ThreatBN254 (Groth16)keccak256
Classical computerSecure (~128 bits)Secure (~256 bits)
Quantum computer (Shor)Broken (polynomial time for discrete log)Not applicable (no algebraic structure)
Quantum computer (Grover)N/AReduced to ~128 bits (still secure)

Rotation Benefits

The rotation ensures that each quantum secret is single-use. After a successful access:

  1. The previous quantumSecret is no longer valid (the commitment has been updated).
  2. Only the current holder knows the next valid preimage (they chose newQuantumCommitment).
  3. An attacker who records all on-chain quantum commitments learns nothing — they are all keccak256 outputs, and finding the preimage requires breaking keccak256.

Key Binding: keyId Derivation

The keyId that addresses entries in the PersistentKeyVault is derived as:

keyId=keccak256(commitment,  issuerAddress)\text{keyId} = \text{keccak256}(\text{commitment},\; \text{issuerAddress})

Why Include issuerAddress?

Including the issuer's Ethereum address in the key ID prevents front-running attacks during the seal phase:

AttackWithout issuerAddressWith issuerAddress
Attacker sees commitment in mempoolAttacker races to store their own encKeyPartB at keyId = keccak256(commitment)Attacker cannot match the keyId because keccak256(commitment, attackerAddress) != keccak256(commitment, issuerAddress)
ResultAttacker's key part B is stored; real user's is rejectedAttacker's entry is at a different keyId; no collision

The issuer address is the msg.sender of the storeKeyPartB transaction. The vault enforces that the caller's address matches the one used in the keyId derivation. This binds the key storage to the identity creator and prevents anyone else from claiming the same key slot.

Client-Side Execution Model

All cryptographic operations that handle plaintext data or private keys execute in the user's browser or local environment. The protocol's on-chain components never see:

Never On-ChainWhere It Lives
Plaintext dataClient memory only
Private keys (secp256k1)Client memory during session, split-encrypted at rest
Commitment secrets (secret, nullifierSecret, blinding)PNG metadata (optionally encrypted) or derived from numeric key seed
PassphrasesClient memory during decryption, never stored
quantumSecret (current preimage)Client memory during access, PNG metadata at rest
Groth16 witnessClient memory during proof generation

The chain stores:

  • Commitments: Poseidon hashes (opaque 254-bit values).
  • Nullifiers: Poseidon hashes (opaque, unlinkable to commitments).
  • Access tags: Poseidon hashes (opaque, session-scoped).
  • Encrypted key parts: encKeyPartB (useless without the corresponding encKeyPartA).
  • Quantum commitments: keccak256 outputs (opaque without preimage).
  • Proofs: Groth16 proof data (πa\pi_a, πb\pi_b, πc\pi_c) — verifiable but zero-knowledge.

This architecture means that even a fully compromised chain — validators, RPC nodes, block explorers, everything — reveals nothing about users' data. The on-chain state is a collection of opaque hashes, encrypted fragments, and zero-knowledge proofs. The plaintext exists only in the client's memory, for only as long as the client needs it.