Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 111 additions & 0 deletions docs/KeyManager.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
# KeyManager Architecture

## Overview

KeyManager supports multiple key source types through a provider abstraction pattern. This allows the system to support different key management solutions (raw private keys, GCP KMS, AWS KMS, etc.) without changing the core KeyManager logic.

## Architecture

```
KeyManager
├── IKeyProvider (interface)
│ ├── RawPrivateKeyProvider (implemented)
│ ├── GcpKmsProvider (future)
│ └── AwsKmsProvider (future)
└── Factory Pattern
└── createKeyProvider(config) → IKeyProvider
```

## Components

### 1. IKeyProvider Interface (`types.ts`)

Base interface that all key providers must implement:

```typescript
interface IKeyProvider {
getType(): KeyProviderType
initialize(): Promise<void>
getPeerId(): PeerId
getLibp2pPrivateKey(): any
getLibp2pPublicKey(): Uint8Array
getEthAddress(): string
getRawPrivateKeyBytes(): Uint8Array
cleanup?(): Promise<void>
}
```

### 2. KeyProviderType Enum

Defines supported key provider types:

- `RAW` - Raw private key from environment variable

### 3. RawPrivateKeyProvider (`providers/RawPrivateKeyProvider.ts`)

Implementation for raw private keys:

- Loads private key from config
- Derives libp2p keys and peerId
- Derives Ethereum address
- Provides raw private key bytes for EVM signer creation

### 4. Factory (`factory.ts`)

Creates and initializes the appropriate key provider:

```typescript
createKeyProvider(config: KeyProviderConfig): Promise<IKeyProvider>
```

### 5. KeyManager (`index.ts`)

Main class that:

- Wraps a key provider
- Manages EVM signer caching
- Provides unified API for key access

## Usage

### Current Usage (Raw Private Key)

```typescript
import { createKeyProvider } from './components/KeyManager/index.js'

const keyManager = new KeyManager(config)
```

## Adding New Key Providers

To add a new key provider (e.g., AWS KMS):

1. **Add to KeyProviderType**:

```typescript
export type KeyProviderType = 'raw' | 'gcp-kms' | 'aws' //new
```

2. **Create provider class**:

```typescript
export class AwsKmsProvider implements IKeyProvider {
// Implement all interface methods
}
```

3. **Update factory**:

```typescript
case 'aws'':
provider = new AwsKmsProvider(config)
break
```

## Benefits

1. **Extensibility**: Easy to add new key sources
2. **Testability**: Can mock key providers for testing
3. **Security**: Supports secure key management solutions (KMS)
4. **Separation of Concerns**: Key retrieval logic separated from key usage logic
82 changes: 82 additions & 0 deletions src/@types/KeyManager.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import type { PeerId } from '@libp2p/interface'
import { Wallet } from 'ethers'
import { EncryptMethod } from './fileObject.js'
import { Readable } from 'stream'
/**
* Key provider types supported by KeyManager
*/

