THIS CODE HAS NOT UNDERGONE A SECURITY AUDIT AND SHOULD NOT BE USED IN PRODUCTION ENVIRONMENTS.
This project is an experimental proof-of-concept for WebAuthn-based UCAN delegations. It has not been reviewed by security professionals and may contain critical vulnerabilities. Use at your own risk for testing and educational purposes only.
While Web Workers provide some isolation from the main thread, they are not a secure execution environment and should not be relied upon for protecting sensitive cryptographic material.
Any malicious code injected into your application (via XSS, supply chain attacks, browser extensions, or compromised dependencies) can:
- Read Worker Memory: Access the worker's global scope and extract secrets
- Intercept Messages: Monitor
postMessage()communication between main thread and worker - Monkey-Patch APIs: Override
crypto.subtle,postMessage, or other worker APIs to exfiltrate keys - Timing Attacks: Measure execution time to infer information about private keys
Example Attack:
// Malicious code injected into main thread or worker
const originalPostMessage = Worker.prototype.postMessage;
Worker.prototype.postMessage = function(msg) {
console.log('Intercepted:', msg); // Exfiltrate secrets
sendToAttacker(msg);
return originalPostMessage.apply(this, arguments);
};The encrypted Ed25519 archive is stored in localStorage, which is accessible to:
- Any JavaScript running in the same origin
- Browser extensions with appropriate permissions
Even with encryption, an attacker who can inject code can:
- Wait for the decryption key to be used
- Intercept the decrypted archive during operation
- Capture the PRF seed when WebAuthn authentication occurs
An attacker with sufficient privileges (e.g., via browser vulnerability, malicious extension) could:
- Dump worker memory to extract private keys
- Access the AES-GCM encryption key while it's in use
- Read plaintext secrets during cryptographic operations
Dependencies used in workers (@ucanto/principal, @noble/hashes, etc.) could be compromised:
- Malicious package updates
- Typosquatting attacks
- Compromised maintainer accounts
For more information on Web Worker security limitations:
The most secure architecture for this application would be to exclusively use hardware-backed WebAuthn keys (P-256 or Ed25519), where:
โ
Private keys never leave hardware
โ
Signing operations performed in secure enclaves (TPM, Secure Element, Trusted Execution Environment)
โ
No key material exposed to JavaScript
โ
Biometric authentication required for each operation
โ
No encrypted keystores in localStorage
| Feature | Current (Ed25519 + Worker) | Ideal (WebAuthn Hardware Keys) |
|---|---|---|
| Private Key Exposure | โ Exists in worker memory | โ Never leaves hardware |
| Attack Surface | โ Large (JS injection, worker compromise) | โ Minimal (hardware isolation) |
| Storage Security | โ Encrypted in localStorage | โ No storage needed |
| Signature Operations | โ In JavaScript/WASM | โ In secure hardware |
| Key Extraction | โ Possible with code injection | โ Cryptographically impossible |
| Biometric Gate | โ One-time (for PRF seed) | โ Per-operation |
Current Implementation (Insecure):
WebAuthn (P-256) โ PRF Seed โ Worker AES Key โ Decrypt Ed25519 Key โ Sign in JS
โ โ
Hardware Secure Software (Vulnerable)
Ideal Implementation (If Possible):
WebAuthn (P-256 or Ed25519) โ Sign UCAN โ Done
โ
Hardware Secure (End-to-End)
However, this ideal implementation is not possible with current web standards (see next section).
While P-256 signature verification support has been explored (see experimental fork), adding P-256 to ucanto/Storacha does not solve the core WebAuthn signature format problem:
- โ Still requires raw signatures (not WebAuthn-wrapped)
- โ Does not enable hardware-backed WebAuthn signing
- โ Application must still use software-based key generation
- โ Worker security vulnerability remains
Reality Check: P-256 and Ed25519 are both affected equally by the WebAuthn signature format incompatibility. Using P-256 instead of Ed25519 would not improve security - both would still require software-based keys in web workers.
WebAuthn supports both P-256 (ES256) and Ed25519 (EdDSA) algorithms, but neither can be used for UCAN signing due to the WebAuthn signature format.
Note: Ed25519 (EdDSA) support in WebAuthn was added in the WebAuthn Level 3 specification finalized in 2025. Browser and hardware support is still rolling out. However, even with Ed25519 support, the WebAuthn signature format limitation remains - you still cannot produce raw Ed25519 signatures for UCAN signing.
According to the W3C WebAuthn Level 3 Specification, WebAuthn doesn't sign arbitrary data directly. Instead, it creates signatures over a specific structure (ยง6.5.5):
signature = sign(authenticatorData || sha256(clientDataJSON))
Where clientDataJSON wraps your data (ยง6.5):
{
"type": "webauthn.get",
"challenge": "base64url(yourData)",
"origin": "https://your-domain.com",
"crossOrigin": false
}This is fundamentally incompatible with what UCAN requires:
signature = sign(rawUcanPayloadBytes)
This limitation applies equally to P-256 AND Ed25519 keys in WebAuthn - the problem is not the cryptographic algorithm, but the signature format specification.
To achieve true hardware-backed UCAN signing, we would need:
- Raw signature capability (not WebAuthn-wrapped) - Missing
- Hardware-backed keys (TPM/Secure Enclave) - โ Available via WebAuthn
- Both simultaneously โ This is what WebAuthn doesn't provide today
WebAuthn provides #2 but not #1. You can have hardware security OR raw UCAN-compatible signatures, but not both with current web standards.
This limitation applies equally to:
- โ WebAuthn P-256 keys
- โ WebAuthn Ed25519 keys
- โ WebCrypto API P-256 keys (not hardware-backed)
- โ WebCrypto API Ed25519 keys (not hardware-backed)
The Ed25519-in-worker approach is a pragmatic compromise given the constraints:
- WebAuthn Limitation: Neither P-256 nor Ed25519 keys in WebAuthn can produce raw signatures for UCAN
- UCAN Ecosystem: Expects standard signature formats (raw ECDSA/EdDSA), not WebAuthn-wrapped assertions
- Storacha Requirement: Only accepts Ed25519 signatures
- Browser-Only Goal: Must work entirely in browser without native applications
- Immediate Functionality: Requires working signatures today, not in 2-5 years
This architectural limitation is not a design flaw but rather the reality of current web cryptography standards.
The signature format limitation is defined in:
- WebAuthn Level 3 ยง 6.5.5 - Generating an Authentication Assertion
- WebAuthn Level 3 ยง 6.5 - CollectedClientData Structure
Key points from the specification:
"Let signature be the assertion signature of the concatenation
authenticatorData || hash..."
Where hash = SHA-256(UTF-8 encoding of clientDataJSON), and clientDataJSON is origin-bound.
This means:
- โ Signatures cannot be verified independently of the web origin
- โ Signatures include ceremony type (
webauthn.getvswebauthn.create) - โ Data is double-hashed (challenge โ clientDataJSON โ SHA-256 โ signed)
- โ No "raw signing mode" exists in WebAuthn Level 3
True hardware-backed UCAN signing would require one of these approaches:
- WebAuthn Extension for raw signing (not proposed for Level 3+)
- New Web Standard for hardware-backed cryptographic operations
- Platform-specific APIs (Apple CryptoKit, Windows CNG) outside the browser
- Native applications with direct hardware access (defeats browser-only goal)
- Custom UCAN Verifier accepting WebAuthn signature format (breaks ecosystem compatibility)
Timeline: Any web standard solution is likely 2-5 years away, if it happens at all.
Workaround Used: Generate Ed25519 keys in software (web worker), derive encryption key from WebAuthn PRF, encrypt and store keys. This provides WebAuthn-gated access to Ed25519 keys but does not provide hardware-backed key security.
While the WebAuthn signature format limitation cannot be solved with current standards, there is a long-term solution that eliminates the web worker security vulnerability entirely: Distributed Key Generation (DKG) with threshold cryptography.
Instead of storing a complete Ed25519 key on a single device (vulnerable to extraction), split the key across multiple devices:
Device 1 (Browser) Device 2 (Mobile)
โ โ
Key Share 1 Key Share 2
(hardware-backed) (hardware-backed)
โ โ
โโโโโโโโโโโฌโโโโโโโโโโโโโโ
โ
Combined Signature
(BOTH devices required)
Key Security Benefits:
- โ No complete key on any device - compromising one device doesn't expose the private key
- โ Multi-device authentication - requires biometric approval on both devices for signing
- โ Hardware-backed shares - each key share protected by WebAuthn on its device
- โ No key material in JavaScript - only combined during threshold signing protocol
- โ Eliminates worker vulnerability - no complete key exists in web worker memory
Attack Mitigation:
| Attack Vector | Current (Ed25519 in Worker) | DKG (Multi-Device) |
|---|---|---|
| Code Injection | โ Can steal complete key | โ Only gets one share (useless alone) |
| Worker Compromise | โ Full key exposed | โ Incomplete key share |
| Lost/Stolen Device | โ Full key compromised | โ Requires both devices |
| Memory Dumping | โ Can extract full key | โ Only partial key share |
This is a Phase 2 long-term goal (12-24 months). For full technical details including:
- Threshold signature protocols (FROST)
- Device communication (js-libp2p)
- QR code signing flow
- Cross-device authentication
While the current architecture is insecure, the following measures reduce (but do not eliminate) risk:
Most Important: To significantly reduce the attack surface:
- โ Use in browsers WITHOUT any extensions (e.g., Chrome/Firefox with zero extensions installed)
- โ Use on mobile phones where browser extensions are typically not available and the attack surface is much smaller
- โ Avoid using with browser extensions installed - they can inject code, intercept messages, and access localStorage
This single measure eliminates many of the code injection attack vectors described in this document.
<meta http-equiv="Content-Security-Policy"
content="default-src 'self';
script-src 'self';
worker-src 'self';
connect-src 'self' https://up.storacha.network;">Use SRI tags for all external scripts:
<script src="..." integrity="sha384-..." crossorigin="anonymous"></script>Lock all dependencies to specific versions and audit regularly:
npm audit
npm audit fix- Clear worker memory after operations
- Re-authenticate frequently
- Don't persist decrypted keys
Warn users that:
- This is not production-grade security
- Private keys may be exposed to malicious code
- Only use for testing with non-critical data
Before using this application, ensure:
- You understand that private keys can be extracted by malicious code
- You are not storing valuable or sensitive data
- You have reviewed all dependencies for security issues
- You are running in an isolated testing environment
- You have a Content Security Policy configured
- You are not using this in production
- You understand that localStorage is not secure storage
- You accept the risks of software-based key management
Want to help make this architecture secure? Here's how:
- Research DKG: Investigate threshold signature schemes (FROST, GG20) for multi-device security
- Secure Storage: Help implement Phase 1 (largeBlob + Storacha credential storage)
- Review Code: Help audit cryptographic implementations
- Documentation: Improve security documentation and best practices
- Roadmap: See PLANNING.md for detailed features and priorities
If you discover a security vulnerability, please:
- Do NOT open a public issue
- Email security concerns to the maintainer
- Provide detailed reproduction steps
- Allow time for patches before public disclosure
For detailed roadmap and future planning, including secure credential storage and multi-device DKG architecture, see:
PLANNING.md - Future Planning & Roadmap
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
USE AT YOUR OWN RISK.