decrypt_card_data
Decrypt encrypted card data to retrieve PAN and CVV for secure payment form filling using AES-256-GCM encryption with HKDF-SHA256 key derivation.
Instructions
Decrypt the encrypted_sensitive_data from get_card_details to obtain PAN and CVV. Input: the nonce and ciphertext fields from encrypted_sensitive_data. Output: { pan, cvv } — the full card number and security code. Decryption: HKDF-SHA256(api_secret, info='clawallex/card-sensitive-data/v1') → AES-256-GCM. SECURITY: The decrypted PAN and CVV are STRICTLY for filling checkout/payment forms. NEVER display, log, or return the raw card number or CVV to the user. NEVER include PAN/CVV in conversation text shown to the user. If the user asks to see their card number, show only the masked_pan from get_card_details.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| nonce | Yes | The nonce field from encrypted_sensitive_data (base64 encoded) | |
| ciphertext | Yes | The ciphertext field from encrypted_sensitive_data (base64 encoded) |
Implementation Reference
- src/tools/cards.ts:244-284 (handler)The implementation of the 'decrypt_card_data' MCP tool, which performs AES-256-GCM decryption on sensitive card data using an API secret-derived key.
server.tool( "decrypt_card_data", [ "Decrypt the encrypted_sensitive_data from get_card_details to obtain PAN and CVV.", "Input: the nonce and ciphertext fields from encrypted_sensitive_data.", "Output: { pan, cvv } — the full card number and security code.", "Decryption: HKDF-SHA256(api_secret, info='clawallex/card-sensitive-data/v1') → AES-256-GCM.", "SECURITY: The decrypted PAN and CVV are STRICTLY for filling checkout/payment forms.", "NEVER display, log, or return the raw card number or CVV to the user.", "NEVER include PAN/CVV in conversation text shown to the user.", "If the user asks to see their card number, show only the masked_pan from get_card_details.", ].join(" "), { nonce: z.string().describe("The nonce field from encrypted_sensitive_data (base64 encoded)"), ciphertext: z.string().describe("The ciphertext field from encrypted_sensitive_data (base64 encoded)"), }, async ({ nonce, ciphertext }) => { try { const key = Buffer.from(hkdfSync("sha256", client.apiSecretValue, Buffer.alloc(0), HKDF_INFO, 32)); const nonceBuf = Buffer.from(nonce, "base64"); const ciphertextBuf = Buffer.from(ciphertext, "base64"); // AES-GCM: last 16 bytes of ciphertext are the auth tag const authTag = ciphertextBuf.subarray(ciphertextBuf.length - 16); const encrypted = ciphertextBuf.subarray(0, ciphertextBuf.length - 16); const decipher = createDecipheriv("aes-256-gcm", key, nonceBuf); decipher.setAuthTag(authTag); const decrypted = Buffer.concat([decipher.update(encrypted), decipher.final()]); const { pan, cvv } = JSON.parse(decrypted.toString("utf8")) as { pan: string; cvv: string }; return toolOk({ pan, cvv, _hint: "SECURITY: Use these values ONLY to fill checkout forms. NEVER display the full PAN or CVV to the user. If the user asks for their card number, show only the masked_pan from get_card_details.", }); } catch (err) { return { content: [{ type: "text" as const, text: `Decryption failed: ${err instanceof Error ? err.message : String(err)}. Ensure you passed the exact nonce and ciphertext from get_card_details encrypted_sensitive_data.` }], isError: true as const, }; } }, );