xref: /csrg-svn/usr.bin/uucp/vms/validate.c (revision 48681)
1*48681Sbostic /*
2*48681Sbostic  *
3*48681Sbostic  *	Routines to deal with the VMS User Authorization File:
4*48681Sbostic  *
5*48681Sbostic  *	get_vms_uaf_record(name,uaf);
6*48681Sbostic  *	validate_vms_user(name,password,uaf);
7*48681Sbostic  *	hash_vms_password(output_buf,input_buf,input_len,username,type,salt);
8*48681Sbostic  *
9*48681Sbostic  *
10*48681Sbostic  */
11*48681Sbostic 
12*48681Sbostic 
13*48681Sbostic /*
14*48681Sbostic  * Includes
15*48681Sbostic  */
16*48681Sbostic #include <vms/fab.h>			/* File access block */
17*48681Sbostic #include <vms/rab.h>			/* Record access block */
18*48681Sbostic #include <vms/rmsdef.h>			/* RMS return codes */
19*48681Sbostic #include <vms/ssdef.h>			/* System service return codes */
20*48681Sbostic #include <vms/uafdef.h>			/* Authorization file records */
21*48681Sbostic #include <vms/logdef.h>			/* Logical name table seach masks */
22*48681Sbostic 
23*48681Sbostic /*
24*48681Sbostic  * defines
25*48681Sbostic  */
26*48681Sbostic #define	RETRY_RLK	2		/* number of retries if record locked */
27*48681Sbostic #define	SLEEP_RLK	75		/* MS to sleep before retrying a GET */
28*48681Sbostic #define	RECSIZ		80		/* Maximum length for password string */
29*48681Sbostic #define	UAFNAME	"SYSUAF"		/* Name of authorization file */
30*48681Sbostic #define	UAFSIZE 6			/* Size of above */
31*48681Sbostic #define	DEFNAME	"SYS$SYSTEM:.DAT"	/* Default path to authorization file */
32*48681Sbostic #define	DEFSIZE	15			/* Size of above */
33*48681Sbostic #define	DEFUSER	"DEFAULT     "		/* "Default user" name */
34*48681Sbostic 
35*48681Sbostic #define	UAF$_NORMAL	1		/* Normal completion */
36*48681Sbostic #define	UAF$_INVUSR	-2		/* Invalid User Name */
37*48681Sbostic #define	UAF$_INVPWD	-4		/* Invalid Password  */
38*48681Sbostic 
39*48681Sbostic #define	UAF$S_USERNAME	12		/* User Name Size */
40*48681Sbostic #define	UAF$S_PWD	8		/* Password Size */
41*48681Sbostic 
42*48681Sbostic struct descr {int size; char *ptr;};	/* VMS descriptor		*/
43*48681Sbostic 
44*48681Sbostic /*
45*48681Sbostic  * OWN STORAGE:
46*48681Sbostic  */
47*48681Sbostic static int wakedelta[] = {-10*1000*SLEEP_RLK,-1};
48*48681Sbostic 
49*48681Sbostic 
50*48681Sbostic /*
51*48681Sbostic  *
52*48681Sbostic  *	status = get_vms_uaf_record(name,uaf);
53*48681Sbostic  *
54*48681Sbostic  */
55*48681Sbostic int get_vms_uaf_record(name,uaf)
56*48681Sbostic char *name;
57*48681Sbostic register struct uaf *uaf;
58*48681Sbostic {
59*48681Sbostic 	struct FAB fab;
60*48681Sbostic 	struct RAB rab;
61*48681Sbostic 	unsigned int old_privs[2],new_privs[2];
62*48681Sbostic 	int status;
63*48681Sbostic 	int default_user = 1;
64*48681Sbostic 	register int i;
65*48681Sbostic 	register char *cp,*cp1,*cp2;
66*48681Sbostic 
67*48681Sbostic 	/*
68*48681Sbostic 	 *	Zero the fab and rab
69*48681Sbostic 	 */
70*48681Sbostic 	bzero(&fab,sizeof(fab));
71*48681Sbostic 	bzero(&rab,sizeof(rab));
72*48681Sbostic 	/*
73*48681Sbostic 	 *	Setup the fab
74*48681Sbostic 	 */
75*48681Sbostic 	fab.fab$b_bid = FAB$C_BID;
76*48681Sbostic 	fab.fab$b_bln = sizeof(fab);
77*48681Sbostic 	fab.fab$l_fna = UAFNAME;
78*48681Sbostic 	fab.fab$b_fns = UAFSIZE;
79*48681Sbostic 	fab.fab$l_dna = DEFNAME;
80*48681Sbostic 	fab.fab$b_dns = DEFSIZE;
81*48681Sbostic 	fab.fab$b_dsbmsk = (1<<LOG$C_GROUP) | (1<<LOG$C_PROCESS);
82*48681Sbostic 	fab.fab$b_fac |= FAB$M_GET;
83*48681Sbostic 	fab.fab$b_shr = (FAB$M_GET|FAB$M_PUT|FAB$M_UPD|FAB$M_DEL);
84*48681Sbostic 	fab.fab$b_org = FAB$C_IDX;
85*48681Sbostic 	fab.fab$b_rfm = FAB$C_VAR;
86*48681Sbostic 	/*
87*48681Sbostic 	 *	setup the rab
88*48681Sbostic 	 */
89*48681Sbostic 	rab.rab$b_bid = RAB$C_BID;
90*48681Sbostic 	rab.rab$b_bln = sizeof(rab);
91*48681Sbostic 	rab.rab$b_rac = RAB$C_KEY;
92*48681Sbostic 	rab.rab$l_rop |= RAB$M_NLK;
93*48681Sbostic 	rab.rab$b_mbc = 10;
94*48681Sbostic 	rab.rab$w_usz = sizeof(struct uaf);
95*48681Sbostic 	rab.rab$l_ubf = (char *)uaf;
96*48681Sbostic 	rab.rab$l_kbfpbf.rab$l_kbf = (unsigned long int *)uaf;
97*48681Sbostic 	rab.rab$b_kszpsz.rab$b_ksz = UAF$S_USERNAME;
98*48681Sbostic 	rab.rab$l_fab = &fab;
99*48681Sbostic 	/*
100*48681Sbostic 	 *	Enable all privileges that we are authorized to have just to
101*48681Sbostic 	 *	enhance the possibility of accessing the SYSUAF file.
102*48681Sbostic 	 */
103*48681Sbostic 	new_privs[0] = -1; new_privs[1] = -1;
104*48681Sbostic 	sys$setprv(1,new_privs,0,old_privs);
105*48681Sbostic 	/*
106*48681Sbostic 	 *  Open the File and connect the RAB
107*48681Sbostic 	 */
108*48681Sbostic 	status = sys$open(&fab);
109*48681Sbostic 	if (!(status & 1)) {
110*48681Sbostic 		if ((status == RMS$_SNE) ||
111*48681Sbostic 		    (status == RMS$_SPE) ||
112*48681Sbostic 		    (status == RMS$_DME)) {
113*48681Sbostic 			fab.fab$b_shr = 0;
114*48681Sbostic 			status = sys$open(&fab);
115*48681Sbostic 			if (!(status & 1)) goto exit;
116*48681Sbostic 		} else goto exit;
117*48681Sbostic 	}
118*48681Sbostic 	status = sys$connect(&rab);
119*48681Sbostic 	if (!(status & 1)) goto exit;
120*48681Sbostic 	/*
121*48681Sbostic 	 *	Move the USERNAME to the uaf$t_username field (as a buffer)
122*48681Sbostic 	 *	uppercaseify it along the way and check it against the
123*48681Sbostic 	 *	username "DEFAULT" (which is not a real username).  Pad
124*48681Sbostic 	 *	the uaf$t_username field with blanks.
125*48681Sbostic 	 */
126*48681Sbostic 	i = UAF$S_USERNAME;
127*48681Sbostic 	cp = name;
128*48681Sbostic 	cp1 = uaf->uaf$t_username;
129*48681Sbostic 	cp2 = DEFUSER;
130*48681Sbostic 	while(--i >= 0) {
131*48681Sbostic 		if (*cp == 0) break;
132*48681Sbostic 		if (*cp != *cp2++) default_user = 0;
133*48681Sbostic 		*cp1++ = ((*cp >= 'a') && (*cp <= 'z')) ?
134*48681Sbostic 				*cp++ + ('A' - 'a') : *cp++;
135*48681Sbostic 	}
136*48681Sbostic 	i++;
137*48681Sbostic 	while(--i >= 0) {
138*48681Sbostic 		*cp1++ = ' ';
139*48681Sbostic 		if (*cp2++ != ' ') default_user = 0;
140*48681Sbostic 	}
141*48681Sbostic 	/*
142*48681Sbostic 	 *	The "DEFAULT" user is illegal!
143*48681Sbostic 	 */
144*48681Sbostic 	if (default_user) {
145*48681Sbostic 		status = UAF$_INVUSR;
146*48681Sbostic 		goto exit;
147*48681Sbostic 	}
148*48681Sbostic 	/*
149*48681Sbostic 	 *	Look up the User's UAF record
150*48681Sbostic 	 */
151*48681Sbostic 	status = get_record(&rab);
152*48681Sbostic 	if (status == RMS$_RNF) status = UAF$_INVUSR;
153*48681Sbostic 
154*48681Sbostic exit:
155*48681Sbostic 	/*
156*48681Sbostic 	 *	Done: close the file, release privileges and return status
157*48681Sbostic 	 */
158*48681Sbostic 	sys$disconnect(&rab);
159*48681Sbostic 	sys$close(&fab);
160*48681Sbostic 	sys$setprv(0,new_privs,0,0);
161*48681Sbostic 	sys$setprv(1,old_privs,0,0);
162*48681Sbostic 	return(status);
163*48681Sbostic }
164*48681Sbostic 
165*48681Sbostic 
166*48681Sbostic /*
167*48681Sbostic  *	Read the record and deal with file locking
168*48681Sbostic  */
169*48681Sbostic static get_record(rab)
170*48681Sbostic struct RAB *rab;
171*48681Sbostic {
172*48681Sbostic 	int retries = RETRY_RLK;
173*48681Sbostic 	int status;
174*48681Sbostic 
175*48681Sbostic 	/*
176*48681Sbostic 	 *	Re-try the appropriate number of times
177*48681Sbostic 	 */
178*48681Sbostic 	while(1) {
179*48681Sbostic 		/*
180*48681Sbostic 		 *	Get the record
181*48681Sbostic 		 */
182*48681Sbostic 		status = sys$get(rab);
183*48681Sbostic 		/*
184*48681Sbostic 		 *	If the return code is not "Record Locked" it is either
185*48681Sbostic 		 *	a success or error that we can't handle, return it.
186*48681Sbostic 		 */
187*48681Sbostic 		if (status != RMS$_RLK) break;
188*48681Sbostic 		/*
189*48681Sbostic 		 *	Record Locked:  If retries exceeded, return error
190*48681Sbostic 		 */
191*48681Sbostic 		if (--retries < 0) break;
192*48681Sbostic 		/*
193*48681Sbostic 		 *	Retry: Sleep first
194*48681Sbostic 		 */
195*48681Sbostic 		status = sys$schdwk(0,0,wakedelta,0);
196*48681Sbostic 		if (status & 1) sys$hiber();
197*48681Sbostic 	}
198*48681Sbostic 	/*
199*48681Sbostic 	 *	Done: Return status
200*48681Sbostic 	 */
201*48681Sbostic 	return(status);
202*48681Sbostic }
203*48681Sbostic 
204*48681Sbostic 
205*48681Sbostic /*
206*48681Sbostic  *
207*48681Sbostic  *	Validate a UserName/Password pair and return the user's UAF record
208*48681Sbostic  *
209*48681Sbostic  */
210*48681Sbostic int validate_vms_user(name,password,uaf)
211*48681Sbostic char *name;
212*48681Sbostic char *password;
213*48681Sbostic register struct uaf *uaf;
214*48681Sbostic {
215*48681Sbostic 	char password_buf[RECSIZ];
216*48681Sbostic 	char username_buf[UAF$S_USERNAME];
217*48681Sbostic 	char encrypt_buf[UAF$S_PWD];
218*48681Sbostic 	register int i;
219*48681Sbostic 	register char *cp,*cp1;
220*48681Sbostic 
221*48681Sbostic 	/*
222*48681Sbostic 	 *	Get the User's UAF record
223*48681Sbostic 	 */
224*48681Sbostic 	i = get_vms_uaf_record(name,uaf);
225*48681Sbostic 	if (!(i & 1)) return(i);
226*48681Sbostic 	/*
227*48681Sbostic 	 *	Limit the username to "UAF$S_USERNAME" size while copying and
228*48681Sbostic 	 *	uppercasifying it.  Pad with spaces to "UAF$S_USERNAME" size.
229*48681Sbostic 	 */
230*48681Sbostic 	i = UAF$S_USERNAME;
231*48681Sbostic 	cp = name;
232*48681Sbostic 	cp1 = username_buf;
233*48681Sbostic 	while(--i >= 0) {
234*48681Sbostic 		if (*cp == 0) break;
235*48681Sbostic 		*cp1++ = ((*cp >= 'a') && (*cp <= 'z')) ?
236*48681Sbostic 				*cp++ + ('A' - 'a') : *cp++;
237*48681Sbostic 	}
238*48681Sbostic 	i++;
239*48681Sbostic 	while(--i >= 0) *cp1++ = ' ';
240*48681Sbostic 	/*
241*48681Sbostic 	 *	Limit the password to "RECSIZ" size while copying and
242*48681Sbostic 	 *	uppercasifying it.
243*48681Sbostic 	 */
244*48681Sbostic 	i = RECSIZ;
245*48681Sbostic 	cp = password;
246*48681Sbostic 	cp1 = password_buf;
247*48681Sbostic 	while(--i >= 0) {
248*48681Sbostic 		if (*cp == 0) break;
249*48681Sbostic 		*cp1++ = ((*cp >= 'a') && (*cp <= 'z')) ?
250*48681Sbostic 				*cp++ + ('A' - 'a') : *cp++;
251*48681Sbostic 	}
252*48681Sbostic 	i = (RECSIZ - 1) - i;	/* Compute size of password string */
253*48681Sbostic 	/*
254*48681Sbostic 	 *	Encrypt the password
255*48681Sbostic 	 */
256*48681Sbostic 	hash_vms_password(encrypt_buf,password_buf,i,username_buf,
257*48681Sbostic 			  uaf->uaf$b_encrypt,uaf->uaf$w_salt);
258*48681Sbostic 	if (bcmp(encrypt_buf,uaf->uaf$l_pwd,UAF$S_PWD) == 0)
259*48681Sbostic 		return(UAF$_NORMAL);
260*48681Sbostic 	else	return(UAF$_INVPWD);
261*48681Sbostic }
262*48681Sbostic 
263*48681Sbostic 
264*48681Sbostic /*
265*48681Sbostic  *
266*48681Sbostic  *	PASSWORD SMASHING CODE:
267*48681Sbostic  *		The real bit hacking is done in "asm" statements, since
268*48681Sbostic  *		"C" is poorly suited towards quad-word arithmetic!
269*48681Sbostic  *
270*48681Sbostic  */
271*48681Sbostic 
272*48681Sbostic /*
273*48681Sbostic  *	AUTODIN II CRC Coefficients:
274*48681Sbostic  */
275*48681Sbostic static unsigned long autodin[] = {
276*48681Sbostic 	000000000000,003555610144,007333420310,004666230254,
277*48681Sbostic 	016667040620,015332650764,011554460530,012001270474,
278*48681Sbostic 	035556101440,036003711504,032665521750,031330331614,
279*48681Sbostic 	023331141260,020664751324,024002561170,027557371034
280*48681Sbostic 	};
281*48681Sbostic 
282*48681Sbostic 
283*48681Sbostic /*
284*48681Sbostic  *	PURDY Polynomial Coefficients
285*48681Sbostic  */
286*48681Sbostic static long c[] = {
287*48681Sbostic 	-83,	-1,		/* C1 */
288*48681Sbostic 	-179,	-1,		/* C2 */
289*48681Sbostic 	-257,	-1,		/* C3 */
290*48681Sbostic 	-323,	-1,		/* C4 */
291*48681Sbostic 	-363,	-1		/* C5 */
292*48681Sbostic 	};
293*48681Sbostic 
294*48681Sbostic /*
295*48681Sbostic  *	Hashing routine
296*48681Sbostic  */
297*48681Sbostic hash_vms_password(output_buf,input_buf,input_length,username,type,salt)
298*48681Sbostic char *output_buf,*input_buf,*username;
299*48681Sbostic unsigned short salt;
300*48681Sbostic {
301*48681Sbostic 	register int i;
302*48681Sbostic 	register char *cp;
303*48681Sbostic 
304*48681Sbostic 	/*
305*48681Sbostic 	 *	Dispatch on encryption type
306*48681Sbostic 	 */
307*48681Sbostic 	if (type == 0) {
308*48681Sbostic 		/*
309*48681Sbostic 		 *	AUTODIN II CRC:
310*48681Sbostic 		 */
311*48681Sbostic 		crc(autodin,-1,input_length,input_buf,output_buf);
312*48681Sbostic 		return;
313*48681Sbostic 	} else {
314*48681Sbostic 		/*
315*48681Sbostic 		 *	PURDY:
316*48681Sbostic 		 */
317*48681Sbostic 
318*48681Sbostic 		i = 8;
319*48681Sbostic 		cp = output_buf;
320*48681Sbostic 		while(--i >= 0) *cp++ = 0;	/* Init output buffer */
321*48681Sbostic 		/*
322*48681Sbostic 		 *	Collapse the password into a quadword
323*48681Sbostic 		 */
324*48681Sbostic 		collapse(input_length,input_buf,output_buf);
325*48681Sbostic 		/*
326*48681Sbostic 		 *	Add salt to middle of quadword
327*48681Sbostic 		 */
328*48681Sbostic 		*((unsigned short *)(output_buf+3)) += salt;
329*48681Sbostic 		/*
330*48681Sbostic 		 *	Collapse the username into the quadword
331*48681Sbostic 		 */
332*48681Sbostic 		collapse(/*UAF$S_USERNAME*/12,username,output_buf);
333*48681Sbostic 		/*
334*48681Sbostic 		 *	Compute the PURDY polynomial:
335*48681Sbostic 		 */
336*48681Sbostic 		purdy(output_buf,c);
337*48681Sbostic 	}
338*48681Sbostic }
339*48681Sbostic 
340*48681Sbostic /*
341*48681Sbostic  *	CRC routine:
342*48681Sbostic  */
343*48681Sbostic static crc(table,initial_crc,input_len,input_buf,output_buf)
344*48681Sbostic {
345*48681Sbostic 	asm("	crc	*4(ap),8(ap),12(ap),*16(ap)");
346*48681Sbostic 	asm("	clrl	r1");
347*48681Sbostic 	asm("	movq	r0,*20(ap)");
348*48681Sbostic }
349*48681Sbostic 
350*48681Sbostic /*
351*48681Sbostic  *	Routine to collapse a string into a quadword:
352*48681Sbostic  */
353*48681Sbostic static collapse(input_len,input_buf,output_buf)
354*48681Sbostic register unsigned char *input_buf;
355*48681Sbostic register int input_len;
356*48681Sbostic register unsigned char *output_buf;
357*48681Sbostic {
358*48681Sbostic 	while(input_len > 0) {
359*48681Sbostic 		output_buf[input_len & ~(-8)] += *input_buf++;
360*48681Sbostic 		input_len--;
361*48681Sbostic 	}
362*48681Sbostic }
363*48681Sbostic 
364*48681Sbostic 
365*48681Sbostic /*
366*48681Sbostic  *
367*48681Sbostic  *	GROMMY STUFF TO COMPUTE THE PURDY POLYNOMIAL
368*48681Sbostic  *
369*48681Sbostic  */
370*48681Sbostic static purdy(U,C)
371*48681Sbostic {
372*48681Sbostic /*
373*48681Sbostic  * This routine computes f(U) = p(U) mod P.  Where P is a prime of the form
374*48681Sbostic  * P = 2^64 - a.  The function p is the following polynomial:
375*48681Sbostic  * X^n0 + X^n1*C1 + X^3*C2 + X^2*C3 + X*C4 + C5
376*48681Sbostic  * The input U is an unsigned quadword.
377*48681Sbostic  */
378*48681Sbostic 	asm("	.set	A,59");		/* 2^64 -59 is the biggest quad prime */
379*48681Sbostic 
380*48681Sbostic 	asm("	movq	*4(ap),-(sp)");	/* Push U */
381*48681Sbostic 	asm("	bsbw	PQMOD_R0");	/* Ensure U less than P */
382*48681Sbostic 	asm("	movaq	(sp),r4");	/* Maintain a pointer to X*/
383*48681Sbostic 	asm("	pushl	8(ap)");
384*48681Sbostic 	asm("	movl	(sp)+,r5");	/* Point to the table of coefficients */
385*48681Sbostic 	asm("	movq	(r4),-(sp)");
386*48681Sbostic 	asm("	pushl	$((1<<24)-63)");/* n1 */
387*48681Sbostic 	asm("	bsbb	PQEXP_R3");	/* X^n1 */
388*48681Sbostic 	asm("	movq	(r4),-(sp)");
389*48681Sbostic 	asm("	pushl	$((1<<24)-3)");
390*48681Sbostic 	asm("	subl2	$((1<<24)-63),(sp)");/* n0-n1 */
391*48681Sbostic 	asm("	bsbb	PQEXP_R3");
392*48681Sbostic 	asm("	movq	(r5)+,-(sp)");	/* C1 */
393*48681Sbostic 	asm("	bsbw	PQADD_R0");	/* X^(n0 - n1) + C1 */
394*48681Sbostic 	asm("	bsbw	PQMUL_R2");	/* X^n0 + X^n1*C1 */
395*48681Sbostic 	asm("	movq	(r5)+,-(sp)");	/* C2 */
396*48681Sbostic 	asm("	movq	(r4),-(sp)");
397*48681Sbostic 	asm("	bsbw	PQMUL_R2");	/* X*C2 */
398*48681Sbostic 	asm("	movq	(r5)+,-(sp)");	/* C3 */
399*48681Sbostic 	asm("	bsbw	PQADD_R0");	/* X*C2 + C3 */
400*48681Sbostic 	asm("	movq	(r4),-(sp)");
401*48681Sbostic 	asm("	bsbb	PQMUL_R2");	/* X^2*C2 + X*C3 */
402*48681Sbostic 	asm("	movq	(r5)+,-(sp)");	/* C4 */
403*48681Sbostic 	asm("	bsbw	PQADD_R0");	/* X^2*C2 + X*C3 + C4 */
404*48681Sbostic 	asm("	movq	(r4),-(sp)");
405*48681Sbostic 	asm("	bsbb	PQMUL_R2");	/* X^3*C2 + X^2*C3 + C4*X */
406*48681Sbostic 	asm("	movq	(r5)+,-(sp)");	/* C5 */
407*48681Sbostic 	asm("	bsbw	PQADD_R0");	/* X^3*C2 + X^2*C3 + C4*X + C5 */
408*48681Sbostic 	asm("	bsbw	PQADD_R0");	/* Add in the high order terms */
409*48681Sbostic 	asm("	movq	(sp)+,*4(ap)");	/* Replace U with f(X) */
410*48681Sbostic 	asm("	movl	$1,r0");
411*48681Sbostic 	asm("	ret");
412*48681Sbostic 
413*48681Sbostic 
414*48681Sbostic 	/*  Replaces the inputs with U^n mod P where P is of the form */
415*48681Sbostic 	/*  P = 2^64 - a. */
416*48681Sbostic 	/* U is a quadword, n is an unsigned longword. */
417*48681Sbostic 
418*48681Sbostic 	asm("PQEXP_R3:");
419*48681Sbostic 	asm("	popr	$8");		/* Record return address */
420*48681Sbostic 	asm("	movq	$1,-(sp)");	/* Initialize */
421*48681Sbostic 	asm("	movq	8+4(sp),-(sp)");/* Copy U to top of stack for speed */
422*48681Sbostic 	asm("	tstl	8+8(sp)");	/* Only handle n greater than */
423*48681Sbostic 	asm("	beqlu	3f");
424*48681Sbostic 	asm("1:	blbc	8+8(sp),2f");
425*48681Sbostic 	asm("	movq	(sp),-(sp)");	/* Copy the current power of U */
426*48681Sbostic 	asm("	movq	8+8(sp),-(sp)");/* Multiply with current value */
427*48681Sbostic 	asm("	bsbb	PQMUL_R2");
428*48681Sbostic 	asm("	movq	(sp)+,8(sp)");	/* Replace current value */
429*48681Sbostic 	asm("	cmpzv	$1,$31,8+8(sp),$0");
430*48681Sbostic 	asm("	beqlu	3f");
431*48681Sbostic 	asm("2:	movq	(sp),-(sp)");	/* Proceed to next power of U */
432*48681Sbostic 	asm("	bsbb	PQMUL_R2");
433*48681Sbostic 	asm("	extzv	$1,$31,8+8(sp),8+8(sp)");
434*48681Sbostic 	asm("	brb	1b");
435*48681Sbostic 	asm("3:	movq	8(sp),8+8+4(sp)");/* Copy the return value */
436*48681Sbostic 	asm("	movaq	8+8+4(sp),sp");	/* Discard the exponent */
437*48681Sbostic 	asm("	jmp	(r3)");		/* return */
438*48681Sbostic 
439*48681Sbostic 	/* Replaces the quadword U on the stack with U mod P where P is of the */
440*48681Sbostic 	/* form P = 2^64 - a. */
441*48681Sbostic 	asm("	.set	U,0");		/* Low  longword of U */
442*48681Sbostic 	asm("	.set	V,U+4");	/* High longword of U */
443*48681Sbostic 	asm("	.set	Y,U+8");	/* Low  longword of Y */
444*48681Sbostic 	asm("	.set	Z,Y+4");	/* High longword of Y */
445*48681Sbostic 	asm("PQMOD_R0:");
446*48681Sbostic 	asm("	popr	$1");		/* Record return address */
447*48681Sbostic 	asm("	cmpl	V(sp),$-1");	/* Replace U with U mod P */
448*48681Sbostic 	asm("	blssu	1f");
449*48681Sbostic 	asm("	cmpl	U(sp),$-A");
450*48681Sbostic 	asm("	blssu	1f");
451*48681Sbostic 	asm("	addl2	$A,U(sp)");
452*48681Sbostic 	asm("	adwc	$0,V(sp)");
453*48681Sbostic 	asm("1:	jmp	(r0)");		/* return */
454*48681Sbostic 
455*48681Sbostic 
456*48681Sbostic 	/* Computes the product U*Y mod P where P is of the form
457*48681Sbostic 	 * P = 2^64 - a.  U, Y are quadwords less than P.  The product
458*48681Sbostic 	 * replaces U and Y on the stack.
459*48681Sbostic 	 *
460*48681Sbostic 	 * The product may be formed as the sum of four longword
461*48681Sbostic 	 * multiplications which are scaled by powers of 2^32 by evaluating:
462*48681Sbostic 	 * 2^64*v*z + 2^32*(v*y + u*z) + u*y
463*48681Sbostic 	 * The result is computed such that division by the modulus P
464*48681Sbostic 	 * is avoided.
465*48681Sbostic 	 */
466*48681Sbostic 	asm("PQMUL_R2:");
467*48681Sbostic 	asm("	popr	$2");		/* Record return address */
468*48681Sbostic 	asm("	movl	sp,r2");	/* Record initial stack value */
469*48681Sbostic 	asm("	pushl	Z(r2)");
470*48681Sbostic 	asm("	pushl	V(r2)");
471*48681Sbostic 	asm("	bsbb	EMULQ");
472*48681Sbostic 	asm("	bsbb	PQMOD_R0");
473*48681Sbostic 	asm("	bsbb	PQLSH_R0");	/* Obtain 2^32*v*z */
474*48681Sbostic 	asm("	pushl	Y(r2)");
475*48681Sbostic 	asm("	pushl	V(r2)");
476*48681Sbostic 	asm("	bsbb	EMULQ");
477*48681Sbostic 	asm("	bsbb	PQMOD_R0");
478*48681Sbostic 	asm("	pushl	Z(r2)");
479*48681Sbostic 	asm("	pushl	U(r2)");
480*48681Sbostic 	asm("	bsbb	EMULQ");
481*48681Sbostic 	asm("	bsbb	PQMOD_R0");
482*48681Sbostic 	asm("	bsbb	PQADD_R0");	/* Obtain (v*y + u*z) */
483*48681Sbostic 	asm("	bsbb	PQADD_R0");	/* Add in 2^32*v*z */
484*48681Sbostic 	asm("	bsbb	PQLSH_R0");	/* Obtain the first two terms */
485*48681Sbostic 	asm("	pushl	Y(r2)");
486*48681Sbostic 	asm("	pushl	U(r2)");
487*48681Sbostic 	asm("	bsbb	EMULQ");
488*48681Sbostic 	asm("	bsbb	PQMOD_R0");	/* Obtain the third term:  u*y */
489*48681Sbostic 	asm("	bsbb	PQADD_R0");	/* Add it in */
490*48681Sbostic 	asm("	movq	(sp)+,Y(r2)");	/* Copy the return value */
491*48681Sbostic 	asm("	movaq	Y(r2),sp");	/* Point the stack to the return value */
492*48681Sbostic 	asm("	jmp	(r1)");		/* return */
493*48681Sbostic 
494*48681Sbostic 
495*48681Sbostic 	/* This routine knows how to multiply two unsigned longwords,
496*48681Sbostic 	 * replacing them with the unsigned quadword product on the stack.
497*48681Sbostic 	 */
498*48681Sbostic 
499*48681Sbostic 	asm("EMULQ:");
500*48681Sbostic 	asm("	emul	4(sp),8(sp),$0,-(sp)");
501*48681Sbostic 	asm("	clrl	-(sp)");
502*48681Sbostic 	asm("	tstl	4+8+4(sp)");	/* Check both longwords to see if we */
503*48681Sbostic 	asm("	bgeq	1f");		/* must compensate for the unsigned
504*48681Sbostic 								bias. */
505*48681Sbostic 	asm("	addl2	4+8+8(sp),(sp)");
506*48681Sbostic 	asm("1:	tstl	4+8+8(sp)");
507*48681Sbostic 	asm("	bgeq	2f");
508*48681Sbostic 	asm("	addl2	4+8+4(sp),(sp)");
509*48681Sbostic 	asm("2:	addl2	(sp)+,4(sp)");	/* Add in the compensation. */
510*48681Sbostic 	asm("	movq	(sp)+,4(sp)");	/* Replace the longwords with their
511*48681Sbostic 							product. */
512*48681Sbostic 	asm("	rsb");
513*48681Sbostic 
514*48681Sbostic 	/*
515*48681Sbostic 	 * Computes the product 2^32*U mod P where P is of the form
516*48681Sbostic 	 * P = 2^64 - a.  U is a quadword less than P.  The product replaces
517*48681Sbostic 	 * U on the stack.
518*48681Sbostic 	 *
519*48681Sbostic 	 * This routine is used by PQMUL in the formation of quadword
520*48681Sbostic 	 * products in such a way as to avoid division by the modulus P.
521*48681Sbostic 	 * The product 2^64*v + 2^32*u is congruent a*v + 2^32*u mod P
522*48681Sbostic 	 * (where u, v are longwords).
523*48681Sbostic 	 */
524*48681Sbostic 	asm("PQLSH_R0:");
525*48681Sbostic 	asm("	popr	$1");		/* Record return address */
526*48681Sbostic 	asm("	pushl	V(sp)");
527*48681Sbostic 	asm("	pushl	$A");
528*48681Sbostic 	asm("	bsbb	EMULQ");	/* Push a*v */
529*48681Sbostic 	asm("	ashq	$32,Y(sp),Y(sp)");/* Form Y = 2^32*u */
530*48681Sbostic 	asm("	brb	PQADD_R0_1");	/* Return the sum U + Y mod P. */
531*48681Sbostic 
532*48681Sbostic 	/*
533*48681Sbostic 	 * Computes the sum U + Y mod P where P is of the form P = 2^64 - a.
534*48681Sbostic 	 * U, Y are quadwords less than P.  The sum replaces U and Y on
535*48681Sbostic 	 * the stack.
536*48681Sbostic 	 */
537*48681Sbostic 	asm("PQADD_R0:");
538*48681Sbostic 	asm("	popr	$1");		/* Record return address */
539*48681Sbostic 	asm("PQADD_R0_1:");
540*48681Sbostic 	asm("	addl2	U(sp),Y(sp)")	/* Add the low longwords */
541*48681Sbostic 	asm("	adwc	V(sp),Z(sp)");	/* Add the high longwords with the carry */
542*48681Sbostic 	asm("	bcs	2f");		/* If the result is greater than a quadword */
543*48681Sbostic 	asm("	cmpl	Z(sp),$-1");
544*48681Sbostic 	asm("	blssu	3f");
545*48681Sbostic 	asm("	cmpl	Y(sp),$-A");	/* or simply greater than or equal to P */
546*48681Sbostic 	asm("	blssu	3f");
547*48681Sbostic 	asm("2:	addl2	$A,Y(sp)");	/* we must subtract P.*/
548*48681Sbostic 	asm("	adwc	$0,Z(sp)");
549*48681Sbostic 	asm("3:	movaq	Y(sp),sp");	/* Point the stack to the return value */
550*48681Sbostic 	asm("	jmp	(r0)");		/* return */
551*48681Sbostic }
552