]> git.proxmox.com Git - mirror_edk2.git/commitdiff
MdePkg/BaseLib: Add Base64Encode() and Base64Decode()
authorMike Turner <miketur@microsoft.com>
Fri, 28 Dec 2018 08:00:02 +0000 (16:00 +0800)
committerLiming Gao <liming.gao@intel.com>
Sat, 2 Feb 2019 13:41:11 +0000 (21:41 +0800)
Introduce public functions Base64Encode and Base64Decode.
https://bugzilla.tianocore.org/show_bug.cgi?id=1370

v2:1.Remove some white space.
   2.Add unit test with test vectors in RFC 4648.
     https://github.com/shenglei10/edk2/tree/encode_test
     https://github.com/shenglei10/edk2/tree/decode_test

v3:1.Align white space.
   2.Update comments of Base64Encode and Base64Decode.
   3.Change the use of macro RETURN_DEVICE_ERROR to
     RETURN_INVALID_PARAMETER in string.c.

v4:Change parameters' names.

v5:1.Update usage of variables.
   2.Remove debug message in Base64Decode().

Cc: Michael D Kinney <michael.d.kinney@intel.com>
Cc: Liming Gao <liming.gao@intel.com>
Contributed-under: TianoCore Contribution Agreement 1.1
Signed-off-by: Shenglei Zhang <shenglei.zhang@intel.com>
Reviewed-by: Ray Ni <ray.ni@intel.com>
Reviewed-by: Liming Gao <liming.gao@intel.com>
MdePkg/Include/Library/BaseLib.h
MdePkg/Library/BaseLib/String.c

index c58e4b089d9674a176bd798dfbb1f257da49c9e8..9c42f82a7d91366304eda8fb2c8a567bb824980a 100644 (file)
@@ -2760,6 +2760,62 @@ AsciiCharToUpper (
   IN      CHAR8                     Chr\r
   );\r
 \r
