Skip to content

Add CTS mode, krb5 integration test#3308

Merged
WillChilds-Klein merged 23 commits into
aws:mainfrom
WillChilds-Klein:krb5-integration-test
Jul 14, 2026
Merged

Add CTS mode, krb5 integration test#3308
WillChilds-Klein merged 23 commits into
aws:mainfrom
WillChilds-Klein:krb5-integration-test

Conversation

@WillChilds-Klein

@WillChilds-Klein WillChilds-Klein commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

Adds a krb5 integration test for AWS-LC's CI, plus the public <openssl/modes.h> API additions needed to make MIT krb5 build cleanly against AWS-LC.

Integration test

  • New tests/ci/integration/run_krb5_integration.sh builds krb5-1.22.2-final against AWS-LC and runs former's tests
  • Asserts kinit is linked against AWS-LC's libcrypto via the existing check-linkage.sh helper (matching how cyrus-sasl, libgit2, tcpdump, etc. do their linkage checks).
  • Runs make check under src/lib/crypto, which exercises hashes, HMAC, encryption types, PRF, and key derivation against AWS-LC. The full top-level make check requires Python and live KDCs and is out of
    scope for this initial drop.
  • 3 source patches are applied to krb5, the motivation for each described in the patch file.

New CTS API

krb5's lib/crypto/openssl/enc_provider/aes.c calls CRYPTO_cts128_encrypt / CRYPTO_cts128_decrypt (and #include <openssl/modes.h>) on its OpenSSL <3.0 code path. AWS-LC didn't ship either:

  • New include/openssl/modes.h exporting cbc128_f, CRYPTO_cts128_encrypt, and CRYPTO_cts128_decrypt.
  • New crypto/fipsmodule/modes/cts.c implementing CBC Ciphertext Stealing in the CS1 / RFC 2040 convention that OpenSSL's legacy API uses (last two ciphertext blocks unconditionally swapped;
    exact-block-multiple input treated as a 16-byte residue; len <= 16 returns zero — callers should use plain CBC for those). This is the convention RFC 3962 / RFC 8009 Kerberos enctypes operate under.
  • The implementation is wired in via bcm.c like the other modes (cbc.c, cfb.c, etc.).

Tests for the new CTS API

crypto/fipsmodule/modes/cts_test.cc covers:

  • All four AES-128 and four AES-256 vectors from RFC 8009 Appendix A, encrypt-then-decrypt round-trip, with the published "AES Output" lines as asserted ciphertexts. All residue cases (6-byte, exact-block,
    5-byte after multi-block) are exercised.
  • Random round-trip across input lengths 17 (smallest legal CTS input) through 95 (5 full blocks + max residue 15) for AES-128, AES-192, and AES-256, with random plaintext and IV per length —
    covers every residue value and both sides of every block boundary.
  • Length contract — edge case lengths return 0 for both encrypt and decrypt.
  • CS1 swap invariant — at residue == 16, output equals raw CBC with the last two blocks swapped.
  • IV chaining — after encryption, ivec is advanced to the (post-swap) penultimate ciphertext block, matching OpenSSL behavior so consumers that chain CTS calls (e.g. krb5) get correct IV continuity.

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.

Adds run_krb5_integration.sh, which builds MIT krb5 against AWS-LC's
OpenSSL-compatible crypto backend (--with-crypto-impl=openssl), asserts
linkage via check-linkage.sh, and runs krb5's crypto unit tests
(src/lib/crypto/make check) which exercise hashes, HMAC, encryption
types, PRF, and key derivation against AWS-LC. Pins to the latest
krb5-*-final release tag resolved at runtime.
@codecov-commenter

codecov-commenter commented Jun 22, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 97.65625% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 78.19%. Comparing base (5df320b) to head (5a7c2bd).
⚠️ Report is 7 commits behind head on main.

Files with missing lines Patch % Lines
crypto/cipher_extra/cts_test.cc 96.42% 1 Missing and 2 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3308      +/-   ##
==========================================
+ Coverage   78.16%   78.19%   +0.03%     
==========================================
  Files         693      695       +2     
  Lines      123955   124083     +128     
  Branches    17213    17221       +8     
