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