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