==========================================
+ Hits        96887    97030     +143     
+ Misses      26149    26131      -18     
- Partials      919      922       +3     

☔ 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.

Adds a krb5 matrix entry alongside cyrus-sasl, modeled on the same
shape (small ubuntu:22.04 / gcc-12 runner) so it gets picked up by
the standard integration omnibus run.
When krb5 is configured with --with-crypto-impl=openssl, the
lib/crypto/builtin/sha2/ subdir is still built unconditionally, even
though sha256.c/sha512.c bodies compile to nothing under
#ifdef K5_BUILTIN_SHA2. Their shared sha2.h, however, defines
SHA256_CTX/SHA512_CTX typedefs unguarded. AWS-LC's <openssl/opensslv.h>
transitively includes <openssl/base.h>, which defines its own
SHA256_CTX/SHA512_CTX typedefs — colliding with sha2.h's.

Stock OpenSSL's <openssl/opensslv.h> contains only version macros, so
the conflict only surfaces against AWS-LC.

Gate the typedef block in sha2.h on K5_BUILTIN_SHA2 to match the .c
file bodies. No-op for the builtin build; resolves the build break
against AWS-LC.
@WillChilds-Klein

Copy link
Copy Markdown
Contributor Author

Blocked on #3309.

Debugging the second build failure (job) revealed a real AWS-LC API gap, not a CI-hygiene issue. lib/crypto/openssl/enc_provider/aes.c (and camellia.c) #include <openssl/modes.h> and call CRYPTO_cts128_encrypt/_decrypt on the OpenSSL <3.0 code path that AWS-LC reports compatible with.

AWS-LC ships neither: <openssl/modes.h> doesn't exist, so the include resolves to system OpenSSL 3.x's header and produces typedef collisions; CRYPTO_cts128_encrypt is not implemented anywhere (only an internal CRYPTO_cts128_encrypt_block declaration in crypto/fipsmodule/modes/internal.h:411, with no body).

Filed #3309 to add public CTS128 APIs + tests. Leaving this PR in draft — will rebase and retest once #3309 lands.

Adds AES Ciphertext Stealing (CTS) mode in the CS1 / RFC 2040 convention
that OpenSSL's legacy |<openssl/modes.h>| API exposes: the last two
ciphertext blocks are unconditionally swapped, and an exact-block-multiple
input is treated as a 16-byte residue. Inputs of <= 16 bytes are
rejected with a return of zero (callers should use plain CBC for those).

Public surface lives at |<openssl/modes.h>|:
  - |cbc128_f| typedef (matches |AES_cbc_encrypt|'s signature)
  - |CRYPTO_cts128_encrypt|
  - |CRYPTO_cts128_decrypt|

Internal block-oriented variants used by the implementation
(|CRYPTO_cts128_encrypt_block|, |CRYPTO_cts128_decrypt_block|) remain
internal-only.

Tests cover:
  - All four AES-128 RFC 8009 Appendix A vectors and four AES-256
    vectors, encrypt-then-decrypt round-trip, with the published "AES
    Output" as the asserted ciphertext.
  - Exhaustive random round-trip across input lengths 17..95 bytes for
    AES-128, AES-192, and AES-256 (every residue and every length
    boundary on either side of a block).
  - Length contract: |len| <= 16 returns zero for both directions.
  - At residue == 16, output equals raw CBC with the last two blocks
    swapped (CS1 always-swap convention).
  - IV is advanced to the (post-swap) penultimate ciphertext block
    after encryption, matching OpenSSL's behavior so callers that
    chain CTS calls (e.g. krb5) get correct IV continuity.

Resolves aws#3309. Unblocks the krb5 integration test in aws#3308.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clang-tidy made some suggestions

Comment thread crypto/fipsmodule/modes/cts.c Outdated
Revert the BN_FLG_CONSTTIME compatibility shim — the macro was
intentionally omitted to surface callers relying on it. Instead,
patch krb5's SPAKE preauth plugin to remove the BN_set_flags call
entirely, since AWS-LC's BN operations are unconditionally
constant-time.
Address cppcoreguidelines-init-variables warnings for the |residue|
variable in both CRYPTO_cts128_encrypt and CRYPTO_cts128_decrypt.
Add the canonical Kerberos AES-CTS test vectors from RFC 3962. These
use AES-128 with the "chicken teriyaki" key at lengths 17, 31, 32,
47, 48, and 64 bytes — covering all boundary conditions including
block-aligned inputs (where CS3 always-swap applies) and multi-block
inputs with varying residue sizes.
Take the krb5 git ref to test as a positional argument instead of
resolving the latest release at runtime. Pin the workflow to
krb5-1.22.2-final for deterministic CI.
Three hygiene fixes from PR security review:

1. |OPENSSL_cleanse| the local |tmp[]| scratch in both
   |CRYPTO_cts128_encrypt| and |CRYPTO_cts128_decrypt| before return.
   In encrypt, |tmp[0..residue-1]| holds the trailing plaintext bytes;
   in decrypt, |tmp[0..15+residue-1]| holds the entire trailing
   16+residue bytes of plaintext after the second |cbc| call. Cleansing
   matches AWS-LC's hygiene posture for plaintext-bearing scratch (cf.
   |gcm.c|).
