Files
ncpfs/lib/ndslib.c
ncpfs archive import cf0c5e59af Import ncpfs 2.2.2
2026-04-28 20:39:59 +02:00

1831 lines
45 KiB
C

/*
NDS client for ncpfs
Copyright (C) 1997 Arne de Bruijn
Copyright (C) 1999, 2000 Petr Vandrovec
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
Revision history:
0.00 1997 Arne de Bruijn
Initial release.
1.00 1999, November 20 Petr Vandrovec <vandrove@vc.cvut.cz>
Moved file to new API.
Added NWIsDSServer, key storing functions, NWDSAuthenticateConn.
1.01 2000, April 15 Petr Vandrovec <vandrove@vc.cvut.cz>
Added new functions needed for NWDSGenerateObjectKeyPair.
1.02 2000, April 23 Petr Vandrovec <vandrove@vc.cvut.cz>
Moved nds_login to ds/setkeys.c, added __NWDSGetPrivateKey.
1.03 2000, July 6 Petr Vandrovec <vandrove@vc.cvut.cz>
Added DCV_DEREF_ALIASES into authentication functions.
1.04 2000, July 10 Gerhard Lausser <GerhardLausser@KirchPayTV.de>
Fixed strcpy_wc call in nds_login_auth.
1.05 2001, March 9 Petr Vandrovec <vandrove@vc.cvut.cz>
Added NWIsDSServerW. For now ISO-8859-1 only.
1.06 2001, May 31 Petr Vandrovec <vandrove@vc.cvut.cz>
Use correct parameter (nuint32) to GetLE32().
*/
#define RANDBUF /* if defined: read random data once from /dev/urandom */
/*#define ERR_MSG*/ /* if defined: show error messages in nds_login_auth */
/*#define DEBUG_PRINT*/
/*#define FIND_ISR */ /* if defined: show reasons for -330 invalid response */
#include "config.h"
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <unistd.h>
#include <sys/time.h>
#include <fcntl.h>
#include <ncp/ndslib.h>
#include <ncp/kernel/ipx.h>
#include <errno.h>
#include <sys/mman.h>
#include "ncplib_i.h"
#include "ndscrypt.h"
#include "nwnet_i.h"
#include "ndslib_i.h"
#define USUALS
typedef u_int32_t word32;
typedef u_int16_t word16;
typedef unsigned char boolean;
#include "mpilib.h"
#ifdef ERR_MSG
#include <stdio.h>
#include "private/libintl.h"
#define _(X) dgettext(PACKAGE, (X))
#define N_(X) (X)
#endif
#ifdef FIND_ISR
#include <stdio.h>
#define ISRPrint(X...) do { fprintf(stderr, __FILE__ ":%d: ", __LINE__); fprintf(stderr, X); } while (0)
#else
#define ISRPrint(X...) do {} while (0)
#endif
static void strcpy_cw(wchar_t *w, const char* s) {
while ((*w++ = *(const nuint8*)s++) != 0);
}
NWDSCCODE NWIsDSServer(NWCONN_HANDLE conn, char* treename) {
char buf[128];
size_t size;
NWDSCCODE err;
size_t namelen;
err = ncp_send_nds(conn, 1, "\0\0\0", 3, buf, sizeof(buf), &size);
if (err)
return 0;
if (size < 8)
return 0;
namelen = DVAL_LH(buf, 4);
if (namelen > size - 8)
return 0;
if (namelen > MAX_TREE_NAME_CHARS + 1)
return 0;
if (buf[8 + namelen - 1])
return 0;
if (treename)
memcpy(treename, buf + 8, namelen);
return 1;
}
NWDSCCODE NWIsDSServerW(NWCONN_HANDLE conn, wchar_t* treename) {
char treechar[MAX_TREE_NAME_CHARS + 1];
NWDSCCODE err;
err = NWIsDSServer(conn, treechar);
if (err && treename) {
char* treesrc = treechar;
do { } while ((*treename++ = *treesrc++ & 0xFF) != 0);
}
return err;
}
static NWDSCCODE nds_read_pk(NWDSContextHandle ctx, const wchar_t* objname,
Octet_String_T** post) {
char attrname[256];
char reply_b[DEFAULT_MESSAGE_LEN];
Buf_T bufattrname;
nuint32 iterhandle = NO_MORE_ITERATIONS;
Buf_T replybuf;
NWObjectCount cnt;
wchar_t name[MAX_DN_CHARS+1];
NWObjectCount valcnt;
enum SYNTAX synt;
size_t size;
Octet_String_T* ost;
NWDSCCODE err;
NWDSSetupBuf(&bufattrname, attrname, sizeof(attrname));
NWDSInitBuf(ctx, DSV_READ, &bufattrname);
NWDSSetupBuf(&replybuf, reply_b, sizeof(reply_b));
NWDSPutAttrName(ctx, &bufattrname, (const NWDSChar*)L"Public Key");
err = NWDSRead(ctx, (const NWDSChar*)objname, DS_ATTRIBUTE_VALUES, 0, &bufattrname, &iterhandle, &replybuf);
if (err)
return err;
err = NWDSGetAttrCount(ctx, &replybuf, &cnt);
if (err)
return err;
if (cnt != 1) {
ISRPrint("Attr count != 1 (%d)\n", cnt);
return ERR_INVALID_SERVER_RESPONSE;
}
err = NWDSGetAttrName(ctx, &replybuf, (NWDSChar*)name, &valcnt, &synt);
if (err)
return err;
if ((synt != SYN_OCTET_STRING) ||
(wcscmp(name, L"Public Key")) ||
(valcnt < 1)) {
ISRPrint("Attr type wrong (synt=%d, valcnt=%d)\n",
synt, valcnt);
return ERR_INVALID_SERVER_RESPONSE;
}
err = NWDSComputeAttrValSize(ctx, &replybuf, SYN_OCTET_STRING, &size);
if (err)
return err;
ost = (Octet_String_T*)malloc(size);
if (!ost)
return ENOMEM;
err = NWDSGetAttrVal(ctx, &replybuf, SYN_OCTET_STRING, ost);
if (err) {
free(ost);
return err;
}
*post = ost;
return 0;
}
#ifdef RANDBUF
#define RANDBUFSIZE 1236 /* total size of all fillrandom's for login+auth */
static char global_randbuf[RANDBUFSIZE];
static char *g_rndp = global_randbuf + RANDBUFSIZE;
static ncpt_mutex_t randbuflock = NCPT_MUTEX_INITIALIZER;
static void fillrandom(char *buf, int buflen) {
ncpt_mutex_lock(&randbuflock);
do {
int i;
if (g_rndp == global_randbuf + RANDBUFSIZE) {
int fh;
if ((fh = open("/dev/urandom", O_RDONLY)) >=0) {
read(fh, global_randbuf, RANDBUFSIZE);
close(fh);
} else {
g_rndp = global_randbuf;
while (g_rndp - global_randbuf < RANDBUFSIZE)
*(g_rndp++) = rand() / ((((unsigned)RAND_MAX)+255) / 256);
}
g_rndp = global_randbuf;
}
if ((i = RANDBUFSIZE - (g_rndp - global_randbuf)) > buflen) i = buflen;
memcpy(buf, g_rndp, i);
buf += i;
g_rndp += i;
buflen -= i;
} while (buflen);
ncpt_mutex_unlock(&randbuflock);
}
#else
static void fillrandom(char *buf, int buflen) {
int fh;
char *p;
if (((fh = open("/dev/urandom", O_RDONLY)) >= 0) {
read(fh, buf, buflen);
close(fh);
} else {
p = buf;
while (p - buf < buflen)
*(p++) = rand() / ((((unsigned)RAND_MAX)+255) / 256);
}
}
#endif
static int countbits_l(char *buf, int bufsize) {
unsigned char b;
while ((--bufsize) && (!buf[bufsize]));
b = (unsigned char)buf[bufsize];
bufsize <<= 3;
while (b) {
b >>= 2; bufsize++;
}
return bufsize;
}
static void copyfill(void *outbuf, int outsize, const void *inbuf, int insize) {
if (outsize < insize) insize = outsize;
memcpy(outbuf, inbuf, insize);
memset((char *)outbuf + insize, 0, outsize - insize);
}
static char keyprefix[] = {1, 0, 0, 0, 3, 0, 1, 0};
static int initkey(const char *key, char **keyptr, size_t *keylen) { /* 1=ok, 0=err */
if (!memcmp(key, keyprefix, 8)) {
if (keylen) *keylen = WVAL_LH(key, 8);
if (keyptr) (const char *)(*keyptr) = key + 10;
return 1;
} else
return 0;
}
static void clearkey(char *key) {
char *keyptr;
size_t keylen;
if (initkey(key, &keyptr, &keylen))
memset(key, 0, keylen + 10);
}
static int findchunk(const char *keyptr, int keylen, const char *chunk,
char **chunkptr) {
const char *p;
if ((p = keyptr)) {
while (p - keyptr < keylen) {
if ((p[0] != chunk[0]) || (p[1] != chunk[1]))
p += 4 + (unsigned char)p[2] + (unsigned char)p[3];
else {
if (chunkptr) (const char *)(*chunkptr) = p + 4;
return (unsigned char)p[2] + (unsigned char)p[3];
}
}
}
if (chunkptr) *chunkptr = NULL;
return 0;
}
#ifdef DEBUG_PRINT
static void printnumber(const char* m, const unit* p) {
int i;
printf("%s: ", m);
for (i = global_precision; i--; ) {
printf("%02X", p[i]);
if ((i & 1) == 0)
printf(" ");
}
printf("\n");
}
static void dumpkey(const nuint8 *key, size_t keylen) {
while (keylen > 8 + 4) {
size_t kl = WVAL_LH(key, 2);
const nuint8 *k2;
nuint16 type = WVAL_HL(key, 0);
size_t kl2 = kl;
printf("El %c%c(%u): ", key[0], key[1], kl);
key += 4;
keylen -= kl + 4;
k2 = key + kl;
while (kl--) {
printf("%02X", key[kl]);
}
printf("\n");
if (type == 0x4E4E) {
unit yy[2000];
unit xx[2000];
memset(xx, 0, sizeof(xx));
memcpy(xx, key, kl2);
set_precision(bytes2units(kl2));
mp_move_and_shift_left_bits(yy, xx, 8);
mp_recip(xx, yy);
printnumber("MyNI", xx);
}
key = k2;
}
}
#endif
static int checkkey(const char *key) { /* 0 - wrong key, != 0 - key ok */
char temp[8];
char *keyptr, *p;
size_t keylen;
if ((initkey(key, &keyptr, &keylen)) &&
(findchunk(keyptr, keylen, "MA", &p))) {
#ifdef DEBUG_PRINT
mpkey(keyptr, keylen);
#endif
nwhash1init(temp, 8);
nwhash1(temp, 8, key + 10, WVAL_LH(key, 8) - 20);
return (!memcmp(p, temp, 8));
}
return 0;
}
static ncpt_mutex_t mpilock = NCPT_MUTEX_INITIALIZER;
static long modexpkey(const char *s_key, char *buf, char *outbuf, int bufsize) {
char *s_keyptr;
size_t s_keylen;
int i, nbits, nblocksize;
int err = -1;
unitptr nmod, nexp, nin, nout;
char *p;
nmod = nexp = nin = nout = NULL;
if (!initkey(s_key, &s_keyptr, &s_keylen)) {
ISRPrint("Initkey failed\n");
return ERR_INVALID_SERVER_RESPONSE;
}
i = findchunk(s_keyptr, s_keylen, "NN", &p);
if (!p) {
ISRPrint("NN chunk not found\n");
return ERR_INVALID_SERVER_RESPONSE;
}
nbits = countbits_l(p, i);
nblocksize = ((nbits + 31) & (~31)) >> 3;
if (!(nmod = malloc(nblocksize * 4)))
return ENOMEM;
copyfill(nmod, nblocksize, p, i);
i = findchunk(s_keyptr, s_keylen, "EN", &p);
err = ERR_INVALID_SERVER_RESPONSE;
if (!p) {
ISRPrint("EN chunk not found\n");
goto end1;
}
nexp = (unitptr)(((char*)nmod) + nblocksize);
nin = (unitptr)(((char*)nexp) + nblocksize);
nout = (unitptr)(((char*)nin) + nblocksize);
copyfill(nexp, nblocksize, p, i);
copyfill(nin, nblocksize, buf, bufsize);
ncpt_mutex_lock(&mpilock);
set_precision(bytes2units(nblocksize));
i = mp_modexp(nout, nin, nexp, nmod);
ncpt_mutex_unlock(&mpilock);
if (i) {
ISRPrint("mp_modexp failed\n");
err = ERR_INVALID_SERVER_RESPONSE;
} else {
copyfill(outbuf, bufsize, nout, nblocksize);
err = 0;
}
end1:
free(nmod);
return err;
}
/* ctx must be in WCHAR_T mode, without DCV_CANONICALIZE_NAMES */
static NWDSCCODE get_public_key(NWDSContextHandle ctx, const wchar_t* objname, nuint8 **key) {
char *keybuf, *kptr;
NWDSCCODE err;
size_t keylen, ofs, klen;
Octet_String_T* ost;
err = nds_read_pk(ctx, objname, &ost);
if (err)
return err;
keybuf = ost->data;
keylen = ost->length;
ofs = WVAL_LH(keybuf, 10) + 0x1a;
if ((ofs > keylen) || (!initkey(keybuf + ofs, &kptr, &klen)) ||
(klen + ofs > keylen) || (!checkkey(keybuf + ofs))) {
err = ERR_INVALID_SERVER_RESPONSE;
ISRPrint("Wrong key\n");
goto err_exit;
}
if (key) {
if (!(kptr = malloc(klen + 10))) {
err = ENOMEM;
goto err_exit;
}
memcpy(kptr, keybuf + ofs, klen + 10);
*key = kptr;
}
err = 0;
err_exit:
free(ost);
return err;
}
static NWDSCCODE __AlignAndEncryptBlockWithSK(
const nuint8* hash,
const nuint8* idata,
size_t ilen,
char* odata,
size_t* olen
) {
size_t filler;
memcpy(odata, idata, ilen);
filler = 8 - (ilen + 5) % 8;
memset(odata + ilen, filler, filler);
ilen += filler;
nwhash1init(odata + ilen, 5);
nwhash1(odata + ilen, 5, odata, ilen);
ilen += 5;
nwencryptblock(hash, odata, ilen, odata);
*olen = ilen;
return 0;
}
NWDSCCODE __NWEncryptWithSK(
const void* hashsrc,
size_t hashsrclen,
const void* idata,
size_t ilen,
void* odata,
size_t* olen
) {
int err;
size_t len;
nuint8 hash[8];
int i;
if (!idata || !ilen || !odata || !olen)
return ERR_NULL_POINTER;
if (!hashsrc)
hashsrclen = 0;
nwhash1init(hash, 8);
for (i = 0; i < 10; i++) {
nwhash1(hash, 8, hashsrc, hashsrclen);
}
err = __AlignAndEncryptBlockWithSK(hash, idata, ilen, (char*)odata + 12, &len);
if (err)
return err;
*olen = len + 12;
DSET_LH(odata, 0, 1);
WSET_LH(odata, 4, 1);
WSET_LH(odata, 6, 6);
WSET_LH(odata, 8, len);
WSET_LH(odata, 10, ilen);
return 0;
}
static const char buf2str1[8] = { 1,0,0,0, 9,0, 2,0};
static const char buf2str2[16] = {65,0,0,0, 1,0,0,0, 1,0, 9,0, 53,0, 28,0};
NWDSCCODE rsa_crypt2(
const nuint8* s_key,
Buf_T* input,
Buf_T* output) {
char hashrand[8], temp[8];
unsigned short cryptbuf[128];
char buf2[56];
NWDSCCODE err;
void* data;
size_t datalen;
nuint8* outp;
char b2[28];
nuint8* ln1;
nuint8* sp1;
size_t outlen;
fillrandom(b2, 28);
buf2[0] = 11;
memcpy(buf2 + 1, b2, 28);
memset(buf2 + 29, 11, 11);
memset(buf2 + 40, 0, 16);
nwhash1(buf2 + 40, 5, buf2 + 1, 39);
nwhash1(buf2 + 45, 2, buf2, 45);
fillrandom(buf2 + 47, 5);
err = modexpkey(s_key, buf2, buf2, 56);
if (err)
return err;
err = NWDSBufPut(output, buf2str1, sizeof(buf2str1));
if (err)
return err;
sp1 = NWDSBufPutPtr(output, 4);
if (!sp1)
return ERR_BUFFER_FULL;
err = NWDSBufPut(output, buf2str2, sizeof(buf2str2));
if (err)
return err;
err = NWDSBufPut(output, buf2, 56);
if (err)
return err;
memset(buf2, 0, sizeof(buf2));
ln1 = NWDSBufPutPtr(output, 4);
if (!ln1)
return ERR_BUFFER_FULL;
outp = NWDSBufPeekPtr(output);
if (!outp)
return ERR_BUFFER_FULL;
data = NWDSBufRetrieve(input, &datalen);
err = __NWEncryptWithSK(b2, 28, data, datalen, outp, &outlen);
if (err)
return err;
DSET_LH(ln1, 0, outlen);
NWDSBufPutSkip(output, outlen);
DSET_LH(sp1, 0, outlen + 76);
memset(hashrand, 0, sizeof(hashrand));
memset(temp, 0, sizeof(temp));
memset(cryptbuf, 0, sizeof(cryptbuf));
return 0;
}
NWDSCCODE rsa_crypt(
NWDSContextHandle ctx,
const wchar_t* servername,
Buf_T* input,
Buf_T* output) {
NWDSCCODE err;
nuint8 *s_key;
if ((err = get_public_key(ctx, servername, &s_key)))
return err;
err = rsa_crypt2(s_key, input, output);
free(s_key);
return err;
}
static NWDSCCODE NWDXSetKeysOnConns(
NWDS_HANDLE dxh,
const void* authinfo,
size_t authinfo_len) {
struct list_head* stop = &dxh->conns;
struct list_head* current;
for (current = stop->next; current != stop; current = current->next) {
NWCONN_HANDLE conn = list_entry(current, struct ncp_conn, nds_ring);
ncp_set_private_key(conn, authinfo, authinfo_len);
}
return 0;
}
static NWDSCCODE NWDXSetKeys(
NWDS_HANDLE dxh,
const nuint8 logindata[8],
const wchar_t* username,
const nuint8* pkey,
size_t pkey_len) {
union __NWDSAuthInfo* ndai;
size_t slen;
size_t tlen;
size_t cpos;
if (!dxh || !logindata || !username || !pkey)
return ERR_NULL_POINTER;
slen = (wcslen(username) + 1) * sizeof(wchar_t);
cpos = ROUNDBUFF(sizeof(*ndai));
tlen = cpos + ROUNDBUFF(slen) + ROUNDBUFF(pkey_len);
ndai = (union __NWDSAuthInfo*)malloc(tlen);
if (!ndai)
return ENOMEM;
ndai->header.total = tlen;
ndai->header.version = 0;
ndai->header.hdrlen = sizeof(*ndai);
memcpy(ndai->header.logindata, logindata, 8);
ndai->header.name_len = slen;
memcpy(ndai->data + cpos, username, slen);
ndai->header.privkey_len = pkey_len;
cpos += ROUNDBUFF(slen);
memcpy(ndai->data + cpos, pkey, pkey_len);
/* ignore errors... there is nothing we can do around */
mlock(ndai, tlen);
NWDXSetKeysOnConns(dxh, ndai, tlen);
if (dxh->authinfo) {
tlen = dxh->authinfo->header.total;
memset(dxh->authinfo, 0, tlen);
munlock(dxh->authinfo, tlen);
free(dxh->authinfo);
}
dxh->authinfo = ndai;
return 0;
}
NWDSCCODE NWDSSetKeys(
NWDSContextHandle ctx,
const nuint8 logindata[8],
const wchar_t* username,
const nuint8* pkey,
size_t pkey_len) {
NWDSCCODE err;
err = NWDSIsContextValid(ctx);
if (err)
return err;
return NWDXSetKeys(ctx->ds_connection,
logindata, username, pkey, pkey_len);
}
NWDSCCODE NWDSGetKeys(
NWDSContextHandle ctx,
union __NWDSAuthInfo** pndai,
size_t* pndailen) {
NWDS_HANDLE dxh;
union __NWDSAuthInfo* ndai;
if (!ctx || !pndai)
return ERR_NULL_POINTER;
dxh = ctx->ds_connection;
if (!dxh)
return ERR_NOT_LOGGED_IN;
ndai = dxh->authinfo;
if (!ndai) {
/* OK, we do not have authentication info */
/* but maybe some permanent/shared connection */
/* does have authentication information */
struct list_head* stop = &dxh->conns;
struct list_head* current;
size_t authinfo_len = 0;
for (current = stop->next; current != stop; current = current->next) {
NWDSCCODE err;
NWCONN_HANDLE conn = list_entry(current, struct ncp_conn, nds_ring);
err = ncp_get_private_key(conn, NULL, &authinfo_len);
if (err)
continue; /* kernel without private key support */
if (!authinfo_len)
continue; /* no private key on this connection */
ndai = (union __NWDSAuthInfo*)malloc(authinfo_len);
if (!ndai)
continue; /* not enough memory */
err = ncp_get_private_key(conn, ndai, &authinfo_len);
if (!err)
break; /* we have it */
free(ndai);
ndai = NULL;
}
if (!ndai)
return ERR_NOT_LOGGED_IN;
mlock(ndai, authinfo_len);
NWDXSetKeysOnConns(dxh, ndai, authinfo_len);
if (dxh->authinfo) {
size_t tlen = dxh->authinfo->header.total;
memset(dxh->authinfo, 0, tlen);
munlock(dxh->authinfo, tlen);
free(dxh->authinfo);
}
dxh->authinfo = ndai;
}
*pndai = ndai;
*pndailen = ndai->header.total;
return 0;
}
#define xprintf(X...)
NWDSCCODE __NWDSGetPrivateKey(
NWCONN_HANDLE conn,
const nuint8* connPublicKey,
const nuint8 rndseed[4],
NWObjectID objectID,
const nuint8 pwd[16],
nuint8 logindata[8],
nuint8** privateKey,
size_t* privateKeyLen
) {
nuint8 crypt1strc[40];
Buf_T cbuf;
Buf_T rpb;
Buf_T rqb;
nuint8 tempbuf[1200];
nuint8 rqb_b[1200];
nuint8 rpb_b[1200];
nuint8 logindata2[8];
nuint8* p;
size_t bl;
NWDSCCODE err;
NWDSCCODE grace_err;
nuint32 len;
size_t n2;
size_t n3;
size_t i;
nuint8 temp[8];
nuint8 hashshuf[8];
bl = sizeof(crypt1strc);
err = __NWEncryptWithSK(pwd, 16, rndseed, 4, crypt1strc, &bl);
if (err)
goto err_exit;
NWDSSetupBuf(&cbuf, tempbuf, sizeof(tempbuf));
p = NWDSBufPutPtr(&cbuf, 4);
fillrandom(p, 4);
NWDSBufPutLE32(&cbuf, 1024);
p = NWDSBufPutPtr(&cbuf, 1024);
fillrandom(p, 1024);
NWDSBufPutBuffer(&cbuf, crypt1strc, bl);
NWDSSetupBuf(&rqb, rqb_b, sizeof(rqb_b));
NWDSInitBuf(NULL, DSV_FINISH_LOGIN, &rqb);
err = rsa_crypt2(connPublicKey, &cbuf, &rqb);
if (err)
goto err_exit;
NWDSSetupBuf(&rpb, rpb_b, sizeof(rpb_b));
if (logindata)
err = __NWDSFinishLoginV2(conn, 0, objectID, &rqb, logindata, &rpb);
else
err = __NWDSFinishLoginV2(conn, 1, objectID, &rqb, logindata2, &rpb);
if ((err != 0) && (err != NWE_PASSWORD_EXPIRED))
goto err_exit;
grace_err = err;
err = NWDSBufGetLE32(&rpb, &len);
if (err)
goto err_exit;
xprintf("Want len %d\n", len);
p = NWDSBufGetPtr(&rpb, len);
if (!p) {
err = ERR_BUFFER_EMPTY;
goto err_exit;
}
err = ERR_INVALID_SERVER_RESPONSE;
if (len < 12) {
ISRPrint("Key shorter than 12 bytes (%d)\n", len);
goto err_exit;
}
xprintf("%04X %04X %02X\n", DVAL_LH(p, 0), DVAL_LH(p, 4), WVAL_LH(p, 8));
if ((DVAL_LH(p, 0) != 0x00000001) ||
(DVAL_LH(p, 4) != 0x00060001)) {
ISRPrint("Invalid key start (%08X %08X %04X)\n",
DVAL_LH(p, 0), DVAL_LH(p, 4), WVAL_LH(p, 8));
goto err_exit;
}
n3 = WVAL_LH(p, 8);
if (len < n3 + 12) {
ISRPrint("Invalid key len (%d < %d)\n", len, n3 + 12);
goto err_exit;
}
p += 12;
nwhash1init(temp, 8);
for (i = 10; i; i--)
nwhash1(temp, 8, crypt1strc, 28);
nwdecryptblock(temp, p, n3, p);
nwhash1init(temp, 5);
nwhash1(temp, 5, p, n3 - 5);
xprintf("G1\n");
if (memcmp(temp, p + n3 - 5, 5)) {
ISRPrint("Wrong hash\n");
goto err_exit;
}
xprintf("G2\n");
if (memcmp(p, tempbuf, 4)) {
ISRPrint("Wrong hash #2\n");
goto err_exit;
}
xprintf("G3\n");
n2 = DVAL_LH(p, 4);
p += 8;
for (i = 0; i < n2; i++)
p[i] ^= tempbuf[i+8];
xprintf("G4: %08X %08X\n", DVAL_LH(p, 0), DVAL_LH(p, 4));
if ((DVAL_LH(p, 0) != 0x00000001) ||
(DVAL_LH(p, 4) != 0x00060001)) {
ISRPrint("Invalid start #2: %08X %08X\n", DVAL_LH(p, 0),
DVAL_LH(p, 4));
goto err_exit;
}
n3 = WVAL_LH(p, 8);
p += 12;
nwhash1init(hashshuf, 8);
for (i = 10; i; i--)
nwhash1(hashshuf, 8, pwd, 16);
nwdecryptblock(hashshuf, p, n3, p);
xprintf("G5: %08X %08X\n", DVAL_LH(p, 0), DVAL_LH(p, 4));
if ((DVAL_LH(p, 0) != 1) ||
(WVAL_LH(p, 4) != 2)) {
ISRPrint("Invalid start #3: %08X %08X\n", DVAL_LH(p, 0),
DVAL_LH(p, 4));
goto err_exit;
}
n3 = WVAL_LH(p, 6);
p += 8;
{
char* tb;
if (!(tb = malloc(n3 + 10))) {
err = ENOMEM;
goto err_exit;
}
DSET_LH(tb, 0, 1);
WSET_LH(tb, 4, 3);
WSET_LH(tb, 6, 1);
WSET_LH(tb, 8, n3);
memcpy(tb + 10, p, n3);
xprintf("G6\n");
if (!checkkey(tb)) {
free(tb);
ISRPrint("Post check failed\n");
goto err_exit;
}
xprintf("G7\n");
if (privateKeyLen)
*privateKeyLen = n3 + 10;
if (privateKey) {
*privateKey = tb;
} else {
memset(tb, 0, n3 + 10);
free(tb);
}
}
err = grace_err;
err_exit:
memset(logindata2, 0, sizeof(logindata2));
memset(tempbuf, 0, sizeof(tempbuf));
memset(rpb_b, 0, sizeof(rpb_b));
memset(hashshuf, 0, sizeof(hashshuf));
memset(crypt1strc, 0, sizeof(crypt1strc));
memset(temp, 0, sizeof(temp));
memset(rqb_b, 0, sizeof(rqb_b));
memset(temp, 0, sizeof(temp));
return err;
}
static NWDSCCODE nds_beginauth2(
NWCONN_HANDLE conn,
NWObjectID user_id,
nuint8 authid[4],
const char* s_key) {
char *p, *pend, *n_temp, temp[8], *k1end;
char randno[4];
NWDSCCODE err;
int n1, n3;
u_int16_t n3a;
char rpb_b[DEFAULT_MESSAGE_LEN];
Buf_T rpb;
size_t k1tl;
n_temp = NULL;
fillrandom(randno, 4);
NWDSSetupBuf(&rpb, rpb_b, sizeof(rpb_b));
err = __NWDSBeginAuthenticationV0(conn, user_id, randno, authid, &rpb);
if (err)
goto err_exit;
err = ERR_INVALID_SERVER_RESPONSE;
p = rpb.curPos;
pend = rpb.dataend;
if (pend - p < 12) {
ISRPrint("Invalid len: %u < 12\n", pend - p);
goto err_exit;
}
if ((DVAL_LH(p, 0) != 0x00000001) ||
(DVAL_LH(p, 4) != 0x00020009)) {
ISRPrint("Invalid start: %08X %08X\n", DVAL_LH(p, 0),
DVAL_LH(p, 4));
goto err_exit;
}
n3 = DVAL_LH(p, 8);
if (n3 < 16) {
ISRPrint("Invalid len: %u < 16\n", n3);
goto err_exit;
}
p += 12;
if (pend - p < n3) {
ISRPrint("Invalid len: %u < %u\n", pend - p, n3);
goto err_exit;
}
pend = p + n3;
k1tl = DVAL_LH(p, 0);
if (k1tl < 12) {
ISRPrint("K1Tl %u < 12\n", k1tl);
goto err_exit;
}
p += 4;
k1end = p + k1tl;
if ((DVAL_LH(p, 0) != 0x00000001) ||
(DVAL_LH(p, 4) != 0x000A0001)) {
ISRPrint("Invalid start: %08X %08X\n", DVAL_LH(p, 0),
DVAL_LH(p, 4));
goto err_exit;
}
n3a = WVAL_LH(p, 8);
p += 12;
if (k1end - p < n3a) {
ISRPrint("Invalid len: %u < %u\n", k1end - p, n3a);
goto err_exit;
}
n1 = ((countbits_l(p, n3a) + 31) & (~31)) >> 3;
if (n1 < 52) {
ISRPrint("Too short key: %u < 52\n", n1);
goto err_exit;
}
if (!(n_temp = malloc(n1))) {
err = ENOMEM;
goto err_exit;
}
copyfill(n_temp, n1, p, n3a);
p = (void*)(((unsigned long)k1end + 3) & ~3);
err = modexpkey(s_key, n_temp, n_temp, n1);
if (err) {
ISRPrint("modexpkey failed\n");
goto err_exit;
}
err = ERR_INVALID_SERVER_RESPONSE;
nwhash1init(temp, 7);
nwhash1(temp + 5, 2, n_temp, 45);
nwhash1(temp, 5, n_temp + 1, 39);
if (memcmp(temp, n_temp + 40, 7)) {
ISRPrint("Invalid hash\n");
goto err_exit;
}
nwhash1init(temp, 8);
for (n1 = 10; n1; n1--)
nwhash1(temp, 8, n_temp + 1, 28);
free(n_temp); n_temp = NULL;
if (pend - p < 16) {
ISRPrint("Invalid len: %u < 16\n", pend - p);
goto err_exit;
}
if ((DVAL_LH(p, 0) != 28) ||
(DVAL_LH(p, 4) != 0x00000001) ||
(DVAL_LH(p, 8) != 0x00060001) ||
(DVAL_LH(p, 12) != 0x00040010)) {
ISRPrint("Invalid start: %u %08X %08X %08X\n", DVAL_LH(p, 0),
DVAL_LH(p, 4), DVAL_LH(p, 8), DVAL_LH(p, 12));
goto err_exit;
}
p += 16;
if (pend - p < 16) {
ISRPrint("Invalid len: %u < 16\n", pend - p);
goto err_exit;
}
nwdecryptblock(temp, p, 16, p);
nwhash1init(temp, 5);
nwhash1(temp, 5, p, 11);
if ((!memcmp(temp, p + 11, 5)) || (!memcmp(p, randno, 4)))
err = 0;
else
ISRPrint("Invalid hash\n");
err_exit:
if (n_temp) free(n_temp);
return err;
}
static char *allocfillchunk(const char *keyptr, int keylen,
const char *chunk, int destsize) {
char *p, *p2;
int i;
i = findchunk(keyptr, keylen, chunk, &p);
if (!p)
return NULL;
if (!(p2 = malloc(destsize)))
return NULL;
copyfill(p2, destsize, p, i);
return p2;
}
static NWDSCCODE nds_beginauth(
NWCONN_HANDLE conn,
NWObjectID user_id,
NWDSContextHandle ctx,
const wchar_t* servername,
nuint8 authid[4]) {
NWDSCCODE err;
nuint8* s_key;
if ((err = get_public_key(ctx, servername, &s_key)))
return err;
err = nds_beginauth2(conn, user_id, authid, s_key);
free(s_key);
return err;
}
static NWDSCCODE gen_auth_data(
Buf_T* outbuf,
const char *u_key,
const char *u_priv_key,
const nuint8* authid,
char *loginstrc,
int loginstrc_len) {
char *keyptr;
size_t keylen;
int i, j;
int nbits, nblocksize, nbytes, nblocksize_nw;
unsigned char nmask;
unitptr n_mod, n_exp, n_pn, n_qn, n_dp, n_dq, n_cr, n_key, n_temp;
unitptr n_key_dp, n_key_dq;
unitptr up, up2;
unitptr randbuf;
char *p;
char *tempbuf;
char hashbuf[0x42];
NWDSCCODE err;
n_temp = n_mod = n_exp = n_pn = n_qn = n_dp = n_dq = n_cr = n_key = NULL;
if (!initkey(u_key, &keyptr, &keylen)) {
ISRPrint("Initkey failed\n");
return ERR_INVALID_SERVER_RESPONSE;
}
i = findchunk(keyptr, keylen, "NN", &p);
if (!p) {
ISRPrint("NN chunk not found\n");
return ERR_INVALID_SERVER_RESPONSE;
}
nbits = countbits_l(p, i);
nbytes = (nbits + 7) >> 3;
nmask = (unsigned char)(255 >> (8 - (nbits & 7)));
/* we really want (x + 31) & ~15... I'm sorry, but Novell thinks that way */
nblocksize_nw = ((nbits + 31) & (~15)) >> 3;
ncpt_mutex_lock(&mpilock);
set_precision(bytes2units(nblocksize_nw));
nblocksize = units2bytes(global_precision);
n_mod = (unitptr)allocfillchunk(keyptr, keylen, "NN", nblocksize);
n_exp = (unitptr)allocfillchunk(keyptr, keylen, "EN", nblocksize);
if (!initkey(u_priv_key, &keyptr, &keylen)) {
err = ERR_INVALID_SERVER_RESPONSE;
ISRPrint("initkey failed\n");
goto err_exit;
}
n_pn = (unitptr)allocfillchunk(keyptr, keylen, "PN", nblocksize);
n_qn = (unitptr)allocfillchunk(keyptr, keylen, "QN", nblocksize);
n_dp = (unitptr)allocfillchunk(keyptr, keylen, "DP", nblocksize);
n_dq = (unitptr)allocfillchunk(keyptr, keylen, "DQ", nblocksize);
n_cr = (unitptr)allocfillchunk(keyptr, keylen, "CR", nblocksize);
n_key = malloc(nblocksize);
nwhash2init(hashbuf);
nwhash2block(hashbuf, loginstrc, loginstrc_len);
nwhash2end(hashbuf);
copyfill(n_key, nblocksize, hashbuf, 16);
if (!(tempbuf = malloc(loginstrc_len + 16))) {
err = ENOMEM;
goto err_exit;
}
DSET_LH(tempbuf, 0, 0);
DSET_LH(tempbuf, 4, 0x3c);
memcpy(tempbuf + 8, authid, 4);
DSET_LH(tempbuf, 12, loginstrc_len);
memcpy(tempbuf + 16, loginstrc, loginstrc_len);
nwhash2init(hashbuf);
nwhash2block(hashbuf, tempbuf, loginstrc_len + 16);
free(tempbuf);
n_temp = malloc(nblocksize * 6);
n_key_dp = (unitptr)(((char*)n_temp) + nblocksize);
n_key_dq = (unitptr)(((char*)n_key_dp) + nblocksize);
mp_mult(n_temp, n_pn, n_qn);
mp_modexp(n_key_dp, n_key, n_dp, n_pn);
mp_modexp(n_key_dq, n_key, n_dq, n_qn);
mp_move(n_temp, n_key_dp);
mp_add(n_temp, n_pn);
mp_sub(n_temp, n_key_dq);
stage_modulus(n_pn);
mp_modmult(n_temp, n_temp, n_cr);
mp_mult(n_key, n_temp, n_qn);
mp_add(n_key, n_key_dq);
randbuf = (unitptr)(((char*)n_key_dq) + nblocksize);
memset(randbuf, 0, nblocksize * 3);
p = NWDSBufPutPtr(outbuf, 12 + nblocksize_nw * 6);
DSET_LH(p, 0, 0x00000001);
DSET_LH(p, 4, 0x00100008);
WSET_LH(p, 8, 3);
WSET_LH(p, 10, nblocksize_nw * 3);
memset(p + 12, 0, nblocksize_nw * 6);
up = randbuf; up2 = (unitptr)(p + 12);
for (i = 3; i; i--) {
fillrandom((char *)up, nbytes);
((char *)up)[nbytes - 1] &= nmask;
if (!(j = mp_compare(up, n_mod))) {
mp_dec(up);
} else if (j > 0) {
mp_sub(up, n_mod);
mp_neg(up);
mp_add(up, n_mod);
}
mp_modexp(up2, up, n_exp, n_mod);
((char *)up) += nblocksize;
((char *)up2) += nblocksize_nw;
}
nwhash2block(hashbuf, p+12, nblocksize_nw * 3);
nwhash2end(hashbuf);
up = randbuf;
for (i = 0; i < 3; i++) {
mp_init(n_temp, WVAL_LH(hashbuf, i<<1));
mp_modexp(up2, n_key, n_temp, n_mod);
stage_modulus(n_mod);
mp_modmult(up2, up2, up);
((char *)up) += nblocksize;
((char *)up2) += nblocksize_nw;
}
if (n_temp) {
mp_init0(n_temp);
mp_init0(n_key_dp);
mp_init0(n_key_dq);
free(n_temp);
}
err = 0;
err_exit:
memset(hashbuf, 0, sizeof(hashbuf));
if (n_pn) { mp_init0(n_pn); free(n_pn); }
if (n_qn) { mp_init0(n_qn); free(n_qn); }
if (n_dp) { mp_init0(n_dp); free(n_dp); }
if (n_dq) { mp_init0(n_dq); free(n_dq); }
if (n_cr) { mp_init0(n_cr); free(n_cr); }
ncpt_mutex_unlock(&mpilock);
free(n_mod);
free(n_exp);
return err;
}
NWDSCCODE NWDSAuthenticateConn(
NWDSContextHandle inctx,
NWCONN_HANDLE conn) {
/* it can be per NWDS_HANDLE lock... */
static ncpt_mutex_t auth_lock = NCPT_MUTEX_INITIALIZER;
nuint8 authid[4];
NWDSCCODE err;
size_t user_name_len;
char *loginstrc;
size_t loginstrc_len;
nuint8 *u_key;
Buf_T signbuf;
char signbuf_b[DEFAULT_MESSAGE_LEN];
Buf_T keybuf;
char keybuf_b[DEFAULT_MESSAGE_LEN];
wchar_t server_name[MAX_DN_CHARS+1];
NWObjectID user_id;
char signkey[8];
wchar_t* w_user_name;
nuint8* u_priv_key;
nuint8* logindata;
union __NWDSAuthInfo* ndai;
size_t ndailen;
NWDSContextHandle ctx;
if (!conn)
return ERR_NULL_POINTER;
ncpt_mutex_lock(&auth_lock);
if (conn->connState & CONNECTION_AUTHENTICATED) {
err = 0;
goto err_exit3;
}
err = NWDSGetKeys(inctx, &ndai, &ndailen);
if (err)
goto err_exit3;
w_user_name = (wchar_t*)(ndai->data + ndai->header.hdrlen);
logindata = ndai->header.logindata;
u_priv_key = ndai->data + ndai->header.hdrlen + ndai->header.name_len;
err = __NWDSGetServerDN(conn, server_name, sizeof(server_name));
if (err)
goto err_exit3;
err = NWDSDuplicateContextHandle(inctx, &ctx);
if (err)
goto err_exit3;
ctx->dck.flags = DCV_XLATE_STRINGS | DCV_TYPELESS_NAMES | DCV_DEREF_ALIASES;
ctx->priv_flags |= DCV_PRIV_AUTHENTICATING;
err = NWDSSetContext(ctx, DCK_LOCAL_CHARSET, "WCHAR_T//");
if (err)
goto err_exit2;
/* FIXME! deref aliases? */
err = NWDSMapNameToID(ctx, conn, (const NWDSChar*)w_user_name, &user_id);
if (err)
goto err_exit2;
if ((err = nds_beginauth(conn, user_id, ctx, server_name, authid)))
goto err_exit2;
u_key = NULL;
loginstrc = malloc(DEFAULT_MESSAGE_LEN);
if (!loginstrc) {
err = ENOMEM;
goto err_exit2;
}
user_name_len = DEFAULT_MESSAGE_LEN - 22;
err = __NWDSGetObjectDNUnicode(conn, user_id, (unicode*)(loginstrc + 22), &user_name_len);
if (err)
goto err_exit;
loginstrc_len = user_name_len + 22;
DSET_LH(loginstrc, 0, 1);
WSET_LH(loginstrc, 4, 6);
memcpy(loginstrc + 6, logindata, 8);
fillrandom(loginstrc + 14, 4);
WSET_LH(loginstrc, 18, 0);
WSET_LH(loginstrc, 20, user_name_len);
if ((err = get_public_key(ctx, w_user_name, &u_key)))
goto err_exit;
NWDSSetupBuf(&signbuf, signbuf_b, sizeof(signbuf_b));
#ifdef SIGNATURES
if (conn->sign_wanted) {
Buf_T tmp;
NWDSSetupBuf(&tmp, signkey, 8);
NWDSBufPutSkip(&tmp, 8);
fillrandom(signkey, 8);
err = rsa_crypt(ctx, server_name, &tmp, &signbuf);
if (err)
goto err_exit;
}
#endif
NWDSSetupBuf(&keybuf,keybuf_b, sizeof(keybuf_b));
if ((err = gen_auth_data(&keybuf, u_key, u_priv_key,
authid, loginstrc, loginstrc_len)))
goto err_exit;
err = __NWDSFinishAuthenticationV0(conn, &signbuf,
loginstrc, loginstrc_len,
&keybuf);
memset(keybuf_b, 0, sizeof(keybuf_b));
if (err)
goto err_exit;
ncp_set_private_key(conn, ndai, ndailen);
conn->user_id = user_id;
conn->user_id_valid = 1;
conn->connState |= CONNECTION_AUTHENTICATED;
#ifdef SIGNATURES
if ((err = ncp_sign_start(conn, signkey)))
goto err_exit;
#endif
err = ncp_change_conn_state(conn, 1);
err_exit:
if (loginstrc) free(loginstrc);
if (u_key) free(u_key);
err_exit2:
NWDSFreeContext(ctx);
err_exit3:
ncpt_mutex_unlock(&auth_lock);
return err;
}
long nds_login_auth(struct ncp_conn *conn, const char *user,
const char *pwd) {
long err;
wchar_t user_w[MAX_DN_CHARS+1];
char *u_priv_key = NULL;
wchar_t server_name[MAX_DN_CHARS+1];
NWCONN_HANDLE userconn = NULL;
int i;
struct timeval tv;
int grace_period = 0;
NWDSContextHandle ctx = NULL;
#ifdef ERR_MSG
char buf[256]; /* to print username */
#endif
gettimeofday(&tv, NULL);
srand(tv.tv_usec);
if (strlen(user) > MAX_DN_CHARS)
return ENAMETOOLONG;
err = NWDSCreateContextHandle(&ctx);
if (err)
return err;
ctx->dck.flags = DCV_XLATE_STRINGS | DCV_TYPELESS_NAMES | DCV_DEREF_ALIASES | DCV_CANONICALIZE_NAMES;
ctx->priv_flags |= DCV_PRIV_AUTHENTICATING;
err = NWDSXlateFromCtx(ctx, user_w, sizeof(user_w), user);
if (err)
goto err_exit;
err = NWDSSetContext(ctx, DCK_LOCAL_CHARSET, "WCHAR_T//");
if (err)
goto err_exit;
NWDSAddConnection(ctx, conn);
err = nds_login(ctx, (const NWDSChar*)user_w, pwd);
if ((err == ERR_NO_SUCH_ENTRY) &&
(user_w[0] != '.') &&
(user_w[wcslen(user_w)-1] != '.')) {
#ifdef ERR_MSG
printf(_("User %s not found in current context.\n"
"Trying server context...\n"), user);
#endif
err = __NWDSGetServerDN(conn, server_name, sizeof(server_name));
if (err)
goto err_exit;
i = 0;
while (server_name[i] && (server_name[i] != '.'))
i++;
if (wcslen(user_w)+wcslen(server_name+i)+1 > MAX_DN_CHARS) {
err = ENAMETOOLONG;
goto err_exit;
}
memcpy(user_w + wcslen(user_w),
server_name + i,
(wcslen(server_name + i) + 1) * sizeof(wchar_t));
ctx->dck.flags &= ~DCV_CANONICALIZE_NAMES;
err = nds_login(ctx, (const NWDSChar*)user_w, pwd);
}
if (err) {
if (err != NWE_PASSWORD_EXPIRED) {
#ifdef ERR_MSG
fprintf(stderr, _("error %d logging in\n"), err);
#endif
goto err_exit;
}
grace_period = 1;
}
if ((err = NWDSAuthenticateConn(ctx, conn))) {
#ifdef ERR_MSG
fprintf(stderr, _("error %d authenticating\n"), err);
#endif
goto err_exit;
}
if (grace_period && (!err)) {
err = NWE_PASSWORD_EXPIRED;
}
err_exit:
if (userconn)
NWCCCloseConn(userconn);
if (ctx)
NWDSFreeContext(ctx);
if (u_priv_key) {
clearkey(u_priv_key);
free(u_priv_key);
}
#ifdef RANDBUF
ncpt_mutex_lock(&randbuflock);
memset(global_randbuf, 0, RANDBUFSIZE);
g_rndp = global_randbuf + RANDBUFSIZE;
ncpt_mutex_unlock(&randbuflock);
#endif
return err;
}
#ifdef DEBUG_PRINT
static void dgcdv(const unit* a, const unit* b, const unit* c, const unit* d, const unit* e) {
unit a1[4096];
unit a2[4096];
unit a3[4096];
printnumber("Z", a);
printnumber("U", b);
printnumber("V", c);
printnumber("X", d);
printnumber("Y", e);
mp_mult(a1, b, d);
mp_mult(a2, c, e);
printnumber("UX", a1);
printnumber("VY", a2);
mp_add(a1, a2);
printnumber("UX+VY", a1);
mp_mod(a3, a1, d);
printnumber("UX+VY%U", a3);
mp_mod(a3, a1, e);
printnumber("UX+VY%V", a3);
}
#endif
/* ctx must be in wchar_t mode */
NWDSCCODE __NWDSGetPublicKeyFromConnection(NWDSContextHandle ctx, NWCONN_HANDLE conn, nuint8 **skey) {
wchar_t serverName[MAX_DN_CHARS+1];
NWDSCCODE err;
*skey = NULL;
err = __NWDSGetServerDN(conn, serverName, sizeof(serverName));
if (err)
return err;
err = get_public_key(ctx, serverName, skey);
if (err)
return err;
return 0;
}
struct keyparam {
unit n_exp[bytes2units(106)];
unit n_mod[bytes2units(106)];
unit n_recip[bytes2units(106)];
unit n_pn[bytes2units(106 /*50*/)];
unit n_qn[bytes2units(106 /*50*/)];
unit n_dp[bytes2units(106 /*50*/)];
unit n_dq[bytes2units(106 /*50*/)];
unit n_cr[bytes2units(106)];
int n_recip_present;
size_t bytes;
nuint8 BA;
size_t BL;
};
static int CreatePublicKey(const struct keyparam* kp, nuint8 *pubkey, size_t *pubkey_len) {
nuint8* ptr;
nuint8 hash[8];
size_t n_exp_len;
size_t len;
n_exp_len = (countbits(kp->n_exp) + 7) / 8 + 2;
len = (4 + 4) /* BV */
+ (4 + 1) * 2 /* BC, BA */
+ (4 + 2) /* BL */
+ (4 + kp->bytes * 2) /* NN */
+ (4 + n_exp_len) /* EN */
+ (4 + 8) /* MA */
+ (2 + 6); /* PURSAF */
if (kp->n_recip_present)
len += 4 + kp->bytes * 2 + 6; /* NI */
if (len + 10 > *pubkey_len)
return NWE_BUFFER_OVERFLOW;
*pubkey_len = len + 10;
DSET_LH(pubkey, 0, 1);
WSET_LH(pubkey, 4, 3);
WSET_LH(pubkey, 6, 1);
WSET_LH(pubkey, 8, len);
pubkey += 10;
ptr = pubkey;
#define PUTHDR(ptr, type, len) \
WSET_HL(ptr, 0, (type)); \
WSET_LH(ptr, 2, len); \
ptr += 4;
#define PUTMEM(ptr, type, len, data) \
PUTHDR(ptr, (type), len); \
memcpy(ptr, data, len); \
ptr += len;
#define PUTNUM(ptr, type, len, data) \
PUTHDR(ptr, (type), len); \
memcpy(ptr, data, len); \
ptr += len;
#define PUTBYTE(ptr, type, data) \
PUTHDR(ptr, (type), 1); \
BSET(ptr, 0, data); \
ptr += 1;
#define PUTWORD(ptr, type, data) \
PUTHDR(ptr, (type), 2); \
WSET_LH(ptr, 0, data); \
ptr += 2;
PUTMEM(ptr, 0x4256, 4, "1.7b");
PUTBYTE(ptr, 0x4243, 3);
PUTBYTE(ptr, 0x4241, kp->BA);
PUTWORD(ptr, 0x424C, kp->BL);
PUTNUM(ptr, 0x4E4E, kp->bytes * 2, kp->n_mod);
if (kp->n_recip_present) {
PUTNUM(ptr, 0x4E49, kp->bytes * 2 + 6, kp->n_recip);
}
PUTNUM(ptr, 0x454E, n_exp_len, kp->n_exp);
nwhash1init(hash, sizeof(hash));
nwhash1(hash, sizeof(hash), pubkey, ptr - pubkey);
PUTMEM(ptr, 0x4D41, sizeof(hash), hash);
WSET_LH(ptr, 0, ptr - pubkey);
memcpy(ptr + 2, "PURSAF", 6);
return 0;
}
static int CreatePrivateKey(const struct keyparam* kp, nuint8* privkey, size_t* privkey_len) {
nuint8* ptr;
nuint8 hash[8];
size_t n_exp_len;
size_t len;
n_exp_len = (countbits(kp->n_exp) + 7 ) / 8 + 2;
len = (4 + 4) /* BV */
+ (4 + 1) * 2 /* BC, BA */
+ (4 + 2) /* BL */
+ (4 + kp->bytes) * 5 /* PN, QN, DP, DQ, CR */
+ (4 + n_exp_len) /* EN */
+ (4 + kp->bytes * 2) /* NN */
+ (4 + 8) /* MA */
+ (2 + 6); /* PRRSAF */
if (len + 8 > *privkey_len)
return NWE_BUFFER_OVERFLOW;
*privkey_len = len + 8;
DSET_LH(privkey, 0, 1);
WSET_LH(privkey, 4, 2);
WSET_LH(privkey, 6, len);
privkey += 8;
ptr = privkey;
PUTMEM(ptr, 0x4256, 4, "1.7b");
PUTBYTE(ptr, 0x4243, 4);
PUTBYTE(ptr, 0x4241, kp->BA);
PUTWORD(ptr, 0x424C, kp->BL);
PUTNUM(ptr, 0x4E4E, kp->bytes * 2, kp->n_mod);
PUTNUM(ptr, 0x454E, n_exp_len, kp->n_exp);
PUTNUM(ptr, 0x504E, kp->bytes, kp->n_pn);
PUTNUM(ptr, 0x514E, kp->bytes, kp->n_qn);
PUTNUM(ptr, 0x4450, kp->bytes, kp->n_dp);
PUTNUM(ptr, 0x4451, kp->bytes, kp->n_dq);
PUTNUM(ptr, 0x4352, kp->bytes, kp->n_cr);
nwhash1init(hash, sizeof(hash));
nwhash1(hash, sizeof(hash), privkey, ptr - privkey);
PUTMEM(ptr, 0x4D41, sizeof(hash), hash);
WSET_LH(ptr, 0, ptr - privkey);
memcpy(ptr + 2, "PRRSAF", 6);
return 0;
}
#undef PUTWORD
#undef PUTBYTE
#undef PUTNUM
#undef PUTMEM
#undef PUTHDR
static int primes[] = {
3, 5, 7, 0, 11, 13, 17, 19, 23, 29, 31, 37, 41,
43, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101,
103, 107, 109, 113, 127, 131,
0x89, 0x8B, 0x95, 0x97, 0x9D, 0xA3, 0xA7, 0xAD, 0xB3, 0xB5,
0xBF, 0xC1, 0xC5, 0xC7, 0xD3, 0xDF, 0xE3, 0xE5, 0xE9, 0xEF,
0xF1, 0xFB, 0x101, 0x107, 0x10D, 0x10F, 0x115, 0x119, 0x11B,
0x125, 0x133, 0x137, 0x139, 0x13D, 0x14B, 0x151, 0x15B, 0x15D,
0x161, 0x167, 0x16F, 0x175, 0x17B, 0x17F, 0x185, 0x18D, 0x191,
0x199, 0x1A3, 0x1A5, 0x1AF, 0x1B1, 0x1B7, 0x1BB, 0x1C1, 0x1C9,
0x1CD, 0x1CF, 0x1D3, 0x1DF, 0x1E7, 0x1EB, 0x1F3, 0 };
static int IsPrime(const unit* n) {
unitptr tmp;
unitptr result;
unitptr oneless;
size_t len;
size_t i;
len = units2bytes(global_precision);
tmp = alloca(len);
result = alloca(len);
oneless = alloca(len);
/* n is odd, so n-1 is even... */
mp_move(oneless, n);
mp_clrbit(oneless, 0);
for (i = 0; i < 100; i++) {
int err;
do {
fillrandom((void*)tmp, len);
if (mp_mod(result, tmp, n)) {
continue; /* return error? */
}
} while (testle(result, 1));
err = mp_modexp(tmp, result, oneless, n);
if (err) {
continue; /* return error? */
}
if (testne(tmp, 1))
return 0;
}
mp_burn(oneless);
mp_burn(result);
mp_burn(tmp);
return 1;
}
static int FindPrime(unitptr p, size_t bits) {
size_t len;
size_t old_precision;
old_precision = global_precision;
set_precision(bits2units(bits + SLOP_BITS));
len = units2bytes(global_precision);
while (1) {
size_t i;
int divd;
nuint8 skip[1000];
fillrandom((void*)p, len);
mp_setbit(p, bits - 2);
mp_setbit(p, bits - 1);
for (i = units2bits(global_precision); i-- > bits; )
mp_clrbit(p, i);
mp_clrbit(p, 0);
memset(skip, 0, sizeof(skip));
for (divd = 3; divd < 9000; divd += 2) {
int modd;
int *prime;
for (prime = primes; *prime && (*prime < divd); prime++) {
if (divd % *prime == 0)
goto next;
}
modd = mp_shortmod(p, divd);
if (!modd)
modd = divd;
for (i = divd - modd; i < sizeof(skip); i += divd) {
skip[i] = 1;
}
next:;
}
for (mp_inc(p), i = 1; i < sizeof(skip); mp_inc(p), mp_inc(p), i += 2) {
if (skip[i])
continue;
if (IsPrime(p)) {
/* extend precision */
for (i = global_precision; i < old_precision; i++) {
p[i] = 0;
}
set_precision(old_precision);
return 0;
}
}
}
}
/* move this to mpilib? */
static void gcdv(unit *z, unit *u, unit *v, const unit *x, const unit *y) {
unit *u2;
unit *v2;
unit *z2;
unit *quot;
unit *tmp;
unit *tmp2;
size_t len = units2bytes(global_precision) * 6;
u2 = alloca(len);
v2 = u2 + global_precision;
z2 = v2 + global_precision;
quot = z2 + global_precision;
tmp = quot + global_precision;
tmp2 = tmp + global_precision;
mp_init(u, 1);
mp_init(v, 0);
mp_move(z, x);
mp_init(u2, 0);
mp_init(v2, 1);
mp_move(z2, y);
while (testne(z2, 0)) {
mp_div(tmp, quot, z, z2);
#define OSTEP(r,prev,mult) \
mp_mult(tmp, prev, mult); \
mp_move(tmp2, r); \
mp_sub(tmp2, tmp); \
mp_move(r, prev); \
mp_move(prev, tmp2);
OSTEP(u, u2, quot);
OSTEP(v, v2, quot);
OSTEP(z, z2, quot);
#undef OSTEP
}
if (mp_tstminus(u)) {
mp_add(u, y);
}
if (mp_tstminus(v)) {
mp_add(v, x);
}
memset(u2, 0, len);
}
static int ComputeKey(struct keyparam *kp) {
unitptr tmp;
unitptr tmp2;
unitptr tmp3;
unitptr n_pn1;
unitptr n_qn1;
unitptr n_dn;
int err;
size_t len = units2bytes(global_precision) * 6;
tmp = alloca(len);
tmp2 = tmp + global_precision;
tmp3 = tmp2 + global_precision;
n_pn1 = tmp3 + global_precision;
n_qn1 = n_pn1 + global_precision;
n_dn = n_qn1 + global_precision;
mp_mult(kp->n_mod, kp->n_pn, kp->n_qn);
/* Hey, why shift by 8? */
mp_move_and_shift_left_bits(tmp, kp->n_mod, 8);
mp_recip(kp->n_recip, tmp);
kp->n_recip_present = 1;
mp_move(n_qn1, kp->n_qn);
mp_dec(n_qn1);
mp_move(n_pn1, kp->n_pn);
mp_dec(n_pn1);
mp_mult(tmp, n_pn1, n_qn1);
gcdv(tmp2, n_dn, tmp3, kp->n_exp, tmp);
#ifdef DEBUG_PRINT
dgcdv(tmp2, n_dn, tmp3, kp->n_exp, tmp);
#endif
if (testne(tmp2, 1)) {
err = ERR_SYSTEM_ERROR;
} else {
mp_div(kp->n_dp, tmp, n_dn, n_pn1);
mp_div(kp->n_dq, tmp, n_dn, n_qn1);
gcdv(tmp, tmp2, kp->n_cr, kp->n_pn, kp->n_qn);
err = 0;
}
memset(tmp, 0, len);
return err;
}
NWDSCCODE __NWGenerateKeyPair(
size_t key_len,
const void* exp,
size_t exp_len,
void* pubkey,
size_t* pubkey_len,
void* privkey,
size_t* privkey_len
) {
nuint8 def_exp[] = { 1, 0, 1};
struct keyparam kp;
NWDSCCODE err;
size_t bits;
size_t words; /* netware native units */
if (!pubkey || !pubkey_len || !privkey || !privkey_len) return -2;
if (!key_len || !exp || !exp_len) {
exp = def_exp;
exp_len = sizeof(def_exp);
key_len = 420; /* well, at least 620 does work too */
} else {
if ((key_len > 760) || (key_len < 256) || (key_len & 1) || (exp_len > 16)) {
return NWE_PARAM_INVALID;
}
}
memset(&kp, 0, sizeof(kp));
kp.BA = 48;
kp.BL = key_len;
memcpy(kp.n_exp, exp, exp_len);
ncpt_mutex_lock(&mpilock);
set_precision(bits2units(kp.BL) + SLOP_BITS);
if ((countbits(kp.n_exp) > kp.BL) || !(kp.n_exp[0] & 1)) {
err = NWE_PARAM_INVALID;
goto quit;
}
bits = kp.BL >> 1;
words = (bits / 16) + 1;
kp.bytes = words * 2;
err = FindPrime(kp.n_pn, bits);
if (err)
goto quit;
err = FindPrime(kp.n_qn, bits);
if (err)
goto quit;
if (mp_compare(kp.n_pn, kp.n_qn) != 1) {
unitptr tmp = alloca(units2bytes(global_precision));
mp_move(tmp, kp.n_pn);
mp_move(kp.n_pn, kp.n_qn);
mp_move(kp.n_qn, tmp);
mp_init0(tmp);
}
/* +6: NI has 6 additonal bytes... ?! */
set_precision(bytes2units(kp.bytes * 2 + 6 + (SLOP_BITS + 7) / 8));
err = ComputeKey(&kp);
if (err)
goto quit;
err = CreatePublicKey(&kp, pubkey, pubkey_len);
if (err)
goto quit;
err = CreatePrivateKey(&kp, privkey, privkey_len);
quit:;
ncpt_mutex_unlock(&mpilock);
memset(&kp, 0, sizeof(kp));
return err;
}