Files
LibreLedger/vendor
LibrePortal 2cffc3ab07 Start LibreLedger: an encrypted money ledger that stores only ciphertext
LibreLedger is a single-person money ledger. The browser encrypts
everything (PBKDF2-HMAC-SHA256 with 250k iterations, then AES-256-GCM),
and a small standard-library Python server stores only the resulting
{v, salt, iv, ct} blob. Ledger files from the earlier Money Ledger version
open unchanged.

- On plain http to a LAN or VPN address, where browsers hide Web Crypto,
  the app switches to vendored @noble/hashes and @noble/ciphers 2.2.0 and
  says so on the lock screen. tests/crypto-interop.test.mjs shows both
  paths read each other's files.
- The server hands out only the app's own files, sends a self-only CSP,
  nosniff, frame denial and no-referrer, checks the shape of each blob,
  refuses cross-site writes and writes atomically. It keeps the last 10
  backups plus one per day for 30 days.
- The container runs that server from a digest-pinned python alpine
  image, as an unprivileged user, with its data in /data and a health
  check on /api/health.

Assisted-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 01:17:16 +01:00
..

Vendored crypto

LibreLedger encrypts in the browser. Where the page is a secure context (HTTPS, http://localhost) it uses the browser's Web Crypto API. Where it is not (plain http:// to a LAN or VPN address), browsers hide crypto.subtle, and crypto-fallback.js runs the same PBKDF2-HMAC-SHA256 and AES-256-GCM from these files instead. They are loaded only in that case, and the server serves only the files listed in server.py (VENDOR_FILES).

Package Version Released Licence npm tarball integrity
@noble/hashes 2.2.0 2026-04-11 MIT sha512-IYqDGiTXab6FniAgnSdZwgWbomxpy9FtYvLKs7wCUs2a8RkITG+DFGO1DM9cr+E3/RgADRpFjrKVaJ1z6sjtEg==
@noble/ciphers 2.2.0 2026-04-11 MIT sha512-Z6pjIZ/8IJcCGzb2S/0Px5J81yij85xASuk1teLNeg75bfT07MV3a/O2Mtn1I2se43k3lkVEcFaR10N4cgQcZA==

Both are zero-dependency libraries by Paul Miller. Audit history, from their READMEs: independent Cure53 audits of @noble/hashes 1.0.0 (Jan 2022) and @noble/ciphers 1.0.0 (Sep 2024), and a self-audit of exactly 2.2.0 for both (Apr 2026), which is why 2.2.0 is the version pinned here. The files below are unmodified copies of the compiled ES modules from those tarballs, with each package's LICENSE. Only the modules needed for PBKDF2-SHA256 and AES-GCM are kept. The tarballs were checked against the integrity values above before extracting.

File hashes

sha256sum:

d079978bb7dac51e88586dd881ea3d7be010624b876d332f395f4a2b934f5fbc  noble-ciphers-2.2.0/aes.js
f36671a5487c9c5050efacb58011c37c24c55a889803cb036cf9d9a6347c1e2d  noble-ciphers-2.2.0/LICENSE
87a6cdf9cad2cec61229404cc7f8da1214952a76114b8a6342a3e771f1127cd1  noble-ciphers-2.2.0/_polyval.js
cfb9806b3339c79544a48a7026ee3c868cd1f0a1555c5eef4854b7eec43ccb63  noble-ciphers-2.2.0/utils.js
137ed94227806b351a55b09801287a4dba72d2a35d3838730becf641271bc3dd  noble-hashes-2.2.0/hmac.js
4f221aee6e072336700c408c68ab3b96a3fc09f6aebe6f48f1bd99e5ef13faec  noble-hashes-2.2.0/LICENSE
8227b9b5cabf078a9d7f7317f7a1ace6e46627539aa9364667aec724e1636f14  noble-hashes-2.2.0/_md.js
d61f870b99cf8e67b6df0ae5f582c74177445844d6a92ea51e8060032d05b555  noble-hashes-2.2.0/pbkdf2.js
0fb8e3c3f2c73a890be2524ac5d2542aaed4decff69e561231a86131203b3973  noble-hashes-2.2.0/sha2.js
766b91a693a798f9d3cde97b25db4a6d0cef66b2ca21153d3d42424d37878870  noble-hashes-2.2.0/_u64.js
e2adfc13c846487feff0410bd5508a1d66f5ebadc3188f3a40a6b55449981e2f  noble-hashes-2.2.0/utils.js

Subresource-integrity form (sha384):

sha384-0YHayko0KfpRNxkCNkZTuDkZIofvvDbgY4URpQALCIVKUglJYgnGQ4qGfXFFzXe8  noble-ciphers-2.2.0/aes.js
sha384-RCB3oUufLSfqmAfe/Csz1PRWhjGYjVj48y1X4s/XpyPQk1Bcd2Pa54pZCBo+u7O1  noble-ciphers-2.2.0/LICENSE
sha384-IHfhTh3OeAiHoY7cB8F9YUBJ9rn6RieetfuNQZY75cIgd5UcjVqOCh6ssn0gIGed  noble-ciphers-2.2.0/_polyval.js
sha384-VZAiMxOScRfdOr1e2+N5h6GRH5I1mBeWGL1BSjmYLB9VuR/ODNHxvqbMzPIvwwXV  noble-ciphers-2.2.0/utils.js
sha384-jjTbQ/7iXpfi7CA4vLI8VW2ljCxeMnfWE6FYpgMrpYgpT+IKKDJLplkP3KlaqLXp  noble-hashes-2.2.0/hmac.js
sha384-a19tauEwIs1iP+2eFZxRsChHXNFBtRprDPX8zuYyn/tuG+DeZzAzH1Fv/gHlGrk7  noble-hashes-2.2.0/LICENSE
sha384-cQrKviUiqTLrOSU3x5KyKb6vIptltNRJQbJGyyXy0+QCUUpOHRwOLM0JuIE//YJB  noble-hashes-2.2.0/_md.js
sha384-z3+NFrFNV0hs4V5jqy3EAGPvJQAi902QeBkbt5se8GRLG02Vbdz12785sBD3H/mx  noble-hashes-2.2.0/pbkdf2.js
sha384-lVYA2l9kQw+NU1uh5QaTLMkTJaFpmFJuIohWWtXdZ5Ri+AdJ+gG7dWCnv0VpvBYt  noble-hashes-2.2.0/sha2.js
sha384-j/S9oLRxzt1W++wUX4Lid0ZXyDVraPlN3I5Olt/q+FBuApzg+Tk6GAjTioMmzu0G  noble-hashes-2.2.0/_u64.js
sha384-eVOwgoKAAt0Ur0m4yWkKHg9hlMn4HGGrUW0PlHqEzo7hYCelwnnIejIRYCoUIbbf  noble-hashes-2.2.0/utils.js

Checking them yourself

for p in hashes ciphers; do
  curl -sSfO "https://registry.npmjs.org/@noble/$p/-/$p-2.2.0.tgz"
  echo "$p sha512-$(openssl dgst -sha512 -binary $p-2.2.0.tgz | base64 -w0)"   # compare with the table
  mkdir -p "$p" && tar xzf "$p-2.2.0.tgz" -C "$p"
done
for f in noble-hashes-2.2.0/*;  do cmp "$f" "hashes/package/${f#*/}";  done
for f in noble-ciphers-2.2.0/*; do cmp "$f" "ciphers/package/${f#*/}"; done

Updating

Pick a release that has been out for a while, verify its tarball integrity against the npm registry, copy the same files into a new versioned folder, update the imports in crypto-fallback.js, VENDOR_FILES in server.py and this file, then run tests/crypto-interop.test.mjs. The modules still carry their //# sourceMappingURL comments; the maps are not shipped, so a browser's developer tools may note a missing map, which is harmless.