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,129 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Omnizip
|
|
4
|
+
module Password
|
|
5
|
+
# Validates password strength and requirements
|
|
6
|
+
class PasswordValidator
|
|
7
|
+
attr_reader :min_length, :require_uppercase, :require_lowercase,
|
|
8
|
+
:require_numbers, :require_special
|
|
9
|
+
|
|
10
|
+
# Initialize password validator
|
|
11
|
+
# @param min_length [Integer] Minimum password length
|
|
12
|
+
# @param require_uppercase [Boolean] Require uppercase letters
|
|
13
|
+
# @param require_lowercase [Boolean] Require lowercase letters
|
|
14
|
+
# @param require_numbers [Boolean] Require numbers
|
|
15
|
+
# @param require_special [Boolean] Require special characters
|
|
16
|
+
def initialize(
|
|
17
|
+
min_length: 8,
|
|
18
|
+
require_uppercase: false,
|
|
19
|
+
require_lowercase: false,
|
|
20
|
+
require_numbers: false,
|
|
21
|
+
require_special: false
|
|
22
|
+
)
|
|
23
|
+
@min_length = min_length
|
|
24
|
+
@require_uppercase = require_uppercase
|
|
25
|
+
@require_lowercase = require_lowercase
|
|
26
|
+
@require_numbers = require_numbers
|
|
27
|
+
@require_special = require_special
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Validate password
|
|
31
|
+
# @param password [String] Password to validate
|
|
32
|
+
# @return [Boolean] True if valid
|
|
33
|
+
# @raise [ArgumentError] If password is invalid
|
|
34
|
+
def validate(password)
|
|
35
|
+
raise ArgumentError, "Password cannot be nil" if password.nil?
|
|
36
|
+
raise ArgumentError, "Password cannot be empty" if password.empty?
|
|
37
|
+
|
|
38
|
+
check_length(password)
|
|
39
|
+
check_uppercase(password) if require_uppercase
|
|
40
|
+
check_lowercase(password) if require_lowercase
|
|
41
|
+
check_numbers(password) if require_numbers
|
|
42
|
+
check_special(password) if require_special
|
|
43
|
+
|
|
44
|
+
true
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Check if password is valid (without raising)
|
|
48
|
+
# @param password [String] Password to check
|
|
49
|
+
# @return [Boolean] True if valid
|
|
50
|
+
def valid?(password)
|
|
51
|
+
validate(password)
|
|
52
|
+
true
|
|
53
|
+
rescue ArgumentError
|
|
54
|
+
false
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Get password strength score (0-100)
|
|
58
|
+
# @param password [String] Password to score
|
|
59
|
+
# @return [Integer] Strength score
|
|
60
|
+
def strength(password)
|
|
61
|
+
return 0 if password.nil? || password.empty?
|
|
62
|
+
|
|
63
|
+
score = 0
|
|
64
|
+
|
|
65
|
+
# Length score (up to 40 points)
|
|
66
|
+
score += [password.length * 4, 40].min
|
|
67
|
+
|
|
68
|
+
# Character variety (up to 60 points)
|
|
69
|
+
score += 15 if /[a-z]/.match?(password)
|
|
70
|
+
score += 15 if /[A-Z]/.match?(password)
|
|
71
|
+
score += 15 if /[0-9]/.match?(password)
|
|
72
|
+
score += 15 if /[^a-zA-Z0-9]/.match?(password)
|
|
73
|
+
|
|
74
|
+
[score, 100].min
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Get password strength label
|
|
78
|
+
# @param password [String] Password to evaluate
|
|
79
|
+
# @return [Symbol] Strength label (:weak, :fair, :good, :strong)
|
|
80
|
+
def strength_label(password)
|
|
81
|
+
score = strength(password)
|
|
82
|
+
|
|
83
|
+
case score
|
|
84
|
+
when 0...30
|
|
85
|
+
:weak
|
|
86
|
+
when 30...50
|
|
87
|
+
:fair
|
|
88
|
+
when 50...75
|
|
89
|
+
:good
|
|
90
|
+
else
|
|
91
|
+
:strong
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
private
|
|
96
|
+
|
|
97
|
+
def check_length(password)
|
|
98
|
+
return if password.length >= min_length
|
|
99
|
+
|
|
100
|
+
raise ArgumentError,
|
|
101
|
+
"Password too short (minimum: #{min_length} characters)"
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def check_uppercase(password)
|
|
105
|
+
return if /[A-Z]/.match?(password)
|
|
106
|
+
|
|
107
|
+
raise ArgumentError, "Password must contain uppercase letters"
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def check_lowercase(password)
|
|
111
|
+
return if /[a-z]/.match?(password)
|
|
112
|
+
|
|
113
|
+
raise ArgumentError, "Password must contain lowercase letters"
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def check_numbers(password)
|
|
117
|
+
return if /[0-9]/.match?(password)
|
|
118
|
+
|
|
119
|
+
raise ArgumentError, "Password must contain numbers"
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def check_special(password)
|
|
123
|
+
return if /[^a-zA-Z0-9]/.match?(password)
|
|
124
|
+
|
|
125
|
+
raise ArgumentError, "Password must contain special characters"
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
end
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "encryption_strategy"
|
|
4
|
+
|
|
5
|
+
module Omnizip
|
|
6
|
+
module Password
|
|
7
|
+
# WinZip AES encryption (recommended)
|
|
8
|
+
# Implements WinZip AES-256 encryption standard
|
|
9
|
+
class WinzipAesStrategy < EncryptionStrategy
|
|
10
|
+
# WinZip AES compression method
|
|
11
|
+
COMPRESSION_METHOD = 99 # AES encryption
|
|
12
|
+
|
|
13
|
+
# AES key sizes
|
|
14
|
+
KEY_SIZE_128 = 1
|
|
15
|
+
KEY_SIZE_192 = 2
|
|
16
|
+
KEY_SIZE_256 = 3
|
|
17
|
+
|
|
18
|
+
# Extra field header IDs
|
|
19
|
+
WINZIP_AES_EXTRA_ID = 0x9901
|
|
20
|
+
|
|
21
|
+
attr_reader :key_size, :actual_compression_method
|
|
22
|
+
|
|
23
|
+
# Initialize WinZip AES encryption
|
|
24
|
+
# @param password [String] Password to use
|
|
25
|
+
# @param key_size [Integer] Key size (128, 192, or 256 bits)
|
|
26
|
+
# @param compression_method [Integer] Actual compression method to use
|
|
27
|
+
def initialize(password, key_size: 256, compression_method: 8)
|
|
28
|
+
super(password)
|
|
29
|
+
@key_size = validate_key_size(key_size)
|
|
30
|
+
@actual_compression_method = compression_method
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Encrypt data using WinZip AES
|
|
34
|
+
# @param data [String] Data to encrypt
|
|
35
|
+
# @return [String] Encrypted data with authentication
|
|
36
|
+
def encrypt(data)
|
|
37
|
+
require "openssl"
|
|
38
|
+
|
|
39
|
+
# Generate salt
|
|
40
|
+
salt = generate_salt
|
|
41
|
+
|
|
42
|
+
# Derive keys
|
|
43
|
+
encryption_key, password_verify, hmac_key = derive_winzip_keys(salt)
|
|
44
|
+
|
|
45
|
+
# Create cipher
|
|
46
|
+
cipher = OpenSSL::Cipher.new("AES-#{key_bits}-CTR")
|
|
47
|
+
cipher.encrypt
|
|
48
|
+
cipher.key = encryption_key
|
|
49
|
+
cipher.iv = "\x00" * 16 # WinZip AES uses zero IV
|
|
50
|
+
|
|
51
|
+
# Encrypt data
|
|
52
|
+
encrypted = cipher.update(data) + cipher.final
|
|
53
|
+
|
|
54
|
+
# Calculate HMAC
|
|
55
|
+
hmac = calculate_hmac(hmac_key, encrypted)
|
|
56
|
+
|
|
57
|
+
# Return: salt + password_verify + encrypted_data + hmac
|
|
58
|
+
salt + password_verify + encrypted + hmac
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Decrypt data using WinZip AES
|
|
62
|
+
# @param data [String] Encrypted data
|
|
63
|
+
# @return [String] Decrypted data
|
|
64
|
+
def decrypt(data)
|
|
65
|
+
require "openssl"
|
|
66
|
+
|
|
67
|
+
# Extract components
|
|
68
|
+
salt_size = salt_length
|
|
69
|
+
salt = data[0, salt_size]
|
|
70
|
+
password_verify_bytes = data[salt_size, 2]
|
|
71
|
+
hmac_size = 10
|
|
72
|
+
encrypted_data = data[(salt_size + 2)...-hmac_size]
|
|
73
|
+
stored_hmac = data[-hmac_size..]
|
|
74
|
+
|
|
75
|
+
# Derive keys
|
|
76
|
+
encryption_key, expected_verify, hmac_key = derive_winzip_keys(salt)
|
|
77
|
+
|
|
78
|
+
# Verify password
|
|
79
|
+
unless password_verify_bytes == expected_verify
|
|
80
|
+
raise Omnizip::PasswordError, "Incorrect password"
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Verify HMAC
|
|
84
|
+
calculated_hmac = calculate_hmac(hmac_key, encrypted_data)
|
|
85
|
+
unless stored_hmac == calculated_hmac
|
|
86
|
+
raise Omnizip::PasswordError, "Data integrity check failed"
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# Decrypt data
|
|
90
|
+
cipher = OpenSSL::Cipher.new("AES-#{key_bits}-CTR")
|
|
91
|
+
cipher.decrypt
|
|
92
|
+
cipher.key = encryption_key
|
|
93
|
+
cipher.iv = "\x00" * 16
|
|
94
|
+
|
|
95
|
+
cipher.update(encrypted_data) + cipher.final
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# Get compression method for ZIP header
|
|
99
|
+
# @return [Integer] Compression method ID (99 for AES)
|
|
100
|
+
def compression_method
|
|
101
|
+
COMPRESSION_METHOD
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# Get extra field data for ZIP central directory
|
|
105
|
+
# @return [String] Extra field data
|
|
106
|
+
def extra_field_data
|
|
107
|
+
# WinZip AES extra field format:
|
|
108
|
+
# 2 bytes: extra field ID (0x9901)
|
|
109
|
+
# 2 bytes: data size
|
|
110
|
+
# 2 bytes: AES version (0x0001 or 0x0002)
|
|
111
|
+
# 2 bytes: vendor ID (0x4145 = "AE")
|
|
112
|
+
# 1 byte: AES strength (1=128, 2=192, 3=256)
|
|
113
|
+
# 2 bytes: actual compression method
|
|
114
|
+
|
|
115
|
+
data = [
|
|
116
|
+
0x0002, # AES version 2
|
|
117
|
+
0x4145, # Vendor ID "AE"
|
|
118
|
+
key_size_code,
|
|
119
|
+
actual_compression_method,
|
|
120
|
+
].pack("vvCv")
|
|
121
|
+
|
|
122
|
+
[WINZIP_AES_EXTRA_ID, data.bytesize].pack("vv") + data
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# Get encryption flags
|
|
126
|
+
# @return [Integer] Encryption flags
|
|
127
|
+
def encryption_flags
|
|
128
|
+
0x0001 | 0x0040 # Encrypted + strong encryption
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
private
|
|
132
|
+
|
|
133
|
+
def key_bits
|
|
134
|
+
case @key_size
|
|
135
|
+
when KEY_SIZE_128 then 128
|
|
136
|
+
when KEY_SIZE_192 then 192
|
|
137
|
+
when KEY_SIZE_256 then 256
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def key_size_code
|
|
142
|
+
@key_size
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def salt_length
|
|
146
|
+
key_bits / 16 # Salt length in bytes
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def validate_key_size(size)
|
|
150
|
+
case size
|
|
151
|
+
when 128 then KEY_SIZE_128
|
|
152
|
+
when 192 then KEY_SIZE_192
|
|
153
|
+
when 256 then KEY_SIZE_256
|
|
154
|
+
else
|
|
155
|
+
raise ArgumentError,
|
|
156
|
+
"Invalid key size: #{size}. Must be 128, 192, or 256"
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def generate_salt
|
|
161
|
+
require "securerandom"
|
|
162
|
+
SecureRandom.random_bytes(salt_length)
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
def derive_winzip_keys(salt)
|
|
166
|
+
require "openssl"
|
|
167
|
+
|
|
168
|
+
# WinZip uses PBKDF2 with HMAC-SHA1
|
|
169
|
+
key_material = OpenSSL::PKCS5.pbkdf2_hmac(
|
|
170
|
+
password,
|
|
171
|
+
salt,
|
|
172
|
+
1000, # iterations
|
|
173
|
+
(2 * key_bits / 8) + 2, # key + hmac + verify
|
|
174
|
+
OpenSSL::Digest.new("SHA1"),
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
key_size_bytes = key_bits / 8
|
|
178
|
+
|
|
179
|
+
encryption_key = key_material[0, key_size_bytes]
|
|
180
|
+
password_verify = key_material[key_size_bytes, 2]
|
|
181
|
+
hmac_key = key_material[(key_size_bytes + 2), key_size_bytes]
|
|
182
|
+
|
|
183
|
+
[encryption_key, password_verify, hmac_key]
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
def calculate_hmac(key, data)
|
|
187
|
+
require "openssl"
|
|
188
|
+
OpenSSL::HMAC.digest(OpenSSL::Digest.new("SHA1"), key, data)[0, 10]
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
end
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "encryption_strategy"
|
|
4
|
+
|
|
5
|
+
module Omnizip
|
|
6
|
+
module Password
|
|
7
|
+
# Traditional ZIP encryption (PKWARE)
|
|
8
|
+
# WARNING: This is a weak encryption method and should not be used
|
|
9
|
+
# for sensitive data. Use WinZipAesStrategy instead.
|
|
10
|
+
class ZipCryptoStrategy < EncryptionStrategy
|
|
11
|
+
# ZIP compression method for traditional encryption
|
|
12
|
+
COMPRESSION_METHOD = 0 # Stored (encryption is separate)
|
|
13
|
+
|
|
14
|
+
# Initialize ZIP crypto encryption
|
|
15
|
+
# @param password [String] Password to use
|
|
16
|
+
# @param warn_weak [Boolean] Show security warning (default: true)
|
|
17
|
+
def initialize(password, warn_weak: true)
|
|
18
|
+
super(password)
|
|
19
|
+
|
|
20
|
+
if warn_weak
|
|
21
|
+
warn "WARNING: Traditional ZIP encryption is weak and easily cracked."
|
|
22
|
+
warn "Consider using WinZip AES encryption instead for better security."
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Encrypt data using traditional ZIP encryption
|
|
27
|
+
# @param data [String] Data to encrypt
|
|
28
|
+
# @return [String] Encrypted data
|
|
29
|
+
def encrypt(data)
|
|
30
|
+
keys = initialize_keys
|
|
31
|
+
|
|
32
|
+
# Generate encryption header
|
|
33
|
+
header = generate_encryption_header(keys)
|
|
34
|
+
|
|
35
|
+
# Encrypt data with same keys (header generation updated them)
|
|
36
|
+
encrypted = data.bytes.map do |byte|
|
|
37
|
+
temp = decrypt_byte(keys)
|
|
38
|
+
update_keys(keys, byte)
|
|
39
|
+
byte ^ temp
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
(header + encrypted).pack("C*")
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Decrypt data using traditional ZIP encryption
|
|
46
|
+
# @param data [String] Data to decrypt
|
|
47
|
+
# @return [String] Decrypted data
|
|
48
|
+
def decrypt(data)
|
|
49
|
+
bytes = data.bytes
|
|
50
|
+
keys = initialize_keys
|
|
51
|
+
|
|
52
|
+
# Skip encryption header (12 bytes)
|
|
53
|
+
header = bytes[0...12]
|
|
54
|
+
encrypted_data = bytes[12..]
|
|
55
|
+
|
|
56
|
+
# Verify header
|
|
57
|
+
header.each { |byte| update_keys(keys, decrypt_byte(keys) ^ byte) }
|
|
58
|
+
|
|
59
|
+
# Decrypt data
|
|
60
|
+
decrypted = encrypted_data.map do |byte|
|
|
61
|
+
temp = decrypt_byte(keys)
|
|
62
|
+
plaintext_byte = byte ^ temp
|
|
63
|
+
update_keys(keys, plaintext_byte)
|
|
64
|
+
plaintext_byte
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
decrypted.pack("C*")
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Get compression method for ZIP header
|
|
71
|
+
# @return [Integer] Compression method ID
|
|
72
|
+
def compression_method
|
|
73
|
+
COMPRESSION_METHOD
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Get encryption flags
|
|
77
|
+
# @return [Integer] Encryption flags (bit 0 set)
|
|
78
|
+
def encryption_flags
|
|
79
|
+
0x0001 # Traditional encryption
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
private
|
|
83
|
+
|
|
84
|
+
# Initialize encryption keys
|
|
85
|
+
# @return [Array<Integer>] Initial key values
|
|
86
|
+
def initialize_keys
|
|
87
|
+
keys = [0x12345678, 0x23456789, 0x34567890]
|
|
88
|
+
|
|
89
|
+
password.each_byte do |byte|
|
|
90
|
+
update_keys(keys, byte)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
keys
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# Update encryption keys
|
|
97
|
+
# @param keys [Array<Integer>] Current keys
|
|
98
|
+
# @param byte [Integer] Byte to incorporate
|
|
99
|
+
def update_keys(keys, byte)
|
|
100
|
+
keys[0] = crc32_update(keys[0], byte)
|
|
101
|
+
keys[1] =
|
|
102
|
+
(((keys[1] + (keys[0] & 0xFF)) * 134_775_813) + 1) & 0xFFFFFFFF
|
|
103
|
+
keys[2] = crc32_update(keys[2], keys[1] >> 24)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# Decrypt a single byte
|
|
107
|
+
# @param keys [Array<Integer>] Current keys
|
|
108
|
+
# @return [Integer] Decrypted byte value
|
|
109
|
+
def decrypt_byte(keys)
|
|
110
|
+
temp = keys[2] | 2
|
|
111
|
+
((temp * (temp ^ 1)) >> 8) & 0xFF
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# CRC32 update for key generation
|
|
115
|
+
# @param crc [Integer] Current CRC value
|
|
116
|
+
# @param byte [Integer] Byte to add
|
|
117
|
+
# @return [Integer] Updated CRC
|
|
118
|
+
def crc32_update(crc, byte)
|
|
119
|
+
require_relative "../checksums/crc32"
|
|
120
|
+
crc32 = Omnizip::Checksums::Crc32.new
|
|
121
|
+
crc32.instance_variable_set(:@crc, crc)
|
|
122
|
+
crc32.update(byte.chr)
|
|
123
|
+
crc32.value
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
# Generate encryption header
|
|
127
|
+
# @param keys [Array<Integer>] Encryption keys
|
|
128
|
+
# @return [Array<Integer>] 12-byte header
|
|
129
|
+
def generate_encryption_header(keys)
|
|
130
|
+
header = Array.new(12) { rand(256) }
|
|
131
|
+
encrypted_header = []
|
|
132
|
+
header.each do |byte|
|
|
133
|
+
temp = decrypt_byte(keys)
|
|
134
|
+
update_keys(keys, byte)
|
|
135
|
+
encrypted_header << (byte ^ temp)
|
|
136
|
+
end
|
|
137
|
+
encrypted_header
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
end
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "password/password_validator"
|
|
4
|
+
require_relative "password/encryption_strategy"
|
|
5
|
+
require_relative "password/zip_crypto_strategy"
|
|
6
|
+
require_relative "password/winzip_aes_strategy"
|
|
7
|
+
require_relative "password/encryption_registry"
|
|
8
|
+
|
|
9
|
+
module Omnizip
|
|
10
|
+
# Password protection module
|
|
11
|
+
# Provides encryption and password validation for archives
|
|
12
|
+
module Password
|
|
13
|
+
class << self
|
|
14
|
+
# Validate a password
|
|
15
|
+
# @param password [String] Password to validate
|
|
16
|
+
# @param options [Hash] Validation options
|
|
17
|
+
# @return [Boolean] True if valid
|
|
18
|
+
def validate(password, **options)
|
|
19
|
+
validator = PasswordValidator.new(**options)
|
|
20
|
+
validator.validate(password)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Check password strength
|
|
24
|
+
# @param password [String] Password to check
|
|
25
|
+
# @return [Symbol] Strength label (:weak, :fair, :good, :strong)
|
|
26
|
+
def strength(password)
|
|
27
|
+
validator = PasswordValidator.new
|
|
28
|
+
validator.strength_label(password)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Create encryption strategy
|
|
32
|
+
# @param method [Symbol] Encryption method
|
|
33
|
+
# @param password [String] Password
|
|
34
|
+
# @param options [Hash] Strategy options
|
|
35
|
+
# @return [EncryptionStrategy] Encryption strategy instance
|
|
36
|
+
def create_strategy(method, password, **options)
|
|
37
|
+
EncryptionRegistry.create(method, password, **options)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Get available encryption methods
|
|
41
|
+
# @return [Array<Symbol>] List of methods
|
|
42
|
+
def encryption_methods
|
|
43
|
+
EncryptionRegistry.strategies
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Prompt for password (with confirmation)
|
|
47
|
+
# @param confirm [Boolean] Require confirmation
|
|
48
|
+
# @return [String] Password entered by user
|
|
49
|
+
def prompt(confirm: true)
|
|
50
|
+
require "io/console"
|
|
51
|
+
|
|
52
|
+
print "Enter password: "
|
|
53
|
+
password = $stdin.noecho(&:gets).chomp
|
|
54
|
+
puts
|
|
55
|
+
|
|
56
|
+
if confirm
|
|
57
|
+
print "Confirm password: "
|
|
58
|
+
confirmation = $stdin.noecho(&:gets).chomp
|
|
59
|
+
puts
|
|
60
|
+
|
|
61
|
+
unless password == confirmation
|
|
62
|
+
raise PasswordError, "Passwords do not match"
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
password
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Read password from environment variable
|
|
70
|
+
# @param var_name [String] Environment variable name
|
|
71
|
+
# @return [String, nil] Password or nil
|
|
72
|
+
def from_env(var_name = "OMNIZIP_PASSWORD")
|
|
73
|
+
ENV.fetch(var_name, nil)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Read password from file
|
|
77
|
+
# @param file_path [String] Path to password file
|
|
78
|
+
# @return [String] Password from file
|
|
79
|
+
def from_file(file_path)
|
|
80
|
+
File.read(file_path).strip
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# Password-related error
|
|
86
|
+
class PasswordError < Error; end
|
|
87
|
+
end
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Omnizip
|
|
4
|
+
module Pipe
|
|
5
|
+
# Stream-based compression for pipe operations
|
|
6
|
+
#
|
|
7
|
+
# Accepts any IO-like input source and writes compressed output
|
|
8
|
+
# to any IO-like sink, using chunk-based processing to avoid
|
|
9
|
+
# loading entire streams into memory.
|
|
10
|
+
#
|
|
11
|
+
# @example Compress stdin to stdout
|
|
12
|
+
# compressor = StreamCompressor.new($stdin, $stdout, :zip)
|
|
13
|
+
# compressor.compress
|
|
14
|
+
#
|
|
15
|
+
# @example Compress with custom chunk size
|
|
16
|
+
# compressor = StreamCompressor.new(
|
|
17
|
+
# input_file,
|
|
18
|
+
# output_file,
|
|
19
|
+
# :zip,
|
|
20
|
+
# chunk_size: 1024 * 1024 # 1MB chunks
|
|
21
|
+
# )
|
|
22
|
+
# compressor.compress
|
|
23
|
+
class StreamCompressor
|
|
24
|
+
# Default chunk size for reading input (64KB)
|
|
25
|
+
DEFAULT_CHUNK_SIZE = 64 * 1024
|
|
26
|
+
|
|
27
|
+
attr_reader :input, :output, :format, :options, :bytes_written
|
|
28
|
+
|
|
29
|
+
# Initialize stream compressor
|
|
30
|
+
#
|
|
31
|
+
# @param input [IO] Input stream to compress
|
|
32
|
+
# @param output [IO] Output stream for compressed data
|
|
33
|
+
# @param format [Symbol] Archive format (:zip, :seven_zip)
|
|
34
|
+
# @param compression [Symbol, nil] Compression algorithm
|
|
35
|
+
# @param options [Hash] Compression options
|
|
36
|
+
# @option options [String] :entry_name Name for entry (default: 'stream.dat')
|
|
37
|
+
# @option options [Integer] :chunk_size Read buffer size
|
|
38
|
+
# @option options [Integer] :level Compression level (1-9)
|
|
39
|
+
# @option options [Proc] :progress Progress callback
|
|
40
|
+
def initialize(input, output, format, compression: nil, **options)
|
|
41
|
+
@input = input
|
|
42
|
+
@output = output
|
|
43
|
+
@format = format
|
|
44
|
+
@compression = compression
|
|
45
|
+
@options = options
|
|
46
|
+
@chunk_size = options[:chunk_size] || DEFAULT_CHUNK_SIZE
|
|
47
|
+
@bytes_read = 0
|
|
48
|
+
@bytes_written = 0
|
|
49
|
+
@progress_callback = options[:progress]
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Compress input stream to output stream
|
|
53
|
+
#
|
|
54
|
+
# Reads input in chunks and writes compressed output,
|
|
55
|
+
# maintaining constant memory usage regardless of input size.
|
|
56
|
+
#
|
|
57
|
+
# @return [Integer] Total bytes written to output
|
|
58
|
+
# @raise [ArgumentError] If format is unsupported
|
|
59
|
+
def compress
|
|
60
|
+
case @format
|
|
61
|
+
when :zip
|
|
62
|
+
compress_zip
|
|
63
|
+
when :seven_zip, :'7z'
|
|
64
|
+
compress_7z
|
|
65
|
+
else
|
|
66
|
+
raise ArgumentError, "Unsupported format: #{@format}"
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
@bytes_written
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
private
|
|
73
|
+
|
|
74
|
+
# Compress to ZIP format
|
|
75
|
+
#
|
|
76
|
+
# Creates a single-entry ZIP archive from the input stream.
|
|
77
|
+
# Uses chunked reading to handle arbitrarily large inputs.
|
|
78
|
+
def compress_zip
|
|
79
|
+
entry_name = @options[:entry_name] || "stream.dat"
|
|
80
|
+
level = @options[:level]
|
|
81
|
+
|
|
82
|
+
Omnizip::Zip::OutputStream.open(@output) do |zos|
|
|
83
|
+
# Create entry with optional compression level
|
|
84
|
+
entry_options = {}
|
|
85
|
+
entry_options[:level] = level if level
|
|
86
|
+
|
|
87
|
+
zos.put_next_entry(entry_name, **entry_options)
|
|
88
|
+
|
|
89
|
+
# Stream chunks from input to output
|
|
90
|
+
stream_chunks(zos)
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# Compress to 7z format
|
|
95
|
+
#
|
|
96
|
+
# Note: 7z streaming support is limited due to format requirements.
|
|
97
|
+
# For full 7z support, use file-based operations.
|
|
98
|
+
def compress_7z
|
|
99
|
+
raise NotImplementedError,
|
|
100
|
+
"7z pipe compression not yet implemented. " \
|
|
101
|
+
"Use file-based operations for 7z format."
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# Stream data in chunks from input to output
|
|
105
|
+
#
|
|
106
|
+
# @param output_stream [IO] Stream to write chunks to
|
|
107
|
+
def stream_chunks(output_stream)
|
|
108
|
+
loop do
|
|
109
|
+
chunk = @input.read(@chunk_size)
|
|
110
|
+
break unless chunk
|
|
111
|
+
|
|
112
|
+
output_stream.write(chunk)
|
|
113
|
+
@bytes_read += chunk.bytesize
|
|
114
|
+
@bytes_written += chunk.bytesize
|
|
115
|
+
|
|
116
|
+
# Call progress callback if provided
|
|
117
|
+
@progress_callback&.call(@bytes_read, @bytes_written)
|
|
118
|
+
end
|
|
119
|
+
rescue IOError, SystemCallError => e
|
|
120
|
+
raise Omnizip::Error, "Stream compression failed: #{e.message}"
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
end
|