From 3f1b63378fb538315e5c7cd47dd4dc333b7cd959 Mon Sep 17 00:00:00 2001 From: J8k3 Date: Sat, 25 Apr 2026 09:20:28 -0400 Subject: [PATCH] Add payment MAC wrapper operations --- AWS_PAYMENT_CRYPTOGRAPHY_RECIPES.md | 55 +++--- PAYMENT_RECIPES.md | 21 ++- src/core/config/Categories.json | 10 + src/core/lib/PaymentMac.mjs | 175 ++++++++++++++++++ src/core/operations/GeneratePaymentMAC.mjs | 93 ++++++++++ src/core/operations/VerifyPaymentMAC.mjs | 91 +++++++++ src/web/stylesheets/components/_operation.css | 2 + tests/operations/tests/Payment.mjs | 54 ++++++ 8 files changed, 471 insertions(+), 30 deletions(-) create mode 100644 src/core/lib/PaymentMac.mjs create mode 100644 src/core/operations/GeneratePaymentMAC.mjs create mode 100644 src/core/operations/VerifyPaymentMAC.mjs diff --git a/AWS_PAYMENT_CRYPTOGRAPHY_RECIPES.md b/AWS_PAYMENT_CRYPTOGRAPHY_RECIPES.md index f6f65422..1edfdcc2 100644 --- a/AWS_PAYMENT_CRYPTOGRAPHY_RECIPES.md +++ b/AWS_PAYMENT_CRYPTOGRAPHY_RECIPES.md @@ -25,8 +25,8 @@ Coverage legend: | `EncryptData` | `Direct` / `Partial` | Direct for AES, TDES, RSA. Partial for DUKPT and EMV-derived encryption. | | `DecryptData` | `Direct` / `Partial` | Direct for AES, TDES, RSA. Partial for DUKPT and EMV-derived decryption. | | `ReEncryptData` | `Direct` / `Partial` | Direct for plain decrypt-then-encrypt workflows. Partial for DUKPT re-encryption. | -| `GenerateMac` | `Direct` / `Partial` | Direct for HMAC and CMAC. Partial for DUKPT MAC and EMV MAC flows. | -| `VerifyMac` | `Direct` / `Partial` | Direct by recomputing and comparing HMAC/CMAC. Partial for DUKPT MAC and EMV MAC flows. | +| `GenerateMac` | `Direct` / `Partial` | Direct for static-key HMAC and CMAC, and direct for the implemented DUKPT CMAC wrapper modes. Partial for ISO 9797, EMV MAC, and AS2805 flows. | +| `VerifyMac` | `Direct` / `Partial` | Direct for static-key HMAC and CMAC, and direct for the implemented DUKPT CMAC wrapper modes. Partial for ISO 9797, EMV MAC, and AS2805 flows. | | `VerifyAuthRequestCryptogram` | `Partial` | Usable for AES-CMAC ARQC/ARPC-style checking when session key and preimage are already known. Dedicated ARQC and ARPC generators now exist for that constrained profile. | | `TranslateKeyMaterial` | `Partial` | Useful for ECDH derivation and TR-31 inspection, not full HSM-side rewrap semantics. | | `GenerateCardValidationData` | `Direct` | Direct for software CVV/CVV2/iCVV generation when the combined CVK pair is provided as clear hex. | @@ -103,15 +103,29 @@ Suggested use: ## 6) AWS `VerifyMac`: Recompute And Compare Operations: -- `From Hex` -- `HMAC` or `CMAC` -- `Take bytes` +- `Verify payment MAC` Suggested use: -- Recompute the MAC using the same starter as `GenerateMac`. -- Compare the result to the AWS `Mac` value manually or with a follow-on comparison recipe. +- Paste the message into the input field, choose the MAC method, and provide either the direct key or the DUKPT BDK plus KSN. +- Supply the expected MAC in hex and let the wrapper recompute and compare it. -## 7) AWS `GenerateCardValidationData`: CVV / CVV2 / iCVV +Notes: +- This covers the implemented static-key HMAC/CMAC and DUKPT-CMAC wrapper modes directly. +- ISO 9797, EMV MAC, and AS2805-specific verification are still partial gaps. + +## 7) AWS `GenerateMac`: Payment Wrapper +Operations: +- `Generate payment MAC` + +Suggested use: +- Paste the message into the input field and choose the payment MAC method that best matches the AWS attributes. +- Use direct key input for static HMAC or CMAC modes, or provide a BDK plus KSN for the implemented DUKPT CMAC request and response modes. + +Notes: +- This wrapper exists for usability so payment users can stay in the `Payments` category without needing to know which low-level primitive is underneath. +- It intentionally reuses the existing generic `HMAC` and `CMAC` implementations. + +## 8) AWS `GenerateCardValidationData`: CVV / CVV2 / iCVV Operations: - `Generate card validation data` @@ -124,7 +138,7 @@ Notes: - This directly covers software generation of CVV/CVV2/iCVV-style values. - Assumption: CVV2 forces service code `000` and iCVV forces `999`. -## 8) AWS `VerifyCardValidationData`: CVV / CVV2 / iCVV +## 9) AWS `VerifyCardValidationData`: CVV / CVV2 / iCVV Operations: - `Verify card validation data` @@ -138,7 +152,7 @@ Notes: ## Partial Recipe Starters -## 9) AWS `EncryptData` / `DecryptData`: DUKPT-Derived Symmetric Flows +## 10) AWS `EncryptData` / `DecryptData`: DUKPT-Derived Symmetric Flows Operations: - `Derive DUKPT key` - `AES Encrypt` or `AES Decrypt` or `Triple DES Encrypt` or `Triple DES Decrypt` @@ -151,20 +165,6 @@ Notes: - This is useful for offline vector work. - It does not claim one-to-one parity with every AWS DUKPT encryption attribute combination. -## 10) AWS `GenerateMac` / `VerifyMac`: DUKPT MAC -Operations: -- `Derive DUKPT key` -- `From Hex` -- `CMAC` or `HMAC` -- `Take bytes` - -Suggested use: -- Derive the transaction key from BDK and KSN. -- Convert `MessageData` from hex and generate the MAC using the derived key. - -Notes: -- Treat this as a lab starter, not proof of parity with AWS’s full DUKPT MAC union attributes. - ## 11) AWS `VerifyAuthRequestCryptogram`: EMV ARQC Check Operations: - `Generate EMV ARQC` @@ -257,6 +257,7 @@ Why: If you want closer AWS coverage, the highest-value missing operations are: 1. PIN block encode/decode for ISO 9564 formats 0, 1, 3, and 4. 2. IBM 3624 and VISA PVV generation and verification. -3. Dedicated EMV MAC and profile-specific EMV session-derivation helpers. -4. Clear-to-encrypted and encrypted-to-encrypted PIN translation flows. -5. TR-31 unwrap and rewrap helpers for dynamic-key workflows. +3. ISO 9797 and AS2805-specific MAC generation and verification. +4. Dedicated EMV MAC and profile-specific EMV session-derivation helpers. +5. Clear-to-encrypted and encrypted-to-encrypted PIN translation flows. +6. TR-31 unwrap and rewrap helpers for dynamic-key workflows. diff --git a/PAYMENT_RECIPES.md b/PAYMENT_RECIPES.md index 81d72ee2..8c5ced0f 100644 --- a/PAYMENT_RECIPES.md +++ b/PAYMENT_RECIPES.md @@ -62,7 +62,22 @@ Scope note: - CVV2 forces service code `000` and iCVV forces `999`. - It does not try to emulate scheme-specific dCVV, token CVV, or issuer-host formatting differences beyond the common decimalization flow. -## 8) EMV ARQC Generation (AES-CMAC Profile) +## 8) Payment MAC Generation And Verification +Operations: +- `Generate payment MAC` +- `Verify payment MAC` + +Suggested use: +- Paste the message data into the input field. +- Choose whether the MAC should use static `HMAC`, static `CMAC`, or DUKPT-derived TDES-CMAC. +- Provide either a direct MAC key or a BDK plus KSN, depending on the selected method. + +Scope note: +- This wrapper intentionally reuses the existing generic `HMAC` and `CMAC` implementations instead of duplicating crypto code. +- Current DUKPT coverage derives TDES session keys and applies TDES-CMAC for request and response MAC variants. +- ISO 9797, EMV session-derivation MAC, and AS2805 are still future additions. + +## 9) EMV ARQC Generation (AES-CMAC Profile) Operations: - `Generate EMV ARQC` @@ -75,7 +90,7 @@ Scope note: - This operation is intentionally limited to AES-CMAC-style EMV profiles. - It does not derive EMV session keys or assemble CDOL/tag data for you. -## 9) EMV ARPC Generation (AES-CMAC Response Profile) +## 10) EMV ARPC Generation (AES-CMAC Response Profile) Operations: - `Generate EMV ARPC` @@ -88,7 +103,7 @@ Scope note: - This operation is intentionally limited to AES-CMAC response profiles where the issuer session key and exact preimage are already known. - Legacy 3DES EMV ARQC/ARPC flows are not covered. -## 10) Combined Message Triage +## 11) Combined Message Triage Operations: - `Parse TR-34 B9 envelope` - `Parse ASN.1 hex string` diff --git a/src/core/config/Categories.json b/src/core/config/Categories.json index 47248cf2..72356ef5 100644 --- a/src/core/config/Categories.json +++ b/src/core/config/Categories.json @@ -568,11 +568,21 @@ { "name": "Payments", "ops": [ + "HMAC", + "CMAC", + "AES Encrypt", + "AES Decrypt", + "Triple DES Encrypt", + "Triple DES Decrypt", + "AES Key Wrap", + "AES Key Unwrap", "Parse TR-31 key block", "Parse TR-34 B9 envelope", "Calculate payment KCV", "Derive ECDH key material", "Derive DUKPT key", + "Generate payment MAC", + "Verify payment MAC", "Generate card validation data", "Verify card validation data", "Generate EMV ARQC", diff --git a/src/core/lib/PaymentMac.mjs b/src/core/lib/PaymentMac.mjs new file mode 100644 index 00000000..56fc4e6a --- /dev/null +++ b/src/core/lib/PaymentMac.mjs @@ -0,0 +1,175 @@ +/** + * @license Apache-2.0 + */ + +import Utils from "../Utils.mjs"; +import OperationError from "../errors/OperationError.mjs"; +import HMAC from "../operations/HMAC.mjs"; +import CMAC from "../operations/CMAC.mjs"; +import DeriveDUKPTKey from "../operations/DeriveDUKPTKey.mjs"; + +const PAYMENT_MAC_METHODS = [ + "HMAC SHA-224", + "HMAC SHA-256", + "HMAC SHA-384", + "HMAC SHA-512", + "AES-CMAC", + "TDES-CMAC", + "DUKPT MAC Request CMAC", + "DUKPT MAC Response CMAC", +]; + +/** + * Converts a string input into an ArrayBuffer according to the selected format. + * + * @param {string} input + * @param {string} inputFormat + * @returns {ArrayBuffer} + */ +function convertInputToBuffer(input, inputFormat) { + const byteString = Utils.convertToByteString(input || "", inputFormat); + return Utils.strToArrayBuffer(byteString); +} + +/** + * Resolves the effective MAC key for the selected method. + * + * @param {string} method + * @param {Object} keySpec + * @returns {{keyHex: string, keyContext: Object}} + */ +function resolveMacKey(method, keySpec) { + const normalizedKey = (keySpec.keyValue || "").replace(/\s+/g, ""); + + if (method === "DUKPT MAC Request CMAC" || method === "DUKPT MAC Response CMAC") { + if (keySpec.keyFormat !== "Hex") { + throw new OperationError("DUKPT BDK must be provided in hex."); + } + if (!keySpec.ksn) { + throw new OperationError("KSN is required for DUKPT MAC methods."); + } + + const variant = method === "DUKPT MAC Request CMAC" ? "MAC Request" : "MAC Response"; + const dukpt = new DeriveDUKPTKey(); + const keyHex = dukpt.run(normalizedKey, ["Derive Session Key", keySpec.ksn, variant, false]); + + return { + keyHex, + keyContext: { + keySource: "Derived from DUKPT BDK", + ksn: keySpec.ksn.replace(/\s+/g, "").toUpperCase(), + dukptVariant: variant + } + }; + } + + const byteString = Utils.convertToByteString(keySpec.keyValue || "", keySpec.keyFormat); + if (!byteString.length) { + throw new OperationError("Key material is required."); + } + + return { + keyHex: byteStringToHex(byteString), + keyContext: { + keySource: "Direct key input" + } + }; +} + +/** + * Converts a byte string into uppercase hex. + * + * @param {string} byteString + * @returns {string} + */ +function byteStringToHex(byteString) { + return Array.from(byteString, ch => ch.charCodeAt(0).toString(16).padStart(2, "0")).join("").toUpperCase(); +} + +/** + * Generates a payment MAC using the selected method. + * + * @param {string} input + * @param {string} inputFormat + * @param {string} method + * @param {string} keyValue + * @param {string} keyFormat + * @param {string} ksn + * @param {number} outputBytes + * @returns {Object} + */ +function generatePaymentMac(input, inputFormat, method, keyValue, keyFormat, ksn, outputBytes) { + const normalizedOutputBytes = Math.max(1, Number(outputBytes) || 8); + const inputBuffer = convertInputToBuffer(input, inputFormat); + const inputHex = byteStringToHex(Utils.arrayBufferToStr(inputBuffer, false)); + const { keyHex, keyContext } = resolveMacKey(method, { keyValue, keyFormat, ksn }); + + let fullMacHex; + if (method.startsWith("HMAC ")) { + const hmac = new HMAC(); + const hashName = { + "HMAC SHA-224": "SHA224", + "HMAC SHA-256": "SHA256", + "HMAC SHA-384": "SHA384", + "HMAC SHA-512": "SHA512", + }[method]; + fullMacHex = hmac.run(inputBuffer, [{ string: keyHex, option: "Hex" }, hashName]).toUpperCase(); + } else { + const cmac = new CMAC(); + const algorithm = method === "AES-CMAC" ? "AES" : "Triple DES"; + fullMacHex = cmac.run(inputBuffer, [{ string: keyHex, option: "Hex" }, algorithm]).toUpperCase(); + } + + const macHex = fullMacHex.substring(0, normalizedOutputBytes * 2); + + return { + method, + inputFormat, + inputHex, + outputBytes: normalizedOutputBytes, + fullMacHex, + macHex, + ...keyContext + }; +} + +/** + * Verifies a payment MAC by recomputing and comparing it. + * + * @param {string} input + * @param {string} inputFormat + * @param {string} method + * @param {string} keyValue + * @param {string} keyFormat + * @param {string} ksn + * @param {string} expectedMac + * @returns {Object} + */ +function verifyPaymentMac(input, inputFormat, method, keyValue, keyFormat, ksn, expectedMac) { + const normalizedExpected = (expectedMac || "").replace(/\s+/g, "").toUpperCase(); + if (!/^[0-9A-F]+$/.test(normalizedExpected) || normalizedExpected.length % 2 !== 0) { + throw new OperationError("Expected MAC must be even-length hex."); + } + + const generated = generatePaymentMac( + input, + inputFormat, + method, + keyValue, + keyFormat, + ksn, + normalizedExpected.length / 2 + ); + + return { + ...generated, + expectedMacHex: normalizedExpected, + valid: generated.macHex === normalizedExpected + }; +} + +export { + PAYMENT_MAC_METHODS, + generatePaymentMac, + verifyPaymentMac, +}; diff --git a/src/core/operations/GeneratePaymentMAC.mjs b/src/core/operations/GeneratePaymentMAC.mjs new file mode 100644 index 00000000..c0680051 --- /dev/null +++ b/src/core/operations/GeneratePaymentMAC.mjs @@ -0,0 +1,93 @@ +/** + * @license Apache-2.0 + */ + +import Operation from "../Operation.mjs"; +import { PAYMENT_MAC_METHODS, generatePaymentMac } from "../lib/PaymentMac.mjs"; + +/** + * Generate payment MAC operation. + */ +class GeneratePaymentMAC extends Operation { + + /** + * GeneratePaymentMAC constructor. + */ + constructor() { + super(); + + this.name = "Generate payment MAC"; + this.module = "Payment"; + this.description = "Paste the message data into the input field and generate a payment-oriented MAC using one payment-facing operation.

