UNPKG

4.29 kBJavaScriptView Raw
1require = require('esm')(module);
2import { sha3, ecdsaSign, ecdsaRecover, publicKeyToAddress } from './util/sign';
3import format from './util/format';
4
5export default class Message {
6 /**
7 * Signs the hash with the privateKey.
8 *
9 * @param privateKey {string|Buffer}
10 * @param messageHash {string|Buffer}
11 * @return {string} The signature as hex string.
12 *
13 * @example
14 * > Message.sign(
15 '0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef', // privateKey
16 '0x592fa743889fc7f92ac2a37bb1f5ba1daf2a5c84741ca0e0061d243a2e6707ba',
17 )
18 "0x6e913e2b76459f19ebd269b82b51a70e912e909b2f5c002312efc27bcc280f3c29134d382aad0dbd3f0ccc9f0eb8f1dbe3f90141d81574ebb6504156b0d7b95f01"
19 */
20 static sign(privateKey, messageHash) {
21 const { r, s, v } = ecdsaSign(format.buffer(messageHash), format.buffer(privateKey));
22 const buffer = Buffer.concat([r, s, format.buffer(v)]);
23 return format.signature(buffer);
24 }
25
26 /**
27 * Recovers the signers publicKey from the signature.
28 *
29 * @param signature {string|Buffer}
30 * @param messageHash {string|Buffer}
31 * @return {string} The publicKey as hex string.
32 *
33 * @example
34 * > Message.recover(
35 '0x6e913e2b76459f19ebd269b82b51a70e912e909b2f5c002312efc27bcc280f3c29134d382aad0dbd3f0ccc9f0eb8f1dbe3f90141d81574ebb6504156b0d7b95f01',
36 '0x592fa743889fc7f92ac2a37bb1f5ba1daf2a5c84741ca0e0061d243a2e6707ba',
37 )
38 "0x4646ae5047316b4230d0086c8acec687f00b1cd9d1dc634f6cb358ac0a9a8ffffe77b4dd0a4bfb95851f3b7355c781dd60f8418fc8a65d14907aff47c903a559"
39 */
40 static recover(signature, messageHash) {
41 const signatureBuffer = format.buffer(signature);
42 const r = signatureBuffer.slice(0, 32);
43 const s = signatureBuffer.slice(32, 64);
44 const v = signatureBuffer[64];
45 const buffer = ecdsaRecover(format.buffer(messageHash), { r, s, v });
46 return format.publicKey(buffer);
47 }
48
49 /**
50 * @param message {string}
51 * @return {Message}
52 *
53 * @example
54 * > msg = new Message('Hello World');
55 Message {
56 message: 'Hello World',
57 }
58 * > msg.sign('0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef');
59 Message {
60 message: 'Hello World',
61 signature: '0x6e913e2b76459f19ebd269b82b51a70e912e909b2f5c002312efc27bcc280f3c29134d382aad0dbd3f0ccc9f0eb8f1dbe3f90141d81574ebb6504156b0d7b95f01'
62 }
63 * > msg.signature
64 "0x6e913e2b76459f19ebd269b82b51a70e912e909b2f5c002312efc27bcc280f3c29134d382aad0dbd3f0ccc9f0eb8f1dbe3f90141d81574ebb6504156b0d7b95f01"
65 * > msg.hash
66 "0x592fa743889fc7f92ac2a37bb1f5ba1daf2a5c84741ca0e0061d243a2e6707ba"
67 * > msg.from
68 "0x1cad0b19bb29d4674531d6f115237e16afce377c"
69 * > msg.r
70 "0x6e913e2b76459f19ebd269b82b51a70e912e909b2f5c002312efc27bcc280f3c"
71 * > msg.s
72 "0x29134d382aad0dbd3f0ccc9f0eb8f1dbe3f90141d81574ebb6504156b0d7b95f"
73 * > msg.v
74 1
75 */
76 constructor(message) {
77 this.message = message;
78 }
79
80 /**
81 * Getter of message hash include signature.
82 *
83 * > Note: calculate every time.
84 *
85 * @return {string}
86 */
87 get hash() {
88 return format.hex(sha3(Buffer.from(this.message)));
89 }
90
91 /**
92 * Getter of sender address.
93 *
94 * > Note: calculate every time.
95 *
96 * @return {string|undefined} If ECDSA recover success return address, else return undefined.
97 */
98 get from() {
99 try {
100 const publicKey = Message.recover(this.signature, this.hash);
101 return format.address(publicKeyToAddress(format.buffer(publicKey)));
102 } catch (e) {
103 return undefined;
104 }
105 }
106
107 /**
108 * Sign message and set 'r','s','v' and 'hash'.
109 *
110 * @param privateKey {string} - Private key hex string.
111 * @return {Message}
112 */
113 sign(privateKey) {
114 this.signature = Message.sign(privateKey, this.hash);
115 return this;
116 }
117
118 get r() {
119 try {
120 return this.signature.slice(0, 2 + 64);
121 } catch (e) {
122 return undefined;
123 }
124 }
125
126 get s() {
127 try {
128 return `0x${this.signature.slice(2 + 64, 2 + 128)}`;
129 } catch (e) {
130 return undefined;
131 }
132 }
133
134 get v() {
135 try {
136 return parseInt(this.signature.slice(2 + 128, 2 + 130), 16);
137 } catch (e) {
138 return undefined;
139 }
140 }
141}