
characters. this should probably be done in some arbitrarly complex way to do it properly and you would have to know what character encoding was used for the password and salt string. git-svn-id: svn://svn.h5l.se/heimdal/trunk/heimdal@7426 ec53bebd-3082-4978-b11e-865c3cabbd6b
2320 lines
53 KiB
C
2320 lines
53 KiB
C
/*
|
|
* Copyright (c) 1997, 1998, 1999 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. All advertising materials mentioning features or use of this software
|
|
* must display the following acknowledgement:
|
|
* This product includes software developed by Kungliga Tekniska
|
|
* Högskolan and its contributors.
|
|
*
|
|
* 4. 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.
|
|
*/
|
|
|
|
#include "krb5_locl.h"
|
|
RCSID("$Id$");
|
|
|
|
#undef CRYPTO_DEBUG
|
|
#ifdef CRYPTO_DEBUG
|
|
static void krb5_crypto_debug(krb5_context, int, size_t, krb5_keyblock*);
|
|
#endif
|
|
|
|
|
|
struct key_data {
|
|
krb5_keyblock *key;
|
|
krb5_data *schedule;
|
|
};
|
|
|
|
struct key_usage {
|
|
unsigned usage;
|
|
struct key_data key;
|
|
};
|
|
|
|
struct krb5_crypto_data {
|
|
struct encryption_type *et;
|
|
struct key_data key;
|
|
int num_key_usage;
|
|
struct key_usage *key_usage;
|
|
};
|
|
|
|
#define CRYPTO_ETYPE(C) ((C)->et->type)
|
|
|
|
/* bits for `flags' below */
|
|
#define F_KEYED 1 /* checksum is keyed */
|
|
#define F_CPROOF 2 /* checksum is collision proof */
|
|
#define F_DERIVED 4 /* uses derived keys */
|
|
#define F_VARIANT 8 /* uses `variant' keys (6.4.3) */
|
|
#define F_PSEUDO 16 /* not a real protocol type */
|
|
|
|
struct salt_type {
|
|
krb5_salttype type;
|
|
const char *name;
|
|
krb5_error_code (*string_to_key)(krb5_context, krb5_enctype, krb5_data,
|
|
krb5_salt, krb5_keyblock*);
|
|
};
|
|
|
|
struct key_type {
|
|
krb5_keytype type; /* XXX */
|
|
const char *name;
|
|
size_t bits;
|
|
size_t size;
|
|
size_t schedule_size;
|
|
#if 0
|
|
krb5_enctype best_etype;
|
|
#endif
|
|
void (*random_key)(krb5_context, krb5_keyblock*);
|
|
void (*schedule)(krb5_context, struct key_data *);
|
|
struct salt_type *string_to_key;
|
|
};
|
|
|
|
struct checksum_type {
|
|
krb5_cksumtype type;
|
|
const char *name;
|
|
size_t blocksize;
|
|
size_t checksumsize;
|
|
unsigned flags;
|
|
void (*checksum)(krb5_context, struct key_data*, void*, size_t, Checksum*);
|
|
krb5_error_code (*verify)(krb5_context, struct key_data*,
|
|
void*, size_t, Checksum*);
|
|
};
|
|
|
|
struct encryption_type {
|
|
krb5_enctype type;
|
|
const char *name;
|
|
size_t blocksize;
|
|
size_t confoundersize;
|
|
struct key_type *keytype;
|
|
struct checksum_type *cksumtype;
|
|
struct checksum_type *keyed_checksum;
|
|
unsigned flags;
|
|
void (*encrypt)(struct key_data *, void *, size_t, int);
|
|
};
|
|
|
|
#define ENCRYPTION_USAGE(U) (((U) << 8) | 0xAA)
|
|
#define INTEGRITY_USAGE(U) (((U) << 8) | 0x55)
|
|
#define CHECKSUM_USAGE(U) (((U) << 8) | 0x99)
|
|
|
|
static struct checksum_type *_find_checksum(krb5_cksumtype type);
|
|
static struct encryption_type *_find_enctype(krb5_enctype type);
|
|
static struct key_type *_find_keytype(krb5_keytype type);
|
|
static krb5_error_code _get_derived_key(krb5_context, krb5_crypto,
|
|
unsigned, struct key_data**);
|
|
static struct key_data *_new_derived_key(krb5_crypto crypto, unsigned usage);
|
|
|
|
/************************************************************
|
|
* *
|
|
************************************************************/
|
|
|
|
static void
|
|
DES_random_key(krb5_context context,
|
|
krb5_keyblock *key)
|
|
{
|
|
des_cblock *k = key->keyvalue.data;
|
|
do {
|
|
krb5_generate_random_block(k, sizeof(des_cblock));
|
|
des_set_odd_parity(k);
|
|
} while(des_is_weak_key(k));
|
|
}
|
|
|
|
static void
|
|
DES_schedule(krb5_context context,
|
|
struct key_data *key)
|
|
{
|
|
des_set_key(key->key->keyvalue.data, key->schedule->data);
|
|
}
|
|
|
|
static krb5_error_code
|
|
DES_string_to_key(krb5_context context,
|
|
krb5_enctype enctype,
|
|
krb5_data password,
|
|
krb5_salt salt,
|
|
krb5_keyblock *key)
|
|
{
|
|
char *s;
|
|
size_t len;
|
|
des_cblock tmp;
|
|
|
|
len = password.length + salt.saltvalue.length + 1;
|
|
s = malloc(len);
|
|
if(s == NULL)
|
|
return ENOMEM;
|
|
memcpy(s, password.data, password.length);
|
|
memcpy(s + password.length, salt.saltvalue.data, salt.saltvalue.length);
|
|
s[len - 1] = '\0';
|
|
des_string_to_key(s, &tmp);
|
|
key->keytype = enctype;
|
|
krb5_data_copy(&key->keyvalue, tmp, sizeof(tmp));
|
|
memset(&tmp, 0, sizeof(tmp));
|
|
memset(s, 0, len);
|
|
free(s);
|
|
return 0;
|
|
}
|
|
|
|
/* This defines the Andrew string_to_key function. It accepts a password
|
|
* string as input and converts its via a one-way encryption algorithm to a DES
|
|
* encryption key. It is compatible with the original Andrew authentication
|
|
* service password database.
|
|
*/
|
|
|
|
/*
|
|
* Short passwords, i.e 8 characters or less.
|
|
*/
|
|
static void
|
|
DES_AFS3_CMU_string_to_key (krb5_data pw,
|
|
krb5_data cell,
|
|
des_cblock *key)
|
|
{
|
|
char password[8+1]; /* crypt is limited to 8 chars anyway */
|
|
int i;
|
|
|
|
for(i = 0; i < 8; i++) {
|
|
char c = ((i < pw.length) ? ((char*)pw.data)[i] : 0) ^
|
|
((i < cell.length) ? ((char*)cell.data)[i] : 0);
|
|
password[i] = c ? c : 'X';
|
|
}
|
|
password[8] = '\0';
|
|
|
|
memcpy(key, crypt(password, "#~") + 2, sizeof(des_cblock));
|
|
|
|
/* parity is inserted into the LSB so left shift each byte up one
|
|
bit. This allows ascii characters with a zero MSB to retain as
|
|
much significance as possible. */
|
|
for (i = 0; i < sizeof(des_cblock); i++)
|
|
((unsigned char*)key)[i] <<= 1;
|
|
des_set_odd_parity (key);
|
|
}
|
|
|
|
/*
|
|
* Long passwords, i.e 9 characters or more.
|
|
*/
|
|
static void
|
|
DES_AFS3_Transarc_string_to_key (krb5_data pw,
|
|
krb5_data cell,
|
|
des_cblock *key)
|
|
{
|
|
des_key_schedule schedule;
|
|
des_cblock temp_key;
|
|
des_cblock ivec;
|
|
char password[512];
|
|
size_t passlen;
|
|
|
|
memcpy(password, pw.data, min(pw.length, sizeof(password)));
|
|
if(pw.length < sizeof(password))
|
|
memcpy(password + pw.length,
|
|
cell.data, min(cell.length,
|
|
sizeof(password) - pw.length));
|
|
passlen = min(sizeof(password), pw.length + cell.length);
|
|
memcpy(&ivec, "kerberos", 8);
|
|
memcpy(&temp_key, "kerberos", 8);
|
|
des_set_odd_parity (&temp_key);
|
|
des_set_key (&temp_key, schedule);
|
|
des_cbc_cksum ((des_cblock *)password, &ivec, passlen, schedule, &ivec);
|
|
|
|
memcpy(&temp_key, &ivec, 8);
|
|
des_set_odd_parity (&temp_key);
|
|
des_set_key (&temp_key, schedule);
|
|
des_cbc_cksum ((des_cblock *)password, key, passlen, schedule, &ivec);
|
|
memset(&schedule, 0, sizeof(schedule));
|
|
memset(&temp_key, 0, sizeof(temp_key));
|
|
memset(&ivec, 0, sizeof(ivec));
|
|
memset(password, 0, sizeof(password));
|
|
|
|
des_set_odd_parity (key);
|
|
}
|
|
|
|
static krb5_error_code
|
|
DES_AFS3_string_to_key(krb5_context context,
|
|
krb5_enctype enctype,
|
|
krb5_data password,
|
|
krb5_salt salt,
|
|
krb5_keyblock *key)
|
|
{
|
|
des_cblock tmp;
|
|
if(password.length > 8)
|
|
DES_AFS3_Transarc_string_to_key(password, salt.saltvalue, &tmp);
|
|
else
|
|
DES_AFS3_CMU_string_to_key(password, salt.saltvalue, &tmp);
|
|
key->keytype = enctype;
|
|
krb5_data_copy(&key->keyvalue, tmp, sizeof(tmp));
|
|
memset(&key, 0, sizeof(key));
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
DES3_random_key(krb5_context context,
|
|
krb5_keyblock *key)
|
|
{
|
|
des_cblock *k = key->keyvalue.data;
|
|
do {
|
|
krb5_generate_random_block(k, 3 * sizeof(des_cblock));
|
|
des_set_odd_parity(&k[0]);
|
|
des_set_odd_parity(&k[1]);
|
|
des_set_odd_parity(&k[2]);
|
|
} while(des_is_weak_key(&k[0]) ||
|
|
des_is_weak_key(&k[1]) ||
|
|
des_is_weak_key(&k[2]));
|
|
}
|
|
|
|
static void
|
|
DES3_schedule(krb5_context context,
|
|
struct key_data *key)
|
|
{
|
|
des_cblock *k = key->key->keyvalue.data;
|
|
des_key_schedule *s = key->schedule->data;
|
|
des_set_key(&k[0], s[0]);
|
|
des_set_key(&k[1], s[1]);
|
|
des_set_key(&k[2], s[2]);
|
|
}
|
|
|
|
/*
|
|
* A = A xor B. A & B are 8 bytes.
|
|
*/
|
|
|
|
static void
|
|
xor (des_cblock *key, const unsigned char *b)
|
|
{
|
|
unsigned char *a = (unsigned char*)key;
|
|
a[0] ^= b[0];
|
|
a[1] ^= b[1];
|
|
a[2] ^= b[2];
|
|
a[3] ^= b[3];
|
|
a[4] ^= b[4];
|
|
a[5] ^= b[5];
|
|
a[6] ^= b[6];
|
|
a[7] ^= b[7];
|
|
}
|
|
|
|
static krb5_error_code
|
|
DES3_string_to_key(krb5_context context,
|
|
krb5_enctype enctype,
|
|
krb5_data password,
|
|
krb5_salt salt,
|
|
krb5_keyblock *key)
|
|
{
|
|
char *str;
|
|
size_t len;
|
|
unsigned char tmp[24];
|
|
des_cblock keys[3];
|
|
|
|
len = password.length + salt.saltvalue.length;
|
|
str = malloc(len);
|
|
if(len != 0 && str == NULL)
|
|
return ENOMEM;
|
|
memcpy(str, password.data, password.length);
|
|
memcpy(str + password.length, salt.saltvalue.data, salt.saltvalue.length);
|
|
{
|
|
des_cblock ivec;
|
|
des_key_schedule s[3];
|
|
int i;
|
|
|
|
_krb5_n_fold(str, len, tmp, 24);
|
|
|
|
for(i = 0; i < 3; i++){
|
|
memcpy(keys + i, tmp + i * 8, sizeof(keys[i]));
|
|
des_set_odd_parity(keys + i);
|
|
if(des_is_weak_key(keys + i))
|
|
xor(keys + i, (unsigned char*)"\0\0\0\0\0\0\0\xf0");
|
|
des_set_key(keys + i, s[i]);
|
|
}
|
|
memset(&ivec, 0, sizeof(ivec));
|
|
des_ede3_cbc_encrypt((void*)tmp, (void*)tmp, sizeof(tmp),
|
|
s[0], s[1], s[2], &ivec, DES_ENCRYPT);
|
|
memset(s, 0, sizeof(s));
|
|
memset(&ivec, 0, sizeof(ivec));
|
|
for(i = 0; i < 3; i++){
|
|
memcpy(keys + i, tmp + i * 8, sizeof(keys[i]));
|
|
des_set_odd_parity(keys + i);
|
|
if(des_is_weak_key(keys + i))
|
|
xor(keys + i, (unsigned char*)"\0\0\0\0\0\0\0\xf0");
|
|
}
|
|
memset(tmp, 0, sizeof(tmp));
|
|
}
|
|
key->keytype = enctype;
|
|
krb5_data_copy(&key->keyvalue, keys, sizeof(keys));
|
|
memset(keys, 0, sizeof(keys));
|
|
memset(str, 0, len);
|
|
free(str);
|
|
return 0;
|
|
}
|
|
|
|
static krb5_error_code
|
|
DES3_string_to_key_derived(krb5_context context,
|
|
krb5_enctype enctype,
|
|
krb5_data password,
|
|
krb5_salt salt,
|
|
krb5_keyblock *key)
|
|
{
|
|
krb5_error_code ret;
|
|
size_t len = password.length + salt.saltvalue.length;
|
|
char *s;
|
|
|
|
s = malloc(len);
|
|
if(len != 0 && s == NULL)
|
|
return ENOMEM;
|
|
memcpy(s, password.data, password.length);
|
|
memcpy(s + password.length, salt.saltvalue.data, salt.saltvalue.length);
|
|
ret = krb5_string_to_key_derived(context,
|
|
s,
|
|
len,
|
|
enctype,
|
|
key);
|
|
memset(s, 0, len);
|
|
free(s);
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* ARCFOUR
|
|
*/
|
|
|
|
static void
|
|
ARCFOUR_random_key(krb5_context context, krb5_keyblock *key)
|
|
{
|
|
krb5_generate_random_block (key->keyvalue.data,
|
|
key->keyvalue.length);
|
|
}
|
|
|
|
static void
|
|
ARCFOUR_schedule(krb5_context context, struct key_data *kd)
|
|
{
|
|
ARCFOUR_set_key (kd->schedule->data,
|
|
kd->key->keyvalue.length, kd->key->keyvalue.data);
|
|
}
|
|
|
|
static krb5_error_code
|
|
ARCFOUR_string_to_key(krb5_context context,
|
|
krb5_enctype enctype,
|
|
krb5_data password,
|
|
krb5_data salt,
|
|
krb5_keyblock *key)
|
|
{
|
|
char *s, *p;
|
|
size_t len;
|
|
int i;
|
|
struct md4 m;
|
|
|
|
len = 2 * (password.length + salt.length);
|
|
s = malloc (len);
|
|
if (len != 0 && s == NULL)
|
|
return ENOMEM;
|
|
for (p = s, i = 0; i < password.length; ++i) {
|
|
*p++ = ((char *)password.data)[i];
|
|
*p++ = 0;
|
|
}
|
|
for (i = 0; i < salt.length; ++i) {
|
|
*p++ = ((char *)salt.data)[i];
|
|
*p++ = 0;
|
|
}
|
|
md4_init(&m);
|
|
md4_update(&m, s, len);
|
|
key->keytype = enctype;
|
|
krb5_data_alloc (&key->keyvalue, 16);
|
|
md4_finito(&m, key->keyvalue.data);
|
|
memset (s, 0, len);
|
|
free (s);
|
|
return 0;
|
|
}
|
|
|
|
extern struct salt_type des_salt[],
|
|
des3_salt[], des3_salt_derived[], arcfour_salt[];
|
|
|
|
struct key_type keytype_null = {
|
|
KEYTYPE_NULL,
|
|
"null",
|
|
0,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
NULL
|
|
};
|
|
|
|
struct key_type keytype_des = {
|
|
KEYTYPE_DES,
|
|
"des",
|
|
56,
|
|
sizeof(des_cblock),
|
|
sizeof(des_key_schedule),
|
|
DES_random_key,
|
|
DES_schedule,
|
|
des_salt
|
|
};
|
|
|
|
struct key_type keytype_des3 = {
|
|
KEYTYPE_DES3,
|
|
"des3",
|
|
168,
|
|
3 * sizeof(des_cblock),
|
|
3 * sizeof(des_key_schedule),
|
|
DES3_random_key,
|
|
DES3_schedule,
|
|
des3_salt
|
|
};
|
|
|
|
struct key_type keytype_des3_derived = {
|
|
KEYTYPE_DES3,
|
|
"des3",
|
|
168,
|
|
3 * sizeof(des_cblock),
|
|
3 * sizeof(des_key_schedule),
|
|
DES3_random_key,
|
|
DES3_schedule,
|
|
des3_salt_derived
|
|
};
|
|
|
|
struct key_type keytype_arcfour = {
|
|
KEYTYPE_ARCFOUR,
|
|
"arcfour",
|
|
128,
|
|
16,
|
|
sizeof(RC4_KEY),
|
|
ARCFOUR_random_key,
|
|
ARCFOUR_schedule,
|
|
arcfour_salt
|
|
};
|
|
|
|
struct key_type *keytypes[] = {
|
|
&keytype_null,
|
|
&keytype_des,
|
|
&keytype_des3_derived,
|
|
&keytype_des3,
|
|
&keytype_arcfour
|
|
};
|
|
|
|
static int num_keytypes = sizeof(keytypes) / sizeof(keytypes[0]);
|
|
|
|
static struct key_type *
|
|
_find_keytype(krb5_keytype type)
|
|
{
|
|
int i;
|
|
for(i = 0; i < num_keytypes; i++)
|
|
if(keytypes[i]->type == type)
|
|
return keytypes[i];
|
|
return NULL;
|
|
}
|
|
|
|
|
|
struct salt_type des_salt[] = {
|
|
{
|
|
KRB5_PW_SALT,
|
|
"pw-salt",
|
|
DES_string_to_key
|
|
},
|
|
{
|
|
KRB5_AFS3_SALT,
|
|
"afs3-salt",
|
|
DES_AFS3_string_to_key
|
|
},
|
|
{ 0 }
|
|
};
|
|
|
|
struct salt_type des3_salt[] = {
|
|
{
|
|
KRB5_PW_SALT,
|
|
"pw-salt",
|
|
DES3_string_to_key
|
|
},
|
|
{ 0 }
|
|
};
|
|
|
|
struct salt_type des3_salt_derived[] = {
|
|
{
|
|
KRB5_PW_SALT,
|
|
"pw-salt",
|
|
DES3_string_to_key_derived
|
|
},
|
|
{ 0 }
|
|
};
|
|
|
|
struct salt_type arcfour_salt[] = {
|
|
{
|
|
KRB5_PW_SALT,
|
|
"pw-salt",
|
|
ARCFOUR_string_to_key
|
|
},
|
|
{ 0 }
|
|
};
|
|
|
|
krb5_error_code
|
|
krb5_salttype_to_string (krb5_context context,
|
|
krb5_enctype etype,
|
|
krb5_salttype stype,
|
|
char **string)
|
|
{
|
|
struct encryption_type *e;
|
|
struct salt_type *st;
|
|
|
|
e = _find_enctype (etype);
|
|
if (e == NULL)
|
|
return KRB5_PROG_ETYPE_NOSUPP;
|
|
for (st = e->keytype->string_to_key; st && st->type; st++) {
|
|
if (st->type == stype) {
|
|
*string = strdup (st->name);
|
|
if (*string == NULL)
|
|
return ENOMEM;
|
|
return 0;
|
|
}
|
|
}
|
|
return HEIM_ERR_SALTTYPE_NOSUPP;
|
|
}
|
|
|
|
krb5_error_code
|
|
krb5_string_to_salttype (krb5_context context,
|
|
krb5_enctype etype,
|
|
const char *string,
|
|
krb5_salttype *salttype)
|
|
{
|
|
struct encryption_type *e;
|
|
struct salt_type *st;
|
|
|
|
e = _find_enctype (etype);
|
|
if (e == NULL)
|
|
return KRB5_PROG_ETYPE_NOSUPP;
|
|
for (st = e->keytype->string_to_key; st && st->type; st++) {
|
|
if (strcasecmp (st->name, string) == 0) {
|
|
*salttype = st->type;
|
|
return 0;
|
|
}
|
|
}
|
|
return HEIM_ERR_SALTTYPE_NOSUPP;
|
|
}
|
|
|
|
krb5_error_code
|
|
krb5_get_pw_salt(krb5_context context,
|
|
krb5_const_principal principal,
|
|
krb5_salt *salt)
|
|
{
|
|
size_t len;
|
|
int i;
|
|
krb5_error_code ret;
|
|
char *p;
|
|
|
|
salt->salttype = KRB5_PW_SALT;
|
|
len = strlen(principal->realm);
|
|
for (i = 0; i < principal->name.name_string.len; ++i)
|
|
len += strlen(principal->name.name_string.val[i]);
|
|
ret = krb5_data_alloc (&salt->saltvalue, len);
|
|
if (ret)
|
|
return ret;
|
|
p = salt->saltvalue.data;
|
|
memcpy (p, principal->realm, strlen(principal->realm));
|
|
p += strlen(principal->realm);
|
|
for (i = 0; i < principal->name.name_string.len; ++i) {
|
|
memcpy (p,
|
|
principal->name.name_string.val[i],
|
|
strlen(principal->name.name_string.val[i]));
|
|
p += strlen(principal->name.name_string.val[i]);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
krb5_error_code
|
|
krb5_free_salt(krb5_context context,
|
|
krb5_salt salt)
|
|
{
|
|
krb5_data_free(&salt.saltvalue);
|
|
return 0;
|
|
}
|
|
|
|
krb5_error_code
|
|
krb5_string_to_key_data (krb5_context context,
|
|
krb5_enctype enctype,
|
|
krb5_data password,
|
|
krb5_principal principal,
|
|
krb5_keyblock *key)
|
|
{
|
|
krb5_error_code ret;
|
|
krb5_salt salt;
|
|
|
|
ret = krb5_get_pw_salt(context, principal, &salt);
|
|
if(ret)
|
|
return ret;
|
|
ret = krb5_string_to_key_data_salt(context, enctype, password, salt, key);
|
|
krb5_free_salt(context, salt);
|
|
return ret;
|
|
}
|
|
|
|
krb5_error_code
|
|
krb5_string_to_key (krb5_context context,
|
|
krb5_enctype enctype,
|
|
const char *password,
|
|
krb5_principal principal,
|
|
krb5_keyblock *key)
|
|
{
|
|
krb5_data pw;
|
|
pw.data = (void*)password;
|
|
pw.length = strlen(password);
|
|
return krb5_string_to_key_data(context, enctype, pw, principal, key);
|
|
}
|
|
|
|
krb5_error_code
|
|
krb5_string_to_key_data_salt (krb5_context context,
|
|
krb5_enctype enctype,
|
|
krb5_data password,
|
|
krb5_salt salt,
|
|
krb5_keyblock *key)
|
|
{
|
|
struct encryption_type *et =_find_enctype(enctype);
|
|
struct salt_type *st;
|
|
if(et == NULL)
|
|
return KRB5_PROG_ETYPE_NOSUPP;
|
|
for(st = et->keytype->string_to_key; st && st->type; st++)
|
|
if(st->type == salt.salttype)
|
|
return (*st->string_to_key)(context, enctype, password, salt, key);
|
|
return HEIM_ERR_SALTTYPE_NOSUPP;
|
|
}
|
|
|
|
krb5_error_code
|
|
krb5_string_to_key_salt (krb5_context context,
|
|
krb5_enctype enctype,
|
|
const char *password,
|
|
krb5_salt salt,
|
|
krb5_keyblock *key)
|
|
{
|
|
krb5_data pw;
|
|
pw.data = (void*)password;
|
|
pw.length = strlen(password);
|
|
return krb5_string_to_key_data_salt(context, enctype, pw, salt, key);
|
|
}
|
|
|
|
krb5_error_code
|
|
krb5_keytype_to_string(krb5_context context,
|
|
krb5_keytype keytype,
|
|
char **string)
|
|
{
|
|
struct key_type *kt = _find_keytype(keytype);
|
|
if(kt == NULL)
|
|
return KRB5_PROG_KEYTYPE_NOSUPP;
|
|
*string = strdup(kt->name);
|
|
if(*string == NULL)
|
|
return ENOMEM;
|
|
return 0;
|
|
}
|
|
|
|
krb5_error_code
|
|
krb5_string_to_keytype(krb5_context context,
|
|
const char *string,
|
|
krb5_keytype *keytype)
|
|
{
|
|
int i;
|
|
for(i = 0; i < num_keytypes; i++)
|
|
if(strcasecmp(keytypes[i]->name, string) == 0){
|
|
*keytype = keytypes[i]->type;
|
|
return 0;
|
|
}
|
|
return KRB5_PROG_KEYTYPE_NOSUPP;
|
|
}
|
|
|
|
krb5_error_code
|
|
krb5_generate_random_keyblock(krb5_context context,
|
|
krb5_enctype type,
|
|
krb5_keyblock *key)
|
|
{
|
|
krb5_error_code ret;
|
|
struct encryption_type *et = _find_enctype(type);
|
|
if(et == NULL)
|
|
return KRB5_PROG_ETYPE_NOSUPP;
|
|
ret = krb5_data_alloc(&key->keyvalue, et->keytype->size);
|
|
if(ret)
|
|
return ret;
|
|
key->keytype = type;
|
|
if(et->keytype->random_key)
|
|
(*et->keytype->random_key)(context, key);
|
|
else
|
|
krb5_generate_random_block(key->keyvalue.data,
|
|
key->keyvalue.length);
|
|
return 0;
|
|
}
|
|
|
|
static krb5_error_code
|
|
_key_schedule(krb5_context context,
|
|
struct key_data *key)
|
|
{
|
|
krb5_error_code ret;
|
|
struct encryption_type *et = _find_enctype(key->key->keytype);
|
|
struct key_type *kt = et->keytype;
|
|
|
|
if(kt->schedule == NULL)
|
|
return 0;
|
|
ALLOC(key->schedule, 1);
|
|
if(key->schedule == NULL)
|
|
return ENOMEM;
|
|
ret = krb5_data_alloc(key->schedule, kt->schedule_size);
|
|
if(ret) {
|
|
free(key->schedule);
|
|
key->schedule = NULL;
|
|
return ret;
|
|
}
|
|
(*kt->schedule)(context, key);
|
|
return 0;
|
|
}
|
|
|
|
/************************************************************
|
|
* *
|
|
************************************************************/
|
|
|
|
static void
|
|
NONE_checksum(krb5_context context,
|
|
struct key_data *key,
|
|
void *data,
|
|
size_t len,
|
|
Checksum *C)
|
|
{
|
|
}
|
|
|
|
static void
|
|
CRC32_checksum(krb5_context context,
|
|
struct key_data *key,
|
|
void *data,
|
|
size_t len,
|
|
Checksum *C)
|
|
{
|
|
u_int32_t crc;
|
|
unsigned char *r = C->checksum.data;
|
|
_krb5_crc_init_table ();
|
|
crc = _krb5_crc_update (data, len, 0);
|
|
r[0] = crc & 0xff;
|
|
r[1] = (crc >> 8) & 0xff;
|
|
r[2] = (crc >> 16) & 0xff;
|
|
r[3] = (crc >> 24) & 0xff;
|
|
}
|
|
|
|
static void
|
|
RSA_MD4_checksum(krb5_context context,
|
|
struct key_data *key,
|
|
void *data,
|
|
size_t len,
|
|
Checksum *C)
|
|
{
|
|
struct md4 m;
|
|
md4_init(&m);
|
|
md4_update(&m, data, len);
|
|
md4_finito(&m, C->checksum.data);
|
|
}
|
|
|
|
static void
|
|
RSA_MD4_DES_checksum(krb5_context context,
|
|
struct key_data *key,
|
|
void *data,
|
|
size_t len,
|
|
Checksum *cksum)
|
|
{
|
|
struct md4 md4;
|
|
des_cblock ivec;
|
|
unsigned char *p = cksum->checksum.data;
|
|
|
|
krb5_generate_random_block(p, 8);
|
|
md4_init(&md4);
|
|
md4_update(&md4, p, 8);
|
|
md4_update(&md4, data, len);
|
|
md4_finito(&md4, p + 8);
|
|
memset (&ivec, 0, sizeof(ivec));
|
|
des_cbc_encrypt((des_cblock*)p,
|
|
(des_cblock*)p,
|
|
24,
|
|
key->schedule->data,
|
|
&ivec,
|
|
DES_ENCRYPT);
|
|
}
|
|
|
|
static krb5_error_code
|
|
RSA_MD4_DES_verify(krb5_context context,
|
|
struct key_data *key,
|
|
void *data,
|
|
size_t len,
|
|
Checksum *C)
|
|
{
|
|
struct md4 md4;
|
|
unsigned char tmp[24];
|
|
unsigned char res[16];
|
|
des_cblock ivec;
|
|
krb5_error_code ret = 0;
|
|
|
|
memset(&ivec, 0, sizeof(ivec));
|
|
des_cbc_encrypt(C->checksum.data,
|
|
(void*)tmp,
|
|
C->checksum.length,
|
|
key->schedule->data,
|
|
&ivec,
|
|
DES_DECRYPT);
|
|
md4_init(&md4);
|
|
md4_update(&md4, tmp, 8); /* confounder */
|
|
md4_update(&md4, data, len);
|
|
md4_finito(&md4, res);
|
|
if(memcmp(res, tmp + 8, sizeof(res)) != 0)
|
|
ret = KRB5KRB_AP_ERR_BAD_INTEGRITY;
|
|
memset(tmp, 0, sizeof(tmp));
|
|
memset(res, 0, sizeof(res));
|
|
return ret;
|
|
}
|
|
|
|
static void
|
|
RSA_MD5_checksum(krb5_context context,
|
|
struct key_data *key,
|
|
void *data,
|
|
size_t len,
|
|
Checksum *C)
|
|
{
|
|
struct md5 m;
|
|
md5_init(&m);
|
|
md5_update(&m, data, len);
|
|
md5_finito(&m, C->checksum.data);
|
|
}
|
|
|
|
static void
|
|
RSA_MD5_DES_checksum(krb5_context context,
|
|
struct key_data *key,
|
|
void *data,
|
|
size_t len,
|
|
Checksum *C)
|
|
{
|
|
struct md5 md5;
|
|
des_cblock ivec;
|
|
unsigned char *p = C->checksum.data;
|
|
|
|
krb5_generate_random_block(p, 8);
|
|
md5_init(&md5);
|
|
md5_update(&md5, p, 8);
|
|
md5_update(&md5, data, len);
|
|
md5_finito(&md5, p + 8);
|
|
memset (&ivec, 0, sizeof(ivec));
|
|
des_cbc_encrypt((des_cblock*)p,
|
|
(des_cblock*)p,
|
|
24,
|
|
key->schedule->data,
|
|
&ivec,
|
|
DES_ENCRYPT);
|
|
}
|
|
|
|
static krb5_error_code
|
|
RSA_MD5_DES_verify(krb5_context context,
|
|
struct key_data *key,
|
|
void *data,
|
|
size_t len,
|
|
Checksum *C)
|
|
{
|
|
struct md5 md5;
|
|
unsigned char tmp[24];
|
|
unsigned char res[16];
|
|
des_cblock ivec;
|
|
des_key_schedule *sched = key->schedule->data;
|
|
krb5_error_code ret = 0;
|
|
|
|
memset(&ivec, 0, sizeof(ivec));
|
|
des_cbc_encrypt(C->checksum.data,
|
|
(void*)tmp,
|
|
C->checksum.length,
|
|
sched[0],
|
|
&ivec,
|
|
DES_DECRYPT);
|
|
md5_init(&md5);
|
|
md5_update(&md5, tmp, 8); /* confounder */
|
|
md5_update(&md5, data, len);
|
|
md5_finito(&md5, res);
|
|
if(memcmp(res, tmp + 8, sizeof(res)) != 0)
|
|
ret = KRB5KRB_AP_ERR_BAD_INTEGRITY;
|
|
memset(tmp, 0, sizeof(tmp));
|
|
memset(res, 0, sizeof(res));
|
|
return ret;
|
|
}
|
|
|
|
static void
|
|
RSA_MD5_DES3_checksum(krb5_context context,
|
|
struct key_data *key,
|
|
void *data,
|
|
size_t len,
|
|
Checksum *C)
|
|
{
|
|
struct md5 md5;
|
|
des_cblock ivec;
|
|
unsigned char *p = C->checksum.data;
|
|
des_key_schedule *sched = key->schedule->data;
|
|
|
|
krb5_generate_random_block(p, 8);
|
|
md5_init(&md5);
|
|
md5_update(&md5, p, 8);
|
|
md5_update(&md5, data, len);
|
|
md5_finito(&md5, p + 8);
|
|
memset (&ivec, 0, sizeof(ivec));
|
|
des_ede3_cbc_encrypt((des_cblock*)p,
|
|
(des_cblock*)p,
|
|
24,
|
|
sched[0], sched[1], sched[2],
|
|
&ivec,
|
|
DES_ENCRYPT);
|
|
}
|
|
|
|
static krb5_error_code
|
|
RSA_MD5_DES3_verify(krb5_context context,
|
|
struct key_data *key,
|
|
void *data,
|
|
size_t len,
|
|
Checksum *C)
|
|
{
|
|
struct md5 md5;
|
|
unsigned char tmp[24];
|
|
unsigned char res[16];
|
|
des_cblock ivec;
|
|
des_key_schedule *sched = key->schedule->data;
|
|
krb5_error_code ret = 0;
|
|
|
|
memset(&ivec, 0, sizeof(ivec));
|
|
des_ede3_cbc_encrypt(C->checksum.data,
|
|
(void*)tmp,
|
|
C->checksum.length,
|
|
sched[0], sched[1], sched[2],
|
|
&ivec,
|
|
DES_DECRYPT);
|
|
md5_init(&md5);
|
|
md5_update(&md5, tmp, 8); /* confounder */
|
|
md5_update(&md5, data, len);
|
|
md5_finito(&md5, res);
|
|
if(memcmp(res, tmp + 8, sizeof(res)) != 0)
|
|
ret = KRB5KRB_AP_ERR_BAD_INTEGRITY;
|
|
memset(tmp, 0, sizeof(tmp));
|
|
memset(res, 0, sizeof(res));
|
|
return ret;
|
|
}
|
|
|
|
static void
|
|
SHA1_checksum(krb5_context context,
|
|
struct key_data *key,
|
|
void *data,
|
|
size_t len,
|
|
Checksum *C)
|
|
{
|
|
struct sha m;
|
|
sha_init(&m);
|
|
sha_update(&m, data, len);
|
|
sha_finito(&m, C->checksum.data);
|
|
}
|
|
|
|
/* HMAC according to RFC2104 */
|
|
static void
|
|
hmac(krb5_context context,
|
|
struct checksum_type *cm,
|
|
void *data,
|
|
size_t len,
|
|
struct key_data *keyblock,
|
|
Checksum *result)
|
|
{
|
|
unsigned char *ipad, *opad;
|
|
unsigned char *key;
|
|
size_t key_len;
|
|
int i;
|
|
|
|
if(keyblock->key->keyvalue.length > cm->blocksize){
|
|
(*cm->checksum)(context,
|
|
keyblock,
|
|
keyblock->key->keyvalue.data,
|
|
keyblock->key->keyvalue.length,
|
|
result);
|
|
key = result->checksum.data;
|
|
key_len = result->checksum.length;
|
|
} else {
|
|
key = keyblock->key->keyvalue.data;
|
|
key_len = keyblock->key->keyvalue.length;
|
|
}
|
|
ipad = malloc(cm->blocksize + len);
|
|
opad = malloc(cm->blocksize + cm->checksumsize);
|
|
memset(ipad, 0x36, cm->blocksize);
|
|
memset(opad, 0x5c, cm->blocksize);
|
|
for(i = 0; i < key_len; i++){
|
|
ipad[i] ^= key[i];
|
|
opad[i] ^= key[i];
|
|
}
|
|
memcpy(ipad + cm->blocksize, data, len);
|
|
(*cm->checksum)(context, keyblock, ipad, cm->blocksize + len, result);
|
|
memcpy(opad + cm->blocksize, result->checksum.data,
|
|
result->checksum.length);
|
|
(*cm->checksum)(context, keyblock, opad,
|
|
cm->blocksize + cm->checksumsize, result);
|
|
memset(ipad, 0, cm->blocksize + len);
|
|
free(ipad);
|
|
memset(opad, 0, cm->blocksize + cm->checksumsize);
|
|
free(opad);
|
|
}
|
|
|
|
static void
|
|
HMAC_SHA1_DES3_checksum(krb5_context context,
|
|
struct key_data *key,
|
|
void *data,
|
|
size_t len,
|
|
Checksum *result)
|
|
{
|
|
struct checksum_type *c = _find_checksum(CKSUMTYPE_SHA1);
|
|
|
|
hmac(context, c, data, len, key, result);
|
|
}
|
|
|
|
struct checksum_type checksum_none = {
|
|
CKSUMTYPE_NONE,
|
|
"none",
|
|
1,
|
|
0,
|
|
0,
|
|
NONE_checksum,
|
|
NULL
|
|
};
|
|
struct checksum_type checksum_crc32 = {
|
|
CKSUMTYPE_CRC32,
|
|
"crc32",
|
|
1,
|
|
4,
|
|
0,
|
|
CRC32_checksum,
|
|
NULL
|
|
};
|
|
struct checksum_type checksum_rsa_md4 = {
|
|
CKSUMTYPE_RSA_MD4,
|
|
"rsa-md4",
|
|
64,
|
|
16,
|
|
F_CPROOF,
|
|
RSA_MD4_checksum,
|
|
NULL
|
|
};
|
|
struct checksum_type checksum_rsa_md4_des = {
|
|
CKSUMTYPE_RSA_MD4_DES,
|
|
"rsa-md4-des",
|
|
64,
|
|
24,
|
|
F_KEYED | F_CPROOF | F_VARIANT,
|
|
RSA_MD4_DES_checksum,
|
|
RSA_MD4_DES_verify
|
|
};
|
|
#if 0
|
|
struct checksum_type checksum_des_mac = {
|
|
CKSUMTYPE_DES_MAC,
|
|
"des-mac",
|
|
0,
|
|
0,
|
|
0,
|
|
DES_MAC_checksum,
|
|
};
|
|
struct checksum_type checksum_des_mac_k = {
|
|
CKSUMTYPE_DES_MAC_K,
|
|
"des-mac-k",
|
|
0,
|
|
0,
|
|
0,
|
|
DES_MAC_K_checksum,
|
|
};
|
|
struct checksum_type checksum_rsa_md4_des_k = {
|
|
CKSUMTYPE_RSA_MD4_DES_K,
|
|
"rsa-md4-des-k",
|
|
0,
|
|
0,
|
|
0,
|
|
RSA_MD4_DES_K_checksum,
|
|
RSA_MD4_DES_K_verify,
|
|
};
|
|
#endif
|
|
struct checksum_type checksum_rsa_md5 = {
|
|
CKSUMTYPE_RSA_MD5,
|
|
"rsa-md5",
|
|
64,
|
|
16,
|
|
F_CPROOF,
|
|
RSA_MD5_checksum,
|
|
NULL
|
|
};
|
|
struct checksum_type checksum_rsa_md5_des = {
|
|
CKSUMTYPE_RSA_MD5_DES,
|
|
"rsa-md5-des",
|
|
64,
|
|
24,
|
|
F_KEYED | F_CPROOF | F_VARIANT,
|
|
RSA_MD5_DES_checksum,
|
|
RSA_MD5_DES_verify,
|
|
};
|
|
struct checksum_type checksum_rsa_md5_des3 = {
|
|
CKSUMTYPE_RSA_MD5_DES3,
|
|
"rsa-md5-des3",
|
|
64,
|
|
24,
|
|
F_KEYED | F_CPROOF | F_VARIANT,
|
|
RSA_MD5_DES3_checksum,
|
|
RSA_MD5_DES3_verify,
|
|
};
|
|
struct checksum_type checksum_sha1 = {
|
|
CKSUMTYPE_SHA1,
|
|
"sha1",
|
|
64,
|
|
20,
|
|
F_CPROOF,
|
|
SHA1_checksum,
|
|
NULL
|
|
};
|
|
struct checksum_type checksum_hmac_sha1_des3 = {
|
|
CKSUMTYPE_HMAC_SHA1_DES3,
|
|
"hmac-sha1-des3",
|
|
64,
|
|
20,
|
|
F_KEYED | F_CPROOF | F_DERIVED,
|
|
HMAC_SHA1_DES3_checksum,
|
|
NULL
|
|
};
|
|
|
|
struct checksum_type *checksum_types[] = {
|
|
&checksum_none,
|
|
&checksum_crc32,
|
|
&checksum_rsa_md4,
|
|
&checksum_rsa_md4_des,
|
|
#if 0
|
|
&checksum_des_mac,
|
|
&checksum_des_mac_k,
|
|
&checksum_rsa_md4_des_k,
|
|
#endif
|
|
&checksum_rsa_md5,
|
|
&checksum_rsa_md5_des,
|
|
&checksum_rsa_md5_des3,
|
|
&checksum_sha1,
|
|
&checksum_hmac_sha1_des3
|
|
};
|
|
|
|
static int num_checksums = sizeof(checksum_types) / sizeof(checksum_types[0]);
|
|
|
|
static struct checksum_type *
|
|
_find_checksum(krb5_cksumtype type)
|
|
{
|
|
int i;
|
|
for(i = 0; i < num_checksums; i++)
|
|
if(checksum_types[i]->type == type)
|
|
return checksum_types[i];
|
|
return NULL;
|
|
}
|
|
|
|
static krb5_error_code
|
|
get_checksum_key(krb5_context context,
|
|
krb5_crypto crypto,
|
|
unsigned usage, /* not krb5_key_usage */
|
|
struct checksum_type *ct,
|
|
struct key_data **key)
|
|
{
|
|
krb5_error_code ret = 0;
|
|
|
|
if(ct->flags & F_DERIVED)
|
|
ret = _get_derived_key(context, crypto, usage, key);
|
|
else if(ct->flags & F_VARIANT) {
|
|
int i;
|
|
|
|
*key = _new_derived_key(crypto, 0xff/* KRB5_KU_RFC1510_VARIANT */);
|
|
if(*key == NULL)
|
|
return ENOMEM;
|
|
ret = krb5_copy_keyblock(context, crypto->key.key, &(*key)->key);
|
|
if(ret)
|
|
return ret;
|
|
for(i = 0; i < (*key)->key->keyvalue.length; i++)
|
|
((unsigned char*)(*key)->key->keyvalue.data)[i] ^= 0xF0;
|
|
} else {
|
|
*key = &crypto->key;
|
|
}
|
|
if(ret == 0)
|
|
ret = _key_schedule(context, *key);
|
|
return ret;
|
|
}
|
|
|
|
static krb5_error_code
|
|
do_checksum (krb5_context context,
|
|
struct checksum_type *ct,
|
|
krb5_crypto crypto,
|
|
unsigned usage,
|
|
void *data,
|
|
size_t len,
|
|
Checksum *result)
|
|
{
|
|
krb5_error_code ret;
|
|
struct key_data *dkey;
|
|
int keyed_checksum;
|
|
|
|
keyed_checksum = (ct->flags & F_KEYED) != 0;
|
|
if(keyed_checksum && crypto == NULL)
|
|
return KRB5_PROG_SUMTYPE_NOSUPP; /* XXX */
|
|
if(keyed_checksum)
|
|
ret = get_checksum_key(context, crypto, usage, ct, &dkey);
|
|
else
|
|
dkey = NULL;
|
|
result->cksumtype = ct->type;
|
|
krb5_data_alloc(&result->checksum, ct->checksumsize);
|
|
(*ct->checksum)(context, dkey, data, len, result);
|
|
return 0;
|
|
}
|
|
|
|
static krb5_error_code
|
|
create_checksum(krb5_context context,
|
|
krb5_crypto crypto,
|
|
unsigned usage, /* not krb5_key_usage */
|
|
krb5_cksumtype type, /* if crypto == NULL */
|
|
void *data,
|
|
size_t len,
|
|
Checksum *result)
|
|
{
|
|
struct checksum_type *ct;
|
|
|
|
if(crypto) {
|
|
ct = crypto->et->keyed_checksum;
|
|
if(ct == NULL)
|
|
ct = crypto->et->cksumtype;
|
|
} else
|
|
ct = _find_checksum(type);
|
|
if(ct == NULL)
|
|
return KRB5_PROG_SUMTYPE_NOSUPP;
|
|
return do_checksum (context, ct, crypto, usage, data, len, result);
|
|
}
|
|
|
|
krb5_error_code
|
|
krb5_create_checksum(krb5_context context,
|
|
krb5_crypto crypto,
|
|
unsigned usage_or_type,
|
|
void *data,
|
|
size_t len,
|
|
Checksum *result)
|
|
{
|
|
return create_checksum(context, crypto,
|
|
CHECKSUM_USAGE(usage_or_type),
|
|
usage_or_type, data, len, result);
|
|
}
|
|
|
|
static krb5_error_code
|
|
verify_checksum(krb5_context context,
|
|
krb5_crypto crypto,
|
|
unsigned usage, /* not krb5_key_usage */
|
|
void *data,
|
|
size_t len,
|
|
Checksum *cksum)
|
|
{
|
|
krb5_error_code ret;
|
|
struct key_data *dkey;
|
|
int keyed_checksum;
|
|
Checksum c;
|
|
struct checksum_type *ct;
|
|
|
|
ct = _find_checksum(cksum->cksumtype);
|
|
if(ct == NULL)
|
|
return KRB5_PROG_SUMTYPE_NOSUPP;
|
|
if(ct->checksumsize != cksum->checksum.length)
|
|
return KRB5KRB_AP_ERR_BAD_INTEGRITY; /* XXX */
|
|
keyed_checksum = (ct->flags & F_KEYED) != 0;
|
|
if(keyed_checksum && crypto == NULL)
|
|
return KRB5_PROG_SUMTYPE_NOSUPP; /* XXX */
|
|
if(keyed_checksum)
|
|
ret = get_checksum_key(context, crypto, usage, ct, &dkey);
|
|
else
|
|
dkey = NULL;
|
|
if(ct->verify)
|
|
return (*ct->verify)(context, dkey, data, len, cksum);
|
|
|
|
ret = krb5_data_alloc (&c.checksum, ct->checksumsize);
|
|
if (ret)
|
|
return ret;
|
|
|
|
(*ct->checksum)(context, dkey, data, len, &c);
|
|
|
|
if(c.checksum.length != cksum->checksum.length ||
|
|
memcmp(c.checksum.data, cksum->checksum.data, c.checksum.length))
|
|
ret = KRB5KRB_AP_ERR_BAD_INTEGRITY;
|
|
else
|
|
ret = 0;
|
|
krb5_data_free (&c.checksum);
|
|
return ret;
|
|
}
|
|
|
|
krb5_error_code
|
|
krb5_verify_checksum(krb5_context context,
|
|
krb5_crypto crypto,
|
|
krb5_key_usage usage,
|
|
void *data,
|
|
size_t len,
|
|
Checksum *cksum)
|
|
{
|
|
return verify_checksum(context, crypto,
|
|
CHECKSUM_USAGE(usage), data, len, cksum);
|
|
}
|
|
|
|
krb5_error_code
|
|
krb5_checksumsize(krb5_context context,
|
|
krb5_cksumtype type,
|
|
size_t *size)
|
|
{
|
|
struct checksum_type *ct = _find_checksum(type);
|
|
if(ct == NULL)
|
|
return KRB5_PROG_SUMTYPE_NOSUPP;
|
|
*size = ct->checksumsize;
|
|
return 0;
|
|
}
|
|
|
|
krb5_boolean
|
|
krb5_checksum_is_keyed(krb5_context context,
|
|
krb5_cksumtype type)
|
|
{
|
|
struct checksum_type *ct = _find_checksum(type);
|
|
if(ct == NULL)
|
|
return KRB5_PROG_SUMTYPE_NOSUPP;
|
|
return ct->flags & F_KEYED;
|
|
}
|
|
|
|
krb5_boolean
|
|
krb5_checksum_is_collision_proof(krb5_context context,
|
|
krb5_cksumtype type)
|
|
{
|
|
struct checksum_type *ct = _find_checksum(type);
|
|
if(ct == NULL)
|
|
return KRB5_PROG_SUMTYPE_NOSUPP;
|
|
return ct->flags & F_CPROOF;
|
|
}
|
|
|
|
/************************************************************
|
|
* *
|
|
************************************************************/
|
|
|
|
static void
|
|
NULL_encrypt(struct key_data *key,
|
|
void *data,
|
|
size_t len,
|
|
krb5_boolean encrypt)
|
|
{
|
|
}
|
|
|
|
static void
|
|
DES_CBC_encrypt_null_ivec(struct key_data *key,
|
|
void *data,
|
|
size_t len,
|
|
krb5_boolean encrypt)
|
|
{
|
|
des_cblock ivec;
|
|
des_key_schedule *s = key->schedule->data;
|
|
memset(&ivec, 0, sizeof(ivec));
|
|
des_cbc_encrypt(data, data, len, *s, &ivec, encrypt);
|
|
}
|
|
|
|
static void
|
|
DES_CBC_encrypt_key_ivec(struct key_data *key,
|
|
void *data,
|
|
size_t len,
|
|
krb5_boolean encrypt)
|
|
{
|
|
des_cblock ivec;
|
|
des_key_schedule *s = key->schedule->data;
|
|
memcpy(&ivec, key->key->keyvalue.data, sizeof(ivec));
|
|
des_cbc_encrypt(data, data, len, *s, &ivec, encrypt);
|
|
}
|
|
|
|
static void
|
|
DES3_CBC_encrypt(struct key_data *key,
|
|
void *data,
|
|
size_t len,
|
|
krb5_boolean encrypt)
|
|
{
|
|
des_cblock ivec;
|
|
des_key_schedule *s = key->schedule->data;
|
|
memset(&ivec, 0, sizeof(ivec));
|
|
des_ede3_cbc_encrypt(data, data, len, s[0], s[1], s[2], &ivec, encrypt);
|
|
}
|
|
|
|
static void
|
|
ARCFOUR_encrypt(struct key_data *key,
|
|
void *data,
|
|
size_t len,
|
|
krb5_boolean encrypt)
|
|
{
|
|
|
|
}
|
|
|
|
/*
|
|
* these should currently be in reverse preference order.
|
|
*/
|
|
|
|
static struct encryption_type etypes[] = {
|
|
{
|
|
ETYPE_NULL,
|
|
"null",
|
|
1,
|
|
0,
|
|
&keytype_null,
|
|
&checksum_none,
|
|
NULL,
|
|
0,
|
|
NULL_encrypt,
|
|
},
|
|
{
|
|
ETYPE_DES_CBC_CRC,
|
|
"des-cbc-crc",
|
|
8,
|
|
8,
|
|
&keytype_des,
|
|
&checksum_crc32,
|
|
NULL,
|
|
0,
|
|
DES_CBC_encrypt_key_ivec,
|
|
},
|
|
{
|
|
ETYPE_DES_CBC_MD4,
|
|
"des-cbc-md4",
|
|
8,
|
|
8,
|
|
&keytype_des,
|
|
&checksum_rsa_md4,
|
|
&checksum_rsa_md4_des,
|
|
0,
|
|
DES_CBC_encrypt_null_ivec,
|
|
},
|
|
{
|
|
ETYPE_DES_CBC_MD5,
|
|
"des-cbc-md5",
|
|
8,
|
|
8,
|
|
&keytype_des,
|
|
&checksum_rsa_md5,
|
|
&checksum_rsa_md5_des,
|
|
0,
|
|
DES_CBC_encrypt_null_ivec,
|
|
},
|
|
{
|
|
ETYPE_DES3_CBC_MD5,
|
|
"des3-cbc-md5",
|
|
8,
|
|
8,
|
|
&keytype_des3,
|
|
&checksum_rsa_md5,
|
|
&checksum_rsa_md5_des3,
|
|
0,
|
|
DES3_CBC_encrypt,
|
|
},
|
|
{
|
|
ETYPE_DES3_CBC_SHA1,
|
|
"des3-cbc-sha1",
|
|
8,
|
|
8,
|
|
&keytype_des3_derived,
|
|
&checksum_sha1,
|
|
&checksum_hmac_sha1_des3,
|
|
F_DERIVED,
|
|
DES3_CBC_encrypt,
|
|
},
|
|
{
|
|
ETYPE_OLD_DES3_CBC_SHA1,
|
|
"old-des3-cbc-sha1",
|
|
8,
|
|
8,
|
|
&keytype_des3,
|
|
&checksum_sha1,
|
|
&checksum_hmac_sha1_des3,
|
|
0,
|
|
DES3_CBC_encrypt,
|
|
},
|
|
{
|
|
ETYPE_DES_CBC_NONE,
|
|
"des-cbc-none",
|
|
8,
|
|
0,
|
|
&keytype_des,
|
|
&checksum_none,
|
|
NULL,
|
|
F_PSEUDO,
|
|
DES_CBC_encrypt_null_ivec,
|
|
},
|
|
{
|
|
ETYPE_DES3_CBC_NONE,
|
|
"des3-cbc-none",
|
|
8,
|
|
0,
|
|
&keytype_des3_derived,
|
|
&checksum_none,
|
|
NULL,
|
|
F_PSEUDO,
|
|
DES_CBC_encrypt_null_ivec,
|
|
},
|
|
};
|
|
|
|
static unsigned num_etypes = sizeof(etypes) / sizeof(etypes[0]);
|
|
|
|
|
|
static struct encryption_type *
|
|
_find_enctype(krb5_enctype type)
|
|
{
|
|
int i;
|
|
for(i = 0; i < num_etypes; i++)
|
|
if(etypes[i].type == type)
|
|
return &etypes[i];
|
|
return NULL;
|
|
}
|
|
|
|
|
|
krb5_error_code
|
|
krb5_enctype_to_string(krb5_context context,
|
|
krb5_enctype etype,
|
|
char **string)
|
|
{
|
|
struct encryption_type *e;
|
|
e = _find_enctype(etype);
|
|
if(e == NULL)
|
|
return KRB5_PROG_ETYPE_NOSUPP;
|
|
*string = strdup(e->name);
|
|
if(*string == NULL)
|
|
return ENOMEM;
|
|
return 0;
|
|
}
|
|
|
|
krb5_error_code
|
|
krb5_string_to_enctype(krb5_context context,
|
|
const char *string,
|
|
krb5_enctype *etype)
|
|
{
|
|
int i;
|
|
for(i = 0; i < num_etypes; i++)
|
|
if(strcasecmp(etypes[i].name, string) == 0){
|
|
*etype = etypes[i].type;
|
|
return 0;
|
|
}
|
|
return KRB5_PROG_ETYPE_NOSUPP;
|
|
}
|
|
|
|
krb5_error_code
|
|
krb5_enctype_to_keytype(krb5_context context,
|
|
krb5_enctype etype,
|
|
krb5_keytype *keytype)
|
|
{
|
|
struct encryption_type *e = _find_enctype(etype);
|
|
if(e == NULL)
|
|
return KRB5_PROG_ETYPE_NOSUPP;
|
|
*keytype = e->keytype->type; /* XXX */
|
|
return 0;
|
|
}
|
|
|
|
#if 0
|
|
krb5_error_code
|
|
krb5_keytype_to_enctype(krb5_context context,
|
|
krb5_keytype keytype,
|
|
krb5_enctype *etype)
|
|
{
|
|
struct key_type *kt = _find_keytype(keytype);
|
|
krb5_warnx(context, "krb5_keytype_to_enctype(%u)", keytype);
|
|
if(kt == NULL)
|
|
return KRB5_PROG_KEYTYPE_NOSUPP;
|
|
*etype = kt->best_etype;
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
krb5_error_code
|
|
krb5_keytype_to_enctypes (krb5_context context,
|
|
krb5_keytype keytype,
|
|
unsigned *len,
|
|
int **val)
|
|
{
|
|
int i;
|
|
unsigned n = 0;
|
|
int *ret;
|
|
|
|
for (i = num_etypes - 1; i >= 0; --i) {
|
|
if (etypes[i].keytype->type == keytype
|
|
&& !(etypes[i].flags & F_PSEUDO))
|
|
++n;
|
|
}
|
|
ret = malloc(n * sizeof(int));
|
|
if (ret == NULL && n != 0)
|
|
return ENOMEM;
|
|
n = 0;
|
|
for (i = num_etypes - 1; i >= 0; --i) {
|
|
if (etypes[i].keytype->type == keytype
|
|
&& !(etypes[i].flags & F_PSEUDO))
|
|
ret[n++] = etypes[i].type;
|
|
}
|
|
*len = n;
|
|
*val = ret;
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* First take the configured list of etypes for `keytype' if available,
|
|
* else, do `krb5_keytype_to_enctypes'.
|
|
*/
|
|
|
|
krb5_error_code
|
|
krb5_keytype_to_enctypes_default (krb5_context context,
|
|
krb5_keytype keytype,
|
|
unsigned *len,
|
|
int **val)
|
|
{
|
|
int i, n;
|
|
int *ret;
|
|
|
|
if (keytype != KEYTYPE_DES || context->etypes_des == NULL)
|
|
return krb5_keytype_to_enctypes (context, keytype, len, val);
|
|
|
|
for (n = 0; context->etypes_des[n]; ++n)
|
|
;
|
|
ret = malloc (n * sizeof(*ret));
|
|
if (ret == NULL && n != 0)
|
|
return ENOMEM;
|
|
for (i = 0; i < n; ++i)
|
|
ret[i] = context->etypes_des[i];
|
|
*len = n;
|
|
*val = ret;
|
|
return 0;
|
|
}
|
|
|
|
krb5_error_code
|
|
krb5_enctype_valid(krb5_context context,
|
|
krb5_enctype etype)
|
|
{
|
|
return _find_enctype(etype) != NULL;
|
|
}
|
|
|
|
/* if two enctypes have compatible keys */
|
|
krb5_boolean
|
|
krb5_enctypes_compatible_keys(krb5_context context,
|
|
krb5_enctype etype1,
|
|
krb5_enctype etype2)
|
|
{
|
|
struct encryption_type *e1 = _find_enctype(etype1);
|
|
struct encryption_type *e2 = _find_enctype(etype2);
|
|
return e1 != NULL && e2 != NULL && e1->keytype == e2->keytype;
|
|
}
|
|
|
|
static krb5_boolean
|
|
derived_crypto(krb5_context context,
|
|
krb5_crypto crypto)
|
|
{
|
|
return (crypto->et->flags & F_DERIVED) != 0;
|
|
}
|
|
|
|
|
|
#define CHECKSUMSIZE(C) ((C)->checksumsize)
|
|
#define CHECKSUMTYPE(C) ((C)->type)
|
|
|
|
static krb5_error_code
|
|
encrypt_internal_derived(krb5_context context,
|
|
krb5_crypto crypto,
|
|
unsigned usage,
|
|
void *data,
|
|
size_t len,
|
|
krb5_data *result)
|
|
{
|
|
size_t sz, block_sz, checksum_sz;
|
|
Checksum cksum;
|
|
unsigned char *p, *q;
|
|
krb5_error_code ret;
|
|
struct key_data *dkey;
|
|
struct encryption_type *et = crypto->et;
|
|
|
|
checksum_sz = CHECKSUMSIZE(et->keyed_checksum);
|
|
|
|
sz = et->confoundersize + /* 4 - length */ len;
|
|
block_sz = (sz + et->blocksize - 1) &~ (et->blocksize - 1); /* pad */
|
|
p = calloc(1, block_sz + checksum_sz);
|
|
if(p == NULL)
|
|
return ENOMEM;
|
|
|
|
q = p;
|
|
krb5_generate_random_block(q, et->confoundersize); /* XXX */
|
|
q += et->confoundersize;
|
|
memcpy(q, data, len);
|
|
|
|
ret = create_checksum(context,
|
|
crypto,
|
|
INTEGRITY_USAGE(usage),
|
|
0,
|
|
p,
|
|
block_sz,
|
|
&cksum);
|
|
if(ret == 0 && cksum.checksum.length != checksum_sz)
|
|
ret = KRB5_CRYPTO_INTERNAL;
|
|
if(ret) {
|
|
memset(p, 0, block_sz + checksum_sz);
|
|
free(p);
|
|
return ret;
|
|
}
|
|
memcpy(p + block_sz, cksum.checksum.data, cksum.checksum.length);
|
|
ret = _get_derived_key(context, crypto, ENCRYPTION_USAGE(usage), &dkey);
|
|
if(ret) {
|
|
memset(p, 0, block_sz + checksum_sz);
|
|
free(p);
|
|
return ret;
|
|
}
|
|
ret = _key_schedule(context, dkey);
|
|
if(ret) {
|
|
memset(p, 0, block_sz);
|
|
free(p);
|
|
return ret;
|
|
}
|
|
#ifdef CRYPTO_DEBUG
|
|
krb5_crypto_debug(context, 1, block_sz, dkey->key);
|
|
#endif
|
|
(*et->encrypt)(dkey, p, block_sz, 1);
|
|
result->data = p;
|
|
result->length = block_sz + checksum_sz;
|
|
return 0;
|
|
}
|
|
|
|
static krb5_error_code
|
|
encrypt_internal(krb5_context context,
|
|
krb5_crypto crypto,
|
|
void *data,
|
|
size_t len,
|
|
krb5_data *result)
|
|
{
|
|
size_t sz, block_sz, checksum_sz;
|
|
Checksum cksum;
|
|
unsigned char *p, *q;
|
|
krb5_error_code ret;
|
|
struct encryption_type *et = crypto->et;
|
|
|
|
checksum_sz = CHECKSUMSIZE(et->cksumtype);
|
|
|
|
sz = et->confoundersize + checksum_sz + len;
|
|
block_sz = (sz + et->blocksize - 1) &~ (et->blocksize - 1); /* pad */
|
|
p = calloc(1, block_sz);
|
|
if(p == NULL)
|
|
return ENOMEM;
|
|
|
|
q = p;
|
|
krb5_generate_random_block(q, et->confoundersize); /* XXX */
|
|
q += et->confoundersize;
|
|
memset(q, 0, checksum_sz);
|
|
q += checksum_sz;
|
|
memcpy(q, data, len);
|
|
|
|
ret = create_checksum(context,
|
|
NULL,
|
|
0,
|
|
CHECKSUMTYPE(et->cksumtype),
|
|
p,
|
|
block_sz,
|
|
&cksum);
|
|
if(ret == 0 && cksum.checksum.length != checksum_sz) {
|
|
free_Checksum (&cksum);
|
|
ret = KRB5_CRYPTO_INTERNAL;
|
|
}
|
|
if(ret) {
|
|
memset(p, 0, block_sz);
|
|
free(p);
|
|
free_Checksum(&cksum);
|
|
return ret;
|
|
}
|
|
memcpy(p + et->confoundersize, cksum.checksum.data, cksum.checksum.length);
|
|
free_Checksum(&cksum);
|
|
ret = _key_schedule(context, &crypto->key);
|
|
if(ret) {
|
|
memset(p, 0, block_sz);
|
|
free(p);
|
|
return ret;
|
|
}
|
|
#ifdef CRYPTO_DEBUG
|
|
krb5_crypto_debug(context, 1, block_sz, crypto->key.key);
|
|
#endif
|
|
(*et->encrypt)(&crypto->key, p, block_sz, 1);
|
|
result->data = p;
|
|
result->length = block_sz;
|
|
return 0;
|
|
}
|
|
|
|
static krb5_error_code
|
|
decrypt_internal_derived(krb5_context context,
|
|
krb5_crypto crypto,
|
|
unsigned usage,
|
|
void *data,
|
|
size_t len,
|
|
krb5_data *result)
|
|
{
|
|
size_t checksum_sz;
|
|
Checksum cksum;
|
|
unsigned char *p;
|
|
krb5_error_code ret;
|
|
struct key_data *dkey;
|
|
struct encryption_type *et = crypto->et;
|
|
unsigned long l;
|
|
|
|
p = malloc(len);
|
|
if(len != 0 && p == NULL)
|
|
return ENOMEM;
|
|
memcpy(p, data, len);
|
|
|
|
checksum_sz = CHECKSUMSIZE(et->keyed_checksum);
|
|
len -= checksum_sz;
|
|
|
|
ret = _get_derived_key(context, crypto, ENCRYPTION_USAGE(usage), &dkey);
|
|
if(ret) {
|
|
free(p);
|
|
return ret;
|
|
}
|
|
ret = _key_schedule(context, dkey);
|
|
if(ret) {
|
|
free(p);
|
|
return ret;
|
|
}
|
|
#ifdef CRYPTO_DEBUG
|
|
krb5_crypto_debug(context, 0, len, dkey->key);
|
|
#endif
|
|
(*et->encrypt)(dkey, p, len, 0);
|
|
|
|
cksum.checksum.data = p + len;
|
|
cksum.checksum.length = checksum_sz;
|
|
cksum.cksumtype = CHECKSUMTYPE(et->keyed_checksum);
|
|
|
|
ret = verify_checksum(context,
|
|
crypto,
|
|
INTEGRITY_USAGE(usage),
|
|
p,
|
|
len,
|
|
&cksum);
|
|
if(ret) {
|
|
free(p);
|
|
return ret;
|
|
}
|
|
l = len - et->confoundersize;
|
|
memmove(p, p + et->confoundersize, l);
|
|
result->data = realloc(p, l);
|
|
if(p == NULL) {
|
|
free(p);
|
|
return ENOMEM;
|
|
}
|
|
result->length = l;
|
|
return 0;
|
|
}
|
|
|
|
static krb5_error_code
|
|
decrypt_internal(krb5_context context,
|
|
krb5_crypto crypto,
|
|
void *data,
|
|
size_t len,
|
|
krb5_data *result)
|
|
{
|
|
krb5_error_code ret;
|
|
unsigned char *p;
|
|
Checksum cksum;
|
|
size_t checksum_sz, l;
|
|
struct encryption_type *et = crypto->et;
|
|
|
|
checksum_sz = CHECKSUMSIZE(et->cksumtype);
|
|
p = malloc(len);
|
|
if(len != 0 && p == NULL)
|
|
return ENOMEM;
|
|
memcpy(p, data, len);
|
|
|
|
ret = _key_schedule(context, &crypto->key);
|
|
if(ret) {
|
|
free(p);
|
|
return ret;
|
|
}
|
|
#ifdef CRYPTO_DEBUG
|
|
krb5_crypto_debug(context, 0, len, crypto->key.key);
|
|
#endif
|
|
(*et->encrypt)(&crypto->key, p, len, 0);
|
|
ret = krb5_data_copy(&cksum.checksum, p + et->confoundersize, checksum_sz);
|
|
if(ret) {
|
|
free(p);
|
|
return ret;
|
|
}
|
|
memset(p + et->confoundersize, 0, checksum_sz);
|
|
cksum.cksumtype = CHECKSUMTYPE(et->cksumtype);
|
|
ret = verify_checksum(context, NULL, 0, p, len, &cksum);
|
|
free_Checksum(&cksum);
|
|
if(ret) {
|
|
free(p);
|
|
return ret;
|
|
}
|
|
l = len - et->confoundersize - checksum_sz;
|
|
memmove(p, p + et->confoundersize + checksum_sz, l);
|
|
result->data = realloc(p, l);
|
|
if(result->data == NULL) {
|
|
free(p);
|
|
return ENOMEM;
|
|
}
|
|
result->length = l;
|
|
return 0;
|
|
}
|
|
|
|
krb5_error_code
|
|
krb5_encrypt(krb5_context context,
|
|
krb5_crypto crypto,
|
|
unsigned usage,
|
|
void *data,
|
|
size_t len,
|
|
krb5_data *result)
|
|
{
|
|
if(derived_crypto(context, crypto))
|
|
return encrypt_internal_derived(context, crypto, usage,
|
|
data, len, result);
|
|
else
|
|
return encrypt_internal(context, crypto, data, len, result);
|
|
}
|
|
|
|
krb5_error_code
|
|
krb5_encrypt_EncryptedData(krb5_context context,
|
|
krb5_crypto crypto,
|
|
unsigned usage,
|
|
void *data,
|
|
size_t len,
|
|
int kvno,
|
|
EncryptedData *result)
|
|
{
|
|
result->etype = CRYPTO_ETYPE(crypto);
|
|
if(kvno){
|
|
ALLOC(result->kvno, 1);
|
|
*result->kvno = kvno;
|
|
}else
|
|
result->kvno = NULL;
|
|
return krb5_encrypt(context, crypto, usage, data, len, &result->cipher);
|
|
}
|
|
|
|
krb5_error_code
|
|
krb5_decrypt(krb5_context context,
|
|
krb5_crypto crypto,
|
|
unsigned usage,
|
|
void *data,
|
|
size_t len,
|
|
krb5_data *result)
|
|
{
|
|
if(derived_crypto(context, crypto))
|
|
return decrypt_internal_derived(context, crypto, usage,
|
|
data, len, result);
|
|
else
|
|
return decrypt_internal(context, crypto, data, len, result);
|
|
}
|
|
|
|
krb5_error_code
|
|
krb5_decrypt_EncryptedData(krb5_context context,
|
|
krb5_crypto crypto,
|
|
unsigned usage,
|
|
EncryptedData *e,
|
|
krb5_data *result)
|
|
{
|
|
return krb5_decrypt(context, crypto, usage,
|
|
e->cipher.data, e->cipher.length, result);
|
|
}
|
|
|
|
/************************************************************
|
|
* *
|
|
************************************************************/
|
|
|
|
void
|
|
krb5_generate_random_block(void *buf, size_t len)
|
|
{
|
|
des_cblock key, out;
|
|
static des_cblock counter;
|
|
static des_key_schedule schedule;
|
|
int i;
|
|
static int initialized = 0;
|
|
|
|
if(!initialized) {
|
|
des_new_random_key(&key);
|
|
des_set_key(&key, schedule);
|
|
memset(&key, 0, sizeof(key));
|
|
des_new_random_key(&counter);
|
|
}
|
|
while(len > 0) {
|
|
des_ecb_encrypt(&counter, &out, schedule, DES_ENCRYPT);
|
|
for(i = 7; i >=0; i--)
|
|
if(counter[i]++)
|
|
break;
|
|
memcpy(buf, out, min(len, sizeof(out)));
|
|
len -= min(len, sizeof(out));
|
|
buf = (char*)buf + sizeof(out);
|
|
}
|
|
}
|
|
|
|
static void
|
|
DES3_postproc(krb5_context context,
|
|
unsigned char *k, size_t len, struct key_data *key)
|
|
{
|
|
unsigned char x[24];
|
|
int i, j;
|
|
|
|
memset(x, 0, sizeof(x));
|
|
for (i = 0; i < 3; ++i) {
|
|
unsigned char foo;
|
|
|
|
for (j = 0; j < 7; ++j) {
|
|
unsigned char b = k[7 * i + j];
|
|
|
|
x[8 * i + j] = b;
|
|
}
|
|
foo = 0;
|
|
for (j = 6; j >= 0; --j) {
|
|
foo |= k[7 * i + j] & 1;
|
|
foo <<= 1;
|
|
}
|
|
x[8 * i + 7] = foo;
|
|
}
|
|
k = key->key->keyvalue.data;
|
|
memcpy(k, x, 24);
|
|
memset(x, 0, sizeof(x));
|
|
if (key->schedule) {
|
|
krb5_free_data(context, key->schedule);
|
|
key->schedule = NULL;
|
|
}
|
|
des_set_odd_parity((des_cblock*)k);
|
|
des_set_odd_parity((des_cblock*)(k + 8));
|
|
des_set_odd_parity((des_cblock*)(k + 16));
|
|
}
|
|
|
|
static krb5_error_code
|
|
derive_key(krb5_context context,
|
|
struct encryption_type *et,
|
|
struct key_data *key,
|
|
void *constant,
|
|
size_t len)
|
|
{
|
|
unsigned char *k;
|
|
unsigned int nblocks = 0, i;
|
|
krb5_error_code ret = 0;
|
|
|
|
struct key_type *kt = et->keytype;
|
|
ret = _key_schedule(context, key);
|
|
if(ret)
|
|
return ret;
|
|
if(et->blocksize * 8 < kt->bits ||
|
|
len != et->blocksize) {
|
|
nblocks = (kt->bits + et->blocksize * 8 - 1) / (et->blocksize * 8);
|
|
k = malloc(nblocks * et->blocksize);
|
|
if(k == NULL)
|
|
return ENOMEM;
|
|
_krb5_n_fold(constant, len, k, et->blocksize);
|
|
for(i = 0; i < nblocks; i++) {
|
|
if(i > 0)
|
|
memcpy(k + i * et->blocksize,
|
|
k + (i - 1) * et->blocksize,
|
|
et->blocksize);
|
|
(*et->encrypt)(key, k + i * et->blocksize, et->blocksize, 1);
|
|
}
|
|
} else {
|
|
void *c = malloc(len);
|
|
size_t res_len = (kt->bits + 7) / 8;
|
|
|
|
if(len != 0 && c == NULL)
|
|
return ENOMEM;
|
|
memcpy(c, constant, len);
|
|
(*et->encrypt)(key, c, len, 1);
|
|
k = malloc(res_len);
|
|
if(res_len != 0 && k == NULL)
|
|
return ENOMEM;
|
|
_krb5_n_fold(c, len, k, res_len);
|
|
free(c);
|
|
}
|
|
|
|
/* XXX keytype dependent post-processing */
|
|
switch(kt->type) {
|
|
case KEYTYPE_DES3:
|
|
DES3_postproc(context, k, nblocks * et->blocksize, key);
|
|
break;
|
|
default:
|
|
krb5_warnx(context, "derive_key() called with unknown keytype (%u)",
|
|
kt->type);
|
|
ret = KRB5_CRYPTO_INTERNAL;
|
|
break;
|
|
}
|
|
memset(k, 0, nblocks * et->blocksize);
|
|
free(k);
|
|
return ret;
|
|
}
|
|
|
|
static struct key_data *
|
|
_new_derived_key(krb5_crypto crypto, unsigned usage)
|
|
{
|
|
struct key_usage *d = crypto->key_usage;
|
|
d = realloc(d, (crypto->num_key_usage + 1) * sizeof(*d));
|
|
if(d == NULL)
|
|
return NULL;
|
|
crypto->key_usage = d;
|
|
d += crypto->num_key_usage++;
|
|
memset(d, 0, sizeof(*d));
|
|
d->usage = usage;
|
|
return &d->key;
|
|
}
|
|
|
|
static krb5_error_code
|
|
_get_derived_key(krb5_context context,
|
|
krb5_crypto crypto,
|
|
unsigned usage,
|
|
struct key_data **key)
|
|
{
|
|
int i;
|
|
struct key_data *d;
|
|
unsigned char constant[5];
|
|
|
|
for(i = 0; i < crypto->num_key_usage; i++)
|
|
if(crypto->key_usage[i].usage == usage) {
|
|
*key = &crypto->key_usage[i].key;
|
|
return 0;
|
|
}
|
|
d = _new_derived_key(crypto, usage);
|
|
if(d == NULL)
|
|
return ENOMEM;
|
|
krb5_copy_keyblock(context, crypto->key.key, &d->key);
|
|
_krb5_put_int(constant, usage, 5);
|
|
derive_key(context, crypto->et, d, constant, sizeof(constant));
|
|
*key = d;
|
|
return 0;
|
|
}
|
|
|
|
|
|
krb5_error_code
|
|
krb5_crypto_init(krb5_context context,
|
|
krb5_keyblock *key,
|
|
krb5_enctype etype,
|
|
krb5_crypto *crypto)
|
|
{
|
|
krb5_error_code ret;
|
|
ALLOC(*crypto, 1);
|
|
if(*crypto == NULL)
|
|
return ENOMEM;
|
|
if(etype == ETYPE_NULL)
|
|
etype = key->keytype;
|
|
(*crypto)->et = _find_enctype(etype);
|
|
if((*crypto)->et == NULL) {
|
|
free(*crypto);
|
|
return KRB5_PROG_ETYPE_NOSUPP;
|
|
}
|
|
ret = krb5_copy_keyblock(context, key, &(*crypto)->key.key);
|
|
if(ret) {
|
|
free(*crypto);
|
|
return ret;
|
|
}
|
|
(*crypto)->key.schedule = NULL;
|
|
(*crypto)->num_key_usage = 0;
|
|
(*crypto)->key_usage = NULL;
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
free_key_data(krb5_context context, struct key_data *key)
|
|
{
|
|
krb5_free_keyblock(context, key->key);
|
|
if(key->schedule) {
|
|
memset(key->schedule->data, 0, key->schedule->length);
|
|
krb5_free_data(context, key->schedule);
|
|
}
|
|
}
|
|
|
|
static void
|
|
free_key_usage(krb5_context context, struct key_usage *ku)
|
|
{
|
|
free_key_data(context, &ku->key);
|
|
}
|
|
|
|
krb5_error_code
|
|
krb5_crypto_destroy(krb5_context context,
|
|
krb5_crypto crypto)
|
|
{
|
|
int i;
|
|
|
|
for(i = 0; i < crypto->num_key_usage; i++)
|
|
free_key_usage(context, &crypto->key_usage[i]);
|
|
free(crypto->key_usage);
|
|
free_key_data(context, &crypto->key);
|
|
free (crypto);
|
|
return 0;
|
|
}
|
|
|
|
krb5_error_code
|
|
krb5_string_to_key_derived(krb5_context context,
|
|
const void *str,
|
|
size_t len,
|
|
krb5_enctype etype,
|
|
krb5_keyblock *key)
|
|
{
|
|
struct encryption_type *et = _find_enctype(etype);
|
|
krb5_error_code ret;
|
|
struct key_data kd;
|
|
u_char *tmp;
|
|
|
|
if(et == NULL)
|
|
return KRB5_PROG_ETYPE_NOSUPP;
|
|
ALLOC(kd.key, 1);
|
|
kd.key->keytype = etype;
|
|
tmp = malloc (et->keytype->bits / 8);
|
|
_krb5_n_fold(str, len, tmp, et->keytype->bits / 8);
|
|
krb5_data_alloc(&kd.key->keyvalue, et->keytype->size);
|
|
kd.schedule = NULL;
|
|
DES3_postproc (context, tmp, et->keytype->bits / 8, &kd); /* XXX */
|
|
ret = derive_key(context,
|
|
et,
|
|
&kd,
|
|
"kerberos", /* XXX well known constant */
|
|
strlen("kerberos"));
|
|
ret = krb5_copy_keyblock_contents(context, kd.key, key);
|
|
free_key_data(context, &kd);
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Return the size of an encrypted packet of length `data_len'
|
|
*/
|
|
|
|
size_t
|
|
krb5_get_wrapped_length (krb5_context context,
|
|
krb5_crypto crypto,
|
|
size_t data_len)
|
|
{
|
|
struct encryption_type *et = crypto->et;
|
|
size_t blocksize = et->blocksize;
|
|
size_t res;
|
|
|
|
res = (data_len + blocksize - 1) / blocksize * blocksize;
|
|
res = res + et->confoundersize + et->cksumtype->checksumsize;
|
|
return res;
|
|
}
|
|
|
|
#ifdef CRYPTO_DEBUG
|
|
|
|
static krb5_error_code
|
|
krb5_get_keyid(krb5_context context,
|
|
krb5_keyblock *key,
|
|
u_int32_t *keyid)
|
|
{
|
|
struct md5 md5;
|
|
unsigned char tmp[16];
|
|
md5_init(&md5);
|
|
md5_update(&md5, key->keyvalue.data, key->keyvalue.length);
|
|
md5_finito(&md5, tmp);
|
|
*keyid = (tmp[12] << 24) | (tmp[13] << 16) | (tmp[14] << 8) | tmp[15];
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
krb5_crypto_debug(krb5_context context,
|
|
int encrypt,
|
|
size_t len,
|
|
krb5_keyblock *key)
|
|
{
|
|
u_int32_t keyid;
|
|
char *kt;
|
|
krb5_get_keyid(context, key, &keyid);
|
|
krb5_enctype_to_string(context, key->keytype, &kt);
|
|
krb5_warnx(context, "%s %lu bytes with key-id %#x (%s)",
|
|
encrypt ? "encrypting" : "decrypting",
|
|
(unsigned long)len,
|
|
keyid,
|
|
kt);
|
|
free(kt);
|
|
}
|
|
|
|
#endif /* CRYPTO_DEBUG */
|