krb5_error_code KRB5_CALLCONV krb5_c_crypto_length(krb5_context context, krb5_enctype enctype, krb5_cryptotype type, unsigned int *size) { const struct krb5_keytypes *ktp; ktp = find_enctype(enctype); if (ktp == NULL) return KRB5_BAD_ENCTYPE; switch (type) { case KRB5_CRYPTO_TYPE_EMPTY: case KRB5_CRYPTO_TYPE_SIGN_ONLY: *size = 0; break; case KRB5_CRYPTO_TYPE_DATA: *size = (unsigned int)~0; /* match Heimdal */ break; case KRB5_CRYPTO_TYPE_HEADER: case KRB5_CRYPTO_TYPE_PADDING: case KRB5_CRYPTO_TYPE_TRAILER: case KRB5_CRYPTO_TYPE_CHECKSUM: *size = ktp->crypto_length(ktp, type); break; default: return EINVAL; } return 0; }
krb5_error_code KRB5_CALLCONV krb5_k_decrypt(krb5_context context, krb5_key key, krb5_keyusage usage, const krb5_data *ivec, const krb5_enc_data *input, krb5_data *output) { const struct krb5_keytypes *ktp; krb5_crypto_iov iov[4]; krb5_error_code ret; unsigned int header_len, trailer_len, plain_len; char *scratch = NULL; ktp = find_enctype(key->keyblock.enctype); if (ktp == NULL) return KRB5_BAD_ENCTYPE; if (input->enctype != ENCTYPE_UNKNOWN && ktp->etype != input->enctype) return KRB5_BAD_ENCTYPE; /* Verify the input and output lengths. */ header_len = ktp->crypto_length(ktp, KRB5_CRYPTO_TYPE_HEADER); trailer_len = ktp->crypto_length(ktp, KRB5_CRYPTO_TYPE_TRAILER); if (input->ciphertext.length < header_len + trailer_len) return KRB5_BAD_MSIZE; plain_len = input->ciphertext.length - header_len - trailer_len; if (output->length < plain_len) return KRB5_BAD_MSIZE; scratch = k5alloc(header_len + trailer_len, &ret); if (scratch == NULL) return ret; iov[0].flags = KRB5_CRYPTO_TYPE_HEADER; iov[0].data = make_data(scratch, header_len); memcpy(iov[0].data.data, input->ciphertext.data, header_len); iov[1].flags = KRB5_CRYPTO_TYPE_DATA; iov[1].data = make_data(output->data, plain_len); memcpy(iov[1].data.data, input->ciphertext.data + header_len, plain_len); /* Use empty padding since tokens don't indicate the padding length. */ iov[2].flags = KRB5_CRYPTO_TYPE_PADDING; iov[2].data = empty_data(); iov[3].flags = KRB5_CRYPTO_TYPE_TRAILER; iov[3].data = make_data(scratch + header_len, trailer_len); memcpy(iov[3].data.data, input->ciphertext.data + header_len + plain_len, trailer_len); ret = ktp->decrypt(ktp, key, usage, ivec, iov, 4); if (ret != 0) zap(output->data, plain_len); else output->length = plain_len; zapfree(scratch, header_len + trailer_len); return ret; }
krb5_error_code srp_gen_keyblock( krb5_context krb5_ctx, char *enc_keytype, char *pass, char *salt, krb5_keyblock *key) { krb5_error_code krb_err = 0; krb5_enctype enctype; krb5_data pass_data = {0}; krb5_data salt_data = {0}; memset(&enctype, 0, sizeof(enctype)); pass_data.data = pass; pass_data.length = (int) strlen(pass); salt_data.data = salt; salt_data.length = (int) strlen(salt); #if 0 /* Prefer to use this, as it takes ENCTYPE_AES256_CTS_HMAC_SHA1_96 */ enctype = find_enctype(enc_keytype); if (!enctype) { krb_err = EINVAL; goto error; } #else krb_err = krb5_string_to_enctype( enc_keytype, &enctype); if (krb_err) { goto error; } #endif krb_err = krb5_c_string_to_key( krb5_ctx, enctype, &pass_data, &salt_data, key); if (krb_err) { goto error; } error: return krb_err; }
krb5_error_code KRB5_CALLCONV krb5_c_crypto_length_iov(krb5_context context, krb5_enctype enctype, krb5_crypto_iov *data, size_t num_data) { size_t i; const struct krb5_keytypes *ktp; unsigned int data_length = 0, pad_length; krb5_crypto_iov *padding = NULL; /* * XXX need to rejig internal interface so we can accurately * report variable header lengths. */ ktp = find_enctype(enctype); if (ktp == NULL) return KRB5_BAD_ENCTYPE; for (i = 0; i < num_data; i++) { krb5_crypto_iov *iov = &data[i]; switch (iov->flags) { case KRB5_CRYPTO_TYPE_DATA: data_length += iov->data.length; break; case KRB5_CRYPTO_TYPE_PADDING: if (padding != NULL) return EINVAL; padding = iov; break; case KRB5_CRYPTO_TYPE_HEADER: case KRB5_CRYPTO_TYPE_TRAILER: case KRB5_CRYPTO_TYPE_CHECKSUM: iov->data.length = ktp->crypto_length(ktp, iov->flags); break; case KRB5_CRYPTO_TYPE_EMPTY: case KRB5_CRYPTO_TYPE_SIGN_ONLY: default: break; } } pad_length = krb5int_c_padding_length(ktp, data_length); if (pad_length != 0 && padding == NULL) return EINVAL; if (padding != NULL) padding->data.length = pad_length; return 0; }
krb5_error_code KRB5_CALLCONV krb5_c_padding_length(krb5_context context, krb5_enctype enctype, size_t data_length, unsigned int *pad_length) { const struct krb5_keytypes *ktp; ktp = find_enctype(enctype); if (ktp == NULL) return KRB5_BAD_ENCTYPE; *pad_length = krb5int_c_padding_length(ktp, data_length); return 0; }
krb5_error_code KRB5_CALLCONV krb5_k_encrypt(krb5_context context, krb5_key key, krb5_keyusage usage, const krb5_data *cipher_state, const krb5_data *input, krb5_enc_data *output) { const struct krb5_keytypes *ktp; krb5_crypto_iov iov[4]; krb5_error_code ret; unsigned int header_len, padding_len, trailer_len, total_len; ktp = find_enctype(key->keyblock.enctype); if (ktp == NULL) return KRB5_BAD_ENCTYPE; output->magic = KV5M_ENC_DATA; output->kvno = 0; output->enctype = key->keyblock.enctype; /* Get the lengths of the token parts and compute the total. */ header_len = ktp->crypto_length(ktp, KRB5_CRYPTO_TYPE_HEADER); padding_len = krb5int_c_padding_length(ktp, input->length); trailer_len = ktp->crypto_length(ktp, KRB5_CRYPTO_TYPE_TRAILER); total_len = header_len + input->length + padding_len + trailer_len; if (output->ciphertext.length < total_len) return KRB5_BAD_MSIZE; /* Set up the iov structures for the token parts. */ iov[0].flags = KRB5_CRYPTO_TYPE_HEADER; iov[0].data = make_data(output->ciphertext.data, header_len); iov[1].flags = KRB5_CRYPTO_TYPE_DATA; iov[1].data = make_data(output->ciphertext.data + header_len, input->length); memcpy(iov[1].data.data, input->data, input->length); iov[2].flags = KRB5_CRYPTO_TYPE_PADDING; iov[2].data = make_data(iov[1].data.data + input->length, padding_len); iov[3].flags = KRB5_CRYPTO_TYPE_TRAILER; iov[3].data = make_data(iov[2].data.data + padding_len, trailer_len); ret = ktp->encrypt(ktp, key, usage, cipher_state, iov, 4); if (ret != 0) zap(iov[1].data.data, iov[1].data.length); else output->ciphertext.length = total_len; return ret; }
krb5_error_code KRB5_CALLCONV krb5_c_string_to_key_with_params(krb5_context context, krb5_enctype enctype, const krb5_data *string, const krb5_data *salt, const krb5_data *params, krb5_keyblock *key) { krb5_error_code ret; const struct krb5_keytypes *ktp; size_t keylength; ktp = find_enctype(enctype); if (ktp == NULL) return KRB5_BAD_ENCTYPE; keylength = ktp->enc->keylength; /* * xxx AFS string2key function is indicated by a special length in * the salt in much of the code. However only the DES enctypes can * deal with this. Using s2kparams would be a much better solution. */ if (salt && salt->length == SALT_TYPE_AFS_LENGTH) { switch (enctype) { case ENCTYPE_DES_CBC_CRC: case ENCTYPE_DES_CBC_MD4: case ENCTYPE_DES_CBC_MD5: break; default: return KRB5_CRYPTO_INTERNAL; } } key->contents = malloc(keylength); if (key->contents == NULL) return ENOMEM; key->magic = KV5M_KEYBLOCK; key->enctype = enctype; key->length = keylength; ret = (*ktp->str2key)(ktp, string, salt, params, key); if (ret) { zapfree(key->contents, keylength); key->length = 0; key->contents = NULL; } 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); }
krb5_error_code KRB5_CALLCONV krb5_c_string_to_key_with_params(krb5_context context, krb5_enctype enctype, const krb5_data *string, const krb5_data *salt, const krb5_data *params, krb5_keyblock *key) { krb5_error_code ret; krb5_data empty = empty_data(); const struct krb5_keytypes *ktp; size_t keylength; ktp = find_enctype(enctype); if (ktp == NULL) return KRB5_BAD_ENCTYPE; keylength = ktp->enc->keylength; /* For compatibility with past behavior, treat a null salt as empty. */ if (salt == NULL) salt = ∅ /* Fail gracefully if someone is using the old AFS string-to-key hack. */ if (salt->length == SALT_TYPE_AFS_LENGTH) return EINVAL; key->contents = malloc(keylength); if (key->contents == NULL) return ENOMEM; key->magic = KV5M_KEYBLOCK; key->enctype = enctype; key->length = keylength; ret = (*ktp->str2key)(ktp, string, salt, params, key); if (ret) { zapfree(key->contents, keylength); key->length = 0; key->contents = NULL; } return ret; }
krb5_error_code KRB5_CALLCONV krb5_c_keyed_checksum_types(krb5_context context, krb5_enctype enctype, unsigned int *count, krb5_cksumtype **cksumtypes) { unsigned int i, c, nctypes; krb5_cksumtype *ctypes; const struct krb5_cksumtypes *ctp; const struct krb5_keytypes *ktp; *count = 0; *cksumtypes = NULL; ktp = find_enctype(enctype); if (ktp == NULL) return KRB5_BAD_ENCTYPE; nctypes = 0; for (i = 0; i < krb5int_cksumtypes_length; i++) { ctp = &krb5int_cksumtypes_list[i]; if (is_keyed_for(ctp, ktp)) nctypes++; } ctypes = malloc(nctypes * sizeof(krb5_cksumtype)); if (ctypes == NULL) return ENOMEM; c = 0; for (i = 0; i < krb5int_cksumtypes_length; i++) { ctp = &krb5int_cksumtypes_list[i]; if (is_keyed_for(ctp, ktp)) ctypes[c++] = ctp->ctype; } *count = nctypes; *cksumtypes = ctypes; return 0; }
krb5_error_code krb5int_c_combine_keys(krb5_context context, krb5_keyblock *key1, krb5_keyblock *key2, krb5_keyblock *outkey) { unsigned char *r1 = NULL, *r2 = NULL, *combined = NULL, *rnd = NULL; unsigned char *output = NULL; size_t keybytes, keylength; const struct krb5_enc_provider *enc; krb5_data input, randbits; krb5_keyblock tkeyblock; krb5_key tkey = NULL; krb5_error_code ret; const struct krb5_keytypes *ktp; krb5_boolean myalloc = FALSE; if (!enctype_ok(key1->enctype) || !enctype_ok(key2->enctype)) return KRB5_CRYPTO_INTERNAL; if (key1->length != key2->length || key1->enctype != key2->enctype) return KRB5_CRYPTO_INTERNAL; /* Find our encryption algorithm. */ ktp = find_enctype(key1->enctype); if (ktp == NULL) return KRB5_BAD_ENCTYPE; enc = ktp->enc; keybytes = enc->keybytes; keylength = enc->keylength; /* Allocate and set up buffers. */ r1 = k5alloc(keybytes, &ret); if (ret) goto cleanup; r2 = k5alloc(keybytes, &ret); if (ret) goto cleanup; rnd = k5alloc(keybytes, &ret); if (ret) goto cleanup; combined = k5alloc(keybytes * 2, &ret); if (ret) goto cleanup; output = k5alloc(keylength, &ret); if (ret) goto cleanup; /* * Get R1 and R2 (by running the input keys through the DR algorithm. * Note this is most of derive-key, but not all. */ input.length = key2->length; input.data = (char *) key2->contents; ret = dr(enc, key1, r1, &input); if (ret) goto cleanup; input.length = key1->length; input.data = (char *) key1->contents; ret = dr(enc, key2, r2, &input); if (ret) goto cleanup; /* * Concatenate the two keys together, and then run them through * n-fold to reduce them to a length appropriate for the random-to-key * operation. Note here that krb5int_nfold() takes sizes in bits, hence * the multiply by 8. */ memcpy(combined, r1, keybytes); memcpy(combined + keybytes, r2, keybytes); krb5int_nfold((keybytes * 2) * 8, combined, keybytes * 8, rnd); /* * Run the "random" bits through random-to-key to produce a encryption * key. */ randbits.length = keybytes; randbits.data = (char *) rnd; tkeyblock.length = keylength; tkeyblock.contents = output; ret = (*ktp->rand2key)(&randbits, &tkeyblock); if (ret) goto cleanup; ret = krb5_k_create_key(NULL, &tkeyblock, &tkey); if (ret) goto cleanup; /* * Run through derive-key one more time to produce the final key. * Note that the input to derive-key is the ASCII string "combine". */ input.length = 7; input.data = "combine"; /* * Just FYI: _if_ we have space here in the key, then simply use it * without modification. But if the key is blank (no allocated storage) * then allocate some memory for it. This allows programs to use one of * the existing keys as the output key, _or_ pass in a blank keyblock * for us to allocate. It's easier for us to allocate it since we already * know the crypto library internals */ if (outkey->length == 0 || outkey->contents == NULL) { outkey->contents = k5alloc(keylength, &ret); if (ret) goto cleanup; outkey->length = keylength; outkey->enctype = key1->enctype; myalloc = TRUE; } ret = krb5int_derive_keyblock(enc, tkey, outkey, &input, DERIVE_RFC3961); if (ret) { if (myalloc) { free(outkey->contents); outkey->contents = NULL; } goto cleanup; } cleanup: zapfree(r1, keybytes); zapfree(r2, keybytes); zapfree(rnd, keybytes); zapfree(combined, keybytes * 2); zapfree(output, keylength); krb5_k_free_key(NULL, tkey); return ret; }