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 */
get_vms_uaf_record(name,uaf)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 */
validate_vms_user(name,password,uaf)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 */
hash_vms_password(output_buf,input_buf,input_length,username,type,salt)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 */
crc(table,initial_crc,input_len,input_buf,output_buf)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 */
collapse(input_len,input_buf,output_buf)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 */
purdy(U,C)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