2 * base64.c: base64 encoding and decoding functions
4 * ====================================================================
5 * Copyright (c) 2000-2004 CollabNet. All rights reserved.
7 * This software is licensed as described in the file COPYING, which
8 * you should have received as part of this distribution. The terms
9 * are also available at http://subversion.tigris.org/license-1.html.
10 * If newer versions of this license are posted there, you may use a
11 * newer version instead, at your option.
13 * This software consists of voluntary contributions made by many
14 * individuals. For exact contribution history, see the revision
15 * history and logs, available at http://subversion.tigris.org/.
16 * ====================================================================
24 #include <apr_pools.h>
25 #include <apr_general.h> /* for APR_INLINE */
27 #include "svn_pools.h"
29 #include "svn_error.h"
30 #include "svn_base64.h"
33 #define BASE64_LINELEN 76
34 static const char base64tab
[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
35 "abcdefghijklmnopqrstuvwxyz0123456789+/";
39 /* Binary input --> base64-encoded output */
43 unsigned char buf
[3]; /* Bytes waiting to be encoded */
44 int buflen
; /* Number of bytes waiting */
45 int linelen
; /* Bytes output so far on this line */
50 /* Base64-encode a group. IN needs to have three bytes and OUT needs
51 to have room for four bytes. The input group is treated as four
52 six-bit units which are treated as lookups into base64tab for the
53 bytes of the output group. */
54 static APR_INLINE
void
55 encode_group(const unsigned char *in
, char *out
)
57 out
[0] = base64tab
[in
[0] >> 2];
58 out
[1] = base64tab
[((in
[0] & 0x3) << 4) | (in
[1] >> 4)];
59 out
[2] = base64tab
[((in
[1] & 0xf) << 2) | (in
[2] >> 6)];
60 out
[3] = base64tab
[in
[2] & 0x3f];
64 /* Base64-encode a byte string which may or may not be the totality of
65 the data being encoded. INBUF and *INBUFLEN carry the leftover
66 data from call to call, and *LINELEN carries the length of the
67 current output line. Make INBUF have room for three characters and
68 initialize *INBUFLEN and *LINELEN to 0. Output will be appended to
71 encode_bytes(svn_stringbuf_t
*str
, const char *data
, apr_size_t len
,
72 unsigned char *inbuf
, int *inbuflen
, int *linelen
)
75 const char *p
= data
, *end
= data
+ len
;
77 /* Keep encoding three-byte groups until we run out. */
78 while (*inbuflen
+ (end
- p
) >= 3)
80 memcpy(inbuf
+ *inbuflen
, p
, 3 - *inbuflen
);
82 encode_group(inbuf
, group
);
83 svn_stringbuf_appendbytes(str
, group
, 4);
86 if (*linelen
== BASE64_LINELEN
)
88 svn_stringbuf_appendcstr(str
, "\n");
93 /* Tack any extra input onto *INBUF. */
94 memcpy(inbuf
+ *inbuflen
, p
, end
- p
);
95 *inbuflen
+= (end
- p
);
99 /* Encode leftover data, if any, and possibly a final newline,
100 appending to STR. LEN must be in the range 0..2. */
102 encode_partial_group(svn_stringbuf_t
*str
, const unsigned char *extra
,
103 int len
, int linelen
)
105 unsigned char ingroup
[3];
110 memcpy(ingroup
, extra
, len
);
111 memset(ingroup
+ len
, 0, 3 - len
);
112 encode_group(ingroup
, outgroup
);
113 memset(outgroup
+ (len
+ 1), '=', 4 - (len
+ 1));
114 svn_stringbuf_appendbytes(str
, outgroup
, 4);
118 svn_stringbuf_appendcstr(str
, "\n");
122 /* Write handler for svn_base64_encode. */
124 encode_data(void *baton
, const char *data
, apr_size_t
*len
)
126 struct encode_baton
*eb
= baton
;
127 apr_pool_t
*subpool
= svn_pool_create(eb
->pool
);
128 svn_stringbuf_t
*encoded
= svn_stringbuf_create("", subpool
);
130 svn_error_t
*err
= SVN_NO_ERROR
;
132 /* Encode this block of data and write it out. */
133 encode_bytes(encoded
, data
, *len
, eb
->buf
, &eb
->buflen
, &eb
->linelen
);
134 enclen
= encoded
->len
;
136 err
= svn_stream_write(eb
->output
, encoded
->data
, &enclen
);
137 svn_pool_destroy(subpool
);
142 /* Close handler for svn_base64_encode(). */
144 finish_encoding_data(void *baton
)
146 struct encode_baton
*eb
= baton
;
147 svn_stringbuf_t
*encoded
= svn_stringbuf_create("", eb
->pool
);
149 svn_error_t
*err
= SVN_NO_ERROR
;
151 /* Encode a partial group at the end if necessary, and write it out. */
152 encode_partial_group(encoded
, eb
->buf
, eb
->buflen
, eb
->linelen
);
153 enclen
= encoded
->len
;
155 err
= svn_stream_write(eb
->output
, encoded
->data
, &enclen
);
157 /* Pass on the close request and clean up the baton. */
158 if (err
== SVN_NO_ERROR
)
159 err
= svn_stream_close(eb
->output
);
160 svn_pool_destroy(eb
->pool
);
166 svn_base64_encode(svn_stream_t
*output
, apr_pool_t
*pool
)
168 apr_pool_t
*subpool
= svn_pool_create(pool
);
169 struct encode_baton
*eb
= apr_palloc(subpool
, sizeof(*eb
));
170 svn_stream_t
*stream
;
176 stream
= svn_stream_create(eb
, pool
);
177 svn_stream_set_write(stream
, encode_data
);
178 svn_stream_set_close(stream
, finish_encoding_data
);
184 svn_base64_encode_string(const svn_string_t
*str
, apr_pool_t
*pool
)
186 svn_stringbuf_t
*encoded
= svn_stringbuf_create("", pool
);
187 svn_string_t
*retval
= apr_pcalloc(pool
, sizeof(*retval
));
188 unsigned char ingroup
[3];
189 int ingrouplen
= 0, linelen
= 0;
191 encode_bytes(encoded
, str
->data
, str
->len
, ingroup
, &ingrouplen
, &linelen
);
192 encode_partial_group(encoded
, ingroup
, ingrouplen
, linelen
);
193 retval
->data
= encoded
->data
;
194 retval
->len
= encoded
->len
;
200 /* Base64-encoded input --> binary output */
202 struct decode_baton
{
203 svn_stream_t
*output
;
204 unsigned char buf
[4]; /* Bytes waiting to be decoded */
205 int buflen
; /* Number of bytes waiting */
206 svn_boolean_t done
; /* True if we already saw an '=' */
211 /* Base64-decode a group. IN needs to have four bytes and OUT needs
212 to have room for three bytes. The input bytes must already have
213 been decoded from base64tab into the range 0..63. The four
214 six-byte values are pasted together to form three eight-bit bytes. */
215 static APR_INLINE
void
216 decode_group(const unsigned char *in
, char *out
)
218 out
[0] = (in
[0] << 2) | (in
[1] >> 4);
219 out
[1] = ((in
[1] & 0xf) << 4) | (in
[2] >> 2);
220 out
[2] = ((in
[2] & 0x3) << 6) | in
[3];
223 /* Lookup table for base64 characters; reverse_base64[ch] gives a
224 negative value if ch is not a valid base64 character, or otherwise
225 the value of the byte represented; 'A' => 0 etc. */
226 static const signed char reverse_base64
[256] = {
227 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
228 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
229 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
230 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,
231 -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
232 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
233 -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
234 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1,
235 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
236 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
237 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
238 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
239 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
240 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
241 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
242 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
245 /* Decode a byte string which may or may not be the total amount of
246 data being decoded. INBUF and *INBUFLEN carry the leftover bytes
247 from call to call, and *DONE keeps track of whether we've seen an
248 '=' which terminates the encoded data. Have room for four bytes in
249 INBUF and initialize *INBUFLEN to 0 and *DONE to FALSE. Output
250 will be appended to STR. */
252 decode_bytes(svn_stringbuf_t
*str
, const char *data
, apr_size_t len
,
253 unsigned char *inbuf
, int *inbuflen
, svn_boolean_t
*done
)
259 /* Resize the stringbuf to make room for the (approximate) size of
260 output, to avoid repeated resizes later. */
261 svn_stringbuf_ensure(str
, (len
/ 4) * 3 + 3);
263 for (p
= data
; !*done
&& p
< data
+ len
; p
++)
267 /* We are at the end and have to decode a partial group. */
270 memset(inbuf
+ *inbuflen
, 0, 4 - *inbuflen
);
271 decode_group(inbuf
, group
);
272 svn_stringbuf_appendbytes(str
, group
, *inbuflen
- 1);
278 find
= reverse_base64
[(unsigned char)*p
];
280 inbuf
[(*inbuflen
)++] = find
;
283 decode_group(inbuf
, group
);
284 svn_stringbuf_appendbytes(str
, group
, 3);
292 /* Write handler for svn_base64_decode. */
294 decode_data(void *baton
, const char *data
, apr_size_t
*len
)
296 struct decode_baton
*db
= baton
;
298 svn_stringbuf_t
*decoded
;
300 svn_error_t
*err
= SVN_NO_ERROR
;
302 /* Decode this block of data. */
303 subpool
= svn_pool_create(db
->pool
);
304 decoded
= svn_stringbuf_create("", subpool
);
305 decode_bytes(decoded
, data
, *len
, db
->buf
, &db
->buflen
, &db
->done
);
307 /* Write the output, clean up, go home. */
308 declen
= decoded
->len
;
310 err
= svn_stream_write(db
->output
, decoded
->data
, &declen
);
311 svn_pool_destroy(subpool
);
316 /* Close handler for svn_base64_decode(). */
318 finish_decoding_data(void *baton
)
320 struct decode_baton
*db
= baton
;
323 /* Pass on the close request and clean up the baton. */
324 err
= svn_stream_close(db
->output
);
325 svn_pool_destroy(db
->pool
);
331 svn_base64_decode(svn_stream_t
*output
, apr_pool_t
*pool
)
333 apr_pool_t
*subpool
= svn_pool_create(pool
);
334 struct decode_baton
*db
= apr_palloc(subpool
, sizeof(*db
));
335 svn_stream_t
*stream
;
341 stream
= svn_stream_create(db
, pool
);
342 svn_stream_set_write(stream
, decode_data
);
343 svn_stream_set_close(stream
, finish_decoding_data
);
349 svn_base64_decode_string(const svn_string_t
*str
, apr_pool_t
*pool
)
351 svn_stringbuf_t
*decoded
= svn_stringbuf_create("", pool
);
352 svn_string_t
*retval
= apr_pcalloc(pool
, sizeof(*retval
));
353 unsigned char ingroup
[4];
355 svn_boolean_t done
= FALSE
;
357 decode_bytes(decoded
, str
->data
, str
->len
, ingroup
, &ingrouplen
, &done
);
358 retval
->data
= decoded
->data
;
359 retval
->len
= decoded
->len
;
365 svn_base64_from_md5(unsigned char digest
[], apr_pool_t
*pool
)
367 svn_stringbuf_t
*md5str
;
368 unsigned char ingroup
[3];
369 int ingrouplen
= 0, linelen
= 0;
370 md5str
= svn_stringbuf_create("", pool
);
372 /* This cast is safe because we know encode_bytes does a memcpy and
373 * does an implicit unsigned char * cast.
375 encode_bytes(md5str
, (char*)digest
, APR_MD5_DIGESTSIZE
, ingroup
,
376 &ingrouplen
, &linelen
);
377 encode_partial_group(md5str
, ingroup
, ingrouplen
, linelen
);
379 /* Our base64-encoding routines append a final newline if any data
380 was created at all, so let's hack that off. */
384 (md5str
)->data
[(md5str
)->len
] = 0;