125 lines
4.4 KiB
Text
125 lines
4.4 KiB
Text
--
|
|
-- demo\rosetta\sha-256.exw
|
|
-- ========================
|
|
--
|
|
-- fairly faithful rendition of https://en.wikipedia.org/wiki/SHA-2
|
|
-- with slightly improved names (eg s0 -> sigma0) from elsewhere.
|
|
-- See also sha-256asm.exw for a faster inline asm version, and
|
|
-- sha-256dll.exw is much shorter as it uses a pre-built dll.
|
|
|
|
--Initial array of round constants
|
|
--(first 32 bits of the fractional parts of the cube roots of the first 64 primes 2..311):
|
|
constant k = {
|
|
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}
|
|
|
|
function pad64(integer v)
|
|
-- round v up to multiple of 64
|
|
return floor((v+63)/64)*64
|
|
end function
|
|
|
|
constant m4 = allocate(4) -- scratch area, for uint32
|
|
|
|
function uint32(atom v)
|
|
--
|
|
-- (note: I have experimented to call this as few times as possible.
|
|
-- It wouldn't hurt to perform this on every maths op, but a
|
|
-- few leading bits in a few work fields don't matter much.)
|
|
--
|
|
poke4(m4,v)
|
|
return peek4u(m4)
|
|
end function
|
|
|
|
function sq_uint32(sequence s)
|
|
-- apply unit32 to all elements of s
|
|
for i=1 to length(s) do
|
|
s[i] = uint32(s[i])
|
|
end for
|
|
return s
|
|
end function
|
|
|
|
function dword(string msg, integer i)
|
|
-- get dword as big-endian
|
|
return msg[i]*#1000000+msg[i+1]*#10000+msg[i+2]*#100+msg[i+3]
|
|
end function
|
|
|
|
function shr(atom v, integer bits)
|
|
return floor(v/power(2,bits))
|
|
end function
|
|
|
|
function ror(atom v, integer bits)
|
|
return or_bits(shr(v,bits),v*power(2,32-bits))
|
|
end function
|
|
|
|
function sha256(string msg)
|
|
-- main function
|
|
atom s0,s1,a,b,c,d,e,f,g,h,ch,temp1,maj,temp2,x
|
|
sequence w = repeat(0,64)
|
|
sequence res
|
|
integer len = length(msg)+1
|
|
--Initial hash values
|
|
--(first 32 bits of the fractional parts of the square roots of the first 8 primes 2..19)
|
|
atom h0 = 0x6a09e667,
|
|
h1 = 0xbb67ae85,
|
|
h2 = 0x3c6ef372,
|
|
h3 = 0xa54ff53a,
|
|
h4 = 0x510e527f,
|
|
h5 = 0x9b05688c,
|
|
h6 = 0x1f83d9ab,
|
|
h7 = 0x5be0cd19
|
|
|
|
-- add the '1' bit and space for size in bits, padded to multiple of 64
|
|
msg &= #80&repeat('\0',pad64(len+8)-len)
|
|
len = (len-1)*8
|
|
for i=length(msg) to 1 by -1 do
|
|
msg[i] = and_bits(len,#FF)
|
|
len = floor(len/#100)
|
|
if len=0 then exit end if
|
|
end for
|
|
|
|
-- Process the message in successive 512-bit (64 byte) chunks
|
|
for chunk=1 to length(msg) by 64 do
|
|
for i=1 to 16 do
|
|
w[i] = dword(msg,chunk+(i-1)*4)
|
|
end for
|
|
-- Extend the first 16 words into the remaining 48 words w[17..64] of the message schedule array
|
|
for i=17 to 64 do
|
|
x = w[i-15]; s0 = xor_bits(xor_bits(ror(x, 7),ror(x,18)),shr(x, 3))
|
|
x = w[i-2]; s1 = xor_bits(xor_bits(ror(x,17),ror(x,19)),shr(x,10))
|
|
w[i] = uint32(w[i-16]+s0+w[i-7]+s1)
|
|
end for
|
|
-- Initialize working variables to current hash value
|
|
{a,b,c,d,e,f,g,h} = {h0,h1,h2,h3,h4,h5,h6,h7}
|
|
|
|
-- Compression function main loop
|
|
for i=1 to 64 do
|
|
s1 = xor_bits(xor_bits(ror(e,6),ror(e,11)),ror(e,25))
|
|
ch = xor_bits(and_bits(e,f),and_bits(not_bits(e),g))
|
|
temp1 = h+s1+ch+k[i]+w[i]
|
|
s0 = xor_bits(xor_bits(ror(a,2),ror(a,13)),ror(a,22))
|
|
maj = xor_bits(xor_bits(and_bits(a,b),and_bits(a,c)),and_bits(b,c))
|
|
temp2 = s0+maj
|
|
|
|
{h,g,f,e,d,c,b,a} = sq_uint32({g,f,e,d+temp1,c,b,a,temp1+temp2})
|
|
|
|
end for
|
|
|
|
-- Add the compressed chunk to the current hash value
|
|
{h0,h1,h2,h3,h4,h5,h6,h7} = sq_add({h0,h1,h2,h3,h4,h5,h6,h7},{a,b,c,d,e,f,g,h})
|
|
end for
|
|
|
|
-- Produce the final hash value (big-endian)
|
|
res = sq_uint32({h0, h1, h2, h3, h4, h5, h6, h7}) -- (or do sq_unit32 on the sq_add above)
|
|
for i=1 to length(res) do
|
|
res[i] = sprintf("%08x",res[i])
|
|
end for
|
|
return join(res)
|
|
end function
|
|
|
|
?sha256("Rosetta code")
|