1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * crypto.c 24 * 25 * Copyright (c) 1997, by Sun Microsystems, Inc. 26 * All rights reserved. 27 * 28 */ 29 30 #pragma ident "%Z%%M% %I% %E% SMI" 31 32 #include <sys/note.h> 33 #include "dh_gssapi.h" 34 #include "crypto.h" 35 36 /* Release the storage for a signature */ 37 void 38 __free_signature(dh_signature_t sig) 39 { 40 Free(sig->dh_signature_val); 41 sig->dh_signature_val = NULL; 42 sig->dh_signature_len = 0; 43 } 44 45 /* Release the storage for a gss_buffer */ 46 void 47 __dh_release_buffer(gss_buffer_t b) 48 { 49 Free(b->value); 50 b->length = 0; 51 b->value = NULL; 52 } 53 54 typedef struct cipher_entry { 55 cipher_proc cipher; /* Routine to en/decrypt with */ 56 unsigned int pad; /* Padding need for the routine */ 57 } cipher_entry, *cipher_t; 58 59 typedef struct verifer_entry { 60 verifier_proc msg; /* Routine to calculate the check sum */ 61 unsigned int size; /* Size of check sum */ 62 cipher_t signer; /* Cipher entry to sign the check sum */ 63 } verifier_entry, *verifier_t; 64 65 typedef struct QOP_entry { 66 int export_level; /* Not currentlyt used */ 67 verifier_t verifier; /* Verifier entry to use for integrity */ 68 } QOP_entry; 69 70 /* 71 * Return the length produced by using cipher entry c given the supplied len 72 */ 73 static unsigned int 74 cipher_pad(cipher_t c, unsigned int len) 75 { 76 unsigned int pad; 77 78 pad = c ? c->pad : 1; 79 80 return (((len + pad - 1)/pad)*pad); 81 } 82 83 84 /* EXPORT DELETE START */ 85 86 /* 87 * Des [en/de]crypt buffer, buf of length, len for each key provided using 88 * an CBC initialization vector ivec. 89 * If the mode is encrypt we will use the following pattern if the number 90 * of keys is odd 91 * encrypt(buf, k[0]), decrypt(buf, k[1]), encrypt(buf, k[2]) 92 * decrypt(buf, k[4]) ... encrypt(buf, k[keynum - 1]) 93 * If we have an even number of keys and additional encryption will be 94 * done with the first key, i.e., ecrypt(buf, k[0]); 95 * In each [en/de]cription above we will used the passed in CBC initialization 96 * vector. The new initialization vector will be the vector return from the 97 * last encryption. 98 * 99 * In the decryption case we reverse the proccess. Note in this case 100 * the return ivec will be from the first decryption. 101 */ 102 103 static int 104 __desN_crypt(des_block keys[], int keynum, char *buf, unsigned int len, 105 unsigned int mode, char *ivec) 106 { 107 /* Get the direction of ciphering */ 108 unsigned int m = mode & (DES_ENCRYPT | DES_DECRYPT); 109 /* Get the remaining flags from mode */ 110 unsigned int flags = mode & ~(DES_ENCRYPT | DES_DECRYPT); 111 des_block svec, dvec; 112 int i, j, stat; 113 114 /* Do we have at least one key */ 115 if (keynum < 1) 116 return (DESERR_BADPARAM); 117 118 /* Save the passed in ivec */ 119 memcpy(svec.c, ivec, sizeof (des_block)); 120 121 /* For each key do the appropriate cipher */ 122 for (i = 0; i < keynum; i++) { 123 j = (mode & DES_DECRYPT) ? keynum - 1 - i : i; 124 stat = cbc_crypt(keys[j].c, buf, len, m | flags, ivec); 125 if (mode & DES_DECRYPT && i == 0) 126 memcpy(dvec.c, ivec, sizeof (des_block)); 127 128 if (DES_FAILED(stat)) 129 return (stat); 130 131 m = (m == DES_ENCRYPT ? DES_DECRYPT : DES_ENCRYPT); 132 133 if ((mode & DES_DECRYPT) || i != keynum - 1 || i%2) 134 memcpy(ivec, svec.c, sizeof (des_block)); 135 } 136 137 /* 138 * If we have an even number of keys then do an extra round of 139 * [en/de]cryption with the first key. 140 */ 141 if (keynum % 2 == 0) 142 stat = cbc_crypt(keys[0].c, buf, len, mode, ivec); 143 144 /* If were decrypting ivec is set from first decryption */ 145 if (mode & DES_DECRYPT) 146 memcpy(ivec, dvec.c, sizeof (des_block)); 147 148 return (stat); 149 } 150 151 /* EXPORT DELETE END */ 152 153 154 /* 155 * DesN crypt packaged for use as a cipher entry 156 */ 157 static OM_uint32 158 __dh_desN_crypt(gss_buffer_t buf, dh_key_set_t keys, cipher_mode_t cipher_mode) 159 { 160 int stat = DESERR_BADPARAM; 161 /* EXPORT DELETE START */ 162 int encrypt_flag = (cipher_mode == ENCIPHER); 163 unsigned mode = (encrypt_flag ? DES_ENCRYPT : DES_DECRYPT) | DES_HW; 164 des_block ivec; 165 166 if (keys->dh_key_set_len < 1) 167 return (DH_BADARG_FAILURE); 168 169 /* 170 * We all ways start of with ivec set to zeros. There is no 171 * good way to maintain ivecs since packets could be out of sequence 172 * duplicated or worst of all lost. Under these conditions the 173 * higher level protocol would have to some how resync the ivecs 174 * on both sides and start again. Theres no mechanism for this in 175 * GSS. 176 */ 177 memset(&ivec, 0, sizeof (ivec)); 178 179 /* Do the encryption/decryption */ 180 stat = __desN_crypt(keys->dh_key_set_val, keys->dh_key_set_len, 181 (char *)buf->value, buf->length, mode, ivec.c); 182 /* EXPORT DELETE END */ 183 184 if (DES_FAILED(stat)) 185 return (DH_CIPHER_FAILURE); 186 187 return (DH_SUCCESS); 188 } 189 190 /* 191 * Package up plain des cbc crypt for use as a cipher entry. 192 */ 193 static OM_uint32 194 __dh_des_crypt(gss_buffer_t buf, dh_key_set_t keys, cipher_mode_t cipher_mode) 195 { 196 int stat = DESERR_BADPARAM; 197 /* EXPORT DELETE START */ 198 int encrypt_flag = (cipher_mode == ENCIPHER); 199 unsigned mode = (encrypt_flag ? DES_ENCRYPT : DES_DECRYPT) | DES_HW; 200 des_block ivec; 201 202 if (keys->dh_key_set_len < 1) 203 return (DH_BADARG_FAILURE); 204 205 /* Set the ivec to zeros and then cbc crypt the result */ 206 memset(&ivec, 0, sizeof (ivec)); 207 stat = cbc_crypt(keys->dh_key_set_val[0].c, (char *)buf->value, 208 buf->length, mode, ivec.c); 209 /* EXPORT DELETE END */ 210 211 if (DES_FAILED(stat)) 212 return (DH_CIPHER_FAILURE); 213 214 return (DH_SUCCESS); 215 } 216 217 /* 218 * MD5_verifier: This is a verifier routine suitable for use in a 219 * verifier entry. It calculates the MD5 check sum over an optional 220 * msg and a token. It signs it using the supplied cipher_proc and stores 221 * the result in signature. 222 * 223 * Note signature should already be allocated and be large enough to 224 * hold the signature after its been encrypted. If keys is null, then 225 * we will just return the unencrypted check sum. 226 */ 227 static OM_uint32 228 MD5_verifier(gss_buffer_t tok, /* The buffer to sign */ 229 gss_buffer_t msg, /* Optional buffer to include */ 230 cipher_proc signer, /* Routine to encrypt the integrity check */ 231 dh_key_set_t keys, /* Optiona keys to be used with the above */ 232 dh_signature_t signature /* The resulting MIC */) 233 { 234 MD5_CTX md5_ctx; /* MD5 context */ 235 gss_buffer_desc buf; /* GSS buffer to hold keys for cipher routine */ 236 237 /* Initialize the MD5 context */ 238 MD5Init(&md5_ctx); 239 /* If we have a message to digest, digest it */ 240 if (msg) 241 MD5Update(&md5_ctx, (unsigned char *)msg->value, msg->length); 242 /* Digest the supplied token */ 243 MD5Update(&md5_ctx, (unsigned char *)tok->value, tok->length); 244 /* Finalize the sum. The MD5 context contains the digets */ 245 MD5Final(&md5_ctx); 246 247 /* Copy the digest to the signature */ 248 memcpy(signature->dh_signature_val, (void *)md5_ctx.digest, 16); 249 250 buf.length = signature->dh_signature_len; 251 buf.value = signature->dh_signature_val; 252 253 /* If we have keys encrypt it */ 254 if (keys != NULL) 255 return (signer(&buf, keys, ENCIPHER)); 256 257 return (DH_SUCCESS); 258 } 259 260 /* Cipher table */ 261 static 262 cipher_entry cipher_tab[] = { 263 { NULL, 1}, 264 { __dh_desN_crypt, 8}, 265 { __dh_des_crypt, 8} 266 }; 267 268 269 #define __NO_CRYPT &cipher_tab[0] 270 #define __DES_N_CRYPT &cipher_tab[1] 271 #define __DES_CRYPT &cipher_tab[2] 272 273 /* Verifier table */ 274 static 275 verifier_entry verifier_tab[] = { 276 { MD5_verifier, 16, __DES_N_CRYPT }, 277 { MD5_verifier, 16, __DES_CRYPT } 278 }; 279 280 /* QOP table */ 281 static 282 QOP_entry QOP_table[] = { 283 { 0, &verifier_tab[0] }, 284 { 0, &verifier_tab[1] } 285 }; 286 287 #define QOP_ENTRIES (sizeof (QOP_table) / sizeof (QOP_entry)) 288 289 /* 290 * __dh_is_valid_QOP: Return true if qop is valid entry into the QOP 291 * table, else return false. 292 */ 293 bool_t 294 __dh_is_valid_QOP(dh_qop_t qop) 295 { 296 bool_t is_valid = FALSE; 297 298 is_valid = qop < QOP_ENTRIES; 299 300 return (is_valid); 301 } 302 303 /* 304 * __alloc_sig: Allocate a signature for a given QOP. This takes into 305 * account the size of the signature after padding for the encryption 306 * routine. 307 */ 308 OM_uint32 309 __alloc_sig(dh_qop_t qop, dh_signature_t sig) 310 { 311 OM_uint32 stat = DH_VERIFIER_FAILURE; 312 verifier_entry *v; 313 314 /* Check that the QOP is valid */ 315 if (!__dh_is_valid_QOP(qop)) 316 return (DH_UNKNOWN_QOP); 317 318 /* Get the verifier entry from the QOP entry */ 319 v = QOP_table[qop].verifier; 320 321 /* Calulate the length needed for the signature */ 322 sig->dh_signature_len = cipher_pad(v->signer, v->size); 323 324 /* Allocate the signature */ 325 sig->dh_signature_val = (void*)New(char, sig->dh_signature_len); 326 if (sig->dh_signature_val == NULL) { 327 sig->dh_signature_len = 0; 328 return (DH_NOMEM_FAILURE); 329 } 330 331 stat = DH_SUCCESS; 332 333 return (stat); 334 } 335 336 /* 337 * __get_sig_size: Return the total size needed for a signature given a QOP. 338 */ 339 OM_uint32 340 __get_sig_size(dh_qop_t qop, unsigned int *size) 341 { 342 /* Check for valid QOP */ 343 if (__dh_is_valid_QOP(qop)) { 344 /* Get the verifier entry */ 345 verifier_t v = QOP_table[qop].verifier; 346 347 /* Return the size include the padding needed for encryption */ 348 *size = v ? cipher_pad(v->signer, v->size) : 0; 349 350 return (DH_SUCCESS); 351 } 352 *size = 0; 353 354 return (DH_UNKNOWN_QOP); 355 } 356 357 /* 358 * __mk_sig: Generate a signature using a given qop over a token of a 359 * given length and an optional message. We use the supplied keys to 360 * encrypt the check sum if they are available. The output is place 361 * in a preallocate signature, that was allocated using __alloc_sig. 362 */ 363 OM_uint32 364 __mk_sig(dh_qop_t qop, /* The QOP to use */ 365 char *tok, /* The token to sign */ 366 long len, /* The tokens length */ 367 gss_buffer_t mesg, /* An optional message to be included */ 368 dh_key_set_t keys, /* The optional encryption keys */ 369 dh_signature_t sig /* The resulting MIC */) 370 { 371 OM_uint32 stat = DH_VERIFIER_FAILURE; 372 373 374 verifier_entry *v; /* Verifier entry */ 375 gss_buffer_desc buf; /* Buffer to package tok */ 376 377 /* Make sure the QOP is valid */ 378 if (!__dh_is_valid_QOP(qop)) 379 return (DH_UNKNOWN_QOP); 380 381 /* Grab the verifier entry for the qop */ 382 v = QOP_table[qop].verifier; 383 384 /* Package the token for use in a verifier_proc */ 385 buf.length = len; 386 buf.value = tok; 387 388 /* 389 * Calculate the signature using the supplied keys. If keys 390 * is null, the the v->signer->cipher routine will not be called 391 * and sig will not be encrypted. 392 */ 393 stat = (*v->msg)(&buf, mesg, v->signer->cipher, keys, sig); 394 395 return (stat); 396 } 397 398 /* 399 * __verify_sig: Verify that the supplied signature, sig, is the same 400 * as the token verifier 401 */ 402 OM_uint32 403 __verify_sig(dh_token_t token, /* The token to be verified */ 404 dh_qop_t qop, /* The QOP to use */ 405 dh_key_set_t keys, /* The context session keys */ 406 dh_signature_t sig /* The signature from the serialized token */) 407 { 408 OM_uint32 stat = DH_VERIFIER_FAILURE; 409 410 cipher_proc cipher; /* cipher routine to use */ 411 gss_buffer_desc buf; /* Packaging for sig */ 412 413 /* Check the QOP */ 414 if (!__dh_is_valid_QOP(qop)) 415 return (DH_UNKNOWN_QOP); 416 417 /* Package up the supplied signature */ 418 buf.length = sig->dh_signature_len; 419 buf.value = sig->dh_signature_val; 420 421 /* Get the cipher proc to use from the verifier entry for qop */ 422 cipher = QOP_table[qop].verifier->signer->cipher; 423 424 /* Encrypt the check sum using the supplied set of keys */ 425 if ((stat = (*cipher)(&buf, keys, ENCIPHER)) != DH_SUCCESS) 426 return (stat); 427 428 /* Compare the signatures */ 429 if (__cmpsig(sig, &token->verifier)) 430 return (DH_SUCCESS); 431 432 stat = DH_VERIFIER_MISMATCH; 433 434 return (stat); 435 } 436 437 /* 438 * __cmpsig: Return true if two signatures are the same, else false. 439 */ 440 bool_t 441 __cmpsig(dh_signature_t s1, dh_signature_t s2) 442 { 443 return (s1->dh_signature_len == s2->dh_signature_len && 444 memcmp(s1->dh_signature_val, 445 s2->dh_signature_val, s1->dh_signature_len) == 0); 446 } 447 448 /* 449 * wrap_msg_body: Wrap the message pointed to be in into a 450 * message pointed to by out that has ben padded out by pad bytes. 451 * 452 * The output message looks like: 453 * out->length = total length of out->value including any padding 454 * out->value points to memory as follows: 455 * +------------+-------------------------+---------| 456 * | in->length | in->value | XDR PAD | 457 * +------------+-------------------------+---------| 458 * 4 bytes in->length bytes 0 - 3 459 */ 460 static OM_uint32 461 wrap_msg_body(gss_buffer_t in, gss_buffer_t out) 462 { 463 XDR xdrs; /* xdrs to wrap with */ 464 unsigned int len, out_len; /* length */ 465 size_t size; 466 467 out->length = 0; 468 out->value = 0; 469 470 /* Make sure the address of len points to a 32 bit word */ 471 len = (unsigned int)in->length; 472 if (len != in->length) 473 return (DH_ENCODE_FAILURE); 474 475 size = ((in->length + sizeof (OM_uint32) + 3)/4) * 4; 476 out_len = size; 477 if (out_len != size) 478 return (DH_ENCODE_FAILURE); 479 480 /* Allocate the output buffer and set the length */ 481 if ((out->value = (void *)New(char, len)) == NULL) 482 return (DH_NOMEM_FAILURE); 483 out->length = out_len; 484 485 486 /* Create xdr stream to wrap into */ 487 xdrmem_create(&xdrs, out->value, out->length, XDR_ENCODE); 488 489 /* Wrap the bytes in value */ 490 if (!xdr_bytes(&xdrs, (char **)&in->value, &len, len)) { 491 __dh_release_buffer(out); 492 return (DH_ENCODE_FAILURE); 493 } 494 495 return (DH_SUCCESS); 496 } 497 498 /* 499 * __QOPSeal: Wrap the input message placing the output in output given 500 * a valid QOP. If confidentialiy is requested it is ignored. We can't 501 * support privacy. The return flag will always be zero. 502 */ 503 OM_uint32 504 __QOPSeal(dh_qop_t qop, /* The QOP to use */ 505 gss_buffer_t input, /* The buffer to wrap */ 506 int conf_req, /* Do we want privacy ? */ 507 dh_key_set_t keys, /* The session keys */ 508 gss_buffer_t output, /* The wraped message */ 509 int *conf_ret /* Did we encrypt it? */) 510 { 511 _NOTE(ARGUNUSED(conf_req,keys)) 512 OM_uint32 stat = DH_CIPHER_FAILURE; 513 514 *conf_ret = FALSE; /* No encryption allowed */ 515 516 /* Check for valid QOP */ 517 if (!__dh_is_valid_QOP(qop)) 518 return (DH_UNKNOWN_QOP); 519 520 /* Wrap the message */ 521 if ((stat = wrap_msg_body(input, output)) 522 != DH_SUCCESS) 523 return (stat); 524 525 return (stat); 526 } 527 528 /* 529 * unwrap_msg_body: Unwrap the message, that was wrapped from above 530 */ 531 static OM_uint32 532 unwrap_msg_body(gss_buffer_t in, gss_buffer_t out) 533 { 534 XDR xdrs; 535 unsigned int len; /* sizeof (len) == 32bits */ 536 537 /* Create an xdr stream to on wrap in */ 538 xdrmem_create(&xdrs, in->value, in->length, XDR_DECODE); 539 540 /* Unwrap the input into out->value */ 541 if (!xdr_bytes(&xdrs, (char **)&out->value, &len, in->length)) 542 return (DH_DECODE_FAILURE); 543 544 /* set the length */ 545 out->length = len; 546 547 return (DH_SUCCESS); 548 } 549 550 /* 551 * __QOPUnSeal: Unwrap the input message into output using the supplied QOP. 552 * Note it is the callers responsibility to release the allocated output 553 * buffer. If conf_req is true we return DH_CIPHER_FAILURE since we don't 554 * support privacy. 555 */ 556 OM_uint32 557 __QOPUnSeal(dh_qop_t qop, /* The QOP to use */ 558 gss_buffer_t input, /* The message to unwrap */ 559 int conf_req, /* Is the message encrypted */ 560 dh_key_set_t keys, /* The session keys to decrypt if conf_req */ 561 gss_buffer_t output /* The unwraped message */) 562 { 563 _NOTE(ARGUNUSED(keys)) 564 OM_uint32 stat = DH_CIPHER_FAILURE; 565 566 /* Check that the qop is valid */ 567 if (!__dh_is_valid_QOP(qop)) 568 return (DH_UNKNOWN_QOP); 569 570 /* Set output to sane values */ 571 output->length = 0; 572 output->value = NULL; 573 574 /* Fail if this is privacy */ 575 if (conf_req) 576 return (DH_CIPHER_FAILURE); 577 578 /* Unwrap the input into the output, return the status */ 579 stat = unwrap_msg_body(input, output); 580 581 return (stat); 582 }