1 /*******************************************************************************
2 
3         copyright:      Copyright (c) 2010 Ulrik Mikaelsson. All rights reserved
4 
5         license:        BSD style: $(LICENSE)
6 
7         author:         Ulrik Mikaelsson
8 
9         standards:      rfc3548, rfc4648
10 
11 *******************************************************************************/
12 
13 /*******************************************************************************
14 
15     This module is used to decode and encode base32 char[] arrays.
16 
17     Example:
18     ---
19     char[] blah = "Hello there, my name is Jeff.";
20 
21     scope encodebuf = new char[allocateEncodeSize(cast(ubyte[])blah)];
22     char[] encoded = encode(cast(ubyte[])blah, encodebuf);
23 
24     scope decodebuf = new ubyte[encoded.length];
25     if (cast(char[])decode(encoded, decodebuf) == "Hello there, my name is Jeff.")
26         Stdout("yay").newline;
27     ---
28 
29     Since v1.0
30 
31 *******************************************************************************/
32 
33 module tango.util.encode.Base32;
34 
35 /*******************************************************************************
36 
37     calculates and returns the size needed to encode the length of the
38     array passed.
39 
40     Params:
41     data = An array that will be encoded
42 
43 *******************************************************************************/
44 
45 
46 size_t allocateEncodeSize(const(ubyte[]) data)
47 {
48     return allocateEncodeSize(data.length);
49 }
50 
51 /*******************************************************************************
52 
53     calculates and returns the size needed to encode the length passed.
54 
55     Params:
56     length = Number of bytes to be encoded
57 
58 *******************************************************************************/
59 
60 size_t allocateEncodeSize(size_t length)
61 {
62     auto inputbits = length * 8;
63     auto inputquantas = (inputbits + 39) / 40; // Round upwards
64     return inputquantas * 8;
65 }
66 
67 
68 /*******************************************************************************
69 
70     encodes data and returns as an ASCII base32 string.
71 
72     Params:
73     data = what is to be encoded
74     buff = buffer large enough to hold encoded data
75     pad  = Whether to pad ascii output with '='-chars
76 
77     Example:
78     ---
79     char[512] encodebuf;
80     char[] myEncodedString = encode(cast(ubyte[])"Hello, how are you today?", encodebuf);
81     Stdout(myEncodedString).newline; // JBSWY3DPFQQGQ33XEBQXEZJAPFXXKIDUN5SGC6J7
82     ---
83 
84 
85 *******************************************************************************/
86 
87 char[] encode(const(ubyte[]) data, char[] buff, bool pad=true)
88 in
89 {
90     assert(data);
91     assert(buff.length >= allocateEncodeSize(data));
92 }
93 body
94 {
95     uint i = 0;
96     ushort remainder; // Carries overflow bits to next char
97     byte remainlen;  // Tracks bits in remainder
98     foreach (ubyte j; data)
99     {
100         remainder = cast(ushort)((remainder<<8) | j);
101         remainlen += 8;
102         do {
103             remainlen -= 5;
104             buff[i++] = _encodeTable[(remainder>>remainlen)&0b11111];
105         } while (remainlen > 5);
106     }
107     if (remainlen)
108         buff[i++] = _encodeTable[(remainder<<(5-remainlen))&0b11111];
109     if (pad) {
110         for (ubyte padCount= cast(ubyte) (-i%8);padCount > 0; padCount--)
111             buff[i++] = base32_PAD;
112     }
113 
114     return buff[0..i];
115 }
116 
117 /*******************************************************************************
118 
119     encodes data and returns as an ASCII base32 string.
120 
121     Params:
122     data = what is to be encoded
123     pad = whether to pad output with '='-chars
124 
125     Example:
126     ---
127     char[] myEncodedString = encode(cast(ubyte[])"Hello, how are you today?");
128     Stdout(myEncodedString).newline; // JBSWY3DPFQQGQ33XEBQXEZJAPFXXKIDUN5SGC6J7
129     ---
130 
131 
132 *******************************************************************************/
133 
134 
135 char[] encode(const(ubyte[]) data, bool pad=true)
136 in
137 {
138     assert(data);
139 }
140 body
141 {
142     auto rtn = new char[allocateEncodeSize(data)];
143     return encode(data, rtn, pad);
144 }
145 
146 /*******************************************************************************
147 
148     decodes an ASCII base32 string and returns it as ubyte[] data. Pre-allocates
149     the size of the array.
150 
151     This decoder will ignore non-base32 characters. So:
152     SGVsbG8sIGhvd
153     yBhcmUgeW91IH
154     RvZGF5Pw==
155 
156     Is valid.
157 
158     Params:
159     data = what is to be decoded
160 
161     Example:
162     ---
163     char[] myDecodedString = cast(char[])decode("JBSWY3DPFQQGQ33XEBQXEZJAPFXXKIDUN5SGC6J7");
164     Stdout(myDecodeString).newline; // Hello, how are you today?
165     ---
166 
167 *******************************************************************************/
168 
169 ubyte[] decode(const(char[]) data)
170 in
171 {
172     assert(data);
173 }
174 body
175 {
176     auto rtn = new ubyte[data.length];
177     return decode(data, rtn);
178 }
179 
180 /*******************************************************************************
181 
182     decodes an ASCII base32 string and returns it as ubyte[] data.
183 
184     This decoder will ignore non-base32 characters. So:
185     SGVsbG8sIGhvd
186     yBhcmUgeW91IH
187     RvZGF5Pw==
188 
189     Is valid.
190 
191     Params:
192     data = what is to be decoded
193     buff = a big enough array to hold the decoded data
194 
195     Example:
196     ---
197     ubyte[512] decodebuf;
198     char[] myDecodedString = cast(char[])decode("JBSWY3DPFQQGQ33XEBQXEZJAPFXXKIDUN5SGC6J7", decodebuf);
199     Stdout(myDecodeString).newline; // Hello, how are you today?
200     ---
201 
202 *******************************************************************************/
203 ubyte[] decode(const(char[]) data, ubyte[] buff)
204 in
205 {
206     assert(data);
207 }
208 body
209 {
210     ushort remainder;
211     byte remainlen;
212     size_t oIndex;
213     foreach (c; data)
214     {
215         auto dec = _decodeTable[c];
216         if (dec & 0b1000_0000)
217             continue;
218         remainder = cast(ushort)((remainder<<5) | dec);
219         for (remainlen += 5; remainlen >= 8; remainlen -= 8)
220             buff[oIndex++] = cast(ubyte) (remainder >> (remainlen-8));
221     }
222 
223     return buff[0..oIndex];
224 }
225 
226 debug (UnitTest)
227 {
228     unittest
229     {
230         immutable immutable(char)[][] testBytes = [
231             "",
232             "foo",
233             "foob",
234             "fooba",
235             "foobar",
236             "Hello, how are you today?",
237         ];
238         immutable immutable(char)[][] testChars = [
239             "",
240             "MZXW6===",
241             "MZXW6YQ=",
242             "MZXW6YTB",
243             "MZXW6YTBOI======",
244             "JBSWY3DPFQQGQ33XEBQXEZJAPFXXKIDUN5SGC6J7",
245         ];
246 
247         for (uint i; i < testBytes.length; i++) {
248             auto resultChars = encode(cast(ubyte[])testBytes[i]);
249             assert(resultChars == testChars[i],
250                     testBytes[i]~": ("~resultChars~") != ("~testChars[i]~")");
251 
252             auto resultBytes = decode(testChars[i]);
253             assert(resultBytes == cast(ubyte[])testBytes[i],
254                     testChars[i]~": ("~cast(char[])resultBytes~") != ("~testBytes[i]~")");
255         }
256     }
257 }
258 
259 
260 
261 private:
262 
263 /*
264     Static immutable tables used for fast lookups to
265     encode and decode data.
266 */
267 immutable ubyte base32_PAD = '=';
268 immutable char[] _encodeTable = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
269 
270 immutable ubyte[] _decodeTable = [
271     0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,
272     0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,
273     0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,
274     0xFF,0xFF,0x1A,0x1B, 0x1C,0x1D,0x1E,0x1F, 0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,
275     0xFF,0x00,0x01,0x02, 0x03,0x04,0x05,0x06, 0x07,0x08,0x09,0x0A, 0x0B,0x0C,0x0D,0x0E,
276     0x0F,0x10,0x11,0x12, 0x13,0x14,0x15,0x16, 0x17,0x18,0x19,0xFF, 0xFF,0xFF,0xFF,0xFF,
277     0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,
278     0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,
279     0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,
280     0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,
281     0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,
282     0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,
283     0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,
284     0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,
285     0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,
286     0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,
287 ];