Skip to content

[FIPS 5 Backport] CLI and Semantic Versioning backport - #3647

Draft
justsmth wants to merge 37 commits into
aws:fips-2026-06-26-snapshotfrom
justsmth:fips-2026-cli-symver-backport
Draft

justsmth wants to merge 37 commits into
aws:fips-2026-06-26-snapshotfrom
justsmth:fips-2026-cli-symver-backport

Conversation

@justsmth

Copy link
Copy Markdown
Contributor

Title: [FIPS 5.x] Backport OpenSSL CLI updates, symbol versioning, and crypto-policies support to fips-2026-06-26-snapshot

Description of changes:

The FIPS 5 snapshot branched at v5.1.0, so it is missing mainline's CLI work since then, symbol versioning, and the crypto-policies reader distributions need to ship it as a drop-in OpenSSL replacement. This is the FIPS 5 counterpart of #3615 and #3618 in one PR. Most of those PRs' sources are already on this branch, so this covers the rest, plus #3423 (self-service symbol registration), which FIPS 4 approximated by hand. Every commit is a cherry-pick -x of the mainline merge. 28 of 34 are byte-identical to mainline, and the other six note their deviation in the commit message. Nothing under crypto/fipsmodule/ changes, and bcm.o is byte-identical to the base.

PR Change Deviation
#3331 Fix libgit2/xtrabackup/grpc tests; add OSSL3 peer cert APIs
#3096 Symbol versioning Keeps ABI_VERSION 0. Registers FIPS_module_name (from #3474)
#3338 Symbol versioning follow-ups
#3343 Pin libssh2 integration
#3341 SSL_OP_IGNORE_UNEXPECTED_EOF over sockets
#3373 Flaky s_client cipher tests
#3386 Document building against AWS-LC
#3347 Harden CLI input validation
#3423 Self-service symbol registration Drops the brainpool entries (#3286 is not on this branch)
#3407 Network s_client tests to integration
#3426 Decouple symbol versioning from dist-pkg
#3370 OpenSSL-compat .pc files with the shim
#3446 BN_FLG_CONSTTIME as zero Drops the krb5 patch removal (no krb5 integration here)
#3469 req -extensions / -reqexts
#3481 OpenSSL long names for ML-DSA OIDs
#3492 bssl client -tls1_2/-tls1_3
#3501 ERR_num_errors/ERR_pop_to_count
#3529 Error-queue suppression scope
#3502 Crypto-policies reader Leaves cpu_getauxval_linux.h (in bcm.o) untouched and defines AT_SECURE in crypto_policy.cc
#3527 Export the reader; CI job
#3536 CLI exit codes
#3503 Seed ciphers and version bounds
#3548 verify/x509 compatibility
#3532 pkcs12 import
#3499 Quote shell args in tool tests
#3551 SIGPIPE race in linkage checks
#3560 Reject malformed end-of-contents
#3563 rehash -compat
#3543 Harden code paths Drops the e_aesccm.c guard (in bcm.o)
#3568 ca fails on unwritable cert
#3504 Seed groups and sigalgs No SSL_CTX_set1_group_ids (#3346) here, so the deduped list is installed into supported_group_list directly. ML-DSA sigalg merge unchanged
#3575 pkcs12 -export, req -batch
#3592 CSR proof-of-possession on x509 -req
#3605 RSA enc/dec and more ciphers

Three branch-only commits port FIPS 4's equivalents from #3618: version nodes renamed to AWS_LC_FIPS5_<major>.<minor> so a FIPS process can't cross-bind with mainline's AWS_LC_*, docs updated to this branch's SONAME and node names, and the silent-drop check run against a FIPS=1 build.

Call-outs:

