Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/third_party/libsrtp/src/srtp/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 153 kB image not shown  

Quelle  srtp.c   Sprache: C

 
/*
 * srtp.c
 *
 * the secure real-time transport protocol
 *
 * David A. McGrew
 * Cisco Systems, Inc.
 */

/*
 *
 * Copyright (c) 2001-2017, 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.
 *
 */


// Leave this as the top level import. Ensures the existence of defines
#include "config.h"

#include "srtp_priv.h"
#include "crypto_types.h"
#include "err.h"
#include "alloc.h" /* for srtp_crypto_alloc() */

#ifdef GCM
#include "aes_gcm.h" /* for AES GCM mode */
#endif

#ifdef OPENSSL_KDF
#include <openssl/kdf.h>
#include "aes_icm_ext.h"
#endif

#include <limits.h>
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#elif defined(HAVE_WINSOCK2_H)
#include <winsock2.h>
#endif

/* the debug module for srtp */
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 srtp_err_status_t srtp_validate_rtp_header(void *rtp_hdr,
                                                  int *pkt_octet_len)
{
    srtp_hdr_t *hdr = (srtp_hdr_t *)rtp_hdr;
    int rtp_header_len;

    if (*pkt_octet_len < octets_in_rtp_header)
        return srtp_err_status_bad_param;

    /* Check RTP header length */
    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 srtp_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 srtp_err_status_bad_param;
    }
    return srtp_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;
}

srtp_err_status_t srtp_stream_dealloc(srtp_stream_ctx_t *stream,
                                      const srtp_stream_ctx_t *stream_template)
{
    srtp_err_status_t status;
    unsigned int i = 0;
    srtp_session_keys_t *session_keys = NULL;
    srtp_session_keys_t *template_session_keys = NULL;

    /*
     * we use a conservative deallocation strategy - if any deallocation
     * fails, then we report that fact without trying to deallocate
     * anything else
     */

    if (stream->session_keys) {
        for (i = 0; i < stream->num_master_keys; i++) {
            session_keys = &stream->session_keys[i];

            if (stream_template &&
                stream->num_master_keys == stream_template->num_master_keys) {
                template_session_keys = &stream_template->session_keys[i];
            } else {
                template_session_keys = NULL;
            }

            /*
            * deallocate cipher, if it is not the same as that in template
            */

            if (template_session_keys &&
                session_keys->rtp_cipher == template_session_keys->rtp_cipher) {
                /* do nothing */
            } else if (session_keys->rtp_cipher) {
                status = srtp_cipher_dealloc(session_keys->rtp_cipher);
                if (status)
                    return status;
            }

            /*
             * deallocate auth function, if it is not the same as that in
             * template
             */

            if (template_session_keys &&
                session_keys->rtp_auth == template_session_keys->rtp_auth) {
                /* do nothing */
            } else if (session_keys->rtp_auth) {
                status = srtp_auth_dealloc(session_keys->rtp_auth);
                if (status)
                    return status;
            }

            if (template_session_keys &&
                session_keys->rtp_xtn_hdr_cipher ==
                    template_session_keys->rtp_xtn_hdr_cipher) {
                /* do nothing */
            } else if (session_keys->rtp_xtn_hdr_cipher) {
                status = srtp_cipher_dealloc(session_keys->rtp_xtn_hdr_cipher);
                if (status)
                    return status;
            }

            /*
             * deallocate rtcp cipher, if it is not the same as that in
             * template
             */

            if (template_session_keys &&
                session_keys->rtcp_cipher ==
                    template_session_keys->rtcp_cipher) {
                /* do nothing */
            } else if (session_keys->rtcp_cipher) {
                status = srtp_cipher_dealloc(session_keys->rtcp_cipher);
                if (status)
                    return status;
            }

            /*
             * deallocate rtcp auth function, if it is not the same as that in
             * template
             */

            if (template_session_keys &&
                session_keys->rtcp_auth == template_session_keys->rtcp_auth) {
                /* do nothing */
            } else if (session_keys->rtcp_auth) {
                status = srtp_auth_dealloc(session_keys->rtcp_auth);
                if (status)
                    return status;
            }

            /*
             * zeroize the salt value
             */

            octet_string_set_to_zero(session_keys->salt, SRTP_AEAD_SALT_LEN);
            octet_string_set_to_zero(session_keys->c_salt, SRTP_AEAD_SALT_LEN);

            if (session_keys->mki_id) {
                octet_string_set_to_zero(session_keys->mki_id,
                                         session_keys->mki_size);
                srtp_crypto_free(session_keys->mki_id);
                session_keys->mki_id = NULL;
            }

            /*
             * deallocate key usage limit, if it is not the same as that in
             * template
             */

            if (template_session_keys &&
                session_keys->limit == template_session_keys->limit) {
                /* do nothing */
            } else if (session_keys->limit) {
                srtp_crypto_free(session_keys->limit);
            }
        }
        srtp_crypto_free(stream->session_keys);
    }

    status = srtp_rdbx_dealloc(&stream->rtp_rdbx);
    if (status)
        return status;

    if (stream_template &&
        stream->enc_xtn_hdr == stream_template->enc_xtn_hdr) {
        /* do nothing */
    } else if (stream->enc_xtn_hdr) {
        srtp_crypto_free(stream->enc_xtn_hdr);
    }

    /* deallocate srtp stream context */
    srtp_crypto_free(stream);

    return srtp_err_status_ok;
}

static srtp_err_status_t srtp_valid_policy(const srtp_policy_t *p)
{
    if (p != NULL && p->deprecated_ekt != NULL) {
        return srtp_err_status_bad_param;
    }

    return srtp_err_status_ok;
}

