Frecon code: sha.pal

From PROSE Programming Language - Wiki
Jump to: navigation, search
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% SHA256 Routines for Frecon
% Author: Mark R. Bannister
% Date: 19/Dec/2017
%
% Provides routines for calculating SHA-256 checksums
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
~Module
EQUS {[frecon]}

~square_prime
EQUD { 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
       0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19 }

~cube_prime
EQUD { 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
       0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
       0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
       0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
       0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
       0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
       0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
       0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
       0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
       0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
       0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
       0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
       0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
       0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
       0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
       0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 }

._init
attr/load       P0, [psString], P1, [psPointer]
func/def        [sha256], &[.sha256], P0, P1, [file]
func/def        [sha256_convert], &[.sha256_convert], P0, P0, [bin]
error/def	[BadFileObject], [Argument is not a file object]
local/rtn

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Calculate SHA-256 checksum of given file object
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
.sha256
var/addr	P10, [file]
attr/addr	P10, P10, [psPointer]

%
% Check that this is a psFile object and that
% it has the psStatUnix class
%
class/load	P8, [psStatUnix]
class/test	P10, [psFile], P8
reg/jmplt	&[.bad_file], SFLG, #2
ne: class/add	P10, P8

%
% Only possible for regular files
%
attr/index	A, P10, [statMode]
opa/and		#0170000
reg/jmpeq	&[.init_hash], A, #0100000

