Skip to main content

Cryptographic Verification

This page explains exactly how the Tamper-Evident Audit Log signs, timestamps, and hash-chains audit entries — and how to verify them independently.

This is the differentiating section of Attestsys documentation. No other Atlassian Marketplace app ships all four properties described here.


Overview of the pipeline

When a Jira event is captured, the following pipeline runs:

Jira event
→ Canonical JSON serialisation (RFC 8785 JCS)
→ RFC 3161 timestamp request (tier-routed TSA)
→ Chain head lock (per-tenant, serialised)
→ Signed payload assembly
→ ECDSA secp256k1 signature
→ Persist to hash-chained audit table

Each step is deterministic and auditable. The signed payload is what gets hashed into the next entry's prev_hash field, creating an unbroken chain.


Canonical JSON (RFC 8785 JCS)

Before signing, the event payload is serialised to Canonical JSON per RFC 8785 JSON Canonicalization Scheme (JCS). This ensures that the same logical payload always produces the same byte sequence, regardless of key ordering or whitespace — making signatures deterministic and reproducible.

The implementation uses io.github.erdtman:java-json-canonicalization:1.1 (pinned). Changing this library version would be chain-breaking — it would invalidate verification of all prior entries.


ECDSA secp256k1 signing

Each audit entry is signed using ECDSA on the secp256k1 elliptic curve — the same curve used in Bitcoin and Ethereum key infrastructure.

Key properties:

  • RFC 6979 deterministic signing — given the same key and message, the signature is always the same. This eliminates the risk of nonce reuse that affects standard randomised ECDSA
  • DER-encoded signatures — stored as standard ASN.1 DER for interoperability with third-party verification tools
  • Per-tenant key pairs — each Jira workspace (tenant) has its own ECDSA key pair. Keys are generated on tenant provisioning and stored with AES-256-GCM envelope encryption at rest
  • GDPR key erasure — if a tenant requests erasure, the private key is tombstoned (set to NULL). The chain entries remain but can no longer be signed with new entries under that key. A tombstone audit entry is appended to preserve chain continuity before key erasure

The public key for each tenant is included in every export bundle in PEM format, enabling independent verification without contacting Attestsys.


RFC 3161 trusted timestamping

Each audit entry receives a RFC 3161 timestamp token from an independent Timestamp Authority (TSA). The TSA is routed by billing tier:

TierTSAStatus
Free / DevelopmentFreeTSANon-qualified. Not listed on any EU national Trusted List. Does not carry eIDAS qualified presumption. Clearly labelled NON_QUALIFIED in every entry and export.
Standard (optional upgrade), Advanced (mandatory), EnterpriseQuoVadis Trustlink B.V. NL (primary) + GlobalSign nv-sa (secondary)Qualified. Listed on EU national Trusted Lists. Carries qualified presumption under eIDAS Art. 41.

The TSA response (a DER-encoded TimeStampToken) is stored alongside the signed entry and included verbatim in every export bundle. Verification tools can check the TSA certificate chain independently using the tsa-certs/ folder in the bundle.

Pending TSA marker

If the TSA is temporarily unreachable, the entry is stored with a PENDING_TSA_MARKER sentinel in place of the timestamp token. A background retry job resolves pending entries asynchronously. Pending entries show as ⚠️ Pending in the issue panel until resolved. The chain is not broken by pending entries — the sentinel value is a fixed 32-byte SHA-256 hash of the ASCII string "ATTESTSYS_PENDING_TSA_V1", which is deterministic and documented.


Hash chain

Each audit entry contains a prev_hash field — the SHA-256 hash of the previous entry's signed payload (the canonical JSON bytes that were ECDSA-signed). The genesis entry (the first entry in a tenant's chain) uses a fixed 32-byte all-zeros genesis value.

This creates a linked chain: modifying any past entry changes its signed payload, which changes its SHA-256 hash, which breaks the prev_hash of the next entry, propagating a detectable break through every subsequent entry to the chain head.

Chain verification:

  1. Reads all entries in sequence order
  2. Recomputes the SHA-256 hash of each entry's signed payload
  3. Compares it to the prev_hash stored in the next entry
  4. Verifies the ECDSA signature of each entry's signed payload against the tenant's public key
  5. Accumulates all failures — does not short-circuit at the first broken link

A chain that is fully intact with no failures is reported as INTACT. Any failure is reported with the entry ID, position in the chain, and the type of failure (hash mismatch, signature invalid, or both).


Offline verification with verify.html

Every export bundle includes a verify.html file — a self-contained verifier that runs entirely in the browser.

To verify:

  1. Unzip the bundle to a local folder
  2. Open verify.html in any modern browser
  3. No network requests are made — the verifier reads only the local bundle files
  4. The verifier performs the same chain and signature checks as the backend verifier

What the verifier checks:

  • ECDSA secp256k1 signature validity for each entry (using the Web Crypto API with the included public key)
  • SHA-256 hash chain linkage for each entry
  • Presence and structure of the RFC 3161 timestamp token for each entry

What the verifier does not check offline:

  • TSA certificate revocation (requires network access to OCSP/CRL endpoints)
  • TSA Trusted List status (requires network access to EU Trusted List)

For a full legal authentication context, TSA revocation and Trusted List status should be checked by a qualified professional at the time of verification.


CLI verification

⚠️ MANUAL REVIEW REQUIRED — The attestsys verify CLI tool is not yet shipped. This section describes the intended interface. Update with actual installation and usage instructions when the CLI is available.

A command-line verifier is planned:

# Verify a specific entry by ID
attestsys verify --entry-id <entry-id>

# Verify an entire exported bundle
attestsys verify --bundle path/to/bundle.zip

The CLI performs the same verification steps as verify.html and is suitable for scripted verification in CI/CD pipelines.


Security properties summary

PropertyValue
Signing algorithmECDSA secp256k1, RFC 6979 deterministic
Signature encodingDER (ASN.1)
Canonical JSONRFC 8785 JCS
Hash algorithm (chain)SHA-256
Timestamp protocolRFC 3161
Free tier TSAFreeTSA (non-qualified)
Paid tier TSA (primary)QuoVadis Trustlink B.V. NL (QTSP, EU Trusted List)
Paid tier TSA (secondary)GlobalSign nv-sa BE (QTSP, EU Trusted List)
Key storageAES-256-GCM envelope encryption at rest
InfrastructureHetzner Cloud, Nuremberg DE (EU only)