Input: message data in the selected input format.
Arguments: choose the MAC method, provide either a direct key or a DUKPT BDK, optionally provide a KSN for DUKPT methods, and choose the truncation length.

This wrapper reuses existing HMAC, CMAC, and DUKPT operations instead of duplicating their crypto logic."; + this.inlineHelp = "Input: message data.
Args: choose the payment MAC method, then provide either a direct key or a DUKPT BDK plus KSN."; + this.testDataSamples = [ + { + name: "Static AES-CMAC sample", + input: "1122334455667788", + args: ["Hex", "AES-CMAC", "00112233445566778899AABBCCDDEEFF", "Hex", "", 8, false] + } + ]; + this.infoURL = "https://docs.aws.amazon.com/payment-cryptography/latest/DataAPIReference/API_GenerateMac.html"; + this.inputType = "string"; + this.outputType = "string"; + this.args = [ + { + name: "Input format", + type: "option", + value: ["Hex", "UTF8", "Latin1", "Base64"], + comment: "How to decode the input field before MAC generation. Use Hex for payment test vectors expressed as hex." + }, + { + name: "MAC method", + type: "option", + value: PAYMENT_MAC_METHODS, + comment: "Static-key HMAC and CMAC modes reuse the existing generic primitives. DUKPT modes derive a TDES session key first and then apply TDES-CMAC." + }, + { + name: "Key / BDK", + type: "string", + value: "", + comment: "Provide the direct MAC key for HMAC or CMAC methods, or the clear BDK for DUKPT methods." + }, + { + name: "Key format", + type: "option", + value: ["Hex", "UTF8", "Latin1", "Base64"], + comment: "How to decode the key input. Assumption: DUKPT BDK input must be Hex." + }, + { + name: "KSN (DUKPT only)", + type: "string", + value: "", + comment: "Required only for DUKPT MAC methods. Provide the full 10-byte KSN as 20 hex characters." + }, + { + name: "Output bytes", + type: "number", + value: 8, + min: 1, + max: 64, + comment: "Number of leftmost MAC bytes to return. Leave at 8 for common payment truncation lengths." + }, + { + name: "Output as JSON", + type: "boolean", + value: false, + comment: "When enabled, returns the full MAC, truncation details, and key-context metadata." + } + ]; + } + + /** + * @param {string} input + * @param {Object[]} args + * @returns {string} + */ + run(input, args) { + const [inputFormat, method, keyValue, keyFormat, ksn, outputBytes, outputJson] = args; + const result = generatePaymentMac(input, inputFormat, method, keyValue, keyFormat, ksn, outputBytes); + return outputJson ? JSON.stringify(result, null, 4) : result.macHex; + } +} + +export default GeneratePaymentMAC; diff --git a/src/core/operations/VerifyPaymentMAC.mjs b/src/core/operations/VerifyPaymentMAC.mjs new file mode 100644 index 00000000..0fc524d3 --- /dev/null +++ b/src/core/operations/VerifyPaymentMAC.mjs @@ -0,0 +1,91 @@ +/** + * @license Apache-2.0 + */ + +import Operation from "../Operation.mjs"; +import { PAYMENT_MAC_METHODS, verifyPaymentMac } from "../lib/PaymentMac.mjs"; + +/** + * Verify payment MAC operation. + */ +class VerifyPaymentMAC extends Operation { + + /** + * VerifyPaymentMAC constructor. + */ + constructor() { + super(); + + this.name = "Verify payment MAC"; + this.module = "Payment"; + this.description = "Paste the message data into the input field and verify a payment-oriented MAC using one payment-facing operation.

