1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this
4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 
6 #include "plbase64.h"
7 #include "prlog.h" /* For PR_NOT_REACHED */
8 #include "prmem.h" /* for malloc / PR_MALLOC */
9 
10 #include <string.h> /* for strlen */
11 
12 static unsigned char *base = (unsigned char *)"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
13 
14 static void
encode3to4(const unsigned char * src,unsigned char * dest)15 encode3to4
16 (
17     const unsigned char    *src,
18     unsigned char          *dest
19 )
20 {
21     PRUint32 b32 = (PRUint32)0;
22     PRIntn i, j = 18;
23 
24     for( i = 0; i < 3; i++ )
25     {
26         b32 <<= 8;
27         b32 |= (PRUint32)src[i];
28     }
29 
30     for( i = 0; i < 4; i++ )
31     {
32         dest[i] = base[ (PRUint32)((b32>>j) & 0x3F) ];
33         j -= 6;
34     }
35 
36     return;
37 }
38 
39 static void
encode2to4(const unsigned char * src,unsigned char * dest)40 encode2to4
41 (
42     const unsigned char    *src,
43     unsigned char          *dest
44 )
45 {
46     dest[0] = base[ (PRUint32)((src[0]>>2) & 0x3F) ];
47     dest[1] = base[ (PRUint32)(((src[0] & 0x03) << 4) | ((src[1] >> 4) & 0x0F)) ];
48     dest[2] = base[ (PRUint32)((src[1] & 0x0F) << 2) ];
49     dest[3] = (unsigned char)'=';
50     return;
51 }
52 
53 static void
encode1to4(const unsigned char * src,unsigned char * dest)54 encode1to4
55 (
56     const unsigned char    *src,
57     unsigned char          *dest
58 )
59 {
60     dest[0] = base[ (PRUint32)((src[0]>>2) & 0x3F) ];
61     dest[1] = base[ (PRUint32)((src[0] & 0x03) << 4) ];
62     dest[2] = (unsigned char)'=';
63     dest[3] = (unsigned char)'=';
64     return;
65 }
66 
67 static void
encode(const unsigned char * src,PRUint32 srclen,unsigned char * dest)68 encode
69 (
70     const unsigned char    *src,
71     PRUint32                srclen,
72     unsigned char          *dest
73 )
74 {
75     while( srclen >= 3 )
76     {
77         encode3to4(src, dest);
78         src += 3;
79         dest += 4;
80         srclen -= 3;
81     }
82 
83     switch( srclen )
84     {
85         case 2:
86             encode2to4(src, dest);
87             break;
88         case 1:
89             encode1to4(src, dest);
90             break;
91         case 0:
92             break;
93         default:
94             PR_NOT_REACHED("coding error");
95     }
96 
97     return;
98 }
99 
100 /*
101  * PL_Base64Encode
102  *
103  * If the destination argument is NULL, a return buffer is
104  * allocated, and the data therein will be null-terminated.
105  * If the destination argument is not NULL, it is assumed to
106  * be of sufficient size, and the contents will not be null-
107  * terminated by this routine.
108  *
109  * Returns null if the allocation fails.
110  */
111 
112 PR_IMPLEMENT(char *)
PL_Base64Encode(const char * src,PRUint32 srclen,char * dest)113 PL_Base64Encode
114 (
115     const char *src,
116     PRUint32    srclen,
117     char       *dest
118 )
119 {
120     if( 0 == srclen )
121     {
122         size_t len = strlen(src);
123         srclen = len;
124         /* Detect truncation. */
125         if( srclen != len )
126         {
127             return (char *)0;
128         }
129     }
130 
131     if( (char *)0 == dest )
132     {
133         PRUint32 destlen;
134         /* Ensure all PRUint32 values stay within range. */
135         if( srclen > (PR_UINT32_MAX/4) * 3 )
136         {
137             return (char *)0;
138         }
139         destlen = ((srclen + 2)/3) * 4;
140         dest = (char *)PR_MALLOC(destlen + 1);
141         if( (char *)0 == dest )
142         {
143             return (char *)0;
144         }
145         dest[ destlen ] = (char)0; /* null terminate */
146     }
147 
148     encode((const unsigned char *)src, srclen, (unsigned char *)dest);
149     return dest;
150 }
151 
152 static PRInt32
codetovalue(unsigned char c)153 codetovalue
154 (
155     unsigned char c
156 )
157 {
158     if( (c >= (unsigned char)'A') && (c <= (unsigned char)'Z') )
159     {
160         return (PRInt32)(c - (unsigned char)'A');
161     }
162     else if( (c >= (unsigned char)'a') && (c <= (unsigned char)'z') )
163     {
164         return ((PRInt32)(c - (unsigned char)'a') +26);
165     }
166     else if( (c >= (unsigned char)'0') && (c <= (unsigned char)'9') )
167     {
168         return ((PRInt32)(c - (unsigned char)'0') +52);
169     }
170     else if( (unsigned char)'+' == c )
171     {
172         return (PRInt32)62;
173     }
174     else if( (unsigned char)'/' == c )
175     {
176         return (PRInt32)63;
177     }
178     else
179     {
180         return -1;
181     }
182 }
183 
184 static PRStatus
decode4to3(const unsigned char * src,unsigned char * dest)185 decode4to3
186 (
187     const unsigned char    *src,
188     unsigned char          *dest
189 )
190 {
191     PRUint32 b32 = (PRUint32)0;
192     PRInt32 bits;
193     PRIntn i;
194 
195     for( i = 0; i < 4; i++ )
196     {
197         bits = codetovalue(src[i]);
198         if( bits < 0 )
199         {
200             return PR_FAILURE;
201         }
202 
203         b32 <<= 6;
204         b32 |= bits;
205     }
206 
207     dest[0] = (unsigned char)((b32 >> 16) & 0xFF);
208     dest[1] = (unsigned char)((b32 >>  8) & 0xFF);
209     dest[2] = (unsigned char)((b32      ) & 0xFF);
210 
211     return PR_SUCCESS;
212 }
213 
214 static PRStatus
decode3to2(const unsigned char * src,unsigned char * dest)215 decode3to2
216 (
217     const unsigned char    *src,
218     unsigned char          *dest
219 )
220 {
221     PRUint32 b32 = (PRUint32)0;
222     PRInt32 bits;
223     PRUint32 ubits;
224 
225     bits = codetovalue(src[0]);
226     if( bits < 0 )
227     {
228         return PR_FAILURE;
229     }
230 
231     b32 = (PRUint32)bits;
232     b32 <<= 6;
233 
234     bits = codetovalue(src[1]);
235     if( bits < 0 )
236     {
237         return PR_FAILURE;
238     }
239 
240     b32 |= (PRUint32)bits;
241     b32 <<= 4;
242 
243     bits = codetovalue(src[2]);
244     if( bits < 0 )
245     {
246         return PR_FAILURE;
247     }
248 
249     ubits = (PRUint32)bits;
250     b32 |= (ubits >> 2);
251 
252     dest[0] = (unsigned char)((b32 >> 8) & 0xFF);
253     dest[1] = (unsigned char)((b32     ) & 0xFF);
254 
255     return PR_SUCCESS;
256 }
257 
258 static PRStatus
decode2to1(const unsigned char * src,unsigned char * dest)259 decode2to1
260 (
261     const unsigned char    *src,
262     unsigned char          *dest
263 )
264 {
265     PRUint32 b32;
266     PRUint32 ubits;
267     PRInt32 bits;
268 
269     bits = codetovalue(src[0]);
270     if( bits < 0 )
271     {
272         return PR_FAILURE;
273     }
274 
275     ubits = (PRUint32)bits;
276     b32 = (ubits << 2);
277 
278     bits = codetovalue(src[1]);
279     if( bits < 0 )
280     {
281         return PR_FAILURE;
282     }
283 
284     ubits = (PRUint32)bits;
285     b32 |= (ubits >> 4);
286 
287     dest[0] = (unsigned char)b32;
288 
289     return PR_SUCCESS;
290 }
291 
292 static PRStatus
decode(const unsigned char * src,PRUint32 srclen,unsigned char * dest)293 decode
294 (
295     const unsigned char    *src,
296     PRUint32                srclen,
297     unsigned char          *dest
298 )
299 {
300     PRStatus rv;
301 
302     while( srclen >= 4 )
303     {
304         rv = decode4to3(src, dest);
305         if( PR_SUCCESS != rv )
306         {
307             return PR_FAILURE;
308         }
309 
310         src += 4;
311         dest += 3;
312         srclen -= 4;
313     }
314 
315     switch( srclen )
316     {
317         case 3:
318             rv = decode3to2(src, dest);
319             break;
320         case 2:
321             rv = decode2to1(src, dest);
322             break;
323         case 1:
324             rv = PR_FAILURE;
325             break;
326         case 0:
327             rv = PR_SUCCESS;
328             break;
329         default:
330             PR_NOT_REACHED("coding error");
331     }
332 
333     return rv;
334 }
335 
336 /*
337  * PL_Base64Decode
338  *
339  * If the destination argument is NULL, a return buffer is
340  * allocated and the data therein will be null-terminated.
341  * If the destination argument is not null, it is assumed
342  * to be of sufficient size, and the data will not be null-
343  * terminated by this routine.
344  *
345  * Returns null if the allocation fails, or if the source string is
346  * not well-formed.
347  */
348 
349 PR_IMPLEMENT(char *)
PL_Base64Decode(const char * src,PRUint32 srclen,char * dest)350 PL_Base64Decode
351 (
352     const char *src,
353     PRUint32    srclen,
354     char       *dest
355 )
356 {
357     PRStatus status;
358     PRBool allocated = PR_FALSE;
359 
360     if( (char *)0 == src )
361     {
362         return (char *)0;
363     }
364 
365     if( 0 == srclen )
366     {
367         size_t len = strlen(src);
368         srclen = len;
369         /* Detect truncation. */
370         if( srclen != len )
371         {
372             return (char *)0;
373         }
374     }
375 
376     if( srclen && (0 == (srclen & 3)) )
377     {
378         if( (char)'=' == src[ srclen-1 ] )
379         {
380             if( (char)'=' == src[ srclen-2 ] )
381             {
382                 srclen -= 2;
383             }
384             else
385             {
386                 srclen -= 1;
387             }
388         }
389     }
390 
391     if( (char *)0 == dest )
392     {
393         /* The following computes ((srclen * 3) / 4) without overflow. */
394         PRUint32 destlen = (srclen / 4) * 3 + ((srclen % 4) * 3) / 4;
395         dest = (char *)PR_MALLOC(destlen + 1);
396         if( (char *)0 == dest )
397         {
398             return (char *)0;
399         }
400         dest[ destlen ] = (char)0; /* null terminate */
401         allocated = PR_TRUE;
402     }
403 
404     status = decode((const unsigned char *)src, srclen, (unsigned char *)dest);
405     if( PR_SUCCESS != status )
406     {
407         if( PR_TRUE == allocated )
408         {
409             PR_DELETE(dest);
410         }
411 
412         return (char *)0;
413     }
414 
415     return dest;
416 }
417