Skip to content
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

net: lib: tls_credentials: Add CSR Generation #71242

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from

Conversation

glarsennordic
Copy link
Contributor

This PR contributes a number of commits which add CSR generation to the tls credentials subsystem.

Add optional tls_credential_csr function to the tls_credentials module,
allowing users to request a private/public keypair be generated, stored
(specifically the private key), and used to generate a CSR with the
requested distinguished name / subject.

Add an implementation of tls_credential_csr to the trusted credentials
backend, and also apply a couple minor related refactors.

Signed-off-by: Georges Oates_Larsen <georges.larsen@nordicsemi.no>
Improve how the tls_credentials shell handls printing large blocks of
data.

Move credentials buffer dumping into its own static function.

Add block prefix and block end strings to make parsing block data output
easier.

Prefix is printed before each line of block data, block end is printed
on new line after block data has finished printing.

Signed-off-by: Georges Oates_Larsen <georges.larsen@nordicsemi.no>
Adds `cred csr` command to the TLS Credentials Shell, exposing the
tls_credential_csr function.

Signed-off-by: Georges Oates_Larsen <georges.larsen@nordicsemi.no>
Add automated tests for tls_credential_csr.

Signed-off-by: Georges Oates_Larsen <georges.larsen@nordicsemi.no>
@glarsennordic
Copy link
Contributor Author

There are some minor mistakes I am already aware of, which I will point out in a self-review and fix the next time I do a deep rebase.

The bigger issue is that the final commit, Test CSR Generation is not working at present. When I attempt on-board execution, it hangs.

There also probably need to be some docs changes reflecting the new command and the new block data output.

I am posting this draft now so that the CSR generation code can be reviewed.

}

