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