support AES-CTR 256 JWK keys in legacy crypto for IE11

This commit is contained in:
Bruno Windels 2020-11-11 12:44:59 +01:00
parent fd9eccec4d
commit 8db7499f5a

View File

@ -241,25 +241,73 @@ class AESCrypto {
async generateKey(format, length = 256) { async generateKey(format, length = 256) {
const cryptoKey = await subtleCryptoResult(this._subtleCrypto.generateKey( const cryptoKey = await subtleCryptoResult(this._subtleCrypto.generateKey(
{"name": "AES-CTR", length}, true, ["encrypt", "decrypt"])); {"name": "AES-CTR", length}, true, ["encrypt", "decrypt"]));
return subtleCryptoResult(this._subtleCrypto.exportKey("jwk", cryptoKey)); return subtleCryptoResult(this._subtleCrypto.exportKey(format, cryptoKey));
} }
async generateIV() { async generateIV() {
const randomBytes = this._crypto.getRandomValues(new Uint8Array(8)); return generateIV(this._crypto);
}
}
function generateIV(crypto) {
const randomBytes = crypto.getRandomValues(new Uint8Array(8));
const ivArray = new Uint8Array(16); const ivArray = new Uint8Array(16);
for (let i = 0; i < randomBytes.length; i += 1) { for (let i = 0; i < randomBytes.length; i += 1) {
ivArray[i] = randomBytes[i]; ivArray[i] = randomBytes[i];
} }
return ivArray; return ivArray;
} }
function jwkKeyToRaw(jwkKey) {
if (jwkKey.alg !== "A256CTR") {
throw new Error(`Unknown algorithm: ${jwkKey.alg}`);
}
if (!jwkKey.key_ops.includes("decrypt")) {
throw new Error(`decrypt missing from key_ops`);
}
if (jwkKey.kty !== "oct") {
throw new Error(`Invalid key type, "oct" expected: ${jwkKey.kty}`);
}
// convert base64-url to normal base64
const base64UrlKey = jwkKey.k;
const base64Key = base64UrlKey.replace(/-/g, "+").replace(/_/g, "/");
return base64.decode(base64Key);
} }
function encodeUnpaddedBase64(buffer) {
const str = base64.encode(buffer);
const paddingIdx = str.indexOf("=");
if (paddingIdx !== -1) {
return str.substr(0, paddingIdx);
} else {
return str;
}
}
function encodeUrlBase64(buffer) {
const unpadded = encodeUnpaddedBase64(buffer);
return unpadded.replace(/\+/g, "-").replace(/\//g, "_");
}
function rawKeyToJwk(key) {
return {
"alg": "A256CTR",
"ext": true,
"k": encodeUrlBase64(key),
"key_ops": [
"encrypt",
"decrypt"
],
"kty": "oct"
};
}
import base64 from "../../../../lib/base64-arraybuffer/index.js"; import base64 from "../../../../lib/base64-arraybuffer/index.js";
class AESLegacyCrypto { class AESLegacyCrypto {
constructor(aesjs) { constructor(aesjs, crypto) {
this._aesjs = aesjs; this._aesjs = aesjs;
this._crypto = crypto;
} }
/** /**
* [decrypt description] * [decrypt description]
@ -274,30 +322,39 @@ class AESLegacyCrypto {
throw new Error(`Unsupported counter length: ${counterLength}`); throw new Error(`Unsupported counter length: ${counterLength}`);
} }
if (jwkKey) { if (jwkKey) {
if (jwkKey.alg !== "A256CTR") { key = jwkKeyToRaw(jwkKey);
throw new Error(`Unknown algorithm: ${jwkKey.alg}`);
}
if (!jwkKey.key_ops.includes("decrypt")) {
throw new Error(`decrypt missing from key_ops`);
}
if (jwkKey.kty !== "oct") {
throw new Error(`Invalid key type, "oct" expected: ${jwkKey.kty}`);
}
// convert base64-url to normal base64
const base64UrlKey = jwkKey.k;
const base64Key = base64UrlKey.replace(/-/g, "+").replace(/_/g, "/");
key = base64.decode(base64Key);
} }
const aesjs = this._aesjs; const aesjs = this._aesjs;
var aesCtr = new aesjs.ModeOfOperation.ctr(new Uint8Array(key), new aesjs.Counter(new Uint8Array(iv))); var aesCtr = new aesjs.ModeOfOperation.ctr(new Uint8Array(key), new aesjs.Counter(new Uint8Array(iv)));
return aesCtr.decrypt(new Uint8Array(data)); return aesCtr.decrypt(new Uint8Array(data));
} }
async encryptCTR({key, iv, data}) { async encryptCTR({key, jwkKey, iv, data}) {
if (jwkKey) {
key = jwkKeyToRaw(jwkKey);
}
const aesjs = this._aesjs; const aesjs = this._aesjs;
var aesCtr = new aesjs.ModeOfOperation.ctr(new Uint8Array(key), new aesjs.Counter(new Uint8Array(iv))); var aesCtr = new aesjs.ModeOfOperation.ctr(new Uint8Array(key), new aesjs.Counter(new Uint8Array(iv)));
return aesCtr.encrypt(new Uint8Array(data)); return aesCtr.encrypt(new Uint8Array(data));
} }
/**
* Generate a CTR key
* @param {String} format "raw" or "jwk"
* @param {Number} length 128 or 256
* @return {Promise<Object>} an object for jwk, or a BufferSource for raw
*/
async generateKey(format, length = 256) {
let key = crypto.getRandomValues(new Uint8Array(length / 8));
if (format === "jwk") {
key = rawKeyToJwk(key);
}
return key;
}
async generateIV() {
return generateIV(this._crypto);
}
} }
function hashName(name) { function hashName(name) {