edockit 0.2.1 → 0.2.3

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/CHANGELOG.md CHANGED
@@ -5,6 +5,19 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.2.3] - 2025-12-30
9
+
10
+ ### Fixed
11
+
12
+ - **Long-Term Validation (LTV) for revoked certificates** - Signatures made before certificate revocation are now correctly validated as valid when a trusted timestamp proves the signing time
13
+
14
+ ## [0.2.2] - 2025-12-30
15
+
16
+ ### Fixed
17
+
18
+ - **proxyUrl now works for timestamp revocation** - TSA certificate revocation checks now correctly use the proxy
19
+ - **XPath DOM mismatch error in browser** - Fixed "Node cannot be used in a document other than the one in which it was created" error when parsing XML in browsers
20
+
8
21
  ## [0.2.1] - 2025-12-30
9
22
 
10
23
  ### Added
@@ -47,6 +60,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
47
60
  - File checksum verification (SHA-256/384/512)
48
61
  - Browser and Node.js support
49
62
 
63
+ [0.2.3]: https://github.com/edgarsj/edockit/compare/v0.2.2...v0.2.3
64
+ [0.2.2]: https://github.com/edgarsj/edockit/compare/v0.2.1...v0.2.2
50
65
  [0.2.1]: https://github.com/edgarsj/edockit/compare/v0.2.0...v0.2.1
51
66
  [0.2.0]: https://github.com/edgarsj/edockit/compare/v0.1.2...v0.2.0
52
67
  [0.1.2]: https://github.com/edgarsj/edockit/releases/tag/v0.1.2
@@ -1,4 +1,4 @@
1
- import { RevocationResult } from "../revocation/types";
1
+ import { RevocationResult, RevocationCheckOptions } from "../revocation/types";
2
2
  /**
3
3
  * Parsed timestamp information from RFC 3161 TimeStampToken
4
4
  */
@@ -45,4 +45,6 @@ export interface TimestampVerificationOptions {
45
45
  verifyTsaCertificate?: boolean;
46
46
  /** Check TSA certificate revocation */
47
47
  checkTsaRevocation?: boolean;
48
+ /** Options for TSA certificate revocation checking (timeouts, proxy, etc.) */
49
+ revocationOptions?: RevocationCheckOptions;
48
50
  }
