Chinese Yellow Pages | Classifieds | Knowledge | Tax | IME

SRTP RFCs and h.235 is Not an easy read.

Here is my very high level beriefing:

AES Encryption

(1) AES is the foundation for both srtp and h.235.6 secured rtp

AES is symmetric encryption,  it takes those two as input

  • key + IV:  128/192/256-bits length key , ( + optional Initialized vector or counter)
  • src input:    a  fixed size of 16 bytes source data as an input,

the output

  • 16 bytes encrypted data

There are several modes:

Summary of Modes
Mode Formulas Ciphertext
ECB Yi=F(PlainTexti,Key) Yi
CBC Yi=PlainTexti XOR Ciphertexti-1 F(Y,key); Ciphertext0=IV
PCBC Yi=PlainTexti XOR (Ciphertexti-1 XOR PlainTexti-1) F(Y,key);Ciphertext0=IV
CFB Yi=Ciphertexti-1 Plaintext XOR F(Y,key);Ciphertext0=IV
OFB Yi=F(Key,Ii-1);Y0=IV Plaintext XOR Yi
CTR Yi=F(Key,IV + g(i) );IV=token(); Plaintext XOR Yi

Note: g(i) is any deterministic function, often the identity function.

srtp use COUNTER mode, while h.235.6 use CBC or EOFB.

 

SRTP and SDP

srtp most likely will be used with sdp.   h.235.8 defined srtp in h.323, but not widely implemeneted in the h.323 world.

  • normally we use sdp  SDP security description ( crypto attribe)  to pass the master key/salt to remote side
e.g: a=crypto:1 AES_CM_128_HMAC_SHA1_80
      inline:PS1uQCVeeCFCanVmcjkpPywjNWhcYD0mXXtxaVBR|2^20

AES_CM_128_HMAC_SHA1_80: the encryption/auth methon is: 
PS1uQCVeeCFCanVmcjkpPywjNWhcYD0mXXtxaVBR: is base64(key + salt )
2^20 is master key lifetime


  • The master key/salt then is used to key generate function to get session key, session salt, session auth key for each srtp/rtcp stream.

The key generate function in SRTP is actually AES-CM ( with different salt input, master key) , then truncation first 128 or 192, 256 bits as the output as the session key/salt/auth key.

see more details at: https://tools.ietf.org/html/rfc3711#appendix-B.2

  • Once we get those session key/salt/auth, the key stream ( in case AES counter mode) is like this:
 E(k, IV) || E(k, IV + 1 mod 2^128) || E(k, IV + 2 mod 2^128) ...
 where IV = (k_s * 2^16) XOR (SSRC * 2^64) XOR (i * 2^16)

k_s is the session salt, ssrc is the rtp’s ssrc

we then xor our rtp data with the keystream to get the encrypted data.

 

h.235.6 

  • h.235.6 uses D-H procedure in h.225 setup/connect msg to establish the master key,
  • then the master endpoint will generate a random session key, encrypted by the master key, pass to remote h.323 endpoint using h.245 OLC. then use AES-CBC mode to encrypt the rtp data
  • the remote side who already know the master key through D-H procedure can to decrypt the session key, then use the session key to decrypt rtp data

libsrtp

libsrtp is an open source srtp implementation. The beauty of it is that we can use it without really knowing the details of the srtp.

we basically

  • pass a key to a policy which defined a specific encryption/auth algothym and key lenght ,
  • create a session out of it,
  • srtp_protect or srtp_unprotect for rtp data ( with header),
  • srtp_protect_rtcp or srtp_unprotect_rtcp for rctp data (wither header),
  • FAQ: the SRTP sender should randomly select an initial sequence number that is always less than 215,  This ensures correct SRTP operation so long as fewer than 215 initial packets are lost in succession, which is within the maximum tolerance of SRTP packet-index determination

 

#include <string>

#include <arpa/inet.h>

#include "srtp.h"
#include <stdio.h> 
#include <string.h> 

#define RTP_MAX_BUF_LEN 16384
#define RTP_HEADER_LEN 12

typedef struct {
 unsigned char cc : 4; /* CSRC count */
 unsigned char x : 1; /* header extension flag */
 unsigned char p : 1; /* padding flag */
 unsigned char version : 2; /* protocol version */
 unsigned char pt : 7; /* payload type */
 unsigned char m : 1; /* marker bit */
 uint16_t seq; /* sequence number */
 uint32_t ts; /* timestamp */
 uint32_t ssrc; /* synchronization source */
} srtp_hdr_t;



typedef srtp_hdr_t rtp_hdr_t;

typedef struct {
 srtp_hdr_t header;
 char body[RTP_MAX_BUF_LEN];
} rtp_msg_t;



int main()
{

 srtp_init();

 srtp_t session;
 srtp_policy_t policy;


 memset(&policy, 0x0, sizeof(srtp_policy_t));

 srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&policy.rtp);
 srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&policy.rtcp);
