/* Copyright (C) 2004-2006 the Minisip Team This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* Copyright (C) 2004-2012 * * Authors: Israel Abad * Erik Eliasson * Johan Bilien * Joachim Orrblad * Werner Dittmann */ #include #include #include #include #include #include #include CryptoContextCtrl::CryptoContextCtrl(uint32_t ssrc, const int32_t ealg, const int32_t aalg, uint8_t* master_key, int32_t master_key_length, uint8_t* master_salt, int32_t master_salt_length, int32_t ekeyl, int32_t akeyl, int32_t skeyl, int32_t tagLength): ssrcCtx(ssrc),using_mki(false),mkiLength(0),mki(NULL), replay_window(0), macCtx(NULL), cipher(NULL), f8Cipher(NULL) { this->ealg = ealg; this->aalg = aalg; this->ekeyl = ekeyl; this->akeyl = akeyl; this->skeyl = skeyl; this->master_key_length = master_key_length; this->master_key = new uint8_t[master_key_length]; memcpy(this->master_key, master_key, master_key_length); this->master_salt_length = master_salt_length; this->master_salt = new uint8_t[master_salt_length]; memcpy(this->master_salt, master_salt, master_salt_length); switch (ealg) { case SrtpEncryptionNull: n_e = 0; k_e = NULL; n_s = 0; k_s = NULL; break; case SrtpEncryptionTWOF8: f8Cipher = new SrtpSymCrypto(SrtpEncryptionTWOF8); case SrtpEncryptionTWOCM: n_e = ekeyl; k_e = new uint8_t[n_e]; n_s = skeyl; k_s = new uint8_t[n_s]; cipher = new SrtpSymCrypto(SrtpEncryptionTWOCM); break; case SrtpEncryptionAESF8: f8Cipher = new SrtpSymCrypto(SrtpEncryptionAESF8); case SrtpEncryptionAESCM: n_e = ekeyl; k_e = new uint8_t[n_e]; n_s = skeyl; k_s = new uint8_t[n_s]; cipher = new SrtpSymCrypto(SrtpEncryptionAESCM); break; } switch (aalg) { case SrtpAuthenticationNull: n_a = 0; k_a = NULL; this->tagLength = 0; break; case SrtpAuthenticationSha1Hmac: case SrtpAuthenticationSkeinHmac: n_a = akeyl; k_a = new uint8_t[n_a]; this->tagLength = tagLength; break; } } CryptoContextCtrl::~CryptoContextCtrl(){ if (mki) delete [] mki; if (master_key_length > 0) { memset(master_key, 0, master_key_length); master_key_length = 0; delete [] master_key; } if (master_salt_length > 0) { memset(master_salt, 0, master_salt_length); master_salt_length = 0; delete [] master_salt; } if (n_e > 0) { memset(k_e, 0, n_e); n_e = 0; delete [] k_e; } if (n_s > 0) { memset(k_s, 0, n_s); n_s = 0; delete [] k_s; } if (n_a > 0) { n_a = 0; memset(k_a, 0, n_a); delete [] k_a; } if (cipher != NULL) { delete cipher; cipher = NULL; } if (f8Cipher != NULL) { delete f8Cipher; f8Cipher = NULL; } if (macCtx != NULL) { switch(aalg) { case SrtpAuthenticationSha1Hmac: freeSha1HmacContext(macCtx); break; case SrtpAuthenticationSkeinHmac: freeSkeinMacContext(macCtx); break; } } ealg = SrtpEncryptionNull; aalg = SrtpAuthenticationNull; } void CryptoContextCtrl::srtcpEncrypt( uint8_t* rtp, int32_t len, uint64_t index, uint32_t ssrc ) { if (ealg == SrtpEncryptionNull) { return; } if (ealg == SrtpEncryptionAESCM || ealg == SrtpEncryptionTWOCM) { /* Compute the CM IV (refer to chapter 4.1.1 in RFC 3711): * * k_s XX XX XX XX XX XX XX XX XX XX XX XX XX XX * SSRC XX XX XX XX * index XX XX XX XX * ------------------------------------------------------XOR * IV XX XX XX XX XX XX XX XX XX XX XX XX XX XX 00 00 * 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 */ unsigned char iv[16]; iv[0] = k_s[0]; iv[1] = k_s[1]; iv[2] = k_s[2]; iv[3] = k_s[3]; // The shifts transform the ssrc and index into network order iv[4] = ((ssrc >> 24) & 0xff) ^ k_s[4]; iv[5] = ((ssrc >> 16) & 0xff) ^ k_s[5]; iv[6] = ((ssrc >> 8) & 0xff) ^ k_s[6]; iv[7] = (ssrc & 0xff) ^ k_s[7]; iv[8] = k_s[8]; iv[9] = k_s[9]; iv[10] = ((index >> 24) & 0xff) ^ k_s[10]; iv[11] = ((index >> 16) & 0xff) ^ k_s[11]; iv[12] = ((index >> 8) & 0xff) ^ k_s[12]; iv[13] = (index & 0xff) ^ k_s[13]; iv[14] = iv[15] = 0; cipher->ctr_encrypt(rtp, len, iv); } if (ealg == SrtpEncryptionAESF8 || ealg == SrtpEncryptionTWOF8) { unsigned char iv[16]; // 4 bytes of the iv are zero // the first byte of the RTP header is not used. iv[0] = 0; iv[1] = 0; iv[2] = 0; iv[3] = 0; // Need the encryption flag index = index | 0x80000000; // set the index and the encrypt flag in network order into IV iv[4] = index >> 24; iv[5] = index >> 16; iv[6] = index >> 8; iv[7] = index; // The fixed header follows and fills the rest of the IV memcpy(iv+8, rtp, 8); cipher->f8_encrypt(rtp, len, iv, f8Cipher); } } /* Warning: tag must have been initialized */ void CryptoContextCtrl::srtcpAuthenticate(uint8_t* rtp, int32_t len, uint32_t index, uint8_t* tag ) { if (aalg == SrtpAuthenticationNull) { return; } int32_t macL; unsigned char temp[20]; const unsigned char* chunks[3]; unsigned int chunkLength[3]; uint32_t beIndex = htonl(index); chunks[0] = rtp; chunkLength[0] = len; chunks[1] = (unsigned char *)&beIndex; chunkLength[1] = 4; chunks[2] = NULL; switch (aalg) { case SrtpAuthenticationSha1Hmac: hmacSha1Ctx(macCtx, chunks, // data chunks to hash chunkLength, // length of the data to hash temp, &macL); /* truncate the result */ memcpy(tag, temp, getTagLength()); break; case SrtpAuthenticationSkeinHmac: macSkeinCtx(macCtx, chunks, // data chunks to hash chunkLength, // length of the data to hash temp); /* truncate the result */ memcpy(tag, temp, getTagLength()); break; } } /* used by the key derivation method */ static void computeIv(unsigned char* iv, uint8_t label, uint8_t* master_salt) { //printf( "Key_ID: %llx\n", key_id ); /* compute the IV key_id: XX XX XX XX XX XX XX master_salt: XX XX XX XX XX XX XX XX XX XX XX XX XX XX ------------------------------------------------------------ XOR IV: XX XX XX XX XX XX XX XX XX XX XX XX XX XX 00 00 */ memcpy(iv, master_salt, 14); iv[7] ^= label; iv[14] = iv[15] = 0; } /* Derives the srtp session keys from the master key */ void CryptoContextCtrl::deriveSrtcpKeys() { uint8_t iv[16]; // prepare AES cipher to compute derived keys. cipher->setNewKey(master_key, master_key_length); memset(master_key, 0, master_key_length); // compute the session encryption key uint8_t label = 3; computeIv(iv, label, master_salt); cipher->get_ctr_cipher_stream(k_e, n_e, iv); // compute the session authentication key label = 4; computeIv(iv, label, master_salt); cipher->get_ctr_cipher_stream(k_a, n_a, iv); // Initialize MAC context with the derived key switch (aalg) { case SrtpAuthenticationSha1Hmac: macCtx = createSha1HmacContext(k_a, n_a); break; case SrtpAuthenticationSkeinHmac: // Skein MAC uses number of bits as MAC size, not just bytes macCtx = createSkeinMacContext(k_a, n_a, tagLength*8, Skein512); break; } memset(k_a, 0, n_a); // compute the session salt label = 5; computeIv(iv, label, master_salt); cipher->get_ctr_cipher_stream(k_s, n_s, iv); memset(master_salt, 0, master_salt_length); // as last step prepare AES cipher with derived key. cipher->setNewKey(k_e, n_e); if (f8Cipher != NULL) cipher->f8_deriveForIV(f8Cipher, k_e, n_e, k_s, n_s); memset(k_e, 0, n_e); } bool CryptoContextCtrl::checkReplay( uint32_t index ) { if ( aalg == SrtpAuthenticationNull && ealg == SrtpEncryptionNull ) { /* No security policy, don't use the replay protection */ return true; } int64_t delta = s_l - index; if (delta > 0) { /* Packet not yet received*/ return true; } else { if( -delta > REPLAY_WINDOW_SIZE ) { /* Packet too old */ return false; } else { if((replay_window >> (-delta)) & 0x1) { /* Packet already received ! */ return false; } else { /* Packet not yet received */ return true; } } } } void CryptoContextCtrl::update(uint32_t index) { int64_t delta = index - s_l; /* update the replay bitmask */ if( delta > 0 ){ replay_window = replay_window << delta; replay_window |= 1; } else { replay_window |= ( 1 << delta ); } s_l = index; } CryptoContextCtrl* CryptoContextCtrl::newCryptoContextForSSRC(uint32_t ssrc) { CryptoContextCtrl* pcc = new CryptoContextCtrl( ssrc, this->ealg, // encryption algo this->aalg, // authentication algo this->master_key, // Master Key this->master_key_length, // Master Key length this->master_salt, // Master Salt this->master_salt_length, // Master Salt length this->ekeyl, // encryption keyl this->akeyl, // authentication key len this->skeyl, // session salt len this->tagLength); // authentication tag len return pcc; } /** EMACS ** * Local variables: * mode: c++ * c-default-style: ellemtel * c-basic-offset: 4 * End: */