diff --git a/cmake/FindMbedTLS.cmake b/cmake/FindMbedTLS.cmake index 048a1e9b4..92a8a8369 100644 --- a/cmake/FindMbedTLS.cmake +++ b/cmake/FindMbedTLS.cmake @@ -43,6 +43,6 @@ FIND_PACKAGE_HANDLE_STANDARD_ARGS(MbedTLS REQUIRED_VARS MBEDTLS_X509 MBEDTLS_INCLUDE_DIRS) IF (MbedTLS_FOUND) - SET(MBEDTLS_LIBRARIES ${MBEDTLS_LIBRARY} ${MBEDTLS_CRYPTO} ${MBEDTLS_X509}) + SET(MBEDTLS_LIBRARIES ${MBEDTLS_LIBRARY} ${MBEDTLS_X509} ${MBEDTLS_CRYPTO} ) MARK_AS_ADVANCED(MBEDTLS_LIBRARIES MBEDTLS_INCLUDE_DIRS) ENDIF () diff --git a/include/picotls/mbedtls.h b/include/picotls/mbedtls.h index 62fd382d3..b18b90109 100644 --- a/include/picotls/mbedtls.h +++ b/include/picotls/mbedtls.h @@ -60,9 +60,21 @@ extern ptls_key_exchange_algorithm_t *ptls_mbedtls_key_exchanges[]; void ptls_mbedtls_random_bytes(void *buf, size_t len); +int ptls_mbedtls_load_file(char const* file_name, unsigned char** buf, size_t* n); + int ptls_mbedtls_load_private_key(ptls_context_t *ctx, char const *pem_fname); void ptls_mbedtls_dispose_sign_certificate(ptls_sign_certificate_t *_self); +int ptls_mbedtls_sign_certificate(ptls_sign_certificate_t* _self, ptls_t* tls, ptls_async_job_t** async, + uint16_t* selected_algorithm, ptls_buffer_t* outbuf, ptls_iovec_t input, + const uint16_t* algorithms, size_t num_algorithms); + +int picoquic_mbedtls_get_certs_from_file(char const* pem_fname, ptls_iovec_t** vec, size_t* count); +int ptls_mbedtls_init_verify_certificate(ptls_context_t* ptls_ctx, char const* pem_fname); +void ptls_mbedtls_dispose_verify_certificate(ptls_context_t* ptls_ctx); + + + #ifdef __cplusplus } #endif diff --git a/lib/mbedtls.c b/lib/mbedtls.c index 86fa7b017..8c0569f7b 100644 --- a/lib/mbedtls.c +++ b/lib/mbedtls.c @@ -305,20 +305,27 @@ static void aead_encrypt_v(struct st_ptls_aead_context_t *_ctx, void *output, pt struct ptls_mbedtls_aead_context_t *ctx = (struct ptls_mbedtls_aead_context_t *)_ctx; psa_aead_operation_t op = psa_aead_operation_init(); uint8_t *dst = output, iv[PTLS_MAX_IV_SIZE], tag[PSA_AEAD_TAG_MAX_SIZE]; - size_t outlen, taglen; + size_t outlen, taglen, inlen = 0, outlen_max; + /* Compute the complete input length, so we can call */ + for (size_t i = 0; i < incnt; i++) { + inlen += input[i].len; + } + outlen_max = PSA_AEAD_UPDATE_OUTPUT_MAX_SIZE(inlen); /* setup op */ CALL_WITH_CHECK(psa_aead_encrypt_setup, &op, ctx->key, ctx->alg); ptls_aead__build_iv(ctx->super.algo, iv, ctx->static_iv, seq); CALL_WITH_CHECK(psa_aead_set_nonce, &op, iv, ctx->super.algo->iv_size); + CALL_WITH_CHECK(psa_aead_set_lengths, &op, aadlen, inlen); CALL_WITH_CHECK(psa_aead_update_ad, &op, aad, aadlen); /* encrypt */ for (size_t i = 0; i < incnt; i++) { - CALL_WITH_CHECK(psa_aead_update, &op, input[i].base, input[i].len, dst, SIZE_MAX, &outlen); + CALL_WITH_CHECK(psa_aead_update, &op, input[i].base, input[i].len, dst, outlen_max, &outlen); dst += outlen; + outlen_max -= outlen; } - CALL_WITH_CHECK(psa_aead_finish, &op, dst, SIZE_MAX, &outlen, tag, sizeof(tag), &taglen); + CALL_WITH_CHECK(psa_aead_finish, &op, dst, outlen_max, &outlen, tag, sizeof(tag), &taglen); dst += outlen; memcpy(dst, tag, taglen); diff --git a/lib/mbedtls_sign.c b/lib/mbedtls_sign.c index de06175f3..3ebedcb8c 100644 --- a/lib/mbedtls_sign.c +++ b/lib/mbedtls_sign.c @@ -1,24 +1,24 @@ /* - * Copyright (c) 2023, Christian Huitema - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ +* Copyright (c) 2023, Christian Huitema +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to +* deal in the Software without restriction, including without limitation the +* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +* sell copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +* IN THE SOFTWARE. +*/ #ifdef _WINDOWS #include "wincompat.h" @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -50,14 +51,29 @@ typedef struct st_ptls_mbedtls_sign_certificate_t { const ptls_mbedtls_signature_scheme_t *schemes; } ptls_mbedtls_sign_certificate_t; +typedef struct st_ptls_mbedtls_verify_certificate_t { + ptls_verify_certificate_t super; + mbedtls_x509_crt *trust_ca; + mbedtls_x509_crl *trust_crl; + int (*f_vrfy)(void*, mbedtls_x509_crt*, int, uint32_t*); + void* p_vrfy; +} ptls_mbedtls_verify_certificate_t; + static const unsigned char ptls_mbedtls_oid_ec_key[] = {0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01}; static const unsigned char ptls_mbedtls_oid_rsa_key[] = {0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01}; +#if 0 +/* Commented out for now, as EDDSA is not yet supported by MbedTLS */ static const unsigned char ptls_mbedtls_oid_ed25519[] = {0x2b, 0x65, 0x70}; +#endif -static const ptls_mbedtls_signature_scheme_t rsa_signature_schemes[] = {{PTLS_SIGNATURE_RSA_PSS_RSAE_SHA256, PSA_ALG_SHA_256}, - {PTLS_SIGNATURE_RSA_PSS_RSAE_SHA384, PSA_ALG_SHA_384}, - {PTLS_SIGNATURE_RSA_PSS_RSAE_SHA512, PSA_ALG_SHA_512}, - {UINT16_MAX, PSA_ALG_NONE}}; +static const ptls_mbedtls_signature_scheme_t rsa_signature_schemes[] = { + {PTLS_SIGNATURE_RSA_PKCS1_SHA256, PSA_ALG_SHA_256}, + {PTLS_SIGNATURE_RSA_PSS_RSAE_SHA256, PSA_ALG_SHA_256}, + {PTLS_SIGNATURE_RSA_PSS_RSAE_SHA384, PSA_ALG_SHA_384}, + {PTLS_SIGNATURE_RSA_PSS_RSAE_SHA512, PSA_ALG_SHA_512}, + {PTLS_SIGNATURE_RSA_PKCS1_SHA1, PSA_ALG_SHA_1}, + {UINT16_MAX, PSA_ALG_NONE} +}; static const ptls_mbedtls_signature_scheme_t secp256r1_signature_schemes[] = { {PTLS_SIGNATURE_ECDSA_SECP256R1_SHA256, PSA_ALG_SHA_256}, {UINT16_MAX, PSA_ALG_NONE}}; @@ -65,8 +81,11 @@ static const ptls_mbedtls_signature_scheme_t secp384r1_signature_schemes[] = { {PTLS_SIGNATURE_ECDSA_SECP384R1_SHA384, PSA_ALG_SHA_384}, {UINT16_MAX, PSA_ALG_NONE}}; static const ptls_mbedtls_signature_scheme_t secp521r1_signature_schemes[] = { {PTLS_SIGNATURE_ECDSA_SECP521R1_SHA512, PSA_ALG_SHA_512}, {UINT16_MAX, PSA_ALG_NONE}}; +#if 0 +/* Commented out for now, as EDDSA is not yet supported by MbedTLS */ static const ptls_mbedtls_signature_scheme_t ed25519_signature_schemes[] = {{PTLS_SIGNATURE_ED25519, PSA_ALG_NONE}, - {UINT16_MAX, PSA_ALG_NONE}}; + {UINT16_MAX, PSA_ALG_NONE}}; +#endif #if defined(MBEDTLS_PEM_PARSE_C) @@ -149,11 +168,14 @@ static int ptls_mbedtls_parse_ecdsa_field(const unsigned char *pem_buf, size_t p return ret; } +#if 0 +/* Code commented out for now, as EDDSA is not supported yet in MbedTLS */ + /* On input, key_index points at the "key information" in a - * "private key" message. For EDDSA, this contains an - * octet string carrying the key itself. On return, key index - * and key length are updated to point at the key field. - */ +* "private key" message. For EDDSA, this contains an +* octet string carrying the key itself. On return, key index +* and key length are updated to point at the key field. +*/ static int ptls_mbedtls_parse_eddsa_key(const unsigned char *pem_buf, size_t pem_len, size_t *key_index, size_t *key_length) { int ret = 0; @@ -174,12 +196,13 @@ static int ptls_mbedtls_parse_eddsa_key(const unsigned char *pem_buf, size_t pem } return ret; } +#endif /* If using PKCS8 encoding, the "private key" field contains the - * same "ecdsa field" found in PEM "EC PRIVATE KEY" files. We - * use the same parser, but we need to reset indices so they - * reflect the unwrapped key. - */ +* same "ecdsa field" found in PEM "EC PRIVATE KEY" files. We +* use the same parser, but we need to reset indices so they +* reflect the unwrapped key. +*/ int ptls_mbedtls_parse_ec_private_key(const unsigned char *pem_buf, size_t pem_len, size_t *key_index, size_t *key_length) { size_t x_offset = 0; @@ -193,8 +216,15 @@ int ptls_mbedtls_parse_ec_private_key(const unsigned char *pem_buf, size_t pem_l return ret; } -int test_parse_private_key_field(const unsigned char *pem_buf, size_t pem_len, size_t *oid_index, size_t *oid_length, - size_t *key_index, size_t *key_length) +/* Parsing the private key field in a PEM key object. +* The syntax is similar to the "public key info", but there +* are differences, such as encoding the key as an octet +* string instead of a bit field. +* TODO: look at unifying the common parts for making the +* code a bit smaller. +*/ +int ptls_parse_private_key_field(const unsigned char *pem_buf, size_t pem_len, size_t *oid_index, size_t *oid_length, + size_t *key_index, size_t *key_length) { int ret = 0; size_t l_oid = 0; @@ -220,8 +250,8 @@ int test_parse_private_key_field(const unsigned char *pem_buf, size_t pem_len, s ret = -1; } else { /* the sequence contains the OID and optional key attributes, - * which we ignore for now. - */ + * which we ignore for now. + */ size_t l_seq = 0; size_t x_seq; ret = ptls_mbedtls_parse_der_length(pem_buf, pem_len, &x, &l_seq); @@ -241,8 +271,8 @@ int test_parse_private_key_field(const unsigned char *pem_buf, size_t pem_len, s } if (ret == 0) { /* At that point the oid has been identified. - * The next parameter is an octet string containing the key info. - */ + * The next parameter is an octet string containing the key info. + */ if (x + 2 > pem_len || pem_buf[x++] != 0x04) { ret = -1; } else { @@ -263,7 +293,7 @@ int test_parse_private_key_field(const unsigned char *pem_buf, size_t pem_len, s } int ptls_mbedtls_get_der_key(mbedtls_pem_context *pem, mbedtls_pk_type_t *pk_type, const unsigned char *key, size_t keylen, - const unsigned char *pwd, size_t pwdlen, int (*f_rng)(void *, unsigned char *, size_t), void *p_rng) + const unsigned char *pwd, size_t pwdlen, int (*f_rng)(void *, unsigned char *, size_t), void *p_rng) { int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; #if defined(MBEDTLS_PEM_PARSE_C) @@ -278,11 +308,11 @@ int ptls_mbedtls_get_der_key(mbedtls_pem_context *pem, mbedtls_pk_type_t *pk_typ #if defined(MBEDTLS_RSA_C) /* Avoid calling mbedtls_pem_read_buffer() on non-null-terminated string */ - if (key[keylen - 1] != '\0') { + if (key[keylen] != '\0') { ret = MBEDTLS_ERR_PEM_NO_HEADER_FOOTER_PRESENT; } else { ret = mbedtls_pem_read_buffer(pem, "-----BEGIN RSA PRIVATE KEY-----", "-----END RSA PRIVATE KEY-----", key, pwd, pwdlen, - &len); + &len); } if (ret == 0) { @@ -293,14 +323,13 @@ int ptls_mbedtls_get_der_key(mbedtls_pem_context *pem, mbedtls_pk_type_t *pk_typ } else if (ret == MBEDTLS_ERR_PEM_PASSWORD_REQUIRED) { return MBEDTLS_ERR_PK_PASSWORD_REQUIRED; } else if (ret != MBEDTLS_ERR_PEM_NO_HEADER_FOOTER_PRESENT) { - return ret; } #endif /* MBEDTLS_RSA_C */ #if defined(MBEDTLS_PK_HAVE_ECC_KEYS) /* Avoid calling mbedtls_pem_read_buffer() on non-null-terminated string */ - if (key[keylen - 1] != '\0') { + if (key[keylen] != '\0') { ret = MBEDTLS_ERR_PEM_NO_HEADER_FOOTER_PRESENT; } else { ret = @@ -319,7 +348,7 @@ int ptls_mbedtls_get_der_key(mbedtls_pem_context *pem, mbedtls_pk_type_t *pk_typ #endif /* MBEDTLS_PK_HAVE_ECC_KEYS */ /* Avoid calling mbedtls_pem_read_buffer() on non-null-terminated string */ - if (key[keylen - 1] != '\0') { + if (key[keylen] != '\0') { ret = MBEDTLS_ERR_PEM_NO_HEADER_FOOTER_PRESENT; } else { ret = mbedtls_pem_read_buffer(pem, "-----BEGIN PRIVATE KEY-----", "-----END PRIVATE KEY-----", key, NULL, 0, &len); @@ -333,11 +362,11 @@ int ptls_mbedtls_get_der_key(mbedtls_pem_context *pem, mbedtls_pk_type_t *pk_typ #if defined(MBEDTLS_PKCS12_C) || defined(MBEDTLS_PKCS5_C) /* Avoid calling mbedtls_pem_read_buffer() on non-null-terminated string */ - if (key[keylen - 1] != '\0') { + if (key[keylen] != '\0') { ret = MBEDTLS_ERR_PEM_NO_HEADER_FOOTER_PRESENT; } else { ret = mbedtls_pem_read_buffer(pem, "-----BEGIN ENCRYPTED PRIVATE KEY-----", "-----END ENCRYPTED PRIVATE KEY-----", key, - NULL, 0, &len); + NULL, 0, &len); } if (ret == 0) { /* infor is unknown */ @@ -350,8 +379,124 @@ int ptls_mbedtls_get_der_key(mbedtls_pem_context *pem, mbedtls_pk_type_t *pk_typ } #endif +/* When finding public keys in a certificate, we expect the syntax to be: +* SubjectPublicKeyInfo ::= SEQUENCE { +* algorithm AlgorithmIdentifier, +* subjectPublicKey BIT STRING +* } +* AlgorithmIdentifier ::= SEQUENCE { +* algorithm OBJECT IDENTIFIER, +* parameters ANY DEFINED BY algorithm OPTIONAL +* } +*/ +static int ptls_mbedtls_parse_public_key_info(const unsigned char *pem_buf, size_t pem_len, + size_t *oid_index, size_t *oid_length, + size_t *param_index, size_t *param_length, + size_t *key_index, size_t *key_length) +{ + int ret = 0; + size_t x = 0; + + if (ret == 0) { + if (pem_buf[x++] != 0x30 /* sequence */) { + ret = PTLS_ERROR_INCORRECT_ASN1_SYNTAX; + } + else { + size_t l_seq1 = 0; + size_t x_seq1; + ret = ptls_mbedtls_parse_der_length(pem_buf, pem_len, &x, &l_seq1); + x_seq1 = x; + if (x + l_seq1 > pem_len || pem_buf[x++] != 0x30) { + ret = PTLS_ERROR_INCORRECT_ASN1_SYNTAX; + } + else { + size_t l_seq = 0; + size_t x_seq; + ret = ptls_mbedtls_parse_der_length(pem_buf, pem_len, &x, &l_seq); + x_seq = x; + if (x + l_seq > pem_len || pem_buf[x++] != 0x06) { + ret = PTLS_ERROR_INCORRECT_ASN1_SYNTAX; + } + else { + /* Sequence contains the OID and optional key attributes */ + *oid_length = pem_buf[x++]; + *oid_index = x; + *param_index = x + *oid_length; + x = x_seq + l_seq; + if (*param_index > x) { + ret = PTLS_ERROR_INCORRECT_ASN1_SYNTAX; + } + else { + *param_length = x - *param_index; + } + } + } + + if (ret == 0) { + /* At that point the oid has been identified. + * The next parameter is an octet string containing the key info. + */ + if (x + 2 > pem_len || pem_buf[x++] != 0x03) { + ret = PTLS_ERROR_INCORRECT_ASN1_SYNTAX; + } + else { + ret = ptls_mbedtls_parse_der_length(pem_buf, pem_len, &x, key_length); + *key_index = x; + x += *key_length; + if (x > pem_len) { + ret = PTLS_ERROR_INCORRECT_ASN1_SYNTAX; + } + } + } + } + } + + return ret; +} + +/* Obtain the public key bits and the public key attributes from the +* subject public key info in a certificate. +*/ +int ptls_mbedtls_get_public_key_info(const unsigned char* pk_raw, size_t pk_raw_len, + psa_key_attributes_t* attributes, + size_t* key_index, size_t* key_length) +{ + size_t oid_index, oid_length, param_index, param_length; + int ret = ptls_mbedtls_parse_public_key_info(pk_raw, pk_raw_len, + &oid_index, &oid_length, ¶m_index, ¶m_length, key_index, key_length); + + if (ret == 0) { + /* find the key type from the OID. Use key type to derive + * further attributes from parameter, or update the value + * of the key index to skip unused version field, etc. + */ + if (oid_length == sizeof(ptls_mbedtls_oid_rsa_key) && + memcmp(pk_raw + oid_index, ptls_mbedtls_oid_rsa_key, sizeof(ptls_mbedtls_oid_rsa_key)) == 0) { + /* We recognized RSA */ + psa_set_key_type(attributes, PSA_KEY_TYPE_RSA_PUBLIC_KEY); + if (*key_length > 0 && pk_raw[*key_index] == 0) { + (*key_index)++; + (*key_length)--; + } + } + else if (oid_length == sizeof(ptls_mbedtls_oid_ec_key) && + memcmp(pk_raw + oid_index, ptls_mbedtls_oid_ec_key, sizeof(ptls_mbedtls_oid_ec_key)) == 0) { + /* We recognized ECDSA */ + psa_set_key_type(attributes, PSA_KEY_TYPE_ECC_PUBLIC_KEY(PSA_ECC_FAMILY_SECP_R1)); + if (*key_length > 0 && pk_raw[*key_index] == 0) { + (*key_index)++; + (*key_length)--; + } + } else { + ret = PTLS_ERROR_NOT_AVAILABLE; + } + } + return ret; +} + + const ptls_mbedtls_signature_scheme_t *ptls_mbedtls_select_signature_scheme(const ptls_mbedtls_signature_scheme_t *available, - const uint16_t *algorithms, size_t num_algorithms) + const uint16_t *algorithms, size_t num_algorithms, uint16_t * selected_algorithm) { const ptls_mbedtls_signature_scheme_t *scheme; @@ -359,6 +504,7 @@ const ptls_mbedtls_signature_scheme_t *ptls_mbedtls_select_signature_scheme(cons for (scheme = available; scheme->scheme_id != UINT16_MAX; ++scheme) { for (size_t i = 0; i != num_algorithms; ++i) { if (algorithms[i] == scheme->scheme_id) { + *selected_algorithm = scheme->scheme_id; return scheme; } } @@ -366,82 +512,95 @@ const ptls_mbedtls_signature_scheme_t *ptls_mbedtls_select_signature_scheme(cons return NULL; } -int ptls_mbedtls_set_available_schemes(ptls_mbedtls_sign_certificate_t *signer) +/* Find whether the signature scheme is supported */ +int ptls_mbedtls_set_schemes_from_key_params(psa_algorithm_t key_algo, size_t key_nb_bits, const ptls_mbedtls_signature_scheme_t** schemes) { int ret = 0; - psa_algorithm_t algo = psa_get_key_algorithm(&signer->attributes); - size_t nb_bits = psa_get_key_bits(&signer->attributes); - switch (algo) { + switch (key_algo) { case PSA_ALG_RSA_PKCS1V15_SIGN_RAW: - signer->schemes = rsa_signature_schemes; + *schemes = rsa_signature_schemes; break; case PSA_ALG_DETERMINISTIC_ECDSA(PSA_ALG_SHA_256): - signer->schemes = secp256r1_signature_schemes; + *schemes = secp256r1_signature_schemes; break; case PSA_ALG_DETERMINISTIC_ECDSA(PSA_ALG_SHA_384): - signer->schemes = secp384r1_signature_schemes; + *schemes = secp384r1_signature_schemes; break; case PSA_ALG_DETERMINISTIC_ECDSA(PSA_ALG_SHA_512): - signer->schemes = secp521r1_signature_schemes; + *schemes = secp521r1_signature_schemes; break; case PSA_ALG_ECDSA_BASE: - switch (nb_bits) { + switch (key_nb_bits) { case 521: - signer->schemes = secp521r1_signature_schemes; + *schemes = secp521r1_signature_schemes; break; case 384: - signer->schemes = secp384r1_signature_schemes; + *schemes = secp384r1_signature_schemes; break; case 256: - signer->schemes = secp256r1_signature_schemes; + *schemes = secp256r1_signature_schemes; break; default: - signer->schemes = secp256r1_signature_schemes; + *schemes = secp256r1_signature_schemes; ret = -1; break; } break; +#if 0 case PSA_ALG_ED25519PH: - signer->schemes = ed25519_signature_schemes; + *schemes = ed25519_signature_schemes; break; +#endif default: - printf("Unknown algo: %x\n", algo); + /* printf("Unknown algo: %x\n", key_algo); */ ret = -1; } return ret; } +int ptls_mbedtls_set_available_schemes(ptls_mbedtls_sign_certificate_t *signer) +{ + int ret = 0; + psa_algorithm_t algo = psa_get_key_algorithm(&signer->attributes); + size_t nb_bits = psa_get_key_bits(&signer->attributes); + + ret = ptls_mbedtls_set_schemes_from_key_params(algo, nb_bits, &signer->schemes); + + return ret; +} + + /* - * Sign a certificate - * - step1, selected a signature algorithm compatible with the public key algorithm - * and with the list specified by the application. - * - step2, compute the hash with the specified algorithm. - * - step3, compute the signature of the hash using psa_sign_hash. - * - * In the case of RSA, we use the algorithm PSA_ALG_RSA_PKCS1V15_SIGN_RAW, which - * pads the hash according to PKCS1V15 before doing the private key operation. - * The implementation of RSA/PKCS1V15 also includes a verification step to protect - * against key attacks through partial faults. - * - * MBEDTLS has a "psa_sign_message" that combines step2 and step3. However, it - * requires specifying an algorithm type that exactly specifies the signature - * algorithm, such as "RSA with SHA384". This is not compatible with the - * "RSA sign raw" algorithm. Instead, we decompose the operation in two steps. - * There is no performance penalty doing so, as "psa_sign_message" is only - * a convenience API. - */ +* Sign a certificate +* - step1, selected a signature algorithm compatible with the public key algorithm +* and with the list specified by the application. +* - step2, compute the hash with the specified algorithm. +* - step3, compute the signature of the hash using psa_sign_hash. +* +* In the case of RSA, we use the algorithm PSA_ALG_RSA_PKCS1V15_SIGN_RAW, which +* pads the hash according to PKCS1V15 before doing the private key operation. +* The implementation of RSA/PKCS1V15 also includes a verification step to protect +* against key attacks through partial faults. +* +* MBEDTLS has a "psa_sign_message" that combines step2 and step3. However, it +* requires specifying an algorithm type that exactly specifies the signature +* algorithm, such as "RSA with SHA384". This is not compatible with the +* "RSA sign raw" algorithm. Instead, we decompose the operation in two steps. +* There is no performance penalty doing so, as "psa_sign_message" is only +* a convenience API. +*/ int ptls_mbedtls_sign_certificate(ptls_sign_certificate_t *_self, ptls_t *tls, ptls_async_job_t **async, - uint16_t *selected_algorithm, ptls_buffer_t *outbuf, ptls_iovec_t input, - const uint16_t *algorithms, size_t num_algorithms) + uint16_t *selected_algorithm, ptls_buffer_t *outbuf, ptls_iovec_t input, + const uint16_t *algorithms, size_t num_algorithms) { int ret = 0; ptls_mbedtls_sign_certificate_t *self = (ptls_mbedtls_sign_certificate_t *)(((unsigned char *)_self) - offsetof(struct st_ptls_mbedtls_sign_certificate_t, super)); /* First, find the set of compatible algorithms */ - const ptls_mbedtls_signature_scheme_t *scheme = ptls_mbedtls_select_signature_scheme(self->schemes, algorithms, num_algorithms); + const ptls_mbedtls_signature_scheme_t *scheme = ptls_mbedtls_select_signature_scheme(self->schemes, algorithms, num_algorithms, selected_algorithm); if (scheme == NULL) { ret = PTLS_ERROR_INCOMPATIBLE_KEY; @@ -481,7 +640,7 @@ int ptls_mbedtls_sign_certificate(ptls_sign_certificate_t *_self, ptls_t *tls, p if ((ret = ptls_buffer_reserve(outbuf, nb_bytes)) == 0) { size_t signature_length = 0; if (psa_sign_hash(self->key_id, sign_algo, hash_value, hash_length, outbuf->base + outbuf->off, nb_bytes, - &signature_length) != 0) { + &signature_length) != 0) { ret = PTLS_ERROR_INCOMPATIBLE_KEY; } else { outbuf->off += signature_length; @@ -497,7 +656,7 @@ void ptls_mbedtls_dispose_sign_certificate(ptls_sign_certificate_t *_self) if (_self != NULL) { ptls_mbedtls_sign_certificate_t *self = (ptls_mbedtls_sign_certificate_t *)(((unsigned char *)_self) - - offsetof(struct st_ptls_mbedtls_sign_certificate_t, super)); + offsetof(struct st_ptls_mbedtls_sign_certificate_t, super)); /* Destroy the key */ psa_destroy_key(self->key_id); psa_reset_key_attributes(&self->attributes); @@ -506,23 +665,23 @@ void ptls_mbedtls_dispose_sign_certificate(ptls_sign_certificate_t *_self) } } /* - * An RSa key is encoded in DER as: - * RSAPrivateKey ::= SEQUENCE { - * version INTEGER, -- must be 0 - * modulus INTEGER, -- n - * publicExponent INTEGER, -- e - * privateExponent INTEGER, -- d - * prime1 INTEGER, -- p - * prime2 INTEGER, -- q - * exponent1 INTEGER, -- d mod (p-1) - * exponent2 INTEGER, -- d mod (q-1) - * coefficient INTEGER, -- (inverse of q) mod p - * } - * - * The number of key bits is the size in bits of the integer N. - * We must decode the length in octets of the integer representation, - * then subtract the number of zeros at the beginning of the data. - */ +* An RSa key is encoded in DER as: +* RSAPrivateKey ::= SEQUENCE { +* version INTEGER, -- must be 0 +* modulus INTEGER, -- n +* publicExponent INTEGER, -- e +* privateExponent INTEGER, -- d +* prime1 INTEGER, -- p +* prime2 INTEGER, -- q +* exponent1 INTEGER, -- d mod (p-1) +* exponent2 INTEGER, -- d mod (q-1) +* coefficient INTEGER, -- (inverse of q) mod p +* } +* +* The number of key bits is the size in bits of the integer N. +* We must decode the length in octets of the integer representation, +* then subtract the number of zeros at the beginning of the data. +*/ int ptls_mbedtls_rsa_get_key_bits(const unsigned char *key_value, size_t key_length, size_t *p_nb_bits) { int ret = 0; @@ -598,6 +757,61 @@ int ptls_mbedtls_set_ec_key_attributes(ptls_mbedtls_sign_certificate_t *signer, return ret; } +int ptls_mbedtls_load_file(char const * file_name, unsigned char ** buf, size_t * n) +{ + int ret = 0; + FILE* F = NULL; + *buf = NULL; + *n = 0; +#ifdef _WINDOWS + errno_t err = fopen_s(&F, file_name, "rb"); + if (err != 0){ + if (F != NULL) { + fclose(F); + F = NULL; + } + } +#else + F = fopen(file_name, "rb"); +#endif + + if (F == NULL) { + ret = PTLS_ERROR_NOT_AVAILABLE; + } else { + long sz; + fseek(F, 0, SEEK_END); + sz = ftell(F); + + if (sz > 0) { + *buf = (unsigned char *)malloc(sz+1); + if (*buf == NULL){ + ret = PTLS_ERROR_NO_MEMORY; + } + else { + size_t nb_read = 0; + fseek(F, 0, SEEK_SET); + while(nb_read < (size_t)sz){ + *n = sz; + size_t ret = fread((*buf) + nb_read, 1, sz - nb_read, F); + if (ret > 0){ + nb_read += ret; + (*buf)[nb_read] = 0; + } else { + /* No need to check for EOF, since we know the length of the file */ + ret = PTLS_ERROR_NOT_AVAILABLE; + free(*buf); + *buf = NULL; + *n = 0; + break; + } + } + } + (void)fclose(F); + } + } + return ret; +} + int ptls_mbedtls_load_private_key(ptls_context_t *ctx, char const *pem_fname) { int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; @@ -616,12 +830,8 @@ int ptls_mbedtls_load_private_key(ptls_context_t *ctx, char const *pem_fname) memset(signer, 0, sizeof(ptls_mbedtls_sign_certificate_t)); signer->attributes = psa_key_attributes_init(); - if ((ret = mbedtls_pk_load_file(pem_fname, &buf, &n)) != 0) { - if (ret == MBEDTLS_ERR_PK_ALLOC_FAILED) { - return (PTLS_ERROR_NO_MEMORY); - } else { - return (PTLS_ERROR_NOT_AVAILABLE); - } + if ((ret = ptls_mbedtls_load_file(pem_fname, &buf, &n)) != 0) { + return ret; } ret = ptls_mbedtls_get_der_key(&pem, &pk_type, buf, n, NULL, 0, NULL, NULL); @@ -642,14 +852,12 @@ int ptls_mbedtls_load_private_key(ptls_context_t *ctx, char const *pem_fname) ret = ptls_mbedtls_set_ec_key_attributes(signer, key_length); } } else if (pk_type == MBEDTLS_PK_NONE) { - /* TODO: not clear whether MBDED TLS supports ED25519 yet. Probably not. */ - /* Should have option to encode RSA or ECDSA using PKCS8 */ size_t oid_index = 0; size_t oid_length = 0; psa_set_key_usage_flags(&signer->attributes, PSA_KEY_USAGE_SIGN_HASH); ret = - test_parse_private_key_field(pem.private_buf, pem.private_buflen, &oid_index, &oid_length, &key_index, &key_length); + ptls_parse_private_key_field(pem.private_buf, pem.private_buflen, &oid_index, &oid_length, &key_index, &key_length); if (ret == 0) { /* need to parse the OID in order to set the parameters */ @@ -659,18 +867,22 @@ int ptls_mbedtls_load_private_key(ptls_context_t *ctx, char const *pem_fname) if (ret == 0) { ret = ptls_mbedtls_set_ec_key_attributes(signer, key_length); } +#if 0 + /* Commenting out as MbedTLS does not support 25519 yet */ } else if (oid_length == sizeof(ptls_mbedtls_oid_ed25519) && - memcmp(pem.private_buf + oid_index, ptls_mbedtls_oid_ed25519, sizeof(ptls_mbedtls_oid_ed25519)) == 0) { + memcmp(pem.private_buf + oid_index, ptls_mbedtls_oid_ed25519, sizeof(ptls_mbedtls_oid_ed25519)) == 0) { + /* This code looks correct, but EDDSA is not supported yet by MbedTLS, + * and attempts to import the key will result in an error, so commenting out for now. */ /* We recognized ED25519 -- PSA_ECC_FAMILY_TWISTED_EDWARDS -- PSA_ALG_ED25519PH */ - psa_set_key_algorithm(&signer->attributes, PSA_ALG_PURE_EDDSA); + psa_set_key_algorithm(&signer->attributes, PSA_ALG_ED25519PH); psa_set_key_type(&signer->attributes, PSA_ECC_FAMILY_TWISTED_EDWARDS); ret = ptls_mbedtls_parse_eddsa_key(pem.private_buf, pem.private_buflen, &key_index, &key_length); psa_set_key_bits(&signer->attributes, 256); +#endif } else if (oid_length == sizeof(ptls_mbedtls_oid_rsa_key) && - memcmp(pem.private_buf + oid_index, ptls_mbedtls_oid_rsa_key, sizeof(ptls_mbedtls_oid_rsa_key)) == 0) { + memcmp(pem.private_buf + oid_index, ptls_mbedtls_oid_rsa_key, sizeof(ptls_mbedtls_oid_rsa_key)) == 0) { /* We recognized RSA */ - key_length = pem.private_buflen; - ptls_mbedtls_set_rsa_key_attributes(signer, pem.private_buf, key_length); + ptls_mbedtls_set_rsa_key_attributes(signer, pem.private_buf + key_index, key_length); } else { ret = PTLS_ERROR_NOT_AVAILABLE; } @@ -701,3 +913,494 @@ int ptls_mbedtls_load_private_key(ptls_context_t *ctx, char const *pem_fname) } return ret; } + +/* Handling of certificates. +* Certificates in picotls are used both at the client and the server side. +* +* The server is programmed with a copy of the certificate chain linking +* the local key and identity to a certificate authority. Picotls formats +* that key and sends it as part of the "server hello". It is signed with +* the server key. +* +* The client is programmed with a list of trusted certificates. It should +* process the list received from the server and verifies that it does +* correctly link the server certificate to one of the certificates in the +* root list. +* +* Mbedtls documents a series of certificate related API in `x509_crt.h`. +* +* On the server side, we read the certificates from a PEM encoded +* file, and provide it to the server. +* +* For verify certificate, picotls uses a two phase API: +* +* - During initialization, prepare a "verify certificate callback" +* - During the handshake, picotls executes the callback. +* +* Picotls verifies certificates using the "verify_certificate" callback. +* +* if ((ret = tls->ctx->verify_certificate->cb(tls->ctx->verify_certificate, +* tls, server_name, &tls->certificate_verify.cb, +* &tls->certificate_verify.verify_ctx, certs, num_certs)) != 0) +* goto Exit; +* +* This is implemented using the function mbedtls_verify_certificate, +* documented during the initialization of the "cb" structure, +* ptls_mbedtls_verify_certificate_t. The function pointer is +* the first member of that structure, followed by other arguments. +* The callback structure is passed as the first argument in the +* callback, with type "self". +* +* The callback should return 0 if the certificate is good. The call may +* also set the value of tls->certificate_verify.cb and +* tls->certificate_verify.verify_ctx. If these are set, picotls will +* then use tls->certificate_verify.cb to verify that the TLS messages +* are properly signed using that certificate. +* +* The function mbedtls_verify_certificate is implemented using the +* function "mbedtls_x509_crt_verify", which has the following arguments: +* +* - A chain of certificates, starting from the server certificate and hopefully +* going all the way to one of the root certificates. In our code, this +* is obtained by parsing the "certs" argument provided by picotls, which +* is an iovec vector of length numcerts, with one entry per certificate in +* the CERTS parameter received from the server. +* - The chain of trusted certificate authorities. In our case, that list is +* initialized during the call to `ptls_mbedssl_init_verify_certificate`, +* loading certificates from a `root` file. +* - A certificate revocation list. We leave that parameter NULL for now. +* - The expected server name, a NULL terminated string. +* - A "verify" function pointer, and its argument. +* +* The call returns 0 (and flags set to 0) if the chain was verified and valid, +* MBEDTLS_ERR_X509_CERT_VERIFY_FAILED if the chain was verified but found to +* be invalid, in which case *flags will have one or more MBEDTLS_X509_BADCERT_XXX +* or MBEDTLS_X509_BADCRL_XXX flags set, or another error +* (and flags set to 0xffffffff) in case of a fatal error encountered +* during the verification process. +* +* The verify callback is a user-supplied callback that can clear / modify / add +* flags for a certificate. If set, the verification callback is called for each +* certificate in the chain (from the trust-ca down to the presented crt). +* The parameters for the callback are: (void *parameter, mbedtls_x509_crt *crt, +* int certificate_depth, int *flags). With the flags representing current flags +* for that specific certificate and the certificate depth from the bottom +* (Peer cert depth = 0). Function pointer and parameters can be set in the call +* to `ptls_mbedssl_init_verify_certificate`. +* +* If the certificate verification is successfull, the code sets the pointer +* and the context for the certificate_verify callback: +* +* struct { +* int (*cb)(void *verify_ctx, uint16_t algo, ptls_iovec_t data, ptls_iovec_t signature); +* void *verify_ctx; +* } certificate_verify; +* +* The structure "certificate_verify" is allocated as part of the PTLS context. We will +* allcoate a a "ptls_mbetls_certificate_verify_ctx_t" ctx as part of the +* +* The verify callback is implemented using `psa_verify_message`, which takes the following +* arguments: +* +* psa_status_t psa_verify_message(psa_key_id_t key, +* psa_algorithm_t alg, +* const uint8_t * input, +* size_t input_length, +* const uint8_t * signature, +* size_t signature_length); +* +* The public key ID will be set from the certificate proposed by the server. The +* input and length of the data to be signed are derived from the data parameter +* in the callback, and the signature and length from the signature parameter of +* the callback. The "alg" parameter of type "psa_algorithm_t" will have to +* be derived from the algo parameter of the callback, which is a 16 bit +* "signature scheme" (see +* https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-signaturescheme). +* +* Picotls will use that callback exactly once, then reset the callback +* pointer to NULL. It does not reset of free the "verify_ctx" -- if necessary, +* the value there should be reset after the first call. +*/ + +typedef struct st_mbedtls_message_verify_ctx_t { + psa_key_id_t key_id; +} mbedtls_message_verify_ctx_t; + +uint16_t mbedtls_verify_sign_algos[] = { + 0x0201, 0x0203, 0x0401, 0x0403, 0x501, 0x0503, 0x0601, 0x0603, + 0x0804, 0x0805, 0x0806, + 0xFFFF +}; + +/* Find the psa_algorithm_t values corresponding to the 16 bit TLS signature scheme */ +psa_algorithm_t mbedtls_get_psa_alg_from_tls_number(uint16_t tls_algo) +{ + psa_algorithm_t alg = PSA_ALG_NONE; + switch (tls_algo) { + case 0x0201: /* PTLS_SIGNATURE_RSA_PKCS1_SHA1 */ + alg = PSA_ALG_RSA_PKCS1V15_SIGN(PSA_ALG_SHA_1); + break; + case 0x0203: /* ecdsa_sha1 */ + alg = PSA_ALG_ECDSA(PSA_ALG_SHA_1); + break; + case 0x401: /* PTLS_SIGNATURE_RSA_PKCS1_SHA256 */ + alg = PSA_ALG_RSA_PKCS1V15_SIGN(PSA_ALG_SHA_256); + break; + case 0x0403: /* PTLS_SIGNATURE_ECDSA_SECP256R1_SHA256 */ + alg = PSA_ALG_ECDSA(PSA_ALG_SHA_256); + break; +#if 0 + /* For further study. These two algorithms might be available in MbedTLS */ + case 0x0420: /* rsa_pkcs1_sha256_legacy */ + break; + case 0x0520: /* rsa_pkcs1_sha384_legacy */ + break; +#endif + case 0x501: /* rsa_pkcs1_sha384 */ + alg = PSA_ALG_RSA_PKCS1V15_SIGN(PSA_ALG_SHA_384); + break; + case 0x0503: /* PTLS_SIGNATURE_ECDSA_SECP384R1_SHA384 */ + alg = PSA_ALG_ECDSA(PSA_ALG_SHA_384); + break; + case 0x0601: /* rsa_pkcs1_sha512 */ + alg = PSA_ALG_RSA_PKCS1V15_SIGN(PSA_ALG_SHA_512); + break; + case 0x0603: /* PTLS_SIGNATURE_ECDSA_SECP521R1_SHA512 */ + alg = PSA_ALG_ECDSA(PSA_ALG_SHA_512); + break; + case 0x0804: /* PTLS_SIGNATURE_RSA_PSS_RSAE_SHA256 */ + alg = PSA_ALG_RSA_PSS(PSA_ALG_SHA_256); + break; + case 0x0805: /* PTLS_SIGNATURE_RSA_PSS_RSAE_SHA384 */ + alg = PSA_ALG_RSA_PSS(PSA_ALG_SHA_384); + break; + case 0x0806: /* PTLS_SIGNATURE_RSA_PSS_RSAE_SHA512 */ + alg = PSA_ALG_RSA_PSS(PSA_ALG_SHA_512); + break; +#if 0 + /* Commented out, as EDDSA is not supported yet in MbedTLS*/ + case 0x0807: /* PTLS_SIGNATURE_ED25519 */ + alg = PSA_ALG_ED25519PH; + break; + case 0x0808: /* PTLS_SIGNATURE_ED448 */ + alg = PSA_ALG_ED448PH; + break; +#endif + default: + break; + } + + return alg; +} + +int mbedtls_verify_sign(void *verify_ctx, uint16_t algo, ptls_iovec_t data, ptls_iovec_t signature) +{ + /* Obtain the key parameters, etc. */ + int ret = 0; + psa_algorithm_t alg = PSA_ALG_NONE; + mbedtls_message_verify_ctx_t * message_verify_ctx = (mbedtls_message_verify_ctx_t*)verify_ctx; + + if (message_verify_ctx == NULL) { + ret = PTLS_ERROR_LIBRARY; + } + else if (data.base != NULL) { + /* Picotls will call verify_sign with data.base == NULL when it + * only wants to clear the memory. This is not an error condition. */ + + /* Find the PSA_ALG for the signature scheme */ + alg = mbedtls_get_psa_alg_from_tls_number(algo); + + if (alg == PSA_ALG_NONE) { + ret = PTLS_ALERT_ILLEGAL_PARAMETER; + } + } + else { + psa_status_t status = psa_verify_message(message_verify_ctx->key_id, alg, data.base, data.len, signature.base, signature.len); + + if (status != PSA_SUCCESS) { + switch (status) { + case PSA_ERROR_NOT_PERMITTED: /* The key does not have the PSA_KEY_USAGE_SIGN_MESSAGE flag, or it does not permit the requested algorithm. */ + ret = PTLS_ERROR_INCOMPATIBLE_KEY; + break; + case PSA_ERROR_INVALID_SIGNATURE: /* The calculation was performed successfully, but the passed signature is not a valid signature. */ + ret = PTLS_ALERT_DECRYPT_ERROR; + break; + case PSA_ERROR_NOT_SUPPORTED: + ret = PTLS_ALERT_ILLEGAL_PARAMETER; + break; + case PSA_ERROR_INSUFFICIENT_MEMORY: + ret = PTLS_ERROR_NO_MEMORY; + break; + default: + ret = PTLS_ERROR_LIBRARY; + break; + } + } + } + /* destroy the key because it is used only once. */ + if (message_verify_ctx != NULL) { + psa_destroy_key(message_verify_ctx->key_id); + free(message_verify_ctx); + } + return ret; +} + +static int mbedtls_verify_certificate(ptls_verify_certificate_t *_self, ptls_t *tls, const char *server_name, + int (**verifier)(void *, uint16_t, ptls_iovec_t, ptls_iovec_t), void **verify_data, ptls_iovec_t *certs, + size_t num_certs) +{ + size_t i; + int ret = 0; + mbedtls_x509_crt* chain_head = (mbedtls_x509_crt*)malloc(sizeof(mbedtls_x509_crt)); + + if (chain_head == NULL) { + ret = PTLS_ERROR_NO_MEMORY; + } + else { + ptls_mbedtls_verify_certificate_t* self = (ptls_mbedtls_verify_certificate_t*)_self; + *verifier = NULL; + *verify_data = NULL; + mbedtls_x509_crt_init(chain_head); + + /* If any certs are given, convert them to MbedTLS representation, then verify the cert chain. If no certs are given, just give + * the override_callback to see if we want to stay fail open. */ + if (num_certs == 0) { + ret = PTLS_ALERT_CERTIFICATE_REQUIRED; + } + else { + mbedtls_x509_crt* previous_chain = chain_head; + mbedtls_x509_crt_init(chain_head); + + for (i = 0; i != num_certs; ++i) { + ret = mbedtls_x509_crt_parse_der(previous_chain, certs[i].base, certs[i].len); + if (i != 0) { + if (previous_chain->next == NULL) { + ret = PTLS_ALERT_BAD_CERTIFICATE; + break; + } + previous_chain = previous_chain->next; + } + } + + if (ret == 0) { + uint32_t flags = 0; + + int verify_ret = mbedtls_x509_crt_verify(chain_head, self->trust_ca, self->trust_crl, server_name, &flags, + self->f_vrfy, self->p_vrfy); + + if (verify_ret != 0) { + switch (verify_ret) { + case MBEDTLS_ERR_X509_CERT_VERIFY_FAILED: + /* if the chain was verified but found to be invalid, in which case + * flags will have one or more MBEDTLS_X509_BADCERT_XXX + * or MBEDTLS_X509_BADCRL_XXX flags set, or another + * error(and flags set to 0xffffffff) in case of a fatal error + * encountered during the verification process. */ + ret = PTLS_ALERT_BAD_CERTIFICATE; + break; + default: + ret = PTLS_ERROR_LIBRARY; + break; + } + } + } + + if (ret == 0) { + mbedtls_message_verify_ctx_t* message_verify_ctx = (mbedtls_message_verify_ctx_t*) + malloc(sizeof(mbedtls_message_verify_ctx_t)); + if (message_verify_ctx == NULL) { + ret = PTLS_ERROR_NO_MEMORY; + } + else { +#if 1 + /* Obtain the key bits from the certificate */ + size_t key_index; + size_t key_length; + psa_key_attributes_t attributes = psa_key_attributes_init(); + + ret = ptls_mbedtls_get_public_key_info(chain_head->pk_raw.p, chain_head->pk_raw.len, + &attributes, &key_index, &key_length); + + if (ret == 0) { + if (psa_import_key(&attributes, chain_head->pk_raw.p + key_index, key_length, &message_verify_ctx->key_id) != 0) { + ret = PTLS_ERROR_LIBRARY; + } + } +#else + psa_status_t status = 0; + psa_key_attributes_t attributes; + memset(&attributes, 0, sizeof(attributes)); + memset(message_verify_ctx, 0, sizeof(mbedtls_message_verify_ctx_t)); + + status = mbedtls_pk_get_psa_attributes(&chain_head->pk, PSA_KEY_USAGE_VERIFY_MESSAGE, &attributes); + if (status == PSA_SUCCESS) { + status = mbedtls_pk_import_into_psa(&chain_head->pk, &attributes, &message_verify_ctx->key_id); + } + if (status != PSA_SUCCESS) { + ret = PTLS_ERROR_LIBRARY; + } +#endif + if (ret != 0) { + free(message_verify_ctx); + } + else { + *verifier = mbedtls_verify_sign; + *verify_data = message_verify_ctx; + } + } + } + } + } + + if (chain_head != NULL) { + mbedtls_x509_crt_free(chain_head); + } + return ret; +} + +/* Read certificates from a file using MbedTLS functions. +* We only use the PEM function to parse PEM files, find +* up to 16 certificates, and convert the base64 encoded +* data to DER encoded binary. No attempt is made to verify +* that these actually are certificates. +* +* Discuss: picotls has a built in function for this. +* Is it really necessary to program an alternative? +*/ +int picoquic_mbedtls_get_certs_from_file(char const * pem_fname, ptls_iovec_t** pvec, size_t * count) +{ + int ret = 0; + *pvec = (ptls_iovec_t*)malloc(sizeof(ptls_iovec_t) * 16); + + *count = 0; + if (*pvec == NULL) { + ret = PTLS_ERROR_NO_MEMORY; + } else { + size_t buf_length; + unsigned char* buf = NULL; + /* The load file function simply loads the file content in memory */ + if ((ret = ptls_mbedtls_load_file(pem_fname, &buf, &buf_length)) == 0) { + size_t length_already_read = 0; + + while (ret == 0 && *count < 16 && length_already_read < (size_t)buf_length) { + mbedtls_pem_context pem = { 0 }; + size_t length_read = 0; + + /* PEM context setup. */ + mbedtls_pem_init(&pem); + /* Read a buffer for PEM information and store the resulting data into the specified context buffers. */ + ret = mbedtls_pem_read_buffer(&pem, + "-----BEGIN CERTIFICATE-----", + "-----END CERTIFICATE-----", + buf + length_already_read, NULL, 0, &length_read); + if (ret == 0) { + /* Certificate was read successfully. PEM buffer contains the base64 value */ + uint8_t* cert = (uint8_t*)malloc(pem.private_buflen); + if (cert == NULL) { + ret = PTLS_ERROR_NO_MEMORY; + } + else { + memcpy(cert, pem.private_buf, pem.private_buflen); + (*pvec)[*count].base = cert; + (*pvec)[*count].len = pem.private_buflen; + *count += 1; + } + } + mbedtls_pem_free(&pem); + length_already_read += length_read; + } + + free(buf); + } + } + return ret; +} + +int ptls_mbedtls_load_certificates(ptls_context_t *ctx, char const *cert_pem_file) +{ + return picoquic_mbedtls_get_certs_from_file(cert_pem_file, &ctx->certificates.list, + &ctx->certificates.count); +} + +/* Creating the call back. This API is not described by picotls. The "backend" +* merely has to provide a "certicate verifier" callback. We consider two ways of +* providing this callback: an API very close to the details of the MBedTLS code, +* with a list of explicit parameters, and a "portable" API whose only +* parameter is a file name for the list of trusted certificates. +*/ + +int ptls_mbedssl_init_verify_certificate_complete(ptls_context_t * ptls_ctx, + mbedtls_x509_crt* trust_ca, mbedtls_x509_crl* trust_crl, + int (*f_vrfy)(void*, mbedtls_x509_crt*, int, uint32_t*), void* p_vrfy) +{ + int ret = 0; + ptls_mbedtls_verify_certificate_t* verifier = + (ptls_mbedtls_verify_certificate_t*)malloc(sizeof(ptls_mbedtls_verify_certificate_t)); + if (verifier == NULL) { + ret = PTLS_ERROR_NO_MEMORY; + } + else { + memset(verifier, 0, sizeof(ptls_mbedtls_verify_certificate_t)); + verifier->super.cb = mbedtls_verify_certificate; + verifier->super.algos = mbedtls_verify_sign_algos; /* list of supported algorithms, end with 0xFFFF */ + verifier->trust_ca = trust_ca; + verifier->trust_crl = trust_crl; + verifier->f_vrfy = f_vrfy; + verifier->p_vrfy = p_vrfy; + ptls_ctx->verify_certificate = &verifier->super; + } + return ret; +} + +int ptls_mbedtls_init_verify_certificate(ptls_context_t* ptls_ctx, char const* pem_fname) +{ + int ret = 0; + mbedtls_x509_crt* chain_head = (mbedtls_x509_crt*)malloc(sizeof(mbedtls_x509_crt)); + + if (chain_head == NULL) { + ret = PTLS_ERROR_NO_MEMORY; + } + else { + int psa_ret; + mbedtls_x509_crt_init(chain_head); + + psa_ret = mbedtls_x509_crt_parse_file(chain_head, pem_fname); + if (psa_ret == 0) { + ret = ptls_mbedssl_init_verify_certificate_complete(ptls_ctx, + chain_head, NULL, NULL, NULL); + } + else if (psa_ret > 0) { + /* some of the certificates could not parsed */ + ret = PTLS_ALERT_BAD_CERTIFICATE; + } + else if (psa_ret == PSA_ERROR_INSUFFICIENT_MEMORY) { + ret = PTLS_ERROR_NO_MEMORY; + } + else { + ret = PTLS_ERROR_LIBRARY; + } + + if (ret != 0 && chain_head != NULL) { + mbedtls_x509_crt_free(chain_head); + } + } + return ret; +} + +void ptls_mbedtls_dispose_verify_certificate(ptls_context_t* ptls_ctx) +{ + ptls_mbedtls_verify_certificate_t* verifier = + (ptls_mbedtls_verify_certificate_t*)ptls_ctx->verify_certificate; + if (verifier != NULL) { + if (verifier->trust_ca != NULL) { + mbedtls_x509_crt_free(verifier->trust_ca); + verifier->trust_ca = NULL; + } + if (verifier->trust_crl != NULL) { + mbedtls_x509_crl_free(verifier->trust_crl); + } + memset(verifier, 0, sizeof(ptls_mbedtls_verify_certificate_t)); + free(verifier); + ptls_ctx->verify_certificate = NULL; + } +} \ No newline at end of file diff --git a/t/mbedtls.c b/t/mbedtls.c index 1ea3416bf..feea42e52 100644 --- a/t/mbedtls.c +++ b/t/mbedtls.c @@ -33,6 +33,12 @@ #include "picotls/minicrypto.h" #include "../deps/picotest/picotest.h" #include "test.h" +#include "mbedtls/build_info.h" +#include "picotls/minicrypto.h" +#include "mbedtls/pk.h" +#include "mbedtls/pem.h" +#include "mbedtls/x509_crt.h" +#include "mbedtls/error.h" typedef struct st_ptls_mbedtls_signature_scheme_t { uint16_t scheme_id; @@ -133,36 +139,107 @@ Output buffer is already partially filled. #define ASSET_SECP384R1_KEY "t/assets/secp384r1/key.pem" #define ASSET_SECP521R1_KEY "t/assets/secp521r1/key.pem" #define ASSET_SECP256R1_PKCS8_KEY "t/assets/secp256r1-pkcs8/key.pem" +#define ASSET_ED25519_KEY "t/assets/ed25519/key.pem" +#define ASSET_NO_SUCH_FILE "t/assets/no_such_file.pem" +#define ASSET_NOT_A_PEM_FILE "t/assets/not_a_valid_pem_file.pem" +#define ASSET_RSA_CERT "t/assets/rsa/cert.pem" +#define ASSET_SECP256R1_CERT "t/assets/secp256r1/cert.pem" +#define ASSET_SECP384R1_CERT "t/assets/secp384r1/cert.pem" +#define ASSET_SECP521R1_CERT "t/assets/secp521r1/cert.pem" +#define ASSET_SECP256R1_PKCS8_CERT "t/assets/secp256r1-pkcs8/cert.pem" +#define ASSET_ED25519_CERT "t/assets/ed25519/cert.pem" +#define ASSET_TEST_CA "t/assets/test-ca.crt" + +#define ASSET_RSA_NAME "rsa.test.example.com" +#define ASSET_RSA_PKCS8_NAME "rsa.test.example.com" +#define ASSET_SECP256R1_NAME "test.example.com" +#define ASSET_SECP384R1_NAME "secp384r1.test.example.com" +#define ASSET_SECP521R1_NAME "secp521r1.test.example.com" +#define ASSET_SECP256R1_PKCS8_NAME "test.example.com" + +int test_load_one_file(char const* path) +{ + size_t n; + unsigned char *buf; + int ret = ptls_mbedtls_load_file(path, &buf, &n); + if (ret != 0) { + printf("Cannot load file from: %s, ret = %d (0x%x, -0x%x)\n", path, ret, ret, (int16_t)-ret); + } + else if (n == 0) { + printf("File %s is empty\n", path); + ret = -1; + } + else if (buf[n] != 0) { + printf("Buffer from %s is not null terminated\n", path); + ret = -1; + } + if (buf != NULL) { + free(buf); + } + return ret; +} + +static void test_load_file_key() +{ + int ret = test_load_one_file(ASSET_RSA_KEY); + if (ret != 0) { + ok(!"fail"); + return; + } + ok(!!"success"); +} -int test_load_one_der_key(char const *path) +static void test_load_file_cert() +{ + int ret = test_load_one_file(ASSET_SECP256R1_PKCS8_CERT); + if (ret != 0) { + ok(!"fail"); + return; + } + ok(!!"success"); +} + +void test_load_file() +{ + subtest("load file key", test_load_file_key); + subtest("load file cert", test_load_file_cert); +} + +typedef struct st_mbedtls_message_verify_ctx_t { + psa_key_id_t key_id; +} mbedtls_message_verify_ctx_t; + +int mbedtls_verify_sign(void* verify_ctx, uint16_t algo, ptls_iovec_t data, ptls_iovec_t signature); +psa_algorithm_t mbedtls_get_psa_alg_from_tls_number(uint16_t tls_algo); + +int test_load_one_key(char const *path, int expect_failure) { int ret = -1; - unsigned char hash[32]; - const unsigned char h0[32] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + unsigned char test_message[32] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32}; ptls_context_t ctx = {0}; ret = ptls_mbedtls_load_private_key(&ctx, path); if (ret != 0) { - printf("Cannot create sign_certificate from: %s\n", path); + /* Cannot create sign_certificate */ + ok(ret == 0 || expect_failure); ret = -1; } else if (ctx.sign_certificate == NULL) { - printf("Sign_certificate not set in ptls context for: %s\n", path); + /* Sign_certificate not set in ptls context */ + ok(ctx.sign_certificate != NULL || expect_failure); ret = -1; } else { /* Try to sign something */ - int ret; ptls_mbedtls_sign_certificate_t *signer = (ptls_mbedtls_sign_certificate_t *)(((unsigned char *)ctx.sign_certificate) - offsetof(struct st_ptls_mbedtls_sign_certificate_t, super)); /* get the key algorithm */ ptls_buffer_t outbuf; uint8_t outbuf_smallbuf[256]; - ptls_iovec_t input = {hash, sizeof(hash)}; + ptls_iovec_t input = {test_message, sizeof(test_message)}; uint16_t selected_algorithm = 0; int num_algorithms = 0; uint16_t algorithms[16]; - memcpy(hash, h0, 32); while (signer->schemes[num_algorithms].scheme_id != UINT16_MAX && num_algorithms < 16) { algorithms[num_algorithms] = signer->schemes[num_algorithms].scheme_id; num_algorithms++; @@ -172,90 +249,472 @@ int test_load_one_der_key(char const *path) ret = ptls_mbedtls_sign_certificate(ctx.sign_certificate, NULL, NULL, &selected_algorithm, &outbuf, input, algorithms, num_algorithms); + ok(ret == 0 || expect_failure); + if (ret == 0) { - printf("Signed a message, key: %s, scheme: %x, signature size: %zu\n", path, selected_algorithm, outbuf.off); - } else { - printf("Sign failed, key: %s, scheme: %x, signature size: %zu\n", path, selected_algorithm, outbuf.off); + /* Create a verifier context and attempt to check the key */ + ptls_iovec_t sig; + uint8_t pubkey_data[1024]; + size_t pubkey_len = 0; + psa_status_t psa_status; + psa_key_attributes_t public_attributes = psa_key_attributes_init(); + + if ((psa_status = psa_export_public_key(signer->key_id, pubkey_data, sizeof(pubkey_data), &pubkey_len)) != 0) { + /* Cannot export public key */ + ret = -1; + ok(ret == 0 || expect_failure); + } + if (ret == 0) { + switch (psa_get_key_type(&signer->attributes)) { + case PSA_KEY_TYPE_RSA_KEY_PAIR: + psa_set_key_type(&public_attributes, PSA_KEY_TYPE_RSA_PUBLIC_KEY); + break; + case PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1): + psa_set_key_type(&public_attributes, PSA_KEY_TYPE_ECC_PUBLIC_KEY(PSA_ECC_FAMILY_SECP_R1)); + break; + default: + /* TODO: add ED25519 when supported by MbedTLS */ + /* Cannot derive public key from key type */ + ret = -1; + break; + } + } + ok(ret == 0 || expect_failure); + if (ret == 0) { + mbedtls_message_verify_ctx_t* verify_ctx = (mbedtls_message_verify_ctx_t*)malloc(sizeof(mbedtls_message_verify_ctx_t)); + if (verify_ctx == NULL) { + ok(verify_ctx != NULL || expect_failure); + ret = -1; + } + else { + psa_algorithm_t sign_alg = mbedtls_get_psa_alg_from_tls_number(selected_algorithm); + psa_set_key_usage_flags(&public_attributes, PSA_KEY_USAGE_VERIFY_MESSAGE | PSA_KEY_USAGE_VERIFY_HASH); + psa_set_key_algorithm(&public_attributes, sign_alg); + + if ((psa_status = psa_import_key(&public_attributes, pubkey_data, pubkey_len, &verify_ctx->key_id)) != 0) { + /* Cannot import public key */ + ok(psa_status == 0 || expect_failure); + free(verify_ctx); + ret = -1; + } + else { + sig.base = outbuf.base; + sig.len = outbuf.off; + + ret = mbedtls_verify_sign(verify_ctx, selected_algorithm, input, sig); + ok(ret == 0 || expect_failure); + } + } + } } + ptls_buffer_dispose(&outbuf); ptls_mbedtls_dispose_sign_certificate(&signer->super); } + if (expect_failure){ + ok(ret != 0); + if (ret == 0) { + ret = -1; + } + else { + ret = 0; + } + } + ok(ret == 0); return ret; } -static void test_load_rsa_key() +void test_load_keys(void) { - int ret = test_load_one_der_key(ASSET_RSA_KEY); + subtest("load rsa key", test_load_one_key, ASSET_RSA_KEY, 0); + subtest("load secp256r1 key", test_load_one_key, ASSET_SECP256R1_KEY, 0); + subtest("load secp384r1 key", test_load_one_key, ASSET_SECP384R1_KEY, 0); + subtest("load secp521r1 key", test_load_one_key, ASSET_SECP521R1_KEY, 0); + subtest("load secp521r1-pkcs8 key", test_load_one_key, ASSET_RSA_PKCS8_KEY, 0); + subtest("load rsa-pkcs8 key", test_load_one_key, ASSET_RSA_PKCS8_KEY, 0); + + /* add tests for failure modes, including EDDSA which is not supported */ + + subtest("load key no such file", test_load_one_key, ASSET_NO_SUCH_FILE, 1); + subtest("load key not a PEM file", test_load_one_key, ASSET_NOT_A_PEM_FILE, 1); + subtest("load key not a key file", test_load_one_key, ASSET_RSA_CERT, 1); + subtest("load key not supported", test_load_one_key, ASSET_ED25519_KEY, 1); +} - if (ret != 0) { - ok(!"fail"); - return; +/* testing of public key export. +* The API to export a public key directly from the certificate is not present +* in older versions of MbedTLS, which might be installed by default in +* old versions of operating systems. Instead, we develop a robust way to +* export the key bits from the "raw public key" bytes in the certificate. +* But we need to test that this work properly, and we do that by +* comparing to the export of key bits from the private key, because for +* these tests we know the private key. +*/ +int ptls_mbedtls_get_public_key_info(const unsigned char* pk_raw, size_t pk_raw_len, + psa_key_attributes_t* attributes, + size_t* key_index, size_t* key_length); + +static void test_retrieve_pubkey_one(char const* key_path, char const* cert_path) +{ + int ret = 0; + ptls_context_t ctx = { 0 }; + mbedtls_x509_crt* chain_head = (mbedtls_x509_crt*)malloc(sizeof(mbedtls_x509_crt)); + uint8_t pubkey_ref[1024]; + size_t pubkey_ref_len = 0; + + /* Preparation: load the certificate and the private key */ + if (chain_head == NULL) { + ret = PTLS_ERROR_NO_MEMORY; + ok(ret == 0); } - ok(!!"success"); + if (ret == 0) { + mbedtls_x509_crt_init(chain_head); + + if (mbedtls_x509_crt_parse_file(chain_head, cert_path) != 0) { + ret = -1; + ok(ret == 0); + } + } + if (ret == 0) { + ret = ptls_mbedtls_load_private_key(&ctx, key_path); + if (ret != 0) { + ok(ret == 0); + } + } + /* Export the pubkey bits from the private key, for reference */ + if (ret == 0) { + ptls_mbedtls_sign_certificate_t* signer = (ptls_mbedtls_sign_certificate_t*) + (((unsigned char*)ctx.sign_certificate) - offsetof(struct st_ptls_mbedtls_sign_certificate_t, super)); + if (psa_export_public_key(signer->key_id, pubkey_ref, sizeof(pubkey_ref), &pubkey_ref_len) != 0) { + ret = -1; + ok(ret == 0); + } + } + /* Obtain the key bits from the certificate */ + if (ret == 0) { + uint8_t * pk_raw = chain_head->pk_raw.p; + size_t pk_raw_len = chain_head->pk_raw.len; + size_t key_index; + size_t key_length; + psa_key_attributes_t attributes = psa_key_attributes_init(); + + ret = ptls_mbedtls_get_public_key_info(pk_raw, pk_raw_len, + &attributes, &key_index, &key_length); + + if (ret == 0) { + /* Compare key bits */ + if (pubkey_ref_len != key_length || + memcmp(pubkey_ref, chain_head->pk_raw.p + key_index, key_length) != 0) { + ok(ret == 0); + } + } + else { + ok(ret == 0); + } + } + + /* Clean up */ + if (ctx.sign_certificate != NULL) { + ptls_mbedtls_dispose_sign_certificate(ctx.sign_certificate); + } + if (chain_head != NULL) { + mbedtls_x509_crt_free(chain_head); + } + ok(ret == 0); } -static void test_load_secp256r1_key() +static void test_retrieve_pubkey() { - int ret = test_load_one_der_key(ASSET_SECP256R1_KEY); - if (ret != 0) { - ok(!"fail"); - return; + subtest("retrieve pubkey RSA", test_retrieve_pubkey_one, ASSET_RSA_KEY, ASSET_RSA_CERT); + subtest("retrieve pubkey secp256r1", test_retrieve_pubkey_one, ASSET_SECP256R1_KEY, ASSET_SECP256R1_CERT); + subtest("retrieve pubkey secp384r1", test_retrieve_pubkey_one, ASSET_SECP384R1_KEY, ASSET_SECP384R1_CERT); + subtest("retrieve pubkey secp521r1", test_retrieve_pubkey_one, ASSET_SECP521R1_KEY, ASSET_SECP521R1_CERT); +} + +/* +* End to end testing of signature and verifiers: +* The general scenario is: +* - prepare a signature of a test string using a simulated +* server programmed with a private key and a certificate +* list. +* - verify the signature in a simulated client programmed +* with a list of trusted certificates. +* +* The test is configured with the file names for the key, +* certificate list, and trusted certificates. +* +* Ideally, we should be able to run the test by mixing and +* matching mbedtls server or clients with other backends. +* However, using openssl will require some plumbing, +* which will be done when integrating this code in +* picotls. For now, we will only do self tests, and test with +* minicrypto if the key is supported. +* +* Consider breaking out parts of this test in separate subtests, +* e.g., load certificate chain, verify certificate chain, +* extract key from certificare chain, verify signature. +*/ + +static const unsigned char test_sign_verify_message[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 , 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, + 60, 61, 62, 63, 64 +}; +const size_t test_sign_verify_message_size = sizeof(test_sign_verify_message); + +static uint16_t test_sign_signature_algorithms[] = { + 0x0401, 0x0403, 0x501, 0x0503, 0x0601, 0x0603, + 0x0804, 0x0805, 0x0806, 0x0807, 0x0808 +}; + +static size_t num_test_sign_signature_algorithms = sizeof(test_sign_signature_algorithms) / sizeof(uint16_t); + +static int test_sign_init_server_mbedtls(ptls_context_t* ctx, char const* key_path, char const* cert_path) +{ + int ret = ptls_mbedtls_load_private_key(ctx, key_path); + if (ret == 0) { + ret = picoquic_mbedtls_get_certs_from_file(cert_path, &ctx->certificates.list, &ctx->certificates.count); } - ok(!!"success"); + return ret; } -static void test_load_secp384r1_key() +static int test_sign_init_server_minicrypto(ptls_context_t* ctx, char const* key_path, char const* cert_path) { - int ret = test_load_one_der_key(ASSET_SECP384R1_KEY); - if (ret != 0) { - ok(!"fail"); - return; + int ret = ptls_minicrypto_load_private_key(ctx, key_path); + if (ret == 0) { + ret = ptls_load_certificates(ctx, cert_path); } - ok(!!"success"); + return ret; } -static void test_load_secp521r1_key() +static void test_sign_free_certificates(ptls_context_t* ctx) { - int ret = test_load_one_der_key(ASSET_SECP521R1_KEY); - if (ret != 0) { - ok(!"fail"); - return; + if (ctx->certificates.list != NULL) { + for (int i = 0; i < ctx->certificates.count; i++) { + free(ctx->certificates.list[i].base); + } + free(ctx->certificates.list); } - ok(!!"success"); + ctx->certificates.list = NULL; + ctx->certificates.count = 0; } -static void test_load_secp256r1_pkcs8_key() +static void test_sign_free_context(ptls_context_t* ctx, int config) { - int ret = test_load_one_der_key(ASSET_SECP256R1_PKCS8_KEY); - if (ret != 0) { - ok(!"fail"); + /* Free the server context */ + if (ctx == NULL) { return; } - ok(!!"success"); + test_sign_free_certificates(ctx); + if (ctx->sign_certificate != NULL) { + switch (config) { + case 0: + ptls_mbedtls_dispose_sign_certificate(ctx->sign_certificate); + break; + case 1: + default: + free(ctx->sign_certificate); + ctx->sign_certificate = NULL; + } + } + + if (ctx->verify_certificate != NULL) { + switch (config) { + case 0: + ptls_mbedtls_dispose_verify_certificate(ctx); + break; + default: + break; + } + } + + free(ctx); } -static void test_load_rsa_pkcs8_key() +static ptls_context_t* test_sign_set_ptls_context(char const* key_path, char const* cert_path, char const* trusted_path, int is_server, int config) { - int ret = test_load_one_der_key(ASSET_RSA_PKCS8_KEY); + int ret = 0; + ptls_context_t* ctx = (ptls_context_t*)malloc(sizeof(ptls_context_t)); + if (ctx == NULL) { + ok(ctx != NULL); + return NULL; + } + + memset(ctx, 0, sizeof(ptls_context_t)); + ctx->get_time = &ptls_get_time; + + switch (config) { + case 0: + ctx->random_bytes = ptls_mbedtls_random_bytes; + case 1: + default: + break; + } + + if (is_server) { + /* First, create the "signer" plug-in */ + switch (config) { + case 0: /* MbedTLS */ + ret = test_sign_init_server_mbedtls(ctx, key_path, cert_path); + if (ret != 0) { + ok(ret == 0); + } + break; + case 1: /* Minicrypto */ + ret = test_sign_init_server_minicrypto(ctx, key_path, cert_path); + if (ret != 0) { + ok(ret == 0); + } + break; + default: + ret = -1; + ok(ret == 0); + break; + } + } + else { + /* Initialize the client verify context */ + switch (config) { + case 0: /* MbedTLS */ + ret = ptls_mbedtls_init_verify_certificate(ctx, trusted_path); + if (ret != 0) { + ok(ret == 0); + } + break; + default: + ret = -1; + ok(ret == 0); + break; + } + } + if (ret != 0) { - ok(!"fail"); - return; + /* Release and return NULL */ + test_sign_free_context(ctx, config); + ctx = NULL; } - ok(!!"success"); + return ctx; +} + +static int test_sign_verify_one(char const* key_path, char const * cert_path, char const * trusted_path, char const * server_name, int server_config, int client_config) +{ + int ret = 0; + ptls_context_t* server_ctx = test_sign_set_ptls_context(key_path, cert_path, trusted_path, 1, server_config); + ptls_context_t* client_ctx = test_sign_set_ptls_context(key_path, cert_path, trusted_path, 0, client_config); + ptls_t* client_tls = NULL; + ptls_t* server_tls = NULL; + uint16_t selected_algorithm = 0; + uint8_t signature_smallbuf[256]; + ptls_buffer_t signature; + struct { + int (*cb)(void *verify_ctx, uint16_t algo, ptls_iovec_t data, ptls_iovec_t signature); + void *verify_ctx; + } certificate_verify; + ptls_iovec_t input; + input.base = (uint8_t *)test_sign_verify_message; + input.len = test_sign_verify_message_size; + + ptls_buffer_init(&signature, signature_smallbuf, sizeof(signature_smallbuf)); + + if (server_ctx == NULL || client_ctx == NULL) { + ok(server_ctx != NULL && client_ctx != NULL); + ret = -1; + } + + if (ret == 0) { + /* Then, create a tls context for the server. */ + server_tls = ptls_new(server_ctx, 1); + if (server_tls == NULL) { + ok(server_tls != NULL); + ret = -1; + } + } + + if (ret == 0) { + /* Then, create the signature messages */ + ret = server_ctx->sign_certificate->cb(server_ctx->sign_certificate, server_tls, NULL, + &selected_algorithm, &signature, input, + test_sign_signature_algorithms, num_test_sign_signature_algorithms); + if (ret != 0) { + ok(ret == 0); + } + } + + if (ret == 0) { + /* Then, create a tls context for the client. */ + client_tls = ptls_new(client_ctx, 0); + if (client_tls == NULL) { + ok(client_tls != NULL); + ret = -1; + } + } + + if (ret == 0) { + /* verify the certificates */ + ret = client_ctx->verify_certificate->cb(client_ctx->verify_certificate, client_tls, server_name, + &certificate_verify.cb, &certificate_verify.verify_ctx, + server_ctx->certificates.list, server_ctx->certificates.count); + if (ret != 0) { + ok(ret == 0); + } + /* verify the signature */ + if (ret == 0) { + ptls_iovec_t sig; + sig.base = signature.base; + sig.len = signature.off; + + ret = certificate_verify.cb(certificate_verify.verify_ctx, selected_algorithm, input, sig); + ok(ret == 0); + } + else if (certificate_verify.cb != NULL) { + ptls_iovec_t empty; + empty.base = NULL; + empty.len = 0; + (void)certificate_verify.cb(certificate_verify.verify_ctx, 0, empty, empty); + } + } + + ptls_buffer_dispose(&signature); + + if (client_tls != NULL) { + ptls_free(client_tls); + } + if (server_tls != NULL) { + ptls_free(server_tls); + } + + test_sign_free_context(server_ctx, server_config); + test_sign_free_context(client_ctx, client_config); + + ok(ret == 0); + return ret; } -void test_sign_certificate(void) +/* TODO: all these tests are failing, because we do not have the +* proper combination of hostname and certificate. Fix that, then +* enable the test. +* +* TODO: add tests of minicrypto server and mbedtls client. +* TODO: add tests of mbedtls versus openssl. +* TODO: add negative testing. + */ + +static void test_sign_verify_end_to_end() { - subtest("load rsa key", test_load_rsa_key); - subtest("load secp256r1 key", test_load_secp256r1_key); - subtest("load secp384r1 key", test_load_secp384r1_key); - subtest("load secp521r1 key", test_load_secp521r1_key); - subtest("load secp521r1-pkcs8 key", test_load_secp256r1_pkcs8_key); - subtest("load rsa-pkcs8 key", test_load_rsa_pkcs8_key); - - /* we do not test EDDSA keys, because they are not yet supported */ + subtest("verify rsa mbedtls mbedtls", test_sign_verify_one, + ASSET_RSA_KEY, ASSET_RSA_CERT, ASSET_TEST_CA, ASSET_RSA_NAME, 0, 0); + subtest("verify secp256r1 mbedtls mbedtls", test_sign_verify_one, + ASSET_SECP256R1_KEY, ASSET_SECP256R1_CERT, ASSET_TEST_CA, ASSET_SECP256R1_NAME, 0, 0); + subtest("verify secp384r1 mbedtls mbedtls", test_sign_verify_one, + ASSET_SECP384R1_KEY, ASSET_SECP384R1_CERT, ASSET_TEST_CA, ASSET_SECP384R1_NAME, 0, 0); + subtest("verify secp521r1 mbedtls mbedtls", test_sign_verify_one, + ASSET_SECP521R1_KEY, ASSET_SECP521R1_CERT, ASSET_TEST_CA, ASSET_SECP521R1_NAME, 0, 0); + subtest("verify secp256r1 pkcs8 mbedtls mbedtls", test_sign_verify_one, + ASSET_SECP256R1_PKCS8_KEY, ASSET_SECP256R1_PKCS8_CERT, ASSET_TEST_CA, ASSET_SECP256R1_PKCS8_NAME, 0, 0); } + DEFINE_FFX_AES128_ALGORITHMS(mbedtls); DEFINE_FFX_CHACHA20_ALGORITHMS(mbedtls); @@ -310,8 +769,18 @@ int main(int argc, char **argv) ctx_peer = &mbedtls_ctx; subtest("minicrypto vs.", test_picotls); - /* test the sign certificate */ - subtest("sign certificate", test_sign_certificate); + /* Test loading file in memory */ + subtest("load file", test_load_file); + + /* test loading of keys in memory and capability to sign, + * and also verify failure modes. */ + subtest("load keys", test_load_keys); + + /* Check that the bits of the public key are correctly retrieved from a certificate */ + subtest("retrieve public key from cert", test_retrieve_pubkey); + + /* End to end test of signing and verifying certicates */ + subtest("sign verify end to end", test_sign_verify_end_to_end); /* Deinitialize the PSA crypto library. */ mbedtls_psa_crypto_free();