-
Tristan Matthews authoredTristan Matthews authored
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
audio_srtp_session.cpp 10.85 KiB
/*
* Copyright (C) 2004, 2005, 2006, 2008, 2009, 2010, 2011 Savoir-Faire Linux Inc.
* Author: Alexandre Savard <alexandre.savard@savoirfairelinux.com>
* Author: Pierre-Luc Bacon <pierre-luc.bacon@savoirfairelinux.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Additional permission under GNU GPL version 3 section 7:
*
* If you modify this program, or any covered work, by linking or
* combining it with the OpenSSL project's OpenSSL library (or a
* modified version of that library), containing parts covered by the
* terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
* grants you additional permission to convey the resulting work.
* Corresponding Source for a non-source form of such a combination
* shall include the source code for the parts of OpenSSL used as well
* as that of the covered work.
*/
#include "audio_srtp_session.h"
#include "logger.h"
#include "array_size.h"
#include <openssl/sha.h>
#include <openssl/hmac.h>
#include <openssl/evp.h>
#include <openssl/bio.h>
#include <openssl/buffer.h>
#include <openssl/rand.h>
#include <cstdio>
#include <cstring>
#include <cerrno>
namespace sfl {
namespace {
std::string
encodeBase64(unsigned char *input, int length)
{
// init decoder
BIO *b64 = BIO_new(BIO_f_base64());
BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
// init internal buffer
BIO *bmem = BIO_new(BIO_s_mem());
// create decoder chain
b64 = BIO_push(b64, bmem);
BIO_write(b64, input, length);
// BIO_flush (b64);
// get pointer to data
BUF_MEM *bptr = 0;
BIO_get_mem_ptr(b64, &bptr);
std::string output(bptr->data, bptr->length);
BIO_free_all(bmem);
return output;
}
std::vector<char> decodeBase64(unsigned char *input, int length)
{
BIO *b64, *bmem;
// init decoder and read-only BIO buffer
b64 = BIO_new(BIO_f_base64());
BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
// init internal buffer
bmem = BIO_new_mem_buf(input, length);
// create encoder chain
bmem = BIO_push(b64, bmem);
std::vector<char> buffer(length, 0);
BIO_read(bmem, &(*buffer.begin()), length);
BIO_free_all(bmem);
return buffer;
}
// Fills the array dest with length random bytes
void buffer_fill(unsigned char dest [], size_t length)
{
DEBUG("AudioSrtp: Init local master key");
// @TODO key may have different length depending on cipher suite
// Allocate memory for key
std::vector<unsigned char> random_key(length);
// Generate ryptographically strong pseudo-random bytes
if (RAND_bytes(&(*random_key.begin()), length) != 1)
DEBUG("Error occured while generating cryptographically strong pseudo-random key");
memcpy(dest, &(*random_key.begin()), length);
}
}
AudioSrtpSession::AudioSrtpSession(SIPCall &call) :
AudioSymmetricRtpSession(call),
remoteCryptoCtx_(0),
localCryptoCtx_(0),
localCryptoSuite_(0),
remoteCryptoSuite_(0),
localMasterKey_(),
localMasterKeyLength_(0),
localMasterSalt_(),
localMasterSaltLength_(0),
remoteMasterKey_(),
remoteMasterKeyLength_(0),
remoteMasterSalt_(),
remoteMasterSaltLength_(0),
remoteOfferIsSet_(false)
{}
ost::CryptoContext* AudioSrtpSession::initLocalCryptoInfo()
{
DEBUG("AudioSrtp: Set cryptographic info for this rtp session");
// Initialize local Crypto context
initializeLocalMasterKey();
initializeLocalMasterSalt();
initializeLocalCryptoContext();
// Set local crypto context in ccrtp
localCryptoCtx_->deriveSrtpKeys(0);
setOutQueueCryptoContext(localCryptoCtx_);
return localCryptoCtx_;
}
std::vector<std::string> AudioSrtpSession::getLocalCryptoInfo()
{
DEBUG("AudioSrtp: Get Cryptographic info from this rtp session");
std::vector<std::string> crypto_vector;
// @TODO we should return a vector containing supported
// cryptographic context tagged 1, 2, 3...
std::string tag = "1";
std::string crypto_suite = sfl::CryptoSuites[localCryptoSuite_].name;
// srtp keys formated as the following as the following
// inline:keyParameters|keylifetime|MasterKeyIdentifier
// inline:NzB4d1BINUAvLEw6UzF3WSJ+PSdFcGdUJShpX1Zj|2^20|1:32
std::string srtp_keys = "inline:";
srtp_keys += getBase64ConcatenatedKeys();
// srtp_keys.append("|2^20|1:32");
// generate crypto attribute
std::string crypto_attr = tag.append(" ");
crypto_attr += crypto_suite.append(" ");
crypto_attr += srtp_keys;
DEBUG("%s", crypto_attr.c_str());
crypto_vector.push_back(crypto_attr);
return crypto_vector;
}
ost::CryptoContext*
AudioSrtpSession::setRemoteCryptoInfo(sfl::SdesNegotiator& nego)
{
if (not remoteOfferIsSet_) {
DEBUG("%s", nego.getKeyInfo().c_str());
// Use second crypto suite if key length is 32 bit, default is 80;
if (nego.getAuthTagLength() == "32") {
localCryptoSuite_ = 1;
remoteCryptoSuite_ = 1;
}
// decode keys
unBase64ConcatenatedKeys(nego.getKeyInfo());
// init crypto content in Srtp session
initializeRemoteCryptoContext();
setInQueueCryptoContext(remoteCryptoCtx_);
remoteOfferIsSet_ = true;
}
return remoteCryptoCtx_;
}
void AudioSrtpSession::initializeLocalMasterKey()
{
DEBUG("AudioSrtp: Init local master key");
// @TODO key may have different length depending on cipher suite
localMasterKeyLength_ = sfl::CryptoSuites[localCryptoSuite_].masterKeyLength / 8;
assert(ARRAYSIZE(localMasterKey_) >= localMasterKeyLength_);
DEBUG("AudioSrtp: Local master key length %d", localMasterKeyLength_);
buffer_fill(localMasterKey_, localMasterKeyLength_);
}
void AudioSrtpSession::initializeLocalMasterSalt()
{
// @TODO key may have different length depending on cipher suite
localMasterSaltLength_ = sfl::CryptoSuites[localCryptoSuite_].masterSaltLength / 8;
assert(ARRAYSIZE(localMasterSalt_) >= localMasterSaltLength_);
DEBUG("AudioSrtp: Local master salt length %d", localMasterSaltLength_);
buffer_fill(localMasterSalt_, localMasterSaltLength_);
}
std::string AudioSrtpSession::getBase64ConcatenatedKeys()
{
DEBUG("AudioSrtp: Get base64 concatenated keys");
// compute concatenated master and salt length
int concatLength = localMasterKeyLength_ + localMasterSaltLength_;
uint8 concatKeys[concatLength];
DEBUG("AudioSrtp: Concatenated length %d", concatLength);
// concatenate keys
memcpy((void*) concatKeys, (void*) localMasterKey_, localMasterKeyLength_);
memcpy((void*)(concatKeys + localMasterKeyLength_), (void*) localMasterSalt_, localMasterSaltLength_);
// encode concatenated keys in base64
return encodeBase64((unsigned char*) concatKeys, concatLength);
}
void AudioSrtpSession::unBase64ConcatenatedKeys(std::string base64keys)
{
remoteMasterKeyLength_ = sfl::CryptoSuites[remoteCryptoSuite_].masterKeyLength / 8;
remoteMasterSaltLength_ = sfl::CryptoSuites[remoteCryptoSuite_].masterSaltLength / 8;
// pointer to binary data
char *dataptr = (char*) base64keys.data();
// decode concatenated binary keys
std::vector<char> output(decodeBase64((unsigned char*) dataptr, strlen(dataptr)));
// copy master and slt respectively
memcpy((void*) remoteMasterKey_, &(*output.begin()), remoteMasterKeyLength_);
memcpy((void*) remoteMasterSalt_, &(*output.begin()) + remoteMasterKeyLength_, remoteMasterSaltLength_);
}
void AudioSrtpSession::initializeRemoteCryptoContext()
{
DEBUG("AudioSrtp: Initialize remote crypto context");
const CryptoSuiteDefinition &crypto = sfl::CryptoSuites[remoteCryptoSuite_];
delete remoteCryptoCtx_;
remoteCryptoCtx_ = new ost::CryptoContext(0x0,
0, // roc,
0L, // keydr,
SrtpEncryptionAESCM,
SrtpAuthenticationSha1Hmac,
remoteMasterKey_,
remoteMasterKeyLength_,
remoteMasterSalt_,
remoteMasterSaltLength_,
crypto.encryptionKeyLength / 8,
crypto.srtpAuthKeyLength / 8,
crypto.masterSaltLength / 8,
crypto.srtpAuthTagLength / 8);
}
void AudioSrtpSession::initializeLocalCryptoContext()
{
DEBUG("AudioSrtp: Initialize local crypto context");
const CryptoSuiteDefinition &crypto = sfl::CryptoSuites[localCryptoSuite_];
delete localCryptoCtx_;
localCryptoCtx_ = new ost::CryptoContext(OutgoingDataQueue::getLocalSSRC(),
0, // roc,
0L, // keydr,
SrtpEncryptionAESCM,
SrtpAuthenticationSha1Hmac,
localMasterKey_,
localMasterKeyLength_,
localMasterSalt_,
localMasterSaltLength_,
crypto.encryptionKeyLength / 8,
crypto.srtpAuthKeyLength / 8,
crypto.masterSaltLength / 8,
crypto.srtpAuthTagLength / 8);
}
void AudioSrtpSession::restoreCryptoContext(ost::CryptoContext *localContext,
ost::CryptoContext *remoteContext)
{
if (remoteCryptoCtx_ != remoteContext) {
delete remoteCryptoCtx_;
remoteCryptoCtx_ = remoteContext;
}
if (localCryptoCtx_ != localContext) {
delete localCryptoCtx_;
localCryptoCtx_ = localContext;
}
setInQueueCryptoContext(remoteCryptoCtx_);
setOutQueueCryptoContext(localCryptoCtx_);
}
}