Skip to main content

POST /keys/export

Export a key wrapped to the client's ephemeral public key

Wraps the key identified by ttag to the client's ephemeral P-256 public key (ECDH → HKDF → AES-GCM) and returns the wrap envelope. The caller must be the owner or have been granted access, otherwise the kS denies the export with 403.

Authentication

Requires the X-Session header (the opaque session id from POST /login). See Authentication.

Request

FieldTypeRequiredDescription
ttagstring (base64)yesBase64 key handle returned by POST /keys.
clientPubKeystring (base64)yesBase64 of the client's ephemeral raw uncompressed P-256 point (65 bytes, `0x04

Example

{
"ttag": "dGFnLWJhc2U2NC1leGFtcGxl",
"clientPubKey": "BFq2...base64-65-byte-point"
}

Responses

200

Wrapped key returned.

FieldTypeRequiredDescription
wrappedWrappedKeyyesECDH-P256 → HKDF-SHA256 → AES-GCM wrap envelope. The client derives the same shared secret from gatewayPubKey + its own ephemeral private key, then AES-GCM-decrypts ciphertext (with iv/authTag) to unwrap the data key. The raw key never crosses the wire in clear.
algostringyesCipher label for the delivered data key (not the wrap envelope). NOTE: the gateway currently hardcodes this to aes-128-cbc; it is NOT derived from the key and does NOT track keyLen. The demo only creates AES-128 keys (NID 419 → 16 bytes), so here algo and keyLen happen to coincide. For an AES-192/256 key, keyLen would be 24/32 while algo would still report aes-128-cbc. Treat keyLen as authoritative for the delivered key length.
keyLeninteger (16, 24, 32)yesLength in bytes of the delivered data key, derived from the export blob NID (419 → 16, 423 → 24, 427 → 32). Authoritative for the key length (see the caveat on algo).

wrapped object

FieldTypeRequiredDescription
gatewayPubKeystring (base64)yesBase64 of the gateway's ephemeral P-256 public point.
saltstring (base64)yesBase64 HKDF salt (16 bytes).
ivstring (base64)yesBase64 AES-GCM initialization vector.
ciphertextstring (base64)yesBase64 AES-GCM ciphertext of the wrapped data key.
authTagstring (base64)yesBase64 AES-GCM authentication tag.

Example

{
"wrapped": {
"gatewayPubKey": "...",
"salt": "...",
"iv": "...",
"ciphertext": "...",
"authTag": "..."
},
"algo": "aes-128-cbc",
"keyLen": 16
}

400

Validation error: missing fields, invalid JSON, or body > 1 MB.

401

Authentication required. Two shapes share this status: a session-layer 401 (a plain Error, no reauth flag) means a missing or unknown/expired X-Session — re-login for a new session id; a token-layer 401 (ReauthError with reauth: true) means the server-held token expired — re-run POST /login and retry.

403

kS RBAC/ACL denial — a real authorization refusal. Surface it; retrying will not help until access is granted.

500

Other kS operation error.

Error handling

This endpoint can return the statuses below. For request bodies and the client action per status, see the full error contract.

StatusMeaning
400Validation error: missing fields, invalid JSON, or body > 1 MB.
401Authentication required. Two shapes share this status: a session-layer 401 (a plain Error, no reauth flag) means a missing or unknown/expired X-Session — re-login for a new session id; a token-layer 401 (ReauthError with reauth: true) means the server-held token expired — re-run POST /login and retry.
403kS RBAC/ACL denial — a real authorization refusal. Surface it; retrying will not help until access is granted.
500Other kS operation error.
Distinguish 401-reauth from 403

A 401 with { "reauth": true } means the server-held token expired — re-run POST /login and retry. A 403 is a real authorization denial — surface it; retrying will not help.