SPB Git forge

spb/admin-ka

Public
41commits 1branches 0releases
172.9 MBsize
maindefault branch
20 days agolast push
JavaScript 65.5% Python 17.8% CSS 13% HTML 3.7%
4.4 KB · 133 lines javascript
Raw Blame History
1import Base64 from "../base64.js";2import { modPow, bigIntToU8Array, u8ArrayToBigInt } from "./bigint.js";34export class RSACipher {5    constructor() {6        this._keyLength = 0;7        this._keyBytes = 0;8        this._n = null;9        this._e = null;10        this._d = null;11        this._nBigInt = null;12        this._eBigInt = null;13        this._dBigInt = null;14        this._extractable = false;15    }1617    get algorithm() {18        return { name: "RSA-PKCS1-v1_5" };19    }2021    _base64urlDecode(data) {22        data = data.replace(/-/g, "+").replace(/_/g, "/");23        data = data.padEnd(Math.ceil(data.length / 4) * 4, "=");24        return Base64.decode(data);25    }2627    _padArray(arr, length) {28        const res = new Uint8Array(length);29        res.set(arr, length - arr.length);30        return res;31    }3233    static async generateKey(algorithm, extractable, _keyUsages) {34        const cipher = new RSACipher;35        await cipher._generateKey(algorithm, extractable);36        return { privateKey: cipher };37    }3839    async _generateKey(algorithm, extractable) {40        this._keyLength = algorithm.modulusLength;41        this._keyBytes = Math.ceil(this._keyLength / 8);42        const key = await window.crypto.subtle.generateKey(43            {44                name: "RSA-OAEP",45                modulusLength: algorithm.modulusLength,46                publicExponent: algorithm.publicExponent,47                hash: {name: "SHA-256"},48            },49            true, ["encrypt", "decrypt"]);50        const privateKey = await window.crypto.subtle.exportKey("jwk", key.privateKey);51        this._n = this._padArray(this._base64urlDecode(privateKey.n), this._keyBytes);52        this._nBigInt = u8ArrayToBigInt(this._n);53        this._e = this._padArray(this._base64urlDecode(privateKey.e), this._keyBytes);54        this._eBigInt = u8ArrayToBigInt(this._e);55        this._d = this._padArray(this._base64urlDecode(privateKey.d), this._keyBytes);56        this._dBigInt = u8ArrayToBigInt(this._d);57        this._extractable = extractable;58    }5960    static async importKey(key, _algorithm, extractable, keyUsages) {61        if (keyUsages.length !== 1 || keyUsages[0] !== "encrypt") {62            throw new Error("only support importing RSA public key");63        }64        const cipher = new RSACipher;65        await cipher._importKey(key, extractable);66        return cipher;67    }6869    async _importKey(key, extractable) {70        const n = key.n;71        const e = key.e;72        if (n.length !== e.length) {73            throw new Error("the sizes of modulus and public exponent do not match");74        }75        this._keyBytes = n.length;76        this._keyLength = this._keyBytes * 8;77        this._n = new Uint8Array(this._keyBytes);78        this._e = new Uint8Array(this._keyBytes);79        this._n.set(n);80        this._e.set(e);81        this._nBigInt = u8ArrayToBigInt(this._n);82        this._eBigInt = u8ArrayToBigInt(this._e);83        this._extractable = extractable;84    }8586    async encrypt(_algorithm, message) {87        if (message.length > this._keyBytes - 11) {88            return null;89        }90        const ps = new Uint8Array(this._keyBytes - message.length - 3);91        window.crypto.getRandomValues(ps);92        for (let i = 0; i < ps.length; i++) {93            ps[i] = Math.floor(ps[i] * 254 / 255 + 1);94        }95        const em = new Uint8Array(this._keyBytes);96        em[1] = 0x02;97        em.set(ps, 2);98        em.set(message, ps.length + 3);99        const emBigInt = u8ArrayToBigInt(em);100        const c = modPow(emBigInt, this._eBigInt, this._nBigInt);101        return bigIntToU8Array(c, this._keyBytes);102    }103104    async decrypt(_algorithm, message) {105        if (message.length !== this._keyBytes) {106            return null;107        }108        const msgBigInt = u8ArrayToBigInt(message);109        const emBigInt = modPow(msgBigInt, this._dBigInt, this._nBigInt);110        const em = bigIntToU8Array(emBigInt, this._keyBytes);111        if (em[0] !== 0x00 || em[1] !== 0x02) {112            return null;113        }114        let i = 2;115        for (; i < em.length; i++) {116            if (em[i] === 0x00) {117                break;118            }119        }120        if (i === em.length) {121            return null;122        }123        return em.slice(i + 1, em.length);124    }125126    async exportKey() {127        if (!this._extractable) {128            throw new Error("key is not extractable");129        }130        return { n: this._n, e: this._e, d: this._d };131    }132}133