Skip to content

Bundle SK CA certificates, fix transport error handling, tighten ADVANCED certificate validation - #1

Merged
maricavor merged 5 commits into
mainfrom
signature-trust-store-and-error-handling
Sep 15, 2026
Merged

maricavor merged 5 commits into
mainfrom
signature-trust-store-and-error-handling

Conversation

@maricavor

Copy link
Copy Markdown
Owner

▎ Bundled CA certificates. Signer certificates chain to SK's own eID CAs, which are not in any OS CA bundle — that store holds TLS roots — so chain validation failed for every real Smart-ID certificate in both environments. The authentication path never exposed this because AuthenticationCertificateValidator does no chain building at all. Ships the same certificates smart-id-java-client does, production and demo kept strictly separate, with TrustedCaCertStore.production / .demo / .default and from_directory / from_pkcs12 / from_certificates for your own. The two production roots aren't in the Java client; they came from the caIssuers AIA URLs of the intermediates they sign, and a spec re-verifies every chain.
▎ Transport errors no longer crash the client. Faraday::TimeoutError is a Faraday::ServerError with a nil response, so a long poll that timed out raised NoMethodError: undefined method '[]' for nil. Now Errors::NetworkTimeoutError. Dropped connections and TLS failures weren't caught at all and leaked raw Faraday exceptions; now Errors::ResponseError.
▎ ADVANCED certificates are checked. Purpose validation returned early for ADVANCED, accepting any Non-Repudiation certificate from any CA in the trust store. Now requires the non-qualified Smart-ID policy OIDs, matching the Java client.
▎ SignatureValueValidator got its first specs, which found a bug: a blanket rescue StandardError swallowed its own RequestSetupErrors and reported them all as "Signature value validation failed".

maricavor and others added 5 commits September 14, 2026 16:08
Signer certificates chain to SK's own eID CAs, which are not in any
operating system CA bundle -- that store holds TLS roots. Chain validation
therefore failed for every real Smart-ID certificate, in both environments.
The authentication path never exposed this because
AuthenticationCertificateValidator does no chain building at all.

Ships the same certificates smart-id-java-client does, for production and
demo, kept strictly separate. TrustedCaCertStore gains production/demo/
default entry points plus from_directory, from_pkcs12 and from_certificates
for supplying your own, and CertificateValidator now defaults to the bundled
store for the configured environment instead of the system CA store alone.

The two production roots are not in smart-id-java-client; they were fetched
from the caIssuers AIA URLs of the intermediates that they sign. A spec
verifies every bundled intermediate still chains to a bundled anchor.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GP9ZnyKyjjFfBmv3evRGV3
handle_server_error and handle_client_error read error.response[:status]
unconditionally, but Faraday::TimeoutError is a Faraday::ServerError with a
nil response, so a session status long poll that timed out raised
"NoMethodError: undefined method '[]' for nil" instead of something a caller
could rescue. Seen against the live demo service.

Timeouts now raise Errors::NetworkTimeoutError, distinct from
SessionTimeoutError which means the user did not respond. Faraday errors that
are neither ClientError nor ServerError -- a dropped connection, a TLS
failure -- were not caught at all and leaked to callers; they now raise
Errors::ResponseError. The duplicated rescue chains in get and post are
shared, since TimeoutError has to be matched before ServerError.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GP9ZnyKyjjFfBmv3evRGV3
Certificate purpose validation returned early for ADVANCED, so any
certificate with Non-Repudiation key usage from any CA in the trust store was
accepted as a signer certificate. Requires the non-qualified Smart-ID policy
OIDs (1.3.6.1.4.1.10015.17.1, 0.4.0.2042.1.1), matching
NonQualifiedSmartIdCertificateValidator in smart-id-java-client.

The existing happy-path spec used an ADVANCED certificate with no policy OIDs
at all, so it was asserting the lax behaviour; it now uses a certificate that
is a real Smart-ID one.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GP9ZnyKyjjFfBmv3evRGV3
…own errors

The only piece of real cryptography in the gem had no specs. Covers a valid
RSASSA-PSS signature, signatures over different data or from a different key,
a PKCS#1 v1.5 signature presented as PSS, and each PSS parameter mismatched
individually.

Writing them surfaced a bug: a blanket "rescue StandardError" swallowed the
RequestSetupErrors raised by validate_inputs and reported all four of them as
"Signature value validation failed", so the documented parameter errors were
unreachable. Gem errors are now re-raised as they are.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GP9ZnyKyjjFfBmv3evRGV3
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GP9ZnyKyjjFfBmv3evRGV3
@maricavor
maricavor merged commit 72bd818 into main Sep 15, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant