aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJack Lloyd <[email protected]>2015-12-19 15:36:40 -0500
committerJack Lloyd <[email protected]>2015-12-19 15:36:40 -0500
commitb48e6fb097c62bb246629ee7a182c57e497e4130 (patch)
tree0cb8ea2d05a89f5e90467f323ae56268d4d3480e
parentd774a9edc46ffcebb28205a678058f083ea75c28 (diff)
CLI rewrite
The command line tools' origin as a collection of examples and test programs glued together led to some unfortunate problems; lots of hardcoded values, missing parameters, and obsolete crypto. Adds a small library for writing command line programs of the sort needed here (cli.h), which cuts the length of many of the commands in half and makes commands more pleasant to write and extend. Generalizes a lot of the commands also, eg previously only signing/verification with DSA/SHA-1 was included! Removes the fuzzer entry point since that's fairly useless outside of an instrumented build. Removes the in-library API for benchmarking.
-rw-r--r--doc/news.rst8
-rw-r--r--src/build-data/os/darwin.txt1
-rw-r--r--src/build-data/os/dragonfly.txt1
-rw-r--r--src/build-data/os/freebsd.txt1
-rw-r--r--src/build-data/os/linux.txt1
-rw-r--r--src/build-data/os/openbsd.txt1
-rw-r--r--src/cli.tgzbin0 -> 19558 bytes
-rw-r--r--src/cli/apps.h77
-rw-r--r--src/cli/asn1.cpp227
-rw-r--r--src/cli/base64.cpp97
-rw-r--r--src/cli/bcrypt.cpp49
-rw-r--r--src/cli/bench.cpp767
-rw-r--r--src/cli/ca.cpp78
-rw-r--r--src/cli/cc_enc.cpp161
-rw-r--r--src/cli/cert_verify.cpp55
-rw-r--r--src/cli/cli.h508
-rw-r--r--src/cli/compress.cpp172
-rw-r--r--src/cli/credentials.h30
-rw-r--r--src/cli/dl_group.cpp85
-rw-r--r--src/cli/dsa_sign.cpp85
-rw-r--r--src/cli/dsa_ver.cpp92
-rw-r--r--src/cli/factor.cpp160
-rw-r--r--src/cli/fpe.cpp153
-rw-r--r--src/cli/fuzzer.cpp141
-rw-r--r--src/cli/getopt.cpp59
-rw-r--r--src/cli/getopt.h106
-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.cpp186
-rw-r--r--src/cli/math.cpp188
-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/pubkey.cpp278
-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.cpp400
-rw-r--r--src/cli/tls_proxy.cpp160
-rw-r--r--src/cli/tls_server.cpp284
-rw-r--r--src/cli/utils.cpp266
-rw-r--r--src/cli/x509.cpp224
-rw-r--r--src/cli/x509print.cpp32
-rw-r--r--src/lib/misc/benchmark/benchmark.cpp165
-rw-r--r--src/lib/misc/benchmark/benchmark.h35
-rw-r--r--src/lib/misc/benchmark/info.txt1
-rw-r--r--src/lib/tls/tls_handshake_hash.h2
-rw-r--r--src/lib/utils/stl_util.h15
-rw-r--r--src/lib/utils/version.cpp24
-rw-r--r--src/lib/utils/version.h13
-rw-r--r--src/tests/main.cpp267
-rw-r--r--src/tests/test_stream.cpp3
-rw-r--r--src/tests/tests.cpp24
-rw-r--r--src/tests/tests.h21
63 files changed, 3299 insertions, 4572 deletions
diff --git a/doc/news.rst b/doc/news.rst
index e9b3d1449..60f90e0b9 100644
--- a/doc/news.rst
+++ b/doc/news.rst
@@ -8,8 +8,12 @@ Version 1.11.26, Not Yet Released
Previously the library would in many cases throw `std::runtime_error`
or `std::invalid_argument` exceptions which would make it hard to determine
the source of the error in some cases.
-
-* Enable RdRand entropy source on Windows/MSVC.
+
+* The command line interface has been mostly rewritten. The syntax of
+ many of the programs has changed, and a number have been extended with
+ new features and options.
+
+* Enable RdRand entropy source on Windows/MSVC. GH #364
Version 1.11.25, 2015-12-07
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/src/build-data/os/darwin.txt b/src/build-data/os/darwin.txt
index b1f2363dc..f95e8f5e8 100644
--- a/src/build-data/os/darwin.txt
+++ b/src/build-data/os/darwin.txt
@@ -17,6 +17,7 @@ gmtime_r
memset_s
readdir
timegm
+sockets
</target_features>
<aliases>
diff --git a/src/build-data/os/dragonfly.txt b/src/build-data/os/dragonfly.txt
index ff8f41d2a..f9137d3c1 100644
--- a/src/build-data/os/dragonfly.txt
+++ b/src/build-data/os/dragonfly.txt
@@ -4,4 +4,5 @@ os_type unix
clock_gettime
gettimeofday
posix_mlock
+sockets
</target_features>
diff --git a/src/build-data/os/freebsd.txt b/src/build-data/os/freebsd.txt
index 510a7accd..dfe3d2edf 100644
--- a/src/build-data/os/freebsd.txt
+++ b/src/build-data/os/freebsd.txt
@@ -8,4 +8,5 @@ gmtime_r
dlopen
readdir
timegm
+sockets
</target_features>
diff --git a/src/build-data/os/linux.txt b/src/build-data/os/linux.txt
index bb65055e4..42f101c3d 100644
--- a/src/build-data/os/linux.txt
+++ b/src/build-data/os/linux.txt
@@ -12,6 +12,7 @@ gmtime_r
dlopen
readdir
timegm
+sockets
</target_features>
<aliases>
diff --git a/src/build-data/os/openbsd.txt b/src/build-data/os/openbsd.txt
index 510a7accd..dfe3d2edf 100644
--- a/src/build-data/os/openbsd.txt
+++ b/src/build-data/os/openbsd.txt
@@ -8,4 +8,5 @@ gmtime_r
dlopen
readdir
timegm
+sockets
</target_features>
diff --git a/src/cli.tgz b/src/cli.tgz
new file mode 100644
index 000000000..f5814fd78
--- /dev/null
+++ b/src/cli.tgz
Binary files differ
diff --git a/src/cli/apps.h b/src/cli/apps.h
deleted file mode 100644
index 9f1f00ba2..000000000
--- a/src/cli/apps.h
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
-* (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
index 2aa94cc39..8096f2d19 100644
--- a/src/cli/asn1.cpp
+++ b/src/cli/asn1.cpp
@@ -4,7 +4,7 @@
* Botan is released under the Simplified BSD License (see license.txt)
*/
-#include "apps.h"
+#include "cli.h"
#if defined(BOTAN_HAS_ASN1) && defined(BOTAN_HAS_PEM_CODEC)
@@ -17,7 +17,6 @@
#include <botan/oids.h>
#include <botan/pem.h>
#include <botan/charset.h>
-using namespace Botan;
#include <iostream>
#include <iomanip>
@@ -34,9 +33,11 @@ using namespace Botan;
*/
#define INITIAL_LEVEL 0
+namespace Botan_CLI {
+
namespace {
-std::string url_encode(const std::vector<byte>& in)
+std::string url_encode(const std::vector<uint8_t>& in)
{
std::ostringstream out;
@@ -55,7 +56,7 @@ std::string url_encode(const std::vector<byte>& in)
}
if(unprintable >= in.size() / 4)
- return hex_encode(in);
+ return Botan::hex_encode(in);
return out.str();
}
@@ -95,57 +96,89 @@ void emit(const std::string& type, size_t level, size_t length, const std::strin
std::cout << out.str() << std::endl;
}
-std::string type_name(ASN1_Tag type)
+std::string type_name(Botan::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";
+ switch(type)
+ {
+ case Botan::PRINTABLE_STRING:
+ return "PRINTABLE STRING";
+
+ case Botan::NUMERIC_STRING:
+ return "NUMERIC STRING";
+
+ case Botan::IA5_STRING:
+ return "IA5 STRING";
+
+ case Botan::T61_STRING:
+ return "T61 STRING";
+
+ case Botan::UTF8_STRING:
+ return "UTF8 STRING";
+
+ case Botan::VISIBLE_STRING:
+ return "VISIBLE STRING";
+
+ case Botan::BMP_STRING:
+ return "BMP STRING";
+
+ case Botan::UTC_TIME:
+ return "UTC TIME";
+
+ case Botan::GENERALIZED_TIME:
+ return "GENERALIZED TIME";
+
+ case Botan::OCTET_STRING:
+ return "OCTET STRING";
+
+ case Botan::BIT_STRING:
+ return "BIT STRING";
+
+ case Botan::ENUMERATED:
+ return "ENUMERATED";
+
+ case Botan::INTEGER:
+ return "INTEGER";
+
+ case Botan::NULL_TAG:
+ return "NULL";
+
+ case Botan::OBJECT_ID:
+ return "OBJECT";
+
+ case Botan::BOOLEAN:
+ return "BOOLEAN";
+ }
+
return "(UNKNOWN)";
}
-void decode(BER_Decoder& decoder, size_t level)
+void decode(Botan::BER_Decoder& decoder, size_t level)
{
- BER_Object obj = decoder.get_next_object();
+ Botan::BER_Object obj = decoder.get_next_object();
- while(obj.type_tag != NO_OBJECT)
+ while(obj.type_tag != Botan::NO_OBJECT)
{
- const ASN1_Tag type_tag = obj.type_tag;
- const ASN1_Tag class_tag = obj.class_tag;
+ const Botan::ASN1_Tag type_tag = obj.type_tag;
+ const Botan::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;
+ Botan::DER_Encoder encoder;
encoder.add_object(type_tag, class_tag, obj.value);
- std::vector<byte> bits = encoder.get_contents_unlocked();
+ std::vector<uint8_t> bits = encoder.get_contents_unlocked();
- BER_Decoder data(bits);
+ Botan::BER_Decoder data(bits);
- if(class_tag & CONSTRUCTED)
+ if(class_tag & Botan::CONSTRUCTED)
{
- BER_Decoder cons_info(obj.value);
- if(type_tag == SEQUENCE)
+ Botan::BER_Decoder cons_info(obj.value);
+ if(type_tag == Botan::SEQUENCE)
{
emit("SEQUENCE", level, length);
decode(cons_info, level+1);
}
- else if(type_tag == SET)
+ else if(type_tag == Botan::SET)
{
emit("SET", level, length);
decode(cons_info, level+1);
@@ -154,13 +187,13 @@ void decode(BER_Decoder& decoder, size_t level)
{
std::string name;
- if((class_tag & APPLICATION) || (class_tag & CONTEXT_SPECIFIC))
+ if((class_tag & Botan::APPLICATION) || (class_tag & Botan::CONTEXT_SPECIFIC))
{
name = "cons [" + std::to_string(type_tag) + "]";
- if(class_tag & APPLICATION)
+ if(class_tag & Botan::APPLICATION)
name += " appl";
- if(class_tag & CONTEXT_SPECIFIC)
+ if(class_tag & Botan::CONTEXT_SPECIFIC)
name += " context";
}
else
@@ -170,15 +203,15 @@ void decode(BER_Decoder& decoder, size_t level)
decode(cons_info, level+1);
}
}
- else if((class_tag & APPLICATION) || (class_tag & CONTEXT_SPECIFIC))
+ else if((class_tag & Botan::APPLICATION) || (class_tag & Botan::CONTEXT_SPECIFIC))
{
#if 0
- std::vector<byte> bits;
+ std::vector<uint8_t> bits;
data.decode(bits, type_tag);
try
{
- BER_Decoder inner(bits);
+ Botan::BER_Decoder inner(bits);
decode(inner, level + 1);
}
catch(...)
@@ -191,33 +224,33 @@ void decode(BER_Decoder& decoder, size_t level)
url_encode(bits));
#endif
}
- else if(type_tag == OBJECT_ID)
+ else if(type_tag == Botan::OBJECT_ID)
{
- OID oid;
+ Botan::OID oid;
data.decode(oid);
- std::string out = OIDS::lookup(oid);
+ std::string out = Botan::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)
+ else if(type_tag == Botan::INTEGER || type_tag == Botan::ENUMERATED)
{
- BigInt number;
+ Botan::BigInt number;
- if(type_tag == INTEGER)
+ if(type_tag == Botan::INTEGER)
data.decode(number);
- else if(type_tag == ENUMERATED)
- data.decode(number, ENUMERATED, class_tag);
+ else if(type_tag == Botan::ENUMERATED)
+ data.decode(number, Botan::ENUMERATED, class_tag);
- std::vector<byte> rep;
+ std::vector<uint8_t> rep;
/* If it's small, it's probably a number, not a hash */
if(number.bits() <= 20)
- rep = BigInt::encode(number, BigInt::Decimal);
+ rep = Botan::BigInt::encode(number, Botan::BigInt::Decimal);
else
- rep = BigInt::encode(number, BigInt::Hexadecimal);
+ rep = Botan::BigInt::encode(number, Botan::BigInt::Hexadecimal);
std::string str;
for(size_t i = 0; i != rep.size(); ++i)
@@ -225,25 +258,25 @@ void decode(BER_Decoder& decoder, size_t level)
emit(type_name(type_tag), level, length, str);
}
- else if(type_tag == BOOLEAN)
+ else if(type_tag == Botan::BOOLEAN)
{
bool boolean;
data.decode(boolean);
emit(type_name(type_tag),
level, length, (boolean ? "true" : "false"));
}
- else if(type_tag == NULL_TAG)
+ else if(type_tag == Botan::NULL_TAG)
{
emit(type_name(type_tag), level, length);
}
- else if(type_tag == OCTET_STRING)
+ else if(type_tag == Botan::OCTET_STRING)
{
- std::vector<byte> bits;
+ std::vector<uint8_t> bits;
data.decode(bits, type_tag);
try
{
- BER_Decoder inner(bits);
+ Botan::BER_Decoder inner(bits);
decode(inner, level + 1);
}
catch(...)
@@ -252,9 +285,9 @@ void decode(BER_Decoder& decoder, size_t level)
url_encode(bits));
}
}
- else if(type_tag == BIT_STRING)
+ else if(type_tag == Botan::BIT_STRING)
{
- std::vector<byte> bits;
+ std::vector<uint8_t> bits;
data.decode(bits, type_tag);
std::vector<bool> bit_set;
@@ -278,30 +311,31 @@ void decode(BER_Decoder& decoder, size_t level)
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)
+ else if(type_tag == Botan::PRINTABLE_STRING ||
+ type_tag == Botan::NUMERIC_STRING ||
+ type_tag == Botan::IA5_STRING ||
+ type_tag == Botan::T61_STRING ||
+ type_tag == Botan::VISIBLE_STRING ||
+ type_tag == Botan::UTF8_STRING ||
+ type_tag == Botan::BMP_STRING)
{
- ASN1_String str;
+ Botan::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));
+ Botan::Charset::transcode(str.iso_8859(),
+ Botan::LATIN1_CHARSET,
+ Botan::UTF8_CHARSET));
}
else
{
emit(type_name(type_tag), level, length, str.iso_8859());
}
}
- else if(type_tag == UTC_TIME || type_tag == GENERALIZED_TIME)
+ else if(type_tag == Botan::UTC_TIME || type_tag == Botan::GENERALIZED_TIME)
{
- X509_Time time;
+ Botan::X509_Time time;
data.decode(time);
emit(type_name(type_tag), level, length, time.readable_string());
}
@@ -317,39 +351,32 @@ void decode(BER_Decoder& decoder, size_t level)
}
}
-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]);
+class ASN1_Printer : public Command
+ {
+ public:
+ ASN1_Printer() : Command("asn1print file") {}
- if(!PEM_Code::matches(in))
+ void go()
{
- 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;
- }
+ Botan::DataSource_Stream in(get_arg("file"));
- return 0;
- }
+ if(!Botan::PEM_Code::matches(in))
+ {
+ Botan::BER_Decoder decoder(in);
+ decode(decoder, INITIAL_LEVEL);
+ }
+ else
+ {
+ std::string label; // ignored
+ Botan::BER_Decoder decoder(Botan::PEM_Code::decode(in, label));
+ decode(decoder, INITIAL_LEVEL);
+ }
+ }
+ };
-REGISTER_APP(asn1);
+BOTAN_REGISTER_COMMAND(ASN1_Printer);
}
diff --git a/src/cli/base64.cpp b/src/cli/base64.cpp
deleted file mode 100644
index d2a9a1853..000000000
--- a/src/cli/base64.cpp
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
-* 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
deleted file mode 100644
index 81f7c536e..000000000
--- a/src/cli/bcrypt.cpp
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
-* (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/bench.cpp b/src/cli/bench.cpp
new file mode 100644
index 000000000..d2688421c
--- /dev/null
+++ b/src/cli/bench.cpp
@@ -0,0 +1,767 @@
+/*
+* (C) 2009,2010,2014,2015 Jack Lloyd
+* (C) 2015 Simon Warta (Kullo GmbH)
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "cli.h"
+#include <sstream>
+#include <iomanip>
+#include <chrono>
+#include <functional>
+
+// Always available:
+#include <botan/block_cipher.h>
+#include <botan/stream_cipher.h>
+#include <botan/hash.h>
+#include <botan/mac.h>
+#include <botan/cipher_mode.h>
+#include <botan/auto_rng.h>
+
+#if defined(BOTAN_HAS_PUBLIC_KEY_CRYPTO)
+ #include <botan/pkcs8.h>
+ #include <botan/pubkey.h>
+ #include <botan/x509_key.h>
+#endif
+
+#if defined(BOTAN_HAS_NUMBERTHEORY)
+ #include <botan/numthry.h>
+#endif
+
+#if defined(BOTAN_HAS_RSA)
+ #include <botan/rsa.h>
+#endif
+
+#if defined(BOTAN_HAS_ECDSA)
+ #include <botan/ecdsa.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_ECDH)
+ #include <botan/ecdh.h>
+#endif
+
+#if defined(BOTAN_HAS_MCELIECE)
+ #include <botan/mceliece.h>
+ #include <botan/mce_kem.h>
+#endif
+
+namespace Botan_CLI {
+
+namespace {
+
+class Timer
+ {
+ public:
+ static uint64_t get_clock() // returns nanoseconds with arbitrary epoch
+ {
+ auto now = std::chrono::high_resolution_clock::now().time_since_epoch();
+ return std::chrono::duration_cast<std::chrono::nanoseconds>(now).count();
+ }
+
+ Timer(const std::string& name, uint64_t event_mult = 1) :
+ m_name(name), m_event_mult(event_mult) {}
+
+ Timer(const std::string& what,
+ const std::string& provider,
+ const std::string& doing,
+ uint64_t event_mult = 1) :
+ m_name(what + (provider.empty() ? provider : " [" + provider + "]")),
+ m_doing(doing),
+ m_event_mult(event_mult) {}
+
+ void start() { stop(); m_timer_start = get_clock(); }
+
+ void stop()
+ {
+ if(m_timer_start)
+ {
+ const uint64_t now = get_clock();
+
+ if(now > m_timer_start)
+ m_time_used += (now - m_timer_start);
+
+ m_timer_start = 0;
+ ++m_event_count;
+ }
+ }
+
+ bool under(std::chrono::milliseconds msec)
+ {
+ return (milliseconds() < msec.count());
+ }
+
+ struct Timer_Scope
+ {
+ public:
+ Timer_Scope(Timer& timer) : m_timer(timer) { m_timer.start(); }
+ ~Timer_Scope() { m_timer.stop(); }
+ private:
+ Timer& m_timer;
+ };
+
+ template<typename F>
+ auto run(F f) -> decltype(f())
+ {
+ Timer_Scope timer(*this);
+ return f();
+ }
+
+ uint64_t 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(); }
+
+ uint64_t event_mult() const { return m_event_mult; }
+ uint64_t events() const { return m_event_count * m_event_mult; }
+ std::string get_name() const { return m_name; }
+ std::string doing() const { return m_doing.empty() ? m_doing : " " + m_doing; }
+ private:
+ std::string m_name, m_doing;
+ uint64_t m_time_used = 0, m_timer_start = 0;
+ uint64_t 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& out, Timer& timer)
+ {
+ const double events_per_second = timer.events() / timer.seconds();
+
+ // use ostringstream to avoid messing with flags on the ostream& itself
+
+ std::ostringstream oss;
+
+ if(timer.event_mult() % 1024 == 0)
+ {
+ // assumed to be a byte count
+ const size_t MiB = 1024*1024;
+
+ const double MiB_total = static_cast<double>(timer.events()) / MiB;
+ const double MiB_per_sec = MiB_total / timer.seconds();
+
+ oss << timer.get_name() << timer.doing() << " "
+ << std::fixed << std::setprecision(3) << MiB_per_sec << " MiB/sec"
+ << " (" << MiB_total << " MiB in " << timer.milliseconds() << " ms)\n";
+ }
+ else
+ {
+ // general event counter
+ oss << timer.get_name() << " "
+ << static_cast<uint64_t>(events_per_second)
+ << timer.doing() << "/sec; "
+ << std::setprecision(2) << std::fixed
+ << timer.ms_per_event() << " ms/op"
+ << " (" << timer.events() << " " << (timer.events() == 1 ? "op" : "ops")
+ << " in " << timer.milliseconds() << " ms)\n";
+ }
+
+ out << oss.str();
+ return out;
+ }
+
+std::vector<std::string> default_benchmark_list()
+ {
+ /*
+ This is not intended to be exhaustive: it just hits the high
+ points of the most interesting or widely used algorithms.
+ */
+
+ return {
+ /* Block ciphers */
+ "AES-128",
+ "AES-192",
+ "AES-256",
+ "Blowfish",
+ "CAST-128",
+ "CAST-256",
+ "DES",
+ "TripleDES",
+ "IDEA",
+ "KASUMI",
+ "Noekeon",
+ "Serpent",
+ "Threefish-512",
+ "Twofish",
+
+ /* 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",
+
+ "ChaCha20Poly1305",
+
+ /* Stream ciphers */
+ "RC4",
+ "Salsa20",
+
+ /* Hashes */
+ "Tiger",
+ "RIPEMD-160",
+ "SHA-160",
+ "SHA-256",
+ "SHA-512",
+ "Skein-512",
+ "Keccak-1600(512)",
+ "Whirlpool",
+
+ /* MACs */
+ "CMAC(AES-128)",
+ "HMAC(SHA-256)",
+
+ /* Misc */
+ "random_prime"
+
+ /* pubkey */
+ "RSA",
+ "DH",
+ "ECDH",
+ "ECDSA",
+ "Curve25519",
+ "McEliece",
+ };
+ }
+
+}
+
+class Benchmark : public Command
+ {
+ public:
+ Benchmark() : Command("bench --msec=1000 --provider= --buf-size=8 *algos") {}
+
+ void go()
+ {
+ std::chrono::milliseconds msec(get_arg_sz("msec"));
+ const size_t buf_size = get_arg_sz("buf-size");
+ const std::string provider = get_arg("provider");
+
+ std::vector<std::string> algos = get_arg_list("algos");
+ const bool using_defaults = (algos.empty());
+ if(using_defaults)
+ algos = default_benchmark_list();
+
+ for(auto algo : algos)
+ {
+ using namespace std::placeholders;
+
+ if(auto enc = Botan::get_cipher_mode(algo, Botan::ENCRYPTION))
+ {
+ auto dec = Botan::get_cipher_mode(algo, Botan::DECRYPTION);
+ bench_cipher_mode(*enc, *dec, msec, buf_size);
+ }
+ else if(Botan::BlockCipher::providers(algo).size() > 0)
+ {
+ bench_providers_of<Botan::BlockCipher>(
+ algo, provider, msec, buf_size,
+ std::bind(&Benchmark::bench_block_cipher, this, _1, _2, _3, _4));
+ }
+ else if(Botan::StreamCipher::providers(algo).size() > 0)
+ {
+ bench_providers_of<Botan::StreamCipher>(
+ algo, provider, msec, buf_size,
+ std::bind(&Benchmark::bench_stream_cipher, this, _1, _2, _3, _4));
+ }
+ else if(Botan::HashFunction::providers(algo).size() > 0)
+ {
+ bench_providers_of<Botan::HashFunction>(
+ algo, provider, msec, buf_size,
+ std::bind(&Benchmark::bench_hash, this, _1, _2, _3, _4));
+ }
+ else if(Botan::MessageAuthenticationCode::providers(algo).size() > 0)
+ {
+ bench_providers_of<Botan::MessageAuthenticationCode>(
+ algo, provider, msec, buf_size,
+ std::bind(&Benchmark::bench_mac, this, _1, _2, _3, _4));
+ }
+#if defined(BOTAN_HAS_RSA)
+ else if(algo == "RSA")
+ {
+ bench_rsa(provider, msec);
+ }
+#endif
+#if defined(BOTAN_HAS_ECDSA)
+ else if(algo == "ECDSA")
+ {
+ bench_ecdsa(provider, msec);
+ }
+#endif
+#if defined(BOTAN_HAS_DIFFIE_HELLMAN)
+ else if(algo == "DH")
+ {
+ bench_dh(provider, msec);
+ }
+#endif
+#if defined(BOTAN_HAS_ECDH)
+ else if(algo == "ECDH")
+ {
+ bench_ecdh(provider, msec);
+ }
+#endif
+#if defined(BOTAN_HAS_CURVE_25519)
+ else if(algo == "Curve25519")
+ {
+ bench_curve25519(provider, msec);
+ }
+#endif
+
+#if defined(BOTAN_HAS_NUMBERTHEORY)
+ else if(algo == "random_prime")
+ {
+ bench_random_prime(msec);
+ }
+#endif
+ else
+ {
+ if(verbose() || !using_defaults)
+ {
+ error_output() << "Unknown algorithm to benchmark '" << algo << "'\n";
+ }
+ }
+ }
+ }
+
+ private:
+
+ Botan::AutoSeeded_RNG m_rng;
+ Botan::RandomNumberGenerator& rng() { return m_rng; }
+
+ template<typename T>
+ using bench_fn = std::function<void (T&,
+ std::string,
+ std::chrono::milliseconds,
+ size_t)>;
+
+ template<typename T>
+ void bench_providers_of(const std::string& algo,
+ const std::string& provider, /* user request, if any */
+ const std::chrono::milliseconds runtime,
+ size_t buf_size,
+ bench_fn<T> bench_one)
+ {
+ for(auto&& prov : T::providers(algo))
+ {
+ if(provider == "" || provider == prov)
+ {
+ auto p = T::create(algo, prov);
+
+ if(p)
+ {
+ bench_one(*p, prov, runtime, buf_size);
+ }
+ }
+ }
+ }
+
+ void bench_block_cipher(Botan::BlockCipher& cipher,
+ const std::string& provider,
+ const std::chrono::milliseconds runtime,
+ size_t buf_size)
+ {
+ Botan::secure_vector<uint8_t> buffer = rng().random_vec(buf_size * 1024);
+
+ Timer encrypt_timer(cipher.name(), provider, "encrypt", buffer.size());
+ Timer decrypt_timer(cipher.name(), provider, "decrypt", buffer.size());
+
+ while(encrypt_timer.under(runtime) && decrypt_timer.under(runtime))
+ {
+ const Botan::SymmetricKey key(rng(), cipher.maximum_keylength());
+
+ cipher.set_key(key);
+ encrypt_timer.run([&] { cipher.encrypt(buffer); });
+ decrypt_timer.run([&] { cipher.decrypt(buffer); });
+ }
+
+ output() << encrypt_timer << decrypt_timer;
+ }
+
+ void bench_stream_cipher(Botan::StreamCipher& cipher,
+ const std::string& provider,
+ const std::chrono::milliseconds runtime,
+ size_t buf_size)
+ {
+ Botan::secure_vector<uint8_t> buffer = rng().random_vec(buf_size * 1024);
+
+ Timer encrypt_timer(cipher.name(), provider, "encrypt", buffer.size());
+
+ while(encrypt_timer.under(runtime))
+ {
+ const Botan::SymmetricKey key(rng(), cipher.maximum_keylength());
+ cipher.set_key(key);
+ encrypt_timer.run([&] { cipher.encipher(buffer); });
+ }
+
+ output() << encrypt_timer;
+ }
+
+ void bench_hash(Botan::HashFunction& hash,
+ const std::string& provider,
+ const std::chrono::milliseconds runtime,
+ size_t buf_size)
+ {
+ Botan::secure_vector<uint8_t> buffer = rng().random_vec(buf_size * 1024);
+
+ Timer timer(hash.name(), provider, "hashing", buffer.size());
+
+ while(timer.under(runtime))
+ {
+ timer.run([&] { hash.update(buffer); });
+ }
+
+ output() << timer;
+ }
+
+ void bench_mac(Botan::MessageAuthenticationCode& mac,
+ const std::string& provider,
+ const std::chrono::milliseconds runtime,
+ size_t buf_size)
+ {
+ Botan::secure_vector<uint8_t> buffer = rng().random_vec(buf_size * 1024);
+
+ Timer timer(mac.name(), provider, "processing", buffer.size());
+
+ while(timer.under(runtime))
+ {
+ const Botan::SymmetricKey key(rng(), mac.maximum_keylength());
+ mac.set_key(key);
+ timer.run([&] { mac.update(buffer); });
+ }
+
+ output() << timer;
+ }
+
+ void bench_cipher_mode(Botan::Cipher_Mode& enc,
+ Botan::Cipher_Mode& dec,
+ const std::chrono::milliseconds runtime,
+ size_t buf_size)
+ {
+ Botan::secure_vector<uint8_t> buffer = rng().random_vec(buf_size * 1024);
+
+ Timer encrypt_timer(enc.name(), "", "encrypt", buffer.size());
+ Timer decrypt_timer(enc.name(), "", "decrypt", buffer.size());
+
+ while(encrypt_timer.under(runtime) && decrypt_timer.under(runtime))
+ {
+ const Botan::SymmetricKey key(rng(), enc.key_spec().maximum_keylength());
+ const Botan::secure_vector<uint8_t> iv = rng().random_vec(enc.default_nonce_length());
+
+ enc.set_key(key);
+ dec.set_key(key);
+
+ enc.start(iv);
+ dec.start(iv);
+
+ // Must run in this order, or AEADs will reject the ciphertext
+ encrypt_timer.run([&] { enc.finish(buffer); });
+ decrypt_timer.run([&] { dec.finish(buffer); });
+ }
+
+ output() << encrypt_timer << decrypt_timer;
+ }
+
+#if defined(BOTAN_HAS_NUMBERTHEORY)
+ void bench_random_prime(const std::chrono::milliseconds runtime)
+ {
+ const size_t coprime = 65537; // simulates RSA key gen
+
+ for(size_t bits : { 1024, 1536 })
+ {
+ Timer genprime_timer("random_prime " + std::to_string(bits));
+ Timer is_prime_timer("is_prime " + std::to_string(bits));
+
+ while(genprime_timer.under(runtime) && is_prime_timer.under(runtime))
+ {
+ const Botan::BigInt p = genprime_timer.run([&] {
+ return Botan::random_prime(rng(), bits, coprime); });
+
+ const bool ok = is_prime_timer.run([&] {
+ return Botan::is_prime(p, rng(), 64, true);
+ });
+
+ if(!ok)
+ {
+ error_output() << "Generated prime " << p
+ << " which then failed primality test";
+ }
+
+ // Now test p+2, p+4, ... which may or may not be prime
+ for(size_t i = 2; i != 64; i += 2)
+ {
+ is_prime_timer.run([&] { Botan::is_prime(p, rng(), 64, true); });
+ }
+ }
+
+ output() << genprime_timer << is_prime_timer;
+ }
+ }
+#endif
+
+#if defined(BOTAN_HAS_PUBLIC_KEY_CRYPTO)
+ void bench_pk_enc(const Botan::Private_Key& key,
+ const std::string& nm,
+ const std::string& provider,
+ const std::string& padding,
+ std::chrono::milliseconds msec)
+ {
+ std::vector<uint8_t> plaintext, ciphertext;
+
+ Botan::PK_Encryptor_EME enc(key, padding, provider);
+ Botan::PK_Decryptor_EME dec(key, padding, provider);
+
+ Timer enc_timer(nm, provider, "encrypt");
+ Timer dec_timer(nm, provider, "decrypt");
+
+ while(enc_timer.under(msec) || dec_timer.under(msec))
+ {
+ // Generate a new random ciphertext to decrypt
+ if(ciphertext.empty() || enc_timer.under(msec))
+ {
+ plaintext = unlock(rng().random_vec(enc.maximum_input_size()));
+ ciphertext = enc_timer.run([&] { return enc.encrypt(plaintext, rng()); });
+ }
+
+ if(dec_timer.under(msec))
+ {
+ auto dec_pt = dec_timer.run([&] { return dec.decrypt(ciphertext); });
+
+ if(dec_pt != plaintext) // sanity check
+ {
+ error_output() << "Bad roundtrip in PK encrypt/decrypt bench\n";
+ }
+ }
+ }
+
+ output() << enc_timer;
+ output() << dec_timer;
+ }
+
+ void bench_pk_ka(const Botan::PK_Key_Agreement_Key& key1,
+ const Botan::PK_Key_Agreement_Key& key2,
+ const std::string& nm,
+ const std::string& provider,
+ const std::string& kdf,
+ std::chrono::milliseconds msec)
+ {
+ Botan::PK_Key_Agreement ka1(key1, kdf /*, provider */);
+ Botan::PK_Key_Agreement ka2(key2, kdf /*, provider */);
+
+ const std::vector<uint8_t> ka1_pub = key1.public_value();
+ const std::vector<uint8_t> ka2_pub = key2.public_value();
+
+ Timer ka_timer(nm, provider, "key agreements");
+
+ while(ka_timer.under(msec))
+ {
+ Botan::SymmetricKey key1 = ka_timer.run([&] { return ka1.derive_key(32, ka2_pub); });
+ Botan::SymmetricKey key2 = ka_timer.run([&] { return ka2.derive_key(32, ka1_pub); });
+
+ if(key1 != key2)
+ {
+ error_output() << "Key agreement mismatch in PK bench\n";
+ }
+ }
+
+ output() << ka_timer;
+ }
+
+ void bench_pk_sig(const Botan::Private_Key& key,
+ const std::string& nm,
+ const std::string& provider,
+ const std::string& padding,
+ std::chrono::milliseconds msec)
+ {
+ std::vector<uint8_t> message, signature, bad_signature;
+
+ Botan::PK_Signer sig(key, padding, Botan::IEEE_1363, provider);
+ Botan::PK_Verifier ver(key, padding, Botan::IEEE_1363, provider);
+
+ Timer sig_timer(nm, provider, "sign");
+ Timer ver_timer(nm, provider, "verify");
+
+ while(ver_timer.under(msec) || sig_timer.under(msec))
+ {
+ if(signature.empty() || sig_timer.under(msec))
+ {
+ /*
+ Length here is kind of arbitrary, but 48 bytes fits into a single
+ hash block so minimizes hashing overhead versus the PK op itself.
+ */
+ message = unlock(rng().random_vec(48));
+
+ signature = sig_timer.run([&] { return sig.sign_message(message, rng()); });
+
+ bad_signature = signature;
+ bad_signature[rng().next_byte() % bad_signature.size()] ^= rng().next_nonzero_byte();
+ }
+
+ if(ver_timer.under(msec))
+ {
+ const bool verified = ver_timer.run([&] {
+ return ver.verify_message(message, signature); });
+
+ if(!verified)
+ {
+ error_output() << "Correct signature rejected in PK signature bench\n";
+ }
+
+ const bool verified_bad = ver_timer.run([&] {
+ return ver.verify_message(message, bad_signature); });
+
+ if(verified_bad)
+ {
+ error_output() << "Bad signature accepted in PK signature bench\n";
+ }
+ }
+ }
+
+ output() << sig_timer;
+ output() << ver_timer;
+ }
+#endif
+
+#if defined(BOTAN_HAS_RSA)
+ void bench_rsa(const std::string& provider,
+ std::chrono::milliseconds msec)
+ {
+ for(size_t keylen : { 1024, 2048, 3072, 4096 })
+ {
+ const std::string nm = "RSA-" + std::to_string(keylen);
+
+ Timer keygen_timer(nm, provider, "keygen");
+
+ std::unique_ptr<Botan::Private_Key> key(keygen_timer.run([&] {
+ return new Botan::RSA_PrivateKey(rng(), keylen);
+ }));
+
+ output() << keygen_timer;
+
+ // Using PKCS #1 padding so OpenSSL provider can play along
+ bench_pk_enc(*key, nm, provider, "EME-PKCS1-v1_5", msec);
+ bench_pk_sig(*key, nm, provider, "EMSA-PKCS1-v1_5(SHA-1)", msec);
+ }
+ }
+#endif
+
+#if defined(BOTAN_HAS_ECDSA)
+ void bench_ecdsa(const std::string& provider,
+ std::chrono::milliseconds msec)
+ {
+ for(std::string grp : { "secp256r1", "secp384r1", "secp521r1" })
+ {
+ const std::string nm = "ECDSA-" + grp;
+
+ Timer keygen_timer(nm, provider, "keygen");
+
+ std::unique_ptr<Botan::Private_Key> key(keygen_timer.run([&] {
+ return new Botan::ECDSA_PrivateKey(rng(), grp);
+ }));
+
+ output() << keygen_timer;
+ bench_pk_sig(*key, nm, provider, "EMSA1(SHA-256)", msec);
+ }
+ }
+#endif
+
+#if defined(BOTAN_HAS_DIFFIE_HELLMAN)
+ void bench_dh(const std::string& provider,
+ std::chrono::milliseconds msec)
+ {
+ for(size_t bits : { 1024, 2048, 3072 })
+ {
+ const std::string grp = "modp/ietf/" + std::to_string(bits);
+ const std::string nm = "DH-" + std::to_string(bits);
+
+ Timer keygen_timer(nm, provider, "keygen");
+
+ std::unique_ptr<Botan::PK_Key_Agreement_Key> key1(keygen_timer.run([&] {
+ return new Botan::DH_PrivateKey(rng(), grp);
+ }));
+ std::unique_ptr<Botan::PK_Key_Agreement_Key> key2(keygen_timer.run([&] {
+ return new Botan::DH_PrivateKey(rng(), grp);
+ }));
+
+ output() << keygen_timer;
+
+ bench_pk_ka(*key1, *key2, nm, provider, "KDF2(SHA-256)", msec);
+ }
+ }
+#endif
+
+#if defined(BOTAN_HAS_ECDH)
+ void bench_ecdh(const std::string& provider,
+ std::chrono::milliseconds msec)
+ {
+ for(std::string grp : { "secp256r1", "secp384r1", "secp521r1" })
+ {
+ const std::string nm = "ECDH-" + grp;
+
+ Timer keygen_timer(nm, provider, "keygen");
+
+ std::unique_ptr<Botan::PK_Key_Agreement_Key> key1(keygen_timer.run([&] {
+ return new Botan::ECDH_PrivateKey(rng(), grp);
+ }));
+ std::unique_ptr<Botan::PK_Key_Agreement_Key> key2(keygen_timer.run([&] {
+ return new Botan::ECDH_PrivateKey(rng(), grp);
+ }));
+
+ output() << keygen_timer;
+
+ bench_pk_ka(*key1, *key2, nm, provider, "KDF2(SHA-256)", msec);
+ }
+ }
+#endif
+
+#if defined(BOTAN_HAS_CURVE_25519)
+ void bench_curve25519(const std::string& provider,
+ std::chrono::milliseconds msec)
+ {
+ const std::string nm = "Curve25519";
+
+ Timer keygen_timer(nm, provider, "keygen");
+
+ std::unique_ptr<Botan::PK_Key_Agreement_Key> key1(keygen_timer.run([&] {
+ return new Botan::Curve25519_PrivateKey(rng());
+ }));
+ std::unique_ptr<Botan::PK_Key_Agreement_Key> key2(keygen_timer.run([&] {
+ return new Botan::Curve25519_PrivateKey(rng());
+ }));
+
+ output() << keygen_timer;
+
+ bench_pk_ka(*key1, *key2, nm, provider, "KDF2(SHA-256)", msec);
+ }
+#endif
+
+
+ };
+
+BOTAN_REGISTER_COMMAND(Benchmark);
+
+}
diff --git a/src/cli/ca.cpp b/src/cli/ca.cpp
deleted file mode 100644
index c62b4f231..000000000
--- a/src/cli/ca.cpp
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
-* (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/cc_enc.cpp b/src/cli/cc_enc.cpp
new file mode 100644
index 000000000..25c9b960b
--- /dev/null
+++ b/src/cli/cc_enc.cpp
@@ -0,0 +1,161 @@
+/*
+* (C) 2014,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "cli.h"
+#include <botan/hex.h>
+
+#if defined(BOTAN_HAS_FPE_FE1) && defined(BOTAN_HAS_PBKDF)
+
+#include <botan/fpe_fe1.h>
+#include <botan/pbkdf.h>
+
+namespace Botan_CLI {
+
+namespace {
+
+uint8_t luhn_checksum(uint64_t cc_number)
+ {
+ uint8_t sum = 0;
+
+ bool alt = false;
+ while(cc_number)
+ {
+ uint8_t 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(uint64_t cc_number)
+ {
+ return (luhn_checksum(cc_number) == 0);
+ }
+
+uint64_t cc_rank(uint64_t cc_number)
+ {
+ // Remove Luhn checksum
+ return cc_number / 10;
+ }
+
+uint64_t cc_derank(uint64_t cc_number)
+ {
+ for(size_t i = 0; i != 10; ++i)
+ {
+ if(luhn_check(cc_number * 10 + i))
+ {
+ return (cc_number * 10 + i);
+ }
+ }
+
+ return 0;
+ }
+
+uint64_t encrypt_cc_number(uint64_t cc_number,
+ const Botan::secure_vector<uint8_t>& key,
+ const std::vector<uint8_t>& tweak)
+ {
+ const Botan::BigInt n = 1000000000000000;
+
+ const uint64_t cc_ranked = cc_rank(cc_number);
+
+ const Botan::BigInt c = Botan::FPE::fe1_encrypt(n, cc_ranked, key, tweak);
+
+ if(c.bits() > 50)
+ throw std::runtime_error("FPE produced a number too large");
+
+ uint64_t enc_cc = 0;
+ for(size_t i = 0; i != 7; ++i)
+ enc_cc = (enc_cc << 8) | c.byte_at(6-i);
+ return cc_derank(enc_cc);
+ }
+
+uint64_t decrypt_cc_number(uint64_t enc_cc,
+ const Botan::secure_vector<uint8_t>& key,
+ const std::vector<uint8_t>& tweak)
+ {
+ const Botan::BigInt n = 1000000000000000;
+
+ const uint64_t cc_ranked = cc_rank(enc_cc);
+
+ const Botan::BigInt c = Botan::FPE::fe1_decrypt(n, cc_ranked, key, tweak);
+
+ if(c.bits() > 50)
+ throw CLI_Error("FPE produced a number too large");
+
+ uint64_t dec_cc = 0;
+ for(size_t i = 0; i != 7; ++i)
+ dec_cc = (dec_cc << 8) | c.byte_at(6-i);
+ return cc_derank(dec_cc);
+ }
+
+}
+
+class CC_Encrypt : public Command
+ {
+ public:
+ CC_Encrypt() : Command("cc_encrypt CC passphrase --tweak=") {}
+
+ void go()
+ {
+ const uint64_t cc_number = std::stoull(get_arg("CC"));
+ const std::vector<uint8_t> tweak = Botan::hex_decode(get_arg("tweak"));
+ const std::string pass = get_arg("passphrase");
+
+ std::unique_ptr<Botan::PBKDF> pbkdf(Botan::PBKDF::create("PBKDF2(SHA-256)"));
+ if(!pbkdf)
+ throw CLI_Error_Unsupported("PBKDF", "PBKDF2(SHA-256)");
+
+ Botan::secure_vector<uint8_t> key =
+ pbkdf->pbkdf_iterations(32, pass,
+ tweak.data(), tweak.size(),
+ 100000);
+
+ output() << encrypt_cc_number(cc_number, key, tweak) << "\n";
+ }
+ };
+
+BOTAN_REGISTER_COMMAND(CC_Encrypt);
+
+class CC_Decrypt : public Command
+ {
+ public:
+ CC_Decrypt() : Command("cc_decrypt CC passphrase --tweak=") {}
+
+ void go() override
+ {
+ const uint64_t cc_number = std::stoull(get_arg("CC"));
+ const std::vector<uint8_t> tweak = Botan::hex_decode(get_arg("tweak"));
+ const std::string pass = get_arg("passphrase");
+
+ std::unique_ptr<Botan::PBKDF> pbkdf(Botan::PBKDF::create("PBKDF2(SHA-256)"));
+ if(!pbkdf)
+ throw CLI_Error_Unsupported("PBKDF", "PBKDF2(SHA-256)");
+
+ Botan::secure_vector<uint8_t> key =
+ pbkdf->pbkdf_iterations(32, pass,
+ tweak.data(), tweak.size(),
+ 100000);
+
+ output() << decrypt_cc_number(cc_number, key, tweak) << "\n";
+ }
+ };
+
+BOTAN_REGISTER_COMMAND(CC_Decrypt);
+
+}
+
+#endif // FPE && PBKDF
diff --git a/src/cli/cert_verify.cpp b/src/cli/cert_verify.cpp
deleted file mode 100644
index 7e03c81ab..000000000
--- a/src/cli/cert_verify.cpp
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
-* 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() < 3)
- {
- std::cout << "Usage: " << args[0] << " subject.pem CA_certificate [CA_certificate ...]"
- << 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/cli.h b/src/cli/cli.h
new file mode 100644
index 000000000..b2be2bd39
--- /dev/null
+++ b/src/cli/cli.h
@@ -0,0 +1,508 @@
+/*
+* (C) 2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_CLI_H__
+#define BOTAN_CLI_H__
+
+#include <botan/build.h>
+#include <botan/parsing.h>
+#include <fstream>
+#include <iostream>
+#include <functional>
+#include <map>
+#include <memory>
+#include <set>
+#include <string>
+#include <vector>
+
+namespace Botan_CLI {
+
+class CLI_Error : public std::runtime_error
+ {
+ public:
+ CLI_Error(const std::string& s) : std::runtime_error(s) {}
+ };
+
+class CLI_IO_Error : public CLI_Error
+ {
+ public:
+ CLI_IO_Error(const std::string& op, const std::string& who) :
+ CLI_Error("Error " + op + " " + who) {}
+ };
+
+class CLI_Usage_Error : public CLI_Error
+ {
+ public:
+ CLI_Usage_Error(const std::string& what) : CLI_Error(what) {}
+ };
+
+/* Thrown eg when a requested feature was compiled out of the library
+ or is not available, eg hashing with
+*/
+class CLI_Error_Unsupported : public CLI_Error
+ {
+ public:
+ CLI_Error_Unsupported(const std::string& what,
+ const std::string& who) :
+ CLI_Error(what + " with '" + who + "' unsupported or not available") {}
+ };
+
+struct CLI_Error_Invalid_Spec : public CLI_Error
+ {
+ public:
+ CLI_Error_Invalid_Spec(const std::string& spec) :
+ CLI_Error("Invalid command spec '" + spec + "'") {}
+ };
+
+class Command
+ {
+ public:
+ /**
+ * The spec string specifies the format of the command line, eg for
+ * a somewhat complicated command:
+ * cmd_name --flag --option1= --option2=opt2val input1 input2 *rest
+ *
+ * By default this is the value returned by help_text()
+ *
+ * The first value is always the command name. Options may appear
+ * in any order. Named arguments are taken from the command line
+ * in the order they appear in the spec.
+ *
+ * --flag can optionally be specified, and takes no value.
+ * Check for it in go() with flag_set()
+ *
+ * --option1 is an option whose default value (if the option
+ * does not appear on the command line) is the empty string.
+ *
+ * --option2 is an option whose default value is opt2val
+ * Read the values in go() using get_arg or get_arg_sz.
+ *
+ * The values input1 and input2 specify named arguments which must
+ * be provided. They are also access via get_arg/get_arg_sz
+ * Because options and arguments for a single command share the same
+ * namespace you can't have a spec like:
+ * cmd --input input
+ * but you hopefully didn't want to do that anyway.
+ *
+ * The leading '*' on '*rest' specifies that all remaining arguments
+ * should be packaged in a list which is available as get_arg_list("rest").
+ * This can only appear on a single value and should be the final
+ * named argument.
+ *
+ * Every command has implicit flags --help, --verbose and implicit
+ * options --output= and --error-output= which override the default
+ * use of std::cout and std::cerr.
+ *
+ * Use of --help is captured in run() and returns help_text().
+ * Use of --verbose can be checked with verbose() or flag_set("verbose")
+ */
+ Command(const std::string& cmd_spec) : m_spec(cmd_spec)
+ {
+ // for checking all spec strings at load time
+ //parse_spec();
+ }
+
+ int run(const std::vector<std::string>& params)
+ {
+ try
+ {
+ // avoid parsing specs except for the command actually running
+ parse_spec();
+
+ std::vector<std::string> args;
+ for(auto&& param : params)
+ {
+ if(param.find("--") == 0)
+ {
+ // option
+ const auto eq = param.find('=');
+
+ if(eq == std::string::npos)
+ {
+ const std::string opt_name = param.substr(2, std::string::npos);
+
+ if(m_spec_flags.count(opt_name) == 0)
+ {
+ if(m_spec_opts.count(opt_name))
+ throw CLI_Usage_Error("Invalid usage of option --" + opt_name +
+ " without value");
+ else
+ throw CLI_Usage_Error("Unknown flag --" + opt_name);
+ }
+
+ m_user_flags.insert(opt_name);
+ }
+ else
+ {
+ const std::string opt_name = param.substr(2, eq - 2);
+ const std::string opt_val = param.substr(eq + 1, std::string::npos);
+
+ if(m_spec_opts.count(opt_name) == 0)
+ {
+ throw CLI_Usage_Error("Unknown option --" + opt_name);
+ }
+
+ m_user_args.insert(std::make_pair(opt_name, opt_val));
+ }
+ }
+ else
+ {
+ // argument
+ args.push_back(param);
+ }
+ }
+
+ size_t arg_i = 0;
+ for(auto&& arg : m_spec_args)
+ {
+ if(arg_i >= args.size())
+ {
+ // not enough arguments
+ throw CLI_Usage_Error("Invalid argument count, got " +
+ std::to_string(args.size()) +
+ " expected " +
+ std::to_string(m_spec_args.size()));
+ }
+
+ m_user_args.insert(std::make_pair(arg, args[arg_i]));
+
+ ++arg_i;
+ }
+
+ if(m_spec_rest.empty())
+ {
+ if(arg_i != args.size())
+ throw CLI_Usage_Error("Too many arguments");
+ }
+ else
+ {
+ m_user_rest.assign(args.begin() + arg_i, args.end());
+ }
+
+ if(flag_set("help"))
+ {
+ output() << help_text() << "\n";
+ return 1;
+ }
+
+ if(m_user_args.count("output"))
+ {
+ m_output_stream.reset(new std::ofstream(get_arg("output")));
+ }
+
+ if(m_user_args.count("error_output"))
+ {
+ m_error_output_stream.reset(new std::ofstream(get_arg("error_output")));
+ }
+
+ // Now insert any defaults for options not supplied by the user
+ for(auto&& opt : m_spec_opts)
+ {
+ if(m_user_args.count(opt.first) == 0)
+ {
+ m_user_args.insert(opt);
+ }
+ }
+
+ this->go();
+ return 0;
+ }
+ catch(CLI_Usage_Error& e)
+ {
+ error_output() << "Usage error: " << e.what() << "\n";
+ error_output() << help_text() << "\n";
+ return 1;
+ }
+ catch(std::exception& e)
+ {
+ error_output() << "Error: " << e.what() << "\n";
+ return 2;
+ }
+ catch(...)
+ {
+ error_output() << "Error: unknown exception\n";
+ return 2;
+ }
+ }
+
+ virtual std::string help_text() const
+ {
+ return "Usage: " + m_spec;
+ }
+
+ std::string cmd_name() const
+ {
+ return m_spec.substr(0, m_spec.find(' '));
+ }
+
+ protected:
+
+ void parse_spec()
+ {
+ const std::vector<std::string> parts = Botan::split_on(m_spec, ' ');
+
+ if(parts.size() == 0)
+ throw CLI_Error_Invalid_Spec(m_spec);
+
+ for(size_t i = 1; i != parts.size(); ++i)
+ {
+ const std::string s = parts[i];
+
+ if(s.empty()) // ?!? (shouldn't happen)
+ throw CLI_Error_Invalid_Spec(m_spec);
+
+ if(s.size() > 2 && s[0] == '-' && s[1] == '-')
+ {
+ // option or flag
+
+ auto eq = s.find('=');
+
+ if(eq == std::string::npos)
+ {
+ m_spec_flags.insert(s.substr(2, std::string::npos));
+ }
+ else
+ {
+ m_spec_opts.insert(std::make_pair(s.substr(2, eq - 2),
+ s.substr(eq + 1, std::string::npos)));
+ }
+ }
+ else if(s[0] == '*')
+ {
+ // rest argument
+ if(m_spec_rest.empty() && s.size() > 2)
+ {
+ m_spec_rest = s.substr(1, std::string::npos);
+ }
+ else
+ {
+ throw CLI_Error_Invalid_Spec(m_spec);
+ }
+ }
+ else
+ {
+ // named argument
+ if(!m_spec_rest.empty()) // rest arg wasn't last
+ throw CLI_Error("Invalid command spec " + m_spec);
+
+ m_spec_args.push_back(s);
+ }
+ }
+
+ m_spec_flags.insert("verbose");
+ m_spec_flags.insert("help");
+ m_spec_opts.insert(std::make_pair("output", ""));
+ m_spec_opts.insert(std::make_pair("error-output", ""));
+ }
+
+ /*
+ * The actual functionality of the cli command implemented in subclas
+ */
+ virtual void go() = 0;
+
+ std::ostream& output()
+ {
+ if(m_output_stream.get())
+ return *m_output_stream;
+ return std::cout;
+ }
+
+ std::ostream& error_output()
+ {
+ if(m_error_output_stream.get())
+ return *m_error_output_stream;
+ return std::cerr;
+ }
+
+ bool verbose() const
+ {
+ return flag_set("verbose");
+ }
+
+ bool flag_set(const std::string& flag_name) const
+ {
+ return m_user_flags.count(flag_name) > 0;
+ }
+
+ std::string get_arg(const std::string& opt_name) const
+ {
+ auto i = m_user_args.find(opt_name);
+ if(i == m_user_args.end())
+ {
+ // this shouldn't occur unless you passed the wrong thing to get_arg
+ throw CLI_Error("Unknown option " + opt_name + " used (program bug)");
+ }
+ return i->second;
+ }
+
+ /*
+ * Like get_arg() but if the argument was not specified or is empty, returns otherwise
+ */
+ std::string get_arg_or(const std::string& opt_name, const std::string& otherwise) const
+ {
+ auto i = m_user_args.find(opt_name);
+ if(i == m_user_args.end() || i->second.empty())
+ {
+ return otherwise;
+ }
+ return i->second;
+ }
+
+ size_t get_arg_sz(const std::string& opt_name) const
+ {
+ const std::string s = get_arg(opt_name);
+
+ try
+ {
+ return static_cast<size_t>(std::stoul(s));
+ }
+ catch(std::exception& e)
+ {
+ throw CLI_Usage_Error("Invalid integer value '" + s + "' for option " + opt_name);
+ }
+ }
+
+ std::vector<std::string> get_arg_list(const std::string& what) const
+ {
+ if(what != m_spec_rest)
+ throw CLI_Error("Unexpected list name '" + what + "'");
+
+ return m_user_rest;
+ }
+
+ /*
+ * Read an entire file into memory and return the contents
+ */
+ std::vector<uint8_t> slurp_file(const std::string& input_file) const
+ {
+ std::vector<uint8_t> buf;
+ auto insert_fn = [&](const uint8_t b[], size_t l)
+ { buf.insert(buf.end(), b, b + l); };
+ this->read_file(input_file, insert_fn);
+ return buf;
+ }
+
+ std::string slurp_file_as_str(const std::string& input_file)
+ {
+ std::string str;
+ auto insert_fn = [&](const uint8_t b[], size_t l)
+ { str.append(reinterpret_cast<const char*>(b), l); };
+ this->read_file(input_file, insert_fn);
+ return str;
+ }
+
+ /*
+ * Read a file calling consumer_fn() with the inputs
+ */
+ void read_file(const std::string& input_file,
+ std::function<void (uint8_t[], size_t)> consumer_fn,
+ size_t buf_size = 0) const
+ {
+ // Any need to support non-binary files here?
+ std::ifstream in(input_file, std::ios::binary);
+
+ // Avoid an infinite loop on --buf-size=0
+ std::vector<uint8_t> buf(buf_size == 0 ? 4096 : buf_size);
+
+ while(in.good())
+ {
+ in.read(reinterpret_cast<char*>(buf.data()), buf.size());
+ consumer_fn(buf.data(), in.gcount());
+ }
+ }
+
+ template<typename Alloc>
+ void write_output_file(const std::string& who,
+ const std::vector<uint8_t, Alloc>& vec) const
+ {
+ write_output_file(who, vec.begin(), vec.size());
+ }
+
+ void write_output_file(const std::string& output_file,
+ const uint8_t buf[], size_t buf_len) const
+ {
+ std::ofstream out(output_file, std::ios::binary);
+ if(!out.good())
+ throw CLI_IO_Error("writing", output_file);
+
+ out.write(reinterpret_cast<const char*>(buf), buf_len);
+ out.close();
+ }
+
+ void write_output_file(const std::string& output_file, const std::string& outstr) const
+ {
+ std::ofstream out(output_file);
+ if(!out.good())
+ throw CLI_IO_Error("writing", output_file);
+
+ out.write(outstr.data(), outstr.size());
+ out.close();
+ }
+
+ private:
+ // set in constructor
+ std::string m_spec;
+
+ // set in parse_spec() from m_spec
+ std::vector<std::string> m_spec_args;
+ std::set<std::string> m_spec_flags;
+ std::map<std::string, std::string> m_spec_opts;
+ std::string m_spec_rest;
+
+ // set in run() from user args
+ std::map<std::string, std::string> m_user_args;
+ std::set<std::string> m_user_flags;
+ std::vector<std::string> m_user_rest;
+
+ std::unique_ptr<std::ofstream> m_output_stream;
+ std::unique_ptr<std::ofstream> m_error_output_stream;
+
+
+ public:
+ // the registry interface:
+
+ static std::map<std::string, std::unique_ptr<Command>>& global_registry()
+ {
+ static std::map<std::string, std::unique_ptr<Command>> g_cmds;
+ return g_cmds;
+ }
+
+ static Command* get_cmd(const std::string& name)
+ {
+ auto& reg = Command::global_registry();
+ auto i = reg.find(name);
+ if(i == reg.end())
+ {
+ return nullptr;
+ }
+
+ return i->second.get();
+ }
+
+ class Registration
+ {
+ public:
+ Registration(Command* cmd)
+ {
+ const std::string name = cmd->cmd_name();
+ auto& reg = Command::global_registry();
+
+ if(reg.count(name) > 0)
+ {
+ throw std::logic_error("Duplicated registration of command " + name);
+ }
+
+ Command::global_registry().insert(std::make_pair(name, std::unique_ptr<Command>(cmd)));
+ }
+ };
+ };
+
+#define BOTAN_REGISTER_COMMAND(CLI_Class) \
+ namespace { Botan_CLI::Command::Registration reg_cmd_ ## CLI_Class(new CLI_Class); }
+
+}
+
+#endif
diff --git a/src/cli/compress.cpp b/src/cli/compress.cpp
index b0d0144d5..5d32267ce 100644
--- a/src/cli/compress.cpp
+++ b/src/cli/compress.cpp
@@ -4,24 +4,30 @@
* Botan is released under the Simplified BSD License (see license.txt)
*/
-#include "apps.h"
+#include "cli.h"
+
+#include <botan/transform.h>
#if defined(BOTAN_HAS_COMPRESSION)
+ #include <botan/compression.h>
+#endif
-#include <botan/compression.h>
-#include <fstream>
+namespace Botan_CLI {
namespace {
-void do_compress(Transform& comp, std::ifstream& in, std::ostream& out)
+void do_compress(Botan::Transform& comp,
+ std::ifstream& in,
+ std::ostream& out,
+ size_t buf_size)
{
- secure_vector<byte> buf;
+ Botan::secure_vector<uint8_t> buf;
comp.start();
while(in.good())
{
- buf.resize(64*1024);
+ buf.resize(buf_size);
in.read(reinterpret_cast<char*>(&buf[0]), buf.size());
buf.resize(in.gcount());
@@ -35,92 +41,114 @@ void do_compress(Transform& comp, std::ifstream& in, std::ostream& out)
out.write(reinterpret_cast<const char*>(&buf[0]), buf.size());
}
-int compress(const std::vector<std::string> &args)
- {
- if(args.size() < 2 || 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);
+class Compress : public Command
+ {
+ public:
+ Compress() : Command("compress --type=gzip --level=6 --buf-size=8192 file") {}
+
+ std::string output_filename(const std::string& input_fsname,
+ const std::string& comp_type)
+ {
+ const std::map<std::string, std::string> suffixes = {
+ { "zlib", "zlib" },
+ { "gzip", "gz" },
+ { "bzip2", "bz2" },
+ { "lzma", "xz" },
+ };
+
+ auto suffix_info = suffixes.find(comp_type);
+ if(suffixes.count(comp_type) == 0)
+ {
+ throw CLI_Error_Unsupported("Compressing", comp_type);
+ }
+
+ return input_fsname + suffix_info->second;
+ }
+
+ void go() override
+ {
+ const std::string comp_type = get_arg("type");
+
+ std::unique_ptr<Botan::Transform> compress;
- if(!in.good())
- {
- std::cout << "Couldn't read " << in_file << std::endl;
- return 1;
- }
+#if defined(BOTAN_HAS_COMPRESSION)
+ compress.reset(Botan::make_compressor(comp_type, get_arg_sz("level")));
+#endif
- const std::string suffix = args.size() >= 3 ? args[2] : "gz";
- const size_t level = args.size() >= 4 ? to_u32bit(args[3]) : 9;
+ if(!compress)
+ {
+ throw CLI_Error_Unsupported("Compression", comp_type);
+ }
- std::unique_ptr<Transform> compress(make_compressor(suffix, level));
+ const std::string in_file = get_arg("file");
+ std::ifstream in(in_file);
- if(!compress)
- {
- std::cout << suffix << " compression not supported" << std::endl;
- return 1;
- }
+ if(!in.good())
+ {
+ throw CLI_IO_Error("reading", in_file);
+ }
- const std::string out_file = in_file + "." + suffix;
- std::ofstream out(out_file);
+ const std::string out_file = output_filename(in_file, comp_type);
+ std::ofstream out(out_file);
+ if(!in.good())
+ {
+ throw CLI_IO_Error("writing", out_file);
+ }
- do_compress(*compress, in, out);
+ do_compress(*compress, in, out, get_arg_sz("buf_size"));
+ }
+ };
- return 0;
- }
+BOTAN_REGISTER_COMMAND(Compress);
-void parse_extension(const std::string& in_file,
- std::string& out_file,
- std::string& suffix)
+class Decompress : public Command
{
- 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 + "'");
+ public:
+ Decompress() : Command("decompress file") {}
- out_file = in_file.substr(0, last_dot);
- suffix = in_file.substr(last_dot+1, std::string::npos);
- }
+ 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 CLI_Error("No extension detected in filename '" + in_file + "'");
-int uncompress(const std::vector<std::string> &args)
- {
- if(args.size() != 2)
- {
- std::cout << "Usage: " << args[0] << " <file>" << std::endl;
- return 1;
- }
+ out_file = in_file.substr(0, last_dot);
+ suffix = in_file.substr(last_dot+1, std::string::npos);
+ }
- const std::string in_file = args[1];
- std::ifstream in(in_file);
+ void go() override
+ {
+ const std::string in_file = get_arg("file");
+ std::string out_file, suffix;
+ parse_extension(in_file, out_file, suffix);
- if(!in.good())
- {
- std::cout << "Couldn't read '" << args[1] << "'" << std::endl;
- return 1;
- }
+ std::ifstream in(in_file);
- std::string out_file, suffix;
- parse_extension(in_file, out_file, suffix);
+ if(!in.good())
+ throw CLI_IO_Error("reading", in_file);
- std::ofstream out(out_file);
+ std::unique_ptr<Botan::Transform> decompress;
- std::unique_ptr<Transform> decompress(make_decompressor(suffix));
+#if defined(BOTAN_HAS_COMPRESSION)
+ decompress.reset(Botan::make_decompressor(suffix));
+#endif
- if(!decompress)
- {
- std::cout << suffix << " decompression not supported" << std::endl;
- return 1;
- }
+ if(!decompress)
+ throw CLI_Error_Unsupported("Decompression", suffix);
- do_compress(*decompress, in, out);
+ std::ofstream out(out_file);
+ if(!out.good())
+ throw CLI_IO_Error("writing", out_file);
- return 0;
- }
+ do_compress(*decompress, in, out, get_arg_sz("buf_size"));
-REGISTER_APP(compress);
-REGISTER_APP(uncompress);
+ }
+ };
-}
+BOTAN_REGISTER_COMMAND(Decompress);
-#endif // BOTAN_HAS_COMPRESSION
+}
diff --git a/src/cli/credentials.h b/src/cli/credentials.h
index 06349657d..11bfd3de1 100644
--- a/src/cli/credentials.h
+++ b/src/cli/credentials.h
@@ -23,7 +23,7 @@ inline bool value_exists(const std::vector<std::string>& vec,
return false;
}
-class Basic_Credentials_Manager : public Credentials_Manager
+class Basic_Credentials_Manager : public Botan::Credentials_Manager
{
public:
Basic_Credentials_Manager()
@@ -31,20 +31,20 @@ class Basic_Credentials_Manager : public Credentials_Manager
load_certstores();
}
- Basic_Credentials_Manager(RandomNumberGenerator& rng,
+ Basic_Credentials_Manager(Botan::RandomNumberGenerator& rng,
const std::string& server_crt,
const std::string& server_key)
{
Certificate_Info cert;
- cert.key.reset(PKCS8::load_key(server_key, rng));
+ cert.key.reset(Botan::PKCS8::load_key(server_key, rng));
- DataSource_Stream in(server_crt);
+ Botan::DataSource_Stream in(server_crt);
while(!in.end_of_data())
{
try
{
- cert.certs.push_back(X509_Certificate(in));
+ cert.certs.push_back(Botan::X509_Certificate(in));
}
catch(std::exception& e)
{
@@ -66,7 +66,7 @@ class Basic_Credentials_Manager : public Credentials_Manager
for(auto&& path : paths)
{
- std::shared_ptr<Certificate_Store> cs(new Certificate_Store_In_Memory(path));
+ std::shared_ptr<Botan::Certificate_Store> cs(new Botan::Certificate_Store_In_Memory(path));
m_certstores.push_back(cs);
}
}
@@ -95,7 +95,7 @@ class Basic_Credentials_Manager : public Credentials_Manager
void verify_certificate_chain(
const std::string& type,
const std::string& purported_hostname,
- const std::vector<X509_Certificate>& cert_chain) override
+ const std::vector<Botan::X509_Certificate>& cert_chain) override
{
try
{
@@ -110,7 +110,7 @@ class Basic_Credentials_Manager : public Credentials_Manager
}
}
- std::vector<X509_Certificate> cert_chain(
+ std::vector<Botan::X509_Certificate> cert_chain(
const std::vector<std::string>& algos,
const std::string& type,
const std::string& hostname) override
@@ -128,12 +128,12 @@ class Basic_Credentials_Manager : public Credentials_Manager
return i.certs;
}
- return std::vector<X509_Certificate>();
+ return std::vector<Botan::X509_Certificate>();
}
- Private_Key* private_key_for(const X509_Certificate& cert,
- const std::string& /*type*/,
- const std::string& /*context*/) override
+ Botan::Private_Key* private_key_for(const Botan::X509_Certificate& cert,
+ const std::string& /*type*/,
+ const std::string& /*context*/) override
{
for(auto&& i : m_creds)
{
@@ -147,12 +147,12 @@ class Basic_Credentials_Manager : public Credentials_Manager
private:
struct Certificate_Info
{
- std::vector<X509_Certificate> certs;
- std::shared_ptr<Private_Key> key;
+ std::vector<Botan::X509_Certificate> certs;
+ std::shared_ptr<Botan::Private_Key> key;
};
std::vector<Certificate_Info> m_creds;
- std::vector<std::shared_ptr<Certificate_Store>> m_certstores;
+ std::vector<std::shared_ptr<Botan::Certificate_Store>> m_certstores;
};
#endif
diff --git a/src/cli/dl_group.cpp b/src/cli/dl_group.cpp
deleted file mode 100644
index e9a4f3fd4..000000000
--- a/src/cli/dl_group.cpp
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
-* (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
deleted file mode 100644
index 03aede585..000000000
--- a/src/cli/dsa_sign.cpp
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
-* (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
deleted file mode 100644
index 13f71b59d..000000000
--- a/src/cli/dsa_ver.cpp
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
-* (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
deleted file mode 100644
index 52da46153..000000000
--- a/src/cli/factor.cpp
+++ /dev/null
@@ -1,160 +0,0 @@
-/*
-* (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
deleted file mode 100644
index 10b3096bf..000000000
--- a/src/cli/fpe.cpp
+++ /dev/null
@@ -1,153 +0,0 @@
-/*
-* (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>
-
-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
deleted file mode 100644
index 35adb9711..000000000
--- a/src/cli/fuzzer.cpp
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
-* (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
deleted file mode 100644
index fde3b5ce9..000000000
--- a/src/cli/getopt.cpp
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
-* (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];
-
- // FIXME: cli app must manually query if user requested help
- // in order to be able to stop the cpp. At the moment e.g.
- // `./botan keygen --help` generates keys.
- if(arg == "help" || arg == "--help" || arg == "-h")
- {
- help(std::cout, appname);
- return;
- }
-
- 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
deleted file mode 100644
index 21a12b1dc..000000000
--- a/src/cli/getopt.h
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
-* (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 <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
deleted file mode 100644
index 9dd07d488..000000000
--- a/src/cli/hash.cpp
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
-* (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] << " algorithm filename [filename ...]" << 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
deleted file mode 100644
index 3cfd0ef61..000000000
--- a/src/cli/implementation/speed.h
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
-* (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
deleted file mode 100644
index a7a344bef..000000000
--- a/src/cli/implementation/speed_prime.cpp
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
-* (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
deleted file mode 100644
index 2ff49bd15..000000000
--- a/src/cli/implementation/speed_public_key.cpp
+++ /dev/null
@@ -1,888 +0,0 @@
-/*
-* (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
deleted file mode 100644
index 2db5cdd70..000000000
--- a/src/cli/implementation/speed_transform.cpp
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
-* (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
deleted file mode 100644
index 14e55316b..000000000
--- a/src/cli/implementation/timer.cpp
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
-* (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
deleted file mode 100644
index ac5bd5cef..000000000
--- a/src/cli/implementation/timer.h
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
-* (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
deleted file mode 100644
index 71fec730b..000000000
--- a/src/cli/is_prime.cpp
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
-* (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
deleted file mode 100644
index 168b27a4a..000000000
--- a/src/cli/keygen.cpp
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
-* (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
index 8d229ce0e..f054e6005 100644
--- a/src/cli/main.cpp
+++ b/src/cli/main.cpp
@@ -1,187 +1,59 @@
/*
-* (C) 2009,2014 Jack Lloyd
+* (C) 2009,2014,2015 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 "cli.h"
#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"
+#include <botan/internal/stl_util.h>
+#include <iterator>
+#include <sstream>
namespace {
-int help(const std::vector<std::string> &args)
+std::string main_help()
{
- std::cout << "Usage: " << args[0] << " [subcommand] [subcommand-options]" << std::endl;
+ const std::set<std::string> avail_commands =
+ Botan::map_keys_as_set(Botan_CLI::Command::global_registry());
- std::set<std::string> apps = AppRegistrations::instance().all_appnames();
+ std::ostringstream oss;
- std::cout << "Available commands:" << std::endl;
+ oss << "Usage: botan <cmd> <cmd-options>\n";
+ oss << "Available commands: ";
+ std::copy(avail_commands.begin(),
+ avail_commands.end(),
+ std::ostream_iterator<std::string>(oss, " "));
+ oss << "\n";
- 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;
+ return oss.str();
}
-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);
+ std::cerr << Botan::runtime_version_check(BOTAN_VERSION_MAJOR,
+ BOTAN_VERSION_MINOR,
+ BOTAN_VERSION_PATCH);
- 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()));
+ const std::string cmd_name = (argc <= 1) ? "help" : argv[1];
- std::cerr << "Unknown command " << cmd << std::endl;
- return help(args);
- }
- catch(std::exception& e)
+ if(cmd_name == "help" || cmd_name == "--help")
{
- std::cerr << e.what() << std::endl;
+ std::cout << main_help();
return 1;
}
- catch(...)
+
+ Botan_CLI::Command* cmd = Botan_CLI::Command::get_cmd(cmd_name);
+
+ if(!cmd)
{
- std::cerr << "Unknown exception caught" << std::endl;
+ std::cout << "Unknown command " << cmd_name << " (try --help)\n";
return 1;
}
- return 0;
+ std::vector<std::string> args(argv + 2, argv + argc);
+ return cmd->run(args);
}
diff --git a/src/cli/math.cpp b/src/cli/math.cpp
new file mode 100644
index 000000000..c6f40e785
--- /dev/null
+++ b/src/cli/math.cpp
@@ -0,0 +1,188 @@
+/*
+* (C) 2009,2010,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "cli.h"
+
+#if defined(BOTAN_HAS_NUMBERTHEORY)
+
+#include <botan/reducer.h>
+#include <botan/numthry.h>
+#include <botan/auto_rng.h>
+#include <iterator>
+
+namespace Botan_CLI {
+
+class Gen_Prime : public Command
+ {
+ public:
+ Gen_Prime() : Command("gen_prime --count=1 bits") {}
+
+ void go() override
+ {
+ Botan::AutoSeeded_RNG rng;
+
+ const size_t bits = get_arg_sz("bits");
+ const size_t cnt = get_arg_sz("count");
+
+ for(size_t i = 0; i != cnt; ++i)
+ {
+ const Botan::BigInt p = Botan::random_prime(rng, bits);
+ output() << p << "\n";
+ }
+ }
+ };
+
+BOTAN_REGISTER_COMMAND(Gen_Prime);
+
+class Is_Prime : public Command
+ {
+ public:
+ Is_Prime() : Command("is_prime --prob=56 n") {}
+
+ void go() override
+ {
+ Botan::BigInt n(get_arg("n"));
+ const size_t prob = get_arg_sz("prob");
+ Botan::AutoSeeded_RNG rng;
+ const bool prime = Botan::is_prime(n, rng, prob);
+
+ output() << n << " is " << (prime ? "probably prime" : "composite") << "\n";
+ }
+ };
+
+BOTAN_REGISTER_COMMAND(Is_Prime);
+
+/*
+* Factor integers using a combination of trial division by small
+* primes, and Pollard's Rho algorithm
+*/
+class Factor : public Command
+ {
+ public:
+ Factor() : Command("factor n") {}
+
+ void go() override
+ {
+ Botan::BigInt n(get_arg("n"));
+
+ Botan::AutoSeeded_RNG rng;
+
+ std::vector<Botan::BigInt> factors = factorize(n, rng);
+ std::sort(factors.begin(), factors.end());
+
+ output() << n << ": ";
+ std::copy(factors.begin(),
+ factors.end(),
+ std::ostream_iterator<Botan::BigInt>(output(), " "));
+ output() << std::endl;
+ }
+
+ private:
+
+ std::vector<Botan::BigInt> factorize(const Botan::BigInt& n_in,
+ Botan::RandomNumberGenerator& rng)
+ {
+ Botan::BigInt n = n_in;
+ std::vector<Botan::BigInt> factors = remove_small_factors(n);
+
+ while(n != 1)
+ {
+ if(Botan::is_prime(n, rng))
+ {
+ factors.push_back(n);
+ break;
+ }
+
+ Botan::BigInt a_factor = 0;
+ while(a_factor == 0)
+ a_factor = rho(n, rng);
+
+ std::vector<Botan::BigInt> rho_factored = factorize(a_factor, rng);
+ for(size_t j = 0; j != rho_factored.size(); j++)
+ factors.push_back(rho_factored[j]);
+
+ n /= a_factor;
+ }
+
+ return factors;
+ }
+
+ /*
+ * 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.
+ */
+ Botan::BigInt rho(const Botan::BigInt& n, Botan::RandomNumberGenerator& rng)
+ {
+ Botan::BigInt x = Botan::BigInt::random_integer(rng, 0, n-1);
+ Botan::BigInt y = x;
+ Botan::BigInt d = 0;
+
+ Botan::Modular_Reducer mod_n(n);
+
+ size_t i = 1, k = 2;
+ while(true)
+ {
+ i++;
+
+ if(i == 0) // overflow, bail out
+ break;
+
+ x = mod_n.multiply((x + 1), x);
+
+ d = Botan::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<Botan::BigInt> remove_small_factors(Botan::BigInt& n)
+ {
+ std::vector<Botan::BigInt> factors;
+
+ while(n.is_even())
+ {
+ factors.push_back(2);
+ n /= 2;
+ }
+
+ for(size_t j = 0; j != Botan::PRIME_TABLE_SIZE; j++)
+ {
+ uint16_t prime = Botan::PRIMES[j];
+ if(n < prime)
+ break;
+
+ Botan::BigInt x = Botan::gcd(n, prime);
+
+ if(x != 1)
+ {
+ n /= x;
+
+ while(x != 1)
+ {
+ x /= prime;
+ factors.push_back(prime);
+ }
+ }
+ }
+
+ return factors;
+ }
+ };
+
+BOTAN_REGISTER_COMMAND(Factor);
+
+}
+
+#endif
diff --git a/src/cli/mce.cpp b/src/cli/mce.cpp
deleted file mode 100644
index 226f21e9c..000000000
--- a/src/cli/mce.cpp
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
-* (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
deleted file mode 100644
index e5b42b076..000000000
--- a/src/cli/ocsp.cpp
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
-* (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
deleted file mode 100644
index 710020666..000000000
--- a/src/cli/pkcs10.cpp
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
-* (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
deleted file mode 100644
index 7bc1c2561..000000000
--- a/src/cli/pkcs8.cpp
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
-* (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
deleted file mode 100644
index c7c9d1ffe..000000000
--- a/src/cli/prime.cpp
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
-* (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 || args.size() > 3)
- {
- 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/pubkey.cpp b/src/cli/pubkey.cpp
new file mode 100644
index 000000000..35d50592f
--- /dev/null
+++ b/src/cli/pubkey.cpp
@@ -0,0 +1,278 @@
+/*
+* (C) 2010,2014,2015 Jack Lloyd
+* (C) 2015 René Korthaus
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "cli.h"
+
+#if defined(BOTAN_HAS_PUBLIC_KEY_CRYPTO)
+
+#include <botan/auto_rng.h>
+#include <botan/base64.h>
+
+#include <botan/pk_keys.h>
+#include <botan/pkcs8.h>
+#include <botan/pubkey.h>
+
+#if defined(BOTAN_HAS_DL_GROUP)
+ #include <botan/dl_group.h>
+#endif
+
+#if defined(BOTAN_HAS_RSA)
+ #include <botan/rsa.h>
+#endif
+
+#if defined(BOTAN_HAS_ECDSA)
+ #include <botan/ecdsa.h>
+#endif
+
+#if defined(BOTAN_HAS_CURVE_25519)
+ #include <botan/curve25519.h>
+#endif
+
+#if defined(BOTAN_HAS_MCELIECE)
+ #include <botan/mceliece.h>
+#endif
+
+namespace Botan_CLI {
+
+class PK_Keygen : public Command
+ {
+ public:
+ PK_Keygen() : Command("keygen --algo=RSA --params= --passphrase= --pbe= --pbe-millis=300") {}
+
+ std::unique_ptr<Botan::Private_Key> do_keygen(const std::string& algo,
+ const std::string& params,
+ Botan::RandomNumberGenerator& rng)
+ {
+ typedef std::function<std::unique_ptr<Botan::Private_Key> (std::string)> gen_fn;
+ std::map<std::string, gen_fn> generators;
+
+#if defined(BOTAN_HAS_RSA)
+ generators["RSA"] = [&rng](std::string param) -> std::unique_ptr<Botan::Private_Key> {
+ if(param.empty())
+ param = "2048";
+ return std::unique_ptr<Botan::Private_Key>(
+ new Botan::RSA_PrivateKey(rng, Botan::to_u32bit(param)));
+ };
+#endif
+
+#if defined(BOTAN_HAS_ECDSA)
+ generators["ECDSA"] = [&rng](std::string param) {
+ if(param.empty())
+ param = "secp256r1";
+ Botan::EC_Group grp(param);
+ return std::unique_ptr<Botan::Private_Key>(
+ new Botan::ECDSA_PrivateKey(rng, grp));
+ };
+#endif
+
+#if defined(BOTAN_HAS_CURVE_25519)
+ generators["Curve25519"] = [&rng](std::string /*ignored*/) {
+ return std::unique_ptr<Botan::Private_Key>(
+ new Botan::Curve25519_PrivateKey(rng));
+ };
+#endif
+
+#if defined(BOTAN_HAS_MCELIECE)
+ generators["McEliece"] = [&rng](std::string param) {
+ if(param.empty())
+ param = "2280,45";
+ std::vector<std::string> param_parts = Botan::split_on(param, ',');
+ if(param_parts.size() != 2)
+ throw CLI_Usage_Error("Bad McEliece parameters " + param);
+ return std::unique_ptr<Botan::Private_Key>(
+ new Botan::McEliece_PrivateKey(rng,
+ Botan::to_u32bit(param_parts[0]),
+ Botan::to_u32bit(param_parts[1])));
+ };
+#endif
+
+ auto gen = generators.find(algo);
+ if(gen == generators.end())
+ {
+ throw CLI_Error_Unsupported("keygen", algo);
+ }
+
+ return gen->second(params);
+ }
+
+ void go() override
+ {
+ Botan::AutoSeeded_RNG rng;
+ std::unique_ptr<Botan::Private_Key> key(do_keygen(get_arg("algo"), get_arg("params"), rng));
+
+ const std::string pass = get_arg("passphrase");
+
+ if(pass.empty())
+ {
+ output() << Botan::PKCS8::PEM_encode(*key);
+ }
+ else
+ {
+ output() << Botan::PKCS8::PEM_encode(*key,
+ rng,
+ pass,
+ std::chrono::milliseconds(get_arg_sz("pbe-millis")),
+ get_arg("pbe"));
+ }
+ }
+ };
+
+BOTAN_REGISTER_COMMAND(PK_Keygen);
+
+namespace {
+
+std::string algo_default_emsa(const std::string& key)
+ {
+ if(key == "RSA")
+ return "EMSA4"; // PSS
+ else if(key == "ECDSA" || key == "DSA")
+ return "EMSA1";
+ else if(key == "RW")
+ return "EMSA2";
+ else
+ return "EMSA1";
+ }
+
+}
+
+class PK_Sign : public Command
+ {
+ public:
+ PK_Sign() : Command("sign --passphrase= --hash=SHA-256 --emsa= key file") {}
+
+ void go() override
+ {
+ Botan::AutoSeeded_RNG rng;
+
+ std::unique_ptr<Botan::Private_Key> key(Botan::PKCS8::load_key(get_arg("key"),
+ rng,
+ get_arg("passphrase")));
+
+ if(!key)
+ throw CLI_Error("Unable to load private key");
+
+ const std::string sig_padding =
+ get_arg_or("emsa", algo_default_emsa(key->algo_name())) + "(" + get_arg("hash") + ")";
+
+ Botan::PK_Signer signer(*key, sig_padding);
+
+ this->read_file(get_arg("file"),
+ [&signer](const uint8_t b[], size_t l) { signer.update(b, l); });
+
+ output() << Botan::base64_encode(signer.signature(rng)) << "\n";
+ }
+ };
+
+BOTAN_REGISTER_COMMAND(PK_Sign);
+
+class PK_Verify : public Command
+ {
+ public:
+ PK_Verify() : Command("verify --hash=SHA-256 --emsa= pubkey file signature") {}
+
+ void go() override
+ {
+ std::unique_ptr<Botan::Public_Key> key(Botan::X509::load_key(get_arg("pubkey")));
+ if(!key)
+ throw CLI_Error("Unable to load public key");
+
+ const std::string sig_padding =
+ get_arg_or("emsa", algo_default_emsa(key->algo_name())) + "(" + get_arg("hash") + ")";
+
+ Botan::PK_Verifier verifier(*key, sig_padding);
+ this->read_file(get_arg("file"),
+ [&verifier](const uint8_t b[], size_t l) { verifier.update(b, l); });
+
+ const Botan::secure_vector<uint8_t> signature =
+ Botan::base64_decode(this->slurp_file_as_str(get_arg("signature")));
+
+ const bool valid = verifier.check_signature(signature);
+
+ output() << "Signature is " << (valid ? "valid" : "invalid") << "\n";
+ }
+ };
+
+BOTAN_REGISTER_COMMAND(PK_Verify);
+
+#if defined(BOTAN_HAS_DL_GROUP)
+
+class Gen_DL_Group : public Command
+ {
+ public:
+ Gen_DL_Group() : Command("gen_dl_group --pbits=1024 --qbits=0 --type=subgroup") {}
+
+ void go() override
+ {
+ Botan::AutoSeeded_RNG rng;
+
+ const size_t pbits = get_arg_sz("pbits");
+
+ const std::string type = get_arg("type");
+
+ if(type == "strong")
+ {
+ Botan::DL_Group grp(rng, Botan::DL_Group::Strong, pbits);
+ output() << grp.PEM_encode(Botan::DL_Group::ANSI_X9_42);
+ }
+ else if(type == "subgroup")
+ {
+ Botan::DL_Group grp(rng, Botan::DL_Group::Prime_Subgroup, pbits, get_arg_sz("qbits"));
+ output() << grp.PEM_encode(Botan::DL_Group::ANSI_X9_42);
+ }
+ else
+ throw CLI_Usage_Error("Invalid DL type '" + type + "'");
+ }
+ };
+
+BOTAN_REGISTER_COMMAND(Gen_DL_Group);
+
+#endif
+
+class PKCS8_Tool : public Command
+ {
+ public:
+ PKCS8_Tool() : Command("pkcs8 --pass-in= --pub-out --pass-out= --pbe= --pbe-millis=300 key") {}
+
+ void go() override
+ {
+ Botan::AutoSeeded_RNG rng;
+
+ std::unique_ptr<Botan::Private_Key> key(Botan::PKCS8::load_key(get_arg("key"),
+ rng,
+ get_arg("pass-in")));
+
+ if(flag_set("pub-out"))
+ {
+ output() << Botan::X509::PEM_encode(*key);
+ }
+ else
+ {
+ const std::string pass = get_arg("pass-out");
+
+ if(pass.empty())
+ {
+ output() << Botan::PKCS8::PEM_encode(*key);
+ }
+ else
+ {
+ output() << Botan::PKCS8::PEM_encode(*key,
+ rng,
+ pass,
+ std::chrono::milliseconds(get_arg_sz("pbe-millis")),
+ get_arg("pbe"));
+ }
+
+ }
+
+ }
+ };
+
+BOTAN_REGISTER_COMMAND(PKCS8_Tool);
+
+}
+
+#endif
diff --git a/src/cli/rng.cpp b/src/cli/rng.cpp
deleted file mode 100644
index ef7b3dc6b..000000000
--- a/src/cli/rng.cpp
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
-* (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() < 2 || args.size() > 3)
- {
- 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
deleted file mode 100644
index dea18420e..000000000
--- a/src/cli/self_sig.cpp
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
-* (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
deleted file mode 100644
index e8d30c6f1..000000000
--- a/src/cli/speed.cpp
+++ /dev/null
@@ -1,214 +0,0 @@
-/*
-* (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
index 7f74e1a37..c13e9019e 100644
--- a/src/cli/tls_client.cpp
+++ b/src/cli/tls_client.cpp
@@ -1,19 +1,19 @@
/*
-* (C) 2014 Jack Lloyd
+* (C) 2014,2015 Jack Lloyd
*
* Botan is released under the Simplified BSD License (see license.txt)
*/
-#include "apps.h"
+#include "cli.h"
#if defined(BOTAN_HAS_TLS) && defined(BOTAN_TARGET_OS_HAS_SOCKETS)
#include <botan/tls_client.h>
-#include <botan/pkcs8.h>
+#include <botan/auto_rng.h>
#include <botan/hex.h>
#if defined(BOTAN_HAS_TLS_SQLITE3_SESSION_MANAGER)
- #include <botan/tls_session_manager_sqlite.h>
+#include <botan/tls_session_manager_sqlite.h>
#endif
#include <string>
@@ -34,260 +34,258 @@
#include "credentials.h"
-using namespace Botan;
+namespace Botan_CLI {
-using namespace std::placeholders;
+class TLS_Client : public Command
+ {
+ public:
+ TLS_Client() : Command("tls_client host --port=443 --type=tcp "
+ "--session-db= --session-db-pass= --next-protocols=") {}
-namespace {
+ void go() override
+ {
+ Botan::AutoSeeded_RNG rng;
+ Botan::TLS::Policy policy; // TODO read from a file
-int connect_to_host(const std::string& host, u16bit port, bool tcp)
- {
- hostent* host_addr = ::gethostbyname(host.c_str());
+ // TODO client cert auth
- if(!host_addr)
- throw std::runtime_error("gethostbyname failed for " + host);
+ std::unique_ptr<Botan::TLS::Session_Manager> session_mgr;
- if(host_addr->h_addrtype != AF_INET) // FIXME
- throw std::runtime_error(host + " has IPv6 address, not supported");
+#if defined(BOTAN_HAS_TLS_SQLITE3_SESSION_MANAGER)
+ const std::string sessions_passphrase = get_arg("session-db-pass");
+ const std::string sessions_db = get_arg("session-db");
- int type = tcp ? SOCK_STREAM : SOCK_DGRAM;
+ if(!sessions_db.empty())
+ {
+ session_mgr.reset(new Botan::TLS::Session_Manager_SQLite(sessions_passphrase, rng, sessions_db));
+ }
+#endif
+ if(!session_mgr)
+ {
+ session_mgr.reset(new Botan::TLS::Session_Manager_In_Memory(rng));
+ }
- int fd = ::socket(PF_INET, type, 0);
- if(fd == -1)
- throw std::runtime_error("Unable to acquire socket");
+ Basic_Credentials_Manager creds;
- sockaddr_in socket_info;
- ::memset(&socket_info, 0, sizeof(socket_info));
- socket_info.sin_family = AF_INET;
- socket_info.sin_port = htons(port);
+ const std::string host = get_arg("host");
+ const uint16_t port = get_arg_sz("port");
+ const std::string transport = get_arg("type");
- ::memcpy(&socket_info.sin_addr,
- host_addr->h_addr,
- host_addr->h_length);
+ if(transport != "tcp" && transport != "udp")
+ throw CLI_Usage_Error("Invalid transport type '" + transport + "' for TLS");
- socket_info.sin_addr = *reinterpret_cast<struct in_addr*>(host_addr->h_addr); // FIXME
+ const bool use_tcp = (transport == "tcp");
- if(::connect(fd, (sockaddr*)&socket_info, sizeof(struct sockaddr)) != 0)
- {
- ::close(fd);
- throw std::runtime_error("connect failed");
- }
+ const std::vector<std::string> protocols_to_offer = Botan::split_on("next-protocols", ',');
- return fd;
- }
+ int sockfd = connect_to_host(host, port, use_tcp);
-bool handshake_complete(const TLS::Session& session)
- {
- std::cout << "Handshake complete, " << session.version().to_string()
- << " using " << session.ciphersuite().to_string() << std::endl;
+ using namespace std::placeholders;
- if(!session.session_id().empty())
- std::cout << "Session ID " << hex_encode(session.session_id()) << std::endl;
+ auto socket_write =
+ use_tcp ?
+ std::bind(stream_socket_write, sockfd, _1, _2) :
+ std::bind(dgram_socket_write, sockfd, _1, _2);
- if(!session.session_ticket().empty())
- std::cout << "Session ticket " << hex_encode(session.session_ticket()) << std::endl;
+ auto version = policy.latest_supported_version(!use_tcp);
- return true;
- }
+ Botan::TLS::Client client(socket_write,
+ std::bind(&TLS_Client::process_data, this, _1, _2),
+ std::bind(&TLS_Client::alert_received, this, _1, _2, _3),
+ std::bind(&TLS_Client::handshake_complete, this, _1),
+ *session_mgr,
+ creds,
+ policy,
+ rng,
+ Botan::TLS::Server_Information(host, port),
+ version,
+ protocols_to_offer);
-void dgram_socket_write(int sockfd, const byte buf[], size_t length)
- {
- int r = send(sockfd, buf, length, MSG_NOSIGNAL);
+ bool first_active = true;
- if(r == -1)
- throw std::runtime_error("Socket write failed errno=" + std::to_string(errno));
- }
+ while(!client.is_closed())
+ {
+ fd_set readfds;
+ FD_ZERO(&readfds);
+ FD_SET(sockfd, &readfds);
-void stream_socket_write(int sockfd, const byte buf[], size_t length)
- {
- size_t offset = 0;
+ if(client.is_active())
+ {
+ FD_SET(STDIN_FILENO, &readfds);
+ if(first_active && !protocols_to_offer.empty())
+ {
+ std::string app = client.application_protocol();
+ if(app != "")
+ output() << "Server choose protocol: " << client.application_protocol() << "\n";
+ first_active = false;
+ }
+ }
- while(length)
- {
- ssize_t sent = ::send(sockfd, (const char*)buf + offset,
- length, MSG_NOSIGNAL);
+ struct timeval timeout = { 1, 0 };
- if(sent == -1)
- {
- if(errno == EINTR)
- sent = 0;
- else
- throw std::runtime_error("Socket write failed errno=" + std::to_string(errno));
- }
+ ::select(sockfd + 1, &readfds, nullptr, nullptr, &timeout);
- offset += sent;
- length -= sent;
- }
- }
+ if(FD_ISSET(sockfd, &readfds))
+ {
+ uint8_t buf[4*1024] = { 0 };
-bool got_alert = false;
+ ssize_t got = ::read(sockfd, buf, sizeof(buf));
-void alert_received(TLS::Alert alert, const byte [], size_t )
- {
- std::cout << "Alert: " << alert.type_string() << std::endl;
- got_alert = true;
- }
+ if(got == 0)
+ {
+ output() << "EOF on socket\n";
+ break;
+ }
+ else if(got == -1)
+ {
+ output() << "Socket error: " << errno << " " << strerror(errno) << "\n";
+ continue;
+ }
-void process_data(const byte buf[], size_t buf_size)
- {
- for(size_t i = 0; i != buf_size; ++i)
- std::cout << buf[i];
- }
+ client.received_data(buf, got);
+ }
-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;
- }
+ if(FD_ISSET(STDIN_FILENO, &readfds))
+ {
+ uint8_t buf[1024] = { 0 };
+ ssize_t got = read(STDIN_FILENO, buf, sizeof(buf));
- try
- {
- AutoSeeded_RNG rng;
- TLS::Policy policy;
+ if(got == 0)
+ {
+ output() << "EOF on stdin\n";
+ client.close();
+ break;
+ }
+ else if(got == -1)
+ {
+ output() << "Stdin error: " << errno << " " << strerror(errno) << "\n";
+ continue;
+ }
-#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
+ if(got == 2 && buf[1] == '\n')
+ {
+ char cmd = buf[0];
+
+ if(cmd == 'R' || cmd == 'r')
+ {
+ output() << "Client initiated renegotiation\n";
+ client.renegotiate(cmd == 'R');
+ }
+ else if(cmd == 'Q')
+ {
+ output() << "Client initiated close\n";
+ client.close();
+ }
+ }
+ else if(buf[0] == 'H')
+ client.heartbeat(&buf[1], got-1);
+ else
+ client.send(buf, got);
+ }
- Basic_Credentials_Manager creds;
+ if(client.timeout_check())
+ {
+ output() << "Timeout detected\n";
+ }
+ }
- 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";
+ ::close(sockfd);
+ }
- const bool use_tcp = (transport == "tcp");
+ private:
+ int connect_to_host(const std::string& host, uint16_t port, bool tcp)
+ {
+ hostent* host_addr = ::gethostbyname(host.c_str());
- const std::vector<std::string> protocols_to_offer = { "test/9.9", "http/1.1", "echo/9.1" };
+ if(!host_addr)
+ throw std::runtime_error("gethostbyname failed for " + host);
- int sockfd = connect_to_host(host, port, use_tcp);
+ if(host_addr->h_addrtype != AF_INET) // FIXME
+ throw std::runtime_error(host + " has IPv6 address, not supported");
- auto socket_write =
- use_tcp ?
- std::bind(stream_socket_write, sockfd, _1, _2) :
- std::bind(dgram_socket_write, sockfd, _1, _2);
+ int type = tcp ? SOCK_STREAM : SOCK_DGRAM;
- auto version = policy.latest_supported_version(!use_tcp);
+ int fd = ::socket(PF_INET, type, 0);
+ if(fd == -1)
+ throw std::runtime_error("Unable to acquire socket");
- 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);
+ sockaddr_in socket_info;
+ ::memset(&socket_info, 0, sizeof(socket_info));
+ socket_info.sin_family = AF_INET;
+ socket_info.sin_port = htons(port);
- bool first_active = true;
+ ::memcpy(&socket_info.sin_addr,
+ host_addr->h_addr,
+ host_addr->h_length);
- while(!client.is_closed())
- {
- fd_set readfds;
- FD_ZERO(&readfds);
- FD_SET(sockfd, &readfds);
+ socket_info.sin_addr = *reinterpret_cast<struct in_addr*>(host_addr->h_addr); // FIXME
- if(client.is_active())
+ if(::connect(fd, (sockaddr*)&socket_info, sizeof(struct sockaddr)) != 0)
{
- 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;
- }
+ ::close(fd);
+ throw std::runtime_error("connect failed");
}
- struct timeval timeout = { 1, 0 };
+ return fd;
+ }
+
+ bool handshake_complete(const Botan::TLS::Session& session)
+ {
+ output() << "Handshake complete, " << session.version().to_string()
+ << " using " << session.ciphersuite().to_string() << "\n";
- ::select(sockfd + 1, &readfds, nullptr, nullptr, &timeout);
+ if(!session.session_id().empty())
+ output() << "Session ID " << Botan::hex_encode(session.session_id()) << "\n";
- if(FD_ISSET(sockfd, &readfds))
- {
- byte buf[4*1024] = { 0 };
+ if(!session.session_ticket().empty())
+ output() << "Session ticket " << Botan::hex_encode(session.session_ticket()) << "\n";
- ssize_t got = ::read(sockfd, buf, sizeof(buf));
+ return true;
+ }
- 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;
- }
+ static void dgram_socket_write(int sockfd, const uint8_t buf[], size_t length)
+ {
+ int r = send(sockfd, buf, length, MSG_NOSIGNAL);
- client.received_data(buf, got);
- }
+ if(r == -1)
+ throw std::runtime_error("Socket write failed errno=" + std::to_string(errno));
+ }
+
+ static void stream_socket_write(int sockfd, const uint8_t buf[], size_t length)
+ {
+ size_t offset = 0;
- if(FD_ISSET(STDIN_FILENO, &readfds))
+ while(length)
{
- byte buf[1024] = { 0 };
- ssize_t got = read(STDIN_FILENO, buf, sizeof(buf));
+ ssize_t sent = ::send(sockfd, (const char*)buf + offset,
+ length, MSG_NOSIGNAL);
- if(got == 0)
- {
- std::cout << "EOF on stdin" << std::endl;
- client.close();
- break;
- }
- else if(got == -1)
+ if(sent == -1)
{
- std::cout << "Stdin error: " << errno << " " << strerror(errno) << std::endl;
- continue;
+ if(errno == EINTR)
+ sent = 0;
+ else
+ throw std::runtime_error("Socket write failed errno=" + std::to_string(errno));
}
- 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);
+ offset += sent;
+ length -= sent;
}
+ }
- if(client.timeout_check())
- {
- std::cout << "Timeout detected" << std::endl;
- }
+ void alert_received(Botan::TLS::Alert alert, const uint8_t [], size_t )
+ {
+ output() << "Alert: " << alert.type_string() << "\n";
+ }
+
+ void process_data(const uint8_t buf[], size_t buf_size)
+ {
+ for(size_t i = 0; i != buf_size; ++i)
+ output() << buf[i];
}
+ };
- ::close(sockfd);
- }
- catch(std::exception& e)
- {
- std::cout << "Exception: " << e.what() << std::endl;
- return 1;
- }
- return 0;
- }
-
-REGISTER_APP(tls_client);
+BOTAN_REGISTER_COMMAND(TLS_Client);
}
diff --git a/src/cli/tls_proxy.cpp b/src/cli/tls_proxy.cpp
index 5071cb8bb..e28ef14bc 100644
--- a/src/cli/tls_proxy.cpp
+++ b/src/cli/tls_proxy.cpp
@@ -5,7 +5,7 @@
* Botan is released under the Simplified BSD License (see license.txt)
*/
-#include "apps.h"
+#include "cli.h"
#if defined(BOTAN_HAS_TLS) && defined(BOTAN_HAS_BOOST_ASIO)
@@ -31,12 +31,12 @@
#include "credentials.h"
-using boost::asio::ip::tcp;
-
-namespace Botan {
+namespace Botan_CLI {
namespace {
+using boost::asio::ip::tcp;
+
inline void log_exception(const char* where, const std::exception& e)
{
std::cout << where << ' ' << e.what() << std::endl;
@@ -47,12 +47,12 @@ 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)
+inline void log_binary_message(const char* where, const uint8_t 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)
+void log_text_message(const char* where, const uint8_t buf[], size_t buf_len)
{
//const char* c = reinterpret_cast<const char*>(buf);
//std::cout << where << ' ' << std::string(c, c + buf_len) << std::endl;
@@ -66,9 +66,9 @@ class tls_proxy_session : public boost::enable_shared_from_this<tls_proxy_sessio
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,
+ Botan::TLS::Session_Manager& session_manager,
+ Botan::Credentials_Manager& credentials,
+ Botan::TLS::Policy& policy,
tcp::resolver::iterator endpoints)
{
return pointer(
@@ -99,9 +99,9 @@ class tls_proxy_session : public boost::enable_shared_from_this<tls_proxy_sessio
private:
tls_proxy_session(boost::asio::io_service& io,
- TLS::Session_Manager& session_manager,
- Credentials_Manager& credentials,
- TLS::Policy& policy,
+ Botan::TLS::Session_Manager& session_manager,
+ Botan::Credentials_Manager& credentials,
+ Botan::TLS::Policy& policy,
tcp::resolver::iterator endpoints) :
m_strand(io),
m_server_endpoints(endpoints),
@@ -174,13 +174,13 @@ class tls_proxy_session : public boost::enable_shared_from_this<tls_proxy_sessio
proxy_write_to_server(nullptr, 0); // initiate another write if needed
}
- void tls_client_write_to_proxy(const byte buf[], size_t buf_len)
+ void tls_client_write_to_proxy(const uint8_t 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)
+ void tls_proxy_write_to_client(const uint8_t buf[], size_t buf_len)
{
if(buf_len > 0)
m_p2c_pending.insert(m_p2c_pending.end(), buf, buf + buf_len);
@@ -202,7 +202,7 @@ class tls_proxy_session : public boost::enable_shared_from_this<tls_proxy_sessio
}
}
- void proxy_write_to_server(const byte buf[], size_t buf_len)
+ void proxy_write_to_server(const uint8_t buf[], size_t buf_len)
{
if(buf_len > 0)
m_p2s_pending.insert(m_p2s_pending.end(), buf, buf + buf_len);
@@ -259,7 +259,7 @@ class tls_proxy_session : public boost::enable_shared_from_this<tls_proxy_sessio
boost::asio::placeholders::bytes_transferred)));
}
- bool tls_handshake_complete(const TLS::Session& session)
+ bool tls_handshake_complete(const Botan::TLS::Session& session)
{
//std::cout << "Handshake from client complete" << std::endl;
@@ -283,9 +283,9 @@ class tls_proxy_session : public boost::enable_shared_from_this<tls_proxy_sessio
return true;
}
- void tls_alert_cb(TLS::Alert alert, const byte[], size_t)
+ void tls_alert_cb(Botan::TLS::Alert alert, const uint8_t[], size_t)
{
- if(alert.type() == TLS::Alert::CLOSE_NOTIFY)
+ if(alert.type() == Botan::TLS::Alert::CLOSE_NOTIFY)
{
m_tls.close();
return;
@@ -301,17 +301,17 @@ class tls_proxy_session : public boost::enable_shared_from_this<tls_proxy_sessio
tcp::socket m_client_socket;
tcp::socket m_server_socket;
- AutoSeeded_RNG m_rng;
- TLS::Server m_tls;
+ Botan::AutoSeeded_RNG m_rng; // RNG per connection
+ Botan::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<uint8_t> m_c2p;
+ std::vector<uint8_t> m_p2c;
+ std::vector<uint8_t> m_p2c_pending;
- std::vector<byte> m_s2p;
- std::vector<byte> m_p2s;
- std::vector<byte> m_p2s_pending;
+ std::vector<uint8_t> m_s2p;
+ std::vector<uint8_t> m_p2s;
+ std::vector<uint8_t> m_p2s_pending;
};
class tls_proxy_server
@@ -321,9 +321,9 @@ class tls_proxy_server
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) :
+ Botan::Credentials_Manager& creds,
+ Botan::TLS::Policy& policy,
+ Botan::TLS::Session_Manager& session_mgr) :
m_acceptor(io, tcp::endpoint(tcp::v4(), port)),
m_server_endpoints(endpoints),
m_creds(creds),
@@ -377,83 +377,73 @@ class tls_proxy_server
tcp::acceptor m_acceptor;
tcp::resolver::iterator m_server_endpoints;
- Credentials_Manager& m_creds;
- TLS::Policy& m_policy;
- TLS::Session_Manager& m_session_manager;
+ Botan::Credentials_Manager& m_creds;
+ Botan::TLS::Policy& m_policy;
+ Botan::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)
+class TLS_Proxy : public Command
{
- if(args.size() != 6)
- {
- std::cout << "Usage: " << args[0] << " listen_port target_host target_port server_cert server_key" << std::endl;
- return 1;
- }
+ public:
+ TLS_Proxy() : Command("tls_proxy listen_port target_host target_port server_cert server_key "
+ "--threads=0 --session-db= --session-db-pass=") {}
+
+ void go()
+ {
+ const size_t listen_port = get_arg_sz("listen_port");
+ const std::string target = get_arg("target_host");
+ const std::string target_port = get_arg("target_port");
- 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 = get_arg("server_cert");
+ const std::string server_key = get_arg("server_key");
- const std::string server_crt = args[4];
- const std::string server_key = args[5];
+ const size_t num_threads = get_arg_sz("threads") || std::thread::hardware_concurrency() || 2;
- const size_t num_threads = choose_thread_count(); // make configurable
+ Botan::AutoSeeded_RNG rng;
+ Basic_Credentials_Manager creds(rng, server_crt, server_key);
- AutoSeeded_RNG rng;
- Basic_Credentials_Manager creds(rng, server_crt, server_key);
+ Botan::TLS::Policy policy; // TODO: Read policy from text file
- TLS::Policy policy; // TODO: Read policy from text file
+ boost::asio::io_service io;
- try
- {
- boost::asio::io_service io;
+ tcp::resolver resolver(io);
+ auto server_endpoint_iterator = resolver.resolve({ target, target_port });
- tcp::resolver resolver(io);
- auto server_endpoint_iterator = resolver.resolve({ target, target_port });
+ std::unique_ptr<Botan::TLS::Session_Manager> session_mgr;
#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
+ const std::string sessions_passphrase = get_arg("session-db-pass");
+ const std::string sessions_db = get_arg("session-db");
- tls_proxy_server server(io, listen_port, server_endpoint_iterator, creds, policy, sessions);
+ if(!sessions_db.empty())
+ {
+ session_mgr.reset(new Botan::TLS::Session_Manager_SQLite(sessions_passphrase, rng, sessions_db));
+ }
+#endif
+ if(!session_mgr)
+ {
+ session_mgr.reset(new Botan::TLS::Session_Manager_In_Memory(rng));
+ }
- std::vector<std::shared_ptr<std::thread>> threads;
+ tls_proxy_server server(io, listen_port, server_endpoint_iterator, creds, policy, *session_mgr);
- for(size_t i = 2; i <= num_threads; ++i)
- threads.push_back(std::make_shared<std::thread>([&io]() { io.run(); }));
+ std::vector<std::shared_ptr<std::thread>> threads;
- io.run();
+ // run forever... first thread is main calling io.run below
+ for(size_t i = 2; i <= num_threads; ++i)
+ threads.push_back(std::make_shared<std::thread>([&io]() { io.run(); }));
- for (size_t i = 0; i < threads.size(); ++i)
- threads[i]->join();
- }
- catch (std::exception& e)
- {
- std::cerr << e.what() << std::endl;
- }
+ io.run();
- return 0;
- }
+ for (size_t i = 0; i < threads.size(); ++i)
+ threads[i]->join();
+ }
+ };
-}
+BOTAN_REGISTER_COMMAND(TLS_Proxy);
}
-REGISTER_APP(tls_proxy);
-
#endif
diff --git a/src/cli/tls_server.cpp b/src/cli/tls_server.cpp
index ea68208b6..6a5b4e812 100644
--- a/src/cli/tls_server.cpp
+++ b/src/cli/tls_server.cpp
@@ -5,24 +5,15 @@
* Botan is released under the Simplified BSD License (see license.txt)
*/
-#include "apps.h"
+#include "cli.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 <botan/auto_rng.h>
#include "credentials.h"
-using namespace Botan;
-
-using namespace std::placeholders;
-
#include <list>
#include <sys/types.h>
@@ -38,128 +29,44 @@ using namespace std::placeholders;
#define MSG_NOSIGNAL 0
#endif
-namespace {
+namespace Botan_CLI {
-int make_server_socket(bool is_tcp, u16bit port)
+class TLS_Server : public Command
{
- 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");
+ public:
+ TLS_Server() : Command("tls_server cert key --port=443 --type=tcp") {}
- 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)
+ void go() override
{
- ::close(fd);
- throw std::runtime_error("listen failed");
- }
- }
+ const std::string server_crt = get_arg("cert");
+ const std::string server_key = get_arg("key");
+ const int port = get_arg_sz("port");
+ const std::string transport = get_arg("type");
- return fd;
- }
+ if(transport != "tcp" && transport != "udp")
+ throw CLI_Usage_Error("Invalid transport type '" + transport + "' for TLS");
-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;
+ const bool is_tcp = (transport == "tcp");
- if(!session.session_ticket().empty())
- std::cout << "Session ticket " << hex_encode(session.session_ticket()) << std::endl;
+ Botan::AutoSeeded_RNG rng;
- return true;
- }
-
-void dgram_socket_write(int sockfd, const byte buf[], size_t length)
- {
- ssize_t sent = ::send(sockfd, buf, length, MSG_NOSIGNAL);
+ Botan::TLS::Policy policy; // TODO read policy from file
- 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;
- }
+ Botan::TLS::Session_Manager_In_Memory session_manager(rng); // TODO sqlite3
-void stream_socket_write(int sockfd, const byte buf[], size_t length)
- {
- while(length)
- {
- ssize_t sent = ::send(sockfd, buf, length, MSG_NOSIGNAL);
+ Basic_Credentials_Manager creds(rng, server_crt, server_key);
- if(sent == -1)
- {
- if(errno == EINTR)
- sent = 0;
- else
- throw std::runtime_error("Socket write failed");
- }
+ 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
+ };
- buf += sent;
- length -= sent;
- }
- }
+ output() << "Listening for new connections on " << transport << " port " << port << std::endl;
-void alert_received(TLS::Alert alert, const byte[], size_t)
- {
- std::cout << "Alert: " << alert.type_string() << std::endl;
- }
+ int server_fd = make_server_socket(is_tcp, port);
-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
+ while(true)
{
int fd;
@@ -180,42 +87,42 @@ int tls_server(const std::vector<std::string> &args)
fd = server_fd;
}
- std::cout << "New connection received" << std::endl;
+ using namespace std::placeholders;
- auto socket_write = is_tcp ? std::bind(stream_socket_write, fd, _1, _2) :
- std::bind(dgram_socket_write, fd, _1, _2);
+ 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)
+ auto proc_fn = [&](const uint8_t 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')
+ for(size_t i = 0; i != input_len; ++i)
{
- pending_output.push_back(s);
- s.clear();
+ 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);
+ Botan::TLS::Server server(socket_write,
+ proc_fn,
+ std::bind(&TLS_Server::alert_received, this, _1, _2, _3),
+ std::bind(&TLS_Server::handshake_complete, this, _1),
+ session_manager,
+ creds,
+ policy,
+ rng,
+ protocol_chooser,
+ !is_tcp);
while(!server.is_closed())
{
- byte buf[4*1024] = { 0 };
+ uint8_t buf[4*1024] = { 0 };
ssize_t got = ::read(fd, buf, sizeof(buf));
if(got == -1)
@@ -245,25 +152,94 @@ int tls_server(const std::vector<std::string> &args)
if(is_tcp)
::close(fd);
+ }
+ }
+ private:
+ int make_server_socket(bool is_tcp, uint16_t 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");
+ }
}
- catch(std::exception& e)
+
+ return fd;
+ }
+
+ bool handshake_complete(const Botan::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 " << Botan::hex_encode(session.session_id()) << std::endl;
+
+ if(!session.session_ticket().empty())
+ std::cout << "Session ticket " << Botan::hex_encode(session.session_ticket()) << std::endl;
+
+ return true;
+ }
+
+ static void dgram_socket_write(int sockfd, const uint8_t 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;
+ }
+
+ static void stream_socket_write(int sockfd, const uint8_t buf[], size_t length)
+ {
+ while(length)
{
- std::cout << "Connection problem: " << e.what() << std::endl;
- return 1;
+ 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;
}
}
- }
- catch(std::exception& e)
- {
- std::cout << e.what() << std::endl;
- return 1;
- }
- return 0;
- }
+ void alert_received(Botan::TLS::Alert alert, const uint8_t[], size_t)
+ {
+ std::cout << "Alert: " << alert.type_string() << std::endl;
+ }
+
+ };
-REGISTER_APP(tls_server);
+BOTAN_REGISTER_COMMAND(TLS_Server);
}
diff --git a/src/cli/utils.cpp b/src/cli/utils.cpp
new file mode 100644
index 000000000..f3ce5f0f9
--- /dev/null
+++ b/src/cli/utils.cpp
@@ -0,0 +1,266 @@
+/*
+* (C) 2009,2010,2014,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "cli.h"
+
+#include <botan/version.h>
+#include <botan/auto_rng.h>
+#include <botan/hash.h>
+#include <botan/cpuid.h>
+#include <botan/hex.h>
+
+#if defined(BOTAN_HAS_BASE64_CODEC)
+ #include <botan/base64.h>
+#endif
+
+#if defined(BOTAN_HAS_SYSTEM_RNG)
+ #include <botan/system_rng.h>
+#endif
+
+#if defined(BOTAN_HAS_HTTP_UTIL)
+ #include <botan/http_util.h>
+#endif
+
+#if defined(BOTAN_HAS_BCRYPT)
+ #include <botan/bcrypt.h>
+#endif
+
+namespace Botan_CLI {
+
+class Config_Info : public Command
+ {
+ public:
+ Config_Info() : Command("config info_type") {}
+
+ std::string help_text() const override
+ {
+ return "Usage: config info_type\n"
+ " prefix: Print install prefix\n"
+ " cflags: Print include params\n"
+ " ldflags: Print linker params\n"
+ " libs: Print libraries\n";
+ }
+
+ void go() override
+ {
+ const std::string arg = get_arg("info_type");
+
+ if(arg == "prefix")
+ {
+ output() << BOTAN_INSTALL_PREFIX << "\n";
+ }
+ else if(arg == "cflags")
+ {
+ output() << "-I" << BOTAN_INSTALL_PREFIX << "/" << BOTAN_INSTALL_HEADER_DIR << "\n";
+ }
+ else if(arg == "ldflags")
+ {
+ output() << "-L" << BOTAN_INSTALL_PREFIX << "/" << BOTAN_INSTALL_LIB_DIR << "\n";
+ }
+ else if(arg == "libs")
+ {
+ output() << "-lbotan-" << Botan::version_major() << "." << Botan::version_minor()
+ << " " << BOTAN_LIB_LINK << "\n";
+ }
+ else
+ {
+ throw CLI_Usage_Error("Unknown option to botan config " + arg);
+ }
+ }
+ };
+
+BOTAN_REGISTER_COMMAND(Config_Info);
+
+class Version_Info : public Command
+ {
+ public:
+ Version_Info() : Command("version --full") {}
+
+ void go() override
+ {
+ if(flag_set("full"))
+ {
+ output() << Botan::version_string() << "\n";
+ }
+ else
+ {
+ output() << Botan::version_major() << "."
+ << Botan::version_minor() << "."
+ << Botan::version_patch() << "\n";
+ }
+ }
+ };
+
+BOTAN_REGISTER_COMMAND(Version_Info);
+
+class Print_Cpuid : public Command
+ {
+ public:
+ Print_Cpuid() : Command("cpuid") {}
+
+ void go() override
+ {
+ Botan::CPUID::print(output());
+ }
+ };
+
+BOTAN_REGISTER_COMMAND(Print_Cpuid);
+
+class Hash : public Command
+ {
+ public:
+ Hash() : Command("hash --algo=SHA-256 --buf-size=4096 *files") {}
+
+ void go() override
+ {
+ const std::string hash_algo = get_arg("algo");
+ std::unique_ptr<Botan::HashFunction> hash_fn(Botan::HashFunction::create(hash_algo));
+
+ if(!hash_fn)
+ throw CLI_Error_Unsupported("hashing", hash_algo);
+
+ const size_t buf_size = get_arg_sz("buf-size");
+
+ for(auto fsname : get_arg_list("files"))
+ {
+ auto update_hash = [&](const uint8_t b[], size_t l) { hash_fn->update(b, l); };
+ read_file(fsname, update_hash, buf_size);
+ output() << Botan::hex_encode(hash_fn->final()) << " " << fsname << "\n";
+ }
+ }
+ };
+
+BOTAN_REGISTER_COMMAND(Hash);
+
+class RNG : public Command
+ {
+ public:
+ RNG() : Command("rng bytes --system") {}
+
+ void go() override
+ {
+ const size_t bytes = get_arg_sz("bytes");
+
+ if(flag_set("system"))
+ {
+#if defined(BOTAN_HAS_SYSTEM_RNG)
+ output() << Botan::hex_encode(Botan::system_rng().random_vec(bytes)) << "\n";
+#else
+ error_output() << "system_rng disabled in build\n";
+#endif
+ }
+ else
+ {
+ Botan::AutoSeeded_RNG rng;
+ output() << Botan::hex_encode(rng.random_vec(bytes)) << "\n";
+ }
+ }
+ };
+
+BOTAN_REGISTER_COMMAND(RNG);
+
+#if defined(BOTAN_HAS_HTTP_UTIL)
+
+class HTTP_Get : public Command
+ {
+ public:
+ HTTP_Get() : Command("http_get url") {}
+
+ void go() override
+ {
+ output() << Botan::HTTP::GET_sync(get_arg("url")) << "\n";
+ }
+ };
+
+BOTAN_REGISTER_COMMAND(HTTP_Get);
+
+#endif // http_util
+
+#if defined(BOTAN_HAS_BASE64_CODEC)
+
+class Base64_Encode : public Command
+ {
+ public:
+ Base64_Encode() : Command("base64_enc file") {}
+
+ void go() override
+ {
+ this->read_file(get_arg("file"),
+ [&](const uint8_t b[], size_t l) { output() << Botan::base64_encode(b, l); },
+ 768);
+ }
+ };
+
+BOTAN_REGISTER_COMMAND(Base64_Encode);
+
+class Base64_Decode : public Command
+ {
+ public:
+ Base64_Decode() : Command("base64_dec file") {}
+
+ void go() override
+ {
+ auto write_bin = [&](const uint8_t b[], size_t l)
+ {
+ Botan::secure_vector<uint8_t> bin = Botan::base64_decode(reinterpret_cast<const char*>(b), l);
+ output().write(reinterpret_cast<const char*>(bin.data()), bin.size());
+ };
+
+ this->read_file(get_arg("file"),
+ write_bin,
+ 1024);
+ }
+ };
+
+BOTAN_REGISTER_COMMAND(Base64_Decode);
+
+#endif // base64
+
+#if defined(BOTAN_HAS_BCRYPT)
+
+class Generate_Bcrypt : public Command
+ {
+ public:
+ Generate_Bcrypt() : Command("gen_bcrypt --work-factor=12 password") {}
+
+ void go()
+ {
+ const std::string password = get_arg("password");
+ const size_t wf = get_arg_sz("work_factor");
+
+ Botan::AutoSeeded_RNG rng;
+ output() << Botan::generate_bcrypt(password, rng, wf) << "\n";
+ }
+ };
+
+BOTAN_REGISTER_COMMAND(Generate_Bcrypt);
+
+class Check_Bcrypt : public Command
+ {
+ public:
+ Check_Bcrypt() : Command("check_bcrypt password hash") {}
+
+ void go()
+ {
+ const std::string password = get_arg("password");
+ const std::string hash = get_arg("hash");
+
+ if(hash.length() != 60)
+ {
+ error_output() << "Note: bcrypt '" << hash << "' has wrong length and cannot be valid\n";
+ }
+
+ const bool ok = Botan::check_bcrypt(password, hash);
+
+ output() << "Password is " << (ok ? "valid" : "NOT valid") << std::endl;
+ }
+ };
+
+BOTAN_REGISTER_COMMAND(Check_Bcrypt);
+
+#endif // bcrypt
+
+}
diff --git a/src/cli/x509.cpp b/src/cli/x509.cpp
new file mode 100644
index 000000000..add73a466
--- /dev/null
+++ b/src/cli/x509.cpp
@@ -0,0 +1,224 @@
+/*
+* (C) 2010,2014,2015 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "cli.h"
+
+#if defined(BOTAN_HAS_X509_CERTIFICATES)
+
+#include <botan/auto_rng.h>
+#include <botan/certstor.h>
+#include <botan/pkcs8.h>
+#include <botan/x509_ca.h>
+#include <botan/x509cert.h>
+#include <botan/x509path.h>
+#include <botan/x509self.h>
+
+#if defined(BOTAN_HAS_OCSP)
+ #include <botan/ocsp.h>
+#endif
+
+namespace Botan_CLI {
+
+class Sign_Cert : public Command
+ {
+ public:
+ Sign_Cert() : Command("sign_cert --ca-key-pass= --hash=SHA-256 "
+ "--duration=365 ca_cert ca_key pkcs10_req") {}
+
+ void go() override
+ {
+ Botan::AutoSeeded_RNG rng;
+
+ Botan::X509_Certificate ca_cert(get_arg("ca_cert"));
+
+ std::unique_ptr<Botan::PKCS8_PrivateKey> key(
+ Botan::PKCS8::load_key(get_arg("ca_key"),
+ rng,
+ get_arg("ca_key_pass")));
+
+ if(!key)
+ throw CLI_Error("Failed to load key from " + get_arg("ca_key"));
+
+ Botan::X509_CA ca(ca_cert, *key, get_arg("hash"));
+
+ Botan::PKCS10_Request req(get_arg("pkcs10_req"));
+
+ auto now = std::chrono::system_clock::now();
+
+ Botan::X509_Time start_time(now);
+
+ typedef std::chrono::duration<int, std::ratio<86400>> days;
+
+ Botan::X509_Time end_time(now + days(get_arg_sz("duration")));
+
+ Botan::X509_Certificate new_cert = ca.sign_request(req, rng,
+ start_time, end_time);
+
+ output() << new_cert.PEM_encode();
+ }
+ };
+
+BOTAN_REGISTER_COMMAND(Sign_Cert);
+
+class Cert_Info : public Command
+ {
+ public:
+ Cert_Info() : Command("cert_info file") {}
+
+ void go() override
+ {
+ Botan::X509_Certificate cert(get_arg("file"));
+ output() << cert.to_string() << "\n";
+ }
+ };
+
+BOTAN_REGISTER_COMMAND(Cert_Info);
+
+#if defined(BOTAN_HAS_OCSP)
+class OCSP_Check : public Command
+ {
+ public:
+ OCSP_Check() : Command("ocsp_check subject issuer") {}
+
+ void go() override
+ {
+ Botan::X509_Certificate subject(get_arg("subject"));
+ Botan::X509_Certificate issuer(get_arg("issuer"));
+
+ Botan::Certificate_Store_In_Memory cas;
+ cas.add_certificate(issuer);
+ Botan::OCSP::Response resp = Botan::OCSP::online_check(issuer, subject, &cas);
+
+ auto status = resp.status_for(issuer, subject);
+
+ if(status == Botan::Certificate_Status_Code::VERIFIED)
+ {
+ output() << "OCSP check OK\n";
+ }
+ else
+ {
+ output() << "OCSP check failed " <<
+ Botan::Path_Validation_Result::status_string(status) << "\n";
+ }
+ }
+ };
+
+BOTAN_REGISTER_COMMAND(OCSP_Check);
+
+#endif // OCSP
+
+class Cert_Verify : public Command
+ {
+ public:
+ Cert_Verify() : Command("cert_verify subject *ca_certs") {}
+
+ void go() override
+ {
+ Botan::X509_Certificate subject_cert(get_arg("subject"));
+ Botan::Certificate_Store_In_Memory trusted;
+
+ for(auto&& certfile : get_arg_list("ca_certs"))
+ {
+ trusted.add_certificate(Botan::X509_Certificate(certfile));
+ }
+
+ Botan::Path_Validation_Restrictions restrictions;
+
+ Botan::Path_Validation_Result result =
+ Botan::x509_path_validate(subject_cert,
+ restrictions,
+ trusted);
+
+ if(result.successful_validation())
+ {
+ output() << "Certificate passes validation checks\n";
+ }
+ else
+ {
+ output() << "Certificate did not validate - " << result.result_string() << "\n";
+ }
+ }
+ };
+
+BOTAN_REGISTER_COMMAND(Cert_Verify);
+
+class Gen_Self_Signed : public Command
+ {
+ public:
+ Gen_Self_Signed() : Command("gen_self_signed key CN --country= --dns= "
+ "--organization= --email= --key-pass= --ca --hash=SHA-256") {}
+
+ void go() override
+ {
+ Botan::AutoSeeded_RNG rng;
+
+ std::unique_ptr<Botan::Private_Key> key(
+ Botan::PKCS8::load_key(get_arg("key"),
+ rng,
+ get_arg("key-pass")));
+
+ if(!key)
+ throw CLI_Error("Failed to load key from " + get_arg("key"));
+
+ Botan::X509_Cert_Options opts;
+
+ opts.common_name = get_arg("CN");
+ opts.country = get_arg("country");
+ opts.organization = get_arg("organization");
+ opts.email = get_arg("email");
+ opts.dns = get_arg("dns");
+
+ if(flag_set("ca"))
+ opts.CA_key();
+
+ Botan::X509_Certificate cert =
+ Botan::X509::create_self_signed_cert(opts, *key, get_arg("hash"), rng);
+
+ output() << cert.PEM_encode();
+ }
+ };
+
+BOTAN_REGISTER_COMMAND(Gen_Self_Signed);
+
+class Generate_PKCS10 : public Command
+ {
+ public:
+ Generate_PKCS10() : Command("gen_pkcs10 key CN --country= --organization= "
+ "--email= --key-pass= --hash=SHA-256") {}
+
+ void go() override
+ {
+ Botan::AutoSeeded_RNG rng;
+
+ std::unique_ptr<Botan::Private_Key> key(
+ Botan::PKCS8::load_key(get_arg("key"),
+ rng,
+ get_arg("key-pass")));
+
+ if(!key)
+ throw CLI_Error("Failed to load key from " + get_arg("key"));
+
+ Botan::X509_Cert_Options opts;
+
+ opts.common_name = get_arg("CN");
+ opts.country = get_arg("country");
+ opts.organization = get_arg("organization");
+ opts.email = get_arg("email");
+
+ Botan::PKCS10_Request req =
+ Botan::X509::create_cert_req(opts, *key,
+ get_arg("hash"),
+ rng);
+
+ output() << req.PEM_encode();
+ }
+ };
+
+BOTAN_REGISTER_COMMAND(Generate_PKCS10);
+
+}
+
+#endif
diff --git a/src/cli/x509print.cpp b/src/cli/x509print.cpp
deleted file mode 100644
index 5c069ca77..000000000
--- a/src/cli/x509print.cpp
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
-* (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() != 2)
- {
- 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
diff --git a/src/lib/misc/benchmark/benchmark.cpp b/src/lib/misc/benchmark/benchmark.cpp
deleted file mode 100644
index 4ecca1566..000000000
--- a/src/lib/misc/benchmark/benchmark.cpp
+++ /dev/null
@@ -1,165 +0,0 @@
-/*
-* Runtime benchmarking
-* (C) 2008-2009,2013 Jack Lloyd
-*
-* Botan is released under the Simplified BSD License (see license.txt)
-*/
-
-#include <botan/benchmark.h>
-#include <botan/exceptn.h>
-#include <botan/buf_comp.h>
-#include <botan/cipher_mode.h>
-#include <botan/block_cipher.h>
-#include <botan/stream_cipher.h>
-#include <botan/hash.h>
-#include <botan/mac.h>
-#include <vector>
-#include <chrono>
-
-namespace Botan {
-
-namespace {
-
-double time_op(std::chrono::nanoseconds runtime, std::function<void ()> op)
- {
- std::chrono::nanoseconds time_used(0);
- size_t reps = 0;
-
- auto start = std::chrono::high_resolution_clock::now();
-
- while(time_used < runtime)
- {
- op();
- ++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;
-
- return reps / seconds_used; // ie, return ops per second
- }
-
-std::map<std::string, double>
-time_algorithm_ops(const std::string& name,
- const std::string& provider,
- RandomNumberGenerator& rng,
- std::chrono::nanoseconds runtime,
- size_t buf_size)
- {
- const size_t Mebibyte = 1024*1024;
-
- secure_vector<byte> buffer(buf_size * 1024);
- rng.randomize(buffer.data(), buffer.size());
-
- const double mb_mult = buffer.size() / static_cast<double>(Mebibyte);
-
- if(auto bc = BlockCipher::create(name, provider))
- {
- const SymmetricKey key(rng, bc->maximum_keylength());
-
- return std::map<std::string, double>({
- { "key schedule", time_op(runtime / 8, [&]() { bc->set_key(key); }) },
- { "encrypt", mb_mult * time_op(runtime / 2, [&]() { bc->encrypt(buffer); }) },
- { "decrypt", mb_mult * time_op(runtime / 2, [&]() { bc->decrypt(buffer); }) },
- });
- }
- else if(auto sc = StreamCipher::create(name, provider))
- {
- const SymmetricKey key(rng, sc->maximum_keylength());
-
- return std::map<std::string, double>({
- { "key schedule", time_op(runtime / 8, [&]() { sc->set_key(key); }) },
- { "", mb_mult * time_op(runtime, [&]() { sc->encipher(buffer); }) },
- });
- }
- else if(auto h = HashFunction::create(name, provider))
- {
- return std::map<std::string, double>({
- { "", mb_mult * time_op(runtime, [&]() { h->update(buffer); }) },
- });
- }
- else if(auto mac = MessageAuthenticationCode::create(name, provider))
- {
- const SymmetricKey key(rng, mac->maximum_keylength());
-
- return std::map<std::string, double>({
- { "key schedule", time_op(runtime / 8, [&]() { mac->set_key(key); }) },
- { "", mb_mult * time_op(runtime, [&]() { mac->update(buffer); }) },
- });
- }
- else
- {
- std::unique_ptr<Cipher_Mode> enc(get_cipher_mode(name, ENCRYPTION));
- std::unique_ptr<Cipher_Mode> dec(get_cipher_mode(name, DECRYPTION));
-
- if(enc && dec)
- {
- const SymmetricKey key(rng, enc->key_spec().maximum_keylength());
-
- return std::map<std::string, double>({
- { "key schedule", time_op(runtime / 4, [&]() { enc->set_key(key); dec->set_key(key); }) / 2 },
- { "encrypt", mb_mult * time_op(runtime / 2, [&]() { enc->update(buffer, 0); buffer.resize(buf_size*1024); }) },
- { "decrypt", mb_mult * time_op(runtime / 2, [&]() { dec->update(buffer, 0); buffer.resize(buf_size*1024); }) },
- });
- }
- }
-
- return std::map<std::string, double>();
- }
-
-double find_first_in(const std::map<std::string, double>& m,
- const std::vector<std::string>& keys)
- {
- for(auto key : keys)
- {
- auto i = m.find(key);
- if(i != m.end())
- return i->second;
- }
-
- throw Exception("In algo benchmark no usable keys found in result");
- }
-
-std::set<std::string> get_all_providers_of(const std::string& algo)
- {
- std::set<std::string> provs;
-
- auto add_to_set = [&provs](const std::vector<std::string>& str) { for(auto&& s : str) { provs.insert(s); } };
-
- add_to_set(BlockCipher::providers(algo));
- add_to_set(StreamCipher::providers(algo));
- add_to_set(HashFunction::providers(algo));
- add_to_set(MessageAuthenticationCode::providers(algo));
-
- return provs;
- }
-
-}
-
-std::map<std::string, double>
-algorithm_benchmark(const std::string& name,
- RandomNumberGenerator& rng,
- std::chrono::milliseconds milliseconds,
- size_t buf_size)
- {
- //Algorithm_Factory& af = global_state().algorithm_factory();
- const auto provider_names = get_all_providers_of(name);
- if (provider_names.empty())
- throw No_Provider_Found(name);
-
- std::map<std::string, double> all_results; // provider -> ops/sec
-
- const std::chrono::nanoseconds ns_per_provider = milliseconds / provider_names.size();
-
- for(auto provider : provider_names)
- {
- auto results = time_algorithm_ops(name, provider, rng, ns_per_provider, buf_size);
- all_results[provider] = find_first_in(results, { "", "update", "encrypt" });
- }
-
- return all_results;
- }
-
-}
diff --git a/src/lib/misc/benchmark/benchmark.h b/src/lib/misc/benchmark/benchmark.h
deleted file mode 100644
index 3fa020e1b..000000000
--- a/src/lib/misc/benchmark/benchmark.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
-* Runtime benchmarking
-* (C) 2008-2009 Jack Lloyd
-*
-* Botan is released under the Simplified BSD License (see license.txt)
-*/
-
-#ifndef BOTAN_RUNTIME_BENCHMARK_H__
-#define BOTAN_RUNTIME_BENCHMARK_H__
-
-#include <botan/rng.h>
-#include <map>
-#include <string>
-#include <chrono>
-
-namespace Botan {
-
-/**
-* Algorithm benchmark
-* @param name the name of the algorithm to test (cipher, hash, or MAC)
-* @param af the algorithm factory used to create objects
-* @param rng the rng to use to generate random inputs
-* @param milliseconds total time for the benchmark to run
-* @param buf_size size of buffer to benchmark against, in KiB
-* @return results a map from provider to speed in mebibytes per second
-*/
-std::map<std::string, double>
-BOTAN_DLL algorithm_benchmark(const std::string& name,
- RandomNumberGenerator& rng,
- std::chrono::milliseconds milliseconds,
- size_t buf_size);
-
-}
-
-#endif
diff --git a/src/lib/misc/benchmark/info.txt b/src/lib/misc/benchmark/info.txt
deleted file mode 100644
index 6a26f0059..000000000
--- a/src/lib/misc/benchmark/info.txt
+++ /dev/null
@@ -1 +0,0 @@
-define RUNTIME_BENCHMARKING 20131128
diff --git a/src/lib/tls/tls_handshake_hash.h b/src/lib/tls/tls_handshake_hash.h
index c6b412473..050f3a454 100644
--- a/src/lib/tls/tls_handshake_hash.h
+++ b/src/lib/tls/tls_handshake_hash.h
@@ -16,8 +16,6 @@ namespace Botan {
namespace TLS {
-using namespace Botan;
-
/**
* TLS Handshake Hash
*/
diff --git a/src/lib/utils/stl_util.h b/src/lib/utils/stl_util.h
index d74cbe713..12b749c3c 100644
--- a/src/lib/utils/stl_util.h
+++ b/src/lib/utils/stl_util.h
@@ -12,6 +12,7 @@
#include <vector>
#include <string>
#include <map>
+#include <set>
#include <botan/secmem.h>
namespace Botan {
@@ -26,6 +27,20 @@ inline std::string to_string(const secure_vector<byte> &bytes)
return std::string(bytes.cbegin(), bytes.cend());
}
+/**
+* Return the keys of a map as a std::set
+*/
+template<typename K, typename V>
+std::set<K> map_keys_as_set(const std::map<K, V>& kv)
+ {
+ std::set<K> s;
+ for(auto&& i : kv)
+ {
+ s.insert(i.first);
+ }
+ return s;
+ }
+
/*
* Searching through a std::map
* @param mapping the map to search
diff --git a/src/lib/utils/version.cpp b/src/lib/utils/version.cpp
index f3e01e290..8e14cc62f 100644
--- a/src/lib/utils/version.cpp
+++ b/src/lib/utils/version.cpp
@@ -1,12 +1,13 @@
/*
* Version Information
-* (C) 1999-2013 Jack Lloyd
+* (C) 1999-2013,2015 Jack Lloyd
*
* Botan is released under the Simplified BSD License (see license.txt)
*/
#include <botan/version.h>
#include <botan/parsing.h>
+#include <sstream>
namespace Botan {
@@ -57,4 +58,25 @@ u32bit version_major() { return BOTAN_VERSION_MAJOR; }
u32bit version_minor() { return BOTAN_VERSION_MINOR; }
u32bit version_patch() { return BOTAN_VERSION_PATCH; }
+std::string runtime_version_check(u32bit major,
+ u32bit minor,
+ u32bit patch)
+ {
+ std::ostringstream oss;
+
+ if(major != version_major() ||
+ minor != version_minor() ||
+ patch != version_patch())
+ {
+ oss << "Warning: linked version ("
+ << Botan::version_major() << '.'
+ << Botan::version_minor() << '.'
+ << Botan::version_patch()
+ << ") does not match version built against ("
+ << major << '.' << minor << '.' << patch << ")\n";
+ }
+
+ return oss.str();
+ }
+
}
diff --git a/src/lib/utils/version.h b/src/lib/utils/version.h
index 3fc6f5fe7..406deae66 100644
--- a/src/lib/utils/version.h
+++ b/src/lib/utils/version.h
@@ -1,6 +1,6 @@
/*
* Version Information
-* (C) 1999-2011 Jack Lloyd
+* (C) 1999-2011,2015 Jack Lloyd
*
* Botan is released under the Simplified BSD License (see license.txt)
*/
@@ -54,6 +54,17 @@ BOTAN_DLL u32bit version_minor();
*/
BOTAN_DLL u32bit version_patch();
+/**
+* Usable for checking that the DLL version loaded at runtime exactly
+* matches the compile-time version. Call using BOTAN_VERSION_* macro
+* values. Returns the empty string if an exact match, otherwise an
+* appropriate message. @added 1.11.26
+*/
+BOTAN_DLL std::string
+runtime_version_check(u32bit major,
+ u32bit minor,
+ u32bit patch);
+
/*
* Macros for compile-time version checks
*/
diff --git a/src/tests/main.cpp b/src/tests/main.cpp
index 7a4649fcf..c4313a4ef 100644
--- a/src/tests/main.cpp
+++ b/src/tests/main.cpp
@@ -4,6 +4,7 @@
* Botan is released under the Simplified BSD License (see license.txt)
*/
+#include "../cli/cli.h"
#include "tests.h"
#include <iostream>
#include <sstream>
@@ -29,23 +30,6 @@ namespace {
using Botan_Tests::Test;
-int help(std::ostream& out, const std::string binary_name)
- {
- std::ostringstream err;
-
- err << "Usage:\n"
- << binary_name << " test1 test2 ...\n"
- << "Available tests: ";
-
- for(auto&& test : Test::registered_tests())
- {
- err << test << " ";
- }
-
- out << err.str() << std::endl;
- return 1;
- }
-
std::string report_out(const std::vector<Test::Result>& results,
size_t& tests_failed,
size_t& tests_ran)
@@ -76,72 +60,11 @@ std::string report_out(const std::vector<Test::Result>& results,
return out.str();
}
-size_t run_tests(const std::vector<std::string>& tests_to_run,
- std::ostream& out,
- size_t threads)
- {
- size_t tests_ran = 0, tests_failed = 0;
-
- if(threads <= 1)
- {
- for(auto&& test_name : tests_to_run)
- {
- std::vector<Test::Result> results = Test::run_test(test_name, false);
- out << report_out(results, tests_failed, tests_ran) << std::flush;
- }
- }
- else
- {
-
- /*
- We're not doing this in a particularly nice way, and variance in time is
- high so commonly we'll 'run dry' by blocking on the first future. But
- plain C++11 <thread> is missing a lot of tools we'd need (like
- wait_for_any on a set of futures) and there is no point pulling in an
- additional dependency just for this. In any case it helps somewhat
- (50-100% speedup) and provides a proof of concept for parallel testing.
- */
-
- typedef std::future<std::vector<Test::Result>> FutureResults;
- std::deque<FutureResults> fut_results;
-
- for(auto&& test_name : tests_to_run)
- {
- fut_results.push_back(std::async(std::launch::async,
- [test_name]() { return Test::run_test(test_name, false); }));
-
- while(fut_results.size() > threads)
- {
- out << report_out(fut_results[0].get(), tests_failed, tests_ran) << std::flush;
- fut_results.pop_front();
- }
- }
-
- while(fut_results.size() > 0)
- {
- out << report_out(fut_results[0].get(), tests_failed, tests_ran) << std::flush;
- fut_results.pop_front();
- }
- }
-
- out << "Tests complete ran " << tests_ran << " tests ";
-
- if(tests_failed > 0)
- {
- out << tests_failed << " tests failed";
- }
- else if(tests_ran > 0)
- {
- out << "all tests ok";
- }
-
- out << std::endl;
-
- return tests_failed;
- }
-
std::unique_ptr<Botan::RandomNumberGenerator>
-setup_tests(std::ostream& out, size_t threads, size_t soak_level, bool log_success, std::string drbg_seed)
+setup_tests(std::ostream& out, size_t threads,
+ size_t soak_level,
+ bool log_success,
+ std::string drbg_seed)
{
out << "Testing " << Botan::version_string() << "\n";
out << "Starting tests";
@@ -190,60 +113,166 @@ setup_tests(std::ostream& out, size_t threads, size_t soak_level, bool log_succe
return rng;
}
-int cpp_main(const std::vector<std::string> args)
+class Test_Runner : public Botan_CLI::Command
{
- try
- {
- if(args.size() == 2 && (args[1] == "--help" || args[1] == "help"))
+ public:
+ Test_Runner() : Command("test --threads=0 --soak=5 --drbg-seed= --log-success *suites") {}
+
+ std::string help_text() const override
{
- return help(std::cout, args[0]);
- }
+ std::ostringstream err;
+
+ err << "Usage: botan-test [--drbg-seed=] [--threads=N] [--log-success] "
+ << "suite suite ...\n\n"
+ << "Available suites\n"
+ << "----------------\n";
+
+ size_t line_len = 0;
+
+ for(auto&& test : Test::registered_tests())
+ {
+ err << test << " ";
+ line_len += test.size() + 1;
+
+ if(line_len > 64)
+ {
+ err << "\n";
+ line_len = 0;
+ }
+ }
- size_t threads = 0;//std::thread::hardware_concurrency();
- size_t soak = 5;
- const std::string drbg_seed = "";
- bool log_success = false;
+ if(line_len > 0)
+ {
+ err << "\n";
+ }
- std::vector<std::string> req(args.begin()+1, args.end());
+ return err.str();
+ }
- if(req.empty())
+ void go() override
{
- req = {"block", "stream", "hash", "mac", "modes", "aead", "kdf", "pbkdf", "hmac_drbg", "x931_rng", "util"};
+ const size_t threads = get_arg_sz("threads");
+ const size_t soak = get_arg_sz("soak");
+ const std::string drbg_seed = get_arg("drbg-seed");
+ bool log_success = flag_set("log-success");
+
+ std::vector<std::string> req = get_arg_list("suites");
- std::set<std::string> all_others = Botan_Tests::Test::registered_tests();
+ if(req.empty())
+ {
+ /*
+ If nothing was requested on the command line, run everything. First
+ run the "essentials" to smoke test, then everything else in
+ alphabetical order.
+ */
+ req = {"block", "stream", "hash", "mac", "modes", "aead"
+ "kdf", "pbkdf", "hmac_drbg", "x931_rng", "util"};
+
+ std::set<std::string> all_others = Botan_Tests::Test::registered_tests();
+
+ for(auto f : req)
+ {
+ all_others.erase(f);
+ }
+
+ req.insert(req.end(), all_others.begin(), all_others.end());
+ }
+
+ std::unique_ptr<Botan::RandomNumberGenerator> rng =
+ setup_tests(std::cout, threads, soak, log_success, drbg_seed);
- for(auto f : req)
- all_others.erase(f);
+ size_t failed = run_tests(req, std::cout, threads);
- req.insert(req.end(), all_others.begin(), all_others.end());
+ // Throw so main returns an error
+ if(failed)
+ throw Botan_Tests::Test_Error("Test suite failure");
}
+ private:
+ size_t run_tests(const std::vector<std::string>& tests_to_run,
+ std::ostream& out,
+ size_t threads)
+ {
+ size_t tests_ran = 0, tests_failed = 0;
- std::unique_ptr<Botan::RandomNumberGenerator> rng =
- setup_tests(std::cout, threads, soak, log_success, drbg_seed);
+ if(threads <= 1)
+ {
+ for(auto&& test_name : tests_to_run)
+ {
+ std::vector<Test::Result> results = Test::run_test(test_name, false);
+ out << report_out(results, tests_failed, tests_ran) << std::flush;
+ }
+ }
+ else
+ {
- size_t failed = run_tests(req, std::cout, threads);
+ /*
+ We're not doing this in a particularly nice way, and variance in time is
+ high so commonly we'll 'run dry' by blocking on the first future. But
+ plain C++11 <thread> is missing a lot of tools we'd need (like
+ wait_for_any on a set of futures) and there is no point pulling in an
+ additional dependency just for this. In any case it helps somewhat
+ (50-100% speedup) and provides a proof of concept for parallel testing.
+ */
+
+ typedef std::future<std::vector<Test::Result>> FutureResults;
+ std::deque<FutureResults> fut_results;
+
+ for(auto&& test_name : tests_to_run)
+ {
+ fut_results.push_back(std::async(std::launch::async,
+ [test_name]() { return Test::run_test(test_name, false); }));
+
+ while(fut_results.size() > threads)
+ {
+ out << report_out(fut_results[0].get(), tests_failed, tests_ran) << std::flush;
+ fut_results.pop_front();
+ }
+ }
+
+ while(fut_results.size() > 0)
+ {
+ out << report_out(fut_results[0].get(), tests_failed, tests_ran) << std::flush;
+ fut_results.pop_front();
+ }
+ }
- if(failed)
- return 2;
+ out << "Tests complete ran " << tests_ran << " tests ";
- return 0;
- }
- catch(std::exception& e)
- {
- std::cout << "Exception caused test abort: " << e.what() << std::endl;
- return 3;
- }
- catch(...)
- {
- std::cout << "Unknown exception caused test abort" << std::endl;
- return 3;
- }
- }
+ if(tests_failed > 0)
+ {
+ out << tests_failed << " tests failed";
+ }
+ else if(tests_ran > 0)
+ {
+ out << "all tests ok";
+ }
+
+ out << std::endl;
+
+ return tests_failed;
+ }
+
+
+ };
+
+BOTAN_REGISTER_COMMAND(Test_Runner);
}
int main(int argc, char* argv[])
{
- std::vector<std::string> args(argv, argv + argc);
- return cpp_main(args);
+ std::cerr << Botan::runtime_version_check(BOTAN_VERSION_MAJOR,
+ BOTAN_VERSION_MINOR,
+ BOTAN_VERSION_PATCH);
+
+ Botan_CLI::Command* cmd = Botan_CLI::Command::get_cmd("test");
+
+ if(!cmd)
+ {
+ std::cout << "Unable to retrieve testing helper (program bug)\n"; // WTF
+ return 1;
+ }
+
+ std::vector<std::string> args(argv + 1, argv + argc);
+ return cmd->run(args);
}
diff --git a/src/tests/test_stream.cpp b/src/tests/test_stream.cpp
index 618d34755..fb38ad677 100644
--- a/src/tests/test_stream.cpp
+++ b/src/tests/test_stream.cpp
@@ -17,7 +17,8 @@ namespace Botan_Tests {
class Stream_Cipher_Tests : public Text_Based_Test
{
public:
- Stream_Cipher_Tests(): Text_Based_Test(Test::data_dir("stream"), {"Key", "In", "Out"}, {"Nonce"}) {}
+ Stream_Cipher_Tests(): Text_Based_Test(Test::data_dir("stream"),
+ {"Key", "In", "Out"}, {"Nonce"}) {}
Test::Result run_one_test(const std::string& algo, const VarMap& vars) override
{
diff --git a/src/tests/tests.cpp b/src/tests/tests.cpp
index 360d671cc..111622dab 100644
--- a/src/tests/tests.cpp
+++ b/src/tests/tests.cpp
@@ -11,6 +11,7 @@
#include <botan/hex.h>
#include <botan/internal/filesystem.h>
#include <botan/internal/bit_ops.h>
+#include <botan/internal/stl_util.h>
namespace Botan_Tests {
@@ -237,7 +238,8 @@ bool Test::Result::test_ne(const std::string& what, const BigInt& produced, cons
#endif
#if defined(BOTAN_HAS_EC_CURVE_GFP)
-bool Test::Result::test_eq(const std::string& what, const Botan::PointGFp& a, const Botan::PointGFp& b)
+bool Test::Result::test_eq(const std::string& what,
+ const Botan::PointGFp& a, const Botan::PointGFp& b)
{
//return test_is_eq(what, a, b);
if(a == b)
@@ -358,21 +360,6 @@ std::map<std::string, std::unique_ptr<Test>>& Test::global_registry()
return g_test_registry;
}
-namespace {
-
-template<typename K, typename V>
-std::set<K> map_keys_as_set(const std::map<K, V>& kv)
- {
- std::set<K> s;
- for(auto&& i : kv)
- {
- s.insert(i.first);
- }
- return s;
- }
-
-}
-
//static
uint64_t Test::timestamp()
{
@@ -383,7 +370,7 @@ uint64_t Test::timestamp()
//static
std::set<std::string> Test::registered_tests()
{
- return map_keys_as_set(Test::global_registry());
+ return Botan::map_keys_as_set(Test::global_registry());
}
//static
@@ -730,7 +717,8 @@ std::vector<Test::Result> Text_Based_Test::run()
}
catch(std::exception& e)
{
- results.push_back(Test::Result::Failure(who, "test " + std::to_string(test_cnt) + " failed with exception '" + e.what() + "'"));
+ results.push_back(Test::Result::Failure(who, "test " + std::to_string(test_cnt) +
+ " failed with exception '" + e.what() + "'"));
}
if(clear_between_callbacks())
diff --git a/src/tests/tests.h b/src/tests/tests.h
index 333cbee98..71b839363 100644
--- a/src/tests/tests.h
+++ b/src/tests/tests.h
@@ -1,4 +1,3 @@
-
/*
* (C) 2014,2015 Jack Lloyd
* (C) 2015 Simon Warta (Kullo GmbH)
@@ -167,7 +166,11 @@ class Test
}
bool test_eq(const std::string& what, const char* produced, const char* expected);
- bool test_eq(const std::string& what, const std::string& produced, const std::string& expected);
+
+ bool test_eq(const std::string& what,
+ const std::string& produced,
+ const std::string& expected);
+
bool test_eq(const std::string& what, bool produced, bool expected);
bool test_eq(const std::string& what, size_t produced, size_t expected);
@@ -183,7 +186,9 @@ class Test
#endif
#if defined(BOTAN_HAS_EC_CURVE_GFP)
- bool test_eq(const std::string& what, const Botan::PointGFp& a, const Botan::PointGFp& b);
+ bool test_eq(const std::string& what,
+ const Botan::PointGFp& a,
+ const Botan::PointGFp& b);
#endif
bool test_eq(const char* producer, const std::string& what,
@@ -273,7 +278,8 @@ class Test
static std::string full_path_for_output_file(const std::string& base);
template<typename Alloc>
- static std::vector<uint8_t, Alloc> mutate_vec(const std::vector<uint8_t, Alloc>& v, bool maybe_resize = false)
+ static std::vector<uint8_t, Alloc>
+ mutate_vec(const std::vector<uint8_t, Alloc>& v, bool maybe_resize = false)
{
auto& rng = Test::rng();
@@ -314,7 +320,8 @@ class Test
/*
* Register the test with the runner
*/
-#define BOTAN_REGISTER_TEST(type, Test_Class) namespace { Test::Registration reg_ ## Test_Class ## _tests(type, new Test_Class); }
+#define BOTAN_REGISTER_TEST(type, Test_Class) \
+ namespace { Test::Registration reg_ ## Test_Class ## _tests(type, new Test_Class); }
/*
* A test based on reading an input file which contains key/value pairs
@@ -363,7 +370,9 @@ class Text_Based_Test : public Test
#endif
std::string get_req_str(const VarMap& vars, const std::string& key) const;
- std::string get_opt_str(const VarMap& vars, const std::string& key, const std::string& def_value) const;
+ std::string get_opt_str(const VarMap& vars,
+ const std::string& key,
+ const std::string& def_value) const;
size_t get_req_sz(const VarMap& vars, const std::string& key) const;
size_t get_opt_sz(const VarMap& vars, const std::string& key, const size_t def_value) const;