diff options
Diffstat (limited to 'epan/dissectors/packet-ssl-utils.c')
-rw-r--r-- | epan/dissectors/packet-ssl-utils.c | 156 |
1 files changed, 111 insertions, 45 deletions
diff --git a/epan/dissectors/packet-ssl-utils.c b/epan/dissectors/packet-ssl-utils.c index 76ff8f0865..76dd8d481a 100644 --- a/epan/dissectors/packet-ssl-utils.c +++ b/epan/dissectors/packet-ssl-utils.c @@ -57,6 +57,11 @@ #if defined(HAVE_LIBGNUTLS) && defined(HAVE_LIBGCRYPT) #include <gnutls/abstract.h> #endif +#if GCRYPT_VERSION_NUMBER >= 0x010600 /* 1.6.0 */ +/* Whether to provide support for authentication in addition to decryption. */ +#define HAVE_LIBGCRYPT_AEAD +#define HAVE_LIBGCRYPT_CCM +#endif /* Lookup tables {{{ */ const value_string ssl_version_short_names[] = { @@ -1842,7 +1847,19 @@ static gint ssl_cipher_init(gcry_cipher_hd_t *cipher, gint algo, guchar* sk, guchar* iv, gint mode) { - gint gcry_modes[]={GCRY_CIPHER_MODE_STREAM,GCRY_CIPHER_MODE_CBC,GCRY_CIPHER_MODE_CTR,GCRY_CIPHER_MODE_CTR,GCRY_CIPHER_MODE_CTR}; + gint gcry_modes[] = { + GCRY_CIPHER_MODE_STREAM, + GCRY_CIPHER_MODE_CBC, +#ifdef HAVE_LIBGCRYPT_AEAD + GCRY_CIPHER_MODE_GCM, + GCRY_CIPHER_MODE_CCM, + GCRY_CIPHER_MODE_CCM +#else + GCRY_CIPHER_MODE_CTR, + GCRY_CIPHER_MODE_CTR, + GCRY_CIPHER_MODE_CTR, +#endif + }; gint err; if (algo == -1) { /* NULL mode */ @@ -2686,12 +2703,12 @@ ssl_change_cipher(SslDecryptSession *ssl_session, gboolean server) } int -ssl_decrypt_record(SslDecryptSession*ssl, SslDecoder* decoder, gint ct, - const guchar* in, guint inl, StringInfo* comp_str _U_, StringInfo* out, guint* outl) +ssl_decrypt_record(SslDecryptSession *ssl,SslDecoder *decoder, guint8 ct, guint16 record_version, + const guchar *in, guint16 inl, StringInfo *comp_str, StringInfo *out_str, guint *outl) { ssl_debug_printf("ssl_decrypt_record: impossible without gnutls. ssl %p" - "decoder %p ct %d, in %p inl %d out %p outl %p\n", ssl, decoder, ct, - in, inl, out, outl); + "decoder %p ct %d, version %d in %p inl %d out %p outl %p\n", ssl, decoder, ct, + record_version, in, inl, out, outl); return 0; } /* }}} */ @@ -2847,7 +2864,7 @@ ssl_create_decoder(const SslCipherSuite *cipher_suite, gint cipher_algo, // decoders since "decryption" is easy for such ciphers. dec->mac_key.data = dec->_mac_key_or_write_iv; ssl_data_set(&dec->mac_key, mk, ssl_cipher_suite_dig(cipher_suite)->len); - } else if (mode == MODE_GCM || mode == MODE_CCM || mode == MODE_CCM_8) { + } else if (ssl_is_aead_cipher(mode)) { /* So far all AEAD ciphers derive the 4-byte implicit nonce part from the write IV */ dec->write_iv.data = dec->_mac_key_or_write_iv; ssl_data_set(&dec->write_iv, iv, 4); @@ -3153,7 +3170,7 @@ ssl_generate_keyring_material(SslDecryptSession*ssl_session) if (cipher_suite->mode == MODE_CBC) { write_iv_len = (guint)gcry_cipher_get_algo_blklen(cipher_algo); - } else if (cipher_suite->mode == MODE_GCM || cipher_suite->mode == MODE_CCM || cipher_suite->mode == MODE_CCM_8) { + } else if (ssl_is_aead_cipher(cipher_suite->mode)) { /* account for a four-byte salt for client and server side (from * client_write_IV and server_write_IV), see GCMNonce (RFC 5288) */ write_iv_len = 4; @@ -3562,11 +3579,13 @@ dtls_check_mac(SslDecoder*decoder, gint ct,int ver, guint8* data, /* Assume that we are called only for a non-NULL decoder which also means that * we have a non-NULL decoder->cipher_suite. */ int -ssl_decrypt_record(SslDecryptSession*ssl,SslDecoder* decoder, gint ct, - const guchar* in, guint inl, StringInfo* comp_str, StringInfo* out_str, guint* outl) +ssl_decrypt_record(SslDecryptSession *ssl,SslDecoder *decoder, guint8 ct, guint16 record_version, + const guchar *in, guint16 inl, StringInfo *comp_str, StringInfo *out_str, guint *outl) { guint pad, worklen, uncomplen; guint8 *mac; + guint auth_tag_len = 0; + gcry_error_t err; ssl_debug_printf("ssl_decrypt_record ciphertext len %d\n", inl); ssl_print_data("Ciphertext",in, inl); @@ -3618,9 +3637,7 @@ ssl_decrypt_record(SslDecryptSession*ssl,SslDecoder* decoder, gint ct, } /* Nonce for GenericAEADCipher */ - if (decoder->cipher_suite->mode == MODE_GCM || - decoder->cipher_suite->mode == MODE_CCM || - decoder->cipher_suite->mode == MODE_CCM_8) { + if (ssl_is_aead_cipher(decoder->cipher_suite->mode)) { /* 4 bytes write_iv, 8 bytes explicit_nonce, 4 bytes counter */ guchar gcm_nonce[16] = { 0 }; @@ -3630,65 +3647,98 @@ ssl_decrypt_record(SslDecryptSession*ssl,SslDecoder* decoder, gint ct, return -1; } + if (decoder->write_iv.data_len != 4) { + ssl_debug_printf("Unexpected implicit nonce length %d!\n", decoder->write_iv.data_len); + return -1; + } + + /* Implicit (4) and explicit (8) part of nonce. */ + memcpy(gcm_nonce, decoder->write_iv.data, decoder->write_iv.data_len); /* salt */ + memcpy(gcm_nonce + decoder->write_iv.data_len, in, SSL_EX_NONCE_LEN_GCM); + if (decoder->cipher_suite->mode == MODE_GCM) { - memcpy(gcm_nonce, decoder->write_iv.data, decoder->write_iv.data_len); /* salt */ - memcpy(gcm_nonce + decoder->write_iv.data_len, in, SSL_EX_NONCE_LEN_GCM); /* NIST SP 800-38D, sect. 7.2 says that the 32-bit counter part starts * at 1, and gets incremented before passing to the block cipher. */ gcm_nonce[4 + SSL_EX_NONCE_LEN_GCM + 3] = 2; + auth_tag_len = 16; } else { /* MODE_CCM and MODE_CCM_8 */ /* The nonce for CCM and GCM are the same, but the nonce is used as input * in the CCM algorithm described in RFC 3610. The nonce generated here is * the one from RFC 3610 sect 2.3. Encryption. */ +#ifndef HAVE_LIBGCRYPT_AEAD + memmove(gcm_nonce + 1, gcm_nonce, 4 + SSL_EX_NONCE_LEN_GCM); /* Flags: (L-1) ; L = 16 - 1 - nonceSize */ gcm_nonce[0] = 3 - 1; - /* struct { opaque salt[4]; opaque nonce_explicit[8] } CCMNonce (RFC 6655) */ - memcpy(gcm_nonce + 1, decoder->write_iv.data, decoder->write_iv.data_len); /* salt */ - memcpy(gcm_nonce + 1 + decoder->write_iv.data_len, in, SSL_EX_NONCE_LEN_GCM); gcm_nonce[4 + SSL_EX_NONCE_LEN_GCM + 3] = 1; +#endif + auth_tag_len = decoder->cipher_suite->mode == MODE_CCM_8 ? 8 : 16; + } + + if (inl < SSL_EX_NONCE_LEN_GCM + auth_tag_len) { + ssl_debug_printf("%s failed: missing tag of length %d, available %d\n", G_STRFUNC, auth_tag_len, inl); + return -1; + } + /* Decrypted fragment without explicit nonce and trailing auth tag. */ + worklen = inl - SSL_EX_NONCE_LEN_GCM - auth_tag_len; + +#ifdef HAVE_LIBGCRYPT_AEAD + gcry_cipher_reset(decoder->evp); + ssl_print_data("nonce", gcm_nonce, 12); + err = gcry_cipher_setiv(decoder->evp, gcm_nonce, 12); + if (err) { + ssl_debug_printf("%s failed to set nonce: %s\n", G_STRFUNC, gcry_strerror(err)); + return -1; + } + + if (decoder->cipher_suite->mode == MODE_CCM || decoder->cipher_suite->mode == MODE_CCM_8) { + guint64 lengths[3] = { + worklen, /* size of plaintext */ + 13, /* size of additional authenticated data */ + auth_tag_len + }; + gcry_cipher_ctl(decoder->evp, GCRYCTL_SET_CCM_LENGTHS, lengths, sizeof(lengths)); } - pad = gcry_cipher_setctr (decoder->evp, gcm_nonce, sizeof (gcm_nonce)); - if (pad != 0) { + guchar aad[13]; + // XXX sequence number is sometimes one too small, why? + phton64(aad, decoder->seq); /* record sequence number */ + aad[8] = ct; /* TLSCompressed.type */ + phton16(aad + 9, record_version); /* TLSCompressed.version */ + phton16(aad + 11, worklen); /* TLSCompressed.length */ + ssl_print_data("AAD", aad, sizeof(aad)); + err = gcry_cipher_authenticate(decoder->evp, aad, sizeof(aad)); + if (err) { + ssl_debug_printf("%s failed to set AAD: %s\n", G_STRFUNC, gcry_strerror(err)); + return -1; + } +#else + (void)record_version; + err = gcry_cipher_setctr(decoder->evp, gcm_nonce, sizeof(gcm_nonce)); + if (err != 0) { ssl_debug_printf("ssl_decrypt_record failed: failed to set CTR: %s %s\n", - gcry_strsource (pad), gcry_strerror (pad)); + gcry_strsource (err), gcry_strerror (err)); return -1; } +#endif inl -= SSL_EX_NONCE_LEN_GCM; in += SSL_EX_NONCE_LEN_GCM; + } else { + /* For other ciphers (like CBC), assume plaintext to have the same + * length as its input. Padding will be removed later. */ + worklen = inl; } /* First decrypt*/ - if ((pad = ssl_cipher_decrypt(&decoder->evp, out_str->data, out_str->data_len, in, inl))!= 0) { + memset(out_str->data, 0xcc, out_str->data_len); /// XXX remove me + if ((pad = ssl_cipher_decrypt(&decoder->evp, out_str->data, out_str->data_len, in, worklen)) != 0) { ssl_debug_printf("ssl_decrypt_record failed: ssl_cipher_decrypt: %s %s\n", gcry_strsource (pad), gcry_strerror (pad)); return -1; } - ssl_print_data("Plaintext", out_str->data, inl); - worklen=inl; + ssl_print_data("Plaintext", out_str->data, worklen); - /* RFC 5116 sect 5.1/5.3: AES128/256 GCM/CCM uses 16 bytes for auth tag - * RFC 6655 sect 6.1: AEAD_AES_128_CCM uses 16 bytes for auth tag */ - if (decoder->cipher_suite->mode == MODE_GCM || - decoder->cipher_suite->mode == MODE_CCM) { - if (worklen < 16) { - ssl_debug_printf("ssl_decrypt_record failed: missing tag, work %d\n", worklen); - return -1; - } - /* XXX - validate auth tag */ - worklen -= 16; - } - /* RFC 6655 sect 6.1: AEAD_AES_128_CCM_8 uses 8 bytes for auth tag */ - if (decoder->cipher_suite->mode == MODE_CCM_8) { - if (worklen < 8) { - ssl_debug_printf("ssl_decrypt_record failed: missing tag, work %d\n", worklen); - return -1; - } - /* XXX - validate auth tag */ - worklen -= 8; - } /* strip padding for GenericBlockCipher */ if (decoder->cipher_suite->mode == MODE_CBC) { @@ -3716,8 +3766,24 @@ ssl_decrypt_record(SslDecryptSession*ssl,SslDecoder* decoder, gint ct, } worklen-=ssl_cipher_suite_dig(decoder->cipher_suite)->len; mac = out_str->data + worklen; - } else /* if (decoder->cipher_suite->mode == MODE_GCM) */ { - /* GenericAEADCipher has no MAC */ + } else /* if (ssl_is_aead_cipher(decoder->cipher_suite->mode)) */ { + /* GenericAEADCipher has no MAC, check auth tag for authenticity. */ +#ifdef HAVE_LIBGCRYPT_AEAD + guchar auth_tag[16]; + err = gcry_cipher_gettag(decoder->evp, auth_tag, auth_tag_len); + if (err) { + ssl_debug_printf("%s failed: cannot obtain tag: %s %s\n", G_STRFUNC, + gcry_strsource(err), gcry_strerror(err)); + } else if (!memcmp(auth_tag, in + worklen, auth_tag_len)) { + ssl_debug_printf("%s auth tag OK\n", G_STRFUNC); + } else { + ssl_debug_printf("%s auth tag mismatch\n", G_STRFUNC); + ssl_print_data("auth_tag(expect)", auth_tag, auth_tag_len); + ssl_print_data("auth_tag(actual)", in + worklen, auth_tag_len); + } +#else + ssl_debug_printf("Libgcrypt is older than 1.6, unable to verify authenticity!\n"); +#endif goto skip_mac; } |