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). */
48struct 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. */
72static 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(). */
84static svn_error_t *
85crypto_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. */
102static svn_error_t *
103err_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. */
118static svn_error_t *
119crypto_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. */
142static svn_error_t *
143get_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). */
167static const svn_string_t *
168wrap_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. */
187svn_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.  */
199svn_error_t *
200svn_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
248svn_error_t *
249svn_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
390svn_error_t *
391svn_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
484svn_error_t *
485svn_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
609svn_error_t *
610svn_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