Skip to content

Commit

Permalink
feat: new package aws-kms-adapter (#1412)
Browse files Browse the repository at this point in the history
* feat: added new app

* feat: additional project structure

* feat: some refactor

* feat: some refactor

* feat: change

* feat: change

* feat: more changes

* feat: more changes

* feat: more changes

* feat: more changes

* feat: some refactor

* feat: some refactor

* feat: aws kms as a new package

* feat: aws kms as a new package

* feat: added more methods

* feat: more changes

* feat: more changes

* feat: more changes

* feat: more changes

* feat: more changes

* feat: more changes

* feat: more changes

* feat: added aws credentials

* feat: breaking commit, to be fixed in the next one

* feat: added error handling

* feat: added exception

* feat: added new error

* feat: error handling

* feat: added test file

* feat: added test config

* feat: added test config

* feat: decoding public key is working as expected

* feat: decoding public key is working as expected

* feat: sign working

* feat: sign working

* feat: all methods except for signTypedData tested and working

* feat: added gitignore

* feat: added gitignore

* feat: more tests

* feat: more tests

* feat: more tests

* feat: unit tests started

* feat: unit tests completed

* feat: more tests

* feat: ensuring that we create a single signer instance

* feat: more tests

* feat: more tests

* feat: more tests

* feat: more tests

* feat: more tests

* feat: more tests

* feat: more tests

* feat: more tests

* feat: more tests

* feat: more tests

* feat: more tests

* feat: more tests

* feat: first localstack config added

* feat: created key with custom material

* feat: created key with custom material

* feat: created key with custom material

* feat: tests working

* feat: added test config file

* feat: added test config file

* feat: the value should be message for EIP712

* feat: amended scripts to fit the gh actions structure

* feat: file renamed since it targets testnet

* feat: file renamed since it targets testnet

* feat: file renamed since it targets testnet

* feat: file renamed since it targets testnet

* feat: file renamed since it targets testnet

* feat: undefined instead of null

* feat: undefined instead of null

* feat: using ethers since viem does not check the types at runtime

* feat: fixed tests

* feat: fixed tests

* feat: readme

* feat: eslint + readme updated

* feat: modified script so we run down regardless the output

* feat: modified script so we run down regardless the output

* feat: modified script so we run down regardless the output

* feat: modified script so we run down regardless the output

* feat: change in one of the comments
  • Loading branch information
freemanzMrojo authored Oct 21, 2024
1 parent e281041 commit cbd3ea7
Show file tree
Hide file tree
Showing 36 changed files with 3,091 additions and 44 deletions.
2 changes: 1 addition & 1 deletion .github/CODE_OF_CONDUCT.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#VeChain SDK Code of Conduct
# VeChain SDK Code of Conduct

## 1. Introduction

Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ Welcome to the VeChain SDK repository! Here's a breakdown of our organized struc
- `./docker`: Streamline your development and deployment with our comprehensive Docker configurations. This directory offers Dockerfile setups designed to create consistent, reproducible environments.
- `./docs`: Your go-to destination for comprehensive documentation. Explore demonstrative examples showcasing the prowess of our SDK. Knowledge is power, and our docs are here to enlighten your path.
- `./packages`: A hub for our monorepo packages, each serving a distinct purpose:
- `./packages/aws-kms-adapter`: The AWS KMS Adapter provides a secure way to sign transactions using AWS Key Management Service (KMS). This adapter allows you to leverage AWS KMS to manage and protect your private keys, ensuring that sensitive cryptographic operations are performed in a secure environment.
- `./packages/core`: The heart of the SDK, housing essential modules for fundamental operations like hashing and cryptography. Dive into the core for the building blocks of your decentralized dreams.
- `./packages/errors`: Delve into the world of error handling with the error package. This module is dedicated to managing and customizing errors within the SDK, ensuring your development experience remains resilient and smooth.
- `./packages/hardhat-plugin`: Seamlessly integrate the VeChain SDK with Hardhat, the Ethereum development environment. This plugin provides a bridge between the VeChain SDK and the Ethereum ecosystem, enabling you to leverage the best of both worlds.
Expand Down
40 changes: 40 additions & 0 deletions docker-compose.localstack.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
services:
localstack:
container_name: localstack
hostname: localstack
image: localstack/localstack:3.3.0
restart: always
ports:
- 4599:4599
environment:
- SERVICES=kms
- HOSTNAME_EXTERNAL=localstack
- DOCKER_HOST=unix:///var/run/docker.sock
- GATEWAY_LISTEN=0.0.0.0:4599
- AWS_ENDPOINT_URL=http://localstack:4599
- AWS_DEFAULT_REGION=eu-west-1
volumes:
- ./localstack/init:/etc/localstack/init/ready.d
- /var/run/docker.sock:/var/run/docker.sock
entrypoint:
[
"/bin/sh",
"-c",
"apt-get update && apt-get -y install jq && docker-entrypoint.sh"
]
healthcheck:
test:
- CMD
- bash
- -c
- $$(awslocal kms list-keys | jq '.Keys | length | . == 1') || exit 1; # There is 1 key at the moment
interval: 5s
timeout: 20s
start_period: 2s
thor-solo:
profiles:
- thor-solo
depends_on:
localstack:
condition: service_healthy

8 changes: 8 additions & 0 deletions localstack/init/kms.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#!/bin/bash

CUSTOM_KEY_MATERIAL="f5KQzETF/SuV/iHWrW/l+pwXfhzW87TJapexPgnqoVg="
CUSTOM_ID="bffb20d8-35ca-4408-9d54-f775b929b38d"

# The command succeeds but the key is not created with the custom key material (should be fixed once this is clarified: https://github.com/localstack/localstack/issues/11678)
# awslocal kms create-key --key-usage SIGN_VERIFY --key-spec ECC_SECG_P256K1 --tags "[{\"TagKey\":\"_custom_key_material_\",\"TagValue\":\"$CUSTOM_KEY_MATERIAL\"},{\"TagKey\":\"_custom_id_\",\"TagValue\":\"$CUSTOM_ID\"}]"
awslocal kms create-key --key-usage SIGN_VERIFY --key-spec ECC_SECG_P256K1 --tags "[{\"TagKey\":\"_custom_id_\",\"TagValue\":\"$CUSTOM_ID\"}]"
25 changes: 25 additions & 0 deletions packages/aws-kms-adapter/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"extends": ["../../.eslintrc.json"],
"rules": {
"import/no-restricted-paths": [
"error",
{
"zones": [
{
"target": "./src",
"from": [
"../core",
"../errors",
"../ethers-adapter",
"../hardhat-plugin",
"../logging",
"../network",
"../rpc-proxy"
],
"message": "Please import using @vechain/sdk-<the-module>"
}
]
}
]
}
}
1 change: 1 addition & 0 deletions packages/aws-kms-adapter/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
tests/aws-credentials.json
172 changes: 172 additions & 0 deletions packages/aws-kms-adapter/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
# AWS KMS Adapter for VeChain SDK

