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.append(
"dec '")
154 .append(jau::toHexString(recevr_fingerprints_.at(used_recevr_key_idx_), jau::lb_endian_t::little))
157 if( force_all_fingerprints || 0 > used_recevr_key_idx_ ) {
158 recevr_fingerprint_str +=
"enc[";
160 for(
const std::vector<uint8_t>& tkf : recevr_fingerprints_) {
162 recevr_fingerprint_str.append(
", ");
164 recevr_fingerprint_str.append(
"'").append(jau::toHexString(tkf, jau::lb_endian_t::little)).append(
"'");
167 recevr_fingerprint_str.append(
"]");
171 std::string res =
"Header[";
172 res +=
"valid "+std::to_string(
isValid() )+
173 ", file[target_path '"+target_path_+
"', plaintext_size "+jau::to_decstring(plaintext_size_)+
174 "], creation "+ts_creation_.toISO8601String()+
" UTC, subject '"+subject_+
"', "+
175 " version['"+plaintext_version_+
176 "', parent '"+plaintext_version_parent_+
"']"+crypto_str+
177 ", fingerprints[sender '"+jau::toHexString(sender_fingerprint_, jau::lb_endian_t::little)+
178 "', recevr["+recevr_fingerprint_str+
179 "]], phash['"+plaintext_hash_algo_+
"', sz "+std::to_string(plaintext_hash_.size())+
"]]";
240 const std::string& passphrase,
241 Botan::AlgorithmIdentifier& pk_alg_id,
244 Botan::AlgorithmIdentifier pbe_alg_id;
248 if(Botan::ASN1::maybe_BER(source) && !Botan::PEM_Code::matches(source)) {
253 while(!source.end_of_data()) {
255 size_t read = source.read_byte(b);
257 key_data.push_back(b);
263 key_data = Botan::PEM_Code::decode(source, label);
266 if(label ==
"PRIVATE KEY") {
267 is_encrypted =
false;
268 }
else if(label ==
"ENCRYPTED PRIVATE KEY") {
269 Botan::DataSource_Memory key_source(key_data);
272 throw Botan::PKCS8_Exception(
"Unknown PEM label " + label);
276 if(key_data.empty()) {
277 throw Botan::PKCS8_Exception(
"No key data found");
279 }
catch(Botan::Decoding_Error& e) {
280 throw Botan::Decoding_Error(
"PKCS #8 private key decoding", e);
285 if(Botan::OIDS::oid2str_or_throw(pbe_alg_id.get_oid()) !=
"PBE-PKCS5v20") {
286 throw Botan::PKCS8_Exception(
"Unknown PBE type " + pbe_alg_id.get_oid().to_string());
288#if defined(BOTAN_HAS_PKCS5_PBES2)
289 key = Botan::pbes2_decrypt(key_data, passphrase, pbe_alg_id.get_parameters());
291 #error Private key is encrypted but PBES2 was disabled in build
292 BOTAN_UNUSED(passphrase);
293 throw Botan::Decoding_Error(
"Private key is encrypted but PBES2 was disabled in build");
299 Botan::BER_Decoder(key)
301 .decode_and_check<
size_t>(0,
"Unknown PKCS #8 version number")
303 .decode(key, Botan::ASN1_Type::OctetString)
306 }
catch(std::exception& e) {
307 throw Botan::Decoding_Error(
"PKCS #8 private key decoding", e);
313 jau::io::ByteStream_File key_data_(privatekey_fname);
315 std::shared_ptr<Botan::Private_Key> key;
316 if( passphrase.empty() ) {
317 key = Botan::PKCS8::load_key(key_data);
328 std::string insec_passphrase_copy(passphrase);
329 Botan::AlgorithmIdentifier alg_id;
332 const std::string alg_name = Botan::OIDS::oid2str_or_empty(alg_id.get_oid());
333 if( alg_name.empty() ) {
334 throw Botan::PKCS8_Exception(
"Unknown algorithm OID: " + alg_id.get_oid().to_string());
336 key = Botan::load_private_key(alg_id, pkcs8_key);
337 ::explicit_bzero(insec_passphrase_copy.data(), insec_passphrase_copy.size());
340 jau_ERR_PRINT(
"Couldn't load Key %s", privatekey_fname);
341 return std::shared_ptr<Botan::Private_Key>();
343 if( key->algo_name() !=
"RSA" ) {
344 jau_ERR_PRINT(
"Key doesn't support RSA %s", privatekey_fname);
345 return std::shared_ptr<Botan::Private_Key>();
361 const std::string hash_str = jau::toHexString(hash_value.data(), hash_value.size(), jau::lb_endian_t::little);
363 jau::io::ByteStream_File out(out_file);
367 if( hash_algo.size() != out.write(hash_algo.data(), hash_algo.size()) ) {
370 if( 1 != out.write(
" ", 1) ) {
373 if( hash_str.size() != out.write(hash_str.data(), hash_str.size()) ) {
376 if( 2 != out.write(
" *", 2) ) {
379 if( hashed_file.size() != out.write(hashed_file.data(), hashed_file.size()) ) {
382 if( 1 != out.write(
"\n", 1) ) {
389 const std::string algo_s(algo);
390 std::unique_ptr<Botan::HashFunction> hash_func = Botan::HashFunction::create(algo_s);
391 if(
nullptr == hash_func ) {
392 jau_ERR_PRINT2(
"Hash failed: Algo %s not available", algo_s);
395 jau::io::StreamConsumerFunc consume_data = [&](jau::io::secure_vector<uint8_t>& data,
bool is_final) ->
bool {
397 hash_func->update(data.data(), data.size());
400 jau::io::secure_vector<uint8_t> io_buffer;
402 const uint64_t in_bytes_total = jau::io::read_stream(source, io_buffer, consume_data);
404 if( source.hasContentSize() && in_bytes_total != source.contentSize() ) {
405 jau_ERR_PRINT2(
"Hash failed: Only read %" PRIu64
" bytes of %s", in_bytes_total, source.toString());
408 std::unique_ptr<std::vector<uint8_t>> res = std::make_unique<std::vector<uint8_t>>(hash_func->output_length());
409 hash_func->final(res->data());
413std::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 using namespace jau::io::fs;
417 if( !jau::io::uri_tk::is_local_file_protocol(path_or_uri) &&
418 jau::io::uri_tk::protocol_supported(path_or_uri) )
420 jau::io::ByteInStream_URL in(path_or_uri, timeout);
422 return calc(algo, in);
425 std::unique_ptr<file_stats> stats;
426 if( jau::io::uri_tk::is_local_file_protocol(path_or_uri) ) {
428 std::string path2 = path_or_uri.substr(7);
429 stats = std::make_unique<file_stats>(path2);
431 stats = std::make_unique<file_stats>(path_or_uri);
433 if( !stats->is_dir() ) {
434 if( stats->has_fd() ) {
435 jau::io::ByteStream_File in(stats->fd());
439 return calc(algo, in);
441 jau::io::ByteStream_File in(stats->path());
445 return calc(algo, in);
452 std::vector<int> dirfds;
453 std::unique_ptr<Botan::HashFunction> hash_func;
454 jau::io::secure_vector<uint8_t> io_buffer;
455 jau::io::StreamConsumerFunc consume_data;
456 uint64_t bytes_hashed;
458 context_t ctx { .dirfds=std::vector<int>(), .hash_func=
nullptr, .io_buffer=jau::io::secure_vector<uint8_t>(), .consume_data=
nullptr, .bytes_hashed=0 };
460 const std::string algo_s(algo);
461 ctx.hash_func = Botan::HashFunction::create(algo_s);
462 if(
nullptr == ctx.hash_func ) {
463 jau_ERR_PRINT2(
"Hash failed: Algo %s not available", algo_s);
467 ctx.consume_data = [&](jau::io::secure_vector<uint8_t>& data,
bool is_final) ->
bool {
469 ctx.hash_func->update(data.data(), data.size());
474 const path_visitor pv = jau::bind_capref<bool, context_t, traverse_event, const file_stats&, size_t>(&ctx,
475 (
bool(*)(context_t*, traverse_event,
const file_stats&,
size_t) )
476 ( [](context_t* ctx_ptr, traverse_event tevt,
const file_stats& element_stats,
size_t depth) ->
bool {
478 if( is_set(tevt, traverse_event::file) &&
479 !is_set(tevt, traverse_event::symlink) )
481 jau::io::ByteStream_File in(ctx_ptr->dirfds.back(), element_stats.item().basename());
485 const uint64_t in_bytes_total = jau::io::read_stream(in, ctx_ptr->io_buffer, ctx_ptr->consume_data);
487 if( in.hasContentSize() && in_bytes_total != in.contentSize() ) {
488 jau_ERR_PRINT2(
"Hash failed: Only read %" PRIu64
" bytes of %s", in_bytes_total, in.toString());
491 ctx_ptr->bytes_hashed += in_bytes_total;
495 const traverse_options topts = traverse_options::recursive | traverse_options::lexicographical_order;
496 if( visit(*stats, topts, pv, &ctx.dirfds) ) {
497 std::unique_ptr<std::vector<uint8_t>> res = std::make_unique<std::vector<uint8_t>>(ctx.hash_func->output_length());
498 ctx.hash_func->final(res->data());
499 bytes_hashed = ctx.bytes_hashed;