Two decisions change shipped artifacts and are worth confirming. ABI_VERSION stays 0 (SONAME libcrypto-awslc.so.0, where mainline's #3096 bumps it to 1), and the nodes are AWS_LC_FIPS5_1.0. Unlike FIPS 4 (awslcfips4), this branch still has SOFTWARE_NAME "awslc". If it gets renamed at the FIPS 5 release, the dist-pkg docs and test globs need the same treatment #3618 gave FIPS 4.

Testing:

On Ubuntu 24.04 x86-64:

  • FIPS=1, distribution config (ENABLE_DIST_PKG, ENABLE_SYMBOL_VERSIONING, ENABLE_CRYPTO_POLICIES), and default builds all pass crypto_test, ssl_test, tool_openssl_test, and bssl_tool_test.
  • run_dist_pkg_install_tests.sh passes, including the symbol-version suite and the new FIPS drop check.
  • CryptoPolicy* passes 75/75; its 3 system-policy cases ran against AL2023's DEFAULT back-end.
  • bcm.o is byte-identical to the base when built from the same path.
  • At every commit, each .map regenerates byte-identically from its registry.

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license and the ISC license.

WillChilds-Klein and others added 30 commits October 9, 2026 18:01
(cherry picked from commit db0dc86)

[fips-2026-06-26-snapshot backport] Kept ABI_VERSION at 0; mainline bumps it to
1, which would change this branch's SONAME from libcrypto-awslc.so.0. Registered
FIPS_module_name, which this branch exports (from aws#3474) but mainline's registry
at this commit predates.
### Issues:
Addresses `V2066643219`

### Description of changes:
Follow-up to aws#3294, which gated `SSL_OP_IGNORE_UNEXPECTED_EOF` on
[`BIO_eof(ssl->rbio.get())`](https://github.com/aws/aws-lc/blob/23df900e6df447bcebfd3e41b4851aad1ab2cc19/ssl/ssl_buffer.cc#L585-L591).

[`BIO_eof()`](https://github.com/aws/aws-lc/blob/23df900e6df447bcebfd3e41b4851aad1ab2cc19/crypto/bio/bio.c#L443)
calls [`BIO_ctrl(bio, BIO_CTRL_EOF,
…)`](https://github.com/aws/aws-lc/blob/23df900e6df447bcebfd3e41b4851aad1ab2cc19/crypto/bio/bio.c#L390),
and socket BIOs (`BIO_s_socket`, used by
[`SSL_set_fd`](https://github.com/python/cpython/blob/main/Modules/_ssl.c#L985)
in CPython) don't implement it, and
[`sock_ctrl`](https://github.com/aws/aws-lc/blob/23df900e6df447bcebfd3e41b4851aad1ab2cc19/crypto/bio/socket.c#L106)
falls through to `default: ret = 0`, so the guard was always false and
the condition was never met.

The testing didn't catch this because it used
[`BIO_pair`](https://github.com/aws/aws-lc/blob/23df900e6df447bcebfd3e41b4851aad1ab2cc19/crypto/bio/pair.c#L381),
which does implement `BIO_CTRL_EOF` and returns 1 at EOF so the guard
passed in the test. Will need to be backported to the FIPS 3.0 and 4.0
branches.

### Testing:
Added a test that reads through a dummy BIO that emulates a socket, as
it returns 0 on read, and checks the client and server both get
`SSL_ERROR_ZERO_RETURN` on an unexpected EOF. Confirmed the test fails
with the old guard and passes with the fix.

By submitting this pull request, I confirm that my contribution is made
under the terms of the Apache 2.0 license and the ISC license.

(cherry picked from commit 4cec1f7)
### Issues:
Resolves P469904855

### Description of changes:
Fixes input validation gaps and tightens file permissions in the CLI
tool:
- Always fail on DNS resolution errors in s_client regardless of quiet
mode
- Reject unrecognized -v2 cipher names in pkcs8 -topk8 instead of
falling through
- Set restrictive permissions (0600) when writing private key files

### Testing:
Tests added for each change.

By submitting this pull request, I confirm that my contribution is made
under the terms of the Apache 2.0 license and the ISC license.

(cherry picked from commit 216d28b)
…elf-service (aws#3423)

### Context/Motivation

The five `EC_group_brainpoolP*r1()` functions added in aws#3286 were never
registered.
* New `OPENSSL_EXPORT` symbols that are not added to the symbol registry
are compiled into shared libraries but hidden by the version script,
preventing applications from linking against them.

### Description of changes:

This PR registers the Brainpool EC group APIs in `AWS_LC_1.0` and makes
the registration path when updating the current node explicit with
`./util/update_symbol_version.sh --current`.

The symbol-versioning tool now distinguishes adding API to the current
open node from opening a new node, rejects accidental reuse of an
existing version node, and uses a deterministic byte-order sort.
Documentation and CI failure messages now explain the impact of
unregistered symbols and how to remediate them.

### Call-outs:

Opening a new symbol-version node is now documented and treated as a
release-level decision. Most API additions should use `--current`;
providing a version creates a new node and closes the current one by
convention.

### Testing:

- `git diff --check`
- Full build and CI tests not run locally.

By submitting this pull request, I confirm that my contribution is made
under the terms of the Apache 2.0 license and the ISC license.

(cherry picked from commit c528a49)

[fips-2026-06-26-snapshot backport] Did not register the EC_group_brainpoolP*r1
functions: brainpool support (aws#3286) is not on this branch. The rest of the
commit, which makes symbol registration self-service, applies unchanged.
### Description of changes:
Five of the eight `SClientTest` cases connect to `amazon.com:443`, so
they fail whenever egress is unavailable or rate limited. This moves
them into a new `tool-openssl/s_client_integration_test.cc`, built into
the existing `integration_test` executable alongside the OCSP responder
tests, and renames them to `SClientIntegrationTest` to match
`OCSPIntegrationTest`.

`integration_test` is deliberately absent from `util/all_tests.json`, so
the default test run (`go run util/all_tests.go`, `ninja run_tests`)
never invokes it. Environments without outbound access, such as internal
sandbox builds, get a hermetic default suite without having to opt out
of anything.

### Call-outs:
* The moved cases no longer run in the default suite. That is the goal,
but the trade-off is that nothing exercises `s_client` against a live
host unless `integration_test` is run explicitly.
* `UnresolvableHost` stays in `tool_openssl_test` even though it passes
`-connect`. It only needs DNS resolution to fail, not egress, so it is
hermetic. I extended its comment to say so, since otherwise it reads
like it was missed during the move.
* `ssl/CMakeLists.txt` gains a few extra sources (`s_client.cc`,
`ordered_args.cc`, `tool/args.cc`, `tool/client.cc`, `tool/file.cc`)
purely as the link closure for `SClientTool`. No new library
dependencies.

### Testing:
Existing tests, relocated rather than rewritten. `tool_openssl_test` and
`integration_test` both pass locally (macOS/arm64): the 3 remaining
`SClientTest` cases and all 5 moved `SClientIntegrationTest` cases. Test
count balances -- 8 before, 3 + 5 after. Also ran `ssl_test` to confirm
the `ssl/CMakeLists.txt` change did not disturb anything else.

By submitting this pull request, I confirm that my contribution is made
under the terms of the Apache 2.0 license and the ISC license.

(cherry picked from commit 85a931c)
### Description of changes:
Symbol versioning was previously enabled only through `ENABLE_DIST_PKG`,
which also relocates headers and renames binaries. This change adds an
independent `ENABLE_SYMBOL_VERSIONING` setting that defaults to
`ENABLE_DIST_PKG`, preserving existing behavior while allowing consumers
to adopt versioned ELF symbols without the other distribution packaging
changes. Explicit requests are rejected for unsupported static, Apple,
and Windows builds, while inherited settings remain disabled without
breaking existing static distribution package configurations.

This also adds `SYMBOL_VERSION_NAMESPACE` for private distributions that
need version nodes other than `AWS_LC_*`. Custom version scripts are
generated in the build tree without modifying the checked-in `.map`
files, and the standalone version script generator supports the same
namespace override.

Documentation is updated to describe the independent configuration,
platform restrictions, and ABI implications of custom namespaces.

### Call-outs:
- `ENABLE_SYMBOL_VERSIONING` intentionally does not use CMake's
`option()` because doing so would cache its initial default and prevent
an unset value from continuing to track `ENABLE_DIST_PKG` across
reconfiguration.
- A custom `SYMBOL_VERSION_NAMESPACE` changes the ABI contract and is
only appropriate for privately distributed libraries. It is rejected
when symbol versioning is disabled.
- Namespace rewriting exists in both CMake and the Go generator so
builds can continue using checked-in version scripts with
`DISABLE_GO=ON`. The tests verify that both implementations produce
identical output.

### Testing:
- Added Go unit coverage for valid and invalid namespaces, single-node
and multi-node rewrites, namespace collisions, inheritance preservation,
and end-to-end version script generation.
- Extended `run_symbol_version_test.sh` to verify that symbol versioning
can be enabled without distribution packaging, does not enable header or
binary cohabitation, writes namespaced scripts only into the build tree,
rejects namespaces when versioning is disabled, and keeps the CMake and
Go rewrite implementations equivalent.
- Existing integration coverage continues to verify version definitions,
versioned exports, linking, and detection of unversioned or silently
dropped symbols.

By submitting this pull request, I confirm that my contribution is made
under the terms of the Apache 2.0 license and the ISC license.

(cherry picked from commit af86c72)
…3370)

### Context and motivation

The OpenSSL shim installs compatible headers and library symlinks, but
its pkg-config interface is incomplete. It provides only `openssl.pc`,
so consumers that require `libcrypto` or `libssl` by package name cannot
resolve the shim. Its metadata can also expose suffixed library names
that CMake's `FindOpenSSL` treats as unresolvable extra dependencies.

### Description of changes

Install `openssl.pc`, `libcrypto.pc`, and `libssl.pc` for the shim, with
metadata describing the unsuffixed OpenSSL interface: the plain
`include` directory, unsuffixed package dependencies, and
`-lcrypto`/`-lssl`. These names resolve through the shim symlinks, while
the native AWS-LC pkg-config modules remain suffixed for cohabitation.

When `BUILD_LIBSSL=OFF`, omit the native and compatibility `libssl.pc`
files, the `libssl` shim symlink, and `libssl` package dependencies.
Update `INCORPORATING.md` to document both pkg-config interfaces.

### Testing

Extended the installation tests across shared, static, shim-disabled,
and `BUILD_LIBSSL=OFF` configurations. They validate the installed
modules and exact pkg-config metadata, compile and run consumers through
the shim, cover a `Requires.private: libcrypto` dependency, exercise
CMake's standard `find_package(OpenSSL)`, and confirm that the native
suffixed interface is unchanged.

### Review considerations

This does not change the public C API or ABI. The compatibility modules
emit unsuffixed linker names, but those names resolve to libraries
retaining their suffixed SONAMEs, so runtime cohabitation with a system
OpenSSL is preserved.

By submitting this pull request, I confirm that my contribution is made
under the terms of the Apache 2.0 license and the ISC license.

(cherry picked from commit 8049abf)
(cherry picked from commit 8670e12)

[fips-2026-06-26-snapshot backport] Dropped the krb5 integration patch removal;
this branch does not carry the krb5 integration test. The bn.h comment replaces
this branch's variant of the old text, so it now matches mainline.
…aws#3469)

### Context and motivation

`openssl req -extensions` was incorrectly applied to CSRs, which could
make CSR generation fail when the selected certificate extension section
required issuer context, such as `authorityKeyIdentifier`.

### Description of changes

Route `-extensions` to certificates generated with `-x509` and add
`-reqexts` for selecting CSR extensions. The options independently
override `x509_extensions` and `req_extensions`, matching OpenSSL 1.1.1
and 3.0 behavior.

### Testing

Added unit and OpenSSL comparison tests covering command-line overrides,
config fallbacks, invalid sections, issuer-dependent extensions, and the
end-to-end CSR signing flow.

### Review considerations

OpenSSL 3.2 and later alias `-reqexts` and `-extensions`; this change
intentionally follows the separate option semantics from OpenSSL 1.1.1
and 3.0.

By submitting this pull request, I confirm that my contribution is made
under the terms of the Apache 2.0 license and the ISC license.

(cherry picked from commit fac7cb9)
### Context and motivation

AWS-LC registers the three ML-DSA OIDs with both short and long name set
to the compact "MLDSA44"/"MLDSA65"/"MLDSA87" spelling. OpenSSL 3.5
registers the same OIDs with the long name
"ML-DSA-44"/"ML-DSA-65"/"ML-DSA-87". Anything that looks up ML-DSA by
name, or reads the name back off a parsed key or certificate, doesn't
interoperate between the two libraries. RubyGems, for example,
identifies ML-DSA keys by the OpenSSL 3.5 long name and fails to
recognize a valid ML-DSA key produced or parsed by AWS-LC.

### Description of changes

Set the long name of the three ML-DSA OIDs to the OpenSSL 3.5 spelling
("ML-DSA-xx") while keeping the short name as "MLDSAxx".
`OBJ_ln2nid`/`OBJ_txt2nid` now resolve the OpenSSL name and `OBJ_nid2ln`
returns it; `OBJ_sn2nid`/`OBJ_txt2nid` still resolve the existing short
name, so no current caller breaks. New `LN_MLDSAxx` macros are added to
`nid.h`.

A couple of things worth knowing:

- `!Cname` in `objects.txt` pins the generated C identifier. Without it,
`objects.go` derives the macro name from the long name and would have
produced `NID_ML_DSA_44` with a freshly allocated NID. With it,
`NID_MLDSAxx`, `OBJ_MLDSAxx`, and `obj_mac.num` are all unchanged.
- `OBJ_obj2txt` prefers the long name, so `i2a_ASN1_OBJECT` and
everything built on it (`X509_print`, `X509_signature_print`, etc.) will
now print `ML-DSA-44` instead of `MLDSA44` for ML-DSA keys and signature
algorithms. This is the intended outcome (matches `openssl x509 -text`
on 3.5), but it is a visible string change for anyone parsing that
output.

This only touches the object-name database. It does not add ML-DSA key
generation by algorithm name; AWS-LC still selects the parameter set by
NID through `EVP_PKEY_CTX`.

### Testing

Added `ObjTest.MLDSANames`, which for each parameter set checks
`OBJ_nid2sn`/`OBJ_nid2ln`, that both spellings resolve through
`OBJ_ln2nid`/`OBJ_sn2nid`/`OBJ_txt2nid`, and that `OBJ_obj2txt` returns
the long name (the path `X509_print` uses). `obj_dat.h` and `nid.h` were
regenerated with `go run objects.go`; re-running the generator on the
branch produces no diff.

### Review considerations

Public API: adds `LN_MLDSA44`/`LN_MLDSA65`/`LN_MLDSA87`. No existing
symbols or NID values change. ML-KEM has the same naming asymmetry with
OpenSSL 3.5 (`MLKEM768` vs `ML-KEM-768`) and is intentionally left for a
follow-up.

By submitting this pull request, I confirm that my contribution is made
under the terms of the Apache 2.0 license and the ISC license.

(cherry picked from commit 254c4cf)
Stack, split out of aws#3442, to merge bottom-up:

1. **aws#3501 -- error-queue primitives (this PR)**
2. aws#3502 -- policy file reader
3. aws#3503 -- cipher lists and version bounds
4. aws#3504 -- groups and signature algorithms
5. aws#3505 -- post-quantum defaults

## Description

- Adds `ERR_num_errors` and `ERR_pop_to_count`, so code that calls into
libcrypto on a caller's behalf can drop the errors it raised and leave
the queue it was handed untouched.
- The existing mark APIs cannot do this. `ERR_set_mark` needs an entry
to mark, so it is a no-op on an empty queue, and popping to a mark
consumes one the caller had already set.
- `ERR_clear_error` and `ERR_restore_state` rebuild the queue, which
dangles the data pointer the caller got from its last
`ERR_get_error_line_data`.
- A count is a position rather than a mark, so it nests inside a
caller's mark without disturbing it.

## Testing / verification

- New error-queue tests cover popping back to a recorded count, a count
taken from an empty queue, and a count at or above the queue's length.
- One case fills the ring past its capacity to confirm a stale count
leaves the caller's errors alone.
- One case wraps a nested call in the caller's own mark and an error
carrying a data string, then checks the mark still pops and the string
is intact.
- One case pins that a mark does not survive
`ERR_save_state`/`ERR_restore_state`, since a snapshot can be restored
many times and would re-arm a mark nobody set.

By submitting this pull request, I confirm that my contribution is made
under the terms of the Apache 2.0 license and the ISC license.

(cherry picked from commit 973b592)
Stack, split out of aws#3442, to merge bottom-up (aws#3501, the error-queue
primitives, has landed):

1. **aws#3502 -- policy file reader (this PR)**
2. aws#3527 -- shared-build symbol export and CI
3. aws#3503 -- cipher lists and version bounds
4. aws#3504 -- groups and signature algorithms
5. aws#3505 -- post-quantum defaults

- Adds an off-by-default build flag, `-DENABLE_CRYPTO_POLICIES`, and a
reader for the OpenSSL back-end file that Amazon Linux 2023 and Fedora
render from the operator's chosen system policy.
- `AWSLC_CRYPTO_POLICY_FILE` relocates that file at build time and is
declared in the CMake cache, so `cmake -L` and cmake-gui list it for a
packager who is not reading the CMakeLists.
- Parses the directives AWS-LC could act on into a fixed-size config
struct. Nothing consumes the result yet.
- Comments, section headers, and unknown keys are ignored, so the reader
tolerates the rest of what the framework writes today and whatever it
adds later. A value too long to represent is ignored the same way, even
where an earlier line set the same key.
- The reader succeeds only if it read the whole file, so half a policy
cannot pass for a shorter one.
- The policy path is fixed at build time and overridable at runtime,
which is how the tests here and in the rest of the stack drive it. The
override is dropped in a secure execution, where the environment sits on
the far side of a privilege boundary from the root-owned default path.

- Parse tests cover a full stock policy, quoting, surrounding
whitespace, a repeated key, and a final line with no trailing newline.
- An overlong value or line is dropped whole rather than truncated, and
drops the value an earlier line gave the same key, so a policy larger
than the reader's buffers cannot quietly become a different policy.
- A missing file, a read error, and null arguments all fail rather than
yielding a half-filled config. The read-error case opens a directory,
which fails on the first read rather than on the open.
- The runtime path override is exercised directly, since every later
test in the stack rests on it.
- Checked the override drop against a real binary: honored as an
ordinary process, ignored once that binary carries file capabilities,
which leave the real and effective ids equal.

By submitting this pull request, I confirm that my contribution is made
under the terms of the Apache 2.0 license and the ISC license.

(cherry picked from commit 6d8a1d6)

[fips-2026-06-26-snapshot backport] Dropped the change to
crypto/fipsmodule/cpucap/cpu_getauxval_linux.h so bcm.o is unchanged. This
branch's copy of that header already has the same <sys/auxv.h> detection and
/proc/self/auxv fallback; it only lacks the AT_SECURE define, which
ssl/crypto_policy.cc now supplies locally. Did not take the BUILD_AWSLC_PROVIDER
options that sit next to the new cache entries in CMakeLists.txt; the provider
is not on this branch.
Stack, split out of aws#3442, to merge bottom-up (aws#3501, the error-queue
primitives, has landed):

1. aws#3502 -- policy file reader
2. **aws#3527 -- shared-build symbol export and CI (this PR)**
3. aws#3503 -- cipher lists and version bounds
4. aws#3504 -- groups and signature algorithms
5. aws#3505 -- post-quantum defaults

## Description

- Exports the two policy-reader internals the tests call and registers
them in the libssl symbol registry. A shared build needs both: hidden
visibility keeps them out of the library, and the version script an
`ENABLE_DIST_PKG` build applies keeps them out again.
- Runs the symbol extractor once more with the crypto-policies build
flag defined, so declarations sitting behind that guard reach the
registry at all.
- Adds the Amazon Linux 2023 CI job for the feature: the suite with the
flag on in stock CMake and in the shared, symbol-versioned
`ENABLE_DIST_PKG` build a distribution packages, a run against the
policy file the system renders, and a build with libssl off.
- The job's seeding-specific parts, the neutralizing path override and
the require-system flag, are inert until aws#3503 adds seeding and the test
hook that reads them.
- The build flag stays off by default, so no shipped configuration
changes.

## Testing / verification

- Ran both build configurations the job runs and the policy tests in
each. Only the distribution one fails to link without this change, which
is why a stock build alone let the gap through.
- Reverting only the generated version script brings the undefined
references back, so the registry entries earn their place alongside the
export attribute.
- Regenerating the version script from the registry is byte-identical,
the invariant the symbol-check job's `mapcheck` mode enforces.

By submitting this pull request, I confirm that my contribution is made
under the terms of the Apache 2.0 license and the ISC license.

(cherry picked from commit 828175d)
### Context and motivation

Tool entry points in `tool-openssl/` return a `bool` that `main()`
collapses to exit status 0 or 1. OpenSSL's CLI uses other exit codes in
a few places that scripts rely on -- notably `verify` (exit 2 on
verification failure) and `x509 -checkend` (exit 1 when the certificate
is about to expire). Our tool always exited 0 for `-checkend` and 1 for
every `verify` failure, so scripts using it as a drop-in replacement got
the wrong answer.

### Description of changes

`tool_func_t` now returns `int`: the process exit status directly.
`kToolExitSuccess` / `kToolExitFailure` (0/1) are defined in
`internal.h`, and `main()` returns whatever the tool returns (still
dumping the error queue on any nonzero status). All tools except two
just map their existing `bool` result to 0/1, so their exit codes are
unchanged.

The two with real behavior changes, now matching OpenSSL 1.1.1 and 3.x:

- `verify` exits 2 when any input certificate fails to load or verify,
and 1 for option/setup errors (bad option, unreadable `-CAfile` or
`-untrusted`). As part of this, the `-untrusted` bundle is loaded once
up front instead of once per input certificate; `X509_STORE_CTX_init`
only borrows the stack, so sharing it is fine. This also fixes the
`-untrusted` fopen error message, which printed the cert path instead of
the chain path.
- `x509 -checkend` exits 1 when the certificate will expire within the
window and 0 otherwise. As before (and like OpenSSL), `-checkend` writes
only its verdict and suppresses the certificate output.

### Testing

Existing tests are updated to assert on the exact exit code rather than
`true`/`false`. New unit tests cover each `verify` exit path
(verification failure, one failing input among several, unparseable
input, missing `-CAfile`, missing `-untrusted`, unknown option) and both
`-checkend` outcomes including the `-out` contents. New comparison tests
(`VerifyComparisonTest.ExitCodes`,
`X509ComparisonTest.CheckendExitCode`) run the built binary against the
reference `openssl` from `OPENSSL_TOOL_PATH` and assert the exit codes
match; these pass locally against OpenSSL 1.1.1, 3.0, and 3.6.

By submitting this pull request, I confirm that my contribution is made
under the terms of the Apache 2.0 license and the ISC license.

(cherry picked from commit 5c7719d)
…ws#3503)

Stack, split out of aws#3442, to merge bottom-up. Below this PR, aws#3501 and
aws#3529 (error-queue primitives and the suppression scope), aws#3502 (policy
file reader), and aws#3527 (shared-build symbol export and CI) have landed.

1. **aws#3503 -- cipher lists and version bounds (this PR)**
2. aws#3504 -- groups and signature algorithms
3. aws#3505 -- post-quantum defaults

## Description

- `SSL_CTX_new` now applies the policy's cipher lists and protocol
bounds when built with the flag, taking the TLS or DTLS directives
according to the context's method.
- Seeding is best-effort and cannot report failure, so it runs inside
`bssl::ScopedErrorSuppression`, which rejects the errors it raises at
the source and is false when the queue cannot be protected, where
seeding is skipped. Trimming afterwards is not enough for a caller whose
queue is already full: there each error seeding raises evicts one of
theirs, and no later trim brings an evicted entry back.
- A cipher rule the setters refuse leaves the built-in list, which
neither setter gives on its own: they install an empty list before
reporting that a rule matched nothing, and allocate again to merge the
TLS 1.2 and 1.3 lists. Both lists are built aside and moved in only once
every step has succeeded.
- A protocol floor the policy names and AWS-LC cannot resolve rises to
the ceiling. Dropping it would leave the built-in floor of TLS 1.0,
below every floor `crypto-policies` renders, so a context would offer
the versions the policy exists to forbid. A floor naming a protocol
older than any AWS-LC implements keeps the built-in one, which is
already stricter, and a ceiling AWS-LC cannot resolve stays unapplied.
- The bounds are resolved as a pair before either is applied, since the
setters check each against the method's whole range and never against
each other, and both are put back if either is refused.
- The `@SECLEVEL=N` prefix of `CipherString` is parsed and dropped,
since AWS-LC has no security levels; the key-size and hash constraints a
level implies are therefore not enforced.
- A policy read whole is cached per process, keyed on the resolved path,
matching how OpenSSL reads `openssl.cnf`: a policy change takes effect
only in processes started afterward, while a changed override path still
misses.
- A read that fails is not cached, since no errno tells a transient
error from a file that is simply absent. Caching the negative let one
bad read decide the policy for every context the process went on to
create; the price is a failing `open()` per `SSL_CTX_new` on a host with
no policy.

## Testing / verification

- The crypto-policies job from aws#3527 covers this code: the whole suite
in debug and release with seeding compiled in but pointed at a path that
does not exist, so the thousands of existing tests still see AWS-LC's
built-in defaults.
- That job's system run drops the override and reads the file
`crypto-policies` itself renders, comparing the automatically seeded
context against one seeded by hand from the same path; the
require-system flag this PR teaches the tests to honor turns an absent
file into a failure rather than a skip.
- The fixture policy is the Amazon Linux 2023 DEFAULT file copied byte
for byte, so it carries the spellings, modifiers, and unimplemented
algorithm names the code has to cope with.
- A `CipherString` the policy applies is checked to leave the TLS 1.3
suites in place, and a `Ciphersuites` value the TLS 1.2 ciphers, which
is the merge seeding now performs itself.
- The caller's error queue is checked five ways: its entries, a mark it
had set, the data pointer from its last `ERR_get_error_line_data`, a
queue filled to capacity, and one with a single slot free.
- The unresolvable floor is exercised for TLS and for DTLS, against a
policy ceiling, against none, against a ceiling below the newest version
the method offers, and with a value too long to store; each case fails
when the raise is neutered.
- Unsatisfiable cipher rules, inverted bounds, a one-sided bound below
an existing floor, a floor naming an older protocol, an unresolvable
ceiling, and a version-locked method are each checked to leave the
context as it was.
- A path that fails and then becomes readable is checked to seed the
second context, which a cached failure prevents.

By submitting this pull request, I confirm that my contribution is made
under the terms of the Apache 2.0 license and the ISC license.

(cherry picked from commit 7ee4bcd)
### Context and motivation

Scripts commonly extract certs and keys with `openssl pkcs12 -in
bundle.p12 ...`. Our CLI had no `pkcs12` subcommand, so this adds an
import-only one aimed at the usual non-interactive invocations.

### Description of changes

Supports `-in`, `-out`, `-nokeys`, `-nocerts`, `-nodes`, `-noout`,
`-passin`, `-password`, `-passout`, `-legacy`, and `-help`. Built on
`PKCS12_get_key_and_certs`, so the MAC is verified before `-out` is
opened. That is stricter than OpenSSL 1.1.x, which truncates `-out`
before parsing; a successful run still opens/truncates `-out` even when
`-noout` or `-nocerts -nokeys` writes nothing.

Output order (certs, then key), `-password` overriding `-passin`
regardless of order, empty-password handling, and the `Mac verify error`
message match OpenSSL 1.1.x. If a key is to be written, `-nodes`
(unencrypted PKCS#8) or `-passout` (PKCS#8 encrypted with AES-256-CBC)
is required; there is no interactive prompt. `-nodes` overrides
`-passout`.

`-legacy` is accepted as a no-op so OpenSSL 3 scripts keep working. This
library already decrypts the RC2/3DES bags that flag loads the legacy
provider for.

Not implemented: `-export`, `-info`, `-clcerts`/`-cacerts`, `-twopass`,
`-nomacver`, `-chain`, `-descert`, interactive prompts, and the `Bag
Attributes` comment lines. Those unsupported options fail rather than
being ignored.

### Testing

38 unit tests plus two comparison tests that export a bundle with the
reference OpenSSL and check that both tools produce the same
certificates and PEM block sequence. Comparison tests run in CI against
OpenSSL 1.1.1 and 3.0.

The unit-test builder uses `PKCS12_create` defaults, so cert bags are
RC2-40 and the key bag is 3DES. Tests do not construct RC4 or OpenSSL 3
PBES2/AES-encrypted bundles (the latter is covered only when the
comparison job uses a 3.x `openssl pkcs12 -export`), and they do not
exercise BER indefinite-length inputs (the library handles those).

### Review considerations

CLI-only, no public API or FIPS changes. The AWS-LC parser is stricter
than OpenSSL in ways that surface here: bundles without a MAC (`-export
-nomac`) are rejected rather than imported with a warning, and bundles
with more than one private key are rejected. Like the other subcommands,
the CLI `ReadAll` helper caps input at 1 MiB.

By submitting this pull request, I confirm that my contribution is made
under the terms of the Apache 2.0 license and the ISC license.

(cherry picked from commit 2b530cf)
### Context and motivation
With pipefail enabled, `ldd | grep -q` can fail even when the expected
library is found: grep exits early, and a subsequent write from ldd
triggers SIGPIPE. The install test misinterprets this failure as static
linkage.

### Description of changes
Capture ldd output before searching it, use literal path matching, and
print the output on failure. Require ldd to succeed so loader errors
containing the expected library path cannot produce false positives.

### Testing
Reproduced the SIGPIPE race on Linux. Ad hoc macOS/Linux tests verified
the fix and covered successful linkage, missing libraries, nonzero
exits, literal paths, and SONAMEs. A real Linux corrupted-library test
confirmed that loader failures are rejected. Shell syntax and whitespace
checks passed. The full install CI job was not run.

By submitting this pull request, I confirm that my contribution is made
under the terms of the Apache 2.0 license and the ISC license.

(cherry picked from commit d821e4c)
### Context and motivation
`ASN1_parse` (the `ASN.1` dumper used by `asn1parse` and diagnostic
output) treated any `[UNIVERSAL 0]` element as an end-of-contents marker
without checking its length. A malformed `[UNIVERSAL 0]` with content
inside an indefinite-length parent elements would truncate that element
and mis-render later elements at the wrong depth. The output is wrong
but the parser stays in-bounds. This is a display/rendering issue in the
dumper, not a memory-safety or broad parsing-rejection change.

### Description of changes
Stops treating malformed tag-zero elements as terminators in the `ASN.1`
dumper. Signal `EOC` only when the element matches the two-byte form:
`tag V_ASN1_EOC`, `class 0`, `content length 0`, `header length 2`.
Anything else falls through as an ordinary unknown primitive and the
loop keeps iterating. Well-formed `EOC` is unaffected.

### Testing
- New test feeds dummy data to `ASN1_parse` followed by a real `INTEGER`
sibling and checks the `INTEGER` prints at the right depth.
- Full `ASN1Test` and `asn1parse` suites still pass.

By submitting this pull request, I confirm that my contribution is made
under the terms of the Apache 2.0 license and the ISC license.

(cherry picked from commit 4a1316d)
…ws#3543)

Harden various code-paths

(cherry picked from commit 6b4fded)

[fips-2026-06-26-snapshot backport] Dropped the INT_MAX length guard in
crypto/fipsmodule/cipher/e_aesccm.c, which is inside bcm.o. None of the tests
this commit adds exercise it. Everything else applies unchanged.
WillChilds-Klein and others added 7 commits October 9, 2026 18:28
…ws#3504)

Stack, split out of aws#3442, to merge bottom-up. Below the stack, aws#3501
and aws#3529 (error-queue primitives and the suppression scope), aws#3502
(policy file reader), and aws#3527 (shared-build symbol export and CI) have
landed.

1. aws#3503 -- cipher lists and version bounds
2. **aws#3504 -- groups and signature algorithms (this PR)**
3. aws#3505 -- comparison documentation

## Description

- Extends seeding to the policy's groups and signature algorithms,
narrowed to the algorithms AWS-LC implements and kept in the operator's
order. Every stock policy names something AWS-LC does not have -- X448,
the FFDHE groups, Ed448, RSA-PSS-PSS, the SHA-224 pairs -- and the
setters reject a whole list on the first unknown name, so applying a
value as written would discard the preference order entirely.
- Reads the OpenSSL 3.5 list syntax the stock policies use: `secp256r1`
for NIST P-256, `*` and `?` on an entry, `/` as an entry boundary, and
`-` to remove. Left as written, the two most-preferred groups in every
Amazon Linux 2023 list are the ones that throw the list away.
- A `-` removes its algorithm whatever else the value names, and takes
the ML-KEM hybrid over a removed group with it, because the hybrid
performs the key exchange the operator just forbade. A value that only
removes is applied to AWS-LC's own default list, in either directive,
and one that leaves nothing at all is skipped, since an empty list is
how AWS-LC asks for its defaults.
- Keeps AWS-LC's post-quantum defaults when the policy says nothing
about them, as Amazon Linux 2023's `DEFAULT` does not; the setters
replace AWS-LC's defaults rather than intersect with them, so seeding
would otherwise downgrade every context. A policy naming any
post-quantum algorithm is taken at its word, and one that names an
algorithm only to remove it keeps that algorithm out of what is
restored. `AWSLC.PostQuantum = off`, a directive of AWS-LC's own, waives
the defaults, since nothing the framework writes says "no post-quantum";
it goes in a `crypto-policies` drop-in file, because the framework
rewrites the generated back-end file on every policy change.
- Adds an internal name-to-signature-algorithm lookup that probes the
existing list parser under `bssl::ScopedErrorSuppression` and reports
the name unresolvable when the queue cannot be protected, so a rejected
name is never observable in the caller's queue. AWS-LC signs with a
different default list than it accepts, so a `SignatureAlgorithms` value
is resolved against each of them. The two preference lists are separate
allocations, so the signing list is moved aside and put back, leaving
the defaults in force when the verify setter fails.

## Testing / verification

- Amazon Linux 2023's `DEFAULT` and `DEFAULT:PQ` renderings are
exercised verbatim and the seeded lists asserted exactly, in policy
order, including where the hybrids and ML-DSA fall. They are the only
stock values that stack modifiers, separate tuples with `/`, and spell
each post-quantum algorithm twice.
- Removal is covered in both directives: alongside entries the same
value keeps, on its own against the default list, removing every group
so the directive is skipped, on an ML-DSA algorithm the post-quantum
defaults would otherwise restore, and on Ed25519, which only the signing
default list carries.
- The post-quantum rules are covered in both directions -- a silent
policy keeps the defaults, a policy naming one is left as written --
with the opt-out exercised case-insensitively and against a removal-only
`Groups` value.
- The test that reads the host's own policy file resolves it through the
seeding path rather than a parser written in the test, so a spelling the
resolver loses cannot pass unnoticed.
- Each guard was checked by neutering it in turn: the P-256 translation,
the modifier handling, the de-duplication, the narrowing, the opt-out,
and the classical-half requirement each fail a named test.

By submitting this pull request, I confirm that my contribution is made
under the terms of the Apache 2.0 license and the ISC license.

(cherry picked from commit eba0a7e)

[fips-2026-06-26-snapshot backport] This branch has no SSL_CTX_set1_group_ids
(aws#3346), so the filtered group list is installed directly into
|ctx->supported_group_list|. Every ID comes from ssl_name_to_group_id and
FilterPolicyIds already drops repeats, which covers the checks the setter would
make. The ML-DSA signature-algorithm merge is unchanged; this branch's defaults
include ML-DSA.
### Context and motivation
Existing certificate-deployment scripts fail because these options are
missing from AWS-LC's CLI.

### Description of changes
Adds `pkcs12 -export` and `req -batch`, which uses config
values/defaults instead of prompting, while preserving import behavior.
Export uses OpenSSL 1.1.1's PBE/MAC defaults (3DES keys, RC2-40 certs,
SHA1 MAC), rejects PBES2 algorithms `PKCS12_create` can't produce, and
uses an empty password when none is supplied rather than prompting.

### Testing
New `req`/`pkcs12` unit tests, plus OpenSSL 1.1.1w/3.6.4
interoperability and Java `keytool` deployment replays.

By submitting this pull request, I confirm that my contribution is made
under the terms of the Apache 2.0 license and the ISC license.

(cherry picked from commit 51db68a)
### Context and motivation

`openssl x509 -req` verifies a CSR's self-signature and rejects a
request whose signature doesn't match its public key. This tool's `-req`
path skipped that check and signed any parseable CSR, so scripts and
tests migrating from OpenSSL wouldn't get the same non-zero exit on a
bad CSR. However, most protocols will also require a proof of possesion
before doing any actions based on a certificate alone so this change
will just help fail faster.

  ### Description of changes

Returns a `kToolExitFailure` (1) when proof of possession fails or when
we're unable to get the public key for verification from the CSR.

  ### Testing

Added regression tests covering the new checks made in this PR.

By submitting this pull request, I confirm that my contribution is made
under the terms of the Apache 2.0 license and the ISC license.

(cherry picked from commit 40858cb)
…SL CLI (aws#3605)

### Related issues
Related to aws#3575.

### Context and motivation
Credential-rotation workflows need RSA key unwrapping and AES-256-CBC
decryption, which our CLI currently lacks.

### Description of changes
Adds `pkeyutl -encrypt/-decrypt` and expands `enc` cipher support,
aligning option handling and output-error reporting with OpenSSL 1.1.1.
Notably, `enc` without a cipher now copies input unchanged instead of
defaulting to AES-128-CBC.

### Testing
50 focused tests and 156 combined tests with aws#3575 passed, including
comparisons against OpenSSL 1.1.1w.

By submitting this pull request, I confirm that my contribution is made
under the terms of the Apache 2.0 license and the ISC license.

(cherry picked from commit c049a90)
glibc resolves a versioned symbol by node name alone, with no regard for which
library provides it, so a FIPS build and a mainline build that share
AWS_LC_<major>.<minor> can cross-bind when both are loaded into one process.
Rename the nodes to AWS_LC_FIPS5_<major>.<minor>, before a versioned release
freezes the old name. The FIPS major version is part of the prefix because the
FIPS 4 branch (AWS_LC_FIPS4) can carry the same node numbers.

The registries and .map files carry the node name, so they are rewritten too.
The scripts that parsed it stopped at a fixed AWS_LC_ prefix: the Go generator's
version sort and the update script's node arithmetic now match the namespace
instead, and the symbol-version test asserts the branch's prefix rather than
accepting any node that starts with AWS_LC_.

Ported from the FIPS 4 backport's 0c9a9d3 with FIPS4 -> FIPS5. Two hunks
were re-applied by hand against the text aws#3423 rewrote.
The consumer-facing docs were mainline's: libcrypto-awslc.so.1 and AWS_LC_1.0,
neither of which this branch installs. It keeps ABI_VERSION 0, so it ships
libcrypto-awslc.so.0 with AWS_LC_FIPS5_1.0 nodes. Library names are unchanged:
this branch keeps mainline's -awslc suffix.

Also states the SONAME-to-node pairing that the ABI-break recipe assumed went
the other way: ABI_VERSION 0 pairs with node 1.0, because nodes start at 1.0 on
every branch while ABI_VERSION reflects what has shipped. A break moves to
ABI_VERSION 1 and AWS_LC_FIPS5_2.0.

Ported from the FIPS 4 backport's 939cae5 and 1c12c5b.
The check compared two non-FIPS builds, so nothing covered the symbols only a
FIPS build exports, on the branch that exists to ship FIPS. Build FIPS=1 shared
libraries both ways and diff them too.

A FIPS build exports the four module-boundary markers delocate emits around
bcm.o, and the version script hides all four; they are allowlisted and reported
rather than left to mask a future FIPS-only export the extractor misses. The
allowlist is also why the check needs its own guard that the versioned side
really carries a version node.

Ported from the FIPS 4 backport's 72e905a. This branch's test locates the
libraries by mainline's -awslc names rather than a derived suffix.
@codecov-commenter

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 63.73898% with 1028 lines in your changes missing coverage. Please review.
✅ Project coverage is 78.20%. Comparing base (e001605) to head (adc393f).

Files with missing lines Patch % Lines
tool-openssl/pkcs12_test.cc 76.25% 190 Missing and 23 partials ⚠️
tool-openssl/pkeyutl_test.cc 43.58% 185 Missing and 4 partials ⚠️
tool-openssl/dgst_test.cc 7.69% 120 Missing ⚠️
tool-openssl/enc_test.cc 52.40% 79 Missing ⚠️
tool-openssl/pkcs8_test.cc 19.51% 66 Missing ⚠️
tool-openssl/dhparam_test.cc 0.00% 52 Missing ⚠️
tool-openssl/pkey_test.cc 22.03% 46 Missing ⚠️
tool-openssl/crl_test.cc 10.00% 36 Missing ⚠️
tool-openssl/pkcs12.cc 89.68% 33 Missing ⚠️
tool-openssl/ecparam_test.cc 0.00% 28 Missing ⚠️
... and 21 more
Additional details and impacted files
@@                     Coverage Diff                      @@
##           fips-2026-06-26-snapshot    #3647      +/-   ##
============================================================
- Coverage                     78.41%   78.20%   -0.22%     
============================================================
  Files                           693      699       +6     
  Lines                        124008   128335    +4327     
  Branches                      17245    17677     +432     
============================================================
+ Hits                          97240   100360    +3120     
- Misses                        25848    27016    +1168     
- Partials                        920      959      +39     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

This branch has not been deployed

No deployments
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.

8 participants