/* 2 July 2002. Daniel Hellerstein (danielh@crosslink.net)

    de_sreA.CMD: The client-side component of the "SRE_A" 
                 SREhttp/2 encryption method

This os/2 rexx program will decrypt responses from SREhttp/2 servers
that have been encrypted using the "SRE_A" encryption method.

To use this program, you'll need to do the following (assuming
you are running OS/2, and you have netscape for OS/2):

a) Copy this file to an applications directory (it does NOT need
   to be in your path). For example, C:\OS2\APPS

b) Tell Netscape to use this DE_SREA whenever it is recieves
   a response from a server that has a mime type of
   application/x-encrypt_SRE_A  
  
   To do this, you should set the following in NetScape:
     1) Open NetScape "applications". 
          for NS4.04 -- look in Edit-Preferences-Applications
          for NS2.02 -- look in Options-General_Preferences-Applications

     2) Create a "new type" with:

         Mime Type: application/x-encrypt_SRE_A
         Application to Use: cmd.exe /c "x:\dir\DE_SREA.cmd PWD:sspwd"
 
           NOTE: the  double quote (") characters MUST be included in 
                 this definition!

        where :

           sspwd   : is your "shared-secret password"
                     If you do NOT specify the PWD:sspwd, then
                     DE_SREA will ask you to provide a "shared-secret"
                     password
           x:\dir\ : is the path you copied this file to (for example,
                     C:\OS2\APPS).

After completing steps a and b, you are ready to recieve encrypted
files from an SREhttp/2 web server. 

When you do recieve an "SRE_A encrypted" response from an SREhttp/2
web server, NetScape should pop up a window that asks you to  
"load" or"save" the file -- you should choose "load".  De_sreA.CMD will then 
be invoked.

After making sure you entered the correct password, DE_SREA will
decrypt the message, and will then ask you whether to display
the message in a new NetScape window, or whether to save it to disk.

Although DE_SREA.CMD was developed under OS/2, it might work under
different flavors of REXX -- we'll be checking on that.

*/

/* ---------- Begin user changeable parameters --------- */

/* set this to be the  fully qualified default output directory */
default_outdir=''


/* ---------- END of user changeable parameters --------- */


parse arg dafile 

hispwd=''

dafile=strip(dafile)
if abbrev(translate(dafile),'PWD:')=1 then do
   parse var dafile pwd dafile
   parse var pwd . ':' hispwd
   hispwd=strip(translate(hispwd))
end /* do */



