UNPKG

devexpress-richedit

Version:

DevExpress Rich Text Editor is an advanced word-processing tool designed for working with rich text documents.

229 lines (228 loc) 9.25 kB
import { RichNumberConverter } from '../../utils/number-converter'; import { MathUtils } from '@devexpress/utils/lib/utils/math'; import { StringUtils } from '@devexpress/utils/lib/utils/string'; import { HashAlgorithmType } from '../options/document-protection'; import { MD5 } from './md5'; import { RIPEMD160 } from './ripemd160'; import { SHA1 } from './sha1'; import { SHA256 } from './sha256'; import { SHA384 } from './sha384'; import { SHA512 } from './sha512'; export class PasswordHashCodeCalculator { getBytes(val) { return new Uint8Array(new Uint32Array([val]).buffer); } calculateLegacyPasswordHash(password) { if (StringUtils.isNullOrEmpty(password)) return null; return this.getBytes(this.calculateLegacyPasswordHashInt(password)); } calculateLegacyPasswordHashInt(password) { if (StringUtils.isNullOrEmpty(password)) return 0; const bytes = this.calculatePasswordBytes(password); const high = this.calculateKeyHighWord(bytes); const low = this.calculateKeyLowWord(bytes); return (((low << 24) & 0xFF000000) | ((low << 8) & 0x00FF0000) | ((high << 8) & 0x0000FF00) | ((high >> 8) & 0x000000FF)); } calculatePasswordHash(password, prefix, hashCount, hashAlgorithmType) { const hashAlgorithm = this.createHashAlgorithm(hashAlgorithmType); if (hashAlgorithm) return this.calculatePasswordHashAfterDetectedAlgoritm(password, prefix, hashCount, hashAlgorithm); else { const legacyPasswordHash = this.calculateLegacyPasswordHashInt(password); return this.concatenate(new Uint8Array(0), legacyPasswordHash); } } generatePasswordPrefix(length) { const result = []; for (let i = 0; i < length; i++) { result[i] = MathUtils.getRandomInt(0, 1000); } return new Uint8Array(result); } createHashAlgorithm(hashAlgorithmType) { switch (hashAlgorithmType) { case HashAlgorithmType.Sha1: return new SHA1(); case HashAlgorithmType.Sha256: return new SHA256(); case HashAlgorithmType.Sha384: return new SHA384(); case HashAlgorithmType.Sha512: return new SHA512(); case HashAlgorithmType.Md5: return new MD5(); case HashAlgorithmType.Ripemd160: return new RIPEMD160(); case HashAlgorithmType.None: case HashAlgorithmType.Mac: case HashAlgorithmType.HMac: case HashAlgorithmType.Ripemd: case HashAlgorithmType.Md2: case HashAlgorithmType.Md4: default: return null; } } calculatePasswordHashAfterDetectedAlgoritm(password, prefix, hashCount, hashAlgorithm) { const legacyPasswordHash = this.calculateLegacyPasswordHashInt(password); return this.calculatePasswordHashCore(this.getUnicodeArray(RichNumberConverter.convertToHexBinary(legacyPasswordHash).toUpperCase()), prefix, hashCount, hashAlgorithm); } getUnicodeArray(base64) { let n = base64.length; const arr = new Uint8Array(n * 2); while (n--) { const index = n * 2; arr[index] = base64.charCodeAt(n); arr[index + 1] = 0; } return arr; } calculatePasswordHashCore(legacyPasswordHash, prefix, hashCount, hashAlgorithm) { let bytes = this.concatenateArrays(prefix, legacyPasswordHash); let wordArray = this.byteArrayToWordArray(bytes); for (let i = 0;;) { wordArray = hashAlgorithm.computeHash(wordArray); if (i < hashCount) { wordArray = this.concatenateArray(wordArray, i); i++; } else break; } return new Uint8Array(this.wordArrayToByteArray(wordArray, wordArray.length)); } byteArrayToWordArray(bytes) { const result = []; for (let i = 0; i < bytes.length; i++) result[(i / 4) | 0] |= bytes[i] << (24 - 8 * i); return result; } wordToByteArray(word, length) { const bytes = [], xFF = 0xFF; if (length > 0) bytes.push(word >>> 24); if (length > 1) bytes.push((word >>> 16) & xFF); if (length > 2) bytes.push((word >>> 8) & xFF); if (length > 3) bytes.push(word & xFF); return bytes; } wordArrayToByteArray(wordArray, length) { length = wordArray.length * 4; const result = []; let bytes; let i = 0; while (length > 0) { bytes = this.wordToByteArray(wordArray[i], Math.min(4, length)); length -= bytes.length; result.push(bytes); i++; } return [].concat.apply([], result); } concatenateArrays(b1, b2) { if (b1 == null) return b2; if (b2 == null) return b1; const result = new Uint8Array(b1.length + b2.length); result.set(b1); result.set(b2, b1.length); return result; } concatenate(bytes, num) { const countBytes = new Array(4); countBytes[3] = ((num & 0xFF000000) >> 24); countBytes[2] = ((num & 0x00FF0000) >> 16); countBytes[1] = ((num & 0x0000FF00) >> 8); countBytes[0] = ((num & 0x000000FF)); const result = new Uint8Array(bytes.length + countBytes.length); result.set(bytes); result.set(countBytes, bytes.length); return result; } concatenateArray(wordArray, num) { const countBytes = new Array(4); countBytes[3] = (num & 0xFF000000) >> 24; countBytes[2] = (num & 0x00FF0000) >> 16; countBytes[1] = (num & 0x0000FF00) >> 8; countBytes[0] = (num & 0x000000FF); const newWordArray = this.byteArrayToWordArray(countBytes); return wordArray.concat(newWordArray); } static compareByteArrays(b1, b2) { if (b1 === b2) return true; if (b1 == null || b2 == null) return false; if (b1.length != b2.length) return false; const count = b1.length; for (let i = 0; i < count; i++) if (b1[i] != b2[i]) return false; return true; } calculatePasswordBytes(password) { if (password.length > 15) password = password.substr(0, 15); const count = password.length; const bytes = new Array(count); for (let i = 0; i < count; i++) { const ch = password.charCodeAt(i); if ((ch & 0x00FF) == 0) bytes[i] = ch >> 8; else bytes[i] = ch & 0x00FF; } return bytes; } calculateKeyLowWord(bytes) { let result = 0; const count = bytes.length; for (let i = count - 1; i >= 0; i--) result = this.processLowWordByte(result, bytes[i]); result = this.processLowWordByte(result, count) ^ 0xCE4B; return result; } processLowWordByte(key, b) { return ((((key >> 14) & 0x0001) | ((key << 1) & 0x7FFF)) ^ b); } calculateKeyHighWord(bytes) { const count = bytes.length; let result = PasswordHashCodeCalculator.initialValues[count - 1]; for (let i = 0; i < count; i++) result = this.processHighWordByte(result, bytes[i], 15 - (count - i)); return result; } processHighWordByte(key, b, rowIndex) { let mask = 1; for (let i = 0; i <= 6; i++, mask <<= 1) if ((b & mask) != 0) key ^= PasswordHashCodeCalculator.encryptionMatrix[rowIndex][i]; return key; } } PasswordHashCodeCalculator.initialValues = [0xE1F0, 0x1D0F, 0xCC9C, 0x84C0, 0x110C, 0x0E10, 0xF1CE, 0x313E, 0x1872, 0xE139, 0xD40F, 0x84F9, 0x280C, 0xA96A, 0x4EC3]; PasswordHashCodeCalculator.encryptionMatrix = [ [0xAEFC, 0x4DD9, 0x9BB2, 0x2745, 0x4E8A, 0x9D14, 0x2A09], [0x7B61, 0xF6C2, 0xFDA5, 0xEB6B, 0xC6F7, 0x9DCF, 0x2BBF], [0x4563, 0x8AC6, 0x05AD, 0x0B5A, 0x16B4, 0x2D68, 0x5AD0], [0x0375, 0x06EA, 0x0DD4, 0x1BA8, 0x3750, 0x6EA0, 0xDD40], [0xD849, 0xA0B3, 0x5147, 0xA28E, 0x553D, 0xAA7A, 0x44D5], [0x6F45, 0xDE8A, 0xAD35, 0x4A4B, 0x9496, 0x390D, 0x721A], [0xEB23, 0xC667, 0x9CEF, 0x29FF, 0x53FE, 0xA7FC, 0x5FD9], [0x47D3, 0x8FA6, 0x0F6D, 0x1EDA, 0x3DB4, 0x7B68, 0xF6D0], [0xB861, 0x60E3, 0xC1C6, 0x93AD, 0x377B, 0x6EF6, 0xDDEC], [0x45A0, 0x8B40, 0x06A1, 0x0D42, 0x1A84, 0x3508, 0x6A10], [0xAA51, 0x4483, 0x8906, 0x022D, 0x045A, 0x08B4, 0x1168], [0x76B4, 0xED68, 0xCAF1, 0x85C3, 0x1BA7, 0x374E, 0x6E9C], [0x3730, 0x6E60, 0xDCC0, 0xA9A1, 0x4363, 0x86C6, 0x1DAD], [0x3331, 0x6662, 0xCCC4, 0x89A9, 0x0373, 0x06E6, 0x0DCC], [0x1021, 0x2042, 0x4084, 0x8108, 0x1231, 0x2462, 0x48C4] ];