File Coverage

blib/lib/Crypt/Random/Source/Strong/Win32.pm
Criterion Covered Total %
statement 7 9 77.7
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 10 12 83.3


line stmt bran cond sub pod time code
1             package Crypt::Random::Source::Strong::Win32;
2 1     1   47122 use 5.008;
  1         5  
  1         60  
3 1     1   1272 use Any::Moose;
  1         46338  
  1         7  
4 1     1   1244 use Win32;
  0            
  0            
5             use Win32::API;
6             use Win32::API::Type;
7              
8             our $VERSION = '0.07';
9              
10             extends qw(
11             Crypt::Random::Source::Strong
12             Crypt::Random::Source::Base
13             );
14              
15             has 'rtlgenrand' => (is => 'ro', isa => 'Win32::API', lazy_build => 1);
16              
17             # For Windows 2000 only.
18             has 'crypt_context' => (is => 'ro', isa => 'Int', lazy_build => 1);
19             has 'cryptacquirecontext' => (is => 'ro', isa => 'Win32::API',
20             lazy_build => 1);
21             has 'cryptgenrandom' => (is => 'ro', isa => 'Win32::API', lazy_build => 1);
22              
23             # The type of cryptographic service provider we want to use.
24             # This doesn't really matter for our purposes, so we just pick
25             # PROV_RSA_FULL, which seems reasonable. For more info, see
26             # http://msdn.microsoft.com/en-us/library/aa380244(v=VS.85).aspx
27             use constant PROV_RSA_FULL => 1;
28              
29             # Flags for CryptGenRandom:
30             # Don't ever display a UI to the user, just fail if one would be needed.
31             use constant CRYPT_SILENT => 64;
32             # Don't require existing public/private keypairs.
33             use constant CRYPT_VERIFYCONTEXT => 0xF0000000;
34              
35             # For some reason, BOOLEAN doesn't work properly as a return type with Win32::API.
36             use constant RTLGENRANDOM_PROTO => <
37             INT SystemFunction036(
38             PVOID RandomBuffer,
39             ULONG RandomBufferLength
40             )
41             END
42              
43             our $IS_WIN2K;
44             BEGIN {
45             my ($major, $minor) = (Win32::GetOSVersion())[1,2];
46             $IS_WIN2K = ($major == 5 and $minor == 0) ? 1 : 0;
47             }
48              
49             # This should be preferred over other generators, on Windows.
50             sub rank { 10 }
51              
52             sub available {
53             return 0 if !($^O eq 'MSWin32' or $^O eq 'cygwin');
54              
55             my $major = (Win32::GetOSVersion())[1];
56              
57             # Major 5 is Windows 2000 and above.
58             return 0 if $major < 5;
59              
60             return 1;
61             }
62              
63             sub get {
64             my ($self, $n) = @_;
65              
66             my $buffer = chr(0) x $n;
67              
68             # Win2K requires a slower, bulkier solution.
69             if ($IS_WIN2K) {
70             my $context = $self->crypt_context;
71             my $result = $self->cryptgenrandom->Call($context, $n, $buffer);
72             if (!$result) {
73             die "CryptGenRandom failed: $^E";
74             }
75             return $buffer;
76             }
77              
78             my $result = $self->rtlgenrand->Call($buffer, $n);
79             if (!$result) {
80             die "RtlGenRand failed: $^E";
81             }
82             return $buffer;
83             }
84              
85             sub _build_rtlgenrand {
86             my $func = Win32::API->new('advapi32', RTLGENRANDOM_PROTO);
87             if (!defined $func) {
88             die "Could not import SystemFunction036: $^E";
89             }
90             return $func;
91             }
92              
93             sub _build_cryptgenrandom {
94             my $func = Win32::API->new("advapi32", 'CryptGenRandom', 'NNP', 'I');
95             if (!defined $func) {
96             die "Could not import CryptGenRandom: $^E"
97             }
98             return $func;
99             }
100              
101             sub _build_cryptacquirecontext {
102             my $func = Win32::API->new("advapi32", 'CryptAcquireContextA', 'PPPNN', 'I');
103             if (!defined $func) {
104             die "Could not import CryptAcquireContext: $^E"
105             }
106             return $func;
107             }
108              
109             sub _build_crypt_context {
110             my ($self) = @_;
111             my $func = $self->cryptacquirecontext;
112             my $context = chr(0) x Win32::API::Type->sizeof('PULONG');
113             my $result = $func->Call($context, 0, 0, PROV_RSA_FULL,
114             CRYPT_SILENT | CRYPT_VERIFYCONTEXT);
115             my $pack_type = Win32::API::Type::packing('PULONG');
116             $context = unpack($pack_type, $context);
117             if (!$result) {
118             die "CryptAcquireContext failed: $^E";
119             }
120             return $context;
121             }
122              
123             __PACKAGE__
124              
125             __END__