From 544374fd1f6972a33cebf1a0ab5176091e888ddc Mon Sep 17 00:00:00 2001 From: Pranav Jain Date: Tue, 28 Apr 2026 17:03:28 -0400 Subject: [PATCH 1/2] refactor: rename KMS to Key Provider across codebase BREAKING CHANGE: All KMS-related environment variables have been renamed. Update your deployment configuration (Helm values, docker-compose, .env files, CI pipelines) before upgrading: KMS_URL -> KEY_PROVIDER_URL KMS_SERVER_CA_CERT_PATH -> KEY_PROVIDER_SERVER_CA_CERT_PATH KMS_CLIENT_TLS_KEY_PATH -> KEY_PROVIDER_CLIENT_TLS_KEY_PATH KMS_CLIENT_TLS_CERT_PATH -> KEY_PROVIDER_CLIENT_TLS_CERT_PATH KMS_CLIENT_TLS_KEY -> KEY_PROVIDER_CLIENT_TLS_KEY KMS_CLIENT_TLS_CERT -> KEY_PROVIDER_CLIENT_TLS_CERT KMS_SERVER_CERT_ALLOW_SELF_SIGNED -> KEY_PROVIDER_SERVER_CERT_ALLOW_SELF_SIGNED The KMS API specification file has been renamed from kms-api-spec.yaml to key-provider-api-spec.yaml. The API contract itself is unchanged -- only the naming has been updated to accurately reflect the vendor-agnostic nature of the key management interface. --- .github/workflows/build-and-test.yaml | 2 +- CLAUDE.md | 6 +- README.md | 58 ++++---- .../aws-interface.md | 20 +-- .../dinamo-interface.md | 10 +- docker-compose.yml | 20 +-- ...pi-spec.yaml => key-provider-api-spec.yaml | 32 ++--- masterBitgoExpress.json | 2 +- ...ient.test.ts => keyProviderClient.test.ts} | 26 ++-- .../advancedWalletManager/mpcFinalize.test.ts | 10 +- .../mpcInitialize.test.ts | 10 +- .../advancedWalletManager/nonRecovery.test.ts | 2 +- .../postIndependentKey.test.ts | 16 +-- .../postMpcV2Key.test.ts | 16 +-- .../advancedWalletManager/recoveryMpc.test.ts | 50 +++---- .../recoveryMpcV2.test.ts | 35 ++--- .../recoveryMultisigTransaction.test.ts | 20 +-- .../recoveryMusigEth.test.ts | 36 ++--- .../signMpcRecoveryTransaction.test.ts | 28 ++-- .../signMpcTransaction.test.ts | 92 +++++++------ .../signMultisigTransaction.test.ts | 29 ++-- src/__tests__/config.test.ts | 102 +++++++------- src/__tests__/routes.test.ts | 2 +- .../handlers/ecdsaEddsaSignTransaction.ts | 6 +- .../handlers/ecdsaMPCV2Recovery.ts | 8 +- .../ecdsaMPCV2WalletGenerationFinalize.ts | 8 +- .../ecdsaMPCV2WalletGenerationInitialize.ts | 6 +- .../ecdsaMPCV2WalletGenerationRound.ts | 6 +- .../handlers/eddsaMPCRecovery.ts | 6 +- .../eddsaMPCWalletGenerationFinalize.ts | 8 +- .../eddsaMPCWalletGenerationInitialize.ts | 6 +- .../handlers/multisigRecovery.ts | 14 +- .../handlers/multisigSignTransaction.ts | 10 +- .../handlers/postIndependentKey.ts | 10 +- .../handlers/utils/utils.ts | 26 ++-- .../keyProviderClient.ts} | 128 ++++++++++-------- .../types/dataKey.ts | 4 +- .../types/generateDataKey.ts | 2 +- .../types/getKey.ts | 2 +- .../types/postKey.ts | 2 +- src/advancedWalletManagerApp.ts | 6 +- src/initConfig.ts | 96 ++++++++----- .../routers/accelerateRoute.ts | 2 +- .../routers/consolidateRoute.ts | 2 +- .../routers/consolidateUnspentsRoute.ts | 2 +- .../routers/generateWalletRoute.ts | 4 +- .../routers/recoveryConsolidationsRoute.ts | 2 +- .../routers/recoveryRoute.ts | 2 +- .../routers/sendManyRoute.ts | 2 +- .../routers/signAndSendMpcRoute.ts | 2 +- src/shared/responseHandler.ts | 4 +- src/shared/types/index.ts | 18 +-- 52 files changed, 535 insertions(+), 483 deletions(-) rename {demo-kms-script => demo-key-provider-script}/aws-interface.md (88%) rename {demo-kms-script => demo-key-provider-script}/dinamo-interface.md (93%) rename kms-api-spec.yaml => key-provider-api-spec.yaml (92%) rename src/__tests__/api/advancedWalletManager/{kmsClient.test.ts => keyProviderClient.test.ts} (74%) rename src/advancedWalletManager/{kmsClient/kmsClient.ts => keyProviderClient/keyProviderClient.ts} (50%) rename src/advancedWalletManager/{kmsClient => keyProviderClient}/types/dataKey.ts (79%) rename src/advancedWalletManager/{kmsClient => keyProviderClient}/types/generateDataKey.ts (83%) rename src/advancedWalletManager/{kmsClient => keyProviderClient}/types/getKey.ts (87%) rename src/advancedWalletManager/{kmsClient => keyProviderClient}/types/postKey.ts (90%) diff --git a/.github/workflows/build-and-test.yaml b/.github/workflows/build-and-test.yaml index bc2baa2a..0aa935cc 100644 --- a/.github/workflows/build-and-test.yaml +++ b/.github/workflows/build-and-test.yaml @@ -173,4 +173,4 @@ jobs: MTLS_ENABLED: true MTLS_REQUEST_CERT: true MTLS_REJECT_UNAUTHORIZED: false - KMS_URL: 'https://localhost:3000/' + KEY_PROVIDER_URL: 'https://localhost:3000/' diff --git a/CLAUDE.md b/CLAUDE.md index 31c587e4..2c17290b 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -30,7 +30,7 @@ Advanced Wallet Manager is a secure cryptocurrency signing server with two opera - Lightweight server focused solely on secure signing operations - Runs on port 3080 by default -- Integrates with KMS for key management +- Integrates with advanced wallets key provider for key management - Handles cryptographic operations securely - Exposes minimal endpoints focused on key generation and signing @@ -56,7 +56,7 @@ Advanced Wallet Manager is a secure cryptocurrency signing server with two opera - `src/initConfig.ts` - Configuration loading and validation - `src/routes/` - Express routes for both modes - `src/api/` - API implementation for both modes -- `src/kms/` - KMS client and operations +- `src/advancedWalletManager/keyProviderClient/` - key provider client and operations - `src/shared/` - Shared utilities and types ### Configuration @@ -73,7 +73,7 @@ Configuration is managed through environment variables with defaults defined in #### Advanced Wallet Manager Mode Specific - `ADVANCED_WALLET_MANAGER_PORT` - Port to listen on (default: 3080) -- `KMS_URL` - Required KMS service URL +- `KEY_PROVIDER_URL` - Required key provider service URL #### Master Express Mode Specific diff --git a/README.md b/README.md index 77502cc1..461b8b5e 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ Advanced wallets operate in two modes: Key features include: - **Complete Infrastructure Control** - Host and manage all components in your own secure environment. -- **KMS/HSM Integration** - Bring your own KMS or HSM by implementing the provided [KMS API interface specification](./kms-api-spec.yaml). Reference implementations available for [AWS HSM](./demo-kms-script/aws-interface.md) and [Dinamo HSM](./demo-kms-script/dinamo-interface.md). +- **KMS/HSM Integration** - Bring your own KMS or HSM by implementing the provided [Advanced Wallets Key Provider API interface specification](./key-provider-api-spec.yaml). Reference implementations available for [AWS HSM](./demo-key-provider-script/aws-interface.md) and [Dinamo HSM](./demo-key-provider-script/dinamo-interface.md). - **Network Isolation** - Advanced Wallet Manager operates in a completely isolated network segment with no external internet access. - **mTLS Security** - Optional mutual TLS with client certificate validation for secure inter-service communications. - **Flexible Configuration** - Environment-based setup with file or variable-based certificates. @@ -38,7 +38,7 @@ Key features include: ## Architecture -- **Advanced Wallet Manager** (Port 3080) - An isolated signing server with no internet access that only connects to your KMS API implementation for key operations. +- **Advanced Wallet Manager** (Port 3080) - An isolated signing server with no internet access that only connects to your Key Provider API implementation for key operations. - **Master Express** (Port 3081) - An API gateway providing end-to-end wallet creation and transaction support, integrating [BitGo APIs](https://developers.bitgo.com/reference/overview#/) with secure communication to Advanced Wallet Manager. ## Installation @@ -49,9 +49,9 @@ Key features include: - **npm** or **yarn** package manager. - **OpenSSL** for certificate generation. - **Docker** and **Docker Compose** for containerized deployment (or you can use **Podman** as alternative to Docker). -- **KMS API Implementation** - You must implement the [KMS API interface specification](./kms-api-spec.yaml) to connect your KMS/HSM to the Advanced Wallet Manager. Reference implementations available: - - [AWS HSM Implementation Example](./demo-kms-script/aws-interface.md) - - [Dinamo HSM Implementation Example](./demo-kms-script/dinamo-interface.md) +- **Key Provider API Implementation** - You must implement the [Key Provider API interface specification](./key-provider-api-spec.yaml) to connect your KMS/HSM to the Advanced Wallet Manager. Reference implementations available: + - [AWS HSM Implementation Example](./demo-key-provider-script/aws-interface.md) + - [Dinamo HSM Implementation Example](./demo-key-provider-script/dinamo-interface.md) ### Setup @@ -119,7 +119,7 @@ TLS_MODE=disabled \ BITGO_ENV=test \ APP_MODE=advanced-wallet-manager \ ADVANCED_WALLET_MANAGER_PORT=3080 \ -KMS_URL=http://localhost:3000 \ +KEY_PROVIDER_URL=http://localhost:3000 \ npm start ``` @@ -168,9 +168,9 @@ curl -X POST http://localhost:3081/ping/advancedWalletManager | Variable | Description | Default | Required | | ------------------------------ | ---------------------------------- | ------- | -------- | | `ADVANCED_WALLET_MANAGER_PORT` | Port to listen on | `3080` | ❌ | -| `KMS_URL` | URL to your KMS API implementation | - | ✅ | +| `KEY_PROVIDER_URL` | URL to your Key Provider API implementation | - | ✅ | -> **Note:** The `KMS_URL` points to your implementation of the KMS API interface. You must implement this interface to connect your KMS/HSM. See [Prerequisites](#prerequisites) for the specification and examples. +> **Note:** The `KEY_PROVIDER_URL` points to your implementation of the Key Provider API interface. You must implement this interface to connect your KMS/HSM. See [Prerequisites](#prerequisites) for the specification and examples. ### Master Express Settings @@ -232,17 +232,17 @@ curl -X POST http://localhost:3081/ping/advancedWalletManager | `AWM_SERVER_CA_CERT` | AWM server CA certificate (alternative) | PEM string | | `AWM_SERVER_CERT_ALLOW_SELF_SIGNED` | Allow self-signed AWM server certificates | Boolean (default: `false`) | -**For Advanced Wallet Manager → KMS:** +**For Advanced Wallet Manager → Key Provider:** -| Variable | Description | Format | -| ----------------------------------- | ----------------------------------------- | -------------------------- | -| `KMS_CLIENT_TLS_KEY_PATH` | Client private key file path | File path | -| `KMS_CLIENT_TLS_KEY` | Client private key (alternative) | PEM string | -| `KMS_CLIENT_TLS_CERT_PATH` | Client certificate file path | File path | -| `KMS_CLIENT_TLS_CERT` | Client certificate (alternative) | PEM string | -| `KMS_SERVER_CA_CERT_PATH` | KMS server CA certificate file path | File path | -| `KMS_SERVER_CA_CERT` | KMS server CA certificate (alternative) | PEM string | -| `KMS_SERVER_CERT_ALLOW_SELF_SIGNED` | Allow self-signed KMS server certificates | Boolean (default: `false`) | +| Variable | Description | Format | +| ------------------------------------------- | -------------------------------------------------- | -------------------------- | +| `KEY_PROVIDER_CLIENT_TLS_KEY_PATH` | Client private key file path | File path | +| `KEY_PROVIDER_CLIENT_TLS_KEY` | Client private key (alternative) | PEM string | +| `KEY_PROVIDER_CLIENT_TLS_CERT_PATH` | Client certificate file path | File path | +| `KEY_PROVIDER_CLIENT_TLS_CERT` | Client certificate (alternative) | PEM string | +| `KEY_PROVIDER_SERVER_CA_CERT_PATH` | key provider server CA certificate file path | File path | +| `KEY_PROVIDER_SERVER_CA_CERT` | key provider server CA certificate (alternative) | PEM string | +| `KEY_PROVIDER_SERVER_CERT_ALLOW_SELF_SIGNED` | Allow self-signed key provider server certificates | Boolean (default: `false`) | > **Note:** For security reasons, when `TLS_MODE=mtls`, outbound client certificates are required and cannot reuse server certificates. When `TLS_MODE=disabled`, these certificates aren't required. @@ -276,7 +276,7 @@ podman run -d \ -e TLS_MODE=mtls \ -e SERVER_TLS_KEY_PATH=/app/certs/advanced-wallet-manager-key.pem \ -e SERVER_TLS_CERT_PATH=/app/certs/advanced-wallet-manager-cert.pem \ - -e KMS_URL=host.containers.internal:3000 \ + -e KEY_PROVIDER_URL=host.containers.internal:3000 \ -e NODE_ENV=development \ -e CLIENT_CERT_ALLOW_SELF_SIGNED=true \ advanced-wallet-manager @@ -351,7 +351,7 @@ The setup creates two distinct networks: ### Prerequisites 1. **Install Docker and Docker Compose** -2. **Ensure your KMS API implementation is running** on your host machine (typically on port 3000) +2. **Ensure your Key Provider API implementation is running** on your host machine (typically on port 3000) ### Quick Start @@ -424,17 +424,17 @@ For production deployments with proper mTLS security: export APP_MODE=advanced-wallet-manager export TLS_MODE=mtls export ADVANCED_WALLET_MANAGER_PORT=3080 -export KMS_URL=https://production-kms.example.com:3000 +export KEY_PROVIDER_URL=https://production-key-provider.example.com:3000 # Server certificates for incoming mTLS connections export SERVER_TLS_KEY_PATH=/secure/certs/awm-server.key export SERVER_TLS_CERT_PATH=/secure/certs/awm-server.crt -# Client certificates for outbound connections to KMS -export KMS_CLIENT_TLS_KEY_PATH=/secure/certs/awm-kms-client.key -export KMS_CLIENT_TLS_CERT_PATH=/secure/certs/awm-kms-client.crt -export KMS_SERVER_CA_CERT_PATH=/secure/certs/kms-ca.crt +# Client certificates for outbound connections to key provider +export KEY_PROVIDER_CLIENT_TLS_KEY_PATH=/secure/certs/awm-key-provider-client.key +export KEY_PROVIDER_CLIENT_TLS_CERT_PATH=/secure/certs/awm-key-provider-client.crt +export KEY_PROVIDER_SERVER_CA_CERT_PATH=/secure/certs/key-provider-ca.crt # Security settings - production-grade export CLIENT_CERT_ALLOW_SELF_SIGNED=false -export KMS_SERVER_CERT_ALLOW_SELF_SIGNED=false +export KEY_PROVIDER_SERVER_CERT_ALLOW_SELF_SIGNED=false export MTLS_ALLOWED_CLIENT_FINGERPRINTS=sha256:1a2b3c...,sha256:4d5e6f... export BITGO_ENV=prod npm start @@ -490,7 +490,7 @@ curl --cert /path/to/client-cert.crt --key /path/to/client-key.key \ For local testing, you can generate and use demo certificates with the self-signed configuration flags: - Generate demo certificates: `npm run generate-test-ssl` (creates `demo.key` and `demo.crt`). -- Set `CLIENT_CERT_ALLOW_SELF_SIGNED=true`, `KMS_SERVER_CERT_ALLOW_SELF_SIGNED=true`, and `AWM_SERVER_CERT_ALLOW_SELF_SIGNED=true`. +- Set `CLIENT_CERT_ALLOW_SELF_SIGNED=true`, `KEY_PROVIDER_SERVER_CERT_ALLOW_SELF_SIGNED=true`, and `AWM_SERVER_CERT_ALLOW_SELF_SIGNED=true`. - Use the demo certificates for all certificate paths (server and client). - **Important:** Demo certificates and self-signed configurations should never be used in production. @@ -499,7 +499,7 @@ For local testing, you can generate and use demo certificates with the self-sign 1. **Use CA-signed certificates** instead of self-signed. 2. **Set `CLIENT_CERT_ALLOW_SELF_SIGNED=false`** and server-specific allow self-signed flags to `false` in production. 3. **Configure client certificate allowlisting** with `MTLS_ALLOWED_CLIENT_FINGERPRINTS`. -4. **Use separate certificates** for each service (server, AWM client, KMS client). +4. **Use separate certificates** for each service (server, AWM client, key provider client). 5. **Regularly rotate certificates**. 6. **Secure private key storage** and use appropriate file permissions. 7. **Always use `TLS_MODE=mtls`** in production environments. @@ -519,7 +519,7 @@ The output format is: `sha256:AB:CD:EF:...` which you can use in the configurati #### Certificate Requirements for Production - All certificates should be CA-signed certificates issued by your organization's PKI. -- Each service must use separate certificates (server cert, AWM client cert, KMS client cert). +- Each service must use separate certificates (server cert, AWM client cert, key provider client cert). - Client certificates for outbound connections must be different from server certificates. - Store private keys in secure locations with restricted file permissions: ```bash diff --git a/demo-kms-script/aws-interface.md b/demo-key-provider-script/aws-interface.md similarity index 88% rename from demo-kms-script/aws-interface.md rename to demo-key-provider-script/aws-interface.md index 745e2f39..260da6e0 100644 --- a/demo-kms-script/aws-interface.md +++ b/demo-key-provider-script/aws-interface.md @@ -1,10 +1,10 @@ -# AWS HSM KMS Implementation Documentation +# AWS HSM Key Provider Implementation Documentation -This document provides a reference implementation for integrating the 4 KMS API's with AWS HSM, covering the complete request-response flow from API handlers to HSM operations. +This document provides a reference implementation for integrating the 4 Key Provider API's with AWS HSM, covering the complete request-response flow from API handlers to HSM operations. ## ⚠️ Security Recommendation -For production KMS implementations, consider implementing the KMS-API in a C++ like language, because JavaScript does not support low-level memory management. Depending on your solution, direct memory management with explicit memory allocation/deallocation might be desirable. +For production key provider implementations, consider implementing the Key Provider API in a C++ like language, because JavaScript does not support low-level memory management. Depending on your solution, direct memory management with explicit memory allocation/deallocation might be desirable. Also consider implementing low level cryptographic operations using low-level languages like C++ or Rust. They typically provide easier and more efficient data manipulation and transaformation. @@ -14,7 +14,7 @@ When working with AWS HSM, adhere to their guidances and best practices for the ## API Overview -The KMS API provides secure key management through four main endpoints that integrate with AWS HSM: +The Key Provider API provides secure key management through four main endpoints that integrate with AWS HSM: - `POST /key` - Store private keys using envelope encryption - `GET /key/{pub}` - Retrieve private keys using envelope decryption @@ -25,10 +25,10 @@ The KMS API provides secure key management through four main endpoints that inte All 4 API's implementation should follow roughly the same dataflow as outlined bellow: ``` -API Request → Handler → KMS Provider → AWS HSM → KMS Provider → Database (if required) → Response +API Request → Handler → Key Provider → AWS HSM → Key Provider → Database (if required) → Response ``` -A KMS provider is the implementation of the code that is in charge of making the necessary calls to the HSM directly. You might have multiple providers in your solution, one for each 3rd party HSM that you wish to use, for example. +A Key Provider is the implementation of the code that is in charge of making the necessary calls to the HSM directly. You might have multiple providers in your solution, one for each 3rd party HSM that you wish to use, for example. ### Handler-to-Provider Mapping @@ -41,10 +41,10 @@ A KMS provider is the implementation of the code that is in charge of making the ## Envelope Encryption Pattern (Recommended) -We recommend using a 3 level key encryption to store and protect the private keys of your advanced wallets. -The 3 levels consist of the root-level key from the KMS, 2nd level data keys generated by the root level key, and the 3rd level private keys used by your wallets directly. +We recommend using a 3 level key encryption to store and protect the private keys of your advanced wallets. +The 3 levels consist of the root-level key from the KMS/HSM, 2nd level data keys generated by the root level key, and the 3rd level private keys used by your wallets directly. -### Layer 1: KMS Keys (AWS HSM) +### Layer 1: Root Keys (AWS HSM) - **Key spec**: `SYMMETRIC_DEFAULT` - **Algorithm**: AES-256-GCM, used by keys generated using the specification `SYMMETRIC_DEFAULT` - **Generation**: AWS HSM @@ -67,7 +67,7 @@ The 3 levels consist of the root-level key from the KMS, 2nd level data keys gen ### Root Key Creation -This following needs to be only run once. The KMS should be functional with just one root-level key. +This following needs to be only run once. The Key Provider should be functional with just one root-level key. ```typescript import * as awskms from '@aws-sdk/client-kms'; diff --git a/demo-kms-script/dinamo-interface.md b/demo-key-provider-script/dinamo-interface.md similarity index 93% rename from demo-kms-script/dinamo-interface.md rename to demo-key-provider-script/dinamo-interface.md index 5a00a6cb..e8e074d5 100644 --- a/demo-kms-script/dinamo-interface.md +++ b/demo-key-provider-script/dinamo-interface.md @@ -1,8 +1,8 @@ -# Dinamo HSM KMS Implementation Documentation +# Dinamo HSM Key Provider Implementation Documentation ## ⚠️ Security Recommendation -**For production KMS implementations, consider implementing the KMS-API in a C++ like language, or use typed arrays like Uint8Array for all sensitive data because JavaScript does not support secure memory management.** +**For production key provider implementations, consider implementing the Key Provider API in a C++ like language, or use typed arrays like Uint8Array for all sensitive data because JavaScript does not support secure memory management.** **Recommended Alternatives:** - **C++/Rust**: Languages with explicit memory management and secure allocation @@ -10,7 +10,7 @@ - **Native Addons**: Implement cryptographic operations in native C++ modules - **Hardware Security**: Use HSM-backed secure memory when available -This document provides a reference implementation for integrating the 4 KMS API's with Dinamo HSM, covering the complete request-response flow from API handlers to HSM operations. +This document provides a reference implementation for integrating the 4 Key Provider API's with Dinamo HSM, covering the complete request-response flow from API handlers to HSM operations. ## Demo Scripts @@ -20,7 +20,7 @@ This document provides a reference implementation for integrating the 4 KMS API' ## Quick Overview -The KMS API provides secure key management through four main endpoints that integrate with Dinamo HSM: +The Key Provider API provides secure key management through four main endpoints that integrate with Dinamo HSM: - `POST /key` - Store private keys using envelope encryption - `GET /key/{pub}` - Retrieve private keys using envelope decryption @@ -30,7 +30,7 @@ The KMS API provides secure key management through four main endpoints that inte ## Architecture Flow ``` -API Request → Handler → KMS Provider → Dinamo HSM → Database → Response +API Request → Handler → Key Provider → Dinamo HSM → Database → Response ``` ### Handler-to-Provider Mapping diff --git a/docker-compose.yml b/docker-compose.yml index ccb778a6..7c806f81 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -22,16 +22,16 @@ services: - TLS_MODE=disabled - CLIENT_CERT_ALLOW_SELF_SIGNED=true - # KMS settings (required) - - KMS_URL=http://172.20.0.1:3000 # UPDATE TO YOUR OWN KMS URL - - KMS_SERVER_CERT_ALLOW_SELF_SIGNED=true - - # Optional KMS TLS settings (uncomment if using mTLS with KMS) - # - KMS_SERVER_CA_CERT_PATH=/path/to/kms-ca-cert.pem - # - KMS_CLIENT_TLS_KEY_PATH=/path/to/kms-client-key.pem - # - KMS_CLIENT_TLS_CERT_PATH=/path/to/kms-client-cert.pem - # - KMS_CLIENT_TLS_KEY= - # - KMS_CLIENT_TLS_CERT= + # key provider settings (required) + - KEY_PROVIDER_URL=http://172.20.0.1:3000 # UPDATE TO YOUR OWN key provider URL + - KEY_PROVIDER_SERVER_CERT_ALLOW_SELF_SIGNED=true + + # Optional key provider TLS settings (uncomment if using mTLS with key provider) + # - KEY_PROVIDER_SERVER_CA_CERT_PATH=/path/to/key-provider-ca-cert.pem + # - KEY_PROVIDER_CLIENT_TLS_KEY_PATH=/path/to/key-provider-client-key.pem + # - KEY_PROVIDER_CLIENT_TLS_CERT_PATH=/path/to/key-provider-client-cert.pem + # - KEY_PROVIDER_CLIENT_TLS_KEY= + # - KEY_PROVIDER_CLIENT_TLS_CERT= # Optional server TLS settings (uncomment if using mTLS) # - SERVER_TLS_KEY_PATH=/path/to/server-key.pem diff --git a/kms-api-spec.yaml b/key-provider-api-spec.yaml similarity index 92% rename from kms-api-spec.yaml rename to key-provider-api-spec.yaml index 078e4127..99adfa76 100644 --- a/kms-api-spec.yaml +++ b/key-provider-api-spec.yaml @@ -1,17 +1,17 @@ openapi: 3.0.0 info: - title: KMS API Interface Specification + title: Advanced Wallets Key Provider API Interface Specification version: 1.0.0 description: | # API Interface for Advanced Wallet Integration - This specification defines the required API interface that must be implemented by clients to integrate - their KMS (Key Management Service) or HSM (Hardware Security Module) provider with advanced wallets. + This specification defines the required API interface that must be implemented by clients to integrate + their KMS/HSM provider with advanced wallets through the Advanced Wallets Key Provider interface. ## Purpose - Clients must implement this API specification to enable secure cryptographic key storage and management + Clients must implement this Advanced Wallets Key Provider API specification to enable secure cryptographic key storage and management for advanced wallets. The implementation should connect to your organization's KMS/HSM provider. ## Implementation Requirements @@ -36,9 +36,9 @@ paths: - Key Management summary: Store a private key description: | - Store a new private key encrypted using the configured KMS provider. + Store a new private key encrypted using the configured KMS/HSM provider. - The private key is encrypted using the KMS before storage. The implementation uses envelope + The private key is encrypted before storage. The implementation uses envelope encryption for large payloads and direct encryption for smaller payloads. **Important**: Keys are identified by the combination of `pub` (public key) and `source` @@ -101,7 +101,7 @@ paths: Retrieve and decrypt a previously stored private key. The key is identified by the public key (`pub`) path parameter and the `source` query parameter. - The KMS provider decrypts the key before returning it. + The KMS/HSM provider decrypts the key before returning it. operationId: getPrivateKey parameters: - name: pub @@ -149,12 +149,12 @@ paths: description: | Generate a new data encryption key for envelope encryption operations. - The KMS provider generates a symmetric key and returns both: + The KMS/HSM provider generates a symmetric key and returns both: - **plaintextKey**: Use immediately for encryption, then discard - **encryptedKey**: Store this to decrypt data later using `/decryptDataKey` This follows the envelope encryption pattern where data is encrypted with the data key, - and the data key itself is encrypted with the KMS root key. + and the data key itself is encrypted with the root key. operationId: generateDataKey requestBody: required: true @@ -182,7 +182,7 @@ paths: plaintextKey: '1,2,3,0,120,222,140,157,217,111,195,208,47,200,213,217,82,189,16,171,207,16,138,46,228,224,190,138,63,132,239,80,164,8,124,105,140,1,174,211,14,152,144,66,115,54,226,169,178,37,100,105,154,15,0,0,0,126,48,124,6,9,42,134,72,134,247,13,1,7,6,160,111,48,109,2,1,0,48,104,6,9,42,134,72,134,247,13,1,7,1,48,30,6,9,96,134,72,1,101,3,4,1,46,48,17,4,12,247,0,189,155,147,80,121,250,71,64,30,121,2,1,16,128,59,175,44,60,80,240,109,12,47,202,7,20,250,186,219,247,41,129,85,0,16,202,62,33,42,240,91,175,106,165,120,107,65,28,21,122,211,235,23,79,65,25,56,107,106,95,112,39,148,183,6,160,119,205,12,116,187,127,63,83' encryptedKey: '62,137,108,179,93,237,135,139,245,68,225,226,124,238,181,134,151,103,246,23,4,9,186,197,223,50,53,196,52,94,22,16' '409': - description: Database conflict - generated another root key for the same KMS provider + description: Database conflict - generated another root key for the same provider content: application/json: schema: @@ -199,7 +199,7 @@ paths: Decrypt a previously generated data encryption key. Provide the `encryptedKey` value that was returned from `/generateDataKey`. - The KMS provider decrypts it and returns the plaintext key. + The KMS/HSM provider decrypts it and returns the plaintext key. Use the plaintext key for decryption operations, then discard it immediately. operationId: decryptDataKey @@ -370,7 +370,7 @@ components: encryptedKey: type: string description: | - Encrypted data key or KMS identifier (comma-separated byte array as string). + Encrypted data key or provider identifier (comma-separated byte array as string). Store this value to decrypt data later using /decryptDataKey. DecryptDataKeyRequest: @@ -422,16 +422,16 @@ components: message: 'Entry with pub MIGeMA0GCSqGSIb3DQEBAQUAA4... and source user not found in database' InternalServerError: - description: Internal server error or KMS operation failure + description: Internal server error or key provider operation failure content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' examples: - kmsFailure: - summary: KMS operation failed + keyProviderFailure: + summary: key provider operation failed value: - message: 'Failed to encrypt private key in KMS. AWS KMS returned InvalidKeyId' + message: 'Failed to encrypt private key. AWS KMS returned InvalidKeyId' databaseFailure: summary: Database operation failed value: diff --git a/masterBitgoExpress.json b/masterBitgoExpress.json index 190add75..6399bae2 100644 --- a/masterBitgoExpress.json +++ b/masterBitgoExpress.json @@ -953,7 +953,7 @@ "/api/v1/{coin}/advancedwallet/generate": { "post": { "summary": "Generates a new advanced wallet.", - "description": "The wallet creation process involves several steps that happen automatically:\n1. User Keychain Creation: Creates the user keychain in the advanced wallet manager and encrypts it with the respective KMS.\n2. Backup Keychain Creation: Creates the backup keychain in the advanced wallet manager and encrypts it with the respective KMS.\n3. Keychain Upload: Uploads the user/backup public keys to BitGo.\n4. BitGo Key Creation: Creates the BitGo key on the BitGo service.\n5. Wallet Creation: Creates the wallet on BitGo with the 3 keys.", + "description": "The wallet creation process involves several steps that happen automatically:\n1. User Keychain Creation: Creates the user keychain in the advanced wallet manager and encrypts it with the respective key provider.\n2. Backup Keychain Creation: Creates the backup keychain in the advanced wallet manager and encrypts it with the respective key provider.\n3. Keychain Upload: Uploads the user/backup public keys to BitGo.\n4. BitGo Key Creation: Creates the BitGo key on the BitGo service.\n5. Wallet Creation: Creates the wallet on BitGo with the 3 keys.", "parameters": [ { "name": "coin", diff --git a/src/__tests__/api/advancedWalletManager/kmsClient.test.ts b/src/__tests__/api/advancedWalletManager/keyProviderClient.test.ts similarity index 74% rename from src/__tests__/api/advancedWalletManager/kmsClient.test.ts rename to src/__tests__/api/advancedWalletManager/keyProviderClient.test.ts index ea43ae78..f585b791 100644 --- a/src/__tests__/api/advancedWalletManager/kmsClient.test.ts +++ b/src/__tests__/api/advancedWalletManager/keyProviderClient.test.ts @@ -12,7 +12,7 @@ describe('postMpcV2Key', () => { let agent: request.SuperAgentTest; // test config - const kmsUrl = 'http://kms.invalid'; + const keyProviderUrl = 'http://key-provider.invalid'; const coin = 'tsol'; const accessToken = 'test-token'; @@ -28,7 +28,7 @@ describe('postMpcV2Key', () => { bind: 'localhost', timeout: 60000, httpLoggerFile: '', - kmsUrl: kmsUrl, + keyProviderUrl: keyProviderUrl, tlsMode: TlsMode.DISABLED, clientCertAllowSelfSigned: true, }; @@ -42,8 +42,8 @@ describe('postMpcV2Key', () => { nock.cleanAll(); }); - it('should bubble up 400 KMS errors', async () => { - nock(kmsUrl).post(/.*/).reply(400, { message: 'This is an error message' }).persist(); + it('should bubble up 400 key provider errors', async () => { + nock(keyProviderUrl).post(/.*/).reply(400, { message: 'This is an error message' }).persist(); const response = await agent .post(`/api/${coin}/mpcv2/initialize`) @@ -55,8 +55,8 @@ describe('postMpcV2Key', () => { response.body.should.have.property('details', 'This is an error message'); }); - it('should bubble up 404 KMS errors', async () => { - nock(kmsUrl).post(/.*/).reply(404, { message: 'This is an error message' }).persist(); + it('should bubble up 404 key provider errors', async () => { + nock(keyProviderUrl).post(/.*/).reply(404, { message: 'This is an error message' }).persist(); const response = await agent .post(`/api/${coin}/mpcv2/initialize`) @@ -68,8 +68,8 @@ describe('postMpcV2Key', () => { response.body.should.have.property('details', 'This is an error message'); }); - it('should bubble up 409 KMS errors', async () => { - nock(kmsUrl).post(/.*/).reply(409, { message: 'This is an error message' }).persist(); + it('should bubble up 409 key provider errors', async () => { + nock(keyProviderUrl).post(/.*/).reply(409, { message: 'This is an error message' }).persist(); const response = await agent .post(`/api/${coin}/mpcv2/initialize`) @@ -81,8 +81,8 @@ describe('postMpcV2Key', () => { response.body.should.have.property('details', 'This is an error message'); }); - it('should bubble up 500 KMS errors', async () => { - nock(kmsUrl).post(/.*/).reply(500, { message: 'This is an error message' }).persist(); + it('should bubble up 500 key provider errors', async () => { + nock(keyProviderUrl).post(/.*/).reply(500, { message: 'This is an error message' }).persist(); const response = await agent .post(`/api/${coin}/mpcv2/initialize`) @@ -94,8 +94,8 @@ describe('postMpcV2Key', () => { response.body.should.have.property('details', 'This is an error message'); }); - it('should handle unexpected KMS errors', async () => { - nock(kmsUrl).post(/.*/).reply(502, { message: 'Unexpected error' }).persist(); + it('should handle unexpected key provider errors', async () => { + nock(keyProviderUrl).post(/.*/).reply(502, { message: 'Unexpected error' }).persist(); const response = await agent .post(`/api/${coin}/mpcv2/initialize`) @@ -106,7 +106,7 @@ describe('postMpcV2Key', () => { response.body.should.have.property('error', 'Internal Server Error'); response.body.should.have.property( 'details', - 'KMS returned unexpected response. 502: Unexpected error', + 'key provider returned unexpected response. 502: Unexpected error', ); }); }); diff --git a/src/__tests__/api/advancedWalletManager/mpcFinalize.test.ts b/src/__tests__/api/advancedWalletManager/mpcFinalize.test.ts index 91dbc41f..6ffc73ec 100644 --- a/src/__tests__/api/advancedWalletManager/mpcFinalize.test.ts +++ b/src/__tests__/api/advancedWalletManager/mpcFinalize.test.ts @@ -14,7 +14,7 @@ describe('MPC Finalize', () => { let agent: request.SuperAgentTest; let app: express.Application; let cfg: AdvancedWalletManagerConfig; - const kmsUrl = 'http://kms.com'; + const keyProviderUrl = 'http://key-provider.test'; let bitgo: BitGoAPI; before(() => { @@ -27,7 +27,7 @@ describe('MPC Finalize', () => { bind: 'localhost', timeout: 60000, httpLoggerFile: '', - kmsUrl: kmsUrl, + keyProviderUrl: keyProviderUrl, tlsMode: TlsMode.DISABLED, clientCertAllowSelfSigned: true, }; @@ -81,13 +81,13 @@ describe('MPC Finalize', () => { }); it('should successfully finalize MPC key generation for user source', async () => { - // Mock data key response from KMS + // Mock data key response from key provider const mockDataKeyResponse = { plaintextKey: '115,23,145,49,185,59,87,165,87,53,233,8,177,59,137,233,118,9,5,73,119,147,55,122,141,249,161,156,19,13,224,101', }; - nock(kmsUrl).post('/decryptDataKey').reply(200, mockDataKeyResponse); - nock(kmsUrl).post('/key').reply(200, { + nock(keyProviderUrl).post('/decryptDataKey').reply(200, mockDataKeyResponse); + nock(keyProviderUrl).post('/key').reply(200, { pub: '821273e7e8ad33fd73d4b924bc83322b5a57027b3db9005355153c57d0512a9e97398fa3ac5f3f0cc6d2fe82c8d8b12c85e8ff572b212aa41ba384201552c9e0', source: 'user', coin: 'tnear', diff --git a/src/__tests__/api/advancedWalletManager/mpcInitialize.test.ts b/src/__tests__/api/advancedWalletManager/mpcInitialize.test.ts index e9e1da3d..e572d8b8 100644 --- a/src/__tests__/api/advancedWalletManager/mpcInitialize.test.ts +++ b/src/__tests__/api/advancedWalletManager/mpcInitialize.test.ts @@ -10,9 +10,9 @@ describe('MPC Initialize', () => { let agent: request.SuperAgentTest; let app: express.Application; let cfg: AdvancedWalletManagerConfig; - const kmsUrl = 'http://kms.com'; + const keyProviderUrl = 'http://key-provider.test'; - // Sample data key response from KMS + // Sample data key response from key provider const mockDataKeyResponse = { plaintextKey: '75,212,73,155,238,206,208,243,103,70,241,121,120,187,188,212,215,169,49,49,158,151,220,182,129,163,146,206,31,176,24,114', @@ -28,7 +28,7 @@ describe('MPC Initialize', () => { bind: 'localhost', timeout: 60000, httpLoggerFile: '', - kmsUrl: kmsUrl, + keyProviderUrl: keyProviderUrl, tlsMode: TlsMode.DISABLED, clientCertAllowSelfSigned: true, }; @@ -42,8 +42,8 @@ describe('MPC Initialize', () => { beforeEach(() => { nock.disableNetConnect(); nock.enableNetConnect('127.0.0.1'); - // Mock KMS service - nock(kmsUrl).post('/generateDataKey').reply(200, mockDataKeyResponse); + // Mock key provider service + nock(keyProviderUrl).post('/generateDataKey').reply(200, mockDataKeyResponse); }); afterEach(() => { diff --git a/src/__tests__/api/advancedWalletManager/nonRecovery.test.ts b/src/__tests__/api/advancedWalletManager/nonRecovery.test.ts index fae1d56c..84c60389 100644 --- a/src/__tests__/api/advancedWalletManager/nonRecovery.test.ts +++ b/src/__tests__/api/advancedWalletManager/nonRecovery.test.ts @@ -19,7 +19,7 @@ describe('Non Recovery', () => { tlsMode: TlsMode.DISABLED, httpLoggerFile: '', clientCertAllowSelfSigned: true, - kmsUrl: 'kms.example.com', + keyProviderUrl: 'key-provider.example.com', }; beforeEach(() => { diff --git a/src/__tests__/api/advancedWalletManager/postIndependentKey.test.ts b/src/__tests__/api/advancedWalletManager/postIndependentKey.test.ts index c33a41f4..14db3d0b 100644 --- a/src/__tests__/api/advancedWalletManager/postIndependentKey.test.ts +++ b/src/__tests__/api/advancedWalletManager/postIndependentKey.test.ts @@ -16,7 +16,7 @@ describe('postIndependentKey', () => { let agent: request.SuperAgentTest; // test cofig - const kmsUrl = 'http://kms.invalid'; + const keyProviderUrl = 'http://key-provider.invalid'; const coin = 'hteth'; const accessToken = 'test-token'; @@ -34,7 +34,7 @@ describe('postIndependentKey', () => { bind: 'localhost', timeout: 60000, httpLoggerFile: '', - kmsUrl: kmsUrl, + keyProviderUrl: keyProviderUrl, tlsMode: TlsMode.DISABLED, clientCertAllowSelfSigned: true, }; @@ -50,14 +50,14 @@ describe('postIndependentKey', () => { // test cases it('should post an independent key successfully', async () => { - const mockKmsResponse = { + const mockKeyProviderResponse = { coin: coin, pub: 'xpub661MyMwAqRbcGAEfZmG74QD11P4dCKRkuwpsJG87QKVPcMdA1PLe76de1Ted54rZ2gyqLYhmdhBCFMrt7AoVwPZwXa3Na9aUnvndvXbvmwu', source: 'user', type: 'independent', }; - const kmsNock = nock(kmsUrl).post(`/key`).reply(200, mockKmsResponse); + const keyProviderNock = nock(keyProviderUrl).post(`/key`).reply(200, mockKeyProviderResponse); const response = await agent .post(`/api/${coin}/key/independent`) @@ -65,11 +65,11 @@ describe('postIndependentKey', () => { .send({ source: 'user' }); response.status.should.equal(200); - response.body.should.have.property('pub', mockKmsResponse.pub); - response.body.should.have.property('coin', mockKmsResponse.coin); - response.body.should.have.property('source', mockKmsResponse.source); + response.body.should.have.property('pub', mockKeyProviderResponse.pub); + response.body.should.have.property('coin', mockKeyProviderResponse.coin); + response.body.should.have.property('source', mockKeyProviderResponse.source); - kmsNock.done(); + keyProviderNock.done(); }); it('should fail to post an independent key if source is not provided', async () => { diff --git a/src/__tests__/api/advancedWalletManager/postMpcV2Key.test.ts b/src/__tests__/api/advancedWalletManager/postMpcV2Key.test.ts index 53b92b48..f6ca5b1b 100644 --- a/src/__tests__/api/advancedWalletManager/postMpcV2Key.test.ts +++ b/src/__tests__/api/advancedWalletManager/postMpcV2Key.test.ts @@ -17,7 +17,7 @@ describe('postMpcV2Key', () => { let agent: request.SuperAgentTest; // test config - const kmsUrl = 'http://kms.invalid'; + const keyProviderUrl = 'http://key-provider.invalid'; const coin = 'hteth'; const accessToken = 'test-token'; @@ -36,7 +36,7 @@ describe('postMpcV2Key', () => { bind: 'localhost', timeout: 60000, httpLoggerFile: '', - kmsUrl: kmsUrl, + keyProviderUrl: keyProviderUrl, tlsMode: TlsMode.DISABLED, clientCertAllowSelfSigned: true, }; @@ -49,22 +49,22 @@ describe('postMpcV2Key', () => { }); beforeEach(() => { - // nocks for KMS responses - nock(kmsUrl) + // nocks for key provider responses + nock(keyProviderUrl) .post(`/generateDataKey`) .reply(200, { plaintextKey: 'test-plaintext-key', encryptedKey: 'test-encrypted-key', }) .persist(); - nock(kmsUrl) + nock(keyProviderUrl) .post(`/decryptDataKey`) .reply(200, { plaintextKey: 'test-plaintext-key', }) .persist(); - nock(kmsUrl) + nock(keyProviderUrl) .post(`/key`) .reply(200, { pub: 'test-pub-key', @@ -74,7 +74,7 @@ describe('postMpcV2Key', () => { }) .persist(); - nock(kmsUrl) + nock(keyProviderUrl) .post(`/key`) .reply(200, { pub: 'test-pub-key', @@ -84,7 +84,7 @@ describe('postMpcV2Key', () => { }) .persist(); - nock(kmsUrl).post(`/postKey`).reply(200, {}).persist(); + nock(keyProviderUrl).post(`/postKey`).reply(200, {}).persist(); }); afterEach(() => { diff --git a/src/__tests__/api/advancedWalletManager/recoveryMpc.test.ts b/src/__tests__/api/advancedWalletManager/recoveryMpc.test.ts index c019310f..24526026 100644 --- a/src/__tests__/api/advancedWalletManager/recoveryMpc.test.ts +++ b/src/__tests__/api/advancedWalletManager/recoveryMpc.test.ts @@ -8,7 +8,7 @@ describe('recoveryMpc', () => { let agent: request.SuperAgentTest; // test config - const kmsUrl = 'http://kms.invalid'; + const keyProviderUrl = 'http://key-provider.invalid'; const sol = 'tsol'; const sui = 'tsui'; const accessToken = 'test-token'; @@ -22,7 +22,7 @@ describe('recoveryMpc', () => { port: 0, // Let OS assign a free port bind: 'localhost', timeout: 60000, - kmsUrl: kmsUrl, + keyProviderUrl: keyProviderUrl, httpLoggerFile: '', tlsMode: TlsMode.DISABLED, @@ -43,30 +43,30 @@ describe('recoveryMpc', () => { describe('ECDSA sol recovery', () => { it('should successfully generate MPC solana transactions', async () => { - const mockKmsUserResponse = { + const mockKeyProviderUserResponse = { prv: '{"uShare":{"i":1,"t":2,"n":3,"y":"85aa6462d927329418f70f6d0863cf6cf33e7da2934f935e5927f1b13062d779","seed":"2f55c80fd6b5583dcde8037b2ee461d2e7d445a4d3e7a9b2a0d3d00b5f534169","chaincode":"66e80f2bf41a5706608352d51ceb07a5aa1729cab6c6993c124d5731546ed9a1"},"bitgoYShare":{"i":1,"j":3,"y":"483e53b72de3aa893df698d0b20b20777fb3d2716cc8483a9e9797174fd52b16","v":"e70696459e46434a2a12cc988e3ae714a61fe96da8a6764d058b849cab50d6dc","u":"49abf8144d265a77cf6d098eff784d6ce56ec77a182f6b39f47d5d8e28f2a802","chaincode":"797348468202f1d7fede0a7851f80162b02e7da306e65075dd864b6789b9bc5b"},"backupYShare":{"i":1,"j":2,"y":"249a9798d0064a989a16cd8f479edf09ffaee73f4175d2ac555ba90ff41b89da","v":"98e31d2b643e40060ba344c6a41fc096ea7e39a1ae879f65e4af645870e90ee0","u":"ac047b1bceab2e1a42d97ab540b39176e545d9c0af4a192aee8e1dae91a4240b","chaincode":"585bdc05c8f84802cbe7b9a1a07d4aa9c5fede93597a622854e9bad83a2d5b78"}}', pub: 'b6f5fb808f538a32735a89609e98fab75690a2c79b26f50a54c4cbf0fbca287138b733783f1590e12b4916ef0f6053b22044860117274bda44bd5d711855f174', source: 'user', type: 'tss', }; - const mockKmsBackupResponse = { + const mockKeyProviderBackupResponse = { prv: '{"uShare":{"i":2,"t":2,"n":3,"y":"249a9798d0064a989a16cd8f479edf09ffaee73f4175d2ac555ba90ff41b89da","seed":"abab5be2b32d07cf39b2a162af0f78bad8325b2fbdc89d14fd8b4e5767b74097","chaincode":"585bdc05c8f84802cbe7b9a1a07d4aa9c5fede93597a622854e9bad83a2d5b78"},"bitgoYShare":{"i":2,"j":3,"y":"483e53b72de3aa893df698d0b20b20777fb3d2716cc8483a9e9797174fd52b16","v":"e70696459e46434a2a12cc988e3ae714a61fe96da8a6764d058b849cab50d6dc","u":"eb54da28da3da22eb3d61797a02a96264be8940b7115aefbb90b9dd044db7f06","chaincode":"797348468202f1d7fede0a7851f80162b02e7da306e65075dd864b6789b9bc5b"},"userYShare":{"i":2,"j":1,"y":"85aa6462d927329418f70f6d0863cf6cf33e7da2934f935e5927f1b13062d779","v":"76cfdcbf0f769f21c64e0faf0072ebccbcc3aaa844522336af27f8e50ed7ca5f","u":"6ce814af82683423c8d8befd13f6eeeb0cd3f7274d1ebfdd5807fd2e4eaadb08","chaincode":"66e80f2bf41a5706608352d51ceb07a5aa1729cab6c6993c124d5731546ed9a1"}}', pub: 'b6f5fb808f538a32735a89609e98fab75690a2c79b26f50a54c4cbf0fbca287138b733783f1590e12b4916ef0f6053b22044860117274bda44bd5d711855f174', source: 'backup', type: 'tss', }; - nock(kmsUrl) - .get(`/key/${mockKmsUserResponse.pub}`) + nock(keyProviderUrl) + .get(`/key/${mockKeyProviderUserResponse.pub}`) .query({ source: 'user' }) - .reply(200, mockKmsUserResponse) + .reply(200, mockKeyProviderUserResponse) .persist(); - nock(kmsUrl) - .get(`/key/${mockKmsBackupResponse.pub}`) + nock(keyProviderUrl) + .get(`/key/${mockKeyProviderBackupResponse.pub}`) .query({ source: 'backup' }) - .reply(200, mockKmsBackupResponse) + .reply(200, mockKeyProviderBackupResponse) .persist(); const input = { @@ -95,22 +95,22 @@ describe('recoveryMpc', () => { nock.cleanAll(); }); - it('should throw 500 Internal Server Error if KMS cannot find user or backup keys', async () => { + it('should throw 500 Internal Server Error if key provider cannot find user or backup keys', async () => { const commonKeychain = 'b6f5fb808f538a32735a89609e98fab75690a2c79b26f50a54c4cbf0fbca287138b733783f1590e12b4916ef0f6053b22044860117274bda44bd5d711855f174'; - const mockKmsUserResponse = {}; - const mockKmsBackupResponse = {}; + const mockKeyProviderUserResponse = {}; + const mockKeyProviderBackupResponse = {}; - nock(kmsUrl) + nock(keyProviderUrl) .get(`/key/${commonKeychain}`) .query({ source: 'user' }) - .reply(200, mockKmsUserResponse) + .reply(200, mockKeyProviderUserResponse) .persist(); - nock(kmsUrl) + nock(keyProviderUrl) .get(`/key/${commonKeychain}`) .query({ source: 'backup' }) - .reply(200, mockKmsBackupResponse) + .reply(200, mockKeyProviderBackupResponse) .persist(); const input = { @@ -143,30 +143,30 @@ describe('recoveryMpc', () => { describe('ECDSA sui recovery', () => { it('should successfully generate MPC sui transactions', async () => { - const mockKmsUserResponse = { + const mockKeyProviderUserResponse = { prv: '{"uShare":{"i":1,"t":2,"n":3,"y":"8e10c0d10fb8a5780bba0f62fb86e2a80fd6f04b348985e9174a4f4f66e1baf5","seed":"368eab02c210effbb345d8c3cbe3d00b61292071feb0eafe26d9ce6060145d7b","chaincode":"e32078c8ba161f4c6c10b01abdf8203aed06878bae6a90906be228fd1b196b18"},"bitgoYShare":{"i":1,"j":3,"y":"532ee5aa5be82b9e64c10ff98d3be21901888bbba2293f0db429b8737cbf94ca","v":"72b2a6d7243654f4e80b050f5fa0c9de505b37ebcd7803dfacf22ba8bc60716a","u":"b13b791180c1148405f2137f011831ec5ffd4dd2584f7bef4f5a174debe8df08","chaincode":"87292c10bbdfd6f15e80dd96f3b530aaf0b7b99d933b3d0b61b7a1565b0a89f9"},"backupYShare":{"i":1,"j":2,"y":"5119753ee78a9f43ed2df6bc79d1d69954ada568dce7c3de6aa031ea2f951967","v":"852459fcfe7170cb433cc98115fb2a292b3301834ce89b0e843ad952cca885d1","u":"0854f68dbf402bc928afb86448eab19fbe90fcb387103d1fff35d02af18b230d","chaincode":"e09a186e387c74144c636793294a1c38017e9e4d5c5156f6b6fc824fe90fdaf0"}}', pub: 'f2b50b246be21f9819cdf08c721cd5d2dfb01efed33c65abd9030703609eef4c4ae3bd47ae726a5216f4f544daf76d1ddf3cdf769df7249284964ca35f33d001', source: 'user', type: 'tss', }; - const mockKmsBackupResponse = { + const mockKeyProviderBackupResponse = { prv: '{"uShare":{"i":2,"t":2,"n":3,"y":"5119753ee78a9f43ed2df6bc79d1d69954ada568dce7c3de6aa031ea2f951967","seed":"e70e6fd6f914b7b854e7b5ab46fff52a530dc3d10aa5a71c2b32559ea349ac4e","chaincode":"e09a186e387c74144c636793294a1c38017e9e4d5c5156f6b6fc824fe90fdaf0"},"bitgoYShare":{"i":2,"j":3,"y":"532ee5aa5be82b9e64c10ff98d3be21901888bbba2293f0db429b8737cbf94ca","v":"72b2a6d7243654f4e80b050f5fa0c9de505b37ebcd7803dfacf22ba8bc60716a","u":"709cfacb0bfa99c38ff319d16e4e2b34ecd8e40025496d21d2932204b785e80d","chaincode":"87292c10bbdfd6f15e80dd96f3b530aaf0b7b99d933b3d0b61b7a1565b0a89f9"},"userYShare":{"i":2,"j":1,"y":"8e10c0d10fb8a5780bba0f62fb86e2a80fd6f04b348985e9174a4f4f66e1baf5","v":"a81795570884a88dc7586c9a49a632a2aea0859eae622c51617387b85e8b5b0a","u":"0857dfc00a1fe3a4a41a752e933788becc6b23c2f5637393906f79b89aa50d0d","chaincode":"e32078c8ba161f4c6c10b01abdf8203aed06878bae6a90906be228fd1b196b18"}}', pub: 'f2b50b246be21f9819cdf08c721cd5d2dfb01efed33c65abd9030703609eef4c4ae3bd47ae726a5216f4f544daf76d1ddf3cdf769df7249284964ca35f33d001', source: 'backup', type: 'tss', }; - nock(kmsUrl) - .get(`/key/${mockKmsUserResponse.pub}`) + nock(keyProviderUrl) + .get(`/key/${mockKeyProviderUserResponse.pub}`) .query({ source: 'user' }) - .reply(200, mockKmsUserResponse) + .reply(200, mockKeyProviderUserResponse) .persist(); - nock(kmsUrl) - .get(`/key/${mockKmsBackupResponse.pub}`) + nock(keyProviderUrl) + .get(`/key/${mockKeyProviderBackupResponse.pub}`) .query({ source: 'backup' }) - .reply(200, mockKmsBackupResponse) + .reply(200, mockKeyProviderBackupResponse) .persist(); const input = { diff --git a/src/__tests__/api/advancedWalletManager/recoveryMpcV2.test.ts b/src/__tests__/api/advancedWalletManager/recoveryMpcV2.test.ts index e59bc170..9f6a36ea 100644 --- a/src/__tests__/api/advancedWalletManager/recoveryMpcV2.test.ts +++ b/src/__tests__/api/advancedWalletManager/recoveryMpcV2.test.ts @@ -15,7 +15,7 @@ describe('recoveryMpcV2', async () => { let agent: request.SuperAgentTest; // test config - const kmsUrl = 'http://kms.invalid'; + const keyProviderUrl = 'http://key-provider.invalid'; const ethLikeCoin = 'hteth'; const cosmosLikeCoin = 'tsei'; const accessToken = 'test-token'; @@ -23,20 +23,20 @@ describe('recoveryMpcV2', async () => { // sinon stubs let configStub: sinon.SinonStub; - // kms nocks setup + // key provider nocks setup const [userShare, backupShare] = await DklsUtils.generateDKGKeyShares(); const userKeyShare = userShare.getKeyShare().toString('base64'); const backupKeyShare = backupShare.getKeyShare().toString('base64'); const commonKeychain = DklsTypes.getCommonKeychain(userShare.getKeyShare()); - const mockKmsUserResponse = { + const mockKeyProviderUserResponse = { prv: JSON.stringify(userKeyShare), pub: commonKeychain, source: 'user', type: 'tss', }; - const mockKmsBackupResponse = { + const mockKeyProviderBackupResponse = { prv: JSON.stringify(backupKeyShare), pub: commonKeychain, source: 'backup', @@ -59,7 +59,7 @@ describe('recoveryMpcV2', async () => { port: 0, // Let OS assign a free port bind: 'localhost', timeout: 60000, - kmsUrl: kmsUrl, + keyProviderUrl: keyProviderUrl, httpLoggerFile: '', tlsMode: TlsMode.DISABLED, clientCertAllowSelfSigned: true, @@ -83,16 +83,16 @@ describe('recoveryMpcV2', async () => { // happy path test it('should be sign a Mpc V2 Recovery', async () => { - // nocks for KMS responses - const userKmsNock = nock(kmsUrl) + // nocks for key provider responses + const userKeyProviderNock = nock(keyProviderUrl) .get(`/key/${input.pub}`) .query({ source: 'user' }) - .reply(200, mockKmsUserResponse) + .reply(200, mockKeyProviderUserResponse) .persist(); - const backupKmsNock = nock(kmsUrl) + const backupKeyProviderNock = nock(keyProviderUrl) .get(`/key/${input.pub}`) .query({ source: 'backup' }) - .reply(200, mockKmsBackupResponse) + .reply(200, mockKeyProviderBackupResponse) .persist(); const ethLikeSignatureResponse = await agent @@ -127,8 +127,8 @@ describe('recoveryMpcV2', async () => { cosmosLikeSignature.should.have.property('s'); cosmosLikeSignature.should.have.property('y'); - userKmsNock.isDone().should.be.true(); - backupKmsNock.isDone().should.be.true(); + userKeyProviderNock.isDone().should.be.true(); + backupKeyProviderNock.isDone().should.be.true(); }); // failure test case @@ -138,12 +138,15 @@ describe('recoveryMpcV2', async () => { pub: commonKeychain, }; - // nocks for KMS responses - nock(kmsUrl).get(`/key/${input.pub}`).query({ source: 'user' }).reply(200, mockKmsUserResponse); - nock(kmsUrl) + // nocks for key provider responses + nock(keyProviderUrl) + .get(`/key/${input.pub}`) + .query({ source: 'user' }) + .reply(200, mockKeyProviderUserResponse); + nock(keyProviderUrl) .get(`/key/${input.pub}`) .query({ source: 'backup' }) - .reply(200, mockKmsBackupResponse); + .reply(200, mockKeyProviderBackupResponse); const signatureResponse = await agent .post(`/api/${ethLikeCoin}/mpcv2/recovery`) diff --git a/src/__tests__/api/advancedWalletManager/recoveryMultisigTransaction.test.ts b/src/__tests__/api/advancedWalletManager/recoveryMultisigTransaction.test.ts index a1cafad2..b8f7d2ad 100644 --- a/src/__tests__/api/advancedWalletManager/recoveryMultisigTransaction.test.ts +++ b/src/__tests__/api/advancedWalletManager/recoveryMultisigTransaction.test.ts @@ -7,11 +7,11 @@ import sinon from 'sinon'; import * as middleware from '../../../shared/middleware'; import { BitGoRequest } from '../../../types/request'; import { BitGoAPI as BitGo } from '@bitgo-beta/sdk-api'; -import * as kmsUtils from '../../../advancedWalletManager/handlers/utils/utils'; +import * as keyProviderUtils from '../../../advancedWalletManager/handlers/utils/utils'; describe('UTXO recovery', () => { let agent: request.SuperAgentTest; - let mockRetrieveKmsPrvKey: sinon.SinonStub; + let mockRetrieveKeyProviderPrvKey: sinon.SinonStub; const coin = 'tbtc'; const config: AdvancedWalletManagerConfig = { appMode: AppMode.ADVANCED_WALLET_MANAGER, @@ -21,7 +21,7 @@ describe('UTXO recovery', () => { httpLoggerFile: '', tlsMode: TlsMode.DISABLED, clientCertAllowSelfSigned: true, - kmsUrl: 'kms.example.com', + keyProviderUrl: 'key-provider.example.com', recoveryMode: true, }; @@ -42,9 +42,9 @@ describe('UTXO recovery', () => { next(); }); - // Mock KMS key retrieval - mockRetrieveKmsPrvKey = sinon.stub(kmsUtils, 'retrieveKmsPrvKey'); - mockRetrieveKmsPrvKey + // Mock key provider key retrieval + mockRetrieveKeyProviderPrvKey = sinon.stub(keyProviderUtils, 'retrieveKeyProviderPrvKey'); + mockRetrieveKeyProviderPrvKey .withArgs({ pub: 'xpub661MyMwAqRbcF3g1sUm7T5pN8ViCr9bS6XiQbq7dVXFdPEGYfhGgjjV2AFxTYVWik29y7NHmCZjWYDkt4RGw57HNYpHnoHeeqJV6s8hwcsV', source: 'user', @@ -54,7 +54,7 @@ describe('UTXO recovery', () => { 'xprv9s21ZrQH143K2ZbYmTE75wsdaTsiSgsajJnooSi1wBieWRwQ89xSBwAYK1VJR795Y8XFCCXYHHs4sk2Heg6dkX3CHMBq5bw8DwBWByWx883', ); - mockRetrieveKmsPrvKey + mockRetrieveKeyProviderPrvKey .withArgs({ pub: 'xpub661MyMwAqRbcEywGPF6Pg1FDUtHGyxsn7nph8dcy8GFLKvQ8hSCKgUm8sNbJhegDbmLtMpMnGZtrqfRXCjeDtfJ2UGDSzNTkRuvAQ5KNPcH', source: 'backup', @@ -116,15 +116,15 @@ describe('UTXO recovery', () => { '01000000000101edd7a583fef5aabf265e6dca24452581a3cca2671a1fa6b4e404bccb6ff4c83b0000000000ffffffff01780f0000000000002200202120dcf53e62a4cc9d3843993aa2258bd14fbf911a4ea4cf4f3ac840f41702790400473044022043a9256810ef47ce36a092305c0b1ef675bce53e46418eea8cacbf1643e541d90220450766e048b841dac658d0a2ba992628bfe131dff078c3a574cadf67b4946647014730440220360045a15e459ed44aa3e52b86dd6a16dddaf319821f4dcc15627686f377edd102205cb3d5feab1a773c518d43422801e01dd1bc586bb09f6a9ed23a1fc0cfeeb5310169522103a1c425fd9b169e6ab5ed3de596acb777ccae0cda3d91256238b5e739a3f14aae210222a76697605c890dc4365132f9ae0d351952a1aad7eecf78d9923766dbe74a1e21033b21c0758ffbd446204914fa1d1c5921e9f82c2671dac89737666aa9375973e953ae00000000', ); - // Verify KMS key retrieval - mockRetrieveKmsPrvKey + // Verify key provider key retrieval + mockRetrieveKeyProviderPrvKey .calledWith({ pub: userPub, source: 'user', cfg: config, }) .should.be.true(); - mockRetrieveKmsPrvKey + mockRetrieveKeyProviderPrvKey .calledWith({ pub: backupPub, source: 'backup', diff --git a/src/__tests__/api/advancedWalletManager/recoveryMusigEth.test.ts b/src/__tests__/api/advancedWalletManager/recoveryMusigEth.test.ts index dadd11cf..cb07cd0d 100644 --- a/src/__tests__/api/advancedWalletManager/recoveryMusigEth.test.ts +++ b/src/__tests__/api/advancedWalletManager/recoveryMusigEth.test.ts @@ -18,7 +18,7 @@ describe('recoveryMultisigTransaction', () => { let agent: request.SuperAgentTest; // test cofig - const kmsUrl = 'http://kms.invalid'; + const keyProviderUrl = 'http://key-provider.invalid'; const coin = 'hteth'; const accessToken = 'test-token'; @@ -37,7 +37,7 @@ describe('recoveryMultisigTransaction', () => { bind: 'localhost', timeout: 60000, httpLoggerFile: '', - kmsUrl: kmsUrl, + keyProviderUrl: keyProviderUrl, tlsMode: TlsMode.DISABLED, clientCertAllowSelfSigned: true, recoveryMode: true, @@ -62,29 +62,29 @@ describe('recoveryMultisigTransaction', () => { const { userPub, backupPub, walletContractAddress, userPrv, backupPrv, txHexResult } = awmData; const unsignedSweepPrebuildTx = unsignedSweepRecJSON as unknown as any; - const mockKmsUserResponse = { + const mockKeyProviderUserResponse = { prv: userPrv, pub: userPub, source: 'user', type: 'independent', }; - const mockKmsBackupResponse = { + const mockKeyProviderBackupResponse = { prv: backupPrv, pub: backupPub, source: 'backup', type: 'independent', }; - const kmsNockUser = nock(kmsUrl) + const keyProviderNockUser = nock(keyProviderUrl) .get(`/key/${userPub}`) .query({ source: 'user' }) - .reply(200, mockKmsUserResponse); + .reply(200, mockKeyProviderUserResponse); - const kmsNockBackup = nock(kmsUrl) + const keyProviderNockBackup = nock(keyProviderUrl) .get(`/key/${backupPub}`) .query({ source: 'backup' }) - .reply(200, mockKmsBackupResponse); + .reply(200, mockKeyProviderBackupResponse); const response = await agent .post(`/api/${coin}/multisig/recovery`) @@ -101,8 +101,8 @@ describe('recoveryMultisigTransaction', () => { response.status.should.equal(200); response.body.should.have.property('txHex', txHexResult); - kmsNockUser.done(); - kmsNockBackup.done(); + keyProviderNockUser.done(); + keyProviderNockBackup.done(); }); it('should fail when prv keys non related to pub keys', async () => { @@ -113,29 +113,29 @@ describe('recoveryMultisigTransaction', () => { const invalidUserPrv = 'invalid-prv'; const invalidBackupPrv = 'invalid-prv'; - const mockKmsUserResponse = { + const mockKeyProviderUserResponse = { prv: invalidUserPrv, pub: userPub, source: 'user', type: 'independent', }; - const mockKmsBackupResponse = { + const mockKeyProviderBackupResponse = { prv: invalidBackupPrv, pub: backupPub, source: 'backup', type: 'independent', }; - const kmsNockUser = nock(kmsUrl) + const keyProviderNockUser = nock(keyProviderUrl) .get(`/key/${userPub}`) .query({ source: 'user' }) - .reply(200, mockKmsUserResponse); + .reply(200, mockKeyProviderUserResponse); - const kmsNockBackup = nock(kmsUrl) + const keyProviderNockBackup = nock(keyProviderUrl) .get(`/key/${backupPub}`) .query({ source: 'backup' }) - .reply(200, mockKmsBackupResponse); + .reply(200, mockKeyProviderBackupResponse); const response = await agent .post(`/api/${coin}/multisig/recovery`) @@ -152,7 +152,7 @@ describe('recoveryMultisigTransaction', () => { response.status.should.equal(500); response.body.should.have.property('error'); - kmsNockUser.done(); - kmsNockBackup.done(); + keyProviderNockUser.done(); + keyProviderNockBackup.done(); }); }); diff --git a/src/__tests__/api/advancedWalletManager/signMpcRecoveryTransaction.test.ts b/src/__tests__/api/advancedWalletManager/signMpcRecoveryTransaction.test.ts index 2c514ac9..47da8681 100644 --- a/src/__tests__/api/advancedWalletManager/signMpcRecoveryTransaction.test.ts +++ b/src/__tests__/api/advancedWalletManager/signMpcRecoveryTransaction.test.ts @@ -3,14 +3,14 @@ import nock from 'nock'; import sinon from 'sinon'; import supertest from 'supertest'; import { Utils } from '@bitgo-beta/sdk-coin-sol'; -import * as kmsUtils from '../../../advancedWalletManager/handlers/utils/utils'; +import * as keyProviderUtils from '../../../advancedWalletManager/handlers/utils/utils'; import { app as expressApp } from '../../../advancedWalletManagerApp'; import { AppMode, AdvancedWalletManagerConfig, TlsMode } from '../../../shared/types'; describe('EdDSA Recovery Signing', () => { let agent: supertest.SuperTest; const config: AdvancedWalletManagerConfig = { - kmsUrl: 'http://localhost:3000', + keyProviderUrl: 'http://localhost:3000', appMode: AppMode.ADVANCED_WALLET_MANAGER, port: 0, bind: 'localhost', @@ -93,9 +93,9 @@ describe('EdDSA Recovery Signing', () => { }); it('should successfully sign a Solana recovery transaction', async () => { - // Mock KMS key retrieval - const mockRetrieveKmsPrvKey = sinon.stub(kmsUtils, 'retrieveKmsPrvKey'); - mockRetrieveKmsPrvKey + // Mock key provider key retrieval + const mockRetrieveKeyProviderPrvKey = sinon.stub(keyProviderUtils, 'retrieveKeyProviderPrvKey'); + mockRetrieveKeyProviderPrvKey .withArgs({ pub: commonKeychain, source: 'user', @@ -103,7 +103,7 @@ describe('EdDSA Recovery Signing', () => { }) .resolves(JSON.stringify(userPrvShare)); - mockRetrieveKmsPrvKey + mockRetrieveKeyProviderPrvKey .withArgs({ pub: commonKeychain, source: 'backup', @@ -128,8 +128,8 @@ describe('EdDSA Recovery Signing', () => { response.body.should.have.property('txHex'); Utils.validateRawTransaction(response.body.txHex, true, true); - // Verify KMS key retrieval calls - mockRetrieveKmsPrvKey + // Verify key provider key retrieval calls + mockRetrieveKeyProviderPrvKey .calledWith({ pub: commonKeychain, source: 'user', @@ -137,7 +137,7 @@ describe('EdDSA Recovery Signing', () => { }) .should.be.true(); - mockRetrieveKmsPrvKey + mockRetrieveKeyProviderPrvKey .calledWith({ pub: commonKeychain, source: 'backup', @@ -147,8 +147,8 @@ describe('EdDSA Recovery Signing', () => { }); it('should fail if user private key is missing', async () => { - const mockRetrieveKmsPrvKey = sinon.stub(kmsUtils, 'retrieveKmsPrvKey'); - mockRetrieveKmsPrvKey + const mockRetrieveKeyProviderPrvKey = sinon.stub(keyProviderUtils, 'retrieveKeyProviderPrvKey'); + mockRetrieveKeyProviderPrvKey .withArgs({ pub: commonKeychain, source: 'user', @@ -174,8 +174,8 @@ describe('EdDSA Recovery Signing', () => { }); it('should fail if backup private key is missing', async () => { - const mockRetrieveKmsPrvKey = sinon.stub(kmsUtils, 'retrieveKmsPrvKey'); - mockRetrieveKmsPrvKey + const mockRetrieveKeyProviderPrvKey = sinon.stub(keyProviderUtils, 'retrieveKeyProviderPrvKey'); + mockRetrieveKeyProviderPrvKey .withArgs({ pub: commonKeychain, source: 'user', @@ -183,7 +183,7 @@ describe('EdDSA Recovery Signing', () => { }) .resolves(JSON.stringify(userPrvShare)); - mockRetrieveKmsPrvKey + mockRetrieveKeyProviderPrvKey .withArgs({ pub: commonKeychain, source: 'backup', diff --git a/src/__tests__/api/advancedWalletManager/signMpcTransaction.test.ts b/src/__tests__/api/advancedWalletManager/signMpcTransaction.test.ts index 5c567918..3054f966 100644 --- a/src/__tests__/api/advancedWalletManager/signMpcTransaction.test.ts +++ b/src/__tests__/api/advancedWalletManager/signMpcTransaction.test.ts @@ -22,7 +22,7 @@ describe('signMpcTransaction', () => { let agent: request.SuperAgentTest; // test config - const kmsUrl = 'http://kms.invalid'; + const keyProviderUrl = 'http://key-provider.invalid'; const coin = 'tsol'; const accessToken = 'test-token'; @@ -41,7 +41,7 @@ describe('signMpcTransaction', () => { bind: 'localhost', timeout: 60000, httpLoggerFile: '', - kmsUrl: kmsUrl, + keyProviderUrl: keyProviderUrl, tlsMode: TlsMode.DISABLED, clientCertAllowSelfSigned: true, }; @@ -110,7 +110,7 @@ describe('signMpcTransaction', () => { backupYShare: backup.yShares[1], }; - const mockKmsResponse = { + const mockKeyProviderResponse = { prv: JSON.stringify(userSigningMaterial), pub: 'DSqMPMsMAbEJVNuPKv1ZFdzt6YvJaDPDddfeW7ajtqds', source: 'user', @@ -129,13 +129,15 @@ describe('signMpcTransaction', () => { encryptedKey: 'mock-encrypted-data-key', }; - // Mock KMS responses - const kmsNock = nock(kmsUrl) + // Mock key provider responses + const keyProviderNock = nock(keyProviderUrl) .get(`/key/${input.pub}`) .query({ source: 'user' }) - .reply(200, mockKmsResponse); + .reply(200, mockKeyProviderResponse); - const dataKeyNock = nock(kmsUrl).post('/generateDataKey').reply(200, mockDataKeyResponse); + const dataKeyNock = nock(keyProviderUrl) + .post('/generateDataKey') + .reply(200, mockDataKeyResponse); const response = await agent .post(`/api/${coin}/mpc/sign/commitment`) @@ -148,7 +150,7 @@ describe('signMpcTransaction', () => { response.body.should.have.property('encryptedUserToBitgoRShare'); response.body.should.have.property('encryptedDataKey'); - kmsNock.done(); + keyProviderNock.done(); dataKeyNock.done(); // Continue with R share test using the returned encryptedUserToBitgoRShare @@ -167,13 +169,13 @@ describe('signMpcTransaction', () => { plaintextKey: 'mock-plaintext-data-key', }; - // Mock KMS responses for R share - const rKmsNock = nock(kmsUrl) + // Mock key provider responses for R share + const rKeyProviderNock = nock(keyProviderUrl) .get(`/key/${rInput.pub}`) .query({ source: 'user' }) - .reply(200, mockKmsResponse); + .reply(200, mockKeyProviderResponse); - const decryptDataKeyNock = nock(kmsUrl) + const decryptDataKeyNock = nock(keyProviderUrl) .post('/decryptDataKey') .reply(200, mockDecryptedDataKeyResponse); @@ -185,7 +187,7 @@ describe('signMpcTransaction', () => { rResponse.status.should.equal(200); rResponse.body.should.have.property('rShare'); - rKmsNock.done(); + rKeyProviderNock.done(); decryptDataKeyNock.done(); // Continue with G share test using the returned rShare @@ -229,11 +231,11 @@ describe('signMpcTransaction', () => { bitgoToUserCommitment: bitgoToUserCommitmentShare, }; - // Mock KMS response for G share - const gKmsNock = nock(kmsUrl) + // Mock key provider response for G share + const gKeyProviderNock = nock(keyProviderUrl) .get(`/key/${gInput.pub}`) .query({ source: 'user' }) - .reply(200, mockKmsResponse); + .reply(200, mockKeyProviderResponse); const gResponse = await agent .post(`/api/${coin}/mpc/sign/g`) @@ -247,10 +249,10 @@ describe('signMpcTransaction', () => { gResponse.body.gShare.should.have.property('gamma'); gResponse.body.gShare.should.have.property('R'); - gKmsNock.done(); + gKeyProviderNock.done(); }); - it('should fail when KMS returns no private key', async () => { + it('should fail when key provider returns no private key', async () => { const input = { source: 'user', pub: 'DSqMPMsMAbEJVNuPKv1ZFdzt6YvJaDPDddfeW7ajtqds', @@ -258,7 +260,7 @@ describe('signMpcTransaction', () => { bitgoGpgPubKey: bitgoGpgPubKey, }; - const kmsNock = nock(kmsUrl) + const keyProviderNock = nock(keyProviderUrl) .get(`/key/${input.pub}`) .query({ source: 'user' }) .reply(404, { error: 'Key not found' }); @@ -270,7 +272,7 @@ describe('signMpcTransaction', () => { response.status.should.equal(500); response.body.should.have.property('error'); - kmsNock.done(); + keyProviderNock.done(); }); it('should fail for unsupported share type', async () => { @@ -319,7 +321,7 @@ describe('signMpcTransaction', () => { const userKeyShare = userShare.getKeyShare().toString('base64'); - const mockKmsResponse = { + const mockKeyProviderResponse = { prv: JSON.stringify(userKeyShare), pub: 'mock-ecdsa-public-key', source: 'user', @@ -369,13 +371,15 @@ describe('signMpcTransaction', () => { encryptedKey: 'mock-encrypted-data-key', }; - // Mock KMS responses for Round 1 - const kmsNock = nock(kmsUrl) + // Mock key provider responses for Round 1 + const keyProviderNock = nock(keyProviderUrl) .get(`/key/${round1Input.pub}`) .query({ source: 'user' }) - .reply(200, mockKmsResponse); + .reply(200, mockKeyProviderResponse); - const dataKeyNock = nock(kmsUrl).post('/generateDataKey').reply(200, mockDataKeyResponse); + const dataKeyNock = nock(keyProviderUrl) + .post('/generateDataKey') + .reply(200, mockDataKeyResponse); /* Signing Round 1 with User Key */ const round1Response = await agent @@ -390,7 +394,7 @@ describe('signMpcTransaction', () => { round1Response.body.should.have.property('encryptedUserGpgPrvKey'); round1Response.body.should.have.property('encryptedDataKey'); - kmsNock.done(); + keyProviderNock.done(); dataKeyNock.done(); /* Signing Round 1 with Bitgo Key */ @@ -431,13 +435,13 @@ describe('signMpcTransaction', () => { plaintextKey: 'mock-plaintext-data-key', }; - // Mock KMS responses for Round 2 - const r2KmsNock = nock(kmsUrl) + // Mock key provider responses for Round 2 + const r2KeyProviderNock = nock(keyProviderUrl) .get(`/key/${round2Input.pub}`) .query({ source: 'user' }) - .reply(200, mockKmsResponse); + .reply(200, mockKeyProviderResponse); - const decryptDataKeyNock = nock(kmsUrl) + const decryptDataKeyNock = nock(keyProviderUrl) .post('/decryptDataKey') .reply(200, mockDecryptedDataKeyResponse); @@ -449,7 +453,7 @@ describe('signMpcTransaction', () => { round2Response.status.should.equal(200); round2Response.body.should.have.property('signatureShareRound2'); round2Response.body.should.have.property('encryptedRound2Session'); - r2KmsNock.done(); + r2KeyProviderNock.done(); decryptDataKeyNock.done(); // Round 2 Signing with Bitgo Key @@ -479,13 +483,13 @@ describe('signMpcTransaction', () => { encryptedRound2Session, }; - // Mock KMS responses for Round 3 - const r3KmsNock = nock(kmsUrl) + // Mock key provider responses for Round 3 + const r3KeyProviderNock = nock(keyProviderUrl) .get(`/key/${round3Input.pub}`) .query({ source: 'user' }) - .reply(200, mockKmsResponse); + .reply(200, mockKeyProviderResponse); - const r3DecryptDataKeyNock = nock(kmsUrl) + const r3DecryptDataKeyNock = nock(keyProviderUrl) .post('/decryptDataKey') .reply(200, mockDecryptedDataKeyResponse); @@ -497,7 +501,7 @@ describe('signMpcTransaction', () => { round3Response.status.should.equal(200); round3Response.body.should.have.property('signatureShareRound3'); - r3KmsNock.done(); + r3KeyProviderNock.done(); r3DecryptDataKeyNock.done(); const { userMsg4 } = await signBitgoMPCv2Round3( @@ -548,7 +552,7 @@ describe('signMpcTransaction', () => { }); it('should fail when required fields are missing for Round 2', async () => { - const mockKmsResponse = { + const mockKeyProviderResponse = { prv: 'mock-ecdsa-private-key', pub: 'mock-ecdsa-public-key', source: 'user', @@ -562,10 +566,10 @@ describe('signMpcTransaction', () => { // Missing encryptedDataKey, encryptedUserGpgPrvKey, encryptedRound1Session }; - const kmsNock = nock(kmsUrl) + const keyProviderNock = nock(keyProviderUrl) .get(`/key/${input.pub}`) .query({ source: 'user' }) - .reply(200, mockKmsResponse); + .reply(200, mockKeyProviderResponse); const response = await agent .post(`/api/${coin}/mpc/sign/mpcv2round2`) @@ -578,11 +582,11 @@ describe('signMpcTransaction', () => { 'encryptedDataKey from Round 1 is required for MPCv2 Round 2', ); - kmsNock.done(); + keyProviderNock.done(); }); it('should fail when required fields are missing for Round 3', async () => { - const mockKmsResponse = { + const mockKeyProviderResponse = { prv: 'mock-ecdsa-private-key', pub: 'mock-ecdsa-public-key', source: 'user', @@ -597,10 +601,10 @@ describe('signMpcTransaction', () => { // Missing bitgoGpgPubKey, encryptedUserGpgPrvKey, encryptedRound2Session }; - const kmsNock = nock(kmsUrl) + const keyProviderNock = nock(keyProviderUrl) .get(`/key/${input.pub}`) .query({ source: 'user' }) - .reply(200, mockKmsResponse); + .reply(200, mockKeyProviderResponse); const response = await agent .post(`/api/${coin}/mpc/sign/mpcv2round3`) @@ -611,7 +615,7 @@ describe('signMpcTransaction', () => { response.body.should.have.property('error'); response.body.details.should.equal('bitgoGpgPubKey is required for MPCv2 Round 3'); - kmsNock.done(); + keyProviderNock.done(); }); it('should fail for unsupported share type', async () => { diff --git a/src/__tests__/api/advancedWalletManager/signMultisigTransaction.test.ts b/src/__tests__/api/advancedWalletManager/signMultisigTransaction.test.ts index bee83492..d5349482 100644 --- a/src/__tests__/api/advancedWalletManager/signMultisigTransaction.test.ts +++ b/src/__tests__/api/advancedWalletManager/signMultisigTransaction.test.ts @@ -15,7 +15,7 @@ describe('signMultisigTransaction', () => { let agent: request.SuperAgentTest; // test cofig - const kmsUrl = 'http://kms.invalid'; + const keyProviderUrl = 'http://key-provider.invalid'; const coin = 'hteth'; const accessToken = 'test-token'; @@ -34,7 +34,7 @@ describe('signMultisigTransaction', () => { bind: 'localhost', timeout: 60000, httpLoggerFile: '', - kmsUrl: kmsUrl, + keyProviderUrl: keyProviderUrl, tlsMode: TlsMode.DISABLED, clientCertAllowSelfSigned: true, }; @@ -95,17 +95,17 @@ describe('signMultisigTransaction', () => { }, }; - const mockKmsResponse = { + const mockKeyProviderResponse = { prv: 'xprv9s21ZrQH143K3gACTjj6hGGGTME8nrhuYiuGVsiVqyxQjZJ1Tr2PZJKAABHLm2gMSwqRmXBXT8VcXppDy43xjwvt9xdgkDSyRPsBUekEaPq', pub: 'xpub661MyMwAqRbcGAEfZmG74QD11P4dCKRkuwpsJG87QKVPcMdA1PLe76de1Ted54rZ2gyqLYhmdhBCFMrt7AoVwPZwXa3Na9aUnvndvXbvmwu', source: 'user', type: 'independent', }; - const kmsNock = nock(kmsUrl) + const keyProviderNock = nock(keyProviderUrl) .get(`/key/${input.pub}`) .query({ source: 'user' }) - .reply(200, mockKmsResponse); + .reply(200, mockKeyProviderResponse); const response = await agent .post(`/api/${coin}/multisig/sign`) @@ -116,7 +116,7 @@ describe('signMultisigTransaction', () => { response.status.should.equal(200); response.body.should.have.property('halfSigned'); - kmsNock.done(); + keyProviderNock.done(); }); it('should sign a tbtc PSBT when walletPubs are provided', async () => { @@ -130,12 +130,15 @@ describe('signMultisigTransaction', () => { const txHex = `${txHexPrefix}01005e0100000001edd7a583fef5aabf265e6dca24452581a3cca2671a1fa6b4e404bccb6ff4c83b0000000000ffffffff01780f0000000000002200202120dcf53e62a4cc9d3843993aa2258bd14fbf911a4ea4cf4f3ac840f4170279000000000001012ba00f00000000000022002008da4d49c618c6a00dc86a962f9c452dc0151653d2630470dcf8375a9f6496a501030401000000010569522103a1c425fd9b169e6ab5ed3de596acb777ccae0cda3d91256238b5e739a3f14aae210222a76697605c890dc4365132f9ae0d351952a1aad7eecf78d9923766dbe74a1e21033b21c0758ffbd446204914fa1d1c5921e9f82c2671dac89737666aa9375973e953ae22060222a76697605c890dc4365132f9ae0d351952a1aad7eecf78d9923766dbe74a1e14502e31ca000000000000000014000000000000002206033b21c0758ffbd446204914fa1d1c5921e9f82c2671dac89737666aa9375973e9146700d77100000000000000001400000000000000220603a1c425fd9b169e6ab5ed3de596acb777ccae0cda3d91256238b5e739a3f14aae14c2d0eb0a000000000000000014000000000000000000`; - const kmsNock = nock(kmsUrl).get(`/key/${userPub}`).query({ source: 'user' }).reply(200, { - prv: 'xprv9s21ZrQH143K2ZbYmTE75wsdaTsiSgsajJnooSi1wBieWRwQ89xSBwAYK1VJR795Y8XFCCXYHHs4sk2Heg6dkX3CHMBq5bw8DwBWByWx883', - pub: userPub, - source: 'user', - type: 'independent', - }); + const keyProviderNock = nock(keyProviderUrl) + .get(`/key/${userPub}`) + .query({ source: 'user' }) + .reply(200, { + prv: 'xprv9s21ZrQH143K2ZbYmTE75wsdaTsiSgsajJnooSi1wBieWRwQ89xSBwAYK1VJR795Y8XFCCXYHHs4sk2Heg6dkX3CHMBq5bw8DwBWByWx883', + pub: userPub, + source: 'user', + type: 'independent', + }); const response = await agent .post(`/api/tbtc/multisig/sign`) @@ -152,6 +155,6 @@ describe('signMultisigTransaction', () => { response.body.should.have.property('txHex'); response.body.txHex.should.startWith(txHexPrefix); - kmsNock.done(); + keyProviderNock.done(); }); }); diff --git a/src/__tests__/config.test.ts b/src/__tests__/config.test.ts index 8739b2a0..32e72c5b 100644 --- a/src/__tests__/config.test.ts +++ b/src/__tests__/config.test.ts @@ -21,7 +21,7 @@ describe('Configuration', () => { process.env = { ...originalEnv }; delete process.env.APP_MODE; delete process.env.BITGO_APP_MODE; - delete process.env.KMS_URL; + delete process.env.KEY_PROVIDER_URL; delete process.env.ADVANCED_WALLET_MANAGER_URL; delete process.env.AWM_SERVER_CA_CERT_PATH; delete process.env.TLS_MODE; @@ -43,15 +43,15 @@ describe('Configuration', () => { delete process.env.BITGO_CUSTOM_BITCOIN_NETWORK; delete process.env.SERVER_TLS_KEY_PATH; delete process.env.SERVER_TLS_CERT_PATH; - delete process.env.KMS_CLIENT_TLS_KEY; - delete process.env.KMS_CLIENT_TLS_CERT; - delete process.env.KMS_CLIENT_TLS_KEY_PATH; - delete process.env.KMS_CLIENT_TLS_CERT_PATH; + delete process.env.KEY_PROVIDER_CLIENT_TLS_KEY; + delete process.env.KEY_PROVIDER_CLIENT_TLS_CERT; + delete process.env.KEY_PROVIDER_CLIENT_TLS_KEY_PATH; + delete process.env.KEY_PROVIDER_CLIENT_TLS_CERT_PATH; delete process.env.AWM_CLIENT_TLS_KEY; delete process.env.AWM_CLIENT_TLS_CERT; delete process.env.AWM_CLIENT_TLS_KEY_PATH; delete process.env.AWM_CLIENT_TLS_CERT_PATH; - delete process.env.KMS_SERVER_CA_CERT_PATH; + delete process.env.KEY_PROVIDER_SERVER_CA_CERT_PATH; delete process.env.RECOVERY_MODE; }); @@ -78,12 +78,12 @@ describe('Configuration', () => { }); it('should use default configuration when minimal environment variables are set', () => { - process.env.KMS_URL = 'http://localhost:3000'; + process.env.KEY_PROVIDER_URL = 'http://localhost:3000'; process.env.SERVER_TLS_KEY = mockTlsKey; process.env.SERVER_TLS_CERT = mockTlsCert; - process.env.KMS_CLIENT_TLS_KEY = mockClientTlsKey; - process.env.KMS_CLIENT_TLS_CERT = mockClientTlsCert; - process.env.KMS_SERVER_CA_CERT_PATH = path.resolve( + process.env.KEY_PROVIDER_CLIENT_TLS_KEY = mockClientTlsKey; + process.env.KEY_PROVIDER_CLIENT_TLS_CERT = mockClientTlsCert; + process.env.KEY_PROVIDER_SERVER_CA_CERT_PATH = path.resolve( __dirname, 'mocks/certs/test-ssl-cert.pem', ); @@ -94,7 +94,7 @@ describe('Configuration', () => { cfg.bind.should.equal('localhost'); cfg.tlsMode.should.equal(TlsMode.MTLS); cfg.timeout.should.equal(305 * 1000); - cfg.kmsUrl.should.equal('http://localhost:3000'); + cfg.keyProviderUrl.should.equal('http://localhost:3000'); cfg.serverTlsKey!.should.equal(mockTlsKey); cfg.serverTlsCert!.should.equal(mockTlsCert); } @@ -102,12 +102,12 @@ describe('Configuration', () => { it('should read port from environment variable', () => { process.env.ADVANCED_WALLET_MANAGER_PORT = '4000'; - process.env.KMS_URL = 'http://localhost:3000'; + process.env.KEY_PROVIDER_URL = 'http://localhost:3000'; process.env.SERVER_TLS_KEY = mockTlsKey; process.env.SERVER_TLS_CERT = mockTlsCert; - process.env.KMS_CLIENT_TLS_KEY = mockClientTlsKey; - process.env.KMS_CLIENT_TLS_CERT = mockClientTlsCert; - process.env.KMS_SERVER_CA_CERT_PATH = path.resolve( + process.env.KEY_PROVIDER_CLIENT_TLS_KEY = mockClientTlsKey; + process.env.KEY_PROVIDER_CLIENT_TLS_CERT = mockClientTlsCert; + process.env.KEY_PROVIDER_SERVER_CA_CERT_PATH = path.resolve( __dirname, 'mocks/certs/test-ssl-cert.pem', ); @@ -115,20 +115,20 @@ describe('Configuration', () => { isAdvancedWalletManagerConfig(cfg).should.be.true(); if (isAdvancedWalletManagerConfig(cfg)) { cfg.port.should.equal(4000); - cfg.kmsUrl.should.equal('http://localhost:3000'); + cfg.keyProviderUrl.should.equal('http://localhost:3000'); cfg.serverTlsKey!.should.equal(mockTlsKey); cfg.serverTlsCert!.should.equal(mockTlsCert); } }); it('should read the recovery mode from the env', () => { - process.env.KMS_URL = 'http://localhost:3000'; + process.env.KEY_PROVIDER_URL = 'http://localhost:3000'; process.env.SERVER_TLS_KEY = mockTlsKey; process.env.SERVER_TLS_CERT = mockTlsCert; - process.env.KMS_CLIENT_TLS_KEY = mockClientTlsKey; - process.env.KMS_CLIENT_TLS_CERT = mockClientTlsCert; + process.env.KEY_PROVIDER_CLIENT_TLS_KEY = mockClientTlsKey; + process.env.KEY_PROVIDER_CLIENT_TLS_CERT = mockClientTlsCert; process.env.RECOVERY_MODE = 'true'; - process.env.KMS_SERVER_CA_CERT_PATH = path.resolve( + process.env.KEY_PROVIDER_SERVER_CA_CERT_PATH = path.resolve( __dirname, 'mocks/certs/test-ssl-cert.pem', ); @@ -137,12 +137,12 @@ describe('Configuration', () => { }); it('should read TLS mode from environment variables', () => { - process.env.KMS_URL = 'http://localhost:3000'; + process.env.KEY_PROVIDER_URL = 'http://localhost:3000'; process.env.SERVER_TLS_KEY = mockTlsKey; process.env.SERVER_TLS_CERT = mockTlsCert; - process.env.KMS_CLIENT_TLS_KEY = mockClientTlsKey; - process.env.KMS_CLIENT_TLS_CERT = mockClientTlsCert; - process.env.KMS_SERVER_CA_CERT_PATH = path.resolve( + process.env.KEY_PROVIDER_CLIENT_TLS_KEY = mockClientTlsKey; + process.env.KEY_PROVIDER_CLIENT_TLS_CERT = mockClientTlsCert; + process.env.KEY_PROVIDER_SERVER_CA_CERT_PATH = path.resolve( __dirname, 'mocks/certs/test-ssl-cert.pem', ); @@ -153,7 +153,7 @@ describe('Configuration', () => { isAdvancedWalletManagerConfig(cfg).should.be.true(); if (isAdvancedWalletManagerConfig(cfg)) { cfg.tlsMode.should.equal(TlsMode.DISABLED); - cfg.kmsUrl.should.equal('http://localhost:3000'); + cfg.keyProviderUrl.should.equal('http://localhost:3000'); } // Test with mTLS explicitly enabled @@ -162,7 +162,7 @@ describe('Configuration', () => { isAdvancedWalletManagerConfig(cfg).should.be.true(); if (isAdvancedWalletManagerConfig(cfg)) { cfg.tlsMode.should.equal(TlsMode.MTLS); - cfg.kmsUrl.should.equal('http://localhost:3000'); + cfg.keyProviderUrl.should.equal('http://localhost:3000'); cfg.serverTlsKey!.should.equal(mockTlsKey); cfg.serverTlsCert!.should.equal(mockTlsCert); } @@ -179,20 +179,20 @@ describe('Configuration', () => { isAdvancedWalletManagerConfig(cfg).should.be.true(); if (isAdvancedWalletManagerConfig(cfg)) { cfg.tlsMode.should.equal(TlsMode.MTLS); - cfg.kmsUrl.should.equal('http://localhost:3000'); + cfg.keyProviderUrl.should.equal('http://localhost:3000'); cfg.serverTlsKey!.should.equal(mockTlsKey); cfg.serverTlsCert!.should.equal(mockTlsCert); } }); it('should read mTLS settings from environment variables', () => { - process.env.KMS_URL = 'http://localhost:3000'; + process.env.KEY_PROVIDER_URL = 'http://localhost:3000'; process.env.SERVER_TLS_KEY = mockTlsKey; process.env.SERVER_TLS_CERT = mockTlsCert; - process.env.KMS_CLIENT_TLS_KEY = mockClientTlsKey; - process.env.KMS_CLIENT_TLS_CERT = mockClientTlsCert; + process.env.KEY_PROVIDER_CLIENT_TLS_KEY = mockClientTlsKey; + process.env.KEY_PROVIDER_CLIENT_TLS_CERT = mockClientTlsCert; process.env.MTLS_ALLOWED_CLIENT_FINGERPRINTS = 'ABC123,DEF456'; - process.env.KMS_SERVER_CA_CERT_PATH = path.resolve( + process.env.KEY_PROVIDER_SERVER_CA_CERT_PATH = path.resolve( __dirname, 'mocks/certs/test-ssl-cert.pem', ); @@ -201,45 +201,45 @@ describe('Configuration', () => { isAdvancedWalletManagerConfig(cfg).should.be.true(); if (isAdvancedWalletManagerConfig(cfg)) { cfg.mtlsAllowedClientFingerprints!.should.deepEqual(['ABC123', 'DEF456']); - cfg.kmsUrl.should.equal('http://localhost:3000'); + cfg.keyProviderUrl.should.equal('http://localhost:3000'); cfg.serverTlsKey!.should.equal(mockTlsKey); cfg.serverTlsCert!.should.equal(mockTlsCert); - cfg.kmsServerCaCertPath!.should.equal( + cfg.keyProviderServerCaCertPath!.should.equal( path.resolve(__dirname, 'mocks/certs/test-ssl-cert.pem'), ); } }); - it('should throw error when KMS_URL is not set', () => { - delete process.env.KMS_URL; + it('should throw error when KEY_PROVIDER_URL is not set', () => { + delete process.env.KEY_PROVIDER_URL; (() => initConfig()).should.throw( - 'KMS_URL environment variable is required and cannot be empty', + 'KEY_PROVIDER_URL environment variable is required and cannot be empty', ); }); - it('should throw error when KMS_URL is empty', () => { - process.env.KMS_URL = ''; + it('should throw error when KEY_PROVIDER_URL is empty', () => { + process.env.KEY_PROVIDER_URL = ''; (() => initConfig()).should.throw( - 'KMS_URL environment variable is required and cannot be empty', + 'KEY_PROVIDER_URL environment variable is required and cannot be empty', ); }); it('should succeed when TLS certificates are not set for disabled TLS mode', () => { - process.env.KMS_URL = 'http://localhost:3000'; + process.env.KEY_PROVIDER_URL = 'http://localhost:3000'; process.env.TLS_MODE = 'disabled'; delete process.env.SERVER_TLS_KEY; delete process.env.SERVER_TLS_CERT; - delete process.env.KMS_SERVER_CA_CERT_PATH; + delete process.env.KEY_PROVIDER_SERVER_CA_CERT_PATH; const cfg = initConfig(); isAdvancedWalletManagerConfig(cfg).should.be.true(); if (isAdvancedWalletManagerConfig(cfg)) { cfg.tlsMode.should.equal(TlsMode.DISABLED); - cfg.kmsUrl.should.equal('http://localhost:3000'); + cfg.keyProviderUrl.should.equal('http://localhost:3000'); } }); it('should throw error when TLS certificates are not set for MTLS mode', () => { - process.env.KMS_URL = 'http://localhost:3000'; + process.env.KEY_PROVIDER_URL = 'http://localhost:3000'; process.env.TLS_MODE = 'mtls'; delete process.env.SERVER_TLS_KEY; delete process.env.SERVER_TLS_CERT; @@ -247,13 +247,13 @@ describe('Configuration', () => { }); it('should read HTTP_LOGFILE into httpLoggerFile in Advanced wallet manager mode', () => { - process.env.KMS_URL = 'http://localhost:3000'; + process.env.KEY_PROVIDER_URL = 'http://localhost:3000'; process.env.SERVER_TLS_KEY = mockTlsKey; process.env.SERVER_TLS_CERT = mockTlsCert; - process.env.KMS_CLIENT_TLS_KEY = mockClientTlsKey; - process.env.KMS_CLIENT_TLS_CERT = mockClientTlsCert; + process.env.KEY_PROVIDER_CLIENT_TLS_KEY = mockClientTlsKey; + process.env.KEY_PROVIDER_CLIENT_TLS_CERT = mockClientTlsCert; process.env.HTTP_LOGFILE = '/tmp/test-http-access.log'; - process.env.KMS_SERVER_CA_CERT_PATH = path.resolve( + process.env.KEY_PROVIDER_SERVER_CA_CERT_PATH = path.resolve( __dirname, 'mocks/certs/test-ssl-cert.pem', ); @@ -264,12 +264,12 @@ describe('Configuration', () => { } }); - it('should throw error when KMS_SERVER_CA_CERT_PATH is not set for MTLS mode', () => { - process.env.KMS_URL = 'http://localhost:3000'; + it('should throw error when KEY_PROVIDER_SERVER_CA_CERT_PATH is not set for MTLS mode', () => { + process.env.KEY_PROVIDER_URL = 'http://localhost:3000'; process.env.TLS_MODE = 'mtls'; - delete process.env.KMS_SERVER_CA_CERT_PATH; + delete process.env.KEY_PROVIDER_SERVER_CA_CERT_PATH; (() => initConfig()).should.throw( - 'KMS_SERVER_CA_CERT_PATH is required when TLS mode is MTLS', + 'KEY_PROVIDER_SERVER_CA_CERT_PATH is required when TLS mode is MTLS', ); }); }); diff --git a/src/__tests__/routes.test.ts b/src/__tests__/routes.test.ts index 50767d38..f8a0984b 100644 --- a/src/__tests__/routes.test.ts +++ b/src/__tests__/routes.test.ts @@ -15,7 +15,7 @@ describe('Routes', () => { httpLoggerFile: '', clientCertAllowSelfSigned: true, tlsMode: TlsMode.DISABLED, - kmsUrl: 'http://localhost:3000/kms', + keyProviderUrl: 'http://localhost:3000/key-provider', timeout: 5000, port: 3000, bind: 'localhost', diff --git a/src/advancedWalletManager/handlers/ecdsaEddsaSignTransaction.ts b/src/advancedWalletManager/handlers/ecdsaEddsaSignTransaction.ts index 83b5d6ee..7264f100 100644 --- a/src/advancedWalletManager/handlers/ecdsaEddsaSignTransaction.ts +++ b/src/advancedWalletManager/handlers/ecdsaEddsaSignTransaction.ts @@ -1,5 +1,5 @@ import { AwmApiSpecRouteRequest } from '../routers/advancedWalletManagerApiSpec'; -import { decryptDataKey, generateDataKey, retrieveKmsPrvKey } from './utils/utils'; +import { decryptDataKey, generateDataKey, retrieveKeyProviderPrvKey } from './utils/utils'; import logger from '../../shared/logger'; import { BaseCoin, @@ -92,8 +92,8 @@ export async function signMpcTransaction(req: AwmApiSpecRouteRequest<'v1.mpc.sig const bitgo = req.bitgo; const coinInstance = await coinFactory.getCoin(coin, bitgo); - // Get private key from KMS - const prv = await retrieveKmsPrvKey({ pub, source, cfg: req.config }); + // Get private key from key provider + const prv = await retrieveKeyProviderPrvKey({ pub, source, cfg: req.config }); if (!prv) { const errorMsg = `Error while MPC signing, missing prv key for pub=${pub}, source=${source}`; diff --git a/src/advancedWalletManager/handlers/ecdsaMPCV2Recovery.ts b/src/advancedWalletManager/handlers/ecdsaMPCV2Recovery.ts index 59f62220..af957421 100644 --- a/src/advancedWalletManager/handlers/ecdsaMPCV2Recovery.ts +++ b/src/advancedWalletManager/handlers/ecdsaMPCV2Recovery.ts @@ -3,7 +3,7 @@ import { AwmApiSpecRouteRequest, MpcV2RecoveryResponseType, } from '../routers/advancedWalletManagerApiSpec'; -import { KmsClient } from '../kmsClient/kmsClient'; +import { KeyProviderClient } from '../keyProviderClient/keyProviderClient'; import { BaseCoin, ECDSAMethodTypes } from '@bitgo-beta/sdk-core'; import { isCosmosLikeCoin, isEcdsaCoin, isEthLikeCoin } from '../../shared/coinUtils'; import { BadRequestError, NotImplementedError } from '../../shared/errors'; @@ -54,9 +54,9 @@ export async function ecdsaMPCv2Recovery( // setup clients and retreive the keys // TODO: this needs to be segerated if the EBE instance cannot retrieve both keys - const kms = new KmsClient(req.config); - const { prv: userPrv } = await kms.getKey({ pub, source: 'user' }); - const { prv: backupPrv } = await kms.getKey({ pub, source: 'backup' }); + const keyProvider = new KeyProviderClient(req.config); + const { prv: userPrv } = await keyProvider.getKey({ pub, source: 'user' }); + const { prv: backupPrv } = await keyProvider.getKey({ pub, source: 'backup' }); // construct tx builder const txHash = await getMessageHash(coin, txHex); diff --git a/src/advancedWalletManager/handlers/ecdsaMPCV2WalletGenerationFinalize.ts b/src/advancedWalletManager/handlers/ecdsaMPCV2WalletGenerationFinalize.ts index 560b3efc..357d177e 100644 --- a/src/advancedWalletManager/handlers/ecdsaMPCV2WalletGenerationFinalize.ts +++ b/src/advancedWalletManager/handlers/ecdsaMPCV2WalletGenerationFinalize.ts @@ -4,7 +4,7 @@ import { MpcV2FinalizeResponseType, MpcV2RoundState, } from '../routers/advancedWalletManagerApiSpec'; -import { KmsClient } from '../kmsClient/kmsClient'; +import { KeyProviderClient } from '../keyProviderClient/keyProviderClient'; import assert from 'assert'; export async function ecdsaMPCv2Finalize( @@ -14,10 +14,10 @@ export async function ecdsaMPCv2Finalize( req.decoded; // setup clients - const kms = new KmsClient(req.config); + const keyProvider = new KeyProviderClient(req.config); // fetch previous state of execution - const { plaintextKey } = await kms.decryptDataKey({ encryptedKey: encryptedDataKey }); + const { plaintextKey } = await keyProvider.decryptDataKey({ encryptedKey: encryptedDataKey }); const state: MpcV2RoundState = JSON.parse( req.bitgo.decrypt({ input: encryptedData, @@ -59,7 +59,7 @@ export async function ecdsaMPCv2Finalize( 'Source and Bitgo Common keychains do not match', ); - await kms.postKey({ + await keyProvider.postKey({ coin: req.decoded.coin, source: req.decoded.source, pub: commonKeychain, diff --git a/src/advancedWalletManager/handlers/ecdsaMPCV2WalletGenerationInitialize.ts b/src/advancedWalletManager/handlers/ecdsaMPCV2WalletGenerationInitialize.ts index df644ba8..6df36e96 100644 --- a/src/advancedWalletManager/handlers/ecdsaMPCV2WalletGenerationInitialize.ts +++ b/src/advancedWalletManager/handlers/ecdsaMPCV2WalletGenerationInitialize.ts @@ -3,7 +3,7 @@ import { MpcV2InitializeResponseType, MpcV2RoundState, } from '../routers/advancedWalletManagerApiSpec'; -import { KmsClient } from '../kmsClient/kmsClient'; +import { KeyProviderClient } from '../keyProviderClient/keyProviderClient'; import * as bitgoSdk from '@bitgo-beta/sdk-core'; import logger from '../../shared/logger'; import { MPCv2PartiesEnum } from '@bitgo-beta/sdk-core/dist/src/bitgo/utils/tss/ecdsa'; @@ -14,11 +14,11 @@ export async function ecdsaMPCv2Initialize( const { source } = req.decoded; // setup clients - const kms = new KmsClient(req.config); + const keyProvider = new KeyProviderClient(req.config); // generate keys required const sourceGpgKey = await bitgoSdk.generateGPGKeyPair('secp256k1'); - const { plaintextKey, encryptedKey } = await kms.generateDataKey({ keyType: 'AES-256' }); + const { plaintextKey, encryptedKey } = await keyProvider.generateDataKey({ keyType: 'AES-256' }); // store the state of execution const state: MpcV2RoundState = { diff --git a/src/advancedWalletManager/handlers/ecdsaMPCV2WalletGenerationRound.ts b/src/advancedWalletManager/handlers/ecdsaMPCV2WalletGenerationRound.ts index b443fc8d..4e3d5736 100644 --- a/src/advancedWalletManager/handlers/ecdsaMPCV2WalletGenerationRound.ts +++ b/src/advancedWalletManager/handlers/ecdsaMPCV2WalletGenerationRound.ts @@ -5,7 +5,7 @@ import { MpcV2RoundState, } from '../routers/advancedWalletManagerApiSpec'; import { MPCv2PartiesEnum } from '@bitgo-beta/sdk-core/dist/src/bitgo/utils/tss/ecdsa'; -import { KmsClient } from '../kmsClient/kmsClient'; +import { KeyProviderClient } from '../keyProviderClient/keyProviderClient'; import logger from '../../shared/logger'; import { BadRequestError, ValidationError } from '../../shared/errors'; @@ -18,7 +18,7 @@ export async function ecdsaMPCv2Round( const counterPartyGpgPubInput = req.body.counterPartyGpgPub; // setup clients - const kms = new KmsClient(req.config); + const keyProvider = new KeyProviderClient(req.config); // sanity checks if (round < 1 || round > 4) { @@ -38,7 +38,7 @@ export async function ecdsaMPCv2Round( } // fetch previous state of execution - const { plaintextKey } = await kms.decryptDataKey({ encryptedKey: encryptedDataKey }); + const { plaintextKey } = await keyProvider.decryptDataKey({ encryptedKey: encryptedDataKey }); const state: MpcV2RoundState = JSON.parse( req.bitgo.decrypt({ input: encryptedData, diff --git a/src/advancedWalletManager/handlers/eddsaMPCRecovery.ts b/src/advancedWalletManager/handlers/eddsaMPCRecovery.ts index 0daac69c..1b70abe9 100644 --- a/src/advancedWalletManager/handlers/eddsaMPCRecovery.ts +++ b/src/advancedWalletManager/handlers/eddsaMPCRecovery.ts @@ -10,7 +10,7 @@ import { import { Ed25519Bip32HdTree } from '@bitgo-beta/sdk-lib-mpc'; import { CoinFamily, coins } from '@bitgo-beta/statics'; import { type KeyPair as SolKeyPair } from '@bitgo-beta/sdk-coin-sol'; -import { checkRecoveryMode, retrieveKmsPrvKey } from './utils/utils'; +import { checkRecoveryMode, retrieveKeyProviderPrvKey } from './utils/utils'; import { AdvancedWalletManagerConfig } from '../../shared/types'; import logger from '../../shared/logger'; @@ -107,13 +107,13 @@ export async function signEddsaRecoveryTransaction({ publicKey = derivedKey; // Get user and backup private keys - const userPrv = await retrieveKmsPrvKey({ + const userPrv = await retrieveKeyProviderPrvKey({ pub: request.commonKeychain.toString(), source: 'user', cfg, }); - const backupPrv = await retrieveKmsPrvKey({ + const backupPrv = await retrieveKeyProviderPrvKey({ pub: request.commonKeychain.toString(), source: 'backup', cfg, diff --git a/src/advancedWalletManager/handlers/eddsaMPCWalletGenerationFinalize.ts b/src/advancedWalletManager/handlers/eddsaMPCWalletGenerationFinalize.ts index d68f5f1d..4b7a7e8e 100644 --- a/src/advancedWalletManager/handlers/eddsaMPCWalletGenerationFinalize.ts +++ b/src/advancedWalletManager/handlers/eddsaMPCWalletGenerationFinalize.ts @@ -5,7 +5,7 @@ import { AwmApiSpecRouteRequest, MpcFinalizeRequestType, } from '../routers/advancedWalletManagerApiSpec'; -import { KmsClient } from '../kmsClient/kmsClient'; +import { KeyProviderClient } from '../keyProviderClient/keyProviderClient'; import { eddsaKeyCombine, gpgDecrypt, gpgEncrypt, verifyWalletSignatures } from './utils/utils'; import coinFactory from '../../shared/coinFactory'; @@ -24,7 +24,7 @@ export async function eddsaFinalize(req: AwmApiSpecRouteRequest<'v1.mpc.key.fina const counterPartyToSourceKeyShare = req.decoded.counterPartyKeyShare; // setup clients - const kms = new KmsClient(req.config); + const keyProvider = new KeyProviderClient(req.config); const coinInstance = await coinFactory.getCoin(coin, req.bitgo); // indexes @@ -33,7 +33,7 @@ export async function eddsaFinalize(req: AwmApiSpecRouteRequest<'v1.mpc.key.fina const bitgoIndex = 3; // Decrypt the encrypted payload using encryptedDataKey to retrieve the previous state of computation - const decryptedDataKey = await kms.decryptDataKey({ encryptedKey: encryptedDataKey }); + const decryptedDataKey = await keyProvider.decryptDataKey({ encryptedKey: encryptedDataKey }); const previousState = JSON.parse( req.bitgo.decrypt({ input: encryptedData, @@ -120,7 +120,7 @@ export async function eddsaFinalize(req: AwmApiSpecRouteRequest<'v1.mpc.key.fina } debugLogger(`Common keychain for ${source}:`, commonKeychain); - await kms.postKey({ + await keyProvider.postKey({ pub: commonKeychain, prv: JSON.stringify(sourceSigningMaterial), source, diff --git a/src/advancedWalletManager/handlers/eddsaMPCWalletGenerationInitialize.ts b/src/advancedWalletManager/handlers/eddsaMPCWalletGenerationInitialize.ts index f7f10cde..228fa669 100644 --- a/src/advancedWalletManager/handlers/eddsaMPCWalletGenerationInitialize.ts +++ b/src/advancedWalletManager/handlers/eddsaMPCWalletGenerationInitialize.ts @@ -1,7 +1,7 @@ import debug from 'debug'; import * as bitgoSdk from '@bitgo-beta/sdk-core'; import { assert } from 'console'; -import { KmsClient } from '../kmsClient/kmsClient'; +import { KeyProviderClient } from '../keyProviderClient/keyProviderClient'; import { AwmApiSpecRouteRequest, KeyShareType, @@ -22,7 +22,7 @@ export async function eddsaInitialize( } // setup clients - const kms = new KmsClient(req.config); + const keyProvider = new KeyProviderClient(req.config); // MPC configuration const MPC = await bitgoSdk.Eddsa.initialize(); @@ -99,7 +99,7 @@ export async function eddsaInitialize( sourcePrivateShare, counterPartyKeyShare: counterPartyGpgPub ? undefined : counterPartyKeyShare, // if counterPartyGpgPub is NOT gpg encrypted, store in payload to be encrypted in finalize }; - const { plaintextKey, encryptedKey } = await kms.generateDataKey({ keyType: 'AES-256' }); + const { plaintextKey, encryptedKey } = await keyProvider.generateDataKey({ keyType: 'AES-256' }); try { const encryptedPayload = req.bitgo.encrypt({ input: JSON.stringify(payload), diff --git a/src/advancedWalletManager/handlers/multisigRecovery.ts b/src/advancedWalletManager/handlers/multisigRecovery.ts index 30207038..7dad73b7 100644 --- a/src/advancedWalletManager/handlers/multisigRecovery.ts +++ b/src/advancedWalletManager/handlers/multisigRecovery.ts @@ -15,7 +15,7 @@ import { getReplayProtectionOptions, } from '../../shared/recoveryUtils'; import { SignedEthLikeRecoveryTx } from '../../types/transaction'; -import { checkRecoveryMode, retrieveKmsPrvKey } from './utils/utils'; +import { checkRecoveryMode, retrieveKeyProviderPrvKey } from './utils/utils'; import coinFactory from '../../shared/coinFactory'; export async function recoveryMultisigTransaction( @@ -27,8 +27,16 @@ export async function recoveryMultisigTransaction( req.decoded; //fetch prv and check that pub are valid - const userPrv = await retrieveKmsPrvKey({ pub: userPub, source: 'user', cfg: req.config }); - const backupPrv = await retrieveKmsPrvKey({ pub: backupPub, source: 'backup', cfg: req.config }); + const userPrv = await retrieveKeyProviderPrvKey({ + pub: userPub, + source: 'user', + cfg: req.config, + }); + const backupPrv = await retrieveKeyProviderPrvKey({ + pub: backupPub, + source: 'backup', + cfg: req.config, + }); if (!userPrv || !backupPrv) { const errorMsg = `Error while recovery wallet, missing prv keys for user or backup on pub keys user=${userPub}, backup=${backupPub}`; diff --git a/src/advancedWalletManager/handlers/multisigSignTransaction.ts b/src/advancedWalletManager/handlers/multisigSignTransaction.ts index 801af6b5..63cd2131 100644 --- a/src/advancedWalletManager/handlers/multisigSignTransaction.ts +++ b/src/advancedWalletManager/handlers/multisigSignTransaction.ts @@ -1,4 +1,4 @@ -import { KmsClient } from '../kmsClient/kmsClient'; +import { KeyProviderClient } from '../keyProviderClient/keyProviderClient'; import { TransactionPrebuild } from '@bitgo-beta/sdk-core'; import logger from '../../shared/logger'; import { AwmApiSpecRouteRequest } from '../routers/advancedWalletManagerApiSpec'; @@ -16,17 +16,17 @@ export async function signMultisigTransaction( req.body; const bitgo = req.bitgo; - const kms = new KmsClient(req.config); + const keyProvider = new KeyProviderClient(req.config); - // Retrieve the private key from KMS + // Retrieve the private key from key provider let prv: string; try { - const res = await kms.getKey({ pub, source }); + const res = await keyProvider.getKey({ pub, source }); prv = res.prv; } catch (error: any) { throw { status: error.status || 500, - message: error.message || 'Failed to retrieve key from KMS', + message: error.message || 'Failed to retrieve key from key provider', }; } diff --git a/src/advancedWalletManager/handlers/postIndependentKey.ts b/src/advancedWalletManager/handlers/postIndependentKey.ts index 31300e3e..30d98c12 100644 --- a/src/advancedWalletManager/handlers/postIndependentKey.ts +++ b/src/advancedWalletManager/handlers/postIndependentKey.ts @@ -1,5 +1,5 @@ import { BitGoAPI } from '@bitgo-beta/sdk-api'; -import { KmsClient } from '../kmsClient/kmsClient'; +import { KeyProviderClient } from '../keyProviderClient/keyProviderClient'; import { AwmApiSpecRouteRequest } from '../routers/advancedWalletManagerApiSpec'; import coinFactory from '../../shared/coinFactory'; @@ -10,7 +10,7 @@ export async function postIndependentKey( // setup clients const bitgo: BitGoAPI = req.bitgo; - const kms = new KmsClient(req.config); + const keyProvider = new KeyProviderClient(req.config); // create public and private key pairs on BitGo SDK const coin = await coinFactory.getCoin(req.params.coin, bitgo); @@ -20,9 +20,9 @@ export async function postIndependentKey( throw new Error('BitGo SDK failed to create public key'); } - // post key to KMS for encryption and storage + // post key to key provider for encryption and storage try { - return await kms.postKey({ + return await keyProvider.postKey({ pub, prv, coin: req.params.coin, @@ -33,7 +33,7 @@ export async function postIndependentKey( } catch (error: any) { throw { status: error.status || 500, - message: error.message || 'Failed to post key to KMS', + message: error.message || 'Failed to post key to key provider', }; } } diff --git a/src/advancedWalletManager/handlers/utils/utils.ts b/src/advancedWalletManager/handlers/utils/utils.ts index 70134612..c95ffdc3 100644 --- a/src/advancedWalletManager/handlers/utils/utils.ts +++ b/src/advancedWalletManager/handlers/utils/utils.ts @@ -3,11 +3,11 @@ import { BaseCoin, type EddsaUtils, Eddsa, EDDSA } from '@bitgo-beta/sdk-core'; import { BitGoAPI } from '@bitgo-beta/sdk-api'; import * as bitgoSdk from '@bitgo-beta/sdk-core'; -import { KmsClient } from '../../kmsClient/kmsClient'; -import { GenerateDataKeyResponse } from '../../kmsClient/types/dataKey'; +import { KeyProviderClient } from '../../keyProviderClient/keyProviderClient'; +import { GenerateDataKeyResponse } from '../../keyProviderClient/types/dataKey'; import { AdvancedWalletManagerConfig } from '../../../shared/types'; -export async function retrieveKmsPrvKey({ +export async function retrieveKeyProviderPrvKey({ pub, source, cfg, @@ -16,17 +16,17 @@ export async function retrieveKmsPrvKey({ source: string; cfg: AdvancedWalletManagerConfig; }): Promise { - const kms = new KmsClient(cfg); - // Retrieve the private key from KMS + const keyProvider = new KeyProviderClient(cfg); + // Retrieve the private key from key provider let prv: string; try { - const res = await kms.getKey({ pub, source }); + const res = await keyProvider.getKey({ pub, source }); prv = res.prv; return prv; } catch (error: any) { throw { status: error.status || 500, - message: error.message || 'Failed to retrieve key from KMS', + message: error.message || 'Failed to retrieve key from key provider', }; } } @@ -87,12 +87,12 @@ export async function generateDataKey({ cfg: AdvancedWalletManagerConfig; }): Promise { try { - const kms = new KmsClient(cfg); - return await kms.generateDataKey({ keyType }); + const keyProvider = new KeyProviderClient(cfg); + return await keyProvider.generateDataKey({ keyType }); } catch (error: any) { throw { status: error.status || 500, - message: error.message || 'Failed to generate data key from KMS', + message: error.message || 'Failed to generate data key from key provider', }; } } @@ -105,13 +105,13 @@ export async function decryptDataKey({ cfg: AdvancedWalletManagerConfig; }): Promise { try { - const kms = new KmsClient(cfg); - const decryptedDataKey = await kms.decryptDataKey({ encryptedKey: encryptedDataKey }); + const keyProvider = new KeyProviderClient(cfg); + const decryptedDataKey = await keyProvider.decryptDataKey({ encryptedKey: encryptedDataKey }); return decryptedDataKey.plaintextKey; } catch (error: any) { throw { status: error.status || 500, - message: error.message || 'Failed to decrypt data key from KMS', + message: error.message || 'Failed to decrypt data key from key provider', }; } } diff --git a/src/advancedWalletManager/kmsClient/kmsClient.ts b/src/advancedWalletManager/keyProviderClient/keyProviderClient.ts similarity index 50% rename from src/advancedWalletManager/kmsClient/kmsClient.ts rename to src/advancedWalletManager/keyProviderClient/keyProviderClient.ts index 15f53965..14fd9cac 100644 --- a/src/advancedWalletManager/kmsClient/kmsClient.ts +++ b/src/advancedWalletManager/keyProviderClient/keyProviderClient.ts @@ -1,14 +1,14 @@ import * as superagent from 'superagent'; import { AdvancedWalletManagerConfig, isMasterExpressConfig, TlsMode } from '../../shared/types'; -import { PostKeyKmsSchema, PostKeyParams, PostKeyResponse } from './types/postKey'; -import { GetKeyKmsSchema, GetKeyParams, GetKeyResponse } from './types/getKey'; +import { PostKeyResponseSchema, PostKeyParams, PostKeyResponse } from './types/postKey'; +import { GetKeyResponseSchema, GetKeyParams, GetKeyResponse } from './types/getKey'; import { - DecryptDataKeyKmsSchema, + DecryptDataKeyResponseSchema, DecryptDataKeyParams, DecryptDataKeyResponse, } from './types/dataKey'; import { - GenerateDataKeyKmsSchema, + GenerateDataKeyResponseSchema, GenerateDataKeyParams, GenerateDataKeyResponse, } from './types/generateDataKey'; @@ -18,39 +18,43 @@ import { URL } from 'url'; import logger from '../../shared/logger'; -export class KmsClient { +export class KeyProviderClient { private readonly url: string; private readonly agent?: https.Agent; constructor(cfg: AdvancedWalletManagerConfig) { if (isMasterExpressConfig(cfg)) { - logger.error('KMS client cannot be initialized in master express mode'); + logger.error('key provider client cannot be initialized in master express mode'); throw new Error('Configuration is not in advanced wallet manager mode'); } - if (!cfg.kmsUrl) { - logger.error('KMS URL not configured. Please set KMS_URL in your environment.'); - throw new Error('KMS URL not configured. Please set KMS_URL in your environment.'); + if (!cfg.keyProviderUrl) { + logger.error( + 'key provider URL not configured. Please set KEY_PROVIDER_URL in your environment.', + ); + throw new Error( + 'key provider URL not configured. Please set KEY_PROVIDER_URL in your environment.', + ); } - const kmsUrlObj = new URL(cfg.kmsUrl); + const urlObj = new URL(cfg.keyProviderUrl); if (cfg.tlsMode === TlsMode.MTLS) { - kmsUrlObj.protocol = 'https:'; - if (cfg.kmsServerCaCert || cfg.kmsServerCertAllowSelfSigned) { + urlObj.protocol = 'https:'; + if (cfg.keyProviderServerCaCert || cfg.keyProviderServerCertAllowSelfSigned) { this.agent = new https.Agent({ - ca: cfg.kmsServerCaCert, - cert: cfg.kmsClientTlsCert, - key: cfg.kmsClientTlsKey, - rejectUnauthorized: !cfg.kmsServerCertAllowSelfSigned, + ca: cfg.keyProviderServerCaCert, + cert: cfg.keyProviderClientTlsCert, + key: cfg.keyProviderClientTlsKey, + rejectUnauthorized: !cfg.keyProviderServerCertAllowSelfSigned, }); } } else { - kmsUrlObj.protocol = 'http:'; + urlObj.protocol = 'http:'; } - this.url = kmsUrlObj.toString().replace(/\/$/, ''); + this.url = urlObj.toString().replace(/\/$/, ''); } - // Handles http erros from KMS + // Handles http errors from key provider private errorHandler(error: superagent.ResponseError, errorLog: string) { logger.error(errorLog, error); @@ -69,7 +73,7 @@ export class KmsClient { throw new Error(error.response?.body.message); default: throw new Error( - `KMS returned unexpected response.${error.status ? ` ${error.status}` : ''}${ + `key provider returned unexpected response.${error.status ? ` ${error.status}` : ''}${ error.response?.body.message ? `: ${error.response?.body.message}` : '' }`, ); @@ -77,118 +81,126 @@ export class KmsClient { } async postKey(params: PostKeyParams): Promise { - logger.info('Posting key to KMS with pub: %s and source: %s', params.pub, params.source); - - // Call KMS to post the key - let kmsResponse: any; + logger.info( + 'Posting key to key provider with pub: %s and source: %s', + params.pub, + params.source, + ); + + // Call key provider to post the key + let response: any; try { let req = superagent.post(`${this.url}/key`).send(params); if (this.agent) req = req.agent(this.agent); - kmsResponse = await req; + response = await req; } catch (error: any) { - this.errorHandler(error, 'Error posting key to KMS'); + this.errorHandler(error, 'Error posting key to key provider'); } // validate the response try { - PostKeyKmsSchema.parse(kmsResponse.body); + PostKeyResponseSchema.parse(response.body); } catch (error: any) { - logger.error('KMS returned unexpected when posting key: ', error); + logger.error('key provider returned unexpected when posting key: ', error); throw new Error( - `KMS returned unexpected response when posting key${ + `key provider returned unexpected response when posting key${ error.message ? `: ${error.message}` : '' }`, ); } - const { pub, coin, source } = kmsResponse.body; + const { pub, coin, source } = response.body; return { pub, coin, source } as PostKeyResponse; } async getKey(params: GetKeyParams): Promise { - logger.info('Getting key from KMS with pub: %s and source: %s', params.pub, params.source); - - // Call KMS to get the key - let kmsResponse: any; + logger.info( + 'Getting key from key provider with pub: %s and source: %s', + params.pub, + params.source, + ); + + // Call key provider to get the key + let response: any; try { let req = superagent.get(`${this.url}/key/${params.pub}`).query({ source: params.source, }); if (this.agent) req = req.agent(this.agent); - kmsResponse = await req; + response = await req; } catch (error: any) { - this.errorHandler(error, 'Error getting key from KMS'); + this.errorHandler(error, 'Error getting key from key provider'); } // validate the response try { - GetKeyKmsSchema.parse(kmsResponse.body); + GetKeyResponseSchema.parse(response.body); } catch (error: any) { - logger.error('KMS returned unexpected response when getting key', error); + logger.error('key provider returned unexpected response when getting key', error); throw new Error( - `KMS returned unexpected response when getting key${ + `key provider returned unexpected response when getting key${ error.message ? `: ${error.message}` : '' }`, ); } - return kmsResponse.body as GetKeyResponse; + return response.body as GetKeyResponse; } async generateDataKey(params: GenerateDataKeyParams): Promise { - logger.info('Generating data key from KMS with type: %s', params.keyType); + logger.info('Generating data key from key provider with type: %s', params.keyType); - // Call KMS to generate the data key - let kmsResponse: any; + // Call key provider to generate the data key + let response: any; try { let req = superagent.post(`${this.url}/generateDataKey`).send(params); if (this.agent) req = req.agent(this.agent); - kmsResponse = await req; + response = await req; } catch (error: any) { - this.errorHandler(error, 'Error generating data key from KMS'); + this.errorHandler(error, 'Error generating data key from key provider'); } // validate the response try { - GenerateDataKeyKmsSchema.parse(kmsResponse.body); + GenerateDataKeyResponseSchema.parse(response.body); } catch (error: any) { - logger.error('KMS returned unexpected response when generating data key', error); + logger.error('key provider returned unexpected response when generating data key', error); throw new Error( - `KMS returned unexpected response when generating data key${ + `key provider returned unexpected response when generating data key${ error.message ? `: ${error.message}` : '' }`, ); } return { - plaintextKey: kmsResponse.body.plaintextKey, - encryptedKey: kmsResponse.body.encryptedKey, + plaintextKey: response.body.plaintextKey, + encryptedKey: response.body.encryptedKey, }; } async decryptDataKey(params: DecryptDataKeyParams): Promise { - // Call KMS to decrypt the data key - let kmsResponse: any; + // Call key provider to decrypt the data key + let response: any; try { let req = superagent.post(`${this.url}/decryptDataKey`).send(params); if (this.agent) req = req.agent(this.agent); - kmsResponse = await req; + response = await req; } catch (error: any) { - this.errorHandler(error, 'Error decrypting data key from KMS'); + this.errorHandler(error, 'Error decrypting data key from key provider'); } // validate the response try { - DecryptDataKeyKmsSchema.parse(kmsResponse.body); + DecryptDataKeyResponseSchema.parse(response.body); } catch (error: any) { - logger.error('KMS returned unexpected response when decrypting data key', error); + logger.error('key provider returned unexpected response when decrypting data key', error); throw new Error( - `KMS returned unexpected response when decrypting data key${ + `key provider returned unexpected response when decrypting data key${ error.message ? `: ${error.message}` : '' }`, ); } - return kmsResponse.body as DecryptDataKeyResponse; + return response.body as DecryptDataKeyResponse; } } diff --git a/src/advancedWalletManager/kmsClient/types/dataKey.ts b/src/advancedWalletManager/keyProviderClient/types/dataKey.ts similarity index 79% rename from src/advancedWalletManager/kmsClient/types/dataKey.ts rename to src/advancedWalletManager/keyProviderClient/types/dataKey.ts index 36094d25..082ffb18 100644 --- a/src/advancedWalletManager/kmsClient/types/dataKey.ts +++ b/src/advancedWalletManager/keyProviderClient/types/dataKey.ts @@ -9,7 +9,7 @@ export interface GenerateDataKeyResponse { encryptedKey: string; } -export const GenerateDataKeyKmsSchema = z.object({ +export const GenerateDataKeyResponseSchema = z.object({ plaintextKey: z.string(), encryptedKey: z.string(), }); @@ -22,6 +22,6 @@ export interface DecryptDataKeyResponse { plaintextKey: string; } -export const DecryptDataKeyKmsSchema = z.object({ +export const DecryptDataKeyResponseSchema = z.object({ plaintextKey: z.string(), }); diff --git a/src/advancedWalletManager/kmsClient/types/generateDataKey.ts b/src/advancedWalletManager/keyProviderClient/types/generateDataKey.ts similarity index 83% rename from src/advancedWalletManager/kmsClient/types/generateDataKey.ts rename to src/advancedWalletManager/keyProviderClient/types/generateDataKey.ts index 07d58c74..62139eb4 100644 --- a/src/advancedWalletManager/kmsClient/types/generateDataKey.ts +++ b/src/advancedWalletManager/keyProviderClient/types/generateDataKey.ts @@ -9,7 +9,7 @@ export interface GenerateDataKeyResponse { encryptedKey: string; } -export const GenerateDataKeyKmsSchema = z.object({ +export const GenerateDataKeyResponseSchema = z.object({ plaintextKey: z.string(), encryptedKey: z.string(), }); diff --git a/src/advancedWalletManager/kmsClient/types/getKey.ts b/src/advancedWalletManager/keyProviderClient/types/getKey.ts similarity index 87% rename from src/advancedWalletManager/kmsClient/types/getKey.ts rename to src/advancedWalletManager/keyProviderClient/types/getKey.ts index 35b9005d..bcdd4c1f 100644 --- a/src/advancedWalletManager/kmsClient/types/getKey.ts +++ b/src/advancedWalletManager/keyProviderClient/types/getKey.ts @@ -12,7 +12,7 @@ export interface GetKeyResponse { type: 'independent' | 'tss'; } -export const GetKeyKmsSchema = z.object({ +export const GetKeyResponseSchema = z.object({ pub: z.string(), prv: z.string(), source: z.enum(['user', 'backup']), diff --git a/src/advancedWalletManager/kmsClient/types/postKey.ts b/src/advancedWalletManager/keyProviderClient/types/postKey.ts similarity index 90% rename from src/advancedWalletManager/kmsClient/types/postKey.ts rename to src/advancedWalletManager/keyProviderClient/types/postKey.ts index f661f615..8ea38651 100644 --- a/src/advancedWalletManager/kmsClient/types/postKey.ts +++ b/src/advancedWalletManager/keyProviderClient/types/postKey.ts @@ -16,7 +16,7 @@ export interface PostKeyResponse { type: 'independent' | 'tss'; } -export const PostKeyKmsSchema = z.object({ +export const PostKeyResponseSchema = z.object({ pub: z.string(), coin: z.string(), source: z.enum(['user', 'backup']), diff --git a/src/advancedWalletManagerApp.ts b/src/advancedWalletManagerApp.ts index 74170755..2995a46b 100644 --- a/src/advancedWalletManagerApp.ts +++ b/src/advancedWalletManagerApp.ts @@ -31,7 +31,7 @@ export function startup(config: AdvancedWalletManagerConfig, baseUri: string): ( logger.info(`Base URI: ${baseUri}`); logger.info(`Port: ${config.port}`); logger.info(`Bind: ${config.bind}`); - logger.info(`KMS URL: ${config.kmsUrl}`); + logger.info(`key provider URL: ${config.keyProviderUrl}`); logger.info(`Recovery Mode: ${config.recoveryMode}`); // mTLS Configuration Section @@ -45,9 +45,9 @@ export function startup(config: AdvancedWalletManagerConfig, baseUri: string): ( ` • Allowed Client Fingerprints: ${config.mtlsAllowedClientFingerprints.join(', ')}`, ); } - logger.info('Client Settings (outbound to KMS):'); + logger.info('Client Settings (outbound to key provider):'); logger.info( - ` • Allow Self-Signed KMS Server Certificates: ${config.kmsServerCertAllowSelfSigned}`, + ` • Allow Self-Signed key provider Server Certificates: ${config.keyProviderServerCertAllowSelfSigned}`, ); } logger.info('========================'); diff --git a/src/initConfig.ts b/src/initConfig.ts index ee3ff8ce..bb69e3cc 100644 --- a/src/initConfig.ts +++ b/src/initConfig.ts @@ -59,7 +59,7 @@ const advancedWalletManagerConfig: AdvancedWalletManagerConfig = { bind: 'localhost', timeout: 305 * 1000, httpLoggerFile: 'logs/http-access.log', - kmsUrl: '', // Will be overridden by environment variable + keyProviderUrl: '', // Will be overridden by environment variable tlsMode: TlsMode.MTLS, clientCertAllowSelfSigned: false, }; @@ -84,11 +84,11 @@ function determineTlsMode(): TlsMode { } function advancedWalletManagerEnvConfig(): Partial { - const kmsUrl = readEnvVar('KMS_URL'); + const keyProviderUrl = readEnvVar('KEY_PROVIDER_URL'); - if (!kmsUrl) { - logger.error('KMS_URL environment variable is required and cannot be empty'); - throw new Error('KMS_URL environment variable is required and cannot be empty'); + if (!keyProviderUrl) { + logger.error('KEY_PROVIDER_URL environment variable is required and cannot be empty'); + throw new Error('KEY_PROVIDER_URL environment variable is required and cannot be empty'); } return { @@ -100,14 +100,15 @@ function advancedWalletManagerEnvConfig(): Partial timeout: Number(readEnvVar('TIMEOUT')), keepAliveTimeout: Number(readEnvVar('KEEP_ALIVE_TIMEOUT')), headersTimeout: Number(readEnvVar('HEADERS_TIMEOUT')), - // KMS settings - kmsUrl, - kmsServerCaCertPath: readEnvVar('KMS_SERVER_CA_CERT_PATH'), - kmsClientTlsKeyPath: readEnvVar('KMS_CLIENT_TLS_KEY_PATH'), - kmsClientTlsCertPath: readEnvVar('KMS_CLIENT_TLS_CERT_PATH'), - kmsClientTlsKey: readEnvVar('KMS_CLIENT_TLS_KEY'), - kmsClientTlsCert: readEnvVar('KMS_CLIENT_TLS_CERT'), - kmsServerCertAllowSelfSigned: readEnvVar('KMS_SERVER_CERT_ALLOW_SELF_SIGNED') === 'true', + // key provider settings + keyProviderUrl, + keyProviderServerCaCertPath: readEnvVar('KEY_PROVIDER_SERVER_CA_CERT_PATH'), + keyProviderClientTlsKeyPath: readEnvVar('KEY_PROVIDER_CLIENT_TLS_KEY_PATH'), + keyProviderClientTlsCertPath: readEnvVar('KEY_PROVIDER_CLIENT_TLS_CERT_PATH'), + keyProviderClientTlsKey: readEnvVar('KEY_PROVIDER_CLIENT_TLS_KEY'), + keyProviderClientTlsCert: readEnvVar('KEY_PROVIDER_CLIENT_TLS_CERT'), + keyProviderServerCertAllowSelfSigned: + readEnvVar('KEY_PROVIDER_SERVER_CERT_ALLOW_SELF_SIGNED') === 'true', // mTLS server settings serverTlsKeyPath: readEnvVar('SERVER_TLS_KEY_PATH'), serverTlsCertPath: readEnvVar('SERVER_TLS_CERT_PATH'), @@ -140,14 +141,14 @@ function mergeAkmConfigs( timeout: get('timeout'), keepAliveTimeout: get('keepAliveTimeout'), headersTimeout: get('headersTimeout'), - kmsUrl: get('kmsUrl'), - kmsServerCaCertPath: get('kmsServerCaCertPath'), - kmsServerCaCert: get('kmsServerCaCert'), - kmsClientTlsKeyPath: get('kmsClientTlsKeyPath'), - kmsClientTlsCertPath: get('kmsClientTlsCertPath'), - kmsClientTlsKey: get('kmsClientTlsKey'), - kmsClientTlsCert: get('kmsClientTlsCert'), - kmsServerCertAllowSelfSigned: get('kmsServerCertAllowSelfSigned'), + keyProviderUrl: get('keyProviderUrl'), + keyProviderServerCaCertPath: get('keyProviderServerCaCertPath'), + keyProviderServerCaCert: get('keyProviderServerCaCert'), + keyProviderClientTlsKeyPath: get('keyProviderClientTlsKeyPath'), + keyProviderClientTlsCertPath: get('keyProviderClientTlsCertPath'), + keyProviderClientTlsKey: get('keyProviderClientTlsKey'), + keyProviderClientTlsCert: get('keyProviderClientTlsCert'), + keyProviderServerCertAllowSelfSigned: get('keyProviderServerCertAllowSelfSigned'), serverTlsKeyPath: get('serverTlsKeyPath'), serverTlsCertPath: get('serverTlsCertPath'), serverTlsKey: get('serverTlsKey'), @@ -196,44 +197,65 @@ function configureAdvancedWalletManagaerMode(): AdvancedWalletManagerConfig { logger.info('✓ TLS certificate loaded from environment variable'); } - if (!config.kmsServerCaCertPath) { - throw new Error('KMS_SERVER_CA_CERT_PATH is required when TLS mode is MTLS'); + if (!config.keyProviderServerCaCertPath) { + throw new Error('KEY_PROVIDER_SERVER_CA_CERT_PATH is required when TLS mode is MTLS'); } - if (config.kmsServerCaCertPath) { + if (config.keyProviderServerCaCertPath) { try { - config.kmsServerCaCert = fs.readFileSync(config.kmsServerCaCertPath, 'utf-8'); - logger.info(`✓ KMS server CA certificate loaded from file: ${config.kmsServerCaCertPath}`); + config.keyProviderServerCaCert = fs.readFileSync( + config.keyProviderServerCaCertPath, + 'utf-8', + ); + logger.info( + `✓ key provider server CA certificate loaded from file: ${config.keyProviderServerCaCertPath}`, + ); } catch (e) { const err = e instanceof Error ? e : new Error(String(e)); - throw new Error(`Failed to read KMS TLS certificate from kmsTlsCert: ${err.message}`); + throw new Error( + `Failed to read key provider TLS certificate from keyProviderTlsCert: ${err.message}`, + ); } } - if (config.kmsClientTlsKeyPath) { + if (config.keyProviderClientTlsKeyPath) { try { - config.kmsClientTlsKey = fs.readFileSync(config.kmsClientTlsKeyPath, 'utf-8'); - logger.info(`✓ KMS client key loaded from file: ${config.kmsClientTlsKeyPath}`); + config.keyProviderClientTlsKey = fs.readFileSync( + config.keyProviderClientTlsKeyPath, + 'utf-8', + ); + logger.info( + `✓ key provider client key loaded from file: ${config.keyProviderClientTlsKeyPath}`, + ); } catch (e) { const err = e instanceof Error ? e : new Error(String(e)); - throw new Error(`Failed to read KMS client key from kmsClientTlsKeyPath: ${err.message}`); + throw new Error( + `Failed to read key provider client key from keyProviderClientTlsKeyPath: ${err.message}`, + ); } } - if (config.kmsClientTlsCertPath) { + if (config.keyProviderClientTlsCertPath) { try { - config.kmsClientTlsCert = fs.readFileSync(config.kmsClientTlsCertPath, 'utf-8'); - logger.info(`✓ KMS client certificate loaded from file: ${config.kmsClientTlsCertPath}`); + config.keyProviderClientTlsCert = fs.readFileSync( + config.keyProviderClientTlsCertPath, + 'utf-8', + ); + logger.info( + `✓ key provider client certificate loaded from file: ${config.keyProviderClientTlsCertPath}`, + ); } catch (e) { const err = e instanceof Error ? e : new Error(String(e)); - throw new Error(`Failed to read KMS client cert from kmsClientTlsCertPath: ${err.message}`); + throw new Error( + `Failed to read key provider client cert from keyProviderClientTlsCertPath: ${err.message}`, + ); } } // Validate that client certificates are provided for outbound mTLS connections if (config.tlsMode === TlsMode.MTLS) { - if (!config.kmsClientTlsKey || !config.kmsClientTlsCert) { + if (!config.keyProviderClientTlsKey || !config.keyProviderClientTlsCert) { throw new Error( - 'KMS_CLIENT_TLS_KEY_PATH and KMS_CLIENT_TLS_CERT_PATH (or KMS_CLIENT_TLS_KEY and KMS_CLIENT_TLS_CERT) are required for outbound mTLS connections to KMS. Client certificates cannot reuse server certificates for security reasons.', + 'KEY_PROVIDER_CLIENT_TLS_KEY_PATH and KEY_PROVIDER_CLIENT_TLS_CERT_PATH (or KEY_PROVIDER_CLIENT_TLS_KEY and KEY_PROVIDER_CLIENT_TLS_CERT) are required for outbound mTLS connections to key provider. Client certificates cannot reuse server certificates for security reasons.', ); } } diff --git a/src/masterBitgoExpress/routers/accelerateRoute.ts b/src/masterBitgoExpress/routers/accelerateRoute.ts index 7c776899..f1ccd582 100644 --- a/src/masterBitgoExpress/routers/accelerateRoute.ts +++ b/src/masterBitgoExpress/routers/accelerateRoute.ts @@ -80,7 +80,7 @@ const AccelerateResponse: HttpResponse = { * * Send a new transaction to accelerate the targeted unconfirmed transaction either by using Child-Pays-For-Parent (CPFP) or Replace-By-Fee (RBF). * - * Retrieves the private key from KMS using the provided public key, then signs and broadcasts the transaction. + * Retrieves the private key from key provider using the provided public key, then signs and broadcasts the transaction. * * Use this endpoint only with advanced wallets. For other wallet types, use [Accelerate Transaction](https://developers.bitgo.com/reference/expresswalletacceleratetx). * diff --git a/src/masterBitgoExpress/routers/consolidateRoute.ts b/src/masterBitgoExpress/routers/consolidateRoute.ts index 90682b39..f36ee1cf 100644 --- a/src/masterBitgoExpress/routers/consolidateRoute.ts +++ b/src/masterBitgoExpress/routers/consolidateRoute.ts @@ -39,7 +39,7 @@ export const ConsolidateResponse: HttpResponse = { * * Build, sign, and send a consolidation transaction, all in one call. For account-based assets, consolidating the balances in the receive addresses to the base address maximizes the spendable balance of a wallet. * - * Retrieves the private key from KMS using the provided public key or common keychain, then signs and broadcasts the transaction. + * Retrieves the private key from key provider using the provided public key or common keychain, then signs and broadcasts the transaction. * * Use this endpoint only with advanced wallets. For other wallet types, use [Consolidate account (simple)](https://developers.bitgo.com/reference/expresswalletconsolidateaccount). * diff --git a/src/masterBitgoExpress/routers/consolidateUnspentsRoute.ts b/src/masterBitgoExpress/routers/consolidateUnspentsRoute.ts index 5bd8d3a4..1970aefb 100644 --- a/src/masterBitgoExpress/routers/consolidateUnspentsRoute.ts +++ b/src/masterBitgoExpress/routers/consolidateUnspentsRoute.ts @@ -78,7 +78,7 @@ export const ConsolidateUnspentsResponse: HttpResponse = { * * Builds, signs, and sends a transaction to consolidate unspents all in 1 call. Consolidating unspents is only for UTXO-based assets. * - * Retrieves the private key from KMS using the provided public key, then signs and broadcasts the transaction. + * Retrieves the private key from key provider using the provided public key, then signs and broadcasts the transaction. * * Use this endpoint only with advanced wallets. For other wallet types, use [Consolidate unspents (simple)](https://developers.bitgo.com/reference/expresswalletconsolidateunspents). * diff --git a/src/masterBitgoExpress/routers/generateWalletRoute.ts b/src/masterBitgoExpress/routers/generateWalletRoute.ts index 6898b1f5..d6a9af87 100644 --- a/src/masterBitgoExpress/routers/generateWalletRoute.ts +++ b/src/masterBitgoExpress/routers/generateWalletRoute.ts @@ -342,8 +342,8 @@ const GenerateWalletRequest = { * Advanced Wallets - Generate Wallet * * Create a new advanced wallet. Calling this endpoint does the following: - * 1. Generates user keychain in isolated AWM, then sends to KMS (encrypts private key, stores public key mapping). - * 2. Generates backup keychain in isolated AWM, then sends to KMS (encrypts private key, stores public key mapping). + * 1. Generates user keychain in isolated AWM, then sends to key provider (encrypts private key, stores public key mapping). + * 2. Generates backup keychain in isolated AWM, then sends to key provider (encrypts private key, stores public key mapping). * 3. Uploads the user and backup public keys to BitGo. * 4. Creates the BitGo key on the BitGo service. * 5. Creates the wallet on BitGo with the 3 keys. diff --git a/src/masterBitgoExpress/routers/recoveryConsolidationsRoute.ts b/src/masterBitgoExpress/routers/recoveryConsolidationsRoute.ts index c807774d..0ab307c1 100644 --- a/src/masterBitgoExpress/routers/recoveryConsolidationsRoute.ts +++ b/src/masterBitgoExpress/routers/recoveryConsolidationsRoute.ts @@ -117,7 +117,7 @@ const RecoveryConsolidationsWalletResponse: HttpResponse = { * * Recover assets from an advanced wallet with a balance in multiple receive addresses. Build, sign, and send a consolidation and recovery, all in one call. Sign using your user and backup keys. Works for both multisignature and MPC recoveries. * - * Retrieves the private keys from KMS using the provided public keys or common keychain, then signs and returns the broadcastable transaction hex. + * Retrieves the private keys from key provider using the provided public keys or common keychain, then signs and returns the broadcastable transaction hex. * * Note: This endpoint only works when AWM and MBE are running in recovery mode. * diff --git a/src/masterBitgoExpress/routers/recoveryRoute.ts b/src/masterBitgoExpress/routers/recoveryRoute.ts index a73037e4..f7be5727 100644 --- a/src/masterBitgoExpress/routers/recoveryRoute.ts +++ b/src/masterBitgoExpress/routers/recoveryRoute.ts @@ -289,7 +289,7 @@ const RecoveryWalletRequest = { * * Recover assets from an advanced wallet with a balance only in the base address. Works for both multisignature and MPC recoveries. * - * Retrieves the private keys from KMS using the provided public keys or common keychain, then signs and returns the broadcastable transaction hex. + * Retrieves the private keys from key provider using the provided public keys or common keychain, then signs and returns the broadcastable transaction hex. * * Note: This endpoint only works when AWM and MBE are running in recovery mode. * diff --git a/src/masterBitgoExpress/routers/sendManyRoute.ts b/src/masterBitgoExpress/routers/sendManyRoute.ts index 608acdf3..a7f61990 100644 --- a/src/masterBitgoExpress/routers/sendManyRoute.ts +++ b/src/masterBitgoExpress/routers/sendManyRoute.ts @@ -212,7 +212,7 @@ export const SendManyResponse: HttpResponse = { * * Send coins or tokens to one or more recipients. You can use this endpoint to schedule outgoing transactions in bulk, lowering your aggregate amount of blockchain fees. * - * Retrieves the private key from KMS using the provided public key, then signs and broadcasts the transaction. + * Retrieves the private key from key provider using the provided public key, then signs and broadcasts the transaction. * * Works with both multisignature and MPC wallets. * diff --git a/src/masterBitgoExpress/routers/signAndSendMpcRoute.ts b/src/masterBitgoExpress/routers/signAndSendMpcRoute.ts index 5ef9e269..d66f474f 100644 --- a/src/masterBitgoExpress/routers/signAndSendMpcRoute.ts +++ b/src/masterBitgoExpress/routers/signAndSendMpcRoute.ts @@ -23,7 +23,7 @@ export const SignMpcResponse: HttpResponse = { * * Sign and send a MPC transaction. * - * Retrieves the private key from KMS using the provided common keychain, then signs and broadcasts the transaction. + * Retrieves the private key from key provider using the provided common keychain, then signs and broadcasts the transaction. * * Use this endpoint only with advanced wallets. For other wallet types, use [Sign MPC transaction](https://developers.bitgo.com/reference/expresswalletsigntxtss). * diff --git a/src/shared/responseHandler.ts b/src/shared/responseHandler.ts index ddbf74df..ebedca54 100644 --- a/src/shared/responseHandler.ts +++ b/src/shared/responseHandler.ts @@ -42,7 +42,7 @@ function checkApiServerRunning(req: BitGoRequest, error: any): void { if (error.code === 'ECONNREFUSED' || error.code === 'ENOTFOUND' || error.code === 'ECONNRESET') { throw new Error( `${ - isMbe ? 'Advanced Wallet Manager' : 'KMS' + isMbe ? 'Advanced Wallet Manager' : 'key provider' } API service is not running or unreachable. Please check if the service is available.`, ); } @@ -50,7 +50,7 @@ function checkApiServerRunning(req: BitGoRequest, error: any): void { if (error.code === 'ETIMEDOUT' || error.timeout) { throw new Error( `${ - isMbe ? 'Advanced Wallet Manager' : 'KMS' + isMbe ? 'Advanced Wallet Manager' : 'key provider' } API request timed out. The service may be overloaded or unreachable.`, ); } diff --git a/src/shared/types/index.ts b/src/shared/types/index.ts index 853526a7..5dadcf65 100644 --- a/src/shared/types/index.ts +++ b/src/shared/types/index.ts @@ -26,15 +26,15 @@ export interface BaseConfig { // Advanced wallet manager mode specific configuration export interface AdvancedWalletManagerConfig extends BaseConfig { appMode: AppMode.ADVANCED_WALLET_MANAGER; - // KMS settings - kmsUrl: string; - kmsServerCaCertPath?: string; - kmsServerCaCert?: string; - kmsClientTlsKeyPath?: string; - kmsClientTlsCertPath?: string; - kmsClientTlsKey?: string; - kmsClientTlsCert?: string; - kmsServerCertAllowSelfSigned?: boolean; + // key provider settings + keyProviderUrl: string; + keyProviderServerCaCertPath?: string; + keyProviderServerCaCert?: string; + keyProviderClientTlsKeyPath?: string; + keyProviderClientTlsCertPath?: string; + keyProviderClientTlsKey?: string; + keyProviderClientTlsCert?: string; + keyProviderServerCertAllowSelfSigned?: boolean; // mTLS server settings serverTlsKeyPath?: string; From 30388aa780924687c2a4a33f4371bb1a1bc9f06a Mon Sep 17 00:00:00 2001 From: Pranav Jain Date: Wed, 29 Apr 2026 15:27:16 -0400 Subject: [PATCH 2/2] chore: lowercase key provider in prose --- README.md | 18 +++++++++--------- demo-key-provider-script/aws-interface.md | 12 ++++++------ demo-key-provider-script/dinamo-interface.md | 8 ++++---- docker-compose.yml | 2 +- key-provider-api-spec.yaml | 4 ++-- 5 files changed, 22 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index 461b8b5e..2c6a466f 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ Advanced wallets operate in two modes: Key features include: - **Complete Infrastructure Control** - Host and manage all components in your own secure environment. -- **KMS/HSM Integration** - Bring your own KMS or HSM by implementing the provided [Advanced Wallets Key Provider API interface specification](./key-provider-api-spec.yaml). Reference implementations available for [AWS HSM](./demo-key-provider-script/aws-interface.md) and [Dinamo HSM](./demo-key-provider-script/dinamo-interface.md). +- **KMS/HSM Integration** - Bring your own KMS or HSM by implementing the provided [advanced wallets key provider API interface specification](./key-provider-api-spec.yaml). Reference implementations available for [AWS HSM](./demo-key-provider-script/aws-interface.md) and [Dinamo HSM](./demo-key-provider-script/dinamo-interface.md). - **Network Isolation** - Advanced Wallet Manager operates in a completely isolated network segment with no external internet access. - **mTLS Security** - Optional mutual TLS with client certificate validation for secure inter-service communications. - **Flexible Configuration** - Environment-based setup with file or variable-based certificates. @@ -38,7 +38,7 @@ Key features include: ## Architecture -- **Advanced Wallet Manager** (Port 3080) - An isolated signing server with no internet access that only connects to your Key Provider API implementation for key operations. +- **Advanced Wallet Manager** (Port 3080) - An isolated signing server with no internet access that only connects to your key provider API implementation for key operations. - **Master Express** (Port 3081) - An API gateway providing end-to-end wallet creation and transaction support, integrating [BitGo APIs](https://developers.bitgo.com/reference/overview#/) with secure communication to Advanced Wallet Manager. ## Installation @@ -49,7 +49,7 @@ Key features include: - **npm** or **yarn** package manager. - **OpenSSL** for certificate generation. - **Docker** and **Docker Compose** for containerized deployment (or you can use **Podman** as alternative to Docker). -- **Key Provider API Implementation** - You must implement the [Key Provider API interface specification](./key-provider-api-spec.yaml) to connect your KMS/HSM to the Advanced Wallet Manager. Reference implementations available: +- **key provider API implementation** - You must implement the [key provider API interface specification](./key-provider-api-spec.yaml) to connect your KMS/HSM to the Advanced Wallet Manager. Reference implementations available: - [AWS HSM Implementation Example](./demo-key-provider-script/aws-interface.md) - [Dinamo HSM Implementation Example](./demo-key-provider-script/dinamo-interface.md) @@ -168,9 +168,9 @@ curl -X POST http://localhost:3081/ping/advancedWalletManager | Variable | Description | Default | Required | | ------------------------------ | ---------------------------------- | ------- | -------- | | `ADVANCED_WALLET_MANAGER_PORT` | Port to listen on | `3080` | ❌ | -| `KEY_PROVIDER_URL` | URL to your Key Provider API implementation | - | ✅ | +| `KEY_PROVIDER_URL` | URL to your key provider API implementation | - | ✅ | -> **Note:** The `KEY_PROVIDER_URL` points to your implementation of the Key Provider API interface. You must implement this interface to connect your KMS/HSM. See [Prerequisites](#prerequisites) for the specification and examples. +> **Note:** The `KEY_PROVIDER_URL` points to your implementation of the key provider API interface. You must implement this interface to connect your KMS/HSM. See [Prerequisites](#prerequisites) for the specification and examples. ### Master Express Settings @@ -232,7 +232,7 @@ curl -X POST http://localhost:3081/ping/advancedWalletManager | `AWM_SERVER_CA_CERT` | AWM server CA certificate (alternative) | PEM string | | `AWM_SERVER_CERT_ALLOW_SELF_SIGNED` | Allow self-signed AWM server certificates | Boolean (default: `false`) | -**For Advanced Wallet Manager → Key Provider:** +**For Advanced Wallet Manager → key provider:** | Variable | Description | Format | | ------------------------------------------- | -------------------------------------------------- | -------------------------- | @@ -240,8 +240,8 @@ curl -X POST http://localhost:3081/ping/advancedWalletManager | `KEY_PROVIDER_CLIENT_TLS_KEY` | Client private key (alternative) | PEM string | | `KEY_PROVIDER_CLIENT_TLS_CERT_PATH` | Client certificate file path | File path | | `KEY_PROVIDER_CLIENT_TLS_CERT` | Client certificate (alternative) | PEM string | -| `KEY_PROVIDER_SERVER_CA_CERT_PATH` | key provider server CA certificate file path | File path | -| `KEY_PROVIDER_SERVER_CA_CERT` | key provider server CA certificate (alternative) | PEM string | +| `KEY_PROVIDER_SERVER_CA_CERT_PATH` | Key provider server CA certificate file path | File path | +| `KEY_PROVIDER_SERVER_CA_CERT` | Key provider server CA certificate (alternative) | PEM string | | `KEY_PROVIDER_SERVER_CERT_ALLOW_SELF_SIGNED` | Allow self-signed key provider server certificates | Boolean (default: `false`) | > **Note:** For security reasons, when `TLS_MODE=mtls`, outbound client certificates are required and cannot reuse server certificates. When `TLS_MODE=disabled`, these certificates aren't required. @@ -351,7 +351,7 @@ The setup creates two distinct networks: ### Prerequisites 1. **Install Docker and Docker Compose** -2. **Ensure your Key Provider API implementation is running** on your host machine (typically on port 3000) +2. **Ensure your key provider API implementation is running** on your host machine (typically on port 3000) ### Quick Start diff --git a/demo-key-provider-script/aws-interface.md b/demo-key-provider-script/aws-interface.md index 260da6e0..8f6f9977 100644 --- a/demo-key-provider-script/aws-interface.md +++ b/demo-key-provider-script/aws-interface.md @@ -1,10 +1,10 @@ # AWS HSM Key Provider Implementation Documentation -This document provides a reference implementation for integrating the 4 Key Provider API's with AWS HSM, covering the complete request-response flow from API handlers to HSM operations. +This document provides a reference implementation for integrating the 4 key provider API's with AWS HSM, covering the complete request-response flow from API handlers to HSM operations. ## ⚠️ Security Recommendation -For production key provider implementations, consider implementing the Key Provider API in a C++ like language, because JavaScript does not support low-level memory management. Depending on your solution, direct memory management with explicit memory allocation/deallocation might be desirable. +For production key provider implementations, consider implementing the key provider API in a C++ like language, because JavaScript does not support low-level memory management. Depending on your solution, direct memory management with explicit memory allocation/deallocation might be desirable. Also consider implementing low level cryptographic operations using low-level languages like C++ or Rust. They typically provide easier and more efficient data manipulation and transaformation. @@ -14,7 +14,7 @@ When working with AWS HSM, adhere to their guidances and best practices for the ## API Overview -The Key Provider API provides secure key management through four main endpoints that integrate with AWS HSM: +The key provider API provides secure key management through four main endpoints that integrate with AWS HSM: - `POST /key` - Store private keys using envelope encryption - `GET /key/{pub}` - Retrieve private keys using envelope decryption @@ -25,10 +25,10 @@ The Key Provider API provides secure key management through four main endpoints All 4 API's implementation should follow roughly the same dataflow as outlined bellow: ``` -API Request → Handler → Key Provider → AWS HSM → Key Provider → Database (if required) → Response +API Request → Handler → key provider → AWS HSM → key provider → Database (if required) → Response ``` -A Key Provider is the implementation of the code that is in charge of making the necessary calls to the HSM directly. You might have multiple providers in your solution, one for each 3rd party HSM that you wish to use, for example. +A key provider is the implementation of the code that is in charge of making the necessary calls to the HSM directly. You might have multiple providers in your solution, one for each 3rd party HSM that you wish to use, for example. ### Handler-to-Provider Mapping @@ -67,7 +67,7 @@ The 3 levels consist of the root-level key from the KMS/HSM, 2nd level data keys ### Root Key Creation -This following needs to be only run once. The Key Provider should be functional with just one root-level key. +This following needs to be only run once. The key provider should be functional with just one root-level key. ```typescript import * as awskms from '@aws-sdk/client-kms'; diff --git a/demo-key-provider-script/dinamo-interface.md b/demo-key-provider-script/dinamo-interface.md index e8e074d5..42d7e5a3 100644 --- a/demo-key-provider-script/dinamo-interface.md +++ b/demo-key-provider-script/dinamo-interface.md @@ -2,7 +2,7 @@ ## ⚠️ Security Recommendation -**For production key provider implementations, consider implementing the Key Provider API in a C++ like language, or use typed arrays like Uint8Array for all sensitive data because JavaScript does not support secure memory management.** +**For production key provider implementations, consider implementing the key provider API in a C++ like language, or use typed arrays like Uint8Array for all sensitive data because JavaScript does not support secure memory management.** **Recommended Alternatives:** - **C++/Rust**: Languages with explicit memory management and secure allocation @@ -10,7 +10,7 @@ - **Native Addons**: Implement cryptographic operations in native C++ modules - **Hardware Security**: Use HSM-backed secure memory when available -This document provides a reference implementation for integrating the 4 Key Provider API's with Dinamo HSM, covering the complete request-response flow from API handlers to HSM operations. +This document provides a reference implementation for integrating the 4 key provider API's with Dinamo HSM, covering the complete request-response flow from API handlers to HSM operations. ## Demo Scripts @@ -20,7 +20,7 @@ This document provides a reference implementation for integrating the 4 Key Prov ## Quick Overview -The Key Provider API provides secure key management through four main endpoints that integrate with Dinamo HSM: +The key provider API provides secure key management through four main endpoints that integrate with Dinamo HSM: - `POST /key` - Store private keys using envelope encryption - `GET /key/{pub}` - Retrieve private keys using envelope decryption @@ -30,7 +30,7 @@ The Key Provider API provides secure key management through four main endpoints ## Architecture Flow ``` -API Request → Handler → Key Provider → Dinamo HSM → Database → Response +API Request → Handler → key provider → Dinamo HSM → Database → Response ``` ### Handler-to-Provider Mapping diff --git a/docker-compose.yml b/docker-compose.yml index 7c806f81..3a5050ce 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -22,7 +22,7 @@ services: - TLS_MODE=disabled - CLIENT_CERT_ALLOW_SELF_SIGNED=true - # key provider settings (required) + # Key provider settings (required) - KEY_PROVIDER_URL=http://172.20.0.1:3000 # UPDATE TO YOUR OWN key provider URL - KEY_PROVIDER_SERVER_CERT_ALLOW_SELF_SIGNED=true diff --git a/key-provider-api-spec.yaml b/key-provider-api-spec.yaml index 99adfa76..040c9a15 100644 --- a/key-provider-api-spec.yaml +++ b/key-provider-api-spec.yaml @@ -7,11 +7,11 @@ info: # API Interface for Advanced Wallet Integration This specification defines the required API interface that must be implemented by clients to integrate - their KMS/HSM provider with advanced wallets through the Advanced Wallets Key Provider interface. + their KMS/HSM provider with advanced wallets through the advanced wallets key provider interface. ## Purpose - Clients must implement this Advanced Wallets Key Provider API specification to enable secure cryptographic key storage and management + Clients must implement this advanced wallets key provider API specification to enable secure cryptographic key storage and management for advanced wallets. The implementation should connect to your organization's KMS/HSM provider. ## Implementation Requirements