]> git.proxmox.com Git - mirror_edk2.git/commitdiff
MdePkg/BaseLib: re-specify Base64Decode(), and add temporary stub impl
authorLaszlo Ersek <lersek@redhat.com>
Mon, 1 Jul 2019 17:46:01 +0000 (19:46 +0200)
committerLaszlo Ersek <lersek@redhat.com>
Tue, 16 Jul 2019 21:04:15 +0000 (23:04 +0200)
Rewrite Base64Decode() from scratch, due to reasons listed in the second
reference below.

As first step, redo the interface contract, and replace the current
implementation with a stub that asserts FALSE, then fails.

Cc: Liming Gao <liming.gao@intel.com>
Cc: Marvin Häuser <mhaeuser@outlook.de>
Cc: Michael D Kinney <michael.d.kinney@intel.com>
Cc: Philippe Mathieu-Daudé <philmd@redhat.com>
Cc: Zhichao Gao <zhichao.gao@intel.com>
Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=1891
Ref: http://mid.mail-archive.com/c495bd0b-ea4d-7206-8a4f-a7149760d19a@redhat.com
Signed-off-by: Laszlo Ersek <lersek@redhat.com>
Reviewed-by: Philippe Mathieu-Daude <philmd@redhat.com>
Reviewed-by: Liming Gao <liming.gao@intel.com>
MdePkg/Include/Library/BaseLib.h
MdePkg/Library/BaseLib/String.c

