/* * 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 "aes_icm.h" /* aes_icm is used in the KDF */ #include "alloc.h" /* for crypto_alloc() */ #ifndef SRTP_KERNEL # include # ifdef HAVE_NETINET_IN_H # include # elif defined(HAVE_WINSOCK2_H) # include # endif #endif /* ! SRTP_KERNEL */ extern cipher_type_t aes_icm; extern auth_type_t tmmhv2; /* 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 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); 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); 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; } 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; } /* 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) return status; /* initialize replay databases */ rdbx_init(&str->rtp_rdbx); rdb_init(&str->rtcp_rdb); /* 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; /* 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, k) initializes kdf with the key k * * 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 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 { aes_icm_ctx_t c; /* cipher used for key derivation */ } srtp_kdf_t; err_status_t srtp_kdf_init(srtp_kdf_t *kdf, const uint8_t key[30]) { aes_icm_context_init(&kdf->c, key); return err_status_ok; } err_status_t srtp_kdf_generate(srtp_kdf_t *kdf, srtp_prf_label label, uint8_t *key, int length) { v128_t nonce; /* set eigth octet of nonce to