1 /**
2  * @file
3  * Conversion to/from base64 encoding
4  *
5  * @authors
6  * Copyright (C) 1993,1995 Carl Harris
7  * Copyright (C) 1997 Eric S. Raymond
8  * Copyright (C) 1999 Brendan Cully <brendan@kublai.com>
9  *
10  * @copyright
11  * This program is free software: you can redistribute it and/or modify it under
12  * the terms of the GNU General Public License as published by the Free Software
13  * Foundation, either version 2 of the License, or (at your option) any later
14  * version.
15  *
16  * This program is distributed in the hope that it will be useful, but WITHOUT
17  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
18  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
19  * details.
20  *
21  * You should have received a copy of the GNU General Public License along with
22  * this program.  If not, see <http://www.gnu.org/licenses/>.
23  */
24 
25 /**
26  * @page mutt_base64 Conversion to/from base64 encoding
27  *
28  * Convert between binary data and base64 text, according to RFC2045.
29  *
30  * @note RFC3548 obsoletes RFC2045.
31  * @note RFC4648 obsoletes RFC3548.
32  */
33 
34 #include "config.h"
35 #include "base64.h"
36 #include "buffer.h"
37 #include "memory.h"
38 #include "string2.h"
39 
40 #define BAD -1
41 
42 /**
43  * B64Chars - Characters of the Base64 encoding
44  */
45 static const char B64Chars[64] = {
46   'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
47   'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
48   'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
49   'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
50   '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/',
51 };
52 
53 /**
54  * Index64 - Lookup table for Base64 encoding characters
55  *
56  * @note This is very similar to the table in imap/utf7.c
57  *
58  * Encoding chars:
59  * * utf7 A-Za-z0-9+,
60  * * mime A-Za-z0-9+/
61  */
62 const int Index64[128] = {
63   // clang-format off
64   -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
65   -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
66   -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, -1,-1,-1,63,
67   52,53,54,55, 56,57,58,59, 60,61,-1,-1, -1,-1,-1,-1,
68   -1, 0, 1, 2,  3, 4, 5, 6,  7, 8, 9,10, 11,12,13,14,
69   15,16,17,18, 19,20,21,22, 23,24,25,-1, -1,-1,-1,-1,
70   -1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
71   41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1
72   // clang-format on
73 };
74 
75 /**
76  * mutt_b64_encode - Convert raw bytes to null-terminated base64 string
77  * @param in     Input buffer for the raw bytes
78  * @param inlen  Length of the input buffer
79  * @param out    Output buffer for the base64 encoded string
80  * @param outlen Length of the output buffer
81  * @retval num Length of the string written to the output buffer
82  *
83  * This function performs base64 encoding. The resulting string is guaranteed
84  * to be null-terminated. The number of characters up to the terminating
85  * NUL-byte is returned (equivalent to calling strlen() on the output buffer
86  * after this function returns).
87  */
mutt_b64_encode(const char * in,size_t inlen,char * out,size_t outlen)88 size_t mutt_b64_encode(const char *in, size_t inlen, char *out, size_t outlen)
89 {
90   if (!in || !out)
91     return 0;
92 
93   unsigned char *begin = (unsigned char *) out;
94   const unsigned char *inu = (const unsigned char *) in;
95 
96   while ((inlen >= 3) && (outlen > 4))
97   {
98     *out++ = B64Chars[inu[0] >> 2];
99     *out++ = B64Chars[((inu[0] << 4) & 0x30) | (inu[1] >> 4)];
100     *out++ = B64Chars[((inu[1] << 2) & 0x3c) | (inu[2] >> 6)];
101     *out++ = B64Chars[inu[2] & 0x3f];
102     outlen -= 4;
103     inlen -= 3;
104     inu += 3;
105   }
106 
107   /* clean up remainder */
108   if ((inlen > 0) && (outlen > 4))
109   {
110     unsigned char fragment;
111 
112     *out++ = B64Chars[inu[0] >> 2];
113     fragment = (inu[0] << 4) & 0x30;
114     if (inlen > 1)
115       fragment |= inu[1] >> 4;
116     *out++ = B64Chars[fragment];
117     *out++ = (inlen < 2) ? '=' : B64Chars[(inu[1] << 2) & 0x3c];
118     *out++ = '=';
119   }
120   *out = '\0';
121   return out - (char *) begin;
122 }
123 
124 /**
125  * mutt_b64_decode - Convert null-terminated base64 string to raw bytes
126  * @param in   Input  buffer for the null-terminated base64-encoded string
127  * @param out  Output buffer for the raw bytes
128  * @param olen Length of the output buffer
129  * @retval num Success, bytes written
130  * @retval -1  Error
131  *
132  * This function performs base64 decoding. The resulting buffer is NOT
133  * null-terminated. If the input buffer contains invalid base64 characters,
134  * this function returns -1.
135  */
mutt_b64_decode(const char * in,char * out,size_t olen)136 int mutt_b64_decode(const char *in, char *out, size_t olen)
137 {
138   if (!in || !out)
139     return -1;
140 
141   int len = 0;
142   unsigned char digit4;
143 
144   do
145   {
146     const unsigned char digit1 = in[0];
147     if ((digit1 > 127) || (base64val(digit1) == BAD))
148       return -1;
149     const unsigned char digit2 = in[1];
150     if ((digit2 > 127) || (base64val(digit2) == BAD))
151       return -1;
152     const unsigned char digit3 = in[2];
153     if ((digit3 > 127) || ((digit3 != '=') && (base64val(digit3) == BAD)))
154       return -1;
155     digit4 = in[3];
156     if ((digit4 > 127) || ((digit4 != '=') && (base64val(digit4) == BAD)))
157       return -1;
158     in += 4;
159 
160     /* digits are already sanity-checked */
161     if (len == olen)
162       return len;
163     *out++ = (base64val(digit1) << 2) | (base64val(digit2) >> 4);
164     len++;
165     if (digit3 != '=')
166     {
167       if (len == olen)
168         return len;
169       *out++ = ((base64val(digit2) << 4) & 0xf0) | (base64val(digit3) >> 2);
170       len++;
171       if (digit4 != '=')
172       {
173         if (len == olen)
174           return len;
175         *out++ = ((base64val(digit3) << 6) & 0xc0) | base64val(digit4);
176         len++;
177       }
178     }
179   } while (*in && digit4 != '=');
180 
181   return len;
182 }
183 
184 /**
185  * mutt_b64_buffer_encode - Convert raw bytes to null-terminated base64 string
186  * @param buf    Buffer for the result
187  * @param in     Input buffer for the raw bytes
188  * @param len    Length of the input buffer
189  * @retval num Length of the string written to the output buffer
190  */
mutt_b64_buffer_encode(struct Buffer * buf,const char * in,size_t len)191 size_t mutt_b64_buffer_encode(struct Buffer *buf, const char *in, size_t len)
192 {
193   if (!buf)
194     return 0;
195 
196   mutt_buffer_alloc(buf, MAX((len * 2), 1024));
197   size_t num = mutt_b64_encode(in, len, buf->data, buf->dsize);
198   mutt_buffer_fix_dptr(buf);
199   return num;
200 }
201 
202 /**
203  * mutt_b64_buffer_decode - Convert null-terminated base64 string to raw bytes
204  * @param buf  Buffer for the result
205  * @param in   Input  buffer for the null-terminated base64-encoded string
206  * @retval num Success, bytes written
207  * @retval -1  Error
208  */
mutt_b64_buffer_decode(struct Buffer * buf,const char * in)209 int mutt_b64_buffer_decode(struct Buffer *buf, const char *in)
210 {
211   if (!buf)
212     return -1;
213 
214   mutt_buffer_alloc(buf, mutt_str_len(in));
215   int olen = mutt_b64_decode(in, buf->data, buf->dsize);
216   /* mutt_from_base64 returns raw bytes, so don't terminate the buffer either */
217   if (olen > 0)
218     mutt_buffer_seek(buf, olen);
219   else
220     mutt_buffer_seek(buf, 0);
221 
222   return olen;
223 }
224