summaryrefslogtreecommitdiff
path: root/srtp/CryptoContext.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'srtp/CryptoContext.cpp')
-rw-r--r--srtp/CryptoContext.cpp465
1 files changed, 465 insertions, 0 deletions
diff --git a/srtp/CryptoContext.cpp b/srtp/CryptoContext.cpp
new file mode 100644
index 0000000..d486f86
--- /dev/null
+++ b/srtp/CryptoContext.cpp
@@ -0,0 +1,465 @@
+/*
+ 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 <i_abad@terra.es>
+ * Erik Eliasson <eliasson@it.kth.se>
+ * Johan Bilien <jobi@via.ecp.fr>
+ * Joachim Orrblad <joachim@orrblad.com>
+ * Werner Dittmann <Werner.Dittmann@t-online.de>
+ */
+
+#include <string.h>
+#include <arpa/inet.h>
+#include <stdio.h>
+
+#include <CryptoContext.h>
+#include <crypto/hmac.h>
+#include <crypto/macSkein.h>
+
+CryptoContext::CryptoContext( uint32_t ssrc,
+ int32_t roc,
+ int64_t key_deriv_rate,
+ 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),
+ roc(roc),guessed_roc(0),s_l(0),key_deriv_rate(key_deriv_rate),
+ replay_window(0),
+ master_key_srtp_use_nb(0), master_key_srtcp_use_nb(0), seqNumSet(false),
+ 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;
+ }
+}
+
+CryptoContext::~CryptoContext() {
+
+ 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) {
+ memset(k_a, 0, n_a);
+ n_a = 0;
+ 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 CryptoContext::srtpEncrypt(uint8_t* pkt, uint8_t* payload, uint32_t paylen, 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 XX XX
+ * ------------------------------------------------------XOR
+ * IV XX XX XX XX XX XX XX XX XX XX XX XX XX XX 00 00
+ */
+
+ unsigned char iv[16];
+ memcpy(iv, k_s, 4);
+
+ int i;
+ for (i = 4; i < 8; i++ ) {
+ iv[i] = (0xFF & (ssrc >> ((7-i)*8))) ^ k_s[i];
+ }
+ for (i = 8; i < 14; i++ ) {
+ iv[i] = (0xFF & (unsigned char)( index >> ((13-i)*8) ) ) ^ k_s[i];
+ }
+ iv[14] = iv[15] = 0;
+
+ cipher->ctr_encrypt(payload, paylen, iv);
+ }
+
+ if (ealg == SrtpEncryptionAESF8 || ealg == SrtpEncryptionTWOF8) {
+
+ /* Create the F8 IV (refer to chapter 4.1.2.2 in RFC 3711):
+ *
+ * IV = 0x00 || M || PT || SEQ || TS || SSRC || ROC
+ * 8Bit 1bit 7bit 16bit 32bit 32bit 32bit
+ * ------------\ /--------------------------------------------------
+ * XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX
+ */
+
+ unsigned char iv[16];
+ uint32_t *ui32p = (uint32_t *)iv;
+
+ memcpy(iv, pkt, 12);
+ iv[0] = 0;
+
+ // set ROC in network order into IV
+ ui32p[3] = htonl(roc);
+
+ cipher->f8_encrypt(payload, paylen, iv, f8Cipher);
+ }
+}
+
+/* Warning: tag must have been initialized */
+void CryptoContext::srtpAuthenticate(uint8_t* pkt, uint32_t pktlen, uint32_t roc, 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 beRoc = htonl(roc);
+
+ chunks[0] = pkt;
+ chunkLength[0] = pktlen;
+
+ chunks[1] = (unsigned char *)&beRoc;
+ 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, uint64_t label, uint64_t index,
+ int64_t kdv, unsigned char* master_salt)
+{
+
+ uint64_t key_id;
+
+ if (kdv == 0) {
+ key_id = label << 48;
+ }
+ else {
+ key_id = ((label << 48) | (index / kdv));
+ }
+
+ //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
+ */
+
+ int i;
+ for (i = 0; i < 7 ; i++ ) {
+ iv[i] = master_salt[i];
+ }
+
+ for (i = 7; i < 14 ; i++ ) {
+ iv[i] = (unsigned char)(0xFF & (key_id >> (8*(13-i)))) ^
+ master_salt[i];
+ }
+
+ iv[14] = iv[15] = 0;
+}
+
+/* Derive the srtp session keys from the master key */
+void CryptoContext::deriveSrtpKeys(uint64_t index)
+{
+ 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
+ uint64_t label = 0;
+ computeIv(iv, label, index, key_deriv_rate, master_salt);
+ cipher->get_ctr_cipher_stream(k_e, n_e, iv);
+
+ // compute the session authentication key
+ label = 0x01;
+ computeIv(iv, label, index, key_deriv_rate, 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 = 0x02;
+ computeIv(iv, label, index, key_deriv_rate, 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);
+}
+
+/* Based on the algorithm provided in Appendix A - draft-ietf-srtp-05.txt */
+uint64_t CryptoContext::guessIndex(uint16_t new_seq_nb )
+{
+ /*
+ * Initialize the sequences number on first call that uses the
+ * sequence number. Either GuessIndex() or checkReplay().
+ */
+ if (!seqNumSet) {
+ seqNumSet = true;
+ s_l = new_seq_nb;
+ }
+ if (s_l < 32768) {
+ if (new_seq_nb - s_l > 32768) {
+ guessed_roc = roc - 1;
+ }
+ else {
+ guessed_roc = roc;
+ }
+ }
+ else {
+ if (s_l - 32768 > new_seq_nb) {
+ guessed_roc = roc + 1;
+ }
+ else {
+ guessed_roc = roc;
+ }
+ }
+
+ return ((uint64_t)guessed_roc) << 16 | new_seq_nb;
+}
+
+bool CryptoContext::checkReplay( uint16_t new_seq_nb )
+{
+ if ( aalg == SrtpAuthenticationNull && ealg == SrtpEncryptionNull ) {
+ /* No security policy, don't use the replay protection */
+ return true;
+ }
+
+ /*
+ * Initialize the sequences number on first call that uses the
+ * sequence number. Either guessIndex() or checkReplay().
+ */
+ if (!seqNumSet) {
+ seqNumSet = true;
+ s_l = new_seq_nb;
+ }
+ uint64_t guessed_index = guessIndex( new_seq_nb );
+ uint64_t local_index = (((uint64_t)roc) << 16) | s_l;
+
+ int64_t delta = guessed_index - local_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 CryptoContext::update(uint16_t new_seq_nb)
+{
+ int64_t delta = guessIndex(new_seq_nb) - (((uint64_t)roc) << 16 | s_l );
+
+ /* update the replay bitmask */
+ if ( delta > 0 ) {
+ replay_window = replay_window << delta;
+ replay_window |= 1;
+ }
+ else {
+ replay_window |= ( 1 << delta );
+ }
+
+ /* update the locally stored ROC and highest sequence number */
+ if ( new_seq_nb > s_l ) {
+ s_l = new_seq_nb;
+ }
+ if ( guessed_roc > roc ) {
+ roc = guessed_roc;
+ s_l = new_seq_nb;
+ }
+}
+
+CryptoContext* CryptoContext::newCryptoContextForSSRC(uint32_t ssrc, int roc, int64_t keyDerivRate)
+{
+ CryptoContext* pcc = new CryptoContext(
+ ssrc,
+ roc, // Roll over Counter,
+ keyDerivRate, // keyderivation << 48,
+ 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:
+ */
+