| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178 |
- export class AESECBCipher {
- constructor() {
- this._key = null;
- }
- get algorithm() {
- return { name: "AES-ECB" };
- }
- static async importKey(key, _algorithm, extractable, keyUsages) {
- const cipher = new AESECBCipher;
- await cipher._importKey(key, extractable, keyUsages);
- return cipher;
- }
- async _importKey(key, extractable, keyUsages) {
- this._key = await window.crypto.subtle.importKey(
- "raw", key, {name: "AES-CBC"}, extractable, keyUsages);
- }
- async encrypt(_algorithm, plaintext) {
- const x = new Uint8Array(plaintext);
- if (x.length % 16 !== 0 || this._key === null) {
- return null;
- }
- const n = x.length / 16;
- for (let i = 0; i < n; i++) {
- const y = new Uint8Array(await window.crypto.subtle.encrypt({
- name: "AES-CBC",
- iv: new Uint8Array(16),
- }, this._key, x.slice(i * 16, i * 16 + 16))).slice(0, 16);
- x.set(y, i * 16);
- }
- return x;
- }
- }
- export class AESEAXCipher {
- constructor() {
- this._rawKey = null;
- this._ctrKey = null;
- this._cbcKey = null;
- this._zeroBlock = new Uint8Array(16);
- this._prefixBlock0 = this._zeroBlock;
- this._prefixBlock1 = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]);
- this._prefixBlock2 = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2]);
- }
- get algorithm() {
- return { name: "AES-EAX" };
- }
- async _encryptBlock(block) {
- const encrypted = await window.crypto.subtle.encrypt({
- name: "AES-CBC",
- iv: this._zeroBlock,
- }, this._cbcKey, block);
- return new Uint8Array(encrypted).slice(0, 16);
- }
- async _initCMAC() {
- const k1 = await this._encryptBlock(this._zeroBlock);
- const k2 = new Uint8Array(16);
- const v = k1[0] >>> 6;
- for (let i = 0; i < 15; i++) {
- k2[i] = (k1[i + 1] >> 6) | (k1[i] << 2);
- k1[i] = (k1[i + 1] >> 7) | (k1[i] << 1);
- }
- const lut = [0x0, 0x87, 0x0e, 0x89];
- k2[14] ^= v >>> 1;
- k2[15] = (k1[15] << 2) ^ lut[v];
- k1[15] = (k1[15] << 1) ^ lut[v >> 1];
- this._k1 = k1;
- this._k2 = k2;
- }
- async _encryptCTR(data, counter) {
- const encrypted = await window.crypto.subtle.encrypt({
- name: "AES-CTR",
- counter: counter,
- length: 128
- }, this._ctrKey, data);
- return new Uint8Array(encrypted);
- }
- async _decryptCTR(data, counter) {
- const decrypted = await window.crypto.subtle.decrypt({
- name: "AES-CTR",
- counter: counter,
- length: 128
- }, this._ctrKey, data);
- return new Uint8Array(decrypted);
- }
- async _computeCMAC(data, prefixBlock) {
- if (prefixBlock.length !== 16) {
- return null;
- }
- const n = Math.floor(data.length / 16);
- const m = Math.ceil(data.length / 16);
- const r = data.length - n * 16;
- const cbcData = new Uint8Array((m + 1) * 16);
- cbcData.set(prefixBlock);
- cbcData.set(data, 16);
- if (r === 0) {
- for (let i = 0; i < 16; i++) {
- cbcData[n * 16 + i] ^= this._k1[i];
- }
- } else {
- cbcData[(n + 1) * 16 + r] = 0x80;
- for (let i = 0; i < 16; i++) {
- cbcData[(n + 1) * 16 + i] ^= this._k2[i];
- }
- }
- let cbcEncrypted = await window.crypto.subtle.encrypt({
- name: "AES-CBC",
- iv: this._zeroBlock,
- }, this._cbcKey, cbcData);
- cbcEncrypted = new Uint8Array(cbcEncrypted);
- const mac = cbcEncrypted.slice(cbcEncrypted.length - 32, cbcEncrypted.length - 16);
- return mac;
- }
- static async importKey(key, _algorithm, _extractable, _keyUsages) {
- const cipher = new AESEAXCipher;
- await cipher._importKey(key);
- return cipher;
- }
- async _importKey(key) {
- this._rawKey = key;
- this._ctrKey = await window.crypto.subtle.importKey(
- "raw", key, {name: "AES-CTR"}, false, ["encrypt", "decrypt"]);
- this._cbcKey = await window.crypto.subtle.importKey(
- "raw", key, {name: "AES-CBC"}, false, ["encrypt"]);
- await this._initCMAC();
- }
- async encrypt(algorithm, message) {
- const ad = algorithm.additionalData;
- const nonce = algorithm.iv;
- const nCMAC = await this._computeCMAC(nonce, this._prefixBlock0);
- const encrypted = await this._encryptCTR(message, nCMAC);
- const adCMAC = await this._computeCMAC(ad, this._prefixBlock1);
- const mac = await this._computeCMAC(encrypted, this._prefixBlock2);
- for (let i = 0; i < 16; i++) {
- mac[i] ^= nCMAC[i] ^ adCMAC[i];
- }
- const res = new Uint8Array(16 + encrypted.length);
- res.set(encrypted);
- res.set(mac, encrypted.length);
- return res;
- }
- async decrypt(algorithm, data) {
- const encrypted = data.slice(0, data.length - 16);
- const ad = algorithm.additionalData;
- const nonce = algorithm.iv;
- const mac = data.slice(data.length - 16);
- const nCMAC = await this._computeCMAC(nonce, this._prefixBlock0);
- const adCMAC = await this._computeCMAC(ad, this._prefixBlock1);
- const computedMac = await this._computeCMAC(encrypted, this._prefixBlock2);
- for (let i = 0; i < 16; i++) {
- computedMac[i] ^= nCMAC[i] ^ adCMAC[i];
- }
- if (computedMac.length !== mac.length) {
- return null;
- }
- for (let i = 0; i < mac.length; i++) {
- if (computedMac[i] !== mac[i]) {
- return null;
- }
- }
- const res = await this._decryptCTR(encrypted, nCMAC);
- return res;
- }
- }
|