diff options
-rw-r--r-- | include/cipherpack/cipherpack.hpp | 9 | ||||
-rw-r--r-- | java/jni/cipherpack/Cipherpack.cxx | 34 | ||||
-rw-r--r-- | java/org/cipherpack/Cipherpack.java | 19 | ||||
-rw-r--r-- | src/cipherpack/crypto0.cpp | 73 | ||||
-rw-r--r-- | test/cipherpack/test_01_cipherpack.cpp | 57 | ||||
-rw-r--r-- | test/java/test/org/cipherpack/Test01Cipherpack.java | 138 |
6 files changed, 290 insertions, 40 deletions
diff --git a/include/cipherpack/cipherpack.hpp b/include/cipherpack/cipherpack.hpp index 8630d07..682f351 100644 --- a/include/cipherpack/cipherpack.hpp +++ b/include/cipherpack/cipherpack.hpp @@ -629,6 +629,15 @@ namespace cipherpack { * @return the calculated hash value or nullptr in case of error */ std::unique_ptr<std::vector<uint8_t>> calc(const std::string_view& algo, jau::io::ByteInStream& source) noexcept; + + /** + * Return the calculated hash value using given algo name and all actual files (not symbolic links) within the given path. + * @param algo the hash algo name + * @param path source path, either a single file or directory for which all files (not symbolic links) are considered + * @param bytes_hashed returns overall bytes hashed + * @return the calculated hash value or nullptr in case of error + */ + std::unique_ptr<std::vector<uint8_t>> calc(const std::string_view& algo, const std::string& path, uint64_t& bytes_hashed) noexcept; } /**@}*/ diff --git a/java/jni/cipherpack/Cipherpack.cxx b/java/jni/cipherpack/Cipherpack.cxx index c9ee478..685f710 100644 --- a/java/jni/cipherpack/Cipherpack.cxx +++ b/java/jni/cipherpack/Cipherpack.cxx @@ -101,7 +101,7 @@ jobject Java_org_cipherpack_Cipherpack_checkSignThenDecrypt1(JNIEnv *env, jclass return nullptr; } -jbyteArray Java_org_cipherpack_Cipherpack_00024HashUtil_calc(JNIEnv *env, jclass jclazz, jstring jalgo, jobject jsource_feed) { +jbyteArray Java_org_cipherpack_Cipherpack_00024HashUtil_calcImpl1(JNIEnv *env, jclass jclazz, jstring jalgo, jobject jsource_feed) { try { jau::jni::shared_ptr_ref<jau::io::ByteInStream> refSource(env, jsource_feed); // hold until done std::string algo = jau::jni::from_jstring_to_string(env, jalgo); @@ -119,3 +119,35 @@ jbyteArray Java_org_cipherpack_Cipherpack_00024HashUtil_calc(JNIEnv *env, jclass } return nullptr; } + +jbyteArray Java_org_cipherpack_Cipherpack_00024HashUtil_calcImpl2(JNIEnv *env, jclass jclazz, jstring jalgo, jstring jpath, jlongArray jbytes_hashed) { + try { + std::string algo = jau::jni::from_jstring_to_string(env, jalgo); + std::string path = jau::jni::from_jstring_to_string(env, jpath); + + if( nullptr == jbytes_hashed ) { + throw jau::IllegalArgumentException("bytes_hashed null", E_FILE_LINE); + } + const size_t bh_size = env->GetArrayLength(jbytes_hashed); + if( 1 > bh_size ) { + throw jau::IllegalArgumentException("bytes_hashed size "+std::to_string(bh_size)+" < 1", E_FILE_LINE); + } + jau::jni::JNICriticalArray<uint64_t, jlongArray> criticalArray(env); // RAII - release + uint64_t * bh_ptr = criticalArray.get(jbytes_hashed, criticalArray.Mode::UPDATE_AND_RELEASE); + if( NULL == bh_ptr ) { + throw jau::InternalError("GetPrimitiveArrayCritical(address byte array) is null", E_FILE_LINE); + } + + std::unique_ptr<std::vector<uint8_t>> hash = cipherpack::hash_util::calc(algo, path, *bh_ptr); + if( nullptr == hash ) { + return nullptr; + } + jbyteArray jhash = env->NewByteArray((jsize)hash->size()); + env->SetByteArrayRegion(jhash, 0, (jsize)hash->size(), (const jbyte *)hash->data()); + jau::jni::java_exception_check_and_throw(env, E_FILE_LINE); + return jhash; + } catch(...) { + rethrow_and_raise_java_exception(env); + } + return nullptr; +} diff --git a/java/org/cipherpack/Cipherpack.java b/java/org/cipherpack/Cipherpack.java index 5d69a5c..32e1401 100644 --- a/java/org/cipherpack/Cipherpack.java +++ b/java/org/cipherpack/Cipherpack.java @@ -250,7 +250,7 @@ public final class Cipherpack { * Hash utility functions to produce a hash file compatible to `sha256sum` * as well as to produce the hash value itself for validation. */ - public static class HashUtil { + public static final class HashUtil { /** Return a lower-case file suffix used to store a `sha256sum` compatible hash signature w/o dot and w/o dashes. */ public static String fileSuffix(final String algo) { return algo.toLowerCase().replace("-", ""); @@ -287,6 +287,21 @@ public final class Cipherpack { * @param source the byte input stream * @return the calculated hash value or null in case of error */ - public static native byte[] calc(final String algo, final ByteInStream source); + public static byte[] calc(final String algo, final ByteInStream source) { + return calcImpl1(algo, source); + } + private static native byte[] calcImpl1(final String algo, final ByteInStream source); + + /** + * Return the calculated hash value using given algo name and all actual files (not symbolic links) within the given path. + * @param algo the hash algo name + * @param path source path, either a single file or directory for which all files (not symbolic links) are considered + * @param bytes_hashed returns overall bytes hashed, an array of length 1 + * @return the calculated hash value or nullptr in case of error + */ + public static byte[] calc(final String algo, final String path, final long bytes_hashed[/*0*/]) { + return calcImpl2(algo, path, bytes_hashed); + } + private static native byte[] calcImpl2(final String algo, final String path, final long bytes_hashed[/*0*/]); } } diff --git a/src/cipherpack/crypto0.cpp b/src/cipherpack/crypto0.cpp index ea49b71..b3f9b4f 100644 --- a/src/cipherpack/crypto0.cpp +++ b/src/cipherpack/crypto0.cpp @@ -391,3 +391,76 @@ std::unique_ptr<std::vector<uint8_t>> cipherpack::hash_util::calc(const std::str hash_func->final(res->data()); return res; } + +std::unique_ptr<std::vector<uint8_t>> cipherpack::hash_util::calc(const std::string_view& algo, const std::string& path, uint64_t& bytes_hashed) noexcept { + bytes_hashed = 0; + jau::fs::file_stats source_stats(path); + if( source_stats.is_file() ) { + jau::io::ByteInStream_File in(path); + if( in.error() ) { + return nullptr; + } + return calc(algo, in); + } + if( !source_stats.is_dir() ) { + ERR_PRINT("path is neither file nor dir: %s", source_stats.to_string()); + return nullptr; + } + + // + // directory handling + // + struct context_t { + std::vector<int> dirfds; + std::unique_ptr<Botan::HashFunction> hash_func; + jau::io::secure_vector<uint8_t> io_buffer; + jau::io::StreamConsumerFunc consume_data; + uint64_t bytes_hashed; + }; + context_t ctx { std::vector<int>(), nullptr, jau::io::secure_vector<uint8_t>(), nullptr, 0 }; + { + const std::string algo_s(algo); + ctx.hash_func = Botan::HashFunction::create(algo_s); + if( nullptr == ctx.hash_func ) { + ERR_PRINT2("Hash failed: Algo %s not available", algo_s.c_str()); + return nullptr; + } + } + ctx.consume_data = [&](jau::io::secure_vector<uint8_t>& data, bool is_final) -> bool { + (void) is_final; + ctx.hash_func->update(data); + return true; + }; + ctx.io_buffer.reserve(Constants::buffer_size); + + const jau::fs::path_visitor pv = jau::bindCaptureRefFunc<bool, context_t, jau::fs::traverse_event, const jau::fs::file_stats&>(&ctx, + ( bool(*)(context_t*, jau::fs::traverse_event, const jau::fs::file_stats&) ) /* help template type deduction of function-ptr */ + ( [](context_t* ctx_ptr, jau::fs::traverse_event tevt, const jau::fs::file_stats& element_stats) -> bool { + if( is_set(tevt, jau::fs::traverse_event::file) && !is_set(tevt, jau::fs::traverse_event::symlink) ) { + // FIXME: It would be desirable to have ByteInStream_File handle dirfd, + // i.e. implementation based on OS level I/O. + // + // const int dirfd = ctx_ptr->dirfds.back(); + // const std::string& basename_ = element_stats.item().basename(); + jau::io::ByteInStream_File in(element_stats.path()); + if( in.error() ) { + return false; + } + const uint64_t in_bytes_total = jau::io::read_stream(in, ctx_ptr->io_buffer, ctx_ptr->consume_data); + in.close(); + if( in.has_content_size() && in_bytes_total != in.content_size() ) { + ERR_PRINT2("Hash failed: Only read %" PRIu64 " bytes of %s", in_bytes_total, in.to_string().c_str()); + return false; + } + ctx_ptr->bytes_hashed += in_bytes_total; + } + return true; + } ) ); + if( jau::fs::visit(source_stats, jau::fs::traverse_options::recursive, pv, &ctx.dirfds) ) { + std::unique_ptr<std::vector<uint8_t>> res = std::make_unique<std::vector<uint8_t>>(ctx.hash_func->output_length()); + ctx.hash_func->final(res->data()); + bytes_hashed = ctx.bytes_hashed; + return res; + } + return nullptr; +} diff --git a/test/cipherpack/test_01_cipherpack.cpp b/test/cipherpack/test_01_cipherpack.cpp index b3f2c0e..9b6f682 100644 --- a/test/cipherpack/test_01_cipherpack.cpp +++ b/test/cipherpack/test_01_cipherpack.cpp @@ -239,7 +239,7 @@ class Test01Cipherpack : public TestData { REQUIRE( hash_value == *orig_hash_value ); const jau::fraction_i64 _td = ( jau::getMonotonicTime() - _t0 ).to_fraction_i64(); - jau::io::print_stats("Hash "+hash_algo, orig_in->content_size(), _td); + jau::io::print_stats("Hash '"+hash_algo+"'", orig_in->content_size(), _td); jau::PLAIN_PRINT(true, ""); } @@ -647,6 +647,59 @@ class Test01Cipherpack : public TestData { } } + const std::string root = "test_data"; + // submodule location with jaulib directly hosted below main project + const std::string project_root2 = "../../../jaulib/test_data"; + + void test50_copy_and_verify() { + const std::string title("test50_copy_and_verify"); + const std::string hash_file = title+".hash"; + + jau::fprintf_td(stderr, "\n"); + jau::fprintf_td(stderr, "%s\n", title.c_str()); + + jau::fs::remove(hash_file); + + jau::fs::file_stats source_stats(project_root2); + REQUIRE( true == source_stats.exists() ); + REQUIRE( true == source_stats.is_dir() ); + + uint64_t source_bytes_hashed = 0; + std::unique_ptr<std::vector<uint8_t>> source_hash = cipherpack::hash_util::calc(cipherpack::default_hash_algo(), source_stats.path(), source_bytes_hashed); + REQUIRE( nullptr != source_hash ); + REQUIRE( true == cipherpack::hash_util::append_to_file(hash_file, source_stats.path(), *source_hash)); + + // copy folder + const std::string dest = root+"_copy_verify_test50"; + { + const jau::fs::copy_options copts = jau::fs::copy_options::recursive | + jau::fs::copy_options::preserve_all | + jau::fs::copy_options::sync | + jau::fs::copy_options::verbose; + jau::fs::remove(dest, jau::fs::traverse_options::recursive); + REQUIRE( true == jau::fs::copy(source_stats.path(), dest, copts) ); + } + jau::fs::file_stats dest_stats(dest); + REQUIRE( true == dest_stats.exists() ); + REQUIRE( true == dest_stats.ok() ); + REQUIRE( true == dest_stats.is_dir() ); + + uint64_t dest_bytes_hashed = 0; + std::unique_ptr<std::vector<uint8_t>> dest_hash = cipherpack::hash_util::calc(cipherpack::default_hash_algo(), dest_stats.path(), dest_bytes_hashed); + REQUIRE( nullptr != dest_hash ); + REQUIRE( true == cipherpack::hash_util::append_to_file(hash_file, dest_stats.path(), *dest_hash)); + + // actual validation of hash values, i.e. same content + REQUIRE( *source_hash == *dest_hash ); + REQUIRE( source_bytes_hashed == dest_bytes_hashed ); + + jau::fprintf_td(stderr, "%s: bytes %s, '%s'\n", title.c_str(), + jau::to_decstring(dest_bytes_hashed).c_str(), + jau::bytesHexString(dest_hash->data(), 0, dest_hash->size(), true /* lsbFirst */, true /* lowerCase */).c_str()); + + REQUIRE( true == jau::fs::remove(dest, jau::fs::traverse_options::recursive) ); + } + }; std::vector<std::string> Test01Cipherpack::fname_payload_lst; @@ -663,4 +716,4 @@ METHOD_AS_TEST_CASE( Test01Cipherpack::test13_dec_http_error, "CipherPack 02 METHOD_AS_TEST_CASE( Test01Cipherpack::test21_enc_dec_fed_ok, "CipherPack 03 test21_enc_dec_fed_ok"); METHOD_AS_TEST_CASE( Test01Cipherpack::test22_enc_dec_fed_irq, "CipherPack 03 test22_enc_dec_fed_irq"); - +METHOD_AS_TEST_CASE( Test01Cipherpack::test50_copy_and_verify, "CipherPack 03 test50_copy_and_verify"); diff --git a/test/java/test/org/cipherpack/Test01Cipherpack.java b/test/java/test/org/cipherpack/Test01Cipherpack.java index d4658ea..f5ebb9d 100644 --- a/test/java/test/org/cipherpack/Test01Cipherpack.java +++ b/test/java/test/org/cipherpack/Test01Cipherpack.java @@ -34,6 +34,8 @@ import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.nio.file.Path; import java.nio.file.Paths; +import java.time.Instant; +import java.time.temporal.ChronoUnit; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -45,12 +47,17 @@ import org.cipherpack.Cipherpack; import org.cipherpack.CipherpackListener; import org.cipherpack.CryptoConfig; import org.cipherpack.PackHeader; +import org.jau.fs.CopyOptions; +import org.jau.fs.FileStats; +import org.jau.fs.FileUtil; +import org.jau.fs.TraverseOptions; import org.jau.io.ByteInStream; import org.jau.io.ByteInStreamUtil; import org.jau.io.ByteInStream_Feed; import org.jau.io.ByteInStream_File; import org.jau.io.ByteInStream_URL; import org.jau.io.PrintUtil; +import org.jau.sys.Clock; import org.jau.util.BasicTypes; import org.junit.AfterClass; import org.junit.Assert; @@ -158,7 +165,7 @@ public class Test01Cipherpack extends data_test { CipherpackListener silentListener = new CipherpackListener(); - @Test(timeout = 10000) + @Test(timeout = 20000) public final void test01_enc_dec_file_ok() { CPFactory.checkInitialized(); final List<String> enc_pub_keys = Arrays.asList(enc_pub_key1_fname, enc_pub_key2_fname, enc_pub_key3_fname); @@ -235,20 +242,7 @@ public class Test01Cipherpack extends data_test { PrintUtil.fprintf_td(System.err, "test01_enc_dec_file_ok: Decypted %s to %s\n", fname_payload_encrypted_lst.get(file_idx), fname_payload_decrypted_lst.get(file_idx)); PrintUtil.fprintf_td(System.err, "test01_enc_dec_file_ok: %s\n", ph2.toString(true, true)); Assert.assertTrue( ph2.isValid() ); - { - final String hashedDescryptedFile = fname_payload_decrypted_lst.get(file_idx); - final String suffix = Cipherpack.HashUtil.fileSuffix(ph2.payload_hash_algo); - final String outFile = hashedDescryptedFile + "." + suffix; - remove_file( outFile ); - Assert.assertTrue( Cipherpack.HashUtil.appendToFile(outFile, hashedDescryptedFile, ph2.payload_hash) ); - - final String origFile = fname_payload_lst.get(file_idx); - final ByteInStream origIn = ByteInStreamUtil.to_ByteInStream(origFile); - Assert.assertNotNull( origIn ); - final byte[] origHashValue = Cipherpack.HashUtil.calc(ph2.payload_hash_algo, origIn); - Assert.assertNotNull( origHashValue ); - Assert.assertArrayEquals(ph2.payload_hash, origHashValue); - } + hash_retest(fname_payload_lst.get(file_idx), fname_payload_decrypted_lst.get(file_idx), ph2.payload_hash_algo, ph2.payload_hash); } { final ByteInStream_File enc_stream = new ByteInStream_File(fname_payload_encrypted_lst.get(file_idx)); @@ -258,25 +252,44 @@ public class Test01Cipherpack extends data_test { PrintUtil.fprintf_td(System.err, "test01_enc_dec_file_ok: Decypted %s to %s\n", fname_payload_encrypted_lst.get(file_idx), fname_payload_decrypted_lst.get(file_idx)); PrintUtil.fprintf_td(System.err, "test01_enc_dec_file_ok: %s\n", ph2.toString(true, true)); Assert.assertTrue( ph2.isValid() ); - { - final String hashedDescryptedFile = fname_payload_decrypted_lst.get(file_idx); - final String suffix = Cipherpack.HashUtil.fileSuffix(ph2.payload_hash_algo); - final String outFile = hashedDescryptedFile + "." + suffix; - remove_file( outFile ); - Assert.assertTrue( Cipherpack.HashUtil.appendToFile(outFile, hashedDescryptedFile, ph2.payload_hash) ); - - final String origFile = fname_payload_lst.get(file_idx); - final ByteInStream origIn = ByteInStreamUtil.to_ByteInStream(origFile); - Assert.assertNotNull( origIn ); - final byte[] origHashValue = Cipherpack.HashUtil.calc(ph2.payload_hash_algo, origIn); - Assert.assertNotNull( origHashValue ); - Assert.assertArrayEquals(ph2.payload_hash, origHashValue); - } + hash_retest(fname_payload_lst.get(file_idx), fname_payload_decrypted_lst.get(file_idx), ph2.payload_hash_algo, ph2.payload_hash); + } + { + final ByteInStream_File enc_stream = new ByteInStream_File(fname_payload_encrypted_lst.get(file_idx)); + final PackHeader ph2 = Cipherpack.checkSignThenDecrypt(sign_pub_keys, dec_sec_key3_fname, dec_sec_key_passphrase, + enc_stream, + silentListener, "BLAKE2b(512)", fname_payload_decrypted_lst.get(file_idx)); + PrintUtil.fprintf_td(System.err, "test01_enc_dec_file_ok: Decypted %s to %s\n", fname_payload_encrypted_lst.get(file_idx), fname_payload_decrypted_lst.get(file_idx)); + PrintUtil.fprintf_td(System.err, "test01_enc_dec_file_ok: %s\n", ph2.toString(true, true)); + Assert.assertTrue( ph2.isValid() ); + hash_retest(fname_payload_lst.get(file_idx), fname_payload_decrypted_lst.get(file_idx), ph2.payload_hash_algo, ph2.payload_hash); } } } - @Test(timeout = 10000) + public static void hash_retest(final String origFile, final String hashedDescryptedFile, + final String hashAlgo, final byte[] hashValue) + { + final String suffix = Cipherpack.HashUtil.fileSuffix(hashAlgo); + final String outFile = hashedDescryptedFile + "." + suffix; + FileUtil.remove(outFile, TraverseOptions.none); + + Assert.assertTrue( Cipherpack.HashUtil.appendToFile(outFile, hashedDescryptedFile, hashValue) ); + + final ByteInStream origIn = ByteInStreamUtil.to_ByteInStream(origFile); + Assert.assertNotNull( origIn ); + final Instant t0 = Clock.getMonotonicTime(); + final byte[] origHashValue = Cipherpack.HashUtil.calc(hashAlgo, origIn); + Assert.assertNotNull( origHashValue ); + Assert.assertArrayEquals(hashValue, origHashValue); + + final Instant t1 = Clock.getMonotonicTime(); + final long td_ms = t0.until(t1, ChronoUnit.MILLIS); + ByteInStreamUtil.print_stats("Hash '"+hashAlgo+"'", origIn.content_size(), td_ms); + PrintUtil.fprintf_td(System.err, "\n"); + } + + @Test(timeout = 20000) public final void test02_enc_dec_file_error() { CPFactory.checkInitialized(); @@ -317,7 +330,7 @@ public class Test01Cipherpack extends data_test { } } - @Test(timeout = 10000) + @Test(timeout = 20000) public final void test11_dec_http_ok() { CPFactory.checkInitialized(); if( !org.jau.io.UriTk.protocol_supported("http:") ) { @@ -372,7 +385,7 @@ public class Test01Cipherpack extends data_test { } } - @Test(timeout = 10000) + @Test(timeout = 20000) public final void test12_dec_http_ok() { CPFactory.checkInitialized(); if( !org.jau.io.UriTk.protocol_supported("http:") ) { @@ -409,7 +422,7 @@ public class Test01Cipherpack extends data_test { } } - @Test(timeout = 10000) + @Test(timeout = 20000) public final void test13_dec_http_error() { CPFactory.checkInitialized(); if( !org.jau.io.UriTk.protocol_supported("http:") ) { @@ -625,7 +638,7 @@ public class Test01Cipherpack extends data_test { } } - @Test(timeout = 10000) + @Test(timeout = 20000) public final void test21_enc_dec_fed_ok() { CPFactory.checkInitialized(); final List<String> enc_pub_keys = Arrays.asList(enc_pub_key1_fname, enc_pub_key2_fname, enc_pub_key3_fname); @@ -710,7 +723,7 @@ public class Test01Cipherpack extends data_test { } } - @Test(timeout = 10000) + @Test(timeout = 20000) public final void test22_enc_dec_fed_irq() { CPFactory.checkInitialized(); final List<String> enc_pub_keys = Arrays.asList(enc_pub_key1_fname, enc_pub_key2_fname, enc_pub_key3_fname); @@ -764,6 +777,61 @@ public class Test01Cipherpack extends data_test { } } + final static String root = "test_data"; + // submodule location with jaulib directly hosted below main project + final static String project_root2 = "../../../jaulib/test_data"; + + @Test(timeout = 20000) + public final void test50_copy_and_verify() { + final String title = "test50_copy_and_verify"; + final String hash_file = title+".hash"; + + PrintUtil.fprintf_td(System.err, "\n"); + PrintUtil.fprintf_td(System.err, "%s\n", title); + + FileUtil.remove(hash_file, TraverseOptions.none); + + final FileStats source_stats = new FileStats(project_root2); + Assert.assertTrue( source_stats.exists() ); + Assert.assertTrue( source_stats.is_dir() ); + + final long[] source_bytes_hashed = { 0 }; + final byte[] source_hash = Cipherpack.HashUtil.calc(Cipherpack.default_hash_algo(), source_stats.path(), source_bytes_hashed); + Assert.assertNotNull( source_hash ); + Assert.assertTrue( Cipherpack.HashUtil.appendToFile(hash_file, source_stats.path(), source_hash)); + + // copy folder + final String dest = root+"_copy_verify_test50"; + { + final CopyOptions copts = new CopyOptions(); + copts.set(CopyOptions.Bit.recursive); + copts.set(CopyOptions.Bit.preserve_all); + copts.set(CopyOptions.Bit.sync); + copts.set(CopyOptions.Bit.verbose); + + FileUtil.remove(dest, TraverseOptions.recursive); + Assert.assertTrue( true == FileUtil.copy(source_stats.path(), dest, copts) ); + } + final FileStats dest_stats = new FileStats(dest); + Assert.assertTrue( source_stats.exists() ); + Assert.assertTrue( source_stats.ok() ); + Assert.assertTrue( source_stats.is_dir() ); + + final long[] dest_bytes_hashed = { 0 }; + final byte[] dest_hash = Cipherpack.HashUtil.calc(Cipherpack.default_hash_algo(), dest_stats.path(), dest_bytes_hashed); + Assert.assertNotNull( dest_hash ); + Assert.assertTrue( Cipherpack.HashUtil.appendToFile(hash_file, dest_stats.path(), dest_hash)); + + // actual validation of hash values, i.e. same content + Assert.assertArrayEquals(source_hash, dest_hash); + Assert.assertEquals( source_bytes_hashed[0], dest_bytes_hashed[0] ); + + PrintUtil.fprintf_td(System.err, "%s: bytes %,d, '%s'\n", title, dest_bytes_hashed[0], + BasicTypes.bytesHexString(dest_hash, 0, dest_hash.length, true /* lsbFirst */)); + + Assert.assertTrue( FileUtil.remove(dest, TraverseOptions.recursive) ); + } + public static void main(final String args[]) { org.junit.runner.JUnitCore.main(Test01Cipherpack.class.getName()); } |