-
-
Notifications
You must be signed in to change notification settings - Fork 8.4k
extmod/modtls_mbedtls: Add support for TLS PSK #17074
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
Code size report:
|
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #17074 +/- ##
==========================================
- Coverage 98.54% 98.53% -0.01%
==========================================
Files 169 169
Lines 21890 21939 +49
==========================================
+ Hits 21571 21618 +47
- Misses 319 321 +2 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
Hey @projectgus and @dpgeorge . I've created this draft of an approach to add support for TLS PSK based on the work in #17074. I've bumped the other thread a few times, but figured I would move the discussion into my actual implementation here. There was some discussion on the different possible approaches and I believe what I have implemented here follows @dpgeorge 's suggestion in 17074. How does it look and does this direction seem good? If so, I can finalize the paperwork to close this out. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The commits in this PR need to be squashed and reworded to match MicroPython's Code Conventions. Aside from that:
extmod/modtls_mbedtls.c
Outdated
if (strcmp(ciphername, "PSK") == 0) { | ||
ssl_context->use_psk = true; | ||
set_psk_ciphersuites(&ssl_context->conf); | ||
return mp_const_none; | ||
} | ||
|
||
// Check if this is a PSK ciphersuite name | ||
if (strncmp(ciphername, "PSK-", 4) == 0 || | ||
strncmp(ciphername, "TLS-PSK-", 8) == 0 || | ||
strncmp(ciphername, "TLS_PSK_", 8) == 0) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a reference that can be cited here to justify this parse? This needs some authority behind it.
Also, why not just use mbedtls's own API to determine both the "is psk" and "is recognised" question together?
const mbedtls_ssl_ciphersuite_t *info = mbedtls_ssl_ciphersuite_from_string(ciphername);
if (mbedtls_ssl_ciphersuite_uses_psk(info)) {
...
(With some extra bits to correctly handle the 'failed lookup' case, of course.)
(Assuming that special-casing PSK here is even the right design anyway.)
// Enable PSK mode | ||
ssl_context->use_psk = true; | ||
|
||
// Create a ciphersuite array with just this one ciphersuite |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why should PSK ciphers be singleton, rather than parsed and accumulated as part of the existing list code?
extmod/modtls_mbedtls.c
Outdated
// SSLContext.set_psk_identity(identity) | ||
static mp_obj_t ssl_context_set_psk_identity(mp_obj_t self_in, mp_obj_t identity) { | ||
mp_obj_ssl_context_t *self = MP_OBJ_TO_PTR(self_in); | ||
self->psk_identity = identity; | ||
return mp_const_none; | ||
} | ||
static MP_DEFINE_CONST_FUN_OBJ_2(ssl_context_set_psk_identity_obj, ssl_context_set_psk_identity); | ||
|
||
// SSLContext.set_psk_key(key) | ||
static mp_obj_t ssl_context_set_psk_key(mp_obj_t self_in, mp_obj_t key) { | ||
mp_obj_ssl_context_t *self = MP_OBJ_TO_PTR(self_in); | ||
self->psk_key = key; | ||
return mp_const_none; | ||
} | ||
static MP_DEFINE_CONST_FUN_OBJ_2(ssl_context_set_psk_key_obj, ssl_context_set_psk_key); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why the intermediate self->psk_identity
and self->psk_key
variables? i.e. instead of a single function that combines the code with the code in ssl_socket_make_new
to call mbedtls_ssl_conf_psk
directly.
extmod/modtls_mbedtls.c
Outdated
@@ -603,6 +708,22 @@ static mp_obj_t ssl_socket_make_new(mp_obj_ssl_context_t *ssl_context, mp_obj_t | |||
|
|||
mbedtls_ssl_init(&o->ssl); | |||
|
|||
// Configure PSK if enabled | |||
if (ssl_context->use_psk && ssl_context->psk_identity != mp_const_none && ssl_context->psk_key != mp_const_none) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This condition should be more paranoid so it can give a better error. Mbedtls would error eventually, but any case where you don't have either all or none of those conditions, should be an error already.
extmod/modtls_mbedtls.c
Outdated
size_t psk_identity_len; | ||
const byte *psk_identity = (const byte *)mp_obj_str_get_data(ssl_context->psk_identity, &psk_identity_len); | ||
|
||
size_t psk_key_len; | ||
const byte *psk_key = (const byte *)mp_obj_str_get_data(ssl_context->psk_key, &psk_key_len); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These conversions could hit the bad_implicit_conversion
path and bypass cleanup --- neither this method nor the setters for those variables actually check mp_obj_is_str_or_bytes
.
// Define known PSK ciphersuites | ||
// These are common PSK ciphersuites supported by mbedtls | ||
static const int known_psk_ciphersuites[] = { | ||
MBEDTLS_TLS_PSK_WITH_AES_128_CBC_SHA256, | ||
MBEDTLS_TLS_PSK_WITH_AES_128_CBC_SHA, | ||
MBEDTLS_TLS_PSK_WITH_AES_256_CBC_SHA, | ||
MBEDTLS_TLS_PSK_WITH_AES_128_GCM_SHA256, | ||
MBEDTLS_TLS_PSK_WITH_AES_256_GCM_SHA384, | ||
0 // Terminating zero | ||
}; | ||
|
||
// Count available PSK ciphersuites | ||
int count = 0; | ||
for (int i = 0; known_psk_ciphersuites[i] != 0; i++) { | ||
count++; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Get the actual authoritative list of supported PSK suites by filtering the output from mbedtls_ssl_list_ciphersuites
using mbedtls_ssl_ciphersuite_from_id
and mbedtls_ssl_ciphersuite_uses_psk
.
// Allocate memory for PSK ciphersuites | ||
psk_ciphersuites = m_new(int, count + 1); | ||
if (psk_ciphersuites == NULL) { | ||
mp_raise_OSError(MP_ENOMEM); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Redundant with the m_malloc_fail
path already present in the allocator.
// Create a ciphersuite array with just this one ciphersuite | ||
ssl_context->ciphersuites = m_new(int, 2); | ||
if (ssl_context->ciphersuites == NULL) { | ||
mp_raise_OSError(MP_ENOMEM); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Redundant with the m_malloc_fail
path already present in the allocator.
extmod/modtls_mbedtls.c
Outdated
// Configure PSK | ||
ret = mbedtls_ssl_conf_psk(&ssl_context->conf, psk_key, psk_key_len, psk_identity, psk_identity_len); | ||
if (ret != 0) { | ||
goto cleanup; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This path needs coverage, with test cases that set invalid keys and identites.
For overall structure and direction, though:
|
This should probably be feature-gated? e.g. with a new Re: #14396 and the client-callback and server-callback API, in my opinion it could be appropriate to add those too, at least if behind another feature gate like |
This commit adds Pre-Shared Key (PSK) support to the mbedTLS-based TLS implementation in MicroPython. New features: - SSLContext.set_psk_identity(identity) method for setting PSK identity - SSLContext.set_psk_key(key) method for setting PSK key - SSLContext.set_ciphers('PSK') method for enabling PSK ciphersuites - Support for both generic PSK mode and specific PSK ciphersuite names - Full client and server PSK support The implementation enables PSK authentication mode which allows TLS connections without traditional certificate-based authentication, using pre-shared keys instead. Tests are added to verify PSK functionality with both client and server roles.
Replace string-based PSK ciphersuite detection with proper mbedtls API calls. - Use mbedtls_ssl_ciphersuite_from_string() instead of strncmp() calls - Add ciphersuite_uses_psk() helper function for PSK detection - Apply consistent mbedtls validation for both PSK and non-PSK ciphersuites - Improve robustness by eliminating fragile string parsing - Maintain backward compatibility with existing PSK functionali
Squashed and working through review feedback now. |
// Helper function to check if a ciphersuite uses PSK | ||
static bool ciphersuite_uses_psk(const mbedtls_ssl_ciphersuite_t *info) { | ||
if (info == NULL) { | ||
return false; | ||
} | ||
|
||
// Check if ciphersuite ID corresponds to any PSK ciphersuite | ||
int id = mbedtls_ssl_ciphersuite_get_id(info); | ||
|
||
// Check for common PSK ciphersuites based on their IDs | ||
// These correspond to the MBEDTLS_TLS_*_PSK_* constants | ||
return (id == 0x2C || // MBEDTLS_TLS_PSK_WITH_NULL_SHA | ||
id == 0x2D || // MBEDTLS_TLS_DHE_PSK_WITH_NULL_SHA | ||
id == 0x2E || // MBEDTLS_TLS_RSA_PSK_WITH_NULL_SHA | ||
id == 0x8C || // MBEDTLS_TLS_PSK_WITH_AES_128_CBC_SHA | ||
id == 0x8D || // MBEDTLS_TLS_PSK_WITH_AES_256_CBC_SHA | ||
id == 0x90 || // MBEDTLS_TLS_DHE_PSK_WITH_AES_128_CBC_SHA | ||
id == 0x91 || // MBEDTLS_TLS_DHE_PSK_WITH_AES_256_CBC_SHA | ||
id == 0x94 || // MBEDTLS_TLS_RSA_PSK_WITH_AES_128_CBC_SHA | ||
id == 0x95 || // MBEDTLS_TLS_RSA_PSK_WITH_AES_256_CBC_SHA | ||
id == 0xA8 || // MBEDTLS_TLS_PSK_WITH_AES_128_GCM_SHA256 | ||
id == 0xA9 || // MBEDTLS_TLS_PSK_WITH_AES_256_GCM_SHA384 | ||
id == 0xAA || // MBEDTLS_TLS_DHE_PSK_WITH_AES_128_GCM_SHA256 | ||
id == 0xAB || // MBEDTLS_TLS_DHE_PSK_WITH_AES_256_GCM_SHA384 | ||
id == 0xAC || // MBEDTLS_TLS_RSA_PSK_WITH_AES_128_GCM_SHA256 | ||
id == 0xAD || // MBEDTLS_TLS_RSA_PSK_WITH_AES_256_GCM_SHA384 | ||
id == 0xAE || // MBEDTLS_TLS_PSK_WITH_AES_128_CBC_SHA256 | ||
id == 0xAF || // MBEDTLS_TLS_PSK_WITH_AES_256_CBC_SHA384 | ||
id == 0xB0 || // MBEDTLS_TLS_PSK_WITH_NULL_SHA256 | ||
id == 0xB1 || // MBEDTLS_TLS_PSK_WITH_NULL_SHA384 | ||
id == 0xB2 || // MBEDTLS_TLS_DHE_PSK_WITH_AES_128_CBC_SHA256 | ||
id == 0xB3 || // MBEDTLS_TLS_DHE_PSK_WITH_AES_256_CBC_SHA384 | ||
id == 0xB4 || // MBEDTLS_TLS_DHE_PSK_WITH_NULL_SHA256 | ||
id == 0xB5 || // MBEDTLS_TLS_DHE_PSK_WITH_NULL_SHA384 | ||
id == 0xB6 || // MBEDTLS_TLS_RSA_PSK_WITH_AES_128_CBC_SHA256 | ||
id == 0xB7 || // MBEDTLS_TLS_RSA_PSK_WITH_AES_256_CBC_SHA384 | ||
id == 0xB8 || // MBEDTLS_TLS_RSA_PSK_WITH_NULL_SHA256 | ||
id == 0xB9); // MBEDTLS_TLS_RSA_PSK_WITH_NULL_SHA384 | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's a pity the actual mbedtls_ssl_ciphersuite_uses_psk
is only part of mbedtls/library/ssl_ciphersuites_internal.h
header rather than a public include --- but IMO it's still better to just link against it anyway instead of re-implementing it. Just declare it yourself the same way its header does without defining it:
int mbedtls_ssl_ciphersuite_uses_psk(const mbedtls_ssl_ciphersuite_t *info);
If that's too spicy, add MBEDTLS_ALLOW_PRIVATE_ACCESS
to mbedtls_config_common.h
and examine the value of info->key_exchange
against the four values it can have that indicate PSK suites.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, don't hardcode the numeric values, use the symbols! You can just id == MBEDTLS_TLS_PSK_WITH_NULL_SHA
, you already even have the mbedtls/ssl_ciphersuites.h
header that defines those.
// SSLContext.set_psk_identity(identity) and set_psk_key(key) | ||
// These methods now configure PSK directly with mbedtls instead of storing values | ||
static mp_obj_t psk_identity = mp_const_none; | ||
static mp_obj_t psk_key = mp_const_none; | ||
|
||
static mp_obj_t ssl_context_set_psk_identity(mp_obj_t self_in, mp_obj_t identity) { | ||
psk_identity = identity; | ||
return mp_const_none; | ||
} | ||
static MP_DEFINE_CONST_FUN_OBJ_2(ssl_context_set_psk_identity_obj, ssl_context_set_psk_identity); | ||
|
||
static mp_obj_t ssl_context_set_psk_key(mp_obj_t self_in, mp_obj_t key) { | ||
psk_key = key; | ||
return mp_const_none; | ||
} | ||
static MP_DEFINE_CONST_FUN_OBJ_2(ssl_context_set_psk_key_obj, ssl_context_set_psk_key); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did ChatGPT write this? Because that's a lie:
These methods now configure PSK directly with mbedtls instead of storing values
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The extra variables in mp_obj_ssl_context_t
was a bit sub-optimal, but using shared static variables like this is completely unacceptable. Even if we ignore mutliple contexts clobbering each other, it's also a use-after-free bug. (Can you spot it? It's because BSS variables aren't visible to micropython's garbage collector unless you give them a root-pointer decorator. The key and identity values here could be eligible for collection the instant these functions return.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The point of my earlier comment was this: The mp_obj_ssl_context_t
object already has a place to store these values, in its contained mbedtls_ssl_config
value --- i.e. the place that calling mbedtls_ssl_conf_psk
stores them.
The API here should just be the thinnest possible veneer over that function to make it callable from micropython --- just a function that takes two string arguments, validates that they're strings, extracts the underlying string pointers, and calls mbedtls_ssl_conf_psk
with them.
Hey @AJMansfield , I appreciate the detailed feedback, but I'm still working through it all, so I'm not sure that's very useful you providing feedback on my intermediate work. I'll ping you directly and set this PR out of draft to ready once I'm ready for another round of feedback. I just like to push to the branch often to get the CI to run and cross check my local setup. |
Understood! Apologies for the pressure: you're right it does neither of us any good to have me constantly looking over your shoulder, we've all got our own workflows. (I certainly made extensive use of the CI for testing changes myself at one point, lol.) Just be careful with the LLMs --- this is a cryptographic module, after all. |
This pull request adds support to the ssl module for TLS-PSK (pre-shared key) authentication.