srtp_unprotect_rtcp(srtp_t ctx, void *srtcp_hdr, int *pkt_octet_len) {
  srtcp_hdr_t *hdr = (srtcp_hdr_t *)srtcp_hdr;
  uint32_t *enc_start;      /* pointer to start of encrypted portion  */
  uint32_t *auth_start;     /* pointer to start of auth. portion      */
  uint32_t *trailer;        /* pointer to start of trailer            */
  unsigned int enc_octet_len = 0;/* number of octets in encrypted portion */
  uint8_t *auth_tag = NULL; /* location of auth_tag within packet     */
  uint8_t tmp_tag[SRTP_MAX_TAG_LEN];
  uint8_t tag_copy[SRTP_MAX_TAG_LEN];
  err_status_t status;   
  unsigned int auth_len;
  int tag_len;
  srtp_stream_ctx_t *stream;
  int prefix_len;
  uint32_t seq_num;
  int e_bit_in_packet;     /* whether the E-bit was found in the packet */
  int sec_serv_confidentiality; /* whether confidentiality was requested */

  /* we assume the hdr is 32-bit aligned to start */

  /* check that the length value is sane; we'll check again once we
     know the tag length, but we at least want to know that it is
     a positive value */
  if (*pkt_octet_len < octets_in_rtcp_header + sizeof(srtcp_trailer_t))
    return err_status_bad_param;

  /*
   * look up ssrc in srtp_stream list, and process the packet with 
   * the appropriate stream.  if we haven't seen this stream before,
   * there's only one key for this srtp_session, and the cipher
   * supports key-sharing, then we assume that a new stream using
   * that key has just started up
   */
  stream = srtp_get_stream(ctx, hdr->ssrc);
  if (stream == NULL) {
    if (ctx->stream_template != NULL) {
      stream = ctx->stream_template;

      /* 
       * check to see if stream_template has an EKT data structure, in
       * which case we initialize the template using the EKT policy
       * referenced by that data (which consists of decrypting the
       * master key from the EKT field)
       *
       * this function initializes a *provisional* stream, and this
       * stream should not be accepted until and unless the packet
       * passes its authentication check
       */ 
      if (stream->ekt != NULL) {
	status = srtp_stream_init_from_ekt(stream, srtcp_hdr, *pkt_octet_len);
	if (status)
	  return status;
      }

      debug_print(mod_srtp, "srtcp using provisional stream (SSRC: 0x%08x)", 
		  hdr->ssrc);
    } else {
      /* no template stream, so we return an error */
      return err_status_no_ctx;
    } 
  }
  
  /* get tag length from stream context */
  tag_len = auth_get_tag_length(stream->rtcp_auth);

  /* check the packet length - it must contain at least a full RTCP
     header, an auth tag (if applicable), and the SRTCP encrypted flag
     and 31-bit index value */
  if (*pkt_octet_len < (int) (octets_in_rtcp_header + tag_len + sizeof(srtcp_trailer_t))) {
    return err_status_bad_param;
  }

  /*
   * Check if this is an AEAD stream (GCM mode).  If so, then dispatch
   * the request to our AEAD handler.
   */
  if (stream->rtp_cipher->algorithm == AES_128_GCM ||
      stream->rtp_cipher->algorithm == AES_256_GCM) {
      return srtp_unprotect_rtcp_aead(ctx, stream, srtcp_hdr, (unsigned int*)pkt_octet_len);
  }

  sec_serv_confidentiality = stream->rtcp_services == sec_serv_conf ||
      stream->rtcp_services == sec_serv_conf_and_auth;

  /*
   * set encryption start, encryption length, and trailer
   */
  enc_octet_len = *pkt_octet_len - 
                  (octets_in_rtcp_header + tag_len + sizeof(srtcp_trailer_t));
  /* index & E (encryption) bit follow normal data.  hdr->len
	 is the number of words (32-bit) in the normal packet minus 1 */
  /* This should point trailer to the word past the end of the
	 normal data. */
  /* This would need to be modified for optional mikey data */
  /*
   * NOTE: trailer is 32-bit aligned because RTCP 'packets' are always
   *	 multiples of 32-bits (RFC 3550 6.1)
   */
  trailer = (uint32_t *) ((char *) hdr +
      *pkt_octet_len -(tag_len + sizeof(srtcp_trailer_t)));
  e_bit_in_packet =
      (*((unsigned char *) trailer) & SRTCP_E_BYTE_BIT) == SRTCP_E_BYTE_BIT;
  if (e_bit_in_packet != sec_serv_confidentiality) {
    return err_status_cant_check;
  }
  if (sec_serv_confidentiality) {
    enc_start = (uint32_t *)hdr + uint32s_in_rtcp_header;  
  } else {
    enc_octet_len = 0;
    enc_start = NULL; /* this indicates that there's no encryption */
  }

  /* 
   * set the auth_start and auth_tag pointers to the proper locations
   * (note that srtcp *always* uses authentication, unlike srtp)
   */
  auth_start = (uint32_t *)hdr;
  auth_len = *pkt_octet_len - tag_len;
  auth_tag = (uint8_t *)hdr + auth_len;

  /* 
   * if EKT is in use, then we make a copy of the tag from the packet,
   * and then zeroize the location of the base tag
   *
   * we first re-position the auth_tag pointer so that it points to
   * the base tag
   */
  if (stream->ekt) {
    auth_tag -= ekt_octets_after_base_tag(stream->ekt);
    memcpy(tag_copy, auth_tag, tag_len);
    octet_string_set_to_zero(auth_tag, tag_len);
    auth_tag = tag_copy;
    auth_len += tag_len;
  }

  /* 
   * check the sequence number for replays
   */
  /* this is easier than dealing with bitfield access */
  seq_num = ntohl(*trailer) & SRTCP_INDEX_MASK;
  debug_print(mod_srtp, "srtcp index: %x", seq_num);
  status = rdb_check(&stream->rtcp_rdb, seq_num);
  if (status)
    return status;

  /* 
   * if we're using aes counter mode, set nonce and seq 
   */
  if (stream->rtcp_cipher->type->id == AES_ICM) {
    v128_t iv;

    iv.v32[0] = 0;
    iv.v32[1] = hdr->ssrc; /* still in network order! */
    iv.v32[2] = htonl(seq_num >> 16);
    iv.v32[3] = htonl(seq_num << 16);
    status = cipher_set_iv(stream->rtcp_cipher, &iv, direction_decrypt);

  } else {  
    v128_t iv;
    
    /* otherwise, just set the index to seq_num */  
    iv.v32[0] = 0;
    iv.v32[1] = 0;
    iv.v32[2] = 0;
    iv.v32[3] = htonl(seq_num);
    status = cipher_set_iv(stream->rtcp_cipher, &iv, direction_decrypt);

  }
  if (status)
    return err_status_cipher_fail;

  /* initialize auth func context */
  auth_start(stream->rtcp_auth);

  /* run auth func over packet, put result into tmp_tag */
  status = auth_compute(stream->rtcp_auth, (uint8_t *)auth_start,  
			auth_len, tmp_tag);
  debug_print(mod_srtp, "srtcp computed tag:       %s", 
	      octet_string_hex_string(tmp_tag, tag_len));
  if (status)
    return err_status_auth_fail;   
  
  /* compare the tag just computed with the one in the packet */
  debug_print(mod_srtp, "srtcp tag from packet:    %s", 
	      octet_string_hex_string(auth_tag, tag_len));  
  if (octet_string_is_eq(tmp_tag, auth_tag, tag_len))
    return err_status_auth_fail;

  /* 
   * if we're authenticating using a universal hash, put the keystream
   * prefix into the authentication tag
   */
  prefix_len = auth_get_prefix_length(stream->rtcp_auth);    
  if (prefix_len) {
    status = cipher_output(stream->rtcp_cipher, auth_tag, prefix_len);
    debug_print(mod_srtp, "keystream prefix: %s", 
		octet_string_hex_string(auth_tag, prefix_len));
    if (status)
      return err_status_cipher_fail;
  }

  /* if we're decrypting, exor keystream into the message */
  if (enc_start) {
    status = cipher_decrypt(stream->rtcp_cipher, 
			    (uint8_t *)enc_start, &enc_octet_len);
    if (status)
      return err_status_cipher_fail;
  }

  /* decrease the packet length by the length of the auth tag and seq_num */
  *pkt_octet_len -= (tag_len + sizeof(srtcp_trailer_t));

  /*
   * if EKT is in effect, subtract the EKT data out of the packet
   * length
   */
  *pkt_octet_len -= ekt_octets_after_base_tag(stream->ekt);

  /* 
   * verify that stream is for received traffic - this check will
   * detect SSRC collisions, since a stream that appears in both
   * srtp_protect() and srtp_unprotect() will fail this test in one of
   * those functions.
   *
   * we do this check *after* the authentication check, so that the
   * latter check will catch any attempts to fool us into thinking
   * that we've got a collision
   */
  if (stream->direction != dir_srtp_receiver) {
    if (stream->direction == dir_unknown) {
      stream->direction = dir_srtp_receiver;
    } else {
      srtp_handle_event(ctx, stream, event_ssrc_collision);
    }
  }

  /* 
   * if the stream is a 'provisional' one, in which the template context
   * is used, then we need to allocate a new stream at this point, since
   * the authentication passed
   */
  if (stream == ctx->stream_template) {  
    srtp_stream_ctx_t *new_stream;

    /* 
     * allocate and initialize a new stream 
     * 
     * note that we indicate failure if we can't allocate the new
     * stream, and some implementations will want to not return
     * failure here
     */
    status = srtp_stream_clone(ctx->stream_template, hdr->ssrc, &new_stream); 
    if (status)
      return status;
    
    /* add new stream to the head of the stream_list */
    new_stream->next = ctx->stream_list;
    ctx->stream_list = new_stream;
    
    /* set stream (the pointer used in this function) */
    stream = new_stream;
  }

  /* we've passed the authentication check, so add seq_num to the rdb */
  rdb_add_index(&stream->rtcp_rdb, seq_num);
    
    
  return err_status_ok;  
}