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,97 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "securerandom"
4
+ require_relative "../../../../crypto/aes256/cipher"
5
+
6
+ module Omnizip
7
+ module Formats
8
+ module Rar
9
+ module Rar5
10
+ module Encryption
11
+ # AES-256-CBC cipher for RAR5 encryption
12
+ #
13
+ # Wrapper around the existing Crypto::Aes256::Cipher that provides
14
+ # RAR5-specific encryption/decryption with proper key and IV handling.
15
+ #
16
+ # RAR5 uses:
17
+ # - AES-256 in CBC mode
18
+ # - PKCS#7 padding
19
+ # - Per-file IV generation
20
+ # - PBKDF2-HMAC-SHA256 key derivation
21
+ #
22
+ # @example Encrypt file data
23
+ # cipher = Aes256Cbc.new(key, iv)
24
+ # encrypted = cipher.encrypt(data)
25
+ class Aes256Cbc
26
+ # IV size (16 bytes = AES block size)
27
+ IV_SIZE = 16
28
+
29
+ # Key size (32 bytes for AES-256)
30
+ KEY_SIZE = 32
31
+
32
+ # @return [String] AES-256 key (32 bytes)
33
+ attr_reader :key
34
+
35
+ # @return [String] Initialization vector (16 bytes)
36
+ attr_reader :iv
37
+
38
+ # Initialize cipher with key and IV
39
+ #
40
+ # @param key [String] 32-byte AES-256 key
41
+ # @param iv [String] 16-byte initialization vector
42
+ # @raise [ArgumentError] If key or IV wrong size
43
+ def initialize(key, iv)
44
+ validate_key_iv(key, iv)
45
+ @key = key
46
+ @iv = iv
47
+ @cipher = Crypto::Aes256::Cipher.new(key, iv)
48
+ end
49
+
50
+ # Encrypt data
51
+ #
52
+ # @param plaintext [String] Data to encrypt
53
+ # @return [String] Encrypted data (with PKCS#7 padding)
54
+ def encrypt(plaintext)
55
+ @cipher.encrypt(plaintext)
56
+ end
57
+
58
+ # Decrypt data
59
+ #
60
+ # @param ciphertext [String] Encrypted data
61
+ # @return [String] Decrypted data (padding removed)
62
+ def decrypt(ciphertext)
63
+ @cipher.decrypt(ciphertext)
64
+ end
65
+
66
+ # Generate random IV
67
+ #
68
+ # @return [String] 16-byte random IV
69
+ def self.generate_iv
70
+ SecureRandom.random_bytes(IV_SIZE)
71
+ end
72
+
73
+ private
74
+
75
+ # Validate key and IV sizes
76
+ #
77
+ # @param key [String] Key to validate
78
+ # @param iv [String] IV to validate
79
+ # @return [void]
80
+ # @raise [ArgumentError] If key or IV wrong size
81
+ def validate_key_iv(key, iv)
82
+ if key.bytesize != KEY_SIZE
83
+ raise ArgumentError,
84
+ "Key must be #{KEY_SIZE} bytes, got #{key.bytesize}"
85
+ end
86
+
87
+ return unless iv.bytesize != IV_SIZE
88
+
89
+ raise ArgumentError,
90
+ "IV must be #{IV_SIZE} bytes, got #{iv.bytesize}"
91
+ end
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,114 @@
1
+ # frozen_string_literal: true
2
+
3
+ begin
4
+ require "lutaml/model"
5
+ rescue LoadError, ArgumentError
6
+ # lutaml-model not available, using simple classes
7
+ end
8
+
9
+ require "base64"
10
+
11
+ module Omnizip
12
+ module Formats
13
+ module Rar
14
+ module Rar5
15
+ module Encryption
16
+ # RAR5 encryption header
17
+ #
18
+ # This model stores encryption parameters needed for password verification
19
+ # and decryption. The header is written at the beginning of encrypted
20
+ # archive sections.
21
+ #
22
+ # RAR5 encryption header contains:
23
+ # - Version (always 0 for AES-256)
24
+ # - KDF iteration count
25
+ # - Salt for key derivation
26
+ # - IV for AES-CBC
27
+ # - Check value for password verification
28
+ #
29
+ # @example Create encryption header
30
+ # header = EncryptionHeader.new(
31
+ # version: 0,
32
+ # kdf_iterations: 262_144,
33
+ # salt: salt,
34
+ # iv: iv,
35
+ # check_value: check
36
+ # )
37
+ class EncryptionHeader < Lutaml::Model::Serializable
38
+ # Encryption version (0 for AES-256)
39
+ attribute :version, :integer, default: 0
40
+
41
+ # PBKDF2 iteration count
42
+ attribute :kdf_iterations, :integer, default: 262_144
43
+
44
+ # Salt for key derivation (16 bytes, base64 encoded for serialization)
45
+ attribute :salt, :string
46
+
47
+ # Initialization vector (16 bytes, base64 encoded for serialization)
48
+ attribute :iv, :string
49
+
50
+ # Password check value (first 8 bytes of encrypted data)
51
+ # Used to verify password before full decryption
52
+ attribute :check_value, :string
53
+
54
+ # Validate header
55
+ #
56
+ # @raise [ArgumentError] If validation fails
57
+ def validate!
58
+ if version != 0
59
+ raise ArgumentError, "Only AES-256 (version 0) is supported"
60
+ end
61
+
62
+ if kdf_iterations < 65_536 || kdf_iterations > 1_048_576
63
+ raise ArgumentError,
64
+ "KDF iterations must be between 65,536 and 1,048,576"
65
+ end
66
+
67
+ # Validate salt (base64 decoded should be 16 bytes)
68
+ decoded_salt = Base64.strict_decode64(salt)
69
+ if decoded_salt.bytesize != 16
70
+ raise ArgumentError, "Salt must be 16 bytes"
71
+ end
72
+
73
+ # Validate IV (base64 decoded should be 16 bytes)
74
+ decoded_iv = Base64.strict_decode64(iv)
75
+ if decoded_iv.bytesize != 16
76
+ raise ArgumentError, "IV must be 16 bytes"
77
+ end
78
+ rescue ArgumentError => e
79
+ raise ArgumentError, "Invalid encryption header: #{e.message}"
80
+ end
81
+
82
+ # Get salt as binary
83
+ #
84
+ # @return [String] 16-byte binary salt
85
+ def salt_binary
86
+ Base64.strict_decode64(salt)
87
+ end
88
+
89
+ # Get IV as binary
90
+ #
91
+ # @return [String] 16-byte binary IV
92
+ def iv_binary
93
+ Base64.strict_decode64(iv)
94
+ end
95
+
96
+ # Set salt from binary
97
+ #
98
+ # @param binary_salt [String] 16-byte binary salt
99
+ def salt_binary=(binary_salt)
100
+ self.salt = Base64.strict_encode64(binary_salt)
101
+ end
102
+
103
+ # Set IV from binary
104
+ #
105
+ # @param binary_iv [String] 16-byte binary IV
106
+ def iv_binary=(binary_iv)
107
+ self.iv = Base64.strict_encode64(binary_iv)
108
+ end
109
+ end
110
+ end
111
+ end
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,166 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "key_derivation"
4
+ require_relative "aes256_cbc"
5
+ require_relative "encryption_header"
6
+
7
+ module Omnizip
8
+ module Formats
9
+ module Rar
10
+ module Rar5
11
+ module Encryption
12
+ # Encryption manager for RAR5 archives
13
+ #
14
+ # This manager coordinates the encryption process:
15
+ # 1. Generate salt and IV
16
+ # 2. Derive key from password using PBKDF2
17
+ # 3. Encrypt file data with AES-256-CBC
18
+ # 4. Create encryption header with metadata
19
+ #
20
+ # @example Encrypt file data
21
+ # manager = EncryptionManager.new("SecurePassword", kdf_iterations: 262_144)
22
+ # result = manager.encrypt_file_data(file_data)
23
+ # # result[:encrypted_data] = encrypted bytes
24
+ # # result[:header] = EncryptionHeader with salt, IV, etc.
25
+ class EncryptionManager
26
+ # @return [String] Password for encryption
27
+ attr_reader :password
28
+
29
+ # @return [Integer] PBKDF2 iteration count
30
+ attr_reader :kdf_iterations
31
+
32
+ # @return [String, nil] Optional pre-generated salt
33
+ attr_reader :salt
34
+
35
+ # @return [String, nil] Optional pre-generated IV
36
+ attr_reader :iv
37
+
38
+ # Initialize encryption manager
39
+ #
40
+ # @param password [String] Encryption password
41
+ # @param options [Hash] Options
42
+ # @option options [Integer] :kdf_iterations PBKDF2 iterations (default: 262,144)
43
+ # @option options [String] :salt Pre-generated salt (16 bytes, optional)
44
+ # @option options [String] :iv Pre-generated IV (16 bytes, optional)
45
+ def initialize(password, options = {})
46
+ @password = password
47
+ @kdf_iterations = options[:kdf_iterations] || KeyDerivation::DEFAULT_ITERATIONS
48
+ @salt = options[:salt]
49
+ @iv = options[:iv]
50
+
51
+ validate_password!
52
+ validate_iterations!
53
+ end
54
+
55
+ # Encrypt file data
56
+ #
57
+ # @param plaintext [String] File data to encrypt
58
+ # @return [Hash] Encryption result
59
+ # @option result [String] :encrypted_data Encrypted bytes
60
+ # @option result [EncryptionHeader] :header Encryption metadata
61
+ # @option result [String] :key Derived encryption key (for debugging)
62
+ def encrypt_file_data(plaintext)
63
+ # Generate or use provided salt and IV
64
+ salt = @salt || KeyDerivation.generate_salt
65
+ iv = @iv || Aes256Cbc.generate_iv
66
+
67
+ # Derive key from password
68
+ key = KeyDerivation.derive_key(@password, salt, @kdf_iterations)
69
+
70
+ # Encrypt data
71
+ cipher = Aes256Cbc.new(key, iv)
72
+ encrypted = cipher.encrypt(plaintext)
73
+
74
+ # Create encryption header
75
+ header = create_encryption_header(salt, iv)
76
+
77
+ {
78
+ encrypted_data: encrypted,
79
+ header: header,
80
+ key: key, # Include for verification if needed
81
+ }
82
+ end
83
+
84
+ # Decrypt file data
85
+ #
86
+ # @param ciphertext [String] Encrypted data
87
+ # @param header [EncryptionHeader] Encryption metadata
88
+ # @return [String] Decrypted data
89
+ # @raise [ArgumentError] If password incorrect
90
+ def decrypt_file_data(ciphertext, header)
91
+ # Extract salt and IV
92
+ salt = header.salt_binary
93
+ iv = header.iv_binary
94
+
95
+ # Derive key from password
96
+ key = KeyDerivation.derive_key(@password, salt,
97
+ header.kdf_iterations)
98
+
99
+ # Decrypt data
100
+ cipher = Aes256Cbc.new(key, iv)
101
+ cipher.decrypt(ciphertext)
102
+ rescue OpenSSL::Cipher::CipherError => e
103
+ raise ArgumentError,
104
+ "Decryption failed (wrong password?): #{e.message}"
105
+ end
106
+
107
+ # Verify password without full decryption
108
+ #
109
+ # This checks if the derived key can decrypt the check value.
110
+ # Faster than decrypting entire file.
111
+ #
112
+ # @param header [EncryptionHeader] Encryption metadata
113
+ # @return [Boolean] true if password correct
114
+ def verify_password(_header)
115
+ # For now, we'll need a small encrypted sample to verify
116
+ # This is a simplified check - full implementation would use
117
+ # the check_value field properly
118
+ true # Placeholder
119
+ rescue StandardError
120
+ false
121
+ end
122
+
123
+ private
124
+
125
+ # Create encryption header
126
+ #
127
+ # @param salt [String] 16-byte salt
128
+ # @param iv [String] 16-byte IV
129
+ # @return [EncryptionHeader] Header object
130
+ def create_encryption_header(salt, iv)
131
+ header = EncryptionHeader.new
132
+ header.version = 0 # AES-256
133
+ header.kdf_iterations = @kdf_iterations
134
+ header.salt_binary = salt
135
+ header.iv_binary = iv
136
+ header.check_value = "" # Placeholder for password check
137
+ header
138
+ end
139
+
140
+ # Validate password
141
+ #
142
+ # @raise [ArgumentError] If password invalid
143
+ def validate_password!
144
+ if @password.nil? || @password.empty?
145
+ raise ArgumentError, "Password cannot be empty"
146
+ end
147
+ end
148
+
149
+ # Validate iteration count
150
+ #
151
+ # @raise [ArgumentError] If iterations invalid
152
+ def validate_iterations!
153
+ min = KeyDerivation::MIN_ITERATIONS
154
+ max = KeyDerivation::MAX_ITERATIONS
155
+
156
+ return if @kdf_iterations.between?(min, max)
157
+
158
+ raise ArgumentError,
159
+ "KDF iterations must be between #{min} and #{max}"
160
+ end
161
+ end
162
+ end
163
+ end
164
+ end
165
+ end
166
+ end
@@ -0,0 +1,97 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "openssl"
4
+ require "securerandom"
5
+
6
+ module Omnizip
7
+ module Formats
8
+ module Rar
9
+ module Rar5
10
+ module Encryption
11
+ # RAR5 password-based key derivation
12
+ #
13
+ # RAR5 uses PBKDF2-HMAC-SHA256 for key derivation, which is more
14
+ # secure than 7-Zip's iterative SHA-256 approach.
15
+ #
16
+ # The process:
17
+ # 1. Generate random salt (16 bytes)
18
+ # 2. Apply PBKDF2-HMAC-SHA256 with configurable iterations
19
+ # 3. Derive 32-byte AES-256 key
20
+ #
21
+ # @example Derive key from password
22
+ # salt = SecureRandom.random_bytes(16)
23
+ # key = KeyDerivation.derive_key("password", salt, 262_144)
24
+ class KeyDerivation
25
+ # Default PBKDF2 iterations (262,144 = 2^18)
26
+ # This provides good security while maintaining reasonable performance
27
+ DEFAULT_ITERATIONS = 262_144
28
+
29
+ # Minimum iterations (2^16 = 65,536)
30
+ MIN_ITERATIONS = 65_536
31
+
32
+ # Maximum iterations (2^20 = 1,048,576)
33
+ MAX_ITERATIONS = 1_048_576
34
+
35
+ # Salt size (16 bytes)
36
+ SALT_SIZE = 16
37
+
38
+ # Key size (32 bytes for AES-256)
39
+ KEY_SIZE = 32
40
+
41
+ # Derive AES-256 key from password using PBKDF2-HMAC-SHA256
42
+ #
43
+ # @param password [String] User password
44
+ # @param salt [String] Random salt (16 bytes)
45
+ # @param iterations [Integer] Number of PBKDF2 iterations
46
+ # @return [String] 32-byte AES-256 key
47
+ # @raise [ArgumentError] If password empty or salt wrong size
48
+ def self.derive_key(password, salt, iterations = DEFAULT_ITERATIONS)
49
+ validate_inputs(password, salt, iterations)
50
+
51
+ # PBKDF2-HMAC-SHA256
52
+ OpenSSL::PKCS5.pbkdf2_hmac(
53
+ password,
54
+ salt,
55
+ iterations,
56
+ KEY_SIZE,
57
+ OpenSSL::Digest.new("SHA256"),
58
+ )
59
+ end
60
+
61
+ # Generate random salt
62
+ #
63
+ # @return [String] 16-byte random salt
64
+ def self.generate_salt
65
+ SecureRandom.random_bytes(SALT_SIZE)
66
+ end
67
+
68
+ # Validate key derivation inputs
69
+ #
70
+ # @param password [String] Password to validate
71
+ # @param salt [String] Salt to validate
72
+ # @param iterations [Integer] Iteration count to validate
73
+ # @return [void]
74
+ # @raise [ArgumentError] If inputs are invalid
75
+ def self.validate_inputs(password, salt, iterations)
76
+ if password.nil? || password.empty?
77
+ raise ArgumentError, "Password cannot be empty"
78
+ end
79
+
80
+ if salt.bytesize != SALT_SIZE
81
+ raise ArgumentError,
82
+ "Salt must be #{SALT_SIZE} bytes, got #{salt.bytesize}"
83
+ end
84
+
85
+ return if iterations.between?(MIN_ITERATIONS, MAX_ITERATIONS)
86
+
87
+ raise ArgumentError,
88
+ "Iterations must be between #{MIN_ITERATIONS} and #{MAX_ITERATIONS}"
89
+ end
90
+
91
+ private_class_method :validate_inputs
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,187 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "vint"
4
+ require_relative "crc32"
5
+
6
+ module Omnizip
7
+ module Formats
8
+ module Rar
9
+ module Rar5
10
+ # Header types
11
+ HEADER_TYPE_MAIN = 1
12
+ HEADER_TYPE_FILE = 2
13
+ HEADER_TYPE_SERVICE = 3
14
+ HEADER_TYPE_ENCRYPTION = 4
15
+ HEADER_TYPE_END = 5
16
+
17
+ # Header flags (common)
18
+ FLAG_EXTRA_AREA = 0x0001
19
+ FLAG_DATA_AREA = 0x0002
20
+
21
+ # Base class for RAR5 headers
22
+ class Header
23
+ attr_reader :type, :flags, :extra_area, :data_area_size, :header_data
24
+
25
+ def initialize(type, flags: 0, extra_area: nil, data_area_size: nil,
26
+ header_data: "")
27
+ @type = type
28
+ @flags = flags
29
+ @flags |= FLAG_EXTRA_AREA if extra_area
30
+ @flags |= FLAG_DATA_AREA if data_area_size
31
+ @extra_area = extra_area
32
+ @data_area_size = data_area_size
33
+ @header_data = header_data
34
+ end
35
+
36
+ def encode
37
+ # Build header without CRC
38
+ header_bytes = build_header_bytes
39
+
40
+ # Calculate CRC32
41
+ crc = CRC32.calculate(header_bytes.pack("C*"))
42
+
43
+ # Prepend CRC (little-endian)
44
+ [crc].pack("V") + header_bytes.pack("C*")
45
+ end
46
+
47
+ private
48
+
49
+ def build_header_bytes
50
+ bytes = []
51
+
52
+ # Header size (placeholder - will calculate)
53
+ size_bytes = []
54
+
55
+ # Type
56
+ type_bytes = VINT.encode(@type)
57
+ size_bytes.concat(type_bytes)
58
+
59
+ # Flags
60
+ flags_bytes = VINT.encode(@flags)
61
+ size_bytes.concat(flags_bytes)
62
+
63
+ # Extra area size (if present)
64
+ if @flags.anybits?(FLAG_EXTRA_AREA)
65
+ extra_size_bytes = VINT.encode(@extra_area.bytesize)
66
+ size_bytes.concat(extra_size_bytes)
67
+ end
68
+
69
+ # Data area size (if present)
70
+ if @flags.anybits?(FLAG_DATA_AREA)
71
+ data_size_bytes = VINT.encode(@data_area_size)
72
+ size_bytes.concat(data_size_bytes)
73
+ end
74
+
75
+ # Header data
76
+ size_bytes.concat(@header_data.bytes)
77
+
78
+ # Extra area
79
+ size_bytes.concat(@extra_area.bytes) if @extra_area
80
+
81
+ # Calculate total header size (excluding CRC)
82
+ header_size = size_bytes.size
83
+ header_size_vint = VINT.encode(header_size)
84
+
85
+ # Build final header
86
+ bytes.concat(header_size_vint)
87
+ bytes.concat(size_bytes)
88
+
89
+ bytes
90
+ end
91
+ end
92
+
93
+ # Main archive header
94
+ class MainHeader < Header
95
+ def initialize(flags: 0)
96
+ # Main header has no data
97
+ super(HEADER_TYPE_MAIN, flags: flags)
98
+ end
99
+ end
100
+
101
+ # File header
102
+ class FileHeader < Header
103
+ # File header flags
104
+ FILE_HAS_ATTRIBUTES = 0x0001
105
+ FILE_HAS_MTIME = 0x0002
106
+ FILE_HAS_CRC32 = 0x0004
107
+
108
+ def initialize(filename:, file_size:, compressed_size:,
109
+ compression_method: 0, flags: 0, mtime: nil, crc32: nil, extra_area: nil)
110
+ # Build file flags based on what's provided
111
+ file_flags = 0
112
+ file_flags |= FILE_HAS_MTIME if mtime
113
+ file_flags |= FILE_HAS_CRC32 if crc32
114
+
115
+ # Build header data with file information
116
+ data = build_file_data(filename, file_size, compressed_size,
117
+ file_flags, compression_method, mtime, crc32)
118
+ super(HEADER_TYPE_FILE, flags: flags, data_area_size: compressed_size, header_data: data, extra_area: extra_area)
119
+ end
120
+
121
+ private
122
+
123
+ def build_file_data(filename, file_size, _compressed_size,
124
+ file_flags, compression_method, mtime, crc32)
125
+ data = []
126
+
127
+ # File flags (VINT)
128
+ data.concat(VINT.encode(file_flags))
129
+
130
+ # Unp size (uncompressed size, VINT)
131
+ data.concat(VINT.encode(file_size))
132
+
133
+ # Attributes (VINT) - ALWAYS present in RAR5
134
+ # Use 0x2483 from official RAR (standard regular file with correct permissions)
135
+ data.concat(VINT.encode(0x2483))
136
+
137
+ # Mystery VINT with value 0x02 - observed in official RAR
138
+ # This appears after attributes in official archives - ALWAYS present
139
+ data.concat(VINT.encode(0x02))
140
+
141
+ # mtime (optional) - only if FILE_HAS_MTIME flag is set
142
+ # RAR5 stores mtime as Unix timestamp (seconds since epoch) in DOS format
143
+ # Format: 4 bytes little-endian (NOT a VINT)
144
+ if file_flags.anybits?(FILE_HAS_MTIME) && mtime
145
+ # Convert Time to Unix timestamp (seconds since 1970-01-01 00:00:00 UTC)
146
+ unix_time = mtime.to_i
147
+ # Pack as 32-bit unsigned little-endian
148
+ data.concat([unix_time].pack("V").bytes)
149
+ end
150
+
151
+ # Data CRC32 (optional) - only if FILE_HAS_CRC32 flag is set
152
+ # Format: 4 bytes little-endian (NOT a VINT)
153
+ if file_flags.anybits?(FILE_HAS_CRC32) && crc32
154
+ # Pack as 32-bit unsigned little-endian
155
+ data.concat([crc32].pack("V").bytes)
156
+ end
157
+
158
+ # Compression info (VINT)
159
+ # Bits 0-5: method (0=STORE, 1-5=LZMA with different levels)
160
+ # Bits 6+: version
161
+ data.concat(VINT.encode(compression_method))
162
+
163
+ # Host OS (VINT) - 1 = Unix
164
+ data.concat(VINT.encode(1)) # Unix
165
+
166
+ # Name length (VINT)
167
+ name_bytes = filename.encode("UTF-8").bytes
168
+ data.concat(VINT.encode(name_bytes.size))
169
+
170
+ # Name
171
+ data.concat(name_bytes)
172
+
173
+ data.pack("C*")
174
+ end
175
+ end
176
+
177
+ # End of archive header
178
+ class EndHeader < Header
179
+ def initialize
180
+ # End header is minimal
181
+ super(HEADER_TYPE_END, flags: 0)
182
+ end
183
+ end
184
+ end
185
+ end
186
+ end
187
+ end