/* * srtp.c * * the secure real-time transport protocol * * David A. McGrew * Cisco Systems, Inc. */ /* * * Copyright (c) 2001-2006, Cisco Systems, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * Neither the name of the Cisco Systems, Inc. nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * */ #include "srtp_priv.h" #include "ekt.h" /* for SRTP Encrypted Key Transport */ #include "alloc.h" /* for crypto_alloc() */ #ifdef OPENSSL #include "aes_gcm_ossl.h" /* for AES GCM mode */ #endif #ifndef SRTP_KERNEL # include # ifdef HAVE_NETINET_IN_H # include # elif defined(HAVE_WINSOCK2_H) # include # endif #endif /* ! SRTP_KERNEL */ /* the debug module for srtp */ debug_module_t mod_srtp = { 0, /* debugging is off by default */ "srtp" /* printable name for module */ }; #define octets_in_rtp_header 12 #define uint32s_in_rtp_header 3 #define octets_in_rtcp_header 8 #define uint32s_in_rtcp_header 2 #define octets_in_rtp_extn_hdr 4 static err_status_t srtp_validate_rtp_header(void *rtp_hdr, int *pkt_octet_len) { srtp_hdr_t *hdr = (srtp_hdr_t *)rtp_hdr; /* Check RTP header length */ int rtp_header_len = octets_in_rtp_header + 4 * hdr->cc; if (hdr->x == 1) rtp_header_len += octets_in_rtp_extn_hdr; if (*pkt_octet_len < rtp_header_len) return err_status_bad_param; /* Verifing profile length. */ if (hdr->x == 1) { srtp_hdr_xtnd_t *xtn_hdr = (srtp_hdr_xtnd_t *)((uint32_t *)hdr + uint32s_in_rtp_header + hdr->cc); int profile_len = ntohs(xtn_hdr->length); rtp_header_len += profile_len * 4; /* profile length counts the number of 32-bit words */ if (*pkt_octet_len < rtp_header_len) return err_status_bad_param; } return err_status_ok; } const char *srtp_get_version_string () { /* * Simply return the autotools generated string */ return SRTP_VER_STRING; } unsigned int srtp_get_version () { unsigned int major = 0, minor = 0, micro = 0; unsigned int rv = 0; int parse_rv; /* * Parse the autotools generated version */ parse_rv = sscanf(SRTP_VERSION, "%u.%u.%u", &major, &minor, µ); if (parse_rv != 3) { /* * We're expected to parse all 3 version levels. * If not, then this must not be an official release. * Return all zeros on the version */ return (0); } /* * We allow 8 bits for the major and minor, while * allowing 16 bits for the micro. 16 bits for the micro * may be beneficial for a continuous delivery model * in the future. */ rv |= (major & 0xFF) << 24; rv |= (minor & 0xFF) << 16; rv |= micro & 0xFF; return rv; } err_status_t srtp_stream_alloc(srtp_stream_ctx_t **str_ptr, const srtp_policy_t *p) { srtp_stream_ctx_t *str; err_status_t stat; /* * This function allocates the stream context, rtp and rtcp ciphers * and auth functions, and key limit structure. If there is a * failure during allocation, we free all previously allocated * memory and return a failure code. The code could probably * be improved, but it works and should be clear. */ /* allocate srtp stream and set str_ptr */ str = (srtp_stream_ctx_t *) crypto_alloc(sizeof(srtp_stream_ctx_t)); if (str == NULL) return err_status_alloc_fail; *str_ptr = str; /* allocate cipher */ stat = crypto_kernel_alloc_cipher(p->rtp.cipher_type, &str->rtp_cipher, p->rtp.cipher_key_len, p->rtp.auth_tag_len); if (stat) { crypto_free(str); return stat; } /* allocate auth function */ stat = crypto_kernel_alloc_auth(p->rtp.auth_type, &str->rtp_auth, p->rtp.auth_key_len, p->rtp.auth_tag_len); if (stat) { cipher_dealloc(str->rtp_cipher); crypto_free(str); return stat; } /* allocate key limit structure */ str->limit = (key_limit_ctx_t*) crypto_alloc(sizeof(key_limit_ctx_t)); if (str->limit == NULL) { auth_dealloc(str->rtp_auth); cipher_dealloc(str->rtp_cipher); crypto_free(str); return err_status_alloc_fail; } /* * ...and now the RTCP-specific initialization - first, allocate * the cipher */ stat = crypto_kernel_alloc_cipher(p->rtcp.cipher_type, &str->rtcp_cipher, p->rtcp.cipher_key_len, p->rtcp.auth_tag_len); if (stat) { auth_dealloc(str->rtp_auth); cipher_dealloc(str->rtp_cipher); crypto_free(str->limit); crypto_free(str); return stat; } /* allocate auth function */ stat = crypto_kernel_alloc_auth(p->rtcp.auth_type, &str->rtcp_auth, p->rtcp.auth_key_len, p->rtcp.auth_tag_len); if (stat) { cipher_dealloc(str->rtcp_cipher); auth_dealloc(str->rtp_auth); cipher_dealloc(str->rtp_cipher); crypto_free(str->limit); crypto_free(str); return stat; } /* allocate ekt data associated with stream */ stat = ekt_alloc(&str->ekt, p->ekt); if (stat) { auth_dealloc(str->rtcp_auth); cipher_dealloc(str->rtcp_cipher); auth_dealloc(str->rtp_auth); cipher_dealloc(str->rtp_cipher); crypto_free(str->limit); crypto_free(str); return stat; } return err_status_ok; } err_status_t srtp_stream_dealloc(srtp_t session, srtp_stream_ctx_t *stream) { err_status_t status; /* * we use a conservative deallocation strategy - if any deallocation * fails, then we report that fact without trying to deallocate * anything else */ /* deallocate cipher, if it is not the same as that in template */ if (session->stream_template && stream->rtp_cipher == session->stream_template->rtp_cipher) { /* do nothing */ } else { status = cipher_dealloc(stream->rtp_cipher); if (status) return status; } /* deallocate auth function, if it is not the same as that in template */ if (session->stream_template && stream->rtp_auth == session->stream_template->rtp_auth) { /* do nothing */ } else { status = auth_dealloc(stream->rtp_auth); if (status) return status; } /* deallocate key usage limit, if it is not the same as that in template */ if (session->stream_template && stream->limit == session->stream_template->limit) { /* do nothing */ } else { crypto_free(stream->limit); } /* * deallocate rtcp cipher, if it is not the same as that in * template */ if (session->stream_template && stream->rtcp_cipher == session->stream_template->rtcp_cipher) { /* do nothing */ } else { status = cipher_dealloc(stream->rtcp_cipher); if (status) return status; } /* * deallocate rtcp auth function, if it is not the same as that in * template */ if (session->stream_template && stream->rtcp_auth == session->stream_template->rtcp_auth) { /* do nothing */ } else { status = auth_dealloc(stream->rtcp_auth); if (status) return status; } status = rdbx_dealloc(&stream->rtp_rdbx); if (status) return status; /* DAM - need to deallocate EKT here */ /* * zeroize the salt value */ memset(stream->salt, 0, SRTP_AEAD_SALT_LEN); memset(stream->c_salt, 0, SRTP_AEAD_SALT_LEN); /* deallocate srtp stream context */ crypto_free(stream); return err_status_ok; } /* * srtp_stream_clone(stream_template, new) allocates a new stream and * initializes it using the cipher and auth of the stream_template * * the only unique data in a cloned stream is the replay database and * the SSRC */ err_status_t srtp_stream_clone(const srtp_stream_ctx_t *stream_template, uint32_t ssrc, srtp_stream_ctx_t **str_ptr) { err_status_t status; srtp_stream_ctx_t *str; debug_print(mod_srtp, "cloning stream (SSRC: 0x%08x)", ssrc); /* allocate srtp stream and set str_ptr */ str = (srtp_stream_ctx_t *) crypto_alloc(sizeof(srtp_stream_ctx_t)); if (str == NULL) return err_status_alloc_fail; *str_ptr = str; /* set cipher and auth pointers to those of the template */ str->rtp_cipher = stream_template->rtp_cipher; str->rtp_auth = stream_template->rtp_auth; str->rtcp_cipher = stream_template->rtcp_cipher; str->rtcp_auth = stream_template->rtcp_auth; /* set key limit to point to that of the template */ status = key_limit_clone(stream_template->limit, &str->limit); if (status) { crypto_free(*str_ptr); *str_ptr = NULL; return status; } /* initialize replay databases */ status = rdbx_init(&str->rtp_rdbx, rdbx_get_window_size(&stream_template->rtp_rdbx)); if (status) { crypto_free(*str_ptr); *str_ptr = NULL; return status; } rdb_init(&str->rtcp_rdb); str->allow_repeat_tx = stream_template->allow_repeat_tx; /* set ssrc to that provided */ str->ssrc = ssrc; /* set direction and security services */ str->direction = stream_template->direction; str->rtp_services = stream_template->rtp_services; str->rtcp_services = stream_template->rtcp_services; /* set pointer to EKT data associated with stream */ str->ekt = stream_template->ekt; /* Copy the salt values */ memcpy(str->salt, stream_template->salt, SRTP_AEAD_SALT_LEN); memcpy(str->c_salt, stream_template->c_salt, SRTP_AEAD_SALT_LEN); /* defensive coding */ str->next = NULL; return err_status_ok; } /* * key derivation functions, internal to libSRTP * * srtp_kdf_t is a key derivation context * * srtp_kdf_init(&kdf, cipher_id, k, keylen) initializes kdf to use cipher * described by cipher_id, with the master key k with length in octets keylen. * * srtp_kdf_generate(&kdf, l, kl, keylen) derives the key * corresponding to label l and puts it into kl; the length * of the key in octets is provided as keylen. this function * should be called once for each subkey that is derived. * * srtp_kdf_clear(&kdf) zeroizes and deallocates the kdf state */ typedef enum { label_rtp_encryption = 0x00, label_rtp_msg_auth = 0x01, label_rtp_salt = 0x02, label_rtcp_encryption = 0x03, label_rtcp_msg_auth = 0x04, label_rtcp_salt = 0x05 } srtp_prf_label; /* * srtp_kdf_t represents a key derivation function. The SRTP * default KDF is the only one implemented at present. */ typedef struct { cipher_t *cipher; /* cipher used for key derivation */ } srtp_kdf_t; err_status_t srtp_kdf_init(srtp_kdf_t *kdf, cipher_type_id_t cipher_id, const uint8_t *key, int length) { err_status_t stat; stat = crypto_kernel_alloc_cipher(cipher_id, &kdf->cipher, length, 0); if (stat) return stat; stat = cipher_init(kdf->cipher, key); if (stat) { cipher_dealloc(kdf->cipher); return stat; } return err_status_ok; } err_status_t srtp_kdf_generate(srtp_kdf_t *kdf, srtp_prf_label label, uint8_t *key, unsigned int length) { v128_t nonce; err_status_t status; /* set eigth octet of nonce to