aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSven Gothel <[email protected]>2022-07-25 19:29:44 +0200
committerSven Gothel <[email protected]>2022-07-25 19:29:44 +0200
commit320881dabd44168cc53f50f348a0e1bd8add8deb (patch)
tree1547d3e49abf7dd691a9587c8649b729448bde6e
parent986ea69db2ba5d3ded085468fcaee9d60472927f (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.hpp26
-rw-r--r--java/jni/cipherpack/CipherpackListener.cxx4
-rw-r--r--java/org/cipherpack/Cipherpack.java18
-rw-r--r--java/org/cipherpack/CipherpackListener.java4
-rw-r--r--java/org/cipherpack/PackHeader.java25
-rw-r--r--src/cipherpack/crypto0.cpp10
-rw-r--r--src/cipherpack/crypto1.cpp229
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;