Tachoparser verifies the authenticity of every signed data block in a tachograph file as part of the normal parsing pass. The outcome of each verification is recorded in theDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/traconiq/tachoparser/llms.txt
Use this file to discover all available pages before exploring further.
verified boolean field on the corresponding data block. This page explains the full verification flow, the certificate chain involved, and what it means when verified is false.
Algorithms by generation
The two tachograph generations use different cryptographic algorithms:-
1st generation (Digital Tachograph) — RSA signatures with SHA-1. Certificates are fixed-size 194-byte structures. The RSA modulus is 1024 bits; the exponent is 64 bits. All keys are stored as parsed
DecodedCertificateFirstGenentries in thePKsFirstGenmap. -
2nd generation (Smart Tachograph) — ECDSA signatures on brainpool elliptic curves, provided by the go-crypto brainpool package. The specific curve — and therefore the hash function — is determined by the certificate’s public key size:
Second-generation certificates are variable-length (204–341 bytes) and stored as
Curve Hash Key size brainpoolP256r1 SHA-256 32 bytes brainpoolP384r1 SHA-384 48 bytes brainpoolP512r1 SHA-512 64 bytes DecodedCertificateSecondGenentries in thePKsSecondGenmap.
The certificate chain
Both generations follow a three-level certificate hierarchy rooted at the ERCA (European Root Certification Authority):The PKsFirstGen and PKsSecondGen maps
At startup, internal/pkg/certificates loads all compiled-in certificate files and populates two package-level maps in the decoder package:
decoder.PKsFirstGen— keyed byuint64certificate holder reference; values areDecodedCertificateFirstGen.decoder.PKsSecondGen— keyed byuint64certificate holder reference; values areDecodedCertificateSecondGen.
dddparser binary logs the count of loaded keys at startup:
The certificate files are compiled into the binary at build time using Go’s
//go:embed directive in internal/pkg/certificates/certificates.go. No external files are read at runtime unless a pks1/ or pks2/ directory exists in the current working directory, in which case those files take precedence (“live mode”).Verification flow
Parse all data blocks (first pass)
UnmarshalTLV (for card data) or UnmarshalTV (for VU data) iterates over the binary input. Each data block — identified by a tag byte ending in 0x00 or 0x02 for TLV, or matched by a 2-byte TV tag for VU — is decoded into its Go struct and its byte range (start offset and length) is recorded in an internal map keyed by tag.Extract signing certificates from the parsed data
After the first pass, the decoder calls
SignCertificateFirstGen() and SignCertificateSecondGen() on the top-level struct. These methods:- Decode the member state certificate and the VU/card certificate embedded in the file.
- Register any newly seen certificates into
PKsFirstGen/PKsSecondGenso that subsequent files in a batch can benefit from previously seen member state keys. - Return the device-level certificate that will be used to verify data block signatures.
Verify each signature (second pass)
The decoder iterates the input a second time, now looking for signature blocks (tag byte ending in
0x01 or 0x03 for TLV). For each signature block found:- The corresponding data block’s byte range is looked up from the map built in step 1.
- For 1st-gen:
SignatureFirstGen.Verify()runs RSA PKCS#1 v1.5 verify with SHA-1 over the data bytes using the device certificate’s public key. - For 2nd-gen:
SignatureSecondGen.Verify()runs ECDSA verify over the data bytes using the brainpool public key extracted from the certificate chain. - The boolean result is written to the
Verifiedfield of the data block struct via reflection.
Propagate the verified field to JSON output
The
verified field is tagged with json:"verified" (and aper:"-" to exclude it from ASN.1 encoding). When the struct is marshalled to JSON by dddparser, every block that was successfully verified will carry "verified": true; all others carry "verified": false.The verified field in practice
verified is set independently on each data block. A file can have some blocks with "verified": true and others with "verified": false — for example, if it contains both 1st-gen and 2nd-gen sections and only one generation’s keys are loaded.
Behavior without keys
When no certificate files are present ininternal/pkg/certificates/pks1/ or pks2/, the embedded certificate directory is empty and PKsFirstGen and PKsSecondGen contain no entries. In this case:
dddparserlogs:loaded certificates: 0 0- Parsing completes normally for both card and VU data.
- All data blocks are decoded and appear in the JSON output.
- Every
verifiedfield isfalsebecause the certificate chain cannot be validated without the root key.