@@ -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}) | |||
@@ -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. |
@@ -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 | |||
@@ -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 &); | |||
@@ -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 |
@@ -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 | |||
@@ -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 | |||
@@ -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)); | |||
@@ -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", | |||
@@ -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; | |||
} | |||
@@ -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 | |||
@@ -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"); | |||
} | |||
} | |||
@@ -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) { | |||
@@ -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/' '{}' \; | |||