// Password.Bcpl -- OS password routines
//	Bob Sproull and Leo Guibas
// Copyright Xerox Corporation 1979, 1981
// Last modified October 4, 1981  2:58 PM by Taft

external
[
//outgoing procedures
Password

//incoming procedures
Zero; Timer

//incoming system static
UserName
]

// One function (Password) does it all:
//  result = Password(string,vector,new)
//
// If "new" is true, it fills vector (9 words long) with encrypted
//	password.  Sets vector!0 to -1 to indicate there is a password
// If "new" is false, compares string with password.  Returns
//	true if password string was good.
//
// The standard OS stores the 9-word vector starting at word
//	#600 in the file Sys.Boot
//	(location #1200 in memory if Sys.Boot is InLd'ed)

//	9-word vector format:
//		flag ne 0 (1 word)
//		a: next 2 words (derived from time when password set)
//		b: next 2 words (derived from UserName when password set)
//		c: next 4 words (actual encrypted password)
//	Note that a and b simply ensure that a given password encrypts
//	differently at different times and for different users.
//
// -- computation is:
//	x ← 16-bit number extracted from ps
//	y ← 32-bit number extracted from ps
//	c ← -a*x*x + b*y

structure String [ length byte ; char↑1,255 byte ]

//---------------------------------------------------------------------------
let Password(ps, v, newFlag) = valof
//---------------------------------------------------------------------------
[
// temps used by machine code -- must immediately follow args in frame
let tempa, tempb, tempc = nil, nil, nil
manifest [ pa = 7; pb = 8; pc = 9 ]

let Add4 = table  // Add4(lvA, lvB, lvC): a ← b+c (b and c are 4 words long)
   [
    55001B  //		sta 3 1 2
   176520B  //		mkone 3 3
   175140B  //		movol 3 3	; ac3 ← 3
   163000B  //		add 3 0
    41007B  //		sta 0 pa 2	; -> last word of a
   167000B  //		add 3 1
    45010B  //		sta 1 pb 2	; -> last word of b
    21003B  //		lda 0 3 2
   163000B  //		add 3 0
    41011B  //		sta 0 pc 2	; -> last word of c
   174020B  //		comz 3 3	; ac3 ← -4, carry ← 0
    23010B  //	loop:	lda 0 @pb 2	; current word of b
    27011B  //		lda 1 @pc 2	; current word of c
   101012B  //		mov# 0 0 szc	; carry from previous iteration?
   101420B  //		 incz 0 0	; yes, add it in and zero carry
   123000B  //		add 1 0		; b+c
    43007B  //		sta 0 @pa 2
    15007B  //		dsz pa 2
    15010B  //		dsz pb 2
    15011B  //		dsz pc 2
   175404B  //		inc 3 3 szr	; skip if 4th iteration
      766B  //		 jmp loop
    35001B  //		lda 3 1 2
     1401B  //		jmp 1 3
   ]

let Mult1 = table  // Mult1(lvA, lvB, lvC): a ← b*c (b and c are 1 word each)
   [
    55001B  //		sta 3 1 2
    41002B  //		sta 0 2 2	; -> a
   135000B  //		mov 1 3		; -> b
    25400B  //		lda 1 0 3	; b
   102460B  //		mkzero 0 0
   155000B  //		mov 2 3		; preserve stack
    33403B  //		lda 2 @3 3	; c
    61020B  //		mul		; ac0,,ac1 ← ac0 + ac1*ac2
   171000B  //		mov 3 2		; restore stack
    43002B  //		sta 0 @2 2	; store high word of a
    11002B  //		isz 2 2
    47002B  //		sta 1 @2 2	; store low word of a
    35001B  //		lda 3 1 2
     1401B  //		jmp 1 3
   ]

// Password (cont'd)

let Mult2 = table  // Mult2(lvA, lvB, lvC): a ← b*c (b and c are 2 words each)
   [
    55001B  //		sta 3 1 2
   155000B  //		mov 2 3		; preserve stack
    41407B  //		sta 0 pa 3	; -> a
    45410B  //		sta 1 pb 3	; -> b
   102460B  //		mkzero 0 0
   131000B  //		mov 1 2
    25001B  //		lda 1 1 2	; low word of b
    31403B  //		lda 2 3 3	; -> c
    31001B  //		lda 2 1 2	; low word of c
    61020B  //		mul		; ac0,,ac1 ← ac0 + ac1*ac2
    31407B  //		lda 2 pa 3
    45003B  //		sta 1 3 2	; store low word of low product
    27410B  //		lda 1 @pb 3	; high word of b
    31403B  //		lda 2 3 3
    31001B  //		lda 2 1 2	; low word of c
    61020B  //		mul		; ac0,,ac1 ← ac0 + ac1*ac2
    41402B  //		sta 0 2 3	; save high word of cross product
   121000B  //		mov 1 0		; add low word into other cross product
    31410B  //		lda 2 pb 3
    25001B  //		lda 1 1 2	; low word of b
    33403B  //		lda 2 @3 3	; high word of c
    61020B  //		mul		; ac0,,ac1 ← ac0 + ac1*ac2
    31407B  //		lda 2 pa 3
    45002B  //		sta 1 2 2	; store low word of cross products
    25402B  //		lda 1 2 3	; combine high words of cross products
   123020B  //		addz 1 0	; this may carry out
    27410B  //		lda 1 @pb 3	; high word of b
    33403B  //		lda 2 @3 3	; high word of c
    61020B  //		mul		; ac0,,ac1 ← ac0 + ac1*ac2
   171002B  //		mov 3 2 szc	; restore stack, test carry from add
   101400B  //		 inc 0 0
    35007B  //		lda 3 pa 2
    45401B  //		sta 1 1 3	; store low word of high product
    41400B  //		sta 0 0 3	; store high word of high product
    35001B  //		lda 3 1 2
     1401B  //		jmp 1 3
   ]

let a = v+1
let b = v+3
let c = vec 4

if newFlag then
   [
   v!0 = -1
   for i = 1 to UserName>>String.length do
      [
      a!1 = a!0 lshift 9 + a!1 rshift 7
      a!0 = (UserName>>String.char↑i) lshift 9 + a!0 rshift 7
      ] 
   Timer(b)      // Manufactured b
   c = v+5
   ]

// Password (cont'd)

// Compute c from a and b:
let x = vec 4
let y = x+2
Zero(x, 4)

for i = 1 to ps>>String.length do
   [
   let c = ps>>String.char↑i
   if c eq $*S then break      //In case he typed in a space.
   let p = ((i rem 3) eq 1)? x, y
   if c ge $a & c le $z then c = c-($a-$A)
   p!1 = p!0 lshift 9 + p!1 rshift 7
   p!0 = c lshift 9 + p!0 rshift 7
   ]

// Now we have a,b,x,y

let t = vec 4
Mult1(t, x, x)   // t ← x*x
Mult2(t, t, a)   // t ← a*x*x

// Now negate t.  Bug -- should be:
//	for i = 0 to 3 do t!i = not (t!i)
// but fixing it now would invalidate all existing passwords!
t!0 = not t!0; t!1 = not t!1
Add4(t, t, table [ 0; 0; 0; 1 ])

let s = vec 4
Mult2(s, b, y)   // s ← b*y
Add4(c, t, s)    // c ← t+s

// All finished; now check it.
for i = 0 to 3 do if v!(i+5) ne c!i then resultis false
resultis true
]