export type KeyProviderType = 'raw' | 'gcp-kms'
/**
* Base interface for key providers.
* Each key provider implementation must implement this interface.
* Initialization happens in the constructor.
*/
export interface IKeyProvider {
/**
* Get the type of this key provider
*/
getType(): string

/**
* Get the libp2p PeerId
*/
getPeerId(): PeerId

/**
* Get the libp2p private key
*/
getLibp2pPrivateKey(): any // libp2p PrivateKey type

/**
* Get the libp2p public key as Uint8Array
*/
getPublicKey(): Uint8Array

/**
* Get the Ethereum address derived from the private key
*/
getEthAddress(): string
/**
* Get the Ethereum Wallet derived from the private key
*/
getEthWallet(): Wallet

/**
* Get the raw private key bytes for EVM signer creation.
* This is used to create ethers Wallet instances.
*/
getRawPrivateKeyBytes(): Uint8Array

/**
* Encrypts data according to a given algorithm
* @param data data to encrypt
* @param algorithm encryption algorithm AES or ECIES
*/
encrypt(data: Uint8Array, algorithm: EncryptMethod): Promise<Buffer>

/**
* Decrypts data according to a given algorithm using node keys
* @param data data to decrypt
* @param algorithm decryption algorithm AES or ECIES
*/
decrypt(data: Uint8Array, algorithm: EncryptMethod): Promise<Buffer>
/**
* Encrypts a stream according to a given algorithm using node keys
* @param inputStream - Readable stream to encrypt
* @param algorithm - Encryption algorithm AES or ECIES
* @returns Readable stream with encrypted data
*/
encryptStream(inputStream: Readable, algorithm: EncryptMethod): Readable
/**
* Decrypts a stream according to a given algorithm using node keys
* @param inputStream - Readable stream to decrypt
* @param algorithm - Decryption algorithm AES or ECIES
* @returns Readable stream with decrypted data
*/
decryptStream(inputStream: Readable, algorithm: EncryptMethod): Readable
/**
* Cleanup resources if needed (e.g., close connections)
*/
cleanup?(): Promise<void>
}
10 changes: 10 additions & 0 deletions src/@types/OceanNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,16 @@ export interface OceanNodeKeys {
publicKey: any
privateKey: any
ethAddress: string
type?: string | 'raw' | 'gcp-kms' // Key provider type (raw, gcp-kms, etc.)
// Raw private key config (when type is 'raw')
// GCP KMS config (when type is 'gcp-kms')
gcpKmsConfig?: {
projectId: string
location: string
keyRing: string
keyName: string
keyVersion?: string
}
}
/* eslint-disable no-unused-vars */
export enum dhtFilterMethod {
Expand Down
58 changes: 54 additions & 4 deletions src/OceanNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ import { GENERIC_EMOJIS, LOG_LEVELS_STR } from './utils/logging/Logger.js'
import { BaseHandler } from './components/core/handler/handler.js'
import { C2DEngines } from './components/c2d/compute_engines.js'
import { Auth } from './components/Auth/index.js'
import { KeyManager } from './components/KeyManager/index.js'
import { BlockchainRegistry } from './components/BlockchainRegistry/index.js'
import { Blockchain } from './utils/blockchain.js'

export interface RequestLimiter {
requester: string | string[] // IP address or peer ID
Expand Down Expand Up @@ -41,8 +44,20 @@ export class OceanNode {
private db?: Database,
private node?: OceanP2P,
private provider?: OceanProvider,
private indexer?: OceanIndexer
private indexer?: OceanIndexer,
public keyManager?: KeyManager,
public blockchainRegistry?: BlockchainRegistry
) {
if (keyManager) {
this.keyManager = keyManager
} else {
this.keyManager = new KeyManager(config)
}
if (blockchainRegistry) {
this.blockchainRegistry = blockchainRegistry
} else {
this.blockchainRegistry = new BlockchainRegistry(this.keyManager, config)
}
this.coreHandlers = CoreHandlersRegistry.getInstance(this)
this.requestMap = new Map<string, RequestLimiter>()
this.config = config
Expand All @@ -55,7 +70,8 @@ export class OceanNode {
if (this.config) {
this.escrow = new Escrow(
this.config.supportedNetworks,
this.config.claimDurationTimeout
this.config.claimDurationTimeout,
this.blockchainRegistry
)
}
}
Expand All @@ -67,11 +83,28 @@ export class OceanNode {
node?: OceanP2P,
provider?: OceanProvider,
indexer?: OceanIndexer,
keyManager?: KeyManager,
blockchainRegistry?: BlockchainRegistry,
newInstance: boolean = false
): OceanNode {
if (!OceanNode.instance || newInstance) {
if (!keyManager || !blockchainRegistry) {
if (!config) {
throw new Error('KeyManager and BlockchainRegistry are required')
}
keyManager = new KeyManager(config)
blockchainRegistry = new BlockchainRegistry(keyManager, config)
}
// prepare compute engines
this.instance = new OceanNode(config, db, node, provider, indexer)
this.instance = new OceanNode(
config,
db,
node,
provider,
indexer,
keyManager,
blockchainRegistry
)
}
return this.instance
}
Expand Down Expand Up @@ -143,12 +176,29 @@ export class OceanNode {
return this.auth
}

public getKeyManager(): KeyManager {
return this.keyManager
}

public getBlockchainRegistry(): BlockchainRegistry {
return this.blockchainRegistry
}

/**
* Get a Blockchain instance for the given chainId.
* Delegates to BlockchainRegistry.
*/
public getBlockchain(chainId: number): Blockchain | null {
return this.blockchainRegistry.getBlockchain(chainId)
}

public setConfig(config: OceanNodeConfig) {
this.config = config
if (this.config) {
this.escrow = new Escrow(
this.config.supportedNetworks,
this.config.claimDurationTimeout
this.config.claimDurationTimeout,
this.blockchainRegistry
)
}
}
Expand Down
Loading
Loading