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

Cryptographic Pseudo-Random Number Generator (CPRNG)

Algorithm creator(s)

ANSI X9.17


PB author(s)

Greg Turgeon


Description

An excellent, cryptographically strong pseudo-random number generator based on the Rijndael algorithm.


Note

This pseudo-random number generator has the unique property that it's cryptographically secure.


Source

https://forum.powerbasic.com/forum/user-to-user-discussions/source-code/24650-cryptographic-prng-for-3-0-7-0


See also


Source Code

Download source code file cprng.bas (Right-click -> "Save as ...")

#IF 0
=====================================================================
                             CPRNG.BAS
            Cryptographic pseudo-random number generator
            Compiles with either PBWIN 7.0+ or PBCC 3.0+
===================================================================== 
The following code provides a source of cryptographically strong random 
bytes for use in creating session keys and other similar purposes. The 
CPRNG is based on the ANSI X9.17 standard for secure key generation and 
uses Rijndael as its cryptographic algorithm. The Rijndael code consists 
of a streamlined version of my full PB Rijndael implementation, which is 
available here: http://www.powerbasic.com/support/forums/Forum7/HTML/001494.html 
A cryptographically strong PRNG produces values which are not predictable. 
This quality separates such generators from other software random number 
generators, which always produce a predictable stream of values regardless 
of the size of a generator's period. If cryptographic strength is not 
required, a CPRNG constitutes overkill. However, if cryptographic strength 
is in fact required, only a generator designed specifically for such 
use is acceptable. 
This PB implementation is hereby placed in the public domain.  Use it as 
you wish.  My hope is discourage reliance on home-grown encryption schemes 
in favor of well-examined, strong, freely available algorithms. 
In this posting, test-bed code appears below the CPRNG.BAS file contents. 
All code requires compiler releases 3.0/7.0 or later. Implementation Notes 

-- When first called, the RandBytes$() function performs all necessary 
   initialization. 

-- Calling RandBytes$(TotalBytes&) with (TotalBytes& < 1) causes the 
   generator to reinitialize. 

-- Functionality is completely self-contained; no global data is employed. 
Greg Turgeon 9/2003 
#ENDIF 


%BLOCKSIZE     = 16
%BLOCKSHIFT    = 4                        'shr (x \ %BLOCKSIZE)
%SUBKEY_SIZE   = (%BLOCKSIZE*2*8)
%MAX_KEY_BYTES = (%BLOCKSIZE*2) 
'-- Encryption context
TYPE RIJNDAEL_CONTEXT
   Rounds        AS LONG
   KeyBlocks     AS LONG                  '4*4-byte blocks
   UserKeyLength AS LONG
   UserKey       AS LONG PTR
   InBlock       AS LONG PTR
   OutBlock      AS LONG PTR
   KE            AS LONG PTR              'pointer to encryption subkey buffer
   KE_Buffer     AS STRING * %SUBKEY_SIZE 'encryption subkey buffer
   Se            AS BYTE PTR
   Te0           AS LONG PTR
   Te1           AS LONG PTR
   Te2           AS LONG PTR 
   Te3           AS LONG PTR
   Rcon          AS LONG PTR
END TYPE 
'-- Generator context
TYPE CPRNG
   Seed              AS BYTE PTR
   Seed_Buffer       AS STRING * %BLOCKSIZE
   Key               AS BYTE PTR
   Key_Buffer        AS STRING * %BLOCKSIZE
   RandBytes         AS BYTE PTR
   RandBytes_Buffer  AS STRING * %BLOCKSIZE
   Temp1             AS BYTE PTR
   Temp2             AS BYTE PTR
   Temp_Buffer       AS STRING * (%BLOCKSIZE*2)
   InBlock           AS BYTE PTR          'used to access RCtx.InBlock & RCtx.OutBlock 
   OutBlock          AS BYTE PTR          'as bytes instead of longs
   RCtx              AS RIJNDAEL_CONTEXT
