diff options
author | lloyd <[email protected]> | 2014-01-01 23:32:24 +0000 |
---|---|---|
committer | lloyd <[email protected]> | 2014-01-01 23:32:24 +0000 |
commit | 7323f3ff83ff2199b1090f9d5f729b08ccac3151 (patch) | |
tree | 84edcf72e8837ac328aae505f5059c95f4e459a5 /src | |
parent | 0c7008498790caea563ed3601df1943f8f7b6269 (diff) |
Move base64, bzip, ca, and tls examples
Diffstat (limited to 'src')
-rw-r--r-- | src/apps/apps.h | 25 | ||||
-rw-r--r-- | src/apps/asn1.cpp (renamed from src/examples/asn1.cpp) | 4 | ||||
-rw-r--r-- | src/apps/base64.cpp | 85 | ||||
-rw-r--r-- | src/apps/bcrypt.cpp (renamed from src/examples/bcrypt.cpp) | 4 | ||||
-rw-r--r-- | src/apps/bzip.cpp | 115 | ||||
-rw-r--r-- | src/apps/ca.cpp | 63 | ||||
-rw-r--r-- | src/apps/credentials.h | 294 | ||||
-rw-r--r-- | src/apps/factor.cpp (renamed from src/examples/factor.cpp) | 4 | ||||
-rw-r--r-- | src/apps/fpe.cpp (renamed from src/examples/fpe.cpp) | 4 | ||||
-rw-r--r-- | src/apps/hash.cpp | 58 | ||||
-rw-r--r-- | src/apps/keygen.cpp | 53 | ||||
-rw-r--r-- | src/apps/pkcs10.cpp | 48 | ||||
-rw-r--r-- | src/apps/read_ssh.cpp (renamed from src/examples/read_ssh.cpp) | 4 | ||||
-rw-r--r-- | src/apps/self_sig.cpp (renamed from src/examples/self_sig.cpp) | 4 | ||||
-rw-r--r-- | src/apps/tls_client.cpp | 263 | ||||
-rw-r--r-- | src/apps/tls_server.cpp | 262 | ||||
-rw-r--r-- | src/apps/x509print.cpp (renamed from src/examples/x509print.cpp) | 4 | ||||
-rw-r--r-- | src/build-data/cc/gcc.txt | 2 | ||||
-rw-r--r-- | src/examples/examples.h | 17 | ||||
-rw-r--r-- | src/main.cpp | 116 | ||||
-rw-r--r-- | src/speed/speed.cpp | 40 | ||||
-rw-r--r-- | src/speed/speed.h | 2 |
22 files changed, 1367 insertions, 104 deletions
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 <botan/auto_rng.h> +#include <botan/hex.h> +#include <iostream> + +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/examples/asn1.cpp b/src/apps/asn1.cpp index 40f597fc3..447462ae0 100644 --- a/src/examples/asn1.cpp +++ b/src/apps/asn1.cpp @@ -1,4 +1,4 @@ -#include "examples.h" +#include "apps.h" #include <botan/bigint.h> #include <botan/der_enc.h> @@ -47,7 +47,7 @@ std::string url_encode(const std::vector<byte>& in) } -int asn1_example(int argc, char* argv[]) +int asn1(int argc, char* argv[]) { if(argc != 2) { 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 <fstream> +#include <iostream> +#include <string> +#include <vector> +#include <cstring> +#include <cstdlib> +#include <botan/b64_filt.h> +#include <botan/pipe.h> + +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<std::string> 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/examples/bcrypt.cpp b/src/apps/bcrypt.cpp index 50205cd4d..497111363 100644 --- a/src/examples/bcrypt.cpp +++ b/src/apps/bcrypt.cpp @@ -1,7 +1,7 @@ -#include "examples.h" +#include "apps.h" #include <botan/bcrypt.h> -int bcrypt_example(int argc, char* argv[]) +int bcrypt(int argc, char* argv[]) { if(argc == 2) { 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 <string> +#include <cstring> +#include <vector> +#include <fstream> +#include <iostream> +#include <botan/botan.h> + +/* +* 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 <botan/bzip2.h> +#endif + +const std::string SUFFIX = ".bz2"; + +int bzip(int argc, char* argv[]) + { + if(argc < 2) + { + std::cout << "Usage: " << argv[0] + << " [-s] [-d] [-1...9] <filenames>" << std::endl; + return 1; + } + +#ifdef BOTAN_HAS_COMPRESSOR_BZIP2 + std::vector<std::string> 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 <botan/x509_ca.h> +using namespace Botan; + +#include <iostream> +#include <memory> +#include <chrono> + +int ca(int argc, char* argv[]) + { + if(argc != 5) + { + std::cout << "Usage: " << argv[0] << " <passphrase> " + << "<ca cert> <ca key> <pkcs10>" << 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<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; + } 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 <botan/credentials_manager.h> +#include <botan/x509self.h> +#include <botan/rsa.h> +#include <botan/dsa.h> +#include <botan/srp6.h> +#include <botan/srp6_files.h> +#include <botan/ecdsa.h> +#include <iostream> +#include <fstream> +#include <memory> + +inline bool value_exists(const std::vector<std::string>& vec, + const std::string& val) + { + for(size_t i = 0; i != vec.size(); ++i) + if(vec[i] == val) + return true; + return false; + } + +class 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<Botan::Certificate_Store*> + trusted_certificate_authorities(const std::string& type, + const std::string& /*hostname*/) + { + // don't ask for client cert + if(type == "tls-server") + return std::vector<Botan::Certificate_Store*>(); + + return m_certstores; + } + + void verify_certificate_chain( + const std::string& type, + const std::string& purported_hostname, + const std::vector<Botan::X509_Certificate>& 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<Botan::byte>& 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<byte*>(&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<Botan::X509_Certificate,Botan::Private_Key*> + 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<Private_Key> 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<Botan::X509_Certificate> cert_chain( + const std::vector<std::string>& cert_key_types, + const std::string& type, + const std::string& context) + { + using namespace Botan; + + std::vector<X509_Certificate> certs; + + try + { + if(type == "tls-server") + { + const std::string hostname = (context == "" ? "localhost" : context); + + if(hostname == "nosuchname") + return std::vector<Botan::X509_Certificate>(); + + 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<X509_Certificate, Private_Key*> 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<Botan::X509_Certificate, Botan::Private_Key*> certs_and_keys; + + std::vector<Botan::Certificate_Store*> m_certstores; + }; + +#endif diff --git a/src/examples/factor.cpp b/src/apps/factor.cpp index 822b62b71..484f024d8 100644 --- a/src/examples/factor.cpp +++ b/src/apps/factor.cpp @@ -7,7 +7,7 @@ * primes, and Pollard's Rho algorithm */ -#include "examples.h" +#include "apps.h" #include <botan/reducer.h> #include <botan/numthry.h> @@ -121,7 +121,7 @@ std::vector<BigInt> factorize(const BigInt& n_in, } -int factor_example(int argc, char* argv[]) +int factor(int argc, char* argv[]) { if(argc != 2) { diff --git a/src/examples/fpe.cpp b/src/apps/fpe.cpp index cccb602fd..dd5b9b682 100644 --- a/src/examples/fpe.cpp +++ b/src/apps/fpe.cpp @@ -1,4 +1,4 @@ -#include "examples.h" +#include "apps.h" #include <botan/fpe_fe1.h> #include <botan/sha160.h> @@ -102,7 +102,7 @@ u64bit decrypt_cc_number(u64bit enc_cc, } -int fpe_example(int argc, char* argv[]) +int fpe(int argc, char* argv[]) { if(argc != 4) { 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 <botan/lookup.h> +#include <iostream> +#include <fstream> + +using namespace Botan; + +int hash(int argc, char* argv[]) + { + if(argc < 3) + { + std::cout << "Usage: " << argv[0] << " digest <filenames>" << 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 <iostream> +#include <fstream> +#include <string> +#include <cstdlib> +#include <memory> + +#include <botan/rsa.h> +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 <botan/x509self.h> +#include <botan/rsa.h> +#include <botan/dsa.h> +using namespace Botan; + +#include <iostream> +#include <fstream> +#include <memory> + +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/examples/read_ssh.cpp b/src/apps/read_ssh.cpp index 83f7ff89e..ce72fa064 100644 --- a/src/examples/read_ssh.cpp +++ b/src/apps/read_ssh.cpp @@ -8,7 +8,7 @@ * Example of reading SSH2 format public keys (see RFC 4716) */ -#include "examples.h" +#include "apps.h" #include <botan/x509_key.h> #include <botan/filters.h> @@ -111,7 +111,7 @@ Public_Key* read_ssh_pubkey(const std::string& file) } -int read_ssh_example(int argc, char* argv[]) +int read_ssh(int argc, char* argv[]) { if(argc != 2) { diff --git a/src/examples/self_sig.cpp b/src/apps/self_sig.cpp index 89d9f84e7..c4202442c 100644 --- a/src/examples/self_sig.cpp +++ b/src/apps/self_sig.cpp @@ -1,4 +1,4 @@ -#include "examples.h" +#include "apps.h" #include <botan/x509self.h> #include <botan/rsa.h> #include <botan/dsa.h> @@ -8,7 +8,7 @@ using namespace Botan; #include <fstream> #include <memory> -int self_sig_example(int argc, char* argv[]) +int self_sig(int argc, char* argv[]) { if(argc != 7) { 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 <botan/tls_client.h> +#include <botan/pkcs8.h> +#include <botan/hex.h> +#include <stdio.h> +#include <string> +#include <iostream> +#include <memory> + +#include <sys/types.h> +#include <sys/time.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <netdb.h> +#include <unistd.h> +#include <errno.h> +#include <fcntl.h> + +#if !defined(MSG_NOSIGNAL) + #define MSG_NOSIGNAL 0 +#endif + +#if defined(BOTAN_HAS_TLS_SQLITE3_SESSION_MANAGER) + #include <botan/tls_session_manager_sqlite.h> +#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<std::string>& 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 <botan/tls_server.h> +#include <botan/hex.h> + +#include <botan/rsa.h> +#include <botan/dsa.h> +#include <botan/x509self.h> +#include <botan/secqueue.h> + +#include "credentials.h" + +using namespace Botan; + +using namespace std::placeholders; + +#include <stdio.h> +#include <string> +#include <iostream> +#include <memory> +#include <list> + +#include <sys/types.h> +#include <sys/time.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <netdb.h> +#include <unistd.h> +#include <errno.h> +#include <fcntl.h> + +#if !defined(MSG_NOSIGNAL) + #define MSG_NOSIGNAL 0 +#endif + +namespace { + +int make_server_socket(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<ssize_t>(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<std::string> 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<std::string> 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/examples/x509print.cpp b/src/apps/x509print.cpp index 88e1dfbaa..c79ae7a6b 100644 --- a/src/examples/x509print.cpp +++ b/src/apps/x509print.cpp @@ -1,7 +1,7 @@ -#include "examples.h" +#include "apps.h" #include <botan/x509cert.h> -int x509_example(int argc, char* argv[]) +int x509(int argc, char* argv[]) { if(argc < 1) { 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/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 <botan/auto_rng.h> -#include <botan/hex.h> -#include <iostream> - -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/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<std::string> 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); |