main
Raw Download raw file
  1export class AESECBCipher {
  2    constructor() {
  3        this._key = null;
  4    }
  5
  6    get algorithm() {
  7        return { name: "AES-ECB" };
  8    }
  9
 10    static async importKey(key, _algorithm, extractable, keyUsages) {
 11        const cipher = new AESECBCipher;
 12        await cipher._importKey(key, extractable, keyUsages);
 13        return cipher;
 14    }
 15
 16    async _importKey(key, extractable, keyUsages) {
 17        this._key = await window.crypto.subtle.importKey(
 18            "raw", key, {name: "AES-CBC"}, extractable, keyUsages);
 19    }
 20
 21    async encrypt(_algorithm, plaintext) {
 22        const x = new Uint8Array(plaintext);
 23        if (x.length % 16 !== 0 || this._key === null) {
 24            return null;
 25        }
 26        const n = x.length / 16;
 27        for (let i = 0; i < n; i++) {
 28            const y = new Uint8Array(await window.crypto.subtle.encrypt({
 29                name: "AES-CBC",
 30                iv: new Uint8Array(16),
 31            }, this._key, x.slice(i * 16, i * 16 + 16))).slice(0, 16);
 32            x.set(y, i * 16);
 33        }
 34        return x;
 35    }
 36}
 37
 38export class AESEAXCipher {
 39    constructor() {
 40        this._rawKey = null;
 41        this._ctrKey = null;
 42        this._cbcKey = null;
 43        this._zeroBlock = new Uint8Array(16);
 44        this._prefixBlock0 = this._zeroBlock;
 45        this._prefixBlock1 = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]);
 46        this._prefixBlock2 = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2]);
 47    }
 48
 49    get algorithm() {
 50        return { name: "AES-EAX" };
 51    }
 52
 53    async _encryptBlock(block) {
 54        const encrypted = await window.crypto.subtle.encrypt({
 55            name: "AES-CBC",
 56            iv: this._zeroBlock,
 57        }, this._cbcKey, block);
 58        return new Uint8Array(encrypted).slice(0, 16);
 59    }
 60
 61    async _initCMAC() {
 62        const k1 = await this._encryptBlock(this._zeroBlock);
 63        const k2 = new Uint8Array(16);
 64        const v = k1[0] >>> 6;
 65        for (let i = 0; i < 15; i++) {
 66            k2[i] = (k1[i + 1] >> 6) | (k1[i] << 2);
 67            k1[i] = (k1[i + 1] >> 7) | (k1[i] << 1);
 68        }
 69        const lut = [0x0, 0x87, 0x0e, 0x89];
 70        k2[14] ^= v >>> 1;
 71        k2[15] = (k1[15] << 2) ^ lut[v];
 72        k1[15] = (k1[15] << 1) ^ lut[v >> 1];
 73        this._k1 = k1;
 74        this._k2 = k2;
 75    }
 76
 77    async _encryptCTR(data, counter) {
 78        const encrypted = await window.crypto.subtle.encrypt({
 79            name: "AES-CTR",
 80            counter: counter,
 81            length: 128
 82        }, this._ctrKey, data);
 83        return new Uint8Array(encrypted);
 84    }
 85
 86    async _decryptCTR(data, counter) {
 87        const decrypted = await window.crypto.subtle.decrypt({
 88            name: "AES-CTR",
 89            counter: counter,
 90            length: 128
 91        }, this._ctrKey, data);
 92        return new Uint8Array(decrypted);
 93    }
 94
 95    async _computeCMAC(data, prefixBlock) {
 96        if (prefixBlock.length !== 16) {
 97            return null;
 98        }
 99        const n = Math.floor(data.length / 16);
100        const m = Math.ceil(data.length / 16);
101        const r = data.length - n * 16;
102        const cbcData = new Uint8Array((m + 1) * 16);
103        cbcData.set(prefixBlock);
104        cbcData.set(data, 16);
105        if (r === 0) {
106            for (let i = 0; i < 16; i++) {
107                cbcData[n * 16 + i] ^= this._k1[i];
108            }
109        } else {
110            cbcData[(n + 1) * 16 + r] = 0x80;
111            for (let i = 0; i < 16; i++) {
112                cbcData[(n + 1) * 16 + i] ^= this._k2[i];
113            }
114        }
115        let cbcEncrypted = await window.crypto.subtle.encrypt({
116            name: "AES-CBC",
117            iv: this._zeroBlock,
118        }, this._cbcKey, cbcData);
119
120        cbcEncrypted = new Uint8Array(cbcEncrypted);
121        const mac = cbcEncrypted.slice(cbcEncrypted.length - 32, cbcEncrypted.length - 16);
122        return mac;
123    }
124
125    static async importKey(key, _algorithm, _extractable, _keyUsages) {
126        const cipher = new AESEAXCipher;
127        await cipher._importKey(key);
128        return cipher;
129    }
130
131    async _importKey(key) {
132        this._rawKey = key;
133        this._ctrKey = await window.crypto.subtle.importKey(
134            "raw", key, {name: "AES-CTR"}, false, ["encrypt", "decrypt"]);
135        this._cbcKey = await window.crypto.subtle.importKey(
136            "raw", key, {name: "AES-CBC"}, false, ["encrypt"]);
137        await this._initCMAC();
138    }
139
140    async encrypt(algorithm, message) {
141        const ad = algorithm.additionalData;
142        const nonce = algorithm.iv;
143        const nCMAC = await this._computeCMAC(nonce, this._prefixBlock0);
144        const encrypted = await this._encryptCTR(message, nCMAC);
145        const adCMAC = await this._computeCMAC(ad, this._prefixBlock1);
146        const mac = await this._computeCMAC(encrypted, this._prefixBlock2);
147        for (let i = 0; i < 16; i++) {
148            mac[i] ^= nCMAC[i] ^ adCMAC[i];
149        }
150        const res = new Uint8Array(16 + encrypted.length);
151        res.set(encrypted);
152        res.set(mac, encrypted.length);
153        return res;
154    }
155
156    async decrypt(algorithm, data) {
157        const encrypted = data.slice(0, data.length - 16);
158        const ad = algorithm.additionalData;
159        const nonce = algorithm.iv;
160        const mac = data.slice(data.length - 16);
161        const nCMAC = await this._computeCMAC(nonce, this._prefixBlock0);
162        const adCMAC = await this._computeCMAC(ad, this._prefixBlock1);
163        const computedMac = await this._computeCMAC(encrypted, this._prefixBlock2);
164        for (let i = 0; i < 16; i++) {
165            computedMac[i] ^= nCMAC[i] ^ adCMAC[i];
166        }
167        if (computedMac.length !== mac.length) {
168            return null;
169        }
170        for (let i = 0; i < mac.length; i++) {
171            if (computedMac[i] !== mac[i]) {
172                return null;
173            }
174        }
175        const res = await this._decryptCTR(encrypted, nCMAC);
176        return res;
177    }
178}