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