LCOV - code coverage report
Current view: top level - lib/support - Base64.cpp (source / functions) Hit Total Coverage
Test: lcov_final.info Lines: 76 146 52.1 %
Date: 2024-02-15 08:20:41 Functions: 6 14 42.9 %

          Line data    Source code
       1             : /*
       2             :  *
       3             :  *    Copyright (c) 2020 Project CHIP Authors
       4             :  *    Copyright (c) 2013-2017 Nest Labs, Inc.
       5             :  *
       6             :  *    Licensed under the Apache License, Version 2.0 (the "License");
       7             :  *    you may not use this file except in compliance with the License.
       8             :  *    You may obtain a copy of the License at
       9             :  *
      10             :  *        http://www.apache.org/licenses/LICENSE-2.0
      11             :  *
      12             :  *    Unless required by applicable law or agreed to in writing, software
      13             :  *    distributed under the License is distributed on an "AS IS" BASIS,
      14             :  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      15             :  *    See the License for the specific language governing permissions and
      16             :  *    limitations under the License.
      17             :  */
      18             : 
      19             : /**
      20             :  *    @file
      21             :  *      Base-64 utility functions.
      22             :  *
      23             :  */
      24             : 
      25             : #ifndef __STDC_LIMIT_MACROS
      26             : #define __STDC_LIMIT_MACROS
      27             : #endif
      28             : #include "Base64.h"
      29             : 
      30             : #include <ctype.h>
      31             : #include <stdint.h>
      32             : 
      33             : namespace chip {
      34             : 
      35             : // Convert a value in the range 0..63 to its equivalent base64 character.
      36             : // Return '=' for any value >= 64.
      37       17048 : static char Base64ValToChar(uint8_t val)
      38             : {
      39       17048 :     if (val < 26)
      40        6677 :         return static_cast<char>('A' + val);
      41       10371 :     val = static_cast<uint8_t>(val - 26);
      42       10371 :     if (val < 26)
      43        6206 :         return static_cast<char>('a' + val);
      44        4165 :     val = static_cast<uint8_t>(val - 26);
      45        4165 :     if (val < 10)
      46        2208 :         return static_cast<char>('0' + val);
      47        1957 :     if (val == 10)
      48         267 :         return '+';
      49        1690 :     if (val == 11)
      50         289 :         return '/';
      51        1401 :     return '=';
      52             : }
      53             : 
      54             : // Convert a base64 character to a value in the range 0..63, or UINT8_MAX if the character is invalid.
      55         678 : static uint8_t Base64CharToVal(uint8_t c)
      56             : {
      57         678 :     if (c == 43)
      58           8 :         return 62;
      59         670 :     if (c == 47)
      60          11 :         return 63;
      61             :     // NOTE: c < 48 will fall through to return UINT8_MAX below.
      62         659 :     c = static_cast<uint8_t>(c - 48);
      63         659 :     if (c < 10)
      64          37 :         return static_cast<uint8_t>(c + 52);
      65             :     // NOTE: c < 17 here will fall through to return UINT8_MAX below.
      66         622 :     c = static_cast<uint8_t>(c - 17);
      67         622 :     if (c < 26)
      68         402 :         return c;
      69             :     // NOTE: c < 32 here will fall through to return UINT8_MAX below.
      70         220 :     c = static_cast<uint8_t>(c - 32);
      71         220 :     if (c < 26)
      72         220 :         return static_cast<uint8_t>(c + 26);
      73           0 :     return UINT8_MAX;
      74             : }
      75             : 
      76             : // Convert a value in the range 0..63 to its equivalent base64url character (see RFC-4648, section 5).
      77             : // Return '=' for any value >= 64.
      78           0 : static char Base64URLValToChar(uint8_t val)
      79             : {
      80           0 :     if (val < 26)
      81           0 :         return static_cast<char>('A' + val);
      82           0 :     val = static_cast<uint8_t>(val - 26);
      83           0 :     if (val < 26)
      84           0 :         return static_cast<char>('a' + val);
      85           0 :     val = static_cast<uint8_t>(val - 26);
      86           0 :     if (val < 10)
      87           0 :         return static_cast<char>('0' + val);
      88           0 :     if (val == 10)
      89           0 :         return '-';
      90           0 :     if (val == 11)
      91           0 :         return '_';
      92           0 :     return '=';
      93             : }
      94             : 
      95             : // Convert a base64url character to a value in the range 0..63, or UINT8_MAX if the character is invalid.
      96           0 : static uint8_t Base64URLCharToVal(uint8_t c)
      97             : {
      98           0 :     if (c == 45)
      99           0 :         return 62;
     100           0 :     if (c == 95)
     101           0 :         return 63;
     102             :     // NOTE: c < 48 will fall through to return UINT8_MAX below.
     103           0 :     c = static_cast<uint8_t>(c - 48);
     104           0 :     if (c < 10)
     105           0 :         return static_cast<uint8_t>(c + 52);
     106             :     // NOTE: c < 17 here will fall through to return UINT8_MAX below.
     107           0 :     c = static_cast<uint8_t>(c - 17);
     108           0 :     if (c < 26)
     109           0 :         return c;
     110             :     // NOTE: c < 32 here will fall through to return UINT8_MAX below.
     111           0 :     c = static_cast<uint8_t>(c - 32);
     112           0 :     if (c < 26)
     113           0 :         return static_cast<uint8_t>(c + 26);
     114           0 :     return UINT8_MAX;
     115             : }
     116             : 
     117         706 : uint16_t Base64Encode(const uint8_t * in, uint16_t inLen, char * out, Base64ValToCharFunct valToCharFunct)
     118             : {
     119         706 :     char * outStart = out;
     120             : 
     121        4968 :     while (inLen > 0)
     122             :     {
     123             :         uint8_t val1, val2, val3, val4;
     124             : 
     125        4262 :         val1 = static_cast<uint8_t>(*in >> 2);
     126        4262 :         val2 = static_cast<uint8_t>((*in << 4) & 0x3F);
     127        4262 :         in++;
     128        4262 :         inLen--;
     129        4262 :         if (inLen > 0)
     130             :         {
     131        3565 :             val2 = static_cast<uint8_t>(val2 | *in >> 4);
     132        3565 :             val3 = static_cast<uint8_t>((*in << 2) & 0x3F);
     133        3565 :             in++;
     134        3565 :             inLen--;
     135        3565 :             if (inLen > 0)
     136             :             {
     137        3558 :                 val3 = static_cast<uint8_t>(val3 | *in >> 6);
     138        3558 :                 val4 = static_cast<uint8_t>(*in & 0x3F);
     139        3558 :                 in++;
     140        3558 :                 inLen--;
     141             :             }
     142             :             else
     143           7 :                 val4 = UINT8_MAX;
     144             :         }
     145             :         else
     146         697 :             val3 = val4 = UINT8_MAX;
     147             : 
     148        4262 :         *out++ = valToCharFunct(val1);
     149        4262 :         *out++ = valToCharFunct(val2);
     150        4262 :         *out++ = valToCharFunct(val3);
     151        4262 :         *out++ = valToCharFunct(val4);
     152             :     }
     153             : 
     154         706 :     return static_cast<uint16_t>(out - outStart);
     155             : }
     156             : 
     157         706 : uint16_t Base64Encode(const uint8_t * in, uint16_t inLen, char * out)
     158             : {
     159         706 :     return Base64Encode(in, inLen, out, Base64ValToChar);
     160             : }
     161             : 
     162           0 : uint16_t Base64URLEncode(const uint8_t * in, uint16_t inLen, char * out)
     163             : {
     164           0 :     return Base64Encode(in, inLen, out, Base64URLValToChar);
     165             : }
     166             : 
     167           0 : uint32_t Base64Encode32(const uint8_t * in, uint32_t inLen, char * out, Base64ValToCharFunct valToCharFunct)
     168             : {
     169           0 :     uint32_t outLen = 0;
     170             : 
     171             :     // Maximum number of input bytes to convert to base-64 in a single call to Base64Encode.
     172             :     // Number is the largest multiple of 3 bytes where the resulting number of base-64 characters
     173             :     // fits within a uint16_t.
     174             :     enum
     175             :     {
     176             :         kMaxConvert = (UINT16_MAX / 4) * 3
     177             :     };
     178             : 
     179             :     while (true)
     180             :     {
     181           0 :         uint16_t inChunkLen = (inLen > kMaxConvert) ? static_cast<uint16_t>(kMaxConvert) : static_cast<uint16_t>(inLen);
     182             : 
     183           0 :         uint16_t outChunkLen = Base64Encode(in, inChunkLen, out, valToCharFunct);
     184             : 
     185           0 :         inLen -= inChunkLen;
     186           0 :         outLen += outChunkLen;
     187             : 
     188           0 :         if (inLen == 0)
     189           0 :             break;
     190             : 
     191           0 :         in += inChunkLen;
     192           0 :         out += outChunkLen;
     193           0 :     }
     194             : 
     195           0 :     return outLen;
     196             : }
     197             : 
     198           0 : uint32_t Base64Encode32(const uint8_t * in, uint32_t inLen, char * out)
     199             : {
     200           0 :     return Base64Encode32(in, inLen, out, Base64ValToChar);
     201             : }
     202             : 
     203          23 : uint16_t Base64Decode(const char * in, uint16_t inLen, uint8_t * out, Base64CharToValFunct charToValFunct)
     204             : {
     205          23 :     uint8_t * outStart = out;
     206             : 
     207             :     // isgraph() returns false for space and ctrl chars
     208         181 :     while (inLen > 0 && isgraph(*in))
     209             :     {
     210         177 :         if (inLen == 1)
     211           0 :             goto fail;
     212             : 
     213         177 :         uint8_t a = charToValFunct(static_cast<uint8_t>(*in++));
     214         177 :         uint8_t b = charToValFunct(static_cast<uint8_t>(*in++));
     215         177 :         inLen     = static_cast<uint16_t>(inLen - 2);
     216             : 
     217         177 :         if (a == UINT8_MAX || b == UINT8_MAX)
     218           0 :             goto fail;
     219             : 
     220         177 :         *out++ = static_cast<uint8_t>((a << 2) | (b >> 4));
     221             : 
     222         177 :         if (inLen == 0 || *in == '=')
     223             :             break;
     224             : 
     225         166 :         uint8_t c = charToValFunct(static_cast<uint8_t>(*in++));
     226         166 :         inLen--;
     227             : 
     228         166 :         if (c == UINT8_MAX)
     229           0 :             goto fail;
     230             : 
     231         166 :         *out++ = static_cast<uint8_t>((b << 4) | (c >> 2));
     232             : 
     233         166 :         if (inLen == 0 || *in == '=')
     234             :             break;
     235             : 
     236         158 :         uint8_t d = charToValFunct(static_cast<uint8_t>(*in++));
     237         158 :         inLen--;
     238             : 
     239         158 :         if (d == UINT8_MAX)
     240           0 :             goto fail;
     241             : 
     242         158 :         *out++ = static_cast<uint8_t>((c << 6) | d);
     243             :     }
     244             : 
     245          23 :     return static_cast<uint16_t>(out - outStart);
     246             : 
     247           0 : fail:
     248           0 :     return UINT16_MAX;
     249             : }
     250             : 
     251          23 : uint16_t Base64Decode(const char * in, uint16_t inLen, uint8_t * out)
     252             : {
     253          23 :     return Base64Decode(in, inLen, out, Base64CharToVal);
     254             : }
     255             : 
     256           0 : uint16_t Base64URLDecode(const char * in, uint16_t inLen, uint8_t * out)
     257             : {
     258           0 :     return Base64Decode(in, inLen, out, Base64URLCharToVal);
     259             : }
     260             : 
     261           0 : uint32_t Base64Decode32(const char * in, uint32_t inLen, uint8_t * out, Base64CharToValFunct charToValFunct)
     262             : {
     263           0 :     uint32_t outLen = 0;
     264             : 
     265             :     // Maximum number of base-64 characters to convert in a single call to Base64Decode.
     266             :     // Number is the largest multiple of 4 characters that fits in a uint16_t.
     267             :     enum
     268             :     {
     269             :         kMaxConvert = (UINT16_MAX / 4) * 4
     270             :     };
     271             : 
     272             :     while (true)
     273             :     {
     274           0 :         uint16_t inChunkLen = (inLen > kMaxConvert) ? static_cast<uint16_t>(kMaxConvert) : static_cast<uint16_t>(inLen);
     275             : 
     276           0 :         uint16_t outChunkLen = Base64Decode(in, inChunkLen, out, charToValFunct);
     277           0 :         if (outChunkLen == UINT16_MAX)
     278           0 :             return UINT32_MAX;
     279             : 
     280           0 :         inLen -= inChunkLen;
     281           0 :         outLen += outChunkLen;
     282             : 
     283           0 :         if (inLen == 0)
     284           0 :             break;
     285             : 
     286           0 :         in += inChunkLen;
     287           0 :         out += outChunkLen;
     288           0 :     }
     289             : 
     290           0 :     return outLen;
     291             : }
     292             : 
     293           0 : uint32_t Base64Decode32(const char * in, uint32_t inLen, uint8_t * out)
     294             : {
     295           0 :     return Base64Decode32(in, inLen, out, Base64CharToVal);
     296             : }
     297             : 
     298             : } // namespace chip
     299             : 
     300             : #ifdef TEST
     301             : 
     302             : #include <stdio.h>
     303             : #include <string.h>
     304             : 
     305             : void TestBase64(const char * test, bool base64URL = false)
     306             : {
     307             :     uint8_t buf[256];
     308             :     char buf2[256];
     309             :     uint16_t len;
     310             : 
     311             :     strcpy((char *) buf, test);
     312             : 
     313             :     len = (base64URL) ? nl::Base64URLDecode((char *) buf, strlen((char *) buf), buf)
     314             :                       : nl::Base64Decode((char *) buf, strlen((char *) buf), buf);
     315             :     printf("%s: ", test);
     316             :     if (len != UINT16_MAX)
     317             :     {
     318             :         printf("(%d) ", len);
     319             :         for (uint16_t i = 0; i < len; i++)
     320             :             printf("%c", buf[i]);
     321             : 
     322             :         len = (base64URL) ? nl::Base64URLEncode(buf, len, buf2) : nl::Base64Encode(buf, len, buf2);
     323             :         printf(" (%d) ", len);
     324             :         for (uint16_t i = 0; i < len; i++)
     325             :             printf("%c", buf2[i]);
     326             :     }
     327             :     else
     328             :         printf("ERROR");
     329             :     printf("\n");
     330             : }
     331             : 
     332             : int main(int argc, char * argv[])
     333             : {
     334             :     TestBase64("");
     335             :     TestBase64("Zg==");
     336             :     TestBase64("Zm8=");
     337             :     TestBase64("Zm9v");
     338             :     TestBase64("Zm9vYg==");
     339             :     TestBase64("Zm9vYmE=");
     340             :     TestBase64("Zm9vYmFy");
     341             :     TestBase64("QmFzZTY0D+8xMjM0D/8=");
     342             : 
     343             :     TestBase64("Zg");
     344             :     TestBase64("Zm8");
     345             :     TestBase64("Zm9vYg");
     346             :     TestBase64("Zm9vYmE");
     347             : 
     348             :     TestBase64("QmFzZTY0D-8xMjM0D_8=", true);
     349             : 
     350             :     // Error cases
     351             :     TestBase64("Z");
     352             :     TestBase64("Z\x019vYmFy");
     353             :     TestBase64("Zm9vY");
     354             :     TestBase64("Zm9vY;");
     355             :     TestBase64("Zm9 vYg");
     356             : }
     357             : 
     358             : #endif // TEST

Generated by: LCOV version 1.14