Infrastructure
How NERO Embedded Wallets work — MPC key splitting, server-side architecture, and security guarantees
Infrastructure
How Embedded Wallets Work
When a user logs in through your app, NERO creates a non-custodial wallet using threshold cryptography. The private key is split into two shares at generation time:
- Device share — held on the user's browser, encrypted with a device-specific key
- Server share — held by the NERO MPC Auth backend, protected by HSM and distributed across nodes
Both shares must cooperate to produce a signature. The full private key sk = sk_A × sk_B (mod q) is never reconstructed during normal signing operations. The MtA (Multiplicative-to-Additive) protocol allows each party to compute partial signatures independently, which are then combined into a standard ECDSA signature.
Key Distribution Model
Security relies on three independent factors:
| Factor | Storage | Protection | Compromise Alone |
|---|---|---|---|
| Device factor | User's browser (IndexedDB) | AES-256-GCM encryption with device-specific key | Attacker gets encrypted blob, unusable without device key |
| Auth factor | NERO backend (PostgreSQL + HSM) | AES-256-GCM + HSM key wrapping + Shamir sub-shares | Attacker gets one encrypted share, insufficient to sign |
| Recovery factor | User-managed backup | Password-protected composite blob (scrypt + AES-256-GCM) | Attacker needs password (12+ chars, scrypt with N=131072) |
Lost share recovery: If the device share is lost but NERO is online, the user re-authenticates and the SDK restores the share from the backend. If NERO goes permanently offline, the recovery factor enables full offline key reconstruction.
Threshold guarantee: Compromising any single factor is insufficient to forge signatures. An attacker needs the device share AND the server share simultaneously to sign transactions.
Server-Side Architecture
The NERO MPC Auth backend is a production-grade Express.js service implementing the server side of MPC protocols.
Request Processing Pipeline
Every API request passes through an 11-layer middleware chain:
| Layer | Purpose |
|---|---|
| Request ID | UUID per request for tracing |
| Request logging | Structured logging for monitoring |
| Security headers | Helmet CSP, HSTS, X-Frame-Options |
| API versioning | Route to correct handler version |
| Device ID | Track device across sessions |
| Content validation | Reject malformed payloads |
| Size limit | 1MB max request body |
| Global rate limiting | Prevent abuse (100 req/15 min default) |
| API key validation | Authenticate the developer's project |
| CORS enforcement | Per-project origin allowlisting |
| Per-key rate limiting | Plan-based operation quotas |
Data Layer
| Component | Purpose |
|---|---|
| PostgreSQL | Users, sessions, key shares, projects, billing, audit logs |
| Redis (optional) | Rate limit counters, plan cache, WebSocket pub/sub |
| HSM | Key material wrapping (AWS KMS, GCP Cloud KMS, or local for development) |
Key Storage
Server key shares are stored encrypted at rest:
Server share (plaintext, in memory only during signing)
│
▼
AES-256-GCM encryption (MPC_ENCRYPTION_KEY)
│
▼
HSM key wrapping (AWS KMS / GCP Cloud KMS)
│
▼
PostgreSQL (mpc_party_states table)
│
▼
Optional: Shamir sub-shares distributed across N nodesThe mpc_party_states table stores:
- Encrypted state blob
- Encryption salt and IV
- GCM authentication tag
- State commitment hash (for integrity verification)
Distributed Deployment
For high availability, the server share can be split into N sub-shares using Shamir's Secret Sharing:
- Sub-shares distributed across nodes in different regions
- M-of-N threshold required for reconstruction
- Each sub-share has its own per-share encryption
- Inter-node authentication uses HMAC-SHA256 (node ID + timestamp + signature)
- Coordinator node collects M sub-shares, reconstructs the share in memory, signs, then zeros the memory
Node roles:
- Standalone — single server (development/small deployments)
- Coordinator — orchestrates signing, collects sub-shares from workers
- Worker — stores sub-shares, responds to coordinator queries
Security Measures
| Mechanism | What It Protects Against |
|---|---|
| AES-256-GCM encryption at rest | Database compromise — shares are encrypted blobs |
| HSM key wrapping (AWS KMS / GCP KMS) | Key material extraction — keys never leave hardware |
| Distributed Shamir sub-shares | Single-node compromise — no node has the complete share |
| Rate limiting (per-key, per-operation) | Brute force and resource exhaustion |
| CORS + domain allowlisting | Unauthorized cross-origin requests |
| Device fingerprinting | Session hijacking — binds sessions to physical devices |
| Audit logging | Incident investigation — full trail of all sensitive operations |
| MFA gating (TOTP, SMS OTP, email OTP) | Credential theft — additional factor for sensitive operations |
| Schnorr proofs (DKLS) | Protocol manipulation — proves correct execution |
| Feldman VSS commitments (Pedersen) | Share tampering — verifiable secret sharing |
| PKCE for OAuth | Authorization code interception and CSRF |
Dual Protocol Architecture
The backend implements two independent MPC protocols:
| DKLS (Default) | Pedersen DKG | |
|---|---|---|
| Sharing model | Multiplicative: sk = sk_A × sk_B (mod q) | Additive: sk = sk_A + sk_B (mod q) |
| Parties | 2 (client + server) | N (multi-party) |
| Wallet type | EOA (Externally Owned Account) | Smart Account (ERC-4337) |
| Key generation | 2 HTTP rounds (commitment → reveal with Schnorr proof) | 3 rounds via WebSocket (commitment → share → verify) |
| Signing | 5 HTTP calls using MtA with oblivious transfer | 3 phases via WebSocket with Lagrange interpolation |
| Zero-knowledge proofs | Schnorr proof of knowledge (Fiat-Shamir) | Feldman VSS polynomial commitments |
| Transport | HTTP request/response | WebSocket for real-time coordination |
Authentication Infrastructure
The backend supports 9 OAuth providers with full server-side implementation:
| Provider | OAuth Flow | User Identifier |
|---|---|---|
| Authorization Code + PKCE | ||
| Apple | Authorization Code | Email or Apple ID |
| GitHub | Authorization Code | |
| Discord | Authorization Code | |
| LINE | Authorization Code | LINE user ID |
| Authorization Code | ||
| Twitter/X | OAuth 2.0 | Email or handle |
| Authorization Code | WeChat union ID | |
| Authorization Code |
Plus passwordless methods: email OTP (via Resend), SMS OTP (via Twilio), and custom JWT verification.
Session management:
- JWT-based (access token + refresh token)
- Access token: 15 minutes default (configurable 1–60 min)
- Refresh token: 7 days
- Automatic token refresh at 60-second threshold
- Device-bound sessions with fingerprint verification
Privacy
- The only required stored identifier is an anonymized OAuth subject ID
- No user email or name is stored by default (configurable per project)
- Device keys are generated locally on the user's browser and never transmitted
- Share exchange during key generation uses ephemeral ECDH keys (discarded after protocol completion)
Billing & Metering
Usage is metered per Monthly Active Wallet (MAW):
| Tier | Price | MAW Included | Overage |
|---|---|---|---|
| Free | $0 | 100 | — |
| Growth | $49/mo | 1,000 | Per-MAW pricing |
| Scale | $149/mo | 10,000 | Per-MAW pricing |
Metered events: wallet creation, signing operations, key material exports. See the Dashboard for plan management.