1 /* 2 * (C) 2018 Jack Lloyd 3 * 4 * Botan is released under the Simplified BSD License (see license.txt) 5 */ 6 7 #include "cli.h" 8 9 #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) && defined(BOTAN_TARGET_OS_HAS_FILESYSTEM) 10 11 #include <botan/pubkey.h> 12 #include <botan/x509_key.h> 13 #include <botan/pkcs8.h> 14 #include <botan/der_enc.h> 15 #include <botan/ber_dec.h> 16 #include <botan/oids.h> 17 #include <botan/aead.h> 18 #include <botan/pem.h> 19 #include <botan/rng.h> 20 21 namespace Botan_CLI { 22 23 namespace { 24 25 class PK_Encrypt final : public Command 26 { 27 public: PK_Encrypt()28 PK_Encrypt() : Command("pk_encrypt --aead=AES-256/GCM pubkey datafile") {} 29 group() const30 std::string group() const override 31 { 32 return "pubkey"; 33 } 34 description() const35 std::string description() const override 36 { 37 return "Encrypt a file using a RSA public key"; 38 } 39 go()40 void go() override 41 { 42 std::unique_ptr<Botan::Public_Key> key(Botan::X509::load_key(get_arg("pubkey"))); 43 if(!key) 44 { 45 throw CLI_Error("Unable to load public key"); 46 } 47 48 if(key->algo_name() != "RSA") 49 { 50 throw CLI_Usage_Error("This function requires an RSA key"); 51 } 52 53 const std::string OAEP_HASH = "SHA-256"; 54 const std::string aead_algo = get_arg("aead"); 55 56 std::unique_ptr<Botan::AEAD_Mode> aead = 57 Botan::AEAD_Mode::create(aead_algo, Botan::ENCRYPTION); 58 59 if(!aead) 60 throw CLI_Usage_Error("The AEAD '" + aead_algo + "' is not available"); 61 62 const Botan::OID aead_oid = Botan::OID::from_string(aead_algo); 63 if(aead_oid.empty()) 64 throw CLI_Usage_Error("No OID defined for AEAD '" + aead_algo + "'"); 65 66 Botan::secure_vector<uint8_t> data; 67 auto insert_fn = [&](const uint8_t b[], size_t l) 68 { 69 data.insert(data.end(), b, b + l); 70 }; 71 this->read_file(get_arg("datafile"), insert_fn); 72 73 const Botan::AlgorithmIdentifier hash_id(OAEP_HASH, Botan::AlgorithmIdentifier::USE_EMPTY_PARAM); 74 const Botan::AlgorithmIdentifier pk_alg_id("RSA/OAEP", hash_id.BER_encode()); 75 76 Botan::PK_Encryptor_EME enc(*key, rng(), "OAEP(" + OAEP_HASH + ")"); 77 78 const Botan::secure_vector<uint8_t> file_key = rng().random_vec(aead->key_spec().maximum_keylength()); 79 80 const std::vector<uint8_t> encrypted_key = enc.encrypt(file_key, rng()); 81 82 const Botan::secure_vector<uint8_t> nonce = rng().random_vec(aead->default_nonce_length()); 83 aead->set_key(file_key); 84 aead->set_associated_data_vec(encrypted_key); 85 aead->start(nonce); 86 87 aead->finish(data); 88 89 std::vector<uint8_t> buf; 90 Botan::DER_Encoder der(buf); 91 92 der.start_cons(Botan::SEQUENCE) 93 .encode(pk_alg_id) 94 .encode(encrypted_key, Botan::OCTET_STRING) 95 .encode(aead_oid) 96 .encode(nonce, Botan::OCTET_STRING) 97 .encode(data, Botan::OCTET_STRING) 98 .end_cons(); 99 100 output() << Botan::PEM_Code::encode(buf, "PUBKEY ENCRYPTED MESSAGE", 72); 101 } 102 }; 103 104 BOTAN_REGISTER_COMMAND("pk_encrypt", PK_Encrypt); 105 106 class PK_Decrypt final : public Command 107 { 108 public: PK_Decrypt()109 PK_Decrypt() : Command("pk_decrypt privkey datafile") {} 110 group() const111 std::string group() const override 112 { 113 return "pubkey"; 114 } 115 description() const116 std::string description() const override 117 { 118 return "Decrypt a file using a RSA private key"; 119 } 120 go()121 void go() override 122 { 123 Botan::DataSource_Stream input_stream(get_arg("privkey")); 124 auto get_pass = [this]() { return get_passphrase("Password"); }; 125 std::unique_ptr<Botan::Private_Key> key = Botan::PKCS8::load_key(input_stream, get_pass); 126 127 if(!key) 128 { 129 throw CLI_Error("Unable to load public key"); 130 } 131 132 if(key->algo_name() != "RSA") 133 { 134 throw CLI_Usage_Error("This function requires an RSA key"); 135 } 136 137 Botan::secure_vector<uint8_t> data; 138 std::vector<uint8_t> encrypted_key; 139 std::vector<uint8_t> nonce; 140 Botan::AlgorithmIdentifier pk_alg_id; 141 Botan::OID aead_oid; 142 143 try 144 { 145 Botan::DataSource_Stream input(get_arg("datafile")); 146 147 Botan::BER_Decoder(Botan::PEM_Code::decode_check_label(input, "PUBKEY ENCRYPTED MESSAGE")) 148 .start_cons(Botan::SEQUENCE) 149 .decode(pk_alg_id) 150 .decode(encrypted_key, Botan::OCTET_STRING) 151 .decode(aead_oid) 152 .decode(nonce, Botan::OCTET_STRING) 153 .decode(data, Botan::OCTET_STRING) 154 .end_cons(); 155 } 156 catch(Botan::Decoding_Error&) 157 { 158 error_output() << "Parsing input file failed: invalid format?\n"; 159 return set_return_code(1); 160 } 161 162 const std::string aead_algo = Botan::OIDS::oid2str_or_empty(aead_oid); 163 if(aead_algo == "") 164 { 165 error_output() << "Ciphertext was encrypted with an unknown algorithm"; 166 return set_return_code(1); 167 } 168 169 if(pk_alg_id.get_oid() != Botan::OID::from_string("RSA/OAEP")) 170 { 171 error_output() << "Ciphertext was encrypted with something other than RSA/OAEP"; 172 return set_return_code(1); 173 } 174 175 Botan::AlgorithmIdentifier oaep_hash_id; 176 Botan::BER_Decoder(pk_alg_id.get_parameters()).decode(oaep_hash_id); 177 178 const std::string oaep_hash = Botan::OIDS::oid2str_or_empty(oaep_hash_id.get_oid()); 179 180 if(oaep_hash.empty()) 181 { 182 error_output() << "Unknown hash function used with OAEP, OID " << oaep_hash_id.get_oid().to_string() << "\n"; 183 return set_return_code(1); 184 } 185 186 if(oaep_hash_id.get_parameters().empty() == false) 187 { 188 error_output() << "Unknown OAEP parameters used\n"; 189 return set_return_code(1); 190 } 191 192 std::unique_ptr<Botan::AEAD_Mode> aead = 193 Botan::AEAD_Mode::create_or_throw(aead_algo, Botan::DECRYPTION); 194 195 const size_t expected_keylen = aead->key_spec().maximum_keylength(); 196 197 Botan::PK_Decryptor_EME dec(*key, rng(), "OAEP(" + oaep_hash + ")"); 198 199 const Botan::secure_vector<uint8_t> file_key = 200 dec.decrypt_or_random(encrypted_key.data(), 201 encrypted_key.size(), 202 expected_keylen, 203 rng()); 204 205 aead->set_key(file_key); 206 aead->set_associated_data_vec(encrypted_key); 207 aead->start(nonce); 208 209 try 210 { 211 aead->finish(data); 212 213 output().write(reinterpret_cast<const char*>(data.data()), data.size()); 214 } 215 catch(Botan::Integrity_Failure&) 216 { 217 error_output() << "Message authentication failure, possible ciphertext tampering\n"; 218 return set_return_code(1); 219 } 220 } 221 }; 222 223 BOTAN_REGISTER_COMMAND("pk_decrypt", PK_Decrypt); 224 225 } 226 227 } 228 229 #endif 230