pdf-lite 1.0.2 → 1.0.4
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/EXAMPLES.md +354 -0
- package/README.md +7 -3
- package/dist/core/index.d.ts +48 -0
- package/dist/core/index.js +48 -0
- package/dist/core/objects/pdf-array.d.ts +1 -1
- package/dist/core/objects/pdf-indirect-object.d.ts +1 -1
- package/dist/core/objects/pdf-start-xref.d.ts +1 -1
- package/dist/core/objects/pdf-xref-table.d.ts +1 -1
- package/dist/core/tokens/number-token.js +11 -6
- package/dist/crypto/index.d.ts +11 -0
- package/dist/crypto/index.js +11 -0
- package/dist/crypto/key-derivation/key-derivation.d.ts +0 -14
- package/dist/crypto/key-derivation/key-derivation.js +1 -26
- package/dist/crypto/key-gen/key-gen-rc4-128.js +1 -2
- package/dist/crypto/key-gen/key-gen-rc4-40.js +1 -2
- package/dist/filters/index.d.ts +7 -0
- package/dist/filters/index.js +7 -0
- package/dist/index.d.ts +5 -1
- package/dist/index.js +5 -1
- package/dist/pdf/index.d.ts +5 -4
- package/dist/pdf/index.js +5 -4
- package/dist/pdf/pdf-document.d.ts +9 -3
- package/dist/pdf/pdf-document.js +15 -3
- package/dist/security/index.d.ts +13 -0
- package/dist/security/index.js +13 -0
- package/dist/signing/index.d.ts +5 -3
- package/dist/signing/index.js +5 -3
- package/dist/signing/signatures/adbe-pkcs7-detached.d.ts +7 -0
- package/dist/signing/signatures/adbe-pkcs7-detached.js +36 -0
- package/dist/signing/signatures/adbe-pkcs7-sha1.d.ts +7 -0
- package/dist/signing/signatures/adbe-pkcs7-sha1.js +54 -0
- package/dist/signing/signatures/adbe-x509-rsa-sha1.d.ts +7 -0
- package/dist/signing/signatures/adbe-x509-rsa-sha1.js +77 -0
- package/dist/signing/signatures/base.d.ts +16 -1
- package/dist/signing/signatures/base.js +18 -0
- package/dist/signing/signatures/etsi-cades-detached.d.ts +7 -0
- package/dist/signing/signatures/etsi-cades-detached.js +36 -0
- package/dist/signing/signatures/etsi-rfc3161.d.ts +7 -0
- package/dist/signing/signatures/etsi-rfc3161.js +102 -0
- package/dist/signing/signer.d.ts +55 -0
- package/dist/signing/signer.js +196 -1
- package/dist/signing/types.d.ts +26 -0
- package/dist/utils/index.d.ts +17 -0
- package/dist/utils/index.js +17 -0
- package/package.json +3 -3
package/dist/signing/signer.d.ts
CHANGED
|
@@ -1,4 +1,24 @@
|
|
|
1
1
|
import { PdfDocument } from '../pdf/pdf-document';
|
|
2
|
+
import { PdfSignatureObject } from './signatures';
|
|
3
|
+
import { PdfSignatureVerificationResult, CertificateValidationOptions, PdfSignatureSubType } from './types';
|
|
4
|
+
/**
|
|
5
|
+
* Result of verifying all signatures in a document.
|
|
6
|
+
*/
|
|
7
|
+
export type PdfDocumentVerificationResult = {
|
|
8
|
+
/** Whether all signatures in the document are valid. */
|
|
9
|
+
valid: boolean;
|
|
10
|
+
/** Individual signature verification results. */
|
|
11
|
+
signatures: {
|
|
12
|
+
/** The signature subfilter type. */
|
|
13
|
+
type: PdfSignatureSubType;
|
|
14
|
+
/** Index of the signature in the document. */
|
|
15
|
+
index: number;
|
|
16
|
+
/** The signature object. */
|
|
17
|
+
signature: PdfSignatureObject;
|
|
18
|
+
/** The verification result. */
|
|
19
|
+
result: PdfSignatureVerificationResult;
|
|
20
|
+
}[];
|
|
21
|
+
};
|
|
2
22
|
/**
|
|
3
23
|
* Handles digital signing operations for PDF documents.
|
|
4
24
|
* Processes signature objects and optionally stores revocation information in the DSS.
|
|
@@ -20,4 +40,39 @@ export declare class PdfSigner {
|
|
|
20
40
|
* @returns The signed document.
|
|
21
41
|
*/
|
|
22
42
|
sign(document: PdfDocument): Promise<PdfDocument>;
|
|
43
|
+
/**
|
|
44
|
+
* Instantiates the appropriate signature object based on SubFilter type.
|
|
45
|
+
*
|
|
46
|
+
* @param signatureDict - The signature dictionary.
|
|
47
|
+
* @returns A properly typed PdfSignatureObject subclass.
|
|
48
|
+
*/
|
|
49
|
+
private instantiateSignatureObject;
|
|
50
|
+
/**
|
|
51
|
+
* Verifies all signatures in the document.
|
|
52
|
+
* First serializes the document to bytes and reloads it to ensure signatures
|
|
53
|
+
* are properly deserialized into the correct classes before verification.
|
|
54
|
+
* Then searches for signature objects, computes their byte ranges, and verifies each one.
|
|
55
|
+
*
|
|
56
|
+
* @param document - The PDF document to verify.
|
|
57
|
+
* @param options - Optional verification options.
|
|
58
|
+
* @returns The verification result for all signatures.
|
|
59
|
+
*
|
|
60
|
+
* @example
|
|
61
|
+
* ```typescript
|
|
62
|
+
* const signer = new PdfSigner()
|
|
63
|
+
* const result = await signer.verify(document)
|
|
64
|
+
* if (result.valid) {
|
|
65
|
+
* console.log('All signatures are valid')
|
|
66
|
+
* } else {
|
|
67
|
+
* result.signatures.forEach(sig => {
|
|
68
|
+
* if (!sig.result.valid) {
|
|
69
|
+
* console.log(`Signature ${sig.index} invalid:`, sig.result.reasons)
|
|
70
|
+
* }
|
|
71
|
+
* })
|
|
72
|
+
* }
|
|
73
|
+
* ```
|
|
74
|
+
*/
|
|
75
|
+
verify(document: PdfDocument, options?: {
|
|
76
|
+
certificateValidation?: CertificateValidationOptions | boolean;
|
|
77
|
+
}): Promise<PdfDocumentVerificationResult>;
|
|
23
78
|
}
|
package/dist/signing/signer.js
CHANGED
|
@@ -3,7 +3,11 @@ import { PdfHexadecimalToken } from '../core/tokens/hexadecimal-token';
|
|
|
3
3
|
import { PdfNameToken } from '../core/tokens/name-token';
|
|
4
4
|
import { concatUint8Arrays } from '../utils/concatUint8Arrays';
|
|
5
5
|
import { PdfDocumentSecurityStoreObject } from './document-security-store';
|
|
6
|
-
import { PdfSignatureObject } from './signatures';
|
|
6
|
+
import { PdfSignatureObject, PdfAdbePkcs7DetachedSignatureObject, PdfAdbePkcs7Sha1SignatureObject, PdfAdbePkcsX509RsaSha1SignatureObject, PdfEtsiCadesDetachedSignatureObject, PdfEtsiRfc3161SignatureObject, PdfSignatureDictionary, } from './signatures';
|
|
7
|
+
import { PdfNumber } from '../core/objects/pdf-number';
|
|
8
|
+
import { PdfIndirectObject } from '../core/objects/pdf-indirect-object';
|
|
9
|
+
import { PdfDictionary } from '../core/objects/pdf-dictionary';
|
|
10
|
+
import { PdfArray } from '../core/objects/pdf-array';
|
|
7
11
|
/**
|
|
8
12
|
* Handles digital signing operations for PDF documents.
|
|
9
13
|
* Processes signature objects and optionally stores revocation information in the DSS.
|
|
@@ -89,4 +93,195 @@ export class PdfSigner {
|
|
|
89
93
|
}
|
|
90
94
|
return document;
|
|
91
95
|
}
|
|
96
|
+
/**
|
|
97
|
+
* Instantiates the appropriate signature object based on SubFilter type.
|
|
98
|
+
*
|
|
99
|
+
* @param signatureDict - The signature dictionary.
|
|
100
|
+
* @returns A properly typed PdfSignatureObject subclass.
|
|
101
|
+
*/
|
|
102
|
+
instantiateSignatureObject(signatureDict) {
|
|
103
|
+
const content = signatureDict.content;
|
|
104
|
+
const subFilter = content.get('SubFilter').value;
|
|
105
|
+
// Create a PdfSignatureDictionary wrapper
|
|
106
|
+
const sigDict = new PdfSignatureDictionary({
|
|
107
|
+
Type: content.get('Type'),
|
|
108
|
+
Filter: content.get('Filter'),
|
|
109
|
+
SubFilter: content.get('SubFilter'),
|
|
110
|
+
Reason: content.get('Reason'),
|
|
111
|
+
M: content.get('M'),
|
|
112
|
+
Name: content.get('Name'),
|
|
113
|
+
Reference: content.get('Reference'),
|
|
114
|
+
ContactInfo: content.get('ContactInfo'),
|
|
115
|
+
Location: content.get('Location'),
|
|
116
|
+
Cert: content.get('Cert'),
|
|
117
|
+
ByteRange: content.get('ByteRange'),
|
|
118
|
+
Contents: content.get('Contents'),
|
|
119
|
+
});
|
|
120
|
+
// Instantiate the appropriate signature type based on SubFilter
|
|
121
|
+
let signatureObj;
|
|
122
|
+
switch (subFilter) {
|
|
123
|
+
case 'adbe.pkcs7.detached':
|
|
124
|
+
signatureObj = new PdfAdbePkcs7DetachedSignatureObject({
|
|
125
|
+
privateKey: new Uint8Array(),
|
|
126
|
+
certificate: new Uint8Array(),
|
|
127
|
+
});
|
|
128
|
+
break;
|
|
129
|
+
case 'adbe.pkcs7.sha1':
|
|
130
|
+
signatureObj = new PdfAdbePkcs7Sha1SignatureObject({
|
|
131
|
+
privateKey: new Uint8Array(),
|
|
132
|
+
certificate: new Uint8Array(),
|
|
133
|
+
});
|
|
134
|
+
break;
|
|
135
|
+
case 'adbe.x509.rsa_sha1':
|
|
136
|
+
signatureObj = new PdfAdbePkcsX509RsaSha1SignatureObject({
|
|
137
|
+
privateKey: new Uint8Array(),
|
|
138
|
+
certificate: new Uint8Array(),
|
|
139
|
+
});
|
|
140
|
+
break;
|
|
141
|
+
case 'ETSI.CAdES.detached':
|
|
142
|
+
signatureObj = new PdfEtsiCadesDetachedSignatureObject({
|
|
143
|
+
privateKey: new Uint8Array(),
|
|
144
|
+
certificate: new Uint8Array(),
|
|
145
|
+
});
|
|
146
|
+
break;
|
|
147
|
+
case 'ETSI.RFC3161':
|
|
148
|
+
signatureObj = new PdfEtsiRfc3161SignatureObject({
|
|
149
|
+
timeStampAuthority: {
|
|
150
|
+
url: '',
|
|
151
|
+
},
|
|
152
|
+
});
|
|
153
|
+
break;
|
|
154
|
+
default:
|
|
155
|
+
throw new Error(`Unsupported signature SubFilter type: ${subFilter}`);
|
|
156
|
+
}
|
|
157
|
+
// Replace the content with the actual signature dictionary
|
|
158
|
+
signatureObj.content = sigDict;
|
|
159
|
+
signatureObj.objectNumber = signatureDict.objectNumber;
|
|
160
|
+
signatureObj.generationNumber = signatureDict.generationNumber;
|
|
161
|
+
return signatureObj;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Verifies all signatures in the document.
|
|
165
|
+
* First serializes the document to bytes and reloads it to ensure signatures
|
|
166
|
+
* are properly deserialized into the correct classes before verification.
|
|
167
|
+
* Then searches for signature objects, computes their byte ranges, and verifies each one.
|
|
168
|
+
*
|
|
169
|
+
* @param document - The PDF document to verify.
|
|
170
|
+
* @param options - Optional verification options.
|
|
171
|
+
* @returns The verification result for all signatures.
|
|
172
|
+
*
|
|
173
|
+
* @example
|
|
174
|
+
* ```typescript
|
|
175
|
+
* const signer = new PdfSigner()
|
|
176
|
+
* const result = await signer.verify(document)
|
|
177
|
+
* if (result.valid) {
|
|
178
|
+
* console.log('All signatures are valid')
|
|
179
|
+
* } else {
|
|
180
|
+
* result.signatures.forEach(sig => {
|
|
181
|
+
* if (!sig.result.valid) {
|
|
182
|
+
* console.log(`Signature ${sig.index} invalid:`, sig.result.reasons)
|
|
183
|
+
* }
|
|
184
|
+
* })
|
|
185
|
+
* }
|
|
186
|
+
* ```
|
|
187
|
+
*/
|
|
188
|
+
async verify(document, options) {
|
|
189
|
+
const documentBytes = document.toBytes();
|
|
190
|
+
const results = [];
|
|
191
|
+
let allValid = true;
|
|
192
|
+
const documentObjects = document.objects;
|
|
193
|
+
for (let i = 0; i < documentObjects.length; i++) {
|
|
194
|
+
const obj = documentObjects[i];
|
|
195
|
+
if (!(obj instanceof PdfIndirectObject)) {
|
|
196
|
+
continue;
|
|
197
|
+
}
|
|
198
|
+
if (!(obj.content instanceof PdfDictionary)) {
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
// Check if this is a signature dictionary by looking for Type = /Sig or SubFilter
|
|
202
|
+
const typeEntry = obj.content.get('Type');
|
|
203
|
+
const subFilterEntry = obj.content.get('SubFilter');
|
|
204
|
+
// A signature can be identified by Type=/Sig or by having a SubFilter entry
|
|
205
|
+
const isSignature = (typeEntry && typeEntry.toString() === '/Sig') ||
|
|
206
|
+
(subFilterEntry &&
|
|
207
|
+
subFilterEntry.toString().startsWith('/adbe.')) ||
|
|
208
|
+
subFilterEntry?.toString().startsWith('/ETSI.');
|
|
209
|
+
if (!isSignature) {
|
|
210
|
+
continue;
|
|
211
|
+
}
|
|
212
|
+
const signatureDict = obj;
|
|
213
|
+
const subFilter = signatureDict.content.get('SubFilter').value;
|
|
214
|
+
// Instantiate the correct signature type
|
|
215
|
+
const signature = this.instantiateSignatureObject(signatureDict);
|
|
216
|
+
// Get the ByteRange from the signature dictionary
|
|
217
|
+
const byteRangeEntry = signatureDict.content.get('ByteRange');
|
|
218
|
+
if (!byteRangeEntry) {
|
|
219
|
+
results.push({
|
|
220
|
+
type: subFilter,
|
|
221
|
+
index: i,
|
|
222
|
+
signature,
|
|
223
|
+
result: {
|
|
224
|
+
valid: false,
|
|
225
|
+
reasons: ['Signature is missing ByteRange entry'],
|
|
226
|
+
},
|
|
227
|
+
});
|
|
228
|
+
allValid = false;
|
|
229
|
+
continue;
|
|
230
|
+
}
|
|
231
|
+
// Extract the byte range values
|
|
232
|
+
const byteRangeArray = byteRangeEntry.as(PdfArray);
|
|
233
|
+
if (!byteRangeArray) {
|
|
234
|
+
results.push({
|
|
235
|
+
type: subFilter,
|
|
236
|
+
index: i,
|
|
237
|
+
signature,
|
|
238
|
+
result: {
|
|
239
|
+
valid: false,
|
|
240
|
+
reasons: ['ByteRange is not an array'],
|
|
241
|
+
},
|
|
242
|
+
});
|
|
243
|
+
allValid = false;
|
|
244
|
+
continue;
|
|
245
|
+
}
|
|
246
|
+
const byteRange = byteRangeArray.items.map((item) => {
|
|
247
|
+
if (item instanceof PdfNumber) {
|
|
248
|
+
return item.value;
|
|
249
|
+
}
|
|
250
|
+
return 0;
|
|
251
|
+
});
|
|
252
|
+
if (byteRange.length !== 4) {
|
|
253
|
+
results.push({
|
|
254
|
+
type: subFilter,
|
|
255
|
+
index: i,
|
|
256
|
+
signature,
|
|
257
|
+
result: {
|
|
258
|
+
valid: false,
|
|
259
|
+
reasons: ['Invalid ByteRange format'],
|
|
260
|
+
},
|
|
261
|
+
});
|
|
262
|
+
allValid = false;
|
|
263
|
+
continue;
|
|
264
|
+
}
|
|
265
|
+
// Compute the bytes that were signed (excluding the signature contents)
|
|
266
|
+
const signedBytes = concatUint8Arrays(documentBytes.slice(byteRange[0], byteRange[0] + byteRange[1]), documentBytes.slice(byteRange[2], byteRange[2] + byteRange[3]));
|
|
267
|
+
// Verify the signature
|
|
268
|
+
const result = await signature.verify({
|
|
269
|
+
bytes: signedBytes,
|
|
270
|
+
certificateValidation: options?.certificateValidation,
|
|
271
|
+
});
|
|
272
|
+
results.push({
|
|
273
|
+
type: subFilter,
|
|
274
|
+
index: i,
|
|
275
|
+
signature,
|
|
276
|
+
result,
|
|
277
|
+
});
|
|
278
|
+
if (!result.valid) {
|
|
279
|
+
allValid = false;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
return {
|
|
283
|
+
valid: allValid,
|
|
284
|
+
signatures: results,
|
|
285
|
+
};
|
|
286
|
+
}
|
|
92
287
|
}
|
package/dist/signing/types.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { HashAlgorithm } from 'pki-lite/core/crypto/index.js';
|
|
2
|
+
import type { CertificateValidationOptions, CertificateValidationResult, TrustAnchor } from 'pki-lite/core/CertificateValidator.js';
|
|
2
3
|
import { PdfDictionary } from '../core/objects/pdf-dictionary';
|
|
3
4
|
import { PdfName } from '../core/objects/pdf-name';
|
|
4
5
|
import { PdfHexadecimal } from '../core/objects/pdf-hexadecimal';
|
|
@@ -6,6 +7,7 @@ import { PdfArray } from '../core/objects/pdf-array';
|
|
|
6
7
|
import { PdfNumber } from '../core/objects/pdf-number';
|
|
7
8
|
import { PdfString } from '../core/objects/pdf-string';
|
|
8
9
|
import { ByteArray } from '../types';
|
|
10
|
+
export type { CertificateValidationOptions, CertificateValidationResult, TrustAnchor, };
|
|
9
11
|
/**
|
|
10
12
|
* PDF signature subfilter types defining the signature format.
|
|
11
13
|
* - 'adbe.pkcs7.detached': PKCS#7 detached signature
|
|
@@ -76,3 +78,27 @@ export type SignaturePolicyDocument = {
|
|
|
76
78
|
/** Hash algorithm used for the policy document. */
|
|
77
79
|
hashAlgorithm: HashAlgorithm;
|
|
78
80
|
};
|
|
81
|
+
/**
|
|
82
|
+
* Result of a PDF signature verification operation.
|
|
83
|
+
*/
|
|
84
|
+
export type PdfSignatureVerificationResult = {
|
|
85
|
+
/** Whether the signature is valid. */
|
|
86
|
+
valid: boolean;
|
|
87
|
+
/** Reasons for verification failure, if any. */
|
|
88
|
+
reasons?: string[];
|
|
89
|
+
/** Certificate validation result, if certificate validation was performed. */
|
|
90
|
+
certificateValidationResult?: CertificateValidationResult;
|
|
91
|
+
};
|
|
92
|
+
/**
|
|
93
|
+
* Options for PDF signature verification.
|
|
94
|
+
*/
|
|
95
|
+
export type PdfSignatureVerificationOptions = {
|
|
96
|
+
/** The original document bytes that were signed. */
|
|
97
|
+
bytes: ByteArray;
|
|
98
|
+
/**
|
|
99
|
+
* Certificate validation options.
|
|
100
|
+
* Pass `true` to use default certificate validation, or provide custom options.
|
|
101
|
+
* Pass `undefined` or `false` to skip certificate validation.
|
|
102
|
+
*/
|
|
103
|
+
certificateValidation?: CertificateValidationOptions | boolean;
|
|
104
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export * from './IterableReadableStream.js';
|
|
2
|
+
export * from './algos.js';
|
|
3
|
+
export * from './assert.js';
|
|
4
|
+
export * from './bytesToHex.js';
|
|
5
|
+
export * from './bytesToHexBytes.js';
|
|
6
|
+
export * from './bytesToString.js';
|
|
7
|
+
export * from './concatUint8Arrays.js';
|
|
8
|
+
export * from './escapeString.js';
|
|
9
|
+
export * from './hexBytesToBytes.js';
|
|
10
|
+
export * from './hexBytesToString.js';
|
|
11
|
+
export * from './hexToBytes.js';
|
|
12
|
+
export * from './padBytes.js';
|
|
13
|
+
export * from './predictors.js';
|
|
14
|
+
export * from './replaceInBuffer.js';
|
|
15
|
+
export * from './stringToBytes.js';
|
|
16
|
+
export * from './stringToHexBytes.js';
|
|
17
|
+
export * from './unescapeString.js';
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export * from './IterableReadableStream.js';
|
|
2
|
+
export * from './algos.js';
|
|
3
|
+
export * from './assert.js';
|
|
4
|
+
export * from './bytesToHex.js';
|
|
5
|
+
export * from './bytesToHexBytes.js';
|
|
6
|
+
export * from './bytesToString.js';
|
|
7
|
+
export * from './concatUint8Arrays.js';
|
|
8
|
+
export * from './escapeString.js';
|
|
9
|
+
export * from './hexBytesToBytes.js';
|
|
10
|
+
export * from './hexBytesToString.js';
|
|
11
|
+
export * from './hexToBytes.js';
|
|
12
|
+
export * from './padBytes.js';
|
|
13
|
+
export * from './predictors.js';
|
|
14
|
+
export * from './replaceInBuffer.js';
|
|
15
|
+
export * from './stringToBytes.js';
|
|
16
|
+
export * from './stringToHexBytes.js';
|
|
17
|
+
export * from './unescapeString.js';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pdf-lite",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.4",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -54,8 +54,8 @@
|
|
|
54
54
|
],
|
|
55
55
|
"dependencies": {
|
|
56
56
|
"pako": "2.1.0",
|
|
57
|
-
"pki-lite": "^1.0.
|
|
58
|
-
"pki-lite-crypto-extended": "^1.0.
|
|
57
|
+
"pki-lite": "^1.0.12",
|
|
58
|
+
"pki-lite-crypto-extended": "^1.0.12"
|
|
59
59
|
},
|
|
60
60
|
"scripts": {
|
|
61
61
|
"test:acceptance": "vitest run test/acceptance",
|