2024-04-29

Navigation

Skip Navigation Links

Hash algorithms

Asymmetric Algorithms

Symmetric Cipher Algorithms

Encoding Algorithms

Compression Algorithms

Pseudo Random Number Algorithms

Steganography

Library Wrappers

String Comparison

Others

Syntax highlighting by Prism
PBCrypto.com Mirror

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

Mirror provided by Knuth Konrad