package/dist/index.cjs.js CHANGED
@@ -111,9 +111,16 @@ function createXMLParser() {
111
111
  function queryByXPath(parent, xpathExpression, namespaces = NAMESPACES) {
112
112
  try {
113
113
  // Browser environment with native XPath
114
- if (typeof document !== "undefined" && document.evaluate) {
114
+ if (typeof document !== "undefined" && typeof document.evaluate === "function") {
115
+ // Use the document that owns the parent node, not the global document
116
+ const ownerDoc = "ownerDocument" in parent ? parent.ownerDocument : parent;
117
+ if (!ownerDoc || typeof ownerDoc.evaluate !== "function") {
118
+ // XMLDocuments from DOMParser don't have evaluate - silently return null
119
+ // (caller should use DOM traversal fallback)
120
+ return null;
121
+ }
115
122
  const nsResolver = createNsResolverForBrowser(namespaces);
116
- const result = document.evaluate(xpathExpression, parent, nsResolver, XPathResult.FIRST_ORDERED_NODE_TYPE, null);
123
+ const result = ownerDoc.evaluate(xpathExpression, parent, nsResolver, XPathResult.FIRST_ORDERED_NODE_TYPE, null);
117
124
  return result.singleNodeValue;
118
125
  }
119
126
  // Node.js environment with xpath module
@@ -161,9 +168,16 @@ function queryByXPath(parent, xpathExpression, namespaces = NAMESPACES) {
161
168
  function queryAllByXPath(parent, xpathExpression, namespaces = NAMESPACES) {
162
169
  try {
163
170
  // Browser environment with native XPath
164
- if (typeof document !== "undefined" && document.evaluate) {
171
+ if (typeof document !== "undefined" && typeof document.evaluate === "function") {
172
+ // Use the document that owns the parent node, not the global document
173
+ const ownerDoc = "ownerDocument" in parent ? parent.ownerDocument : parent;
174
+ if (!ownerDoc || typeof ownerDoc.evaluate !== "function") {
175
+ // XMLDocuments from DOMParser don't have evaluate - silently return empty
176
+ // (caller should use DOM traversal fallback)
177
+ return [];
178
+ }
165
179
  const nsResolver = createNsResolverForBrowser(namespaces);
166
- const result = document.evaluate(xpathExpression, parent, nsResolver, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
180
+ const result = ownerDoc.evaluate(xpathExpression, parent, nsResolver, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
167
181
  const elements = [];
168
182
  for (let i = 0; i < result.snapshotLength; i++) {
169
183
  elements.push(result.snapshotItem(i));
@@ -10122,7 +10136,7 @@ async function verifyTimestamp(timestampBase64, options = {}) {
10122
10136
  // Check TSA certificate revocation if requested
10123
10137
  if (options.checkTsaRevocation !== false) {
10124
10138
  try {
10125
- tsaRevocation = await checkCertificateRevocation(tsaCert);
10139
+ tsaRevocation = await checkCertificateRevocation(tsaCert, options.revocationOptions);
10126
10140
  // If TSA certificate is revoked, the timestamp is invalid
10127
10141
  if (tsaRevocation.status === "revoked") {
10128
10142
  return {
@@ -10530,6 +10544,7 @@ async function verifySignature(signatureInfo, files, options = {}) {
10530
10544
  timestampResult = await verifyTimestamp(signatureInfo.signatureTimestamp, {
10531
10545
  signatureValue: signatureInfo.signatureValue,
10532
10546
  verifyTsaCertificate: true,
10547
+ revocationOptions: options.revocationOptions,
10533
10548
  });
10534
10549
  if (timestampResult.isValid && timestampResult.info) {
10535
10550
  // Use timestamp time as the trusted signing time
@@ -10554,11 +10569,23 @@ async function verifySignature(signatureInfo, files, options = {}) {
10554
10569
  ...options.revocationOptions,
10555
10570
  });
10556
10571
  certResult.revocation = revocationResult;
10557
- // If certificate is revoked, mark certificate as invalid
10572
+ // If certificate is revoked, check if signature was made before revocation (LTV)
10558
10573
  if (revocationResult.status === "revoked") {
10559
- certResult.isValid = false;
10560
- certResult.reason = revocationResult.reason || "Certificate has been revoked";
10561
- errors.push(`Certificate revoked: ${revocationResult.reason || "No reason provided"}`);
10574
+ const revokedAt = revocationResult.revokedAt;
10575
+ // Long-Term Validation: if we have a trusted timestamp proving the signature
10576
+ // was made before revocation, the signature is still valid
10577
+ if (revokedAt && trustedSigningTime < revokedAt) {
10578
+ // Signature was made before revocation - still valid (LTV)
10579
+ certResult.revocation.isValid = true;
10580
+ certResult.revocation.reason = `Certificate was revoked on ${revokedAt.toISOString()}, but signature was made on ${trustedSigningTime.toISOString()} (before revocation)`;
10581
+ }
10582
+ else {
10583
+ // Signature was made after revocation or no revocation date available
10584
+ certResult.isValid = false;
10585
+ const revokedAtStr = revokedAt ? ` on ${revokedAt.toISOString()}` : "";
10586
+ certResult.reason = `Certificate was revoked${revokedAtStr}`;
10587
+ errors.push(`Certificate revoked${revokedAtStr}`);
10588
+ }
10562
10589
  }
10563
10590
  // Note: 'unknown' status is a soft fail - certificate remains valid
10564
10591
  // but user can check revocation.status to see if it couldn't be verified