Skip to content

Add shared crypto library #1

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 27 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
22b8a23
Add shared crypto library
jeskew Apr 10, 2017
7c7f709
Ensure test artifacts are present and browser tests are executed
jeskew Apr 17, 2017
9af81e3
Use Promise instead of PromiseLike
jeskew Apr 19, 2017
101d5b0
Remove webkit vendor prefix workaround
jeskew Apr 19, 2017
8243a3f
Get rid of default exports
jeskew May 1, 2017
10ce849
Move crypto interfaces to @aws/types
jeskew May 2, 2017
7760ac8
Add new crypto interfaces to types package
jeskew May 12, 2017
552a5bc
Add node.js random source package
jeskew May 12, 2017
c869470
Add SJCL bitArray module
jeskew May 12, 2017
95ab4c8
Add SJCL codecString module
jeskew May 12, 2017
7f73a40
Add SJCL codecHex module
jeskew May 12, 2017
a04f34e
Add SJCL SHA-256 module
jeskew May 12, 2017
f3c197b
Add SJCL AES module
jeskew May 13, 2017
e1c4cde
Add SJCL random module
jeskew May 13, 2017
4b63ce5
Add SJCL codecArrayBuffer module
jeskew May 13, 2017
715942c
Refrain from modifying any SJCL source code; exclude crypto-browserif…
jeskew May 13, 2017
2d223a3
Add IE11 detection module
jeskew May 13, 2017
5c18392
Add WebCrypto detection module
jeskew May 13, 2017
40ee19e
Add browser random source module
jeskew May 14, 2017
dc8c9de
Add an isNode module
jeskew May 14, 2017
7bed652
Add universal random source module
jeskew May 14, 2017
0cd5ce3
Add a SHA-256 node module
jeskew May 14, 2017
025ff34
Add SJCL hmac module
jeskew May 14, 2017
fdf68b1
Add a SHA-256 browser module
jeskew May 15, 2017
f30495f
Add a SHA-256 universal module
jeskew May 16, 2017
c3ae6b7
Remove omnibus crypto module and the associated types
jeskew May 16, 2017
a3b334b
Improve diff documentation
jeskew May 25, 2017
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/crypto-ie11-detection/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
node_modules
*.js
*.js.map
*.d.ts
52 changes: 52 additions & 0 deletions packages/crypto-ie11-detection/__tests__/MsWindow.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import {isMsWindow, MsWindow} from "../lib/MsWindow";

const fakeMsWindow: MsWindow = {
MSInputMethodContext: {},
msCrypto: {
getRandomValues: () => {},
subtle: {
decrypt(alg: any, key: any) { return {} as any; },
digest(alg: any) { return {} as any; },
encrypt(alg: any, key: any) { return {} as any; },
exportKey(format: any, key: any) { return {} as any; },
generateKey(alg: any) { return {} as any; },
importKey(format: any, keyData: any, alg: any) { return {} as any; },
sign(alg: any, key: any) { return {} as any; },
verify(alg: any, key: any, signature: any) { return {} as any; },
},
},
} as any;

jest.mock('../lib/isNativeCode', () => {
return { isNativeCode: jest.fn() };
});
import {isNativeCode as isNativeCodeMock} from '../lib/isNativeCode';

beforeEach(() => {
(isNativeCodeMock as any).mockClear();
});

describe('isMsWindow', () => {
it(
'should return false if an object does not fulfill the MsWindow interface',
() => {
expect(isMsWindow({} as any)).toBe(false);
}
);

it(
'should return false if an object fulfills the MsWindow interface but SubtleCrypto methods are user-defined',
() => {
(isNativeCodeMock as any).mockReturnValue(false);
expect(isMsWindow(fakeMsWindow)).toBe(false);
}
);

it(
'should return true if an object fulfills the MsWindow interface and the SubtleCrypto methods are native code',
() => {
(isNativeCodeMock as any).mockReturnValue(true);
expect(isMsWindow(fakeMsWindow)).toBe(true);
}
);
});
23 changes: 23 additions & 0 deletions packages/crypto-ie11-detection/__tests__/isNativeCode.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import {isNativeCode} from "../lib/isNativeCode";

