Browse Source

- Added OpenSSL support

- Added support of aes-128,192,256 algorithms
master
Artyom Beilis 13 years ago
parent
commit
7cfdc9f703
14 changed files with 672 additions and 206 deletions
  1. +32
    -17
      CMakeLists.txt
  2. +6
    -0
      COPYING
  3. +3
    -0
      config.cmake.h
  4. +64
    -1
      cppcms/crypto.h
  5. +51
    -0
      private/aes.h
  6. +11
    -10
      private/aes_encryptor.h
  7. +320
    -0
      src/aes.cpp
  8. +48
    -143
      src/aes_encryptor.cpp
  9. +7
    -7
      src/config.js
  10. +72
    -0
      src/crypto.cpp
  11. +4
    -4
      src/session_pool.cpp
  12. +13
    -4
      src/urandom.cpp
  13. +37
    -17
      tests/encryptor_test.cpp
  14. +4
    -3
      tools/boost_to_booster

+ 32
- 17
CMakeLists.txt View File

@@ -20,7 +20,8 @@ option(DISABLE_ICU_LOCALIZATION
"Use standard std::locales instead of ICU" OFF)
option(DISABLE_SHARED "Disable shared libraries build" OFF)
option(DISABLE_STATIC "Disable static libraries build" OFF)
option(DISABLE_GCRYPT "Disable usage of gcrypt library, no AES cookies encryption would be available" OFF)
option(DISABLE_GCRYPT "Disable usage of gcrypt library" OFF)
option(DISABLE_OPENSSL "Disable usage of openssl library" OFF)
option(DISABLE_FCGI "Disable fastcgi web server api" OFF)
option(DISABLE_SCGI "Disable scgi web server api" OFF)
option(DISABLE_HTTP "Disable http web server" OFF)
@@ -280,10 +281,33 @@ set(CPPCMS_SOURCES
if(NOT DISABLE_GCRYPT)
find_path(GCRYPT_INCLUDE_DIR gcrypt.h)
find_library(LIB_GCRYPT gcrypt)
if(LIB_GCRYPT AND GCRYPT_INCLUDE_DIR)
include_directories(${GCRYPT_INCLUDE_DIR})
endif()
endif(NOT DISABLE_GCRYPT)
endif()

if(NOT DISABLE_OPENSSL)
find_path(OPENSSL_INCLUDE_DIR openssl/aes.h)
find_library(LIB_OPENSSL crypto)
endif()

if(GCRYPT_INCLUDE_DIR AND LIB_GCRYPT)
set(LIB_CRYPT ${LIB_GCRYPT})
set(CPPCMS_HAVE_GCRYPT 1)
include_directories(${GCRYPT_INCLUDE_DIR})
elseif (OPENSSL_INCLUDE_DIR AND LIB_OPENSSL)
set(LIB_CRYPT ${LIB_OPENSSL})
set(CPPCMS_HAVE_OPENSSL 1)
include_directories(${OPENSSL_INCLUDE_DIR})
endif()


if(LIB_CRYPT)
set(CPPCMS_SOURCES ${CPPCMS_SOURCES}
src/aes_encryptor.cpp
src/aes.cpp)
else()
message("GNU Gcrypt or OpenSSL librarys are not found, disabling aes_encryptor")
endif()



if(NOT DISABLE_FCGI)
set(CPPCMS_SOURCES ${CPPCMS_SOURCES} src/fastcgi_api.cpp)
@@ -302,15 +326,6 @@ if(NOT DISABLE_HTTP)
endif()


if(LIB_GCRYPT)
set(CPPCMS_SOURCES ${CPPCMS_SOURCES} src/aes_encryptor.cpp)
set(CPPCMS_HAVE_GCRYPT 1)
else(LIB_GCRYPT)
message("GNU Gcrypt library not found, disabling aes_encryptor")
endif(LIB_GCRYPT)


if(WIN32 AND NOT CYGWIN)
set(CPPCMS_SOURCES ${CPPCMS_SOURCES} src/session_win32_file_storage.cpp)
else(WIN32 AND NOT CYGWIN)
@@ -341,9 +356,9 @@ foreach(ALIB ${CPPCMS_LIBS})
else(DISABLE_SHARED)
target_link_libraries(${ALIB} booster)
endif(DISABLE_SHARED)
if(LIB_GCRYPT)
target_link_libraries(${ALIB} ${LIB_GCRYPT})
endif(LIB_GCRYPT)
if(LIB_CRYPT)
target_link_libraries(${ALIB} ${LIB_CRYPT})
endif(LIB_CRYPT)
if(WS2_32 AND WSOCK32)
target_link_libraries(${ALIB} ${WS2_32})


+ 6
- 0
COPYING View File

@@ -25,4 +25,10 @@
- noicu, localization pars of CppCMS are licenses under Boost software license.
- base64.cpp - MIT license (see file for copyright notices)
- md5.cpp/md5.h - see copyright notices in files (permissive license)
- sha1.h - Boost software license, see copiright notices in the file.

Note:
-----------

This software is released under the LGPL with the additional exemption that
compiling, linking, and/or using OpenSSL is allowed.

+ 3
- 0
config.cmake.h View File

@@ -80,6 +80,9 @@
/* "Enable GNU GCrypt library */
#cmakedefine CPPCMS_HAVE_GCRYPT

/* "Enable OpenSSL library */
#cmakedefine CPPCMS_HAVE_OPENSSL

/* "Have std::wstring" */
#cmakedefine CPPCMS_HAVE_STD_WSTRING



+ 64
- 1
cppcms/crypto.h View File

@@ -26,34 +26,97 @@
#include <string>

namespace cppcms {
///
/// \brief this class provides an API to calculate various cryptographic hash functions
///
class CPPCMS_API message_digest : public booster::noncopyable {
public:
protected:
/// It should be implemented in derived classes
message_digest()
{
}
public:
virtual ~message_digest()
{
}
///
/// Get the size of message digest, for example for MD5 it is 16, for SHA1 it is 20
///
virtual unsigned digest_size() const = 0;
///
/// Get processing block size, returns 64 or 128, used mostly for correct HMAC calculations
///
virtual unsigned block_size() const = 0;

///
/// Add more data of size bytes for processing
///
virtual void append(void const *ptr,size_t size) = 0;
///
/// Read the message digest for the data and reset it into initial state,
/// provided buffer must be digest_size() bytes
///
virtual void readout(void *ptr) = 0;

///
/// Make a polymorphic copy of this object, note the state of copied object is reset to
/// initial
///
virtual message_digest *clone() const = 0;

///
/// Get the name of the hash function
///
virtual char const *name() const = 0;

///
/// Create MD5 message digest
///
static std::auto_ptr<message_digest> md5();
///
/// Create SHA1 message digest
///
static std::auto_ptr<message_digest> sha1();
///
/// Create message digest by name, more then sha1 and md5 may be supported,
/// if CppCMS is compiled with cryptography library like libgcrypt or openssl
///
static std::auto_ptr<message_digest> create_by_name(std::string const &name);
};
///
/// This object calculates the HMAC signature for the input data
///
class CPPCMS_API hmac : public booster::noncopyable {
public:
///
/// Create hmac that uses given \a digest algorithm and a binary key - \a key
///
hmac(std::auto_ptr<message_digest> digest,std::string const &key);
///
/// Create hmac that uses message digest algorithm called \a name and use a binary key - \a key
///
hmac(std::string const &name,std::string const &key);
~hmac();

///
/// Get the size of the signtature
///
unsigned digest_size() const;

///
/// Add data for signing
///
void append(void const *ptr,size_t size);

///
/// Get the signature for all the data, after calling this function
/// the state of the hmac is reset and it can't be used again for
/// signing the data.
///
/// Note: provided buffer must be digest_size() bytes long.
///
void readout(void *ptr);
private:
void init(std::string const &);


+ 51
- 0
private/aes.h View File

@@ -0,0 +1,51 @@
///////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2008-2010 Artyom Beilis (Tonkikh) <artyomtnk@yahoo.com>
//
// This program 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.
//
// 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 Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
///////////////////////////////////////////////////////////////////////////////
#ifndef CPPCMS_PRIVATE_AES_H
#define CPPCMS_PRIVATE_AES_H

#include <memory>
#include <string>


namespace cppcms {
namespace impl {

struct aes_api {
typedef enum {
aes128 = 128,
aes192 = 192,
aes256 = 256
} aes_type;
virtual ~aes_api()
{
}
virtual aes_api *clone() const = 0;
virtual void encrypt(void const *in,void *out,unsigned len) = 0;
virtual void decrypt(void const *in,void *out,unsigned len) = 0;

static std::auto_ptr<aes_api> create(aes_type type,std::string const &key);
};

} // impl
} //cppcms


#endif

+ 11
- 10
private/aes_encryptor.h View File

@@ -20,28 +20,29 @@
#define CPPCMS_AES_ENCRYPTOR_H

#include <string>
#include <gcrypt.h>
#include "base_encryptor.h"
#include "aes.h"

namespace cppcms {
namespace sessions {
namespace impl {

class CPPCMS_API aes_cipher : public base_encryptor {
bool loaded_;
gcry_cipher_hd_t hd_out;
gcry_cipher_hd_t hd_in;
public:
aes_cipher(std::string key,std::string name);
~aes_cipher();
virtual std::string encrypt(std::string const &plain,time_t timeout);
virtual bool decrypt(std::string const &cipher,std::string &plain,time_t *timeout=NULL) ;
private:
struct aes_hdr {
char salt[16];
char md5[16];
};
void load();
std::vector<unsigned char> key;
public:
virtual std::string encrypt(std::string const &plain,time_t timeout);
virtual bool decrypt(std::string const &cipher,std::string &plain,time_t *timeout=NULL) ;
aes_cipher(std::string key);
~aes_cipher();
std::auto_ptr<cppcms::impl::aes_api> api_;
cppcms::impl::aes_api::aes_type type_;
std::string key_;

};

} // impl


+ 320
- 0
src/aes.cpp View File

@@ -0,0 +1,320 @@
///////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2008-2010 Artyom Beilis (Tonkikh) <artyomtnk@yahoo.com>
//
// This program 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.
//
// 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 Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
///////////////////////////////////////////////////////////////////////////////
#define CPPCMS_SOURCE
#include "aes.h"
#include <cppcms/urandom.h>
#include <cppcms/config.h>
#include <stdexcept>
#include <string.h>


#if defined(CPPCMS_HAVE_OPENSSL)
#include <openssl/aes.h>
#endif

#if defined(CPPCMS_HAVE_GCRYPT)
#include <gcrypt.h>

#ifdef CPPCMS_WIN_NATIVE
# define CPPCMS_GCRYPT_USE_BOOSTER_THREADS
#endif

#ifdef CPPCMS_GCRYPT_USE_BOOSTER_THREADS

#include <booster/thread.h>

extern "C" {
static int nt_mutex_init(void **p)
{
try {
*p=new booster::mutex();
return 0;
}
catch(...)
{
return 1;
}
}
static int nt_mutex_destroy(void **p)
{
booster::mutex **m=reinterpret_cast<booster::mutex **>(p);
delete *m;
*m=0;
return 0;
}

static int nt_mutex_lock(void **p)
{
booster::mutex *m=reinterpret_cast<booster::mutex *>(*p);
try {
m->lock();
return 0;
}
catch(...)
{
return 1;
}
}

static int nt_mutex_unlock(void **p)
{
booster::mutex *m=reinterpret_cast<booster::mutex *>(*p);
try {
m->unlock();
return 0;
}
catch(...)
{
return 1;
}
}

static struct gcry_thread_cbs threads_nt = {
GCRY_THREAD_OPTION_USER,
0,
nt_mutex_init,
nt_mutex_destroy,
nt_mutex_lock,
nt_mutex_unlock,
0,0,0,0,
0,0,0,0
};

static void set_gcrypt_cbs()
{
gcry_control (GCRYCTL_SET_THREAD_CBS, &threads_nt);
}
} // extern "C"
#else // pthreads

#include <pthread.h>
#include <errno.h>

extern "C" {

GCRY_THREAD_OPTION_PTHREAD_IMPL;
static void set_gcrypt_cbs()
{
gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread);
}
} // exten C

#endif

// Static library initialization
namespace {
class load {
public:
load() {
set_gcrypt_cbs();
gcry_check_version(NULL);
}
} loader;
} // anon namespace

#endif // defined(CPPCMS_HAVE_GCRYPT)



namespace cppcms {
namespace impl {

#if defined CPPCMS_HAVE_GCRYPT

class gcrypt_aes_encryptor : public aes_api {
void operator=(gcrypt_aes_encryptor const &);
public:
gcrypt_aes_encryptor(gcrypt_aes_encryptor const &other)
{
init(other.type_,other.key_);
}
gcrypt_aes_encryptor(aes_type type,std::string const &key)
{
init(type,key);
}
virtual ~gcrypt_aes_encryptor()
{
gcry_cipher_close(enc_);
gcry_cipher_close(dec_);
}
virtual gcrypt_aes_encryptor *clone() const
{
return new gcrypt_aes_encryptor(*this);
}
virtual void encrypt(void const *in,void *out,unsigned len)
{
if(len % 16 != 0) {
throw std::invalid_argument("Invalid block size");
}
if(gcry_cipher_encrypt(enc_,out,len,in,len)!=0) {
throw std::runtime_error("Encryption failed");
}
}
virtual void decrypt(void const *in,void *out,unsigned len)
{
if(len % 16 != 0) {
throw std::invalid_argument("Invalid block size");
}
if(gcry_cipher_decrypt(enc_,out,len,in,len)!=0) {
throw std::runtime_error("Decryption failed");
}
}
private:
void init(aes_type type,std::string const &key)
{
if(key.size()*8!=unsigned(type)) {
throw std::invalid_argument("Invalid key size");
}
key_ = key;
type_ = type;
int algo=0;
switch(type) {
case aes128:
algo = GCRY_CIPHER_AES128;
break;
case aes192:
algo = GCRY_CIPHER_AES192;
break;
case aes256:
algo = GCRY_CIPHER_AES256;
break;
default:
throw std::invalid_argument("Invalid encryption method");
}

enc_ = 0;
dec_ = 0;
char iv_enc[16];
char iv_dec[16]={0};
urandom_device rnd;
rnd.generate(iv_enc,sizeof(iv_enc));

if( gcry_cipher_open(&enc_,algo,GCRY_CIPHER_MODE_CBC,0)
|| gcry_cipher_open(&dec_,algo,GCRY_CIPHER_MODE_CBC,0)
|| gcry_cipher_setkey(enc_,key_.c_str(),key.size())
|| gcry_cipher_setkey(dec_,key_.c_str(),key.size())
|| gcry_cipher_setiv(enc_,iv_enc,16)
|| gcry_cipher_setiv(dec_,iv_dec,16)
)
{
if(enc_)
gcry_cipher_close(enc_);
if(dec_)
gcry_cipher_close(dec_);
throw std::runtime_error("Failed to create AES encryptor");
}

}

gcry_cipher_hd_t enc_;
gcry_cipher_hd_t dec_;
std::string key_;
aes_type type_;

};
typedef gcrypt_aes_encryptor aes_encryption_provider;

#elif defined CPPCMS_HAVE_OPENSSL

class openssl_aes_encryptor : public aes_api {
void operator=(openssl_aes_encryptor const &);
public:
openssl_aes_encryptor(openssl_aes_encryptor const &other) :
key_enc_(other.key_enc_),
key_dec_(other.key_dec_)
{
reset();
}

openssl_aes_encryptor(aes_type type,std::string const &key)
{
if(key.size()*8!=unsigned(type)) {
throw std::invalid_argument("Invalid key size");
}
AES_set_encrypt_key(reinterpret_cast<unsigned char const *>(key.c_str()), type, &key_enc_);
AES_set_decrypt_key(reinterpret_cast<unsigned char const *>(key.c_str()), type, &key_dec_);
reset();
}
virtual openssl_aes_encryptor *clone() const
{
return new openssl_aes_encryptor(*this);
}
virtual void encrypt(void const *in,void *out,unsigned len)
{
if(len % 16 != 0) {
throw std::invalid_argument("Invalid block size");
}
AES_cbc_encrypt(reinterpret_cast<unsigned char const *>(in),
reinterpret_cast<unsigned char *>(out), len,
&key_enc_,
iv_enc_,
AES_ENCRYPT);
}
virtual void decrypt(void const *in,void *out,unsigned len)
{
if(len % 16 != 0) {
throw std::invalid_argument("Invalid block size");
}
AES_cbc_encrypt(reinterpret_cast<unsigned char const *>(in),
reinterpret_cast<unsigned char *>(out), len,
&key_dec_,
iv_dec_,
AES_DECRYPT);
}
virtual ~openssl_aes_encryptor()
{
memset(&key_enc_,0,sizeof(key_enc_));
memset(&key_dec_,0,sizeof(key_dec_));
memset(iv_dec_,0,sizeof(iv_dec_));
memset(iv_enc_,0,sizeof(iv_dec_));
}
private:
void reset()
{
memset(iv_dec_,0,sizeof(iv_dec_));
urandom_device rnd;
rnd.generate(iv_enc_,sizeof(iv_enc_));
}

AES_KEY key_enc_;
AES_KEY key_dec_;
unsigned char iv_enc_[16];
unsigned char iv_dec_[16];
};

typedef openssl_aes_encryptor aes_encryption_provider;

#else
# error "Invalid conditions"
#endif

std::auto_ptr<aes_api> aes_api::create(aes_api::aes_type type,std::string const &key)
{
std::auto_ptr<aes_api> res(new aes_encryption_provider(type,key));
return res;
}


} // impl
} //cppcms



+ 48
- 143
src/aes_encryptor.cpp View File

@@ -23,168 +23,65 @@
#include <string>
#include <vector>
#include <algorithm>
#include <sstream>
#include <cppcms/cppcms_error.h>
#include "aes_encryptor.h"

#include <cppcms/base64.h>
#include <cppcms/crypto.h>
#include <string.h>
#include <time.h>

#ifdef CPPCMS_WIN_NATIVE
#define CPPCMS_GCRYPT_USE_BOOSTER_THREADS
#endif

#ifdef CPPCMS_GCRYPT_USE_BOOSTER_THREADS
using namespace std;

#include <booster/thread.h>
namespace cppcms {
namespace sessions {
namespace impl {

static int nt_mutex_init(void **p)
aes_cipher::aes_cipher(std::string k,std::string name)
{
try {
*p=new booster::mutex();
return 0;
using cppcms::impl::aes_api;
if(name == "aes" || name == "aes-128" || name == "aes128") {
type_ = aes_api::aes128;
}
catch(...)
{
return 1;
else if(name == "aes-192" || name == "aes192") {
type_ = aes_api::aes192;
}
}
static int nt_mutex_destroy(void **p)
{
booster::mutex **m=reinterpret_cast<booster::mutex **>(p);
delete *m;
*m=0;
return 0;
}

static int nt_mutex_lock(void **p)
{
booster::mutex *m=reinterpret_cast<booster::mutex *>(*p);
try {
m->lock();
return 0;
else if(name == "aes-256" || name == "aes256") {
type_ = aes_api::aes256;
}
catch(...)
{
return 1;
else {
throw cppcms_error("Unsupported AES algorithm `" + name + "', supported are aes,aes-128, aes-192, aes-256");
}
}

static int nt_mutex_unlock(void **p)
{
booster::mutex *m=reinterpret_cast<booster::mutex *>(*p);
try {
m->unlock();
return 0;
}
catch(...)
{
return 1;
if(k.size() != unsigned(type_ / 4) ) {
std::ostringstream ss;
ss << "AES: requires key length for algorithm " << name << " is " << type_/8
<< " bytes (" << type_/4 << "hexadecimal digits)";
throw cppcms_error(ss.str());
}
}

static struct gcry_thread_cbs threads_nt = {
GCRY_THREAD_OPTION_USER,
0,
nt_mutex_init,
nt_mutex_destroy,
nt_mutex_lock,
nt_mutex_unlock,
0,0,0,0,
0,0,0,0
};

static void set_gcrypt_cbs()
{
gcry_control (GCRYCTL_SET_THREAD_CBS, &threads_nt);
key_ = to_binary(k);
}
#else

#include <pthread.h>
#include <errno.h>

GCRY_THREAD_OPTION_PTHREAD_IMPL;

static void set_gcrypt_cbs()
aes_cipher::~aes_cipher()
{
gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread);
}

#endif


using namespace std;

namespace cppcms {
namespace sessions {
namespace impl {

namespace {
class load {
public:
load() {
set_gcrypt_cbs();
gcry_check_version(NULL);
}
} loader;

} // anon namespace

void aes_cipher::load()
{
if(loaded_)
return;;
bool in=false,out=false;
try {
in=gcry_cipher_open(&hd_in,GCRY_CIPHER_AES,GCRY_CIPHER_MODE_CBC,0) == 0;
out=gcry_cipher_open(&hd_out,GCRY_CIPHER_AES,GCRY_CIPHER_MODE_CBC,0) == 0;
if(!in || !out){
throw cppcms_error("AES cipher initialization failed");
}
if( gcry_cipher_setkey(hd_in,&key.front(),16) != 0) {
throw cppcms_error("AES cipher initialization failed");
}
if( gcry_cipher_setkey(hd_out,&key.front(),16) != 0) {
throw cppcms_error("AES cipher initialization failed");
}
char iv[16];
gcry_create_nonce(iv,sizeof(iv));
gcry_cipher_setiv(hd_out,iv,sizeof(iv));
loaded_ = true;
return;
}
catch(...) {
if(in) gcry_cipher_close(hd_in);
if(out) gcry_cipher_close(hd_out);
throw;
}
}

aes_cipher::aes_cipher(string k) :
loaded_(false)
{
if(k.size()!=32) {
throw cppcms_error("AES requires key length of 16 bytes (32 hexadecimal digits)");
}

std::string skey = to_binary(k);
key.assign(skey.c_str(),skey.c_str()+skey.size());
if(!api_.get())
api_ = cppcms::impl::aes_api::create(type_,key_);
}

aes_cipher::~aes_cipher()
{
if(loaded_) {
gcry_cipher_close(hd_in);
gcry_cipher_close(hd_out);
}
}

string aes_cipher::encrypt(string const &plain,time_t timeout)
std::string aes_cipher::encrypt(string const &plain,time_t timeout)
{
load();

size_t block_size=(plain.size() + 15) / 16 * 16;

vector<unsigned char> data(sizeof(aes_hdr)+sizeof(info)+block_size,0);
std::vector<unsigned char> data(sizeof(aes_hdr)+sizeof(info)+block_size,0);
copy(plain.begin(),plain.end(),data.begin() + sizeof(aes_hdr)+sizeof(info));
aes_hdr &aes_header=*(aes_hdr*)(&data.front());
info &header=*(info *)(&data.front()+sizeof(aes_hdr));
@@ -192,27 +89,35 @@ string aes_cipher::encrypt(string const &plain,time_t timeout)
header.size=plain.size();
memset(&aes_header,0,16);

gcry_md_hash_buffer(GCRY_MD_MD5,&aes_header.md5,&header,block_size+sizeof(info));
gcry_cipher_encrypt(hd_out,&data.front(),data.size(),NULL,0);
std::auto_ptr<message_digest> md(message_digest::md5());
md->append(&header,block_size+sizeof(info));
md->readout(&aes_header.md5);

std::vector<unsigned char> odata(data.size(),0);
api_->encrypt(&data.front(),&odata.front(),data.size());

return base64_enc(data);
return base64_enc(odata);
}

bool aes_cipher::decrypt(string const &cipher,string &plain,time_t *timeout)
{
load();
vector<unsigned char> data;
base64_dec(cipher,data);
vector<unsigned char> idata;
base64_dec(cipher,idata);
size_t norm_size=b64url::decoded_size(cipher.size());
if(norm_size<sizeof(info)+sizeof(aes_hdr) || norm_size % 16 !=0)
return false;

gcry_cipher_decrypt(hd_in,&data.front(),data.size(),NULL,0);
gcry_cipher_reset(hd_in);
vector<char> md5(16,0);
gcry_md_hash_buffer(GCRY_MD_MD5,&md5.front(),&data.front()+sizeof(aes_hdr),data.size()-sizeof(aes_hdr));
vector<unsigned char> data(idata.size(),0);
api_->decrypt(&idata.front(),&data.front(),data.size());
char md5[16];
std::auto_ptr<message_digest> md(message_digest::md5());
md->append(&data.front()+sizeof(aes_hdr),data.size()-sizeof(aes_hdr));
md->readout(md5);
aes_hdr &aes_header = *(aes_hdr*)&data.front();
if(!std::equal(md5.begin(),md5.end(),aes_header.md5)) {
if(memcmp(md5,aes_header.md5,16)!=0) {
return false;
}
info &header=*(info *)(&data.front()+sizeof(aes_hdr));


+ 7
- 7
src/config.js View File

@@ -1,4 +1,4 @@
// CppCMS Configuration file
/// CppCMS Configuration file
// Extended JSON format, "//" like comments are allowed

{
@@ -49,17 +49,17 @@
// "buffer" : 4096
},
"localization" : {
"encoding" : "UTF-8",
"backend" : "winapi",
"messages" : {
"paths" : [ "./transtext/locale" ],
"domains" : [ "test", "app" ]
},
//"locales" : [ "he_IL.UTF-8", "en_US.UTF-8", "he_IL.UTF-8@calendar=hebrew" ],
"locales" : [ "en_US.UTF-8", "en_US.UTF-8", "he_IL.UTF-8@calendar=hebrew" ],
"disable_charset_in_content_type" : false // Disable
},
"session" : {
"expire" : "browser",
"timeout" : 10, // seconds
"timeout" : 600, // seconds
"cookies" : {
"prefix" : "cppcms_session",
"domain" : "",
@@ -72,11 +72,11 @@
"client_size_limit" : 1000,
"gc" : 10,
"client" : {
"encryptor" : "hmac-sha1", // "hmac" = "hmac-sha1", "hmac-md5", "hmac-sha224", "hmac-sha384", "hmac-sha256", "hmac-sha512"
//"encryptor" : "aes",
//"encryptor" : "hmac-sha1", // "hmac" = "hmac-sha1", "hmac-md5", "hmac-sha224", "hmac-sha384", "hmac-sha256", "hmac-sha512"
//"encryptor" : "hmac-sha512", // "hmac" = "hmac-sha1", "hmac-md5", "hmac-sha224", "hmac-sha384", "hmac-sha256", "hmac-sha512"
"encryptor" : "aes",
// aes or hmac -- hmac -- signature only, aes -- encryption and signature
"key" : "261965ba80a79c034c9ae366a19a2627"
// 32 digit hexadecimal secret number
},
"server" : {
"storage" : "files",


+ 72
- 0
src/crypto.cpp View File

@@ -16,11 +16,15 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
///////////////////////////////////////////////////////////////////////////////
#define CPPCMS_SOURCE
#include <cppcms/crypto.h>
#include <cppcms/config.h>
#ifdef CPPCMS_HAVE_GCRYPT
# include <gcrypt.h>
#endif
#ifdef CPPCMS_HAVE_OPENSSL
# include <openssl/sha.h>
#endif
#include <vector>
#include <stdexcept>
#include "md5.h"
@@ -175,6 +179,62 @@ namespace cppcms {
};

#endif

#ifdef CPPCMS_HAVE_OPENSSL
#define CPPCMS_SSL_MD(len,base_len) \
class ssl_sha##len : public message_digest { \
public: \
ssl_sha##len() \
{ \
SHA ## len ## _Init(&state_); \
} \
virtual message_digest *clone() const \
{ \
return new ssl_sha##len(); \
} \
virtual ~ssl_sha##len() \
{ \
memset(&state_,0,sizeof(state_)); \
} \
virtual void append(void const *ptr,size_t size) \
{ \
SHA ## len ## _Update(&state_,ptr,size); \
} \
virtual void readout(void *ptr) \
{ \
SHA ## len ## _Final( \
(unsigned char *)(ptr), \
&state_ \
); \
SHA ## len ## _Init(&state_); \
} \
virtual char const *name() const \
{ \
return "sha" #len; \
} \
virtual unsigned digest_size() const \
{ \
return len / 8; \
} \
virtual unsigned block_size() const \
{ \
if (len >= 384) \
return 128; \
else \
return 64; \
} \
private: \
SHA ## base_len ## _CTX state_; \
};

CPPCMS_SSL_MD(224,256)
CPPCMS_SSL_MD(256,256)
CPPCMS_SSL_MD(384,512)
CPPCMS_SSL_MD(512,512)

#endif

} // anon
std::auto_ptr<message_digest> message_digest::md5()
@@ -210,6 +270,18 @@ namespace cppcms {
else if(name == "sha512")
d.reset(new gcrypt_digets(GCRY_MD_SHA512));
#endif

#ifdef CPPCMS_HAVE_OPENSSL
else if(name == "sha224")
d.reset(new ssl_sha224());
else if(name == "sha256")
d.reset(new ssl_sha256());
else if(name == "sha384")
d.reset(new ssl_sha384());
else if(name == "sha512")
d.reset(new ssl_sha512());
#endif
return d;
}



+ 4
- 4
src/session_pool.cpp View File

@@ -36,7 +36,7 @@
namespace boost = cppcms_boost;
#endif

#ifdef CPPCMS_HAVE_GCRYPT
#if defined(CPPCMS_HAVE_GCRYPT) || defined(CPPCMS_HAVE_OPENSSL)
#include "aes_encryptor.h"
#endif

@@ -200,10 +200,10 @@ void session_pool::init()
std::string key = srv.settings().get<std::string>("session.client.key");
factory.reset(new enc_factory_param<cppcms::sessions::impl::hmac_cipher>(key,algo));
}
#ifdef CPPCMS_HAVE_GCRYPT
else if(enc=="aes") {
#if defined(CPPCMS_HAVE_GCRYPT) || defined(CPPCMS_HAVE_OPENSSL)
else if(enc.compare(0,3,"aes") == 0) {
std::string key = srv.settings().get<std::string>("session.client.key");
factory.reset(new enc_factory<cppcms::sessions::impl::aes_cipher>(key));
factory.reset(new enc_factory_param<cppcms::sessions::impl::aes_cipher>(key,enc));
}
#endif
else


+ 13
- 4
src/urandom.cpp View File

@@ -65,7 +65,11 @@ namespace cppcms {

#else

#include <fstream>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>


namespace cppcms {
struct urandom_device::_data {};
@@ -78,10 +82,15 @@ namespace cppcms {
}
void urandom_device::generate(void *ptr,unsigned len)
{
std::ifstream u("/dev/urandom");
if(u.good() && !u.read(reinterpret_cast<char *>(ptr),len).fail() && u.gcount()==int(len))
if(len == 0)
return;
throw cppcms_error("Failed to read /dev/urandom");
int fd = open("/dev/urandom",O_RDONLY);
if(!fd)
throw cppcms_error("Failed to open /dev/urandom");
int n = read(fd,ptr,len);
close(fd);
if(n!=int(len))
throw cppcms_error("Failed to read /dev/urandom");
}
}



+ 37
- 17
tests/encryptor_test.cpp View File

@@ -20,7 +20,7 @@
#include <cppcms/session_cookies.h>
#include <cppcms/crypto.h>
#include "hmac_encryptor.h"
#ifdef CPPCMS_HAVE_GCRYPT
#if defined(CPPCMS_HAVE_GCRYPT) || defined(CPPCMS_HAVE_OPENSSL)
#include "aes_encryptor.h"
#endif
#include "test.h"
@@ -30,12 +30,11 @@


template<typename Encryptor>
void run_test(bool is_signature = false)
void run_test(std::string name,std::string key,bool is_signature = false)
{
std::string key="261965ba80a79c034c9ae366a19a2627";
char c1=key[0];
char c2=key[31];
std::auto_ptr<cppcms::sessions::encryptor> enc(new Encryptor(key));
std::auto_ptr<cppcms::sessions::encryptor> enc(new Encryptor(key,name));
time_t now=time(0)+5;
std::string cipher=enc->encrypt("Hello World",now);
std::string plain;
@@ -68,14 +67,14 @@ void run_test(bool is_signature = false)
TEST(!enc->decrypt(cipher,plain,&time));
cipher=enc->encrypt("test",now);
key[0]=c1+1;
enc.reset(new Encryptor(key));
enc.reset(new Encryptor(key,name));
TEST(!enc->decrypt(cipher,plain,&time));
key[0]=c1;
key[31]=c2+1;
enc.reset(new Encryptor(key));
enc.reset(new Encryptor(key,name));
TEST(!enc->decrypt(cipher,plain,&time));
key[31]=c2;
enc.reset(new Encryptor(key));
enc.reset(new Encryptor(key,name));
TEST(enc->decrypt(cipher,plain,&time) && plain=="test" && time==now);
// Salt works
if(!is_signature) {
@@ -108,7 +107,7 @@ std::string get_diget(MD &d,std::string const &source)
void test_crypto()
{
using namespace cppcms;
std::cout << "- testing sha1/md5" << std::endl;
std::cout << "-- testing sha1/md5" << std::endl;
TEST(message_digest::md5().get());
TEST(message_digest::sha1().get());
TEST(message_digest::md5()->name() == std::string("md5"));
@@ -129,7 +128,7 @@ void test_crypto()
TEST(get_diget(*d,"")=="da39a3ee5e6b4b0d3255bfef95601890afd80709");
TEST(get_diget(*d,"Hello World!")=="2ef7bde608ce5404e97d5f042f95f89f1c232871");
}
std::cout << "- testing hmac-sha1/md5" << std::endl;
std::cout << "-- testing hmac-sha1/md5" << std::endl;
{
hmac d("md5","Jefe");
TEST(get_diget(d,"what do ya want for nothing?") == "750c783e6ab0b503eaa86e310a5db738");
@@ -139,8 +138,8 @@ void test_crypto()
TEST(get_diget(d,"what do ya want for nothing?") == "4891f8cf6a4641897159756847369d1a");
}

#ifdef CPPCMS_HAVE_GCRYPT
std::cout << "- testing shaXXX " << std::endl;
#if defined(CPPCMS_HAVE_GCRYPT) || defined(CPPCMS_HAVE_OPENSSL)
std::cout << "-- testing shaXXX " << std::endl;
{
std::auto_ptr<message_digest> d(message_digest::create_by_name("sha256"));
TEST(d->name() == std::string("sha256"));
@@ -158,15 +157,36 @@ void test_crypto()
int main()
{
try {
std::cout << "Testing basic cryptography" << std::endl;
std::cout << "- Testing basic cryptography functions" << std::endl;

test_crypto();

std::cout << "Testing hmac cookies signature" << std::endl;
run_test<cppcms::sessions::impl::hmac_cipher>(true);
#ifdef CPPCMS_HAVE_GCRYPT
std::cout << "Testing aes cookies encryptor" << std::endl;
run_test<cppcms::sessions::impl::aes_cipher>();
std::string key="261965ba80a79c034c9ae366a19a2627";

std::cout << "- Testing internal cryptography library" << std::endl;
std::cout << "-- Testing hmac cookies signature" << std::endl;
run_test<cppcms::sessions::impl::hmac_cipher>("sha1",key,true);
run_test<cppcms::sessions::impl::hmac_cipher>("md5",key,true);
#if defined(CPPCMS_HAVE_GCRYPT) || defined(CPPCMS_HAVE_OPENSSL)
std::cout << "- Testing external cryptography library" << std::endl;
std::cout << "-- Testing hmac cookies signature" << std::endl;
std::cout << "--- hmac-sha224" << std::endl;
run_test<cppcms::sessions::impl::hmac_cipher>("sha224",key,true);
std::cout << "--- hmac-sha256" << std::endl;
run_test<cppcms::sessions::impl::hmac_cipher>("sha256",key,true);
std::cout << "--- hmac-sha384" << std::endl;
run_test<cppcms::sessions::impl::hmac_cipher>("sha384",key,true);
std::cout << "--- hmac-sha512" << std::endl;
run_test<cppcms::sessions::impl::hmac_cipher>("sha512",key,true);
std::cout << "-- Testing aes cookies encryption" << std::endl;
std::cout << "--- aes" << std::endl;
run_test<cppcms::sessions::impl::aes_cipher>("aes",key);
std::cout << "--- aes128" << std::endl;
run_test<cppcms::sessions::impl::aes_cipher>("aes128",key);
std::cout << "--- aes192" << std::endl;
run_test<cppcms::sessions::impl::aes_cipher>("aes192",key + key.substr(16));
std::cout << "--- aes256" << std::endl;
run_test<cppcms::sessions::impl::aes_cipher>("aes256",key + key);
#endif
}
catch(std::exception const &e) {


+ 4
- 3
tools/boost_to_booster View File

@@ -1,7 +1,8 @@
#!/bin/bash

#
#svn export https://cppcms.svn.sourceforge.net/svnroot/cppcms/boost_locale/branches/rework trunk
# rm -fr trunk
# svn export https://cppcms.svn.sourceforge.net/svnroot/cppcms/boost_locale/branches/rework trunk
#

rm -fr boost_locale
@@ -12,12 +13,12 @@ cp -a trunk boost_locale
#
# Linux version
#
find boost_locale -name '*.hpp' -exec rename 's/\.hpp/\.h/' '{}' \;
#find boost_locale -name '*.hpp' -exec rename 's/\.hpp/\.h/' '{}' \;
#

# Cygwin version
#
# find boost_locale -name '*.hpp' -exec rename .hpp .h '{}' \;
find boost_locale -name '*.hpp' -exec rename .hpp .h '{}' \;
#

find boost_locale -regextype posix-extended -regex '.*\.(cpp|h)' -exec sed -i 's/_HPP_INCLUDED/_H_INCLUDED/' '{}' \;


Loading…
Cancel
Save