Skip to content

Parity — v0.2.0

This map was written after v0.2.0 shipped — the release predates the parity map itself, and a git tag is immutable — so it is a retrospective reading rather than a document published at the time. It is drawn from the v0.2.0 tree: the 66 routes it registers, the crypto it implements, and the CI jobs it ran.

main sat exactly on v0.2.0 when the map was first written, so this snapshot and the map at that moment describe the same surface.

How the emulator’s surface maps to real Key Vault (as documented at learn.microsoft.com/azure/key-vault), and — the point of this table — whether real work happens or just the API shape.

The design bet is that the durable, testable surface is protocol + real cryptography + identity, and those are done for real: real RS256 token validation, real RSA/EC keys doing real signing and encryption, real X.509 issuance. What is left out is the infrastructure around the vault — ARM, the HSM, private networking — which no localhost process can honestly provide.

“Real via our own wire-protocol implementation.” A row is 🟢 Real not only when real cryptography does the work, but also when the emulator itself implements Key Vault’s wire protocol and the logic behind it — so a real, unmodified SDK gets byte- and behaviour-identical responses. The Entra challenge handshake, the object model and the error envelope are all in this category.

Meaning
🟢 RealGenuine work: real signed JWTs verified, real crypto, real X.509, real logic enforced — no pretending.
🟡 EmulatedFaithful API contract + persisted state, but no engine — clock-derived or management-only.
🟠 Bring-your-own-engineReal when you attach a real external engine; contract-only otherwise.
🔴 Not implementedAbsent (404).

Authentication & identity (authentication/)