reg/copy	P10, []
reg/save	P10, (#64)
func/rtn	P10

.init_hash
%
% Initialise hash values h0 to h7 (P0-P7) from square_prime data segment
%
attr/load	P8, [psIndex]
reg/load	P12, (&[~square_prime])
reg/load	P0, (P12)
reg/load	P1, (P12)
reg/load	P2, (P12)
reg/load	P3, (P12)
reg/load	P4, (P12)
reg/load	P5, (P12)
reg/load	P6, (P12)
reg/load	P7, (P12)

%
% Initialise array k from cube_prime data segment
%
mtx/local	P14, P8, [w], #64
mtx/local	P15, P8, [k], #64
reg/clr		A
reg/load	P12, (&[~cube_prime])

.init_loop
reg/load	P13, (P12)
reg/jmpeq	&[.read_file], P13, NULL
mtx/set		P15, A, P13
op/incr
reg/jmplt	&[.init_loop], A, #64

.read_file
%
% Process file in 512-bit chunks
%
obj/edit	P10, P10
class/add	P10, [psStreamOpen]
attr/add	P10, [psStreamMode], [r],
		     [psStreamBufferSize], #1048576,    % buffer in 1MB blocks
		     [psStreamReadSize], #64
obj/commitref	P10, P10
attr/load	P11, [psByteStream]
attr/def        P8, [psInteger], #0     % length of original message in bits

.process
reg/jmpne	&[.read_next], P8, NULL % read from file if not reached EOF
reg/jmpeq	&[.digest], PEEK, LOCK	% finished, compute digest
stack/pull	P12			% pull last chunk from stack
local/jmp	&[.process_chunk]

.read_next
%
% Read next 512 bits from input file
%
attr/copy	P12, P10, P11
reg/jmpeq	&[.append_bits_eof], P12, NULL
reg/xload	P13, (P12)
op/mult		PUSH, P13, #8          % convert to number of bits
opx/add         P8, P8, PULL           % add onto message length counter
reg/jmplt	&[.append_bits], P13, #64

.process_chunk
%
% Copy first 16 words into work array w
% At this point, register P12 will contain exactly 512 bits of data
%
reg/clr		A
reg/load	P13, A
.copy16
reg/load	PUSH, (P12, A)         % read 32 bits at offset A, push to stack
mtx/set		P14, P13, PULL         % store in w[0..15]
opa/add		#4                     % increment A by 32 bits
op/incr		P13                    % increment array index
reg/jmplt	&[.copy16], P13, #16   % continue while array index < 16

%
% Extend first 16 words into remaining 48 words of the work array
%
.extend                                % for i (P13) from 16 to 63
reg/load	A, P13
opa/sub		#15
mtx/get		P9, P14, A             % w[i-15]
attr/index	P9, P9                 % convert XVALUE to raw index
op/ror		PUSH, P9, #7           % rightrotate 7, push to stack
op/ror		PUSH, P9, #18          % rightrotate 18, push to stack
op/shr		PUSH, P9, #3           % rightshift 3, push to stack
op/xor		PUSH, PULL, PULL, PULL % xor top 3 items on stack and push back

reg/load	A, P13
opa/sub		#2
mtx/get		P9, P14, A             % w[i-2]
attr/index	P9, P9                 % convert XVALUE to raw index
op/ror		PUSH, P9, #17          % rightrotate 17, push to stack
op/ror		PUSH, P9, #19          % rightrotate 19, push to stack
op/shr		PUSH, P9, #10          % rightshift 10, push to stack
op/xor		PUSH, PULL, PULL, PULL % xor top 3 items on stack and push back

reg/load	A, P13
opa/sub		#16
mtx/get		P9, P14, A             % w[i-16]
attr/index	PUSH, P9               % convert XVALUE to raw index and push

reg/load	A, P13
opa/sub		#7
mtx/get		P9, P14, A             % w[i-7]
attr/index	PUSH, P9               % convert XVALUE to raw index and push

op/add		PUSH, PULL, PULL, PULL, PULL
mtx/set		P14, P13, PULL         % w[i] = sum of top 4 items on stack

op/incr		P13
reg/jmplt	&[.extend], P13, #64   % loop while i < 64

%
% Save current hash value
%
reg/load	PUSH, P7, PUSH, P6, PUSH, P5, PUSH, P4,
		PUSH, P3, PUSH, P2, PUSH, P1, PUSH, P0

%
% Compression loop
%
stack/push	P10                    % save P10
reg/clr		A
.compress
op/ror		PUSH, P4, #6           % e rightrotate 6, push to stack
op/ror		PUSH, P4, #11          % e rightrotate 11, push to stack
op/ror		PUSH, P4, #25          % e rightrotate 25, push to stack
op/xor		PUSH, PULL, PULL, PULL % xor top 3 items on stack and push back
op/and		PUSH, P4, P5           % e and f
op/not		PUSH, P4               % not e
op/and		PUSH, PULL, P6         % and g
op/xor		PUSH, PULL, PULL       % xor top 2 items on stack and push back
mtx/get		PUSH, P15, A           % k[i]
attr/index	PUSH, PULL             % convert XVALUE to raw index
mtx/get		PUSH, P14, A           % w[i]
attr/index	PUSH, PULL             % convert XVALUE to raw index
op/add		P9, P7, PULL, PULL,    % temp1 (P9) = sum of h with top
                    PULL, PULL         %              4 items on stack

op/ror		PUSH, P0, #2           % a rightrotate 2
op/ror		PUSH, P0, #13          % a rightrotate 13
op/ror		PUSH, P0, #22          % a rightrotate 22
op/xor		PUSH, PULL, PULL, PULL % xor top 3 items on stack and push back
op/and		PUSH, P0, P1           % a and b
op/and		PUSH, P0, P2           % a and c
op/and		PUSH, P1, P2           % b and c
op/xor		PUSH, PULL, PULL, PULL % xor top 3 items on stack and push back
op/add		P10, PULL, PULL        % temp2 (P10) = sum of top 2 stack items

reg/roll	#-1, #8                % roll registers P0-P7 right
op/add		P0, P9, P10            % a = temp1 + temp2
op/add		P4, P4, P9             % e += temp1

op/incr
reg/jmplt	&[.compress], A, #64   % loop while i < 64

stack/pull	P10                    % restore register P10

%
% Add the compressed chunk to the current hash value pushed on stack earlier
%
op/add		P0, P0, PULL
op/add		P1, P1, PULL
op/add		P2, P2, PULL
op/add		P3, P3, PULL
op/add		P4, P4, PULL
op/add		P5, P5, PULL
op/add		P6, P6, PULL
op/add		P7, P7, PULL

local/jmp	&[.process]

.append_bits_eof
%
% Reached EOF with no new data, we now require 0x80
% followed by 55 null bytes then the length of the
% original message as a 64-bit big endian integer
%
reg/copy	P12, []
reg/save	P12, (#64)		% set-up new zeroed 512-bit chunk
reg/save	P12, (#0x80000000, #0)	% set the first bit

.end_chunk
attr/export	PUSH, P8, #9		% export data size in bits as big endian
reg/copy	PUSH, (PULL, #1)	% discard the sign byte
reg/save	P12, (PULL, #56)	% write to end of 512-bit chunk
reg/load	P8, NULL		% set P8 to NULL to indicate last chunk
local/jmp	&[.process_chunk]

.append_bits
%
% Reached final chunk of file which is less than 512 bits
%
% If there are at least 9 bytes remaining before the end of the chunk
% then we write 0x80 ... null bytes ... 64-bit integer (message length)
% and finish, otherwise we write 0x80 ... null bytes ... and go round again
%
reg/save	P12, (#64)		% expand current chunk to 512 bits
reg/save	P12, (#0x80000000, P13) % set the first bit of the expansion
reg/jmple	&[.end_chunk], P13, #55 % last chunk to process

.two_more_chunks
attr/export	PUSH, P8, #64		% push 2nd chunk to stack for later
reg/load	P8, NULL		% set P8 to NULL to indicate last chunk
local/jmp	&[.process_chunk]

.digest
%
% Generate the final hash value
%
reg/copy	P8, []
reg/save	P8, (#32)

reg/save	P8, (P0, #0x00)
reg/save	P8, (P1, #0x04)
reg/save	P8, (P2, #0x08)
reg/save	P8, (P3, #0x0c)
reg/save	P8, (P4, #0x10)
reg/save	P8, (P5, #0x14)
reg/save	P8, (P6, #0x18)
reg/save	P8, (P7, #0x1c)
func/rtn	P8

.bad_file
error/now	![.prose.error.frecon.BadFileObject]

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Convert SHA-256 in binary format to a hexadecimal string
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
.sha256_convert
var/addr        P0, [bin]
attr/copy       P0, P0, [psString]
reg/copy        P1, []
reg/save        P1, (#64)
attr/load       P15, [psIndex]
reg/clr         A

.convert_loop
reg/copy        P2, (P0, A, #4)
attr/import     P3, P15, P2
attr/index      P3, P3
reg/conv        P3, P3, #16
op/shl          P4, A, #1
reg/save        P1, (P3, P4)
opa/add         #4
reg/jmplt       &[.convert_loop], A, #32
func/rtn        P1