From: Greg Hudson Date: Sat, 28 Nov 2009 15:53:39 +0000 (+0000) Subject: Clean up the arcfour token encryption and decryption functions by X-Git-Tag: krb5-1.8-alpha1~131 X-Git-Url: http://git.tremily.us/?a=commitdiff_plain;h=01a2a0fe9b4bd1900773487db290f3138e20c1d7;p=krb5.git Clean up the arcfour token encryption and decryption functions by making use of newer convenience functions and by factoring out the derivation of the usage and encryption keys. git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@23377 dc483132-0cff-0310-8789-dd5450dbe970 --- diff --git a/src/lib/crypto/krb/arcfour/arcfour-int.h b/src/lib/crypto/krb/arcfour/arcfour-int.h index ff811f6f5..15ab75bbd 100644 --- a/src/lib/crypto/krb/arcfour/arcfour-int.h +++ b/src/lib/crypto/krb/arcfour/arcfour-int.h @@ -14,8 +14,20 @@ #define CONFOUNDERLENGTH 8 -krb5_keyusage krb5int_arcfour_translate_usage(krb5_keyusage usage); +krb5_keyusage +krb5int_arcfour_translate_usage(krb5_keyusage usage); -extern const char *const krb5int_arcfour_l40; +krb5_error_code +krb5int_arcfour_usage_key(const struct krb5_enc_provider *enc, + const struct krb5_hash_provider *hash, + const krb5_keyblock *session_keyblock, + krb5_keyusage usage, + krb5_keyblock *out); + +krb5_error_code +krb5int_arcfour_enc_key(const struct krb5_enc_provider *enc, + const struct krb5_hash_provider *hash, + const krb5_keyblock *usage_keyblock, + const krb5_data *checksum, krb5_keyblock *out); #endif /* ARCFOUR_INT_H */ diff --git a/src/lib/crypto/krb/arcfour/arcfour.c b/src/lib/crypto/krb/arcfour/arcfour.c index 8939c3006..a057da09f 100644 --- a/src/lib/crypto/krb/arcfour/arcfour.c +++ b/src/lib/crypto/krb/arcfour/arcfour.c @@ -11,57 +11,89 @@ #include "arcfour-int.h" #include "hash_provider/hash_provider.h" -const char *const krb5int_arcfour_l40 = "fortybits"; +const char *const l40 = "fortybits"; void krb5int_arcfour_encrypt_length(const struct krb5_enc_provider *enc, const struct krb5_hash_provider *hash, size_t inputlen, size_t *length) { - size_t blocksize, hashsize; - - blocksize = enc->block_size; - hashsize = hash->hashsize; - /* checksum + (confounder + inputlen, in even blocksize) */ - *length = hashsize + krb5_roundup(8 + inputlen, blocksize); + *length = hash->hashsize + krb5_roundup(8 + inputlen, enc->block_size); } krb5_keyusage krb5int_arcfour_translate_usage(krb5_keyusage usage) { switch (usage) { - case 1: /* AS-REQ PA-ENC-TIMESTAMP padata timestamp, */ - return 1; - case 2: /* ticket from kdc */ - return 2; - case 3: /* as-rep encrypted part */ - return 8; - case 4: /* tgs-req authz data */ - return 4; - case 5: /* tgs-req authz data in subkey */ - return 5; - case 6: /* tgs-req authenticator cksum */ - return 6; - case 7: /* tgs-req authenticator */ - return 7; - case 8: - return 8; - case 9: /* tgs-rep encrypted with subkey */ - return 9; - case 10: /* ap-rep authentication cksum */ - return 10; /* xxx Microsoft never uses this*/ - case 11: /* app-req authenticator */ - return 11; - case 12: /* app-rep encrypted part */ - return 12; - case 23: /* sign wrap token*/ - return 13; - default: - return usage; + case 1: return 1; /* AS-REQ PA-ENC-TIMESTAMP padata timestamp, */ + case 2: return 2; /* ticket from kdc */ + case 3: return 8; /* as-rep encrypted part */ + case 4: return 4; /* tgs-req authz data */ + case 5: return 5; /* tgs-req authz data in subkey */ + case 6: return 6; /* tgs-req authenticator cksum */ + case 7: return 7; /* tgs-req authenticator */ + case 8: return 8; + case 9: return 9; /* tgs-rep encrypted with subkey */ + case 10: return 10; /* ap-rep authentication cksum (never used by MS) */ + case 11: return 11; /* app-req authenticator */ + case 12: return 12; /* app-rep encrypted part */ + case 23: return 13; /* sign wrap token*/ + default: return usage; } } +/* Derive a usage key from a session key and krb5 usage constant. */ +krb5_error_code +krb5int_arcfour_usage_key(const struct krb5_enc_provider *enc, + const struct krb5_hash_provider *hash, + const krb5_keyblock *session_keyblock, + krb5_keyusage usage, + krb5_keyblock *out) +{ + char salt_buf[14]; + krb5_data out_data = make_data(out->contents, out->length); + krb5_data salt = make_data(salt_buf, sizeof(salt_buf)); + krb5_keyusage ms_usage; + + /* Generate the salt. */ + ms_usage = krb5int_arcfour_translate_usage(usage); + if (session_keyblock->enctype == ENCTYPE_ARCFOUR_HMAC_EXP) { + strncpy(salt_buf, l40, sizeof(salt_buf)); + store_32_le(ms_usage, salt_buf + 10); + } else { + salt.length=4; + store_32_le(ms_usage, salt_buf); + } + + /* Compute HMAC(key, salt) to produce the usage key. */ + return krb5int_hmac_keyblock(hash, session_keyblock, 1, &salt, &out_data); +} + +/* Derive an encryption key from a usage key and checksum. */ +krb5_error_code +krb5int_arcfour_enc_key(const struct krb5_enc_provider *enc, + const struct krb5_hash_provider *hash, + const krb5_keyblock *usage_keyblock, + const krb5_data *checksum, krb5_keyblock *out) +{ + krb5_keyblock *trunc_keyblock = NULL; + krb5_data out_data = make_data(out->contents, out->length); + krb5_error_code ret; + + /* Copy usage_keyblock to trunc_keyblock and truncate if exportable. */ + ret = krb5int_c_copy_keyblock(NULL, usage_keyblock, &trunc_keyblock); + if (ret != 0) + return ret; + if (trunc_keyblock->enctype == ENCTYPE_ARCFOUR_HMAC_EXP) + memset(trunc_keyblock->contents + 7, 0xab, 9); + + /* Compute HMAC(trunc_key, checksum) to produce the encryption key. */ + ret = krb5int_hmac_keyblock(hash, trunc_keyblock, 1, checksum, &out_data); + krb5int_c_free_keyblock(NULL, trunc_keyblock); + return ret; +} + krb5_error_code krb5int_arcfour_encrypt(const struct krb5_enc_provider *enc, const struct krb5_hash_provider *hash, @@ -69,129 +101,75 @@ krb5int_arcfour_encrypt(const struct krb5_enc_provider *enc, const krb5_data *ivec, const krb5_data *input, krb5_data *output) { - krb5_keyblock k1, k2, k3; - krb5_key k3key = NULL; - krb5_data d1, d2, d3, salt, plaintext, checksum, ciphertext, confounder; - krb5_keyusage ms_usage; - size_t keylength, keybytes, blocksize, hashsize; + krb5_keyblock *usage_keyblock = NULL, *enc_keyblock = NULL; + krb5_key enc_key; + krb5_data plaintext = empty_data(); + krb5_data checksum, ciphertext, confounder; krb5_error_code ret; + unsigned int plainlen; - blocksize = enc->block_size; - keybytes = enc->keybytes; - keylength = enc->keylength; - hashsize = hash->hashsize; - - d1.length=keybytes; - d1.data=malloc(d1.length); - if (d1.data == NULL) - return (ENOMEM); - k1 = key->keyblock; - k1.length=d1.length; - k1.contents= (void *) d1.data; - - d2.length=keybytes; - d2.data=malloc(d2.length); - if (d2.data == NULL) { - free(d1.data); - return (ENOMEM); - } - k2 = key->keyblock; - k2.length=d2.length; - k2.contents=(void *) d2.data; - - d3.length=keybytes; - d3.data=malloc(d3.length); - if (d3.data == NULL) { - free(d1.data); - free(d2.data); - return (ENOMEM); - } - k3 = key->keyblock; - k3.length=d3.length; - k3.contents= (void *) d3.data; - - salt.length=14; - salt.data=malloc(salt.length); - if (salt.data == NULL) { - free(d1.data); - free(d2.data); - free(d3.data); - return (ENOMEM); - } - - /* is "input" already blocksize aligned? if it is, then we need this - step, otherwise we do not */ - plaintext.length=krb5_roundup(input->length+CONFOUNDERLENGTH,blocksize); - plaintext.data=malloc(plaintext.length); - if (plaintext.data == NULL) { - free(d1.data); - free(d2.data); - free(d3.data); - free(salt.data); - return(ENOMEM); - } - - /* setup convienient pointers into the allocated data */ - checksum.length=hashsize; - checksum.data=output->data; - ciphertext.length=krb5_roundup(input->length+CONFOUNDERLENGTH,blocksize); - ciphertext.data=output->data+hashsize; - confounder.length=CONFOUNDERLENGTH; - confounder.data=plaintext.data; - output->length = plaintext.length+hashsize; - - /* begin the encryption, computer K1 */ - ms_usage=krb5int_arcfour_translate_usage(usage); - if (key->keyblock.enctype == ENCTYPE_ARCFOUR_HMAC_EXP) { - strncpy(salt.data, krb5int_arcfour_l40, salt.length); - store_32_le(ms_usage, salt.data+10); - } else { - salt.length=4; - store_32_le(ms_usage, salt.data); - } - krb5int_hmac(hash, key, 1, &salt, &d1); + /* Allocate buffers. */ + plainlen = krb5_roundup(input->length + CONFOUNDERLENGTH, enc->block_size); + ret = alloc_data(&plaintext, plainlen); + if (ret != 0) + goto cleanup; + ret = krb5int_c_init_keyblock(NULL, key->keyblock.enctype, enc->keybytes, + &usage_keyblock); + if (ret != 0) + goto cleanup; + ret = krb5int_c_init_keyblock(NULL, key->keyblock.enctype, enc->keybytes, + &enc_keyblock); + if (ret != 0) + goto cleanup; - memcpy(k2.contents, k1.contents, k2.length); + /* Set up subsets of output and plaintext. */ + checksum = make_data(output->data, hash->hashsize); + ciphertext = make_data(output->data + hash->hashsize, plainlen); + confounder = make_data(plaintext.data, CONFOUNDERLENGTH); - if (key->keyblock.enctype==ENCTYPE_ARCFOUR_HMAC_EXP) - memset(k1.contents+7, 0xab, 9); + /* Derive a usage key from the session key and usage. */ + ret = krb5int_arcfour_usage_key(enc, hash, &key->keyblock, usage, + usage_keyblock); + if (ret != 0) + goto cleanup; - ret=krb5_c_random_make_octets(/* XXX */ 0, &confounder); - memcpy(plaintext.data+confounder.length, input->data, input->length); + /* Compose a confounder with the input data to form the plaintext. */ + ret = krb5_c_random_make_octets(NULL, &confounder); + memcpy(plaintext.data + confounder.length, input->data, input->length); if (ret) goto cleanup; - ret = krb5int_hmac_keyblock(hash, &k2, 1, &plaintext, &checksum); + /* Compute HMAC(usage key, plaintext) to get the checksum. */ + ret = krb5int_hmac_keyblock(hash, usage_keyblock, 1, &plaintext, + &checksum); if (ret) goto cleanup; - ret = krb5int_hmac_keyblock(hash, &k1, 1, &checksum, &d3); + /* Derive the encryption key from the usage key and checksum. */ + ret = krb5int_arcfour_enc_key(enc, hash, usage_keyblock, &checksum, + enc_keyblock); if (ret) goto cleanup; - ret = krb5_k_create_key(NULL, &k3, &k3key); + /* Encrypt the plaintext. */ + ret = krb5_k_create_key(NULL, enc_keyblock, &enc_key); + if (ret) + goto cleanup; + ret = (*enc->encrypt)(enc_key, ivec, &plaintext, &ciphertext); + krb5_k_free_key(NULL, enc_key); if (ret) goto cleanup; - ret=(*(enc->encrypt))(k3key, ivec, &plaintext, &ciphertext); + output->length = plaintext.length + hash->hashsize; + return 0; cleanup: - memset(d1.data, 0, d1.length); - memset(d2.data, 0, d2.length); - memset(d3.data, 0, d3.length); - memset(salt.data, 0, salt.length); - memset(plaintext.data, 0, plaintext.length); - - free(d1.data); - free(d2.data); - free(d3.data); - free(salt.data); - free(plaintext.data); - krb5_k_free_key(NULL, k3key); - return (ret); + krb5int_c_free_keyblock(NULL, usage_keyblock); + krb5int_c_free_keyblock(NULL, enc_keyblock); + zapfree(plaintext.data, plaintext.length); + return ret; } -/* This is the arcfour-hmac decryption routine */ krb5_error_code krb5int_arcfour_decrypt(const struct krb5_enc_provider *enc, const struct krb5_hash_provider *hash, @@ -199,110 +177,64 @@ krb5int_arcfour_decrypt(const struct krb5_enc_provider *enc, const krb5_data *ivec, const krb5_data *input, krb5_data *output) { - krb5_keyblock k1,k2,k3; - krb5_key k3key; - krb5_data d1,d2,d3,salt,ciphertext,plaintext,checksum; - krb5_keyusage ms_usage; - size_t keybytes, keylength, hashsize, blocksize; + krb5_keyblock *usage_keyblock = NULL, *enc_keyblock = NULL; + krb5_data plaintext = empty_data(), comp_checksum = empty_data(); + krb5_data checksum, ciphertext; + krb5_key enc_key; krb5_error_code ret; - blocksize = enc->block_size; - keybytes = enc->keybytes; - keylength = enc->keylength; - hashsize = hash->hashsize; - - d1.length=keybytes; - d1.data=malloc(d1.length); - if (d1.data == NULL) - return (ENOMEM); - k1 = key->keyblock; - k1.length=d1.length; - k1.contents= (void *) d1.data; - - d2.length=keybytes; - d2.data=malloc(d2.length); - if (d2.data == NULL) { - free(d1.data); - return (ENOMEM); - } - k2 = key->keyblock; - k2.length=d2.length; - k2.contents= (void *) d2.data; - - d3.length=keybytes; - d3.data=malloc(d3.length); - if (d3.data == NULL) { - free(d1.data); - free(d2.data); - return (ENOMEM); - } - k3 = key->keyblock; - k3.length=d3.length; - k3.contents= (void *) d3.data; - - salt.length=14; - salt.data=malloc(salt.length); - if(salt.data==NULL) { - free(d1.data); - free(d2.data); - free(d3.data); - return (ENOMEM); - } - - ciphertext.length=input->length-hashsize; - ciphertext.data=input->data+hashsize; - plaintext.length=ciphertext.length; - plaintext.data=malloc(plaintext.length); - if (plaintext.data == NULL) { - free(d1.data); - free(d2.data); - free(d3.data); - free(salt.data); - return (ENOMEM); - } + /* Set up subsets of input. */ + checksum = make_data(input->data, hash->hashsize); + ciphertext = make_data(input->data + hash->hashsize, + input->length - hash->hashsize); - checksum.length=hashsize; - checksum.data=input->data; - - ms_usage=krb5int_arcfour_translate_usage(usage); + /* Allocate buffers. */ + ret = alloc_data(&plaintext, ciphertext.length); + if (ret != 0) + goto cleanup; + ret = alloc_data(&comp_checksum, hash->hashsize); + if (ret != 0) + goto cleanup; + ret = krb5int_c_init_keyblock(NULL, key->keyblock.enctype, enc->keybytes, + &usage_keyblock); + if (ret != 0) + goto cleanup; + ret = krb5int_c_init_keyblock(NULL, key->keyblock.enctype, enc->keybytes, + &enc_keyblock); + if (ret != 0) + goto cleanup; - /* We may have to try two ms_usage values; see below. */ + /* We may have to try two usage values; see below. */ do { - /* compute the salt */ - if (key->keyblock.enctype == ENCTYPE_ARCFOUR_HMAC_EXP) { - strncpy(salt.data, krb5int_arcfour_l40, salt.length); - store_32_le(ms_usage, salt.data + 10); - } else { - salt.length = 4; - store_32_le(ms_usage, salt.data); - } - ret = krb5int_hmac(hash, key, 1, &salt, &d1); - if (ret) + /* Derive a usage key from the session key and usage. */ + ret = krb5int_arcfour_usage_key(enc, hash, &key->keyblock, usage, + usage_keyblock); + if (ret != 0) goto cleanup; - memcpy(k2.contents, k1.contents, k2.length); - - if (key->keyblock.enctype == ENCTYPE_ARCFOUR_HMAC_EXP) - memset(k1.contents + 7, 0xab, 9); - - ret = krb5int_hmac_keyblock(hash, &k1, 1, &checksum, &d3); + /* Derive the encryption key from the usage key and checksum. */ + ret = krb5int_arcfour_enc_key(enc, hash, usage_keyblock, &checksum, + enc_keyblock); if (ret) goto cleanup; - ret = krb5_k_create_key(NULL, &k3, &k3key); + /* Decrypt the ciphertext. */ + ret = krb5_k_create_key(NULL, enc_keyblock, &enc_key); if (ret) goto cleanup; - ret = (*(enc->decrypt))(k3key, ivec, &ciphertext, &plaintext); - krb5_k_free_key(NULL, k3key); + ret = (*enc->decrypt)(enc_key, ivec, &ciphertext, &plaintext); + krb5_k_free_key(NULL, enc_key); if (ret) goto cleanup; - ret = krb5int_hmac_keyblock(hash, &k2, 1, &plaintext, &d1); + /* Compute HMAC(usage key, plaintext) to get the checksum. */ + ret = krb5int_hmac_keyblock(hash, usage_keyblock, 1, &plaintext, + &comp_checksum); if (ret) goto cleanup; - if (memcmp(checksum.data, d1.data, hashsize) != 0) { - if (ms_usage == 9) { + if (memcmp(checksum.data, comp_checksum.data, hash->hashsize) != 0) { + if (usage == 9) { /* * RFC 4757 specifies usage 8 for TGS-REP encrypted * parts encrypted in a subkey, but the value used by MS @@ -310,7 +242,7 @@ krb5int_arcfour_decrypt(const struct krb5_enc_provider *enc, * back to 8 on failure in case we are communicating * with a KDC using the value from the RFC. */ - ms_usage = 8; + usage = 8; continue; } ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; @@ -320,21 +252,15 @@ krb5int_arcfour_decrypt(const struct krb5_enc_provider *enc, break; } while (1); - memcpy(output->data, plaintext.data+CONFOUNDERLENGTH, - (plaintext.length-CONFOUNDERLENGTH)); - output->length=plaintext.length-CONFOUNDERLENGTH; + /* Remove the confounder from the plaintext to get the output. */ + memcpy(output->data, plaintext.data + CONFOUNDERLENGTH, + plaintext.length - CONFOUNDERLENGTH); + output->length = plaintext.length - CONFOUNDERLENGTH; cleanup: - memset(d1.data, 0, d1.length); - memset(d2.data, 0, d2.length); - memset(d3.data, 0, d2.length); - memset(salt.data, 0, salt.length); - memset(plaintext.data, 0, plaintext.length); - - free(d1.data); - free(d2.data); - free(d3.data); - free(salt.data); - free(plaintext.data); - return (ret); + krb5int_c_free_keyblock(NULL, usage_keyblock); + krb5int_c_free_keyblock(NULL, enc_keyblock); + zapfree(plaintext.data, plaintext.length); + zapfree(comp_checksum.data, comp_checksum.length); + return ret; } diff --git a/src/lib/crypto/krb/arcfour/arcfour_aead.c b/src/lib/crypto/krb/arcfour/arcfour_aead.c index 335075973..a409484c5 100644 --- a/src/lib/crypto/krb/arcfour/arcfour_aead.c +++ b/src/lib/crypto/krb/arcfour/arcfour_aead.c @@ -61,24 +61,6 @@ krb5int_arcfour_crypto_length(const struct krb5_aead_provider *aead, return 0; } -static krb5_error_code -alloc_derived_key(const struct krb5_enc_provider *enc, - krb5_keyblock *dst, - krb5_data *data, - const krb5_keyblock *src) -{ - data->length = enc->keybytes; - data->data = malloc(data->length); - if (data->data == NULL) - return ENOMEM; - - *dst = *src; - dst->length = data->length; - dst->contents = (void *)data->data; - - return 0; -} - static krb5_error_code krb5int_arcfour_encrypt_iov(const struct krb5_aead_provider *aead, const struct krb5_enc_provider *enc, @@ -91,18 +73,11 @@ krb5int_arcfour_encrypt_iov(const struct krb5_aead_provider *aead, { krb5_error_code ret; krb5_crypto_iov *header, *trailer; - krb5_keyblock k1, k2, k3; - krb5_key k3key = NULL; - krb5_data d1, d2, d3; + krb5_keyblock *usage_keyblock = NULL, *enc_keyblock = NULL; + krb5_key enc_key; krb5_data checksum, confounder, header_data; - krb5_keyusage ms_usage; - char salt_data[14]; - krb5_data salt; size_t i; - d1.length = d2.length = d3.length = 0; - d1.data = d2.data = d3.data = NULL; - /* * Caller must have provided space for the header, padding * and trailer; per RFC 4757 we will arrange it as: @@ -117,100 +92,69 @@ krb5int_arcfour_encrypt_iov(const struct krb5_aead_provider *aead, header_data = header->data; - /* Trailer may be absent */ + /* Trailer may be absent. */ trailer = krb5int_c_locate_iov(data, num_data, KRB5_CRYPTO_TYPE_TRAILER); if (trailer != NULL) trailer->data.length = 0; - /* Ensure that there is no padding */ + /* Ensure that there is no padding. */ for (i = 0; i < num_data; i++) { if (data[i].flags == KRB5_CRYPTO_TYPE_PADDING) data[i].data.length = 0; } - ret = alloc_derived_key(enc, &k1, &d1, &key->keyblock); + ret = krb5int_c_init_keyblock(NULL, key->keyblock.enctype, enc->keybytes, + &usage_keyblock); if (ret != 0) goto cleanup; - - ret = alloc_derived_key(enc, &k2, &d2, &key->keyblock); - if (ret != 0) - goto cleanup; - - ret = alloc_derived_key(enc, &k3, &d3, &key->keyblock); + ret = krb5int_c_init_keyblock(NULL, key->keyblock.enctype, enc->keybytes, + &enc_keyblock); if (ret != 0) goto cleanup; - /* Begin the encryption, compute K1 */ - salt.data = salt_data; - salt.length = sizeof(salt_data); - - ms_usage = krb5int_arcfour_translate_usage(usage); - - if (key->keyblock.enctype == ENCTYPE_ARCFOUR_HMAC_EXP) { - strncpy(salt.data, krb5int_arcfour_l40, salt.length); - store_32_le(ms_usage, salt.data + 10); - } else { - salt.length = 4; - store_32_le(ms_usage, salt.data); - } - ret = krb5int_hmac(hash, key, 1, &salt, &d1); + /* Derive a usage key from the session key and usage. */ + ret = krb5int_arcfour_usage_key(enc, hash, &key->keyblock, usage, + usage_keyblock); if (ret != 0) goto cleanup; - memcpy(k2.contents, k1.contents, k2.length); - - if (key->keyblock.enctype == ENCTYPE_ARCFOUR_HMAC_EXP) - memset(k1.contents + 7, 0xAB, 9); - + /* Generate a confounder in the header block, after the checksum. */ header->data.length = hash->hashsize + CONFOUNDERLENGTH; - - confounder.data = header->data.data + hash->hashsize; - confounder.length = CONFOUNDERLENGTH; - + confounder = make_data(header->data.data + hash->hashsize, + CONFOUNDERLENGTH); ret = krb5_c_random_make_octets(0, &confounder); if (ret != 0) goto cleanup; + checksum = make_data(header->data.data, hash->hashsize); - checksum.data = header->data.data; - checksum.length = hash->hashsize; - - /* Adjust pointers so confounder is at start of header */ + /* Adjust pointers so confounder is at start of header. */ header->data.length -= hash->hashsize; - header->data.data += hash->hashsize; + header->data.data += hash->hashsize; - ret = krb5int_hmac_iov_keyblock(hash, &k2, data, num_data, &checksum); + /* Compute the checksum using the usage key. */ + ret = krb5int_hmac_iov_keyblock(hash, usage_keyblock, data, num_data, + &checksum); if (ret != 0) goto cleanup; - ret = krb5int_hmac_keyblock(hash, &k1, 1, &checksum, &d3); - if (ret != 0) + /* Derive the encryption key from the usage key and checksum. */ + ret = krb5int_arcfour_enc_key(enc, hash, usage_keyblock, &checksum, + enc_keyblock); + if (ret) goto cleanup; - ret = krb5_k_create_key(NULL, &k3, &k3key); + ret = krb5_k_create_key(NULL, enc_keyblock, &enc_key); if (ret != 0) goto cleanup; - - ret = enc->encrypt_iov(k3key, ivec, data, num_data); + ret = enc->encrypt_iov(enc_key, ivec, data, num_data); + krb5_k_free_key(NULL, enc_key); if (ret != 0) goto cleanup; cleanup: - header->data = header_data; /* restore header pointers */ - - if (d1.data != NULL) { - memset(d1.data, 0, d1.length); - free(d1.data); - } - if (d2.data != NULL) { - memset(d2.data, 0, d2.length); - free(d2.data); - } - if (d3.data != NULL) { - memset(d3.data, 0, d3.length); - free(d3.data); - } - - krb5_k_free_key(NULL, k3key); + header->data = header_data; /* Restore header pointers. */ + krb5int_c_free_keyblock(NULL, usage_keyblock); + krb5int_c_free_keyblock(NULL, enc_keyblock); return ret; } @@ -226,16 +170,9 @@ krb5int_arcfour_decrypt_iov(const struct krb5_aead_provider *aead, { krb5_error_code ret; krb5_crypto_iov *header, *trailer; - krb5_keyblock k1, k2, k3; - krb5_key k3key = NULL; - krb5_data d1, d2, d3; - krb5_data checksum, header_data; - krb5_keyusage ms_usage; - char salt_data[14]; - krb5_data salt; - - d1.length = d2.length = d3.length = 0; - d1.data = d2.data = d3.data = NULL; + krb5_keyblock *usage_keyblock = NULL, *enc_keyblock = NULL; + krb5_key enc_key; + krb5_data checksum, header_data, comp_checksum = empty_data(); header = krb5int_c_locate_iov(data, num_data, KRB5_CRYPTO_TYPE_HEADER); if (header == NULL || @@ -248,85 +185,78 @@ krb5int_arcfour_decrypt_iov(const struct krb5_aead_provider *aead, if (trailer != NULL && trailer->data.length != 0) return KRB5_BAD_MSIZE; - ret = alloc_derived_key(enc, &k1, &d1, &key->keyblock); - if (ret != 0) - goto cleanup; - - ret = alloc_derived_key(enc, &k2, &d2, &key->keyblock); + /* Allocate buffers. */ + ret = alloc_data(&comp_checksum, hash->hashsize); if (ret != 0) goto cleanup; - - ret = alloc_derived_key(enc, &k3, &d3, &key->keyblock); + ret = krb5int_c_init_keyblock(NULL, key->keyblock.enctype, enc->keybytes, + &usage_keyblock); if (ret != 0) goto cleanup; - - /* Begin the decryption, compute K1 */ - salt.data = salt_data; - salt.length = sizeof(salt_data); - - ms_usage = krb5int_arcfour_translate_usage(usage); - - if (key->keyblock.enctype == ENCTYPE_ARCFOUR_HMAC_EXP) { - strncpy(salt.data, krb5int_arcfour_l40, salt.length); - store_32_le(ms_usage, (unsigned char *)salt.data + 10); - } else { - salt.length = 4; - store_32_le(ms_usage, (unsigned char *)salt.data); - } - ret = krb5int_hmac(hash, key, 1, &salt, &d1); + ret = krb5int_c_init_keyblock(NULL, key->keyblock.enctype, enc->keybytes, + &enc_keyblock); if (ret != 0) goto cleanup; - memcpy(k2.contents, k1.contents, k2.length); - - if (key->keyblock.enctype == ENCTYPE_ARCFOUR_HMAC_EXP) - memset(k1.contents + 7, 0xAB, 9); + checksum = make_data(header->data.data, hash->hashsize); - checksum.data = header->data.data; - checksum.length = hash->hashsize; - - /* Adjust pointers so confounder is at start of header */ + /* Adjust pointers so confounder is at start of header. */ header->data.length -= hash->hashsize; - header->data.data += hash->hashsize; - - ret = krb5int_hmac_keyblock(hash, &k1, 1, &checksum, &d3); - if (ret != 0) - goto cleanup; - - ret = krb5_k_create_key(NULL, &k3, &k3key); - if (ret != 0) - goto cleanup; + header->data.data += hash->hashsize; + + /* We may have to try two usage values; see below. */ + do { + /* Derive a usage key from the session key and usage. */ + ret = krb5int_arcfour_usage_key(enc, hash, &key->keyblock, usage, + usage_keyblock); + if (ret != 0) + goto cleanup; + + /* Derive the encryption key from the usage key and checksum. */ + ret = krb5int_arcfour_enc_key(enc, hash, usage_keyblock, &checksum, + enc_keyblock); + if (ret) + goto cleanup; + + /* Decrypt the ciphertext. */ + ret = krb5_k_create_key(NULL, enc_keyblock, &enc_key); + if (ret != 0) + goto cleanup; + ret = enc->decrypt_iov(enc_key, ivec, data, num_data); + krb5_k_free_key(NULL, enc_key); + if (ret != 0) + goto cleanup; + + /* Compute HMAC(usage key, plaintext) to get the checksum. */ + ret = krb5int_hmac_iov_keyblock(hash, usage_keyblock, data, num_data, + &comp_checksum); + if (ret != 0) + goto cleanup; + + if (memcmp(checksum.data, comp_checksum.data, hash->hashsize) != 0) { + if (usage == 9) { + /* + * RFC 4757 specifies usage 8 for TGS-REP encrypted + * parts encrypted in a subkey, but the value used by MS + * is actually 9. We now use 9 to start with, but fall + * back to 8 on failure in case we are communicating + * with a KDC using the value from the RFC. + */ + usage = 8; + continue; + } + ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; + goto cleanup; + } - ret = enc->decrypt_iov(k3key, ivec, data, num_data); - if (ret != 0) - goto cleanup; - - ret = krb5int_hmac_iov_keyblock(hash, &k2, data, num_data, &d1); - if (ret != 0) - goto cleanup; - - if (memcmp(checksum.data, d1.data, hash->hashsize) != 0) { - ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; - goto cleanup; - } + break; + } while (1); cleanup: - header->data = header_data; /* restore header pointers */ - - if (d1.data != NULL) { - memset(d1.data, 0, d1.length); - free(d1.data); - } - if (d2.data != NULL) { - memset(d2.data, 0, d2.length); - free(d2.data); - } - if (d3.data != NULL) { - memset(d3.data, 0, d3.length); - free(d3.data); - } - - krb5_k_free_key(NULL, k3key); + header->data = header_data; /* Restore header pointers. */ + krb5int_c_free_keyblock(NULL, usage_keyblock); + krb5int_c_free_keyblock(NULL, enc_keyblock); + zapfree(comp_checksum.data, comp_checksum.length); return ret; }