aboutsummaryrefslogtreecommitdiffstats
path: root/src/cli
diff options
context:
space:
mode:
authorSimon Warta <[email protected]>2015-12-08 17:25:03 +0100
committerSimon Warta <[email protected]>2015-12-09 15:28:07 +0100
commit1a0f3438a97b7913ccf444bc48510e0d145af551 (patch)
treebb4f275f5eba30076d9b48a9164c38b71e2a11e6 /src/cli
parent0261351f68674105a40d1938a001ba65dda756ed (diff)
Rename cmd/app -> cli
Diffstat (limited to 'src/cli')
-rw-r--r--src/cli/apps.h77
-rw-r--r--src/cli/asn1.cpp356
-rw-r--r--src/cli/base64.cpp97
-rw-r--r--src/cli/bcrypt.cpp49
-rw-r--r--src/cli/ca.cpp77
-rw-r--r--src/cli/cert_verify.cpp54
-rw-r--r--src/cli/compress.cpp126
-rw-r--r--src/cli/credentials.h158
-rw-r--r--src/cli/dl_group.cpp85
-rw-r--r--src/cli/dsa_sign.cpp85
-rw-r--r--src/cli/dsa_ver.cpp91
-rw-r--r--src/cli/factor.cpp160
-rw-r--r--src/cli/fpe.cpp154
-rw-r--r--src/cli/fuzzer.cpp141
-rw-r--r--src/cli/getopt.cpp53
-rw-r--r--src/cli/getopt.h107
-rw-r--r--src/cli/hash.cpp63
-rw-r--r--src/cli/implementation/speed.h30
-rw-r--r--src/cli/implementation/speed_prime.cpp96
-rw-r--r--src/cli/implementation/speed_public_key.cpp888
-rw-r--r--src/cli/implementation/speed_transform.cpp67
-rw-r--r--src/cli/implementation/timer.cpp60
-rw-r--r--src/cli/implementation/timer.h55
-rw-r--r--src/cli/is_prime.cpp50
-rw-r--r--src/cli/keygen.cpp128
-rw-r--r--src/cli/main.cpp187
-rw-r--r--src/cli/mce.cpp124
-rw-r--r--src/cli/ocsp.cpp53
-rw-r--r--src/cli/pkcs10.cpp64
-rw-r--r--src/cli/pkcs8.cpp76
-rw-r--r--src/cli/prime.cpp48
-rw-r--r--src/cli/rng.cpp68
-rw-r--r--src/cli/self_sig.cpp84
-rw-r--r--src/cli/speed.cpp214
-rw-r--r--src/cli/tls_client.cpp294
-rw-r--r--src/cli/tls_proxy.cpp459
-rw-r--r--src/cli/tls_server.cpp270
-rw-r--r--src/cli/x509print.cpp32
38 files changed, 5280 insertions, 0 deletions
diff --git a/src/cli/apps.h b/src/cli/apps.h
new file mode 100644
index 000000000..9f1f00ba2
--- /dev/null
+++ b/src/cli/apps.h
@@ -0,0 +1,77 @@
+/*
+* (C) 2014,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <iostream>
+#include <functional>
+#include <string>
+#include <set>
+#include <botan/build.h>
+#include <botan/hex.h>
+#include <botan/auto_rng.h>
+#include "getopt.h"
+
+using namespace Botan;
+
+typedef std::function<int (std::vector<std::string>)> app_fn;
+
+class AppRegistrations
+ {
+ public:
+ void add(const std::string& name, app_fn fn)
+ {
+ m_apps[name] = fn;
+ }
+
+ bool has(const std::string& cmd) const
+ {
+ return m_apps.count(cmd) > 0;
+ }
+
+ std::set<std::string> all_appnames() const
+ {
+ std::set<std::string> apps;
+ for(auto i : m_apps)
+ apps.insert(i.first);
+ return apps;
+ }
+
+ // TODO: Remove redundancy cmd == args[0]
+ int run(const std::string& cmd, std::vector<std::string> args) const
+ {
+ const auto app = m_apps.find(cmd);
+ if(app != m_apps.end())
+ return app->second(args);
+ return -1;
+ }
+
+ static AppRegistrations& instance()
+ {
+ static AppRegistrations s_apps;
+ return s_apps;
+ }
+
+ class AppRegistration
+ {
+ public:
+ AppRegistration(const std::string& name, app_fn fn)
+ {
+ AppRegistrations::instance().add(name, fn);
+ }
+ };
+
+ private:
+ AppRegistrations() {}
+
+ std::map<std::string, app_fn> m_apps;
+ };
+
+#define REGISTER_APP(nm) AppRegistrations::AppRegistration g_ ## nm ## _registration(#nm, nm)
+
+#if defined(BOTAN_TARGET_OS_IS_WINDOWS) || defined(BOTAN_TARGET_OS_IS_MINGW)
+ #undef BOTAN_TARGET_OS_HAS_SOCKETS
+#else
+ #define BOTAN_TARGET_OS_HAS_SOCKETS
+#endif
diff --git a/src/cli/asn1.cpp b/src/cli/asn1.cpp
new file mode 100644
index 000000000..2aa94cc39
--- /dev/null
+++ b/src/cli/asn1.cpp
@@ -0,0 +1,356 @@
+/*
+* (C) 2014,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "apps.h"
+
+#if defined(BOTAN_HAS_ASN1) && defined(BOTAN_HAS_PEM_CODEC)
+
+#include <botan/bigint.h>
+#include <botan/hex.h>
+#include <botan/der_enc.h>
+#include <botan/ber_dec.h>
+#include <botan/asn1_time.h>
+#include <botan/asn1_str.h>
+#include <botan/oids.h>
+#include <botan/pem.h>
+#include <botan/charset.h>
+using namespace Botan;
+
+#include <iostream>
+#include <iomanip>
+#include <sstream>
+#include <ctype.h>
+
+// Set this if your terminal understands UTF-8; otherwise output is in Latin-1
+#define UTF8_TERMINAL 1
+
+/*
+ What level the outermost layer of stuff is at. Probably 0 or 1; asn1parse
+ uses 0 as the outermost, while 1 makes more sense to me. 2+ doesn't make
+ much sense at all.
+*/
+#define INITIAL_LEVEL 0
+
+namespace {
+
+std::string url_encode(const std::vector<byte>& in)
+ {
+ std::ostringstream out;
+
+ size_t unprintable = 0;
+
+ for(size_t i = 0; i != in.size(); ++i)
+ {
+ const int c = in[i];
+ if(::isprint(c))
+ out << static_cast<char>(c);
+ else
+ {
+ out << "%" << std::hex << static_cast<int>(c) << std::dec;
+ ++unprintable;
+ }
+ }
+
+ if(unprintable >= in.size() / 4)
+ return hex_encode(in);
+
+ return out.str();
+ }
+
+void emit(const std::string& type, size_t level, size_t length, const std::string& value = "")
+ {
+ const size_t LIMIT = 4*1024;
+ const size_t BIN_LIMIT = 1024;
+
+ std::ostringstream out;
+
+ out << " d=" << std::setw(2) << level
+ << ", l=" << std::setw(4) << length << ": ";
+
+ for(size_t i = INITIAL_LEVEL; i != level; ++i)
+ out << ' ';
+
+ out << type;
+
+ bool should_skip = false;
+
+ if(value.length() > LIMIT)
+ should_skip = true;
+
+ if((type == "OCTET STRING" || type == "BIT STRING") && value.length() > BIN_LIMIT)
+ should_skip = true;
+
+ if(value != "" && !should_skip)
+ {
+ if(out.tellp() % 2 == 0) out << ' ';
+
+ while(out.tellp() < 50) out << ' ';
+
+ out << value;
+ }
+
+ std::cout << out.str() << std::endl;
+ }
+
+std::string type_name(ASN1_Tag type)
+ {
+ if(type == PRINTABLE_STRING) return "PRINTABLE STRING";
+ if(type == NUMERIC_STRING) return "NUMERIC STRING";
+ if(type == IA5_STRING) return "IA5 STRING";
+ if(type == T61_STRING) return "T61 STRING";
+ if(type == UTF8_STRING) return "UTF8 STRING";
+ if(type == VISIBLE_STRING) return "VISIBLE STRING";
+ if(type == BMP_STRING) return "BMP STRING";
+
+ if(type == UTC_TIME) return "UTC TIME";
+ if(type == GENERALIZED_TIME) return "GENERALIZED TIME";
+
+ if(type == OCTET_STRING) return "OCTET STRING";
+ if(type == BIT_STRING) return "BIT STRING";
+
+ if(type == ENUMERATED) return "ENUMERATED";
+ if(type == INTEGER) return "INTEGER";
+ if(type == NULL_TAG) return "NULL";
+ if(type == OBJECT_ID) return "OBJECT";
+ if(type == BOOLEAN) return "BOOLEAN";
+ return "(UNKNOWN)";
+ }
+
+void decode(BER_Decoder& decoder, size_t level)
+ {
+ BER_Object obj = decoder.get_next_object();
+
+ while(obj.type_tag != NO_OBJECT)
+ {
+ const ASN1_Tag type_tag = obj.type_tag;
+ const ASN1_Tag class_tag = obj.class_tag;
+ const size_t length = obj.value.size();
+
+ /* hack to insert the tag+length back in front of the stuff now
+ that we've gotten the type info */
+ DER_Encoder encoder;
+ encoder.add_object(type_tag, class_tag, obj.value);
+ std::vector<byte> bits = encoder.get_contents_unlocked();
+
+ BER_Decoder data(bits);
+
+ if(class_tag & CONSTRUCTED)
+ {
+ BER_Decoder cons_info(obj.value);
+ if(type_tag == SEQUENCE)
+ {
+ emit("SEQUENCE", level, length);
+ decode(cons_info, level+1);
+ }
+ else if(type_tag == SET)
+ {
+ emit("SET", level, length);
+ decode(cons_info, level+1);
+ }
+ else
+ {
+ std::string name;
+
+ if((class_tag & APPLICATION) || (class_tag & CONTEXT_SPECIFIC))
+ {
+ name = "cons [" + std::to_string(type_tag) + "]";
+
+ if(class_tag & APPLICATION)
+ name += " appl";
+ if(class_tag & CONTEXT_SPECIFIC)
+ name += " context";
+ }
+ else
+ name = type_name(type_tag) + " (cons)";
+
+ emit(name, level, length);
+ decode(cons_info, level+1);
+ }
+ }
+ else if((class_tag & APPLICATION) || (class_tag & CONTEXT_SPECIFIC))
+ {
+#if 0
+ std::vector<byte> bits;
+ data.decode(bits, type_tag);
+
+ try
+ {
+ BER_Decoder inner(bits);
+ decode(inner, level + 1);
+ }
+ catch(...)
+ {
+ emit("[" + std::to_string(type_tag) + "]", level, length,
+ url_encode(bits));
+ }
+#else
+ emit("[" + std::to_string(type_tag) + "]", level, length,
+ url_encode(bits));
+#endif
+ }
+ else if(type_tag == OBJECT_ID)
+ {
+ OID oid;
+ data.decode(oid);
+
+ std::string out = OIDS::lookup(oid);
+ if(out != oid.as_string())
+ out += " [" + oid.as_string() + "]";
+
+ emit(type_name(type_tag), level, length, out);
+ }
+ else if(type_tag == INTEGER || type_tag == ENUMERATED)
+ {
+ BigInt number;
+
+ if(type_tag == INTEGER)
+ data.decode(number);
+ else if(type_tag == ENUMERATED)
+ data.decode(number, ENUMERATED, class_tag);
+
+ std::vector<byte> rep;
+
+ /* If it's small, it's probably a number, not a hash */
+ if(number.bits() <= 20)
+ rep = BigInt::encode(number, BigInt::Decimal);
+ else
+ rep = BigInt::encode(number, BigInt::Hexadecimal);
+
+ std::string str;
+ for(size_t i = 0; i != rep.size(); ++i)
+ str += static_cast<char>(rep[i]);
+
+ emit(type_name(type_tag), level, length, str);
+ }
+ else if(type_tag == BOOLEAN)
+ {
+ bool boolean;
+ data.decode(boolean);
+ emit(type_name(type_tag),
+ level, length, (boolean ? "true" : "false"));
+ }
+ else if(type_tag == NULL_TAG)
+ {
+ emit(type_name(type_tag), level, length);
+ }
+ else if(type_tag == OCTET_STRING)
+ {
+ std::vector<byte> bits;
+ data.decode(bits, type_tag);
+
+ try
+ {
+ BER_Decoder inner(bits);
+ decode(inner, level + 1);
+ }
+ catch(...)
+ {
+ emit(type_name(type_tag), level, length,
+ url_encode(bits));
+ }
+ }
+ else if(type_tag == BIT_STRING)
+ {
+ std::vector<byte> bits;
+ data.decode(bits, type_tag);
+
+ std::vector<bool> bit_set;
+
+ for(size_t i = 0; i != bits.size(); ++i)
+ for(size_t j = 0; j != 8; ++j)
+ {
+ const bool bit = static_cast<bool>((bits[bits.size()-i-1] >> (7-j)) & 1);
+ bit_set.push_back(bit);
+ }
+
+ std::string bit_str;
+ for(size_t i = 0; i != bit_set.size(); ++i)
+ {
+ bool the_bit = bit_set[bit_set.size()-i-1];
+
+ if(!the_bit && bit_str.size() == 0)
+ continue;
+ bit_str += (the_bit ? "1" : "0");
+ }
+
+ emit(type_name(type_tag), level, length, bit_str);
+ }
+ else if(type_tag == PRINTABLE_STRING ||
+ type_tag == NUMERIC_STRING ||
+ type_tag == IA5_STRING ||
+ type_tag == T61_STRING ||
+ type_tag == VISIBLE_STRING ||
+ type_tag == UTF8_STRING ||
+ type_tag == BMP_STRING)
+ {
+ ASN1_String str;
+ data.decode(str);
+ if(UTF8_TERMINAL)
+ {
+ emit(type_name(type_tag), level, length,
+ Charset::transcode(str.iso_8859(),
+ LATIN1_CHARSET, UTF8_CHARSET));
+ }
+ else
+ {
+ emit(type_name(type_tag), level, length, str.iso_8859());
+ }
+ }
+ else if(type_tag == UTC_TIME || type_tag == GENERALIZED_TIME)
+ {
+ X509_Time time;
+ data.decode(time);
+ emit(type_name(type_tag), level, length, time.readable_string());
+ }
+ else
+ {
+ std::cout << "Unknown ASN.1 tag class="
+ << static_cast<int>(class_tag)
+ << " type="
+ << static_cast<int>(type_tag) << std::endl;
+ }
+
+ obj = decoder.get_next_object();
+ }
+ }
+
+int asn1(const std::vector<std::string> &args)
+ {
+ if(args.size() != 2)
+ {
+ std::cout << "Usage: " << args[0] << " <file>" << std::endl;
+ return 1;
+ }
+
+ try {
+ DataSource_Stream in(args[1]);
+
+ if(!PEM_Code::matches(in))
+ {
+ BER_Decoder decoder(in);
+ decode(decoder, INITIAL_LEVEL);
+ }
+ else
+ {
+ std::string label; // ignored
+ BER_Decoder decoder(PEM_Code::decode(in, label));
+ decode(decoder, INITIAL_LEVEL);
+ }
+ }
+ catch(std::exception& e)
+ {
+ std::cout << "Error: " << e.what() << std::endl;
+ return 2;
+ }
+
+ return 0;
+ }
+
+REGISTER_APP(asn1);
+
+}
+
+#endif // BOTAN_HAS_ASN1 && BOTAN_HAS_PEM_CODEC
diff --git a/src/cli/base64.cpp b/src/cli/base64.cpp
new file mode 100644
index 000000000..d2a9a1853
--- /dev/null
+++ b/src/cli/base64.cpp
@@ -0,0 +1,97 @@
+/*
+* Encode/decode base64 strings
+* (C) 2009 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "apps.h"
+
+#if defined(BOTAN_HAS_CODEC_FILTERS)
+
+#include <fstream>
+#include <iostream>
+#include <string>
+#include <vector>
+#include <cstdlib>
+#include <botan/b64_filt.h>
+#include <botan/pipe.h>
+
+namespace {
+
+int base64(const std::vector<std::string> &args)
+ {
+ if(args.size() < 2)
+ {
+ std::cout << "Usage: " << args[0] << " [-w] [-c n] [-e|-d] files...\n"
+ " -e : Encode input to base64 strings (default)\n"
+ " -d : Decode base64 input\n"
+ " -w : Wrap lines\n"
+ " -c n: Wrap lines at column n, default 78" << std::endl;
+ return 1;
+ }
+
+ u32bit column = 78;
+ bool wrap = false;
+ bool encoding = true;
+ std::vector<std::string> files;
+
+ for(int j = 1; j < args.size(); j++)
+ {
+ const std::string this_arg = args[j];
+
+ if(this_arg == "-w")
+ wrap = true;
+ else if(this_arg == "-e");
+ else if(this_arg == "-d")
+ encoding = false;
+ else if(this_arg == "-c")
+ {
+ if(j+1 < args.size())
+ {
+ column = to_u32bit(args[j+1]);
+ j++;
+ }
+ else
+ {
+ std::cout << "No argument for -c option" << std::endl;
+ return 1;
+ }
+ }
+ else files.push_back(args[j]);
+ }
+
+ for(unsigned int j = 0; j != files.size(); j++)
+ {
+ std::istream* stream;
+ if(files[j] == "-") stream = &std::cin;
+ else stream = new std::ifstream(files[j]);
+
+ if(!*stream)
+ {
+ std::cout << "ERROR, couldn't open " << files[j] << std::endl;
+ continue;
+ }
+
+ Botan::Filter* f = nullptr;
+
+ if(encoding)
+ f = new Botan::Base64_Encoder(wrap, column);
+ else
+ f = new Botan::Base64_Decoder;
+
+ Botan::Pipe pipe(f);
+ pipe.start_msg();
+ *stream >> pipe;
+ pipe.end_msg();
+ pipe.set_default_msg(j);
+ std::cout << pipe;
+ if(files[j] != "-") delete stream;
+ }
+ return 0;
+ }
+
+REGISTER_APP(base64);
+
+}
+#endif // BOTAN_HAS_CODEC_FILTERS
diff --git a/src/cli/bcrypt.cpp b/src/cli/bcrypt.cpp
new file mode 100644
index 000000000..81f7c536e
--- /dev/null
+++ b/src/cli/bcrypt.cpp
@@ -0,0 +1,49 @@
+/*
+* (C) 2014,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "apps.h"
+
+#if defined(BOTAN_HAS_BCRYPT)
+
+#include <botan/bcrypt.h>
+
+namespace {
+
+int bcrypt(const std::vector<std::string> &args)
+ {
+ if(args.size() == 2)
+ {
+ AutoSeeded_RNG rng;
+
+ const std::string password = args[1];
+
+ std::cout << generate_bcrypt(password, rng, 12) << std::endl;
+ return 0;
+ }
+ else if(args.size() == 3)
+ {
+ const std::string password = args[1];
+ const std::string hash = args[2];
+
+ if(hash.length() != 60)
+ std::cout << "Note: bcrypt '" << hash << "' has wrong length and cannot be valid" << std::endl;
+
+ const bool ok = check_bcrypt(password, hash);
+
+ std::cout << "Password is " << (ok ? "valid" : "NOT valid") << std::endl;
+ return (ok ? 0 : 1);
+ }
+
+ std::cout << "Usage: " << args[0] << " password\n"
+ << " " << args[0] << " password passhash" << std::endl;
+ return 1;
+ }
+
+REGISTER_APP(bcrypt);
+
+}
+
+#endif
diff --git a/src/cli/ca.cpp b/src/cli/ca.cpp
new file mode 100644
index 000000000..fb6d9582a
--- /dev/null
+++ b/src/cli/ca.cpp
@@ -0,0 +1,77 @@
+/*
+* (C) 2014,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "apps.h"
+
+#if defined(BOTAN_HAS_X509_CERTIFICATES)
+
+#include <botan/x509_ca.h>
+#include <botan/pkcs8.h>
+
+namespace {
+
+int ca(const std::vector<std::string> &args)
+ {
+ using namespace Botan;
+
+ if(args.size() != 5)
+ {
+ std::cout << "Usage: " << args[0] << " <passphrase> "
+ << "<ca cert> <ca key> <pkcs10>" << std::endl;
+ return 1;
+ }
+
+ const std::string arg_passphrase = args[1];
+ const std::string arg_ca_cert = args[2];
+ const std::string arg_ca_key = args[3];
+ const std::string arg_req_file = args[4];
+
+ try
+ {
+ AutoSeeded_RNG rng;
+
+ X509_Certificate ca_cert(arg_ca_cert);
+
+ std::unique_ptr<PKCS8_PrivateKey> privkey(
+ PKCS8::load_key(arg_ca_key, rng, arg_passphrase)
+ );
+
+ X509_CA ca(ca_cert, *privkey, "SHA-256");
+
+ // got a request
+ PKCS10_Request req(arg_req_file);
+
+ // you would insert checks here, and perhaps modify the request
+ // (this example should be extended to show how)
+
+ // now sign the request
+ auto now = std::chrono::system_clock::now();
+
+ X509_Time start_time(now);
+
+ typedef std::chrono::duration<int, std::ratio<31556926>> years;
+
+ X509_Time end_time(now + years(1));
+
+ X509_Certificate new_cert = ca.sign_request(req, rng,
+ start_time, end_time);
+
+ // send the new cert back to the requestor
+ std::cout << new_cert.PEM_encode();
+ }
+ catch(std::exception& e)
+ {
+ std::cout << e.what() << std::endl;
+ return 1;
+ }
+ return 0;
+ }
+
+REGISTER_APP(ca);
+
+}
+
+#endif
diff --git a/src/cli/cert_verify.cpp b/src/cli/cert_verify.cpp
new file mode 100644
index 000000000..7a1bec983
--- /dev/null
+++ b/src/cli/cert_verify.cpp
@@ -0,0 +1,54 @@
+/*
+* Simple example of a certificate validation
+* (C) 2010 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "apps.h"
+
+#if defined(BOTAN_HAS_X509_CERTIFICATES)
+
+#include <botan/x509path.h>
+
+namespace {
+
+int cert_verify(const std::vector<std::string> &args)
+ {
+ using namespace Botan;
+
+ if(args.size() <= 2)
+ {
+ std::cout << "Usage: " << args[0] << " subject.pem [CA certificates...]" << std::endl;
+ return 1;
+ }
+
+ X509_Certificate subject_cert(args[1]);
+
+ Certificate_Store_In_Memory certs;
+
+ for(const auto certfile : std::vector<std::string>(args.begin()+2, args.end()))
+ {
+ certs.add_certificate(X509_Certificate(certfile));
+ }
+
+ Path_Validation_Restrictions restrictions;
+
+ Path_Validation_Result result =
+ x509_path_validate(subject_cert,
+ restrictions,
+ certs);
+
+ if(result.successful_validation())
+ std::cout << "Certificate validated" << std::endl;
+ else
+ std::cout << "Certificate did not validate - " << result.result_string() << std::endl;
+
+ return 0;
+ }
+
+REGISTER_APP(cert_verify);
+
+}
+
+#endif // BOTAN_HAS_X509_CERTIFICATES
diff --git a/src/cli/compress.cpp b/src/cli/compress.cpp
new file mode 100644
index 000000000..93bc76eb4
--- /dev/null
+++ b/src/cli/compress.cpp
@@ -0,0 +1,126 @@
+/*
+* (C) 2014,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "apps.h"
+
+#if defined(BOTAN_HAS_COMPRESSION)
+
+#include <botan/compression.h>
+#include <fstream>
+
+namespace {
+
+void do_compress(Transform& comp, std::ifstream& in, std::ostream& out)
+ {
+ secure_vector<byte> buf;
+
+ comp.start();
+
+ while(in.good())
+ {
+ buf.resize(64*1024);
+ in.read(reinterpret_cast<char*>(&buf[0]), buf.size());
+ buf.resize(in.gcount());
+
+ comp.update(buf);
+
+ out.write(reinterpret_cast<const char*>(&buf[0]), buf.size());
+ }
+
+ buf.clear();
+ comp.finish(buf);
+ out.write(reinterpret_cast<const char*>(&buf[0]), buf.size());
+ }
+
+int compress(const std::vector<std::string> &args)
+ {
+ if(args.size() != 2 && args.size() != 3 && args.size() != 4)
+ {
+ std::cout << "Usage: " << args[0] << " input [type] [level]" << std::endl;
+ return 1;
+ }
+
+ const std::string in_file = args[1];
+ std::ifstream in(in_file);
+
+ if(!in.good())
+ {
+ std::cout << "Couldn't read " << in_file << std::endl;
+ return 1;
+ }
+
+ const std::string suffix = args.size() >= 3 ? args[2] : "gz";
+ const size_t level = args.size() >= 4 ? to_u32bit(args[3]) : 9;
+
+ std::unique_ptr<Transform> compress(make_compressor(suffix, level));
+
+ if(!compress)
+ {
+ std::cout << suffix << " compression not supported" << std::endl;
+ return 1;
+ }
+
+ const std::string out_file = in_file + "." + suffix;
+ std::ofstream out(out_file);
+
+ do_compress(*compress, in, out);
+
+ return 0;
+ }
+
+void parse_extension(const std::string& in_file,
+ std::string& out_file,
+ std::string& suffix)
+ {
+ auto last_dot = in_file.find_last_of('.');
+ if(last_dot == std::string::npos || last_dot == 0)
+ throw std::runtime_error("No extension detected in filename '" + in_file + "'");
+
+ out_file = in_file.substr(0, last_dot);
+ suffix = in_file.substr(last_dot+1, std::string::npos);
+ }
+
+int uncompress(const std::vector<std::string> &args)
+ {
+ if(args.size() != 2)
+ {
+ std::cout << "Usage: " << args[0] << " <file>" << std::endl;
+ return 1;
+ }
+
+ const std::string in_file = args[1];
+ std::ifstream in(in_file);
+
+ if(!in.good())
+ {
+ std::cout << "Couldn't read '" << args[1] << "'" << std::endl;
+ return 1;
+ }
+
+ std::string out_file, suffix;
+ parse_extension(in_file, out_file, suffix);
+
+ std::ofstream out(out_file);
+
+ std::unique_ptr<Transform> decompress(make_decompressor(suffix));
+
+ if(!decompress)
+ {
+ std::cout << suffix << " decompression not supported" << std::endl;
+ return 1;
+ }
+
+ do_compress(*decompress, in, out);
+
+ return 0;
+ }
+
+REGISTER_APP(compress);
+REGISTER_APP(uncompress);
+
+}
+
+#endif // BOTAN_HAS_COMPRESSION
diff --git a/src/cli/credentials.h b/src/cli/credentials.h
new file mode 100644
index 000000000..06349657d
--- /dev/null
+++ b/src/cli/credentials.h
@@ -0,0 +1,158 @@
+/*
+* (C) 2014,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef EXAMPLE_CREDENTIALS_MANAGER_H__
+#define EXAMPLE_CREDENTIALS_MANAGER_H__
+
+#include <botan/pkcs8.h>
+#include <botan/credentials_manager.h>
+#include <botan/x509self.h>
+#include <iostream>
+#include <fstream>
+#include <memory>
+
+inline bool value_exists(const std::vector<std::string>& vec,
+ const std::string& val)
+ {
+ for(size_t i = 0; i != vec.size(); ++i)
+ if(vec[i] == val)
+ return true;
+ return false;
+ }
+
+class Basic_Credentials_Manager : public Credentials_Manager
+ {
+ public:
+ Basic_Credentials_Manager()
+ {
+ load_certstores();
+ }
+
+ Basic_Credentials_Manager(RandomNumberGenerator& rng,
+ const std::string& server_crt,
+ const std::string& server_key)
+ {
+ Certificate_Info cert;
+
+ cert.key.reset(PKCS8::load_key(server_key, rng));
+
+ DataSource_Stream in(server_crt);
+ while(!in.end_of_data())
+ {
+ try
+ {
+ cert.certs.push_back(X509_Certificate(in));
+ }
+ catch(std::exception& e)
+ {
+
+ }
+ }
+
+ // TODO: attempt to validate chain ourselves
+
+ m_creds.push_back(cert);
+ }
+
+ void load_certstores()
+ {
+ try
+ {
+ // TODO: make path configurable
+ const std::vector<std::string> paths = { "/usr/share/ca-certificates" };
+
+ for(auto&& path : paths)
+ {
+ std::shared_ptr<Certificate_Store> cs(new Certificate_Store_In_Memory(path));
+ m_certstores.push_back(cs);
+ }
+ }
+ catch(std::exception& e)
+ {
+ //std::cout << e.what() << "\n";
+ }
+ }
+
+ std::vector<Botan::Certificate_Store*>
+ trusted_certificate_authorities(const std::string& type,
+ const std::string& /*hostname*/) override
+ {
+ std::vector<Botan::Certificate_Store*> v;
+
+ // don't ask for client certs
+ if(type == "tls-server")
+ return v;
+
+ for(auto&& cs : m_certstores)
+ v.push_back(cs.get());
+
+ return v;
+ }
+
+ void verify_certificate_chain(
+ const std::string& type,
+ const std::string& purported_hostname,
+ const std::vector<X509_Certificate>& cert_chain) override
+ {
+ try
+ {
+ Credentials_Manager::verify_certificate_chain(type,
+ purported_hostname,
+ cert_chain);
+ }
+ catch(std::exception& e)
+ {
+ std::cout << e.what() << std::endl;
+ //throw;
+ }
+ }
+
+ std::vector<X509_Certificate> cert_chain(
+ const std::vector<std::string>& algos,
+ const std::string& type,
+ const std::string& hostname) override
+ {
+ BOTAN_UNUSED(type);
+
+ for(auto&& i : m_creds)
+ {
+ if(std::find(algos.begin(), algos.end(), i.key->algo_name()) == algos.end())
+ continue;
+
+ if(hostname != "" && !i.certs[0].matches_dns_name(hostname))
+ continue;
+
+ return i.certs;
+ }
+
+ return std::vector<X509_Certificate>();
+ }
+
+ Private_Key* private_key_for(const X509_Certificate& cert,
+ const std::string& /*type*/,
+ const std::string& /*context*/) override
+ {
+ for(auto&& i : m_creds)
+ {
+ if(cert == i.certs[0])
+ return i.key.get();
+ }
+
+ return nullptr;
+ }
+
+ private:
+ struct Certificate_Info
+ {
+ std::vector<X509_Certificate> certs;
+ std::shared_ptr<Private_Key> key;
+ };
+
+ std::vector<Certificate_Info> m_creds;
+ std::vector<std::shared_ptr<Certificate_Store>> m_certstores;
+ };
+
+#endif
diff --git a/src/cli/dl_group.cpp b/src/cli/dl_group.cpp
new file mode 100644
index 000000000..e9a4f3fd4
--- /dev/null
+++ b/src/cli/dl_group.cpp
@@ -0,0 +1,85 @@
+/*
+* (C) 2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "apps.h"
+
+#if defined(BOTAN_HAS_DL_GROUP)
+
+#include <botan/dl_group.h>
+#include <fstream>
+
+namespace {
+
+std::string read_file_contents(const std::string& filename)
+ {
+ std::ifstream in(filename.c_str());
+ if(!in.good())
+ throw std::runtime_error("Failure reading " + filename);
+
+ std::vector<std::string> contents;
+ size_t total_size = 0;
+ while(in.good())
+ {
+ std::string line;
+ std::getline(in, line);
+ total_size += line.size();
+ contents.push_back(std::move(line));
+ }
+
+ std::string res;
+ contents.reserve(total_size);
+ for(auto&& line : contents)
+ res += line;
+ return res;
+ }
+
+int dl_group(const std::vector<std::string> &args)
+ {
+ if(args.size() < 2)
+ {
+ std::cout << "Usage: " << args[0] << " [create bits|info file]" << std::endl;
+ return 1;
+ }
+
+ const std::string cmd = args[1];
+
+ if(cmd == "create")
+ {
+ AutoSeeded_RNG rng;
+ const size_t bits = to_u32bit(args[2]);
+
+ const DL_Group::PrimeType prime_type = DL_Group::Strong;
+ //const DL_Group::PrimeType prime_type = DL_Group::Prime_Subgroup;
+
+ DL_Group grp(rng, prime_type, bits);
+
+ std::cout << grp.PEM_encode(DL_Group::DSA_PARAMETERS);
+ }
+ else if(cmd == "info")
+ {
+ DL_Group grp;
+ std::string pem = read_file_contents(args[2]);
+ std::cout << pem << "\n";
+
+ std::cout << "DL_Group " << grp.get_p().bits() << " bits\n";
+ std::cout << "p=" << grp.get_p() << "\n";
+ std::cout << "q=" << grp.get_q() << "\n";
+ std::cout << "g=" << grp.get_g() << "\n";
+ }
+ else
+ {
+ std::cout << "ERROR: Unknown command\n";
+ return 1;
+ }
+
+ return 0;
+ }
+
+REGISTER_APP(dl_group);
+
+}
+
+#endif
diff --git a/src/cli/dsa_sign.cpp b/src/cli/dsa_sign.cpp
new file mode 100644
index 000000000..03aede585
--- /dev/null
+++ b/src/cli/dsa_sign.cpp
@@ -0,0 +1,85 @@
+/*
+* (C) 2014,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "apps.h"
+
+#if defined(BOTAN_HAS_DSA)
+
+#include <botan/dsa.h>
+#include <botan/pubkey.h>
+#include <botan/pkcs8.h>
+#include <botan/base64.h>
+#include <fstream>
+
+namespace {
+
+int dsa_sign(const std::vector<std::string> &args)
+ {
+ using namespace Botan;
+
+ const std::string SUFFIX = ".sig";
+
+ if(args.size() != 4)
+ {
+ std::cout << "Usage: " << args[0] << " keyfile messagefile passphrase"
+ << std::endl;
+ return 1;
+ }
+
+ try {
+ std::string passphrase(args[3]);
+
+ std::ifstream message(args[2], std::ios::binary);
+ if(!message)
+ {
+ std::cout << "Couldn't read the message file." << std::endl;
+ return 1;
+ }
+
+ std::string outfile = args[2] + SUFFIX;
+ std::ofstream sigfile(outfile);
+ if(!sigfile)
+ {
+ std::cout << "Couldn't write the signature to "
+ << outfile << std::endl;
+ return 1;
+ }
+
+ AutoSeeded_RNG rng;
+
+ std::unique_ptr<PKCS8_PrivateKey> key(
+ PKCS8::load_key(args[1], rng, passphrase)
+ );
+
+ DSA_PrivateKey* dsakey = dynamic_cast<DSA_PrivateKey*>(key.get());
+
+ if(!dsakey)
+ {
+ std::cout << "The loaded key is not a DSA key!" << std::endl;
+ return 1;
+ }
+
+ PK_Signer signer(*dsakey, "EMSA1(SHA-1)");
+
+ DataSource_Stream in(message);
+ byte buf[4096] = { 0 };
+ while(size_t got = in.read(buf, sizeof(buf)))
+ signer.update(buf, got);
+
+ sigfile << base64_encode(signer.signature(rng)) << std::endl;
+ }
+ catch(std::exception& e)
+ {
+ std::cout << "Exception caught: " << e.what() << std::endl;
+ }
+ return 0;
+ }
+
+REGISTER_APP(dsa_sign);
+
+}
+
+#endif
diff --git a/src/cli/dsa_ver.cpp b/src/cli/dsa_ver.cpp
new file mode 100644
index 000000000..64d60a5cf
--- /dev/null
+++ b/src/cli/dsa_ver.cpp
@@ -0,0 +1,91 @@
+/*
+* (C) 2014,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "apps.h"
+
+#if defined(BOTAN_HAS_DSA)
+
+#include <fstream>
+#include <botan/pubkey.h>
+#include <botan/dsa.h>
+#include <botan/base64.h>
+
+using namespace Botan;
+
+namespace {
+
+int dsa_verify(const std::vector<std::string> &args)
+ {
+ if(args.size() != 4)
+ {
+ std::cout << "Usage: " << args[0]
+ << " keyfile messagefile sigfile" << std::endl;
+ return 1;
+ }
+
+
+ try {
+ std::ifstream message(args[2], std::ios::binary);
+ if(!message)
+ {
+ std::cout << "Couldn't read the message file." << std::endl;
+ return 1;
+ }
+
+ std::ifstream sigfile(args[3]);
+ if(!sigfile)
+ {
+ std::cout << "Couldn't read the signature file." << std::endl;
+ return 1;
+ }
+
+ std::string sigstr;
+ getline(sigfile, sigstr);
+
+ std::unique_ptr<X509_PublicKey> key(X509::load_key(args[1]));
+ DSA_PublicKey* dsakey = dynamic_cast<DSA_PublicKey*>(key.get());
+
+ if(!dsakey)
+ {
+ std::cout << "The loaded key is not a DSA key!" << std::endl;
+ return 1;
+ }
+
+ secure_vector<byte> sig = base64_decode(sigstr);
+
+ PK_Verifier ver(*dsakey, "EMSA1(SHA-1)");
+
+ DataSource_Stream in(message);
+ byte buf[4096] = { 0 };
+ while(size_t got = in.read(buf, sizeof(buf)))
+ ver.update(buf, got);
+
+ const bool ok = ver.check_signature(sig);
+
+ if(ok)
+ {
+ std::cout << "Signature verified" << std::endl;
+ return 0;
+ }
+ else
+ {
+ std::cout << "Signature did NOT verify" << std::endl;
+ return 1;
+ }
+ }
+ catch(std::exception& e)
+ {
+ std::cout << "Exception caught: " << e.what() << std::endl;
+ return 2;
+ }
+ }
+
+REGISTER_APP(dsa_verify);
+
+}
+
+#endif
+
diff --git a/src/cli/factor.cpp b/src/cli/factor.cpp
new file mode 100644
index 000000000..d2c0a2df5
--- /dev/null
+++ b/src/cli/factor.cpp
@@ -0,0 +1,160 @@
+/*
+* (C) 2009-2010 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*
+* Factor integers using a combination of trial division by small
+* primes, and Pollard's Rho algorithm
+*/
+
+#include "apps.h"
+
+#if defined(BOTAN_HAS_NUMBERTHEORY)
+
+#include <botan/reducer.h>
+#include <botan/numthry.h>
+
+#include <algorithm>
+#include <iostream>
+#include <iterator>
+
+using namespace Botan;
+
+namespace {
+
+/*
+* Pollard's Rho algorithm, as described in the MIT algorithms book. We
+* use (x^2+x) mod n instead of (x*2-1) mod n as the random function,
+* it _seems_ to lead to faster factorization for the values I tried.
+*/
+BigInt rho(const BigInt& n, RandomNumberGenerator& rng)
+ {
+ BigInt x = BigInt::random_integer(rng, 0, n-1);
+ BigInt y = x;
+ BigInt d = 0;
+
+ Modular_Reducer mod_n(n);
+
+ u32bit i = 1, k = 2;
+ while(true)
+ {
+ i++;
+
+ if(i == 0) // overflow, bail out
+ break;
+
+ x = mod_n.multiply((x + 1), x);
+
+ d = gcd(y - x, n);
+ if(d != 1 && d != n)
+ return d;
+
+ if(i == k)
+ {
+ y = x;
+ k = 2*k;
+ }
+ }
+ return 0;
+ }
+
+// Remove (and return) any small (< 2^16) factors
+std::vector<BigInt> remove_small_factors(BigInt& n)
+ {
+ std::vector<BigInt> factors;
+
+ while(n.is_even())
+ {
+ factors.push_back(2);
+ n /= 2;
+ }
+
+ for(u32bit j = 0; j != PRIME_TABLE_SIZE; j++)
+ {
+ if(n < PRIMES[j])
+ break;
+
+ BigInt x = gcd(n, PRIMES[j]);
+
+ if(x != 1)
+ {
+ n /= x;
+
+ u32bit occurs = 0;
+ while(x != 1)
+ {
+ x /= PRIMES[j];
+ occurs++;
+ }
+
+ for(u32bit k = 0; k != occurs; k++)
+ factors.push_back(PRIMES[j]);
+ }
+ }
+
+ return factors;
+ }
+
+std::vector<BigInt> factorize(const BigInt& n_in,
+ RandomNumberGenerator& rng)
+ {
+ BigInt n = n_in;
+ std::vector<BigInt> factors = remove_small_factors(n);
+
+ while(n != 1)
+ {
+ if(is_prime(n, rng))
+ {
+ factors.push_back(n);
+ break;
+ }
+
+ BigInt a_factor = 0;
+ while(a_factor == 0)
+ a_factor = rho(n, rng);
+
+ std::vector<BigInt> rho_factored = factorize(a_factor, rng);
+ for(u32bit j = 0; j != rho_factored.size(); j++)
+ factors.push_back(rho_factored[j]);
+
+ n /= a_factor;
+ }
+ return factors;
+ }
+
+int factor(const std::vector<std::string> &args)
+ {
+ if(args.size() != 2)
+ {
+ std::cout << "Usage: " << args[0] << " <integer>" << std::endl;
+ return 1;
+ }
+
+ try
+ {
+ BigInt n(args[1]);
+
+ AutoSeeded_RNG rng;
+
+ std::vector<BigInt> factors = factorize(n, rng);
+ std::sort(factors.begin(), factors.end());
+
+ std::cout << n << ": ";
+ std::copy(factors.begin(),
+ factors.end(),
+ std::ostream_iterator<BigInt>(std::cout, " "));
+ std::cout << std::endl;
+ }
+ catch(std::exception& e)
+ {
+ std::cout << e.what() << std::endl;
+ return 1;
+ }
+ return 0;
+ }
+
+REGISTER_APP(factor);
+
+}
+
+#endif // BOTAN_HAS_NUMBERTHEORY
diff --git a/src/cli/fpe.cpp b/src/cli/fpe.cpp
new file mode 100644
index 000000000..97ca34b24
--- /dev/null
+++ b/src/cli/fpe.cpp
@@ -0,0 +1,154 @@
+/*
+* (C) 2014,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "apps.h"
+
+#if defined(BOTAN_HAS_FPE_FE1) && defined(BOTAN_HAS_SHA1)
+
+#include <botan/fpe_fe1.h>
+#include <botan/sha160.h>
+#include <stdexcept>
+
+using namespace Botan;
+
+namespace {
+
+byte luhn_checksum(u64bit cc_number)
+ {
+ byte sum = 0;
+
+ bool alt = false;
+ while(cc_number)
+ {
+ byte digit = cc_number % 10;
+ if(alt)
+ {
+ digit *= 2;
+ if(digit > 9)
+ digit -= 9;
+ }
+
+ sum += digit;
+
+ cc_number /= 10;
+ alt = !alt;
+ }
+
+ return (sum % 10);
+ }
+
+bool luhn_check(u64bit cc_number)
+ {
+ return (luhn_checksum(cc_number) == 0);
+ }
+
+u64bit cc_rank(u64bit cc_number)
+ {
+ // Remove Luhn checksum
+ return cc_number / 10;
+ }
+
+u64bit cc_derank(u64bit cc_number)
+ {
+ for(u32bit i = 0; i != 10; ++i)
+ if(luhn_check(cc_number * 10 + i))
+ return (cc_number * 10 + i);
+ return 0;
+ }
+
+/*
+* Use the SHA-1 hash of the account name or ID as a tweak
+*/
+std::vector<byte> sha1(const std::string& acct_name)
+ {
+ SHA_160 hash;
+ hash.update(acct_name);
+ return unlock(hash.final());
+ }
+
+u64bit encrypt_cc_number(u64bit cc_number,
+ const SymmetricKey& key,
+ const std::string& acct_name)
+ {
+ BigInt n = 1000000000000000;
+
+ u64bit cc_ranked = cc_rank(cc_number);
+
+ BigInt c = FPE::fe1_encrypt(n, cc_ranked, key, sha1(acct_name));
+
+ if(c.bits() > 50)
+ throw std::runtime_error("FPE produced a number too large");
+
+ u64bit enc_cc = 0;
+ for(u32bit i = 0; i != 7; ++i)
+ enc_cc = (enc_cc << 8) | c.byte_at(6-i);
+ return cc_derank(enc_cc);
+ }
+
+u64bit decrypt_cc_number(u64bit enc_cc,
+ const SymmetricKey& key,
+ const std::string& acct_name)
+ {
+ BigInt n = 1000000000000000;
+
+ u64bit cc_ranked = cc_rank(enc_cc);
+
+ BigInt c = FPE::fe1_decrypt(n, cc_ranked, key, sha1(acct_name));
+
+ if(c.bits() > 50)
+ throw std::runtime_error("FPE produced a number too large");
+
+ u64bit dec_cc = 0;
+ for(u32bit i = 0; i != 7; ++i)
+ dec_cc = (dec_cc << 8) | c.byte_at(6-i);
+ return cc_derank(dec_cc);
+ }
+
+int fpe(const std::vector<std::string> &args)
+ {
+ if(args.size() != 4)
+ {
+ std::cout << "Usage: " << args[0] << " cc-number acct-name passwd" << std::endl;
+ return 1;
+ }
+
+ u64bit cc_number = atoll(args[1].c_str());
+ std::string acct_name = args[2];
+ std::string passwd = args[3];
+
+ std::cout << "Input was: " << cc_number << ' '
+ << luhn_check(cc_number) << std::endl;
+
+ /*
+ * In practice something like PBKDF2 with a salt and high iteration
+ * count would be a good idea.
+ */
+ SymmetricKey key(sha1(passwd));
+
+ u64bit enc_cc = encrypt_cc_number(cc_number, key, acct_name);
+
+ std::cout << "Encrypted: " << enc_cc
+ << ' ' << luhn_check(enc_cc) << std::endl;
+
+ u64bit dec_cc = decrypt_cc_number(enc_cc, key, acct_name);
+
+ std::cout << "Decrypted: " << dec_cc
+ << ' ' << luhn_check(dec_cc) << std::endl;
+
+ if(dec_cc != cc_number)
+ {
+ std::cout << "Something went wrong :( Bad CC checksum?" << std::endl;
+ return 2;
+ }
+
+ return 0;
+ }
+
+REGISTER_APP(fpe);
+
+}
+
+#endif // BOTAN_HAS_FPE_FE1 && BOTAN_HAS_SHA1
diff --git a/src/cli/fuzzer.cpp b/src/cli/fuzzer.cpp
new file mode 100644
index 000000000..35adb9711
--- /dev/null
+++ b/src/cli/fuzzer.cpp
@@ -0,0 +1,141 @@
+/*
+* (C) 2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "apps.h"
+#include <fstream>
+#include <botan/auto_rng.h>
+
+#if defined(BOTAN_HAS_X509_CERTIFICATES)
+#include <botan/x509cert.h>
+#include <botan/x509_crl.h>
+#include <botan/pkcs8.h>
+#endif
+
+#if defined(BOTAN_HAS_TLS)
+#include <botan/tls_client.h>
+#endif
+
+namespace {
+
+#if defined(BOTAN_HAS_TLS)
+
+class Fuzzer_Creds : public Credentials_Manager
+ {
+ public:
+ void verify_certificate_chain(const std::string& type,
+ const std::string& purported_hostname,
+ const std::vector<X509_Certificate>& cert_chain) override
+ {
+ try
+ {
+ Credentials_Manager::verify_certificate_chain(type,
+ purported_hostname,
+ cert_chain);
+ }
+ catch(std::exception& e) {}
+ }
+
+ std::string psk_identity_hint(const std::string&, const std::string&) override { return "psk_hint"; }
+ std::string psk_identity(const std::string&, const std::string&, const std::string&) override { return "psk_id"; }
+ SymmetricKey psk(const std::string&, const std::string&, const std::string&) override
+ {
+ return SymmetricKey("AABBCCDDEEFF00112233445566778899");
+ }
+ };
+
+#endif
+
+int fuzzer(const std::vector<std::string> &args)
+ {
+ if(args.size() != 3)
+ {
+ std::cout << "Usage: " << args[0] << " [type] [input_file]\n"
+ << "Hook for fuzzers such as afl (produces no output)\n"
+ << "Types: cert crl privkey tls_client" << std::endl;
+ return 1;
+ }
+
+ const std::string type = args[1];
+ const std::string input = args[2];
+
+ AutoSeeded_RNG rng;
+
+#if defined(BOTAN_HAS_X509_CERTIFICATES)
+ if(type == "cert")
+ {
+ X509_Certificate cert(input);
+ return 0;
+ }
+
+ if(type == "crl")
+ {
+ X509_CRL crl(input);
+ return 0;
+ }
+
+ if(type == "privkey")
+ {
+ std::unique_ptr<Private_Key>(PKCS8::load_key(input, rng));
+ return 0;
+ }
+
+#endif
+
+#if defined(BOTAN_HAS_TLS)
+ if(type == "tls_client")
+ {
+ auto dev_null = [](const byte[], size_t) {};
+
+ auto ignore_alerts = [](TLS::Alert, const byte[], size_t) {};
+ auto ignore_hs = [](const TLS::Session&) { return true; };
+
+ TLS::Session_Manager_In_Memory session_manager(rng);
+ TLS::Policy policy;
+ TLS::Protocol_Version client_offer = TLS::Protocol_Version::TLS_V12;
+ TLS::Server_Information info("server.name", 443);
+ const std::vector<std::string> protocols_to_offer = { "fuzz/1.0", "http/1.1", "bunny/1.21.3" };
+ Fuzzer_Creds creds;
+
+ TLS::Client client(dev_null,
+ dev_null,
+ ignore_alerts,
+ ignore_hs,
+ session_manager,
+ creds,
+ policy,
+ rng,
+ info,
+ client_offer,
+ protocols_to_offer);
+
+ std::ifstream in(input.c_str());
+
+ std::vector<byte> buf(1024);
+
+ try
+ {
+ while(in.good())
+ {
+ in.read((char*)&buf[0], buf.size());
+ size_t got = in.gcount();
+ client.received_data(&buf[0], got);
+ }
+ }
+ catch(std::exception& e)
+ {
+ return 0;
+ }
+ return 0;
+ }
+#endif
+
+ std::cout << "Unknown type '" << type << "'" << std::endl;
+ return 1;
+ }
+
+REGISTER_APP(fuzzer);
+
+}
diff --git a/src/cli/getopt.cpp b/src/cli/getopt.cpp
new file mode 100644
index 000000000..7b7e14932
--- /dev/null
+++ b/src/cli/getopt.cpp
@@ -0,0 +1,53 @@
+/*
+* (C) 2009 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "getopt.h"
+#include <iostream>
+
+void OptionParser::parse(const std::vector<std::string> &args)
+ {
+ const std::string appname = args[0];
+
+ // ship first, args[0] is the app name
+ for(size_t j = 1; j != args.size(); j++)
+ {
+ std::string arg = args[j];
+
+ if(arg == "help" || arg == "--help" || arg == "-h")
+ return help(std::cout, appname);
+
+ if(arg.size() > 2 && arg[0] == '-' && arg[1] == '-')
+ {
+ const std::string opt_name = arg.substr(0, arg.find('='));
+
+ arg = arg.substr(2);
+
+ std::string::size_type mark = arg.find('=');
+ OptionFlag opt = find_option(arg.substr(0, mark));
+
+ if(opt.takes_arg())
+ {
+ if(mark == std::string::npos)
+ throw std::runtime_error("Option " + opt_name +
+ " requires an argument");
+
+ std::string name = arg.substr(0, mark);
+ std::string value = arg.substr(mark+1);
+
+ options[name] = value;
+ }
+ else
+ {
+ if(mark != std::string::npos)
+ throw std::runtime_error("Option " + opt_name + " does not take an argument");
+
+ options[arg] = "";
+ }
+ }
+ else
+ leftover.push_back(arg);
+ }
+ }
diff --git a/src/cli/getopt.h b/src/cli/getopt.h
new file mode 100644
index 000000000..f683159d0
--- /dev/null
+++ b/src/cli/getopt.h
@@ -0,0 +1,107 @@
+/*
+* (C) 2009 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_CHECK_GETOPT_H__
+#define BOTAN_CHECK_GETOPT_H__
+
+#include <string>
+#include <vector>
+#include <stdexcept>
+#include <map>
+
+#include <botan/parsing.h>
+
+class OptionParser
+ {
+ public:
+ std::vector<std::string> arguments() const { return leftover; }
+
+ bool is_set(const std::string& key) const
+ {
+ return (options.find(key) != options.end());
+ }
+
+ std::string value(const std::string& key) const
+ {
+ std::map<std::string, std::string>::const_iterator i = options.find(key);
+ if(i == options.end())
+ throw std::runtime_error("Option '" + key + "' not set");
+ return i->second;
+ }
+
+ std::string value_if_set(const std::string& key) const
+ {
+ return value_or_else(key, "");
+ }
+
+ std::string value_or_else(const std::string& key,
+ const std::string& or_else) const
+ {
+ return is_set(key) ? value(key) : or_else;
+ }
+
+ size_t int_value_or_else(const std::string& key, size_t or_else) const
+ {
+ return is_set(key) ? Botan::to_u32bit(value(key)) : or_else;
+ }
+
+ void help(std::ostream& o, const std::string &appname)
+ {
+ o << "Usage: " << appname << " ";
+
+ for(auto flag : flags)
+ {
+ o << "--" << flag.name();
+ if(flag.takes_arg())
+ o << "=";
+ o << " ";
+ }
+
+ o << std::endl;
+ }
+
+ void parse(const std::vector<std::string> &args);
+
+ OptionParser(const std::string& opt_string)
+ {
+ std::vector<std::string> opts = Botan::split_on(opt_string, '|');
+
+ for(size_t j = 0; j != opts.size(); j++)
+ flags.push_back(OptionFlag(opts[j]));
+ }
+
+ private:
+ class OptionFlag
+ {
+ public:
+ std::string name() const { return opt_name; }
+ bool takes_arg() const { return opt_takes_arg; }
+
+ OptionFlag(const std::string& opt_string)
+ {
+ std::string::size_type mark = opt_string.find('=');
+ opt_name = opt_string.substr(0, mark);
+ opt_takes_arg = (mark != std::string::npos);
+ }
+ private:
+ std::string opt_name;
+ bool opt_takes_arg;
+ };
+
+ OptionFlag find_option(const std::string& name) const
+ {
+ for(size_t j = 0; j != flags.size(); j++)
+ if(flags[j].name() == name)
+ return flags[j];
+ throw std::runtime_error("Unknown option " + name);
+ }
+
+ std::vector<OptionFlag> flags;
+ std::map<std::string, std::string> options;
+ std::vector<std::string> leftover;
+ };
+
+#endif
diff --git a/src/cli/hash.cpp b/src/cli/hash.cpp
new file mode 100644
index 000000000..81a72ca17
--- /dev/null
+++ b/src/cli/hash.cpp
@@ -0,0 +1,63 @@
+/*
+* (C) 2009 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "apps.h"
+
+#if defined(BOTAN_HAS_CODEC_FILTERS)
+
+#include <botan/filters.h>
+#include <iostream>
+#include <fstream>
+
+using namespace Botan;
+
+namespace {
+
+int hash(const std::vector<std::string> &args)
+ {
+ if(args.size() < 3)
+ {
+ std::cout << "Usage: " << args[0] << " digest <filenames>" << std::endl;
+ return 1;
+ }
+
+ std::string hash = args[1];
+ /* a couple of special cases, kind of a crock */
+ if(hash == "sha1") hash = "SHA-1";
+ if(hash == "md5") hash = "MD5";
+
+ try {
+ Pipe pipe(new Hash_Filter(hash), new Hex_Encoder);
+
+ int skipped = 0;
+ for(int j = 2; j < args.size(); j++)
+ {
+ std::ifstream file(args[j], std::ios::binary);
+ if(!file)
+ {
+ std::cout << "ERROR: could not open " << args[j] << std::endl;
+ skipped++;
+ continue;
+ }
+ pipe.start_msg();
+ file >> pipe;
+ pipe.end_msg();
+ pipe.set_default_msg(j-2-skipped);
+ std::cout << pipe << " " << args[j] << std::endl;
+ }
+ }
+ catch(std::exception& e)
+ {
+ std::cout << "Exception caught: " << e.what() << std::endl;
+ }
+ return 0;
+ }
+
+REGISTER_APP(hash);
+
+}
+
+#endif // BOTAN_HAS_CODEC_FILTERS
diff --git a/src/cli/implementation/speed.h b/src/cli/implementation/speed.h
new file mode 100644
index 000000000..3cfd0ef61
--- /dev/null
+++ b/src/cli/implementation/speed.h
@@ -0,0 +1,30 @@
+/*
+* (C) 2014 Jack Lloyd
+* (C) 2015 Simon Warta (Kullo GmbH)
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_CHECK_BENCHMARK_H__
+#define BOTAN_CHECK_BENCHMARK_H__
+
+#include <botan/rng.h>
+#include <string>
+#include <chrono>
+
+void benchmark_public_key(Botan::RandomNumberGenerator& rng,
+ const std::string& algo,
+ const std::string& provider,
+ double seconds);
+
+std::map<std::string, double> benchmark_is_prime(Botan::RandomNumberGenerator &rng,
+ const std::chrono::milliseconds runtime);
+
+std::map<std::string, double> benchmark_random_prime(Botan::RandomNumberGenerator &rng,
+ const std::chrono::milliseconds runtime);
+
+bool benchmark_transform(Botan::RandomNumberGenerator& rng, const std::string& algo_name,
+ const std::chrono::milliseconds runtime);
+
+
+#endif
diff --git a/src/cli/implementation/speed_prime.cpp b/src/cli/implementation/speed_prime.cpp
new file mode 100644
index 000000000..a7a344bef
--- /dev/null
+++ b/src/cli/implementation/speed_prime.cpp
@@ -0,0 +1,96 @@
+/*
+* (C) 2015 Simon Warta (Kullo GmbH)
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "speed.h"
+
+using namespace Botan;
+
+#if defined(BOTAN_HAS_NUMBERTHEORY)
+
+#include <botan/numthry.h>
+#include <chrono>
+
+namespace {
+const size_t RSA_EXP = 65537;
+const size_t RSA_BITS = 2048;
+
+const auto SAMPLE_DATA = std::vector<BigInt>{
+ BigInt("147572674850319649408425528388978190129147580193826207917614581140087181349813950149403694089438460074197915858493514824951402666113125007396293585089750170962882245605820159076002066981429137353341175732273168874025070408827561035943771554301600092787967626039381375658829078877390735440179859333066156895363"),
+ BigInt("147572674850319649408425528388978190129147580193826207917614581140087181349813950149403694089438460074197915858493514824951402666113125007396293585089750170962882245605820159076002066981429137353341175732273168874025070408827561035943771554301600092787967626039381375658829078877390735440179859333066156895377"),
+ BigInt("147572674850319649408425528388978190129147580193826207917614581140087181349813950149403694089438460074197915858493514824951402666113125007396293585089750170962882245605820159076002066981429137353341175732273168874025070408827561035943771554301600092787967626039381375658829078877390735440179859333066156895381"),
+ BigInt("147572674850319649408425528388978190129147580193826207917614581140087181349813950149403694089438460074197915858493514824951402666113125007396293585089750170962882245605820159076002066981429137353341175732273168874025070408827561035943771554301600092787967626039381375658829078877390735440179859333066156895389"),
+ BigInt("147572674850319649408425528388978190129147580193826207917614581140087181349813950149403694089438460074197915858493514824951402666113125007396293585089750170962882245605820159076002066981429137353341175732273168874025070408827561035943771554301600092787967626039381375658829078877390735440179859333066156895399"),
+ BigInt("147572674850319649408425528388978190129147580193826207917614581140087181349813950149403694089438460074197915858493514824951402666113125007396293585089750170962882245605820159076002066981429137353341175732273168874025070408827561035943771554301600092787967626039381375658829078877390735440179859333066156895401"),
+ BigInt("147572674850319649408425528388978190129147580193826207917614581140087181349813950149403694089438460074197915858493514824951402666113125007396293585089750170962882245605820159076002066981429137353341175732273168874025070408827561035943771554301600092787967626039381375658829078877390735440179859333066156895431"),
+ BigInt("147572674850319649408425528388978190129147580193826207917614581140087181349813950149403694089438460074197915858493514824951402666113125007396293585089750170962882245605820159076002066981429137353341175732273168874025070408827561035943771554301600092787967626039381375658829078877390735440179859333066156895441"),
+ BigInt("147572674850319649408425528388978190129147580193826207917614581140087181349813950149403694089438460074197915858493514824951402666113125007396293585089750170962882245605820159076002066981429137353341175732273168874025070408827561035943771554301600092787967626039381375658829078877390735440179859333066156895461"),
+ BigInt("147572674850319649408425528388978190129147580193826207917614581140087181349813950149403694089438460074197915858493514824951402666113125007396293585089750170962882245605820159076002066981429137353341175732273168874025070408827561035943771554301600092787967626039381375658829078877390735440179859333066156895471"),
+ };
+}
+
+#endif // BOTAN_HAS_NUMBERTHEORY
+
+std::map<std::string, double> benchmark_is_prime(RandomNumberGenerator& rng,
+ const std::chrono::milliseconds runtime)
+ {
+ std::map<std::string, double> speeds;
+
+#if defined(BOTAN_HAS_NUMBERTHEORY)
+
+ std::chrono::nanoseconds time_used(0);
+ size_t reps = 0;
+
+ auto start = std::chrono::high_resolution_clock::now();
+
+ while(time_used < runtime)
+ {
+ // main work
+ for (const BigInt &p : SAMPLE_DATA)
+ {
+ is_prime(p, rng, 64, true);
+ }
+
+ ++reps;
+ time_used = std::chrono::high_resolution_clock::now() - start;
+ }
+
+ const double seconds_used = static_cast<double>(time_used.count()) / 1000000000;
+ speeds["base"] = reps / seconds_used; // ie, return ops per second
+
+#endif // BOTAN_HAS_NUMBERTHEORY
+
+ return speeds;
+ }
+
+std::map<std::string, double> benchmark_random_prime(RandomNumberGenerator& rng,
+ const std::chrono::milliseconds runtime)
+ {
+ std::map<std::string, double> speeds;
+
+#if defined(BOTAN_HAS_NUMBERTHEORY)
+
+ std::chrono::nanoseconds time_used(0);
+ size_t reps = 0;
+
+ auto start = std::chrono::high_resolution_clock::now();
+
+ while(time_used < runtime)
+ {
+ // main work
+ random_prime(rng, (RSA_BITS + 1) / 2, RSA_EXP);
+
+ ++reps;
+ time_used = std::chrono::high_resolution_clock::now() - start;
+ }
+
+ const double seconds_used = static_cast<double>(time_used.count()) / 1000000000;
+ speeds["base"] = reps / seconds_used; // ie, return ops per second
+
+#endif // BOTAN_HAS_NUMBERTHEORY
+
+ return speeds;
+ }
+
diff --git a/src/cli/implementation/speed_public_key.cpp b/src/cli/implementation/speed_public_key.cpp
new file mode 100644
index 000000000..2ff49bd15
--- /dev/null
+++ b/src/cli/implementation/speed_public_key.cpp
@@ -0,0 +1,888 @@
+/*
+* (C) 2009-2010 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "../apps.h"
+
+#if defined(BOTAN_HAS_PUBLIC_KEY_CRYPTO)
+
+#include "speed.h"
+#include "timer.h"
+
+#include <map>
+#include <sstream>
+#include <botan/mem_ops.h>
+#include <botan/parsing.h>
+#include <botan/pkcs8.h>
+#include <botan/pubkey.h>
+#include <botan/x509_key.h>
+
+#if defined(BOTAN_HAS_RSA)
+ #include <botan/rsa.h>
+#endif
+
+#if defined(BOTAN_HAS_DSA)
+ #include <botan/dsa.h>
+#endif
+
+#if defined(BOTAN_HAS_DIFFIE_HELLMAN)
+ #include <botan/dh.h>
+#endif
+
+#if defined(BOTAN_HAS_CURVE_25519)
+ #include <botan/curve25519.h>
+#endif
+
+#if defined(BOTAN_HAS_NYBERG_RUEPPEL)
+ #include <botan/nr.h>
+#endif
+
+#if defined(BOTAN_HAS_RW)
+ #include <botan/rw.h>
+#endif
+
+#if defined(BOTAN_HAS_ELGAMAL)
+ #include <botan/elgamal.h>
+#endif
+
+#if defined(BOTAN_HAS_DLIES) && defined(BOTAN_HAS_KDF2)
+ #include <botan/dlies.h>
+ #include <botan/kdf2.h>
+ #include <botan/hmac.h>
+ #include <botan/sha160.h>
+#endif
+
+#if defined(BOTAN_HAS_ECDSA)
+ #include <botan/ecdsa.h>
+#endif
+
+#if defined(BOTAN_HAS_ECDH)
+ #include <botan/ecdh.h>
+#endif
+
+#if defined(BOTAN_HAS_GOST_34_10_2001)
+ #include <botan/gost_3410.h>
+#endif
+
+#if defined(BOTAN_HAS_MCELIECE)
+ #include <botan/mceliece.h>
+ #include <botan/mce_kem.h>
+#endif
+
+using namespace Botan;
+
+#include <iostream>
+#include <fstream>
+#include <string>
+#include <memory>
+#include <set>
+
+namespace {
+
+const char* ec_domains[] = {
+ // "secp160r2",
+ // "secp192r1",
+ // "secp224r1",
+ "secp256r1",
+ "secp384r1",
+ "secp521r1",
+ //"brainpool256r1",
+ //"brainpool384r1",
+ //"brainpool512r1",
+ nullptr
+};
+
+class Benchmark_Report
+ {
+ public:
+ void report(const std::string& name, Timer timer)
+ {
+ std::cout << name << " " << timer << std::endl;
+ data[name].insert(timer);
+ }
+
+ private:
+ std::map<std::string, std::set<Timer> > data;
+ };
+
+void benchmark_enc_dec(PK_Encryptor& enc, PK_Decryptor& dec,
+ Timer& enc_timer, Timer& dec_timer,
+ RandomNumberGenerator& rng,
+ u32bit runs, double seconds)
+ {
+ std::vector<byte> plaintext, ciphertext;
+
+ for(u32bit i = 0; i != runs; ++i)
+ {
+ if(enc_timer.seconds() < seconds || ciphertext.size() == 0)
+ {
+ plaintext.resize(enc.maximum_input_size());
+
+ // Ensure for Raw, etc, it stays large
+ if((i % 100) == 0)
+ {
+ rng.randomize(&plaintext[0], plaintext.size());
+ plaintext[0] |= 0x80;
+ }
+
+ enc_timer.start();
+ ciphertext = enc.encrypt(plaintext, rng);
+ enc_timer.stop();
+ }
+
+ if(dec_timer.seconds() < seconds)
+ {
+ dec_timer.start();
+ std::vector<byte> plaintext_out = unlock(dec.decrypt(ciphertext));
+ dec_timer.stop();
+
+ if(plaintext_out != plaintext)
+ { // has never happened...
+ std::cerr << "Contents mismatched on decryption during benchmark!" << std::endl;
+ }
+ }
+ }
+ }
+
+void benchmark_sig_ver(PK_Verifier& ver, PK_Signer& sig,
+ Timer& verify_timer, Timer& sig_timer,
+ RandomNumberGenerator& rng,
+ u32bit runs, double seconds)
+ {
+ std::vector<byte> message, signature, sig_random;
+
+ for(u32bit i = 0; i != runs; ++i)
+ {
+ if(sig_timer.seconds() < seconds || signature.size() == 0)
+ {
+ if((i % 100) == 0)
+ {
+ message.resize(48);
+ rng.randomize(&message[0], message.size());
+ }
+ else
+ message[0]++;
+
+ sig_timer.start();
+ signature = sig.sign_message(message, rng);
+ sig_timer.stop();
+ }
+
+ if(verify_timer.seconds() < seconds)
+ {
+ verify_timer.start();
+ const bool verified = ver.verify_message(message, signature);
+ verify_timer.stop();
+
+ if(!verified)
+ std::cerr << "Signature verification failure" << std::endl;
+
+ if((i % 100) == 0)
+ {
+ sig_random = unlock(rng.random_vec(signature.size()));
+
+ verify_timer.start();
+ const bool verified_bad = ver.verify_message(message, sig_random);
+ verify_timer.stop();
+
+ if(verified_bad)
+ std::cerr << "Signature verification failure (bad sig OK)" << std::endl;
+ }
+ }
+ }
+ }
+
+/*
+ Between benchmark_rsa_rw + benchmark_dsa_nr:
+ Type of the key
+ Arguments to the constructor (A list of some arbitrary type?)
+ Type of padding
+*/
+
+#if defined(BOTAN_HAS_RSA)
+void benchmark_rsa(const std::string& provider,
+ RandomNumberGenerator& rng,
+ double seconds,
+ Benchmark_Report& report)
+ {
+
+ size_t keylens[] = { 1024, 2048, 4096, 6144, 0 };
+
+ for(size_t i = 0; keylens[i]; ++i)
+ {
+ size_t keylen = keylens[i];
+
+ //const std::string sig_padding = "PSSR(SHA-256)";
+ //const std::string enc_padding = "OAEP(SHA-1)";
+ const std::string sig_padding = "EMSA-PKCS1-v1_5(SHA-1)";
+ const std::string enc_padding = "EME-PKCS1-v1_5";
+
+ Timer keygen_timer("keygen");
+ Timer verify_timer(sig_padding + " verify");
+ Timer sig_timer(sig_padding + " signature");
+ Timer enc_timer(enc_padding + " encrypt");
+ Timer dec_timer(enc_padding + " decrypt");
+
+ try
+ {
+
+#if 0
+ // for profiling
+ PKCS8_PrivateKey* pkcs8_key =
+ PKCS8::load_key("rsa/" + to_string(keylen) + ".pem", rng);
+ RSA_PrivateKey* key_ptr = dynamic_cast<RSA_PrivateKey*>(pkcs8_key);
+
+ RSA_PrivateKey key = *key_ptr;
+#else
+ keygen_timer.start();
+ RSA_PrivateKey key(rng, keylen);
+ keygen_timer.stop();
+#endif
+
+ while(verify_timer.seconds() < seconds ||
+ sig_timer.seconds() < seconds)
+ {
+ PK_Encryptor_EME enc(key, enc_padding, provider);
+ PK_Decryptor_EME dec(key, enc_padding, provider);
+
+ benchmark_enc_dec(enc, dec, enc_timer, dec_timer,
+ rng, 10000, seconds);
+
+ PK_Signer sig(key, sig_padding, IEEE_1363, provider);
+ PK_Verifier ver(key, sig_padding, IEEE_1363, provider);
+
+ benchmark_sig_ver(ver, sig, verify_timer,
+ sig_timer, rng, 10000, seconds);
+ }
+
+ const std::string rsa_keylen = "RSA-" + std::to_string(keylen);
+
+ report.report(rsa_keylen, keygen_timer);
+ report.report(rsa_keylen, verify_timer);
+ report.report(rsa_keylen, sig_timer);
+ report.report(rsa_keylen, enc_timer);
+ report.report(rsa_keylen, dec_timer);
+ }
+ catch(Stream_IO_Error)
+ {
+ }
+ catch(Exception& e)
+ {
+ std::cout << e.what() << std::endl;
+ }
+ }
+
+ }
+#endif
+
+#if defined(BOTAN_HAS_RW)
+void benchmark_rw(RandomNumberGenerator& rng,
+ double seconds,
+ Benchmark_Report& report)
+ {
+
+ const u32bit keylens[] = { 1024, 2048, 4096, 6144, 0 };
+
+ for(size_t j = 0; keylens[j]; j++)
+ {
+ u32bit keylen = keylens[j];
+
+ std::string padding = "EMSA2(SHA-256)";
+
+ Timer keygen_timer("keygen");
+ Timer verify_timer(padding + " verify");
+ Timer sig_timer(padding + " signature");
+
+ while(verify_timer.seconds() < seconds ||
+ sig_timer.seconds() < seconds)
+ {
+ keygen_timer.start();
+ RW_PrivateKey key(rng, keylen);
+ keygen_timer.stop();
+
+ PK_Signer sig(key, padding);
+ PK_Verifier ver(key, padding);
+
+ benchmark_sig_ver(ver, sig, verify_timer, sig_timer,
+ rng, 10000, seconds);
+ }
+
+ const std::string nm = "RW-" + std::to_string(keylen);
+ report.report(nm, keygen_timer);
+ report.report(nm, verify_timer);
+ report.report(nm, sig_timer);
+ }
+ }
+#endif
+
+#if defined(BOTAN_HAS_ECDSA)
+
+void benchmark_ecdsa(const std::string& provider,
+ RandomNumberGenerator& rng,
+ double seconds,
+ Benchmark_Report& report)
+ {
+ for(size_t j = 0; ec_domains[j]; j++)
+ {
+ EC_Group params(ec_domains[j]);
+
+ const size_t pbits = params.get_curve().get_p().bits();
+
+ size_t hashbits = pbits;
+
+ if(hashbits <= 192)
+ hashbits = 160;
+ if(hashbits == 521)
+ hashbits = 512;
+
+ const std::string padding = "EMSA1(SHA-" + std::to_string(hashbits) + ")";
+
+ Timer keygen_timer("keygen");
+ Timer verify_timer(padding + " verify");
+ Timer sig_timer(padding + " signature");
+
+ while(verify_timer.seconds() < seconds ||
+ sig_timer.seconds() < seconds)
+ {
+ keygen_timer.start();
+ ECDSA_PrivateKey key(rng, params);
+ keygen_timer.stop();
+
+ PK_Signer sig(key, padding, IEEE_1363, provider);
+ PK_Verifier ver(key, padding, IEEE_1363, provider);
+
+ benchmark_sig_ver(ver, sig, verify_timer,
+ sig_timer, rng, 1000, seconds);
+ }
+
+ const std::string nm = std::string("ECDSA ") + ec_domains[j];
+
+ report.report(nm, keygen_timer);
+ report.report(nm, verify_timer);
+ report.report(nm, sig_timer);
+ }
+ }
+
+#endif
+
+#if defined(BOTAN_HAS_GOST_34_10_2001)
+
+void benchmark_gost_3410(RandomNumberGenerator& rng,
+ double seconds,
+ Benchmark_Report& report)
+ {
+ for(size_t j = 0; ec_domains[j]; j++)
+ {
+ EC_Group params(ec_domains[j]);
+
+ const size_t pbits = params.get_curve().get_p().bits();
+
+ const std::string padding = "EMSA1(GOST-34.11)";
+
+ Timer keygen_timer("keygen");
+ Timer verify_timer(padding + " verify");
+ Timer sig_timer(padding + " signature");
+
+ while(verify_timer.seconds() < seconds ||
+ sig_timer.seconds() < seconds)
+ {
+ keygen_timer.start();
+ GOST_3410_PrivateKey key(rng, params);
+ keygen_timer.stop();
+
+ PK_Signer sig(key, padding, IEEE_1363);
+ PK_Verifier ver(key, padding);
+
+ benchmark_sig_ver(ver, sig, verify_timer,
+ sig_timer, rng, 1000, seconds);
+ }
+
+ const std::string nm = "GOST-34.10-" + std::to_string(pbits);
+
+ report.report(nm, keygen_timer);
+ report.report(nm, verify_timer);
+ report.report(nm, sig_timer);
+ }
+ }
+
+#endif
+
+#if defined(BOTAN_HAS_ECDH)
+
+void benchmark_ecdh(RandomNumberGenerator& rng,
+ double seconds,
+ Benchmark_Report& report)
+ {
+ for(size_t j = 0; ec_domains[j]; j++)
+ {
+ EC_Group params(ec_domains[j]);
+
+ size_t pbits = params.get_curve().get_p().bits();
+
+ Timer keygen_timer("keygen");
+ Timer kex_timer("key exchange");
+
+ while(kex_timer.seconds() < seconds)
+ {
+ keygen_timer.start();
+ ECDH_PrivateKey ecdh1(rng, params);
+ keygen_timer.stop();
+
+ keygen_timer.start();
+ ECDH_PrivateKey ecdh2(rng, params);
+ keygen_timer.stop();
+
+ PK_Key_Agreement ka1(ecdh1, "KDF2(SHA-1)");
+ PK_Key_Agreement ka2(ecdh2, "KDF2(SHA-1)");
+
+ SymmetricKey secret1, secret2;
+
+ for(size_t i = 0; i != 1000; ++i)
+ {
+ if(kex_timer.seconds() > seconds)
+ break;
+
+ kex_timer.start();
+ secret1 = ka1.derive_key(32, ecdh2.public_value());
+ kex_timer.stop();
+
+ kex_timer.start();
+ secret2 = ka2.derive_key(32, ecdh1.public_value());
+ kex_timer.stop();
+
+ if(secret1 != secret2)
+ std::cerr << "ECDH secrets did not match" << std::endl;
+ }
+ }
+
+ const std::string nm = "ECDH-" + std::to_string(pbits);
+ report.report(nm, keygen_timer);
+ report.report(nm, kex_timer);
+ }
+ }
+
+#endif
+
+template<typename PRIV_KEY_TYPE>
+void benchmark_dsa_nr(RandomNumberGenerator& rng,
+ double seconds,
+ Benchmark_Report& report)
+ {
+#if defined(BOTAN_HAS_NYBERG_RUEPPEL) || defined(BOTAN_HAS_DSA)
+ const char* domains[] = { "dsa/jce/1024",
+ "dsa/botan/2048",
+ "dsa/botan/3072",
+ nullptr };
+
+ std::string algo_name;
+
+ for(size_t j = 0; domains[j]; j++)
+ {
+ size_t pbits = to_u32bit(split_on(domains[j], '/')[2]);
+ size_t qbits = (pbits <= 1024) ? 160 : 256;
+
+ const std::string padding = "EMSA1(SHA-" + std::to_string(qbits) + ")";
+
+ Timer keygen_timer("keygen");
+ Timer verify_timer(padding + " verify");
+ Timer sig_timer(padding + " signature");
+
+ while(verify_timer.seconds() < seconds ||
+ sig_timer.seconds() < seconds)
+ {
+ DL_Group group(domains[j]);
+
+ keygen_timer.start();
+ PRIV_KEY_TYPE key(rng, group);
+ algo_name = key.algo_name();
+ keygen_timer.stop();
+
+ PK_Signer sig(key, padding, IEEE_1363);
+ PK_Verifier ver(key, padding);
+
+ benchmark_sig_ver(ver, sig, verify_timer,
+ sig_timer, rng, 1000, seconds);
+ }
+
+ const std::string nm = algo_name + "-" + std::to_string(pbits);
+ report.report(nm, keygen_timer);
+ report.report(nm, verify_timer);
+ report.report(nm, sig_timer);
+ }
+#endif
+ }
+
+#if defined(BOTAN_HAS_CURVE_25519)
+void benchmark_curve25519(RandomNumberGenerator& rng,
+ double seconds,
+ Benchmark_Report& report)
+ {
+ Timer keygen_timer("keygen");
+ Timer kex_timer("key exchange");
+
+ while(kex_timer.seconds() < seconds)
+ {
+ keygen_timer.start();
+ Curve25519_PrivateKey key1(rng);
+ keygen_timer.stop();
+
+ keygen_timer.start();
+ Curve25519_PrivateKey key2(rng);
+ keygen_timer.stop();
+
+ PK_Key_Agreement ka1(key1, "KDF2(SHA-256)");
+ PK_Key_Agreement ka2(key2, "KDF2(SHA-256)");
+
+ SymmetricKey secret1, secret2;
+
+ for(size_t i = 0; i != 1000; ++i)
+ {
+ if(kex_timer.seconds() > seconds)
+ break;
+
+ kex_timer.start();
+ secret1 = ka1.derive_key(32, key2.public_value());
+ kex_timer.stop();
+
+ kex_timer.start();
+ secret2 = ka2.derive_key(32, key1.public_value());
+ kex_timer.stop();
+
+ if(secret1 != secret2)
+ std::cerr << "Curve25519 secrets did not match" << std::endl;
+ }
+ }
+
+ const std::string nm = "Curve25519";
+ report.report(nm, keygen_timer);
+ report.report(nm, kex_timer);
+ }
+#endif
+
+#ifdef BOTAN_HAS_DIFFIE_HELLMAN
+void benchmark_dh(RandomNumberGenerator& rng,
+ double seconds,
+ Benchmark_Report& report)
+ {
+ const char* domains[] = { "modp/ietf/1024",
+ "modp/ietf/2048",
+ "modp/ietf/3072",
+ "modp/ietf/4096",
+ "modp/ietf/6144",
+ "modp/ietf/8192",
+ nullptr };
+
+ for(size_t j = 0; domains[j]; j++)
+ {
+ Timer keygen_timer("keygen");
+ Timer kex_timer("key exchange");
+
+ while(kex_timer.seconds() < seconds)
+ {
+ DL_Group group(domains[j]);
+
+ keygen_timer.start();
+ DH_PrivateKey dh1(rng, group);
+ keygen_timer.stop();
+
+ keygen_timer.start();
+ DH_PrivateKey dh2(rng, group);
+ keygen_timer.stop();
+
+ PK_Key_Agreement ka1(dh1, "KDF2(SHA-1)");
+ PK_Key_Agreement ka2(dh2, "KDF2(SHA-1)");
+
+ SymmetricKey secret1, secret2;
+
+ for(size_t i = 0; i != 1000; ++i)
+ {
+ if(kex_timer.seconds() > seconds)
+ break;
+
+ kex_timer.start();
+ secret1 = ka1.derive_key(32, dh2.public_value());
+ kex_timer.stop();
+
+ kex_timer.start();
+ secret2 = ka2.derive_key(32, dh1.public_value());
+ kex_timer.stop();
+
+ if(secret1 != secret2)
+ std::cerr << "DH secrets did not match" << std::endl;
+ }
+ }
+
+ const std::string nm = "DH-" + split_on(domains[j], '/')[2];
+ report.report(nm, keygen_timer);
+ report.report(nm, kex_timer);
+ }
+ }
+#endif
+
+#if defined(BOTAN_HAS_DIFFIE_HELLMAN) && defined(BOTAN_HAS_DLIES) && defined(BOTAN_HAS_KDF2)
+void benchmark_dlies(RandomNumberGenerator& rng,
+ double seconds,
+ Benchmark_Report& report)
+ {
+ const char* domains[] = { "modp/ietf/768",
+ "modp/ietf/1024",
+ "modp/ietf/2048",
+ "modp/ietf/3072",
+ "modp/ietf/4096",
+ "modp/ietf/6144",
+ "modp/ietf/8192",
+ nullptr };
+
+ for(size_t j = 0; domains[j]; j++)
+ {
+ Timer keygen_timer("keygen");
+ Timer kex_timer("key exchange");
+
+ Timer enc_timer("encrypt");
+ Timer dec_timer("decrypt");
+
+ while(enc_timer.seconds() < seconds || dec_timer.seconds() < seconds)
+ {
+ DL_Group group(domains[j]);
+
+ keygen_timer.start();
+ DH_PrivateKey dh1_priv(rng, group);
+ keygen_timer.stop();
+
+ keygen_timer.start();
+ DH_PrivateKey dh2_priv(rng, group);
+ keygen_timer.stop();
+
+ DH_PublicKey dh2_pub(dh2_priv);
+
+ DLIES_Encryptor dlies_enc(dh1_priv,
+ new KDF2(new SHA_160),
+ new HMAC(new SHA_160));
+
+ dlies_enc.set_other_key(dh2_pub.public_value());
+
+ DLIES_Decryptor dlies_dec(dh2_priv,
+ new KDF2(new SHA_160),
+ new HMAC(new SHA_160));
+
+ benchmark_enc_dec(dlies_enc, dlies_dec,
+ enc_timer, dec_timer, rng,
+ 1000, seconds);
+ }
+
+ const std::string nm = "DLIES-" + split_on(domains[j], '/')[2];
+ report.report(nm, keygen_timer);
+ report.report(nm, enc_timer);
+ report.report(nm, dec_timer);
+ }
+ }
+#endif
+
+#ifdef BOTAN_HAS_ELGAMAL
+void benchmark_elg(RandomNumberGenerator& rng,
+ double seconds,
+ Benchmark_Report& report)
+ {
+ const char* domains[] = { "modp/ietf/768",
+ "modp/ietf/1024",
+ "modp/ietf/2048",
+ "modp/ietf/3072",
+ "modp/ietf/4096",
+ "modp/ietf/6144",
+ "modp/ietf/8192",
+ nullptr };
+
+ const std::string algo_name = "ElGamal";
+
+ for(size_t j = 0; domains[j]; j++)
+ {
+ size_t pbits = to_u32bit(split_on(domains[j], '/')[2]);
+
+ const std::string padding = "EME1(SHA-1)";
+
+ Timer keygen_timer("keygen");
+ Timer enc_timer(padding + " encrypt");
+ Timer dec_timer(padding + " decrypt");
+
+ while(enc_timer.seconds() < seconds ||
+ dec_timer.seconds() < seconds)
+ {
+ DL_Group group(domains[j]);
+
+ keygen_timer.start();
+ ElGamal_PrivateKey key(rng, group);
+ keygen_timer.stop();
+
+ PK_Decryptor_EME dec(key, padding);
+ PK_Encryptor_EME enc(key, padding);
+
+ benchmark_enc_dec(enc, dec, enc_timer, dec_timer,
+ rng, 1000, seconds);
+ }
+
+ const std::string nm = algo_name + "-" + std::to_string(pbits);
+ report.report(nm, keygen_timer);
+ report.report(nm, enc_timer);
+ report.report(nm, dec_timer);
+ }
+ }
+#endif
+
+#if defined(BOTAN_HAS_MCELIECE)
+void benchmark_mce(RandomNumberGenerator& rng,
+ double seconds,
+ Benchmark_Report& report)
+ {
+ const std::vector<std::pair<size_t, size_t>> params = {
+ { 1632, 33 },
+ { 2480, 45 },
+ { 2960, 57 },
+ { 3408, 67 },
+ { 4264, 95 },
+ { 6624, 115 }
+ };
+
+ const std::string algo_name = "McEliece";
+
+ for(auto& param : params)
+ {
+ Timer keygen_timer("keygen");
+ Timer enc_timer("encrypt");
+ Timer dec_timer("decrypt");
+
+ keygen_timer.start();
+ McEliece_PrivateKey priv_key(rng, param.first, param.second);
+ McEliece_PublicKey pub_key(priv_key.x509_subject_public_key());
+ keygen_timer.stop();
+
+ McEliece_KEM_Encryptor enc_kem(pub_key);
+ McEliece_KEM_Decryptor dec_kem(priv_key);
+
+ while(enc_timer.seconds() < seconds ||
+ dec_timer.seconds() < seconds)
+ {
+ enc_timer.start();
+ auto enc_pair = enc_kem.encrypt(rng);
+ enc_timer.stop();
+
+ dec_timer.start();
+ auto dec_key = dec_kem.decrypt_vec(enc_pair.first);
+ dec_timer.stop();
+
+ BOTAN_ASSERT_EQUAL(enc_pair.second, dec_key, "KEM result matches");
+ }
+
+ const std::string nm = algo_name + "-" +
+ std::to_string(param.first) + "," +
+ std::to_string(param.second);
+
+ std::ostringstream keysize_report;
+ keysize_report << "(work factor " << pub_key.estimated_strength() << ", "
+ << "pub bytes " << pub_key.x509_subject_public_key().size() << " "
+ << "priv bytes " << priv_key.pkcs8_private_key().size() << ")";
+
+ report.report(nm + " " + keysize_report.str(), keygen_timer);
+ report.report(nm, enc_timer);
+ report.report(nm, dec_timer);
+ }
+ }
+#endif
+
+}
+
+void benchmark_public_key(RandomNumberGenerator& rng,
+ const std::string& algo,
+ const std::string& provider,
+ double seconds)
+ {
+ /*
+ There is some strangeness going on here. It looks like algorithms
+ at the end take some kind of penalty. For example, running the RW tests
+ first got a result of:
+ RW-1024: 148.14 ms / private operation
+ but running them last output:
+ RW-1024: 363.54 ms / private operation
+
+ I think it's from memory fragmentation in the allocators, but I'm
+ not really sure. Need to investigate.
+
+ Until then, I've basically ordered the tests in order of most important
+ algorithms (RSA, DSA) to least important (NR, RW).
+
+ This strange behaviour does not seem to occur with DH (?)
+
+ To get more accurate runs, use --bench-algo (RSA|DSA|DH|ELG|NR); in this
+ case the distortion is less than 5%, which is good enough.
+
+ We do random keys with the DL schemes, since it's so easy and fast to
+ generate keys for them. For RSA and RW, we load the keys from a file. The
+ RSA keys are stored in a PKCS #8 structure, while RW is stored in a more
+ ad-hoc format (the RW algorithm has no assigned OID that I know of, so
+ there is no way to encode a RW key into a PKCS #8 structure).
+ */
+
+ Benchmark_Report report;
+
+#if defined(BOTAN_HAS_RSA)
+ if(algo == "All" || algo == "RSA")
+ benchmark_rsa(provider, rng, seconds, report);
+#endif
+
+#if defined(BOTAN_HAS_DSA)
+ if(algo == "All" || algo == "DSA")
+ benchmark_dsa_nr<DSA_PrivateKey>(rng, seconds, report);
+#endif
+
+#if defined(BOTAN_HAS_ECDSA)
+ if(algo == "All" || algo == "ECDSA")
+ benchmark_ecdsa(provider, rng, seconds, report);
+#endif
+
+#if defined(BOTAN_HAS_ECDH)
+ if(algo == "All" || algo == "ECDH")
+ benchmark_ecdh(rng, seconds, report);
+#endif
+
+#if defined(BOTAN_HAS_GOST_34_10_2001)
+ if(algo == "All" || algo == "GOST-34.10")
+ benchmark_gost_3410(rng, seconds, report);
+#endif
+
+#if defined(BOTAN_HAS_CURVE_25519)
+ if(algo == "All" || algo == "Curve25519")
+ benchmark_curve25519(rng, seconds, report);
+#endif
+
+#if defined(BOTAN_HAS_DIFFIE_HELLMAN)
+ if(algo == "All" || algo == "DH")
+ benchmark_dh(rng, seconds, report);
+#endif
+
+#if defined(BOTAN_HAS_DIFFIE_HELLMAN) && defined(BOTAN_HAS_DLIES) && defined(BOTAN_HAS_KDF2)
+ if(algo == "All" || algo == "DLIES")
+ benchmark_dlies(rng, seconds, report);
+#endif
+
+#if defined(BOTAN_HAS_ELGAMAL)
+ if(algo == "All" || algo == "ELG" || algo == "ElGamal")
+ benchmark_elg(rng, seconds, report);
+#endif
+
+#if defined(BOTAN_HAS_NYBERG_RUEPPEL)
+ if(algo == "All" || algo == "NR")
+ benchmark_dsa_nr<NR_PrivateKey>(rng, seconds, report);
+#endif
+
+#if defined(BOTAN_HAS_RW)
+ if(algo == "All" || algo == "RW")
+ benchmark_rw(rng, seconds, report);
+#endif
+
+#if defined(BOTAN_HAS_MCELIECE)
+ if(algo == "All" || algo == "McEliece")
+ benchmark_mce(rng, seconds, report);
+#endif
+ }
+#endif // BOTAN_HAS_PUBLIC_KEY_CRYPTO
diff --git a/src/cli/implementation/speed_transform.cpp b/src/cli/implementation/speed_transform.cpp
new file mode 100644
index 000000000..2db5cdd70
--- /dev/null
+++ b/src/cli/implementation/speed_transform.cpp
@@ -0,0 +1,67 @@
+/*
+* (C) 2009 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "speed.h"
+
+#include <iostream>
+#include <iomanip>
+
+#include <botan/cipher_mode.h>
+#include <botan/transform.h>
+
+using namespace Botan;
+
+namespace {
+void benchmark_transform(std::unique_ptr<Transform> tf,
+ RandomNumberGenerator& rng,
+ const std::chrono::milliseconds runtime)
+ {
+ for(size_t buf_size : { 16, 64, 256, 1024, 8192 })
+ {
+ secure_vector<byte> buffer(buf_size);
+
+ std::chrono::nanoseconds time_used(0);
+
+ tf->start(rng.random_vec(tf->default_nonce_length()));
+
+ auto start = std::chrono::high_resolution_clock::now();
+
+ secure_vector<byte> buf(buf_size);
+ size_t reps = 0;
+ while(time_used < runtime)
+ {
+ tf->update(buf);
+ buf.resize(buf_size);
+ ++reps;
+ time_used = std::chrono::high_resolution_clock::now() - start;
+ }
+
+ const u64bit nsec_used = std::chrono::duration_cast<std::chrono::nanoseconds>(time_used).count();
+
+ const double seconds_used = static_cast<double>(nsec_used) / 1000000000;
+
+ const double Mbps = ((reps / seconds_used) * buf_size) / 1024 / 1024;
+
+ std::cout << tf->name() << " " << std::setprecision(4) << Mbps
+ << " MiB / sec with " << buf_size << " byte blocks" << std::endl;
+ }
+ }
+}
+
+bool benchmark_transform(RandomNumberGenerator& rng, const std::string& algo_name,
+ const std::chrono::milliseconds runtime)
+ {
+ std::unique_ptr<Transform> tf;
+ tf.reset(get_cipher_mode(algo_name, ENCRYPTION));
+ if(!tf)
+ return false;
+
+ if(Keyed_Transform* keyed = dynamic_cast<Keyed_Transform*>(tf.get()))
+ keyed->set_key(rng.random_vec(keyed->key_spec().maximum_keylength()));
+
+ benchmark_transform(std::move(tf), rng, runtime);
+ return true;
+ }
diff --git a/src/cli/implementation/timer.cpp b/src/cli/implementation/timer.cpp
new file mode 100644
index 000000000..14e55316b
--- /dev/null
+++ b/src/cli/implementation/timer.cpp
@@ -0,0 +1,60 @@
+/*
+* (C) 2009 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "timer.h"
+#include <chrono>
+#include <iomanip>
+
+void Timer::start()
+ {
+ stop();
+ m_timer_start = get_clock();
+ }
+
+void Timer::stop()
+ {
+ if(m_timer_start)
+ {
+ const u64bit now = get_clock();
+
+ if(now > m_timer_start)
+ m_time_used += (now - m_timer_start);
+
+ m_timer_start = 0;
+ ++m_event_count;
+ }
+ }
+
+u64bit Timer::get_clock()
+ {
+ auto now = std::chrono::high_resolution_clock::now().time_since_epoch();
+ return std::chrono::duration_cast<std::chrono::nanoseconds>(now).count();
+ }
+
+std::ostream& operator<<(std::ostream& out, Timer& timer)
+ {
+ //out << timer.value() << " ";
+
+ double events_per_second_fl =
+ static_cast<double>(timer.events() / timer.seconds());
+
+ u64bit events_per_second = static_cast<u64bit>(events_per_second_fl);
+
+ out << events_per_second << " " << timer.get_name() << " per second; ";
+
+ std::string op_or_ops = (timer.events() == 1) ? "op" : "ops";
+
+ const std::ios::fmtflags flags = out.flags();
+
+ out << std::setprecision(2) << std::fixed
+ << timer.ms_per_event() << " ms/op"
+ << " (" << timer.events() << " " << op_or_ops << " in "
+ << timer.milliseconds() << " ms)";
+
+ out.flags(flags);
+
+ return out;
+ }
diff --git a/src/cli/implementation/timer.h b/src/cli/implementation/timer.h
new file mode 100644
index 000000000..ac5bd5cef
--- /dev/null
+++ b/src/cli/implementation/timer.h
@@ -0,0 +1,55 @@
+/*
+* (C) 2014 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_BENCHMARK_TIMER_H__
+#define BOTAN_BENCHMARK_TIMER_H__
+
+#include <botan/types.h>
+#include <ostream>
+#include <string>
+
+using Botan::u64bit;
+using Botan::u32bit;
+
+class Timer
+ {
+ public:
+ static u64bit get_clock();
+
+ Timer(const std::string& name, u32bit event_mult = 1) :
+ m_name(name), m_event_mult(event_mult) {}
+
+ void start();
+ void stop();
+
+ u64bit value() { stop(); return m_time_used; }
+ double seconds() { return milliseconds() / 1000.0; }
+ double milliseconds() { return value() / 1000000.0; }
+
+ double ms_per_event() { return milliseconds() / events(); }
+ double seconds_per_event() { return seconds() / events(); }
+
+ u64bit events() const { return m_event_count * m_event_mult; }
+ std::string get_name() const { return m_name; }
+ private:
+ std::string m_name;
+ u64bit m_time_used = 0, m_timer_start = 0;
+ u64bit m_event_count = 0, m_event_mult = 0;
+ };
+
+inline bool operator<(const Timer& x, const Timer& y)
+ {
+ return (x.get_name() < y.get_name());
+ }
+
+inline bool operator==(const Timer& x, const Timer& y)
+ {
+ return (x.get_name() == y.get_name());
+ }
+
+std::ostream& operator<<(std::ostream&, Timer&);
+
+#endif
diff --git a/src/cli/is_prime.cpp b/src/cli/is_prime.cpp
new file mode 100644
index 000000000..71fec730b
--- /dev/null
+++ b/src/cli/is_prime.cpp
@@ -0,0 +1,50 @@
+/*
+* (C) 2014,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "apps.h"
+
+#if defined(BOTAN_HAS_NUMBERTHEORY)
+
+#include <botan/numthry.h>
+
+namespace {
+
+int is_prime(const std::vector<std::string> &args)
+ {
+ if(args.size() != 2 && args.size() != 3)
+ {
+ std::cerr << "Usage: " << args[0] << " n <prob>" << std::endl;
+ return 2;
+ }
+
+ BigInt n(args[1]);
+
+ size_t prob = 56;
+
+ if(args.size() == 3)
+ prob = to_u32bit(args[2]);
+
+ AutoSeeded_RNG rng;
+
+ const bool prime = is_prime(n, rng, prob);
+
+ if(prime)
+ {
+ std::cout << n << " is prime" << std::endl;
+ return 0;
+ }
+ else
+ {
+ std::cout << n << " is not prime" << std::endl;
+ return 1;
+ }
+ }
+
+REGISTER_APP(is_prime);
+
+}
+
+#endif // BOTAN_HAS_NUMBERTHEORY
diff --git a/src/cli/keygen.cpp b/src/cli/keygen.cpp
new file mode 100644
index 000000000..168b27a4a
--- /dev/null
+++ b/src/cli/keygen.cpp
@@ -0,0 +1,128 @@
+/*
+* (C) 2014,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "apps.h"
+
+#if defined(BOTAN_HAS_PUBLIC_KEY_CRYPTO) && defined(BOTAN_HAS_X509_CERTIFICATES)
+
+#include <iostream>
+#include <fstream>
+#include <string>
+#include <cstdlib>
+#include <memory>
+#include <botan/pk_keys.h>
+#include <botan/pkcs8.h>
+
+#if defined(BOTAN_HAS_RSA)
+#include <botan/rsa.h>
+#endif
+
+#if defined(BOTAN_HAS_DSA)
+#include <botan/dsa.h>
+#endif
+
+#if defined(BOTAN_HAS_ECDSA)
+#include <botan/ecdsa.h>
+#endif
+
+#if defined(BOTAN_HAS_CURVE_25519)
+#include <botan/curve25519.h>
+#endif
+
+using namespace Botan;
+
+namespace {
+
+std::string dsa_group_for(size_t bits)
+ {
+ if(bits == 1024)
+ return "dsa/jce/1024";
+ if(bits == 2048)
+ return "dsa/botan/2048";
+ if(bits == 3072)
+ return "dsa/botan/3072";
+ throw std::runtime_error("No registered DSA group for " + std::to_string(bits) + " bits");
+ }
+
+Private_Key* gen_key(RandomNumberGenerator& rng, const std::string& algo, size_t bits)
+ {
+#if defined(BOTAN_HAS_RSA)
+ if(algo == "rsa")
+ return new RSA_PrivateKey(rng, bits);
+#endif
+
+#if defined(BOTAN_HAS_DSA)
+ if(algo == "dsa")
+ {
+ DL_Group grp(dsa_group_for(bits));
+ return new DSA_PrivateKey(rng, grp);
+ }
+#endif
+
+#if defined(BOTAN_HAS_ECDSA)
+ if(algo == "ecdsa")
+ {
+ EC_Group grp("secp" + std::to_string(bits) + "r1");
+ return new ECDSA_PrivateKey(rng, grp);
+ }
+#endif
+
+#if defined(BOTAN_HAS_CURVE_25519)
+ if(algo == "curve25519")
+ return new Curve25519_PrivateKey(rng);
+#endif
+
+ throw std::runtime_error("Unknown algorithm " + algo);
+ }
+
+
+int keygen(const std::vector<std::string> &args)
+ {
+ OptionParser opts("algo=|bits=|passphrase=|pbe=");
+ opts.parse(args);
+
+ const std::string algo = opts.value_or_else("algo", "rsa");
+ const size_t bits = opts.int_value_or_else("bits", 2048);
+ const std::string pass = opts.value_or_else("passphrase", "");
+ const std::string pbe = opts.value_or_else("pbe", "");
+
+ try
+ {
+ std::ofstream pub("public.pem");
+ std::ofstream priv("private.pem");
+
+ if(!priv || !pub)
+ {
+ std::cout << "Couldn't write output files" << std::endl;
+ return 1;
+ }
+
+ AutoSeeded_RNG rng;
+
+ std::unique_ptr<Private_Key> key(gen_key(rng, algo, bits));
+
+ pub << X509::PEM_encode(*key);
+
+ if(pass == "")
+ priv << PKCS8::PEM_encode(*key);
+ else
+ priv << PKCS8::PEM_encode(*key, rng, pass, std::chrono::milliseconds(300), pbe);
+
+ std::cout << "Wrote " << bits << " bit " << algo << " key to public.pem / private.pem" << std::endl;
+ }
+ catch(std::exception& e)
+ {
+ std::cout << "Exception caught: " << e.what() << std::endl;
+ }
+
+ return 0;
+ }
+
+REGISTER_APP(keygen);
+
+}
+
+#endif // BOTAN_HAS_PUBLIC_KEY_CRYPTO && BOTAN_HAS_X509_CERTIFICATES
diff --git a/src/cli/main.cpp b/src/cli/main.cpp
new file mode 100644
index 000000000..8d229ce0e
--- /dev/null
+++ b/src/cli/main.cpp
@@ -0,0 +1,187 @@
+/*
+* (C) 2009,2014 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <vector>
+#include <string>
+
+#include <iostream>
+#include <cstdlib>
+#include <exception>
+#include <limits>
+#include <memory>
+
+#include <botan/version.h>
+#include <botan/cpuid.h>
+
+#if defined(BOTAN_HAS_HTTP_UTIL)
+#include <botan/http_util.h>
+#endif
+
+using namespace Botan;
+
+#include "apps.h"
+
+namespace {
+
+int help(const std::vector<std::string> &args)
+ {
+ std::cout << "Usage: " << args[0] << " [subcommand] [subcommand-options]" << std::endl;
+
+ std::set<std::string> apps = AppRegistrations::instance().all_appnames();
+
+ std::cout << "Available commands:" << std::endl;
+
+ size_t idx = 1;
+ for(auto&& app: apps)
+ {
+ std::cout << app;
+
+ if(idx % 3 == 0)
+ std::cout << std::endl;
+ else
+ std::cout << std::string(18-app.size(), ' ');
+
+ ++idx;
+ }
+ std::cout << std::endl;
+
+ return 1;
+ }
+
+int config(const std::vector<std::string> &args)
+ {
+ if(args.size() != 2)
+ {
+ std::cout << "Usage: " << args[0] << " <what>\n"
+ << " prefix: Print install prefix\n"
+ << " cflags: Print include params\n"
+ << " ldflags: Print linker params\n"
+ << " libs: Print libraries" << std::endl;
+ return 1;
+ }
+
+ const std::string arg = args[1];
+
+ if(arg == "prefix")
+ std::cout << BOTAN_INSTALL_PREFIX << std::endl;
+
+ else if(arg == "cflags")
+ std::cout << "-I" << BOTAN_INSTALL_PREFIX << "/" << BOTAN_INSTALL_HEADER_DIR << std::endl;
+
+ else if(arg == "ldflags")
+ std::cout << "-L" << BOTAN_INSTALL_PREFIX << "/" << BOTAN_INSTALL_LIB_DIR << std::endl;
+
+ else if(arg == "libs")
+ std::cout << "-lbotan-" << version_major() << "." << version_minor()
+ << " " << BOTAN_LIB_LINK << std::endl;
+
+ else
+ {
+ std::cerr << "Unknown option " << arg << " to botan config" << std::endl;
+ return 1;
+ }
+
+ return 0;
+ }
+REGISTER_APP(config);
+
+int version(const std::vector<std::string> &args)
+ {
+ if(BOTAN_VERSION_MAJOR != version_major() ||
+ BOTAN_VERSION_MINOR != version_minor() ||
+ BOTAN_VERSION_PATCH != version_patch())
+ {
+ std::cerr << "Warning: linked version ("
+ << version_major() << '.'
+ << version_minor() << '.'
+ << version_patch()
+ << ") does not match version built against ("
+ << BOTAN_VERSION_MAJOR << '.'
+ << BOTAN_VERSION_MINOR << '.'
+ << BOTAN_VERSION_PATCH << ")" << std::endl;
+ }
+
+ if(args.size() == 1)
+ {
+ std::cout << Botan::version_major() << "."
+ << Botan::version_minor() << "."
+ << Botan::version_patch() << std::endl;
+ }
+ else if(args.size() == 2 && args[1] == "--full")
+ {
+ std::cout << Botan::version_string() << std::endl;
+ }
+ else
+ {
+ std::cout << "Usage: " << args[0] << " version [--full]" << std::endl;
+ return 1;
+ }
+
+ return 0;
+ }
+REGISTER_APP(version);
+
+int cpuid(const std::vector<std::string> &args)
+ {
+ BOTAN_UNUSED(args);
+ CPUID::print(std::cout);
+ return 0;
+ }
+REGISTER_APP(cpuid);
+
+#if defined(BOTAN_HAS_HTTP_UTIL)
+int http_get(const std::vector<std::string> &args)
+ {
+ if(args.size() != 2)
+ {
+ std::cout << "Usage " << args[0] << " <url>" << std::endl;
+ return 1;
+ }
+
+ auto resp = HTTP::GET_sync(args[1]);
+ std::cout << resp << std::endl;
+ return 0;
+ }
+REGISTER_APP(http_get);
+
+#endif
+
+}
+
+int main(int argc, char* argv[])
+ {
+ const std::vector<std::string> args(argv, argv + argc);
+
+ try
+ {
+ if(args.size() < 2)
+ return help(args);
+
+ const std::string cmd = args[1];
+
+ if(cmd == "help" || cmd == "-h")
+ return help(args);
+
+ AppRegistrations& apps = AppRegistrations::instance();
+ if(apps.has(cmd))
+ return apps.run(cmd, std::vector<std::string>(args.begin()+1, args.end()));
+
+ std::cerr << "Unknown command " << cmd << std::endl;
+ return help(args);
+ }
+ catch(std::exception& e)
+ {
+ std::cerr << e.what() << std::endl;
+ return 1;
+ }
+ catch(...)
+ {
+ std::cerr << "Unknown exception caught" << std::endl;
+ return 1;
+ }
+
+ return 0;
+ }
diff --git a/src/cli/mce.cpp b/src/cli/mce.cpp
new file mode 100644
index 000000000..226f21e9c
--- /dev/null
+++ b/src/cli/mce.cpp
@@ -0,0 +1,124 @@
+/*
+* (C) 2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "apps.h"
+
+#if defined(BOTAN_HAS_MCELIECE)
+
+#include <botan/mceliece.h>
+#include <botan/mceies.h>
+#include <botan/pkcs8.h>
+#include <fstream>
+
+namespace {
+
+int mce(const std::vector<std::string> &args)
+ {
+ if(args.size() < 4)
+ {
+ std::cout << "Usage: " << args[0] << " [keygen n t pass|keybits n t|encrypt file key|decrypt file key pass]"
+ << std::endl;
+ return 1;
+ }
+
+ const std::string cmd = args[1];
+
+ AutoSeeded_RNG rng;
+
+ if(cmd == "keygen")
+ {
+ const u32bit n = to_u32bit(args[2]);
+ const u32bit t = to_u32bit(args[3]);
+ const std::string pass = args[4];
+
+ McEliece_PrivateKey pk(rng, n, t);
+
+ bool ok = pk.check_key(rng, true);
+
+ if(!ok)
+ {
+ std::cout << "Keygen failed self-test\n";
+ return 2;
+ }
+
+ /*
+ secure_vector<byte> priv = PKCS8::BER_encode(pk);
+ std::vector<byte> pub = X509::BER_encode(pk);
+ std::cout << priv.size()/1024.0 << " " << pub.size()/1024.0 << "\n";
+ */
+
+ std::ofstream pub_file("mce.pub");
+ pub_file << X509::PEM_encode(pk);
+ pub_file.close();
+
+ std::ofstream priv_file("mce.priv");
+ priv_file << PKCS8::PEM_encode(pk, rng, pass);
+ priv_file.close();
+ }
+ else if(cmd == "keybits")
+ {
+ const u32bit n = to_u32bit(args[2]);
+ const u32bit t = to_u32bit(args[3]);
+ std::cout << "McEliece key with params (" << n << "," << t << ") has "
+ << mceliece_work_factor(n, t) << " bit security\n";
+ }
+ else if(cmd == "encrypt")
+ {
+ std::unique_ptr<Public_Key> p8(X509::load_key(args[3]));
+ const McEliece_PublicKey* key = dynamic_cast<McEliece_PublicKey*>(p8.get());
+
+ if(!key)
+ {
+ throw std::runtime_error("Loading McEliece public key failed");
+ }
+
+ const std::string input_path = args[2];
+ std::ifstream in(input_path, std::ios::binary);
+ std::string pt((std::istreambuf_iterator<char>(in)), std::istreambuf_iterator<char>());
+
+ secure_vector<byte> ct = mceies_encrypt(*key,
+ reinterpret_cast<const byte*>(pt.data()),
+ pt.size(),
+ nullptr, 0, rng, "AES-128/GCM");
+
+ std::cout << pt.size() << " -> " << ct.size() << std::endl;
+
+ std::ofstream out(std::string(input_path) + ".ct", std::ios::binary);
+ out.write(reinterpret_cast<const char*>(ct.data()), ct.size());
+ out.close();
+ }
+ else if(cmd == "decrypt")
+ {
+ const std::string key_file = args[3];
+ const std::string pass = args[4];
+ std::unique_ptr<Private_Key> p8(PKCS8::load_key(key_file, rng, pass));
+ const McEliece_PrivateKey* key = dynamic_cast<McEliece_PrivateKey*>(p8.get());
+
+ if(!key)
+ {
+ throw std::runtime_error("Loading McEliece private key failed");
+ }
+
+ std::ifstream in(args[2], std::ios::binary);
+ std::string ct((std::istreambuf_iterator<char>(in)), std::istreambuf_iterator<char>());
+
+ secure_vector<byte> pt = mceies_decrypt(*key,
+ reinterpret_cast<const byte*>(ct.data()),
+ ct.size(),
+ nullptr, 0, "AES-128/GCM");
+
+ std::ofstream out("mce.plaintext", std::ios::binary);
+ out.write(reinterpret_cast<const char*>(pt.data()), pt.size());
+ out.close();
+ }
+ return 0;
+ }
+
+}
+
+REGISTER_APP(mce);
+
+#endif
diff --git a/src/cli/ocsp.cpp b/src/cli/ocsp.cpp
new file mode 100644
index 000000000..e5b42b076
--- /dev/null
+++ b/src/cli/ocsp.cpp
@@ -0,0 +1,53 @@
+/*
+* (C) 2014,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "apps.h"
+
+#if defined(BOTAN_HAS_OCSP)
+
+#include <botan/x509cert.h>
+#include <botan/certstor.h>
+#include <botan/x509path.h>
+#include <botan/ocsp.h>
+
+using namespace Botan;
+
+namespace {
+
+int ocsp_check(const std::vector<std::string> &args)
+ {
+ if(args.size() != 2)
+ {
+ std::cout << "Usage: " << args[0] << " subject.pem issuer.pem" << std::endl;
+ return 2;
+ }
+
+ X509_Certificate subject(args[1]);
+ X509_Certificate issuer(args[2]);
+
+ Certificate_Store_In_Memory cas;
+ cas.add_certificate(issuer);
+ OCSP::Response resp = OCSP::online_check(issuer, subject, &cas);
+
+ auto status = resp.status_for(issuer, subject);
+
+ if(status == Certificate_Status_Code::VERIFIED)
+ {
+ std::cout << "OCSP check OK" << std::endl;
+ return 0;
+ }
+ else
+ {
+ std::cout << "OCSP check failed " << Path_Validation_Result::status_string(status) << std::endl;
+ return 1;
+ }
+ }
+
+REGISTER_APP(ocsp_check);
+
+}
+
+#endif
diff --git a/src/cli/pkcs10.cpp b/src/cli/pkcs10.cpp
new file mode 100644
index 000000000..106fe2c24
--- /dev/null
+++ b/src/cli/pkcs10.cpp
@@ -0,0 +1,64 @@
+/*
+* (C) 2014,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "apps.h"
+
+#if defined(BOTAN_HAS_X509_CERTIFICATES) && defined(BOTAN_HAS_RSA)
+
+#include <botan/pkcs8.h>
+#include <botan/x509self.h>
+#include <botan/rsa.h>
+#include <fstream>
+#include <memory>
+
+using namespace Botan;
+
+namespace {
+
+int pkcs10(const std::vector<std::string> &args)
+ {
+ if(args.size() != 6)
+ {
+ std::cout << "Usage: " << args[0]
+ << " passphrase name country_code organization email" << std::endl;
+ return 1;
+ }
+
+ try
+ {
+ AutoSeeded_RNG rng;
+
+ RSA_PrivateKey priv_key(rng, 1024);
+
+ std::ofstream key_file("private.pem");
+ key_file << PKCS8::PEM_encode(priv_key, rng, args[1]);
+
+ X509_Cert_Options opts;
+
+ opts.common_name = args[2];
+ opts.country = args[3];
+ opts.organization = args[4];
+ opts.email = args[5];
+
+ PKCS10_Request req = X509::create_cert_req(opts, priv_key,
+ "SHA-256", rng);
+
+ std::ofstream req_file("req.pem");
+ req_file << req.PEM_encode();
+ }
+ catch(std::exception& e)
+ {
+ std::cout << e.what() << std::endl;
+ return 1;
+ }
+ return 0;
+ }
+
+REGISTER_APP(pkcs10);
+
+}
+
+#endif // BOTAN_HAS_X509_CERTIFICATES && BOTAN_HAS_RSA
diff --git a/src/cli/pkcs8.cpp b/src/cli/pkcs8.cpp
new file mode 100644
index 000000000..7bc1c2561
--- /dev/null
+++ b/src/cli/pkcs8.cpp
@@ -0,0 +1,76 @@
+/*
+* (C) 2015 René Korthaus
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "apps.h"
+#include <iostream>
+#include <fstream>
+#include <string>
+#include <memory>
+#include <chrono>
+
+#if defined(BOTAN_HAS_PUBLIC_KEY_CRYPTO)
+#include <botan/pk_keys.h>
+#include <botan/pkcs8.h>
+#include <botan/x509_key.h>
+
+using namespace Botan;
+
+namespace {
+
+int pkcs8(const std::vector<std::string> &args)
+ {
+ OptionParser opts("in=|out=|passin=|passout=|pbe=|pubout");
+ opts.parse(args);
+
+ const std::string passin = opts.value_or_else("passin", "");
+ const std::string passout = opts.value_or_else("passout", "");
+ const std::string pbe = opts.value_or_else("pbe", "");
+
+ if(args.size() < 3)
+ {
+ opts.help(std::cout, "pkcs8");
+ return 1;
+ }
+
+ try
+ {
+ std::ofstream out_key(opts.value("out"));
+
+ if (!out_key)
+ {
+ std::cout << "Couldn't write key" << std::endl;
+ return 1;
+ }
+
+ AutoSeeded_RNG rng;
+ std::unique_ptr<Private_Key> key(PKCS8::load_key(opts.value("in"), rng, passin));
+
+ if(opts.is_set("pubout"))
+ {
+ out_key << X509::PEM_encode(*key);
+ }
+ else
+ {
+ if(passout.empty())
+ out_key << PKCS8::PEM_encode(*key);
+ else
+ out_key << PKCS8::PEM_encode(*key, rng, passout, std::chrono::milliseconds(300), pbe);
+ }
+ }
+ catch(std::exception& e)
+ {
+ std::cout << "Exception caught: " << e.what() << std::endl;
+ return 2;
+ }
+
+ return 0;
+ }
+
+REGISTER_APP(pkcs8);
+
+}
+
+#endif
diff --git a/src/cli/prime.cpp b/src/cli/prime.cpp
new file mode 100644
index 000000000..82efa75d2
--- /dev/null
+++ b/src/cli/prime.cpp
@@ -0,0 +1,48 @@
+/*
+* (C) 2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "apps.h"
+
+#if defined(BOTAN_HAS_NUMBERTHEORY)
+
+#include <botan/numthry.h>
+
+#include <algorithm>
+#include <iostream>
+
+namespace {
+
+int prime(const std::vector<std::string> &args)
+ {
+ if(args.size() < 2)
+ {
+ std::cout << "Usage: " << args[0] << " bits count" << std::endl;
+ return 1;
+ }
+
+ AutoSeeded_RNG rng;
+ const size_t bits = to_u32bit(args[1]);
+ const size_t cnt = args.size() >= 3 ? to_u32bit(args[2]) : 1;
+
+ for(size_t i = 0; i != cnt; ++i)
+ {
+ const BigInt p = random_prime(rng, bits);
+ std::cout << p << std::endl;
+
+ if(p.bits() != bits)
+ {
+ std::cout << "Result not exactly requested bit size, got " << p.bits() << std::endl;
+ }
+ }
+
+ return 0;
+ }
+
+}
+
+REGISTER_APP(prime);
+
+#endif
diff --git a/src/cli/rng.cpp b/src/cli/rng.cpp
new file mode 100644
index 000000000..3fe8719ce
--- /dev/null
+++ b/src/cli/rng.cpp
@@ -0,0 +1,68 @@
+/*
+* (C) 2014 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "apps.h"
+#include <botan/entropy_src.h>
+#include <botan/auto_rng.h>
+
+#if defined(BOTAN_HAS_SYSTEM_RNG)
+ #include <botan/system_rng.h>
+#endif
+
+namespace {
+
+int rng(const std::vector<std::string> &args)
+ {
+ if(args.size() == 1)
+ {
+ std::cout << "Usage: " << args[0] << " [--raw-entropy] n\n"
+ << "n: number of bytes"
+ << std::endl;
+ return 1;
+ }
+
+ try
+ {
+ const size_t bytes_count = to_u32bit(args.back());
+ const bool raw = (args.size() == 3 && args[1] == "--raw-entropy");
+
+#if defined(BOTAN_HAS_SYSTEM_RNG)
+ std::cout << "System " << hex_encode(system_rng().random_vec(bytes_count)) << std::endl;
+#endif
+
+ if(!raw)
+ {
+ AutoSeeded_RNG rng;
+ std::cout << hex_encode(rng.random_vec(bytes_count)) << std::endl;
+ }
+ else
+ {
+ double total_collected = 0;
+
+ Entropy_Accumulator accum(
+ [bytes_count,&total_collected](const byte in[], size_t in_len, double entropy_estimate)
+ {
+ std::cout << "Collected estimated "<< entropy_estimate << " bits in "
+ << hex_encode(in, in_len) << std::endl;
+ total_collected += entropy_estimate;
+ return total_collected >= bytes_count;
+ });
+
+ Entropy_Sources::global_sources().poll(accum);
+ }
+ }
+ catch(std::exception& e)
+ {
+ std::cout << "Error: " << e.what() << std::endl;
+ return 1;
+ }
+
+ return 0;
+ }
+
+REGISTER_APP(rng);
+
+}
diff --git a/src/cli/self_sig.cpp b/src/cli/self_sig.cpp
new file mode 100644
index 000000000..2c43f7acc
--- /dev/null
+++ b/src/cli/self_sig.cpp
@@ -0,0 +1,84 @@
+/*
+* (C) 2014,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "apps.h"
+
+#if defined(BOTAN_HAS_X509_CERTIFICATES) && defined(BOTAN_HAS_RSA)
+
+#include <botan/pkcs8.h>
+#include <botan/x509self.h>
+#include <botan/rsa.h>
+#include <fstream>
+#include <memory>
+
+using namespace Botan;
+
+namespace {
+
+int self_sig(const std::vector<std::string> &args)
+ {
+ if(args.size() != 7)
+ {
+ std::cout << "Usage: " << args[0]
+ << " passphrase [CA|user] name country_code organization email"
+ << std::endl;
+ return 1;
+ }
+
+ std::string CA_flag = args[2];
+ bool do_CA = false;
+
+ if(CA_flag == "CA") do_CA = true;
+ else if(CA_flag == "user") do_CA = false;
+ else
+ {
+ std::cout << "Bad flag for CA/user switch: " << CA_flag << std::endl;
+ return 1;
+ }
+
+ try
+ {
+ AutoSeeded_RNG rng;
+
+ RSA_PrivateKey key(rng, 2048);
+ //DL_Group group(rng, DL_Group::DSA_Kosherizer, 2048, 256);
+
+
+ std::ofstream priv_key("private.pem");
+ priv_key << PKCS8::PEM_encode(key, rng, args[1]);
+
+ X509_Cert_Options opts;
+ opts.common_name = args[3];
+ opts.country = args[4];
+ opts.organization = args[5];
+ opts.email = args[6];
+ /* Fill in other values of opts here */
+
+ //opts.xmpp = "[email protected]";
+
+ if(do_CA)
+ opts.CA_key();
+
+ X509_Certificate cert =
+ X509::create_self_signed_cert(opts, key, "SHA-256", rng);
+
+ std::ofstream cert_file("cert.pem");
+ cert_file << cert.PEM_encode();
+ }
+ catch(std::exception& e)
+ {
+ std::cout << "Exception: " << e.what() << std::endl;
+ return 1;
+ }
+
+ return 0;
+ }
+
+REGISTER_APP(self_sig);
+
+}
+
+#endif // BOTAN_HAS_X509_CERTIFICATES && BOTAN_HAS_RSA
diff --git a/src/cli/speed.cpp b/src/cli/speed.cpp
new file mode 100644
index 000000000..e8d30c6f1
--- /dev/null
+++ b/src/cli/speed.cpp
@@ -0,0 +1,214 @@
+/*
+* (C) 2009 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "apps.h"
+
+#if defined(BOTAN_HAS_RUNTIME_BENCHMARKING)
+
+#include "implementation/speed.h"
+
+#include <iostream>
+#include <iomanip>
+
+#include <botan/benchmark.h>
+#include <botan/auto_rng.h>
+
+using namespace Botan;
+
+namespace {
+const std::vector<std::string> default_benchmark_list = {
+ /* Block ciphers */
+ "AES-128",
+ "AES-192",
+ "AES-256",
+ "Blowfish",
+ "CAST-128",
+ "CAST-256",
+ "DES",
+ "IDEA",
+ "KASUMI",
+ "MARS",
+ "MISTY1",
+ "Noekeon",
+ "RC2",
+ "RC5(16)",
+ "RC6",
+ "SAFER-SK(10)",
+ "SEED",
+ "Serpent",
+ "Skipjack",
+ "Square",
+ "TEA",
+ "TripleDES",
+ "Threefish-512",
+ "Twofish",
+ "XTEA",
+
+ /* Cipher modes */
+ "AES-128/CBC",
+ "AES-128/CTR-BE",
+ "AES-128/EAX",
+ "AES-128/OCB",
+ "AES-128/GCM",
+ "AES-128/XTS",
+
+ "Serpent/CBC",
+ "Serpent/CTR-BE",
+ "Serpent/EAX",
+ "Serpent/OCB",
+ "Serpent/GCM",
+ "Serpent/XTS",
+
+ /* Stream ciphers */
+ "RC4",
+ "Salsa20",
+
+ /* Hashes */
+ "Keccak-1600(512)",
+ "MD5",
+ "RIPEMD-160",
+ "SHA-160",
+ "SHA-256",
+ "SHA-384",
+ "SHA-512",
+ "Skein-512",
+ "Tiger",
+ "Whirlpool",
+
+ /* MACs */
+ "CMAC(AES-128)",
+ "HMAC(SHA-1)",
+
+ /* Misc */
+ "is_prime",
+ "random_prime"
+};
+
+void report_results(const std::string& algo,
+ const std::map<std::string, double>& speeds)
+ {
+ if(speeds.empty())
+ return;
+
+ // invert, showing fastest impl first
+ std::map<double, std::string> results;
+
+ for(auto i = speeds.begin(); i != speeds.end(); ++i)
+ {
+ // Speeds might collide, tweak slightly to handle this
+ if(results[i->second] == "")
+ results[i->second] = i->first;
+ else
+ results[i->second - .01] = i->first;
+ }
+
+ std::cout << algo;
+
+ const std::ios::fmtflags flags = std::cout.flags();
+ for(auto i = results.rbegin(); i != results.rend(); ++i)
+ {
+ std::cout << " [" << i->second << "] "
+ << std::fixed << std::setprecision(2) << i->first;
+ }
+ std::cout << std::endl;
+ std::cout.flags(flags);
+ }
+
+void bench_algo(const std::string& algo,
+ const std::string& provider,
+ RandomNumberGenerator& rng,
+ double seconds,
+ size_t buf_size)
+ {
+ std::chrono::milliseconds runtime(
+ static_cast<std::chrono::milliseconds::rep>(seconds * 1000));
+
+ if (algo == "random_prime")
+ {
+ auto speeds = benchmark_random_prime(rng, runtime);
+ report_results(algo, speeds);
+ return;
+ }
+
+ if (algo == "is_prime")
+ {
+ auto speeds = benchmark_is_prime(rng, runtime);
+ report_results(algo, speeds);
+ return;
+ }
+
+ // This does report itself
+ if (benchmark_transform(rng, algo, runtime))
+ return;
+
+ try
+ {
+ auto speeds = algorithm_benchmark(algo, rng, runtime, buf_size);
+ report_results(algo, speeds);
+ }
+ catch (No_Provider_Found)
+ {
+ #if defined(BOTAN_HAS_PUBLIC_KEY_CRYPTO)
+ benchmark_public_key(rng, algo, provider, seconds);
+ #endif
+ }
+ }
+
+int speed(const std::vector<std::string> &args)
+ {
+ OptionParser opts("seconds=|buf-size=|provider=");
+ opts.parse(args);
+
+ double seconds = .5;
+ u32bit buf_size = 16;
+
+ if(opts.is_set("seconds"))
+ {
+ seconds = std::atof(opts.value("seconds").c_str());
+ if(seconds < 0.1 || seconds > (5 * 60))
+ {
+ std::cout << "Invalid argument to --seconds" << std::endl;
+ return 2;
+ }
+ }
+
+ if(opts.is_set("buf-size"))
+ {
+ buf_size = std::atoi(opts.value("buf-size").c_str());
+ if(buf_size == 0 || buf_size > 1024)
+ {
+ std::cout << "Invalid argument to --buf-size" << std::endl;
+ return 2;
+ }
+ }
+
+ const std::string provider = opts.value_if_set("provider");
+
+ std::vector<std::string> opt_args = opts.arguments();
+
+ if(opt_args.empty())
+ opt_args = default_benchmark_list;
+
+ if(opt_args[0] == "help" || opt_args[0] == "-h")
+ {
+ std::cout << "Usage: " << args[0] << " [algo name...]" << std::endl;
+ return 1;
+ }
+
+ AutoSeeded_RNG rng;
+
+ for(auto alg: opt_args)
+ {
+ bench_algo(alg, provider, rng, seconds, buf_size);
+ }
+
+ return 0;
+ }
+
+REGISTER_APP(speed);
+
+}
+#endif // BOTAN_HAS_RUNTIME_BENCHMARKING
diff --git a/src/cli/tls_client.cpp b/src/cli/tls_client.cpp
new file mode 100644
index 000000000..7f74e1a37
--- /dev/null
+++ b/src/cli/tls_client.cpp
@@ -0,0 +1,294 @@
+/*
+* (C) 2014 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "apps.h"
+
+#if defined(BOTAN_HAS_TLS) && defined(BOTAN_TARGET_OS_HAS_SOCKETS)
+
+#include <botan/tls_client.h>
+#include <botan/pkcs8.h>
+#include <botan/hex.h>
+
+#if defined(BOTAN_HAS_TLS_SQLITE3_SESSION_MANAGER)
+ #include <botan/tls_session_manager_sqlite.h>
+#endif
+
+#include <string>
+#include <memory>
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#if !defined(MSG_NOSIGNAL)
+ #define MSG_NOSIGNAL 0
+#endif
+
+#include "credentials.h"
+
+using namespace Botan;
+
+using namespace std::placeholders;
+
+namespace {
+
+int connect_to_host(const std::string& host, u16bit port, bool tcp)
+ {
+ hostent* host_addr = ::gethostbyname(host.c_str());
+
+ if(!host_addr)
+ throw std::runtime_error("gethostbyname failed for " + host);
+
+ if(host_addr->h_addrtype != AF_INET) // FIXME
+ throw std::runtime_error(host + " has IPv6 address, not supported");
+
+ int type = tcp ? SOCK_STREAM : SOCK_DGRAM;
+
+ int fd = ::socket(PF_INET, type, 0);
+ if(fd == -1)
+ throw std::runtime_error("Unable to acquire socket");
+
+ sockaddr_in socket_info;
+ ::memset(&socket_info, 0, sizeof(socket_info));
+ socket_info.sin_family = AF_INET;
+ socket_info.sin_port = htons(port);
+
+ ::memcpy(&socket_info.sin_addr,
+ host_addr->h_addr,
+ host_addr->h_length);
+
+ socket_info.sin_addr = *reinterpret_cast<struct in_addr*>(host_addr->h_addr); // FIXME
+
+ if(::connect(fd, (sockaddr*)&socket_info, sizeof(struct sockaddr)) != 0)
+ {
+ ::close(fd);
+ throw std::runtime_error("connect failed");
+ }
+
+ return fd;
+ }
+
+bool handshake_complete(const TLS::Session& session)
+ {
+ std::cout << "Handshake complete, " << session.version().to_string()
+ << " using " << session.ciphersuite().to_string() << std::endl;
+
+ if(!session.session_id().empty())
+ std::cout << "Session ID " << hex_encode(session.session_id()) << std::endl;
+
+ if(!session.session_ticket().empty())
+ std::cout << "Session ticket " << hex_encode(session.session_ticket()) << std::endl;
+
+ return true;
+ }
+
+void dgram_socket_write(int sockfd, const byte buf[], size_t length)
+ {
+ int r = send(sockfd, buf, length, MSG_NOSIGNAL);
+
+ if(r == -1)
+ throw std::runtime_error("Socket write failed errno=" + std::to_string(errno));
+ }
+
+void stream_socket_write(int sockfd, const byte buf[], size_t length)
+ {
+ size_t offset = 0;
+
+ while(length)
+ {
+ ssize_t sent = ::send(sockfd, (const char*)buf + offset,
+ length, MSG_NOSIGNAL);
+
+ if(sent == -1)
+ {
+ if(errno == EINTR)
+ sent = 0;
+ else
+ throw std::runtime_error("Socket write failed errno=" + std::to_string(errno));
+ }
+
+ offset += sent;
+ length -= sent;
+ }
+ }
+
+bool got_alert = false;
+
+void alert_received(TLS::Alert alert, const byte [], size_t )
+ {
+ std::cout << "Alert: " << alert.type_string() << std::endl;
+ got_alert = true;
+ }
+
+void process_data(const byte buf[], size_t buf_size)
+ {
+ for(size_t i = 0; i != buf_size; ++i)
+ std::cout << buf[i];
+ }
+
+int tls_client(const std::vector<std::string> &args)
+ {
+ if(args.size() != 2 && args.size() != 3 && args.size() != 4)
+ {
+ std::cout << "Usage " << args[0] << " host [port] [udp|tcp]" << std::endl;
+ return 1;
+ }
+
+ try
+ {
+ AutoSeeded_RNG rng;
+ TLS::Policy policy;
+
+#if defined(BOTAN_HAS_TLS_SQLITE3_SESSION_MANAGER)
+ const std::string passphrase = "correct horse battery staple";
+ const std::string sessions_db = "sessions.db";
+
+ TLS::Session_Manager_SQLite session_manager(passphrase,
+ rng,
+ sessions_db);
+#else
+ TLS::Session_Manager_In_Memory session_manager(rng);
+#endif
+
+ Basic_Credentials_Manager creds;
+
+ const std::string host = args[1];
+ const u32bit port = args.size() >= 3 ? Botan::to_u32bit(args[2]) : 443;
+ const std::string transport = args.size() >= 4 ? args[3] : "tcp";
+
+ const bool use_tcp = (transport == "tcp");
+
+ const std::vector<std::string> protocols_to_offer = { "test/9.9", "http/1.1", "echo/9.1" };
+
+ int sockfd = connect_to_host(host, port, use_tcp);
+
+ auto socket_write =
+ use_tcp ?
+ std::bind(stream_socket_write, sockfd, _1, _2) :
+ std::bind(dgram_socket_write, sockfd, _1, _2);
+
+ auto version = policy.latest_supported_version(!use_tcp);
+
+ TLS::Client client(socket_write,
+ process_data,
+ alert_received,
+ handshake_complete,
+ session_manager,
+ creds,
+ policy,
+ rng,
+ TLS::Server_Information(host, port),
+ version,
+ protocols_to_offer);
+
+ bool first_active = true;
+
+ while(!client.is_closed())
+ {
+ fd_set readfds;
+ FD_ZERO(&readfds);
+ FD_SET(sockfd, &readfds);
+
+ if(client.is_active())
+ {
+ FD_SET(STDIN_FILENO, &readfds);
+ if(first_active && !protocols_to_offer.empty())
+ {
+ std::string app = client.application_protocol();
+ if(app != "")
+ std::cout << "Server choose protocol: " << client.application_protocol() << std::endl;
+ first_active = false;
+ }
+ }
+
+ struct timeval timeout = { 1, 0 };
+
+ ::select(sockfd + 1, &readfds, nullptr, nullptr, &timeout);
+
+ if(FD_ISSET(sockfd, &readfds))
+ {
+ byte buf[4*1024] = { 0 };
+
+ ssize_t got = ::read(sockfd, buf, sizeof(buf));
+
+ if(got == 0)
+ {
+ std::cout << "EOF on socket" << std::endl;
+ break;
+ }
+ else if(got == -1)
+ {
+ std::cout << "Socket error: " << errno << " " << strerror(errno) << std::endl;
+ continue;
+ }
+
+ client.received_data(buf, got);
+ }
+
+ if(FD_ISSET(STDIN_FILENO, &readfds))
+ {
+ byte buf[1024] = { 0 };
+ ssize_t got = read(STDIN_FILENO, buf, sizeof(buf));
+
+ if(got == 0)
+ {
+ std::cout << "EOF on stdin" << std::endl;
+ client.close();
+ break;
+ }
+ else if(got == -1)
+ {
+ std::cout << "Stdin error: " << errno << " " << strerror(errno) << std::endl;
+ continue;
+ }
+
+ if(got == 2 && buf[1] == '\n')
+ {
+ char cmd = buf[0];
+
+ if(cmd == 'R' || cmd == 'r')
+ {
+ std::cout << "Client initiated renegotiation" << std::endl;
+ client.renegotiate(cmd == 'R');
+ }
+ else if(cmd == 'Q')
+ {
+ std::cout << "Client initiated close" << std::endl;
+ client.close();
+ }
+ }
+ else if(buf[0] == 'H')
+ client.heartbeat(&buf[1], got-1);
+ else
+ client.send(buf, got);
+ }
+
+ if(client.timeout_check())
+ {
+ std::cout << "Timeout detected" << std::endl;
+ }
+ }
+
+ ::close(sockfd);
+ }
+ catch(std::exception& e)
+ {
+ std::cout << "Exception: " << e.what() << std::endl;
+ return 1;
+ }
+ return 0;
+ }
+
+REGISTER_APP(tls_client);
+
+}
+
+#endif
diff --git a/src/cli/tls_proxy.cpp b/src/cli/tls_proxy.cpp
new file mode 100644
index 000000000..5071cb8bb
--- /dev/null
+++ b/src/cli/tls_proxy.cpp
@@ -0,0 +1,459 @@
+/*
+* TLS Server Proxy
+* (C) 2014,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "apps.h"
+
+#if defined(BOTAN_HAS_TLS) && defined(BOTAN_HAS_BOOST_ASIO)
+
+#include <iostream>
+#include <string>
+#include <vector>
+#include <thread>
+
+#define _GLIBCXX_HAVE_GTHR_DEFAULT
+#include <boost/asio.hpp>
+#include <boost/bind.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/enable_shared_from_this.hpp>
+
+#include <botan/tls_server.h>
+#include <botan/x509cert.h>
+#include <botan/pkcs8.h>
+#include <botan/auto_rng.h>
+
+#if defined(BOTAN_HAS_TLS_SQLITE3_SESSION_MANAGER)
+ #include <botan/tls_session_manager_sqlite.h>
+#endif
+
+#include "credentials.h"
+
+using boost::asio::ip::tcp;
+
+namespace Botan {
+
+namespace {
+
+inline void log_exception(const char* where, const std::exception& e)
+ {
+ std::cout << where << ' ' << e.what() << std::endl;
+ }
+
+inline void log_error(const char* where, const boost::system::error_code& error)
+ {
+ //std::cout << where << ' ' << error.message() << std::endl;
+ }
+
+inline void log_binary_message(const char* where, const byte buf[], size_t buf_len)
+ {
+ //std::cout << where << ' ' << hex_encode(buf, buf_len) << std::endl;
+ }
+
+void log_text_message(const char* where, const byte buf[], size_t buf_len)
+ {
+ //const char* c = reinterpret_cast<const char*>(buf);
+ //std::cout << where << ' ' << std::string(c, c + buf_len) << std::endl;
+ }
+
+class tls_proxy_session : public boost::enable_shared_from_this<tls_proxy_session>
+ {
+ public:
+ enum { readbuf_size = 4 * 1024 };
+
+ typedef boost::shared_ptr<tls_proxy_session> pointer;
+
+ static pointer create(boost::asio::io_service& io,
+ TLS::Session_Manager& session_manager,
+ Credentials_Manager& credentials,
+ TLS::Policy& policy,
+ tcp::resolver::iterator endpoints)
+ {
+ return pointer(
+ new tls_proxy_session(
+ io,
+ session_manager,
+ credentials,
+ policy,
+ endpoints)
+ );
+ }
+
+ tcp::socket& client_socket() { return m_client_socket; }
+
+ void start()
+ {
+ m_c2p.resize(readbuf_size);
+
+ client_read(boost::system::error_code(), 0); // start read loop
+ }
+
+ void stop()
+ {
+ m_tls.close();
+ m_client_socket.close();
+ m_server_socket.close();
+ }
+
+ private:
+ tls_proxy_session(boost::asio::io_service& io,
+ TLS::Session_Manager& session_manager,
+ Credentials_Manager& credentials,
+ TLS::Policy& policy,
+ tcp::resolver::iterator endpoints) :
+ m_strand(io),
+ m_server_endpoints(endpoints),
+ m_client_socket(io),
+ m_server_socket(io),
+ m_tls(boost::bind(&tls_proxy_session::tls_proxy_write_to_client, this, _1, _2),
+ boost::bind(&tls_proxy_session::tls_client_write_to_proxy, this, _1, _2),
+ boost::bind(&tls_proxy_session::tls_alert_cb, this, _1, _2, _3),
+ boost::bind(&tls_proxy_session::tls_handshake_complete, this, _1),
+ session_manager,
+ credentials,
+ policy,
+ m_rng)
+ {
+ }
+
+ void client_read(const boost::system::error_code& error,
+ size_t bytes_transferred)
+ {
+ if(error)
+ {
+ log_error("Read failed", error);
+ stop();
+ return;
+ }
+
+ try
+ {
+ if(!m_tls.is_active())
+ log_binary_message("From client", &m_c2p[0], bytes_transferred);
+ m_tls.received_data(&m_c2p[0], bytes_transferred);
+ }
+ catch(std::exception& e)
+ {
+ log_exception("TLS connection failed", e);
+ stop();
+ return;
+ }
+
+ m_client_socket.async_read_some(
+ boost::asio::buffer(&m_c2p[0], m_c2p.size()),
+ m_strand.wrap(boost::bind(&tls_proxy_session::client_read, shared_from_this(),
+ boost::asio::placeholders::error,
+ boost::asio::placeholders::bytes_transferred)));
+ }
+
+ void handle_client_write_completion(const boost::system::error_code& error)
+ {
+ if(error)
+ {
+ log_error("Client write", error);
+ stop();
+ return;
+ }
+
+ m_p2c.clear();
+ tls_proxy_write_to_client(nullptr, 0); // initiate another write if needed
+ }
+
+ void handle_server_write_completion(const boost::system::error_code& error)
+ {
+ if(error)
+ {
+ log_error("Server write", error);
+ stop();
+ return;
+ }
+
+ m_p2s.clear();
+ proxy_write_to_server(nullptr, 0); // initiate another write if needed
+ }
+
+ void tls_client_write_to_proxy(const byte buf[], size_t buf_len)
+ {
+ // Immediately bounce message to server
+ proxy_write_to_server(buf, buf_len);
+ }
+
+ void tls_proxy_write_to_client(const byte buf[], size_t buf_len)
+ {
+ if(buf_len > 0)
+ m_p2c_pending.insert(m_p2c_pending.end(), buf, buf + buf_len);
+
+ // no write now active and we still have output pending
+ if(m_p2c.empty() && !m_p2c_pending.empty())
+ {
+ std::swap(m_p2c_pending, m_p2c);
+
+ //log_binary_message("To Client", &m_p2c[0], m_p2c.size());
+
+ boost::asio::async_write(
+ m_client_socket,
+ boost::asio::buffer(&m_p2c[0], m_p2c.size()),
+ m_strand.wrap(boost::bind(
+ &tls_proxy_session::handle_client_write_completion,
+ shared_from_this(),
+ boost::asio::placeholders::error)));
+ }
+ }
+
+ void proxy_write_to_server(const byte buf[], size_t buf_len)
+ {
+ if(buf_len > 0)
+ m_p2s_pending.insert(m_p2s_pending.end(), buf, buf + buf_len);
+
+ // no write now active and we still have output pending
+ if(m_p2s.empty() && !m_p2s_pending.empty())
+ {
+ std::swap(m_p2s_pending, m_p2s);
+
+ log_text_message("To Server", &m_p2s[0], m_p2s.size());
+
+ boost::asio::async_write(
+ m_server_socket,
+ boost::asio::buffer(&m_p2s[0], m_p2s.size()),
+ m_strand.wrap(boost::bind(
+ &tls_proxy_session::handle_server_write_completion,
+ shared_from_this(),
+ boost::asio::placeholders::error)));
+ }
+ }
+
+ void server_read(const boost::system::error_code& error,
+ size_t bytes_transferred)
+ {
+ if(error)
+ {
+ log_error("Server read failed", error);
+ stop();
+ return;
+ }
+
+ try
+ {
+ if(bytes_transferred)
+ {
+ log_text_message("Server to client", &m_s2p[0], m_s2p.size());
+ log_binary_message("Server to client", &m_s2p[0], m_s2p.size());
+ m_tls.send(&m_s2p[0], bytes_transferred);
+ }
+ }
+ catch(std::exception& e)
+ {
+ log_exception("TLS connection failed", e);
+ stop();
+ return;
+ }
+
+ m_s2p.resize(readbuf_size);
+
+ m_server_socket.async_read_some(
+ boost::asio::buffer(&m_s2p[0], m_s2p.size()),
+ m_strand.wrap(boost::bind(&tls_proxy_session::server_read, shared_from_this(),
+ boost::asio::placeholders::error,
+ boost::asio::placeholders::bytes_transferred)));
+ }
+
+ bool tls_handshake_complete(const TLS::Session& session)
+ {
+ //std::cout << "Handshake from client complete" << std::endl;
+
+ m_hostname = session.server_info().hostname();
+
+ if(m_hostname != "")
+ std::cout << "Client requested hostname '" << m_hostname << "'" << std::endl;
+
+ async_connect(m_server_socket, m_server_endpoints,
+ [this](boost::system::error_code ec, tcp::resolver::iterator endpoint)
+ {
+ if(ec)
+ {
+ log_error("Server connection", ec);
+ return;
+ }
+
+ server_read(boost::system::error_code(), 0); // start read loop
+ proxy_write_to_server(nullptr, 0);
+ });
+ return true;
+ }
+
+ void tls_alert_cb(TLS::Alert alert, const byte[], size_t)
+ {
+ if(alert.type() == TLS::Alert::CLOSE_NOTIFY)
+ {
+ m_tls.close();
+ return;
+ }
+ else
+ std::cout << "Alert " << alert.type_string() << std::endl;
+ }
+
+ boost::asio::io_service::strand m_strand;
+
+ tcp::resolver::iterator m_server_endpoints;
+
+ tcp::socket m_client_socket;
+ tcp::socket m_server_socket;
+
+ AutoSeeded_RNG m_rng;
+ TLS::Server m_tls;
+ std::string m_hostname;
+
+ std::vector<byte> m_c2p;
+ std::vector<byte> m_p2c;
+ std::vector<byte> m_p2c_pending;
+
+ std::vector<byte> m_s2p;
+ std::vector<byte> m_p2s;
+ std::vector<byte> m_p2s_pending;
+ };
+
+class tls_proxy_server
+ {
+ public:
+ typedef tls_proxy_session session;
+
+ tls_proxy_server(boost::asio::io_service& io, unsigned short port,
+ tcp::resolver::iterator endpoints,
+ Credentials_Manager& creds,
+ TLS::Policy& policy,
+ TLS::Session_Manager& session_mgr) :
+ m_acceptor(io, tcp::endpoint(tcp::v4(), port)),
+ m_server_endpoints(endpoints),
+ m_creds(creds),
+ m_policy(policy),
+ m_session_manager(session_mgr)
+ {
+ session::pointer new_session = make_session();
+
+ m_acceptor.async_accept(
+ new_session->client_socket(),
+ boost::bind(
+ &tls_proxy_server::handle_accept,
+ this,
+ new_session,
+ boost::asio::placeholders::error)
+ );
+ }
+
+ private:
+ session::pointer make_session()
+ {
+ return session::create(
+ m_acceptor.get_io_service(),
+ m_session_manager,
+ m_creds,
+ m_policy,
+ m_server_endpoints
+ );
+ }
+
+ void handle_accept(session::pointer new_session,
+ const boost::system::error_code& error)
+ {
+ if (!error)
+ {
+ new_session->start();
+
+ new_session = make_session();
+
+ m_acceptor.async_accept(
+ new_session->client_socket(),
+ boost::bind(
+ &tls_proxy_server::handle_accept,
+ this,
+ new_session,
+ boost::asio::placeholders::error)
+ );
+ }
+ }
+
+ tcp::acceptor m_acceptor;
+ tcp::resolver::iterator m_server_endpoints;
+
+ Credentials_Manager& m_creds;
+ TLS::Policy& m_policy;
+ TLS::Session_Manager& m_session_manager;
+ };
+
+size_t choose_thread_count()
+ {
+ size_t result = std::thread::hardware_concurrency();
+
+ if(result)
+ return result;
+
+ return 2;
+ }
+
+int tls_proxy(const std::vector<std::string> &args)
+ {
+ if(args.size() != 6)
+ {
+ std::cout << "Usage: " << args[0] << " listen_port target_host target_port server_cert server_key" << std::endl;
+ return 1;
+ }
+
+ const size_t listen_port = to_u32bit(args[1]);
+ const std::string target = args[2];
+ const std::string target_port = args[3];
+
+ const std::string server_crt = args[4];
+ const std::string server_key = args[5];
+
+ const size_t num_threads = choose_thread_count(); // make configurable
+
+ AutoSeeded_RNG rng;
+ Basic_Credentials_Manager creds(rng, server_crt, server_key);
+
+ TLS::Policy policy; // TODO: Read policy from text file
+
+ try
+ {
+ boost::asio::io_service io;
+
+ tcp::resolver resolver(io);
+ auto server_endpoint_iterator = resolver.resolve({ target, target_port });
+
+#if defined(BOTAN_HAS_TLS_SQLITE3_SESSION_MANAGER)
+ // Todo: make configurable
+ const std::string sessions_passphrase = "correct horse battery staple";
+ const std::string sessions_db = "sessions.db";
+ TLS::Session_Manager_SQLite sessions(sessions_passphrase, rng, sessions_db);
+#else
+ TLS::Session_Manager_In_Memory sessions(rng);
+#endif
+
+ tls_proxy_server server(io, listen_port, server_endpoint_iterator, creds, policy, sessions);
+
+ std::vector<std::shared_ptr<std::thread>> threads;
+
+ for(size_t i = 2; i <= num_threads; ++i)
+ threads.push_back(std::make_shared<std::thread>([&io]() { io.run(); }));
+
+ io.run();
+
+ for (size_t i = 0; i < threads.size(); ++i)
+ threads[i]->join();
+ }
+ catch (std::exception& e)
+ {
+ std::cerr << e.what() << std::endl;
+ }
+
+ return 0;
+ }
+
+}
+
+}
+
+REGISTER_APP(tls_proxy);
+
+#endif
diff --git a/src/cli/tls_server.cpp b/src/cli/tls_server.cpp
new file mode 100644
index 000000000..ea68208b6
--- /dev/null
+++ b/src/cli/tls_server.cpp
@@ -0,0 +1,270 @@
+/*
+* TLS echo server using BSD sockets
+* (C) 2014 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "apps.h"
+
+#if defined(BOTAN_HAS_TLS) && defined(BOTAN_TARGET_OS_HAS_SOCKETS)
+
+#include <botan/tls_server.h>
+#include <botan/hex.h>
+
+#include <botan/rsa.h>
+#include <botan/dsa.h>
+#include <botan/x509self.h>
+#include <botan/secqueue.h>
+
+#include "credentials.h"
+
+using namespace Botan;
+
+using namespace std::placeholders;
+
+#include <list>
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#if !defined(MSG_NOSIGNAL)
+ #define MSG_NOSIGNAL 0
+#endif
+
+namespace {
+
+int make_server_socket(bool is_tcp, u16bit port)
+ {
+ const int type = is_tcp ? SOCK_STREAM : SOCK_DGRAM;
+
+ int fd = ::socket(PF_INET, type, 0);
+ if(fd == -1)
+ throw std::runtime_error("Unable to acquire socket");
+
+ sockaddr_in socket_info;
+ ::memset(&socket_info, 0, sizeof(socket_info));
+ socket_info.sin_family = AF_INET;
+ socket_info.sin_port = htons(port);
+
+ // FIXME: support limiting listeners
+ socket_info.sin_addr.s_addr = INADDR_ANY;
+
+ if(::bind(fd, (sockaddr*)&socket_info, sizeof(struct sockaddr)) != 0)
+ {
+ ::close(fd);
+ throw std::runtime_error("server bind failed");
+ }
+
+ if(is_tcp)
+ {
+ if(::listen(fd, 100) != 0)
+ {
+ ::close(fd);
+ throw std::runtime_error("listen failed");
+ }
+ }
+
+ return fd;
+ }
+
+bool handshake_complete(const TLS::Session& session)
+ {
+ std::cout << "Handshake complete, " << session.version().to_string()
+ << " using " << session.ciphersuite().to_string() << std::endl;
+
+ if(!session.session_id().empty())
+ std::cout << "Session ID " << hex_encode(session.session_id()) << std::endl;
+
+ if(!session.session_ticket().empty())
+ std::cout << "Session ticket " << hex_encode(session.session_ticket()) << std::endl;
+
+ return true;
+ }
+
+void dgram_socket_write(int sockfd, const byte buf[], size_t length)
+ {
+ ssize_t sent = ::send(sockfd, buf, length, MSG_NOSIGNAL);
+
+ if(sent == -1)
+ std::cout << "Error writing to socket - " << strerror(errno) << std::endl;
+ else if(sent != static_cast<ssize_t>(length))
+ std::cout << "Packet of length " << length << " truncated to " << sent << std::endl;
+ }
+
+void stream_socket_write(int sockfd, const byte buf[], size_t length)
+ {
+ while(length)
+ {
+ ssize_t sent = ::send(sockfd, buf, length, MSG_NOSIGNAL);
+
+ if(sent == -1)
+ {
+ if(errno == EINTR)
+ sent = 0;
+ else
+ throw std::runtime_error("Socket write failed");
+ }
+
+ buf += sent;
+ length -= sent;
+ }
+ }
+
+void alert_received(TLS::Alert alert, const byte[], size_t)
+ {
+ std::cout << "Alert: " << alert.type_string() << std::endl;
+ }
+
+int tls_server(const std::vector<std::string> &args)
+ {
+ if(args.size() != 4 && args.size() != 5)
+ {
+ std::cout << "Usage: " << args[0] << " server.crt server.key port [tcp|udp]" << std::endl;
+ return 1;
+ }
+
+ const std::string server_crt = args[1];
+ const std::string server_key = args[2];
+ const int port = to_u32bit(args[3]);
+ const std::string transport = (args.size() >= 5) ? args[4] : "tcp";
+
+ const bool is_tcp = (transport == "tcp");
+
+ try
+ {
+ AutoSeeded_RNG rng;
+
+ TLS::Policy policy;
+
+ TLS::Session_Manager_In_Memory session_manager(rng);
+
+ Basic_Credentials_Manager creds(rng, server_crt, server_key);
+
+ auto protocol_chooser = [](const std::vector<std::string>& protocols) -> std::string {
+ for(size_t i = 0; i != protocols.size(); ++i)
+ std::cout << "Client offered protocol " << i << " = " << protocols[i] << std::endl;
+ return "echo/1.0"; // too bad
+ };
+
+ std::cout << "Listening for new connections on " << transport << " port " << port << std::endl;
+
+ int server_fd = make_server_socket(is_tcp, port);
+
+ while(true)
+ {
+ try
+ {
+ int fd;
+
+ if(is_tcp)
+ fd = ::accept(server_fd, nullptr, nullptr);
+ else
+ {
+ struct sockaddr_in from;
+ socklen_t from_len = sizeof(sockaddr_in);
+
+ if(::recvfrom(server_fd, nullptr, 0, MSG_PEEK,
+ (struct sockaddr*)&from, &from_len) != 0)
+ throw std::runtime_error("Could not peek next packet");
+
+ if(::connect(server_fd, (struct sockaddr*)&from, from_len) != 0)
+ throw std::runtime_error("Could not connect UDP socket");
+
+ fd = server_fd;
+ }
+
+ std::cout << "New connection received" << std::endl;
+
+ auto socket_write = is_tcp ? std::bind(stream_socket_write, fd, _1, _2) :
+ std::bind(dgram_socket_write, fd, _1, _2);
+
+ std::string s;
+ std::list<std::string> pending_output;
+
+ auto proc_fn = [&](const byte input[], size_t input_len)
+ {
+ for(size_t i = 0; i != input_len; ++i)
+ {
+ const char c = static_cast<char>(input[i]);
+ s += c;
+ if(c == '\n')
+ {
+ pending_output.push_back(s);
+ s.clear();
+ }
+ }
+ };
+
+ TLS::Server server(socket_write,
+ proc_fn,
+ alert_received,
+ handshake_complete,
+ session_manager,
+ creds,
+ policy,
+ rng,
+ protocol_chooser,
+ !is_tcp);
+
+ while(!server.is_closed())
+ {
+ byte buf[4*1024] = { 0 };
+ ssize_t got = ::read(fd, buf, sizeof(buf));
+
+ if(got == -1)
+ {
+ std::cout << "Error in socket read - " << strerror(errno) << std::endl;
+ break;
+ }
+
+ if(got == 0)
+ {
+ std::cout << "EOF on socket" << std::endl;
+ break;
+ }
+
+ server.received_data(buf, got);
+
+ while(server.is_active() && !pending_output.empty())
+ {
+ std::string s = pending_output.front();
+ pending_output.pop_front();
+ server.send(s);
+
+ if(s == "quit\n")
+ server.close();
+ }
+ }
+
+ if(is_tcp)
+ ::close(fd);
+
+ }
+ catch(std::exception& e)
+ {
+ std::cout << "Connection problem: " << e.what() << std::endl;
+ return 1;
+ }
+ }
+ }
+ catch(std::exception& e)
+ {
+ std::cout << e.what() << std::endl;
+ return 1;
+ }
+
+ return 0;
+ }
+
+REGISTER_APP(tls_server);
+
+}
+
+#endif
diff --git a/src/cli/x509print.cpp b/src/cli/x509print.cpp
new file mode 100644
index 000000000..e583c91c2
--- /dev/null
+++ b/src/cli/x509print.cpp
@@ -0,0 +1,32 @@
+/*
+* (C) 2014 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "apps.h"
+#if defined(BOTAN_HAS_X509_CERTIFICATES)
+#include <botan/x509cert.h>
+
+namespace {
+
+int x509print(const std::vector<std::string> &args)
+ {
+ if(args.size() != 1)
+ {
+ std::cout << "Usage: " << args[0] << " cert.pem" << std::endl;
+ return 1;
+ }
+
+ X509_Certificate cert(args[1]);
+
+ std::cout << cert.to_string() << std::endl;
+
+ return 0;
+ }
+
+REGISTER_APP(x509print);
+
+}
+
+#endif