1 |
|
2 |
|
3 |
|
4 |
|
5 | 'use strict'
|
6 |
|
7 | import { Aescbc } from './aescbc'
|
8 | import { cmp } from './cmp'
|
9 | import { Hash } from './hash'
|
10 | import { KeyPair } from './key-pair'
|
11 | import { Point } from './point'
|
12 | import { PubKey } from './pub-key'
|
13 | import { Random } from './random'
|
14 | import { Workers } from './workers'
|
15 |
|
16 | class Ecies {}
|
17 |
|
18 | Ecies.ivkEkM = function (privKey, pubKey) {
|
19 | const r = privKey.bn
|
20 | const KB = pubKey.point
|
21 | const P = KB.mul(r)
|
22 | const S = new PubKey(P)
|
23 | const Sbuf = S.toBuffer()
|
24 | const hash = Hash.sha512(Sbuf)
|
25 | return {
|
26 | iv: hash.slice(0, 16),
|
27 | kE: hash.slice(16, 32),
|
28 | kM: hash.slice(32, 64)
|
29 | }
|
30 | }
|
31 |
|
32 | Ecies.electrumEncrypt = function (messageBuf, toPubKey, fromKeyPair, noKey = false) {
|
33 | if (!Buffer.isBuffer(messageBuf)) {
|
34 | throw new Error('messageBuf must be a buffer')
|
35 | }
|
36 | let Rbuf
|
37 | if (fromKeyPair === null) {
|
38 | fromKeyPair = KeyPair.fromRandom()
|
39 | }
|
40 | if (!noKey) {
|
41 | Rbuf = fromKeyPair.pubKey.toDer(true)
|
42 | }
|
43 | const { iv, kE, kM } = Ecies.ivkEkM(fromKeyPair.privKey, toPubKey)
|
44 | const ciphertext = Aescbc.encrypt(messageBuf, kE, iv, false)
|
45 | const BIE1 = Buffer.from('BIE1')
|
46 | let encBuf
|
47 | if (Rbuf) {
|
48 | encBuf = Buffer.concat([BIE1, Rbuf, ciphertext])
|
49 | } else {
|
50 | encBuf = Buffer.concat([BIE1, ciphertext])
|
51 | }
|
52 | const hmac = Hash.sha256Hmac(encBuf, kM)
|
53 | return Buffer.concat([encBuf, hmac])
|
54 | }
|
55 |
|
56 | Ecies.electrumDecrypt = function (encBuf, toPrivKey, fromPubKey = null) {
|
57 | if (!Buffer.isBuffer(encBuf)) {
|
58 | throw new Error('encBuf must be a buffer')
|
59 | }
|
60 | const tagLength = 32
|
61 |
|
62 | const magic = encBuf.slice(0, 4)
|
63 | if (!magic.equals(Buffer.from('BIE1'))) {
|
64 | throw new Error('Invalid Magic')
|
65 | }
|
66 | let offset = 4
|
67 | if (fromPubKey === null) {
|
68 |
|
69 | const pub = encBuf.slice(4, 37)
|
70 | fromPubKey = PubKey.fromDer(pub)
|
71 | offset = 37
|
72 | }
|
73 | const { iv, kE, kM } = Ecies.ivkEkM(toPrivKey, fromPubKey)
|
74 | const ciphertext = encBuf.slice(offset, encBuf.length - tagLength)
|
75 | const hmac = encBuf.slice(encBuf.length - tagLength, encBuf.length)
|
76 |
|
77 | const hmac2 = Hash.sha256Hmac(encBuf.slice(0, encBuf.length - tagLength), kM)
|
78 |
|
79 | if (!hmac.equals(hmac2)) {
|
80 | throw new Error('Invalid checksum')
|
81 | }
|
82 | return Aescbc.decrypt(ciphertext, kE, iv)
|
83 | }
|
84 |
|
85 | Ecies.bitcoreEncrypt = function (messageBuf, toPubKey, fromKeyPair, ivBuf) {
|
86 | if (!fromKeyPair) {
|
87 | fromKeyPair = KeyPair.fromRandom()
|
88 | }
|
89 | const r = fromKeyPair.privKey.bn
|
90 | const RPubKey = fromKeyPair.pubKey
|
91 | const RBuf = RPubKey.toDer(true)
|
92 | const KB = toPubKey.point
|
93 | const P = KB.mul(r)
|
94 | const S = P.getX()
|
95 | const Sbuf = S.toBuffer({ size: 32 })
|
96 | const kEkM = Hash.sha512(Sbuf)
|
97 | const kE = kEkM.slice(0, 32)
|
98 | const kM = kEkM.slice(32, 64)
|
99 | const c = Aescbc.encrypt(messageBuf, kE, ivBuf)
|
100 | const d = Hash.sha256Hmac(c, kM)
|
101 | const encBuf = Buffer.concat([RBuf, c, d])
|
102 | return encBuf
|
103 | }
|
104 |
|
105 | Ecies.asyncBitcoreEncrypt = async function (
|
106 | messageBuf,
|
107 | toPubKey,
|
108 | fromKeyPair,
|
109 | ivBuf
|
110 | ) {
|
111 | if (!fromKeyPair) {
|
112 | fromKeyPair = await KeyPair.asyncFromRandom()
|
113 | }
|
114 | if (!ivBuf) {
|
115 | ivBuf = Random.getRandomBuffer(128 / 8)
|
116 | }
|
117 | const args = [messageBuf, toPubKey, fromKeyPair, ivBuf]
|
118 | const workersResult = await Workers.asyncClassMethod(Ecies, 'bitcoreEncrypt', args)
|
119 | return workersResult.resbuf
|
120 | }
|
121 |
|
122 | Ecies.bitcoreDecrypt = function (encBuf, toPrivKey) {
|
123 | const kB = toPrivKey.bn
|
124 | const fromPubKey = PubKey.fromDer(encBuf.slice(0, 33))
|
125 | const R = fromPubKey.point
|
126 | const P = R.mul(kB)
|
127 | if (P.eq(new Point())) {
|
128 | throw new Error('P equals 0')
|
129 | }
|
130 | const S = P.getX()
|
131 | const Sbuf = S.toBuffer({ size: 32 })
|
132 | const kEkM = Hash.sha512(Sbuf)
|
133 | const kE = kEkM.slice(0, 32)
|
134 | const kM = kEkM.slice(32, 64)
|
135 | const c = encBuf.slice(33, encBuf.length - 32)
|
136 | const d = encBuf.slice(encBuf.length - 32, encBuf.length)
|
137 | const d2 = Hash.sha256Hmac(c, kM)
|
138 | if (!cmp(d, d2)) {
|
139 | throw new Error('Invalid checksum')
|
140 | }
|
141 | const messageBuf = Aescbc.decrypt(c, kE)
|
142 | return messageBuf
|
143 | }
|
144 |
|
145 | Ecies.asyncBitcoreDecrypt = async function (encBuf, toPrivKey) {
|
146 | const args = [encBuf, toPrivKey]
|
147 | const workersResult = await Workers.asyncClassMethod(Ecies, 'bitcoreDecrypt', args)
|
148 | return workersResult.resbuf
|
149 | }
|
150 |
|
151 | export { Ecies }
|