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 ];