END TYPE 
'--------------------
'-- Utility macros
'-------------------- 
'--------------------
MACRO zbs(total_bytes)=string$(total_bytes,0) 
'--------------------
MACRO FUNCTION makebuffer(total_bytes)
MACROTEMP s
LOCAL s$
s = zbs(total_bytes)
END MACRO =strptr(s) 
'--------------------
MACRO FUNCTION shiftlc(xx,constant_shiftval)
retval = (xx)
!  shl   retval, constant_shiftval
END MACRO =retval 
'--------------------
MACRO FUNCTION shiftrc(xx,constant_shiftval)
retval = (xx)
!  shr   retval, constant_shiftval
END MACRO =retval 
'--------------------
MACRO FUNCTION byte3(xx)
retval = (xx)
!  shr   retval, 24
END MACRO =retval 
'--------------------
MACRO FUNCTION byte2(xx)
retval = (xx)
!  shr   retval, 16
END MACRO =(retval AND &hff) 
'--------------------
MACRO FUNCTION byte1(xx)
retval = (xx)
!  shr   retval, 8
END MACRO =(retval AND &hff) 
'--------------------
MACRO FUNCTION byte0(xx)
retval = (xx)
END MACRO =(retval AND &hff) 
'--------------------
MACRO FUNCTION get_block(mem_ptr)
!  mov   edx, mem_ptr
!  mov   eax, [edx]
!  mov   retval, eax
!  add   edx, 4
!  mov   mem_ptr, edx
END MACRO =retval 
'--------------------
MACRO put_block(mem_ptr,value)
retval=value
!  mov   edx, retval
!  mov   eax, mem_ptr
!  mov   [eax], edx
!  add   eax, 4
!  mov   mem_ptr, eax
END MACRO 
DECLARE FUNCTION RandBytes$(BYVAL TotalBytes&)
'-- Support functions (No direct access should be attempted)
DECLARE FUNCTION Init_CPRNG&(Rand AS CPRNG)
DECLARE FUNCTION MakeRandBytes&(Rand AS CPRNG)
DECLARE FUNCTION Set_Key&(RCtx AS RIJNDAEL_CONTEXT)
DECLARE FUNCTION EncryptBlock&(RCtx AS RIJNDAEL_CONTEXT)
DECLARE FUNCTION Rijndael_Init&(RCtx AS RIJNDAEL_CONTEXT) 
'====================
FUNCTION RandBytes$(BYVAL TotalBytes&)
STATIC rand AS CPRNG, passes&, retval&
LOCAL buffer$, pbuffer AS STRING PTR * %BLOCKSIZE 
'-- Initialize CPRNG on first run; exit on error
if Rand.Seed = 0 then 
   if Init_CPRNG&(rand) = 0 then exit function
end if
'-- Reinitialize only
if TotalBytes& < 1 then 
   reset rand
   Init_CPRNG& rand
   exit function
end if 
if (Totalbytes& AND &b1111) then
   buffer = zbs(TotalBytes& + %BLOCKSIZE)
else  'if (Totalbytes& MOD %BLOCKSIZE) = 0
   buffer = zbs(TotalBytes&)
end if 
pbuffer = strptr(buffer)
passes = shiftrc(len(buffer),%BLOCKSHIFT) 'len(buffer)\%BLOCKSIZE
do while passes
   MakeRandBytes rand
   @pbuffer = rand.RandBytes_Buffer
   incr pbuffer : decr passes
loop 
function = left$(buffer, TotalBytes&)
END FUNCTION 
'====================
FUNCTION Init_CPRNG&(Rand AS CPRNG)
LOCAL x&, m AS POINTAPI, pseed AS LONG PTR 
'-- Initialize key buffer with current system time
GetSystemTime byval varptr(Rand.key_buffer) 
'-- Initialze seed buffer with various system status data 
Rand.seed_buffer = guid$
pseed = varptr(Rand.seed_buffer)
x = GetCurrentProcessID
@pseed = @pseed XOR x : incr pseed
x = x XOR GetModuleHandle("")
@pseed = @pseed XOR x : incr pseed
GetCursorPos m
x = x XOR m.x XOR m.y
@pseed = @pseed XOR x : incr pseed
x = x XOR GetCurrentThreadID
@pseed = @pseed XOR x 
'-- Initialize encryption context using seed and key values
Rand.RCtx.UserKey        = varptr(Rand.Key_Buffer)
Rand.RCtx.UserKeyLength  = len(Rand.Key_Buffer)
if Set_Key&(Rand.RCtx) then 
   Rand.Seed            = varptr(Rand.Seed_Buffer)
   Rand.Key             = varptr(Rand.Key_Buffer)
   Rand.RandBytes       = varptr(Rand.RandBytes_Buffer)
   Rand.Temp1           = varptr(Rand.Temp_Buffer)
   Rand.Temp2           = Rand.Temp1 + %BLOCKSIZE 
   Rand.RCtx.InBlock     = Rand.Temp1
   Rand.RCtx.OutBlock    = Rand.Temp2
   EncryptBlock Rand.RCtx
   '-- Warm it up
   MakeRandBytes Rand
   function = -1
