File Coverage

MD5.xs
Criterion Covered Total %
statement 0 126 0.0
branch 0 90 0.0
condition n/a
subroutine n/a
pod n/a
total 0 216 0.0


line stmt bran cond sub pod time code
1             /*
2             * This library is free software; you can redistribute it and/or
3             * modify it under the same terms as Perl itself.
4             *
5             * Copyright 2023-2026 Michal Josef Špaček.
6             * Copyright 1998-2000 Gisle Aas.
7             * Copyright 1995-1996 Neil Winton.
8             * Copyright 1991-1992 RSA Data Security, Inc.
9             *
10             * This code is derived from Neil Winton's MD5-1.7 Perl module, which in
11             * turn is derived from the reference implementation in RFC 1321 which
12             * comes with this message:
13             *
14             * Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
15             * rights reserved.
16             *
17             * License to copy and use this software is granted provided that it
18             * is identified as the "RSA Data Security, Inc. MD5 Message-Digest
19             * Algorithm" in all material mentioning or referencing this software
20             * or this function.
21             *
22             * License is also granted to make and use derivative works provided
23             * that such works are identified as "derived from the RSA Data
24             * Security, Inc. MD5 Message-Digest Algorithm" in all material
25             * mentioning or referencing the derived work.
26             *
27             * RSA Data Security, Inc. makes no representations concerning either
28             * the merchantability of this software or the suitability of this
29             * software for any particular purpose. It is provided "as is"
30             * without express or implied warranty of any kind.
31             *
32             * These notices must be retained in any copies of any part of this
33             * documentation and/or software.
34             */
35              
36             #include "EXTERN.h"
37             #include "perl.h"
38             #include "XSUB.h"
39              
40             #include
41             #include
42              
43             STATIC const struct {
44             int (*svt_get)(SV* sv, MAGIC* mg);
45             int (*svt_set)(SV* sv, MAGIC* mg);
46             U32 (*svt_len)(SV* sv, MAGIC* mg);
47             int (*svt_clear)(SV* sv, MAGIC* mg);
48             int (*svt_free)(SV* sv, MAGIC* mg);
49             } vtbl_md5 = {
50             NULL, NULL, NULL, NULL, NULL
51             };
52              
53             /* TODO defined(USE_ITHREADS) && defined(MGf_DUP) */
54             static SV*
55 0           new_md5_ctx(pTHX_ MD5_CTX *context, const char *sclass)
56             {
57 0           SV *sv = newSV(0);
58 0           SV *obj = newRV_noinc(sv);
59              
60 0           sv_bless(obj, gv_stashpv(sclass, 0));
61 0           sv_magicext(sv, NULL, PERL_MAGIC_ext, (const MGVTBL * const)&vtbl_md5, (const char *)context, 0);
62              
63 0           return obj;
64             }
65              
66             static MD5_CTX*
67 0           get_md5_ctx(pTHX_ SV* sv)
68             {
69             MAGIC *mg;
70              
71 0 0         if (!sv_derived_from(sv, "Digest::MD5"))
72 0           croak("Not a reference to a Digest::MD5 object");
73              
74 0 0         for (mg = SvMAGIC(SvRV(sv)); mg; mg = mg->mg_moremagic) {
75 0 0         if (mg->mg_type == PERL_MAGIC_ext
76 0 0         && mg->mg_virtual == (const MGVTBL * const)&vtbl_md5) {
77 0           return (MD5_CTX *)mg->mg_ptr;
78             }
79             }
80              
81 0           croak("Failed to get MD5_CTX pointer");
82             return (MD5_CTX*)0; /* some compilers insist on a return value */
83             }
84              
85             static char*
86 0           hex_16(const unsigned char* from, char* to)
87             {
88             static const char hexdigits[] = "0123456789abcdef";
89 0           const unsigned char *end = from + 16;
90 0           char *d = to;
91              
92 0 0         while (from < end) {
93 0           *d++ = hexdigits[(*from >> 4)];
94 0           *d++ = hexdigits[(*from & 0x0F)];
95 0           from++;
96             }
97 0           *d = '\0';
98 0           return to;
99             }
100              
101             static char*
102 0           base64_16(const unsigned char* from, char* to)
103             {
104             static const char base64[] =
105             "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
106 0           const unsigned char *end = from + 16;
107             unsigned char c1, c2, c3;
108 0           char *d = to;
109              
110             while (1) {
111 0           c1 = *from++;
112 0           *d++ = base64[c1>>2];
113 0 0         if (from == end) {
114 0           *d++ = base64[(c1 & 0x3) << 4];
115 0           break;
116             }
117 0           c2 = *from++;
118 0           c3 = *from++;
119 0           *d++ = base64[((c1 & 0x3) << 4) | ((c2 & 0xF0) >> 4)];
120 0           *d++ = base64[((c2 & 0xF) << 2) | ((c3 & 0xC0) >>6)];
121 0           *d++ = base64[c3 & 0x3F];
122             }
123 0           *d = '\0';
124 0           return to;
125             }
126              
127             /* Formats */
128             #define F_BIN 0
129             #define F_HEX 1
130             #define F_B64 2
131              
132 0           static SV* make_mortal_sv(pTHX_ const unsigned char *src, int type)
133             {
134             STRLEN len;
135             char result[33];
136             char *ret;
137              
138 0           switch (type) {
139 0           case F_BIN:
140 0           ret = (char*)src;
141 0           len = 16;
142 0           break;
143 0           case F_HEX:
144 0           ret = hex_16(src, result);
145 0           len = 32;
146 0           break;
147 0           case F_B64:
148 0           ret = base64_16(src, result);
149 0           len = 22;
150 0           break;
151 0           default:
152 0           croak("Bad conversion type (%d)", type);
153             break;
154             }
155 0           return sv_2mortal(newSVpv(ret,len));
156             }
157              
158             typedef PerlIO* InputStream;
159              
160             MODULE = Digest::MD5 PACKAGE = Digest::MD5
161              
162             PROTOTYPES: DISABLE
163              
164             void
165             new(xclass)
166             SV* xclass
167             PREINIT:
168             MD5_CTX* context;
169             PPCODE:
170 0 0         if (!SvROK(xclass)) {
171             STRLEN my_na;
172 0           const char *sclass = SvPV(xclass, my_na);
173 0           New(55, context, 1, MD5_CTX);
174 0           ST(0) = sv_2mortal(new_md5_ctx(aTHX_ context, sclass));
175             } else {
176 0           context = get_md5_ctx(aTHX_ xclass);
177             }
178 0           MD5_Init(context);
179 0           XSRETURN(1);
180              
181             void
182             addfile(self, fh)
183             SV* self
184             InputStream fh
185             PREINIT:
186 0           MD5_CTX* context = get_md5_ctx(aTHX_ self);
187 0           STRLEN fill = context->Nl & 0x3F;
188             #ifdef USE_HEAP_INSTEAD_OF_STACK
189             unsigned char* buffer;
190             #else
191             unsigned char buffer[4096];
192             #endif
193             int n;
194             CODE:
195 0 0         if (fh) {
196             #ifdef USE_HEAP_INSTEAD_OF_STACK
197             New(0, buffer, 4096, unsigned char);
198             assert(buffer);
199             #endif
200 0 0         if (fill) {
201             /* The MD5Update() function is faster if it can work with
202             * complete blocks. This will fill up any buffered block
203             * first.
204             */
205 0           STRLEN missing = 64 - fill;
206 0 0         if ( (n = PerlIO_read(fh, buffer, missing)) > 0)
207 0           MD5_Update(context, buffer, n);
208             else
209 0           XSRETURN(1); /* self */
210             }
211              
212             /* Process blocks until EOF or error */
213 0 0         while ( (n = PerlIO_read(fh, buffer, sizeof(buffer))) > 0) {
214 0           MD5_Update(context, buffer, n);
215             }
216             #ifdef USE_HEAP_INSTEAD_OF_STACK
217             Safefree(buffer);
218             #endif
219 0 0         if (PerlIO_error(fh)) {
220 0           croak("Reading from filehandle failed");
221             }
222             } else {
223 0           croak("No filehandle passed");
224             }
225 0           XSRETURN(1); /* self */
226              
227             void
228             clone(self)
229             SV* self
230             PREINIT:
231 0           MD5_CTX* cont = get_md5_ctx(aTHX_ self);
232 0           const char *myname = sv_reftype(SvRV(self),TRUE);
233             MD5_CTX* context;
234             PPCODE:
235 0           New(55, context, 1, MD5_CTX);
236 0           ST(0) = sv_2mortal(new_md5_ctx(aTHX_ context, myname));
237 0           memcpy(context, cont, sizeof(MD5_CTX));
238 0           XSRETURN(1);
239              
240             void
241             DESTROY(context)
242             MD5_CTX* context
243             CODE:
244 0           Safefree(context);
245              
246             void
247             add(self, ...)
248             SV* self
249             PREINIT:
250 0           MD5_CTX* context = get_md5_ctx(aTHX_ self);
251             int i;
252             unsigned char *data;
253             STRLEN len;
254             PPCODE:
255 0 0         for (i = 1; i < items; i++) {
256 0           U32 had_utf8 = SvUTF8(ST(i));
257 0           data = (unsigned char *)(SvPVbyte(ST(i), len));
258 0           MD5_Update(context, data, len);
259 0 0         if (had_utf8)
260 0           sv_utf8_upgrade(ST(i));
261             }
262 0           XSRETURN(1); /* self */
263              
264             void
265             digest(context)
266             MD5_CTX* context
267             ALIAS:
268             Digest::MD5::digest = F_BIN
269             Digest::MD5::hexdigest = F_HEX
270             Digest::MD5::b64digest = F_B64
271             PREINIT:
272             unsigned char digeststr[16];
273             PPCODE:
274 0           MD5_Final(digeststr, context);
275 0           MD5_Init(context); /* In case it is reused */
276 0           ST(0) = make_mortal_sv(aTHX_ digeststr, ix);
277 0           XSRETURN(1);
278              
279             void
280             md5(...)
281             ALIAS:
282             Digest::MD5::md5 = F_BIN
283             Digest::MD5::md5_hex = F_HEX
284             Digest::MD5::md5_base64 = F_B64
285             PREINIT:
286             MD5_CTX context;
287             int i;
288             unsigned char *data;
289             STRLEN len;
290             unsigned char digeststr[16];
291             PPCODE:
292 0           MD5_Init(&context);
293              
294 0 0         if ((PL_dowarn & G_WARN_ON) || ckWARN(WARN_SYNTAX)) {
    0          
295 0           const char *msg = 0;
296 0 0         if (items == 1) {
297 0 0         if (SvROK(ST(0))) {
298 0           SV* sv = SvRV(ST(0));
299             char *name;
300 0 0         if (SvOBJECT(sv) && (name = HvNAME(SvSTASH(sv)))
    0          
    0          
    0          
    0          
    0          
    0          
    0          
301 0 0         && strEQ(name, "Digest::MD5"))
302 0           msg = "probably called as method";
303             else
304 0           msg = "called with reference argument";
305             }
306 0 0         } else if (items > 1) {
307 0           data = (unsigned char *)SvPV(ST(0), len);
308 0 0         if (len == 11 && memEQ("Digest::MD5", data, 11)) {
    0          
309 0           msg = "probably called as class method";
310 0 0         } else if (SvROK(ST(0))) {
311 0           SV* sv = SvRV(ST(0));
312             char *name;
313 0 0         if (SvOBJECT(sv) && (name = HvNAME(SvSTASH(sv)))
    0          
    0          
    0          
    0          
    0          
    0          
    0          
314 0 0         && strEQ(name, "Digest::MD5"))
315 0           msg = "probably called as method";
316             }
317             }
318 0 0         if (msg) {
319 0 0         const char *f = (ix == F_BIN) ? "md5" :
    0          
320             (ix == F_HEX) ? "md5_hex" : "md5_base64";
321 0           warn("&Digest::MD5::%s function %s", f, msg);
322             }
323             }
324              
325 0 0         for (i = 0; i < items; i++) {
326 0           U32 had_utf8 = SvUTF8(ST(i));
327 0           data = (unsigned char *)(SvPVbyte(ST(i), len));
328 0           MD5_Update(&context, data, len);
329 0 0         if (had_utf8)
330 0           sv_utf8_upgrade(ST(i));
331             }
332 0           MD5_Final(digeststr, &context);
333 0           ST(0) = make_mortal_sv(aTHX_ digeststr, ix);
334 0           XSRETURN(1);