diff options
author | lloyd <[email protected]> | 2014-01-01 21:20:55 +0000 |
---|---|---|
committer | lloyd <[email protected]> | 2014-01-01 21:20:55 +0000 |
commit | 197dc467dec28a04c3b2f30da7cef122dfbb13e9 (patch) | |
tree | cdbd3ddaec051c72f0a757db461973d90c37b97a /lib/wrap | |
parent | 62faac373c07cfe10bc8c309e89ebdd30d8e5eaa (diff) |
Shuffle things around. Add NIST X.509 test to build.
Diffstat (limited to 'lib/wrap')
28 files changed, 3469 insertions, 0 deletions
diff --git a/lib/wrap/perl-xs/Botan.pm b/lib/wrap/perl-xs/Botan.pm new file mode 100644 index 000000000..ac4ad91fb --- /dev/null +++ b/lib/wrap/perl-xs/Botan.pm @@ -0,0 +1,117 @@ +package Botan; + +use strict; +use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $AUTOLOAD); + +require DynaLoader; +require AutoLoader; +use Carp; + +@ISA = qw(DynaLoader); +$VERSION = '0.01'; + +@EXPORT_OK = qw( + NONE + IGNORE_WS + FULL_CHECK +); + +%EXPORT_TAGS = ( + 'all' => [ @EXPORT_OK ], + 'decoder_checking' => [ qw( + NONE + IGNORE_WS + FULL_CHECK + )], + +); + + +sub AUTOLOAD +{ + # This AUTOLOAD is used to 'autoload' constants from the constant() + # XS function. If a constant is not found then control is passed + # to the AUTOLOAD in AutoLoader. + + my $constname = $AUTOLOAD; + $constname =~ s/.*:://; + croak '& not defined' if $constname eq 'constant'; +# my $val = constant($constname, @_ ? $_[0] : 0); + my $val = constant($constname); + if ($! != 0) { + if ( $! =~ /Invalid/ ) + { + $AutoLoader::AUTOLOAD = $AUTOLOAD; + goto &AutoLoader::AUTOLOAD; + } + else + { + croak "Your vendor has not defined Botan symbol $constname"; + } + } + no strict 'refs'; + *$AUTOLOAD = sub { $val }; + goto &$AUTOLOAD; +} + + +bootstrap Botan $VERSION; + +# to setup inheritance... + +package Botan::Filter; +use vars qw(@ISA); +@ISA = qw(); + +package Botan::Chain; +use vars qw(@ISA); +@ISA = qw( Botan::Filter ); + +package Botan::Fork; +use vars qw(@ISA); +@ISA = qw( Botan::Filter ); + +package Botan::Hex_Encoder; +use vars qw(@ISA); +@ISA = qw( Botan::Filter ); + +package Botan::Hex_Decoder; +use vars qw(@ISA); +@ISA = qw( Botan::Filter ); + +package Botan::Base64_Decoder; +use vars qw(@ISA); +@ISA = qw( Botan::Filter ); + +package Botan::Base64_Encoder; +use vars qw(@ISA); +@ISA = qw( Botan::Filter ); + + +package Botan; + +1; +__END__ + +=head1 NAME + +Botan - Perl extension for access to Botan ... + +=head1 SYNOPSIS + + use Botan; + blah blah blah + +=head1 DESCRIPTION + +Blah blah blah. + +=head1 AUTHOR + +Vaclav Ovsik <[email protected]> + +=head1 SEE ALSO + +Bla + +=cut diff --git a/lib/wrap/perl-xs/Botan.xs b/lib/wrap/perl-xs/Botan.xs new file mode 100644 index 000000000..135da0d5d --- /dev/null +++ b/lib/wrap/perl-xs/Botan.xs @@ -0,0 +1,828 @@ +#ifdef __cplusplus +extern "C" { +#endif + +#include "EXTERN.h" +#include "perl.h" +#include "XSUB.h" + +#ifdef __cplusplus +} +#endif + +#include <botan/alg_id.h> +#include <botan/asn1_alt_name.h> +#include <botan/asn1_oid.h> +#include <botan/filters.h> +#include <botan/init.h> +#include <botan/oids.h> +#include <botan/x509cert.h> +#include <botan/x509_ext.h> + + +/* xsubpp converts ':' to '_' in typemap. We create our types without ':' */ + +typedef Botan::ASN1_String Botan__ASN1_String; +typedef Botan::AlgorithmIdentifier Botan__AlgorithmIdentifier; +typedef Botan::AlternativeName Botan__AlternativeName; +typedef Botan::Attribute Botan__Attribute; +typedef Botan::Base64_Decoder Botan__Base64_Decoder; +typedef Botan::Base64_Encoder Botan__Base64_Encoder; +typedef Botan::Chain Botan__Chain; +typedef Botan::Certificate_Extension Botan__Extension; +typedef Botan::Filter Botan__Filter; +typedef Botan::Fork Botan__Fork; +typedef Botan::Hex_Decoder Botan__Hex_Decoder; +typedef Botan::Hex_Encoder Botan__Hex_Encoder; +typedef Botan::OID Botan__OID; +typedef Botan::Pipe Botan__Pipe; +typedef Botan::X509_Certificate Botan__X509_Certificate; +typedef Botan::X509_DN Botan__X509_DN; +typedef Botan::X509_Time Botan__X509_Time; +typedef Botan::u32bit Botan__u32bit; + + +/* Types to keep track of destruction C++ objects passed + * into other objects... + * An Botan object is deleted by his parent object into which is passed, + * e.g. some Filter is deleted when his Pipe is destructed. We must + * track this and not to delete object again in Perls destructor. + */ + +class ObjectInfo +{ +private: + I32 d_signature; + bool d_del; +public: + static I32 const SIGNVAL = 0x696a626f; + ObjectInfo() : d_signature(SIGNVAL), + d_del(true) {}; + ~ObjectInfo() {}; + void set_delete(bool del = true) { d_del = del; }; + void set_delete_no() { set_delete(false); }; + void set_delete_yes() { set_delete(true); }; + bool should_delete() const { return d_del; }; +}; + +/* Constant object in initial state - template */ + +ObjectInfo const oi_init; + + +/* Botan library initializer ... */ + +Botan::LibraryInitializer botan_init; + + + +/*============================================================================*/ + +MODULE = Botan PACKAGE = Botan + +PROTOTYPES: ENABLE + +void +constant(char *name) + CODE: + using namespace Botan; + errno = 0; + switch (name[0]) + { + case 'F': + if ( strEQ(name, "FULL_CHECK") ) + XSRETURN_IV( FULL_CHECK ); // Decoder_Checking enum + break; + case 'I': + if ( strEQ(name, "IGNORE_WS") ) + XSRETURN_IV( IGNORE_WS ); // Decoder_Checking enum + break; + case 'N': + if ( strEQ(name, "NONE") ) + XSRETURN_IV( NONE ); // Decoder_Checking enum + break; + } + errno = EINVAL; + XSRETURN_UNDEF; + + +# =========================== Botan::Chain ========================== + +MODULE = Botan PACKAGE = Botan::Chain + +Botan__Chain * +Botan__Chain::new(f1 = 0, f2 = 0, f3 = 0, f4 = 0) + Botan__Filter *f1; + Botan__Filter *f2; + Botan__Filter *f3; + Botan__Filter *f4; + PREINIT: + ObjectInfo *f1_oi; + ObjectInfo *f2_oi; + ObjectInfo *f3_oi; + ObjectInfo *f4_oi; + CODE: + try { + RETVAL = new Botan__Chain(f1, f2, f3, f4); + if ( f1 ) f1_oi->set_delete_no(); + if ( f2 ) f2_oi->set_delete_no(); + if ( f3 ) f3_oi->set_delete_no(); + if ( f4 ) f4_oi->set_delete_no(); + } + catch (const std::exception &e) { + croak(e.what()); + } + OUTPUT: + RETVAL + +void +Botan__Chain::DESTROY() + PREINIT: + ObjectInfo *THIS_oi; + CODE: + if ( THIS_oi->should_delete() ) + try { + delete THIS; + } + catch (const std::exception &e) { + croak(e.what()); + } + + +# =========================== Botan::Fork ========================== + +MODULE = Botan PACKAGE = Botan::Fork + +Botan__Fork * +Botan__Fork::new(f1 = 0, f2 = 0, f3 = 0, f4 = 0) + Botan__Filter *f1; + Botan__Filter *f2; + Botan__Filter *f3; + Botan__Filter *f4; + PREINIT: + ObjectInfo *f1_oi; + ObjectInfo *f2_oi; + ObjectInfo *f3_oi; + ObjectInfo *f4_oi; + CODE: + try { + RETVAL = new Botan__Fork(f1, f2, f3, f4); + if ( f1 ) f1_oi->set_delete_no(); + if ( f2 ) f2_oi->set_delete_no(); + if ( f3 ) f3_oi->set_delete_no(); + if ( f4 ) f4_oi->set_delete_no(); + } + catch (const std::exception &e) { + croak(e.what()); + } + OUTPUT: + RETVAL + +void +Botan__Fork::DESTROY() + PREINIT: + ObjectInfo *THIS_oi; + CODE: + if ( THIS_oi->should_delete() ) + try { + delete THIS; + } + catch (const std::exception &e) { + croak(e.what()); + } + + +# ============================ Botan::Base64_Decoder ============================ + +MODULE = Botan PACKAGE = Botan::Base64_Decoder + +Botan__Base64_Decoder * +Botan__Base64_Decoder::new(checking = Botan::NONE) + int checking; + CODE: + try { + using namespace Botan; + RETVAL = new Base64_Decoder((Decoder_Checking)checking); + } + catch (const std::exception &e) { + croak(e.what()); + } + OUTPUT: + RETVAL + +void +Botan__Base64_Decoder::DESTROY() + PREINIT: + ObjectInfo *THIS_oi; + CODE: + if ( THIS_oi->should_delete() ) + try { + delete THIS; + } + catch (const std::exception &e) { + croak(e.what()); + } + + +# =========================== Botan::Base64_Encoder ========================== + +MODULE = Botan PACKAGE = Botan::Base64_Encoder + +Botan__Base64_Encoder * +Botan__Base64_Encoder::new(breaks = false, length = 72) + bool breaks; + Botan__u32bit length; + CODE: + try { + RETVAL = new Botan__Base64_Encoder(breaks, length); + } + catch (const std::exception &e) { + croak(e.what()); + } + OUTPUT: + RETVAL + +void +Botan__Base64_Encoder::DESTROY() + PREINIT: + ObjectInfo *THIS_oi; + CODE: + if ( THIS_oi->should_delete() ) + try { + delete THIS; + } + catch (const std::exception &e) { + croak(e.what()); + } + + +# ============================ Botan::Hex_Decoder ============================ + +MODULE = Botan PACKAGE = Botan::Hex_Decoder + +Botan__Hex_Decoder * +Botan__Hex_Decoder::new(checking = Botan::NONE) + int checking; + CODE: + try { + using namespace Botan; + RETVAL = new Hex_Decoder((Decoder_Checking)checking); + } + catch (const std::exception &e) { + croak(e.what()); + } + OUTPUT: + RETVAL + +void +Botan__Hex_Decoder::DESTROY() + PREINIT: + ObjectInfo *THIS_oi; + CODE: + if ( THIS_oi->should_delete() ) + try { + delete THIS; + } + catch (const std::exception &e) { + croak(e.what()); + } + + +# ============================ Botan::Hex_Encoder ============================ + +MODULE = Botan PACKAGE = Botan::Hex_Encoder + +Botan__Hex_Encoder * +Botan__Hex_Encoder::new(breaks = false, length = 72, lcase = false) + bool breaks; + Botan__u32bit length; + bool lcase; + CODE: + try { + using Botan::Hex_Encoder; + RETVAL = new Hex_Encoder(breaks, length, + lcase ? Hex_Encoder::Lowercase : Hex_Encoder::Uppercase); + } + catch (const std::exception &e) { + croak(e.what()); + } + OUTPUT: + RETVAL + +void +Botan__Hex_Encoder::DESTROY() + PREINIT: + ObjectInfo *THIS_oi; + CODE: + if ( THIS_oi->should_delete() ) + try { + delete THIS; + } + catch (const std::exception &e) { + croak(e.what()); + } + + +# ================================ Botan::OID ================================ + +MODULE = Botan PACKAGE = Botan::OID + +Botan__OID * +Botan__OID::new(s) + char *s; + CODE: + try { + RETVAL = new Botan__OID(s); + } + catch (const std::exception &e) { + croak(e.what()); + } + OUTPUT: + RETVAL + +void +Botan__OID::DESTROY() + CODE: + try { + delete THIS; + } + catch (const std::exception &e) { + croak(e.what()); + } + +char * +Botan__OID::as_string() + CODE: + try { + RETVAL = const_cast<char *>(THIS->as_string().c_str()); + } + catch (const std::exception &e) { + croak(e.what()); + } + OUTPUT: + RETVAL + + +# ================================ Botan::OIDS ================================ + +MODULE = Botan PACKAGE = Botan::OIDS + +void +add_oid(oid, name) + Botan__OID *oid; + char *name; + CODE: + try { + Botan::OIDS::add_oid(*oid, name); + } + catch (const std::exception &e) { + croak(e.what()); + } + +char * +lookup_by_oid(oid) + Botan__OID *oid; + CODE: + try { + RETVAL = const_cast<char *>(Botan::OIDS::lookup(*oid).c_str()); + } + catch (const std::exception &e) { + croak(e.what()); + } + OUTPUT: + RETVAL + +Botan__OID * +lookup_by_name(name) + char *name; + CODE: + try { + RETVAL = new Botan__OID(Botan::OIDS::lookup(name)); + } + catch (const std::exception &e) { + croak(e.what()); + } + char const * CLASS = "Botan::OID"; + OUTPUT: + RETVAL + +int +have_oid(name) + char *name; + CODE: + try { + RETVAL = Botan::OIDS::have_oid(name); + } + catch (const std::exception &e) { + croak(e.what()); + } + OUTPUT: + RETVAL + + +# ================================ Botan::Pipe ================================ + +MODULE = Botan PACKAGE = Botan::Pipe + +Botan__Pipe * +Botan__Pipe::new(...) + CODE: + for (I32 i = 1; i < items; i++) + { + if ( !sv_isobject(ST(i)) || (SvTYPE(SvRV(ST(i))) != SVt_PVMG) ) + croak("Botan::Pipe::new() -- arg %u is not " + "a blessed SV reference", i +1); + if ( !sv_derived_from(ST(i), "Botan::Filter") ) + croak("Botan::Pipe::new() -- arg %u is not " + "an object derived from Botan::Filter", i +1); + MAGIC *mg = mg_find(SvRV(ST(i)), '~'); + if ( mg == 0 + || mg->mg_len != sizeof(ObjectInfo) + || *(I32 *)(mg->mg_ptr) != ObjectInfo::SIGNVAL ) + croak("Botan::Pipe::new() -- arg %u has no " + "valid private magic data (ObjectInfo)", i +1); + } + try { + RETVAL = new Botan__Pipe(); + for (I32 i = 1; i < items; i++) + { + SV *osv = (SV *)SvRV(ST(i)); + ObjectInfo *oi = (ObjectInfo *)(mg_find(osv, '~')->mg_ptr); + RETVAL->append((Botan__Filter *)(SvIV(osv))); + oi->set_delete_no(); + } + } + catch (const std::exception &e) { + croak(e.what()); + } + OUTPUT: + RETVAL + +void +Botan__Pipe::DESTROY() + PREINIT: + ObjectInfo *THIS_oi; + CODE: + try { + delete THIS; + } + catch (const std::exception &e) { + croak(e.what()); + } + +void +Botan__Pipe::write(s) + SV *s; + PREINIT: + ObjectInfo *THIS_oi; + CODE: + STRLEN len; + char *ptr = SvPV(s, len); + try { + THIS->write((unsigned char *)ptr, len); + } + catch (const std::exception &e) { + croak(e.what()); + } + +void +Botan__Pipe::process_msg(s) + SV *s; + PREINIT: + ObjectInfo *THIS_oi; + CODE: + STRLEN len; + char *ptr = SvPV(s, len); + try { + THIS->process_msg((unsigned char *)ptr, len); + } + catch (const std::exception &e) { + croak(e.what()); + } + +Botan__u32bit +Botan__Pipe::remaining(msgno = Botan::Pipe::DEFAULT_MESSAGE) + Botan__u32bit msgno; + PREINIT: + ObjectInfo *THIS_oi; + CODE: + try { + RETVAL = THIS->remaining(msgno); + } + catch (const std::exception &e) { + croak(e.what()); + } + OUTPUT: + RETVAL + +SV * +Botan__Pipe::read(len = 0xFFFFFFFF, msgno = Botan::Pipe::DEFAULT_MESSAGE) + Botan__u32bit len; + Botan__u32bit msgno; + PREINIT: + ObjectInfo *THIS_oi; + CODE: + try { + if ( len > THIS->remaining(msgno) ) + len = THIS->remaining(msgno); + RETVAL = NEWSV(0, len); + SvPOK_on(RETVAL); + if ( len > 0 ) + SvCUR_set(RETVAL, THIS->read((unsigned char *)SvPVX(RETVAL), + len, msgno)); + } + catch (const std::exception &e) { + croak(e.what()); + } + OUTPUT: + RETVAL + +SV * +Botan__Pipe::peek(len = 0xFFFFFFFF, offset = 0, \ + msgno = Botan::Pipe::DEFAULT_MESSAGE) + Botan__u32bit len; + Botan__u32bit offset; + Botan__u32bit msgno; + PREINIT: + ObjectInfo *THIS_oi; + CODE: + try { + if ( len > THIS->remaining(msgno) ) + len = THIS->remaining(msgno); + RETVAL = NEWSV(0, len); + SvPOK_on(RETVAL); + if ( len > 0 ) + SvCUR_set(RETVAL, THIS->peek((unsigned char *)SvPVX(RETVAL), + len, offset, msgno)); + } + catch (const std::exception &e) { + croak(e.what()); + } + OUTPUT: + RETVAL + +Botan__u32bit +Botan__Pipe::default_msg() + PREINIT: + ObjectInfo *THIS_oi; + CODE: + try { + RETVAL = THIS->default_msg(); + } + catch (const std::exception &e) { + croak(e.what()); + } + OUTPUT: + RETVAL + +void +Botan__Pipe::set_default_msg(msgno) + Botan__u32bit msgno; + PREINIT: + ObjectInfo *THIS_oi; + CODE: + try { + THIS->set_default_msg(msgno); + } + catch (const std::exception &e) { + croak(e.what()); + } + +Botan__u32bit +Botan__Pipe::message_count() + PREINIT: + ObjectInfo *THIS_oi; + CODE: + try { + RETVAL = THIS->message_count(); + } + catch (const std::exception &e) { + croak(e.what()); + } + OUTPUT: + RETVAL + +bool +Botan__Pipe::end_of_data() + PREINIT: + ObjectInfo *THIS_oi; + CODE: + try { + RETVAL = THIS->end_of_data(); + } + catch (const std::exception &e) { + croak(e.what()); + } + OUTPUT: + RETVAL + +void +Botan__Pipe::start_msg() + PREINIT: + ObjectInfo *THIS_oi; + CODE: + try { + THIS->start_msg(); + } + catch (const std::exception &e) { + croak(e.what()); + } + +void +Botan__Pipe::end_msg() + PREINIT: + ObjectInfo *THIS_oi; + CODE: + try { + THIS->end_msg(); + } + catch (const std::exception &e) { + croak(e.what()); + } + +void +Botan__Pipe::reset() + PREINIT: + ObjectInfo *THIS_oi; + CODE: + try { + THIS->reset(); + } + catch (const std::exception &e) { + croak(e.what()); + } + + +# ========================== Botan::X509_Certificate ========================== + +MODULE = Botan PACKAGE = Botan::X509_Certificate + +Botan__X509_Certificate * +Botan__X509_Certificate::new(char *fn) + CODE: + try { + RETVAL = new Botan__X509_Certificate(fn); + } + catch (const std::exception &e) { + croak(e.what()); + } + OUTPUT: + RETVAL + +void +Botan__X509_Certificate::DESTROY() + CODE: + try { + delete THIS; + } + catch (const std::exception &e) { + croak(e.what()); + } + +unsigned int +Botan__X509_Certificate::x509_version() + CODE: + try { + RETVAL = THIS->x509_version(); + } + catch (const std::exception &e) { + croak(e.what()); + } + OUTPUT: + RETVAL + +char * +Botan__X509_Certificate::start_time() + CODE: + try { + RETVAL = const_cast<char *>(THIS->start_time().c_str()); + } + catch (const std::exception &e) { + croak(e.what()); + } + OUTPUT: + RETVAL + +char * +Botan__X509_Certificate::end_time() + CODE: + try { + RETVAL = const_cast<char *>(THIS->end_time().c_str()); + } + catch (const std::exception &e) { + croak(e.what()); + } + OUTPUT: + RETVAL + +char * +Botan__X509_Certificate::subject_info(char *info) + CODE: + try { + std::vector<std::string> s = THIS->subject_info(info); + + if(s.size() > 0) + RETVAL = const_cast<char *>(s[0].c_str()); + else + RETVAL = "err"; + } + catch (const std::exception &e) { + croak(e.what()); + } + OUTPUT: + RETVAL + +char * +Botan__X509_Certificate::issuer_info(char *info) + CODE: + try { + std::vector<std::string> s = THIS->subject_info(info); + + if(s.size() > 0) + RETVAL = const_cast<char *>(s[0].c_str()); + else + RETVAL = "err"; + } + catch (const std::exception &e) { + croak(e.what()); + } + OUTPUT: + RETVAL + +Botan__X509_DN * +Botan__X509_Certificate::subject_dn() + CODE: + try { + RETVAL = new Botan__X509_DN(THIS->subject_dn()); + } + catch (const std::exception &e) { + croak(e.what()); + } + char const * CLASS = "Botan::X509_DN"; + OUTPUT: + RETVAL + +Botan__X509_DN * +Botan__X509_Certificate::issuer_dn() + CODE: + try { + RETVAL = new Botan__X509_DN(THIS->issuer_dn()); + } + catch (const std::exception &e) { + croak(e.what()); + } + char const * CLASS = "Botan::X509_DN"; + OUTPUT: + RETVAL + + +# ============================== Botan::X509_DN ============================== + +MODULE = Botan PACKAGE = Botan::X509_DN + +Botan__X509_DN * +Botan__X509_DN::new() + CODE: + try { + RETVAL = new Botan__X509_DN(); + } + catch (const std::exception &e) { + croak(e.what()); + } + OUTPUT: + RETVAL + +void +Botan__X509_DN::DESTROY() + CODE: + try { + delete THIS; + } + catch (const std::exception &e) { + croak(e.what()); + } + +AV * +Botan__X509_DN::get_attributes() + CODE: + try { + using namespace std; + using namespace Botan; + + typedef multimap<OID, string>::const_iterator rdn_iter; + + multimap<OID, string> const &atrmmap = THIS->get_attributes(); + RETVAL = newAV(); + for(rdn_iter i = atrmmap.begin(); i != atrmmap.end(); i++) + { + string const &atr = i->first.as_string(); + string const &val = i->second; + av_push(RETVAL, newSVpvn(atr.c_str(), atr.length())); + av_push(RETVAL, newSVpvn(val.c_str(), val.length())); + } + } + catch (const std::exception &e) { + croak(e.what()); + } + OUTPUT: + RETVAL diff --git a/lib/wrap/perl-xs/Changes b/lib/wrap/perl-xs/Changes new file mode 100644 index 000000000..5f32b0c63 --- /dev/null +++ b/lib/wrap/perl-xs/Changes @@ -0,0 +1,4 @@ +Revision history for Perl extension to Botan. + +0.01 Fri, 20 Feb 2004 15:10:50 +0100 + - first version diff --git a/lib/wrap/perl-xs/MANIFEST b/lib/wrap/perl-xs/MANIFEST new file mode 100644 index 000000000..b9d8454d6 --- /dev/null +++ b/lib/wrap/perl-xs/MANIFEST @@ -0,0 +1,15 @@ +Botan.pm +Botan.xs +Changes +MANIFEST +Makefile.PL +data/ca.cert.der +data/ca.cert.pem +t/base64.t +t/filt.t +t/hex.t +t/oid.t +t/pipe.t +t/testutl.pl +t/x509cert.t +typemap diff --git a/lib/wrap/perl-xs/Makefile.PL b/lib/wrap/perl-xs/Makefile.PL new file mode 100644 index 000000000..5a3276aec --- /dev/null +++ b/lib/wrap/perl-xs/Makefile.PL @@ -0,0 +1,29 @@ +use ExtUtils::MakeMaker; + +my ($cc, $cflags, $lids); +if ( $^O eq 'MSWin32' ) +{ +# $cflags = ''; +# $libs = ':nosearch -lgdi32 -llibeay32'; +} +else +{ + $cc = 'g++'; + $cflags = $Config::Config{ccflags} . ' -Wno-write-strings -fexceptions ' . qx( botan-config-1.11 --cflags ); + $libs = qx( botan-config --libs ); +} + +WriteMakefile( + 'NAME' => 'Botan', + 'DISTNAME' => 'Botan-XS', + 'VERSION_FROM' => 'Botan.pm', # finds $VERSION + 'XSOPT' => '-C++', + 'CC' => $cc, + 'LD' => '$(CC)', + 'CCFLAGS' => $cflags, + 'LIBS' => [ $libs ], + 'OPTIMIZE' => '-g', +# 'clean' => { +# 'FILES' => 'neco.p12 rnd', +# }, +); diff --git a/lib/wrap/perl-xs/data/ca.cert.der b/lib/wrap/perl-xs/data/ca.cert.der Binary files differnew file mode 100644 index 000000000..d6ed8aeaf --- /dev/null +++ b/lib/wrap/perl-xs/data/ca.cert.der diff --git a/lib/wrap/perl-xs/data/ca.cert.pem b/lib/wrap/perl-xs/data/ca.cert.pem new file mode 100644 index 000000000..012913b26 --- /dev/null +++ b/lib/wrap/perl-xs/data/ca.cert.pem @@ -0,0 +1,17 @@ +-----BEGIN CERTIFICATE----- +MIICxDCCAi2gAwIBAgIBEjANBgkqhkiG9w0BAQUFADBSMQswCQYDVQQGEwJDWjER +MA8GA1UEChMISUNaIGEucy4xGDAWBgNVBAMTD1Rlc3QgcHJpbWFyeSBDQTEWMBQG +CSqGSIb3DQEJARYHY2FAaS5jejAeFw0wMDA4MjAyMTQ4MDBaFw0wMjA4MTAyMTQ4 +MDBaME8xCzAJBgNVBAYTAkNaMREwDwYDVQQKEwhJQ1ogYS5zLjEVMBMGA1UEAxMM +VGVzdCBzaWduIENBMRYwFAYJKoZIhvcNAQkBFgdjYUBpLmN6MIGfMA0GCSqGSIb3 +DQEBAQUAA4GNADCBiQKBgQCo2GReNqwU0/8bZZua5hgYaVHvD9QAmfILNXD25jRk +C8lqe5m/GzbmftSUso5HyUy1t+qzvRDTmxK8uRn0P00Mqj9gjwF8PGQvZE/FrDF7 +rta9GCcH4n2GfQ0iexlhRZW44AfOD4HCgq38Z0bzBclsvUslBWe1AT+S5+chZ5Wb +UwIDAQABo4GsMIGpMAwGA1UdEwQFMAMBAf8wHQYDVR0OBBYEFLXqc1b1DOfGehii +k4Z+/ih9BYZmMHoGA1UdIwRzMHGAFL7x2ToS4RDAbDJu4fHnzzGjfGmgoVakVDBS +MQswCQYDVQQGEwJDWjERMA8GA1UEChMISUNaIGEucy4xGDAWBgNVBAMTD1Rlc3Qg +cHJpbWFyeSBDQTEWMBQGCSqGSIb3DQEJARYHY2FAaS5jeoIBADANBgkqhkiG9w0B +AQUFAAOBgQAKD9ku9kKXUGhSw8KuWJXTnEsIUzDtgmREBEUOtEvGfU45vogWN7ZL +9fQZ1deywN4RJ4T5ZTTcCTPodOdG+IXLJ+uPn/m9iQ/D86c3GKS3yx4JNAn5PH1m +qLsMYVjbFD2uREZQsqbg3RT6L1D8+oK0pN379u3bD6oJx/qa7+F4Jg== +-----END CERTIFICATE----- diff --git a/lib/wrap/perl-xs/t/base64.t b/lib/wrap/perl-xs/t/base64.t new file mode 100644 index 000000000..f0973e13e --- /dev/null +++ b/lib/wrap/perl-xs/t/base64.t @@ -0,0 +1,273 @@ +# vim: set ft=perl: +# Before `make install' is performed this script should be runnable with +# `make test'. After `make install' it should work as `perl test.pl' + +######################### We start with some black magic to print on failure. + +# Change 1..1 below to 1..last_test_to_print . +# (It may become useful if the test is moved to ./t subdirectory.) + +BEGIN { $| = 1; print "1..24\n"; } +END { print "not ok 1\n" unless $loaded; } + +require 't/testutl.pl'; +use Botan; + +$loaded = 1; +print "ok 1\n"; + +######################### End of black magic. + +# Insert your test code below (better if it prints "ok 13" +# (correspondingly "not ok 13") depending on the success of chunk 13 +# of the test code): + +use strict; + +# Data prep + +my $botan_lic_b64_garbage = <<'EOF'; +Q29weXJpZ2h0IChDKSAxOTk5LTIwMDQgVGhlIEJvdGFuIFByb2plY3QuIEFsbCBy__� +aWdodHMgcmVzZXJ2ZWQuCgpSZWRpc3RyaWJ1dGlvbiBhbmQgdXNlIGluIHNvdXJj$$*: +ZSBhbmQgYmluYXJ5IGZvcm1zLCBmb3IgYW55IHVzZSwgd2l0aCBvciB3aXRob3V0!@#$%^&*( +Cm1vZGlmaWNhdGlvbiwgaXMgcGVybWl0dGVkIHByb3ZpZGVkIHRoYXQgdGhlIGZv[\] +bGxvd2luZyBjb25kaXRpb25zIGFyZSBtZXQ6CgoxLiBSZWRpc3RyaWJ1dGlvbnMg'~` +b2Ygc291cmNlIGNvZGUgbXVzdCByZXRhaW4gdGhlIGFib3ZlIGNvcHlyaWdodCBu() +b3RpY2UsIHRoaXMKbGlzdCBvZiBjb25kaXRpb25zLCBhbmQgdGhlIGZvbGxvd2lu +ZyBkaXNjbGFpbWVyLgoKMi4gUmVkaXN0cmlidXRpb25zIGluIGJpbmFyeSBmb3Jt +IG11c3QgcmVwcm9kdWNlIHRoZSBhYm92ZSBjb3B5cmlnaHQgbm90aWNlLAp0aGlz +IGxpc3Qgb2YgY29uZGl0aW9ucywgYW5kIHRoZSBmb2xsb3dpbmcgZGlzY2xhaW1l +ciBpbiB0aGUgZG9jdW1lbnRhdGlvbgphbmQvb3Igb3RoZXIgbWF0ZXJpYWxzIHBy_,^ +b3ZpZGVkIHdpdGggdGhlIGRpc3RyaWJ1dGlvbi4KClRISVMgU09GVFdBUkUgSVMg{|}~~~~~ +UFJPVklERUQgQlkgVEhFIEFVVEhPUihTKSAiQVMgSVMiIEFORCBBTlkgRVhQUkVT~~~~~~~~ +UyBPUiBJTVBMSUVECldBUlJBTlRJRVMsIElOQ0xVRElORywgQlVUIE5PVCBMSU1J__:; +VEVEIFRPLCBUSEUgSU1QTElFRCBXQVJSQU5USUVTIE9GCk1FUkNIQU5UQUJJTElU +WSBBTkQgRklUTkVTUyBGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UsIEFSRSBESVND +TEFJTUVELgoKSU4gTk8gRVZFTlQgU0hBTEwgVEhFIEFVVEhPUihTKSBPUiBDT05U +UklCVVRPUihTKSBCRSBMSUFCTEUgRk9SIEFOWSBESVJFQ1QsCklORElSRUNULCBJ +TkNJREVOVEFMLCBTUEVDSUFMLCBFWEVNUExBUlksIE9SIENPTlNFUVVFTlRJQUwg +REFNQUdFUyAoSU5DTFVESU5HLApCVVQgTk9UIExJTUlURUQgVE8sIFBST0NVUkVN +RU5UIE9GIFNVQlNUSVRVVEUgR09PRFMgT1IgU0VSVklDRVM7IExPU1MgT0YgVVNF +LApEQVRBLCBPUiBQUk9GSVRTOyBPUiBCVVNJTkVTUyBJTlRFUlJVUFRJT04pIEhP +V0VWRVIgQ0FVU0VEIEFORCBPTiBBTlkgVEhFT1JZIE9GCkxJQUJJTElUWSwgV0hF +VEhFUiBJTiBDT05UUkFDVCwgU1RSSUNUIExJQUJJTElUWSwgT1IgVE9SVCAoSU5D +TFVESU5HIE5FR0xJR0VOQ0UKT1IgT1RIRVJXSVNFKSBBUklTSU5HIElOIEFOWSBX +QVkgT1VUIE9GIFRIRSBVU0UgT0YgVEhJUyBTT0ZUV0FSRSwgRVZFTiBJRgpBRFZJ +U0VEIE9GIFRIRSBQT1NTSUJJTElUWSBPRiBTVUNIIERBTUFHRS4K +EOF + +my $botan_lic_b64_ws = $botan_lic_b64_garbage; +$botan_lic_b64_ws =~ s/[^A-Za-z0-9+\/= \n]//g; + +my $botan_lic_b64 = $botan_lic_b64_ws; +$botan_lic_b64 =~ s/[ \n]//g; + + +my $botan_lic = <<'EOF'; +Copyright (C) 1999-2004 The Botan Project. All rights reserved. + +Redistribution and use in source and binary forms, for any use, with or without +modification, is permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this +list of conditions, and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions, and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) "AS IS" AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ARE DISCLAIMED. + +IN NO EVENT SHALL THE AUTHOR(S) OR CONTRIBUTOR(S) BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +EOF + + +# Decoder... + +my $f; + +eval { $f = Botan::Base64_Decoder->new(&Botan::NONE); }; +print "not " if $@ || !defined $f; +print "ok 2\n"; + +my $dec; +eval { $dec = Botan::Pipe->new($f); }; +print "not " if $@ || !defined $dec; +print "ok 3\n"; + +eval { $f = Botan::Base64_Decoder->new(&Botan::IGNORE_WS); }; +print "not " if $@ || !defined $f; +print "ok 4\n"; + +my $dec_is; +eval { $dec_is = Botan::Pipe->new($f); }; +print "not " if $@ || !defined $dec_is; +print "ok 5\n"; + +eval { $f = Botan::Base64_Decoder->new(&Botan::FULL_CHECK); }; +print "not " if $@ || !defined $f; +print "ok 6\n"; + +my $dec_fc; +eval { $dec_fc = Botan::Pipe->new($f); }; +print "not " if $@ || !defined $dec_fc; +print "ok 7\n"; + + +# Testing clean base64 input + +my $data; + +undef $data; +eval { + $dec->process_msg($botan_lic_b64); + $data = $dec->read(); +}; + +print "not " if $@ || $data ne $botan_lic; +print "ok 8\n"; + +undef $data; +eval { + $dec_is->process_msg($botan_lic_b64); + $data = $dec_is->read(); +}; + +print "not " if $@ || $data ne $botan_lic; +print "ok 9\n"; + +undef $data; +eval { + $dec_fc->process_msg($botan_lic_b64); + $data = $dec_fc->read(); +}; + +print "not " if $@ || $data ne $botan_lic; +print "ok 10\n"; + + +# Testing base64 input with whitespaces + +undef $data; +eval { + $dec->process_msg($botan_lic_b64_ws); + $dec->set_default_msg(1); + $data = $dec->read(); +}; + +print "not " if $@ || $data ne $botan_lic; +print "ok 11\n"; + +undef $data; +eval { + $dec_is->process_msg($botan_lic_b64_ws); + $dec_is->set_default_msg(1); + $data = $dec_is->read(); +}; + +print "not " if $@ || $data ne $botan_lic; +print "ok 12\n"; + +undef $data; +eval { + $dec_fc->process_msg($botan_lic_b64_ws); + $dec_fc->set_default_msg(1); + $data = $dec_fc->read(); +}; + +print "not " unless $@ && !defined $data; +print "ok 13\n"; + + +# Testing base64 input with garbage + +undef $data; +eval { + $dec->process_msg($botan_lic_b64_garbage); + $dec->set_default_msg(2); + $data = $dec->read(); +}; + +print "not " if $@ || $data ne $botan_lic; +print "ok 14\n"; + +undef $data; +eval { + $dec_is->process_msg($botan_lic_b64_garbage); + $dec_is->set_default_msg(2); + $data = $dec_is->read(); +}; + +print "not " unless $@ && !defined $data; +print "ok 15\n"; + +undef $data; +eval { + $dec_fc->process_msg($botan_lic_b64_garbage); + $dec_fc->set_default_msg(2); + $data = $dec_fc->read(); +}; + +print "not " unless $@ && !defined $data; +print "ok 16\n"; + + +# Encoder... + +eval { $f = Botan::Base64_Encoder->new(); }; +print "not " if $@ || !defined $f; +print "ok 17\n"; + +my $enc; +eval { $enc = Botan::Pipe->new($f); }; +print "not " if $@ || !defined $enc; +print "ok 18\n"; + +eval { $f = Botan::Base64_Encoder->new(1, 5); }; +print "not " if $@ || !defined $f; +print "ok 19\n"; + +my $enc2; +eval { $enc2 = Botan::Pipe->new($f); }; +print "not " if $@ || !defined $enc2; +print "ok 20\n"; + +undef $data; +eval { + $enc->process_msg("Hello\n"); + $data = $enc->read(); +}; +print "not " if $@ || $data ne "SGVsbG8K"; +print "ok 21\n"; + +undef $data; +eval { + $enc2->process_msg("Hello\n"); + $data = $enc2->read(); +}; +print "not " if $@ || $data ne "SGVsb\nG8K\n"; +print "ok 22\n"; + + +# Encoder with decoder... + +my $p; +eval { + $p = Botan::Pipe->new( + Botan::Base64_Encoder->new(), + Botan::Base64_Decoder->new(), + ); +}; +print "not " if $@ || !defined $p; +print "ok 23\n"; + +print "not " unless random_message_ok($p); +print "ok 24\n"; diff --git a/lib/wrap/perl-xs/t/filt.t b/lib/wrap/perl-xs/t/filt.t new file mode 100644 index 000000000..2a7b4c8ba --- /dev/null +++ b/lib/wrap/perl-xs/t/filt.t @@ -0,0 +1,56 @@ +# vim: set ft=perl: +# Before `make install' is performed this script should be runnable with +# `make test'. After `make install' it should work as `perl test.pl' + +######################### We start with some black magic to print on failure. + +# Change 1..1 below to 1..last_test_to_print . +# (It may become useful if the test is moved to ./t subdirectory.) + +BEGIN { $| = 1; print "1..5\n"; } +END { print "not ok 1\n" unless $loaded; } + +use Botan; + +$loaded = 1; +print "ok 1\n"; + +######################### End of black magic. + +# Insert your test code below (better if it prints "ok 13" +# (correspondingly "not ok 13") depending on the success of chunk 13 +# of the test code): + +use strict; + +my $pipe = Botan::Pipe->new(Botan::Hex_Encoder->new()); + +print "not " unless $pipe; +print "ok 2\n"; + +$pipe->process_msg('FOO'); + +print "not " if $pipe->read() ne '464F4F'; +print "ok 3\n"; + +$pipe = Botan::Pipe->new(Botan::Hex_Encoder->new(0, 0, 1)); + +print "not " unless $pipe; +print "ok 4\n"; + +$pipe->process_msg('FOO'); + +print "not " if $pipe->read() ne '464f4f'; +print "ok 5\n"; + + + + + + +#my $pipe = Botan::Pipe->new(Botan::Base64_Encoder->new()); +#$pipe->process_msg('FOO'); +# +#print "not " if $pipe->read() ne 'Rk9P'; +#print "ok 4\n"; + diff --git a/lib/wrap/perl-xs/t/hex.t b/lib/wrap/perl-xs/t/hex.t new file mode 100644 index 000000000..6f447b25c --- /dev/null +++ b/lib/wrap/perl-xs/t/hex.t @@ -0,0 +1,256 @@ +# vim: set ft=perl: +# Before `make install' is performed this script should be runnable with +# `make test'. After `make install' it should work as `perl test.pl' + +######################### We start with some black magic to print on failure. + +# Change 1..1 below to 1..last_test_to_print . +# (It may become useful if the test is moved to ./t subdirectory.) + +BEGIN { $| = 1; print "1..24\n"; } +END { print "not ok 1\n" unless $loaded; } + +require 't/testutl.pl'; +use Botan; + +$loaded = 1; +print "ok 1\n"; + +######################### End of black magic. + +# Insert your test code below (better if it prints "ok 13" +# (correspondingly "not ok 13") depending on the success of chunk 13 +# of the test code): + +use strict; + +# Data prep + +my ($hex, $hex_ws, $hex_garbage); +while ( $_ = <DATA> ) +{ + $hex_garbage .= $_; + s/[^[:xdigit:][:space:]]//g; + $hex_ws .= $_; + s/[^[:xdigit:]]//g; + $hex .= $_; +} +my $data_test = pack("H*", $hex); + +# Decoder... + +my $f; + +eval { $f = Botan::Hex_Decoder->new(&Botan::NONE); }; +print "not " if $@ || !defined $f; +print "ok 2\n"; + +my $dec; +eval { $dec = Botan::Pipe->new($f); }; +print "not " if $@ || !defined $dec; +print "ok 3\n"; + +eval { $f = Botan::Hex_Decoder->new(&Botan::IGNORE_WS); }; +print "not " if $@ || !defined $f; +print "ok 4\n"; + +my $dec_is; +eval { $dec_is = Botan::Pipe->new($f); }; +print "not " if $@ || !defined $dec_is; +print "ok 5\n"; + +eval { $f = Botan::Hex_Decoder->new(&Botan::FULL_CHECK); }; +print "not " if $@ || !defined $f; +print "ok 6\n"; + +my $dec_fc; +eval { $dec_fc = Botan::Pipe->new($f); }; +print "not " if $@ || !defined $dec_fc; +print "ok 7\n"; + + +# Testing clean hexadecimal input + +my $data; + +undef $data; +eval { + $dec->process_msg($hex); + $data = $dec->read(); +}; + +print "not " if $@ || $data ne $data_test; +print "ok 8\n"; + +undef $data; +eval { + $dec_is->process_msg($hex); + $data = $dec_is->read(); +}; + +print "not " if $@ || $data ne $data_test; +print "ok 9\n"; + +undef $data; +eval { + $dec_fc->process_msg($hex); + $data = $dec_fc->read(); +}; + +print "not " if $@ || $data ne $data_test; +print "ok 10\n"; + + +# Testing hexadecimal input with whitespaces + +undef $data; +eval { + $dec->process_msg($hex_ws); + $dec->set_default_msg(1); + $data = $dec->read(); +}; + +print "not " if $@ || $data ne $data_test; +print "ok 11\n"; + +undef $data; +eval { + $dec_is->process_msg($hex_ws); + $dec_is->set_default_msg(1); + $data = $dec_is->read(); +}; + +print "not " if $@ || $data ne $data_test; +print "ok 12\n"; + +undef $data; +eval { + $dec_fc->process_msg($hex_ws); + $dec_fc->set_default_msg(1); + $data = $dec_fc->read(); +}; + +print "not " unless $@ && !defined $data; +print "ok 13\n"; + + +# Testing hexadecimal input with garbage + +undef $data; +eval { + $dec->process_msg($hex_garbage); + $dec->set_default_msg(2); + $data = $dec->read(); +}; + +print "not " if $@ || $data ne $data_test; +print "ok 14\n"; + +undef $data; +eval { + $dec_is->process_msg($hex_garbage); + $dec_is->set_default_msg(2); + $data = $dec_is->read(); +}; + +print "not " unless $@ && !defined $data; +print "ok 15\n"; + +undef $data; +eval { + $dec_fc->process_msg($hex_garbage); + $dec_fc->set_default_msg(2); + $data = $dec_fc->read(); +}; + +print "not " unless $@ && !defined $data; +print "ok 16\n"; + + +# Encoder... + +eval { $f = Botan::Hex_Encoder->new(); }; +print "not " if $@ || !defined $f; +print "ok 17\n"; + +my $enc; +eval { $enc = Botan::Pipe->new($f); }; +print "not " if $@ || !defined $enc; +print "ok 18\n"; + +eval { $f = Botan::Hex_Encoder->new(1, 5, 1); }; +print "not " if $@ || !defined $f; +print "ok 19\n"; + +my $enc2; +eval { $enc2 = Botan::Pipe->new($f); }; +print "not " if $@ || !defined $enc2; +print "ok 20\n"; + +undef $data; +eval { + $enc->process_msg("Hello\n"); + $data = $enc->read(); +}; +print "not " if $@ || $data ne "48656C6C6F0A"; +print "ok 21\n"; + +undef $data; +eval { + $enc2->process_msg("Hello\n"); + $data = $enc2->read(); +}; +print "not " if $@ || $data ne "48656\nc6c6f\n0a\n"; +print "ok 22\n"; + + +# Encoder with decoder... + +my $p; +eval { + $p = Botan::Pipe->new( + Botan::Hex_Encoder->new(), + Botan::Hex_Decoder->new(), + ); +}; +print "not " if $@ || !defined $p; +print "ok 23\n"; + +print "not " unless random_message_ok($p); +print "ok 24\n"; + + + +__DATA__ +cb13 4a4d 7522 1fd3 c6f6 7786 d04b 3043 ..JMu"....w..K.. +4552 4bcf 4d2b 9d71 0cfe 4d6a 1caf bcfd .RK.M+.q..Mj.... +8f91 6151 ff85 e900 7e6a bafc 15e9 ae51 ...Q....~j.....Q +b14b 7210 bb40 5958 2b82 d49e b808 68a5 .Kr..@YX+.....h. +7945 9dec f686 9b98 989e 826d 8088 6ee7 y..........m..n. +d066 1eac 8c34 c461 bb54 7726 87ab d681 .........Tw&.... +a0be 52e5 1128 0cf2 759e cb2d e690 4ed9 ..R..(..u..-..N. +7e88 bda7 2523 4a0f 185a 02b1 f898 fc41 ~...%#J..Z...... +dd48 fa87 945d 7611 b8c9 a50a 2de2 b670 .H...]v.....-..p +0056 c8be 2cbb e7d0 1e70 4a3d 79f0 dce9 .V..,....pJ=y... +b57f 154b 2b3a db73 f086 de11 9f3e 1641 ...K+:.s.....>.. +3a28 8b9b bb0f 682b 80db b791 89e0 62c0 :(....h+........ +7204 db97 5432 2eb0 a04e f38e 809f 7223 r...T....N....r# +912e e552 1452 6dd2 e09f dd06 c715 7c1a ...R.Rm.......|. +fe3d d6cc b6d0 a17a 27d7 4327 4e43 8af3 .=.....z'..'N... +6eb5 e9f8 bfe9 34c3 6636 8243 358f 966d n..............m +7d87 d17b 5c37 6acb 4972 f4ec 6806 bbde }..{\.j.Ir..h... +2689 a019 a9e2 4101 7fe2 de72 bc03 eb5e &..........r...^ +b699 2d6b f8cd a08e 6e01 edfc a81a 94b6 ..-k....n....... +9073 15fb efb2 c8d9 9f85 6633 85f1 e9d0 .s.............. +20ce 578b ab9d 2e51 b947 69bf fba5 82c6 .W....Q.Gi..... +2ed0 dd36 d679 a399 7db3 8a0d cdef 0eda .....y..}....... +e761 e7f1 5b17 3f67 0c83 215a eddf 9d2a ....[.?g..!Z...* +5e70 0a77 c92e 94e1 a82b fd7c f10a 894f ^p.w.....+.|...O +2955 f0e8 7398 f409 2040 b797 da03 a5a6 )U..s... @...... +7ba4 c3c9 2659 b9f7 6a56 e17a b481 983f {...&Y..jV.z...? +00ed 3cc8 5a22 ad5c b6e0 3566 d717 35a6 ..<.Z".\........ +1523 4104 de63 477e fd24 68e5 e816 98df .#....G~.$h..... +1747 417e db72 a76a be5b b9dc 3dfb 2d05 .G.~.r.j.[..=.-. +d27f e597 eafc 9a29 15c5 792d 9c88 9aea .......)..y-.... +485e e431 96c3 7723 da6d 28b2 477a fd12 H^....w#.m(.Gz.. +e645 5dcd 7d5a d8b4 7acc 10b2 b41a e11d ..].}Z..z....... diff --git a/lib/wrap/perl-xs/t/oid.t b/lib/wrap/perl-xs/t/oid.t new file mode 100644 index 000000000..66204541f --- /dev/null +++ b/lib/wrap/perl-xs/t/oid.t @@ -0,0 +1,45 @@ +# vim: set ft=perl: +# Before `make install' is performed this script should be runnable with +# `make test'. After `make install' it should work as `perl test.pl' + +######################### We start with some black magic to print on failure. + +# Change 1..1 below to 1..last_test_to_print . +# (It may become useful if the test is moved to ./t subdirectory.) + +BEGIN { $| = 1; print "1..6\n"; } +END { print "not ok 1\n" unless $loaded; } + +use Botan; + +$loaded = 1; +print "ok 1\n"; + +######################### End of black magic. + +# Insert your test code below (better if it prints "ok 13" +# (correspondingly "not ok 13") depending on the success of chunk 13 +# of the test code): + +use strict; + +print "not " unless Botan::OIDS::have_oid('X520.CommonName'); +print "ok 2\n"; + +my $oid_c = Botan::OID->new('2.5.4.3'); +print "not " if Botan::OIDS::lookup_by_oid($oid_c) ne 'X520.CommonName'; +print "ok 3\n"; + +my $oid_x = Botan::OIDS::lookup_by_name('X520.CommonName'); +print "not " if $oid_x->as_string() ne '2.5.4.3'; +print "ok 4\n"; + +my $oid_foo_num = '1.2.3.4.5.6.7.8.9.10.11.12.13.14.15'; +my $oid_foo = Botan::OID->new($oid_foo_num); +print "not " if Botan::OIDS::lookup_by_oid($oid_foo) ne $oid_foo_num; +print "ok 5\n"; + +Botan::OIDS::add_oid($oid_foo, 'Zito.Foo'); + +print "not " if Botan::OIDS::lookup_by_oid($oid_foo) ne 'Zito.Foo'; +print "ok 6\n"; diff --git a/lib/wrap/perl-xs/t/pipe.t b/lib/wrap/perl-xs/t/pipe.t new file mode 100644 index 000000000..f850d8519 --- /dev/null +++ b/lib/wrap/perl-xs/t/pipe.t @@ -0,0 +1,98 @@ +# vim: set ft=perl: +# Before `make install' is performed this script should be runnable with +# `make test'. After `make install' it should work as `perl test.pl' + +######################### We start with some black magic to print on failure. + +# Change 1..1 below to 1..last_test_to_print . +# (It may become useful if the test is moved to ./t subdirectory.) + +BEGIN { $| = 1; print "1..20\n"; } +END { print "not ok 1\n" unless $loaded; } + +use Botan; + +$loaded = 1; +print "ok 1\n"; + +######################### End of black magic. + +# Insert your test code below (better if it prints "ok 13" +# (correspondingly "not ok 13") depending on the success of chunk 13 +# of the test code): + +use strict; + +my $pipe = Botan::Pipe->new(); + +print "not " unless $pipe; +print "ok 2\n"; + +$pipe->start_msg(); +$pipe->write('Hello world'); +$pipe->end_msg(); + +print "not " if $pipe->message_count() != 1; +print "ok 3\n"; + +print "not " if $pipe->remaining() != 11; +print "ok 4\n"; + +print "not " if $pipe->end_of_data(); +print "ok 5\n"; + +print "not " if $pipe->read() ne 'Hello world'; +print "ok 6\n"; + +print "not " if $pipe->remaining() != 0; +print "ok 7\n"; + +print "not " unless $pipe->end_of_data(); +print "ok 8\n"; + +$pipe->process_msg('Hello world'); + +print "not " if $pipe->message_count() != 2; +print "ok 9\n"; + +my $msg_num = $pipe->message_count() -1; + +print "not " if $pipe->read(5, $msg_num) ne 'Hello'; +print "ok 10\n"; + +print "not " if $pipe->read(6, $msg_num) ne ' world'; +print "ok 11\n"; + +print "not " if $pipe->remaining() != 0; +print "ok 12\n"; + +print "not " unless $pipe->end_of_data(); +print "ok 13\n"; + +$pipe->process_msg("The\0string\0with\0null\0chars\0"); +$msg_num = $pipe->message_count() -1; + +print "not " if $pipe->read(80, $msg_num) ne "The\0string\0with\0null\0chars\0"; +print "ok 14\n"; + +$pipe->process_msg('FOO BAR'); +$pipe->set_default_msg($pipe->message_count() -1); + +print "not " if $pipe->peek(3) ne 'FOO'; +print "ok 15\n"; + +print "not " if $pipe->peek(3, 4) ne 'BAR'; +print "ok 16\n"; + +print "not " if $pipe->peek() ne 'FOO BAR'; +print "ok 17\n"; + +print "not " if $pipe->read() ne 'FOO BAR'; +print "ok 18\n"; + +print "not " if $pipe->remaining() != 0; +print "ok 19\n"; + +print "not " unless $pipe->end_of_data(); +print "ok 20\n"; + diff --git a/lib/wrap/perl-xs/t/testutl.pl b/lib/wrap/perl-xs/t/testutl.pl new file mode 100644 index 000000000..add6f6a45 --- /dev/null +++ b/lib/wrap/perl-xs/t/testutl.pl @@ -0,0 +1,26 @@ +#!/usr/bin/perl + +sub random_message_ok +{ + my ($pipe, $iter, $chunkmax) = @_; + $iter = 100 unless defined $iter; + $chunkmax = 300 unless defined $chunkmax; + eval { + my $input = ''; + $pipe->start_msg(); + for(my $i = 0; $i < $iter; $i++) + { + my $chunk = ''; + my $chunklen = int(rand($chunkmax)); + $chunk .= pack("C", int(rand(256))) while $chunklen--; + $input .= $chunk; + $pipe->write($chunk); + } + $pipe->end_msg(); + my $msg_num = $pipe->message_count() -1; + my $output = $pipe->read(0xFFFFFFFF, $msg_num); + return $input eq $output; + }; +} + +1; diff --git a/lib/wrap/perl-xs/t/x509cert.t b/lib/wrap/perl-xs/t/x509cert.t new file mode 100644 index 000000000..2a943aeac --- /dev/null +++ b/lib/wrap/perl-xs/t/x509cert.t @@ -0,0 +1,42 @@ +# vim: set ft=perl: +# Before `make install' is performed this script should be runnable with +# `make test'. After `make install' it should work as `perl test.pl' + +######################### We start with some black magic to print on failure. + +# Change 1..1 below to 1..last_test_to_print . +# (It may become useful if the test is moved to ./t subdirectory.) + +BEGIN { $| = 1; print "1..4\n"; } +END { print "not ok 1\n" unless $loaded; } + +use Botan; + +$loaded = 1; +print "ok 1\n"; + +######################### End of black magic. + +# Insert your test code below (better if it prints "ok 13" +# (correspondingly "not ok 13") depending on the success of chunk 13 +# of the test code): + +use strict; + +my $cert = Botan::X509_Certificate->new('data/ca.cert.der'); + +print "not " if $cert->x509_version() != 3; +print "ok 2\n"; + +print "not " if $cert->start_time() ne '2000/8/20 21:48:00 UTC'; +print "ok 3\n"; + +print "not " if $cert->end_time() ne '2002/8/10 21:48:00 UTC'; +print "ok 4\n"; + +#my $subject = $cert->subject_dn()->get_attributes(); +#print STDERR "subject=", join(',', @{$subject}), "\n"; +# +#my $issuer = $cert->issuer_dn()->get_attributes(); +#print STDERR "issuer=", join(',', @{$issuer}), "\n"; +# diff --git a/lib/wrap/perl-xs/typemap b/lib/wrap/perl-xs/typemap new file mode 100644 index 000000000..d7403d40d --- /dev/null +++ b/lib/wrap/perl-xs/typemap @@ -0,0 +1,62 @@ +TYPEMAP + +Botan__ASN1_String * O_OBJECT +Botan__AlgorithmIdentifier * O_OBJECT +Botan__AlternativeName * O_OBJECT +Botan__Attribute * O_OBJECT +Botan__Base64_Decoder * O_EXTOBJECT +Botan__Base64_Encoder * O_EXTOBJECT +Botan__Chain * O_EXTOBJECT +Botan__Extension * O_OBJECT +Botan__Filter * O_EXTOBJECT +Botan__Fork * O_EXTOBJECT +Botan__Hex_Decoder * O_EXTOBJECT +Botan__Hex_Encoder * O_EXTOBJECT +Botan__OID * O_OBJECT +Botan__Pipe * O_OBJECT +Botan__X509_Certificate * O_OBJECT +Botan__X509_DN * O_OBJECT +Botan__X509_Time * O_OBJECT +Botan__u32bit T_UV + + +###################################################################### +OUTPUT + +# The Perl object is blessed into 'CLASS', which should be a +# char* having the name of the package for the blessing. +O_OBJECT + sv_setref_pv($arg, CLASS, (void*)$var); + +O_EXTOBJECT + sv_setref_pv($arg, CLASS, (void*)$var); + sv_magic(SvRV($arg), 0, '~', (char *)&oi_init, sizeof(oi_init)); + + +###################################################################### +INPUT + +O_OBJECT + if ( sv_isobject($arg) && (SvTYPE(SvRV($arg)) == SVt_PVMG) ) + $var = ($type)SvIV((SV*)SvRV( $arg )); + else + croak(\"${Package}::$func_name() -- \" + \"$var is not a blessed SV reference\"); + +# The pointer variable "ObjectInfo *${var}_oi;" must be declared +# in PREINIT section. I don't know how to emit this declaration safely here. +O_EXTOBJECT + if ( sv_isobject($arg) && (SvTYPE(SvRV($arg)) == SVt_PVMG) ) + $var = ($type)SvIV((SV*)SvRV($arg)); + else + croak(\"${Package}::$func_name() -- \" + \"$var is not a blessed SV reference\"); + { + MAGIC *mg = mg_find(SvRV($arg), '~'); + if ( mg == 0 + || mg->mg_len != sizeof(ObjectInfo) + || *(I32 *)(mg->mg_ptr) != ObjectInfo::SIGNVAL ) + croak(\"${Package}::$func_name() -- \" + \"private magic data for $var invalid\"); + ${var}_oi = (ObjectInfo *)(mg->mg_ptr); + } diff --git a/lib/wrap/python/__init__.py b/lib/wrap/python/__init__.py new file mode 100644 index 000000000..2df9a456f --- /dev/null +++ b/lib/wrap/python/__init__.py @@ -0,0 +1,4 @@ +from _botan import * + +# Initialize the library when the module is imported +init = LibraryInitializer() diff --git a/lib/wrap/python/core.cpp b/lib/wrap/python/core.cpp new file mode 100644 index 000000000..6dcceee74 --- /dev/null +++ b/lib/wrap/python/core.cpp @@ -0,0 +1,232 @@ +/* +* Boost.Python module definition +* (C) 1999-2007 Jack Lloyd +*/ + +#include <botan/init.h> +#include <botan/pipe.h> +#include <botan/lookup.h> +#include <botan/cryptobox.h> +#include <botan/pbkdf2.h> +#include <botan/hmac.h> +using namespace Botan; + +#include "python_botan.h" + +class Py_Cipher + { + public: + Py_Cipher(std::string algo_name, std::string direction, + std::string key); + + std::string cipher_noiv(const std::string& text); + + std::string cipher(const std::string& text, + const std::string& iv); + + std::string name() const { return algo_name; } + private: + std::string algo_name; + Keyed_Filter* filter; + Pipe pipe; + }; + +std::string Py_Cipher::cipher(const std::string& input, + const std::string& iv_str) + { + if(iv_str.size()) + { + const byte* iv_bytes = reinterpret_cast<const byte*>(iv_str.data()); + u32bit iv_len = iv_str.size(); + filter->set_iv(InitializationVector(iv_bytes, iv_len)); + } + + pipe.process_msg(input); + return pipe.read_all_as_string(Pipe::LAST_MESSAGE); + } + +// For IV-less algorithms +std::string Py_Cipher::cipher_noiv(const std::string& input) + { + pipe.process_msg(input); + return pipe.read_all_as_string(Pipe::LAST_MESSAGE); + } + +Py_Cipher::Py_Cipher(std::string algo_name, + std::string direction, + std::string key_str) + { + const byte* key_bytes = reinterpret_cast<const byte*>(key_str.data()); + u32bit key_len = key_str.size(); + + Cipher_Dir dir; + + if(direction == "encrypt") + dir = ENCRYPTION; + else if(direction == "decrypt") + dir = DECRYPTION; + else + throw std::invalid_argument("Bad cipher direction " + direction); + + filter = get_cipher(algo_name, dir); + filter->set_key(SymmetricKey(key_bytes, key_len)); + pipe.append(filter); + } + +class Py_HashFunction + { + public: + Py_HashFunction(const std::string& algo_name) + { + hash = get_hash(algo_name); + } + + ~Py_HashFunction() { delete hash; } + + void update(const std::string& input) + { + hash->update(input); + } + + std::string final() + { + std::string out(output_length(), 0); + hash->final(reinterpret_cast<byte*>(&out[0])); + return out; + } + + std::string name() const + { + return hash->name(); + } + + u32bit output_length() const + { + return hash->output_length(); + } + + private: + HashFunction* hash; + }; + +class Py_MAC + { + public: + + Py_MAC(const std::string& name, const std::string& key_str) + { + mac = get_mac(name); + + mac->set_key(reinterpret_cast<const byte*>(key_str.data()), + key_str.size()); + } + + ~Py_MAC() { delete mac; } + + u32bit output_length() const { return mac->output_length(); } + + std::string name() const { return mac->name(); } + + void update(const std::string& in) { mac->update(in); } + + std::string final() + { + std::string out(output_length(), 0); + mac->final(reinterpret_cast<byte*>(&out[0])); + return out; + } + private: + MessageAuthenticationCode* mac; + }; + +std::string cryptobox_encrypt(const std::string& in, + const std::string& passphrase, + Python_RandomNumberGenerator& rng) + { + const byte* in_bytes = reinterpret_cast<const byte*>(in.data()); + + return CryptoBox::encrypt(in_bytes, in.size(), + passphrase, rng.get_underlying_rng()); + } + +std::string cryptobox_decrypt(const std::string& in, + const std::string& passphrase) + { + const byte* in_bytes = reinterpret_cast<const byte*>(in.data()); + + return CryptoBox::decrypt(in_bytes, in.size(), + passphrase); + } + +std::string python_pbkdf2(const std::string& passphrase, + const std::string& salt, + u32bit iterations, + u32bit output_size, + const std::string& hash_fn) + { + PKCS5_PBKDF2 pbkdf2(new HMAC(get_hash(hash_fn))); + + return make_string( + pbkdf2.derive_key(output_size, + passphrase, + reinterpret_cast<const byte*>(salt.data()), + salt.size(), + iterations).bits_of()); + } + +std::string python_kdf2(const std::string& param, + const std::string& masterkey, + u32bit outputlength) + { + std::unique_ptr<KDF> kdf(get_kdf("KDF2(SHA-1)")); + + return make_string( + kdf->derive_key(outputlength, + reinterpret_cast<const byte*>(masterkey.data()), + masterkey.length(), + param)); + } + +BOOST_PYTHON_MODULE(_botan) + { + python::class_<LibraryInitializer>("LibraryInitializer") + .def(python::init< python::optional<std::string> >()); + + python::class_<Python_RandomNumberGenerator>("RandomNumberGenerator") + .def(python::init<>()) + .def("__str__", &Python_RandomNumberGenerator::name) + .def("name", &Python_RandomNumberGenerator::name) + .def("reseed", &Python_RandomNumberGenerator::reseed) + .def("add_entropy", &Python_RandomNumberGenerator::add_entropy) + .def("gen_random_byte", &Python_RandomNumberGenerator::gen_random_byte) + .def("gen_random", &Python_RandomNumberGenerator::gen_random); + + python::class_<Py_Cipher, boost::noncopyable> + ("Cipher", python::init<std::string, std::string, std::string>()) + .def("name", &Py_Cipher::name) + .def("cipher", &Py_Cipher::cipher) + .def("cipher", &Py_Cipher::cipher_noiv); + + python::class_<Py_HashFunction, boost::noncopyable> + ("HashFunction", python::init<std::string>()) + .def("update", &Py_HashFunction::update) + .def("final", &Py_HashFunction::final) + .def("name", &Py_HashFunction::name) + .def("output_length", &Py_HashFunction::output_length); + + python::class_<Py_MAC, boost::noncopyable> + ("MAC", python::init<std::string, std::string>()) + .def("update", &Py_MAC::update) + .def("final", &Py_MAC::final) + .def("name", &Py_MAC::name) + .def("output_length", &Py_MAC::output_length); + + python::def("cryptobox_encrypt", cryptobox_encrypt); + python::def("cryptobox_decrypt", cryptobox_decrypt); + python::def("pbkdf2", python_pbkdf2); + python::def("derive_key", python_kdf2); + + export_filters(); + export_rsa(); + export_x509(); + } diff --git a/lib/wrap/python/filter.cpp b/lib/wrap/python/filter.cpp new file mode 100644 index 000000000..e329ed708 --- /dev/null +++ b/lib/wrap/python/filter.cpp @@ -0,0 +1,177 @@ +/* +* Boost.Python module definition +* (C) 1999-2007 Jack Lloyd +* +* Distributed under the terms of the Botan license +*/ + +#include <boost/python.hpp> +using namespace boost::python; + +#include <botan/pipe.h> +#include <botan/lookup.h> +using namespace Botan; + +class Py_Filter : public Filter + { + public: + virtual void write_str(const std::string&) = 0; + + std::string name() const { return "Py_Filter_FIXME"; } + + void write(const byte data[], size_t length) + { + write_str(std::string((const char*)data, length)); + } + + void send_str(const std::string& str) + { + send((const byte*)str.data(), str.length()); + } + }; + +class FilterWrapper : public Py_Filter, public wrapper<Py_Filter> + { + public: + void start_msg() + { + if(override start_msg = this->get_override("start_msg")) + start_msg(); + } + + void end_msg() + { + if(override end_msg = this->get_override("end_msg")) + end_msg(); + } + + void default_start_msg() {} + void default_end_msg() {} + + virtual void write_str(const std::string& str) + { + this->get_override("write")(str); + } + }; + +Filter* return_or_raise(Filter* filter, const std::string& name) + { + if(filter) + return filter; + throw Invalid_Argument("Filter " + name + " could not be found"); + } + +Filter* make_filter1(const std::string& name) + { + Filter* filter = 0; + + if(have_hash(name)) filter = new Hash_Filter(name); + else if(name == "Hex_Encoder") filter = new Hex_Encoder; + else if(name == "Hex_Decoder") filter = new Hex_Decoder; + else if(name == "Base64_Encoder") filter = new Base64_Encoder; + else if(name == "Base64_Decoder") filter = new Base64_Decoder; + + return return_or_raise(filter, name); + } + +Filter* make_filter2(const std::string& name, + const SymmetricKey& key) + { + Filter* filter = 0; + + if(have_mac(name)) + filter = new MAC_Filter(name, key); + else if(have_stream_cipher(name)) + filter = new StreamCipher_Filter(name, key); + + return return_or_raise(filter, name); + } + +// FIXME: add new wrapper for Keyed_Filter here +Filter* make_filter3(const std::string& name, + const SymmetricKey& key, + Cipher_Dir direction) + { + return return_or_raise( + get_cipher(name, key, direction), + name); + } + +Filter* make_filter4(const std::string& name, + const SymmetricKey& key, + const InitializationVector& iv, + Cipher_Dir direction) + { + return return_or_raise( + get_cipher(name, key, iv, direction), + name); + } + +void append_filter(Pipe& pipe, std::auto_ptr<Filter> filter) + { + pipe.append(filter.get()); + filter.release(); + } + +void prepend_filter(Pipe& pipe, std::auto_ptr<Filter> filter) + { + pipe.prepend(filter.get()); + filter.release(); + } + +void do_send(std::auto_ptr<FilterWrapper> filter, const std::string& data) + { + filter->send_str(data); + } + +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(rallas_ovls, read_all_as_string, 0, 1) + +void export_filters() + { + class_<Filter, std::auto_ptr<Filter>, boost::noncopyable> + ("__Internal_FilterObj", no_init); + + def("make_filter", make_filter1, + return_value_policy<manage_new_object>()); + def("make_filter", make_filter2, + return_value_policy<manage_new_object>()); + def("make_filter", make_filter3, + return_value_policy<manage_new_object>()); + def("make_filter", make_filter4, + return_value_policy<manage_new_object>()); + + // This might not work - Pipe will delete the filter, but Python + // might have allocated the space with malloc() or who-knows-what -> bad + class_<FilterWrapper, std::auto_ptr<FilterWrapper>, + bases<Filter>, boost::noncopyable> + ("FilterObj") + .def("write", pure_virtual(&Py_Filter::write_str)) + .def("send", &do_send) + .def("start_msg", &Filter::start_msg, &FilterWrapper::default_start_msg) + .def("end_msg", &Filter::end_msg, &FilterWrapper::default_end_msg); + + implicitly_convertible<std::auto_ptr<FilterWrapper>, + std::auto_ptr<Filter> >(); + + void (Pipe::*pipe_write_str)(const std::string&) = &Pipe::write; + void (Pipe::*pipe_process_str)(const std::string&) = &Pipe::process_msg; + + class_<Pipe, boost::noncopyable>("PipeObj") + .def(init<>()) + /* + .def_readonly("LAST_MESSAGE", &Pipe::LAST_MESSAGE) + .def_readonly("DEFAULT_MESSAGE", &Pipe::DEFAULT_MESSAGE) + */ + .add_property("default_msg", &Pipe::default_msg, &Pipe::set_default_msg) + .add_property("msg_count", &Pipe::message_count) + .def("append", append_filter) + .def("prepend", prepend_filter) + .def("reset", &Pipe::reset) + .def("pop", &Pipe::pop) + .def("end_of_data", &Pipe::end_of_data) + .def("start_msg", &Pipe::start_msg) + .def("end_msg", &Pipe::end_msg) + .def("write", pipe_write_str) + .def("process_msg", pipe_process_str) + .def("read_all", &Pipe::read_all_as_string, rallas_ovls()); + } diff --git a/lib/wrap/python/python_botan.h b/lib/wrap/python/python_botan.h new file mode 100644 index 000000000..501f4b9eb --- /dev/null +++ b/lib/wrap/python/python_botan.h @@ -0,0 +1,86 @@ +/* +* (C) 2009 Jack Lloyd +* +* Distributed under the terms of the Botan license +*/ + +#ifndef BOTAN_BOOST_PYTHON_COMMON_H__ +#define BOTAN_BOOST_PYTHON_COMMON_H__ + +#include <botan/exceptn.h> +#include <botan/parsing.h> +#include <botan/secmem.h> +using namespace Botan; + +#include <boost/python.hpp> +namespace python = boost::python; + +extern void export_filters(); +extern void export_rsa(); +extern void export_x509(); + +class Bad_Size : public Exception + { + public: + Bad_Size(u32bit got, u32bit expected) : + Exception("Bad size detected in Python/C++ conversion layer: got " + + std::to_string(got) + " bytes, expected " + + std::to_string(expected)) + {} + }; + +inline std::string make_string(const byte input[], u32bit length) + { + return std::string((const char*)input, length); + } + +template<typename Alloc> +inline std::string make_string(const std::vector<byte, Alloc>& in) + { + return make_string(&in[0], in.size()); + } + +inline void string2binary(const std::string& from, byte to[], u32bit expected) + { + if(from.size() != expected) + throw Bad_Size(from.size(), expected); + std::memcpy(to, from.data(), expected); + } + +template<typename T> +inline python::object get_owner(T* me) + { + return python::object( + python::handle<>( + python::borrowed(python::detail::wrapper_base_::get_owner(*me)))); + } + +class Python_RandomNumberGenerator + { + public: + Python_RandomNumberGenerator() + { rng = RandomNumberGenerator::make_rng(); } + ~Python_RandomNumberGenerator() { delete rng; } + + std::string name() const { return rng->name(); } + + void reseed() { rng->reseed(192); } + + int gen_random_byte() { return rng->next_byte(); } + + std::string gen_random(int n) + { + std::string s(n, 0); + rng->randomize(reinterpret_cast<byte*>(&s[0]), n); + return s; + } + + void add_entropy(const std::string& in) + { rng->add_entropy(reinterpret_cast<const byte*>(in.c_str()), in.length()); } + + RandomNumberGenerator& get_underlying_rng() { return *rng; } + private: + RandomNumberGenerator* rng; + }; + +#endif diff --git a/lib/wrap/python/rsa.cpp b/lib/wrap/python/rsa.cpp new file mode 100644 index 000000000..770082945 --- /dev/null +++ b/lib/wrap/python/rsa.cpp @@ -0,0 +1,220 @@ +/* +* Boost.Python module definition +* (C) 1999-2007 Jack Lloyd +* +* Distributed under the terms of the Botan license +*/ + +#include <botan/rsa.h> +#include <botan/pubkey.h> +#include <botan/x509_key.h> +using namespace Botan; + +#include "python_botan.h" +#include <sstream> + +std::string bigint2str(const BigInt& n) + { + std::ostringstream out; + out << n; + return out.str(); + } + +class Py_RSA_PrivateKey + { + public: + Py_RSA_PrivateKey(std::string pem_str, + Python_RandomNumberGenerator& rng, + std::string pass); + Py_RSA_PrivateKey(std::string pem_str, + Python_RandomNumberGenerator& rng); + + Py_RSA_PrivateKey(u32bit bits, Python_RandomNumberGenerator& rng); + ~Py_RSA_PrivateKey() { delete rsa_key; } + + std::string to_string() const + { + return PKCS8::PEM_encode(*rsa_key); + } + + std::string to_ber() const + { + secure_vector<byte> bits = PKCS8::BER_encode(*rsa_key); + return std::string(reinterpret_cast<const char*>(&bits[0]), bits.size()); + } + + std::string get_N() const { return bigint2str(get_bigint_N()); } + std::string get_E() const { return bigint2str(get_bigint_E()); } + + const BigInt& get_bigint_N() const { return rsa_key->get_n(); } + const BigInt& get_bigint_E() const { return rsa_key->get_e(); } + + std::string decrypt(const std::string& in, + const std::string& padding); + + std::string sign(const std::string& in, + const std::string& padding, + Python_RandomNumberGenerator& rng); + private: + RSA_PrivateKey* rsa_key; + }; + +std::string Py_RSA_PrivateKey::decrypt(const std::string& in, + const std::string& padding) + { + PK_Decryptor_EME dec(*rsa_key, padding); + + const byte* in_bytes = reinterpret_cast<const byte*>(in.data()); + + return make_string(dec.decrypt(in_bytes, in.size())); + } + +std::string Py_RSA_PrivateKey::sign(const std::string& in, + const std::string& padding, + Python_RandomNumberGenerator& rng) + { + PK_Signer sign(*rsa_key, padding); + const byte* in_bytes = reinterpret_cast<const byte*>(in.data()); + sign.update(in_bytes, in.size()); + return make_string(sign.signature(rng.get_underlying_rng())); + } + +Py_RSA_PrivateKey::Py_RSA_PrivateKey(u32bit bits, + Python_RandomNumberGenerator& rng) + { + rsa_key = new RSA_PrivateKey(rng.get_underlying_rng(), bits); + } + +Py_RSA_PrivateKey::Py_RSA_PrivateKey(std::string pem_str, + Python_RandomNumberGenerator& rng) + { + DataSource_Memory in(pem_str); + + Private_Key* pkcs8_key = + PKCS8::load_key(in, + rng.get_underlying_rng()); + + rsa_key = dynamic_cast<RSA_PrivateKey*>(pkcs8_key); + + if(!rsa_key) + throw std::invalid_argument("Key is not an RSA key"); + } + +Py_RSA_PrivateKey::Py_RSA_PrivateKey(std::string pem_str, + Python_RandomNumberGenerator& rng, + std::string passphrase) + { + DataSource_Memory in(pem_str); + + Private_Key* pkcs8_key = + PKCS8::load_key(in, + rng.get_underlying_rng(), + passphrase); + + rsa_key = dynamic_cast<RSA_PrivateKey*>(pkcs8_key); + + if(!rsa_key) + throw std::invalid_argument("Key is not an RSA key"); + } + +class Py_RSA_PublicKey + { + public: + Py_RSA_PublicKey(std::string pem_str); + Py_RSA_PublicKey(const Py_RSA_PrivateKey&); + ~Py_RSA_PublicKey() { delete rsa_key; } + + std::string get_N() const { return bigint2str(get_bigint_N()); } + std::string get_E() const { return bigint2str(get_bigint_E()); } + + const BigInt& get_bigint_N() const { return rsa_key->get_n(); } + const BigInt& get_bigint_E() const { return rsa_key->get_e(); } + + std::string to_string() const + { + return X509::PEM_encode(*rsa_key); + } + + std::string to_ber() const + { + std::vector<byte> bits = X509::BER_encode(*rsa_key); + + return std::string(reinterpret_cast<const char*>(&bits[0]), + bits.size()); + } + + std::string encrypt(const std::string& in, + const std::string& padding, + Python_RandomNumberGenerator& rng); + + bool verify(const std::string& in, + const std::string& padding, + const std::string& signature); + private: + RSA_PublicKey* rsa_key; + }; + +Py_RSA_PublicKey::Py_RSA_PublicKey(const Py_RSA_PrivateKey& priv) + { + rsa_key = new RSA_PublicKey(priv.get_bigint_N(), priv.get_bigint_E()); + } + +Py_RSA_PublicKey::Py_RSA_PublicKey(std::string pem_str) + { + DataSource_Memory in(pem_str); + Public_Key* x509_key = X509::load_key(in); + + rsa_key = dynamic_cast<RSA_PublicKey*>(x509_key); + + if(!rsa_key) + throw std::invalid_argument("Key is not an RSA key"); + } + +std::string Py_RSA_PublicKey::encrypt(const std::string& in, + const std::string& padding, + Python_RandomNumberGenerator& rng) + { + PK_Encryptor_EME enc(*rsa_key, padding); + + const byte* in_bytes = reinterpret_cast<const byte*>(in.data()); + + return make_string(enc.encrypt(in_bytes, in.size(), + rng.get_underlying_rng())); + } + +bool Py_RSA_PublicKey::verify(const std::string& in, + const std::string& signature, + const std::string& padding) + { + PK_Verifier ver(*rsa_key, padding); + + const byte* in_bytes = reinterpret_cast<const byte*>(in.data()); + const byte* sig_bytes = reinterpret_cast<const byte*>(signature.data()); + + ver.update(in_bytes, in.size()); + return ver.check_signature(sig_bytes, signature.size()); + } + +void export_rsa() + { + python::class_<Py_RSA_PublicKey> + ("RSA_PublicKey", python::init<std::string>()) + .def(python::init<const Py_RSA_PrivateKey&>()) + .def("to_string", &Py_RSA_PublicKey::to_string) + .def("to_ber", &Py_RSA_PublicKey::to_ber) + .def("encrypt", &Py_RSA_PublicKey::encrypt) + .def("verify", &Py_RSA_PublicKey::verify) + .def("get_N", &Py_RSA_PublicKey::get_N) + .def("get_E", &Py_RSA_PublicKey::get_E); + + python::class_<Py_RSA_PrivateKey> + ("RSA_PrivateKey", python::init<std::string, Python_RandomNumberGenerator&, std::string>()) + .def(python::init<std::string, Python_RandomNumberGenerator&>()) + .def(python::init<u32bit, Python_RandomNumberGenerator&>()) + .def("to_string", &Py_RSA_PrivateKey::to_string) + .def("to_ber", &Py_RSA_PrivateKey::to_ber) + .def("decrypt", &Py_RSA_PrivateKey::decrypt) + .def("sign", &Py_RSA_PrivateKey::sign) + .def("get_N", &Py_RSA_PrivateKey::get_N) + .def("get_E", &Py_RSA_PrivateKey::get_E); + } diff --git a/lib/wrap/python/x509.cpp b/lib/wrap/python/x509.cpp new file mode 100644 index 000000000..57beb7e4a --- /dev/null +++ b/lib/wrap/python/x509.cpp @@ -0,0 +1,88 @@ +/* +* Boost.Python module definition +* (C) 2009 Jack Lloyd +* +* Distributed under the terms of the Botan license +*/ + +#include <botan/oids.h> +#include <botan/pipe.h> +#include <botan/filters.h> +#include <botan/x509cert.h> +#include <botan/x509_crl.h> +using namespace Botan; + +#include <boost/python.hpp> +namespace python = boost::python; + +template<typename T> +class vector_to_list + { + public: + static PyObject* convert(const std::vector<T>& in) + { + python::list out; + typename std::vector<T>::const_iterator i = in.begin(); + while(i != in.end()) + { + out.append(*i); + ++i; + } + return python::incref(out.ptr()); + } + + vector_to_list() + { + python::to_python_converter<std::vector<T>, vector_to_list<T> >(); + } + }; + +template<typename T> +class memvec_to_hexstr + { + public: + static PyObject* convert(const T& in) + { + Pipe pipe(new Hex_Encoder); + pipe.process_msg(in); + std::string result = pipe.read_all_as_string(); + return python::incref(python::str(result).ptr()); + } + + memvec_to_hexstr() + { + python::to_python_converter<T, memvec_to_hexstr<T> >(); + } + }; + +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(add_cert_ols, add_cert, 1, 2) +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(validate_cert_ols, validate_cert, 1, 2) + +void export_x509() + { + vector_to_list<std::string>(); + vector_to_list<X509_Certificate>(); + memvec_to_hexstr<std::vector<byte> >(); + + python::class_<X509_Certificate> + ("X509_Certificate", python::init<std::string>()) + .def(python::self == python::self) + .def(python::self != python::self) + .add_property("version", &X509_Certificate::x509_version) + .add_property("is_CA", &X509_Certificate::is_CA_cert) + .add_property("self_signed", &X509_Certificate::is_self_signed) + .add_property("pathlimit", &X509_Certificate::path_limit) + .add_property("as_pem", &X509_Object::PEM_encode) + .def("start_time", &X509_Certificate::start_time) + .def("end_time", &X509_Certificate::end_time) + .def("subject_info", &X509_Certificate::subject_info) + .def("issuer_info", &X509_Certificate::issuer_info) + .def("ex_constraints", &X509_Certificate::ex_constraints) + .def("policies", &X509_Certificate::policies) + .def("subject_key_id", &X509_Certificate::subject_key_id) + .def("authority_key_id", &X509_Certificate::authority_key_id); + + python::class_<X509_CRL> + ("X509_CRL", python::init<std::string>()) + .add_property("as_pem", &X509_Object::PEM_encode); + } diff --git a/lib/wrap/sqlite/codec.cpp b/lib/wrap/sqlite/codec.cpp new file mode 100644 index 000000000..70519ccfa --- /dev/null +++ b/lib/wrap/sqlite/codec.cpp @@ -0,0 +1,203 @@ +/* + * Codec class for SQLite3 encryption codec. + * (C) 2010 Olivier de Gaalon + * + * Distributed under the terms of the Botan license + */ + +#include "codec.h" +#include <botan/init.h> + +Codec::Codec(void *db) +{ + InitializeCodec(db); +} + +Codec::Codec(const Codec *other, void *db) +{ + //Only used to copy main db key for an attached db + InitializeCodec(db); + m_hasReadKey = other->m_hasReadKey; + m_hasWriteKey = other->m_hasWriteKey; + m_readKey = other->m_readKey; + m_ivReadKey = other->m_ivReadKey; + m_writeKey = other->m_writeKey; + m_ivWriteKey = other->m_ivWriteKey; +} + +void Codec::InitializeCodec(void *db) +{ + m_hasReadKey = false; + m_hasWriteKey = false; + m_db = db; + + try + { + m_encipherFilter = get_cipher(BLOCK_CIPHER_STR, ENCRYPTION); + m_decipherFilter = get_cipher(BLOCK_CIPHER_STR, DECRYPTION); + m_cmac = new MAC_Filter(MAC_STR); + m_encipherPipe.append(m_encipherFilter); + m_decipherPipe.append(m_decipherFilter); + m_macPipe.append(m_cmac); + } + catch(Botan::Exception e) + { + m_botanErrorMsg = e.what(); + } +} + +void Codec::GenerateWriteKey(const char *userPassword, int passwordLength) +{ + try + { +#if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,9,4) + PBKDF *pbkdf = get_pbkdf(PBKDF_STR); + SymmetricKey masterKey = + pbkdf->derive_key(KEY_SIZE + IV_DERIVATION_KEY_SIZE, std::string(userPassword, passwordLength), + (const byte*)SALT_STR.c_str(), SALT_SIZE, PBKDF_ITERATIONS); +#elif BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,8,0) + S2K* s2k = get_s2k(PBKDF_STR); + s2k->set_iterations(PBKDF_ITERATIONS); + s2k->change_salt((const byte*)SALT_STR.c_str(), SALT_SIZE); + + SymmetricKey masterKey = + s2k->derive_key(KEY_SIZE + IV_DERIVATION_KEY_SIZE, std::string(userPassword, passwordLength)); +#else +#error "This code requires botan 1.8 or newer" +#endif + m_writeKey = SymmetricKey(masterKey.bits_of(), KEY_SIZE); + m_ivWriteKey = SymmetricKey(masterKey.bits_of() + KEY_SIZE, IV_DERIVATION_KEY_SIZE); + + m_hasWriteKey = true; + } + catch(Botan::Exception e) + { + m_botanErrorMsg = e.what(); + } +} + +void Codec::DropWriteKey() +{ + m_hasWriteKey = false; +} + +void Codec::SetReadIsWrite() +{ + m_readKey = m_writeKey; + m_ivReadKey = m_ivWriteKey; + m_hasReadKey = m_hasWriteKey; +} + +void Codec::SetWriteIsRead() +{ + m_writeKey = m_readKey; + m_ivWriteKey = m_ivReadKey; + m_hasWriteKey = m_hasReadKey; +} + +unsigned char* Codec::Encrypt(int page, unsigned char *data, bool useWriteKey) +{ + memcpy(m_page, data, m_pageSize); + + try + { + m_encipherFilter->set_key(useWriteKey ? m_writeKey : m_readKey); + m_encipherFilter->set_iv(GetIVForPage(page, useWriteKey)); + m_encipherPipe.process_msg(m_page, m_pageSize); + m_encipherPipe.read(m_page, m_encipherPipe.remaining(Pipe::LAST_MESSAGE), Pipe::LAST_MESSAGE); + } + catch(Botan::Exception e) + { + m_botanErrorMsg = e.what(); + } + + return m_page; //return location of newly ciphered data +} + +void Codec::Decrypt(int page, unsigned char *data) +{ + try + { + m_decipherFilter->set_key(m_readKey); + m_decipherFilter->set_iv(GetIVForPage(page, false)); + m_decipherPipe.process_msg(data, m_pageSize); + m_decipherPipe.read(data, m_decipherPipe.remaining(Pipe::LAST_MESSAGE), Pipe::LAST_MESSAGE); + } + catch(Botan::Exception e) + { + m_botanErrorMsg = e.what(); + } +} + +InitializationVector Codec::GetIVForPage(u32bit page, bool useWriteKey) +{ + try + { + static unsigned char *intiv[4]; + store_le(page, (byte*)intiv); + m_cmac->set_key(useWriteKey ? m_ivWriteKey : m_ivReadKey); + m_macPipe.process_msg((byte*)intiv, 4); + return m_macPipe.read_all(Pipe::LAST_MESSAGE); + } + catch(Botan::Exception e) + { + m_botanErrorMsg = e.what(); + } +} + +const char* Codec::GetAndResetError() +{ + const char *message = m_botanErrorMsg; + m_botanErrorMsg = 0; + return message; +} + +#include "codec_c_interface.h" + +void InitializeBotan() { + LibraryInitializer::initialize(); +} +void* InitializeNewCodec(void *db) { + return new Codec(db); +} +void* InitializeFromOtherCodec(const void *otherCodec, void *db) { + return new Codec((Codec*)otherCodec, db); +} +void GenerateWriteKey(void *codec, const char *userPassword, int passwordLength) { + ((Codec*)codec)->GenerateWriteKey(userPassword, passwordLength); +} +void DropWriteKey(void *codec) { + ((Codec*)codec)->DropWriteKey(); +} +void SetWriteIsRead(void *codec) { + ((Codec*)codec)->SetWriteIsRead(); +} +void SetReadIsWrite(void *codec) { + ((Codec*)codec)->SetReadIsWrite(); +} +unsigned char* Encrypt(void *codec, int page, unsigned char *data, Bool useWriteKey) { + return ((Codec*)codec)->Encrypt(page, data, useWriteKey); +} +void Decrypt(void *codec, int page, unsigned char *data) { + ((Codec*)codec)->Decrypt(page, data); +} +void SetPageSize(void *codec, int pageSize) { + ((Codec*)codec)->SetPageSize(pageSize); +} +Bool HasReadKey(void *codec) { + return ((Codec*)codec)->HasReadKey(); +} +Bool HasWriteKey(void *codec) { + return ((Codec*)codec)->HasWriteKey(); +} +void* GetDB(void *codec) { + return ((Codec*)codec)->GetDB(); +} +const char* GetAndResetError(void *codec) +{ + return ((Codec*)codec)->GetAndResetError(); +} +void DeleteCodec(void *codec) { + Codec *deleteThisCodec = (Codec*)codec; + delete deleteThisCodec; +} diff --git a/lib/wrap/sqlite/codec.h b/lib/wrap/sqlite/codec.h new file mode 100644 index 000000000..2f94bcc59 --- /dev/null +++ b/lib/wrap/sqlite/codec.h @@ -0,0 +1,106 @@ +/* + * Codec class for SQLite3 encryption codec. + * (C) 2010 Olivier de Gaalon + * + * Distributed under the terms of the Botan license + */ + +#ifndef _CODEC_H_ +#define _CODEC_H_ + +#include <string> +#include <botan/botan.h> +#include <botan/loadstor.h> + +using namespace std; +using namespace Botan; + +/*These constants can be used to tweak the codec behavior as follows + *Note that once you've encrypted a database with these settings, + *recompiling with any different settings will give you a library that + *cannot read that database, even given the same passphrase.*/ + +//BLOCK_CIPHER_STR: Cipher and mode used for encrypting the database +//make sure to add "/NoPadding" for modes that use padding schemes +const string BLOCK_CIPHER_STR = "Twofish/XTS"; + +//PBKDF_STR: Key derivation function used to derive both the encryption +//and IV derivation keys from the given database passphrase +const string PBKDF_STR = "PBKDF2(SHA-160)"; + +//SALT_STR: Hard coded salt used to derive the key from the passphrase. +const string SALT_STR = "&g#nB'9]"; + +//SALT_SIZE: Size of the salt in bytes (as given in SALT_STR) +const int SALT_SIZE = 64/8; //64 bit, 8 byte salt + +//MAC_STR: CMAC used to derive the IV that is used for db page +//encryption +const string MAC_STR = "CMAC(Twofish)"; + +//PBKDF_ITERATIONS: Number of hash iterations used in the key derivation +//process. +const int PBKDF_ITERATIONS = 10000; + +//KEY_SIZE: Size of the encryption key. Note that XTS splits the key +//between two ciphers, so if you're using XTS, double the intended key +//size. (ie, "AES-128/XTS" should have a 256 bit KEY_SIZE) +const int KEY_SIZE = 512/8; //512 bit, 64 byte key. (256 bit XTS key) + +//IV_DERIVATION_KEY_SIZE: Size of the key used with the CMAC (MAC_STR) +//above. +const int IV_DERIVATION_KEY_SIZE = 256/8; //256 bit, 32 byte key + +//This is definited in sqlite.h and very unlikely to change +#define SQLITE_MAX_PAGE_SIZE 32768 + +class Codec +{ +public: + Codec(void *db); + Codec(const Codec* other, void *db); + + void GenerateWriteKey(const char *userPassword, int passwordLength); + void DropWriteKey(); + void SetWriteIsRead(); + void SetReadIsWrite(); + + unsigned char* Encrypt(int page, unsigned char *data, bool useWriteKey); + void Decrypt(int page, unsigned char *data); + + void SetPageSize(int pageSize) { m_pageSize = pageSize; } + + bool HasReadKey() { return m_hasReadKey; } + bool HasWriteKey() { return m_hasWriteKey; } + void* GetDB() { return m_db; } + const char* GetAndResetError(); + +private: + bool m_hasReadKey; + bool m_hasWriteKey; + + SymmetricKey + m_readKey, + m_writeKey, + m_ivReadKey, + m_ivWriteKey; + + Pipe + m_encipherPipe, + m_decipherPipe, + m_macPipe; + + Keyed_Filter *m_encipherFilter; + Keyed_Filter *m_decipherFilter; + MAC_Filter *m_cmac; + + int m_pageSize; + unsigned char m_page[SQLITE_MAX_PAGE_SIZE]; + void *m_db; + const char *m_botanErrorMsg; + + InitializationVector GetIVForPage(u32bit page, bool useWriteKey); + void InitializeCodec(void *db); +}; + +#endif diff --git a/lib/wrap/sqlite/codec_c_interface.h b/lib/wrap/sqlite/codec_c_interface.h new file mode 100644 index 000000000..1d8c8706f --- /dev/null +++ b/lib/wrap/sqlite/codec_c_interface.h @@ -0,0 +1,90 @@ +/* + * Encryption codec class C interface + * (C) 2010 Olivier de Gaalon + * + * Distributed under the terms of the Botan license + */ + +#ifndef _CODEC_C_INTERFACE_H_ +#define _CODEC_C_INTERFACE_H_ + +#ifdef __cplusplus +typedef unsigned char Bool; +#endif + +#ifdef __cplusplus +extern "C" +#endif +void InitializeBotan(); + +#ifdef __cplusplus +extern "C" +#endif +void* InitializeNewCodec(void *db); + +#ifdef __cplusplus +extern "C" +#endif +void* InitializeFromOtherCodec(const void *otherCodec, void *db); + +#ifdef __cplusplus +extern "C" +#endif +void GenerateWriteKey(void *codec, const char *userPassword, int passwordLength); + +#ifdef __cplusplus +extern "C" +#endif +void DropWriteKey(void *codec); + +#ifdef __cplusplus +extern "C" +#endif +void SetWriteIsRead(void *codec); + +#ifdef __cplusplus +extern "C" +#endif +void SetReadIsWrite(void *codec); + +#ifdef __cplusplus +extern "C" +#endif +unsigned char* Encrypt(void *codec, int page, unsigned char *data, Bool useWriteKey); + +#ifdef __cplusplus +extern "C" +#endif +void Decrypt(void *codec, int page, unsigned char *data); + +#ifdef __cplusplus +extern "C" +#endif +void SetPageSize(void *codec, int pageSize); + +#ifdef __cplusplus +extern "C" +#endif +Bool HasReadKey(void *codec); + +#ifdef __cplusplus +extern "C" +#endif +Bool HasWriteKey(void *codec); + +#ifdef __cplusplus +extern "C" +#endif +void* GetDB(void *codec); + +#ifdef __cplusplus +extern "C" +#endif +const char* GetAndResetError(void *codec); + +#ifdef __cplusplus +extern "C" +#endif +void DeleteCodec(void *codec); + +#endif
\ No newline at end of file diff --git a/lib/wrap/sqlite/codecext.c b/lib/wrap/sqlite/codecext.c new file mode 100644 index 000000000..e32d60dc2 --- /dev/null +++ b/lib/wrap/sqlite/codecext.c @@ -0,0 +1,242 @@ +/* + * Encryption codec implementation + * (C) 2010 Olivier de Gaalon + * + * Distributed under the terms of the Botan license + */ + +#ifndef SQLITE_OMIT_DISKIO +#ifdef SQLITE_HAS_CODEC + +#include "codec_c_interface.h" + +Bool HandleError(void *pCodec) +{ + const char *error = GetAndResetError(pCodec); + if (error) { + sqlite3Error((sqlite3*)GetDB(pCodec), SQLITE_ERROR, "Botan Error: %s", error); + return 1; + } + return 0; +} + +// Guessing that "see" is related to SQLite Encryption Extension" (the semi-official, for-pay, encryption codec) +// Just as useful for initializing Botan. +void sqlite3_activate_see(const char *info) +{ + InitializeBotan(); +} + +// Free the encryption codec, called from pager.c (address passed in sqlite3PagerSetCodec) +void sqlite3PagerFreeCodec(void *pCodec) +{ + if (pCodec) + DeleteCodec(pCodec); +} + +// Report the page size to the codec, called from pager.c (address passed in sqlite3PagerSetCodec) +void sqlite3CodecSizeChange(void *pCodec, int pageSize, int nReserve) +{ + SetPageSize(pCodec, pageSize); +} + +// Encrypt/Decrypt functionality, called by pager.c +void* sqlite3Codec(void *pCodec, void *data, Pgno nPageNum, int nMode) +{ + if (pCodec == NULL) //Db not encrypted + return data; + + switch(nMode) + { + case 0: // Undo a "case 7" journal file encryption + case 2: // Reload a page + case 3: // Load a page + if (HasReadKey(pCodec)) + Decrypt(pCodec, nPageNum, (unsigned char*) data); + break; + case 6: // Encrypt a page for the main database file + if (HasWriteKey(pCodec)) + data = Encrypt(pCodec, nPageNum, (unsigned char*) data, 1); + break; + case 7: // Encrypt a page for the journal file + /* + *Under normal circumstances, the readkey is the same as the writekey. However, + *when the database is being rekeyed, the readkey is not the same as the writekey. + *(The writekey is the "destination key" for the rekey operation and the readkey + *is the key the db is currently encrypted with) + *Therefore, for case 7, when the rollback is being written, always encrypt using + *the database's readkey, which is guaranteed to be the same key that was used to + *read and write the original data. + */ + if (HasReadKey(pCodec)) + data = Encrypt(pCodec, nPageNum, (unsigned char*) data, 0); + break; + } + + HandleError(pCodec); + + return data; +} + +int sqlite3CodecAttach(sqlite3 *db, int nDb, const void *zKey, int nKey) +{ + void *pCodec; + + if (zKey == NULL || nKey <= 0) + { + // No key specified, could mean either use the main db's encryption or no encryption + if (nDb != 0 && nKey < 0) + { + //Is an attached database, therefore use the key of main database, if main database is encrypted + void *pMainCodec = sqlite3PagerGetCodec(sqlite3BtreePager(db->aDb[0].pBt)); + if (pMainCodec != NULL) + { + pCodec = InitializeFromOtherCodec(pMainCodec, db); + sqlite3PagerSetCodec(sqlite3BtreePager(db->aDb[nDb].pBt), + sqlite3Codec, + sqlite3CodecSizeChange, + sqlite3PagerFreeCodec, pCodec); + } + } + } + else + { + // Key specified, setup encryption key for database + pCodec = InitializeNewCodec(db); + GenerateWriteKey(pCodec, (const char*) zKey, nKey); + SetReadIsWrite(pCodec); + sqlite3PagerSetCodec(sqlite3BtreePager(db->aDb[nDb].pBt), + sqlite3Codec, + sqlite3CodecSizeChange, + sqlite3PagerFreeCodec, pCodec); + } + + if (HandleError(pCodec)) + return SQLITE_ERROR; + + return SQLITE_OK; +} + +void sqlite3CodecGetKey(sqlite3* db, int nDb, void **zKey, int *nKey) +{ + // The unencrypted password is not stored for security reasons + // therefore always return NULL + *zKey = NULL; + *nKey = -1; +} + +int sqlite3_key(sqlite3 *db, const void *zKey, int nKey) +{ + // The key is only set for the main database, not the temp database + return sqlite3CodecAttach(db, 0, zKey, nKey); +} + +int sqlite3_rekey(sqlite3 *db, const void *zKey, int nKey) +{ + // Changes the encryption key for an existing database. + int rc = SQLITE_ERROR; + Btree *pbt = db->aDb[0].pBt; + Pager *pPager = sqlite3BtreePager(pbt); + void *pCodec = sqlite3PagerGetCodec(pPager); + + if ((zKey == NULL || nKey == 0) && pCodec == NULL) + { + // Database not encrypted and key not specified. Do nothing + return SQLITE_OK; + } + + if (pCodec == NULL) + { + // Database not encrypted, but key specified. Encrypt database + pCodec = InitializeNewCodec(db); + GenerateWriteKey(pCodec, (const char*) zKey, nKey); + + if (HandleError(pCodec)) + return SQLITE_ERROR; + + sqlite3PagerSetCodec(pPager, sqlite3Codec, sqlite3CodecSizeChange, sqlite3PagerFreeCodec, pCodec); + } + else if (zKey == NULL || nKey == 0) + { + // Database encrypted, but key not specified. Decrypt database + // Keep read key, drop write key + DropWriteKey(pCodec); + } + else + { + // Database encrypted and key specified. Re-encrypt database with new key + // Keep read key, change write key to new key + GenerateWriteKey(pCodec, (const char*) zKey, nKey); + if (HandleError(pCodec)) + return SQLITE_ERROR; + } + + // Start transaction + rc = sqlite3BtreeBeginTrans(pbt, 1); + if (rc == SQLITE_OK) + { + // Rewrite all pages using the new encryption key (if specified) + int nPageCount = -1; + sqlite3PagerPagecount(pPager, &nPageCount); + Pgno nPage = (Pgno) nPageCount; + + Pgno nSkip = PAGER_MJ_PGNO(pPager); + DbPage *pPage; + + Pgno n; + for (n = 1; rc == SQLITE_OK && n <= nPage; n++) + { + if (n == nSkip) + continue; + + rc = sqlite3PagerGet(pPager, n, &pPage); + + if (!rc) + { + rc = sqlite3PagerWrite(pPage); + sqlite3PagerUnref(pPage); + } + else + sqlite3Error(db, SQLITE_ERROR, "%s", "Error while rekeying database page. Transaction Canceled."); + } + } + else + sqlite3Error(db, SQLITE_ERROR, "%s", "Error beginning rekey transaction. Make sure that the current encryption key is correct."); + + if (rc == SQLITE_OK) + { + // All good, commit + rc = sqlite3BtreeCommit(pbt); + + if (rc == SQLITE_OK) + { + //Database rekeyed and committed successfully, update read key + if (HasWriteKey(pCodec)) + SetReadIsWrite(pCodec); + else //No write key == no longer encrypted + sqlite3PagerSetCodec(pPager, NULL, NULL, NULL, NULL); + } + else + { + //FIXME: can't trigger this, not sure if rollback is needed, reference implementation didn't rollback + sqlite3Error(db, SQLITE_ERROR, "%s", "Could not commit rekey transaction."); + } + } + else + { + // Rollback, rekey failed + sqlite3BtreeRollback(pbt, SQLITE_ERROR); + + // go back to read key + if (HasReadKey(pCodec)) + SetWriteIsRead(pCodec); + else //Database wasn't encrypted to start with + sqlite3PagerSetCodec(pPager, NULL, NULL, NULL, NULL); + } + + return rc; +} + +#endif // SQLITE_HAS_CODEC + +#endif // SQLITE_OMIT_DISKIO diff --git a/lib/wrap/sqlite/readme.txt b/lib/wrap/sqlite/readme.txt new file mode 100644 index 000000000..4971fd44b --- /dev/null +++ b/lib/wrap/sqlite/readme.txt @@ -0,0 +1,35 @@ +Build instructions for BotanSqlite3 +--- + +Requirements: + 1. Botan 1.9.0 or later + 2. SQLite3 amalgamation source, version 3.7.12.1 or later (previous versions may work, some will need minor changes) + + +Building: + +1. Extract sqlite3 amalgamation to a directory and add BotanSqlite3 source files + + If desired, codec.h can be modified to tweak the encryption algothrithms and parameters. (Defaults to Twofish/XTS with 256 bit key) + +2. Apply the patch "sqlite3.diff": + $ patch -p0 < sqlite3-amalgamation.patch + + If the patching fails for some reason (ie, changes in SQLite3), it should be trivial to do it manually. + +3. Compile the sqlite3 library with Botan encryption support: + $ gcc -c sqlite3.c -o botansqlite3.o && gcc -c codec.cpp -o codec.o `pkg-config --cflags botan-1.10` && ar rcs libbotansqlite3.a botansqlite3.o codec.o + + (replace "botan-1.10" with appropriate version) + +Testing: + +1. Build the test: + $ g++ test_sqlite.cpp -o test_sqlite `botan-config-1.10 --libs` ./libbotansqlite3.a + + (replace botan-config-1.10 w/ appropriate version) + +2. Run the test + $ ./test_sqlite + +3. Look for "All seems good" diff --git a/lib/wrap/sqlite/sqlite3-amalgamation.patch b/lib/wrap/sqlite/sqlite3-amalgamation.patch new file mode 100644 index 000000000..1c2a5c69d --- /dev/null +++ b/lib/wrap/sqlite/sqlite3-amalgamation.patch @@ -0,0 +1,15 @@ +--- ./sqlite3.c.orig 2011-05-12 10:03:32.051879390 +0800 ++++ ./sqlite3.c 2011-05-12 10:09:04.028550281 +0800 +@@ -17,6 +17,7 @@ + ** language. The code for the "sqlite3" command-line shell is also in a + ** separate file. This file contains only code for the core SQLite library. + */ ++#define SQLITE_HAS_CODEC 1 + #define SQLITE_CORE 1 + #define SQLITE_AMALGAMATION 1 + #ifndef SQLITE_PRIVATE +@@ -125956,3 +125957,4 @@ + #endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) */ + + /************** End of fts3_icu.c ********************************************/ ++#include "codecext.c" diff --git a/lib/wrap/sqlite/test_sqlite.cpp b/lib/wrap/sqlite/test_sqlite.cpp new file mode 100644 index 000000000..a516fd82b --- /dev/null +++ b/lib/wrap/sqlite/test_sqlite.cpp @@ -0,0 +1,103 @@ +/* + * Quick and dirty test for SQLite3 encryption codec. + * (C) 2010 Olivier de Gaalon + * + * Distributed under the terms of the Botan license + */ + +#define SQLITE_HAS_CODEC 1 + +#include <sqlite3.h> +#include <stdio.h> + +namespace SQL +{ + const char * CREATE_TABLE_TEST = + "create table 'test' (id INTEGER PRIMARY KEY, name TEXT, creationtime TEXT);"; + const char * CREATE_TABLE_TEST2 = + "create table 'test2' (id INTEGER PRIMARY KEY, name TEXT, creationtime TEXT);"; + const char * INSERT_INTO_TEST = + "INSERT INTO test (name, creationtime) VALUES ('widget', '1st time');\ + INSERT INTO test (name, creationtime) VALUES ('widget', '2nd time');\ + INSERT INTO test (name, creationtime) VALUES ('widget', '3rd time');\ + INSERT INTO test (name, creationtime) VALUES ('widget', '4th time');\ + INSERT INTO test (name, creationtime) VALUES ('widget', '5th time');"; + const char * INSERT_INTO_TEST2 = + "INSERT INTO test2 (name, creationtime) VALUES ('widget2', '1st time2');\ + INSERT INTO test2 (name, creationtime) VALUES ('widget2', '2nd time2');\ + INSERT INTO test2 (name, creationtime) VALUES ('widget2', '3rd time2');\ + INSERT INTO test2 (name, creationtime) VALUES ('widget2', '4th time2');\ + INSERT INTO test2 (name, creationtime) VALUES ('widget2', '5th time2');"; + const char * SELECT_FROM_TEST = + "SELECT * FROM test;"; + const char * SELECT_FROM_TEST2 = + "SELECT * FROM test2;"; +}; + +static int callback(void *NotUsed, int argc, char **argv, char **azColName){ + int i; + fprintf(stderr, "\t"); + for(i=0; i<argc; i++){ + fprintf(stderr, "%s = %s | ", azColName[i], argv[i] ? argv[i] : "NULL"); + } + fprintf(stderr, "\n"); + return 0; +} + +int main(int argc, char** argv) +{ + sqlite3 * db; + const char * key = "anotherkey"; + const char * dbname = "./testdb"; + int keylen = 7; + char * error=0; + + fprintf(stderr, "Creating Database \"%s\"\n", dbname); + int rc = sqlite3_open(dbname, &db); + if (rc != SQLITE_OK) { fprintf(stderr, "Can't open/create database: %s\n", sqlite3_errmsg(db)); return 1; } + + fprintf(stderr, "Keying Database with key \"%s\"\n", key); + rc = sqlite3_key(db, key, keylen); + if (rc != SQLITE_OK) { fprintf(stderr, "Can't key database: %s\n", sqlite3_errmsg(db)); return 1; } + + fprintf(stderr, "Creating table \"test\"\n"); + rc = sqlite3_exec(db, SQL::CREATE_TABLE_TEST, 0, 0, &error); + if (rc != SQLITE_OK) { fprintf(stderr, "SQL error: %s\n", error); return 1; } + + fprintf(stderr, "Creating table \"test2\"\n"); + rc = sqlite3_exec(db, SQL::CREATE_TABLE_TEST2, 0, 0, &error); + if (rc != SQLITE_OK) { fprintf(stderr, "SQL error: %s\n", error); return 1; } + + fprintf(stderr, "Inserting into table \"test\"\n"); + rc = sqlite3_exec(db, SQL::INSERT_INTO_TEST, 0, 0, &error); + if (rc != SQLITE_OK) { fprintf(stderr, "SQL error: %s\n", error); return 1; } + + fprintf(stderr, "Inserting into table \"test2\"\n"); + rc = sqlite3_exec(db, SQL::INSERT_INTO_TEST2, 0, 0, &error); + if (rc != SQLITE_OK) { fprintf(stderr, "SQL error: %s\n", error); return 1; } + + fprintf(stderr, "Closing Database \"%s\"\n", dbname); + sqlite3_close(db); + + fprintf(stderr, "Opening Database \"%s\"\n", dbname); + rc = sqlite3_open(dbname, &db); + if (rc != SQLITE_OK) { fprintf(stderr, "Can't open/create database: %s\n", sqlite3_errmsg(db)); return 1; } + + fprintf(stderr, "Keying Database with key \"%s\"\n", key); + rc = sqlite3_key(db, key, keylen); + if (rc != SQLITE_OK) { fprintf(stderr, "Can't key database: %s\n", sqlite3_errmsg(db)); return 1; } + + fprintf(stderr, "Selecting all from test\n"); + rc = sqlite3_exec(db, SQL::SELECT_FROM_TEST, callback, 0, &error); + if (rc != SQLITE_OK) { fprintf(stderr, "SQL error: %s\n", error); return 1; } + + fprintf(stderr, "Selecting all from test2\n"); + rc = sqlite3_exec(db, SQL::SELECT_FROM_TEST2, callback, 0, &error); + if (rc != SQLITE_OK) { fprintf(stderr, "SQL error: %s\n", error); return 1; } + + fprintf(stderr, "Closing Database \"%s\"\n", dbname); + sqlite3_close(db); + + fprintf(stderr, "All Seems Good \n"); + return 0; +} |