Skip to content

[test] various zero-length message positive and negative tests for AEAD ciphers - #31555

Closed
bbbrumley wants to merge 1 commit into
openssl:masterfrom
bbbrumley:bbb_evpnull
Closed

bbbrumley wants to merge 1 commit into
openssl:masterfrom
bbbrumley:bbb_evpnull

Conversation

@bbbrumley

Copy link
Copy Markdown
Contributor

A zero-length AEAD message driven through the one-shot EVP_Cipher() interface must agree with the streaming EVP_CipherFinal_ex() path. This checks:

  • an empty message yields the same tag via both interfaces
  • the true tag passes verification on decrypt
  • the modified tag fails verification on decrypt
Checklist
  • tests are added or updated

@bbbrumley

Copy link
Copy Markdown
Contributor Author

@andrewkdinh would you please help me with this one? I'm sure the CI barfs all over the place.

Relates to #29934 and your "one-shot" comment. If I remove the block you wanted, this test fails when applied to that dev branch. (I.e., the code block is indeed necessary, as I noted in that PR.)

What needs resolving is all the various corner cases like CCM / SIV / OCB / ChaCha20-Poly1305 / rando-weird-TLS-ciphers. I don't know the history of all those, but happy to take part in the conversation, and add workaround logic / fixes if necessary.

@bbbrumley

Copy link
Copy Markdown
Contributor Author

oh and @mattcaswell do I need an updated CLA? This is mostly from Claude

@openssl-machine openssl-machine added the approval: review pending This pull request needs review by a committer label Jun 17, 2026
@bbbrumley

Copy link
Copy Markdown
Contributor Author

For transparency, the nature of this PR is that @andrewkdinh asked me to remove a certain code block in #29934 so I did that, fully expecting the tests to vomit all over my terminal

they did not

now they do

@andrewkdinh

andrewkdinh commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

oh and @mattcaswell do I need an updated CLA? This is mostly from Claude

@bbbrumley yes, please sign the updated CLA: https://openssl-library.org/policies/cla/
Also follow the directions for attributing to Claude

@andrewkdinh
andrewkdinh self-requested a review June 17, 2026 13:50
@openssl-machine openssl-machine added the hold: cla required The contributor needs to submit a license agreement. label Jun 17, 2026
@bbbrumley

Copy link
Copy Markdown
Contributor Author

yes, please sign the updated CLA

done moments ago!

Also follow the directions for attributing to Claude

I think I got it -- it's just the Co-Authored-By: FOO bit?

@andrewkdinh

Copy link
Copy Markdown
Contributor

yes, please sign the updated CLA

done moments ago!

Also follow the directions for attributing to Claude

I think I got it -- it's just the Co-Authored-By: FOO bit?

As far as I understand, yes (Co-Authored By: name+model)

@bbbrumley bbbrumley closed this Jun 18, 2026
@bbbrumley bbbrumley reopened this Jun 18, 2026
@openssl-machine openssl-machine removed the hold: cla required The contributor needs to submit a license agreement. label Jun 18, 2026
@bbbrumley

Copy link
Copy Markdown
Contributor Author

@aisle-analyzer

@aisle-research-bot

aisle-research-bot Bot commented Jun 18, 2026

Copy link
Copy Markdown

🔒 Aisle Security Analysis

We found 1 low security issue(s) in this PR:

See details in the comment below.

Analyzed PR: #31555 at commit a95bc9e

Last updated on: 2026-06-18T06:27:07Z

Comment thread test/evp_extra_test.c
@bbbrumley

Copy link
Copy Markdown
Contributor Author

As far as I understand, yes (Co-Authored By: name+model)

Hmm OK as of today this is now Assisted-by per this policy

@bbbrumley

Copy link
Copy Markdown
Contributor Author

So I pushed a hack in 9adde00 just to turn the CI green. We need to go through the cases and discuss individually. Gonna start those convos now. Please help, need maintainers that at least know the history of these ciphers

@bbbrumley

Copy link
Copy Markdown
Contributor Author
        || EVP_CIPHER_is_a(info->ciph, "AES-128-CBC-HMAC-SHA1") /* TODO TLS stitched MTE cipher, OK to skip? */
        || EVP_CIPHER_is_a(info->ciph, "AES-256-CBC-HMAC-SHA1") /* TODO TLS stitched MTE cipher, OK to skip? */
        || EVP_CIPHER_is_a(info->ciph, "AES-128-CBC-HMAC-SHA256") /* TODO TLS stitched MTE cipher OK to skip? */
        || EVP_CIPHER_is_a(info->ciph, "AES-256-CBC-HMAC-SHA256") /* TODO TLS stitched MTE cipher OK to skip? */

