mirror of
https://github.com/vector-im/hydrogen-web.git
synced 2024-12-23 11:35:04 +01:00
support AES-CTR 256 JWK keys in legacy crypto for IE11
This commit is contained in:
parent
fd9eccec4d
commit
8db7499f5a
@ -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) {
|
||||||
|
Loading…
Reference in New Issue
Block a user