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