/
index.js
80 lines (64 loc) · 2.71 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
"use strict"
const EthereumTx = require("ethereumjs-tx")
const KeyVault = require("azure-keyvault")
const secp256k1 = require("secp256k1")
const BN = require("bn.js")
const assert = require("assert")
const _ = require("underscore")
// The order of the curve 'n'. See: https://en.bitcoin.it/wiki/Secp256k1
const CURVE_ORDER = new BN(Buffer.from('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141', 'hex'))
const HALF_CURVE_ORDER = CURVE_ORDER.clone().ishrn(1)
function isHigh(num) {
return num.ucmp(HALF_CURVE_ORDER) === 1
}
function makeCanonical(buffer) {
let r = new BN(buffer.slice(0, 32))
let s = new BN(buffer.slice(32, 64))
if (isHigh(s)) {
s = CURVE_ORDER.sub(s)
}
return Buffer.concat([r.toBuffer(), s.toBuffer()])
}
/**
* Signs a transaction using azure key vault
* @param {EthereumTx} tx the transaction object to sign
* @param {KeyVault.KeyVaultClient} client the key vault client object
* @param {String} vaultUri the vault URI
* @param {String} keyName the name of the EC key
* @param {String} keyVersion the version of the key
* @return {Buffer} the signed transaction object
*/
const sign = async function (tx, client, vaultUri, keyName, keyVersion) {
assert.equal(true, tx instanceof EthereumTx, "Transaction must be of type 'require(\"ethereumjs-tx\")'")
assert.equal(true, client instanceof KeyVault.KeyVaultClient, "Client must be of type 'require(\"azure-keyvault\").KeyVaultClient'")
const msgHash = tx.hash(false)
const keyBundle = await client.getKey(vaultUri, keyName, keyVersion)
const pubKey = Buffer.concat([Uint8Array.from([4]), keyBundle.key.x, keyBundle.key.y])
const signResult = await client.sign(vaultUri, keyName, keyVersion, "ECDSA256", msgHash)
const signature = makeCanonical(Buffer.from(signResult.result))
// Sanity check
console.debug("secp256k1 verify: " + secp256k1.verify(msgHash, signature, pubKey))
let v = -1
// Recover the public key by comparing the recovered key with the actual public key.
// If a match is found, that's the value of 'v'
for (let i = 0; i <= 1; i++) {
const recoveredPubKey = secp256k1.recover(msgHash, signature, i, false)
if (_.isEqual(pubKey, recoveredPubKey)) {
v = i
break
}
}
assert.equal(true, v === 0 || v === 1)
// As per the EIP-155 spec, the value of 'v' is also dependent on the chain id.
// See: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-155.md#specification
v += 27
if (tx.getChainId() > 0) {
v += tx.getChainId() * 2 + 8;
}
return {
r: signature.slice(0, 32),
s: signature.slice(32, 64),
v: Buffer.from([v])
}
}
module.exports = { sign: sign }