Input: message data in the selected input format.
Arguments: choose the MAC method, provide either a direct key or a DUKPT BDK, add the KSN for DUKPT methods, and supply the expected MAC as hex.

This wrapper recomputes the MAC using the same payment-specific assumptions as the generate operation."; + this.inlineHelp = "Input: message data.
Args: choose the payment MAC method, provide the key context, then paste the expected MAC."; + this.testDataSamples = [ + { + name: "Static AES-CMAC verification sample", + input: "1122334455667788", + args: ["Hex", "AES-CMAC", "00112233445566778899AABBCCDDEEFF", "Hex", "", "339AF1AD1650E908", true] + } + ]; + this.infoURL = "https://docs.aws.amazon.com/payment-cryptography/latest/DataAPIReference/API_VerifyMac.html"; + this.inputType = "string"; + this.outputType = "string"; + this.args = [ + { + name: "Input format", + type: "option", + value: ["Hex", "UTF8", "Latin1", "Base64"], + comment: "How to decode the input field before MAC verification." + }, + { + name: "MAC method", + type: "option", + value: PAYMENT_MAC_METHODS, + comment: "Static-key HMAC and CMAC modes reuse the existing generic primitives. DUKPT modes derive a TDES session key first and then apply TDES-CMAC." + }, + { + name: "Key / BDK", + type: "string", + value: "", + comment: "Provide the direct MAC key for HMAC or CMAC methods, or the clear BDK for DUKPT methods." + }, + { + name: "Key format", + type: "option", + value: ["Hex", "UTF8", "Latin1", "Base64"], + comment: "How to decode the key input. Assumption: DUKPT BDK input must be Hex." + }, + { + name: "KSN (DUKPT only)", + type: "string", + value: "", + comment: "Required only for DUKPT MAC methods. Provide the full 10-byte KSN as 20 hex characters." + }, + { + name: "Expected MAC (hex)", + type: "string", + value: "", + comment: "MAC value to compare against, expressed as even-length hex." + }, + { + name: "Output as JSON", + type: "boolean", + value: true, + comment: "When enabled, returns the recomputed MAC, comparison target, and validity result." + } + ]; + } + + /** + * @param {string} input + * @param {Object[]} args + * @returns {string} + */ + run(input, args) { + const [inputFormat, method, keyValue, keyFormat, ksn, expectedMac, outputJson] = args; + const result = verifyPaymentMac(input, inputFormat, method, keyValue, keyFormat, ksn, expectedMac); + return outputJson ? JSON.stringify(result, null, 4) : String(result.valid); + } +} + +export default VerifyPaymentMAC; diff --git a/src/web/stylesheets/components/_operation.css b/src/web/stylesheets/components/_operation.css index 4f5edf66..1b36d460 100755 --- a/src/web/stylesheets/components/_operation.css +++ b/src/web/stylesheets/components/_operation.css @@ -221,6 +221,8 @@ input.toggle-string { filter: brightness(100%); } +.operation .form-group.is-filled label.bmd-label-floating, +.operation .form-group.is-focused label.bmd-label-floating, .operation .bmd-form-group.is-filled label.bmd-label-floating, .operation .bmd-form-group.is-focused label.bmd-label-floating { top: 4px !important; diff --git a/tests/operations/tests/Payment.mjs b/tests/operations/tests/Payment.mjs index 7f982c57..c5bf0847 100644 --- a/tests/operations/tests/Payment.mjs +++ b/tests/operations/tests/Payment.mjs @@ -255,6 +255,60 @@ TestRegister.addTests([ } ] }, + { + name: "Generate payment MAC: AES-CMAC", + input: "1122334455667788", + expectedOutput: "339AF1AD1650E908", + recipeConfig: [ + { + op: "Generate payment MAC", + args: ["Hex", "AES-CMAC", "00112233445566778899AABBCCDDEEFF", "Hex", "", 8, false] + } + ] + }, + { + name: "Generate payment MAC: HMAC SHA-256", + input: "1122334455667788", + expectedOutput: "9300E1D36DD30415", + recipeConfig: [ + { + op: "Generate payment MAC", + args: ["Hex", "HMAC SHA-256", "00112233445566778899AABBCCDDEEFF", "Hex", "", 8, false] + } + ] + }, + { + name: "Generate payment MAC: DUKPT MAC Request CMAC", + input: "1122334455667788", + expectedOutput: "3616961727FE155D", + recipeConfig: [ + { + op: "Generate payment MAC", + args: ["Hex", "DUKPT MAC Request CMAC", "0123456789ABCDEFFEDCBA9876543210", "Hex", "FFFF9876543210E00008", 8, false] + } + ] + }, + { + name: "Verify payment MAC: AES-CMAC", + input: "1122334455667788", + expectedOutput: JSON.stringify({ + method: "AES-CMAC", + inputFormat: "Hex", + inputHex: "1122334455667788", + outputBytes: 8, + fullMacHex: "339AF1AD1650E908A794284D91DC6D29", + macHex: "339AF1AD1650E908", + keySource: "Direct key input", + expectedMacHex: "339AF1AD1650E908", + valid: true + }, null, 4), + recipeConfig: [ + { + op: "Verify payment MAC", + args: ["Hex", "AES-CMAC", "00112233445566778899AABBCCDDEEFF", "Hex", "", "339AF1AD1650E908", true] + } + ] + }, { name: "Derive ECDH key material: raw shared secret", input: ecdhPrivateKey,