TentacleID: "Own Your Digital Self."
1. Executive Summary
TentacleID is a browser-based, self-sovereign identity (SSI) wallet implemented as a Chrome Extension (Manifest V3). It allows users to create a decentralized identity (DID), manage Verifiable Credentials (VCs), and authenticate with third-party applications (dApps) using Zero-Knowledge Proofs (ZKPs) and Selective Disclosure.
The core philosophy is "One Identity, Many Reaches." The user maintains custody of their private keys within the extension's encrypted storage, "reaching out" to websites only with the specific data they consent to share.
2. System Architecture
The project follows a modular architecture typical of modern Web3 wallets, split into four distinct contexts:
A. The Core (Cryptographic Engine)
Located in src/core/, this is the agnostic logic layer. It has no DOM dependencies and handles the math.
crypto.js: Wraps the native Web Crypto API. Uses ECDSA P-256 for signing/verification and AES-GCM for local data encryption.did.js: Implements thedid:keymethod. The DID is derived deterministically from the public key multibase, meaning no blockchain anchoring is required (ephemeral/local usage).credentials.js: Implements W3C Verifiable Credentials Data Model 1.1. It handles the creation of credentials and the logic for Selective Disclosure (hashing individual claims with salts).zkproofs.js: A custom implementation of hash-commitment schemes to prove predicates (likeage > 18) without revealing the underlying value.storage.js: An encrypted wrapper around IndexedDB.
B. The Extension (The Bridge)
- Background Service Worker (
background.js): The persistent brain. It manages the wallet state (locked/unlocked), holds the decrypted key in memory (while unlocked), and orchestrates communication between the popup and content scripts. - Content Script (
content.js&injected.js): The injection layer.injected.jsexposes thewindow.tentacleIDAPI to the DOM.content.jsacts as a relay, passing messages from the untrusted DOM to the secure Background context. - Popup (
popup/): The UI. Built with vanilla JS/HTML/CSS. It communicates with the Background worker to retrieve state and approve/deny requests.
C. The Demos (The Consumers)
- Demo Main: Showcases standard login and email sharing.
- Demo Bar (Velvet Lounge): Showcases ZK Age Verification.
3. Deep Dive: Cryptography & Security Model
3.1. Key Management
- Algorithm: ECDSA on the P-256 curve (NIST).
- Storage: Keys are never stored in plaintext.
- When the user creates an identity, a random
saltis generated. - The user's passphrase +
saltruns through PBKDF2 (100,000 iterations, SHA-256) to derive a symmetric encryption key. The private key is encrypted with AES-GCM using this derived key and stored in IndexedDB (
identitystore).Unlocking: When the user enters their password, the key is re-derived. If it successfully decrypts the private key, the wallet is "unlocked" and the raw key is held in the Background Worker's memory variable
encryptionKey.
3.2. Decentralized Identifier (DID)
TentacleID uses the did:key method for simplicity and portability.
- Format:
did:key:z[multibase_encoded_public_key] - Resolution: The DID Document is deterministically generated from the DID string itself. No external resolver or ledger is needed.
3.3. Zero-Knowledge & Selective Disclosure
The application does not use SNARKs (which are heavy). Instead, it uses Hash-Based Commitment Schemes.
How Selective Disclosure Works (credentials.js):
- Issuance: When a credential is created, every claim (e.g., "email", "birthDate") is individually hashed with a unique, cryptographically secure random
salt. Hash(salt + key + value)Presentation: When a website requests specific claims, the wallet creates a derived credential containing only the requested fields.
Verification: The wallet provides the
saltfor the revealed fields. The verifier hashes the provided value with the salt and compares it against the signature in the original credential.
How Age Proof Works (zkproofs.js):
- Commitment: The wallet creates a commitment to the birth timestamp:
Hash(randomness + timestamp). - Blind Proof: To prove
Age >= 21, the wallet calculates the threshold date. It constructs a hash chain including theminAgeand a nonce, proving it possesses the pre-image (the birthdate) that satisfies the conditionbirthDate < thresholdDate, without revealingbirthDate.
4. Data Flow Analysis
Scenario: User Logging into "The Velvet Lounge" (Demo Bar)
- Initialization:
- User opens
index.html. content.jsinjectsinjected.js.injected.jsattacheswindow.tentacleIDand firestentacleid:ready.Request:
User clicks "Verify with TentacleID".
app.jscallswindow.tentacleID.verifyAge(21).Message Chain:
injected.js(postMessage) ->content.js(runtime.sendMessage) ->background.js.Processing:
background.jsreceivesCREDENTIAL_REQUEST.It checks
session.unlocked. If false, creates a pending request and opens the Popup.User Action: User unlocks wallet (if needed) and clicks "Approve" in the Popup.
Proof Generation:
background.jsretrieves the user'sAgeCredentialfrom IndexedDB.It calls
zkproofs.generateAgeRangeProofusing the stored birthdate.It signs the proof payload using the user's private ECDSA key.
Response:
background.jssends the proof back tocontent.js->injected.js.app.jsreceives the promise resolution:{ verified: true, did: "..." }.UI updates to show the "Access Granted" screen.
5. API Reference (window.tentacleID)
Developers integrate with TentacleID using this API.
| Method | Description |
|---|---|
isInstalled() |
Returns { installed: true, version: "..." }. Checks if the extension is active. |
authenticate(options) |
Performs a cryptographic login. Options: { requestedClaims: [], challenge: "..." }. Returns a signed JWT-like proof. |
verifyAge(age, purpose) |
Requests a ZK proof that the user is over the specified age. Returns { verified: bool }. |
verifyEmail(purpose) |
Requests temporary access to the user's email address. Returns { email: string } if granted. |
getDID() |
Returns the user's public DID string. |
createLoginButton(selector) |
Helper to inject a pre-styled login button into the DOM. |
6. Storage Schema (IndexedDB)
The database TentacleID (Version 1) contains four object stores:
identity: Stores the primary user identity.- Key:
primary Data: Encrypted private key + public key.
credentials: Stores VCs issued to the user.Structure:
{ id, type, issuer, data (encrypted), iv }permissions: Tracks which domains have access to what data.Structure:
{ domain, claims, expiresAt }Note: The background worker runs a cleanup job every 60s to remove expired permissions.
settings: Configuration flags (e.g.,encryptionSalt).
7. Passkey Integration (passkeys.js)
TentacleID supports Biometric Unlock via WebAuthn.
- Setup: Uses
navigator.credentials.createwithauthenticatorAttachment: 'platform'. This registers the browser/device (TouchID/Windows Hello) as an authenticator. - Storage: The
rawIdof the credential is stored in IndexedDB. - Unlock: Uses
navigator.credentials.get. Upon success, the extension retrieves a stored "Passkey Identity" blob to restore the session without requiring the master password re-entry.
8. Visual Effects
- Octopus 3D (
octopus-3d.js): A clever CSS-only 3D engine in the popup. It calculates mouse position relative to the center of the container and appliesrotateXandrotateYtransforms to an image stack, creating a parallax/3D effect similar to the MetaMask fox. - Fluid Interfaces: The CSS uses complex
box-shadowlayering andconic-gradientanimations to create "soap bubble" and "liquid" aesthetic effects on buttons and cards.
9. Conclusion
TentacleID is a robust implementation of Self-Sovereign Identity principles. It avoids the pitfall of centralized data silos by keeping all personal data encrypted on the client. It uses standard W3C protocols (did:key, VC Data Model) ensuring theoretical interoperability with other decentralized identity systems. The addition of hash-based selective disclosure and age proofs makes it a privacy-preserving tool for the modern web.
Built With
- chrome-manifest-v3
- css
- extension
- html5
- javascript
- web-crypto-api
Log in or sign up for Devpost to join the conversation.