224 const std::vector<std::string>& enc_pub_keys,
225 const std::string& sign_sec_key_fname,
const jau::io::secure_string& passphrase,
226 jau::io::ByteStream& source,
227 const std::string& target_path,
const std::string& subject,
228 const std::string& plaintext_version,
229 const std::string& plaintext_version_parent,
231 const std::string_view& plaintext_hash_algo) {
232 const bool decrypt_mode =
false;
234 const jau::fraction_timespec ts_creation = jau::getWallClockTime();
237 source.contentSize(),
240 plaintext_version, plaintext_version_parent,
242 std::vector<uint8_t>(),
243 std::vector<std::vector<uint8_t>>(),
247 if(
nullptr == listener ) {
248 jau_ERR_PRINT2(
"Encrypt failed: Listener is nullptr for source %s", source.toString());
252 if( source.fail() ) {
253 listener->
notifyError(decrypt_mode, header,
"Source has an error "+source.toString());
256 const bool has_plaintext_size = source.hasContentSize();
257 uint64_t plaintext_size = has_plaintext_size ? source.contentSize() : 0;
259 if( !crypto_cfg.
valid() ) {
264 const jau::fraction_timespec _t0 = jau::getMonotonicTime();
266 uint64_t out_bytes_header = 0;
269 std::unique_ptr<Botan::HashFunction> plaintext_hash_func =
nullptr;
270 if( !plaintext_hash_algo.empty() ) {
271 const std::string plaintext_hash_algo_s(plaintext_hash_algo);
272 plaintext_hash_func = Botan::HashFunction::create(plaintext_hash_algo_s);
273 if(
nullptr == plaintext_hash_func ) {
274 listener->
notifyError(decrypt_mode, header,
"Plaintext hash algo '"+plaintext_hash_algo_s+
"' not available");
278 std::unique_ptr<Botan::HashFunction> fingerprint_hash_func = Botan::HashFunction::create(crypto_cfg.
pk_fingerprt_hash_algo);
279 if(
nullptr == fingerprint_hash_func ) {
284 Botan::RandomNumberGenerator& rng = Botan::system_rng();
286 std::shared_ptr<Botan::Private_Key> sign_sec_key =
load_private_key(sign_sec_key_fname, passphrase);
287 if( !sign_sec_key ) {
291 const Botan::OID sym_enc_algo_oid = Botan::OID::from_string(crypto_cfg.
sym_enc_algo);
292 if( sym_enc_algo_oid.empty() ) {
296 std::shared_ptr<Botan::AEAD_Mode> aead = Botan::AEAD_Mode::create(crypto_cfg.
sym_enc_algo, Botan::ENCRYPTION);
303 std::vector<uint8_t> sender_fingerprint =
_fingerprint_public(*sign_sec_key, *fingerprint_hash_func);
305 struct recevr_data_t {
306 std::shared_ptr<Botan::Public_Key> pub_key;
307 std::vector<uint8_t> fingerprint;
308 std::vector<uint8_t> encrypted_sym_key;
309 std::vector<uint8_t> encrypted_nonce;
311 std::vector<recevr_data_t> recevr_data_list;
312 std::vector<std::vector<uint8_t>> recevr_fingerprints;
314 for(
const std::string& pub_key_fname : enc_pub_keys ) {
315 recevr_data_t recevr_data;
318 if( !recevr_data.pub_key ) {
319 listener->
notifyError(decrypt_mode, header,
"Loading pub-key file '"+pub_key_fname+
"' failed");
323 recevr_data.fingerprint =
_fingerprint_public(*recevr_data.pub_key, *fingerprint_hash_func);
324 recevr_fingerprints.push_back(recevr_data.fingerprint);
325 recevr_data.encrypted_sym_key = enc.encrypt(plain_sym_key, rng);
326 recevr_data.encrypted_nonce = enc.encrypt(nonce, rng);
327 recevr_data_list.push_back(recevr_data);
330 std::vector<uint8_t> sender_signature;
336 header_buffer.clear();
338 Botan::DER_Encoder der(header_buffer);
341 .encode(
to_OctetString( target_path ), Botan::ASN1_Type::OctetString )
342 .encode(
to_BigInt( plaintext_size ), Botan::ASN1_Type::Integer )
343 .encode(
to_BigInt(
static_cast<uint64_t
>( ts_creation.tv_sec ) ), Botan::ASN1_Type::Integer )
344 .encode(
to_BigInt(
static_cast<uint64_t
>( ts_creation.tv_nsec ) ), Botan::ASN1_Type::Integer )
345 .encode(
to_OctetString( subject ), Botan::ASN1_Type::OctetString )
346 .encode(
to_OctetString( plaintext_version ), Botan::ASN1_Type::OctetString )
347 .encode(
to_OctetString( plaintext_version_parent ), Botan::ASN1_Type::OctetString )
353 .encode( sym_enc_algo_oid )
354 .encode( sender_fingerprint, Botan::ASN1_Type::OctetString )
355 .encode( recevr_data_list.size(), Botan::ASN1_Type::Integer )
359 Botan::PK_Signer signer(*sign_sec_key, rng, crypto_cfg.
pk_sign_algo);
360 signer.update(header_buffer);
361 out_bytes_header += header_buffer.size();
365 jau_DBG_PRINT(
"Encrypt: DER Header1 written + %zu bytes / %" PRIu64
" bytes", header_buffer.size(), out_bytes_header);
367 for(
const recevr_data_t& recevr_data : recevr_data_list) {
369 header_buffer.clear();
371 Botan::DER_Encoder der(header_buffer);
373 .encode( recevr_data.fingerprint, Botan::ASN1_Type::OctetString )
374 .encode( recevr_data.encrypted_sym_key, Botan::ASN1_Type::OctetString )
375 .encode( recevr_data.encrypted_nonce, Botan::ASN1_Type::OctetString )
378 signer.update(header_buffer);
379 out_bytes_header += header_buffer.size();
383 jau_DBG_PRINT(
"Encrypt: DER Header-recevr written + %zu bytes / %" PRIu64
" bytes", header_buffer.size(), out_bytes_header);
387 sender_signature = signer.signature(rng);
388 jau_DBG_PRINT(
"Encrypt: Signature for %" PRIu64
" bytes: %s", out_bytes_header,
389 jau::toHexString(sender_signature, jau::lb_endian_t::little));
390 header_buffer.clear();
392 Botan::DER_Encoder der(header_buffer);
394 .encode( sender_signature, Botan::ASN1_Type::OctetString )
397 out_bytes_header += header_buffer.size();
401 jau_DBG_PRINT(
"Encrypt: DER Header2 written + %zu bytes / %" PRIu64
" bytes for %zu keys", header_buffer.size(), out_bytes_header, recevr_data_list.size());
408 plaintext_version, plaintext_version_parent,
415 jau_DBG_PRINT(
"Encrypt: DER Header done, %" PRIu64
" header: %s",
416 out_bytes_header, header.
to_string(
true ,
true ));
419 jau_DBG_PRINT(
"Encrypt: notifyHeader() user abort from %s", source.toString());
427 aead->set_key(plain_sym_key);
428 aead->set_associated_data_vec(sender_signature);
431 const bool sent_content_to_user = listener->
getSendContent( decrypt_mode );
432 uint64_t out_bytes_ciphertext = 0;
433 bool consume_abort =
false;
438 if(
nullptr != plaintext_hash_func ) {
439 plaintext_hash_func->update(data);
442 if( sent_content_to_user ) {
445 out_bytes_ciphertext += data.size();
447 res = listener->
notifyProgress(decrypt_mode, plaintext_size, source.position());
449 jau_DBG_PRINT(
"Encrypt: EncPayload written0 + %zu bytes -> %" PRIu64
" bytes / %zu bytes, user[sent %d, res %d]",
450 data.size(), out_bytes_ciphertext, plaintext_size, sent_content_to_user, res);
452 if(
nullptr != plaintext_hash_func ) {
453 plaintext_hash_func->update(data);
456 if( sent_content_to_user ) {
459 out_bytes_ciphertext += data.size();
460 if( !has_plaintext_size ) {
461 plaintext_size = out_bytes_ciphertext;
465 res = listener->
notifyProgress(decrypt_mode, plaintext_size, source.position());
467 jau_DBG_PRINT(
"Encrypt: EncPayload writtenF + %zu bytes -> %" PRIu64
" bytes / %zu bytes, user[sent %d, res %d]",
468 data.size(), out_bytes_ciphertext, plaintext_size, sent_content_to_user, res);
470 consume_abort = !res;
478 const uint64_t in_bytes_total =
_read_stream(source, io_buffer, consume_data);
481 if(
nullptr != plaintext_hash_func ) {
482 std::vector<uint8_t> hash_value( plaintext_hash_func->output_length() );
483 plaintext_hash_func->final(hash_value.data());
487 if( consume_abort ) {
488 jau_DBG_PRINT(
"Encrypt: Processing aborted %s", source.toString());
491 if ( source.fail() ) {
492 listener->
notifyError(decrypt_mode, header,
"Source read failed "+source.toString());
495 if( source.position() != in_bytes_total ) {
496 listener->
notifyError(decrypt_mode, header,
"Writing done, "+jau::to_decstring(in_bytes_total)+
" bytes read != "+source.toString());
498 }
else if( jau::environment::get().verbose ) {
499 jau_WORDY_PRINT(
"Encrypt: Reading done from %s", source.toString());
500 jau_WORDY_PRINT(
"Encrypt: Writing done, %s header + %s ciphertext for %s plaintext bytes written, ratio %f out/in",
501 jau::to_decstring(out_bytes_header),
502 jau::to_decstring(out_bytes_ciphertext),
503 jau::to_decstring(in_bytes_total), in_bytes_total > 0 ? (
double)(out_bytes_header+out_bytes_ciphertext)/(
double)in_bytes_total : 0.0);
504 jau_WORDY_PRINT(
"Encrypt: Writing done: source: %s", source.toString());
507 if( jau::environment::get().verbose ) {
508 const jau::fraction_i64 _td = ( jau::getMonotonicTime() - _t0 ).to_fraction_i64();
509 jau::io::print_stats(
"Encrypt", (out_bytes_header+out_bytes_ciphertext), _td);
513 }
catch (std::exception &e) {
514 jau_ERR_PRINT(
"Encrypt failed: Caught exception: %s on %s", e.what(), source.toString());
520 const std::vector<std::string>& enc_pub_keys,
521 const std::string& sign_sec_key_fname,
const jau::io::secure_string& passphrase,
522 jau::io::ByteStream& source,
523 const std::string& target_path,
const std::string& subject,
524 const std::string& plaintext_version,
525 const std::string& plaintext_version_parent,
527 const std::string_view& plaintext_hash_algo,
528 const std::string dest_fname) {
530 const bool decrypt_mode =
false;
532 if( dest_fname.empty() ) {
535 sign_sec_key_fname, passphrase,
537 target_path, subject,
539 plaintext_version_parent,
540 listener, plaintext_hash_algo);
542 listener->
notifyEnd(decrypt_mode, header);
546 std::string dest_fname2;
547 if( dest_fname ==
"/dev/stdout" || dest_fname ==
"-" ) {
548 dest_fname2 =
"/dev/stdout";
550 dest_fname2 = dest_fname;
553 const jau::fraction_timespec ts_creation = jau::getWallClockTime();
556 source.contentSize(),
559 plaintext_version, plaintext_version_parent,
561 std::vector<uint8_t>(),
562 std::vector<std::vector<uint8_t>>(),
566 if(
nullptr == listener ) {
567 jau_ERR_PRINT2(
"Encrypt failed: Listener is nullptr for source %s", source.toString());
573 jau::io::ByteStream_File* outfile_;
574 uint64_t& out_bytes_header_;
575 uint64_t& out_bytes_plaintext_;
579 out_bytes_header_(bytes_header), out_bytes_plaintext_(bytes_plaintext)
582 void set_outfile(jau::io::ByteStream_File* of)
noexcept { outfile_ = of; }
589 if(
nullptr != outfile_ ) {
590 if( data.size() != outfile_->write(data.data(), data.size()) ) {
593 if( outfile_->fail() ) {
597 case content_type::header:
598 out_bytes_header_ += data.size();
600 case content_type::message:
603 out_bytes_plaintext_ += data.size();
607 if( parent->getSendContent(decrypt_mode) ) {
608 return parent->contentProcessed(decrypt_mode, ctype, data, is_final);
614 uint64_t out_bytes_header=0, out_bytes_plaintext=0;
615 std::shared_ptr<MyListener> my_listener = std::make_shared<MyListener>(listener, out_bytes_header, out_bytes_plaintext);
617 const jau::io::fs::file_stats output_stats(dest_fname2);
618 if( output_stats.exists() && !output_stats.has_fd() ) {
619 if( output_stats.is_file() ) {
620 if( !jau::io::fs::remove(dest_fname2) ) {
621 listener->
notifyError(decrypt_mode, header,
"Failed deletion of existing output file "+output_stats.toString());
624 }
else if( output_stats.is_dir() || output_stats.is_block() ) {
625 listener->
notifyError(decrypt_mode, header,
"Not overwriting existing "+output_stats.toString());
630 jau::io::ByteStream_File outfile(dest_fname2);
631 if ( !outfile.good() ) {
632 listener->
notifyError(decrypt_mode, header,
"Output file not open "+dest_fname2);
635 my_listener->set_outfile(&outfile);
639 sign_sec_key_fname, passphrase,
641 target_path, subject,
643 plaintext_version_parent,
644 my_listener, plaintext_hash_algo);
646 const jau::io::fs::file_stats output_stats(dest_fname2);
647 if ( outfile.fail() ) {
648 if( output_stats.is_file() && !output_stats.has_fd() ) {
649 jau::io::fs::remove(dest_fname2);
652 listener->
notifyError(decrypt_mode, header,
"Output file write failed "+dest_fname2);
658 if( output_stats.is_file() && !output_stats.has_fd() ) {
659 jau::io::fs::remove(dest_fname2);
665 const jau::io::fs::file_stats output_stats(dest_fname2);
667 constexpr const bool ciphertext_size_same =
false;
668 if constexpr ( ciphertext_size_same ) {
670 if( output_stats.is_file() && out_bytes_header + out_bytes_plaintext != output_stats.size() ) {
671 if( output_stats.is_file() && !output_stats.has_fd() ) {
672 jau::io::fs::remove(dest_fname2);
676 "Writing done, "+jau::to_decstring(out_bytes_header)+
" header + "+jau::to_decstring(out_bytes_plaintext)+
677 " plaintext != "+jau::to_decstring(output_stats.size())+
" total bytes");
681 jau_WORDY_PRINT(
"Encrypt: Writing done: output: %s", output_stats.toString());
683 my_listener->notifyEnd(decrypt_mode, header);
689 const std::string& dec_sec_key_fname,
const jau::io::secure_string& passphrase,
690 jau::io::ByteStream& source,
692 const std::string_view& plaintext_hash_algo) {
693 const bool decrypt_mode =
true;
695 jau::fraction_timespec ts_creation;
698 if(
nullptr == listener ) {
699 jau_ERR_PRINT2(
"Decrypt failed: Listener is nullptr for source %s", source.toString());
703 const jau::fraction_timespec _t0 = jau::getMonotonicTime();
705 if( !source.good() ) {
706 listener->
notifyError(decrypt_mode, header,
"Source is EOS or has an error "+source.toString());
710 Botan::RandomNumberGenerator& rng = Botan::system_rng();
712 std::unique_ptr<Botan::HashFunction> fingerprint_hash_func;
713 struct sender_data_t {
714 std::shared_ptr<Botan::Public_Key> pub_key;
715 std::vector<uint8_t> fingerprint;
717 std::vector<sender_data_t> sender_data_list;
718 for(
const std::string& pub_key_fname : sign_pub_keys ) {
719 sender_data_t sender_data;
721 if( !sender_data.pub_key ) {
722 listener->
notifyError(decrypt_mode, header,
"Loading pub-key file '"+pub_key_fname+
"' failed");
725 sender_data_list.push_back(sender_data);
727 std::shared_ptr<Botan::Public_Key> sender_pub_key =
nullptr;
729 std::vector<std::vector<uint8_t>> recevr_fingerprints;
730 std::shared_ptr<Botan::Private_Key> dec_sec_key =
load_private_key(dec_sec_key_fname, passphrase);
732 listener->
notifyError(decrypt_mode, header,
"Loading priv-key file '"+dec_sec_key_fname+
"' failed");
736 std::string package_magic_in;
737 std::string target_path;
739 bool has_plaintext_size;
740 uint64_t plaintext_size;
741 std::string plaintext_version;
742 std::string plaintext_version_parent;
745 Botan::OID sym_enc_algo_oid;
747 std::vector<uint8_t> fingerprt_sender;
748 ssize_t recevr_count;
749 ssize_t used_recevr_key_idx = -1;
750 std::vector<uint8_t> encrypted_sym_key;
751 std::vector<uint8_t> encrypted_nonce;
753 std::vector<uint8_t> sender_signature;
755 std::unique_ptr<Botan::HashFunction> hash_func =
nullptr;
756 if( !plaintext_hash_algo.empty() ) {
757 const std::string plaintext_hash_algo_s(plaintext_hash_algo);
758 hash_func = Botan::HashFunction::create(plaintext_hash_algo_s);
759 if(
nullptr == hash_func ) {
760 listener->
notifyError(decrypt_mode, header,
"Payload hash algo '"+plaintext_hash_algo_s+
"' not available");
765 jau::io::secure_vector<uint8_t> input_buffer;
766 jau::io::ByteStream_Recorder input(source, input_buffer);
768 uint64_t in_bytes_header = 0;
772 input.startRecording();
774 std::vector<uint8_t> package_magic_charvec;
776 Botan::BER_Decoder ber0(winput);
778 Botan::BER_Decoder ber = ber0.start_sequence();
779 ber.decode(package_magic_charvec, Botan::ASN1_Type::OctetString);
780 package_magic_in =
to_string(package_magic_charvec);
785 "' in "+source.toString());
788 jau_DBG_PRINT(
"Decrypt: Magic is %s", package_magic_in);
790 std::vector<uint8_t> target_path_charvec;
791 Botan::BigInt bi_plaintext_size;
792 Botan::BigInt bi_ts_creation_sec;
793 Botan::BigInt bi_ts_creation_nsec;
794 std::vector<uint8_t> subject_charvec;
795 std::vector<uint8_t> plaintext_version_charvec;
796 std::vector<uint8_t> plaintext_version_parent_charvec;
798 std::vector<uint8_t> pk_type_cv;
799 std::vector<uint8_t> pk_fingerprt_hash_algo_cv;
800 std::vector<uint8_t> pk_enc_padding_algo_cv;
801 std::vector<uint8_t> pk_enc_hash_algo_cv;
802 std::vector<uint8_t> pk_sign_algo_cv;
803 Botan::BigInt bi_recevr_count;
805 ber.decode( target_path_charvec, Botan::ASN1_Type::OctetString )
806 .decode( bi_plaintext_size, Botan::ASN1_Type::Integer )
807 .decode( bi_ts_creation_sec, Botan::ASN1_Type::Integer )
808 .decode( bi_ts_creation_nsec, Botan::ASN1_Type::Integer )
809 .decode( subject_charvec, Botan::ASN1_Type::OctetString )
810 .decode( plaintext_version_charvec, Botan::ASN1_Type::OctetString )
811 .decode( plaintext_version_parent_charvec, Botan::ASN1_Type::OctetString )
812 .decode( pk_type_cv, Botan::ASN1_Type::OctetString )
813 .decode( pk_fingerprt_hash_algo_cv, Botan::ASN1_Type::OctetString )
814 .decode( pk_enc_padding_algo_cv, Botan::ASN1_Type::OctetString )
815 .decode( pk_enc_hash_algo_cv, Botan::ASN1_Type::OctetString )
816 .decode( pk_sign_algo_cv, Botan::ASN1_Type::OctetString )
817 .decode( sym_enc_algo_oid )
818 .decode( fingerprt_sender, Botan::ASN1_Type::OctetString )
819 .decode( bi_recevr_count, Botan::ASN1_Type::Integer )
823 target_path =
to_string(target_path_charvec);
826 has_plaintext_size = 0 < plaintext_size;
827 ts_creation.tv_sec =
static_cast<int64_t
>(
to_uint64_t(bi_ts_creation_sec) );
828 ts_creation.tv_nsec =
static_cast<int64_t
>(
to_uint64_t(bi_ts_creation_nsec) );
829 plaintext_version =
to_string(plaintext_version_charvec);
830 plaintext_version_parent =
to_string(plaintext_version_parent_charvec);
836 crypto_cfg.
sym_enc_algo = Botan::OIDS::oid2str_or_empty( sym_enc_algo_oid );
844 plaintext_version, plaintext_version_parent,
851 if( fingerprt_sender.empty() ) {
852 listener->
notifyError(decrypt_mode, header,
"Fingerprint sender is empty");
856 if(
nullptr == fingerprint_hash_func ) {
862 for( sender_data_t& sender_data : sender_data_list ) {
863 if( sender_data.pub_key->algo_name() == crypto_cfg.
pk_type ) {
864 sender_data.fingerprint =
_fingerprint_public( *sender_data.pub_key, *fingerprint_hash_func );
865 if( fingerprt_sender == sender_data.fingerprint ) {
866 sender_pub_key = sender_data.pub_key;
871 if(
nullptr == sender_pub_key ) {
873 "No matching sender fingerprint, received '"+jau::toHexString(fingerprt_sender, jau::lb_endian_t::little)+
874 "` in "+source.toString());
878 Botan::PK_Verifier verifier(*sender_pub_key, crypto_cfg.
pk_sign_algo);
879 verifier.update( input.get_recording() );
880 in_bytes_header += input.get_recording().size();
881 input.startRecording();
883 const std::vector<uint8_t> dec_key_fingerprint =
_fingerprint_public( *dec_sec_key, *fingerprint_hash_func );
884 std::vector<uint8_t> receiver_fingerprint_temp;
885 std::vector<uint8_t> encrypted_sym_key_temp;
886 std::vector<uint8_t> encrypted_nonce_temp;
889 for(ssize_t idx=0; idx < recevr_count; idx++) {
890 Botan::BER_Decoder ber(winput);
892 .decode(receiver_fingerprint_temp, Botan::ASN1_Type::OctetString)
893 .decode(encrypted_sym_key_temp, Botan::ASN1_Type::OctetString)
894 .decode(encrypted_nonce_temp, Botan::ASN1_Type::OctetString)
897 verifier.update( input.get_recording() );
898 in_bytes_header += input.get_recording().size();
899 input.startRecording();
901 recevr_fingerprints.push_back(receiver_fingerprint_temp);
903 if( 0 > used_recevr_key_idx ) {
904 if( !receiver_fingerprint_temp.empty() && receiver_fingerprint_temp == dec_key_fingerprint ) {
906 used_recevr_key_idx = idx;
907 encrypted_sym_key = encrypted_sym_key_temp;
908 encrypted_nonce = encrypted_nonce_temp;
916 plaintext_version, plaintext_version_parent,
923 if( 0 > used_recevr_key_idx || 0 == recevr_count ) {
925 "No matching receiver key found "+std::to_string(used_recevr_key_idx)+
"/"+std::to_string(recevr_count)+
926 " in "+source.toString());
930 const uint64_t in_bytes_signature = in_bytes_header;
932 Botan::BER_Decoder ber(winput);
934 .decode(sender_signature, Botan::ASN1_Type::OctetString)
937 in_bytes_header += input.get_recording().size();
938 input.clearRecording();
940 if( !verifier.check_signature(sender_signature) ) {
942 "Signature mismatch on "+std::to_string(in_bytes_signature)+
" header bytes / "+std::to_string(in_bytes_header)+
943 " bytes, received signature '"+jau::toHexString(sender_signature, jau::lb_endian_t::little)+
944 "' in "+source.toString() );
948 jau_DBG_PRINT(
"Decrypt: Signature OK for %" PRIu64
" header bytes / %" PRIu64
": %s from %s",
949 in_bytes_signature, in_bytes_header,
950 jau::toHexString(sender_signature, jau::lb_endian_t::little),
953 jau_DBG_PRINT(
"Decrypt: DER Header*: enc_key %zu/%zu (size %zu): %s",
954 used_recevr_key_idx, recevr_count, encrypted_sym_key.size(),
956 }
catch (Botan::Decoding_Error &e) {
957 listener->
notifyError(decrypt_mode, header,
"Header Decoding: "+std::string(e.what())+
" on "+input.toString()+
" -> "+source.toString());
960 jau_DBG_PRINT(
"Decrypt: target_path '%s', net_file_size %s, version %s (parent %s), subject %s",
961 target_path, jau::to_decstring(plaintext_size),
963 plaintext_version_parent,
965 jau_DBG_PRINT(
"Decrypt: creation time %s UTC", ts_creation.toISO8601String());
968 jau_DBG_PRINT(
"Decrypt: notifyHeader() user abort from %s", source.toString());
976 std::shared_ptr<Botan::AEAD_Mode> aead = Botan::AEAD_Mode::create_or_throw(crypto_cfg.
sym_enc_algo, Botan::DECRYPTION);
978 listener->
notifyError(decrypt_mode, header,
"sym_enc_algo '"+crypto_cfg.
sym_enc_algo+
"' not available from '"+source.toString()+
"'");
981 const size_t expected_keylen = aead->key_spec().maximum_keylength();
986 dec.decrypt_or_random(encrypted_sym_key.data(), encrypted_sym_key.size(), expected_keylen, rng);
993 plaintext_version, plaintext_version_parent,
999 jau_DBG_PRINT(
"Decrypt sym_key[sz %zu], %s", plain_file_key.size(), crypto_cfg.
to_string());
1001 if( !crypto_cfg.
valid() ) {
1002 listener->
notifyError(decrypt_mode, header,
"CryptoConfig incomplete "+crypto_cfg.
to_string()+
" from "+source.toString());
1006 aead->set_key(plain_file_key);
1007 aead->set_associated_data_vec(sender_signature);
1010 const bool sent_content_to_user = listener->
getSendContent( decrypt_mode );
1011 uint64_t out_bytes_plaintext = 0;
1012 bool consume_abort =
false;
1015 const uint64_t next_total = out_bytes_plaintext + data.size();
1016 const size_t minimum_final_size = aead->minimum_final_size();
1018 const size_t update_granularity = aead->update_granularity();
1019 jau_DBG_PRINT(
"Decrypt: update_gran %zu, min_fin_sz %zu, is_final %d, has_plaintext_size %d, %" PRIu64
" + %zu = %" PRIu64
" <= %" PRIu64
" = %d",
1020 update_granularity, minimum_final_size,
1021 is_final, has_plaintext_size,
1022 out_bytes_plaintext, data.size(), next_total, plaintext_size, next_total <= plaintext_size);
1025 if( !is_final && ( !has_plaintext_size || ( 0 < minimum_final_size && next_total <= plaintext_size ) || next_total < plaintext_size ) ) {
1028 }
catch (std::exception &e) {
1029 consume_abort =
true;
1030 listener->
notifyError(decrypt_mode, header,
"Decrypting (.): "+std::string(e.what())+
1031 " @ plaintext ( "+std::to_string(out_bytes_plaintext)+
" + "+std::to_string(data.size())+
" ) / "+std::to_string(plaintext_size)+
1032 " bytes, final "+std::to_string(is_final)+
" on "+source.toString());
1035 if(
nullptr != hash_func ) {
1036 hash_func->update(data);
1038 if( sent_content_to_user ) {
1041 out_bytes_plaintext += data.size();
1043 res = listener->
notifyProgress(decrypt_mode, plaintext_size, out_bytes_plaintext);
1045 jau_DBG_PRINT(
"Decrypt: DecPayload written0 + %zu bytes -> %" PRIu64
" bytes / %zu bytes, user[sent %d, res %d]",
1046 data.size(), out_bytes_plaintext, plaintext_size, sent_content_to_user, res);
1047 consume_abort = !res;
1052 }
catch (std::exception &e) {
1053 consume_abort =
true;
1054 listener->
notifyError(decrypt_mode, header,
"Decrypting (F): "+std::string(e.what())+
1055 " @ plaintext ( "+std::to_string(out_bytes_plaintext)+
" + "+std::to_string(data.size())+
" ) / "+std::to_string(plaintext_size)+
1056 " bytes, final "+std::to_string(is_final)+
" on "+source.toString());
1059 if(
nullptr != hash_func ) {
1060 hash_func->update(data);
1062 if( sent_content_to_user ) {
1065 out_bytes_plaintext += data.size();
1066 if( !has_plaintext_size ) {
1067 plaintext_size = out_bytes_plaintext;
1071 res = listener->
notifyProgress(decrypt_mode, plaintext_size, out_bytes_plaintext);
1073 jau_DBG_PRINT(
"Decrypt: DecPayload writtenF + %zu bytes -> %" PRIu64
" bytes / %zu bytes, user[sent %d, res %d]",
1074 data.size(), out_bytes_plaintext, plaintext_size, sent_content_to_user, res);
1075 consume_abort = !res;
1088 const uint64_t in_bytes_total =
_read_stream(input, io_buffer1, io_buffer2, consume_data);
1091 if(
nullptr != hash_func ) {
1092 std::vector<uint8_t> hash_value( hash_func->output_length() );
1093 hash_func->final(hash_value.data());
1096 if( consume_abort ) {
1097 jau_DBG_PRINT(
"Decrypt: Processing aborted %s", source.toString());
1100 if ( source.fail() ) {
1101 listener->
notifyError(decrypt_mode, header,
"Reading stream failed "+source.toString());
1104 if( 0==in_bytes_total ) {
1105 listener->
notifyError(decrypt_mode, header,
"Processing stream failed "+source.toString());
1108 if( out_bytes_plaintext != plaintext_size ) {
1109 listener->
notifyError(decrypt_mode, header,
"Plaintext size mismatch: "+
1110 jau::to_decstring(out_bytes_plaintext)+
" output bytes != "+
1111 jau::to_decstring(plaintext_size)+
" header plaintext bytes from "+
1115 jau_WORDY_PRINT(
"Decrypt: Reading done from %s", source.toString());
1116 jau_WORDY_PRINT(
"Decrypt: Writing done, %s plaintext bytes from %s ciphertext bytes input, ratio %f in/out",
1117 jau::to_decstring(out_bytes_plaintext),
1118 jau::to_decstring(in_bytes_total),
1119 in_bytes_total > 0 ? (
double)out_bytes_plaintext/(
double)in_bytes_total : 0.0);
1122 const jau::fraction_i64 _td = ( jau::getMonotonicTime() - _t0 ).to_fraction_i64();
1123 if( jau::environment::get().verbose ) {
1124 jau::io::print_stats(
"Decrypt", out_bytes_plaintext, _td);
1129 }
catch (std::exception &e) {
1130 listener->
notifyError(decrypt_mode, header,
"Exception: "+std::string(e.what())+
" on "+source.toString());
1136 const std::string& dec_sec_key_fname,
const jau::io::secure_string& passphrase,
1137 jau::io::ByteStream& source,
1139 const std::string_view& plaintext_hash_algo,
1140 const std::string dest_fname) {
1142 const bool decrypt_mode =
true;
1144 if( dest_fname.empty() ) {
1146 dec_sec_key_fname, passphrase,
1147 source, listener, plaintext_hash_algo);
1149 listener->
notifyEnd(decrypt_mode, header);
1153 std::string dest_fname2;
1154 if( dest_fname ==
"/dev/stdout" || dest_fname ==
"-" ) {
1155 dest_fname2 =
"/dev/stdout";
1157 dest_fname2 = dest_fname;
1160 jau::fraction_timespec ts_creation;
1163 if(
nullptr == listener ) {
1164 jau_ERR_PRINT2(
"Decrypt failed: Listener is nullptr for source %s", source.toString());
1170 bool sent_content_to_user;
1171 jau::io::ByteStream_File* outfile_;
1172 uint64_t& out_bytes_plaintext_;
1178 out_bytes_plaintext_(bytes_plaintext)
1181 void set_outfile(jau::io::ByteStream_File* of)
noexcept { outfile_ = of; }
1188 if(
nullptr != outfile_ && content_type::message == ctype ) {
1189 if( data.size() != outfile_->write(data.data(), data.size()) ) {
1192 if( outfile_->fail() ) {
1195 out_bytes_plaintext_ += data.size();
1197 if( sent_content_to_user ) {
1198 return parent->contentProcessed(decrypt_mode, ctype, data, is_final);
1204 uint64_t out_bytes_plaintext=0;
1205 std::shared_ptr<MyListener> my_listener = std::make_shared<MyListener>(listener, out_bytes_plaintext);
1207 const jau::io::fs::file_stats output_stats(dest_fname2);
1208 if( output_stats.exists() && !output_stats.has_fd() ) {
1209 if( output_stats.is_file() ) {
1210 if( !jau::io::fs::remove(dest_fname2) ) {
1211 my_listener->notifyError(decrypt_mode, header,
"Failed deletion of existing output file "+output_stats.toString());
1214 }
else if( output_stats.is_dir() || output_stats.is_block() ) {
1215 my_listener->notifyError(decrypt_mode, header,
"Not overwriting existing "+output_stats.toString());
1220 jau::io::ByteStream_File outfile(dest_fname2);
1221 if ( !outfile.good() ) {
1222 my_listener->notifyError(decrypt_mode, header,
"Failed to open output file "+outfile.toString());
1225 my_listener->set_outfile(&outfile);
1228 dec_sec_key_fname, passphrase,
1229 source, my_listener, plaintext_hash_algo);
1232 const jau::io::fs::file_stats output_stats(dest_fname2);
1233 if ( outfile.fail() ) {
1234 if( output_stats.is_file() && !output_stats.has_fd() ) {
1235 jau::io::fs::remove(dest_fname2);
1238 my_listener->notifyError(decrypt_mode, header,
"Failed to write output file "+dest_fname2);
1244 if( output_stats.is_file() && !output_stats.has_fd() ) {
1245 jau::io::fs::remove(dest_fname2);
1251 const jau::io::fs::file_stats output_stats(dest_fname2);
1252 if( output_stats.is_file() && header.
plaintext_size() != output_stats.size() ) {
1253 if( output_stats.is_file() && !output_stats.has_fd() ) {
1254 jau::io::fs::remove(dest_fname2);
1257 listener->
notifyError(decrypt_mode, header,
"Output plaintext file size mismatch: "+
1258 jau::to_decstring(output_stats.size())+
" output file bytes != "+
1259 jau::to_decstring(header.
plaintext_size())+
" header plaintext bytes from "+
1264 jau_WORDY_PRINT(
"Decrypt: Writing done: output: %s", output_stats.toString());
1265 my_listener->notifyEnd(decrypt_mode, header);