1/* 2 * Copyright (c) 2008 Kungliga Tekniska Högskolan 3 * (Royal Institute of Technology, Stockholm, Sweden). 4 * All rights reserved. 5 * 6 * Portions Copyright (c) 2009 - 2010 Apple Inc. All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * 3. Neither the name of KTH nor the names of its contributors may be 20 * used to endorse or promote products derived from this software without 21 * specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY KTH AND ITS CONTRIBUTORS ``AS IS'' AND ANY 24 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 26 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KTH OR ITS CONTRIBUTORS BE 27 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 28 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 29 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 30 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 31 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 32 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 33 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 */ 35 36#include "scram.h" 37 38#ifdef ENABLE_SCRAM 39 40static void 41scram_data_zero(heim_scram_data *data) 42{ 43 if (data) { 44 data->data = NULL; 45 data->length = 0; 46 } 47} 48 49void 50heim_scram_data_free(heim_scram_data *data) 51{ 52 free(data->data); 53 scram_data_zero(data); 54} 55 56static void 57scram_data_alloc(heim_scram_data *to, size_t length) 58{ 59 to->length = length; 60 to->data = malloc(to->length); 61 heim_assert(to->data != NULL, "out of memory"); 62} 63 64static void 65scram_data_copy(heim_scram_data *to, void *data, size_t length) 66{ 67 scram_data_alloc(to, length); 68 memcpy(to->data, data, length); 69} 70 71 72static heim_scram_pairs * 73scram_pairs_new(void) 74{ 75 heim_scram_pairs *d; 76 d = calloc(1, sizeof(*d)); 77 if (d == NULL) 78 return NULL; 79 d->flags = SCRAM_ARRAY_ALLOCATED|SCRAM_PAIR_ALLOCATED; 80 return d; 81} 82 83void 84_heim_scram_pairs_free(heim_scram_pairs *d) 85{ 86 if (d == NULL) 87 return; 88 89 if (d->flags & SCRAM_PAIR_ALLOCATED) { 90 size_t i; 91 for (i = 0; i < d->len; i++) 92 free(d->val[i].data.data); 93 } 94 if (d->flags & SCRAM_ARRAY_ALLOCATED) 95 free(d->val); 96 free(d); 97} 98 99 100static heim_scram_data * 101scram_find_type(heim_scram_pairs *d, char type) 102{ 103 size_t i; 104 for (i = 0; i < d->len; i++) 105 if (d->val[i].type == type) 106 return &d->val[i].data; 107 return NULL; 108} 109 110static int 111scram_add_type(heim_scram_pairs *d, char type, heim_scram_data *data) 112{ 113 void *ptr; 114 115 if ((d->flags & (SCRAM_ARRAY_ALLOCATED|SCRAM_PAIR_ALLOCATED)) != (SCRAM_ARRAY_ALLOCATED|SCRAM_PAIR_ALLOCATED)) 116 return EINVAL; 117 118 ptr = realloc(d->val, (d->len + 1) * sizeof(d->val[0])); 119 if (ptr == NULL) 120 return EINVAL; 121 d->val = ptr; 122 123 d->val[d->len].type = type; 124 scram_data_copy(&d->val[d->len].data, data->data, data->length); 125 126 d->len++; 127 128 return 0; 129} 130 131static int 132scram_add_string(heim_scram_pairs *d, char type, const char *str) 133{ 134 heim_scram_data data; 135 data.data = rk_UNCONST(str); 136 data.length = strlen(str); 137 return scram_add_type(d, type, &data); 138} 139 140static int 141scram_add_base64(heim_scram_pairs *d, char type, heim_scram_data *data) 142{ 143 char *str; 144 int ret; 145 146 if (base64_encode(data->data, (int)data->length, &str) < 0) 147 return ENOMEM; 148 149 ret = scram_add_string(d, type, str); 150 free(str); 151 152 return ret; 153} 154 155struct heim_scram_method_desc { 156 CCDigestAlg dalg; 157 CCHmacAlgorithm halg; 158 size_t halglength; 159 CCPseudoRandomAlgorithm alg; 160 size_t length; 161}; 162 163struct heim_scram_method_desc heim_scram_digest_sha1_s = { 164 kCCDigestSHA1, 165 kCCHmacAlgSHA1, 166 CC_SHA1_DIGEST_LENGTH, 167 kCCPRFHmacAlgSHA1, 168 CC_SHA1_DIGEST_LENGTH 169}; 170 171struct heim_scram_method_desc heim_scram_digest_sha256_s = { 172 kCCDigestSHA256, 173 kCCHmacAlgSHA256, 174 CC_SHA256_DIGEST_LENGTH, 175 kCCPRFHmacAlgSHA256, 176 CC_SHA256_DIGEST_LENGTH 177}; 178 179int 180_heim_scram_parse(heim_scram_data *data, heim_scram_pairs **pd) 181{ 182 size_t i, n, start; 183 unsigned char *p; 184 heim_scram_pairs *d; 185 186 *pd = NULL; 187 188 d = scram_pairs_new(); 189 190 d->flags &= ~SCRAM_PAIR_ALLOCATED; 191 192 if (data->length < 2) 193 return EINVAL; 194 195 p = data->data; 196 197 if (memcmp(p, "n,", 2) == 0) { 198 d->flags |= SCRAM_BINDINGS_NO; 199 start = 2; 200 } else if (memcmp(p, "y,", 2) == 0) { 201 d->flags |= SCRAM_BINDINGS_YES; 202 start = 2; 203 } else 204 start = 0; 205 206 /* count the , */ 207 for (n = 1, i = start; i < data->length; i++) 208 if (p[i] == ',') 209 n++; 210 211 d->val = calloc(n, sizeof(d->val[0])); 212 if (d->val == NULL) 213 return ENOMEM; 214 d->len = n; 215 216 /* now parse up the arguments */ 217 i = start; 218 n = 0; 219 while (n < d->len && i < data->length) { 220 size_t m; 221 222 if (i > data->length - 2) 223 goto bad; 224 d->val[n].type = p[i]; 225 if (p[i + 1] != '=') 226 goto bad; 227 i += 2; 228 d->val[n].data.data = &p[i]; 229 m = i; 230 while (i < data->length && p[i] != ',') 231 i++; 232 d->val[n].data.length = i - m; 233 n++; 234 i++; /* skip over , */ 235 } 236 237 *pd = d; 238 239 return 0; 240 bad: 241 _heim_scram_pairs_free(d); 242 return EINVAL; 243} 244 245static int 246remove_proof(heim_scram_data *in, heim_scram_data *out) 247{ 248 unsigned char *p; 249 size_t i; 250 251 if (in->length < 3) 252 return HSCRAM_ERR_INVALID_PROOF; 253 254 p = in->data; 255 for (i = in->length - 1; i > 0; i--) 256 if (p[i] == ',') 257 break; 258 if (i == 0) 259 return HSCRAM_ERR_INVALID_PROOF; 260 if (i + 3 > in->length) 261 return HSCRAM_ERR_INVALID_PROOF; 262 if (p[i + 1] != 'p') 263 return HSCRAM_ERR_INVALID_PROOF; 264 if (p[i + 2] != '=') 265 return HSCRAM_ERR_INVALID_PROOF; 266 267 out->length = i; 268 out->data = p; 269 270 return 0; 271} 272 273int 274_heim_scram_unparse(heim_scram_pairs *d, heim_scram_data *out) 275{ 276 size_t i, len; 277 unsigned char *p; 278 279 heim_assert(d->len != 0, "no key pairs"); 280 281 len = d->len * 3 - 1; /* t=, */ 282 283 if (d->flags & (SCRAM_BINDINGS_YES|SCRAM_BINDINGS_NO)) 284 len += 2; 285 286 for (i = 0; i < d->len; i++) 287 len += d->val[i].data.length; 288 289 scram_data_alloc(out, len); 290 p = out->data; 291 292 if (d->flags & SCRAM_BINDINGS_YES) { 293 memcpy(p, "y,", 2); 294 p += 2; 295 } else if (d->flags & SCRAM_BINDINGS_NO) { 296 memcpy(p, "n,", 2); 297 p += 2; 298 } 299 300 for (i = 0; i < d->len; i++) { 301 *p++ = d->val[i].type; 302 *p++ = '='; 303 memcpy(p, d->val[i].data.data, d->val[i].data.length); 304 p += d->val[i].data.length; 305 if (i + 1 < d->len) 306 *p++ = ','; 307 } 308 heim_assert((size_t)((p - (unsigned char *)out->data)) == out->length, "generated packet wrong length"); 309 return 0; 310} 311 312#define TOPTIONAL 0x100 313 314static const int client_first[] = 315 { 'p' | TOPTIONAL, 'm' | TOPTIONAL, 'n', 'r', 0 }; 316static const int server_first[] = 317 { 'm' | TOPTIONAL, 'r', 's', 'i', 0 }; 318static const int client_final[] = 319 { 'c', 'r', 'p', 0 }; 320static const int server_final[] = 321 { 'v', 0 }; 322 323static int 324_scram_validate(heim_scram_pairs *d, const int *template) 325{ 326 size_t i = 0; 327 int same; 328 while(*template) { 329 same = (*template & 0xff) == d->val[i].type; 330 if (!same && (*template & TOPTIONAL) == 0) 331 return HSCRAM_ERR_INVALID_MESSAGE; 332 else if (same) 333 i++; 334 template++; 335 } 336 return 0; 337} 338 339static int 340scram_authmessage_signature(heim_scram_method method, 341 const heim_scram_data *key, 342 const heim_scram_data *c1, 343 const heim_scram_data *s1, 344 const heim_scram_data *c2noproof, 345 const heim_scram_data *clientKey, 346 heim_scram_data *sig) 347{ 348 CCHmacContext hmac; 349 350 CCHmacInit(&hmac, method->halg, key->data, key->length); 351 352 /* only for session key generation */ 353 if (clientKey) { 354 CCHmacUpdate(&hmac, "GSS-API session key", 19); 355 CCHmacUpdate(&hmac, clientKey->data, clientKey->length); 356 } 357 358 /* Build AuthMessage */ 359 CCHmacUpdate(&hmac, c1->data, c1->length); 360 CCHmacUpdate(&hmac, (const void *)",", 1); 361 CCHmacUpdate(&hmac, s1->data, s1->length); 362 CCHmacUpdate(&hmac, (const void *)",", 1); 363 CCHmacUpdate(&hmac, c2noproof->data, c2noproof->length); 364 365 scram_data_alloc(sig, method->halglength); 366 367 CCHmacFinal(&hmac, sig->data); 368 memset(&hmac, 0, sizeof(hmac)); 369 370 return 0; 371} 372 373/* generate "printable" nonce */ 374 375static void 376generate_nonce(size_t len, heim_scram_data *result) 377{ 378 unsigned char *p; 379 char *str; 380 381 p = malloc(len); 382 heim_assert(p != NULL, "out of memory"); 383 if (CCRandomCopyBytes(kCCRandomDefault, p, len) != 0) 384 heim_abort("CCRandomCopyBytes failes"); 385 386 if (base64_encode(p, (int)len, &str) < 0) 387 heim_abort("base64 encode failed"); 388 389 free(p); 390 391 result->data = str; 392 result->length = strlen(str); 393} 394 395int 396heim_scram_client1(const char *username, 397 heim_scram_data *ch, 398 heim_scram_method method, 399 heim_scram **scram, 400 heim_scram_data *out) 401{ 402 heim_scram_pairs *msg; 403 heim_scram *s; 404 int ret; 405 406 scram_data_zero(out); 407 *scram = NULL; 408 409 s = calloc(1, sizeof(*s)); 410 if (s == NULL) 411 return ENOMEM; 412 413 s->type = CLIENT; 414 s->method = method; 415 416 generate_nonce(12, &s->nonce); 417 418 msg = scram_pairs_new(); 419 420 if (ch == NULL) 421 msg->flags |= SCRAM_BINDINGS_NO; 422 423 ret = scram_add_string(msg, 'n', username); 424 if (ret) { 425 _heim_scram_pairs_free(msg); 426 heim_scram_free(s); 427 return ret; 428 } 429 430 ret = scram_add_type(msg, 'r', &s->nonce); 431 if (ret) { 432 _heim_scram_pairs_free(msg); 433 heim_scram_free(s); 434 return ret; 435 } 436 437 ret = _heim_scram_unparse(msg, &s->client1); 438 _heim_scram_pairs_free(msg); 439 if (ret) { 440 heim_scram_free(s); 441 return ret; 442 } 443 444 *out = s->client1; 445 *scram = s; 446 447 return 0; 448} 449 450int 451heim_scram_server1(heim_scram_data *in, 452 heim_scram_data *ch, 453 heim_scram_method method, 454 struct heim_scram_server *server, 455 void *ctx, 456 heim_scram **scram, 457 heim_scram_data *out) 458{ 459 heim_scram_data *user, *clientnonce; 460 heim_scram *s; 461 heim_scram_pairs *p = NULL, *q = NULL; 462 heim_scram_data salt, servernonce; 463 unsigned int iteration; 464 char iter[12]; 465 int ret; 466 467 memset(&p, 0, sizeof(p)); 468 469 scram_data_zero(out); 470 scram_data_zero(&salt); 471 scram_data_zero(&servernonce); 472 473 *scram = NULL; 474 475 ret = _heim_scram_parse(in, &p); 476 if (ret) 477 return ret; 478 479 ret = _scram_validate(p, client_first); 480 if (ret) { 481 _heim_scram_pairs_free(p); 482 return ret; 483 } 484 485 s = calloc(1, sizeof(*s)); 486 if (s == NULL) 487 goto out; 488 489 s->type = SERVER; 490 s->server = server; 491 s->ctx = ctx; 492 s->method = method; 493 494 scram_data_copy(&s->client1, in->data, in->length); 495 496 user = scram_find_type(p, 'n'); 497 clientnonce = scram_find_type(p, 'r'); 498 499 heim_assert(clientnonce != NULL && user != NULL, "validate doesn't work"); 500 501 scram_data_copy(&s->user, user->data, user->length); 502 503 ret = (s->server->param)(s->ctx, &s->user, &salt, 504 &iteration, &servernonce); 505 if (ret) 506 goto out; 507 508 /* 509 * If ->param didn't generate nonce, let do it ourself 510 */ 511 512 if (servernonce.length == 0) 513 generate_nonce(12, &servernonce); 514 515 s->nonce.length = clientnonce->length + servernonce.length; 516 s->nonce.data = malloc(s->nonce.length); 517 518 memcpy(s->nonce.data, clientnonce->data, clientnonce->length); 519 memcpy(((unsigned char *)s->nonce.data) + clientnonce->length, 520 servernonce.data, servernonce.length); 521 522 q = scram_pairs_new(); 523 524 ret = scram_add_type(q, 'r', &s->nonce); 525 if (ret) 526 goto out; 527 528 ret = scram_add_type(q, 's', &salt); 529 if (ret) 530 goto out; 531 532 snprintf(iter, sizeof(iter), "%lu", (unsigned long)iteration); 533 ret = scram_add_string(q, 'i', iter); 534 if (ret) 535 goto out; 536 537 ret = _heim_scram_unparse(q, &s->server1); 538 if (ret) 539 goto out; 540 541 *out = s->server1; 542 *scram = s; 543 544 out: 545 if (ret) 546 heim_scram_free(s); 547 _heim_scram_pairs_free(p); 548 _heim_scram_pairs_free(q); 549 heim_scram_data_free(&salt); 550 heim_scram_data_free(&servernonce); 551 552 return ret; 553} 554 555int 556heim_scram_generate(heim_scram_method method, 557 const heim_scram_data *stored_key, 558 const heim_scram_data *server_key, 559 const heim_scram_data *c1, 560 const heim_scram_data *s1, 561 const heim_scram_data *c2noproof, 562 heim_scram_data *clientSig, 563 heim_scram_data *serverSig) 564{ 565 int ret; 566 567 scram_data_zero(clientSig); 568 scram_data_zero(serverSig); 569 570 ret = scram_authmessage_signature(method, stored_key, 571 c1, s1, c2noproof, NULL, clientSig); 572 if (ret) 573 return ret; 574 575 ret = scram_authmessage_signature(method, server_key, 576 c1, s1, c2noproof, NULL, serverSig); 577 if (ret) 578 heim_scram_data_free(clientSig); 579 580 return ret; 581} 582 583int 584heim_scram_session_key(heim_scram_method method, 585 const heim_scram_data *stored_key, 586 const heim_scram_data *client_key, 587 const heim_scram_data *c1, 588 const heim_scram_data *s1, 589 const heim_scram_data *c2noproof, 590 heim_scram_data *sessionKey) 591{ 592 return scram_authmessage_signature(method, 593 stored_key, 594 c1, s1, c2noproof, 595 client_key, 596 sessionKey); 597} 598 599int 600heim_scram_validate_client_signature(heim_scram_method method, 601 const heim_scram_data *stored_key, 602 const heim_scram_data *client_signature, 603 const heim_scram_data *proof, 604 heim_scram_data *clientKey) 605{ 606 unsigned char *p, *q, *u = NULL; 607 size_t length, n; 608 int ret; 609 610 scram_data_zero(clientKey); 611 612 if (stored_key->length != method->length || client_signature->length != method->length || proof->length != method->length) 613 return EINVAL; 614 615 q = client_signature->data; 616 p = proof->data; 617 u = malloc(method->length); 618 if (u == NULL) 619 return ENOMEM; 620 621 for (n = 0 ; n < proof->length; n++) 622 u[n] = p[n] ^ q[n]; 623 624 scram_data_copy(clientKey, u, proof->length); 625 626 length = method->length; 627 ret = CCDigest(method->dalg, u, length, u); 628 if (ret != 0) { 629 ret = EINVAL; 630 goto out; 631 } 632 633 ret = memcmp(u, stored_key->data, stored_key->length); 634 if (ret != 0) 635 ret = EINVAL; 636 637 out: 638 free(u); 639 if (ret) 640 heim_scram_data_free(clientKey); 641 642 return ret; 643} 644 645 646static int 647client_calculate(void *ctx, 648 heim_scram_method method, 649 unsigned int iterations, 650 heim_scram_data *salt, 651 const heim_scram_data *c1, 652 const heim_scram_data *s1, 653 const heim_scram_data *c2noproof, 654 heim_scram_data *proof, 655 heim_scram_data *server, 656 heim_scram_data *sessionKey) 657{ 658 heim_scram_data client, stored, server_key; 659 unsigned char *p, *q; 660 size_t n; 661 int ret; 662 663 scram_data_zero(proof); 664 scram_data_zero(server); 665 666 ret = heim_scram_stored_key(method, ctx, iterations, salt, 667 &client, &stored, &server_key); 668 if (ret) 669 goto out; 670 671 ret = heim_scram_generate(method, &stored, &server_key, 672 c1, s1, c2noproof, proof, server); 673 if (ret) 674 goto out; 675 676 677 ret = heim_scram_session_key(method, &stored, &client, 678 c1, s1, c2noproof, 679 sessionKey); 680 if (ret) 681 goto out; 682 683 /* 684 * Now client_key XOR proof 685 */ 686 p = proof->data; 687 q = client.data; 688 689 heim_assert(proof->length == client.length, "proof.length == client.length"); 690 691 for (n = 0 ; n < client.length; n++) 692 p[n] = p[n] ^ q[n]; 693 694 out: 695 heim_scram_data_free(&server_key); 696 heim_scram_data_free(&stored); 697 heim_scram_data_free(&client); 698 699 return ret; 700} 701 702 703struct heim_scram_client heim_scram_client_password_procs_s = { 704 .version = SCRAM_CLIENT_VERSION_1, 705 .calculate = client_calculate 706}; 707 708int 709heim_scram_client2(heim_scram_data *in, 710 struct heim_scram_client *client, 711 void *ctx, 712 struct heim_scram *scram, 713 heim_scram_data *out) 714{ 715 heim_scram_pairs *p, *q = NULL; 716 heim_scram_data *servernonce, *salt, *iterations; 717 unsigned int iter; 718 char *str; 719 int ret; 720 721 scram_data_zero(out); 722 723 if (scram->type != CLIENT) 724 return HSCRAM_ERR_INVALID_ROLE; 725 726 ret = _heim_scram_parse(in, &p); 727 if (ret) 728 return ret; 729 730 ret = _scram_validate(p, server_first); 731 if (ret) { 732 _heim_scram_pairs_free(p); 733 return ret; 734 } 735 736 scram_data_copy(&scram->server1, in->data, in->length); 737 738 servernonce = scram_find_type(p, 'r'); 739 740 /* Validate that the server reflects back our nonce to us */ 741 if (servernonce->length < scram->nonce.length || memcmp(scram->nonce.data, servernonce->data, scram->nonce.length) != 0) { 742 _heim_scram_pairs_free(p); 743 return EINVAL; 744 } 745 746 salt = scram_find_type(p, 's'); 747 iterations = scram_find_type(p, 'i'); 748 749 heim_assert(servernonce != NULL && salt != NULL && iterations != NULL, 750 "validate doesn't work"); 751 752 str = malloc(iterations->length + 1); 753 memcpy(str, iterations->data, iterations->length); 754 str[iterations->length] = '\0'; 755 iter = atoi(str); 756 free(str); 757 if (iter == 0) { 758 _heim_scram_pairs_free(p); 759 return EINVAL; 760 } 761 762 q = scram_pairs_new(); 763 764 scram_add_string(q, 'c', "biws"); 765 scram_add_type(q, 'r', servernonce); 766 767 ret = _heim_scram_unparse(q, out); 768 if (ret) 769 goto out; 770 771 ret = client->calculate(ctx, scram->method, 772 iter, salt, &scram->client1, &scram->server1, out, 773 &scram->ClientProof, &scram->ServerSignature, 774 &scram->SessionKey); 775 heim_scram_data_free(out); 776 if (ret) 777 goto out; 778 779 ret = scram_add_base64(q, 'p', &scram->ClientProof); 780 if (ret) 781 goto out; 782 783 ret = _heim_scram_unparse(q, out); 784 if (ret) 785 goto out; 786 787 out: 788 _heim_scram_pairs_free(p); 789 _heim_scram_pairs_free(q); 790 791 return ret; 792} 793 794 795int 796heim_scram_server2(heim_scram_data *in, 797 struct heim_scram *scram, 798 heim_scram_data *out) 799{ 800 heim_scram_pairs *p = NULL, *q = NULL; 801 heim_scram_data *nonce, *proof, binaryproof, noproof, server; 802 int ret; 803 804 scram_data_zero(out); 805 scram_data_zero(&binaryproof); 806 807 if (scram->type != SERVER) 808 return HSCRAM_ERR_INVALID_ROLE; 809 810 ret = _heim_scram_parse(in, &p); 811 if (ret) 812 return ret; 813 814 ret = _scram_validate(p, client_final); 815 if (ret) 816 goto out; 817 818 /* chbinding = scram_find_type(p, 'c'); */ 819 nonce = scram_find_type(p, 'r'); 820 821 /* Validate that the client reflects back our nonce to us */ 822 if (nonce->length != scram->nonce.length || memcmp(scram->nonce.data, nonce->data, scram->nonce.length) != 0) { 823 ret = EINVAL; 824 goto out; 825 } 826 827 proof = scram_find_type(p, 'p'); 828 829 scram_data_alloc(&binaryproof, proof->length + 1); 830 memcpy(binaryproof.data, proof->data, proof->length); 831 ((char *)binaryproof.data)[proof->length] = '\0'; 832 ret = base64_decode(binaryproof.data, binaryproof.data); 833 if (ret < 0) { 834 ret = EINVAL; 835 goto out; 836 } 837 binaryproof.length = ret; 838 839 840 ret = remove_proof(in, &noproof); 841 if (ret) 842 goto out; 843 844 ret = scram->server->calculate(scram->ctx, 845 scram->method, 846 &scram->user, 847 &scram->client1, 848 &scram->server1, 849 &noproof, 850 &binaryproof, 851 &server, 852 &scram->SessionKey); 853 if (ret) 854 goto out; 855 856 857 q = scram_pairs_new(); 858 859 ret = scram_add_base64(q, 'v', &server); 860 heim_scram_data_free(&server); 861 if (ret) 862 goto out; 863 864 ret = _heim_scram_unparse(q, out); 865 866 out: 867 heim_scram_data_free(&binaryproof); 868 _heim_scram_pairs_free(p); 869 _heim_scram_pairs_free(q); 870 871 return ret; 872} 873 874int 875heim_scram_client3(heim_scram_data *in, 876 heim_scram *scram) 877{ 878 heim_scram_pairs *p; 879 heim_scram_data *data; 880 char *str; 881 int ret; 882 883 if (scram->type != CLIENT) 884 return HSCRAM_ERR_INVALID_ROLE; 885 886 ret = _heim_scram_parse(in, &p); 887 if (ret) 888 return ret; 889 890 ret = _scram_validate(p, server_final); 891 if (ret) { 892 _heim_scram_pairs_free(p); 893 return ret; 894 } 895 896 data = scram_find_type(p, 'v'); 897 898 if (base64_encode(scram->ServerSignature.data, 899 (int)scram->ServerSignature.length, 900 &str) < 0) { 901 ret = EINVAL; 902 goto out; 903 } 904 905 if (strlen(str) != data->length || 906 memcmp(str, data->data, data->length) != 0) 907 ret = EINVAL; 908 else 909 ret = 0; 910 911 free(str); 912 913 out: 914 _heim_scram_pairs_free(p); 915 916 return ret; 917} 918 919int 920heim_scram_get_channel_binding(heim_scram *scram, 921 heim_scram_data *ch) 922{ 923 scram_data_zero(ch); 924 925 return 0; 926} 927 928int 929heim_scram_get_session_key(heim_scram *scram, 930 heim_scram_data *sessionKey) 931{ 932 scram_data_copy(sessionKey, scram->SessionKey.data, scram->SessionKey.length); 933 return 0; 934} 935 936 937void 938heim_scram_free(heim_scram *scram) 939{ 940 if (scram == NULL) 941 return; 942 943 heim_scram_data_free(&scram->client1); 944 heim_scram_data_free(&scram->server1); 945 heim_scram_data_free(&scram->nonce); 946 heim_scram_data_free(&scram->ClientProof); 947 heim_scram_data_free(&scram->ServerSignature); 948 heim_scram_data_free(&scram->SessionKey); 949 950 951 memset(scram, 0, sizeof(*scram)); 952 free(scram); 953} 954 955 956int 957heim_scram_salted_key(heim_scram_method method, 958 const char *password, 959 unsigned int iterations, 960 heim_scram_data *salt, 961 heim_scram_data *data) 962{ 963 heim_scram_data key; 964 size_t in32_len, out32_len, pwlen; 965 uint32_t *in32, *out32; 966 char *pw = NULL; 967 int ret; 968 969 scram_data_zero(data); 970 971 key.length = method->length; 972 key.data = malloc(key.length); 973 if (key.data == NULL) 974 return ENOMEM; 975 976 ret = wind_utf8ucs4_copy(password, &in32, &in32_len); 977 if (ret) { 978 heim_scram_data_free(&key); 979 return ret; 980 } 981 982 if (in32_len > UINT_MAX/(sizeof(out32[0]) * 4)) { 983 heim_scram_data_free(&key); 984 return ERANGE; 985 } 986 987 out32_len = in32_len * 4; 988 out32 = malloc(out32_len * sizeof(out32[0])); 989 if (out32 == NULL) { 990 heim_scram_data_free(&key); 991 return ENOMEM; 992 } 993 994 ret = wind_stringprep(in32, in32_len, out32, &out32_len, WIND_PROFILE_SASL); 995 free(in32); 996 if (ret) { 997 free(out32); 998 heim_scram_data_free(&key); 999 return ret; 1000 } 1001 1002 ret = wind_ucs4utf8_copy(out32, out32_len, &pw, &pwlen); 1003 free(out32); 1004 if (ret) { 1005 heim_scram_data_free(&key); 1006 return ret; 1007 } 1008 1009 ret = CCKeyDerivationPBKDF(kCCPBKDF2, pw, pwlen, 1010 salt->data, salt->length, 1011 method->alg, iterations, 1012 key.data, key.length); 1013 if (ret) { 1014 heim_scram_data_free(&key); 1015 return ret; 1016 } 1017 1018 *data = key; 1019 1020 return 0; 1021} 1022 1023int 1024heim_scram_stored_key(heim_scram_method method, 1025 const char *password, 1026 unsigned int iterations, 1027 heim_scram_data *salt, 1028 heim_scram_data *client_key, 1029 heim_scram_data *stored_key, 1030 heim_scram_data *server_key) 1031{ 1032 size_t length; 1033 heim_scram_data sk; 1034 int ret; 1035 1036 scram_data_zero(client_key); 1037 scram_data_zero(stored_key); 1038 scram_data_zero(server_key); 1039 1040 ret = heim_scram_salted_key(method, password, 1041 iterations, salt, &sk); 1042 if (ret) 1043 return ret; 1044 1045 length = method->halglength; 1046 scram_data_alloc(stored_key, length); 1047 scram_data_alloc(client_key, length); 1048 1049 CCHmac(method->halg, sk.data, sk.length, "Client Key", 10, client_key->data); 1050 1051 ret = CCDigest(method->dalg, client_key->data, length, 1052 stored_key->data); 1053 if (ret) { 1054 heim_scram_data_free(&sk); 1055 return EINVAL; 1056 } 1057 1058 if (server_key) { 1059 scram_data_alloc(server_key, length); 1060 1061 CCHmac(method->halg, sk.data, sk.length, "Server Key", 10, 1062 server_key->data); 1063 } 1064 1065 heim_scram_data_free(&sk); 1066 1067 return 0; 1068} 1069 1070#endif 1071