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

256 bit seed for PRNGs or encryption purposes.

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

  • PBWin/PBCC 256 bit seed for PRNGs or encryption purposes.

    Minimum requirements: OS >= Windows Vista and compilers 10 or 6.

    Intel introduced RdRand with its Ivy Bridge CPUs and RdSeed with its Broadwell CPUs; bypassing Haswell.

    I have an Ivy Bridge so no RdSeed.

    RdRand is reseeded, at most, every 511 * 128 bits and during idle periods. An analogy could be a quartz watch getting a radio signal every 24 hours.

    What is the difference between RdRand and RdSeed?

    From Intel:

    The short answer

    The decision process for which instruction to use is mercifully simple, and based on what the output will be used for.

    If you wish to seed another pseudorandom number generator (PRNG), use RDSEED

    For all other purposes, use RDRAND

    That's it. RDSEED is intended for seeding a software PRNG of arbitrary width. RDRAND is intended for applications that merely require high-quality random numbers.
    The numbers returned by RdRand have additive prediction resistance. "If you put two 64-bit values with additive prediction resistance togehter (sic), the prediction resistance of the resulting value is only 65 bits (2^64 + 2^64 = 2^65)." says Intel. They go on, "The numbers retuned by RDSEED have multiplicative prediction resistance. If you use two 64-bit samples with multiplicative prediction resistance to build a 128-bit value, you end up with a random number with 128 bits of prediction resistance (2^128 * 2^128 = 2^256). Combine two of those 128-bit values together, and you get a 256-bit number with 256 bits of prediction resistance."

    Oops. John M. of the Intel Developer Zone should have written '2^64 * 2^64 = 2^128' in the last part. That with the misspelling of 'together' indicates a bad hair day.

    In the Digital Random Number Generator (DRNG) Software Implementation Guide we are given a method whereby a RdRand output can be made with multiplicative prediction resistance.

    The method is to use 512 * 128 bit random numbers, encrypt them with CBC-MAC AES and use the last 128 bits, normally the MAC, as a seed. I say 'normally' because we are not using the result as a MAC - we are simply 'hammering' 512 blocks via a '512:1 data reduction' to a seed. It is usually dangerous to use a random set of Initial Values ( IV ) in the context of a MAC generation but, again, we are not using the result as a MAC and Intel use a random IV.

    I suspect that Microsoft's BCryptGenRandom may not have multiplicative prediction resistance. It certainly did not when it was introduced in Windows Vista ( NIST SP 800-90 standard ) - not sure about Windows 8 and since. I have no idea how often Microsoft reseed their generator.

    It occurred to me that we could use CBC-MAC AES on BCryptGenRandom to get "seed-grade entropy".

    The following code does just that. It uses a random IV and generates a random encryption key by BCryptGenRandom itself. The 'plaintext' to encrypt is 512 blocks of 16 random bytes ( 8KB ). The exercise is repeated to give 2 * 128 bits - a 256 bit seed. It has two modes depending upon whether we want a binary output or a hex output. If a hex output then it is placed upon the clipboard.

    How long will this lot take? On my machine a 256 bit seed is determined in 0.25ms.

    Uses?

    CMWC256Lite ( Random number generator ) is from CMWC256 and written for >= Windows XP SP3. It has a method 'GetHexSeed256' and that can be used in Seed( GetHexSeed256 ). If you have >= Windows Vista then you can now use Seed( GetSeed256( %Bin ) ).

    Another use is in generating a key for encryption for putting into a password manager.

    Code:
    #Compile Exe
    #Dim All
    #Register None
     
    #Include "Win32API.inc"
     
    %BlockSize = 16
    %AES128 = 16
    %AES192 = 24
    %AES256 = 32
    %Bin = 0
    %Hex = 1
     
    Function PBMain() As Long
     
      MsgBox GetSeed256( %Hex )
     
    End Function
     
    Function GetSeed256( lMode As Long ) As String
    Local hAESAlg, hHashAlg, dwResult, hRand, hKey As Dword
    Local IV, AESKey, sRandomBytes, sReturn, sTemp, sFunction As String
    Local i, ClipResult As Long
     
      For i = 1 To 2 ' generate 2 * 128 bits ==> 256 bits
     
        IV = String$(16,0) ' Generate a random IV
        BCryptOpenAlgorithmProvider(hRand, $$BCRYPT_RNG_ALGORITHM, $$Nul, 0) ' Prepare For Random number generation
        BCryptGenRandom(hRand, StrPtr( IV ), 16, 0)
        BCryptCloseAlgorithmProvider(hRand, 0)
     
        AESKey = String$( 32, 0 ) ' Generate a random encryption key
        BCryptOpenAlgorithmProvider(hRand, $$BCRYPT_RNG_ALGORITHM, $$Nul, 0) ' Prepare For Random number generation
        BCryptGenRandom(hRand, StrPtr( AESKey ), 32, 0) ' 256 bits
        BCryptCloseAlgorithmProvider(hRand, 0)
     
        sRandomBytes = String$( 8192, 0 ) ' Generate 512 * %BlockSize random numbers
        BCryptOpenAlgorithmProvider(hRand, $$BCRYPT_RNG_ALGORITHM, $$Nul, 0) ' Prepare For Random number generation
        BCryptGenRandom(hRand, StrPtr( sRandomBytes ), 8192, 0)
        BCryptCloseAlgorithmProvider(hRand, 0)
     
        BCryptOpenAlgorithmProvider( hAESAlg, $$BCRYPT_AES_ALGORITHM, $$Nul, 0 )
        ' Use default CBC algorithm
        BCryptGenerateSymmetricKey( hAESAlg, hKey, ByVal %null, 0, StrPtr( AESKey ), %AES128, 0 ) ' We want hKey
        ' Perform the encryption in place - output buffer = input buffer
        ' Note that we do not need padding so last parameter is zero
        BCryptEnCrypt( hKey, StrPtr( sRandomBytes ), Len( sRandomBytes ), ByVal %Null, StrPtr( IV ), _
                      %BlockSize, StrPtr( sRandomBytes ), Len( sRandomBytes ), dwResult, 0 )
        BCryptCloseAlgorithmProvider( hAESAlg, 0 )
        BCryptDestroyKey( hKey )
     
        sReturn += Right$( sRandomBytes, 16 ) ' Last block of ciphertext - 128 bits
     
      Next
     
      If lMode = %Hex Then
        sTemp = Space$( 65 )
        CryptBinaryToString( StrPtr( sReturn ), 32, %CRYPT_STRING_HEXRAW Or %CRYPT_STRING_NOCRLF, ByVal StrPtr( sTemp ), 65)
        sFunction = UCase$( Left$( sTemp, 64 ) )
        Do
          Clipboard Reset, ClipResult
        Loop Until IsTrue ClipResult
        Do
          Clipboard Set Text sFunction, ClipResult
        Loop Until IsTrue ClipResult
        Function = sFunction
      Else
        Function = sReturn
      End If
     
    End Function
    Last edited by David Roberts; 13 Nov 2015, 04:22 AM.

  • #2
    One invocation of $$BCRYPT_RNG_ALGORITHM will do.

    Replace the contents of the loop with:

    Code:
        BCryptOpenAlgorithmProvider(hRand, $$BCRYPT_RNG_ALGORITHM, $$Nul, 0) ' Prepare For Random number generation
        IV = String$(16,0) ' Generate a random IV
        BCryptGenRandom(hRand, StrPtr( IV ), 16, 0)
        AESKey = String$( 32, 0 ) ' Generate a random encryption key
        BCryptGenRandom(hRand, StrPtr( AESKey ), 32, 0) ' 256 bits
        sRandomBytes = String$( 8192, 0 ) ' Generate 512 * %BlockSize random numbers
        BCryptGenRandom(hRand, StrPtr( sRandomBytes ), 8192, 0)
        BCryptCloseAlgorithmProvider(hRand, 0)
    
        BCryptOpenAlgorithmProvider( hAESAlg, $$BCRYPT_AES_ALGORITHM, $$Nul, 0 )
        ' Use default CBC algorithm
        BCryptGenerateSymmetricKey( hAESAlg, hKey, ByVal %null, 0, StrPtr( AESKey ), %AES128, 0 ) ' We want hKey
        ' Perform the encryption in place - output buffer = input buffer
        ' Note that we do not need padding so last parameter is zero
        BCryptEnCrypt( hKey, StrPtr( sRandomBytes ), Len( sRandomBytes ), ByVal %Null, StrPtr( IV ), _
                       %BlockSize, StrPtr( sRandomBytes ), Len( sRandomBytes ), dwResult, 0 )
        BCryptCloseAlgorithmProvider( hAESAlg, 0 )
        BCryptDestroyKey( hKey )
     
        sReturn += Right$( sRandomBytes, 16 ) ' Last block of ciphertext - 128 bits
    Added: Intel use AES128 - no need to get carried away. We don't then need a 256 bit AESKey but it is a good habit to get into as a key of that size can be used for AES128, AES192 and AES256.

    Added: A 256 bit seed is hard-wired, as seen in the If/End If construct , but the above can be adapted to generate seeds in multiples of 128 bits => 128, 256, 384, 512 and so on.
    Last edited by David Roberts; 13 Nov 2015, 10:57 AM.

    Comment


    • #3
      I am getting lazy in my old age. For the sake of completeness here is a version which will generate keys in multiples of 128 bits.

      New name: GetCSSeed( lMode as Long, lMultiples as Long ) As String - lMultiples as in multiples of 128 bits and CSSeed stands for Cryptographically Secure Seed. An empty string is returned if we pass lMultiples < 1.

      Code:
      #Compile Exe
      #Dim All
      #Register None
      
      #Include "Win32API.inc"
      
      %BlockSize = 16
      %AES128 = 16
      %AES192 = 24
      %AES256 = 32
      %Bin = 0
      %Hex = 1
      
      Function PBMain() As Long
      
        MsgBox GetCSSeed( %Hex, 2 )
      
      End Function
      
      Function GetCSSeed( lMode As Long, lMultiples As Long ) As String
      ' lMultiples as in multiples of 128 bits
      Local hAESAlg, hHashAlg, dwResult, hRand, hKey As Dword
      Local IV, AESKey, sRandomBytes, sReturn, sTemp, sFunction As String
      Local i, ClipResult, lSize As Long
      
        If lMultiples < 1 Then Exit Function
      
        For i = 1 To lMultiples ' generate lMultiples * 128 bits
      
          BCryptOpenAlgorithmProvider(hRand, $$BCRYPT_RNG_ALGORITHM, $$Nul, 0) ' Prepare For Random number generation
          IV = String$(16,0) ' Generate a random IV
          BCryptGenRandom(hRand, StrPtr( IV ), 16, 0)
          AESKey = String$( 32, 0 ) ' Generate a random encryption key
          BCryptGenRandom(hRand, StrPtr( AESKey ), 32, 0) ' 256 bits
          sRandomBytes = String$( 8192, 0 ) ' Generate 512 * %BlockSize random numbers
          BCryptGenRandom(hRand, StrPtr( sRandomBytes ), 8192, 0)
          BCryptCloseAlgorithmProvider(hRand, 0)
      
          BCryptOpenAlgorithmProvider( hAESAlg, $$BCRYPT_AES_ALGORITHM, $$Nul, 0 )
          ' Use default CBC algorithm
          BCryptGenerateSymmetricKey( hAESAlg, hKey, ByVal %null, 0, StrPtr( AESKey ), %AES128, 0 ) ' We want hKey
          ' Perform the encryption in place - output buffer = input buffer
          ' Note that we do not need for padding so last paramter is zero
          BCryptEnCrypt( hKey, StrPtr( sRandomBytes ), Len( sRandomBytes ), ByVal %Null, StrPtr( IV ), _
                         %BlockSize, StrPtr( sRandomBytes ), Len( sRandomBytes ), dwResult, 0 )
          BCryptCloseAlgorithmProvider( hAESAlg, 0 )
          BCryptDestroyKey( hKey )
      
          sReturn += Right$( sRandomBytes, 16 ) ' Last block of ciphertext - 128 bits
      
        Next
      
        If lMode = %Hex Then
          lSize = 16 * 2 * lMultiples
          sTemp = Space$( lSize + 1 )
          CryptBinaryToString( StrPtr( sReturn ), lSize\2, %CRYPT_STRING_HEXRAW Or %CRYPT_STRING_NOCRLF, _
                               ByVal StrPtr( sTemp ), lSize + 1)
          sFunction = UCase$( Left$( sTemp, lSize ) )
          Do
            Clipboard Reset, ClipResult
          Loop Until IsTrue ClipResult
          Do
            Clipboard Set Text sFunction, ClipResult
          Loop Until IsTrue ClipResult
          Function = sFunction
        Else
          Function = sReturn
        End If
      
      End Function
      Last edited by David Roberts; 13 Nov 2015, 12:01 PM.

      Comment


      • #4
        I have just read a cryptanalysis of Cryptgenrandom, the predecessor to Bcryptgenrandom. I have often wondered how many bytes of data we can request before the system blocks further requests whilst it refreshes the entropy pool.

        In the code we request 8KB of data. I have decided to reduce that to 4KB requiring the following edit.

        Code:
        sRandomBytes = String$( 4096, 0 ) ' Generate 256 * %BlockSize random numbers
        BCryptGenRandom(hRand, StrPtr( sRandomBytes ), 4096, 0)
        To help, perhaps, I have included the following at the head of the Function.

        Code:
        Local Time As TIMECAPS
         
          TimeGetDevCaps( Time, SizeOf(Time) )
          TimeBeginPeriod(Time.wPeriodMin)
          Sleep 16
        and to insert a small 'breather' in the loop of 'Sleep 20' just before the Next statement.

        Whether or not the above is beneficial I do not know but it will not do any harm.

        Comment

        Working...
        X