Frecon code: sha.pal
From PROSE Programming Language - Wiki
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% 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