The AWS KMS Adapter for VeChain SDK provides a secure way to sign transactions using AWS Key Management Service (KMS). This adapter allows you to leverage AWS KMS to manage and protect your private keys, ensuring that sensitive cryptographic operations are performed in a secure environment.

## Features

- **Secure Key Management**: Use AWS KMS to securely manage and protect your private keys.
- **Transaction Signing**: Sign VeChain transactions using keys stored in AWS KMS.
- **Integration with VeChain SDK**: Seamlessly integrate with the VeChain SDK for blockchain interactions.
- **[WIP] Sign and send transactions using a delegator key**: You can specify the key ID of a delegator key to leverage this VeChain feature for signing and sending transactions.

## Installation

To install the AWS KMS Adapter, use the following command:

```sh
yarn add @vechain/sdk-aws-kms-adapter
```

## Test

To run all the tests, including the ones relying on a local instance of Thor Solo + LocalStack, please run:

```bash
yarn test:solo
```

## Usage

To integrate this into your code, depending on how you plan to manage your AWS credentials, you can choose one of the following examples.

Within this repo, you can create a credentials file called `aws-credentials.json` with your custom credentials under the `tests` folder in case you want to give it a try before integrating with your project. A valid format would be as follows:

```json
{
// AWS KMS keyId (mandatory)
"keyId": "00000000-0000-0000-0000-000000000000",
// AWS region (mandatory)
"region": "eu-west-1",
// AWS credentials (optional)
"credentials": {
// AWS access key id (mandatory if credentials)
"accessKeyId": "test",
// AWS secret access key (mandatory if credentials)
"secretAccessKey": "test",
// AWS session token if SSO is configured (optional)
"sessionToken": "test"
},
// AWS endpoint (optional, to be used locally along with LocalStack)
"endpoint": "http://localhost:4599"
}
```

### IAM roles

This is the preferred way. If you integrate this library in an app deployed in AWS following with IAM roles, you can just do as follows:

```ts
import { KMSVeChainProvider, KMSVeChainSigner } from '@vechain/sdk-aws-kms-adapter';
import {
THOR_SOLO_URL,
ThorClient
} from '@vechain/sdk-network';
...

interface AwsClientParameters {
keyId: string;
region: string;
credentials?: {
accessKeyId: string;
secretAccessKey: string;
sessionToken?: string;
};
endpoint?: string;
}

...

const thorClient = ThorClient.fromUrl(THOR_SOLO_URL);
const provider = new KMSVeChainProvider(
thorClient,
awsClientParameters.keyId,
awsClientParameters.region
);
const signer = new KMSVeChainSigner(provider);
// Signing typed data as per EIP712
const signature = await signer.signTypedData(
typedData.domain,
typedData.types,
typedData.data
);
```

### AWS credentials (SSO)

This way you can connect to your AWS account by using `accessKeyId`, `secretAccessKey` and `sessionToken` if SSO is enabled.