end if
END FUNCTION 
'====================
FUNCTION MakeRandBytes&(Rand AS CPRNG)
REGISTER i& 
'-- Advance counter & encrypt it
QueryPerformanceCounter byval Rand.Temp1
QueryPerformanceCounter byval Rand.Temp1+8 
Rand.RCtx.InBlock  = Rand.Temp1
Rand.RCtx.OutBlock = Rand.Temp2
EncryptBlock Rand.RCtx 
'-- Combine saved seed w/encrypted counter
Rand.InBlock = Rand.RCtx.InBlock
for i = 0 to %BLOCKSIZE-1
   Rand.@InBlock[i] = Rand.@Temp2[i] XOR Rand.@Seed[i]
next i 
'-- Generate randon bytes that will be returned by 
'   RandBytes$() function
Rand.RCtx.OutBlock = Rand.Randbytes
EncryptBlock Rand.RCtx 
'-- Setup for next run
for i = 0 to %BLOCKSIZE-1
   Rand.@InBlock[i] = Rand.@Temp2[i] XOR Rand.@Randbytes[i]
next i
Rand.RCtx.OutBlock = Rand.Seed
EncryptBlock Rand.RCtx
END FUNCTION 
'====================
FUNCTION Set_Key&(RCtx AS RIJNDAEL_CONTEXT)
LOCAL i&, n&, nk&, pblock&, r&, temp&, t&, u&, v&, retval&
LOCAL in_key AS LONG PTR, k AS LONG PTR, s AS BYTE PTR 
RCtx.Rounds = 10
RCtx.KeyBlocks = (RCtx.Rounds+1)*4
Rijndael_Init RCtx 
in_key = MakeBuffer(%MAX_KEY_BYTES) 
pblock = RCtx.UserKey
k = RCtx.KE  'local copy
for i = 0 to (RCtx.Rounds-6)-1
   @k[i] = get_block(pblock)
next i 
s = RCtx.Se
i = RCtx.Rounds - 6 : nk = i : n = 0
do while i < RCtx.KeyBlocks
   temp = @k[i-1]
   if n = 0 then
      n = Nk
      t = byte2(temp)
      v = shiftlc(@s[t],24)
      t = byte1(temp) : u = shiftlc(@s[t],16)
      v = v OR u
      t = byte0(temp) : u = shiftlc(@s[t],8)
      v = v OR u
      u = @s[byte3(temp)]
      temp = (v OR u) XOR RCtx.@Rcon[r] : incr r
   else
      if ((Nk = 8) AND (n = 4)) then
         t = byte3(temp)
         v = shiftlc(@s[t],24)
         t = byte2(temp) : u = shiftlc(@s[t],16)
         v = v OR u
         t = byte1(temp) : u = shiftlc(@s[t],8)
         v = v OR u
         u = @s[temp AND &hff]
         temp = v OR u
      end if
   end if
   @k[i] = @k[i-nk] XOR temp
   incr i : decr n