describe('isNativeCode', () => {
it(
'should return true if the function string indicates it is native code',
() => {
const originalToString = Function.prototype.toString;
try {
Function.prototype.toString = () => 'function name() { [native code] }';
expect(isNativeCode(() => {})).toBe(true);
} finally {
Function.prototype.toString = originalToString;
}
}
);

it(
'should return false if the function string does not indicate it contains native code',
() => {
expect(isNativeCode(() => {})).toBe(false);
}
);
});
5 changes: 5 additions & 0 deletions packages/crypto-ie11-detection/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export * from './lib/CryptoOperation';
export * from './lib/Key';
export * from './lib/KeyOperation';
export * from './lib/MsSubtleCrypto';
export * from './lib/MsWindow';
21 changes: 21 additions & 0 deletions packages/crypto-ie11-detection/lib/CryptoOperation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import {Key} from "./Key";

/**
* Represents a cryptographic operation that has been instantiated but not
* necessarily fed all data or finalized.
*
* @see https://msdn.microsoft.com/en-us/library/dn280996(v=vs.85).aspx
*/
export interface CryptoOperation {
readonly algorithm: string;
readonly key: Key;
onabort: (event: Event) => void;
oncomplete: (event: Event) => void;
onerror: (event: Event) => void;
onprogress: (event: Event) => void;
readonly result: ArrayBuffer|undefined;

abort(): void;
finish(): void;
process(buffer: ArrayBufferView): void;
}
12 changes: 12 additions & 0 deletions packages/crypto-ie11-detection/lib/Key.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/**
* The result of a successful KeyOperation.
*
* @see {KeyOperation}
* @see https://msdn.microsoft.com/en-us/library/dn302313(v=vs.85).aspx
*/
export interface Key {
readonly algorithm: string;
readonly extractable: boolean;
readonly keyUsage: Array<string>;
readonly type: string;
}
13 changes: 13 additions & 0 deletions packages/crypto-ie11-detection/lib/KeyOperation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import {Key} from "./Key";

/**
* Represents the return of a key-related operation that may or may not have
* been completed.
*
* @see https://msdn.microsoft.com/en-us/library/dn302314(v=vs.85).aspx
*/
export interface KeyOperation {
oncomplete: (event: Event) => void;
onerror: (event: Event) => void;
readonly result: Key|undefined;
}
79 changes: 79 additions & 0 deletions packages/crypto-ie11-detection/lib/MsSubtleCrypto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import {CryptoOperation} from './CryptoOperation';
import {Key} from './Key';
import {KeyOperation} from './KeyOperation';

export type KeyUsage = 'encrypt'|'decrypt'|'sign'|'verify'|'derive'|'wrap'|'unwrap';

export type EncryptionOrVerificationAlgorithm = 'RSAES-PKCS1-v1_5';
export type Ie11EncryptionAlgorithm = 'AES-CBC'|'AES-GCM'|'RSA-OAEP'|EncryptionOrVerificationAlgorithm;
export type Ie11DigestAlgorithm = 'SHA-1'|'SHA-256'|'SHA-384';

export interface HashAlgorithm {
name: Ie11DigestAlgorithm;
}

export interface HmacAlgorithm {
name: 'HMAC';
hash: HashAlgorithm;
}

export type SigningAlgorithm = HmacAlgorithm;

/**
* Represent ths SubtleCrypto interface as implemented in Internet Explorer 11.
* This implementation was based on an earlier version of the WebCrypto API and
* differs from the `window.crypto.subtle` object exposed in Chrome, Safari,
* Firefox, and MS Edge.
*
* @see https://msdn.microsoft.com/en-us/library/dn302325(v=vs.85).aspx
*/
export interface MsSubtleCrypto {
decrypt(
algorithm: Ie11EncryptionAlgorithm,
key: Key,
buffer?: ArrayBufferView
): CryptoOperation;

digest(
algorithm: Ie11DigestAlgorithm,
buffer?: ArrayBufferView
): CryptoOperation;

encrypt(
algorithm: Ie11EncryptionAlgorithm,
key: Key,
buffer?: ArrayBufferView
): CryptoOperation;

exportKey(
format: string,
key: Key
): KeyOperation;

generateKey(
algorithm: SigningAlgorithm|Ie11EncryptionAlgorithm,
extractable?: boolean,
keyUsages?: Array<KeyUsage>
): KeyOperation;

importKey(
format: string,
keyData: ArrayBufferView,
algorithm: any,
extractable?: boolean,
keyUsages?: Array<KeyUsage>
): KeyOperation;

sign(
algorithm: SigningAlgorithm,
key: Key,
buffer?: ArrayBufferView,
): CryptoOperation;

verify(
algorithm: SigningAlgorithm|EncryptionOrVerificationAlgorithm,
key: Key,
signature: ArrayBufferView,
buffer?: ArrayBufferView
): CryptoOperation;
}
56 changes: 56 additions & 0 deletions packages/crypto-ie11-detection/lib/MsWindow.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import {isNativeCode} from './isNativeCode';
import {MsSubtleCrypto} from "./MsSubtleCrypto";

