From 6c75fbeb689a4a6974cf8563cfed0607f7f765f0 Mon Sep 17 00:00:00 2001 From: doug <> Date: Sun, 27 Aug 2017 02:58:04 +0000 Subject: Rewrite SRTP extension using CBB/CBS and the new extension framework. input + ok beck@, jsing@ --- src/lib/libssl/d1_srtp.c | 220 +--------------- src/lib/libssl/ssl_locl.h | 11 +- src/lib/libssl/ssl_tlsext.c | 224 +++++++++++++++- src/lib/libssl/ssl_tlsext.h | 11 +- src/lib/libssl/t1_lib.c | 58 +---- src/regress/lib/libssl/tlsext/tlsexttest.c | 403 ++++++++++++++++++++++++++++- 6 files changed, 652 insertions(+), 275 deletions(-) diff --git a/src/lib/libssl/d1_srtp.c b/src/lib/libssl/d1_srtp.c index 26c14543fc..eb1877a12c 100644 --- a/src/lib/libssl/d1_srtp.c +++ b/src/lib/libssl/d1_srtp.c @@ -1,4 +1,4 @@ -/* $OpenBSD: d1_srtp.c,v 1.21 2017/02/07 02:08:38 beck Exp $ */ +/* $OpenBSD: d1_srtp.c,v 1.22 2017/08/27 02:58:04 doug Exp $ */ /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) * All rights reserved. * @@ -138,8 +138,8 @@ static SRTP_PROTECTION_PROFILE srtp_known_profiles[] = { {0} }; -static int -find_profile_by_name(char *profile_name, SRTP_PROTECTION_PROFILE **pptr, +int +srtp_find_profile_by_name(char *profile_name, SRTP_PROTECTION_PROFILE **pptr, unsigned len) { SRTP_PROTECTION_PROFILE *p; @@ -158,8 +158,8 @@ find_profile_by_name(char *profile_name, SRTP_PROTECTION_PROFILE **pptr, return 1; } -static int -find_profile_by_num(unsigned profile_num, SRTP_PROTECTION_PROFILE **pptr) +int +srtp_find_profile_by_num(unsigned profile_num, SRTP_PROTECTION_PROFILE **pptr) { SRTP_PROTECTION_PROFILE *p; @@ -194,7 +194,7 @@ ssl_ctx_make_profiles(const char *profiles_string, do { col = strchr(ptr, ':'); - if (!find_profile_by_name(ptr, &p, + if (!srtp_find_profile_by_name(ptr, &p, col ? col - ptr : (int)strlen(ptr))) { sk_SRTP_PROTECTION_PROFILE_push(profiles, p); } else { @@ -246,212 +246,4 @@ SSL_get_selected_srtp_profile(SSL *s) return s->internal->srtp_profile; } -/* Note: this function returns 0 length if there are no - profiles specified */ -int -ssl_add_clienthello_use_srtp_ext(SSL *s, unsigned char *p, int *len, int maxlen) -{ - int ct = 0; - int i; - STACK_OF(SRTP_PROTECTION_PROFILE) *clnt = 0; - SRTP_PROTECTION_PROFILE *prof; - - clnt = SSL_get_srtp_profiles(s); - - ct = sk_SRTP_PROTECTION_PROFILE_num(clnt); /* -1 if clnt == 0 */ - - if (p) { - if (ct == 0) { - SSLerror(s, SSL_R_EMPTY_SRTP_PROTECTION_PROFILE_LIST); - return 1; - } - - if ((2 + ct * 2 + 1) > maxlen) { - SSLerror(s, SSL_R_SRTP_PROTECTION_PROFILE_LIST_TOO_LONG); - return 1; - } - - /* Add the length */ - s2n(ct * 2, p); - for (i = 0; i < ct; i++) { - prof = sk_SRTP_PROTECTION_PROFILE_value(clnt, i); - s2n(prof->id, p); - } - - /* Add an empty use_mki value */ - *p++ = 0; - } - - *len = 2 + ct*2 + 1; - - return 0; -} - - -int -ssl_parse_clienthello_use_srtp_ext(SSL *s, const unsigned char *d, int len, - int *al) -{ - SRTP_PROTECTION_PROFILE *cprof, *sprof; - STACK_OF(SRTP_PROTECTION_PROFILE) *clnt = 0, *srvr; - int i, j; - int ret = 1; - uint16_t id; - CBS cbs, ciphers, mki; - - if (len < 0) { - SSLerror(s, SSL_R_BAD_SRTP_PROTECTION_PROFILE_LIST); - *al = SSL_AD_DECODE_ERROR; - goto done; - } - - CBS_init(&cbs, d, len); - /* Pull off the cipher suite list */ - if (!CBS_get_u16_length_prefixed(&cbs, &ciphers) || - CBS_len(&ciphers) % 2) { - SSLerror(s, SSL_R_BAD_SRTP_PROTECTION_PROFILE_LIST); - *al = SSL_AD_DECODE_ERROR; - goto done; - } - - clnt = sk_SRTP_PROTECTION_PROFILE_new_null(); - - while (CBS_len(&ciphers) > 0) { - if (!CBS_get_u16(&ciphers, &id)) { - SSLerror(s, SSL_R_BAD_SRTP_PROTECTION_PROFILE_LIST); - *al = SSL_AD_DECODE_ERROR; - goto done; - } - - if (!find_profile_by_num(id, &cprof)) - sk_SRTP_PROTECTION_PROFILE_push(clnt, cprof); - else - ; /* Ignore */ - } - - /* Extract the MKI value as a sanity check, but discard it for now. */ - if (!CBS_get_u8_length_prefixed(&cbs, &mki) || - CBS_len(&cbs) != 0) { - SSLerror(s, SSL_R_BAD_SRTP_MKI_VALUE); - *al = SSL_AD_DECODE_ERROR; - goto done; - } - - srvr = SSL_get_srtp_profiles(s); - - /* - * Pick our most preferred profile. If no profiles have been - * configured then the outer loop doesn't run - * (sk_SRTP_PROTECTION_PROFILE_num() = -1) - * and so we just return without doing anything. - */ - for (i = 0; i < sk_SRTP_PROTECTION_PROFILE_num(srvr); i++) { - sprof = sk_SRTP_PROTECTION_PROFILE_value(srvr, i); - - for (j = 0; j < sk_SRTP_PROTECTION_PROFILE_num(clnt); j++) { - cprof = sk_SRTP_PROTECTION_PROFILE_value(clnt, j); - - if (cprof->id == sprof->id) { - s->internal->srtp_profile = sprof; - *al = 0; - ret = 0; - goto done; - } - } - } - - ret = 0; - -done: - sk_SRTP_PROTECTION_PROFILE_free(clnt); - - return ret; -} - -int -ssl_add_serverhello_use_srtp_ext(SSL *s, unsigned char *p, int *len, int maxlen) -{ - if (p) { - if (maxlen < 5) { - SSLerror(s, SSL_R_SRTP_PROTECTION_PROFILE_LIST_TOO_LONG); - return 1; - } - - if (s->internal->srtp_profile == 0) { - SSLerror(s, SSL_R_USE_SRTP_NOT_NEGOTIATED); - return 1; - } - s2n(2, p); - s2n(s->internal->srtp_profile->id, p); - *p++ = 0; - } - *len = 5; - - return 0; -} - - -int -ssl_parse_serverhello_use_srtp_ext(SSL *s, const unsigned char *d, int len, int *al) -{ - STACK_OF(SRTP_PROTECTION_PROFILE) *clnt; - SRTP_PROTECTION_PROFILE *prof; - int i; - uint16_t id; - CBS cbs, profile_ids, mki; - - if (len < 0) { - SSLerror(s, SSL_R_BAD_SRTP_PROTECTION_PROFILE_LIST); - *al = SSL_AD_DECODE_ERROR; - return 1; - } - - CBS_init(&cbs, d, len); - - /* - * As per RFC 5764 section 4.1.1, server response MUST be a single - * profile id. - */ - if (!CBS_get_u16_length_prefixed(&cbs, &profile_ids) || - !CBS_get_u16(&profile_ids, &id) || CBS_len(&profile_ids) != 0) { - SSLerror(s, SSL_R_BAD_SRTP_PROTECTION_PROFILE_LIST); - *al = SSL_AD_DECODE_ERROR; - return 1; - } - - /* Must be no MKI, since we never offer one. */ - if (!CBS_get_u8_length_prefixed(&cbs, &mki) || CBS_len(&mki) != 0) { - SSLerror(s, SSL_R_BAD_SRTP_MKI_VALUE); - *al = SSL_AD_ILLEGAL_PARAMETER; - return 1; - } - - clnt = SSL_get_srtp_profiles(s); - - /* Throw an error if the server gave us an unsolicited extension. */ - if (clnt == NULL) { - SSLerror(s, SSL_R_NO_SRTP_PROFILES); - *al = SSL_AD_DECODE_ERROR; - return 1; - } - - /* - * Check to see if the server gave us something we support - * (and presumably offered). - */ - for (i = 0; i < sk_SRTP_PROTECTION_PROFILE_num(clnt); i++) { - prof = sk_SRTP_PROTECTION_PROFILE_value(clnt, i); - - if (prof->id == id) { - s->internal->srtp_profile = prof; - *al = 0; - return 0; - } - } - - SSLerror(s, SSL_R_BAD_SRTP_PROTECTION_PROFILE_LIST); - *al = SSL_AD_DECODE_ERROR; - return 1; -} - #endif diff --git a/src/lib/libssl/ssl_locl.h b/src/lib/libssl/ssl_locl.h index 624df9c929..e789a4ae27 100644 --- a/src/lib/libssl/ssl_locl.h +++ b/src/lib/libssl/ssl_locl.h @@ -1,4 +1,4 @@ -/* $OpenBSD: ssl_locl.h,v 1.191 2017/08/26 20:23:46 doug Exp $ */ +/* $OpenBSD: ssl_locl.h,v 1.192 2017/08/27 02:58:04 doug Exp $ */ /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) * All rights reserved. * @@ -1348,6 +1348,15 @@ void tls1_get_formatlist(SSL *s, int client_formats, const uint8_t **pformats, void tls1_get_curvelist(SSL *s, int client_curves, const uint16_t **pcurves, size_t *pcurveslen); +#ifndef OPENSSL_NO_SRTP + +int srtp_find_profile_by_name(char *profile_name, + SRTP_PROTECTION_PROFILE **pptr, unsigned len); +int srtp_find_profile_by_num(unsigned profile_num, + SRTP_PROTECTION_PROFILE **pptr); + +#endif /* OPENSSL_NO_SRTP */ + __END_HIDDEN_DECLS #endif diff --git a/src/lib/libssl/ssl_tlsext.c b/src/lib/libssl/ssl_tlsext.c index 405882c0e9..2438b90d04 100644 --- a/src/lib/libssl/ssl_tlsext.c +++ b/src/lib/libssl/ssl_tlsext.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssl_tlsext.c,v 1.11 2017/08/26 20:23:46 doug Exp $ */ +/* $OpenBSD: ssl_tlsext.c,v 1.12 2017/08/27 02:58:04 doug Exp $ */ /* * Copyright (c) 2016, 2017 Joel Sing * Copyright (c) 2017 Doug Hogan @@ -704,6 +704,7 @@ tlsext_sni_serverhello_parse(SSL *s, CBS *cbs, int *alert) return 1; } + /* *Certificate Status Request - RFC 6066 section 8. */ @@ -983,6 +984,216 @@ tlsext_sessionticket_serverhello_parse(SSL *s, CBS *cbs, int *alert) return 1; } +/* + * DTLS extension for SRTP key establishment - RFC 5764 + */ + +#ifndef OPENSSL_NO_SRTP + +int +tlsext_srtp_clienthello_needs(SSL *s) +{ + return SSL_IS_DTLS(s) && SSL_get_srtp_profiles(s) != NULL; +} + +int +tlsext_srtp_clienthello_build(SSL *s, CBB *cbb) +{ + CBB profiles, mki; + int ct, i; + STACK_OF(SRTP_PROTECTION_PROFILE) *clnt = NULL; + SRTP_PROTECTION_PROFILE *prof; + + if ((clnt = SSL_get_srtp_profiles(s)) == NULL) { + SSLerror(s, SSL_R_EMPTY_SRTP_PROTECTION_PROFILE_LIST); + return 0; + } + + if ((ct = sk_SRTP_PROTECTION_PROFILE_num(clnt)) < 1) { + SSLerror(s, SSL_R_EMPTY_SRTP_PROTECTION_PROFILE_LIST); + return 0; + } + + if (!CBB_add_u16_length_prefixed(cbb, &profiles)) + return 0; + + for (i = 0; i < ct; i++) { + if ((prof = sk_SRTP_PROTECTION_PROFILE_value(clnt, i)) == NULL) + return 0; + if (!CBB_add_u16(&profiles, prof->id)) + return 0; + } + + if (!CBB_add_u8_length_prefixed(cbb, &mki)) + return 0; + + if (!CBB_flush(cbb)) + return 0; + + return 1; +} + +int +tlsext_srtp_clienthello_parse(SSL *s, CBS *cbs, int *alert) +{ + SRTP_PROTECTION_PROFILE *cprof, *sprof; + STACK_OF(SRTP_PROTECTION_PROFILE) *clnt = NULL, *srvr; + int i, j; + int ret; + uint16_t id; + CBS profiles, mki; + + ret = 0; + + if (!CBS_get_u16_length_prefixed(cbs, &profiles)) + goto err; + if (CBS_len(&profiles) == 0 || CBS_len(&profiles) % 2 != 0) + goto err; + + if ((clnt = sk_SRTP_PROTECTION_PROFILE_new_null()) == NULL) + goto err; + + while (CBS_len(&profiles) > 0) { + if (!CBS_get_u16(&profiles, &id)) + goto err; + + if (!srtp_find_profile_by_num(id, &cprof)) { + if (!sk_SRTP_PROTECTION_PROFILE_push(clnt, cprof)) + goto err; + } + } + + if (!CBS_get_u8_length_prefixed(cbs, &mki) || CBS_len(&mki) != 0) { + SSLerror(s, SSL_R_BAD_SRTP_MKI_VALUE); + *alert = SSL_AD_DECODE_ERROR; + goto done; + } + if (CBS_len(cbs) != 0) + goto err; + + /* + * Per RFC 5764 section 4.1.1 + * + * Find the server preferred profile using the client's list. + * + * The server MUST send a profile if it sends the use_srtp + * extension. If one is not found, it should fall back to the + * negotiated DTLS cipher suite or return a DTLS alert. + */ + if ((srvr = SSL_get_srtp_profiles(s)) == NULL) + goto err; + for (i = 0; i < sk_SRTP_PROTECTION_PROFILE_num(srvr); i++) { + if ((sprof = sk_SRTP_PROTECTION_PROFILE_value(srvr, i)) + == NULL) + goto err; + + for (j = 0; j < sk_SRTP_PROTECTION_PROFILE_num(clnt); j++) { + if ((cprof = sk_SRTP_PROTECTION_PROFILE_value(clnt, j)) + == NULL) + goto err; + + if (cprof->id == sprof->id) { + s->internal->srtp_profile = sprof; + ret = 1; + goto done; + } + } + } + + /* If we didn't find anything, fall back to the negotiated */ + ret = 1; + goto done; + + err: + SSLerror(s, SSL_R_BAD_SRTP_PROTECTION_PROFILE_LIST); + *alert = SSL_AD_DECODE_ERROR; + + done: + sk_SRTP_PROTECTION_PROFILE_free(clnt); + return ret; +} + +int +tlsext_srtp_serverhello_needs(SSL *s) +{ + return SSL_IS_DTLS(s) && SSL_get_selected_srtp_profile(s) != NULL; +} + +int +tlsext_srtp_serverhello_build(SSL *s, CBB *cbb) +{ + SRTP_PROTECTION_PROFILE *profile; + CBB srtp, mki; + + if (!CBB_add_u16_length_prefixed(cbb, &srtp)) + return 0; + + if ((profile = SSL_get_selected_srtp_profile(s)) == NULL) + return 0; + + if (!CBB_add_u16(&srtp, profile->id)) + return 0; + + if (!CBB_add_u8_length_prefixed(cbb, &mki)) + return 0; + + if (!CBB_flush(cbb)) + return 0; + + return 1; +} + +int +tlsext_srtp_serverhello_parse(SSL *s, CBS *cbs, int *alert) +{ + STACK_OF(SRTP_PROTECTION_PROFILE) *clnt; + SRTP_PROTECTION_PROFILE *prof; + int i; + uint16_t id; + CBS profile_ids, mki; + + if (!CBS_get_u16_length_prefixed(cbs, &profile_ids)) { + SSLerror(s, SSL_R_BAD_SRTP_PROTECTION_PROFILE_LIST); + goto err; + } + + if (!CBS_get_u16(&profile_ids, &id) || CBS_len(&profile_ids) != 0) { + SSLerror(s, SSL_R_BAD_SRTP_PROTECTION_PROFILE_LIST); + goto err; + } + + if (!CBS_get_u8_length_prefixed(cbs, &mki) || CBS_len(&mki) != 0) { + SSLerror(s, SSL_R_BAD_SRTP_MKI_VALUE); + *alert = SSL_AD_ILLEGAL_PARAMETER; + return 0; + } + + if ((clnt = SSL_get_srtp_profiles(s)) == NULL) { + SSLerror(s, SSL_R_NO_SRTP_PROFILES); + goto err; + } + + for (i = 0; i < sk_SRTP_PROTECTION_PROFILE_num(clnt); i++) { + if ((prof = sk_SRTP_PROTECTION_PROFILE_value(clnt, i)) + == NULL) { + SSLerror(s, SSL_R_NO_SRTP_PROFILES); + goto err; + } + + if (prof->id == id) { + s->internal->srtp_profile = prof; + return 1; + } + } + + SSLerror(s, SSL_R_BAD_SRTP_PROTECTION_PROFILE_LIST); + err: + *alert = SSL_AD_DECODE_ERROR; + return 0; +} + +#endif /* OPENSSL_NO_SRTP */ + struct tls_extension { uint16_t type; int (*clienthello_needs)(SSL *s); @@ -1066,6 +1277,17 @@ static struct tls_extension tls_extensions[] = { .serverhello_build = tlsext_alpn_serverhello_build, .serverhello_parse = tlsext_alpn_serverhello_parse, }, +#ifndef OPENSSL_NO_SRTP + { + .type = TLSEXT_TYPE_use_srtp, + .clienthello_needs = tlsext_srtp_clienthello_needs, + .clienthello_build = tlsext_srtp_clienthello_build, + .clienthello_parse = tlsext_srtp_clienthello_parse, + .serverhello_needs = tlsext_srtp_serverhello_needs, + .serverhello_build = tlsext_srtp_serverhello_build, + .serverhello_parse = tlsext_srtp_serverhello_parse, + } +#endif /* OPENSSL_NO_SRTP */ }; #define N_TLS_EXTENSIONS (sizeof(tls_extensions) / sizeof(*tls_extensions)) diff --git a/src/lib/libssl/ssl_tlsext.h b/src/lib/libssl/ssl_tlsext.h index 21f9bb1bf9..7c6250a7f7 100644 --- a/src/lib/libssl/ssl_tlsext.h +++ b/src/lib/libssl/ssl_tlsext.h @@ -1,4 +1,4 @@ -/* $OpenBSD: ssl_tlsext.h,v 1.9 2017/08/26 20:23:46 doug Exp $ */ +/* $OpenBSD: ssl_tlsext.h,v 1.10 2017/08/27 02:58:04 doug Exp $ */ /* * Copyright (c) 2016, 2017 Joel Sing * Copyright (c) 2017 Doug Hogan @@ -72,6 +72,15 @@ int tlsext_sessionticket_serverhello_needs(SSL *s); int tlsext_sessionticket_serverhello_build(SSL *s, CBB *cbb); int tlsext_sessionticket_serverhello_parse(SSL *s, CBS *cbs, int *alert); +#ifndef OPENSSL_NO_SRTP +int tlsext_srtp_clienthello_needs(SSL *s); +int tlsext_srtp_clienthello_build(SSL *s, CBB *cbb); +int tlsext_srtp_clienthello_parse(SSL *s, CBS *cbs, int *alert); +int tlsext_srtp_serverhello_needs(SSL *s); +int tlsext_srtp_serverhello_build(SSL *s, CBB *cbb); +int tlsext_srtp_serverhello_parse(SSL *s, CBS *cbs, int *alert); +#endif + int tlsext_clienthello_build(SSL *s, CBB *cbb); int tlsext_clienthello_parse_one(SSL *s, CBS *cbs, uint16_t tlsext_type, int *alert); diff --git a/src/lib/libssl/t1_lib.c b/src/lib/libssl/t1_lib.c index eb1d96cc11..a9f10166fe 100644 --- a/src/lib/libssl/t1_lib.c +++ b/src/lib/libssl/t1_lib.c @@ -1,4 +1,4 @@ -/* $OpenBSD: t1_lib.c,v 1.135 2017/08/26 20:23:46 doug Exp $ */ +/* $OpenBSD: t1_lib.c,v 1.136 2017/08/27 02:58:04 doug Exp $ */ /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) * All rights reserved. * @@ -687,26 +687,6 @@ ssl_add_clienthello_tlsext(SSL *s, unsigned char *p, unsigned char *limit) return NULL; ret += len; -#ifndef OPENSSL_NO_SRTP - if (SSL_IS_DTLS(s) && SSL_get_srtp_profiles(s)) { - int el; - - ssl_add_clienthello_use_srtp_ext(s, 0, &el, 0); - - if ((size_t)(limit - ret) < 4 + el) - return NULL; - - s2n(TLSEXT_TYPE_use_srtp, ret); - s2n(el, ret); - - if (ssl_add_clienthello_use_srtp_ext(s, ret, &el, el)) { - SSLerror(s, ERR_R_INTERNAL_ERROR); - return NULL; - } - ret += el; - } -#endif - if ((extdatalen = ret - p - 2) == 0) return p; @@ -745,26 +725,6 @@ ssl_add_serverhello_tlsext(SSL *s, unsigned char *p, unsigned char *limit) * extension. */ -#ifndef OPENSSL_NO_SRTP - if (SSL_IS_DTLS(s) && s->internal->srtp_profile) { - int el; - - ssl_add_serverhello_use_srtp_ext(s, 0, &el, 0); - - if ((size_t)(limit - ret) < 4 + el) - return NULL; - - s2n(TLSEXT_TYPE_use_srtp, ret); - s2n(el, ret); - - if (ssl_add_serverhello_use_srtp_ext(s, ret, &el, el)) { - SSLerror(s, ERR_R_INTERNAL_ERROR); - return NULL; - } - ret += el; - } -#endif - if ((extdatalen = ret - p - 2) == 0) return p; @@ -815,14 +775,6 @@ ssl_parse_clienthello_tlsext(SSL *s, unsigned char **p, unsigned char *d, if (!tlsext_clienthello_parse_one(s, &cbs, type, al)) return 0; - /* session ticket processed earlier */ -#ifndef OPENSSL_NO_SRTP - else if (SSL_IS_DTLS(s) && type == TLSEXT_TYPE_use_srtp) { - if (ssl_parse_clienthello_use_srtp_ext(s, data, size, al)) - return 0; - } -#endif - data += size; } @@ -888,14 +840,6 @@ ssl_parse_serverhello_tlsext(SSL *s, unsigned char **p, size_t n, int *al) if (!tlsext_serverhello_parse_one(s, &cbs, type, al)) return 0; -#ifndef OPENSSL_NO_SRTP - else if (SSL_IS_DTLS(s) && type == TLSEXT_TYPE_use_srtp) { - if (ssl_parse_serverhello_use_srtp_ext(s, data, - size, al)) - return 0; - } -#endif - data += size; } diff --git a/src/regress/lib/libssl/tlsext/tlsexttest.c b/src/regress/lib/libssl/tlsext/tlsexttest.c index d3858e4c50..1b2820d78b 100644 --- a/src/regress/lib/libssl/tlsext/tlsexttest.c +++ b/src/regress/lib/libssl/tlsext/tlsexttest.c @@ -1,4 +1,4 @@ -/* $OpenBSD: tlsexttest.c,v 1.14 2017/08/27 02:17:51 beck Exp $ */ +/* $OpenBSD: tlsexttest.c,v 1.15 2017/08/27 02:58:04 doug Exp $ */ /* * Copyright (c) 2017 Joel Sing * Copyright (c) 2017 Doug Hogan @@ -2257,6 +2257,400 @@ test_tlsext_sessionticket_serverhello(void) return (failure); } +#ifndef OPENSSL_NO_SRTP +/* + * Supported Secure Real-time Transport Protocol (RFC 5764 section 4.1.1) + */ + +/* Colon separated string values */ +const char *tlsext_srtp_single_profile = "SRTP_AES128_CM_SHA1_80"; +const char *tlsext_srtp_multiple_profiles = "SRTP_AES128_CM_SHA1_80:SRTP_AES128_CM_SHA1_32"; + +const char *tlsext_srtp_aes128cmsha80 = "SRTP_AES128_CM_SHA1_80"; +const char *tlsext_srtp_aes128cmsha32 = "SRTP_AES128_CM_SHA1_32"; + +const uint8_t tlsext_srtp_single[] = { + /* SRTPProtectionProfile SRTPProtectionProfiles<2..2^16-1> */ + 0x00, 0x02, /* len */ + 0x00, 0x01, /* SRTP_AES128_CM_SHA1_80 */ + 0x00 /* opaque srtp_mki<0..255> */ +}; + +const uint8_t tlsext_srtp_multiple[] = { + /* SRTPProtectionProfile SRTPProtectionProfiles<2..2^16-1> */ + 0x00, 0x04, /* len */ + 0x00, 0x01, /* SRTP_AES128_CM_SHA1_80 */ + 0x00, 0x02, /* SRTP_AES128_CM_SHA1_32 */ + 0x00 /* opaque srtp_mki<0..255> */ +}; + +const uint8_t tlsext_srtp_multiple_invalid[] = { + /* SRTPProtectionProfile SRTPProtectionProfiles<2..2^16-1> */ + 0x00, 0x04, /* len */ + 0x00, 0x08, /* arbitrary value not found in known profiles */ + 0x00, 0x09, /* arbitrary value not found in known profiles */ + 0x00 /* opaque srtp_mki<0..255> */ +}; + +const uint8_t tlsext_srtp_single_invalid[] = { + /* SRTPProtectionProfile SRTPProtectionProfiles<2..2^16-1> */ + 0x00, 0x02, /* len */ + 0x00, 0x08, /* arbitrary value not found in known profiles */ + 0x00 /* opaque srtp_mki<0..255> */ +}; + +const uint8_t tlsext_srtp_multiple_one_valid[] = { + /* SRTPProtectionProfile SRTPProtectionProfiles<2..2^16-1> */ + 0x00, 0x04, /* len */ + 0x00, 0x08, /* arbitrary value not found in known profiles */ + 0x00, 0x02, /* SRTP_AES128_CM_SHA1_32 */ + 0x00 /* opaque srtp_mki<0..255> */ +}; + +static int +test_tlsext_srtp_clienthello(void) +{ + SRTP_PROTECTION_PROFILE *prof; + SSL_CTX *ssl_ctx = NULL; + SSL *ssl = NULL; + uint8_t *data = NULL; + CBB cbb; + CBS cbs; + int failure, alert; + size_t dlen; + + CBB_init(&cbb, 0); + + failure = 1; + + /* SRTP is for DTLS */ + if ((ssl_ctx = SSL_CTX_new(DTLSv1_client_method())) == NULL) + errx(1, "failed to create SSL_CTX"); + if ((ssl = SSL_new(ssl_ctx)) == NULL) + errx(1, "failed to create SSL"); + + /* By default, we don't need this */ + if (tlsext_srtp_clienthello_needs(ssl)) { + FAIL("clienthello should not need SRTP by default\n"); + goto err; + } + + if (SSL_set_tlsext_use_srtp(ssl, tlsext_srtp_single_profile) != 0) { + FAIL("should be able to set a single SRTP\n"); + goto err; + } + if (!tlsext_srtp_clienthello_needs(ssl)) { + FAIL("clienthello should need SRTP\n"); + goto err; + } + + /* Make sure we can build the clienthello with a single profile. */ + + if (!tlsext_srtp_clienthello_build(ssl, &cbb)) { + FAIL("clienthello failed to build SRTP\n"); + goto err; + } + if (!CBB_finish(&cbb, &data, &dlen)) + errx(1, "failed to finish CBB"); + + if (dlen != sizeof(tlsext_srtp_single)) { + FAIL("got clienthello SRTP with length %zu, " + "want length %zu\n", dlen, + sizeof(tlsext_srtp_single)); + compare_data(data, dlen, tlsext_srtp_single, + sizeof(tlsext_srtp_single)); + goto err; + } + if (memcmp(data, tlsext_srtp_single, dlen) != 0) { + FAIL("clienthello SRTP differs:\n"); + compare_data(data, dlen, tlsext_srtp_single, + sizeof(tlsext_srtp_single)); + goto err; + } + + CBB_cleanup(&cbb); + CBB_init(&cbb, 0); + free(data); + data = NULL; + + /* Make sure we can parse the single profile. */ + + if (SSL_get_selected_srtp_profile(ssl) != NULL) { + FAIL("SRTP profile should not be set yet\n"); + goto err; + } + + CBS_init(&cbs, tlsext_srtp_single, sizeof(tlsext_srtp_single)); + if (!tlsext_srtp_clienthello_parse(ssl, &cbs, &alert)) { + FAIL("failed to parse SRTP\n"); + goto err; + } + + if ((prof = SSL_get_selected_srtp_profile(ssl)) == NULL) { + FAIL("SRTP profile should be set now\n"); + goto err; + } + if (strcmp(prof->name, tlsext_srtp_aes128cmsha80) != 0) { + FAIL("SRTP profile was not set properly\n"); + goto err; + } + + if (!tlsext_srtp_serverhello_needs(ssl)) { + FAIL("should send server extension when profile selected\n"); + goto err; + } + + /* Make sure we can build the clienthello with multiple entries. */ + + if (SSL_set_tlsext_use_srtp(ssl, tlsext_srtp_multiple_profiles) != 0) { + FAIL("should be able to set SRTP to multiple profiles\n"); + goto err; + } + if (!tlsext_srtp_clienthello_needs(ssl)) { + FAIL("clienthello should need SRTP by now\n"); + goto err; + } + + if (!tlsext_srtp_clienthello_build(ssl, &cbb)) { + FAIL("clienthello failed to build SRTP\n"); + goto err; + } + if (!CBB_finish(&cbb, &data, &dlen)) + errx(1, "failed to finish CBB"); + + if (dlen != sizeof(tlsext_srtp_multiple)) { + FAIL("got clienthello SRTP with length %zu, " + "want length %zu\n", dlen, + sizeof(tlsext_srtp_multiple)); + compare_data(data, dlen, tlsext_srtp_multiple, + sizeof(tlsext_srtp_multiple)); + goto err; + } + if (memcmp(data, tlsext_srtp_multiple, dlen) != 0) { + FAIL("clienthello SRTP differs:\n"); + compare_data(data, dlen, tlsext_srtp_multiple, + sizeof(tlsext_srtp_multiple)); + goto err; + } + + CBB_cleanup(&cbb); + CBB_init(&cbb, 0); + free(data); + data = NULL; + + /* Make sure we can parse multiple profiles (selects server preferred) */ + + ssl->internal->srtp_profile = NULL; + + CBS_init(&cbs, tlsext_srtp_multiple, + sizeof(tlsext_srtp_multiple)); + if (!tlsext_srtp_clienthello_parse(ssl, &cbs, &alert)) { + FAIL("failed to parse SRTP\n"); + goto err; + } + + if ((prof = SSL_get_selected_srtp_profile(ssl)) == NULL) { + FAIL("SRTP profile should be set now\n"); + goto err; + } + if (strcmp(prof->name, tlsext_srtp_aes128cmsha80) != 0) { + FAIL("SRTP profile was not set properly\n"); + goto err; + } + + if (!tlsext_srtp_serverhello_needs(ssl)) { + FAIL("should send server extension when profile selected\n"); + goto err; + } + + /* + * Make sure we can parse the clienthello with multiple entries + * where one is unknown. + */ + ssl->internal->srtp_profile = NULL; + + CBS_init(&cbs, tlsext_srtp_multiple_one_valid, + sizeof(tlsext_srtp_multiple_one_valid)); + if (!tlsext_srtp_clienthello_parse(ssl, &cbs, &alert)) { + FAIL("failed to parse SRTP\n"); + goto err; + } + + if ((prof = SSL_get_selected_srtp_profile(ssl)) == NULL) { + FAIL("SRTP profile should be set now\n"); + goto err; + } + if (strcmp(prof->name, tlsext_srtp_aes128cmsha32) != 0) { + FAIL("SRTP profile was not set properly\n"); + goto err; + } + + if (!tlsext_srtp_serverhello_needs(ssl)) { + FAIL("should send server extension when profile selected\n"); + goto err; + } + + /* Make sure we fall back to negotiated when none work. */ + + ssl->internal->srtp_profile = NULL; + + CBS_init(&cbs, tlsext_srtp_multiple_invalid, + sizeof(tlsext_srtp_multiple_invalid)); + if (!tlsext_srtp_clienthello_parse(ssl, &cbs, &alert)) { + FAIL("should be able to fall back to negotiated\n"); + goto err; + } + + /* If we fallback, the server should NOT send the extension. */ + if (SSL_get_selected_srtp_profile(ssl) != NULL) { + FAIL("should not have selected a profile when none found\n"); + goto err; + } + if (tlsext_srtp_serverhello_needs(ssl)) { + FAIL("should not send server tlsext when no profile found\n"); + goto err; + } + + failure = 0; + + err: + CBB_cleanup(&cbb); + SSL_CTX_free(ssl_ctx); + SSL_free(ssl); + free(data); + + return (failure); +} + +static int +test_tlsext_srtp_serverhello(void) +{ + SRTP_PROTECTION_PROFILE *prof; + SSL_CTX *ssl_ctx = NULL; + SSL *ssl = NULL; + uint8_t *data = NULL; + CBB cbb; + CBS cbs; + int failure, alert; + size_t dlen; + + CBB_init(&cbb, 0); + + failure = 1; + + /* SRTP is for DTLS */ + if ((ssl_ctx = SSL_CTX_new(DTLSv1_client_method())) == NULL) + errx(1, "failed to create SSL_CTX"); + if ((ssl = SSL_new(ssl_ctx)) == NULL) + errx(1, "failed to create SSL"); + + /* By default, we don't need this */ + if (tlsext_srtp_serverhello_needs(ssl)) { + FAIL("serverhello should not need SRTP by default\n"); + goto err; + } + + if (srtp_find_profile_by_name((char *)tlsext_srtp_aes128cmsha80, &prof, + strlen(tlsext_srtp_aes128cmsha80))) { + FAIL("should be able to find the given profile\n"); + goto err; + } + ssl->internal->srtp_profile = prof; + if (!tlsext_srtp_serverhello_needs(ssl)) { + FAIL("serverhello should need SRTP by now\n"); + goto err; + } + + /* Make sure we can build the serverhello with a single profile. */ + + if (!tlsext_srtp_serverhello_build(ssl, &cbb)) { + FAIL("serverhello failed to build SRTP\n"); + goto err; + } + if (!CBB_finish(&cbb, &data, &dlen)) + errx(1, "failed to finish CBB"); + + if (dlen != sizeof(tlsext_srtp_single)) { + FAIL("got serverhello SRTP with length %zu, " + "want length %zu\n", dlen, + sizeof(tlsext_srtp_single)); + compare_data(data, dlen, tlsext_srtp_single, + sizeof(tlsext_srtp_single)); + goto err; + } + if (memcmp(data, tlsext_srtp_single, dlen) != 0) { + FAIL("serverhello SRTP differs:\n"); + compare_data(data, dlen, tlsext_srtp_single, + sizeof(tlsext_srtp_single)); + goto err; + } + + CBB_cleanup(&cbb); + CBB_init(&cbb, 0); + free(data); + data = NULL; + + /* Make sure we can parse the single profile. */ + ssl->internal->srtp_profile = NULL; + + if (SSL_get_selected_srtp_profile(ssl) != NULL) { + FAIL("SRTP profile should not be set yet\n"); + goto err; + } + + /* Setup the environment as if a client sent a list of profiles. */ + if (SSL_set_tlsext_use_srtp(ssl, tlsext_srtp_multiple_profiles) != 0) { + FAIL("should be able to set multiple profiles in SRTP\n"); + goto err; + } + + CBS_init(&cbs, tlsext_srtp_single, sizeof(tlsext_srtp_single)); + if (!tlsext_srtp_serverhello_parse(ssl, &cbs, &alert)) { + FAIL("failed to parse SRTP\n"); + goto err; + } + + if ((prof = SSL_get_selected_srtp_profile(ssl)) == NULL) { + FAIL("SRTP profile should be set now\n"); + goto err; + } + if (strcmp(prof->name, tlsext_srtp_aes128cmsha80) != 0) { + FAIL("SRTP profile was not set properly\n"); + goto err; + } + + /* Make sure we cannot parse multiple profiles */ + ssl->internal->srtp_profile = NULL; + + CBS_init(&cbs, tlsext_srtp_multiple, + sizeof(tlsext_srtp_multiple)); + if (tlsext_srtp_serverhello_parse(ssl, &cbs, &alert)) { + FAIL("should not find multiple entries from the server\n"); + goto err; + } + + /* Make sure we cannot parse a serverhello with unknown profile */ + ssl->internal->srtp_profile = NULL; + + CBS_init(&cbs, tlsext_srtp_single_invalid, + sizeof(tlsext_srtp_single_invalid)); + if (tlsext_srtp_serverhello_parse(ssl, &cbs, &alert)) { + FAIL("should not be able to parse this\n"); + goto err; + } + + failure = 0; + + err: + CBB_cleanup(&cbb); + SSL_CTX_free(ssl_ctx); + SSL_free(ssl); + free(data); + + return (failure); +} +#endif /* OPENSSL_NO_SRTP */ + int main(int argc, char **argv) { @@ -2288,5 +2682,12 @@ main(int argc, char **argv) failed |= test_tlsext_sessionticket_clienthello(); failed |= test_tlsext_sessionticket_serverhello(); +#ifndef OPENSSL_NO_SRTP + failed |= test_tlsext_srtp_clienthello(); + failed |= test_tlsext_srtp_serverhello(); +#else + fprintf(stderr, "Skipping SRTP tests due to OPENSSL_NO_SRTP\n"); +#endif + return (failed); } -- cgit v1.2.3-55-g6feb