File Coverage

inc/CryptX_PRNG.xs.inc
Criterion Covered Total %
statement 109 128 85.1
branch 62 88 70.4
condition n/a
subroutine n/a
pod n/a
total 171 216 79.1


line stmt bran cond sub pod time code
1             MODULE = CryptX PACKAGE = Crypt::PRNG
2              
3             PROTOTYPES: DISABLE
4              
5             #define CRYPTX_PRNG_MAX_OUTPUT_LEN 1000000000UL /* below ULONG_MAX/4, prevents base64 sizing overflow on 32bit */
6              
7             Crypt::PRNG
8             new(char * class, ...)
9             CODE:
10             {
11 56           IV curpid = (IV)PerlProc_getpid();
12             int rv, id, idx;
13 56           unsigned char *ent=NULL;
14 56           STRLEN ent_len=0;
15             unsigned char entropy_buf[40];
16 56           char *prng_name = (char *)"ChaCha20";
17 56           STRLEN prng_name_len = 0;
18 56           SV *entropy = &PL_sv_undef;
19 56           SV *prng_name_sv = NULL;
20              
21             /* we need to handle:
22             Crypt::PRNG->new('RC4');
23             Crypt::Cipher::RC4->new();
24             */
25 56           idx = strcmp("Crypt::PRNG", class) == 0 ? 1 : 0;
26 56 50         if (idx == 1 && idx + 1 <= items) {
    100          
27 51           prng_name_sv = ST(idx);
28 51 100         if (!SvPOK_spec(prng_name_sv)) croak("FATAL: invalid PRNG name");
    100          
    50          
    100          
    50          
29 49           prng_name = SvPVbyte(prng_name_sv, prng_name_len);
30             }
31 5 50         else if (idx == 0) {
32 0           prng_name = class; /* cryptx_internal_find_prng strips the prefix */
33             }
34 54 100         if (idx + 2 <= items) entropy = ST(idx + 1);
35              
36 54           Newz(0, RETVAL, 1, struct prng_struct);
37 54 50         if (!RETVAL) croak("FATAL: Newz failed");
38              
39 54           id = cryptx_internal_find_prng(prng_name);
40 54 100         if (id == -1) {
41 1           Safefree(RETVAL);
42 1           croak("FATAL: find_prng failed for '%s'", prng_name);
43             }
44 53           RETVAL->last_pid = curpid;
45 53           RETVAL->desc = &prng_descriptor[id];
46              
47 53           rv = RETVAL->desc->start(&RETVAL->state);
48 53 50         if (rv != CRYPT_OK) {
49 0           Safefree(RETVAL);
50 0           croak("FATAL: PRNG_start failed: %s", error_to_string(rv));
51             }
52              
53 53 100         if (SvOK(entropy)) {
54 32           ent = (unsigned char *) SvPVbyte(entropy, ent_len);
55 32           rv = RETVAL->desc->add_entropy(ent, (unsigned long)ent_len, &RETVAL->state);
56 32 100         if (rv != CRYPT_OK) {
57 5           cryptx_internal_prng_done(RETVAL);
58 5           Safefree(RETVAL);
59 5           croak("FATAL: PRNG_add_entropy failed: %s", error_to_string(rv));
60             }
61             }
62             else {
63 21 50         if (rng_get_bytes(entropy_buf, 40, NULL) != 40) {
64 0           cryptx_internal_prng_done(RETVAL);
65 0           Safefree(RETVAL);
66 0           croak("FATAL: rng_get_bytes failed");
67             }
68 21           rv = RETVAL->desc->add_entropy(entropy_buf, 40, &RETVAL->state);
69 21           zeromem(entropy_buf, 40);
70 21 50         if (rv != CRYPT_OK) {
71 0           cryptx_internal_prng_done(RETVAL);
72 0           Safefree(RETVAL);
73 0           croak("FATAL: PRNG_add_entropy failed: %s", error_to_string(rv));
74             }
75             }
76 48           rv = RETVAL->desc->ready(&RETVAL->state);
77 48 100         if (rv != CRYPT_OK) {
78 4           cryptx_internal_prng_done(RETVAL);
79 4           Safefree(RETVAL);
80 4           croak("FATAL: PRNG_ready failed: %s", error_to_string(rv));
81             }
82             }
83             OUTPUT:
84             RETVAL
85              
86             void
87             DESTROY(Crypt::PRNG self)
88             CODE:
89             {
90 44 50         if (self != NULL) {
91 44           cryptx_internal_prng_done(self);
92 44           Safefree(self);
93             }
94             }
95              
96             void
97             add_entropy(Crypt::PRNG self, SV * entropy=&PL_sv_undef)
98             CODE:
99             {
100 2           STRLEN in_len=0;
101 2           unsigned char *in_buffer=NULL;
102             unsigned char entropy_buf[40];
103             int rv;
104 2 100         if (SvOK(entropy)) {
105 1           in_buffer = (unsigned char *) SvPVbyte(entropy, in_len);
106 1           rv = self->desc->add_entropy(in_buffer, (unsigned long)in_len, &self->state);
107 1 50         if (rv != CRYPT_OK) croak("FATAL: PRNG_add_entropy failed: %s", error_to_string(rv));
108             }
109             else {
110 1 50         if (rng_get_bytes(entropy_buf, 40, NULL) != 40) croak("FATAL: rng_get_bytes failed");
111 1           rv = self->desc->add_entropy(entropy_buf, 40, &self->state);
112 1           zeromem(entropy_buf, 40);
113 1 50         if (rv != CRYPT_OK) croak("FATAL: PRNG_add_entropy failed: %s", error_to_string(rv));
114             }
115 2           rv = self->desc->ready(&self->state);
116 2 50         if (rv != CRYPT_OK) croak("FATAL: PRNG_ready failed: %s", error_to_string(rv));
117             }
118              
119             SV *
120             bytes(Crypt::PRNG self, unsigned long output_len)
121             ALIAS:
122             bytes_hex = 1
123             bytes_b64 = 2
124             bytes_b64u = 3
125             CODE:
126             {
127             int rv_len, rv;
128             unsigned long len, chunks;
129 148           unsigned char *tmp = NULL;
130             char *rdata;
131              
132 148 100         if (output_len > CRYPTX_PRNG_MAX_OUTPUT_LEN) croak("FATAL: output_len too large");
133              
134 132 100         if (output_len == 0) {
135 8           RETVAL = newSVpvn("", 0);
136             }
137             else {
138 124           cryptx_internal_prng_reseed(self);
139 124 100         if (ix == 1) {
140             /* HEX */
141 18           Newz(0, tmp, output_len, unsigned char);
142 18 50         if (tmp == NULL) croak("FATAL: Newz failed");
143 18           rv_len = (self->desc->read)(tmp, (unsigned long)output_len, &self->state);
144 18 50         if ((UV)rv_len != output_len) {
145 0           Safefree(tmp);
146 0           croak("FATAL: PRNG_read failed");
147             }
148 18           RETVAL = NEWSV(0, output_len * 2 + 1); /* avoid zero! */
149 18           SvPOK_only(RETVAL);
150 18           SvCUR_set(RETVAL, output_len * 2 + 1);
151 18           rdata = SvPVX(RETVAL);
152 18           len = output_len * 2 + 1;
153 18           rv = base16_encode(tmp, output_len, rdata, &len, 0);
154 18           SvCUR_set(RETVAL, len);
155 18           Safefree(tmp);
156 18 50         if (rv != CRYPT_OK) {
157 0           SvREFCNT_dec(RETVAL);
158 0           croak("FATAL: base16_encode failed");
159             }
160             }
161 106 100         else if (ix == 2 || ix == 3) {
    100          
162             /* BASE64 or BASE64URL */
163 36           chunks = (output_len + 2) / 3;
164 36           Newz(0, tmp, output_len, unsigned char);
165 36 50         if (tmp == NULL) croak("FATAL: Newz failed");
166 36           rv_len = (self->desc->read)(tmp, (unsigned long)output_len, &self->state);
167 36 50         if ((UV)rv_len != output_len) {
168 0           Safefree(tmp);
169 0           croak("FATAL: PRNG_read failed");
170             }
171 36           len = chunks * 4 + 1;
172 36           RETVAL = NEWSV(0, len); /* avoid zero! */
173 36           SvPOK_only(RETVAL);
174 36           SvCUR_set(RETVAL, len);
175 36           rdata = SvPVX(RETVAL);
176 36 100         rv = ix == 3 ? base64url_encode(tmp, output_len, rdata, &len) :
177 18           base64_encode(tmp, output_len, rdata, &len);
178 36           SvCUR_set(RETVAL, len);
179 36           Safefree(tmp);
180 36 50         if (rv != CRYPT_OK) {
181 0           SvREFCNT_dec(RETVAL);
182 0 0         croak(ix == 3 ? "FATAL: base64url_encode failed" : "FATAL: base64_encode failed");
183             }
184             }
185             else {
186             /* RAW BYTES */
187 70           RETVAL = NEWSV(0, output_len); /* avoid zero! */
188 70           SvPOK_only(RETVAL);
189 70           SvCUR_set(RETVAL, output_len);
190 70           rdata = SvPVX(RETVAL);
191 70           rv_len = (self->desc->read)((unsigned char*)rdata, (unsigned long)output_len, &self->state);
192 70 50         if ((UV)rv_len != output_len) {
193 0           SvREFCNT_dec(RETVAL);
194 0           croak("FATAL: PRNG_read failed");
195             }
196             }
197             }
198             }
199             OUTPUT:
200             RETVAL
201              
202             UV
203             int32(Crypt::PRNG self)
204             CODE:
205             {
206             int i;
207             unsigned char rdata[4];
208              
209 12000           cryptx_internal_prng_reseed(self);
210              
211 12000           i = (self->desc->read)(rdata, 4, &self->state);
212 12000 50         if (i != 4) croak("FATAL: PRNG_read failed");
213 12000 100         RETVAL = ((UV)(rdata[0])<<24) + ((UV)(rdata[1])<<16) + ((UV)(rdata[2])<<8) + ((UV)(rdata[3]));
214             }
215             OUTPUT:
216             RETVAL
217              
218             NV
219             double(Crypt::PRNG self, SV * limit_sv = NULL)
220             CODE:
221             {
222             int i;
223             unsigned long a, b; /* 32bit is enough */
224             unsigned char rdata[7]; /* for double we need 53 bits */
225              
226 24000           cryptx_internal_prng_reseed(self);
227              
228 24000           i = (self->desc->read)(rdata, 7, &self->state);
229 24000 50         if (i != 7) croak("FATAL: PRNG_read failed");
230 24000           a = (((unsigned long)(rdata[0])<<16) + ((unsigned long)(rdata[1])<<8) + ((unsigned long)(rdata[2]))) & 0x1FFFFF; /* 21 bits */
231 24000           b = ((unsigned long)(rdata[3])<<24) + ((unsigned long)(rdata[4])<<16) + ((unsigned long)(rdata[5])<<8) + ((unsigned long)(rdata[6])); /* 32 bits */
232 24000           RETVAL = ( (NV)a * 4294967296.0 + (NV)b ) / 9007199254740992.0; /* (a * 2^32 + b) / 2^53 */
233 24000 100         if (limit_sv && SvOK(limit_sv)) {
    50          
234 18000           NV limit = SvNV(limit_sv);
235 18000 100         if (limit > 0 || limit < 0) RETVAL = RETVAL * limit;
    50          
236             }
237             }
238             OUTPUT:
239             RETVAL