1 /* 2 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 3 * Use is subject to license terms. 4 */ 5 #pragma ident "%Z%%M% %I% %E% SMI" 6 7 /* 8 * Copyright (c) 2000 by Computer Science Laboratory, 9 * Rensselaer Polytechnic Institute 10 * #include STD_DISCLAIMER 11 */ 12 13 #include <k5-int.h> 14 #include <arcfour.h> 15 16 /* The blocksize for the enctype */ 17 static void k5_arcfour_blocksize(size_t *); 18 19 /* keysize for the enctype (number of bytes, and length of key (parity/etc) */ 20 static void k5_arcfour_keysize(size_t *, size_t *); 21 22 /* from a random bitstrem, construct a key */ 23 static krb5_error_code 24 k5_arcfour_make_key(krb5_context, const krb5_data *, krb5_keyblock *); 25 26 /* This seems to work... although I am not sure what the implications are 27 in other places in the kerberos library */ 28 static void 29 k5_arcfour_blocksize(size_t *blocksize) 30 { 31 KRB5_LOG0(KRB5_INFO, "k5_arcfour_blocksize called"); 32 *blocksize = 1; 33 } 34 35 /* Keysize is arbitrary in arcfour, but the constraints of the system, and 36 to attempt to work with the MSFT system forces us to 16byte/128bit. 37 Since there is no parity in the key, the byte and length are the same. 38 */ 39 static void 40 k5_arcfour_keysize(size_t *keybytes, size_t *keylength) 41 { 42 KRB5_LOG0(KRB5_INFO, "k5_arcfour_keysize called"); 43 *keybytes = 16; 44 *keylength = 16; 45 } 46 #ifndef _KERNEL 47 static krb5_error_code 48 setup_arcfour_crypto(CK_SESSION_HANDLE session, 49 const krb5_keyblock *key, 50 KRB5_MECH_TO_PKCS *algos, 51 CK_OBJECT_HANDLE *hKey) 52 { 53 krb5_error_code ret = 0; 54 CK_RV rv; 55 CK_OBJECT_CLASS class = CKO_SECRET_KEY; 56 CK_KEY_TYPE keyType = CKK_RC4; 57 CK_BBOOL true = TRUE, false = FALSE; 58 CK_ATTRIBUTE template[5]; 59 60 if ((rv = get_algo(key->enctype, algos)) != CKR_OK) { 61 KRB5_LOG0(KRB5_ERR, "failure to get algo id in function " 62 "k5_arcfour_decrypt."); 63 return (PKCS_ERR); 64 } 65 66 template[0].type = CKA_CLASS; 67 template[0].pValue = &class; 68 template[0].ulValueLen = sizeof (class); 69 template[1].type = CKA_KEY_TYPE; 70 template[1].pValue = &keyType; 71 template[1].ulValueLen = sizeof (keyType); 72 template[2].type = CKA_TOKEN; 73 template[2].pValue = &false; 74 template[2].ulValueLen = sizeof (false); 75 template[3].type = CKA_ENCRYPT; 76 template[3].pValue = &true; 77 template[3].ulValueLen = sizeof (true); 78 template[4].type = CKA_VALUE; 79 template[4].pValue = key->contents; 80 template[4].ulValueLen = key->length; 81 82 /* Create an object handle for the key */ 83 if ((rv = C_CreateObject(session, template, 84 sizeof(template)/sizeof(CK_ATTRIBUTE), 85 hKey)) != CKR_OK) { 86 KRB5_LOG(KRB5_ERR, "C_CreateObject failed in " 87 "k5_arcfour_decrypt: rv = 0x%x.", rv); 88 ret = PKCS_ERR; 89 } 90 91 return (ret); 92 } 93 #endif /* !_KERNEL */ 94 95 96 /* The workhorse of the arcfour system, this impliments the cipher */ 97 /* ARGSUSED */ 98 static krb5_error_code 99 k5_arcfour_decrypt(krb5_context context, 100 const krb5_keyblock *key, const krb5_data *state, 101 const krb5_data *input, krb5_data *output) 102 { 103 krb5_error_code ret = 0; 104 105 #ifndef _KERNEL 106 CK_RV rv; 107 KRB5_MECH_TO_PKCS algos; 108 CK_OBJECT_HANDLE *kptr = NULL, hKey = CK_INVALID_HANDLE; 109 CK_MECHANISM mechanism; 110 CK_SESSION_HANDLE session = 0; 111 CK_ULONG outlen; 112 int need_init = 0; 113 #endif 114 115 KRB5_LOG0(KRB5_INFO, "k5_arcfour_decrypt start"); 116 if (key->length != 16) 117 return(KRB5_BAD_KEYSIZE); 118 if (input->length != output->length) 119 return(KRB5_BAD_MSIZE); 120 121 #ifndef _KERNEL 122 /* 123 * If RC4 is being used to encrypt a stream of data blocks, 124 * the keys for encrypt and decrypt must be kept separate 125 * so that their associated state data doesn't get mixed up 126 * between operations. The r-cmds (rlogin, rsh, rcp) use 127 * the "init_state" function (see bottom of this module) 128 * to set up and prepare for stream encryption. 129 * 130 * Normally, the RC4 key is used as a single operation 131 * (i.e. call C_Encrypt) instead of a constantly updating 132 * stream cipher (C_EncryptUpdate). In those cases, we just 133 * use a short-term key object defined locally. We don't 134 * have to save state between operations. 135 * 136 * This logic here is to make sure that the keys are tracked 137 * correctly depending on how they are used and that the RC4 138 * context record is properly initialized. 139 */ 140 if (!context->arcfour_ctx.initialized) { 141 session = krb_ctx_hSession(context); 142 /* Just use a local, 1-time only key object */ 143 kptr = (CK_OBJECT_HANDLE *)&hKey; 144 need_init = 1; 145 } else { 146 session = context->arcfour_ctx.dSession; 147 /* If the dKey handle was not defined, we need to initialize one */ 148 if (context->arcfour_ctx.dKey == CK_INVALID_HANDLE) { 149 need_init = 1; 150 /* Use the long-term key object in the RC4 context area */ 151 kptr = &context->arcfour_ctx.dKey; 152 } 153 } 154 155 if (need_init) { 156 ret = setup_arcfour_crypto(session, key, &algos, kptr); 157 if (ret) 158 goto cleanup; 159 160 mechanism.mechanism = algos.enc_algo; 161 mechanism.pParameter = NULL; 162 mechanism.ulParameterLen = 0; 163 164 rv = C_DecryptInit(session, &mechanism, *kptr); 165 166 if (rv != CKR_OK) { 167 KRB5_LOG(KRB5_ERR, "C_DecryptInit failed in " 168 "k5_arcfour_decrypt: rv = 0x%x", rv); 169 ret = PKCS_ERR; 170 goto cleanup; 171 } 172 } 173 174 outlen = (CK_ULONG)output->length; 175 if (context->arcfour_ctx.initialized) 176 rv = C_DecryptUpdate(session, 177 (CK_BYTE_PTR)input->data, 178 (CK_ULONG)input->length, 179 (CK_BYTE_PTR)output->data, 180 (CK_ULONG_PTR)&outlen); 181 else { 182 rv = C_Decrypt(session, 183 (CK_BYTE_PTR)input->data, 184 (CK_ULONG)input->length, 185 (CK_BYTE_PTR)output->data, 186 (CK_ULONG_PTR)&outlen); 187 } 188 output->length = (uint32_t)outlen; 189 190 if (rv != CKR_OK) { 191 KRB5_LOG(KRB5_ERR, 192 "C_DecryptUpdate failed in k5_arcfour_decrypt: " 193 "rv = 0x%x", rv); 194 ret = PKCS_ERR; 195 } 196 cleanup: 197 if (ret) 198 bzero(output->data, input->length); 199 200 /* If we used a 1-time only key object, destroy it now */ 201 if (hKey != CK_INVALID_HANDLE) 202 (void)C_DestroyObject(session, hKey); 203 204 #else /* !_KERNEL */ 205 KRB5_LOG(KRB5_INFO, "key->kef_mt = %ld", (ulong_t) key->kef_mt); 206 ret = k5_ef_crypto((const char *)input->data, (char *)output->data, 207 input->length, (krb5_keyblock *)key, NULL, 0); 208 #endif /* !_KERNEL */ 209 210 KRB5_LOG0(KRB5_INFO, "k5_arcfour_docrypt end"); 211 return (ret); 212 } 213 214 /* ARGSUSED */ 215 static krb5_error_code 216 k5_arcfour_encrypt(krb5_context context, 217 const krb5_keyblock *key, const krb5_data *state, 218 const krb5_data *input, krb5_data *output) 219 { 220 krb5_error_code ret = 0; 221 222 #ifndef _KERNEL 223 CK_RV rv; 224 KRB5_MECH_TO_PKCS algos; 225 CK_MECHANISM mechanism; 226 CK_OBJECT_HANDLE *kptr = NULL, hKey = CK_INVALID_HANDLE; 227 CK_SESSION_HANDLE session; 228 int need_init = 0; 229 CK_ULONG outlen; 230 #endif 231 232 KRB5_LOG0(KRB5_INFO, "k5_arcfour_encrypt start"); 233 if (key->length != 16) 234 return(KRB5_BAD_KEYSIZE); 235 if (input->length != output->length) 236 return(KRB5_BAD_MSIZE); 237 238 #ifndef _KERNEL 239 240 /* 241 * See the comments in the k5_arcfour_decrypt routine (above) 242 * for an explanation of why the key handles are initialized 243 * and used as they are here. 244 */ 245 if (!context->arcfour_ctx.initialized) { 246 session = krb_ctx_hSession(context); 247 kptr = (CK_OBJECT_HANDLE *)&hKey; 248 need_init = 1; 249 } else { 250 session = context->arcfour_ctx.eSession; 251 if (context->arcfour_ctx.eKey == 0) { 252 kptr = &context->arcfour_ctx.eKey; 253 need_init = 1; 254 } 255 } 256 257 if (need_init) { 258 ret = setup_arcfour_crypto(session, key, &algos, kptr); 259 if (ret) 260 goto cleanup; 261 262 mechanism.mechanism = algos.enc_algo; 263 mechanism.pParameter = NULL; 264 mechanism.ulParameterLen = 0; 265 266 rv = C_EncryptInit(session, &mechanism, *kptr); 267 268 if (rv != CKR_OK) { 269 KRB5_LOG(KRB5_ERR, "C_EncryptInit failed in " 270 "k5_arcfour_encrypt: rv = 0x%x", rv); 271 ret = PKCS_ERR; 272 goto cleanup; 273 } 274 } 275 276 /* 277 * If we've initialize the stream for use with r-commands, 278 * use the open-ended session handle and call. 279 */ 280 outlen = (CK_ULONG)output->length; 281 if (context->arcfour_ctx.initialized) 282 rv = C_EncryptUpdate(session, 283 (CK_BYTE_PTR)input->data, 284 (CK_ULONG)input->length, 285 (CK_BYTE_PTR)output->data, 286 (CK_ULONG_PTR)&outlen); 287 else { 288 rv = C_Encrypt(session, 289 (CK_BYTE_PTR)input->data, 290 (CK_ULONG)input->length, 291 (CK_BYTE_PTR)output->data, 292 (CK_ULONG_PTR)&outlen); 293 } 294 output->length = (uint32_t)outlen; 295 296 if (rv != CKR_OK) { 297 KRB5_LOG(KRB5_ERR, 298 "C_EncryptUpdate failed in k5_arcfour_encrypt: " 299 "rv = 0x%x", rv); 300 ret = PKCS_ERR; 301 } 302 cleanup: 303 final_cleanup: 304 if (ret) 305 bzero(output->data, input->length); 306 307 if (hKey != CK_INVALID_HANDLE) 308 (void)C_DestroyObject(session, hKey); 309 310 #else /* !_KERNEL */ 311 KRB5_LOG1(KRB5_INFO, "key->kef_mt = %ld key->key_tmpl = %ld", 312 (ulong_t) key->kef_mt, (ulong_t) key->key_tmpl); 313 ret = k5_ef_crypto((const char *)input->data, (char *)output->data, 314 input->length, (krb5_keyblock *)key, NULL, 1); 315 #endif /* !_KERNEL */ 316 317 KRB5_LOG0(KRB5_INFO, "k5_arcfour_docrypt end"); 318 return (ret); 319 } 320 321 /* ARGSUSED */ 322 static krb5_error_code 323 k5_arcfour_make_key(krb5_context context, 324 const krb5_data *randombits, krb5_keyblock *key) 325 { 326 krb5_error_code ret = 0; 327 KRB5_LOG0(KRB5_INFO, "k5_arcfour_make_key() start\n"); 328 329 if (key->length != 16) 330 return(KRB5_BAD_KEYSIZE); 331 if (randombits->length != 16) 332 return(KRB5_CRYPTO_INTERNAL); 333 334 key->magic = KV5M_KEYBLOCK; 335 key->length = 16; 336 key->dk_list = NULL; 337 #ifdef _KERNEL 338 key->kef_key.ck_data = NULL; 339 key->key_tmpl = NULL; 340 ret = init_key_kef(context->kef_cipher_mt, key); 341 #else 342 key->hKey = CK_INVALID_HANDLE; 343 ret = init_key_uef(krb_ctx_hSession(context), key); 344 #endif /* _KERNEL */ 345 346 bcopy(randombits->data, key->contents, randombits->length); 347 348 KRB5_LOG0(KRB5_INFO, "k5_arcfour_make_key() end\n"); 349 return (ret); 350 } 351 352 /*ARGSUSED*/ 353 static krb5_error_code 354 k5_arcfour_init_state (krb5_context context, 355 const krb5_keyblock *key, 356 krb5_keyusage keyusage, krb5_data *new_state) 357 { 358 krb5_error_code retval = 0; 359 #ifndef _KERNEL 360 if (!context->arcfour_ctx.initialized) { 361 retval = krb5_open_pkcs11_session(&context->arcfour_ctx.eSession); 362 if (retval) 363 return (retval); 364 retval = krb5_open_pkcs11_session(&context->arcfour_ctx.dSession); 365 if (retval) 366 return (retval); 367 context->arcfour_ctx.initialized = 1; 368 context->arcfour_ctx.eKey = CK_INVALID_HANDLE; 369 context->arcfour_ctx.dKey = CK_INVALID_HANDLE; 370 } 371 #endif 372 return (retval); 373 } 374 375 /* Since the arcfour cipher is identical going forwards and backwards, 376 we just call "docrypt" directly 377 */ 378 const struct krb5_enc_provider krb5int_enc_arcfour = { 379 k5_arcfour_blocksize, 380 k5_arcfour_keysize, 381 k5_arcfour_encrypt, 382 k5_arcfour_decrypt, 383 k5_arcfour_make_key, 384 k5_arcfour_init_state, 385 krb5int_default_free_state 386 }; 387