Skip to content

Commit 848ae16

Browse files
authored
Vendor and modularize the portions of the SJCL necessary for SHA-256 HMAC and CSPRNG (#17)
* Vendor and modularize the portions of the SJCL necessary for SHA-256 HMAC and CSPRNG * Ensure comments and exceptions are present * Ensure the SJCL random module does not throw reference errors
1 parent 6d0aede commit 848ae16

File tree

48 files changed

+2293
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+2293
-0
lines changed

packages/crypto-sjcl-aes/.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
*.js
2+
*.js.map
3+
4+
!/index.js
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import Aes = require('../');
2+
3+
describe('aes', () => {
4+
it('should properly export the appropriate SJCL functions', () => {
5+
expect(typeof Aes).toBe('function');
6+
const aes = new Aes([0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef]);
7+
expect(typeof aes.encrypt).toBe('function');
8+
expect(typeof aes.decrypt).toBe('function');
9+
});
10+
});

packages/crypto-sjcl-aes/index.d.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import {BitArray} from '@aws/crypto-sjcl-bitArray';
2+
3+
declare class aes {
4+
/**
5+
* Schedule out an AES key for both encryption and decryption. This
6+
* is a low-level class. Use a cipher mode to do bulk encryption.
7+
*
8+
* @constructor
9+
* @param key The key as an array of 4, 6 or 8 words.
10+
*/
11+
constructor(key: BitArray);
12+
13+
/**
14+
* Encrypt an array of 4 big-endian words.
15+
* @param data The plaintext.
16+
* @return The ciphertext.
17+
*/
18+
encrypt(data: BitArray): BitArray;
19+
20+
/**
21+
* Decrypt an array of 4 big-endian words.
22+
* @param data The ciphertext.
23+
* @return The plaintext.
24+
*/
25+
decrypt(data: BitArray): BitArray;
26+
}
27+
28+
export = aes;

packages/crypto-sjcl-aes/index.js

Lines changed: 227 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,227 @@
1+
var sjcl = {
2+
cipher: {},
3+
exception: {
4+
/**
5+
* Invalid parameter.
6+
* @constructor
7+
*/
8+
invalid: function (message) {
9+
this.toString = function () {
10+
return "INVALID: " + this.message;
11+
};
12+
this.message = message;
13+
}
14+
}
15+
};
16+
17+
// BEGIN COPY OF SJCL/core/aes.js
18+
19+
/** @fileOverview Low-level AES implementation.
20+
*
21+
* This file contains a low-level implementation of AES, optimized for
22+
* size and for efficiency on several browsers. It is based on
23+
* OpenSSL's aes_core.c, a public-domain implementation by Vincent
24+
* Rijmen, Antoon Bosselaers and Paulo Barreto.
25+
*
26+
* An older version of this implementation is available in the public
27+
* domain, but this one is (c) Emily Stark, Mike Hamburg, Dan Boneh,
28+
* Stanford University 2008-2010 and BSD-licensed for liability
29+
* reasons.
30+
*
31+
* @author Emily Stark
32+
* @author Mike Hamburg
33+
* @author Dan Boneh
34+
*/
35+
36+
/**
37+
* Schedule out an AES key for both encryption and decryption. This
38+
* is a low-level class. Use a cipher mode to do bulk encryption.
39+
*
40+
* @constructor
41+
* @param {Array} key The key as an array of 4, 6 or 8 words.
42+
*/
43+
sjcl.cipher.aes = function (key) {
44+
if (!this._tables[0][0][0]) {
45+
this._precompute();
46+
}
47+
48+
var i, j, tmp,
49+
encKey, decKey,
50+
sbox = this._tables[0][4], decTable = this._tables[1],
51+
keyLen = key.length, rcon = 1;
52+
53+
if (keyLen !== 4 && keyLen !== 6 && keyLen !== 8) {
54+
throw new sjcl.exception.invalid("invalid aes key size");
55+
}
56+
57+
this._key = [encKey = key.slice(0), decKey = []];
58+
59+
// schedule encryption keys
60+
for (i = keyLen; i < 4 * keyLen + 28; i++) {
61+
tmp = encKey[i-1];
62+
63+
// apply sbox
64+
if (i%keyLen === 0 || (keyLen === 8 && i%keyLen === 4)) {
65+
tmp = sbox[tmp>>>24]<<24 ^ sbox[tmp>>16&255]<<16 ^ sbox[tmp>>8&255]<<8 ^ sbox[tmp&255];
66+
67+
// shift rows and add rcon
68+
if (i%keyLen === 0) {
69+
tmp = tmp<<8 ^ tmp>>>24 ^ rcon<<24;
70+
rcon = rcon<<1 ^ (rcon>>7)*283;
71+
}
72+
}
73+
74+
encKey[i] = encKey[i-keyLen] ^ tmp;
75+
}
76+
77+
// schedule decryption keys
78+
for (j = 0; i; j++, i--) {
79+
tmp = encKey[j&3 ? i : i - 4];
80+
if (i<=4 || j<4) {
81+
decKey[j] = tmp;
82+
} else {
83+
decKey[j] = decTable[0][sbox[tmp>>>24 ]] ^
84+
decTable[1][sbox[tmp>>16 & 255]] ^
85+
decTable[2][sbox[tmp>>8 & 255]] ^
86+
decTable[3][sbox[tmp & 255]];
87+
}
88+
}
89+
};
90+
91+
sjcl.cipher.aes.prototype = {
92+
// public
93+
/* Something like this might appear here eventually
94+
name: "AES",
95+
blockSize: 4,
96+
keySizes: [4,6,8],
97+
*/
98+
99+
/**
100+
* Encrypt an array of 4 big-endian words.
101+
* @param {Array} data The plaintext.
102+
* @return {Array} The ciphertext.
103+
*/
104+
encrypt:function (data) { return this._crypt(data,0); },
105+
106+
/**
107+
* Decrypt an array of 4 big-endian words.
108+
* @param {Array} data The ciphertext.
109+
* @return {Array} The plaintext.
110+
*/
111+
decrypt:function (data) { return this._crypt(data,1); },
112+
113+
/**
114+
* The expanded S-box and inverse S-box tables. These will be computed
115+
* on the client so that we don't have to send them down the wire.
116+
*
117+
* There are two tables, _tables[0] is for encryption and
118+
* _tables[1] is for decryption.
119+
*
120+
* The first 4 sub-tables are the expanded S-box with MixColumns. The
121+
* last (_tables[01][4]) is the S-box itself.
122+
*
123+
* @private
124+
*/
125+
_tables: [[[],[],[],[],[]],[[],[],[],[],[]]],
126+
127+
/**
128+
* Expand the S-box tables.
129+
*
130+
* @private
131+
*/
132+
_precompute: function () {
133+
var encTable = this._tables[0], decTable = this._tables[1],
134+
sbox = encTable[4], sboxInv = decTable[4],
135+
i, x, xInv, d=[], th=[], x2, x4, x8, s, tEnc, tDec;
136+
137+
// Compute double and third tables
138+
for (i = 0; i < 256; i++) {
139+
th[( d[i] = i<<1 ^ (i>>7)*283 )^i]=i;
140+
}
141+
142+
for (x = xInv = 0; !sbox[x]; x ^= x2 || 1, xInv = th[xInv] || 1) {
143+
// Compute sbox
144+
s = xInv ^ xInv<<1 ^ xInv<<2 ^ xInv<<3 ^ xInv<<4;
145+
s = s>>8 ^ s&255 ^ 99;
146+
sbox[x] = s;
147+
sboxInv[s] = x;
148+
149+
// Compute MixColumns
150+
x8 = d[x4 = d[x2 = d[x]]];
151+
tDec = x8*0x1010101 ^ x4*0x10001 ^ x2*0x101 ^ x*0x1010100;
152+
tEnc = d[s]*0x101 ^ s*0x1010100;
153+
154+
for (i = 0; i < 4; i++) {
155+
encTable[i][x] = tEnc = tEnc<<24 ^ tEnc>>>8;
156+
decTable[i][s] = tDec = tDec<<24 ^ tDec>>>8;
157+
}
158+
}
159+
160+
// Compactify. Considerable speedup on Firefox.
161+
for (i = 0; i < 5; i++) {
162+
encTable[i] = encTable[i].slice(0);
163+
decTable[i] = decTable[i].slice(0);
164+
}
165+
},
166+
167+
/**
168+
* Encryption and decryption core.
169+
* @param {Array} input Four words to be encrypted or decrypted.
170+
* @param dir The direction, 0 for encrypt and 1 for decrypt.
171+
* @return {Array} The four encrypted or decrypted words.
172+
* @private
173+
*/
174+
_crypt:function (input, dir) {
175+
if (input.length !== 4) {
176+
throw new sjcl.exception.invalid("invalid aes block size");
177+
}
178+
179+
var key = this._key[dir],
180+
// state variables a,b,c,d are loaded with pre-whitened data
181+
a = input[0] ^ key[0],
182+
b = input[dir ? 3 : 1] ^ key[1],
183+
c = input[2] ^ key[2],
184+
d = input[dir ? 1 : 3] ^ key[3],
185+
a2, b2, c2,
186+
187+
nInnerRounds = key.length/4 - 2,
188+
i,
189+
kIndex = 4,
190+
out = [0,0,0,0],
191+
table = this._tables[dir],
192+
193+
// load up the tables
194+
t0 = table[0],
195+
t1 = table[1],
196+
t2 = table[2],
197+
t3 = table[3],
198+
sbox = table[4];
199+
200+
// Inner rounds. Cribbed from OpenSSL.
201+
for (i = 0; i < nInnerRounds; i++) {
202+
a2 = t0[a>>>24] ^ t1[b>>16 & 255] ^ t2[c>>8 & 255] ^ t3[d & 255] ^ key[kIndex];
203+
b2 = t0[b>>>24] ^ t1[c>>16 & 255] ^ t2[d>>8 & 255] ^ t3[a & 255] ^ key[kIndex + 1];
204+
c2 = t0[c>>>24] ^ t1[d>>16 & 255] ^ t2[a>>8 & 255] ^ t3[b & 255] ^ key[kIndex + 2];
205+
d = t0[d>>>24] ^ t1[a>>16 & 255] ^ t2[b>>8 & 255] ^ t3[c & 255] ^ key[kIndex + 3];
206+
kIndex += 4;
207+
a=a2; b=b2; c=c2;
208+
}
209+
210+
// Last round.
211+
for (i = 0; i < 4; i++) {
212+
out[dir ? 3&-i : i] =
213+
sbox[a>>>24 ]<<24 ^
214+
sbox[b>>16 & 255]<<16 ^
215+
sbox[c>>8 & 255]<<8 ^
216+
sbox[d & 255] ^
217+
key[kIndex++];
218+
a2=a; a=b; b=c; c=d; d=a2;
219+
}
220+
221+
return out;
222+
}
223+
};
224+
225+
// END COPY OF SJCL/core/aes.js
226+
227+
module.exports = sjcl.cipher.aes;

packages/crypto-sjcl-aes/package.json

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{
2+
"name": "@aws/crypto-sjcl-aes",
3+
"private": true,
4+
"version": "0.0.1",
5+
"description": "The aes portion of the Stanford JavaScript Cryptography Library exposed as a CommonJS module",
6+
"scripts": {
7+
"pretest": "tsc",
8+
"test": "jest"
9+
},
10+
"author": "[email protected]",
11+
"license": "UNLICENSED",
12+
"main": "index.js",
13+
"dependencies": {
14+
"@aws/crypto-sjcl-bitArray": "^0.0.1"
15+
},
16+
"devDependencies": {
17+
"sjcl": "^1.0.6",
18+
"@types/jest": "^19.2.2",
19+
"jest": "^19.0.2",
20+
"typescript": "^2.3"
21+
}
22+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"compilerOptions": {
3+
"target": "es5",
4+
"module": "commonjs",
5+
"strict": true
6+
}
7+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
*.js
2+
*.js.map
3+
4+
!/index.js
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import * as bitArray from '../';
2+
3+
describe('bitArray', () => {
4+
it('should properly export the appropriate SJCL functions', () => {
5+
expect(typeof bitArray.bitSlice).toBe('function');
6+
expect(typeof bitArray.extract).toBe('function');
7+
expect(typeof bitArray.concat).toBe('function');
8+
expect(typeof bitArray.bitLength).toBe('function');
9+
expect(typeof bitArray.clamp).toBe('function');
10+
expect(typeof bitArray.partial).toBe('function');
11+
expect(typeof bitArray.getPartial).toBe('function');
12+
expect(typeof bitArray.equal).toBe('function');
13+
expect(typeof bitArray._shiftRight).toBe('function');
14+
expect(typeof bitArray._xor4).toBe('function');
15+
expect(typeof bitArray.byteswapM).toBe('function');
16+
});
17+
});

0 commit comments

Comments
 (0)