UNPKG

4.19 kBJavaScriptView Raw
1/**
2 * Ecies
3 * =====
4 */
5'use strict'
6
7import { Aescbc } from './aescbc'
8import { cmp } from './cmp'
9import { Hash } from './hash'
10import { KeyPair } from './key-pair'
11import { Point } from './point'
12import { PubKey } from './pub-key'
13import { Random } from './random'
14import { Workers } from './workers'
15
16class Ecies {}
17
18Ecies.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
32Ecies.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
56Ecies.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 // BIE1 use compressed public key, length is always 33.
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
85Ecies.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
105Ecies.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
122Ecies.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
145Ecies.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
151export { Ecies }