UNPKG

3.75 kBJavaScriptView Raw
1/**
2 * Cbc
3 * ===
4 *
5 * Cipher Block Chaining (Cbc). This is a low-level tool for chaining multiple
6 * encrypted blocks together, usually with Aes. This is a low-level tool that
7 * does not include authentication. You should only be using this if you have
8 * authentication at another step. It is best combined with Hmac.
9 *
10 * http://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher-block_chaining_.28Cbc.29
11 */
12'use strict'
13
14import { cmp } from './cmp'
15
16class Cbc {}
17
18Cbc.buf2BlocksBuf = function (buf, blockSize) {
19 const bytesize = blockSize / 8
20 const blockBufs = []
21
22 for (let i = 0; i <= buf.length / bytesize; i++) {
23 let blockBuf = buf.slice(i * bytesize, i * bytesize + bytesize)
24
25 if (blockBuf.length < blockSize) {
26 blockBuf = Cbc.pkcs7Pad(blockBuf, blockSize)
27 }
28
29 blockBufs.push(blockBuf)
30 }
31
32 return blockBufs
33}
34
35Cbc.blockBufs2Buf = function (blockBufs) {
36 let last = blockBufs[blockBufs.length - 1]
37 last = Cbc.pkcs7Unpad(last)
38 blockBufs[blockBufs.length - 1] = last
39
40 const buf = Buffer.concat(blockBufs)
41
42 return buf
43}
44
45Cbc.encrypt = function (messageBuf, ivBuf, blockCipher, cipherKeyBuf) {
46 const blockSize = ivBuf.length * 8
47 const blockBufs = Cbc.buf2BlocksBuf(messageBuf, blockSize)
48 const encBufs = Cbc.encryptBlocks(blockBufs, ivBuf, blockCipher, cipherKeyBuf)
49 const encBuf = Buffer.concat(encBufs)
50 return encBuf
51}
52
53Cbc.decrypt = function (encBuf, ivBuf, blockCipher, cipherKeyBuf) {
54 const bytesize = ivBuf.length
55 const encBufs = []
56 for (let i = 0; i < encBuf.length / bytesize; i++) {
57 encBufs.push(encBuf.slice(i * bytesize, i * bytesize + bytesize))
58 }
59 const blockBufs = Cbc.decryptBlocks(encBufs, ivBuf, blockCipher, cipherKeyBuf)
60 const buf = Cbc.blockBufs2Buf(blockBufs)
61 return buf
62}
63
64Cbc.encryptBlock = function (blockBuf, ivBuf, blockCipher, cipherKeyBuf) {
65 const xorbuf = Cbc.xorBufs(blockBuf, ivBuf)
66 const encBuf = blockCipher.encrypt(xorbuf, cipherKeyBuf)
67 return encBuf
68}
69
70Cbc.decryptBlock = function (encBuf, ivBuf, blockCipher, cipherKeyBuf) {
71 const xorbuf = blockCipher.decrypt(encBuf, cipherKeyBuf)
72 const blockBuf = Cbc.xorBufs(xorbuf, ivBuf)
73 return blockBuf
74}
75
76Cbc.encryptBlocks = function (blockBufs, ivBuf, blockCipher, cipherKeyBuf) {
77 const encBufs = []
78
79 for (let i = 0; i < blockBufs.length; i++) {
80 const blockBuf = blockBufs[i]
81 const encBuf = Cbc.encryptBlock(blockBuf, ivBuf, blockCipher, cipherKeyBuf)
82
83 encBufs.push(encBuf)
84
85 ivBuf = encBuf
86 }
87
88 return encBufs
89}
90
91Cbc.decryptBlocks = function (encBufs, ivBuf, blockCipher, cipherKeyBuf) {
92 const blockBufs = []
93
94 for (let i = 0; i < encBufs.length; i++) {
95 const encBuf = encBufs[i]
96 const blockBuf = Cbc.decryptBlock(encBuf, ivBuf, blockCipher, cipherKeyBuf)
97
98 blockBufs.push(blockBuf)
99
100 ivBuf = encBuf
101 }
102
103 return blockBufs
104}
105
106Cbc.pkcs7Pad = function (buf, blockSize) {
107 const bytesize = blockSize / 8
108 const padbytesize = bytesize - buf.length
109 const pad = Buffer.alloc(padbytesize)
110 pad.fill(padbytesize)
111 const paddedbuf = Buffer.concat([buf, pad])
112 return paddedbuf
113}
114
115Cbc.pkcs7Unpad = function (paddedbuf) {
116 const padlength = paddedbuf[paddedbuf.length - 1]
117 const padbuf = paddedbuf.slice(paddedbuf.length - padlength, paddedbuf.length)
118 const padbuf2 = Buffer.alloc(padlength)
119 padbuf2.fill(padlength)
120 if (!cmp(padbuf, padbuf2)) {
121 throw new Error('invalid padding')
122 }
123 return paddedbuf.slice(0, paddedbuf.length - padlength)
124}
125
126Cbc.xorBufs = function (buf1, buf2) {
127 if (buf1.length !== buf2.length) {
128 throw new Error('bufs must have the same length')
129 }
130
131 const buf = Buffer.alloc(buf1.length)
132
133 for (let i = 0; i < buf1.length; i++) {
134 buf[i] = buf1[i] ^ buf2[i]
135 }
136
137 return buf
138}
139
140export { Cbc }