1 /*-
2 * Copyright (c) 2026 GeorgH93
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
18 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26 #include "test.h"
27
28 static const char *password = "test_password_zipx";
29
30 /* 512 bytes of test data — large enough to exercise decompression. */
31 static const char file_data[] =
32 "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz\n"
33 "The quick brown fox jumps over the lazy dog. Pack my box with five\n"
34 "dozen liquor jugs. How vexingly quick daft zebras jump! Bright\n"
35 "vixens jump; dozy fowl quack. Jackdaws love my big sphinx of\n"
36 "quartz. The five boxing wizards jump quickly. Amazingly few\n"
37 "discotheques provide jukeboxes. Crazy Frederick bought many very\n"
38 "exquisite opal jewels. We promptly judged antique ivory buckles\n"
39 "for the next prize. Sixty zippers were quickly picked from the bag.\n";
40 static const size_t file_size = sizeof(file_data) - 1;
41
42 struct streaming_buffer {
43 const char *data;
44 la_ssize_t len;
45 size_t pos;
46 int served;
47 };
48
read_streaming_buffer(struct archive * a,void * _client_data,const void ** buffer)49 static la_ssize_t read_streaming_buffer(struct archive *a, void *_client_data, const void **buffer) {
50 (void)a;
51 struct streaming_buffer *buff = _client_data;
52 if (buff->served) return 0;
53 *buffer = buff->data;
54 buff->served = 1;
55 return buff->len;
56 }
57
58 static void
validate_entry_read(struct archive * a,const int streaming_reader)59 validate_entry_read(struct archive *a, const int streaming_reader)
60 {
61 struct archive_entry *ae;
62 size_t total_read;
63 ssize_t r;
64 char readbuf[1024];
65
66 assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
67 assertEqualString("encrypted_file.txt", archive_entry_pathname(ae));
68 if (!streaming_reader)
69 assertEqualInt((int)file_size, (int)archive_entry_size(ae));
70 else
71 assertEqualInt(archive_entry_size(ae), 0);
72 assertEqualInt(1, archive_entry_is_data_encrypted(ae));
73
74 total_read = 0;
75 for (;;) {
76 r = archive_read_data(a, readbuf, sizeof(readbuf));
77 if (r <= 0) {
78 if (r < 0) {
79 failure("archive_read_data returned %d: %s",
80 (int)r, archive_error_string(a));
81 assertEqualInt(r > 0, 1);
82 }
83 break;
84 }
85 assertEqualMem(readbuf, file_data + total_read, (size_t)r);
86 total_read += (size_t)r;
87 }
88 assertEqualInt((int)total_read, (int)file_size);
89 }
90
91 static void
test_encrypted_zipx_read_mem(char * buff,size_t used)92 test_encrypted_zipx_read_mem(char* buff, size_t used)
93 {
94 struct archive *a;
95 struct archive_entry *ae;
96 char readbuf[1024];
97
98 /* ---- Read back without password — should fail ---- */
99 assert((a = archive_read_new()) != NULL);
100 assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a));
101 assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a));
102 assertEqualIntA(a, ARCHIVE_OK, archive_read_open_memory(a, buff, used));
103 assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
104 assertEqualString("encrypted_file.txt", archive_entry_pathname(ae));
105 assertEqualInt(1, archive_entry_is_data_encrypted(ae));
106 assertEqualInt(0, archive_entry_is_metadata_encrypted(ae));
107 assertEqualInt(ARCHIVE_FAILED, archive_read_data(a, readbuf, sizeof(readbuf)));
108 assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
109 assertEqualInt(ARCHIVE_OK, archive_read_free(a));
110
111 /* ---- Read back with correct password ---- */
112 assert((a = archive_read_new()) != NULL);
113 assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a));
114 assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a));
115 assertEqualIntA(a, ARCHIVE_OK, archive_read_add_passphrase(a, password));
116 assertEqualIntA(a, ARCHIVE_OK, archive_read_open_memory(a, buff, used));
117
118 validate_entry_read(a, 0);
119
120 assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
121 assertEqualInt(ARCHIVE_OK, archive_read_free(a));
122
123 /* ---- Seeking reader with correct password ---- */
124 assert((a = archive_read_new()) != NULL);
125 assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a));
126 assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a));
127 assertEqualIntA(a, ARCHIVE_OK, archive_read_add_passphrase(a, password));
128 assertEqualIntA(a, ARCHIVE_OK, read_open_memory_seek(a, buff, used, 7));
129
130 validate_entry_read(a, 0);
131
132 assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
133 assertEqualInt(ARCHIVE_OK, archive_read_free(a));
134 }
135
136 static void
test_encrypted_zipx_read_callback(const char * buff,const size_t used)137 test_encrypted_zipx_read_callback(const char* buff, const size_t used)
138 {
139 struct archive *a;
140 struct streaming_buffer stream_buffer = { buff, used, 0, 0 };
141
142 /* ---- Read back with correct password ---- */
143 assert((a = archive_read_new()) != NULL);
144 assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_zip(a));
145 assertEqualIntA(a, ARCHIVE_OK, archive_read_add_passphrase(a, password));
146 /* NOTE: archive_read_open2 with read callback only -> NON-seekable. */
147 assertEqualIntA(a, ARCHIVE_OK, archive_read_open2(a, &stream_buffer, NULL, read_streaming_buffer, NULL, NULL));
148 validate_entry_read(a, 1);
149 assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
150 assertEqualInt(ARCHIVE_OK, archive_read_free(a));
151 }
152
153 static void
test_encrypted_zipx(const char * compression_name,const int streaming_entry)154 test_encrypted_zipx(const char *compression_name, const int streaming_entry)
155 {
156 struct archive *a;
157 struct archive_entry *ae;
158 size_t used;
159 size_t buffsize = 1000000;
160 char *buff;
161
162 buff = malloc(buffsize);
163 assert(buff != NULL);
164
165 /* ---- Write encrypted zipx archive ---- */
166 assert((a = archive_write_new()) != NULL);
167 assertEqualIntA(a, ARCHIVE_OK, archive_write_set_format_zip(a));
168 assertEqualIntA(a, ARCHIVE_OK, archive_write_add_filter_none(a));
169
170 if (ARCHIVE_OK != archive_write_set_options(a,
171 "zip:encryption=aes256")) {
172 skipping("This system does not have cryptographic library");
173 archive_write_free(a);
174 free(buff);
175 return;
176 }
177
178 if (ARCHIVE_OK != archive_write_set_options(a, compression_name)) {
179 skipping("%s is not supported on this platform", compression_name);
180 archive_write_free(a);
181 free(buff);
182 return;
183 }
184
185 assertEqualIntA(a, ARCHIVE_OK, archive_write_set_passphrase(a, password));
186 assertEqualIntA(a, ARCHIVE_OK, archive_write_set_options(a, "zip:experimental"));
187 assertEqualIntA(a, ARCHIVE_OK, archive_write_open_memory(a, buff, buffsize, &used));
188
189 assert((ae = archive_entry_new()) != NULL);
190 archive_entry_set_mtime(ae, 1, 0);
191 archive_entry_copy_pathname(ae, "encrypted_file.txt");
192 archive_entry_set_mode(ae, AE_IFREG | 0644);
193 if (!streaming_entry)
194 archive_entry_set_size(ae, file_size);
195 assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae));
196 assertEqualInt(file_size, archive_write_data(a, file_data, file_size));
197 archive_entry_free(ae);
198 assertEqualIntA(a, ARCHIVE_OK, archive_write_close(a));
199 assertEqualInt(ARCHIVE_OK, archive_write_free(a));
200
201 test_encrypted_zipx_read_mem(buff, used);
202 test_encrypted_zipx_read_callback(buff, used);
203
204 free(buff);
205 }
206
DEFINE_TEST(test_read_format_zip_deflate_encrypted)207 DEFINE_TEST(test_read_format_zip_deflate_encrypted)
208 {
209 test_encrypted_zipx("zip:compression=deflate", 0);
210 }
211
DEFINE_TEST(test_read_format_zip_zipx_bzip2_encrypted)212 DEFINE_TEST(test_read_format_zip_zipx_bzip2_encrypted)
213 {
214 test_encrypted_zipx("zip:compression=bzip2", 0);
215 }
216
DEFINE_TEST(test_read_format_zip_zipx_lzma_encrypted)217 DEFINE_TEST(test_read_format_zip_zipx_lzma_encrypted)
218 {
219 test_encrypted_zipx("zip:compression=lzma", 0);
220 }
221
DEFINE_TEST(test_read_format_zip_zipx_xz_encrypted)222 DEFINE_TEST(test_read_format_zip_zipx_xz_encrypted)
223 {
224 test_encrypted_zipx("zip:compression=xz", 0);
225 }
226
DEFINE_TEST(test_read_format_zip_zipx_zstd_encrypted)227 DEFINE_TEST(test_read_format_zip_zipx_zstd_encrypted)
228 {
229 test_encrypted_zipx("zip:compression=zstd", 0);
230 }
231
DEFINE_TEST(test_read_format_zip_deflate_encrypted_streaming)232 DEFINE_TEST(test_read_format_zip_deflate_encrypted_streaming)
233 {
234 test_encrypted_zipx("zip:compression=deflate", 1);
235 }
236
DEFINE_TEST(test_read_format_zip_zipx_bzip2_encrypted_streaming)237 DEFINE_TEST(test_read_format_zip_zipx_bzip2_encrypted_streaming)
238 {
239 test_encrypted_zipx("zip:compression=bzip2", 1);
240 }
241
DEFINE_TEST(test_read_format_zip_zipx_lzma_encrypted_streaming)242 DEFINE_TEST(test_read_format_zip_zipx_lzma_encrypted_streaming)
243 {
244 test_encrypted_zipx("zip:compression=lzma", 1);
245 }
246
DEFINE_TEST(test_read_format_zip_zipx_xz_encrypted_streaming)247 DEFINE_TEST(test_read_format_zip_zipx_xz_encrypted_streaming)
248 {
249 test_encrypted_zipx("zip:compression=xz", 1);
250 }
251
DEFINE_TEST(test_read_format_zip_zipx_zstd_encrypted_streaming)252 DEFINE_TEST(test_read_format_zip_zipx_zstd_encrypted_streaming)
253 {
254 test_encrypted_zipx("zip:compression=zstd", 1);
255 }
256
DEFINE_TEST(test_read_format_zip_ppmd8_aes256_streaming)257 DEFINE_TEST(test_read_format_zip_ppmd8_aes256_streaming)
258 {
259 const char *refname = "test_read_format_zip_ppmd8_aes256_streaming.zipx";
260 size_t used;
261 char buff[600];
262 extract_reference_file(refname);
263 FILE *f = fopen(refname, "rb");
264 used = fread(buff, 1, sizeof(buff), f);
265 fclose(f);
266 test_encrypted_zipx_read_mem(buff, used);
267 test_encrypted_zipx_read_callback(buff, used);
268 }
269