Branch data Line data Source code
1 : : /* -*- mode: C -*-
2 : : *
3 : : * File: pdf-stm-f-aesv2.c
4 : : * Date: Sun Dec 14 20:13:53 2008
5 : : *
6 : : * GNU PDF Library - AESV2 stream filter
7 : : *
8 : : */
9 : :
10 : : /* Copyright (C) 2008, 2009 Free Software Foundation, Inc. */
11 : :
12 : : /* This program is free software: you can redistribute it and/or modify
13 : : * it under the terms of the GNU General Public License as published by
14 : : * the Free Software Foundation, either version 3 of the License, or
15 : : * (at your option) any later version.
16 : : *
17 : : * This program is distributed in the hope that it will be useful,
18 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 : : * GNU General Public License for more details.
21 : : *
22 : : * You should have received a copy of the GNU General Public License
23 : : * along with this program. If not, see <http://www.gnu.org/licenses/>.
24 : : */
25 : :
26 : : #include <config.h>
27 : :
28 : : #include <stdlib.h>
29 : : #include <string.h>
30 : :
31 : : #include <pdf-types.h>
32 : : #include <pdf-types-buffer.h>
33 : : #include <pdf-hash.h>
34 : : #include <pdf-alloc.h>
35 : : #include <pdf-crypt.h>
36 : : #include <pdf-stm-f-aesv2.h>
37 : : #include <pdf-hash-helper.h>
38 : :
39 : : /* Define AESv2 encoder */
40 : 10 : PDF_STM_FILTER_DEFINE (pdf_stm_f_aesv2enc_get,
41 : : stm_f_aesv2_init,
42 : : stm_f_aesv2enc_apply,
43 : : stm_f_aesv2_deinit);
44 : :
45 : : /* Define AESv2 decoder */
46 : 10 : PDF_STM_FILTER_DEFINE (pdf_stm_f_aesv2dec_get,
47 : : stm_f_aesv2_init,
48 : : stm_f_aesv2dec_apply,
49 : : stm_f_aesv2_deinit);
50 : :
51 : : #define AESV2_CACHE_SIZE 16
52 : : #define AESv2_PARAM_KEY "Key"
53 : : #define AESv2_PARAM_KEY_SIZE "KeySize"
54 : :
55 : : /* Encryption and decryption */
56 : : typedef enum {
57 : : PDF_STM_F_AESV2_MODE_ENCODE,
58 : : PDF_STM_F_AESV2_MODE_DECODE
59 : : } pdf_stm_f_aesv2_mode_e;
60 : :
61 : : /* Internal state */
62 : : struct pdf_stm_f_aesv2_s
63 : : {
64 : : pdf_crypt_cipher_t *cipher;
65 : :
66 : : pdf_buffer_t *in_cache;
67 : : pdf_buffer_t *out_cache;
68 : :
69 : : pdf_char_t *key;
70 : : pdf_size_t keysize;
71 : : };
72 : :
73 : : /* Common implementation */
74 : :
75 : : static pdf_bool_t
76 : 20 : stm_f_aesv2_init (const pdf_hash_t *params,
77 : : void **state,
78 : : pdf_error_t **error)
79 : : {
80 : : struct pdf_stm_f_aesv2_s *filter_state;
81 : : const pdf_char_t *key;
82 : : pdf_size_t keysize;
83 : :
84 : : /* We demand all parameters are present */
85 [ + - ][ + - ]: 20 : if (!params ||
[ - + ]
86 : : !pdf_hash_key_p (params, AESv2_PARAM_KEY) ||
87 : : !pdf_hash_key_p (params, AESv2_PARAM_KEY))
88 : : {
89 [ # # ][ # # ]: 0 : pdf_set_error (error,
[ # # ][ # # ]
90 : : PDF_EDOMAIN_BASE_STM,
91 : : PDF_EBADDATA,
92 : : "cannot initialize AESv2 encoder/decoder: "
93 : : "parameters missing ('Key': %s, 'KeySize': %s)",
94 : : ((params && pdf_hash_key_p (params, AESv2_PARAM_KEY)) ?
95 : : "available" : "missing"),
96 : : ((params && pdf_hash_key_p (params, AESv2_PARAM_KEY_SIZE)) ?
97 : : "available" : "missing"));
98 : 0 : return PDF_FALSE;
99 : : }
100 : :
101 : 20 : filter_state = pdf_alloc (sizeof (struct pdf_stm_f_aesv2_s));
102 [ - + ]: 20 : if (!filter_state)
103 : : {
104 : 0 : pdf_set_error (error,
105 : : PDF_EDOMAIN_BASE_STM,
106 : : PDF_ENOMEM,
107 : : "cannot create AESv2 encoder/decoder internal state: "
108 : : "couldn't allocate %lu bytes",
109 : : (unsigned long)sizeof (struct pdf_stm_f_aesv2_s));
110 : 0 : return PDF_FALSE;
111 : : }
112 : :
113 : : /* Initialize cache buffers */
114 : 20 : filter_state->in_cache = pdf_buffer_new (AESV2_CACHE_SIZE, error);
115 [ - + ]: 20 : if (!(filter_state->in_cache))
116 : : {
117 : 0 : stm_f_aesv2_deinit (filter_state);
118 : 0 : return PDF_FALSE;
119 : : }
120 : 20 : filter_state->out_cache = pdf_buffer_new (AESV2_CACHE_SIZE, error);
121 [ - + ]: 20 : if (!(filter_state->out_cache))
122 : : {
123 : 0 : stm_f_aesv2_deinit (filter_state);
124 : 0 : return PDF_FALSE;
125 : : }
126 : :
127 : : /* Note that Key may NOT be NUL-terminated */
128 : 20 : key = pdf_hash_get_value (params, AESv2_PARAM_KEY);
129 : 20 : keysize = pdf_hash_get_size (params, AESv2_PARAM_KEY_SIZE);
130 : :
131 : : /* Keep a copy of the key in the filter */
132 : 20 : filter_state->key = (pdf_char_t *)pdf_alloc (keysize);
133 [ - + ]: 20 : if (!filter_state->key)
134 : : {
135 : 0 : pdf_set_error (error,
136 : : PDF_EDOMAIN_BASE_STM,
137 : : PDF_ENOMEM,
138 : : "cannot copy AESv2 key: "
139 : : "couldn't allocate %lu bytes",
140 : : (unsigned long)keysize);
141 : 0 : stm_f_aesv2_deinit (filter_state);
142 : 0 : return PDF_FALSE;
143 : : }
144 : 20 : filter_state->keysize = keysize;
145 : 20 : memcpy (filter_state->key, key, keysize);
146 : :
147 : 20 : filter_state->cipher = pdf_crypt_cipher_new (PDF_CRYPT_CIPHER_ALGO_AESV2, error);
148 [ - + ]: 20 : if (!filter_state->cipher)
149 : : {
150 : 0 : stm_f_aesv2_deinit (filter_state);
151 : 0 : return PDF_FALSE;
152 : : }
153 : :
154 [ - + ]: 20 : if (!pdf_crypt_cipher_set_key (filter_state->cipher,
155 : : filter_state->key,
156 : : filter_state->keysize,
157 : : error))
158 : : {
159 : 0 : stm_f_aesv2_deinit (filter_state);
160 : 0 : return PDF_FALSE;
161 : : }
162 : :
163 : 20 : *state = filter_state;
164 : 20 : return PDF_TRUE;
165 : : }
166 : :
167 : : static void
168 : 20 : stm_f_aesv2_deinit (void *state)
169 : : {
170 : 20 : struct pdf_stm_f_aesv2_s *filter_state = state;
171 : :
172 [ + - ]: 20 : if (filter_state->cipher)
173 : 20 : pdf_crypt_cipher_destroy (filter_state->cipher);
174 [ + - ]: 20 : if (filter_state->in_cache)
175 : 20 : pdf_buffer_destroy (filter_state->in_cache);
176 [ + - ]: 20 : if (filter_state->out_cache)
177 : 20 : pdf_buffer_destroy (filter_state->out_cache);
178 [ + - ]: 20 : if (filter_state->key)
179 : 20 : pdf_dealloc (filter_state->key);
180 : 20 : pdf_dealloc (state);
181 : 20 : }
182 : :
183 : : static enum pdf_stm_filter_apply_status_e
184 : 60 : stm_f_aesv2_apply (pdf_stm_f_aesv2_mode_e mode,
185 : : void *state,
186 : : pdf_buffer_t *in,
187 : : pdf_buffer_t *out,
188 : : pdf_bool_t finish,
189 : : pdf_error_t **error)
190 : : {
191 : 60 : struct pdf_stm_f_aesv2_s *filter_state = state;
192 : 60 : pdf_crypt_cipher_t *cipher = filter_state->cipher;
193 : 60 : pdf_buffer_t *in_cache = filter_state->in_cache;
194 : 60 : pdf_buffer_t *out_cache = filter_state->out_cache;
195 : :
196 : : while (PDF_TRUE)
197 : : {
198 : : pdf_size_t in_size;
199 : : pdf_size_t out_size;
200 : : pdf_size_t in_cache_size;
201 : : pdf_size_t out_cache_size;
202 : : pdf_size_t bytes_to_read;
203 : : pdf_size_t bytes_to_write;
204 : :
205 [ - + ]: 100 : PDF_ASSERT (in->wp >= in->rp);
206 [ - + ]: 100 : PDF_ASSERT (out->size >= out->wp);
207 : :
208 : : /* TODO: Shouldn't this be like this?
209 : : * in_size = in->size - in->rp;
210 : : */
211 : 100 : in_size = in->wp - in->rp;
212 : 100 : in_cache_size = in_cache->size - in_cache->wp;
213 : 100 : bytes_to_read = PDF_MIN (in_size, in_cache_size);
214 : :
215 : : /* Read bytes from IN and fill IN_CACHE */
216 : 100 : memcpy (in_cache->data + in_cache->wp,
217 : : in->data + in->rp,
218 : : bytes_to_read);
219 : :
220 : 100 : in_cache->wp += bytes_to_read;
221 : 100 : in->rp += bytes_to_read;
222 : :
223 : : /* If we cannot fill all IN_CACHE... */
224 [ + + ]: 100 : if (!pdf_buffer_full_p (in_cache))
225 : : {
226 [ + + ][ + + ]: 50 : if (finish &&
[ - + ]
227 : : mode == PDF_STM_F_AESV2_MODE_DECODE &&
228 : 10 : in_cache->wp > 0)
229 : : {
230 : : /* TODO: Better error explanation */
231 : 0 : pdf_set_error (error,
232 : : PDF_EDOMAIN_BASE_STM,
233 : : PDF_ERROR,
234 : : "");
235 : 0 : return PDF_STM_FILTER_APPLY_STATUS_ERROR;
236 : : }
237 : :
238 : : /* ...pad the cache if we have reached EOD */
239 [ + + ][ + + ]: 50 : if (finish &&
[ + - ]
240 : : mode == PDF_STM_F_AESV2_MODE_ENCODE &&
241 : : !pdf_buffer_full_p (in_cache))
242 : : {
243 : : pdf_size_t padding;
244 : :
245 : 10 : padding = in_cache->size - in_cache->wp;
246 : 10 : memset (in_cache->data + in_cache->wp,
247 : : padding,
248 : : padding);
249 : :
250 : 10 : in_cache->wp += padding;
251 : : }
252 : : else
253 : : {
254 [ + + ]: 40 : if (pdf_buffer_eob_p (out_cache))
255 : 30 : return PDF_STM_FILTER_APPLY_STATUS_NO_INPUT; /* ...ask more input */
256 : : }
257 : : }
258 : :
259 : : /* If OUT_CACHE is empty and IN_CACHE is full, then it is ready
260 : : * to be processed. */
261 [ + + ][ + - ]: 70 : if (pdf_buffer_full_p (in_cache) &&
262 : : pdf_buffer_eob_p (out_cache))
263 : : {
264 [ + + - ]: 60 : switch (mode)
265 : : {
266 : : case PDF_STM_F_AESV2_MODE_ENCODE:
267 [ - + ]: 30 : if (!pdf_crypt_cipher_encrypt (cipher,
268 : : (pdf_char_t *)out_cache->data,
269 : : out_cache->size,
270 : : (const pdf_char_t *)in_cache->data,
271 : : in_cache->size,
272 : : NULL,
273 : : error))
274 : 0 : return PDF_STM_FILTER_APPLY_STATUS_ERROR;
275 : : break;
276 : :
277 : : case PDF_STM_F_AESV2_MODE_DECODE:
278 [ - + ]: 30 : if (!pdf_crypt_cipher_decrypt (cipher,
279 : : (pdf_char_t *)out_cache->data,
280 : : out_cache->size,
281 : : (const pdf_char_t *)in_cache->data,
282 : : in_cache->size,
283 : : NULL,
284 : : error))
285 : 0 : return PDF_STM_FILTER_APPLY_STATUS_ERROR;
286 : : break;
287 : : }
288 : :
289 : : /* Both cache are full now */
290 : 60 : pdf_buffer_rewind (in_cache);
291 : 60 : out_cache->wp = out_cache->size;
292 : : }
293 : :
294 : : /* When we are decrypting data, we need know what block is the
295 : : * last. If both IN and IN_CACHE are empty, then OUT_CACHE could
296 : : * hold it. So, we ask more input to we make sure.*/
297 [ + + ][ + + ]: 70 : if (mode == PDF_STM_F_AESV2_MODE_DECODE &&
[ + - ]
298 : : pdf_buffer_eob_p (in) &&
299 : : pdf_buffer_eob_p (in_cache))
300 : : {
301 : : /* When we know it is the last, remove the padding. */
302 [ + + ]: 20 : if (finish)
303 : : {
304 : : pdf_size_t padding;
305 : :
306 : 10 : padding = out_cache->data[out_cache->size - 1];
307 [ - + ]: 10 : if (padding > AESV2_CACHE_SIZE)
308 : : {
309 : 0 : pdf_set_error (error,
310 : : PDF_EDOMAIN_BASE_STM,
311 : : PDF_ERROR,
312 : : "Padding longer than AESv2 cache (%lu > %lu)",
313 : : (unsigned long)padding,
314 : : (unsigned long)AESV2_CACHE_SIZE);
315 : 0 : return PDF_STM_FILTER_APPLY_STATUS_ERROR;
316 : : }
317 : :
318 : 10 : out_cache->wp = out_cache->size - padding;
319 : : }
320 : : else /* Ask for more input */
321 : 10 : return PDF_STM_FILTER_APPLY_STATUS_NO_INPUT;
322 : : }
323 : :
324 : : /* Finally, we fill the OUT buffer */
325 : 60 : out_size = out->size - out->wp;
326 : 60 : out_cache_size = out_cache->wp - out_cache->rp;
327 : 60 : bytes_to_write = PDF_MIN (out_size, out_cache_size);
328 : :
329 : 60 : memcpy (out->data + out->wp,
330 : : out_cache->data + out_cache->rp,
331 : : bytes_to_write);
332 : :
333 : 60 : out_cache->rp += bytes_to_write;
334 : 60 : out->wp += bytes_to_write;
335 : :
336 [ + + ]: 60 : if (finish)
337 : 20 : return PDF_STM_FILTER_APPLY_STATUS_EOF;
338 : :
339 [ - + ]: 40 : if (pdf_buffer_full_p (out))
340 : 0 : return PDF_STM_FILTER_APPLY_STATUS_NO_OUTPUT;
341 : :
342 : : /* Continue loop */
343 : 40 : pdf_buffer_rewind (out_cache);
344 : 100 : }
345 : : }
346 : :
347 : : /* Encode filter */
348 : :
349 : : static enum pdf_stm_filter_apply_status_e
350 : 30 : stm_f_aesv2enc_apply (void *state,
351 : : pdf_buffer_t *in,
352 : : pdf_buffer_t *out,
353 : : pdf_bool_t finish,
354 : : pdf_error_t **error)
355 : : {
356 : 30 : return stm_f_aesv2_apply (PDF_STM_F_AESV2_MODE_ENCODE,
357 : : state,
358 : : in,
359 : : out,
360 : : finish,
361 : : error);
362 : : }
363 : :
364 : : /* Decode filter */
365 : :
366 : : static enum pdf_stm_filter_apply_status_e
367 : 30 : stm_f_aesv2dec_apply (void *state,
368 : : pdf_buffer_t *in,
369 : : pdf_buffer_t *out,
370 : : pdf_bool_t finish,
371 : : pdf_error_t **error)
372 : : {
373 : 30 : return stm_f_aesv2_apply (PDF_STM_F_AESV2_MODE_DECODE,
374 : : state,
375 : : in,
376 : : out,
377 : : finish,
378 : : error);
379 : : }
380 : :
381 : : /* End of pdf_stm_f_aesv2.c */
|