/* $NetBSD: crypto-arcfour.c,v 1.6 2023/06/19 21:41:44 christos Exp $ */
/*
* Copyright (c) 1997 - 2008 Kungliga Tekniska Högskolan
* (Royal Institute of Technology, Stockholm, Sweden).
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the Institute nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* ARCFOUR
*/
#include "krb5_locl.h"
static struct _krb5_key_type keytype_arcfour = {
KRB5_ENCTYPE_ARCFOUR_HMAC_MD5,
"arcfour",
128,
16,
sizeof(struct _krb5_evp_schedule),
NULL,
_krb5_evp_schedule,
_krb5_arcfour_salt,
NULL,
_krb5_evp_cleanup,
EVP_rc4
};
/*
* checksum according to section 5. of draft-brezak-win2k-krb-rc4-hmac-03.txt
*/
krb5_error_code
_krb5_HMAC_MD5_checksum(krb5_context context,
struct _krb5_key_data *key,
const void *data,
size_t len,
unsigned usage,
Checksum *result)
{
EVP_MD_CTX *m;
struct _krb5_checksum_type *c = _krb5_find_checksum (CKSUMTYPE_RSA_MD5);
const char signature[] = "signaturekey";
Checksum ksign_c;
struct _krb5_key_data ksign;
krb5_keyblock kb;
unsigned char t[4];
unsigned char tmp[16];
unsigned char ksign_c_data[16];
krb5_error_code ret;
m = EVP_MD_CTX_create();
if (m == NULL)
return krb5_enomem(context);
ksign_c.checksum.length = sizeof(ksign_c_data);
ksign_c.checksum.data = ksign_c_data;
ret = _krb5_internal_hmac(context, c, signature, sizeof(signature),
0, key, &ksign_c);
if (ret) {
EVP_MD_CTX_destroy(m);
return ret;
}
ksign.key = &kb;
kb.keyvalue = ksign_c.checksum;
EVP_DigestInit_ex(m, EVP_md5(), NULL);
t[0] = (usage >> 0) & 0xFF;
t[1] = (usage >> 8) & 0xFF;
t[2] = (usage >> 16) & 0xFF;
t[3] = (usage >> 24) & 0xFF;
EVP_DigestUpdate(m, t, 4);
EVP_DigestUpdate(m, data, len);
EVP_DigestFinal_ex (m, tmp, NULL);
EVP_MD_CTX_destroy(m);
ret = _krb5_internal_hmac(context, c, tmp, sizeof(tmp), 0, &ksign, result);
if (ret)
return ret;
return 0;
}
struct _krb5_checksum_type _krb5_checksum_hmac_md5 = {
CKSUMTYPE_HMAC_MD5,
"hmac-md5",
64,
16,
F_KEYED | F_CPROOF,
_krb5_HMAC_MD5_checksum,
NULL
};
/*
* section 6 of draft-brezak-win2k-krb-rc4-hmac-03
*
* warning: not for small children
*/
static krb5_error_code
ARCFOUR_subencrypt(krb5_context context,
struct _krb5_key_data *key,
void *data,
size_t len,
unsigned usage,
void *ivec)
{
EVP_CIPHER_CTX *ctx;
struct _krb5_checksum_type *c = _krb5_find_checksum (CKSUMTYPE_RSA_MD5);
Checksum k1_c, k2_c, k3_c, cksum;
struct _krb5_key_data ke;
krb5_keyblock kb;
unsigned char t[4];
unsigned char *cdata = data;
unsigned char k1_c_data[16], k2_c_data[16], k3_c_data[16];
krb5_error_code ret;
t[0] = (usage >> 0) & 0xFF;
t[1] = (usage >> 8) & 0xFF;
t[2] = (usage >> 16) & 0xFF;
t[3] = (usage >> 24) & 0xFF;
k1_c.checksum.length = sizeof(k1_c_data);
k1_c.checksum.data = k1_c_data;
ret = _krb5_internal_hmac(context, c, t, sizeof(t), 0, key, &k1_c);
if (ret)
krb5_abortx(context, "hmac failed");
memcpy (k2_c_data, k1_c_data, sizeof(k1_c_data));
k2_c.checksum.length = sizeof(k2_c_data);
k2_c.checksum.data = k2_c_data;
ke.key = &kb;
kb.keyvalue = k2_c.checksum;
cksum.checksum.length = 16;
cksum.checksum.data = data;
ret = _krb5_internal_hmac(context, c, cdata + 16, len - 16, 0, &ke, &cksum);
if (ret)
krb5_abortx(context, "hmac failed");
ke.key = &kb;
kb.keyvalue = k1_c.checksum;
k3_c.checksum.length = sizeof(k3_c_data);
k3_c.checksum.data = k3_c_data;
ret = _krb5_internal_hmac(context, c, data, 16, 0, &ke, &k3_c);
if (ret)
krb5_abortx(context, "hmac failed");
#if OPENSSL_VERSION_NUMBER < 0x10100000UL
EVP_CIPHER_CTX ctxst;
ctx = &ctxst;
EVP_CIPHER_CTX_init(ctx);
#else
ctx = EVP_CIPHER_CTX_new();
#endif
if (!EVP_CipherInit_ex(ctx, EVP_rc4(), NULL, k3_c.checksum.data, NULL, 1))
krb5_abortx(context, "rc4 cipher not supported");
EVP_Cipher(ctx, cdata + 16, cdata + 16, len - 16);
#if OPENSSL_VERSION_NUMBER < 0x10100000UL
EVP_CIPHER_CTX_cleanup(ctx);
#else
EVP_CIPHER_CTX_free(ctx);
#endif
memset_s(k1_c_data, sizeof(k1_c_data), 0, sizeof(k1_c_data));
memset_s(k2_c_data, sizeof(k2_c_data), 0, sizeof(k2_c_data));
memset_s(k3_c_data, sizeof(k3_c_data), 0, sizeof(k3_c_data));
return 0;
}
static krb5_error_code
ARCFOUR_subdecrypt(krb5_context context,
struct _krb5_key_data *key,
void *data,
size_t len,
unsigned usage,
void *ivec)
{
EVP_CIPHER_CTX *ctx;
struct _krb5_checksum_type *c = _krb5_find_checksum (CKSUMTYPE_RSA_MD5);
Checksum k1_c, k2_c, k3_c, cksum;
struct _krb5_key_data ke;
krb5_keyblock kb;
unsigned char t[4];
unsigned char *cdata = data;
unsigned char k1_c_data[16], k2_c_data[16], k3_c_data[16];
unsigned char cksum_data[16];
krb5_error_code ret;
t[0] = (usage >> 0) & 0xFF;
t[1] = (usage >> 8) & 0xFF;
t[2] = (usage >> 16) & 0xFF;
t[3] = (usage >> 24) & 0xFF;
k1_c.checksum.length = sizeof(k1_c_data);
k1_c.checksum.data = k1_c_data;
ret = _krb5_internal_hmac(context, c, t, sizeof(t), 0, key, &k1_c);
if (ret)
krb5_abortx(context, "hmac failed");
memcpy (k2_c_data, k1_c_data, sizeof(k1_c_data));
k2_c.checksum.length = sizeof(k2_c_data);
k2_c.checksum.data = k2_c_data;
ke.key = &kb;
kb.keyvalue = k1_c.checksum;
k3_c.checksum.length = sizeof(k3_c_data);
k3_c.checksum.data = k3_c_data;
ret = _krb5_internal_hmac(context, c, cdata, 16, 0, &ke, &k3_c);
if (ret)
krb5_abortx(context, "hmac failed");
#if OPENSSL_VERSION_NUMBER < 0x10100000UL
EVP_CIPHER_CTX ctxst;
ctx = &ctxst;
EVP_CIPHER_CTX_init(ctx);
#else
ctx = EVP_CIPHER_CTX_new();
#endif
if (!EVP_CipherInit_ex(ctx, EVP_rc4(), NULL, k3_c.checksum.data, NULL, 0))
krb5_abortx(context, "rc4 cipher not supported");
EVP_Cipher(ctx, cdata + 16, cdata + 16, len - 16);
#if OPENSSL_VERSION_NUMBER < 0x10100000UL
EVP_CIPHER_CTX_cleanup(ctx);
#else
EVP_CIPHER_CTX_free(ctx);
#endif
ke.key = &kb;
kb.keyvalue = k2_c.checksum;
cksum.checksum.length = 16;
cksum.checksum.data = cksum_data;
ret = _krb5_internal_hmac(context, c, cdata + 16, len - 16, 0, &ke, &cksum);
if (ret)
krb5_abortx(context, "hmac failed");
memset_s(k1_c_data, sizeof(k1_c_data), 0, sizeof(k1_c_data));
memset_s(k2_c_data, sizeof(k2_c_data), 0, sizeof(k2_c_data));
memset_s(k3_c_data, sizeof(k3_c_data), 0, sizeof(k3_c_data));
if (ct_memcmp (cksum.checksum.data, data, 16) != 0) {
krb5_clear_error_message (context);
return KRB5KRB_AP_ERR_BAD_INTEGRITY;
} else {
return 0;
}
}
/*
* convert the usage numbers used in
* draft-ietf-cat-kerb-key-derivation-00.txt to the ones in
* draft-brezak-win2k-krb-rc4-hmac-04.txt
*/
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
_krb5_usage2arcfour(krb5_context context, unsigned *usage)
{
switch (*usage) {
case KRB5_KU_AS_REP_ENC_PART : /* 3 */
*usage = 8;
return 0;
case KRB5_KU_USAGE_SEAL : /* 22 */
*usage = 13;
return 0;
case KRB5_KU_USAGE_SIGN : /* 23 */
*usage = 15;
return 0;
case KRB5_KU_USAGE_SEQ: /* 24 */
*usage = 0;
return 0;
default :
return 0;
}
}
static krb5_error_code
ARCFOUR_encrypt(krb5_context context,
struct _krb5_key_data *key,
void *data,
size_t len,
krb5_boolean encryptp,
int usage,
void *ivec)
{
krb5_error_code ret;
unsigned keyusage = usage;
if((ret = _krb5_usage2arcfour (context, &keyusage)) != 0)
return ret;
if (encryptp)
return ARCFOUR_subencrypt (context, key, data, len, keyusage, ivec);
else
return ARCFOUR_subdecrypt (context, key, data, len, keyusage, ivec);
}
static krb5_error_code
ARCFOUR_prf(krb5_context context,
krb5_crypto crypto,
const krb5_data *in,
krb5_data *out)
{
struct _krb5_checksum_type *c = _krb5_find_checksum(CKSUMTYPE_SHA1);
krb5_error_code ret;
Checksum res;
ret = krb5_data_alloc(out, c->checksumsize);
if (ret)
return ret;
res.checksum.data = out->data;
res.checksum.length = out->length;
ret = _krb5_internal_hmac(context, c, in->data, in->length, 0, &crypto->key, &res);
if (ret)
krb5_data_free(out);
return 0;
}
struct _krb5_encryption_type _krb5_enctype_arcfour_hmac_md5 = {
ETYPE_ARCFOUR_HMAC_MD5,
"arcfour-hmac-md5",
"rc4-hmac",
1,
1,
8,
&keytype_arcfour,
&_krb5_checksum_hmac_md5,
&_krb5_checksum_hmac_md5,
F_SPECIAL | F_WEAK,
ARCFOUR_encrypt,
0,
ARCFOUR_prf
};