]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - contrib/subversion/subversion/libsvn_subr/crypto.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / contrib / subversion / subversion / libsvn_subr / crypto.c
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 \f
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 *
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 *
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 *
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 *
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 *
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 \f
184 /*** Semi-public APIs ***/
185
186 /* Return TRUE iff Subversion's cryptographic support is available. */
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 *
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 *
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 *
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 *
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 *
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 }