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 实现的密码学套件。目标是提供一个简单易用的密码学库。

449 lines (448 loc) 15.5 kB
import { KitError, trying, U8, u8, wrap } from './utils'; function createCodec(parse, stringify, format) { function codec(input) { return typeof input === 'string' ? parse(input) : stringify(input); } return wrap(codec, { FORMAT: format }); } function UTF8ToU8(input) { /** * 尝试使用 TextEncoder 编码 * 否则使用自定义编码器 */ const [error, result] = trying(() => new TextEncoder().encode(input)); if (!error) return u8(result); /** provided by xingluo233 */ const buffer = []; for (let i = 0; i < input.length; i++) { const char_code = input.codePointAt(i); if (char_code === undefined) { } else if (char_code < 0x80) { buffer.push(char_code); } else if (char_code < 0x800) { buffer.push(0xc0 | (char_code >> 6)); buffer.push(0x80 | (char_code & 0x3f)); } else if (char_code < 0x10000) { buffer.push(0xe0 | (char_code >> 12)); buffer.push(0x80 | ((char_code >> 6) & 0x3f)); buffer.push(0x80 | (char_code & 0x3f)); } else if (char_code < 0x110000) { buffer.push(0xf0 | (char_code >> 18)); buffer.push(0x80 | ((char_code >> 12) & 0x3f)); buffer.push(0x80 | ((char_code >> 6) & 0x3f)); buffer.push(0x80 | (char_code & 0x3f)); i++; } } return U8.from(buffer); } function U8ToUTF8(input) { /** * 尝试使用 TextDecoder 解码 * 否则使用自定义解码器 */ const [error, result] = trying(() => new TextDecoder().decode(input)); if (!error) return result; /** provided by xingluo233 */ const str = []; for (let i = 0; i < input.length;) { const byte1 = input[i++]; if (byte1 < 0x80) { str.push(String.fromCharCode(byte1)); } else if (byte1 >= 0xc0 && byte1 < 0xe0) { const byte2 = input[i++]; const char_code = ((byte1 & 0x1f) << 6) | (byte2 & 0x3f); str.push(String.fromCharCode(char_code)); } else if (byte1 >= 0xe0 && byte1 < 0xf0) { const byte2 = input[i++]; const byte3 = input[i++]; const char_code = ((byte1 & 0x0f) << 12) | ((byte2 & 0x3f) << 6) | (byte3 & 0x3f); str.push(String.fromCharCode(char_code)); } else if (byte1 >= 0xf0 && byte1 < 0xf8) { const byte2 = input[i++]; const byte3 = input[i++]; const byte4 = input[i++]; const char_code = ((byte1 & 0x07) << 18) | ((byte2 & 0x3f) << 12) | ((byte3 & 0x3f) << 6) | (byte4 & 0x3f); str.push(String.fromCodePoint(char_code)); } else { console.warn('Included an invalid UTF-8 byte'); } } return str.join(''); } /** UTF-8 编解码器 / Codec */ export const UTF8 = createCodec(UTF8ToU8, U8ToUTF8, 'utf-8'); function HEXToU8(input) { input = input.replace(/^0x/, ''); // eslint-disable-next-line node/prefer-global/buffer const [error, result] = trying(() => Buffer.from(input.replace(/[^0-9a-f]/gi, ''), 'hex')); if (!error) return u8(result); const arr = input.match(/[\da-f]{2}/gi); if (arr == null) { return new U8(); } return new U8(arr.map((h) => Number.parseInt(h, 16))); } function U8ToHEX(input) { // eslint-disable-next-line node/prefer-global/buffer const [error, result] = trying(() => Buffer.from(input).toString('hex')); if (!error) return result; let str = ''; for (let i = 0; i < input.length; i++) { str += input[i].toString(16).padStart(2, '0'); } return str; } /** hex 编解码器 / Codec */ export const HEX = createCodec(HEXToU8, U8ToHEX, 'hex'); function B64ToU8(input) { // eslint-disable-next-line node/prefer-global/buffer const [error, result] = trying(() => Buffer.from(input.replace(/[^A-Z0-9+/]/gi, ''), 'base64')); if (!error) return u8(result); return B64CommonParse(input); } function U8ToB64(input) { // eslint-disable-next-line node/prefer-global/buffer const [error, result] = trying(() => Buffer.from(input).toString('base64')); if (!error) return result; return B64CommonStringify(input, false); } /** base64 编解码器 / Codec */ export const B64 = createCodec(B64ToU8, U8ToB64, 'base64'); function B64URLToU8(input) { // eslint-disable-next-line node/prefer-global/buffer const [error, result] = trying(() => Buffer.from(input.replace(/[^\w-]/g, ''), 'base64url')); if (!error) return u8(result); return B64CommonParse(input); } function U8ToB64URL(input) { // eslint-disable-next-line node/prefer-global/buffer const [error, result] = trying(() => Buffer.from(input).toString('base64url')); if (!error) return result; return B64(input).replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, ''); } /** base64url 编解码器 / Codec */ export const B64URL = createCodec(B64URLToU8, U8ToB64URL, 'base64url'); /** * provided by xingluo233 * * B64CommonParse can parse B64 or B64url string to Uint8Array * * B64CommonParse 可以将 B64 或者 B64url 字符串解析为 Uint8Array * * @param {string} input - B64 或 B64url 字符串 */ function B64CommonParse(input) { const map = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; input = input .replace(/-/g, '+') .replace(/_/g, '/') .replace(/[^A-Z0-9+/]/gi, ''); const length = input.length * 0.75; const result = new U8(length); let i = 0; let j = 0; while (i < input.length) { const a = map.indexOf(input.charAt(i++)); const b = map.indexOf(input.charAt(i++)); const c = map.indexOf(input.charAt(i++)); const d = map.indexOf(input.charAt(i++)); const combined = (a << 18) | (b << 12) | (c << 6) | d; result[j++] = (combined >> 16) & 0xff; result[j++] = (combined >> 8) & 0xff; result[j++] = combined & 0xff; } return result; } /** * B64CommonStringify can stringify Uint8Array to B64 or B64url string * * B64CommonStringify 可以将 Uint8Array 编码为 B64 或 B64url 字符串 * * @param {Uint8Array} input - Uint8Array * @param {boolean} url - 是否是 B64url 字符串 */ function B64CommonStringify(input, url) { let map = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; map += url ? '-_' : '+/'; let result = ''; let i; for (i = 0; i < input.length - 2; i += 3) { result += map[input[i] >> 2]; result += map[((input[i] & 3) << 4) | (input[i + 1] >> 4)]; result += map[((input[i + 1] & 15) << 2) | (input[i + 2] >> 6)]; result += map[input[i + 2] & 63]; } if (i === input.length - 2) { result += map[input[i] >> 2]; result += map[((input[i] & 3) << 4) | (input[i + 1] >> 4)]; result += map[(input[i + 1] & 15) << 2]; result += url ? '' : '='; } else if (i === input.length - 1) { result += map[input[i] >> 2]; result += map[(input[i] & 3) << 4]; result += url ? '' : '=='; } return result; } function TheB32Codec(args) { // 使用默认配置处理 B32 编解码 const RFC4648_B32_MAP = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'; if (typeof args === 'string') { const input = args.toUpperCase().replace(/[^A-Z2-7]/g, ''); return B32CommonParse(input, RFC4648_B32_MAP); } else if (args instanceof Uint8Array) { return B32CommonStringify(args, RFC4648_B32_MAP, false); } // 创建 B32 变体 const { variant = 'rfc4648', padding = false } = args; if (variant === 'rfc4648') { function B32ToU8(input) { input = input.toUpperCase().replace(/[^A-Z2-7]/g, ''); return B32CommonParse(input, RFC4648_B32_MAP); } function U8ToB32(input) { return B32CommonStringify(input, RFC4648_B32_MAP, padding); } return createCodec(B32ToU8, U8ToB32, 'base32'); } else if (variant === 'rfc4648-hex') { const RFC4648_B32_HEX_MAP = '0123456789ABCDEFGHIJKLMNOPQRSTUV'; function B32HexToU8(input) { input = input.toUpperCase().replace(/[^0-9A-V]/g, ''); return B32CommonParse(input, RFC4648_B32_HEX_MAP); } function U8ToB32Hex(input) { return B32CommonStringify(input, RFC4648_B32_HEX_MAP, padding); } return createCodec(B32HexToU8, U8ToB32Hex, 'base32-hex'); } else if (variant === 'crockford') { const RFC4648_B32_CROCKFORD_MAP = '0123456789ABCDEFGHJKMNPQRSTVWXYZ'; function B32CrockfordToU8(input) { input = input .toUpperCase() .replace(/O/g, '0') .replace(/[IL]/g, '1') .replace(/[^0-9A-HJKMNP-TV-Z]/g, ''); return B32CommonParse(input, RFC4648_B32_CROCKFORD_MAP); } function U8ToB32Crockford(input) { return B32CommonStringify(input, RFC4648_B32_CROCKFORD_MAP, padding); } return createCodec(B32CrockfordToU8, U8ToB32Crockford, 'base32-crockford'); } } /** base32 编解码器 / Codec */ export const B32 = wrap(TheB32Codec, { FORMAT: 'base32' }); /** * B32CommonParse can parse B32 string to Uint8Array * * B32CommonParse 可以将 B32 字符串解析为 Uint8Array * * @param {string} input - B32 或 B32url 字符串 * @param {string} map - 字符表 */ function B32CommonParse(input, map) { const length = input.length * 0.625; const result = new U8(length); let i = 0; let j = 0; while (i < input.length) { const a = map.indexOf(input.charAt(i++)); const b = map.indexOf(input.charAt(i++)); const c = map.indexOf(input.charAt(i++)); const d = map.indexOf(input.charAt(i++)); const e = map.indexOf(input.charAt(i++)); const f = map.indexOf(input.charAt(i++)); const g = map.indexOf(input.charAt(i++)); const h = map.indexOf(input.charAt(i++)); result[j++] = (a << 3) | (b >> 2); result[j++] = ((b & 0b11) << 6) | (c << 1) | (d >> 4); result[j++] = ((d & 0b1111) << 4) | (e >> 1); result[j++] = ((e & 0b1) << 7) | (f << 2) | (g >> 3); result[j++] = ((g & 0b111) << 5) | h; } return result; } /** * B32CommonStringify can stringify Uint8Array to B32 string * * B32CommonStringify 可以将 Uint8Array 编码为 B32 字符串 * * @param {Uint8Array} input - Uint8Array * @param {string} map - 字符表 * @param {boolean} pad - 是否填充 */ function B32CommonStringify(input, map, pad) { let result = ''; let i; for (i = 0; i < input.length - 4; i += 5) { const E0 = input[i]; const E1 = input[i + 1]; const E2 = input[i + 2]; const E3 = input[i + 3]; const E4 = input[i + 4]; result += map[E0 >> 3]; result += map[((E0 & 0b111) << 2) | (E1 >> 6)]; result += map[(E1 >> 1) & 0b11111]; result += map[((E1 & 0b1) << 4) | (E2 >> 4)]; result += map[((E2 & 0b1111) << 1) | (E3 >> 7)]; result += map[(E3 >> 2) & 0b11111]; result += map[((E3 & 0b11) << 3) | (E4 >> 5)]; result += map[E4 & 0b11111]; } if (i === input.length - 4) { const E0 = input[i]; const E1 = input[i + 1]; const E2 = input[i + 2]; const E3 = input[i + 3]; result += map[E0 >> 3]; result += map[((E0 & 0b111) << 2) | (E1 >> 6)]; result += map[(E1 >> 1) & 0b11111]; result += map[((E1 & 0b1) << 4) | (E2 >> 4)]; result += map[((E2 & 0b1111) << 1) | (E3 >> 7)]; result += map[(E3 >> 2) & 0b11111]; result += map[(E3 & 0b11) << 3]; result += pad ? '=' : ''; } else if (i === input.length - 3) { const E0 = input[i]; const E1 = input[i + 1]; const E2 = input[i + 2]; result += map[E0 >> 3]; result += map[((E0 & 0b111) << 2) | (E1 >> 6)]; result += map[(E1 >> 1) & 0b11111]; result += map[((E1 & 0b1) << 4) | (E2 >> 4)]; result += map[(E2 & 0b1111) << 1]; result += pad ? '===' : ''; } else if (i === input.length - 2) { const E0 = input[i]; const E1 = input[i + 1]; result += map[E0 >> 3]; result += map[((E0 & 0b111) << 2) | (E1 >> 6)]; result += map[(E1 >> 1) & 0b11111]; result += map[(E1 & 0b1) << 4]; result += pad ? '====' : ''; } else if (i === input.length - 1) { const E0 = input[i]; result += map[E0 >> 3]; result += map[(E0 & 0b111) << 2]; result += pad ? '======' : ''; } return result; } function CSVToU8(input) { const coreValueMap = new Map(); coreValueMap.set('富强', 0); coreValueMap.set('民主', 1); coreValueMap.set('文明', 2); coreValueMap.set('和谐', 3); coreValueMap.set('自由', 4); coreValueMap.set('平等', 5); coreValueMap.set('公正', 6); coreValueMap.set('法治', 7); coreValueMap.set('爱国', 8); coreValueMap.set('敬业', 9); coreValueMap.set('诚信', 10); coreValueMap.set('友善', 11); const from = (value) => { const nibble = coreValueMap.get(value); if (nibble === undefined) { throw new KitError('你竟然在社会主义核心价值观里夹带私货!'); } return nibble; }; const coreValues = input.match(/(\S){2}/g); if (coreValues == null) return new U8(); let h = 0; let l = 0; let count = 0; const result = []; for (let i = 0; i < coreValues.length; i++) { const isHigh = count % 2 === 0; let nibble = from(coreValues[i]); if (nibble === 10 || nibble === 11) { i++; if (i === coreValues.length) { throw new KitError('你的社会主义核心价值观破碎了!'); } nibble = nibble === 10 ? 10 + from(coreValues[i]) : 6 + from(coreValues[i]); } if (isHigh) { h = nibble; } else { l = nibble; } if (!isHigh) { result.push(((h << 4) | l) & 0xff); } count++; } return new U8(result); } function U8ToCSV(input) { const rand = () => Math.random() >= 0.5; const map = [ '富强', '民主', '文明', '和谐', '自由', '平等', '公正', '法治', '爱国', '敬业', '诚信', '友善', ]; let result = ''; input.forEach((byte) => { const h = (byte >> 4) & 0xf; const l = byte & 0xf; if (h < 10) { result += map[h]; } else if (rand()) { result += map[11] + map[h - 6]; } else { result += map[11] + map[h - 6]; } if (l < 10) { result += map[l]; } else if (rand()) { result += map[10] + map[l - 10]; } else { result += map[11] + map[l - 6]; } }); return result; } /** 社会主义核心价值观编解码器 / Core Socialist Values Codec */ export const CSV = createCodec(CSVToU8, U8ToCSV, 'core-socialist-values');