29 #include <curl/curl.h>
32#include <jau/cpuid.hpp>
33#include <jau/os/os_support.hpp>
35#include <jau/debug.hpp>
57 std::unique_ptr<Botan::HashFunction> hash_func = Botan::HashFunction::create(algo);
58 if(
nullptr == hash_func ) {
61 return hash_func->provider();
67 jau::fprintf_td(stderr,
"hash '%s': Not available, provider {", algo.c_str());
69 jau::fprintf_td(stderr,
"hash '%s': provider '%s' of {", algo.c_str(), p.c_str());
71 std::vector<std::string> hash_provider = Botan::HashFunction::providers(algo);
72 for(
const std::string& pi : hash_provider) {
73 ::fprintf(stderr,
"'%s', ", pi.c_str());
75 ::fprintf(stderr,
"}\n");
78environment::environment() noexcept {
79 jau::environment::get(
"cipherpack");
82 curl_global_init(CURL_GLOBAL_ALL);
88 jau::fprintf_td(stderr,
"%s\n", jau::os::get_platform_info().c_str());
148 const std::string crypto_str = show_crypto_algos ?
", "+crypto_cfg_.to_string() :
"";
150 std::string recevr_fingerprint_str;
152 if( 0 <= used_recevr_key_idx_ ) {
153 recevr_fingerprint_str +=
"dec '"+jau::bytesHexString(recevr_fingerprints_.at(used_recevr_key_idx_),
true )+
"', ";
155 if( force_all_fingerprints || 0 > used_recevr_key_idx_ ) {
156 recevr_fingerprint_str +=
"enc[";
158 for(
const std::vector<uint8_t>& tkf : recevr_fingerprints_) {
160 recevr_fingerprint_str +=
", ";
162 recevr_fingerprint_str.append(
"'").append(jau::bytesHexString(tkf,
true )).append(
"'");
165 recevr_fingerprint_str +=
"]";
169 std::string res =
"Header[";
171 ", file[target_path '"+target_path_+
"', plaintext_size "+jau::to_decstring(plaintext_size_).c_str()+
172 "], creation "+ts_creation_.to_iso8601_string()+
" UTC, subject '"+subject_+
"', "+
173 " version['"+plaintext_version_+
174 "', parent '"+plaintext_version_parent_+
"']"+crypto_str+
175 ", fingerprints[sender '"+jau::bytesHexString(sender_fingerprint_,
true )+
176 "', recevr["+recevr_fingerprint_str+
177 "]], phash['"+plaintext_hash_algo_+
"', sz "+
std::to_string(plaintext_hash_.size())+
"]]";
182 jau::io::ByteInStream_File key_data_(pubkey_fname);
184 std::shared_ptr<Botan::Public_Key> key(Botan::X509::load_key(key_data));
186 ERR_PRINT(
"Couldn't load Key %s", pubkey_fname.c_str());
187 return std::shared_ptr<Botan::Public_Key>();
189 if( key->algo_name() !=
"RSA" ) {
190 ERR_PRINT(
"Key doesn't support RSA %s", pubkey_fname.c_str());
191 return std::shared_ptr<Botan::Public_Key>();
203 Botan::AlgorithmIdentifier& pbe_alg_id)
207 Botan::BER_Decoder(source)
210 .decode(key_data, Botan::ASN1_Type::OctetString)
216#if defined(BOTAN_HAS_PKCS5_PBES2)
226 const std::string& passphrase,
227 const std::vector<uint8_t>& params);
238 const std::string& passphrase,
239 Botan::AlgorithmIdentifier& pk_alg_id,
242 Botan::AlgorithmIdentifier pbe_alg_id;
246 if(Botan::ASN1::maybe_BER(source) && !Botan::PEM_Code::matches(source)) {
251 while(!source.end_of_data()) {
253 size_t read = source.read_byte(b);
255 key_data.push_back(b);
261 key_data = Botan::PEM_Code::decode(source, label);
264 if(label ==
"PRIVATE KEY") {
265 is_encrypted =
false;
266 }
else if(label ==
"ENCRYPTED PRIVATE KEY") {
267 Botan::DataSource_Memory key_source(key_data);
270 throw Botan::PKCS8_Exception(
"Unknown PEM label " + label);
274 if(key_data.empty()) {
275 throw Botan::PKCS8_Exception(
"No key data found");
277 }
catch(Botan::Decoding_Error& e) {
278 throw Botan::Decoding_Error(
"PKCS #8 private key decoding", e);
283 if(Botan::OIDS::oid2str_or_throw(pbe_alg_id.get_oid()) !=
"PBE-PKCS5v20") {
284 throw Botan::PKCS8_Exception(
"Unknown PBE type " + pbe_alg_id.get_oid().to_string());
286#if defined(BOTAN_HAS_PKCS5_PBES2)
287 key = Botan::pbes2_decrypt(key_data, passphrase, pbe_alg_id.get_parameters());
289 #error Private key is encrypted but PBES2 was disabled in build
290 BOTAN_UNUSED(passphrase);
291 throw Botan::Decoding_Error(
"Private key is encrypted but PBES2 was disabled in build");
297 Botan::BER_Decoder(key)
299 .decode_and_check<
size_t>(0,
"Unknown PKCS #8 version number")
301 .decode(key, Botan::ASN1_Type::OctetString)
304 }
catch(std::exception& e) {
305 throw Botan::Decoding_Error(
"PKCS #8 private key decoding", e);
311 jau::io::ByteInStream_File key_data_(privatekey_fname);
313 std::shared_ptr<Botan::Private_Key> key;
314 if( passphrase.empty() ) {
315 key = Botan::PKCS8::load_key(key_data);
326 std::string insec_passphrase_copy(passphrase);
327 Botan::AlgorithmIdentifier alg_id;
330 const std::string alg_name = Botan::OIDS::oid2str_or_empty(alg_id.get_oid());
331 if( alg_name.empty() ) {
332 throw Botan::PKCS8_Exception(
"Unknown algorithm OID: " + alg_id.get_oid().to_string());
335 ::explicit_bzero(insec_passphrase_copy.data(), insec_passphrase_copy.size());
338 ERR_PRINT(
"Couldn't load Key %s", privatekey_fname.c_str());
339 return std::shared_ptr<Botan::Private_Key>();
341 if( key->algo_name() !=
"RSA" ) {
342 ERR_PRINT(
"Key doesn't support RSA %s", privatekey_fname.c_str());
343 return std::shared_ptr<Botan::Private_Key>();
349 std::string s = algo;
351 std::transform(s.begin(), s.end(), s.begin(), ::tolower);
353 auto it = std::remove( s.begin(), s.end(),
'-');
354 s.erase(it, s.end());
359 const std::string hash_str = jau::bytesHexString(hash_value.data(), 0, hash_value.size(),
true ,
true );
361 jau::io::ByteOutStream_File out(out_file);
365 if( hash_algo.size() != out.write(hash_algo.data(), hash_algo.size()) ) {
368 if( 1 != out.write(
" ", 1) ) {
371 if( hash_str.size() != out.write(hash_str.data(), hash_str.size()) ) {
374 if( 2 != out.write(
" *", 2) ) {
377 if( hashed_file.size() != out.write(hashed_file.data(), hashed_file.size()) ) {
380 if( 1 != out.write(
"\n", 1) ) {
387 const std::string algo_s(algo);
388 std::unique_ptr<Botan::HashFunction> hash_func = Botan::HashFunction::create(algo_s);
389 if(
nullptr == hash_func ) {
390 ERR_PRINT2(
"Hash failed: Algo %s not available", algo_s.c_str());
393 jau::io::StreamConsumerFunc consume_data = [&](jau::io::secure_vector<uint8_t>& data,
bool is_final) ->
bool {
395 hash_func->update(data.data(), data.size());
398 jau::io::secure_vector<uint8_t> io_buffer;
400 const uint64_t in_bytes_total = jau::io::read_stream(source, io_buffer, consume_data);
402 if( source.has_content_size() && in_bytes_total != source.content_size() ) {
403 ERR_PRINT2(
"Hash failed: Only read %" PRIu64
" bytes of %s", in_bytes_total, source.to_string().c_str());
406 std::unique_ptr<std::vector<uint8_t>> res = std::make_unique<std::vector<uint8_t>>(hash_func->output_length());
407 hash_func->final(res->data());
411std::unique_ptr<std::vector<uint8_t>>
cipherpack::hash_util::calc(
const std::string_view& algo,
const std::string& path_or_uri, uint64_t& bytes_hashed, jau::fraction_i64 timeout)
noexcept {
414 if( !jau::io::uri_tk::is_local_file_protocol(path_or_uri) &&
415 jau::io::uri_tk::protocol_supported(path_or_uri) )
417 jau::io::ByteInStream_URL in(path_or_uri, timeout);
419 return calc(algo, in);
422 std::unique_ptr<jau::fs::file_stats> stats;
423 if( jau::io::uri_tk::is_local_file_protocol(path_or_uri) ) {
425 std::string path2 = path_or_uri.substr(7);
426 stats = std::make_unique<jau::fs::file_stats>(path2);
428 stats = std::make_unique<jau::fs::file_stats>(path_or_uri);
430 if( !stats->is_dir() ) {
431 if( stats->has_fd() ) {
432 jau::io::ByteInStream_File in(stats->fd());
436 return calc(algo, in);
438 jau::io::ByteInStream_File in(stats->path());
442 return calc(algo, in);
449 std::vector<int> dirfds;
450 std::unique_ptr<Botan::HashFunction> hash_func;
451 jau::io::secure_vector<uint8_t> io_buffer;
452 jau::io::StreamConsumerFunc consume_data;
453 uint64_t bytes_hashed;
455 context_t ctx { std::vector<int>(),
nullptr, jau::io::secure_vector<uint8_t>(),
nullptr, 0 };
457 const std::string algo_s(algo);
458 ctx.hash_func = Botan::HashFunction::create(algo_s);
459 if(
nullptr == ctx.hash_func ) {
460 ERR_PRINT2(
"Hash failed: Algo %s not available", algo_s.c_str());
464 ctx.consume_data = [&](jau::io::secure_vector<uint8_t>& data,
bool is_final) ->
bool {
466 ctx.hash_func->update(data.data(), data.size());
471 const jau::fs::path_visitor pv = jau::bind_capref<bool, context_t, jau::fs::traverse_event, const jau::fs::file_stats&, size_t>(&ctx,
472 (
bool(*)(context_t*, jau::fs::traverse_event,
const jau::fs::file_stats&,
size_t) )
473 ( [](context_t* ctx_ptr, jau::fs::traverse_event tevt,
const jau::fs::file_stats& element_stats,
size_t depth) ->
bool {
475 if( is_set(tevt, jau::fs::traverse_event::file) && !is_set(tevt, jau::fs::traverse_event::symlink) ) {
476 jau::io::ByteInStream_File in(ctx_ptr->dirfds.back(), element_stats.item().basename());
480 const uint64_t in_bytes_total = jau::io::read_stream(in, ctx_ptr->io_buffer, ctx_ptr->consume_data);
482 if( in.has_content_size() && in_bytes_total != in.content_size() ) {
483 ERR_PRINT2(
"Hash failed: Only read %" PRIu64
" bytes of %s", in_bytes_total, in.to_string().c_str());
486 ctx_ptr->bytes_hashed += in_bytes_total;
490 const jau::fs::traverse_options topts = jau::fs::traverse_options::recursive | jau::fs::traverse_options::lexicographical_order;
491 if( jau::fs::visit(*stats, topts, pv, &ctx.dirfds) ) {
492 std::unique_ptr<std::vector<uint8_t>> res = std::make_unique<std::vector<uint8_t>>(ctx.hash_func->output_length());
493 ctx.hash_func->final(res->data());
494 bytes_hashed = ctx.bytes_hashed;
static std::string to_string()
Return a possibly empty string containing list of known CPU extensions.
static bool has_simd_32()
static const std::string package_magic
Package magic CIPHERPACK_0004.
static constexpr const size_t buffer_size
Intermediate copy buffer size of 16384 bytes, usually the 4 x 4096 bytes page-size.
This class represents an abstract data source object.
void print_info() noexcept
static const std::string default_pk_sign_algo
static constexpr const size_t ChaCha_Nonce_BitSize
Symmetric Encryption nonce size in bytes.
static const std::string default_pk_type
static std::string cp_query_hash_provider(const std::string &algo) noexcept
static const std::string default_sym_enc_mac_algo
static void cp_print_hash_provider(const std::string &algo) noexcept
static const std::string default_pk_fingerprt_hash_algo
static cipherpack::secure_vector< uint8_t > jau_PKCS8_extract(Botan::DataSource &source, Botan::AlgorithmIdentifier &pbe_alg_id)
Get info from an EncryptedPrivateKeyInfo.
static const std::string default_pk_enc_padding_algo
static cipherpack::secure_vector< uint8_t > jau_PKCS8_decode(Botan::DataSource &source, const std::string &passphrase, Botan::AlgorithmIdentifier &pk_alg_id, bool is_encrypted)
PEM decode and/or decrypt a private key.
static const std::string default_hash_algo_
static const std::string default_pk_enc_hash_algo
static std::string to_string(const std::vector< uint8_t > &v)
std::shared_ptr< Botan::Public_Key > load_public_key(const std::string &pubkey_fname)
std::shared_ptr< Botan::Private_Key > load_private_key(const std::string &privatekey_fname, const jau::io::secure_string &passphrase)
std::vector< T, Botan::secure_allocator< T > > secure_vector
std::string_view default_hash_algo() noexcept
Name of default hash algo for the plaintext message, e.g.
std::string file_suffix(const std::string &algo) noexcept
Return a lower-case file suffix used to store a sha256sum compatible hash signature w/o dot and w/o d...
std::unique_ptr< std::vector< uint8_t > > calc(const std::string_view &algo, jau::io::ByteInStream &source) noexcept
Return the calculated hash value using given algo name and byte input stream.
bool append_to_file(const std::string &out_file, const std::string &hashed_file, const std::string_view &hash_algo, const std::vector< uint8_t > &hash_value) noexcept
Append the hash signature to the text file out_file.
CryptoConfig, contains crypto algorithms settings given at encryption wired via the Cipherpack Data S...
size_t sym_enc_nonce_bytes
bool valid() const noexcept
static CryptoConfig getDefault() noexcept
Returns default CryptoConfig.
std::string pk_enc_padding_algo
std::string pk_fingerprt_hash_algo
std::string pk_enc_hash_algo
std::string to_string() const noexcept