/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2003 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
/*
* Portions of this source code were derived from Berkeley 4.3 BSD
* under license from the Regents of the University of California.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include "mt.h"
#include <stdlib.h>
#include <mp.h>
#include <rpc/key_prot.h>
#include <rpcsvc/nis_dhext.h>
#include <thread.h>
extern long random();
extern void _mp_move(MINT *, MINT *);
extern void des_setparity(char *);
static void adjust();
void __gen_dhkeys();
static MINT *MODULUS_192_0;
static mutex_t mod_192_0_lck = DEFAULTMUTEX;
static bool_t first_time = TRUE;
/*
* symbol names for the entry points into the Diffie-Hellman
* GSS mech backend routines
*/
static char dl_gen_funcname[] = "__dl_gen_dhkeys";
static char dl_gen_common_funcname[] = "__dl_gen_common_dhkeys";
/*
* Generate a seed
*/
static void
getseed(seed, seedsize, pass)
char *seed;
int seedsize;
unsigned char *pass;
{
int i;
int rseed;
struct timeval tv;
(void) gettimeofday(&tv, (struct timezone *)NULL);
rseed = tv.tv_sec + tv.tv_usec;
for (i = 0; i < 8; i++) {
rseed ^= (rseed << 8) | pass[i];
}
(void) srandom(rseed);
for (i = 0; i < seedsize; i++) {
seed[i] = (random() & 0xff) ^ pass[i % 8];
}
}
/*
* Adjust the input key so that it is 0-filled on the left
*/
static void
adjust(keyout, keyin)
char keyout[HEXKEYBYTES + 1];
char *keyin;
{
char *p;
char *s;
for (p = keyin; *p; p++)
;
for (s = keyout + HEXKEYBYTES; p >= keyin; p--, s--) {
*s = *p;
}
while (s >= keyout) {
*s-- = '0';
}
}
/*
* generate a Diffie-Hellman key-pair based on the given password.
* public and secret are buffers of size HEXKEYBYTES + 1.
*/
void
__gen_dhkeys(public, secret, pass)
char *public;
char *secret;
char *pass;
{
int i;
#define BASEBITS (8 * sizeof (short) - 1)
#define BASE (1 << BASEBITS)
MINT *pk = mp_itom(0);
MINT *sk = mp_itom(0);
MINT *tmp;
MINT *base = mp_itom(BASE/2); /* BASE won't fit in a short */
MINT *root = mp_itom(PROOT);
MINT *modulus = mp_xtom(HEXMODULUS);
unsigned short r;
unsigned short seed[KEYSIZE/BASEBITS + 1];
char *xkey;
/* multiply base by 2 to get BASE */
tmp = mp_itom(2);
mp_mult(base, tmp, base);
mp_mfree(tmp);
getseed((char *)seed, (int)sizeof (seed), (uchar_t *)pass);
for (i = 0; i < KEYSIZE/BASEBITS + 1; i++) {
r = seed[i] % ((unsigned short)BASE);
tmp = mp_itom(r);
mp_mult(sk, base, sk);
mp_madd(sk, tmp, sk);
mp_mfree(tmp);
}
tmp = mp_itom(0);
mp_mdiv(sk, modulus, tmp, sk);
mp_mfree(tmp);
mp_pow(root, sk, modulus, pk);
xkey = mp_mtox(sk);
(void) adjust(secret, xkey);
xkey = mp_mtox(pk);
(void) adjust(public, xkey);
mp_mfree(sk);
mp_mfree(base);
mp_mfree(pk);
mp_mfree(root);
mp_mfree(modulus);
}
/*
* Generic key size Diffie-Hellman key pair generation routine. For classic
* AUTH_DES, just call the current routine to handle it. Else, call the
* one in the appro GSS mech backend.
*
*/
int
__gen_dhkeys_g(char *pkey, /* out */
char *skey, /* out */
keylen_t keylen, /* in */
algtype_t algtype, /* in */
char *pass) /* in */
{
const int classic_des = keylen == 192 && algtype == 0;
if (! pkey || ! skey || ! pass)
return (0);
if (classic_des) {
__gen_dhkeys(pkey, skey, pass);
return (1);
} else {
int (*dlfp)(); /* func ptr to dynamic loaded lib */
if (dlfp = (int (*)())__nis_get_mechanism_symbol(keylen,
algtype,
dl_gen_funcname)) {
(*dlfp)(pkey, skey, pass); /* void */
return (1);
}
}
return (0);
}
/*
* Choose middle 64 bits of the common key to use as our des key, possibly
* overwriting the lower order bits by setting parity.
*
* (copied/moved) from keyserv's setkey.c for the DH extensions.
*/
int
__extractdeskey(ck, deskey)
MINT *ck;
des_block *deskey;
{
MINT *a;
short r;
int i;
short base = (1 << 8);
char *k;
a = mp_itom(0);
_mp_move(ck, a);
for (i = 0; i < ((KEYSIZE - 64) / 2) / 8; i++) {
mp_sdiv(a, base, a, &r);
}
k = deskey->c;
for (i = 0; i < 8; i++) {
mp_sdiv(a, base, a, &r);
*k++ = r;
}
mp_mfree(a);
des_setparity((char *)deskey);
return (0);
}
/*
* Set the modulus for all our 192bit (algtype=0) Diffie-Hellman operations
*/
static void
setmodulus_192_0(void)
{
(void) mutex_lock(&mod_192_0_lck);
if (first_time) {
first_time = FALSE;
MODULUS_192_0 = mp_xtom(HEXMODULUS);
}
(void) mutex_unlock(&mod_192_0_lck);
}
/*
* Generic key size Diffie-Hellman common key generation routine.
* For classic AUTH_DES, do it inline like it's already done in several
* places (keyserv being one place). For new long key sizes,
* call the appro GSS mech backend routine.
*
* Arg 'keynum' is the size of the 'deskeys' array. It should be a 1
* classic AUTH_DES and a 3 for new long DH keys.
*
* Returns 1 on success and 0 on err.
*/
int
__gen_common_dhkeys_g(char *xpublic, /* in */
char *xsecret, /* in */
keylen_t keylen, /* in */
algtype_t algtype, /* in */
des_block deskeys[], /* out */
keynum_t keynum) /* in */
{
const int classic_des = keylen == 192 && algtype == 0;
if (! xpublic || ! xsecret || ! deskeys)
return (0);
if (classic_des) {
MINT *common;
MINT *public;
MINT *secret;
setmodulus_192_0();
public = mp_xtom(xpublic);
secret = mp_xtom(xsecret);
common = mp_itom(0);
mp_pow(public, secret, MODULUS_192_0, common);
(void) __extractdeskey(common, &deskeys[0]);
return (1);
} else {
int (*dlfp)(); /* func ptr to dynamically loaded lib */
if (dlfp = (int (*)())__nis_get_mechanism_symbol(keylen,
algtype,
dl_gen_common_funcname)) {
/* function called will have void return value */
(*dlfp)(xpublic, xsecret, deskeys, keynum);
return (1);
}
}
return (0);
}