type SubtleCryptoMethod = 'decrypt'|'digest'|'encrypt'|'exportKey'|'generateKey'|'importKey'|'sign'|'verify';

const msSubtleCryptoMethods: Array<SubtleCryptoMethod> = [
'decrypt',
'digest',
'encrypt',
'exportKey',
'generateKey',
'importKey',
'sign',
'verify',
];

/**
* The value accessible as `window.msCrypto` in Internet Explorer 11.
*/
export interface MsCrypto {
getRandomValues: (toFill: Uint8Array) => void;
subtle: MsSubtleCrypto;
}

/**
* The `window` object in Internet Explorer 11. This interface does not
* exhaustively document the prefixed features of `window` in IE11.
*/
export interface MsWindow extends Window {
MSInputMethodContext: any;
msCrypto: MsCrypto;
}

function quacksLikeAnMsWindow(window: Window): window is MsWindow {
return 'MSInputMethodContext' in window &&
'msCrypto' in window;
}

export function isMsWindow(window: Window): window is MsWindow {
if (quacksLikeAnMsWindow(window) && window.msCrypto.subtle !== undefined) {
const {getRandomValues, subtle} = window.msCrypto;
const methodsToTest = msSubtleCryptoMethods
.map<Function>(methodName => subtle[methodName])
.concat(getRandomValues);

for (let method of methodsToTest) {
if (!isNativeCode(method)) {
return false;
}
}

return true;
}

return false;
}
17 changes: 17 additions & 0 deletions packages/crypto-ie11-detection/lib/isNativeCode.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
const nativeCodeRegex = /\[native code]/;

/**
* A best-effort check to see if a function is user-defined or provided by the
* browser. This is a heuristic rather than something 100% accurate.
*
* @internal
*/
export function isNativeCode(func: Function): boolean {
try {
if (nativeCodeRegex.test(Function.prototype.toString.call(func))) {
return true;
}
} catch (e) {}

return false;
}
18 changes: 18 additions & 0 deletions packages/crypto-ie11-detection/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"name": "@aws/crypto-ie11-detection",
"private": true,
"version": "0.0.1",
"description": "Provides functions and types for detecting if the host environment is IE11",
"scripts": {
"pretest": "tsc",
"test": "jest"
},
"author": "[email protected]",
"license": "UNLICENSED",
"main": "index.js",
"devDependencies": {
"@types/jest": "^19.2.2",
"jest": "^19.0.2",
"typescript": "^2.3"
}
}
10 changes: 10 additions & 0 deletions packages/crypto-ie11-detection/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"compilerOptions": {
"module": "commonjs",
"target": "es5",
"strict": true,
"sourceMap": true,
"declaration": true,
"stripInternal": true
}
}
3 changes: 3 additions & 0 deletions packages/crypto-random-source-browser/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
*.js
*.js.map
*.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import {randomValues} from '../lib/ie11RandomValues';

beforeEach(() => {
(window as any).msCrypto = {
getRandomValues(toFill: Uint8Array) {
const view = new DataView(toFill.buffer);
for (let i = 0; i < toFill.byteLength; i++) {
view.setUint8(i, 0x00);
}
}
};
});

describe('randomValues', () => {
it('should call the random source built into IE 11', async () => {
expect(await randomValues(4))
.toMatchObject(Uint8Array.from([0, 0, 0, 0]));
});

it(
'should convert a failed random generation into a promise rejection',
async () => {
(window as any).msCrypto.getRandomValues = () => {
throw new Error('PANIC PANIC');
};

await randomValues(12).then(
() => { throw new Error('The promise should have been rejected'); },
() => { /* promise rejected, just as expected */ }
);
}
);
});
Loading