Skip to content

Repository files navigation

xeparaksts — open-source signing with the Latvian eID card

Signs and verifies with the eID card, without eParakstitajs 3's 190 MB of bundled JavaFX. Two binaries, no runtime dependency beyond the card middleware and libxml2. BSD-licensed; see LICENSE.

Card-signed output passes eParakstitajs 3's own validator end to end: QESIG, QTSA, profile LT, every check PASSED.

Install

Pick your distribution: install_void.md, install_ubuntu.md, install_debian.md, or install_other.md. Take note that only void has been tested. Then:

sh build.sh              builds eparaksts.bin and eparaksts_gui.bin
sh check_card_stack.sh   card inserted — checks each layer in turn

Build dependencies: a C compiler, OpenSSL headers with the static libssl.a / libcrypto.a, libxml2 headers, libX11 headers.

What is required, and what is not

required?
PC/SC daemon + CCID driver yes talks to the reader
Latvia-eID middleware yes eidlv-pkcs11.so, the only way to reach the card
eParakstitajs 3 no the official app; useful only as a cross-check
eParaksts Token Signing no browser extension, for signing inside web pages
Browser security device no card login by TLS client certificate

The first two are what the install documents cover. The other three live in optional/ and nothing here looks for them.

The middleware is closed-source and not ours to redistribute, so the Void installer downloads it from LVRTC and checks its SHA-256; the Debian and Ubuntu routes install the same .deb natively. It must land at exactly /opt/latvia-eid — it has RPATH=/opt/latvia-eid/lib and carries its own OpenSSL 3 and wxWidgets. Do not add that directory to /etc/ld.so.conf.d: its bundled libcrypto.so.3 would shadow the system one for every process. The RPATH is enough on its own.

Of the symlinks, the tool needs only /usr/lib/pkcs11/eidlv-pkcs11.so; the rest are conveniences from the package's own postinst.

Command line

./eparaksts.bin info                          slots, certificates, roles
./eparaksts.bin sign      <file> <out.p7s>    detached CAdES
./eparaksts.bin verify    <file> <sig.p7s> [chain.pem] [--trusted-list]
./eparaksts.bin sign-pdf  <in.pdf> <out.pdf>  PAdES
./eparaksts.bin verify-pdf <signed.pdf> [chain.pem]
./eparaksts.bin staple    <signed.pdf> <out.pdf>  add OCSP (B -> LT)
./eparaksts.bin probe     <in.pdf> <outdir>       variants, one PIN
./eparaksts.bin tsl       <ca.pem>            EU trusted list lookup
./eparaksts.bin xmlsig    <file.xml>          check an XMLDSig
./eparaksts.bin selftest                      format check, no card needed

Flags: --slot N, --pin PIN, --timestamp-url URL, --no-timestamp, --software-key (throwaway key, for testing file handling without the card).

Slot 0 is the authentication key (PIN1); slot 1 is the qualified signature key (PIN2) and is the default. Only the second is legally qualified, and verify says which one was used.

PIN safety: three wrong tries block the key. The tool refuses to start if the token reports FINAL-TRY, and never retries after a rejected PIN.

GUI

./eparaksts_gui.bin

Plain Xlib, core bitmap font, no toolkit, so the window is up immediately.

  1. Choose file..., or type the path straight into the File path box
  2. pick PIN1 - rehearsal (slot 0, no legal weight) or PIN2 - qualified (slot 1, a real qualified signature)
  3. focus the PIN box, type the PIN
  4. Sign or Verify

Tab moves focus, Enter presses, Esc quits, and each control's accelerator is the bold letter in its label — P the file path, F choose, 1/2 slot, T type the PIN, K picker command, S sign, V verify, C copy the log, Q quit. Ctrl+C and Alt+C also copy the log. Bare letters do nothing while a text field has focus, so a PIN containing "s" cannot start a signature.

Tab skips the File picker command box — it is set once and then left alone — but Tab still gets you out of it once you are in. Reach it with K or a click; its grey background is the reminder that it is not an everyday field.

.pdf in, <name>_signed.pdf out; anything else gets a detached <name>.p7s. The PIN goes to the tool on stdin, never on its command line where ps would show it, and is wiped from memory once used.

File picking, and an apology

There is no file dialog. The File path box is editable, so on a machine with no picker at all you can just type the path. The File picker command field runs whatever you put in it; %o is the file the picker must write the chosen path into, %d the directory to start in. It is saved to ~/.config/eparaksts/gui.conf as you type. The default is

st -e vifm --choose-files %o %d

Anything that can write a path to a file will do:

zenity --file-selection > %o
kdialog --getopenfilename %d > %o
alacritty -e ranger --choosefile=%o %d

The apology: the program does not ask XDG, a desktop portal, or anything else what your system's file picker is — it just runs that command. On a machine with neither vifm nor st, Choose file... fails until you edit the field — type the path instead. Doing this properly means a portal round trip over D-Bus, which is more machinery than the rest of this program put together, and it is a very low priority. Sorry.

What it produces

CAdES-T: detached CMS SignedData, RSA-2048 + SHA-256, signing contentType, messageDigest and signingCertificateV2, with an RFC 3161 timestamp attached. In a PDF that blob goes in /Contents with /SubFilter /ETSI.CAdES.detached, which is PAdES; staple then adds OCSP in /DSS, taking it from PAdES-B to PAdES-LT.

Timestamps come from a public EU-qualified TSA (Izenpe by default) because eParaksts' own timestamp service sits behind OAuth2 credentials. analysis.md has the full format and every pitfall met while working it out.

The card

Two slots, and they are not interchangeable:

slot 0 slot 1
token User PIN (Latvia eID) Signature PIN (Latvia eID QSCD)
keyUsage digitalSignature, keyEncipherment nonRepudiation only
qcStatements absent QcCompliance + QcSSCD
meaning authentication, PIN1 qualified signature, PIN2

A slot 0 signature verifies cryptographically and means nothing legally — the official app rejects it for being unqualified.

Known limits

  • PDFs using cross-reference streams (PDF 1.5+) are refused, not mangled
  • so are PDFs that already have an /AcroForm, which includes already-signed ones — a second signature needs that merge
  • lotl_anchors.pem is trust-on-first-use and expires 2027-11-17
  • eDoc / ASiC-E is not implemented

Issues in eParakstitajs 3, found while testing against it

  1. Its own signatures expire after ~15 minutes. It staples an OCSP response with a 15-minute nextUpdate, then tests freshness against now rather than validity at signing time, so a PAdES-LT signature it just made stops validating a quarter of an hour later. staple can refresh such a file.
  2. A signature dictionary with no /M gives Cannot invoke "Object.getClass()" because "date" is null, reported as a failed signing certificate check — a crash presented as a cryptographic failure.
  3. It asks for both PINs to sign. PIN1 is not needed; this tool signs with PIN2 alone and the result validates.
  4. It emits BER with indefinite lengths rather than DER.
  5. Its TSA and OCSP responder sit behind OAuth2, so no third party can use them.

Notes

Vibes

About

eparaksts for linux + x11

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages