From 7323f3ff83ff2199b1090f9d5f729b08ccac3151 Mon Sep 17 00:00:00 2001 From: lloyd Date: Wed, 1 Jan 2014 23:32:24 +0000 Subject: Move base64, bzip, ca, and tls examples --- src/apps/apps.h | 25 ++++ src/apps/asn1.cpp | 319 +++++++++++++++++++++++++++++++++++++++++++++ src/apps/base64.cpp | 85 ++++++++++++ src/apps/bcrypt.cpp | 32 +++++ src/apps/bzip.cpp | 115 ++++++++++++++++ src/apps/ca.cpp | 63 +++++++++ src/apps/credentials.h | 294 +++++++++++++++++++++++++++++++++++++++++ src/apps/factor.cpp | 153 ++++++++++++++++++++++ src/apps/fpe.cpp | 143 ++++++++++++++++++++ src/apps/hash.cpp | 58 +++++++++ src/apps/keygen.cpp | 53 ++++++++ src/apps/pkcs10.cpp | 48 +++++++ src/apps/read_ssh.cpp | 134 +++++++++++++++++++ src/apps/self_sig.cpp | 70 ++++++++++ src/apps/tls_client.cpp | 263 +++++++++++++++++++++++++++++++++++++ src/apps/tls_server.cpp | 262 +++++++++++++++++++++++++++++++++++++ src/apps/x509print.cpp | 17 +++ src/build-data/cc/gcc.txt | 2 +- src/examples/asn1.cpp | 319 --------------------------------------------- src/examples/bcrypt.cpp | 32 ----- src/examples/examples.h | 17 --- src/examples/factor.cpp | 153 ---------------------- src/examples/fpe.cpp | 143 -------------------- src/examples/read_ssh.cpp | 134 ------------------- src/examples/self_sig.cpp | 70 ---------- src/examples/x509print.cpp | 17 --- src/main.cpp | 116 +++++++---------- src/speed/speed.cpp | 40 ++++++ src/speed/speed.h | 2 + 29 files changed, 2221 insertions(+), 958 deletions(-) create mode 100644 src/apps/apps.h create mode 100644 src/apps/asn1.cpp create mode 100644 src/apps/base64.cpp create mode 100644 src/apps/bcrypt.cpp create mode 100644 src/apps/bzip.cpp create mode 100644 src/apps/ca.cpp create mode 100644 src/apps/credentials.h create mode 100644 src/apps/factor.cpp create mode 100644 src/apps/fpe.cpp create mode 100644 src/apps/hash.cpp create mode 100644 src/apps/keygen.cpp create mode 100644 src/apps/pkcs10.cpp create mode 100644 src/apps/read_ssh.cpp create mode 100644 src/apps/self_sig.cpp create mode 100644 src/apps/tls_client.cpp create mode 100644 src/apps/tls_server.cpp create mode 100644 src/apps/x509print.cpp delete mode 100644 src/examples/asn1.cpp delete mode 100644 src/examples/bcrypt.cpp delete mode 100644 src/examples/examples.h delete mode 100644 src/examples/factor.cpp delete mode 100644 src/examples/fpe.cpp delete mode 100644 src/examples/read_ssh.cpp delete mode 100644 src/examples/self_sig.cpp delete mode 100644 src/examples/x509print.cpp (limited to 'src') diff --git a/src/apps/apps.h b/src/apps/apps.h new file mode 100644 index 000000000..babbea080 --- /dev/null +++ b/src/apps/apps.h @@ -0,0 +1,25 @@ + +#include "../common.h" +#include +#include +#include + +using namespace Botan; + +#define DEFINE_EXAMPLE(cmd) int cmd (int argc, char* argv[]); + +DEFINE_EXAMPLE(asn1); +DEFINE_EXAMPLE(bcrypt); +DEFINE_EXAMPLE(bzip); +DEFINE_EXAMPLE(base64); +DEFINE_EXAMPLE(ca); +DEFINE_EXAMPLE(factor); +DEFINE_EXAMPLE(fpe); +DEFINE_EXAMPLE(hash); +DEFINE_EXAMPLE(keygen); +DEFINE_EXAMPLE(pkcs10); +DEFINE_EXAMPLE(read_ssh); +DEFINE_EXAMPLE(self_sig); +DEFINE_EXAMPLE(tls_client); +DEFINE_EXAMPLE(tls_server); +DEFINE_EXAMPLE(x509); diff --git a/src/apps/asn1.cpp b/src/apps/asn1.cpp new file mode 100644 index 000000000..447462ae0 --- /dev/null +++ b/src/apps/asn1.cpp @@ -0,0 +1,319 @@ +#include "apps.h" + +#include +#include +#include +#include +#include +#include +#include +#include +using namespace Botan; + +#include +#include +#include + +// Set this if your terminal understands UTF-8; otherwise output is in Latin-1 +#define UTF8_TERMINAL 1 + +/* + What level the outermost layer of stuff is at. Probably 0 or 1; asn1parse + uses 0 as the outermost, while 1 makes more sense to me. 2+ doesn't make + much sense at all. +*/ +#define INITIAL_LEVEL 0 + +void decode(BER_Decoder&, size_t); +void emit(const std::string&, size_t, size_t, const std::string& = ""); +std::string type_name(ASN1_Tag); + +namespace { + +std::string url_encode(const std::vector& in) + { + std::ostringstream out; + + for(size_t i = 0; i != in.size(); ++i) + { + const int c = in[i]; + if(isprint((int)c)) + out << (char)c; + else + out << "%" << std::hex << (int)c << std::dec; + } + return out.str(); + } + +} + +int asn1(int argc, char* argv[]) + { + if(argc != 2) + { + printf("Usage: %s \n", argv[0]); + return 1; + } + + try { + DataSource_Stream in(argv[1]); + + if(!PEM_Code::matches(in)) + { + BER_Decoder decoder(in); + decode(decoder, INITIAL_LEVEL); + } + else + { + std::string label; // ignored + BER_Decoder decoder(PEM_Code::decode(in, label)); + decode(decoder, INITIAL_LEVEL); + } + + } + catch(std::exception& e) + { + printf("%s\n", e.what()); + return 1; + } + return 0; + } + +void decode(BER_Decoder& decoder, size_t level) + { + BER_Object obj = decoder.get_next_object(); + + while(obj.type_tag != NO_OBJECT) + { + const ASN1_Tag type_tag = obj.type_tag; + const ASN1_Tag class_tag = obj.class_tag; + const size_t length = obj.value.size(); + + /* hack to insert the tag+length back in front of the stuff now + that we've gotten the type info */ + DER_Encoder encoder; + encoder.add_object(type_tag, class_tag, obj.value); + std::vector bits = encoder.get_contents_unlocked(); + + BER_Decoder data(bits); + + if(class_tag & CONSTRUCTED) + { + BER_Decoder cons_info(obj.value); + if(type_tag == SEQUENCE) + { + emit("SEQUENCE", level, length); + decode(cons_info, level+1); + } + else if(type_tag == SET) + { + emit("SET", level, length); + decode(cons_info, level+1); + } + else + { + std::string name; + + if((class_tag & APPLICATION) || (class_tag & CONTEXT_SPECIFIC)) + { + name = "cons [" + std::to_string(type_tag) + "]"; + + if(class_tag & APPLICATION) + name += " appl"; + if(class_tag & CONTEXT_SPECIFIC) + name += " context"; + } + else + name = type_name(type_tag) + " (cons)"; + + emit(name, level, length); + decode(cons_info, level+1); + } + } + else if((class_tag & APPLICATION) || (class_tag & CONTEXT_SPECIFIC)) + { +#if 0 + std::vector bits; + data.decode(bits, type_tag); + + try + { + BER_Decoder inner(bits); + decode(inner, level + 1); + } + catch(...) + { + emit("[" + std::to_string(type_tag) + "]", level, length, + url_encode(bits)); + } +#else + emit("[" + std::to_string(type_tag) + "]", level, length, + url_encode(bits)); +#endif + } + else if(type_tag == OBJECT_ID) + { + OID oid; + data.decode(oid); + + std::string out = OIDS::lookup(oid); + if(out != oid.as_string()) + out += " [" + oid.as_string() + "]"; + + emit(type_name(type_tag), level, length, out); + } + else if(type_tag == INTEGER || type_tag == ENUMERATED) + { + BigInt number; + + if(type_tag == INTEGER) + data.decode(number); + else if(type_tag == ENUMERATED) + data.decode(number, ENUMERATED, class_tag); + + std::vector rep; + + /* If it's small, it's probably a number, not a hash */ + if(number.bits() <= 16) + rep = BigInt::encode(number, BigInt::Decimal); + else + rep = BigInt::encode(number, BigInt::Hexadecimal); + + std::string str; + for(size_t i = 0; i != rep.size(); ++i) + str += (char)rep[i]; + + emit(type_name(type_tag), level, length, str); + } + else if(type_tag == BOOLEAN) + { + bool boolean; + data.decode(boolean); + emit(type_name(type_tag), + level, length, (boolean ? "true" : "false")); + } + else if(type_tag == NULL_TAG) + { + emit(type_name(type_tag), level, length); + } + else if(type_tag == OCTET_STRING) + { + std::vector bits; + data.decode(bits, type_tag); + + try + { + BER_Decoder inner(bits); + decode(inner, level + 1); + } + catch(...) + { + emit(type_name(type_tag), level, length, + url_encode(bits)); + } + } + else if(type_tag == BIT_STRING) + { + std::vector bits; + data.decode(bits, type_tag); + + std::vector bit_set; + + for(size_t i = 0; i != bits.size(); ++i) + for(size_t j = 0; j != 8; ++j) + bit_set.push_back((bool)((bits[bits.size()-i-1] >> (7-j)) & 1)); + + std::string bit_str; + for(size_t i = 0; i != bit_set.size(); ++i) + { + bool the_bit = bit_set[bit_set.size()-i-1]; + + if(!the_bit && bit_str.size() == 0) + continue; + bit_str += (the_bit ? "1" : "0"); + } + + emit(type_name(type_tag), level, length, bit_str); + } + else if(type_tag == PRINTABLE_STRING || + type_tag == NUMERIC_STRING || + type_tag == IA5_STRING || + type_tag == T61_STRING || + type_tag == VISIBLE_STRING || + type_tag == UTF8_STRING || + type_tag == BMP_STRING) + { + ASN1_String str; + data.decode(str); + if(UTF8_TERMINAL) + emit(type_name(type_tag), level, length, + Charset::transcode(str.iso_8859(), + LATIN1_CHARSET, UTF8_CHARSET)); + else + emit(type_name(type_tag), level, length, str.iso_8859()); + } + else if(type_tag == UTC_TIME || type_tag == GENERALIZED_TIME) + { + X509_Time time; + data.decode(time); + emit(type_name(type_tag), level, length, time.readable_string()); + } + else + fprintf(stderr, "Unknown tag: class=%02X, type=%02X\n", + class_tag, type_tag); + + obj = decoder.get_next_object(); + } + } + +void emit(const std::string& type, size_t level, size_t length, + const std::string& value) + { + const size_t LIMIT = 128; + const size_t BIN_LIMIT = 64; + + int written = 0; + written += printf(" d=%2d, l=%4d: ", (int)level, (int)length); + for(size_t i = INITIAL_LEVEL; i != level; ++i) + written += printf(" "); + written += printf("%s ", type.c_str()); + + bool should_skip = false; + if(value.length() > LIMIT) should_skip = true; + if((type == "OCTET STRING" || type == "BIT STRING") && + value.length() > BIN_LIMIT) + should_skip = true; + + if(value != "" && !should_skip) + { + if(written % 2 == 0) printf(" "); + while(written < 50) written += printf(" "); + printf(":%s\n", value.c_str()); + } + else + printf("\n"); + } + +std::string type_name(ASN1_Tag type) + { + if(type == PRINTABLE_STRING) return "PRINTABLE STRING"; + if(type == NUMERIC_STRING) return "NUMERIC STRING"; + if(type == IA5_STRING) return "IA5 STRING"; + if(type == T61_STRING) return "T61 STRING"; + if(type == UTF8_STRING) return "UTF8 STRING"; + if(type == VISIBLE_STRING) return "VISIBLE STRING"; + if(type == BMP_STRING) return "BMP STRING"; + + if(type == UTC_TIME) return "UTC TIME"; + if(type == GENERALIZED_TIME) return "GENERALIZED TIME"; + + if(type == OCTET_STRING) return "OCTET STRING"; + if(type == BIT_STRING) return "BIT STRING"; + + if(type == ENUMERATED) return "ENUMERATED"; + if(type == INTEGER) return "INTEGER"; + if(type == NULL_TAG) return "NULL"; + if(type == OBJECT_ID) return "OBJECT"; + if(type == BOOLEAN) return "BOOLEAN"; + return "(UNKNOWN)"; + } diff --git a/src/apps/base64.cpp b/src/apps/base64.cpp new file mode 100644 index 000000000..697fcb42c --- /dev/null +++ b/src/apps/base64.cpp @@ -0,0 +1,85 @@ +/* +* Encode/decode base64 strings +* (C) 2009 Jack Lloyd +* +* Distributed under the terms of the Botan license +*/ + +#include "apps.h" +#include +#include +#include +#include +#include +#include +#include +#include + +int base64(int argc, char* argv[]) + { + if(argc < 2) + { + std::cout << "Usage: " << argv[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\n"; + return 1; + } + + int column = 78; + bool wrap = false; + bool encoding = true; + std::vector files; + + for(int j = 1; argv[j] != nullptr; j++) + { + std::string this_arg = argv[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(argv[j+1]) + { column = atoi(argv[j+1]); j++; } + else + { + std::cout << "No argument for -c option" << std::endl; + return 1; + } + } + else files.push_back(argv[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].c_str()); + + 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; + } diff --git a/src/apps/bcrypt.cpp b/src/apps/bcrypt.cpp new file mode 100644 index 000000000..497111363 --- /dev/null +++ b/src/apps/bcrypt.cpp @@ -0,0 +1,32 @@ +#include "apps.h" +#include + +int bcrypt(int argc, char* argv[]) + { + if(argc == 2) + { + AutoSeeded_RNG rng; + + const std::string password = argv[1]; + + std::cout << generate_bcrypt(password, rng, 12) << "\n"; + return 0; + } + else if(argc == 3) + { + const std::string password = argv[1]; + const std::string hash = argv[2]; + + if(hash.length() != 60) + std::cout << "Note: bcrypt '" << hash << "' has wrong length and cannot be valid\n"; + + const bool ok = check_bcrypt(password, hash); + + std::cout << "Password is " << (ok ? "valid" : "NOT valid") << "\n"; + return (ok ? 0 : 1); + } + + std::cout << "Usage: " << argv[0] << " password\n" + << " " << argv[0] << " password passhash\n"; + return 1; + } diff --git a/src/apps/bzip.cpp b/src/apps/bzip.cpp new file mode 100644 index 000000000..94a8bdff7 --- /dev/null +++ b/src/apps/bzip.cpp @@ -0,0 +1,115 @@ +/* +* Bzip2 Compression/Decompression +* (C) 2009 Jack Lloyd +* +* Distributed under the terms of the Botan license +*/ + +#include "apps.h" + +#include +#include +#include +#include +#include +#include + +/* +* If Bzip2 isn't included, we know nothing works at compile time, but +* we wait to fail at runtime. Otherwise I would get 2-3 mails a month +* about how this was failing to compile (even with an informative +* #error message explaining the situation) because bzip2 wasn't +* included in the build. +*/ + +#if defined(BOTAN_HAS_COMPRESSOR_BZIP2) + #include +#endif + +const std::string SUFFIX = ".bz2"; + +int bzip(int argc, char* argv[]) + { + if(argc < 2) + { + std::cout << "Usage: " << argv[0] + << " [-s] [-d] [-1...9] " << std::endl; + return 1; + } + +#ifdef BOTAN_HAS_COMPRESSOR_BZIP2 + std::vector files; + bool decompress = false, small = false; + int level = 9; + + for(int j = 1; argv[j] != 0; j++) + { + if(std::strcmp(argv[j], "-d") == 0) { decompress = true; continue; } + if(std::strcmp(argv[j], "-s") == 0) { small = true; continue; } + if(std::strcmp(argv[j], "-1") == 0) { level = 1; continue; } + if(std::strcmp(argv[j], "-2") == 0) { level = 2; continue; } + if(std::strcmp(argv[j], "-3") == 0) { level = 3; continue; } + if(std::strcmp(argv[j], "-4") == 0) { level = 4; continue; } + if(std::strcmp(argv[j], "-5") == 0) { level = 5; continue; } + if(std::strcmp(argv[j], "-6") == 0) { level = 6; continue; } + if(std::strcmp(argv[j], "-7") == 0) { level = 7; continue; } + if(std::strcmp(argv[j], "-8") == 0) { level = 8; continue; } + if(std::strcmp(argv[j], "-9") == 0) { level = 9; continue; } + files.push_back(argv[j]); + } + + try { + + Botan::Filter* bzip = 0; + if(decompress) + bzip = new Botan::Bzip_Decompression(small); + else + bzip = new Botan::Bzip_Compression(level); + + Botan::Pipe pipe(bzip); + + for(unsigned int j = 0; j != files.size(); j++) + { + std::string infile = files[j], outfile = files[j]; + if(!decompress) + outfile = outfile += SUFFIX; + else + outfile = outfile.replace(outfile.find(SUFFIX), + SUFFIX.length(), ""); + + std::ifstream in(infile.c_str(), std::ios::binary); + std::ofstream out(outfile.c_str(), std::ios::binary); + if(!in) + { + std::cout << "ERROR: could not read " << infile << std::endl; + continue; + } + if(!out) + { + std::cout << "ERROR: could not write " << outfile << std::endl; + continue; + } + + pipe.start_msg(); + in >> pipe; + pipe.end_msg(); + pipe.set_default_msg(j); + out << pipe; + + in.close(); + out.close(); + return 0; + } + } + catch(std::exception& e) + { + std::cout << "Exception caught: " << e.what() << std::endl; + return 1; + } +#else + + std::cout << "Sorry, support for bzip2 not compiled into Botan\n"; +#endif + + return 1; + } diff --git a/src/apps/ca.cpp b/src/apps/ca.cpp new file mode 100644 index 000000000..881036fc8 --- /dev/null +++ b/src/apps/ca.cpp @@ -0,0 +1,63 @@ + +#include "apps.h" +#include +using namespace Botan; + +#include +#include +#include + +int ca(int argc, char* argv[]) + { + if(argc != 5) + { + std::cout << "Usage: " << argv[0] << " " + << " " << std::endl; + return 1; + } + + try + { + const std::string arg_passphrase = argv[1]; + const std::string arg_ca_cert = argv[2]; + const std::string arg_ca_key = argv[3]; + const std::string arg_req_file = argv[4]; + + AutoSeeded_RNG rng; + + X509_Certificate ca_cert(arg_ca_cert); + + std::auto_ptr 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> 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; + } diff --git a/src/apps/credentials.h b/src/apps/credentials.h new file mode 100644 index 000000000..6c150f881 --- /dev/null +++ b/src/apps/credentials.h @@ -0,0 +1,294 @@ + +#ifndef EXAMPLE_CREDENTIALS_MANAGER_H__ +#define EXAMPLE_CREDENTIALS_MANAGER_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +inline bool value_exists(const std::vector& vec, + const std::string& val) + { + for(size_t i = 0; i != vec.size(); ++i) + if(vec[i] == val) + return true; + return false; + } + +class Credentials_Manager_Simple : public Botan::Credentials_Manager + { + public: + Credentials_Manager_Simple(Botan::RandomNumberGenerator& rng) : + rng(rng) + { + m_certstores.push_back(new Botan::Certificate_Store_In_Memory("/usr/share/ca-certificates")); + } + + std::string srp_identifier(const std::string& type, + const std::string& hostname) + { + if(type == "tls-client" && hostname == "srp-host") + return "user"; + return ""; + } + + bool attempt_srp(const std::string& type, + const std::string& hostname) + { + if(hostname == "srp-host" && (type == "tls-client" || type == "tls-server")) + return true; + return false; + } + + std::vector + trusted_certificate_authorities(const std::string& type, + const std::string& /*hostname*/) + { + // don't ask for client cert + if(type == "tls-server") + return std::vector(); + + return m_certstores; + } + + void verify_certificate_chain( + const std::string& type, + const std::string& purported_hostname, + const std::vector& cert_chain) + { + try + { + Botan::Credentials_Manager::verify_certificate_chain(type, + purported_hostname, + cert_chain); + } + catch(std::exception& e) + { + std::cout << "Certificate verification failed - " << e.what() << " - but will ignore\n"; + } + } + + std::string srp_password(const std::string& type, + const std::string& hostname, + const std::string& identifier) + { + if(type == "tls-client" && hostname == "srp-host" && identifier == "user") + return "password"; + + return ""; + } + + bool srp_verifier(const std::string& /*type*/, + const std::string& context, + const std::string& identifier, + std::string& group_id, + Botan::BigInt& verifier, + std::vector& salt, + bool generate_fake_on_unknown) + { + + std::string pass = srp_password("tls-client", context, identifier); + if(pass == "") + { + if(!generate_fake_on_unknown) + return false; + + pass.resize(16); + rng.randomize(reinterpret_cast(&pass[0]), pass.size()); + } + + group_id = "modp/srp/2048"; + + salt.resize(16); + rng.randomize(&salt[0], salt.size()); + + verifier = Botan::generate_srp6_verifier(identifier, + pass, + salt, + group_id, + "SHA-1"); + + return true; + } + + std::string psk_identity_hint(const std::string&, + const std::string&) + { + return ""; + } + + std::string psk_identity(const std::string&, const std::string&, + const std::string& identity_hint) + { + std::cout << "Server sent PSK identity_hint " << identity_hint << "\n"; + return "Client_identity"; + } + + Botan::SymmetricKey psk(const std::string& type, const std::string& context, + const std::string& identity) + { + if(type == "tls-server" && context == "session-ticket") + { + if(session_ticket_key.length() == 0) + session_ticket_key = Botan::SymmetricKey(rng, 32); + return session_ticket_key; + } + + if(identity == "Client_identity") + return Botan::SymmetricKey("b5a72e1387552e6dc10766dc0eda12961f5b21e17f98ef4c41e6572e53bd7527"); + if(identity == "lloyd") + return Botan::SymmetricKey("85b3c1b7dc62b507636ac767999c9630"); + + throw Botan::Internal_Error("No PSK set for " + identity); + } + + std::pair + load_or_make_cert(const std::string& hostname, + const std::string& key_type, + Botan::RandomNumberGenerator& rng) + { + using namespace Botan; + + const std::string key_fsname_prefix = hostname + "." + key_type + "."; + const std::string key_file_name = key_fsname_prefix + "key"; + const std::string cert_file_name = key_fsname_prefix + "crt"; + + try + { + X509_Certificate cert(cert_file_name); + Private_Key* key = PKCS8::load_key(key_file_name, rng); + + //std::cout << "Loaded existing key/cert from " << cert_file_name << " and " << key_file_name << "\n"; + + return std::make_pair(cert, key); + } + catch(...) {} + + // Failed. Instead, make a new one + + std::cout << "Creating new certificate for identifier '" << hostname << "'\n"; + + X509_Cert_Options opts; + + opts.common_name = hostname; + opts.country = "US"; + opts.email = "root@" + hostname; + opts.dns = hostname; + + std::unique_ptr key; + if(key_type == "rsa") + key.reset(new RSA_PrivateKey(rng, 1024)); + else if(key_type == "dsa") + key.reset(new DSA_PrivateKey(rng, DL_Group("dsa/jce/1024"))); + else if(key_type == "ecdsa") + key.reset(new ECDSA_PrivateKey(rng, EC_Group("secp256r1"))); + else + throw std::runtime_error("Don't know what to do about key type '" + key_type + "'"); + + X509_Certificate cert = + X509::create_self_signed_cert(opts, *key, "SHA-1", rng); + + // Now save both + + std::cout << "Saving new " << key_type << " key to " << key_file_name << "\n"; + std::ofstream key_file(key_file_name.c_str()); + key_file << PKCS8::PEM_encode(*key, rng, ""); + key_file.close(); + + std::cout << "Saving new " << key_type << " cert to " << key_file_name << "\n"; + std::ofstream cert_file(cert_file_name.c_str()); + cert_file << cert.PEM_encode() << "\n"; + cert_file.close(); + + return std::make_pair(cert, key.release()); + } + + std::vector cert_chain( + const std::vector& cert_key_types, + const std::string& type, + const std::string& context) + { + using namespace Botan; + + std::vector certs; + + try + { + if(type == "tls-server") + { + const std::string hostname = (context == "" ? "localhost" : context); + + if(hostname == "nosuchname") + return std::vector(); + + for(auto i : certs_and_keys) + { + if(hostname != "" && !i.first.matches_dns_name(hostname)) + continue; + + if(!value_exists(cert_key_types, i.second->algo_name())) + continue; + + certs.push_back(i.first); + } + + if(!certs.empty()) + return certs; + + std::string key_name = ""; + + if(value_exists(cert_key_types, "RSA")) + key_name = "rsa"; + else if(value_exists(cert_key_types, "DSA")) + key_name = "dsa"; + else if(value_exists(cert_key_types, "ECDSA")) + key_name = "ecdsa"; + + std::pair cert_and_key = + load_or_make_cert(hostname, key_name, rng); + + certs_and_keys[cert_and_key.first] = cert_and_key.second; + certs.push_back(cert_and_key.first); + } + else if(type == "tls-client") + { + X509_Certificate cert("user-rsa.crt"); + Private_Key* key = PKCS8::load_key("user-rsa.key", rng); + + certs_and_keys[cert] = key; + certs.push_back(cert); + } + } + catch(std::exception& e) + { + std::cout << e.what() << "\n"; + } + + return certs; + } + + Botan::Private_Key* private_key_for(const Botan::X509_Certificate& cert, + const std::string& /*type*/, + const std::string& /*context*/) + { + return certs_and_keys[cert]; + } + + private: + Botan::RandomNumberGenerator& rng; + + Botan::SymmetricKey session_ticket_key; + + std::map certs_and_keys; + + std::vector m_certstores; + }; + +#endif diff --git a/src/apps/factor.cpp b/src/apps/factor.cpp new file mode 100644 index 000000000..484f024d8 --- /dev/null +++ b/src/apps/factor.cpp @@ -0,0 +1,153 @@ +/* +* (C) 2009-2010 Jack Lloyd +* +* Distributed under the terms of the Botan license +* +* Factor integers using a combination of trial division by small +* primes, and Pollard's Rho algorithm +*/ + +#include "apps.h" + +#include +#include +using namespace Botan; + +#include +#include +#include + +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 remove_small_factors(BigInt& n) + { + std::vector 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 factorize(const BigInt& n_in, + RandomNumberGenerator& rng) + { + BigInt n = n_in; + std::vector factors = remove_small_factors(n); + + while(n != 1) + { + if(check_prime(n, rng)) + { + factors.push_back(n); + break; + } + + BigInt a_factor = 0; + while(a_factor == 0) + a_factor = rho(n, rng); + + std::vector 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(int argc, char* argv[]) + { + if(argc != 2) + { + std::cout << "Usage: " << argv[0] << " \n"; + return 1; + } + + try + { + BigInt n(argv[1]); + + AutoSeeded_RNG rng; + + std::vector factors = factorize(n, rng); + std::sort(factors.begin(), factors.end()); + + std::cout << n << ": "; + std::copy(factors.begin(), + factors.end(), + std::ostream_iterator(std::cout, " ")); + std::cout << "\n"; + } + catch(std::exception& e) + { + std::cout << e.what() << std::endl; + return 1; + } + return 0; + } diff --git a/src/apps/fpe.cpp b/src/apps/fpe.cpp new file mode 100644 index 000000000..dd5b9b682 --- /dev/null +++ b/src/apps/fpe.cpp @@ -0,0 +1,143 @@ +#include "apps.h" +#include +#include + +using namespace Botan; + +#include +#include + +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 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(int argc, char* argv[]) + { + if(argc != 4) + { + std::cout << "Usage: " << argv[0] << " cc-number acct-name passwd\n"; + return 1; + } + + u64bit cc_number = atoll(argv[1]); + std::string acct_name = argv[2]; + std::string passwd = argv[3]; + + std::cout << "Input was: " << cc_number << ' ' + << luhn_check(cc_number) << '\n'; + + /* + * 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) << '\n'; + + u64bit dec_cc = decrypt_cc_number(enc_cc, key, acct_name); + + std::cout << "Decrypted: " << dec_cc + << ' ' << luhn_check(dec_cc) << '\n'; + + if(dec_cc != cc_number) + { + std::cout << "Something went wrong :( Bad CC checksum?\n"; + return 2; + } + + return 0; + } diff --git a/src/apps/hash.cpp b/src/apps/hash.cpp new file mode 100644 index 000000000..c1e5aab3e --- /dev/null +++ b/src/apps/hash.cpp @@ -0,0 +1,58 @@ +/* +* (C) 2009 Jack Lloyd +* +* Distributed under the terms of the Botan license +*/ + +#include "apps.h" +#include +#include +#include + +using namespace Botan; + +int hash(int argc, char* argv[]) + { + if(argc < 3) + { + std::cout << "Usage: " << argv[0] << " digest " << std::endl; + return 1; + } + + std::string hash = argv[1]; + /* a couple of special cases, kind of a crock */ + if(hash == "sha1") hash = "SHA-1"; + if(hash == "md5") hash = "MD5"; + + try { + if(!have_hash(hash)) + { + std::cout << "Unknown hash \"" << argv[1] << "\"" << std::endl; + return 1; + } + + Pipe pipe(new Hash_Filter(hash), new Hex_Encoder); + + int skipped = 0; + for(int j = 2; argv[j] != nullptr; j++) + { + std::ifstream file(argv[j], std::ios::binary); + if(!file) + { + std::cout << "ERROR: could not open " << argv[j] << std::endl; + skipped++; + continue; + } + pipe.start_msg(); + file >> pipe; + pipe.end_msg(); + pipe.set_default_msg(j-2-skipped); + std::cout << pipe << " " << argv[j] << std::endl; + } + } + catch(std::exception& e) + { + std::cout << "Exception caught: " << e.what() << std::endl; + } + return 0; + } diff --git a/src/apps/keygen.cpp b/src/apps/keygen.cpp new file mode 100644 index 000000000..885364834 --- /dev/null +++ b/src/apps/keygen.cpp @@ -0,0 +1,53 @@ +#include "apps.h" +#include +#include +#include +#include +#include + +#include +using namespace Botan; + +int keygen(int argc, char* argv[]) + { + if(argc != 2 && argc != 3) + { + std::cout << "Usage: " << argv[0] << " bitsize [passphrase]" + << std::endl; + return 1; + } + + const size_t bits = std::atoi(argv[1]); + if(bits < 1024 || bits > 16384) + { + std::cout << "Invalid argument for bitsize" << std::endl; + return 1; + } + + try + { + std::ofstream pub("rsapub.pem"); + std::ofstream priv("rsapriv.pem"); + if(!priv || !pub) + { + std::cout << "Couldn't write output files" << std::endl; + return 1; + } + + AutoSeeded_RNG rng; + + RSA_PrivateKey key(rng, bits); + pub << X509::PEM_encode(key); + + if(argc == 2) + priv << PKCS8::PEM_encode(key); + else + priv << PKCS8::PEM_encode(key, rng, argv[2]); + } + catch(std::exception& e) + { + std::cout << "Exception caught: " << e.what() << std::endl; + } + + return 0; + } diff --git a/src/apps/pkcs10.cpp b/src/apps/pkcs10.cpp new file mode 100644 index 000000000..6e36f73fb --- /dev/null +++ b/src/apps/pkcs10.cpp @@ -0,0 +1,48 @@ +#include "apps.h" +#include +#include +#include +using namespace Botan; + +#include +#include +#include + +int pkcs10(int argc, char* argv[]) + { + if(argc != 6) + { + std::cout << "Usage: " << argv[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, argv[1]); + + X509_Cert_Options opts; + + opts.common_name = argv[2]; + opts.country = argv[3]; + opts.organization = argv[4]; + opts.email = argv[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; + } diff --git a/src/apps/read_ssh.cpp b/src/apps/read_ssh.cpp new file mode 100644 index 000000000..ce72fa064 --- /dev/null +++ b/src/apps/read_ssh.cpp @@ -0,0 +1,134 @@ +/* +* (C) 2009 Jack Lloyd +* +* Distributed under the terms of the Botan license +*/ + +/* +* Example of reading SSH2 format public keys (see RFC 4716) +*/ + +#include "apps.h" + +#include +#include +#include +#include +#include +#include +#include + +using namespace Botan; + +namespace { + +u32bit read_u32bit(Pipe& pipe) + { + byte out[4] = { 0 }; + pipe.read(out, 4); + u32bit len = load_be(out, 0); + if(len > 10000) + throw Decoding_Error("Huge size in read_u32bit, something went wrong"); + return len; + } + +std::string read_string(Pipe& pipe) + { + u32bit len = read_u32bit(pipe); + + std::string out(len, 'X'); + pipe.read(reinterpret_cast(&out[0]), len); + return out; + } + +BigInt read_bigint(Pipe& pipe) + { + u32bit len = read_u32bit(pipe); + + secure_vector buf(len); + pipe.read(&buf[0], len); + return BigInt::decode(buf); + } + +Public_Key* read_ssh_pubkey(const std::string& file) + { + std::ifstream in(file.c_str()); + + const std::string ssh_header = "---- BEGIN SSH2 PUBLIC KEY ----"; + const std::string ssh_trailer = "---- END SSH2 PUBLIC KEY ----"; + + std::string hex_bits; + + std::string line; + std::getline(in, line); + + if(line != ssh_header) + return nullptr; + + while(in.good()) + { + std::getline(in, line); + + if(line.find("Comment: ") == 0) + { + while(line[line.size()-1] == '\\') + std::getline(in, line); + std::getline(in, line); + } + + if(line == ssh_trailer) + break; + + hex_bits += line; + } + + Pipe pipe(new Base64_Decoder); + pipe.process_msg(hex_bits); + + std::string key_type = read_string(pipe); + + if(key_type != "ssh-rsa" && key_type != "ssh-dss") + return nullptr; + + if(key_type == "ssh-rsa") + { + BigInt e = read_bigint(pipe); + BigInt n = read_bigint(pipe); + return new RSA_PublicKey(n, e); + } + else if(key_type == "ssh-dss") + { + BigInt p = read_bigint(pipe); + BigInt q = read_bigint(pipe); + BigInt g = read_bigint(pipe); + BigInt y = read_bigint(pipe); + + return new DSA_PublicKey(DL_Group(p, q, g), y); + } + + return nullptr; + } + +} + +int read_ssh(int argc, char* argv[]) + { + if(argc != 2) + { + std::cout << "Usage: " << argv[0] << " file"; + return 1; + } + + const std::string filename = argv[1]; + std::unique_ptr key(read_ssh_pubkey(filename)); + + if(key == 0) + { + std::cout << "Failed to read" << filename << "\n"; + return 1; + } + + std::cout << X509::PEM_encode(*key); + + return 0; + } diff --git a/src/apps/self_sig.cpp b/src/apps/self_sig.cpp new file mode 100644 index 000000000..c4202442c --- /dev/null +++ b/src/apps/self_sig.cpp @@ -0,0 +1,70 @@ +#include "apps.h" +#include +#include +#include +using namespace Botan; + +#include +#include +#include + +int self_sig(int argc, char* argv[]) + { + if(argc != 7) + { + std::cout << "Usage: " << argv[0] + << " passphrase [CA|user] name country_code organization email" + << std::endl; + return 1; + } + + std::string CA_flag = argv[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); + + //DSA_PrivateKey key(rng, group); + + std::ofstream priv_key("private.pem"); + priv_key << PKCS8::PEM_encode(key, rng, argv[1]); + + X509_Cert_Options opts; + + opts.common_name = argv[3]; + opts.country = argv[4]; + opts.organization = argv[5]; + opts.email = argv[6]; + /* Fill in other values of opts here */ + + //opts.xmpp = "lloyd@randombit.net"; + + 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; + } diff --git a/src/apps/tls_client.cpp b/src/apps/tls_client.cpp new file mode 100644 index 000000000..24c8197f6 --- /dev/null +++ b/src/apps/tls_client.cpp @@ -0,0 +1,263 @@ +#include "apps.h" +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#if !defined(MSG_NOSIGNAL) + #define MSG_NOSIGNAL 0 +#endif + +#if defined(BOTAN_HAS_TLS_SQLITE3_SESSION_MANAGER) + #include +#endif + +#include "credentials.h" + +using namespace Botan; + +using namespace std::placeholders; + +namespace { + +int connect_to_host(const std::string& host, u16bit port, const std::string& transport) + { + hostent* host_addr = ::gethostbyname(host.c_str()); + + if(host_addr == 0) + throw std::runtime_error("gethostbyname failed for " + host); + + if(host_addr->h_addrtype != AF_INET) // FIXME + throw std::runtime_error(host + " has IPv6 address"); + + int type = (transport == "tcp") ? SOCK_STREAM : SOCK_DGRAM; + + int fd = ::socket(PF_INET, type, 0); + if(fd == -1) + throw std::runtime_error("Unable to acquire socket"); + + sockaddr_in socket_info; + ::memset(&socket_info, 0, sizeof(socket_info)); + socket_info.sin_family = AF_INET; + socket_info.sin_port = htons(port); + + ::memcpy(&socket_info.sin_addr, + host_addr->h_addr, + host_addr->h_length); + + socket_info.sin_addr = *(struct in_addr*)host_addr->h_addr; // FIXME + + if(::connect(fd, (sockaddr*)&socket_info, sizeof(struct sockaddr)) != 0) + { + ::close(fd); + throw std::runtime_error("connect failed"); + } + + return fd; + } + +bool handshake_complete(const TLS::Session& session) + { + std::cout << "Handshake complete, " << session.version().to_string() + << " using " << session.ciphersuite().to_string() << "\n"; + + if(!session.session_id().empty()) + std::cout << "Session ID " << hex_encode(session.session_id()) << "\n"; + + if(!session.session_ticket().empty()) + std::cout << "Session ticket " << hex_encode(session.session_ticket()) << "\n"; + + return true; + } + +void dgram_socket_write(int sockfd, const byte buf[], size_t length) + { + send(sockfd, buf, length, MSG_NOSIGNAL); + } + +void stream_socket_write(int sockfd, const byte buf[], size_t length) + { + size_t offset = 0; + + while(length) + { + ssize_t sent = ::send(sockfd, (const char*)buf + offset, + length, MSG_NOSIGNAL); + + if(sent == -1) + { + if(errno == EINTR) + sent = 0; + else + throw std::runtime_error("Socket::write: Socket write failed"); + } + + offset += sent; + length -= sent; + } + } + +bool got_alert = false; + +void alert_received(TLS::Alert alert, const byte [], size_t ) + { + std::cout << "Alert: " << alert.type_string() << "\n"; + got_alert = true; + } + +void process_data(const byte buf[], size_t buf_size) + { + for(size_t i = 0; i != buf_size; ++i) + std::cout << buf[i]; + } + +std::string protocol_chooser(const std::vector& protocols) + { + for(size_t i = 0; i != protocols.size(); ++i) + std::cout << "Protocol " << i << " = " << protocols[i] << "\n"; + return "http/1.1"; + } + +} + +int tls_client(int argc, char* argv[]) + { + if(argc != 2 && argc != 3 && argc != 4) + { + std::cout << "Usage " << argv[0] << " host [port] [udp|tcp]\n"; + return 1; + } + + try + { + AutoSeeded_RNG rng; + TLS::Policy policy; + +#if defined(BOTAN_HAS_TLS_SQLITE3_SESSION_MANAGER) + TLS::Session_Manager_SQLite session_manager("my secret passphrase", + rng, + "sessions.db"); +#else + TLS::Session_Manager_In_Memory session_manager(rng); +#endif + + Credentials_Manager_Simple creds(rng); + + std::string host = argv[1]; + u32bit port = argc >= 3 ? Botan::to_u32bit(argv[2]) : 443; + std::string transport = argc >= 4 ? argv[3] : "tcp"; + + int sockfd = connect_to_host(host, port, transport); + + auto socket_write = + (transport == "tcp") ? + std::bind(stream_socket_write, sockfd, _1, _2) : + std::bind(dgram_socket_write, sockfd, _1, _2); + + auto version = + (transport == "tcp") ? + TLS::Protocol_Version::latest_tls_version() : + TLS::Protocol_Version::latest_dtls_version(); + + TLS::Client client(socket_write, + process_data, + alert_received, + handshake_complete, + session_manager, + creds, + policy, + rng, + TLS::Server_Information(host, port), + version, + protocol_chooser); + + while(!client.is_closed()) + { + fd_set readfds; + FD_ZERO(&readfds); + FD_SET(sockfd, &readfds); + FD_SET(STDIN_FILENO, &readfds); + + ::select(sockfd + 1, &readfds, NULL, NULL, NULL); + + if(FD_ISSET(sockfd, &readfds)) + { + byte buf[4*1024] = { 0 }; + + ssize_t got = ::read(sockfd, buf, sizeof(buf)); + + if(got == 0) + { + std::cout << "EOF on socket\n"; + break; + } + else if(got == -1) + { + std::cout << "Socket error: " << errno << " " << strerror(errno) << "\n"; + continue; + } + + //std::cout << "Socket - got " << got << " bytes\n"; + client.received_data(buf, got); + } + else if(FD_ISSET(STDIN_FILENO, &readfds)) + { + byte buf[1024] = { 0 }; + ssize_t got = read(STDIN_FILENO, buf, sizeof(buf)); + + if(got == 0) + { + std::cout << "EOF on stdin\n"; + client.close(); + break; + } + else if(got == -1) + { + std::cout << "Stdin error: " << errno << " " << strerror(errno) << "\n"; + continue; + } + + if(got == 2 && buf[1] == '\n') + { + char cmd = buf[0]; + + if(cmd == 'R' || cmd == 'r') + { + std::cout << "Client initiated renegotiation\n"; + client.renegotiate(cmd == 'R'); + } + else if(cmd == 'Q') + { + std::cout << "Client initiated close\n"; + client.close(); + } + } + else if(buf[0] == 'H') + client.heartbeat(&buf[1], got-1); + else + client.send(buf, got); + } + } + + ::close(sockfd); + + } + catch(std::exception& e) + { + std::cout << "Exception: " << e.what() << "\n"; + return 1; + } + return 0; + } diff --git a/src/apps/tls_server.cpp b/src/apps/tls_server.cpp new file mode 100644 index 000000000..cb212fef6 --- /dev/null +++ b/src/apps/tls_server.cpp @@ -0,0 +1,262 @@ +#include "apps.h" +#include +#include + +#include +#include +#include +#include + +#include "credentials.h" + +using namespace Botan; + +using namespace std::placeholders; + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#if !defined(MSG_NOSIGNAL) + #define MSG_NOSIGNAL 0 +#endif + +namespace { + +int make_server_socket(const std::string& transport, u16bit port) + { + int type = (transport == "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(transport != "udp") + { + if(::listen(fd, 100) != 0) + { + ::close(fd); + throw std::runtime_error("listen failed"); + } + } + + return fd; + } + +bool handshake_complete(const TLS::Session& session) + { + std::cout << "Handshake complete, " << session.version().to_string() + << " using " << session.ciphersuite().to_string() << "\n"; + + if(!session.session_id().empty()) + std::cout << "Session ID " << hex_encode(session.session_id()) << "\n"; + + if(!session.session_ticket().empty()) + std::cout << "Session ticket " << hex_encode(session.session_ticket()) << "\n"; + + return true; + } + +void dgram_socket_write(int sockfd, const byte buf[], size_t length) + { + ssize_t sent = ::send(sockfd, buf, length, MSG_NOSIGNAL); + + if(sent == -1) + std::cout << "Error writing to socket - " << strerror(errno) << "\n"; + else if(sent != static_cast(length)) + std::cout << "Packet of length " << length << " truncated to " << sent << "\n"; + } + +void stream_socket_write(int sockfd, const byte buf[], size_t length) + { + size_t offset = 0; + + while(length) + { + ssize_t sent = ::send(sockfd, (const char*)buf + offset, + length, MSG_NOSIGNAL); + + if(sent == -1) + { + if(errno == EINTR) + sent = 0; + else + throw std::runtime_error("Socket::write: Socket write failed"); + } + + offset += sent; + length -= sent; + } + } + +void alert_received(TLS::Alert alert, const byte buf[], size_t buf_size) + { + std::cout << "Alert: " << alert.type_string() << "\n"; + } + +} + +int tls_server(int argc, char* argv[]) + { + int port = 4433; + std::string transport = "tcp"; + + if(argc >= 2) + port = to_u32bit(argv[1]); + if(argc >= 3) + transport = argv[2]; + + try + { + AutoSeeded_RNG rng; + + TLS::Policy policy; + + TLS::Session_Manager_In_Memory session_manager(rng); + + Credentials_Manager_Simple creds(rng); + + /* + * These are the protocols we advertise to the client, but the + * client will send back whatever it actually plans on talking, + * which may or may not take into account what we advertise. + */ + const std::vector protocols = { "echo/1.0", "echo/1.1" }; + + std::cout << "Listening for new connections on " << transport << " port " << port << "\n"; + + int server_fd = make_server_socket(transport, port); + + while(true) + { + try + { + int fd; + + if(transport == "tcp") + fd = ::accept(server_fd, NULL, NULL); + else + { + struct sockaddr_in from; + socklen_t from_len = sizeof(sockaddr_in); + + if(::recvfrom(server_fd, NULL, 0, MSG_PEEK, + (struct sockaddr*)&from, &from_len) != 0) + throw std::runtime_error("Could not peek next packet"); + + if(::connect(server_fd, (struct sockaddr*)&from, from_len) != 0) + throw std::runtime_error("Could not connect UDP socket"); + + fd = server_fd; + } + + std::cout << "New connection received\n"; + + auto socket_write = + (transport == "tcp") ? + std::bind(stream_socket_write, fd, _1, _2) : + std::bind(dgram_socket_write, fd, _1, _2); + + std::string s; + std::list pending_output; + + pending_output.push_back("Welcome to the best echo server evar\n"); + + auto proc_fn = [&](const byte input[], size_t input_len) + { + for(size_t i = 0; i != input_len; ++i) + { + char c = (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, + protocols); + + while(!server.is_closed()) + { + byte buf[4*1024] = { 0 }; + ssize_t got = ::read(fd, buf, sizeof(buf)); + + if(got == -1) + { + std::cout << "Error in socket read - " << strerror(errno) << "\n"; + break; + } + + if(got == 0) + { + std::cout << "EOF on socket\n"; + break; + } + + server.received_data(buf, got); + + while(server.is_active() && !pending_output.empty()) + { + std::string s = pending_output.front(); + pending_output.pop_front(); + server.send(s); + + if(s == "quit\n") + server.close(); + } + } + + if(transport == "tcp") + ::close(fd); + + } + catch(std::exception& e) + { + std::cout << "Connection problem: " << e.what() << "\n"; + return 1; + } + } + } + catch(std::exception& e) + { + std::cout << e.what() << "\n"; + return 1; + } + + return 0; + } diff --git a/src/apps/x509print.cpp b/src/apps/x509print.cpp new file mode 100644 index 000000000..c79ae7a6b --- /dev/null +++ b/src/apps/x509print.cpp @@ -0,0 +1,17 @@ +#include "apps.h" +#include + +int x509(int argc, char* argv[]) + { + if(argc < 1) + { + std::cout << "Usage: " << argv[0] << " cert.pem\n"; + return 1; + } + + X509_Certificate cert(argv[1]); + + std::cout << cert.to_string() << "\n"; + + return 0; + } diff --git a/src/build-data/cc/gcc.txt b/src/build-data/cc/gcc.txt index fe89512c0..be51a46ad 100644 --- a/src/build-data/cc/gcc.txt +++ b/src/build-data/cc/gcc.txt @@ -9,7 +9,7 @@ add_lib_dir_option -L add_lib_option -l lang_flags "-std=c++11 -D_REENTRANT -fstack-protector" -maintainer_warning_flags "-Werror -Wno-error=old-style-cast -Wno-error=zero-as-null-pointer-constant" +maintainer_warning_flags "-Werror -Wno-error=old-style-cast -Wno-error=zero-as-null-pointer-constant -Wno-error=unused-parameter" warning_flags "-Wall -Wextra -Wstrict-aliasing -Wstrict-overflow=5 -Wcast-align -Wmissing-declarations -Wpointer-arith -Wcast-qual -Wold-style-cast -Wzero-as-null-pointer-constant" lib_opt_flags "-O3" diff --git a/src/examples/asn1.cpp b/src/examples/asn1.cpp deleted file mode 100644 index 40f597fc3..000000000 --- a/src/examples/asn1.cpp +++ /dev/null @@ -1,319 +0,0 @@ -#include "examples.h" - -#include -#include -#include -#include -#include -#include -#include -#include -using namespace Botan; - -#include -#include -#include - -// Set this if your terminal understands UTF-8; otherwise output is in Latin-1 -#define UTF8_TERMINAL 1 - -/* - What level the outermost layer of stuff is at. Probably 0 or 1; asn1parse - uses 0 as the outermost, while 1 makes more sense to me. 2+ doesn't make - much sense at all. -*/ -#define INITIAL_LEVEL 0 - -void decode(BER_Decoder&, size_t); -void emit(const std::string&, size_t, size_t, const std::string& = ""); -std::string type_name(ASN1_Tag); - -namespace { - -std::string url_encode(const std::vector& in) - { - std::ostringstream out; - - for(size_t i = 0; i != in.size(); ++i) - { - const int c = in[i]; - if(isprint((int)c)) - out << (char)c; - else - out << "%" << std::hex << (int)c << std::dec; - } - return out.str(); - } - -} - -int asn1_example(int argc, char* argv[]) - { - if(argc != 2) - { - printf("Usage: %s \n", argv[0]); - return 1; - } - - try { - DataSource_Stream in(argv[1]); - - if(!PEM_Code::matches(in)) - { - BER_Decoder decoder(in); - decode(decoder, INITIAL_LEVEL); - } - else - { - std::string label; // ignored - BER_Decoder decoder(PEM_Code::decode(in, label)); - decode(decoder, INITIAL_LEVEL); - } - - } - catch(std::exception& e) - { - printf("%s\n", e.what()); - return 1; - } - return 0; - } - -void decode(BER_Decoder& decoder, size_t level) - { - BER_Object obj = decoder.get_next_object(); - - while(obj.type_tag != NO_OBJECT) - { - const ASN1_Tag type_tag = obj.type_tag; - const ASN1_Tag class_tag = obj.class_tag; - const size_t length = obj.value.size(); - - /* hack to insert the tag+length back in front of the stuff now - that we've gotten the type info */ - DER_Encoder encoder; - encoder.add_object(type_tag, class_tag, obj.value); - std::vector bits = encoder.get_contents_unlocked(); - - BER_Decoder data(bits); - - if(class_tag & CONSTRUCTED) - { - BER_Decoder cons_info(obj.value); - if(type_tag == SEQUENCE) - { - emit("SEQUENCE", level, length); - decode(cons_info, level+1); - } - else if(type_tag == SET) - { - emit("SET", level, length); - decode(cons_info, level+1); - } - else - { - std::string name; - - if((class_tag & APPLICATION) || (class_tag & CONTEXT_SPECIFIC)) - { - name = "cons [" + std::to_string(type_tag) + "]"; - - if(class_tag & APPLICATION) - name += " appl"; - if(class_tag & CONTEXT_SPECIFIC) - name += " context"; - } - else - name = type_name(type_tag) + " (cons)"; - - emit(name, level, length); - decode(cons_info, level+1); - } - } - else if((class_tag & APPLICATION) || (class_tag & CONTEXT_SPECIFIC)) - { -#if 0 - std::vector bits; - data.decode(bits, type_tag); - - try - { - BER_Decoder inner(bits); - decode(inner, level + 1); - } - catch(...) - { - emit("[" + std::to_string(type_tag) + "]", level, length, - url_encode(bits)); - } -#else - emit("[" + std::to_string(type_tag) + "]", level, length, - url_encode(bits)); -#endif - } - else if(type_tag == OBJECT_ID) - { - OID oid; - data.decode(oid); - - std::string out = OIDS::lookup(oid); - if(out != oid.as_string()) - out += " [" + oid.as_string() + "]"; - - emit(type_name(type_tag), level, length, out); - } - else if(type_tag == INTEGER || type_tag == ENUMERATED) - { - BigInt number; - - if(type_tag == INTEGER) - data.decode(number); - else if(type_tag == ENUMERATED) - data.decode(number, ENUMERATED, class_tag); - - std::vector rep; - - /* If it's small, it's probably a number, not a hash */ - if(number.bits() <= 16) - rep = BigInt::encode(number, BigInt::Decimal); - else - rep = BigInt::encode(number, BigInt::Hexadecimal); - - std::string str; - for(size_t i = 0; i != rep.size(); ++i) - str += (char)rep[i]; - - emit(type_name(type_tag), level, length, str); - } - else if(type_tag == BOOLEAN) - { - bool boolean; - data.decode(boolean); - emit(type_name(type_tag), - level, length, (boolean ? "true" : "false")); - } - else if(type_tag == NULL_TAG) - { - emit(type_name(type_tag), level, length); - } - else if(type_tag == OCTET_STRING) - { - std::vector bits; - data.decode(bits, type_tag); - - try - { - BER_Decoder inner(bits); - decode(inner, level + 1); - } - catch(...) - { - emit(type_name(type_tag), level, length, - url_encode(bits)); - } - } - else if(type_tag == BIT_STRING) - { - std::vector bits; - data.decode(bits, type_tag); - - std::vector bit_set; - - for(size_t i = 0; i != bits.size(); ++i) - for(size_t j = 0; j != 8; ++j) - bit_set.push_back((bool)((bits[bits.size()-i-1] >> (7-j)) & 1)); - - std::string bit_str; - for(size_t i = 0; i != bit_set.size(); ++i) - { - bool the_bit = bit_set[bit_set.size()-i-1]; - - if(!the_bit && bit_str.size() == 0) - continue; - bit_str += (the_bit ? "1" : "0"); - } - - emit(type_name(type_tag), level, length, bit_str); - } - else if(type_tag == PRINTABLE_STRING || - type_tag == NUMERIC_STRING || - type_tag == IA5_STRING || - type_tag == T61_STRING || - type_tag == VISIBLE_STRING || - type_tag == UTF8_STRING || - type_tag == BMP_STRING) - { - ASN1_String str; - data.decode(str); - if(UTF8_TERMINAL) - emit(type_name(type_tag), level, length, - Charset::transcode(str.iso_8859(), - LATIN1_CHARSET, UTF8_CHARSET)); - else - emit(type_name(type_tag), level, length, str.iso_8859()); - } - else if(type_tag == UTC_TIME || type_tag == GENERALIZED_TIME) - { - X509_Time time; - data.decode(time); - emit(type_name(type_tag), level, length, time.readable_string()); - } - else - fprintf(stderr, "Unknown tag: class=%02X, type=%02X\n", - class_tag, type_tag); - - obj = decoder.get_next_object(); - } - } - -void emit(const std::string& type, size_t level, size_t length, - const std::string& value) - { - const size_t LIMIT = 128; - const size_t BIN_LIMIT = 64; - - int written = 0; - written += printf(" d=%2d, l=%4d: ", (int)level, (int)length); - for(size_t i = INITIAL_LEVEL; i != level; ++i) - written += printf(" "); - written += printf("%s ", type.c_str()); - - bool should_skip = false; - if(value.length() > LIMIT) should_skip = true; - if((type == "OCTET STRING" || type == "BIT STRING") && - value.length() > BIN_LIMIT) - should_skip = true; - - if(value != "" && !should_skip) - { - if(written % 2 == 0) printf(" "); - while(written < 50) written += printf(" "); - printf(":%s\n", value.c_str()); - } - else - printf("\n"); - } - -std::string type_name(ASN1_Tag type) - { - if(type == PRINTABLE_STRING) return "PRINTABLE STRING"; - if(type == NUMERIC_STRING) return "NUMERIC STRING"; - if(type == IA5_STRING) return "IA5 STRING"; - if(type == T61_STRING) return "T61 STRING"; - if(type == UTF8_STRING) return "UTF8 STRING"; - if(type == VISIBLE_STRING) return "VISIBLE STRING"; - if(type == BMP_STRING) return "BMP STRING"; - - if(type == UTC_TIME) return "UTC TIME"; - if(type == GENERALIZED_TIME) return "GENERALIZED TIME"; - - if(type == OCTET_STRING) return "OCTET STRING"; - if(type == BIT_STRING) return "BIT STRING"; - - if(type == ENUMERATED) return "ENUMERATED"; - if(type == INTEGER) return "INTEGER"; - if(type == NULL_TAG) return "NULL"; - if(type == OBJECT_ID) return "OBJECT"; - if(type == BOOLEAN) return "BOOLEAN"; - return "(UNKNOWN)"; - } diff --git a/src/examples/bcrypt.cpp b/src/examples/bcrypt.cpp deleted file mode 100644 index 50205cd4d..000000000 --- a/src/examples/bcrypt.cpp +++ /dev/null @@ -1,32 +0,0 @@ -#include "examples.h" -#include - -int bcrypt_example(int argc, char* argv[]) - { - if(argc == 2) - { - AutoSeeded_RNG rng; - - const std::string password = argv[1]; - - std::cout << generate_bcrypt(password, rng, 12) << "\n"; - return 0; - } - else if(argc == 3) - { - const std::string password = argv[1]; - const std::string hash = argv[2]; - - if(hash.length() != 60) - std::cout << "Note: bcrypt '" << hash << "' has wrong length and cannot be valid\n"; - - const bool ok = check_bcrypt(password, hash); - - std::cout << "Password is " << (ok ? "valid" : "NOT valid") << "\n"; - return (ok ? 0 : 1); - } - - std::cout << "Usage: " << argv[0] << " password\n" - << " " << argv[0] << " password passhash\n"; - return 1; - } diff --git a/src/examples/examples.h b/src/examples/examples.h deleted file mode 100644 index 2a9a361c7..000000000 --- a/src/examples/examples.h +++ /dev/null @@ -1,17 +0,0 @@ - -#include "../common.h" -#include -#include -#include - -using namespace Botan; - -int asn1_example(int argc, char* argv[]); -int bcrypt_example(int argc, char* argv[]); -int factor_example(int argc, char* argv[]); -int fpe_example(int argc, char* argv[]); -int read_ssh_example(int argc, char* argv[]); - -int self_sig_example(int argc, char* argv[]); - -int x509_example(int argc, char* argv[]); diff --git a/src/examples/factor.cpp b/src/examples/factor.cpp deleted file mode 100644 index 822b62b71..000000000 --- a/src/examples/factor.cpp +++ /dev/null @@ -1,153 +0,0 @@ -/* -* (C) 2009-2010 Jack Lloyd -* -* Distributed under the terms of the Botan license -* -* Factor integers using a combination of trial division by small -* primes, and Pollard's Rho algorithm -*/ - -#include "examples.h" - -#include -#include -using namespace Botan; - -#include -#include -#include - -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 remove_small_factors(BigInt& n) - { - std::vector 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 factorize(const BigInt& n_in, - RandomNumberGenerator& rng) - { - BigInt n = n_in; - std::vector factors = remove_small_factors(n); - - while(n != 1) - { - if(check_prime(n, rng)) - { - factors.push_back(n); - break; - } - - BigInt a_factor = 0; - while(a_factor == 0) - a_factor = rho(n, rng); - - std::vector 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_example(int argc, char* argv[]) - { - if(argc != 2) - { - std::cout << "Usage: " << argv[0] << " \n"; - return 1; - } - - try - { - BigInt n(argv[1]); - - AutoSeeded_RNG rng; - - std::vector factors = factorize(n, rng); - std::sort(factors.begin(), factors.end()); - - std::cout << n << ": "; - std::copy(factors.begin(), - factors.end(), - std::ostream_iterator(std::cout, " ")); - std::cout << "\n"; - } - catch(std::exception& e) - { - std::cout << e.what() << std::endl; - return 1; - } - return 0; - } diff --git a/src/examples/fpe.cpp b/src/examples/fpe.cpp deleted file mode 100644 index cccb602fd..000000000 --- a/src/examples/fpe.cpp +++ /dev/null @@ -1,143 +0,0 @@ -#include "examples.h" -#include -#include - -using namespace Botan; - -#include -#include - -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 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_example(int argc, char* argv[]) - { - if(argc != 4) - { - std::cout << "Usage: " << argv[0] << " cc-number acct-name passwd\n"; - return 1; - } - - u64bit cc_number = atoll(argv[1]); - std::string acct_name = argv[2]; - std::string passwd = argv[3]; - - std::cout << "Input was: " << cc_number << ' ' - << luhn_check(cc_number) << '\n'; - - /* - * 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) << '\n'; - - u64bit dec_cc = decrypt_cc_number(enc_cc, key, acct_name); - - std::cout << "Decrypted: " << dec_cc - << ' ' << luhn_check(dec_cc) << '\n'; - - if(dec_cc != cc_number) - { - std::cout << "Something went wrong :( Bad CC checksum?\n"; - return 2; - } - - return 0; - } diff --git a/src/examples/read_ssh.cpp b/src/examples/read_ssh.cpp deleted file mode 100644 index 83f7ff89e..000000000 --- a/src/examples/read_ssh.cpp +++ /dev/null @@ -1,134 +0,0 @@ -/* -* (C) 2009 Jack Lloyd -* -* Distributed under the terms of the Botan license -*/ - -/* -* Example of reading SSH2 format public keys (see RFC 4716) -*/ - -#include "examples.h" - -#include -#include -#include -#include -#include -#include -#include - -using namespace Botan; - -namespace { - -u32bit read_u32bit(Pipe& pipe) - { - byte out[4] = { 0 }; - pipe.read(out, 4); - u32bit len = load_be(out, 0); - if(len > 10000) - throw Decoding_Error("Huge size in read_u32bit, something went wrong"); - return len; - } - -std::string read_string(Pipe& pipe) - { - u32bit len = read_u32bit(pipe); - - std::string out(len, 'X'); - pipe.read(reinterpret_cast(&out[0]), len); - return out; - } - -BigInt read_bigint(Pipe& pipe) - { - u32bit len = read_u32bit(pipe); - - secure_vector buf(len); - pipe.read(&buf[0], len); - return BigInt::decode(buf); - } - -Public_Key* read_ssh_pubkey(const std::string& file) - { - std::ifstream in(file.c_str()); - - const std::string ssh_header = "---- BEGIN SSH2 PUBLIC KEY ----"; - const std::string ssh_trailer = "---- END SSH2 PUBLIC KEY ----"; - - std::string hex_bits; - - std::string line; - std::getline(in, line); - - if(line != ssh_header) - return nullptr; - - while(in.good()) - { - std::getline(in, line); - - if(line.find("Comment: ") == 0) - { - while(line[line.size()-1] == '\\') - std::getline(in, line); - std::getline(in, line); - } - - if(line == ssh_trailer) - break; - - hex_bits += line; - } - - Pipe pipe(new Base64_Decoder); - pipe.process_msg(hex_bits); - - std::string key_type = read_string(pipe); - - if(key_type != "ssh-rsa" && key_type != "ssh-dss") - return nullptr; - - if(key_type == "ssh-rsa") - { - BigInt e = read_bigint(pipe); - BigInt n = read_bigint(pipe); - return new RSA_PublicKey(n, e); - } - else if(key_type == "ssh-dss") - { - BigInt p = read_bigint(pipe); - BigInt q = read_bigint(pipe); - BigInt g = read_bigint(pipe); - BigInt y = read_bigint(pipe); - - return new DSA_PublicKey(DL_Group(p, q, g), y); - } - - return nullptr; - } - -} - -int read_ssh_example(int argc, char* argv[]) - { - if(argc != 2) - { - std::cout << "Usage: " << argv[0] << " file"; - return 1; - } - - const std::string filename = argv[1]; - std::unique_ptr key(read_ssh_pubkey(filename)); - - if(key == 0) - { - std::cout << "Failed to read" << filename << "\n"; - return 1; - } - - std::cout << X509::PEM_encode(*key); - - return 0; - } diff --git a/src/examples/self_sig.cpp b/src/examples/self_sig.cpp deleted file mode 100644 index 89d9f84e7..000000000 --- a/src/examples/self_sig.cpp +++ /dev/null @@ -1,70 +0,0 @@ -#include "examples.h" -#include -#include -#include -using namespace Botan; - -#include -#include -#include - -int self_sig_example(int argc, char* argv[]) - { - if(argc != 7) - { - std::cout << "Usage: " << argv[0] - << " passphrase [CA|user] name country_code organization email" - << std::endl; - return 1; - } - - std::string CA_flag = argv[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); - - //DSA_PrivateKey key(rng, group); - - std::ofstream priv_key("private.pem"); - priv_key << PKCS8::PEM_encode(key, rng, argv[1]); - - X509_Cert_Options opts; - - opts.common_name = argv[3]; - opts.country = argv[4]; - opts.organization = argv[5]; - opts.email = argv[6]; - /* Fill in other values of opts here */ - - //opts.xmpp = "lloyd@randombit.net"; - - 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; - } diff --git a/src/examples/x509print.cpp b/src/examples/x509print.cpp deleted file mode 100644 index 88e1dfbaa..000000000 --- a/src/examples/x509print.cpp +++ /dev/null @@ -1,17 +0,0 @@ -#include "examples.h" -#include - -int x509_example(int argc, char* argv[]) - { - if(argc < 1) - { - std::cout << "Usage: " << argv[0] << " cert.pem\n"; - return 1; - } - - X509_Certificate cert(argv[1]); - - std::cout << cert.to_string() << "\n"; - - return 0; - } diff --git a/src/main.cpp b/src/main.cpp index baf37e895..956413d4c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -29,7 +29,7 @@ using namespace Botan; #include "common.h" #include "speed/speed.h" #include "tests/tests.h" -#include "examples/examples.h" +#include "apps/apps.h" // from common.h void strip_comments(std::string& line) @@ -76,6 +76,18 @@ std::vector parse(const std::string& line) return substr; } +namespace { + +int help(int , char* argv[]) + { + std::cout << "Usage: " << argv[0] << " subcommand\n"; + std::cout << "Common commands: help version test speed\n"; + std::cout << "Other commands: cpuid bcrypt x509 factor tls_client asn1 base64 hash self_sig\n"; + return 1; + } + +} + int main(int argc, char* argv[]) { if(BOTAN_VERSION_MAJOR != version_major() || @@ -94,37 +106,36 @@ int main(int argc, char* argv[]) try { - OptionParser opts("help|test|validate|" - "algo=|seconds=|buf-size="); - opts.parse(argv); - Botan::LibraryInitializer init; - if(opts.is_set("help") || argc < 2) - { - std::cout << "Commands: test version time bcrypt\n"; - return 1; - } + if(argc < 2) + return help(argc, argv); const std::string cmd = argv[1]; + if(cmd == "help") + return help(argc, argv); + if(cmd == "version") { std::cout << Botan::version_string() << "\n"; return 0; } + if(cmd == "cpuid") + { + CPUID::print(std::cout); + return 0; + } + if(cmd == "test") { const size_t failures = run_all_tests(); return failures ? 1 : 0; } - if(cmd == "cpuid") - { - CPUID::print(std::cout); - return 0; - } + if(cmd == "speed") + return speed_main(argc - 1, argv + 1); if(cmd == "http_get") { @@ -132,63 +143,24 @@ int main(int argc, char* argv[]) std::cout << resp << "\n"; } - if(cmd == "bcrypt") - return bcrypt_example(argc - 1, argv + 1); - - if(cmd == "fpe") - return fpe_example(argc - 1, argv + 1); - - if(cmd == "read_ssh_key") - return read_ssh_example(argc - 1, argv + 1); - - if(cmd == "self_sig") - return self_sig_example(argc - 1, argv + 1); - - if(cmd == "x509") - return x509_example(argc - 1, argv + 1); - - if(cmd == "factor") - return factor_example(argc - 1, argv + 1); - - if(cmd == "asn1") - return asn1_example(argc - 1, argv + 1); - - if(cmd == "speed") - { - 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\n"; - 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\n"; - return 2; - } - } - - if(opts.is_set("--algo")) - { - AutoSeeded_RNG rng; - for(auto alg: Botan::split_on(opts.value("algo"), ',')) - bench_algo(alg, rng, seconds, buf_size); - } - /* - else - benchmark(seconds, buf_size); - */ - } +#define CALL_CMD(cmdsym) \ + do { if(cmd == #cmdsym) { return cmdsym (argc - 1, argv + 1); } } while(0) + + CALL_CMD(asn1); + CALL_CMD(base64); + CALL_CMD(bcrypt); + CALL_CMD(bzip); + CALL_CMD(ca); + CALL_CMD(factor); + CALL_CMD(fpe); + CALL_CMD(hash); + CALL_CMD(keygen); + CALL_CMD(pkcs10); + CALL_CMD(read_ssh); + CALL_CMD(self_sig); + CALL_CMD(tls_client); + CALL_CMD(tls_server); + CALL_CMD(x509); } catch(std::exception& e) { diff --git a/src/speed/speed.cpp b/src/speed/speed.cpp index b8c14ee59..1cd3ec0f7 100644 --- a/src/speed/speed.cpp +++ b/src/speed/speed.cpp @@ -198,3 +198,43 @@ void benchmark(double seconds, size_t buf_size) for(size_t i = 0; algos[i] != ""; ++i) bench_algo(algos[i], rng, seconds, buf_size); } + +int speed_main(int , char* argv[]) + { + OptionParser opts("algo=|seconds=|buf-size="); + opts.parse(argv); + + 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\n"; + 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\n"; + return 2; + } + } + + if(opts.is_set("algo")) + { + AutoSeeded_RNG rng; + for(auto alg: Botan::split_on(opts.value("algo"), ',')) + bench_algo(alg, rng, seconds, buf_size); + } + else + benchmark(seconds, buf_size); + + return 0; + } diff --git a/src/speed/speed.h b/src/speed/speed.h index d115960fb..ec0d06733 100644 --- a/src/speed/speed.h +++ b/src/speed/speed.h @@ -9,6 +9,8 @@ using namespace Botan; +int speed_main(int argc, char* argv[]); + void benchmark(double seconds, size_t buf_size); -- cgit v1.2.3