1 /*
2 * crypto.c : cryptographic routines
3 *
4 * ====================================================================
5 * Licensed to the Apache Software Foundation (ASF) under one
6 * or more contributor license agreements. See the NOTICE file
7 * distributed with this work for additional information
8 * regarding copyright ownership. The ASF licenses this file
9 * to you under the Apache License, Version 2.0 (the
10 * "License"); you may not use this file except in compliance
11 * with the License. You may obtain a copy of the License at
12 *
13 * http://www.apache.org/licenses/LICENSE-2.0
14 *
15 * Unless required by applicable law or agreed to in writing,
16 * software distributed under the License is distributed on an
17 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18 * KIND, either express or implied. See the License for the
19 * specific language governing permissions and limitations
20 * under the License.
21 * ====================================================================
22 */
23
24 #include "crypto.h"
25
26 #ifdef SVN_HAVE_CRYPTO
27 #include <apr_random.h>
28 #include <apr_crypto.h>
29 #endif /* SVN_HAVE_CRYPTO */
30
31 #include "svn_types.h"
32 #include "svn_checksum.h"
33
34 #include "svn_private_config.h"
35 #include "private/svn_atomic.h"
36
37
38 /* 1000 iterations is the recommended minimum, per RFC 2898, section 4.2. */
39 #define NUM_ITERATIONS 1000
40
41
42 /* Size (in bytes) of the random data we'll prepend to encrypted data. */
43 #define RANDOM_PREFIX_LEN 4
44
45
46 /* A structure for containing Subversion's cryptography-related bits
47 (so we can avoid passing around APR-isms outside this module). */
48 struct svn_crypto__ctx_t {
49 #ifdef SVN_HAVE_CRYPTO
50 apr_crypto_t *crypto; /* APR cryptography context. */
51
52 #if 0
53 /* ### For now, we will use apr_generate_random_bytes(). If we need
54 ### more strength, then we can set this member using
55 ### apr_random_standard_new(), then use
56 ### apr_generate_random_bytes() to generate entropy for seeding
57 ### apr_random_t. See httpd/server/core.c:ap_init_rng() */
58 apr_random_t *rand;
59 #endif /* 0 */
60 #else /* SVN_HAVE_CRYPTO */
61 int unused_but_required_to_satisfy_c_compilers;
62 #endif /* SVN_HAVE_CRYPTO */
63 };
64
65
66
67 /*** Helper Functions ***/
68 #ifdef SVN_HAVE_CRYPTO
69
70
71 /* One-time initialization of the cryptography subsystem. */
72 static volatile svn_atomic_t crypto_init_state = 0;
73
74
75 #define CRYPTO_INIT(scratch_pool) \
76 SVN_ERR(svn_atomic__init_once(&crypto_init_state, \
77 crypto_init, NULL, (scratch_pool)))
78
79
80 /* Initialize the APR cryptography subsystem (if available), using
81 ANY_POOL's ancestor root pool for the registration of cleanups,
82 shutdowns, etc. */
83 /* Don't call this function directly! Use svn_atomic__init_once(). */
84 static svn_error_t *
crypto_init(void * baton,apr_pool_t * any_pool)85 crypto_init(void *baton, apr_pool_t *any_pool)
86 {
87 /* NOTE: this function will locate the topmost ancestor of ANY_POOL
88 for its cleanup handlers. We don't have to worry about ANY_POOL
89 being cleared. */
90 apr_status_t apr_err = apr_crypto_init(any_pool);
91 if (apr_err)
92 return svn_error_wrap_apr(apr_err,
93 _("Failed to initialize cryptography "
94 "subsystem"));
95
96 return SVN_NO_ERROR;
97 }
98
99
100 /* If APU_ERR is non-NULL, create and return a Subversion error using
101 APR_ERR and APU_ERR. */
102 static svn_error_t *
err_from_apu_err(apr_status_t apr_err,const apu_err_t * apu_err)103 err_from_apu_err(apr_status_t apr_err,
104 const apu_err_t *apu_err)
105 {
106 if (apu_err)
107 return svn_error_createf(apr_err, NULL,
108 _("code (%d), reason (\"%s\"), msg (\"%s\")"),
109 apu_err->rc,
110 apu_err->reason ? apu_err->reason : "",
111 apu_err->msg ? apu_err->msg : "");
112 return SVN_NO_ERROR;
113 }
114
115
116 /* Generate a Subversion error which describes the state reflected by
117 APR_ERR and any crypto errors registered with CTX. */
118 static svn_error_t *
crypto_error_create(svn_crypto__ctx_t * ctx,apr_status_t apr_err,const char * msg)119 crypto_error_create(svn_crypto__ctx_t *ctx,
120 apr_status_t apr_err,
121 const char *msg)
122 {
123 const apu_err_t *apu_err;
124 apr_status_t rv = apr_crypto_error(&apu_err, ctx->crypto);
125 svn_error_t *child;
126
127 /* Ugh. The APIs are a bit slippery, so be wary. */
128 if (apr_err == APR_SUCCESS)
129 apr_err = APR_EGENERAL;
130
131 if (rv == APR_SUCCESS)
132 child = err_from_apu_err(apr_err, apu_err);
133 else
134 child = svn_error_wrap_apr(rv, _("Fetching error from APR"));
135
136 return svn_error_create(apr_err, child, msg);
137 }
138
139
140 /* Set RAND_BYTES to a block of bytes containing random data RAND_LEN
141 long and allocated from RESULT_POOL. */
142 static svn_error_t *
get_random_bytes(const unsigned char ** rand_bytes,svn_crypto__ctx_t * ctx,apr_size_t rand_len,apr_pool_t * result_pool)143 get_random_bytes(const unsigned char **rand_bytes,
144 svn_crypto__ctx_t *ctx,
145 apr_size_t rand_len,
146 apr_pool_t *result_pool)
147 {
148 apr_status_t apr_err;
149 unsigned char *bytes;
150
151 bytes = apr_palloc(result_pool, rand_len);
152 apr_err = apr_generate_random_bytes(bytes, rand_len);
153 if (apr_err != APR_SUCCESS)
154 return svn_error_wrap_apr(apr_err, _("Error obtaining random data"));
155
156 *rand_bytes = bytes;
157 return SVN_NO_ERROR;
158 }
159
160
161 /* Return an svn_string_t allocated from RESULT_POOL, with its .data
162 and .len members set to DATA and LEN, respective.
163
164 WARNING: No lifetime management of DATA is offered here, so you
165 probably want to ensure that that information is allocated in a
166 sufficiently long-lived pool (such as, for example, RESULT_POOL). */
167 static const svn_string_t *
wrap_as_string(const unsigned char * data,apr_size_t len,apr_pool_t * result_pool)168 wrap_as_string(const unsigned char *data,
169 apr_size_t len,
170 apr_pool_t *result_pool)
171 {
172 svn_string_t *s = apr_palloc(result_pool, sizeof(*s));
173
174 s->data = (const char *)data; /* better already be in RESULT_POOL */
175 s->len = len;
176 return s;
177 }
178
179
180 #endif /* SVN_HAVE_CRYPTO */
181
182
183
184 /*** Semi-public APIs ***/
185
186 /* Return TRUE iff Subversion's cryptographic support is available. */
svn_crypto__is_available(void)187 svn_boolean_t svn_crypto__is_available(void)
188 {
189 #ifdef SVN_HAVE_CRYPTO
190 return TRUE;
191 #else /* SVN_HAVE_CRYPTO */
192 return FALSE;
193 #endif /* SVN_HAVE_CRYPTO */
194 }
195
196
197 /* Set CTX to a Subversion cryptography context allocated from
198 RESULT_POOL. */
199 svn_error_t *
svn_crypto__context_create(svn_crypto__ctx_t ** ctx,apr_pool_t * result_pool)200 svn_crypto__context_create(svn_crypto__ctx_t **ctx,
201 apr_pool_t *result_pool)
202 {
203 #ifdef SVN_HAVE_CRYPTO
204 apr_status_t apr_err;
205 const apu_err_t *apu_err = NULL;
206 apr_crypto_t *apr_crypto;
207 const apr_crypto_driver_t *driver;
208
209 CRYPTO_INIT(result_pool);
210
211 /* Load the crypto driver.
212
213 ### TODO: For the sake of flexibility, should we use
214 ### APU_CRYPTO_RECOMMENDED_DRIVER instead of hard coding
215 ### "openssl" here?
216
217 NOTE: Potential bugs in get_driver() imply we might get
218 APR_SUCCESS and NULL. Sigh. Just be a little more careful in
219 error generation here. */
220 apr_err = apr_crypto_get_driver(&driver, "openssl", NULL, &apu_err,
221 result_pool);
222 if (apr_err != APR_SUCCESS)
223 return svn_error_create(apr_err, err_from_apu_err(apr_err, apu_err),
224 _("OpenSSL crypto driver error"));
225 if (driver == NULL)
226 return svn_error_create(APR_EGENERAL,
227 err_from_apu_err(APR_EGENERAL, apu_err),
228 _("Bad return value while loading crypto "
229 "driver"));
230
231 apr_err = apr_crypto_make(&apr_crypto, driver, NULL, result_pool);
232 if (apr_err != APR_SUCCESS || apr_crypto == NULL)
233 return svn_error_create(apr_err, NULL,
234 _("Error creating OpenSSL crypto context"));
235
236 /* Allocate and initialize our crypto context. */
237 *ctx = apr_palloc(result_pool, sizeof(**ctx));
238 (*ctx)->crypto = apr_crypto;
239
240 return SVN_NO_ERROR;
241 #else /* SVN_HAVE_CRYPTO */
242 return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
243 "Cryptographic support is not available");
244 #endif /* SVN_HAVE_CRYPTO */
245 }
246
247
248 svn_error_t *
svn_crypto__encrypt_password(const svn_string_t ** ciphertext,const svn_string_t ** iv,const svn_string_t ** salt,svn_crypto__ctx_t * ctx,const char * password,const svn_string_t * master,apr_pool_t * result_pool,apr_pool_t * scratch_pool)249 svn_crypto__encrypt_password(const svn_string_t **ciphertext,
250 const svn_string_t **iv,
251 const svn_string_t **salt,
252 svn_crypto__ctx_t *ctx,
253 const char *password,
254 const svn_string_t *master,
255 apr_pool_t *result_pool,
256 apr_pool_t *scratch_pool)
257 {
258 #ifdef SVN_HAVE_CRYPTO
259 svn_error_t *err = SVN_NO_ERROR;
260 const unsigned char *salt_vector;
261 const unsigned char *iv_vector;
262 apr_size_t iv_len;
263 apr_crypto_key_t *key = NULL;
264 apr_status_t apr_err;
265 const unsigned char *prefix;
266 apr_crypto_block_t *block_ctx = NULL;
267 apr_size_t block_size;
268 unsigned char *assembled;
269 apr_size_t password_len, assembled_len = 0;
270 apr_size_t result_len;
271 unsigned char *result;
272 apr_size_t ignored_result_len = 0;
273
274 SVN_ERR_ASSERT(ctx != NULL);
275
276 /* Generate the salt. */
277 #define SALT_LEN 8
278 SVN_ERR(get_random_bytes(&salt_vector, ctx, SALT_LEN, result_pool));
279
280 /* Initialize the passphrase. */
281 apr_err = apr_crypto_passphrase(&key, &iv_len,
282 master->data, master->len,
283 salt_vector, SALT_LEN,
284 APR_KEY_AES_256, APR_MODE_CBC,
285 FALSE /* doPad */, NUM_ITERATIONS,
286 ctx->crypto,
287 scratch_pool);
288 if (apr_err != APR_SUCCESS)
289 return svn_error_trace(crypto_error_create(
290 ctx, apr_err,
291 _("Error creating derived key")));
292 if (! key)
293 return svn_error_create(APR_EGENERAL, NULL,
294 _("Error creating derived key"));
295 if (iv_len == 0)
296 return svn_error_create(APR_EGENERAL, NULL,
297 _("Unexpected IV length returned"));
298
299 /* Generate the proper length IV. */
300 SVN_ERR(get_random_bytes(&iv_vector, ctx, iv_len, result_pool));
301
302 /* Initialize block encryption. */
303 apr_err = apr_crypto_block_encrypt_init(&block_ctx, &iv_vector, key,
304 &block_size, scratch_pool);
305 if ((apr_err != APR_SUCCESS) || (! block_ctx))
306 return svn_error_trace(crypto_error_create(
307 ctx, apr_err,
308 _("Error initializing block encryption")));
309
310 /* Generate a 4-byte prefix. */
311 SVN_ERR(get_random_bytes(&prefix, ctx, RANDOM_PREFIX_LEN, scratch_pool));
312
313 /* Combine our prefix, original password, and appropriate padding.
314 We won't bother padding if the prefix and password combined
315 perfectly align on the block boundary. If they don't,
316 however, we'll drop a NUL byte after the password and pad with
317 random stuff after that to the block boundary. */
318 password_len = strlen(password);
319 assembled_len = RANDOM_PREFIX_LEN + password_len;
320 if ((assembled_len % block_size) == 0)
321 {
322 assembled = apr_palloc(scratch_pool, assembled_len);
323 memcpy(assembled, prefix, RANDOM_PREFIX_LEN);
324 memcpy(assembled + RANDOM_PREFIX_LEN, password, password_len);
325 }
326 else
327 {
328 const unsigned char *padding;
329 apr_size_t pad_len = block_size - (assembled_len % block_size) - 1;
330
331 SVN_ERR(get_random_bytes(&padding, ctx, pad_len, scratch_pool));
332 assembled_len = assembled_len + 1 + pad_len;
333 assembled = apr_palloc(scratch_pool, assembled_len);
334 memcpy(assembled, prefix, RANDOM_PREFIX_LEN);
335 memcpy(assembled + RANDOM_PREFIX_LEN, password, password_len);
336 *(assembled + RANDOM_PREFIX_LEN + password_len) = '\0';
337 memcpy(assembled + RANDOM_PREFIX_LEN + password_len + 1,
338 padding, pad_len);
339 }
340
341 /* Get the length that we need to allocate. */
342 apr_err = apr_crypto_block_encrypt(NULL, &result_len, assembled,
343 assembled_len, block_ctx);
344 if (apr_err != APR_SUCCESS)
345 {
346 err = crypto_error_create(ctx, apr_err,
347 _("Error fetching result length"));
348 goto cleanup;
349 }
350
351 /* Allocate our result buffer. */
352 result = apr_palloc(result_pool, result_len);
353
354 /* Encrypt the block. */
355 apr_err = apr_crypto_block_encrypt(&result, &result_len, assembled,
356 assembled_len, block_ctx);
357 if (apr_err != APR_SUCCESS)
358 {
359 err = crypto_error_create(ctx, apr_err,
360 _("Error during block encryption"));
361 goto cleanup;
362 }
363
364 /* Finalize the block encryption. Since we padded everything, this should
365 not produce any more encrypted output. */
366 apr_err = apr_crypto_block_encrypt_finish(NULL,
367 &ignored_result_len,
368 block_ctx);
369 if (apr_err != APR_SUCCESS)
370 {
371 err = crypto_error_create(ctx, apr_err,
372 _("Error finalizing block encryption"));
373 goto cleanup;
374 }
375
376 *ciphertext = wrap_as_string(result, result_len, result_pool);
377 *iv = wrap_as_string(iv_vector, iv_len, result_pool);
378 *salt = wrap_as_string(salt_vector, SALT_LEN, result_pool);
379
380 cleanup:
381 apr_crypto_block_cleanup(block_ctx);
382 return err;
383 #else /* SVN_HAVE_CRYPTO */
384 return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
385 "Cryptographic support is not available");
386 #endif /* SVN_HAVE_CRYPTO */
387 }
388
389
390 svn_error_t *
svn_crypto__decrypt_password(const char ** plaintext,svn_crypto__ctx_t * ctx,const svn_string_t * ciphertext,const svn_string_t * iv,const svn_string_t * salt,const svn_string_t * master,apr_pool_t * result_pool,apr_pool_t * scratch_pool)391 svn_crypto__decrypt_password(const char **plaintext,
392 svn_crypto__ctx_t *ctx,
393 const svn_string_t *ciphertext,
394 const svn_string_t *iv,
395 const svn_string_t *salt,
396 const svn_string_t *master,
397 apr_pool_t *result_pool,
398 apr_pool_t *scratch_pool)
399 {
400 #ifdef SVN_HAVE_CRYPTO
401 svn_error_t *err = SVN_NO_ERROR;
402 apr_status_t apr_err;
403 apr_crypto_block_t *block_ctx = NULL;
404 apr_size_t block_size, iv_len;
405 apr_crypto_key_t *key = NULL;
406 unsigned char *result;
407 apr_size_t result_len = 0, final_len = 0;
408
409 /* Initialize the passphrase. */
410 apr_err = apr_crypto_passphrase(&key, &iv_len,
411 master->data, master->len,
412 (unsigned char *)salt->data, salt->len,
413 APR_KEY_AES_256, APR_MODE_CBC,
414 FALSE /* doPad */, NUM_ITERATIONS,
415 ctx->crypto, scratch_pool);
416 if (apr_err != APR_SUCCESS)
417 return svn_error_trace(crypto_error_create(
418 ctx, apr_err,
419 _("Error creating derived key")));
420 if (! key)
421 return svn_error_create(APR_EGENERAL, NULL,
422 _("Error creating derived key"));
423 if (iv_len == 0)
424 return svn_error_create(APR_EGENERAL, NULL,
425 _("Unexpected IV length returned"));
426 if (iv_len != iv->len)
427 return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
428 _("Provided IV has incorrect length"));
429
430 apr_err = apr_crypto_block_decrypt_init(&block_ctx, &block_size,
431 (unsigned char *)iv->data,
432 key, scratch_pool);
433 if ((apr_err != APR_SUCCESS) || (! block_ctx))
434 return svn_error_trace(crypto_error_create(
435 ctx, apr_err,
436 _("Error initializing block decryption")));
437
438 apr_err = apr_crypto_block_decrypt(NULL, &result_len,
439 (unsigned char *)ciphertext->data,
440 ciphertext->len, block_ctx);
441 if (apr_err != APR_SUCCESS)
442 {
443 err = crypto_error_create(ctx, apr_err,
444 _("Error fetching result length"));
445 goto cleanup;
446 }
447
448 result = apr_palloc(scratch_pool, result_len);
449 apr_err = apr_crypto_block_decrypt(&result, &result_len,
450 (unsigned char *)ciphertext->data,
451 ciphertext->len, block_ctx);
452 if (apr_err != APR_SUCCESS)
453 {
454 err = crypto_error_create(ctx, apr_err,
455 _("Error during block decryption"));
456 goto cleanup;
457 }
458
459 apr_err = apr_crypto_block_decrypt_finish(result + result_len, &final_len,
460 block_ctx);
461 if (apr_err != APR_SUCCESS)
462 {
463 err = crypto_error_create(ctx, apr_err,
464 _("Error finalizing block decryption"));
465 goto cleanup;
466 }
467
468 /* Copy the non-random bits of the resulting plaintext, skipping the
469 prefix and ignoring any trailing padding. */
470 *plaintext = apr_pstrndup(result_pool,
471 (const char *)(result + RANDOM_PREFIX_LEN),
472 result_len + final_len - RANDOM_PREFIX_LEN);
473
474 cleanup:
475 apr_crypto_block_cleanup(block_ctx);
476 return err;
477 #else /* SVN_HAVE_CRYPTO */
478 return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
479 "Cryptographic support is not available");
480 #endif /* SVN_HAVE_CRYPTO */
481 }
482
483
484 svn_error_t *
svn_crypto__generate_secret_checktext(const svn_string_t ** ciphertext,const svn_string_t ** iv,const svn_string_t ** salt,const char ** checktext,svn_crypto__ctx_t * ctx,const svn_string_t * master,apr_pool_t * result_pool,apr_pool_t * scratch_pool)485 svn_crypto__generate_secret_checktext(const svn_string_t **ciphertext,
486 const svn_string_t **iv,
487 const svn_string_t **salt,
488 const char **checktext,
489 svn_crypto__ctx_t *ctx,
490 const svn_string_t *master,
491 apr_pool_t *result_pool,
492 apr_pool_t *scratch_pool)
493 {
494 #ifdef SVN_HAVE_CRYPTO
495 svn_error_t *err = SVN_NO_ERROR;
496 const unsigned char *salt_vector;
497 const unsigned char *iv_vector;
498 const unsigned char *stuff_vector;
499 apr_size_t iv_len;
500 apr_crypto_key_t *key = NULL;
501 apr_status_t apr_err;
502 apr_crypto_block_t *block_ctx = NULL;
503 apr_size_t block_size;
504 apr_size_t result_len;
505 unsigned char *result;
506 apr_size_t ignored_result_len = 0;
507 apr_size_t stuff_len;
508 svn_checksum_t *stuff_sum;
509
510 SVN_ERR_ASSERT(ctx != NULL);
511
512 /* Generate the salt. */
513 SVN_ERR(get_random_bytes(&salt_vector, ctx, SALT_LEN, result_pool));
514
515 /* Initialize the passphrase. */
516 apr_err = apr_crypto_passphrase(&key, &iv_len,
517 master->data, master->len,
518 salt_vector, SALT_LEN,
519 APR_KEY_AES_256, APR_MODE_CBC,
520 FALSE /* doPad */, NUM_ITERATIONS,
521 ctx->crypto,
522 scratch_pool);
523 if (apr_err != APR_SUCCESS)
524 return svn_error_trace(crypto_error_create(
525 ctx, apr_err,
526 _("Error creating derived key")));
527 if (! key)
528 return svn_error_create(APR_EGENERAL, NULL,
529 _("Error creating derived key"));
530 if (iv_len == 0)
531 return svn_error_create(APR_EGENERAL, NULL,
532 _("Unexpected IV length returned"));
533
534 /* Generate the proper length IV. */
535 SVN_ERR(get_random_bytes(&iv_vector, ctx, iv_len, result_pool));
536
537 /* Initialize block encryption. */
538 apr_err = apr_crypto_block_encrypt_init(&block_ctx, &iv_vector, key,
539 &block_size, scratch_pool);
540 if ((apr_err != APR_SUCCESS) || (! block_ctx))
541 return svn_error_trace(crypto_error_create(
542 ctx, apr_err,
543 _("Error initializing block encryption")));
544
545 /* Generate a blob of random data, block-aligned per the
546 requirements of the encryption algorithm, but with a minimum size
547 of our choosing. */
548 #define MIN_STUFF_LEN 32
549 if (MIN_STUFF_LEN % block_size)
550 stuff_len = MIN_STUFF_LEN + (block_size - (MIN_STUFF_LEN % block_size));
551 else
552 stuff_len = MIN_STUFF_LEN;
553 SVN_ERR(get_random_bytes(&stuff_vector, ctx, stuff_len, scratch_pool));
554
555 /* ### FIXME: This should be a SHA-256. */
556 SVN_ERR(svn_checksum(&stuff_sum, svn_checksum_sha1, stuff_vector,
557 stuff_len, scratch_pool));
558
559 /* Get the length that we need to allocate. */
560 apr_err = apr_crypto_block_encrypt(NULL, &result_len, stuff_vector,
561 stuff_len, block_ctx);
562 if (apr_err != APR_SUCCESS)
563 {
564 err = crypto_error_create(ctx, apr_err,
565 _("Error fetching result length"));
566 goto cleanup;
567 }
568
569 /* Allocate our result buffer. */
570 result = apr_palloc(result_pool, result_len);
571
572 /* Encrypt the block. */
573 apr_err = apr_crypto_block_encrypt(&result, &result_len, stuff_vector,
574 stuff_len, block_ctx);
575 if (apr_err != APR_SUCCESS)
576 {
577 err = crypto_error_create(ctx, apr_err,
578 _("Error during block encryption"));
579 goto cleanup;
580 }
581
582 /* Finalize the block encryption. Since we padded everything, this should
583 not produce any more encrypted output. */
584 apr_err = apr_crypto_block_encrypt_finish(NULL,
585 &ignored_result_len,
586 block_ctx);
587 if (apr_err != APR_SUCCESS)
588 {
589 err = crypto_error_create(ctx, apr_err,
590 _("Error finalizing block encryption"));
591 goto cleanup;
592 }
593
594 *ciphertext = wrap_as_string(result, result_len, result_pool);
595 *iv = wrap_as_string(iv_vector, iv_len, result_pool);
596 *salt = wrap_as_string(salt_vector, SALT_LEN, result_pool);
597 *checktext = svn_checksum_to_cstring(stuff_sum, result_pool);
598
599 cleanup:
600 apr_crypto_block_cleanup(block_ctx);
601 return err;
602 #else /* SVN_HAVE_CRYPTO */
603 return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
604 "Cryptographic support is not available");
605 #endif /* SVN_HAVE_CRYPTO */
606 }
607
608
609 svn_error_t *
svn_crypto__verify_secret(svn_boolean_t * is_valid,svn_crypto__ctx_t * ctx,const svn_string_t * master,const svn_string_t * ciphertext,const svn_string_t * iv,const svn_string_t * salt,const char * checktext,apr_pool_t * scratch_pool)610 svn_crypto__verify_secret(svn_boolean_t *is_valid,
611 svn_crypto__ctx_t *ctx,
612 const svn_string_t *master,
613 const svn_string_t *ciphertext,
614 const svn_string_t *iv,
615 const svn_string_t *salt,
616 const char *checktext,
617 apr_pool_t *scratch_pool)
618 {
619 #ifdef SVN_HAVE_CRYPTO
620 svn_error_t *err = SVN_NO_ERROR;
621 apr_status_t apr_err;
622 apr_crypto_block_t *block_ctx = NULL;
623 apr_size_t block_size, iv_len;
624 apr_crypto_key_t *key = NULL;
625 unsigned char *result;
626 apr_size_t result_len = 0, final_len = 0;
627 svn_checksum_t *result_sum;
628
629 *is_valid = FALSE;
630
631 /* Initialize the passphrase. */
632 apr_err = apr_crypto_passphrase(&key, &iv_len,
633 master->data, master->len,
634 (unsigned char *)salt->data, salt->len,
635 APR_KEY_AES_256, APR_MODE_CBC,
636 FALSE /* doPad */, NUM_ITERATIONS,
637 ctx->crypto, scratch_pool);
638 if (apr_err != APR_SUCCESS)
639 return svn_error_trace(crypto_error_create(
640 ctx, apr_err,
641 _("Error creating derived key")));
642 if (! key)
643 return svn_error_create(APR_EGENERAL, NULL,
644 _("Error creating derived key"));
645 if (iv_len == 0)
646 return svn_error_create(APR_EGENERAL, NULL,
647 _("Unexpected IV length returned"));
648 if (iv_len != iv->len)
649 return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
650 _("Provided IV has incorrect length"));
651
652 apr_err = apr_crypto_block_decrypt_init(&block_ctx, &block_size,
653 (unsigned char *)iv->data,
654 key, scratch_pool);
655 if ((apr_err != APR_SUCCESS) || (! block_ctx))
656 return svn_error_trace(crypto_error_create(
657 ctx, apr_err,
658 _("Error initializing block decryption")));
659
660 apr_err = apr_crypto_block_decrypt(NULL, &result_len,
661 (unsigned char *)ciphertext->data,
662 ciphertext->len, block_ctx);
663 if (apr_err != APR_SUCCESS)
664 {
665 err = crypto_error_create(ctx, apr_err,
666 _("Error fetching result length"));
667 goto cleanup;
668 }
669
670 result = apr_palloc(scratch_pool, result_len);
671 apr_err = apr_crypto_block_decrypt(&result, &result_len,
672 (unsigned char *)ciphertext->data,
673 ciphertext->len, block_ctx);
674 if (apr_err != APR_SUCCESS)
675 {
676 err = crypto_error_create(ctx, apr_err,
677 _("Error during block decryption"));
678 goto cleanup;
679 }
680
681 apr_err = apr_crypto_block_decrypt_finish(result + result_len, &final_len,
682 block_ctx);
683 if (apr_err != APR_SUCCESS)
684 {
685 err = crypto_error_create(ctx, apr_err,
686 _("Error finalizing block decryption"));
687 goto cleanup;
688 }
689
690 /* ### FIXME: This should be a SHA-256. */
691 SVN_ERR(svn_checksum(&result_sum, svn_checksum_sha1, result,
692 result_len + final_len, scratch_pool));
693
694 *is_valid = strcmp(checktext,
695 svn_checksum_to_cstring(result_sum, scratch_pool)) == 0;
696
697 cleanup:
698 apr_crypto_block_cleanup(block_ctx);
699 return err;
700 #else /* SVN_HAVE_CRYPTO */
701 *is_valid = FALSE;
702 return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
703 "Cryptographic support is not available");
704 #endif /* SVN_HAVE_CRYPTO */
705 }
706