2. Add |assert(key != NULL && ivec != NULL)| at function entry and
   |assert(in != NULL && out != NULL)| after the |len <= 16| early
   return, matching the |CRYPTO_cbc128_*| family this lives next to.
3. Add |assert(!buffers_alias(in, len, out, len))| to enforce the
   "may not alias" precondition documented in <openssl/modes.h>.

Also adds |#include <openssl/mem.h>| for |OPENSSL_cleanse|.
Wrap all RAND_bytes calls in cts_test.cc in ASSERT_TRUE so a DRBG
failure fails the test rather than silently proceeding with stale
buffer contents.
The public <openssl/modes.h> and modes/internal.h both defined an
identical cbc128_f typedef. Include the public header from the internal
one and keep a single definition.

The krb5 integration script copied check-linkage.sh into the scratch
folder but never used the copy; the linkage assertion runs directly
from AWS_LC_BUILD_FOLDER.
Comment thread crypto/fipsmodule/modes/cts.c Outdated
Addresses review feedback: AES_cbc_encrypt and the other cbc128_f
implementations use unaligned loads (movups and equivalents) and impose
no alignment requirement on their I/O buffers, so the alignas(16) on
the stack scratch buffers had no effect worth keeping. The directive in
cbc.c predates this file and was copied along without a concrete
justification.
@github-actions

Copy link
Copy Markdown
Contributor

🔒 Security ReviewView Report

Please review before merging.

sgmenda
sgmenda previously approved these changes Jul 13, 2026
Comment thread crypto/fipsmodule/modes/cts.c Outdated
The implementation unconditionally swaps the last two ciphertext
blocks (even for block-aligned inputs), which is the defining
characteristic of CBC-CS3 per NIST SP 800-38A Addendum. The old
comments incorrectly labeled it CS1 and claimed CS3 was not provided.

Update the header comment in cts.c and all CS1 references in the test
file to correctly say CS3. No code changes.
sgmenda
sgmenda previously approved these changes Jul 13, 2026

@samuel40791765 samuel40791765 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we look into upstreaming these patches? If so, it would probably be better to gate the ``BN_set_flags(w, BN_FLG_CONSTTIME);one behind theOPENSSL_IS_AWSLC` preprocessor macro.

Comment thread crypto/cipher_extra/cts.c
Comment thread crypto/fipsmodule/modes/cts_test.cc Outdated
Comment thread crypto/fipsmodule/modes/cts.c Outdated
@WillChilds-Klein

Copy link
Copy Markdown
Contributor Author

Should we look into upstreaming these patches? If so, it would probably be better to gate the ``BN_set_flags(w, BN_FLG_CONSTTIME);one behind theOPENSSL_IS_AWSLC` preprocessor macro.

Good idea. Upstream PR #1533.

@sgmenda sgmenda left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

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.

4 participants