Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Conversation

fwh-dc
Copy link
Contributor

@fwh-dc fwh-dc commented Oct 16, 2025

Checklist
  • documentation is added or updated
  • tests are added or updated

@fwh-dc
Copy link
Contributor Author

fwh-dc commented Oct 16, 2025

@nhorman I'm having some trouble porting the change that I did for the ossltest engine in https://github.com/openssl/openssl/pull/25119/files#diff-373f9ff409bc1e8eeb2d6335cccac16270dfa06ba39d1c3d49a4bdc318b23267

Since you've made the initial porting to the ossltest provider you might have some insight in where to look?

@nhorman
Copy link
Contributor

nhorman commented Oct 16, 2025

@fwh-dc If you have specific questions, I'll be happy to try answer them. For now, I'll just give you some general guidance.

The purpose of the ossltest provider is to provide the needed digests/ciphers/aead/rng methods for tls communication, but to do so in a way that "fake" or "known" data is returned as ciphertext and plaintext, so that the tests we have in place can validate that we receive the known data at various points during the handshake.

aes128ecb is close enough to aes128cbc that you can likely just follow the pattern for the cbc algorithm in the provider.

some notes about that:

  1. Start by adding an entry to the ossltest_ciphers array:
ALG(PROV_NAMES_AES_128_ECB, ossl_testaes128_ecb_functions}
  1. ossl_testaes128_cbc_functions should be an array of type OSSL_DISPATCH that contains the methods for implementing the cipher (NEWCTX, FREECTX, ENCRYPT/DECRYPT_INIT, GET_PARAMS, etc). See ossl_testaes128_cbc_functions for the full list.

  2. The NEWCTX function (like ossl_testaes128_cbc_newctx) should do two primary things
    a) allocate and return a structure of type PROV_EVP_AES128_ECB_CTX, which you should define in the provider. It can contain whatever information is useful to you, but should contain at least an OSSL_LIB_CTX pointer, to track the libctx it was allocated against, as well as an EVP_CIPHER_CTX pointer
    b) it should allocate and store in (a) an EVP_CIPHER_CTX using a cipher of the same type (AES-128-ECB) allocated from the default provider. I usually store this in (a) in a variable called sub_ctx. This is done so that in the osstest provider we can simulate the actually encrypt/decrypt work prior to returning "known" data.

  3. The remainder of the OSSL_DISPATCH functions largely work as "pass-through" operations. For instance the GET_CTX_PARAMS method (example ossl_test_aes128cbc_get_ctx_params) just calls the corresponding function against the sub_ctx cipher context from (3)(b). This saves you the work of having to do all the parameter encoding/decoding within the provider directly.

  4. There are a few notable exceptions to (4). The best example is in ossl_test_aes128cbc_update() which does our block cipher update for encrypt and decrypt. Here you see we do some magic:
    a) We start by doing some basic input and output buffer validation, nothing really magic there, just making sure that that the output buffer is large enough to hold the result of our operation plus any padding we might need on encrypt.
    b) We do the real encrypt/decrypt using EVP_Cipher against our sub_ctx context.
    c) This is key, we did (5)(b) just to confirm that the encryption happens. We then do a memcpy of the input to the output, effectively making our plaintext and ciphertext the same thing. This is of course wrong for any real cryptographic operation, but its done in the test provider so that the test harness in TLSProxy.pm has known data to check against when a handshake is in progress.
    d) After the memcpy, we have to either (1) pad the ciphertext to the block size on encrypt, or (b) strip the plaintext padding back to the original message. On encrypt we figure out how many bytes we need to pad to (16 on cbc, not sure what the value is for ecb, but I imagine its the same). For whatever that padding value is, we add that many bytes to the message, with each byte containing a value equal to the number of bytes we padded (i.e. if we had to pad the message with 12 bytes of data, each of the last 12 bytes in the message would contain the value 0xc). On decrypt, we do the opposite, we read the last byte in the message, and strip that many bytes off.

Truthfully, to get this to work, you can probably just clone all the structures/functions referencing aes128cbc, rename them to aes128ecb, and fix up the internal implementations of your renamed symbols to reference the ECB cipher where needed.

Hope that helps.

@fwh-dc
Copy link
Contributor Author

fwh-dc commented Oct 17, 2025

@nhorman I tried that approach but somehow it fails and I was just thinking if you could spot something out of the ordinary. But it sounds like I'm on the right path.

Do you test the provider somewhere? Because I couldn't find that anywhere and I think that might be the best way to debug the current implementation.

@nhorman
Copy link
Contributor

nhorman commented Oct 17, 2025

The provider is used in all the tls tests (test/recipies/70-test_tls13*). Basically anything that uses TLSProxy.pm starts a client and server with the options --provider=p_ossltest -propquery ?provider=p_ossltest. So the provider is definitely getting used. Given the nature of the above tests (the fact that plaintext == ciphertext as an expectation for all the handshake and data frames), if the provider wasn't getting used, those tests would fail spectacularly.

