diff options
author | Sven Gothel <[email protected]> | 2022-07-25 19:29:44 +0200 |
---|---|---|
committer | Sven Gothel <[email protected]> | 2022-07-25 19:29:44 +0200 |
commit | 320881dabd44168cc53f50f348a0e1bd8add8deb (patch) | |
tree | 1547d3e49abf7dd691a9587c8649b729448bde6e | |
parent | 986ea69db2ba5d3ded085468fcaee9d60472927f (diff) |
cipherpack pipe support: {content -> plaintext}_size may be undetermined if streaming, set at end of pack/unpack only
Hence we allow a zero sized plaintext_size in the header, which is wired to the receiver.
-rw-r--r-- | include/cipherpack/cipherpack.hpp | 26 | ||||
-rw-r--r-- | java/jni/cipherpack/CipherpackListener.cxx | 4 | ||||
-rw-r--r-- | java/org/cipherpack/Cipherpack.java | 18 | ||||
-rw-r--r-- | java/org/cipherpack/CipherpackListener.java | 4 | ||||
-rw-r--r-- | java/org/cipherpack/PackHeader.java | 25 | ||||
-rw-r--r-- | src/cipherpack/crypto0.cpp | 10 | ||||
-rw-r--r-- | src/cipherpack/crypto1.cpp | 229 |
7 files changed, 169 insertions, 147 deletions
diff --git a/include/cipherpack/cipherpack.hpp b/include/cipherpack/cipherpack.hpp index f5fa839..4207efa 100644 --- a/include/cipherpack/cipherpack.hpp +++ b/include/cipherpack/cipherpack.hpp @@ -98,13 +98,13 @@ namespace cipherpack { * ``` * DER Header 1 { * ASN1_Type::OctetString stream_magic // simple stream identifier to be matched - * ASN1_Type::OctetString target_path // designated target path for this plaintext message, user semantic - * ASN1_Type::Integer plaintext_size // content size of plaintext message + * ASN1_Type::OctetString target_path // optional target path for the plaintext message, user application specific. + * ASN1_Type::Integer plaintext_size // size in bytes of plaintext message, zero if not determined at start of streaming * ASN1_Type::Integer creation_timestamp_sec // message creation timestamp, second component * ASN1_Type::Integer creation_timestamp_nsec // message creation timestamp, nanoseconds component - * ASN1_Type::OctetString subject // designated subject of message - * ASN1_Type::OctetString plaintext_version // version of this plaintext message, user semantic - * ASN1_Type::OctetString plaintext_version_parent // version of this plaintext message's preceding message, user semantic + * ASN1_Type::OctetString subject // optional subject of message, user application specific. + * ASN1_Type::OctetString plaintext_version // version of this plaintext message, user application specific. + * ASN1_Type::OctetString plaintext_version_parent // version of this plaintext message's preceding message, user application specific. * ASN1_Type::OctetString pk_type // public-key type. Default "RSA". * ASN1_Type::OctetString pk_fingerprt_hash_algo // public-key fingerprint hash. Default "SHA-256". * ASN1_Type::OctetString pk_enc_padding_algo // public-key encryption padding. Default "OAEP". @@ -317,9 +317,11 @@ namespace cipherpack { /** Returns the designated target path for this plaintext message, see @ref cipherpack_stream "Cipherpack Data Stream". */ const std::string& getTargetPath() const noexcept { return target_path; } - /** Returns the plaintext message size in bytes, see @ref cipherpack_stream "Cipherpack Data Stream". */ + /** Returns the plaintext message size in bytes, zero if not determined yet. See @ref cipherpack_stream "Cipherpack Data Stream". */ uint64_t getPlaintextSize() const noexcept { return plaintext_size; } + void setPlaintextSize(const uint64_t v) noexcept { plaintext_size=v; } + /** Returns the creation time since Unix epoch, see @ref cipherpack_stream "Cipherpack Data Stream". */ constexpr const jau::fraction_timespec& getCreationTime() const noexcept { return ts_creation; } @@ -440,13 +442,13 @@ namespace cipherpack { * In case contentProcessed() gets called, notifyProgress() is called thereafter. * * @param decrypt_mode true if sender is decrypting, otherwise sender is encrypting - * @param content_size the unencrypted content size + * @param plaintext_size the plaintext message size, zero if not determined yet * @param bytes_processed the number of unencrypted bytes processed * @see contentProcessed() */ - virtual void notifyProgress(const bool decrypt_mode, const uint64_t content_size, const uint64_t bytes_processed) noexcept { + virtual void notifyProgress(const bool decrypt_mode, const uint64_t plaintext_size, const uint64_t bytes_processed) noexcept { (void)decrypt_mode; - (void)content_size; + (void)plaintext_size; (void)bytes_processed; } @@ -549,10 +551,10 @@ namespace cipherpack { * @param sign_sec_key_fname Private key of the sender, used to sign the DER-Header-1 incl encrypted symmetric-key for authenticity. * @param passphrase Passphrase for `sign_sec_key_fname`, may be an empty secure_string for no passphrase. * @param source The source jau::io::ByteInStream of the plaintext message. - * @param target_path Designated target path for the message - * @param subject Designated subject of message from sender + * @param target_path Optional target path for the message, user application specific. + * @param subject Optional subject of message, user application specific. * @param plaintext_version Version of this plaintext message, user semantic - * @param plaintext_version_parent Version of this plaintext message's preceding message, user semantic. + * @param plaintext_version_parent Version of this plaintext message's preceding message, user application specific * @param listener CipherpackListener listener used for notifications and optionally * to send the ciphertext destination bytes via CipherpackListener::contentProcessed() * @param plaintext_hash_algo Optional hash algorithm for the plaintext message, produced for convenience and not wired. See default_hash_algo(). diff --git a/java/jni/cipherpack/CipherpackListener.cxx b/java/jni/cipherpack/CipherpackListener.cxx index a2808ee..eb4b8d0 100644 --- a/java/jni/cipherpack/CipherpackListener.cxx +++ b/java/jni/cipherpack/CipherpackListener.cxx @@ -115,13 +115,13 @@ class JNICipherpackListener : public cipherpack::CipherpackListener { env->DeleteLocalRef(jph); } - void notifyProgress(const bool decrypt_mode, const uint64_t content_size, const uint64_t bytes_processed) noexcept override { + void notifyProgress(const bool decrypt_mode, const uint64_t plaintext_size, const uint64_t bytes_processed) noexcept override { JNIEnv *env = *jau::jni::jni_env; jau::jni::JavaAnonRef asl_java = getJavaObject(); // hold until done! jau::jni::JavaGlobalObj::check(asl_java, E_FILE_LINE); env->CallVoidMethod(jau::jni::JavaGlobalObj::GetObject(asl_java), mNotifyProgress, decrypt_mode ? JNI_TRUE : JNI_FALSE, - static_cast<jlong>(content_size), + static_cast<jlong>(plaintext_size), static_cast<jlong>(bytes_processed)); jau::jni::java_exception_check_and_throw(env, E_FILE_LINE); } diff --git a/java/org/cipherpack/Cipherpack.java b/java/org/cipherpack/Cipherpack.java index c01dd64..7d1bbf9 100644 --- a/java/org/cipherpack/Cipherpack.java +++ b/java/org/cipherpack/Cipherpack.java @@ -88,13 +88,13 @@ import org.jau.util.BasicTypes; * ``` * DER Header 1 { * ASN1_Type::OctetString stream_magic // simple stream identifier to be matched - * ASN1_Type::OctetString target_path // designated target path for this plaintext message, user semantic - * ASN1_Type::Integer plaintext_size // content size of plaintext message + * ASN1_Type::OctetString target_path // optional target path for the plaintext message, user application specific. + * ASN1_Type::Integer plaintext_size // size in bytes of plaintext message, zero if not determined at start of streaming * ASN1_Type::Integer creation_timestamp_sec // message creation timestamp, second component * ASN1_Type::Integer creation_timestamp_nsec // message creation timestamp, nanoseconds component - * ASN1_Type::OctetString subject // designated subject of message - * ASN1_Type::OctetString plaintext_version // version of this plaintext message, user semantic - * ASN1_Type::OctetString plaintext_version_parent // version of this plaintext message's preceding message, user semantic + * ASN1_Type::OctetString subject // optional subject of message, user application specific. + * ASN1_Type::OctetString plaintext_version // version of this plaintext message, user application specific. + * ASN1_Type::OctetString plaintext_version_parent // version of this plaintext message's preceding message, user application specific. * ASN1_Type::OctetString pk_type // public-key type. Default "RSA". * ASN1_Type::OctetString pk_fingerprt_hash_algo // public-key fingerprint hash. Default "SHA-256". * ASN1_Type::OctetString pk_enc_padding_algo // public-key encryption padding. Default "OAEP". @@ -154,10 +154,10 @@ public final class Cipherpack { * @param sign_sec_key_fname Private key of the sender, used to sign the DER-Header-1 incl encrypted symmetric-key for authenticity. * @param passphrase Passphrase for `sign_sec_key_fname`, may be null or empty for no passphrase. * @param source The source ByteInStream of the plaintext message. - * @param target_path Designated target path for the message - * @param subject Designated subject of message from sender - * @param plaintext_version Version of this plaintext message, user semantic. - * @param plaintext_version_parent Version of this plaintext message's preceding message, user semantic. + * @param target_path Optional target path for the message, user application specific. + * @param subject Optional subject of message from sender, user application specific. + * @param plaintext_version Version of this plaintext message, user application specific. + * @param plaintext_version_parent Version of this plaintext message's preceding message, user application specific. * @param listener CipherpackListener listener used for notifications and optionally * to send the ciphertext destination bytes via CipherpackListener::contentProcessed() * @param plaintext_hash_algo Optional hash algorithm for the plaintext message, produced for convenience and not wired. See {@link Cipherpack#default_hash_algo()}. diff --git a/java/org/cipherpack/CipherpackListener.java b/java/org/cipherpack/CipherpackListener.java index e3d1ed7..a92ec72 100644 --- a/java/org/cipherpack/CipherpackListener.java +++ b/java/org/cipherpack/CipherpackListener.java @@ -94,11 +94,11 @@ public class CipherpackListener extends CPNativeDownlink { * In case contentProcessed() gets called, notifyProgress() is called thereafter. * * @param decrypt_mode true if sender is decrypting, otherwise sender is encrypting - * @param content_size the unencrypted content size + * @param plaintext_size the plaintext message size, zero if not determined yet * @param bytes_processed the number of unencrypted bytes processed * @see contentProcessed() */ - public void notifyProgress(final boolean decrypt_mode, final long content_size, final long bytes_processed) { } + public void notifyProgress(final boolean decrypt_mode, final long plaintext_size, final long bytes_processed) { } /** * User notification of process completion. diff --git a/java/org/cipherpack/PackHeader.java b/java/org/cipherpack/PackHeader.java index e93dea8..cce8032 100644 --- a/java/org/cipherpack/PackHeader.java +++ b/java/org/cipherpack/PackHeader.java @@ -23,21 +23,12 @@ */ package org.cipherpack; -import java.io.BufferedWriter; -import java.io.File; -import java.io.FileOutputStream; -import java.io.FileWriter; -import java.io.OutputStream; -import java.nio.charset.StandardCharsets; import java.time.Instant; import java.time.ZoneOffset; import java.time.ZonedDateTime; import java.util.ArrayList; import java.util.List; -import org.jau.io.PrintUtil; -import org.jau.util.BasicTypes; - /** * Cipherpack header less encrypted keys or signatures as described in @ref cipherpack_stream "Cipherpack Data Stream" * @@ -48,7 +39,7 @@ public class PackHeader { /** Designated target path for this plaintext message, see @ref cipherpack_stream "Cipherpack Data Stream". */ public final String target_path; - /** Plaintext message size in bytes, see @ref cipherpack_stream "Cipherpack Data Stream". */ + /** Plaintext message size in bytes, zero if not determined yet. See @ref cipherpack_stream "Cipherpack Data Stream". */ public final long plaintext_size; /** Creation time since Unix epoch, second component, see @ref cipherpack_stream "Cipherpack Data Stream". */ @@ -112,7 +103,7 @@ public class PackHeader { } PackHeader(final String target_path_, - final long content_size_, + final long plaintext_size_, final long ts_creation_sec_, final long ts_creation_nsec_, final String subject_, @@ -125,7 +116,7 @@ public class PackHeader { final byte[] plaintext_hash_, final boolean valid_) { this.target_path = target_path_; - this.plaintext_size = content_size_; + this.plaintext_size = plaintext_size_; this.ts_creation_sec = ts_creation_sec_; this.ts_creation_nsec = ts_creation_nsec_; this.subject = subject_; @@ -147,7 +138,7 @@ public class PackHeader { * @return string representation */ public String toString(final boolean show_crypto_algos, final boolean force_all_fingerprints) { - final String crypto_str = show_crypto_algos ? crypto_cfg.toString() : ""; + final String crypto_str = show_crypto_algos ? ", "+crypto_cfg.toString() : ""; final StringBuilder recevr_fingerprint = new StringBuilder(); { @@ -169,11 +160,11 @@ public class PackHeader { } final ZonedDateTime utc_creation = Instant.ofEpochSecond(ts_creation_sec, ts_creation_nsec).atZone(ZoneOffset.UTC); final String res = "Header[valid "+valid+ - ", file[target_path "+target_path+", content_size "+String.format("%,d", plaintext_size)+ + ", file[target_path '"+target_path+"', plaintext_size "+String.format("%,d", plaintext_size)+ "], creation "+utc_creation.toString()+" , subject '"+subject+"', "+ - " version["+plaintext_version+ - ", parent "+plaintext_version_parent+crypto_str+ - "], fingerprints[sender '"+sender_fingerprint+ + " version['"+plaintext_version+ + "', parent '"+plaintext_version_parent+"']"+crypto_str+ + ", fingerprints[sender '"+sender_fingerprint+ "', recevr["+recevr_fingerprint+ "]], phash['"+plaintext_hash_algo+"', sz "+plaintext_hash.length+"]]"; return res; diff --git a/src/cipherpack/crypto0.cpp b/src/cipherpack/crypto0.cpp index 4309a83..321eca3 100644 --- a/src/cipherpack/crypto0.cpp +++ b/src/cipherpack/crypto0.cpp @@ -142,7 +142,7 @@ std::string CryptoConfig::to_string() const noexcept { } std::string PackHeader::toString(const bool show_crypto_algos, const bool force_all_fingerprints) const noexcept { - const std::string crypto_str = show_crypto_algos ? crypto_cfg.to_string() : ""; + const std::string crypto_str = show_crypto_algos ? ", "+crypto_cfg.to_string() : ""; std::string recevr_fingerprint_str; { @@ -165,11 +165,11 @@ std::string PackHeader::toString(const bool show_crypto_algos, const bool force_ std::string res = "Header["; res += "valid "+std::to_string( isValid() )+ - ", file[target_path "+target_path+", content_size "+jau::to_decstring(plaintext_size).c_str()+ + ", file[target_path '"+target_path+"', plaintext_size "+jau::to_decstring(plaintext_size).c_str()+ "], creation "+ts_creation.to_iso8601_string()+" UTC, subject '"+subject+"', "+ - " version["+plaintext_version+ - ", parent "+plaintext_version_parent+crypto_str+ - "], fingerprints[sender '"+sender_fingerprint+ + " version['"+plaintext_version+ + "', parent '"+plaintext_version_parent+"']"+crypto_str+ + ", fingerprints[sender '"+sender_fingerprint+ "', recevr["+recevr_fingerprint_str+ "]], phash['"+plaintext_hash_algo+"', sz "+std::to_string(plaintext_hash.size())+"]]"; return res; diff --git a/src/cipherpack/crypto1.cpp b/src/cipherpack/crypto1.cpp index 1c57463..9fe09dd 100644 --- a/src/cipherpack/crypto1.cpp +++ b/src/cipherpack/crypto1.cpp @@ -76,8 +76,8 @@ class WrappingCipherpackListener : public CipherpackListener{ parent->notifyHeader(decrypt_mode, header, verified); } - void notifyProgress(const bool decrypt_mode, const uint64_t content_size, const uint64_t bytes_processed) noexcept override { - parent->notifyProgress(decrypt_mode, content_size, bytes_processed); + void notifyProgress(const bool decrypt_mode, const uint64_t plaintext_size, const uint64_t bytes_processed) noexcept override { + parent->notifyProgress(decrypt_mode, plaintext_size, bytes_processed); } void notifyEnd(const bool decrypt_mode, const PackHeader& header, const bool success) noexcept override { @@ -130,11 +130,8 @@ static PackHeader encryptThenSign_Impl(const CryptoConfig& crypto_cfg, ERR_PRINT2("Encrypt failed: Source is EOS or has an error %s", source.to_string().c_str()); return header; } - if( !source.has_content_size() ) { - ERR_PRINT2("Encrypt failed: Source doesn't provide content_size %s", source.to_string().c_str()); - return header; - } - const uint64_t content_size = source.content_size(); + const bool has_plaintext_size = source.has_content_size(); + uint64_t plaintext_size = has_plaintext_size ? source.content_size() : 0; if( !crypto_cfg.valid() ) { ERR_PRINT2("Encrypt failed: CryptoConfig incomplete %s", crypto_cfg.to_string().c_str()); @@ -143,10 +140,6 @@ static PackHeader encryptThenSign_Impl(const CryptoConfig& crypto_cfg, const jau::fraction_timespec _t0 = jau::getMonotonicTime(); - if( target_path.empty() ) { - ERR_PRINT2("Encrypt failed: Target path is empty for %s", source.to_string().c_str()); - return header; - } uint64_t out_bytes_header = 0; try { @@ -214,7 +207,7 @@ static PackHeader encryptThenSign_Impl(const CryptoConfig& crypto_cfg, der.start_sequence() .encode( to_OctetString( Constants::package_magic ), Botan::ASN1_Type::OctetString ) .encode( to_OctetString( target_path ), Botan::ASN1_Type::OctetString ) - .encode( to_BigInt( static_cast<uint64_t>( content_size ) ), Botan::ASN1_Type::Integer ) + .encode( to_BigInt( static_cast<uint64_t>( plaintext_size ) ), Botan::ASN1_Type::Integer ) .encode( to_BigInt( static_cast<uint64_t>( ts_creation.tv_sec ) ), Botan::ASN1_Type::Integer ) .encode( to_BigInt( static_cast<uint64_t>( ts_creation.tv_nsec ) ), Botan::ASN1_Type::Integer ) .encode( to_OctetString( subject ), Botan::ASN1_Type::OctetString ) @@ -280,7 +273,7 @@ static PackHeader encryptThenSign_Impl(const CryptoConfig& crypto_cfg, } header = PackHeader(target_path, - content_size, + plaintext_size, ts_creation, subject, plaintext_version, plaintext_version_parent, @@ -317,8 +310,8 @@ static PackHeader encryptThenSign_Impl(const CryptoConfig& crypto_cfg, } out_bytes_plaintext += data.size(); DBG_PRINT("Encrypt: EncPayload written0 + %zu bytes -> %" PRIu64 " bytes / %zu bytes, user[sent %d, res %d]", - data.size(), out_bytes_plaintext, content_size, sent_content_to_user, res); - listener->notifyProgress(decrypt_mode, content_size, source.bytes_read()); + data.size(), out_bytes_plaintext, plaintext_size, sent_content_to_user, res); + listener->notifyProgress(decrypt_mode, plaintext_size, source.bytes_read()); } else { if( nullptr != hash_func ) { hash_func->update(data); @@ -328,9 +321,13 @@ static PackHeader encryptThenSign_Impl(const CryptoConfig& crypto_cfg, res = listener->contentProcessed(decrypt_mode, CipherpackListener::content_type::message, data, true /* is_final */); } out_bytes_plaintext += data.size(); + if( !has_plaintext_size ) { + plaintext_size = out_bytes_plaintext; + header.setPlaintextSize(plaintext_size); + } DBG_PRINT("Encrypt: EncPayload writtenF + %zu bytes -> %" PRIu64 " bytes / %zu bytes, user[sent %d, res %d]", - data.size(), out_bytes_plaintext, content_size, sent_content_to_user, res); - listener->notifyProgress(decrypt_mode, content_size, source.bytes_read()); + data.size(), out_bytes_plaintext, plaintext_size, sent_content_to_user, res); + listener->notifyProgress(decrypt_mode, plaintext_size, source.bytes_read()); } return res; }; @@ -385,11 +382,11 @@ PackHeader cipherpack::encryptThenSign(const CryptoConfig& crypto_cfg, const std::string& plaintext_version_parent, CipherpackListenerRef listener, const std::string_view& plaintext_hash_algo, - const std::string destination_fname) { + const std::string dest_fname) { environment::get(); const bool decrypt_mode = false; - if( destination_fname.empty() ) { + if( dest_fname.empty() ) { PackHeader ph = encryptThenSign_Impl(crypto_cfg, enc_pub_keys, sign_sec_key_fname, passphrase, @@ -401,6 +398,12 @@ PackHeader cipherpack::encryptThenSign(const CryptoConfig& crypto_cfg, listener->notifyEnd(decrypt_mode, ph, ph.isValid()); return ph; } + std::string dest_fname2; + if( dest_fname == "/dev/stdout" || dest_fname == "-" ) { + dest_fname2 = "/dev/stdout"; + } else { + dest_fname2 = dest_fname; + } const jau::fraction_timespec ts_creation = jau::getWallClockTime(); @@ -464,24 +467,24 @@ PackHeader cipherpack::encryptThenSign(const CryptoConfig& crypto_cfg, uint64_t out_bytes_header=0, out_bytes_plaintext=0; std::shared_ptr<MyListener> my_listener = std::make_shared<MyListener>(listener, out_bytes_header, out_bytes_plaintext); { - const jau::fs::file_stats output_stats(destination_fname); + const jau::fs::file_stats output_stats(dest_fname2); if( output_stats.exists() ) { if( output_stats.is_file() ) { - if( !jau::fs::remove(destination_fname) ) { + if( !jau::fs::remove(dest_fname2) ) { ERR_PRINT2("Encrypt failed: Failed deletion of existing output file %s", output_stats.to_string().c_str()); my_listener->notifyEnd(decrypt_mode, header, false); return header; } - } else { + } else if( !output_stats.is_fd() ) { ERR_PRINT2("Encrypt failed: Not overwriting existing %s", output_stats.to_string().c_str()); my_listener->notifyEnd(decrypt_mode, header, false); return header; } } } - std::ofstream outfile(destination_fname, std::ios::out | std::ios::binary); + std::ofstream outfile(dest_fname2, std::ios::out | std::ios::binary); if ( !outfile.good() || !outfile.is_open() ) { - ERR_PRINT2("Encrypt failed: Output file not open %s", destination_fname.c_str()); + ERR_PRINT2("Encrypt failed: Output file not open %s", dest_fname2.c_str()); my_listener->notifyEnd(decrypt_mode, header, false); return header; } @@ -495,34 +498,49 @@ PackHeader cipherpack::encryptThenSign(const CryptoConfig& crypto_cfg, plaintext_version, plaintext_version_parent, my_listener, plaintext_hash_algo); - if ( outfile.fail() ) { - ERR_PRINT2("Encrypt failed: Output file write failed %s", destination_fname.c_str()); - jau::fs::remove(destination_fname); - ph.setValid(false); - my_listener->notifyEnd(decrypt_mode, ph, false); - return header; - } - outfile.close(); - if( !ph.isValid() ) { - jau::fs::remove(destination_fname); - my_listener->notifyEnd(decrypt_mode, ph, false); - return header; - } + { + const jau::fs::file_stats output_stats(dest_fname2); + if ( outfile.fail() ) { + ERR_PRINT2("Encrypt failed: Output file write failed %s", dest_fname2.c_str()); + if( output_stats.is_file() ) { + jau::fs::remove(dest_fname2); + } + ph.setValid(false); + my_listener->notifyEnd(decrypt_mode, ph, false); + return header; + } + outfile.close(); - const jau::fs::file_stats output_stats(destination_fname); - if( out_bytes_header + out_bytes_plaintext != output_stats.size() ) { - ERR_PRINT2("Encrypt: Writing done, %s header + %s plaintext != %s total bytes", - jau::to_decstring(out_bytes_header).c_str(), - jau::to_decstring(out_bytes_plaintext).c_str(), - jau::to_decstring(output_stats.size()).c_str()); - jau::fs::remove(destination_fname); - ph.setValid(false); - my_listener->notifyEnd(decrypt_mode, ph, false); - return header; + if( !ph.isValid() ) { + if( output_stats.is_file() ) { + jau::fs::remove(dest_fname2); + } + my_listener->notifyEnd(decrypt_mode, ph, false); + return header; + } + } + // outfile closed + const jau::fs::file_stats output_stats(dest_fname2); + // TODO: Perhaps figure out 'ciphertext_size_same' for all streamcipher. true for `ChaCha20Poly1305` + constexpr const bool ciphertext_size_same = false; + if constexpr ( ciphertext_size_same ) { + // Test is only valid, IFF out_bytes_plaintext == out_bytes_ciphertext + if( output_stats.is_file() && out_bytes_header + out_bytes_plaintext != output_stats.size() ) { + ERR_PRINT2("Encrypt: Writing done, %s header + %s plaintext != %s total bytes", + jau::to_decstring(out_bytes_header).c_str(), + jau::to_decstring(out_bytes_plaintext).c_str(), + jau::to_decstring(output_stats.size()).c_str()); + if( output_stats.is_file() ) { + jau::fs::remove(dest_fname2); + } + ph.setValid(false); + my_listener->notifyEnd(decrypt_mode, ph, false); + return header; + } } - WORDY_PRINT("Encrypt: Writing done: output: %s", output_stats.to_string().c_str()); + my_listener->notifyEnd(decrypt_mode, ph, true); return ph; } @@ -576,7 +594,8 @@ static PackHeader checkSignThenDecrypt_Impl(const std::vector<std::string>& sign std::string package_magic_in; std::string target_path; std::string subject; - uint64_t content_size; + bool has_plaintext_size; + uint64_t plaintext_size; std::string plaintext_version; std::string plaintext_version_parent; @@ -624,7 +643,7 @@ static PackHeader checkSignThenDecrypt_Impl(const std::vector<std::string>& sign DBG_PRINT("Decrypt: Magic is %s", package_magic_in.c_str()); std::vector<uint8_t> target_path_charvec; - Botan::BigInt bi_content_size; + Botan::BigInt bi_plaintext_size; Botan::BigInt bi_ts_creation_sec; Botan::BigInt bi_ts_creation_nsec; std::vector<uint8_t> subject_charvec; @@ -641,7 +660,7 @@ static PackHeader checkSignThenDecrypt_Impl(const std::vector<std::string>& sign Botan::BigInt bi_recevr_count; ber.decode( target_path_charvec, Botan::ASN1_Type::OctetString ) - .decode( bi_content_size, Botan::ASN1_Type::Integer ) + .decode( bi_plaintext_size, Botan::ASN1_Type::Integer ) .decode( bi_ts_creation_sec, Botan::ASN1_Type::Integer ) .decode( bi_ts_creation_nsec, Botan::ASN1_Type::Integer ) .decode( subject_charvec, Botan::ASN1_Type::OctetString ) @@ -660,7 +679,8 @@ static PackHeader checkSignThenDecrypt_Impl(const std::vector<std::string>& sign target_path = to_string(target_path_charvec); subject = to_string(subject_charvec); - content_size = to_uint64_t(bi_content_size); + plaintext_size = to_uint64_t(bi_plaintext_size); + has_plaintext_size = 0 < plaintext_size; ts_creation.tv_sec = static_cast<int64_t>( to_uint64_t(bi_ts_creation_sec) ); ts_creation.tv_nsec = static_cast<int64_t>( to_uint64_t(bi_ts_creation_nsec) ); plaintext_version = to_string(plaintext_version_charvec); @@ -676,7 +696,7 @@ static PackHeader checkSignThenDecrypt_Impl(const std::vector<std::string>& sign } header = PackHeader(target_path, - content_size, + plaintext_size, ts_creation, subject, plaintext_version, plaintext_version_parent, @@ -686,17 +706,6 @@ static PackHeader checkSignThenDecrypt_Impl(const std::vector<std::string>& sign used_recevr_key_idx, false /* valid */); - if( target_path.empty() ) { - ERR_PRINT2("Decrypt failed: Unknown target_path in %s", source.to_string().c_str()); - listener->notifyHeader(decrypt_mode, header, false); - return header; - } - if( 0 == content_size ) { - ERR_PRINT2("Decrypt failed: Zero file-size in %s", source.to_string().c_str()); - listener->notifyHeader(decrypt_mode, header, false); - return header; - } - for( sender_data_t& sign_key_data : sender_data_list ) { if( sign_key_data.pub_key->algo_name() == crypto_cfg.pk_type ) { sign_key_data.fingerprint = sign_key_data.pub_key->fingerprint_public( crypto_cfg.pk_fingerprt_hash_algo ); @@ -757,7 +766,7 @@ static PackHeader checkSignThenDecrypt_Impl(const std::vector<std::string>& sign if( 0 > used_recevr_key_idx || 0 == recevr_count ) { jau::INFO_PRINT("Decrypt failed: No matching receiver key found %zd/%zu in %s", used_recevr_key_idx, recevr_count, source.to_string().c_str()); header = PackHeader(target_path, - content_size, + plaintext_size, ts_creation, subject, plaintext_version, plaintext_version_parent, @@ -771,7 +780,7 @@ static PackHeader checkSignThenDecrypt_Impl(const std::vector<std::string>& sign } header = PackHeader(target_path, - content_size, + plaintext_size, ts_creation, subject, plaintext_version, plaintext_version_parent, @@ -812,8 +821,8 @@ static PackHeader checkSignThenDecrypt_Impl(const std::vector<std::string>& sign ERR_PRINT("Decrypt failed: Caught exception: %s on %s", e.what(), source.to_string().c_str()); return header; } - DBG_PRINT("Decrypt: target_path '%s', net_file_size %s, version %s (parent %s), intention %s", - target_path.c_str(), jau::to_decstring(content_size).c_str(), + DBG_PRINT("Decrypt: target_path '%s', net_file_size %s, version %s (parent %s), subject %s", + target_path.c_str(), jau::to_decstring(plaintext_size).c_str(), plaintext_version.c_str(), plaintext_version_parent.c_str(), subject.c_str()); @@ -839,7 +848,7 @@ static PackHeader checkSignThenDecrypt_Impl(const std::vector<std::string>& sign const jau::io::secure_vector<uint8_t> nonce = dec.decrypt(encrypted_nonce); crypto_cfg.sym_enc_nonce_bytes = nonce.size(); header = PackHeader(target_path, - content_size, + plaintext_size, ts_creation, subject, plaintext_version, plaintext_version_parent, @@ -864,7 +873,7 @@ static PackHeader checkSignThenDecrypt_Impl(const std::vector<std::string>& sign uint64_t out_bytes_plaintext = 0; jau::io::StreamConsumerFunc consume_data = [&](jau::io::secure_vector<uint8_t>& data, bool is_final) -> bool { bool res = true; - if( !is_final && out_bytes_plaintext + data.size() < content_size ) { + if( !is_final && ( !has_plaintext_size || out_bytes_plaintext + data.size() < plaintext_size ) ) { aead->update(data); if( nullptr != hash_func ) { hash_func->update(data); @@ -874,8 +883,8 @@ static PackHeader checkSignThenDecrypt_Impl(const std::vector<std::string>& sign } out_bytes_plaintext += data.size(); DBG_PRINT("Decrypt: DecPayload written0 + %zu bytes -> %" PRIu64 " bytes / %zu bytes, user[sent %d, res %d]", - data.size(), out_bytes_plaintext, content_size, sent_content_to_user, res); - listener->notifyProgress(decrypt_mode, content_size, out_bytes_plaintext); + data.size(), out_bytes_plaintext, plaintext_size, sent_content_to_user, res); + listener->notifyProgress(decrypt_mode, plaintext_size, out_bytes_plaintext); return res; // continue if user so desires } else { aead->finish(data); @@ -886,9 +895,13 @@ static PackHeader checkSignThenDecrypt_Impl(const std::vector<std::string>& sign res = listener->contentProcessed(decrypt_mode, CipherpackListener::content_type::message, data, true /* is_final */); } out_bytes_plaintext += data.size(); + if( !has_plaintext_size ) { + plaintext_size = out_bytes_plaintext; + header.setPlaintextSize(plaintext_size); + } DBG_PRINT("Decrypt: DecPayload writtenF + %zu bytes -> %" PRIu64 " bytes / %zu bytes, user[sent %d, res %d]", - data.size(), out_bytes_plaintext, content_size, sent_content_to_user, res); - listener->notifyProgress(decrypt_mode, content_size, out_bytes_plaintext); + data.size(), out_bytes_plaintext, plaintext_size, sent_content_to_user, res); + listener->notifyProgress(decrypt_mode, plaintext_size, out_bytes_plaintext); return false; // EOS } }; @@ -907,10 +920,10 @@ static PackHeader checkSignThenDecrypt_Impl(const std::vector<std::string>& sign ERR_PRINT2("Decrypt failed: Input file read failed %s", source.to_string().c_str()); return header; } - if( out_bytes_plaintext != content_size ) { + if( out_bytes_plaintext != plaintext_size ) { ERR_PRINT2("Decrypt: Writing done, %s output plaintext != %s header files size from %s", jau::to_decstring(out_bytes_plaintext).c_str(), - jau::to_decstring(content_size).c_str(), source.to_string().c_str()); + jau::to_decstring(plaintext_size).c_str(), source.to_string().c_str()); return header; } else { WORDY_PRINT("Decrypt: Reading done from %s", source.to_string().c_str()); @@ -938,17 +951,23 @@ PackHeader cipherpack::checkSignThenDecrypt(const std::vector<std::string>& sign jau::io::ByteInStream& source, CipherpackListenerRef listener, const std::string_view& plaintext_hash_algo, - const std::string destination_fname) { + const std::string dest_fname) { environment::get(); const bool decrypt_mode = true; - if( destination_fname.empty() ) { + if( dest_fname.empty() ) { PackHeader ph = checkSignThenDecrypt_Impl(sign_pub_keys, dec_sec_key_fname, passphrase, source, listener, plaintext_hash_algo); listener->notifyEnd(decrypt_mode, ph, ph.isValid()); return ph; } + std::string dest_fname2; + if( dest_fname == "/dev/stdout" || dest_fname == "-" ) { + dest_fname2 = "/dev/stdout"; + } else { + dest_fname2 = dest_fname; + } jau::fraction_timespec ts_creation; PackHeader header(ts_creation); @@ -992,24 +1011,24 @@ PackHeader cipherpack::checkSignThenDecrypt(const std::vector<std::string>& sign uint64_t out_bytes_plaintext=0; std::shared_ptr<MyListener> my_listener = std::make_shared<MyListener>(listener, out_bytes_plaintext); { - const jau::fs::file_stats output_stats(destination_fname); + const jau::fs::file_stats output_stats(dest_fname2); if( output_stats.exists() ) { if( output_stats.is_file() ) { - if( !jau::fs::remove(destination_fname) ) { + if( !jau::fs::remove(dest_fname2) ) { ERR_PRINT2("Decrypt failed: Failed deletion of existing output file %s", output_stats.to_string().c_str()); my_listener->notifyEnd(decrypt_mode, header, false); return header; } - } else { + } else if( !output_stats.is_fd() ) { ERR_PRINT2("Decrypt failed: Not overwriting existing %s", output_stats.to_string().c_str()); my_listener->notifyEnd(decrypt_mode, header, false); return header; } } } - std::ofstream outfile(destination_fname, std::ios::out | std::ios::binary); + std::ofstream outfile(dest_fname2, std::ios::out | std::ios::binary); if ( !outfile.good() || !outfile.is_open() ) { - ERR_PRINT2("Decrypt failed: Output file not open %s", destination_fname.c_str()); + ERR_PRINT2("Decrypt failed: Output file not open %s", dest_fname2.c_str()); my_listener->notifyEnd(decrypt_mode, header, false); return header; } @@ -1018,27 +1037,37 @@ PackHeader cipherpack::checkSignThenDecrypt(const std::vector<std::string>& sign PackHeader ph = checkSignThenDecrypt_Impl(sign_pub_keys, dec_sec_key_fname, passphrase, source, my_listener, plaintext_hash_algo); - if ( outfile.fail() ) { - ERR_PRINT2("Decrypt failed: Output file write failed %s", destination_fname.c_str()); - jau::fs::remove(destination_fname); - ph.setValid(false); - my_listener->notifyEnd(decrypt_mode, ph, false); - return ph; - } - outfile.close(); - if( !ph.isValid() ) { - jau::fs::remove(destination_fname); - my_listener->notifyEnd(decrypt_mode, ph, false); - return ph; - } + { + const jau::fs::file_stats output_stats(dest_fname2); + if ( outfile.fail() ) { + ERR_PRINT2("Decrypt failed: Output file write failed %s", dest_fname2.c_str()); + if( output_stats.is_file() ) { + jau::fs::remove(dest_fname2); + } + ph.setValid(false); + my_listener->notifyEnd(decrypt_mode, ph, false); + return ph; + } + outfile.close(); - const jau::fs::file_stats output_stats(destination_fname); - if( ph.getPlaintextSize() != output_stats.size() ) { - ERR_PRINT2("Decrypt: Writing done, %s content_size != %s total bytes", + if( !ph.isValid() ) { + if( output_stats.is_file() ) { + jau::fs::remove(dest_fname2); + } + my_listener->notifyEnd(decrypt_mode, ph, false); + return ph; + } + } + // outfile closed + const jau::fs::file_stats output_stats(dest_fname2); + if( output_stats.is_file() && ph.getPlaintextSize() != output_stats.size() ) { + ERR_PRINT2("Decrypt: Writing done, %s plaintext_size != %s total bytes", jau::to_decstring(ph.getPlaintextSize()).c_str(), jau::to_decstring(output_stats.size()).c_str()); - jau::fs::remove(destination_fname); + if( output_stats.is_file() ) { + jau::fs::remove(dest_fname2); + } ph.setValid(false); my_listener->notifyEnd(decrypt_mode, ph, false); return ph; |