say "  <<<< The SREhttp/2 decrypter (for the SRE_A encryption method) >>>>"
say ' '
if default_outdir='' then default_outdir=directory()
outdir=strip(default_outdir,'t','\')||'\'
crlf='0d0a'x
daname='TEMP.OUT'

foo=rxfuncquery('sysloadfuncs') /* use rexxutil if it's available */
if foo=1 then do
  call RxFuncAdd 'SysLoadFuncs', 'RexxUtil', 'SysLoadFuncs'
  call SysLoadFuncs
end

if dafile='' then do
   call charout, "Enter name of application/x-encrypt_SRE_A file: "
   pull dafile
end 

dafile=strip(dafile)

/* 1) read the file */
a=stream(dafile,'c','open read')
if abbrev(translate(a),'READY')<>1 then do
    say "Problem opening: "dafile '= 'a
    call charout, ' (hit ENTER to continue)' ; pull foo
    exit
end /* do */
ilen=stream(dafile,'c','query size')
if ilen=0 | ilen='' then do
    say "Problem querying: "dafile
    call charout, ' (hit ENTER to continue)' ; pull foo
    exit
end /* do */
stuff=charin(dafile,1,ilen)
foo=stream(dafile,'c','close')
hash16='' ; ctype='' ; nonce='' ; seed='' ; epwd=''; clength=''
server=''
selector=''

/* parse into head and body */
abound=''

do forever
   if stuff='' then do
        say "Problem: no body for this file: " dafile
        call charout, ' (hit ENTER to continue)';pull foo
        exit
   end /* do */
   parse var stuff aline (crlf) stuff

   taline=strip(translate(aline))
   if taline='' then leave      /* empty line signifies end of head */

   select
     when abbrev(taline,'X-NONCE')=1  then do
          parse var aline . ':' NONCE
     end 

     when abbrev(taline,'X-HASH16')=1 then do
        parse var aline . ':' hash16
     end 

     when abbrev(taline,'CONTENT-TYPE')=1 then do
          parse var aline . ':' ctype
     end /* do */

     when abbrev(taline,'CONTENT-LENGTH')=1  then do
          parse var aline . ':' clength
        
     end /* do */

     when abbrev(taline,'SERVER')=1     then do
          parse var aline . ':' SERVER   
     end /* do */
    
     when abbrev(taline,'X-SELECTOR')=1   then do
          parse var aline . ':' SELECTOR   
     end /* do */

     when abbrev(taline,'X-SEED')=1  then do   /* this should never be available */
          parse var aline . ':' SEED    /* -- only used for debug runs */
     end /* do */

     when abbrev(taline,'X-EPWD')=1  then do   /* this should never be available */
          parse var aline . ':' EPWD    /* -- only used for debug runs */
     end /* do */

     when abbrev(taline,'CONTENT-DISPOSITION')=1 then do      /* get "Filename" */
          parse var taline . 'FILENAME' daname ';' .
          daname=space(daname,0)
          daname=strip(daname,,'=')
          daname=strip(daname,,'"')
     end /* do */

     otherwise nop
   end  /* select */
end /* do */

/* nonce and hash16 must be present */
if nonce='' | hash16='' then do 
    say ' Problem: nonce or hash16 is missing '
    call charout, ' (hit ENTER to continue)' ; pull foo
    exit
end /* do */

say "De-encrypting response from " server "..."
say "    (mimetype= "ctype', length='clength 'bytes)'
say


getspwd:
if hispwd='' then do
  call charout,' Please enter your "shared-secret" password: '
  pull hispwd ; hispwd=space(hispwd,0)
end

/* 1) combine nonce and hispwd */
ss=strip(nonce)||hispwd

/* 2) compute md5 hash */
md5=sref_md5x(ss)

/* 3) pull out first 16 characters */
a16=translate(left(md5,16))

/* 4) compare to hash16 -- if wrong, ask for new password */
if translate(a16)<>hash16 then do
    say '  ! Incorrect "shared-secret" password. Please re-enter.'
    say
    hispwd=''
    signal getspwd
end

/* 5) extract last 3 seed numbers  */
numeric digits 13
ix=x2d(substr(md5,30,3))
iy=x2d(substr(md5,27,3))
iz=x2d(substr(md5,25,2))

/* 6) de-encrypt the file using a random number sequence */
numeric digits 12
mx32=4294967295

/* pack to multiple of 4 length */
i4s=trunc(length(stuff)/4)
i4sb=length(stuff)//4
if i4sb>0 then do
 i4s=i4s+1
 stuff=stuff||copies(' ',i4sb)
end

amask=''
do mm=1 to length(stuff)/4
  arand=random3(mx32)
  darand=right(d2c(arand),4,0)
  amask=amask||darand
  if (mm//2500)=0 then say "   ... processing byte # "mm*4 
end

aanew=bitxor(stuff,amask)

aanew=left(aanew,clength)  /* get rid of pad characters */

say
call charout, "Display in a new NetScape window (Y/N)? "
pull yn
if yn='Y' then do
    tmpfile=dafile
    ijj=lastpos('.',dafile)
    if ijj>0 then tmpfile=left(dafile,ijj-1)
    do mm=1 to 99
       t2=tmpfile||'.$'||mm
       if stream(t2,'c','query exists')='' then leave
    end /* do */
    
/* write to t2 */
    foo=stream(t2,'c','open write')
    if abbrev(translate(foo),'READY')=0 then do
        say "Problem: could not create temporary file: "t2
        call charout, ' (hit ENTER to continue)' ; pull foo
        exit
    end /* do */
    foo=charout(t2,aanew,1)
    if foo<>0 then do
        say "Problem: could not write temporary file: "t2
        call charout, ' (hit ENTER to continue)' ; pull foo
        exit
    end /* do */
    yy=stream(t2,'c','close')
    t2=translate(stream(t2,'c','query exists'),'/','\')

    doog='@NETSCAPE  file:///'t2
    address cmd '@NETSCAPE  file:///'t2
    foo=deleteme(t2)             /* cleanup */
end /* do */
else do
   useout=getofile(outdir||daname)
   if useout<>"" then do
           foo=stream(useout,'c','open write')
           foo=charout(useout,aanew,1)
           if foo<>0 then do
                say "Problem writing "useout
           end /* do */
           else
                say "   .... "useout " written successfully "
          foo=stream(useout,'c','close')
           ill=lastpos('\',useout)
           if ill>0 then  outdir=left(useout,ill)
   end /* do */
end /* do */

exit


/****************************************************/
/************** Ask for an output file *************/
getofile:procedure
parse arg defout
do forever
  aa="   Output to file (ENTER="defout"):"
  if length(aa)>40 then do
     say aa
     call charout, "    ? "
  end
  else do
    call charout,aa' ?'
  end

  pull gfile2 ; gfile2=strip(gfile2)
  if right(gfile2,1)='\' & defout<>'' then do
     iu=lastpos('\',defout)
     gfile2=gfile2||substr(defout,iu+1)
  end
  if gfile2='.'  then return ''
  if gfile2="" then gfile2=defout
  if gfile2="" then iterate
  gfile0=stream(gfile2,'c','query exists')
  if gfile0<>"" then do
      call charout,'   'Gfile0 ' exists.  Overwrite (Y/N)'
      pull anans
      if abbrev(strip(anans),'Y')<>1 then iterate
      foo=deleteme(gfile0)
      if foo=1 then do
         say '    Could not delete 'gfile0'. Try a different file name'
         iterate
      end
  end
  return gfile2
end /* do */


deleteme:procedure
parse arg afile
if rxfuncquery('SYSFILEDELETE')=0 then foo=sysfiledelete(gfile0)
if stream(gfile,'c','query exists')<>''then return 0
return 1

/****************************************************/
/****************************************************/
/**************  Compute an MD5 hash *************/
/* A fully rexx md5 digest computation procedure.
  This is NOT FAST  --  for small strings it is
  tolerable (0.15 seconds on a p166 for 50 character strings),
  but for larger strings (or files) it can take many seconds --
  you should instead use a DLL product (such as MD5_OS2) */


/*  ------------------------------ */
sref_md5x:procedure
parse arg stuff

numeric digits 11
lenstuff=length(stuff)

c0=d2c(0)
c1=d2c(128)
c1a=d2c(255)
c1111=c1a||c1a||c1a||c1a
slen=length(stuff)*8
slen512=slen//512

/* pad message to multiple of 512 bits.  Last 2 words are 64 bit # bits in message*/
if slen512=448 then  addme=512
if slen512<448 then addme=448-slen512
if slen512>448 then addme=960-slen512
addwords=addme/8

apad=c1||copies(c0,addwords-1)

xlen=reverse(right(d2c(lenstuff*8),4,c0))||c0||c0||c0||c0  /* 2**32 max bytes in message */

/* NEWSTUFF is the message to be md5'ed */
newstuff=stuff||apad||xlen

/* starting values of registers */
 a ='67452301'x;
 b ='efcdab89'x;
 c ='98badcfe'x;
 d ='10325476'x;

lennews=length(newstuff)/4

/* loop through entire message */
do i1 = 0 to ((lennews/16)-1)
  i16=i1*64
  do j=1 to 16
     j4=((j-1)*4)+1
     jj=i16+j4
     m.j=reverse(substr(newstuff,jj,4))
  end /* do */

/* transform this block of 16 chars to 4 values. Save prior values first */
 aa=a;bb=b;cc=c;dd=d

/* do 4 rounds, 16 operations per round (rounds differ in bit'ing functions */
S11=7
S12=12
S13=17
S14=22
  a=round1( a, b, c, d,   0 , S11, 3614090360); /* 1 */
  d=round1( d, a, b, c,   1 , S12, 3905402710); /* 2 */
  c=round1( c, d, a, b,   2 , S13,  606105819); /* 3 */
  b=round1( b, c, d, a,   3 , S14, 3250441966); /* 4 */
  a=round1( a, b, c, d,   4 , S11, 4118548399); /* 5 */
  d=round1( d, a, b, c,   5 , S12, 1200080426); /* 6 */
  c=round1( c, d, a, b,   6 , S13, 2821735955); /* 7 */
  b=round1( b, c, d, a,   7 , S14, 4249261313); /* 8 */
  a=round1( a, b, c, d,   8 , S11, 1770035416); /* 9 */
  d=round1( d, a, b, c,   9 , S12, 2336552879); /* 10 */
  c=round1( c, d, a, b,  10 , S13, 4294925233); /* 11 */
  b=round1( b, c, d, a,  11 , S14, 2304563134); /* 12 */
  a=round1( a, b, c, d,  12 , S11, 1804603682); /* 13 */
  d=round1( d, a, b, c,  13 , S12, 4254626195); /* 14 */
  c=round1( c, d, a, b,  14 , S13, 2792965006); /* 15 */
  b=round1( b, c, d, a,  15 , S14, 1236535329); /* 16 */

  /* Round 2 */
S21=5
S22=9
S23=14
S24=20
a= round2( a, b, c, d,   1 , S21, 4129170786); /* 17 */
d= round2( d, a, b, c,   6 , S22, 3225465664); /* 18 */
c=  round2( c, d, a, b,  11 , S23,  643717713); /* 19 */
b=  round2( b, c, d, a,   0 , S24, 3921069994); /* 20 */
a=  round2( a, b, c, d,   5 , S21, 3593408605); /* 21 */
d=  round2( d, a, b, c,  10 , S22,   38016083); /* 22 */
c=  round2( c, d, a, b,  15 , S23, 3634488961); /* 23 */
b= round2( b, c, d, a,   4 , S24, 3889429448); /* 24 */
a= round2( a, b, c, d,   9 , S21,  568446438); /* 25 */
d= round2( d, a, b, c,  14 , S22, 3275163606); /* 26 */
c=  round2( c, d, a, b,   3 , S23, 4107603335); /* 27 */
b=  round2( b, c, d, a,   8 , S24, 1163531501); /* 28 */
a=  round2( a, b, c, d,  13 , S21, 2850285829); /* 29 */
d=  round2( d, a, b, c,   2 , S22, 4243563512); /* 30 */
c=  round2( c, d, a, b,   7 , S23, 1735328473); /* 31 */
b= round2( b, c, d, a,  12 , S24, 2368359562); /* 32 */

  /* Round 3 */
S31= 4
S32= 11
S33= 16
S34= 23
a= round3( a, b, c, d,   5 , S31, 4294588738); /* 33 */
d=  round3( d, a, b, c,   8 , S32, 2272392833); /* 34 */
c=  round3( c, d, a, b,  11 , S33, 1839030562); /* 35 */
b=  round3( b, c, d, a,  14 , S34, 4259657740); /* 36 */
a=  round3( a, b, c, d,   1 , S31, 2763975236); /* 37 */
d=  round3( d, a, b, c,   4 , S32, 1272893353); /* 38 */
c=  round3( c, d, a, b,   7 , S33, 4139469664); /* 39 */
b=  round3( b, c, d, a,  10 , S34, 3200236656); /* 40 */
a=  round3( a, b, c, d,  13 , S31,  681279174); /* 41 */
d=  round3( d, a, b, c,   0 , S32, 3936430074); /* 42 */
c=  round3( c, d, a, b,   3 , S33, 3572445317); /* 43 */
b=  round3( b, c, d, a,   6 , S34,   76029189); /* 44 */
a=  round3( a, b, c, d,   9 , S31, 3654602809); /* 45 */
d=  round3( d, a, b, c,  12 , S32, 3873151461); /* 46 */
c=  round3( c, d, a, b,  15 , S33,  530742520); /* 47 */
b=  round3( b, c, d, a,   2 , S34, 3299628645); /* 48 */

  /* Round 4 */
S41=6
S42=10
S43=15
s44=21
a=round4( a, b, c, d,   0 , S41, 4096336452); /* 49 */
d=round4( d, a, b, c,   7 , S42, 1126891415); /* 50 */
c=round4( c, d, a, b,  14 , S43, 2878612391); /* 51 */
b=round4( b, c, d, a,   5 , s44, 4237533241); /* 52 */
a=round4( a, b, c, d,  12 , S41, 1700485571); /* 53 */
d=round4( d, a, b, c,   3 , S42, 2399980690); /* 54 */
c=round4( c, d, a, b,  10 , S43, 4293915773); /* 55 */
b=round4( b, c, d, a,   1 , s44,  2240044497); /* 56 */
a=round4( a, b, c, d,   8 , S41, 1873313359); /* 57 */
d=round4( d, a, b, c,  15 , S42, 4264355552); /* 58 */
c=round4( c, d, a, b,   6 , S43, 2734768916); /* 59 */
b=round4( b, c, d, a,  13 , s44, 1309151649); /* 60 */
a=round4( a, b, c, d,   4 , S41, 4149444226); /* 61 */
d=round4( d, a, b, c,  11 , S42, 3174756917); /* 62 */
c=round4( c, d, a, b,   2 , S43,  718787259); /* 63 */
b=round4( b, c, d, a,   9 , s44, 3951481745); /* 64 */


a=m32add(aa,a) ; b=m32add(bb,b) ; c=m32add(cc,c) ; d=m32add(dd,d)

end

aa=c2x(reverse(a))||c2x(reverse(b))||c2x(reverse(C))||c2x(reverse(D))

return lower(aa)


/* round 1 to 4 functins */

round1:procedure expose m. c1111 c0 c1
parse arg a1,b1,c1,d1,kth,shift,sini
kth=kth+1
t1=c2d(a1)+c2d(f(b1,c1,d1))+ c2d(m.kth) + sini
t1a=right(d2c(t1),4,c0)
t2=rotleft(t1a,shift)
t3=m32add(t2,b1)
return t3

round2:procedure expose m. c1111 c0 c1
parse arg a1,b1,c1,d1,kth,shift,sini
kth=kth+1
t1=c2d(a1)+c2d(g(b1,c1,d1))+ c2d(m.kth) + sini
t1a=right(d2c(t1),4,c0)
t2=rotleft(t1a,shift)
t3=m32add(t2,b1)
return t3

round3:procedure expose m. c1111 c0 c1
parse arg a1,b1,c1,d1,kth,shift,sini
kth=kth+1
t1=c2d(a1)+c2d(h(b1,c1,d1))+ c2d(m.kth) + sini
t1a=right(d2c(t1),4,c0)
t2=rotleft(t1a,shift)
t3=m32add(t2,b1)
return t3

round4:procedure expose m. c1111 c0 c1
parse arg a1,b1,c1,d1,kth,shift,sini
kth=kth+1
t1=c2d(a1)+c2d(i(b1,c1,d1))+ c2d(m.kth) + sini
t1a=right(d2c(t1),4,c0)
t2=rotleft(t1a,shift)
t3=m32add(t2,b1)
return t3

/* add to "char" numbers, modulo 2**32, return as char */
m32add:procedure expose c0 c1 c1111
parse arg v1,v2
t1=c2d(v1)+c2d(v2)
t2=d2c(t1)
t3=right(t2,4,c0)
return t3

/*********** Basic functions */
/* F(x, y, z) == (((x) & (y)) | ((~x) & (z))) */
f:procedure expose c0 c1 c1111 
parse arg x,y,z
t1=bitand(x,y)
notx=bitxor(x,c1111)
t2=bitand(notx,z)
return bitor(t1,t2)


/* G(x, y, z) == (((x) & (z)) | ((y) & (~z)))*/
g:procedure expose c0 c1 c1111
parse arg x,y,z
t1=bitand(x,z)
notz=bitxor(z,c1111)
t2=bitand(y,notz)
return bitor(t1,t2)

/* H(x, y, z) == ((x) ^ (y) ^ (z)) */
h:procedure expose c0 c1 c1111
parse arg x,y,z
t1=bitxor(x,y)
return bitxor(t1,z)

/* I(x, y, z) == ((y) ^ ((x) | (~z))) */
i:procedure expose c0 c1 c1111
parse arg x,y,z
notz=bitxor(z,c1111)
t2=bitor(x,notz)
return bitxor(y,t2)

/* bit rotate to the left by s positions */
rotleft:procedure 
parse arg achar,s
if s=0 then return achar

bits=x2b(c2x(achar))
lb=length(bits)
t1=left(bits,s)
t2=bits||t1
yib=right(t2,lb)
return x2c(b2x(yib))



/****************************************************/
/****************************************************/
/****************************************************/
/* *********************** */
/* UNIFORM DISTRIBUTION RANDOM # GENERATOR.  
  FROM APPL STATIS 1982, VOL31 Pg183
  Requires one to set ix iy and iz sees beforehand 
*/
random3:procedure expose ix iy iz 
parse arg mx32
IX=(171*IX)//30269
IY=(172*IY)//30307
IZ=(170*IZ)//30323
RANDOM=(IX/30269.) + (IY/30307.)  + (IZ/30323)
random=trunc((random // 1.0)*mx32)

RETURN random