Running your dtlsack test locally from this branch, I think you're not getting to a point where the provider is being used however. Looking at one of the failed CI tests:
https://productionresultssa18.blob.core.windows.net/actions-results/301f9827-8d72-4332-a9fc-1851eedd0555/workflow-job-run-9c743e6e-748a-5c33-94ac-6d7adcb129a6/logs/job/job-logs.txt?rsct=text%2Fplain&se=2025-10-17T11%3A36%3A41Z&sig=HQoFvduJ25cRSinFK7IwdCJfu8OodpsvpeeujYG6s%2BU%3D&ske=2025-10-17T21%3A40%3A17Z&skoid=ca7593d4-ee42-46cd-af88-8b886a2f84eb&sks=b&skt=2025-10-17T09%3A40%3A17Z&sktid=398a6654-997b-47e9-b12b-9515b896b4de&skv=2025-11-05&sp=r&spr=https&sr=b&st=2025-10-17T11%3A26%3A36Z&sv=2025-11-05

All the messages starting at timestamp 2025-10-16T04:20:27.1563433Z show that the proxy is receiving only client messages, but the server is not responding. Taking a tcpdump here during the test:
cap.zip

you can see that dtls client hellos are getting sent, but there is no response from the server. Why thats happening, I'm not sure, but it would seem the first order of business is to figure out why you're not getting connected.

The ports change on every run, but using a test sample here, I see that in one run of the dtlsack test, The client is assigned port 64920, and the proxy 58890, with the server listening on 59435. Looking at the corresponding trace (not the one above), I see a dtls client hello geting forwarded from the client by the proxy to server port 59435 (which is good), but the server responds with a icmp dst port unreachable frame listing 59435 as the unreachable dst port. So that suggests to me that there is something wrong in the proxy code or the s_server setup that is causing udp frame reception to not work there. Thats the thing to look into first.

This is an asside, but something to keep in mind as well for next steps once you figure out the above. In the trace above, looking at the client hello, specifically the listed cipher suites from the client, I only see AES_256_GCM_SHA384, TLS_CHACHA20_POLY1305_SHA256, and AES_128_GCM_SHA256 listed. Given what you are doing in this PR, I would have expected to see an ECB cipher listed there as well, and its not, so once you get the connection issue sorted, I imagine you will have subsequent issues actually using the cipher, as its not getting advertised by the tls protocol as an available option to use.

@fwh-dc
Copy link
Contributor Author

fwh-dc commented Oct 17, 2025

Thanks for the explanation!

I think I need to add a bit of explaination:

So this particular addition will be executed when DTLS 1.3 does sequence number encryption. And since tests are now failing by the addition of this piece of code I'm assuming that it gets called.

I think the issue is that the client sends its client hello to the proxy and the proxy sends it to the server, but the server fails because of an issue with the implementation. So my question was really if there's something obvious in the implementation that does not look right.

I will create a test case to check the ECB implementation. If you spot something unusual in the meantime then I'd be happy to hear from you.
Thanks!

@nhorman
Copy link
Contributor

nhorman commented Oct 17, 2025

ah, you appear to be correct, if I attach a debugger to the server process it appears the connection gets accepted, but something happens during the setup that causes the connection to then be immediately closed while we're setting up for the handshake. What that is I can't tell at the moment, but a bit of tracing through the dtls code shows that dtls_crypt_sequence_number is returning 0 after calling EVP_CipherFinal_ex on the ecb cipher.

Copy link
Member

@mattcaswell mattcaswell left a comment

Choose a reason for hiding this comment

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

Fixing these issues should hopefully make it start working?

* handle that padding in ecb_update on our own, so intercept and
* squash that here
*/
return 1;
Copy link
Member

Choose a reason for hiding this comment

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

This needs to be properly wired up for this case. Unlike aes-128-cbc this is not being used for a TLS ciphersuite. This is just being used a standard EVP cipher. in particular we need to pass down the parameters to turn off padding.

Suggested change
return 1;
PROV_EVP_AES128_ECB_CTX *ctx = (PROV_EVP_AES128_ECB_CTX *)vprovctx;
return EVP_CIPHER_CTX_set_params(ctx->sub_ctx, params);

int soutl;
int ret;

ret = EVP_CipherFinal_ex(ctx->sub_ctx, out, &soutl);
Copy link
Member

Choose a reason for hiding this comment

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

You need to update outl here


memcpy(inbuf, in, inl);

soutl = EVP_Cipher(ctx->sub_ctx, (unsigned char *)out, in, (unsigned int)inl);
Copy link
Member

Choose a reason for hiding this comment

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

Call actual EVP_CipherUpdate here.

@t8m t8m added approval: review pending This pull request needs review by a committer triaged: feature The issue/pr requests/adds a feature tests: exempted The PR is exempt from requirements for testing branch: feature The issue or PR is relevant only to one of the feature branches. labels Oct 17, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approval: review pending This pull request needs review by a committer branch: feature The issue or PR is relevant only to one of the feature branches. tests: exempted The PR is exempt from requirements for testing triaged: feature The issue/pr requests/adds a feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

Morty Proxy This is a proxified and sanitized view of the page, visit original site.