rn-pdf-decrypt 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +116 -0
- package/dist/crypto-aes.js +192 -0
- package/dist/crypto-aes.mjs +190 -0
- package/dist/crypto-rc4.js +185 -0
- package/dist/crypto-rc4.mjs +183 -0
- package/dist/index.d.ts +33 -0
- package/dist/index.js +5 -0
- package/dist/index.mjs +3 -0
- package/dist/pdf-decrypt.js +883 -0
- package/dist/pdf-decrypt.mjs +886 -0
- package/package.json +68 -0
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* rn-pdf-decrypt - RC4 cryptographic utilities
|
|
3
|
+
* React Native compatible fork of @pdfsmaller/pdf-decrypt
|
|
4
|
+
*
|
|
5
|
+
* @author imdewan (https://github.com/imdewan/rn-pdf-decrypt)
|
|
6
|
+
* @license MIT
|
|
7
|
+
* @see https://github.com/imdewan/rn-pdf-decrypt
|
|
8
|
+
*
|
|
9
|
+
* This minimal cryptographic implementation was built to solve the "impossible"
|
|
10
|
+
* problem of real PDF encryption within Cloudflare Workers' 1MB limit.
|
|
11
|
+
* Total size: ~7KB for complete PDF encryption!
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
// Minimal cryptographic functions for PDF encryption
|
|
15
|
+
// Implements only what's needed for PDF Standard Security Handler
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Minimal MD5 implementation
|
|
19
|
+
* Based on the MD5 algorithm - only what's needed for PDF encryption
|
|
20
|
+
* Part of rn-pdf-decrypt
|
|
21
|
+
*/
|
|
22
|
+
export function md5(data) {
|
|
23
|
+
const bytes = typeof data === 'string' ? new TextEncoder().encode(data) : data;
|
|
24
|
+
|
|
25
|
+
// Initialize MD5 constants
|
|
26
|
+
const S = [
|
|
27
|
+
7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,
|
|
28
|
+
5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20,
|
|
29
|
+
4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23,
|
|
30
|
+
6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21
|
|
31
|
+
];
|
|
32
|
+
|
|
33
|
+
const K = new Uint32Array([
|
|
34
|
+
0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee,
|
|
35
|
+
0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
|
|
36
|
+
0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be,
|
|
37
|
+
0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,
|
|
38
|
+
0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa,
|
|
39
|
+
0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
|
|
40
|
+
0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed,
|
|
41
|
+
0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,
|
|
42
|
+
0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c,
|
|
43
|
+
0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
|
|
44
|
+
0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05,
|
|
45
|
+
0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
|
|
46
|
+
0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039,
|
|
47
|
+
0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
|
|
48
|
+
0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1,
|
|
49
|
+
0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391
|
|
50
|
+
]);
|
|
51
|
+
|
|
52
|
+
// Initialize hash values
|
|
53
|
+
let a0 = 0x67452301;
|
|
54
|
+
let b0 = 0xefcdab89;
|
|
55
|
+
let c0 = 0x98badcfe;
|
|
56
|
+
let d0 = 0x10325476;
|
|
57
|
+
|
|
58
|
+
// Pre-processing
|
|
59
|
+
const msgLen = bytes.length;
|
|
60
|
+
const msgBitLen = msgLen * 8;
|
|
61
|
+
const msgLenPadded = ((msgLen + 9 + 63) & ~63);
|
|
62
|
+
const msg = new Uint8Array(msgLenPadded);
|
|
63
|
+
msg.set(bytes);
|
|
64
|
+
msg[msgLen] = 0x80;
|
|
65
|
+
|
|
66
|
+
// Append length in bits
|
|
67
|
+
const dataView = new DataView(msg.buffer);
|
|
68
|
+
dataView.setUint32(msgLenPadded - 8, msgBitLen, true);
|
|
69
|
+
dataView.setUint32(msgLenPadded - 4, 0, true);
|
|
70
|
+
|
|
71
|
+
// Process message in 512-bit chunks
|
|
72
|
+
for (let offset = 0; offset < msgLenPadded; offset += 64) {
|
|
73
|
+
const chunk = new Uint32Array(msg.buffer, offset, 16);
|
|
74
|
+
|
|
75
|
+
let a = a0, b = b0, c = c0, d = d0;
|
|
76
|
+
|
|
77
|
+
for (let i = 0; i < 64; i++) {
|
|
78
|
+
let f, g;
|
|
79
|
+
|
|
80
|
+
if (i < 16) {
|
|
81
|
+
f = (b & c) | ((~b) & d);
|
|
82
|
+
g = i;
|
|
83
|
+
} else if (i < 32) {
|
|
84
|
+
f = (d & b) | ((~d) & c);
|
|
85
|
+
g = (5 * i + 1) % 16;
|
|
86
|
+
} else if (i < 48) {
|
|
87
|
+
f = b ^ c ^ d;
|
|
88
|
+
g = (3 * i + 5) % 16;
|
|
89
|
+
} else {
|
|
90
|
+
f = c ^ (b | (~d));
|
|
91
|
+
g = (7 * i) % 16;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
f = (f + a + K[i] + chunk[g]) >>> 0;
|
|
95
|
+
a = d;
|
|
96
|
+
d = c;
|
|
97
|
+
c = b;
|
|
98
|
+
b = (b + ((f << S[i]) | (f >>> (32 - S[i])))) >>> 0;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
a0 = (a0 + a) >>> 0;
|
|
102
|
+
b0 = (b0 + b) >>> 0;
|
|
103
|
+
c0 = (c0 + c) >>> 0;
|
|
104
|
+
d0 = (d0 + d) >>> 0;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Produce the final hash value
|
|
108
|
+
const result = new Uint8Array(16);
|
|
109
|
+
const view = new DataView(result.buffer);
|
|
110
|
+
view.setUint32(0, a0, true);
|
|
111
|
+
view.setUint32(4, b0, true);
|
|
112
|
+
view.setUint32(8, c0, true);
|
|
113
|
+
view.setUint32(12, d0, true);
|
|
114
|
+
|
|
115
|
+
return result;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* RC4 encryption/decryption
|
|
120
|
+
* RC4 is symmetric, so encryption and decryption are the same operation
|
|
121
|
+
* Part of rn-pdf-decrypt
|
|
122
|
+
*/
|
|
123
|
+
export class RC4 {
|
|
124
|
+
constructor(key) {
|
|
125
|
+
this.s = new Uint8Array(256);
|
|
126
|
+
this.i = 0;
|
|
127
|
+
this.j = 0;
|
|
128
|
+
|
|
129
|
+
// Key scheduling algorithm (KSA)
|
|
130
|
+
for (let i = 0; i < 256; i++) {
|
|
131
|
+
this.s[i] = i;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
let j = 0;
|
|
135
|
+
for (let i = 0; i < 256; i++) {
|
|
136
|
+
j = (j + this.s[i] + key[i % key.length]) & 0xFF;
|
|
137
|
+
// Swap
|
|
138
|
+
[this.s[i], this.s[j]] = [this.s[j], this.s[i]];
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Encrypt/decrypt data
|
|
144
|
+
* @param {Uint8Array} data - Data to encrypt or decrypt
|
|
145
|
+
* @returns {Uint8Array} - Encrypted/decrypted data
|
|
146
|
+
*/
|
|
147
|
+
process(data) {
|
|
148
|
+
const result = new Uint8Array(data.length);
|
|
149
|
+
|
|
150
|
+
for (let k = 0; k < data.length; k++) {
|
|
151
|
+
this.i = (this.i + 1) & 0xFF;
|
|
152
|
+
this.j = (this.j + this.s[this.i]) & 0xFF;
|
|
153
|
+
|
|
154
|
+
// Swap
|
|
155
|
+
[this.s[this.i], this.s[this.j]] = [this.s[this.j], this.s[this.i]];
|
|
156
|
+
|
|
157
|
+
const t = (this.s[this.i] + this.s[this.j]) & 0xFF;
|
|
158
|
+
result[k] = data[k] ^ this.s[t];
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return result;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Convert hex string to Uint8Array
|
|
167
|
+
*/
|
|
168
|
+
export function hexToBytes(hex) {
|
|
169
|
+
const bytes = new Uint8Array(hex.length / 2);
|
|
170
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
171
|
+
bytes[i] = parseInt(hex.substr(i * 2, 2), 16);
|
|
172
|
+
}
|
|
173
|
+
return bytes;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Convert Uint8Array to hex string
|
|
178
|
+
*/
|
|
179
|
+
export function bytesToHex(bytes) {
|
|
180
|
+
return Array.from(bytes)
|
|
181
|
+
.map(b => b.toString(16).padStart(2, '0'))
|
|
182
|
+
.join('');
|
|
183
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export declare function decryptPDF(
|
|
2
|
+
pdfBytes: Uint8Array,
|
|
3
|
+
password: string
|
|
4
|
+
): Promise<Uint8Array>;
|
|
5
|
+
|
|
6
|
+
export declare function isEncrypted(
|
|
7
|
+
pdfBytes: Uint8Array
|
|
8
|
+
): Promise<{
|
|
9
|
+
encrypted: boolean;
|
|
10
|
+
algorithm?: 'AES-256' | 'RC4';
|
|
11
|
+
version?: number;
|
|
12
|
+
revision?: number;
|
|
13
|
+
keyLength?: number;
|
|
14
|
+
}>;
|
|
15
|
+
|
|
16
|
+
export declare function md5(data: Uint8Array | string): Uint8Array;
|
|
17
|
+
export declare class RC4 {
|
|
18
|
+
constructor(key: Uint8Array);
|
|
19
|
+
process(data: Uint8Array): Uint8Array;
|
|
20
|
+
}
|
|
21
|
+
export declare function hexToBytes(hex: string): Uint8Array;
|
|
22
|
+
export declare function bytesToHex(bytes: Uint8Array): string;
|
|
23
|
+
|
|
24
|
+
export declare function sha256(data: Uint8Array): Promise<Uint8Array>;
|
|
25
|
+
export declare function sha384(data: Uint8Array): Promise<Uint8Array>;
|
|
26
|
+
export declare function sha512(data: Uint8Array): Promise<Uint8Array>;
|
|
27
|
+
export declare function aes256CbcDecrypt(data: Uint8Array, key: Uint8Array, iv: Uint8Array): Promise<Uint8Array>;
|
|
28
|
+
export declare function aes256CbcDecryptNoPad(ciphertext: Uint8Array, key: Uint8Array, iv: Uint8Array): Promise<Uint8Array>;
|
|
29
|
+
export declare function aes256EcbDecryptBlock(block: Uint8Array, key: Uint8Array): Promise<Uint8Array>;
|
|
30
|
+
export declare function importAES256DecryptKey(key: Uint8Array): Promise<Uint8Array>;
|
|
31
|
+
export declare function aes256CbcDecryptWithKey(data: Uint8Array, key: Uint8Array, iv: Uint8Array): Promise<Uint8Array>;
|
|
32
|
+
export declare function computeHash2B(password: Uint8Array, salt: Uint8Array, userKey: Uint8Array): Promise<Uint8Array>;
|
|
33
|
+
export declare function concat(...arrays: Uint8Array[]): Uint8Array;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
const { decryptPDF, isEncrypted } = require('./pdf-decrypt.js');
|
|
2
|
+
const { md5, RC4, hexToBytes, bytesToHex } = require('./crypto-rc4.js');
|
|
3
|
+
const { sha256, sha384, sha512, aes256CbcDecrypt, aes256CbcDecryptNoPad, aes256EcbDecryptBlock, importAES256DecryptKey, aes256CbcDecryptWithKey, computeHash2B, concat } = require('./crypto-aes.js');
|
|
4
|
+
|
|
5
|
+
module.exports = { decryptPDF, isEncrypted, md5, RC4, hexToBytes, bytesToHex, sha256, sha384, sha512, aes256CbcDecrypt, aes256CbcDecryptNoPad, aes256EcbDecryptBlock, importAES256DecryptKey, aes256CbcDecryptWithKey, computeHash2B, concat };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { decryptPDF, isEncrypted } from './pdf-decrypt.mjs';
|
|
2
|
+
export { md5, RC4, hexToBytes, bytesToHex } from './crypto-rc4.mjs';
|
|
3
|
+
export { sha256, sha384, sha512, aes256CbcDecrypt, aes256CbcDecryptNoPad, aes256EcbDecryptBlock, importAES256DecryptKey, aes256CbcDecryptWithKey, computeHash2B, concat } from './crypto-aes.mjs';
|