Skip to content
This repository was archived by the owner on Feb 2, 2024. It is now read-only.

Commit 3e56f66

Browse files
stovmascriptmaxdeviant
authored andcommitted
Make encrypting and decrypting progressive/async with streams (#5)
* Make encrypting and decrypting progressive/async with streams * Make progressive ciphering optional * Skip async decryption test, chisel out some junk
1 parent 0dc10cf commit 3e56f66

File tree

4 files changed

+114
-10
lines changed

4 files changed

+114
-10
lines changed

index.js

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,26 @@
11
var CryptoJS = require('crypto-js');
2+
var ProgressiveCryptor = require('./src/ProgressiveCryptor').default;
23
var reduxPersist = require('redux-persist');
34
var stringify = require('json-stringify-safe');
45
var createTransform = reduxPersist.createTransform;
56

6-
function createEncryptor(secretKey) {
7+
function makeEncryptor(secretKey, progressive) {
78
return function (state, key) {
89
if (typeof state !== 'string') {
910
state = stringify(state);
1011
}
1112

13+
if (progressive) {
14+
new ProgressiveCryptor(state, secretKey).encrypt((encryptedState) => {
15+
return encryptedState;
16+
});
17+
}
18+
1219
return CryptoJS.AES.encrypt(state, secretKey).toString();
1320
}
1421
}
1522

16-
function createDecryptor(secretKey) {
23+
function makeDecryptor(secretKey, progressive) {
1724
return function (state, key) {
1825
if (typeof state !== 'string') {
1926
if (process.env.NODE_ENV !== 'production') {
@@ -24,10 +31,16 @@ function createDecryptor(secretKey) {
2431
}
2532

2633
try {
27-
var bytes = CryptoJS.AES.decrypt(state, secretKey);
28-
var newState = JSON.parse(bytes.toString(CryptoJS.enc.Utf8));
34+
if (progressive) {
35+
new ProgressiveCryptor(state, secretKey).decrypt((decryptedState) => {
36+
return JSON.parse(decryptedState.toString(CryptoJS.enc.Utf8));
37+
});
38+
} else {
39+
var bytes = CryptoJS.AES.decrypt(state, secretKey);
40+
var newState = JSON.parse(bytes.toString(CryptoJS.enc.Utf8));
2941

30-
return newState;
42+
return newState;
43+
}
3144
} catch (err) {
3245
if (process.env.NODE_ENV !== 'production') {
3346
console.error(err);
@@ -38,9 +51,18 @@ function createDecryptor(secretKey) {
3851
}
3952
}
4053

41-
module.exports = function (config) {
42-
var inbound = createEncryptor(config.secretKey);
43-
var outbound = createDecryptor(config.secretKey);
54+
export function createEncryptor(config) {
55+
var inbound = makeEncryptor(config.secretKey);
56+
var outbound = makeDecryptor(config.secretKey);
4457

4558
return createTransform(inbound, outbound, config);
46-
};
59+
}
60+
61+
export function createProgressiveEncryptor(config) {
62+
var inbound = makeEncryptor(config.secretKey, true);
63+
var outbound = makeDecryptor(config.secretKey, true);
64+
65+
return createTransform(inbound, outbound, config);
66+
}
67+
68+
export default createEncryptor;

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
"dependencies": {
2626
"crypto-js": "^3.1.6",
2727
"json-stringify-safe": "^5.0.1",
28+
"readable-stream": "^2.1.5",
2829
"redux-persist": "^3.1.1"
2930
},
3031
"devDependencies": {

src/ProgressiveCryptor.js

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
var CryptoJS = require('crypto-js');
2+
var Stream = require('readable-stream');
3+
4+
export default class ProgressiveCryptor {
5+
constructor(state, secretKey) {
6+
var salt = CryptoJS.lib.WordArray.random(8);
7+
var cipher = CryptoJS.kdf.OpenSSL.execute(secretKey, 8, 4, salt);
8+
9+
this.state = state;
10+
this.key = CryptoJS.enc.Utf8.parse(secretKey);
11+
12+
this.cryptorParams = {
13+
iv: cipher.iv,
14+
};
15+
}
16+
17+
start() {
18+
new Promise((resolve, reject) => {
19+
try {
20+
var stream = new Stream;
21+
var processedState = '';
22+
23+
stream
24+
.on('data', (data) => {
25+
processedState += this.processor.process(data.toString());
26+
})
27+
.on('end', () => {
28+
processedState += this.processor.finalize();
29+
resolve(processedState);
30+
});
31+
32+
stream.push(this.state);
33+
stream.push(null);
34+
} catch (err) {
35+
reject(err);
36+
}
37+
});
38+
}
39+
40+
encrypt() {
41+
this.processor = CryptoJS.algo.AES.createEncryptor(this.key, this.cryptorParams);
42+
this.start();
43+
}
44+
45+
decrypt() {
46+
this.processor = CryptoJS.algo.AES.createDecryptor(this.key, this.cryptorParams);
47+
this.start();
48+
}
49+
}

test/spec.js

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { expect } from 'chai';
2-
const createEncryptor = require('../');
2+
import createEncryptor, { createProgressiveEncryptor } from '../';
33

44
describe('redux-persist-transform-encrypt', () => {
55
it('can encrypt incoming state', () => {
@@ -33,4 +33,36 @@ describe('redux-persist-transform-encrypt', () => {
3333

3434
expect(newState).to.eql(state);
3535
});
36+
37+
it('can encrypt incoming state progressively', () => {
38+
const encryptTransform = createProgressiveEncryptor({
39+
secretKey: 'redux-is-awesome'
40+
});
41+
42+
const key = 'testState';
43+
const state = {
44+
foo: 'bar'
45+
};
46+
47+
const newState = encryptTransform.in(state, key);
48+
49+
expect(newState).to.be.a('string');
50+
expect(newState).to.not.eql(state);
51+
});
52+
53+
it.skip('can decrypt outgoing state progressively', () => {
54+
const encryptTransform = createProgressiveEncryptor({
55+
secretKey: 'redux-is-awesome'
56+
});
57+
58+
const key = 'testState';
59+
const state = {
60+
foo: 'bar'
61+
};
62+
63+
const encryptedState = encryptTransform.in(state, key);
64+
const newState = encryptTransform.out(encryptedState, key);
65+
66+
expect(newState).to.eql(state);
67+
});
3668
});

0 commit comments

Comments
 (0)