```ts
import { KMSVeChainProvider, KMSVeChainSigner } from '@vechain/sdk-aws-kms-adapter';
import {
signerUtils,
THOR_SOLO_URL,
ThorClient
} from '@vechain/sdk-network';
...

interface AwsClientParameters {
keyId: string;
region: string;
credentials?: {
accessKeyId: string;
secretAccessKey: string;
sessionToken?: string;
};
endpoint?: string;
}

...

const thorClient = ThorClient.fromUrl(THOR_SOLO_URL);
const provider = new KMSVeChainProvider(
thorClient,
awsClientParameters.keyId,
awsClientParameters.region
awsClientParameters.credentials
);
const signer = new KMSVeChainSigner(provider);
// Signing and sending a transaction
const receipt = await signer.sendTransaction(
signerUtils.transactionBodyToTransactionRequestInput(
txBody,
originAddress
)
);
```

### AWS endpoint (LocalStack)

You can also leverage LocalStack so you can try the library locally. Sample values are included in the file `tests/test-aws-credentials.json`.

```ts
import { KMSVeChainProvider, KMSVeChainSigner } from '@vechain/sdk-aws-kms-adapter';
import {
THOR_SOLO_URL,
ThorClient
} from '@vechain/sdk-network';
...

interface AwsClientParameters {
keyId: string;
region: string;
credentials?: {
accessKeyId: string;
secretAccessKey: string;
sessionToken?: string;
};
endpoint?: string;
}

...

const thorClient = ThorClient.fromUrl(THOR_SOLO_URL);
const provider = new KMSVeChainProvider(
thorClient,
awsClientParameters.keyId,
awsClientParameters.region
awsClientParameters.credentials
);
const signer = new KMSVeChainSigner(provider);
// Returns the address related to the KMS key
const address = await signer.getAddress();
```
23 changes: 23 additions & 0 deletions packages/aws-kms-adapter/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/** @type {import('ts-jest').JestConfigWithTsJest} */
// Coverage threshold would apply to yarn test, not yarn test:unit
const isUnitTest = process.env.UNIT;

module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
coverageReporters: ['html', 'lcov', 'json'],
runner: 'groups',
reporters: ['default', 'jest-junit'],
workerThreads: true,
coverageThreshold:
isUnitTest !== 'true'
? {
global: {
branches: 100,
functions: 100,
lines: 99,
statements: 99
}
}
: undefined
};
34 changes: 34 additions & 0 deletions packages/aws-kms-adapter/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
"name": "sdk-aws-kms-adapter",
"description": "This module implements the VeChain abstract signer so it is integrated with AWS KMS",
"version": "1.0.0-beta.32",
"main": "dist/index.js",
"module": "dist/index.mjs",
"types": "dist/index.d.ts",
"author": "VeChain Foundation",
"license": "MIT",
"files": [
"dist",
"src",
"package.json",
"README.md",
"LICENSE"
],
"scripts": {
"build": "rm -rf ./dist && tsup-node src/index.ts --format cjs,esm --dts",
"lint": "eslint --ext .ts src --ext .ts tests",
"format": "prettier --write src/**/*.ts tests/**/*.ts",
"start-thor-solo": "echo 'Starting thor solo node ...' && docker compose -f ../../docker-compose.thor.yml -f ../../docker-compose.localstack.yml --profile thor-solo up -d --wait && echo '\nThor solo node and localstack started ...'",
"stop-thor-solo": "echo 'Stopping thor solo node ...' && docker compose -f ../../docker-compose.thor.yml -f ../../docker-compose.localstack.yml --profile thor-solo down && echo 'Thor solo node and localstack stopped ...'",
"test": "docker compose -f ../../docker-compose.localstack.yml up -d && sleep 10 && yarn test:all; ret=$?; docker compose -f ../../docker-compose.localstack.yml down; exit $ret",
"test:all": "rm -rf ./coverage && jest --coverage --coverageDirectory=coverage --group=integration --group=unit",
"test:solo": "(yarn start-thor-solo && yarn test:all && yarn stop-thor-solo) || yarn stop-thor-solo",
"test:unit": "rm -rf ./coverageUnit && UNIT=true jest --coverage --coverageDirectory=coverageUnit --group=unit"
},
"dependencies": {
"@aws-sdk/client-kms": "^3.529.1",
"@vechain/sdk-errors": "1.0.0-beta.32",
"@vechain/sdk-network": "1.0.0-beta.32",
"asn1js": "^3.0.5"
}
}
Loading

1 comment on commit cbd3ea7

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test Coverage

Summary

Lines Statements Branches Functions
Coverage: 99%
99.07% (4307/4347) 97.77% (1403/1435) 99.11% (894/902)
Title Tests Skipped Failures Errors Time
core 799 0 💤 0 ❌ 0 🔥 2m 12s ⏱️
network 735 0 💤 0 ❌ 0 🔥 4m 49s ⏱️
errors 42 0 💤 0 ❌ 0 🔥 16.299s ⏱️

Please sign in to comment.