Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
michael@0 | 1 | /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
michael@0 | 2 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 5 | |
michael@0 | 6 | #include "plbase64.h" |
michael@0 | 7 | #include "prlog.h" /* For PR_NOT_REACHED */ |
michael@0 | 8 | #include "prmem.h" /* for malloc / PR_MALLOC */ |
michael@0 | 9 | |
michael@0 | 10 | #include <string.h> /* for strlen */ |
michael@0 | 11 | |
michael@0 | 12 | static unsigned char *base = (unsigned char *)"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; |
michael@0 | 13 | |
michael@0 | 14 | static void |
michael@0 | 15 | encode3to4 |
michael@0 | 16 | ( |
michael@0 | 17 | const unsigned char *src, |
michael@0 | 18 | unsigned char *dest |
michael@0 | 19 | ) |
michael@0 | 20 | { |
michael@0 | 21 | PRUint32 b32 = (PRUint32)0; |
michael@0 | 22 | PRIntn i, j = 18; |
michael@0 | 23 | |
michael@0 | 24 | for( i = 0; i < 3; i++ ) |
michael@0 | 25 | { |
michael@0 | 26 | b32 <<= 8; |
michael@0 | 27 | b32 |= (PRUint32)src[i]; |
michael@0 | 28 | } |
michael@0 | 29 | |
michael@0 | 30 | for( i = 0; i < 4; i++ ) |
michael@0 | 31 | { |
michael@0 | 32 | dest[i] = base[ (PRUint32)((b32>>j) & 0x3F) ]; |
michael@0 | 33 | j -= 6; |
michael@0 | 34 | } |
michael@0 | 35 | |
michael@0 | 36 | return; |
michael@0 | 37 | } |
michael@0 | 38 | |
michael@0 | 39 | static void |
michael@0 | 40 | encode2to4 |
michael@0 | 41 | ( |
michael@0 | 42 | const unsigned char *src, |
michael@0 | 43 | unsigned char *dest |
michael@0 | 44 | ) |
michael@0 | 45 | { |
michael@0 | 46 | dest[0] = base[ (PRUint32)((src[0]>>2) & 0x3F) ]; |
michael@0 | 47 | dest[1] = base[ (PRUint32)(((src[0] & 0x03) << 4) | ((src[1] >> 4) & 0x0F)) ]; |
michael@0 | 48 | dest[2] = base[ (PRUint32)((src[1] & 0x0F) << 2) ]; |
michael@0 | 49 | dest[3] = (unsigned char)'='; |
michael@0 | 50 | return; |
michael@0 | 51 | } |
michael@0 | 52 | |
michael@0 | 53 | static void |
michael@0 | 54 | encode1to4 |
michael@0 | 55 | ( |
michael@0 | 56 | const unsigned char *src, |
michael@0 | 57 | unsigned char *dest |
michael@0 | 58 | ) |
michael@0 | 59 | { |
michael@0 | 60 | dest[0] = base[ (PRUint32)((src[0]>>2) & 0x3F) ]; |
michael@0 | 61 | dest[1] = base[ (PRUint32)((src[0] & 0x03) << 4) ]; |
michael@0 | 62 | dest[2] = (unsigned char)'='; |
michael@0 | 63 | dest[3] = (unsigned char)'='; |
michael@0 | 64 | return; |
michael@0 | 65 | } |
michael@0 | 66 | |
michael@0 | 67 | static void |
michael@0 | 68 | encode |
michael@0 | 69 | ( |
michael@0 | 70 | const unsigned char *src, |
michael@0 | 71 | PRUint32 srclen, |
michael@0 | 72 | unsigned char *dest |
michael@0 | 73 | ) |
michael@0 | 74 | { |
michael@0 | 75 | while( srclen >= 3 ) |
michael@0 | 76 | { |
michael@0 | 77 | encode3to4(src, dest); |
michael@0 | 78 | src += 3; |
michael@0 | 79 | dest += 4; |
michael@0 | 80 | srclen -= 3; |
michael@0 | 81 | } |
michael@0 | 82 | |
michael@0 | 83 | switch( srclen ) |
michael@0 | 84 | { |
michael@0 | 85 | case 2: |
michael@0 | 86 | encode2to4(src, dest); |
michael@0 | 87 | break; |
michael@0 | 88 | case 1: |
michael@0 | 89 | encode1to4(src, dest); |
michael@0 | 90 | break; |
michael@0 | 91 | case 0: |
michael@0 | 92 | break; |
michael@0 | 93 | default: |
michael@0 | 94 | PR_NOT_REACHED("coding error"); |
michael@0 | 95 | } |
michael@0 | 96 | |
michael@0 | 97 | return; |
michael@0 | 98 | } |
michael@0 | 99 | |
michael@0 | 100 | /* |
michael@0 | 101 | * PL_Base64Encode |
michael@0 | 102 | * |
michael@0 | 103 | * If the destination argument is NULL, a return buffer is |
michael@0 | 104 | * allocated, and the data therein will be null-terminated. |
michael@0 | 105 | * If the destination argument is not NULL, it is assumed to |
michael@0 | 106 | * be of sufficient size, and the contents will not be null- |
michael@0 | 107 | * terminated by this routine. |
michael@0 | 108 | * |
michael@0 | 109 | * Returns null if the allocation fails. |
michael@0 | 110 | */ |
michael@0 | 111 | |
michael@0 | 112 | PR_IMPLEMENT(char *) |
michael@0 | 113 | PL_Base64Encode |
michael@0 | 114 | ( |
michael@0 | 115 | const char *src, |
michael@0 | 116 | PRUint32 srclen, |
michael@0 | 117 | char *dest |
michael@0 | 118 | ) |
michael@0 | 119 | { |
michael@0 | 120 | if( 0 == srclen ) |
michael@0 | 121 | { |
michael@0 | 122 | size_t len = strlen(src); |
michael@0 | 123 | srclen = len; |
michael@0 | 124 | /* Detect truncation. */ |
michael@0 | 125 | if( srclen != len ) |
michael@0 | 126 | { |
michael@0 | 127 | return (char *)0; |
michael@0 | 128 | } |
michael@0 | 129 | } |
michael@0 | 130 | |
michael@0 | 131 | if( (char *)0 == dest ) |
michael@0 | 132 | { |
michael@0 | 133 | PRUint32 destlen; |
michael@0 | 134 | /* Ensure all PRUint32 values stay within range. */ |
michael@0 | 135 | if( srclen > (PR_UINT32_MAX/4) * 3 ) |
michael@0 | 136 | { |
michael@0 | 137 | return (char *)0; |
michael@0 | 138 | } |
michael@0 | 139 | destlen = ((srclen + 2)/3) * 4; |
michael@0 | 140 | dest = (char *)PR_MALLOC(destlen + 1); |
michael@0 | 141 | if( (char *)0 == dest ) |
michael@0 | 142 | { |
michael@0 | 143 | return (char *)0; |
michael@0 | 144 | } |
michael@0 | 145 | dest[ destlen ] = (char)0; /* null terminate */ |
michael@0 | 146 | } |
michael@0 | 147 | |
michael@0 | 148 | encode((const unsigned char *)src, srclen, (unsigned char *)dest); |
michael@0 | 149 | return dest; |
michael@0 | 150 | } |
michael@0 | 151 | |
michael@0 | 152 | static PRInt32 |
michael@0 | 153 | codetovalue |
michael@0 | 154 | ( |
michael@0 | 155 | unsigned char c |
michael@0 | 156 | ) |
michael@0 | 157 | { |
michael@0 | 158 | if( (c >= (unsigned char)'A') && (c <= (unsigned char)'Z') ) |
michael@0 | 159 | { |
michael@0 | 160 | return (PRInt32)(c - (unsigned char)'A'); |
michael@0 | 161 | } |
michael@0 | 162 | else if( (c >= (unsigned char)'a') && (c <= (unsigned char)'z') ) |
michael@0 | 163 | { |
michael@0 | 164 | return ((PRInt32)(c - (unsigned char)'a') +26); |
michael@0 | 165 | } |
michael@0 | 166 | else if( (c >= (unsigned char)'0') && (c <= (unsigned char)'9') ) |
michael@0 | 167 | { |
michael@0 | 168 | return ((PRInt32)(c - (unsigned char)'0') +52); |
michael@0 | 169 | } |
michael@0 | 170 | else if( (unsigned char)'+' == c ) |
michael@0 | 171 | { |
michael@0 | 172 | return (PRInt32)62; |
michael@0 | 173 | } |
michael@0 | 174 | else if( (unsigned char)'/' == c ) |
michael@0 | 175 | { |
michael@0 | 176 | return (PRInt32)63; |
michael@0 | 177 | } |
michael@0 | 178 | else |
michael@0 | 179 | { |
michael@0 | 180 | return -1; |
michael@0 | 181 | } |
michael@0 | 182 | } |
michael@0 | 183 | |
michael@0 | 184 | static PRStatus |
michael@0 | 185 | decode4to3 |
michael@0 | 186 | ( |
michael@0 | 187 | const unsigned char *src, |
michael@0 | 188 | unsigned char *dest |
michael@0 | 189 | ) |
michael@0 | 190 | { |
michael@0 | 191 | PRUint32 b32 = (PRUint32)0; |
michael@0 | 192 | PRInt32 bits; |
michael@0 | 193 | PRIntn i; |
michael@0 | 194 | |
michael@0 | 195 | for( i = 0; i < 4; i++ ) |
michael@0 | 196 | { |
michael@0 | 197 | bits = codetovalue(src[i]); |
michael@0 | 198 | if( bits < 0 ) |
michael@0 | 199 | { |
michael@0 | 200 | return PR_FAILURE; |
michael@0 | 201 | } |
michael@0 | 202 | |
michael@0 | 203 | b32 <<= 6; |
michael@0 | 204 | b32 |= bits; |
michael@0 | 205 | } |
michael@0 | 206 | |
michael@0 | 207 | dest[0] = (unsigned char)((b32 >> 16) & 0xFF); |
michael@0 | 208 | dest[1] = (unsigned char)((b32 >> 8) & 0xFF); |
michael@0 | 209 | dest[2] = (unsigned char)((b32 ) & 0xFF); |
michael@0 | 210 | |
michael@0 | 211 | return PR_SUCCESS; |
michael@0 | 212 | } |
michael@0 | 213 | |
michael@0 | 214 | static PRStatus |
michael@0 | 215 | decode3to2 |
michael@0 | 216 | ( |
michael@0 | 217 | const unsigned char *src, |
michael@0 | 218 | unsigned char *dest |
michael@0 | 219 | ) |
michael@0 | 220 | { |
michael@0 | 221 | PRUint32 b32 = (PRUint32)0; |
michael@0 | 222 | PRInt32 bits; |
michael@0 | 223 | PRUint32 ubits; |
michael@0 | 224 | |
michael@0 | 225 | bits = codetovalue(src[0]); |
michael@0 | 226 | if( bits < 0 ) |
michael@0 | 227 | { |
michael@0 | 228 | return PR_FAILURE; |
michael@0 | 229 | } |
michael@0 | 230 | |
michael@0 | 231 | b32 = (PRUint32)bits; |
michael@0 | 232 | b32 <<= 6; |
michael@0 | 233 | |
michael@0 | 234 | bits = codetovalue(src[1]); |
michael@0 | 235 | if( bits < 0 ) |
michael@0 | 236 | { |
michael@0 | 237 | return PR_FAILURE; |
michael@0 | 238 | } |
michael@0 | 239 | |
michael@0 | 240 | b32 |= (PRUint32)bits; |
michael@0 | 241 | b32 <<= 4; |
michael@0 | 242 | |
michael@0 | 243 | bits = codetovalue(src[2]); |
michael@0 | 244 | if( bits < 0 ) |
michael@0 | 245 | { |
michael@0 | 246 | return PR_FAILURE; |
michael@0 | 247 | } |
michael@0 | 248 | |
michael@0 | 249 | ubits = (PRUint32)bits; |
michael@0 | 250 | b32 |= (ubits >> 2); |
michael@0 | 251 | |
michael@0 | 252 | dest[0] = (unsigned char)((b32 >> 8) & 0xFF); |
michael@0 | 253 | dest[1] = (unsigned char)((b32 ) & 0xFF); |
michael@0 | 254 | |
michael@0 | 255 | return PR_SUCCESS; |
michael@0 | 256 | } |
michael@0 | 257 | |
michael@0 | 258 | static PRStatus |
michael@0 | 259 | decode2to1 |
michael@0 | 260 | ( |
michael@0 | 261 | const unsigned char *src, |
michael@0 | 262 | unsigned char *dest |
michael@0 | 263 | ) |
michael@0 | 264 | { |
michael@0 | 265 | PRUint32 b32; |
michael@0 | 266 | PRUint32 ubits; |
michael@0 | 267 | PRInt32 bits; |
michael@0 | 268 | |
michael@0 | 269 | bits = codetovalue(src[0]); |
michael@0 | 270 | if( bits < 0 ) |
michael@0 | 271 | { |
michael@0 | 272 | return PR_FAILURE; |
michael@0 | 273 | } |
michael@0 | 274 | |
michael@0 | 275 | ubits = (PRUint32)bits; |
michael@0 | 276 | b32 = (ubits << 2); |
michael@0 | 277 | |
michael@0 | 278 | bits = codetovalue(src[1]); |
michael@0 | 279 | if( bits < 0 ) |
michael@0 | 280 | { |
michael@0 | 281 | return PR_FAILURE; |
michael@0 | 282 | } |
michael@0 | 283 | |
michael@0 | 284 | ubits = (PRUint32)bits; |
michael@0 | 285 | b32 |= (ubits >> 4); |
michael@0 | 286 | |
michael@0 | 287 | dest[0] = (unsigned char)b32; |
michael@0 | 288 | |
michael@0 | 289 | return PR_SUCCESS; |
michael@0 | 290 | } |
michael@0 | 291 | |
michael@0 | 292 | static PRStatus |
michael@0 | 293 | decode |
michael@0 | 294 | ( |
michael@0 | 295 | const unsigned char *src, |
michael@0 | 296 | PRUint32 srclen, |
michael@0 | 297 | unsigned char *dest |
michael@0 | 298 | ) |
michael@0 | 299 | { |
michael@0 | 300 | PRStatus rv; |
michael@0 | 301 | |
michael@0 | 302 | while( srclen >= 4 ) |
michael@0 | 303 | { |
michael@0 | 304 | rv = decode4to3(src, dest); |
michael@0 | 305 | if( PR_SUCCESS != rv ) |
michael@0 | 306 | { |
michael@0 | 307 | return PR_FAILURE; |
michael@0 | 308 | } |
michael@0 | 309 | |
michael@0 | 310 | src += 4; |
michael@0 | 311 | dest += 3; |
michael@0 | 312 | srclen -= 4; |
michael@0 | 313 | } |
michael@0 | 314 | |
michael@0 | 315 | switch( srclen ) |
michael@0 | 316 | { |
michael@0 | 317 | case 3: |
michael@0 | 318 | rv = decode3to2(src, dest); |
michael@0 | 319 | break; |
michael@0 | 320 | case 2: |
michael@0 | 321 | rv = decode2to1(src, dest); |
michael@0 | 322 | break; |
michael@0 | 323 | case 1: |
michael@0 | 324 | rv = PR_FAILURE; |
michael@0 | 325 | break; |
michael@0 | 326 | case 0: |
michael@0 | 327 | rv = PR_SUCCESS; |
michael@0 | 328 | break; |
michael@0 | 329 | default: |
michael@0 | 330 | PR_NOT_REACHED("coding error"); |
michael@0 | 331 | } |
michael@0 | 332 | |
michael@0 | 333 | return rv; |
michael@0 | 334 | } |
michael@0 | 335 | |
michael@0 | 336 | /* |
michael@0 | 337 | * PL_Base64Decode |
michael@0 | 338 | * |
michael@0 | 339 | * If the destination argument is NULL, a return buffer is |
michael@0 | 340 | * allocated and the data therein will be null-terminated. |
michael@0 | 341 | * If the destination argument is not null, it is assumed |
michael@0 | 342 | * to be of sufficient size, and the data will not be null- |
michael@0 | 343 | * terminated by this routine. |
michael@0 | 344 | * |
michael@0 | 345 | * Returns null if the allocation fails, or if the source string is |
michael@0 | 346 | * not well-formed. |
michael@0 | 347 | */ |
michael@0 | 348 | |
michael@0 | 349 | PR_IMPLEMENT(char *) |
michael@0 | 350 | PL_Base64Decode |
michael@0 | 351 | ( |
michael@0 | 352 | const char *src, |
michael@0 | 353 | PRUint32 srclen, |
michael@0 | 354 | char *dest |
michael@0 | 355 | ) |
michael@0 | 356 | { |
michael@0 | 357 | PRStatus status; |
michael@0 | 358 | PRBool allocated = PR_FALSE; |
michael@0 | 359 | |
michael@0 | 360 | if( (char *)0 == src ) |
michael@0 | 361 | { |
michael@0 | 362 | return (char *)0; |
michael@0 | 363 | } |
michael@0 | 364 | |
michael@0 | 365 | if( 0 == srclen ) |
michael@0 | 366 | { |
michael@0 | 367 | size_t len = strlen(src); |
michael@0 | 368 | srclen = len; |
michael@0 | 369 | /* Detect truncation. */ |
michael@0 | 370 | if( srclen != len ) |
michael@0 | 371 | { |
michael@0 | 372 | return (char *)0; |
michael@0 | 373 | } |
michael@0 | 374 | } |
michael@0 | 375 | |
michael@0 | 376 | if( srclen && (0 == (srclen & 3)) ) |
michael@0 | 377 | { |
michael@0 | 378 | if( (char)'=' == src[ srclen-1 ] ) |
michael@0 | 379 | { |
michael@0 | 380 | if( (char)'=' == src[ srclen-2 ] ) |
michael@0 | 381 | { |
michael@0 | 382 | srclen -= 2; |
michael@0 | 383 | } |
michael@0 | 384 | else |
michael@0 | 385 | { |
michael@0 | 386 | srclen -= 1; |
michael@0 | 387 | } |
michael@0 | 388 | } |
michael@0 | 389 | } |
michael@0 | 390 | |
michael@0 | 391 | if( (char *)0 == dest ) |
michael@0 | 392 | { |
michael@0 | 393 | /* The following computes ((srclen * 3) / 4) without overflow. */ |
michael@0 | 394 | PRUint32 destlen = (srclen / 4) * 3 + ((srclen % 4) * 3) / 4; |
michael@0 | 395 | dest = (char *)PR_MALLOC(destlen + 1); |
michael@0 | 396 | if( (char *)0 == dest ) |
michael@0 | 397 | { |
michael@0 | 398 | return (char *)0; |
michael@0 | 399 | } |
michael@0 | 400 | dest[ destlen ] = (char)0; /* null terminate */ |
michael@0 | 401 | allocated = PR_TRUE; |
michael@0 | 402 | } |
michael@0 | 403 | |
michael@0 | 404 | status = decode((const unsigned char *)src, srclen, (unsigned char *)dest); |
michael@0 | 405 | if( PR_SUCCESS != status ) |
michael@0 | 406 | { |
michael@0 | 407 | if( PR_TRUE == allocated ) |
michael@0 | 408 | { |
michael@0 | 409 | PR_DELETE(dest); |
michael@0 | 410 | } |
michael@0 | 411 | |
michael@0 | 412 | return (char *)0; |
michael@0 | 413 | } |
michael@0 | 414 | |
michael@0 | 415 | return dest; |
michael@0 | 416 | } |