Section titled “Authentication & identity (authentication/)”
Key Vault featureEmulatorType
Entra bearer challenge (401 + WWW-Authenticate)Tokenless request returns the real challenge — Bearer authorization="…", resource="https://vault.azure.net" with AKV code AKV10000; unmodified azidentity walks it🟢 Real
Token validation (RS256 / JWKS / issuer / audience / expiry)Signature verified before any claim is read; issuer + audience (string or array) + exp/nbf with 60s skew, on the emulator’s controllable clock; JWKS cached by kid, refetched once on miss🟢 Real
Principal derivation (oidsub; idtyp=app → service principal)Full🟢 Real
Multi-tenant / multiple trusted issuersExactly one issuer is configured🔴 Not implemented
Key Vault featureEmulatorType
Set / get / list / list-versions, get by versionFull; real bytes persisted🟢 Real
Versioning (32-hex version per write)Full — every write mints a new version🟢 Real
Attributes enabledEnforced — a disabled object is refused🟢 Real
Attributes nbf / expStored and returned, informational only — deliberately not enforced, matching the documented emulator posture🟡 Emulated
Backup / restoreRound-trips a base64url JSON blob (name + versions), not an opaque encrypted blob as real Key Vault emits🟡 Emulated
Key Vault featureEmulatorType
Create key — RSA 2048/3072/4096, EC P-256/P-384/P-521Real crypto/rsa + crypto/ecdsa keygen🟢 Real
Sign / verify — RS256/384/512, PS256/384/512, ES256/384/512Real PKCS#1v15 / PSS / ECDSA; ES* uses Azure’s raw r‖s encoding🟢 Real
Encrypt / decrypt — RSA1_5, RSA-OAEP, RSA-OAEP-256Real RSA🟢 Real
Wrap / unwrap keyReal — the same RSA path as encrypt/decrypt🟢 Real
Import key (JWK)Real: RSA n/e/d/p/q precomputed + validated; EC crv/x/y/d checked on-curve🟢 Real
Get random bytesReal crypto/rand, 1–128 enforced🟢 Real
Public JWK exposure (private material never leaves)Full — private PKCS#8 stays in the store🟢 Real
RSA-HSM / EC-HSM key typesAccepted, then silently normalised to software keys — no HSM exists🟡 Emulated
Secure Key Release (/release)Real signed JWS with a fresh signer and public JWK header, but no attestation — the claim is self-declared🟡 Emulated
Key rotation policy (get/set)Stored and round-tripped verbatim🟡 Emulated
Rotate key (POST /keys/{name}/rotate)— no route🔴 Not implemented
key_ops enforcementStored and echoed into the JWK, but not enforced — a sign-only key will still encrypt🔴 Not implemented
oct / oct-HSM symmetric keys— only RSA/EC are generated🔴 Not implemented
AES algorithms (A128/192/256 CBC/CBCPAD/GCM, AESKW)— the crypto paths require an RSA key🔴 Not implemented
BYOK (KEK-wrapped import)— import takes raw private members only🔴 Not implemented
Key backup / restoreRound-trips a JSON blob carrying the private DER, not an opaque encrypted blob🟡 Emulated
Key Vault featureEmulatorType
Create self-signed certificateReal x509.CreateCertificate — random 128-bit serial, KeyUsage/ExtKeyUsage/BasicConstraints, SAN DNS names🟢 Real
Certificate policy (key_props, x509_props, issuer)Honoured — key type/size/curve, subject, SANs, validity months🟢 Real
Import — PKCS#12 (PFX) and PEM (PKCS#8 / PKCS#1 / SEC1)Real parsing; cert-only PEM supported🟢 Real
Linked key + secret materialised under the same nameFull on create, as real Key Vault🟢 Real
Certificate signing request (PKCS#10) for a named issuerReal CSR; the operation reports inProgress with the CSR bytes🟢 Real
Merge a signed chainReal — and the leaf’s public key is verified to match the pending key before merge (400 otherwise)🟢 Real
Issuance by a real CAThe emulator generates the key and a real CSR and merges the chain your CA signs — real X.509 only when you attach that CA🟠 BYO-engine
Delete cascade to the linked key/secretDiverges from real Key Vault: creation cascades, deletion does not, so delete → purge → restore can leave the linked objects behind🟡 Emulated
Issuers / contactsOpaque document round-trip; they do not drive issuance🟡 Emulated
Certificate backup / restoreRound-trips a JSON blob, not an opaque encrypted blob🟡 Emulated
Cancel / delete a pending certificate operation— no route🔴 Not implemented
Key Vault featureEmulatorType
Soft-delete → list-deleted → recover → purge (secrets, keys, certificates)Real state machine; retention validated 7–90 days🟡 Emulated
Retention window expiryGenuinely clock-driven — an object past purgeAt is purged lazily on read/list, against the controllable clock🟡 Emulated
Name reuse while soft-deleted → 409 ConflictEnforced🟢 Real
Purge protection / non-purgeable recoveryLevelrecoveryLevel is always Recoverable+Purgeable; purge is always allowed🔴 Not implemented
Key Vault featureEmulatorType
Host-routed vaults ({name}.vault.azure.net)Full — host selects the vault; anything else falls back to the default vault🟢 Real
Canonical object IDs (https://{vault}.vault.azure.net/...)Always rendered canonically regardless of the listen address🟢 Real
TLS with a cert covering *.vault.azure.netReal self-signed material, persisted so fingerprints are stable🟢 Real
Paging (maxresults, nextLink)Full, capped at 25🟢 Real
Key Vault error envelope + x-ms-request-idOn every response🟢 Real
api-version validation / version-differentiated behaviourNever read — any value (or none) is accepted and merely echoed into nextLink🔴 Not implemented
Key Vault featureEmulatorType
Data-plane authorizationA per-principal operation allowlist (POST /_emulator/permissions, ops named {type}/{op}, * wildcard); empty = full access🟡 Emulated
RBAC data-plane roles (Key Vault Secrets User, …)🔴 Not implemented
Access policies (the classic vault access-policy document)🔴 Not implemented

Emulator-only (no Key Vault equivalent — these exist for testing)

Section titled “Emulator-only (no Key Vault equivalent — these exist for testing)”
FeaturePurpose
Clock control (/_emulator/clock)Freeze/advance/offset — makes token expiry and soft-delete retention deterministic
Fault injection (/_emulator/faults)Force 429 + Retry-After or 500 for the next N requests, to exercise SDK retry paths
Permissions (/_emulator/permissions)The authorization allowlist above
Read-only portal (/_emulator/portal/)Inspect vault state in a browser

Ecosystem conformance: real clients as witnesses

Section titled “Ecosystem conformance: real clients as witnesses”
Real client (pinned)Surface exercisedStatus
azsecrets (Azure Go SDK)Secrets, versions, soft-delete🟢 CI test
azkeys (Azure Go SDK)Keys, sign/verify, encrypt/decrypt, wrap/unwrap🟢 CI test
azcertificates (Azure Go SDK)Certificate create/import/merge🟢 CI test
azidentity (ClientSecretCredential)The Entra challenge handshake, against an in-process real entra-emulator🟢 CI test
Three-emulator chain (vault secret → managed identity → Entra → Fabric)The family integration, incl. a negative test🟢 CI chain (stdlib HTTP, not an SDK)
azure-keyvault-secrets / azure-identity (Python)Documented in the quickstart🔴 Not wired in CI
.NET / JavaScript SDKsAsserted to work🔴 Not wired in CI

The Go SDK tests are the real oracle: they reconstruct an *rsa.PublicKey from the returned JWK and verify an SDK-produced signature outside the SDK, with a tampered-signature negative alongside.

Scope boundary: the vault, not the infrastructure around it

Section titled “Scope boundary: the vault, not the infrastructure around it”
Azure featureWhy out of scopeType
ARM control plane (create/delete vaults, Microsoft.KeyVault/vaults)A different plane: subscriptions, resource groups, deployments. The emulator serves the data plane a vault client talks to🔴
Managed HSM (managedhsm.azure.net)A distinct service and a hardware trust boundary — a localhost process cannot honestly emulate an HSM’s guarantees🔴
Private endpoints / firewall / network ACLsNetwork topology, not vault behaviour🔴
Customer-managed-key encryption of the vault itselfInfrastructure-level🔴
Real CA issuance (DigiCert / GlobalSign integrations)Needs a real CA — hence the BYO-engine CSR/merge path above🔴
Diagnostic logs / Event Grid notifications / metricsAzure Monitor surface🔴