File Coverage

inc/CryptX_PRNG.xs.inc
Criterion Covered Total %
statement 75 120 62.5
branch 41 98 41.8
condition n/a
subroutine n/a
pod n/a
total 116 218 53.2


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