krb5_error_code krb5int_dk_cmac_prf(const struct krb5_keytypes *ktp, krb5_key key, const krb5_data *in, krb5_data *out) { krb5_crypto_iov iov; krb5_data prfconst = make_data("prf", 3); krb5_key kp = NULL; krb5_error_code ret; if (ktp->prf_length != ktp->enc->block_size) return KRB5_BAD_MSIZE; iov.flags = KRB5_CRYPTO_TYPE_DATA; iov.data = *in; /* Derive a key using the PRF constant. */ ret = krb5int_derive_key(ktp->enc, key, &kp, &prfconst, DERIVE_SP800_108_CMAC); if (ret != 0) goto cleanup; /* PRF is CMAC of input */ ret = krb5int_cmac_checksum(ktp->enc, kp, &iov, 1, out); if (ret != 0) goto cleanup; cleanup: krb5_k_free_key(NULL, kp); return ret; }
int main(int argc, char **argv) { #ifdef CAMELLIA krb5_context context = NULL; krb5_keyblock keyblock; krb5_key key; const struct krb5_enc_provider *enc = &krb5int_enc_camellia128; krb5_crypto_iov iov; unsigned char resultbuf[16]; krb5_data result = make_data(resultbuf, 16); /* Create the example key. */ keyblock.magic = KV5M_KEYBLOCK; keyblock.enctype = ENCTYPE_CAMELLIA128_CTS_CMAC; keyblock.length = 16; keyblock.contents = keybytes; assert(krb5_k_create_key(context, &keyblock, &key) == 0); /* Example 1. */ iov.flags = KRB5_CRYPTO_TYPE_DATA; iov.data = make_data(input, 0); assert(krb5int_cmac_checksum(enc, key, &iov, 1, &result) == 0); check_result("example 1", resultbuf, cmac1); /* Example 2. */ iov.data.length = 16; assert(krb5int_cmac_checksum(enc, key, &iov, 1, &result) == 0); check_result("example 2", resultbuf, cmac2); /* Example 3. */ iov.data.length = 40; assert(krb5int_cmac_checksum(enc, key, &iov, 1, &result) == 0); check_result("example 3", resultbuf, cmac3); /* Example 4. */ iov.data.length = 64; assert(krb5int_cmac_checksum(enc, key, &iov, 1, &result) == 0); check_result("example 4", resultbuf, cmac4); printf("All CMAC tests passed.\n"); krb5_k_free_key(context, key); #endif /* CAMELLIA */ return 0; }
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; }
/* * NIST SP800-108 KDF in feedback mode (section 5.2). * Parameters: * - CMAC (with enc as the enc provider) is the PRF. * - A block counter of four bytes is used. * - Label is the key derivation constant. * - Context is empty. * - Four bytes are used to encode the output length in the PRF input. */ static krb5_error_code derive_random_sp800_108_cmac(const struct krb5_enc_provider *enc, krb5_key inkey, krb5_data *outrnd, const krb5_data *in_constant) { size_t blocksize, keybytes, n; krb5_crypto_iov iov[6]; krb5_error_code ret; krb5_data prf; unsigned int i; unsigned char ibuf[4], Lbuf[4]; blocksize = enc->block_size; keybytes = enc->keybytes; if (inkey->keyblock.length != enc->keylength || outrnd->length != keybytes) return KRB5_CRYPTO_INTERNAL; /* Allocate encryption data buffer. */ ret = alloc_data(&prf, blocksize); if (ret) return ret; /* K(i-1): the previous block of PRF output, initially all-zeros. */ iov[0].flags = KRB5_CRYPTO_TYPE_DATA; iov[0].data = prf; /* [i]2: four-byte big-endian binary string giving the block counter */ iov[1].flags = KRB5_CRYPTO_TYPE_DATA; iov[1].data = make_data(ibuf, sizeof(ibuf)); /* Label: the fixed derived-key input */ iov[2].flags = KRB5_CRYPTO_TYPE_DATA; iov[2].data = *in_constant; /* 0x00: separator byte */ iov[3].flags = KRB5_CRYPTO_TYPE_DATA; iov[3].data = make_data("", 1); /* Context: (unused) */ iov[4].flags = KRB5_CRYPTO_TYPE_DATA; iov[4].data = empty_data(); /* [L]2: four-byte big-endian binary string giving the output length */ iov[5].flags = KRB5_CRYPTO_TYPE_DATA; iov[5].data = make_data(Lbuf, sizeof(Lbuf)); store_32_be(outrnd->length * 8, Lbuf); for (i = 1, n = 0; n < keybytes; i++) { /* Update the block counter. */ store_32_be(i, ibuf); /* Compute a CMAC checksum, storing the result into K(i-1). */ ret = krb5int_cmac_checksum(enc, inkey, iov, 6, &prf); if (ret) goto cleanup; /* Copy the result into the appropriate part of the output buffer. */ if (keybytes - n <= blocksize) { memcpy(outrnd->data + n, prf.data, keybytes - n); break; } memcpy(outrnd->data + n, prf.data, blocksize); n += blocksize; } cleanup: zapfree(prf.data, blocksize); return ret; }