UNPKG

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