index a22bfc9fadff9b0494b930f7a6e9f30152153ac9..2a75bc023f56adb4d6a1c88ed6493550815f3cd4 100644 (file)
@@ -2785,31 +2785,94 @@ Base64Encode (
   );\r
 \r
 /**\r
-  Convert Base64 ascii string to binary data based on RFC4648.\r
-\r
-  Produce Null-terminated binary data in the output buffer specified by Destination and DestinationSize.\r
-  The binary data is produced by converting the Base64 ascii string specified by Source and SourceLength.\r
-\r
-  @param Source          Input ASCII characters\r
-  @param SourceLength    Number of ASCII characters\r
-  @param Destination     Pointer to output buffer\r
-  @param DestinationSize Caller is responsible for passing in buffer of at least DestinationSize.\r
-                         Set 0 to get the size needed. Set to bytes stored on return.\r
-\r
-  @retval RETURN_SUCCESS             When binary buffer is filled in.\r
-  @retval RETURN_INVALID_PARAMETER   If Source is NULL or DestinationSize is NULL.\r
-  @retval RETURN_INVALID_PARAMETER   If SourceLength or DestinationSize is bigger than (MAX_ADDRESS -(UINTN)Destination ).\r
-  @retval RETURN_INVALID_PARAMETER   If there is any invalid character in input stream.\r
-  @retval RETURN_BUFFER_TOO_SMALL    If buffer length is smaller than required buffer size.\r
-\r
- **/\r
+  Decode Base64 ASCII encoded data to 8-bit binary representation, based on\r
+  RFC4648.\r
+\r
+  Decoding occurs according to "Table 1: The Base 64 Alphabet" in RFC4648.\r
+\r
+  Whitespace is ignored at all positions:\r
+  - 0x09 ('\t') horizontal tab\r
+  - 0x0A ('\n') new line\r
+  - 0x0B ('\v') vertical tab\r
+  - 0x0C ('\f') form feed\r
+  - 0x0D ('\r') carriage return\r
+  - 0x20 (' ')  space\r
+\r
+  The minimum amount of required padding (with ASCII 0x3D, '=') is tolerated\r
+  and enforced at the end of the Base64 ASCII encoded data, and only there.\r
+\r
+  Other characters outside of the encoding alphabet cause the function to\r
+  reject the Base64 ASCII encoded data.\r
+\r
+  @param[in] Source               Array of CHAR8 elements containing the Base64\r
+                                  ASCII encoding. May be NULL if SourceSize is\r
+                                  zero.\r
+\r
+  @param[in] SourceSize           Number of CHAR8 elements in Source.\r
+\r
+  @param[out] Destination         Array of UINT8 elements receiving the decoded\r
+                                  8-bit binary representation. Allocated by the\r
+                                  caller. May be NULL if DestinationSize is\r
+                                  zero on input. If NULL, decoding is\r
+                                  performed, but the 8-bit binary\r
+                                  representation is not stored. If non-NULL and\r
+                                  the function returns an error, the contents\r
+                                  of Destination are indeterminate.\r
+\r
+  @param[in,out] DestinationSize  On input, the number of UINT8 elements that\r
+                                  the caller allocated for Destination. On\r
+                                  output, if the function returns\r
+                                  RETURN_SUCCESS or RETURN_BUFFER_TOO_SMALL,\r
+                                  the number of UINT8 elements that are\r
+                                  required for decoding the Base64 ASCII\r
+                                  representation. If the function returns a\r
+                                  value different from both RETURN_SUCCESS and\r
+                                  RETURN_BUFFER_TOO_SMALL, then DestinationSize\r
+                                  is indeterminate on output.\r
+\r
+  @retval RETURN_SUCCESS            SourceSize CHAR8 elements at Source have\r
+                                    been decoded to on-output DestinationSize\r
+                                    UINT8 elements at Destination. Note that\r
+                                    RETURN_SUCCESS covers the case when\r
+                                    DestinationSize is zero on input, and\r
+                                    Source decodes to zero bytes (due to\r
+                                    containing at most ignored whitespace).\r
+\r
+  @retval RETURN_BUFFER_TOO_SMALL   The input value of DestinationSize is not\r
+                                    large enough for decoding SourceSize CHAR8\r
+                                    elements at Source. The required number of\r
+                                    UINT8 elements has been stored to\r
+                                    DestinationSize.\r
+\r
+  @retval RETURN_INVALID_PARAMETER  DestinationSize is NULL.\r
+\r
+  @retval RETURN_INVALID_PARAMETER  Source is NULL, but SourceSize is not zero.\r
+\r
+  @retval RETURN_INVALID_PARAMETER  Destination is NULL, but DestinationSize is\r
+                                    not zero on input.\r
+\r
+  @retval RETURN_INVALID_PARAMETER  Source is non-NULL, and (Source +\r
+                                    SourceSize) would wrap around MAX_ADDRESS.\r
+\r
+  @retval RETURN_INVALID_PARAMETER  Destination is non-NULL, and (Destination +\r
+                                    DestinationSize) would wrap around\r
+                                    MAX_ADDRESS, as specified on input.\r
+\r
+  @retval RETURN_INVALID_PARAMETER  None of Source and Destination are NULL,\r
+                                    and CHAR8[SourceSize] at Source overlaps\r
+                                    UINT8[DestinationSize] at Destination, as\r
+                                    specified on input.\r
+\r
+  @retval RETURN_INVALID_PARAMETER  Invalid CHAR8 element encountered in\r
+                                    Source.\r
+**/\r
 RETURN_STATUS\r
 EFIAPI\r
 Base64Decode (\r
-  IN  CONST CHAR8  *Source,\r
-  IN        UINTN   SourceLength,\r
-  OUT       UINT8  *Destination  OPTIONAL,\r
-  IN OUT    UINTN  *DestinationSize\r
+  IN     CONST CHAR8 *Source          OPTIONAL,\r
+  IN     UINTN       SourceSize,\r
+  OUT    UINT8       *Destination     OPTIONAL,\r
+  IN OUT UINTN       *DestinationSize\r
   );\r
 \r
 /**\r
index 32e189791cb8716cd543a2b7231dc6ee51b46892..f8397035c32a263645a8fe1beb53573de06936bd 100644 (file)
@@ -1757,45 +1757,10 @@ AsciiStrToUnicodeStr (
 \r
 #endif\r
 \r
-//\r
-// The basis for Base64 encoding is RFC 4686 https://tools.ietf.org/html/rfc4648\r
-//\r
-// RFC 4686 has a number of MAY and SHOULD cases.  This implementation chooses\r
-// the more restrictive versions for security concerns (see RFC 4686 section 3.3).\r
-//\r
-// A invalid character, if encountered during the decode operation, causes the data\r
-// to be rejected. In addition, the '=' padding character is only allowed at the end\r
-// of the Base64 encoded string.\r
-//\r
-#define BAD_V  99\r
-\r
 STATIC CHAR8 EncodingTable[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"\r
                                 "abcdefghijklmnopqrstuvwxyz"\r
                                 "0123456789+/";\r
 \r
-STATIC UINT8 DecodingTable[] = {\r
-  //\r
-  // Valid characters ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\r
-  // Also, set '=' as a zero for decoding\r
-  // 0  ,            1,           2,           3,            4,           5,            6,           7,           8,            9,           a,            b,            c,           d,            e,            f\r
-  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,   //   0\r
-  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,   //  10\r
-  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,     62,  BAD_V,  BAD_V,  BAD_V,     63,   //  20\r
-     52,     53,     54,     55,     56,     57,     58,     59,     60,     61,  BAD_V,  BAD_V,  BAD_V,      0,  BAD_V,  BAD_V,   //  30\r
-  BAD_V,      0,      1,      2,      3,      4,      5,      6,      7,      8,      9,     10,     11,     12,     13,     14,   //  40\r
-     15,     16,     17,     18,     19,     20,     21,     22,     23,     24,     25,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,   //  50\r
-  BAD_V,     26,     27,     28,     29,     30,     31,     32,     33,     34,     35,     36,     37,     38,     39,     40,   //  60\r
-     41,     42,     43,     44,     45,     46,     47,     48,     49,     50,     51,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,   //  70\r
-  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,   //  80\r
-  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,   //  90\r
-  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,   //  a0\r
-  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,   //  b0\r
-  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,   //  c0\r
-  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,   //  d0\r
-  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,   //  d0\r
-  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V,  BAD_V    //  f0\r
-};\r
-\r
 /**\r
   Convert binary data to a Base64 encoded ascii string based on RFC4648.\r
 \r
@@ -1918,174 +1883,98 @@ Base64Encode (
 }\r
 \r
 /**\r
-  Convert Base64 ascii string to binary data based on RFC4648.\r
-\r
-  Produce Null-terminated binary data in the output buffer specified by Destination and DestinationSize.\r
-  The binary data is produced by converting the Base64 ascii string specified by Source and SourceLength.\r
-\r
-  @param Source            Input ASCII characters\r
-  @param SourceLength      Number of ASCII characters\r
-  @param Destination       Pointer to output buffer\r
-  @param DestinationSize   Caller is responsible for passing in buffer of at least DestinationSize.\r
-                           Set 0 to get the size needed. Set to bytes stored on return.\r
-\r
-  @retval RETURN_SUCCESS             When binary buffer is filled in.\r
-  @retval RETURN_INVALID_PARAMETER   If Source is NULL or DestinationSize is NULL.\r
-  @retval RETURN_INVALID_PARAMETER   If SourceLength or DestinationSize is bigger than (MAX_ADDRESS -(UINTN)Destination ).\r
-  @retval RETURN_INVALID_PARAMETER   If there is any invalid character in input stream.\r
-  @retval RETURN_BUFFER_TOO_SMALL    If buffer length is smaller than required buffer size.\r
- **/\r
+  Decode Base64 ASCII encoded data to 8-bit binary representation, based on\r
+  RFC4648.\r
+\r
+  Decoding occurs according to "Table 1: The Base 64 Alphabet" in RFC4648.\r
+\r
+  Whitespace is ignored at all positions:\r
+  - 0x09 ('\t') horizontal tab\r
+  - 0x0A ('\n') new line\r
+  - 0x0B ('\v') vertical tab\r
+  - 0x0C ('\f') form feed\r
+  - 0x0D ('\r') carriage return\r
+  - 0x20 (' ')  space\r
+\r
+  The minimum amount of required padding (with ASCII 0x3D, '=') is tolerated\r
+  and enforced at the end of the Base64 ASCII encoded data, and only there.\r
+\r
+  Other characters outside of the encoding alphabet cause the function to\r
+  reject the Base64 ASCII encoded data.\r
+\r
+  @param[in] Source               Array of CHAR8 elements containing the Base64\r
+                                  ASCII encoding. May be NULL if SourceSize is\r
+                                  zero.\r
+\r
+  @param[in] SourceSize           Number of CHAR8 elements in Source.\r
+\r
+  @param[out] Destination         Array of UINT8 elements receiving the decoded\r
+                                  8-bit binary representation. Allocated by the\r
+                                  caller. May be NULL if DestinationSize is\r
+                                  zero on input. If NULL, decoding is\r
+                                  performed, but the 8-bit binary\r
+                                  representation is not stored. If non-NULL and\r
+                                  the function returns an error, the contents\r
+                                  of Destination are indeterminate.\r
+\r
+  @param[in,out] DestinationSize  On input, the number of UINT8 elements that\r
+                                  the caller allocated for Destination. On\r
+                                  output, if the function returns\r
+                                  RETURN_SUCCESS or RETURN_BUFFER_TOO_SMALL,\r
+                                  the number of UINT8 elements that are\r
+                                  required for decoding the Base64 ASCII\r
+                                  representation. If the function returns a\r
+                                  value different from both RETURN_SUCCESS and\r
+                                  RETURN_BUFFER_TOO_SMALL, then DestinationSize\r
+                                  is indeterminate on output.\r
+\r
+  @retval RETURN_SUCCESS            SourceSize CHAR8 elements at Source have\r
+                                    been decoded to on-output DestinationSize\r
+                                    UINT8 elements at Destination. Note that\r
+                                    RETURN_SUCCESS covers the case when\r
+                                    DestinationSize is zero on input, and\r
+                                    Source decodes to zero bytes (due to\r
+                                    containing at most ignored whitespace).\r
+\r
+  @retval RETURN_BUFFER_TOO_SMALL   The input value of DestinationSize is not\r
+                                    large enough for decoding SourceSize CHAR8\r
+                                    elements at Source. The required number of\r
+                                    UINT8 elements has been stored to\r
+                                    DestinationSize.\r
+\r
+  @retval RETURN_INVALID_PARAMETER  DestinationSize is NULL.\r
+\r
+  @retval RETURN_INVALID_PARAMETER  Source is NULL, but SourceSize is not zero.\r
+\r
+  @retval RETURN_INVALID_PARAMETER  Destination is NULL, but DestinationSize is\r
+                                    not zero on input.\r
+\r
+  @retval RETURN_INVALID_PARAMETER  Source is non-NULL, and (Source +\r
+                                    SourceSize) would wrap around MAX_ADDRESS.\r
+\r
+  @retval RETURN_INVALID_PARAMETER  Destination is non-NULL, and (Destination +\r
+                                    DestinationSize) would wrap around\r
+                                    MAX_ADDRESS, as specified on input.\r
+\r
+  @retval RETURN_INVALID_PARAMETER  None of Source and Destination are NULL,\r
+                                    and CHAR8[SourceSize] at Source overlaps\r
+                                    UINT8[DestinationSize] at Destination, as\r
+                                    specified on input.\r
+\r
+  @retval RETURN_INVALID_PARAMETER  Invalid CHAR8 element encountered in\r
+                                    Source.\r
+**/\r
 RETURN_STATUS\r
 EFIAPI\r
 Base64Decode (\r
-  IN  CONST CHAR8  *Source,\r
-  IN        UINTN   SourceLength,\r
-  OUT       UINT8  *Destination   OPTIONAL,\r
-  IN OUT    UINTN  *DestinationSize\r
+  IN     CONST CHAR8 *Source          OPTIONAL,\r
+  IN     UINTN       SourceSize,\r
+  OUT    UINT8       *Destination     OPTIONAL,\r
+  IN OUT UINTN       *DestinationSize\r
   )\r
 {\r
-\r
-  UINT32   Value;\r
-  CHAR8    Chr;\r
-  INTN     BufferSize;\r
-  UINTN    SourceIndex;\r
-  UINTN    DestinationIndex;\r
-  UINTN    Index;\r
-  UINTN    ActualSourceLength;\r
-\r
-  //\r
-  // Check pointers are not NULL\r
-  //\r
-  if ((Source == NULL) || (DestinationSize == NULL)) {\r
-    return RETURN_INVALID_PARAMETER;\r
-  }\r
-\r
-  //\r
-  // Check if SourceLength or  DestinationSize is valid\r
-  //\r
-  if ((SourceLength >= (MAX_ADDRESS - (UINTN)Source)) || (*DestinationSize >= (MAX_ADDRESS - (UINTN)Destination))){\r
-    return RETURN_INVALID_PARAMETER;\r
-  }\r
-\r
-  ActualSourceLength = 0;\r
-  BufferSize = 0;\r
-\r
-  //\r
-  // Determine the actual number of valid characters in the string.\r
-  // All invalid characters except selected white space characters,\r
-  // will cause the Base64 string to be rejected. White space to allow\r
-  // properly formatted XML will be ignored.\r
-  //\r
-  // See section 3.3 of RFC 4648.\r
-  //\r
-  for (SourceIndex = 0; SourceIndex < SourceLength; SourceIndex++) {\r
-\r
-    //\r
-    // '=' is part of the quantum\r
-    //\r
-    if (Source[SourceIndex] == '=') {\r
-      ActualSourceLength++;\r
-      BufferSize--;\r
-\r
-      //\r
-      // Only two '=' characters can be valid.\r
-      //\r
-      if (BufferSize < -2) {\r
-        return RETURN_INVALID_PARAMETER;\r
-      }\r
-    }\r
-    else {\r
-      Chr = Source[SourceIndex];\r
-      if (BAD_V != DecodingTable[(UINT8) Chr]) {\r
-\r
-        //\r
-        // The '=' characters are only valid at the end, so any\r
-        // valid character after an '=', will be flagged as an error.\r
-        //\r
-        if (BufferSize < 0) {\r
-          return RETURN_INVALID_PARAMETER;\r
-        }\r
-          ActualSourceLength++;\r
-      }\r
-        else {\r
-\r
-        //\r
-        // The reset of the decoder will ignore all invalid characters allowed here.\r
-        // Ignoring selected white space is useful.  In this case, the decoder will\r
-        // ignore ' ', '\t', '\n', and '\r'.\r
-        //\r
-        if ((Chr != ' ') &&(Chr != '\t') &&(Chr != '\n') &&(Chr != '\r')) {\r
-          return RETURN_INVALID_PARAMETER;\r
-        }\r
-      }\r
-    }\r
-  }\r
-\r
-  //\r
-  // The Base64 character string must be a multiple of 4 character quantums.\r
-  //\r
-  if (ActualSourceLength % 4 != 0) {\r
-    return RETURN_INVALID_PARAMETER;\r
-  }\r
-\r
-  BufferSize += ActualSourceLength / 4 * 3;\r
-    if (BufferSize < 0) {\r
-      return RETURN_INVALID_PARAMETER;\r
-  }\r
-\r
-  //\r
-  // BufferSize is >= 0\r
-  //\r
-  if ((Destination == NULL) || (*DestinationSize < (UINTN) BufferSize)) {\r
-    *DestinationSize = BufferSize;\r
-    return RETURN_BUFFER_TOO_SMALL;\r
-  }\r
-\r
-  //\r
-  // If no decodable characters, return a size of zero. RFC 4686 test vector 1.\r
-  //\r
-  if (ActualSourceLength == 0) {\r
-    *DestinationSize = 0;\r
-    return RETURN_SUCCESS;\r
-  }\r
-\r
-  //\r
-  // Input data is verified to be a multiple of 4 valid charcters.  Process four\r
-  // characters at a time. Uncounted (ie. invalid)  characters will be ignored.\r
-  //\r
-  for (SourceIndex = 0, DestinationIndex = 0; (SourceIndex < SourceLength) && (DestinationIndex < *DestinationSize); ) {\r
-    Value = 0;\r
-\r
-    //\r
-    // Get 24 bits of data from 4 input characters, each character representing 6 bits\r
-    //\r
-    for (Index = 0; Index < 4; Index++) {\r
-      do {\r
-      Chr = DecodingTable[(UINT8) Source[SourceIndex++]];\r
-      } while (Chr == BAD_V);\r
-      Value <<= 6;\r
-      Value |= (UINT32)Chr;\r
-    }\r
-\r
-    //\r
-    // Store 3 bytes of binary data (24 bits)\r
-    //\r
-    *Destination++ = (UINT8) (Value >> 16);\r
-    DestinationIndex++;\r
-\r
-    //\r
-    // Due to the '=' special cases for the two bytes at the end,\r
-    // we have to check the length and not store the padding data\r
-    //\r
-    if (DestinationIndex++ < *DestinationSize) {\r
-      *Destination++ = (UINT8) (Value >>  8);\r
-    }\r
-    if (DestinationIndex++ < *DestinationSize) {\r
-      *Destination++ = (UINT8) Value;\r
-    }\r
-  }\r
-\r
-  return RETURN_SUCCESS;\r
+  ASSERT (FALSE);\r
+  return RETURN_INVALID_PARAMETER;\r
 }\r
 \r
 /**\r