Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

webcrypto-liner

Package Overview
Dependencies
Maintainers
2
Versions
60
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

webcrypto-liner - npm Package Compare versions

Comparing version 0.1.8 to 0.1.9

BrowserSupport.md

6

package.json
{
"name": "webcrypto-liner",
"version": "0.1.8",
"version": "0.1.9",
"description": "A WebCrypto pollyfill that \"smooths out\" the rough-edges in existing User Agent implementations.",

@@ -8,4 +8,4 @@ "main": "build/index.js",

"build": "npm run build:es5",
"build:es5": "tsc --module commonjs --target es5",
"build:es2015": "tsc --module es2015 --target es2015",
"build:es5": "tsc",
"build:es2015": "tsc -p tsconfig.es2015.json",
"build:rollup": "npm run build:es2015 && rollup -o=index.js -i=build/index.js",

@@ -12,0 +12,0 @@ "build:webpack": "webpack",

@@ -40,4 +40,4 @@ # webcrypto-liner

| Encryption/Decryption | RSA-OAEP, AES-CBC, and AES-GCM |
| Sign/Verify | RSA-PSS, ~~RSASSA-PKCS1-v1_5,~~ and ECDSA |
| Hash | SHA-1, SHA-224, SHA-256, and SHA-384 |
| Sign/Verify | RSA-PSS, RSASSA_PKCS1-v1_5 and ECDSA |
| Hash | SHA-1, and SHA-224 |
| Derive Key/Bits | ECDH |

@@ -114,2 +114,3 @@ | Keywrap | AES-GCM, AES-CBC |

- **Do I need to include elliptic.js?** No, not unless you want to use the algorithms it exposes.
- **How are random numbers generated?** We use two libraries for crypto operations in Javascript, [asymcrypto](https://github.com/vibornoff/asmcrypto.js/blob/bffc0674c7756dff16c69c5665b9eea2e0409736/src/random/globals.js#L4) and [ellipticjs](https://github.com/indutny/elliptic/blob/cbace4683a4a548dc0306ef36756151a20299cd5/dist/elliptic.js#L7464) both rely on [window.crypto.getRandomValues](http://caniuse.com/#feat=getrandomvalues) where available. `asymcrypto` also has a fallback mechanism where it generates its own random numbers if not present.
- **How big is the total package?** Right now, if you include all optional dependencies (minfied) the package is ~300 KB, if you include only ECC or only RSA support that is lowered to about 180 KB. Additionally you will see GZIP compression provide about 30% savings above and beyond that. If you use `webcrypto-liner` as just an interopability shim and do not use any of the optional third-party libraries it will be under 44 KB in size.

@@ -116,0 +117,0 @@ - **Will it work in Node?** No. It is compiles to pure Javascript but uses the `window` object so it wont work in Node at this time. With some minor changes it should also be able to work in Node also but you really should be using [node-webcrypto-ossl](https://github.com/PeculiarVentures/node-webcrypto-ossl) on Node instead.

import { BaseCrypto, AlgorithmNames, AlgorithmError, Base64Url } from "webcrypto-core";
import { LinerError } from "../crypto";
import { LinerError } from "../error";
import { CryptoKey, CryptoKeyPair } from "../key";
import { string2buffer, buffer2string, concat } from "../helper";
import * as asmCrypto from "asmcrypto.js";
import { nativeCrypto } from "../init";

@@ -19,130 +18,149 @@

static generateKey(alg: AesKeyGenParams, extractable: boolean, keyUsage: string[]): PromiseLike<CryptoKey> {
return new Promise<CryptoKey>(resolve => {
this.checkModule();
return Promise.resolve()
.then(() => {
this.checkModule();
// gat random bytes for key
const key = nativeCrypto.getRandomValues(new Uint8Array(alg.length / 8));
// gat random bytes for key
const key = nativeCrypto.getRandomValues(new Uint8Array(alg.length / 8));
// set key params
const aesKey: AesCryptoKey = new CryptoKey();
aesKey.key = key as Uint8Array;
aesKey.algorithm = alg;
aesKey.extractable = extractable;
aesKey.type = "secret";
aesKey.usages = keyUsage;
resolve(aesKey);
});
// set key params
const aesKey: AesCryptoKey = new CryptoKey();
aesKey.key = key as Uint8Array;
aesKey.algorithm = alg;
aesKey.extractable = extractable;
aesKey.type = "secret";
aesKey.usages = keyUsage;
return aesKey;
});
}
static encrypt(algorithm: Algorithm, key: AesCryptoKey, data: Uint8Array): PromiseLike<ArrayBuffer> {
return new Promise(resolve => {
let res: Uint8Array;
switch (algorithm.name.toUpperCase()) {
case AlgorithmNames.AesCBC:
let algCBC = algorithm as AesCbcParams;
res = asmCrypto.AES_CBC.encrypt(data, key.key, undefined, algCBC.iv) as Uint8Array;
break;
case AlgorithmNames.AesGCM:
let algGCM = algorithm as AesGcmParams;
algGCM.tagLength = algGCM.tagLength || 128;
res = asmCrypto.AES_GCM.encrypt(data, key.key, algGCM.iv, algGCM.additionalData, algGCM.tagLength / 8) as Uint8Array;
break;
default:
throw new LinerError(AlgorithmError.UNSUPPORTED_ALGORITHM, algorithm.name);
}
resolve(res.buffer);
});
return Promise.resolve()
.then(() => {
let res: Uint8Array;
switch (algorithm.name.toUpperCase()) {
case AlgorithmNames.AesCBC:
let algCBC = algorithm as AesCbcParams;
res = asmCrypto.AES_CBC.encrypt(data, key.key, undefined, algCBC.iv) as Uint8Array;
break;
case AlgorithmNames.AesGCM:
let algGCM = algorithm as AesGcmParams;
algGCM.tagLength = algGCM.tagLength || 128;
res = asmCrypto.AES_GCM.encrypt(data, key.key, algGCM.iv, algGCM.additionalData, algGCM.tagLength / 8) as Uint8Array;
break;
default:
throw new LinerError(AlgorithmError.UNSUPPORTED_ALGORITHM, algorithm.name);
}
return res.buffer;
});
}
static decrypt(algorithm: Algorithm, key: AesCryptoKey, data: Uint8Array): PromiseLike<ArrayBuffer> {
return new Promise(resolve => {
let res: Uint8Array;
return Promise.resolve()
.then(() => {
let res: Uint8Array;
switch (algorithm.name.toUpperCase()) {
case AlgorithmNames.AesCBC:
let algCBC = algorithm as AesCbcParams;
res = asmCrypto.AES_CBC.decrypt(data, key.key, undefined, algCBC.iv) as Uint8Array;
break;
case AlgorithmNames.AesGCM:
let algGCM = algorithm as AesGcmParams;
algGCM.tagLength = algGCM.tagLength || 128;
res = asmCrypto.AES_GCM.decrypt(data, key.key, algGCM.iv, algGCM.additionalData, algGCM.tagLength / 8) as Uint8Array;
break;
default:
throw new LinerError(AlgorithmError.UNSUPPORTED_ALGORITHM, algorithm.name);
}
resolve(res.buffer);
});
switch (algorithm.name.toUpperCase()) {
case AlgorithmNames.AesCBC:
let algCBC = algorithm as AesCbcParams;
res = asmCrypto.AES_CBC.decrypt(data, key.key, undefined, algCBC.iv) as Uint8Array;
break;
case AlgorithmNames.AesGCM:
let algGCM = algorithm as AesGcmParams;
algGCM.tagLength = algGCM.tagLength || 128;
res = asmCrypto.AES_GCM.decrypt(data, key.key, algGCM.iv, algGCM.additionalData, algGCM.tagLength / 8) as Uint8Array;
break;
default:
throw new LinerError(AlgorithmError.UNSUPPORTED_ALGORITHM, algorithm.name);
}
return res.buffer;
});
}
static wrapKey(format: string, key: CryptoKey, wrappingKey: CryptoKey, wrapAlgorithm: Algorithm): PromiseLike<ArrayBuffer> {
return new Promise((resolve, reject) => {
self.crypto.subtle.exportKey(format, key)
.then((data: any) => {
let raw: Uint8Array;
if (!(data instanceof ArrayBuffer)) {
// JWK
raw = string2buffer(JSON.stringify(data));
}
else {
// ArrayBuffer
raw = new Uint8Array(data);
}
return self.crypto.subtle.encrypt(wrapAlgorithm, wrappingKey, raw);
})
.then(resolve, reject);
});
let crypto: Crypto;
return Promise.resolve()
.then(() => {
crypto = new Crypto();
return crypto.subtle.exportKey(format, key);
})
.then((data: any) => {
let raw: Uint8Array;
if (!(data instanceof ArrayBuffer)) {
// JWK
raw = string2buffer(JSON.stringify(data));
}
else {
// ArrayBuffer
raw = new Uint8Array(data);
}
return crypto.subtle.encrypt(wrapAlgorithm, wrappingKey, raw);
});
}
static unwrapKey(format: string, wrappedKey: Uint8Array, unwrappingKey: CryptoKey, unwrapAlgorithm: Algorithm, unwrappedKeyAlgorithm: Algorithm, extractable: boolean, keyUsages: string[]): PromiseLike<CryptoKey> {
return new Promise((resolve, reject) => {
self.crypto.subtle.decrypt(unwrapAlgorithm, unwrappingKey, wrappedKey)
.then((data: any) => {
let _data: any;
if (format.toLowerCase() === "jwk")
_data = JSON.parse(buffer2string(new Uint8Array(data)));
else
_data = new Uint8Array(data);
return self.crypto.subtle.importKey(format, _data, unwrappedKeyAlgorithm, extractable, keyUsages);
})
.then(resolve, reject);
});
let crypto: Crypto;
return Promise.resolve()
.then(() => {
crypto = new Crypto();
return crypto.subtle.decrypt(unwrapAlgorithm, unwrappingKey, wrappedKey);
})
.then((data: any) => {
let _data: any;
if (format.toLowerCase() === "jwk")
_data = JSON.parse(buffer2string(new Uint8Array(data)));
else
_data = new Uint8Array(data);
return crypto.subtle.importKey(format, _data, unwrappedKeyAlgorithm, extractable, keyUsages);
});
}
static alg2jwk(alg: Algorithm): string {
return `A${(alg as AesKeyAlgorithm).length}${/-(\w+)/i.exec(alg.name!.toUpperCase()) ![1]}`;
}
static jwk2alg(alg: string): Algorithm {
throw new Error("Not implemented");
}
static exportKey(format: string, key: AesCryptoKey): PromiseLike<JsonWebKey | ArrayBuffer> {
return new Promise((resolve, reject) => {
const raw = key.key;
if (format.toLowerCase() === "jwk") {
let jwk: JsonWebKey = {
alg: `A${(key.algorithm as AesKeyAlgorithm).length}${/-(\w+)/i.exec(key.algorithm.name!.toUpperCase()) ![1]}`,
ext: key.extractable,
k: Base64Url.encode(raw),
key_ops: key.usages,
kty: "oct"
};
resolve(jwk);
}
else {
resolve(raw.buffer);
}
});
return Promise.resolve()
.then(() => {
const raw = key.key;
if (format.toLowerCase() === "jwk") {
let jwk: JsonWebKey = {
alg: this.alg2jwk(key.algorithm as Algorithm),
ext: key.extractable,
k: Base64Url.encode(raw),
key_ops: key.usages,
kty: "oct"
};
return jwk;
}
else {
return raw.buffer;
}
});
}
static importKey(format: string, keyData: JsonWebKey | Uint8Array, algorithm: Algorithm, extractable: boolean, keyUsages: string[]): PromiseLike<CryptoKey> {
return new Promise((resolve, reject) => {
let raw: Uint8Array;
if (format.toLowerCase() === "jwk") {
const jwk = keyData as JsonWebKey;
raw = Base64Url.decode(jwk.k!);
}
else
raw = new Uint8Array(keyData as Uint8Array);
const key = new CryptoKey();
key.algorithm = algorithm;
key.type = "secret";
key.usages = keyUsages;
key.key = raw;
resolve(key);
});
return Promise.resolve()
.then(() => {
let raw: Uint8Array;
if (format.toLowerCase() === "jwk") {
const jwk = keyData as JsonWebKey;
raw = Base64Url.decode(jwk.k!);
}
else
raw = new Uint8Array(keyData as Uint8Array);
const key = new CryptoKey();
key.algorithm = algorithm;
key.type = "secret";
key.usages = keyUsages;
key.key = raw;
return key;
});
}
}
}
import { Crypto } from "../crypto";

@@ -1,12 +0,4 @@

import { WebCryptoError } from "webcrypto-core";
import { SubtleCrypto } from "./subtle";
import { nativeCrypto } from "./init";
export class LinerError extends WebCryptoError {
code = 10;
static MODULE_NOT_FOUND = "Module '%1' is not found. Download it from %2.\nOnly hash algorithms supported by the user agent will be supported.";
static UNSUPPORTED_ALGORITHM = "Unsupported algorithm '%1'";
}
export class Crypto {

@@ -13,0 +5,0 @@

import { BaseCrypto, AlgorithmNames, AlgorithmError, Base64Url } from "webcrypto-core";
import { LinerError } from "../crypto";
import { LinerError } from "../error";
import { CryptoKey, CryptoKeyPair } from "../key";
import { string2buffer, buffer2string, concat } from "../helper";
import * as elliptic from "elliptic";
// import * as elliptic from "elliptic";
declare let elliptic: any;

@@ -65,158 +66,168 @@

static generateKey(alg: Algorithm, extractable: boolean, keyUsage: string[]) {
return new Promise<CryptoKeyPair>(resolve => {
this.checkModule();
const _alg: EcKeyGenParams = alg as any;
const key = new elliptic.ec(_alg.namedCurve.replace("-", "").toLowerCase()); // converts name to 'p192', ...
return Promise.resolve()
.then(() => {
this.checkModule();
const _alg: EcKeyGenParams = alg as any;
const key = new elliptic.ec(_alg.namedCurve.replace("-", "").toLowerCase()); // converts name to 'p192', ...
// set key params
const prvKey = new CryptoKey();
const pubKey = new CryptoKey();
prvKey.key = pubKey.key = key.genKeyPair();
prvKey.algorithm = pubKey.algorithm = _alg;
prvKey.extractable = extractable;
pubKey.extractable = true;
prvKey.type = "private";
pubKey.type = "public";
if (alg.name === AlgorithmNames.EcDSA) {
prvKey.usages = ["sign"];
pubKey.usages = ["verify"];
}
else if (alg.name === AlgorithmNames.EcDH) {
prvKey.usages = pubKey.usages = ["deriveKey", "deriveBits"];
}
resolve({
privateKey: prvKey,
publicKey: pubKey
// set key params
const prvKey = new CryptoKey();
const pubKey = new CryptoKey();
prvKey.key = pubKey.key = key.genKeyPair();
prvKey.algorithm = pubKey.algorithm = _alg;
prvKey.extractable = extractable;
pubKey.extractable = true;
prvKey.type = "private";
pubKey.type = "public";
if (alg.name === AlgorithmNames.EcDSA) {
prvKey.usages = ["sign"];
pubKey.usages = ["verify"];
}
else if (alg.name === AlgorithmNames.EcDH) {
prvKey.usages = pubKey.usages = ["deriveKey", "deriveBits"];
}
return {
privateKey: prvKey,
publicKey: pubKey
};
});
});
}
static sign(algorithm: Algorithm, key: CryptoKey, data: Uint8Array): PromiseLike<ArrayBuffer> {
return new Promise((resolve, reject) => {
const _alg: EcdsaParams = algorithm as any;
return Promise.resolve()
.then(() => {
const _alg: EcdsaParams = algorithm as any;
// get digest
(self.crypto.subtle.digest(_alg.hash, data) as Promise<ArrayBuffer>)
.then(hash => {
const array = b2a(hash);
const signature = key.key.sign(array);
const hexSignature = buffer2hex(signature.r.toArray(), true) + buffer2hex(signature.s.toArray(), true);
resolve(hex2buffer(hexSignature).buffer);
})
.catch(reject);
});
// get digest
let crypto = new Crypto();
return crypto.subtle.digest(_alg.hash, data);
})
.then(hash => {
const array = b2a(hash);
const signature = key.key.sign(array);
const hexSignature = buffer2hex(signature.r.toArray(), true) + buffer2hex(signature.s.toArray(), true);
return hex2buffer(hexSignature).buffer;
});
}
static verify(algorithm: Algorithm, key: CryptoKey, signature: Uint8Array, data: Uint8Array): PromiseLike<boolean> {
return new Promise((resolve, reject) => {
const _alg: EcdsaParams = algorithm as any;
const sig = {
r: signature.slice(0, signature.byteLength / 2),
s: signature.slice(signature.byteLength / 2)
};
// get digest
(self.crypto.subtle.digest(_alg.hash, data) as Promise<ArrayBuffer>)
.then(hash => {
const array = b2a(hash);
resolve(key.key.verify(array, sig));
})
.catch(reject);
});
let sig: { r: Uint8Array, s: Uint8Array };
return Promise.resolve()
.then(() => {
const _alg: EcdsaParams = algorithm as any;
sig = {
r: signature.slice(0, signature.byteLength / 2),
s: signature.slice(signature.byteLength / 2)
};
// get digest
let crypto = new Crypto();
return crypto.subtle.digest(_alg.hash, data);
})
.then(hash => {
const array = b2a(hash);
return (key.key.verify(array, sig));
});
}
static deriveKey(algorithm: EcdhKeyDeriveParams, baseKey: CryptoKey, derivedKeyType: AesKeyGenParams, extractable: boolean, keyUsages: string[]): PromiseLike<CryptoKey> {
return new Promise((resolve, reject) => {
this.deriveBits(algorithm, baseKey, derivedKeyType.length)
.then((bits: ArrayBuffer) => {
return self.crypto.subtle.importKey("raw", new Uint8Array(bits), derivedKeyType, extractable, keyUsages);
})
.then(resolve, reject);
});
return Promise.resolve()
.then(() =>
this.deriveBits(algorithm, baseKey, derivedKeyType.length)
)
.then((bits: ArrayBuffer) => {
let crypto = new Crypto();
return crypto.subtle.importKey("raw", new Uint8Array(bits), derivedKeyType, extractable, keyUsages);
});
}
static deriveBits(algorithm: EcdhKeyDeriveParams, baseKey: CryptoKey, length: number): PromiseLike<ArrayBuffer> {
return new Promise((resolve, reject) => {
let promise = (Promise as any).resolve(null);
const shared = baseKey.key.derive((algorithm.public as CryptoKey).key.getPublic());
let array = new Uint8Array(shared.toArray());
// Padding
let len = array.length;
len = len > 32 ? len > 48 ? 66 : 48 : 32;
if (array.length < len)
array = concat(new Uint8Array(len - array.length), array);
const buf = array.slice(0, length / 8).buffer;
resolve(buf);
});
return Promise.resolve()
.then(() => {
let promise = (Promise as any).resolve(null);
const shared = baseKey.key.derive((algorithm.public as CryptoKey).key.getPublic());
let array = new Uint8Array(shared.toArray());
// Padding
let len = array.length;
len = (len > 32 ? (len > 48 ? 66 : 48) : 32);
if (array.length < len)
array = concat(new Uint8Array(len - array.length), array);
const buf = array.slice(0, length / 8).buffer;
return buf;
});
}
static exportKey(format: string, key: EcCryptoKey): PromiseLike<JsonWebKey | ArrayBuffer> {
return new Promise((resolve, reject) => {
const ecKey = key.key;
if (format.toLowerCase() === "jwk") {
let hexPub = ecKey.getPublic("hex").slice(2); // ignore first '04'
const hexX = hexPub.slice(0, hexPub.length / 2);
const hexY = hexPub.slice(hexPub.length / 2, hexPub.length);
if (key.type === "public") {
// public
let jwk: JsonWebKey = {
crv: (key.algorithm as EcKeyGenParams).namedCurve,
ext: key.extractable,
x: Base64Url.encode(hex2buffer(hexX, true)),
y: Base64Url.encode(hex2buffer(hexY, true)),
key_ops: [],
kty: "EC"
};
resolve(jwk);
return Promise.resolve()
.then(() => {
const ecKey = key.key;
if (format.toLowerCase() === "jwk") {
let hexPub = ecKey.getPublic("hex").slice(2); // ignore first '04'
const hexX = hexPub.slice(0, hexPub.length / 2);
const hexY = hexPub.slice(hexPub.length / 2, hexPub.length);
if (key.type === "public") {
// public
let jwk: JsonWebKey = {
crv: (key.algorithm as EcKeyGenParams).namedCurve,
ext: key.extractable,
x: Base64Url.encode(hex2buffer(hexX, true)),
y: Base64Url.encode(hex2buffer(hexY, true)),
key_ops: key.usages,
kty: "EC"
};
return jwk;
}
else {
// private
let jwk: JsonWebKey = {
crv: (key.algorithm as EcKeyGenParams).namedCurve,
ext: key.extractable,
d: Base64Url.encode(hex2buffer(ecKey.getPrivate("hex"), true)),
x: Base64Url.encode(hex2buffer(hexX, true)),
y: Base64Url.encode(hex2buffer(hexY, true)),
key_ops: key.usages,
kty: "EC"
};
return jwk;
}
}
else {
// private
let jwk: JsonWebKey = {
crv: (key.algorithm as EcKeyGenParams).namedCurve,
ext: key.extractable,
d: Base64Url.encode(hex2buffer(ecKey.getPrivate("hex"), true)),
x: Base64Url.encode(hex2buffer(hexX, true)),
y: Base64Url.encode(hex2buffer(hexY, true)),
key_ops: key.usages,
kty: "EC"
};
resolve(jwk);
throw new LinerError(`Format '${format}' is not implemented`);
}
}
else {
throw new LinerError(`Format '${format}' is not implemented`);
}
});
});
}
static importKey(format: string, keyData: JsonWebKey | BufferSource, algorithm: string | RsaHashedImportParams | EcKeyImportParams | HmacImportParams | DhImportKeyParams, extractable: boolean, keyUsages: string[]): PromiseLike<CryptoKey> {
return new Promise((resolve, reject) => {
const key: EcCryptoKey = new CryptoKey();
key.algorithm = algorithm;
if (format.toLowerCase() === "jwk") {
const ecKey = new elliptic.ec((keyData as JsonWebKey).crv!.replace("-", "").toLowerCase());
if ((keyData as JsonWebKey).d) {
// Private key
key.key = ecKey.keyFromPrivate(Base64Url.decode((keyData as JsonWebKey).d!));
key.type = "private";
}
else {
// Public key
let bufferPubKey = concat(
new Uint8Array([4]),
Base64Url.decode((keyData as JsonWebKey).x!),
Base64Url.decode((keyData as JsonWebKey).y!)
);
const hexPubKey = buffer2hex(bufferPubKey);
return Promise.resolve()
.then(() => {
const key: EcCryptoKey = new CryptoKey();
key.algorithm = algorithm;
if (format.toLowerCase() === "jwk") {
const ecKey = new elliptic.ec((keyData as JsonWebKey).crv!.replace("-", "").toLowerCase());
if ((keyData as JsonWebKey).d) {
// Private key
key.key = ecKey.keyFromPrivate(Base64Url.decode((keyData as JsonWebKey).d!));
key.type = "private";
}
else {
// Public key
let bufferPubKey = concat(
new Uint8Array([4]),
Base64Url.decode((keyData as JsonWebKey).x!),
Base64Url.decode((keyData as JsonWebKey).y!)
);
const hexPubKey = buffer2hex(bufferPubKey);
key.key = ecKey.keyFromPublic(hexPubKey, "hex");
key.type = "public";
key.key = ecKey.keyFromPublic(hexPubKey, "hex");
key.type = "public";
}
}
}
else
throw new LinerError(`Format '${format}' is not implemented`);
key.extractable = extractable;
key.usages = keyUsages;
resolve(key);
});
else
throw new LinerError(`Format '${format}' is not implemented`);
key.extractable = extractable;
key.usages = keyUsages;
return key;
});
}
}
}
import { Crypto } from "../crypto";

@@ -17,11 +17,5 @@ export let Browser = {

};
if (typeof window !== "object" && typeof self !== void "object")
return {
name: "NodeJS",
version: process.version
};
const userAgent = self.navigator.userAgent;
let reg: RegExpExecArray | null;
let reg: string[] | null;
if (reg = /edge\/([\d\.]+)/i.exec(userAgent)) {

@@ -33,2 +27,5 @@ res.name = Browser.Edge;

res.version = /msie ([\d\.]+)/i.exec(userAgent) ![1];
} else if (/Trident/i.test(userAgent)) {
res.name = Browser.IE;
res.version = /rv:([\d\.]+)/i.exec(userAgent) ![1];
} else if (/chrome/i.test(userAgent)) {

@@ -35,0 +32,0 @@ res.name = Browser.Chrome;

export * from "./init";
export * from "./main";
export * from "./crypto";

@@ -0,4 +1,18 @@

import { LinerError } from "./error";
let _w: any;
if (typeof self === "undefined") {
_w = { crypto: { subtle: {} } };
const crypto = require("crypto");
_w = {
crypto: {
subtle: {},
getRandomValues: (array: ArrayBufferView) => {
let buf = array.buffer;
let uint8buf = new Uint8Array(buf);
const rnd = crypto.randomBytes(uint8buf.length);
rnd.forEach((octet: number, index: number) => uint8buf[index] = octet);
return array;
}
}
};
}

@@ -9,2 +23,47 @@ else

export const nativeCrypto: NativeCrypto = _w.msCrypto || _w.crypto;
export const nativeSubtle: NativeSubtleCrypto = nativeCrypto.subtle || (nativeCrypto as any).webkitSubtle;
export const nativeSubtle: NativeSubtleCrypto = nativeCrypto.subtle || (nativeCrypto as any).webkitSubtle;
function WrapFunction(subtle: any, name: string) {
const fn = subtle[name];
subtle[name] = function () {
const _args = arguments;
return new Promise((resolve, reject) => {
let op: any = fn.apply(subtle, _args);
op.oncomplete = (e: any) => {
console.log("Complited");
resolve(e.target.result);
};
op.onerror = (e: any) => {
console.log("Error");
reject(`Error on running '${name}' function`);
};
});
};
}
if (_w.msCrypto) {
if (!_w.Promise)
throw new LinerError(LinerError.MODULE_NOT_FOUND, "Promise", "https://www.promisejs.org");
WrapFunction(nativeSubtle, "generateKey");
WrapFunction(nativeSubtle, "digest");
WrapFunction(nativeSubtle, "sign");
WrapFunction(nativeSubtle, "verify");
WrapFunction(nativeSubtle, "encrypt");
WrapFunction(nativeSubtle, "decrypt");
WrapFunction(nativeSubtle, "importKey");
WrapFunction(nativeSubtle, "exportKey");
WrapFunction(nativeSubtle, "wrapKey");
WrapFunction(nativeSubtle, "unwrapKey");
WrapFunction(nativeSubtle, "deriveKey");
WrapFunction(nativeSubtle, "deriveBits");
}
// fix: Math.imul for IE
if (!(Math as any).imul)
(Math as any).imul = function imul(a: number, b: number) {
let ah = (a >>> 16) & 0xffff;
let al = a & 0xffff;
let bh = (b >>> 16) & 0xffff;
let bl = b & 0xffff;
return ((al * bl) + (((ah * bl + al * bh) << 16) >>> 0) | 0);
};
import { BaseCrypto, AlgorithmNames, AlgorithmError, Base64Url } from "webcrypto-core";
import { LinerError } from "../crypto";
import { LinerError } from "../error";
import { CryptoKey, CryptoKeyPair } from "../key";
import { string2buffer, buffer2string, concat } from "../helper";
import * as asmCrypto from "asmcrypto.js";

@@ -34,212 +33,220 @@ interface RsaCryptoKey extends CryptoKey {

static generateKey(alg: RsaKeyGenParams, extractable: boolean, keyUsage: string[]): PromiseLike<CryptoKeyPair> {
return new Promise<CryptoKeyPair>(resolve => {
this.checkModule();
return Promise.resolve()
.then(() => {
this.checkModule();
const pubExp = alg.publicExponent[0] === 3 ? 3 : 65537;
const rsaKey = asmCrypto.RSA.generateKey(alg.modulusLength, pubExp);
const privateKey: RsaCryptoKey = new CryptoKey();
const publicKey: RsaCryptoKey = new CryptoKey();
privateKey.key = publicKey.key = rsaKey;
privateKey.algorithm = publicKey.algorithm = alg;
privateKey.extractable = extractable;
publicKey.extractable = true;
privateKey.type = "private";
publicKey.type = "public";
switch (alg.name.toLowerCase()) {
case AlgorithmNames.RsaOAEP.toLowerCase():
privateKey.usages = this.filterUsages(["decrypt", "unwrapKey"], keyUsage);
publicKey.usages = this.filterUsages(["encrypt", "wrapKey"], keyUsage);
break;
case AlgorithmNames.RsaPSS.toLowerCase():
privateKey.usages = this.filterUsages(["sign"], keyUsage);
publicKey.usages = this.filterUsages(["verify"], keyUsage);
break;
default:
throw new LinerError(LinerError.UNSUPPORTED_ALGORITHM, alg.name);
}
resolve({ privateKey, publicKey });
});
const pubExp = alg.publicExponent[0] === 3 ? 3 : 65537;
const rsaKey = asmCrypto.RSA.generateKey(alg.modulusLength, pubExp);
const privateKey: RsaCryptoKey = new CryptoKey();
const publicKey: RsaCryptoKey = new CryptoKey();
privateKey.key = publicKey.key = rsaKey;
privateKey.algorithm = publicKey.algorithm = alg;
privateKey.extractable = extractable;
publicKey.extractable = true;
privateKey.type = "private";
publicKey.type = "public";
switch (alg.name.toLowerCase()) {
case AlgorithmNames.RsaOAEP.toLowerCase():
privateKey.usages = this.filterUsages(["decrypt", "unwrapKey"], keyUsage);
publicKey.usages = this.filterUsages(["encrypt", "wrapKey"], keyUsage);
break;
case AlgorithmNames.RsaPSS.toLowerCase():
privateKey.usages = this.filterUsages(["sign"], keyUsage);
publicKey.usages = this.filterUsages(["verify"], keyUsage);
break;
default:
throw new LinerError(LinerError.UNSUPPORTED_ALGORITHM, alg.name);
}
return { privateKey, publicKey };
});
}
static sign(algorithm: Algorithm, key: RsaCryptoKey, data: Uint8Array): PromiseLike<ArrayBuffer> {
return new Promise((resolve, reject) => {
switch (algorithm.name.toLowerCase()) {
case AlgorithmNames.RsaPSS.toLowerCase():
let keyAlg: RsaHashedKeyGenParams = key.algorithm as any;
let _alg: RsaPssParams = algorithm as any;
let sign: typeof asmCrypto.RSA_PSS_SHA1.sign;
switch ((keyAlg.hash as Algorithm).name.toUpperCase()) {
case AlgorithmNames.Sha1:
sign = asmCrypto.RSA_PSS_SHA1.sign;
break;
case AlgorithmNames.Sha256:
sign = asmCrypto.RSA_PSS_SHA256.sign;
break;
default:
throw new LinerError(LinerError.UNSUPPORTED_ALGORITHM, key.algorithm.name);
}
resolve(sign(data, key.key, _alg.saltLength).buffer);
break;
default:
throw new LinerError(LinerError.UNSUPPORTED_ALGORITHM, algorithm.name);
}
});
return Promise.resolve()
.then(() => {
switch (algorithm.name.toLowerCase()) {
case AlgorithmNames.RsaPSS.toLowerCase():
let keyAlg: RsaHashedKeyGenParams = key.algorithm as any;
let _alg: RsaPssParams = algorithm as any;
let sign: typeof asmCrypto.RSA_PSS_SHA1.sign;
switch ((keyAlg.hash as Algorithm).name.toUpperCase()) {
case AlgorithmNames.Sha1:
sign = asmCrypto.RSA_PSS_SHA1.sign;
break;
case AlgorithmNames.Sha256:
sign = asmCrypto.RSA_PSS_SHA256.sign;
break;
default:
throw new LinerError(LinerError.UNSUPPORTED_ALGORITHM, key.algorithm.name);
}
return sign(data, key.key, _alg.saltLength).buffer;
default:
throw new LinerError(LinerError.UNSUPPORTED_ALGORITHM, algorithm.name);
}
});
}
static verify(algorithm: Algorithm, key: RsaCryptoKey, signature: Uint8Array, data: Uint8Array): PromiseLike<boolean> {
return new Promise((resolve, reject) => {
switch (algorithm.name.toLowerCase()) {
case AlgorithmNames.RsaPSS.toLowerCase():
let keyAlg: RsaHashedKeyGenParams = key.algorithm as any;
let _alg: RsaPssParams = algorithm as any;
let verify: typeof asmCrypto.RSA_PSS_SHA1.verify;
switch ((keyAlg.hash as Algorithm).name.toUpperCase()) {
case AlgorithmNames.Sha1:
verify = asmCrypto.RSA_PSS_SHA1.verify;
break;
case AlgorithmNames.Sha256:
verify = asmCrypto.RSA_PSS_SHA256.verify;
break;
default:
throw new LinerError(LinerError.UNSUPPORTED_ALGORITHM, key.algorithm.name);
}
resolve(verify(signature, data, key.key, _alg.saltLength));
break;
default:
throw new LinerError(LinerError.UNSUPPORTED_ALGORITHM, algorithm.name);
}
});
return Promise.resolve()
.then(() => {
switch (algorithm.name.toLowerCase()) {
case AlgorithmNames.RsaPSS.toLowerCase():
let keyAlg: RsaHashedKeyGenParams = key.algorithm as any;
let _alg: RsaPssParams = algorithm as any;
let verify: typeof asmCrypto.RSA_PSS_SHA1.verify;
switch ((keyAlg.hash as Algorithm).name.toUpperCase()) {
case AlgorithmNames.Sha1:
verify = asmCrypto.RSA_PSS_SHA1.verify;
break;
case AlgorithmNames.Sha256:
verify = asmCrypto.RSA_PSS_SHA256.verify;
break;
default:
throw new LinerError(LinerError.UNSUPPORTED_ALGORITHM, key.algorithm.name);
}
return verify(signature, data, key.key, _alg.saltLength);
default:
throw new LinerError(LinerError.UNSUPPORTED_ALGORITHM, algorithm.name);
}
});
}
static encrypt(algorithm: Algorithm, key: RsaCryptoKey, data: Uint8Array): PromiseLike<ArrayBuffer> {
return new Promise((resolve, reject) => {
switch (algorithm.name.toLowerCase()) {
case AlgorithmNames.RsaOAEP.toLowerCase():
let keyAlg: RsaHashedKeyGenParams = key.algorithm as any;
let _alg: RsaOaepParams = algorithm as any;
let encrypt: typeof asmCrypto.RSA_OAEP_SHA1.encrypt;
switch ((keyAlg.hash as Algorithm).name.toUpperCase()) {
case AlgorithmNames.Sha1:
encrypt = asmCrypto.RSA_OAEP_SHA1.encrypt;
break;
case AlgorithmNames.Sha256:
encrypt = asmCrypto.RSA_OAEP_SHA256.encrypt;
break;
default:
throw new LinerError(LinerError.UNSUPPORTED_ALGORITHM, `${keyAlg.name} ${(keyAlg.hash as Algorithm).name}`);
}
resolve(encrypt(data, key.key, _alg.label));
break;
default:
throw new LinerError(LinerError.UNSUPPORTED_ALGORITHM, algorithm.name);
}
});
return Promise.resolve()
.then(() => {
switch (algorithm.name.toLowerCase()) {
case AlgorithmNames.RsaOAEP.toLowerCase():
let keyAlg: RsaHashedKeyGenParams = key.algorithm as any;
let _alg: RsaOaepParams = algorithm as any;
let encrypt: typeof asmCrypto.RSA_OAEP_SHA1.encrypt;
switch ((keyAlg.hash as Algorithm).name.toUpperCase()) {
case AlgorithmNames.Sha1:
encrypt = asmCrypto.RSA_OAEP_SHA1.encrypt;
break;
case AlgorithmNames.Sha256:
encrypt = asmCrypto.RSA_OAEP_SHA256.encrypt;
break;
default:
throw new LinerError(LinerError.UNSUPPORTED_ALGORITHM, `${keyAlg.name} ${(keyAlg.hash as Algorithm).name}`);
}
return encrypt(data, key.key, _alg.label);
default:
throw new LinerError(LinerError.UNSUPPORTED_ALGORITHM, algorithm.name);
}
});
}
static decrypt(algorithm: Algorithm, key: RsaCryptoKey, data: Uint8Array): PromiseLike<ArrayBuffer> {
return new Promise((resolve, reject) => {
switch (algorithm.name.toLowerCase()) {
case AlgorithmNames.RsaOAEP.toLowerCase():
let keyAlg: RsaHashedKeyGenParams = key.algorithm as any;
let _alg: RsaOaepParams = algorithm as any;
let decrypt: typeof asmCrypto.RSA_OAEP_SHA1.decrypt;
switch ((keyAlg.hash as Algorithm).name.toUpperCase()) {
case AlgorithmNames.Sha1:
decrypt = asmCrypto.RSA_OAEP_SHA1.decrypt;
break;
case AlgorithmNames.Sha256:
decrypt = asmCrypto.RSA_OAEP_SHA256.decrypt;
break;
default:
throw new LinerError(LinerError.UNSUPPORTED_ALGORITHM, `${keyAlg.name} ${(keyAlg.hash as Algorithm).name}`);
}
resolve(decrypt(data, key.key, _alg.label));
break;
default:
throw new LinerError(LinerError.UNSUPPORTED_ALGORITHM, algorithm.name);
}
});
return Promise.resolve()
.then(() => {
switch (algorithm.name.toLowerCase()) {
case AlgorithmNames.RsaOAEP.toLowerCase():
let keyAlg: RsaHashedKeyGenParams = key.algorithm as any;
let _alg: RsaOaepParams = algorithm as any;
let decrypt: typeof asmCrypto.RSA_OAEP_SHA1.decrypt;
switch ((keyAlg.hash as Algorithm).name.toUpperCase()) {
case AlgorithmNames.Sha1:
decrypt = asmCrypto.RSA_OAEP_SHA1.decrypt;
break;
case AlgorithmNames.Sha256:
decrypt = asmCrypto.RSA_OAEP_SHA256.decrypt;
break;
default:
throw new LinerError(LinerError.UNSUPPORTED_ALGORITHM, `${keyAlg.name} ${(keyAlg.hash as Algorithm).name}`);
}
return decrypt(data, key.key, _alg.label);
default:
throw new LinerError(LinerError.UNSUPPORTED_ALGORITHM, algorithm.name);
}
});
}
static wrapKey(format: string, key: CryptoKey, wrappingKey: CryptoKey, wrapAlgorithm: Algorithm): PromiseLike<ArrayBuffer> {
return new Promise((resolve, reject) => {
self.crypto.subtle.exportKey(format, key)
.then((data: any) => {
let raw: Uint8Array;
if (!(data instanceof ArrayBuffer)) {
// JWK
raw = string2buffer(JSON.stringify(data));
}
else {
// ArrayBuffer
raw = new Uint8Array(data);
}
return self.crypto.subtle.encrypt(wrapAlgorithm, wrappingKey, raw);
})
.then(resolve, reject);
});
let crypto: Crypto;
return Promise.resolve()
.then(() => {
crypto = new Crypto();
return crypto.subtle.exportKey(format, key);
})
.then((data: any) => {
let raw: Uint8Array;
if (!(data instanceof ArrayBuffer)) {
// JWK
raw = string2buffer(JSON.stringify(data));
}
else {
// ArrayBuffer
raw = new Uint8Array(data);
}
return crypto.subtle.encrypt(wrapAlgorithm, wrappingKey, raw);
});
}
static unwrapKey(format: string, wrappedKey: Uint8Array, unwrappingKey: CryptoKey, unwrapAlgorithm: Algorithm, unwrappedKeyAlgorithm: Algorithm, extractable: boolean, keyUsages: string[]): PromiseLike<CryptoKey> {
return new Promise((resolve, reject) => {
self.crypto.subtle.decrypt(unwrapAlgorithm, unwrappingKey, wrappedKey)
.then((data: any) => {
let _data: any;
if (format.toLowerCase() === "jwk")
_data = JSON.parse(buffer2string(new Uint8Array(data)));
else
_data = new Uint8Array(data);
return self.crypto.subtle.importKey(format, _data, unwrappedKeyAlgorithm, extractable, keyUsages);
})
.then(resolve, reject);
});
let crypto: Crypto;
return Promise.resolve()
.then(() => {
crypto = new Crypto();
return crypto.subtle.decrypt(unwrapAlgorithm, unwrappingKey, wrappedKey);
})
.then((data: any) => {
let _data: any;
if (format.toLowerCase() === "jwk")
_data = JSON.parse(buffer2string(new Uint8Array(data)));
else
_data = new Uint8Array(data);
return crypto.subtle.importKey(format, _data, unwrappedKeyAlgorithm, extractable, keyUsages);
});
}
static alg2jwk(alg: Algorithm) {
const hash = (alg as any).hash as Algorithm;
const hashSize = /(\d+)/.exec(hash.name) ![1];
switch (alg.name!.toUpperCase()) {
case AlgorithmNames.RsaOAEP.toUpperCase():
return `RSA-OAEP${hashSize === "1" ? "" : `-${hashSize}`}`;
case AlgorithmNames.RsaPSS.toUpperCase():
return `PS${hashSize}`;
case AlgorithmNames.RsaSSA.toUpperCase():
return `RS${hashSize}`;
default:
throw new AlgorithmError(AlgorithmError.UNSUPPORTED_ALGORITHM, alg.name);
}
}
static jwk2alg(alg: string): Algorithm {
throw new Error("Not implemented");
}
static exportKey(format: string, key: RsaCryptoKey): PromiseLike<JsonWebKey | ArrayBuffer> {
return new Promise((resolve, reject) => {
if (format.toLowerCase() === "jwk") {
const jwk: JsonWebKey = {
kty: "RSA",
ext: true,
key_ops: key.usages
};
const hash = (key.algorithm as RsaHashedKeyAlgorithm).hash as Algorithm;
const hashSize = /(\d+)/.exec(hash.name) ![1];
switch (key.algorithm.name!.toUpperCase()) {
case AlgorithmNames.RsaOAEP.toUpperCase():
jwk.alg = `RSA-OAEP${hashSize === "1" ? "" : `-${hashSize}`}`;
break;
case AlgorithmNames.RsaPSS.toUpperCase():
jwk.alg = `PS${hashSize}`;
break;
case AlgorithmNames.RsaSSA.toUpperCase():
jwk.alg = `RS${hashSize}`;
break;
default:
throw new AlgorithmError(AlgorithmError.UNSUPPORTED_ALGORITHM, key.algorithm.name);
return Promise.resolve()
.then(() => {
if (format.toLowerCase() === "jwk") {
const jwk: JsonWebKey = {
kty: "RSA",
ext: true,
key_ops: key.usages
};
jwk.alg = this.alg2jwk(key.algorithm as Algorithm);
jwk.n = Base64Url.encode(removeLeadingZero(key.key[0]));
jwk.e = Base64Url.encode(removeLeadingZero(key.key[1]));
if (key.type === "private") {
jwk.d = Base64Url.encode(removeLeadingZero(key.key[2]));
jwk.p = Base64Url.encode(removeLeadingZero(key.key[3]));
jwk.q = Base64Url.encode(removeLeadingZero(key.key[4]));
jwk.dp = Base64Url.encode(removeLeadingZero(key.key[5]));
jwk.dq = Base64Url.encode(removeLeadingZero(key.key[6]));
jwk.qi = Base64Url.encode(removeLeadingZero(key.key[7]));
}
return jwk;
}
jwk.n = Base64Url.encode(removeLeadingZero(key.key[0]));
jwk.e = Base64Url.encode(removeLeadingZero(key.key[1]));
if (key.type === "private") {
jwk.d = Base64Url.encode(removeLeadingZero(key.key[2]));
jwk.p = Base64Url.encode(removeLeadingZero(key.key[3]));
jwk.q = Base64Url.encode(removeLeadingZero(key.key[4]));
jwk.dp = Base64Url.encode(removeLeadingZero(key.key[5]));
jwk.dq = Base64Url.encode(removeLeadingZero(key.key[6]));
jwk.qi = Base64Url.encode(removeLeadingZero(key.key[7]));
else {
throw new LinerError(LinerError.NOT_SUPPORTED);
}
resolve(jwk);
}
else {
throw new LinerError(LinerError.NOT_SUPPORTED);
}
});
});
}
static importKey(format: string, keyData: JsonWebKey | Uint8Array, algorithm: Algorithm, extractable: boolean, keyUsages: string[]): PromiseLike<CryptoKey> {
return new Promise((resolve, reject) => {
return Promise.resolve()
.then(() => {
let raw: Uint8Array;

@@ -266,9 +273,10 @@ let jwk: JsonWebKey;

key.type = "public";
resolve(key);
return key;
}
else
throw new LinerError(LinerError.NOT_SUPPORTED);
resolve(key);
});
}
}
}
import { Crypto } from "../crypto";
import { BaseCrypto, AlgorithmNames, AlgorithmError, Base64Url } from "webcrypto-core";
import { LinerError } from "../crypto";
import { LinerError } from "../error";
import { CryptoKey, CryptoKeyPair } from "../key";
import { string2buffer, buffer2string, concat } from "../helper";
import * as asmCrypto from "asmcrypto.js";
export class ShaCrypto extends BaseCrypto {
static digest(alg: Algorithm, message: Uint8Array) {
return new Promise<ArrayBuffer>(resolve => {
if (typeof asmCrypto === "undefined")
throw new LinerError(LinerError.MODULE_NOT_FOUND, "asmCrypto", "https://github.com/vibornoff/asmcrypto.js");
switch (alg.name.toUpperCase()) {
case AlgorithmNames.Sha1:
resolve(asmCrypto.SHA1.bytes(message).buffer);
break;
case AlgorithmNames.Sha256:
resolve(asmCrypto.SHA256.bytes(message).buffer);
break;
default:
throw new LinerError(`Not supported algorithm '${alg.name}'`);
}
});
return Promise.resolve()
.then(() => {
if (typeof asmCrypto === "undefined")
throw new LinerError(LinerError.MODULE_NOT_FOUND, "asmCrypto", "https://github.com/vibornoff/asmcrypto.js");
switch (alg.name.toUpperCase()) {
case AlgorithmNames.Sha1:
return asmCrypto.SHA1.bytes(message).buffer;
case AlgorithmNames.Sha256:
return asmCrypto.SHA256.bytes(message).buffer;
default:
throw new LinerError(`Not supported algorithm '${alg.name}'`);
}
});
}
}

@@ -8,3 +8,4 @@ // Core

import { nativeSubtle } from "./init";
import { LinerError } from "./crypto";
import { LinerError } from "./error";
import { Crypto } from "./crypto";
import { CryptoKey, CryptoKeyPair } from "./key";

@@ -24,16 +25,19 @@ import { string2buffer, buffer2string, concat, Browser, BrowserInfo, assign } from "./helper";

function PrepareKey(key: CryptoKey, subtle: typeof BaseCrypto): PromiseLike<CryptoKey> {
let promise = Promise.resolve(key);
if (!key.key)
if (!key.extractable) {
throw new LinerError("'key' is Native CryptoKey. It can't be converted to JS CryptoKey");
}
else {
promise = promise.then(() =>
self.crypto.subtle.exportKey("jwk", key)
)
.then((jwk: any) =>
subtle.importKey("jwk", jwk, key.algorithm as Algorithm, true, key.usages)
);
}
return promise;
return Promise.resolve()
.then(() => {
if (!key.key) {
if (!key.extractable) {
throw new LinerError("'key' is Native CryptoKey. It can't be converted to JS CryptoKey");
}
else {
let crypto = new Crypto();
return crypto.subtle.exportKey("jwk", key)
.then((jwk: any) =>
subtle.importKey("jwk", jwk, key.algorithm as Algorithm, true, key.usages)
);
}
}
else
return key;
});
}

@@ -63,9 +67,5 @@

if (keys) {
if ("keyUsage" in keys || ((keys as IE).privateKey && "keyUsage" in keys)) {
let _keys: IE = keys;
if (!_keys.privateKey)
_keys.usages = keyUsages;
}
FixCryptoKeyUsages(keys, keyUsages);
SetHashAlgorithm(_alg, keys);
return new Promise(resolve => resolve(keys));
return keys;
}

@@ -113,3 +113,3 @@ let Class: typeof BaseCrypto;

.then((digest: ArrayBuffer) => {
if (digest) return new Promise(resolve => resolve(digest));
if (digest) return digest;
return ShaCrypto.digest(_alg, _data);

@@ -128,3 +128,6 @@ });

GetHashAlgorithm(_alg, key);
const _alg2 = GetHashAlgorithm(key);
if (_alg2) {
args[0] = assign(_alg, _alg2);
}
try {

@@ -141,3 +144,3 @@ return nativeSubtle.sign.apply(nativeSubtle, args)

.then((signature: ArrayBuffer) => {
if (signature) return new Promise(resolve => resolve(signature));
if (signature) return signature;
let Class: typeof BaseCrypto;

@@ -170,3 +173,6 @@ switch (_alg.name.toLowerCase()) {

GetHashAlgorithm(_alg, key);
const _alg2 = GetHashAlgorithm(key);
if (_alg2) {
args[0] = assign(_alg, _alg2);
}
try {

@@ -183,3 +189,3 @@ return nativeSubtle.verify.apply(nativeSubtle, args)

.then((result: boolean) => {
if (typeof result === "boolean") return new Promise(resolve => resolve(result));
if (typeof result === "boolean") return result;
let Class: typeof BaseCrypto;

@@ -221,3 +227,3 @@ switch (_alg.name.toLowerCase()) {

.then((bits: ArrayBuffer) => {
if (bits) return new Promise(resolve => resolve(bits));
if (bits) return bits;
let Class: typeof BaseCrypto;

@@ -255,3 +261,6 @@ switch (_alg.name.toLowerCase()) {

.then((key: CryptoKey) => {
if (key) return new Promise(resolve => resolve(key));
if (key) {
FixCryptoKeyUsages(key, keyUsages);
return key;
}
let Class: typeof BaseCrypto;

@@ -288,4 +297,16 @@ switch (_alg.name.toLowerCase()) {

})
.then((msg: ArrayBuffer) => {
if (msg) return new Promise(resolve => resolve(msg));
.then((msg: any) => {
if (msg) {
if (BrowserInfo().name === Browser.IE &&
_alg.name.toUpperCase() === AlgorithmNames.AesGCM &&
msg.ciphertext) {
// Concatinate values in IE
let buf = new Uint8Array(msg.ciphertext.byteLength + msg.tag.byteLength);
let count = 0;
new Uint8Array(msg.ciphertext).forEach((v: number) => buf[count++] = v);
new Uint8Array(msg.tag).forEach((v: number) => buf[count++] = v);
msg = buf.buffer;
}
return Promise.resolve(msg);
}
let Class: typeof BaseCrypto;

@@ -316,4 +337,15 @@ switch (_alg.name.toLowerCase()) {

let _data2: any = _data;
if (BrowserInfo().name === Browser.IE &&
_alg.name.toUpperCase() === AlgorithmNames.AesGCM) {
// Split buffer
const len = _data.byteLength - ((_alg as any).tagLength / 8);
_data2 = {
ciphertext: _data.buffer.slice(0, len),
tag: _data.buffer.slice(len, _data.byteLength)
};
}
try {
return nativeSubtle.decrypt.apply(nativeSubtle, args)
return nativeSubtle.decrypt.call(nativeSubtle, _alg, key, _data2)
.catch((e: Error) => {

@@ -328,3 +360,3 @@ console.warn(`WebCrypto: native 'decrypt' for ${_alg.name} doesn't work.`, e.message || "");

.then((msg: ArrayBuffer) => {
if (msg) return new Promise(resolve => resolve(msg));
if (msg) return msg;
let Class: typeof BaseCrypto;

@@ -365,3 +397,3 @@ switch (_alg.name.toLowerCase()) {

.then((msg: ArrayBuffer) => {
if (msg) return new Promise(resolve => resolve(msg));
if (msg) return msg;
let Class: typeof BaseCrypto;

@@ -404,4 +436,7 @@ switch (_alg.name.toLowerCase()) {

})
.then((msg: ArrayBuffer) => {
if (msg) return new Promise(resolve => resolve(msg));
.then((k: CryptoKey) => {
if (k) {
FixCryptoKeyUsages(k, keyUsages);
return k;
}
let Class: typeof BaseCrypto;

@@ -427,3 +462,2 @@ switch (_alg.name.toLowerCase()) {

.then(() => {
try {

@@ -445,2 +479,5 @@ return nativeSubtle.exportKey.apply(nativeSubtle, args)

}
let alg = GetHashAlgorithm(key);
if (!alg) alg = assign({}, key.algorithm);
FixExportJwk(msg, alg, key.usages);
return Promise.resolve(msg);

@@ -481,4 +518,8 @@ }

// Fix: Safari
if (BrowserInfo().name === Browser.Safari) {
if (BrowserInfo().name === Browser.Safari || BrowserInfo().name === Browser.IE) {
// Converts JWK to ArrayBuffer
if (BrowserInfo().name === Browser.IE) {
keyData = assign({}, keyData);
FixImportJwk(keyData);
}
args[1] = string2buffer(JSON.stringify(keyData)).buffer;

@@ -501,6 +542,7 @@ }

})
.then((msg: CryptoKey) => {
if (msg) {
SetHashAlgorithm(_alg, msg);
return new Promise(resolve => resolve(msg));
.then((k: CryptoKey) => {
if (k) {
SetHashAlgorithm(_alg, k);
FixCryptoKeyUsages(k, keyUsages);
return Promise.resolve(k);
}

@@ -531,3 +573,3 @@ let Class: typeof BaseCrypto;

function SetHashAlgorithm(alg: Algorithm, key: CryptoKey | CryptoKeyPair) {
if ((BrowserInfo().name === Browser.Edge || BrowserInfo().name === Browser.Safari) && /^rsa/i.test(alg.name)) {
if ((BrowserInfo().name === Browser.IE || BrowserInfo().name === Browser.Edge || BrowserInfo().name === Browser.Safari) && /^rsa/i.test(alg.name)) {
if ((key as CryptoKeyPair).privateKey) {

@@ -543,12 +585,108 @@ keys.push({ hash: (alg as any).hash, key: (key as CryptoKeyPair).privateKey });

// fix hash alg for rsa key
function GetHashAlgorithm(alg: Algorithm, key: CryptoKey) {
if ((BrowserInfo().name === Browser.Edge || BrowserInfo().name === Browser.Safari) && /^rsa/i.test(alg.name)) {
keys.some(item => {
if (item.key = key) {
(alg as any).hash = item.hash;
return true;
function GetHashAlgorithm(key: CryptoKey) {
let alg: Algorithm | null = null;
keys.some(item => {
if (item.key === key) {
alg = assign({}, key.algorithm, { hash: item.hash });
return true;
}
return false;
});
return alg;
}
// Extend Uint8Array for IE
if (!Uint8Array.prototype.forEach) {
(Uint8Array as any).prototype.forEach = function (cb: (value: number, index: number, array: Uint8Array) => void) {
for (let i = 0; i < this.length; i++) {
cb(this[i], i, this);
}
};
}
if (!Uint8Array.prototype.slice) {
(Uint8Array as any).prototype.slice = function (start: number, end: number) {
return new Uint8Array(this.buffer.slice(start, end));
};
}
if (!Uint8Array.prototype.filter) {
(Uint8Array as any).prototype.filter = function (cb: (value: number, index: number, array: Uint8Array) => void) {
let buf: number[] = [];
for (let i = 0; i < this.length; i++) {
if (cb(this[i], i, this))
buf.push(this[i]);
}
return new Uint8Array(buf);
};
}
function FixCryptoKeyUsages(key: CryptoKey | CryptoKeyPair, keyUsages: string[]) {
const keys: CryptoKey[] = [];
if ((key as CryptoKeyPair).privateKey) {
keys.push((key as CryptoKeyPair).privateKey);
keys.push((key as CryptoKeyPair).publicKey);
}
else {
keys.push(key as CryptoKey);
}
keys.forEach((k: any) => {
if ("keyUsage" in k) {
k.usages = k.keyUsage || [];
// add usages
if (!k.usages.length) {
["verify", "encrypt", "wrapKey"]
.forEach(usage => {
if (keyUsages.indexOf(usage) > -1 && (k.type === "public" || k.type === "secret"))
k.usages.push(usage);
});
["sign", "decrypt", "unwrapKey", "deriveKey", "deriveBits"]
.forEach(usage => {
if (keyUsages.indexOf(usage) > -1 && (k.type === "private" || k.type === "secret"))
k.usages.push(usage);
});
}
return false;
});
}
});
}
function FixExportJwk(jwk: any, alg: any, keyUsages: string[]) {
if (alg && BrowserInfo().name === Browser.IE) {
// ext
if ("extractable" in jwk) {
jwk.ext = jwk.extractable;
delete jwk.extractable;
}
// add alg
let CryptoClass: AlgorithmConverter | null = null;
switch (alg.name.toUpperCase()) {
case AlgorithmNames.RsaOAEP.toUpperCase():
case AlgorithmNames.RsaPSS.toUpperCase():
case AlgorithmNames.RsaSSA.toUpperCase():
CryptoClass = RsaCrypto;
break;
case AlgorithmNames.AesCBC.toUpperCase():
case AlgorithmNames.AesGCM.toUpperCase():
CryptoClass = AesCrypto;
break;
}
if (CryptoClass && !jwk.alg) {
jwk.alg = CryptoClass.alg2jwk(alg);
}
// add key_ops
if (!("key_ops" in jwk))
jwk.key_ops = keyUsages;
}
}
}
function FixImportJwk(jwk: any) {
if (BrowserInfo().name === Browser.IE) {
// ext
if ("ext" in jwk) {
jwk.extractable = jwk.ext;
delete jwk.ext;
}
delete jwk.key_ops;
delete jwk.alg;
}
}
"use strict"
const path = require("path");
const webpack = require("webpack");
module.exports = {
entry: "./src/index.ts",
module.exports = {
entry: {
"webcrypto-liner.shim": "./src/shim.ts",
"webcrypto-liner.lib": "./src/lib.ts",
},
output: {
filename: "index.js"
library: "liner",
path: path.join(__dirname, "dist"),
filename: "[name].js"
},

@@ -15,9 +21,12 @@ resolve: {

loaders: [
{ test: /\.ts$/, loader: "ts-loader", exclude:path.resolve(__dirname, "node_modules") }
{ test: /\.ts$/, loader: "ts-loader", exclude: path.resolve(__dirname, "node_modules") }
]
},
externals: {
crypto: "require(\"crypto\");",
},
node: {
Buffer: false,
crypto: false,
Buffer: false,
crypto: false
}
}

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc