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