From 5a1bcdf036840ee3c70beb458c421e7f5a294b8d Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Thu, 12 Feb 2026 01:10:05 +0100 Subject: [PATCH] tls: fix cipher-id selection in server mode ECDSA keys still don't work, and currently will be ignored function old new delta tls_handshake_as_server 824 1601 +777 .rodata 107764 108007 +243 set_cipher_parameters - 161 +161 packed_usage 36072 36146 +74 static.BLOCK_NAMES - 70 +70 client_hello_ciphers - 32 +32 ssl_server_main 288 279 -9 load_rsa_priv_key 329 282 -47 tls_handshake 1676 1530 -146 ------------------------------------------------------------------------------ (add/remove: 3/0 grow/shrink: 3/3 up/down: 1357/-202) Total: 1155 bytes Signed-off-by: Denys Vlasenko --- include/libbb.h | 3 +- networking/ssl_server.c | 41 ++- networking/tls.c | 607 ++++++++++++++++++++++++++-------------- 3 files changed, 430 insertions(+), 221 deletions(-) diff --git a/include/libbb.h b/include/libbb.h index 434a660bd..5f4ba6b0f 100644 --- a/include/libbb.h +++ b/include/libbb.h @@ -928,8 +928,7 @@ static inline tls_state_t *new_tls_state(void) } void FAST_FUNC tls_handshake(tls_state_t *tls, const char *sni); void FAST_FUNC tls_handshake_as_server(tls_state_t *tls, - const char *privkey_der_filename, - const char *cert_der_filename); + const char *pem_filename); #define TLSLOOP_EXIT_ON_LOCAL_EOF (1 << 0) void tls_run_copy_loop(tls_state_t *tls, unsigned flags) FAST_FUNC; diff --git a/networking/ssl_server.c b/networking/ssl_server.c index 665009aea..2a89dae6c 100644 --- a/networking/ssl_server.c +++ b/networking/ssl_server.c @@ -14,9 +14,35 @@ //kbuild:lib-$(CONFIG_SSL_SERVER) += ssl_server.o //usage:#define ssl_server_trivial_usage -//usage: "[-vv] -p PRIVKEY.der -c CERT.der PROG ARGS" +//usage: "-f PRIVKEY_CERT.pem PROG ARGS" //usage:#define ssl_server_full_usage "" - +//usage: "Inetd-style TLS server\n" +//usage: "\n -f PEMFILE HAProxy-style CRT file" +/* +# Generate RSA key and certificate +openssl req -x509 -newkey rsa:4096 \ + -keyout $HOSTNAME-rsa.key \ + -out $HOSTNAME-rsa.crt \ + -sha256 -days 9999 -nodes \ + -subj /CN=$HOSTNAME \ + -addext "subjectAltName=DNS:$HOSTNAME" +# Generate ECDSA key and certificate +openssl genpkey -algorithm EC \ + -pkeyopt ec_paramgen_curve:prime256v1 \ + -out $HOSTNAME-ecdsa.key +fopenssl req -new -x509 \ + -key $HOSTNAME-ecdsa.key \ + -out $HOSTNAME-ecdsa.crt \ + -sha256 -days 9999 \ + -subj "/CN=$HOSTNAME" \ + -addext "subjectAltName=DNS:$HOSTNAME" +# Concatenate all these files into PRIVKEY_CERT.pem +{ cat $HOSTNAME-rsa.key + cat $HOSTNAME-rsa.crt + cat $HOSTNAME-ecdsa.key + cat $HOSTNAME-ecdsa.crt +} >PRIVKEY_CERT.pem +*/ #include "libbb.h" /* TLS server applet. @@ -42,18 +68,17 @@ int ssl_server_main(int argc UNUSED_PARAM, char **argv) struct fd_pair from_prog; pid_t pid; tls_state_t *tls; - const char *privkey_in_der_format; - const char *cert_in_der_format; + const char *pem_file; unsigned opt; tls = new_tls_state(); /* "+": stop on first non-option */ - opt = getopt32(argv, "+""vp:c:", - &privkey_in_der_format, &cert_in_der_format + opt = getopt32(argv, "+""vf:", + &pem_file ); argv += optind; - if (!argv[0] || (opt >> 1) == 0) + if (!argv[0] || !(opt & 2)) bb_show_usage(); /* In inetd mode, stdin/stdout are the socket. @@ -66,7 +91,7 @@ int ssl_server_main(int argc UNUSED_PARAM, char **argv) tls->ofd = 4; /* This can abort on errors */ - tls_handshake_as_server(tls, privkey_in_der_format, cert_in_der_format); + tls_handshake_as_server(tls, pem_file); /* Run PROG, wrap its data in TLS and I/O to socket */ xpiped_pair(to_prog); diff --git a/networking/tls.c b/networking/tls.c index a27f0955a..2f0afe00a 100644 --- a/networking/tls.c +++ b/networking/tls.c @@ -189,6 +189,76 @@ #define TLS_MAX_CRYPTBLOCK_SIZE 16 #define TLS_MAX_OUTBUF (1 << 14) +/* Cipher suites we support, in preference order (best first) */ +#define NUM_CIPHERS (0 \ + + 4 * ENABLE_FEATURE_TLS_SHA1 \ + + ALLOW_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 \ + + ALLOW_ECDHE_RSA_WITH_AES_128_CBC_SHA256 \ + + ALLOW_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 \ + + ALLOW_ECDHE_RSA_WITH_AES_128_GCM_SHA256 \ + + 2 * ENABLE_FEATURE_TLS_SHA1 \ + + ALLOW_RSA_WITH_AES_128_CBC_SHA256 \ + + ALLOW_RSA_WITH_AES_256_CBC_SHA256 \ + + ALLOW_RSA_WITH_AES_128_GCM_SHA256 \ + + ALLOW_RSA_NULL_SHA256 \ + ) +static const uint8_t client_hello_ciphers[] = { + 0x00,2 * (1 + NUM_CIPHERS), //len16_be + 0x00,0xFF, //not a cipher - TLS_EMPTY_RENEGOTIATION_INFO_SCSV + /* ^^^^^^ RFC 5746 Renegotiation Indication Extension - some servers will refuse to work with us otherwise */ +#if ENABLE_FEATURE_TLS_SHA1 + 0xC0,0x09, // 1 TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA - ok: wget https://is.gd/ + 0xC0,0x0A, // 2 TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA - ok: wget https://is.gd/ + 0xC0,0x13, // 3 TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA - ok: openssl s_server ... -cipher ECDHE-RSA-AES128-SHA + 0xC0,0x14, // 4 TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA - ok: openssl s_server ... -cipher ECDHE-RSA-AES256-SHA (might fail with older openssl) +// 0xC0,0x18, // TLS_ECDH_anon_WITH_AES_128_CBC_SHA +// 0xC0,0x19, // TLS_ECDH_anon_WITH_AES_256_CBC_SHA +#endif +#if ALLOW_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 + 0xC0,0x23, // 5 TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 - ok: wget https://is.gd/ +#endif +// 0xC0,0x24, // TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 - can't do SHA384 yet +#if ALLOW_ECDHE_RSA_WITH_AES_128_CBC_SHA256 + 0xC0,0x27, // 6 TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 - ok: openssl s_server ... -cipher ECDHE-RSA-AES128-SHA256 +#endif +// 0xC0,0x28, // TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 - can't do SHA384 yet +#if ALLOW_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 + 0xC0,0x2B, // 7 TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 - ok: wget https://is.gd/ +#endif +// 0xC0,0x2C, // TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 - wget https://is.gd/: "TLS error from peer (alert code 20): bad MAC" +//TODO: GCM_SHA384 ciphers can be supported, only need sha384-based PRF? +#if ALLOW_ECDHE_RSA_WITH_AES_128_GCM_SHA256 + 0xC0,0x2F, // 8 TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 - ok: openssl s_server ... -cipher ECDHE-RSA-AES128-GCM-SHA256 +#endif +// 0xC0,0x30, // TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 - openssl s_server ... -cipher ECDHE-RSA-AES256-GCM-SHA384: "decryption failed or bad record mac" +//possibly these too: +#if ENABLE_FEATURE_TLS_SHA1 +// 0xC0,0x35, // TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA +// 0xC0,0x36, // TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA +#endif +// 0xC0,0x37, // TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 +// 0xC0,0x38, // TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384 - can't do SHA384 yet +#if ENABLE_FEATURE_TLS_SHA1 + 0x00,0x2F, // 9 TLS_RSA_WITH_AES_128_CBC_SHA - ok: openssl s_server ... -cipher AES128-SHA + 0x00,0x35, //10 TLS_RSA_WITH_AES_256_CBC_SHA - ok: openssl s_server ... -cipher AES256-SHA +#endif +#if ALLOW_RSA_WITH_AES_128_CBC_SHA256 + 0x00,0x3C, //11 TLS_RSA_WITH_AES_128_CBC_SHA256 - ok: openssl s_server ... -cipher AES128-SHA256 +#endif +#if ALLOW_RSA_WITH_AES_256_CBC_SHA256 + 0x00,0x3D, //12 TLS_RSA_WITH_AES_256_CBC_SHA256 - ok: openssl s_server ... -cipher AES256-SHA256 +#endif +#if ALLOW_RSA_WITH_AES_128_GCM_SHA256 + 0x00,0x9C, //13 TLS_RSA_WITH_AES_128_GCM_SHA256 - ok: openssl s_server ... -cipher AES128-GCM-SHA256 +#endif +// 0x00,0x9D, // TLS_RSA_WITH_AES_256_GCM_SHA384 - openssl s_server ... -cipher AES256-GCM-SHA384: "decryption failed or bad record mac" +#if ALLOW_RSA_NULL_SHA256 + 0x00,0x3B, // TLS_RSA_WITH_NULL_SHA256 +#endif + 0x01,0x00, //not a cipher - comprtypes_len, comprtype +}; +#define supported_ciphers (client_hello_ciphers + 4) + enum { AES128_KEYSIZE = 16, AES256_KEYSIZE = 32, @@ -246,6 +316,67 @@ enum { ENCRYPTION_AESGCM = 1 << 5, // else AES-SHA (or NULL-SHA if ALLOW_RSA_NULL_SHA256=1) }; +/* Note: return value matches KEY_RSA (0) / KEY_ECDSA (1) enum values */ +static int is_cipher_ECDSA(const uint8_t *cipherid) +{ + uint8_t cipher_lo; + if (cipherid[0] != 0xC0) + return 0; + /* ECDHE cipher - check if ECDSA or RSA */ + cipher_lo = cipherid[1]; + return (cipher_lo == 0x09 || cipher_lo == 0x0A + || cipher_lo == 0x23 || cipher_lo == 0x2B + ); +} + +/* Set cipher parameters based on selected cipher_id */ +static void set_cipher_parameters(tls_state_t *tls, const uint8_t *cipherid) +{ + uint8_t cipherid1 = cipherid[1]; + + tls->cipher_id = 0x100 * cipherid[0] + cipherid1; + + /* Set defaults for RSA with AES-256 */ + tls->key_size = AES256_KEYSIZE; + tls->MAC_size = SHA256_OUTSIZE; + tls->IV_size = 0; + + if (cipherid[0] == 0xC0) { + /* All C0xx are ECDHE */ + tls->flags |= NEED_EC_KEY; + if (cipherid1 & 1) { + /* Odd numbered C0xx use AES128 (even ones use AES256) */ + tls->key_size = AES128_KEYSIZE; + } + if (ENABLE_FEATURE_TLS_SHA1 && cipherid1 <= 0x19) { + tls->MAC_size = SHA1_OUTSIZE; + } else + if (cipherid1 >= 0x2B && cipherid1 <= 0x30) { + /* C02B,2C,2F,30 are AES-GCM */ + tls->flags |= ENCRYPTION_AESGCM; + tls->MAC_size = 0; + tls->IV_size = 4; + } + } else { + /* All 00xx are RSA */ + if ((ENABLE_FEATURE_TLS_SHA1 && cipherid1 == 0x2F) + || cipherid1 == 0x3C + || cipherid1 == 0x9C + ) { + tls->key_size = AES128_KEYSIZE; + } + if (ENABLE_FEATURE_TLS_SHA1 && cipherid1 <= 0x35) { + tls->MAC_size = SHA1_OUTSIZE; + } else + if (cipherid1 == 0x9C /*|| cipherid1 == 0x9D*/) { + /* 009C,9D are AES-GCM */ + tls->flags |= ENCRYPTION_AESGCM; + tls->MAC_size = 0; + tls->IV_size = 4; + } + } +} + struct record_hdr { uint8_t type; uint8_t proto_maj, proto_min; @@ -273,14 +404,22 @@ struct tls_handshake_data { //uint8_t saved_client_hello[1]; #if ENABLE_SSL_SERVER // || ENABLE_FEATURE_HTTPD_SSL - psRsaKey_t server_rsa_priv_key; - uint8_t *server_cert_der; - size_t server_cert_der_len; + /* Server certificate and key data */ + char *keys[2]; + char *certs[2]; + unsigned keysize[2]; + unsigned certsize[2]; + int key_type_chosen; + psRsaKey_t rsa_priv_key; ///* For ECDHE: server's ephemeral EC private key */ //uint8_t ecc_priv_key32[32]; #endif }; +enum { + KEY_RSA, + KEY_ECDSA, +}; static unsigned get24be(const uint8_t *p) { @@ -1403,73 +1542,6 @@ static void *get_outbuf_fill_handshake_record(tls_state_t *tls, unsigned type, u static void send_client_hello_and_alloc_hsd(tls_state_t *tls, const char *sni) { -#define NUM_CIPHERS (0 \ - + 4 * ENABLE_FEATURE_TLS_SHA1 \ - + ALLOW_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 \ - + ALLOW_ECDHE_RSA_WITH_AES_128_CBC_SHA256 \ - + ALLOW_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 \ - + ALLOW_ECDHE_RSA_WITH_AES_128_GCM_SHA256 \ - + 2 * ENABLE_FEATURE_TLS_SHA1 \ - + ALLOW_RSA_WITH_AES_128_CBC_SHA256 \ - + ALLOW_RSA_WITH_AES_256_CBC_SHA256 \ - + ALLOW_RSA_WITH_AES_128_GCM_SHA256 \ - + ALLOW_RSA_NULL_SHA256 \ - ) - static const uint8_t ciphers[] = { - 0x00,2 * (1 + NUM_CIPHERS), //len16_be - 0x00,0xFF, //not a cipher - TLS_EMPTY_RENEGOTIATION_INFO_SCSV - /* ^^^^^^ RFC 5746 Renegotiation Indication Extension - some servers will refuse to work with us otherwise */ -#if ENABLE_FEATURE_TLS_SHA1 - 0xC0,0x09, // 1 TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA - ok: wget https://is.gd/ - 0xC0,0x0A, // 2 TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA - ok: wget https://is.gd/ - 0xC0,0x13, // 3 TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA - ok: openssl s_server ... -cipher ECDHE-RSA-AES128-SHA - 0xC0,0x14, // 4 TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA - ok: openssl s_server ... -cipher ECDHE-RSA-AES256-SHA (might fail with older openssl) - // 0xC0,0x18, // TLS_ECDH_anon_WITH_AES_128_CBC_SHA - // 0xC0,0x19, // TLS_ECDH_anon_WITH_AES_256_CBC_SHA -#endif -#if ALLOW_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 - 0xC0,0x23, // 5 TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 - ok: wget https://is.gd/ -#endif - // 0xC0,0x24, // TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 - can't do SHA384 yet -#if ALLOW_ECDHE_RSA_WITH_AES_128_CBC_SHA256 - 0xC0,0x27, // 6 TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 - ok: openssl s_server ... -cipher ECDHE-RSA-AES128-SHA256 -#endif - // 0xC0,0x28, // TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 - can't do SHA384 yet -#if ALLOW_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 - 0xC0,0x2B, // 7 TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 - ok: wget https://is.gd/ -#endif - // 0xC0,0x2C, // TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 - wget https://is.gd/: "TLS error from peer (alert code 20): bad MAC" -//TODO: GCM_SHA384 ciphers can be supported, only need sha384-based PRF? -#if ALLOW_ECDHE_RSA_WITH_AES_128_GCM_SHA256 - 0xC0,0x2F, // 8 TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 - ok: openssl s_server ... -cipher ECDHE-RSA-AES128-GCM-SHA256 -#endif - // 0xC0,0x30, // TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 - openssl s_server ... -cipher ECDHE-RSA-AES256-GCM-SHA384: "decryption failed or bad record mac" - //possibly these too: -#if ENABLE_FEATURE_TLS_SHA1 - // 0xC0,0x35, // TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA - // 0xC0,0x36, // TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA -#endif - // 0xC0,0x37, // TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 - // 0xC0,0x38, // TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384 - can't do SHA384 yet -#if ENABLE_FEATURE_TLS_SHA1 - 0x00,0x2F, // 9 TLS_RSA_WITH_AES_128_CBC_SHA - ok: openssl s_server ... -cipher AES128-SHA - 0x00,0x35, //10 TLS_RSA_WITH_AES_256_CBC_SHA - ok: openssl s_server ... -cipher AES256-SHA -#endif -#if ALLOW_RSA_WITH_AES_128_CBC_SHA256 - 0x00,0x3C, //11 TLS_RSA_WITH_AES_128_CBC_SHA256 - ok: openssl s_server ... -cipher AES128-SHA256 -#endif -#if ALLOW_RSA_WITH_AES_256_CBC_SHA256 - 0x00,0x3D, //12 TLS_RSA_WITH_AES_256_CBC_SHA256 - ok: openssl s_server ... -cipher AES256-SHA256 -#endif -#if ALLOW_RSA_WITH_AES_128_GCM_SHA256 - 0x00,0x9C, //13 TLS_RSA_WITH_AES_128_GCM_SHA256 - ok: openssl s_server ... -cipher AES128-GCM-SHA256 -#endif - // 0x00,0x9D, // TLS_RSA_WITH_AES_256_GCM_SHA384 - openssl s_server ... -cipher AES256-GCM-SHA384: "decryption failed or bad record mac" -#if ALLOW_RSA_NULL_SHA256 - 0x00,0x3B, // TLS_RSA_WITH_NULL_SHA256 -#endif - 0x01,0x00, //not a cipher - comprtypes_len, comprtype - }; struct client_hello { uint8_t type; uint8_t len24_hi, len24_mid, len24_lo; @@ -1556,8 +1628,8 @@ static void send_client_hello_and_alloc_hsd(tls_state_t *tls, const char *sni) memset(record->rand32, 0x11, sizeof(record->rand32)); /* record->session_id_len = 0; - already is */ - BUILD_BUG_ON(sizeof(ciphers) != 2 * (1 + 1 + NUM_CIPHERS + 1)); - memcpy(&record->cipherid_len16_hi, ciphers, sizeof(ciphers)); + BUILD_BUG_ON(sizeof(client_hello_ciphers) != 2 * (1 + 1 + NUM_CIPHERS + 1)); + memcpy(&record->cipherid_len16_hi, client_hello_ciphers, sizeof(client_hello_ciphers)); ptr = (void*)(record + 1); *ptr++ = ext_len >> 8; @@ -1612,7 +1684,6 @@ static void get_server_hello(tls_state_t *tls) struct server_hello *hp; uint8_t *cipherid; - uint8_t cipherid1; int len, len24; len = tls_xread_handshake_block(tls, 74 - 32); @@ -1651,74 +1722,7 @@ static void get_server_hello(tls_state_t *tls) memcpy(tls->hsd->client_and_server_rand32 + 32, hp->rand32, sizeof(hp->rand32)); - /* Set up encryption params based on selected cipher */ -#if 0 - 0xC0,0x09, // 1 TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA - ok: wget https://is.gd/ - 0xC0,0x0A, // 2 TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA - ok: wget https://is.gd/ - 0xC0,0x13, // 3 TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA - ok: openssl s_server ... -cipher ECDHE-RSA-AES128-SHA - 0xC0,0x14, // 4 TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA - ok: openssl s_server ... -cipher ECDHE-RSA-AES256-SHA (might fail with older openssl) - // 0xC0,0x18, // TLS_ECDH_anon_WITH_AES_128_CBC_SHA - // 0xC0,0x19, // TLS_ECDH_anon_WITH_AES_256_CBC_SHA - 0xC0,0x23, // 5 TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 - ok: wget https://is.gd/ - // 0xC0,0x24, // TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 - can't do SHA384 yet - 0xC0,0x27, // 6 TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 - ok: openssl s_server ... -cipher ECDHE-RSA-AES128-SHA256 - // 0xC0,0x28, // TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 - can't do SHA384 yet - 0xC0,0x2B, // 7 TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 - ok: wget https://is.gd/ - // 0xC0,0x2C, // TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 - wget https://is.gd/: "TLS error from peer (alert code 20): bad MAC" - 0xC0,0x2F, // 8 TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 - ok: openssl s_server ... -cipher ECDHE-RSA-AES128-GCM-SHA256 - // 0xC0,0x30, // TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 - openssl s_server ... -cipher ECDHE-RSA-AES256-GCM-SHA384: "decryption failed or bad record mac" - //possibly these too: - // 0xC0,0x35, // TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA - // 0xC0,0x36, // TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA - // 0xC0,0x37, // TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 - // 0xC0,0x38, // TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384 - can't do SHA384 yet - 0x00,0x2F, // 9 TLS_RSA_WITH_AES_128_CBC_SHA - ok: openssl s_server ... -cipher AES128-SHA - 0x00,0x35, //10 TLS_RSA_WITH_AES_256_CBC_SHA - ok: openssl s_server ... -cipher AES256-SHA - 0x00,0x3C, //11 TLS_RSA_WITH_AES_128_CBC_SHA256 - ok: openssl s_server ... -cipher AES128-SHA256 - 0x00,0x3D, //12 TLS_RSA_WITH_AES_256_CBC_SHA256 - ok: openssl s_server ... -cipher AES256-SHA256 - 0x00,0x9C, //13 TLS_RSA_WITH_AES_128_GCM_SHA256 - ok: openssl s_server ... -cipher AES128-GCM-SHA256 - // 0x00,0x9D, // TLS_RSA_WITH_AES_256_GCM_SHA384 - openssl s_server ... -cipher AES256-GCM-SHA384: "decryption failed or bad record mac" - 0x00,0x3B, // TLS_RSA_WITH_NULL_SHA256 -#endif - cipherid1 = cipherid[1]; - tls->cipher_id = 0x100 * cipherid[0] + cipherid1; - tls->key_size = AES256_KEYSIZE; - tls->MAC_size = SHA256_OUTSIZE; - /*tls->IV_size = 0; - already is */ - if (cipherid[0] == 0xC0) { - /* All C0xx are ECDHE */ - tls->flags |= NEED_EC_KEY; - if (cipherid1 & 1) { - /* Odd numbered C0xx use AES128 (even ones use AES256) */ - tls->key_size = AES128_KEYSIZE; - } - if (ENABLE_FEATURE_TLS_SHA1 && cipherid1 <= 0x19) { - tls->MAC_size = SHA1_OUTSIZE; - } else - if (cipherid1 >= 0x2B && cipherid1 <= 0x30) { - /* C02B,2C,2F,30 are AES-GCM */ - tls->flags |= ENCRYPTION_AESGCM; - tls->MAC_size = 0; - tls->IV_size = 4; - } - } else { - /* All 00xx are RSA */ - if ((ENABLE_FEATURE_TLS_SHA1 && cipherid1 == 0x2F) - || cipherid1 == 0x3C - || cipherid1 == 0x9C - ) { - tls->key_size = AES128_KEYSIZE; - } - if (ENABLE_FEATURE_TLS_SHA1 && cipherid1 <= 0x35) { - tls->MAC_size = SHA1_OUTSIZE; - } else - if (cipherid1 == 0x9C /*|| cipherid1 == 0x9D*/) { - /* 009C,9D are AES-GCM */ - tls->flags |= ENCRYPTION_AESGCM; - tls->MAC_size = 0; - tls->IV_size = 4; - } - } + set_cipher_parameters(tls, cipherid); dbg("server chose cipher %04x", tls->cipher_id); dbg("key_size:%u MAC_size:%u IV_size:%u", tls->key_size, tls->MAC_size, tls->IV_size); @@ -2424,6 +2428,7 @@ static void get_client_hello(tls_state_t *tls) uint8_t session_id_len; /* followed by session_id, cipher suites, compression methods, extensions */ }; + unsigned i, j; struct client_hello *hp; uint8_t *p; unsigned cipher_list_len; @@ -2434,14 +2439,13 @@ static void get_client_hello(tls_state_t *tls) hp = (void*)(tls->inbuf + RECHDR_LEN); if (hp->type != HANDSHAKE_CLIENT_HELLO || hp->len24_hi != 0 - || hp->len24_mid != 0 - /* hp->len24_lo checked later */ + /* hp->len24_mid,lo checked later */ || hp->proto_maj != TLS_MAJ ///? || hp->proto_min != TLS_MIN ) { bad_record_die(tls, "'client hello'", len); } - dbg("<< CLIENT_HELLO len:%d len24:%d", len, hp->len24_lo); + dbg("<< CLIENT_HELLO len:%d len24:%d", len, (hp->len24_mid << 8) | hp->len24_lo); /* Save client random */ memcpy(tls->hsd->client_and_server_rand32, hp->rand32, 32); @@ -2469,15 +2473,49 @@ static void get_client_hello(tls_state_t *tls) bb_simple_error_msg_and_die("malformed ClientHello"); } -//FIXME: proper cipher suite selection - see get_server_hello() - tls->cipher_id = TLS_RSA_WITH_AES_128_CBC_SHA256; - //tls->flags = 0; /* RSA mode, no ECDHE */ - /* Set cipher parameters for TLS_RSA_WITH_AES_128_CBC_SHA256 (0x003C) */ - tls->key_size = AES128_KEYSIZE; /* 16 bytes */ - tls->MAC_size = SHA256_OUTSIZE; /* 32 bytes */ - //tls->IV_size = 0; /* For CBC mode, IV is sent with each encrypted record */ + /* Select cipher + cert pair from client's list, preferring our ciphers in order */ + for (i = 0; i < NUM_CIPHERS*2; i += 2) { + const uint8_t *our_cipher = &supported_ciphers[i]; + int key_type; - dbg("Selected cipher: %04x", tls->cipher_id); + /* Skip all ECDHE ciphers (0xC0xx) - we don't support server-side ephemeral + * EC key generation yet. ECDHE_RSA uses RSA certificates (which we have) + * but still requires generating ephemeral EC keys for the key exchange. + * We only support plain RSA key exchange (0x00xx) on the server side. + */ + if (our_cipher[0] == 0xC0) { + //TODO: implement server-side ECDHE + continue; + } + + /* Determine required key type for this cipher */ + key_type = is_cipher_ECDSA(our_cipher); + if (key_type == KEY_ECDSA) { + if (!tls->hsd->keys[KEY_ECDSA]) + /* No ECDSA cert configured, can't choose this */ + continue; + //TODO: ECDSA not supported yet at all + continue; + } else { + if (!tls->hsd->keys[KEY_RSA]) + /* No RSA cert configured, can't choose this */ + continue; + /* We _can_ choose this! */ + } + + /* Check if this cipher is in client's list */ + for (j = 0; j < cipher_list_len; j += 2) { + if (p[j] == our_cipher[0] && p[j + 1] == our_cipher[1]) { + /* Found a match! */ + set_cipher_parameters(tls, our_cipher); + dbg("Selected cipher: %04x", tls->cipher_id); + tls->hsd->key_type_chosen = key_type; + return; + } + } + /* try our next cipherid */ + } + bb_simple_error_msg_and_die("no common cipher suites"); } static void send_server_hello(tls_state_t *tls) @@ -2510,7 +2548,7 @@ static void send_server_hello(tls_state_t *tls) /* Selected cipher suite */ record->cipherid_hi = tls->cipher_id >> 8; - record->cipherid_lo = tls->cipher_id & 0xff; + record->cipherid_lo = tls->cipher_id; /* & 0xff implicit */ /* No compression */ //record->comprtype = 0; @@ -2532,37 +2570,14 @@ static void send_server_hello(tls_state_t *tls) static void send_server_certificate(tls_state_t *tls) { - struct certificate_msg { - uint8_t type; - uint8_t len24_hi, len24_mid, len24_lo; - uint8_t cert_chain_len24_hi, cert_chain_len24_mid, cert_chain_len24_lo; - uint8_t cert1_len24_hi, cert1_len24_mid, cert1_len24_lo; - /* followed by certificate DER data */ - }; - struct certificate_msg *record; - unsigned total_len, cert_len, chain_len; + void *record; + int n = tls->hsd->key_type_chosen; + int sz = tls->hsd->certsize[n]; - cert_len = tls->hsd->server_cert_der_len; - total_len = sizeof(*record) + cert_len; - - record = get_outbuf_fill_handshake_record(tls, HANDSHAKE_CERTIFICATE, total_len); - - /* Certificate chain length (just one cert for now) */ - chain_len = cert_len + 3; /* 3 bytes for cert length */ - record->cert_chain_len24_hi = chain_len >> 16; - record->cert_chain_len24_mid = (chain_len >> 8) & 0xff; - record->cert_chain_len24_lo = chain_len & 0xff; - - /* First certificate length */ - record->cert1_len24_hi = cert_len >> 16; - record->cert1_len24_mid = (cert_len >> 8) & 0xff; - record->cert1_len24_lo = cert_len & 0xff; - - /* Copy certificate DER data */ - memcpy(record + 1, tls->hsd->server_cert_der, cert_len); - - dbg(">> CERTIFICATE (len=%u)", cert_len); - xwrite_and_update_handshake_hash(tls, total_len); + record = tls_get_outbuf(tls, sz); + memcpy(record, tls->hsd->certs[n], sz); + dbg(">> CERTIFICATE"); + xwrite_and_update_handshake_hash(tls, sz); } static void send_server_hello_done(tls_state_t *tls) @@ -2615,9 +2630,10 @@ static void get_client_key_exchange(tls_state_t *tls) { int32 ret; uint32 premaster_len; + psRsaKey_t *key = &tls->hsd->rsa_priv_key; premaster_len = RSA_PREMASTER_SIZE; - ret = psRsaDecryptPriv(NULL, &tls->hsd->server_rsa_priv_key, + ret = psRsaDecryptPriv(NULL, key, encrypted_premaster, enckey_len, premaster, premaster_len, NULL); @@ -2665,16 +2681,10 @@ static void get_client_key_exchange(tls_state_t *tls) * } */ static NOINLINE /* don't inline - large stack use */ -void load_rsa_priv_key(psRsaKey_t *key, const char *filename) +void load_rsa_priv_key(psRsaKey_t *key, uint8_t *buf, ssize_t sz) { - uint8_t buf[4*1024]; /* DER key files are usually ~1kbyte */ - ssize_t sz; uint8_t *der, *end; - sz = open_read_close(filename, buf, sizeof(buf)); - if (sz < 0) - bb_perror_msg_and_die("can't read '%s'", filename); - der = buf; end = der + sz; @@ -2690,15 +2700,12 @@ void load_rsa_priv_key(psRsaKey_t *key, const char *filename) if (*der == 0x30) { /* PKCS#8 format - skip AlgorithmIdentifier and enter OCTET STRING */ - dbg("Detected PKCS#8 private key format"); der = skip_der_item(der, end); /* Skip AlgorithmIdentifier */ der = enter_der_item(der, &end); /* Enter OCTET STRING containing PKCS#1 key */ der = enter_der_item(der, &end); /* Enter the PKCS#1 SEQUENCE */ der = skip_der_item(der, end); /* Skip version again */ - } else { - /* PKCS#1 format - we already skipped the version */ - dbg("Detected PKCS#1 private key format"); } + /* else: PKCS#1 format - we already skipped the version */ /* Read the key components */ der_binary_to_pstm(&key->N, der, end); /* modulus */ @@ -2726,26 +2733,203 @@ void load_rsa_priv_key(psRsaKey_t *key, const char *filename) key->size = pstm_unsigned_bin_size(&key->N); key->optimized = 1; /* We have CRT parameters */ +} - dbg("Loaded RSA private key, size:%d", key->size); +static char *decode_base64_or_die(char *dst, const char *src) +{ + char *dst_end = decode_base64(dst, &src); + if (*src != '\0') + bb_error_msg_and_die("base64 decode error"); + return dst_end; +} + +/* Parse PEM file and extract key + cert chain pairs + * Returns number of pairs loaded + */ +static void load_pem_key_cert_pairs(tls_state_t *tls, const char *pem_filename) +{ + static const char BLOCK_NAMES[] ALIGN1 = + "EC PARAMETERS" "\0" + "CERTIFICATE" "\0" + "EC PRIVATE KEY" "\0" + "PRIVATE KEY" "\0" + "RSA PRIVATE KEY""\0" + ; + enum { + str_EC_PARAMETERS = 0, + str_CERTIFICATE = 1, + str_EC_KEY = 2, + }; + char *p; + char *pem_data; + size_t pem_size; + char *der_data; + unsigned der_size; + int keyidx; + + /* Read PEM file */ + pem_size = 64 * 1024; /* sanity limit */ + pem_data = xmalloc_xopen_read_close(pem_filename, &pem_size); + + der_data = NULL; + der_size = 0; + keyidx = -1; /* "we did not see any KEY yet" */ + + p = pem_data; + while (1) { + unsigned n; + char *block_end; + char *block_type_end; + + /* Find next PEM block */ + p = skip_whitespace(p); + if (*p == '\0') + break; /* end of file */ + p = is_prefixed_with(p, "-----BEGIN "); + if (!p) + goto err; + block_type_end = strstr(p, "-----\n"); + if (!block_type_end) + goto err; + block_type_end += 5; + block_end = strstr(block_type_end, "\n-----END "); + if (!block_end) + goto err; + *block_end = '\0'; + block_end += 10; +//-----BEGIN PRIVATE KEY-----\n +// ^p ^block_type_end +//BASE64HERE-BASE64HERE +//-----END PRIVATE KEY----- +// ^block_end + /* The headers must match */ + *block_type_end = '\0'; + if (!is_prefixed_with(block_end, p)) + goto err; + /* Truncate trailing dashes from block type name */ + block_type_end[-5] = '\0'; + + block_end += (block_type_end - p); + block_type_end++; +//BASE64HERE-BASE64HERE +//^block_type_end +//-----END PRIVATE KEY----- +// ^block_end + n = index_in_strings(BLOCK_NAMES, p); + if ((int)n < 0) + bb_error_msg_and_die("'%s': unknown PEM block '%s'", pem_filename, p); + + /* Note: may point to "\n" or even NUL */ + p = block_end; + + /* What block do we see? */ + + if (n == str_EC_PARAMETERS) { + /* "openssl ecparam -genkey" generates these, skip silently */ + continue; /* skip */ + } + + if (n == str_CERTIFICATE) { + struct certificate_msg { + uint8_t type; + uint8_t len24_hi, len24_mid, len24_lo; + uint8_t cert_chain_len24_hi, cert_chain_len24_mid, cert_chain_len24_lo; + uint8_t cert1_len24_hi, cert1_len24_mid, cert1_len24_lo; + /* followed by certificate DER data */ + /* followed by cert2_len24, cert2 DER data, ... */ + }; + struct certificate_msg *cert_msg; + unsigned start; + + if (keyidx < 0) + bb_error_msg_and_die("'%s': certificate must be after key", pem_filename); + + /* We create or update a full HANDSHAKE_CERTIFICATE message */ + if (der_size == 0) { + der_size = sizeof(*cert_msg); + der_data = xzalloc(der_size); + cert_msg = (void*)der_data; + cert_msg->type = HANDSHAKE_CERTIFICATE; + } else { + /* We are here when we decode second or later cert */ + der_size += 3; /* for len24 */ + } + + /* Decode BASE64 */ + start = der_size; + der_size += block_end - block_type_end; /* worst case size */ + der_data = xrealloc(der_data, der_size); + der_size = decode_base64_or_die(der_data + start, block_type_end) - der_data; + der_data = xrealloc(der_data, der_size); + + /* Fill last cert's len24 */ + n = der_size - start; + der_data[start - 3] = n >> 16; + der_data[start - 2] = n >> 8; + der_data[start - 1] = n; + /* Update sizes in header */ + cert_msg = (void*)der_data; + n = der_size - 4; + cert_msg->len24_hi = n >> 16; + cert_msg->len24_mid = n >> 8; + cert_msg->len24_lo = n; + n -= 3; + cert_msg->cert_chain_len24_hi = n >> 16; + cert_msg->cert_chain_len24_mid = n >> 8; + cert_msg->cert_chain_len24_lo = n; + + tls->hsd->certs[keyidx] = der_data; + tls->hsd->certsize[keyidx] = der_size; + continue; + } + + /* We see a key */ + + /* Decode BASE64 */ + der_size = block_end - block_type_end; /* worst case size */ + der_data = xmalloc(der_size); + der_size = decode_base64_or_die(der_data, block_type_end) - der_data; + der_data = xrealloc(der_data, der_size); + + keyidx = (n == str_EC_KEY) ? KEY_ECDSA : KEY_RSA; + if (tls->hsd->keys[keyidx]) + bb_error_msg_and_die("'%s': more than one key", pem_filename); + tls->hsd->keys[keyidx] = der_data; + tls->hsd->keysize[keyidx] = der_size; + + der_data = NULL; + der_size = 0; + } /* while (parsing PEM) */ + free(pem_data); + + if (!tls->hsd->keys[KEY_RSA] && !tls->hsd->keys[KEY_ECDSA]) + bb_error_msg_and_die("'%s': no private keys", pem_filename); + + if (tls->hsd->keys[KEY_RSA]) { + if (!tls->hsd->certs[KEY_RSA]) + bb_error_msg_and_die("'%s': key with no cert", pem_filename); + /* Parse RSA key from DER */ + load_rsa_priv_key(&tls->hsd->rsa_priv_key, (uint8_t*)tls->hsd->keys[KEY_RSA], tls->hsd->keysize[KEY_RSA]); + } + if (tls->hsd->keys[KEY_ECDSA]) { + if (!tls->hsd->certs[KEY_ECDSA]) + bb_error_msg_and_die("'%s': key with no cert", pem_filename); + bb_error_msg("'%s': ECDSA keys not supported", pem_filename); + } + + return; + err: + bb_error_msg_and_die("malformed PEM file at '%.*s'", (int)(skip_whitespace(p) - p), p); } void FAST_FUNC tls_handshake_as_server(tls_state_t *tls, - const char *privkey_der_filename, - const char *cert_der_filename) + const char *pem_filename) { - dbg("Starting TLS server handshake"); - /* Allocate handshake data */ tls->hsd = xzalloc(sizeof(*tls->hsd)); - /* Load server private key */ - tls->hsd->server_cert_der_len = 64*1024; /* sanity limit (don't load multi-megabyte "certificates") */ - tls->hsd->server_cert_der = xmalloc_xopen_read_close(cert_der_filename, &tls->hsd->server_cert_der_len); - - /* Load server certificate */ - load_rsa_priv_key(&tls->hsd->server_rsa_priv_key, privkey_der_filename); - dbg("Loaded private key: %d bytes", tls->hsd->server_rsa_priv_key.size); + /* Load server key(s) and certificate(s) from PEM file */ + load_pem_key_cert_pairs(tls, pem_filename); sha256_begin(&tls->hsd->handshake_hash_ctx); tls->expecting_first_packet = 1; @@ -2776,13 +2960,14 @@ void FAST_FUNC tls_handshake_as_server(tls_state_t *tls, send_change_cipher_spec(tls); send_finished(tls, "server finished"); - dbg("Server handshake complete"); - /* application data can be sent/received */ /* free handshake data */ - psRsaKey_clear(&tls->hsd->server_rsa_priv_key); - free(tls->hsd->server_cert_der); + psRsaKey_clear(&tls->hsd->rsa_priv_key); + free(tls->hsd->keys[0]); + free(tls->hsd->keys[1]); + free(tls->hsd->certs[0]); + free(tls->hsd->certs[1]); // if (PARANOIA) // memset(tls->hsd, 0, sizeof(*tls->hsd)); free(tls->hsd);