//srtp_crypto_policy_set_rtp_default(&policy.rtp);
//srtp_crypto_policy_set_rtcp_default(&policy.rtcp);
 policy.ssrc.type = ssrc_any_outbound;


 // key should include key and salt ( base64 decoded )
 //policy.key = Base64::decode(sdp_crypto.key_salt_);
 //uint8_t* master_key_and_salt = new uint8_t[SRTP_MAX_KEY_LEN];

 //std::string key_salt_{"DsT+Z+PoSAEIlUMpaaxr6OV+5zKo9kHzSYXj3qLQ"} ;
 std::string key_salt_{"EIlUMpaaxr6OV+5zKo9kHzSYXj3qLQ"} ;
 unsigned char* master_key_and_salt = new unsigned char[SRTP_MAX_KEY_LEN];
 //memcpy( master_key_and_salt, Base64::decode(key_salt_).data(), SRTP_MAX_KEY_LEN);
 memcpy( master_key_and_salt, key_salt_.data(), 30);
/*
 unsigned char* master_key_and_salt = new unsigned char[SRTP_MAX_KEY_LEN];

 int pad;
 int expected_len = (policy.rtp.cipher_key_len * 4) / 3;
 char* input_key = new char[SRTP_MAX_KEY_LEN];
 //char* input_key = (char*)sdp_crypto.key_salt_.c_str();
 memcpy( input_key, sdp_crypto.key_salt_.c_str(), SRTP_MAX_KEY_LEN);
 //int len = base64_string_to_octet_string(master_key_and_salt, &pad, (char*) (sdp_crypto.key_salt_.c_str()),
 int len = base64_string_to_octet_string( (char*) master_key_and_salt, &pad, input_key,
 expected_len);
 if (pad != 0) {
 util::log(util::loc(), "error: padding in base64 unexpected ");
 // should exit? this
 }
*/

 policy.key = master_key_and_salt;
 policy.next = NULL;
 srtp_err_status_t status = srtp_create(&session, &policy);
 //srtp_create(&session, &policy);
 if( status ) {
 printf( "error: srtp_create() failed with code: %d", status);
 return 0;
 } 

 printf( "create ok");

 #define buf_size 2048
 char rtp_buffer[buf_size];
 int len;

 memset(rtp_buffer, 0x0, buf_size );

 rtp_msg_t* rtp_ptr;
 rtp_ptr = ( rtp_msg_t* ) rtp_buffer;

 /* update header */
 rtp_ptr->header.seq = ntohs(rtp_ptr->header.seq) + 1;
 rtp_ptr->header.seq = htons(rtp_ptr->header.seq);
 rtp_ptr->header.ts = ntohl(rtp_ptr->header.ts) + 1;
 rtp_ptr->header.ts = htonl(rtp_ptr->header.ts);

 /* marshal data */
 len = 4;
 strncpy(rtp_ptr->body, "test", len);

 int pkt_len = len + RTP_HEADER_LEN ;

 printf( "\n before encryption, %d \n", pkt_len);
 for( int i =0 ; i< pkt_len ; ++i ) {
 printf("%x", rtp_buffer[i]);
 }


 rtp_buffer[0] = 'A'; 
 status = srtp_protect(session, rtp_buffer, &pkt_len);
 if( status ) {
 printf( "error: srtp_protect failed with code: %d\n", status);
 } else {
 printf( " srtp_protect OK: %d, pkt_len :%d \n", status, pkt_len);
 }

 printf( "\n after encryption \n");
 for( int i =0 ; i< pkt_len ; ++i ) {
 printf("%x", rtp_buffer[i]);
 }

 printf( "\n try to decrypt: %d \n", pkt_len);

 //decryp
 srtp_t session_recv;
 srtp_policy_t policy_recv;

 policy_recv = policy;
 policy_recv.ssrc.type = ssrc_any_inbound;

 status = srtp_create(&session_recv, &policy_recv);
 //srtp_create(&session, &policy);
 if( status ) {
 printf( "error: recv srtp_create() failed with code: %d", status);
 return 0;
 } 

 int lenOut = pkt_len + 0;
 printf( "\n try to set decrypt leng: %d \n", lenOut);

 char rtp_buffer_out[buf_size];
 memcpy( rtp_buffer_out, rtp_buffer, buf_size);
 status = srtp_unprotect(session_recv, rtp_buffer_out, &lenOut);
 if( status ) {
 printf( "error: srtp_unprotect failed with code: %d \n", status);
 } else {
 printf( "OK: srtp_unprotect code: %d , lengout :%d \n", status, lenOut);

 printf( "\n after decryption \n");
 for( int i =0 ; i< lenOut ; ++i ) {
 printf("%x", rtp_buffer_out[i]);
 }


 }


 return 1;
}


 

References:

https://en.wikipedia.org/wiki/Advanced_Encryption_Standard

https://www.cisco.com/c/en/us/about/security-center/securing-voip.html

https://github.com/cisco/libsrtp

https://tools.ietf.org/html/rfc3711

https://tools.ietf.org/html/rfc4568

Leave a Reply

Your email address will not be published. Required fields are marked *