UNPKG

6.52 kBJavaScriptView Raw
1"use strict";
2var __importDefault = (this && this.__importDefault) || function (mod) {
3 return (mod && mod.__esModule) ? mod : { "default": mod };
4};
5Object.defineProperty(exports, "__esModule", { value: true });
6exports.hashPersonalMessage = exports.isValidSignature = exports.fromRpcSig = exports.toCompactSig = exports.toRpcSig = exports.ecrecover = exports.ecsign = void 0;
7const secp256k1_1 = require("ethereum-cryptography/secp256k1");
8const bn_js_1 = __importDefault(require("bn.js"));
9const bytes_1 = require("./bytes");
10const hash_1 = require("./hash");
11const helpers_1 = require("./helpers");
12const types_1 = require("./types");
13function ecsign(msgHash, privateKey, chainId) {
14 const { signature, recid: recovery } = (0, secp256k1_1.ecdsaSign)(msgHash, privateKey);
15 const r = Buffer.from(signature.slice(0, 32));
16 const s = Buffer.from(signature.slice(32, 64));
17 if (!chainId || typeof chainId === 'number') {
18 // return legacy type ECDSASignature (deprecated in favor of ECDSASignatureBuffer to handle large chainIds)
19 if (chainId && !Number.isSafeInteger(chainId)) {
20 throw new Error('The provided number is greater than MAX_SAFE_INTEGER (please use an alternative input type)');
21 }
22 const v = chainId ? recovery + (chainId * 2 + 35) : recovery + 27;
23 return { r, s, v };
24 }
25 const chainIdBN = (0, types_1.toType)(chainId, types_1.TypeOutput.BN);
26 const v = chainIdBN.muln(2).addn(35).addn(recovery).toArrayLike(Buffer);
27 return { r, s, v };
28}
29exports.ecsign = ecsign;
30function calculateSigRecovery(v, chainId) {
31 const vBN = (0, types_1.toType)(v, types_1.TypeOutput.BN);
32 if (!chainId) {
33 return vBN.subn(27);
34 }
35 const chainIdBN = (0, types_1.toType)(chainId, types_1.TypeOutput.BN);
36 return vBN.sub(chainIdBN.muln(2).addn(35));
37}
38function isValidSigRecovery(recovery) {
39 const rec = new bn_js_1.default(recovery);
40 return rec.eqn(0) || rec.eqn(1);
41}
42/**
43 * ECDSA public key recovery from signature.
44 * @returns Recovered public key
45 */
46const ecrecover = function (msgHash, v, r, s, chainId) {
47 const signature = Buffer.concat([(0, bytes_1.setLengthLeft)(r, 32), (0, bytes_1.setLengthLeft)(s, 32)], 64);
48 const recovery = calculateSigRecovery(v, chainId);
49 if (!isValidSigRecovery(recovery)) {
50 throw new Error('Invalid signature v value');
51 }
52 const senderPubKey = (0, secp256k1_1.ecdsaRecover)(signature, recovery.toNumber(), msgHash);
53 return Buffer.from((0, secp256k1_1.publicKeyConvert)(senderPubKey, false).slice(1));
54};
55exports.ecrecover = ecrecover;
56/**
57 * Convert signature parameters into the format of `eth_sign` RPC method.
58 * @returns Signature
59 */
60const toRpcSig = function (v, r, s, chainId) {
61 const recovery = calculateSigRecovery(v, chainId);
62 if (!isValidSigRecovery(recovery)) {
63 throw new Error('Invalid signature v value');
64 }
65 // geth (and the RPC eth_sign method) uses the 65 byte format used by Bitcoin
66 return (0, bytes_1.bufferToHex)(Buffer.concat([(0, bytes_1.setLengthLeft)(r, 32), (0, bytes_1.setLengthLeft)(s, 32), (0, bytes_1.toBuffer)(v)]));
67};
68exports.toRpcSig = toRpcSig;
69/**
70 * Convert signature parameters into the format of Compact Signature Representation (EIP-2098).
71 * @returns Signature
72 */
73const toCompactSig = function (v, r, s, chainId) {
74 const recovery = calculateSigRecovery(v, chainId);
75 if (!isValidSigRecovery(recovery)) {
76 throw new Error('Invalid signature v value');
77 }
78 const vn = (0, types_1.toType)(v, types_1.TypeOutput.Number);
79 let ss = s;
80 if ((vn > 28 && vn % 2 === 1) || vn === 1 || vn === 28) {
81 ss = Buffer.from(s);
82 ss[0] |= 0x80;
83 }
84 return (0, bytes_1.bufferToHex)(Buffer.concat([(0, bytes_1.setLengthLeft)(r, 32), (0, bytes_1.setLengthLeft)(ss, 32)]));
85};
86exports.toCompactSig = toCompactSig;
87/**
88 * Convert signature format of the `eth_sign` RPC method to signature parameters
89 * NOTE: all because of a bug in geth: https://github.com/ethereum/go-ethereum/issues/2053
90 */
91const fromRpcSig = function (sig) {
92 const buf = (0, bytes_1.toBuffer)(sig);
93 let r;
94 let s;
95 let v;
96 if (buf.length >= 65) {
97 r = buf.slice(0, 32);
98 s = buf.slice(32, 64);
99 v = (0, bytes_1.bufferToInt)(buf.slice(64));
100 }
101 else if (buf.length === 64) {
102 // Compact Signature Representation (https://eips.ethereum.org/EIPS/eip-2098)
103 r = buf.slice(0, 32);
104 s = buf.slice(32, 64);
105 v = (0, bytes_1.bufferToInt)(buf.slice(32, 33)) >> 7;
106 s[0] &= 0x7f;
107 }
108 else {
109 throw new Error('Invalid signature length');
110 }
111 // support both versions of `eth_sign` responses
112 if (v < 27) {
113 v += 27;
114 }
115 return {
116 v,
117 r,
118 s,
119 };
120};
121exports.fromRpcSig = fromRpcSig;
122/**
123 * Validate a ECDSA signature.
124 * @param homesteadOrLater Indicates whether this is being used on either the homestead hardfork or a later one
125 */
126const isValidSignature = function (v, r, s, homesteadOrLater = true, chainId) {
127 const SECP256K1_N_DIV_2 = new bn_js_1.default('7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0', 16);
128 const SECP256K1_N = new bn_js_1.default('fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141', 16);
129 if (r.length !== 32 || s.length !== 32) {
130 return false;
131 }
132 if (!isValidSigRecovery(calculateSigRecovery(v, chainId))) {
133 return false;
134 }
135 const rBN = new bn_js_1.default(r);
136 const sBN = new bn_js_1.default(s);
137 if (rBN.isZero() || rBN.gt(SECP256K1_N) || sBN.isZero() || sBN.gt(SECP256K1_N)) {
138 return false;
139 }
140 if (homesteadOrLater && sBN.cmp(SECP256K1_N_DIV_2) === 1) {
141 return false;
142 }
143 return true;
144};
145exports.isValidSignature = isValidSignature;
146/**
147 * Returns the keccak-256 hash of `message`, prefixed with the header used by the `eth_sign` RPC call.
148 * The output of this function can be fed into `ecsign` to produce the same signature as the `eth_sign`
149 * call for a given `message`, or fed to `ecrecover` along with a signature to recover the public key
150 * used to produce the signature.
151 */
152const hashPersonalMessage = function (message) {
153 (0, helpers_1.assertIsBuffer)(message);
154 const prefix = Buffer.from(`\u0019Ethereum Signed Message:\n${message.length}`, 'utf-8');
155 return (0, hash_1.keccak)(Buffer.concat([prefix, message]));
156};
157exports.hashPersonalMessage = hashPersonalMessage;
158//# sourceMappingURL=signature.js.map
\No newline at end of file