loop 
function = -1
END FUNCTION 
'--------------------
MACRO FUNCTION packbytes(aa,bb,cc,dd)
aa = shiftlc(aa,24)
bb = shiftlc(bb,16)
cc = shiftlc(cc,8)
END MACRO = (aa OR bb OR cc OR dd) 
'--------------------
MACRO FUNCTION enc_ta(aa,bb,cc,dd)
u =       RCtx.@Te0[byte3(aa)]
u = u XOR RCtx.@Te1[byte2(bb)]
u = u XOR RCtx.@Te2[byte1(cc)]
u = u XOR RCtx.@Te3[dd AND &hff]
END MACRO = u 
'--------------------
MACRO FUNCTION enc_tal(aa,bb,cc,dd,ww)
u = RCtx.@Se[byte3(aa)] : v = shiftrc(ww,24)
a0 = u XOR v
u = RCtx.@Se[byte2(bb)] : v = shiftrc(ww,16)
a1 = (u XOR v) AND &hff
u = RCtx.@Se[byte1(cc)] : v = shiftrc(ww,8)
a2 = (u XOR v) AND &hff
u = RCtx.@Se[dd AND &hff] : v = ww
a3 = (u XOR v) AND &hff
retval = packbytes(a0,a1,a2,a3)
END MACRO = retval 
'====================
FUNCTION EncryptBlock&(RCtx AS RIJNDAEL_CONTEXT)
LOCAL r&, t&, u&, v&, w&, pblock&, retval&, k AS LONG PTR
LOCAL a0&, a1&, a2&, a3&, t0&, t1&, t2&, t3&, s0&, s1&, s2&, s3& 
pblock = RCtx.InBlock
t0 = get_block(pblock) : t1 = get_block(pblock)
t2 = get_block(pblock) : t3 = get_block(pblock) 
k = RCtx.KE
t0 = t0 XOR @k[0] : t1 = t1 XOR @k[1]
t2 = t2 XOR @k[2] : t3 = t3 XOR @k[3]
for r = 1 to RCtx.Rounds-1
   k = k + (4*4)
   t = enc_ta(t0,t1,t2,t3) : a0 = t XOR @k[0]
   t = enc_ta(t1,t2,t3,t0) : a1 = t XOR @k[1]
   t = enc_ta(t2,t3,t0,t1) : a2 = t XOR @k[2]
   t = enc_ta(t3,t0,t1,t2) : a3 = t XOR @k[3] 
   t0 = a0 : t1 = a1 : t2 = a2 : t3 = a3
next r
k = k + (4*4)
w = @k[0] : s0 = enc_tal(t0,t1,t2,t3,w)
w = @k[1] : s1 = enc_tal(t1,t2,t3,t0,w)
w = @k[2] : s2 = enc_tal(t2,t3,t0,t1,w)
w = @k[3] : s3 = enc_tal(t3,t0,t1,t2,w) 
pblock = RCtx.OutBlock
put_block(pblock,s0) : put_block(pblock,s1)
put_block(pblock,s2) : put_block(pblock,s3)
END FUNCTION 
'====================
FUNCTION Rijndael_Init&(RCtx AS RIJNDAEL_CONTEXT)
STATIC table_buffer$, se AS BYTE PTR
STATIC te0 AS LONG PTR, te1 AS LONG PTR, te2 AS LONG PTR,  te3 AS LONG PTR
STATIC rcon AS LONG PTR 
RCtx.KE_Buffer = zbs(%SUBKEY_SIZE)
RCtx.KE        = varptr(RCtx.KE_Buffer)
if te0 = 0 then gosub MakeTables 
RCtx.Se        = se
RCtx.Te0       = te0
RCtx.Te1       = te1
RCtx.Te2       = te2
RCtx.Te3       = te3
RCtx.Rcon      = rcon
exit function 
'============
MakeTables:
LOCAL ss AS BYTE PTR
LOCAL s1&, s2&, s3&, i1&, t&, u&, v&, retval&
%ROOT = &h11B 
table_buffer$ = zbs((1024*4)+(256*2)+(40)) 
te0 = strptr(table_buffer)
te1 = (te0+1024)   : te2 = te0+(1024*2) : te3  = te0+(1024*3) 
se  = te0+(1024*4) : rcon = se+(256*2) 
ss = codeptr(SS_Table)
for i1 = 0 to 255
   s1 = @ss[i1] 
   s2 = shiftlc(s1,1)
   if (s2 >= &h100) then s2 = s2 XOR %ROOT
   s3 = s2 XOR s1 
   @se[i1] = s1 AND 255 
   u = shiftlc(s2,24)
   v = shiftlc(s1,16)
   t = u OR v
   u = shiftlc(s1,8)
   t = t OR u OR s3
   @te0[i1] = t 
   u = shiftrc(t,8)
   v = shiftlc(t,24)
   @te1[i1] = u OR v 
   u = shiftrc(t,16)
   v = shiftlc(t,16)
   @te2[i1] = u OR v 
   u = shiftrc(t,24)
   v = shiftlc(t,8)
   @te3[i1] = u OR v
