1 | var nacl = require('tweetnacl')
|
2 | , crypto = require('crypto')
|
3 | , assert = require('assert')
|
4 | , constants = require('./constants')
|
5 | , a = require('../utils/a')
|
6 | , through = require('through')
|
7 |
|
8 | nacl.stream = require('nacl-stream').stream
|
9 |
|
10 | function createEphemeral (recipient, nonce, totalSize) {
|
11 | var boxKey = nacl.box.keyPair()
|
12 | var ephPk = Buffer(boxKey.publicKey)
|
13 | var ephSk = Buffer(boxKey.secretKey)
|
14 | return makeEphemeral(ephPk, nonce, totalSize, recipient.encryptPk, ephSk)
|
15 | }
|
16 |
|
17 | function makeEphemeral (ephPk, nonce, size, encryptPk, decryptSk) {
|
18 | var beforeResult = nacl.box.before(a(encryptPk), a(decryptSk))
|
19 | assert(beforeResult)
|
20 | var k = Buffer(beforeResult), encryptedLen, totalSize
|
21 | if (Buffer.isBuffer(size)) {
|
22 |
|
23 | encryptedLen = size
|
24 | var decryptResult = nacl.box.open.after(a(size), a(nonce), a(k))
|
25 | assert(decryptResult)
|
26 | var totalSize = Buffer(decryptResult).readDoubleBE(0)
|
27 | }
|
28 | else if (typeof size === 'number') {
|
29 |
|
30 | totalSize = size
|
31 | var len = Buffer(8)
|
32 | len.writeDoubleBE(size, 0)
|
33 | var encryptResult = nacl.box.after(a(len), a(nonce), a(k))
|
34 | assert(encryptResult)
|
35 | encryptedLen = Buffer(encryptResult)
|
36 | }
|
37 | else throw new Error('invalid size')
|
38 | return {
|
39 | ephPk: ephPk,
|
40 | nonce: nonce,
|
41 | encryptedLen: encryptedLen,
|
42 | totalSize: totalSize,
|
43 | encryptPk: encryptPk,
|
44 | createEncryptor: function (isLast) {
|
45 | return createEncryptor(this.nonce, k, isLast)
|
46 | },
|
47 | createDecryptor: function (encryptedSize) {
|
48 | return createDecryptor(this.nonce, k, encryptedSize - constants.EPH_LENGTH)
|
49 | },
|
50 | toBuffer: function () {
|
51 | var buf = Buffer.concat([
|
52 | this.ephPk,
|
53 | this.nonce,
|
54 | this.encryptedLen
|
55 | ])
|
56 | assert.equal(buf.length, constants.EPH_LENGTH)
|
57 | return buf
|
58 | },
|
59 | createHmac: function (alg) {
|
60 | return crypto.createHmac(alg, k)
|
61 | }
|
62 | }
|
63 | }
|
64 |
|
65 | function parseEphemeral (buf, wallet) {
|
66 | try {
|
67 | assert.equal(buf.length, constants.EPH_LENGTH)
|
68 | }
|
69 | catch (e) {
|
70 | throw new Error('invalid ephemeral')
|
71 | }
|
72 | var ephPk = buf.slice(0, 32)
|
73 | var nonce = buf.slice(32, 56)
|
74 | var encryptedLen = buf.slice(56)
|
75 | return makeEphemeral(ephPk, nonce, encryptedLen, ephPk, wallet.decryptSk)
|
76 | }
|
77 |
|
78 | function createEncryptor (nonce, k, isLast) {
|
79 | var encryptor = nacl.stream.createEncryptor(a(k), a(nonce.slice(0, 16)), constants.MAX_CHUNK)
|
80 | return through(function write (data) {
|
81 | var encryptedChunk = encryptor.encryptChunk(a(data), isLast())
|
82 | assert(encryptedChunk)
|
83 | this.queue(Buffer(encryptedChunk))
|
84 | if (isLast()) {
|
85 | encryptor.clean()
|
86 | }
|
87 | })
|
88 | }
|
89 |
|
90 | function createDecryptor (nonce, k, totalSize) {
|
91 | var size = 0
|
92 | var decryptor = nacl.stream.createDecryptor(a(k), a(nonce.slice(0, 16)), constants.MAX_CHUNK)
|
93 | var buf = Buffer('')
|
94 | return through(function write (data) {
|
95 | size += data.length
|
96 | buf = Buffer.concat([buf, data])
|
97 | var isLast = size === totalSize
|
98 | while (buf.length) {
|
99 | var len = nacl.stream.readChunkLength(buf)
|
100 | if (buf.length < len + 20) {
|
101 | return
|
102 | }
|
103 | var chunk = buf.slice(0, len + 20)
|
104 | buf = buf.slice(len + 20)
|
105 | var decryptedChunk = decryptor.decryptChunk(a(chunk), isLast && !buf.length)
|
106 | assert(decryptedChunk)
|
107 | this.queue(Buffer(decryptedChunk))
|
108 | }
|
109 | if (isLast) {
|
110 | decryptor.clean()
|
111 | }
|
112 | })
|
113 | }
|
114 |
|
115 | module.exports = {
|
116 | create: createEphemeral,
|
117 | parse: parseEphemeral
|
118 | } |
\ | No newline at end of file |