From c2429f1cf9106504422da7194a42a62df5141a8b Mon Sep 17 00:00:00 2001 From: Kapil Gupta Date: Fri, 13 May 2022 12:57:47 +0800 Subject: [PATCH] wpa_supplicant: sync eap code with upstream --- components/wpa_supplicant/CMakeLists.txt | 19 +- components/wpa_supplicant/Kconfig | 13 + .../src/crypto/crypto_mbedtls-ec.c | 2 +- .../src/crypto/crypto_mbedtls-rsa.c | 133 +- .../src/crypto/crypto_mbedtls.c | 160 +- .../esp_supplicant/src/crypto/tls_mbedtls.c | 6 +- .../esp_supplicant/src/esp_wpa2.c | 4 +- .../wpa_supplicant/include/utils/wpa_debug.h | 8 +- components/wpa_supplicant/port/include/os.h | 9 +- .../port/include/supplicant_opt.h | 8 - components/wpa_supplicant/port/os_xtensa.c | 2 +- .../src/crypto/crypto_internal.c | 2 + .../wpa_supplicant/src/crypto/crypto_ops.c | 2 +- .../wpa_supplicant/src/crypto/libtommath.h | 3441 ----------------- components/wpa_supplicant/src/crypto/sha384.h | 1 + .../wpa_supplicant/src/{tls => crypto}/tls.h | 194 +- .../src/{tls => crypto}/tls_internal.c | 201 +- components/wpa_supplicant/src/eap_peer/eap.c | 6 +- .../wpa_supplicant/src/eap_peer/eap_fast.c | 2 +- .../src/eap_peer/eap_fast_common.c | 2 +- .../src/eap_peer/eap_mschapv2.c | 2 +- .../wpa_supplicant/src/eap_peer/eap_peap.c | 2 +- .../wpa_supplicant/src/eap_peer/eap_tls.c | 2 +- .../src/eap_peer/eap_tls_common.c | 2 +- .../wpa_supplicant/src/eap_peer/eap_ttls.c | 2 +- components/wpa_supplicant/src/tls/asn1.c | 470 ++- components/wpa_supplicant/src/tls/asn1.h | 152 +- components/wpa_supplicant/src/tls/bignum.h | 10 +- components/wpa_supplicant/src/tls/pkcs1.c | 192 +- components/wpa_supplicant/src/tls/pkcs1.h | 7 + components/wpa_supplicant/src/tls/pkcs5.c | 470 ++- components/wpa_supplicant/src/tls/pkcs8.c | 74 +- components/wpa_supplicant/src/tls/rsa.c | 21 +- .../wpa_supplicant/src/tls/tlsv1_client.c | 239 +- .../wpa_supplicant/src/tls/tlsv1_client.h | 13 +- .../wpa_supplicant/src/tls/tlsv1_client_i.h | 22 +- .../src/tls/tlsv1_client_ocsp.c | 759 ++++ .../src/tls/tlsv1_client_read.c | 601 ++- .../src/tls/tlsv1_client_write.c | 205 +- .../wpa_supplicant/src/tls/tlsv1_common.c | 64 +- .../wpa_supplicant/src/tls/tlsv1_common.h | 3 +- .../wpa_supplicant/src/tls/tlsv1_cred.c | 792 +++- .../wpa_supplicant/src/tls/tlsv1_cred.h | 8 + .../wpa_supplicant/src/tls/tlsv1_record.c | 60 +- .../wpa_supplicant/src/tls/tlsv1_server.c | 345 +- .../wpa_supplicant/src/tls/tlsv1_server.h | 12 +- .../wpa_supplicant/src/tls/tlsv1_server_i.h | 18 + .../src/tls/tlsv1_server_read.c | 608 +-- .../src/tls/tlsv1_server_write.c | 388 +- components/wpa_supplicant/src/tls/x509v3.c | 935 +++-- components/wpa_supplicant/src/tls/x509v3.h | 43 +- .../wifi/wifi_eap_fast/sdkconfig.defaults | 2 +- 52 files changed, 6008 insertions(+), 4730 deletions(-) delete mode 100644 components/wpa_supplicant/src/crypto/libtommath.h rename components/wpa_supplicant/src/{tls => crypto}/tls.h (74%) rename components/wpa_supplicant/src/{tls => crypto}/tls_internal.c (76%) create mode 100644 components/wpa_supplicant/src/tls/tlsv1_client_ocsp.c diff --git a/components/wpa_supplicant/CMakeLists.txt b/components/wpa_supplicant/CMakeLists.txt index f96f9d833e..702d9aa000 100644 --- a/components/wpa_supplicant/CMakeLists.txt +++ b/components/wpa_supplicant/CMakeLists.txt @@ -66,7 +66,7 @@ if(CONFIG_ESP_WIFI_SOFTAP_SUPPORT) set(esp_srcs ${esp_srcs} "esp_supplicant/src/esp_hostap.c") endif() -if(CONFIG_WPA_MBEDTLS_CRYPTO) +if(CONFIG_WPA_MBEDTLS_TLS_CLIENT) set(tls_src "esp_supplicant/src/crypto/tls_mbedtls.c") else() set(tls_src @@ -77,16 +77,14 @@ else() "src/tls/pkcs8.c" "src/tls/bignum.c" "src/tls/rsa.c" - "src/tls/tls_internal.c" + "src/crypto/tls_internal.c" "src/tls/tlsv1_client.c" "src/tls/tlsv1_client_read.c" "src/tls/tlsv1_client_write.c" "src/tls/tlsv1_common.c" "src/tls/tlsv1_cred.c" "src/tls/tlsv1_record.c" - "src/tls/tlsv1_server.c" - "src/tls/tlsv1_server_read.c" - "src/tls/tlsv1_server_write.c" + "src/tls/tlsv1_client_ocsp.c" "src/tls/x509v3.c") endif() @@ -94,6 +92,7 @@ if(CONFIG_WPA_MBEDTLS_CRYPTO) set(crypto_src "esp_supplicant/src/crypto/crypto_mbedtls.c" "esp_supplicant/src/crypto/crypto_mbedtls-bignum.c" + "esp_supplicant/src/crypto/crypto_mbedtls-rsa.c" "esp_supplicant/src/crypto/crypto_mbedtls-ec.c") # Add internal RC4 if RC4 is disabled in mbedtls if(CONFIG_MBEDTLS_RC4_DISABLED) @@ -193,7 +192,6 @@ target_compile_definitions(${COMPONENT_LIB} PRIVATE EAP_TTLS EAP_TLS EAP_PEAP - EAP_FAST USE_WPA2_TASK CONFIG_WPS2 CONFIG_WPS_PIN @@ -235,4 +233,13 @@ endif() if(CONFIG_WPA_11R_SUPPORT) target_compile_definitions(${COMPONENT_LIB} PRIVATE CONFIG_IEEE80211R) endif() +if(NOT CONFIG_WPA_MBEDTLS_TLS_CLIENT) + target_compile_definitions(${COMPONENT_LIB} PRIVATE CONFIG_TLS_INTERNAL_CLIENT + CONFIG_TLSV11 CONFIG_TLSV12 CONFIG_INTERNAL_SHA384 CONFIG_INTERNAL_SHA512 EAP_FAST) +endif() +if(CONFIG_WPA_MBEDTLS_CRYPTO) + target_compile_definitions(${COMPONENT_LIB} PRIVATE CONFIG_CRYPTO_MBEDTLS) +else() + target_compile_definitions(${COMPONENT_LIB} PRIVATE CONFIG_CRYPTO_INTERNAL) +endif() set_property(TARGET ${COMPONENT_LIB} APPEND PROPERTY LINK_INTERFACE_MULTIPLICITY 3) diff --git a/components/wpa_supplicant/Kconfig b/components/wpa_supplicant/Kconfig index b36fbf45ea..5501c47906 100644 --- a/components/wpa_supplicant/Kconfig +++ b/components/wpa_supplicant/Kconfig @@ -11,6 +11,19 @@ menu "Supplicant" help Select this option to use MbedTLS crypto APIs which utilize hardware acceleration. + if WPA_MBEDTLS_CRYPTO + config WPA_MBEDTLS_TLS_CLIENT + bool "Use MbedTLS TLS client for WiFi Enterprise connection" + default y + select MBEDTLS_TLS_ENABLED + help + Select this option to use MbedTLS TLS client for WPA2 enterprise connection. + Please note that from MbedTLS-3.0 onwards, MbedTLS does not support SSL-3.0 + TLS-v1.0, TLS-v1.1 versions. Incase your server is using one of these version, + it is advisable to update your server. + Please disable this option for compatibilty with older TLS versions. + endif + config WPA_WAPI_PSK bool "Enable WAPI PSK support" default n diff --git a/components/wpa_supplicant/esp_supplicant/src/crypto/crypto_mbedtls-ec.c b/components/wpa_supplicant/esp_supplicant/src/crypto/crypto_mbedtls-ec.c index 93a68f139b..eef2ce1be7 100644 --- a/components/wpa_supplicant/esp_supplicant/src/crypto/crypto_mbedtls-ec.c +++ b/components/wpa_supplicant/esp_supplicant/src/crypto/crypto_mbedtls-ec.c @@ -32,7 +32,7 @@ struct crypto_ec { mbedtls_ecp_group group; }; -int crypto_rng_wrapper(void *ctx, unsigned char *buf, size_t len) +static int crypto_rng_wrapper(void *ctx, unsigned char *buf, size_t len) { return random_get_bytes(buf, len); } diff --git a/components/wpa_supplicant/esp_supplicant/src/crypto/crypto_mbedtls-rsa.c b/components/wpa_supplicant/esp_supplicant/src/crypto/crypto_mbedtls-rsa.c index b9bc4d148f..fd80e5b96a 100644 --- a/components/wpa_supplicant/esp_supplicant/src/crypto/crypto_mbedtls-rsa.c +++ b/components/wpa_supplicant/esp_supplicant/src/crypto/crypto_mbedtls-rsa.c @@ -13,7 +13,7 @@ #include "crypto.h" #include "common/defs.h" -#ifdef USE_MBEDTLS_CRYPTO +#ifdef CONFIG_CRYPTO_MBEDTLS #include "mbedtls/entropy.h" #include "mbedtls/ctr_drbg.h" @@ -26,6 +26,7 @@ struct crypto_public_key; struct crypto_private_key; +#ifdef DEBUG_PRINT static void crypto_dump_verify_info(u32 flags) { char dump_buffer[1024]; @@ -33,6 +34,14 @@ static void crypto_dump_verify_info(u32 flags) mbedtls_x509_crt_verify_info(dump_buffer, 1024, " ! ", flags ); wpa_printf(MSG_ERROR, "%s", dump_buffer); } +#else +static void crypto_dump_verify_info(u32 flags) { } +#endif + +static int crypto_rng_wrapper(void *ctx, unsigned char *buf, size_t len) +{ + return os_get_random(buf, len); +} int crypto_verify_cert(const u8 *cert_start, int certlen, const u8 *ca_cert_start, int ca_certlen) { @@ -110,7 +119,8 @@ struct crypto_private_key * crypto_private_key_import(const u8 *key, mbedtls_pk_init(pkey); - ret = mbedtls_pk_parse_key(pkey, key, len, (const unsigned char *)passwd, passwd ? os_strlen(passwd) : 0); + ret = mbedtls_pk_parse_key(pkey, key, len, (const unsigned char *)passwd, + passwd ? os_strlen(passwd) : 0, crypto_rng_wrapper, NULL); if (ret < 0) { wpa_printf(MSG_ERROR, "failed to parse private key"); @@ -201,13 +211,13 @@ int crypto_public_key_encrypt_pkcs1_v15(struct crypto_public_key *key, } ret = mbedtls_rsa_pkcs1_encrypt(mbedtls_pk_rsa(*pkey), mbedtls_ctr_drbg_random, - ctr_drbg, MBEDTLS_RSA_PUBLIC, inlen, in, out); + ctr_drbg, inlen, in, out); if(ret != 0) { wpa_printf(MSG_ERROR, " failed ! mbedtls_rsa_pkcs1_encrypt returned -0x%04x", -ret); goto cleanup; } - *outlen = mbedtls_pk_rsa(*pkey)->len; + *outlen = mbedtls_pk_rsa(*pkey)->MBEDTLS_PRIVATE(len); cleanup: mbedtls_ctr_drbg_free( ctr_drbg ); @@ -246,9 +256,9 @@ int crypto_private_key_decrypt_pkcs1_v15(struct crypto_private_key *key, if (ret < 0) goto cleanup; - i = mbedtls_pk_rsa(*pkey)->len; + i = mbedtls_pk_rsa(*pkey)->MBEDTLS_PRIVATE(len); ret = mbedtls_rsa_rsaes_pkcs1_v15_decrypt(mbedtls_pk_rsa(*pkey), mbedtls_ctr_drbg_random, - ctr_drbg, MBEDTLS_RSA_PRIVATE, &i, in, out, *outlen); + ctr_drbg, &i, in, out, *outlen); *outlen = i; @@ -267,17 +277,37 @@ int crypto_private_key_sign_pkcs1(struct crypto_private_key *key, u8 *out, size_t *outlen) { int ret; + const char *pers = "rsa_encrypt"; mbedtls_pk_context *pkey = (mbedtls_pk_context *)key; + mbedtls_entropy_context *entropy = os_malloc(sizeof(*entropy)); + mbedtls_ctr_drbg_context *ctr_drbg = os_malloc(sizeof(*ctr_drbg)); - if((ret = mbedtls_rsa_pkcs1_sign(mbedtls_pk_rsa(*pkey), NULL, NULL, MBEDTLS_RSA_PRIVATE, - (mbedtls_pk_rsa(*pkey))->hash_id, - inlen, in, out)) != 0 ) { - wpa_printf(MSG_ERROR, " failed ! mbedtls_rsa_pkcs1_sign returned %d", ret ); + if (!pkey || !entropy || !ctr_drbg) { + if (entropy) + os_free(entropy); + if (ctr_drbg) + os_free(ctr_drbg); return -1; } - *outlen = mbedtls_pk_rsa(*pkey)->len; + mbedtls_ctr_drbg_init( ctr_drbg ); + mbedtls_entropy_init( entropy ); + ret = mbedtls_ctr_drbg_seed(ctr_drbg, mbedtls_entropy_func, + entropy, (const unsigned char *) pers, + strlen(pers)); - return 0; + if((ret = mbedtls_rsa_pkcs1_sign(mbedtls_pk_rsa(*pkey), mbedtls_ctr_drbg_random, ctr_drbg, + (mbedtls_pk_rsa(*pkey))->MBEDTLS_PRIVATE(hash_id), + inlen, in, out)) != 0 ) { + wpa_printf(MSG_ERROR, " failed ! mbedtls_rsa_pkcs1_sign returned %d", ret ); + goto cleanup; + } + *outlen = mbedtls_pk_rsa(*pkey)->MBEDTLS_PRIVATE(len); +cleanup: + mbedtls_ctr_drbg_free( ctr_drbg ); + mbedtls_entropy_free( entropy ); + os_free(entropy); + os_free(ctr_drbg); + return ret; } @@ -302,44 +332,69 @@ void crypto_private_key_free(struct crypto_private_key *key) os_free(pkey); } - int crypto_public_key_decrypt_pkcs1(struct crypto_public_key *key, const u8 *crypt, size_t crypt_len, u8 *plain, size_t *plain_len) { - const char *pers = "rsa_decrypt"; + size_t len; + u8 *pos; mbedtls_pk_context *pkey = (mbedtls_pk_context *)key; - mbedtls_ctr_drbg_context ctr_drbg; - mbedtls_entropy_context entropy; - size_t i; - mbedtls_entropy_init( &entropy ); - mbedtls_ctr_drbg_init( &ctr_drbg ); + len = *plain_len; + if (mbedtls_rsa_public(mbedtls_pk_rsa(*pkey), crypt, plain) < 0) + return -1; - int ret = mbedtls_ctr_drbg_seed( &ctr_drbg, mbedtls_entropy_func, - &entropy, (const unsigned char *) pers, - strlen( pers ) ); - if(ret != 0) { - wpa_printf(MSG_ERROR, " failed ! mbedtls_ctr_drbg_seed returned %d", - ret ); - goto cleanup; + /* + * PKCS #1 v1.5, 8.1: + * + * EB = 00 || BT || PS || 00 || D + * BT = 00 or 01 + * PS = k-3-||D|| times (00 if BT=00) or (FF if BT=01) + * k = length of modulus in octets + * + * Based on 10.1.3, "The block type shall be 01" for a signature. + */ + + if (len < 3 + 8 + 16 /* min hash len */ || + plain[0] != 0x00 || plain[1] != 0x01) { + wpa_printf(MSG_INFO, "LibTomCrypt: Invalid signature EB " + "structure"); + wpa_hexdump_key(MSG_DEBUG, "Signature EB", plain, len); + return -1; } - i = mbedtls_pk_rsa(*pkey)->len; - ret = mbedtls_rsa_pkcs1_decrypt(mbedtls_pk_rsa(*pkey), mbedtls_ctr_drbg_random, - &ctr_drbg, MBEDTLS_RSA_PUBLIC, &i, - crypt, plain, *plain_len); - if( ret != 0 ) { - wpa_printf(MSG_ERROR, " failed ! mbedtls_rsa_pkcs1_decrypt returned %d", - ret ); - goto cleanup; + pos = plain + 3; + /* BT = 01 */ + if (plain[2] != 0xff) { + wpa_printf(MSG_INFO, "LibTomCrypt: Invalid signature " + "PS (BT=01)"); + wpa_hexdump_key(MSG_DEBUG, "Signature EB", plain, len); + return -1; } - *plain_len = i; + while (pos < plain + len && *pos == 0xff) + pos++; -cleanup: - mbedtls_entropy_free( &entropy ); - mbedtls_ctr_drbg_free( &ctr_drbg ); + if (pos - plain - 2 < 8) { + /* PKCS #1 v1.5, 8.1: At least eight octets long PS */ + wpa_printf(MSG_INFO, "LibTomCrypt: Too short signature " + "padding"); + wpa_hexdump_key(MSG_DEBUG, "Signature EB", plain, len); + return -1; + } - return ret; + if (pos + 16 /* min hash len */ >= plain + len || *pos != 0x00) { + wpa_printf(MSG_INFO, "LibTomCrypt: Invalid signature EB " + "structure (2)"); + wpa_hexdump_key(MSG_DEBUG, "Signature EB", plain, len); + return -1; + } + pos++; + len -= pos - plain; + + /* Strip PKCS #1 header */ + os_memmove(plain, pos, len); + *plain_len = len; + + return 0; } #endif diff --git a/components/wpa_supplicant/esp_supplicant/src/crypto/crypto_mbedtls.c b/components/wpa_supplicant/esp_supplicant/src/crypto/crypto_mbedtls.c index 629e5f3245..577b50d202 100644 --- a/components/wpa_supplicant/esp_supplicant/src/crypto/crypto_mbedtls.c +++ b/components/wpa_supplicant/esp_supplicant/src/crypto/crypto_mbedtls.c @@ -28,9 +28,10 @@ #include "common.h" #include "utils/wpabuf.h" #include "dh_group5.h" +#include "md5.h" #include "sha1.h" #include "sha256.h" -#include "md5.h" +#include "sha384.h" #include "aes_wrap.h" #include "crypto.h" #include "mbedtls/esp_config.h" @@ -95,6 +96,12 @@ int sha384_vector(size_t num_elem, const u8 *addr[], const size_t *len, return digest_vector(MBEDTLS_MD_SHA384, num_elem, addr, len, mac); } +int sha512_vector(size_t num_elem, const u8 *addr[], const size_t *len, + u8 *mac) +{ + return digest_vector(MBEDTLS_MD_SHA512, num_elem, addr, len, mac); +} + int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { return digest_vector(MBEDTLS_MD_SHA1, num_elem, addr, len, mac); @@ -122,21 +129,41 @@ struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key, struct crypto_hash *ctx; mbedtls_md_type_t md_type; const mbedtls_md_info_t *md_info; + int ret; + int is_hmac = 0; switch (alg) { - case CRYPTO_HASH_ALG_HMAC_MD5: - md_type = MBEDTLS_MD_MD5; - break; - case CRYPTO_HASH_ALG_HMAC_SHA1: - md_type = MBEDTLS_MD_SHA1; - break; - case CRYPTO_HASH_ALG_HMAC_SHA256: - md_type = MBEDTLS_MD_SHA256; - break; - default: - return NULL; + case CRYPTO_HASH_ALG_MD5: + case CRYPTO_HASH_ALG_HMAC_MD5: + md_type = MBEDTLS_MD_MD5; + break; + case CRYPTO_HASH_ALG_SHA1: + case CRYPTO_HASH_ALG_HMAC_SHA1: + md_type = MBEDTLS_MD_SHA1; + break; + case CRYPTO_HASH_ALG_SHA256: + case CRYPTO_HASH_ALG_HMAC_SHA256: + md_type = MBEDTLS_MD_SHA256; + break; + case CRYPTO_HASH_ALG_SHA384: + md_type = MBEDTLS_MD_SHA384; + break; + case CRYPTO_HASH_ALG_SHA512: + md_type = MBEDTLS_MD_SHA512; + break; + default: + return NULL; } + switch (alg) { + case CRYPTO_HASH_ALG_HMAC_MD5: + case CRYPTO_HASH_ALG_HMAC_SHA1: + case CRYPTO_HASH_ALG_HMAC_SHA256: + is_hmac = 1; + break; + default: + break; + } ctx = os_zalloc(sizeof(*ctx)); if (ctx == NULL) { return NULL; @@ -153,6 +180,15 @@ struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key, if (mbedtls_md_hmac_starts(&ctx->ctx, key, key_len) != 0) { goto cleanup; } + if (is_hmac) { + ret = mbedtls_md_hmac_starts(&ctx->ctx, key, key_len); + } else { + ret = mbedtls_md_starts(&ctx->ctx); + } + if (ret < 0) { + goto cleanup; + } + return ctx; cleanup: os_free(ctx); @@ -166,7 +202,11 @@ void crypto_hash_update(struct crypto_hash *ctx, const u8 *data, size_t len) if (ctx == NULL) { return; } - ret = mbedtls_md_hmac_update(&ctx->ctx, data, len); + if (ctx->ctx.MBEDTLS_PRIVATE(hmac_ctx)) { + ret = mbedtls_md_hmac_update(&ctx->ctx, data, len); + } else { + ret = mbedtls_md_update(&ctx->ctx, data, len); + } if (ret != 0) { wpa_printf(MSG_ERROR, "%s: mbedtls_md_hmac_update failed", __func__); } @@ -174,18 +214,70 @@ void crypto_hash_update(struct crypto_hash *ctx, const u8 *data, size_t len) int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len) { - int ret; + int ret = 0; + mbedtls_md_type_t md_type; if (ctx == NULL) { return -2; } if (mac == NULL || len == NULL) { - mbedtls_md_free(&ctx->ctx); - bin_clear_free(ctx, sizeof(*ctx)); - return 0; + goto err; } - ret = mbedtls_md_hmac_finish(&ctx->ctx, mac); + md_type = mbedtls_md_get_type(ctx->ctx.MBEDTLS_PRIVATE(md_info)); + switch(md_type) { + case MBEDTLS_MD_MD5: + if (*len < MD5_MAC_LEN) { + *len = MD5_MAC_LEN; + ret = -1; + goto err; + } + *len = MD5_MAC_LEN; + break; + case MBEDTLS_MD_SHA1: + if (*len < SHA1_MAC_LEN) { + *len = SHA1_MAC_LEN; + ret = -1; + goto err; + } + *len = SHA1_MAC_LEN; + break; + case MBEDTLS_MD_SHA256: + if (*len < SHA256_MAC_LEN) { + *len = SHA256_MAC_LEN; + ret = -1; + goto err; + } + *len = SHA256_MAC_LEN; + break; + case MBEDTLS_MD_SHA384: + if (*len < SHA384_MAC_LEN) { + *len = SHA384_MAC_LEN; + ret = -1; + goto err; + } + *len = SHA384_MAC_LEN; + break; + case MBEDTLS_MD_SHA512: + if (*len < SHA512_MAC_LEN) { + *len = SHA512_MAC_LEN; + ret = -1; + goto err; + } + *len = SHA512_MAC_LEN; + break; + default: + *len = 0; + ret = -1; + goto err; + } + if (ctx->ctx.MBEDTLS_PRIVATE(hmac_ctx)) { + ret = mbedtls_md_hmac_finish(&ctx->ctx, mac); + } else { + ret = mbedtls_md_finish(&ctx->ctx, mac); + } + +err: mbedtls_md_free(&ctx->ctx); bin_clear_free(ctx, sizeof(*ctx)); @@ -398,6 +490,7 @@ int aes_128_cbc_decrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len) } +#ifdef CONFIG_TLS_INTERNAL_CLIENT struct crypto_cipher { mbedtls_cipher_context_t ctx_enc; mbedtls_cipher_context_t ctx_dec; @@ -405,7 +498,7 @@ struct crypto_cipher { static int crypto_init_cipher_ctx(mbedtls_cipher_context_t *ctx, const mbedtls_cipher_info_t *cipher_info, - const u8 *iv, const u8 *key, + const u8 *iv, const u8 *key, size_t key_len, mbedtls_operation_t operation) { mbedtls_cipher_init(ctx); @@ -416,8 +509,7 @@ static int crypto_init_cipher_ctx(mbedtls_cipher_context_t *ctx, return -1; } - if (mbedtls_cipher_setkey(ctx, key, cipher_info->MBEDTLS_PRIVATE(key_bitlen), - operation) != 0) { + if (mbedtls_cipher_setkey(ctx, key, key_len * 8, operation) != 0) { wpa_printf(MSG_ERROR, "mbedtls_cipher_setkey returned error"); return -1; } @@ -490,15 +582,21 @@ struct crypto_cipher *crypto_cipher_init(enum crypto_cipher_alg alg, /* Init both ctx encryption/decryption */ if (crypto_init_cipher_ctx(&ctx->ctx_enc, cipher_info, iv, key, - MBEDTLS_ENCRYPT) < 0) { + key_len, MBEDTLS_ENCRYPT) < 0) { goto cleanup; } if (crypto_init_cipher_ctx(&ctx->ctx_dec, cipher_info, iv, key, - MBEDTLS_DECRYPT) < 0) { + key_len, MBEDTLS_DECRYPT) < 0) { goto cleanup; } + if (mbedtls_cipher_set_padding_mode(&ctx->ctx_enc, MBEDTLS_PADDING_NONE) < 0) { + goto cleanup; + } + if (mbedtls_cipher_set_padding_mode(&ctx->ctx_dec, MBEDTLS_PADDING_NONE) < 0) { + goto cleanup; + } return ctx; cleanup: @@ -506,12 +604,11 @@ cleanup: return NULL; } -#if 0 int crypto_cipher_encrypt(struct crypto_cipher *ctx, const u8 *plain, u8 *crypt, size_t len) { int ret; - size_t olen = 1200; + size_t olen = 0; ret = mbedtls_cipher_update(&ctx->ctx_enc, plain, len, crypt, &olen); if (ret != 0) { @@ -530,7 +627,7 @@ int crypto_cipher_decrypt(struct crypto_cipher *ctx, const u8 *crypt, u8 *plain, size_t len) { int ret; - size_t olen = 1200; + size_t olen = 0; ret = mbedtls_cipher_update(&ctx->ctx_dec, crypt, len, plain, &olen); if (ret != 0) { @@ -544,7 +641,6 @@ int crypto_cipher_decrypt(struct crypto_cipher *ctx, const u8 *crypt, return 0; } -#endif void crypto_cipher_deinit(struct crypto_cipher *ctx) { @@ -552,6 +648,7 @@ void crypto_cipher_deinit(struct crypto_cipher *ctx) mbedtls_cipher_free(&ctx->ctx_dec); os_free(ctx); } +#endif int aes_ctr_encrypt(const u8 *key, size_t key_len, const u8 *nonce, u8 *data, size_t data_len) @@ -909,3 +1006,12 @@ int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey, return 0; } + +int crypto_global_init(void) +{ + return 0; +} + +void crypto_global_deinit(void) +{ +} diff --git a/components/wpa_supplicant/esp_supplicant/src/crypto/tls_mbedtls.c b/components/wpa_supplicant/esp_supplicant/src/crypto/tls_mbedtls.c index 2bacc18524..575a59e458 100644 --- a/components/wpa_supplicant/esp_supplicant/src/crypto/tls_mbedtls.c +++ b/components/wpa_supplicant/esp_supplicant/src/crypto/tls_mbedtls.c @@ -7,7 +7,7 @@ #include "utils/includes.h" #include "utils/common.h" -#include "tls/tls.h" +#include "crypto/tls.h" #include "crypto/sha1.h" #include "crypto/md5.h" #include "crypto/sha256.h" @@ -608,7 +608,7 @@ exit: return ret; } -void *tls_init() +void *tls_init(const struct tls_config *conf) { tls_instance_count++; return &tls_instance_count; @@ -660,7 +660,7 @@ int tls_connection_established(void *tls_ctx, struct tls_connection *conn) return 0; } -int tls_global_set_verify(void *tls_ctx, int check_crl) +int tls_global_set_verify(void *tls_ctx, int check_crl, int strict) { wpa_printf(MSG_INFO, "TLS: global settings are not supported"); return -1; diff --git a/components/wpa_supplicant/esp_supplicant/src/esp_wpa2.c b/components/wpa_supplicant/esp_supplicant/src/esp_wpa2.c index a1dd2e25f5..b85b263ab0 100644 --- a/components/wpa_supplicant/esp_supplicant/src/esp_wpa2.c +++ b/components/wpa_supplicant/esp_supplicant/src/esp_wpa2.c @@ -25,7 +25,7 @@ #include "crypto/crypto.h" #include "utils/ext_password.h" -#include "tls/tls.h" +#include "crypto/tls.h" #include "eap_peer/eap_i.h" #include "eap_peer/eap_config.h" #include "eap_peer/eap.h" @@ -717,7 +717,7 @@ static int eap_peer_sm_init(void) goto _err; } - sm->ssl_ctx = tls_init(); + sm->ssl_ctx = tls_init(NULL); if (sm->ssl_ctx == NULL) { wpa_printf(MSG_WARNING, "SSL: Failed to initialize TLS context."); ret = ESP_FAIL; diff --git a/components/wpa_supplicant/include/utils/wpa_debug.h b/components/wpa_supplicant/include/utils/wpa_debug.h index a870385771..4664975e15 100644 --- a/components/wpa_supplicant/include/utils/wpa_debug.h +++ b/components/wpa_supplicant/include/utils/wpa_debug.h @@ -65,12 +65,12 @@ void wpa_debug_print_timestamp(void); #define wpa_dbg(ctx, level, fmt, args...) wpa_printf(level, fmt, ##args) void wpa_dump_mem(char* desc, uint8_t *addr, uint16_t len); -static inline void wpa_hexdump_ascii(int level, const char *title, const u8 *buf, size_t len) +static inline void wpa_hexdump_ascii(int level, const char *title, const void *buf, size_t len) { } -static inline void wpa_hexdump_ascii_key(int level, const char *title, const u8 *buf, size_t len) +static inline void wpa_hexdump_ascii_key(int level, const char *title, const void *buf, size_t len) { } @@ -128,7 +128,7 @@ static inline void wpa_hexdump_buf_key(int level, const char *title, * the hex numbers and ASCII characters (for printable range) are shown. 16 * bytes per line will be shown. */ -void wpa_hexdump_ascii(int level, const char *title, const u8 *buf, +void wpa_hexdump_ascii(int level, const char *title, const void *buf, size_t len); /** @@ -145,7 +145,7 @@ void wpa_hexdump_ascii(int level, const char *title, const u8 *buf, * bytes per line will be shown. This works like wpa_hexdump_ascii(), but by * default, does not include secret keys (passwords, etc.) in debug output. */ -void wpa_hexdump_ascii_key(int level, const char *title, const u8 *buf, +void wpa_hexdump_ascii_key(int level, const char *title, const void *buf, size_t len); #else #define wpa_printf(level,fmt, args...) do {} while(0) diff --git a/components/wpa_supplicant/port/include/os.h b/components/wpa_supplicant/port/include/os.h index ce9aa559a4..4a5602ddd6 100644 --- a/components/wpa_supplicant/port/include/os.h +++ b/components/wpa_supplicant/port/include/os.h @@ -185,7 +185,11 @@ int os_unsetenv(const char *name); * binary and text files can be read with this function. The caller is * responsible for freeing the returned buffer with os_free(). */ -char * os_readfile(const char *name, size_t *len); +/* We don't support file reading support */ +static inline char *os_readfile(const char *name, size_t *len) +{ + return NULL; +} /* * The following functions are wrapper for standard ANSI C or POSIX functions. @@ -250,7 +254,6 @@ char * ets_strdup(const char *s); #define os_memcmp_const(s1, s2, n) memcmp((s1), (s2), (n)) #endif - #ifndef os_strlen #define os_strlen(s) strlen(s) #endif @@ -316,7 +319,7 @@ static inline void * os_realloc_array(void *ptr, size_t nmemb, size_t size) return os_realloc(ptr, nmemb * size); } -#ifdef USE_MBEDTLS_CRYPTO +#ifdef CONFIG_CRYPTO_MBEDTLS void forced_memzero(void *ptr, size_t len); #else /* Try to prevent most compilers from optimizing out clearing of memory that diff --git a/components/wpa_supplicant/port/include/supplicant_opt.h b/components/wpa_supplicant/port/include/supplicant_opt.h index 3de8c9cc86..74bd00d30d 100644 --- a/components/wpa_supplicant/port/include/supplicant_opt.h +++ b/components/wpa_supplicant/port/include/supplicant_opt.h @@ -9,14 +9,6 @@ #include "sdkconfig.h" -#if CONFIG_WPA_MBEDTLS_CRYPTO -#define USE_MBEDTLS_CRYPTO 1 -#else -#define CONFIG_TLS_INTERNAL_CLIENT -#define CONFIG_CRYPTO_INTERNAL -#define CONFIG_TLSV12 -#endif - #if CONFIG_WPA_DEBUG_PRINT #define DEBUG_PRINT #endif diff --git a/components/wpa_supplicant/port/os_xtensa.c b/components/wpa_supplicant/port/os_xtensa.c index db1991f0d9..bc5cbf72d8 100644 --- a/components/wpa_supplicant/port/os_xtensa.c +++ b/components/wpa_supplicant/port/os_xtensa.c @@ -61,7 +61,7 @@ void os_sleep(os_time_t sec, os_time_t usec) } } -#ifdef USE_MBEDTLS_CRYPTO +#ifdef CONFIG_CRYPTO_MBEDTLS void forced_memzero(void *ptr, size_t len) { mbedtls_platform_zeroize(ptr, len); diff --git a/components/wpa_supplicant/src/crypto/crypto_internal.c b/components/wpa_supplicant/src/crypto/crypto_internal.c index 3d14babe5a..aad40af16e 100644 --- a/components/wpa_supplicant/src/crypto/crypto_internal.c +++ b/components/wpa_supplicant/src/crypto/crypto_internal.c @@ -11,6 +11,8 @@ #include "common.h" #include "crypto.h" #include "sha256_i.h" +#include "sha384_i.h" +#include "sha512_i.h" #include "sha1_i.h" #include "md5_i.h" diff --git a/components/wpa_supplicant/src/crypto/crypto_ops.c b/components/wpa_supplicant/src/crypto/crypto_ops.c index d4aff08787..128af4e1ae 100644 --- a/components/wpa_supplicant/src/crypto/crypto_ops.c +++ b/components/wpa_supplicant/src/crypto/crypto_ops.c @@ -48,7 +48,7 @@ static int esp_aes_gmac(const u8 *key, size_t key_len, const u8 *iv, size_t iv_l /* * This structure is used to set the cyrpto callback function for station to connect when in security mode. - * These functions either call MbedTLS API's if USE_MBEDTLS_CRYPTO flag is set through Kconfig, or native + * These functions either call MbedTLS API's if CONFIG_CRYPTO_MBEDTLS flag is set through Kconfig, or native * API's otherwise. We recommend setting the flag since MbedTLS API's utilize hardware acceleration while * native API's are use software implementations. */ diff --git a/components/wpa_supplicant/src/crypto/libtommath.h b/components/wpa_supplicant/src/crypto/libtommath.h deleted file mode 100644 index b5f1a0eb52..0000000000 --- a/components/wpa_supplicant/src/crypto/libtommath.h +++ /dev/null @@ -1,3441 +0,0 @@ -/* - * Minimal code for RSA support from LibTomMath 0.41 - * http://libtom.org/ - * http://libtom.org/files/ltm-0.41.tar.bz2 - * This library was released in public domain by Tom St Denis. - * - * The combination in this file may not use all of the optimized algorithms - * from LibTomMath and may be considerable slower than the LibTomMath with its - * default settings. The main purpose of having this version here is to make it - * easier to build bignum.c wrapper without having to install and build an - * external library. - * - * If CONFIG_INTERNAL_LIBTOMMATH is defined, bignum.c includes this - * libtommath.c file instead of using the external LibTomMath library. - */ -//#include "c_types.h" -#include "os.h" -#include "stdarg.h" - - -#ifndef CHAR_BIT -#define CHAR_BIT 8 -#endif - -#define BN_MP_INVMOD_C -#define BN_S_MP_EXPTMOD_C /* Note: #undef in tommath_superclass.h; this would - * require BN_MP_EXPTMOD_FAST_C instead */ -#define BN_S_MP_MUL_DIGS_C -#define BN_MP_INVMOD_SLOW_C -#define BN_S_MP_SQR_C -#define BN_S_MP_MUL_HIGH_DIGS_C /* Note: #undef in tommath_superclass.h; this - * would require other than mp_reduce */ - -#ifdef LTM_FAST - -/* Use faster div at the cost of about 1 kB */ -#define BN_MP_MUL_D_C - -/* Include faster exptmod (Montgomery) at the cost of about 2.5 kB in code */ -#define BN_MP_EXPTMOD_FAST_C -#define BN_MP_MONTGOMERY_SETUP_C -#define BN_FAST_MP_MONTGOMERY_REDUCE_C -#define BN_MP_MONTGOMERY_CALC_NORMALIZATION_C -#define BN_MP_MUL_2_C - -/* Include faster sqr at the cost of about 0.5 kB in code */ -#define BN_FAST_S_MP_SQR_C - -#else /* LTM_FAST */ - -#define BN_MP_DIV_SMALL -#define BN_MP_INIT_MULTI_C -#define BN_MP_CLEAR_MULTI_C -#define BN_MP_ABS_C -#endif /* LTM_FAST */ - -/* Current uses do not require support for negative exponent in exptmod, so we - * can save about 1.5 kB in leaving out invmod. */ -#define LTM_NO_NEG_EXP - -/* from tommath.h */ - -#ifndef MIN - #define MIN(x,y) ((x)<(y)?(x):(y)) -#endif - -#ifndef MAX - #define MAX(x,y) ((x)>(y)?(x):(y)) -#endif - -#define OPT_CAST(x) (x *) - -typedef unsigned long mp_digit; -typedef u64 mp_word; - -#define DIGIT_BIT 28 -#define MP_28BIT - - -#define XMALLOC os_malloc -#define XFREE os_free -#define XREALLOC os_realloc - - -#define MP_MASK ((((mp_digit)1)<<((mp_digit)DIGIT_BIT))-((mp_digit)1)) - -#define MP_LT -1 /* less than */ -#define MP_EQ 0 /* equal to */ -#define MP_GT 1 /* greater than */ - -#define MP_ZPOS 0 /* positive integer */ -#define MP_NEG 1 /* negative */ - -#define MP_OKAY 0 /* ok result */ -#define MP_MEM -2 /* out of mem */ -#define MP_VAL -3 /* invalid input */ - -#define MP_YES 1 /* yes response */ -#define MP_NO 0 /* no response */ - -typedef int mp_err; - -/* define this to use lower memory usage routines (exptmods mostly) */ -#define MP_LOW_MEM - -/* default precision */ -#ifndef MP_PREC - #ifndef MP_LOW_MEM - #define MP_PREC 32 /* default digits of precision */ - #else - #define MP_PREC 8 /* default digits of precision */ - #endif -#endif - -/* size of comba arrays, should be at least 2 * 2**(BITS_PER_WORD - BITS_PER_DIGIT*2) */ -#define MP_WARRAY (1 << (sizeof(mp_word) * CHAR_BIT - 2 * DIGIT_BIT + 1)) - -/* the infamous mp_int structure */ -typedef struct { - int used, alloc, sign; - mp_digit *dp; -} mp_int; - - -/* ---> Basic Manipulations <--- */ -#define mp_iszero(a) (((a)->used == 0) ? MP_YES : MP_NO) -#define mp_iseven(a) (((a)->used > 0 && (((a)->dp[0] & 1) == 0)) ? MP_YES : MP_NO) -#define mp_isodd(a) (((a)->used > 0 && (((a)->dp[0] & 1) == 1)) ? MP_YES : MP_NO) - - -/* prototypes for copied functions */ -#define s_mp_mul(a, b, c) s_mp_mul_digs(a, b, c, (a)->used + (b)->used + 1) -static int s_mp_exptmod(mp_int * G, mp_int * X, mp_int * P, mp_int * Y, int redmode); -static int s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs); -static int s_mp_sqr(mp_int * a, mp_int * b); -static int s_mp_mul_high_digs(mp_int * a, mp_int * b, mp_int * c, int digs); - -static int fast_s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs); - -#ifdef BN_MP_INIT_MULTI_C -static int mp_init_multi(mp_int *mp, ...); -#endif -#ifdef BN_MP_CLEAR_MULTI_C -static void mp_clear_multi(mp_int *mp, ...); -#endif -static int mp_lshd(mp_int * a, int b); -static void mp_set(mp_int * a, mp_digit b); -static void mp_clamp(mp_int * a); -static void mp_exch(mp_int * a, mp_int * b); -static void mp_rshd(mp_int * a, int b); -static void mp_zero(mp_int * a); -static int mp_mod_2d(mp_int * a, int b, mp_int * c); -static int mp_div_2d(mp_int * a, int b, mp_int * c, mp_int * d); -static int mp_init_copy(mp_int * a, mp_int * b); -static int mp_mul_2d(mp_int * a, int b, mp_int * c); -#ifndef LTM_NO_NEG_EXP -static int mp_div_2(mp_int * a, mp_int * b); -static int mp_invmod(mp_int * a, mp_int * b, mp_int * c); -static int mp_invmod_slow(mp_int * a, mp_int * b, mp_int * c); -#endif /* LTM_NO_NEG_EXP */ -static int mp_copy(mp_int * a, mp_int * b); -static int mp_count_bits(mp_int * a); -static int mp_div(mp_int * a, mp_int * b, mp_int * c, mp_int * d); -static int mp_mod(mp_int * a, mp_int * b, mp_int * c); -static int mp_grow(mp_int * a, int size); -static int mp_cmp_mag(mp_int * a, mp_int * b); -#ifdef BN_MP_ABS_C -static int mp_abs(mp_int * a, mp_int * b); -#endif -static int mp_sqr(mp_int * a, mp_int * b); -static int mp_reduce_2k_l(mp_int *a, mp_int *n, mp_int *d); -static int mp_reduce_2k_setup_l(mp_int *a, mp_int *d); -static int mp_2expt(mp_int * a, int b); -static int mp_reduce_setup(mp_int * a, mp_int * b); -static int mp_reduce(mp_int * x, mp_int * m, mp_int * mu); -static int mp_init_size(mp_int * a, int size); -#ifdef BN_MP_EXPTMOD_FAST_C -static int mp_exptmod_fast (mp_int * G, mp_int * X, mp_int * P, mp_int * Y, int redmode); -#endif /* BN_MP_EXPTMOD_FAST_C */ -#ifdef BN_FAST_S_MP_SQR_C -static int fast_s_mp_sqr (mp_int * a, mp_int * b); -#endif /* BN_FAST_S_MP_SQR_C */ -#ifdef BN_MP_MUL_D_C -static int mp_mul_d (mp_int * a, mp_digit b, mp_int * c); -#endif /* BN_MP_MUL_D_C */ - - - -/* functions from bn_.c */ - - -/* reverse an array, used for radix code */ -static void -bn_reverse (unsigned char *s, int len) -{ - int ix, iy; - unsigned char t; - - ix = 0; - iy = len - 1; - while (ix < iy) { - t = s[ix]; - s[ix] = s[iy]; - s[iy] = t; - ++ix; - --iy; - } -} - - -/* low level addition, based on HAC pp.594, Algorithm 14.7 */ -static int -s_mp_add (mp_int * a, mp_int * b, mp_int * c) -{ - mp_int *x; - int olduse, res, min, max; - - /* find sizes, we let |a| <= |b| which means we have to sort - * them. "x" will point to the input with the most digits - */ - if (a->used > b->used) { - min = b->used; - max = a->used; - x = a; - } else { - min = a->used; - max = b->used; - x = b; - } - - /* init result */ - if (c->alloc < max + 1) { - if ((res = mp_grow (c, max + 1)) != MP_OKAY) { - return res; - } - } - - /* get old used digit count and set new one */ - olduse = c->used; - c->used = max + 1; - - { - register mp_digit u, *tmpa, *tmpb, *tmpc; - register int i; - - /* alias for digit pointers */ - - /* first input */ - tmpa = a->dp; - - /* second input */ - tmpb = b->dp; - - /* destination */ - tmpc = c->dp; - - /* zero the carry */ - u = 0; - for (i = 0; i < min; i++) { - /* Compute the sum at one digit, T[i] = A[i] + B[i] + U */ - *tmpc = *tmpa++ + *tmpb++ + u; - - /* U = carry bit of T[i] */ - u = *tmpc >> ((mp_digit)DIGIT_BIT); - - /* take away carry bit from T[i] */ - *tmpc++ &= MP_MASK; - } - - /* now copy higher words if any, that is in A+B - * if A or B has more digits add those in - */ - if (min != max) { - for (; i < max; i++) { - /* T[i] = X[i] + U */ - *tmpc = x->dp[i] + u; - - /* U = carry bit of T[i] */ - u = *tmpc >> ((mp_digit)DIGIT_BIT); - - /* take away carry bit from T[i] */ - *tmpc++ &= MP_MASK; - } - } - - /* add carry */ - *tmpc++ = u; - - /* clear digits above oldused */ - for (i = c->used; i < olduse; i++) { - *tmpc++ = 0; - } - } - - mp_clamp (c); - return MP_OKAY; -} - - -/* low level subtraction (assumes |a| > |b|), HAC pp.595 Algorithm 14.9 */ -static int -s_mp_sub (mp_int * a, mp_int * b, mp_int * c) -{ - int olduse, res, min, max; - - /* find sizes */ - min = b->used; - max = a->used; - - /* init result */ - if (c->alloc < max) { - if ((res = mp_grow (c, max)) != MP_OKAY) { - return res; - } - } - olduse = c->used; - c->used = max; - - { - register mp_digit u, *tmpa, *tmpb, *tmpc; - register int i; - - /* alias for digit pointers */ - tmpa = a->dp; - tmpb = b->dp; - tmpc = c->dp; - - /* set carry to zero */ - u = 0; - for (i = 0; i < min; i++) { - /* T[i] = A[i] - B[i] - U */ - *tmpc = *tmpa++ - *tmpb++ - u; - - /* U = carry bit of T[i] - * Note this saves performing an AND operation since - * if a carry does occur it will propagate all the way to the - * MSB. As a result a single shift is enough to get the carry - */ - u = *tmpc >> ((mp_digit)(CHAR_BIT * sizeof (mp_digit) - 1)); - - /* Clear carry from T[i] */ - *tmpc++ &= MP_MASK; - } - - /* now copy higher words if any, e.g. if A has more digits than B */ - for (; i < max; i++) { - /* T[i] = A[i] - U */ - *tmpc = *tmpa++ - u; - - /* U = carry bit of T[i] */ - u = *tmpc >> ((mp_digit)(CHAR_BIT * sizeof (mp_digit) - 1)); - - /* Clear carry from T[i] */ - *tmpc++ &= MP_MASK; - } - - /* clear digits above used (since we may not have grown result above) */ - for (i = c->used; i < olduse; i++) { - *tmpc++ = 0; - } - } - - mp_clamp (c); - return MP_OKAY; -} - - -/* init a new mp_int */ -static int -mp_init (mp_int * a) -{ - int i; - - /* allocate memory required and clear it */ - a->dp = OPT_CAST(mp_digit) XMALLOC (sizeof (mp_digit) * MP_PREC); - if (a->dp == NULL) { - return MP_MEM; - } - - /* set the digits to zero */ - for (i = 0; i < MP_PREC; i++) { - a->dp[i] = 0; - } - - /* set the used to zero, allocated digits to the default precision - * and sign to positive */ - a->used = 0; - a->alloc = MP_PREC; - a->sign = MP_ZPOS; - - return MP_OKAY; -} - - -/* clear one (frees) */ -static void -mp_clear (mp_int * a) -{ - int i; - - /* only do anything if a hasn't been freed previously */ - if (a->dp != NULL) { - /* first zero the digits */ - for (i = 0; i < a->used; i++) { - a->dp[i] = 0; - } - - /* free ram */ - XFREE(a->dp); - - /* reset members to make debugging easier */ - a->dp = NULL; - a->alloc = a->used = 0; - a->sign = MP_ZPOS; - } -} - - -/* high level addition (handles signs) */ -static int -mp_add (mp_int * a, mp_int * b, mp_int * c) -{ - int sa, sb, res; - - /* get sign of both inputs */ - sa = a->sign; - sb = b->sign; - - /* handle two cases, not four */ - if (sa == sb) { - /* both positive or both negative */ - /* add their magnitudes, copy the sign */ - c->sign = sa; - res = s_mp_add (a, b, c); - } else { - /* one positive, the other negative */ - /* subtract the one with the greater magnitude from */ - /* the one of the lesser magnitude. The result gets */ - /* the sign of the one with the greater magnitude. */ - if (mp_cmp_mag (a, b) == MP_LT) { - c->sign = sb; - res = s_mp_sub (b, a, c); - } else { - c->sign = sa; - res = s_mp_sub (a, b, c); - } - } - return res; -} - - -/* high level subtraction (handles signs) */ -static int -mp_sub (mp_int * a, mp_int * b, mp_int * c) -{ - int sa, sb, res; - - sa = a->sign; - sb = b->sign; - - if (sa != sb) { - /* subtract a negative from a positive, OR */ - /* subtract a positive from a negative. */ - /* In either case, ADD their magnitudes, */ - /* and use the sign of the first number. */ - c->sign = sa; - res = s_mp_add (a, b, c); - } else { - /* subtract a positive from a positive, OR */ - /* subtract a negative from a negative. */ - /* First, take the difference between their */ - /* magnitudes, then... */ - if (mp_cmp_mag (a, b) != MP_LT) { - /* Copy the sign from the first */ - c->sign = sa; - /* The first has a larger or equal magnitude */ - res = s_mp_sub (a, b, c); - } else { - /* The result has the *opposite* sign from */ - /* the first number. */ - c->sign = (sa == MP_ZPOS) ? MP_NEG : MP_ZPOS; - /* The second has a larger magnitude */ - res = s_mp_sub (b, a, c); - } - } - return res; -} - - -/* high level multiplication (handles sign) */ -static int -mp_mul (mp_int * a, mp_int * b, mp_int * c) -{ - int res, neg; - neg = (a->sign == b->sign) ? MP_ZPOS : MP_NEG; - - /* use Toom-Cook? */ -#ifdef BN_MP_TOOM_MUL_C - if (MIN (a->used, b->used) >= TOOM_MUL_CUTOFF) { - res = mp_toom_mul(a, b, c); - } else -#endif -#ifdef BN_MP_KARATSUBA_MUL_C - /* use Karatsuba? */ - if (MIN (a->used, b->used) >= KARATSUBA_MUL_CUTOFF) { - res = mp_karatsuba_mul (a, b, c); - } else -#endif - { - /* can we use the fast multiplier? - * - * The fast multiplier can be used if the output will - * have less than MP_WARRAY digits and the number of - * digits won't affect carry propagation - */ -#ifdef BN_FAST_S_MP_MUL_DIGS_C - int digs = a->used + b->used + 1; - - if ((digs < MP_WARRAY) && - MIN(a->used, b->used) <= - (1 << ((CHAR_BIT * sizeof (mp_word)) - (2 * DIGIT_BIT)))) { - res = fast_s_mp_mul_digs (a, b, c, digs); - } else -#endif -#ifdef BN_S_MP_MUL_DIGS_C - res = s_mp_mul (a, b, c); /* uses s_mp_mul_digs */ -#else -#error mp_mul could fail - res = MP_VAL; -#endif - - } - c->sign = (c->used > 0) ? neg : MP_ZPOS; - return res; -} - - -/* d = a * b (mod c) */ -static int -mp_mulmod (mp_int * a, mp_int * b, mp_int * c, mp_int * d) -{ - int res; - mp_int t; - - if ((res = mp_init (&t)) != MP_OKAY) { - return res; - } - - if ((res = mp_mul (a, b, &t)) != MP_OKAY) { - mp_clear (&t); - return res; - } - res = mp_mod (&t, c, d); - mp_clear (&t); - return res; -} - - -/* c = a mod b, 0 <= c < b */ -static int -mp_mod (mp_int * a, mp_int * b, mp_int * c) -{ - mp_int t; - int res; - - if ((res = mp_init (&t)) != MP_OKAY) { - return res; - } - - if ((res = mp_div (a, b, NULL, &t)) != MP_OKAY) { - mp_clear (&t); - return res; - } - - if (t.sign != b->sign) { - res = mp_add (b, &t, c); - } else { - res = MP_OKAY; - mp_exch (&t, c); - } - - mp_clear (&t); - return res; -} - - -/* this is a shell function that calls either the normal or Montgomery - * exptmod functions. Originally the call to the montgomery code was - * embedded in the normal function but that wasted a lot of stack space - * for nothing (since 99% of the time the Montgomery code would be called) - */ -static int -mp_exptmod (mp_int * G, mp_int * X, mp_int * P, mp_int * Y) -{ - int dr; - - /* modulus P must be positive */ - if (P->sign == MP_NEG) { - return MP_VAL; - } - - /* if exponent X is negative we have to recurse */ - if (X->sign == MP_NEG) { -#ifdef LTM_NO_NEG_EXP - return MP_VAL; -#else /* LTM_NO_NEG_EXP */ -#ifdef BN_MP_INVMOD_C - mp_int tmpG, tmpX; - int err; - - /* first compute 1/G mod P */ - if ((err = mp_init(&tmpG)) != MP_OKAY) { - return err; - } - if ((err = mp_invmod(G, P, &tmpG)) != MP_OKAY) { - mp_clear(&tmpG); - return err; - } - - /* now get |X| */ - if ((err = mp_init(&tmpX)) != MP_OKAY) { - mp_clear(&tmpG); - return err; - } - if ((err = mp_abs(X, &tmpX)) != MP_OKAY) { - mp_clear_multi(&tmpG, &tmpX, NULL); - return err; - } - - /* and now compute (1/G)**|X| instead of G**X [X < 0] */ - err = mp_exptmod(&tmpG, &tmpX, P, Y); - mp_clear_multi(&tmpG, &tmpX, NULL); - return err; -#else -#error mp_exptmod would always fail - /* no invmod */ - return MP_VAL; -#endif -#endif /* LTM_NO_NEG_EXP */ - } - -/* modified diminished radix reduction */ -#if defined(BN_MP_REDUCE_IS_2K_L_C) && defined(BN_MP_REDUCE_2K_L_C) && defined(BN_S_MP_EXPTMOD_C) - if (mp_reduce_is_2k_l(P) == MP_YES) { - return s_mp_exptmod(G, X, P, Y, 1); - } -#endif - -#ifdef BN_MP_DR_IS_MODULUS_C - /* is it a DR modulus? */ - dr = mp_dr_is_modulus(P); -#else - /* default to no */ - dr = 0; -#endif - -#ifdef BN_MP_REDUCE_IS_2K_C - /* if not, is it a unrestricted DR modulus? */ - if (dr == 0) { - dr = mp_reduce_is_2k(P) << 1; - } -#endif - - /* if the modulus is odd or dr != 0 use the montgomery method */ -#ifdef BN_MP_EXPTMOD_FAST_C - if (mp_isodd (P) == 1 || dr != 0) { - return mp_exptmod_fast (G, X, P, Y, dr); - } else { -#endif -#ifdef BN_S_MP_EXPTMOD_C - (void) dr; - /* otherwise use the generic Barrett reduction technique */ - return s_mp_exptmod (G, X, P, Y, 0); -#else -#error mp_exptmod could fail - /* no exptmod for evens */ - return MP_VAL; -#endif -#ifdef BN_MP_EXPTMOD_FAST_C - } -#endif -} - - -/* compare two ints (signed)*/ -static int -mp_cmp (mp_int * a, mp_int * b) -{ - /* compare based on sign */ - if (a->sign != b->sign) { - if (a->sign == MP_NEG) { - return MP_LT; - } else { - return MP_GT; - } - } - - /* compare digits */ - if (a->sign == MP_NEG) { - /* if negative compare opposite direction */ - return mp_cmp_mag(b, a); - } else { - return mp_cmp_mag(a, b); - } -} - - -/* compare a digit */ -static int -mp_cmp_d(mp_int * a, mp_digit b) -{ - /* compare based on sign */ - if (a->sign == MP_NEG) { - return MP_LT; - } - - /* compare based on magnitude */ - if (a->used > 1) { - return MP_GT; - } - - /* compare the only digit of a to b */ - if (a->dp[0] > b) { - return MP_GT; - } else if (a->dp[0] < b) { - return MP_LT; - } else { - return MP_EQ; - } -} - - -#ifndef LTM_NO_NEG_EXP -/* hac 14.61, pp608 */ -static int -mp_invmod (mp_int * a, mp_int * b, mp_int * c) -{ - /* b cannot be negative */ - if (b->sign == MP_NEG || mp_iszero(b) == 1) { - return MP_VAL; - } - -#ifdef BN_FAST_MP_INVMOD_C - /* if the modulus is odd we can use a faster routine instead */ - if (mp_isodd (b) == 1) { - return fast_mp_invmod (a, b, c); - } -#endif - -#ifdef BN_MP_INVMOD_SLOW_C - return mp_invmod_slow(a, b, c); -#endif - -#ifndef BN_FAST_MP_INVMOD_C -#ifndef BN_MP_INVMOD_SLOW_C -#error mp_invmod would always fail -#endif -#endif - return MP_VAL; -} -#endif /* LTM_NO_NEG_EXP */ - - -/* get the size for an unsigned equivalent */ -static int -mp_unsigned_bin_size (mp_int * a) -{ - int size = mp_count_bits (a); - return (size / 8 + ((size & 7) != 0 ? 1 : 0)); -} - - -#ifndef LTM_NO_NEG_EXP -/* hac 14.61, pp608 */ -static int -mp_invmod_slow (mp_int * a, mp_int * b, mp_int * c) -{ - mp_int x, y, u, v, A, B, C, D; - int res; - - /* b cannot be negative */ - if (b->sign == MP_NEG || mp_iszero(b) == 1) { - return MP_VAL; - } - - /* init temps */ - if ((res = mp_init_multi(&x, &y, &u, &v, - &A, &B, &C, &D, NULL)) != MP_OKAY) { - return res; - } - - /* x = a, y = b */ - if ((res = mp_mod(a, b, &x)) != MP_OKAY) { - goto LBL_ERR; - } - if ((res = mp_copy (b, &y)) != MP_OKAY) { - goto LBL_ERR; - } - - /* 2. [modified] if x,y are both even then return an error! */ - if (mp_iseven (&x) == 1 && mp_iseven (&y) == 1) { - res = MP_VAL; - goto LBL_ERR; - } - - /* 3. u=x, v=y, A=1, B=0, C=0,D=1 */ - if ((res = mp_copy (&x, &u)) != MP_OKAY) { - goto LBL_ERR; - } - if ((res = mp_copy (&y, &v)) != MP_OKAY) { - goto LBL_ERR; - } - mp_set (&A, 1); - mp_set (&D, 1); - -top: - /* 4. while u is even do */ - while (mp_iseven (&u) == 1) { - /* 4.1 u = u/2 */ - if ((res = mp_div_2 (&u, &u)) != MP_OKAY) { - goto LBL_ERR; - } - /* 4.2 if A or B is odd then */ - if (mp_isodd (&A) == 1 || mp_isodd (&B) == 1) { - /* A = (A+y)/2, B = (B-x)/2 */ - if ((res = mp_add (&A, &y, &A)) != MP_OKAY) { - goto LBL_ERR; - } - if ((res = mp_sub (&B, &x, &B)) != MP_OKAY) { - goto LBL_ERR; - } - } - /* A = A/2, B = B/2 */ - if ((res = mp_div_2 (&A, &A)) != MP_OKAY) { - goto LBL_ERR; - } - if ((res = mp_div_2 (&B, &B)) != MP_OKAY) { - goto LBL_ERR; - } - } - - /* 5. while v is even do */ - while (mp_iseven (&v) == 1) { - /* 5.1 v = v/2 */ - if ((res = mp_div_2 (&v, &v)) != MP_OKAY) { - goto LBL_ERR; - } - /* 5.2 if C or D is odd then */ - if (mp_isodd (&C) == 1 || mp_isodd (&D) == 1) { - /* C = (C+y)/2, D = (D-x)/2 */ - if ((res = mp_add (&C, &y, &C)) != MP_OKAY) { - goto LBL_ERR; - } - if ((res = mp_sub (&D, &x, &D)) != MP_OKAY) { - goto LBL_ERR; - } - } - /* C = C/2, D = D/2 */ - if ((res = mp_div_2 (&C, &C)) != MP_OKAY) { - goto LBL_ERR; - } - if ((res = mp_div_2 (&D, &D)) != MP_OKAY) { - goto LBL_ERR; - } - } - - /* 6. if u >= v then */ - if (mp_cmp (&u, &v) != MP_LT) { - /* u = u - v, A = A - C, B = B - D */ - if ((res = mp_sub (&u, &v, &u)) != MP_OKAY) { - goto LBL_ERR; - } - - if ((res = mp_sub (&A, &C, &A)) != MP_OKAY) { - goto LBL_ERR; - } - - if ((res = mp_sub (&B, &D, &B)) != MP_OKAY) { - goto LBL_ERR; - } - } else { - /* v - v - u, C = C - A, D = D - B */ - if ((res = mp_sub (&v, &u, &v)) != MP_OKAY) { - goto LBL_ERR; - } - - if ((res = mp_sub (&C, &A, &C)) != MP_OKAY) { - goto LBL_ERR; - } - - if ((res = mp_sub (&D, &B, &D)) != MP_OKAY) { - goto LBL_ERR; - } - } - - /* if not zero goto step 4 */ - if (mp_iszero (&u) == 0) - goto top; - - /* now a = C, b = D, gcd == g*v */ - - /* if v != 1 then there is no inverse */ - if (mp_cmp_d (&v, 1) != MP_EQ) { - res = MP_VAL; - goto LBL_ERR; - } - - /* if its too low */ - while (mp_cmp_d(&C, 0) == MP_LT) { - if ((res = mp_add(&C, b, &C)) != MP_OKAY) { - goto LBL_ERR; - } - } - - /* too big */ - while (mp_cmp_mag(&C, b) != MP_LT) { - if ((res = mp_sub(&C, b, &C)) != MP_OKAY) { - goto LBL_ERR; - } - } - - /* C is now the inverse */ - mp_exch (&C, c); - res = MP_OKAY; -LBL_ERR:mp_clear_multi (&x, &y, &u, &v, &A, &B, &C, &D, NULL); - return res; -} -#endif /* LTM_NO_NEG_EXP */ - - -/* compare maginitude of two ints (unsigned) */ -static int -mp_cmp_mag (mp_int * a, mp_int * b) -{ - int n; - mp_digit *tmpa, *tmpb; - - /* compare based on # of non-zero digits */ - if (a->used > b->used) { - return MP_GT; - } - - if (a->used < b->used) { - return MP_LT; - } - - /* alias for a */ - tmpa = a->dp + (a->used - 1); - - /* alias for b */ - tmpb = b->dp + (a->used - 1); - - /* compare based on digits */ - for (n = 0; n < a->used; ++n, --tmpa, --tmpb) { - if (*tmpa > *tmpb) { - return MP_GT; - } - - if (*tmpa < *tmpb) { - return MP_LT; - } - } - return MP_EQ; -} - - -/* reads a unsigned char array, assumes the msb is stored first [big endian] */ -static int -mp_read_unsigned_bin (mp_int * a, const unsigned char *b, int c) -{ - int res; - - /* make sure there are at least two digits */ - if (a->alloc < 2) { - if ((res = mp_grow(a, 2)) != MP_OKAY) { - return res; - } - } - - /* zero the int */ - mp_zero (a); - - /* read the bytes in */ - while (c-- > 0) { - if ((res = mp_mul_2d (a, 8, a)) != MP_OKAY) { - return res; - } - -#ifndef MP_8BIT - a->dp[0] |= *b++; - a->used += 1; -#else - a->dp[0] = (*b & MP_MASK); - a->dp[1] |= ((*b++ >> 7U) & 1); - a->used += 2; -#endif - } - mp_clamp (a); - return MP_OKAY; -} - - -/* store in unsigned [big endian] format */ -static int -mp_to_unsigned_bin (mp_int * a, unsigned char *b) -{ - int x, res; - mp_int t; - - if ((res = mp_init_copy (&t, a)) != MP_OKAY) { - return res; - } - - x = 0; - while (mp_iszero (&t) == 0) { -#ifndef MP_8BIT - b[x++] = (unsigned char) (t.dp[0] & 255); -#else - b[x++] = (unsigned char) (t.dp[0] | ((t.dp[1] & 0x01) << 7)); -#endif - if ((res = mp_div_2d (&t, 8, &t, NULL)) != MP_OKAY) { - mp_clear (&t); - return res; - } - } - bn_reverse (b, x); - mp_clear (&t); - return MP_OKAY; -} - - -/* shift right by a certain bit count (store quotient in c, optional remainder in d) */ -static int -mp_div_2d (mp_int * a, int b, mp_int * c, mp_int * d) -{ - mp_digit D, r, rr; - int x, res; - mp_int t; - - - /* if the shift count is <= 0 then we do no work */ - if (b <= 0) { - res = mp_copy (a, c); - if (d != NULL) { - mp_zero (d); - } - return res; - } - - if ((res = mp_init (&t)) != MP_OKAY) { - return res; - } - - /* get the remainder */ - if (d != NULL) { - if ((res = mp_mod_2d (a, b, &t)) != MP_OKAY) { - mp_clear (&t); - return res; - } - } - - /* copy */ - if ((res = mp_copy (a, c)) != MP_OKAY) { - mp_clear (&t); - return res; - } - - /* shift by as many digits in the bit count */ - if (b >= (int)DIGIT_BIT) { - mp_rshd (c, b / DIGIT_BIT); - } - - /* shift any bit count < DIGIT_BIT */ - D = (mp_digit) (b % DIGIT_BIT); - if (D != 0) { - register mp_digit *tmpc, mask, shift; - - /* mask */ - mask = (((mp_digit)1) << D) - 1; - - /* shift for lsb */ - shift = DIGIT_BIT - D; - - /* alias */ - tmpc = c->dp + (c->used - 1); - - /* carry */ - r = 0; - for (x = c->used - 1; x >= 0; x--) { - /* get the lower bits of this word in a temp */ - rr = *tmpc & mask; - - /* shift the current word and mix in the carry bits from the previous word */ - *tmpc = (*tmpc >> D) | (r << shift); - --tmpc; - - /* set the carry to the carry bits of the current word found above */ - r = rr; - } - } - mp_clamp (c); - if (d != NULL) { - mp_exch (&t, d); - } - mp_clear (&t); - return MP_OKAY; -} - - -static int -mp_init_copy (mp_int * a, mp_int * b) -{ - int res; - - if ((res = mp_init (a)) != MP_OKAY) { - return res; - } - return mp_copy (b, a); -} - - -/* set to zero */ -static void -mp_zero (mp_int * a) -{ - int n; - mp_digit *tmp; - - a->sign = MP_ZPOS; - a->used = 0; - - tmp = a->dp; - for (n = 0; n < a->alloc; n++) { - *tmp++ = 0; - } -} - - -/* copy, b = a */ -static int -mp_copy (mp_int * a, mp_int * b) -{ - int res, n; - - /* if dst == src do nothing */ - if (a == b) { - return MP_OKAY; - } - - /* grow dest */ - if (b->alloc < a->used) { - if ((res = mp_grow (b, a->used)) != MP_OKAY) { - return res; - } - } - - /* zero b and copy the parameters over */ - { - register mp_digit *tmpa, *tmpb; - - /* pointer aliases */ - - /* source */ - tmpa = a->dp; - - /* destination */ - tmpb = b->dp; - - /* copy all the digits */ - for (n = 0; n < a->used; n++) { - *tmpb++ = *tmpa++; - } - - /* clear high digits */ - for (; n < b->used; n++) { - *tmpb++ = 0; - } - } - - /* copy used count and sign */ - b->used = a->used; - b->sign = a->sign; - return MP_OKAY; -} - - -/* shift right a certain amount of digits */ -static void -mp_rshd (mp_int * a, int b) -{ - int x; - - /* if b <= 0 then ignore it */ - if (b <= 0) { - return; - } - - /* if b > used then simply zero it and return */ - if (a->used <= b) { - mp_zero (a); - return; - } - - { - register mp_digit *bottom, *top; - - /* shift the digits down */ - - /* bottom */ - bottom = a->dp; - - /* top [offset into digits] */ - top = a->dp + b; - - /* this is implemented as a sliding window where - * the window is b-digits long and digits from - * the top of the window are copied to the bottom - * - * e.g. - - b-2 | b-1 | b0 | b1 | b2 | ... | bb | ----> - /\ | ----> - \-------------------/ ----> - */ - for (x = 0; x < (a->used - b); x++) { - *bottom++ = *top++; - } - - /* zero the top digits */ - for (; x < a->used; x++) { - *bottom++ = 0; - } - } - - /* remove excess digits */ - a->used -= b; -} - - -/* swap the elements of two integers, for cases where you can't simply swap the - * mp_int pointers around - */ -static void -mp_exch (mp_int * a, mp_int * b) -{ - mp_int t; - - t = *a; - *a = *b; - *b = t; -} - - -/* trim unused digits - * - * This is used to ensure that leading zero digits are - * trimed and the leading "used" digit will be non-zero - * Typically very fast. Also fixes the sign if there - * are no more leading digits - */ -static void -mp_clamp (mp_int * a) -{ - /* decrease used while the most significant digit is - * zero. - */ - while (a->used > 0 && a->dp[a->used - 1] == 0) { - --(a->used); - } - - /* reset the sign flag if used == 0 */ - if (a->used == 0) { - a->sign = MP_ZPOS; - } -} - - -/* grow as required */ -static int -mp_grow (mp_int * a, int size) -{ - int i; - mp_digit *tmp; - - /* if the alloc size is smaller alloc more ram */ - if (a->alloc < size) { - /* ensure there are always at least MP_PREC digits extra on top */ - size += (MP_PREC * 2) - (size % MP_PREC); - - /* reallocate the array a->dp - * - * We store the return in a temporary variable - * in case the operation failed we don't want - * to overwrite the dp member of a. - */ - tmp = OPT_CAST(mp_digit) XREALLOC (a->dp, sizeof (mp_digit) * size); - if (tmp == NULL) { - /* reallocation failed but "a" is still valid [can be freed] */ - return MP_MEM; - } - - /* reallocation succeeded so set a->dp */ - a->dp = tmp; - - /* zero excess digits */ - i = a->alloc; - a->alloc = size; - for (; i < a->alloc; i++) { - a->dp[i] = 0; - } - } - return MP_OKAY; -} - - -#ifdef BN_MP_ABS_C -/* b = |a| - * - * Simple function copies the input and fixes the sign to positive - */ -static int -mp_abs (mp_int * a, mp_int * b) -{ - int res; - - /* copy a to b */ - if (a != b) { - if ((res = mp_copy (a, b)) != MP_OKAY) { - return res; - } - } - - /* force the sign of b to positive */ - b->sign = MP_ZPOS; - - return MP_OKAY; -} -#endif - - -/* set to a digit */ -static void -mp_set (mp_int * a, mp_digit b) -{ - mp_zero (a); - a->dp[0] = b & MP_MASK; - a->used = (a->dp[0] != 0) ? 1 : 0; -} - - -#ifndef LTM_NO_NEG_EXP -/* b = a/2 */ -static int -mp_div_2(mp_int * a, mp_int * b) -{ - int x, res, oldused; - - /* copy */ - if (b->alloc < a->used) { - if ((res = mp_grow (b, a->used)) != MP_OKAY) { - return res; - } - } - - oldused = b->used; - b->used = a->used; - { - register mp_digit r, rr, *tmpa, *tmpb; - - /* source alias */ - tmpa = a->dp + b->used - 1; - - /* dest alias */ - tmpb = b->dp + b->used - 1; - - /* carry */ - r = 0; - for (x = b->used - 1; x >= 0; x--) { - /* get the carry for the next iteration */ - rr = *tmpa & 1; - - /* shift the current digit, add in carry and store */ - *tmpb-- = (*tmpa-- >> 1) | (r << (DIGIT_BIT - 1)); - - /* forward carry to next iteration */ - r = rr; - } - - /* zero excess digits */ - tmpb = b->dp + b->used; - for (x = b->used; x < oldused; x++) { - *tmpb++ = 0; - } - } - b->sign = a->sign; - mp_clamp (b); - return MP_OKAY; -} -#endif /* LTM_NO_NEG_EXP */ - - -/* shift left by a certain bit count */ -static int -mp_mul_2d (mp_int * a, int b, mp_int * c) -{ - mp_digit d; - int res; - - /* copy */ - if (a != c) { - if ((res = mp_copy (a, c)) != MP_OKAY) { - return res; - } - } - - if (c->alloc < (int)(c->used + b/DIGIT_BIT + 1)) { - if ((res = mp_grow (c, c->used + b / DIGIT_BIT + 1)) != MP_OKAY) { - return res; - } - } - - /* shift by as many digits in the bit count */ - if (b >= (int)DIGIT_BIT) { - if ((res = mp_lshd (c, b / DIGIT_BIT)) != MP_OKAY) { - return res; - } - } - - /* shift any bit count < DIGIT_BIT */ - d = (mp_digit) (b % DIGIT_BIT); - if (d != 0) { - register mp_digit *tmpc, shift, mask, r, rr; - register int x; - - /* bitmask for carries */ - mask = (((mp_digit)1) << d) - 1; - - /* shift for msbs */ - shift = DIGIT_BIT - d; - - /* alias */ - tmpc = c->dp; - - /* carry */ - r = 0; - for (x = 0; x < c->used; x++) { - /* get the higher bits of the current word */ - rr = (*tmpc >> shift) & mask; - - /* shift the current word and OR in the carry */ - *tmpc = ((*tmpc << d) | r) & MP_MASK; - ++tmpc; - - /* set the carry to the carry bits of the current word */ - r = rr; - } - - /* set final carry */ - if (r != 0) { - c->dp[(c->used)++] = r; - } - } - mp_clamp (c); - return MP_OKAY; -} - - -#ifdef BN_MP_INIT_MULTI_C -static int -mp_init_multi(mp_int *mp, ...) -{ - mp_err res = MP_OKAY; /* Assume ok until proven otherwise */ - int n = 0; /* Number of ok inits */ - mp_int* cur_arg = mp; - va_list args; - - va_start(args, mp); /* init args to next argument from caller */ - while (cur_arg != NULL) { - if (mp_init(cur_arg) != MP_OKAY) { - /* Oops - error! Back-track and mp_clear what we already - succeeded in init-ing, then return error. - */ - va_list clean_args; - - /* end the current list */ - va_end(args); - - /* now start cleaning up */ - cur_arg = mp; - va_start(clean_args, mp); - while (n--) { - mp_clear(cur_arg); - cur_arg = va_arg(clean_args, mp_int*); - } - va_end(clean_args); - res = MP_MEM; - break; - } - n++; - cur_arg = va_arg(args, mp_int*); - } - va_end(args); - return res; /* Assumed ok, if error flagged above. */ -} -#endif - - -#ifdef BN_MP_CLEAR_MULTI_C -static void -mp_clear_multi(mp_int *mp, ...) -{ - mp_int* next_mp = mp; - va_list args; - va_start(args, mp); - while (next_mp != NULL) { - mp_clear(next_mp); - next_mp = va_arg(args, mp_int*); - } - va_end(args); -} -#endif - - -/* shift left a certain amount of digits */ -static int -mp_lshd (mp_int * a, int b) -{ - int x, res; - - /* if its less than zero return */ - if (b <= 0) { - return MP_OKAY; - } - - /* grow to fit the new digits */ - if (a->alloc < a->used + b) { - if ((res = mp_grow (a, a->used + b)) != MP_OKAY) { - return res; - } - } - - { - register mp_digit *top, *bottom; - - /* increment the used by the shift amount then copy upwards */ - a->used += b; - - /* top */ - top = a->dp + a->used - 1; - - /* base */ - bottom = a->dp + a->used - 1 - b; - - /* much like mp_rshd this is implemented using a sliding window - * except the window goes the otherway around. Copying from - * the bottom to the top. see bn_mp_rshd.c for more info. - */ - for (x = a->used - 1; x >= b; x--) { - *top-- = *bottom--; - } - - /* zero the lower digits */ - top = a->dp; - for (x = 0; x < b; x++) { - *top++ = 0; - } - } - return MP_OKAY; -} - - -/* returns the number of bits in an int */ -static int -mp_count_bits (mp_int * a) -{ - int r; - mp_digit q; - - /* shortcut */ - if (a->used == 0) { - return 0; - } - - /* get number of digits and add that */ - r = (a->used - 1) * DIGIT_BIT; - - /* take the last digit and count the bits in it */ - q = a->dp[a->used - 1]; - while (q > ((mp_digit) 0)) { - ++r; - q >>= ((mp_digit) 1); - } - return r; -} - - -/* calc a value mod 2**b */ -static int -mp_mod_2d (mp_int * a, int b, mp_int * c) -{ - int x, res; - - /* if b is <= 0 then zero the int */ - if (b <= 0) { - mp_zero (c); - return MP_OKAY; - } - - /* if the modulus is larger than the value than return */ - if (b >= (int) (a->used * DIGIT_BIT)) { - res = mp_copy (a, c); - return res; - } - - /* copy */ - if ((res = mp_copy (a, c)) != MP_OKAY) { - return res; - } - - /* zero digits above the last digit of the modulus */ - for (x = (b / DIGIT_BIT) + ((b % DIGIT_BIT) == 0 ? 0 : 1); x < c->used; x++) { - c->dp[x] = 0; - } - /* clear the digit that is not completely outside/inside the modulus */ - c->dp[b / DIGIT_BIT] &= - (mp_digit) ((((mp_digit) 1) << (((mp_digit) b) % DIGIT_BIT)) - ((mp_digit) 1)); - mp_clamp (c); - return MP_OKAY; -} - - -#ifdef BN_MP_DIV_SMALL - -/* slower bit-bang division... also smaller */ -static int -mp_div(mp_int * a, mp_int * b, mp_int * c, mp_int * d) -{ - mp_int ta, tb, tq, q; - int res, n, n2; - - /* is divisor zero ? */ - if (mp_iszero (b) == 1) { - return MP_VAL; - } - - /* if a < b then q=0, r = a */ - if (mp_cmp_mag (a, b) == MP_LT) { - if (d != NULL) { - res = mp_copy (a, d); - } else { - res = MP_OKAY; - } - if (c != NULL) { - mp_zero (c); - } - return res; - } - - /* init our temps */ - if ((res = mp_init_multi(&ta, &tb, &tq, &q, NULL)) != MP_OKAY) { - return res; - } - - - mp_set(&tq, 1); - n = mp_count_bits(a) - mp_count_bits(b); - if (((res = mp_abs(a, &ta)) != MP_OKAY) || - ((res = mp_abs(b, &tb)) != MP_OKAY) || - ((res = mp_mul_2d(&tb, n, &tb)) != MP_OKAY) || - ((res = mp_mul_2d(&tq, n, &tq)) != MP_OKAY)) { - goto LBL_ERR; - } - - while (n-- >= 0) { - if (mp_cmp(&tb, &ta) != MP_GT) { - if (((res = mp_sub(&ta, &tb, &ta)) != MP_OKAY) || - ((res = mp_add(&q, &tq, &q)) != MP_OKAY)) { - goto LBL_ERR; - } - } - if (((res = mp_div_2d(&tb, 1, &tb, NULL)) != MP_OKAY) || - ((res = mp_div_2d(&tq, 1, &tq, NULL)) != MP_OKAY)) { - goto LBL_ERR; - } - } - - /* now q == quotient and ta == remainder */ - n = a->sign; - n2 = (a->sign == b->sign ? MP_ZPOS : MP_NEG); - if (c != NULL) { - mp_exch(c, &q); - c->sign = (mp_iszero(c) == MP_YES) ? MP_ZPOS : n2; - } - if (d != NULL) { - mp_exch(d, &ta); - d->sign = (mp_iszero(d) == MP_YES) ? MP_ZPOS : n; - } -LBL_ERR: - mp_clear_multi(&ta, &tb, &tq, &q, NULL); - return res; -} - -#else - -/* integer signed division. - * c*b + d == a [e.g. a/b, c=quotient, d=remainder] - * HAC pp.598 Algorithm 14.20 - * - * Note that the description in HAC is horribly - * incomplete. For example, it doesn't consider - * the case where digits are removed from 'x' in - * the inner loop. It also doesn't consider the - * case that y has fewer than three digits, etc.. - * - * The overall algorithm is as described as - * 14.20 from HAC but fixed to treat these cases. -*/ -static int -mp_div (mp_int * a, mp_int * b, mp_int * c, mp_int * d) -{ - mp_int q, x, y, t1, t2; - int res, n, t, i, norm, neg; - - /* is divisor zero ? */ - if (mp_iszero (b) == 1) { - return MP_VAL; - } - - /* if a < b then q=0, r = a */ - if (mp_cmp_mag (a, b) == MP_LT) { - if (d != NULL) { - res = mp_copy (a, d); - } else { - res = MP_OKAY; - } - if (c != NULL) { - mp_zero (c); - } - return res; - } - - if ((res = mp_init_size (&q, a->used + 2)) != MP_OKAY) { - return res; - } - q.used = a->used + 2; - - if ((res = mp_init (&t1)) != MP_OKAY) { - goto LBL_Q; - } - - if ((res = mp_init (&t2)) != MP_OKAY) { - goto LBL_T1; - } - - if ((res = mp_init_copy (&x, a)) != MP_OKAY) { - goto LBL_T2; - } - - if ((res = mp_init_copy (&y, b)) != MP_OKAY) { - goto LBL_X; - } - - /* fix the sign */ - neg = (a->sign == b->sign) ? MP_ZPOS : MP_NEG; - x.sign = y.sign = MP_ZPOS; - - /* normalize both x and y, ensure that y >= b/2, [b == 2**DIGIT_BIT] */ - norm = mp_count_bits(&y) % DIGIT_BIT; - if (norm < (int)(DIGIT_BIT-1)) { - norm = (DIGIT_BIT-1) - norm; - if ((res = mp_mul_2d (&x, norm, &x)) != MP_OKAY) { - goto LBL_Y; - } - if ((res = mp_mul_2d (&y, norm, &y)) != MP_OKAY) { - goto LBL_Y; - } - } else { - norm = 0; - } - - /* note hac does 0 based, so if used==5 then its 0,1,2,3,4, e.g. use 4 */ - n = x.used - 1; - t = y.used - 1; - - /* while (x >= y*b**n-t) do { q[n-t] += 1; x -= y*b**{n-t} } */ - if ((res = mp_lshd (&y, n - t)) != MP_OKAY) { /* y = y*b**{n-t} */ - goto LBL_Y; - } - - while (mp_cmp (&x, &y) != MP_LT) { - ++(q.dp[n - t]); - if ((res = mp_sub (&x, &y, &x)) != MP_OKAY) { - goto LBL_Y; - } - } - - /* reset y by shifting it back down */ - mp_rshd (&y, n - t); - - /* step 3. for i from n down to (t + 1) */ - for (i = n; i >= (t + 1); i--) { - if (i > x.used) { - continue; - } - - /* step 3.1 if xi == yt then set q{i-t-1} to b-1, - * otherwise set q{i-t-1} to (xi*b + x{i-1})/yt */ - if (x.dp[i] == y.dp[t]) { - q.dp[i - t - 1] = ((((mp_digit)1) << DIGIT_BIT) - 1); - } else { - mp_word tmp; - tmp = ((mp_word) x.dp[i]) << ((mp_word) DIGIT_BIT); - tmp |= ((mp_word) x.dp[i - 1]); - tmp /= ((mp_word) y.dp[t]); - if (tmp > (mp_word) MP_MASK) - tmp = MP_MASK; - q.dp[i - t - 1] = (mp_digit) (tmp & (mp_word) (MP_MASK)); - } - - /* while (q{i-t-1} * (yt * b + y{t-1})) > - xi * b**2 + xi-1 * b + xi-2 - - do q{i-t-1} -= 1; - */ - q.dp[i - t - 1] = (q.dp[i - t - 1] + 1) & MP_MASK; - do { - q.dp[i - t - 1] = (q.dp[i - t - 1] - 1) & MP_MASK; - - /* find left hand */ - mp_zero (&t1); - t1.dp[0] = (t - 1 < 0) ? 0 : y.dp[t - 1]; - t1.dp[1] = y.dp[t]; - t1.used = 2; - if ((res = mp_mul_d (&t1, q.dp[i - t - 1], &t1)) != MP_OKAY) { - goto LBL_Y; - } - - /* find right hand */ - t2.dp[0] = (i - 2 < 0) ? 0 : x.dp[i - 2]; - t2.dp[1] = (i - 1 < 0) ? 0 : x.dp[i - 1]; - t2.dp[2] = x.dp[i]; - t2.used = 3; - } while (mp_cmp_mag(&t1, &t2) == MP_GT); - - /* step 3.3 x = x - q{i-t-1} * y * b**{i-t-1} */ - if ((res = mp_mul_d (&y, q.dp[i - t - 1], &t1)) != MP_OKAY) { - goto LBL_Y; - } - - if ((res = mp_lshd (&t1, i - t - 1)) != MP_OKAY) { - goto LBL_Y; - } - - if ((res = mp_sub (&x, &t1, &x)) != MP_OKAY) { - goto LBL_Y; - } - - /* if x < 0 then { x = x + y*b**{i-t-1}; q{i-t-1} -= 1; } */ - if (x.sign == MP_NEG) { - if ((res = mp_copy (&y, &t1)) != MP_OKAY) { - goto LBL_Y; - } - if ((res = mp_lshd (&t1, i - t - 1)) != MP_OKAY) { - goto LBL_Y; - } - if ((res = mp_add (&x, &t1, &x)) != MP_OKAY) { - goto LBL_Y; - } - - q.dp[i - t - 1] = (q.dp[i - t - 1] - 1UL) & MP_MASK; - } - } - - /* now q is the quotient and x is the remainder - * [which we have to normalize] - */ - - /* get sign before writing to c */ - x.sign = x.used == 0 ? MP_ZPOS : a->sign; - - if (c != NULL) { - mp_clamp (&q); - mp_exch (&q, c); - c->sign = neg; - } - - if (d != NULL) { - mp_div_2d (&x, norm, &x, NULL); - mp_exch (&x, d); - } - - res = MP_OKAY; - -LBL_Y:mp_clear (&y); -LBL_X:mp_clear (&x); -LBL_T2:mp_clear (&t2); -LBL_T1:mp_clear (&t1); -LBL_Q:mp_clear (&q); - return res; -} - -#endif - - -#ifdef MP_LOW_MEM - #define TAB_SIZE 32 -#else - #define TAB_SIZE 256 -#endif - -static int -s_mp_exptmod (mp_int * G, mp_int * X, mp_int * P, mp_int * Y, int redmode) -{ - mp_int M[TAB_SIZE], res, mu; - mp_digit buf; - int err, bitbuf, bitcpy, bitcnt, mode, digidx, x, y, winsize; - int (*redux)(mp_int*,mp_int*,mp_int*); - - /* find window size */ - x = mp_count_bits (X); - if (x <= 7) { - winsize = 2; - } else if (x <= 36) { - winsize = 3; - } else if (x <= 140) { - winsize = 4; - } else if (x <= 450) { - winsize = 5; - } else if (x <= 1303) { - winsize = 6; - } else if (x <= 3529) { - winsize = 7; - } else { - winsize = 8; - } - -#ifdef MP_LOW_MEM - if (winsize > 5) { - winsize = 5; - } -#endif - - /* init M array */ - /* init first cell */ - if ((err = mp_init(&M[1])) != MP_OKAY) { - return err; - } - - /* now init the second half of the array */ - for (x = 1<<(winsize-1); x < (1 << winsize); x++) { - if ((err = mp_init(&M[x])) != MP_OKAY) { - for (y = 1<<(winsize-1); y < x; y++) { - mp_clear (&M[y]); - } - mp_clear(&M[1]); - return err; - } - } - - /* create mu, used for Barrett reduction */ - if ((err = mp_init (&mu)) != MP_OKAY) { - goto LBL_M; - } - - if (redmode == 0) { - if ((err = mp_reduce_setup (&mu, P)) != MP_OKAY) { - goto LBL_MU; - } - redux = mp_reduce; - } else { - if ((err = mp_reduce_2k_setup_l (P, &mu)) != MP_OKAY) { - goto LBL_MU; - } - redux = mp_reduce_2k_l; - } - - /* create M table - * - * The M table contains powers of the base, - * e.g. M[x] = G**x mod P - * - * The first half of the table is not - * computed though accept for M[0] and M[1] - */ - if ((err = mp_mod (G, P, &M[1])) != MP_OKAY) { - goto LBL_MU; - } - - /* compute the value at M[1<<(winsize-1)] by squaring - * M[1] (winsize-1) times - */ - if ((err = mp_copy (&M[1], &M[1 << (winsize - 1)])) != MP_OKAY) { - goto LBL_MU; - } - - for (x = 0; x < (winsize - 1); x++) { - /* square it */ - if ((err = mp_sqr (&M[1 << (winsize - 1)], - &M[1 << (winsize - 1)])) != MP_OKAY) { - goto LBL_MU; - } - - /* reduce modulo P */ - if ((err = redux (&M[1 << (winsize - 1)], P, &mu)) != MP_OKAY) { - goto LBL_MU; - } - } - - /* create upper table, that is M[x] = M[x-1] * M[1] (mod P) - * for x = (2**(winsize - 1) + 1) to (2**winsize - 1) - */ - for (x = (1 << (winsize - 1)) + 1; x < (1 << winsize); x++) { - if ((err = mp_mul (&M[x - 1], &M[1], &M[x])) != MP_OKAY) { - goto LBL_MU; - } - if ((err = redux (&M[x], P, &mu)) != MP_OKAY) { - goto LBL_MU; - } - } - - /* setup result */ - if ((err = mp_init (&res)) != MP_OKAY) { - goto LBL_MU; - } - mp_set (&res, 1); - - /* set initial mode and bit cnt */ - mode = 0; - bitcnt = 1; - buf = 0; - digidx = X->used - 1; - bitcpy = 0; - bitbuf = 0; - - for (;;) { - /* grab next digit as required */ - if (--bitcnt == 0) { - /* if digidx == -1 we are out of digits */ - if (digidx == -1) { - break; - } - /* read next digit and reset the bitcnt */ - buf = X->dp[digidx--]; - bitcnt = (int) DIGIT_BIT; - } - - /* grab the next msb from the exponent */ - y = (buf >> (mp_digit)(DIGIT_BIT - 1)) & 1; - buf <<= (mp_digit)1; - - /* if the bit is zero and mode == 0 then we ignore it - * These represent the leading zero bits before the first 1 bit - * in the exponent. Technically this opt is not required but it - * does lower the # of trivial squaring/reductions used - */ - if (mode == 0 && y == 0) { - continue; - } - - /* if the bit is zero and mode == 1 then we square */ - if (mode == 1 && y == 0) { - if ((err = mp_sqr (&res, &res)) != MP_OKAY) { - goto LBL_RES; - } - if ((err = redux (&res, P, &mu)) != MP_OKAY) { - goto LBL_RES; - } - continue; - } - - /* else we add it to the window */ - bitbuf |= (y << (winsize - ++bitcpy)); - mode = 2; - - if (bitcpy == winsize) { - /* ok window is filled so square as required and multiply */ - /* square first */ - for (x = 0; x < winsize; x++) { - if ((err = mp_sqr (&res, &res)) != MP_OKAY) { - goto LBL_RES; - } - if ((err = redux (&res, P, &mu)) != MP_OKAY) { - goto LBL_RES; - } - } - - /* then multiply */ - if ((err = mp_mul (&res, &M[bitbuf], &res)) != MP_OKAY) { - goto LBL_RES; - } - if ((err = redux (&res, P, &mu)) != MP_OKAY) { - goto LBL_RES; - } - - /* empty window and reset */ - bitcpy = 0; - bitbuf = 0; - mode = 1; - } - } - - /* if bits remain then square/multiply */ - if (mode == 2 && bitcpy > 0) { - /* square then multiply if the bit is set */ - for (x = 0; x < bitcpy; x++) { - if ((err = mp_sqr (&res, &res)) != MP_OKAY) { - goto LBL_RES; - } - if ((err = redux (&res, P, &mu)) != MP_OKAY) { - goto LBL_RES; - } - - bitbuf <<= 1; - if ((bitbuf & (1 << winsize)) != 0) { - /* then multiply */ - if ((err = mp_mul (&res, &M[1], &res)) != MP_OKAY) { - goto LBL_RES; - } - if ((err = redux (&res, P, &mu)) != MP_OKAY) { - goto LBL_RES; - } - } - } - } - - mp_exch (&res, Y); - err = MP_OKAY; -LBL_RES:mp_clear (&res); -LBL_MU:mp_clear (&mu); -LBL_M: - mp_clear(&M[1]); - for (x = 1<<(winsize-1); x < (1 << winsize); x++) { - mp_clear (&M[x]); - } - return err; -} - - -/* computes b = a*a */ -static int -mp_sqr (mp_int * a, mp_int * b) -{ - int res; - -#ifdef BN_MP_TOOM_SQR_C - /* use Toom-Cook? */ - if (a->used >= TOOM_SQR_CUTOFF) { - res = mp_toom_sqr(a, b); - /* Karatsuba? */ - } else -#endif -#ifdef BN_MP_KARATSUBA_SQR_C -if (a->used >= KARATSUBA_SQR_CUTOFF) { - res = mp_karatsuba_sqr (a, b); - } else -#endif - { -#ifdef BN_FAST_S_MP_SQR_C - /* can we use the fast comba multiplier? */ - if ((a->used * 2 + 1) < MP_WARRAY && - a->used < - (1 << (sizeof(mp_word) * CHAR_BIT - 2*DIGIT_BIT - 1))) { - res = fast_s_mp_sqr (a, b); - } else -#endif -#ifdef BN_S_MP_SQR_C - res = s_mp_sqr (a, b); -#else -#error mp_sqr could fail - res = MP_VAL; -#endif - } - b->sign = MP_ZPOS; - return res; -} - - -/* reduces a modulo n where n is of the form 2**p - d - This differs from reduce_2k since "d" can be larger - than a single digit. -*/ -static int -mp_reduce_2k_l(mp_int *a, mp_int *n, mp_int *d) -{ - mp_int q; - int p, res; - - if ((res = mp_init(&q)) != MP_OKAY) { - return res; - } - - p = mp_count_bits(n); -top: - /* q = a/2**p, a = a mod 2**p */ - if ((res = mp_div_2d(a, p, &q, a)) != MP_OKAY) { - goto ERR; - } - - /* q = q * d */ - if ((res = mp_mul(&q, d, &q)) != MP_OKAY) { - goto ERR; - } - - /* a = a + q */ - if ((res = s_mp_add(a, &q, a)) != MP_OKAY) { - goto ERR; - } - - if (mp_cmp_mag(a, n) != MP_LT) { - s_mp_sub(a, n, a); - goto top; - } - -ERR: - mp_clear(&q); - return res; -} - - -/* determines the setup value */ -static int -mp_reduce_2k_setup_l(mp_int *a, mp_int *d) -{ - int res; - mp_int tmp; - - if ((res = mp_init(&tmp)) != MP_OKAY) { - return res; - } - - if ((res = mp_2expt(&tmp, mp_count_bits(a))) != MP_OKAY) { - goto ERR; - } - - if ((res = s_mp_sub(&tmp, a, d)) != MP_OKAY) { - goto ERR; - } - -ERR: - mp_clear(&tmp); - return res; -} - - -/* computes a = 2**b - * - * Simple algorithm which zeroes the int, grows it then just sets one bit - * as required. - */ -static int -mp_2expt (mp_int * a, int b) -{ - int res; - - /* zero a as per default */ - mp_zero (a); - - /* grow a to accommodate the single bit */ - if ((res = mp_grow (a, b / DIGIT_BIT + 1)) != MP_OKAY) { - return res; - } - - /* set the used count of where the bit will go */ - a->used = b / DIGIT_BIT + 1; - - /* put the single bit in its place */ - a->dp[b / DIGIT_BIT] = ((mp_digit)1) << (b % DIGIT_BIT); - - return MP_OKAY; -} - - -/* pre-calculate the value required for Barrett reduction - * For a given modulus "b" it calulates the value required in "a" - */ -static int -mp_reduce_setup (mp_int * a, mp_int * b) -{ - int res; - - if ((res = mp_2expt (a, b->used * 2 * DIGIT_BIT)) != MP_OKAY) { - return res; - } - return mp_div (a, b, a, NULL); -} - - -/* reduces x mod m, assumes 0 < x < m**2, mu is - * precomputed via mp_reduce_setup. - * From HAC pp.604 Algorithm 14.42 - */ -static int -mp_reduce (mp_int * x, mp_int * m, mp_int * mu) -{ - mp_int q; - int res, um = m->used; - - /* q = x */ - if ((res = mp_init_copy (&q, x)) != MP_OKAY) { - return res; - } - - /* q1 = x / b**(k-1) */ - mp_rshd (&q, um - 1); - - /* according to HAC this optimization is ok */ - if (((unsigned long) um) > (((mp_digit)1) << (DIGIT_BIT - 1))) { - if ((res = mp_mul (&q, mu, &q)) != MP_OKAY) { - goto CLEANUP; - } - } else { -#ifdef BN_S_MP_MUL_HIGH_DIGS_C - if ((res = s_mp_mul_high_digs (&q, mu, &q, um)) != MP_OKAY) { - goto CLEANUP; - } -#elif defined(BN_FAST_S_MP_MUL_HIGH_DIGS_C) - if ((res = fast_s_mp_mul_high_digs (&q, mu, &q, um)) != MP_OKAY) { - goto CLEANUP; - } -#else - { -#error mp_reduce would always fail - res = MP_VAL; - goto CLEANUP; - } -#endif - } - - /* q3 = q2 / b**(k+1) */ - mp_rshd (&q, um + 1); - - /* x = x mod b**(k+1), quick (no division) */ - if ((res = mp_mod_2d (x, DIGIT_BIT * (um + 1), x)) != MP_OKAY) { - goto CLEANUP; - } - - /* q = q * m mod b**(k+1), quick (no division) */ - if ((res = s_mp_mul_digs (&q, m, &q, um + 1)) != MP_OKAY) { - goto CLEANUP; - } - - /* x = x - q */ - if ((res = mp_sub (x, &q, x)) != MP_OKAY) { - goto CLEANUP; - } - - /* If x < 0, add b**(k+1) to it */ - if (mp_cmp_d (x, 0) == MP_LT) { - mp_set (&q, 1); - if ((res = mp_lshd (&q, um + 1)) != MP_OKAY) { - goto CLEANUP; - } - if ((res = mp_add (x, &q, x)) != MP_OKAY) { - goto CLEANUP; - } - } - - /* Back off if it's too big */ - while (mp_cmp (x, m) != MP_LT) { - if ((res = s_mp_sub (x, m, x)) != MP_OKAY) { - goto CLEANUP; - } - } - -CLEANUP: - mp_clear (&q); - - return res; -} - - -/* multiplies |a| * |b| and only computes up to digs digits of result - * HAC pp. 595, Algorithm 14.12 Modified so you can control how - * many digits of output are created. - */ -static int -s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs) -{ - mp_int t; - int res, pa, pb, ix, iy; - mp_digit u; - mp_word r; - mp_digit tmpx, *tmpt, *tmpy; - - /* can we use the fast multiplier? */ - if (((digs) < MP_WARRAY) && - MIN (a->used, b->used) < - (1 << ((CHAR_BIT * sizeof (mp_word)) - (2 * DIGIT_BIT)))) { - return fast_s_mp_mul_digs (a, b, c, digs); - } - - if ((res = mp_init_size (&t, digs)) != MP_OKAY) { - return res; - } - t.used = digs; - - /* compute the digits of the product directly */ - pa = a->used; - for (ix = 0; ix < pa; ix++) { - /* set the carry to zero */ - u = 0; - - /* limit ourselves to making digs digits of output */ - pb = MIN (b->used, digs - ix); - - /* setup some aliases */ - /* copy of the digit from a used within the nested loop */ - tmpx = a->dp[ix]; - - /* an alias for the destination shifted ix places */ - tmpt = t.dp + ix; - - /* an alias for the digits of b */ - tmpy = b->dp; - - /* compute the columns of the output and propagate the carry */ - for (iy = 0; iy < pb; iy++) { - /* compute the column as a mp_word */ - r = ((mp_word)*tmpt) + - ((mp_word)tmpx) * ((mp_word)*tmpy++) + - ((mp_word) u); - - /* the new column is the lower part of the result */ - *tmpt++ = (mp_digit) (r & ((mp_word) MP_MASK)); - - /* get the carry word from the result */ - u = (mp_digit) (r >> ((mp_word) DIGIT_BIT)); - } - /* set carry if it is placed below digs */ - if (ix + iy < digs) { - *tmpt = u; - } - } - - mp_clamp (&t); - mp_exch (&t, c); - - mp_clear (&t); - return MP_OKAY; -} - - -/* Fast (comba) multiplier - * - * This is the fast column-array [comba] multiplier. It is - * designed to compute the columns of the product first - * then handle the carries afterwards. This has the effect - * of making the nested loops that compute the columns very - * simple and schedulable on super-scalar processors. - * - * This has been modified to produce a variable number of - * digits of output so if say only a half-product is required - * you don't have to compute the upper half (a feature - * required for fast Barrett reduction). - * - * Based on Algorithm 14.12 on pp.595 of HAC. - * - */ -static int -fast_s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs) -{ - int olduse, res, pa, ix, iz; - mp_digit W[MP_WARRAY]; - register mp_word _W; - - /* grow the destination as required */ - if (c->alloc < digs) { - if ((res = mp_grow (c, digs)) != MP_OKAY) { - return res; - } - } - - /* number of output digits to produce */ - pa = MIN(digs, a->used + b->used); - - /* clear the carry */ - _W = 0; - for (ix = 0; ix < pa; ix++) { - int tx, ty; - int iy; - mp_digit *tmpx, *tmpy; - - /* get offsets into the two bignums */ - ty = MIN(b->used-1, ix); - tx = ix - ty; - - /* setup temp aliases */ - tmpx = a->dp + tx; - tmpy = b->dp + ty; - - /* this is the number of times the loop will iterrate, essentially - while (tx++ < a->used && ty-- >= 0) { ... } - */ - iy = MIN(a->used-tx, ty+1); - - /* execute loop */ - for (iz = 0; iz < iy; ++iz) { - _W += ((mp_word)*tmpx++)*((mp_word)*tmpy--); - - } - - /* store term */ - W[ix] = ((mp_digit)_W) & MP_MASK; - - /* make next carry */ - _W = _W >> ((mp_word)DIGIT_BIT); - } - - /* setup dest */ - olduse = c->used; - c->used = pa; - - { - register mp_digit *tmpc; - tmpc = c->dp; - for (ix = 0; ix < pa+1; ix++) { - /* now extract the previous digit [below the carry] */ - *tmpc++ = W[ix]; - } - - /* clear unused digits [that existed in the old copy of c] */ - for (; ix < olduse; ix++) { - *tmpc++ = 0; - } - } - mp_clamp (c); - return MP_OKAY; -} - - -/* init an mp_init for a given size */ -static int -mp_init_size (mp_int * a, int size) -{ - int x; - - /* pad size so there are always extra digits */ - size += (MP_PREC * 2) - (size % MP_PREC); - - /* alloc mem */ - a->dp = OPT_CAST(mp_digit) XMALLOC (sizeof (mp_digit) * size); - if (a->dp == NULL) { - return MP_MEM; - } - - /* set the members */ - a->used = 0; - a->alloc = size; - a->sign = MP_ZPOS; - - /* zero the digits */ - for (x = 0; x < size; x++) { - a->dp[x] = 0; - } - - return MP_OKAY; -} - - -/* low level squaring, b = a*a, HAC pp.596-597, Algorithm 14.16 */ -static int -s_mp_sqr (mp_int * a, mp_int * b) -{ - mp_int t; - int res, ix, iy, pa; - mp_word r; - mp_digit u, tmpx, *tmpt; - - pa = a->used; - if ((res = mp_init_size (&t, 2*pa + 1)) != MP_OKAY) { - return res; - } - - /* default used is maximum possible size */ - t.used = 2*pa + 1; - - for (ix = 0; ix < pa; ix++) { - /* first calculate the digit at 2*ix */ - /* calculate double precision result */ - r = ((mp_word) t.dp[2*ix]) + - ((mp_word)a->dp[ix])*((mp_word)a->dp[ix]); - - /* store lower part in result */ - t.dp[ix+ix] = (mp_digit) (r & ((mp_word) MP_MASK)); - - /* get the carry */ - u = (mp_digit)(r >> ((mp_word) DIGIT_BIT)); - - /* left hand side of A[ix] * A[iy] */ - tmpx = a->dp[ix]; - - /* alias for where to store the results */ - tmpt = t.dp + (2*ix + 1); - - for (iy = ix + 1; iy < pa; iy++) { - /* first calculate the product */ - r = ((mp_word)tmpx) * ((mp_word)a->dp[iy]); - - /* now calculate the double precision result, note we use - * addition instead of *2 since it's easier to optimize - */ - r = ((mp_word) *tmpt) + r + r + ((mp_word) u); - - /* store lower part */ - *tmpt++ = (mp_digit) (r & ((mp_word) MP_MASK)); - - /* get carry */ - u = (mp_digit)(r >> ((mp_word) DIGIT_BIT)); - } - /* propagate upwards */ - while (u != ((mp_digit) 0)) { - r = ((mp_word) *tmpt) + ((mp_word) u); - *tmpt++ = (mp_digit) (r & ((mp_word) MP_MASK)); - u = (mp_digit)(r >> ((mp_word) DIGIT_BIT)); - } - } - - mp_clamp (&t); - mp_exch (&t, b); - mp_clear (&t); - return MP_OKAY; -} - - -/* multiplies |a| * |b| and does not compute the lower digs digits - * [meant to get the higher part of the product] - */ -static int -s_mp_mul_high_digs (mp_int * a, mp_int * b, mp_int * c, int digs) -{ - mp_int t; - int res, pa, pb, ix, iy; - mp_digit u; - mp_word r; - mp_digit tmpx, *tmpt, *tmpy; - - /* can we use the fast multiplier? */ -#ifdef BN_FAST_S_MP_MUL_HIGH_DIGS_C - if (((a->used + b->used + 1) < MP_WARRAY) - && MIN (a->used, b->used) < (1 << ((CHAR_BIT * sizeof (mp_word)) - (2 * DIGIT_BIT)))) { - return fast_s_mp_mul_high_digs (a, b, c, digs); - } -#endif - - if ((res = mp_init_size (&t, a->used + b->used + 1)) != MP_OKAY) { - return res; - } - t.used = a->used + b->used + 1; - - pa = a->used; - pb = b->used; - for (ix = 0; ix < pa; ix++) { - /* clear the carry */ - u = 0; - - /* left hand side of A[ix] * B[iy] */ - tmpx = a->dp[ix]; - - /* alias to the address of where the digits will be stored */ - tmpt = &(t.dp[digs]); - - /* alias for where to read the right hand side from */ - tmpy = b->dp + (digs - ix); - - for (iy = digs - ix; iy < pb; iy++) { - /* calculate the double precision result */ - r = ((mp_word)*tmpt) + - ((mp_word)tmpx) * ((mp_word)*tmpy++) + - ((mp_word) u); - - /* get the lower part */ - *tmpt++ = (mp_digit) (r & ((mp_word) MP_MASK)); - - /* carry the carry */ - u = (mp_digit) (r >> ((mp_word) DIGIT_BIT)); - } - *tmpt = u; - } - mp_clamp (&t); - mp_exch (&t, c); - mp_clear (&t); - return MP_OKAY; -} - - -#ifdef BN_MP_MONTGOMERY_SETUP_C -/* setups the montgomery reduction stuff */ -static int -mp_montgomery_setup (mp_int * n, mp_digit * rho) -{ - mp_digit x, b; - -/* fast inversion mod 2**k - * - * Based on the fact that - * - * XA = 1 (mod 2**n) => (X(2-XA)) A = 1 (mod 2**2n) - * => 2*X*A - X*X*A*A = 1 - * => 2*(1) - (1) = 1 - */ - b = n->dp[0]; - - if ((b & 1) == 0) { - return MP_VAL; - } - - x = (((b + 2) & 4) << 1) + b; /* here x*a==1 mod 2**4 */ - x *= 2 - b * x; /* here x*a==1 mod 2**8 */ -#if !defined(MP_8BIT) - x *= 2 - b * x; /* here x*a==1 mod 2**16 */ -#endif -#if defined(MP_64BIT) || !(defined(MP_8BIT) || defined(MP_16BIT)) - x *= 2 - b * x; /* here x*a==1 mod 2**32 */ -#endif -#ifdef MP_64BIT - x *= 2 - b * x; /* here x*a==1 mod 2**64 */ -#endif - - /* rho = -1/m mod b */ - *rho = (unsigned long)(((mp_word)1 << ((mp_word) DIGIT_BIT)) - x) & MP_MASK; - - return MP_OKAY; -} -#endif - - -#ifdef BN_FAST_MP_MONTGOMERY_REDUCE_C -/* computes xR**-1 == x (mod N) via Montgomery Reduction - * - * This is an optimized implementation of montgomery_reduce - * which uses the comba method to quickly calculate the columns of the - * reduction. - * - * Based on Algorithm 14.32 on pp.601 of HAC. -*/ -int -fast_mp_montgomery_reduce (mp_int * x, mp_int * n, mp_digit rho) -{ - int ix, res, olduse; - mp_word W[MP_WARRAY]; - - /* get old used count */ - olduse = x->used; - - /* grow a as required */ - if (x->alloc < n->used + 1) { - if ((res = mp_grow (x, n->used + 1)) != MP_OKAY) { - return res; - } - } - - /* first we have to get the digits of the input into - * an array of double precision words W[...] - */ - { - register mp_word *_W; - register mp_digit *tmpx; - - /* alias for the W[] array */ - _W = W; - - /* alias for the digits of x*/ - tmpx = x->dp; - - /* copy the digits of a into W[0..a->used-1] */ - for (ix = 0; ix < x->used; ix++) { - *_W++ = *tmpx++; - } - - /* zero the high words of W[a->used..m->used*2] */ - for (; ix < n->used * 2 + 1; ix++) { - *_W++ = 0; - } - } - - /* now we proceed to zero successive digits - * from the least significant upwards - */ - for (ix = 0; ix < n->used; ix++) { - /* mu = ai * m' mod b - * - * We avoid a double precision multiplication (which isn't required) - * by casting the value down to a mp_digit. Note this requires - * that W[ix-1] have the carry cleared (see after the inner loop) - */ - register mp_digit mu; - mu = (mp_digit) (((W[ix] & MP_MASK) * rho) & MP_MASK); - - /* a = a + mu * m * b**i - * - * This is computed in place and on the fly. The multiplication - * by b**i is handled by offseting which columns the results - * are added to. - * - * Note the comba method normally doesn't handle carries in the - * inner loop In this case we fix the carry from the previous - * column since the Montgomery reduction requires digits of the - * result (so far) [see above] to work. This is - * handled by fixing up one carry after the inner loop. The - * carry fixups are done in order so after these loops the - * first m->used words of W[] have the carries fixed - */ - { - register int iy; - register mp_digit *tmpn; - register mp_word *_W; - - /* alias for the digits of the modulus */ - tmpn = n->dp; - - /* Alias for the columns set by an offset of ix */ - _W = W + ix; - - /* inner loop */ - for (iy = 0; iy < n->used; iy++) { - *_W++ += ((mp_word)mu) * ((mp_word)*tmpn++); - } - } - - /* now fix carry for next digit, W[ix+1] */ - W[ix + 1] += W[ix] >> ((mp_word) DIGIT_BIT); - } - - /* now we have to propagate the carries and - * shift the words downward [all those least - * significant digits we zeroed]. - */ - { - register mp_digit *tmpx; - register mp_word *_W, *_W1; - - /* nox fix rest of carries */ - - /* alias for current word */ - _W1 = W + ix; - - /* alias for next word, where the carry goes */ - _W = W + ++ix; - - for (; ix <= n->used * 2 + 1; ix++) { - *_W++ += *_W1++ >> ((mp_word) DIGIT_BIT); - } - - /* copy out, A = A/b**n - * - * The result is A/b**n but instead of converting from an - * array of mp_word to mp_digit than calling mp_rshd - * we just copy them in the right order - */ - - /* alias for destination word */ - tmpx = x->dp; - - /* alias for shifted double precision result */ - _W = W + n->used; - - for (ix = 0; ix < n->used + 1; ix++) { - *tmpx++ = (mp_digit)(*_W++ & ((mp_word) MP_MASK)); - } - - /* zero oldused digits, if the input a was larger than - * m->used+1 we'll have to clear the digits - */ - for (; ix < olduse; ix++) { - *tmpx++ = 0; - } - } - - /* set the max used and clamp */ - x->used = n->used + 1; - mp_clamp (x); - - /* if A >= m then A = A - m */ - if (mp_cmp_mag (x, n) != MP_LT) { - return s_mp_sub (x, n, x); - } - return MP_OKAY; -} -#endif - - -#ifdef BN_MP_MUL_2_C -/* b = a*2 */ -static int -mp_mul_2(mp_int * a, mp_int * b) -{ - int x, res, oldused; - - /* grow to accommodate result */ - if (b->alloc < a->used + 1) { - if ((res = mp_grow (b, a->used + 1)) != MP_OKAY) { - return res; - } - } - - oldused = b->used; - b->used = a->used; - - { - register mp_digit r, rr, *tmpa, *tmpb; - - /* alias for source */ - tmpa = a->dp; - - /* alias for dest */ - tmpb = b->dp; - - /* carry */ - r = 0; - for (x = 0; x < a->used; x++) { - - /* get what will be the *next* carry bit from the - * MSB of the current digit - */ - rr = *tmpa >> ((mp_digit)(DIGIT_BIT - 1)); - - /* now shift up this digit, add in the carry [from the previous] */ - *tmpb++ = ((*tmpa++ << ((mp_digit)1)) | r) & MP_MASK; - - /* copy the carry that would be from the source - * digit into the next iteration - */ - r = rr; - } - - /* new leading digit? */ - if (r != 0) { - /* add a MSB which is always 1 at this point */ - *tmpb = 1; - ++(b->used); - } - - /* now zero any excess digits on the destination - * that we didn't write to - */ - tmpb = b->dp + b->used; - for (x = b->used; x < oldused; x++) { - *tmpb++ = 0; - } - } - b->sign = a->sign; - return MP_OKAY; -} -#endif - - -#ifdef BN_MP_MONTGOMERY_CALC_NORMALIZATION_C -/* - * shifts with subtractions when the result is greater than b. - * - * The method is slightly modified to shift B unconditionally up to just under - * the leading bit of b. This saves a lot of multiple precision shifting. - */ -static int -mp_montgomery_calc_normalization (mp_int * a, mp_int * b) -{ - int x, bits, res; - - /* how many bits of last digit does b use */ - bits = mp_count_bits (b) % DIGIT_BIT; - - if (b->used > 1) { - if ((res = mp_2expt (a, (b->used - 1) * DIGIT_BIT + bits - 1)) != MP_OKAY) { - return res; - } - } else { - mp_set(a, 1); - bits = 1; - } - - - /* now compute C = A * B mod b */ - for (x = bits - 1; x < (int)DIGIT_BIT; x++) { - if ((res = mp_mul_2 (a, a)) != MP_OKAY) { - return res; - } - if (mp_cmp_mag (a, b) != MP_LT) { - if ((res = s_mp_sub (a, b, a)) != MP_OKAY) { - return res; - } - } - } - - return MP_OKAY; -} -#endif - - -#ifdef BN_MP_EXPTMOD_FAST_C -/* computes Y == G**X mod P, HAC pp.616, Algorithm 14.85 - * - * Uses a left-to-right k-ary sliding window to compute the modular exponentiation. - * The value of k changes based on the size of the exponent. - * - * Uses Montgomery or Diminished Radix reduction [whichever appropriate] - */ - -static int -mp_exptmod_fast (mp_int * G, mp_int * X, mp_int * P, mp_int * Y, int redmode) -{ - mp_int M[TAB_SIZE], res; - mp_digit buf, mp; - int err, bitbuf, bitcpy, bitcnt, mode, digidx, x, y, winsize; - - /* use a pointer to the reduction algorithm. This allows us to use - * one of many reduction algorithms without modding the guts of - * the code with if statements everywhere. - */ - int (*redux)(mp_int*,mp_int*,mp_digit); - - /* find window size */ - x = mp_count_bits (X); - if (x <= 7) { - winsize = 2; - } else if (x <= 36) { - winsize = 3; - } else if (x <= 140) { - winsize = 4; - } else if (x <= 450) { - winsize = 5; - } else if (x <= 1303) { - winsize = 6; - } else if (x <= 3529) { - winsize = 7; - } else { - winsize = 8; - } - -#ifdef MP_LOW_MEM - if (winsize > 5) { - winsize = 5; - } -#endif - - /* init M array */ - /* init first cell */ - if ((err = mp_init(&M[1])) != MP_OKAY) { - return err; - } - - /* now init the second half of the array */ - for (x = 1<<(winsize-1); x < (1 << winsize); x++) { - if ((err = mp_init(&M[x])) != MP_OKAY) { - for (y = 1<<(winsize-1); y < x; y++) { - mp_clear (&M[y]); - } - mp_clear(&M[1]); - return err; - } - } - - /* determine and setup reduction code */ - if (redmode == 0) { -#ifdef BN_MP_MONTGOMERY_SETUP_C - /* now setup montgomery */ - if ((err = mp_montgomery_setup (P, &mp)) != MP_OKAY) { - goto LBL_M; - } -#else - err = MP_VAL; - goto LBL_M; -#endif - - /* automatically pick the comba one if available (saves quite a few calls/ifs) */ -#ifdef BN_FAST_MP_MONTGOMERY_REDUCE_C - if (((P->used * 2 + 1) < MP_WARRAY) && - P->used < (1 << ((CHAR_BIT * sizeof (mp_word)) - (2 * DIGIT_BIT)))) { - redux = fast_mp_montgomery_reduce; - } else -#endif - { -#ifdef BN_MP_MONTGOMERY_REDUCE_C - /* use slower baseline Montgomery method */ - redux = mp_montgomery_reduce; -#else - err = MP_VAL; - goto LBL_M; -#endif - } - } else if (redmode == 1) { -#if defined(BN_MP_DR_SETUP_C) && defined(BN_MP_DR_REDUCE_C) - /* setup DR reduction for moduli of the form B**k - b */ - mp_dr_setup(P, &mp); - redux = mp_dr_reduce; -#else - err = MP_VAL; - goto LBL_M; -#endif - } else { -#if defined(BN_MP_REDUCE_2K_SETUP_C) && defined(BN_MP_REDUCE_2K_C) - /* setup DR reduction for moduli of the form 2**k - b */ - if ((err = mp_reduce_2k_setup(P, &mp)) != MP_OKAY) { - goto LBL_M; - } - redux = mp_reduce_2k; -#else - err = MP_VAL; - goto LBL_M; -#endif - } - - /* setup result */ - if ((err = mp_init (&res)) != MP_OKAY) { - goto LBL_M; - } - - /* create M table - * - - * - * The first half of the table is not computed though accept for M[0] and M[1] - */ - - if (redmode == 0) { -#ifdef BN_MP_MONTGOMERY_CALC_NORMALIZATION_C - /* now we need R mod m */ - if ((err = mp_montgomery_calc_normalization (&res, P)) != MP_OKAY) { - goto LBL_RES; - } -#else - err = MP_VAL; - goto LBL_RES; -#endif - - /* now set M[1] to G * R mod m */ - if ((err = mp_mulmod (G, &res, P, &M[1])) != MP_OKAY) { - goto LBL_RES; - } - } else { - mp_set(&res, 1); - if ((err = mp_mod(G, P, &M[1])) != MP_OKAY) { - goto LBL_RES; - } - } - - /* compute the value at M[1<<(winsize-1)] by squaring M[1] (winsize-1) times */ - if ((err = mp_copy (&M[1], &M[1 << (winsize - 1)])) != MP_OKAY) { - goto LBL_RES; - } - - for (x = 0; x < (winsize - 1); x++) { - if ((err = mp_sqr (&M[1 << (winsize - 1)], &M[1 << (winsize - 1)])) != MP_OKAY) { - goto LBL_RES; - } - if ((err = redux (&M[1 << (winsize - 1)], P, mp)) != MP_OKAY) { - goto LBL_RES; - } - } - - /* create upper table */ - for (x = (1 << (winsize - 1)) + 1; x < (1 << winsize); x++) { - if ((err = mp_mul (&M[x - 1], &M[1], &M[x])) != MP_OKAY) { - goto LBL_RES; - } - if ((err = redux (&M[x], P, mp)) != MP_OKAY) { - goto LBL_RES; - } - } - - /* set initial mode and bit cnt */ - mode = 0; - bitcnt = 1; - buf = 0; - digidx = X->used - 1; - bitcpy = 0; - bitbuf = 0; - - for (;;) { - /* grab next digit as required */ - if (--bitcnt == 0) { - /* if digidx == -1 we are out of digits so break */ - if (digidx == -1) { - break; - } - /* read next digit and reset bitcnt */ - buf = X->dp[digidx--]; - bitcnt = (int)DIGIT_BIT; - } - - /* grab the next msb from the exponent */ - y = (mp_digit)(buf >> (DIGIT_BIT - 1)) & 1; - buf <<= (mp_digit)1; - - /* if the bit is zero and mode == 0 then we ignore it - * These represent the leading zero bits before the first 1 bit - * in the exponent. Technically this opt is not required but it - * does lower the # of trivial squaring/reductions used - */ - if (mode == 0 && y == 0) { - continue; - } - - /* if the bit is zero and mode == 1 then we square */ - if (mode == 1 && y == 0) { - if ((err = mp_sqr (&res, &res)) != MP_OKAY) { - goto LBL_RES; - } - if ((err = redux (&res, P, mp)) != MP_OKAY) { - goto LBL_RES; - } - continue; - } - - /* else we add it to the window */ - bitbuf |= (y << (winsize - ++bitcpy)); - mode = 2; - - if (bitcpy == winsize) { - /* ok window is filled so square as required and multiply */ - /* square first */ - for (x = 0; x < winsize; x++) { - if ((err = mp_sqr (&res, &res)) != MP_OKAY) { - goto LBL_RES; - } - if ((err = redux (&res, P, mp)) != MP_OKAY) { - goto LBL_RES; - } - } - - /* then multiply */ - if ((err = mp_mul (&res, &M[bitbuf], &res)) != MP_OKAY) { - goto LBL_RES; - } - if ((err = redux (&res, P, mp)) != MP_OKAY) { - goto LBL_RES; - } - - /* empty window and reset */ - bitcpy = 0; - bitbuf = 0; - mode = 1; - } - } - - /* if bits remain then square/multiply */ - if (mode == 2 && bitcpy > 0) { - /* square then multiply if the bit is set */ - for (x = 0; x < bitcpy; x++) { - if ((err = mp_sqr (&res, &res)) != MP_OKAY) { - goto LBL_RES; - } - if ((err = redux (&res, P, mp)) != MP_OKAY) { - goto LBL_RES; - } - - /* get next bit of the window */ - bitbuf <<= 1; - if ((bitbuf & (1 << winsize)) != 0) { - /* then multiply */ - if ((err = mp_mul (&res, &M[1], &res)) != MP_OKAY) { - goto LBL_RES; - } - if ((err = redux (&res, P, mp)) != MP_OKAY) { - goto LBL_RES; - } - } - } - } - - if (redmode == 0) { - /* fixup result if Montgomery reduction is used - * recall that any value in a Montgomery system is - * actually multiplied by R mod n. So we have - * to reduce one more time to cancel out the factor - * of R. - */ - if ((err = redux(&res, P, mp)) != MP_OKAY) { - goto LBL_RES; - } - } - - /* swap res with Y */ - mp_exch (&res, Y); - err = MP_OKAY; -LBL_RES:mp_clear (&res); -LBL_M: - mp_clear(&M[1]); - for (x = 1<<(winsize-1); x < (1 << winsize); x++) { - mp_clear (&M[x]); - } - return err; -} -#endif - - -#ifdef BN_FAST_S_MP_SQR_C -/* the jist of squaring... - * you do like mult except the offset of the tmpx [one that - * starts closer to zero] can't equal the offset of tmpy. - * So basically you set up iy like before then you min it with - * (ty-tx) so that it never happens. You double all those - * you add in the inner loop - -After that loop you do the squares and add them in. -*/ - -static int -fast_s_mp_sqr (mp_int * a, mp_int * b) -{ - int olduse, res, pa, ix, iz; - mp_digit W[MP_WARRAY], *tmpx; - mp_word W1; - - /* grow the destination as required */ - pa = a->used + a->used; - if (b->alloc < pa) { - if ((res = mp_grow (b, pa)) != MP_OKAY) { - return res; - } - } - - /* number of output digits to produce */ - W1 = 0; - for (ix = 0; ix < pa; ix++) { - int tx, ty, iy; - mp_word _W; - mp_digit *tmpy; - - /* clear counter */ - _W = 0; - - /* get offsets into the two bignums */ - ty = MIN(a->used-1, ix); - tx = ix - ty; - - /* setup temp aliases */ - tmpx = a->dp + tx; - tmpy = a->dp + ty; - - /* this is the number of times the loop will iterrate, essentially - while (tx++ < a->used && ty-- >= 0) { ... } - */ - iy = MIN(a->used-tx, ty+1); - - /* now for squaring tx can never equal ty - * we halve the distance since they approach at a rate of 2x - * and we have to round because odd cases need to be executed - */ - iy = MIN(iy, (ty-tx+1)>>1); - - /* execute loop */ - for (iz = 0; iz < iy; iz++) { - _W += ((mp_word)*tmpx++)*((mp_word)*tmpy--); - } - - /* double the inner product and add carry */ - _W = _W + _W + W1; - - /* even columns have the square term in them */ - if ((ix&1) == 0) { - _W += ((mp_word)a->dp[ix>>1])*((mp_word)a->dp[ix>>1]); - } - - /* store it */ - W[ix] = (mp_digit)(_W & MP_MASK); - - /* make next carry */ - W1 = _W >> ((mp_word)DIGIT_BIT); - } - - /* setup dest */ - olduse = b->used; - b->used = a->used+a->used; - - { - mp_digit *tmpb; - tmpb = b->dp; - for (ix = 0; ix < pa; ix++) { - *tmpb++ = W[ix] & MP_MASK; - } - - /* clear unused digits [that existed in the old copy of c] */ - for (; ix < olduse; ix++) { - *tmpb++ = 0; - } - } - mp_clamp (b); - return MP_OKAY; -} -#endif - - -#ifdef BN_MP_MUL_D_C -/* multiply by a digit */ -static int -mp_mul_d (mp_int * a, mp_digit b, mp_int * c) -{ - mp_digit u, *tmpa, *tmpc; - mp_word r; - int ix, res, olduse; - - /* make sure c is big enough to hold a*b */ - if (c->alloc < a->used + 1) { - if ((res = mp_grow (c, a->used + 1)) != MP_OKAY) { - return res; - } - } - - /* get the original destinations used count */ - olduse = c->used; - - /* set the sign */ - c->sign = a->sign; - - /* alias for a->dp [source] */ - tmpa = a->dp; - - /* alias for c->dp [dest] */ - tmpc = c->dp; - - /* zero carry */ - u = 0; - - /* compute columns */ - for (ix = 0; ix < a->used; ix++) { - /* compute product and carry sum for this term */ - r = ((mp_word) u) + ((mp_word)*tmpa++) * ((mp_word)b); - - /* mask off higher bits to get a single digit */ - *tmpc++ = (mp_digit) (r & ((mp_word) MP_MASK)); - - /* send carry into next iteration */ - u = (mp_digit) (r >> ((mp_word) DIGIT_BIT)); - } - - /* store final carry [if any] and increment ix offset */ - *tmpc++ = u; - ++ix; - - /* now zero digits above the top */ - while (ix++ < olduse) { - *tmpc++ = 0; - } - - /* set used count */ - c->used = a->used + 1; - mp_clamp(c); - - return MP_OKAY; -} -#endif diff --git a/components/wpa_supplicant/src/crypto/sha384.h b/components/wpa_supplicant/src/crypto/sha384.h index d946907c67..1a2c1edffb 100644 --- a/components/wpa_supplicant/src/crypto/sha384.h +++ b/components/wpa_supplicant/src/crypto/sha384.h @@ -10,6 +10,7 @@ #define SHA384_H #define SHA384_MAC_LEN 48 +#define SHA512_MAC_LEN 64 int hmac_sha384_vector(const u8 *key, size_t key_len, size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac); diff --git a/components/wpa_supplicant/src/tls/tls.h b/components/wpa_supplicant/src/crypto/tls.h similarity index 74% rename from components/wpa_supplicant/src/tls/tls.h rename to components/wpa_supplicant/src/crypto/tls.h index 3fe3fe5406..eb86937390 100644 --- a/components/wpa_supplicant/src/tls/tls.h +++ b/components/wpa_supplicant/src/crypto/tls.h @@ -38,7 +38,26 @@ enum tls_fail_reason { TLS_FAIL_SUBJECT_MISMATCH = 5, TLS_FAIL_ALTSUBJECT_MISMATCH = 6, TLS_FAIL_BAD_CERTIFICATE = 7, - TLS_FAIL_SERVER_CHAIN_PROBE = 8 + TLS_FAIL_SERVER_CHAIN_PROBE = 8, + TLS_FAIL_DOMAIN_SUFFIX_MISMATCH = 9, + TLS_FAIL_DOMAIN_MISMATCH = 10, + TLS_FAIL_INSUFFICIENT_KEY_LEN = 11, + TLS_FAIL_DN_MISMATCH = 12, +}; + + +#define TLS_MAX_ALT_SUBJECT 10 + +struct tls_cert_data { + int depth; + const char *subject; + const struct wpabuf *cert; + const u8 *hash; + size_t hash_len; + const char *altsubject[TLS_MAX_ALT_SUBJECT]; + int num_altsubject; + const char *serial_num; + int tod; }; union tls_event_data { @@ -50,13 +69,7 @@ union tls_event_data { const struct wpabuf *cert; } cert_fail; - struct { - int depth; - const char *subject; - const struct wpabuf *cert; - const u8 *hash; - size_t hash_len; - } peer_cert; + struct tls_cert_data peer_cert; struct { int is_local; @@ -71,6 +84,10 @@ struct tls_config { const char *pkcs11_module_path; int fips_mode; int cert_in_cb; + const char *openssl_ciphers; + unsigned int tls_session_lifetime; + unsigned int crl_reload_interval; + unsigned int tls_flags; void (*event_cb)(void *ctx, enum tls_event ev, union tls_event_data *data); @@ -82,8 +99,19 @@ struct tls_config { #define TLS_CONN_DISABLE_SESSION_TICKET BIT(2) #define TLS_CONN_REQUEST_OCSP BIT(3) #define TLS_CONN_REQUIRE_OCSP BIT(4) -#define TLS_CONN_SUITEB BIT(11) +#define TLS_CONN_DISABLE_TLSv1_1 BIT(5) +#define TLS_CONN_DISABLE_TLSv1_2 BIT(6) #define TLS_CONN_EAP_FAST BIT(7) +#define TLS_CONN_DISABLE_TLSv1_0 BIT(8) +#define TLS_CONN_EXT_CERT_CHECK BIT(9) +#define TLS_CONN_REQUIRE_OCSP_ALL BIT(10) +#define TLS_CONN_SUITEB BIT(11) +#define TLS_CONN_SUITEB_NO_ECDH BIT(12) +#define TLS_CONN_DISABLE_TLSv1_3 BIT(13) +#define TLS_CONN_ENABLE_TLSv1_0 BIT(14) +#define TLS_CONN_ENABLE_TLSv1_1 BIT(15) +#define TLS_CONN_ENABLE_TLSv1_2 BIT(16) +#define TLS_CONN_TEAP_ANON_DH BIT(17) #define TLS_CONN_USE_DEFAULT_CERT_BUNDLE BIT(18) /** @@ -97,6 +125,19 @@ struct tls_config { * %NULL to allow all subjects * @altsubject_match: String to match in the alternative subject of the peer * certificate or %NULL to allow all alternative subjects + * @suffix_match: Semicolon deliminated string of values to suffix match against + * the dNSName or CN of the peer certificate or %NULL to allow all domain names. + * This may allow subdomains and wildcard certificates. Each domain name label + * must have a full case-insensitive match. + * @domain_match: String to match in the dNSName or CN of the peer + * certificate or %NULL to allow all domain names. This requires a full, + * case-insensitive match. + * + * More than one match string can be provided by using semicolons to + * separate the strings (e.g., example.org;example.com). When multiple + * strings are specified, a match with any one of the values is + * considered a sufficient match for the certificate, i.e., the + * conditions are ORed together. * @client_cert: File or reference name for client X.509 certificate in PEM or * DER format * @client_cert_blob: client_cert as inlined data or %NULL if not used @@ -119,9 +160,16 @@ struct tls_config { * specific for now) * @cert_id: the certificate's id when using engine * @ca_cert_id: the CA certificate's id when using engine + * @openssl_ciphers: OpenSSL cipher configuration + * @openssl_ecdh_curves: OpenSSL ECDH curve configuration. %NULL for auto if + * supported, empty string to disable, or a colon-separated curve list. * @flags: Parameter options (TLS_CONN_*) * @ocsp_stapling_response: DER encoded file with cached OCSP stapling response * or %NULL if OCSP is not enabled + * @ocsp_stapling_response_multi: DER encoded file with cached OCSP stapling + * response list (OCSPResponseList for ocsp_multi in RFC 6961) or %NULL if + * ocsp_multi is not enabled + * @check_cert_subject: Client certificate subject name matching string * * TLS connection parameters to be configured with tls_connection_set_params() * and tls_global_set_params(). @@ -138,13 +186,18 @@ struct tls_connection_params { const char *ca_path; const char *subject_match; const char *altsubject_match; + const char *suffix_match; + const char *domain_match; const char *client_cert; + const char *client_cert2; const u8 *client_cert_blob; size_t client_cert_blob_len; const char *private_key; + const char *private_key2; const u8 *private_key_blob; size_t private_key_blob_len; const char *private_key_passwd; + const char *private_key_passwd2; const char *dh_file; const u8 *dh_blob; size_t dh_blob_len; @@ -156,9 +209,13 @@ struct tls_connection_params { const char *key_id; const char *cert_id; const char *ca_cert_id; + const char *openssl_ciphers; + const char *openssl_ecdh_curves; unsigned int flags; const char *ocsp_stapling_response; + const char *ocsp_stapling_response_multi; + const char *check_cert_subject; }; @@ -174,7 +231,7 @@ struct tls_connection_params { * authentication types), the TLS library wrapper should maintain a reference * counter and do global initialization only when moving from 0 to 1 reference. */ -void * tls_init(void); +void * tls_init(const struct tls_config *conf); /** * tls_deinit - Deinitialize TLS library @@ -221,6 +278,18 @@ void tls_connection_deinit(void *tls_ctx, struct tls_connection *conn); */ int tls_connection_established(void *tls_ctx, struct tls_connection *conn); +/** + * tls_connection_peer_serial_num - Fetch peer certificate serial number + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * Returns: Allocated string buffer containing the peer certificate serial + * number or %NULL on error. + * + * The caller is responsible for freeing the returned buffer with os_free(). + */ +char * tls_connection_peer_serial_num(void *tls_ctx, + struct tls_connection *conn); + /** * tls_connection_shutdown - Shutdown TLS connection * @tls_ctx: TLS context data from tls_init() @@ -235,6 +304,7 @@ int tls_connection_established(void *tls_ctx, struct tls_connection *conn); int tls_connection_shutdown(void *tls_ctx, struct tls_connection *conn); enum { + TLS_SET_PARAMS_ENGINE_PRV_BAD_PIN = -4, TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED = -3, TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED = -2 }; @@ -245,10 +315,12 @@ enum { * @conn: Connection context data from tls_connection_init() * @params: Connection parameters * Returns: 0 on success, -1 on failure, - * TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED (-2) on possible PIN error causing - * PKCS#11 engine failure, or + * TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED (-2) on error causing PKCS#11 engine + * failure, or * TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED (-3) on failure to verify the - * PKCS#11 engine private key. + * PKCS#11 engine private key, or + * TLS_SET_PARAMS_ENGINE_PRV_BAD_PIN (-4) on PIN error causing PKCS#11 engine + * failure. */ int __must_check tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, @@ -259,10 +331,12 @@ tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, * @tls_ctx: TLS context data from tls_init() * @params: Global TLS parameters * Returns: 0 on success, -1 on failure, - * TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED (-2) on possible PIN error causing - * PKCS#11 engine failure, or + * TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED (-2) on error causing PKCS#11 engine + * failure, or * TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED (-3) on failure to verify the - * PKCS#11 engine private key. + * PKCS#11 engine private key, or + * TLS_SET_PARAMS_ENGINE_PRV_BAD_PIN (-4) on PIN error causing PKCS#11 engine + * failure. */ int __must_check tls_global_set_params( void *tls_ctx, const struct tls_connection_params *params); @@ -272,9 +346,11 @@ int __must_check tls_global_set_params( * @tls_ctx: TLS context data from tls_init() * @check_crl: 0 = do not verify CRLs, 1 = verify CRL for the user certificate, * 2 = verify CRL for all certificates + * @strict: 0 = allow CRL time errors, 1 = do not allow CRL time errors * Returns: 0 on success, -1 on failure */ -int __must_check tls_global_set_verify(void *tls_ctx, int check_crl); +int __must_check tls_global_set_verify(void *tls_ctx, int check_crl, + int strict); /** * tls_connection_set_verify - Set certificate verification options @@ -286,7 +362,7 @@ int __must_check tls_global_set_verify(void *tls_ctx, int check_crl); * @session_ctx_len: Length of @session_ctx in bytes. * Returns: 0 on success, -1 on failure */ -int tls_connection_set_verify(void *tls_ctx, +int __must_check tls_connection_set_verify(void *tls_ctx, struct tls_connection *conn, int verify_peer, unsigned int flags, @@ -460,6 +536,19 @@ int __must_check tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn, u8 *ciphers); +/** + * tls_get_version - Get the current TLS version number + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * @buf: Buffer for returning the TLS version number + * @buflen: buf size + * Returns: 0 on success, -1 on failure + * + * Get the currently used TLS version number. + */ +int __must_check tls_get_version(void *tls_ctx, struct tls_connection *conn, + char *buf, size_t buflen); + /** * tls_get_cipher - Get current cipher name * @tls_ctx: TLS context data from tls_init() @@ -527,13 +616,6 @@ int tls_connection_get_read_alerts(void *tls_ctx, struct tls_connection *conn); int tls_connection_get_write_alerts(void *tls_ctx, struct tls_connection *conn); -/** - * tls_capabilities - Get supported TLS capabilities - * @tls_ctx: TLS context data from tls_init() - * Returns: Bit field of supported TLS capabilities (TLS_CAPABILITY_*) - */ -unsigned int tls_capabilities(void *tls_ctx); - typedef int (*tls_session_ticket_cb) (void *ctx, const u8 *ticket, size_t len, const u8 *client_random, const u8 *server_random, u8 *master_secret); @@ -542,7 +624,65 @@ int __must_check tls_connection_set_session_ticket_cb( void *tls_ctx, struct tls_connection *conn, tls_session_ticket_cb cb, void *ctx); -int tls_prf_sha1_md5(const u8 *secret, size_t secret_len, const char *label, - const u8 *seed, size_t seed_len, u8 *out, size_t outlen); +void tls_connection_set_log_cb(struct tls_connection *conn, + void (*log_cb)(void *ctx, const char *msg), + void *ctx); + +#define TLS_BREAK_VERIFY_DATA BIT(0) +#define TLS_BREAK_SRV_KEY_X_HASH BIT(1) +#define TLS_BREAK_SRV_KEY_X_SIGNATURE BIT(2) +#define TLS_DHE_PRIME_511B BIT(3) +#define TLS_DHE_PRIME_767B BIT(4) +#define TLS_DHE_PRIME_15 BIT(5) +#define TLS_DHE_PRIME_58B BIT(6) +#define TLS_DHE_NON_PRIME BIT(7) + +void tls_connection_set_test_flags(struct tls_connection *conn, u32 flags); + +int tls_get_library_version(char *buf, size_t buf_len); + +void tls_connection_set_success_data(struct tls_connection *conn, + struct wpabuf *data); + +void tls_connection_set_success_data_resumed(struct tls_connection *conn); + +const struct wpabuf * +tls_connection_get_success_data(struct tls_connection *conn); + +void tls_connection_remove_session(struct tls_connection *conn); + +/** + * tls_get_tls_unique - Fetch "tls-unique" for channel binding + * @conn: Connection context data from tls_connection_init() + * @buf: Buffer for returning the value + * @max_len: Maximum length of the buffer in bytes + * Returns: Number of bytes written to buf or -1 on error + * + * This function can be used to fetch "tls-unique" (RFC 5929, Section 3) which + * is the first TLS Finished message sent in the most recent TLS handshake of + * the TLS connection. + */ +int tls_get_tls_unique(struct tls_connection *conn, u8 *buf, size_t max_len); + +/** + * tls_connection_get_cipher_suite - Get current TLS cipher suite + * @conn: Connection context data from tls_connection_init() + * Returns: TLS cipher suite of the current connection or 0 on error + */ +u16 tls_connection_get_cipher_suite(struct tls_connection *conn); + +/** + * tls_connection_get_peer_subject - Get peer subject + * @conn: Connection context data from tls_connection_init() + * Returns: Peer subject or %NULL if not authenticated or not available + */ +const char * tls_connection_get_peer_subject(struct tls_connection *conn); + +/** + * tls_connection_get_own_cert_used - Was own certificate used + * @conn: Connection context data from tls_connection_init() + * Returns: true if own certificate was used during authentication + */ +bool tls_connection_get_own_cert_used(struct tls_connection *conn); #endif /* TLS_H */ diff --git a/components/wpa_supplicant/src/tls/tls_internal.c b/components/wpa_supplicant/src/crypto/tls_internal.c similarity index 76% rename from components/wpa_supplicant/src/tls/tls_internal.c rename to components/wpa_supplicant/src/crypto/tls_internal.c index 1db85731c2..8095b43bd2 100644 --- a/components/wpa_supplicant/src/tls/tls_internal.c +++ b/components/wpa_supplicant/src/crypto/tls_internal.c @@ -1,6 +1,6 @@ /* * TLS interface functions and an internal TLS implementation - * Copyright (c) 2004-2011, Jouni Malinen + * Copyright (c) 2004-2019, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -9,30 +9,35 @@ * integrated TLSv1 implementation. */ -#include "utils/includes.h" +#include "includes.h" -#include "utils/common.h" -#include "crypto/sha1.h" -#include "crypto/md5.h" -#include "tls/tls.h" +#include "common.h" +#include "tls.h" #include "tls/tlsv1_client.h" #include "tls/tlsv1_server.h" + static int tls_ref_count = 0; struct tls_global { int server; struct tlsv1_credentials *server_cred; int check_crl; + + void (*event_cb)(void *ctx, enum tls_event ev, + union tls_event_data *data); + void *cb_ctx; + int cert_in_cb; }; struct tls_connection { struct tlsv1_client *client; struct tlsv1_server *server; + struct tls_global *global; }; -void * tls_init(void) +void * tls_init(const struct tls_config *conf) { struct tls_global *global; @@ -48,9 +53,14 @@ void * tls_init(void) } tls_ref_count++; - global = (struct tls_global *)os_zalloc(sizeof(*global)); + global = os_zalloc(sizeof(*global)); if (global == NULL) return NULL; + if (conf) { + global->event_cb = conf->event_cb; + global->cb_ctx = conf->cb_ctx; + global->cert_in_cb = conf->cert_in_cb; + } return global; } @@ -64,10 +74,12 @@ void tls_deinit(void *ssl_ctx) tlsv1_client_global_deinit(); #endif /* CONFIG_TLS_INTERNAL_CLIENT */ #ifdef CONFIG_TLS_INTERNAL_SERVER - tlsv1_cred_free(global->server_cred); tlsv1_server_global_deinit(); #endif /* CONFIG_TLS_INTERNAL_SERVER */ } +#ifdef CONFIG_TLS_INTERNAL_SERVER + tlsv1_cred_free(global->server_cred); +#endif /* CONFIG_TLS_INTERNAL_SERVER */ os_free(global); } @@ -83,9 +95,11 @@ struct tls_connection * tls_connection_init(void *tls_ctx) struct tls_connection *conn; struct tls_global *global = tls_ctx; - conn = (struct tls_connection *)os_zalloc(sizeof(*conn)); + conn = os_zalloc(sizeof(*conn)); if (conn == NULL) return NULL; + conn->global = global; + #ifdef CONFIG_TLS_INTERNAL_CLIENT if (!global->server) { conn->client = tlsv1_client_init(); @@ -93,6 +107,8 @@ struct tls_connection * tls_connection_init(void *tls_ctx) os_free(conn); return NULL; } + tlsv1_client_set_cb(conn->client, global->event_cb, + global->cb_ctx, global->cert_in_cb); } #endif /* CONFIG_TLS_INTERNAL_CLIENT */ #ifdef CONFIG_TLS_INTERNAL_SERVER @@ -109,6 +125,28 @@ struct tls_connection * tls_connection_init(void *tls_ctx) } +#ifdef CONFIG_TESTING_OPTIONS +#ifdef CONFIG_TLS_INTERNAL_SERVER +void tls_connection_set_test_flags(struct tls_connection *conn, u32 flags) +{ + if (conn->server) + tlsv1_server_set_test_flags(conn->server, flags); +} +#endif /* CONFIG_TLS_INTERNAL_SERVER */ +#endif /* CONFIG_TESTING_OPTIONS */ + + +void tls_connection_set_log_cb(struct tls_connection *conn, + void (*log_cb)(void *ctx, const char *msg), + void *ctx) +{ +#ifdef CONFIG_TLS_INTERNAL_SERVER + if (conn->server) + tlsv1_server_set_log_cb(conn->server, log_cb, ctx); +#endif /* CONFIG_TLS_INTERNAL_SERVER */ +} + + void tls_connection_deinit(void *tls_ctx, struct tls_connection *conn) { if (conn == NULL) @@ -139,6 +177,14 @@ int tls_connection_established(void *tls_ctx, struct tls_connection *conn) } +char * tls_connection_peer_serial_num(void *tls_ctx, + struct tls_connection *conn) +{ + /* TODO */ + return NULL; +} + + int tls_connection_shutdown(void *tls_ctx, struct tls_connection *conn) { #ifdef CONFIG_TLS_INTERNAL_CLIENT @@ -162,10 +208,52 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, if (conn->client == NULL) return -1; + if (params->flags & TLS_CONN_EXT_CERT_CHECK) { + wpa_printf(MSG_INFO, + "TLS: tls_ext_cert_check=1 not supported"); + return -1; + } + cred = tlsv1_cred_alloc(); if (cred == NULL) return -1; + if (params->subject_match) { + wpa_printf(MSG_INFO, "TLS: subject_match not supported"); + tlsv1_cred_free(cred); + return -1; + } + + if (params->altsubject_match) { + wpa_printf(MSG_INFO, "TLS: altsubject_match not supported"); + tlsv1_cred_free(cred); + return -1; + } + + if (params->suffix_match) { + wpa_printf(MSG_INFO, "TLS: suffix_match not supported"); + tlsv1_cred_free(cred); + return -1; + } + + if (params->domain_match) { + wpa_printf(MSG_INFO, "TLS: domain_match not supported"); + tlsv1_cred_free(cred); + return -1; + } + + if (params->openssl_ciphers) { + wpa_printf(MSG_INFO, "TLS: openssl_ciphers not supported"); + tlsv1_cred_free(cred); + return -1; + } + + if (params->openssl_ecdh_curves) { + wpa_printf(MSG_INFO, "TLS: openssl_ecdh_curves not supported"); + tlsv1_cred_free(cred); + return -1; + } + if (tlsv1_set_ca_cert(cred, params->ca_cert, params->ca_cert_blob, params->ca_cert_blob_len, params->ca_path)) { @@ -193,13 +281,19 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, return -1; } + if (tlsv1_set_dhparams(cred, params->dh_file, params->dh_blob, + params->dh_blob_len)) { + wpa_printf(MSG_INFO, "TLS: Failed to load DH parameters"); + tlsv1_cred_free(cred); + return -1; + } + if (tlsv1_client_set_cred(conn->client, cred) < 0) { tlsv1_cred_free(cred); return -1; } - tlsv1_client_set_time_checks( - conn->client, !(params->flags & TLS_CONN_DISABLE_TIME_CHECKS)); + tlsv1_client_set_flags(conn->client, params->flags); return 0; #else /* CONFIG_TLS_INTERNAL_CLIENT */ @@ -215,6 +309,9 @@ int tls_global_set_params(void *tls_ctx, struct tls_global *global = tls_ctx; struct tlsv1_credentials *cred; + if (params->check_cert_subject) + return -1; /* not yet supported */ + /* Currently, global parameters are only set when running in server * mode. */ global->server = 1; @@ -251,6 +348,13 @@ int tls_global_set_params(void *tls_ctx, return -1; } + if (params->ocsp_stapling_response) + cred->ocsp_stapling_response = + os_strdup(params->ocsp_stapling_response); + if (params->ocsp_stapling_response_multi) + cred->ocsp_stapling_response_multi = + os_strdup(params->ocsp_stapling_response_multi); + return 0; #else /* CONFIG_TLS_INTERNAL_SERVER */ return -1; @@ -258,7 +362,7 @@ int tls_global_set_params(void *tls_ctx, } -int tls_global_set_verify(void *tls_ctx, int check_crl) +int tls_global_set_verify(void *tls_ctx, int check_crl, int strict) { struct tls_global *global = tls_ctx; global->check_crl = check_crl; @@ -279,7 +383,7 @@ int tls_connection_set_verify(void *tls_ctx, struct tls_connection *conn, int tls_connection_get_random(void *tls_ctx, struct tls_connection *conn, - struct tls_random *data) + struct tls_random *data) { #ifdef CONFIG_TLS_INTERNAL_CLIENT if (conn->client) @@ -328,15 +432,15 @@ static int tls_connection_prf(void *tls_ctx, struct tls_connection *conn, #ifdef CONFIG_TLS_INTERNAL_CLIENT if (conn->client) { - ret = tlsv1_client_prf(conn->client, label, - server_random_first, + ret = tlsv1_client_prf(conn->client, label, context, + context_len, server_random_first, _out, skip + out_len); } #endif /* CONFIG_TLS_INTERNAL_CLIENT */ #ifdef CONFIG_TLS_INTERNAL_SERVER if (conn->server) { - ret = tlsv1_server_prf(conn->server, label, - server_random_first, + ret = tlsv1_server_prf(conn->server, label, context, + context_len, server_random_first, _out, skip + out_len); } #endif /* CONFIG_TLS_INTERNAL_SERVER */ @@ -394,9 +498,8 @@ struct wpabuf * tls_connection_handshake2(void *tls_ctx, in_data ? wpabuf_head(in_data) : NULL, in_data ? wpabuf_len(in_data) : 0, &res_len, &ad, &ad_len, need_more_data); - if (res == NULL) { + if (res == NULL) return NULL; - } out = wpabuf_alloc_ext_data(res, res_len); if (out == NULL) { os_free(res); @@ -576,6 +679,19 @@ int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn, } +int tls_get_version(void *ssl_ctx, struct tls_connection *conn, + char *buf, size_t buflen) +{ + if (conn == NULL) + return -1; +#ifdef CONFIG_TLS_INTERNAL_CLIENT + if (conn->client) + return tlsv1_client_get_version(conn->client, buf, buflen); +#endif /* CONFIG_TLS_INTERNAL_CLIENT */ + return -1; +} + + int tls_get_cipher(void *tls_ctx, struct tls_connection *conn, char *buf, size_t buflen) { @@ -616,12 +732,20 @@ int tls_connection_client_hello_ext(void *tls_ctx, struct tls_connection *conn, int tls_connection_get_failed(void *tls_ctx, struct tls_connection *conn) { +#ifdef CONFIG_TLS_INTERNAL_SERVER + if (conn->server) + return tlsv1_server_get_failed(conn->server); +#endif /* CONFIG_TLS_INTERNAL_SERVER */ return 0; } int tls_connection_get_read_alerts(void *tls_ctx, struct tls_connection *conn) { +#ifdef CONFIG_TLS_INTERNAL_SERVER + if (conn->server) + return tlsv1_server_get_read_alerts(conn->server); +#endif /* CONFIG_TLS_INTERNAL_SERVER */ return 0; } @@ -629,13 +753,13 @@ int tls_connection_get_read_alerts(void *tls_ctx, struct tls_connection *conn) int tls_connection_get_write_alerts(void *tls_ctx, struct tls_connection *conn) { +#ifdef CONFIG_TLS_INTERNAL_SERVER + if (conn->server) + return tlsv1_server_get_write_alerts(conn->server); +#endif /* CONFIG_TLS_INTERNAL_SERVER */ return 0; } -unsigned int tls_capabilities(void *tls_ctx) -{ - return 0; -} int tls_connection_set_session_ticket_cb(void *tls_ctx, struct tls_connection *conn, @@ -656,3 +780,32 @@ int tls_connection_set_session_ticket_cb(void *tls_ctx, #endif /* CONFIG_TLS_INTERNAL_SERVER */ return -1; } + + +int tls_get_library_version(char *buf, size_t buf_len) +{ + return os_snprintf(buf, buf_len, "internal"); +} + + +void tls_connection_set_success_data(struct tls_connection *conn, + struct wpabuf *data) +{ +} + + +void tls_connection_set_success_data_resumed(struct tls_connection *conn) +{ +} + + +const struct wpabuf * +tls_connection_get_success_data(struct tls_connection *conn) +{ + return NULL; +} + + +void tls_connection_remove_session(struct tls_connection *conn) +{ +} diff --git a/components/wpa_supplicant/src/eap_peer/eap.c b/components/wpa_supplicant/src/eap_peer/eap.c index 24c282b749..1d4b5036e5 100644 --- a/components/wpa_supplicant/src/eap_peer/eap.c +++ b/components/wpa_supplicant/src/eap_peer/eap.c @@ -30,7 +30,7 @@ #include "crypto/sha256.h" #include "utils/ext_password.h" -#include "tls/tls.h" +#include "crypto/tls.h" #include "eap_peer/eap_i.h" #include "eap_peer/eap_config.h" #include "eap_peer/eap.h" @@ -296,12 +296,10 @@ int eap_peer_register_methods(void) ret = eap_peer_mschapv2_register(); #endif -#ifndef USE_MBEDTLS_CRYPTO #ifdef EAP_FAST if (ret == 0) ret = eap_peer_fast_register(); #endif -#endif #ifdef EAP_PEAP if (ret == 0) @@ -620,7 +618,7 @@ int eap_peer_config_init( config_methods[allowed_method_count].vendor = EAP_VENDOR_IETF; config_methods[allowed_method_count++].method = EAP_TYPE_TLS; } -#ifndef USE_MBEDTLS_CRYPTO +#ifdef EAP_FAST if (g_wpa_pac_file) { //set EAP-FAST config_methods[allowed_method_count].vendor = EAP_VENDOR_IETF; diff --git a/components/wpa_supplicant/src/eap_peer/eap_fast.c b/components/wpa_supplicant/src/eap_peer/eap_fast.c index b54bae3738..37e0ef18b7 100644 --- a/components/wpa_supplicant/src/eap_peer/eap_fast.c +++ b/components/wpa_supplicant/src/eap_peer/eap_fast.c @@ -9,7 +9,7 @@ #include "includes.h" #include "common.h" -#include "tls/tls.h" +#include "crypto/tls.h" #include "crypto/sha1.h" #include "eap_peer/eap_tlv_common.h" #include "eap_peer/eap_methods.h" diff --git a/components/wpa_supplicant/src/eap_peer/eap_fast_common.c b/components/wpa_supplicant/src/eap_peer/eap_fast_common.c index 9702472531..bb64ca750b 100644 --- a/components/wpa_supplicant/src/eap_peer/eap_fast_common.c +++ b/components/wpa_supplicant/src/eap_peer/eap_fast_common.c @@ -10,7 +10,7 @@ #include "common.h" #include "crypto/sha1.h" -#include "tls/tls.h" +#include "crypto/tls.h" #include "eap_peer/eap_defs.h" #include "eap_peer/eap_tlv_common.h" #include "eap_peer/eap_fast_common.h" diff --git a/components/wpa_supplicant/src/eap_peer/eap_mschapv2.c b/components/wpa_supplicant/src/eap_peer/eap_mschapv2.c index 93fd16ac02..a8c99fff18 100644 --- a/components/wpa_supplicant/src/eap_peer/eap_mschapv2.c +++ b/components/wpa_supplicant/src/eap_peer/eap_mschapv2.c @@ -12,7 +12,7 @@ #include "rsn_supp/wpa.h" #include "crypto/random.h" #include "crypto/ms_funcs.h" -#include "tls/tls.h" +#include "crypto/tls.h" #include "eap_peer/eap_i.h" #include "eap_peer/eap_defs.h" #include "eap_peer/eap_tls_common.h" diff --git a/components/wpa_supplicant/src/eap_peer/eap_peap.c b/components/wpa_supplicant/src/eap_peer/eap_peap.c index b21ab6bac7..866dbee118 100644 --- a/components/wpa_supplicant/src/eap_peer/eap_peap.c +++ b/components/wpa_supplicant/src/eap_peer/eap_peap.c @@ -10,7 +10,7 @@ #ifdef EAP_PEAP #include "utils/common.h" #include "crypto/sha1.h" -#include "tls/tls.h" +#include "crypto/tls.h" #include "eap_peer/eap_tlv_common.h" #include "eap_peer/eap_peap_common.h" #include "eap_peer/eap_i.h" diff --git a/components/wpa_supplicant/src/eap_peer/eap_tls.c b/components/wpa_supplicant/src/eap_peer/eap_tls.c index 0a30fc906c..0da0c7f86a 100644 --- a/components/wpa_supplicant/src/eap_peer/eap_tls.c +++ b/components/wpa_supplicant/src/eap_peer/eap_tls.c @@ -9,7 +9,7 @@ #ifdef EAP_TLS #include "utils/common.h" -#include "tls/tls.h" +#include "crypto/tls.h" #include "eap_peer/eap_i.h" #include "eap_peer/eap_defs.h" #include "eap_peer/eap_tls_common.h" diff --git a/components/wpa_supplicant/src/eap_peer/eap_tls_common.c b/components/wpa_supplicant/src/eap_peer/eap_tls_common.c index 6201eb7c50..bb13ccf8dc 100644 --- a/components/wpa_supplicant/src/eap_peer/eap_tls_common.c +++ b/components/wpa_supplicant/src/eap_peer/eap_tls_common.c @@ -10,7 +10,7 @@ #include "utils/common.h" #include "crypto/sha1.h" -#include "tls/tls.h" +#include "crypto/tls.h" #include "eap_peer/eap_i.h" #include "eap_peer/eap_tls_common.h" #include "eap_peer/eap_config.h" diff --git a/components/wpa_supplicant/src/eap_peer/eap_ttls.c b/components/wpa_supplicant/src/eap_peer/eap_ttls.c index 933f035e66..fdd3e1005a 100644 --- a/components/wpa_supplicant/src/eap_peer/eap_ttls.c +++ b/components/wpa_supplicant/src/eap_peer/eap_ttls.c @@ -12,7 +12,7 @@ #include "utils/common.h" #include "crypto/ms_funcs.h" #include "crypto/sha1.h" -#include "tls/tls.h" +#include "crypto/tls.h" #include "eap_peer/chap.h" #include "eap_peer/eap.h" #include "eap_peer/eap_ttls.h" diff --git a/components/wpa_supplicant/src/tls/asn1.c b/components/wpa_supplicant/src/tls/asn1.c index 08d476254f..04d5320490 100644 --- a/components/wpa_supplicant/src/tls/asn1.c +++ b/components/wpa_supplicant/src/tls/asn1.c @@ -1,15 +1,172 @@ /* * ASN.1 DER parsing - * Copyright (c) 2006, Jouni Malinen + * Copyright (c) 2006-2014, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ -#include "utils/includes.h" +#include "includes.h" + +#include "common.h" +#include "utils/wpabuf.h" +#include "asn1.h" + +const struct asn1_oid asn1_sha1_oid = { + .oid = { 1, 3, 14, 3, 2, 26 }, + .len = 6 +}; + +const struct asn1_oid asn1_sha256_oid = { + .oid = { 2, 16, 840, 1, 101, 3, 4, 2, 1 }, + .len = 9 +}; + +const struct asn1_oid asn1_ec_public_key_oid = { + .oid = { 1, 2, 840, 10045, 2, 1 }, + .len = 6 +}; + +const struct asn1_oid asn1_prime256v1_oid = { + .oid = { 1, 2, 840, 10045, 3, 1, 7 }, + .len = 7 +}; + +const struct asn1_oid asn1_secp384r1_oid = { + .oid = { 1, 3, 132, 0, 34 }, + .len = 5 +}; + +const struct asn1_oid asn1_secp521r1_oid = { + .oid = { 1, 3, 132, 0, 35 }, + .len = 5 +}; + +const struct asn1_oid asn1_brainpoolP256r1_oid = { + .oid = { 1, 3, 36, 3, 3, 2, 8, 1, 1, 7 }, + .len = 10 +}; + +const struct asn1_oid asn1_brainpoolP384r1_oid = { + .oid = { 1, 3, 36, 3, 3, 2, 8, 1, 1, 11 }, + .len = 10 +}; + +const struct asn1_oid asn1_brainpoolP512r1_oid = { + .oid = { 1, 3, 36, 3, 3, 2, 8, 1, 1, 13 }, + .len = 10 +}; + +const struct asn1_oid asn1_aes_siv_cmac_aead_256_oid = { + .oid = { 1, 2, 840, 113549, 1, 9, 16, 3, 22 }, + .len = 9 +}; + +const struct asn1_oid asn1_aes_siv_cmac_aead_384_oid = { + .oid = { 1, 2, 840, 113549, 1, 9, 16, 3, 23 }, + .len = 9 +}; + +const struct asn1_oid asn1_aes_siv_cmac_aead_512_oid = { + .oid = { 1, 2, 840, 113549, 1, 9, 16, 3, 24 }, + .len = 9 +}; + +const struct asn1_oid asn1_pbkdf2_oid = { + .oid = { 1, 2, 840, 113549, 1, 5, 12 }, + .len = 7 +}; + +const struct asn1_oid asn1_pbkdf2_hmac_sha256_oid = { + .oid = { 1, 2, 840, 113549, 2, 9 }, + .len = 6 +}; + +const struct asn1_oid asn1_pbkdf2_hmac_sha384_oid = { + .oid = { 1, 2, 840, 113549, 2, 10 }, + .len = 6 +}; + +const struct asn1_oid asn1_pbkdf2_hmac_sha512_oid = { + .oid = { 1, 2, 840, 113549, 2, 11 }, + .len = 6 +}; + +const struct asn1_oid asn1_dpp_config_params_oid = { + .oid = { 1, 3, 6, 1, 4, 1, 40808, 1, 2, 1 }, + .len = 10 +}; + +const struct asn1_oid asn1_dpp_asymmetric_key_package_oid = { + .oid = { 1, 3, 6, 1, 4, 1, 40808, 1, 2, 2 }, + .len = 10 +}; + + +static int asn1_valid_der_boolean(struct asn1_hdr *hdr) +{ + /* Enforce DER requirements for a single way of encoding a BOOLEAN */ + if (hdr->length != 1) { + wpa_printf(MSG_DEBUG, "ASN.1: Unexpected BOOLEAN length (%u)", + hdr->length); + return 0; + } + + if (hdr->payload[0] != 0 && hdr->payload[0] != 0xff) { + wpa_printf(MSG_DEBUG, + "ASN.1: Invalid BOOLEAN value 0x%x (DER requires 0 or 0xff)", + hdr->payload[0]); + return 0; + } + + return 1; +} + + +static int asn1_valid_der(struct asn1_hdr *hdr) +{ + if (hdr->class != ASN1_CLASS_UNIVERSAL) + return 1; + if (hdr->tag == ASN1_TAG_BOOLEAN && !asn1_valid_der_boolean(hdr)) + return 0; + if (hdr->tag == ASN1_TAG_NULL && hdr->length != 0) + return 0; + + /* Check for allowed primitive/constructed values */ + if (hdr->constructed && + (hdr->tag == ASN1_TAG_BOOLEAN || + hdr->tag == ASN1_TAG_INTEGER || + hdr->tag == ASN1_TAG_NULL || + hdr->tag == ASN1_TAG_OID || + hdr->tag == ANS1_TAG_RELATIVE_OID || + hdr->tag == ASN1_TAG_REAL || + hdr->tag == ASN1_TAG_ENUMERATED || + hdr->tag == ASN1_TAG_BITSTRING || + hdr->tag == ASN1_TAG_OCTETSTRING || + hdr->tag == ASN1_TAG_NUMERICSTRING || + hdr->tag == ASN1_TAG_PRINTABLESTRING || + hdr->tag == ASN1_TAG_T61STRING || + hdr->tag == ASN1_TAG_VIDEOTEXSTRING || + hdr->tag == ASN1_TAG_VISIBLESTRING || + hdr->tag == ASN1_TAG_IA5STRING || + hdr->tag == ASN1_TAG_GRAPHICSTRING || + hdr->tag == ASN1_TAG_GENERALSTRING || + hdr->tag == ASN1_TAG_UNIVERSALSTRING || + hdr->tag == ASN1_TAG_UTF8STRING || + hdr->tag == ASN1_TAG_BMPSTRING || + hdr->tag == ASN1_TAG_CHARACTERSTRING || + hdr->tag == ASN1_TAG_UTCTIME || + hdr->tag == ASN1_TAG_GENERALIZEDTIME || + hdr->tag == ASN1_TAG_TIME)) + return 0; + if (!hdr->constructed && + (hdr->tag == ASN1_TAG_SEQUENCE || + hdr->tag == ASN1_TAG_SET)) + return 0; + + return 1; +} -#include "utils/common.h" -#include "tls/asn1.h" int asn1_get_next(const u8 *buf, size_t len, struct asn1_hdr *hdr) { @@ -20,26 +177,51 @@ int asn1_get_next(const u8 *buf, size_t len, struct asn1_hdr *hdr) pos = buf; end = buf + len; + if (pos >= end) { + wpa_printf(MSG_DEBUG, "ASN.1: No room for Identifier"); + return -1; + } hdr->identifier = *pos++; hdr->class = hdr->identifier >> 6; hdr->constructed = !!(hdr->identifier & (1 << 5)); if ((hdr->identifier & 0x1f) == 0x1f) { + size_t ext_len = 0; + hdr->tag = 0; + if (pos == end || (*pos & 0x7f) == 0) { + wpa_printf(MSG_DEBUG, + "ASN.1: Invalid extended tag (first octet has to be included with at least one nonzero bit for the tag value)"); + return -1; + } do { if (pos >= end) { wpa_printf(MSG_DEBUG, "ASN.1: Identifier " "underflow"); return -1; } + ext_len++; tmp = *pos++; - wpa_printf(MSG_DEBUG, "ASN.1: Extended tag data: " + wpa_printf(MSG_MSGDUMP, "ASN.1: Extended tag data: " "0x%02x", tmp); hdr->tag = (hdr->tag << 7) | (tmp & 0x7f); } while (tmp & 0x80); + wpa_printf(MSG_MSGDUMP, "ASN.1: Extended Tag: 0x%x (len=%zu)", + hdr->tag, ext_len); + if ((hdr->class != ASN1_CLASS_PRIVATE && hdr->tag < 31) || + ext_len * 7 > sizeof(hdr->tag) * 8) { + wpa_printf(MSG_DEBUG, + "ASN.1: Invalid or unsupported (too large) extended Tag: 0x%x (len=%zu)", + hdr->tag, ext_len); + return -1; + } } else hdr->tag = hdr->identifier & 0x1f; + if (pos >= end) { + wpa_printf(MSG_DEBUG, "ASN.1: No room for Length"); + return -1; + } tmp = *pos++; if (tmp & 0x80) { if (tmp == 0xff) { @@ -49,6 +231,11 @@ int asn1_get_next(const u8 *buf, size_t len, struct asn1_hdr *hdr) } tmp &= 0x7f; /* number of subsequent octets */ hdr->length = 0; + if (tmp == 0 || pos == end || *pos == 0) { + wpa_printf(MSG_DEBUG, + "ASN.1: Definite long form of the length does not start with a nonzero value"); + return -1; + } if (tmp > 4) { wpa_printf(MSG_DEBUG, "ASN.1: Too long length field"); return -1; @@ -61,6 +248,11 @@ int asn1_get_next(const u8 *buf, size_t len, struct asn1_hdr *hdr) } hdr->length = (hdr->length << 8) | *pos++; } + if (hdr->length < 128) { + wpa_printf(MSG_DEBUG, + "ASN.1: Definite long form of the length used with too short length"); + return -1; + } } else { /* Short form - length 0..127 in one octet */ hdr->length = tmp; @@ -72,10 +264,29 @@ int asn1_get_next(const u8 *buf, size_t len, struct asn1_hdr *hdr) } hdr->payload = pos; + + if (!asn1_valid_der(hdr)) { + asn1_print_hdr(hdr, "ASN.1: Invalid DER encoding: "); + return -1; + } return 0; } +void asn1_print_hdr(const struct asn1_hdr *hdr, const char *title) +{ + wpa_printf(MSG_DEBUG, "%sclass %d constructed %d tag 0x%x", + title, hdr->class, hdr->constructed, hdr->tag); +} + + +void asn1_unexpected(const struct asn1_hdr *hdr, const char *title) +{ + wpa_printf(MSG_DEBUG, "%s - found class %d constructed %d tag 0x%x", + title, hdr->class, hdr->constructed, hdr->tag); +} + + int asn1_parse_oid(const u8 *buf, size_t len, struct asn1_oid *oid) { const u8 *pos, *end; @@ -125,12 +336,9 @@ int asn1_get_oid(const u8 *buf, size_t len, struct asn1_oid *oid, { struct asn1_hdr hdr; - if (asn1_get_next(buf, len, &hdr) < 0 || hdr.length == 0) - return -1; - - if (hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_OID) { - wpa_printf(MSG_DEBUG, "ASN.1: Expected OID - found class %d " - "tag 0x%x", hdr.class, hdr.tag); + if (asn1_get_next(buf, len, &hdr) < 0 || hdr.length == 0 || + !asn1_is_oid(&hdr)) { + asn1_unexpected(&hdr, "ASN.1: Expected OID"); return -1; } @@ -140,7 +348,7 @@ int asn1_get_oid(const u8 *buf, size_t len, struct asn1_oid *oid, } -void asn1_oid_to_str(struct asn1_oid *oid, char *buf, size_t len) +void asn1_oid_to_str(const struct asn1_oid *oid, char *buf, size_t len) { char *pos = buf; size_t i; @@ -155,7 +363,7 @@ void asn1_oid_to_str(struct asn1_oid *oid, char *buf, size_t len) ret = os_snprintf(pos, buf + len - pos, "%s%lu", i == 0 ? "" : ".", oid->oid[i]); - if (ret < 0 || ret >= buf + len - pos) + if (os_snprintf_error(buf + len - pos, ret)) break; pos += ret; } @@ -204,3 +412,239 @@ unsigned long asn1_bit_string_to_long(const u8 *buf, size_t len) return val; } + + +int asn1_oid_equal(const struct asn1_oid *a, const struct asn1_oid *b) +{ + size_t i; + + if (a->len != b->len) + return 0; + + for (i = 0; i < a->len; i++) { + if (a->oid[i] != b->oid[i]) + return 0; + } + + return 1; +} + + +int asn1_get_integer(const u8 *buf, size_t len, int *integer, const u8 **next) +{ + struct asn1_hdr hdr; + size_t left; + const u8 *pos; + int value; + + if (asn1_get_next(buf, len, &hdr) < 0 || hdr.length == 0 || + !asn1_is_integer(&hdr)) { + asn1_unexpected(&hdr, "ASN.1: Expected INTEGER"); + return -1; + } + + *next = hdr.payload + hdr.length; + pos = hdr.payload; + left = hdr.length; + if (left > sizeof(value)) { + wpa_printf(MSG_DEBUG, "ASN.1: Too large INTEGER (len %u)", + hdr.length); + return -1; + } + value = 0; + while (left) { + value <<= 8; + value |= *pos++; + left--; + } + + *integer = value; + return 0; +} + + +int asn1_get_sequence(const u8 *buf, size_t len, struct asn1_hdr *hdr, + const u8 **next) +{ + if (asn1_get_next(buf, len, hdr) < 0 || !asn1_is_sequence(hdr)) { + asn1_unexpected(hdr, "ASN.1: Expected SEQUENCE"); + return -1; + } + + if (next) + *next = hdr->payload + hdr->length; + return 0; +} + + +int asn1_get_alg_id(const u8 *buf, size_t len, struct asn1_oid *oid, + const u8 **params, size_t *params_len, const u8 **next) +{ + const u8 *pos = buf, *end = buf + len; + struct asn1_hdr hdr; + + /* + * AlgorithmIdentifier ::= SEQUENCE { + * algorithm OBJECT IDENTIFIER, + * parameters ANY DEFINED BY algorithm OPTIONAL} + */ + if (asn1_get_sequence(pos, end - pos, &hdr, next) < 0 || + asn1_get_oid(hdr.payload, hdr.length, oid, &pos) < 0) + return -1; + + if (params && params_len) { + *params = pos; + *params_len = hdr.payload + hdr.length - pos; + } + + return 0; +} + + +void asn1_put_integer(struct wpabuf *buf, int val) +{ + u8 bin[4]; + int zeros; + + WPA_PUT_BE32(bin, val); + zeros = 0; + while (zeros < 3 && bin[zeros] == 0) + zeros++; + wpabuf_put_u8(buf, ASN1_TAG_INTEGER); + wpabuf_put_u8(buf, 4 - zeros); + wpabuf_put_data(buf, &bin[zeros], 4 - zeros); +} + + +static void asn1_put_len(struct wpabuf *buf, size_t len) +{ + if (len <= 0x7f) { + wpabuf_put_u8(buf, len); + } else if (len <= 0xff) { + wpabuf_put_u8(buf, 0x80 | 1); + wpabuf_put_u8(buf, len); + } else if (len <= 0xffff) { + wpabuf_put_u8(buf, 0x80 | 2); + wpabuf_put_be16(buf, len); + } else if (len <= 0xffffff) { + wpabuf_put_u8(buf, 0x80 | 3); + wpabuf_put_be24(buf, len); + } else { + wpabuf_put_u8(buf, 0x80 | 4); + wpabuf_put_be32(buf, len); + } +} + + +void asn1_put_octet_string(struct wpabuf *buf, const struct wpabuf *val) +{ + wpabuf_put_u8(buf, ASN1_TAG_OCTETSTRING); + asn1_put_len(buf, wpabuf_len(val)); + wpabuf_put_buf(buf, val); +} + + +void asn1_put_oid(struct wpabuf *buf, const struct asn1_oid *oid) +{ + u8 *len; + size_t i; + + if (oid->len < 2) + return; + wpabuf_put_u8(buf, ASN1_TAG_OID); + len = wpabuf_put(buf, 1); + wpabuf_put_u8(buf, 40 * oid->oid[0] + oid->oid[1]); + for (i = 2; i < oid->len; i++) { + unsigned long val = oid->oid[i]; + u8 bytes[8]; + int idx = 0; + + while (val) { + bytes[idx] = (idx ? 0x80 : 0x00) | (val & 0x7f); + idx++; + val >>= 7; + } + if (idx == 0) { + bytes[idx] = 0; + idx = 1; + } + while (idx > 0) { + idx--; + wpabuf_put_u8(buf, bytes[idx]); + } + } + *len = (u8 *) wpabuf_put(buf, 0) - len - 1; +} + + +void asn1_put_hdr(struct wpabuf *buf, u8 class, int constructed, u8 tag, + size_t len) +{ + wpabuf_put_u8(buf, class << 6 | (constructed ? 0x20 : 0x00) | tag); + asn1_put_len(buf, len); +} + + +void asn1_put_sequence(struct wpabuf *buf, const struct wpabuf *payload) +{ + asn1_put_hdr(buf, ASN1_CLASS_UNIVERSAL, 1, ASN1_TAG_SEQUENCE, + wpabuf_len(payload)); + wpabuf_put_buf(buf, payload); +} + + +void asn1_put_set(struct wpabuf *buf, const struct wpabuf *payload) +{ + asn1_put_hdr(buf, ASN1_CLASS_UNIVERSAL, 1, ASN1_TAG_SET, + wpabuf_len(payload)); + wpabuf_put_buf(buf, payload); +} + + +void asn1_put_utf8string(struct wpabuf *buf, const char *val) +{ + asn1_put_hdr(buf, ASN1_CLASS_UNIVERSAL, 0, ASN1_TAG_UTF8STRING, + os_strlen(val)); + wpabuf_put_str(buf, val); +} + + +struct wpabuf * asn1_build_alg_id(const struct asn1_oid *oid, + const struct wpabuf *params) +{ + struct wpabuf *buf; + size_t len; + + /* + * AlgorithmIdentifier ::= SEQUENCE { + * algorithm OBJECT IDENTIFIER, + * parameters ANY DEFINED BY algorithm OPTIONAL} + */ + + len = 100; + if (params) + len += wpabuf_len(params); + buf = wpabuf_alloc(len); + if (!buf) + return NULL; + asn1_put_oid(buf, oid); + if (params) + wpabuf_put_buf(buf, params); + return asn1_encaps(buf, ASN1_CLASS_UNIVERSAL, ASN1_TAG_SEQUENCE); +} + + +struct wpabuf * asn1_encaps(struct wpabuf *buf, u8 class, u8 tag) +{ + struct wpabuf *res; + + if (!buf) + return NULL; + res = wpabuf_alloc(10 + wpabuf_len(buf)); + if (res) { + asn1_put_hdr(res, class, 1, tag, wpabuf_len(buf)); + wpabuf_put_buf(res, buf); + } + wpabuf_clear_free(buf); + return res; +} diff --git a/components/wpa_supplicant/src/tls/asn1.h b/components/wpa_supplicant/src/tls/asn1.h index 6342c4cc79..a4d1be4735 100644 --- a/components/wpa_supplicant/src/tls/asn1.h +++ b/components/wpa_supplicant/src/tls/asn1.h @@ -20,13 +20,15 @@ #define ASN1_TAG_EXTERNAL 0x08 /* not yet parsed */ #define ASN1_TAG_REAL 0x09 /* not yet parsed */ #define ASN1_TAG_ENUMERATED 0x0A /* not yet parsed */ +#define ASN1_TAG_EMBEDDED_PDV 0x0B /* not yet parsed */ #define ASN1_TAG_UTF8STRING 0x0C /* not yet parsed */ #define ANS1_TAG_RELATIVE_OID 0x0D +#define ASN1_TAG_TIME 0x0E #define ASN1_TAG_SEQUENCE 0x10 /* shall be constructed */ #define ASN1_TAG_SET 0x11 #define ASN1_TAG_NUMERICSTRING 0x12 /* not yet parsed */ #define ASN1_TAG_PRINTABLESTRING 0x13 -#define ASN1_TAG_TG1STRING 0x14 /* not yet parsed */ +#define ASN1_TAG_T61STRING 0x14 /* not yet parsed */ #define ASN1_TAG_VIDEOTEXSTRING 0x15 /* not yet parsed */ #define ASN1_TAG_IA5STRING 0x16 #define ASN1_TAG_UTCTIME 0x17 @@ -35,7 +37,8 @@ #define ASN1_TAG_VISIBLESTRING 0x1A #define ASN1_TAG_GENERALSTRING 0x1B /* not yet parsed */ #define ASN1_TAG_UNIVERSALSTRING 0x1C /* not yet parsed */ -#define ASN1_TAG_BMPSTRING 0x1D /* not yet parsed */ +#define ASN1_TAG_CHARACTERSTRING 0x1D /* not yet parsed */ +#define ASN1_TAG_BMPSTRING 0x1E /* not yet parsed */ #define ASN1_CLASS_UNIVERSAL 0 #define ASN1_CLASS_APPLICATION 1 @@ -57,10 +60,153 @@ struct asn1_oid { int asn1_get_next(const u8 *buf, size_t len, struct asn1_hdr *hdr); +void asn1_print_hdr(const struct asn1_hdr *hdr, const char *title); +void asn1_unexpected(const struct asn1_hdr *hdr, const char *title); int asn1_parse_oid(const u8 *buf, size_t len, struct asn1_oid *oid); int asn1_get_oid(const u8 *buf, size_t len, struct asn1_oid *oid, const u8 **next); -void asn1_oid_to_str(struct asn1_oid *oid, char *buf, size_t len); +void asn1_oid_to_str(const struct asn1_oid *oid, char *buf, size_t len); unsigned long asn1_bit_string_to_long(const u8 *buf, size_t len); +int asn1_oid_equal(const struct asn1_oid *a, const struct asn1_oid *b); +int asn1_get_integer(const u8 *buf, size_t len, int *integer, const u8 **next); +int asn1_get_sequence(const u8 *buf, size_t len, struct asn1_hdr *hdr, + const u8 **next); +int asn1_get_alg_id(const u8 *buf, size_t len, struct asn1_oid *oid, + const u8 **params, size_t *params_len, const u8 **next); +void asn1_put_integer(struct wpabuf *buf, int val); +void asn1_put_octet_string(struct wpabuf *buf, const struct wpabuf *val); +void asn1_put_oid(struct wpabuf *buf, const struct asn1_oid *oid); +void asn1_put_hdr(struct wpabuf *buf, u8 class, int constructed, u8 tag, + size_t len); +void asn1_put_sequence(struct wpabuf *buf, const struct wpabuf *payload); +void asn1_put_set(struct wpabuf *buf, const struct wpabuf *payload); +void asn1_put_utf8string(struct wpabuf *buf, const char *val); +struct wpabuf * asn1_build_alg_id(const struct asn1_oid *oid, + const struct wpabuf *params); +struct wpabuf * asn1_encaps(struct wpabuf *buf, u8 class, u8 tag); + +static inline bool asn1_is_oid(const struct asn1_hdr *hdr) +{ + return hdr->class == ASN1_CLASS_UNIVERSAL && + hdr->tag == ASN1_TAG_OID; +} + +static inline bool asn1_is_boolean(const struct asn1_hdr *hdr) +{ + return hdr->class == ASN1_CLASS_UNIVERSAL && + hdr->tag == ASN1_TAG_BOOLEAN; +} + +static inline bool asn1_is_integer(const struct asn1_hdr *hdr) +{ + return hdr->class == ASN1_CLASS_UNIVERSAL && + hdr->tag == ASN1_TAG_INTEGER; +} + +static inline bool asn1_is_enumerated(const struct asn1_hdr *hdr) +{ + return hdr->class == ASN1_CLASS_UNIVERSAL && + hdr->tag == ASN1_TAG_ENUMERATED; +} + +static inline bool asn1_is_sequence(const struct asn1_hdr *hdr) +{ + return hdr->class == ASN1_CLASS_UNIVERSAL && + hdr->tag == ASN1_TAG_SEQUENCE; +} + +static inline bool asn1_is_set(const struct asn1_hdr *hdr) +{ + return hdr->class == ASN1_CLASS_UNIVERSAL && + hdr->tag == ASN1_TAG_SET; +} + +static inline bool asn1_is_octetstring(const struct asn1_hdr *hdr) +{ + return hdr->class == ASN1_CLASS_UNIVERSAL && + hdr->tag == ASN1_TAG_OCTETSTRING; +} + +static inline bool asn1_is_bitstring(const struct asn1_hdr *hdr) +{ + return hdr->class == ASN1_CLASS_UNIVERSAL && + hdr->tag == ASN1_TAG_BITSTRING; +} + +static inline bool asn1_is_utctime(const struct asn1_hdr *hdr) +{ + return hdr->class == ASN1_CLASS_UNIVERSAL && + hdr->tag == ASN1_TAG_UTCTIME; +} + +static inline bool asn1_is_generalizedtime(const struct asn1_hdr *hdr) +{ + return hdr->class == ASN1_CLASS_UNIVERSAL && + hdr->tag == ASN1_TAG_GENERALIZEDTIME; +} + +static inline bool asn1_is_string_type(const struct asn1_hdr *hdr) +{ + if (hdr->class != ASN1_CLASS_UNIVERSAL || hdr->constructed) + return false; + return hdr->tag == ASN1_TAG_UTF8STRING || + hdr->tag == ASN1_TAG_NUMERICSTRING || + hdr->tag == ASN1_TAG_PRINTABLESTRING || + hdr->tag == ASN1_TAG_T61STRING || + hdr->tag == ASN1_TAG_VIDEOTEXSTRING || + hdr->tag == ASN1_TAG_IA5STRING || + hdr->tag == ASN1_TAG_GRAPHICSTRING || + hdr->tag == ASN1_TAG_VISIBLESTRING || + hdr->tag == ASN1_TAG_GENERALSTRING || + hdr->tag == ASN1_TAG_UNIVERSALSTRING || + hdr->tag == ASN1_TAG_CHARACTERSTRING || + hdr->tag == ASN1_TAG_BMPSTRING; +} + +static inline bool asn1_is_bmpstring(const struct asn1_hdr *hdr) +{ + return hdr->class == ASN1_CLASS_UNIVERSAL && + hdr->tag == ASN1_TAG_BMPSTRING; +} + +static inline bool asn1_is_utf8string(const struct asn1_hdr *hdr) +{ + return hdr->class == ASN1_CLASS_UNIVERSAL && + hdr->tag == ASN1_TAG_UTF8STRING; +} + +static inline bool asn1_is_null(const struct asn1_hdr *hdr) +{ + return hdr->class == ASN1_CLASS_UNIVERSAL && + hdr->tag == ASN1_TAG_NULL; +} + +static inline bool asn1_is_cs_tag(const struct asn1_hdr *hdr, unsigned int tag) +{ + return hdr->class == ASN1_CLASS_CONTEXT_SPECIFIC && + hdr->tag == tag; +} + +extern const struct asn1_oid asn1_sha1_oid; +extern const struct asn1_oid asn1_sha256_oid; +extern const struct asn1_oid asn1_ec_public_key_oid; +extern const struct asn1_oid asn1_prime256v1_oid; +extern const struct asn1_oid asn1_secp384r1_oid; +extern const struct asn1_oid asn1_secp521r1_oid; +extern const struct asn1_oid asn1_brainpoolP256r1_oid; +extern const struct asn1_oid asn1_brainpoolP384r1_oid; +extern const struct asn1_oid asn1_brainpoolP512r1_oid; +extern const struct asn1_oid asn1_aes_siv_cmac_aead_256_oid; +extern const struct asn1_oid asn1_aes_siv_cmac_aead_384_oid; +extern const struct asn1_oid asn1_aes_siv_cmac_aead_512_oid; +extern const struct asn1_oid asn1_aes_siv_cmac_aead_256_oid; +extern const struct asn1_oid asn1_aes_siv_cmac_aead_384_oid; +extern const struct asn1_oid asn1_aes_siv_cmac_aead_512_oid; +extern const struct asn1_oid asn1_pbkdf2_oid; +extern const struct asn1_oid asn1_pbkdf2_hmac_sha256_oid; +extern const struct asn1_oid asn1_pbkdf2_hmac_sha384_oid; +extern const struct asn1_oid asn1_pbkdf2_hmac_sha512_oid; +extern const struct asn1_oid asn1_dpp_config_params_oid; +extern const struct asn1_oid asn1_dpp_asymmetric_key_package_oid; #endif /* ASN1_H */ diff --git a/components/wpa_supplicant/src/tls/bignum.h b/components/wpa_supplicant/src/tls/bignum.h index f25e26783a..24acdce597 100644 --- a/components/wpa_supplicant/src/tls/bignum.h +++ b/components/wpa_supplicant/src/tls/bignum.h @@ -2,14 +2,8 @@ * Big number math * Copyright (c) 2006, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #ifndef BIGNUM_H diff --git a/components/wpa_supplicant/src/tls/pkcs1.c b/components/wpa_supplicant/src/tls/pkcs1.c index 51f5f0312d..49e439d027 100644 --- a/components/wpa_supplicant/src/tls/pkcs1.c +++ b/components/wpa_supplicant/src/tls/pkcs1.c @@ -1,16 +1,18 @@ /* * PKCS #1 (RSA Encryption) - * Copyright (c) 2006-2009, Jouni Malinen + * Copyright (c) 2006-2014, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ -#include "utils/includes.h" +#include "includes.h" -#include "utils/common.h" -#include "tls/rsa.h" -#include "tls/pkcs1.h" +#include "common.h" +#include "crypto/crypto.h" +#include "rsa.h" +#include "asn1.h" +#include "pkcs1.h" static int pkcs1_generate_encryption_block(u8 block_type, size_t modlen, @@ -113,6 +115,11 @@ int pkcs1_v15_private_key_decrypt(struct crypto_rsa_key *key, pos++; if (pos == end) return -1; + if (pos - out - 2 < 8) { + /* PKCS #1 v1.5, 8.1: At least eight octets long PS */ + wpa_printf(MSG_INFO, "LibTomCrypt: Too short padding"); + return -1; + } pos++; *outlen -= pos - out; @@ -142,46 +149,41 @@ int pkcs1_decrypt_public_key(struct crypto_rsa_key *key, * BT = 00 or 01 * PS = k-3-||D|| times (00 if BT=00) or (FF if BT=01) * k = length of modulus in octets + * + * Based on 10.1.3, "The block type shall be 01" for a signature. */ if (len < 3 + 8 + 16 /* min hash len */ || - plain[0] != 0x00 || (plain[1] != 0x00 && plain[1] != 0x01)) { + plain[0] != 0x00 || plain[1] != 0x01) { wpa_printf(MSG_INFO, "LibTomCrypt: Invalid signature EB " "structure"); + wpa_hexdump_key(MSG_DEBUG, "Signature EB", plain, len); return -1; } pos = plain + 3; - if (plain[1] == 0x00) { - /* BT = 00 */ - if (plain[2] != 0x00) { - wpa_printf(MSG_INFO, "LibTomCrypt: Invalid signature " - "PS (BT=00)"); - return -1; - } - while (pos + 1 < plain + len && *pos == 0x00 && pos[1] == 0x00) - pos++; - } else { - /* BT = 01 */ - if (plain[2] != 0xff) { - wpa_printf(MSG_INFO, "LibTomCrypt: Invalid signature " - "PS (BT=01)"); - return -1; - } - while (pos < plain + len && *pos == 0xff) - pos++; + /* BT = 01 */ + if (plain[2] != 0xff) { + wpa_printf(MSG_INFO, "LibTomCrypt: Invalid signature " + "PS (BT=01)"); + wpa_hexdump_key(MSG_DEBUG, "Signature EB", plain, len); + return -1; } + while (pos < plain + len && *pos == 0xff) + pos++; if (pos - plain - 2 < 8) { /* PKCS #1 v1.5, 8.1: At least eight octets long PS */ wpa_printf(MSG_INFO, "LibTomCrypt: Too short signature " "padding"); + wpa_hexdump_key(MSG_DEBUG, "Signature EB", plain, len); return -1; } if (pos + 16 /* min hash len */ >= plain + len || *pos != 0x00) { wpa_printf(MSG_INFO, "LibTomCrypt: Invalid signature EB " "structure (2)"); + wpa_hexdump_key(MSG_DEBUG, "Signature EB", plain, len); return -1; } pos++; @@ -193,3 +195,145 @@ int pkcs1_decrypt_public_key(struct crypto_rsa_key *key, return 0; } + + +int pkcs1_v15_sig_ver(struct crypto_public_key *pk, + const u8 *s, size_t s_len, + const struct asn1_oid *hash_alg, + const u8 *hash, size_t hash_len) +{ + int res; + u8 *decrypted; + size_t decrypted_len; + const u8 *pos, *end, *next, *da_end; + struct asn1_hdr hdr; + struct asn1_oid oid; + + decrypted = os_malloc(s_len); + if (decrypted == NULL) + return -1; + decrypted_len = s_len; + res = crypto_public_key_decrypt_pkcs1(pk, s, s_len, decrypted, + &decrypted_len); + if (res < 0) { + wpa_printf(MSG_INFO, "PKCS #1: RSA decrypt failed"); + os_free(decrypted); + return -1; + } + wpa_hexdump(MSG_DEBUG, "Decrypted(S)", decrypted, decrypted_len); + + /* + * PKCS #1 v1.5, 10.1.2: + * + * DigestInfo ::= SEQUENCE { + * digestAlgorithm DigestAlgorithmIdentifier, + * digest Digest + * } + * + * DigestAlgorithmIdentifier ::= AlgorithmIdentifier + * + * Digest ::= OCTET STRING + * + */ + if (asn1_get_next(decrypted, decrypted_len, &hdr) < 0 || + !asn1_is_sequence(&hdr)) { + asn1_unexpected(&hdr, + "PKCS #1: Expected SEQUENCE (DigestInfo)"); + os_free(decrypted); + return -1; + } + wpa_hexdump(MSG_MSGDUMP, "PKCS #1: DigestInfo", + hdr.payload, hdr.length); + + pos = hdr.payload; + end = pos + hdr.length; + + /* + * X.509: + * AlgorithmIdentifier ::= SEQUENCE { + * algorithm OBJECT IDENTIFIER, + * parameters ANY DEFINED BY algorithm OPTIONAL + * } + */ + + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + !asn1_is_sequence(&hdr)) { + asn1_unexpected(&hdr, + "PKCS #1: Expected SEQUENCE (AlgorithmIdentifier)"); + os_free(decrypted); + return -1; + } + wpa_hexdump(MSG_MSGDUMP, "PKCS #1: DigestAlgorithmIdentifier", + hdr.payload, hdr.length); + da_end = hdr.payload + hdr.length; + + if (asn1_get_oid(hdr.payload, hdr.length, &oid, &next)) { + wpa_printf(MSG_DEBUG, + "PKCS #1: Failed to parse digestAlgorithm"); + os_free(decrypted); + return -1; + } + wpa_hexdump(MSG_MSGDUMP, "PKCS #1: Digest algorithm parameters", + next, da_end - next); + + /* + * RFC 5754: The correct encoding for the SHA2 algorithms would be to + * omit the parameters, but there are implementation that encode these + * as a NULL element. Allow these two cases and reject anything else. + */ + if (da_end > next && + (asn1_get_next(next, da_end - next, &hdr) < 0 || + !asn1_is_null(&hdr) || + hdr.payload + hdr.length != da_end)) { + wpa_printf(MSG_DEBUG, + "PKCS #1: Unexpected digest algorithm parameters"); + os_free(decrypted); + return -1; + } + + if (!asn1_oid_equal(&oid, hash_alg)) { + char txt[100], txt2[100]; + asn1_oid_to_str(&oid, txt, sizeof(txt)); + asn1_oid_to_str(hash_alg, txt2, sizeof(txt2)); + wpa_printf(MSG_DEBUG, + "PKCS #1: Hash alg OID mismatch: was %s, expected %s", + txt, txt2); + os_free(decrypted); + return -1; + } + + /* Digest ::= OCTET STRING */ + pos = da_end; + + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + !asn1_is_octetstring(&hdr)) { + asn1_unexpected(&hdr, + "PKCS #1: Expected OCTETSTRING (Digest)"); + os_free(decrypted); + return -1; + } + wpa_hexdump(MSG_MSGDUMP, "PKCS #1: Decrypted Digest", + hdr.payload, hdr.length); + + if (hdr.length != hash_len || + os_memcmp_const(hdr.payload, hash, hdr.length) != 0) { + wpa_printf(MSG_INFO, "PKCS #1: Digest value does not match calculated hash"); + os_free(decrypted); + return -1; + } + + os_free(decrypted); + + if (hdr.payload + hdr.length != decrypted + decrypted_len) { + wpa_printf(MSG_INFO, + "PKCS #1: Extra data after signature - reject"); + + wpa_hexdump(MSG_DEBUG, "PKCS #1: Extra data", + hdr.payload + hdr.length, + decrypted + decrypted_len - hdr.payload - + hdr.length); + return -1; + } + + return 0; +} diff --git a/components/wpa_supplicant/src/tls/pkcs1.h b/components/wpa_supplicant/src/tls/pkcs1.h index ed64defaaf..f37ebf3875 100644 --- a/components/wpa_supplicant/src/tls/pkcs1.h +++ b/components/wpa_supplicant/src/tls/pkcs1.h @@ -9,6 +9,9 @@ #ifndef PKCS1_H #define PKCS1_H +struct crypto_public_key; +struct asn1_oid; + int pkcs1_encrypt(int block_type, struct crypto_rsa_key *key, int use_private, const u8 *in, size_t inlen, u8 *out, size_t *outlen); @@ -18,5 +21,9 @@ int pkcs1_v15_private_key_decrypt(struct crypto_rsa_key *key, int pkcs1_decrypt_public_key(struct crypto_rsa_key *key, const u8 *crypt, size_t crypt_len, u8 *plain, size_t *plain_len); +int pkcs1_v15_sig_ver(struct crypto_public_key *pk, + const u8 *s, size_t s_len, + const struct asn1_oid *hash_alg, + const u8 *hash, size_t hash_len); #endif /* PKCS1_H */ diff --git a/components/wpa_supplicant/src/tls/pkcs5.c b/components/wpa_supplicant/src/tls/pkcs5.c index aa600a026b..7bef89b4fd 100644 --- a/components/wpa_supplicant/src/tls/pkcs5.c +++ b/components/wpa_supplicant/src/tls/pkcs5.c @@ -1,48 +1,263 @@ /* * PKCS #5 (Password-based Encryption) - * Copyright (c) 2009, Jouni Malinen + * Copyright (c) 2009-2015, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ -#include "utils/includes.h" +#include "includes.h" -#include "utils/common.h" +#include "common.h" #include "crypto/crypto.h" #include "crypto/md5.h" -#include "tls/asn1.h" -#include "tls/pkcs5.h" +#include "crypto/sha1.h" +#include "asn1.h" +#include "pkcs5.h" -#include "eap_peer/eap_i.h" struct pkcs5_params { enum pkcs5_alg { PKCS5_ALG_UNKNOWN, - PKCS5_ALG_MD5_DES_CBC + PKCS5_ALG_MD5_DES_CBC, + PKCS5_ALG_PBES2, + PKCS5_ALG_SHA1_3DES_CBC, } alg; - u8 salt[8]; + u8 salt[64]; size_t salt_len; unsigned int iter_count; + enum pbes2_enc_alg { + PBES2_ENC_ALG_UNKNOWN, + PBES2_ENC_ALG_DES_EDE3_CBC, + } enc_alg; + u8 iv[8]; + size_t iv_len; }; +static int oid_is_rsadsi(struct asn1_oid *oid) +{ + return oid->len >= 4 && + oid->oid[0] == 1 /* iso */ && + oid->oid[1] == 2 /* member-body */ && + oid->oid[2] == 840 /* us */ && + oid->oid[3] == 113549 /* rsadsi */; +} + + +static int pkcs5_is_oid(struct asn1_oid *oid, unsigned long alg) +{ + return oid->len == 7 && + oid_is_rsadsi(oid) && + oid->oid[4] == 1 /* pkcs */ && + oid->oid[5] == 5 /* pkcs-5 */ && + oid->oid[6] == alg; +} + + +static int enc_alg_is_oid(struct asn1_oid *oid, unsigned long alg) +{ + return oid->len == 6 && + oid_is_rsadsi(oid) && + oid->oid[4] == 3 /* encryptionAlgorithm */ && + oid->oid[5] == alg; +} + + +static int pkcs12_is_pbe_oid(struct asn1_oid *oid, unsigned long alg) +{ + return oid->len == 8 && + oid_is_rsadsi(oid) && + oid->oid[4] == 1 /* pkcs */ && + oid->oid[5] == 12 /* pkcs-12 */ && + oid->oid[6] == 1 /* pkcs-12PbeIds */ && + oid->oid[7] == alg; +} + + static enum pkcs5_alg pkcs5_get_alg(struct asn1_oid *oid) { - if (oid->len == 7 && - oid->oid[0] == 1 /* iso */ && - oid->oid[1] == 2 /* member-body */ && - oid->oid[2] == 840 /* us */ && - oid->oid[3] == 113549 /* rsadsi */ && - oid->oid[4] == 1 /* pkcs */ && - oid->oid[5] == 5 /* pkcs-5 */ && - oid->oid[6] == 3 /* pbeWithMD5AndDES-CBC */) + if (pkcs5_is_oid(oid, 3)) /* pbeWithMD5AndDES-CBC (PBES1) */ return PKCS5_ALG_MD5_DES_CBC; - + if (pkcs12_is_pbe_oid(oid, 3)) /* pbeWithSHAAnd3-KeyTripleDES-CBC */ + return PKCS5_ALG_SHA1_3DES_CBC; + if (pkcs5_is_oid(oid, 13)) /* id-PBES2 (PBES2) */ + return PKCS5_ALG_PBES2; return PKCS5_ALG_UNKNOWN; } +static int pkcs5_get_params_pbes2(struct pkcs5_params *params, const u8 *pos, + const u8 *enc_alg_end) +{ + struct asn1_hdr hdr; + const u8 *end, *kdf_end; + struct asn1_oid oid; + char obuf[80]; + + /* + * RFC 2898, Ch. A.4 + * + * PBES2-params ::= SEQUENCE { + * keyDerivationFunc AlgorithmIdentifier {{PBES2-KDFs}}, + * encryptionScheme AlgorithmIdentifier {{PBES2-Encs}} } + * + * PBES2-KDFs ALGORITHM-IDENTIFIER ::= + * { {PBKDF2-params IDENTIFIED BY id-PBKDF2}, ... } + */ + + if (asn1_get_next(pos, enc_alg_end - pos, &hdr) < 0 || + !asn1_is_sequence(&hdr)) { + asn1_unexpected(&hdr, + "PKCS #5: Expected SEQUENCE (PBES2-params)"); + return -1; + } + pos = hdr.payload; + end = hdr.payload + hdr.length; + + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + !asn1_is_sequence(&hdr)) { + asn1_unexpected(&hdr, + "PKCS #5: Expected SEQUENCE (keyDerivationFunc)"); + return -1; + } + + pos = hdr.payload; + kdf_end = end = hdr.payload + hdr.length; + + if (asn1_get_oid(pos, end - pos, &oid, &pos)) { + wpa_printf(MSG_DEBUG, + "PKCS #5: Failed to parse OID (keyDerivationFunc algorithm)"); + return -1; + } + + asn1_oid_to_str(&oid, obuf, sizeof(obuf)); + wpa_printf(MSG_DEBUG, "PKCS #5: PBES2 keyDerivationFunc algorithm %s", + obuf); + if (!pkcs5_is_oid(&oid, 12)) /* id-PBKDF2 */ { + wpa_printf(MSG_DEBUG, + "PKCS #5: Unsupported PBES2 keyDerivationFunc algorithm %s", + obuf); + return -1; + } + + /* + * RFC 2898, C. + * + * PBKDF2-params ::= SEQUENCE { + * salt CHOICE { + * specified OCTET STRING, + * otherSource AlgorithmIdentifier {{PBKDF2-SaltSources}} + * }, + * iterationCount INTEGER (1..MAX), + * keyLength INTEGER (1..MAX) OPTIONAL, + * prf AlgorithmIdentifier {{PBKDF2-PRFs}} DEFAULT + * algid-hmacWithSHA1 + * } + */ + + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + !asn1_is_sequence(&hdr)) { + asn1_unexpected(&hdr, + "PKCS #5: Expected SEQUENCE (PBKDF2-params)"); + return -1; + } + + pos = hdr.payload; + end = hdr.payload + hdr.length; + + /* For now, only support the salt CHOICE specified (OCTET STRING) */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + !asn1_is_octetstring(&hdr) || + hdr.length > sizeof(params->salt)) { + asn1_unexpected(&hdr, + "PKCS #5: Expected OCTET STRING (salt.specified)"); + return -1; + } + pos = hdr.payload + hdr.length; + os_memcpy(params->salt, hdr.payload, hdr.length); + params->salt_len = hdr.length; + wpa_hexdump(MSG_DEBUG, "PKCS #5: salt", params->salt, params->salt_len); + + /* iterationCount INTEGER */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || !asn1_is_integer(&hdr)) { + asn1_unexpected(&hdr, "PKCS #5: Expected INTEGER"); + return -1; + } + if (hdr.length == 1) { + params->iter_count = *hdr.payload; + } else if (hdr.length == 2) { + params->iter_count = WPA_GET_BE16(hdr.payload); + } else if (hdr.length == 4) { + params->iter_count = WPA_GET_BE32(hdr.payload); + } else { + wpa_hexdump(MSG_DEBUG, + "PKCS #5: Unsupported INTEGER value (iterationCount)", + hdr.payload, hdr.length); + return -1; + } + wpa_printf(MSG_DEBUG, "PKCS #5: iterationCount=0x%x", + params->iter_count); + if (params->iter_count == 0 || params->iter_count > 0xffff) { + wpa_printf(MSG_INFO, "PKCS #5: Unsupported iterationCount=0x%x", + params->iter_count); + return -1; + } + + /* For now, ignore optional keyLength and prf */ + + pos = kdf_end; + + /* encryptionScheme AlgorithmIdentifier {{PBES2-Encs}} */ + + if (asn1_get_next(pos, enc_alg_end - pos, &hdr) < 0 || + !asn1_is_sequence(&hdr)) { + asn1_unexpected(&hdr, + "PKCS #5: Expected SEQUENCE (encryptionScheme)"); + return -1; + } + + pos = hdr.payload; + end = hdr.payload + hdr.length; + + if (asn1_get_oid(pos, end - pos, &oid, &pos)) { + wpa_printf(MSG_DEBUG, + "PKCS #5: Failed to parse OID (encryptionScheme algorithm)"); + return -1; + } + + asn1_oid_to_str(&oid, obuf, sizeof(obuf)); + wpa_printf(MSG_DEBUG, "PKCS #5: PBES2 encryptionScheme algorithm %s", + obuf); + if (enc_alg_is_oid(&oid, 7)) { + params->enc_alg = PBES2_ENC_ALG_DES_EDE3_CBC; + } else { + wpa_printf(MSG_DEBUG, + "PKCS #5: Unsupported PBES2 encryptionScheme algorithm %s", + obuf); + return -1; + } + + /* + * RFC 2898, B.2.2: + * The parameters field associated with this OID in an + * AlgorithmIdentifier shall have type OCTET STRING (SIZE(8)), + * specifying the initialization vector for CBC mode. + */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + !asn1_is_octetstring(&hdr) || hdr.length != 8) { + asn1_unexpected(&hdr, + "PKCS #5: Expected OCTET STRING (SIZE(8)) (IV)"); + return -1; + } + os_memcpy(params->iv, hdr.payload, hdr.length); + params->iv_len = hdr.length; + wpa_hexdump(MSG_DEBUG, "PKCS #5: IV", params->iv, params->iv_len); + + return 0; +} + + static int pkcs5_get_params(const u8 *enc_alg, size_t enc_alg_len, struct pkcs5_params *params) { @@ -72,32 +287,39 @@ static int pkcs5_get_params(const u8 *enc_alg, size_t enc_alg_len, return -1; } + if (params->alg == PKCS5_ALG_PBES2) + return pkcs5_get_params_pbes2(params, pos, enc_alg_end); + + /* PBES1 */ + /* * PKCS#5, Section 8 * PBEParameter ::= SEQUENCE { * salt OCTET STRING SIZE(8), * iterationCount INTEGER } + * + * Note: The same implementation can be used to parse the PKCS #12 + * version described in RFC 7292, C: + * pkcs-12PbeParams ::= SEQUENCE { + * salt OCTET STRING, + * iterations INTEGER + * } */ if (asn1_get_next(pos, enc_alg_end - pos, &hdr) < 0 || - hdr.class != ASN1_CLASS_UNIVERSAL || - hdr.tag != ASN1_TAG_SEQUENCE) { - wpa_printf(MSG_DEBUG, "PKCS #5: Expected SEQUENCE " - "(PBEParameter) - found class %d tag 0x%x", - hdr.class, hdr.tag); + !asn1_is_sequence(&hdr)) { + asn1_unexpected(&hdr, + "PKCS #5: Expected SEQUENCE (PBEParameter)"); return -1; } pos = hdr.payload; end = hdr.payload + hdr.length; - /* salt OCTET STRING SIZE(8) */ + /* salt OCTET STRING SIZE(8) (PKCS #5) or OCTET STRING (PKCS #12) */ if (asn1_get_next(pos, end - pos, &hdr) < 0 || - hdr.class != ASN1_CLASS_UNIVERSAL || - hdr.tag != ASN1_TAG_OCTETSTRING || - hdr.length != 8) { - wpa_printf(MSG_DEBUG, "PKCS #5: Expected OCTETSTRING SIZE(8) " - "(salt) - found class %d tag 0x%x size %d", - hdr.class, hdr.tag, hdr.length); + !asn1_is_octetstring(&hdr) || hdr.length > sizeof(params->salt)) { + asn1_unexpected(&hdr, + "PKCS #5: Expected OCTETSTRING SIZE(8) (salt)"); return -1; } pos = hdr.payload + hdr.length; @@ -108,9 +330,8 @@ static int pkcs5_get_params(const u8 *enc_alg, size_t enc_alg_len, /* iterationCount INTEGER */ if (asn1_get_next(pos, end - pos, &hdr) < 0 || - hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_INTEGER) { - wpa_printf(MSG_DEBUG, "PKCS #5: Expected INTEGER - found " - "class %d tag 0x%x", hdr.class, hdr.tag); + !asn1_is_integer(&hdr)) { + asn1_unexpected(&hdr, "PKCS #5: Expected INTEGER"); return -1; } if (hdr.length == 1) @@ -137,6 +358,174 @@ static int pkcs5_get_params(const u8 *enc_alg, size_t enc_alg_len, } +static struct crypto_cipher * +pkcs5_crypto_init_pbes2(struct pkcs5_params *params, const char *passwd) +{ + u8 key[24]; + + if (params->enc_alg != PBES2_ENC_ALG_DES_EDE3_CBC || + params->iv_len != 8) + return NULL; + + wpa_hexdump_ascii_key(MSG_DEBUG, "PKCS #5: PBES2 password for PBKDF2", + passwd, os_strlen(passwd)); + wpa_hexdump(MSG_DEBUG, "PKCS #5: PBES2 salt for PBKDF2", + params->salt, params->salt_len); + wpa_printf(MSG_DEBUG, "PKCS #5: PBES2 PBKDF2 iterations: %u", + params->iter_count); + if (pbkdf2_sha1(passwd, params->salt, params->salt_len, + params->iter_count, key, sizeof(key)) < 0) + return NULL; + wpa_hexdump_key(MSG_DEBUG, "PKCS #5: DES EDE3 key", key, sizeof(key)); + wpa_hexdump(MSG_DEBUG, "PKCS #5: DES IV", params->iv, params->iv_len); + + return crypto_cipher_init(CRYPTO_CIPHER_ALG_3DES, params->iv, + key, sizeof(key)); +} + + +static void add_byte_array_mod(u8 *a, const u8 *b, size_t len) +{ + size_t i; + unsigned int carry = 0; + + for (i = len - 1; i < len; i--) { + carry = carry + a[i] + b[i]; + a[i] = carry & 0xff; + carry >>= 8; + } +} + + +static int pkcs12_key_gen(const u8 *pw, size_t pw_len, const u8 *salt, + size_t salt_len, u8 id, unsigned int iter, + size_t out_len, u8 *out) +{ + unsigned int u, v, S_len, P_len, i; + u8 *D = NULL, *I = NULL, *B = NULL, *pos; + int res = -1; + + /* RFC 7292, B.2 */ + u = SHA1_MAC_LEN; + v = 64; + + /* D = copies of ID */ + D = os_malloc(v); + if (!D) + goto done; + os_memset(D, id, v); + + /* S = copies of salt; P = copies of password, I = S || P */ + S_len = v * ((salt_len + v - 1) / v); + P_len = v * ((pw_len + v - 1) / v); + I = os_malloc(S_len + P_len); + if (!I) + goto done; + pos = I; + if (salt_len) { + for (i = 0; i < S_len; i++) + *pos++ = salt[i % salt_len]; + } + if (pw_len) { + for (i = 0; i < P_len; i++) + *pos++ = pw[i % pw_len]; + } + + B = os_malloc(v); + if (!B) + goto done; + + for (;;) { + u8 hash[SHA1_MAC_LEN]; + const u8 *addr[2]; + size_t len[2]; + + addr[0] = D; + len[0] = v; + addr[1] = I; + len[1] = S_len + P_len; + if (sha1_vector(2, addr, len, hash) < 0) + goto done; + + addr[0] = hash; + len[0] = SHA1_MAC_LEN; + for (i = 1; i < iter; i++) { + if (sha1_vector(1, addr, len, hash) < 0) + goto done; + } + + if (out_len <= u) { + os_memcpy(out, hash, out_len); + res = 0; + goto done; + } + + os_memcpy(out, hash, u); + out += u; + out_len -= u; + + /* I_j = (I_j + B + 1) mod 2^(v*8) */ + /* B = copies of Ai (final hash value) */ + for (i = 0; i < v; i++) + B[i] = hash[i % u]; + inc_byte_array(B, v); + for (i = 0; i < S_len + P_len; i += v) + add_byte_array_mod(&I[i], B, v); + } + +done: + os_free(B); + os_free(I); + os_free(D); + return res; +} + + +#define PKCS12_ID_ENC 1 +#define PKCS12_ID_IV 2 +#define PKCS12_ID_MAC 3 + +static struct crypto_cipher * +pkcs12_crypto_init_sha1(struct pkcs5_params *params, const char *passwd) +{ + unsigned int i; + u8 *pw; + size_t pw_len; + u8 key[24]; + u8 iv[8]; + + if (params->alg != PKCS5_ALG_SHA1_3DES_CBC) + return NULL; + + pw_len = passwd ? os_strlen(passwd) : 0; + pw = os_malloc(2 * (pw_len + 1)); + if (!pw) + return NULL; + if (pw_len) { + for (i = 0; i <= pw_len; i++) + WPA_PUT_BE16(&pw[2 * i], passwd[i]); + pw_len = 2 * (pw_len + 1); + } + + if (pkcs12_key_gen(pw, pw_len, params->salt, params->salt_len, + PKCS12_ID_ENC, params->iter_count, + sizeof(key), key) < 0 || + pkcs12_key_gen(pw, pw_len, params->salt, params->salt_len, + PKCS12_ID_IV, params->iter_count, + sizeof(iv), iv) < 0) { + os_free(pw); + return NULL; + } + + os_free(pw); + + wpa_hexdump_key(MSG_DEBUG, "PKCS #12: DES key", key, sizeof(key)); + wpa_hexdump_key(MSG_DEBUG, "PKCS #12: DES IV", iv, sizeof(iv)); + + return crypto_cipher_init(CRYPTO_CIPHER_ALG_3DES, iv, key, sizeof(key)); +} + + static struct crypto_cipher * pkcs5_crypto_init(struct pkcs5_params *params, const char *passwd) { @@ -145,9 +534,14 @@ static struct crypto_cipher * pkcs5_crypto_init(struct pkcs5_params *params, const u8 *addr[2]; size_t len[2]; - if (params->alg != PKCS5_ALG_MD5_DES_CBC) { + if (params->alg == PKCS5_ALG_PBES2) + return pkcs5_crypto_init_pbes2(params, passwd); + + if (params->alg == PKCS5_ALG_SHA1_3DES_CBC) + return pkcs12_crypto_init_sha1(params, passwd); + + if (params->alg != PKCS5_ALG_MD5_DES_CBC) return NULL; - } addr[0] = (const u8 *) passwd; len[0] = os_strlen(passwd); @@ -166,14 +560,14 @@ static struct crypto_cipher * pkcs5_crypto_init(struct pkcs5_params *params, wpa_hexdump_key(MSG_DEBUG, "PKCS #5: DES IV", hash + 8, 8); return crypto_cipher_init(CRYPTO_CIPHER_ALG_DES, hash + 8, hash, 8); - } + u8 * pkcs5_decrypt(const u8 *enc_alg, size_t enc_alg_len, const u8 *enc_data, size_t enc_data_len, const char *passwd, size_t *data_len) { - struct crypto_cipher *ctx = NULL; + struct crypto_cipher *ctx; u8 *eb, pad; struct pkcs5_params params; unsigned int i; @@ -184,7 +578,6 @@ u8 * pkcs5_decrypt(const u8 *enc_alg, size_t enc_alg_len, } ctx = pkcs5_crypto_init(¶ms, passwd); - if (ctx == NULL) { wpa_printf(MSG_DEBUG, "PKCS #5: Failed to initialize crypto"); return NULL; @@ -204,13 +597,12 @@ u8 * pkcs5_decrypt(const u8 *enc_alg, size_t enc_alg_len, return NULL; } - if ((int)crypto_cipher_decrypt(ctx, enc_data, eb, enc_data_len) < 0) { + if (crypto_cipher_decrypt(ctx, enc_data, eb, enc_data_len) < 0) { wpa_printf(MSG_DEBUG, "PKCS #5: Failed to decrypt EB"); crypto_cipher_deinit(ctx); os_free(eb); return NULL; } - crypto_cipher_deinit(ctx); pad = eb[enc_data_len - 1]; diff --git a/components/wpa_supplicant/src/tls/pkcs8.c b/components/wpa_supplicant/src/tls/pkcs8.c index 6e012437b1..75bbd120c0 100644 --- a/components/wpa_supplicant/src/tls/pkcs8.c +++ b/components/wpa_supplicant/src/tls/pkcs8.c @@ -6,14 +6,15 @@ * See README for more details. */ -#include "utils/includes.h" +#include "includes.h" + +#include "common.h" +#include "asn1.h" +#include "bignum.h" +#include "rsa.h" +#include "pkcs5.h" +#include "pkcs8.h" -#include "utils/common.h" -#include "tls/asn1.h" -#include "tls/bignum.h" -#include "tls/rsa.h" -#include "tls/pkcs5.h" -#include "tls/pkcs8.h" struct crypto_private_key * pkcs8_key_import(const u8 *buf, size_t len) { @@ -26,22 +27,17 @@ struct crypto_private_key * pkcs8_key_import(const u8 *buf, size_t len) /* PKCS #8, Chapter 6 */ /* PrivateKeyInfo ::= SEQUENCE */ - if (asn1_get_next(buf, len, &hdr) < 0 || - hdr.class != ASN1_CLASS_UNIVERSAL || - hdr.tag != ASN1_TAG_SEQUENCE) { - wpa_printf(MSG_DEBUG, "PKCS #8: Does not start with PKCS #8 " - "header (SEQUENCE); assume PKCS #8 not used"); + if (asn1_get_next(buf, len, &hdr) < 0 || !asn1_is_sequence(&hdr)) { + asn1_unexpected(&hdr, + "PKCS #8: Does not start with PKCS #8 header (SEQUENCE)"); return NULL; } pos = hdr.payload; end = pos + hdr.length; /* version Version (Version ::= INTEGER) */ - if (asn1_get_next(pos, end - pos, &hdr) < 0 || - hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_INTEGER) { - wpa_printf(MSG_DEBUG, "PKCS #8: Expected INTEGER - found " - "class %d tag 0x%x; assume PKCS #8 not used", - hdr.class, hdr.tag); + if (asn1_get_next(pos, end - pos, &hdr) < 0 || !asn1_is_integer(&hdr)) { + asn1_unexpected(&hdr, "PKCS #8: Expected INTEGER"); return NULL; } @@ -67,13 +63,9 @@ struct crypto_private_key * pkcs8_key_import(const u8 *buf, size_t len) /* privateKeyAlgorithm PrivateKeyAlgorithmIdentifier * (PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier) */ - if (asn1_get_next(pos, len, &hdr) < 0 || - hdr.class != ASN1_CLASS_UNIVERSAL || - hdr.tag != ASN1_TAG_SEQUENCE) { - wpa_printf(MSG_DEBUG, "PKCS #8: Expected SEQUENCE " - "(AlgorithmIdentifier) - found class %d tag 0x%x; " - "assume PKCS #8 not used", - hdr.class, hdr.tag); + if (asn1_get_next(pos, len, &hdr) < 0 || !asn1_is_sequence(&hdr)) { + asn1_unexpected(&hdr, + "PKCS #8: Expected SEQUENCE (AlgorithmIdentifier); assume PKCS #8 not used"); return NULL; } @@ -103,11 +95,9 @@ struct crypto_private_key * pkcs8_key_import(const u8 *buf, size_t len) /* privateKey PrivateKey (PrivateKey ::= OCTET STRING) */ if (asn1_get_next(pos, end - pos, &hdr) < 0 || - hdr.class != ASN1_CLASS_UNIVERSAL || - hdr.tag != ASN1_TAG_OCTETSTRING) { - wpa_printf(MSG_DEBUG, "PKCS #8: Expected OCTETSTRING " - "(privateKey) - found class %d tag 0x%x", - hdr.class, hdr.tag); + !asn1_is_octetstring(&hdr)) { + asn1_unexpected(&hdr, + "PKCS #8: Expected OCTETSTRING (privateKey)"); return NULL; } wpa_printf(MSG_DEBUG, "PKCS #8: Try to parse RSAPrivateKey"); @@ -138,12 +128,9 @@ pkcs8_enc_key_import(const u8 *buf, size_t len, const char *passwd) * EncryptedData ::= OCTET STRING */ - if (asn1_get_next(buf, len, &hdr) < 0 || - hdr.class != ASN1_CLASS_UNIVERSAL || - hdr.tag != ASN1_TAG_SEQUENCE) { - wpa_printf(MSG_DEBUG, "PKCS #8: Does not start with PKCS #8 " - "header (SEQUENCE); assume encrypted PKCS #8 not " - "used"); + if (asn1_get_next(buf, len, &hdr) < 0 || !asn1_is_sequence(&hdr)) { + asn1_unexpected(&hdr, + "PKCS #8: Does not start with PKCS #8 header (SEQUENCE); assume encrypted PKCS #8 not used"); return NULL; } pos = hdr.payload; @@ -151,12 +138,9 @@ pkcs8_enc_key_import(const u8 *buf, size_t len, const char *passwd) /* encryptionAlgorithm EncryptionAlgorithmIdentifier */ if (asn1_get_next(pos, end - pos, &hdr) < 0 || - hdr.class != ASN1_CLASS_UNIVERSAL || - hdr.tag != ASN1_TAG_SEQUENCE) { - wpa_printf(MSG_DEBUG, "PKCS #8: Expected SEQUENCE " - "(AlgorithmIdentifier) - found class %d tag 0x%x; " - "assume encrypted PKCS #8 not used", - hdr.class, hdr.tag); + !asn1_is_sequence(&hdr)) { + asn1_unexpected(&hdr, + "PKCS #8: Expected SEQUENCE (AlgorithmIdentifier); assume encrypted PKCS #8 not used"); return NULL; } enc_alg = hdr.payload; @@ -165,11 +149,9 @@ pkcs8_enc_key_import(const u8 *buf, size_t len, const char *passwd) /* encryptedData EncryptedData */ if (asn1_get_next(pos, end - pos, &hdr) < 0 || - hdr.class != ASN1_CLASS_UNIVERSAL || - hdr.tag != ASN1_TAG_OCTETSTRING) { - wpa_printf(MSG_DEBUG, "PKCS #8: Expected OCTETSTRING " - "(encryptedData) - found class %d tag 0x%x", - hdr.class, hdr.tag); + !asn1_is_octetstring(&hdr)) { + asn1_unexpected(&hdr, + "PKCS #8: Expected OCTETSTRING (encryptedData)"); return NULL; } diff --git a/components/wpa_supplicant/src/tls/rsa.c b/components/wpa_supplicant/src/tls/rsa.c index 1b01f5843d..56ae7d7795 100644 --- a/components/wpa_supplicant/src/tls/rsa.c +++ b/components/wpa_supplicant/src/tls/rsa.c @@ -37,9 +37,8 @@ static const u8 * crypto_rsa_parse_integer(const u8 *pos, const u8 *end, return NULL; if (asn1_get_next(pos, end - pos, &hdr) < 0 || - hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_INTEGER) { - wpa_printf(MSG_DEBUG, "RSA: Expected INTEGER - found class %d " - "tag 0x%x", hdr.class, hdr.tag); + !asn1_is_integer(&hdr)) { + asn1_unexpected(&hdr, "RSA: Expected INTEGER"); return NULL; } @@ -84,12 +83,8 @@ crypto_rsa_import_public_key(const u8 *buf, size_t len) * } */ - if (asn1_get_next(buf, len, &hdr) < 0 || - hdr.class != ASN1_CLASS_UNIVERSAL || - hdr.tag != ASN1_TAG_SEQUENCE) { - wpa_printf(MSG_DEBUG, "RSA: Expected SEQUENCE " - "(public key) - found class %d tag 0x%x", - hdr.class, hdr.tag); + if (asn1_get_next(buf, len, &hdr) < 0 || !asn1_is_sequence(&hdr)) { + asn1_unexpected(&hdr, "RSA: Expected SEQUENCE (public key)"); goto error; } pos = hdr.payload; @@ -191,12 +186,8 @@ crypto_rsa_import_private_key(const u8 *buf, size_t len) * * Version ::= INTEGER -- shall be 0 for this version of the standard */ - if (asn1_get_next(buf, len, &hdr) < 0 || - hdr.class != ASN1_CLASS_UNIVERSAL || - hdr.tag != ASN1_TAG_SEQUENCE) { - wpa_printf(MSG_DEBUG, "RSA: Expected SEQUENCE " - "(public key) - found class %d tag 0x%x", - hdr.class, hdr.tag); + if (asn1_get_next(buf, len, &hdr) < 0 || !asn1_is_sequence(&hdr)) { + asn1_unexpected(&hdr, "RSA: Expected SEQUENCE (public key)"); goto error; } pos = hdr.payload; diff --git a/components/wpa_supplicant/src/tls/tlsv1_client.c b/components/wpa_supplicant/src/tls/tlsv1_client.c index 7a6048eece..486da16fd3 100644 --- a/components/wpa_supplicant/src/tls/tlsv1_client.c +++ b/components/wpa_supplicant/src/tls/tlsv1_client.c @@ -1,20 +1,21 @@ /* * TLS v1.0/v1.1/v1.2 client (RFC 2246, RFC 4346, RFC 5246) - * Copyright (c) 2006-2011, Jouni Malinen + * Copyright (c) 2006-2019, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ -#include "utils/includes.h" +#include "includes.h" -#include "utils/common.h" +#include "common.h" #include "crypto/sha1.h" -#include "tls/tls.h" -#include "tls/tlsv1_common.h" -#include "tls/tlsv1_record.h" -#include "tls/tlsv1_client.h" -#include "tls/tlsv1_client_i.h" +#include "crypto/tls.h" +#include "x509v3.h" +#include "tlsv1_common.h" +#include "tlsv1_record.h" +#include "tlsv1_client.h" +#include "tlsv1_client_i.h" /* TODO: * Support for a message fragmented across several records (RFC 2246, 6.2.1) @@ -37,9 +38,33 @@ void tlsv1_client_free_dh(struct tlsv1_client *conn) } -int tls_derive_pre_master_secret(u8 *pre_master_secret) +u16 tls_client_highest_ver(struct tlsv1_client *conn) { - WPA_PUT_BE16(pre_master_secret, TLS_VERSION); + u16 tls_version = TLS_VERSION; + + /* Pick the highest locally enabled TLS version */ +#ifdef CONFIG_TLSV12 + if ((conn->flags & TLS_CONN_DISABLE_TLSv1_2) && + tls_version == TLS_VERSION_1_2) + tls_version = TLS_VERSION_1_1; +#endif /* CONFIG_TLSV12 */ +#ifdef CONFIG_TLSV11 + if ((conn->flags & TLS_CONN_DISABLE_TLSv1_1) && + tls_version == TLS_VERSION_1_1) + tls_version = TLS_VERSION_1; +#endif /* CONFIG_TLSV11 */ + if ((conn->flags & TLS_CONN_DISABLE_TLSv1_0) && + tls_version == TLS_VERSION_1) + return 0; + + return tls_version; +} + + +int tls_derive_pre_master_secret(struct tlsv1_client *conn, + u8 *pre_master_secret) +{ + WPA_PUT_BE16(pre_master_secret, tls_client_highest_ver(conn)); if (os_get_random(pre_master_secret + 2, TLS_PRE_MASTER_SECRET_LEN - 2)) return -1; @@ -110,7 +135,6 @@ int tls_derive_keys(struct tlsv1_client *conn, pos += conn->rl.iv_size; /* server_write_IV */ os_memcpy(conn->rl.read_iv, pos, conn->rl.iv_size); - pos += conn->rl.iv_size; } else { /* * Use IV field to set the mask value for TLS v1.1. A fixed @@ -245,7 +269,7 @@ failed: conn->alert_description, out_len); } else if (msg == NULL) { - msg = (u8 *)os_zalloc(1); + msg = os_zalloc(1); *out_len = 0; } @@ -444,7 +468,7 @@ struct tlsv1_client * tlsv1_client_init(void) size_t count; u16 *suites; - conn = (struct tlsv1_client *)os_zalloc(sizeof(*conn)); + conn = os_zalloc(sizeof(*conn)); if (conn == NULL) return NULL; @@ -459,13 +483,16 @@ struct tlsv1_client * tlsv1_client_init(void) count = 0; suites = conn->cipher_suites; + suites[count++] = TLS_DHE_RSA_WITH_AES_256_CBC_SHA256; suites[count++] = TLS_RSA_WITH_AES_256_CBC_SHA256; + suites[count++] = TLS_DHE_RSA_WITH_AES_256_CBC_SHA; suites[count++] = TLS_RSA_WITH_AES_256_CBC_SHA; + suites[count++] = TLS_DHE_RSA_WITH_AES_128_CBC_SHA256; suites[count++] = TLS_RSA_WITH_AES_128_CBC_SHA256; + suites[count++] = TLS_DHE_RSA_WITH_AES_128_CBC_SHA; suites[count++] = TLS_RSA_WITH_AES_128_CBC_SHA; -#ifdef CONFIG_DES3 + suites[count++] = TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA; suites[count++] = TLS_RSA_WITH_3DES_EDE_CBC_SHA; -#endif //CONFIG_DES3 suites[count++] = TLS_RSA_WITH_RC4_128_SHA; suites[count++] = TLS_RSA_WITH_RC4_128_MD5; conn->num_cipher_suites = count; @@ -491,6 +518,7 @@ void tlsv1_client_deinit(struct tlsv1_client *conn) tlsv1_client_free_dh(conn); tlsv1_cred_free(conn->cred); wpabuf_free(conn->partial_input); + x509_certificate_chain_free(conn->server_cert); os_free(conn); } @@ -510,6 +538,8 @@ int tlsv1_client_established(struct tlsv1_client *conn) * tlsv1_client_prf - Use TLS-PRF to derive keying material * @conn: TLSv1 client connection data from tlsv1_client_init() * @label: Label (e.g., description of the key) for PRF + * @context: Optional extra upper-layer context (max len 2^16) + * @context_len: The length of the context value * @server_random_first: seed is 0 = client_random|server_random, * 1 = server_random|client_random * @out: Buffer for output data from TLS-PRF @@ -517,13 +547,26 @@ int tlsv1_client_established(struct tlsv1_client *conn) * Returns: 0 on success, -1 on failure */ int tlsv1_client_prf(struct tlsv1_client *conn, const char *label, + const u8 *context, size_t context_len, int server_random_first, u8 *out, size_t out_len) { - u8 seed[2 * TLS_RANDOM_LEN]; + u8 *seed, *pos; + size_t seed_len = 2 * TLS_RANDOM_LEN; + int res; if (conn->state != ESTABLISHED) return -1; + if (context_len > 65535) + return -1; + + if (context) + seed_len += 2 + context_len; + + seed = os_malloc(seed_len); + if (!seed) + return -1; + if (server_random_first) { os_memcpy(seed, conn->server_random, TLS_RANDOM_LEN); os_memcpy(seed + TLS_RANDOM_LEN, conn->client_random, @@ -534,9 +577,18 @@ int tlsv1_client_prf(struct tlsv1_client *conn, const char *label, TLS_RANDOM_LEN); } - return tls_prf(conn->rl.tls_version, - conn->master_secret, TLS_MASTER_SECRET_LEN, - label, seed, 2 * TLS_RANDOM_LEN, out, out_len); + if (context) { + pos = seed + 2 * TLS_RANDOM_LEN; + WPA_PUT_BE16(pos, context_len); + pos += 2; + os_memcpy(pos, context, context_len); + } + + res = tls_prf(conn->rl.tls_version, + conn->master_secret, TLS_MASTER_SECRET_LEN, + label, seed, seed_len, out, out_len); + os_free(seed); + return res; } @@ -552,7 +604,6 @@ int tlsv1_client_prf(struct tlsv1_client *conn, const char *label, int tlsv1_client_get_cipher(struct tlsv1_client *conn, char *buf, size_t buflen) { -#ifndef ESPRESSIF_USE char *cipher; switch (conn->rl.cipher_suite) { @@ -562,18 +613,32 @@ int tlsv1_client_get_cipher(struct tlsv1_client *conn, char *buf, case TLS_RSA_WITH_RC4_128_SHA: cipher = "RC4-SHA"; break; -#ifdef CONFIG_DES case TLS_RSA_WITH_DES_CBC_SHA: cipher = "DES-CBC-SHA"; break; -#endif -#ifdef CONFIG_DES3 case TLS_RSA_WITH_3DES_EDE_CBC_SHA: cipher = "DES-CBC3-SHA"; break; -#endif - case TLS_DH_anon_WITH_AES_128_CBC_SHA256: - cipher = "ADH-AES-128-SHA256"; + case TLS_DHE_RSA_WITH_DES_CBC_SHA: + cipher = "DHE-RSA-DES-CBC-SHA"; + break; + case TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA: + cipher = "DHE-RSA-DES-CBC3-SHA"; + break; + case TLS_DH_anon_WITH_RC4_128_MD5: + cipher = "ADH-RC4-MD5"; + break; + case TLS_DH_anon_WITH_DES_CBC_SHA: + cipher = "ADH-DES-SHA"; + break; + case TLS_DH_anon_WITH_3DES_EDE_CBC_SHA: + cipher = "ADH-DES-CBC3-SHA"; + break; + case TLS_RSA_WITH_AES_128_CBC_SHA: + cipher = "AES-128-SHA"; + break; + case TLS_DHE_RSA_WITH_AES_128_CBC_SHA: + cipher = "DHE-RSA-AES-128-SHA"; break; case TLS_DH_anon_WITH_AES_128_CBC_SHA: cipher = "ADH-AES-128-SHA"; @@ -581,68 +646,37 @@ int tlsv1_client_get_cipher(struct tlsv1_client *conn, char *buf, case TLS_RSA_WITH_AES_256_CBC_SHA: cipher = "AES-256-SHA"; break; - case TLS_RSA_WITH_AES_256_CBC_SHA256: - cipher = "AES-256-SHA256"; + case TLS_DHE_RSA_WITH_AES_256_CBC_SHA: + cipher = "DHE-RSA-AES-256-SHA"; break; - case TLS_RSA_WITH_AES_128_CBC_SHA: - cipher = "AES-128-SHA"; + case TLS_DH_anon_WITH_AES_256_CBC_SHA: + cipher = "ADH-AES-256-SHA"; break; case TLS_RSA_WITH_AES_128_CBC_SHA256: cipher = "AES-128-SHA256"; break; + case TLS_RSA_WITH_AES_256_CBC_SHA256: + cipher = "AES-256-SHA256"; + break; + case TLS_DHE_RSA_WITH_AES_128_CBC_SHA256: + cipher = "DHE-RSA-AES-128-SHA256"; + break; + case TLS_DHE_RSA_WITH_AES_256_CBC_SHA256: + cipher = "DHE-RSA-AES-256-SHA256"; + break; + case TLS_DH_anon_WITH_AES_128_CBC_SHA256: + cipher = "ADH-AES-128-SHA256"; + break; + case TLS_DH_anon_WITH_AES_256_CBC_SHA256: + cipher = "ADH-AES-256-SHA256"; + break; default: return -1; } - os_memcpy((u8 *)buf, (u8 *)cipher, buflen); - + if (os_strlcpy(buf, cipher, buflen) >= buflen) + return -1; return 0; -#else - char cipher[20]; - - switch (conn->rl.cipher_suite) { - case TLS_RSA_WITH_RC4_128_MD5: - strcpy(cipher, "RC4-MD5"); - break; - case TLS_RSA_WITH_RC4_128_SHA: - strcpy(cipher, "RC4-SHA"); - break; -#ifdef CONFIG_DES - case TLS_RSA_WITH_DES_CBC_SHA: - strcpy(cipher, "DES-CBC-SHA"); - break; -#endif -#ifdef CONFIG_DES3 - case TLS_RSA_WITH_3DES_EDE_CBC_SHA: - strcpy(cipher, "DES-CBC3-SHA"); - break; -#endif - case TLS_DH_anon_WITH_AES_128_CBC_SHA256: - strcpy(cipher, "ADH-AES-128-SHA256"); - break; - case TLS_DH_anon_WITH_AES_128_CBC_SHA: - strcpy(cipher, "ADH-AES-128-SHA"); - break; - case TLS_RSA_WITH_AES_256_CBC_SHA: - strcpy(cipher, "AES-256-SHA"); - break; - case TLS_RSA_WITH_AES_256_CBC_SHA256: - strcpy(cipher, "AES-256-SHA256"); - break; - case TLS_RSA_WITH_AES_128_CBC_SHA: - strcpy(cipher, "AES-128-SHA"); - break; - case TLS_RSA_WITH_AES_128_CBC_SHA256: - strcpy(cipher, "AES-128-SHA256"); - break; - default: - return -1; - } - - os_memcpy((u8 *)buf, (u8 *)cipher, buflen); - - return 0; -#endif } @@ -786,13 +820,9 @@ int tlsv1_client_set_cipher_list(struct tlsv1_client *conn, u8 *ciphers) suites[count++] = TLS_DH_anon_WITH_AES_256_CBC_SHA; suites[count++] = TLS_DH_anon_WITH_AES_128_CBC_SHA256; suites[count++] = TLS_DH_anon_WITH_AES_128_CBC_SHA; -#ifdef CONFIG_DES3 suites[count++] = TLS_DH_anon_WITH_3DES_EDE_CBC_SHA; -#endif suites[count++] = TLS_DH_anon_WITH_RC4_128_MD5; -#ifdef CONFIG_DES suites[count++] = TLS_DH_anon_WITH_DES_CBC_SHA; -#endif /* * Cisco AP (at least 350 and 1200 series) local authentication @@ -830,9 +860,15 @@ int tlsv1_client_set_cred(struct tlsv1_client *conn, } -void tlsv1_client_set_time_checks(struct tlsv1_client *conn, int enabled) +/** + * tlsv1_client_set_flags - Set connection flags + * @conn: TLSv1 client connection data from tlsv1_client_init() + * @flags: TLS_CONN_* bitfield + */ +void tlsv1_client_set_flags(struct tlsv1_client *conn, unsigned int flags) { - conn->disable_time_checks = !enabled; + conn->flags = flags; + conn->rl.tls_version = tls_client_highest_ver(conn); } @@ -845,3 +881,38 @@ void tlsv1_client_set_session_ticket_cb(struct tlsv1_client *conn, conn->session_ticket_cb = cb; conn->session_ticket_cb_ctx = ctx; } + + +void tlsv1_client_set_cb(struct tlsv1_client *conn, + void (*event_cb)(void *ctx, enum tls_event ev, + union tls_event_data *data), + void *cb_ctx, + int cert_in_cb) +{ + conn->event_cb = event_cb; + conn->cb_ctx = cb_ctx; + conn->cert_in_cb = !!cert_in_cb; +} + + +int tlsv1_client_get_version(struct tlsv1_client *conn, char *buf, + size_t buflen) +{ + if (!conn) + return -1; + switch (conn->rl.tls_version) { + case TLS_VERSION_1: + os_strlcpy(buf, "TLSv1", buflen); + break; + case TLS_VERSION_1_1: + os_strlcpy(buf, "TLSv1.1", buflen); + break; + case TLS_VERSION_1_2: + os_strlcpy(buf, "TLSv1.2", buflen); + break; + default: + return -1; + } + + return 0; +} diff --git a/components/wpa_supplicant/src/tls/tlsv1_client.h b/components/wpa_supplicant/src/tls/tlsv1_client.h index a4e25e9699..7fcc256f14 100644 --- a/components/wpa_supplicant/src/tls/tlsv1_client.h +++ b/components/wpa_supplicant/src/tls/tlsv1_client.h @@ -1,6 +1,6 @@ /* * TLS v1.0/v1.1/v1.2 client (RFC 2246, RFC 4346, RFC 5246) - * Copyright (c) 2006-2011, Jouni Malinen + * Copyright (c) 2006-2019, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -19,6 +19,7 @@ struct tlsv1_client * tlsv1_client_init(void); void tlsv1_client_deinit(struct tlsv1_client *conn); int tlsv1_client_established(struct tlsv1_client *conn); int tlsv1_client_prf(struct tlsv1_client *conn, const char *label, + const u8 *context, size_t context_len, int server_random_first, u8 *out, size_t out_len); u8 * tlsv1_client_handshake(struct tlsv1_client *conn, const u8 *in_data, size_t in_len, @@ -41,7 +42,7 @@ int tlsv1_client_get_keyblock_size(struct tlsv1_client *conn); int tlsv1_client_set_cipher_list(struct tlsv1_client *conn, u8 *ciphers); int tlsv1_client_set_cred(struct tlsv1_client *conn, struct tlsv1_credentials *cred); -void tlsv1_client_set_time_checks(struct tlsv1_client *conn, int enabled); +void tlsv1_client_set_flags(struct tlsv1_client *conn, unsigned int flags); typedef int (*tlsv1_client_session_ticket_cb) (void *ctx, const u8 *ticket, size_t len, const u8 *client_random, @@ -51,4 +52,12 @@ void tlsv1_client_set_session_ticket_cb(struct tlsv1_client *conn, tlsv1_client_session_ticket_cb cb, void *ctx); +void tlsv1_client_set_cb(struct tlsv1_client *conn, + void (*event_cb)(void *ctx, enum tls_event ev, + union tls_event_data *data), + void *cb_ctx, + int cert_in_cb); +int tlsv1_client_get_version(struct tlsv1_client *conn, char *buf, + size_t buflen); + #endif /* TLSV1_CLIENT_H */ diff --git a/components/wpa_supplicant/src/tls/tlsv1_client_i.h b/components/wpa_supplicant/src/tls/tlsv1_client_i.h index 55fdcf8d04..ccb2e15808 100644 --- a/components/wpa_supplicant/src/tls/tlsv1_client_i.h +++ b/components/wpa_supplicant/src/tls/tlsv1_client_i.h @@ -29,11 +29,14 @@ struct tlsv1_client { u8 alert_level; u8 alert_description; + unsigned int flags; /* TLS_CONN_* bitfield */ + unsigned int certificate_requested:1; unsigned int session_resumed:1; unsigned int session_ticket_included:1; unsigned int use_session_ticket:1; - unsigned int disable_time_checks:1; + unsigned int cert_in_cb:1; + unsigned int ocsp_resp_received:1; struct crypto_public_key *server_rsa_key; @@ -64,12 +67,20 @@ struct tlsv1_client { void *session_ticket_cb_ctx; struct wpabuf *partial_input; + + void (*event_cb)(void *ctx, enum tls_event ev, + union tls_event_data *data); + void *cb_ctx; + + struct x509_certificate *server_cert; }; void tls_alert(struct tlsv1_client *conn, u8 level, u8 description); void tlsv1_client_free_dh(struct tlsv1_client *conn); -int tls_derive_pre_master_secret(u8 *pre_master_secret); +u16 tls_client_highest_ver(struct tlsv1_client *conn); +int tls_derive_pre_master_secret(struct tlsv1_client *conn, + u8 *pre_master_secret); int tls_derive_keys(struct tlsv1_client *conn, const u8 *pre_master_secret, size_t pre_master_secret_len); u8 * tls_send_client_hello(struct tlsv1_client *conn, size_t *out_len); @@ -81,4 +92,11 @@ int tlsv1_client_process_handshake(struct tlsv1_client *conn, u8 ct, const u8 *buf, size_t *len, u8 **out_data, size_t *out_len); +enum tls_ocsp_result { + TLS_OCSP_NO_RESPONSE, TLS_OCSP_INVALID, TLS_OCSP_GOOD, TLS_OCSP_REVOKED +}; + +enum tls_ocsp_result tls_process_ocsp_response(struct tlsv1_client *conn, + const u8 *resp, size_t len); + #endif /* TLSV1_CLIENT_I_H */ diff --git a/components/wpa_supplicant/src/tls/tlsv1_client_ocsp.c b/components/wpa_supplicant/src/tls/tlsv1_client_ocsp.c new file mode 100644 index 0000000000..128f4b5b9e --- /dev/null +++ b/components/wpa_supplicant/src/tls/tlsv1_client_ocsp.c @@ -0,0 +1,759 @@ +/* + * TLSv1 client - OCSP + * Copyright (c) 2015, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "crypto/tls.h" +#include "crypto/sha1.h" +#include "asn1.h" +#include "x509v3.h" +#include "tlsv1_common.h" +#include "tlsv1_record.h" +#include "tlsv1_client.h" +#include "tlsv1_client_i.h" + + +/* RFC 6960, 4.2.1: OCSPResponseStatus ::= ENUMERATED */ +enum ocsp_response_status { + OCSP_RESP_STATUS_SUCCESSFUL = 0, + OCSP_RESP_STATUS_MALFORMED_REQ = 1, + OCSP_RESP_STATUS_INT_ERROR = 2, + OCSP_RESP_STATUS_TRY_LATER = 3, + /* 4 not used */ + OCSP_RESP_STATUS_SIG_REQUIRED = 5, + OCSP_RESP_STATUS_UNAUTHORIZED = 6, +}; + + +static int is_oid_basic_ocsp_resp(struct asn1_oid *oid) +{ + return oid->len == 10 && + oid->oid[0] == 1 /* iso */ && + oid->oid[1] == 3 /* identified-organization */ && + oid->oid[2] == 6 /* dod */ && + oid->oid[3] == 1 /* internet */ && + oid->oid[4] == 5 /* security */ && + oid->oid[5] == 5 /* mechanisms */ && + oid->oid[6] == 7 /* id-pkix */ && + oid->oid[7] == 48 /* id-ad */ && + oid->oid[8] == 1 /* id-pkix-ocsp */ && + oid->oid[9] == 1 /* id-pkix-ocsp-basic */; +} + + +static int ocsp_responder_id_match(struct x509_certificate *signer, + struct x509_name *name, const u8 *key_hash) +{ + if (key_hash) { + u8 hash[SHA1_MAC_LEN]; + const u8 *addr[1] = { signer->public_key }; + size_t len[1] = { signer->public_key_len }; + + if (sha1_vector(1, addr, len, hash) < 0) + return 0; + return os_memcmp(hash, key_hash, SHA1_MAC_LEN) == 0; + } + + return x509_name_compare(&signer->subject, name) == 0; +} + + +static unsigned int ocsp_hash_data(struct asn1_oid *alg, const u8 *data, + size_t data_len, u8 *hash) +{ + const u8 *addr[1] = { data }; + size_t len[1] = { data_len }; + char buf[100]; + + if (x509_sha1_oid(alg)) { + if (sha1_vector(1, addr, len, hash) < 0) + return 0; + wpa_hexdump(MSG_MSGDUMP, "OCSP: Hash (SHA1)", hash, 20); + return 20; + } + + if (x509_sha256_oid(alg)) { + if (sha256_vector(1, addr, len, hash) < 0) + return 0; + wpa_hexdump(MSG_MSGDUMP, "OCSP: Hash (SHA256)", hash, 32); + return 32; + } + + if (x509_sha384_oid(alg)) { + if (sha384_vector(1, addr, len, hash) < 0) + return 0; + wpa_hexdump(MSG_MSGDUMP, "OCSP: Hash (SHA384)", hash, 48); + return 48; + } + + if (x509_sha512_oid(alg)) { + if (sha512_vector(1, addr, len, hash) < 0) + return 0; + wpa_hexdump(MSG_MSGDUMP, "OCSP: Hash (SHA512)", hash, 64); + return 64; + } + + + asn1_oid_to_str(alg, buf, sizeof(buf)); + wpa_printf(MSG_DEBUG, "OCSP: Could not calculate hash with alg %s", + buf); + return 0; +} + + +static int tls_process_ocsp_single_response(struct tlsv1_client *conn, + struct x509_certificate *cert, + struct x509_certificate *issuer, + const u8 *resp, size_t len, + enum tls_ocsp_result *res) +{ + struct asn1_hdr hdr; + const u8 *pos, *end; + struct x509_algorithm_identifier alg; + const u8 *name_hash, *key_hash; + size_t name_hash_len, key_hash_len; + const u8 *serial_number; + size_t serial_number_len; + u8 hash[64]; + unsigned int hash_len; + unsigned int cert_status; + os_time_t update; + struct os_time now; + + wpa_hexdump(MSG_MSGDUMP, "OCSP: SingleResponse", resp, len); + + /* + * SingleResponse ::= SEQUENCE { + * certID CertID, + * certStatus CertStatus, + * thisUpdate GeneralizedTime, + * nextUpdate [0] EXPLICIT GeneralizedTime OPTIONAL, + * singleExtensions [1] EXPLICIT Extensions OPTIONAL } + */ + + /* CertID ::= SEQUENCE */ + if (asn1_get_next(resp, len, &hdr) < 0 || !asn1_is_sequence(&hdr)) { + asn1_unexpected(&hdr, "OCSP: Expected SEQUENCE (CertID)"); + return -1; + } + pos = hdr.payload; + end = hdr.payload + hdr.length; + + /* + * CertID ::= SEQUENCE { + * hashAlgorithm AlgorithmIdentifier, + * issuerNameHash OCTET STRING, + * issuerKeyHash OCTET STRING, + * serialNumber CertificateSerialNumber } + */ + + /* hashAlgorithm AlgorithmIdentifier */ + if (x509_parse_algorithm_identifier(pos, end - pos, &alg, &pos)) + return -1; + + /* issuerNameHash OCTET STRING */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + !asn1_is_octetstring(&hdr)) { + asn1_unexpected(&hdr, + "OCSP: Expected OCTET STRING (issuerNameHash)"); + return -1; + } + name_hash = hdr.payload; + name_hash_len = hdr.length; + wpa_hexdump(MSG_DEBUG, "OCSP: issuerNameHash", + name_hash, name_hash_len); + pos = hdr.payload + hdr.length; + + wpa_hexdump(MSG_DEBUG, "OCSP: Issuer subject DN", + issuer->subject_dn, issuer->subject_dn_len); + hash_len = ocsp_hash_data(&alg.oid, issuer->subject_dn, + issuer->subject_dn_len, hash); + if (hash_len == 0 || name_hash_len != hash_len || + os_memcmp(name_hash, hash, hash_len) != 0) { + wpa_printf(MSG_DEBUG, "OCSP: issuerNameHash mismatch"); + wpa_hexdump(MSG_DEBUG, "OCSP: Calculated issuerNameHash", + hash, hash_len); + return -1; + } + + /* issuerKeyHash OCTET STRING */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + !asn1_is_octetstring(&hdr)) { + asn1_unexpected(&hdr, + "OCSP: Expected OCTET STRING (issuerKeyHash)"); + return -1; + } + key_hash = hdr.payload; + key_hash_len = hdr.length; + wpa_hexdump(MSG_DEBUG, "OCSP: issuerKeyHash", key_hash, key_hash_len); + pos = hdr.payload + hdr.length; + + hash_len = ocsp_hash_data(&alg.oid, issuer->public_key, + issuer->public_key_len, hash); + if (hash_len == 0 || key_hash_len != hash_len || + os_memcmp(key_hash, hash, hash_len) != 0) { + wpa_printf(MSG_DEBUG, "OCSP: issuerKeyHash mismatch"); + wpa_hexdump(MSG_DEBUG, "OCSP: Calculated issuerKeyHash", + hash, hash_len); + return -1; + } + + /* serialNumber CertificateSerialNumber ::= INTEGER */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + !asn1_is_integer(&hdr) || + hdr.length < 1 || hdr.length > X509_MAX_SERIAL_NUM_LEN) { + asn1_unexpected(&hdr, + "OCSP: No INTEGER tag found for serialNumber"); + return -1; + } + serial_number = hdr.payload; + serial_number_len = hdr.length; + while (serial_number_len > 0 && serial_number[0] == 0) { + serial_number++; + serial_number_len--; + } + wpa_hexdump(MSG_MSGDUMP, "OCSP: serialNumber", serial_number, + serial_number_len); + + if (serial_number_len != cert->serial_number_len || + os_memcmp(serial_number, cert->serial_number, + serial_number_len) != 0) { + wpa_printf(MSG_DEBUG, "OCSP: serialNumber mismatch"); + return -1; + } + + pos = end; + end = resp + len; + + /* certStatus CertStatus ::= CHOICE + * + * CertStatus ::= CHOICE { + * good [0] IMPLICIT NULL, + * revoked [1] IMPLICIT RevokedInfo, + * unknown [2] IMPLICIT UnknownInfo } + */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC) { + asn1_unexpected(&hdr, "OCSP: Expected CHOICE (CertStatus)"); + return -1; + } + cert_status = hdr.tag; + wpa_printf(MSG_DEBUG, "OCSP: certStatus=%u", cert_status); + wpa_hexdump(MSG_DEBUG, "OCSP: CertStatus additional data", + hdr.payload, hdr.length); + pos = hdr.payload + hdr.length; + + os_get_time(&now); + /* thisUpdate GeneralizedTime */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + !asn1_is_generalizedtime(&hdr) || + x509_parse_time(hdr.payload, hdr.length, hdr.tag, &update) < 0) { + wpa_printf(MSG_DEBUG, "OCSP: Failed to parse thisUpdate"); + return -1; + } + wpa_printf(MSG_DEBUG, "OCSP: thisUpdate %lu", (unsigned long) update); + pos = hdr.payload + hdr.length; + if ((unsigned long) now.sec < (unsigned long) update) { + wpa_printf(MSG_DEBUG, + "OCSP: thisUpdate time in the future (response not yet valid)"); + return -1; + } + + /* nextUpdate [0] EXPLICIT GeneralizedTime OPTIONAL */ + if (pos < end) { + if (asn1_get_next(pos, end - pos, &hdr) < 0) + return -1; + if (asn1_is_cs_tag(&hdr, 0) && hdr.constructed) { + const u8 *next = hdr.payload + hdr.length; + + if (asn1_get_next(hdr.payload, hdr.length, &hdr) < 0 || + !asn1_is_generalizedtime(&hdr) || + x509_parse_time(hdr.payload, hdr.length, hdr.tag, + &update) < 0) { + wpa_printf(MSG_DEBUG, + "OCSP: Failed to parse nextUpdate"); + return -1; + } + wpa_printf(MSG_DEBUG, "OCSP: nextUpdate %lu", + (unsigned long) update); + pos = next; + if ((unsigned long) now.sec > (unsigned long) update) { + wpa_printf(MSG_DEBUG, "OCSP: nextUpdate time in the past (response has expired)"); + return -1; + } + } + } + + /* singleExtensions [1] EXPLICIT Extensions OPTIONAL */ + if (pos < end) { + wpa_hexdump(MSG_MSGDUMP, "OCSP: singleExtensions", + pos, end - pos); + /* Ignore for now */ + } + + if (cert_status == 0 /* good */) + *res = TLS_OCSP_GOOD; + else if (cert_status == 1 /* revoked */) + *res = TLS_OCSP_REVOKED; + else + return -1; + return 0; +} + + +static enum tls_ocsp_result +tls_process_ocsp_responses(struct tlsv1_client *conn, + struct x509_certificate *cert, + struct x509_certificate *issuer, const u8 *resp, + size_t len) +{ + struct asn1_hdr hdr; + const u8 *pos, *end; + enum tls_ocsp_result res; + + pos = resp; + end = resp + len; + while (pos < end) { + /* SingleResponse ::= SEQUENCE */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + !asn1_is_sequence(&hdr)) { + asn1_unexpected(&hdr, + "OCSP: Expected SEQUENCE (SingleResponse)"); + return TLS_OCSP_INVALID; + } + if (tls_process_ocsp_single_response(conn, cert, issuer, + hdr.payload, hdr.length, + &res) == 0) + return res; + pos = hdr.payload + hdr.length; + } + + wpa_printf(MSG_DEBUG, + "OCSP: Did not find a response matching the server certificate"); + return TLS_OCSP_NO_RESPONSE; +} + + +static enum tls_ocsp_result +tls_process_basic_ocsp_response(struct tlsv1_client *conn, + struct x509_certificate *srv_cert, + const u8 *resp, size_t len) +{ + struct asn1_hdr hdr; + const u8 *pos, *end; + const u8 *resp_data, *sign_value, *key_hash = NULL, *responses; + const u8 *resp_data_signed; + size_t resp_data_len, sign_value_len, responses_len; + size_t resp_data_signed_len; + struct x509_algorithm_identifier alg; + struct x509_certificate *certs = NULL, *last_cert = NULL; + struct x509_certificate *issuer, *signer; + struct x509_name name; /* used if key_hash == NULL */ + char buf[100]; + os_time_t produced_at; + enum tls_ocsp_result res; + + wpa_hexdump(MSG_MSGDUMP, "OCSP: BasicOCSPResponse", resp, len); + + os_memset(&name, 0, sizeof(name)); + + /* + * RFC 6960, 4.2.1: + * BasicOCSPResponse ::= SEQUENCE { + * tbsResponseData ResponseData, + * signatureAlgorithm AlgorithmIdentifier, + * signature BIT STRING, + * certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL } + */ + + if (asn1_get_next(resp, len, &hdr) < 0 || !asn1_is_sequence(&hdr)) { + asn1_unexpected(&hdr, + "OCSP: Expected SEQUENCE (BasicOCSPResponse)"); + return TLS_OCSP_INVALID; + } + pos = hdr.payload; + end = hdr.payload + hdr.length; + + /* ResponseData ::= SEQUENCE */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + !asn1_is_sequence(&hdr)) { + asn1_unexpected(&hdr, + "OCSP: Expected SEQUENCE (ResponseData)"); + return TLS_OCSP_INVALID; + } + resp_data = hdr.payload; + resp_data_len = hdr.length; + resp_data_signed = pos; + pos = hdr.payload + hdr.length; + resp_data_signed_len = pos - resp_data_signed; + + /* signatureAlgorithm AlgorithmIdentifier */ + if (x509_parse_algorithm_identifier(pos, end - pos, &alg, &pos)) + return TLS_OCSP_INVALID; + + /* signature BIT STRING */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + !asn1_is_bitstring(&hdr)) { + asn1_unexpected(&hdr, + "OCSP: Expected BITSTRING (signature)"); + return TLS_OCSP_INVALID; + } + if (hdr.length < 1) + return TLS_OCSP_INVALID; + pos = hdr.payload; + if (*pos) { + wpa_printf(MSG_DEBUG, "OCSP: BITSTRING - %d unused bits", *pos); + /* PKCS #1 v1.5 10.2.1: + * It is an error if the length in bits of the signature S is + * not a multiple of eight. + */ + return TLS_OCSP_INVALID; + } + sign_value = pos + 1; + sign_value_len = hdr.length - 1; + pos += hdr.length; + wpa_hexdump(MSG_MSGDUMP, "OCSP: signature", sign_value, sign_value_len); + + /* certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL */ + if (pos < end) { + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + !hdr.constructed || !asn1_is_cs_tag(&hdr, 0)) { + asn1_unexpected(&hdr, + "OCSP: Expected [0] EXPLICIT (certs)"); + return TLS_OCSP_INVALID; + } + wpa_hexdump(MSG_MSGDUMP, "OCSP: certs", + hdr.payload, hdr.length); + pos = hdr.payload; + end = hdr.payload + hdr.length; + while (pos < end) { + struct x509_certificate *cert; + + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + !asn1_is_sequence(&hdr)) { + asn1_unexpected(&hdr, + "OCSP: Expected SEQUENCE (Certificate)"); + goto fail; + } + + cert = x509_certificate_parse(hdr.payload, hdr.length); + if (!cert) + goto fail; + if (last_cert) { + last_cert->next = cert; + last_cert = cert; + } else { + last_cert = certs = cert; + } + pos = hdr.payload + hdr.length; + } + } + + /* + * ResponseData ::= SEQUENCE { + * version [0] EXPLICIT Version DEFAULT v1, + * responderID ResponderID, + * producedAt GeneralizedTime, + * responses SEQUENCE OF SingleResponse, + * responseExtensions [1] EXPLICIT Extensions OPTIONAL } + */ + pos = resp_data; + end = resp_data + resp_data_len; + wpa_hexdump(MSG_MSGDUMP, "OCSP: ResponseData", pos, end - pos); + + /* + * version [0] EXPLICIT Version DEFAULT v1 + * Version ::= INTEGER { v1(0) } + */ + if (asn1_get_next(pos, end - pos, &hdr) == 0 && hdr.constructed && + asn1_is_cs_tag(&hdr, 0)) { + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + !asn1_is_integer(&hdr) || hdr.length != 1) { + asn1_unexpected(&hdr, + "OCSP: No INTEGER (len=1) tag found for version field"); + goto fail; + } + wpa_printf(MSG_DEBUG, "OCSP: ResponseData version %u", + hdr.payload[0]); + if (hdr.payload[0] != 0) { + wpa_printf(MSG_DEBUG, + "OCSP: Unsupported ResponseData version %u", + hdr.payload[0]); + goto no_resp; + } + pos = hdr.payload + hdr.length; + } else { + wpa_printf(MSG_DEBUG, + "OCSP: Default ResponseData version (v1)"); + } + + /* + * ResponderID ::= CHOICE { + * byName [1] Name, + * byKey [2] KeyHash } + */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC) { + asn1_unexpected(&hdr, "OCSP: Expected CHOICE (ResponderID)"); + goto fail; + } + + if (hdr.tag == 1) { + /* Name */ + if (x509_parse_name(hdr.payload, hdr.length, &name, &pos) < 0) + goto fail; + x509_name_string(&name, buf, sizeof(buf)); + wpa_printf(MSG_DEBUG, "OCSP: ResponderID byName Name: %s", buf); + } else if (hdr.tag == 2) { + /* KeyHash ::= OCTET STRING */ + if (asn1_get_next(hdr.payload, hdr.length, &hdr) < 0 || + !asn1_is_octetstring(&hdr)) { + asn1_unexpected(&hdr, + "OCSP: Expected OCTET STRING (KeyHash)"); + goto fail; + } + key_hash = hdr.payload; + wpa_hexdump(MSG_DEBUG, "OCSP: ResponderID byKey KeyHash", + key_hash, hdr.length); + if (hdr.length != SHA1_MAC_LEN) { + wpa_printf(MSG_DEBUG, + "OCSP: Unexpected byKey KeyHash length %u - expected %u for SHA-1", + hdr.length, SHA1_MAC_LEN); + goto fail; + } + pos = hdr.payload + hdr.length; + } else { + wpa_printf(MSG_DEBUG, "OCSP: Unexpected ResponderID CHOICE %u", + hdr.tag); + goto fail; + } + + /* producedAt GeneralizedTime */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + !asn1_is_generalizedtime(&hdr) || + x509_parse_time(hdr.payload, hdr.length, hdr.tag, + &produced_at) < 0) { + wpa_printf(MSG_DEBUG, "OCSP: Failed to parse producedAt"); + goto fail; + } + wpa_printf(MSG_DEBUG, "OCSP: producedAt %lu", + (unsigned long) produced_at); + pos = hdr.payload + hdr.length; + + /* responses SEQUENCE OF SingleResponse */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + !asn1_is_sequence(&hdr)) { + asn1_unexpected(&hdr, + "OCSP: Expected SEQUENCE (responses)"); + goto fail; + } + responses = hdr.payload; + responses_len = hdr.length; + wpa_hexdump(MSG_MSGDUMP, "OCSP: responses", responses, responses_len); + pos = hdr.payload + hdr.length; + + if (pos < end) { + /* responseExtensions [1] EXPLICIT Extensions OPTIONAL */ + wpa_hexdump(MSG_MSGDUMP, "OCSP: responseExtensions", + pos, end - pos); + /* Ignore for now. */ + } + + if (!srv_cert) { + wpa_printf(MSG_DEBUG, + "OCSP: Server certificate not known - cannot check OCSP response"); + goto no_resp; + } + + if (srv_cert->next) { + /* Issuer has already been verified in the chain */ + issuer = srv_cert->next; + } else { + /* Find issuer from the set of trusted certificates */ + for (issuer = conn->cred ? conn->cred->trusted_certs : NULL; + issuer; issuer = issuer->next) { + if (x509_name_compare(&srv_cert->issuer, + &issuer->subject) == 0) + break; + } + } + if (!issuer) { + wpa_printf(MSG_DEBUG, + "OCSP: Server certificate issuer not known - cannot check OCSP response"); + goto no_resp; + } + + if (ocsp_responder_id_match(issuer, &name, key_hash)) { + wpa_printf(MSG_DEBUG, + "OCSP: Server certificate issuer certificate matches ResponderID"); + signer = issuer; + } else { + for (signer = certs; signer; signer = signer->next) { + if (!ocsp_responder_id_match(signer, &name, key_hash) || + x509_name_compare(&srv_cert->issuer, + &issuer->subject) != 0 || + !(signer->ext_key_usage & + X509_EXT_KEY_USAGE_OCSP) || + x509_certificate_check_signature(issuer, signer) < + 0) + continue; + wpa_printf(MSG_DEBUG, + "OCSP: An extra certificate from the response matches ResponderID and is trusted as an OCSP signer"); + break; + } + if (!signer) { + wpa_printf(MSG_DEBUG, + "OCSP: Could not find OCSP signer certificate"); + goto no_resp; + } + } + + x509_free_name(&name); + os_memset(&name, 0, sizeof(name)); + x509_certificate_chain_free(certs); + certs = NULL; + + if (x509_check_signature(signer, &alg, sign_value, sign_value_len, + resp_data_signed, resp_data_signed_len) < 0) { + wpa_printf(MSG_DEBUG, "OCSP: Invalid signature"); + return TLS_OCSP_INVALID; + } + + res = tls_process_ocsp_responses(conn, srv_cert, issuer, + responses, responses_len); + if (res == TLS_OCSP_REVOKED) + srv_cert->ocsp_revoked = 1; + else if (res == TLS_OCSP_GOOD) + srv_cert->ocsp_good = 1; + return res; + +no_resp: + x509_free_name(&name); + x509_certificate_chain_free(certs); + return TLS_OCSP_NO_RESPONSE; + +fail: + x509_free_name(&name); + x509_certificate_chain_free(certs); + return TLS_OCSP_INVALID; +} + + +enum tls_ocsp_result tls_process_ocsp_response(struct tlsv1_client *conn, + const u8 *resp, size_t len) +{ + struct asn1_hdr hdr; + const u8 *pos, *end; + u8 resp_status; + struct asn1_oid oid; + char obuf[80]; + struct x509_certificate *cert; + enum tls_ocsp_result res = TLS_OCSP_NO_RESPONSE; + enum tls_ocsp_result res_first = res; + + wpa_hexdump(MSG_MSGDUMP, "TLSv1: OCSPResponse", resp, len); + + /* + * RFC 6960, 4.2.1: + * OCSPResponse ::= SEQUENCE { + * responseStatus OCSPResponseStatus, + * responseBytes [0] EXPLICIT ResponseBytes OPTIONAL } + */ + + if (asn1_get_next(resp, len, &hdr) < 0 || !asn1_is_sequence(&hdr)) { + asn1_unexpected(&hdr, + "OCSP: Expected SEQUENCE (OCSPResponse)"); + return TLS_OCSP_INVALID; + } + pos = hdr.payload; + end = hdr.payload + hdr.length; + + /* OCSPResponseStatus ::= ENUMERATED */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + !asn1_is_enumerated(&hdr) || hdr.length != 1) { + asn1_unexpected(&hdr, + "OCSP: Expected ENUMERATED (responseStatus)"); + return TLS_OCSP_INVALID; + } + resp_status = hdr.payload[0]; + wpa_printf(MSG_DEBUG, "OCSP: responseStatus %u", resp_status); + pos = hdr.payload + hdr.length; + if (resp_status != OCSP_RESP_STATUS_SUCCESSFUL) { + wpa_printf(MSG_DEBUG, "OCSP: No stapling result"); + return TLS_OCSP_NO_RESPONSE; + } + + /* responseBytes [0] EXPLICIT ResponseBytes OPTIONAL */ + if (pos == end) + return TLS_OCSP_NO_RESPONSE; + + if (asn1_get_next(pos, end - pos, &hdr) < 0 || !hdr.constructed || + !asn1_is_cs_tag(&hdr, 0)) { + asn1_unexpected(&hdr, + "OCSP: Expected [0] EXPLICIT (responseBytes)"); + return TLS_OCSP_INVALID; + } + + /* + * ResponseBytes ::= SEQUENCE { + * responseType OBJECT IDENTIFIER, + * response OCTET STRING } + */ + + if (asn1_get_next(hdr.payload, hdr.length, &hdr) < 0 || + !asn1_is_sequence(&hdr)) { + asn1_unexpected(&hdr, + "OCSP: Expected SEQUENCE (ResponseBytes)"); + return TLS_OCSP_INVALID; + } + pos = hdr.payload; + end = hdr.payload + hdr.length; + + /* responseType OBJECT IDENTIFIER */ + if (asn1_get_oid(pos, end - pos, &oid, &pos)) { + wpa_printf(MSG_DEBUG, + "OCSP: Failed to parse OID (responseType)"); + return TLS_OCSP_INVALID; + } + asn1_oid_to_str(&oid, obuf, sizeof(obuf)); + wpa_printf(MSG_DEBUG, "OCSP: responseType %s", obuf); + if (!is_oid_basic_ocsp_resp(&oid)) { + wpa_printf(MSG_DEBUG, "OCSP: Ignore unsupported response type"); + return TLS_OCSP_NO_RESPONSE; + } + + /* response OCTET STRING */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + !asn1_is_octetstring(&hdr)) { + asn1_unexpected(&hdr, "OCSP: Expected OCTET STRING (response)"); + return TLS_OCSP_INVALID; + } + + cert = conn->server_cert; + while (cert) { + if (!cert->ocsp_good && !cert->ocsp_revoked) { + char sbuf[128]; + + x509_name_string(&cert->subject, sbuf, sizeof(sbuf)); + wpa_printf(MSG_DEBUG, + "OCSP: Trying to find certificate status for %s", + sbuf); + + res = tls_process_basic_ocsp_response(conn, cert, + hdr.payload, + hdr.length); + if (cert == conn->server_cert) + res_first = res; + } + if (res == TLS_OCSP_REVOKED || cert->issuer_trusted) + break; + cert = cert->next; + } + return res == TLS_OCSP_REVOKED ? res : res_first; +} diff --git a/components/wpa_supplicant/src/tls/tlsv1_client_read.c b/components/wpa_supplicant/src/tls/tlsv1_client_read.c index ada19464ae..9eac82f16e 100644 --- a/components/wpa_supplicant/src/tls/tlsv1_client_read.c +++ b/components/wpa_supplicant/src/tls/tlsv1_client_read.c @@ -1,24 +1,23 @@ /* * TLSv1 client - read handshake message - * Copyright (c) 2006-2011, Jouni Malinen + * Copyright (c) 2006-2015, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ -#include "utils/includes.h" +#include "includes.h" -#include "utils/common.h" +#include "common.h" #include "crypto/md5.h" #include "crypto/sha1.h" #include "crypto/sha256.h" -#include "tls/tls.h" -#include "tls/x509v3.h" -#include "tls/tlsv1_common.h" -#include "tls/tlsv1_record.h" -#include "tls/tlsv1_client.h" -#include "tls/tlsv1_client_i.h" -#include "eap_peer/eap_i.h" +#include "crypto/tls.h" +#include "x509v3.h" +#include "tlsv1_common.h" +#include "tlsv1_record.h" +#include "tlsv1_client.h" +#include "tlsv1_client_i.h" static int tls_process_server_key_exchange(struct tlsv1_client *conn, u8 ct, const u8 *in_data, size_t *in_len); @@ -28,6 +27,57 @@ static int tls_process_server_hello_done(struct tlsv1_client *conn, u8 ct, const u8 *in_data, size_t *in_len); +static int tls_version_disabled(struct tlsv1_client *conn, u16 ver) +{ + return (((conn->flags & TLS_CONN_DISABLE_TLSv1_0) && + ver == TLS_VERSION_1) || + ((conn->flags & TLS_CONN_DISABLE_TLSv1_1) && + ver == TLS_VERSION_1_1) || + ((conn->flags & TLS_CONN_DISABLE_TLSv1_2) && + ver == TLS_VERSION_1_2)); +} + + +static int tls_process_server_hello_extensions(struct tlsv1_client *conn, + const u8 *pos, size_t len) +{ + const u8 *end = pos + len; + + wpa_hexdump(MSG_MSGDUMP, "TLSv1: ServerHello extensions", + pos, len); + while (pos < end) { + u16 elen; + + if (end - pos < 4) { + wpa_printf(MSG_INFO, "TLSv1: Truncated ServerHello extension header"); + return -1; + } + +#ifdef DEBUG_PRINT + u16 ext; + ext = WPA_GET_BE16(pos); + wpa_printf(MSG_DEBUG, "TLSv1: ServerHello ExtensionType %u", + ext); +#endif + pos += 2; + elen = WPA_GET_BE16(pos); + pos += 2; + + if (elen > end - pos) { + wpa_printf(MSG_INFO, "TLSv1: Truncated ServerHello extension"); + return -1; + } + + wpa_hexdump(MSG_DEBUG, "TLSv1: ServerHello extension data", + pos, elen); + + pos += elen; + } + + return 0; +} + + static int tls_process_server_hello(struct tlsv1_client *conn, u8 ct, const u8 *in_data, size_t *in_len) { @@ -77,7 +127,8 @@ static int tls_process_server_hello(struct tlsv1_client *conn, u8 ct, if (end - pos < 2) goto decode_error; tls_version = WPA_GET_BE16(pos); - if (!tls_version_ok(tls_version)) { + if (!tls_version_ok(tls_version) || + tls_version_disabled(conn, tls_version)) { wpa_printf(MSG_DEBUG, "TLSv1: Unexpected protocol version in " "ServerHello %u.%u", pos[0], pos[1]); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, @@ -166,8 +217,24 @@ static int tls_process_server_hello(struct tlsv1_client *conn, u8 ct, } pos++; + if (end - pos >= 2) { + u16 ext_len; + + ext_len = WPA_GET_BE16(pos); + pos += 2; + if (end - pos < ext_len) { + wpa_printf(MSG_INFO, + "TLSv1: Invalid ServerHello extension length: %u (left: %u)", + ext_len, (unsigned int) (end - pos)); + goto decode_error; + } + + if (tls_process_server_hello_extensions(conn, pos, ext_len)) + goto decode_error; + pos += ext_len; + } + if (end != pos) { - /* TODO: ServerHello extensions */ wpa_hexdump(MSG_DEBUG, "TLSv1: Unexpected extra data in the " "end of ServerHello", pos, end - pos); goto decode_error; @@ -212,6 +279,83 @@ decode_error: } +static void tls_peer_cert_event(struct tlsv1_client *conn, int depth, + struct x509_certificate *cert) +{ + union tls_event_data ev; + struct wpabuf *cert_buf = NULL; +#ifdef CONFIG_SHA256 + u8 hash[32]; +#endif /* CONFIG_SHA256 */ + char subject[128]; + + if (!conn->event_cb) + return; + + os_memset(&ev, 0, sizeof(ev)); + if ((conn->cred && conn->cred->cert_probe) || conn->cert_in_cb) { + cert_buf = wpabuf_alloc_copy(cert->cert_start, + cert->cert_len); + ev.peer_cert.cert = cert_buf; + } +#ifdef CONFIG_SHA256 + if (cert_buf) { + const u8 *addr[1]; + size_t len[1]; + addr[0] = wpabuf_head(cert_buf); + len[0] = wpabuf_len(cert_buf); + if (sha256_vector(1, addr, len, hash) == 0) { + ev.peer_cert.hash = hash; + ev.peer_cert.hash_len = sizeof(hash); + } + } +#endif /* CONFIG_SHA256 */ + + ev.peer_cert.depth = depth; + x509_name_string(&cert->subject, subject, sizeof(subject)); + ev.peer_cert.subject = subject; + + if (cert->extensions_present & X509_EXT_CERTIFICATE_POLICY) { + if (cert->certificate_policy & X509_EXT_CERT_POLICY_TOD_STRICT) + ev.peer_cert.tod = 1; + else if (cert->certificate_policy & + X509_EXT_CERT_POLICY_TOD_TOFU) + ev.peer_cert.tod = 2; + } + + conn->event_cb(conn->cb_ctx, TLS_PEER_CERTIFICATE, &ev); + wpabuf_free(cert_buf); +} + + +static void tls_cert_chain_failure_event(struct tlsv1_client *conn, int depth, + struct x509_certificate *cert, + enum tls_fail_reason reason, + const char *reason_txt) +{ +#ifndef ESP_SUPPLICANT + struct wpabuf *cert_buf = NULL; + union tls_event_data ev; + char subject[128]; + + if (!conn->event_cb || !cert) + return; + + os_memset(&ev, 0, sizeof(ev)); + ev.cert_fail.depth = depth; + x509_name_string(&cert->subject, subject, sizeof(subject)); + ev.peer_cert.subject = subject; + ev.cert_fail.reason = reason; + ev.cert_fail.reason_txt = reason_txt; + cert_buf = wpabuf_alloc_copy(cert->cert_start, + cert->cert_len); + ev.cert_fail.cert = cert_buf; + conn->event_cb(conn->cb_ctx, TLS_CERT_CHAIN_FAILURE, &ev); + wpabuf_free(cert_buf); +#endif +} + + static int tls_process_certificate(struct tlsv1_client *conn, u8 ct, const u8 *in_data, size_t *in_len) { @@ -271,7 +415,8 @@ static int tls_process_certificate(struct tlsv1_client *conn, u8 ct, return -1; } - wpa_printf(MSG_DEBUG, "TLSv1: Received Certificate (certificate_list len %lu)", + wpa_printf(MSG_DEBUG, + "TLSv1: Received Certificate (certificate_list len %lu)", (unsigned long) len); /* @@ -354,6 +499,8 @@ static int tls_process_certificate(struct tlsv1_client *conn, u8 ct, return -1; } + tls_peer_cert_event(conn, idx, cert); + if (last == NULL) chain = cert; else @@ -364,31 +511,99 @@ static int tls_process_certificate(struct tlsv1_client *conn, u8 ct, pos += cert_len; } - if (conn->cred && - x509_certificate_chain_validate(conn->cred->trusted_certs, chain, - &reason, conn->disable_time_checks) - < 0) { + if (conn->cred && conn->cred->server_cert_only && chain) { + u8 hash[SHA256_MAC_LEN]; + char buf[128]; + + wpa_printf(MSG_DEBUG, + "TLSv1: Validate server certificate hash"); + x509_name_string(&chain->subject, buf, sizeof(buf)); + wpa_printf(MSG_DEBUG, "TLSv1: 0: %s", buf); + if (sha256_vector(1, &chain->cert_start, &chain->cert_len, + hash) < 0 || + os_memcmp(conn->cred->srv_cert_hash, hash, + SHA256_MAC_LEN) != 0) { + wpa_printf(MSG_DEBUG, + "TLSv1: Server certificate hash mismatch"); + wpa_hexdump(MSG_MSGDUMP, "TLSv1: SHA256 hash", + hash, SHA256_MAC_LEN); + if (conn->event_cb) { + union tls_event_data ev; + + os_memset(&ev, 0, sizeof(ev)); + ev.cert_fail.reason = TLS_FAIL_UNSPECIFIED; + ev.cert_fail.reason_txt = + "Server certificate mismatch"; + ev.cert_fail.subject = buf; + conn->event_cb(conn->cb_ctx, + TLS_CERT_CHAIN_FAILURE, &ev); + } + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_BAD_CERTIFICATE); + x509_certificate_chain_free(chain); + return -1; + } + } else if (conn->cred && conn->cred->cert_probe) { + wpa_printf(MSG_DEBUG, + "TLSv1: Reject server certificate on probe-only run"); + if (conn->event_cb) { + union tls_event_data ev; + char buf[128]; + + os_memset(&ev, 0, sizeof(ev)); + ev.cert_fail.reason = TLS_FAIL_SERVER_CHAIN_PROBE; + ev.cert_fail.reason_txt = + "Server certificate chain probe"; + if (chain) { + x509_name_string(&chain->subject, buf, + sizeof(buf)); + ev.cert_fail.subject = buf; + } + conn->event_cb(conn->cb_ctx, TLS_CERT_CHAIN_FAILURE, + &ev); + } + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_BAD_CERTIFICATE); + x509_certificate_chain_free(chain); + return -1; + } else if (conn->cred && conn->cred->ca_cert_verify && + x509_certificate_chain_validate( + conn->cred->trusted_certs, chain, &reason, + !!(conn->flags & TLS_CONN_DISABLE_TIME_CHECKS)) + < 0) { int tls_reason; wpa_printf(MSG_DEBUG, "TLSv1: Server certificate chain " "validation failed (reason=%d)", reason); switch (reason) { case X509_VALIDATE_BAD_CERTIFICATE: tls_reason = TLS_ALERT_BAD_CERTIFICATE; + tls_cert_chain_failure_event( + conn, 0, chain, TLS_FAIL_BAD_CERTIFICATE, + "bad certificate"); break; case X509_VALIDATE_UNSUPPORTED_CERTIFICATE: tls_reason = TLS_ALERT_UNSUPPORTED_CERTIFICATE; break; case X509_VALIDATE_CERTIFICATE_REVOKED: tls_reason = TLS_ALERT_CERTIFICATE_REVOKED; + tls_cert_chain_failure_event( + conn, 0, chain, TLS_FAIL_REVOKED, + "certificate revoked"); break; case X509_VALIDATE_CERTIFICATE_EXPIRED: tls_reason = TLS_ALERT_CERTIFICATE_EXPIRED; + tls_cert_chain_failure_event( + conn, 0, chain, TLS_FAIL_EXPIRED, + "certificate has expired or is not yet valid"); break; case X509_VALIDATE_CERTIFICATE_UNKNOWN: tls_reason = TLS_ALERT_CERTIFICATE_UNKNOWN; break; case X509_VALIDATE_UNKNOWN_CA: tls_reason = TLS_ALERT_UNKNOWN_CA; + tls_cert_chain_failure_event( + conn, 0, chain, TLS_FAIL_UNTRUSTED, + "unknown CA"); break; default: tls_reason = TLS_ALERT_BAD_CERTIFICATE; @@ -399,21 +614,66 @@ static int tls_process_certificate(struct tlsv1_client *conn, u8 ct, return -1; } - x509_certificate_chain_free(chain); + if (conn->cred && !conn->cred->server_cert_only && chain && + (chain->extensions_present & X509_EXT_EXT_KEY_USAGE) && + !(chain->ext_key_usage & + (X509_EXT_KEY_USAGE_ANY | X509_EXT_KEY_USAGE_SERVER_AUTH))) { + tls_cert_chain_failure_event( + conn, 0, chain, TLS_FAIL_BAD_CERTIFICATE, + "certificate not allowed for server authentication"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_BAD_CERTIFICATE); + x509_certificate_chain_free(chain); + return -1; + } + + if (conn->flags & TLS_CONN_REQUEST_OCSP) { + x509_certificate_chain_free(conn->server_cert); + conn->server_cert = chain; + } else { + x509_certificate_chain_free(chain); + } *in_len = end - in_data; + conn->state = SERVER_KEY_EXCHANGE; return 0; } +static unsigned int count_bits(const u8 *val, size_t len) +{ + size_t i; + unsigned int bits; + u8 tmp; + + for (i = 0; i < len; i++) { + if (val[i]) + break; + } + if (i == len) + return 0; + + bits = (len - i - 1) * 8; + tmp = val[i]; + while (tmp) { + bits++; + tmp >>= 1; + } + + return bits; +} + + static int tlsv1_process_diffie_hellman(struct tlsv1_client *conn, const u8 *buf, size_t len, tls_key_exchange key_exchange) { const u8 *pos, *end, *server_params, *server_params_end; u8 alert; + unsigned int bits; + u16 val; tlsv1_client_free_dh(conn); @@ -423,31 +683,38 @@ static int tlsv1_process_diffie_hellman(struct tlsv1_client *conn, if (end - pos < 3) goto fail; server_params = pos; - conn->dh_p_len = WPA_GET_BE16(pos); + val = WPA_GET_BE16(pos); pos += 2; - if (conn->dh_p_len == 0 || end - pos < (int) conn->dh_p_len) { - wpa_printf(MSG_DEBUG, "TLSv1: Invalid dh_p length %lu", - (unsigned long) conn->dh_p_len); + if (val == 0 || val > (size_t) (end - pos)) { + wpa_printf(MSG_DEBUG, "TLSv1: Invalid dh_p length %u", val); goto fail; } - conn->dh_p = os_malloc(conn->dh_p_len); + conn->dh_p_len = val; + bits = count_bits(pos, conn->dh_p_len); + if (bits < 768) { + wpa_printf(MSG_INFO, "TLSv1: Reject under 768-bit DH prime (insecure; only %u bits)", + bits); + wpa_hexdump(MSG_DEBUG, "TLSv1: Rejected DH prime", + pos, conn->dh_p_len); + goto fail; + } + conn->dh_p = os_memdup(pos, conn->dh_p_len); if (conn->dh_p == NULL) goto fail; - os_memcpy(conn->dh_p, pos, conn->dh_p_len); pos += conn->dh_p_len; wpa_hexdump(MSG_DEBUG, "TLSv1: DH p (prime)", conn->dh_p, conn->dh_p_len); if (end - pos < 3) goto fail; - conn->dh_g_len = WPA_GET_BE16(pos); + val = WPA_GET_BE16(pos); pos += 2; - if (conn->dh_g_len == 0 || end - pos < (int) conn->dh_g_len) + if (val == 0 || val > (size_t) (end - pos)) goto fail; - conn->dh_g = os_malloc(conn->dh_g_len); + conn->dh_g_len = val; + conn->dh_g = os_memdup(pos, conn->dh_g_len); if (conn->dh_g == NULL) goto fail; - os_memcpy(conn->dh_g, pos, conn->dh_g_len); pos += conn->dh_g_len; wpa_hexdump(MSG_DEBUG, "TLSv1: DH g (generator)", conn->dh_g, conn->dh_g_len); @@ -456,14 +723,14 @@ static int tlsv1_process_diffie_hellman(struct tlsv1_client *conn, if (end - pos < 3) goto fail; - conn->dh_ys_len = WPA_GET_BE16(pos); + val = WPA_GET_BE16(pos); pos += 2; - if (conn->dh_ys_len == 0 || end - pos < (int) conn->dh_ys_len) + if (val == 0 || val > (size_t) (end - pos)) goto fail; - conn->dh_ys = os_malloc(conn->dh_ys_len); + conn->dh_ys_len = val; + conn->dh_ys = os_memdup(pos, conn->dh_ys_len); if (conn->dh_ys == NULL) goto fail; - os_memcpy(conn->dh_ys, pos, conn->dh_ys_len); pos += conn->dh_ys_len; wpa_hexdump(MSG_DEBUG, "TLSv1: DH Ys (server's public value)", conn->dh_ys, conn->dh_ys_len); @@ -487,7 +754,9 @@ static int tlsv1_process_diffie_hellman(struct tlsv1_client *conn, */ if (end - pos < 2) goto fail; - if ((pos[0] != TLS_HASH_ALG_SHA256) || + if ((pos[0] != TLS_HASH_ALG_SHA256 && + pos[0] != TLS_HASH_ALG_SHA384 && + pos[0] != TLS_HASH_ALG_SHA512) || pos[1] != TLS_SIGN_ALG_RSA) { wpa_printf(MSG_DEBUG, "TLSv1.2: Unsupported hash(%u)/signature(%u) algorithm", pos[0], pos[1]); @@ -531,6 +800,229 @@ fail: } +static enum tls_ocsp_result +tls_process_certificate_status_ocsp_response(struct tlsv1_client *conn, + const u8 *pos, size_t len) +{ + const u8 *end = pos + len; + u32 ocsp_resp_len; + + /* opaque OCSPResponse<1..2^24-1>; */ + if (end - pos < 3) { + wpa_printf(MSG_INFO, "TLSv1: Too short OCSPResponse"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); + return TLS_OCSP_INVALID; + } + ocsp_resp_len = WPA_GET_BE24(pos); + pos += 3; + if (end - pos < ocsp_resp_len) { + wpa_printf(MSG_INFO, "TLSv1: Truncated OCSPResponse"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); + return TLS_OCSP_INVALID; + } + + return tls_process_ocsp_response(conn, pos, ocsp_resp_len); +} + + +static int tls_process_certificate_status(struct tlsv1_client *conn, u8 ct, + const u8 *in_data, size_t *in_len) +{ + const u8 *pos, *end; + size_t left, len; + u8 type, status_type; + enum tls_ocsp_result res; + struct x509_certificate *cert; + int depth; + + if (ct != TLS_CONTENT_TYPE_HANDSHAKE) { + wpa_printf(MSG_DEBUG, + "TLSv1: Expected Handshake; received content type 0x%x", + ct); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + + pos = in_data; + left = *in_len; + + if (left < 4) { + wpa_printf(MSG_DEBUG, + "TLSv1: Too short CertificateStatus (left=%lu)", + (unsigned long) left); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); + return -1; + } + + type = *pos++; + len = WPA_GET_BE24(pos); + pos += 3; + left -= 4; + + if (len > left) { + wpa_printf(MSG_DEBUG, + "TLSv1: Mismatch in CertificateStatus length (len=%lu != left=%lu)", + (unsigned long) len, (unsigned long) left); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); + return -1; + } + + end = pos + len; + + if (type != TLS_HANDSHAKE_TYPE_CERTIFICATE_STATUS) { + wpa_printf(MSG_DEBUG, + "TLSv1: Received unexpected handshake message %d (expected CertificateStatus)", + type); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_UNEXPECTED_MESSAGE); + return -1; + } + + wpa_printf(MSG_DEBUG, "TLSv1: Received CertificateStatus"); + + /* + * struct { + * CertificateStatusType status_type; + * select (status_type) { + * case ocsp: OCSPResponse; + * case ocsp_multi: OCSPResponseList; + * } response; + * } CertificateStatus; + */ + if (end - pos < 1) { + wpa_printf(MSG_INFO, "TLSv1: Too short CertificateStatus"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); + return -1; + } + status_type = *pos++; + wpa_printf(MSG_DEBUG, "TLSv1: CertificateStatus status_type %u", + status_type); + + if (status_type == 1 /* ocsp */) { + res = tls_process_certificate_status_ocsp_response( + conn, pos, end - pos); + } else if (status_type == 2 /* ocsp_multi */) { + int good = 0, revoked = 0; + u32 resp_len; + + res = TLS_OCSP_NO_RESPONSE; + + /* + * opaque OCSPResponse<0..2^24-1>; + * + * struct { + * OCSPResponse ocsp_response_list<1..2^24-1>; + * } OCSPResponseList; + */ + if (end - pos < 3) { + wpa_printf(MSG_DEBUG, + "TLSv1: Truncated OCSPResponseList"); + res = TLS_OCSP_INVALID; + goto done; + } + resp_len = WPA_GET_BE24(pos); + pos += 3; + if (end - pos < resp_len) { + wpa_printf(MSG_DEBUG, + "TLSv1: Truncated OCSPResponseList(len=%u)", + resp_len); + res = TLS_OCSP_INVALID; + goto done; + } + end = pos + resp_len; + + while (end - pos >= 3) { + resp_len = WPA_GET_BE24(pos); + pos += 3; + if (resp_len > end - pos) { + wpa_printf(MSG_DEBUG, + "TLSv1: Truncated OCSPResponse(len=%u; left=%d) in ocsp_multi", + resp_len, (int) (end - pos)); + res = TLS_OCSP_INVALID; + break; + } + if (!resp_len) + continue; /* Skip an empty response */ + res = tls_process_certificate_status_ocsp_response( + conn, pos - 3, resp_len + 3); + if (res == TLS_OCSP_REVOKED) + revoked++; + else if (res == TLS_OCSP_GOOD) + good++; + pos += resp_len; + } + + if (revoked) + res = TLS_OCSP_REVOKED; + else if (good) + res = TLS_OCSP_GOOD; + } else { + wpa_printf(MSG_DEBUG, + "TLSv1: Ignore unsupported CertificateStatus"); + goto skip; + } + +done: + if (res == TLS_OCSP_REVOKED) { + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_CERTIFICATE_REVOKED); + for (cert = conn->server_cert, depth = 0; cert; + cert = cert->next, depth++) { + if (cert->ocsp_revoked) { + tls_cert_chain_failure_event( + conn, depth, cert, TLS_FAIL_REVOKED, + "certificate revoked"); + } + } + return -1; + } + + if (conn->flags & TLS_CONN_REQUIRE_OCSP_ALL) { + /* + * Verify that each certificate on the chain that is not part + * of the trusted certificates has a good status. If not, + * terminate handshake. + */ + for (cert = conn->server_cert, depth = 0; cert; + cert = cert->next, depth++) { + if (!cert->ocsp_good) { + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_BAD_CERTIFICATE_STATUS_RESPONSE); + tls_cert_chain_failure_event( + conn, depth, cert, + TLS_FAIL_UNSPECIFIED, + "bad certificate status response"); + return -1; + } + if (cert->issuer_trusted) + break; + } + } + + if ((conn->flags & TLS_CONN_REQUIRE_OCSP) && res != TLS_OCSP_GOOD) { + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + res == TLS_OCSP_INVALID ? TLS_ALERT_DECODE_ERROR : + TLS_ALERT_BAD_CERTIFICATE_STATUS_RESPONSE); + if (conn->server_cert) + tls_cert_chain_failure_event( + conn, 0, conn->server_cert, + TLS_FAIL_UNSPECIFIED, + "bad certificate status response"); + return -1; + } + + conn->ocsp_resp_received = 1; + +skip: + *in_len = end - in_data; + + conn->state = SERVER_KEY_EXCHANGE; + + return 0; +} + + static int tls_process_server_key_exchange(struct tlsv1_client *conn, u8 ct, const u8 *in_data, size_t *in_len) { @@ -572,6 +1064,10 @@ static int tls_process_server_key_exchange(struct tlsv1_client *conn, u8 ct, end = pos + len; + if ((conn->flags & TLS_CONN_REQUEST_OCSP) && + type == TLS_HANDSHAKE_TYPE_CERTIFICATE_STATUS) + return tls_process_certificate_status(conn, ct, in_data, + in_len); if (type == TLS_HANDSHAKE_TYPE_CERTIFICATE_REQUEST) return tls_process_certificate_request(conn, ct, in_data, in_len); @@ -581,7 +1077,9 @@ static int tls_process_server_key_exchange(struct tlsv1_client *conn, u8 ct, if (type != TLS_HANDSHAKE_TYPE_SERVER_KEY_EXCHANGE) { wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake " "message %d (expected ServerKeyExchange/" - "CertificateRequest/ServerHelloDone)", type); + "CertificateRequest/ServerHelloDone%s)", type, + (conn->flags & TLS_CONN_REQUEST_OCSP) ? + "/CertificateStatus" : ""); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_UNEXPECTED_MESSAGE); return -1; @@ -615,6 +1113,7 @@ static int tls_process_server_key_exchange(struct tlsv1_client *conn, u8 ct, } *in_len = end - in_data; + conn->state = SERVER_CERTIFICATE_REQUEST; return 0; @@ -678,6 +1177,7 @@ static int tls_process_certificate_request(struct tlsv1_client *conn, u8 ct, conn->certificate_requested = 1; *in_len = end - in_data; + conn->state = SERVER_HELLO_DONE; return 0; @@ -733,7 +1233,17 @@ static int tls_process_server_hello_done(struct tlsv1_client *conn, u8 ct, wpa_printf(MSG_DEBUG, "TLSv1: Received ServerHelloDone"); + if ((conn->flags & TLS_CONN_REQUIRE_OCSP) && + !conn->ocsp_resp_received) { + wpa_printf(MSG_INFO, + "TLSv1: No OCSP response received - reject handshake"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_BAD_CERTIFICATE_STATUS_RESPONSE); + return -1; + } + *in_len = end - in_data; + conn->state = CLIENT_KEY_EXCHANGE; return 0; @@ -767,7 +1277,7 @@ static int tls_process_server_change_cipher_spec(struct tlsv1_client *conn, TLS_ALERT_HANDSHAKE_FAILURE); return -1; } - printf("[Debug] set the state to server certificate \n"); + conn->state = SERVER_CERTIFICATE; return tls_process_certificate(conn, ct, in_data, in_len); @@ -804,6 +1314,7 @@ static int tls_process_server_change_cipher_spec(struct tlsv1_client *conn, } *in_len = pos + 1 - in_data; + conn->state = SERVER_FINISHED; return 0; @@ -874,9 +1385,11 @@ static int tls_process_server_finished(struct tlsv1_client *conn, u8 ct, #ifdef CONFIG_TLSV12 if (conn->rl.tls_version >= TLS_VERSION_1_2) { hlen = SHA256_MAC_LEN; - if (conn->verify.sha256_server == NULL || - crypto_hash_finish(conn->verify.sha256_server, hash, &hlen) < 0) { - tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); + if (conn->verify.sha256_server == NULL || + crypto_hash_finish(conn->verify.sha256_server, hash, &hlen) + < 0) { + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); conn->verify.sha256_server = NULL; return -1; } @@ -923,8 +1436,10 @@ static int tls_process_server_finished(struct tlsv1_client *conn, u8 ct, wpa_hexdump_key(MSG_DEBUG, "TLSv1: verify_data (server)", verify_data, TLS_VERIFY_DATA_LEN); - if (os_memcmp(pos, verify_data, TLS_VERIFY_DATA_LEN) != 0) { + if (os_memcmp_const(pos, verify_data, TLS_VERIFY_DATA_LEN) != 0) { wpa_printf(MSG_INFO, "TLSv1: Mismatch in verify_data"); + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_DECRYPT_ERROR); return -1; } @@ -934,6 +1449,7 @@ static int tls_process_server_finished(struct tlsv1_client *conn, u8 ct, conn->state = (conn->session_resumed || conn->use_session_ticket) ? CHANGE_CIPHER_SPEC : ACK_FINISHED; + return 0; } @@ -1027,10 +1543,8 @@ int tlsv1_client_process_handshake(struct tlsv1_client *conn, u8 ct, return -1; break; case SERVER_FINISHED: - if (tls_process_server_finished(conn, ct, buf, len)) { - printf("[debug] server finish process fall \n"); + if (tls_process_server_finished(conn, ct, buf, len)) return -1; - } break; case ACK_FINISHED: if (out_data && @@ -1045,9 +1559,8 @@ int tlsv1_client_process_handshake(struct tlsv1_client *conn, u8 ct, return -1; } - if (ct == TLS_CONTENT_TYPE_HANDSHAKE) { + if (ct == TLS_CONTENT_TYPE_HANDSHAKE) tls_verify_hash_add(&conn->verify, buf, *len); - } return 0; } diff --git a/components/wpa_supplicant/src/tls/tlsv1_client_write.c b/components/wpa_supplicant/src/tls/tlsv1_client_write.c index a28ce4fa35..9b12618aaf 100644 --- a/components/wpa_supplicant/src/tls/tlsv1_client_write.c +++ b/components/wpa_supplicant/src/tls/tlsv1_client_write.c @@ -1,26 +1,25 @@ /* * TLSv1 client - write handshake message - * Copyright (c) 2006-2011, Jouni Malinen + * Copyright (c) 2006-2015, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ -#include "utils/includes.h" +#include "includes.h" -#include "utils/common.h" +#include "common.h" #include "crypto/md5.h" #include "crypto/sha1.h" #include "crypto/sha256.h" +#include "crypto/tls.h" #include "crypto/random.h" -#include "tls/tls.h" -#include "tls/x509v3.h" -#include "tls/tlsv1_common.h" -#include "tls/tlsv1_record.h" -#include "tls/tlsv1_client.h" -#include "tls/tlsv1_client_i.h" +#include "x509v3.h" +#include "tlsv1_common.h" +#include "tlsv1_record.h" +#include "tlsv1_client.h" +#include "tlsv1_client_i.h" -#include "eap_peer/eap_i.h" static size_t tls_client_cert_chain_der_len(struct tlsv1_client *conn) { @@ -49,11 +48,21 @@ u8 * tls_send_client_hello(struct tlsv1_client *conn, size_t *out_len) struct os_time now; size_t len, i; u8 *ext_start; + u16 tls_version = tls_client_highest_ver(conn); - wpa_printf(MSG_DEBUG, "TLSv1: Send ClientHello"); + if (!tls_version) { + wpa_printf(MSG_INFO, "TLSv1: No TLS version allowed"); + return NULL; + } + + wpa_printf(MSG_DEBUG, "TLSv1: Send ClientHello (ver %s)", + tls_version_str(tls_version)); *out_len = 0; os_get_time(&now); +#ifdef TEST_FUZZ + now.sec = 0xfffefdfc; +#endif /* TEST_FUZZ */ WPA_PUT_BE32(conn->client_random, now.sec); if (random_get_bytes(conn->client_random + 4, TLS_RANDOM_LEN - 4)) { wpa_printf(MSG_ERROR, "TLSv1: Could not generate " @@ -83,7 +92,7 @@ u8 * tls_send_client_hello(struct tlsv1_client *conn, size_t *out_len) pos += 3; /* body - ClientHello */ /* ProtocolVersion client_version */ - WPA_PUT_BE16(pos, TLS_VERSION); + WPA_PUT_BE16(pos, tls_version); pos += 2; /* Random random: uint32 gmt_unix_time, opaque random_bytes */ os_memcpy(pos, conn->client_random, TLS_RANDOM_LEN); @@ -117,12 +126,16 @@ u8 * tls_send_client_hello(struct tlsv1_client *conn, size_t *out_len) WPA_PUT_BE16(pos, TLS_EXT_SIGNATURE_ALGORITHMS); pos += 2; /* opaque extension_data<0..2^16-1> length */ - WPA_PUT_BE16(pos, 4); + WPA_PUT_BE16(pos, 8); pos += 2; /* supported_signature_algorithms<2..2^16-2> length */ - WPA_PUT_BE16(pos, 2); + WPA_PUT_BE16(pos, 6); pos += 2; /* supported_signature_algorithms */ + *pos++ = TLS_HASH_ALG_SHA512; + *pos++ = TLS_SIGN_ALG_RSA; + *pos++ = TLS_HASH_ALG_SHA384; + *pos++ = TLS_SIGN_ALG_RSA; *pos++ = TLS_HASH_ALG_SHA256; *pos++ = TLS_SIGN_ALG_RSA; } @@ -134,6 +147,84 @@ u8 * tls_send_client_hello(struct tlsv1_client *conn, size_t *out_len) pos += conn->client_hello_ext_len; } + if (conn->flags & TLS_CONN_REQUEST_OCSP) { + wpa_printf(MSG_DEBUG, + "TLSv1: Add status_request extension for OCSP stapling"); + /* ExtensionsType extension_type = status_request(5) */ + WPA_PUT_BE16(pos, TLS_EXT_STATUS_REQUEST); + pos += 2; + /* opaque extension_data<0..2^16-1> length */ + WPA_PUT_BE16(pos, 5); + pos += 2; + + /* + * RFC 6066, 8: + * struct { + * CertificateStatusType status_type; + * select (status_type) { + * case ocsp: OCSPStatusRequest; + * } request; + * } CertificateStatusRequest; + * + * enum { ocsp(1), (255) } CertificateStatusType; + */ + *pos++ = 1; /* status_type = ocsp(1) */ + + /* + * struct { + * ResponderID responder_id_list<0..2^16-1>; + * Extensions request_extensions; + * } OCSPStatusRequest; + * + * opaque ResponderID<1..2^16-1>; + * opaque Extensions<0..2^16-1>; + */ + WPA_PUT_BE16(pos, 0); /* responder_id_list(empty) */ + pos += 2; + WPA_PUT_BE16(pos, 0); /* request_extensions(empty) */ + pos += 2; + + wpa_printf(MSG_DEBUG, + "TLSv1: Add status_request_v2 extension for OCSP stapling"); + /* ExtensionsType extension_type = status_request_v2(17) */ + WPA_PUT_BE16(pos, TLS_EXT_STATUS_REQUEST_V2); + pos += 2; + /* opaque extension_data<0..2^16-1> length */ + WPA_PUT_BE16(pos, 7); + pos += 2; + + /* + * RFC 6961, 2.2: + * struct { + * CertificateStatusType status_type; + * uint16 request_length; + * select (status_type) { + * case ocsp: OCSPStatusRequest; + * case ocsp_multi: OCSPStatusRequest; + * } request; + * } CertificateStatusRequestItemV2; + * + * enum { ocsp(1), ocsp_multi(2), (255) } CertificateStatusType; + * + * struct { + * CertificateStatusRequestItemV2 + * certificate_status_req_list<1..2^16-1>; + * } CertificateStatusRequestListV2; + */ + + /* certificate_status_req_list<1..2^16-1> */ + WPA_PUT_BE16(pos, 5); + pos += 2; + + /* CertificateStatusRequestItemV2 */ + *pos++ = 2; /* status_type = ocsp_multi(2) */ + /* OCSPStatusRequest as shown above for v1 */ + WPA_PUT_BE16(pos, 0); /* responder_id_list(empty) */ + pos += 2; + WPA_PUT_BE16(pos, 0); /* request_extensions(empty) */ + pos += 2; + } + if (pos == ext_start + 2) pos -= 2; /* no extensions */ else @@ -166,6 +257,11 @@ static int tls_write_client_certificate(struct tlsv1_client *conn, struct x509_certificate *cert; pos = *msgpos; + if (TLS_RECORD_HEADER_LEN + 1 + 3 + 3 > end - pos) { + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } wpa_printf(MSG_DEBUG, "TLSv1: Send Certificate"); rhdr = pos; @@ -186,7 +282,7 @@ static int tls_write_client_certificate(struct tlsv1_client *conn, pos += 3; cert = conn->cred ? conn->cred->cert : NULL; while (cert) { - if (pos + 3 + cert->cert_len > end) { + if (3 + cert->cert_len > (size_t) (end - pos)) { wpa_printf(MSG_DEBUG, "TLSv1: Not enough buffer space " "for Certificate (cert_len=%lu left=%lu)", (unsigned long) cert->cert_len, @@ -237,7 +333,7 @@ static int tls_write_client_certificate(struct tlsv1_client *conn, } -static int tlsv1_key_x_anon_dh(struct tlsv1_client *conn, u8 **pos, u8 *end) +static int tlsv1_key_x_dh(struct tlsv1_client *conn, u8 **pos, u8 *end) { /* ClientDiffieHellmanPublic */ u8 *csecret, *csecret_start, *dh_yc, *shared; @@ -283,12 +379,12 @@ static int tlsv1_key_x_anon_dh(struct tlsv1_client *conn, u8 **pos, u8 *end) os_free(csecret); return -1; } - if(crypto_mod_exp(conn->dh_g, conn->dh_g_len, - csecret_start, csecret_len, - conn->dh_p, conn->dh_p_len, - dh_yc, &dh_yc_len)) { - tls_alert(conn, TLS_ALERT_LEVEL_FATAL, - TLS_ALERT_INTERNAL_ERROR); + if (crypto_mod_exp(conn->dh_g, conn->dh_g_len, + csecret_start, csecret_len, + conn->dh_p, conn->dh_p_len, + dh_yc, &dh_yc_len)) { + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); os_free(csecret); os_free(dh_yc); return -1; @@ -297,9 +393,16 @@ static int tlsv1_key_x_anon_dh(struct tlsv1_client *conn, u8 **pos, u8 *end) wpa_hexdump(MSG_DEBUG, "TLSv1: DH Yc (client's public value)", dh_yc, dh_yc_len); + if (end - *pos < 2) { + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + os_free(csecret); + os_free(dh_yc); + return -1; + } WPA_PUT_BE16(*pos, dh_yc_len); *pos += 2; - if (*pos + dh_yc_len > end) { + if (dh_yc_len > (size_t) (end - *pos)) { wpa_printf(MSG_DEBUG, "TLSv1: Not enough room in the " "message buffer for Yc"); tls_alert(conn, TLS_ALERT_LEVEL_FATAL, @@ -324,12 +427,12 @@ static int tlsv1_key_x_anon_dh(struct tlsv1_client *conn, u8 **pos, u8 *end) } /* shared = Ys^csecret mod p */ - if(crypto_mod_exp(conn->dh_ys, conn->dh_ys_len, - csecret_start, csecret_len, - conn->dh_p, conn->dh_p_len, - shared, &shared_len)) { - tls_alert(conn, TLS_ALERT_LEVEL_FATAL, - TLS_ALERT_INTERNAL_ERROR); + if (crypto_mod_exp(conn->dh_ys, conn->dh_ys_len, + csecret_start, csecret_len, + conn->dh_p, conn->dh_p_len, + shared, &shared_len)) { + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); os_free(csecret); os_free(shared); return -1; @@ -359,7 +462,7 @@ static int tlsv1_key_x_rsa(struct tlsv1_client *conn, u8 **pos, u8 *end) size_t clen; int res; - if (tls_derive_pre_master_secret(pre_master_secret) < 0 || + if (tls_derive_pre_master_secret(conn, pre_master_secret) < 0 || tls_derive_keys(conn, pre_master_secret, TLS_PRE_MASTER_SECRET_LEN)) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive keys"); @@ -431,8 +534,8 @@ static int tls_write_client_key_exchange(struct tlsv1_client *conn, hs_length = pos; pos += 3; /* body - ClientKeyExchange */ - if (keyx == TLS_KEY_X_DH_anon) { - if (tlsv1_key_x_anon_dh(conn, &pos, end) < 0) + if (keyx == TLS_KEY_X_DH_anon || keyx == TLS_KEY_X_DHE_RSA) { + if (tlsv1_key_x_dh(conn, &pos, end) < 0) return -1; } else { if (tlsv1_key_x_rsa(conn, &pos, end) < 0) @@ -464,7 +567,6 @@ static int tls_write_client_certificate_verify(struct tlsv1_client *conn, u8 *pos, *rhdr, *hs_start, *hs_length, *signed_start; size_t rlen, hlen, clen; u8 hash[100], *hpos; - enum { SIGN_ALG_RSA, SIGN_ALG_DSA } alg = SIGN_ALG_RSA; pos = *msgpos; @@ -511,7 +613,7 @@ static int tls_write_client_certificate_verify(struct tlsv1_client *conn, 0) { conn->verify.sha256_cert = NULL; tls_alert(conn, TLS_ALERT_LEVEL_FATAL, - TLS_ALERT_INTERNAL_ERROR); + TLS_ALERT_INTERNAL_ERROR); return -1; } conn->verify.sha256_cert = NULL; @@ -537,21 +639,17 @@ static int tls_write_client_certificate_verify(struct tlsv1_client *conn, } else { #endif /* CONFIG_TLSV12 */ - if (alg == SIGN_ALG_RSA) { - hlen = MD5_MAC_LEN; - if (conn->verify.md5_cert == NULL || - crypto_hash_finish(conn->verify.md5_cert, hpos, &hlen) < 0) - { - tls_alert(conn, TLS_ALERT_LEVEL_FATAL, - TLS_ALERT_INTERNAL_ERROR); - conn->verify.md5_cert = NULL; - crypto_hash_finish(conn->verify.sha1_cert, NULL, NULL); - conn->verify.sha1_cert = NULL; - return -1; - } - hpos += MD5_MAC_LEN; - } else - crypto_hash_finish(conn->verify.md5_cert, NULL, NULL); + hlen = MD5_MAC_LEN; + if (conn->verify.md5_cert == NULL || + crypto_hash_finish(conn->verify.md5_cert, hpos, &hlen) < 0) { + tls_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + conn->verify.md5_cert = NULL; + crypto_hash_finish(conn->verify.sha1_cert, NULL, NULL); + conn->verify.sha1_cert = NULL; + return -1; + } + hpos += MD5_MAC_LEN; conn->verify.md5_cert = NULL; hlen = SHA1_MAC_LEN; @@ -564,8 +662,7 @@ static int tls_write_client_certificate_verify(struct tlsv1_client *conn, } conn->verify.sha1_cert = NULL; - if (alg == SIGN_ALG_RSA) - hlen += MD5_MAC_LEN; + hlen += MD5_MAC_LEN; #ifdef CONFIG_TLSV12 } @@ -688,7 +785,7 @@ static int tls_write_client_finished(struct tlsv1_client *conn, crypto_hash_finish(conn->verify.sha256_client, hash, &hlen) < 0) { tls_alert(conn, TLS_ALERT_LEVEL_FATAL, - TLS_ALERT_INTERNAL_ERROR); + TLS_ALERT_INTERNAL_ERROR); conn->verify.sha256_client = NULL; return -1; } @@ -796,6 +893,7 @@ static u8 * tls_send_client_key_exchange(struct tlsv1_client *conn, } *out_len = pos - msg; + conn->state = SERVER_CHANGE_CIPHER_SPEC; return msg; @@ -826,7 +924,8 @@ static u8 * tls_send_change_cipher_spec(struct tlsv1_client *conn, wpa_printf(MSG_DEBUG, "TLSv1: Session resumption completed " "successfully"); - + if (!conn->session_resumed && conn->use_session_ticket) + conn->session_resumed = 1; conn->state = ESTABLISHED; return msg; diff --git a/components/wpa_supplicant/src/tls/tlsv1_common.c b/components/wpa_supplicant/src/tls/tlsv1_common.c index 3225a11c17..e178915a45 100644 --- a/components/wpa_supplicant/src/tls/tlsv1_common.c +++ b/components/wpa_supplicant/src/tls/tlsv1_common.c @@ -1,21 +1,19 @@ /* * TLSv1 common routines - * Copyright (c) 2006-2011, Jouni Malinen + * Copyright (c) 2006-2014, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ -#include "utils/includes.h" +#include "includes.h" -#include "utils/common.h" +#include "common.h" #include "crypto/md5.h" #include "crypto/sha1.h" #include "crypto/sha256.h" -#include "tls/tls.h" -#include "tls/x509v3.h" -#include "tls/tlsv1_common.h" -#include "eap_peer/eap_i.h" +#include "x509v3.h" +#include "tlsv1_common.h" /* @@ -32,44 +30,47 @@ static const struct tls_cipher_suite tls_cipher_suites[] = { TLS_HASH_MD5 }, { TLS_RSA_WITH_RC4_128_SHA, TLS_KEY_X_RSA, TLS_CIPHER_RC4_128, TLS_HASH_SHA }, -#ifdef CONFIG_DES { TLS_RSA_WITH_DES_CBC_SHA, TLS_KEY_X_RSA, TLS_CIPHER_DES_CBC, TLS_HASH_SHA }, -#endif -#ifdef CONFIG_DES3 { TLS_RSA_WITH_3DES_EDE_CBC_SHA, TLS_KEY_X_RSA, TLS_CIPHER_3DES_EDE_CBC, TLS_HASH_SHA }, -#endif + { TLS_DHE_RSA_WITH_DES_CBC_SHA, TLS_KEY_X_DHE_RSA, TLS_CIPHER_DES_CBC, + TLS_HASH_SHA}, + { TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, TLS_KEY_X_DHE_RSA, + TLS_CIPHER_3DES_EDE_CBC, TLS_HASH_SHA }, { TLS_DH_anon_WITH_RC4_128_MD5, TLS_KEY_X_DH_anon, TLS_CIPHER_RC4_128, TLS_HASH_MD5 }, -#ifdef CONFIG_DES { TLS_DH_anon_WITH_DES_CBC_SHA, TLS_KEY_X_DH_anon, TLS_CIPHER_DES_CBC, TLS_HASH_SHA }, -#endif -#ifdef CONFIG_DES3 { TLS_DH_anon_WITH_3DES_EDE_CBC_SHA, TLS_KEY_X_DH_anon, TLS_CIPHER_3DES_EDE_CBC, TLS_HASH_SHA }, -#endif { TLS_RSA_WITH_AES_128_CBC_SHA, TLS_KEY_X_RSA, TLS_CIPHER_AES_128_CBC, TLS_HASH_SHA }, + { TLS_DHE_RSA_WITH_AES_128_CBC_SHA, TLS_KEY_X_DHE_RSA, + TLS_CIPHER_AES_128_CBC, TLS_HASH_SHA }, { TLS_DH_anon_WITH_AES_128_CBC_SHA, TLS_KEY_X_DH_anon, TLS_CIPHER_AES_128_CBC, TLS_HASH_SHA }, { TLS_RSA_WITH_AES_256_CBC_SHA, TLS_KEY_X_RSA, TLS_CIPHER_AES_256_CBC, TLS_HASH_SHA }, + { TLS_DHE_RSA_WITH_AES_256_CBC_SHA, TLS_KEY_X_DHE_RSA, + TLS_CIPHER_AES_256_CBC, TLS_HASH_SHA }, { TLS_DH_anon_WITH_AES_256_CBC_SHA, TLS_KEY_X_DH_anon, TLS_CIPHER_AES_256_CBC, TLS_HASH_SHA }, { TLS_RSA_WITH_AES_128_CBC_SHA256, TLS_KEY_X_RSA, TLS_CIPHER_AES_128_CBC, TLS_HASH_SHA256 }, { TLS_RSA_WITH_AES_256_CBC_SHA256, TLS_KEY_X_RSA, TLS_CIPHER_AES_256_CBC, TLS_HASH_SHA256 }, + { TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, TLS_KEY_X_DHE_RSA, + TLS_CIPHER_AES_128_CBC, TLS_HASH_SHA256 }, + { TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, TLS_KEY_X_DHE_RSA, + TLS_CIPHER_AES_256_CBC, TLS_HASH_SHA256 }, { TLS_DH_anon_WITH_AES_128_CBC_SHA256, TLS_KEY_X_DH_anon, TLS_CIPHER_AES_128_CBC, TLS_HASH_SHA256 }, { TLS_DH_anon_WITH_AES_256_CBC_SHA256, TLS_KEY_X_DH_anon, TLS_CIPHER_AES_256_CBC, TLS_HASH_SHA256 } }; -#define NUM_ELEMS(a) (sizeof(a) / sizeof((a)[0])) -#define NUM_TLS_CIPHER_SUITES NUM_ELEMS(tls_cipher_suites) +#define NUM_TLS_CIPHER_SUITES ARRAY_SIZE(tls_cipher_suites) static const struct tls_cipher_data tls_ciphers[] = { @@ -83,23 +84,19 @@ static const struct tls_cipher_data tls_ciphers[] = { CRYPTO_CIPHER_ALG_RC4 }, { TLS_CIPHER_RC4_128, TLS_CIPHER_STREAM, 16, 16, 0, CRYPTO_CIPHER_ALG_RC4 }, -#ifdef CONFIG_DES { TLS_CIPHER_DES40_CBC, TLS_CIPHER_BLOCK, 5, 8, 8, CRYPTO_CIPHER_ALG_DES }, { TLS_CIPHER_DES_CBC, TLS_CIPHER_BLOCK, 8, 8, 8, CRYPTO_CIPHER_ALG_DES }, -#endif -#ifdef CONFIG_DES3 { TLS_CIPHER_3DES_EDE_CBC, TLS_CIPHER_BLOCK, 24, 24, 8, CRYPTO_CIPHER_ALG_3DES }, -#endif { TLS_CIPHER_AES_128_CBC, TLS_CIPHER_BLOCK, 16, 16, 16, CRYPTO_CIPHER_ALG_AES }, { TLS_CIPHER_AES_256_CBC, TLS_CIPHER_BLOCK, 32, 32, 16, CRYPTO_CIPHER_ALG_AES } }; -#define NUM_TLS_CIPHER_DATA NUM_ELEMS(tls_ciphers) +#define NUM_TLS_CIPHER_DATA ARRAY_SIZE(tls_ciphers) /** @@ -222,12 +219,13 @@ int tls_verify_hash_init(struct tls_verify_hash *verify) return -1; } #ifdef CONFIG_TLSV12 - verify->sha256_client = crypto_hash_init(CRYPTO_HASH_ALG_SHA256, NULL, 0); - verify->sha256_server = crypto_hash_init(CRYPTO_HASH_ALG_SHA256, NULL, 0); - verify->sha256_cert = crypto_hash_init(CRYPTO_HASH_ALG_SHA256, NULL, 0); - - if (verify->sha256_client == NULL || - verify->sha256_server == NULL || + verify->sha256_client = crypto_hash_init(CRYPTO_HASH_ALG_SHA256, NULL, + 0); + verify->sha256_server = crypto_hash_init(CRYPTO_HASH_ALG_SHA256, NULL, + 0); + verify->sha256_cert = crypto_hash_init(CRYPTO_HASH_ALG_SHA256, NULL, + 0); + if (verify->sha256_client == NULL || verify->sha256_server == NULL || verify->sha256_cert == NULL) { tls_verify_hash_free(verify); return -1; @@ -254,7 +252,7 @@ void tls_verify_hash_add(struct tls_verify_hash *verify, const u8 *buf, } #ifdef CONFIG_TLSV12 if (verify->sha256_client) - crypto_hash_update(verify->sha256_client, buf, len); + crypto_hash_update(verify->sha256_client, buf, len); if (verify->sha256_server) crypto_hash_update(verify->sha256_server, buf, len); if (verify->sha256_cert) @@ -352,6 +350,14 @@ int tlsv12_key_x_server_params_hash(u16 tls_version, u8 hash_alg, alg = CRYPTO_HASH_ALG_SHA256; hlen = SHA256_MAC_LEN; break; + case TLS_HASH_ALG_SHA384: + alg = CRYPTO_HASH_ALG_SHA384; + hlen = 48; + break; + case TLS_HASH_ALG_SHA512: + alg = CRYPTO_HASH_ALG_SHA512; + hlen = 64; + break; default: return -1; } diff --git a/components/wpa_supplicant/src/tls/tlsv1_common.h b/components/wpa_supplicant/src/tls/tlsv1_common.h index 75a6274d3d..e30b15a030 100644 --- a/components/wpa_supplicant/src/tls/tlsv1_common.h +++ b/components/wpa_supplicant/src/tls/tlsv1_common.h @@ -1,6 +1,6 @@ /* * TLSv1 common definitions - * Copyright (c) 2006-2011, Jouni Malinen + * Copyright (c) 2006-2014, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -170,6 +170,7 @@ enum { #define TLS_EXT_TRUNCATED_HMAC 4 /* RFC 4366 */ #define TLS_EXT_STATUS_REQUEST 5 /* RFC 4366 */ #define TLS_EXT_SIGNATURE_ALGORITHMS 13 /* RFC 5246 */ +#define TLS_EXT_STATUS_REQUEST_V2 17 /* RFC 6961 */ #define TLS_EXT_SESSION_TICKET 35 /* RFC 4507 */ #define TLS_EXT_PAC_OPAQUE TLS_EXT_SESSION_TICKET /* EAP-FAST terminology */ diff --git a/components/wpa_supplicant/src/tls/tlsv1_cred.c b/components/wpa_supplicant/src/tls/tlsv1_cred.c index d359a0d8d1..1310f4e10e 100644 --- a/components/wpa_supplicant/src/tls/tlsv1_cred.c +++ b/components/wpa_supplicant/src/tls/tlsv1_cred.c @@ -1,23 +1,27 @@ /* * TLSv1 credentials - * Copyright (c) 2006-2009, Jouni Malinen + * Copyright (c) 2006-2015, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ -#include "utils/includes.h" +#include "includes.h" -#include "utils/common.h" -#include "utils/base64.h" +#include "common.h" +#include "base64.h" #include "crypto/crypto.h" -#include "tls/x509v3.h" -#include "tls/tlsv1_cred.h" +#include "crypto/sha1.h" +#include "pkcs5.h" +#include "pkcs8.h" +#include "x509v3.h" +#include "tlsv1_cred.h" + struct tlsv1_credentials * tlsv1_cred_alloc(void) { struct tlsv1_credentials *cred; - cred = (struct tlsv1_credentials *)os_zalloc(sizeof(*cred)); + cred = os_zalloc(sizeof(*cred)); return cred; } @@ -32,6 +36,8 @@ void tlsv1_cred_free(struct tlsv1_credentials *cred) crypto_private_key_free(cred->key); os_free(cred->dh_p); os_free(cred->dh_g); + os_free(cred->ocsp_stapling_response); + os_free(cred->ocsp_stapling_response_multi); os_free(cred); } @@ -124,7 +130,7 @@ static int tlsv1_add_cert(struct x509_certificate **chain, return -1; } - der = (unsigned char *)base64_decode((const char *)pos, end - pos, &der_len); + der = base64_decode((const char *) pos, end - pos, &der_len); if (der == NULL) { wpa_printf(MSG_INFO, "TLSv1: Could not decode PEM " "certificate"); @@ -156,10 +162,11 @@ static int tlsv1_set_cert_chain(struct x509_certificate **chain, return tlsv1_add_cert(chain, cert_blob, cert_blob_len); if (cert) { - u8 *buf = NULL; - size_t len = 0; + u8 *buf; + size_t len; int ret; + buf = (u8 *) os_readfile(cert, &len); if (buf == NULL) { wpa_printf(MSG_INFO, "TLSv1: Failed to read '%s'", cert); @@ -188,6 +195,43 @@ int tlsv1_set_ca_cert(struct tlsv1_credentials *cred, const char *cert, const u8 *cert_blob, size_t cert_blob_len, const char *path) { + if (cert && os_strncmp(cert, "hash://", 7) == 0) { + const char *pos = cert + 7; + if (os_strncmp(pos, "server/sha256/", 14) != 0) { + wpa_printf(MSG_DEBUG, + "TLSv1: Unsupported ca_cert hash value '%s'", + cert); + return -1; + } + pos += 14; + if (os_strlen(pos) != 32 * 2) { + wpa_printf(MSG_DEBUG, + "TLSv1: Unexpected SHA256 hash length in ca_cert '%s'", + cert); + return -1; + } + if (hexstr2bin(pos, cred->srv_cert_hash, 32) < 0) { + wpa_printf(MSG_DEBUG, + "TLSv1: Invalid SHA256 hash value in ca_cert '%s'", + cert); + return -1; + } + cred->server_cert_only = 1; + cred->ca_cert_verify = 0; + wpa_printf(MSG_DEBUG, + "TLSv1: Checking only server certificate match"); + return 0; + } + + if (cert && os_strncmp(cert, "probe://", 8) == 0) { + cred->cert_probe = 1; + cred->ca_cert_verify = 0; + wpa_printf(MSG_DEBUG, "TLSv1: Only probe server certificate"); + return 0; + } + + cred->ca_cert_verify = cert || cert_blob || path; + if (tlsv1_set_cert_chain(&cred->trusted_certs, cert, cert_blob, cert_blob_len) < 0) return -1; @@ -249,7 +293,7 @@ static struct crypto_private_key * tlsv1_set_key_pem(const u8 *key, size_t len) } } - der = (unsigned char *)base64_decode((const char *)pos, end - pos, &der_len); + der = base64_decode((const char *) pos, end - pos, &der_len); if (!der) return NULL; pkey = crypto_private_key_import(der, der_len, NULL); @@ -277,7 +321,7 @@ static struct crypto_private_key * tlsv1_set_key_enc_pem(const u8 *key, if (!end) return NULL; - der = (unsigned char *)base64_decode((const char *)pos, end - pos, &der_len); + der = base64_decode((const char *) pos, end - pos, &der_len); if (!der) return NULL; pkey = crypto_private_key_import(der, der_len, passwd); @@ -286,6 +330,679 @@ static struct crypto_private_key * tlsv1_set_key_enc_pem(const u8 *key, } +#ifdef PKCS12_FUNCS + +static int oid_is_rsadsi(struct asn1_oid *oid) +{ + return oid->len >= 4 && + oid->oid[0] == 1 /* iso */ && + oid->oid[1] == 2 /* member-body */ && + oid->oid[2] == 840 /* us */ && + oid->oid[3] == 113549 /* rsadsi */; +} + + +static int pkcs12_is_bagtype_oid(struct asn1_oid *oid, unsigned long type) +{ + return oid->len == 9 && + oid_is_rsadsi(oid) && + oid->oid[4] == 1 /* pkcs */ && + oid->oid[5] == 12 /* pkcs-12 */ && + oid->oid[6] == 10 && + oid->oid[7] == 1 /* bagtypes */ && + oid->oid[8] == type; +} + + +static int is_oid_pkcs7(struct asn1_oid *oid) +{ + return oid->len == 7 && + oid->oid[0] == 1 /* iso */ && + oid->oid[1] == 2 /* member-body */ && + oid->oid[2] == 840 /* us */ && + oid->oid[3] == 113549 /* rsadsi */ && + oid->oid[4] == 1 /* pkcs */ && + oid->oid[5] == 7 /* pkcs-7 */; +} + + +static int is_oid_pkcs7_data(struct asn1_oid *oid) +{ + return is_oid_pkcs7(oid) && oid->oid[6] == 1 /* data */; +} + + +static int is_oid_pkcs7_enc_data(struct asn1_oid *oid) +{ + return is_oid_pkcs7(oid) && oid->oid[6] == 6 /* encryptedData */; +} + + +static int is_oid_pkcs9(struct asn1_oid *oid) +{ + return oid->len >= 6 && + oid->oid[0] == 1 /* iso */ && + oid->oid[1] == 2 /* member-body */ && + oid->oid[2] == 840 /* us */ && + oid->oid[3] == 113549 /* rsadsi */ && + oid->oid[4] == 1 /* pkcs */ && + oid->oid[5] == 9 /* pkcs-9 */; +} + + +static int is_oid_pkcs9_friendly_name(struct asn1_oid *oid) +{ + return oid->len == 7 && is_oid_pkcs9(oid) && + oid->oid[6] == 20; +} + + +static int is_oid_pkcs9_local_key_id(struct asn1_oid *oid) +{ + return oid->len == 7 && is_oid_pkcs9(oid) && + oid->oid[6] == 21; +} + + +static int is_oid_pkcs9_x509_cert(struct asn1_oid *oid) +{ + return oid->len == 8 && is_oid_pkcs9(oid) && + oid->oid[6] == 22 /* certTypes */ && + oid->oid[7] == 1 /* x509Certificate */; +} + + +static int pkcs12_keybag(struct tlsv1_credentials *cred, + const u8 *buf, size_t len) +{ + /* TODO */ + return 0; +} + + +static int pkcs12_pkcs8_keybag(struct tlsv1_credentials *cred, + const u8 *buf, size_t len, + const char *passwd) +{ + struct crypto_private_key *key; + + /* PKCS8ShroudedKeyBag ::= EncryptedPrivateKeyInfo */ + key = pkcs8_enc_key_import(buf, len, passwd); + if (!key) + return -1; + + wpa_printf(MSG_DEBUG, + "PKCS #12: Successfully decrypted PKCS8ShroudedKeyBag"); + crypto_private_key_free(cred->key); + cred->key = key; + + return 0; +} + + +static int pkcs12_certbag(struct tlsv1_credentials *cred, + const u8 *buf, size_t len) +{ + struct asn1_hdr hdr; + struct asn1_oid oid; + char obuf[80]; + const u8 *pos, *end; + + /* + * CertBag ::= SEQUENCE { + * certId BAG-TYPE.&id ({CertTypes}), + * certValue [0] EXPLICIT BAG-TYPE.&Type ({CertTypes}{@certId}) + * } + */ + + if (asn1_get_next(buf, len, &hdr) < 0 || !asn1_is_sequence(&hdr)) { + asn1_unexpected(&hdr, "PKCS #12: Expected SEQUENCE (CertBag)"); + return -1; + } + + pos = hdr.payload; + end = hdr.payload + hdr.length; + + if (asn1_get_oid(pos, end - pos, &oid, &pos)) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Failed to parse OID (certId)"); + return -1; + } + + asn1_oid_to_str(&oid, obuf, sizeof(obuf)); + wpa_printf(MSG_DEBUG, "PKCS #12: certId %s", obuf); + + if (!is_oid_pkcs9_x509_cert(&oid)) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Ignored unsupported certificate type (certId %s)", + obuf); + } + + if (asn1_get_next(pos, end - pos, &hdr) < 0 || !hdr.constructed || + !asn1_is_cs_tag(&hdr, 0)) { + asn1_unexpected(&hdr, + "PKCS #12: Expected [0] EXPLICIT (certValue)"); + return -1; + } + + if (asn1_get_next(hdr.payload, hdr.length, &hdr) < 0 || + !asn1_is_octetstring(&hdr)) { + asn1_unexpected(&hdr, + "PKCS #12: Expected OCTET STRING (x509Certificate)"); + return -1; + } + + wpa_hexdump(MSG_DEBUG, "PKCS #12: x509Certificate", + hdr.payload, hdr.length); + if (cred->cert) { + struct x509_certificate *cert; + + wpa_printf(MSG_DEBUG, "PKCS #12: Ignore extra certificate"); + cert = x509_certificate_parse(hdr.payload, hdr.length); + if (!cert) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Failed to parse x509Certificate"); + return 0; + } + x509_certificate_chain_free(cert); + + return 0; + } + return tlsv1_set_cert(cred, NULL, hdr.payload, hdr.length); +} + + +static int pkcs12_parse_attr_friendly_name(const u8 *pos, const u8 *end) +{ + struct asn1_hdr hdr; + + /* + * RFC 2985, 5.5.1: + * friendlyName ATTRIBUTE ::= { + * WITH SYNTAX BMPString (SIZE(1..pkcs-9-ub-friendlyName)) + * EQUALITY MATCHING RULE caseIgnoreMatch + * SINGLE VALUE TRUE + * ID pkcs-9-at-friendlyName + * } + */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + !asn1_is_bmpstring(&hdr)) { + asn1_unexpected(&hdr, + "PKCS #12: Expected BMPSTRING (friendlyName)"); + return 0; + } + wpa_hexdump_ascii(MSG_DEBUG, "PKCS #12: friendlyName", + hdr.payload, hdr.length); + return 0; +} + + +static int pkcs12_parse_attr_local_key_id(const u8 *pos, const u8 *end) +{ + struct asn1_hdr hdr; + + /* + * RFC 2985, 5.5.2: + * localKeyId ATTRIBUTE ::= { + * WITH SYNTAX OCTET STRING + * EQUALITY MATCHING RULE octetStringMatch + * SINGLE VALUE TRUE + * ID pkcs-9-at-localKeyId + * } + */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + !asn1_is_octetstring(&hdr)) { + asn1_unexpected(&hdr, + "PKCS #12: Expected OCTET STRING (localKeyID)"); + return -1; + } + wpa_hexdump_key(MSG_DEBUG, "PKCS #12: localKeyID", + hdr.payload, hdr.length); + return 0; +} + + +static int pkcs12_parse_attr(const u8 *pos, size_t len) +{ + const u8 *end = pos + len; + struct asn1_hdr hdr; + struct asn1_oid a_oid; + char obuf[80]; + + /* + * PKCS12Attribute ::= SEQUENCE { + * attrId ATTRIBUTE.&id ({PKCS12AttrSet}), + * attrValues SET OF ATTRIBUTE.&Type ({PKCS12AttrSet}{@attrId}) + * } + */ + + if (asn1_get_oid(pos, end - pos, &a_oid, &pos)) { + wpa_printf(MSG_DEBUG, "PKCS #12: Failed to parse OID (attrId)"); + return -1; + } + + asn1_oid_to_str(&a_oid, obuf, sizeof(obuf)); + wpa_printf(MSG_DEBUG, "PKCS #12: attrId %s", obuf); + + if (asn1_get_next(pos, end - pos, &hdr) < 0 || !asn1_is_set(&hdr)) { + asn1_unexpected(&hdr, "PKCS #12: Expected SET (attrValues)"); + return -1; + } + wpa_hexdump_key(MSG_MSGDUMP, "PKCS #12: attrValues", + hdr.payload, hdr.length); + pos = hdr.payload; + end = hdr.payload + hdr.length; + + if (is_oid_pkcs9_friendly_name(&a_oid)) + return pkcs12_parse_attr_friendly_name(pos, end); + if (is_oid_pkcs9_local_key_id(&a_oid)) + return pkcs12_parse_attr_local_key_id(pos, end); + + wpa_printf(MSG_DEBUG, "PKCS #12: Ignore unknown attribute"); + return 0; +} + + +static int pkcs12_safebag(struct tlsv1_credentials *cred, + const u8 *buf, size_t len, const char *passwd) +{ + struct asn1_hdr hdr; + struct asn1_oid oid; + char obuf[80]; + const u8 *pos = buf, *end = buf + len; + const u8 *value; + size_t value_len; + + wpa_hexdump_key(MSG_MSGDUMP, "PKCS #12: SafeBag", buf, len); + + /* BAG-TYPE ::= TYPE-IDENTIFIER */ + if (asn1_get_oid(pos, end - pos, &oid, &pos)) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Failed to parse OID (BAG-TYPE)"); + return -1; + } + + asn1_oid_to_str(&oid, obuf, sizeof(obuf)); + wpa_printf(MSG_DEBUG, "PKCS #12: BAG-TYPE %s", obuf); + + if (asn1_get_next(pos, end - pos, &hdr) < 0 || !hdr.constructed || + !asn1_is_cs_tag(&hdr, 0)) { + asn1_unexpected(&hdr, + "PKCS #12: Expected [0] EXPLICIT (bagValue)"); + return 0; + } + value = hdr.payload; + value_len = hdr.length; + wpa_hexdump_key(MSG_MSGDUMP, "PKCS #12: bagValue", value, value_len); + pos = hdr.payload + hdr.length; + + if (pos < end) { + /* bagAttributes SET OF PKCS12Attribute OPTIONAL */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + !asn1_is_set(&hdr)) { + asn1_unexpected(&hdr, + "PKCS #12: Expected SET (bagAttributes)"); + return -1; + } + wpa_hexdump_key(MSG_MSGDUMP, "PKCS #12: bagAttributes", + hdr.payload, hdr.length); + + pos = hdr.payload; + end = hdr.payload + hdr.length; + while (pos < end) { + /* PKCS12Attribute ::= SEQUENCE */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + !asn1_is_sequence(&hdr)) { + asn1_unexpected(&hdr, + "PKCS #12: Expected SEQUENCE (PKCS12Attribute)"); + return -1; + } + if (pkcs12_parse_attr(hdr.payload, hdr.length) < 0) + return -1; + pos = hdr.payload + hdr.length; + } + } + + if (pkcs12_is_bagtype_oid(&oid, 1)) + return pkcs12_keybag(cred, value, value_len); + if (pkcs12_is_bagtype_oid(&oid, 2)) + return pkcs12_pkcs8_keybag(cred, value, value_len, passwd); + if (pkcs12_is_bagtype_oid(&oid, 3)) + return pkcs12_certbag(cred, value, value_len); + + wpa_printf(MSG_DEBUG, "PKCS #12: Ignore unsupported BAG-TYPE"); + return 0; +} + + +static int pkcs12_safecontents(struct tlsv1_credentials *cred, + const u8 *buf, size_t len, + const char *passwd) +{ + struct asn1_hdr hdr; + const u8 *pos, *end; + + /* SafeContents ::= SEQUENCE OF SafeBag */ + if (asn1_get_next(buf, len, &hdr) < 0 || !asn1_is_sequence(&hdr)) { + asn1_unexpected(&hdr, + "PKCS #12: Expected SEQUENCE (SafeContents)"); + return -1; + } + pos = hdr.payload; + end = hdr.payload + hdr.length; + + /* + * SafeBag ::= SEQUENCE { + * bagId BAG-TYPE.&id ({PKCS12BagSet}) + * bagValue [0] EXPLICIT BAG-TYPE.&Type({PKCS12BagSet}{@bagId}), + * bagAttributes SET OF PKCS12Attribute OPTIONAL + * } + */ + + while (pos < end) { + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + !asn1_is_sequence(&hdr)) { + asn1_unexpected(&hdr, + "PKCS #12: Expected SEQUENCE (SafeBag)"); + return -1; + } + if (pkcs12_safebag(cred, hdr.payload, hdr.length, passwd) < 0) + return -1; + pos = hdr.payload + hdr.length; + } + + return 0; +} + + +static int pkcs12_parse_content_data(struct tlsv1_credentials *cred, + const u8 *pos, const u8 *end, + const char *passwd) +{ + struct asn1_hdr hdr; + + /* Data ::= OCTET STRING */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + !asn1_is_octetstring(&hdr)) { + asn1_unexpected(&hdr, "PKCS #12: Expected OCTET STRING (Data)"); + return -1; + } + + wpa_hexdump(MSG_MSGDUMP, "PKCS #12: Data", hdr.payload, hdr.length); + + return pkcs12_safecontents(cred, hdr.payload, hdr.length, passwd); +} + + +static int pkcs12_parse_content_enc_data(struct tlsv1_credentials *cred, + const u8 *pos, const u8 *end, + const char *passwd) +{ + struct asn1_hdr hdr; + struct asn1_oid oid; + char buf[80]; + const u8 *enc_alg; + u8 *data; + size_t enc_alg_len, data_len; + int res = -1; + + /* + * EncryptedData ::= SEQUENCE { + * version Version, + * encryptedContentInfo EncryptedContentInfo } + */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + !asn1_is_sequence(&hdr)) { + asn1_unexpected(&hdr, + "PKCS #12: Expected SEQUENCE (EncryptedData)"); + return 0; + } + pos = hdr.payload; + + /* Version ::= INTEGER */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || !asn1_is_integer(&hdr)) { + asn1_unexpected(&hdr, + "PKCS #12: No INTEGER tag found for version"); + return -1; + } + if (hdr.length != 1 || hdr.payload[0] != 0) { + wpa_printf(MSG_DEBUG, "PKCS #12: Unrecognized PKCS #7 version"); + return -1; + } + pos = hdr.payload + hdr.length; + + wpa_hexdump(MSG_MSGDUMP, "PKCS #12: EncryptedContentInfo", + pos, end - pos); + + /* + * EncryptedContentInfo ::= SEQUENCE { + * contentType ContentType, + * contentEncryptionAlgorithm ContentEncryptionAlgorithmIdentifier, + * encryptedContent [0] IMPLICIT EncryptedContent OPTIONAL } + */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + !asn1_is_sequence(&hdr)) { + asn1_unexpected(&hdr, + "PKCS #12: Expected SEQUENCE (EncryptedContentInfo)"); + return -1; + } + + pos = hdr.payload; + end = pos + hdr.length; + + /* ContentType ::= OBJECT IDENTIFIER */ + if (asn1_get_oid(pos, end - pos, &oid, &pos)) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Could not find OBJECT IDENTIFIER (contentType)"); + return -1; + } + asn1_oid_to_str(&oid, buf, sizeof(buf)); + wpa_printf(MSG_DEBUG, "PKCS #12: EncryptedContentInfo::contentType %s", + buf); + + if (!is_oid_pkcs7_data(&oid)) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Unsupported EncryptedContentInfo::contentType %s", + buf); + return 0; + } + + /* ContentEncryptionAlgorithmIdentifier ::= AlgorithmIdentifier */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + !asn1_is_sequence(&hdr)) { + asn1_unexpected(&hdr, + "PKCS #12: Expected SEQUENCE (ContentEncryptionAlgorithmIdentifier)"); + return -1; + } + enc_alg = hdr.payload; + enc_alg_len = hdr.length; + pos = hdr.payload + hdr.length; + + if (asn1_get_next(pos, end - pos, &hdr) < 0 || hdr.constructed || + !asn1_is_cs_tag(&hdr, 0)) { + asn1_unexpected(&hdr, + "PKCS #12: Expected [0] IMPLICIT (encryptedContent)"); + return -1; + } + + /* EncryptedContent ::= OCTET STRING */ + data = pkcs5_decrypt(enc_alg, enc_alg_len, hdr.payload, hdr.length, + passwd, &data_len); + if (data) { + wpa_hexdump_key(MSG_MSGDUMP, + "PKCS #12: Decrypted encryptedContent", + data, data_len); + res = pkcs12_safecontents(cred, data, data_len, passwd); + os_free(data); + } + + return res; +} + + +static int pkcs12_parse_content(struct tlsv1_credentials *cred, + const u8 *buf, size_t len, + const char *passwd) +{ + const u8 *pos = buf; + const u8 *end = buf + len; + struct asn1_oid oid; + char txt[80]; + struct asn1_hdr hdr; + + wpa_hexdump(MSG_MSGDUMP, "PKCS #12: ContentInfo", buf, len); + + if (asn1_get_oid(pos, end - pos, &oid, &pos)) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Could not find OBJECT IDENTIFIER (contentType)"); + return 0; + } + + asn1_oid_to_str(&oid, txt, sizeof(txt)); + wpa_printf(MSG_DEBUG, "PKCS #12: contentType %s", txt); + + if (asn1_get_next(pos, end - pos, &hdr) < 0 || !hdr.constructed || + !asn1_is_cs_tag(&hdr, 0)) { + asn1_unexpected(&hdr, + "PKCS #12: Expected [0] EXPLICIT (content)"); + return 0; + } + pos = hdr.payload; + + if (is_oid_pkcs7_data(&oid)) + return pkcs12_parse_content_data(cred, pos, end, passwd); + if (is_oid_pkcs7_enc_data(&oid)) + return pkcs12_parse_content_enc_data(cred, pos, end, passwd); + + wpa_printf(MSG_DEBUG, "PKCS #12: Ignored unsupported contentType %s", + txt); + + return 0; +} + + +static int pkcs12_parse(struct tlsv1_credentials *cred, + const u8 *key, size_t len, const char *passwd) +{ + struct asn1_hdr hdr; + const u8 *pos, *end; + struct asn1_oid oid; + char buf[80]; + + /* + * PFX ::= SEQUENCE { + * version INTEGER {v3(3)}(v3,...), + * authSafe ContentInfo, + * macData MacData OPTIONAL + * } + */ + + if (asn1_get_next(key, len, &hdr) < 0 || !asn1_is_sequence(&hdr)) { + asn1_unexpected(&hdr, + "PKCS #12: Expected SEQUENCE (PFX); assume PKCS #12 not used"); + return -1; + } + + pos = hdr.payload; + end = pos + hdr.length; + + if (asn1_get_next(pos, end - pos, &hdr) < 0 || !asn1_is_integer(&hdr)) { + asn1_unexpected(&hdr, + "PKCS #12: No INTEGER tag found for version"); + return -1; + } + if (hdr.length != 1 || hdr.payload[0] != 3) { + wpa_printf(MSG_DEBUG, "PKCS #12: Unrecognized version"); + return -1; + } + pos = hdr.payload + hdr.length; + + /* + * ContentInfo ::= SEQUENCE { + * contentType ContentType, + * content [0] EXPLICIT ANY DEFINED BY contentType OPTIONAL } + */ + + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + !asn1_is_sequence(&hdr)) { + asn1_unexpected(&hdr, + "PKCS #12: Expected SEQUENCE (authSafe); assume PKCS #12 not used"); + return -1; + } + + pos = hdr.payload; + end = pos + hdr.length; + + /* ContentType ::= OBJECT IDENTIFIER */ + if (asn1_get_oid(pos, end - pos, &oid, &pos)) { + wpa_printf(MSG_DEBUG, + "PKCS #12: Could not find OBJECT IDENTIFIER (contentType); assume PKCS #12 not used"); + return -1; + } + asn1_oid_to_str(&oid, buf, sizeof(buf)); + wpa_printf(MSG_DEBUG, "PKCS #12: contentType %s", buf); + if (!is_oid_pkcs7_data(&oid)) { + wpa_printf(MSG_DEBUG, "PKCS #12: Unsupported contentType %s", + buf); + return -1; + } + + if (asn1_get_next(pos, end - pos, &hdr) < 0 || !hdr.constructed || + !asn1_is_cs_tag(&hdr, 0)) { + asn1_unexpected(&hdr, + "PKCS #12: Expected [0] EXPLICIT (content); assume PKCS #12 not used"); + return -1; + } + + pos = hdr.payload; + + /* Data ::= OCTET STRING */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + !asn1_is_octetstring(&hdr)) { + asn1_unexpected(&hdr, + "PKCS #12: Expected OCTET STRING (Data); assume PKCS #12 not used"); + return -1; + } + + /* + * AuthenticatedSafe ::= SEQUENCE OF ContentInfo + * -- Data if unencrypted + * -- EncryptedData if password-encrypted + * -- EnvelopedData if public key-encrypted + */ + wpa_hexdump(MSG_MSGDUMP, "PKCS #12: Data content", + hdr.payload, hdr.length); + + if (asn1_get_next(hdr.payload, hdr.length, &hdr) < 0 || + !asn1_is_sequence(&hdr)) { + asn1_unexpected(&hdr, + "PKCS #12: Expected SEQUENCE within Data content; assume PKCS #12 not used"); + return -1; + } + + pos = hdr.payload; + end = pos + hdr.length; + + while (end > pos) { + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + !asn1_is_sequence(&hdr)) { + asn1_unexpected(&hdr, + "PKCS #12: Expected SEQUENCE (ContentInfo); assume PKCS #12 not used"); + return -1; + } + if (pkcs12_parse_content(cred, hdr.payload, hdr.length, + passwd) < 0) + return -1; + + pos = hdr.payload + hdr.length; + } + + return 0; +} + +#endif /* PKCS12_FUNCS */ + + static int tlsv1_set_key(struct tlsv1_credentials *cred, const u8 *key, size_t len, const char *passwd) { @@ -294,6 +1011,10 @@ static int tlsv1_set_key(struct tlsv1_credentials *cred, cred->key = tlsv1_set_key_pem(key, len); if (cred->key == NULL) cred->key = tlsv1_set_key_enc_pem(key, len, passwd); +#ifdef PKCS12_FUNCS + if (!cred->key) + pkcs12_parse(cred, key, len, passwd); +#endif /* PKCS12_FUNCS */ if (cred->key == NULL) { wpa_printf(MSG_INFO, "TLSv1: Failed to parse private key"); return -1; @@ -327,10 +1048,11 @@ int tlsv1_set_private_key(struct tlsv1_credentials *cred, private_key_passwd); if (private_key) { - u8 *buf = NULL; - size_t len = 0; + u8 *buf; + size_t len; int ret; + buf = (u8 *) os_readfile(private_key, &len); if (buf == NULL) { wpa_printf(MSG_INFO, "TLSv1: Failed to read '%s'", private_key); @@ -363,24 +1085,17 @@ static int tlsv1_set_dhparams_der(struct tlsv1_credentials *cred, */ /* DHParamer ::= SEQUENCE */ - if (asn1_get_next(pos, len, &hdr) < 0 || - hdr.class != ASN1_CLASS_UNIVERSAL || - hdr.tag != ASN1_TAG_SEQUENCE) { - wpa_printf(MSG_DEBUG, "DH: DH parameters did not start with a " - "valid SEQUENCE - found class %d tag 0x%x", - hdr.class, hdr.tag); + if (asn1_get_next(pos, len, &hdr) < 0 || !asn1_is_sequence(&hdr)) { + asn1_unexpected(&hdr, + "DH: DH parameters did not start with a valid SEQUENCE"); return -1; } pos = hdr.payload; /* prime INTEGER */ - if (asn1_get_next(pos, end - pos, &hdr) < 0) - return -1; - - if (hdr.class != ASN1_CLASS_UNIVERSAL || - hdr.tag != ASN1_TAG_INTEGER) { - wpa_printf(MSG_DEBUG, "DH: No INTEGER tag found for p; " - "class=%d tag=0x%x", hdr.class, hdr.tag); + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + !asn1_is_integer(&hdr)) { + asn1_unexpected(&hdr, "DH: No INTEGER tag found for p"); return -1; } @@ -388,21 +1103,16 @@ static int tlsv1_set_dhparams_der(struct tlsv1_credentials *cred, if (hdr.length == 0) return -1; os_free(cred->dh_p); - cred->dh_p = os_malloc(hdr.length); + cred->dh_p = os_memdup(hdr.payload, hdr.length); if (cred->dh_p == NULL) return -1; - os_memcpy(cred->dh_p, hdr.payload, hdr.length); cred->dh_p_len = hdr.length; pos = hdr.payload + hdr.length; /* base INTEGER */ - if (asn1_get_next(pos, end - pos, &hdr) < 0) - return -1; - - if (hdr.class != ASN1_CLASS_UNIVERSAL || - hdr.tag != ASN1_TAG_INTEGER) { - wpa_printf(MSG_DEBUG, "DH: No INTEGER tag found for g; " - "class=%d tag=0x%x", hdr.class, hdr.tag); + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + !asn1_is_integer(&hdr)) { + asn1_unexpected(&hdr, "DH: No INTEGER tag found for g"); return -1; } @@ -410,10 +1120,9 @@ static int tlsv1_set_dhparams_der(struct tlsv1_credentials *cred, if (hdr.length == 0) return -1; os_free(cred->dh_g); - cred->dh_g = os_malloc(hdr.length); + cred->dh_g = os_memdup(hdr.payload, hdr.length); if (cred->dh_g == NULL) return -1; - os_memcpy(cred->dh_g, hdr.payload, hdr.length); cred->dh_g_len = hdr.length; return 0; @@ -449,7 +1158,7 @@ static int tlsv1_set_dhparams_blob(struct tlsv1_credentials *cred, return -1; } - der = (unsigned char *)base64_decode((const char *)pos, end - pos, &der_len); + der = base64_decode((const char *) pos, end - pos, &der_len); if (der == NULL) { wpa_printf(MSG_INFO, "TLSv1: Could not decode PEM dhparams"); return -1; @@ -483,10 +1192,11 @@ int tlsv1_set_dhparams(struct tlsv1_credentials *cred, const char *dh_file, return tlsv1_set_dhparams_blob(cred, dh_blob, dh_blob_len); if (dh_file) { - u8 *buf = NULL; - size_t len = 0; + u8 *buf; + size_t len; int ret; + buf = (u8 *) os_readfile(dh_file, &len); if (buf == NULL) { wpa_printf(MSG_INFO, "TLSv1: Failed to read '%s'", dh_file); diff --git a/components/wpa_supplicant/src/tls/tlsv1_cred.h b/components/wpa_supplicant/src/tls/tlsv1_cred.h index 68fbdc9230..716e93c390 100644 --- a/components/wpa_supplicant/src/tls/tlsv1_cred.h +++ b/components/wpa_supplicant/src/tls/tlsv1_cred.h @@ -14,11 +14,19 @@ struct tlsv1_credentials { struct x509_certificate *cert; struct crypto_private_key *key; + unsigned int cert_probe:1; + unsigned int ca_cert_verify:1; + unsigned int server_cert_only:1; + u8 srv_cert_hash[32]; + /* Diffie-Hellman parameters */ u8 *dh_p; /* prime */ size_t dh_p_len; u8 *dh_g; /* generator */ size_t dh_g_len; + + char *ocsp_stapling_response; + char *ocsp_stapling_response_multi; }; diff --git a/components/wpa_supplicant/src/tls/tlsv1_record.c b/components/wpa_supplicant/src/tls/tlsv1_record.c index a5b8b0810b..0c6897a8fc 100644 --- a/components/wpa_supplicant/src/tls/tlsv1_record.c +++ b/components/wpa_supplicant/src/tls/tlsv1_record.c @@ -6,16 +6,15 @@ * See README for more details. */ -#include "utils/includes.h" +#include "includes.h" -#include "utils/common.h" +#include "common.h" #include "crypto/md5.h" #include "crypto/sha1.h" #include "crypto/sha256.h" -#include "tls/tlsv1_common.h" -#include "tls/tlsv1_record.h" +#include "tlsv1_common.h" +#include "tlsv1_record.h" -#include "eap_peer/eap_i.h" /** * tlsv1_record_set_cipher_suite - TLS record layer: Set cipher suite @@ -83,13 +82,11 @@ int tlsv1_record_change_write_cipher(struct tlsv1_record_layer *rl) if (rl->write_cbc) { crypto_cipher_deinit(rl->write_cbc); rl->write_cbc = NULL; - } if (rl->cipher_alg != CRYPTO_CIPHER_NULL) { rl->write_cbc = crypto_cipher_init(rl->cipher_alg, - rl->write_iv, rl->write_key, - rl->key_material_len); - + rl->write_iv, rl->write_key, + rl->key_material_len); if (rl->write_cbc == NULL) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to initialize " "cipher"); @@ -112,19 +109,18 @@ int tlsv1_record_change_write_cipher(struct tlsv1_record_layer *rl) int tlsv1_record_change_read_cipher(struct tlsv1_record_layer *rl) { wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - New read cipher suite " - "0x%04x \n", rl->cipher_suite); + "0x%04x", rl->cipher_suite); rl->read_cipher_suite = rl->cipher_suite; os_memset(rl->read_seq_num, 0, TLS_SEQ_NUM_LEN); if (rl->read_cbc) { crypto_cipher_deinit(rl->read_cbc); - rl->read_cbc = NULL; + rl->read_cbc = NULL; } - if (rl->cipher_alg != CRYPTO_CIPHER_NULL) { rl->read_cbc = crypto_cipher_init(rl->cipher_alg, - rl->read_iv, rl->read_key, - rl->key_material_len); + rl->read_iv, rl->read_key, + rl->key_material_len); if (rl->read_cbc == NULL) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to initialize " "cipher"); @@ -156,7 +152,7 @@ int tlsv1_record_send(struct tlsv1_record_layer *rl, u8 content_type, u8 *buf, size_t *out_len) { u8 *pos, *ct_start, *length, *cpayload; - struct crypto_hash *hmac = NULL; + struct crypto_hash *hmac; size_t clen; int explicit_iv; @@ -208,7 +204,8 @@ int tlsv1_record_send(struct tlsv1_record_layer *rl, u8 content_type, u8 *buf, * TLSCompressed.version + TLSCompressed.length + * TLSCompressed.fragment */ - hmac = crypto_hash_init(rl->hash_alg, rl->write_mac_secret, rl->hash_size); + hmac = crypto_hash_init(rl->hash_alg, rl->write_mac_secret, + rl->hash_size); if (hmac == NULL) { wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Failed " "to initialize HMAC"); @@ -223,15 +220,14 @@ int tlsv1_record_send(struct tlsv1_record_layer *rl, u8 content_type, u8 *buf, wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Not " "enough room for MAC"); crypto_hash_finish(hmac, NULL, NULL); - return -1; } - if ((int)crypto_hash_finish(hmac, pos, &clen) < 0) { - wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Failed to calculate HMAC"); + if (crypto_hash_finish(hmac, pos, &clen) < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Failed " + "to calculate HMAC"); return -1; } - wpa_hexdump(MSG_MSGDUMP, "TLSv1: Record Layer - Write HMAC", pos, clen); pos += clen; @@ -250,8 +246,8 @@ int tlsv1_record_send(struct tlsv1_record_layer *rl, u8 content_type, u8 *buf, pos += pad + 1; } - if ((int)crypto_cipher_encrypt(rl->write_cbc, cpayload, - cpayload, pos - cpayload) < 0) + if (crypto_cipher_encrypt(rl->write_cbc, cpayload, + cpayload, pos - cpayload) < 0) return -1; } @@ -285,7 +281,7 @@ int tlsv1_record_receive(struct tlsv1_record_layer *rl, { size_t i, rlen, hlen; u8 padlen; - struct crypto_hash *hmac = NULL; + struct crypto_hash *hmac; u8 len[2], hash[100]; int force_mac_error = 0; u8 ct; @@ -358,12 +354,11 @@ int tlsv1_record_receive(struct tlsv1_record_layer *rl, if (rl->read_cipher_suite != TLS_NULL_WITH_NULL_NULL) { size_t plen; - if ((int)crypto_cipher_decrypt(rl->read_cbc, in_data, - out_data, in_len) < 0) { + if (crypto_cipher_decrypt(rl->read_cbc, in_data, + out_data, in_len) < 0) { *alert = TLS_ALERT_DECRYPTION_FAILED; return -1; } - plen = in_len; wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Record Layer - Decrypted " "data", out_data, plen); @@ -438,8 +433,8 @@ int tlsv1_record_receive(struct tlsv1_record_layer *rl, plen -= rl->hash_size; - hmac = crypto_hash_init(rl->hash_alg, rl->read_mac_secret, rl->hash_size); - + hmac = crypto_hash_init(rl->hash_alg, rl->read_mac_secret, + rl->hash_size); if (hmac == NULL) { wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Failed " "to initialize HMAC"); @@ -453,16 +448,15 @@ int tlsv1_record_receive(struct tlsv1_record_layer *rl, WPA_PUT_BE16(len, plen); crypto_hash_update(hmac, len, 2); crypto_hash_update(hmac, out_data, plen); - hlen = sizeof(hash); - if ((int)crypto_hash_finish(hmac, hash, &hlen) < 0) { - wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Failed to calculate HMAC"); + if (crypto_hash_finish(hmac, hash, &hlen) < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Failed " + "to calculate HMAC"); *alert = TLS_ALERT_INTERNAL_ERROR; return -1; } - if (hlen != rl->hash_size || - os_memcmp(hash, out_data + plen, hlen) != 0 || + os_memcmp_const(hash, out_data + plen, hlen) != 0 || force_mac_error) { wpa_printf(MSG_DEBUG, "TLSv1: Invalid HMAC value in " "received message (force_mac_error=%d)", diff --git a/components/wpa_supplicant/src/tls/tlsv1_server.c b/components/wpa_supplicant/src/tls/tlsv1_server.c index e6ef926191..12dcc859a2 100644 --- a/components/wpa_supplicant/src/tls/tlsv1_server.c +++ b/components/wpa_supplicant/src/tls/tlsv1_server.c @@ -1,26 +1,51 @@ /* * TLS v1.0/v1.1/v1.2 server (RFC 2246, RFC 4346, RFC 5246) - * Copyright (c) 2006-2011, Jouni Malinen + * Copyright (c) 2006-2019, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ -#include "utils/includes.h" +#include "includes.h" -#include "utils/common.h" +#include "common.h" #include "crypto/sha1.h" -#include "tls/tls.h" -#include "tls/tlsv1_common.h" -#include "tls/tlsv1_record.h" -#include "tls/tlsv1_server.h" -#include "tls/tlsv1_server_i.h" +#include "crypto/tls.h" +#include "tlsv1_common.h" +#include "tlsv1_record.h" +#include "tlsv1_server.h" +#include "tlsv1_server_i.h" /* TODO: * Support for a message fragmented across several records (RFC 2246, 6.2.1) */ +void tlsv1_server_log(struct tlsv1_server *conn, const char *fmt, ...) +{ + va_list ap; + char *buf; + int buflen; + + va_start(ap, fmt); + buflen = vsnprintf(NULL, 0, fmt, ap) + 1; + va_end(ap); + + buf = os_malloc(buflen); + if (buf == NULL) + return; + va_start(ap, fmt); + vsnprintf(buf, buflen, fmt, ap); + va_end(ap); + + wpa_printf(MSG_DEBUG, "TLSv1: %s", buf); + if (conn->log_cb) + conn->log_cb(conn->log_cb_ctx, buf); + + os_free(buf); +} + + void tlsv1_server_alert(struct tlsv1_server *conn, u8 level, u8 description) { conn->alert_level = level; @@ -139,7 +164,8 @@ u8 * tlsv1_server_handshake(struct tlsv1_server *conn, /* need more data */ wpa_printf(MSG_DEBUG, "TLSv1: Partial processing not " "yet supported"); - tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, alert); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); goto failed; } ct = pos[0]; @@ -179,6 +205,7 @@ failed: msg = tlsv1_server_send_alert(conn, conn->alert_level, conn->alert_description, out_len); + conn->write_alerts++; } return msg; @@ -250,8 +277,7 @@ int tlsv1_server_decrypt(struct tlsv1_server *conn, used = tlsv1_record_receive(&conn->rl, pos, in_end - pos, out_pos, &olen, &alert); if (used < 0) { - wpa_printf(MSG_DEBUG, "TLSv1: Record layer processing " - "failed"); + tlsv1_server_log(conn, "Record layer processing failed"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, alert); return -1; } @@ -265,14 +291,14 @@ int tlsv1_server_decrypt(struct tlsv1_server *conn, if (ct == TLS_CONTENT_TYPE_ALERT) { if (olen < 2) { - wpa_printf(MSG_DEBUG, "TLSv1: Alert " - "underflow"); + tlsv1_server_log(conn, "Alert underflow"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; } - wpa_printf(MSG_DEBUG, "TLSv1: Received alert %d:%d", - out_pos[0], out_pos[1]); + tlsv1_server_log(conn, "Received alert %d:%d", + out_pos[0], out_pos[1]); + conn->read_alerts++; if (out_pos[0] == TLS_ALERT_LEVEL_WARNING) { /* Continue processing */ pos += used; @@ -285,13 +311,23 @@ int tlsv1_server_decrypt(struct tlsv1_server *conn, } if (ct != TLS_CONTENT_TYPE_APPLICATION_DATA) { - wpa_printf(MSG_DEBUG, "TLSv1: Unexpected content type " - "0x%x", pos[0]); + tlsv1_server_log(conn, "Unexpected content type 0x%x", + pos[0]); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_UNEXPECTED_MESSAGE); return -1; } +#ifdef CONFIG_TESTING_OPTIONS + if ((conn->test_flags & + (TLS_BREAK_VERIFY_DATA | TLS_BREAK_SRV_KEY_X_HASH | + TLS_BREAK_SRV_KEY_X_SIGNATURE)) && + !conn->test_failure_reported) { + tlsv1_server_log(conn, "TEST-FAILURE: Client ApplData received after invalid handshake"); + conn->test_failure_reported = 1; + } +#endif /* CONFIG_TESTING_OPTIONS */ + out_pos += olen; if (out_pos > out_end) { wpa_printf(MSG_DEBUG, "TLSv1: Buffer not large enough " @@ -344,7 +380,7 @@ struct tlsv1_server * tlsv1_server_init(struct tlsv1_credentials *cred) size_t count; u16 *suites; - conn = (struct tlsv1_server *)os_zalloc(sizeof(*conn)); + conn = os_zalloc(sizeof(*conn)); if (conn == NULL) return NULL; @@ -361,11 +397,16 @@ struct tlsv1_server * tlsv1_server_init(struct tlsv1_credentials *cred) count = 0; suites = conn->cipher_suites; + suites[count++] = TLS_DHE_RSA_WITH_AES_256_CBC_SHA256; + suites[count++] = TLS_RSA_WITH_AES_256_CBC_SHA256; + suites[count++] = TLS_DHE_RSA_WITH_AES_256_CBC_SHA; suites[count++] = TLS_RSA_WITH_AES_256_CBC_SHA; + suites[count++] = TLS_DHE_RSA_WITH_AES_128_CBC_SHA256; + suites[count++] = TLS_RSA_WITH_AES_128_CBC_SHA256; + suites[count++] = TLS_DHE_RSA_WITH_AES_128_CBC_SHA; suites[count++] = TLS_RSA_WITH_AES_128_CBC_SHA; -#ifdef CONFIG_DES3 + suites[count++] = TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA; suites[count++] = TLS_RSA_WITH_3DES_EDE_CBC_SHA; -#endif suites[count++] = TLS_RSA_WITH_RC4_128_SHA; suites[count++] = TLS_RSA_WITH_RC4_128_MD5; conn->num_cipher_suites = count; @@ -421,6 +462,8 @@ int tlsv1_server_established(struct tlsv1_server *conn) * tlsv1_server_prf - Use TLS-PRF to derive keying material * @conn: TLSv1 server connection data from tlsv1_server_init() * @label: Label (e.g., description of the key) for PRF + * @context: Optional extra upper-layer context (max len 2^16) + * @context_len: The length of the context value * @server_random_first: seed is 0 = client_random|server_random, * 1 = server_random|client_random * @out: Buffer for output data from TLS-PRF @@ -428,13 +471,26 @@ int tlsv1_server_established(struct tlsv1_server *conn) * Returns: 0 on success, -1 on failure */ int tlsv1_server_prf(struct tlsv1_server *conn, const char *label, + const u8 *context, size_t context_len, int server_random_first, u8 *out, size_t out_len) { - u8 seed[2 * TLS_RANDOM_LEN]; + u8 *seed, *pos; + size_t seed_len = 2 * TLS_RANDOM_LEN; + int res; if (conn->state != ESTABLISHED) return -1; + if (context_len > 65535) + return -1; + + if (context) + seed_len += 2 + context_len; + + seed = os_malloc(seed_len); + if (!seed) + return -1; + if (server_random_first) { os_memcpy(seed, conn->server_random, TLS_RANDOM_LEN); os_memcpy(seed + TLS_RANDOM_LEN, conn->client_random, @@ -445,9 +501,18 @@ int tlsv1_server_prf(struct tlsv1_server *conn, const char *label, TLS_RANDOM_LEN); } - return tls_prf(conn->rl.tls_version, - conn->master_secret, TLS_MASTER_SECRET_LEN, - label, seed, 2 * TLS_RANDOM_LEN, out, out_len); + if (context) { + pos = seed + 2 * TLS_RANDOM_LEN; + WPA_PUT_BE16(pos, context_len); + pos += 2; + os_memcpy(pos, context, context_len); + } + + res = tls_prf(conn->rl.tls_version, + conn->master_secret, TLS_MASTER_SECRET_LEN, + label, seed, seed_len, out, out_len); + os_free(seed); + return res; } @@ -463,8 +528,7 @@ int tlsv1_server_prf(struct tlsv1_server *conn, const char *label, int tlsv1_server_get_cipher(struct tlsv1_server *conn, char *buf, size_t buflen) { -#ifndef ESPRESSIF_USE - char *cipher; + char *cipher; switch (conn->rl.cipher_suite) { case TLS_RSA_WITH_RC4_128_MD5: @@ -473,67 +537,70 @@ int tlsv1_server_get_cipher(struct tlsv1_server *conn, char *buf, case TLS_RSA_WITH_RC4_128_SHA: cipher = "RC4-SHA"; break; -#ifdef CONFIG_DES case TLS_RSA_WITH_DES_CBC_SHA: cipher = "DES-CBC-SHA"; break; -#endif -#ifdef CONFIG_DES3 case TLS_RSA_WITH_3DES_EDE_CBC_SHA: cipher = "DES-CBC3-SHA"; break; -#endif + case TLS_DHE_RSA_WITH_DES_CBC_SHA: + cipher = "DHE-RSA-DES-CBC-SHA"; + break; + case TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA: + cipher = "DHE-RSA-DES-CBC3-SHA"; + break; + case TLS_DH_anon_WITH_RC4_128_MD5: + cipher = "ADH-RC4-MD5"; + break; + case TLS_DH_anon_WITH_DES_CBC_SHA: + cipher = "ADH-DES-SHA"; + break; + case TLS_DH_anon_WITH_3DES_EDE_CBC_SHA: + cipher = "ADH-DES-CBC3-SHA"; + break; + case TLS_RSA_WITH_AES_128_CBC_SHA: + cipher = "AES-128-SHA"; + break; + case TLS_DHE_RSA_WITH_AES_128_CBC_SHA: + cipher = "DHE-RSA-AES-128-SHA"; + break; case TLS_DH_anon_WITH_AES_128_CBC_SHA: cipher = "ADH-AES-128-SHA"; break; case TLS_RSA_WITH_AES_256_CBC_SHA: cipher = "AES-256-SHA"; break; - case TLS_RSA_WITH_AES_128_CBC_SHA: - cipher = "AES-128-SHA"; + case TLS_DHE_RSA_WITH_AES_256_CBC_SHA: + cipher = "DHE-RSA-AES-256-SHA"; + break; + case TLS_DH_anon_WITH_AES_256_CBC_SHA: + cipher = "ADH-AES-256-SHA"; + break; + case TLS_RSA_WITH_AES_128_CBC_SHA256: + cipher = "AES-128-SHA256"; + break; + case TLS_RSA_WITH_AES_256_CBC_SHA256: + cipher = "AES-256-SHA256"; + break; + case TLS_DHE_RSA_WITH_AES_128_CBC_SHA256: + cipher = "DHE-RSA-AES-128-SHA256"; + break; + case TLS_DHE_RSA_WITH_AES_256_CBC_SHA256: + cipher = "DHE-RSA-AES-256-SHA256"; + break; + case TLS_DH_anon_WITH_AES_128_CBC_SHA256: + cipher = "ADH-AES-128-SHA256"; + break; + case TLS_DH_anon_WITH_AES_256_CBC_SHA256: + cipher = "ADH-AES-256-SHA256"; break; default: return -1; } - //if (os_strlcpy(buf, cipher, buflen) >= buflen) - // return -1; - - os_memcpy((u8 *)buf, (u8 *)cipher, buflen); - + if (os_strlcpy(buf, cipher, buflen) >= buflen) + return -1; return 0; -#else - char cipher[20]; - - switch (conn->rl.cipher_suite) { - case TLS_RSA_WITH_RC4_128_MD5: - strcpy(cipher, "RC4-MD5"); - break; - case TLS_RSA_WITH_RC4_128_SHA: - strcpy(cipher, "RC4-SHA"); - break; - case TLS_RSA_WITH_DES_CBC_SHA: - strcpy(cipher, "DES-CBC-SHA"); - break; - case TLS_RSA_WITH_3DES_EDE_CBC_SHA: - strcpy(cipher, "DES-CBC3-SHA"); - break; - case TLS_DH_anon_WITH_AES_128_CBC_SHA: - strcpy(cipher, "ADH-AES-128-SHA"); - break; - case TLS_RSA_WITH_AES_256_CBC_SHA: - strcpy(cipher, "AES-256-SHA"); - break; - case TLS_RSA_WITH_AES_128_CBC_SHA: - strcpy(cipher, "AES-128-SHA"); - break; - default: - return -1; - } - os_memcpy((u8 *)buf, (u8 *)cipher, buflen); - - return 0; -#endif } @@ -658,3 +725,143 @@ void tlsv1_server_set_session_ticket_cb(struct tlsv1_server *conn, conn->session_ticket_cb = cb; conn->session_ticket_cb_ctx = ctx; } + + +void tlsv1_server_set_log_cb(struct tlsv1_server *conn, + void (*cb)(void *ctx, const char *msg), void *ctx) +{ + conn->log_cb = cb; + conn->log_cb_ctx = ctx; +} + + +int tlsv1_server_get_failed(struct tlsv1_server *conn) +{ + return conn->state == FAILED; +} + + +int tlsv1_server_get_read_alerts(struct tlsv1_server *conn) +{ + return conn->read_alerts; +} + + +int tlsv1_server_get_write_alerts(struct tlsv1_server *conn) +{ + return conn->write_alerts; +} + + +#ifdef CONFIG_TESTING_OPTIONS +void tlsv1_server_set_test_flags(struct tlsv1_server *conn, u32 flags) +{ + conn->test_flags = flags; +} + + +static const u8 test_tls_prime15[1] = { + 15 +}; + +static const u8 test_tls_prime511b[64] = { + 0x50, 0xfb, 0xf1, 0xae, 0x01, 0xf1, 0xfe, 0xe6, + 0xe1, 0xae, 0xdc, 0x1e, 0xbe, 0xfb, 0x9e, 0x58, + 0x9a, 0xd7, 0x54, 0x9d, 0x6b, 0xb3, 0x78, 0xe2, + 0x39, 0x7f, 0x30, 0x01, 0x25, 0xa1, 0xf9, 0x7c, + 0x55, 0x0e, 0xa1, 0x15, 0xcc, 0x36, 0x34, 0xbb, + 0x6c, 0x8b, 0x64, 0x45, 0x15, 0x7f, 0xd3, 0xe7, + 0x31, 0xc8, 0x8e, 0x56, 0x8e, 0x95, 0xdc, 0xea, + 0x9e, 0xdf, 0xf7, 0x56, 0xdd, 0xb0, 0x34, 0xdb +}; + +static const u8 test_tls_prime767b[96] = { + 0x4c, 0xdc, 0xb8, 0x21, 0x20, 0x9d, 0xe8, 0xa3, + 0x53, 0xd9, 0x1c, 0x18, 0xc1, 0x3a, 0x58, 0x67, + 0xa7, 0x85, 0xf9, 0x28, 0x9b, 0xce, 0xc0, 0xd1, + 0x05, 0x84, 0x61, 0x97, 0xb2, 0x86, 0x1c, 0xd0, + 0xd1, 0x96, 0x23, 0x29, 0x8c, 0xc5, 0x30, 0x68, + 0x3e, 0xf9, 0x05, 0xba, 0x60, 0xeb, 0xdb, 0xee, + 0x2d, 0xdf, 0x84, 0x65, 0x49, 0x87, 0x90, 0x2a, + 0xc9, 0x8e, 0x34, 0x63, 0x6d, 0x9a, 0x2d, 0x32, + 0x1c, 0x46, 0xd5, 0x4e, 0x20, 0x20, 0x90, 0xac, + 0xd5, 0x48, 0x79, 0x99, 0x0c, 0xe6, 0xed, 0xbf, + 0x79, 0xc2, 0x47, 0x50, 0x95, 0x38, 0x38, 0xbc, + 0xde, 0xb0, 0xd2, 0xe8, 0x97, 0xcb, 0x22, 0xbb +}; + +static const u8 test_tls_prime58[128] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0xc1, 0xba, 0xc8, 0x25, 0xbe, 0x2d, 0xf3 +}; + +static const u8 test_tls_non_prime[] = { + /* + * This is not a prime and the value has the following factors: + * 13736783488716579923 * 16254860191773456563 * 18229434976173670763 * + * 11112313018289079419 * 10260802278580253339 * 12394009491575311499 * + * 12419059668711064739 * 14317973192687985827 * 10498605410533203179 * + * 16338688760390249003 * 11128963991123878883 * 12990532258280301419 * + * 3 + */ + 0x0C, 0x8C, 0x36, 0x9C, 0x6F, 0x71, 0x2E, 0xA7, + 0xAB, 0x32, 0xD3, 0x0F, 0x68, 0x3D, 0xB2, 0x6D, + 0x81, 0xDD, 0xC4, 0x84, 0x0D, 0x9C, 0x6E, 0x36, + 0x29, 0x70, 0xF3, 0x1E, 0x9A, 0x42, 0x0B, 0x67, + 0x82, 0x6B, 0xB1, 0xF2, 0xAF, 0x55, 0x28, 0xE7, + 0xDB, 0x67, 0x6C, 0xF7, 0x6B, 0xAC, 0xAC, 0xE5, + 0xF7, 0x9F, 0xD4, 0x63, 0x55, 0x70, 0x32, 0x7C, + 0x70, 0xFB, 0xAF, 0xB8, 0xEB, 0x37, 0xCF, 0x3F, + 0xFE, 0x94, 0x73, 0xF9, 0x7A, 0xC7, 0x12, 0x2E, + 0x9B, 0xB4, 0x7D, 0x08, 0x60, 0x83, 0x43, 0x52, + 0x83, 0x1E, 0xA5, 0xFC, 0xFA, 0x87, 0x12, 0xF4, + 0x64, 0xE2, 0xCE, 0x71, 0x17, 0x72, 0xB6, 0xAB +}; + +#endif /* CONFIG_TESTING_OPTIONS */ + + +void tlsv1_server_get_dh_p(struct tlsv1_server *conn, const u8 **dh_p, + size_t *dh_p_len) +{ + *dh_p = conn->cred->dh_p; + *dh_p_len = conn->cred->dh_p_len; + +#ifdef CONFIG_TESTING_OPTIONS + if (conn->test_flags & TLS_DHE_PRIME_511B) { + tlsv1_server_log(conn, "TESTING: Use short 511-bit prime with DHE"); + *dh_p = test_tls_prime511b; + *dh_p_len = sizeof(test_tls_prime511b); + } else if (conn->test_flags & TLS_DHE_PRIME_767B) { + tlsv1_server_log(conn, "TESTING: Use short 767-bit prime with DHE"); + *dh_p = test_tls_prime767b; + *dh_p_len = sizeof(test_tls_prime767b); + } else if (conn->test_flags & TLS_DHE_PRIME_15) { + tlsv1_server_log(conn, "TESTING: Use bogus 15 \"prime\" with DHE"); + *dh_p = test_tls_prime15; + *dh_p_len = sizeof(test_tls_prime15); + } else if (conn->test_flags & TLS_DHE_PRIME_58B) { + tlsv1_server_log(conn, "TESTING: Use short 58-bit prime in long container with DHE"); + *dh_p = test_tls_prime58; + *dh_p_len = sizeof(test_tls_prime58); + } else if (conn->test_flags & TLS_DHE_NON_PRIME) { + tlsv1_server_log(conn, "TESTING: Use claim non-prime as the DHE prime"); + *dh_p = test_tls_non_prime; + *dh_p_len = sizeof(test_tls_non_prime); + } +#endif /* CONFIG_TESTING_OPTIONS */ +} diff --git a/components/wpa_supplicant/src/tls/tlsv1_server.h b/components/wpa_supplicant/src/tls/tlsv1_server.h index 08a23fbb3e..c9c0875ca3 100644 --- a/components/wpa_supplicant/src/tls/tlsv1_server.h +++ b/components/wpa_supplicant/src/tls/tlsv1_server.h @@ -1,6 +1,6 @@ /* * TLS v1.0/v1.1/v1.2 server (RFC 2246, RFC 4346, RFC 5246) - * Copyright (c) 2006-2011, Jouni Malinen + * Copyright (c) 2006-2019, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -19,6 +19,7 @@ struct tlsv1_server * tlsv1_server_init(struct tlsv1_credentials *cred); void tlsv1_server_deinit(struct tlsv1_server *conn); int tlsv1_server_established(struct tlsv1_server *conn); int tlsv1_server_prf(struct tlsv1_server *conn, const char *label, + const u8 *context, size_t context_len, int server_random_first, u8 *out, size_t out_len); u8 * tlsv1_server_handshake(struct tlsv1_server *conn, const u8 *in_data, size_t in_len, size_t *out_len); @@ -45,4 +46,13 @@ void tlsv1_server_set_session_ticket_cb(struct tlsv1_server *conn, tlsv1_server_session_ticket_cb cb, void *ctx); +void tlsv1_server_set_log_cb(struct tlsv1_server *conn, + void (*cb)(void *ctx, const char *msg), void *ctx); + +int tlsv1_server_get_failed(struct tlsv1_server *conn); +int tlsv1_server_get_read_alerts(struct tlsv1_server *conn); +int tlsv1_server_get_write_alerts(struct tlsv1_server *conn); + +void tlsv1_server_set_test_flags(struct tlsv1_server *conn, u32 flags); + #endif /* TLSV1_SERVER_H */ diff --git a/components/wpa_supplicant/src/tls/tlsv1_server_i.h b/components/wpa_supplicant/src/tls/tlsv1_server_i.h index 1f61533a5a..2622585d84 100644 --- a/components/wpa_supplicant/src/tls/tlsv1_server_i.h +++ b/components/wpa_supplicant/src/tls/tlsv1_server_i.h @@ -30,6 +30,8 @@ struct tlsv1_server { u8 alert_level; u8 alert_description; + int read_alerts, write_alerts; + struct crypto_public_key *client_rsa_key; struct tls_verify_hash verify; @@ -51,13 +53,27 @@ struct tlsv1_server { tlsv1_server_session_ticket_cb session_ticket_cb; void *session_ticket_cb_ctx; + void (*log_cb)(void *ctx, const char *msg); + void *log_cb_ctx; + int use_session_ticket; + unsigned int status_request:1; + unsigned int status_request_v2:1; + unsigned int status_request_multi:1; u8 *dh_secret; size_t dh_secret_len; + +#ifdef CONFIG_TESTING_OPTIONS + u32 test_flags; + int test_failure_reported; +#endif /* CONFIG_TESTING_OPTIONS */ }; +void tlsv1_server_log(struct tlsv1_server *conn, const char *fmt, ...) +PRINTF_FORMAT(2, 3); + void tlsv1_server_alert(struct tlsv1_server *conn, u8 level, u8 description); int tlsv1_server_derive_keys(struct tlsv1_server *conn, const u8 *pre_master_secret, @@ -67,5 +83,7 @@ u8 * tlsv1_server_send_alert(struct tlsv1_server *conn, u8 level, u8 description, size_t *out_len); int tlsv1_server_process_handshake(struct tlsv1_server *conn, u8 ct, const u8 *buf, size_t *len); +void tlsv1_server_get_dh_p(struct tlsv1_server *conn, const u8 **dh_p, + size_t *dh_p_len); #endif /* TLSV1_SERVER_I_H */ diff --git a/components/wpa_supplicant/src/tls/tlsv1_server_read.c b/components/wpa_supplicant/src/tls/tlsv1_server_read.c index cc42662689..e957678fc0 100644 --- a/components/wpa_supplicant/src/tls/tlsv1_server_read.c +++ b/components/wpa_supplicant/src/tls/tlsv1_server_read.c @@ -1,25 +1,24 @@ /* * TLSv1 server - read handshake message - * Copyright (c) 2006-2011, Jouni Malinen + * Copyright (c) 2006-2014, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ -#include "utils/includes.h" +#include "includes.h" -#include "utils/common.h" +#include "common.h" #include "crypto/md5.h" #include "crypto/sha1.h" #include "crypto/sha256.h" -#include "tls/tls.h" -#include "tls/x509v3.h" -#include "tls/tlsv1_common.h" -#include "tls/tlsv1_record.h" -#include "tls/tlsv1_server.h" -#include "tls/tlsv1_server_i.h" +#include "crypto/tls.h" +#include "x509v3.h" +#include "tlsv1_common.h" +#include "tlsv1_record.h" +#include "tlsv1_server.h" +#include "tlsv1_server_i.h" -#include "eap_peer/eap_i.h" static int tls_process_client_key_exchange(struct tlsv1_server *conn, u8 ct, const u8 *in_data, size_t *in_len); @@ -28,6 +27,97 @@ static int tls_process_change_cipher_spec(struct tlsv1_server *conn, size_t *in_len); +static int testing_cipher_suite_filter(struct tlsv1_server *conn, u16 suite) +{ +#ifdef CONFIG_TESTING_OPTIONS + if ((conn->test_flags & + (TLS_BREAK_SRV_KEY_X_HASH | TLS_BREAK_SRV_KEY_X_SIGNATURE | + TLS_DHE_PRIME_511B | TLS_DHE_PRIME_767B | TLS_DHE_PRIME_15 | + TLS_DHE_PRIME_58B | TLS_DHE_NON_PRIME)) && + suite != TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 && + suite != TLS_DHE_RSA_WITH_AES_256_CBC_SHA && + suite != TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 && + suite != TLS_DHE_RSA_WITH_AES_128_CBC_SHA && + suite != TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA) + return 1; +#endif /* CONFIG_TESTING_OPTIONS */ + + return 0; +} + + +static void tls_process_status_request_item(struct tlsv1_server *conn, + const u8 *req, size_t req_len) +{ + const u8 *pos, *end; + u8 status_type; + + pos = req; + end = req + req_len; + + /* + * RFC 6961, 2.2: + * struct { + * CertificateStatusType status_type; + * uint16 request_length; + * select (status_type) { + * case ocsp: OCSPStatusRequest; + * case ocsp_multi: OCSPStatusRequest; + * } request; + * } CertificateStatusRequestItemV2; + * + * enum { ocsp(1), ocsp_multi(2), (255) } CertificateStatusType; + */ + + if (end - pos < 1) + return; /* Truncated data */ + + status_type = *pos++; + wpa_printf(MSG_DEBUG, "TLSv1: CertificateStatusType %u", status_type); + if (status_type != 1 && status_type != 2) + return; /* Unsupported status type */ + /* + * For now, only OCSP stapling is supported, so ignore the specific + * request, if any. + */ + wpa_hexdump(MSG_DEBUG, "TLSv1: OCSPStatusRequest", pos, end - pos); + + if (status_type == 2) + conn->status_request_multi = 1; +} + + +static void tls_process_status_request_v2(struct tlsv1_server *conn, + const u8 *ext, size_t ext_len) +{ + const u8 *pos, *end; + + conn->status_request_v2 = 1; + + pos = ext; + end = ext + ext_len; + + /* + * RFC 6961, 2.2: + * struct { + * CertificateStatusRequestItemV2 + * certificate_status_req_list<1..2^16-1>; + * } CertificateStatusRequestListV2; + */ + + while (end - pos >= 2) { + u16 len; + + len = WPA_GET_BE16(pos); + pos += 2; + if (len > end - pos) + break; /* Truncated data */ + tls_process_status_request_item(conn, pos, len); + pos += len; + } +} + + static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct, const u8 *in_data, size_t *in_len) { @@ -39,8 +129,8 @@ static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct, u16 ext_type, ext_len; if (ct != TLS_CONTENT_TYPE_HANDSHAKE) { - wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; " - "received content type 0x%x", ct); + tlsv1_server_log(conn, "Expected Handshake; received content type 0x%x", + ct); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_UNEXPECTED_MESSAGE); return -1; @@ -49,26 +139,33 @@ static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct, pos = in_data; left = *in_len; - if (left < 4) + if (left < 4) { + tlsv1_server_log(conn, + "Truncated handshake message (expected ClientHello)"); goto decode_error; + } /* HandshakeType msg_type */ if (*pos != TLS_HANDSHAKE_TYPE_CLIENT_HELLO) { - wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake " - "message %d (expected ClientHello)", *pos); + tlsv1_server_log(conn, "Received unexpected handshake message %d (expected ClientHello)", + *pos); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_UNEXPECTED_MESSAGE); return -1; } - wpa_printf(MSG_DEBUG, "TLSv1: Received ClientHello"); + tlsv1_server_log(conn, "Received ClientHello"); pos++; /* uint24 length */ len = WPA_GET_BE24(pos); pos += 3; left -= 4; - if (len > left) + if (len > left) { + tlsv1_server_log(conn, + "Truncated ClientHello (len=%d left=%d)", + (int) len, (int) left); goto decode_error; + } /* body - ClientHello */ @@ -76,16 +173,18 @@ static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct, end = pos + len; /* ProtocolVersion client_version */ - if (end - pos < 2) + if (end - pos < 2) { + tlsv1_server_log(conn, "Truncated ClientHello/client_version"); goto decode_error; + } conn->client_version = WPA_GET_BE16(pos); - wpa_printf(MSG_DEBUG, "TLSv1: Client version %d.%d", - conn->client_version >> 8, conn->client_version & 0xff); + tlsv1_server_log(conn, "Client version %d.%d", + conn->client_version >> 8, + conn->client_version & 0xff); if (conn->client_version < TLS_VERSION_1) { - wpa_printf(MSG_DEBUG, "TLSv1: Unexpected protocol version in " - "ClientHello %u.%u", - conn->client_version >> 8, - conn->client_version & 0xff); + tlsv1_server_log(conn, "Unexpected protocol version in ClientHello %u.%u", + conn->client_version >> 8, + conn->client_version & 0xff); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_PROTOCOL_VERSION); return -1; @@ -102,12 +201,14 @@ static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct, conn->rl.tls_version = TLS_VERSION_1_1; else conn->rl.tls_version = conn->client_version; - wpa_printf(MSG_DEBUG, "TLSv1: Using TLS v%s", - tls_version_str(conn->rl.tls_version)); + tlsv1_server_log(conn, "Using TLS v%s", + tls_version_str(conn->rl.tls_version)); /* Random random */ - if (end - pos < TLS_RANDOM_LEN) + if (end - pos < TLS_RANDOM_LEN) { + tlsv1_server_log(conn, "Truncated ClientHello/client_random"); goto decode_error; + } os_memcpy(conn->client_random, pos, TLS_RANDOM_LEN); pos += TLS_RANDOM_LEN; @@ -115,29 +216,42 @@ static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct, conn->client_random, TLS_RANDOM_LEN); /* SessionID session_id */ - if (end - pos < 1) + if (end - pos < 1) { + tlsv1_server_log(conn, "Truncated ClientHello/session_id len"); goto decode_error; - if (end - pos < 1 + *pos || *pos > TLS_SESSION_ID_MAX_LEN) + } + if (end - pos < 1 + *pos || *pos > TLS_SESSION_ID_MAX_LEN) { + tlsv1_server_log(conn, "Truncated ClientHello/session_id"); goto decode_error; + } wpa_hexdump(MSG_MSGDUMP, "TLSv1: client session_id", pos + 1, *pos); pos += 1 + *pos; /* TODO: add support for session resumption */ /* CipherSuite cipher_suites<2..2^16-1> */ - if (end - pos < 2) + if (end - pos < 2) { + tlsv1_server_log(conn, + "Truncated ClientHello/cipher_suites len"); goto decode_error; + } num_suites = WPA_GET_BE16(pos); pos += 2; - if (end - pos < num_suites) + if (end - pos < num_suites) { + tlsv1_server_log(conn, "Truncated ClientHello/cipher_suites"); goto decode_error; + } wpa_hexdump(MSG_MSGDUMP, "TLSv1: client cipher suites", pos, num_suites); - if (num_suites & 1) + if (num_suites & 1) { + tlsv1_server_log(conn, "Odd len ClientHello/cipher_suites"); goto decode_error; + } num_suites /= 2; cipher_suite = 0; for (i = 0; !cipher_suite && i < conn->num_cipher_suites; i++) { + if (testing_cipher_suite_filter(conn, conn->cipher_suites[i])) + continue; c = pos; for (j = 0; j < num_suites; j++) { u16 tmp = WPA_GET_BE16(c); @@ -150,8 +264,7 @@ static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct, } pos += num_suites * 2; if (!cipher_suite) { - wpa_printf(MSG_INFO, "TLSv1: No supported cipher suite " - "available"); + tlsv1_server_log(conn, "No supported cipher suite available"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_ILLEGAL_PARAMETER); return -1; @@ -168,11 +281,17 @@ static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct, conn->cipher_suite = cipher_suite; /* CompressionMethod compression_methods<1..2^8-1> */ - if (end - pos < 1) + if (end - pos < 1) { + tlsv1_server_log(conn, + "Truncated ClientHello/compression_methods len"); goto decode_error; + } num_suites = *pos++; - if (end - pos < num_suites) + if (end - pos < num_suites) { + tlsv1_server_log(conn, + "Truncated ClientHello/compression_methods"); goto decode_error; + } wpa_hexdump(MSG_MSGDUMP, "TLSv1: client compression_methods", pos, num_suites); compr_null_found = 0; @@ -181,16 +300,15 @@ static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct, compr_null_found = 1; } if (!compr_null_found) { - wpa_printf(MSG_INFO, "TLSv1: Client does not accept NULL " - "compression"); + tlsv1_server_log(conn, "Client does not accept NULL compression"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_ILLEGAL_PARAMETER); return -1; } if (end - pos == 1) { - wpa_printf(MSG_DEBUG, "TLSv1: Unexpected extra octet in the " - "end of ClientHello: 0x%02x", *pos); + tlsv1_server_log(conn, "Unexpected extra octet in the end of ClientHello: 0x%02x", + *pos); goto decode_error; } @@ -199,12 +317,11 @@ static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct, ext_len = WPA_GET_BE16(pos); pos += 2; - wpa_printf(MSG_DEBUG, "TLSv1: %u bytes of ClientHello " - "extensions", ext_len); + tlsv1_server_log(conn, "%u bytes of ClientHello extensions", + ext_len); if (end - pos != ext_len) { - wpa_printf(MSG_DEBUG, "TLSv1: Invalid ClientHello " - "extension list length %u (expected %u)", - ext_len, (unsigned int) (end - pos)); + tlsv1_server_log(conn, "Invalid ClientHello extension list length %u (expected %u)", + ext_len, (unsigned int) (end - pos)); goto decode_error; } @@ -217,8 +334,7 @@ static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct, while (pos < end) { if (end - pos < 2) { - wpa_printf(MSG_DEBUG, "TLSv1: Invalid " - "extension_type field"); + tlsv1_server_log(conn, "Invalid extension_type field"); goto decode_error; } @@ -226,8 +342,7 @@ static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct, pos += 2; if (end - pos < 2) { - wpa_printf(MSG_DEBUG, "TLSv1: Invalid " - "extension_data length field"); + tlsv1_server_log(conn, "Invalid extension_data length field"); goto decode_error; } @@ -235,13 +350,12 @@ static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct, pos += 2; if (end - pos < ext_len) { - wpa_printf(MSG_DEBUG, "TLSv1: Invalid " - "extension_data field"); + tlsv1_server_log(conn, "Invalid extension_data field"); goto decode_error; } - wpa_printf(MSG_DEBUG, "TLSv1: ClientHello Extension " - "type %u", ext_type); + tlsv1_server_log(conn, "ClientHello Extension type %u", + ext_type); wpa_hexdump(MSG_MSGDUMP, "TLSv1: ClientHello " "Extension data", pos, ext_len); @@ -253,6 +367,11 @@ static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct, ext_len); conn->session_ticket_len = ext_len; } + } else if (ext_type == TLS_EXT_STATUS_REQUEST) { + conn->status_request = 1; + } else if (ext_type == TLS_EXT_STATUS_REQUEST_V2) { + tls_process_status_request_v2(conn, pos, + ext_len); } pos += ext_len; @@ -261,14 +380,13 @@ static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct, *in_len = end - in_data; - wpa_printf(MSG_DEBUG, "TLSv1: ClientHello OK - proceed to " - "ServerHello"); + tlsv1_server_log(conn, "ClientHello OK - proceed to ServerHello"); conn->state = SERVER_HELLO; return 0; decode_error: - wpa_printf(MSG_DEBUG, "TLSv1: Failed to decode ClientHello"); + tlsv1_server_log(conn, "Failed to decode ClientHello"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; @@ -285,8 +403,8 @@ static int tls_process_certificate(struct tlsv1_server *conn, u8 ct, int reason; if (ct != TLS_CONTENT_TYPE_HANDSHAKE) { - wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; " - "received content type 0x%x", ct); + tlsv1_server_log(conn, "Expected Handshake; received content type 0x%x", + ct); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_UNEXPECTED_MESSAGE); return -1; @@ -296,8 +414,8 @@ static int tls_process_certificate(struct tlsv1_server *conn, u8 ct, left = *in_len; if (left < 4) { - wpa_printf(MSG_DEBUG, "TLSv1: Too short Certificate message " - "(len=%lu)", (unsigned long) left); + tlsv1_server_log(conn, "Too short Certificate message (len=%lu)", + (unsigned long) left); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; @@ -309,9 +427,8 @@ static int tls_process_certificate(struct tlsv1_server *conn, u8 ct, left -= 4; if (len > left) { - wpa_printf(MSG_DEBUG, "TLSv1: Unexpected Certificate message " - "length (len=%lu != left=%lu)", - (unsigned long) len, (unsigned long) left); + tlsv1_server_log(conn, "Unexpected Certificate message length (len=%lu != left=%lu)", + (unsigned long) len, (unsigned long) left); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; @@ -319,8 +436,7 @@ static int tls_process_certificate(struct tlsv1_server *conn, u8 ct, if (type == TLS_HANDSHAKE_TYPE_CLIENT_KEY_EXCHANGE) { if (conn->verify_peer) { - wpa_printf(MSG_DEBUG, "TLSv1: Client did not include " - "Certificate"); + tlsv1_server_log(conn, "Client did not include Certificate"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_UNEXPECTED_MESSAGE); return -1; @@ -330,16 +446,15 @@ static int tls_process_certificate(struct tlsv1_server *conn, u8 ct, in_len); } if (type != TLS_HANDSHAKE_TYPE_CERTIFICATE) { - wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake " - "message %d (expected Certificate/" - "ClientKeyExchange)", type); + tlsv1_server_log(conn, "Received unexpected handshake message %d (expected Certificate/ClientKeyExchange)", + type); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_UNEXPECTED_MESSAGE); return -1; } - wpa_printf(MSG_DEBUG, "TLSv1: Received Certificate (certificate_list len %lu)", - (unsigned long) len); + tlsv1_server_log(conn, "Received Certificate (certificate_list len %lu)", + (unsigned long) len); /* * opaque ASN.1Cert<2^24-1>; @@ -352,8 +467,8 @@ static int tls_process_certificate(struct tlsv1_server *conn, u8 ct, end = pos + len; if (end - pos < 3) { - wpa_printf(MSG_DEBUG, "TLSv1: Too short Certificate " - "(left=%lu)", (unsigned long) left); + tlsv1_server_log(conn, "Too short Certificate (left=%lu)", + (unsigned long) left); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; @@ -363,10 +478,9 @@ static int tls_process_certificate(struct tlsv1_server *conn, u8 ct, pos += 3; if ((size_t) (end - pos) != list_len) { - wpa_printf(MSG_DEBUG, "TLSv1: Unexpected certificate_list " - "length (len=%lu left=%lu)", - (unsigned long) list_len, - (unsigned long) (end - pos)); + tlsv1_server_log(conn, "Unexpected certificate_list length (len=%lu left=%lu)", + (unsigned long) list_len, + (unsigned long) (end - pos)); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; @@ -375,8 +489,7 @@ static int tls_process_certificate(struct tlsv1_server *conn, u8 ct, idx = 0; while (pos < end) { if (end - pos < 3) { - wpa_printf(MSG_DEBUG, "TLSv1: Failed to parse " - "certificate_list"); + tlsv1_server_log(conn, "Failed to parse certificate_list"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); x509_certificate_chain_free(chain); @@ -387,25 +500,23 @@ static int tls_process_certificate(struct tlsv1_server *conn, u8 ct, pos += 3; if ((size_t) (end - pos) < cert_len) { - wpa_printf(MSG_DEBUG, "TLSv1: Unexpected certificate " - "length (len=%lu left=%lu)", - (unsigned long) cert_len, - (unsigned long) (end - pos)); + tlsv1_server_log(conn, "Unexpected certificate length (len=%lu left=%lu)", + (unsigned long) cert_len, + (unsigned long) (end - pos)); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); x509_certificate_chain_free(chain); return -1; } - wpa_printf(MSG_DEBUG, "TLSv1: Certificate %lu (len %lu)", - (unsigned long) idx, (unsigned long) cert_len); + tlsv1_server_log(conn, "Certificate %lu (len %lu)", + (unsigned long) idx, (unsigned long) cert_len); if (idx == 0) { crypto_public_key_free(conn->client_rsa_key); if (tls_parse_cert(pos, cert_len, &conn->client_rsa_key)) { - wpa_printf(MSG_DEBUG, "TLSv1: Failed to parse " - "the certificate"); + tlsv1_server_log(conn, "Failed to parse the certificate"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_BAD_CERTIFICATE); x509_certificate_chain_free(chain); @@ -415,8 +526,7 @@ static int tls_process_certificate(struct tlsv1_server *conn, u8 ct, cert = x509_certificate_parse(pos, cert_len); if (cert == NULL) { - wpa_printf(MSG_DEBUG, "TLSv1: Failed to parse " - "the certificate"); + tlsv1_server_log(conn, "Failed to parse the certificate"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_BAD_CERTIFICATE); x509_certificate_chain_free(chain); @@ -436,8 +546,8 @@ static int tls_process_certificate(struct tlsv1_server *conn, u8 ct, if (x509_certificate_chain_validate(conn->cred->trusted_certs, chain, &reason, 0) < 0) { int tls_reason; - wpa_printf(MSG_DEBUG, "TLSv1: Server certificate chain " - "validation failed (reason=%d)", reason); + tlsv1_server_log(conn, "Server certificate chain validation failed (reason=%d)", + reason); switch (reason) { case X509_VALIDATE_BAD_CERTIFICATE: tls_reason = TLS_ALERT_BAD_CERTIFICATE; @@ -466,6 +576,15 @@ static int tls_process_certificate(struct tlsv1_server *conn, u8 ct, return -1; } + if (chain && (chain->extensions_present & X509_EXT_EXT_KEY_USAGE) && + !(chain->ext_key_usage & + (X509_EXT_KEY_USAGE_ANY | X509_EXT_KEY_USAGE_CLIENT_AUTH))) { + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_BAD_CERTIFICATE); + x509_certificate_chain_free(chain); + return -1; + } + x509_certificate_chain_free(chain); *in_len = end - in_data; @@ -494,9 +613,8 @@ static int tls_process_client_key_exchange_rsa( encr_len = WPA_GET_BE16(pos); pos += 2; if (pos + encr_len > end) { - wpa_printf(MSG_DEBUG, "TLSv1: Invalid ClientKeyExchange " - "format: encr_len=%u left=%u", - encr_len, (unsigned int) (end - pos)); + tlsv1_server_log(conn, "Invalid ClientKeyExchange format: encr_len=%u left=%u", + encr_len, (unsigned int) (end - pos)); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; @@ -539,15 +657,13 @@ static int tls_process_client_key_exchange_rsa( } if (!use_random && outlen != TLS_PRE_MASTER_SECRET_LEN) { - wpa_printf(MSG_DEBUG, "TLSv1: Unexpected PreMasterSecret " - "length %lu", (unsigned long) outlen); + tlsv1_server_log(conn, "Unexpected PreMasterSecret length %lu", + (unsigned long) outlen); use_random = 1; } if (!use_random && WPA_GET_BE16(out) != conn->client_version) { - wpa_printf(MSG_DEBUG, "TLSv1: Client version in " - "ClientKeyExchange does not match with version in " - "ClientHello"); + tlsv1_server_log(conn, "Client version in ClientKeyExchange does not match with version in ClientHello"); use_random = 1; } @@ -582,7 +698,7 @@ static int tls_process_client_key_exchange_rsa( } -static int tls_process_client_key_exchange_dh_anon( +static int tls_process_client_key_exchange_dh( struct tlsv1_server *conn, const u8 *pos, const u8 *end) { const u8 *dh_yc; @@ -590,6 +706,8 @@ static int tls_process_client_key_exchange_dh_anon( u8 *shared; size_t shared_len; int res; + const u8 *dh_p; + size_t dh_p_len; /* * struct { @@ -600,6 +718,7 @@ static int tls_process_client_key_exchange_dh_anon( * } ClientDiffieHellmanPublic; */ + tlsv1_server_log(conn, "ClientDiffieHellmanPublic received"); wpa_hexdump(MSG_MSGDUMP, "TLSv1: ClientDiffieHellmanPublic", pos, end - pos); @@ -612,8 +731,7 @@ static int tls_process_client_key_exchange_dh_anon( } if (end - pos < 3) { - wpa_printf(MSG_DEBUG, "TLSv1: Invalid client public value " - "length"); + tlsv1_server_log(conn, "Invalid client public value length"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; @@ -622,9 +740,9 @@ static int tls_process_client_key_exchange_dh_anon( dh_yc_len = WPA_GET_BE16(pos); dh_yc = pos + 2; - if (dh_yc + dh_yc_len > end) { - wpa_printf(MSG_DEBUG, "TLSv1: Client public value overflow " - "(length %d)", dh_yc_len); + if (dh_yc_len > end - dh_yc) { + tlsv1_server_log(conn, "Client public value overflow (length %d)", + dh_yc_len); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; @@ -641,7 +759,9 @@ static int tls_process_client_key_exchange_dh_anon( return -1; } - shared_len = conn->cred->dh_p_len; + tlsv1_server_get_dh_p(conn, &dh_p, &dh_p_len); + + shared_len = dh_p_len; shared = os_malloc(shared_len); if (shared == NULL) { wpa_printf(MSG_DEBUG, "TLSv1: Could not allocate memory for " @@ -653,15 +773,13 @@ static int tls_process_client_key_exchange_dh_anon( /* shared = Yc^secret mod p */ if (crypto_mod_exp(dh_yc, dh_yc_len, conn->dh_secret, - conn->dh_secret_len, - conn->cred->dh_p, conn->cred->dh_p_len, - shared, &shared_len)) { + conn->dh_secret_len, dh_p, dh_p_len, + shared, &shared_len)) { os_free(shared); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, - TLS_ALERT_INTERNAL_ERROR); + TLS_ALERT_INTERNAL_ERROR); return -1; } - wpa_hexdump_key(MSG_DEBUG, "TLSv1: Shared secret from DH key exchange", shared, shared_len); @@ -696,8 +814,8 @@ static int tls_process_client_key_exchange(struct tlsv1_server *conn, u8 ct, const struct tls_cipher_suite *suite; if (ct != TLS_CONTENT_TYPE_HANDSHAKE) { - wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; " - "received content type 0x%x", ct); + tlsv1_server_log(conn, "Expected Handshake; received content type 0x%x", + ct); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_UNEXPECTED_MESSAGE); return -1; @@ -707,8 +825,8 @@ static int tls_process_client_key_exchange(struct tlsv1_server *conn, u8 ct, left = *in_len; if (left < 4) { - wpa_printf(MSG_DEBUG, "TLSv1: Too short ClientKeyExchange " - "(Left=%lu)", (unsigned long) left); + tlsv1_server_log(conn, "Too short ClientKeyExchange (Left=%lu)", + (unsigned long) left); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; @@ -720,9 +838,8 @@ static int tls_process_client_key_exchange(struct tlsv1_server *conn, u8 ct, left -= 4; if (len > left) { - wpa_printf(MSG_DEBUG, "TLSv1: Mismatch in ClientKeyExchange " - "length (len=%lu != left=%lu)", - (unsigned long) len, (unsigned long) left); + tlsv1_server_log(conn, "Mismatch in ClientKeyExchange length (len=%lu != left=%lu)", + (unsigned long) len, (unsigned long) left); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; @@ -731,14 +848,14 @@ static int tls_process_client_key_exchange(struct tlsv1_server *conn, u8 ct, end = pos + len; if (type != TLS_HANDSHAKE_TYPE_CLIENT_KEY_EXCHANGE) { - wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake " - "message %d (expected ClientKeyExchange)", type); + tlsv1_server_log(conn, "Received unexpected handshake message %d (expected ClientKeyExchange)", + type); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_UNEXPECTED_MESSAGE); return -1; } - wpa_printf(MSG_DEBUG, "TLSv1: Received ClientKeyExchange"); + tlsv1_server_log(conn, "Received ClientKeyExchange"); wpa_hexdump(MSG_DEBUG, "TLSv1: ClientKeyExchange", pos, len); @@ -748,11 +865,11 @@ static int tls_process_client_key_exchange(struct tlsv1_server *conn, u8 ct, else keyx = suite->key_exchange; - if (keyx == TLS_KEY_X_DH_anon && - tls_process_client_key_exchange_dh_anon(conn, pos, end) < 0) + if ((keyx == TLS_KEY_X_DH_anon || keyx == TLS_KEY_X_DHE_RSA) && + tls_process_client_key_exchange_dh(conn, pos, end) < 0) return -1; - if (keyx != TLS_KEY_X_DH_anon && + if (keyx != TLS_KEY_X_DH_anon && keyx != TLS_KEY_X_DHE_RSA && tls_process_client_key_exchange_rsa(conn, pos, end) < 0) return -1; @@ -770,15 +887,13 @@ static int tls_process_certificate_verify(struct tlsv1_server *conn, u8 ct, const u8 *pos, *end; size_t left, len; u8 type; - size_t hlen, buflen; - u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN], *hpos, *buf; - enum { SIGN_ALG_RSA, SIGN_ALG_DSA } alg = SIGN_ALG_RSA; - u16 slen; + size_t hlen; + u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN], *hpos; + u8 alert; if (ct == TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC) { if (conn->verify_peer) { - wpa_printf(MSG_DEBUG, "TLSv1: Client did not include " - "CertificateVerify"); + tlsv1_server_log(conn, "Client did not include CertificateVerify"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_UNEXPECTED_MESSAGE); return -1; @@ -789,8 +904,8 @@ static int tls_process_certificate_verify(struct tlsv1_server *conn, u8 ct, } if (ct != TLS_CONTENT_TYPE_HANDSHAKE) { - wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; " - "received content type 0x%x", ct); + tlsv1_server_log(conn, "Expected Handshake; received content type 0x%x", + ct); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_UNEXPECTED_MESSAGE); return -1; @@ -800,8 +915,8 @@ static int tls_process_certificate_verify(struct tlsv1_server *conn, u8 ct, left = *in_len; if (left < 4) { - wpa_printf(MSG_DEBUG, "TLSv1: Too short CertificateVerify " - "message (len=%lu)", (unsigned long) left); + tlsv1_server_log(conn, "Too short CertificateVerify message (len=%lu)", + (unsigned long) left); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; @@ -813,9 +928,8 @@ static int tls_process_certificate_verify(struct tlsv1_server *conn, u8 ct, left -= 4; if (len > left) { - wpa_printf(MSG_DEBUG, "TLSv1: Unexpected CertificateVerify " - "message length (len=%lu != left=%lu)", - (unsigned long) len, (unsigned long) left); + tlsv1_server_log(conn, "Unexpected CertificateVerify message length (len=%lu != left=%lu)", + (unsigned long) len, (unsigned long) left); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; @@ -824,14 +938,14 @@ static int tls_process_certificate_verify(struct tlsv1_server *conn, u8 ct, end = pos + len; if (type != TLS_HANDSHAKE_TYPE_CERTIFICATE_VERIFY) { - wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake " - "message %d (expected CertificateVerify)", type); + tlsv1_server_log(conn, "Received unexpected handshake message %d (expected CertificateVerify)", + type); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_UNEXPECTED_MESSAGE); return -1; } - wpa_printf(MSG_DEBUG, "TLSv1: Received CertificateVerify"); + tlsv1_server_log(conn, "Received CertificateVerify"); /* * struct { @@ -871,32 +985,28 @@ static int tls_process_certificate_verify(struct tlsv1_server *conn, u8 ct, hlen = SHA256_MAC_LEN; if (conn->verify.sha256_cert == NULL || - crypto_hash_finish(conn->verify.sha256_cert, hpos, &hlen) < - 0) { + crypto_hash_finish(conn->verify.sha256_cert, hpos, &hlen) < + 0) { conn->verify.sha256_cert = NULL; tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, - TLS_ALERT_INTERNAL_ERROR); + TLS_ALERT_INTERNAL_ERROR); return -1; } conn->verify.sha256_cert = NULL; } else { #endif /* CONFIG_TLSV12 */ - if (alg == SIGN_ALG_RSA) { - hlen = MD5_MAC_LEN; - if (conn->verify.md5_cert == NULL || - crypto_hash_finish(conn->verify.md5_cert, hpos, &hlen) < 0) - { - tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, - TLS_ALERT_INTERNAL_ERROR); - conn->verify.md5_cert = NULL; - crypto_hash_finish(conn->verify.sha1_cert, NULL, NULL); - conn->verify.sha1_cert = NULL; - return -1; - } - hpos += MD5_MAC_LEN; - } else - crypto_hash_finish(conn->verify.md5_cert, NULL, NULL); + hlen = MD5_MAC_LEN; + if (conn->verify.md5_cert == NULL || + crypto_hash_finish(conn->verify.md5_cert, hpos, &hlen) < 0) { + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + conn->verify.md5_cert = NULL; + crypto_hash_finish(conn->verify.sha1_cert, NULL, NULL); + conn->verify.sha1_cert = NULL; + return -1; + } + hpos += MD5_MAC_LEN; conn->verify.md5_cert = NULL; hlen = SHA1_MAC_LEN; @@ -909,8 +1019,7 @@ static int tls_process_certificate_verify(struct tlsv1_server *conn, u8 ct, } conn->verify.sha1_cert = NULL; - if (alg == SIGN_ALG_RSA) - hlen += MD5_MAC_LEN; + hlen += MD5_MAC_LEN; #ifdef CONFIG_TLSV12 } @@ -918,89 +1027,12 @@ static int tls_process_certificate_verify(struct tlsv1_server *conn, u8 ct, wpa_hexdump(MSG_MSGDUMP, "TLSv1: CertificateVerify hash", hash, hlen); - if (end - pos < 2) { - tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, - TLS_ALERT_DECODE_ERROR); + if (tls_verify_signature(conn->rl.tls_version, conn->client_rsa_key, + hash, hlen, pos, end - pos, &alert) < 0) { + tlsv1_server_log(conn, "Invalid Signature in CertificateVerify"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, alert); return -1; } - slen = WPA_GET_BE16(pos); - pos += 2; - if (end - pos < slen) { - tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, - TLS_ALERT_DECODE_ERROR); - return -1; - } - - wpa_hexdump(MSG_MSGDUMP, "TLSv1: Signature", pos, end - pos); - if (conn->client_rsa_key == NULL) { - wpa_printf(MSG_DEBUG, "TLSv1: No client public key to verify " - "signature"); - tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, - TLS_ALERT_INTERNAL_ERROR); - return -1; - } - - buflen = end - pos; - buf = os_malloc(end - pos); - if (crypto_public_key_decrypt_pkcs1(conn->client_rsa_key, - pos, end - pos, buf, &buflen) < 0) - { - wpa_printf(MSG_DEBUG, "TLSv1: Failed to decrypt signature"); - os_free(buf); - tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, - TLS_ALERT_DECRYPT_ERROR); - return -1; - } - - wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Decrypted Signature", - buf, buflen); - -#ifdef CONFIG_TLSV12 - if (conn->rl.tls_version >= TLS_VERSION_1_2) { - /* - * RFC 3447, A.2.4 RSASSA-PKCS1-v1_5 - * - * DigestInfo ::= SEQUENCE { - * digestAlgorithm DigestAlgorithm, - * digest OCTET STRING - * } - * - * SHA-256 OID: sha256WithRSAEncryption ::= {pkcs-1 11} - * - * DER encoded DigestInfo for SHA256 per RFC 3447: - * 30 31 30 0d 06 09 60 86 48 01 65 03 04 02 01 05 00 04 20 || - * H - */ - if (buflen >= 19 + 32 && - os_memcmp(buf, "\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01" - "\x65\x03\x04\x02\x01\x05\x00\x04\x20", 19) == 0) - { - wpa_printf(MSG_DEBUG, "TLSv1.2: DigestAlgorithn = " - "SHA-256"); - os_memmove(buf, buf + 19, buflen - 19); - buflen -= 19; - } else { - wpa_printf(MSG_DEBUG, "TLSv1.2: Unrecognized " - "DigestInfo"); - os_free(buf); - tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, - TLS_ALERT_DECRYPT_ERROR); - return -1; - } - } -#endif /* CONFIG_TLSV12 */ - - if (buflen != hlen || os_memcmp(buf, hash, buflen) != 0) { - wpa_printf(MSG_DEBUG, "TLSv1: Invalid Signature in " - "CertificateVerify - did not match with calculated " - "hash"); - os_free(buf); - tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, - TLS_ALERT_DECRYPT_ERROR); - return -1; - } - - os_free(buf); *in_len = end - in_data; @@ -1018,8 +1050,8 @@ static int tls_process_change_cipher_spec(struct tlsv1_server *conn, size_t left; if (ct != TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC) { - wpa_printf(MSG_DEBUG, "TLSv1: Expected ChangeCipherSpec; " - "received content type 0x%x", ct); + tlsv1_server_log(conn, "Expected ChangeCipherSpec; received content type 0x%x", + ct); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_UNEXPECTED_MESSAGE); return -1; @@ -1029,21 +1061,21 @@ static int tls_process_change_cipher_spec(struct tlsv1_server *conn, left = *in_len; if (left < 1) { - wpa_printf(MSG_DEBUG, "TLSv1: Too short ChangeCipherSpec"); + tlsv1_server_log(conn, "Too short ChangeCipherSpec"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; } if (*pos != TLS_CHANGE_CIPHER_SPEC) { - wpa_printf(MSG_DEBUG, "TLSv1: Expected ChangeCipherSpec; " - "received data 0x%x", *pos); + tlsv1_server_log(conn, "Expected ChangeCipherSpec; received data 0x%x", + *pos); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_UNEXPECTED_MESSAGE); return -1; } - wpa_printf(MSG_DEBUG, "TLSv1: Received ChangeCipherSpec"); + tlsv1_server_log(conn, "Received ChangeCipherSpec"); if (tlsv1_record_change_read_cipher(&conn->rl) < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to change read cipher " "for record layer"); @@ -1068,9 +1100,48 @@ static int tls_process_client_finished(struct tlsv1_server *conn, u8 ct, u8 verify_data[TLS_VERIFY_DATA_LEN]; u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN]; +#ifdef CONFIG_TESTING_OPTIONS + if ((conn->test_flags & + (TLS_BREAK_SRV_KEY_X_HASH | TLS_BREAK_SRV_KEY_X_SIGNATURE)) && + !conn->test_failure_reported) { + tlsv1_server_log(conn, "TEST-FAILURE: Client Finished received after invalid ServerKeyExchange"); + conn->test_failure_reported = 1; + } + + if ((conn->test_flags & TLS_DHE_PRIME_15) && + !conn->test_failure_reported) { + tlsv1_server_log(conn, "TEST-FAILURE: Client Finished received after bogus DHE \"prime\" 15"); + conn->test_failure_reported = 1; + } + + if ((conn->test_flags & TLS_DHE_PRIME_58B) && + !conn->test_failure_reported) { + tlsv1_server_log(conn, "TEST-FAILURE: Client Finished received after short 58-bit DHE prime in long container"); + conn->test_failure_reported = 1; + } + + if ((conn->test_flags & TLS_DHE_PRIME_511B) && + !conn->test_failure_reported) { + tlsv1_server_log(conn, "TEST-WARNING: Client Finished received after short 511-bit DHE prime (insecure)"); + conn->test_failure_reported = 1; + } + + if ((conn->test_flags & TLS_DHE_PRIME_767B) && + !conn->test_failure_reported) { + tlsv1_server_log(conn, "TEST-NOTE: Client Finished received after 767-bit DHE prime (relatively insecure)"); + conn->test_failure_reported = 1; + } + + if ((conn->test_flags & TLS_DHE_NON_PRIME) && + !conn->test_failure_reported) { + tlsv1_server_log(conn, "TEST-NOTE: Client Finished received after non-prime claimed as DHE prime"); + conn->test_failure_reported = 1; + } +#endif /* CONFIG_TESTING_OPTIONS */ + if (ct != TLS_CONTENT_TYPE_HANDSHAKE) { - wpa_printf(MSG_DEBUG, "TLSv1: Expected Finished; " - "received content type 0x%x", ct); + tlsv1_server_log(conn, "Expected Finished; received content type 0x%x", + ct); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_UNEXPECTED_MESSAGE); return -1; @@ -1080,9 +1151,8 @@ static int tls_process_client_finished(struct tlsv1_server *conn, u8 ct, left = *in_len; if (left < 4) { - wpa_printf(MSG_DEBUG, "TLSv1: Too short record (left=%lu) for " - "Finished", - (unsigned long) left); + tlsv1_server_log(conn, "Too short record (left=%lu) forFinished", + (unsigned long) left); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; @@ -1102,18 +1172,16 @@ static int tls_process_client_finished(struct tlsv1_server *conn, u8 ct, left -= 4; if (len > left) { - wpa_printf(MSG_DEBUG, "TLSv1: Too short buffer for Finished " - "(len=%lu > left=%lu)", - (unsigned long) len, (unsigned long) left); + tlsv1_server_log(conn, "Too short buffer for Finished (len=%lu > left=%lu)", + (unsigned long) len, (unsigned long) left); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; } end = pos + len; if (len != TLS_VERIFY_DATA_LEN) { - wpa_printf(MSG_DEBUG, "TLSv1: Unexpected verify_data length " - "in Finished: %lu (expected %d)", - (unsigned long) len, TLS_VERIFY_DATA_LEN); + tlsv1_server_log(conn, "Unexpected verify_data length in Finished: %lu (expected %d)", + (unsigned long) len, TLS_VERIFY_DATA_LEN); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; @@ -1125,10 +1193,10 @@ static int tls_process_client_finished(struct tlsv1_server *conn, u8 ct, if (conn->rl.tls_version >= TLS_VERSION_1_2) { hlen = SHA256_MAC_LEN; if (conn->verify.sha256_client == NULL || - crypto_hash_finish(conn->verify.sha256_client, hash, &hlen) - < 0) { + crypto_hash_finish(conn->verify.sha256_client, hash, &hlen) + < 0) { tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, - TLS_ALERT_INTERNAL_ERROR); + TLS_ALERT_INTERNAL_ERROR); conn->verify.sha256_client = NULL; return -1; } @@ -1175,19 +1243,19 @@ static int tls_process_client_finished(struct tlsv1_server *conn, u8 ct, wpa_hexdump_key(MSG_DEBUG, "TLSv1: verify_data (client)", verify_data, TLS_VERIFY_DATA_LEN); - if (os_memcmp(pos, verify_data, TLS_VERIFY_DATA_LEN) != 0) { - wpa_printf(MSG_INFO, "TLSv1: Mismatch in verify_data"); + if (os_memcmp_const(pos, verify_data, TLS_VERIFY_DATA_LEN) != 0) { + tlsv1_server_log(conn, "Mismatch in verify_data"); + conn->state = FAILED; return -1; } - wpa_printf(MSG_DEBUG, "TLSv1: Received Finished"); + tlsv1_server_log(conn, "Received Finished"); *in_len = end - in_data; if (conn->use_session_ticket) { /* Abbreviated handshake using session ticket; RFC 4507 */ - wpa_printf(MSG_DEBUG, "TLSv1: Abbreviated handshake completed " - "successfully"); + tlsv1_server_log(conn, "Abbreviated handshake completed successfully"); conn->state = ESTABLISHED; } else { /* Full handshake */ @@ -1203,13 +1271,12 @@ int tlsv1_server_process_handshake(struct tlsv1_server *conn, u8 ct, { if (ct == TLS_CONTENT_TYPE_ALERT) { if (*len < 2) { - wpa_printf(MSG_DEBUG, "TLSv1: Alert underflow"); + tlsv1_server_log(conn, "Alert underflow"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR); return -1; } - wpa_printf(MSG_DEBUG, "TLSv1: Received alert %d:%d", - buf[0], buf[1]); + tlsv1_server_log(conn, "Received alert %d:%d", buf[0], buf[1]); *len = 2; conn->state = FAILED; return -1; @@ -1241,9 +1308,8 @@ int tlsv1_server_process_handshake(struct tlsv1_server *conn, u8 ct, return -1; break; default: - wpa_printf(MSG_DEBUG, "TLSv1: Unexpected state %d " - "while processing received message", - conn->state); + tlsv1_server_log(conn, "Unexpected state %d while processing received message", + conn->state); return -1; } diff --git a/components/wpa_supplicant/src/tls/tlsv1_server_write.c b/components/wpa_supplicant/src/tls/tlsv1_server_write.c index 3d393bc9c7..8d36cf1353 100644 --- a/components/wpa_supplicant/src/tls/tlsv1_server_write.c +++ b/components/wpa_supplicant/src/tls/tlsv1_server_write.c @@ -1,33 +1,32 @@ /* * TLSv1 server - write handshake message - * Copyright (c) 2006-2011, Jouni Malinen + * Copyright (c) 2006-2014, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ -#include "utils/includes.h" +#include "includes.h" -#include "utils/common.h" +#include "common.h" #include "crypto/md5.h" #include "crypto/sha1.h" #include "crypto/sha256.h" +#include "crypto/tls.h" #include "crypto/random.h" -#include "tls/tls.h" -#include "tls/x509v3.h" -#include "tls/tlsv1_common.h" -#include "tls/tlsv1_record.h" -#include "tls/tlsv1_server.h" -#include "tls/tlsv1_server_i.h" +#include "x509v3.h" +#include "tlsv1_common.h" +#include "tlsv1_record.h" +#include "tlsv1_server.h" +#include "tlsv1_server_i.h" -#include "eap_peer/eap_i.h" static size_t tls_server_cert_chain_der_len(struct tlsv1_server *conn) { size_t len = 0; struct x509_certificate *cert; - cert = conn->cred->cert; + cert = conn->cred ? conn->cred->cert : NULL; while (cert) { len += 3 + cert->cert_len; if (x509_certificate_self_signed(cert)) @@ -43,17 +42,20 @@ static size_t tls_server_cert_chain_der_len(struct tlsv1_server *conn) static int tls_write_server_hello(struct tlsv1_server *conn, u8 **msgpos, u8 *end) { - u8 *pos, *rhdr, *hs_start, *hs_length; + u8 *pos, *rhdr, *hs_start, *hs_length, *ext_start; struct os_time now; size_t rlen; pos = *msgpos; - wpa_printf(MSG_DEBUG, "TLSv1: Send ServerHello"); + tlsv1_server_log(conn, "Send ServerHello"); rhdr = pos; pos += TLS_RECORD_HEADER_LEN; os_get_time(&now); +#ifdef TEST_FUZZ + now.sec = 0xfffefdfc; +#endif /* TEST_FUZZ */ WPA_PUT_BE32(conn->server_random, now.sec); if (random_get_bytes(conn->server_random + 4, TLS_RANDOM_LEN - 4)) { wpa_printf(MSG_ERROR, "TLSv1: Could not generate " @@ -98,6 +100,32 @@ static int tls_write_server_hello(struct tlsv1_server *conn, /* CompressionMethod compression_method */ *pos++ = TLS_COMPRESSION_NULL; + /* Extension */ + ext_start = pos; + pos += 2; + + if (conn->status_request) { + /* Add a status_request extension with empty extension_data */ + /* ExtensionsType extension_type = status_request(5) */ + WPA_PUT_BE16(pos, TLS_EXT_STATUS_REQUEST); + pos += 2; + /* opaque extension_data<0..2^16-1> length */ + WPA_PUT_BE16(pos, 0); + pos += 2; + } + + if (conn->status_request_v2) { + /* + Add a status_request_v2 extension with empty extension_data + */ + /* ExtensionsType extension_type = status_request_v2(17) */ + WPA_PUT_BE16(pos, TLS_EXT_STATUS_REQUEST_V2); + pos += 2; + /* opaque extension_data<0..2^16-1> length */ + WPA_PUT_BE16(pos, 0); + pos += 2; + } + if (conn->session_ticket && conn->session_ticket_cb) { int res = conn->session_ticket_cb( conn->session_ticket_cb_ctx, @@ -105,8 +133,7 @@ static int tls_write_server_hello(struct tlsv1_server *conn, conn->client_random, conn->server_random, conn->master_secret); if (res < 0) { - wpa_printf(MSG_DEBUG, "TLSv1: SessionTicket callback " - "indicated failure"); + tlsv1_server_log(conn, "SessionTicket callback indicated failure"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_HANDSHAKE_FAILURE); return -1; @@ -135,6 +162,11 @@ static int tls_write_server_hello(struct tlsv1_server *conn, */ } + if (pos == ext_start + 2) + pos -= 2; /* no extensions */ + else + WPA_PUT_BE16(ext_start, pos - ext_start - 2); + WPA_PUT_BE24(hs_length, pos - hs_length - 3); tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start); @@ -170,8 +202,13 @@ static int tls_write_server_certificate(struct tlsv1_server *conn, } pos = *msgpos; + if (TLS_RECORD_HEADER_LEN + 1 + 3 + 3 > end - pos) { + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } - wpa_printf(MSG_DEBUG, "TLSv1: Send Certificate"); + tlsv1_server_log(conn, "Send Certificate"); rhdr = pos; pos += TLS_RECORD_HEADER_LEN; @@ -190,7 +227,7 @@ static int tls_write_server_certificate(struct tlsv1_server *conn, pos += 3; cert = conn->cred->cert; while (cert) { - if (pos + 3 + cert->cert_len > end) { + if (3 + cert->cert_len > (size_t) (end - pos)) { wpa_printf(MSG_DEBUG, "TLSv1: Not enough buffer space " "for Certificate (cert_len=%lu left=%lu)", (unsigned long) cert->cert_len, @@ -241,15 +278,104 @@ static int tls_write_server_certificate(struct tlsv1_server *conn, } +static int tls_write_server_certificate_status(struct tlsv1_server *conn, + u8 **msgpos, u8 *end, + int ocsp_multi, + char *ocsp_resp, + size_t ocsp_resp_len) +{ + u8 *pos, *rhdr, *hs_start, *hs_length; + size_t rlen; + + if (!ocsp_resp) { + /* + * Client did not request certificate status or there is no + * matching response cached. + */ + return 0; + } + + pos = *msgpos; + if (TLS_RECORD_HEADER_LEN + 1 + 3 + 1 + 3 + ocsp_resp_len > + (unsigned int) (end - pos)) { + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + + tlsv1_server_log(conn, "Send CertificateStatus (multi=%d)", ocsp_multi); + rhdr = pos; + pos += TLS_RECORD_HEADER_LEN; + + /* opaque fragment[TLSPlaintext.length] */ + + /* Handshake */ + hs_start = pos; + /* HandshakeType msg_type */ + *pos++ = TLS_HANDSHAKE_TYPE_CERTIFICATE_STATUS; + /* uint24 length (to be filled) */ + hs_length = pos; + pos += 3; + + /* body - CertificateStatus + * + * struct { + * CertificateStatusType status_type; + * select (status_type) { + * case ocsp: OCSPResponse; + * case ocsp_multi: OCSPResponseList; + * } response; + * } CertificateStatus; + * + * opaque OCSPResponse<1..2^24-1>; + * + * struct { + * OCSPResponse ocsp_response_list<1..2^24-1>; + * } OCSPResponseList; + */ + + /* CertificateStatusType status_type */ + if (ocsp_multi) + *pos++ = 2; /* ocsp_multi(2) */ + else + *pos++ = 1; /* ocsp(1) */ + /* uint24 length of OCSPResponse */ + WPA_PUT_BE24(pos, ocsp_resp_len); + pos += 3; + os_memcpy(pos, ocsp_resp, ocsp_resp_len); + pos += ocsp_resp_len; + + WPA_PUT_BE24(hs_length, pos - hs_length - 3); + + if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE, + rhdr, end - rhdr, hs_start, pos - hs_start, + &rlen) < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + pos = rhdr + rlen; + + tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start); + + *msgpos = pos; + + return 0; +} + + static int tls_write_server_key_exchange(struct tlsv1_server *conn, u8 **msgpos, u8 *end) { tls_key_exchange keyx; const struct tls_cipher_suite *suite; - u8 *pos, *rhdr, *hs_start, *hs_length; + u8 *pos, *rhdr, *hs_start, *hs_length, *server_params; size_t rlen; u8 *dh_ys; size_t dh_ys_len; + const u8 *dh_p; + size_t dh_p_len; suite = tls_get_cipher_suite(conn->rl.cipher_suite); if (suite == NULL) @@ -262,8 +388,7 @@ static int tls_write_server_key_exchange(struct tlsv1_server *conn, return 0; } - if (keyx != TLS_KEY_X_DH_anon) { - /* TODO? */ + if (keyx != TLS_KEY_X_DH_anon && keyx != TLS_KEY_X_DHE_RSA) { wpa_printf(MSG_DEBUG, "TLSv1: ServerKeyExchange not yet " "supported with key exchange type %d", keyx); return -1; @@ -276,8 +401,10 @@ static int tls_write_server_key_exchange(struct tlsv1_server *conn, return -1; } + tlsv1_server_get_dh_p(conn, &dh_p, &dh_p_len); + os_free(conn->dh_secret); - conn->dh_secret_len = conn->cred->dh_p_len; + conn->dh_secret_len = dh_p_len; conn->dh_secret = os_malloc(conn->dh_secret_len); if (conn->dh_secret == NULL) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to allocate " @@ -296,8 +423,7 @@ static int tls_write_server_key_exchange(struct tlsv1_server *conn, return -1; } - if (os_memcmp(conn->dh_secret, conn->cred->dh_p, conn->dh_secret_len) > - 0) + if (os_memcmp(conn->dh_secret, dh_p, conn->dh_secret_len) > 0) conn->dh_secret[0] = 0; /* make sure secret < p */ pos = conn->dh_secret; @@ -312,7 +438,7 @@ static int tls_write_server_key_exchange(struct tlsv1_server *conn, conn->dh_secret, conn->dh_secret_len); /* Ys = g^secret mod p */ - dh_ys_len = conn->cred->dh_p_len; + dh_ys_len = dh_p_len; dh_ys = os_malloc(dh_ys_len); if (dh_ys == NULL) { wpa_printf(MSG_DEBUG, "TLSv1: Failed to allocate memory for " @@ -322,16 +448,14 @@ static int tls_write_server_key_exchange(struct tlsv1_server *conn, return -1; } if (crypto_mod_exp(conn->cred->dh_g, conn->cred->dh_g_len, - conn->dh_secret, conn->dh_secret_len, - conn->cred->dh_p, conn->cred->dh_p_len, - dh_ys, &dh_ys_len)) { + conn->dh_secret, conn->dh_secret_len, + dh_p, dh_p_len, dh_ys, &dh_ys_len)) { tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, - TLS_ALERT_INTERNAL_ERROR); + TLS_ALERT_INTERNAL_ERROR); os_free(dh_ys); return -1; } - wpa_hexdump(MSG_DEBUG, "TLSv1: DH Ys (server's public value)", dh_ys, dh_ys_len); @@ -356,7 +480,7 @@ static int tls_write_server_key_exchange(struct tlsv1_server *conn, pos = *msgpos; - wpa_printf(MSG_DEBUG, "TLSv1: Send ServerKeyExchange"); + tlsv1_server_log(conn, "Send ServerKeyExchange"); rhdr = pos; pos += TLS_RECORD_HEADER_LEN; @@ -371,8 +495,9 @@ static int tls_write_server_key_exchange(struct tlsv1_server *conn, pos += 3; /* body - ServerDHParams */ + server_params = pos; /* dh_p */ - if (pos + 2 + conn->cred->dh_p_len > end) { + if (2 + dh_p_len > (size_t) (end - pos)) { wpa_printf(MSG_DEBUG, "TLSv1: Not enough buffer space for " "dh_p"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, @@ -380,13 +505,13 @@ static int tls_write_server_key_exchange(struct tlsv1_server *conn, os_free(dh_ys); return -1; } - WPA_PUT_BE16(pos, conn->cred->dh_p_len); + WPA_PUT_BE16(pos, dh_p_len); pos += 2; - os_memcpy(pos, conn->cred->dh_p, conn->cred->dh_p_len); - pos += conn->cred->dh_p_len; + os_memcpy(pos, dh_p, dh_p_len); + pos += dh_p_len; /* dh_g */ - if (pos + 2 + conn->cred->dh_g_len > end) { + if (2 + conn->cred->dh_g_len > (size_t) (end - pos)) { wpa_printf(MSG_DEBUG, "TLSv1: Not enough buffer space for " "dh_g"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, @@ -400,7 +525,7 @@ static int tls_write_server_key_exchange(struct tlsv1_server *conn, pos += conn->cred->dh_g_len; /* dh_Ys */ - if (pos + 2 + dh_ys_len > end) { + if (2 + dh_ys_len > (size_t) (end - pos)) { wpa_printf(MSG_DEBUG, "TLSv1: Not enough buffer space for " "dh_Ys"); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, @@ -414,6 +539,139 @@ static int tls_write_server_key_exchange(struct tlsv1_server *conn, pos += dh_ys_len; os_free(dh_ys); + /* + * select (SignatureAlgorithm) + * { case anonymous: struct { }; + * case rsa: + * digitally-signed struct { + * opaque md5_hash[16]; + * opaque sha_hash[20]; + * }; + * case dsa: + * digitally-signed struct { + * opaque sha_hash[20]; + * }; + * } Signature; + * + * md5_hash + * MD5(ClientHello.random + ServerHello.random + ServerParams); + * + * sha_hash + * SHA(ClientHello.random + ServerHello.random + ServerParams); + */ + + if (keyx == TLS_KEY_X_DHE_RSA) { + u8 hash[100]; + u8 *signed_start; + size_t clen; + int hlen; + + if (conn->rl.tls_version >= TLS_VERSION_1_2) { +#ifdef CONFIG_TLSV12 + hlen = tlsv12_key_x_server_params_hash( + conn->rl.tls_version, TLS_HASH_ALG_SHA256, + conn->client_random, + conn->server_random, server_params, + pos - server_params, hash + 19); + + /* + * RFC 5246, 4.7: + * TLS v1.2 adds explicit indication of the used + * signature and hash algorithms. + * + * struct { + * HashAlgorithm hash; + * SignatureAlgorithm signature; + * } SignatureAndHashAlgorithm; + */ + if (hlen < 0 || end - pos < 2) { + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + *pos++ = TLS_HASH_ALG_SHA256; + *pos++ = TLS_SIGN_ALG_RSA; + + /* + * RFC 3447, A.2.4 RSASSA-PKCS1-v1_5 + * + * DigestInfo ::= SEQUENCE { + * digestAlgorithm DigestAlgorithm, + * digest OCTET STRING + * } + * + * SHA-256 OID: sha256WithRSAEncryption ::= {pkcs-1 11} + * + * DER encoded DigestInfo for SHA256 per RFC 3447: + * 30 31 30 0d 06 09 60 86 48 01 65 03 04 02 01 05 00 + * 04 20 || H + */ + hlen += 19; + os_memcpy(hash, + "\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01\x65" + "\x03\x04\x02\x01\x05\x00\x04\x20", 19); + +#else /* CONFIG_TLSV12 */ + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; +#endif /* CONFIG_TLSV12 */ + } else { + hlen = tls_key_x_server_params_hash( + conn->rl.tls_version, conn->client_random, + conn->server_random, server_params, + pos - server_params, hash); + } + + if (hlen < 0) { + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + + wpa_hexdump(MSG_MSGDUMP, "TLS: ServerKeyExchange signed_params hash", + hash, hlen); +#ifdef CONFIG_TESTING_OPTIONS + if (conn->test_flags & TLS_BREAK_SRV_KEY_X_HASH) { + tlsv1_server_log(conn, "TESTING: Break ServerKeyExchange signed params hash"); + hash[hlen - 1] ^= 0x80; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + /* + * RFC 2246, 4.7: + * In digital signing, one-way hash functions are used as input + * for a signing algorithm. A digitally-signed element is + * encoded as an opaque vector <0..2^16-1>, where the length is + * specified by the signing algorithm and key. + * + * In RSA signing, a 36-byte structure of two hashes (one SHA + * and one MD5) is signed (encrypted with the private key). It + * is encoded with PKCS #1 block type 0 or type 1 as described + * in [PKCS1]. + */ + signed_start = pos; /* length to be filled */ + pos += 2; + clen = end - pos; + if (conn->cred == NULL || + crypto_private_key_sign_pkcs1(conn->cred->key, hash, hlen, + pos, &clen) < 0) { + wpa_printf(MSG_DEBUG, "TLSv1: Failed to sign hash (PKCS #1)"); + tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, + TLS_ALERT_INTERNAL_ERROR); + return -1; + } + WPA_PUT_BE16(signed_start, clen); +#ifdef CONFIG_TESTING_OPTIONS + if (conn->test_flags & TLS_BREAK_SRV_KEY_X_SIGNATURE) { + tlsv1_server_log(conn, "TESTING: Break ServerKeyExchange signed params signature"); + pos[clen - 1] ^= 0x80; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + pos += clen; + } + WPA_PUT_BE24(hs_length, pos - hs_length - 3); if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE, @@ -447,7 +705,7 @@ static int tls_write_server_certificate_request(struct tlsv1_server *conn, pos = *msgpos; - wpa_printf(MSG_DEBUG, "TLSv1: Send CertificateRequest"); + tlsv1_server_log(conn, "Send CertificateRequest"); rhdr = pos; pos += TLS_RECORD_HEADER_LEN; @@ -507,7 +765,7 @@ static int tls_write_server_hello_done(struct tlsv1_server *conn, size_t rlen; u8 payload[4]; - wpa_printf(MSG_DEBUG, "TLSv1: Send ServerHelloDone"); + tlsv1_server_log(conn, "Send ServerHelloDone"); /* opaque fragment[TLSPlaintext.length] */ @@ -543,7 +801,7 @@ static int tls_write_server_change_cipher_spec(struct tlsv1_server *conn, size_t rlen; u8 payload[1]; - wpa_printf(MSG_DEBUG, "TLSv1: Send ChangeCipherSpec"); + tlsv1_server_log(conn, "Send ChangeCipherSpec"); payload[0] = TLS_CHANGE_CIPHER_SPEC; @@ -580,7 +838,7 @@ static int tls_write_server_finished(struct tlsv1_server *conn, pos = *msgpos; - wpa_printf(MSG_DEBUG, "TLSv1: Send Finished"); + tlsv1_server_log(conn, "Send Finished"); /* Encrypted Handshake Message: Finished */ @@ -588,11 +846,11 @@ static int tls_write_server_finished(struct tlsv1_server *conn, if (conn->rl.tls_version >= TLS_VERSION_1_2) { hlen = SHA256_MAC_LEN; if (conn->verify.sha256_server == NULL || - crypto_hash_finish(conn->verify.sha256_server, hash, &hlen) - < 0) { + crypto_hash_finish(conn->verify.sha256_server, hash, &hlen) + < 0) { conn->verify.sha256_server = NULL; tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, - TLS_ALERT_INTERNAL_ERROR); + TLS_ALERT_INTERNAL_ERROR); return -1; } conn->verify.sha256_server = NULL; @@ -637,6 +895,12 @@ static int tls_write_server_finished(struct tlsv1_server *conn, } wpa_hexdump_key(MSG_DEBUG, "TLSv1: verify_data (server)", verify_data + 1 + 3, TLS_VERIFY_DATA_LEN); +#ifdef CONFIG_TESTING_OPTIONS + if (conn->test_flags & TLS_BREAK_VERIFY_DATA) { + tlsv1_server_log(conn, "TESTING: Break verify_data (server)"); + verify_data[1 + 3 + 1] ^= 0x80; + } +#endif /* CONFIG_TESTING_OPTIONS */ /* Handshake */ pos = hs_start = verify_data; @@ -667,24 +931,46 @@ static u8 * tls_send_server_hello(struct tlsv1_server *conn, size_t *out_len) { u8 *msg, *end, *pos; size_t msglen; + int ocsp_multi = 0; + char *ocsp_resp = NULL; + size_t ocsp_resp_len = 0; *out_len = 0; - msglen = 1000 + tls_server_cert_chain_der_len(conn); + if (conn->status_request_multi && + conn->cred->ocsp_stapling_response_multi) { + ocsp_resp = os_readfile( + conn->cred->ocsp_stapling_response_multi, + &ocsp_resp_len); + ocsp_multi = 1; + } else if ((conn->status_request || conn->status_request_v2) && + conn->cred->ocsp_stapling_response) { + ocsp_resp = os_readfile(conn->cred->ocsp_stapling_response, + &ocsp_resp_len); + } + if (!ocsp_resp) + ocsp_resp_len = 0; + + msglen = 1000 + tls_server_cert_chain_der_len(conn) + ocsp_resp_len; msg = os_malloc(msglen); - if (msg == NULL) + if (msg == NULL) { + os_free(ocsp_resp); return NULL; + } pos = msg; end = msg + msglen; if (tls_write_server_hello(conn, &pos, end) < 0) { os_free(msg); + os_free(ocsp_resp); return NULL; } if (conn->use_session_ticket) { + os_free(ocsp_resp); + /* Abbreviated handshake using session ticket; RFC 4507 */ if (tls_write_server_change_cipher_spec(conn, &pos, end) < 0 || tls_write_server_finished(conn, &pos, end) < 0) { @@ -701,12 +987,16 @@ static u8 * tls_send_server_hello(struct tlsv1_server *conn, size_t *out_len) /* Full handshake */ if (tls_write_server_certificate(conn, &pos, end) < 0 || + tls_write_server_certificate_status(conn, &pos, end, ocsp_multi, + ocsp_resp, ocsp_resp_len) < 0 || tls_write_server_key_exchange(conn, &pos, end) < 0 || tls_write_server_certificate_request(conn, &pos, end) < 0 || tls_write_server_hello_done(conn, &pos, end) < 0) { os_free(msg); + os_free(ocsp_resp); return NULL; } + os_free(ocsp_resp); *out_len = pos - msg; @@ -738,7 +1028,7 @@ static u8 * tls_send_change_cipher_spec(struct tlsv1_server *conn, *out_len = pos - msg; - wpa_printf(MSG_DEBUG, "TLSv1: Handshake completed successfully"); + tlsv1_server_log(conn, "Handshake completed successfully"); conn->state = ESTABLISHED; return msg; @@ -757,8 +1047,8 @@ u8 * tlsv1_server_handshake_write(struct tlsv1_server *conn, size_t *out_len) /* Abbreviated handshake was already completed. */ return NULL; } - wpa_printf(MSG_DEBUG, "TLSv1: Unexpected state %d while " - "generating reply", conn->state); + tlsv1_server_log(conn, "Unexpected state %d while generating reply", + conn->state); return NULL; } } @@ -769,7 +1059,7 @@ u8 * tlsv1_server_send_alert(struct tlsv1_server *conn, u8 level, { u8 *alert, *pos, *length; - wpa_printf(MSG_DEBUG, "TLSv1: Send Alert(%d:%d)", level, description); + tlsv1_server_log(conn, "Send Alert(%d:%d)", level, description); *out_len = 0; alert = os_malloc(10); diff --git a/components/wpa_supplicant/src/tls/x509v3.c b/components/wpa_supplicant/src/tls/x509v3.c index 4d99c72ccd..1bbd80ade8 100644 --- a/components/wpa_supplicant/src/tls/x509v3.c +++ b/components/wpa_supplicant/src/tls/x509v3.c @@ -1,21 +1,20 @@ /* * X.509v3 certificate parsing and processing (RFC 3280 profile) - * Copyright (c) 2006-2011, Jouni Malinen + * Copyright (c) 2006-2015, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. */ -#include "utils/includes.h" -#include "rsn_supp/wpa.h" -#include "utils/common.h" +#include "includes.h" + +#include "common.h" #include "crypto/crypto.h" -#include "tls/asn1.h" -#include "tls/x509v3.h" +#include "asn1.h" +#include "x509v3.h" -#include "eap_peer/eap_i.h" -static void x509_free_name(struct x509_name *name) +void x509_free_name(struct x509_name *name) { size_t i; @@ -56,6 +55,7 @@ void x509_certificate_free(struct x509_certificate *cert) x509_free_name(&cert->subject); os_free(cert->public_key); os_free(cert->sign_value); + os_free(cert->subject_dn); os_free(cert); } @@ -123,13 +123,13 @@ static int x509_str_compare(const char *a, const char *b) if (aa == NULL || bb == NULL) { os_free(aa); os_free(bb); - return strcasecmp(a, b); + return os_strcasecmp(a, b); } x509_str_strip_whitespace(aa); x509_str_strip_whitespace(bb); - ret = strcasecmp(aa, bb); + ret = os_strcasecmp(aa, bb); os_free(aa); os_free(bb); @@ -178,9 +178,9 @@ int x509_name_compare(struct x509_name *a, struct x509_name *b) } -static int x509_parse_algorithm_identifier( - const u8 *buf, size_t len, - struct x509_algorithm_identifier *id, const u8 **next) +int x509_parse_algorithm_identifier(const u8 *buf, size_t len, + struct x509_algorithm_identifier *id, + const u8 **next) { struct asn1_hdr hdr; const u8 *pos, *end; @@ -192,20 +192,16 @@ static int x509_parse_algorithm_identifier( * } */ - if (asn1_get_next(buf, len, &hdr) < 0 || - hdr.class != ASN1_CLASS_UNIVERSAL || - hdr.tag != ASN1_TAG_SEQUENCE) { - wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE " - "(AlgorithmIdentifier) - found class %d tag 0x%x", - hdr.class, hdr.tag); + if (asn1_get_next(buf, len, &hdr) < 0 || !asn1_is_sequence(&hdr)) { + asn1_unexpected(&hdr, + "X509: Expected SEQUENCE (AlgorithmIdentifier)"); return -1; } + if (hdr.length > buf + len - hdr.payload) + return -1; pos = hdr.payload; end = pos + hdr.length; - if (end > buf + len) - return -1; - *next = end; if (asn1_get_oid(pos, end - pos, &id->oid, &pos)) @@ -235,16 +231,14 @@ static int x509_parse_public_key(const u8 *buf, size_t len, end = buf + len; if (asn1_get_next(pos, end - pos, &hdr) < 0 || - hdr.class != ASN1_CLASS_UNIVERSAL || - hdr.tag != ASN1_TAG_SEQUENCE) { - wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE " - "(SubjectPublicKeyInfo) - found class %d tag 0x%x", - hdr.class, hdr.tag); + !asn1_is_sequence(&hdr)) { + asn1_unexpected(&hdr, + "X509: Expected SEQUENCE (SubjectPublicKeyInfo)"); return -1; } pos = hdr.payload; - if (pos + hdr.length > end) + if (hdr.length > end - pos) return -1; end = pos + hdr.length; *next = end; @@ -254,18 +248,17 @@ static int x509_parse_public_key(const u8 *buf, size_t len, return -1; if (asn1_get_next(pos, end - pos, &hdr) < 0 || - hdr.class != ASN1_CLASS_UNIVERSAL || - hdr.tag != ASN1_TAG_BITSTRING) { - wpa_printf(MSG_DEBUG, "X509: Expected BITSTRING " - "(subjectPublicKey) - found class %d tag 0x%x", - hdr.class, hdr.tag); + !asn1_is_bitstring(&hdr)) { + asn1_unexpected(&hdr, + "X509: Expected BITSTRING (subjectPublicKey)"); return -1; } if (hdr.length < 1) return -1; pos = hdr.payload; if (*pos) { - wpa_printf(MSG_DEBUG, "X509: BITSTRING - %d unused bits", + wpa_printf(MSG_DEBUG, + "X509: BITSTRING (subjectPublicKey) - %d unused bits", *pos); /* * TODO: should this be rejected? X.509 certificates are @@ -275,13 +268,12 @@ static int x509_parse_public_key(const u8 *buf, size_t len, */ } os_free(cert->public_key); - cert->public_key = os_malloc(hdr.length - 1); + cert->public_key = os_memdup(pos + 1, hdr.length - 1); if (cert->public_key == NULL) { wpa_printf(MSG_DEBUG, "X509: Failed to allocate memory for " "public key"); return -1; } - os_memcpy(cert->public_key, pos + 1, hdr.length - 1); cert->public_key_len = hdr.length - 1; wpa_hexdump(MSG_MSGDUMP, "X509: subjectPublicKey", cert->public_key, cert->public_key_len); @@ -290,8 +282,8 @@ static int x509_parse_public_key(const u8 *buf, size_t len, } -static int x509_parse_name(const u8 *buf, size_t len, struct x509_name *name, - const u8 **next) +int x509_parse_name(const u8 *buf, size_t len, struct x509_name *name, + const u8 **next) { struct asn1_hdr hdr; const u8 *pos, *end, *set_pos, *set_end, *seq_pos, *seq_end; @@ -310,17 +302,14 @@ static int x509_parse_name(const u8 *buf, size_t len, struct x509_name *name, * AttributeValue ::= ANY DEFINED BY AttributeType */ - if (asn1_get_next(buf, len, &hdr) < 0 || - hdr.class != ASN1_CLASS_UNIVERSAL || - hdr.tag != ASN1_TAG_SEQUENCE) { - wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE " - "(Name / RDNSequencer) - found class %d tag 0x%x", - hdr.class, hdr.tag); + if (asn1_get_next(buf, len, &hdr) < 0 || !asn1_is_sequence(&hdr)) { + asn1_unexpected(&hdr, + "X509: Expected SEQUENCE (Name / RDNSequencer)"); return -1; } pos = hdr.payload; - if (pos + hdr.length > buf + len) + if (hdr.length > buf + len - pos) return -1; end = *next = pos + hdr.length; @@ -329,11 +318,9 @@ static int x509_parse_name(const u8 *buf, size_t len, struct x509_name *name, enum x509_name_attr_type type; if (asn1_get_next(pos, end - pos, &hdr) < 0 || - hdr.class != ASN1_CLASS_UNIVERSAL || - hdr.tag != ASN1_TAG_SET) { - wpa_printf(MSG_DEBUG, "X509: Expected SET " - "(RelativeDistinguishedName) - found class " - "%d tag 0x%x", hdr.class, hdr.tag); + !asn1_is_set(&hdr)) { + asn1_unexpected(&hdr, + "X509: Expected SET (RelativeDistinguishedName)"); x509_free_name(name); return -1; } @@ -342,11 +329,9 @@ static int x509_parse_name(const u8 *buf, size_t len, struct x509_name *name, pos = set_end = hdr.payload + hdr.length; if (asn1_get_next(set_pos, set_end - set_pos, &hdr) < 0 || - hdr.class != ASN1_CLASS_UNIVERSAL || - hdr.tag != ASN1_TAG_SEQUENCE) { - wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE " - "(AttributeTypeAndValue) - found class %d " - "tag 0x%x", hdr.class, hdr.tag); + !asn1_is_sequence(&hdr)) { + asn1_unexpected(&hdr, + "X509: Expected SEQUENCE (AttributeTypeAndValue)"); x509_free_name(name); return -1; } @@ -367,6 +352,13 @@ static int x509_parse_name(const u8 *buf, size_t len, struct x509_name *name, return -1; } + if (!asn1_is_string_type(&hdr)) { + wpa_printf(MSG_DEBUG, + "X509: Ignore non-string type attribute (tag 0x%x)", + hdr.tag); + continue; + } + /* RFC 3280: * MUST: country, organization, organizational-unit, * distinguished name qualifier, state or province name, @@ -444,7 +436,7 @@ static int x509_parse_name(const u8 *buf, size_t len, struct x509_name *name, return -1; } - val = (char *)dup_binstr(hdr.payload, hdr.length); + val = dup_binstr(hdr.payload, hdr.length); if (val == NULL) { x509_free_name(name); return -1; @@ -469,7 +461,6 @@ static int x509_parse_name(const u8 *buf, size_t len, struct x509_name *name, static char * x509_name_attr_str(enum x509_name_attr_type type) { -#ifndef ESPRESSIF_USE switch (type) { case X509_NAME_ATTR_NOT_USED: return "[N/A]"; @@ -489,38 +480,6 @@ static char * x509_name_attr_str(enum x509_name_attr_type type) return "OU"; } return "?"; -#else - static char name_attr[6]; - switch (type) { - case X509_NAME_ATTR_NOT_USED: - strcpy(name_attr, "[N/A]"); - break; - case X509_NAME_ATTR_DC: - strcpy(name_attr, "DC"); - break; - case X509_NAME_ATTR_CN: - strcpy(name_attr, "CN"); - break; - case X509_NAME_ATTR_C: - strcpy(name_attr, "C"); - break; - case X509_NAME_ATTR_L: - strcpy(name_attr, "L"); - break; - case X509_NAME_ATTR_ST: - strcpy(name_attr, "ST"); - break; - case X509_NAME_ATTR_O: - strcpy(name_attr, "O"); - break; - case X509_NAME_ATTR_OU: - strcpy(name_attr, "OU"); - break; - default : - strcpy(name_attr, "?"); - } - return name_attr; -#endif } @@ -546,7 +505,7 @@ void x509_name_string(struct x509_name *name, char *buf, size_t len) ret = os_snprintf(pos, end - pos, "%s=%s, ", x509_name_attr_str(name->attr[i].type), name->attr[i].value); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) goto done; pos += ret; } @@ -561,19 +520,169 @@ void x509_name_string(struct x509_name *name, char *buf, size_t len) if (name->email) { ret = os_snprintf(pos, end - pos, "/emailAddress=%s", name->email); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) goto done; pos += ret; } done: + if (pos < end) + *pos = '\0'; end[-1] = '\0'; } -static int x509_parse_time(const u8 *buf, size_t len, u8 asn1_tag, - os_time_t *val) +static int parse_uint2(const char *pos, size_t len) { + char buf[2] = {0}; + int ret = 0; + int i; + + if (len < 2) + return -1; + + for (i = 0; i < 2; i++) { + buf[0] = pos[i]; + ret *= 10; + ret += atoi(buf); + } + + return ret; +} + + +static int parse_uint4(const char *pos, size_t len) +{ + char buf[2] = {0}; + int ret = 0; + int i; + + if (len < 4) + return -1; + + for (i = 0; i < 4; i++) { + buf[0] = pos[i]; + ret *= 10; + ret += atoi(buf); + } + + return ret; +} + + +int x509_parse_time(const u8 *buf, size_t len, u8 asn1_tag, os_time_t *val) +{ + const char *pos, *end; + int year, month, day, hour, min, sec; + + /* + * Time ::= CHOICE { + * utcTime UTCTime, + * generalTime GeneralizedTime + * } + * + * UTCTime: YYMMDDHHMMSSZ + * GeneralizedTime: YYYYMMDDHHMMSSZ + */ + + pos = (const char *) buf; + end = pos + len; + + switch (asn1_tag) { + case ASN1_TAG_UTCTIME: + if (len != 13 || buf[12] != 'Z') { + wpa_hexdump_ascii(MSG_DEBUG, "X509: Unrecognized " + "UTCTime format", buf, len); + return -1; + } + year = parse_uint2(pos, end - pos); + if (year < 0) { + wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse " + "UTCTime year", buf, len); + return -1; + } + if (year < 50) + year += 2000; + else + year += 1900; + pos += 2; + break; + case ASN1_TAG_GENERALIZEDTIME: + if (len != 15 || buf[14] != 'Z') { + wpa_hexdump_ascii(MSG_DEBUG, "X509: Unrecognized " + "GeneralizedTime format", buf, len); + return -1; + } + year = parse_uint4(pos, end - pos); + if (year < 0) { + wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse " + "GeneralizedTime year", buf, len); + return -1; + } + pos += 4; + break; + default: + wpa_printf(MSG_DEBUG, "X509: Expected UTCTime or " + "GeneralizedTime - found tag 0x%x", asn1_tag); + return -1; + } + + month = parse_uint2(pos, end - pos); + if (month < 0) { + wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse Time " + "(month)", buf, len); + return -1; + } + pos += 2; + + day = parse_uint2(pos, end - pos); + if (day < 0) { + wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse Time " + "(day)", buf, len); + return -1; + } + pos += 2; + + hour = parse_uint2(pos, end - pos); + if (hour < 0) { + wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse Time " + "(hour)", buf, len); + return -1; + } + pos += 2; + + min = parse_uint2(pos, end - pos); + if (min < 0) { + wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse Time " + "(min)", buf, len); + return -1; + } + pos += 2; + + sec = parse_uint2(pos, end - pos); + if (sec < 0) { + wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse Time " + "(sec)", buf, len); + return -1; + } + + if (os_mktime(year, month, day, hour, min, sec, val) < 0) { + wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to convert Time", + buf, len); + if (year < 1970) { + /* + * At least some test certificates have been configured + * to use dates prior to 1970. Set the date to + * beginning of 1970 to handle these case. + */ + wpa_printf(MSG_DEBUG, "X509: Year=%d before epoch - " + "assume epoch as the time", year); + *val = 0; + return 0; + } + return -1; + } + return 0; } @@ -597,24 +706,20 @@ static int x509_parse_validity(const u8 *buf, size_t len, * validity dates in 2050 or later MUST be encoded as GeneralizedTime. */ - if (asn1_get_next(buf, len, &hdr) < 0 || - hdr.class != ASN1_CLASS_UNIVERSAL || - hdr.tag != ASN1_TAG_SEQUENCE) { - wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE " - "(Validity) - found class %d tag 0x%x", - hdr.class, hdr.tag); + if (asn1_get_next(buf, len, &hdr) < 0 || !asn1_is_sequence(&hdr)) { + asn1_unexpected(&hdr, "X509: Expected SEQUENCE (Validity)"); return -1; } pos = hdr.payload; plen = hdr.length; - if (pos + plen > buf + len) + if (plen > (size_t) (buf + len - pos)) return -1; *next = pos + plen; if (asn1_get_next(pos, plen, &hdr) < 0 || - hdr.class != ASN1_CLASS_UNIVERSAL || + (!asn1_is_utctime(&hdr) && !asn1_is_generalizedtime(&hdr)) || x509_parse_time(hdr.payload, hdr.length, hdr.tag, &cert->not_before) < 0) { wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse notBefore " @@ -626,7 +731,7 @@ static int x509_parse_validity(const u8 *buf, size_t len, plen = *next - pos; if (asn1_get_next(pos, plen, &hdr) < 0 || - hdr.class != ASN1_CLASS_UNIVERSAL || + (!asn1_is_utctime(&hdr) && !asn1_is_generalizedtime(&hdr)) || x509_parse_time(hdr.payload, hdr.length, hdr.tag, &cert->not_after) < 0) { wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse notAfter " @@ -634,7 +739,7 @@ static int x509_parse_validity(const u8 *buf, size_t len, return -1; } - wpa_printf(MSG_DEBUG, "X509: Validity: notBefore: %lu notAfter: %lu", + wpa_printf(MSG_MSGDUMP, "X509: Validity: notBefore: %lu notAfter: %lu", (unsigned long) cert->not_before, (unsigned long) cert->not_after); @@ -652,6 +757,15 @@ static int x509_id_ce_oid(struct asn1_oid *oid) } +static int x509_any_ext_key_usage_oid(struct asn1_oid *oid) +{ + return oid->len == 6 && + x509_id_ce_oid(oid) && + oid->oid[3] == 37 /* extKeyUsage */ && + oid->oid[4] == 0 /* anyExtendedKeyUsage */; +} + + static int x509_parse_ext_key_usage(struct x509_certificate *cert, const u8 *pos, size_t len) { @@ -670,13 +784,9 @@ static int x509_parse_ext_key_usage(struct x509_certificate *cert, * decipherOnly (8) } */ - if (asn1_get_next(pos, len, &hdr) < 0 || - hdr.class != ASN1_CLASS_UNIVERSAL || - hdr.tag != ASN1_TAG_BITSTRING || + if (asn1_get_next(pos, len, &hdr) < 0 || !asn1_is_bitstring(&hdr) || hdr.length < 1) { - wpa_printf(MSG_DEBUG, "X509: Expected BIT STRING in " - "KeyUsage; found %d tag 0x%x len %d", - hdr.class, hdr.tag, hdr.length); + asn1_unexpected(&hdr, "X509: Expected BIT STRING in KeyUsage"); return -1; } @@ -695,6 +805,7 @@ static int x509_parse_ext_basic_constraints(struct x509_certificate *cert, struct asn1_hdr hdr; unsigned long value; size_t left; + const u8 *end_seq; /* * BasicConstraints ::= SEQUENCE { @@ -702,12 +813,9 @@ static int x509_parse_ext_basic_constraints(struct x509_certificate *cert, * pathLenConstraint INTEGER (0..MAX) OPTIONAL } */ - if (asn1_get_next(pos, len, &hdr) < 0 || - hdr.class != ASN1_CLASS_UNIVERSAL || - hdr.tag != ASN1_TAG_SEQUENCE) { - wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE in " - "BasicConstraints; found %d tag 0x%x", - hdr.class, hdr.tag); + if (asn1_get_next(pos, len, &hdr) < 0 || !asn1_is_sequence(&hdr)) { + asn1_unexpected(&hdr, + "X509: Expected SEQUENCE in BasicConstraints"); return -1; } @@ -716,41 +824,33 @@ static int x509_parse_ext_basic_constraints(struct x509_certificate *cert, if (hdr.length == 0) return 0; - if (asn1_get_next(hdr.payload, hdr.length, &hdr) < 0 || - hdr.class != ASN1_CLASS_UNIVERSAL) { + end_seq = hdr.payload + hdr.length; + if (asn1_get_next(hdr.payload, hdr.length, &hdr) < 0) { wpa_printf(MSG_DEBUG, "X509: Failed to parse " "BasicConstraints"); return -1; } - if (hdr.tag == ASN1_TAG_BOOLEAN) { - if (hdr.length != 1) { - wpa_printf(MSG_DEBUG, "X509: Unexpected " - "Boolean length (%u) in BasicConstraints", - hdr.length); - return -1; - } + if (asn1_is_boolean(&hdr)) { cert->ca = hdr.payload[0]; - if (hdr.payload + hdr.length == pos + len) { + pos = hdr.payload + hdr.length; + if (pos >= end_seq) { + /* No optional pathLenConstraint */ wpa_printf(MSG_DEBUG, "X509: BasicConstraints - cA=%d", cert->ca); return 0; } - - if (asn1_get_next(hdr.payload + hdr.length, len - hdr.length, - &hdr) < 0 || - hdr.class != ASN1_CLASS_UNIVERSAL) { + if (asn1_get_next(pos, end_seq - pos, &hdr) < 0) { wpa_printf(MSG_DEBUG, "X509: Failed to parse " "BasicConstraints"); return -1; } } - if (hdr.tag != ASN1_TAG_INTEGER) { - wpa_printf(MSG_DEBUG, "X509: Expected INTEGER in " - "BasicConstraints; found class %d tag 0x%x", - hdr.class, hdr.tag); + if (!asn1_is_integer(&hdr)) { + asn1_unexpected(&hdr, + "X509: Expected INTEGER in BasicConstraints"); return -1; } @@ -780,7 +880,7 @@ static int x509_parse_alt_name_rfc8222(struct x509_name *name, /* rfc822Name IA5String */ wpa_hexdump_ascii(MSG_MSGDUMP, "X509: altName - rfc822Name", pos, len); os_free(name->alt_email); - name->alt_email = (char *)os_zalloc(len + 1); + name->alt_email = os_zalloc(len + 1); if (name->alt_email == NULL) return -1; os_memcpy(name->alt_email, pos, len); @@ -802,7 +902,7 @@ static int x509_parse_alt_name_dns(struct x509_name *name, /* dNSName IA5String */ wpa_hexdump_ascii(MSG_MSGDUMP, "X509: altName - dNSName", pos, len); os_free(name->dns); - name->dns = (char *)os_zalloc(len + 1); + name->dns = os_zalloc(len + 1); if (name->dns == NULL) return -1; os_memcpy(name->dns, pos, len); @@ -826,7 +926,7 @@ static int x509_parse_alt_name_uri(struct x509_name *name, "X509: altName - uniformResourceIdentifier", pos, len); os_free(name->uri); - name->uri = (char *)os_zalloc(len + 1); + name->uri = os_zalloc(len + 1); if (name->uri == NULL) return -1; os_memcpy(name->uri, pos, len); @@ -848,10 +948,9 @@ static int x509_parse_alt_name_ip(struct x509_name *name, /* iPAddress OCTET STRING */ wpa_hexdump(MSG_MSGDUMP, "X509: altName - iPAddress", pos, len); os_free(name->ip); - name->ip = os_malloc(len); + name->ip = os_memdup(pos, len); if (name->ip == NULL) return -1; - os_memcpy(name->ip, pos, len); name->ip_len = len; return 0; } @@ -867,7 +966,7 @@ static int x509_parse_alt_name_rid(struct x509_name *name, return -1; asn1_oid_to_str(&name->rid, buf, sizeof(buf)); - wpa_printf(MSG_DEBUG, "X509: altName - registeredID: %s", buf); + wpa_printf(MSG_MSGDUMP, "X509: altName - registeredID: %s", buf); return 0; } @@ -958,12 +1057,9 @@ static int x509_parse_ext_subject_alt_name(struct x509_certificate *cert, /* SubjectAltName ::= GeneralNames */ - if (asn1_get_next(pos, len, &hdr) < 0 || - hdr.class != ASN1_CLASS_UNIVERSAL || - hdr.tag != ASN1_TAG_SEQUENCE) { - wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE in " - "SubjectAltName; found %d tag 0x%x", - hdr.class, hdr.tag); + if (asn1_get_next(pos, len, &hdr) < 0 || !asn1_is_sequence(&hdr)) { + asn1_unexpected(&hdr, + "X509: Expected SEQUENCE in SubjectAltName"); return -1; } @@ -985,12 +1081,9 @@ static int x509_parse_ext_issuer_alt_name(struct x509_certificate *cert, /* IssuerAltName ::= GeneralNames */ - if (asn1_get_next(pos, len, &hdr) < 0 || - hdr.class != ASN1_CLASS_UNIVERSAL || - hdr.tag != ASN1_TAG_SEQUENCE) { - wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE in " - "IssuerAltName; found %d tag 0x%x", - hdr.class, hdr.tag); + if (asn1_get_next(pos, len, &hdr) < 0 || !asn1_is_sequence(&hdr)) { + asn1_unexpected(&hdr, + "X509: Expected SEQUENCE in IssuerAltName"); return -1; } @@ -1005,6 +1098,233 @@ static int x509_parse_ext_issuer_alt_name(struct x509_certificate *cert, } +static int x509_id_cert_policy_any_oid(struct asn1_oid *oid) +{ + return oid->len == 5 && + oid->oid[0] == 2 /* iso/itu-t */ && + oid->oid[1] == 5 /* X.500 Directory Services */ && + oid->oid[2] == 29 /* id-ce */ && + oid->oid[3] == 32 /* id-ce-certificate-policies */ && + oid->oid[4] == 0 /* anyPolicy */; +} + + +static int x509_id_wfa_oid(struct asn1_oid *oid) +{ + return oid->len >= 7 && + oid->oid[0] == 1 /* iso */ && + oid->oid[1] == 3 /* identified-organization */ && + oid->oid[2] == 6 /* dod */ && + oid->oid[3] == 1 /* internet */ && + oid->oid[4] == 4 /* private */ && + oid->oid[5] == 1 /* enterprise */ && + oid->oid[6] == 40808 /* WFA */; +} + + +static int x509_id_wfa_tod_oid(struct asn1_oid *oid) +{ + return oid->len >= 9 && + x509_id_wfa_oid(oid) && + oid->oid[7] == 1 && + oid->oid[8] == 3; +} + + +static int x509_id_wfa_tod_strict_oid(struct asn1_oid *oid) +{ + return oid->len == 10 && + x509_id_wfa_tod_oid(oid) && + oid->oid[9] == 1; +} + + +static int x509_id_wfa_tod_tofu_oid(struct asn1_oid *oid) +{ + return oid->len == 10 && + x509_id_wfa_tod_oid(oid) && + oid->oid[9] == 2; +} + + +static int x509_parse_ext_certificate_policies(struct x509_certificate *cert, + const u8 *pos, size_t len) +{ + struct asn1_hdr hdr; + const u8 *end; + + /* + * certificatePolicies ::= SEQUENCE SIZE (1..MAX) OF PolicyInformation + * + * PolicyInformation ::= SEQUENCE { + * policyIdentifier CertPolicyId, + * policyQualifiers SEQUENCE SIZE (1..MAX) OF + * PolicyQualifierInfo OPTIONAL } + * + * CertPolicyId ::= OBJECT IDENTIFIER + */ + + if (asn1_get_next(pos, len, &hdr) < 0 || !asn1_is_sequence(&hdr)) { + asn1_unexpected(&hdr, + "X509: Expected SEQUENCE (certificatePolicies)"); + return -1; + } + if (hdr.length > pos + len - hdr.payload) + return -1; + pos = hdr.payload; + end = pos + hdr.length; + + wpa_hexdump(MSG_MSGDUMP, "X509: certificatePolicies", pos, end - pos); + + while (pos < end) { + const u8 *pol_end; + struct asn1_oid oid; + char buf[80]; + + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + !asn1_is_sequence(&hdr)) { + asn1_unexpected(&hdr, + "X509: Expected SEQUENCE (PolicyInformation)"); + return -1; + } + if (hdr.length > end - hdr.payload) + return -1; + pos = hdr.payload; + pol_end = pos + hdr.length; + wpa_hexdump(MSG_MSGDUMP, "X509: PolicyInformation", + pos, pol_end - pos); + + if (asn1_get_oid(pos, pol_end - pos, &oid, &pos)) + return -1; + if (x509_id_cert_policy_any_oid(&oid)) { + os_strlcpy(buf, "anyPolicy-STRICT", sizeof(buf)); + cert->certificate_policy |= + X509_EXT_CERT_POLICY_ANY; + } else if (x509_id_wfa_tod_strict_oid(&oid)) { + os_strlcpy(buf, "TOD-STRICT", sizeof(buf)); + cert->certificate_policy |= + X509_EXT_CERT_POLICY_TOD_STRICT; + } else if (x509_id_wfa_tod_tofu_oid(&oid)) { + os_strlcpy(buf, "TOD-TOFU", sizeof(buf)); + cert->certificate_policy |= + X509_EXT_CERT_POLICY_TOD_TOFU; + } else { + asn1_oid_to_str(&oid, buf, sizeof(buf)); + } + wpa_printf(MSG_DEBUG, "policyIdentifier: %s", buf); + + pos = pol_end; + } + + cert->extensions_present |= X509_EXT_CERTIFICATE_POLICY; + + return 0; +} + + +static int x509_id_pkix_oid(struct asn1_oid *oid) +{ + return oid->len >= 7 && + oid->oid[0] == 1 /* iso */ && + oid->oid[1] == 3 /* identified-organization */ && + oid->oid[2] == 6 /* dod */ && + oid->oid[3] == 1 /* internet */ && + oid->oid[4] == 5 /* security */ && + oid->oid[5] == 5 /* mechanisms */ && + oid->oid[6] == 7 /* id-pkix */; +} + + +static int x509_id_kp_oid(struct asn1_oid *oid) +{ + /* id-kp */ + return oid->len >= 8 && + x509_id_pkix_oid(oid) && + oid->oid[7] == 3 /* id-kp */; +} + + +static int x509_id_kp_server_auth_oid(struct asn1_oid *oid) +{ + /* id-kp */ + return oid->len == 9 && + x509_id_kp_oid(oid) && + oid->oid[8] == 1 /* id-kp-serverAuth */; +} + + +static int x509_id_kp_client_auth_oid(struct asn1_oid *oid) +{ + /* id-kp */ + return oid->len == 9 && + x509_id_kp_oid(oid) && + oid->oid[8] == 2 /* id-kp-clientAuth */; +} + + +static int x509_id_kp_ocsp_oid(struct asn1_oid *oid) +{ + /* id-kp */ + return oid->len == 9 && + x509_id_kp_oid(oid) && + oid->oid[8] == 9 /* id-kp-OCSPSigning */; +} + + +static int x509_parse_ext_ext_key_usage(struct x509_certificate *cert, + const u8 *pos, size_t len) +{ + struct asn1_hdr hdr; + const u8 *end; + struct asn1_oid oid; + + /* + * ExtKeyUsageSyntax ::= SEQUENCE SIZE (1..MAX) OF KeyPurposeId + * + * KeyPurposeId ::= OBJECT IDENTIFIER + */ + + if (asn1_get_next(pos, len, &hdr) < 0 || !asn1_is_sequence(&hdr)) { + asn1_unexpected(&hdr, + "X509: Expected SEQUENCE (ExtKeyUsageSyntax)"); + return -1; + } + if (hdr.length > pos + len - hdr.payload) + return -1; + pos = hdr.payload; + end = pos + hdr.length; + + wpa_hexdump(MSG_MSGDUMP, "X509: ExtKeyUsageSyntax", pos, end - pos); + + while (pos < end) { + char buf[80]; + + if (asn1_get_oid(pos, end - pos, &oid, &pos)) + return -1; + if (x509_any_ext_key_usage_oid(&oid)) { + os_strlcpy(buf, "anyExtendedKeyUsage", sizeof(buf)); + cert->ext_key_usage |= X509_EXT_KEY_USAGE_ANY; + } else if (x509_id_kp_server_auth_oid(&oid)) { + os_strlcpy(buf, "id-kp-serverAuth", sizeof(buf)); + cert->ext_key_usage |= X509_EXT_KEY_USAGE_SERVER_AUTH; + } else if (x509_id_kp_client_auth_oid(&oid)) { + os_strlcpy(buf, "id-kp-clientAuth", sizeof(buf)); + cert->ext_key_usage |= X509_EXT_KEY_USAGE_CLIENT_AUTH; + } else if (x509_id_kp_ocsp_oid(&oid)) { + os_strlcpy(buf, "id-kp-OCSPSigning", sizeof(buf)); + cert->ext_key_usage |= X509_EXT_KEY_USAGE_OCSP; + } else { + asn1_oid_to_str(&oid, buf, sizeof(buf)); + } + wpa_printf(MSG_DEBUG, "ExtKeyUsage KeyPurposeId: %s", buf); + } + + cert->extensions_present |= X509_EXT_EXT_KEY_USAGE; + + return 0; +} + + static int x509_parse_extension_data(struct x509_certificate *cert, struct asn1_oid *oid, const u8 *pos, size_t len) @@ -1013,10 +1333,8 @@ static int x509_parse_extension_data(struct x509_certificate *cert, return 1; /* TODO: add other extensions required by RFC 3280, Ch 4.2: - * certificate policies (section 4.2.1.5) * name constraints (section 4.2.1.11) * policy constraints (section 4.2.1.12) - * extended key usage (section 4.2.1.13) * inhibit any-policy (section 4.2.1.15) */ switch (oid->oid[3]) { @@ -1028,6 +1346,10 @@ static int x509_parse_extension_data(struct x509_certificate *cert, return x509_parse_ext_issuer_alt_name(cert, pos, len); case 19: /* id-ce-basicConstraints */ return x509_parse_ext_basic_constraints(cert, pos, len); + case 32: /* id-ce-certificatePolicies */ + return x509_parse_ext_certificate_policies(cert, pos, len); + case 37: /* id-ce-extKeyUsage */ + return x509_parse_ext_ext_key_usage(cert, pos, len); default: return 1; } @@ -1051,12 +1373,8 @@ static int x509_parse_extension(struct x509_certificate *cert, * } */ - if (asn1_get_next(pos, len, &hdr) < 0 || - hdr.class != ASN1_CLASS_UNIVERSAL || - hdr.tag != ASN1_TAG_SEQUENCE) { - wpa_printf(MSG_DEBUG, "X509: Unexpected ASN.1 header in " - "Extensions: class %d tag 0x%x; expected SEQUENCE", - hdr.class, hdr.tag); + if (asn1_get_next(pos, len, &hdr) < 0 || !asn1_is_sequence(&hdr)) { + asn1_unexpected(&hdr, "X509: Expected SEQUENCE in Extensions"); return -1; } pos = hdr.payload; @@ -1069,31 +1387,27 @@ static int x509_parse_extension(struct x509_certificate *cert, } if (asn1_get_next(pos, end - pos, &hdr) < 0 || - hdr.class != ASN1_CLASS_UNIVERSAL || - (hdr.tag != ASN1_TAG_BOOLEAN && - hdr.tag != ASN1_TAG_OCTETSTRING)) { - wpa_printf(MSG_DEBUG, "X509: Unexpected ASN.1 header in " - "Extensions: class %d tag 0x%x; expected BOOLEAN " - "or OCTET STRING", hdr.class, hdr.tag); + (!asn1_is_boolean(&hdr) && !asn1_is_octetstring(&hdr))) { + asn1_unexpected(&hdr, + "X509: Expected BOOLEAN or OCTETSTRING in Extensions"); return -1; } if (hdr.tag == ASN1_TAG_BOOLEAN) { - if (hdr.length != 1) { - wpa_printf(MSG_DEBUG, "X509: Unexpected " - "Boolean length (%u)", hdr.length); - return -1; - } critical_ext = hdr.payload[0]; pos = hdr.payload; + /* + * Number of CA certificates seem to be using Private class in + * one of the X.509v3 extensions, so let's accept that instead + * of rejecting the certificate. asn1_is_octetstring() covers + * the more common case. + */ if (asn1_get_next(pos, end - pos, &hdr) < 0 || - (hdr.class != ASN1_CLASS_UNIVERSAL && - hdr.class != ASN1_CLASS_PRIVATE) || - hdr.tag != ASN1_TAG_OCTETSTRING) { - wpa_printf(MSG_DEBUG, "X509: Unexpected ASN.1 header " - "in Extensions: class %d tag 0x%x; " - "expected OCTET STRING", - hdr.class, hdr.tag); + (!asn1_is_octetstring(&hdr) && + !(hdr.class == ASN1_CLASS_PRIVATE && + hdr.tag == ASN1_TAG_OCTETSTRING))) { + asn1_unexpected(&hdr, + "X509: Expected OCTETSTRING in Extensions"); return -1; } } @@ -1109,7 +1423,7 @@ static int x509_parse_extension(struct x509_certificate *cert, if (res == 1 && critical_ext) { wpa_printf(MSG_INFO, "X509: Unknown critical extension %s", buf); - //return -1; //for wpa2 certification , commenout , ignore the error + return -1; } return 0; @@ -1124,12 +1438,8 @@ static int x509_parse_extensions(struct x509_certificate *cert, /* Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension */ - if (asn1_get_next(pos, len, &hdr) < 0 || - hdr.class != ASN1_CLASS_UNIVERSAL || - hdr.tag != ASN1_TAG_SEQUENCE) { - wpa_printf(MSG_DEBUG, "X509: Unexpected ASN.1 data " - "for Extensions: class %d tag 0x%x; " - "expected SEQUENCE", hdr.class, hdr.tag); + if (asn1_get_next(pos, len, &hdr) < 0 || !asn1_is_sequence(&hdr)) { + asn1_unexpected(&hdr, "X509: Expected SEQUENCE for Extensions"); return -1; } @@ -1155,14 +1465,12 @@ static int x509_parse_tbs_certificate(const u8 *buf, size_t len, size_t left; char sbuf[128]; unsigned long value; + const u8 *subject_dn; /* tbsCertificate TBSCertificate ::= SEQUENCE */ - if (asn1_get_next(buf, len, &hdr) < 0 || - hdr.class != ASN1_CLASS_UNIVERSAL || - hdr.tag != ASN1_TAG_SEQUENCE) { - wpa_printf(MSG_DEBUG, "X509: tbsCertificate did not start " - "with a valid SEQUENCE - found class %d tag 0x%x", - hdr.class, hdr.tag); + if (asn1_get_next(buf, len, &hdr) < 0 || !asn1_is_sequence(&hdr)) { + asn1_unexpected(&hdr, + "X509: tbsCertificate did not start with a valid SEQUENCE"); return -1; } pos = hdr.payload; @@ -1176,15 +1484,11 @@ static int x509_parse_tbs_certificate(const u8 *buf, size_t len, return -1; pos = hdr.payload; - if (hdr.class == ASN1_CLASS_CONTEXT_SPECIFIC) { - if (asn1_get_next(pos, end - pos, &hdr) < 0) - return -1; - - if (hdr.class != ASN1_CLASS_UNIVERSAL || - hdr.tag != ASN1_TAG_INTEGER) { - wpa_printf(MSG_DEBUG, "X509: No INTEGER tag found for " - "version field - found class %d tag 0x%x", - hdr.class, hdr.tag); + if (asn1_is_cs_tag(&hdr, 0) && hdr.constructed) { + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + !asn1_is_integer(&hdr)) { + asn1_unexpected(&hdr, + "X509: No INTEGER tag found for version field"); return -1; } if (hdr.length != 1) { @@ -1214,25 +1518,25 @@ static int x509_parse_tbs_certificate(const u8 *buf, size_t len, return -1; } else cert->version = X509_CERT_V1; - wpa_printf(MSG_DEBUG, "X509: Version X.509v%d", cert->version + 1); + wpa_printf(MSG_MSGDUMP, "X509: Version X.509v%d", cert->version + 1); /* serialNumber CertificateSerialNumber ::= INTEGER */ - if (hdr.class != ASN1_CLASS_UNIVERSAL || - hdr.tag != ASN1_TAG_INTEGER) { - wpa_printf(MSG_DEBUG, "X509: No INTEGER tag found for " - "serialNumber; class=%d tag=0x%x", - hdr.class, hdr.tag); + if (!asn1_is_integer(&hdr) || + hdr.length < 1 || hdr.length > X509_MAX_SERIAL_NUM_LEN) { + asn1_unexpected(&hdr, + "X509: No INTEGER tag found for serialNumber"); return -1; } - pos = hdr.payload; - left = hdr.length; - while (left) { - cert->serial_number <<= 8; - cert->serial_number |= *pos++; - left--; + pos = hdr.payload + hdr.length; + while (hdr.length > 0 && hdr.payload[0] == 0) { + hdr.payload++; + hdr.length--; } - wpa_printf(MSG_DEBUG, "X509: serialNumber %lu", cert->serial_number); + os_memcpy(cert->serial_number, hdr.payload, hdr.length); + cert->serial_number_len = hdr.length; + wpa_hexdump(MSG_MSGDUMP, "X509: serialNumber", cert->serial_number, + cert->serial_number_len); /* signature AlgorithmIdentifier */ if (x509_parse_algorithm_identifier(pos, end - pos, &cert->signature, @@ -1243,17 +1547,23 @@ static int x509_parse_tbs_certificate(const u8 *buf, size_t len, if (x509_parse_name(pos, end - pos, &cert->issuer, &pos)) return -1; x509_name_string(&cert->issuer, sbuf, sizeof(sbuf)); - wpa_printf(MSG_DEBUG, "X509: issuer %s", sbuf); + wpa_printf(MSG_MSGDUMP, "X509: issuer %s", sbuf); /* validity Validity */ if (x509_parse_validity(pos, end - pos, cert, &pos)) return -1; /* subject Name */ + subject_dn = pos; if (x509_parse_name(pos, end - pos, &cert->subject, &pos)) return -1; + cert->subject_dn = os_malloc(pos - subject_dn); + if (!cert->subject_dn) + return -1; + cert->subject_dn_len = pos - subject_dn; + os_memcpy(cert->subject_dn, subject_dn, cert->subject_dn_len); x509_name_string(&cert->subject, sbuf, sizeof(sbuf)); - wpa_printf(MSG_DEBUG, "X509: subject %s", sbuf); + wpa_printf(MSG_MSGDUMP, "X509: subject %s", sbuf); /* subjectPublicKeyInfo SubjectPublicKeyInfo */ if (x509_parse_public_key(pos, end - pos, cert, &pos)) @@ -1267,10 +1577,8 @@ static int x509_parse_tbs_certificate(const u8 *buf, size_t len, if (asn1_get_next(pos, end - pos, &hdr) < 0 || hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC) { - wpa_printf(MSG_DEBUG, "X509: Expected Context-Specific" - " tag to parse optional tbsCertificate " - "field(s); parsed class %d tag 0x%x", - hdr.class, hdr.tag); + asn1_unexpected(&hdr, + "X509: Expected Context-Specific tag to parse optional tbsCertificate field(s)"); return -1; } @@ -1279,15 +1587,14 @@ static int x509_parse_tbs_certificate(const u8 *buf, size_t len, wpa_printf(MSG_DEBUG, "X509: issuerUniqueID"); /* TODO: parse UniqueIdentifier ::= BIT STRING */ - if (hdr.payload + hdr.length == end) + pos = hdr.payload + hdr.length; + if (pos == end) return 0; if (asn1_get_next(pos, end - pos, &hdr) < 0 || hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC) { - wpa_printf(MSG_DEBUG, "X509: Expected Context-Specific" - " tag to parse optional tbsCertificate " - "field(s); parsed class %d tag 0x%x", - hdr.class, hdr.tag); + asn1_unexpected(&hdr, + "X509: Expected Context-Specific tag to parse optional tbsCertificate field(s)"); return -1; } } @@ -1297,23 +1604,22 @@ static int x509_parse_tbs_certificate(const u8 *buf, size_t len, wpa_printf(MSG_DEBUG, "X509: subjectUniqueID"); /* TODO: parse UniqueIdentifier ::= BIT STRING */ - if (hdr.payload + hdr.length == end) + pos = hdr.payload + hdr.length; + if (pos == end) return 0; if (asn1_get_next(pos, end - pos, &hdr) < 0 || hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC) { - wpa_printf(MSG_DEBUG, "X509: Expected Context-Specific" - " tag to parse optional tbsCertificate " - "field(s); parsed class %d tag 0x%x", - hdr.class, hdr.tag); + asn1_unexpected(&hdr, + "X509: Expected Context-Specific tag to parse optional tbsCertificate field(s)"); return -1; } } if (hdr.tag != 3) { - wpa_printf(MSG_DEBUG, "X509: Ignored unexpected " - "Context-Specific tag %d in optional " - "tbsCertificate fields", hdr.tag); + wpa_printf(MSG_DEBUG, + "X509: Ignored unexpected Context-Specific constructed %d tag %d in optional tbsCertificate fields", + hdr.constructed, hdr.tag); return 0; } @@ -1365,7 +1671,8 @@ static int x509_digest_oid(struct asn1_oid *oid) oid->oid[4] == 2 /* digestAlgorithm */; } -static int x509_sha1_oid(struct asn1_oid *oid) + +int x509_sha1_oid(struct asn1_oid *oid) { return oid->len == 6 && oid->oid[0] == 1 /* iso */ && @@ -1376,6 +1683,7 @@ static int x509_sha1_oid(struct asn1_oid *oid) oid->oid[5] == 26 /* id-sha1 */; } + static int x509_sha2_oid(struct asn1_oid *oid) { return oid->len == 9 && @@ -1390,21 +1698,21 @@ static int x509_sha2_oid(struct asn1_oid *oid) } -static int x509_sha256_oid(struct asn1_oid *oid) +int x509_sha256_oid(struct asn1_oid *oid) { return x509_sha2_oid(oid) && oid->oid[8] == 1 /* sha256 */; } -static int x509_sha384_oid(struct asn1_oid *oid) +int x509_sha384_oid(struct asn1_oid *oid) { return x509_sha2_oid(oid) && oid->oid[8] == 2 /* sha384 */; } -static int x509_sha512_oid(struct asn1_oid *oid) +int x509_sha512_oid(struct asn1_oid *oid) { return x509_sha2_oid(oid) && oid->oid[8] == 3 /* sha512 */; @@ -1426,7 +1734,7 @@ struct x509_certificate * x509_certificate_parse(const u8 *buf, size_t len) const u8 *pos, *end, *hash_start; struct x509_certificate *cert; - cert = (struct x509_certificate *)os_zalloc(sizeof(*cert) + len); + cert = os_zalloc(sizeof(*cert) + len); if (cert == NULL) return NULL; os_memcpy(cert + 1, buf, len); @@ -1439,26 +1747,23 @@ struct x509_certificate * x509_certificate_parse(const u8 *buf, size_t len) /* RFC 3280 - X.509 v3 certificate / ASN.1 DER */ /* Certificate ::= SEQUENCE */ - if (asn1_get_next(pos, len, &hdr) < 0 || - hdr.class != ASN1_CLASS_UNIVERSAL || - hdr.tag != ASN1_TAG_SEQUENCE) { - wpa_printf(MSG_DEBUG, "X509: Certificate did not start with " - "a valid SEQUENCE - found class %d tag 0x%x", - hdr.class, hdr.tag); + if (asn1_get_next(pos, len, &hdr) < 0 || !asn1_is_sequence(&hdr)) { + asn1_unexpected(&hdr, + "X509: Certificate did not start with a valid SEQUENCE"); x509_certificate_free(cert); return NULL; } pos = hdr.payload; - if (pos + hdr.length > end) { + if (hdr.length > end - pos) { x509_certificate_free(cert); return NULL; } - if (pos + hdr.length < end) { + if (hdr.length < end - pos) { wpa_hexdump(MSG_MSGDUMP, "X509: Ignoring extra data after DER " "encoded certificate", - pos + hdr.length, end - pos + hdr.length); + pos + hdr.length, end - (pos + hdr.length)); end = pos + hdr.length; } @@ -1479,11 +1784,9 @@ struct x509_certificate * x509_certificate_parse(const u8 *buf, size_t len) /* signatureValue BIT STRING */ if (asn1_get_next(pos, end - pos, &hdr) < 0 || - hdr.class != ASN1_CLASS_UNIVERSAL || - hdr.tag != ASN1_TAG_BITSTRING) { - wpa_printf(MSG_DEBUG, "X509: Expected BITSTRING " - "(signatureValue) - found class %d tag 0x%x", - hdr.class, hdr.tag); + !asn1_is_bitstring(&hdr)) { + asn1_unexpected(&hdr, + "X509: Expected BITSTRING (signatureValue)"); x509_certificate_free(cert); return NULL; } @@ -1493,7 +1796,8 @@ struct x509_certificate * x509_certificate_parse(const u8 *buf, size_t len) } pos = hdr.payload; if (*pos) { - wpa_printf(MSG_DEBUG, "X509: BITSTRING - %d unused bits", + wpa_printf(MSG_DEBUG, + "X509: BITSTRING (signatureValue) - %d unused bits", *pos); /* PKCS #1 v1.5 10.2.1: * It is an error if the length in bits of the signature S is @@ -1503,14 +1807,13 @@ struct x509_certificate * x509_certificate_parse(const u8 *buf, size_t len) return NULL; } os_free(cert->sign_value); - cert->sign_value = os_malloc(hdr.length - 1); + cert->sign_value = os_memdup(pos + 1, hdr.length - 1); if (cert->sign_value == NULL) { wpa_printf(MSG_DEBUG, "X509: Failed to allocate memory for " "signatureValue"); x509_certificate_free(cert); return NULL; } - os_memcpy(cert->sign_value, pos + 1, hdr.length - 1); cert->sign_value_len = hdr.length - 1; wpa_hexdump(MSG_MSGDUMP, "X509: signature", cert->sign_value, cert->sign_value_len); @@ -1528,6 +1831,17 @@ struct x509_certificate * x509_certificate_parse(const u8 *buf, size_t len) */ int x509_certificate_check_signature(struct x509_certificate *issuer, struct x509_certificate *cert) +{ + return x509_check_signature(issuer, &cert->signature, + cert->sign_value, cert->sign_value_len, + cert->tbs_cert_start, cert->tbs_cert_len); +} + + +int x509_check_signature(struct x509_certificate *issuer, + struct x509_algorithm_identifier *signature, + const u8 *sign_value, size_t sign_value_len, + const u8 *signed_data, size_t signed_data_len) { struct crypto_public_key *pk; u8 *data; @@ -1537,10 +1851,12 @@ int x509_certificate_check_signature(struct x509_certificate *issuer, struct asn1_oid oid; u8 hash[64]; size_t hash_len; + const u8 *addr[1] = { signed_data }; + size_t len[1] = { signed_data_len }; - if (!x509_pkcs_oid(&cert->signature.oid) || - cert->signature.oid.len != 7 || - cert->signature.oid.oid[5] != 1 /* pkcs-1 */) { + if (!x509_pkcs_oid(&signature->oid) || + signature->oid.len != 7 || + signature->oid.oid[5] != 1 /* pkcs-1 */) { wpa_printf(MSG_DEBUG, "X509: Unrecognized signature " "algorithm"); return -1; @@ -1551,15 +1867,15 @@ int x509_certificate_check_signature(struct x509_certificate *issuer, if (pk == NULL) return -1; - data_len = cert->sign_value_len; + data_len = sign_value_len; data = os_malloc(data_len); if (data == NULL) { crypto_public_key_free(pk); return -1; } - if (crypto_public_key_decrypt_pkcs1(pk, cert->sign_value, - cert->sign_value_len, data, + if (crypto_public_key_decrypt_pkcs1(pk, sign_value, + sign_value_len, data, &data_len) < 0) { wpa_printf(MSG_DEBUG, "X509: Failed to decrypt signature"); crypto_public_key_free(pk); @@ -1584,14 +1900,12 @@ int x509_certificate_check_signature(struct x509_certificate *issuer, * */ if (asn1_get_next(data, data_len, &hdr) < 0 || - hdr.class != ASN1_CLASS_UNIVERSAL || - hdr.tag != ASN1_TAG_SEQUENCE) { - wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE " - "(DigestInfo) - found class %d tag 0x%x", - hdr.class, hdr.tag); + !asn1_is_sequence(&hdr)) { + asn1_unexpected(&hdr, "X509: Expected SEQUENCE (DigestInfo)"); os_free(data); return -1; } + wpa_hexdump(MSG_MSGDUMP, "X509: DigestInfo", hdr.payload, hdr.length); pos = hdr.payload; end = pos + hdr.length; @@ -1605,14 +1919,14 @@ int x509_certificate_check_signature(struct x509_certificate *issuer, */ if (asn1_get_next(pos, end - pos, &hdr) < 0 || - hdr.class != ASN1_CLASS_UNIVERSAL || - hdr.tag != ASN1_TAG_SEQUENCE) { - wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE " - "(AlgorithmIdentifier) - found class %d tag 0x%x", - hdr.class, hdr.tag); + !asn1_is_sequence(&hdr)) { + asn1_unexpected(&hdr, + "X509: Expected SEQUENCE (AlgorithmIdentifier)"); os_free(data); return -1; } + wpa_hexdump(MSG_MSGDUMP, "X509: DigestAlgorithmIdentifier", + hdr.payload, hdr.length); da_end = hdr.payload + hdr.length; if (asn1_get_oid(hdr.payload, hdr.length, &oid, &next)) { @@ -1620,14 +1934,30 @@ int x509_certificate_check_signature(struct x509_certificate *issuer, os_free(data); return -1; } + wpa_hexdump(MSG_MSGDUMP, "X509: Digest algorithm parameters", + next, da_end - next); + + /* + * RFC 5754: The correct encoding for the SHA2 algorithms would be to + * omit the parameters, but there are implementation that encode these + * as a NULL element. Allow these two cases and reject anything else. + */ + if (da_end > next && + (asn1_get_next(next, da_end - next, &hdr) < 0 || + !asn1_is_null(&hdr) || + hdr.payload + hdr.length != da_end)) { + wpa_printf(MSG_DEBUG, + "X509: Unexpected digest algorithm parameters"); + os_free(data); + return -1; + } if (x509_sha1_oid(&oid)) { - if (cert->signature.oid.oid[6] != - 5 /* sha-1WithRSAEncryption */) { + if (signature->oid.oid[6] != 5 /* sha-1WithRSAEncryption */) { wpa_printf(MSG_DEBUG, "X509: digestAlgorithm SHA1 " "does not match with certificate " "signatureAlgorithm (%lu)", - cert->signature.oid.oid[6]); + signature->oid.oid[6]); os_free(data); return -1; } @@ -1635,12 +1965,12 @@ int x509_certificate_check_signature(struct x509_certificate *issuer, } if (x509_sha256_oid(&oid)) { - if (cert->signature.oid.oid[6] != + if (signature->oid.oid[6] != 11 /* sha2561WithRSAEncryption */) { wpa_printf(MSG_DEBUG, "X509: digestAlgorithm SHA256 " "does not match with certificate " "signatureAlgorithm (%lu)", - cert->signature.oid.oid[6]); + signature->oid.oid[6]); os_free(data); return -1; } @@ -1648,12 +1978,11 @@ int x509_certificate_check_signature(struct x509_certificate *issuer, } if (x509_sha384_oid(&oid)) { - if (cert->signature.oid.oid[6] != - 12 /* sha384WithRSAEncryption */) { + if (signature->oid.oid[6] != 12 /* sha384WithRSAEncryption */) { wpa_printf(MSG_DEBUG, "X509: digestAlgorithm SHA384 " "does not match with certificate " "signatureAlgorithm (%lu)", - cert->signature.oid.oid[6]); + signature->oid.oid[6]); os_free(data); return -1; } @@ -1661,12 +1990,11 @@ int x509_certificate_check_signature(struct x509_certificate *issuer, } if (x509_sha512_oid(&oid)) { - if (cert->signature.oid.oid[6] != - 13 /* sha512WithRSAEncryption */) { + if (signature->oid.oid[6] != 13 /* sha512WithRSAEncryption */) { wpa_printf(MSG_DEBUG, "X509: digestAlgorithm SHA512 " "does not match with certificate " "signatureAlgorithm (%lu)", - cert->signature.oid.oid[6]); + signature->oid.oid[6]); os_free(data); return -1; } @@ -1680,12 +2008,11 @@ int x509_certificate_check_signature(struct x509_certificate *issuer, } switch (oid.oid[5]) { case 5: /* md5 */ - if (cert->signature.oid.oid[6] != 4 /* md5WithRSAEncryption */) - { + if (signature->oid.oid[6] != 4 /* md5WithRSAEncryption */) { wpa_printf(MSG_DEBUG, "X509: digestAlgorithm MD5 does " "not match with certificate " "signatureAlgorithm (%lu)", - cert->signature.oid.oid[6]); + signature->oid.oid[6]); os_free(data); return -1; } @@ -1702,52 +2029,43 @@ int x509_certificate_check_signature(struct x509_certificate *issuer, skip_digest_oid: /* Digest ::= OCTET STRING */ pos = da_end; - end = data + data_len; if (asn1_get_next(pos, end - pos, &hdr) < 0 || - hdr.class != ASN1_CLASS_UNIVERSAL || - hdr.tag != ASN1_TAG_OCTETSTRING) { - wpa_printf(MSG_DEBUG, "X509: Expected OCTETSTRING " - "(Digest) - found class %d tag 0x%x", - hdr.class, hdr.tag); + !asn1_is_octetstring(&hdr)) { + asn1_unexpected(&hdr, "X509: Expected OCTETSTRING (Digest)"); os_free(data); return -1; } wpa_hexdump(MSG_MSGDUMP, "X509: Decrypted Digest", hdr.payload, hdr.length); - switch (cert->signature.oid.oid[6]) { + switch (signature->oid.oid[6]) { case 4: /* md5WithRSAEncryption */ - md5_vector(1, &cert->tbs_cert_start, &cert->tbs_cert_len, - hash); + md5_vector(1, addr, len, hash); hash_len = 16; wpa_hexdump(MSG_MSGDUMP, "X509: Certificate hash (MD5)", hash, hash_len); break; case 5: /* sha-1WithRSAEncryption */ - sha1_vector(1, &cert->tbs_cert_start, &cert->tbs_cert_len, - hash); + sha1_vector(1, addr, len, hash); hash_len = 20; wpa_hexdump(MSG_MSGDUMP, "X509: Certificate hash (SHA1)", hash, hash_len); break; case 11: /* sha256WithRSAEncryption */ - sha256_vector(1, &cert->tbs_cert_start, &cert->tbs_cert_len, - hash); + sha256_vector(1, addr, len, hash); hash_len = 32; wpa_hexdump(MSG_MSGDUMP, "X509: Certificate hash (SHA256)", hash, hash_len); break; case 12: /* sha384WithRSAEncryption */ - sha384_vector(1, &cert->tbs_cert_start, &cert->tbs_cert_len, - hash); + sha384_vector(1, addr, len, hash); hash_len = 48; wpa_hexdump(MSG_MSGDUMP, "X509: Certificate hash (SHA384)", hash, hash_len); break; case 13: /* sha512WithRSAEncryption */ - sha512_vector(1, &cert->tbs_cert_start, &cert->tbs_cert_len, - hash); + sha512_vector(1, addr, len, hash); hash_len = 64; wpa_hexdump(MSG_MSGDUMP, "X509: Certificate hash (SHA512)", hash, hash_len); @@ -1755,19 +2073,28 @@ skip_digest_oid: case 2: /* md2WithRSAEncryption */ default: wpa_printf(MSG_INFO, "X509: Unsupported certificate signature " - "algorithm (%lu)", cert->signature.oid.oid[6]); + "algorithm (%lu)", signature->oid.oid[6]); os_free(data); return -1; } if (hdr.length != hash_len || - os_memcmp(hdr.payload, hash, hdr.length) != 0) { + os_memcmp_const(hdr.payload, hash, hdr.length) != 0) { wpa_printf(MSG_INFO, "X509: Certificate Digest does not match " "with calculated tbsCertificate hash"); os_free(data); return -1; } + if (hdr.payload + hdr.length < data + data_len) { + wpa_hexdump(MSG_INFO, + "X509: Extra data after certificate signature hash", + hdr.payload + hdr.length, + data + data_len - hdr.payload - hdr.length); + os_free(data); + return -1; + } + os_free(data); wpa_printf(MSG_DEBUG, "X509: Certificate Digest matches with " @@ -1828,6 +2155,7 @@ int x509_certificate_chain_validate(struct x509_certificate *trusted, os_get_time(&now); for (cert = chain, idx = 0; cert; cert = cert->next, idx++) { + cert->issuer_trusted = 0; x509_name_string(&cert->subject, buf, sizeof(buf)); wpa_printf(MSG_DEBUG, "X509: %lu: %s", idx, buf); @@ -1841,7 +2169,7 @@ int x509_certificate_chain_validate(struct x509_certificate *trusted, (unsigned long) cert->not_after)) { wpa_printf(MSG_INFO, "X509: Certificate not valid " "(now=%lu not_before=%lu not_after=%lu)", - (unsigned long)now.sec, (unsigned long)cert->not_before, (unsigned long)cert->not_after); + now.sec, cert->not_before, cert->not_after); *reason = X509_VALIDATE_CERTIFICATE_EXPIRED; return -1; } @@ -1913,6 +2241,7 @@ int x509_certificate_chain_validate(struct x509_certificate *trusted, wpa_printf(MSG_DEBUG, "X509: Trusted certificate " "found to complete the chain"); + cert->issuer_trusted = 1; chain_trusted = 1; } } diff --git a/components/wpa_supplicant/src/tls/x509v3.h b/components/wpa_supplicant/src/tls/x509v3.h index 91a35baf92..e3b108ff43 100644 --- a/components/wpa_supplicant/src/tls/x509v3.h +++ b/components/wpa_supplicant/src/tls/x509v3.h @@ -45,13 +45,18 @@ struct x509_name { struct asn1_oid rid; /* registeredID */ }; +#define X509_MAX_SERIAL_NUM_LEN 20 + struct x509_certificate { struct x509_certificate *next; enum { X509_CERT_V1 = 0, X509_CERT_V2 = 1, X509_CERT_V3 = 2 } version; - unsigned long serial_number; + u8 serial_number[X509_MAX_SERIAL_NUM_LEN]; + size_t serial_number_len; struct x509_algorithm_identifier signature; struct x509_name issuer; struct x509_name subject; + u8 *subject_dn; + size_t subject_dn_len; os_time_t not_before; os_time_t not_after; struct x509_algorithm_identifier public_key_alg; @@ -68,6 +73,8 @@ struct x509_certificate { #define X509_EXT_KEY_USAGE (1 << 2) #define X509_EXT_SUBJECT_ALT_NAME (1 << 3) #define X509_EXT_ISSUER_ALT_NAME (1 << 4) +#define X509_EXT_EXT_KEY_USAGE (1 << 5) +#define X509_EXT_CERTIFICATE_POLICY (1 << 6) /* BasicConstraints */ int ca; /* cA */ @@ -85,6 +92,19 @@ struct x509_certificate { #define X509_KEY_USAGE_ENCIPHER_ONLY (1 << 7) #define X509_KEY_USAGE_DECIPHER_ONLY (1 << 8) + /* ExtKeyUsage */ + unsigned long ext_key_usage; +#define X509_EXT_KEY_USAGE_ANY (1 << 0) +#define X509_EXT_KEY_USAGE_SERVER_AUTH (1 << 1) +#define X509_EXT_KEY_USAGE_CLIENT_AUTH (1 << 2) +#define X509_EXT_KEY_USAGE_OCSP (1 << 3) + + /* CertificatePolicy */ + unsigned long certificate_policy; +#define X509_EXT_CERT_POLICY_ANY (1 << 0) +#define X509_EXT_CERT_POLICY_TOD_STRICT (1 << 1) +#define X509_EXT_CERT_POLICY_TOD_TOFU (1 << 2) + /* * The DER format certificate follows struct x509_certificate. These * pointers point to that buffer. @@ -93,6 +113,11 @@ struct x509_certificate { size_t cert_len; const u8 *tbs_cert_start; size_t tbs_cert_len; + + /* Meta data used for certificate validation */ + unsigned int ocsp_good:1; + unsigned int ocsp_revoked:1; + unsigned int issuer_trusted:1; }; enum { @@ -106,10 +131,21 @@ enum { }; void x509_certificate_free(struct x509_certificate *cert); +int x509_parse_algorithm_identifier(const u8 *buf, size_t len, + struct x509_algorithm_identifier *id, + const u8 **next); +int x509_parse_name(const u8 *buf, size_t len, struct x509_name *name, + const u8 **next); +int x509_parse_time(const u8 *buf, size_t len, u8 asn1_tag, os_time_t *val); struct x509_certificate * x509_certificate_parse(const u8 *buf, size_t len); +void x509_free_name(struct x509_name *name); void x509_name_string(struct x509_name *name, char *buf, size_t len); int x509_name_compare(struct x509_name *a, struct x509_name *b); void x509_certificate_chain_free(struct x509_certificate *cert); +int x509_check_signature(struct x509_certificate *issuer, + struct x509_algorithm_identifier *signature, + const u8 *sign_value, size_t sign_value_len, + const u8 *signed_data, size_t signed_data_len); int x509_certificate_check_signature(struct x509_certificate *issuer, struct x509_certificate *cert); int x509_certificate_chain_validate(struct x509_certificate *trusted, @@ -120,4 +156,9 @@ x509_certificate_get_subject(struct x509_certificate *chain, struct x509_name *name); int x509_certificate_self_signed(struct x509_certificate *cert); +int x509_sha1_oid(struct asn1_oid *oid); +int x509_sha256_oid(struct asn1_oid *oid); +int x509_sha384_oid(struct asn1_oid *oid); +int x509_sha512_oid(struct asn1_oid *oid); + #endif /* X509V3_H */ diff --git a/examples/wifi/wifi_eap_fast/sdkconfig.defaults b/examples/wifi/wifi_eap_fast/sdkconfig.defaults index be56c7c496..911714bd95 100644 --- a/examples/wifi/wifi_eap_fast/sdkconfig.defaults +++ b/examples/wifi/wifi_eap_fast/sdkconfig.defaults @@ -1 +1 @@ -CONFIG_WPA_MBEDTLS_CRYPTO=n +CONFIG_WPA_MBEDTLS_TLS_CLIENT=n