+/**\r
+  Convert binary data to a Base64 encoded ascii string based on RFC4648.\r
+\r
+  Produce a Null-terminated Ascii string in the output buffer specified by Destination and DestinationSize.\r
+  The Ascii string is produced by converting the data string specified by Source and SourceLength.\r
+\r
+  @param Source           Input UINT8 data\r
+  @param SourceLength     Number of UINT8 bytes of data\r
+  @param Destination      Pointer to output string buffer\r
+  @param DestinationSize  Size of ascii buffer. Set to 0 to get the size needed.\r
+                          Caller is responsible for passing in buffer of DestinationSize\r
+\r
+  @retval RETURN_SUCCESS             When ascii 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_BUFFER_TOO_SMALL    If SourceLength is 0 and DestinationSize is <1.\r
+  @retval RETURN_BUFFER_TOO_SMALL    If Destination is NULL or DestinationSize is smaller than required buffersize.\r
+\r
+**/\r
+RETURN_STATUS\r
+EFIAPI\r
+Base64Encode (\r
+  IN  CONST UINT8  *Source,\r
+  IN        UINTN   SourceLength,\r
+  OUT       CHAR8  *Destination  OPTIONAL,\r
+  IN OUT    UINTN  *DestinationSize\r
+  );\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
+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
+  );\r
+\r
 /**\r
   Converts an 8-bit value to an 8-bit BCD value.\r
 \r
index 467d91069d9d32b5657c58e45470307b65dcba67..53ff730e9e9ffde492a6d9336c31995418812d4c 100644 (file)
@@ -1763,6 +1763,337 @@ 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
+  Produce a Null-terminated Ascii string in the output buffer specified by Destination and DestinationSize.\r
+  The Ascii string is produced by converting the data string specified by Source and SourceLength.\r
+\r
+  @param Source            Input UINT8 data\r
+  @param SourceLength      Number of UINT8 bytes of data\r
+  @param Destination       Pointer to output string buffer\r
+  @param DestinationSize   Size of ascii buffer. Set to 0 to get the size needed.\r
+                           Caller is responsible for passing in buffer of DestinationSize\r
+\r
+  @retval RETURN_SUCCESS             When ascii 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_BUFFER_TOO_SMALL    If SourceLength is 0 and DestinationSize is <1.\r
+  @retval RETURN_BUFFER_TOO_SMALL    If Destination is NULL or DestinationSize is smaller than required buffersize.\r
+\r
+**/\r
+RETURN_STATUS\r
+EFIAPI\r
+Base64Encode (\r
+  IN  CONST UINT8  *Source,\r
+  IN        UINTN   SourceLength,\r
+  OUT       CHAR8  *Destination   OPTIONAL,\r
+  IN OUT    UINTN  *DestinationSize\r
+  )\r
+{\r
+\r
+  UINTN          RequiredSize;\r
+  UINTN          Left;\r
+\r
+  //\r
+  // Check pointers, and SourceLength is valid\r
+  //\r
+  if ((Source == NULL) || (DestinationSize == NULL)) {\r
+    return RETURN_INVALID_PARAMETER;\r
+  }\r
+\r
+  //\r
+  // Allow for RFC 4648 test vector 1\r
+  //\r
+  if (SourceLength == 0) {\r
+    if (*DestinationSize < 1) {\r
+      *DestinationSize = 1;\r
+      return RETURN_BUFFER_TOO_SMALL;\r
+    }\r
+    *DestinationSize = 1;\r
+    *Destination = '\0';\r
+    return RETURN_SUCCESS;\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
+  //\r
+  // 4 ascii per 3 bytes + NULL\r
+  //\r
+  RequiredSize = ((SourceLength + 2) / 3) * 4 + 1;\r
+  if ((Destination == NULL) || *DestinationSize < RequiredSize) {\r
+    *DestinationSize = RequiredSize;\r
+    return RETURN_BUFFER_TOO_SMALL;\r
+  }\r
+\r
+  Left = SourceLength;\r
+\r
+  //\r
+  // Encode 24 bits (three bytes) into 4 ascii characters\r
+  //\r
+  while (Left >= 3) {\r
+\r
+    *Destination++ = EncodingTable[( Source[0] & 0xfc) >> 2 ];\r
+    *Destination++ = EncodingTable[((Source[0] & 0x03) << 4) + ((Source[1] & 0xf0) >> 4)];\r
+    *Destination++ = EncodingTable[((Source[1] & 0x0f) << 2) + ((Source[2] & 0xc0) >> 6)];\r
+    *Destination++ = EncodingTable[( Source[2] & 0x3f)];\r
+    Left -= 3;\r
+    Source += 3;\r
+  }\r
+\r
+  //\r
+  // Handle the remainder, and add padding '=' characters as necessary.\r
+  //\r
+  switch (Left) {\r
+    case 0:\r
+\r
+      //\r
+      // No bytes Left, done.\r
+      //\r
+      break;\r
+    case 1:\r
+\r
+      //\r
+      // One more data byte, two pad characters\r
+      //\r
+      *Destination++ = EncodingTable[( Source[0] & 0xfc) >> 2];\r
+      *Destination++ = EncodingTable[((Source[0] & 0x03) << 4)];\r
+      *Destination++ = '=';\r
+      *Destination++ = '=';\r
+      break;\r
+    case 2:\r
+\r
+      //\r
+      // Two more data bytes, and one pad character\r
+      //\r
+      *Destination++ = EncodingTable[( Source[0] & 0xfc) >> 2];\r
+      *Destination++ = EncodingTable[((Source[0] & 0x03) << 4) + ((Source[1] & 0xf0) >> 4)];\r
+      *Destination++ = EncodingTable[((Source[1] & 0x0f) << 2)];\r
+      *Destination++ = '=';\r
+      break;\r
+    }\r
+  //\r
+  // Add terminating NULL\r
+  //\r
+  *Destination = '\0';\r
+  return RETURN_SUCCESS;\r
+}\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
+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
+  )\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 |= 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
+}\r
+\r
 /**\r
   Converts an 8-bit value to an 8-bit BCD value.\r
 \r