diff options
author | Jack Lloyd <[email protected]> | 2018-07-01 16:32:28 -0400 |
---|---|---|
committer | Jack Lloyd <[email protected]> | 2018-07-04 10:22:56 -0400 |
commit | a2ce8a8ebd5abc25b97a5bc7312c47fcceba9fd1 (patch) | |
tree | 0f7aa5914ed51b5ad16f955569b42d74156890e9 /src/cli | |
parent | 0e8e8f112dfe714faba49176e1d3be97fd2e32dd (diff) |
Add pk_encrypt/pk_decrypt commands
Diffstat (limited to 'src/cli')
-rw-r--r-- | src/cli/cli.cpp | 38 | ||||
-rw-r--r-- | src/cli/cli.h | 2 | ||||
-rw-r--r-- | src/cli/pk_crypt.cpp | 212 |
3 files changed, 250 insertions, 2 deletions
diff --git a/src/cli/cli.cpp b/src/cli/cli.cpp index e3ff2a75f..a7914ba92 100644 --- a/src/cli/cli.cpp +++ b/src/cli/cli.cpp @@ -11,6 +11,10 @@ #include <iostream> #include <fstream> +#if defined(BOTAN_TARGET_OS_HAS_POSIX1) + #include <termios.h> +#endif + namespace Botan_CLI { Command::Command(const std::string& cmd_spec) : m_spec(cmd_spec) @@ -182,8 +186,8 @@ void Command::read_file(const std::string& input_file, } void Command::do_read_file(std::istream& in, - std::function<void (uint8_t[], size_t)> consumer_fn, - size_t buf_size) const + std::function<void (uint8_t[], size_t)> consumer_fn, + size_t buf_size) const { // Avoid an infinite loop on --buf-size=0 std::vector<uint8_t> buf(buf_size == 0 ? 4096 : buf_size); @@ -206,6 +210,36 @@ Botan::RandomNumberGenerator& Command::rng() return *m_rng.get(); } +std::string Command::get_passphrase(const std::string& prompt) + { + error_output() << prompt << ": " << std::flush; + std::string pass; + +#if defined(BOTAN_TARGET_OS_HAS_POSIX1) + + struct termios old_flags; + ::tcgetattr(fileno(stdin), &old_flags); + struct termios noecho_flags = old_flags; + noecho_flags.c_lflag &= ~ECHO; + noecho_flags.c_lflag |= ECHONL; + + if(::tcsetattr(fileno(stdin), TCSANOW, &noecho_flags) != 0) + throw CLI_Error("Clearing terminal echo bit failed"); + + std::getline(std::cin, pass); + + if(::tcsetattr(::fileno(stdin), TCSANOW, &old_flags) != 0) + throw CLI_Error("Restoring terminal echo bit failed"); +#else + + // TODO equivalent for Windows ... + std::getline(std::cin, pass); + +#endif + + return pass; + } + // Registration code Command::Registration::Registration(const std::string& name, Command::cmd_maker_fn maker_fn) diff --git a/src/cli/cli.h b/src/cli/cli.h index 97128a722..fca8e5225 100644 --- a/src/cli/cli.h +++ b/src/cli/cli.h @@ -118,6 +118,8 @@ class Command return flag_set("verbose"); } + std::string get_passphrase(const std::string& prompt); + bool flag_set(const std::string& flag_name) const; std::string get_arg(const std::string& opt_name) const; diff --git a/src/cli/pk_crypt.cpp b/src/cli/pk_crypt.cpp new file mode 100644 index 000000000..69a8fdf02 --- /dev/null +++ b/src/cli/pk_crypt.cpp @@ -0,0 +1,212 @@ +/* +* (C) 2018 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include "cli.h" + +#if defined(BOTAN_HAS_RSA) && defined(BOTAN_HAS_AEAD_MODES) && defined(BOTAN_HAS_EME_OAEP) && defined(BOTAN_HAS_SHA2_32) && defined(BOTAN_HAS_PEM_CODEC) + +#include <botan/pubkey.h> +#include <botan/x509_key.h> +#include <botan/pkcs8.h> +#include <botan/der_enc.h> +#include <botan/ber_dec.h> +#include <botan/oids.h> +#include <botan/aead.h> +#include <botan/pem.h> + +namespace Botan_CLI { + +namespace { + +class PK_Encrypt final : public Command + { + public: + PK_Encrypt() : Command("pk_encrypt --aead=AES-256/GCM pubkey datafile") {} + + std::string group() const override + { + return "pubkey"; + } + + std::string description() const override + { + return "Encrypt a file using a RSA public key"; + } + + void go() override + { + std::unique_ptr<Botan::Public_Key> key(Botan::X509::load_key(get_arg("pubkey"))); + if(!key) + { + throw CLI_Error("Unable to load public key"); + } + + if(key->algo_name() != "RSA") + { + throw CLI_Usage_Error("This function requires an RSA key"); + } + + const std::string OAEP_HASH = "SHA-256"; + const std::string aead_algo = get_arg("aead"); + + std::unique_ptr<Botan::AEAD_Mode> aead = + Botan::AEAD_Mode::create(aead_algo, Botan::ENCRYPTION); + + if(!aead) + throw CLI_Usage_Error("The AEAD '" + aead_algo + "' is not available"); + + const Botan::OID aead_oid = Botan::OIDS::lookup(aead_algo); + if(aead_oid.empty()) + throw CLI_Usage_Error("No OID defined for AEAD '" + aead_algo + "'"); + + Botan::secure_vector<uint8_t> data; + auto insert_fn = [&](const uint8_t b[], size_t l) + { + data.insert(data.end(), b, b + l); + }; + this->read_file(get_arg("datafile"), insert_fn); + + const Botan::AlgorithmIdentifier hash_id(OAEP_HASH, Botan::AlgorithmIdentifier::USE_NULL_PARAM); + const Botan::AlgorithmIdentifier pk_alg_id("RSA/OAEP", hash_id.BER_encode()); + + Botan::PK_Encryptor_EME enc(*key, rng(), "OAEP(" + OAEP_HASH + ")"); + + const Botan::secure_vector<uint8_t> file_key = rng().random_vec(aead->key_spec().maximum_keylength()); + + const std::vector<uint8_t> encrypted_key = enc.encrypt(file_key, rng()); + + const Botan::secure_vector<uint8_t> nonce = rng().random_vec(aead->default_nonce_length()); + aead->set_key(file_key); + aead->set_associated_data_vec(encrypted_key); + aead->start(nonce); + + aead->finish(data); + + std::vector<uint8_t> buf; + Botan::DER_Encoder der(buf); + + der.start_cons(Botan::SEQUENCE) + .encode(pk_alg_id) + .encode(encrypted_key, Botan::OCTET_STRING) + .encode(aead_oid) + .encode(nonce, Botan::OCTET_STRING) + .encode(data, Botan::OCTET_STRING) + .end_cons(); + + output() << Botan::PEM_Code::encode(buf, "PUBKEY ENCRYPTED MESSAGE", 72); + } + }; + +BOTAN_REGISTER_COMMAND("pk_encrypt", PK_Encrypt); + +class PK_Decrypt final : public Command + { + public: + PK_Decrypt() : Command("pk_decrypt privkey datafile") {} + + std::string group() const override + { + return "pubkey"; + } + + std::string description() const override + { + return "Decrypt a file using a RSA private key"; + } + + void go() override + { + Botan::DataSource_Stream input_stream(get_arg("privkey")); + auto get_pass = [this]() { return get_passphrase("Password"); }; + std::unique_ptr<Botan::Private_Key> key = Botan::PKCS8::load_key(input_stream, get_pass); + + if(!key) + { + throw CLI_Error("Unable to load public key"); + } + + if(key->algo_name() != "RSA") + { + throw CLI_Usage_Error("This function requires an RSA key"); + } + + Botan::secure_vector<uint8_t> data; + std::vector<uint8_t> encrypted_key; + std::vector<uint8_t> nonce; + Botan::AlgorithmIdentifier pk_alg_id; + Botan::OID aead_oid; + + try + { + Botan::DataSource_Stream input(get_arg("datafile")); + + Botan::BER_Decoder(Botan::PEM_Code::decode_check_label(input, "PUBKEY ENCRYPTED MESSAGE")) + .start_cons(Botan::SEQUENCE) + .decode(pk_alg_id) + .decode(encrypted_key, Botan::OCTET_STRING) + .decode(aead_oid) + .decode(nonce, Botan::OCTET_STRING) + .decode(data, Botan::OCTET_STRING) + .end_cons(); + } + catch(Botan::Decoding_Error&) + { + output() << "Parsing input file failed: invalid format?\n"; + set_return_code(1); + return; + } + + const std::string aead_algo = Botan::OIDS::lookup(aead_oid); + if(aead_algo == "") + { + output() << "Ciphertext was encrypted with an unknown algorithm"; + set_return_code(1); + return; + } + + if(pk_alg_id.get_oid() != Botan::OIDS::lookup("RSA/OAEP")) + { + output() << "Ciphertext was encrypted with something other than RSA/OAEP"; + set_return_code(1); + return; + } + + Botan::AlgorithmIdentifier oaep_hash; + Botan::BER_Decoder(pk_alg_id.get_parameters()).decode(oaep_hash); + + Botan::PK_Decryptor_EME dec(*key, rng(), "OAEP(" + Botan::OIDS::lookup(oaep_hash.get_oid()) + ")"); + + const Botan::secure_vector<uint8_t> file_key = dec.decrypt(encrypted_key); + + std::unique_ptr<Botan::AEAD_Mode> aead = + Botan::AEAD_Mode::create_or_throw(aead_algo, Botan::DECRYPTION); + + aead->set_key(file_key); + aead->set_associated_data_vec(encrypted_key); + aead->start(nonce); + + try + { + aead->finish(data); + + output().write(reinterpret_cast<const char*>(data.data()), data.size()); + } + catch(Botan::Integrity_Failure&) + { + output() << "Message authentication failure, possible ciphertext tampering\n"; + set_return_code(1); + return; + } + } + }; + +BOTAN_REGISTER_COMMAND("pk_decrypt", PK_Decrypt); + +} + +} + +#endif |