/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "plbase64.h" #include "prlog.h" /* For PR_NOT_REACHED */ #include "prmem.h" /* for malloc / PR_MALLOC */ #include /* for strlen */ static unsigned char *base = (unsigned char *)"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; static void encode3to4(const unsigned char* src, unsigned char* dest) { PRUint32 b32 = (PRUint32)0; PRIntn i, j = 18; for (i = 0; i < 3; i++) { b32 <<= 8; b32 |= (PRUint32)src[i]; } for (i = 0; i < 4; i++) { dest[i] = base[(PRUint32)((b32 >> j) & 0x3F)]; j -= 6; } return; } static void encode2to4(const unsigned char* src, unsigned char* dest) { dest[0] = base[(PRUint32)((src[0] >> 2) & 0x3F)]; dest[1] = base[(PRUint32)(((src[0] & 0x03) << 4) | ((src[1] >> 4) & 0x0F))]; dest[2] = base[(PRUint32)((src[1] & 0x0F) << 2)]; dest[3] = (unsigned char)'='; return; } static void encode1to4(const unsigned char* src, unsigned char* dest) { dest[0] = base[(PRUint32)((src[0] >> 2) & 0x3F)]; dest[1] = base[(PRUint32)((src[0] & 0x03) << 4)]; dest[2] = (unsigned char)'='; dest[3] = (unsigned char)'='; return; } static void encode(const unsigned char* src, PRUint32 srclen, unsigned char* dest) { while (srclen >= 3) { encode3to4(src, dest); src += 3; dest += 4; srclen -= 3; } switch (srclen) { case 2: encode2to4(src, dest); break; case 1: encode1to4(src, dest); break; case 0: break; default: PR_NOT_REACHED("coding error"); } return; } /* * PL_Base64Encode * * If the destination argument is NULL, a return buffer is * allocated, and the data therein will be null-terminated. * If the destination argument is not NULL, it is assumed to * be of sufficient size, and the contents will not be null- * terminated by this routine. * * Returns null if the allocation fails. */ PR_IMPLEMENT(char*) PL_Base64Encode(const char* src, PRUint32 srclen, char* dest) { if (0 == srclen) { size_t len = strlen(src); srclen = len; /* Detect truncation. */ if (srclen != len) { return (char*)0; } } if ((char*)0 == dest) { PRUint32 destlen; /* Ensure all PRUint32 values stay within range. */ if (srclen > (PR_UINT32_MAX / 4) * 3) { return (char*)0; } destlen = ((srclen + 2) / 3) * 4; dest = (char*)PR_MALLOC(destlen + 1); if ((char*)0 == dest) { return (char*)0; } dest[destlen] = (char)0; /* null terminate */ } encode((const unsigned char*)src, srclen, (unsigned char*)dest); return dest; } static PRInt32 codetovalue(unsigned char c) { if ((c >= (unsigned char)'A') && (c <= (unsigned char)'Z')) { return (PRInt32)(c - (unsigned char)'A'); } else if ((c >= (unsigned char)'a') && (c <= (unsigned char)'z')) { return ((PRInt32)(c - (unsigned char)'a') + 26); } else if ((c >= (unsigned char)'0') && (c <= (unsigned char)'9')) { return ((PRInt32)(c - (unsigned char)'0') + 52); } else if ((unsigned char)'+' == c) { return (PRInt32)62; } else if ((unsigned char)'/' == c) { return (PRInt32)63; } else { return -1; } } static PRStatus decode4to3(const unsigned char* src, unsigned char* dest) { PRUint32 b32 = (PRUint32)0; PRInt32 bits; PRIntn i; for (i = 0; i < 4; i++) { bits = codetovalue(src[i]); if (bits < 0) { return PR_FAILURE; } b32 <<= 6; b32 |= bits; } dest[0] = (unsigned char)((b32 >> 16) & 0xFF); dest[1] = (unsigned char)((b32 >> 8) & 0xFF); dest[2] = (unsigned char)((b32) & 0xFF); return PR_SUCCESS; } static PRStatus decode3to2(const unsigned char* src, unsigned char* dest) { PRUint32 b32 = (PRUint32)0; PRInt32 bits; PRUint32 ubits; bits = codetovalue(src[0]); if (bits < 0) { return PR_FAILURE; } b32 = (PRUint32)bits; b32 <<= 6; bits = codetovalue(src[1]); if (bits < 0) { return PR_FAILURE; } b32 |= (PRUint32)bits; b32 <<= 4; bits = codetovalue(src[2]); if (bits < 0) { return PR_FAILURE; } ubits = (PRUint32)bits; b32 |= (ubits >> 2); dest[0] = (unsigned char)((b32 >> 8) & 0xFF); dest[1] = (unsigned char)((b32) & 0xFF); return PR_SUCCESS; } static PRStatus decode2to1(const unsigned char* src, unsigned char* dest) { PRUint32 b32; PRUint32 ubits; PRInt32 bits; bits = codetovalue(src[0]); if (bits < 0) { return PR_FAILURE; } ubits = (PRUint32)bits; b32 = (ubits << 2); bits = codetovalue(src[1]); if (bits < 0) { return PR_FAILURE; } ubits = (PRUint32)bits; b32 |= (ubits >> 4); dest[0] = (unsigned char)b32; return PR_SUCCESS; } static PRStatus decode(const unsigned char* src, PRUint32 srclen, unsigned char* dest) { PRStatus rv; while (srclen >= 4) { rv = decode4to3(src, dest); if (PR_SUCCESS != rv) { return PR_FAILURE; } src += 4; dest += 3; srclen -= 4; } switch (srclen) { case 3: rv = decode3to2(src, dest); break; case 2: rv = decode2to1(src, dest); break; case 1: rv = PR_FAILURE; break; case 0: rv = PR_SUCCESS; break; default: PR_NOT_REACHED("coding error"); } return rv; } /* * PL_Base64Decode * * If the destination argument is NULL, a return buffer is * allocated and the data therein will be null-terminated. * If the destination argument is not null, it is assumed * to be of sufficient size, and the data will not be null- * terminated by this routine. * * Returns null if the allocation fails, or if the source string is * not well-formed. */ PR_IMPLEMENT(char*) PL_Base64Decode(const char* src, PRUint32 srclen, char* dest) { PRStatus status; PRBool allocated = PR_FALSE; if ((char*)0 == src) { return (char*)0; } if (0 == srclen) { size_t len = strlen(src); srclen = len; /* Detect truncation. */ if (srclen != len) { return (char*)0; } } if (srclen && (0 == (srclen & 3))) { if ((char)'=' == src[srclen - 1]) { if ((char)'=' == src[srclen - 2]) { srclen -= 2; } else { srclen -= 1; } } } if ((char*)0 == dest) { /* The following computes ((srclen * 3) / 4) without overflow. */ PRUint32 destlen = (srclen / 4) * 3 + ((srclen % 4) * 3) / 4; dest = (char*)PR_MALLOC(destlen + 1); if ((char*)0 == dest) { return (char*)0; } dest[destlen] = (char)0; /* null terminate */ allocated = PR_TRUE; } status = decode((const unsigned char*)src, srclen, (unsigned char*)dest); if (PR_SUCCESS != status) { if (PR_TRUE == allocated) { PR_DELETE(dest); } return (char*)0; } return dest; }