Move base64 implementation to own file.
[paraslash.git] / base64.c
1 /*
2 * The code in this file was taken from openssh-5.2p1, Copyright (c) 1996 by
3 * Internet Software Consortium. Portions Copyright (c) 1995 by International
4 * Business Machines, Inc.
5 */
6
7 /** \file base64.c Uudecode and base64decode implementation. */
8
9 #include <regex.h>
10
11 #include "para.h"
12 #include "error.h"
13 #include "base64.h"
14 #include "string.h"
15
16 static const char Base64[] =
17 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
18 static const char Pad64 = '=';
19
20 /**
21 * base64-decode a buffer.
22 *
23 * \param src The buffer to decode.
24 * \param target Result is stored here.
25 * \param targsize Number of bytes of \a target.
26 *
27 * Skips all whitespace anywhere. Converts characters, four at a time, starting
28 * at (or after) src from base - 64 numbers into three 8 bit bytes in the
29 * target area.
30 *
31 * \return The number of data bytes stored at the target, -E_BASE64 on errors.
32 */
33 int base64_decode(char const *src, unsigned char *target, size_t targsize)
34 {
35 unsigned int tarindex, state;
36 int ch;
37 char *pos;
38
39 state = 0;
40 tarindex = 0;
41
42 while ((ch = *src++) != '\0') {
43 if (para_isspace(ch)) /* Skip whitespace anywhere. */
44 continue;
45
46 if (ch == Pad64)
47 break;
48
49 pos = strchr(Base64, ch);
50 if (pos == NULL) /* A non-base64 character. */
51 return -E_BASE64;
52
53 switch (state) {
54 case 0:
55 if (tarindex >= targsize)
56 return -E_BASE64;
57 target[tarindex] = (pos - Base64) << 2;
58 state = 1;
59 break;
60 case 1:
61 if (tarindex + 1 >= targsize)
62 return -E_BASE64;
63 target[tarindex] |= (pos - Base64) >> 4;
64 target[tarindex + 1] = ((pos - Base64) & 0x0f) << 4;
65 tarindex++;
66 state = 2;
67 break;
68 case 2:
69 if (tarindex + 1 >= targsize)
70 return -E_BASE64;
71 target[tarindex] |= (pos - Base64) >> 2;
72 target[tarindex + 1] = ((pos - Base64) & 0x03) << 6;
73 tarindex++;
74 state = 3;
75 break;
76 case 3:
77 if (tarindex >= targsize)
78 return -E_BASE64;
79 target[tarindex] |= pos - Base64;
80 tarindex++;
81 state = 0;
82 break;
83 }
84 }
85
86 /*
87 * We are done decoding Base-64 chars. Let's see if we ended
88 * on a byte boundary, and/or with erroneous trailing characters.
89 */
90
91 if (ch == Pad64) { /* We got a pad char. */
92 ch = *src++; /* Skip it, get next. */
93 switch (state) {
94 case 0: /* Invalid = in first position */
95 case 1: /* Invalid = in second position */
96 return -E_BASE64;
97
98 case 2: /* Valid, means one byte of info */
99 /* Skip any number of spaces. */
100 for (; ch != '\0'; ch = *src++)
101 if (!isspace(ch))
102 break;
103 /* Make sure there is another trailing = sign. */
104 if (ch != Pad64)
105 return -E_BASE64;
106 ch = *src++; /* Skip the = */
107 /* Fall through to "single trailing =" case. */
108 /* FALLTHROUGH */
109
110 case 3: /* Valid, means two bytes of info */
111 /*
112 * We know this char is an =. Is there anything but
113 * whitespace after it?
114 */
115 for (; ch != '\0'; ch = *src++)
116 if (!isspace(ch))
117 return -E_BASE64;
118
119 /*
120 * Now make sure for cases 2 and 3 that the "extra"
121 * bits that slopped past the last full byte were
122 * zeros. If we don't check them, they become a
123 * subliminal channel.
124 */
125 if (target[tarindex] != 0)
126 return -E_BASE64;
127 }
128 } else {
129 /*
130 * We ended by seeing the end of the string. Make sure we
131 * have no partial bytes lying around.
132 */
133 if (state != 0)
134 return -E_BASE64;
135 }
136 return tarindex;
137 }
138
139 /**
140 * uudecode a buffer.
141 *
142 * \param src The buffer to decode.
143 * \param target Result buffer.
144 * \param targsize The length of \a target in bytes.
145 *
146 * This is just a simple wrapper for base64_decode() which strips whitespace.
147 *
148 * \return The return value of the underlying call to base64_decode().
149 */
150 int uudecode(const char *src, unsigned char *target, size_t targsize)
151 {
152 int len;
153 char *encoded, *p;
154
155 /* copy the 'readonly' source */
156 encoded = para_strdup(src);
157 /* skip whitespace and data */
158 for (p = encoded; *p == ' ' || *p == '\t'; p++)
159 ;
160 for (; *p != '\0' && *p != ' ' && *p != '\t'; p++)
161 ;
162 /* and remove trailing whitespace because base64_decode needs this */
163 *p = '\0';
164 len = base64_decode(encoded, target, targsize);
165 free(encoded);
166 return len;
167 }