SHA-256
Algorithm creator(s)
NIST and NSA
PB author(s)
Greg Turgeon
Description
Creates a 256-bit hash (message digest) from messages up to 2^64 bits
Note
Produces a message digest of 256 bits, providing no more than 128 bits of security against collision attacks.
Source
https://forum.powerbasic.com/forum/user-to-user-discussions/source-code/23494-sha256-for-pbcc-and-pbdll-hash-functions-test-bed?t=22881
See also
Source Code
Download source code file sha-256.bas (Right-click -> "Save as ...")
#IF 0
== SHA256 ==
Secure 256-bit hashing for PowerBASIC
Code for the following two files appears below:
SHA256.INC Hash routines for returning 32-byte SHA256 hashes
of buffers and files
SHA256.BAS Test bed EXE illustrating buffer and file hashing
All code compiles with either PBDLL 6.0 or PBCC 2.0.
Available at the following URL is a PDF file containing the NIST
specifications for the 256-bit, 384-bit, and 512-bit extensions to
the SHA standard:
http://csrc.nist.gov/cryptval/shs.html
A hash is considered secure when it possesses the following qualities.
-- Determining the input string from the hash (i.e., working backward
from the hash alone to determine the string which generated it) is not
considered feasible.
-- Given an input string, it is not considered feasible to find another
string which hashes to the same value.
-- It is not considered feasible to find two random strings which hash
to the same value.
An input string can be of any length and thus far longer than its
resulting hash. This is accomplished through use of a compression
function which treats the input as a combination of any previously
hashed input and the current input. Designing this feature is one of
the most important challenges when creating a secure hash algorithm.
Secure hashes have many uses, among them validation of passphrases.
In this arrangement, a user enters a passphrase when setting up an
account. The input string is hashed, and the hash is stored (not the
passphrase). When the user seeks readmission, the passphrase entered
is hashed, and the hash is compared with the one on record. If the
hashes match, the strings which generated them also must match. If
the collection of stored hashes is compromised, the passphrases
remain unknown.
Secure hashes are not designed for speed. The implementation below
relies on assembly language to improve speed in its most heavily
traveled section of code, but unless security is required, a secure
hash is a poor choice when compared with the many simpler, far more
efficient hash algorithms in widespread use.
Greg Turgeon
gturgeon@compuserve.com
1/10/2001
#ENDIF
'=====================================================================
'-- SHA256.INC
'-- WIN32 API not required
'-- Uses no global data
'=====================================================================
TYPE tSHA_STATE
state0 AS DWORD
state1 AS DWORD
state2 AS DWORD
state3 AS DWORD
state4 AS DWORD
state5 AS DWORD
state6 AS DWORD
state7 AS DWORD
pbuf AS BYTE PTR
buflen AS LONG
pstate AS DWORD PTR
k_array AS LONG PTR
END TYPE
%PBUF_OFFSET = 32
%BLOCKSIZE = 64 'bytes
%FILE_BUFFERSIZE = 32000 'bytes
DECLARE SUB SHA_Buffer(pBuffer AS BYTE PTR, BYVAL Length&, Hash AS DWORD PTR)
DECLARE FUNCTION SHA_File&(File_Name$, Hash$)
DECLARE SUB SHA_Init(pSHA_STATE AS tSHA_STATE PTR)
DECLARE SUB SHA_Compress(pSHA_State AS tSHA_STATE PTR)
DECLARE FUNCTION MakePadding$(BYVAL TotalBytes&)
'====================
SUB SHA_Init(pSHA_STATE AS tSHA_STATE PTR)
LOCAL p&
@pSHA_STATE.k_array = codeptr(K_Array_Data)
p& = codeptr(Init_Values)
@pSHA_STATE.pstate = pSHA_STATE
! push esi
! push edi
! mov esi, p&
! mov edi, pSHA_STATE
! mov ecx, 8
! cld
! rep movsd
! pop edi
! pop esi
exit sub
'============
Init_Values:
! DD &h6A09E667???, &hBB67AE85???, &h3C6EF372???, &hA54FF53A???
! DD &h510E527F???, &h9B05688C???, &h1F83D9AB???, &h5BE0CD19???
K_Array_Data:
! DD &h428a2f98???, &h71374491???, &hb5c0fbcf???, &he9b5dba5???, &h3956c25b???, &h59f111f1???
! DD &h923f82a4???, &hab1c5ed5???, &hd807aa98???, &h12835b01???, &h243185be???, &h550c7dc3???
! DD &h72be5d74???, &h80deb1fe???, &h9bdc06a7???, &hc19bf174???, &he49b69c1???, &hefbe4786???
! DD &h0fc19dc6???, &h240ca1cc???, &h2de92c6f???, &h4a7484aa???, &h5cb0a9dc???, &h76f988da???
! DD &h983e5152???, &ha831c66d???, &hb00327c8???, &hbf597fc7???, &hc6e00bf3???, &hd5a79147???
! DD &h06ca6351???, &h14292967???, &h27b70a85???, &h2e1b2138???, &h4d2c6dfc???, &h53380d13???
! DD &h650a7354???, &h766a0abb???, &h81c2c92e???, &h92722c85???, &ha2bfe8a1???, &ha81a664b???
! DD &hc24b8b70???, &hc76c51a3???, &hd192e819???, &hd6990624???, &hf40e3585???, &h106aa070???
! DD &h19a4c116???, &h1e376c08???, &h2748774c???, &h34b0bcb5???, &h391c0cb3???, &h4ed8aa4a???
! DD &h5b9cca4f???, &h682e6ff3???, &h748f82ee???, &h78a5636f???, &h84c87814???, &h8cc70208???
! DD &h90befffa???, &ha4506ceb???, &hbef9a3f7???, &hc67178f2???
END SUB
'====================
SUB SHA_Compress(pST AS tSHA_STATE PTR)
STATIC s_array&(), w_array&()
STATIC s AS LONG PTR, w AS LONG PTR
LOCAL pk_array&, t0&, t1&, wi&, ki&, i&
if w& = 0 then
redim w_array&(63) : w = varptr(w_array&(0))
redim s_array&(7) : s = varptr(s_array&(0))
end if
'-- Copy for inline assembler
pk_array& = @pST.k_array
! push esi
! push edi
! push ebx
'-- Copy current state into s&()
! mov esi, pST ;esi -> pST.state0
! mov edi, s
! push esi
! mov ecx, 8
! cld
! rep movsd
'-- Copy current data block to w&() w/little-to-big endian conversion
! pop esi ;esi -> st.state0
! add esi, %PBUF_OFFSET ;esi -> st.buf
! mov edi, w
! mov eax, [esi]
! mov esi, eax
! mov ecx, 16*4
SwapCopyTop:
! sub ecx, 4
! mov eax, [esi+ecx]
! bswap eax
! mov [edi+ecx], eax
! test ecx, ecx
! jnz SwapCopyTop
'-- Fill W[16to63]
! mov esi, w
! push ebp
! mov ebp, 16
FillTop:
! mov ebx, ebp
! sub ebx, 2
! mov eax, [esi]+[ebx*4]
! mov ecx, eax
! mov edx, ecx
! sub ebx, 13 ;prep for next access: (-2)+(-13) = -15
! ror eax, 17
! ror ecx, 19
! shr edx, 10
! xor eax, ecx
! xor eax, edx
! mov edi, eax ;edi = temp total
! mov eax, [esi]+[ebx*4]
! mov ecx, eax
! mov edx, ecx
! ror eax, 7
! ror ecx, 18
! shr edx, 3
! add ebx, 8 ;= (-15)+8 = -7
! xor eax, ecx
! xor eax, edx
! add edi, eax
! mov eax, [esi]+[ebx*4]
! sub ebx, 9 ;= (-7)+(-9) = -16
! add edi, eax
! mov eax, [esi]+[ebx*4]
! mov ebx, ebp
! add eax, edi
! inc ebp
! mov [esi]+[ebx*4], eax
! cmp ebp, 63
! jg FillDone
! jmp FillTop
FillDone:
! pop ebp
'-- Compress: i& = 0 to 63
! xor eax, eax
! mov esi, s ;here to CompressDone & END SUB: esi -> s[0]
! mov i&, eax
CompressTop:
! mov ebx, eax
! mov edx, w
! mov eax, [edx]+[ebx*4]
! mov wi&, eax
! mov edx, pk_array&
! mov eax, [edx]+[ebx*4]
! mov ki&, eax
! mov eax, [esi+16]
! mov ecx, [esi+20]
! mov ebx, eax
! mov edx, eax
! and eax, [esi+20]
! not edx
! and edx, [esi+24]
! xor eax, edx
! mov ecx, ebx
! mov ebx, eax
! mov eax, ecx
! mov edx, ecx
! ror eax, 6
! ror ecx, 11
! ror edx, 25
! add ebx, ki&
! xor eax, ecx
! add ebx, wi&
! xor eax, edx
! add eax, ebx
! add eax, [esi+28]
! mov t0&, eax
! mov eax, [esi]
! mov ecx, [esi+4]
! mov ebx, eax
! mov edx, eax
! and eax, ecx
! and edx, [esi+8]
! xor eax, edx
! and ecx, [esi+8]
! xor eax, ecx
! mov edx, eax
! mov eax, ebx
! mov ecx, ebx
! ror eax, 2
! ror ecx, 13
! ror ebx, 22
! xor eax, ecx
! xor eax, ebx
! add eax, edx
! mov t1&, eax
' @s[7] = @s[6] : @s[6] = @s[5] : @s[5] = @s[4] : @s[4] = @s[3] + t0&
' @s[3] = @s[2] : @s[2] = @s[1] : @s[1] = @s[0] : @s[0] = t0& + t1&
! mov edi, esi
! mov ecx, t0&
! mov eax, [esi+24] ;@s[7] = @s[6]
! mov ebx, [esi+20] ;@s[6] = @s[5]
! mov edx, [esi+16] ;@s[5] = @s[4]
! mov [edi+28], eax
! mov [edi+24], ebx
! mov [edi+20], edx
! mov eax, [esi+12] ;@s[4] = @s[3] + T0&
! mov ebx, [esi+8] ;@s[3] = @s[2]
! add eax, ecx
! mov edx, [esi] ;@s[1] = @s[0]
! mov [edi+12], ebx
! mov [edi+16], eax
! mov ebx, ecx ;@s[0] = T0& + T1&
! mov eax, [esi+4] ;@s[2] = @s[1]
! mov [edi+4], edx
! add ebx, t1&
! mov [edi+8], eax
! mov [edi], ebx
! mov eax, i&
! inc eax
! cmp eax, 63
! jg CompressDone
! mov i&, eax
! jmp CompressTop
CompressDone:
'-- Add current state s() to context
' for i& = 0 to 7 : @pST.@pstate[i&] = @pST.@pstate[i&] + @s[i&] : next i&
! mov edi, pST
! mov ecx, 8*4
AddCopyTop:
! sub ecx, 4
! mov eax, [edi+ecx]
! add eax, [esi+ecx]
! mov [edi+ecx], eax
! test ecx, ecx
! jnz AddCopyTop
! pop ebx
! pop edi
! pop esi
END SUB
'====================
SUB SHA_Buffer(DataBuffer AS BYTE PTR, BYVAL Length&, Hash AS DWORD PTR) EXPORT
'-- Expects parameter Hash to point to buffer of correct size of 32 bytes (256bits \ 8)
REGISTER i&
LOCAL lastbuff$
LOCAL st AS tSHA_STATE, p&
i& = Length& AND (%BLOCKSIZE-1)
lastbuff$ = peek$((DataBuffer + Length&) - i&, i&)
lastbuff$ = lastbuff$ + MakePadding$(Length&)
SHA_Init byval varptr(st)
st.buflen = Length&
st.pbuf = DataBuffer
i& = Length& AND (NOT %BLOCKSIZE-1)
do while i&
SHA_Compress byval varptr(st)
st.pbuf = st.pbuf + %BLOCKSIZE
i& = i& - %BLOCKSIZE
loop
st.buflen = len(lastbuff$)
st.pbuf = strptr(lastbuff$)
do while st.buflen
SHA_Compress byval varptr(st)
st.pbuf = st.pbuf + %BLOCKSIZE
st.buflen = st.buflen - %BLOCKSIZE
loop
'-- Copy current state (as dwords) from s&() to Hash
' for i& = 0 to 7 : @Hash[i&] = st.@pstate[i&] : next i&
p& = st.pstate
! push esi
! push edi
! mov esi, p& ;esi -> st.state0
! mov edi, Hash
! mov ecx, 8
! cld
! rep movsd
! pop edi
! pop esi
END SUB
'====================
FUNCTION SHA_File&(File_Name$, Hash$) EXPORT
'-- Returns 0 on success, or PB (not OS) error code
REGISTER i&, bytesleft&
LOCAL buffer$, padding$
LOCAL st AS tSHA_STATE, phash AS DWORD PTR
LOCAL infile&, ecode&, lastpass&, maxstring&
'-- If file not found, return PB error code
if len(dir$(File_Name$)) = 0 then
function = 53 : exit function
end if
buffer$ = string$(%FILE_BUFFERSIZE, 0)
maxstring& = %FILE_BUFFERSIZE
st.buflen = %BLOCKSIZE
SHA_Init byval varptr(st)
infile& = freefile
open File_Name$ for binary lock shared as infile& base=0
if err then goto SHA_File_Error
bytesleft& = lof(infile&)
padding$ = MakePadding$(bytesleft&)
do
'Resize if necessary & flag final buffer
if bytesleft& =< maxstring& then
maxstring& = bytesleft&
buffer$ = string$(maxstring&, 0)
incr lastpass&
end if
get infile&,, buffer$ : if err then goto SHA_File_Error
if lastpass& then buffer$ = buffer$ + padding$
st.pbuf = strptr(buffer$)
for i& = 1 to (len(buffer$) \ %BLOCKSIZE)
SHA_Compress byval varptr(st)
st.pbuf = st.pbuf + %BLOCKSIZE
next i&
bytesleft& = bytesleft& - maxstring&
loop until lastpass&
close infile& : if err then goto SHA_File_Error
'-- Copy current state (as dwords) from s&() to Hash$
'for i& = 0 to 7 : @Hash[i&] = st.@pstate[i&] : next i&
Hash$ = string$(32,0)
phash = strptr(Hash$)
lastpass& = st.pstate
! push esi
! push edi
! mov esi, lastpass& ;esi -> st.state0
! mov edi, phash
! mov ecx, 8
! cld
! rep movsd
! pop edi
! pop esi
Exit_SHA_File:
function = ecode&
exit function
'============
SHA_File_Error:
if err then
ecode& = errclear
else
ecode& = -1
end if
resume Exit_SHA_File
END FUNCTION
'=========================
FUNCTION MakePadding$(BYVAL TotalBytes&)
'-- Creates the necessary string to append to buffer being hashed
LOCAL buffbits&&, padding$
LOCAL pbyte1 AS BYTE PTR, pbyte2 AS BYTE PTR, padbytes&, i&
buffbits&& = TotalBytes& * 8
padding$ = string$(8,0)
pbyte1 = strptr(padding$) : pbyte2 = varptr(buffbits&&)
'-- Copy bytes in reverse
for i& = 0 to 7
@pbyte1[i&] = @pbyte2[7 - i&]
next i&
padbytes& = %BLOCKSIZE - ((TotalBytes&+9) AND (%BLOCKSIZE-1))
function = chr$(&h80) + string$(padbytes&,0) + padding$
END FUNCTION
'-- END SHA256.INC ---------------------------------------------------
'=====================================================================
'-- SHA256.BAS
'-- Test bed for SHA256.INC
'-- Compiles with either PBDLL or PBCC
'=====================================================================
#COMPILE EXE
#DIM ALL
#REGISTER NONE
DEFLNG A-Z
'============
#INCLUDE "WIN32API.INC"
#INCLUDE "SHA256.INC"
DECLARE SUB VerifyImplementation()
DECLARE FUNCTION ShowHash&(ShouldBe$, Hash$)
DECLARE FUNCTION ShowText&(T$)
'====================
FUNCTION PBMain&()
REGISTER i&
LOCAL file_name$, t$, hash$, ecode&
LOCAL pdword AS DWORD PTR
#IF %DEF(%PB_CC32)
LOCAL launched&
if (cursory = 1) and (cursorx = 1) then launched& = -1
#ENDIF
call VerifyImplementation
file_name$ = "c:\autoexec.bat"
ecode& = SHA_File&(file_name$, hash$)
if ecode& then
ShowText file_name$ + $CR + "Error: " + str$(ecode&)
else
pdword = strptr(hash$)
t$ = file_name$ + $CR
for i& = 0 to 7
t$ = t$ + hex$(@pdword[i&], 8) + " "
next i&
t$ = rtrim$(t$," ") + $CR
ShowText t$
end if
function = ecode&
'============
ExitPBMain:
#IF %DEF(%PB_CC32)
if launched& then
input flush
mouse 1, down : mouse on
print "Click or press any key to end";
waitkey$
end if
#ENDIF
END FUNCTION
'====================
SUB VerifyImplementation()
LOCAL buffer$, sha$, shouldbe$
gosub TestVectors1
gosub TestVectors2
gosub TestVectors3
gosub TestVectors4
exit sub
'============
TestVectors1:
buffer$ = "abc"
sha$ = string$(32,0)
SHA_Buffer byval strptr(buffer$), len(buffer$), byval strptr(sha$)
shouldbe$ = "BA7816BF 8F01CFEA 414140DE 5DAE2223 B00361A3 96177A9C B410FF61 F20015AD"
ShowHash shouldbe$, sha$
RETURN
'============
TestVectors2:
buffer$ = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
sha$ = string$(32,0)
SHA_Buffer byval strptr(buffer$), len(buffer$), byval strptr(sha$)
shouldbe$ = "248D6A61 D20638B8 E5C02693 0C3E6039 A33CE459 64FF2167 F6ECEDD4 19DB06C1"
ShowHash shouldbe$, sha$
RETURN
'============
TestVectors3:
buffer$ = "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhij"
buffer$ = buffer$ + "klmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"
sha$ = string$(32,0)
SHA_Buffer byval strptr(buffer$), len(buffer$), byval strptr(sha$)
shouldbe$ = "CF5B16A7 78AF8380 036CE59E 7B049237 0B249B11 E8F07A51 AFAC4503 7AFEE9D1"
ShowHash shouldbe$, sha$
RETURN
'============
TestVectors4:
buffer$ = string$(1000000,"a")
sha$ = string$(32,0)
SHA_Buffer byval strptr(buffer$), len(buffer$), byval strptr(sha$)
shouldbe$ = "CDC76E5C 9914FB92 81A1C7E2 84D73E67 F1809A48 A497200E 046D39CC C7112CD0"
ShowHash shouldbe$, sha$
RETURN
END SUB
'====================
FUNCTION ShowHash&(ShouldBe$, Hash$)
REGISTER i&
LOCAL t$, pdword AS DWORD PTR
t$ = "Should be:" + $CR + shouldbe$ + $CR
pdword = strptr(Hash$)
t$ = t$ + "Actual: " + $CR
for i& = 0 to 7
t$ = t$ + hex$(@pdword[i&], 8) + " "
next i&
ShowText t$
END FUNCTION
'====================
FUNCTION ShowText&(T$)
'-- Text display for PBCC and PBDLL
'-- Note: Destroys T$ under PBCC
#IF %DEF(%PB_CC32)
if instr(T$, $CR) = 0 then
print T$
else
LOCAL tt$
'-- Be sure of trailing $CR
T$ = rtrim$(T$, $CR) + $CR
do while len(T$)
tt$ = extract$(T$, $CR)
print tt$
T$ = remain$(T$, $CR)
loop
end if
#ELSEIF %DEF(%PB_DLL32)
msgbox T$
#ENDIF
END FUNCTION