next i1 
LOCAL i&, r&
r = 1
@rcon[0] = shiftlc(r,24)
for i = 1 to 9
   r = shiftlc(r,1)
   if (r >= &h100) then r = r XOR %ROOT
   @rcon[i] = shiftlc(r,24)
next i
RETURN 
SS_Table:
! DD  &h7b777c63, &hc56f6bf2, &h2b670130, &h76abd7fe
! DD  &h7dc982ca, &hf04759fa, &hafa2d4ad, &hc072a49c
! DD  &h2693fdb7, &hccf73f36, &hf1e5a534, &h1531d871
! DD  &hc323c704, &h9a059618, &he2801207, &h75b227eb
! DD  &h1a2c8309, &ha05a6e1b, &hb3d63b52, &h842fe329
! DD  &hed00d153, &h5bb1fc20, &h39becb6a, &hcf584c4a
! DD  &hfbaaefd0, &h85334d43, &h7f02f945, &ha89f3c50
! DD  &h8f40a351, &hf5389d92, &h21dab6bc, &hd2f3ff10
! DD  &hec130ccd, &h1744975f, &h3d7ea7c4, &h73195d64
! DD  &hdc4f8160, &h88902a22, &h14b8ee46, &hdb0b5ede
! DD  &h0a3a32e0, &h5c240649, &h62acd3c2, &h79e49591
! DD  &h6d37c8e7, &ha94ed58d, &heaf4566c, &h08ae7a65
! DD  &h2e2578ba, &hc6b4a61c, &h1f74dde8, &h8a8bbd4b
! DD  &h66b53e70, &h0ef60348, &hb9573561, &h9e1dc186
! DD  &h1198f8e1, &h948ed969, &he9871e9b, &hdf2855ce
! DD  &h0d89a18c, &h6842e6bf, &h0f2d9941, &h16bb54b0
END FUNCTION
'-- end CPRNG.BAS 
'=====================================================================
'                           TSTCPRNG.BAS
'             Compiles with either PBWIN 7.0+ or PBCC 3.0+
'=====================================================================
#COMPILE EXE
#REGISTER NONE
#DIM ALL
'============
DEFLNG A-Z
%USEMACROS = 1
#INCLUDE "WIN32API.INC" 
'--------------------
'-- Utility macros
'-------------------- 
'--------------------
#IF %def(%pb_win32)
MACRO eol=$CR
MACRO mbox(t)=msgbox t
#ELSEIF %def(%pb_cc32)
MACRO eol=$CRLF
MACRO mbox(t)=stdout t
#ENDIF 
'--------------------
MACRO EnterCC
#IF %def(%pb_cc32)
LOCAL launched&
if (cursory = 1) and (cursorx = 1) then launched = -1
#ENDIF 
END MACRO 
'--------------------
MACRO ExitCC
#IF %def(%pb_cc32)
if launched then
   input flush
   stdout "Press any key to end" 
   waitkey$
end if
#ENDIF 
END MACRO 
#INCLUDE "CPRNG.BAS" 
DECLARE FUNCTION Hex2Show$(Buffer$) 
'====================
FUNCTION PBMain&()
REGISTER i& : LOCAL t$
EnterCC 
for i = 1 to 20
   t = t + Hex2Show$(RandBytes$(20)) + eol
next i
t = t + eol
mbox(t) 
'-- Reset generator
RandBytes$ 0 
t = ""
for i = 1 to 20
   t = t + Hex2Show$(RandBytes$(20)) + eol
next i
t = t + eol
mbox(t) 
ExitCC
END FUNCTION 
'====================
FUNCTION Hex2Show$(Buffer$)
REGISTER i& : LOCAL t$, b AS BYTE PTR
b = strptr(Buffer$)
for i = 0 to len(Buffer$)-1
   t = t + hex$(@b[i],2) + " "
next i
function = t
END FUNCTION
'-- end TSTCPRNG.BAS

Mirror provided by Knuth Konrad