UNPKG

mima-kit

Version:

mima-kit is a cryptographic suite implemented in TypeScript. The goal is to provide an easy-to-use cryptographic library. mima-kit 是一个使用 TypeScript 实现的密码学套件。目标是提供一个简单易用的密码学库。

264 lines (263 loc) 9.46 kB
import { ASN1 } from '../../core/asn1'; import { Counter, KitError, U8, getBIBits, joinBuffer } from '../../core/utils'; import { sha256 } from '../../hash/sha256'; import { rsa } from './rsa'; /** * PKCS#1 v2.2 的 掩码生成函数 MGF1 * * Mask Generation Function MGF1 of PKCS#1 v2.2 */ export function mgf1(hash) { return (mdfSeed, maskLen) => { const T = []; const C = new Counter(joinBuffer(mdfSeed, new Uint8Array(4))); for (let i = 0; i < maskLen; i += hash.DIGEST_SIZE) { T.push(hash(C)); C.inc(mdfSeed.length); } return joinBuffer(...T).slice(0, maskLen); }; } // * Encryption Scheme /** * 最优非对称加密填充的 RSA 加密方案 (OAEP) * * RSA Encryption Scheme with Optimal Asymmetric Encryption Padding (OAEP) * * @param {RSAPublicKey | RSAPrivateKey} key - RSA 公钥或私钥 / RSA public or private key * @param {Hash} [hash] - 散列函数 / Hash function (default: SHA-256) * @param {MGF} [mgf] - 掩码生成函数 / Mask generation function (default: MGF1) * @param {Uint8Array} [label] - 标签 / Label (default: empty) */ export function pkcs1_es_oaep(key, hash = sha256, mgf = mgf1(hash), label = new Uint8Array()) { const k = (getBIBits(key.n) + 7) >> 3; const hLen = hash.DIGEST_SIZE; const MAX_MESSAGE_LENGTH = k - 2 * hLen - 2; if (MAX_MESSAGE_LENGTH <= 0) { throw new KitError('Invalid key or hash function'); } const _rsa = rsa(key); const lHash = hash(label); const encrypt = (M) => { const mLen = M.length; if (mLen > MAX_MESSAGE_LENGTH) { throw new KitError('Message too long'); } // DB = lHash || PS || 0x01 || M const PS = new U8(MAX_MESSAGE_LENGTH - mLen); const DB = joinBuffer(lHash, PS, new U8([0x01]), M); // EM = 0x00 || maskedSeed || maskedDB const seed = new U8(hLen); crypto.getRandomValues(seed); const dbMask = mgf(seed, DB.length); const maskedDB = DB.map((v, i) => v ^ dbMask[i]); const seedMask = mgf(maskedDB, hLen); const maskedSeed = seed.map((v, i) => v ^ seedMask[i]); const EM = joinBuffer(new U8([0x00]), maskedSeed, maskedDB); return U8.fromBI(_rsa.encrypt(EM), k); }; const decrypt = (C) => { if (k !== C.length) { throw new KitError('Decryption error'); } const EM = U8.fromBI(_rsa.decrypt(C), k); if (EM[0] !== 0x00) { throw new KitError('Decryption error'); } const maskedSeed = EM.subarray(1, hLen + 1); const maskedDB = EM.subarray(hLen + 1); const seedMask = mgf(maskedDB, hLen); const seed = maskedSeed.map((v, i) => v ^ seedMask[i]); const dbMask = mgf(seed, maskedDB.length); const DB = maskedDB.map((v, i) => v ^ dbMask[i]); const lHash_ = DB.subarray(0, hLen); if (lHash.some((v, i) => v !== lHash_[i])) { throw new KitError('Decryption error'); } const PS = DB.subarray(hLen); const mOffset = PS.findIndex(v => v === 0x01); if (mOffset === -1) { throw new KitError('Decryption error'); } if (PS.subarray(0, mOffset).some(v => v !== 0x00)) { throw new KitError('Decryption error'); } const M = PS.slice(mOffset + 1); return M; }; return { encrypt, decrypt }; } /** * RSA 加密方案 (PKCS#1 v1.5) * * RSA Encryption Scheme (PKCS#1 v1.5) * * @param {RSAPublicKey | RSAPrivateKey} key - RSA 公钥或私钥 / RSA public or private key */ export function pkcs1_es_1_5(key) { const k = (getBIBits(key.n) + 7) >> 3; const MAX_MESSAGE_LENGTH = k - 11; if (MAX_MESSAGE_LENGTH <= 0) { throw new KitError('Invalid key'); } const _rsa = rsa(key); const encrypt = (M) => { if (M.length > MAX_MESSAGE_LENGTH) { throw new KitError('Message is too long'); } const PS = new Uint8Array(k - M.length - 3); do { crypto.getRandomValues(PS); } while (PS.includes(0x00)); const EM = joinBuffer(new U8([0x00, 0x02]), PS, new U8([0x00]), M); return U8.fromBI(_rsa.encrypt(EM), k); }; const decrypt = (C) => { if (C.length !== k) { throw new KitError('Decryption error'); } const EM = U8.fromBI(_rsa.decrypt(C), k); if (EM[0] !== 0x00 || EM[1] !== 0x02) { throw new KitError('Decryption error'); } const SeparatorIndex = EM.subarray(2).findIndex(v => v === 0x00); if (SeparatorIndex === -1) { throw new KitError('Decryption error'); } const M = EM.slice(SeparatorIndex + 3); return M; }; return { encrypt, decrypt }; } // * Signature Scheme with Appendix /** * 基于 概率签名方案 的 RSA 附录签名方案 (PSS) * * RSA Signature Scheme with Appendix - Probabilistic Signature Scheme (PSS) * * @param {RSAPublicKey | RSAPrivateKey} key - RSA 公钥或私钥 / RSA public or private key * @param {Hash} [hash] - 散列函数 / Hash function (default: SHA-256) * @param {MGF} [mgf] - 掩码生成函数 / Mask generation function (default: MGF1) * @param {number} [sLen] - 盐长度 / Salt length (default: hash.DIGEST_SIZE) */ export function pkcs1_ssa_pss(key, hash = sha256, mgf = mgf1(hash), sLen = hash.DIGEST_SIZE) { const modBits = getBIBits(key.n); const k = (modBits + 7) >> 3; const emLen = (modBits + 6) >> 3; const emsa = emsa_pss(hash, mgf, sLen); const _rsa = rsa(key); const sign = (M) => { const EM = emsa.encode(M, modBits - 1); const S = _rsa.sign(EM); return U8.fromBI(S, k); }; const verify = (M, S) => { if (S.length !== k) { return false; } const EM = U8.fromBI(_rsa.verify(S), emLen); return emsa.verify(M, EM, modBits - 1); }; return { sign, verify }; } /** * RSA 附录签名方案 (PKCS#1 v1.5) * * RSA Signature Scheme with Appendix (PKCS#1 v1.5) * * @param {RSAPublicKey | RSAPrivateKey} key - RSA 公钥或私钥 / RSA public or private key * @param {Hash} [hash] - 散列函数 / Hash function (default: SHA-256) */ export function pkcs1_ssa_1_5(key, hash = sha256) { const modBits = getBIBits(key.n); const k = (modBits + 7) >> 3; const _rsa = rsa(key); const sign = (M) => { const EM = emsa_1_5(M, k, hash); const S = _rsa.sign(EM); return U8.fromBI(S, k); }; const verify = (M, S) => { if (S.length !== k) { return false; } const EM = U8.fromBI(_rsa.verify(S), k); const EM2 = emsa_1_5(M, k, hash); return EM.every((v, i) => v === EM2[i]); }; return { sign, verify }; } // * Encoding Method for Signatures with Appendix function emsa_pss(hash = sha256, mgf = mgf1(hash), sLen = hash.DIGEST_SIZE) { const hLen = hash.DIGEST_SIZE; const encode = (M, emBits) => { const mHash = hash(M); const emLen = (emBits + 7) >> 3; if (emLen < hLen + sLen + 2) { throw new KitError('Encoding error'); } const salt = new U8(sLen); crypto.getRandomValues(salt); const M2 = joinBuffer(new U8(8), mHash, salt); const H = hash(M2); const PS = new U8(emLen - sLen - hLen - 2); const DB = joinBuffer(PS, new U8([0x01]), salt); const dbMask = mgf(H, DB.length); const maskedDB = DB.map((v, i) => v ^ dbMask[i]); const bitMask = 0xFF >> (emLen << 3) - emBits; maskedDB[0] &= bitMask; const EM = joinBuffer(maskedDB, H, new U8([0xBC])); return EM; }; const verify = (M, EM, emBits) => { const mHash = hash(M); const emLen = (emBits + 7) >> 3; if (emLen !== EM.length || emLen < hLen + sLen + 2) { return false; } if (EM[emLen - 1] !== 0xBC) { return false; } const maskedDB = EM.subarray(0, emLen - hLen - 1); const H = EM.subarray(maskedDB.length, emLen - 1); const bitMask = 0xFF >> (emLen << 3) - emBits; if (maskedDB[0] > bitMask) { return false; } const dbMask = mgf(H, maskedDB.length); const DB = maskedDB.map((v, i) => v ^ dbMask[i]); DB[0] &= bitMask; const PS = DB.subarray(0, DB.length - sLen - 1); if (PS.some(v => v !== 0x00)) { return false; } if (DB[PS.length] !== 0x01) { return false; } const salt = DB.subarray(PS.length + 1); const M2 = joinBuffer(new U8(8), mHash, salt); const H2 = hash(M2); return H2.every((v, i) => v === H[i]); }; return { encode, verify }; } function emsa_1_5(M, emLen, hash = sha256) { const H = hash(M); const digestAlgorithm = ASN1.SEQUENCE([ ASN1.OBJECT_IDENTIFIER(hash.OID), ASN1.NULL(), ]); const digest = ASN1.OCTET_STRING(H); const DigestInfo = ASN1.SEQUENCE([ digestAlgorithm, digest, ]); const T = DigestInfo; const psLen = emLen - T.length - 3; if (psLen < 8) { throw new KitError('intended encoded message length too short'); } const PS = new U8(psLen).fill(0xFF); const EM = joinBuffer(new U8([0x00, 0x01]), PS, new U8([0x00]), T); return EM; }