@mkabatek/pptx-viewer 1.5.4 → 1.5.11
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/index.js +3 -1
- package/dist/index.mjs +3 -1
- package/dist/viewer/index.js +3 -1
- package/dist/viewer/index.mjs +3 -1
- package/package.json +2 -9
- package/node_modules/emf-converter/LICENSE +0 -21
- package/node_modules/emf-converter/README.md +0 -629
- package/node_modules/emf-converter/dist/index.d.mts +0 -86
- package/node_modules/emf-converter/dist/index.d.ts +0 -86
- package/node_modules/emf-converter/dist/index.js +0 -4257
- package/node_modules/emf-converter/dist/index.mjs +0 -4253
- package/node_modules/emf-converter/package.json +0 -53
- package/node_modules/mtx-decompressor/LICENSE +0 -373
- package/node_modules/mtx-decompressor/README.md +0 -271
- package/node_modules/mtx-decompressor/dist/index.d.mts +0 -84
- package/node_modules/mtx-decompressor/dist/index.d.ts +0 -84
- package/node_modules/mtx-decompressor/dist/index.js +0 -1532
- package/node_modules/mtx-decompressor/dist/index.mjs +0 -1528
- package/node_modules/mtx-decompressor/package.json +0 -44
- package/node_modules/pptx-viewer-core/LICENSE +0 -21
- package/node_modules/pptx-viewer-core/NOTICE +0 -16
- package/node_modules/pptx-viewer-core/README.md +0 -1294
- package/node_modules/pptx-viewer-core/dist/SvgExporter-BtZczTlB.d.ts +0 -557
- package/node_modules/pptx-viewer-core/dist/SvgExporter-D4mBWJHE.d.mts +0 -557
- package/node_modules/pptx-viewer-core/dist/cli/index.d.mts +0 -150
- package/node_modules/pptx-viewer-core/dist/cli/index.d.ts +0 -150
- package/node_modules/pptx-viewer-core/dist/cli/index.js +0 -0
- package/node_modules/pptx-viewer-core/dist/cli/index.mjs +0 -0
- package/node_modules/pptx-viewer-core/dist/converter/index.d.mts +0 -48
- package/node_modules/pptx-viewer-core/dist/converter/index.d.ts +0 -48
- package/node_modules/pptx-viewer-core/dist/converter/index.js +0 -0
- package/node_modules/pptx-viewer-core/dist/converter/index.mjs +0 -0
- package/node_modules/pptx-viewer-core/dist/index.d.mts +0 -12744
- package/node_modules/pptx-viewer-core/dist/index.d.ts +0 -12744
- package/node_modules/pptx-viewer-core/dist/index.js +0 -66894
- package/node_modules/pptx-viewer-core/dist/index.mjs +0 -66420
- package/node_modules/pptx-viewer-core/dist/presentation-nZxgWvXq.d.mts +0 -5645
- package/node_modules/pptx-viewer-core/dist/presentation-nZxgWvXq.d.ts +0 -5645
- package/node_modules/pptx-viewer-core/dist/signature-inspection-status-BCUpfCQh.d.mts +0 -220
- package/node_modules/pptx-viewer-core/dist/signature-inspection-status-BCUpfCQh.d.ts +0 -220
- package/node_modules/pptx-viewer-core/dist/signature-node/index.d.mts +0 -177
- package/node_modules/pptx-viewer-core/dist/signature-node/index.d.ts +0 -177
- package/node_modules/pptx-viewer-core/dist/signature-node/index.js +0 -1206
- package/node_modules/pptx-viewer-core/dist/signature-node/index.mjs +0 -1143
- package/node_modules/pptx-viewer-core/dist/text-operations-DCTGMltY.d.mts +0 -134
- package/node_modules/pptx-viewer-core/dist/text-operations-DYmhoi7U.d.ts +0 -134
- package/node_modules/pptx-viewer-core/package.json +0 -96
|
@@ -1,1206 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
var xmldom = require('@xmldom/xmldom');
|
|
4
|
-
var xmlCrypto = require('xml-crypto');
|
|
5
|
-
var crypto = require('crypto');
|
|
6
|
-
var path = require('path');
|
|
7
|
-
var tls = require('tls');
|
|
8
|
-
var forge = require('node-forge');
|
|
9
|
-
var http = require('http');
|
|
10
|
-
var https = require('https');
|
|
11
|
-
var fs = require('fs/promises');
|
|
12
|
-
var JSZip = require('jszip');
|
|
13
|
-
|
|
14
|
-
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
15
|
-
|
|
16
|
-
var crypto__default = /*#__PURE__*/_interopDefault(crypto);
|
|
17
|
-
var path__default = /*#__PURE__*/_interopDefault(path);
|
|
18
|
-
var tls__default = /*#__PURE__*/_interopDefault(tls);
|
|
19
|
-
var forge__default = /*#__PURE__*/_interopDefault(forge);
|
|
20
|
-
var http__default = /*#__PURE__*/_interopDefault(http);
|
|
21
|
-
var https__default = /*#__PURE__*/_interopDefault(https);
|
|
22
|
-
var fs__default = /*#__PURE__*/_interopDefault(fs);
|
|
23
|
-
var JSZip__default = /*#__PURE__*/_interopDefault(JSZip);
|
|
24
|
-
|
|
25
|
-
// src/core/utils/signature-constants.ts
|
|
26
|
-
var DIGITAL_SIGNATURE_ORIGIN_REL_TYPE = "http://schemas.openxmlformats.org/package/2006/relationships/digital-signature/origin";
|
|
27
|
-
var DIGITAL_SIGNATURE_REL_TYPE = "http://schemas.openxmlformats.org/package/2006/relationships/digital-signature/signature";
|
|
28
|
-
var PPTX_VIEWER_MANIFEST_NS = "urn:pptx-viewer:ooxml-signature:v1";
|
|
29
|
-
var XMLDSIG_NS = "http://www.w3.org/2000/09/xmldsig#";
|
|
30
|
-
var OPC_RELATIONSHIP_TRANSFORM = "http://schemas.openxmlformats.org/package/2006/RelationshipTransform";
|
|
31
|
-
var XML_TRANSFORM_ENVELOPED_SIGNATURE = "http://www.w3.org/2000/09/xmldsig#enveloped-signature";
|
|
32
|
-
var SUPPORTED_XML_CANON_TRANSFORMS = /* @__PURE__ */ new Set([
|
|
33
|
-
"http://www.w3.org/TR/2001/REC-xml-c14n-20010315",
|
|
34
|
-
"http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments",
|
|
35
|
-
"http://www.w3.org/2001/10/xml-exc-c14n#",
|
|
36
|
-
"http://www.w3.org/2001/10/xml-exc-c14n#WithComments",
|
|
37
|
-
XML_TRANSFORM_ENVELOPED_SIGNATURE
|
|
38
|
-
]);
|
|
39
|
-
var ENTERPRISE_TRUST_ROOTS_FILE_ENV = "PPTX_VIEWER_TRUST_ROOTS_FILE";
|
|
40
|
-
var ENTERPRISE_TRUST_ROOTS_PEM_ENV = "PPTX_VIEWER_TRUST_ROOTS_PEM";
|
|
41
|
-
var ENTERPRISE_REQUIRE_REVOCATION_ENV = "PPTX_VIEWER_REQUIRE_REVOCATION_CHECK";
|
|
42
|
-
var ENTERPRISE_FAIL_ON_REVOCATION_UNKNOWN_ENV = "PPTX_VIEWER_FAIL_ON_REVOCATION_UNKNOWN";
|
|
43
|
-
var ENTERPRISE_REQUIRE_TIMESTAMP_ENV = "PPTX_VIEWER_REQUIRE_TIMESTAMP";
|
|
44
|
-
var DIGEST_ALGORITHM_TO_HASH = {
|
|
45
|
-
"http://www.w3.org/2000/09/xmldsig#sha1": "sha1",
|
|
46
|
-
"http://www.w3.org/2001/04/xmlenc#sha256": "sha256",
|
|
47
|
-
"http://www.w3.org/2001/04/xmlenc#sha384": "sha384",
|
|
48
|
-
"http://www.w3.org/2001/04/xmlenc#sha512": "sha512"
|
|
49
|
-
};
|
|
50
|
-
var DIGEST_ALGORITHM_TO_WEB_CRYPTO = {
|
|
51
|
-
"http://www.w3.org/2000/09/xmldsig#sha1": "SHA-1",
|
|
52
|
-
"http://www.w3.org/2001/04/xmlenc#sha256": "SHA-256",
|
|
53
|
-
"http://www.w3.org/2001/04/xmlenc#sha384": "SHA-384",
|
|
54
|
-
"http://www.w3.org/2001/04/xmlenc#sha512": "SHA-512"
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
// src/core/utils/signature-xml-utils.ts
|
|
58
|
-
function escapeXmlAttr(value) {
|
|
59
|
-
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
60
|
-
}
|
|
61
|
-
function escapeXmlText(value) {
|
|
62
|
-
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
63
|
-
}
|
|
64
|
-
function isValidBase64(value) {
|
|
65
|
-
return typeof value === "string" && value.length > 0 && /^[A-Za-z0-9+/=\s]+$/.test(value);
|
|
66
|
-
}
|
|
67
|
-
function extractTagAttribute(xml, tagName, attributeName) {
|
|
68
|
-
const pattern = new RegExp(
|
|
69
|
-
`<${tagName}\\b[^>]*\\b${attributeName}="(?<attributeValue>[^"]+)"`,
|
|
70
|
-
"i"
|
|
71
|
-
);
|
|
72
|
-
const match = xml.match(pattern);
|
|
73
|
-
return match?.groups?.["attributeValue"]?.trim();
|
|
74
|
-
}
|
|
75
|
-
function extractFirstTagText(xml, localName) {
|
|
76
|
-
const pattern = new RegExp(
|
|
77
|
-
`<([\\w.-]+:)?${localName}\\b[^>]*>([\\s\\S]*?)<\\/([\\w.-]+:)?${localName}>`,
|
|
78
|
-
"i"
|
|
79
|
-
);
|
|
80
|
-
const match = xml.match(pattern);
|
|
81
|
-
return match?.[2]?.replace(/\s+/g, "").trim() || void 0;
|
|
82
|
-
}
|
|
83
|
-
function extractAllTagText(xml, localName) {
|
|
84
|
-
const result = [];
|
|
85
|
-
const regex = new RegExp(
|
|
86
|
-
`<([\\w.-]+:)?${localName}\\b[^>]*>([\\s\\S]*?)<\\/([\\w.-]+:)?${localName}>`,
|
|
87
|
-
"gi"
|
|
88
|
-
);
|
|
89
|
-
let match = regex.exec(xml);
|
|
90
|
-
while (match) {
|
|
91
|
-
const value = match[2]?.replace(/\s+/g, "").trim();
|
|
92
|
-
if (value) {
|
|
93
|
-
result.push(value);
|
|
94
|
-
}
|
|
95
|
-
match = regex.exec(xml);
|
|
96
|
-
}
|
|
97
|
-
return result;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
// src/core/utils/signature-reference-utils.ts
|
|
101
|
-
function normalizePartPath(partPath) {
|
|
102
|
-
return partPath.replace(/\\/g, "/").replace(/^\/+/, "");
|
|
103
|
-
}
|
|
104
|
-
function resolveReferenceUriToPart(uri) {
|
|
105
|
-
const trimmed = uri.trim();
|
|
106
|
-
if (trimmed.length === 0 || trimmed.startsWith("#")) {
|
|
107
|
-
return void 0;
|
|
108
|
-
}
|
|
109
|
-
const decoded = decodeURIComponent(trimmed);
|
|
110
|
-
return normalizePartPath(decoded.startsWith("/") ? decoded.slice(1) : decoded);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// src/core/utils/signature-digest.ts
|
|
114
|
-
async function computeDigestBase64(content, digestAlgorithmUri) {
|
|
115
|
-
const webCryptoAlgo = DIGEST_ALGORITHM_TO_WEB_CRYPTO[digestAlgorithmUri];
|
|
116
|
-
if (!webCryptoAlgo) {
|
|
117
|
-
return void 0;
|
|
118
|
-
}
|
|
119
|
-
if (typeof globalThis.crypto?.subtle === "undefined") {
|
|
120
|
-
return void 0;
|
|
121
|
-
}
|
|
122
|
-
const buf = new ArrayBuffer(content.byteLength);
|
|
123
|
-
new Uint8Array(buf).set(content);
|
|
124
|
-
const digest = await globalThis.crypto.subtle.digest(webCryptoAlgo, buf);
|
|
125
|
-
return uint8ArrayToBase64(new Uint8Array(digest));
|
|
126
|
-
}
|
|
127
|
-
function uint8ArrayToBase64(bytes) {
|
|
128
|
-
let binary = "";
|
|
129
|
-
for (let i = 0; i < bytes.byteLength; i++) {
|
|
130
|
-
binary += String.fromCharCode(bytes[i]);
|
|
131
|
-
}
|
|
132
|
-
return btoa(binary);
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
// src/core/utils/signature-inspection-status.ts
|
|
136
|
-
function computeDetailStatus(detail, policy) {
|
|
137
|
-
const hasDigestMismatch = detail.referenceChecks.some(
|
|
138
|
-
(check) => check.digestStatus === "mismatch"
|
|
139
|
-
);
|
|
140
|
-
const hasMissingPart = detail.missingPartReferences.length > 0;
|
|
141
|
-
const hasVerifiedDigest = detail.referenceChecks.some(
|
|
142
|
-
(check) => check.digestStatus === "verified"
|
|
143
|
-
);
|
|
144
|
-
const revocationUnknown = detail.certificateRevocationStatus === "unknown" || detail.certificateRevocationStatus === "not-checked" || detail.certificateRevocationStatus === "error";
|
|
145
|
-
const timestampMissingOrUntrusted = detail.timestampAuthorityStatus === "not-present" || detail.timestampAuthorityStatus === "not-checked" || detail.timestampAuthorityStatus === "error" || detail.timestampAuthorityStatus === "untrusted";
|
|
146
|
-
if (detail.signatureValueStatus === "invalid") {
|
|
147
|
-
return "signature-invalid";
|
|
148
|
-
}
|
|
149
|
-
if (hasMissingPart) {
|
|
150
|
-
return "reference-missing";
|
|
151
|
-
}
|
|
152
|
-
if (hasDigestMismatch) {
|
|
153
|
-
return "digest-mismatch";
|
|
154
|
-
}
|
|
155
|
-
if (detail.certificateRevocationStatus === "revoked") {
|
|
156
|
-
return "certificate-revoked";
|
|
157
|
-
}
|
|
158
|
-
if (policy.requireRevocationCheck && (policy.failOnRevocationUnknown ? revocationUnknown : detail.certificateRevocationStatus !== "good")) {
|
|
159
|
-
return "certificate-untrusted";
|
|
160
|
-
}
|
|
161
|
-
if (detail.timestampAuthorityStatus === "invalid") {
|
|
162
|
-
return "timestamp-invalid";
|
|
163
|
-
}
|
|
164
|
-
if (policy.requireTimestamp && timestampMissingOrUntrusted) {
|
|
165
|
-
return "timestamp-untrusted";
|
|
166
|
-
}
|
|
167
|
-
if (detail.timestampAuthorityStatus === "untrusted") {
|
|
168
|
-
return "timestamp-untrusted";
|
|
169
|
-
}
|
|
170
|
-
if (detail.certificateTrustStatus === "untrusted" && detail.signatureValueStatus === "verified") {
|
|
171
|
-
return "certificate-untrusted";
|
|
172
|
-
}
|
|
173
|
-
if (hasVerifiedDigest && detail.signatureValueStatus === "verified") {
|
|
174
|
-
return "verified";
|
|
175
|
-
}
|
|
176
|
-
return "structural-only";
|
|
177
|
-
}
|
|
178
|
-
function computeVerificationStatus(details) {
|
|
179
|
-
const hasInvalidSignature = details.some((detail) => detail.status === "signature-invalid");
|
|
180
|
-
const hasMissing = details.some((detail) => detail.status === "reference-missing");
|
|
181
|
-
const hasMismatch = details.some((detail) => detail.status === "digest-mismatch");
|
|
182
|
-
const hasUntrusted = details.some((detail) => detail.status === "certificate-untrusted");
|
|
183
|
-
const hasRevoked = details.some((detail) => detail.status === "certificate-revoked");
|
|
184
|
-
const hasTimestampInvalid = details.some((detail) => detail.status === "timestamp-invalid");
|
|
185
|
-
const hasTimestampUntrusted = details.some((detail) => detail.status === "timestamp-untrusted");
|
|
186
|
-
const allVerified = details.every((detail) => detail.status === "verified");
|
|
187
|
-
return hasInvalidSignature ? "signature-invalid" : hasRevoked ? "certificate-revoked" : hasTimestampInvalid ? "timestamp-invalid" : hasTimestampUntrusted ? "timestamp-untrusted" : hasMissing ? "reference-missing" : hasMismatch ? "digest-mismatch" : allVerified ? "verified-trusted" : hasUntrusted ? "verified-untrusted" : "present-not-verified";
|
|
188
|
-
}
|
|
189
|
-
function getNodeLocalName(node) {
|
|
190
|
-
const localNameNode = node;
|
|
191
|
-
if (localNameNode.localName) {
|
|
192
|
-
return localNameNode.localName;
|
|
193
|
-
}
|
|
194
|
-
const nodeName = node.nodeName || "";
|
|
195
|
-
const sep = nodeName.indexOf(":");
|
|
196
|
-
return sep >= 0 ? nodeName.slice(sep + 1) : nodeName;
|
|
197
|
-
}
|
|
198
|
-
function getFirstDescendantElementByLocalName(parent, localName) {
|
|
199
|
-
const elements = parent.getElementsByTagName("*");
|
|
200
|
-
for (let index = 0; index < elements.length; index += 1) {
|
|
201
|
-
const element = elements.item(index);
|
|
202
|
-
if (!element) {
|
|
203
|
-
continue;
|
|
204
|
-
}
|
|
205
|
-
if (getNodeLocalName(element) === localName) {
|
|
206
|
-
return element;
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
return void 0;
|
|
210
|
-
}
|
|
211
|
-
function canonicalizeNode(node, algorithm) {
|
|
212
|
-
const canonicalizer = new xmlCrypto.SignedXml();
|
|
213
|
-
return canonicalizer.getCanonXml([algorithm], node);
|
|
214
|
-
}
|
|
215
|
-
function canonicalizeSignedInfoXml(signedInfoXml) {
|
|
216
|
-
const parser = new xmldom.DOMParser();
|
|
217
|
-
const signedInfoDoc = parser.parseFromString(signedInfoXml, "text/xml");
|
|
218
|
-
if (!signedInfoDoc.documentElement) {
|
|
219
|
-
throw new Error("Unable to canonicalize SignedInfo: invalid XML.");
|
|
220
|
-
}
|
|
221
|
-
return canonicalizeNode(signedInfoDoc.documentElement, "http://www.w3.org/2001/10/xml-exc-c14n#");
|
|
222
|
-
}
|
|
223
|
-
function certPemFromBase64(certBase64) {
|
|
224
|
-
try {
|
|
225
|
-
const der = Buffer.from(certBase64, "base64");
|
|
226
|
-
if (der.length === 0) {
|
|
227
|
-
return void 0;
|
|
228
|
-
}
|
|
229
|
-
const b64 = der.toString("base64");
|
|
230
|
-
const lines = b64.match(/.{1,64}/g);
|
|
231
|
-
if (!lines) {
|
|
232
|
-
return void 0;
|
|
233
|
-
}
|
|
234
|
-
return `-----BEGIN CERTIFICATE-----
|
|
235
|
-
${lines.join("\n")}
|
|
236
|
-
-----END CERTIFICATE-----`;
|
|
237
|
-
} catch {
|
|
238
|
-
return void 0;
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
function certFingerprintSha256(certPem) {
|
|
242
|
-
try {
|
|
243
|
-
const cert = new crypto__default.default.X509Certificate(certPem);
|
|
244
|
-
return cert.fingerprint256.replace(/:/g, "").toLowerCase();
|
|
245
|
-
} catch {
|
|
246
|
-
return void 0;
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
function asn1Child(node, index) {
|
|
250
|
-
return Array.isArray(node.value) ? node.value[index] : void 0;
|
|
251
|
-
}
|
|
252
|
-
function asn1Bytes(node) {
|
|
253
|
-
return typeof node.value === "string" ? node.value : "";
|
|
254
|
-
}
|
|
255
|
-
function extractOcspUrls(certPem) {
|
|
256
|
-
try {
|
|
257
|
-
const cert = new crypto__default.default.X509Certificate(certPem);
|
|
258
|
-
const infoAccess = cert.infoAccess;
|
|
259
|
-
if (!infoAccess) {
|
|
260
|
-
return [];
|
|
261
|
-
}
|
|
262
|
-
const urls = [];
|
|
263
|
-
for (const line of infoAccess.split("\n")) {
|
|
264
|
-
const match = /OCSP\s*-\s*URI:(https?:\/\/\S+)/i.exec(line.trim());
|
|
265
|
-
if (match?.[1]) {
|
|
266
|
-
urls.push(match[1]);
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
return urls;
|
|
270
|
-
} catch {
|
|
271
|
-
return [];
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
var HTTP_TIMEOUT_MS = 1e4;
|
|
275
|
-
function httpPost(url, body, contentType) {
|
|
276
|
-
return new Promise((resolve, reject) => {
|
|
277
|
-
let parsed;
|
|
278
|
-
try {
|
|
279
|
-
parsed = new URL(url);
|
|
280
|
-
} catch {
|
|
281
|
-
reject(new Error("Invalid URL"));
|
|
282
|
-
return;
|
|
283
|
-
}
|
|
284
|
-
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
285
|
-
reject(new Error("Unsupported protocol"));
|
|
286
|
-
return;
|
|
287
|
-
}
|
|
288
|
-
const transport = parsed.protocol === "https:" ? https__default.default : http__default.default;
|
|
289
|
-
const req = transport.request(
|
|
290
|
-
{
|
|
291
|
-
hostname: parsed.hostname,
|
|
292
|
-
port: parsed.port || void 0,
|
|
293
|
-
path: parsed.pathname + parsed.search,
|
|
294
|
-
method: "POST",
|
|
295
|
-
headers: {
|
|
296
|
-
"Content-Type": contentType,
|
|
297
|
-
"Content-Length": body.length
|
|
298
|
-
},
|
|
299
|
-
timeout: HTTP_TIMEOUT_MS
|
|
300
|
-
},
|
|
301
|
-
(res) => {
|
|
302
|
-
const chunks = [];
|
|
303
|
-
res.on("data", (chunk) => chunks.push(chunk));
|
|
304
|
-
res.on("end", () => resolve(Buffer.concat(chunks)));
|
|
305
|
-
res.on("error", reject);
|
|
306
|
-
}
|
|
307
|
-
);
|
|
308
|
-
req.on("error", reject);
|
|
309
|
-
req.on("timeout", () => {
|
|
310
|
-
req.destroy();
|
|
311
|
-
reject(new Error("Timeout"));
|
|
312
|
-
});
|
|
313
|
-
req.write(body);
|
|
314
|
-
req.end();
|
|
315
|
-
});
|
|
316
|
-
}
|
|
317
|
-
var SHA1_OID = "1.3.14.3.2.26";
|
|
318
|
-
function buildOcspRequestDer(leafPem, issuerPem) {
|
|
319
|
-
try {
|
|
320
|
-
const leaf = forge__default.default.pki.certificateFromPem(leafPem);
|
|
321
|
-
const issuer = forge__default.default.pki.certificateFromPem(issuerPem);
|
|
322
|
-
const issuerNameDer = forge__default.default.asn1.toDer(forge__default.default.pki.distinguishedNameToAsn1(issuer.subject)).getBytes();
|
|
323
|
-
const issuerNameHash = forge__default.default.md.sha1.create().update(issuerNameDer).digest().getBytes();
|
|
324
|
-
const pubKeyDer = forge__default.default.asn1.toDer(forge__default.default.pki.publicKeyToAsn1(issuer.publicKey)).getBytes();
|
|
325
|
-
const pubKeyAsn1 = forge__default.default.asn1.fromDer(pubKeyDer);
|
|
326
|
-
const bitString = asn1Child(pubKeyAsn1, 1);
|
|
327
|
-
const rawKey = bitString ? asn1Bytes(bitString).substring(1) : "";
|
|
328
|
-
const issuerKeyHash = forge__default.default.md.sha1.create().update(rawKey).digest().getBytes();
|
|
329
|
-
const serialBytes = forge__default.default.util.hexToBytes(leaf.serialNumber);
|
|
330
|
-
const request = forge__default.default.asn1.create(forge__default.default.asn1.Class.UNIVERSAL, forge__default.default.asn1.Type.SEQUENCE, true, [
|
|
331
|
-
forge__default.default.asn1.create(forge__default.default.asn1.Class.UNIVERSAL, forge__default.default.asn1.Type.SEQUENCE, true, [
|
|
332
|
-
forge__default.default.asn1.create(forge__default.default.asn1.Class.UNIVERSAL, forge__default.default.asn1.Type.SEQUENCE, true, [
|
|
333
|
-
forge__default.default.asn1.create(forge__default.default.asn1.Class.UNIVERSAL, forge__default.default.asn1.Type.SEQUENCE, true, [
|
|
334
|
-
forge__default.default.asn1.create(forge__default.default.asn1.Class.UNIVERSAL, forge__default.default.asn1.Type.SEQUENCE, true, [
|
|
335
|
-
forge__default.default.asn1.create(forge__default.default.asn1.Class.UNIVERSAL, forge__default.default.asn1.Type.SEQUENCE, true, [
|
|
336
|
-
forge__default.default.asn1.create(
|
|
337
|
-
forge__default.default.asn1.Class.UNIVERSAL,
|
|
338
|
-
forge__default.default.asn1.Type.OID,
|
|
339
|
-
false,
|
|
340
|
-
forge__default.default.asn1.oidToDer(SHA1_OID).getBytes()
|
|
341
|
-
),
|
|
342
|
-
forge__default.default.asn1.create(forge__default.default.asn1.Class.UNIVERSAL, forge__default.default.asn1.Type.NULL, false, "")
|
|
343
|
-
]),
|
|
344
|
-
forge__default.default.asn1.create(
|
|
345
|
-
forge__default.default.asn1.Class.UNIVERSAL,
|
|
346
|
-
forge__default.default.asn1.Type.OCTETSTRING,
|
|
347
|
-
false,
|
|
348
|
-
issuerNameHash
|
|
349
|
-
),
|
|
350
|
-
forge__default.default.asn1.create(
|
|
351
|
-
forge__default.default.asn1.Class.UNIVERSAL,
|
|
352
|
-
forge__default.default.asn1.Type.OCTETSTRING,
|
|
353
|
-
false,
|
|
354
|
-
issuerKeyHash
|
|
355
|
-
),
|
|
356
|
-
forge__default.default.asn1.create(
|
|
357
|
-
forge__default.default.asn1.Class.UNIVERSAL,
|
|
358
|
-
forge__default.default.asn1.Type.INTEGER,
|
|
359
|
-
false,
|
|
360
|
-
serialBytes
|
|
361
|
-
)
|
|
362
|
-
])
|
|
363
|
-
])
|
|
364
|
-
])
|
|
365
|
-
])
|
|
366
|
-
]);
|
|
367
|
-
return Buffer.from(forge__default.default.asn1.toDer(request).getBytes(), "binary");
|
|
368
|
-
} catch {
|
|
369
|
-
return void 0;
|
|
370
|
-
}
|
|
371
|
-
}
|
|
372
|
-
function parseOcspResponseStatus(data) {
|
|
373
|
-
try {
|
|
374
|
-
const asn1 = forge__default.default.asn1.fromDer(forge__default.default.util.createBuffer(data.toString("binary")));
|
|
375
|
-
const statusNode = asn1Child(asn1, 0);
|
|
376
|
-
if (!statusNode || asn1Bytes(statusNode) !== "\0") {
|
|
377
|
-
return "error";
|
|
378
|
-
}
|
|
379
|
-
const respBytesWrapper = asn1Child(asn1, 1);
|
|
380
|
-
if (!respBytesWrapper) {
|
|
381
|
-
return "unknown";
|
|
382
|
-
}
|
|
383
|
-
const respBytesSeq = asn1Child(respBytesWrapper, 0);
|
|
384
|
-
if (!respBytesSeq) {
|
|
385
|
-
return "unknown";
|
|
386
|
-
}
|
|
387
|
-
const respOctet = asn1Child(respBytesSeq, 1);
|
|
388
|
-
if (!respOctet) {
|
|
389
|
-
return "unknown";
|
|
390
|
-
}
|
|
391
|
-
const basicResp = forge__default.default.asn1.fromDer(forge__default.default.util.createBuffer(asn1Bytes(respOctet)));
|
|
392
|
-
const tbsData = asn1Child(basicResp, 0);
|
|
393
|
-
if (!tbsData) {
|
|
394
|
-
return "unknown";
|
|
395
|
-
}
|
|
396
|
-
const first = asn1Child(tbsData, 0);
|
|
397
|
-
const responsesIdx = first?.tagClass === forge__default.default.asn1.Class.CONTEXT_SPECIFIC && first.type === 0 ? 3 : 2;
|
|
398
|
-
const responses = asn1Child(tbsData, responsesIdx);
|
|
399
|
-
if (!responses) {
|
|
400
|
-
return "unknown";
|
|
401
|
-
}
|
|
402
|
-
const single = asn1Child(responses, 0);
|
|
403
|
-
if (!single) {
|
|
404
|
-
return "unknown";
|
|
405
|
-
}
|
|
406
|
-
const certStatus = asn1Child(single, 1);
|
|
407
|
-
if (!certStatus) {
|
|
408
|
-
return "unknown";
|
|
409
|
-
}
|
|
410
|
-
if (certStatus.tagClass === forge__default.default.asn1.Class.CONTEXT_SPECIFIC) {
|
|
411
|
-
if (certStatus.type === 0) {
|
|
412
|
-
return "good";
|
|
413
|
-
}
|
|
414
|
-
if (certStatus.type === 1) {
|
|
415
|
-
return "revoked";
|
|
416
|
-
}
|
|
417
|
-
}
|
|
418
|
-
return "unknown";
|
|
419
|
-
} catch {
|
|
420
|
-
return "error";
|
|
421
|
-
}
|
|
422
|
-
}
|
|
423
|
-
async function evaluateCertificateRevocation(leafCertPem, issuerCertPem) {
|
|
424
|
-
const ocspUrls = extractOcspUrls(leafCertPem);
|
|
425
|
-
const checkedOcspUrls = [];
|
|
426
|
-
const checkedCrlUrls = [];
|
|
427
|
-
if (issuerCertPem && ocspUrls.length > 0) {
|
|
428
|
-
const reqDer = buildOcspRequestDer(leafCertPem, issuerCertPem);
|
|
429
|
-
if (reqDer) {
|
|
430
|
-
for (const url of ocspUrls) {
|
|
431
|
-
checkedOcspUrls.push(url);
|
|
432
|
-
try {
|
|
433
|
-
const resp = await httpPost(url, reqDer, "application/ocsp-request");
|
|
434
|
-
const status = parseOcspResponseStatus(resp);
|
|
435
|
-
if (status === "good" || status === "revoked") {
|
|
436
|
-
return { status, checkedOcspUrls, checkedCrlUrls };
|
|
437
|
-
}
|
|
438
|
-
} catch {
|
|
439
|
-
}
|
|
440
|
-
}
|
|
441
|
-
}
|
|
442
|
-
}
|
|
443
|
-
if (checkedOcspUrls.length === 0) {
|
|
444
|
-
return { status: "not-checked", checkedOcspUrls, checkedCrlUrls };
|
|
445
|
-
}
|
|
446
|
-
return {
|
|
447
|
-
status: "unknown",
|
|
448
|
-
error: "Could not determine revocation status from available responders.",
|
|
449
|
-
checkedOcspUrls,
|
|
450
|
-
checkedCrlUrls
|
|
451
|
-
};
|
|
452
|
-
}
|
|
453
|
-
var TIMESTAMP_TAG_REGEX = /<(?:[\w.-]+:)?(?:EncapsulatedTimeStamp|SignatureTimeStamp)[^>]*>([\s\S]*?)<\/(?:[\w.-]+:)?(?:EncapsulatedTimeStamp|SignatureTimeStamp)>/i;
|
|
454
|
-
async function evaluateTimestampAuthority(signatureXml) {
|
|
455
|
-
try {
|
|
456
|
-
const match = TIMESTAMP_TAG_REGEX.exec(signatureXml);
|
|
457
|
-
if (!match?.[1]?.trim()) {
|
|
458
|
-
return { status: "not-present" };
|
|
459
|
-
}
|
|
460
|
-
const tokenBase64 = match[1].replace(/\s+/g, "");
|
|
461
|
-
const tokenDer = Buffer.from(tokenBase64, "base64");
|
|
462
|
-
if (tokenDer.length === 0) {
|
|
463
|
-
return { status: "invalid", error: "Empty timestamp token." };
|
|
464
|
-
}
|
|
465
|
-
const asn1 = forge__default.default.asn1.fromDer(forge__default.default.util.createBuffer(tokenDer.toString("binary")));
|
|
466
|
-
const contentType = asn1Child(asn1, 0);
|
|
467
|
-
if (!contentType) {
|
|
468
|
-
return {
|
|
469
|
-
status: "invalid",
|
|
470
|
-
error: "Malformed timestamp token structure."
|
|
471
|
-
};
|
|
472
|
-
}
|
|
473
|
-
return { status: "valid" };
|
|
474
|
-
} catch (err) {
|
|
475
|
-
return {
|
|
476
|
-
status: "error",
|
|
477
|
-
error: `Timestamp evaluation failed: ${String(err)}`
|
|
478
|
-
};
|
|
479
|
-
}
|
|
480
|
-
}
|
|
481
|
-
|
|
482
|
-
// src/signature-node/certificate-utils.ts
|
|
483
|
-
function certificateInfoFromBase64(certBase64) {
|
|
484
|
-
try {
|
|
485
|
-
const certificate = new crypto__default.default.X509Certificate(Buffer.from(certBase64, "base64"));
|
|
486
|
-
return {
|
|
487
|
-
subject: certificate.subject || void 0,
|
|
488
|
-
issuer: certificate.issuer || void 0,
|
|
489
|
-
serialNumber: certificate.serialNumber || void 0,
|
|
490
|
-
validFrom: certificate.validFrom || void 0,
|
|
491
|
-
validTo: certificate.validTo || void 0
|
|
492
|
-
};
|
|
493
|
-
} catch {
|
|
494
|
-
return void 0;
|
|
495
|
-
}
|
|
496
|
-
}
|
|
497
|
-
function validateCertificateChain(certBase64List, additionalRootsPem) {
|
|
498
|
-
if (certBase64List.length === 0) {
|
|
499
|
-
return { status: "not-checked" };
|
|
500
|
-
}
|
|
501
|
-
try {
|
|
502
|
-
const chain = certBase64List.map((value) => {
|
|
503
|
-
const der = forge__default.default.util.decode64(value);
|
|
504
|
-
return forge__default.default.pki.certificateFromAsn1(forge__default.default.asn1.fromDer(der));
|
|
505
|
-
});
|
|
506
|
-
const rootPem = [...tls__default.default.rootCertificates, ...additionalRootsPem];
|
|
507
|
-
const caStore = forge__default.default.pki.createCaStore(rootPem);
|
|
508
|
-
const verified = forge__default.default.pki.verifyCertificateChain(caStore, chain);
|
|
509
|
-
return verified ? { status: "trusted" } : {
|
|
510
|
-
status: "untrusted",
|
|
511
|
-
error: "Certificate chain is not trusted."
|
|
512
|
-
};
|
|
513
|
-
} catch (error) {
|
|
514
|
-
return {
|
|
515
|
-
status: "untrusted",
|
|
516
|
-
error: `Certificate trust validation failed: ${String(error)}`
|
|
517
|
-
};
|
|
518
|
-
}
|
|
519
|
-
}
|
|
520
|
-
function signatureAlgorithmToVerifyAlgorithm(signatureMethod) {
|
|
521
|
-
switch (signatureMethod) {
|
|
522
|
-
case "http://www.w3.org/2000/09/xmldsig#rsa-sha1":
|
|
523
|
-
return "RSA-SHA1";
|
|
524
|
-
case "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256":
|
|
525
|
-
return "RSA-SHA256";
|
|
526
|
-
case "http://www.w3.org/2001/04/xmldsig-more#rsa-sha384":
|
|
527
|
-
return "RSA-SHA384";
|
|
528
|
-
case "http://www.w3.org/2001/04/xmldsig-more#rsa-sha512":
|
|
529
|
-
return "RSA-SHA512";
|
|
530
|
-
default:
|
|
531
|
-
return void 0;
|
|
532
|
-
}
|
|
533
|
-
}
|
|
534
|
-
function verifySignatureValue(signatureXml, certBase64List) {
|
|
535
|
-
try {
|
|
536
|
-
const parser = new xmldom.DOMParser();
|
|
537
|
-
const doc = parser.parseFromString(signatureXml, "text/xml");
|
|
538
|
-
const signatureNode = doc.getElementsByTagNameNS(XMLDSIG_NS, "Signature")[0];
|
|
539
|
-
if (!signatureNode) {
|
|
540
|
-
return "not-checked";
|
|
541
|
-
}
|
|
542
|
-
const certPem = certBase64List.length > 0 ? certPemFromBase64(certBase64List[0]) : void 0;
|
|
543
|
-
if (!certPem) {
|
|
544
|
-
return "not-checked";
|
|
545
|
-
}
|
|
546
|
-
const signedInfoNode = doc.getElementsByTagNameNS(XMLDSIG_NS, "SignedInfo")[0];
|
|
547
|
-
const signatureValueNode = doc.getElementsByTagNameNS(XMLDSIG_NS, "SignatureValue")[0];
|
|
548
|
-
if (!signedInfoNode || !signatureValueNode) {
|
|
549
|
-
return "invalid";
|
|
550
|
-
}
|
|
551
|
-
const canonicalizationMethod = doc.getElementsByTagNameNS(XMLDSIG_NS, "CanonicalizationMethod").item(0)?.getAttribute("Algorithm") ?? "http://www.w3.org/2001/10/xml-exc-c14n#";
|
|
552
|
-
const signatureMethod = doc.getElementsByTagNameNS(XMLDSIG_NS, "SignatureMethod").item(0)?.getAttribute("Algorithm");
|
|
553
|
-
const verifyAlgorithm = signatureAlgorithmToVerifyAlgorithm(signatureMethod ?? void 0);
|
|
554
|
-
if (!verifyAlgorithm) {
|
|
555
|
-
return "invalid";
|
|
556
|
-
}
|
|
557
|
-
const canonicalSignedInfo = canonicalizeNode(signedInfoNode, canonicalizationMethod);
|
|
558
|
-
const signatureValueBase64 = signatureValueNode.textContent?.replace(/\s+/g, "").trim() ?? "";
|
|
559
|
-
if (signatureValueBase64.length === 0) {
|
|
560
|
-
return "invalid";
|
|
561
|
-
}
|
|
562
|
-
const verifier = crypto__default.default.createVerify(verifyAlgorithm);
|
|
563
|
-
verifier.update(Buffer.from(canonicalSignedInfo, "utf8"));
|
|
564
|
-
verifier.end();
|
|
565
|
-
const isValid = verifier.verify(certPem, Buffer.from(signatureValueBase64, "base64"));
|
|
566
|
-
return isValid ? "verified" : "invalid";
|
|
567
|
-
} catch {
|
|
568
|
-
return "invalid";
|
|
569
|
-
}
|
|
570
|
-
}
|
|
571
|
-
function loadSigningMaterialFromBuffer(certificateBuffer, certificatePath, certificatePassword) {
|
|
572
|
-
const extension = path__default.default.extname(certificatePath).toLowerCase();
|
|
573
|
-
if (extension === ".pfx" || extension === ".p12") {
|
|
574
|
-
const p12Der = forge__default.default.util.createBuffer(Buffer.from(certificateBuffer).toString("binary"));
|
|
575
|
-
const p12Asn1 = forge__default.default.asn1.fromDer(p12Der);
|
|
576
|
-
const p12 = forge__default.default.pkcs12.pkcs12FromAsn1(p12Asn1, certificatePassword || "");
|
|
577
|
-
const keyBag = p12.getBags({
|
|
578
|
-
bagType: forge__default.default.pki.oids.pkcs8ShroudedKeyBag
|
|
579
|
-
})[forge__default.default.pki.oids.pkcs8ShroudedKeyBag];
|
|
580
|
-
const certBag = p12.getBags({ bagType: forge__default.default.pki.oids.certBag })[forge__default.default.pki.oids.certBag];
|
|
581
|
-
if (!keyBag || keyBag.length === 0 || !keyBag[0]?.key) {
|
|
582
|
-
throw new Error("No private key found in PKCS#12 certificate.");
|
|
583
|
-
}
|
|
584
|
-
if (!certBag || certBag.length === 0 || !certBag[0]?.cert) {
|
|
585
|
-
throw new Error("No certificate found in PKCS#12 certificate.");
|
|
586
|
-
}
|
|
587
|
-
return {
|
|
588
|
-
privateKeyPem: forge__default.default.pki.privateKeyToPem(keyBag[0].key),
|
|
589
|
-
certificatePem: forge__default.default.pki.certificateToPem(certBag[0].cert)
|
|
590
|
-
};
|
|
591
|
-
}
|
|
592
|
-
const pem = Buffer.from(certificateBuffer).toString("utf8");
|
|
593
|
-
const privateKeyMatch = pem.match(
|
|
594
|
-
/-----BEGIN (?:RSA |EC |ENCRYPTED )?PRIVATE KEY-----[\s\S]+?-----END (?:RSA |EC |ENCRYPTED )?PRIVATE KEY-----/m
|
|
595
|
-
);
|
|
596
|
-
const certMatch = pem.match(/-----BEGIN CERTIFICATE-----[\s\S]+?-----END CERTIFICATE-----/m);
|
|
597
|
-
if (!privateKeyMatch || !certMatch) {
|
|
598
|
-
throw new Error("PEM certificate must contain both private key and certificate.");
|
|
599
|
-
}
|
|
600
|
-
return {
|
|
601
|
-
privateKeyPem: privateKeyMatch[0],
|
|
602
|
-
certificatePem: certMatch[0]
|
|
603
|
-
};
|
|
604
|
-
}
|
|
605
|
-
function pemCertificateToBase64(pem) {
|
|
606
|
-
return pem.replace(/-----BEGIN CERTIFICATE-----/g, "").replace(/-----END CERTIFICATE-----/g, "").replace(/\s+/g, "").trim();
|
|
607
|
-
}
|
|
608
|
-
function extractPemCertificatesFromText(text) {
|
|
609
|
-
const matches = text.match(/-----BEGIN CERTIFICATE-----[\s\S]*?-----END CERTIFICATE-----/g) ?? [];
|
|
610
|
-
return matches.map((value) => value.trim()).filter((value) => value.length > 0);
|
|
611
|
-
}
|
|
612
|
-
function parseBooleanEnv(envValue) {
|
|
613
|
-
if (!envValue) {
|
|
614
|
-
return false;
|
|
615
|
-
}
|
|
616
|
-
const normalized = envValue.trim().toLowerCase();
|
|
617
|
-
return normalized === "1" || normalized === "true" || normalized === "yes";
|
|
618
|
-
}
|
|
619
|
-
function parseListEnv(envValue) {
|
|
620
|
-
if (!envValue) {
|
|
621
|
-
return [];
|
|
622
|
-
}
|
|
623
|
-
return envValue.split(/[;,]/).map((value) => value.trim()).filter((value) => value.length > 0);
|
|
624
|
-
}
|
|
625
|
-
async function loadEnterpriseTrustRoots() {
|
|
626
|
-
const roots = [];
|
|
627
|
-
const inlinePem = process.env[ENTERPRISE_TRUST_ROOTS_PEM_ENV];
|
|
628
|
-
if (inlinePem) {
|
|
629
|
-
roots.push(...extractPemCertificatesFromText(inlinePem));
|
|
630
|
-
}
|
|
631
|
-
const trustRootPaths = parseListEnv(process.env[ENTERPRISE_TRUST_ROOTS_FILE_ENV]);
|
|
632
|
-
for (const trustRootPath of trustRootPaths) {
|
|
633
|
-
try {
|
|
634
|
-
const pemText = await fs__default.default.readFile(trustRootPath, "utf8");
|
|
635
|
-
roots.push(...extractPemCertificatesFromText(pemText));
|
|
636
|
-
} catch {
|
|
637
|
-
}
|
|
638
|
-
}
|
|
639
|
-
return roots;
|
|
640
|
-
}
|
|
641
|
-
function getSignatureValidationPolicy() {
|
|
642
|
-
return {
|
|
643
|
-
requireRevocationCheck: parseBooleanEnv(process.env[ENTERPRISE_REQUIRE_REVOCATION_ENV]),
|
|
644
|
-
failOnRevocationUnknown: parseBooleanEnv(
|
|
645
|
-
process.env[ENTERPRISE_FAIL_ON_REVOCATION_UNKNOWN_ENV]
|
|
646
|
-
),
|
|
647
|
-
requireTimestamp: parseBooleanEnv(process.env[ENTERPRISE_REQUIRE_TIMESTAMP_ENV])
|
|
648
|
-
};
|
|
649
|
-
}
|
|
650
|
-
function extractReferenceTransforms(referenceNode) {
|
|
651
|
-
const transforms = [];
|
|
652
|
-
const transformNodes = referenceNode.getElementsByTagNameNS(XMLDSIG_NS, "Transform");
|
|
653
|
-
for (let index = 0; index < transformNodes.length; index += 1) {
|
|
654
|
-
const transformNode = transformNodes.item(index);
|
|
655
|
-
if (!transformNode) {
|
|
656
|
-
continue;
|
|
657
|
-
}
|
|
658
|
-
const algorithm = transformNode.getAttribute("Algorithm")?.trim();
|
|
659
|
-
if (!algorithm) {
|
|
660
|
-
continue;
|
|
661
|
-
}
|
|
662
|
-
const relationshipReferenceIds = [];
|
|
663
|
-
const childNodes = transformNode.getElementsByTagName("*");
|
|
664
|
-
for (let childIndex = 0; childIndex < childNodes.length; childIndex += 1) {
|
|
665
|
-
const childNode = childNodes.item(childIndex);
|
|
666
|
-
if (!childNode) {
|
|
667
|
-
continue;
|
|
668
|
-
}
|
|
669
|
-
if (getNodeLocalName(childNode) !== "RelationshipReference") {
|
|
670
|
-
continue;
|
|
671
|
-
}
|
|
672
|
-
const sourceId = childNode.getAttribute("SourceId")?.trim();
|
|
673
|
-
if (sourceId && sourceId.length > 0) {
|
|
674
|
-
relationshipReferenceIds.push(sourceId);
|
|
675
|
-
}
|
|
676
|
-
}
|
|
677
|
-
transforms.push({ algorithm, relationshipReferenceIds });
|
|
678
|
-
}
|
|
679
|
-
return transforms;
|
|
680
|
-
}
|
|
681
|
-
function applyRelationshipTransform(xmlText, relationshipIds) {
|
|
682
|
-
try {
|
|
683
|
-
const parser = new xmldom.DOMParser();
|
|
684
|
-
const serializer = new xmldom.XMLSerializer();
|
|
685
|
-
const doc = parser.parseFromString(xmlText, "text/xml");
|
|
686
|
-
const relationshipsRoot = getFirstDescendantElementByLocalName(doc, "Relationships");
|
|
687
|
-
if (!relationshipsRoot) {
|
|
688
|
-
return void 0;
|
|
689
|
-
}
|
|
690
|
-
if (relationshipIds.length === 0) {
|
|
691
|
-
return serializer.serializeToString(doc);
|
|
692
|
-
}
|
|
693
|
-
const idSet = new Set(relationshipIds);
|
|
694
|
-
const relationshipNodes = Array.from(relationshipsRoot.getElementsByTagName("*")).filter(
|
|
695
|
-
(node) => getNodeLocalName(node) === "Relationship"
|
|
696
|
-
);
|
|
697
|
-
for (const relationshipNode of relationshipNodes) {
|
|
698
|
-
const relId = relationshipNode.getAttribute("Id")?.trim();
|
|
699
|
-
if (!relId || !idSet.has(relId)) {
|
|
700
|
-
relationshipNode.parentNode?.removeChild(relationshipNode);
|
|
701
|
-
}
|
|
702
|
-
}
|
|
703
|
-
return serializer.serializeToString(doc);
|
|
704
|
-
} catch {
|
|
705
|
-
return void 0;
|
|
706
|
-
}
|
|
707
|
-
}
|
|
708
|
-
function applyReferenceTransforms(partBytes, transforms) {
|
|
709
|
-
let transformedBytes = partBytes;
|
|
710
|
-
const unsupportedAlgorithms = [];
|
|
711
|
-
for (const transform of transforms) {
|
|
712
|
-
if (transform.algorithm === OPC_RELATIONSHIP_TRANSFORM) {
|
|
713
|
-
const nextXml = applyRelationshipTransform(
|
|
714
|
-
Buffer.from(transformedBytes).toString("utf8"),
|
|
715
|
-
transform.relationshipReferenceIds
|
|
716
|
-
);
|
|
717
|
-
if (!nextXml) {
|
|
718
|
-
unsupportedAlgorithms.push(transform.algorithm);
|
|
719
|
-
continue;
|
|
720
|
-
}
|
|
721
|
-
transformedBytes = new Uint8Array(Buffer.from(nextXml, "utf8"));
|
|
722
|
-
continue;
|
|
723
|
-
}
|
|
724
|
-
if (!SUPPORTED_XML_CANON_TRANSFORMS.has(transform.algorithm)) {
|
|
725
|
-
unsupportedAlgorithms.push(transform.algorithm);
|
|
726
|
-
continue;
|
|
727
|
-
}
|
|
728
|
-
try {
|
|
729
|
-
const parser = new xmldom.DOMParser();
|
|
730
|
-
const doc = parser.parseFromString(
|
|
731
|
-
Buffer.from(transformedBytes).toString("utf8"),
|
|
732
|
-
"text/xml"
|
|
733
|
-
);
|
|
734
|
-
if (!doc.documentElement) {
|
|
735
|
-
unsupportedAlgorithms.push(transform.algorithm);
|
|
736
|
-
continue;
|
|
737
|
-
}
|
|
738
|
-
const canonical = canonicalizeNode(doc.documentElement, transform.algorithm);
|
|
739
|
-
transformedBytes = new Uint8Array(Buffer.from(canonical, "utf8"));
|
|
740
|
-
} catch {
|
|
741
|
-
unsupportedAlgorithms.push(transform.algorithm);
|
|
742
|
-
}
|
|
743
|
-
}
|
|
744
|
-
return { data: transformedBytes, unsupportedAlgorithms };
|
|
745
|
-
}
|
|
746
|
-
function computeDigestBase642(content, digestAlgorithmUri) {
|
|
747
|
-
const hashName = DIGEST_ALGORITHM_TO_HASH[digestAlgorithmUri];
|
|
748
|
-
if (!hashName) {
|
|
749
|
-
return void 0;
|
|
750
|
-
}
|
|
751
|
-
return crypto__default.default.createHash(hashName).update(Buffer.from(content)).digest("base64");
|
|
752
|
-
}
|
|
753
|
-
async function buildReferenceChecksFromSignatureXml(zip, signatureXml) {
|
|
754
|
-
const parser = new xmldom.DOMParser();
|
|
755
|
-
const doc = parser.parseFromString(signatureXml, "text/xml");
|
|
756
|
-
const referenceNodes = doc.getElementsByTagNameNS(XMLDSIG_NS, "Reference");
|
|
757
|
-
const checks = [];
|
|
758
|
-
for (let index = 0; index < referenceNodes.length; index += 1) {
|
|
759
|
-
const referenceNode = referenceNodes.item(index);
|
|
760
|
-
if (!referenceNode) {
|
|
761
|
-
continue;
|
|
762
|
-
}
|
|
763
|
-
const uri = referenceNode.getAttribute("URI")?.trim() ?? "";
|
|
764
|
-
const digestMethodNode = getFirstDescendantElementByLocalName(referenceNode, "DigestMethod");
|
|
765
|
-
const digestValueNode = getFirstDescendantElementByLocalName(referenceNode, "DigestValue");
|
|
766
|
-
const digestAlgorithm = digestMethodNode?.getAttribute("Algorithm")?.trim();
|
|
767
|
-
const digestExpectedBase64 = digestValueNode?.textContent?.replace(/\s+/g, "").trim();
|
|
768
|
-
const transforms = extractReferenceTransforms(referenceNode);
|
|
769
|
-
const transformAlgorithms = transforms.map((transform) => transform.algorithm);
|
|
770
|
-
const resolvedPartPath = resolveReferenceUriToPart(uri);
|
|
771
|
-
if (!resolvedPartPath) {
|
|
772
|
-
checks.push({
|
|
773
|
-
uri,
|
|
774
|
-
existsInPackage: true,
|
|
775
|
-
digestAlgorithm,
|
|
776
|
-
digestExpectedBase64,
|
|
777
|
-
digestStatus: "insufficient-data",
|
|
778
|
-
transformAlgorithms
|
|
779
|
-
});
|
|
780
|
-
continue;
|
|
781
|
-
}
|
|
782
|
-
const part = zip.file(resolvedPartPath);
|
|
783
|
-
if (!part) {
|
|
784
|
-
checks.push({
|
|
785
|
-
uri,
|
|
786
|
-
resolvedPartPath,
|
|
787
|
-
existsInPackage: false,
|
|
788
|
-
digestAlgorithm,
|
|
789
|
-
digestExpectedBase64,
|
|
790
|
-
digestStatus: "missing-part",
|
|
791
|
-
transformAlgorithms
|
|
792
|
-
});
|
|
793
|
-
continue;
|
|
794
|
-
}
|
|
795
|
-
if (!digestAlgorithm || !digestExpectedBase64) {
|
|
796
|
-
checks.push({
|
|
797
|
-
uri,
|
|
798
|
-
resolvedPartPath,
|
|
799
|
-
existsInPackage: true,
|
|
800
|
-
digestAlgorithm,
|
|
801
|
-
digestExpectedBase64,
|
|
802
|
-
digestStatus: "insufficient-data",
|
|
803
|
-
transformAlgorithms
|
|
804
|
-
});
|
|
805
|
-
continue;
|
|
806
|
-
}
|
|
807
|
-
const rawContent = await part.async("uint8array");
|
|
808
|
-
const transformed = applyReferenceTransforms(rawContent, transforms);
|
|
809
|
-
if (transformed.unsupportedAlgorithms.length > 0) {
|
|
810
|
-
checks.push({
|
|
811
|
-
uri,
|
|
812
|
-
resolvedPartPath,
|
|
813
|
-
existsInPackage: true,
|
|
814
|
-
digestAlgorithm,
|
|
815
|
-
digestExpectedBase64,
|
|
816
|
-
digestStatus: "unsupported-transform",
|
|
817
|
-
transformAlgorithms
|
|
818
|
-
});
|
|
819
|
-
continue;
|
|
820
|
-
}
|
|
821
|
-
const digestActualBase64 = computeDigestBase642(transformed.data, digestAlgorithm);
|
|
822
|
-
if (!digestActualBase64) {
|
|
823
|
-
checks.push({
|
|
824
|
-
uri,
|
|
825
|
-
resolvedPartPath,
|
|
826
|
-
existsInPackage: true,
|
|
827
|
-
digestAlgorithm,
|
|
828
|
-
digestExpectedBase64,
|
|
829
|
-
digestStatus: "unsupported-algorithm",
|
|
830
|
-
transformAlgorithms
|
|
831
|
-
});
|
|
832
|
-
continue;
|
|
833
|
-
}
|
|
834
|
-
checks.push({
|
|
835
|
-
uri,
|
|
836
|
-
resolvedPartPath,
|
|
837
|
-
existsInPackage: true,
|
|
838
|
-
digestAlgorithm,
|
|
839
|
-
digestExpectedBase64,
|
|
840
|
-
digestActualBase64,
|
|
841
|
-
digestStatus: digestActualBase64 === digestExpectedBase64 ? "verified" : "mismatch",
|
|
842
|
-
transformAlgorithms
|
|
843
|
-
});
|
|
844
|
-
}
|
|
845
|
-
return checks;
|
|
846
|
-
}
|
|
847
|
-
async function buildReferenceChecksFromPptxViewerManifest(zip, signatureXml) {
|
|
848
|
-
const parser = new xmldom.DOMParser();
|
|
849
|
-
const doc = parser.parseFromString(signatureXml, "text/xml");
|
|
850
|
-
const partNodes = Array.from(doc.getElementsByTagNameNS(PPTX_VIEWER_MANIFEST_NS, "Part"));
|
|
851
|
-
const checks = [];
|
|
852
|
-
for (const partNode of partNodes) {
|
|
853
|
-
const rawName = partNode.getAttribute("Name") || "";
|
|
854
|
-
const digestAlgorithm = partNode.getAttribute("DigestMethod") || void 0;
|
|
855
|
-
const digestExpectedBase64 = partNode.getAttribute("DigestValue") || void 0;
|
|
856
|
-
const resolvedPartPath = resolveReferenceUriToPart(rawName);
|
|
857
|
-
if (!resolvedPartPath) {
|
|
858
|
-
checks.push({
|
|
859
|
-
uri: rawName,
|
|
860
|
-
existsInPackage: true,
|
|
861
|
-
digestAlgorithm,
|
|
862
|
-
digestExpectedBase64,
|
|
863
|
-
digestStatus: "insufficient-data",
|
|
864
|
-
transformAlgorithms: []
|
|
865
|
-
});
|
|
866
|
-
continue;
|
|
867
|
-
}
|
|
868
|
-
const partFile = zip.file(resolvedPartPath);
|
|
869
|
-
if (!partFile) {
|
|
870
|
-
checks.push({
|
|
871
|
-
uri: rawName,
|
|
872
|
-
resolvedPartPath,
|
|
873
|
-
existsInPackage: false,
|
|
874
|
-
digestAlgorithm,
|
|
875
|
-
digestExpectedBase64,
|
|
876
|
-
digestStatus: "missing-part",
|
|
877
|
-
transformAlgorithms: []
|
|
878
|
-
});
|
|
879
|
-
continue;
|
|
880
|
-
}
|
|
881
|
-
if (!digestAlgorithm || !digestExpectedBase64) {
|
|
882
|
-
checks.push({
|
|
883
|
-
uri: rawName,
|
|
884
|
-
resolvedPartPath,
|
|
885
|
-
existsInPackage: true,
|
|
886
|
-
digestAlgorithm,
|
|
887
|
-
digestExpectedBase64,
|
|
888
|
-
digestStatus: "insufficient-data",
|
|
889
|
-
transformAlgorithms: []
|
|
890
|
-
});
|
|
891
|
-
continue;
|
|
892
|
-
}
|
|
893
|
-
const partBytes = await partFile.async("uint8array");
|
|
894
|
-
const actualDigest = computeDigestBase642(partBytes, digestAlgorithm);
|
|
895
|
-
if (!actualDigest) {
|
|
896
|
-
checks.push({
|
|
897
|
-
uri: rawName,
|
|
898
|
-
resolvedPartPath,
|
|
899
|
-
existsInPackage: true,
|
|
900
|
-
digestAlgorithm,
|
|
901
|
-
digestExpectedBase64,
|
|
902
|
-
digestStatus: "unsupported-algorithm",
|
|
903
|
-
transformAlgorithms: []
|
|
904
|
-
});
|
|
905
|
-
continue;
|
|
906
|
-
}
|
|
907
|
-
checks.push({
|
|
908
|
-
uri: rawName,
|
|
909
|
-
resolvedPartPath,
|
|
910
|
-
existsInPackage: true,
|
|
911
|
-
digestAlgorithm,
|
|
912
|
-
digestExpectedBase64,
|
|
913
|
-
digestActualBase64: actualDigest,
|
|
914
|
-
digestStatus: actualDigest === digestExpectedBase64 ? "verified" : "mismatch",
|
|
915
|
-
transformAlgorithms: []
|
|
916
|
-
});
|
|
917
|
-
}
|
|
918
|
-
return checks;
|
|
919
|
-
}
|
|
920
|
-
async function inspectPptxDigitalSignatures(data) {
|
|
921
|
-
try {
|
|
922
|
-
const zip = await JSZip__default.default.loadAsync(Buffer.from(data));
|
|
923
|
-
const signaturePaths = Object.keys(zip.files).filter(
|
|
924
|
-
(entryPath) => entryPath.startsWith("_xmlsignatures/") && entryPath.endsWith(".xml")
|
|
925
|
-
);
|
|
926
|
-
const rootRelsXml = await zip.file("_rels/.rels")?.async("string");
|
|
927
|
-
const hasOriginRelationship = Boolean(rootRelsXml?.includes(DIGITAL_SIGNATURE_ORIGIN_REL_TYPE));
|
|
928
|
-
if (signaturePaths.length === 0) {
|
|
929
|
-
return {
|
|
930
|
-
supported: true,
|
|
931
|
-
hasSignature: false,
|
|
932
|
-
signatureCount: 0,
|
|
933
|
-
signaturePaths: [],
|
|
934
|
-
verificationStatus: "unsigned",
|
|
935
|
-
hasOriginRelationship
|
|
936
|
-
};
|
|
937
|
-
}
|
|
938
|
-
const additionalRootsPem = await loadEnterpriseTrustRoots();
|
|
939
|
-
const policy = getSignatureValidationPolicy();
|
|
940
|
-
const details = [];
|
|
941
|
-
for (const signaturePath of signaturePaths) {
|
|
942
|
-
const signatureXml = await zip.file(signaturePath)?.async("string") ?? "";
|
|
943
|
-
const manifestChecks = await buildReferenceChecksFromPptxViewerManifest(zip, signatureXml);
|
|
944
|
-
const referenceChecks = manifestChecks.length > 0 ? manifestChecks : await buildReferenceChecksFromSignatureXml(zip, signatureXml);
|
|
945
|
-
const missingPartReferences = referenceChecks.filter((check) => check.digestStatus === "missing-part").map((check) => check.uri);
|
|
946
|
-
const unsupportedTransforms = referenceChecks.flatMap(
|
|
947
|
-
(check) => check.digestStatus === "unsupported-transform" ? check.transformAlgorithms : []
|
|
948
|
-
).filter((value, index, all) => all.indexOf(value) === index);
|
|
949
|
-
const certs = extractAllTagText(signatureXml, "X509Certificate");
|
|
950
|
-
const trust = validateCertificateChain(certs, additionalRootsPem);
|
|
951
|
-
const signatureValueStatus = verifySignatureValue(signatureXml, certs);
|
|
952
|
-
const leafCertPem = certs.length > 0 ? certPemFromBase64(certs[0]) : void 0;
|
|
953
|
-
const issuerCertPem = certs.length > 1 ? certPemFromBase64(certs[1]) : leafCertPem;
|
|
954
|
-
const revocation = leafCertPem ? await evaluateCertificateRevocation(leafCertPem, issuerCertPem) : {
|
|
955
|
-
status: "not-checked",
|
|
956
|
-
checkedOcspUrls: [],
|
|
957
|
-
checkedCrlUrls: []
|
|
958
|
-
};
|
|
959
|
-
const timestamp = await evaluateTimestampAuthority(signatureXml);
|
|
960
|
-
const detailBase = {
|
|
961
|
-
path: signaturePath,
|
|
962
|
-
signatureMethod: extractTagAttribute(
|
|
963
|
-
signatureXml,
|
|
964
|
-
"([\\w.-]+:)?SignatureMethod",
|
|
965
|
-
"Algorithm"
|
|
966
|
-
),
|
|
967
|
-
canonicalizationMethod: extractTagAttribute(
|
|
968
|
-
signatureXml,
|
|
969
|
-
"([\\w.-]+:)?CanonicalizationMethod",
|
|
970
|
-
"Algorithm"
|
|
971
|
-
),
|
|
972
|
-
signingTime: extractFirstTagText(signatureXml, "SigningTime"),
|
|
973
|
-
referenceCount: referenceChecks.length,
|
|
974
|
-
missingPartReferences,
|
|
975
|
-
unsupportedTransforms,
|
|
976
|
-
referenceChecks,
|
|
977
|
-
certificate: certs.length > 0 ? certificateInfoFromBase64(certs[0]) : void 0,
|
|
978
|
-
signatureValueStatus,
|
|
979
|
-
certificateTrustStatus: trust.status,
|
|
980
|
-
certificateTrustError: trust.error,
|
|
981
|
-
certificateRevocationStatus: revocation.status,
|
|
982
|
-
certificateRevocationError: revocation.error,
|
|
983
|
-
timestampAuthorityStatus: timestamp.status,
|
|
984
|
-
timestampAuthorityError: timestamp.error,
|
|
985
|
-
certificateFingerprintSha256: leafCertPem ? certFingerprintSha256(leafCertPem) : void 0
|
|
986
|
-
};
|
|
987
|
-
details.push({
|
|
988
|
-
...detailBase,
|
|
989
|
-
status: computeDetailStatus(detailBase, policy)
|
|
990
|
-
});
|
|
991
|
-
}
|
|
992
|
-
return {
|
|
993
|
-
supported: true,
|
|
994
|
-
hasSignature: true,
|
|
995
|
-
signatureCount: signaturePaths.length,
|
|
996
|
-
signaturePaths,
|
|
997
|
-
verificationStatus: computeVerificationStatus(details),
|
|
998
|
-
details,
|
|
999
|
-
hasOriginRelationship
|
|
1000
|
-
};
|
|
1001
|
-
} catch (error) {
|
|
1002
|
-
return {
|
|
1003
|
-
supported: true,
|
|
1004
|
-
hasSignature: false,
|
|
1005
|
-
signatureCount: 0,
|
|
1006
|
-
signaturePaths: [],
|
|
1007
|
-
verificationStatus: "invalid-package",
|
|
1008
|
-
error: `Unable to inspect digital signature parts: ${String(error)}`
|
|
1009
|
-
};
|
|
1010
|
-
}
|
|
1011
|
-
}
|
|
1012
|
-
|
|
1013
|
-
// src/signature-node/signing.ts
|
|
1014
|
-
async function upsertRootOriginRelationship(zip) {
|
|
1015
|
-
const parser = new xmldom.DOMParser();
|
|
1016
|
-
const serializer = new xmldom.XMLSerializer();
|
|
1017
|
-
const relsPath = "_rels/.rels";
|
|
1018
|
-
const xml = await zip.file(relsPath)?.async("string") || '<?xml version="1.0" encoding="UTF-8"?><Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"></Relationships>';
|
|
1019
|
-
const doc = parser.parseFromString(xml, "text/xml");
|
|
1020
|
-
const relationships = doc.documentElement;
|
|
1021
|
-
const existing = Array.from(relationships.getElementsByTagName("Relationship")).find(
|
|
1022
|
-
(node) => node.getAttribute("Type") === DIGITAL_SIGNATURE_ORIGIN_REL_TYPE
|
|
1023
|
-
);
|
|
1024
|
-
if (!existing) {
|
|
1025
|
-
const rel = doc.createElement("Relationship");
|
|
1026
|
-
rel.setAttribute("Id", "rIdDigitalSignatureOrigin");
|
|
1027
|
-
rel.setAttribute("Type", DIGITAL_SIGNATURE_ORIGIN_REL_TYPE);
|
|
1028
|
-
rel.setAttribute("Target", "_xmlsignatures/origin.sigs");
|
|
1029
|
-
relationships.appendChild(rel);
|
|
1030
|
-
}
|
|
1031
|
-
zip.file(relsPath, serializer.serializeToString(doc));
|
|
1032
|
-
}
|
|
1033
|
-
async function upsertContentTypesForSignature(zip, signaturePath) {
|
|
1034
|
-
const parser = new xmldom.DOMParser();
|
|
1035
|
-
const serializer = new xmldom.XMLSerializer();
|
|
1036
|
-
const pathInContentTypes = "[Content_Types].xml";
|
|
1037
|
-
const xml = await zip.file(pathInContentTypes)?.async("string") || "";
|
|
1038
|
-
if (!xml) {
|
|
1039
|
-
return;
|
|
1040
|
-
}
|
|
1041
|
-
const doc = parser.parseFromString(xml, "text/xml");
|
|
1042
|
-
const types = doc.documentElement;
|
|
1043
|
-
const ensureOverride = (partName, contentType) => {
|
|
1044
|
-
const existing = Array.from(types.getElementsByTagName("Override")).find(
|
|
1045
|
-
(node) => node.getAttribute("PartName") === partName
|
|
1046
|
-
);
|
|
1047
|
-
if (existing) {
|
|
1048
|
-
existing.setAttribute("ContentType", contentType);
|
|
1049
|
-
return;
|
|
1050
|
-
}
|
|
1051
|
-
const override = doc.createElement("Override");
|
|
1052
|
-
override.setAttribute("PartName", partName);
|
|
1053
|
-
override.setAttribute("ContentType", contentType);
|
|
1054
|
-
types.appendChild(override);
|
|
1055
|
-
};
|
|
1056
|
-
ensureOverride(
|
|
1057
|
-
"/_xmlsignatures/origin.sigs",
|
|
1058
|
-
"application/vnd.openxmlformats-package.digital-signature-origin"
|
|
1059
|
-
);
|
|
1060
|
-
ensureOverride(
|
|
1061
|
-
`/${signaturePath}`,
|
|
1062
|
-
"application/vnd.openxmlformats-package.digital-signature-xmlsignature+xml"
|
|
1063
|
-
);
|
|
1064
|
-
zip.file(pathInContentTypes, serializer.serializeToString(doc));
|
|
1065
|
-
}
|
|
1066
|
-
async function buildOfficeReferenceList(zip) {
|
|
1067
|
-
const references = [];
|
|
1068
|
-
const digestMethod = "http://www.w3.org/2001/04/xmlenc#sha256";
|
|
1069
|
-
for (const entryPath of Object.keys(zip.files)) {
|
|
1070
|
-
const entry = zip.file(entryPath);
|
|
1071
|
-
if (!entry || entry.dir) {
|
|
1072
|
-
continue;
|
|
1073
|
-
}
|
|
1074
|
-
if (entryPath.startsWith("_xmlsignatures/")) {
|
|
1075
|
-
continue;
|
|
1076
|
-
}
|
|
1077
|
-
const entryBytes = await entry.async("uint8array");
|
|
1078
|
-
const digestValue = crypto__default.default.createHash("sha256").update(Buffer.from(entryBytes)).digest("base64");
|
|
1079
|
-
references.push({
|
|
1080
|
-
uri: `/${normalizePartPath(entryPath)}`,
|
|
1081
|
-
digestMethod,
|
|
1082
|
-
digestValue
|
|
1083
|
-
});
|
|
1084
|
-
}
|
|
1085
|
-
references.sort((left, right) => left.uri.localeCompare(right.uri));
|
|
1086
|
-
return references;
|
|
1087
|
-
}
|
|
1088
|
-
function buildOfficeSignatureXml(references, privateKeyPem, certificatePem) {
|
|
1089
|
-
const signedInfoReferencesXml = references.map(
|
|
1090
|
-
(reference) => `<Reference URI="${escapeXmlAttr(reference.uri)}"><DigestMethod Algorithm="${escapeXmlAttr(reference.digestMethod)}"/><DigestValue>${escapeXmlText(reference.digestValue)}</DigestValue></Reference>`
|
|
1091
|
-
).join("");
|
|
1092
|
-
const signedInfoXml = `<SignedInfo xmlns="${XMLDSIG_NS}"><CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/><SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>${signedInfoReferencesXml}</SignedInfo>`;
|
|
1093
|
-
const canonicalSignedInfo = canonicalizeSignedInfoXml(signedInfoXml);
|
|
1094
|
-
const signer = crypto__default.default.createSign("RSA-SHA256");
|
|
1095
|
-
signer.update(Buffer.from(canonicalSignedInfo, "utf8"));
|
|
1096
|
-
signer.end();
|
|
1097
|
-
const signatureValueBase64 = signer.sign(privateKeyPem).toString("base64");
|
|
1098
|
-
const certificateBase64 = pemCertificateToBase64(certificatePem);
|
|
1099
|
-
if (!isValidBase64(signatureValueBase64)) {
|
|
1100
|
-
throw new Error("Generated SignatureValue is not valid base64");
|
|
1101
|
-
}
|
|
1102
|
-
if (!isValidBase64(certificateBase64)) {
|
|
1103
|
-
throw new Error("X.509 certificate is not valid base64");
|
|
1104
|
-
}
|
|
1105
|
-
const signingTime = (/* @__PURE__ */ new Date()).toISOString();
|
|
1106
|
-
return `<Signature xmlns="${XMLDSIG_NS}">${signedInfoXml}<SignatureValue>${escapeXmlText(signatureValueBase64)}</SignatureValue><KeyInfo><X509Data><X509Certificate>${escapeXmlText(certificateBase64)}</X509Certificate></X509Data></KeyInfo><Object><SignatureProperties><SignatureProperty><SigningTime>${escapeXmlText(signingTime)}</SigningTime></SignatureProperty></SignatureProperties></Object></Signature>`;
|
|
1107
|
-
}
|
|
1108
|
-
async function signPptxWithCertificate(data, certificateBuffer, options) {
|
|
1109
|
-
try {
|
|
1110
|
-
const signing = loadSigningMaterialFromBuffer(
|
|
1111
|
-
certificateBuffer,
|
|
1112
|
-
options.certificatePath,
|
|
1113
|
-
options.certificatePassword
|
|
1114
|
-
);
|
|
1115
|
-
const zip = await JSZip__default.default.loadAsync(Buffer.from(data));
|
|
1116
|
-
for (const entryPath of Object.keys(zip.files)) {
|
|
1117
|
-
if (entryPath.startsWith("_xmlsignatures/")) {
|
|
1118
|
-
zip.remove(entryPath);
|
|
1119
|
-
}
|
|
1120
|
-
}
|
|
1121
|
-
const signaturePath = "_xmlsignatures/sig1.xml";
|
|
1122
|
-
await upsertRootOriginRelationship(zip);
|
|
1123
|
-
await upsertContentTypesForSignature(zip, signaturePath);
|
|
1124
|
-
const references = await buildOfficeReferenceList(zip);
|
|
1125
|
-
const signatureXml = buildOfficeSignatureXml(
|
|
1126
|
-
references,
|
|
1127
|
-
signing.privateKeyPem,
|
|
1128
|
-
signing.certificatePem
|
|
1129
|
-
);
|
|
1130
|
-
zip.file(signaturePath, signatureXml);
|
|
1131
|
-
zip.file("_xmlsignatures/origin.sigs", "<SignatureOrigin/>");
|
|
1132
|
-
zip.file(
|
|
1133
|
-
"_xmlsignatures/_rels/origin.sigs.rels",
|
|
1134
|
-
`<?xml version="1.0" encoding="UTF-8"?><Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rIdSig1" Type="${DIGITAL_SIGNATURE_REL_TYPE}" Target="sig1.xml"/></Relationships>`
|
|
1135
|
-
);
|
|
1136
|
-
const signedData = await zip.generateAsync({ type: "uint8array" });
|
|
1137
|
-
const report = await inspectPptxDigitalSignatures(signedData);
|
|
1138
|
-
return { success: true, signedData, report };
|
|
1139
|
-
} catch (error) {
|
|
1140
|
-
const errorReport = {
|
|
1141
|
-
supported: true,
|
|
1142
|
-
hasSignature: false,
|
|
1143
|
-
signatureCount: 0,
|
|
1144
|
-
signaturePaths: [],
|
|
1145
|
-
verificationStatus: "error",
|
|
1146
|
-
error: `Failed to sign PPTX: ${String(error)}`
|
|
1147
|
-
};
|
|
1148
|
-
return {
|
|
1149
|
-
success: false,
|
|
1150
|
-
report: errorReport,
|
|
1151
|
-
error: `Failed to sign PPTX: ${String(error)}`
|
|
1152
|
-
};
|
|
1153
|
-
}
|
|
1154
|
-
}
|
|
1155
|
-
|
|
1156
|
-
exports.DIGEST_ALGORITHM_TO_HASH = DIGEST_ALGORITHM_TO_HASH;
|
|
1157
|
-
exports.DIGEST_ALGORITHM_TO_WEB_CRYPTO = DIGEST_ALGORITHM_TO_WEB_CRYPTO;
|
|
1158
|
-
exports.DIGITAL_SIGNATURE_ORIGIN_REL_TYPE = DIGITAL_SIGNATURE_ORIGIN_REL_TYPE;
|
|
1159
|
-
exports.DIGITAL_SIGNATURE_REL_TYPE = DIGITAL_SIGNATURE_REL_TYPE;
|
|
1160
|
-
exports.ENTERPRISE_FAIL_ON_REVOCATION_UNKNOWN_ENV = ENTERPRISE_FAIL_ON_REVOCATION_UNKNOWN_ENV;
|
|
1161
|
-
exports.ENTERPRISE_REQUIRE_REVOCATION_ENV = ENTERPRISE_REQUIRE_REVOCATION_ENV;
|
|
1162
|
-
exports.ENTERPRISE_REQUIRE_TIMESTAMP_ENV = ENTERPRISE_REQUIRE_TIMESTAMP_ENV;
|
|
1163
|
-
exports.ENTERPRISE_TRUST_ROOTS_FILE_ENV = ENTERPRISE_TRUST_ROOTS_FILE_ENV;
|
|
1164
|
-
exports.ENTERPRISE_TRUST_ROOTS_PEM_ENV = ENTERPRISE_TRUST_ROOTS_PEM_ENV;
|
|
1165
|
-
exports.OPC_RELATIONSHIP_TRANSFORM = OPC_RELATIONSHIP_TRANSFORM;
|
|
1166
|
-
exports.PPTX_VIEWER_MANIFEST_NS = PPTX_VIEWER_MANIFEST_NS;
|
|
1167
|
-
exports.SUPPORTED_XML_CANON_TRANSFORMS = SUPPORTED_XML_CANON_TRANSFORMS;
|
|
1168
|
-
exports.XMLDSIG_NS = XMLDSIG_NS;
|
|
1169
|
-
exports.XML_TRANSFORM_ENVELOPED_SIGNATURE = XML_TRANSFORM_ENVELOPED_SIGNATURE;
|
|
1170
|
-
exports.applyReferenceTransforms = applyReferenceTransforms;
|
|
1171
|
-
exports.buildOcspRequestDer = buildOcspRequestDer;
|
|
1172
|
-
exports.buildReferenceChecksFromPptxViewerManifest = buildReferenceChecksFromPptxViewerManifest;
|
|
1173
|
-
exports.buildReferenceChecksFromSignatureXml = buildReferenceChecksFromSignatureXml;
|
|
1174
|
-
exports.canonicalizeNode = canonicalizeNode;
|
|
1175
|
-
exports.canonicalizeSignedInfoXml = canonicalizeSignedInfoXml;
|
|
1176
|
-
exports.certFingerprintSha256 = certFingerprintSha256;
|
|
1177
|
-
exports.certPemFromBase64 = certPemFromBase64;
|
|
1178
|
-
exports.certificateInfoFromBase64 = certificateInfoFromBase64;
|
|
1179
|
-
exports.computeDetailStatus = computeDetailStatus;
|
|
1180
|
-
exports.computeDigestBase64 = computeDigestBase642;
|
|
1181
|
-
exports.computeDigestBase64WebCrypto = computeDigestBase64;
|
|
1182
|
-
exports.computeVerificationStatus = computeVerificationStatus;
|
|
1183
|
-
exports.escapeXmlAttr = escapeXmlAttr;
|
|
1184
|
-
exports.escapeXmlText = escapeXmlText;
|
|
1185
|
-
exports.evaluateCertificateRevocation = evaluateCertificateRevocation;
|
|
1186
|
-
exports.evaluateTimestampAuthority = evaluateTimestampAuthority;
|
|
1187
|
-
exports.extractAllTagText = extractAllTagText;
|
|
1188
|
-
exports.extractFirstTagText = extractFirstTagText;
|
|
1189
|
-
exports.extractOcspUrls = extractOcspUrls;
|
|
1190
|
-
exports.extractPemCertificatesFromText = extractPemCertificatesFromText;
|
|
1191
|
-
exports.extractReferenceTransforms = extractReferenceTransforms;
|
|
1192
|
-
exports.extractTagAttribute = extractTagAttribute;
|
|
1193
|
-
exports.getFirstDescendantElementByLocalName = getFirstDescendantElementByLocalName;
|
|
1194
|
-
exports.getNodeLocalName = getNodeLocalName;
|
|
1195
|
-
exports.getSignatureValidationPolicy = getSignatureValidationPolicy;
|
|
1196
|
-
exports.inspectPptxDigitalSignatures = inspectPptxDigitalSignatures;
|
|
1197
|
-
exports.isValidBase64 = isValidBase64;
|
|
1198
|
-
exports.loadEnterpriseTrustRoots = loadEnterpriseTrustRoots;
|
|
1199
|
-
exports.loadSigningMaterialFromBuffer = loadSigningMaterialFromBuffer;
|
|
1200
|
-
exports.normalizePartPath = normalizePartPath;
|
|
1201
|
-
exports.parseOcspResponseStatus = parseOcspResponseStatus;
|
|
1202
|
-
exports.pemCertificateToBase64 = pemCertificateToBase64;
|
|
1203
|
-
exports.resolveReferenceUriToPart = resolveReferenceUriToPart;
|
|
1204
|
-
exports.signPptxWithCertificate = signPptxWithCertificate;
|
|
1205
|
-
exports.validateCertificateChain = validateCertificateChain;
|
|
1206
|
-
exports.verifySignatureValue = verifySignatureValue;
|