Skip to content

Commit 2b044b2

Browse files
committed
add an RC4-based transformer
1 parent 5d0890b commit 2b044b2

5 files changed

Lines changed: 142 additions & 0 deletions

File tree

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
"jasmine-core": "^2.3.4",
6060
"lodash": "^3.10.1",
6161
"request": "^2.53.0",
62+
"simple-rc4": "0.0.1-b",
6263
"socks5-http-client": "^1.0.2",
6364
"ssh2": "0.5.0",
6465
"tslint": "^3.3.0",

src/churn-pipe/churn-pipe.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import net = require('../net/net.types');
1212
import PassThrough = require('../transformers/passthrough');
1313
import promises = require('../promises/promises');
1414
import protean = require('../transformers/protean');
15+
import rc4 = require('../transformers/rc4');
1516
import sequence = require('../transformers/byteSequenceShaper');
1617
import transformer = require('../transformers/transformer');
1718

@@ -29,6 +30,7 @@ var transformers :{[name:string] : new() => transformer.Transformer} = {
2930
'fragmentationShaper': fragmentation.FragmentationShaper,
3031
'none': PassThrough,
3132
'protean': protean.Protean,
33+
'rc4': rc4.Rc4Transformer,
3234
'sequenceShaper': sequence.ByteSequenceShaper
3335
};
3436

src/transformers/rc4.spec.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/// <reference path='../../../third_party/typings/browser.d.ts' />
2+
3+
import freedomMocker = require('../freedom/mocks/mock-freedom-in-module-env');
4+
declare let freedom: freedom.FreedomInModuleEnv;
5+
freedom = freedomMocker.makeMockFreedomInModuleEnv();
6+
7+
import arraybuffers = require('../arraybuffers/arraybuffers');
8+
import rc4 = require('./rc4');
9+
10+
describe('rc4 transformer', function() {
11+
let transformer: rc4.Rc4Transformer;
12+
13+
beforeEach(function() {
14+
transformer = new rc4.Rc4Transformer();
15+
});
16+
17+
it('simple transform/restore', function() {
18+
const p = new Uint8Array([0, 1, 2]);
19+
20+
const transformedFragments = transformer.transform(p);
21+
const result = transformer.restore(transformedFragments[0])[0];
22+
23+
expect(arraybuffers.byteEquality(p, result)).toBeTruthy();
24+
});
25+
});

src/transformers/rc4.ts

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
/// <reference path='../../../third_party/sha1/sha1.d.ts' />
2+
/// <reference path='../../../third_party/simple-rc4/simple-rc4.d.ts' />
3+
/// <reference path='../../../third_party/typings/browser.d.ts' />
4+
5+
import logging = require('../logging/logging');
6+
import rc4 = require('simple-rc4');
7+
import sha1 = require('crypto/sha1');
8+
import transformer = require('./transformer');
9+
10+
const log = new logging.Log('rc4 transformer');
11+
12+
// Accepted in serialised form by configure().
13+
export interface Config {
14+
key: string
15+
}
16+
17+
const KEY_LENGTH_BYTES = 16;
18+
const R_LENGTH_BYTES = 8;
19+
const TRUNCATED_IV_LENGTH_BYTES = 8;
20+
21+
// Creates a sample (non-random) config, suitable for testing.
22+
export function sampleConfig(): Config {
23+
return {
24+
key: new Buffer(KEY_LENGTH_BYTES).fill(0).toString('hex')
25+
};
26+
}
27+
28+
// Fast, uniformly random transformer with minimal length expansion, via RC4.
29+
// From a proposal by kpdyer.
30+
//
31+
// Terminology:
32+
// - K: 128-bit session key, negotiated through the signalling channel
33+
// - RANDOM(N): a cryptographically secure N-byte string
34+
// - X[n,...,m]: the (m - n + 1) bytes starting at byte-index n of X
35+
// - |X|: length, in bytes, of X
36+
//
37+
// Pseudo-code:
38+
// def Encrypt(K, P):
39+
// R = RANDOM(8)
40+
// IV = SHA1(K || R)
41+
// RET R || RC4(IV[0,...,7], P)
42+
//
43+
// def Decrypt(K1, C):
44+
// R = C[0,...,7]
45+
// IV = SHA1(K1 || R)
46+
// RET RC4(IV[0,...,7], C[8,...,|C | -1])
47+
export class Rc4Transformer implements transformer.Transformer {
48+
private key_: Buffer;
49+
50+
public constructor() {
51+
this.configure(JSON.stringify(sampleConfig()));
52+
}
53+
54+
public configure = (json: string): void => {
55+
try {
56+
const config = <Config>JSON.parse(json);
57+
if (config.key === undefined) {
58+
throw new Error("must set key parameter");
59+
}
60+
const key = new Buffer(config.key, 'hex');
61+
if (key.byteLength !== KEY_LENGTH_BYTES) {
62+
throw new Error('keys must be ' + KEY_LENGTH_BYTES + ' bytes in length');
63+
}
64+
this.key_ = key;
65+
} catch (e) {
66+
throw new Error('could not parse config: ' + e.message);
67+
}
68+
}
69+
70+
// Applies RC4(IV[0,...,7] to bytes, as described in the pseudocode above.
71+
private update_ = (r: Buffer, bytes:Buffer): void => {
72+
// TODO: use the crypto module and avoid string conversion
73+
const iv = sha1.str_sha1(Buffer.concat([this.key_, r]).toString('binary'));
74+
const truncatedIv = new Buffer(iv, 'binary').slice(0, TRUNCATED_IV_LENGTH_BYTES);
75+
new rc4(truncatedIv).update(bytes);
76+
}
77+
78+
public transform = (ab: ArrayBuffer): ArrayBuffer[] => {
79+
const p = new Buffer(ab);
80+
81+
const r = new Buffer(R_LENGTH_BYTES);
82+
crypto.getRandomValues(r);
83+
84+
this.update_(r, p);
85+
86+
return [Buffer.concat([r, p]).buffer];
87+
}
88+
89+
public restore = (ab: ArrayBuffer): ArrayBuffer[] => {
90+
const c = new Buffer(ab);
91+
92+
const r = c.slice(0, R_LENGTH_BYTES);
93+
const tail = c.slice(R_LENGTH_BYTES);
94+
this.update_(r, tail);
95+
96+
// Because tail is constructed via Buffer#slice, its buffer field
97+
// still references ab, which still includes r.
98+
const slicedResult = tail.buffer.slice(8);
99+
return [slicedResult];
100+
}
101+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// TypeScript typings for:
2+
// https://www.npmjs.com/package/simple-rc4
3+
4+
declare module 'simple-rc4' {
5+
class RC4 {
6+
constructor(key:Buffer);
7+
8+
// Returns the supplied Buffer, encoded.
9+
update(msg: Buffer): Buffer;
10+
}
11+
12+
export = RC4;
13+
}

0 commit comments

Comments
 (0)