omnizip 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.rspec +3 -0
- data/.rubocop.yml +32 -0
- data/.rubocop_todo.yml +754 -0
- data/COPYING +502 -0
- data/Gemfile +17 -0
- data/LICENSE +12 -0
- data/README.adoc +1045 -0
- data/Rakefile +12 -0
- data/benchmark/README.md +260 -0
- data/benchmark/benchmark_suite.rb +125 -0
- data/benchmark/compression_bench.rb +181 -0
- data/benchmark/filter_bench.rb +180 -0
- data/benchmark/models/benchmark_result.rb +59 -0
- data/benchmark/models/comparison_result.rb +69 -0
- data/benchmark/profile_suite.rb +167 -0
- data/benchmark/reporter.rb +150 -0
- data/benchmark/run_benchmarks.rb +66 -0
- data/benchmark/test_data.rb +137 -0
- data/config/formats/rar3_spec.yml +91 -0
- data/config/formats/rar5_spec.yml +102 -0
- data/docs/.github/workflows/docs.yml +142 -0
- data/docs/.gitignore +21 -0
- data/docs/.lychee.toml +67 -0
- data/docs/Gemfile +13 -0
- data/docs/RAR_WRITE_SUPPORT.md +26 -0
- data/docs/README.md +101 -0
- data/docs/_config.yml +112 -0
- data/docs/assets/logo.svg +1 -0
- data/docs/assets/omnizip-logo.pdf +1540 -11
- data/docs/comparison/feature-matrix.adoc +694 -0
- data/docs/comparison/index.adoc +113 -0
- data/docs/comparison/vs-7zip.adoc +309 -0
- data/docs/comparison/vs-peazip.adoc +77 -0
- data/docs/comparison/vs-rubyzip.adoc +342 -0
- data/docs/comparison/vs-winrar.adoc +100 -0
- data/docs/compatibility.adoc +579 -0
- data/docs/concepts/index.adoc +129 -0
- data/docs/developer/architecture.adoc +256 -0
- data/docs/developer/contributing.adoc +158 -0
- data/docs/developer/index.adoc +25 -0
- data/docs/developer/testing.adoc +212 -0
- data/docs/getting-started/basic-usage.adoc +271 -0
- data/docs/getting-started/index.adoc +42 -0
- data/docs/getting-started/installation.adoc +138 -0
- data/docs/getting-started/quick-start.adoc +185 -0
- data/docs/getting-started/your-first-archive.adoc +218 -0
- data/docs/guides/advanced-features/encryption.adoc +300 -0
- data/docs/guides/advanced-features/index.adoc +49 -0
- data/docs/guides/advanced-features/parallel-processing.adoc +246 -0
- data/docs/guides/advanced-features/progress-tracking.adoc +320 -0
- data/docs/guides/advanced-features/streaming.adoc +212 -0
- data/docs/guides/archive-formats/gzip-format.adoc +107 -0
- data/docs/guides/archive-formats/index.adoc +130 -0
- data/docs/guides/archive-formats/rar-format.adoc +104 -0
- data/docs/guides/archive-formats/rar5.adoc +521 -0
- data/docs/guides/archive-formats/seven-zip-format.adoc +35 -0
- data/docs/guides/archive-formats/tar-format.adoc +106 -0
- data/docs/guides/archive-formats/xz-format.adoc +118 -0
- data/docs/guides/archive-formats/zip-format.adoc +35 -0
- data/docs/guides/compression-algorithms/bzip2.adoc +113 -0
- data/docs/guides/compression-algorithms/deflate.adoc +319 -0
- data/docs/guides/compression-algorithms/index.adoc +190 -0
- data/docs/guides/compression-algorithms/lzma.adoc +398 -0
- data/docs/guides/compression-algorithms/lzma2.adoc +327 -0
- data/docs/guides/compression-algorithms/ppmd.adoc +316 -0
- data/docs/guides/compression-algorithms/zstandard.adoc +361 -0
- data/docs/guides/creating-archives.adoc +354 -0
- data/docs/guides/extracting-archives.adoc +53 -0
- data/docs/guides/format-conversion.adoc +64 -0
- data/docs/guides/index.adoc +49 -0
- data/docs/guides/migration-rubyzip.adoc +217 -0
- data/docs/guides/parity-archives.adoc +605 -0
- data/docs/guides/performance-tuning.adoc +88 -0
- data/docs/index.adoc +218 -0
- data/docs/lychee.toml +67 -0
- data/docs/reference/api/overview.adoc +188 -0
- data/docs/reference/cli/compress-command.adoc +114 -0
- data/docs/reference/cli/overview.adoc +140 -0
- data/docs/reference/index.adoc +26 -0
- data/docs/resources/faq.adoc +185 -0
- data/docs/resources/quick-reference.adoc +222 -0
- data/docs/troubleshooting/index.adoc +208 -0
- data/examples/api_comparison.rb +205 -0
- data/examples/deflate64_example.rb +96 -0
- data/examples/par2_demo.rb +121 -0
- data/examples/quick_start_native.rb +150 -0
- data/examples/quick_start_rubyzip.rb +115 -0
- data/examples/rubyzip_compatibility_demo.rb +194 -0
- data/exe/omnizip +27 -0
- data/lib/omnizip/algorithm.rb +130 -0
- data/lib/omnizip/algorithm_registry.rb +86 -0
- data/lib/omnizip/algorithms/.keep +0 -0
- data/lib/omnizip/algorithms/bzip2/bwt.rb +225 -0
- data/lib/omnizip/algorithms/bzip2/decoder.rb +193 -0
- data/lib/omnizip/algorithms/bzip2/encoder.rb +237 -0
- data/lib/omnizip/algorithms/bzip2/huffman.rb +206 -0
- data/lib/omnizip/algorithms/bzip2/mtf.rb +101 -0
- data/lib/omnizip/algorithms/bzip2/rle.rb +151 -0
- data/lib/omnizip/algorithms/bzip2.rb +130 -0
- data/lib/omnizip/algorithms/deflate/constants.rb +28 -0
- data/lib/omnizip/algorithms/deflate/decoder.rb +38 -0
- data/lib/omnizip/algorithms/deflate/encoder.rb +46 -0
- data/lib/omnizip/algorithms/deflate.rb +128 -0
- data/lib/omnizip/algorithms/deflate64/constants.rb +45 -0
- data/lib/omnizip/algorithms/deflate64/decoder.rb +153 -0
- data/lib/omnizip/algorithms/deflate64/encoder.rb +98 -0
- data/lib/omnizip/algorithms/deflate64/huffman_coder.rb +354 -0
- data/lib/omnizip/algorithms/deflate64/lz77_encoder.rb +142 -0
- data/lib/omnizip/algorithms/deflate64.rb +109 -0
- data/lib/omnizip/algorithms/lzma/bit_model.rb +120 -0
- data/lib/omnizip/algorithms/lzma/constants.rb +112 -0
- data/lib/omnizip/algorithms/lzma/decoder.rb +148 -0
- data/lib/omnizip/algorithms/lzma/dictionary.rb +69 -0
- data/lib/omnizip/algorithms/lzma/distance_coder.rb +415 -0
- data/lib/omnizip/algorithms/lzma/encoder.rb +142 -0
- data/lib/omnizip/algorithms/lzma/length_coder.rb +260 -0
- data/lib/omnizip/algorithms/lzma/literal_decoder.rb +320 -0
- data/lib/omnizip/algorithms/lzma/literal_encoder.rb +210 -0
- data/lib/omnizip/algorithms/lzma/lzip_decoder.rb +341 -0
- data/lib/omnizip/algorithms/lzma/lzma_alone_decoder.rb +192 -0
- data/lib/omnizip/algorithms/lzma/lzma_state.rb +128 -0
- data/lib/omnizip/algorithms/lzma/match.rb +32 -0
- data/lib/omnizip/algorithms/lzma/match_finder.rb +205 -0
- data/lib/omnizip/algorithms/lzma/match_finder_config.rb +142 -0
- data/lib/omnizip/algorithms/lzma/match_finder_factory.rb +88 -0
- data/lib/omnizip/algorithms/lzma/optimal_encoder.rb +130 -0
- data/lib/omnizip/algorithms/lzma/probability_models.rb +72 -0
- data/lib/omnizip/algorithms/lzma/range_coder.rb +85 -0
- data/lib/omnizip/algorithms/lzma/range_decoder.rb +434 -0
- data/lib/omnizip/algorithms/lzma/range_encoder.rb +194 -0
- data/lib/omnizip/algorithms/lzma/state.rb +127 -0
- data/lib/omnizip/algorithms/lzma/xz_buffered_range_encoder.rb +325 -0
- data/lib/omnizip/algorithms/lzma/xz_encoder.rb +426 -0
- data/lib/omnizip/algorithms/lzma/xz_encoder_fast.rb +645 -0
- data/lib/omnizip/algorithms/lzma/xz_match_finder_adapter.rb +227 -0
- data/lib/omnizip/algorithms/lzma/xz_price_calculator.rb +169 -0
- data/lib/omnizip/algorithms/lzma/xz_probability_models.rb +261 -0
- data/lib/omnizip/algorithms/lzma/xz_range_encoder.rb +223 -0
- data/lib/omnizip/algorithms/lzma/xz_range_encoder_exact.rb +331 -0
- data/lib/omnizip/algorithms/lzma/xz_state.rb +116 -0
- data/lib/omnizip/algorithms/lzma/xz_utils_decoder.rb +2055 -0
- data/lib/omnizip/algorithms/lzma.rb +238 -0
- data/lib/omnizip/algorithms/lzma2/chunk_manager.rb +182 -0
- data/lib/omnizip/algorithms/lzma2/constants.rb +41 -0
- data/lib/omnizip/algorithms/lzma2/encoder.rb +147 -0
- data/lib/omnizip/algorithms/lzma2/lzma2_chunk.rb +161 -0
- data/lib/omnizip/algorithms/lzma2/properties.rb +179 -0
- data/lib/omnizip/algorithms/lzma2/simple_lzma2_encoder.rb +127 -0
- data/lib/omnizip/algorithms/lzma2/xz_encoder_adapter.rb +85 -0
- data/lib/omnizip/algorithms/lzma2.rb +141 -0
- data/lib/omnizip/algorithms/ppmd7/constants.rb +74 -0
- data/lib/omnizip/algorithms/ppmd7/context.rb +154 -0
- data/lib/omnizip/algorithms/ppmd7/decoder.rb +126 -0
- data/lib/omnizip/algorithms/ppmd7/encoder.rb +163 -0
- data/lib/omnizip/algorithms/ppmd7/model.rb +248 -0
- data/lib/omnizip/algorithms/ppmd7/symbol_state.rb +57 -0
- data/lib/omnizip/algorithms/ppmd7.rb +116 -0
- data/lib/omnizip/algorithms/ppmd8/constants.rb +61 -0
- data/lib/omnizip/algorithms/ppmd8/context.rb +34 -0
- data/lib/omnizip/algorithms/ppmd8/decoder.rb +107 -0
- data/lib/omnizip/algorithms/ppmd8/encoder.rb +138 -0
- data/lib/omnizip/algorithms/ppmd8/model.rb +250 -0
- data/lib/omnizip/algorithms/ppmd8/restoration_method.rb +78 -0
- data/lib/omnizip/algorithms/ppmd8.rb +82 -0
- data/lib/omnizip/algorithms/ppmd_base.rb +138 -0
- data/lib/omnizip/algorithms/sevenzip_lzma2.rb +123 -0
- data/lib/omnizip/algorithms/xz_lzma2.rb +118 -0
- data/lib/omnizip/algorithms/zstandard/constants.rb +25 -0
- data/lib/omnizip/algorithms/zstandard/decoder.rb +46 -0
- data/lib/omnizip/algorithms/zstandard/encoder.rb +51 -0
- data/lib/omnizip/algorithms/zstandard.rb +138 -0
- data/lib/omnizip/buffer/memory_archive.rb +251 -0
- data/lib/omnizip/buffer/memory_extractor.rb +224 -0
- data/lib/omnizip/buffer.rb +176 -0
- data/lib/omnizip/checksum_registry.rb +114 -0
- data/lib/omnizip/checksums/crc32.rb +100 -0
- data/lib/omnizip/checksums/crc64.rb +101 -0
- data/lib/omnizip/checksums/crc_base.rb +158 -0
- data/lib/omnizip/checksums/verifier.rb +131 -0
- data/lib/omnizip/chunked/memory_manager.rb +194 -0
- data/lib/omnizip/chunked/reader.rb +78 -0
- data/lib/omnizip/chunked/writer.rb +120 -0
- data/lib/omnizip/chunked.rb +129 -0
- data/lib/omnizip/cli/output_formatter.rb +104 -0
- data/lib/omnizip/cli.rb +572 -0
- data/lib/omnizip/commands/.keep +0 -0
- data/lib/omnizip/commands/archive_create_command.rb +427 -0
- data/lib/omnizip/commands/archive_extract_command.rb +272 -0
- data/lib/omnizip/commands/archive_list_command.rb +218 -0
- data/lib/omnizip/commands/archive_repair_command.rb +131 -0
- data/lib/omnizip/commands/archive_verify_command.rb +117 -0
- data/lib/omnizip/commands/compress_command.rb +117 -0
- data/lib/omnizip/commands/decompress_command.rb +120 -0
- data/lib/omnizip/commands/list_command.rb +53 -0
- data/lib/omnizip/commands/metadata_command.rb +153 -0
- data/lib/omnizip/commands/parity_create_command.rb +122 -0
- data/lib/omnizip/commands/parity_repair_command.rb +122 -0
- data/lib/omnizip/commands/parity_verify_command.rb +124 -0
- data/lib/omnizip/commands/profile_list_command.rb +56 -0
- data/lib/omnizip/commands/profile_show_command.rb +44 -0
- data/lib/omnizip/convenience.rb +359 -0
- data/lib/omnizip/converter/conversion_registry.rb +49 -0
- data/lib/omnizip/converter/conversion_strategy.rb +121 -0
- data/lib/omnizip/converter/seven_zip_to_zip_strategy.rb +97 -0
- data/lib/omnizip/converter/zip_to_seven_zip_strategy.rb +112 -0
- data/lib/omnizip/converter.rb +105 -0
- data/lib/omnizip/crypto/aes256/cipher.rb +100 -0
- data/lib/omnizip/crypto/aes256/constants.rb +28 -0
- data/lib/omnizip/crypto/aes256/key_derivation.rb +101 -0
- data/lib/omnizip/crypto/aes256.rb +102 -0
- data/lib/omnizip/error.rb +106 -0
- data/lib/omnizip/eta/exponential_smoothing_estimator.rb +98 -0
- data/lib/omnizip/eta/moving_average_estimator.rb +99 -0
- data/lib/omnizip/eta/rate_calculator.rb +104 -0
- data/lib/omnizip/eta/sample_history.rb +143 -0
- data/lib/omnizip/eta/time_estimator.rb +106 -0
- data/lib/omnizip/eta.rb +63 -0
- data/lib/omnizip/extraction/filter_chain.rb +177 -0
- data/lib/omnizip/extraction/glob_pattern.rb +140 -0
- data/lib/omnizip/extraction/pattern_matcher.rb +70 -0
- data/lib/omnizip/extraction/predicate_pattern.rb +52 -0
- data/lib/omnizip/extraction/regex_pattern.rb +50 -0
- data/lib/omnizip/extraction/selective_extractor.rb +240 -0
- data/lib/omnizip/extraction.rb +111 -0
- data/lib/omnizip/file_type/mime_classifier.rb +144 -0
- data/lib/omnizip/file_type.rb +113 -0
- data/lib/omnizip/filter.rb +139 -0
- data/lib/omnizip/filter_pipeline.rb +108 -0
- data/lib/omnizip/filter_registry.rb +166 -0
- data/lib/omnizip/filters/bcj.rb +279 -0
- data/lib/omnizip/filters/bcj2/constants.rb +53 -0
- data/lib/omnizip/filters/bcj2/decoder.rb +200 -0
- data/lib/omnizip/filters/bcj2/encoder.rb +61 -0
- data/lib/omnizip/filters/bcj2/stream_data.rb +93 -0
- data/lib/omnizip/filters/bcj2.rb +99 -0
- data/lib/omnizip/filters/bcj_arm.rb +176 -0
- data/lib/omnizip/filters/bcj_arm64.rb +244 -0
- data/lib/omnizip/filters/bcj_ia64.rb +196 -0
- data/lib/omnizip/filters/bcj_ppc.rb +190 -0
- data/lib/omnizip/filters/bcj_sparc.rb +176 -0
- data/lib/omnizip/filters/bcj_x86.rb +193 -0
- data/lib/omnizip/filters/delta.rb +196 -0
- data/lib/omnizip/filters/filter_base.rb +72 -0
- data/lib/omnizip/filters/registry.rb +123 -0
- data/lib/omnizip/filters/xz_delta.rb +258 -0
- data/lib/omnizip/format_detector.rb +162 -0
- data/lib/omnizip/format_registry.rb +59 -0
- data/lib/omnizip/formats/.keep +0 -0
- data/lib/omnizip/formats/bzip2_file.rb +172 -0
- data/lib/omnizip/formats/cpio/constants.rb +55 -0
- data/lib/omnizip/formats/cpio/entry.rb +385 -0
- data/lib/omnizip/formats/cpio/reader.rb +196 -0
- data/lib/omnizip/formats/cpio/writer.rb +234 -0
- data/lib/omnizip/formats/cpio.rb +140 -0
- data/lib/omnizip/formats/format_spec_loader.rb +230 -0
- data/lib/omnizip/formats/gzip.rb +238 -0
- data/lib/omnizip/formats/iso/directory_builder.rb +297 -0
- data/lib/omnizip/formats/iso/directory_record.rb +152 -0
- data/lib/omnizip/formats/iso/joliet.rb +204 -0
- data/lib/omnizip/formats/iso/path_table.rb +125 -0
- data/lib/omnizip/formats/iso/reader.rb +197 -0
- data/lib/omnizip/formats/iso/rock_ridge.rb +349 -0
- data/lib/omnizip/formats/iso/volume_builder.rb +320 -0
- data/lib/omnizip/formats/iso/volume_descriptor.rb +168 -0
- data/lib/omnizip/formats/iso/writer.rb +530 -0
- data/lib/omnizip/formats/iso.rb +140 -0
- data/lib/omnizip/formats/lzip.rb +175 -0
- data/lib/omnizip/formats/lzma_alone.rb +171 -0
- data/lib/omnizip/formats/rar/archive_repairer.rb +243 -0
- data/lib/omnizip/formats/rar/archive_verifier.rb +195 -0
- data/lib/omnizip/formats/rar/block_parser.rb +243 -0
- data/lib/omnizip/formats/rar/compression/bit_stream.rb +180 -0
- data/lib/omnizip/formats/rar/compression/dispatcher.rb +217 -0
- data/lib/omnizip/formats/rar/compression/lz77_huffman/decoder.rb +216 -0
- data/lib/omnizip/formats/rar/compression/lz77_huffman/encoder.rb +158 -0
- data/lib/omnizip/formats/rar/compression/lz77_huffman/huffman_builder.rb +217 -0
- data/lib/omnizip/formats/rar/compression/lz77_huffman/huffman_coder.rb +189 -0
- data/lib/omnizip/formats/rar/compression/lz77_huffman/match_finder.rb +135 -0
- data/lib/omnizip/formats/rar/compression/lz77_huffman/sliding_window.rb +165 -0
- data/lib/omnizip/formats/rar/compression/ppmd/context.rb +105 -0
- data/lib/omnizip/formats/rar/compression/ppmd/decoder.rb +219 -0
- data/lib/omnizip/formats/rar/compression/ppmd/encoder.rb +262 -0
- data/lib/omnizip/formats/rar/compression_method_registry.rb +106 -0
- data/lib/omnizip/formats/rar/constants.rb +82 -0
- data/lib/omnizip/formats/rar/decompressor.rb +238 -0
- data/lib/omnizip/formats/rar/external_writer.rb +312 -0
- data/lib/omnizip/formats/rar/header.rb +192 -0
- data/lib/omnizip/formats/rar/license_validator.rb +109 -0
- data/lib/omnizip/formats/rar/models/rar_archive.rb +77 -0
- data/lib/omnizip/formats/rar/models/rar_entry.rb +65 -0
- data/lib/omnizip/formats/rar/models/rar_volume.rb +56 -0
- data/lib/omnizip/formats/rar/parity_handler.rb +292 -0
- data/lib/omnizip/formats/rar/rar5/compression/lzma.rb +202 -0
- data/lib/omnizip/formats/rar/rar5/compression/lzss.rb +578 -0
- data/lib/omnizip/formats/rar/rar5/compression/store.rb +60 -0
- data/lib/omnizip/formats/rar/rar5/crc32.rb +39 -0
- data/lib/omnizip/formats/rar/rar5/encryption/aes256_cbc.rb +97 -0
- data/lib/omnizip/formats/rar/rar5/encryption/encryption_header.rb +114 -0
- data/lib/omnizip/formats/rar/rar5/encryption/encryption_manager.rb +166 -0
- data/lib/omnizip/formats/rar/rar5/encryption/key_derivation.rb +97 -0
- data/lib/omnizip/formats/rar/rar5/header.rb +187 -0
- data/lib/omnizip/formats/rar/rar5/models/encryption_options.rb +74 -0
- data/lib/omnizip/formats/rar/rar5/models/recovery_options.rb +63 -0
- data/lib/omnizip/formats/rar/rar5/models/solid_options.rb +63 -0
- data/lib/omnizip/formats/rar/rar5/models/volume_options.rb +74 -0
- data/lib/omnizip/formats/rar/rar5/multi_volume/ARCHITECTURE.md +290 -0
- data/lib/omnizip/formats/rar/rar5/multi_volume/volume_manager.rb +264 -0
- data/lib/omnizip/formats/rar/rar5/multi_volume/volume_splitter.rb +155 -0
- data/lib/omnizip/formats/rar/rar5/multi_volume/volume_writer.rb +194 -0
- data/lib/omnizip/formats/rar/rar5/solid/solid_encoder.rb +109 -0
- data/lib/omnizip/formats/rar/rar5/solid/solid_manager.rb +142 -0
- data/lib/omnizip/formats/rar/rar5/solid/solid_stream.rb +121 -0
- data/lib/omnizip/formats/rar/rar5/vint.rb +65 -0
- data/lib/omnizip/formats/rar/rar5/writer.rb +466 -0
- data/lib/omnizip/formats/rar/rar_format_base.rb +241 -0
- data/lib/omnizip/formats/rar/reader.rb +366 -0
- data/lib/omnizip/formats/rar/recovery_record.rb +245 -0
- data/lib/omnizip/formats/rar/volume_manager.rb +168 -0
- data/lib/omnizip/formats/rar/writer.rb +431 -0
- data/lib/omnizip/formats/rar.rb +205 -0
- data/lib/omnizip/formats/rar3/compressor.rb +73 -0
- data/lib/omnizip/formats/rar3/decompressor.rb +66 -0
- data/lib/omnizip/formats/rar3/reader.rb +386 -0
- data/lib/omnizip/formats/rar3/writer.rb +219 -0
- data/lib/omnizip/formats/rar5/compressor.rb +73 -0
- data/lib/omnizip/formats/rar5/decompressor.rb +66 -0
- data/lib/omnizip/formats/rar5/reader.rb +342 -0
- data/lib/omnizip/formats/rar5/writer.rb +214 -0
- data/lib/omnizip/formats/seven_zip/coder_chain.rb +150 -0
- data/lib/omnizip/formats/seven_zip/constants.rb +126 -0
- data/lib/omnizip/formats/seven_zip/encoded_header.rb +114 -0
- data/lib/omnizip/formats/seven_zip/encrypted_header.rb +142 -0
- data/lib/omnizip/formats/seven_zip/file_collector.rb +144 -0
- data/lib/omnizip/formats/seven_zip/header.rb +106 -0
- data/lib/omnizip/formats/seven_zip/header_encryptor.rb +134 -0
- data/lib/omnizip/formats/seven_zip/header_writer.rb +466 -0
- data/lib/omnizip/formats/seven_zip/models/coder_info.rb +30 -0
- data/lib/omnizip/formats/seven_zip/models/file_entry.rb +58 -0
- data/lib/omnizip/formats/seven_zip/models/folder.rb +69 -0
- data/lib/omnizip/formats/seven_zip/models/stream_info.rb +42 -0
- data/lib/omnizip/formats/seven_zip/parser.rb +660 -0
- data/lib/omnizip/formats/seven_zip/reader.rb +458 -0
- data/lib/omnizip/formats/seven_zip/split_archive_reader.rb +632 -0
- data/lib/omnizip/formats/seven_zip/split_archive_writer.rb +315 -0
- data/lib/omnizip/formats/seven_zip/stream_compressor.rb +151 -0
- data/lib/omnizip/formats/seven_zip/stream_decompressor.rb +162 -0
- data/lib/omnizip/formats/seven_zip/writer.rb +740 -0
- data/lib/omnizip/formats/seven_zip.rb +93 -0
- data/lib/omnizip/formats/tar/constants.rb +73 -0
- data/lib/omnizip/formats/tar/entry.rb +94 -0
- data/lib/omnizip/formats/tar/header.rb +168 -0
- data/lib/omnizip/formats/tar/reader.rb +121 -0
- data/lib/omnizip/formats/tar/writer.rb +216 -0
- data/lib/omnizip/formats/tar.rb +84 -0
- data/lib/omnizip/formats/xz/reader.rb +116 -0
- data/lib/omnizip/formats/xz.rb +237 -0
- data/lib/omnizip/formats/xz_impl/block_decoder.rb +754 -0
- data/lib/omnizip/formats/xz_impl/block_encoder.rb +306 -0
- data/lib/omnizip/formats/xz_impl/block_header.rb +210 -0
- data/lib/omnizip/formats/xz_impl/block_header_parser.rb +186 -0
- data/lib/omnizip/formats/xz_impl/constants.rb +49 -0
- data/lib/omnizip/formats/xz_impl/index_decoder.rb +174 -0
- data/lib/omnizip/formats/xz_impl/index_encoder.rb +122 -0
- data/lib/omnizip/formats/xz_impl/stream_decoder.rb +468 -0
- data/lib/omnizip/formats/xz_impl/stream_encoder.rb +99 -0
- data/lib/omnizip/formats/xz_impl/stream_footer.rb +81 -0
- data/lib/omnizip/formats/xz_impl/stream_footer_parser.rb +117 -0
- data/lib/omnizip/formats/xz_impl/stream_header.rb +55 -0
- data/lib/omnizip/formats/xz_impl/stream_header_parser.rb +108 -0
- data/lib/omnizip/formats/xz_impl/vli.rb +128 -0
- data/lib/omnizip/formats/xz_impl/writer.rb +421 -0
- data/lib/omnizip/formats/zip/central_directory_header.rb +195 -0
- data/lib/omnizip/formats/zip/constants.rb +69 -0
- data/lib/omnizip/formats/zip/end_of_central_directory.rb +133 -0
- data/lib/omnizip/formats/zip/local_file_header.rb +138 -0
- data/lib/omnizip/formats/zip/reader.rb +250 -0
- data/lib/omnizip/formats/zip/unix_extra_field.rb +153 -0
- data/lib/omnizip/formats/zip/writer.rb +375 -0
- data/lib/omnizip/formats/zip/zip64_end_of_central_directory.rb +104 -0
- data/lib/omnizip/formats/zip/zip64_end_of_central_directory_locator.rb +66 -0
- data/lib/omnizip/formats/zip/zip64_extra_field.rb +114 -0
- data/lib/omnizip/formats/zip.rb +50 -0
- data/lib/omnizip/implementations/base/lzma2_decoder_base.rb +75 -0
- data/lib/omnizip/implementations/base/lzma2_encoder_base.rb +128 -0
- data/lib/omnizip/implementations/base/lzma_decoder_base.rb +83 -0
- data/lib/omnizip/implementations/base/lzma_encoder_base.rb +108 -0
- data/lib/omnizip/implementations/base/state_machine_base.rb +182 -0
- data/lib/omnizip/implementations/seven_zip/lzma/decoder.rb +421 -0
- data/lib/omnizip/implementations/seven_zip/lzma/encoder.rb +465 -0
- data/lib/omnizip/implementations/seven_zip/lzma/match_finder.rb +288 -0
- data/lib/omnizip/implementations/seven_zip/lzma/range_decoder.rb +200 -0
- data/lib/omnizip/implementations/seven_zip/lzma/range_encoder.rb +197 -0
- data/lib/omnizip/implementations/seven_zip/lzma/state_machine.rb +141 -0
- data/lib/omnizip/implementations/seven_zip/lzma2/encoder.rb +519 -0
- data/lib/omnizip/implementations/xz_utils/lzma2/decoder.rb +723 -0
- data/lib/omnizip/implementations/xz_utils/lzma2/encoder.rb +750 -0
- data/lib/omnizip/io/buffered_input.rb +146 -0
- data/lib/omnizip/io/buffered_output.rb +105 -0
- data/lib/omnizip/io/stream_manager.rb +115 -0
- data/lib/omnizip/link_handler/hard_link.rb +79 -0
- data/lib/omnizip/link_handler/symbolic_link.rb +74 -0
- data/lib/omnizip/link_handler.rb +124 -0
- data/lib/omnizip/metadata/archive_metadata.rb +114 -0
- data/lib/omnizip/metadata/entry_metadata.rb +146 -0
- data/lib/omnizip/metadata/metadata_editor.rb +171 -0
- data/lib/omnizip/metadata/metadata_registry.rb +64 -0
- data/lib/omnizip/metadata/metadata_validator.rb +99 -0
- data/lib/omnizip/metadata.rb +57 -0
- data/lib/omnizip/models/.keep +0 -0
- data/lib/omnizip/models/algorithm_metadata.rb +73 -0
- data/lib/omnizip/models/compression_options.rb +71 -0
- data/lib/omnizip/models/conversion_options.rb +87 -0
- data/lib/omnizip/models/conversion_result.rb +135 -0
- data/lib/omnizip/models/eta_result.rb +46 -0
- data/lib/omnizip/models/extraction_rule.rb +115 -0
- data/lib/omnizip/models/filter_chain.rb +144 -0
- data/lib/omnizip/models/filter_config.rb +183 -0
- data/lib/omnizip/models/match_result.rb +124 -0
- data/lib/omnizip/models/optimization_suggestion.rb +91 -0
- data/lib/omnizip/models/parallel_options.rb +104 -0
- data/lib/omnizip/models/performance_result.rb +79 -0
- data/lib/omnizip/models/profile_report.rb +82 -0
- data/lib/omnizip/models/progress_options.rb +38 -0
- data/lib/omnizip/models/split_options.rb +116 -0
- data/lib/omnizip/optimization_registry.rb +81 -0
- data/lib/omnizip/parallel/job_queue.rb +209 -0
- data/lib/omnizip/parallel/job_scheduler.rb +203 -0
- data/lib/omnizip/parallel/parallel_compressor.rb +347 -0
- data/lib/omnizip/parallel/parallel_extractor.rb +329 -0
- data/lib/omnizip/parallel/worker_pool.rb +223 -0
- data/lib/omnizip/parallel.rb +149 -0
- data/lib/omnizip/parity/chunked_block_processor.rb +196 -0
- data/lib/omnizip/parity/galois16.rb +145 -0
- data/lib/omnizip/parity/models/creator_packet.rb +73 -0
- data/lib/omnizip/parity/models/file_description_packet.rb +133 -0
- data/lib/omnizip/parity/models/ifsc_packet.rb +123 -0
- data/lib/omnizip/parity/models/main_packet.rb +128 -0
- data/lib/omnizip/parity/models/packet.rb +156 -0
- data/lib/omnizip/parity/models/packet_registry.rb +109 -0
- data/lib/omnizip/parity/models/recovery_slice_packet.rb +78 -0
- data/lib/omnizip/parity/par2_creator.rb +531 -0
- data/lib/omnizip/parity/par2_repairer.rb +407 -0
- data/lib/omnizip/parity/par2_verifier.rb +364 -0
- data/lib/omnizip/parity/par2cmdline_algorithm.rb +110 -0
- data/lib/omnizip/parity/par2cmdline_coefficients.rb +78 -0
- data/lib/omnizip/parity/reed_solomon_decoder.rb +266 -0
- data/lib/omnizip/parity/reed_solomon_encoder.rb +111 -0
- data/lib/omnizip/parity/reed_solomon_matrix.rb +342 -0
- data/lib/omnizip/parity.rb +186 -0
- data/lib/omnizip/password/encryption_registry.rb +65 -0
- data/lib/omnizip/password/encryption_strategy.rb +96 -0
- data/lib/omnizip/password/password_validator.rb +129 -0
- data/lib/omnizip/password/winzip_aes_strategy.rb +192 -0
- data/lib/omnizip/password/zip_crypto_strategy.rb +141 -0
- data/lib/omnizip/password.rb +87 -0
- data/lib/omnizip/pipe/stream_compressor.rb +124 -0
- data/lib/omnizip/pipe/stream_decompressor.rb +174 -0
- data/lib/omnizip/pipe.rb +121 -0
- data/lib/omnizip/platform/ntfs_streams.rb +201 -0
- data/lib/omnizip/platform.rb +189 -0
- data/lib/omnizip/profile/archive_profile.rb +39 -0
- data/lib/omnizip/profile/balanced_profile.rb +33 -0
- data/lib/omnizip/profile/binary_profile.rb +36 -0
- data/lib/omnizip/profile/compression_profile.rb +158 -0
- data/lib/omnizip/profile/custom_profile.rb +157 -0
- data/lib/omnizip/profile/fast_profile.rb +33 -0
- data/lib/omnizip/profile/maximum_profile.rb +33 -0
- data/lib/omnizip/profile/profile_detector.rb +110 -0
- data/lib/omnizip/profile/profile_registry.rb +161 -0
- data/lib/omnizip/profile/text_profile.rb +36 -0
- data/lib/omnizip/profile.rb +190 -0
- data/lib/omnizip/profiler/memory_profiler.rb +66 -0
- data/lib/omnizip/profiler/method_profiler.rb +49 -0
- data/lib/omnizip/profiler/report_generator.rb +169 -0
- data/lib/omnizip/profiler.rb +204 -0
- data/lib/omnizip/progress/callback_reporter.rb +36 -0
- data/lib/omnizip/progress/console_reporter.rb +62 -0
- data/lib/omnizip/progress/log_reporter.rb +91 -0
- data/lib/omnizip/progress/operation_progress.rb +118 -0
- data/lib/omnizip/progress/progress_bar.rb +156 -0
- data/lib/omnizip/progress/progress_reporter.rb +40 -0
- data/lib/omnizip/progress/progress_tracker.rb +190 -0
- data/lib/omnizip/progress/silent_reporter.rb +24 -0
- data/lib/omnizip/progress.rb +127 -0
- data/lib/omnizip/rubyzip_compat.rb +63 -0
- data/lib/omnizip/temp/safe_extract.rb +168 -0
- data/lib/omnizip/temp/temp_file.rb +124 -0
- data/lib/omnizip/temp/temp_file_pool.rb +109 -0
- data/lib/omnizip/temp.rb +181 -0
- data/lib/omnizip/version.rb +5 -0
- data/lib/omnizip/zip/entry.rb +156 -0
- data/lib/omnizip/zip/file.rb +485 -0
- data/lib/omnizip/zip/input_stream.rb +273 -0
- data/lib/omnizip/zip/output_stream.rb +324 -0
- data/lib/omnizip.rb +156 -0
- data/readme-docs/advanced-features.adoc +515 -0
- data/readme-docs/api-usage.adoc +444 -0
- data/readme-docs/architecture.adoc +449 -0
- data/readme-docs/archive-formats.adoc +479 -0
- data/readme-docs/cli-usage.adoc +222 -0
- data/readme-docs/compression-algorithms.adoc +442 -0
- data/readme-docs/compression-profiles.adoc +247 -0
- data/readme-docs/encryption-checksums.adoc +328 -0
- data/readme-docs/format-converter.adoc +325 -0
- data/readme-docs/installation.adoc +228 -0
- data/readme-docs/par2-archives.adoc +608 -0
- data/readme-docs/performance-profiler.adoc +389 -0
- data/readme-docs/preprocessing-filters.adoc +280 -0
- data/xz-file-format-1.2.1.txt +1174 -0
- metadata +617 -0
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "securerandom"
|
|
4
|
+
require_relative "../../../../crypto/aes256/cipher"
|
|
5
|
+
|
|
6
|
+
module Omnizip
|
|
7
|
+
module Formats
|
|
8
|
+
module Rar
|
|
9
|
+
module Rar5
|
|
10
|
+
module Encryption
|
|
11
|
+
# AES-256-CBC cipher for RAR5 encryption
|
|
12
|
+
#
|
|
13
|
+
# Wrapper around the existing Crypto::Aes256::Cipher that provides
|
|
14
|
+
# RAR5-specific encryption/decryption with proper key and IV handling.
|
|
15
|
+
#
|
|
16
|
+
# RAR5 uses:
|
|
17
|
+
# - AES-256 in CBC mode
|
|
18
|
+
# - PKCS#7 padding
|
|
19
|
+
# - Per-file IV generation
|
|
20
|
+
# - PBKDF2-HMAC-SHA256 key derivation
|
|
21
|
+
#
|
|
22
|
+
# @example Encrypt file data
|
|
23
|
+
# cipher = Aes256Cbc.new(key, iv)
|
|
24
|
+
# encrypted = cipher.encrypt(data)
|
|
25
|
+
class Aes256Cbc
|
|
26
|
+
# IV size (16 bytes = AES block size)
|
|
27
|
+
IV_SIZE = 16
|
|
28
|
+
|
|
29
|
+
# Key size (32 bytes for AES-256)
|
|
30
|
+
KEY_SIZE = 32
|
|
31
|
+
|
|
32
|
+
# @return [String] AES-256 key (32 bytes)
|
|
33
|
+
attr_reader :key
|
|
34
|
+
|
|
35
|
+
# @return [String] Initialization vector (16 bytes)
|
|
36
|
+
attr_reader :iv
|
|
37
|
+
|
|
38
|
+
# Initialize cipher with key and IV
|
|
39
|
+
#
|
|
40
|
+
# @param key [String] 32-byte AES-256 key
|
|
41
|
+
# @param iv [String] 16-byte initialization vector
|
|
42
|
+
# @raise [ArgumentError] If key or IV wrong size
|
|
43
|
+
def initialize(key, iv)
|
|
44
|
+
validate_key_iv(key, iv)
|
|
45
|
+
@key = key
|
|
46
|
+
@iv = iv
|
|
47
|
+
@cipher = Crypto::Aes256::Cipher.new(key, iv)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Encrypt data
|
|
51
|
+
#
|
|
52
|
+
# @param plaintext [String] Data to encrypt
|
|
53
|
+
# @return [String] Encrypted data (with PKCS#7 padding)
|
|
54
|
+
def encrypt(plaintext)
|
|
55
|
+
@cipher.encrypt(plaintext)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Decrypt data
|
|
59
|
+
#
|
|
60
|
+
# @param ciphertext [String] Encrypted data
|
|
61
|
+
# @return [String] Decrypted data (padding removed)
|
|
62
|
+
def decrypt(ciphertext)
|
|
63
|
+
@cipher.decrypt(ciphertext)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Generate random IV
|
|
67
|
+
#
|
|
68
|
+
# @return [String] 16-byte random IV
|
|
69
|
+
def self.generate_iv
|
|
70
|
+
SecureRandom.random_bytes(IV_SIZE)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
private
|
|
74
|
+
|
|
75
|
+
# Validate key and IV sizes
|
|
76
|
+
#
|
|
77
|
+
# @param key [String] Key to validate
|
|
78
|
+
# @param iv [String] IV to validate
|
|
79
|
+
# @return [void]
|
|
80
|
+
# @raise [ArgumentError] If key or IV wrong size
|
|
81
|
+
def validate_key_iv(key, iv)
|
|
82
|
+
if key.bytesize != KEY_SIZE
|
|
83
|
+
raise ArgumentError,
|
|
84
|
+
"Key must be #{KEY_SIZE} bytes, got #{key.bytesize}"
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
return unless iv.bytesize != IV_SIZE
|
|
88
|
+
|
|
89
|
+
raise ArgumentError,
|
|
90
|
+
"IV must be #{IV_SIZE} bytes, got #{iv.bytesize}"
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
begin
|
|
4
|
+
require "lutaml/model"
|
|
5
|
+
rescue LoadError, ArgumentError
|
|
6
|
+
# lutaml-model not available, using simple classes
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
require "base64"
|
|
10
|
+
|
|
11
|
+
module Omnizip
|
|
12
|
+
module Formats
|
|
13
|
+
module Rar
|
|
14
|
+
module Rar5
|
|
15
|
+
module Encryption
|
|
16
|
+
# RAR5 encryption header
|
|
17
|
+
#
|
|
18
|
+
# This model stores encryption parameters needed for password verification
|
|
19
|
+
# and decryption. The header is written at the beginning of encrypted
|
|
20
|
+
# archive sections.
|
|
21
|
+
#
|
|
22
|
+
# RAR5 encryption header contains:
|
|
23
|
+
# - Version (always 0 for AES-256)
|
|
24
|
+
# - KDF iteration count
|
|
25
|
+
# - Salt for key derivation
|
|
26
|
+
# - IV for AES-CBC
|
|
27
|
+
# - Check value for password verification
|
|
28
|
+
#
|
|
29
|
+
# @example Create encryption header
|
|
30
|
+
# header = EncryptionHeader.new(
|
|
31
|
+
# version: 0,
|
|
32
|
+
# kdf_iterations: 262_144,
|
|
33
|
+
# salt: salt,
|
|
34
|
+
# iv: iv,
|
|
35
|
+
# check_value: check
|
|
36
|
+
# )
|
|
37
|
+
class EncryptionHeader < Lutaml::Model::Serializable
|
|
38
|
+
# Encryption version (0 for AES-256)
|
|
39
|
+
attribute :version, :integer, default: 0
|
|
40
|
+
|
|
41
|
+
# PBKDF2 iteration count
|
|
42
|
+
attribute :kdf_iterations, :integer, default: 262_144
|
|
43
|
+
|
|
44
|
+
# Salt for key derivation (16 bytes, base64 encoded for serialization)
|
|
45
|
+
attribute :salt, :string
|
|
46
|
+
|
|
47
|
+
# Initialization vector (16 bytes, base64 encoded for serialization)
|
|
48
|
+
attribute :iv, :string
|
|
49
|
+
|
|
50
|
+
# Password check value (first 8 bytes of encrypted data)
|
|
51
|
+
# Used to verify password before full decryption
|
|
52
|
+
attribute :check_value, :string
|
|
53
|
+
|
|
54
|
+
# Validate header
|
|
55
|
+
#
|
|
56
|
+
# @raise [ArgumentError] If validation fails
|
|
57
|
+
def validate!
|
|
58
|
+
if version != 0
|
|
59
|
+
raise ArgumentError, "Only AES-256 (version 0) is supported"
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
if kdf_iterations < 65_536 || kdf_iterations > 1_048_576
|
|
63
|
+
raise ArgumentError,
|
|
64
|
+
"KDF iterations must be between 65,536 and 1,048,576"
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Validate salt (base64 decoded should be 16 bytes)
|
|
68
|
+
decoded_salt = Base64.strict_decode64(salt)
|
|
69
|
+
if decoded_salt.bytesize != 16
|
|
70
|
+
raise ArgumentError, "Salt must be 16 bytes"
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Validate IV (base64 decoded should be 16 bytes)
|
|
74
|
+
decoded_iv = Base64.strict_decode64(iv)
|
|
75
|
+
if decoded_iv.bytesize != 16
|
|
76
|
+
raise ArgumentError, "IV must be 16 bytes"
|
|
77
|
+
end
|
|
78
|
+
rescue ArgumentError => e
|
|
79
|
+
raise ArgumentError, "Invalid encryption header: #{e.message}"
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Get salt as binary
|
|
83
|
+
#
|
|
84
|
+
# @return [String] 16-byte binary salt
|
|
85
|
+
def salt_binary
|
|
86
|
+
Base64.strict_decode64(salt)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# Get IV as binary
|
|
90
|
+
#
|
|
91
|
+
# @return [String] 16-byte binary IV
|
|
92
|
+
def iv_binary
|
|
93
|
+
Base64.strict_decode64(iv)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# Set salt from binary
|
|
97
|
+
#
|
|
98
|
+
# @param binary_salt [String] 16-byte binary salt
|
|
99
|
+
def salt_binary=(binary_salt)
|
|
100
|
+
self.salt = Base64.strict_encode64(binary_salt)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# Set IV from binary
|
|
104
|
+
#
|
|
105
|
+
# @param binary_iv [String] 16-byte binary IV
|
|
106
|
+
def iv_binary=(binary_iv)
|
|
107
|
+
self.iv = Base64.strict_encode64(binary_iv)
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "key_derivation"
|
|
4
|
+
require_relative "aes256_cbc"
|
|
5
|
+
require_relative "encryption_header"
|
|
6
|
+
|
|
7
|
+
module Omnizip
|
|
8
|
+
module Formats
|
|
9
|
+
module Rar
|
|
10
|
+
module Rar5
|
|
11
|
+
module Encryption
|
|
12
|
+
# Encryption manager for RAR5 archives
|
|
13
|
+
#
|
|
14
|
+
# This manager coordinates the encryption process:
|
|
15
|
+
# 1. Generate salt and IV
|
|
16
|
+
# 2. Derive key from password using PBKDF2
|
|
17
|
+
# 3. Encrypt file data with AES-256-CBC
|
|
18
|
+
# 4. Create encryption header with metadata
|
|
19
|
+
#
|
|
20
|
+
# @example Encrypt file data
|
|
21
|
+
# manager = EncryptionManager.new("SecurePassword", kdf_iterations: 262_144)
|
|
22
|
+
# result = manager.encrypt_file_data(file_data)
|
|
23
|
+
# # result[:encrypted_data] = encrypted bytes
|
|
24
|
+
# # result[:header] = EncryptionHeader with salt, IV, etc.
|
|
25
|
+
class EncryptionManager
|
|
26
|
+
# @return [String] Password for encryption
|
|
27
|
+
attr_reader :password
|
|
28
|
+
|
|
29
|
+
# @return [Integer] PBKDF2 iteration count
|
|
30
|
+
attr_reader :kdf_iterations
|
|
31
|
+
|
|
32
|
+
# @return [String, nil] Optional pre-generated salt
|
|
33
|
+
attr_reader :salt
|
|
34
|
+
|
|
35
|
+
# @return [String, nil] Optional pre-generated IV
|
|
36
|
+
attr_reader :iv
|
|
37
|
+
|
|
38
|
+
# Initialize encryption manager
|
|
39
|
+
#
|
|
40
|
+
# @param password [String] Encryption password
|
|
41
|
+
# @param options [Hash] Options
|
|
42
|
+
# @option options [Integer] :kdf_iterations PBKDF2 iterations (default: 262,144)
|
|
43
|
+
# @option options [String] :salt Pre-generated salt (16 bytes, optional)
|
|
44
|
+
# @option options [String] :iv Pre-generated IV (16 bytes, optional)
|
|
45
|
+
def initialize(password, options = {})
|
|
46
|
+
@password = password
|
|
47
|
+
@kdf_iterations = options[:kdf_iterations] || KeyDerivation::DEFAULT_ITERATIONS
|
|
48
|
+
@salt = options[:salt]
|
|
49
|
+
@iv = options[:iv]
|
|
50
|
+
|
|
51
|
+
validate_password!
|
|
52
|
+
validate_iterations!
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Encrypt file data
|
|
56
|
+
#
|
|
57
|
+
# @param plaintext [String] File data to encrypt
|
|
58
|
+
# @return [Hash] Encryption result
|
|
59
|
+
# @option result [String] :encrypted_data Encrypted bytes
|
|
60
|
+
# @option result [EncryptionHeader] :header Encryption metadata
|
|
61
|
+
# @option result [String] :key Derived encryption key (for debugging)
|
|
62
|
+
def encrypt_file_data(plaintext)
|
|
63
|
+
# Generate or use provided salt and IV
|
|
64
|
+
salt = @salt || KeyDerivation.generate_salt
|
|
65
|
+
iv = @iv || Aes256Cbc.generate_iv
|
|
66
|
+
|
|
67
|
+
# Derive key from password
|
|
68
|
+
key = KeyDerivation.derive_key(@password, salt, @kdf_iterations)
|
|
69
|
+
|
|
70
|
+
# Encrypt data
|
|
71
|
+
cipher = Aes256Cbc.new(key, iv)
|
|
72
|
+
encrypted = cipher.encrypt(plaintext)
|
|
73
|
+
|
|
74
|
+
# Create encryption header
|
|
75
|
+
header = create_encryption_header(salt, iv)
|
|
76
|
+
|
|
77
|
+
{
|
|
78
|
+
encrypted_data: encrypted,
|
|
79
|
+
header: header,
|
|
80
|
+
key: key, # Include for verification if needed
|
|
81
|
+
}
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Decrypt file data
|
|
85
|
+
#
|
|
86
|
+
# @param ciphertext [String] Encrypted data
|
|
87
|
+
# @param header [EncryptionHeader] Encryption metadata
|
|
88
|
+
# @return [String] Decrypted data
|
|
89
|
+
# @raise [ArgumentError] If password incorrect
|
|
90
|
+
def decrypt_file_data(ciphertext, header)
|
|
91
|
+
# Extract salt and IV
|
|
92
|
+
salt = header.salt_binary
|
|
93
|
+
iv = header.iv_binary
|
|
94
|
+
|
|
95
|
+
# Derive key from password
|
|
96
|
+
key = KeyDerivation.derive_key(@password, salt,
|
|
97
|
+
header.kdf_iterations)
|
|
98
|
+
|
|
99
|
+
# Decrypt data
|
|
100
|
+
cipher = Aes256Cbc.new(key, iv)
|
|
101
|
+
cipher.decrypt(ciphertext)
|
|
102
|
+
rescue OpenSSL::Cipher::CipherError => e
|
|
103
|
+
raise ArgumentError,
|
|
104
|
+
"Decryption failed (wrong password?): #{e.message}"
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# Verify password without full decryption
|
|
108
|
+
#
|
|
109
|
+
# This checks if the derived key can decrypt the check value.
|
|
110
|
+
# Faster than decrypting entire file.
|
|
111
|
+
#
|
|
112
|
+
# @param header [EncryptionHeader] Encryption metadata
|
|
113
|
+
# @return [Boolean] true if password correct
|
|
114
|
+
def verify_password(_header)
|
|
115
|
+
# For now, we'll need a small encrypted sample to verify
|
|
116
|
+
# This is a simplified check - full implementation would use
|
|
117
|
+
# the check_value field properly
|
|
118
|
+
true # Placeholder
|
|
119
|
+
rescue StandardError
|
|
120
|
+
false
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
private
|
|
124
|
+
|
|
125
|
+
# Create encryption header
|
|
126
|
+
#
|
|
127
|
+
# @param salt [String] 16-byte salt
|
|
128
|
+
# @param iv [String] 16-byte IV
|
|
129
|
+
# @return [EncryptionHeader] Header object
|
|
130
|
+
def create_encryption_header(salt, iv)
|
|
131
|
+
header = EncryptionHeader.new
|
|
132
|
+
header.version = 0 # AES-256
|
|
133
|
+
header.kdf_iterations = @kdf_iterations
|
|
134
|
+
header.salt_binary = salt
|
|
135
|
+
header.iv_binary = iv
|
|
136
|
+
header.check_value = "" # Placeholder for password check
|
|
137
|
+
header
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# Validate password
|
|
141
|
+
#
|
|
142
|
+
# @raise [ArgumentError] If password invalid
|
|
143
|
+
def validate_password!
|
|
144
|
+
if @password.nil? || @password.empty?
|
|
145
|
+
raise ArgumentError, "Password cannot be empty"
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# Validate iteration count
|
|
150
|
+
#
|
|
151
|
+
# @raise [ArgumentError] If iterations invalid
|
|
152
|
+
def validate_iterations!
|
|
153
|
+
min = KeyDerivation::MIN_ITERATIONS
|
|
154
|
+
max = KeyDerivation::MAX_ITERATIONS
|
|
155
|
+
|
|
156
|
+
return if @kdf_iterations.between?(min, max)
|
|
157
|
+
|
|
158
|
+
raise ArgumentError,
|
|
159
|
+
"KDF iterations must be between #{min} and #{max}"
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
end
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "openssl"
|
|
4
|
+
require "securerandom"
|
|
5
|
+
|
|
6
|
+
module Omnizip
|
|
7
|
+
module Formats
|
|
8
|
+
module Rar
|
|
9
|
+
module Rar5
|
|
10
|
+
module Encryption
|
|
11
|
+
# RAR5 password-based key derivation
|
|
12
|
+
#
|
|
13
|
+
# RAR5 uses PBKDF2-HMAC-SHA256 for key derivation, which is more
|
|
14
|
+
# secure than 7-Zip's iterative SHA-256 approach.
|
|
15
|
+
#
|
|
16
|
+
# The process:
|
|
17
|
+
# 1. Generate random salt (16 bytes)
|
|
18
|
+
# 2. Apply PBKDF2-HMAC-SHA256 with configurable iterations
|
|
19
|
+
# 3. Derive 32-byte AES-256 key
|
|
20
|
+
#
|
|
21
|
+
# @example Derive key from password
|
|
22
|
+
# salt = SecureRandom.random_bytes(16)
|
|
23
|
+
# key = KeyDerivation.derive_key("password", salt, 262_144)
|
|
24
|
+
class KeyDerivation
|
|
25
|
+
# Default PBKDF2 iterations (262,144 = 2^18)
|
|
26
|
+
# This provides good security while maintaining reasonable performance
|
|
27
|
+
DEFAULT_ITERATIONS = 262_144
|
|
28
|
+
|
|
29
|
+
# Minimum iterations (2^16 = 65,536)
|
|
30
|
+
MIN_ITERATIONS = 65_536
|
|
31
|
+
|
|
32
|
+
# Maximum iterations (2^20 = 1,048,576)
|
|
33
|
+
MAX_ITERATIONS = 1_048_576
|
|
34
|
+
|
|
35
|
+
# Salt size (16 bytes)
|
|
36
|
+
SALT_SIZE = 16
|
|
37
|
+
|
|
38
|
+
# Key size (32 bytes for AES-256)
|
|
39
|
+
KEY_SIZE = 32
|
|
40
|
+
|
|
41
|
+
# Derive AES-256 key from password using PBKDF2-HMAC-SHA256
|
|
42
|
+
#
|
|
43
|
+
# @param password [String] User password
|
|
44
|
+
# @param salt [String] Random salt (16 bytes)
|
|
45
|
+
# @param iterations [Integer] Number of PBKDF2 iterations
|
|
46
|
+
# @return [String] 32-byte AES-256 key
|
|
47
|
+
# @raise [ArgumentError] If password empty or salt wrong size
|
|
48
|
+
def self.derive_key(password, salt, iterations = DEFAULT_ITERATIONS)
|
|
49
|
+
validate_inputs(password, salt, iterations)
|
|
50
|
+
|
|
51
|
+
# PBKDF2-HMAC-SHA256
|
|
52
|
+
OpenSSL::PKCS5.pbkdf2_hmac(
|
|
53
|
+
password,
|
|
54
|
+
salt,
|
|
55
|
+
iterations,
|
|
56
|
+
KEY_SIZE,
|
|
57
|
+
OpenSSL::Digest.new("SHA256"),
|
|
58
|
+
)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Generate random salt
|
|
62
|
+
#
|
|
63
|
+
# @return [String] 16-byte random salt
|
|
64
|
+
def self.generate_salt
|
|
65
|
+
SecureRandom.random_bytes(SALT_SIZE)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Validate key derivation inputs
|
|
69
|
+
#
|
|
70
|
+
# @param password [String] Password to validate
|
|
71
|
+
# @param salt [String] Salt to validate
|
|
72
|
+
# @param iterations [Integer] Iteration count to validate
|
|
73
|
+
# @return [void]
|
|
74
|
+
# @raise [ArgumentError] If inputs are invalid
|
|
75
|
+
def self.validate_inputs(password, salt, iterations)
|
|
76
|
+
if password.nil? || password.empty?
|
|
77
|
+
raise ArgumentError, "Password cannot be empty"
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
if salt.bytesize != SALT_SIZE
|
|
81
|
+
raise ArgumentError,
|
|
82
|
+
"Salt must be #{SALT_SIZE} bytes, got #{salt.bytesize}"
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
return if iterations.between?(MIN_ITERATIONS, MAX_ITERATIONS)
|
|
86
|
+
|
|
87
|
+
raise ArgumentError,
|
|
88
|
+
"Iterations must be between #{MIN_ITERATIONS} and #{MAX_ITERATIONS}"
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
private_class_method :validate_inputs
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "vint"
|
|
4
|
+
require_relative "crc32"
|
|
5
|
+
|
|
6
|
+
module Omnizip
|
|
7
|
+
module Formats
|
|
8
|
+
module Rar
|
|
9
|
+
module Rar5
|
|
10
|
+
# Header types
|
|
11
|
+
HEADER_TYPE_MAIN = 1
|
|
12
|
+
HEADER_TYPE_FILE = 2
|
|
13
|
+
HEADER_TYPE_SERVICE = 3
|
|
14
|
+
HEADER_TYPE_ENCRYPTION = 4
|
|
15
|
+
HEADER_TYPE_END = 5
|
|
16
|
+
|
|
17
|
+
# Header flags (common)
|
|
18
|
+
FLAG_EXTRA_AREA = 0x0001
|
|
19
|
+
FLAG_DATA_AREA = 0x0002
|
|
20
|
+
|
|
21
|
+
# Base class for RAR5 headers
|
|
22
|
+
class Header
|
|
23
|
+
attr_reader :type, :flags, :extra_area, :data_area_size, :header_data
|
|
24
|
+
|
|
25
|
+
def initialize(type, flags: 0, extra_area: nil, data_area_size: nil,
|
|
26
|
+
header_data: "")
|
|
27
|
+
@type = type
|
|
28
|
+
@flags = flags
|
|
29
|
+
@flags |= FLAG_EXTRA_AREA if extra_area
|
|
30
|
+
@flags |= FLAG_DATA_AREA if data_area_size
|
|
31
|
+
@extra_area = extra_area
|
|
32
|
+
@data_area_size = data_area_size
|
|
33
|
+
@header_data = header_data
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def encode
|
|
37
|
+
# Build header without CRC
|
|
38
|
+
header_bytes = build_header_bytes
|
|
39
|
+
|
|
40
|
+
# Calculate CRC32
|
|
41
|
+
crc = CRC32.calculate(header_bytes.pack("C*"))
|
|
42
|
+
|
|
43
|
+
# Prepend CRC (little-endian)
|
|
44
|
+
[crc].pack("V") + header_bytes.pack("C*")
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
private
|
|
48
|
+
|
|
49
|
+
def build_header_bytes
|
|
50
|
+
bytes = []
|
|
51
|
+
|
|
52
|
+
# Header size (placeholder - will calculate)
|
|
53
|
+
size_bytes = []
|
|
54
|
+
|
|
55
|
+
# Type
|
|
56
|
+
type_bytes = VINT.encode(@type)
|
|
57
|
+
size_bytes.concat(type_bytes)
|
|
58
|
+
|
|
59
|
+
# Flags
|
|
60
|
+
flags_bytes = VINT.encode(@flags)
|
|
61
|
+
size_bytes.concat(flags_bytes)
|
|
62
|
+
|
|
63
|
+
# Extra area size (if present)
|
|
64
|
+
if @flags.anybits?(FLAG_EXTRA_AREA)
|
|
65
|
+
extra_size_bytes = VINT.encode(@extra_area.bytesize)
|
|
66
|
+
size_bytes.concat(extra_size_bytes)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Data area size (if present)
|
|
70
|
+
if @flags.anybits?(FLAG_DATA_AREA)
|
|
71
|
+
data_size_bytes = VINT.encode(@data_area_size)
|
|
72
|
+
size_bytes.concat(data_size_bytes)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Header data
|
|
76
|
+
size_bytes.concat(@header_data.bytes)
|
|
77
|
+
|
|
78
|
+
# Extra area
|
|
79
|
+
size_bytes.concat(@extra_area.bytes) if @extra_area
|
|
80
|
+
|
|
81
|
+
# Calculate total header size (excluding CRC)
|
|
82
|
+
header_size = size_bytes.size
|
|
83
|
+
header_size_vint = VINT.encode(header_size)
|
|
84
|
+
|
|
85
|
+
# Build final header
|
|
86
|
+
bytes.concat(header_size_vint)
|
|
87
|
+
bytes.concat(size_bytes)
|
|
88
|
+
|
|
89
|
+
bytes
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# Main archive header
|
|
94
|
+
class MainHeader < Header
|
|
95
|
+
def initialize(flags: 0)
|
|
96
|
+
# Main header has no data
|
|
97
|
+
super(HEADER_TYPE_MAIN, flags: flags)
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# File header
|
|
102
|
+
class FileHeader < Header
|
|
103
|
+
# File header flags
|
|
104
|
+
FILE_HAS_ATTRIBUTES = 0x0001
|
|
105
|
+
FILE_HAS_MTIME = 0x0002
|
|
106
|
+
FILE_HAS_CRC32 = 0x0004
|
|
107
|
+
|
|
108
|
+
def initialize(filename:, file_size:, compressed_size:,
|
|
109
|
+
compression_method: 0, flags: 0, mtime: nil, crc32: nil, extra_area: nil)
|
|
110
|
+
# Build file flags based on what's provided
|
|
111
|
+
file_flags = 0
|
|
112
|
+
file_flags |= FILE_HAS_MTIME if mtime
|
|
113
|
+
file_flags |= FILE_HAS_CRC32 if crc32
|
|
114
|
+
|
|
115
|
+
# Build header data with file information
|
|
116
|
+
data = build_file_data(filename, file_size, compressed_size,
|
|
117
|
+
file_flags, compression_method, mtime, crc32)
|
|
118
|
+
super(HEADER_TYPE_FILE, flags: flags, data_area_size: compressed_size, header_data: data, extra_area: extra_area)
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
private
|
|
122
|
+
|
|
123
|
+
def build_file_data(filename, file_size, _compressed_size,
|
|
124
|
+
file_flags, compression_method, mtime, crc32)
|
|
125
|
+
data = []
|
|
126
|
+
|
|
127
|
+
# File flags (VINT)
|
|
128
|
+
data.concat(VINT.encode(file_flags))
|
|
129
|
+
|
|
130
|
+
# Unp size (uncompressed size, VINT)
|
|
131
|
+
data.concat(VINT.encode(file_size))
|
|
132
|
+
|
|
133
|
+
# Attributes (VINT) - ALWAYS present in RAR5
|
|
134
|
+
# Use 0x2483 from official RAR (standard regular file with correct permissions)
|
|
135
|
+
data.concat(VINT.encode(0x2483))
|
|
136
|
+
|
|
137
|
+
# Mystery VINT with value 0x02 - observed in official RAR
|
|
138
|
+
# This appears after attributes in official archives - ALWAYS present
|
|
139
|
+
data.concat(VINT.encode(0x02))
|
|
140
|
+
|
|
141
|
+
# mtime (optional) - only if FILE_HAS_MTIME flag is set
|
|
142
|
+
# RAR5 stores mtime as Unix timestamp (seconds since epoch) in DOS format
|
|
143
|
+
# Format: 4 bytes little-endian (NOT a VINT)
|
|
144
|
+
if file_flags.anybits?(FILE_HAS_MTIME) && mtime
|
|
145
|
+
# Convert Time to Unix timestamp (seconds since 1970-01-01 00:00:00 UTC)
|
|
146
|
+
unix_time = mtime.to_i
|
|
147
|
+
# Pack as 32-bit unsigned little-endian
|
|
148
|
+
data.concat([unix_time].pack("V").bytes)
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# Data CRC32 (optional) - only if FILE_HAS_CRC32 flag is set
|
|
152
|
+
# Format: 4 bytes little-endian (NOT a VINT)
|
|
153
|
+
if file_flags.anybits?(FILE_HAS_CRC32) && crc32
|
|
154
|
+
# Pack as 32-bit unsigned little-endian
|
|
155
|
+
data.concat([crc32].pack("V").bytes)
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
# Compression info (VINT)
|
|
159
|
+
# Bits 0-5: method (0=STORE, 1-5=LZMA with different levels)
|
|
160
|
+
# Bits 6+: version
|
|
161
|
+
data.concat(VINT.encode(compression_method))
|
|
162
|
+
|
|
163
|
+
# Host OS (VINT) - 1 = Unix
|
|
164
|
+
data.concat(VINT.encode(1)) # Unix
|
|
165
|
+
|
|
166
|
+
# Name length (VINT)
|
|
167
|
+
name_bytes = filename.encode("UTF-8").bytes
|
|
168
|
+
data.concat(VINT.encode(name_bytes.size))
|
|
169
|
+
|
|
170
|
+
# Name
|
|
171
|
+
data.concat(name_bytes)
|
|
172
|
+
|
|
173
|
+
data.pack("C*")
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
# End of archive header
|
|
178
|
+
class EndHeader < Header
|
|
179
|
+
def initialize
|
|
180
|
+
# End header is minimal
|
|
181
|
+
super(HEADER_TYPE_END, flags: 0)
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
end
|