@syncfusion/ej2-pdf
Version:
Feature-rich JavaScript PDF library with built-in support for loading and manipulating PDF document.
411 lines (410 loc) • 17.9 kB
JavaScript
import { _isName, _PdfName } from './../pdf-primitives';
import { FormatError, _stringToBytes, _areArrayEqual } from './../utils';
import { _MD5 } from './encryptors/messageDigest5';
import { _AdvancedEncryption, _BasicEncryption } from './encryptors/basic-encryption';
import { _NormalCipherFour, _NullCipher } from './encryptors/normal-cipher';
import { _CipherTransform } from './encryptors/cipher-tranform';
import { _AdvancedEncryption128Cipher, _AdvancedEncryption256Cipher } from './encryptors/advance-cipher';
var _PdfEncryptor = /** @class */ (function () {
function _PdfEncryptor(dictionary, id, password) {
this._isUserPassword = true;
this._hasUserPasswordOnly = false;
this._encryptOnlyAttachment = false;
this._encryptMetaData = true;
this._defaultPasswordBytes = new Uint8Array([0x28, 0xbf, 0x4e, 0x5e, 0x4e, 0x75, 0x8a, 0x41, 0x64, 0x00, 0x4e, 0x56, 0xff,
0xfa, 0x01, 0x08, 0x2e, 0x2e, 0x00, 0xb6, 0xd0, 0x68, 0x3e, 0x80, 0x2f, 0x0c, 0xa9, 0xfe, 0x64, 0x53, 0x69, 0x7a]);
if (password === null || typeof password === 'undefined') {
password = '';
}
var filter = dictionary.get('Filter');
if (!_isName(filter, 'Standard')) {
throw new FormatError('unknown encryption method');
}
this._filterName = filter.name;
this._dictionary = dictionary;
var algorithm = dictionary.get('V');
if (!Number.isInteger(algorithm) || (algorithm !== 1 && algorithm !== 2 && algorithm !== 4 && algorithm !== 5)) {
throw new FormatError('unsupported encryption algorithm');
}
this._algorithm = algorithm;
var keyLength = dictionary.get('Length');
if (!keyLength) {
if (algorithm <= 3) {
keyLength = 40;
}
else {
var cfDictionary = dictionary.get('CF');
var streamCryptoName = dictionary.get('StmF');
if (cfDictionary && streamCryptoName) {
cfDictionary.suppressEncryption = true;
var handlerDictionary = cfDictionary.get(streamCryptoName.name);
keyLength = (handlerDictionary && handlerDictionary.get('Length')) || 128;
if (keyLength < 40) {
keyLength <<= 3;
}
}
}
}
if (!Number.isInteger(keyLength) || keyLength < 40 || keyLength % 8 !== 0) {
throw new FormatError('invalid key length');
}
var ownerPassword = _stringToBytes(dictionary.get('O'), false, true).subarray(0, 32);
var userPassword = _stringToBytes(dictionary.get('U'), false, true).subarray(0, 32);
var flag = dictionary.get('P');
var revision = dictionary.get('R');
this._encryptMetaData = (algorithm === 4 || algorithm === 5) && dictionary.get('EncryptMetadata') !== false;
var fileIdBytes = _stringToBytes(id, false, true);
var passwordBytes;
if (password) {
passwordBytes = _stringToBytes(password, false, true);
}
var encryptionKey;
if (algorithm !== 5) {
encryptionKey = this._prepareKeyData(fileIdBytes, passwordBytes, ownerPassword, userPassword, flag, revision, keyLength, this._encryptMetaData);
if (encryptionKey) {
this._isUserPassword = true;
if (password) {
var decodedPassword = this._decodeUserPassword(passwordBytes, ownerPassword, revision, keyLength);
var ownerEncryptionKey = this._prepareKeyData(fileIdBytes, decodedPassword, ownerPassword, userPassword, flag, revision, keyLength, this._encryptMetaData);
if (ownerEncryptionKey && _areArrayEqual(ownerEncryptionKey, encryptionKey)) {
this._hasUserPasswordOnly = true;
}
}
}
}
else {
var ownerValidationKey = _stringToBytes(dictionary.get('O'), false, true);
var ownerValidationSalt = ownerValidationKey.subarray(32, 40);
var ownerKeySalt = ownerValidationKey.subarray(40, 48);
var userValidationKey = _stringToBytes(dictionary.get('U'), false, true);
var uBytes = userValidationKey.subarray(0, 48);
var userValidationSalt = userValidationKey.subarray(32, 40);
var userKeySalt = userValidationKey.subarray(40, 48);
var ownerEncryption = _stringToBytes(dictionary.get('OE'), false, true);
var userEncryption = _stringToBytes(dictionary.get('UE'), false, true);
var algorithm_1;
if (revision === 6) {
algorithm_1 = new _AdvancedEncryption();
}
else {
algorithm_1 = new _BasicEncryption();
}
var p = void 0;
if (passwordBytes) {
p = passwordBytes.subarray(0, Math.min(127, passwordBytes.length));
}
else {
p = new Uint8Array([]);
}
if (algorithm_1._checkUserPassword(p, userValidationSalt, userPassword)) {
encryptionKey = this._createEncryptionKey(true, p, ownerKeySalt, uBytes, userKeySalt, ownerEncryption, userEncryption, algorithm_1);
this._isUserPassword = true;
if (password.length && algorithm_1._checkOwnerPassword(p, ownerValidationSalt, uBytes, ownerPassword)) {
this._hasUserPasswordOnly = true;
}
}
else if (password.length && algorithm_1._checkOwnerPassword(p, ownerValidationSalt, uBytes, ownerPassword)) {
encryptionKey = this._createEncryptionKey(false, passwordBytes, ownerKeySalt, uBytes, userKeySalt, ownerEncryption, userEncryption, algorithm_1);
this._isUserPassword = false;
}
}
if (!encryptionKey) {
if (password) {
var decodedPassword = this._decodeUserPassword(passwordBytes, ownerPassword, revision, keyLength);
encryptionKey = this._prepareKeyData(fileIdBytes, decodedPassword, ownerPassword, userPassword, flag, revision, keyLength, this._encryptMetaData);
this._isUserPassword = false;
}
else {
throw new Error('Cannot open an encrypted document. The password is invalid.');
}
}
if (algorithm >= 4) {
var cipherDictionary = dictionary.get('CF');
if (cipherDictionary) {
cipherDictionary.suppressEncryption = true;
if (cipherDictionary.has('StdCF')) {
var standardCryptFilter = cipherDictionary.get('StdCF');
if (standardCryptFilter && standardCryptFilter.has('AuthEvent')) {
var event_1 = standardCryptFilter.get('AuthEvent');
if (event_1 && event_1.name === 'EFOpen') {
this._encryptOnlyAttachment = true;
}
}
}
}
this._cipherDictionary = cipherDictionary;
this._stream = dictionary.get('StmF') || _PdfName.get('Identity');
this._string = dictionary.get('StrF') || _PdfName.get('Identity');
this._eff = dictionary.get('EFF') || this._stream;
}
if (!encryptionKey && !this._encryptOnlyAttachment) {
throw new Error('Cannot open an encrypted document. The password is invalid.');
}
this._encryptionKey = encryptionKey;
}
Object.defineProperty(_PdfEncryptor.prototype, "_md5", {
get: function () {
if (typeof this._messageDigest === 'undefined' || this._messageDigest === null) {
this._messageDigest = new _MD5();
}
return this._messageDigest;
},
enumerable: true,
configurable: true
});
_PdfEncryptor.prototype._createEncryptionKey = function (isUserKey, password, ownerKeySalt, uBytes, userKeySalt, ownerEncryption, userEncryption, algorithm) {
if (isUserKey) {
return algorithm._getUserKey(password, userKeySalt, userEncryption);
}
else {
return algorithm._getOwnerKey(password, ownerKeySalt, uBytes, ownerEncryption);
}
};
_PdfEncryptor.prototype._prepareKeyData = function (id, password, ownerPassword, userPassword, flags, revision, keyLength, encryptMetaData) {
var hashData = new Uint8Array(40 + ownerPassword.length + id.length);
var i = 0;
var j = 0;
var n;
if (password) {
n = Math.min(32, password.length);
for (; i < n; ++i) {
hashData[i] = password[i];
}
}
while (i < 32) {
hashData[i++] = this._defaultPasswordBytes[j++];
}
for (j = 0, n = ownerPassword.length; j < n; ++j) {
hashData[i++] = ownerPassword[j];
}
hashData[i++] = flags & 0xff;
hashData[i++] = (flags >> 8) & 0xff;
hashData[i++] = (flags >> 16) & 0xff;
hashData[i++] = (flags >>> 24) & 0xff;
for (j = 0, n = id.length; j < n; ++j) {
hashData[i++] = id[j];
}
if (revision >= 4 && !encryptMetaData) {
hashData[i++] = 0xff;
hashData[i++] = 0xff;
hashData[i++] = 0xff;
hashData[i++] = 0xff;
}
var hash = this._md5.hash(hashData, 0, i);
var keyLengthInBytes = keyLength >> 3;
if (revision >= 3) {
for (j = 0; j < 50; ++j) {
hash = this._md5.hash(hash, 0, keyLengthInBytes);
}
}
var encryptionKey = hash.subarray(0, keyLengthInBytes);
var cipher;
var checkData;
if (revision >= 3) {
for (i = 0; i < 32; ++i) {
hashData[i] = this._defaultPasswordBytes[i];
}
for (j = 0, n = id.length; j < n; ++j) {
hashData[i++] = id[j];
}
cipher = new _NormalCipherFour(encryptionKey);
checkData = cipher._encryptBlock(this._md5.hash(hashData, 0, i));
n = encryptionKey.length;
var derivedKey = new Uint8Array(n);
for (j = 1; j <= 19; ++j) {
for (var k = 0; k < n; ++k) {
derivedKey[k] = encryptionKey[k] ^ j;
}
cipher = new _NormalCipherFour(derivedKey);
checkData = cipher._encryptBlock(checkData);
}
for (j = 0, n = checkData.length; j < n; ++j) {
if (userPassword[j] !== checkData[j]) {
return null;
}
}
}
else {
cipher = new _NormalCipherFour(encryptionKey);
checkData = cipher._encryptBlock(this._defaultPasswordBytes);
for (j = 0, n = checkData.length; j < n; ++j) {
if (userPassword[j] !== checkData[j]) {
return null;
}
}
}
return encryptionKey;
};
_PdfEncryptor.prototype._decodeUserPassword = function (password, ownerPassword, revision, keyLength) {
var hashData = new Uint8Array(32);
var i = 0;
var j = 0;
var n = Math.min(32, password.length);
for (; i < n; ++i) {
hashData[i] = password[i];
}
while (i < 32) {
hashData[i++] = this._defaultPasswordBytes[j++];
}
var hash = this._md5.hash(hashData, 0, i);
var keyLengthInBytes = keyLength >> 3;
if (revision >= 3) {
for (j = 0; j < 50; ++j) {
hash = this._md5.hash(hash, 0, hash.length);
}
}
var cipher;
var userPassword;
if (revision >= 3) {
userPassword = ownerPassword;
var derivedKey = new Uint8Array(keyLengthInBytes);
for (j = 19; j >= 0; j--) {
for (var k = 0; k < keyLengthInBytes; ++k) {
derivedKey[k] = hash[k] ^ j;
}
cipher = new _NormalCipherFour(derivedKey);
userPassword = cipher._encryptBlock(userPassword);
}
}
else {
cipher = new _NormalCipherFour(hash.subarray(0, keyLengthInBytes));
userPassword = cipher._encryptBlock(ownerPassword);
}
return userPassword;
};
_PdfEncryptor.prototype._createCipherTransform = function (objectNumber, generationNumber) {
if (this._algorithm === 4 || this._algorithm === 5) {
var stringCipher = this._buildCipherConstructor(this._cipherDictionary, this._string, objectNumber, generationNumber, this._encryptionKey);
var streamCipher = this._buildCipherConstructor(this._cipherDictionary, this._stream, objectNumber, generationNumber, this._encryptionKey);
return new _CipherTransform(stringCipher, streamCipher);
}
var key = this._buildObjectKey(objectNumber, generationNumber, this._encryptionKey, false);
return new _CipherTransform(new _NormalCipherFour(key), new _NormalCipherFour(key));
};
_PdfEncryptor.prototype._buildCipherConstructor = function (cipherDictionary, name, objectNumber, generationNumber, key) {
var cryptFilter = cipherDictionary.get(name.name);
var cfm;
if (cryptFilter) {
cfm = cryptFilter.get('CFM');
}
if (!cfm) {
return new _NullCipher();
}
switch (cfm.name) {
case 'None':
return new _NullCipher();
case 'AESV2':
return new _AdvancedEncryption128Cipher(this._buildObjectKey(objectNumber, generationNumber, key, true));
case 'AESV3':
return new _AdvancedEncryption256Cipher(key);
case 'V2':
return new _NormalCipherFour(this._buildObjectKey(objectNumber, generationNumber, key, false));
}
throw new FormatError('Unknown cryptography method');
};
_PdfEncryptor.prototype._buildObjectKey = function (objectNumber, generationNumber, encryptionKey, isAdvancedEncryption) {
if (isAdvancedEncryption === void 0) { isAdvancedEncryption = false; }
var key = new Uint8Array(encryptionKey.length + 9);
var i;
for (i = 0; i < encryptionKey.length; ++i) {
key[i] = encryptionKey[i];
}
key[i++] = objectNumber & 0xff;
key[i++] = (objectNumber >> 8) & 0xff;
key[i++] = (objectNumber >> 16) & 0xff;
key[i++] = generationNumber & 0xff;
key[i++] = (generationNumber >> 8) & 0xff;
if (isAdvancedEncryption) {
key[i++] = 0x73;
key[i++] = 0x41;
key[i++] = 0x6c;
key[i++] = 0x54;
}
var hash = this._md5.hash(key, 0, i);
return hash.subarray(0, Math.min(encryptionKey.length + 5, 16));
};
return _PdfEncryptor;
}());
export { _PdfEncryptor };
var _Word64 = /** @class */ (function () {
function _Word64(high, low) {
this.high = high | 0;
this.low = low | 0;
}
_Word64.prototype.and = function (word) {
this.high &= word.high;
this.low &= word.low;
};
_Word64.prototype.or = function (word) {
this.high |= word.high;
this.low |= word.low;
};
_Word64.prototype.not = function () {
this.high = ~this.high;
this.low = ~this.low;
};
_Word64.prototype.xor = function (word) {
this.high ^= word.high;
this.low ^= word.low;
};
_Word64.prototype.shiftRight = function (places) {
if (places >= 32) {
this.low = (this.high >>> (places - 32)) | 0;
this.high = 0;
}
else {
this.low = (this.low >>> places) | (this.high << (32 - places));
this.high = (this.high >>> places) | 0;
}
};
_Word64.prototype.shiftLeft = function (places) {
if (places >= 32) {
this.high = this.low << (places - 32);
this.low = 0;
}
else {
this.high = (this.high << places) | (this.low >>> (32 - places));
this.low <<= places;
}
};
_Word64.prototype.rotateRight = function (places) {
var low;
var high;
if (places & 32) {
high = this.low;
low = this.high;
}
else {
low = this.low;
high = this.high;
}
places &= 31;
this.low = (low >>> places) | (high << (32 - places));
this.high = (high >>> places) | (low << (32 - places));
};
_Word64.prototype.add = function (word) {
var lowAdd = (this.low >>> 0) + (word.low >>> 0);
var highAdd = (this.high >>> 0) + (word.high >>> 0);
if (lowAdd > 0xffffffff) {
highAdd += 1;
}
this.low = lowAdd | 0;
this.high = highAdd | 0;
};
_Word64.prototype.copyTo = function (bytes, offset) {
bytes[offset] = (this.high >>> 24) & 0xff;
bytes[offset + 1] = (this.high >> 16) & 0xff;
bytes[offset + 2] = (this.high >> 8) & 0xff;
bytes[offset + 3] = this.high & 0xff;
bytes[offset + 4] = (this.low >>> 24) & 0xff;
bytes[offset + 5] = (this.low >> 16) & 0xff;
bytes[offset + 6] = (this.low >> 8) & 0xff;
bytes[offset + 7] = this.low & 0xff;
};
_Word64.prototype.assign = function (word) {
this.high = word.high;
this.low = word.low;
};
return _Word64;
}());
export { _Word64 };