I think these are legacy TLS MAC-then-encrypt ciphers that we can just safely skip because they are special purpose.

Thoughts?

@bbbrumley

Copy link
Copy Markdown
Contributor Author
        || info->mode == EVP_CIPH_GCM_SIV_MODE /* TODO bug? */

This seems like a bug to me. Even if SIV is two-pass, I would think one-shot and init-update-final (with a single update, essentially you have all the data) should work and be consistent?

@beldmit

beldmit commented Jun 19, 2026

Copy link
Copy Markdown
Member
        || info->mode == EVP_CIPH_GCM_SIV_MODE /* TODO bug? */

This seems like a bug to me. Even if SIV is two-pass, I would think one-shot and init-update-final (with a single update, essentially you have all the data) should work and be consistent?

I've recently fixed a CVE related to GCM SIV 0-bytes length processing, are there any differences with your code (didn't look yet)?

@bbbrumley

Copy link
Copy Markdown
Contributor Author
        || EVP_CIPHER_is_a(info->ciph, "ChaCha20-Poly1305")) /* TODO bug? */

Having implemented poly1305 myself in assembly (SIMD with floating point), this seems like a bug to me.

@bbbrumley

bbbrumley commented Jun 19, 2026

Copy link
Copy Markdown
Contributor Author

are there any differences with your code

I think one difference is here

https://github.com/openssl/openssl/blob/master/test/evp_extra_test.c#L7233

