Demo app to sign Ethereum tx with Google Cloud KMS.
This repo is tutorial with example source code on how to use Google Cloud KMS to sign ethereum transactions with web3.js and Ethers.js.
- Create a Google Cloud project, let's call it
my-project
. - Install Google Cloud CLI and login to your Google Cloud account.
- Install and configure Git and Node.js.
- Clone this repo:
git clone git@github.com:Hugoo/ethereum-kms-sample.git && cd ethereum-kms-sample
- Create
.env
file:cp .env.example .env
- Install dependencies:
npm i
Let's start by creating our key in Google Cloud KMS. To do this, you can either use the gcloud
CLI or the Google Cloud UI.
We will use the gcloud
CLI. Please note that you can also create these keys programmatically with the @google-cloud/kms
library.
gcloud config set project my-project
> Updated property [core/project].
Create a key ring (reference):
gcloud kms keyrings create ethereum-keys --location=us-east1
You should see your new key ring in the Google Cloud Dashboard.
Then, use gcloud kms keys create
to create a new key:
gcloud kms keys create key-1 --location=us-east1 --keyring=ethereum-keys --purpose=asymmetric-signing --default-algorithm=ec-sign-secp256k1-sha256 --protection-level=hsm
This key will be used to sign our transactions, therefore we should set its purpose to asymmetric-signing
. You can find more information about the purposes of a key here.
According to the Ethereum Yellow Paper Appendix F. Signing Transactions, transactions are signed using recoverable ECDSA signatures. The method uses the SECP-256k1
curve. Therefore, the algorithm of our key should be ec-sign-secp256k1-sha256
.
The list of available algorithms can be found here.
Cool, now we have a key ready to be used to sign our transactions :)
Now that our key is ready, we need to setup our Node.js project so it can use it. Depending on where your application will run, you may need to use a service account. If the application is running within Google Cloud, for instance, in Google App Engine, you can skip using a key and set the permissions directly to the App Engine default service account. For this tutorial, we will focus on running the scripts locally, therefore, we will need to handle the authentication of our script to Google Cloud through a service account.
Create a new service account:
gcloud iam service-accounts create ethereum-signer \
--description="ethereum-signer" \
--display-name="ethereum-signer"
> Created service account [ethereum-signer].
Grant the cloudkms.signerVerifier
role to your service account:
gcloud projects add-iam-policy-binding my-project \
--member serviceAccount:ethereum-signer@my-project.iam.gserviceaccount.com --role roles/cloudkms.signerVerifier
SA_ID
: The ID of your service account. This can either be the service account's email address in the formSA_NAME
@PROJECT_ID
.iam.gserviceaccount.com, or the service account's unique numeric ID.
You can check if the service account was created correctly on the IAM Dashboard:
Finally, you can create a key file for this service account:
gcloud iam service-accounts keys create ./keys/signer-sa-key.json --iam-account=tx-signer@my-project.iam.gserviceaccount.com
You should have a key in ./keys/signer-sa-key.json
.
Finally, let's install the @google-cloud/kms
lib and verify if everything works
npm i @google-cloud/kms
npx ts-node src/check-keys.ts
{
pem: '-----BEGIN PUBLIC KEY-----\n' +
'MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAElBNf5/Pw2LHcKxP0DyhUaNRC8UpHOs0w\n' +
'Ny+/0PSNdBoLrhLxZmeTVSwreMUOjabpM6TMjjBJgXLISOhW3SO0EA==\n' +
'-----END PUBLIC KEY-----\n',
algorithm: 'EC_SIGN_SECP256K1_SHA256',
pemCrc32c: { value: '3112486914' },
name: 'projects/my-project/locations/us-east1/keyRings/ethereum-keys/cryptoKeys/key-1/cryptoKeyVersions/1',
protectionLevel: 'HSM'
}
From this pem string, we can compute our Ethereum address. For more information, you can check the ./src/check-keys.ts
script and read the article about Playing with ethereum secp256k1 keys.
npx ts-node src/check-keys.ts
This script should output the expected Ethereum address associated with your KMS key.
We will need a RPC access to the Ethereum network, you can get one from Alchemy (this is an affiliate link).
Then grab your API key:
And add this key in your .env
file for the ALCHEMY_API_KEY=
variable.
You can also get rETH from a faucet, to fund your account (running the check-keys.ts
script should give you your Ethereum address).
With Ethers.js, we can use the Signer
API to connect to KMS.
Luckily, there is this super package which lets you use it very easily: https://github.com/openlawteam/ethers-gcp-kms-signer.
You can check the code in the ./src/ethersjs.ts
file.
npx ts-node src/check-keys.ts
{
type: 2,
chainId: 3,
nonce: 0,
maxPriorityFeePerGas: BigNumber { _hex: '0x59682f00', _isBigNumber: true },
maxFeePerGas: BigNumber { _hex: '0x11210f9eac', _isBigNumber: true },
gasPrice: null,
gasLimit: BigNumber { _hex: '0x5208', _isBigNumber: true },
to: '0xE94E130546485b928C9C9b9A5e69EB787172952e',
value: BigNumber { _hex: '0x038d7ea4c68000', _isBigNumber: true },
data: '0x',
accessList: [],
hash: '0xda045876596e832703b131cfc5991a18617a0685dcdbd571da52273c6e481fbc',
v: 0,
r: '0x49c68f3df02aeb31d00b3326446973f0453c76cb7afc6ff8c78f7c00f928e8da',
s: '0x522df0a097fc6452e75995c606b8de5793a7b9d1623d35753b113317fc6ca1fa',
from: '0x19a7930683619396d06bdA6Ce43dc7A8659E7C20',
confirmations: 0,
wait: [Function (anonymous)]
}
To use KMS with Web3js, we can either:
- Create a provider
- Create an account
The account should have this interface:
export interface Account {
address: string;
privateKey: string;
signTransaction: (
transactionConfig: TransactionConfig,
callback?: (signTransaction: SignedTransaction) => void,
) => Promise<SignedTransaction>;
sign: (data: string) => Sign;
encrypt: (password: string) => EncryptedKeystoreV3Json;
}