krb5_error_code krb5int_dk_cmac_encrypt(const struct krb5_keytypes *ktp, krb5_key key, krb5_keyusage usage, const krb5_data *ivec, krb5_crypto_iov *data, size_t num_data) { const struct krb5_enc_provider *enc = ktp->enc; krb5_error_code ret; krb5_crypto_iov *header, *trailer, *padding; krb5_data cksum = empty_data(); krb5_key ke = NULL, ki = NULL; /* E(Confounder | Plaintext | Pad) | Checksum */ /* Validate header and trailer lengths, and zero out padding length. */ header = krb5int_c_locate_iov(data, num_data, KRB5_CRYPTO_TYPE_HEADER); if (header == NULL || header->data.length < enc->block_size) return KRB5_BAD_MSIZE; trailer = krb5int_c_locate_iov(data, num_data, KRB5_CRYPTO_TYPE_TRAILER); if (trailer == NULL || trailer->data.length < enc->block_size) return KRB5_BAD_MSIZE; padding = krb5int_c_locate_iov(data, num_data, KRB5_CRYPTO_TYPE_PADDING); if (padding != NULL) padding->data.length = 0; /* Derive the encryption and integrity keys. */ ret = derive_keys(enc, key, usage, &ke, &ki); if (ret != 0) goto cleanup; /* Generate confounder. */ header->data.length = enc->block_size; ret = krb5_c_random_make_octets(NULL, &header->data); if (ret != 0) goto cleanup; /* Checksum the plaintext. */ ret = krb5int_cmac_checksum(enc, ki, data, num_data, &trailer->data); if (ret != 0) goto cleanup; /* Encrypt the plaintext (header | data | padding) */ ret = enc->encrypt(ke, ivec, data, num_data); if (ret != 0) goto cleanup; cleanup: krb5_k_free_key(NULL, ke); krb5_k_free_key(NULL, ki); zapfree(cksum.data, cksum.length); return ret; }
krb5_error_code krb5int_dk_cmac_decrypt(const struct krb5_keytypes *ktp, krb5_key key, krb5_keyusage usage, const krb5_data *ivec, krb5_crypto_iov *data, size_t num_data) { const struct krb5_enc_provider *enc = ktp->enc; krb5_error_code ret; krb5_crypto_iov *header, *trailer; krb5_data cksum = empty_data(); krb5_key ke = NULL, ki = NULL; /* E(Confounder | Plaintext | Pad) | Checksum */ /* Validate header and trailer lengths. */ header = krb5int_c_locate_iov(data, num_data, KRB5_CRYPTO_TYPE_HEADER); if (header == NULL || header->data.length != enc->block_size) return KRB5_BAD_MSIZE; trailer = krb5int_c_locate_iov(data, num_data, KRB5_CRYPTO_TYPE_TRAILER); if (trailer == NULL || trailer->data.length != enc->block_size) return KRB5_BAD_MSIZE; /* Derive the encryption and integrity keys. */ ret = derive_keys(enc, key, usage, &ke, &ki); if (ret != 0) goto cleanup; /* Decrypt the plaintext (header | data | padding). */ ret = enc->decrypt(ke, ivec, data, num_data); if (ret != 0) goto cleanup; /* Verify the hash. */ ret = alloc_data(&cksum, enc->block_size); if (ret != 0) goto cleanup; ret = krb5int_cmac_checksum(enc, ki, data, num_data, &cksum); if (ret != 0) goto cleanup; if (k5_bcmp(cksum.data, trailer->data.data, enc->block_size) != 0) ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; cleanup: krb5_k_free_key(NULL, ke); krb5_k_free_key(NULL, ki); zapfree(cksum.data, cksum.length); return ret; }
static krb5_error_code krb5int_raw_decrypt_iov(const struct krb5_aead_provider *aead, const struct krb5_enc_provider *enc, const struct krb5_hash_provider *hash, const krb5_keyblock *key, krb5_keyusage usage, const krb5_data *ivec, krb5_crypto_iov *data, size_t num_data) { krb5_error_code ret; size_t i; unsigned int blocksize = 0; /* careful, this is enc block size not confounder len */ unsigned int cipherlen = 0; if (krb5int_c_locate_iov(data, num_data, KRB5_CRYPTO_TYPE_STREAM) != NULL) { return krb5int_c_iov_decrypt_stream(aead, enc, hash, key, usage, ivec, data, num_data); } /* E(Confounder | Plaintext | Pad) | Checksum */ ret = aead->crypto_length(aead, enc, hash, KRB5_CRYPTO_TYPE_PADDING, &blocksize); if (ret != 0) return ret; for (i = 0; i < num_data; i++) { const krb5_crypto_iov *iov = &data[i]; if (ENCRYPT_DATA_IOV(iov)) cipherlen += iov->data.length; } if (blocksize == 0) { /* Check for correct input length in CTS mode */ if (enc->block_size != 0 && cipherlen < enc->block_size) return KRB5_BAD_MSIZE; } else { /* Check that the input data is correctly padded */ if ((cipherlen % blocksize) != 0) return KRB5_BAD_MSIZE; } /* Validate header and trailer lengths */ /* derive the keys */ /* decrypt the plaintext (header | data | padding) */ assert(enc->decrypt_iov != NULL); ret = enc->decrypt_iov(key, ivec, data, num_data); /* will update ivec */ return ret; }
krb5_error_code KRB5_CALLCONV krb5_c_make_checksum_iov(krb5_context context, krb5_cksumtype cksumtype, const krb5_keyblock *key, krb5_keyusage usage, krb5_crypto_iov *data, size_t num_data) { unsigned int i; size_t cksumlen; krb5_error_code ret; krb5_data cksum_data; krb5_crypto_iov *checksum; for (i = 0; i < krb5_cksumtypes_length; i++) { if (krb5_cksumtypes_list[i].ctype == cksumtype) break; } if (i == krb5_cksumtypes_length) return(KRB5_BAD_ENCTYPE); if (krb5_cksumtypes_list[i].keyhash != NULL) cksum_data.length = krb5_cksumtypes_list[i].keyhash->hashsize; else cksum_data.length = krb5_cksumtypes_list[i].hash->hashsize; if (krb5_cksumtypes_list[i].trunc_size != 0) cksumlen = krb5_cksumtypes_list[i].trunc_size; else cksumlen = cksum_data.length; checksum = krb5int_c_locate_iov(data, num_data, KRB5_CRYPTO_TYPE_CHECKSUM); if (checksum == NULL || checksum->data.length < cksumlen) return(KRB5_BAD_MSIZE); cksum_data.data = malloc(cksum_data.length); if (cksum_data.data == NULL) return(ENOMEM); ret = krb5int_c_make_checksum_iov(&krb5_cksumtypes_list[i], key, usage, data, num_data, &cksum_data); if (ret == 0) { memcpy(checksum->data.data, cksum_data.data, cksumlen); checksum->data.length = cksumlen; } free(cksum_data.data); return(ret); }
static krb5_error_code krb5int_raw_encrypt_iov(const struct krb5_aead_provider *aead, const struct krb5_enc_provider *enc, const struct krb5_hash_provider *hash, const krb5_keyblock *key, krb5_keyusage usage, const krb5_data *ivec, krb5_crypto_iov *data, size_t num_data) { krb5_error_code ret; krb5_crypto_iov *padding; size_t i; unsigned int blocksize = 0; unsigned int plainlen = 0; unsigned int padsize = 0; ret = aead->crypto_length(aead, enc, hash, KRB5_CRYPTO_TYPE_PADDING, &blocksize); if (ret != 0) return ret; for (i = 0; i < num_data; i++) { krb5_crypto_iov *iov = &data[i]; if (iov->flags == KRB5_CRYPTO_TYPE_DATA) plainlen += iov->data.length; } if (blocksize != 0) { /* Check that the input data is correctly padded */ if (plainlen % blocksize) padsize = blocksize - (plainlen % blocksize); } padding = krb5int_c_locate_iov(data, num_data, KRB5_CRYPTO_TYPE_PADDING); if (padsize && (padding == NULL || padding->data.length < padsize)) return KRB5_BAD_MSIZE; if (padding != NULL) { memset(padding->data.data, 0, padsize); padding->data.length = padsize; } assert(enc->encrypt_iov != NULL); ret = enc->encrypt_iov(key, ivec, data, num_data); /* will update ivec */ return ret; }
krb5_error_code KRB5_CALLCONV krb5_k_decrypt_iov(krb5_context context, krb5_key key, krb5_keyusage usage, const krb5_data *cipher_state, krb5_crypto_iov *data, size_t num_data) { const struct krb5_keytypes *ktp; ktp = find_enctype(key->keyblock.enctype); if (ktp == NULL) return KRB5_BAD_ENCTYPE; if (krb5int_c_locate_iov(data, num_data, KRB5_CRYPTO_TYPE_STREAM) != NULL) { return krb5int_c_iov_decrypt_stream(ktp, key, usage, cipher_state, data, num_data); } return ktp->decrypt(ktp, key, usage, cipher_state, data, num_data); }
static krb5_error_code krb5int_arcfour_encrypt_iov(const struct krb5_aead_provider *aead, const struct krb5_enc_provider *enc, const struct krb5_hash_provider *hash, const krb5_keyblock *key, krb5_keyusage usage, const krb5_data *ivec, krb5_crypto_iov *data, size_t num_data) { krb5_error_code ret; krb5_crypto_iov *header, *trailer; krb5_keyblock k1, k2, k3; krb5_data d1, d2, d3; 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: * * Checksum | E(Confounder | Plaintext) */ header = krb5int_c_locate_iov(data, num_data, KRB5_CRYPTO_TYPE_HEADER); if (header == NULL || header->data.length < hash->hashsize + CONFOUNDERLENGTH) return KRB5_BAD_MSIZE; header_data = header->data; /* 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 */ 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); if (ret != 0) goto cleanup; ret = alloc_derived_key(enc, &k2, &d2, key); if (ret != 0) goto cleanup; ret = alloc_derived_key(enc, &k3, &d3, key); 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->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 = krb5_hmac(hash, key, 1, &salt, &d1); if (ret != 0) goto cleanup; memcpy(k2.contents, k1.contents, k2.length); if (key->enctype == ENCTYPE_ARCFOUR_HMAC_EXP) memset(k1.contents + 7, 0xAB, 9); header->data.length = hash->hashsize + CONFOUNDERLENGTH; confounder.data = header->data.data + hash->hashsize; confounder.length = CONFOUNDERLENGTH; ret = krb5_c_random_make_octets(0, &confounder); if (ret != 0) goto cleanup; checksum.data = header->data.data; checksum.length = hash->hashsize; /* Adjust pointers so confounder is at start of header */ header->data.length -= hash->hashsize; header->data.data += hash->hashsize; ret = krb5int_hmac_iov(hash, &k2, data, num_data, &checksum); if (ret != 0) goto cleanup; ret = krb5_hmac(hash, &k1, 1, &checksum, &d3); if (ret != 0) goto cleanup; ret = enc->encrypt_iov(&k3, ivec, data, num_data); 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); } return ret; }
static krb5_error_code krb5int_arcfour_decrypt_iov(const struct krb5_aead_provider *aead, const struct krb5_enc_provider *enc, const struct krb5_hash_provider *hash, const krb5_keyblock *key, krb5_keyusage usage, const krb5_data *ivec, krb5_crypto_iov *data, size_t num_data) { krb5_error_code ret; krb5_crypto_iov *header, *trailer; krb5_keyblock k1, k2, k3; 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; header = krb5int_c_locate_iov(data, num_data, KRB5_CRYPTO_TYPE_HEADER); if (header == NULL || header->data.length != hash->hashsize + CONFOUNDERLENGTH) return KRB5_BAD_MSIZE; header_data = header->data; trailer = krb5int_c_locate_iov(data, num_data, KRB5_CRYPTO_TYPE_TRAILER); if (trailer != NULL && trailer->data.length != 0) return KRB5_BAD_MSIZE; ret = alloc_derived_key(enc, &k1, &d1, key); if (ret != 0) goto cleanup; ret = alloc_derived_key(enc, &k2, &d2, key); if (ret != 0) goto cleanup; ret = alloc_derived_key(enc, &k3, &d3, key); 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->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 = krb5_hmac(hash, key, 1, &salt, &d1); if (ret != 0) goto cleanup; memcpy(k2.contents, k1.contents, k2.length); if (key->enctype == ENCTYPE_ARCFOUR_HMAC_EXP) memset(k1.contents + 7, 0xAB, 9); checksum.data = header->data.data; checksum.length = hash->hashsize; /* Adjust pointers so confounder is at start of header */ header->data.length -= hash->hashsize; header->data.data += hash->hashsize; ret = krb5_hmac(hash, &k1, 1, &checksum, &d3); if (ret != 0) goto cleanup; ret = enc->decrypt_iov(&k3, ivec, data, num_data); if (ret != 0) goto cleanup; ret = krb5int_hmac_iov(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; } 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); } return ret; }
krb5_error_code krb5int_arcfour_decrypt(const struct krb5_keytypes *ktp, krb5_key key, krb5_keyusage usage, const krb5_data *ivec, krb5_crypto_iov *data, size_t num_data) { const struct krb5_enc_provider *enc = ktp->enc; const struct krb5_hash_provider *hash = ktp->hash; krb5_error_code ret; krb5_crypto_iov *header, *trailer; krb5_keyblock *usage_keyblock = NULL, *enc_keyblock = NULL; krb5_data checksum, header_data, comp_checksum = empty_data(); header = krb5int_c_locate_iov(data, num_data, KRB5_CRYPTO_TYPE_HEADER); if (header == NULL || header->data.length != hash->hashsize + CONFOUNDERLENGTH) return KRB5_BAD_MSIZE; header_data = header->data; trailer = krb5int_c_locate_iov(data, num_data, KRB5_CRYPTO_TYPE_TRAILER); if (trailer != NULL && trailer->data.length != 0) return KRB5_BAD_MSIZE; /* Allocate buffers. */ 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; checksum = make_data(header->data.data, hash->hashsize); /* Adjust pointers so confounder is at start of header. */ header->data.length -= hash->hashsize; 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 = 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 = enc_key(enc, hash, usage_keyblock, &checksum, enc_keyblock); if (ret) goto cleanup; /* Decrypt the ciphertext. */ ret = keyblock_crypt(enc, enc_keyblock, ivec, data, num_data); if (ret != 0) goto cleanup; /* Compute HMAC(usage key, plaintext) to get the checksum. */ ret = krb5int_hmac_keyblock(hash, usage_keyblock, data, num_data, &comp_checksum); if (ret != 0) goto cleanup; if (k5_bcmp(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. ivec is always NULL in this case. * We need to re-encrypt the data in the wrong key first. */ ret = keyblock_crypt(enc, enc_keyblock, NULL, data, num_data); if (ret != 0) goto cleanup; usage = 8; continue; } ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; goto cleanup; } break; } while (1); cleanup: 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; }
krb5_error_code krb5int_arcfour_encrypt(const struct krb5_keytypes *ktp, krb5_key key, krb5_keyusage usage, const krb5_data *ivec, krb5_crypto_iov *data, size_t num_data) { const struct krb5_enc_provider *enc = ktp->enc; const struct krb5_hash_provider *hash = ktp->hash; krb5_error_code ret; krb5_crypto_iov *header, *trailer; krb5_keyblock *usage_keyblock = NULL, *enc_keyblock = NULL; krb5_data checksum, confounder, header_data; size_t i; /* * Caller must have provided space for the header, padding * and trailer; per RFC 4757 we will arrange it as: * * Checksum | E(Confounder | Plaintext) */ header = krb5int_c_locate_iov(data, num_data, KRB5_CRYPTO_TYPE_HEADER); if (header == NULL || header->data.length < hash->hashsize + CONFOUNDERLENGTH) return KRB5_BAD_MSIZE; header_data = header->data; /* 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. */ for (i = 0; i < num_data; i++) { if (data[i].flags == KRB5_CRYPTO_TYPE_PADDING) data[i].data.length = 0; } 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; /* Derive a usage key from the session key and usage. */ ret = usage_key(enc, hash, &key->keyblock, usage, usage_keyblock); if (ret != 0) goto cleanup; /* Generate a confounder in the header block, after the checksum. */ header->data.length = hash->hashsize + 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); /* Adjust pointers so confounder is at start of header. */ header->data.length -= hash->hashsize; header->data.data += hash->hashsize; /* Compute the checksum using the usage key. */ ret = krb5int_hmac_keyblock(hash, usage_keyblock, data, num_data, &checksum); if (ret != 0) goto cleanup; /* Derive the encryption key from the usage key and checksum. */ ret = enc_key(enc, hash, usage_keyblock, &checksum, enc_keyblock); if (ret) goto cleanup; ret = keyblock_crypt(enc, enc_keyblock, ivec, data, num_data); cleanup: header->data = header_data; /* Restore header pointers. */ krb5int_c_free_keyblock(NULL, usage_keyblock); krb5int_c_free_keyblock(NULL, enc_keyblock); return ret; }
krb5_error_code krb5int_c_iov_decrypt_stream(const struct krb5_keytypes *ktp, krb5_key key, krb5_keyusage keyusage, const krb5_data *ivec, krb5_crypto_iov *data, size_t num_data) { krb5_error_code ret; unsigned int header_len, trailer_len; krb5_crypto_iov *iov; krb5_crypto_iov *stream; size_t i, j; int got_data = 0; stream = krb5int_c_locate_iov(data, num_data, KRB5_CRYPTO_TYPE_STREAM); assert(stream != NULL); header_len = ktp->crypto_length(ktp, KRB5_CRYPTO_TYPE_HEADER); trailer_len = ktp->crypto_length(ktp, KRB5_CRYPTO_TYPE_TRAILER); if (stream->data.length < header_len + trailer_len) return KRB5_BAD_MSIZE; iov = calloc(num_data + 2, sizeof(krb5_crypto_iov)); if (iov == NULL) return ENOMEM; i = 0; iov[i].flags = KRB5_CRYPTO_TYPE_HEADER; /* takes place of STREAM */ iov[i].data = make_data(stream->data.data, header_len); i++; for (j = 0; j < num_data; j++) { if (data[j].flags == KRB5_CRYPTO_TYPE_DATA) { if (got_data) { free(iov); return KRB5_BAD_MSIZE; } got_data++; data[j].data.data = stream->data.data + header_len; data[j].data.length = stream->data.length - header_len - trailer_len; } if (data[j].flags == KRB5_CRYPTO_TYPE_SIGN_ONLY || data[j].flags == KRB5_CRYPTO_TYPE_DATA) iov[i++] = data[j]; } /* Use empty padding since tokens don't indicate the padding length. */ iov[i].flags = KRB5_CRYPTO_TYPE_PADDING; iov[i].data = empty_data(); i++; iov[i].flags = KRB5_CRYPTO_TYPE_TRAILER; iov[i].data = make_data(stream->data.data + stream->data.length - trailer_len, trailer_len); i++; assert(i <= num_data + 2); ret = ktp->decrypt(ktp, key, keyusage, ivec, iov, i); free(iov); return ret; }