David Garamond <lists / zara.6.isreserved.com> wrote in message news:<400FF2F2.7080800 / zara.6.isreserved.com>...
> I'm trying to do the equivalent of this C code:
> 
> long getrand()
> {
>    HCRYPTPROV hProv = 0;
>    CryptAcquireContext(&hProv,
>      0, 0, PROV_RSA_FULL,
>      CRYPT_VERIFYCONTEXT);
>    long rnd;
>    CryptGenRandom(hProv,
>      sizeof(rnd), (BYTE*)&rnd);
>    CryptReleaseContext(hProv, 0);
>    return rnd;
> }
> 
> Eexcept that I want to generate 16 bytes of random numbers. Here's my 
> stab so far:
> 
> require 'Win32API'
> cac = Win32API.new("advapi32", "CryptAcquireContext",
>                     ['P','L','L','I','I'], 'N')
> cgr = Win32API.new("advapi32", "CryptGenRandom", ['P','I','P'], 'N')
> hProv = " " * 16
> cac.call(hProv, 0, 0, 1, 251658240)
> guid = " " * 16
> cgr.call(hProv, 16, guid)
> p guid
> 
>  From wincrypt.h:
> 
> #define PROV_RSA_FULL             1
> #define CRYPT_VERIFYCONTEXT       0xF0000000
> 
> BOOL WINAPI CryptGenRandom (HCRYPTPROV hProv, DWORD dwLen, BYTE *pbBuffer);
> 
> BOOL WINAPI CryptAcquireContext(HCRYPTPROV *phProv, LPCSTR pszContainer,
> 				   LPCSTR pszProvider, DWORD dwProvType,
> 				   DWORD dwFlags);
> 
> 
> No segfaults, but the output comes up still empty. Any pointer where I 
> did wrong? Sigh, I'm so terrible when it comes to C.


Unfortunately you have two mistakes. The first simple one is that the
decimal equivalent for 0xF0000000 (CRYPT_VERIFYCONTEXT) is 4026531840
not 251658240.

The second is a little more subtle. CryptAcquireContext takes a
pointer to a HCRYPTPROV which is a 4 byte long. After this call
returns, the HCRYPTPROV value needs to be unpacked as a L:

hProvI, = hProv.unpack('L')

CryptGenRandom takes a HCRYPTPROV value (a long). So the first
parameter should be specified as a 'L':

cgr = Win32API.new("advapi32", "CryptGenRandom", ['L','I','P'], 'N')

Here is you code snippet rewritten with as few changes as possible:

#==========================================================================
require 'Win32API'
cac = Win32API.new("advapi32", "CryptAcquireContext",
                    ['P','L','L','I','I'], 'N')
cgr = Win32API.new("advapi32", "CryptGenRandom", ['L','I','P'], 'N')
hProv = " " * 16
cac.call(hProv, 0, 0, 1, 4026531840)
guid = " " * 16
hProvI, = hProv.unpack('L')
cgr.call(hProvI, 16, guid)
p guid
#==========================================================================


Really you should check for errors and display them. Here is a
somewhat more refined implementation:

#==========================================================================
require 'Win32API'

PROV_RSA_FULL       = 1
CRYPT_VERIFYCONTEXT = 0xF0000000

FORMAT_MESSAGE_IGNORE_INSERTS  = 0x00000200
FORMAT_MESSAGE_FROM_SYSTEM     = 0x00001000

CryptAcquireContext = Win32API.new("advapi32", "CryptAcquireContext",
'PPPII', 'L')
CryptGenRandom = Win32API.new("advapi32", "CryptGenRandom", 
                              'LIP', 'L')
GetLastError = Win32API.new("Kernel32", "GetLastError", '', 'L')
FormatMessageA = Win32API.new("Kernel32", "FormatMessageA",
                              'LPLLPLPPPPPPPP', 'L')

def lastErrorMessage
  code = GetLastError.call
  msg = "\0" * 1024
  len = FormatMessageA.call(FORMAT_MESSAGE_IGNORE_INSERTS +
                            FORMAT_MESSAGE_FROM_SYSTEM, 0,
                            code, 0, msg, 1024, nil, nil,
                            nil, nil, nil, nil, nil, nil)
  msg[0, len].tr("\r", '').chomp
end

def getRandGUID
  hProvStr = " " * 4
  if CryptAcquireContext.call(hProvStr, nil, nil, PROV_RSA_FULL,
                              CRYPT_VERIFYCONTEXT) == 0
    raise "CryptAcquireContext failed: #{lastErrorMessage}"
  end
  hProv, = hProvStr.unpack('L')
  guid = " " * 16
  if CryptGenRandom.call(hProv, 16, guid) == 0
    raise "CryptAcquireContext failed: #{lastErrorMessage}"
  end
  guid
end

p getRandGUID
#==========================================================================