e9892244d62a0e00a320003bb3fe740e5e89f866
[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 }