blob: 766deac38c1ed30c03cb7f56466eaad38c478905 [file] [log] [blame]
/*
Copyright (C) 2005, 2004, 2012 Erik Eliasson, Johan Bilien, Werner Dittmann
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
* In addition, as a special exception, the copyright holders give
* permission to link the code of portions of this program with the
* OpenSSL library under certain conditions as described in each
* individual source file, and distribute linked combinations
* including the two.
* You must obey the GNU General Public License in all respects
* for all of the code used other than OpenSSL. If you modify
* file(s) with this exception, you may extend this exception to your
* version of the file(s), but you are not obligated to do so. If you
* do not wish to do so, delete this exception statement from your
* version. If you delete this exception statement from all source
* files in the program, then also delete it here.
*/
/**
* @author Erik Eliasson <eliasson@it.kth.se>
* @author Johan Bilien <jobi@via.ecp.fr>
* @author Werner Dittmann <Werner.Dittmann@t-online.de>
*/
extern void initializeGcrypt();
#define MAKE_F8_TEST
#include <gcrypt.h> // the include of gcrypt
#include <stdlib.h>
#include <crypto/SrtpSymCrypto.h>
#include <crypto/twofish.h>
#include <stdio.h>
SrtpSymCrypto::SrtpSymCrypto(int algo) : key(NULL), algorithm(algo) {
initializeGcrypt();
}
SrtpSymCrypto::SrtpSymCrypto( uint8_t* k, int32_t keyLength, int algo) :
key(NULL), algorithm(algo) {
initializeGcrypt();
setNewKey(k, keyLength);
}
SrtpSymCrypto::~SrtpSymCrypto() {
if (key) {
if (algorithm == SrtpEncryptionAESCM || algorithm == SrtpEncryptionAESF8)
gcry_cipher_close(static_cast<gcry_cipher_hd_t>(key));
else if (algorithm == SrtpEncryptionTWOCM || algorithm == SrtpEncryptionTWOF8) {
memset(key, 0, sizeof(Twofish_key));
delete[] (uint8_t*)key;
}
key = NULL;
}
}
static int twoFishInit = 0;
bool SrtpSymCrypto::setNewKey(const uint8_t* k, int32_t keyLength) {
// release an existing key before setting a new one
if (algorithm == SrtpEncryptionAESCM || algorithm == SrtpEncryptionAESF8) {
if (key != NULL) {
gcry_cipher_close(static_cast<gcry_cipher_hd_t>(key));
key = NULL;
}
int algo = 0;
if (keyLength == 16) {
algo = GCRY_CIPHER_AES;
}
else if (keyLength == 32) {
algo = GCRY_CIPHER_AES256;
}
else {
return false;
}
gcry_cipher_hd_t tmp;
gcry_cipher_open(&tmp, algo, GCRY_CIPHER_MODE_ECB, 0);
key = tmp;
gcry_cipher_setkey(static_cast<gcry_cipher_hd_t>(key), k, keyLength);
}
else if (algorithm == SrtpEncryptionTWOCM || algorithm == SrtpEncryptionTWOF8) {
if (!twoFishInit) {
Twofish_initialise();
twoFishInit = 1;
}
if (key != NULL)
delete[] (uint8_t*)key;
key = new uint8_t[sizeof(Twofish_key)];
memset(key, 0, sizeof(Twofish_key));
Twofish_prepare_key((Twofish_Byte*)k, keyLength, (Twofish_key*)key);
}
else
return false;
return true;
}
void SrtpSymCrypto::encrypt(const uint8_t* input, uint8_t* output) {
if (key != NULL) {
if (algorithm == SrtpEncryptionAESCM || algorithm == SrtpEncryptionAESF8)
gcry_cipher_encrypt (static_cast<gcry_cipher_hd_t>(key),
output, SRTP_BLOCK_SIZE, input, SRTP_BLOCK_SIZE);
else if (algorithm == SrtpEncryptionTWOCM || algorithm == SrtpEncryptionTWOF8)
Twofish_encrypt((Twofish_key*)key, (Twofish_Byte*)input,
(Twofish_Byte*)output);
}
}
void SrtpSymCrypto::get_ctr_cipher_stream( uint8_t* output, uint32_t length,
uint8_t* iv ) {
uint16_t ctr = 0;
unsigned char temp[SRTP_BLOCK_SIZE];
for(ctr = 0; ctr < length/SRTP_BLOCK_SIZE; ctr++ ){
//compute the cipher stream
iv[14] = (uint8_t)((ctr & 0xFF00) >> 8);
iv[15] = (uint8_t)((ctr & 0x00FF));
encrypt(iv, &output[ctr*SRTP_BLOCK_SIZE]);
}
if ((length % SRTP_BLOCK_SIZE) > 0) {
// Treat the last bytes:
iv[14] = (uint8_t)((ctr & 0xFF00) >> 8);
iv[15] = (uint8_t)((ctr & 0x00FF));
encrypt(iv, temp);
memcpy(&output[ctr*SRTP_BLOCK_SIZE], temp, length % SRTP_BLOCK_SIZE);
}
}
void SrtpSymCrypto::ctr_encrypt( const uint8_t* input, uint32_t input_length,
uint8_t* output, uint8_t* iv ) {
if (key == NULL)
return;
uint16_t ctr = 0;
unsigned char temp[SRTP_BLOCK_SIZE];
int l = input_length/SRTP_BLOCK_SIZE;
for ( ctr = 0; ctr < l; ctr++ ) {
iv[14] = (uint8_t)((ctr & 0xFF00) >> 8);
iv[15] = (uint8_t)((ctr & 0x00FF));
encrypt(iv, temp);
for (int i = 0; i < SRTP_BLOCK_SIZE; i++ ) {
*output++ = temp[i] ^ *input++;
}
}
l = input_length % SRTP_BLOCK_SIZE;
if (l > 0) {
// Treat the last bytes:
iv[14] = (uint8_t)((ctr & 0xFF00) >> 8);
iv[15] = (uint8_t)((ctr & 0x00FF));
encrypt(iv, temp);
for (int i = 0; i < l; i++ ) {
*output++ = temp[i] ^ *input++;
}
}
}
void SrtpSymCrypto::ctr_encrypt( uint8_t* data, uint32_t data_length, uint8_t* iv ) {
if (key == NULL)
return;
uint16_t ctr = 0;
unsigned char temp[SRTP_BLOCK_SIZE];
int l = data_length/SRTP_BLOCK_SIZE;
for (ctr = 0; ctr < l; ctr++ ) {
iv[14] = (uint8_t)((ctr & 0xFF00) >> 8);
iv[15] = (uint8_t)((ctr & 0x00FF));
encrypt(iv, temp);
for (int i = 0; i < SRTP_BLOCK_SIZE; i++ ) {
*data++ ^= temp[i];
}
}
l = data_length % SRTP_BLOCK_SIZE;
if (l > 0) {
// Treat the last bytes:
iv[14] = (uint8_t)((ctr & 0xFF00) >> 8);
iv[15] = (uint8_t)((ctr & 0x00FF));
encrypt(iv, temp);
for (int i = 0; i < l; i++ ) {
*data++ ^= temp[i];
}
}
}
void SrtpSymCrypto::f8_encrypt(const uint8_t* data, uint32_t data_length, uint8_t* iv, SrtpSymCrypto* f8Cipher ) {
f8_encrypt(data, data_length, const_cast<uint8_t*>(data), iv, f8Cipher);
}
#define MAX_KEYLEN 32
void SrtpSymCrypto::f8_deriveForIV(SrtpSymCrypto* f8Cipher, uint8_t* key, int32_t keyLen,
uint8_t* salt, int32_t saltLen) {
unsigned char *cp_in, *cp_in1, *cp_out;
unsigned char maskedKey[MAX_KEYLEN];
unsigned char saltMask[MAX_KEYLEN];
if (keyLen > MAX_KEYLEN)
return;
if (saltLen > keyLen)
return;
/*
* First copy the salt into the mask field, then fill with 0x55 to
* get a full key.
*/
memcpy(saltMask, salt, saltLen);
memset(saltMask+saltLen, 0x55, keyLen-saltLen);
/*
* XOR the original key with the above created mask to
* get the special key.
*/
cp_out = maskedKey;
cp_in = key;
cp_in1 = saltMask;
for (int i = 0; i < keyLen; i++) {
*cp_out++ = *cp_in++ ^ *cp_in1++;
}
/*
* Prepare the a new AES cipher with the special key to compute IV'
*/
f8Cipher->setNewKey(maskedKey, keyLen);
}
void SrtpSymCrypto::f8_encrypt(const uint8_t* in, uint32_t in_length, uint8_t* out,
uint8_t* iv, SrtpSymCrypto* f8Cipher ) {
int offset = 0;
unsigned char ivAccent[SRTP_BLOCK_SIZE];
unsigned char S[SRTP_BLOCK_SIZE];
F8_CIPHER_CTX f8ctx;
if (key == NULL)
return;
/*
* Get memory for the derived IV (IV')
*/
f8ctx.ivAccent = ivAccent;
/*
* Use the derived IV encryption setup to encrypt the original IV to produce IV'.
*/
f8Cipher->encrypt(iv, f8ctx.ivAccent);
f8ctx.J = 0; // initialize the counter
f8ctx.S = S; // get the key stream buffer
memset(f8ctx.S, 0, SRTP_BLOCK_SIZE); // initial value for key stream
while (in_length >= SRTP_BLOCK_SIZE) {
processBlock(&f8ctx, in+offset, SRTP_BLOCK_SIZE, out+offset);
in_length -= SRTP_BLOCK_SIZE;
offset += SRTP_BLOCK_SIZE;
}
if (in_length > 0) {
processBlock(&f8ctx, in+offset, in_length, out+offset);
}
}
int SrtpSymCrypto::processBlock(F8_CIPHER_CTX *f8ctx, const uint8_t* in, int32_t length, uint8_t* out) {
int i;
const uint8_t *cp_in;
uint8_t* cp_in1, *cp_out;
uint32_t *ui32p;
/*
* XOR the previous key stream with IV'
* ( S(-1) xor IV' )
*/
cp_in = f8ctx->ivAccent;
cp_out = f8ctx->S;
for (i = 0; i < SRTP_BLOCK_SIZE; i++) {
*cp_out++ ^= *cp_in++;
}
/*
* Now XOR (S(n-1) xor IV') with the current counter, then increment the counter
*/
ui32p = (uint32_t *)f8ctx->S;
ui32p[3] ^= htonl(f8ctx->J);
f8ctx->J++;
/*
* Now compute the new key stream using encrypt
*/
encrypt(f8ctx->S, f8ctx->S);
/*
* as the last step XOR the plain text with the key stream to produce
* the ciphertext.
*/
cp_out = out;
cp_in = in;
cp_in1 = f8ctx->S;
for (i = 0; i < length; i++) {
*cp_out++ = *cp_in++ ^ *cp_in1++;
}
return length;
}
/** EMACS **
* Local variables:
* mode: c++
* c-default-style: ellemtel
* c-basic-offset: 4
* End:
*/