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.
Files changed (511) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +3 -0
  3. data/.rubocop.yml +32 -0
  4. data/.rubocop_todo.yml +754 -0
  5. data/COPYING +502 -0
  6. data/Gemfile +17 -0
  7. data/LICENSE +12 -0
  8. data/README.adoc +1045 -0
  9. data/Rakefile +12 -0
  10. data/benchmark/README.md +260 -0
  11. data/benchmark/benchmark_suite.rb +125 -0
  12. data/benchmark/compression_bench.rb +181 -0
  13. data/benchmark/filter_bench.rb +180 -0
  14. data/benchmark/models/benchmark_result.rb +59 -0
  15. data/benchmark/models/comparison_result.rb +69 -0
  16. data/benchmark/profile_suite.rb +167 -0
  17. data/benchmark/reporter.rb +150 -0
  18. data/benchmark/run_benchmarks.rb +66 -0
  19. data/benchmark/test_data.rb +137 -0
  20. data/config/formats/rar3_spec.yml +91 -0
  21. data/config/formats/rar5_spec.yml +102 -0
  22. data/docs/.github/workflows/docs.yml +142 -0
  23. data/docs/.gitignore +21 -0
  24. data/docs/.lychee.toml +67 -0
  25. data/docs/Gemfile +13 -0
  26. data/docs/RAR_WRITE_SUPPORT.md +26 -0
  27. data/docs/README.md +101 -0
  28. data/docs/_config.yml +112 -0
  29. data/docs/assets/logo.svg +1 -0
  30. data/docs/assets/omnizip-logo.pdf +1540 -11
  31. data/docs/comparison/feature-matrix.adoc +694 -0
  32. data/docs/comparison/index.adoc +113 -0
  33. data/docs/comparison/vs-7zip.adoc +309 -0
  34. data/docs/comparison/vs-peazip.adoc +77 -0
  35. data/docs/comparison/vs-rubyzip.adoc +342 -0
  36. data/docs/comparison/vs-winrar.adoc +100 -0
  37. data/docs/compatibility.adoc +579 -0
  38. data/docs/concepts/index.adoc +129 -0
  39. data/docs/developer/architecture.adoc +256 -0
  40. data/docs/developer/contributing.adoc +158 -0
  41. data/docs/developer/index.adoc +25 -0
  42. data/docs/developer/testing.adoc +212 -0
  43. data/docs/getting-started/basic-usage.adoc +271 -0
  44. data/docs/getting-started/index.adoc +42 -0
  45. data/docs/getting-started/installation.adoc +138 -0
  46. data/docs/getting-started/quick-start.adoc +185 -0
  47. data/docs/getting-started/your-first-archive.adoc +218 -0
  48. data/docs/guides/advanced-features/encryption.adoc +300 -0
  49. data/docs/guides/advanced-features/index.adoc +49 -0
  50. data/docs/guides/advanced-features/parallel-processing.adoc +246 -0
  51. data/docs/guides/advanced-features/progress-tracking.adoc +320 -0
  52. data/docs/guides/advanced-features/streaming.adoc +212 -0
  53. data/docs/guides/archive-formats/gzip-format.adoc +107 -0
  54. data/docs/guides/archive-formats/index.adoc +130 -0
  55. data/docs/guides/archive-formats/rar-format.adoc +104 -0
  56. data/docs/guides/archive-formats/rar5.adoc +521 -0
  57. data/docs/guides/archive-formats/seven-zip-format.adoc +35 -0
  58. data/docs/guides/archive-formats/tar-format.adoc +106 -0
  59. data/docs/guides/archive-formats/xz-format.adoc +118 -0
  60. data/docs/guides/archive-formats/zip-format.adoc +35 -0
  61. data/docs/guides/compression-algorithms/bzip2.adoc +113 -0
  62. data/docs/guides/compression-algorithms/deflate.adoc +319 -0
  63. data/docs/guides/compression-algorithms/index.adoc +190 -0
  64. data/docs/guides/compression-algorithms/lzma.adoc +398 -0
  65. data/docs/guides/compression-algorithms/lzma2.adoc +327 -0
  66. data/docs/guides/compression-algorithms/ppmd.adoc +316 -0
  67. data/docs/guides/compression-algorithms/zstandard.adoc +361 -0
  68. data/docs/guides/creating-archives.adoc +354 -0
  69. data/docs/guides/extracting-archives.adoc +53 -0
  70. data/docs/guides/format-conversion.adoc +64 -0
  71. data/docs/guides/index.adoc +49 -0
  72. data/docs/guides/migration-rubyzip.adoc +217 -0
  73. data/docs/guides/parity-archives.adoc +605 -0
  74. data/docs/guides/performance-tuning.adoc +88 -0
  75. data/docs/index.adoc +218 -0
  76. data/docs/lychee.toml +67 -0
  77. data/docs/reference/api/overview.adoc +188 -0
  78. data/docs/reference/cli/compress-command.adoc +114 -0
  79. data/docs/reference/cli/overview.adoc +140 -0
  80. data/docs/reference/index.adoc +26 -0
  81. data/docs/resources/faq.adoc +185 -0
  82. data/docs/resources/quick-reference.adoc +222 -0
  83. data/docs/troubleshooting/index.adoc +208 -0
  84. data/examples/api_comparison.rb +205 -0
  85. data/examples/deflate64_example.rb +96 -0
  86. data/examples/par2_demo.rb +121 -0
  87. data/examples/quick_start_native.rb +150 -0
  88. data/examples/quick_start_rubyzip.rb +115 -0
  89. data/examples/rubyzip_compatibility_demo.rb +194 -0
  90. data/exe/omnizip +27 -0
  91. data/lib/omnizip/algorithm.rb +130 -0
  92. data/lib/omnizip/algorithm_registry.rb +86 -0
  93. data/lib/omnizip/algorithms/.keep +0 -0
  94. data/lib/omnizip/algorithms/bzip2/bwt.rb +225 -0
  95. data/lib/omnizip/algorithms/bzip2/decoder.rb +193 -0
  96. data/lib/omnizip/algorithms/bzip2/encoder.rb +237 -0
  97. data/lib/omnizip/algorithms/bzip2/huffman.rb +206 -0
  98. data/lib/omnizip/algorithms/bzip2/mtf.rb +101 -0
  99. data/lib/omnizip/algorithms/bzip2/rle.rb +151 -0
  100. data/lib/omnizip/algorithms/bzip2.rb +130 -0
  101. data/lib/omnizip/algorithms/deflate/constants.rb +28 -0
  102. data/lib/omnizip/algorithms/deflate/decoder.rb +38 -0
  103. data/lib/omnizip/algorithms/deflate/encoder.rb +46 -0
  104. data/lib/omnizip/algorithms/deflate.rb +128 -0
  105. data/lib/omnizip/algorithms/deflate64/constants.rb +45 -0
  106. data/lib/omnizip/algorithms/deflate64/decoder.rb +153 -0
  107. data/lib/omnizip/algorithms/deflate64/encoder.rb +98 -0
  108. data/lib/omnizip/algorithms/deflate64/huffman_coder.rb +354 -0
  109. data/lib/omnizip/algorithms/deflate64/lz77_encoder.rb +142 -0
  110. data/lib/omnizip/algorithms/deflate64.rb +109 -0
  111. data/lib/omnizip/algorithms/lzma/bit_model.rb +120 -0
  112. data/lib/omnizip/algorithms/lzma/constants.rb +112 -0
  113. data/lib/omnizip/algorithms/lzma/decoder.rb +148 -0
  114. data/lib/omnizip/algorithms/lzma/dictionary.rb +69 -0
  115. data/lib/omnizip/algorithms/lzma/distance_coder.rb +415 -0
  116. data/lib/omnizip/algorithms/lzma/encoder.rb +142 -0
  117. data/lib/omnizip/algorithms/lzma/length_coder.rb +260 -0
  118. data/lib/omnizip/algorithms/lzma/literal_decoder.rb +320 -0
  119. data/lib/omnizip/algorithms/lzma/literal_encoder.rb +210 -0
  120. data/lib/omnizip/algorithms/lzma/lzip_decoder.rb +341 -0
  121. data/lib/omnizip/algorithms/lzma/lzma_alone_decoder.rb +192 -0
  122. data/lib/omnizip/algorithms/lzma/lzma_state.rb +128 -0
  123. data/lib/omnizip/algorithms/lzma/match.rb +32 -0
  124. data/lib/omnizip/algorithms/lzma/match_finder.rb +205 -0
  125. data/lib/omnizip/algorithms/lzma/match_finder_config.rb +142 -0
  126. data/lib/omnizip/algorithms/lzma/match_finder_factory.rb +88 -0
  127. data/lib/omnizip/algorithms/lzma/optimal_encoder.rb +130 -0
  128. data/lib/omnizip/algorithms/lzma/probability_models.rb +72 -0
  129. data/lib/omnizip/algorithms/lzma/range_coder.rb +85 -0
  130. data/lib/omnizip/algorithms/lzma/range_decoder.rb +434 -0
  131. data/lib/omnizip/algorithms/lzma/range_encoder.rb +194 -0
  132. data/lib/omnizip/algorithms/lzma/state.rb +127 -0
  133. data/lib/omnizip/algorithms/lzma/xz_buffered_range_encoder.rb +325 -0
  134. data/lib/omnizip/algorithms/lzma/xz_encoder.rb +426 -0
  135. data/lib/omnizip/algorithms/lzma/xz_encoder_fast.rb +645 -0
  136. data/lib/omnizip/algorithms/lzma/xz_match_finder_adapter.rb +227 -0
  137. data/lib/omnizip/algorithms/lzma/xz_price_calculator.rb +169 -0
  138. data/lib/omnizip/algorithms/lzma/xz_probability_models.rb +261 -0
  139. data/lib/omnizip/algorithms/lzma/xz_range_encoder.rb +223 -0
  140. data/lib/omnizip/algorithms/lzma/xz_range_encoder_exact.rb +331 -0
  141. data/lib/omnizip/algorithms/lzma/xz_state.rb +116 -0
  142. data/lib/omnizip/algorithms/lzma/xz_utils_decoder.rb +2055 -0
  143. data/lib/omnizip/algorithms/lzma.rb +238 -0
  144. data/lib/omnizip/algorithms/lzma2/chunk_manager.rb +182 -0
  145. data/lib/omnizip/algorithms/lzma2/constants.rb +41 -0
  146. data/lib/omnizip/algorithms/lzma2/encoder.rb +147 -0
  147. data/lib/omnizip/algorithms/lzma2/lzma2_chunk.rb +161 -0
  148. data/lib/omnizip/algorithms/lzma2/properties.rb +179 -0
  149. data/lib/omnizip/algorithms/lzma2/simple_lzma2_encoder.rb +127 -0
  150. data/lib/omnizip/algorithms/lzma2/xz_encoder_adapter.rb +85 -0
  151. data/lib/omnizip/algorithms/lzma2.rb +141 -0
  152. data/lib/omnizip/algorithms/ppmd7/constants.rb +74 -0
  153. data/lib/omnizip/algorithms/ppmd7/context.rb +154 -0
  154. data/lib/omnizip/algorithms/ppmd7/decoder.rb +126 -0
  155. data/lib/omnizip/algorithms/ppmd7/encoder.rb +163 -0
  156. data/lib/omnizip/algorithms/ppmd7/model.rb +248 -0
  157. data/lib/omnizip/algorithms/ppmd7/symbol_state.rb +57 -0
  158. data/lib/omnizip/algorithms/ppmd7.rb +116 -0
  159. data/lib/omnizip/algorithms/ppmd8/constants.rb +61 -0
  160. data/lib/omnizip/algorithms/ppmd8/context.rb +34 -0
  161. data/lib/omnizip/algorithms/ppmd8/decoder.rb +107 -0
  162. data/lib/omnizip/algorithms/ppmd8/encoder.rb +138 -0
  163. data/lib/omnizip/algorithms/ppmd8/model.rb +250 -0
  164. data/lib/omnizip/algorithms/ppmd8/restoration_method.rb +78 -0
  165. data/lib/omnizip/algorithms/ppmd8.rb +82 -0
  166. data/lib/omnizip/algorithms/ppmd_base.rb +138 -0
  167. data/lib/omnizip/algorithms/sevenzip_lzma2.rb +123 -0
  168. data/lib/omnizip/algorithms/xz_lzma2.rb +118 -0
  169. data/lib/omnizip/algorithms/zstandard/constants.rb +25 -0
  170. data/lib/omnizip/algorithms/zstandard/decoder.rb +46 -0
  171. data/lib/omnizip/algorithms/zstandard/encoder.rb +51 -0
  172. data/lib/omnizip/algorithms/zstandard.rb +138 -0
  173. data/lib/omnizip/buffer/memory_archive.rb +251 -0
  174. data/lib/omnizip/buffer/memory_extractor.rb +224 -0
  175. data/lib/omnizip/buffer.rb +176 -0
  176. data/lib/omnizip/checksum_registry.rb +114 -0
  177. data/lib/omnizip/checksums/crc32.rb +100 -0
  178. data/lib/omnizip/checksums/crc64.rb +101 -0
  179. data/lib/omnizip/checksums/crc_base.rb +158 -0
  180. data/lib/omnizip/checksums/verifier.rb +131 -0
  181. data/lib/omnizip/chunked/memory_manager.rb +194 -0
  182. data/lib/omnizip/chunked/reader.rb +78 -0
  183. data/lib/omnizip/chunked/writer.rb +120 -0
  184. data/lib/omnizip/chunked.rb +129 -0
  185. data/lib/omnizip/cli/output_formatter.rb +104 -0
  186. data/lib/omnizip/cli.rb +572 -0
  187. data/lib/omnizip/commands/.keep +0 -0
  188. data/lib/omnizip/commands/archive_create_command.rb +427 -0
  189. data/lib/omnizip/commands/archive_extract_command.rb +272 -0
  190. data/lib/omnizip/commands/archive_list_command.rb +218 -0
  191. data/lib/omnizip/commands/archive_repair_command.rb +131 -0
  192. data/lib/omnizip/commands/archive_verify_command.rb +117 -0
  193. data/lib/omnizip/commands/compress_command.rb +117 -0
  194. data/lib/omnizip/commands/decompress_command.rb +120 -0
  195. data/lib/omnizip/commands/list_command.rb +53 -0
  196. data/lib/omnizip/commands/metadata_command.rb +153 -0
  197. data/lib/omnizip/commands/parity_create_command.rb +122 -0
  198. data/lib/omnizip/commands/parity_repair_command.rb +122 -0
  199. data/lib/omnizip/commands/parity_verify_command.rb +124 -0
  200. data/lib/omnizip/commands/profile_list_command.rb +56 -0
  201. data/lib/omnizip/commands/profile_show_command.rb +44 -0
  202. data/lib/omnizip/convenience.rb +359 -0
  203. data/lib/omnizip/converter/conversion_registry.rb +49 -0
  204. data/lib/omnizip/converter/conversion_strategy.rb +121 -0
  205. data/lib/omnizip/converter/seven_zip_to_zip_strategy.rb +97 -0
  206. data/lib/omnizip/converter/zip_to_seven_zip_strategy.rb +112 -0
  207. data/lib/omnizip/converter.rb +105 -0
  208. data/lib/omnizip/crypto/aes256/cipher.rb +100 -0
  209. data/lib/omnizip/crypto/aes256/constants.rb +28 -0
  210. data/lib/omnizip/crypto/aes256/key_derivation.rb +101 -0
  211. data/lib/omnizip/crypto/aes256.rb +102 -0
  212. data/lib/omnizip/error.rb +106 -0
  213. data/lib/omnizip/eta/exponential_smoothing_estimator.rb +98 -0
  214. data/lib/omnizip/eta/moving_average_estimator.rb +99 -0
  215. data/lib/omnizip/eta/rate_calculator.rb +104 -0
  216. data/lib/omnizip/eta/sample_history.rb +143 -0
  217. data/lib/omnizip/eta/time_estimator.rb +106 -0
  218. data/lib/omnizip/eta.rb +63 -0
  219. data/lib/omnizip/extraction/filter_chain.rb +177 -0
  220. data/lib/omnizip/extraction/glob_pattern.rb +140 -0
  221. data/lib/omnizip/extraction/pattern_matcher.rb +70 -0
  222. data/lib/omnizip/extraction/predicate_pattern.rb +52 -0
  223. data/lib/omnizip/extraction/regex_pattern.rb +50 -0
  224. data/lib/omnizip/extraction/selective_extractor.rb +240 -0
  225. data/lib/omnizip/extraction.rb +111 -0
  226. data/lib/omnizip/file_type/mime_classifier.rb +144 -0
  227. data/lib/omnizip/file_type.rb +113 -0
  228. data/lib/omnizip/filter.rb +139 -0
  229. data/lib/omnizip/filter_pipeline.rb +108 -0
  230. data/lib/omnizip/filter_registry.rb +166 -0
  231. data/lib/omnizip/filters/bcj.rb +279 -0
  232. data/lib/omnizip/filters/bcj2/constants.rb +53 -0
  233. data/lib/omnizip/filters/bcj2/decoder.rb +200 -0
  234. data/lib/omnizip/filters/bcj2/encoder.rb +61 -0
  235. data/lib/omnizip/filters/bcj2/stream_data.rb +93 -0
  236. data/lib/omnizip/filters/bcj2.rb +99 -0
  237. data/lib/omnizip/filters/bcj_arm.rb +176 -0
  238. data/lib/omnizip/filters/bcj_arm64.rb +244 -0
  239. data/lib/omnizip/filters/bcj_ia64.rb +196 -0
  240. data/lib/omnizip/filters/bcj_ppc.rb +190 -0
  241. data/lib/omnizip/filters/bcj_sparc.rb +176 -0
  242. data/lib/omnizip/filters/bcj_x86.rb +193 -0
  243. data/lib/omnizip/filters/delta.rb +196 -0
  244. data/lib/omnizip/filters/filter_base.rb +72 -0
  245. data/lib/omnizip/filters/registry.rb +123 -0
  246. data/lib/omnizip/filters/xz_delta.rb +258 -0
  247. data/lib/omnizip/format_detector.rb +162 -0
  248. data/lib/omnizip/format_registry.rb +59 -0
  249. data/lib/omnizip/formats/.keep +0 -0
  250. data/lib/omnizip/formats/bzip2_file.rb +172 -0
  251. data/lib/omnizip/formats/cpio/constants.rb +55 -0
  252. data/lib/omnizip/formats/cpio/entry.rb +385 -0
  253. data/lib/omnizip/formats/cpio/reader.rb +196 -0
  254. data/lib/omnizip/formats/cpio/writer.rb +234 -0
  255. data/lib/omnizip/formats/cpio.rb +140 -0
  256. data/lib/omnizip/formats/format_spec_loader.rb +230 -0
  257. data/lib/omnizip/formats/gzip.rb +238 -0
  258. data/lib/omnizip/formats/iso/directory_builder.rb +297 -0
  259. data/lib/omnizip/formats/iso/directory_record.rb +152 -0
  260. data/lib/omnizip/formats/iso/joliet.rb +204 -0
  261. data/lib/omnizip/formats/iso/path_table.rb +125 -0
  262. data/lib/omnizip/formats/iso/reader.rb +197 -0
  263. data/lib/omnizip/formats/iso/rock_ridge.rb +349 -0
  264. data/lib/omnizip/formats/iso/volume_builder.rb +320 -0
  265. data/lib/omnizip/formats/iso/volume_descriptor.rb +168 -0
  266. data/lib/omnizip/formats/iso/writer.rb +530 -0
  267. data/lib/omnizip/formats/iso.rb +140 -0
  268. data/lib/omnizip/formats/lzip.rb +175 -0
  269. data/lib/omnizip/formats/lzma_alone.rb +171 -0
  270. data/lib/omnizip/formats/rar/archive_repairer.rb +243 -0
  271. data/lib/omnizip/formats/rar/archive_verifier.rb +195 -0
  272. data/lib/omnizip/formats/rar/block_parser.rb +243 -0
  273. data/lib/omnizip/formats/rar/compression/bit_stream.rb +180 -0
  274. data/lib/omnizip/formats/rar/compression/dispatcher.rb +217 -0
  275. data/lib/omnizip/formats/rar/compression/lz77_huffman/decoder.rb +216 -0
  276. data/lib/omnizip/formats/rar/compression/lz77_huffman/encoder.rb +158 -0
  277. data/lib/omnizip/formats/rar/compression/lz77_huffman/huffman_builder.rb +217 -0
  278. data/lib/omnizip/formats/rar/compression/lz77_huffman/huffman_coder.rb +189 -0
  279. data/lib/omnizip/formats/rar/compression/lz77_huffman/match_finder.rb +135 -0
  280. data/lib/omnizip/formats/rar/compression/lz77_huffman/sliding_window.rb +165 -0
  281. data/lib/omnizip/formats/rar/compression/ppmd/context.rb +105 -0
  282. data/lib/omnizip/formats/rar/compression/ppmd/decoder.rb +219 -0
  283. data/lib/omnizip/formats/rar/compression/ppmd/encoder.rb +262 -0
  284. data/lib/omnizip/formats/rar/compression_method_registry.rb +106 -0
  285. data/lib/omnizip/formats/rar/constants.rb +82 -0
  286. data/lib/omnizip/formats/rar/decompressor.rb +238 -0
  287. data/lib/omnizip/formats/rar/external_writer.rb +312 -0
  288. data/lib/omnizip/formats/rar/header.rb +192 -0
  289. data/lib/omnizip/formats/rar/license_validator.rb +109 -0
  290. data/lib/omnizip/formats/rar/models/rar_archive.rb +77 -0
  291. data/lib/omnizip/formats/rar/models/rar_entry.rb +65 -0
  292. data/lib/omnizip/formats/rar/models/rar_volume.rb +56 -0
  293. data/lib/omnizip/formats/rar/parity_handler.rb +292 -0
  294. data/lib/omnizip/formats/rar/rar5/compression/lzma.rb +202 -0
  295. data/lib/omnizip/formats/rar/rar5/compression/lzss.rb +578 -0
  296. data/lib/omnizip/formats/rar/rar5/compression/store.rb +60 -0
  297. data/lib/omnizip/formats/rar/rar5/crc32.rb +39 -0
  298. data/lib/omnizip/formats/rar/rar5/encryption/aes256_cbc.rb +97 -0
  299. data/lib/omnizip/formats/rar/rar5/encryption/encryption_header.rb +114 -0
  300. data/lib/omnizip/formats/rar/rar5/encryption/encryption_manager.rb +166 -0
  301. data/lib/omnizip/formats/rar/rar5/encryption/key_derivation.rb +97 -0
  302. data/lib/omnizip/formats/rar/rar5/header.rb +187 -0
  303. data/lib/omnizip/formats/rar/rar5/models/encryption_options.rb +74 -0
  304. data/lib/omnizip/formats/rar/rar5/models/recovery_options.rb +63 -0
  305. data/lib/omnizip/formats/rar/rar5/models/solid_options.rb +63 -0
  306. data/lib/omnizip/formats/rar/rar5/models/volume_options.rb +74 -0
  307. data/lib/omnizip/formats/rar/rar5/multi_volume/ARCHITECTURE.md +290 -0
  308. data/lib/omnizip/formats/rar/rar5/multi_volume/volume_manager.rb +264 -0
  309. data/lib/omnizip/formats/rar/rar5/multi_volume/volume_splitter.rb +155 -0
  310. data/lib/omnizip/formats/rar/rar5/multi_volume/volume_writer.rb +194 -0
  311. data/lib/omnizip/formats/rar/rar5/solid/solid_encoder.rb +109 -0
  312. data/lib/omnizip/formats/rar/rar5/solid/solid_manager.rb +142 -0
  313. data/lib/omnizip/formats/rar/rar5/solid/solid_stream.rb +121 -0
  314. data/lib/omnizip/formats/rar/rar5/vint.rb +65 -0
  315. data/lib/omnizip/formats/rar/rar5/writer.rb +466 -0
  316. data/lib/omnizip/formats/rar/rar_format_base.rb +241 -0
  317. data/lib/omnizip/formats/rar/reader.rb +366 -0
  318. data/lib/omnizip/formats/rar/recovery_record.rb +245 -0
  319. data/lib/omnizip/formats/rar/volume_manager.rb +168 -0
  320. data/lib/omnizip/formats/rar/writer.rb +431 -0
  321. data/lib/omnizip/formats/rar.rb +205 -0
  322. data/lib/omnizip/formats/rar3/compressor.rb +73 -0
  323. data/lib/omnizip/formats/rar3/decompressor.rb +66 -0
  324. data/lib/omnizip/formats/rar3/reader.rb +386 -0
  325. data/lib/omnizip/formats/rar3/writer.rb +219 -0
  326. data/lib/omnizip/formats/rar5/compressor.rb +73 -0
  327. data/lib/omnizip/formats/rar5/decompressor.rb +66 -0
  328. data/lib/omnizip/formats/rar5/reader.rb +342 -0
  329. data/lib/omnizip/formats/rar5/writer.rb +214 -0
  330. data/lib/omnizip/formats/seven_zip/coder_chain.rb +150 -0
  331. data/lib/omnizip/formats/seven_zip/constants.rb +126 -0
  332. data/lib/omnizip/formats/seven_zip/encoded_header.rb +114 -0
  333. data/lib/omnizip/formats/seven_zip/encrypted_header.rb +142 -0
  334. data/lib/omnizip/formats/seven_zip/file_collector.rb +144 -0
  335. data/lib/omnizip/formats/seven_zip/header.rb +106 -0
  336. data/lib/omnizip/formats/seven_zip/header_encryptor.rb +134 -0
  337. data/lib/omnizip/formats/seven_zip/header_writer.rb +466 -0
  338. data/lib/omnizip/formats/seven_zip/models/coder_info.rb +30 -0
  339. data/lib/omnizip/formats/seven_zip/models/file_entry.rb +58 -0
  340. data/lib/omnizip/formats/seven_zip/models/folder.rb +69 -0
  341. data/lib/omnizip/formats/seven_zip/models/stream_info.rb +42 -0
  342. data/lib/omnizip/formats/seven_zip/parser.rb +660 -0
  343. data/lib/omnizip/formats/seven_zip/reader.rb +458 -0
  344. data/lib/omnizip/formats/seven_zip/split_archive_reader.rb +632 -0
  345. data/lib/omnizip/formats/seven_zip/split_archive_writer.rb +315 -0
  346. data/lib/omnizip/formats/seven_zip/stream_compressor.rb +151 -0
  347. data/lib/omnizip/formats/seven_zip/stream_decompressor.rb +162 -0
  348. data/lib/omnizip/formats/seven_zip/writer.rb +740 -0
  349. data/lib/omnizip/formats/seven_zip.rb +93 -0
  350. data/lib/omnizip/formats/tar/constants.rb +73 -0
  351. data/lib/omnizip/formats/tar/entry.rb +94 -0
  352. data/lib/omnizip/formats/tar/header.rb +168 -0
  353. data/lib/omnizip/formats/tar/reader.rb +121 -0
  354. data/lib/omnizip/formats/tar/writer.rb +216 -0
  355. data/lib/omnizip/formats/tar.rb +84 -0
  356. data/lib/omnizip/formats/xz/reader.rb +116 -0
  357. data/lib/omnizip/formats/xz.rb +237 -0
  358. data/lib/omnizip/formats/xz_impl/block_decoder.rb +754 -0
  359. data/lib/omnizip/formats/xz_impl/block_encoder.rb +306 -0
  360. data/lib/omnizip/formats/xz_impl/block_header.rb +210 -0
  361. data/lib/omnizip/formats/xz_impl/block_header_parser.rb +186 -0
  362. data/lib/omnizip/formats/xz_impl/constants.rb +49 -0
  363. data/lib/omnizip/formats/xz_impl/index_decoder.rb +174 -0
  364. data/lib/omnizip/formats/xz_impl/index_encoder.rb +122 -0
  365. data/lib/omnizip/formats/xz_impl/stream_decoder.rb +468 -0
  366. data/lib/omnizip/formats/xz_impl/stream_encoder.rb +99 -0
  367. data/lib/omnizip/formats/xz_impl/stream_footer.rb +81 -0
  368. data/lib/omnizip/formats/xz_impl/stream_footer_parser.rb +117 -0
  369. data/lib/omnizip/formats/xz_impl/stream_header.rb +55 -0
  370. data/lib/omnizip/formats/xz_impl/stream_header_parser.rb +108 -0
  371. data/lib/omnizip/formats/xz_impl/vli.rb +128 -0
  372. data/lib/omnizip/formats/xz_impl/writer.rb +421 -0
  373. data/lib/omnizip/formats/zip/central_directory_header.rb +195 -0
  374. data/lib/omnizip/formats/zip/constants.rb +69 -0
  375. data/lib/omnizip/formats/zip/end_of_central_directory.rb +133 -0
  376. data/lib/omnizip/formats/zip/local_file_header.rb +138 -0
  377. data/lib/omnizip/formats/zip/reader.rb +250 -0
  378. data/lib/omnizip/formats/zip/unix_extra_field.rb +153 -0
  379. data/lib/omnizip/formats/zip/writer.rb +375 -0
  380. data/lib/omnizip/formats/zip/zip64_end_of_central_directory.rb +104 -0
  381. data/lib/omnizip/formats/zip/zip64_end_of_central_directory_locator.rb +66 -0
  382. data/lib/omnizip/formats/zip/zip64_extra_field.rb +114 -0
  383. data/lib/omnizip/formats/zip.rb +50 -0
  384. data/lib/omnizip/implementations/base/lzma2_decoder_base.rb +75 -0
  385. data/lib/omnizip/implementations/base/lzma2_encoder_base.rb +128 -0
  386. data/lib/omnizip/implementations/base/lzma_decoder_base.rb +83 -0
  387. data/lib/omnizip/implementations/base/lzma_encoder_base.rb +108 -0
  388. data/lib/omnizip/implementations/base/state_machine_base.rb +182 -0
  389. data/lib/omnizip/implementations/seven_zip/lzma/decoder.rb +421 -0
  390. data/lib/omnizip/implementations/seven_zip/lzma/encoder.rb +465 -0
  391. data/lib/omnizip/implementations/seven_zip/lzma/match_finder.rb +288 -0
  392. data/lib/omnizip/implementations/seven_zip/lzma/range_decoder.rb +200 -0
  393. data/lib/omnizip/implementations/seven_zip/lzma/range_encoder.rb +197 -0
  394. data/lib/omnizip/implementations/seven_zip/lzma/state_machine.rb +141 -0
  395. data/lib/omnizip/implementations/seven_zip/lzma2/encoder.rb +519 -0
  396. data/lib/omnizip/implementations/xz_utils/lzma2/decoder.rb +723 -0
  397. data/lib/omnizip/implementations/xz_utils/lzma2/encoder.rb +750 -0
  398. data/lib/omnizip/io/buffered_input.rb +146 -0
  399. data/lib/omnizip/io/buffered_output.rb +105 -0
  400. data/lib/omnizip/io/stream_manager.rb +115 -0
  401. data/lib/omnizip/link_handler/hard_link.rb +79 -0
  402. data/lib/omnizip/link_handler/symbolic_link.rb +74 -0
  403. data/lib/omnizip/link_handler.rb +124 -0
  404. data/lib/omnizip/metadata/archive_metadata.rb +114 -0
  405. data/lib/omnizip/metadata/entry_metadata.rb +146 -0
  406. data/lib/omnizip/metadata/metadata_editor.rb +171 -0
  407. data/lib/omnizip/metadata/metadata_registry.rb +64 -0
  408. data/lib/omnizip/metadata/metadata_validator.rb +99 -0
  409. data/lib/omnizip/metadata.rb +57 -0
  410. data/lib/omnizip/models/.keep +0 -0
  411. data/lib/omnizip/models/algorithm_metadata.rb +73 -0
  412. data/lib/omnizip/models/compression_options.rb +71 -0
  413. data/lib/omnizip/models/conversion_options.rb +87 -0
  414. data/lib/omnizip/models/conversion_result.rb +135 -0
  415. data/lib/omnizip/models/eta_result.rb +46 -0
  416. data/lib/omnizip/models/extraction_rule.rb +115 -0
  417. data/lib/omnizip/models/filter_chain.rb +144 -0
  418. data/lib/omnizip/models/filter_config.rb +183 -0
  419. data/lib/omnizip/models/match_result.rb +124 -0
  420. data/lib/omnizip/models/optimization_suggestion.rb +91 -0
  421. data/lib/omnizip/models/parallel_options.rb +104 -0
  422. data/lib/omnizip/models/performance_result.rb +79 -0
  423. data/lib/omnizip/models/profile_report.rb +82 -0
  424. data/lib/omnizip/models/progress_options.rb +38 -0
  425. data/lib/omnizip/models/split_options.rb +116 -0
  426. data/lib/omnizip/optimization_registry.rb +81 -0
  427. data/lib/omnizip/parallel/job_queue.rb +209 -0
  428. data/lib/omnizip/parallel/job_scheduler.rb +203 -0
  429. data/lib/omnizip/parallel/parallel_compressor.rb +347 -0
  430. data/lib/omnizip/parallel/parallel_extractor.rb +329 -0
  431. data/lib/omnizip/parallel/worker_pool.rb +223 -0
  432. data/lib/omnizip/parallel.rb +149 -0
  433. data/lib/omnizip/parity/chunked_block_processor.rb +196 -0
  434. data/lib/omnizip/parity/galois16.rb +145 -0
  435. data/lib/omnizip/parity/models/creator_packet.rb +73 -0
  436. data/lib/omnizip/parity/models/file_description_packet.rb +133 -0
  437. data/lib/omnizip/parity/models/ifsc_packet.rb +123 -0
  438. data/lib/omnizip/parity/models/main_packet.rb +128 -0
  439. data/lib/omnizip/parity/models/packet.rb +156 -0
  440. data/lib/omnizip/parity/models/packet_registry.rb +109 -0
  441. data/lib/omnizip/parity/models/recovery_slice_packet.rb +78 -0
  442. data/lib/omnizip/parity/par2_creator.rb +531 -0
  443. data/lib/omnizip/parity/par2_repairer.rb +407 -0
  444. data/lib/omnizip/parity/par2_verifier.rb +364 -0
  445. data/lib/omnizip/parity/par2cmdline_algorithm.rb +110 -0
  446. data/lib/omnizip/parity/par2cmdline_coefficients.rb +78 -0
  447. data/lib/omnizip/parity/reed_solomon_decoder.rb +266 -0
  448. data/lib/omnizip/parity/reed_solomon_encoder.rb +111 -0
  449. data/lib/omnizip/parity/reed_solomon_matrix.rb +342 -0
  450. data/lib/omnizip/parity.rb +186 -0
  451. data/lib/omnizip/password/encryption_registry.rb +65 -0
  452. data/lib/omnizip/password/encryption_strategy.rb +96 -0
  453. data/lib/omnizip/password/password_validator.rb +129 -0
  454. data/lib/omnizip/password/winzip_aes_strategy.rb +192 -0
  455. data/lib/omnizip/password/zip_crypto_strategy.rb +141 -0
  456. data/lib/omnizip/password.rb +87 -0
  457. data/lib/omnizip/pipe/stream_compressor.rb +124 -0
  458. data/lib/omnizip/pipe/stream_decompressor.rb +174 -0
  459. data/lib/omnizip/pipe.rb +121 -0
  460. data/lib/omnizip/platform/ntfs_streams.rb +201 -0
  461. data/lib/omnizip/platform.rb +189 -0
  462. data/lib/omnizip/profile/archive_profile.rb +39 -0
  463. data/lib/omnizip/profile/balanced_profile.rb +33 -0
  464. data/lib/omnizip/profile/binary_profile.rb +36 -0
  465. data/lib/omnizip/profile/compression_profile.rb +158 -0
  466. data/lib/omnizip/profile/custom_profile.rb +157 -0
  467. data/lib/omnizip/profile/fast_profile.rb +33 -0
  468. data/lib/omnizip/profile/maximum_profile.rb +33 -0
  469. data/lib/omnizip/profile/profile_detector.rb +110 -0
  470. data/lib/omnizip/profile/profile_registry.rb +161 -0
  471. data/lib/omnizip/profile/text_profile.rb +36 -0
  472. data/lib/omnizip/profile.rb +190 -0
  473. data/lib/omnizip/profiler/memory_profiler.rb +66 -0
  474. data/lib/omnizip/profiler/method_profiler.rb +49 -0
  475. data/lib/omnizip/profiler/report_generator.rb +169 -0
  476. data/lib/omnizip/profiler.rb +204 -0
  477. data/lib/omnizip/progress/callback_reporter.rb +36 -0
  478. data/lib/omnizip/progress/console_reporter.rb +62 -0
  479. data/lib/omnizip/progress/log_reporter.rb +91 -0
  480. data/lib/omnizip/progress/operation_progress.rb +118 -0
  481. data/lib/omnizip/progress/progress_bar.rb +156 -0
  482. data/lib/omnizip/progress/progress_reporter.rb +40 -0
  483. data/lib/omnizip/progress/progress_tracker.rb +190 -0
  484. data/lib/omnizip/progress/silent_reporter.rb +24 -0
  485. data/lib/omnizip/progress.rb +127 -0
  486. data/lib/omnizip/rubyzip_compat.rb +63 -0
  487. data/lib/omnizip/temp/safe_extract.rb +168 -0
  488. data/lib/omnizip/temp/temp_file.rb +124 -0
  489. data/lib/omnizip/temp/temp_file_pool.rb +109 -0
  490. data/lib/omnizip/temp.rb +181 -0
  491. data/lib/omnizip/version.rb +5 -0
  492. data/lib/omnizip/zip/entry.rb +156 -0
  493. data/lib/omnizip/zip/file.rb +485 -0
  494. data/lib/omnizip/zip/input_stream.rb +273 -0
  495. data/lib/omnizip/zip/output_stream.rb +324 -0
  496. data/lib/omnizip.rb +156 -0
  497. data/readme-docs/advanced-features.adoc +515 -0
  498. data/readme-docs/api-usage.adoc +444 -0
  499. data/readme-docs/architecture.adoc +449 -0
  500. data/readme-docs/archive-formats.adoc +479 -0
  501. data/readme-docs/cli-usage.adoc +222 -0
  502. data/readme-docs/compression-algorithms.adoc +442 -0
  503. data/readme-docs/compression-profiles.adoc +247 -0
  504. data/readme-docs/encryption-checksums.adoc +328 -0
  505. data/readme-docs/format-converter.adoc +325 -0
  506. data/readme-docs/installation.adoc +228 -0
  507. data/readme-docs/par2-archives.adoc +608 -0
  508. data/readme-docs/performance-profiler.adoc +389 -0
  509. data/readme-docs/preprocessing-filters.adoc +280 -0
  510. data/xz-file-format-1.2.1.txt +1174 -0
  511. 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