Passkey adoption has accelerated as major operating systems and web applications implement FIDO2/WebAuthn standards. However, vendor lock-in remains an architectural hurdle. Transferring passkeys between credential managers, such as moving from an OS-level enclave to an open-source password manager, has historically relied on proprietary export formats or unencrypted backups.
The release of the standardized Opaque, Interoperable Passkey Record specification and its accompanying Go reference implementation provides a cryptographically secure model for exporting and importing passkeys across hardware and software platforms.
The Credential Migration Problem
Passkeys replace traditional passwords with asymmetric public-private key pairs:
- The public key resides on the rely party (RP) server.
- The private key stays in a secure enclave, hardware security key, or encrypted credential store.
When users switch security providers, exporting raw private keys presents significant risk. Proprietary JSON dumps leave secrets vulnerable to file system exposure, while platform-specific sync mechanisms keep credentials locked inside proprietary ecosystems.
LEGACY EXPORT (Vulnerable):
Passkey Vault ---> Unencrypted JSON Export File ---> Target Vault
(Exposes raw ECC private keys on disk)
OPAQUE INTEROPERABLE SPECIFICATION:
Passkey Vault ---> Enveloped CBOR Payload ---> Key Exchange Protocol ---> Target Vault
(Cryptographically protected transit format)
Cryptographic Architecture of Opaque Passkey Records
The Opaque Passkey Record specification addresses security risks by wrapping WebAuthn credentials in an encrypted, authenticated container backed by CBOR (Concise Binary Object Representation).
+-------------------------------------------------------------------+
| Opaque Passkey Container (CBOR Encapsulation) |
| |
| +-------------------------------------------------------------+ |
| | Header: Version, Suite Identifier, Salt, KDF Parameters | |
| +-------------------------------------------------------------+ |
| | Payload: AES-256-GCM Encrypted Passkey Blob | |
| | - Credential ID (bytes) | |
| | - Private Key Material (EC2/OKP / ECDSA-P256 or Ed25519) | |
| | - Relying Party ID (domain) | |
| | - Sign Count & User Handle Metadata | |
| +-------------------------------------------------------------+ |
| | Authentication Tag: HMAC-SHA256 / GCM Auth Tag | |
| +-------------------------------------------------------------+ |
+-------------------------------------------------------------------+
1. Key Derivation and Transport Encryption
To export credentials safely over un-trusted channels or storage media, the record container uses HKDF (HMAC-based Extract-and-Expand Key Derivation Function) paired with AES-256-GCM.
The export key is derived from an ephemeral secret exchanged out-of-band between the source vault and destination vault via a Diffie-Hellman handshake or password-authenticated key exchange (PAKE):
K_derived = HKDF-Expand(HKDF-Extract(Salt, IKM), "PasskeyExportV1", 32)
Where:
IKMis the shared input key material established between platforms.Saltis a 32-byte cryptographically secure random value attached to the container header.
2. Standardized Binary Schema (CBOR)
Unlike text-based JSON exports prone to character encoding issues, passkey records use binary CBOR encoding. This structure preserves exact byte representations for public key identifiers, signature counts, and binary user handles.
Working with Passkey Records in Go
The Go reference API provides high-level primitives for encoding, encrypting, and validating passkey payloads.
package main
import (
"crypto/rand"
"fmt"
"log"
"github.com/filippo.io/passkey"
)
func main() {
// 1. Define passkey credential material
cred := &passkey.Credential{
RPID: "example.com",
UserName: "alex@example.com",
UserHandle: []byte{0x01, 0x02, 0x03, 0x04},
CredentialID: []byte("unique-cred-id-12345"),
PrivateKey: []byte("P-256-PRIVATE-KEY-BYTES"),
SignCount: 42,
}
// 2. Generate ephemeral transport key (e.g. established via PAKE)
transportKey := make([]byte, 32)
if _, err := rand.Read(transportKey); err != nil {
log.Fatalf("Failed to generate key: %v", err)
}
// 3. Encrypt and pack into Opaque Passkey Record format
opaqueRecord, err := passkey.MarshalEncrypted(cred, transportKey)
if err != nil {
log.Fatalf("Marshal failed: %v", err)
}
fmt.Printf("Generated Opaque Record (%d bytes)\n", len(opaqueRecord))
// 4. Import & Unmarshal on destination vault
importedCred, err := passkey.UnmarshalEncrypted(opaqueRecord, transportKey)
if err != nil {
log.Fatalf("Unmarshal failed: %v", err)
}
fmt.Printf("Successfully imported passkey for RP: %s, User: %s\n",
importedCred.RPID, importedCred.UserName)
}Security Implications for Password Managers and IDPs
Standardizing passkey export formats impacts password manager vendors, enterprise identity providers, and end users:
1. Eliminating Platform Lock-In
Users can migrate credential stores between desktop OS vaults, browser extension managers, and hardware tokens without generating fresh passkey pairings across all registered services.
2. Prevention of Credential Leakage During Sync
Because exports use authenticated encryption (AES-GCM) with strict key derivation rules, intercepted export files remain unreadable without the out-of-band transport secret.
3. Preservation of WebAuthn Metadata
Legacy export workarounds often lost sign counts or user handle associations, causing authentication failures on strict WebAuthn servers. The standardized CBOR schema guarantees full spec-compliant field preservation across platforms.
A Standardized Future for Authentication
The Opaque, Interoperable Passkey Record specification addresses a major remaining gap in WebAuthn adoption. By combining CBOR binary encoding with transport-layer key derivation, the Go reference implementation gives developers and platform vendors a secure blueprint for cross-platform credential mobility.



