Announcement

Collapse

Forum Guidelines

This forum is for finished source code that is working properly. If you have questions about this or any other source code, please post it in one of the Discussion Forums, not here.
See more
See less

Cryptographic PRNG for 3.0+ & 7.0+

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

    Cryptographic PRNG for 3.0+ & 7.0+

    Code:
    #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: 
     [url="http://www.powerbasic.com/support/pbforums/showthread.php?t=23464"]http://www.powerbasic.com/support/pbforums/showthread.php?t=23464[/url] 
       
    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, [i]only[/i] 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.
       
    <u>implementation notes</u>
    -- 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

    ------------------
    -- gturgeon at compuserve dot com --
Working...
X
😀
🥰
🤢
😎
😡
👍
👎