From af19c31ee67dd014df4938221521c68080180973 Mon Sep 17 00:00:00 2001
From: Pierre Leroux
Date: Mon, 14 Sep 2026 11:42:23 +0200
Subject: [PATCH 1/2] Refactor LCP CRL caching and add build-time fallback
---
src/main.ts | 161 +++----------
src/main/services/lcpCrlCache.ts | 213 ++++++++++++++++++
.../r2-lcp-js/parser/epub/lcp-certificate.ts | 40 ++--
src/r2-xxx-js/r2-lcp-js/parser/epub/lcp.ts | 8 +-
4 files changed, 270 insertions(+), 152 deletions(-)
create mode 100644 src/main/services/lcpCrlCache.ts
diff --git a/src/main.ts b/src/main.ts
index fac769e411..247cadbdb8 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -10,7 +10,7 @@ import * as path from "node:path";
import * as fs from "node:fs";
import { commandLineMainEntry } from "readium-desktop/main/cli";
import { httpGetWithAuth } from "readium-desktop/main/network/http";
-import { CRL_URL, DUMMY_CRL } from "@r2-lcp-js/parser/epub/lcp-certificate";
+import { BUILD_CRL, BUILD_CRL_CACHED_AT, CRL_URL } from "@r2-lcp-js/parser/epub/lcp-certificate";
import { setLcpNativePluginPath, setCRLGetter } from "@r2-lcp-js/parser/epub/lcp";
import { initGlobalConverters_OPDS } from "@r2-opds-js/opds/init-globals";
import {
@@ -25,6 +25,7 @@ import { app } from "electron";
import { _APP_NAME, _APP_VERSION, _PACK_NAME } from "readium-desktop/preprocessor-directives";
import { FORCE_PROD_DB_IN_DEV, USER_DATA_FOLDER } from "readium-desktop/common/constant";
import { appendFileSyncWithRotation } from "readium-desktop/utils/log";
+import { LcpCrlCache } from "./main/services/lcpCrlCache";
// isURL() excludes the file: and data: URL protocols; the compile-time TLD policy decides whether localhost / non-TLD hosts are accepted (note that ftp: is accepted)
// import isURL from "validator/lib/isURL";
@@ -71,139 +72,37 @@ initGlobalConverters_GENERIC();
const lcpNativePluginPath = path.normalize(path.join(__dirname, "external-assets", "lcp.node"));
setLcpNativePluginPath(lcpNativePluginPath);
-interface ILcpCrlCache {
- crlPem: string;
- etag: string | undefined;
- lastModified: string | undefined;
- validatedAt: number;
- expiresAt: number;
- refreshPromise: Promise | undefined;
-}
-
-const lcpCrlCache: ILcpCrlCache = {
- crlPem: DUMMY_CRL,
- etag: undefined,
- lastModified: undefined,
- validatedAt: 0,
- expiresAt: 0,
- refreshPromise: undefined,
-};
-
-const LCP_CRL_CACHE_FALLBACK_FRESHNESS_MS = 60 * 60 * 1000;
-
-const getCacheControlMaxAgeMs = (cacheControl: string | undefined): number | undefined => {
- if (!cacheControl) {
- return undefined;
- }
- let maxAgeMs: number | undefined;
- for (const directive of cacheControl.split(",")) {
- const [rawName, rawValue] = directive.trim().split("=", 2);
- const name = rawName.trim().toLowerCase();
- const value = rawValue?.trim();
- if (name === "no-cache" || name === "no-store") {
- return 0;
- }
- if (name === "max-age" && value) {
- const seconds = Number(value.replace(/^"|"$/g, ""));
- if (Number.isFinite(seconds) && seconds >= 0) {
- maxAgeMs = seconds * 1000;
- }
- }
- }
- return maxAgeMs;
-};
-
-const getLcpCrlExpiresAt = (headers: { get(name: string): string | null } | undefined, validatedAt: number): number => {
- const cacheControlMaxAgeMs = getCacheControlMaxAgeMs(headers?.get("cache-control") || undefined);
- return validatedAt + (cacheControlMaxAgeMs ?? LCP_CRL_CACHE_FALLBACK_FRESHNESS_MS);
-};
-
-const isLcpCrlCacheExpired = () =>
- Date.now() >= lcpCrlCache.expiresAt;
-
-const refreshLcpCrlCache = (): Promise => {
- debug("REFRESH LCP CRL REQUEST", lcpCrlCache);
- if (typeof lcpCrlCache.refreshPromise !== "undefined") {
- return lcpCrlCache.refreshPromise;
- }
-
- lcpCrlCache.refreshPromise = (async () => {
- try {
- const headers: Record = {
+const lcpCrlCache = new LcpCrlCache({
+ defaultCrlPem: BUILD_CRL,
+ defaultCrlCachedAt: BUILD_CRL_CACHED_AT,
+ fetchCrl: async () => {
+ debug("LCP CRL HTTP fetch");
+ // RFC 2585 Security Considerations: CRL retrieval does not need
+ // authentication, so this uses Thorium's no-auth HTTP helper.
+ const res = await httpGetWithAuth(false)(CRL_URL, {
+ headers: {
Accept: ContentType.PkixCrl,
- };
- if (lcpCrlCache.etag) {
- headers["If-None-Match"] = lcpCrlCache.etag;
- }
- if (lcpCrlCache.lastModified) {
- headers["If-Modified-Since"] = lcpCrlCache.lastModified;
- }
- // RFC 2585 Security Considerations: CRL retrieval does not need
- // authentication, so this uses Thorium's no-auth HTTP helper.
- const res = await httpGetWithAuth(false)(CRL_URL, {
- headers,
- // Reject redirects so the native LCP plugin receives bytes from the
- // configured CRL endpoint only.
- redirect: "error",
- });
- if (res.statusCode === 304) {
- lcpCrlCache.lastModified = res.response.headers?.get("last-modified") || lcpCrlCache.lastModified;
- const validatedAt = Date.now();
- lcpCrlCache.validatedAt = validatedAt;
- lcpCrlCache.expiresAt = getLcpCrlExpiresAt(res.response.headers, validatedAt);
- debug("LCP CRL HTTP cache refreshed: not modified");
- return;
- }
- const mediaType = res.contentType?.split(";")[0].trim().toLowerCase();
- // RFC 5280 section 4.2.1.13 says HTTP CRL distribution point URIs point
- // to a single DER encoded CRL, and HTTP servers SHOULD respond with
- // Content-Type application/pkix-crl.
- // https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.13
- // RFC 2585 section 4.2 registers application/pkix-crl.
- // https://datatracker.ietf.org/doc/html/rfc2585#section-4.2
- // RFC 2585 Security Considerations: authentication is not necessary
- // to retrieve certificates and CRLs.
- // https://datatracker.ietf.org/doc/html/rfc2585#page-6
- if (res.statusCode === 200 && mediaType === ContentType.PkixCrl) {
- const buf = await res.response.buffer();
- const lcplStr = "-----BEGIN X509 CRL-----\n" + buf.toString("base64") + "\n-----END X509 CRL-----";
- lcpCrlCache.crlPem = lcplStr;
- lcpCrlCache.etag = res.response.headers?.get("etag") || undefined; // '"295-65b0d9de8addd"' double quote is included
- lcpCrlCache.lastModified = res.response.headers?.get("last-modified") || undefined;
- const validatedAt = Date.now();
- lcpCrlCache.validatedAt = validatedAt;
- lcpCrlCache.expiresAt = getLcpCrlExpiresAt(res.response.headers, validatedAt);
- debug("LCP CRL HTTP fetch success");
- debug(lcplStr);
- return;
- }
- debug(`LCP CRL HTTP fetch fail; keeping cached CRL (${res.statusCode} ${res.contentType})`);
- } catch (err) {
- debug("LCP CRL HTTP fetch error; keeping cached CRL");
- debug(err);
+ },
+ // Reject redirects so the native LCP plugin receives bytes from the
+ // configured CRL endpoint only.
+ redirect: "error",
+ });
+ if (res.statusCode !== 200 || !res.response?.buffer) {
+ throw new Error(`LCP CRL HTTP fetch failed (${res.statusCode || res.statusMessage || "unknown error"})`);
}
- })().finally(() => {
- lcpCrlCache.refreshPromise = undefined;
- });
-
- return lcpCrlCache.refreshPromise;
-};
-const initLcpCrlCacheValidatedAt = lcpCrlCache.validatedAt;
-refreshLcpCrlCache().then(() => {
- debug(lcpCrlCache.validatedAt > initLcpCrlCacheValidatedAt ? "INIT LCP CRL LOADED" : "INIT LCP CRL FAILED");
- debug(lcpCrlCache);
-}).catch((err) => {
- debug("INIT LCP CRL FAILED");
- debug(err);
-});
-
-setCRLGetter(async (): Promise => {
- const crlPem = lcpCrlCache.crlPem;
- if (isLcpCrlCacheExpired()) {
- void refreshLcpCrlCache();
- }
- return crlPem;
+ const der = await res.response.buffer();
+ debug("LCP CRL HTTP fetch success");
+ return der;
+ },
+ log: (message, error) => {
+ debug(message);
+ if (typeof error !== "undefined") {
+ debug(error);
+ }
+ },
});
+lcpCrlCache.preload();
+setCRLGetter((): Promise => lcpCrlCache.retrieve());
app.commandLine.appendSwitch("autoplay-policy", "no-user-gesture-required");
app.commandLine.appendSwitch("enable-speech-dispatcher");
diff --git a/src/main/services/lcpCrlCache.ts b/src/main/services/lcpCrlCache.ts
new file mode 100644
index 0000000000..52ce020250
--- /dev/null
+++ b/src/main/services/lcpCrlCache.ts
@@ -0,0 +1,213 @@
+// ==LICENSE-BEGIN==
+// Copyright 2017 European Digital Reading Lab. All rights reserved.
+// Licensed to the Readium Foundation under one or more contributor license agreements.
+// Use of this source code is governed by a BSD-style license
+// that can be found in the LICENSE file exposed on Github (readium) in the project repository.
+// ==LICENSE-END==
+
+import { Buffer } from "node:buffer";
+
+const PEM_HEADER = "-----BEGIN X509 CRL-----";
+const PEM_FOOTER = "-----END X509 CRL-----";
+
+export const LCP_CRL_CACHE_EXPIRATION_MS = 7 * 24 * 60 * 60 * 1000;
+
+interface ILcpCrlCacheEntry {
+ crlPem: string;
+ cachedAt: number;
+}
+
+interface ILcpCrlMemoryEntry {
+ crlPem: string;
+ isExpired: boolean;
+}
+
+export const encodeLcpCrlPem = (der: Uint8Array): string =>
+ `${PEM_HEADER}${Buffer.from(der).toString("base64")}${PEM_FOOTER}`;
+
+/**
+ * Checks that the data has the outer DER structure of an X.509 CRL.
+ *
+ * CertificateList is a SEQUENCE whose first element, tbsCertList, is another
+ * SEQUENCE. This deliberately does not parse the full CRL; it rejects non-DER
+ * responses such as captive portal HTML and truncated downloads.
+ */
+export const isX509Crl = (data: Uint8Array): boolean => {
+ if (data.length < 2 || data[0] !== 0x30) {
+ return false;
+ }
+
+ let headerSize: number;
+ let contentLength: number;
+ if ((data[1] & 0x80) === 0) {
+ headerSize = 2;
+ contentLength = data[1];
+ } else {
+ const lengthSize = data[1] & 0x7F;
+ if (lengthSize < 1 || lengthSize > 4 || data.length < 2 + lengthSize) {
+ return false;
+ }
+ headerSize = 2 + lengthSize;
+ contentLength = data.slice(2, headerSize).reduce((length, byte) => length * 256 + byte, 0);
+ }
+
+ if (headerSize + contentLength !== data.length) {
+ return false;
+ }
+
+ return data.length > headerSize && data[headerSize] === 0x30;
+};
+
+interface ILcpCrlCacheOptions {
+ fetchCrl: () => Promise;
+ expirationMs?: number;
+ defaultCrlPem?: string;
+ defaultCrlCachedAt?: number;
+ now?: () => number;
+ log?: (message: string, error?: unknown) => void;
+}
+
+/**
+ * In-memory CRL cache following readium/swift-toolkit's refresh behavior, with
+ * Swift's readLocal() model represented here as readMemory(). If no memory CRL
+ * has been seeded, a missing CRL blocks on the network, while an expired valid
+ * CRL is returned immediately and refreshed in the background.
+ *
+ * Thorium seeds this cache with the build-time BUILD_CRL constant and its
+ * BUILD_CRL_CACHED_AT timestamp. This gives liblcp a trusted fallback before
+ * the first network refresh completes, while using the same freshness rule for
+ * the bundled CRL as for a CRL fetched during the current process.
+ *
+ * This intentionally mirrors the Swift refresh algorithm, not its persistence
+ * semantics. The Swift toolkit implementation stores its cache in UserDefaults,
+ * which may be app-container scoped on sandboxed Apple platforms. In Electron,
+ * the closest equivalent is app.getPath("userData"), a normal per-user app data
+ * directory that another same-user process can usually modify or roll back.
+ *
+ * If this cache ever becomes disk-backed, treat the disk bytes and timestamps as
+ * untrusted hints only. Before promoting a persisted CRL into memory, verify the
+ * CRL signature against a pinned LCP CA/CRL-signing certificate or public key,
+ * verify issuer/AuthorityKeyIdentifier, require cRLSign on the signing
+ * certificate, and prefer signed CRL fields such as thisUpdate, nextUpdate and
+ * CRLNumber over a local cachedAt value. Invalid persisted data should be
+ * ignored and refreshed from the network instead of being passed to liblcp.
+ *
+ * Current logical model:
+ * startup:
+ * BUILD_CRL is seeded in memory with BUILD_CRL_CACHED_AT
+ * preload starts a network refresh only if the memory CRL is expired
+ * unlock:
+ * fresh memory CRL -> return it
+ * expired memory CRL -> return it and refresh in the background
+ * no memory CRL seed -> wait for one network refresh before unlocking
+ *
+ * Logical model for a future disk-backed cache:
+ * startup:
+ * disk CRL present and authenticated -> load into memory, then refresh if stale
+ * disk CRL missing/invalid/expired -> fall back to BUILD_CRL and refresh
+ * unlock:
+ * fresh memory CRL -> return it
+ * stale but authentic CRL -> return it and refresh in the background
+ * no authentic local CRL -> use BUILD_CRL or wait for one network refresh
+ */
+export class LcpCrlCache {
+ private readonly expirationMs: number;
+ private readonly fetchCrl: () => Promise;
+ private readonly log: (message: string, error?: unknown) => void;
+ private readonly now: () => number;
+ private memoryCrl: ILcpCrlCacheEntry | undefined;
+ private refreshPromise: Promise | undefined;
+
+ constructor(options: ILcpCrlCacheOptions) {
+ this.expirationMs = options.expirationMs ?? LCP_CRL_CACHE_EXPIRATION_MS;
+ this.fetchCrl = options.fetchCrl;
+ this.log = options.log ?? (() => undefined);
+ this.now = options.now ?? Date.now;
+
+ if (options.defaultCrlPem) {
+ this.memoryCrl = {
+ crlPem: options.defaultCrlPem,
+ cachedAt: options.defaultCrlCachedAt ?? this.now() - this.expirationMs,
+ };
+ }
+ }
+
+ /** Warms a missing or expired cache without making startup wait. */
+ public preload(): void {
+ this.log("LCP CRL PRELOAD");
+ const memoryCrl = this.readMemory();
+ if (memoryCrl?.isExpired ?? true) {
+ void this.refresh().catch((error) => this.log("LCP CRL preload failed", error));
+ }
+ }
+
+ public async retrieve(): Promise {
+ this.log("LCP CRL RETRIEVE");
+ const memoryCrl = this.readMemory();
+ if (!memoryCrl) {
+ return this.refresh();
+ }
+
+ if (memoryCrl.isExpired) {
+ void this.refresh().catch((error) => this.log("LCP CRL background refresh failed", error));
+ }
+ return memoryCrl.crlPem;
+ }
+
+ /**
+ * Swift's CRLService.readLocal() reads and validates UserDefaults. Thorium
+ * deliberately keeps the cache memory-only, so the equivalent decision point
+ * simply exposes the current memory CRL and whether it is expired.
+ */
+ private readMemory(): ILcpCrlMemoryEntry | undefined {
+ const memoryCrl = this.memoryCrl;
+ if (!memoryCrl) {
+ return undefined;
+ }
+
+ return {
+ crlPem: memoryCrl.crlPem,
+ isExpired: this.isExpired(memoryCrl),
+ };
+ }
+
+ private isExpired(entry: ILcpCrlCacheEntry): boolean {
+ return this.now() - entry.cachedAt >= this.expirationMs;
+ }
+
+ /** Starts a refresh, or returns the refresh already in flight. */
+ private refresh(): Promise {
+ if (typeof this.refreshPromise !== "undefined") {
+ return this.refreshPromise;
+ }
+
+ this.log("LCP CRL REFRESH");
+ const refreshPromise = this.fetchAndSave();
+ this.refreshPromise = refreshPromise;
+ void refreshPromise.then(
+ () => this.clearRefreshPromise(refreshPromise),
+ () => this.clearRefreshPromise(refreshPromise),
+ );
+ return refreshPromise;
+ }
+
+ private clearRefreshPromise(refreshPromise: Promise): void {
+ if (this.refreshPromise === refreshPromise) {
+ this.refreshPromise = undefined;
+ }
+ }
+
+ private async fetchAndSave(): Promise {
+ const der = await this.fetchCrl();
+ if (!isX509Crl(der)) {
+ throw new Error("The LCP CRL response is not a valid DER-encoded X.509 CRL");
+ }
+
+ const entry: ILcpCrlCacheEntry = {
+ crlPem: encodeLcpCrlPem(der),
+ cachedAt: this.now(),
+ };
+ this.memoryCrl = entry;
+ return entry.crlPem;
+ }
+}
diff --git a/src/r2-xxx-js/r2-lcp-js/parser/epub/lcp-certificate.ts b/src/r2-xxx-js/r2-lcp-js/parser/epub/lcp-certificate.ts
index cbfecdd2d8..d6f02b8d61 100644
--- a/src/r2-xxx-js/r2-lcp-js/parser/epub/lcp-certificate.ts
+++ b/src/r2-xxx-js/r2-lcp-js/parser/epub/lcp-certificate.ts
@@ -15,20 +15,26 @@ export const CRL_URL = "http://crl.edrlab.telesec.de/rl/EDRLab_CA.crl";
export const CRL_URL_ALT = "http://crl.edrlab.telesec.de/rl/Readium_LCP_Root_CA.crl";
// curl http://crl.edrlab.telesec.de/rl/Readium_LCP_Root_CA.crl -s | openssl crl -inform DER -text -noout
-export const DUMMY_CRL = `-----BEGIN X509 CRL-----
-MIICrTCBljANBgkqhkiG9w0BAQQFADBnMQswCQYDVQQGEwJGUjEOMAwGA1UEBxMF
-UGFyaXMxDzANBgNVBAoTBkVEUkxhYjESMBAGA1UECxMJTENQIFRlc3RzMSMwIQYD
-VQQDExpFRFJMYWIgUmVhZGl1bSBMQ1AgdGVzdCBDQRcNMTcwOTI2MTM1NTE1WhcN
-MjcwOTI0MTM1NTE1WjANBgkqhkiG9w0BAQQFAAOCAgEA27f50xnlaKGUdqs6u6rD
-WsR75z+tZrH4J2aA5E9I/K5fNe20FftQZb6XNjVQTNvawoMW0q+Rh9dVjDnV5Cfw
-ptchu738ZQr8iCOLQHvIM6wqQj7XwMqvyNaaeGMZxfRMGlx7T9DOwvtWFCc5X0ik
-YGPPV19CFf1cas8x9Y3LE8GmCtX9eUrotWLKRggG+qRTCri/SlaoicfzqhViiGeL
-dW8RpG/Q6ox+tLHti3fxOgZarMgMbRmUa6OTh8pnxrfnrdtD2PbwACvaEMCpNCZR
-aSTMRmIxw8UUbUA/JxDIwyISGn3ZRgbFAglYzaX80rSQZr6e0bFlzHl1xZtZ0Raz
-GQWP9vvfH5ESp6FsD98g//VYigatoPz/EKU4cfP+1W/Zrr4jRSBFB37rxASXPBcx
-L8cerb9nnRbAEvIqxnR4e0ZkhMyqIrLUZ3Jva0fC30kdtp09/KJ22mXKBz85wUQa
-7ihiSz7pov0R9hpY93fvt++idHBECRNGOeBC4wRtGxpru8ZUa0/KFOD0HXHMQDwV
-cIa/72T0okStOqjIOcWflxl/eAvUXwtet9Ht3o9giSl6hAObAeleMJOB37Bq9ASf
-h4w7d5he8zqfsCGjaG1OVQNWVAGxQQViWVysfcJohny4PIVAc9KkjCFa/QrkNGjr
-kUiV/PFCwL66iiF666DrXLY=
------END X509 CRL-----`;
+/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+// run this at each new build to update the CRL:
+node -e "const url='http://crl.edrlab.telesec.de/rl/EDRLab_CA.crl'; const tick=String.fromCharCode(96); const now=Date.now(); fetch(url).then(async r=>{ if(!r.ok) throw new Error('HTTP '+r.status); const buf=Buffer.from(await r.arrayBuffer()); const b64=buf.toString('base64').match(/.{1,64}/g).join('\n'); console.log('// Build-time CRL fallback generated from CRL_URL on '+new Date(now).toISOString()+'.'); console.log('export const BUILD_CRL_CACHED_AT = '+now+';'); console.log('export const BUILD_CRL = '+tick+'-----BEGIN X509 CRL-----\n'+b64+'\n-----END X509 CRL-----'+tick+';'); }).catch(e=>{ console.error(e); process.exit(1); });"
+*/
+
+// Build-time CRL fallback generated from CRL_URL on 2026-09-14T09:37:19.832Z.
+export const BUILD_CRL_CACHED_AT = 1789378639832;
+export const BUILD_CRL = `-----BEGIN X509 CRL-----
+MIICkTCCAXkCAQEwDQYJKoZIhvcNAQELBQAwQjETMBEGA1UEChMKZWRybGFiLm9y
+ZzEXMBUGA1UECxMOZWRybGFiLm9yZyBMQ1AxEjAQBgNVBAMTCUVEUkxhYiBDQRcN
+MjYwOTEzMTQxNzA4WhcNMjYwOTE4MTQxNzA3WjCB0DAnAgg9/PrnYyy4ABcNMjYw
+ODA1MjAyMTEzWjAMMAoGA1UdFQQDCgEBMCgCCQCoPyWN9DqSBhcNMjYwNTI1MDc1
+NTAwWjAMMAoGA1UdFQQDCgEGMCgCCQCwrtK1lYNPKhcNMjYwODI4MTcyNzQ1WjAM
+MAoGA1UdFQQDCgEGMCcCCAD7am95HSWbFw0yNjAzMjMxMjQ1NThaMAwwCgYDVR0V
+BAMKAQYwKAIJAKjr9Zx5OfipFw0yNjAyMTIxMDE0MDJaMAwwCgYDVR0VBAMKAQag
+MDAuMB8GA1UdIwQYMBaAFNxc/JPkH5/usLrqUgsrylJc4MmHMAsGA1UdFAQEAgIN
+/jANBgkqhkiG9w0BAQsFAAOCAQEAhHCfMKjWaIORdex9iYL2WYOK/qOyRegPa+uT
+TeS6SAqFPwT8EWuo0aa9dSt2GXtMNfPEmOyxioVvhV2gfYyjbmoDyUJDlkySUAcO
+c4voHAuf4wT2y1GzuvI4pQNn3KkZu35HrWBy5pMFwrBkTSRlbTtYESpWlXAezrmD
+YN/kMSjDLSyan15L9r1Hkp1gKMxTluUByQW4pm1zY3MR19Gkew8RivQWt5yPUfCD
+3+HMgBYMnbHmxpUPi3LyfswTiMZxNT2BHiHy6Qdg3uC25tTQs3sq5ih1ErRMuCl/
+MVZZnnZh4KBBFzgcZsSFf8Kggn5SW9BUZRGN4QHuAY9Sma4fAQ==
+-----END X509 CRL-----`;
\ No newline at end of file
diff --git a/src/r2-xxx-js/r2-lcp-js/parser/epub/lcp.ts b/src/r2-xxx-js/r2-lcp-js/parser/epub/lcp.ts
index 91a772c1db..49ae4a77e1 100644
--- a/src/r2-xxx-js/r2-lcp-js/parser/epub/lcp.ts
+++ b/src/r2-xxx-js/r2-lcp-js/parser/epub/lcp.ts
@@ -19,7 +19,7 @@ import { JsonElementType, JsonObject, JsonProperty } from "ta-json-x";
// import { streamToBufferPromise } from "@r2-utils-js/_utils/stream/BufferUtils";
-import { DUMMY_CRL } from "./lcp-certificate";
+import { BUILD_CRL } from "./lcp-certificate";
import { Encryption } from "./lcp-encryption";
import { Link } from "./lcp-link";
import { Rights } from "./lcp-rights";
@@ -232,7 +232,7 @@ export class LCP {
this.init();
if (this._usesNativeNodePlugin) {
- const crlPem = _getCRLPem ? await _getCRLPem() : DUMMY_CRL;
+ const crlPem = _getCRLPem ? await _getCRLPem() : BUILD_CRL;
// always generates USER_KEY_CHECK_INVALID = 141
const sha256DummyPassphrase = "0".repeat(64);
@@ -290,7 +290,7 @@ export class LCP {
}
if (this._usesNativeNodePlugin) {
- const crlPem = _getCRLPem ? await _getCRLPem() : DUMMY_CRL;
+ const crlPem = _getCRLPem ? await _getCRLPem() : BUILD_CRL;
return new Promise((resolve, reject) => {
@@ -492,7 +492,7 @@ export class LCP {
// // // eslint-disable-next-line @typescript-eslint/prefer-promise-reject-errors
// // reject(err);
// debug(err);
- // resolve(DUMMY_CRL);
+ // resolve(BUILD_CRL);
// };
// const success = async (response: request.RequestResponse) => {
From 2f6ba2d8fa06a4b25574f8607b6a04514f048944 Mon Sep 17 00:00:00 2001
From: Pierre Leroux
Date: Mon, 14 Sep 2026 11:52:15 +0200
Subject: [PATCH 2/2] lint
---
src/r2-xxx-js/r2-lcp-js/parser/epub/lcp-certificate.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/r2-xxx-js/r2-lcp-js/parser/epub/lcp-certificate.ts b/src/r2-xxx-js/r2-lcp-js/parser/epub/lcp-certificate.ts
index d6f02b8d61..ef79f5e035 100644
--- a/src/r2-xxx-js/r2-lcp-js/parser/epub/lcp-certificate.ts
+++ b/src/r2-xxx-js/r2-lcp-js/parser/epub/lcp-certificate.ts
@@ -37,4 +37,4 @@ c4voHAuf4wT2y1GzuvI4pQNn3KkZu35HrWBy5pMFwrBkTSRlbTtYESpWlXAezrmD
YN/kMSjDLSyan15L9r1Hkp1gKMxTluUByQW4pm1zY3MR19Gkew8RivQWt5yPUfCD
3+HMgBYMnbHmxpUPi3LyfswTiMZxNT2BHiHy6Qdg3uC25tTQs3sq5ih1ErRMuCl/
MVZZnnZh4KBBFzgcZsSFf8Kggn5SW9BUZRGN4QHuAY9Sma4fAQ==
------END X509 CRL-----`;
\ No newline at end of file
+-----END X509 CRL-----`;