aboutsummaryrefslogtreecommitdiffstats
path: root/src/cli
diff options
context:
space:
mode:
Diffstat (limited to 'src/cli')
-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
45 files changed, 3067 insertions, 4221 deletions
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