ethers-gcp-kms-signer
Advanced tools
Comparing version 1.0.0 to 1.1.0
@@ -0,1 +1,8 @@ | ||
# [1.1.0](https://github.com/openlawteam/ethers-gcp-kms-signer/compare/v1.0.0...v1.1.0) (2021-10-28) | ||
### feat | ||
* sign typed data with gcp kms ([#2](https://github.com/openlawteam/ethers-gcp-kms-signer/issues/2)) ([a77b11f](https://github.com/openlawteam/ethers-gcp-kms-signer/commit/a77b11f85621d12b087977c27302dadf27ed9b39)) | ||
# 1.0.0 (2021-10-25) | ||
@@ -2,0 +9,0 @@ |
@@ -6,10 +6,16 @@ "use strict"; | ||
}); | ||
exports.GcpKmsSigner = void 0; | ||
exports.TypedDataVersion = exports.GcpKmsSigner = void 0; | ||
var _dotenv = _interopRequireDefault(require("dotenv")); | ||
var _ethSigUtil = require("@metamask/eth-sig-util"); | ||
var _ethers = require("ethers"); | ||
var _ethereumjsUtil = require("ethereumjs-util"); | ||
var _gcpKmsUtils = require("./util/gcp-kms-utils"); | ||
var _signatureUtils = require("./util/signature-utils"); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
@@ -25,2 +31,5 @@ | ||
const TypedDataVersion = _ethSigUtil.SignTypedDataVersion; | ||
exports.TypedDataVersion = TypedDataVersion; | ||
class GcpKmsSigner extends _ethers.ethers.Signer { | ||
@@ -78,7 +87,57 @@ constructor(kmsCredentials, provider) { | ||
} | ||
/** | ||
* Original implementation takes into account the private key, but here we use the private | ||
* key from the GCP KMS, so we don't need to provide the PK as signature option. | ||
* Source code: https://github.com/MetaMask/eth-sig-util/blob/main/src/sign-typed-data.ts#L510 | ||
* . | ||
* Sign typed data according to EIP-712. The signing differs based upon the `version`. | ||
* | ||
* V1 is based upon [an early version of EIP-712](https://github.com/ethereum/EIPs/pull/712/commits/21abe254fe0452d8583d5b132b1d7be87c0439ca) | ||
* that lacked some later security improvements, and should generally be neglected in favor of | ||
* later versions. | ||
* | ||
* V3 is based on [EIP-712](https://eips.ethereum.org/EIPS/eip-712), except that arrays and | ||
* recursive data structures are not supported. | ||
* | ||
* V4 is based on [EIP-712](https://eips.ethereum.org/EIPS/eip-712), and includes full support of | ||
* arrays and recursive data structures. | ||
* | ||
* @param options - The signing options. | ||
* @param options.data - The typed data to sign. | ||
* @param options.version - The signing version to use. | ||
* @returns The '0x'-prefixed hex encoded signature. | ||
*/ | ||
signTransaction(transaction) { | ||
signTypedData({ | ||
data, | ||
version | ||
}) { | ||
var _this4 = this; | ||
return _asyncToGenerator(function* () { | ||
(0, _signatureUtils.validateVersion)(version); | ||
if (data === null || data === undefined) { | ||
throw new Error("Missing data parameter"); | ||
} | ||
let messageSignature; | ||
if (version === _ethSigUtil.SignTypedDataVersion.V1) { | ||
messageSignature = _this4._signDigest((0, _ethSigUtil.typedSignatureHash)(data)); | ||
} else { | ||
const eip712Hash = _ethSigUtil.TypedDataUtils.eip712Hash(data, version); | ||
messageSignature = _this4._signDigest((0, _ethereumjsUtil.bufferToHex)(eip712Hash)); | ||
} | ||
return messageSignature; | ||
})(); | ||
} | ||
signTransaction(transaction) { | ||
var _this5 = this; | ||
return _asyncToGenerator(function* () { | ||
const unsignedTx = yield _ethers.ethers.utils.resolveProperties(transaction); | ||
@@ -88,3 +147,3 @@ | ||
const transactionSignature = yield _this4._signDigest(_ethers.ethers.utils.keccak256(serializedTx)); | ||
const transactionSignature = yield _this5._signDigest(_ethers.ethers.utils.keccak256(serializedTx)); | ||
return _ethers.ethers.utils.serializeTransaction(unsignedTx, transactionSignature); | ||
@@ -91,0 +150,0 @@ })(); |
{ | ||
"name": "ethers-gcp-kms-signer", | ||
"version": "1.0.0", | ||
"version": "1.1.0", | ||
"description": "An Ethers.js compatible signer that connects to GCP KMS", | ||
"main": "dist/signer.ts", | ||
"main": "dist/signer.js", | ||
"types": "dist/**/*.d.ts", | ||
"scripts": { | ||
@@ -10,7 +11,7 @@ "type-check": "tsc --noEmit", | ||
"coverage": "npm test -- --coverage", | ||
"lint": "eslint . --ext js,ts,tsx", | ||
"lint": "eslint . --ext js,ts", | ||
"lint:fix": "npm run lint -- --fix", | ||
"clean": "rimraf dist", | ||
"prebuild": "npm run clean", | ||
"build": "tsc --emitDeclarationOnly && babel src -d dist --ignore src/**/*.spec.ts,src/**/*.test.ts -x .js,.ts,.tsx", | ||
"build": "tsc -p tsconfig.build.json --emitDeclarationOnly && babel src -d dist --ignore **/*.test.d.ts,**/*.test.ts -x .ts", | ||
"preversion": "npm run lint && npm test && npm run build", | ||
@@ -39,6 +40,6 @@ "semantic-release": "semantic-release", | ||
}, | ||
"types": "dist/ts/src/index.d.ts", | ||
"homepage": "https://github.com/openlawteam/ethers-gcp-kms-signer#readme", | ||
"dependencies": { | ||
"@google-cloud/kms": "^2.10.0", | ||
"@metamask/eth-sig-util": "^4.0.0", | ||
"asn1.js": "^5.4.1", | ||
@@ -48,2 +49,3 @@ "bn.js": "^5.2.0", | ||
"dotenv": "^10.0.0", | ||
"ethereumjs-util": "^7.1.3", | ||
"ethers": "^5.5.1", | ||
@@ -50,0 +52,0 @@ "key-encoder": "^2.0.3" |
import configs from "dotenv"; | ||
import { ethers } from "ethers"; | ||
import { GcpKmsSigner } from "./signer"; | ||
import { GcpKmsSigner, TypedDataVersion } from "./signer"; | ||
import { recoverTypedSignature } from "./util/signature-utils"; | ||
configs.config(); | ||
type CouponType = "coupon"; | ||
type CouponData = { | ||
type: CouponType; | ||
authorizedMember: string; | ||
amount: string; | ||
nonce: string; | ||
}; | ||
const kmsCredentials = { | ||
@@ -15,4 +25,4 @@ projectId: process.env.KMS_PROJECT_ID, | ||
describe.skip("sign with Google KMS", () => { | ||
test("should send a signed transaction using KMS signer", async () => { | ||
describe.skip("Google KMS Signer", () => { | ||
test("should send a signed transaction", async () => { | ||
const provider = ethers.providers.InfuraProvider.getWebSocketProvider("rinkeby", process.env.INFURA_KEY); | ||
@@ -32,2 +42,69 @@ | ||
}); | ||
test("should sign a message with typed data v4", async () => { | ||
const signer = new GcpKmsSigner(kmsCredentials); | ||
const signerEthAddress = await signer.getAddress(); | ||
const memberAddress = "0xa9f01aaD34F2aF948F55612d06E51ae46ee08Bd4"; | ||
const couponData: CouponData = { | ||
type: "coupon" as CouponType, | ||
authorizedMember: memberAddress, | ||
amount: "100", | ||
nonce: "1", | ||
}; | ||
const domain = { | ||
name: "Snapshot Message", | ||
version: "4", | ||
chainId: 1, | ||
verifyingContract: "0x0000000000000000000000000000000000000000", | ||
actionId: "0x2", | ||
}; | ||
const types = { | ||
Message: [ | ||
{ name: "authorizedMember", type: "address" }, | ||
{ name: "amount", type: "uint256" }, | ||
{ name: "nonce", type: "uint256" }, | ||
], | ||
EIP712Domain: [ | ||
{ name: "name", type: "string" }, | ||
{ name: "version", type: "string" }, | ||
{ name: "chainId", type: "uint256" }, | ||
{ name: "verifyingContract", type: "address" }, | ||
{ name: "actionId", type: "address" }, | ||
], | ||
}; | ||
const signature = await signer.signTypedData({ | ||
data: { | ||
types, | ||
primaryType: "Message", | ||
domain, | ||
message: couponData, | ||
}, | ||
version: TypedDataVersion.V4, | ||
}); | ||
/* eslint-disable no-console */ | ||
console.log(`Signature: ${signature}`); | ||
expect(signature).not.toBeNull(); | ||
const recoveredAddress = recoverTypedSignature({ | ||
data: { | ||
types, | ||
primaryType: "Message", | ||
domain, | ||
message: couponData, | ||
}, | ||
signature, | ||
version: TypedDataVersion.V4, | ||
}); | ||
/* eslint-disable no-console */ | ||
console.log(`Signer Address: ${signerEthAddress}`); | ||
console.log(`Recovered Address: ${recoveredAddress}`); | ||
expect(recoveredAddress).toEqual(signerEthAddress); | ||
}); | ||
}); |
import configs from "dotenv"; | ||
import { | ||
MessageTypes, | ||
SignTypedDataVersion, | ||
TypedDataV1, | ||
TypedMessage, | ||
typedSignatureHash, | ||
TypedDataUtils, | ||
} from "@metamask/eth-sig-util"; | ||
import { ethers, UnsignedTransaction } from "ethers"; | ||
import { bufferToHex } from "ethereumjs-util"; | ||
import { getPublicKey, getEthereumAddress, requestKmsSignature, determineCorrectV } from "./util/gcp-kms-utils"; | ||
import { validateVersion } from "./util/signature-utils"; | ||
configs.config(); | ||
export const TypedDataVersion = SignTypedDataVersion; | ||
export interface GcpKmsSignerCredentials { | ||
@@ -51,2 +63,50 @@ projectId: string; | ||
/** | ||
* Original implementation takes into account the private key, but here we use the private | ||
* key from the GCP KMS, so we don't need to provide the PK as signature option. | ||
* Source code: https://github.com/MetaMask/eth-sig-util/blob/main/src/sign-typed-data.ts#L510 | ||
* . | ||
* Sign typed data according to EIP-712. The signing differs based upon the `version`. | ||
* | ||
* V1 is based upon [an early version of EIP-712](https://github.com/ethereum/EIPs/pull/712/commits/21abe254fe0452d8583d5b132b1d7be87c0439ca) | ||
* that lacked some later security improvements, and should generally be neglected in favor of | ||
* later versions. | ||
* | ||
* V3 is based on [EIP-712](https://eips.ethereum.org/EIPS/eip-712), except that arrays and | ||
* recursive data structures are not supported. | ||
* | ||
* V4 is based on [EIP-712](https://eips.ethereum.org/EIPS/eip-712), and includes full support of | ||
* arrays and recursive data structures. | ||
* | ||
* @param options - The signing options. | ||
* @param options.data - The typed data to sign. | ||
* @param options.version - The signing version to use. | ||
* @returns The '0x'-prefixed hex encoded signature. | ||
*/ | ||
async signTypedData<V extends SignTypedDataVersion, T extends MessageTypes>({ | ||
data, | ||
version, | ||
}: { | ||
data: V extends "V1" ? TypedDataV1 : TypedMessage<T>; | ||
version: V; | ||
}): Promise<string> { | ||
validateVersion(version); | ||
if (data === null || data === undefined) { | ||
throw new Error("Missing data parameter"); | ||
} | ||
let messageSignature: Promise<string>; | ||
if (version === SignTypedDataVersion.V1) { | ||
messageSignature = this._signDigest(typedSignatureHash(data as TypedDataV1)); | ||
} else { | ||
const eip712Hash: Buffer = TypedDataUtils.eip712Hash( | ||
data as TypedMessage<T>, | ||
version as SignTypedDataVersion.V3 | SignTypedDataVersion.V4 | ||
); | ||
messageSignature = this._signDigest(bufferToHex(eip712Hash)); | ||
} | ||
return messageSignature; | ||
} | ||
async signTransaction(transaction: ethers.utils.Deferrable<ethers.providers.TransactionRequest>): Promise<string> { | ||
@@ -53,0 +113,0 @@ const unsignedTx = await ethers.utils.resolveProperties(transaction); |
{ | ||
"compilerOptions": { | ||
"resolveJsonModule": true, | ||
"outDir": "dist/ts", | ||
"target": "esnext", | ||
"module": "esnext", | ||
"moduleResolution": "node", | ||
"jsx": "react", | ||
"strict": false, | ||
"declaration": true, | ||
"noFallthroughCasesInSwitch": true, | ||
"noImplicitReturns": true, | ||
"noUnusedLocals": true, | ||
"noUnusedParameters": true, | ||
"stripInternal": true, | ||
"allowSyntheticDefaultImports": true | ||
} | ||
"esModuleInterop": true, | ||
"inlineSources": true, | ||
"lib": [ | ||
"ES2020" | ||
], | ||
"module": "CommonJS", | ||
"moduleResolution": "Node", | ||
"outDir": "dist", | ||
"rootDir": "src", | ||
"sourceMap": true, | ||
"target": "ES2017", | ||
"typeRoots": [ | ||
"./node_modules/@types" | ||
] | ||
}, | ||
"include": [ | ||
"**/*.ts" | ||
], | ||
} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
53329
29
904
9
+ Addedethereumjs-util@^7.1.3
+ Added@metamask/eth-sig-util@4.0.1(transitive)
+ Added@types/bn.js@4.11.6(transitive)
+ Added@types/pbkdf2@3.1.2(transitive)
+ Added@types/secp256k1@4.0.6(transitive)
+ Addedbase-x@3.0.9(transitive)
+ Addedblakejs@1.2.1(transitive)
+ Addedbrowserify-aes@1.2.0(transitive)
+ Addedbs58@4.0.1(transitive)
+ Addedbs58check@2.1.2(transitive)
+ Addedbuffer-xor@1.0.3(transitive)
+ Addedcipher-base@1.0.4(transitive)
+ Addedcreate-hash@1.2.0(transitive)
+ Addedcreate-hmac@1.1.7(transitive)
+ Addedelliptic@6.5.5(transitive)
+ Addedethereum-cryptography@0.1.3(transitive)
+ Addedethereumjs-abi@0.6.8(transitive)
+ Addedethereumjs-util@6.2.17.1.5(transitive)
+ Addedethjs-util@0.1.6(transitive)
+ Addedevp_bytestokey@1.0.3(transitive)
+ Addedhash-base@3.1.0(transitive)
+ Addedis-hex-prefixed@1.0.0(transitive)
+ Addedkeccak@3.0.4(transitive)
+ Addedmd5.js@1.3.5(transitive)
+ Addednode-addon-api@2.0.2(transitive)
+ Addednode-gyp-build@4.8.0(transitive)
+ Addedpbkdf2@3.1.2(transitive)
+ Addedrandombytes@2.1.0(transitive)
+ Addedripemd160@2.0.2(transitive)
+ Addedrlp@2.2.7(transitive)
+ Addedsecp256k1@4.0.3(transitive)
+ Addedsetimmediate@1.0.5(transitive)
+ Addedsha.js@2.4.11(transitive)
+ Addedstrip-hex-prefix@1.0.0(transitive)
+ Addedtweetnacl@1.0.3(transitive)
+ Addedtweetnacl-util@0.15.1(transitive)