Convert code in src/core/crypto.js
to use "normal" classes
All of this code predates the existence of native JS classes, however we can now clean this up a bit. This patch thus let us remove some variable "shadowing" from the code.
This commit is contained in:
parent
b884757873
commit
6b4c4f80e4
@ -28,9 +28,8 @@ import {
|
|||||||
import { isDict, isName, Name } from "./primitives.js";
|
import { isDict, isName, Name } from "./primitives.js";
|
||||||
import { DecryptStream } from "./stream.js";
|
import { DecryptStream } from "./stream.js";
|
||||||
|
|
||||||
var ARCFourCipher = (function ARCFourCipherClosure() {
|
class ARCFourCipher {
|
||||||
// eslint-disable-next-line no-shadow
|
constructor(key) {
|
||||||
function ARCFourCipher(key) {
|
|
||||||
this.a = 0;
|
this.a = 0;
|
||||||
this.b = 0;
|
this.b = 0;
|
||||||
var s = new Uint8Array(256);
|
var s = new Uint8Array(256);
|
||||||
@ -50,35 +49,37 @@ var ARCFourCipher = (function ARCFourCipherClosure() {
|
|||||||
this.s = s;
|
this.s = s;
|
||||||
}
|
}
|
||||||
|
|
||||||
ARCFourCipher.prototype = {
|
encryptBlock(data) {
|
||||||
encryptBlock: function ARCFourCipher_encryptBlock(data) {
|
var i,
|
||||||
var i,
|
n = data.length,
|
||||||
n = data.length,
|
tmp,
|
||||||
tmp,
|
tmp2;
|
||||||
tmp2;
|
var a = this.a,
|
||||||
var a = this.a,
|
b = this.b,
|
||||||
b = this.b,
|
s = this.s;
|
||||||
s = this.s;
|
var output = new Uint8Array(n);
|
||||||
var output = new Uint8Array(n);
|
for (i = 0; i < n; ++i) {
|
||||||
for (i = 0; i < n; ++i) {
|
a = (a + 1) & 0xff;
|
||||||
a = (a + 1) & 0xff;
|
tmp = s[a];
|
||||||
tmp = s[a];
|
b = (b + tmp) & 0xff;
|
||||||
b = (b + tmp) & 0xff;
|
tmp2 = s[b];
|
||||||
tmp2 = s[b];
|
s[a] = tmp2;
|
||||||
s[a] = tmp2;
|
s[b] = tmp;
|
||||||
s[b] = tmp;
|
output[i] = data[i] ^ s[(tmp + tmp2) & 0xff];
|
||||||
output[i] = data[i] ^ s[(tmp + tmp2) & 0xff];
|
}
|
||||||
}
|
this.a = a;
|
||||||
this.a = a;
|
this.b = b;
|
||||||
this.b = b;
|
return output;
|
||||||
return output;
|
}
|
||||||
},
|
|
||||||
};
|
|
||||||
ARCFourCipher.prototype.decryptBlock = ARCFourCipher.prototype.encryptBlock;
|
|
||||||
ARCFourCipher.prototype.encrypt = ARCFourCipher.prototype.encryptBlock;
|
|
||||||
|
|
||||||
return ARCFourCipher;
|
decryptBlock(data) {
|
||||||
})();
|
return this.encryptBlock(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
encrypt(data) {
|
||||||
|
return this.encryptBlock(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var calculateMD5 = (function calculateMD5Closure() {
|
var calculateMD5 = (function calculateMD5Closure() {
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
@ -180,94 +181,93 @@ var calculateMD5 = (function calculateMD5Closure() {
|
|||||||
|
|
||||||
return hash;
|
return hash;
|
||||||
})();
|
})();
|
||||||
var Word64 = (function Word64Closure() {
|
|
||||||
// eslint-disable-next-line no-shadow
|
class Word64 {
|
||||||
function Word64(highInteger, lowInteger) {
|
constructor(highInteger, lowInteger) {
|
||||||
this.high = highInteger | 0;
|
this.high = highInteger | 0;
|
||||||
this.low = lowInteger | 0;
|
this.low = lowInteger | 0;
|
||||||
}
|
}
|
||||||
Word64.prototype = {
|
|
||||||
and: function Word64_and(word) {
|
|
||||||
this.high &= word.high;
|
|
||||||
this.low &= word.low;
|
|
||||||
},
|
|
||||||
xor: function Word64_xor(word) {
|
|
||||||
this.high ^= word.high;
|
|
||||||
this.low ^= word.low;
|
|
||||||
},
|
|
||||||
|
|
||||||
or: function Word64_or(word) {
|
and(word) {
|
||||||
this.high |= word.high;
|
this.high &= word.high;
|
||||||
this.low |= word.low;
|
this.low &= word.low;
|
||||||
},
|
}
|
||||||
|
|
||||||
shiftRight: function Word64_shiftRight(places) {
|
xor(word) {
|
||||||
if (places >= 32) {
|
this.high ^= word.high;
|
||||||
this.low = (this.high >>> (places - 32)) | 0;
|
this.low ^= word.low;
|
||||||
this.high = 0;
|
}
|
||||||
} else {
|
|
||||||
this.low = (this.low >>> places) | (this.high << (32 - places));
|
|
||||||
this.high = (this.high >>> places) | 0;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
shiftLeft: function Word64_shiftLeft(places) {
|
or(word) {
|
||||||
if (places >= 32) {
|
this.high |= word.high;
|
||||||
this.high = this.low << (places - 32);
|
this.low |= word.low;
|
||||||
this.low = 0;
|
}
|
||||||
} else {
|
|
||||||
this.high = (this.high << places) | (this.low >>> (32 - places));
|
|
||||||
this.low = this.low << places;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
rotateRight: function Word64_rotateRight(places) {
|
shiftRight(places) {
|
||||||
var low, high;
|
if (places >= 32) {
|
||||||
if (places & 32) {
|
this.low = (this.high >>> (places - 32)) | 0;
|
||||||
high = this.low;
|
this.high = 0;
|
||||||
low = this.high;
|
} else {
|
||||||
} else {
|
this.low = (this.low >>> places) | (this.high << (32 - places));
|
||||||
low = this.low;
|
this.high = (this.high >>> places) | 0;
|
||||||
high = this.high;
|
}
|
||||||
}
|
}
|
||||||
places &= 31;
|
|
||||||
this.low = (low >>> places) | (high << (32 - places));
|
|
||||||
this.high = (high >>> places) | (low << (32 - places));
|
|
||||||
},
|
|
||||||
|
|
||||||
not: function Word64_not() {
|
shiftLeft(places) {
|
||||||
this.high = ~this.high;
|
if (places >= 32) {
|
||||||
this.low = ~this.low;
|
this.high = this.low << (places - 32);
|
||||||
},
|
this.low = 0;
|
||||||
|
} else {
|
||||||
|
this.high = (this.high << places) | (this.low >>> (32 - places));
|
||||||
|
this.low = this.low << places;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
add: function Word64_add(word) {
|
rotateRight(places) {
|
||||||
var lowAdd = (this.low >>> 0) + (word.low >>> 0);
|
var low, high;
|
||||||
var highAdd = (this.high >>> 0) + (word.high >>> 0);
|
if (places & 32) {
|
||||||
if (lowAdd > 0xffffffff) {
|
high = this.low;
|
||||||
highAdd += 1;
|
low = this.high;
|
||||||
}
|
} else {
|
||||||
this.low = lowAdd | 0;
|
low = this.low;
|
||||||
this.high = highAdd | 0;
|
high = this.high;
|
||||||
},
|
}
|
||||||
|
places &= 31;
|
||||||
|
this.low = (low >>> places) | (high << (32 - places));
|
||||||
|
this.high = (high >>> places) | (low << (32 - places));
|
||||||
|
}
|
||||||
|
|
||||||
copyTo: function Word64_copyTo(bytes, offset) {
|
not() {
|
||||||
bytes[offset] = (this.high >>> 24) & 0xff;
|
this.high = ~this.high;
|
||||||
bytes[offset + 1] = (this.high >> 16) & 0xff;
|
this.low = ~this.low;
|
||||||
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;
|
|
||||||
},
|
|
||||||
|
|
||||||
assign: function Word64_assign(word) {
|
add(word) {
|
||||||
this.high = word.high;
|
var lowAdd = (this.low >>> 0) + (word.low >>> 0);
|
||||||
this.low = word.low;
|
var highAdd = (this.high >>> 0) + (word.high >>> 0);
|
||||||
},
|
if (lowAdd > 0xffffffff) {
|
||||||
};
|
highAdd += 1;
|
||||||
return Word64;
|
}
|
||||||
})();
|
this.low = lowAdd | 0;
|
||||||
|
this.high = highAdd | 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
copyTo(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;
|
||||||
|
}
|
||||||
|
|
||||||
|
assign(word) {
|
||||||
|
this.high = word.high;
|
||||||
|
this.low = word.low;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var calculateSHA256 = (function calculateSHA256Closure() {
|
var calculateSHA256 = (function calculateSHA256Closure() {
|
||||||
function rotr(x, n) {
|
function rotr(x, n) {
|
||||||
@ -521,8 +521,7 @@ var calculateSHA512 = (function calculateSHA512Closure() {
|
|||||||
new Word64(0x4cc5d4be, 0xcb3e42b6), new Word64(0x597f299c, 0xfc657e2a),
|
new Word64(0x4cc5d4be, 0xcb3e42b6), new Word64(0x597f299c, 0xfc657e2a),
|
||||||
new Word64(0x5fcb6fab, 0x3ad6faec), new Word64(0x6c44198c, 0x4a475817)];
|
new Word64(0x5fcb6fab, 0x3ad6faec), new Word64(0x6c44198c, 0x4a475817)];
|
||||||
|
|
||||||
function hash(data, offset, length, mode384) {
|
function hash(data, offset, length, mode384 = false) {
|
||||||
mode384 = !!mode384;
|
|
||||||
// initial hash values
|
// initial hash values
|
||||||
var h0, h1, h2, h3, h4, h5, h6, h7;
|
var h0, h1, h2, h3, h4, h5, h6, h7;
|
||||||
if (!mode384) {
|
if (!mode384) {
|
||||||
@ -687,28 +686,20 @@ var calculateSHA512 = (function calculateSHA512Closure() {
|
|||||||
|
|
||||||
return hash;
|
return hash;
|
||||||
})();
|
})();
|
||||||
var calculateSHA384 = (function calculateSHA384Closure() {
|
|
||||||
function hash(data, offset, length) {
|
function calculateSHA384(data, offset, length) {
|
||||||
return calculateSHA512(data, offset, length, true);
|
return calculateSHA512(data, offset, length, /* mode384 = */ true);
|
||||||
|
}
|
||||||
|
|
||||||
|
class NullCipher {
|
||||||
|
decryptBlock(data) {
|
||||||
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
return hash;
|
encrypt(data) {
|
||||||
})();
|
return data;
|
||||||
var NullCipher = (function NullCipherClosure() {
|
}
|
||||||
// eslint-disable-next-line no-shadow
|
}
|
||||||
function NullCipher() {}
|
|
||||||
|
|
||||||
NullCipher.prototype = {
|
|
||||||
decryptBlock: function NullCipher_decryptBlock(data) {
|
|
||||||
return data;
|
|
||||||
},
|
|
||||||
encrypt: function NullCipher_encrypt(data) {
|
|
||||||
return data;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
return NullCipher;
|
|
||||||
})();
|
|
||||||
|
|
||||||
class AESBaseCipher {
|
class AESBaseCipher {
|
||||||
constructor() {
|
constructor() {
|
||||||
@ -1262,65 +1253,44 @@ class AES256Cipher extends AESBaseCipher {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var PDF17 = (function PDF17Closure() {
|
class PDF17 {
|
||||||
// eslint-disable-next-line no-shadow
|
checkOwnerPassword(password, ownerValidationSalt, userBytes, ownerPassword) {
|
||||||
function PDF17() {}
|
var hashData = new Uint8Array(password.length + 56);
|
||||||
|
hashData.set(password, 0);
|
||||||
|
hashData.set(ownerValidationSalt, password.length);
|
||||||
|
hashData.set(userBytes, password.length + ownerValidationSalt.length);
|
||||||
|
var result = calculateSHA256(hashData, 0, hashData.length);
|
||||||
|
return isArrayEqual(result, ownerPassword);
|
||||||
|
}
|
||||||
|
|
||||||
PDF17.prototype = {
|
checkUserPassword(password, userValidationSalt, userPassword) {
|
||||||
checkOwnerPassword: function PDF17_checkOwnerPassword(
|
var hashData = new Uint8Array(password.length + 8);
|
||||||
password,
|
hashData.set(password, 0);
|
||||||
ownerValidationSalt,
|
hashData.set(userValidationSalt, password.length);
|
||||||
userBytes,
|
var result = calculateSHA256(hashData, 0, hashData.length);
|
||||||
ownerPassword
|
return isArrayEqual(result, userPassword);
|
||||||
) {
|
}
|
||||||
var hashData = new Uint8Array(password.length + 56);
|
|
||||||
hashData.set(password, 0);
|
getOwnerKey(password, ownerKeySalt, userBytes, ownerEncryption) {
|
||||||
hashData.set(ownerValidationSalt, password.length);
|
var hashData = new Uint8Array(password.length + 56);
|
||||||
hashData.set(userBytes, password.length + ownerValidationSalt.length);
|
hashData.set(password, 0);
|
||||||
var result = calculateSHA256(hashData, 0, hashData.length);
|
hashData.set(ownerKeySalt, password.length);
|
||||||
return isArrayEqual(result, ownerPassword);
|
hashData.set(userBytes, password.length + ownerKeySalt.length);
|
||||||
},
|
var key = calculateSHA256(hashData, 0, hashData.length);
|
||||||
checkUserPassword: function PDF17_checkUserPassword(
|
var cipher = new AES256Cipher(key);
|
||||||
password,
|
return cipher.decryptBlock(ownerEncryption, false, new Uint8Array(16));
|
||||||
userValidationSalt,
|
}
|
||||||
userPassword
|
|
||||||
) {
|
getUserKey(password, userKeySalt, userEncryption) {
|
||||||
var hashData = new Uint8Array(password.length + 8);
|
var hashData = new Uint8Array(password.length + 8);
|
||||||
hashData.set(password, 0);
|
hashData.set(password, 0);
|
||||||
hashData.set(userValidationSalt, password.length);
|
hashData.set(userKeySalt, password.length);
|
||||||
var result = calculateSHA256(hashData, 0, hashData.length);
|
// `key` is the decryption key for the UE string.
|
||||||
return isArrayEqual(result, userPassword);
|
var key = calculateSHA256(hashData, 0, hashData.length);
|
||||||
},
|
var cipher = new AES256Cipher(key);
|
||||||
getOwnerKey: function PDF17_getOwnerKey(
|
return cipher.decryptBlock(userEncryption, false, new Uint8Array(16));
|
||||||
password,
|
}
|
||||||
ownerKeySalt,
|
}
|
||||||
userBytes,
|
|
||||||
ownerEncryption
|
|
||||||
) {
|
|
||||||
var hashData = new Uint8Array(password.length + 56);
|
|
||||||
hashData.set(password, 0);
|
|
||||||
hashData.set(ownerKeySalt, password.length);
|
|
||||||
hashData.set(userBytes, password.length + ownerKeySalt.length);
|
|
||||||
var key = calculateSHA256(hashData, 0, hashData.length);
|
|
||||||
var cipher = new AES256Cipher(key);
|
|
||||||
return cipher.decryptBlock(ownerEncryption, false, new Uint8Array(16));
|
|
||||||
},
|
|
||||||
getUserKey: function PDF17_getUserKey(
|
|
||||||
password,
|
|
||||||
userKeySalt,
|
|
||||||
userEncryption
|
|
||||||
) {
|
|
||||||
var hashData = new Uint8Array(password.length + 8);
|
|
||||||
hashData.set(password, 0);
|
|
||||||
hashData.set(userKeySalt, password.length);
|
|
||||||
// `key` is the decryption key for the UE string.
|
|
||||||
var key = calculateSHA256(hashData, 0, hashData.length);
|
|
||||||
var cipher = new AES256Cipher(key);
|
|
||||||
return cipher.decryptBlock(userEncryption, false, new Uint8Array(16));
|
|
||||||
},
|
|
||||||
};
|
|
||||||
return PDF17;
|
|
||||||
})();
|
|
||||||
|
|
||||||
var PDF20 = (function PDF20Closure() {
|
var PDF20 = (function PDF20Closure() {
|
||||||
function calculatePDF20Hash(password, input, userBytes) {
|
function calculatePDF20Hash(password, input, userBytes) {
|
||||||
@ -1370,13 +1340,12 @@ var PDF20 = (function PDF20Closure() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line no-shadow
|
// eslint-disable-next-line no-shadow
|
||||||
function PDF20() {}
|
class PDF20 {
|
||||||
|
hash(password, concatBytes, userBytes) {
|
||||||
PDF20.prototype = {
|
|
||||||
hash: function PDF20_hash(password, concatBytes, userBytes) {
|
|
||||||
return calculatePDF20Hash(password, concatBytes, userBytes);
|
return calculatePDF20Hash(password, concatBytes, userBytes);
|
||||||
},
|
}
|
||||||
checkOwnerPassword: function PDF20_checkOwnerPassword(
|
|
||||||
|
checkOwnerPassword(
|
||||||
password,
|
password,
|
||||||
ownerValidationSalt,
|
ownerValidationSalt,
|
||||||
userBytes,
|
userBytes,
|
||||||
@ -1388,24 +1357,17 @@ var PDF20 = (function PDF20Closure() {
|
|||||||
hashData.set(userBytes, password.length + ownerValidationSalt.length);
|
hashData.set(userBytes, password.length + ownerValidationSalt.length);
|
||||||
var result = calculatePDF20Hash(password, hashData, userBytes);
|
var result = calculatePDF20Hash(password, hashData, userBytes);
|
||||||
return isArrayEqual(result, ownerPassword);
|
return isArrayEqual(result, ownerPassword);
|
||||||
},
|
}
|
||||||
checkUserPassword: function PDF20_checkUserPassword(
|
|
||||||
password,
|
checkUserPassword(password, userValidationSalt, userPassword) {
|
||||||
userValidationSalt,
|
|
||||||
userPassword
|
|
||||||
) {
|
|
||||||
var hashData = new Uint8Array(password.length + 8);
|
var hashData = new Uint8Array(password.length + 8);
|
||||||
hashData.set(password, 0);
|
hashData.set(password, 0);
|
||||||
hashData.set(userValidationSalt, password.length);
|
hashData.set(userValidationSalt, password.length);
|
||||||
var result = calculatePDF20Hash(password, hashData, []);
|
var result = calculatePDF20Hash(password, hashData, []);
|
||||||
return isArrayEqual(result, userPassword);
|
return isArrayEqual(result, userPassword);
|
||||||
},
|
}
|
||||||
getOwnerKey: function PDF20_getOwnerKey(
|
|
||||||
password,
|
getOwnerKey(password, ownerKeySalt, userBytes, ownerEncryption) {
|
||||||
ownerKeySalt,
|
|
||||||
userBytes,
|
|
||||||
ownerEncryption
|
|
||||||
) {
|
|
||||||
var hashData = new Uint8Array(password.length + 56);
|
var hashData = new Uint8Array(password.length + 56);
|
||||||
hashData.set(password, 0);
|
hashData.set(password, 0);
|
||||||
hashData.set(ownerKeySalt, password.length);
|
hashData.set(ownerKeySalt, password.length);
|
||||||
@ -1413,12 +1375,9 @@ var PDF20 = (function PDF20Closure() {
|
|||||||
var key = calculatePDF20Hash(password, hashData, userBytes);
|
var key = calculatePDF20Hash(password, hashData, userBytes);
|
||||||
var cipher = new AES256Cipher(key);
|
var cipher = new AES256Cipher(key);
|
||||||
return cipher.decryptBlock(ownerEncryption, false, new Uint8Array(16));
|
return cipher.decryptBlock(ownerEncryption, false, new Uint8Array(16));
|
||||||
},
|
}
|
||||||
getUserKey: function PDF20_getUserKey(
|
|
||||||
password,
|
getUserKey(password, userKeySalt, userEncryption) {
|
||||||
userKeySalt,
|
|
||||||
userEncryption
|
|
||||||
) {
|
|
||||||
var hashData = new Uint8Array(password.length + 8);
|
var hashData = new Uint8Array(password.length + 8);
|
||||||
hashData.set(password, 0);
|
hashData.set(password, 0);
|
||||||
hashData.set(userKeySalt, password.length);
|
hashData.set(userKeySalt, password.length);
|
||||||
@ -1426,74 +1385,73 @@ var PDF20 = (function PDF20Closure() {
|
|||||||
var key = calculatePDF20Hash(password, hashData, []);
|
var key = calculatePDF20Hash(password, hashData, []);
|
||||||
var cipher = new AES256Cipher(key);
|
var cipher = new AES256Cipher(key);
|
||||||
return cipher.decryptBlock(userEncryption, false, new Uint8Array(16));
|
return cipher.decryptBlock(userEncryption, false, new Uint8Array(16));
|
||||||
},
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
return PDF20;
|
return PDF20;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
var CipherTransform = (function CipherTransformClosure() {
|
class CipherTransform {
|
||||||
// eslint-disable-next-line no-shadow
|
constructor(stringCipherConstructor, streamCipherConstructor) {
|
||||||
function CipherTransform(stringCipherConstructor, streamCipherConstructor) {
|
|
||||||
this.StringCipherConstructor = stringCipherConstructor;
|
this.StringCipherConstructor = stringCipherConstructor;
|
||||||
this.StreamCipherConstructor = streamCipherConstructor;
|
this.StreamCipherConstructor = streamCipherConstructor;
|
||||||
}
|
}
|
||||||
|
|
||||||
CipherTransform.prototype = {
|
createStream(stream, length) {
|
||||||
createStream: function CipherTransform_createStream(stream, length) {
|
var cipher = new this.StreamCipherConstructor();
|
||||||
var cipher = new this.StreamCipherConstructor();
|
return new DecryptStream(
|
||||||
return new DecryptStream(
|
stream,
|
||||||
stream,
|
length,
|
||||||
length,
|
function cipherTransformDecryptStream(data, finalize) {
|
||||||
function cipherTransformDecryptStream(data, finalize) {
|
return cipher.decryptBlock(data, finalize);
|
||||||
return cipher.decryptBlock(data, finalize);
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
decryptString(s) {
|
||||||
|
var cipher = new this.StringCipherConstructor();
|
||||||
|
var data = stringToBytes(s);
|
||||||
|
data = cipher.decryptBlock(data, true);
|
||||||
|
return bytesToString(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
encryptString(s) {
|
||||||
|
const cipher = new this.StringCipherConstructor();
|
||||||
|
if (cipher instanceof AESBaseCipher) {
|
||||||
|
// Append some chars equal to "16 - (M mod 16)"
|
||||||
|
// where M is the string length (see section 7.6.2 in PDF specification)
|
||||||
|
// to have a final string where the length is a multiple of 16.
|
||||||
|
const strLen = s.length;
|
||||||
|
const pad = 16 - (strLen % 16);
|
||||||
|
if (pad !== 16) {
|
||||||
|
s = s.padEnd(16 * Math.ceil(strLen / 16), String.fromCharCode(pad));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate an initialization vector
|
||||||
|
const iv = new Uint8Array(16);
|
||||||
|
if (typeof crypto !== "undefined") {
|
||||||
|
crypto.getRandomValues(iv);
|
||||||
|
} else {
|
||||||
|
for (let i = 0; i < 16; i++) {
|
||||||
|
iv[i] = Math.floor(256 * Math.random());
|
||||||
}
|
}
|
||||||
);
|
|
||||||
},
|
|
||||||
decryptString: function CipherTransform_decryptString(s) {
|
|
||||||
var cipher = new this.StringCipherConstructor();
|
|
||||||
var data = stringToBytes(s);
|
|
||||||
data = cipher.decryptBlock(data, true);
|
|
||||||
return bytesToString(data);
|
|
||||||
},
|
|
||||||
encryptString: function CipherTransform_encryptString(s) {
|
|
||||||
const cipher = new this.StringCipherConstructor();
|
|
||||||
if (cipher instanceof AESBaseCipher) {
|
|
||||||
// Append some chars equal to "16 - (M mod 16)"
|
|
||||||
// where M is the string length (see section 7.6.2 in PDF specification)
|
|
||||||
// to have a final string where the length is a multiple of 16.
|
|
||||||
const strLen = s.length;
|
|
||||||
const pad = 16 - (strLen % 16);
|
|
||||||
if (pad !== 16) {
|
|
||||||
s = s.padEnd(16 * Math.ceil(strLen / 16), String.fromCharCode(pad));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate an initialization vector
|
|
||||||
const iv = new Uint8Array(16);
|
|
||||||
if (typeof crypto !== "undefined") {
|
|
||||||
crypto.getRandomValues(iv);
|
|
||||||
} else {
|
|
||||||
for (let i = 0; i < 16; i++) {
|
|
||||||
iv[i] = Math.floor(256 * Math.random());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let data = stringToBytes(s);
|
|
||||||
data = cipher.encrypt(data, iv);
|
|
||||||
|
|
||||||
const buf = new Uint8Array(16 + data.length);
|
|
||||||
buf.set(iv);
|
|
||||||
buf.set(data, 16);
|
|
||||||
|
|
||||||
return bytesToString(buf);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let data = stringToBytes(s);
|
let data = stringToBytes(s);
|
||||||
data = cipher.encrypt(data);
|
data = cipher.encrypt(data, iv);
|
||||||
return bytesToString(data);
|
|
||||||
},
|
const buf = new Uint8Array(16 + data.length);
|
||||||
};
|
buf.set(iv);
|
||||||
return CipherTransform;
|
buf.set(data, 16);
|
||||||
})();
|
|
||||||
|
return bytesToString(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
let data = stringToBytes(s);
|
||||||
|
data = cipher.encrypt(data);
|
||||||
|
return bytesToString(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var CipherTransformFactory = (function CipherTransformFactoryClosure() {
|
var CipherTransformFactory = (function CipherTransformFactoryClosure() {
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
@ -1684,161 +1642,7 @@ var CipherTransformFactory = (function CipherTransformFactoryClosure() {
|
|||||||
|
|
||||||
var identityName = Name.get("Identity");
|
var identityName = Name.get("Identity");
|
||||||
|
|
||||||
// eslint-disable-next-line no-shadow
|
function buildObjectKey(num, gen, encryptionKey, isAes = false) {
|
||||||
function CipherTransformFactory(dict, fileId, password) {
|
|
||||||
var filter = dict.get("Filter");
|
|
||||||
if (!isName(filter, "Standard")) {
|
|
||||||
throw new FormatError("unknown encryption method");
|
|
||||||
}
|
|
||||||
this.dict = dict;
|
|
||||||
var algorithm = dict.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 = dict.get("Length");
|
|
||||||
if (!keyLength) {
|
|
||||||
// Spec asks to rely on encryption dictionary's Length entry, however
|
|
||||||
// some PDFs don't have it. Trying to recover.
|
|
||||||
if (algorithm <= 3) {
|
|
||||||
// For 1 and 2 it's fixed to 40-bit, for 3 40-bit is a minimal value.
|
|
||||||
keyLength = 40;
|
|
||||||
} else {
|
|
||||||
// Trying to find default handler -- it usually has Length.
|
|
||||||
var cfDict = dict.get("CF");
|
|
||||||
var streamCryptoName = dict.get("StmF");
|
|
||||||
if (isDict(cfDict) && isName(streamCryptoName)) {
|
|
||||||
cfDict.suppressEncryption = true; // See comment below.
|
|
||||||
var handlerDict = cfDict.get(streamCryptoName.name);
|
|
||||||
keyLength = (handlerDict && handlerDict.get("Length")) || 128;
|
|
||||||
if (keyLength < 40) {
|
|
||||||
// Sometimes it's incorrect value of bits, generators specify bytes.
|
|
||||||
keyLength <<= 3;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!Number.isInteger(keyLength) || keyLength < 40 || keyLength % 8 !== 0) {
|
|
||||||
throw new FormatError("invalid key length");
|
|
||||||
}
|
|
||||||
|
|
||||||
// prepare keys
|
|
||||||
var ownerPassword = stringToBytes(dict.get("O")).subarray(0, 32);
|
|
||||||
var userPassword = stringToBytes(dict.get("U")).subarray(0, 32);
|
|
||||||
var flags = dict.get("P");
|
|
||||||
var revision = dict.get("R");
|
|
||||||
// meaningful when V is 4 or 5
|
|
||||||
var encryptMetadata =
|
|
||||||
(algorithm === 4 || algorithm === 5) &&
|
|
||||||
dict.get("EncryptMetadata") !== false;
|
|
||||||
this.encryptMetadata = encryptMetadata;
|
|
||||||
|
|
||||||
var fileIdBytes = stringToBytes(fileId);
|
|
||||||
var passwordBytes;
|
|
||||||
if (password) {
|
|
||||||
if (revision === 6) {
|
|
||||||
try {
|
|
||||||
password = utf8StringToString(password);
|
|
||||||
} catch (ex) {
|
|
||||||
warn(
|
|
||||||
"CipherTransformFactory: " +
|
|
||||||
"Unable to convert UTF8 encoded password."
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
passwordBytes = stringToBytes(password);
|
|
||||||
}
|
|
||||||
|
|
||||||
var encryptionKey;
|
|
||||||
if (algorithm !== 5) {
|
|
||||||
encryptionKey = prepareKeyData(
|
|
||||||
fileIdBytes,
|
|
||||||
passwordBytes,
|
|
||||||
ownerPassword,
|
|
||||||
userPassword,
|
|
||||||
flags,
|
|
||||||
revision,
|
|
||||||
keyLength,
|
|
||||||
encryptMetadata
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
var ownerValidationSalt = stringToBytes(dict.get("O")).subarray(32, 40);
|
|
||||||
var ownerKeySalt = stringToBytes(dict.get("O")).subarray(40, 48);
|
|
||||||
var uBytes = stringToBytes(dict.get("U")).subarray(0, 48);
|
|
||||||
var userValidationSalt = stringToBytes(dict.get("U")).subarray(32, 40);
|
|
||||||
var userKeySalt = stringToBytes(dict.get("U")).subarray(40, 48);
|
|
||||||
var ownerEncryption = stringToBytes(dict.get("OE"));
|
|
||||||
var userEncryption = stringToBytes(dict.get("UE"));
|
|
||||||
var perms = stringToBytes(dict.get("Perms"));
|
|
||||||
encryptionKey = createEncryptionKey20(
|
|
||||||
revision,
|
|
||||||
passwordBytes,
|
|
||||||
ownerPassword,
|
|
||||||
ownerValidationSalt,
|
|
||||||
ownerKeySalt,
|
|
||||||
uBytes,
|
|
||||||
userPassword,
|
|
||||||
userValidationSalt,
|
|
||||||
userKeySalt,
|
|
||||||
ownerEncryption,
|
|
||||||
userEncryption,
|
|
||||||
perms
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (!encryptionKey && !password) {
|
|
||||||
throw new PasswordException(
|
|
||||||
"No password given",
|
|
||||||
PasswordResponses.NEED_PASSWORD
|
|
||||||
);
|
|
||||||
} else if (!encryptionKey && password) {
|
|
||||||
// Attempting use the password as an owner password
|
|
||||||
var decodedPassword = decodeUserPassword(
|
|
||||||
passwordBytes,
|
|
||||||
ownerPassword,
|
|
||||||
revision,
|
|
||||||
keyLength
|
|
||||||
);
|
|
||||||
encryptionKey = prepareKeyData(
|
|
||||||
fileIdBytes,
|
|
||||||
decodedPassword,
|
|
||||||
ownerPassword,
|
|
||||||
userPassword,
|
|
||||||
flags,
|
|
||||||
revision,
|
|
||||||
keyLength,
|
|
||||||
encryptMetadata
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!encryptionKey) {
|
|
||||||
throw new PasswordException(
|
|
||||||
"Incorrect Password",
|
|
||||||
PasswordResponses.INCORRECT_PASSWORD
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.encryptionKey = encryptionKey;
|
|
||||||
|
|
||||||
if (algorithm >= 4) {
|
|
||||||
var cf = dict.get("CF");
|
|
||||||
if (isDict(cf)) {
|
|
||||||
// The 'CF' dictionary itself should not be encrypted, and by setting
|
|
||||||
// `suppressEncryption` we can prevent an infinite loop inside of
|
|
||||||
// `XRef_fetchUncompressed` if the dictionary contains indirect objects
|
|
||||||
// (fixes issue7665.pdf).
|
|
||||||
cf.suppressEncryption = true;
|
|
||||||
}
|
|
||||||
this.cf = cf;
|
|
||||||
this.stmf = dict.get("StmF") || identityName;
|
|
||||||
this.strf = dict.get("StrF") || identityName;
|
|
||||||
this.eff = dict.get("EFF") || this.stmf;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildObjectKey(num, gen, encryptionKey, isAes) {
|
|
||||||
var key = new Uint8Array(encryptionKey.length + 9),
|
var key = new Uint8Array(encryptionKey.length + 9),
|
||||||
i,
|
i,
|
||||||
n;
|
n;
|
||||||
@ -1876,12 +1680,16 @@ var CipherTransformFactory = (function CipherTransformFactoryClosure() {
|
|||||||
}
|
}
|
||||||
if (cfm.name === "V2") {
|
if (cfm.name === "V2") {
|
||||||
return function cipherTransformFactoryBuildCipherConstructorV2() {
|
return function cipherTransformFactoryBuildCipherConstructorV2() {
|
||||||
return new ARCFourCipher(buildObjectKey(num, gen, key, false));
|
return new ARCFourCipher(
|
||||||
|
buildObjectKey(num, gen, key, /* isAes = */ false)
|
||||||
|
);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if (cfm.name === "AESV2") {
|
if (cfm.name === "AESV2") {
|
||||||
return function cipherTransformFactoryBuildCipherConstructorAESV2() {
|
return function cipherTransformFactoryBuildCipherConstructorAESV2() {
|
||||||
return new AES128Cipher(buildObjectKey(num, gen, key, true));
|
return new AES128Cipher(
|
||||||
|
buildObjectKey(num, gen, key, /* isAes = */ true)
|
||||||
|
);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if (cfm.name === "AESV3") {
|
if (cfm.name === "AESV3") {
|
||||||
@ -1892,11 +1700,170 @@ var CipherTransformFactory = (function CipherTransformFactoryClosure() {
|
|||||||
throw new FormatError("Unknown crypto method");
|
throw new FormatError("Unknown crypto method");
|
||||||
}
|
}
|
||||||
|
|
||||||
CipherTransformFactory.prototype = {
|
// eslint-disable-next-line no-shadow
|
||||||
createCipherTransform: function CipherTransformFactory_createCipherTransform(
|
class CipherTransformFactory {
|
||||||
num,
|
constructor(dict, fileId, password) {
|
||||||
gen
|
var filter = dict.get("Filter");
|
||||||
) {
|
if (!isName(filter, "Standard")) {
|
||||||
|
throw new FormatError("unknown encryption method");
|
||||||
|
}
|
||||||
|
this.dict = dict;
|
||||||
|
var algorithm = dict.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 = dict.get("Length");
|
||||||
|
if (!keyLength) {
|
||||||
|
// Spec asks to rely on encryption dictionary's Length entry, however
|
||||||
|
// some PDFs don't have it. Trying to recover.
|
||||||
|
if (algorithm <= 3) {
|
||||||
|
// For 1 and 2 it's fixed to 40-bit, for 3 40-bit is a minimal value.
|
||||||
|
keyLength = 40;
|
||||||
|
} else {
|
||||||
|
// Trying to find default handler -- it usually has Length.
|
||||||
|
var cfDict = dict.get("CF");
|
||||||
|
var streamCryptoName = dict.get("StmF");
|
||||||
|
if (isDict(cfDict) && isName(streamCryptoName)) {
|
||||||
|
cfDict.suppressEncryption = true; // See comment below.
|
||||||
|
var handlerDict = cfDict.get(streamCryptoName.name);
|
||||||
|
keyLength = (handlerDict && handlerDict.get("Length")) || 128;
|
||||||
|
if (keyLength < 40) {
|
||||||
|
// Sometimes it's incorrect value of bits, generators specify
|
||||||
|
// bytes.
|
||||||
|
keyLength <<= 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
!Number.isInteger(keyLength) ||
|
||||||
|
keyLength < 40 ||
|
||||||
|
keyLength % 8 !== 0
|
||||||
|
) {
|
||||||
|
throw new FormatError("invalid key length");
|
||||||
|
}
|
||||||
|
|
||||||
|
// prepare keys
|
||||||
|
var ownerPassword = stringToBytes(dict.get("O")).subarray(0, 32);
|
||||||
|
var userPassword = stringToBytes(dict.get("U")).subarray(0, 32);
|
||||||
|
var flags = dict.get("P");
|
||||||
|
var revision = dict.get("R");
|
||||||
|
// meaningful when V is 4 or 5
|
||||||
|
var encryptMetadata =
|
||||||
|
(algorithm === 4 || algorithm === 5) &&
|
||||||
|
dict.get("EncryptMetadata") !== false;
|
||||||
|
this.encryptMetadata = encryptMetadata;
|
||||||
|
|
||||||
|
var fileIdBytes = stringToBytes(fileId);
|
||||||
|
var passwordBytes;
|
||||||
|
if (password) {
|
||||||
|
if (revision === 6) {
|
||||||
|
try {
|
||||||
|
password = utf8StringToString(password);
|
||||||
|
} catch (ex) {
|
||||||
|
warn(
|
||||||
|
"CipherTransformFactory: " +
|
||||||
|
"Unable to convert UTF8 encoded password."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
passwordBytes = stringToBytes(password);
|
||||||
|
}
|
||||||
|
|
||||||
|
var encryptionKey;
|
||||||
|
if (algorithm !== 5) {
|
||||||
|
encryptionKey = prepareKeyData(
|
||||||
|
fileIdBytes,
|
||||||
|
passwordBytes,
|
||||||
|
ownerPassword,
|
||||||
|
userPassword,
|
||||||
|
flags,
|
||||||
|
revision,
|
||||||
|
keyLength,
|
||||||
|
encryptMetadata
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
var ownerValidationSalt = stringToBytes(dict.get("O")).subarray(32, 40);
|
||||||
|
var ownerKeySalt = stringToBytes(dict.get("O")).subarray(40, 48);
|
||||||
|
var uBytes = stringToBytes(dict.get("U")).subarray(0, 48);
|
||||||
|
var userValidationSalt = stringToBytes(dict.get("U")).subarray(32, 40);
|
||||||
|
var userKeySalt = stringToBytes(dict.get("U")).subarray(40, 48);
|
||||||
|
var ownerEncryption = stringToBytes(dict.get("OE"));
|
||||||
|
var userEncryption = stringToBytes(dict.get("UE"));
|
||||||
|
var perms = stringToBytes(dict.get("Perms"));
|
||||||
|
encryptionKey = createEncryptionKey20(
|
||||||
|
revision,
|
||||||
|
passwordBytes,
|
||||||
|
ownerPassword,
|
||||||
|
ownerValidationSalt,
|
||||||
|
ownerKeySalt,
|
||||||
|
uBytes,
|
||||||
|
userPassword,
|
||||||
|
userValidationSalt,
|
||||||
|
userKeySalt,
|
||||||
|
ownerEncryption,
|
||||||
|
userEncryption,
|
||||||
|
perms
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (!encryptionKey && !password) {
|
||||||
|
throw new PasswordException(
|
||||||
|
"No password given",
|
||||||
|
PasswordResponses.NEED_PASSWORD
|
||||||
|
);
|
||||||
|
} else if (!encryptionKey && password) {
|
||||||
|
// Attempting use the password as an owner password
|
||||||
|
var decodedPassword = decodeUserPassword(
|
||||||
|
passwordBytes,
|
||||||
|
ownerPassword,
|
||||||
|
revision,
|
||||||
|
keyLength
|
||||||
|
);
|
||||||
|
encryptionKey = prepareKeyData(
|
||||||
|
fileIdBytes,
|
||||||
|
decodedPassword,
|
||||||
|
ownerPassword,
|
||||||
|
userPassword,
|
||||||
|
flags,
|
||||||
|
revision,
|
||||||
|
keyLength,
|
||||||
|
encryptMetadata
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!encryptionKey) {
|
||||||
|
throw new PasswordException(
|
||||||
|
"Incorrect Password",
|
||||||
|
PasswordResponses.INCORRECT_PASSWORD
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.encryptionKey = encryptionKey;
|
||||||
|
|
||||||
|
if (algorithm >= 4) {
|
||||||
|
var cf = dict.get("CF");
|
||||||
|
if (isDict(cf)) {
|
||||||
|
// The 'CF' dictionary itself should not be encrypted, and by setting
|
||||||
|
// `suppressEncryption` we can prevent an infinite loop inside of
|
||||||
|
// `XRef_fetchUncompressed` if the dictionary contains indirect
|
||||||
|
// objects (fixes issue7665.pdf).
|
||||||
|
cf.suppressEncryption = true;
|
||||||
|
}
|
||||||
|
this.cf = cf;
|
||||||
|
this.stmf = dict.get("StmF") || identityName;
|
||||||
|
this.strf = dict.get("StrF") || identityName;
|
||||||
|
this.eff = dict.get("EFF") || this.stmf;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
createCipherTransform(num, gen) {
|
||||||
if (this.algorithm === 4 || this.algorithm === 5) {
|
if (this.algorithm === 4 || this.algorithm === 5) {
|
||||||
return new CipherTransform(
|
return new CipherTransform(
|
||||||
buildCipherConstructor(
|
buildCipherConstructor(
|
||||||
@ -1916,13 +1883,18 @@ var CipherTransformFactory = (function CipherTransformFactoryClosure() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
// algorithms 1 and 2
|
// algorithms 1 and 2
|
||||||
var key = buildObjectKey(num, gen, this.encryptionKey, false);
|
var key = buildObjectKey(
|
||||||
|
num,
|
||||||
|
gen,
|
||||||
|
this.encryptionKey,
|
||||||
|
/* isAes = */ false
|
||||||
|
);
|
||||||
var cipherConstructor = function buildCipherCipherConstructor() {
|
var cipherConstructor = function buildCipherCipherConstructor() {
|
||||||
return new ARCFourCipher(key);
|
return new ARCFourCipher(key);
|
||||||
};
|
};
|
||||||
return new CipherTransform(cipherConstructor, cipherConstructor);
|
return new CipherTransform(cipherConstructor, cipherConstructor);
|
||||||
},
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
return CipherTransformFactory;
|
return CipherTransformFactory;
|
||||||
})();
|
})();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user