blob: a43337c6526ff9b129d225b5b30990f67c3dac0c [file] [log] [blame]
// Copyright (C) 2010 David Sugar, Tycho Softworks.
//
// This file is part of GNU uCommon C++.
//
// GNU uCommon C++ 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 3 of the License, or
// (at your option) any later version.
//
// GNU uCommon C++ 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 GNU uCommon C++. If not, see <http://www.gnu.org/licenses/>.
#include "local.h"
static const unsigned char *_salt = NULL;
static unsigned _rounds = 1;
void Cipher::Key::assign(const char *text, size_t size)
{
assign(text, size, _salt, _rounds);
}
void Cipher::Key::options(const unsigned char *salt, unsigned rounds)
{
_salt = salt;
_rounds = rounds;
}
void Cipher::Key::assign(const char *text, size_t size, const unsigned char *salt, unsigned rounds)
{
if(!algotype || !hashtype)
return;
if(!size)
size = strlen((const char *)text);
if(!rounds)
rounds = _rounds;
if(!salt)
salt = _salt;
if(EVP_BytesToKey((const EVP_CIPHER*)algotype, (const EVP_MD*)hashtype, salt, (const unsigned char *)text, size, rounds, keybuf, ivbuf) < (int)keysize)
keysize = 0;
}
void Cipher::Key::set(const char *cipher, const char *digest)
{
set(cipher);
// never use sha0...
if(eq_case(digest, "sha"))
digest = "sha1";
hashtype = EVP_get_digestbyname(digest);
}
void Cipher::Key::set(const char *cipher)
{
char algoname[64];
clear();
String::set(algoname, sizeof(algoname), cipher);
char *fpart = strchr(algoname, '-');
char *lpart = strrchr(algoname, '-');
if(fpart && fpart == lpart)
strcpy(fpart, fpart + 1);
algotype = EVP_get_cipherbyname(algoname);
if(!algotype)
return;
keysize = EVP_CIPHER_key_length((const EVP_CIPHER*)algotype);
blksize = EVP_CIPHER_block_size((const EVP_CIPHER*)algotype);
}
bool Cipher::has(const char *id)
{
// make sure cipher-bitsize forms without -mode do not fail...
char algoname[64];
String::set(algoname, sizeof(algoname), id);
char *fpart = strchr(algoname, '-');
char *lpart = strrchr(algoname, '-');
if(fpart && fpart == lpart)
strcpy(fpart, fpart + 1);
return (EVP_get_cipherbyname(algoname) != NULL);
}
void Cipher::push(unsigned char *address, size_t size)
{
}
void Cipher::release(void)
{
keys.clear();
if(context) {
EVP_CIPHER_CTX_cleanup((EVP_CIPHER_CTX*)context);
delete (EVP_CIPHER_CTX*)context;
context = NULL;
}
}
void Cipher::set(key_t key, mode_t mode, unsigned char *address, size_t size)
{
release();
bufsize = size;
bufmode = mode;
bufaddr = address;
memcpy(&keys, key, sizeof(keys));
if(!keys.keysize)
return;
context = new EVP_CIPHER_CTX;
EVP_CIPHER_CTX_init((EVP_CIPHER_CTX *)context);
EVP_CipherInit_ex((EVP_CIPHER_CTX *)context, (EVP_CIPHER *)keys.algotype, NULL, keys.keybuf, keys.ivbuf, (int)mode);
EVP_CIPHER_CTX_set_padding((EVP_CIPHER_CTX *)context, 0);
}
size_t Cipher::put(const unsigned char *data, size_t size)
{
int outlen;
size_t count = 0;
if(!bufaddr)
return 0;
if(size % keys.iosize())
return 0;
while(bufsize && size + bufpos > bufsize) {
size_t diff = bufsize - bufpos;
count += put(data, diff);
data += diff;
size -= diff;
}
if(!EVP_CipherUpdate((EVP_CIPHER_CTX *)context, bufaddr + bufpos, &outlen, data, size)) {
release();
return count;
}
bufpos += outlen;
count += outlen;
if(bufsize && bufpos >= bufsize) {
push(bufaddr, bufsize);
bufpos = 0;
}
return count;
}
size_t Cipher::pad(const unsigned char *data, size_t size)
{
size_t padsize = 0;
unsigned char padbuf[64];
const unsigned char *ep;
if(!bufaddr)
return 0;
switch(bufmode) {
case DECRYPT:
if(size % keys.iosize())
return 0;
put(data, size);
ep = data + size - 1;
bufpos -= *ep;
size -= *ep;
break;
case ENCRYPT:
padsize = size % keys.iosize();
put(data, size - padsize);
if(padsize) {
memcpy(padbuf, data + size - padsize, padsize);
memset(padbuf + padsize, keys.iosize() - padsize, keys.iosize() - padsize);
size = (size - padsize) + keys.iosize();
}
else {
size += keys.iosize();
memset(padbuf, keys.iosize(), keys.iosize());
}
put((const unsigned char *)padbuf, keys.iosize());
zerofill(padbuf, sizeof(padbuf));
}
flush();
return size;
}