Skip to content

Releases: offsetsecurity/cipherhorizon

CipherHorizon v0.4.0

CipherHorizon v0.4.0 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 22 Oct 08:42
6bc2a02

Security

  • Built with Go 1.27.3 (GO-2026-6809). crypto/x509 now distrusts compromised intermediate certificates, and the
    trust check uses it.

Added

  • Classical strength for every detected algorithm (PRD-010,
    ENH-001). Strengths are computed with NIST's own methods, and the tests
    reproduce NIST's published tables:

    • RSA and finite-field DH by the General Number Field Sieve estimate (FIPS 140-3 IG D.B, SP 800-56B Rev. 2
      Appendix D);
    • elliptic curves at half the order (SP 800-186);
    • ciphers and hashes by SP 800-57 Part 1 Tables 2 and 3;
    • post-quantum algorithms by their NIST category.

    Where it shows:

    • TLS report and PDF: each cipher suite's word now comes from the server and is as strong as its weakest
      part (cipher, key exchange, certificate key, MAC), with its bits and that part named. Groups and certificate
      keys show their bits.
    • API: the evaluation (eval/9) carries suiteStrength and keyStrength; stored scans are re-evaluated
      on start.
    • CLI: tls scan --details prints each suite's and group's strength.
    • Inventory: a Classical strength column, and classicalStrength on every algorithm and key row of
      /api/v1/inventory.
    • SSH reports (page and PDF): a strength for every key exchange, host key (by the presented key's size),
      cipher and MAC.
    • CBOM: classicalSecurityLevel on every algorithm with a strength (1.6 and 1.7).
  • Every algorithm by class: asymmetric, symmetric, hash (PRD-011, #20).

    • A TLS report's Configuration ends with Every algorithm, by class: one lane per class, each algorithm with its
      role, a bits bar, its post-quantum status and the suites or certificates that use it, the weakest ringed.
    • Every suite row shows its parts (cipher, key exchange, signature, MAC, key-derivation hash) as cells with their
      bits, the weakest ringed.
    • The Inventory filters algorithms by class (?class=, a segmented control), and its Kind says the class.
    • The report's CBOM tab has a Classical strength column.
    • The PDF and tls scan --details list the algorithms by class, and the CLI lists each suite's parts.
    • API: the evaluation (eval/10) carries algorithms, and every suite part carries its class.
  • Open any certificate and read the whole X.509 document (PRD-012, #21).

    • Every certificate in the chain and the CBOM tab opens its page.
    • The page shows the chain it was served in, its validity, the key and the issuer's signature with their strengths
      and post-quantum status, its names and uses, every extension with its criticality and decoded value, the
      identifiers to copy, and the PEM (folded) to copy or download.
    • It is decoded from the DER the scan kept (GET /api/v1/certificates/{sha256}/x509), so earlier scans have it
      too.
  • Instruments, not paragraphs: the report, the certificate page and the Inventory are rebuilt
    (PRD-013, UX-DESIGN-011).
    One set of shared instruments draws every quantity the same way on every page: a 0 to 256 bits bar over the
    strength bands, a strength chip, a part cell, a post-quantum glyph, a status tile, a validity bar, the hygiene
    gauge and the readiness stepper. Each says in words what it shows.

    • Summary: four figures (readiness, hygiene, the weakest classical strength, the certificate), Fix first as
      the three things to fix first, each with one chip saying when, the rest folded under a line that counts them, one bar of every check's result, and a tile per area that opens it.
    • Configuration is one place to check the whole setup, in a reviewer's order: protocols and cipher suites
      (what a current client gets, then each version with its groups and its suites in the server's order), the
      certificate chain (each card opens its certificate; a missing issuer is drawn), key exchange and signatures,
      revocation, protocol features, and every algorithm by class. Every TLS version starts folded to one line that
      says whether it should be on and how its suites rate. A row of chips at the top jumps to each part, and
      each part ends with the issues found in it. HSTS shows the header as sent.
    • Inventory: algorithms By class and By classical strength (?strength=: broken, below-112, 112, 128,
      192, 256, not-rated), each bar opening its list; the list is weakest first, with a bits bar.
    • SSH reports (PRD-014,
      UX-DESIGN-012) get the same one-stop Configuration: version and
      software (what a current client gets, as a banner), key exchange (readiness, then each method as a row with its
      bits bar, post-quantum glyph and baseline mark), host keys (a card per key), ciphers, MACs and compression, and
      the baseline (a tile per requirement linking to its source, the changes as chips in their parts, the
      sshd_config lines). The Summary has the same four-figure strip, and the baseline is line 1 of Fix first.
      Checked by CRYPTO-REVIEW-002.
    • Scanner: a TLS scan records whether the server negotiates the heartbeat extension (RFC 6520).
    • Every number and claim on these screens was checked against its source
      (CRYPTO-REVIEW-001).

Changed

  • Breaking (API): the evaluation is now eval/12, and stored scans are re-evaluated on start. The band field
    uses the new hygiene band names (below), and a section's status can be unknown (revocation, when nothing
    vouches for the certificate). Integrations that match on band names or on the set of statuses must be updated.

  • The TLS hygiene bands have new names: Excellent, Good, Fair, Poor, Failing (they were Strong, Sound, Fair,
    Weak, Broken). Strong and Weak now mean only a suite's strength (UX-REVIEW-010, #22). The scores and the
    thresholds are unchanged. Stored scans are migrated, and the API's band field uses the new names.

  • Each suite gains the hash that derives its keys as a part, rated in that use (SP 800-57 Table 3). With NIST's
    numbers it changes no word.

  • Signature algorithms are rated as their hash and their key together, so SHA256withRSA from an imported CBOM
    now reads 128 bits instead of not rated.

  • The report's Summary:

    • the line under N things to fix names the first one;
    • the Cipher suites row names the weakest suite and its bits;
    • the Overview's TLS endpoints tile keeps to its count and the exposed figure.
  • The Inventory explains every not rated strength on the row.

  • Suite strength words follow the weakest-part rule, so some suites read differently:

    • a suite authenticated by an RSA-2048 certificate (112 bits) is now Acceptable, not Strong;
    • a suite with a 1024-bit DH prime is Weak.

    The words are defined in docs/methodology.md and explained in Help.

Fixed

  • Reports on real servers say the right thing (PRD-015,
    CRYPTO-REVIEW-003; eval/12, stored scans are re-evaluated on start):
    • a server whose only suites Go's TLS client will not offer (3DES, RC4, NULL, small DH) had its no-SNI fallback
      certificate reported as its own; the served chain is now read from the scanner's raw TLS 1.2 handshakes, which
      also finds an RSA certificate a server keeps for RSA-only clients. Scan such servers again;
    • a leaf signed straight by an unknown root is no longer called a missing intermediate;
    • a chain under Superfish, eDellRoot or DSDTestProvider is The certificate chains to a root whose private key is
      public
      (critical);
    • revocation reads Status unknown, not Good, unless a stapled OCSP response vouches for the certificate;
    • an untrusted root is described as a private CA or a public root since removed, not only as a private one;
    • a composite DH modulus is critical, and a DH prime that is not a safe prime is low;
    • at equal bits, the weakest classical strength picks a disallowed algorithm (3DES) over an allowed one.
  • SSH evaluation (eval/11; stored scans are re-evaluated on start):
    • a truncated MAC is as strong as its tag: hmac-sha1-96 is 96 bits, not 128 (NIST SP 800-107 Rev. 1);
    • the next steps name OpenSSH releases only to a server that identifies as OpenSSH;
    • a baseline rule with nothing to judge (the RSA size rule on a server with no RSA key) says it does not apply
      instead of Met.
  • Single-DES suites (56-bit key) are Insecure, not Weak (#18).
  • The TLS hygiene figure on a report's Summary opened About this report. It now opens how the score was worked
    out, on Weaknesses.
  • A running scan's connection count could go backwards. Concurrent probes reported counts out of order, and
    progress writes could land out of order. The count now only moves forward, and writes are serialised. This was
    found by a CI run of the workflow test.

Verify what you downloaded

sha256sum --check --ignore-missing checksums.txt
cosign verify-blob checksums.txt --bundle checksums.txt.sigstore.json \
  --certificate-identity-regexp '^https://github.com/offsetsecurity/cipherhorizon/\.github/workflows/release\.yml@refs/tags/v' \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com

The inventory scripts a server of this version hands out are attached too, with
inventory-scripts.sha256; cipherhorizon scripts list prints the same hashes offline.

Full instructions, including the container image and build provenance: docs/engineering/DISTRIBUTION.md.

CipherHorizon v0.3.0

CipherHorizon v0.3.0 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 18 Oct 09:34
7da548a

Changed

  • The remote TLS CBOM says how it knows each fact, and keeps every identifier it read
    (AUDIT-TLS-CBOM-001). All of it is additive: new components,
    cipherhorizon:* properties, and CycloneDX 1.7 fields the schema defines. See docs/cbom-model.md.
    • Evidence states. Every component has cipherhorizon:evidence:

      • observed: a ServerHello, or a certificate and what it states;
      • accepted: a group or signature scheme a single-item probe was answered for;
      • derived: what an accepted suite's IANA entry names;
      • negotiated: used by the one handshake a current client completed.

      A negotiated component also says which version (negotiated-in).

    • Coverage. The document's metadata states, per scan stage, whether it was done, not tested, not applicable,
      not reached, cut by the budget or inconclusive, and whether the scan was complete. An absent component can now
      be told apart from an untested one.

    • No identifier is dropped.

      • A certificate signature or key algorithm the catalogue does not know is kept by the OID read from the
        certificate (identifier-status = unrecognised), with no family guessed.
      • Every accepted group is listed with its IANA code point, including brainpool, binary and GOST curves.
      • The TLS 1.3 signature schemes are also listed with their code points.
    • RSASSA-PSS parameters are decoded per certificate: hash, mask generation, salt length and trailer field, with
      RFC 8017 defaults marked.

    • Suites reference their MAC or hash: HMAC-SHA-1, HMAC-SHA-256, the new HMAC-SHA-384 (1.2.840.113549.2.10),
      HMAC-MD5, or the SHA-256/SHA-384 of an AEAD suite's PRF or HKDF.

    • Certificates carry every extension.

      • The 1.7 common names: subjectAlternativeName, authorityInformationAccess, crlDistributionPoints,
        authorityKeyIdentifier, subjectKeyIdentifier, signedCertificateTimestamp.
      • Every other extension as a 1.7 custom extension named by its OID: name constraints and the TLS Feature
        decoded, unknown ones marked not decoded, critical flagged.
    • The served chain is linked: issued-by, only to a certificate the server sent. Each certificate says which
      handshakes returned it (seen-in, no-sni).

    • Public keys carry their SPKI SHA-256 (the 1.7 fingerprint) and RSA exponent.

  • tls-report/2 gains optional certificate fields: signatureParams, authorityKeyId, subjectKeyId and
    extensions. Older reports read as before.

Verify what you downloaded

sha256sum --check --ignore-missing checksums.txt
cosign verify-blob checksums.txt --bundle checksums.txt.sigstore.json \
  --certificate-identity-regexp '^https://github.com/offsetsecurity/cipherhorizon/\.github/workflows/release\.yml@refs/tags/v' \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com

The inventory scripts a server of this version hands out are attached too, with
inventory-scripts.sha256; cipherhorizon scripts list prints the same hashes offline.

Full instructions, including the container image and build provenance: docs/engineering/DISTRIBUTION.md.

CipherHorizon v0.2.0

CipherHorizon v0.2.0 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 17 Oct 21:51
4b19bc9

Added

  • Charts you can act on. Every chart mark that counts something opens it, already filtered
    (PRD-009, UX-DESIGN-008):
    • Overview:
      • a readiness level opens the TLS endpoints or SSH servers at that level (both, as two links, when both
        protocols are there);
      • a severity chip opens the findings of that severity;
      • "N exposed to harvest-now-decrypt-later" opens that protocol's exposed endpoints.
    • Inventory:
      • each ring segment and legend row opens the algorithms with that post-quantum status;
      • each By purpose segment opens that purpose and status;
      • a Replace first, protocol or software row opens exactly that item;
      • each certificate-expiry segment opens the certificates in that window.
    • A report's history: each point opens the scan it stands for.
    • The same behaviour for every mark: a tooltip on hover and keyboard focus, the other marks dimmed, Tab and
      Enter, a name that says what opens.
  • Filters in the address. The TLS and SSH lists (level, exposed), Findings (severity, status, kind),
    Certificates (expires, key, sig, origin) and the inventory lists (q, name, quantum, purpose,
    source) read their filters from the URL and write changes back. A filtered list can be bookmarked, and Back
    returns to the chart. A filter the page has no control for shows as a removable chip.
    • GET /api/v1/inventory gains quantum (vulnerable, safe, unknown), purpose and name (exact). vulnerable=1
      still works.
  • Quiet motion. Everything is listed in DESIGN.md §9, and all of it is off under reduced motion:
    • rings sweep to their share;
    • inventory bars grow;
    • history points arrive after their line;
    • figures count to their value once;
    • a list cross-fades when its filter changes;
    • clickable cells show an accent edge;
    • a pressed button settles 1px.
  • serve --port, and CIPHERHORIZON_PORT / CIPHERHORIZON_ADDR.
    • The port can be changed without retyping the host, and --addr may be a host alone (--addr 0.0.0.0 --port 9000).
      --port 0 takes any free port and prints it.
    • A port already in use now says so and suggests the next one, instead of a raw socket error.
    • healthcheck follows the same variables, so the container image, compose file and Quadlet unit change port
      with one variable. See Choosing the port.

Changed

  • The CBOM lists what it used to leave out (CBOM-REVIEW-002).
    Consumers keyed on component counts will see more components:
    • Legacy bulk ciphers: a suite's 3DES, RC4 (export RC4-40 included), DES and RC2 cipher is now an algorithm
      asset, with its cited classical status. Before, a server accepting only such suites had none of them in its CBOM.
    • Named-curve key exchange: accepted groups secp256r1, secp384r1, secp521r1 and x448 are now
      ECDH-P256, ECDH-P384, ECDH-P521 (id-ecDH with the curve) and X448 (1.3.101.111), judged
      quantum-vulnerable. Before, they were missing.
    • Certificate signatures: a certificate's signature algorithm is now exact, with its OID. For example
      ecdsa-with-SHA256 (1.2.840.10045.4.3.2), sha256WithRSAEncryption (1.2.840.113549.1.1.11) or RSASSA-PSS
      (1.2.840.113549.1.1.10), instead of the family ECDSA (no OID) or RSA (the key's OID).
    • ChaCha20-Poly1305 carries its OID, 1.2.840.113549.1.9.16.3.18 (RFC 8103).
    • CycloneDX 1.7 certificates fill serialNumber and fingerprint (SHA-256). 1.6 keeps the
      cipherhorizon:cert-serial property.
    • A static-RSA suite lists RSA once, not twice.
  • The container image sets CIPHERHORIZON_PORT=8080 and runs serve --addr 0.0.0.0. Its health check no longer
    names a port. The systemd unit passes --addr 127.0.0.1, so CIPHERHORIZON_OPTS="--port 9000" works.

Fixed

  • Two keys with one name were one inventory row with a random status. Keys are named by their certificate's
    subject, so an ECDSA and an ML-DSA certificate for the same host merged into one row. That row was
    quantum-vulnerable or quantum-safe depending on the order the merge read them, so the Inventory's counts could
    change between two loads of the same data.
    • A key's algorithm is now part of its identity: such keys are two rows, each with its own status, and the list
      names the algorithm.
    • Any disagreement between sources resolves to the worse status, whatever the order.

Verify what you downloaded

sha256sum --check --ignore-missing checksums.txt
cosign verify-blob checksums.txt --bundle checksums.txt.sigstore.json \
  --certificate-identity-regexp '^https://github.com/offsetsecurity/cipherhorizon/\.github/workflows/release\.yml@refs/tags/v' \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com

The inventory scripts a server of this version hands out are attached too, with
inventory-scripts.sha256; cipherhorizon scripts list prints the same hashes offline.

Full instructions, including the container image and build provenance: docs/engineering/DISTRIBUTION.md.

CipherHorizon v0.1.1

CipherHorizon v0.1.1 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 14 Oct 11:14
f0b62c1

Fixed

  • Release notes. A GitHub release now carries its CHANGELOG section. The release configuration disabled
    GoReleaser's changelog, which also dropped the notes file, so v0.1.0 was published with only the verification
    footer (its notes were then set by hand).
  • The container smoke test passes against a published image on Podman. It declares the health check in exec
    form (the image has no shell), says to pass CERT= and KEY= when openssl is missing, and no longer stops
    at random under pipefail.

Changed

  • Fewer entries per image in the container registry (5 instead of 9): no BuildKit SBOM and provenance
    attestations, and GitHub's provenance attestation is no longer copied into GHCR. Provenance is still verified
    with gh attestation verify; SBOMs are release assets, as before.
  • The release verifies the image signature before it publishes anything, with the command users are given.
  • No website for now: the GitHub Pages workflow is removed; site/ is kept.
  • Dependencies: modernc.org/sqlite 1.60.1; GitHub Actions setup-node 7.1.0, upload-artifact 7.0.2,
    CodeQL 4.38.3, sbom-action 0.24.3.

Verify what you downloaded

sha256sum --check --ignore-missing checksums.txt
cosign verify-blob checksums.txt --bundle checksums.txt.sigstore.json \
  --certificate-identity-regexp '^https://github.com/offsetsecurity/cipherhorizon/\.github/workflows/release\.yml@refs/tags/v' \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com

The inventory scripts a server of this version hands out are attached too, with
inventory-scripts.sha256; cipherhorizon scripts list prints the same hashes offline.

Full instructions, including the container image and build provenance: docs/engineering/DISTRIBUTION.md.

CipherHorizon v0.1.0

CipherHorizon v0.1.0 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 13 Oct 10:53
83928e9

Post-quantum readiness for the protocols your organisation runs on: TLS, SSH and, next, VPN. A deep scan of
each, a readiness level from L0 to L4, and one CycloneDX CBOM, self-hosted. The first public release, a 0.x
pre-release.

Highlights

  • TLS endpoints. CipherHorizon has its own TLS implementation for scanning. It checks:

    • every protocol from SSLv2 to TLS 1.3;
    • every cipher suite and key-exchange group (hybrids such as X25519MLKEM768 included);
    • the TLS 1.3 signature schemes (ML-DSA included);
    • the certificate chain against the Mozilla and Chrome root stores, or your own CA;
    • OCSP stapling, Certificate Transparency and HSTS.

    STARTTLS works for SMTP, IMAP, POP3, FTP, XMPP, LDAP and PostgreSQL. There are 55 cited checks and a TLS hygiene
    score with its arithmetic shown.

  • SSH servers.

    • Reads what a server offers and negotiates as OpenSSH 10.6 would, the ML-KEM hybrid included.
    • Fetches and verifies every host key: Ed25519, ECDSA, RSA, ML-DSA and the composite ssh-mldsa44-ed25519.
    • Judges the server against the cited CipherHorizon SSH baseline 2026, and writes the sshd_config lines
      that would make it meet the baseline.
    • Never logs in.
    • Checked against real OpenSSH 9.2 and 10.0.
  • Post-quantum readiness. L0 to L4 for every TLS endpoint and SSH server, each protocol by its own stated rules,
    with harvest-now-decrypt-later exposure.

  • Machines and inventory.

    • Two readable inventory scripts (PowerShell, shell) list a machine's cryptographic software and certificates.
    • CBOMs from other tools can be imported.
    • Everything merges into one CycloneDX 1.7 (or 1.6) CBOM.
  • The web app.

    • A page per protocol, Findings across everything with triage, and Systems ranked for migration.
    • PDF reports, schedules and history.
    • Sign-in when you want it: administrator, analyst and viewer roles, and an exportable activity log.
  • Command line and CI. tls scan and ssh scan, with --fail-on gates (exit 3), JSON and CBOM output, and a
    REST API described at /api/v1/openapi.json.

Install

Platform How
Container ghcr.io/offsetsecurity/cipherhorizon:0.1.0 (amd64, arm64), with docker-compose.yml or the Podman Quadlet unit in deploy/
Debian, Ubuntu sudo apt install ./cipherhorizon_0.1.0_amd64.deb
RHEL, Fedora sudo dnf install ./cipherhorizon-0.1.0-1.x86_64.rpm
Windows Unzip cipherhorizon_0.1.0_windows_amd64.zip, then run cipherhorizon.exe service install as administrator
Any OS Run cipherhorizon serve from the archive

Then open http://127.0.0.1:8080.

More: INSTALL.md.

Known limitations

  • Machines. The host collector has run on Windows (standard user) and in fixture tests for Linux, but not on a
    live Linux host or with administrator rights. macOS machines are not inventoried.
  • TLS. Validated against test servers and, for trust, against OpenSSL. A matrix of real TLS stacks (nginx,
    Apache, Postfix) is not yet recorded. There are no active vulnerability probes, by design.
  • SSH. Servers that offer only diffie-hellman-group18-sha512, group exchange or sntrup761 are judged on their
    algorithm lists, and their host keys are not fetched. BSI TR-02102-4 is planned as a second baseline.
  • Storage. SQLite only.

Everything in this release, in detail: CHANGELOG.md.

Verify what you downloaded

sha256sum --check --ignore-missing checksums.txt
cosign verify-blob checksums.txt --bundle checksums.txt.sigstore.json \
  --certificate-identity-regexp '^https://github.com/offsetsecurity/cipherhorizon/\.github/workflows/release\.yml@refs/tags/v' \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com
cosign verify ghcr.io/offsetsecurity/cipherhorizon:0.1.0 \
  --certificate-identity-regexp '^https://github.com/offsetsecurity/cipherhorizon/\.github/workflows/release\.yml@refs/tags/v' \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com

The inventory scripts a server of this version hands out are attached too, with inventory-scripts.sha256.
cipherhorizon scripts list prints the same hashes offline. Build provenance: gh attestation verify <file> --repo offsetsecurity/cipherhorizon. Full instructions:
DISTRIBUTION.md.