/* Destroy temporary PSA key if it was created */
if (key_created) {
Copy link
Contributor

Choose a reason for hiding this comment

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

instead of having a separate variable, would checking key_id != PSA_KEY_ID_NULL be sufficient ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You're right, that should work, good catch

@@ -35,6 +36,12 @@ config TLS_CREDENTIALS_BACKEND_PROTECTED_STORAGE

endchoice

config TLS_CREDENTIAL_CSR
bool "Enable CSR and private/public keypair generation"
Copy link
Contributor

Choose a reason for hiding this comment

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

should this be marked EXPERIMENTAL until opaque keys can be used?
currently, the private key material is made available in non-secure memory.
this should be noted in an obvious place.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think it should yes, I will do so

mbedtls_pk_context pk_ctx;
mbedtls_x509write_csr writer;
void* cred_start = 0;
size_t csr_max = *csr_len;
Copy link
Contributor

Choose a reason for hiding this comment

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

should csr_len be checked for NULL?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Can't hurt to do so, good catch

mbedtls_pk_context pk_ctx;
mbedtls_x509write_csr writer;
void* cred_start = 0;
size_t csr_max = *csr_len;
Copy link
Contributor

Choose a reason for hiding this comment

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

this could be const size_t csr_max = *csr_len;

@@ -124,6 +124,37 @@ int tls_credential_get(sec_tag_t tag, enum tls_credential_type type,
*/
int tls_credential_delete(sec_tag_t tag, enum tls_credential_type type);

/**
* @brief Create client private key and certificate signing request (CSR).
Copy link
Contributor

Choose a reason for hiding this comment

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

since this function does 2 things, any consideration for:

change to tls_credential_generate_key - with optional CSR or public key output
and/or
allow tls_credential_csr to use existing credentials

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The thought has definitely crossed my mind.

My initial thought had been to add only tls_credentials_csr and then later add a tls_credential_keygen function which only performs keygen.

But you make a very good point; Users may want tls_credentials_csr to support using a pre-existing private key, so long as the credentials backend can handle it.

It will be some extra work, but I'll go ahead and implement tls_credential_keygen, and give tls_credentials_csr an option to use an existing PK sectag rather than generate one. I think you are correct that this would be cleaner, and it will save us headache down the line if we implement it now.

/**
* @brief Create client private key and certificate signing request (CSR).
*
* @details This function generates a public/private keypair, and then signs a corresponding PKCS#10
Copy link
Contributor

Choose a reason for hiding this comment

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

add that SECP256R1 is used for key generation.

or add a config struct to allow user to specify key type/size along with CSR info such as country, organization, etc. (instead of only the char *dn parameter).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

add that SECP256R1 is used for key generation.

The thought I had was that it could simply be up to the storage back-end which key type is used for generating the CSR.

or add a config struct to allow user to specify key type/size along with CSR info such as country, organization, etc. (instead of only the char *dn parameter).

The dn parameter does allow a full distinguished name to be specified, so country, organization, etc are all possible. I'd rather avoid re-inventing a struct representation for those values when MbedTLS already accepts them quite naturally as a string.

As for key-type customization, I am struggling to see a way to do this cleanly. There are a lot of concerns to balance:

  1. If we need key type customization, we probably need it in command line too, so the system has to be compatible with that
  2. We should, IMO, try to keep the tls_credentials module API agnostic of the cryptography backend being used (such as MbedTLS). So we cannot simply wrap MbedTLS types (very easily)
  3. Others have expressed interest in expanding the TLS Credentials module to allow multiple simultaneous backends / APIs to create additional backends.

I suspect the cleanest way to handle this would probably be to allow a custom keytype to be requested via an optional string argument (alongside the dn argument). It would be up to the back-end to interpret/parse the keytype string, but this is probably the most versatile / interoperable option possible. I'd be willing to implement that in this PR.

Either that, or we will need to develop a full backend options customization system (similar to what sockets provide), and that, I think, should be a separate PR.

What do you think of the strings solution? @jayteemo

And @rlubos what are your thoughts on all of this as well?

Copy link
Contributor

Choose a reason for hiding this comment

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

The dn parameter does allow a full distinguished name to be specified, so country, organization, etc are all possible.

Ah ok, I didn't realize that accepted everything 👍🏻

Copy link
Contributor

Choose a reason for hiding this comment

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

image

I am referring to this.
Either indicate in the function header what it does or allow the user to specify their own key parameters.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, I understood you -- That section of code you highlight is in the secure credentials backend. While this is currently the only backend which supports CSR generation, it is conceivable that there might be other backends which also support CSR generation eventually.

Therefore, if we are letting the backends be in charge of what key types they support generating, it doesn't make sense to specify that in the header file (which describes the abstract API all backends conform to)

Allowing users to specify a desired key type for the back-end to consider does make some sense, But if we're going to do it, I think it will either need to come as part of another PR, or be a simple string argument which the backend is responsible for interpreting

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm only asking that you pick one option:
Either document that only ecc 256 keys are supported or allow the user to specify.

Maybe the documenting should be done in kconfig, maybe with an option?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Either document that only ecc 256 keys are supported

I could document it in the back-end for which it is true, sure.

But I cannot document it in tls_credentials.h since which key-types are supported depends on the back-end in use.

or allow the user to specify.

The problem with that is that it is complicated to do cleanly. I was asking your opinion on using a pure string to allow this.

But I have decided after some research and thought that a simple enum listing a subset of the IANA supported groups is probably sufficient for this purpose.

Copy link
Contributor

Choose a reason for hiding this comment

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

But I cannot document it in tls_credentials.h since which key-types are supported depends on the back-end in use.

i understand, i suggested using kconfig.

a simple enum listing a subset of the IANA supported groups is probably sufficient for this purpose.

sounds good.

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not really an expert in the area of key generation, but from the API perspective I think it'd make sense to provide an option to select key type (or parameters?) instead of hardcoding this within the backend. If the backed does not support requested params, it could simply reject the request.

@@ -21,6 +21,7 @@ choice TLS_CREDENTIALS_BACKEND

config TLS_CREDENTIALS_BACKEND_VOLATILE
bool "TLS credentials management volatile backend"
depends on !TLS_CREDENTIAL_CSR
Copy link
Contributor

Choose a reason for hiding this comment

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

should this dependency be moved to the TLS_CREDENTIAL_CSR option?
as in, TLS_CREDENTIAL_CSR depends on !TLS_CREDENTIALS_BACKEND_VOLATILE
or maybe better depends on TLS_CREDENTIALS_BACKEND_PROTECTED_STORAGE

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Personally, I think it makes more sense for the backends to specify which features they support, rather than for the features to specify which backends support them

In other words, I don't so much see this as "TLS_CREDENTIAL_CSR requires TLS_CREDENTIALS_BACKEND_PROTECTED_STORAGE", but rather, "TLS_CREDENTIALS_BACKEND_VOLATILE does not support the optional TLS_CREDENTIAL_CSR feature"

Copy link
Contributor

Choose a reason for hiding this comment

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

Ok.
Use whatever the standard is for zephyr... I don't know what that is 🙈

@@ -14,6 +14,9 @@
#include "tls_internal.h"
#include "tls_credentials_digest_raw.h"

/* This lets check which MbedTLS features are enabled */
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Suggested change
/* This lets check which MbedTLS features are enabled */
/* This lets us check which MbedTLS features are enabled */

#include CONFIG_MBEDTLS_CFG_FILE
#endif /* CONFIG_MBEDTLS_CFG_FILE */
#endif /* CONFIG_MBEDTLS */
/* This lets check which MbedTLS features are enabled */
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Suggested change
/* This lets check which MbedTLS features are enabled */
/* This lets us check which MbedTLS features are enabled */

@@ -0,0 +1,24 @@
/*
* Copyright (c) 2023 Nordic Semiconductor ASA
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Update copyright

return -EBADF;
}
return 0;
//TODO: Allow a customizable dump prefix.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Remove this TODO, its already done

/**
* @brief Create client private key and certificate signing request (CSR).
*
* @details This function generates a public/private keypair, and then signs a corresponding PKCS#10
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not really an expert in the area of key generation, but from the API perspective I think it'd make sense to provide an option to select key type (or parameters?) instead of hardcoding this within the backend. If the backed does not support requested params, it could simply reject the request.

* @brief Internal helper file which allows support for MbedTLS features to be checked easily.
*/

#ifndef __TLS_CREDEMTIALS_MBEDTLS_CONFIG_H
Copy link
Contributor

Choose a reason for hiding this comment

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

Typo, CREDEMTIALS

#if defined(CONFIG_MBEDTLS) && defined(MBEDTLS_USE_PSA_CRYPTO) && defined(MBEDTLS_X509_CSR_WRITE_C)

#include <psa/crypto.h>
#include "mbedtls/pk.h"
Copy link
Contributor

Choose a reason for hiding this comment

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

#include <mbedtls/pk.h>, same below

int tls_credential_csr(sec_tag_t tag, char *dn, void *csr, size_t *csr_len)
{
int ret = 0;

Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think we need those empty lines between variable definitons.

Comment on lines +724 to +725
"NULL-terminated, but a NULL-terminated "
"format was specified.\n");
Copy link
Contributor

Choose a reason for hiding this comment

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

Something weird with indentation here.

if (mbed_status) {
LOG_ERR("Failed to set up opaque private key. Status: %d", mbed_status);
ret = -EFAULT;
mbedtls_pk_free(&pk_ctx);
Copy link
Contributor

Choose a reason for hiding this comment

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

Isn't this done already after the cleanup label?

@@ -0,0 +1,14 @@

# Same as prj_trusted_tfm, but enable CSR generation.
Copy link
Contributor

Choose a reason for hiding this comment

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

This must've been overlooked in previous case, but since it's an overlay file it would be good to stick to common overlay file format (overlay-trusted-tfm-csr.conf).

@rlubos
Copy link
Contributor

rlubos commented Apr 12, 2024

@carlescufi Any idea who else from the upstream could be interested in reviewing this?

Copy link

This pull request has been marked as stale because it has been open (more than) 60 days with no activity. Remove the stale label or add a comment saying that you would like to have the label removed otherwise this pull request will automatically be closed in 14 days. Note, that you can always re-open a closed pull request at any time.

@github-actions github-actions bot added the Stale label Jun 12, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants