Guidance
指路人
g.yi.org
Guidance Forums / Reginald Rexx / Base64 encoding

Register 
注册
Search 搜索
首页 
Home Home
Software
Upload

  
Forum List • Thread List • Reply • Refresh • New Topic • Search • Previous • Next First 1 Last
Message1. Base64 encoding
#1894
Posted by: 2003-07-11 12:33:40 Last edited by: Jeff Glatt 2007-06-26 15:08:25 (Total edited 1 time)
Is it possible to manipulate bits in Rexx? 

I want to write a Rexx program that will do Code64 manipulation. (Code 64 is where one creates an array of 64 unique alphanumeric characters (A-Z + a-z, 0-9 + 2 special chars (+ and /)) for which any entry can be accessed using 6 binary bits (2 to power 6 = 64). 

Using this approach allows taking a binary input stream (normal hex data) and for each 6 bits, substituting an alphanumeric char ifrom the table (i.e. if the 6 bits were 011100  (x'1C' or (16 + 12)) then the substituted alphanum char would be the 28th entry in the 64 entry array - from the above char sets mentioned, 00011100  or x'1C' or 28, would yield the 'b' char. 'z' would be the 52nd entry in the array, 9 would be the 62nd while + = 63rd and  /=64th).

Doing the reverse allows restoration of the original binary (hex data). This is achieved by taking each character from the input stream and searching the Code64 array for a character & using its addr position in the table to determine the pattern of 6 bits used to index to it.

The purpose of the Code64 transformation is to make binary data readable and easy to restore back to binary. All my research thus far highlights Rexx as a 'string' manipulation language but I was hoping there might be some libs or functions that supported 'bit' stream manipulation as well.
Message2. Re: Bit manipulation in REXX
#1895
Posted by: 2003-07-13 10:18:51
Have you taken a look at the X2B and B2X conversion functions?  I think they will give you the binary part you are looking for, but may be a bit slow to do the job you are looking to have done.
Message3. Re: Bit manipulation in REXX
#1896
Posted by: 2003-07-15 19:00:47
The following example uses "standard" rexx built-in functions to read in some data from a file, convert it to base64, and output to another file.

I have defined the base64 "lookup array" using the stem variable named base64. Then, I read the data (to be converted to base64) from a stream. Each read process reads the next 3 bytes of the source file. I then transform these 3 bytes (24 bit) to 4x6bit (base 64). This gives me the "index" into the base64 array. Finally, I build a part of a record (ie, the converted data).

When a record is full (maybe for sending inside a SMTP-Mail), you must append a 'LF' character ('0A'X).

If you do not have at least 3 bytes inside your file, you must make a 'base64-like-file-end'.
/* BASE64 ENCODING-----------------------------------*/
base64.0='A';base64.1='B';base64.2='C';base64.3='D';base64.4='E';base64.5='F';base64.6='G'
base64.7='H';base64.8='I';base64.9='J';base64.10='K';base64.11='L';base64.12='M';base64.13='N'
base64.14='O';base64.15='P';base64.16='Q';base64.17='R';base64.18='S';base64.19='T';base64.20='U'
base64.21='V';base64.22='W';base64.23='X';base64.24='Y';base64.25='Z';base64.26='a';base64.27='b'
base64.28='c';base64.29='d';base64.30='e';base64.31='f';base64.32='g';base64.33='h';base64.34='i'
base64.35='j';base64.36='k';base64.37='l';base64.38='m';base64.39='n';base64.40='o';base64.41='p'
base64.42='q';base64.43='r';base64.44='s';base64.45='t';base64.46='u';base64.47='v';base64.48='w'
base64.49='x';base64.50='y';base64.51='z';base64.52='0';base64.53='1';base64.54='2';base64.55='3'
base64.56='4';base64.57='5';base64.58='6';base64.59='7';base64.60='8';base64.61='9';base64.62='+'
base64.63='/';base64.64='='

filein = 'c:testinp.txt'
fileout = 'c:testout.txt'

sdatal.z=""
size=CHARS(filein)

DO WHILE size>2
   sdatalin=CHARIN(filein,,3)
   sdataout=X2B(C2X(sdatalin))
   x1=X2D(B2X(SUBSTR(sdataout,1,6)))
   x2=X2D(B2X(SUBSTR(sdataout,7,6)))
   x3=X2D(B2X(SUBSTR(sdataout,13,6)))
   x4=X2D(B2X(SUBSTR(sdataout,19,6)))
   sdatal.z=sdatal.z||base64.x1||base64.x2||base64.x3||base64.x4
   DROP sdatalin
   DROP sdataout
   size=CHARS(filein)
END

SELECT
   WHEN size=2 THEN DO
      sdatalin=CHARIN(filein,,sbyte)
      sdataout=X2B(C2X(sdatalin))
      x1=X2D(B2X(SUBSTR(sdataout,1,6)))
      x2=X2D(B2X(SUBSTR(sdataout,7,6)))
      h3=SUBSTR(sdataout,13,4)||"00" 
      x3=X2D(B2X(h3))
      x4=X2D(B2X(000000))
      sdatal.z=sdatal.z||base64.x1||base64.x2||base64.x3||base64.x4
   END
   WHEN size=1 THEN DO
      sdatalin=CHARIN(filein,,sbyte)
      sdataout=X2B(C2X(sdatalin))
      x1=X2D(B2X(SUBSTR(sdataout,1,6)))
      h2=SUBSTR(sdataout,7,2)||"0000" 
      x2=X2D(B2X(h2))
      x3=X2D(B2X(000000))
      x4=X2D(B2X(000000))
      sdatal.z=sdatal.z||base64.x1||base64.x2||base64.x3||base64.x4
   END
   OTHERWISE NOP
END
sdatal.z=sdatal.z||base64.64||'0A'x
x=CHAROUT(fileout,sdatal.z)
question:
  CALL STREAM filein,"C","Close"
  CALL STREAM fileout,"C","Close"

Message4. Re: Bit manipulation in REXX
#1898
Posted by: 2003-07-16 06:25:54
Excellent!

What this allows is to actually use a Base64 approach to do some mild encryption.

We plan to scramble the Base64 table & duplicate the table in mem so that there are 2 identical copies one after the other (128 bytes in size).

We then encrypt a line at a time but generate a random num between 0-63 & the 6-bit code for this number becomes the 1st char in that line. Every other char is encoded by 1st adding that random number to the starting position in the Base64 array.

This means that no two lines will come out the same, but providing the table sequence is maintained, the lines will always decode correctly (once the 1st 6-bit index is picked up).

It effectively provides a simple but workable encryption technique.

By scrambling the Base64 table, I mean re-sequencing it so it no longer follows the Base64 convention.

For example...
table = "AbCd0EfGh1IjKlM2nOpQ3rStU4vWxY5zaBc6DeFg7HiJk8LmNo9PqRs+TuVw/XyZ"
...is an example of a scrambled sequence. Then by doubling the table, we get...
table = "AbCd0EfGh1IjKlM2nOpQ3rStU4vWxY5zaBc6DeFg7HiJk8LmNo9PqRs+TuVw/XyZAbCd0EfGh1IjKlM2nOpQ3rStU4vWxY5zaBc6DeFg7HiJk8LmNo9PqRs+TuVw/XyZ"
Table is now 128 bytes long. If we obtain a random number between 0 & 63, then add 1, we have a number between 1 & 64 (eg 25). This number would be used as the starting point in the 128 byte Table.

If we had an input hex stream where the 1st 24 bits were...
00110001 00110010 00110011  (ascii CHARS 1, 2 & 3)
... the 4 positions we would normally index into the table (if it was still only 64 bytes long) would be 001100 010011 001000 110011  (or 12, 19, 8, 51). If we then add the random number (25) to each index, we end up with indexes that will be 25 positions further into the table (37, 44, 33, 76).

If the 1st 6bits of the resulting encrypted string are the random number, & the rest of that line are the 6-bit entries from the table, then when we decrypt we take the 1st 6 bits & use them to establish our base position in the same table, we get the same pattern back out. Because of the random number, no 2 lines will encrypt the same but will always decrypt correctly.

Here are two REXX scripts that show how to encrypt and then decrypt in base64.
/************************************/
/* EncryptV2.rex  Ver 2.1         */
/* Doug Marker - 15 July 2003      */
/************************************/

filein = 'c:testinp.txt'
fileout = 'c:testout.txt'
seedtable='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
basetable=seedtable||seedtable    /* this doubles the size and duplicates the characters */
size=CHARS(filein)
IF size < 1 THEN DO
   SAY "Error: input file is empty"
   RETURN(1)
END

CALL STREAM fileout,"C","Create"
randomnum=RANDOM(1,64)
coretable=SUBSTR(basetable, randomnum, 64)
keychar=SUBSTR(seedtable,randomnum,1)
output=keychar

DO WHILE size>2
   sdatalin=CHARIN(filein,,3); SAY sdatalin
   sdataout=X2B(C2X(sdatalin)); 
   index1=X2D(B2X(SUBSTR(sdataout,1,6)))+1
   index2=X2D(B2X(SUBSTR(sdataout,7,6)))+1
   index3=X2D(B2X(SUBSTR(sdataout,13,6)))+1
   index4=X2D(B2X(SUBSTR(sdataout,19,6)))+1
   sdata=SUBSTR(coretable,index1,1)||substr(coretable,index2,1)||substr(coretable,index3,1)||substr(coretable,index4,1)
   DROP sdatalin
   DROP sdataout
   output=output||sdata
   size=CHARS(filein)
END
SELECT 
   WHEN size=2 THEN DO
      sdatalin=CHARIN(filein,,2) || ' '
      sdataout=X2B(C2X(sdatalin))
      index1=X2D(B2X(SUBSTR(sdataout,1,6)))+1
      index2=X2D(B2X(SUBSTR(sdataout,7,6)))+1
      index3=X2D(B2X(SUBSTR(sdataout,13,6)))+1
      index4=X2D(B2X(SUBSTR(sdataout,19,6)))+1
      sdata=SUBSTR(coretable,index1,1)||substr(coretable,index2,1)||substr(coretable,index3,1)||substr(coretable,index4,1)
      output=output||sdata
   END
   WHEN size=1 THEN DO
      sdatalin=CHARIN(filein,,2) || '  '
      sdataout=X2B(C2X(sdatalin)); SAY sdataout
      index1=X2D(B2X(SUBSTR(sdataout,1,6)))+1
      index2=X2D(B2X(SUBSTR(sdataout,7,6)))+1
      index3=X2D(B2X(SUBSTR(sdataout,13,6)))+1
      index4=X2D(B2X(SUBSTR(sdataout,19,6)))+1
      SAY index1 index2 index3 index4
      sdata=SUBSTR(coretable,index1,1)||substr(coretable,index2,1)||substr(coretable,index3,1)||substr(coretable,index4,1)
      output=output||sdata
   END
   OTHERWISE NOP
END

x=CHAROUT(fileout,output)
SAY output
CALL STREAM filein,"C","Close"
CALL STREAM fileout,"C","Close"
RETURN

/************************************/
/* DecryptV2.rex  Ver 2.0         */
/* Doug Marker - 15 July 2003      */
/************************************/

filein = 'c:testout.txt'
output=''

size=CHARS(filein)-1
IF size>1 THEN sizerem=size//4  /* subtract 1 to account for the key (offset) */
IF (size=0 | sizerem>0) THEN DO
   SAY "filesize error - should be multiple of 4. Filesize is = " size
   RETURN (1)
END

seedtable='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
basetable=seedtable||seedtable      /* this doubles the seedtable to 128 chars */
fileout = 'c:testdec.txt'
CALL STREAM fileout,"C","Create"

keychar=CHARIN(filein,,1)
offset=POS(keychar,seedtable)
coretable=SUBSTR(basetable,offset,64)
i=0                        /* set i to 0, we will use it to count chars in/out */

DO WHILE size>3
   /* Read in 4 chars */
   i=i+1  /* used to count input & output counts */
   crypt1=CHARIN(filein,,1)
   crypt2=CHARIN(filein,,1)
   crypt3=CHARIN(filein,,1)
   crypt4=CHARIN(filein,,1)
   index1=POS(crypt1,(coretable))-1
   index2=POS(crypt2,(coretable))-1
   index3=POS(crypt3,(coretable))-1
   index4=POS(crypt4,(coretable))-1
   binary1=X2B(D2X(index1,2))
   binary2=X2B(D2X(index2,2))
   binary3=X2B(D2X(index3,2))
   binary4=X2B(D2X(index4,2))
   binsub1=SUBSTR(binary1,3,6)
   binsub2=SUBSTR(binary2,3,6)
   binsub3=SUBSTR(binary3,3,6)
   binsub4=SUBSTR(binary4,3,6)
   hexstr = binsub1||binsub2||binsub3||binsub4
   byte1=SUBSTR(hexstr,1,8)
   byte2=SUBSTR(hexstr,9,8)
   byte3=SUBSTR(hexstr,17,8)
   char1=X2C(B2X(byte1))
   char2=X2C(B2X(byte2))
   char3=X2C(B2X(byte3))
   output=output||char1||char2||char3
   size=CHARS(filein)
END
x=CHAROUT(fileout, output); sizeout=LENGTH(output)
SAY "Encrypted chars in  = " || (i*4) || "."
SAY "Decrypted chars out = " || sizeout ||"."
SAY "De-encrypted output =" output

question:
CALL STREAM filein,"C","Close"
CALL STREAM fileout,"C","Close"
Message5. Re: Bit manipulation in REXX
#3575
Posted by: Jeff Glatt 2004-03-24 18:07:52
I've added a new function to Reginald called BIT() that does a lot of this bit manipulation with a lot less hassle than BITAND and BITOR (because BIT can directly work on REXX numbers without any conversion first)
Forum List • Thread List • Reply • Refresh • New Topic • Search • Previous • Next First 1 Last
掌柜推荐
 
 
 
 
 
 
 
 
 
 
 
 
© Fri 2024-3-29  Guidance Laboratory Inc.
Email:webmaster1g.yi.org Hits:0