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