so even though the length is zero, aad is not NULL ? Not saying that part of the test is wrong, just saying, in my head, a non-NULL pointer with len 0 and a NULL pointer with len 0 should be treated the same? (Edit: for example, GCM treats them the same, and all the other AEAD ciphers passing this test that don't match this skip filter.)

@bbbrumley

Copy link
Copy Markdown
Contributor Author
        || info->mode == EVP_CIPH_OCB_MODE /* TODO bug? */

I have never implemented or taught OCB, google tells me it's single pass, so without knowing more, seems like a bug?

@bbbrumley

Copy link
Copy Markdown
Contributor Author
        || info->mode == EVP_CIPH_CCM_MODE /* TODO bug? */

I have never implemented or taught CCM, google tells me it's single pass, so without knowing more, seems like a bug?

@bbbrumley

Copy link
Copy Markdown
Contributor Author

Full disclosure, could be the test is abusing the API in some unexpected way. But as a user, this is indeed how I'd write it

But I know the OpenSSL AEAD API is full of all sorts of fun hard edges

@bbbrumley

Copy link
Copy Markdown
Contributor Author

Full disclosure, could be the test is abusing the API in some unexpected way. But as a user, this is indeed how I'd write it

But I know the OpenSSL AEAD API is full of all sorts of fun hard edges

So is this call sequence valid for AEAD?

EVP_EncryptInit_ex2
EVP_EncryptFinal_ex

In my head, that should produce a tag for a zero-length message with zero-length AAD?

The API contract for this should be in the man page? Help 😁

@openssl-machine openssl-machine added approval: done This pull request has the required number of approvals approval: ready to merge The 24 hour grace period has passed, ready to merge and removed approval: review pending This pull request needs review by a committer approval: done This pull request has the required number of approvals labels Jun 22, 2026
@openssl-machine

Copy link
Copy Markdown
Collaborator

This pull request is ready to merge

@t8m t8m added branch: master Applies to master branch triaged: feature The issue/pr requests/adds a feature tests: present The PR has suitable tests present extended tests Run extended tests in CI labels Jun 23, 2026
@t8m t8m closed this Jun 23, 2026
@t8m t8m reopened this Jun 23, 2026
@t8m

t8m commented Jun 24, 2026

Copy link
Copy Markdown
Member

Merged to the master branch. Thank you for your contribution.

@t8m t8m closed this Jun 24, 2026
openssl-machine pushed a commit that referenced this pull request Jun 24, 2026
…AD ciphers

A zero-length AEAD message driven through the one-shot EVP_Cipher() interface
must agree with the streaming EVP_CipherFinal_ex() path. This checks:
- an empty message yields the same tag via both interfaces
- the true tag passes verification on decrypt
- the modified tag fails verification on decrypt

Assisted-by: Claude:claude-opus-4-8

Reviewed-by: Dmitry Belyavskiy <[email protected]>
Reviewed-by: Neil Horman <[email protected]>
MergeDate: Wed Jun 24 12:47:11 2026
(Merged from #31555)
n13l pushed a commit to n13l/openssl that referenced this pull request Jul 25, 2026
…AD ciphers

A zero-length AEAD message driven through the one-shot EVP_Cipher() interface
must agree with the streaming EVP_CipherFinal_ex() path. This checks:
- an empty message yields the same tag via both interfaces
- the true tag passes verification on decrypt
- the modified tag fails verification on decrypt

Assisted-by: Claude:claude-opus-4-8

Reviewed-by: Dmitry Belyavskiy <[email protected]>
Reviewed-by: Neil Horman <[email protected]>
MergeDate: Wed Jun 24 12:47:11 2026
(Merged from openssl#31555)
openssl-machine pushed a commit that referenced this pull request Aug 7, 2026
At the EVP level, for AEADs EVP_Cipher(ctx, out, NULL, 0) performs
finalization. For consistency across AEADs (GCM, etc.), on decrypt it
must check the tag. OCB and ChaCha20-Poly1305 took an early exit on the
empty message (with or without AAD) and returned success without
checking, so a forged tag (with or without AAD) was accepted before this
change.

Follow-up to #31555

Assisted-by: Claude:claude-opus-4-8

Reviewed-by: Frederik Wedel-Heinen <[email protected]>
Reviewed-by: Dmitry Belyavskiy <[email protected]>
MergeDate: Fri Aug  7 13:34:35 2026
(Merged from #32173)
idrassi added a commit to amcrypto-jp/openssl that referenced this pull request Aug 11, 2026
For the affected OpenSSL built-in provider AEAD implementations,
EVP_Cipher(ctx, out, NULL, 0) reaches the ccipher callback as a
NULL-input terminal call. OCB and ChaCha20-Poly1305 took an early exit
on an empty message, with or without AAD, and returned success without
comparing an explicitly supplied tag. Consequently a corrupted tag was
accepted before this change.

Make these built-in callbacks perform their terminal tag operation,
aligning their explicit-tag handling with the streaming Final path
without defining NULL input as part of the generic EVP_Cipher()
contract.

AES-GCM-SIV also failed to generate a tag when Final was its first
empty-message operation. Generate the tag in that case and propagate
failures from the matching empty-message decrypt operation.

The stable ChaCha20-Poly1305 implementation aliases Update to the
one-shot cipher callback, so this backport introduces a dedicated Update
callback to preserve zero-length Update as a no-op.

Follow-up to openssl#31555
Fixes openssl#32258

Assisted-by: Claude:claude-opus-4-8
Assisted-by: Codex:gpt-5.6-sol

(cherry picked from commit 5741d29)

Co-authored-by: Mounir IDRASSI <[email protected]>
idrassi added a commit to amcrypto-jp/openssl that referenced this pull request Aug 19, 2026
For the affected OpenSSL built-in provider AEAD implementations,
EVP_Cipher(ctx, out, NULL, 0) reaches the ccipher callback as a
NULL-input terminal call. OCB and ChaCha20-Poly1305 took an early exit
on an empty message, with or without AAD, and returned success without
comparing an explicitly supplied tag. Consequently a corrupted tag was
accepted before this change.

Make these built-in callbacks perform their terminal tag operation,
aligning their explicit-tag handling with the streaming Final path
without defining NULL input as part of the generic EVP_Cipher()
contract.

AES-GCM-SIV also failed to generate a tag when Final was its first
empty-message operation. Generate the tag in that case and propagate
failures from the matching empty-message decrypt operation.

OpenSSL 4.0 already has a dedicated ChaCha20-Poly1305 Update callback,
so keep zero-length Update as a no-op there while allowing the
NULL-input ccipher call to perform the terminal tag operation.

Follow-up to openssl#31555
Fixes openssl#32258
Fixes CVE-2026-75803

Assisted-by: Claude:claude-opus-4-8
Assisted-by: Codex:gpt-5.6-sol

(cherry picked from commit 5741d29)

Co-authored-by: Mounir IDRASSI <[email protected]>
openssl-machine pushed a commit that referenced this pull request Aug 19, 2026
For the affected OpenSSL built-in provider AEAD implementations,
EVP_Cipher(ctx, out, NULL, 0) reaches the ccipher callback as a
NULL-input terminal call. OCB and ChaCha20-Poly1305 took an early exit
on an empty message, with or without AAD, and returned success without
comparing an explicitly supplied tag. Consequently a corrupted tag was
accepted before this change.

Make these built-in callbacks perform their terminal tag operation,
aligning their explicit-tag handling with the streaming Final path
without defining NULL input as part of the generic EVP_Cipher()
contract.

AES-GCM-SIV also failed to generate a tag when Final was its first
empty-message operation. Generate the tag in that case and propagate
failures from the matching empty-message decrypt operation.

OpenSSL 4.0 already has a dedicated ChaCha20-Poly1305 Update callback,
so keep zero-length Update as a no-op there while allowing the
NULL-input ccipher call to perform the terminal tag operation.

Follow-up to #31555
Fixes #32258
Fixes CVE-2026-75803

Assisted-by: Claude:claude-opus-4-8
Assisted-by: Codex:gpt-5.6-sol

(cherry picked from commit 5741d29)

Co-authored-by: Mounir IDRASSI <[email protected]>
Reviewed-by: Milan Broz <[email protected]>
Reviewed-by: Tomas Mraz <[email protected]>
Merge-date: Wed Aug 19 15:56:02 2026
Merged-from: #32300
openssl-machine pushed a commit that referenced this pull request Aug 19, 2026
For the affected OpenSSL built-in provider AEAD implementations,
EVP_Cipher(ctx, out, NULL, 0) reaches the ccipher callback as a
NULL-input terminal call. OCB and ChaCha20-Poly1305 took an early exit
on an empty message, with or without AAD, and returned success without
comparing an explicitly supplied tag. Consequently a corrupted tag was
accepted before this change.

Make these built-in callbacks perform their terminal tag operation,
aligning their explicit-tag handling with the streaming Final path
without defining NULL input as part of the generic EVP_Cipher()
contract.

AES-GCM-SIV also failed to generate a tag when Final was its first
empty-message operation. Generate the tag in that case and propagate
failures from the matching empty-message decrypt operation.

The stable ChaCha20-Poly1305 implementation aliases Update to the
one-shot cipher callback, so this backport introduces a dedicated Update
callback to preserve zero-length Update as a no-op.

Follow-up to #31555
Fixes #32258
Fixes CVE-2026-75803

Assisted-by: Claude:claude-opus-4-8
Assisted-by: Codex:gpt-5.6-sol

(cherry picked from commit 5741d29)

Co-authored-by: Mounir IDRASSI <[email protected]>
Reviewed-by: Milan Broz <[email protected]>
Reviewed-by: Tomas Mraz <[email protected]>
Merge-date: Wed Aug 19 15:58:27 2026
Merged-from: #32259
openssl-machine pushed a commit that referenced this pull request Aug 19, 2026
For the affected OpenSSL built-in provider AEAD implementations,
EVP_Cipher(ctx, out, NULL, 0) reaches the ccipher callback as a
NULL-input terminal call. OCB and ChaCha20-Poly1305 took an early exit
on an empty message, with or without AAD, and returned success without
comparing an explicitly supplied tag. Consequently a corrupted tag was
accepted before this change.

Make these built-in callbacks perform their terminal tag operation,
aligning their explicit-tag handling with the streaming Final path
without defining NULL input as part of the generic EVP_Cipher()
contract.

AES-GCM-SIV also failed to generate a tag when Final was its first
empty-message operation. Generate the tag in that case and propagate
failures from the matching empty-message decrypt operation.

The stable ChaCha20-Poly1305 implementation aliases Update to the
one-shot cipher callback, so this backport introduces a dedicated Update
callback to preserve zero-length Update as a no-op.

Follow-up to #31555
Fixes #32258
Fixes CVE-2026-75803

Assisted-by: Claude:claude-opus-4-8
Assisted-by: Codex:gpt-5.6-sol

(cherry picked from commit 5741d29)

Co-authored-by: Mounir IDRASSI <[email protected]>
Reviewed-by: Bob Beck <[email protected]>
Reviewed-by: Tomas Mraz <[email protected]>
Merge-date: Wed Aug 19 17:41:16 2026
Merged-from: #32416
openssl-machine pushed a commit that referenced this pull request Aug 19, 2026
For the affected OpenSSL built-in provider AEAD implementations,
EVP_Cipher(ctx, out, NULL, 0) reaches the ccipher callback as a
NULL-input terminal call. OCB and ChaCha20-Poly1305 took an early exit
on an empty message, with or without AAD, and returned success without
comparing an explicitly supplied tag. Consequently a corrupted tag was
accepted before this change.

Make these built-in callbacks perform their terminal tag operation,
aligning their explicit-tag handling with the streaming Final path
without defining NULL input as part of the generic EVP_Cipher()
contract.

AES-GCM-SIV also failed to generate a tag when Final was its first
empty-message operation. Generate the tag in that case and propagate
failures from the matching empty-message decrypt operation.

The stable ChaCha20-Poly1305 implementation aliases Update to the
one-shot cipher callback, so this backport introduces a dedicated Update
callback to preserve zero-length Update as a no-op.

Follow-up to #31555
Fixes #32258
Fixes CVE-2026-75803

Assisted-by: Claude:claude-opus-4-8
Assisted-by: Codex:gpt-5.6-sol

(cherry picked from commit 5741d29)

Co-authored-by: Mounir IDRASSI <[email protected]>
Reviewed-by: Bob Beck <[email protected]>
Reviewed-by: Tomas Mraz <[email protected]>
Merge-date: Wed Aug 19 17:41:16 2026
Merged-from: #32416
openssl-machine pushed a commit that referenced this pull request Aug 19, 2026
For the affected OpenSSL built-in provider AEAD implementations,
EVP_Cipher(ctx, out, NULL, 0) reaches the ccipher callback as a
NULL-input terminal call. OCB and ChaCha20-Poly1305 took an early exit
on an empty message, with or without AAD, and returned success without
comparing an explicitly supplied tag. Consequently a corrupted tag was
accepted before this change.

Make these built-in callbacks perform their terminal tag operation,
aligning their explicit-tag handling with the streaming Final path
without defining NULL input as part of the generic EVP_Cipher()
contract.

AES-GCM-SIV also failed to generate a tag when Final was its first
empty-message operation. Generate the tag in that case and propagate
failures from the matching empty-message decrypt operation.

The stable ChaCha20-Poly1305 implementation aliases Update to the
one-shot cipher callback, so this backport introduces a dedicated Update
callback to preserve zero-length Update as a no-op.

Follow-up to #31555
Fixes #32258
Fixes CVE-2026-75803

Assisted-by: Claude:claude-opus-4-8
Assisted-by: Codex:gpt-5.6-sol

(cherry picked from commit 5741d29)

Co-authored-by: Mounir IDRASSI <[email protected]>
Reviewed-by: Bob Beck <[email protected]>
Reviewed-by: Tomas Mraz <[email protected]>
Merge-date: Wed Aug 19 17:43:24 2026
Merged-from: #32417
rickyringler pushed a commit to rickyringler/openssl that referenced this pull request Aug 21, 2026
…AD ciphers

A zero-length AEAD message driven through the one-shot EVP_Cipher() interface
must agree with the streaming EVP_CipherFinal_ex() path. This checks:
- an empty message yields the same tag via both interfaces
- the true tag passes verification on decrypt
- the modified tag fails verification on decrypt

Assisted-by: Claude:claude-opus-4-8

Reviewed-by: Dmitry Belyavskiy <[email protected]>
Reviewed-by: Neil Horman <[email protected]>
MergeDate: Wed Jun 24 12:47:11 2026
(Merged from openssl#31555)
rickyringler pushed a commit to rickyringler/openssl that referenced this pull request Aug 21, 2026
At the EVP level, for AEADs EVP_Cipher(ctx, out, NULL, 0) performs
finalization. For consistency across AEADs (GCM, etc.), on decrypt it
must check the tag. OCB and ChaCha20-Poly1305 took an early exit on the
empty message (with or without AAD) and returned success without
checking, so a forged tag (with or without AAD) was accepted before this
change.

Follow-up to openssl#31555

Assisted-by: Claude:claude-opus-4-8

Reviewed-by: Frederik Wedel-Heinen <[email protected]>
Reviewed-by: Dmitry Belyavskiy <[email protected]>
MergeDate: Fri Aug  7 13:34:35 2026
(Merged from openssl#32173)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approval: ready to merge The 24 hour grace period has passed, ready to merge branch: master Applies to master branch extended tests Run extended tests in CI tests: present The PR has suitable tests present triaged: feature The issue/pr requests/adds a feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants