@paniniapi/forensics 0.0.0-stage → 0.1.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/dist/asn1.d.ts +74 -0
- package/dist/asn1.d.ts.map +1 -0
- package/dist/asn1.js +186 -0
- package/dist/asn1.js.map +1 -0
- package/dist/consistency.d.ts +29 -0
- package/dist/consistency.d.ts.map +1 -0
- package/dist/consistency.js +163 -0
- package/dist/consistency.js.map +1 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +29 -0
- package/dist/index.js.map +1 -0
- package/dist/pdf/content.d.ts +22 -0
- package/dist/pdf/content.d.ts.map +1 -0
- package/dist/pdf/content.js +91 -0
- package/dist/pdf/content.js.map +1 -0
- package/dist/pdf/signature.d.ts +40 -0
- package/dist/pdf/signature.d.ts.map +1 -0
- package/dist/pdf/signature.js +351 -0
- package/dist/pdf/signature.js.map +1 -0
- package/dist/pdf/structure.d.ts +31 -0
- package/dist/pdf/structure.d.ts.map +1 -0
- package/dist/pdf/structure.js +172 -0
- package/dist/pdf/structure.js.map +1 -0
- package/dist/report.d.ts +29 -0
- package/dist/report.d.ts.map +1 -0
- package/dist/report.js +159 -0
- package/dist/report.js.map +1 -0
- package/dist/types.d.ts +64 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +18 -0
- package/dist/types.js.map +1 -0
- package/package.json +38 -4
- package/README.md +0 -3
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"content.js","sourceRoot":"","sources":["../../src/pdf/content.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright 2026 Kapil — Project Panini Contributors
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* PDF digital-signature validation (ISO 32000 / RFC 5652 CMS).
|
|
18
|
+
*
|
|
19
|
+
* For every `/ByteRange`+`/Contents` signature field:
|
|
20
|
+
* 1. bytes outside the signed ranges → post-sign modification;
|
|
21
|
+
* 2. hash the signed byte ranges → compare to the CMS `messageDigest`
|
|
22
|
+
* signed attribute → proves the exact bytes the signer saw;
|
|
23
|
+
* 3. verify the signerInfo signature over signedAttrs with the
|
|
24
|
+
* signer's certificate public key (node:crypto, PKCS#1 v1.5 RSA or
|
|
25
|
+
* ECDSA-DER);
|
|
26
|
+
* 4. walk the embedded certificate chain to a caller-supplied trust
|
|
27
|
+
* anchor (CCA-India roots or a bank CA loaded by the integrator).
|
|
28
|
+
*/
|
|
29
|
+
import * as crypto from 'node:crypto';
|
|
30
|
+
import type { SignatureCheck } from '../types';
|
|
31
|
+
export interface SignatureOptions {
|
|
32
|
+
/** Trust anchors as PEM strings or X509Certificate objects. */
|
|
33
|
+
readonly trustStore?: readonly (string | crypto.X509Certificate)[];
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Validate every signature in the PDF. Never throws — errors land in
|
|
37
|
+
* `check.errors` and the check's validity fields stay null.
|
|
38
|
+
*/
|
|
39
|
+
export declare function verifyPdfSignatures(pdf: Uint8Array, opts?: SignatureOptions): readonly SignatureCheck[];
|
|
40
|
+
//# sourceMappingURL=signature.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signature.d.ts","sourceRoot":"","sources":["../../src/pdf/signature.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,MAAM,MAAM,aAAa,CAAC;AAStC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAoK/C,MAAM,WAAW,gBAAgB;IAC/B,+DAA+D;IAC/D,QAAQ,CAAC,UAAU,CAAC,EAAE,SAAS,CAAC,MAAM,GAAG,MAAM,CAAC,eAAe,CAAC,EAAE,CAAC;CACpE;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,GAAG,EAAE,UAAU,EACf,IAAI,GAAE,gBAAqB,GAC1B,SAAS,cAAc,EAAE,CAI3B"}
|
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Copyright 2026 Kapil — Project Panini Contributors
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
18
|
+
if (k2 === undefined) k2 = k;
|
|
19
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
20
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
21
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
22
|
+
}
|
|
23
|
+
Object.defineProperty(o, k2, desc);
|
|
24
|
+
}) : (function(o, m, k, k2) {
|
|
25
|
+
if (k2 === undefined) k2 = k;
|
|
26
|
+
o[k2] = m[k];
|
|
27
|
+
}));
|
|
28
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
29
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
30
|
+
}) : function(o, v) {
|
|
31
|
+
o["default"] = v;
|
|
32
|
+
});
|
|
33
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
34
|
+
var ownKeys = function(o) {
|
|
35
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
36
|
+
var ar = [];
|
|
37
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
38
|
+
return ar;
|
|
39
|
+
};
|
|
40
|
+
return ownKeys(o);
|
|
41
|
+
};
|
|
42
|
+
return function (mod) {
|
|
43
|
+
if (mod && mod.__esModule) return mod;
|
|
44
|
+
var result = {};
|
|
45
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
46
|
+
__setModuleDefault(result, mod);
|
|
47
|
+
return result;
|
|
48
|
+
};
|
|
49
|
+
})();
|
|
50
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
51
|
+
exports.verifyPdfSignatures = verifyPdfSignatures;
|
|
52
|
+
/**
|
|
53
|
+
* PDF digital-signature validation (ISO 32000 / RFC 5652 CMS).
|
|
54
|
+
*
|
|
55
|
+
* For every `/ByteRange`+`/Contents` signature field:
|
|
56
|
+
* 1. bytes outside the signed ranges → post-sign modification;
|
|
57
|
+
* 2. hash the signed byte ranges → compare to the CMS `messageDigest`
|
|
58
|
+
* signed attribute → proves the exact bytes the signer saw;
|
|
59
|
+
* 3. verify the signerInfo signature over signedAttrs with the
|
|
60
|
+
* signer's certificate public key (node:crypto, PKCS#1 v1.5 RSA or
|
|
61
|
+
* ECDSA-DER);
|
|
62
|
+
* 4. walk the embedded certificate chain to a caller-supplied trust
|
|
63
|
+
* anchor (CCA-India roots or a bank CA loaded by the integrator).
|
|
64
|
+
*/
|
|
65
|
+
const crypto = __importStar(require("node:crypto"));
|
|
66
|
+
const asn1_1 = require("../asn1");
|
|
67
|
+
const LATIN1 = 'latin1';
|
|
68
|
+
const OID = {
|
|
69
|
+
signedData: '1.2.840.113549.1.2.1',
|
|
70
|
+
messageDigest: '1.2.840.113549.1.9.4',
|
|
71
|
+
signingTime: '1.2.840.113549.1.9.5',
|
|
72
|
+
};
|
|
73
|
+
const DIGEST_OID_TO_NODE = {
|
|
74
|
+
'1.3.14.3.2.26': 'sha1',
|
|
75
|
+
'2.16.840.1.101.3.4.2.1': 'sha256',
|
|
76
|
+
'2.16.840.1.101.3.4.2.2': 'sha384',
|
|
77
|
+
'2.16.840.1.101.3.4.2.3': 'sha512',
|
|
78
|
+
'2.16.840.1.101.3.4.2.4': 'sha224',
|
|
79
|
+
};
|
|
80
|
+
const SIG_OID = {
|
|
81
|
+
'1.2.840.113549.1.1.5': { hash: 'sha1', kind: 'rsa' },
|
|
82
|
+
'1.2.840.113549.1.1.11': { hash: 'sha256', kind: 'rsa' },
|
|
83
|
+
'1.2.840.113549.1.1.12': { hash: 'sha384', kind: 'rsa' },
|
|
84
|
+
'1.2.840.113549.1.1.13': { hash: 'sha512', kind: 'rsa' },
|
|
85
|
+
'1.2.840.10045.4.3.2': { hash: 'sha256', kind: 'ecdsa' },
|
|
86
|
+
'1.2.840.10045.4.3.3': { hash: 'sha384', kind: 'ecdsa' },
|
|
87
|
+
'1.2.840.10045.4.3.4': { hash: 'sha512', kind: 'ecdsa' },
|
|
88
|
+
};
|
|
89
|
+
function findSigFields(latin) {
|
|
90
|
+
const out = [];
|
|
91
|
+
const re = /\/ByteRange\s*\[\s*(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s*\]/g;
|
|
92
|
+
let m;
|
|
93
|
+
while ((m = re.exec(latin)) !== null) {
|
|
94
|
+
// /Contents is the nearest hex string after ByteRange within the sig
|
|
95
|
+
// dict. CMS blobs are zero-padded — the hex span can be tens of KB.
|
|
96
|
+
const after = latin.slice(m.index, m.index + (1 << 18));
|
|
97
|
+
const cm = /\/Contents\s*<([0-9a-fA-F\s]*)>/.exec(after);
|
|
98
|
+
if (cm === null)
|
|
99
|
+
continue;
|
|
100
|
+
const contents = Buffer.from(cm[1].replace(/\s/g, ''), 'hex');
|
|
101
|
+
const nm = /\/Name\s*\(([^)]*)\)/.exec(after);
|
|
102
|
+
out.push({
|
|
103
|
+
byteRange: [Number(m[1]), Number(m[2]), Number(m[3]), Number(m[4])],
|
|
104
|
+
contents,
|
|
105
|
+
name: nm !== null ? nm[1] : null,
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
return out;
|
|
109
|
+
}
|
|
110
|
+
/** SET OF header (universal 17, constructed) wrapping signedAttrs content —
|
|
111
|
+
* CMS signs the attrs re-tagged as SET OF, not the [0] context form. */
|
|
112
|
+
function setOfWrap(content) {
|
|
113
|
+
const len = content.length;
|
|
114
|
+
let header;
|
|
115
|
+
if (len < 0x80)
|
|
116
|
+
header = Buffer.from([0x31, len]);
|
|
117
|
+
else if (len < 0x100)
|
|
118
|
+
header = Buffer.from([0x31, 0x81, len]);
|
|
119
|
+
else
|
|
120
|
+
header = Buffer.from([0x31, 0x82, len >> 8, len & 0xff]);
|
|
121
|
+
return Buffer.concat([header, Buffer.from(content)]);
|
|
122
|
+
}
|
|
123
|
+
function parseSignerInfo(si) {
|
|
124
|
+
let cursor = 1; // skip version
|
|
125
|
+
const sid = si.children[cursor];
|
|
126
|
+
let issuerName = null;
|
|
127
|
+
let serial = null;
|
|
128
|
+
if (sid.tagClass === 0 && sid.tagNumber === 16) {
|
|
129
|
+
// issuerAndSerialNumber
|
|
130
|
+
issuerName = (0, asn1_1.stringValue)(sid.children[0] ?? sid).slice(0, 200);
|
|
131
|
+
const serialNode = (0, asn1_1.universal)(sid, 2);
|
|
132
|
+
serial = serialNode ? intLike(serialNode) : null;
|
|
133
|
+
}
|
|
134
|
+
cursor++;
|
|
135
|
+
const digestOid = (0, asn1_1.oidValue)((0, asn1_1.universal)(si.children[cursor], 6) ?? si.children[cursor]);
|
|
136
|
+
const digestAlg = DIGEST_OID_TO_NODE[digestOid] ?? digestOid;
|
|
137
|
+
cursor++;
|
|
138
|
+
// optional signedAttrs [0]
|
|
139
|
+
let signedAttrsRaw = null;
|
|
140
|
+
let messageDigest = null;
|
|
141
|
+
let signingTime = null;
|
|
142
|
+
if (si.children[cursor].tagClass === 2 && si.children[cursor].tagNumber === 0) {
|
|
143
|
+
const attrs = si.children[cursor];
|
|
144
|
+
signedAttrsRaw = setOfWrap(attrs.content);
|
|
145
|
+
for (const attr of attrs.children) {
|
|
146
|
+
const oid = attr.children[0] ? (0, asn1_1.oidValue)(attr.children[0]) : '';
|
|
147
|
+
const valSet = attr.children[1];
|
|
148
|
+
const first = valSet?.children[0];
|
|
149
|
+
if (oid === OID.messageDigest && first !== undefined)
|
|
150
|
+
messageDigest = Buffer.from(first.content);
|
|
151
|
+
if (oid === OID.signingTime && first !== undefined)
|
|
152
|
+
signingTime = (0, asn1_1.stringValue)(first);
|
|
153
|
+
}
|
|
154
|
+
cursor++;
|
|
155
|
+
}
|
|
156
|
+
const sigAlgOid = (0, asn1_1.oidValue)((0, asn1_1.universal)(si.children[cursor], 6) ?? si.children[cursor]);
|
|
157
|
+
cursor++;
|
|
158
|
+
const signature = Buffer.from(si.children[cursor].content);
|
|
159
|
+
if (signedAttrsRaw === null)
|
|
160
|
+
throw new asn1_1.Asn1Error('signerInfo has no signedAttrs — non-CMS form');
|
|
161
|
+
return {
|
|
162
|
+
digestAlg,
|
|
163
|
+
sigAlgOid,
|
|
164
|
+
signature,
|
|
165
|
+
signedAttrsRaw,
|
|
166
|
+
messageDigest,
|
|
167
|
+
signingTime,
|
|
168
|
+
issuerName,
|
|
169
|
+
serial,
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
function intLike(node) {
|
|
173
|
+
let v = 0n;
|
|
174
|
+
for (const b of node.content)
|
|
175
|
+
v = (v << 8n) | BigInt(b);
|
|
176
|
+
return v;
|
|
177
|
+
}
|
|
178
|
+
function parseCms(der) {
|
|
179
|
+
const contentInfo = (0, asn1_1.parseDerPadded)(der);
|
|
180
|
+
const typeOid = (0, asn1_1.oidValue)(contentInfo.children[0]);
|
|
181
|
+
if (typeOid !== OID.signedData) {
|
|
182
|
+
throw new asn1_1.Asn1Error(`content type ${typeOid} — not signedData (timestamp-only or enveloped)`);
|
|
183
|
+
}
|
|
184
|
+
const signedData = contentInfo.children[1].children[0]; // [0] EXPLICIT
|
|
185
|
+
const certs = [];
|
|
186
|
+
let signerInfos;
|
|
187
|
+
for (const c of signedData.children) {
|
|
188
|
+
if (c.tagClass === 2 && c.tagNumber === 0) {
|
|
189
|
+
// certificates [0] IMPLICIT SET — children are cert SEQUENCES
|
|
190
|
+
for (const certNode of c.children) {
|
|
191
|
+
try {
|
|
192
|
+
certs.push(new crypto.X509Certificate(Buffer.from(certNode.tlv)));
|
|
193
|
+
}
|
|
194
|
+
catch {
|
|
195
|
+
/* attribute certs / non-X509 entries — skip */
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
if (c.tagClass === 0 && c.tagNumber === 17)
|
|
200
|
+
signerInfos = c; // SET OF SignerInfo
|
|
201
|
+
}
|
|
202
|
+
if (signerInfos === undefined || signerInfos.children.length === 0) {
|
|
203
|
+
throw new asn1_1.Asn1Error('CMS signedData carries no signerInfos');
|
|
204
|
+
}
|
|
205
|
+
return { signer: parseSignerInfo(signerInfos.children[0]), certs };
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Validate every signature in the PDF. Never throws — errors land in
|
|
209
|
+
* `check.errors` and the check's validity fields stay null.
|
|
210
|
+
*/
|
|
211
|
+
function verifyPdfSignatures(pdf, opts = {}) {
|
|
212
|
+
const latin = Buffer.from(pdf).toString(LATIN1);
|
|
213
|
+
const fields = findSigFields(latin);
|
|
214
|
+
return fields.map((f) => checkOne(pdf, f, opts));
|
|
215
|
+
}
|
|
216
|
+
function checkOne(pdf, f, opts) {
|
|
217
|
+
const errors = [];
|
|
218
|
+
let postSignBytes = false;
|
|
219
|
+
let digestValid = null;
|
|
220
|
+
let signatureValid = null;
|
|
221
|
+
let chainTrusted = null;
|
|
222
|
+
const [o1, l1, o2, l2] = f.byteRange;
|
|
223
|
+
if (o2 + l2 < pdf.length) {
|
|
224
|
+
postSignBytes = true;
|
|
225
|
+
errors.push(`${pdf.length - (o2 + l2)} byte(s) after the signed ByteRange`);
|
|
226
|
+
}
|
|
227
|
+
let cms;
|
|
228
|
+
try {
|
|
229
|
+
cms = parseCms(new Uint8Array(f.contents));
|
|
230
|
+
}
|
|
231
|
+
catch (e) {
|
|
232
|
+
errors.push(`CMS parse failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
233
|
+
return {
|
|
234
|
+
present: true,
|
|
235
|
+
fieldName: f.name,
|
|
236
|
+
signer: null,
|
|
237
|
+
signTime: null,
|
|
238
|
+
digestValid,
|
|
239
|
+
signatureValid,
|
|
240
|
+
chainTrusted,
|
|
241
|
+
postSignBytes,
|
|
242
|
+
errors,
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
const { signer, certs } = cms;
|
|
246
|
+
const signTime = signer.signingTime;
|
|
247
|
+
const signerCert = pickSignerCert(certs, signer);
|
|
248
|
+
const signerName = signerCert?.subject?.match(/CN=([^\n]+)/)?.[1]?.trim() ?? null;
|
|
249
|
+
// (b) byte-range digest vs messageDigest signed attribute.
|
|
250
|
+
const nodeHash = DIGEST_OID_TO_NODE[signer.digestAlg] ?? signer.digestAlg;
|
|
251
|
+
try {
|
|
252
|
+
const h = crypto.createHash(nodeHash);
|
|
253
|
+
h.update(pdf.subarray(o1, o1 + l1));
|
|
254
|
+
h.update(pdf.subarray(o2, o2 + l2));
|
|
255
|
+
const computed = h.digest();
|
|
256
|
+
if (signer.messageDigest === null) {
|
|
257
|
+
errors.push('signedAttrs carry no messageDigest — cannot bind signature to bytes');
|
|
258
|
+
}
|
|
259
|
+
else {
|
|
260
|
+
digestValid = computed.equals(signer.messageDigest);
|
|
261
|
+
if (!digestValid)
|
|
262
|
+
errors.push('messageDigest mismatch — signed bytes were altered');
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
catch (e) {
|
|
266
|
+
errors.push(`digest failed (${nodeHash}): ${e instanceof Error ? e.message : String(e)}`);
|
|
267
|
+
}
|
|
268
|
+
// (c) cryptographic signature over signedAttrs.
|
|
269
|
+
if (signerCert === null) {
|
|
270
|
+
errors.push('no signer certificate embedded');
|
|
271
|
+
}
|
|
272
|
+
else {
|
|
273
|
+
const alg = SIG_OID[signer.sigAlgOid];
|
|
274
|
+
if (alg === undefined) {
|
|
275
|
+
errors.push(`unsupported signature algorithm ${signer.sigAlgOid} (e.g. RSA-PSS)`);
|
|
276
|
+
}
|
|
277
|
+
else {
|
|
278
|
+
try {
|
|
279
|
+
const key = alg.kind === 'ecdsa'
|
|
280
|
+
? { key: signerCert.publicKey, dsaEncoding: 'der' }
|
|
281
|
+
: { key: signerCert.publicKey };
|
|
282
|
+
signatureValid = crypto.verify(alg.hash, signer.signedAttrsRaw, key, signer.signature);
|
|
283
|
+
if (!signatureValid)
|
|
284
|
+
errors.push('signature does not verify over signedAttrs');
|
|
285
|
+
}
|
|
286
|
+
catch (e) {
|
|
287
|
+
errors.push(`verify failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
// (d) chain to caller trust anchor.
|
|
292
|
+
if (opts.trustStore !== undefined && opts.trustStore.length > 0 && signerCert !== null) {
|
|
293
|
+
chainTrusted = chainToAnchor(signerCert, certs, opts.trustStore, errors);
|
|
294
|
+
}
|
|
295
|
+
return {
|
|
296
|
+
present: true,
|
|
297
|
+
fieldName: f.name,
|
|
298
|
+
signer: signerName,
|
|
299
|
+
signTime,
|
|
300
|
+
digestValid,
|
|
301
|
+
signatureValid,
|
|
302
|
+
chainTrusted,
|
|
303
|
+
postSignBytes,
|
|
304
|
+
errors,
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
function pickSignerCert(certs, signer) {
|
|
308
|
+
if (certs.length === 0)
|
|
309
|
+
return null;
|
|
310
|
+
// Match issuerAndSerialNumber when present; else the only leaf.
|
|
311
|
+
if (signer.serial !== null) {
|
|
312
|
+
const hit = certs.find((c) => c.serialNumber !== '' && BigInt(`0x${c.serialNumber}`) === signer.serial);
|
|
313
|
+
if (hit !== undefined)
|
|
314
|
+
return hit;
|
|
315
|
+
}
|
|
316
|
+
return certs[0] ?? null;
|
|
317
|
+
}
|
|
318
|
+
/** Walk issuer links among embedded certs; anchor must be in trustStore. */
|
|
319
|
+
function chainToAnchor(leaf, pool, trustStore, errors) {
|
|
320
|
+
const anchors = [];
|
|
321
|
+
for (const t of trustStore) {
|
|
322
|
+
try {
|
|
323
|
+
anchors.push(typeof t === 'string' ? new crypto.X509Certificate(t) : t);
|
|
324
|
+
}
|
|
325
|
+
catch {
|
|
326
|
+
errors.push('unparsable trust anchor skipped');
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
const trustedFp = new Set(anchors.map((a) => a.fingerprint256));
|
|
330
|
+
let cur = leaf;
|
|
331
|
+
for (let depth = 0; depth < 8; depth++) {
|
|
332
|
+
if (trustedFp.has(cur.fingerprint256))
|
|
333
|
+
return true;
|
|
334
|
+
const issuer = pool.find((c) => c.subject === cur.issuer && c !== cur) ?? cur;
|
|
335
|
+
if (issuer === cur)
|
|
336
|
+
break; // self-signed, not trusted
|
|
337
|
+
try {
|
|
338
|
+
if (!cur.verify(issuer.publicKey)) {
|
|
339
|
+
errors.push(`chain link failed: ${cur.subject.slice(0, 60)} not signed by issuer`);
|
|
340
|
+
return false;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
catch (e) {
|
|
344
|
+
errors.push(`chain verify error: ${e instanceof Error ? e.message : String(e)}`);
|
|
345
|
+
return false;
|
|
346
|
+
}
|
|
347
|
+
cur = issuer;
|
|
348
|
+
}
|
|
349
|
+
return false;
|
|
350
|
+
}
|
|
351
|
+
//# sourceMappingURL=signature.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signature.js","sourceRoot":"","sources":["../../src/pdf/signature.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright 2026 Kapil — Project Panini Contributors
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* Raw-byte structural forensics for PDF statement files. Everything here
|
|
18
|
+
* works on the container — no content trust required. Each check emits
|
|
19
|
+
* explainable signals; none of them auto-proves fraud alone.
|
|
20
|
+
*/
|
|
21
|
+
import type { IntegritySignal } from '../types';
|
|
22
|
+
export interface StructureOptions {
|
|
23
|
+
/** Statement period end (YYYY-MM-DD) when known — dates after it on ModDate are suspicious. */
|
|
24
|
+
readonly statementPeriodEnd?: string | null;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Structural signals from raw PDF bytes. Never throws — malformed input
|
|
28
|
+
* yields a `structure_unreadable` warning signal, not an exception.
|
|
29
|
+
*/
|
|
30
|
+
export declare function inspectPdfStructure(pdf: Uint8Array, opts?: StructureOptions): IntegritySignal[];
|
|
31
|
+
//# sourceMappingURL=structure.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"structure.d.ts","sourceRoot":"","sources":["../../src/pdf/structure.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH;;;;GAIG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AA4DhD,MAAM,WAAW,gBAAgB;IAC/B,+FAA+F;IAC/F,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7C;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,GAAG,EAAE,UAAU,EACf,IAAI,GAAE,gBAAqB,GAC1B,eAAe,EAAE,CA6GnB"}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Copyright 2026 Kapil — Project Panini Contributors
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
|
+
exports.inspectPdfStructure = inspectPdfStructure;
|
|
19
|
+
const LATIN1 = 'latin1';
|
|
20
|
+
/** Producer/Creator strings strongly associated with interactive PDF editors. */
|
|
21
|
+
const EDITOR_SIGNATURES = [
|
|
22
|
+
/ilovepdf/i,
|
|
23
|
+
/pdfescape/i,
|
|
24
|
+
/smallpdf/i,
|
|
25
|
+
/sejda/i,
|
|
26
|
+
/pdf-xchange/i,
|
|
27
|
+
/foxit\s+(?:phantompdf|editor)/i,
|
|
28
|
+
/nitro/i,
|
|
29
|
+
/wkhtmltopdf/i, // HTML→PDF — statements must not be regenerated
|
|
30
|
+
/weasyprint/i,
|
|
31
|
+
/cairosvg/i,
|
|
32
|
+
/reportlab/i,
|
|
33
|
+
/fpdf/i,
|
|
34
|
+
/tcpdf/i,
|
|
35
|
+
/dompdf/i,
|
|
36
|
+
/itext(?!.*(?:bank|rbi))/i, // iText is also a legit bank generator — warning only
|
|
37
|
+
];
|
|
38
|
+
const PDF_DATE_RE = /D:?(\d{4})(\d{2})(\d{2})(\d{2})?(\d{2})?(\d{2})?/;
|
|
39
|
+
function pdfDateToIso(raw) {
|
|
40
|
+
const m = PDF_DATE_RE.exec(raw);
|
|
41
|
+
if (m === null)
|
|
42
|
+
return null;
|
|
43
|
+
return `${m[1]}-${m[2]}-${m[3]}`;
|
|
44
|
+
}
|
|
45
|
+
function infoString(dict, key) {
|
|
46
|
+
// /Key (literal) or /Key <hex>
|
|
47
|
+
const lit = new RegExp(`/${key}\\s*\\(([^)]*)\\)`).exec(dict);
|
|
48
|
+
if (lit !== null)
|
|
49
|
+
return lit[1];
|
|
50
|
+
const hex = new RegExp(`/${key}\\s*<([0-9a-fA-F]*)>`).exec(dict);
|
|
51
|
+
if (hex !== null) {
|
|
52
|
+
const raw = Buffer.from(hex[1] ?? '', 'hex');
|
|
53
|
+
if (raw.length % 2 === 1)
|
|
54
|
+
return raw.toString('latin1');
|
|
55
|
+
return Buffer.from(raw).swap16().toString('utf16le');
|
|
56
|
+
}
|
|
57
|
+
return undefined;
|
|
58
|
+
}
|
|
59
|
+
function findInfoDict(latin) {
|
|
60
|
+
const ref = /\/Info\s+(\d+)\s+(\d+)\s+R/.exec(latin);
|
|
61
|
+
if (ref === null)
|
|
62
|
+
return null;
|
|
63
|
+
const objRe = new RegExp(`${ref[1]}\\s+${ref[2]}\\s+obj\\s*<<([\\s\\S]*?)>>`, 'm');
|
|
64
|
+
const m = objRe.exec(latin);
|
|
65
|
+
return m ? `<<${m[1]}>>` : null;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Structural signals from raw PDF bytes. Never throws — malformed input
|
|
69
|
+
* yields a `structure_unreadable` warning signal, not an exception.
|
|
70
|
+
*/
|
|
71
|
+
function inspectPdfStructure(pdf, opts = {}) {
|
|
72
|
+
const signals = [];
|
|
73
|
+
const latin = Buffer.from(pdf).toString(LATIN1);
|
|
74
|
+
// ── Incremental updates ──────────────────────────────────────────────
|
|
75
|
+
const eofCount = (latin.match(/%%EOF/g) ?? []).length;
|
|
76
|
+
const xrefCount = (latin.match(/startxref/g) ?? []).length;
|
|
77
|
+
if (eofCount > 1 || xrefCount > 1) {
|
|
78
|
+
signals.push({
|
|
79
|
+
ruleId: 'pdf.incremental_update',
|
|
80
|
+
severity: 'warning',
|
|
81
|
+
message: `document carries ${Math.max(eofCount, xrefCount)} revision(s) — content was appended after generation`,
|
|
82
|
+
evidence: `%%EOF×${eofCount} startxref×${xrefCount}`,
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
// ── Producer / Creator / dates ───────────────────────────────────────
|
|
86
|
+
const infoDict = findInfoDict(latin);
|
|
87
|
+
const info = {
|
|
88
|
+
producer: undefined,
|
|
89
|
+
creator: undefined,
|
|
90
|
+
creationDate: undefined,
|
|
91
|
+
modDate: undefined,
|
|
92
|
+
};
|
|
93
|
+
if (infoDict !== null) {
|
|
94
|
+
info.producer = infoString(infoDict, 'Producer');
|
|
95
|
+
info.creator = infoString(infoDict, 'Creator');
|
|
96
|
+
info.creationDate = infoString(infoDict, 'CreationDate');
|
|
97
|
+
info.modDate = infoString(infoDict, 'ModDate');
|
|
98
|
+
}
|
|
99
|
+
for (const [key, value] of [
|
|
100
|
+
['Producer', info.producer],
|
|
101
|
+
['Creator', info.creator],
|
|
102
|
+
]) {
|
|
103
|
+
if (value === undefined)
|
|
104
|
+
continue;
|
|
105
|
+
const editor = EDITOR_SIGNATURES.find((re) => re.test(value));
|
|
106
|
+
if (editor !== undefined) {
|
|
107
|
+
signals.push({
|
|
108
|
+
ruleId: 'pdf.editor_producer',
|
|
109
|
+
severity: 'critical',
|
|
110
|
+
message: `/${key} '${value}' is a consumer PDF editor — bank-generated statements do not carry this`,
|
|
111
|
+
evidence: `/${key} (${value})`,
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
// ModDate implies a post-generation save.
|
|
116
|
+
const createdIso = info.creationDate !== undefined ? pdfDateToIso(info.creationDate) : null;
|
|
117
|
+
const modifiedIso = info.modDate !== undefined ? pdfDateToIso(info.modDate) : null;
|
|
118
|
+
if (modifiedIso !== null && createdIso !== null && modifiedIso > createdIso) {
|
|
119
|
+
signals.push({
|
|
120
|
+
ruleId: 'pdf.modified_after_creation',
|
|
121
|
+
severity: 'warning',
|
|
122
|
+
message: `ModDate ${modifiedIso} is after CreationDate ${createdIso} — file was re-saved after generation`,
|
|
123
|
+
evidence: `CreationDate=${createdIso} ModDate=${modifiedIso}`,
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
if (opts.statementPeriodEnd != null &&
|
|
127
|
+
modifiedIso !== null &&
|
|
128
|
+
modifiedIso > opts.statementPeriodEnd &&
|
|
129
|
+
createdIso !== null &&
|
|
130
|
+
modifiedIso > createdIso) {
|
|
131
|
+
signals.push({
|
|
132
|
+
ruleId: 'pdf.modified_after_period',
|
|
133
|
+
severity: 'critical',
|
|
134
|
+
message: `file modified ${modifiedIso}, after the statement period ended ${opts.statementPeriodEnd}`,
|
|
135
|
+
evidence: `ModDate=${modifiedIso} periodEnd=${opts.statementPeriodEnd}`,
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
// ── Interactive overlays ─────────────────────────────────────────────
|
|
139
|
+
// AcroForm + signature fields are the normal carrier for signed
|
|
140
|
+
// statements — only suspicious when interactive fields exist with NO
|
|
141
|
+
// signature dict at all (fill-in overlays without signer evidence).
|
|
142
|
+
const sigFieldCount = (latin.match(/\/FT\s*\/Sig\b/g) ?? []).length;
|
|
143
|
+
const hasAcroForm = /\/AcroForm\b/.test(latin) || /\/SigFlags\s*\d/.test(latin);
|
|
144
|
+
if (hasAcroForm && sigFieldCount === 0 && !/\/Type\s*\/Sig\b/.test(latin)) {
|
|
145
|
+
signals.push({
|
|
146
|
+
ruleId: 'pdf.acroform_overlay',
|
|
147
|
+
severity: 'warning',
|
|
148
|
+
message: 'document carries interactive form fields but no signature — values can overlay or replace rendered text',
|
|
149
|
+
evidence: '/AcroForm present, /FT /Sig absent',
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
const annotCount = (latin.match(/\/Subtype\s*\/(?:FreeText|Stamp|Highlight|Squiggly|StrikeOut)/g) ?? []).length;
|
|
153
|
+
if (annotCount > 0) {
|
|
154
|
+
signals.push({
|
|
155
|
+
ruleId: 'pdf.covering_annotations',
|
|
156
|
+
severity: 'warning',
|
|
157
|
+
message: `${annotCount} markup annotation(s) can visually cover underlying statement text`,
|
|
158
|
+
evidence: `annotation subtypes ×${annotCount}`,
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
// ── Header sanity ────────────────────────────────────────────────────
|
|
162
|
+
if (!latin.startsWith('%PDF-')) {
|
|
163
|
+
signals.push({
|
|
164
|
+
ruleId: 'pdf.bad_header',
|
|
165
|
+
severity: 'warning',
|
|
166
|
+
message: 'file does not open with a %PDF- header',
|
|
167
|
+
evidence: `first bytes: ${latin.slice(0, 8)}`,
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
return signals;
|
|
171
|
+
}
|
|
172
|
+
//# sourceMappingURL=structure.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"structure.js","sourceRoot":"","sources":["../../src/pdf/structure.ts"],"names":[],"mappings":"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"}
|