1251881Speter/*
2251881Speter * crypto.c :  cryptographic routines
3251881Speter *
4251881Speter * ====================================================================
5251881Speter *    Licensed to the Apache Software Foundation (ASF) under one
6251881Speter *    or more contributor license agreements.  See the NOTICE file
7251881Speter *    distributed with this work for additional information
8251881Speter *    regarding copyright ownership.  The ASF licenses this file
9251881Speter *    to you under the Apache License, Version 2.0 (the
10251881Speter *    "License"); you may not use this file except in compliance
11251881Speter *    with the License.  You may obtain a copy of the License at
12251881Speter *
13251881Speter *      http://www.apache.org/licenses/LICENSE-2.0
14251881Speter *
15251881Speter *    Unless required by applicable law or agreed to in writing,
16251881Speter *    software distributed under the License is distributed on an
17251881Speter *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18251881Speter *    KIND, either express or implied.  See the License for the
19251881Speter *    specific language governing permissions and limitations
20251881Speter *    under the License.
21251881Speter * ====================================================================
22251881Speter */
23251881Speter
24251881Speter#include "crypto.h"
25251881Speter
26251881Speter#ifdef SVN_HAVE_CRYPTO
27251881Speter#include <apr_random.h>
28251881Speter#include <apr_crypto.h>
29251881Speter#endif /* SVN_HAVE_CRYPTO */
30251881Speter
31251881Speter#include "svn_types.h"
32251881Speter#include "svn_checksum.h"
33251881Speter
34251881Speter#include "svn_private_config.h"
35251881Speter#include "private/svn_atomic.h"
36251881Speter
37251881Speter
38251881Speter/* 1000 iterations is the recommended minimum, per RFC 2898, section 4.2.  */
39251881Speter#define NUM_ITERATIONS 1000
40251881Speter
41251881Speter
42251881Speter/* Size (in bytes) of the random data we'll prepend to encrypted data. */
43251881Speter#define RANDOM_PREFIX_LEN 4
44251881Speter
45251881Speter
46251881Speter/* A structure for containing Subversion's cryptography-related bits
47251881Speter   (so we can avoid passing around APR-isms outside this module). */
48251881Speterstruct svn_crypto__ctx_t {
49251881Speter#ifdef SVN_HAVE_CRYPTO
50251881Speter  apr_crypto_t *crypto;  /* APR cryptography context. */
51251881Speter
52251881Speter#if 0
53251881Speter  /* ### For now, we will use apr_generate_random_bytes(). If we need
54251881Speter     ### more strength, then we can set this member using
55251881Speter     ### apr_random_standard_new(), then use
56251881Speter     ### apr_generate_random_bytes() to generate entropy for seeding
57251881Speter     ### apr_random_t. See httpd/server/core.c:ap_init_rng()  */
58251881Speter  apr_random_t *rand;
59251881Speter#endif /* 0 */
60251881Speter#else /* SVN_HAVE_CRYPTO */
61251881Speter  int unused_but_required_to_satisfy_c_compilers;
62251881Speter#endif /* SVN_HAVE_CRYPTO */
63251881Speter};
64251881Speter
65251881Speter
66251881Speter
67251881Speter/*** Helper Functions ***/
68251881Speter#ifdef SVN_HAVE_CRYPTO
69251881Speter
70251881Speter
71251881Speter/* One-time initialization of the cryptography subsystem. */
72251881Speterstatic volatile svn_atomic_t crypto_init_state = 0;
73251881Speter
74251881Speter
75251881Speter#define CRYPTO_INIT(scratch_pool) \
76251881Speter  SVN_ERR(svn_atomic__init_once(&crypto_init_state, \
77251881Speter                                crypto_init, NULL, (scratch_pool)))
78251881Speter
79251881Speter
80251881Speter/* Initialize the APR cryptography subsystem (if available), using
81251881Speter   ANY_POOL's ancestor root pool for the registration of cleanups,
82251881Speter   shutdowns, etc.   */
83251881Speter/* Don't call this function directly!  Use svn_atomic__init_once(). */
84251881Speterstatic svn_error_t *
85251881Spetercrypto_init(void *baton, apr_pool_t *any_pool)
86251881Speter{
87251881Speter  /* NOTE: this function will locate the topmost ancestor of ANY_POOL
88251881Speter     for its cleanup handlers. We don't have to worry about ANY_POOL
89251881Speter     being cleared.  */
90251881Speter  apr_status_t apr_err = apr_crypto_init(any_pool);
91251881Speter  if (apr_err)
92251881Speter    return svn_error_wrap_apr(apr_err,
93251881Speter                              _("Failed to initialize cryptography "
94251881Speter                                "subsystem"));
95251881Speter
96251881Speter  return SVN_NO_ERROR;
97251881Speter}
98251881Speter
99251881Speter
100251881Speter/* If APU_ERR is non-NULL, create and return a Subversion error using
101251881Speter   APR_ERR and APU_ERR. */
102251881Speterstatic svn_error_t *
103251881Spetererr_from_apu_err(apr_status_t apr_err,
104251881Speter                 const apu_err_t *apu_err)
105251881Speter{
106251881Speter  if (apu_err)
107251881Speter    return svn_error_createf(apr_err, NULL,
108251881Speter                             _("code (%d), reason (\"%s\"), msg (\"%s\")"),
109251881Speter                             apu_err->rc,
110251881Speter                             apu_err->reason ? apu_err->reason : "",
111251881Speter                             apu_err->msg ? apu_err->msg : "");
112251881Speter  return SVN_NO_ERROR;
113251881Speter}
114251881Speter
115251881Speter
116251881Speter/* Generate a Subversion error which describes the state reflected by
117251881Speter   APR_ERR and any crypto errors registered with CTX. */
118251881Speterstatic svn_error_t *
119251881Spetercrypto_error_create(svn_crypto__ctx_t *ctx,
120251881Speter                    apr_status_t apr_err,
121251881Speter                    const char *msg)
122251881Speter{
123251881Speter  const apu_err_t *apu_err;
124251881Speter  apr_status_t rv = apr_crypto_error(&apu_err, ctx->crypto);
125251881Speter  svn_error_t *child;
126251881Speter
127251881Speter  /* Ugh. The APIs are a bit slippery, so be wary.  */
128251881Speter  if (apr_err == APR_SUCCESS)
129251881Speter    apr_err = APR_EGENERAL;
130251881Speter
131251881Speter  if (rv == APR_SUCCESS)
132251881Speter    child = err_from_apu_err(apr_err, apu_err);
133251881Speter  else
134251881Speter    child = svn_error_wrap_apr(rv, _("Fetching error from APR"));
135251881Speter
136251881Speter  return svn_error_create(apr_err, child, msg);
137251881Speter}
138251881Speter
139251881Speter
140251881Speter/* Set RAND_BYTES to a block of bytes containing random data RAND_LEN
141251881Speter   long and allocated from RESULT_POOL. */
142251881Speterstatic svn_error_t *
143251881Speterget_random_bytes(const unsigned char **rand_bytes,
144251881Speter                 svn_crypto__ctx_t *ctx,
145251881Speter                 apr_size_t rand_len,
146251881Speter                 apr_pool_t *result_pool)
147251881Speter{
148251881Speter  apr_status_t apr_err;
149251881Speter  unsigned char *bytes;
150251881Speter
151251881Speter  bytes = apr_palloc(result_pool, rand_len);
152251881Speter  apr_err = apr_generate_random_bytes(bytes, rand_len);
153251881Speter  if (apr_err != APR_SUCCESS)
154251881Speter    return svn_error_wrap_apr(apr_err, _("Error obtaining random data"));
155251881Speter
156251881Speter  *rand_bytes = bytes;
157251881Speter  return SVN_NO_ERROR;
158251881Speter}
159251881Speter
160251881Speter
161251881Speter/* Return an svn_string_t allocated from RESULT_POOL, with its .data
162251881Speter   and .len members set to DATA and LEN, respective.
163251881Speter
164251881Speter   WARNING: No lifetime management of DATA is offered here, so you
165251881Speter   probably want to ensure that that information is allocated in a
166251881Speter   sufficiently long-lived pool (such as, for example, RESULT_POOL). */
167251881Speterstatic const svn_string_t *
168251881Speterwrap_as_string(const unsigned char *data,
169251881Speter               apr_size_t len,
170251881Speter               apr_pool_t *result_pool)
171251881Speter{
172251881Speter  svn_string_t *s = apr_palloc(result_pool, sizeof(*s));
173251881Speter
174251881Speter  s->data = (const char *)data;  /* better already be in RESULT_POOL  */
175251881Speter  s->len = len;
176251881Speter  return s;
177251881Speter}
178251881Speter
179251881Speter
180251881Speter#endif /* SVN_HAVE_CRYPTO */
181251881Speter
182251881Speter
183251881Speter
184251881Speter/*** Semi-public APIs ***/
185251881Speter
186251881Speter/* Return TRUE iff Subversion's cryptographic support is available. */
187251881Spetersvn_boolean_t svn_crypto__is_available(void)
188251881Speter{
189251881Speter#ifdef SVN_HAVE_CRYPTO
190251881Speter  return TRUE;
191251881Speter#else /* SVN_HAVE_CRYPTO */
192251881Speter  return FALSE;
193251881Speter#endif /* SVN_HAVE_CRYPTO */
194251881Speter}
195251881Speter
196251881Speter
197251881Speter/* Set CTX to a Subversion cryptography context allocated from
198251881Speter   RESULT_POOL.  */
199251881Spetersvn_error_t *
200251881Spetersvn_crypto__context_create(svn_crypto__ctx_t **ctx,
201251881Speter                           apr_pool_t *result_pool)
202251881Speter{
203251881Speter#ifdef SVN_HAVE_CRYPTO
204251881Speter  apr_status_t apr_err;
205251881Speter  const apu_err_t *apu_err = NULL;
206251881Speter  apr_crypto_t *apr_crypto;
207251881Speter  const apr_crypto_driver_t *driver;
208251881Speter
209251881Speter  CRYPTO_INIT(result_pool);
210251881Speter
211251881Speter  /* Load the crypto driver.
212251881Speter
213251881Speter     ### TODO: For the sake of flexibility, should we use
214251881Speter     ### APU_CRYPTO_RECOMMENDED_DRIVER instead of hard coding
215251881Speter     ### "openssl" here?
216251881Speter
217251881Speter     NOTE: Potential bugs in get_driver() imply we might get
218251881Speter     APR_SUCCESS and NULL.  Sigh. Just be a little more careful in
219251881Speter     error generation here.  */
220251881Speter  apr_err = apr_crypto_get_driver(&driver, "openssl", NULL, &apu_err,
221251881Speter                                  result_pool);
222251881Speter  if (apr_err != APR_SUCCESS)
223251881Speter    return svn_error_create(apr_err, err_from_apu_err(apr_err, apu_err),
224251881Speter                            _("OpenSSL crypto driver error"));
225251881Speter  if (driver == NULL)
226251881Speter    return svn_error_create(APR_EGENERAL,
227251881Speter                            err_from_apu_err(APR_EGENERAL, apu_err),
228251881Speter                            _("Bad return value while loading crypto "
229251881Speter                              "driver"));
230251881Speter
231251881Speter  apr_err = apr_crypto_make(&apr_crypto, driver, NULL, result_pool);
232251881Speter  if (apr_err != APR_SUCCESS || apr_crypto == NULL)
233251881Speter    return svn_error_create(apr_err, NULL,
234251881Speter                            _("Error creating OpenSSL crypto context"));
235251881Speter
236251881Speter  /* Allocate and initialize our crypto context. */
237251881Speter  *ctx = apr_palloc(result_pool, sizeof(**ctx));
238251881Speter  (*ctx)->crypto = apr_crypto;
239251881Speter
240251881Speter  return SVN_NO_ERROR;
241251881Speter#else /* SVN_HAVE_CRYPTO */
242251881Speter  return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
243251881Speter                          "Cryptographic support is not available");
244251881Speter#endif /* SVN_HAVE_CRYPTO */
245251881Speter}
246251881Speter
247251881Speter
248251881Spetersvn_error_t *
249251881Spetersvn_crypto__encrypt_password(const svn_string_t **ciphertext,
250251881Speter                             const svn_string_t **iv,
251251881Speter                             const svn_string_t **salt,
252251881Speter                             svn_crypto__ctx_t *ctx,
253251881Speter                             const char *password,
254251881Speter                             const svn_string_t *master,
255251881Speter                             apr_pool_t *result_pool,
256251881Speter                             apr_pool_t *scratch_pool)
257251881Speter{
258251881Speter#ifdef SVN_HAVE_CRYPTO
259251881Speter  svn_error_t *err = SVN_NO_ERROR;
260251881Speter  const unsigned char *salt_vector;
261251881Speter  const unsigned char *iv_vector;
262251881Speter  apr_size_t iv_len;
263251881Speter  apr_crypto_key_t *key = NULL;
264251881Speter  apr_status_t apr_err;
265251881Speter  const unsigned char *prefix;
266251881Speter  apr_crypto_block_t *block_ctx = NULL;
267251881Speter  apr_size_t block_size;
268251881Speter  unsigned char *assembled;
269251881Speter  apr_size_t password_len, assembled_len = 0;
270251881Speter  apr_size_t result_len;
271251881Speter  unsigned char *result;
272251881Speter  apr_size_t ignored_result_len = 0;
273251881Speter
274251881Speter  SVN_ERR_ASSERT(ctx != NULL);
275251881Speter
276251881Speter  /* Generate the salt. */
277251881Speter#define SALT_LEN 8
278251881Speter  SVN_ERR(get_random_bytes(&salt_vector, ctx, SALT_LEN, result_pool));
279251881Speter
280251881Speter  /* Initialize the passphrase.  */
281251881Speter  apr_err = apr_crypto_passphrase(&key, &iv_len,
282251881Speter                                  master->data, master->len,
283251881Speter                                  salt_vector, SALT_LEN,
284251881Speter                                  APR_KEY_AES_256, APR_MODE_CBC,
285251881Speter                                  FALSE /* doPad */, NUM_ITERATIONS,
286251881Speter                                  ctx->crypto,
287251881Speter                                  scratch_pool);
288251881Speter  if (apr_err != APR_SUCCESS)
289251881Speter    return svn_error_trace(crypto_error_create(
290251881Speter                               ctx, apr_err,
291251881Speter                               _("Error creating derived key")));
292251881Speter  if (! key)
293251881Speter    return svn_error_create(APR_EGENERAL, NULL,
294251881Speter                            _("Error creating derived key"));
295251881Speter  if (iv_len == 0)
296251881Speter    return svn_error_create(APR_EGENERAL, NULL,
297251881Speter                            _("Unexpected IV length returned"));
298251881Speter
299251881Speter  /* Generate the proper length IV.  */
300251881Speter  SVN_ERR(get_random_bytes(&iv_vector, ctx, iv_len, result_pool));
301251881Speter
302251881Speter  /* Initialize block encryption. */
303251881Speter  apr_err = apr_crypto_block_encrypt_init(&block_ctx, &iv_vector, key,
304251881Speter                                          &block_size, scratch_pool);
305251881Speter  if ((apr_err != APR_SUCCESS) || (! block_ctx))
306251881Speter    return svn_error_trace(crypto_error_create(
307251881Speter                             ctx, apr_err,
308251881Speter                             _("Error initializing block encryption")));
309251881Speter
310251881Speter  /* Generate a 4-byte prefix. */
311251881Speter  SVN_ERR(get_random_bytes(&prefix, ctx, RANDOM_PREFIX_LEN, scratch_pool));
312251881Speter
313251881Speter  /* Combine our prefix, original password, and appropriate padding.
314251881Speter     We won't bother padding if the prefix and password combined
315251881Speter     perfectly align on the block boundary.  If they don't,
316251881Speter     however, we'll drop a NUL byte after the password and pad with
317251881Speter     random stuff after that to the block boundary. */
318251881Speter  password_len = strlen(password);
319251881Speter  assembled_len = RANDOM_PREFIX_LEN + password_len;
320251881Speter  if ((assembled_len % block_size) == 0)
321251881Speter    {
322251881Speter      assembled = apr_palloc(scratch_pool, assembled_len);
323251881Speter      memcpy(assembled, prefix, RANDOM_PREFIX_LEN);
324251881Speter      memcpy(assembled + RANDOM_PREFIX_LEN, password, password_len);
325251881Speter    }
326251881Speter  else
327251881Speter    {
328251881Speter      const unsigned char *padding;
329251881Speter      apr_size_t pad_len = block_size - (assembled_len % block_size) - 1;
330251881Speter
331251881Speter      SVN_ERR(get_random_bytes(&padding, ctx, pad_len, scratch_pool));
332251881Speter      assembled_len = assembled_len + 1 + pad_len;
333251881Speter      assembled = apr_palloc(scratch_pool, assembled_len);
334251881Speter      memcpy(assembled, prefix, RANDOM_PREFIX_LEN);
335251881Speter      memcpy(assembled + RANDOM_PREFIX_LEN, password, password_len);
336251881Speter      *(assembled + RANDOM_PREFIX_LEN + password_len) = '\0';
337251881Speter      memcpy(assembled + RANDOM_PREFIX_LEN + password_len + 1,
338251881Speter             padding, pad_len);
339251881Speter    }
340251881Speter
341251881Speter  /* Get the length that we need to allocate.  */
342251881Speter  apr_err = apr_crypto_block_encrypt(NULL, &result_len, assembled,
343251881Speter                                     assembled_len, block_ctx);
344251881Speter  if (apr_err != APR_SUCCESS)
345251881Speter    {
346251881Speter      err = crypto_error_create(ctx, apr_err,
347251881Speter                                _("Error fetching result length"));
348251881Speter      goto cleanup;
349251881Speter    }
350251881Speter
351251881Speter  /* Allocate our result buffer.  */
352251881Speter  result = apr_palloc(result_pool, result_len);
353251881Speter
354251881Speter  /* Encrypt the block. */
355251881Speter  apr_err = apr_crypto_block_encrypt(&result, &result_len, assembled,
356251881Speter                                     assembled_len, block_ctx);
357251881Speter  if (apr_err != APR_SUCCESS)
358251881Speter    {
359251881Speter      err = crypto_error_create(ctx, apr_err,
360251881Speter                                _("Error during block encryption"));
361251881Speter      goto cleanup;
362251881Speter    }
363251881Speter
364251881Speter  /* Finalize the block encryption. Since we padded everything, this should
365251881Speter     not produce any more encrypted output.  */
366251881Speter  apr_err = apr_crypto_block_encrypt_finish(NULL,
367251881Speter                                            &ignored_result_len,
368251881Speter                                            block_ctx);
369251881Speter  if (apr_err != APR_SUCCESS)
370251881Speter    {
371251881Speter      err = crypto_error_create(ctx, apr_err,
372251881Speter                                _("Error finalizing block encryption"));
373251881Speter      goto cleanup;
374251881Speter    }
375251881Speter
376251881Speter  *ciphertext = wrap_as_string(result, result_len, result_pool);
377251881Speter  *iv = wrap_as_string(iv_vector, iv_len, result_pool);
378251881Speter  *salt = wrap_as_string(salt_vector, SALT_LEN, result_pool);
379251881Speter
380251881Speter cleanup:
381251881Speter  apr_crypto_block_cleanup(block_ctx);
382251881Speter  return err;
383251881Speter#else /* SVN_HAVE_CRYPTO */
384251881Speter  return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
385251881Speter                          "Cryptographic support is not available");
386251881Speter#endif /* SVN_HAVE_CRYPTO */
387251881Speter}
388251881Speter
389251881Speter
390251881Spetersvn_error_t *
391251881Spetersvn_crypto__decrypt_password(const char **plaintext,
392251881Speter                             svn_crypto__ctx_t *ctx,
393251881Speter                             const svn_string_t *ciphertext,
394251881Speter                             const svn_string_t *iv,
395251881Speter                             const svn_string_t *salt,
396251881Speter                             const svn_string_t *master,
397251881Speter                             apr_pool_t *result_pool,
398251881Speter                             apr_pool_t *scratch_pool)
399251881Speter{
400251881Speter#ifdef SVN_HAVE_CRYPTO
401251881Speter  svn_error_t *err = SVN_NO_ERROR;
402251881Speter  apr_status_t apr_err;
403251881Speter  apr_crypto_block_t *block_ctx = NULL;
404251881Speter  apr_size_t block_size, iv_len;
405251881Speter  apr_crypto_key_t *key = NULL;
406251881Speter  unsigned char *result;
407251881Speter  apr_size_t result_len = 0, final_len = 0;
408251881Speter
409251881Speter  /* Initialize the passphrase.  */
410251881Speter  apr_err = apr_crypto_passphrase(&key, &iv_len,
411251881Speter                                  master->data, master->len,
412251881Speter                                  (unsigned char *)salt->data, salt->len,
413251881Speter                                  APR_KEY_AES_256, APR_MODE_CBC,
414251881Speter                                  FALSE /* doPad */, NUM_ITERATIONS,
415251881Speter                                  ctx->crypto, scratch_pool);
416251881Speter  if (apr_err != APR_SUCCESS)
417251881Speter    return svn_error_trace(crypto_error_create(
418251881Speter                               ctx, apr_err,
419251881Speter                               _("Error creating derived key")));
420251881Speter  if (! key)
421251881Speter    return svn_error_create(APR_EGENERAL, NULL,
422251881Speter                            _("Error creating derived key"));
423251881Speter  if (iv_len == 0)
424251881Speter    return svn_error_create(APR_EGENERAL, NULL,
425251881Speter                            _("Unexpected IV length returned"));
426251881Speter  if (iv_len != iv->len)
427251881Speter    return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
428251881Speter                            _("Provided IV has incorrect length"));
429251881Speter
430251881Speter  apr_err = apr_crypto_block_decrypt_init(&block_ctx, &block_size,
431251881Speter                                          (unsigned char *)iv->data,
432251881Speter                                          key, scratch_pool);
433251881Speter  if ((apr_err != APR_SUCCESS) || (! block_ctx))
434251881Speter    return svn_error_trace(crypto_error_create(
435251881Speter                             ctx, apr_err,
436251881Speter                             _("Error initializing block decryption")));
437251881Speter
438251881Speter  apr_err = apr_crypto_block_decrypt(NULL, &result_len,
439251881Speter                                     (unsigned char *)ciphertext->data,
440251881Speter                                     ciphertext->len, block_ctx);
441251881Speter  if (apr_err != APR_SUCCESS)
442251881Speter    {
443251881Speter      err = crypto_error_create(ctx, apr_err,
444251881Speter                                _("Error fetching result length"));
445251881Speter      goto cleanup;
446251881Speter    }
447251881Speter
448251881Speter  result = apr_palloc(scratch_pool, result_len);
449251881Speter  apr_err = apr_crypto_block_decrypt(&result, &result_len,
450251881Speter                                     (unsigned char *)ciphertext->data,
451251881Speter                                     ciphertext->len, block_ctx);
452251881Speter  if (apr_err != APR_SUCCESS)
453251881Speter    {
454251881Speter      err = crypto_error_create(ctx, apr_err,
455251881Speter                                _("Error during block decryption"));
456251881Speter      goto cleanup;
457251881Speter    }
458251881Speter
459251881Speter  apr_err = apr_crypto_block_decrypt_finish(result + result_len, &final_len,
460251881Speter                                            block_ctx);
461251881Speter  if (apr_err != APR_SUCCESS)
462251881Speter    {
463251881Speter      err = crypto_error_create(ctx, apr_err,
464251881Speter                                _("Error finalizing block decryption"));
465251881Speter      goto cleanup;
466251881Speter    }
467251881Speter
468251881Speter  /* Copy the non-random bits of the resulting plaintext, skipping the
469251881Speter     prefix and ignoring any trailing padding. */
470251881Speter  *plaintext = apr_pstrndup(result_pool,
471251881Speter                            (const char *)(result + RANDOM_PREFIX_LEN),
472251881Speter                            result_len + final_len - RANDOM_PREFIX_LEN);
473251881Speter
474251881Speter cleanup:
475251881Speter  apr_crypto_block_cleanup(block_ctx);
476251881Speter  return err;
477251881Speter#else /* SVN_HAVE_CRYPTO */
478251881Speter  return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
479251881Speter                          "Cryptographic support is not available");
480251881Speter#endif /* SVN_HAVE_CRYPTO */
481251881Speter}
482251881Speter
483251881Speter
484251881Spetersvn_error_t *
485251881Spetersvn_crypto__generate_secret_checktext(const svn_string_t **ciphertext,
486251881Speter                                      const svn_string_t **iv,
487251881Speter                                      const svn_string_t **salt,
488251881Speter                                      const char **checktext,
489251881Speter                                      svn_crypto__ctx_t *ctx,
490251881Speter                                      const svn_string_t *master,
491251881Speter                                      apr_pool_t *result_pool,
492251881Speter                                      apr_pool_t *scratch_pool)
493251881Speter{
494251881Speter#ifdef SVN_HAVE_CRYPTO
495251881Speter  svn_error_t *err = SVN_NO_ERROR;
496251881Speter  const unsigned char *salt_vector;
497251881Speter  const unsigned char *iv_vector;
498251881Speter  const unsigned char *stuff_vector;
499251881Speter  apr_size_t iv_len;
500251881Speter  apr_crypto_key_t *key = NULL;
501251881Speter  apr_status_t apr_err;
502251881Speter  apr_crypto_block_t *block_ctx = NULL;
503251881Speter  apr_size_t block_size;
504251881Speter  apr_size_t result_len;
505251881Speter  unsigned char *result;
506251881Speter  apr_size_t ignored_result_len = 0;
507251881Speter  apr_size_t stuff_len;
508251881Speter  svn_checksum_t *stuff_sum;
509251881Speter
510251881Speter  SVN_ERR_ASSERT(ctx != NULL);
511251881Speter
512251881Speter  /* Generate the salt. */
513251881Speter  SVN_ERR(get_random_bytes(&salt_vector, ctx, SALT_LEN, result_pool));
514251881Speter
515251881Speter  /* Initialize the passphrase.  */
516251881Speter  apr_err = apr_crypto_passphrase(&key, &iv_len,
517251881Speter                                  master->data, master->len,
518251881Speter                                  salt_vector, SALT_LEN,
519251881Speter                                  APR_KEY_AES_256, APR_MODE_CBC,
520251881Speter                                  FALSE /* doPad */, NUM_ITERATIONS,
521251881Speter                                  ctx->crypto,
522251881Speter                                  scratch_pool);
523251881Speter  if (apr_err != APR_SUCCESS)
524251881Speter    return svn_error_trace(crypto_error_create(
525251881Speter                               ctx, apr_err,
526251881Speter                               _("Error creating derived key")));
527251881Speter  if (! key)
528251881Speter    return svn_error_create(APR_EGENERAL, NULL,
529251881Speter                            _("Error creating derived key"));
530251881Speter  if (iv_len == 0)
531251881Speter    return svn_error_create(APR_EGENERAL, NULL,
532251881Speter                            _("Unexpected IV length returned"));
533251881Speter
534251881Speter  /* Generate the proper length IV.  */
535251881Speter  SVN_ERR(get_random_bytes(&iv_vector, ctx, iv_len, result_pool));
536251881Speter
537251881Speter  /* Initialize block encryption. */
538251881Speter  apr_err = apr_crypto_block_encrypt_init(&block_ctx, &iv_vector, key,
539251881Speter                                          &block_size, scratch_pool);
540251881Speter  if ((apr_err != APR_SUCCESS) || (! block_ctx))
541251881Speter    return svn_error_trace(crypto_error_create(
542251881Speter                             ctx, apr_err,
543251881Speter                             _("Error initializing block encryption")));
544251881Speter
545251881Speter  /* Generate a blob of random data, block-aligned per the
546251881Speter     requirements of the encryption algorithm, but with a minimum size
547251881Speter     of our choosing.  */
548251881Speter#define MIN_STUFF_LEN 32
549251881Speter  if (MIN_STUFF_LEN % block_size)
550251881Speter    stuff_len = MIN_STUFF_LEN + (block_size - (MIN_STUFF_LEN % block_size));
551251881Speter  else
552251881Speter    stuff_len = MIN_STUFF_LEN;
553251881Speter  SVN_ERR(get_random_bytes(&stuff_vector, ctx, stuff_len, scratch_pool));
554251881Speter
555251881Speter  /* ### FIXME:  This should be a SHA-256.  */
556251881Speter  SVN_ERR(svn_checksum(&stuff_sum, svn_checksum_sha1, stuff_vector,
557251881Speter                       stuff_len, scratch_pool));
558251881Speter
559251881Speter  /* Get the length that we need to allocate.  */
560251881Speter  apr_err = apr_crypto_block_encrypt(NULL, &result_len, stuff_vector,
561251881Speter                                     stuff_len, block_ctx);
562251881Speter  if (apr_err != APR_SUCCESS)
563251881Speter    {
564251881Speter      err = crypto_error_create(ctx, apr_err,
565251881Speter                                _("Error fetching result length"));
566251881Speter      goto cleanup;
567251881Speter    }
568251881Speter
569251881Speter  /* Allocate our result buffer.  */
570251881Speter  result = apr_palloc(result_pool, result_len);
571251881Speter
572251881Speter  /* Encrypt the block. */
573251881Speter  apr_err = apr_crypto_block_encrypt(&result, &result_len, stuff_vector,
574251881Speter                                     stuff_len, block_ctx);
575251881Speter  if (apr_err != APR_SUCCESS)
576251881Speter    {
577251881Speter      err = crypto_error_create(ctx, apr_err,
578251881Speter                                _("Error during block encryption"));
579251881Speter      goto cleanup;
580251881Speter    }
581251881Speter
582251881Speter  /* Finalize the block encryption. Since we padded everything, this should
583251881Speter     not produce any more encrypted output.  */
584251881Speter  apr_err = apr_crypto_block_encrypt_finish(NULL,
585251881Speter                                            &ignored_result_len,
586251881Speter                                            block_ctx);
587251881Speter  if (apr_err != APR_SUCCESS)
588251881Speter    {
589251881Speter      err = crypto_error_create(ctx, apr_err,
590251881Speter                                _("Error finalizing block encryption"));
591251881Speter      goto cleanup;
592251881Speter    }
593251881Speter
594251881Speter  *ciphertext = wrap_as_string(result, result_len, result_pool);
595251881Speter  *iv = wrap_as_string(iv_vector, iv_len, result_pool);
596251881Speter  *salt = wrap_as_string(salt_vector, SALT_LEN, result_pool);
597251881Speter  *checktext = svn_checksum_to_cstring(stuff_sum, result_pool);
598251881Speter
599251881Speter cleanup:
600251881Speter  apr_crypto_block_cleanup(block_ctx);
601251881Speter  return err;
602251881Speter#else /* SVN_HAVE_CRYPTO */
603251881Speter  return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
604251881Speter                          "Cryptographic support is not available");
605251881Speter#endif /* SVN_HAVE_CRYPTO */
606251881Speter}
607251881Speter
608251881Speter
609251881Spetersvn_error_t *
610251881Spetersvn_crypto__verify_secret(svn_boolean_t *is_valid,
611251881Speter                          svn_crypto__ctx_t *ctx,
612251881Speter                          const svn_string_t *master,
613251881Speter                          const svn_string_t *ciphertext,
614251881Speter                          const svn_string_t *iv,
615251881Speter                          const svn_string_t *salt,
616251881Speter                          const char *checktext,
617251881Speter                          apr_pool_t *scratch_pool)
618251881Speter{
619251881Speter#ifdef SVN_HAVE_CRYPTO
620251881Speter  svn_error_t *err = SVN_NO_ERROR;
621251881Speter  apr_status_t apr_err;
622251881Speter  apr_crypto_block_t *block_ctx = NULL;
623251881Speter  apr_size_t block_size, iv_len;
624251881Speter  apr_crypto_key_t *key = NULL;
625251881Speter  unsigned char *result;
626251881Speter  apr_size_t result_len = 0, final_len = 0;
627251881Speter  svn_checksum_t *result_sum;
628251881Speter
629251881Speter  *is_valid = FALSE;
630251881Speter
631251881Speter  /* Initialize the passphrase.  */
632251881Speter  apr_err = apr_crypto_passphrase(&key, &iv_len,
633251881Speter                                  master->data, master->len,
634251881Speter                                  (unsigned char *)salt->data, salt->len,
635251881Speter                                  APR_KEY_AES_256, APR_MODE_CBC,
636251881Speter                                  FALSE /* doPad */, NUM_ITERATIONS,
637251881Speter                                  ctx->crypto, scratch_pool);
638251881Speter  if (apr_err != APR_SUCCESS)
639251881Speter    return svn_error_trace(crypto_error_create(
640251881Speter                               ctx, apr_err,
641251881Speter                               _("Error creating derived key")));
642251881Speter  if (! key)
643251881Speter    return svn_error_create(APR_EGENERAL, NULL,
644251881Speter                            _("Error creating derived key"));
645251881Speter  if (iv_len == 0)
646251881Speter    return svn_error_create(APR_EGENERAL, NULL,
647251881Speter                            _("Unexpected IV length returned"));
648251881Speter  if (iv_len != iv->len)
649251881Speter    return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
650251881Speter                            _("Provided IV has incorrect length"));
651251881Speter
652251881Speter  apr_err = apr_crypto_block_decrypt_init(&block_ctx, &block_size,
653251881Speter                                          (unsigned char *)iv->data,
654251881Speter                                          key, scratch_pool);
655251881Speter  if ((apr_err != APR_SUCCESS) || (! block_ctx))
656251881Speter    return svn_error_trace(crypto_error_create(
657251881Speter                             ctx, apr_err,
658251881Speter                             _("Error initializing block decryption")));
659251881Speter
660251881Speter  apr_err = apr_crypto_block_decrypt(NULL, &result_len,
661251881Speter                                     (unsigned char *)ciphertext->data,
662251881Speter                                     ciphertext->len, block_ctx);
663251881Speter  if (apr_err != APR_SUCCESS)
664251881Speter    {
665251881Speter      err = crypto_error_create(ctx, apr_err,
666251881Speter                                _("Error fetching result length"));
667251881Speter      goto cleanup;
668251881Speter    }
669251881Speter
670251881Speter  result = apr_palloc(scratch_pool, result_len);
671251881Speter  apr_err = apr_crypto_block_decrypt(&result, &result_len,
672251881Speter                                     (unsigned char *)ciphertext->data,
673251881Speter                                     ciphertext->len, block_ctx);
674251881Speter  if (apr_err != APR_SUCCESS)
675251881Speter    {
676251881Speter      err = crypto_error_create(ctx, apr_err,
677251881Speter                                _("Error during block decryption"));
678251881Speter      goto cleanup;
679251881Speter    }
680251881Speter
681251881Speter  apr_err = apr_crypto_block_decrypt_finish(result + result_len, &final_len,
682251881Speter                                            block_ctx);
683251881Speter  if (apr_err != APR_SUCCESS)
684251881Speter    {
685251881Speter      err = crypto_error_create(ctx, apr_err,
686251881Speter                                _("Error finalizing block decryption"));
687251881Speter      goto cleanup;
688251881Speter    }
689251881Speter
690251881Speter  /* ### FIXME:  This should be a SHA-256.  */
691251881Speter  SVN_ERR(svn_checksum(&result_sum, svn_checksum_sha1, result,
692251881Speter                       result_len + final_len, scratch_pool));
693251881Speter
694251881Speter  *is_valid = strcmp(checktext,
695251881Speter                     svn_checksum_to_cstring(result_sum, scratch_pool)) == 0;
696251881Speter
697251881Speter cleanup:
698251881Speter  apr_crypto_block_cleanup(block_ctx);
699251881Speter  return err;
700251881Speter#else /* SVN_HAVE_CRYPTO */
701251881Speter  *is_valid = FALSE;
702251881Speter  return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
703251881Speter                          "Cryptographic support is not available");
704251881Speter#endif /* SVN_HAVE_CRYPTO */
705251881Speter}
706