srtp_err_status_t srtp_stream_alloc(srtp_stream_ctx_t **str_ptr,
                                    const srtp_policy_t *p)
{
    srtp_stream_ctx_t *str;
    srtp_err_status_t stat;
    unsigned int i = 0;
    srtp_session_keys_t *session_keys = NULL;

    stat = srtp_valid_policy(p);
    if (stat != srtp_err_status_ok) {
        return 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 *)srtp_crypto_alloc(sizeof(srtp_stream_ctx_t));
    if (str == NULL)
        return srtp_err_status_alloc_fail;

    *str_ptr = str;

    /*
     *To keep backwards API compatible if someone is using multiple master
     * keys then key should be set to NULL
     */

    if (p->key != NULL) {
        str->num_master_keys = 1;
    } else {
        str->num_master_keys = p->num_master_keys;
    }

    str->session_keys = (srtp_session_keys_t *)srtp_crypto_alloc(
        sizeof(srtp_session_keys_t) * str->num_master_keys);

    if (str->session_keys == NULL) {
        srtp_stream_dealloc(str, NULL);
        return srtp_err_status_alloc_fail;
    }

    for (i = 0; i < str->num_master_keys; i++) {
        session_keys = &str->session_keys[i];

        /* allocate cipher */
        stat = srtp_crypto_kernel_alloc_cipher(
            p->rtp.cipher_type, &session_keys->rtp_cipher,
            p->rtp.cipher_key_len, p->rtp.auth_tag_len);
        if (stat) {
            srtp_stream_dealloc(str, NULL);
            return stat;
        }

        /* allocate auth function */
        stat = srtp_crypto_kernel_alloc_auth(
            p->rtp.auth_type, &session_keys->rtp_auth, p->rtp.auth_key_len,
            p->rtp.auth_tag_len);
        if (stat) {
            srtp_stream_dealloc(str, NULL);
            return stat;
        }

        /*
         * ...and now the RTCP-specific initialization - first, allocate
         * the cipher
         */

        stat = srtp_crypto_kernel_alloc_cipher(
            p->rtcp.cipher_type, &session_keys->rtcp_cipher,
            p->rtcp.cipher_key_len, p->rtcp.auth_tag_len);
        if (stat) {
            srtp_stream_dealloc(str, NULL);
            return stat;
        }

        /* allocate auth function */
        stat = srtp_crypto_kernel_alloc_auth(
            p->rtcp.auth_type, &session_keys->rtcp_auth, p->rtcp.auth_key_len,
            p->rtcp.auth_tag_len);
        if (stat) {
            srtp_stream_dealloc(str, NULL);
            return stat;
        }

        session_keys->mki_id = NULL;

        /* allocate key limit structure */
        session_keys->limit = (srtp_key_limit_ctx_t *)srtp_crypto_alloc(
            sizeof(srtp_key_limit_ctx_t));
        if (session_keys->limit == NULL) {
            srtp_stream_dealloc(str, NULL);
            return srtp_err_status_alloc_fail;
        }
    }

    if (p->enc_xtn_hdr && p->enc_xtn_hdr_count > 0) {
        srtp_cipher_type_id_t enc_xtn_hdr_cipher_type;
        int enc_xtn_hdr_cipher_key_len;

        str->enc_xtn_hdr = (int *)srtp_crypto_alloc(p->enc_xtn_hdr_count *
                                                    sizeof(p->enc_xtn_hdr[0]));
        if (!str->enc_xtn_hdr) {
            srtp_stream_dealloc(str, NULL);
            return srtp_err_status_alloc_fail;
        }
        memcpy(str->enc_xtn_hdr, p->enc_xtn_hdr,
               p->enc_xtn_hdr_count * sizeof(p->enc_xtn_hdr[0]));
        str->enc_xtn_hdr_count = p->enc_xtn_hdr_count;

        /*
         * For GCM ciphers, the corresponding ICM cipher is used for header
         * extensions encryption.
         */

        switch (p->rtp.cipher_type) {
        case SRTP_AES_GCM_128:
            enc_xtn_hdr_cipher_type = SRTP_AES_ICM_128;
            enc_xtn_hdr_cipher_key_len = SRTP_AES_ICM_128_KEY_LEN_WSALT;
            break;
        case SRTP_AES_GCM_256:
            enc_xtn_hdr_cipher_type = SRTP_AES_ICM_256;
            enc_xtn_hdr_cipher_key_len = SRTP_AES_ICM_256_KEY_LEN_WSALT;
            break;
        default:
            enc_xtn_hdr_cipher_type = p->rtp.cipher_type;
            enc_xtn_hdr_cipher_key_len = p->rtp.cipher_key_len;
            break;
        }

        for (i = 0; i < str->num_master_keys; i++) {
            session_keys = &str->session_keys[i];

            /* allocate cipher for extensions header encryption */
            stat = srtp_crypto_kernel_alloc_cipher(
                enc_xtn_hdr_cipher_type, &session_keys->rtp_xtn_hdr_cipher,
                enc_xtn_hdr_cipher_key_len, 0);
            if (stat) {
                srtp_stream_dealloc(str, NULL);
                return stat;
            }
        }
    } else {
        for (i = 0; i < str->num_master_keys; i++) {
            session_keys = &str->session_keys[i];
            session_keys->rtp_xtn_hdr_cipher = NULL;
        }

        str->enc_xtn_hdr = NULL;
        str->enc_xtn_hdr_count = 0;
    }

    return srtp_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
 */


srtp_err_status_t srtp_stream_clone(const srtp_stream_ctx_t *stream_template,
                                    uint32_t ssrc,
                                    srtp_stream_ctx_t **str_ptr)
{
    srtp_err_status_t status;
    srtp_stream_ctx_t *str;
    unsigned int i = 0;
    srtp_session_keys_t *session_keys = NULL;
    const srtp_session_keys_t *template_session_keys = NULL;

    debug_print(mod_srtp, "cloning stream (SSRC: 0x%08x)", ntohl(ssrc));

    /* allocate srtp stream and set str_ptr */
    str = (srtp_stream_ctx_t *)srtp_crypto_alloc(sizeof(srtp_stream_ctx_t));
    if (str == NULL)
        return srtp_err_status_alloc_fail;
    *str_ptr = str;

    str->num_master_keys = stream_template->num_master_keys;
    str->session_keys = (srtp_session_keys_t *)srtp_crypto_alloc(
        sizeof(srtp_session_keys_t) * str->num_master_keys);

    if (str->session_keys == NULL) {
        srtp_stream_dealloc(*str_ptr, stream_template);
        *str_ptr = NULL;
        return srtp_err_status_alloc_fail;
    }

    for (i = 0; i < stream_template->num_master_keys; i++) {
        session_keys = &str->session_keys[i];
        template_session_keys = &stream_template->session_keys[i];

        /* set cipher and auth pointers to those of the template */
        session_keys->rtp_cipher = template_session_keys->rtp_cipher;
        session_keys->rtp_auth = template_session_keys->rtp_auth;
        session_keys->rtp_xtn_hdr_cipher =
            template_session_keys->rtp_xtn_hdr_cipher;
        session_keys->rtcp_cipher = template_session_keys->rtcp_cipher;
        session_keys->rtcp_auth = template_session_keys->rtcp_auth;
        session_keys->mki_size = template_session_keys->mki_size;

        if (template_session_keys->mki_size == 0) {
            session_keys->mki_id = NULL;
        } else {
            session_keys->mki_id =
                srtp_crypto_alloc(template_session_keys->mki_size);

            if (session_keys->mki_id == NULL) {
                srtp_stream_dealloc(*str_ptr, stream_template);
                *str_ptr = NULL;
                return srtp_err_status_init_fail;
            }
            memcpy(session_keys->mki_id, template_session_keys->mki_id,
                   session_keys->mki_size);
        }
        /* Copy the salt values */
        memcpy(session_keys->salt, template_session_keys->salt,
               SRTP_AEAD_SALT_LEN);
        memcpy(session_keys->c_salt, template_session_keys->c_salt,
               SRTP_AEAD_SALT_LEN);

        /* set key limit to point to that of the template */
        status = srtp_key_limit_clone(template_session_keys->limit,
                                      &session_keys->limit);
        if (status) {
            srtp_stream_dealloc(*str_ptr, stream_template);
            *str_ptr = NULL;
            return status;
        }
    }

    /* initialize replay databases */
    status = srtp_rdbx_init(
        &str->rtp_rdbx, srtp_rdbx_get_window_size(&stream_template->rtp_rdbx));
    if (status) {
        srtp_stream_dealloc(*str_ptr, stream_template);
        *str_ptr = NULL;
        return status;
    }
    srtp_rdb_init(&str->rtcp_rdb);
    str->allow_repeat_tx = stream_template->allow_repeat_tx;

    /* set ssrc to that provided */
    str->ssrc = ssrc;

    /* reset pending ROC */
    str->pending_roc = 0;

    /* set direction and security services */
    str->direction = stream_template->direction;
    str->rtp_services = stream_template->rtp_services;
    str->rtcp_services = stream_template->rtcp_services;

    /* copy information about extensions header encryption */
    str->enc_xtn_hdr = stream_template->enc_xtn_hdr;
    str->enc_xtn_hdr_count = stream_template->enc_xtn_hdr_count;

    /* defensive coding */
    str->next = NULL;
    return srtp_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,
    label_rtp_header_encryption = 0x06,
    label_rtp_header_salt = 0x07
} srtp_prf_label;

#define MAX_SRTP_KEY_LEN 256

#if defined(OPENSSL) && defined(OPENSSL_KDF)
#define MAX_SRTP_AESKEY_LEN 32
#define MAX_SRTP_SALT_LEN 14

/*
 * srtp_kdf_t represents a key derivation function.  The SRTP
 * default KDF is the only one implemented at present.
 */

typedef struct {
    uint8_t master_key[MAX_SRTP_AESKEY_LEN];
    uint8_t master_salt[MAX_SRTP_SALT_LEN];
    const EVP_CIPHER *evp;
} srtp_kdf_t;

static srtp_err_status_t srtp_kdf_init(srtp_kdf_t *kdf,
                                       const uint8_t *key,
                                       int key_len,
                                       int salt_len)
{
    memset(kdf, 0x0, sizeof(srtp_kdf_t));

    /* The NULL cipher has zero key length */
    if (key_len == 0)
        return srtp_err_status_ok;

    if ((key_len > MAX_SRTP_AESKEY_LEN) || (salt_len > MAX_SRTP_SALT_LEN)) {
        return srtp_err_status_bad_param;
    }
    switch (key_len) {
    case SRTP_AES_256_KEYSIZE:
        kdf->evp = EVP_aes_256_ctr();
        break;
    case SRTP_AES_192_KEYSIZE:
        kdf->evp = EVP_aes_192_ctr();
        break;
    case SRTP_AES_128_KEYSIZE:
        kdf->evp = EVP_aes_128_ctr();
        break;
    default:
        return srtp_err_status_bad_param;
        break;
    }
    memcpy(kdf->master_key, key, key_len);
    memcpy(kdf->master_salt, key + key_len, salt_len);
    return srtp_err_status_ok;
}

static srtp_err_status_t srtp_kdf_generate(srtp_kdf_t *kdf,
                                           srtp_prf_label label,
                                           uint8_t *key,
                                           unsigned int length)
{
    int ret;

    /* The NULL cipher will not have an EVP */
    if (!kdf->evp)
        return srtp_err_status_ok;
    octet_string_set_to_zero(key, length);

    /*
     * Invoke the OpenSSL SRTP KDF function
     * This is useful if OpenSSL is in FIPS mode and FIP
     * compliance is required for SRTP.
     */

    ret = kdf_srtp(kdf->evp, (char *)&kdf->master_key,
                   (char *)&kdf->master_salt, NULL, NULL, label, (char *)key);
    if (ret == -1) {
        return (srtp_err_status_algo_fail);
    }

    return srtp_err_status_ok;
}

static srtp_err_status_t srtp_kdf_clear(srtp_kdf_t *kdf)
{
    octet_string_set_to_zero(kdf->master_key, MAX_SRTP_AESKEY_LEN);
    octet_string_set_to_zero(kdf->master_salt, MAX_SRTP_SALT_LEN);
    kdf->evp = NULL;

    return srtp_err_status_ok;
}

#else  /* if OPENSSL_KDF */

/*
 * srtp_kdf_t represents a key derivation function.  The SRTP
 * default KDF is the only one implemented at present.
 */

typedef struct {
    srtp_cipher_t *cipher; /* cipher used for key derivation  */
} srtp_kdf_t;

static srtp_err_status_t srtp_kdf_init(srtp_kdf_t *kdf,
                                       const uint8_t *key,
                                       int key_len)
{
    srtp_cipher_type_id_t cipher_id;
    srtp_err_status_t stat;

    switch (key_len) {
    case SRTP_AES_ICM_256_KEY_LEN_WSALT:
        cipher_id = SRTP_AES_ICM_256;
        break;
    case SRTP_AES_ICM_192_KEY_LEN_WSALT:
        cipher_id = SRTP_AES_ICM_192;
        break;
    case SRTP_AES_ICM_128_KEY_LEN_WSALT:
        cipher_id = SRTP_AES_ICM_128;
        break;
    default:
        return srtp_err_status_bad_param;
        break;
    }

    stat = srtp_crypto_kernel_alloc_cipher(cipher_id, &kdf->cipher, key_len, 0);
    if (stat)
        return stat;

    stat = srtp_cipher_init(kdf->cipher, key);
    if (stat) {
        srtp_cipher_dealloc(kdf->cipher);
        return stat;
    }
    return srtp_err_status_ok;
}

static srtp_err_status_t srtp_kdf_generate(srtp_kdf_t *kdf,
                                           srtp_prf_label label,
                                           uint8_t *key,
                                           unsigned int length)
{
    srtp_err_status_t status;
    v128_t nonce;

    /* set eigth octet of nonce to <label>, set the rest of it to zero */
    v128_set_to_zero(&nonce);
    nonce.v8[7] = label;

    status = srtp_cipher_set_iv(kdf->cipher, (uint8_t *)&nonce,
                                srtp_direction_encrypt);
    if (status)
        return status;

    /* generate keystream output */
    octet_string_set_to_zero(key, length);
    status = srtp_cipher_encrypt(kdf->cipher, key, &length);
    if (status)
        return status;

    return srtp_err_status_ok;
}

static srtp_err_status_t srtp_kdf_clear(srtp_kdf_t *kdf)
{
    srtp_err_status_t status;
    status = srtp_cipher_dealloc(kdf->cipher);
    if (status)
        return status;
    kdf->cipher = NULL;
    return srtp_err_status_ok;
}
#endif /* else OPENSSL_KDF */

/*
 *  end of key derivation functions
 */


/* Get the base key length corresponding to a given combined key+salt
 * length for the given cipher.
 * TODO: key and salt lengths should be separate fields in the policy.  */

static inline int base_key_length(const srtp_cipher_type_t *cipher,
                                  int key_length)
{
    switch (cipher->id) {
    case SRTP_NULL_CIPHER:
        return 0;
    case SRTP_AES_ICM_128:
    case SRTP_AES_ICM_192:
    case SRTP_AES_ICM_256:
        /* The legacy modes are derived from
         * the configured key length on the policy */

        return key_length - SRTP_SALT_LEN;
    case SRTP_AES_GCM_128:
        return key_length - SRTP_AEAD_SALT_LEN;
    case SRTP_AES_GCM_256:
        return key_length - SRTP_AEAD_SALT_LEN;
    default:
        return key_length;
    }
}

/* Get the key length that the application should supply for the given cipher */
static inline int full_key_length(const srtp_cipher_type_t *cipher)
{
    switch (cipher->id) {
    case SRTP_NULL_CIPHER:
    case SRTP_AES_ICM_128:
        return SRTP_AES_ICM_128_KEY_LEN_WSALT;
    case SRTP_AES_ICM_192:
        return SRTP_AES_ICM_192_KEY_LEN_WSALT;
    case SRTP_AES_ICM_256:
        return SRTP_AES_ICM_256_KEY_LEN_WSALT;
    case SRTP_AES_GCM_128:
        return SRTP_AES_GCM_128_KEY_LEN_WSALT;
    case SRTP_AES_GCM_256:
        return SRTP_AES_GCM_256_KEY_LEN_WSALT;
    default:
        return 0;
    }
}

unsigned int srtp_validate_policy_master_keys(const srtp_policy_t *policy)
{
    unsigned long i = 0;

    if (policy->key == NULL) {
        if (policy->num_master_keys <= 0)
            return 0;

        if (policy->num_master_keys > SRTP_MAX_NUM_MASTER_KEYS)
            return 0;

        for (i = 0; i < policy->num_master_keys; i++) {
            if (policy->keys[i]->key == NULL)
                return 0;
            if (policy->keys[i]->mki_size > SRTP_MAX_MKI_LEN)
                return 0;
        }
    }

    return 1;
}

srtp_session_keys_t *srtp_get_session_keys_with_mki_index(
    srtp_stream_ctx_t *stream,
    unsigned int use_mki,
    unsigned int mki_index)
{
    if (use_mki) {
        if (mki_index >= stream->num_master_keys) {
            return NULL;
        }
        return &stream->session_keys[mki_index];
    }

    return &stream->session_keys[0];
}

unsigned int srtp_inject_mki(uint8_t *mki_tag_location,
                             srtp_session_keys_t *session_keys,
                             unsigned int use_mki)
{
    unsigned int mki_size = 0;

    if (use_mki) {
        mki_size = session_keys->mki_size;

        if (mki_size != 0) {
            // Write MKI into memory
            memcpy(mki_tag_location, session_keys->mki_id, mki_size);
        }
    }

    return mki_size;
}

srtp_err_status_t srtp_stream_init_all_master_keys(
    srtp_stream_ctx_t *srtp,
    unsigned char *key,
    srtp_master_key_t **keys,
    const unsigned int max_master_keys)
{
    unsigned int i = 0;
    srtp_err_status_t status = srtp_err_status_ok;
    srtp_master_key_t single_master_key;

    if (key != NULL) {
        srtp->num_master_keys = 1;
        single_master_key.key = key;
        single_master_key.mki_id = NULL;
        single_master_key.mki_size = 0;
        status = srtp_stream_init_keys(srtp, &single_master_key, 0);
    } else {
        srtp->num_master_keys = max_master_keys;

        for (i = 0; i < srtp->num_master_keys && i < SRTP_MAX_NUM_MASTER_KEYS;
             i++) {
            status = srtp_stream_init_keys(srtp, keys[i], i);

            if (status) {
                return status;
            }
        }
    }

    return status;
}

srtp_err_status_t srtp_stream_init_keys(srtp_stream_ctx_t *srtp,
                                        srtp_master_key_t *master_key,
                                        const unsigned int current_mki_index)
{
    srtp_err_status_t stat;
    srtp_kdf_t kdf;
    uint8_t tmp_key[MAX_SRTP_KEY_LEN];
    int input_keylen, input_keylen_rtcp;
    int kdf_keylen = 30, rtp_keylen, rtcp_keylen;
    int rtp_base_key_len, rtp_salt_len;
    int rtcp_base_key_len, rtcp_salt_len;
    srtp_session_keys_t *session_keys = NULL;
    unsigned char *key = master_key->key;

    /* If RTP or RTCP have a key length > AES-128, assume matching kdf. */
    /* TODO: kdf algorithm, master key length, and master salt length should
     * be part of srtp_policy_t.
    */

    session_keys = &srtp->session_keys[current_mki_index];

/* initialize key limit to maximum value */
#ifdef NO_64BIT_MATH
    {
        uint64_t temp;
        temp = make64(UINT_MAX, UINT_MAX);
        srtp_key_limit_set(session_keys->limit, temp);
    }
#else
    srtp_key_limit_set(session_keys->limit, 0xffffffffffffLL);
#endif

    if (master_key->mki_size != 0) {
        session_keys->mki_id = srtp_crypto_alloc(master_key->mki_size);

        if (session_keys->mki_id == NULL) {
            return srtp_err_status_init_fail;
        }
        memcpy(session_keys->mki_id, master_key->mki_id, master_key->mki_size);
    } else {
        session_keys->mki_id = NULL;
    }

    session_keys->mki_size = master_key->mki_size;

    input_keylen = full_key_length(session_keys->rtp_cipher->type);
    input_keylen_rtcp = full_key_length(session_keys->rtcp_cipher->type);
    if (input_keylen_rtcp > input_keylen) {
        input_keylen = input_keylen_rtcp;
    }

    rtp_keylen = srtp_cipher_get_key_length(session_keys->rtp_cipher);
    rtcp_keylen = srtp_cipher_get_key_length(session_keys->rtcp_cipher);
    rtp_base_key_len =
        base_key_length(session_keys->rtp_cipher->type, rtp_keylen);
    rtp_salt_len = rtp_keylen - rtp_base_key_len;

    if (rtp_keylen > kdf_keylen) {
        kdf_keylen = 46; /* AES-CTR mode is always used for KDF */
    }

    if (rtcp_keylen > kdf_keylen) {
        kdf_keylen = 46; /* AES-CTR mode is always used for KDF */
    }

    if (input_keylen > kdf_keylen) {
        kdf_keylen = 46; /* AES-CTR mode is always used for KDF */
    }

    debug_print(mod_srtp, "input key len: %d", input_keylen);
    debug_print(mod_srtp, "srtp key len: %d", rtp_keylen);
    debug_print(mod_srtp, "srtcp key len: %d", rtcp_keylen);
    debug_print(mod_srtp, "base key len: %d", rtp_base_key_len);
    debug_print(mod_srtp, "kdf key len: %d", kdf_keylen);
    debug_print(mod_srtp, "rtp salt len: %d", rtp_salt_len);

    /*
     * Make sure the key given to us is 'zero' appended.  GCM
     * mode uses a shorter master SALT (96 bits), but still relies on
     * the legacy CTR mode KDF, which uses a 112 bit master SALT.
     */

    memset(tmp_key, 0x0, MAX_SRTP_KEY_LEN);
    memcpy(tmp_key, key, input_keylen);

/* initialize KDF state     */
#if defined(OPENSSL) && defined(OPENSSL_KDF)
    stat = srtp_kdf_init(&kdf, (const uint8_t *)tmp_key, rtp_base_key_len,
                         rtp_salt_len);
#else
    stat = srtp_kdf_init(&kdf, (const uint8_t *)tmp_key, kdf_keylen);
#endif
    if (stat) {
        /* zeroize temp buffer */
        octet_string_set_to_zero(tmp_key, MAX_SRTP_KEY_LEN);
        return srtp_err_status_init_fail;
    }

    /* generate encryption key  */
    stat = srtp_kdf_generate(&kdf, label_rtp_encryption, tmp_key,
                             rtp_base_key_len);
    if (stat) {
        /* zeroize temp buffer */
        octet_string_set_to_zero(tmp_key, MAX_SRTP_KEY_LEN);
        return srtp_err_status_init_fail;
    }
    debug_print(mod_srtp, "cipher key: %s",
                srtp_octet_string_hex_string(tmp_key, rtp_base_key_len));

    /*
     * if the cipher in the srtp context uses a salt, then we need
     * to generate the salt value
     */

    if (rtp_salt_len > 0) {
        debug_print0(mod_srtp, "found rtp_salt_len > 0, generating salt");

        /* generate encryption salt, put after encryption key */
        stat = srtp_kdf_generate(&kdf, label_rtp_salt,
                                 tmp_key + rtp_base_key_len, rtp_salt_len);
        if (stat) {
            /* zeroize temp buffer */
            octet_string_set_to_zero(tmp_key, MAX_SRTP_KEY_LEN);
            return srtp_err_status_init_fail;
        }
        memcpy(session_keys->salt, tmp_key + rtp_base_key_len,
               SRTP_AEAD_SALT_LEN);
    }
    if (rtp_salt_len > 0) {
        debug_print(mod_srtp, "cipher salt: %s",
                    srtp_octet_string_hex_string(tmp_key + rtp_base_key_len,
                                                 rtp_salt_len));
    }

    /* initialize cipher */
    stat = srtp_cipher_init(session_keys->rtp_cipher, tmp_key);
    if (stat) {
        /* zeroize temp buffer */
        octet_string_set_to_zero(tmp_key, MAX_SRTP_KEY_LEN);
        return srtp_err_status_init_fail;
    }

    if (session_keys->rtp_xtn_hdr_cipher) {
        /* generate extensions header encryption key  */
        int rtp_xtn_hdr_keylen;
        int rtp_xtn_hdr_base_key_len;
        int rtp_xtn_hdr_salt_len;
        srtp_kdf_t tmp_kdf;
        srtp_kdf_t *xtn_hdr_kdf;

        if (session_keys->rtp_xtn_hdr_cipher->type !=
            session_keys->rtp_cipher->type) {
            /*
             * With GCM ciphers, the header extensions are still encrypted using
             * the corresponding ICM cipher.
             * See https://tools.ietf.org/html/rfc7714#section-8.3
             */

            uint8_t tmp_xtn_hdr_key[MAX_SRTP_KEY_LEN];
            rtp_xtn_hdr_keylen =
                srtp_cipher_get_key_length(session_keys->rtp_xtn_hdr_cipher);
            rtp_xtn_hdr_base_key_len = base_key_length(
                session_keys->rtp_xtn_hdr_cipher->type, rtp_xtn_hdr_keylen);
            rtp_xtn_hdr_salt_len =
                rtp_xtn_hdr_keylen - rtp_xtn_hdr_base_key_len;
            if (rtp_xtn_hdr_salt_len > rtp_salt_len) {
                switch (session_keys->rtp_cipher->type->id) {
                case SRTP_AES_GCM_128:
                case SRTP_AES_GCM_256:
                    /*
                     * The shorter GCM salt is padded to the required ICM salt
                     * length.
                     */

                    rtp_xtn_hdr_salt_len = rtp_salt_len;
                    break;
                default:
                    /* zeroize temp buffer */
                    octet_string_set_to_zero(tmp_key, MAX_SRTP_KEY_LEN);
                    return srtp_err_status_bad_param;
                }
            }
            memset(tmp_xtn_hdr_key, 0x0, MAX_SRTP_KEY_LEN);
            memcpy(tmp_xtn_hdr_key, key,
                   (rtp_xtn_hdr_base_key_len + rtp_xtn_hdr_salt_len));
            xtn_hdr_kdf = &tmp_kdf;

/* initialize KDF state */
#if defined(OPENSSL) && defined(OPENSSL_KDF)
            stat =
                srtp_kdf_init(xtn_hdr_kdf, (const uint8_t *)tmp_xtn_hdr_key,
                              rtp_xtn_hdr_base_key_len, rtp_xtn_hdr_salt_len);
#else
            stat = srtp_kdf_init(xtn_hdr_kdf, (const uint8_t *)tmp_xtn_hdr_key,
                                 kdf_keylen);
#endif
            octet_string_set_to_zero(tmp_xtn_hdr_key, MAX_SRTP_KEY_LEN);
            if (stat) {
                /* zeroize temp buffer */
                octet_string_set_to_zero(tmp_key, MAX_SRTP_KEY_LEN);
                return srtp_err_status_init_fail;
            }
        } else {
            /* Reuse main KDF. */
            rtp_xtn_hdr_keylen = rtp_keylen;
            rtp_xtn_hdr_base_key_len = rtp_base_key_len;
            rtp_xtn_hdr_salt_len = rtp_salt_len;
            xtn_hdr_kdf = &kdf;
        }

        stat = srtp_kdf_generate(xtn_hdr_kdf, label_rtp_header_encryption,
                                 tmp_key, rtp_xtn_hdr_base_key_len);
        if (stat) {
            /* zeroize temp buffer */
            octet_string_set_to_zero(tmp_key, MAX_SRTP_KEY_LEN);
            return srtp_err_status_init_fail;
        }
        debug_print(
            mod_srtp, "extensions cipher key: %s",
            srtp_octet_string_hex_string(tmp_key, rtp_xtn_hdr_base_key_len));

        /*
         * if the cipher in the srtp context uses a salt, then we need
         * to generate the salt value
         */

        if (rtp_xtn_hdr_salt_len > 0) {
            debug_print0(mod_srtp,
                         "found rtp_xtn_hdr_salt_len > 0, generating salt");

            /* generate encryption salt, put after encryption key */
            stat = srtp_kdf_generate(xtn_hdr_kdf, label_rtp_header_salt,
                                     tmp_key + rtp_xtn_hdr_base_key_len,
                                     rtp_xtn_hdr_salt_len);
            if (stat) {
                /* zeroize temp buffer */
                octet_string_set_to_zero(tmp_key, MAX_SRTP_KEY_LEN);
                return srtp_err_status_init_fail;
            }
        }
        if (rtp_xtn_hdr_salt_len > 0) {
            debug_print(
                mod_srtp, "extensions cipher salt: %s",
                srtp_octet_string_hex_string(tmp_key + rtp_xtn_hdr_base_key_len,
                                             rtp_xtn_hdr_salt_len));
        }

        /* initialize extensions header cipher */
        stat = srtp_cipher_init(session_keys->rtp_xtn_hdr_cipher, tmp_key);
        if (stat) {
            /* zeroize temp buffer */
            octet_string_set_to_zero(tmp_key, MAX_SRTP_KEY_LEN);
            return srtp_err_status_init_fail;
        }

        if (xtn_hdr_kdf != &kdf) {
            /* release memory for custom header extension encryption kdf */
            stat = srtp_kdf_clear(xtn_hdr_kdf);
            if (stat) {
                /* zeroize temp buffer */
                octet_string_set_to_zero(tmp_key, MAX_SRTP_KEY_LEN);
                return srtp_err_status_init_fail;
            }
        }
    }

    /* generate authentication key */
    stat = srtp_kdf_generate(&kdf, label_rtp_msg_auth, tmp_key,
                             srtp_auth_get_key_length(session_keys->rtp_auth));
    if (stat) {
        /* zeroize temp buffer */
        octet_string_set_to_zero(tmp_key, MAX_SRTP_KEY_LEN);
        return srtp_err_status_init_fail;
    }
    debug_print(mod_srtp, "auth key: %s",
                srtp_octet_string_hex_string(
                    tmp_key, srtp_auth_get_key_length(session_keys->rtp_auth)));

    /* initialize auth function */
    stat = srtp_auth_init(session_keys->rtp_auth, tmp_key);
    if (stat) {
        /* zeroize temp buffer */
        octet_string_set_to_zero(tmp_key, MAX_SRTP_KEY_LEN);
        return srtp_err_status_init_fail;
    }

    /*
     * ...now initialize SRTCP keys
     */


    rtcp_base_key_len =
        base_key_length(session_keys->rtcp_cipher->type, rtcp_keylen);
    rtcp_salt_len = rtcp_keylen - rtcp_base_key_len;
    debug_print(mod_srtp, "rtcp salt len: %d", rtcp_salt_len);

    /* generate encryption key  */
    stat = srtp_kdf_generate(&kdf, label_rtcp_encryption, tmp_key,
                             rtcp_base_key_len);
    if (stat) {
        /* zeroize temp buffer */
        octet_string_set_to_zero(tmp_key, MAX_SRTP_KEY_LEN);
        return srtp_err_status_init_fail;
    }

    /*
     * if the cipher in the srtp context uses a salt, then we need
     * to generate the salt value
     */

    if (rtcp_salt_len > 0) {
        debug_print0(mod_srtp, "found rtcp_salt_len > 0, generating rtcp salt");

        /* generate encryption salt, put after encryption key */
        stat = srtp_kdf_generate(&kdf, label_rtcp_salt,
                                 tmp_key + rtcp_base_key_len, rtcp_salt_len);
        if (stat) {
            /* zeroize temp buffer */
            octet_string_set_to_zero(tmp_key, MAX_SRTP_KEY_LEN);
            return srtp_err_status_init_fail;
        }
        memcpy(session_keys->c_salt, tmp_key + rtcp_base_key_len,
               SRTP_AEAD_SALT_LEN);
    }
    debug_print(mod_srtp, "rtcp cipher key: %s",
                srtp_octet_string_hex_string(tmp_key, rtcp_base_key_len));
    if (rtcp_salt_len > 0) {
        debug_print(mod_srtp, "rtcp cipher salt: %s",
                    srtp_octet_string_hex_string(tmp_key + rtcp_base_key_len,
                                                 rtcp_salt_len));
    }

    /* initialize cipher */
    stat = srtp_cipher_init(session_keys->rtcp_cipher, tmp_key);
    if (stat) {
        /* zeroize temp buffer */
        octet_string_set_to_zero(tmp_key, MAX_SRTP_KEY_LEN);
        return srtp_err_status_init_fail;
    }

    /* generate authentication key */
    stat = srtp_kdf_generate(&kdf, label_rtcp_msg_auth, tmp_key,
                             srtp_auth_get_key_length(session_keys->rtcp_auth));
    if (stat) {
        /* zeroize temp buffer */
        octet_string_set_to_zero(tmp_key, MAX_SRTP_KEY_LEN);
        return srtp_err_status_init_fail;
    }

    debug_print(
        mod_srtp, "rtcp auth key: %s",
        srtp_octet_string_hex_string(
            tmp_key, srtp_auth_get_key_length(session_keys->rtcp_auth)));

    /* initialize auth function */
    stat = srtp_auth_init(session_keys->rtcp_auth, tmp_key);
    if (stat) {
        /* zeroize temp buffer */
        octet_string_set_to_zero(tmp_key, MAX_SRTP_KEY_LEN);
        return srtp_err_status_init_fail;
    }

    /* clear memory then return */
    stat = srtp_kdf_clear(&kdf);
    octet_string_set_to_zero(tmp_key, MAX_SRTP_KEY_LEN);
    if (stat)
        return srtp_err_status_init_fail;

    return srtp_err_status_ok;
}

srtp_err_status_t srtp_stream_init(srtp_stream_ctx_t *srtp,
                                   const srtp_policy_t *p)
{
    srtp_err_status_t err;

    err = srtp_valid_policy(p);
    if (err != srtp_err_status_ok) {
        return err;
    }

    debug_print(mod_srtp, "initializing stream (SSRC: 0x%08x)", p->ssrc.value);

    /* initialize replay database */
    /*
     * window size MUST be at least 64.  MAY be larger.  Values more than
     * 2^15 aren't meaningful due to how extended sequence numbers are
     * calculated.
     * Let a window size of 0 imply the default value.
     */


    if (p->window_size != 0 &&
        (p->window_size < 64 || p->window_size >= 0x8000))
        return srtp_err_status_bad_param;

    if (p->window_size != 0)
        err = srtp_rdbx_init(&srtp->rtp_rdbx, p->window_size);
    else
        err = srtp_rdbx_init(&srtp->rtp_rdbx, 128);
    if (err)
        return err;

    /* set the SSRC value */
    srtp->ssrc = htonl(p->ssrc.value);

    /* reset pending ROC */
    srtp->pending_roc = 0;

    /* set the security service flags */
    srtp->rtp_services = p->rtp.sec_serv;
    srtp->rtcp_services = p->rtcp.sec_serv;

    /*
     * set direction to unknown - this flag gets checked in srtp_protect(),
     * srtp_unprotect(), srtp_protect_rtcp(), and srtp_unprotect_rtcp(), and
     * gets set appropriately if it is set to unknown.
     */

    srtp->direction = dir_unknown;

    /* initialize SRTCP replay database */
    srtp_rdb_init(&srtp->rtcp_rdb);

    /* initialize allow_repeat_tx */
    /* guard against uninitialized memory: allow only 0 or 1 here */
    if (p->allow_repeat_tx != 0 && p->allow_repeat_tx != 1) {
        srtp_rdbx_dealloc(&srtp->rtp_rdbx);
        return srtp_err_status_bad_param;
    }
    srtp->allow_repeat_tx = p->allow_repeat_tx;

    /* DAM - no RTCP key limit at present */

    /* initialize keys */
    err = srtp_stream_init_all_master_keys(srtp, p->key, p->keys,
                                           p->num_master_keys);
    if (err) {
        srtp_rdbx_dealloc(&srtp->rtp_rdbx);
        return err;
    }

    return srtp_err_status_ok;
}

/*
 * srtp_event_reporter is an event handler function that merely
 * reports the events that are reported by the callbacks
 */


void srtp_event_reporter(srtp_event_data_t *data)
{
    srtp_err_report(srtp_err_level_warning, "srtp: in stream 0x%x: ",
                    data->ssrc);

    switch (data->event) {
    case event_ssrc_collision:
        srtp_err_report(srtp_err_level_warning, "\tSSRC collision\n");
        break;
    case event_key_soft_limit:
        srtp_err_report(srtp_err_level_warning,
                        "\tkey usage soft limit reached\n");
        break;
    case event_key_hard_limit:
        srtp_err_report(srtp_err_level_warning,
                        "\tkey usage hard limit reached\n");
        break;
    case event_packet_index_limit:
        srtp_err_report(srtp_err_level_warning,
                        "\tpacket index limit reached\n");
        break;
    default:
        srtp_err_report(srtp_err_level_warning,
                        "\tunknown event reported to handler\n");
    }
}

/*
 * srtp_event_handler is a global variable holding a pointer to the
 * event handler function; this function is called for any unexpected
 * event that needs to be handled out of the SRTP data path.  see
 * srtp_event_t in srtp.h for more info
 *
 * it is okay to set srtp_event_handler to NULL, but we set
 * it to the srtp_event_reporter.
 */


static srtp_event_handler_func_t *srtp_event_handler = srtp_event_reporter;

srtp_err_status_t srtp_install_event_handler(srtp_event_handler_func_t func)
{
    /*
     * note that we accept NULL arguments intentionally - calling this
     * function with a NULL arguments removes an event handler that's
     * been previously installed
     */


    /* set global event handling function */
    srtp_event_handler = func;
    return srtp_err_status_ok;
}

/*
 * Check if the given extension header id is / should be encrypted.
 * Returns 1 if yes, otherwise 0.
 */

static int srtp_protect_extension_header(srtp_stream_ctx_t *stream, int id)
{
    int *enc_xtn_hdr = stream->enc_xtn_hdr;
    int count = stream->enc_xtn_hdr_count;

    if (!enc_xtn_hdr || count <= 0) {
        return 0;
    }

    while (count > 0) {
        if (*enc_xtn_hdr == id) {
            return 1;
        }

        enc_xtn_hdr++;
        count--;
    }
    return 0;
}

/*
 * extensions header encryption RFC 6904
 */

static srtp_err_status_t srtp_process_header_encryption(
    srtp_stream_ctx_t *stream,
    srtp_hdr_xtnd_t *xtn_hdr,
    srtp_session_keys_t *session_keys)
{
    srtp_err_status_t status;
    uint8_t keystream[257]; /* Maximum 2 bytes header + 255 bytes data. */
    int keystream_pos;
    uint8_t *xtn_hdr_data = ((uint8_t *)xtn_hdr) + octets_in_rtp_extn_hdr;
    uint8_t *xtn_hdr_end =
        xtn_hdr_data + (ntohs(xtn_hdr->length) * sizeof(uint32_t));

    if (ntohs(xtn_hdr->profile_specific) == 0xbede) {
        /* RFC 5285, section 4.2. One-Byte Header */
        while (xtn_hdr_data < xtn_hdr_end) {
            uint8_t xid = (*xtn_hdr_data & 0xf0) >> 4;
            unsigned int xlen = (*xtn_hdr_data & 0x0f) + 1;
            uint32_t xlen_with_header = 1 + xlen;
            xtn_hdr_data++;

            if (xtn_hdr_data + xlen > xtn_hdr_end)
                return srtp_err_status_parse_err;

            if (xid == 15) {
                /* found header 15, stop further processing. */
                break;
            }

            status = srtp_cipher_output(session_keys->rtp_xtn_hdr_cipher,
                                        keystream, &xlen_with_header);
            if (status)
                return srtp_err_status_cipher_fail;

            if (srtp_protect_extension_header(stream, xid)) {
                keystream_pos = 1;
                while (xlen > 0) {
                    *xtn_hdr_data ^= keystream[keystream_pos++];
                    xtn_hdr_data++;
                    xlen--;
                }
            } else {
                xtn_hdr_data += xlen;
            }

            /* skip padding bytes. */
            while (xtn_hdr_data < xtn_hdr_end && *xtn_hdr_data == 0) {
                xtn_hdr_data++;
            }
        }
    } else if ((ntohs(xtn_hdr->profile_specific) & 0xfff0) == 0x1000) {
        /* RFC 5285, section 4.3. Two-Byte Header */
        while (xtn_hdr_data + 1 < xtn_hdr_end) {
            uint8_t xid = *xtn_hdr_data;
            unsigned int xlen = *(xtn_hdr_data + 1);
            uint32_t xlen_with_header = 2 + xlen;
            xtn_hdr_data += 2;

            if (xtn_hdr_data + xlen > xtn_hdr_end)
                return srtp_err_status_parse_err;

            status = srtp_cipher_output(session_keys->rtp_xtn_hdr_cipher,
                                        keystream, &xlen_with_header);
            if (status)
                return srtp_err_status_cipher_fail;

            if (xlen > 0 && srtp_protect_extension_header(stream, xid)) {
                keystream_pos = 2;
                while (xlen > 0) {
                    *xtn_hdr_data ^= keystream[keystream_pos++];
                    xtn_hdr_data++;
                    xlen--;
                }
            } else {
                xtn_hdr_data += xlen;
            }

            /* skip padding bytes. */
            while (xtn_hdr_data < xtn_hdr_end && *xtn_hdr_data == 0) {
                xtn_hdr_data++;
            }
        }
    } else {
        /* unsupported extension header format. */
        return srtp_err_status_parse_err;
    }

    return srtp_err_status_ok;
}

/*
 * AEAD uses a new IV formation method.  This function implements
 * section 8.1. (SRTP IV Formation for AES-GCM) of RFC7714.
 * The calculation is defined as, where (+) is the xor operation:
 *
 *
 *              0  0  0  0  0  0  0  0  0  0  1  1
 *              0  1  2  3  4  5  6  7  8  9  0  1
 *            +--+--+--+--+--+--+--+--+--+--+--+--+
 *            |00|00|    SSRC   |     ROC   | SEQ |---+
 *            +--+--+--+--+--+--+--+--+--+--+--+--+   |
 *                                                    |
 *            +--+--+--+--+--+--+--+--+--+--+--+--+   |
 *            |         Encryption Salt           |->(+)
 *            +--+--+--+--+--+--+--+--+--+--+--+--+   |
 *                                                    |
 *            +--+--+--+--+--+--+--+--+--+--+--+--+   |
 *            |       Initialization Vector       |<--+
 *            +--+--+--+--+--+--+--+--+--+--+--+--+*
 *
 * Input:  *session_keys - pointer to SRTP stream context session keys,
 *                         used to retrieve the SALT
 *         *iv     - Pointer to receive the calculated IV
 *         *seq    - The ROC and SEQ value to use for the
 *                   IV calculation.
 *         *hdr    - The RTP header, used to get the SSRC value
 *
 */


static void srtp_calc_aead_iv(srtp_session_keys_t *session_keys,
                              v128_t *iv,
                              srtp_xtd_seq_num_t *seq,
                              srtp_hdr_t *hdr)
{
    v128_t in;
    v128_t salt;

#ifdef NO_64BIT_MATH
    uint32_t local_roc = ((high32(*seq) << 16) | (low32(*seq) >> 16));
    uint16_t local_seq = (uint16_t)(low32(*seq));
#else
    uint32_t local_roc = (uint32_t)(*seq >> 16);
    uint16_t local_seq = (uint16_t)*seq;
#endif

    memset(&in, 0, sizeof(v128_t));
    memset(&salt, 0, sizeof(v128_t));

    in.v16[5] = htons(local_seq);
    local_roc = htonl(local_roc);
    memcpy(&in.v16[3], &local_roc, sizeof(local_roc));

    /*
     * Copy in the RTP SSRC value
     */

    memcpy(&in.v8[2], &hdr->ssrc, 4);
    debug_print(mod_srtp, "Pre-salted RTP IV = %s\n", v128_hex_string(&in));

    /*
     * Get the SALT value from the context
     */

    memcpy(salt.v8, session_keys->salt, SRTP_AEAD_SALT_LEN);
    debug_print(mod_srtp, "RTP SALT = %s\n", v128_hex_string(&salt));

    /*
     * Finally, apply tyhe SALT to the input
     */

    v128_xor(iv, &in, &salt);
}

srtp_session_keys_t *srtp_get_session_keys(srtp_stream_ctx_t *stream,
                                           uint8_t *hdr,
                                           const unsigned int *pkt_octet_len,
                                           unsigned int *mki_size)
{
    unsigned int base_mki_start_location = *pkt_octet_len;
    unsigned int mki_start_location = 0;
    unsigned int tag_len = 0;
    unsigned int i = 0;

    // Determine the authentication tag size
    if (stream->session_keys[0].rtp_cipher->algorithm == SRTP_AES_GCM_128 ||
        stream->session_keys[0].rtp_cipher->algorithm == SRTP_AES_GCM_256) {
        tag_len = 0;
    } else {
        tag_len = srtp_auth_get_tag_length(stream->session_keys[0].rtp_auth);
    }

    if (tag_len > base_mki_start_location) {
        *mki_size = 0;
        return NULL;
    }

    base_mki_start_location -= tag_len;

    for (i = 0; i < stream->num_master_keys; i++) {
        if (stream->session_keys[i].mki_size != 0 &&
            stream->session_keys[i].mki_size <= base_mki_start_location) {
            *mki_size = stream->session_keys[i].mki_size;
            mki_start_location = base_mki_start_location - *mki_size;

            if (memcmp(hdr + mki_start_location, stream->session_keys[i].mki_id,
                       *mki_size) == 0) {
                return &stream->session_keys[i];
            }
        }
    }

    *mki_size = 0;
    return NULL;
}

static srtp_err_status_t srtp_estimate_index(srtp_rdbx_t *rdbx,
                                             uint32_t roc,
                                             srtp_xtd_seq_num_t *est,
                                             srtp_sequence_number_t seq,
                                             int *delta)
{
#ifdef NO_64BIT_MATH
    uint32_t internal_pkt_idx_reduced;
    uint32_t external_pkt_idx_reduced;
    uint32_t internal_roc;
    uint32_t roc_difference;
#endif

#ifdef NO_64BIT_MATH
    *est = (srtp_xtd_seq_num_t)make64(roc >> 16, (roc << 16) | seq);
    *delta = low32(est) - rdbx->index;
#else
    *est = (srtp_xtd_seq_num_t)(((uint64_t)roc) << 16) | seq;
    *delta = (int)(*est - rdbx->index);
#endif

    if (*est > rdbx->index) {
#ifdef NO_64BIT_MATH
        internal_roc = (uint32_t)(rdbx->index >> 16);
        roc_difference = roc - internal_roc;
        if (roc_difference > 1) {
            *delta = 0;
            return srtp_err_status_pkt_idx_adv;
        }

        internal_pkt_idx_reduced = (uint32_t)(rdbx->index & 0xFFFF);
        external_pkt_idx_reduced = (uint32_t)((roc_difference << 16) | seq);

        if (external_pkt_idx_reduced - internal_pkt_idx_reduced >
            seq_num_median) {
            *delta = 0;
            return srtp_err_status_pkt_idx_adv;
        }
#else
        if (*est - rdbx->index > seq_num_median) {
            *delta = 0;
            return srtp_err_status_pkt_idx_adv;
        }
#endif
    } else if (*est < rdbx->index) {
#ifdef NO_64BIT_MATH

        internal_roc = (uint32_t)(rdbx->index >> 16);
        roc_difference = internal_roc - roc;
        if (roc_difference > 1) {
            *delta = 0;
            return srtp_err_status_pkt_idx_adv;
        }

        internal_pkt_idx_reduced =
            (uint32_t)((roc_difference << 16) | rdbx->index & 0xFFFF);
        external_pkt_idx_reduced = (uint32_t)(seq);

        if (internal_pkt_idx_reduced - external_pkt_idx_reduced >
            seq_num_median) {
            *delta = 0;
            return srtp_err_status_pkt_idx_old;
        }
#else
        if (rdbx->index - *est > seq_num_median) {
            *delta = 0;
            return srtp_err_status_pkt_idx_old;
        }
#endif
    }

    return srtp_err_status_ok;
}

static srtp_err_status_t srtp_get_est_pkt_index(srtp_hdr_t *hdr,
                                                srtp_stream_ctx_t *stream,
                                                srtp_xtd_seq_num_t *est,
                                                int *delta)
{
    srtp_err_status_t result = srtp_err_status_ok;

    if (stream->pending_roc) {
        result = srtp_estimate_index(&stream->rtp_rdbx, stream->pending_roc,
                                     est, ntohs(hdr->seq), delta);
    } else {
        /* estimate packet index from seq. num. in header */
        *delta =
            srtp_rdbx_estimate_index(&stream->rtp_rdbx, est, ntohs(hdr->seq));
    }

#ifdef NO_64BIT_MATH
    debug_print2(mod_srtp, "estimated u_packet index: %08x%08x", high32(*est),
                 low32(*est));
#else
    debug_print(mod_srtp, "estimated u_packet index: %016" PRIx64, *est);
#endif
    return result;
}

/*
 * This function handles outgoing SRTP packets while in AEAD mode,
 * which currently supports AES-GCM encryption.  All packets are
 * encrypted and authenticated.
 */

static srtp_err_status_t srtp_protect_aead(srtp_ctx_t *ctx,
                                           srtp_stream_ctx_t *stream,
                                           void *rtp_hdr,
                                           unsigned int *pkt_octet_len,
                                           srtp_session_keys_t *session_keys,
                                           unsigned int use_mki)
{
    srtp_hdr_t *hdr = (srtp_hdr_t *)rtp_hdr;
    uint32_t *enc_start;    /* pointer to start of encrypted portion  */
    int enc_octet_len = 0;  /* number of octets in encrypted portion  */
    srtp_xtd_seq_num_t est; /* estimated xtd_seq_num_t of *hdr        */
    int delta;              /* delta of local pkt idx and that in hdr */
    srtp_err_status_t status;
    uint32_t tag_len;
    v128_t iv;
    unsigned int aad_len;
    srtp_hdr_xtnd_t *xtn_hdr = NULL;
    unsigned int mki_size = 0;
    uint8_t *mki_location = NULL;

    debug_print0(mod_srtp, "function srtp_protect_aead");

    /*
     * update the key usage limit, and check it to make sure that we
     * didn't just hit either the soft limit or the hard limit, and call
     * the event handler if we hit either.
     */

    switch (srtp_key_limit_update(session_keys->limit)) {
    case srtp_key_event_normal:
        break;
    case srtp_key_event_hard_limit:
        srtp_handle_event(ctx, stream, event_key_hard_limit);
        return srtp_err_status_key_expired;
    case srtp_key_event_soft_limit:
    default:
        srtp_handle_event(ctx, stream, event_key_soft_limit);
        break;
    }

    /* get tag length from stream */
    tag_len = srtp_auth_get_tag_length(session_keys->rtp_auth);

    /*
     * find starting point for encryption and length of data to be
     * encrypted - the encrypted portion starts after the rtp header
     * extension, if present; otherwise, it starts after the last csrc,
     * if any are present
     */

    enc_start = (uint32_t *)hdr + uint32s_in_rtp_header + hdr->cc;
    if (hdr->x == 1) {
        xtn_hdr = (srtp_hdr_xtnd_t *)enc_start;
        enc_start += (ntohs(xtn_hdr->length) + 1);
    }
    /* note: the passed size is without the auth tag */
    if (!((uint8_t *)enc_start <= (uint8_t *)hdr + *pkt_octet_len))
        return srtp_err_status_parse_err;
    enc_octet_len =
        (int)(*pkt_octet_len - ((uint8_t *)enc_start - (uint8_t *)hdr));
    if (enc_octet_len < 0)
        return srtp_err_status_parse_err;

    /*
     * estimate the packet index using the start of the replay window
     * and the sequence number from the header
     */

    delta = srtp_rdbx_estimate_index(&stream->rtp_rdbx, &est, ntohs(hdr->seq));
    status = srtp_rdbx_check(&stream->rtp_rdbx, delta);
    if (status) {
        if (status != srtp_err_status_replay_fail || !stream->allow_repeat_tx) {
            return status; /* we've been asked to reuse an index */
        }
    } else {
        srtp_rdbx_add_index(&stream->rtp_rdbx, delta);
    }

#ifdef NO_64BIT_MATH
    debug_print2(mod_srtp, "estimated packet index: %08x%08x", high32(est),
                 low32(est));
#else
    debug_print(mod_srtp, "estimated packet index: %016" PRIx64, est);
#endif

    /*
     * AEAD uses a new IV formation method
     */

    srtp_calc_aead_iv(session_keys, &iv, &est, hdr);
/* shift est, put into network byte order */
#ifdef NO_64BIT_MATH
    est = be64_to_cpu(
        make64((high32(est) << 16) | (low32(est) >> 16), low32(est) << 16));
#else
    est = be64_to_cpu(est << 16);
#endif

    status = srtp_cipher_set_iv(session_keys->rtp_cipher, (uint8_t *)&iv,
                                srtp_direction_encrypt);
    if (!status && session_keys->rtp_xtn_hdr_cipher) {
        iv.v32[0] = 0;
        iv.v32[1] = hdr->ssrc;
        iv.v64[1] = est;
        status = srtp_cipher_set_iv(session_keys->rtp_xtn_hdr_cipher,
                                    (uint8_t *)&iv, srtp_direction_encrypt);
    }
    if (status) {
        return srtp_err_status_cipher_fail;
    }

    if (xtn_hdr && session_keys->rtp_xtn_hdr_cipher) {
        /*
         * extensions header encryption RFC 6904
         */

        status = srtp_process_header_encryption(stream, xtn_hdr, session_keys);
        if (status) {
            return status;
        }
    }

    /*
     * Set the AAD over the RTP header
     */

    aad_len = (uint8_t *)enc_start - (uint8_t *)hdr;
    status =
        srtp_cipher_set_aad(session_keys->rtp_cipher, (uint8_t *)hdr, aad_len);
    if (status) {
        return (srtp_err_status_cipher_fail);
    }

    /* Encrypt the payload  */
    status = srtp_cipher_encrypt(session_keys->rtp_cipher, (uint8_t *)enc_start,
                                 (unsigned int *)&enc_octet_len);
    if (status) {
        return srtp_err_status_cipher_fail;
    }
    /*
     * If we're doing GCM, we need to get the tag
     * and append that to the output
     */

    status =
        srtp_cipher_get_tag(session_keys->rtp_cipher,
                            (uint8_t *)enc_start + enc_octet_len, &tag_len);
    if (status) {
        return (srtp_err_status_cipher_fail);
    }

    mki_location = (uint8_t *)hdr + *pkt_octet_len + tag_len;
    mki_size = srtp_inject_mki(mki_location, session_keys, use_mki);

    /* increase the packet length by the length of the auth tag */
    *pkt_octet_len += tag_len;

    /* increase the packet length by the length of the mki_size */
    *pkt_octet_len += mki_size;

    return srtp_err_status_ok;
}

/*
 * This function handles incoming SRTP packets while in AEAD mode,
 * which currently supports AES-GCM encryption.  All packets are
 * encrypted and authenticated.  Note, the auth tag is at the end
 * of the packet stream and is automatically checked by GCM
 * when decrypting the payload.
 */

static srtp_err_status_t srtp_unprotect_aead(srtp_ctx_t *ctx,
                                             srtp_stream_ctx_t *stream,
                                             int delta,
                                             srtp_xtd_seq_num_t est,
                                             void *srtp_hdr,
                                             unsigned int *pkt_octet_len,
                                             srtp_session_keys_t *session_keys,
                                             unsigned int mki_size)
{
    srtp_hdr_t *hdr = (srtp_hdr_t *)srtp_hdr;
    uint32_t *enc_start;            /* pointer to start of encrypted portion  */
    unsigned int enc_octet_len = 0; /* number of octets in encrypted portion */
    v128_t iv;
    srtp_err_status_t status;
    int tag_len;
    unsigned int aad_len;
    srtp_hdr_xtnd_t *xtn_hdr = NULL;

    debug_print0(mod_srtp, "function srtp_unprotect_aead");

#ifdef NO_64BIT_MATH
    debug_print2(mod_srtp, "estimated u_packet index: %08x%08x", high32(est),
                 low32(est));
#else
    debug_print(mod_srtp, "estimated u_packet index: %016" PRIx64, est);
#endif

    /* get tag length from stream */
    tag_len = srtp_auth_get_tag_length(session_keys->rtp_auth);

    /*
     * AEAD uses a new IV formation method
     */

    srtp_calc_aead_iv(session_keys, &iv, &est, hdr);
    status = srtp_cipher_set_iv(session_keys->rtp_cipher, (uint8_t *)&iv,
                                srtp_direction_decrypt);
    if (!status && session_keys->rtp_xtn_hdr_cipher) {
        iv.v32[0] = 0;
        iv.v32[1] = hdr->ssrc;
#ifdef NO_64BIT_MATH
        iv.v64[1] = be64_to_cpu(
            make64((high32(est) << 16) | (low32(est) >> 16), low32(est) << 16));
#else
        iv.v64[1] = be64_to_cpu(est << 16);
#endif
        status = srtp_cipher_set_iv(session_keys->rtp_xtn_hdr_cipher,
                                    (uint8_t *)&iv, srtp_direction_encrypt);
    }
    if (status) {
        return srtp_err_status_cipher_fail;
    }

    /*
     * find starting point for decryption and length of data to be
     * decrypted - the encrypted portion starts after the rtp header
     * extension, if present; otherwise, it starts after the last csrc,
     * if any are present
     */

    enc_start = (uint32_t *)hdr + uint32s_in_rtp_header + hdr->cc;
    if (hdr->x == 1) {
        xtn_hdr = (srtp_hdr_xtnd_t *)enc_start;
        enc_start += (ntohs(xtn_hdr->length) + 1);
    }
    if (!((uint8_t *)enc_start <=
          (uint8_t *)hdr + (*pkt_octet_len - tag_len - mki_size)))
        return srtp_err_status_parse_err;
    /*
     * We pass the tag down to the cipher when doing GCM mode
     */

    enc_octet_len = (unsigned int)(*pkt_octet_len - mki_size -
                                   ((uint8_t *)enc_start - (uint8_t *)hdr));

    /*
     * Sanity check the encrypted payload length against
     * the tag size.  It must always be at least as large
     * as the tag length.
     */

    if (enc_octet_len < (unsigned int)tag_len) {
        return srtp_err_status_cipher_fail;
    }

    /*
     * update the key usage limit, and check it to make sure that we
     * didn't just hit either the soft limit or the hard limit, and call
     * the event handler if we hit either.
     */

    switch (srtp_key_limit_update(session_keys->limit)) {
    case srtp_key_event_normal:
        break;
    case srtp_key_event_soft_limit:
        srtp_handle_event(ctx, stream, event_key_soft_limit);
        break;
    case srtp_key_event_hard_limit:
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=95 H=82 G=88

¤ Dauer der Verarbeitung: 0.35 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.