Added Base64 API to own files.
[silc.git] / lib / silcutil / silcbase64.c
1 /*
2
3   silcbase64.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 2007 Pekka Riikonen
8
9   This program is free software; you can redistribute it and/or modify
10   it under the terms of the GNU General Public License as published by
11   the Free Software Foundation; version 2 of the License.
12
13   This program is distributed in the hope that it will be useful,
14   but WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16   GNU General Public License for more details.
17
18 */
19
20 #include "silc.h"
21
22 static unsigned char pem_enc[64] =
23 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
24
25 /* Encodes data into Base 64 encoding. Returns NULL terminated base 64 encoded
26    data string. */
27
28 char *silc_base64_encode(SilcStack stack, unsigned char *data, SilcUInt32 len)
29 {
30   int i, j;
31   SilcUInt32 bits, c, char_count;
32   char *pem;
33
34   char_count = 0;
35   bits = 0;
36   j = 0;
37
38   pem = silc_scalloc(stack, ((len * 8 + 5) / 6) + 5, sizeof(*pem));
39
40   for (i = 0; i < len; i++) {
41     c = data[i];
42     bits += c;
43     char_count++;
44
45     if (char_count == 3) {
46       pem[j++] = pem_enc[bits  >> 18];
47       pem[j++] = pem_enc[(bits >> 12) & 0x3f];
48       pem[j++] = pem_enc[(bits >> 6)  & 0x3f];
49       pem[j++] = pem_enc[bits & 0x3f];
50       bits = 0;
51       char_count = 0;
52     } else {
53       bits <<= 8;
54     }
55   }
56
57   if (char_count != 0) {
58     bits <<= 16 - (8 * char_count);
59     pem[j++] = pem_enc[bits >> 18];
60     pem[j++] = pem_enc[(bits >> 12) & 0x3f];
61
62     if (char_count == 1) {
63       pem[j++] = '=';
64       pem[j] = '=';
65     } else {
66       pem[j++] = pem_enc[(bits >> 6) & 0x3f];
67       pem[j] = '=';
68     }
69   }
70
71   return pem;
72 }
73
74 /* Same as above but puts newline ('\n') every 72 characters. */
75
76 char *silc_base64_encode_file(SilcStack stack,
77                               unsigned char *data, SilcUInt32 data_len)
78 {
79   int i, j;
80   SilcUInt32 len, cols;
81   char *pem, *pem2;
82
83   pem = silc_base64_encode(stack, data, data_len);
84   len = strlen(pem);
85
86   pem2 = silc_scalloc(stack, len + (len / 72) + 1, sizeof(*pem2));
87
88   for (i = 0, j = 0, cols = 1; i < len; i++, cols++) {
89     if (cols == 72) {
90       pem2[i] = '\n';
91       cols = 0;
92       len++;
93       continue;
94     }
95
96     pem2[i] = pem[j++];
97   }
98
99   silc_sfree(stack, pem);
100   return pem2;
101 }
102
103 /* Decodes Base 64 into data. Returns the decoded data. */
104
105 unsigned char *silc_base64_decode(SilcStack stack,
106                                   unsigned char *base64,
107                                   SilcUInt32 base64_len,
108                                   SilcUInt32 *ret_len)
109 {
110   int i, j;
111   SilcUInt32 len, c, char_count, bits;
112   unsigned char *data;
113   static char ialpha[256], decoder[256];
114
115   for (i = 64 - 1; i >= 0; i--) {
116     ialpha[pem_enc[i]] = 1;
117     decoder[pem_enc[i]] = i;
118   }
119
120   char_count = 0;
121   bits = 0;
122   j = 0;
123
124   if (!base64_len)
125     len = strlen(base64);
126   else
127     len = base64_len;
128
129   data = silc_scalloc(stack, ((len * 6) / 8), sizeof(*data));
130
131   for (i = 0; i < len; i++) {
132     c = base64[i];
133
134     if (c == '=')
135       break;
136
137     if (c > 127 || !ialpha[c])
138       continue;
139
140     bits += decoder[c];
141     char_count++;
142
143     if (char_count == 4) {
144       data[j++] = bits >> 16;
145       data[j++] = (bits >> 8) & 0xff;
146       data[j++] = bits & 0xff;
147       bits = 0;
148       char_count = 0;
149     } else {
150       bits <<= 6;
151     }
152   }
153
154   switch(char_count) {
155   case 1:
156     silc_sfree(stack, data);
157     return NULL;
158     break;
159   case 2:
160     data[j++] = bits >> 10;
161     break;
162   case 3:
163     data[j++] = bits >> 16;
164     data[j++] = (bits >> 8) & 0xff;
165     break;
166   }
167
168   if (ret_len)
169     *ret_len = j;
170
171   return data;
172 }