UNPKG

3.46 kBJavaScriptView Raw
1var 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
8nacl.stream = require('nacl-stream').stream
9
10function 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
17function 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 // decrypt the message length
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 // encrypt the message length
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
65function 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
78function 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
90function 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
115module.exports = {
116 create: createEphemeral,
117 parse: parseEphemeral
118}
\No newline at end of file