1 /*
2  *  Off-the-Record Messaging library
3  *  Copyright (C) 2004-2012  Ian Goldberg, Chris Alexander, Willy Lew,
4  *  			     Nikita Borisov
5  *                           <otr@cypherpunks.ca>
6  *
7  *  This library is free software; you can redistribute it and/or
8  *  modify it under the terms of version 2.1 of the GNU Lesser General
9  *  Public License as published by the Free Software Foundation.
10  *
11  *  This library is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  *  Lesser General Public License for more details.
15  *
16  *  You should have received a copy of the GNU Lesser General Public
17  *  License along with this library; if not, write to the Free Software
18  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19  */
20 
21 /* Modified from: */
22 
23 /*********************************************************************\
24 
25 MODULE NAME:    b64.c
26 
27 AUTHOR:         Bob Trower 08/04/01
28 
29 LICENCE:        Copyright (c) 2001 Bob Trower, Trantor Standard Systems Inc.
30 
31 		Permission is hereby granted, free of charge, to any person
32 		obtaining a copy of this software and associated
33 		documentation files (the "Software"), to deal in the
34 		Software without restriction, including without limitation
35 		the rights to use, copy, modify, merge, publish, distribute,
36 		sublicense, and/or sell copies of the Software, and to
37 		permit persons to whom the Software is furnished to do so,
38 		subject to the following conditions:
39 
40 		The above copyright notice and this permission notice shall
41 		be included in all copies or substantial portions of the
42 		Software.
43 
44 		THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
45 		KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
46 		WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
47 		PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
48 		OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
49 		OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
50 		OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
51 		SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
52 
53 VERSION HISTORY:
54 		Bob Trower 08/04/01 -- Create Version 0.00.00B
55 
56 \******************************************************************* */
57 
58 /* system headers */
59 #include <stdio.h>
60 #include <string.h>
61 
62 /* libotr headers */
63 #include "b64.h"
64 
65 /*
66 ** Translation Table as described in RFC1113
67 */
68 static const char cb64[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
69 
70 /*
71 ** Translation Table to decode (created by author)
72 */
73 static const char cd64[]="|$$$}rstuvwxyz{$$$$$$$>?@ABCDEFGHIJKLMNOPQRSTUVW$$$$$$XYZ[\\]^_`abcdefghijklmnopq";
74 
75 /*
76 ** encodeblock
77 **
78 ** encode up to 3 8-bit binary bytes as 4 '6-bit' characters.
79 ** len must be 1, 2, or 3.
80 */
encodeblock(char * out,const unsigned char * in,size_t len)81 static void encodeblock( char *out, const unsigned char *in, size_t len )
82 {
83     unsigned char in0, in1, in2;
84     in0 = in[0];
85     in1 = len > 1 ? in[1] : 0;
86     in2 = len > 2 ? in[2] : 0;
87 
88     out[0] = cb64[ in0 >> 2 ];
89     out[1] = cb64[ ((in0 & 0x03) << 4) | ((in1 & 0xf0) >> 4) ];
90     out[2] = len > 1 ? cb64[ ((in1 & 0x0f) << 2) | ((in2 & 0xc0) >> 6) ]
91 		     : '=';
92     out[3] = len > 2 ? cb64[ in2 & 0x3f ]
93 		     : '=';
94 }
95 
96 /*
97  * base64 encode data.  Insert no linebreaks or whitespace.
98  *
99  * The buffer base64data must contain at least ((datalen+2)/3)*4 bytes of
100  * space.  This function will return the number of bytes actually used.
101  */
otrl_base64_encode(char * base64data,const unsigned char * data,size_t datalen)102 size_t otrl_base64_encode(char *base64data, const unsigned char *data,
103 	size_t datalen)
104 {
105     size_t base64len = 0;
106 
107     while(datalen > 2) {
108 	encodeblock(base64data, data, 3);
109 	base64data += 4;
110 	base64len += 4;
111 	data += 3;
112 	datalen -= 3;
113     }
114     if (datalen > 0) {
115 	encodeblock(base64data, data, datalen);
116 	base64len += 4;
117     }
118 
119     return base64len;
120 }
121 
decode(unsigned char * out,const char * in,size_t b64len)122 static size_t decode(unsigned char *out, const char *in, size_t b64len)
123 {
124     size_t written = 0;
125     unsigned char c = 0;
126 
127     if (b64len > 0) {
128 	c = in[0] << 2;
129     }
130     if (b64len > 1) {
131 	out[0] = c | in[1] >> 4;
132 	written = 1;
133 	c = in[1] << 4;
134     }
135     if (b64len > 2) {
136 	out[1] = c | in[2] >> 2;
137 	written = 2;
138 	c = in[2] << 6;
139     }
140     if (b64len > 3) {
141 	out[2] = c | in[3];
142 	written = 3;
143     }
144     return written;
145 }
146 
147 /*
148  * base64 decode data.  Skip non-base64 chars, and terminate at the
149  * first '=', or the end of the buffer.
150  *
151  * The buffer data must contain at least ((base64len+3) / 4) * 3 bytes
152  * of space.  This function will return the number of bytes actually
153  * used.
154  */
otrl_base64_decode(unsigned char * data,const char * base64data,size_t base64len)155 size_t otrl_base64_decode(unsigned char *data, const char *base64data,
156 	size_t base64len)
157 {
158     size_t datalen = 0;
159     char b64[4];
160     size_t b64accum = 0;
161 
162     while(base64len > 0) {
163 	char b = *base64data;
164 	unsigned char bdecode;
165 	++base64data;
166 	--base64len;
167 	if (b < '+' || b > 'z') continue;  /* Skip non-base64 chars */
168 	if (b == '=') {
169 	    /* Force termination */
170 	    datalen += decode(data, b64, b64accum);
171 	    base64len = 0;
172 	} else {
173 	    bdecode = cd64[b-'+'];
174 	    if (bdecode == '$') continue;  /* Skip non-base64 chars */
175 	    b64[b64accum++] = bdecode-'>';
176 	    if (b64accum == 4) {
177 		/* We have a complete block; decode it. */
178 		size_t written = decode(data, b64, b64accum);
179 		data += written;
180 		datalen += written;
181 		b64accum = 0;
182 	    }
183 	}
184     }
185 
186     /* Just discard any short block at the end. */
187 
188     return datalen;
189 }
190 
191 /*
192  * Base64-encode a block of data, stick "?OTR:" and "." around it, and
193  * return the result, or NULL in the event of a memory error.  The
194  * caller must free() the return value.
195  */
otrl_base64_otr_encode(const unsigned char * buf,size_t buflen)196 char *otrl_base64_otr_encode(const unsigned char *buf, size_t buflen)
197 {
198     char *base64buf;
199     size_t base64len;
200     const size_t HALF_MAX_SIZE_T = ((size_t)-1) >> 1;
201 
202     if (buflen > HALF_MAX_SIZE_T) {
203 	/* You somehow have a buffer that's of size more than half of
204 	 * all addressable memory, and you now want a base64 version in
205 	 * a new buffer 33% larger?  Not going to happen.  Exit now,
206 	 * rather in the malloc below, to avoid integer overflowing the
207 	 * computation of base64len. */
208 	 return NULL;
209     }
210 
211     /* Make the base64-encoding. */
212     base64len = ((buflen + 2) / 3) * 4;
213     base64buf = malloc(5 + base64len + 1 + 1);
214     if (base64buf == NULL) {
215 	return NULL;
216     }
217     memmove(base64buf, "?OTR:", 5);
218     otrl_base64_encode(base64buf+5, buf, buflen);
219     base64buf[5 + base64len] = '.';
220     base64buf[5 + base64len + 1] = '\0';
221 
222     return base64buf;
223 }
224 
225 /*
226  * Base64-decode the portion of the given message between "?OTR:" and
227  * ".".  Set *bufp to the decoded data, and set *lenp to its length.
228  * The caller must free() the result.  Return 0 on success, -1 on a
229  * memory error, or -2 on invalid input.
230  */
otrl_base64_otr_decode(const char * msg,unsigned char ** bufp,size_t * lenp)231 int otrl_base64_otr_decode(const char *msg, unsigned char **bufp,
232 	size_t *lenp)
233 {
234     char *otrtag, *endtag;
235     size_t msglen, rawlen;
236     unsigned char *rawmsg;
237 
238     otrtag = strstr(msg, "?OTR:");
239     if (!otrtag) {
240 	return -2;
241     }
242 
243     endtag = strchr(otrtag, '.');
244     if (endtag) {
245 	msglen = endtag-otrtag;
246     } else {
247 	return -2;
248     }
249 
250     /* Skip over the "?OTR:" */
251     otrtag += 5;
252     msglen -= 5;
253 
254     /* Base64-decode the message */
255     rawlen = OTRL_B64_MAX_DECODED_SIZE(msglen);   /* maximum possible */
256     rawmsg = malloc(rawlen);
257     if (!rawmsg && rawlen > 0) {
258 	return -1;
259     }
260 
261     rawlen = otrl_base64_decode(rawmsg, otrtag, msglen);  /* actual size */
262 
263     *bufp = rawmsg;
264     *lenp = rawlen;
265 
266     return 0;
267 }
268