* [PATCH v4] MdePkg/BaseLib: Add Base64Encode() and Base64Decode()
@ 2019-01-30 6:28 Shenglei Zhang
2019-01-30 9:49 ` Ni, Ruiyu
0 siblings, 1 reply; 3+ messages in thread
From: Shenglei Zhang @ 2019-01-30 6:28 UTC (permalink / raw)
To: edk2-devel; +Cc: Mike Turner, Michael D Kinney, Liming Gao
From: Mike Turner <miketur@microsoft.com>
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.
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>
---
MdePkg/Include/Library/BaseLib.h | 56 +++++
MdePkg/Library/BaseLib/String.c | 344 +++++++++++++++++++++++++++++++
2 files changed, 400 insertions(+)
diff --git a/MdePkg/Include/Library/BaseLib.h b/MdePkg/Include/Library/BaseLib.h
index 1eb842384e..03173def58 100644
--- a/MdePkg/Include/Library/BaseLib.h
+++ b/MdePkg/Include/Library/BaseLib.h
@@ -2720,6 +2720,62 @@ AsciiStrnToUnicodeStrS (
OUT UINTN *DestinationLength
);
+/**
+ Convert binary data to a Base64 encoded ascii string based on RFC4648.
+
+ Produce a Null-terminated Ascii string in the output buffer specified by Destination and DestinationSize.
+ The Ascii string is produced by converting the data string specified by Source and SourceLength.
+
+ @param Source Input UINT8 data
+ @param SourceLength Number of UINT8 bytes of data
+ @param Destination Pointer to output string buffer
+ @param DestinationSize Size of ascii buffer. Set to 0 to get the size needed.
+ Caller is responsible for passing in buffer of DestinationSize
+
+ @retval RETURN_SUCCESS When ascii buffer is filled in.
+ @retval RETURN_INVALID_PARAMETER If Source is NULL or DestinationSize is NULL.
+ @retval RETURN_INVALID_PARAMETER If SourceLength or DestinationSize is bigger than (MAX_ADDRESS - (UINTN)Destination).
+ @retval RETURN_BUFFER_TOO_SMALL If SourceLength is 0 and DestinationSize is <1.
+ @retval RETURN_BUFFER_TOO_SMALL If Destination is NULL or DestinationSize is smaller than required buffersize.
+
+**/
+RETURN_STATUS
+EFIAPI
+Base64Encode (
+ IN CONST UINT8 *Source,
+ IN UINTN SourceLength,
+ OUT CHAR8 *Destination OPTIONAL,
+ IN OUT UINTN *DestinationSize
+ );
+
+/**
+ Convert Base64 ascii string to binary data based on RFC4648.
+
+ Produce Null-terminated binary data in the output buffer specified by Destination and DestinationSize.
+ The binary data is produced by converting the Base64 ascii string specified by Source and SourceLength.
+
+ @param Source Input ASCII characters
+ @param SourceLength Number of ASCII characters
+ @param Destination Pointer to output buffer
+ @param DestinationSize Caller is responsible for passing in buffer of at least DestinationSize.
+ Set 0 to get the size needed. Set to bytes stored on return.
+
+ @retval RETURN_SUCCESS When binary buffer is filled in.
+ @retval RETURN_INVALID_PARAMETER If Source is NULL or DestinationSize is NULL.
+ @retval RETURN_INVALID_PARAMETER If SourceLength or DestinationSize is bigger than (MAX_ADDRESS -(UINTN)Destination ).
+ @retval RETURN_INVALID_PARAMETER If there is any invalid character in input stream.
+ @retval RETURN_BUFFER_TOO_SMALL If buffer length is smaller than required buffer size.
+
+ **/
+RETURN_STATUS
+EFIAPI
+Base64Decode (
+ IN CONST CHAR8 *Source,
+ IN UINTN SourceLength,
+ OUT UINT8 *Destination OPTIONAL,
+ IN OUT UINTN *DestinationSize
+ );
+
/**
Converts an 8-bit value to an 8-bit BCD value.
diff --git a/MdePkg/Library/BaseLib/String.c b/MdePkg/Library/BaseLib/String.c
index e6df12797d..e4aa5a57e4 100644
--- a/MdePkg/Library/BaseLib/String.c
+++ b/MdePkg/Library/BaseLib/String.c
@@ -1763,6 +1763,350 @@ AsciiStrToUnicodeStr (
#endif
+//
+// The basis for Base64 encoding is RFC 4686 https://tools.ietf.org/html/rfc4648
+//
+// RFC 4686 has a number of MAY and SHOULD cases. This implementation chooses
+// the more restrictive versions for security concerns (see RFC 4686 section 3.3).
+//
+// A invalid character, if encountered during the decode operation, causes the data
+// to be rejected. In addition, the '=' padding character is only allowed at the end
+// of the Base64 encoded string.
+//
+#define BAD_V 99
+
+STATIC CHAR8 Encoding_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz"
+ "0123456789+/";
+
+STATIC UINT8 Decoding_table[] = {
+ //
+ // Valid characters ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/
+ // Also, set '=' as a zero for decoding
+ // 0 , 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f
+ 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
+ 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
+ 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
+ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, BAD_V, BAD_V, BAD_V, 0, BAD_V, BAD_V, // 30
+ BAD_V, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, // 40
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, BAD_V, BAD_V, BAD_V, BAD_V, BAD_V, // 50
+ BAD_V, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, // 60
+ 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, BAD_V, BAD_V, BAD_V, BAD_V, BAD_V, // 70
+ 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
+ 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
+ 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
+ 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
+ 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
+ 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
+ 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
+ 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
+};
+
+/**
+ Convert binary data to a Base64 encoded ascii string based on RFC4648.
+
+ Produce a Null-terminated Ascii string in the output buffer specified by Destination and DestinationSize.
+ The Ascii string is produced by converting the data string specified by Source and SourceLength.
+
+ @param Source Input UINT8 data
+ @param SourceLength Number of UINT8 bytes of data
+ @param Destination Pointer to output string buffer
+ @param DestinationSize Size of ascii buffer. Set to 0 to get the size needed.
+ Caller is responsible for passing in buffer of DestinationSize
+
+ @retval RETURN_SUCCESS When ascii buffer is filled in.
+ @retval RETURN_INVALID_PARAMETER If Source is NULL or DestinationSize is NULL.
+ @retval RETURN_INVALID_PARAMETER If SourceLength or DestinationSize is bigger than (MAX_ADDRESS - (UINTN)Destination).
+ @retval RETURN_BUFFER_TOO_SMALL If SourceLength is 0 and DestinationSize is <1.
+ @retval RETURN_BUFFER_TOO_SMALL If Destination is NULL or DestinationSize is smaller than required buffersize.
+
+**/
+RETURN_STATUS
+EFIAPI
+Base64Encode (
+ IN CONST UINT8 *Source,
+ IN UINTN SourceLength,
+ OUT CHAR8 *Destination OPTIONAL,
+ IN OUT UINTN *DestinationSize
+ )
+{
+
+ UINTN RequiredSize;
+ UINTN Left;
+ CONST UINT8 *Inptr;
+ CHAR8 *Outptr;
+
+ //
+ // Check pointers, and SourceLength is valid
+ //
+ if ((Source == NULL) || (DestinationSize == NULL)) {
+ return RETURN_INVALID_PARAMETER;
+ }
+
+ //
+ // Allow for RFC 4648 test vector 1
+ //
+ if (SourceLength == 0) {
+ if (*DestinationSize < 1) {
+ *DestinationSize = 1;
+ return RETURN_BUFFER_TOO_SMALL;
+ }
+ *DestinationSize = 1;
+ *Destination = '\0';
+ return RETURN_SUCCESS;
+ }
+
+ //
+ // Check if SourceLength or DestinationSize is valid
+ //
+ if ((SourceLength >= (MAX_ADDRESS - (UINTN)Source)) || (*DestinationSize >= (MAX_ADDRESS - (UINTN)Destination))){
+ return RETURN_INVALID_PARAMETER;
+ }
+
+ //
+ // 4 ascii per 3 bytes + NULL
+ //
+ RequiredSize = ((SourceLength + 2) / 3) * 4 + 1;
+ if ((Destination == NULL) || *DestinationSize < RequiredSize) {
+ *DestinationSize = RequiredSize;
+ return RETURN_BUFFER_TOO_SMALL;
+ }
+
+ Left = SourceLength;
+ Outptr = Destination;
+ Inptr = Source;
+
+ //
+ // Encode 24 bits (three bytes) into 4 ascii characters
+ //
+ while (Left >= 3) {
+
+ *Outptr++ = Encoding_table[( Inptr[0] & 0xfc) >> 2 ];
+ *Outptr++ = Encoding_table[((Inptr[0] & 0x03) << 4) + ((Inptr[1] & 0xf0) >> 4)];
+ *Outptr++ = Encoding_table[((Inptr[1] & 0x0f) << 2) + ((Inptr[2] & 0xc0) >> 6)];
+ *Outptr++ = Encoding_table[( Inptr[2] & 0x3f)];
+ Left -= 3;
+ Inptr += 3;
+ }
+
+ //
+ // Handle the remainder, and add padding '=' characters as necessary.
+ //
+ switch (Left) {
+ case 0:
+
+ //
+ // No bytes Left, done.
+ //
+ break;
+ case 1:
+
+ //
+ // One more data byte, two pad characters
+ //
+ *Outptr++ = Encoding_table[( Inptr[0] & 0xfc) >> 2];
+ *Outptr++ = Encoding_table[((Inptr[0] & 0x03) << 4)];
+ *Outptr++ = '=';
+ *Outptr++ = '=';
+ break;
+ case 2:
+
+ //
+ // Two more data bytes, and one pad character
+ //
+ *Outptr++ = Encoding_table[( Inptr[0] & 0xfc) >> 2];
+ *Outptr++ = Encoding_table[((Inptr[0] & 0x03) << 4) + ((Inptr[1] & 0xf0) >> 4)];
+ *Outptr++ = Encoding_table[((Inptr[1] & 0x0f) << 2)];
+ *Outptr++ = '=';
+ break;
+ }
+ //
+ // Add terminating NULL
+ //
+ *Outptr = '\0';
+ return RETURN_SUCCESS;
+}
+
+/**
+ Convert Base64 ascii string to binary data based on RFC4648.
+
+ Produce Null-terminated binary data in the output buffer specified by Destination and DestinationSize.
+ The binary data is produced by converting the Base64 ascii string specified by Source and SourceLength.
+
+ @param Source Input ASCII characters
+ @param SourceLength Number of ASCII characters
+ @param Destination Pointer to output buffer
+ @param DestinationSize Caller is responsible for passing in buffer of at least DestinationSize.
+ Set 0 to get the size needed. Set to bytes stored on return.
+
+ @retval RETURN_SUCCESS When binary buffer is filled in.
+ @retval RETURN_INVALID_PARAMETER If Source is NULL or DestinationSize is NULL.
+ @retval RETURN_INVALID_PARAMETER If SourceLength or DestinationSize is bigger than (MAX_ADDRESS -(UINTN)Destination ).
+ @retval RETURN_INVALID_PARAMETER If there is any invalid character in input stream.
+ @retval RETURN_BUFFER_TOO_SMALL If buffer length is smaller than required buffer size.
+ **/
+RETURN_STATUS
+EFIAPI
+Base64Decode (
+ IN CONST CHAR8 *Source,
+ IN UINTN SourceLength,
+ OUT UINT8 *Destination OPTIONAL,
+ IN OUT UINTN *DestinationSize
+ )
+{
+
+ UINT8 *BinData;
+ UINT32 Value;
+ CHAR8 Chr;
+ INTN BufferSize;
+ UINTN Indx;
+ UINTN Ondx;
+ UINTN Icnt;
+ UINTN ActualSourceLength;
+
+ //
+ // Check pointers are not NULL
+ //
+ if ((Source == NULL) || (DestinationSize == NULL)) {
+ DEBUG((DEBUG_ERROR, "Source=%p, DestinationSize=%\n", Source, DestinationSize));
+ return RETURN_INVALID_PARAMETER;
+ }
+
+ //
+ // Check if SourceLength or DestinationSize is valid
+ //
+ if ((SourceLength >= (MAX_ADDRESS - (UINTN)Source)) || (*DestinationSize >= (MAX_ADDRESS - (UINTN)Destination))){
+ return RETURN_INVALID_PARAMETER;
+ }
+
+ ActualSourceLength = 0;
+ BufferSize = 0;
+
+ //
+ // Determine the actual number of valid characters in the string.
+ // All invalid characters except selected white space characters,
+ // will cause the Base64 string to be rejected. White space to allow
+ // properly formatted XML will be ignored.
+ //
+ // See section 3.3 of RFC 4648.
+ //
+ for (Indx = 0; Indx < SourceLength; Indx++) {
+
+ //
+ // '=' is part of the quantum
+ //
+ if (Source[Indx] == '=') {
+ ActualSourceLength++;
+ BufferSize--;
+
+ //
+ // Only two '=' characters can be valid.
+ //
+ if (BufferSize < -2) {
+ DEBUG((DEBUG_ERROR, "Source=%p, Invalid = at %d\n", Source, Indx));
+ return RETURN_INVALID_PARAMETER;
+ }
+ }
+ else {
+ Chr = Source[Indx];
+ if (BAD_V != Decoding_table[(UINT8) Chr]) {
+
+ //
+ // The '=' characters are only valid at the end, so any
+ // valid character after an '=', will be flagged as an error.
+ //
+ if (BufferSize < 0) {
+ DEBUG((DEBUG_ERROR, "Source=%p, Invalid character %c at %d\n", Source, Chr, Indx));
+ return RETURN_INVALID_PARAMETER;
+ }
+ ActualSourceLength++;
+ }
+ else {
+
+ //
+ // The reset of the decoder will ignore all invalid characters allowed here.
+ // Ignoring selected white space is useful. In this case, the decoder will
+ // ignore ' ', '\t', '\n', and '\r'.
+ //
+ if ((Chr != ' ') &&(Chr != '\t') &&(Chr != '\n') &&(Chr != '\r')) {
+ DEBUG((DEBUG_ERROR, "Source=%p, Invalid character %c at %d\n", Source, Chr, Indx));
+ return RETURN_INVALID_PARAMETER;
+ }
+ }
+ }
+ }
+
+ //
+ // The Base64 character string must be a multiple of 4 character quantums.
+ //
+ if (ActualSourceLength % 4 != 0) {
+ DEBUG((DEBUG_ERROR,"Source=%p, Invalid data length %d\n", Source, ActualSourceLength));
+ return RETURN_INVALID_PARAMETER;
+ }
+
+ BufferSize += ActualSourceLength / 4 * 3;
+ if (BufferSize < 0) {
+ DEBUG((DEBUG_ERROR,"BufferSize(%d) is wrong because of bad input.\n", BufferSize));
+ return RETURN_INVALID_PARAMETER;
+ }
+
+ //
+ // BufferSize is >= 0
+ //
+ if ((Destination == NULL) || (*DestinationSize < (UINTN) BufferSize)) {
+ *DestinationSize = BufferSize;
+ return RETURN_BUFFER_TOO_SMALL;
+ }
+
+ //
+ // If no decodable characters, return a size of zero. RFC 4686 test vector 1.
+ //
+ if (ActualSourceLength == 0) {
+ *DestinationSize = 0;
+ return RETURN_SUCCESS;
+ }
+
+ BinData = Destination;
+
+ //
+ // Input data is verified to be a multiple of 4 valid charcters. Process four
+ // characters at a time. Uncounted (ie. invalid) characters will be ignored.
+ //
+ for (Indx = 0, Ondx = 0; (Indx < SourceLength) && (Ondx < *DestinationSize); ) {
+ Value = 0;
+
+ //
+ // Get 24 bits of data from 4 input characters, each character representing 6 bits
+ //
+ for (Icnt = 0; Icnt < 4; Icnt++) {
+ do {
+ Chr = Decoding_table[(UINT8) Source[Indx++]];
+ } while (Chr == BAD_V);
+ Value <<= 6;
+ Value |= Chr;
+ }
+
+ //
+ // Store 3 bytes of binary data (24 bits)
+ //
+ *BinData++ = (UINT8) (Value >> 16);
+ Ondx++;
+
+ //
+ // Due to the '=' special cases for the two bytes at the end,
+ // we have to check the length and not store the padding data
+ //
+ if (Ondx++ < *DestinationSize) {
+ *BinData++ = (UINT8) (Value >> 8);
+ }
+ if (Ondx++ < *DestinationSize) {
+ *BinData++ = (UINT8) Value;
+ }
+ }
+
+ return RETURN_SUCCESS;
+}
+
/**
Converts an 8-bit value to an 8-bit BCD value.
--
2.18.0.windows.1
^ permalink raw reply related [flat|nested] 3+ messages in thread
* Re: [PATCH v4] MdePkg/BaseLib: Add Base64Encode() and Base64Decode()
2019-01-30 6:28 [PATCH v4] MdePkg/BaseLib: Add Base64Encode() and Base64Decode() Shenglei Zhang
@ 2019-01-30 9:49 ` Ni, Ruiyu
2019-01-31 7:08 ` Zhang, Shenglei
0 siblings, 1 reply; 3+ messages in thread
From: Ni, Ruiyu @ 2019-01-30 9:49 UTC (permalink / raw)
To: Shenglei Zhang, edk2-devel; +Cc: Michael D Kinney, Mike Turner, Liming Gao
Comments in below.
On 1/30/2019 2:28 PM, Shenglei Zhang wrote:
> From: Mike Turner <miketur@microsoft.com>
>
> 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.
>
> 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>
> ---
> MdePkg/Include/Library/BaseLib.h | 56 +++++
> MdePkg/Library/BaseLib/String.c | 344 +++++++++++++++++++++++++++++++
> 2 files changed, 400 insertions(+)
>
> diff --git a/MdePkg/Include/Library/BaseLib.h b/MdePkg/Include/Library/BaseLib.h
> index 1eb842384e..03173def58 100644
> --- a/MdePkg/Include/Library/BaseLib.h
> +++ b/MdePkg/Include/Library/BaseLib.h
> @@ -2720,6 +2720,62 @@ AsciiStrnToUnicodeStrS (
> OUT UINTN *DestinationLength
> );
>
> +/**
> + Convert binary data to a Base64 encoded ascii string based on RFC4648.
> +
> + Produce a Null-terminated Ascii string in the output buffer specified by Destination and DestinationSize.
> + The Ascii string is produced by converting the data string specified by Source and SourceLength.
> +
> + @param Source Input UINT8 data
> + @param SourceLength Number of UINT8 bytes of data
> + @param Destination Pointer to output string buffer
> + @param DestinationSize Size of ascii buffer. Set to 0 to get the size needed.
> + Caller is responsible for passing in buffer of DestinationSize
> +
> + @retval RETURN_SUCCESS When ascii buffer is filled in.
> + @retval RETURN_INVALID_PARAMETER If Source is NULL or DestinationSize is NULL.
> + @retval RETURN_INVALID_PARAMETER If SourceLength or DestinationSize is bigger than (MAX_ADDRESS - (UINTN)Destination).
> + @retval RETURN_BUFFER_TOO_SMALL If SourceLength is 0 and DestinationSize is <1.
> + @retval RETURN_BUFFER_TOO_SMALL If Destination is NULL or DestinationSize is smaller than required buffersize.
> +
> +**/
> +RETURN_STATUS
> +EFIAPI
> +Base64Encode (
> + IN CONST UINT8 *Source,
> + IN UINTN SourceLength,
> + OUT CHAR8 *Destination OPTIONAL,
> + IN OUT UINTN *DestinationSize
> + );
> +
> +/**
> + Convert Base64 ascii string to binary data based on RFC4648.
> +
> + Produce Null-terminated binary data in the output buffer specified by Destination and DestinationSize.
> + The binary data is produced by converting the Base64 ascii string specified by Source and SourceLength.
> +
> + @param Source Input ASCII characters
> + @param SourceLength Number of ASCII characters
> + @param Destination Pointer to output buffer
> + @param DestinationSize Caller is responsible for passing in buffer of at least DestinationSize.
> + Set 0 to get the size needed. Set to bytes stored on return.
> +
> + @retval RETURN_SUCCESS When binary buffer is filled in.
> + @retval RETURN_INVALID_PARAMETER If Source is NULL or DestinationSize is NULL.
> + @retval RETURN_INVALID_PARAMETER If SourceLength or DestinationSize is bigger than (MAX_ADDRESS -(UINTN)Destination ).
> + @retval RETURN_INVALID_PARAMETER If there is any invalid character in input stream.
> + @retval RETURN_BUFFER_TOO_SMALL If buffer length is smaller than required buffer size.
> +
> + **/
> +RETURN_STATUS
> +EFIAPI
> +Base64Decode (
> + IN CONST CHAR8 *Source,
> + IN UINTN SourceLength,
> + OUT UINT8 *Destination OPTIONAL,
> + IN OUT UINTN *DestinationSize
> + );
> +
> /**
> Converts an 8-bit value to an 8-bit BCD value.
>
> diff --git a/MdePkg/Library/BaseLib/String.c b/MdePkg/Library/BaseLib/String.c
> index e6df12797d..e4aa5a57e4 100644
> --- a/MdePkg/Library/BaseLib/String.c
> +++ b/MdePkg/Library/BaseLib/String.c
> @@ -1763,6 +1763,350 @@ AsciiStrToUnicodeStr (
>
> #endif
>
> +//
> +// The basis for Base64 encoding is RFC 4686 https://tools.ietf.org/html/rfc4648
> +//
> +// RFC 4686 has a number of MAY and SHOULD cases. This implementation chooses
> +// the more restrictive versions for security concerns (see RFC 4686 section 3.3).
> +//
> +// A invalid character, if encountered during the decode operation, causes the data
> +// to be rejected. In addition, the '=' padding character is only allowed at the end
> +// of the Base64 encoded string.
> +//
> +#define BAD_V 99
> +
> +STATIC CHAR8 Encoding_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
> + "abcdefghijklmnopqrstuvwxyz"
> + "0123456789+/";
> +
> +STATIC UINT8 Decoding_table[] = {
1. Can the Encoding_table/Decoding_table pass ECC check?
> + //
> + // Valid characters ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/
> + // Also, set '=' as a zero for decoding
> + // 0 , 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f
> + 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
> + 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
> + 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
> + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, BAD_V, BAD_V, BAD_V, 0, BAD_V, BAD_V, // 30
> + BAD_V, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, // 40
> + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, BAD_V, BAD_V, BAD_V, BAD_V, BAD_V, // 50
> + BAD_V, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, // 60
> + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, BAD_V, BAD_V, BAD_V, BAD_V, BAD_V, // 70
> + 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
> + 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
> + 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
> + 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
> + 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
> + 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
> + 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
> + 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
> +};
> +
> +/**
> + Convert binary data to a Base64 encoded ascii string based on RFC4648.
> +
> + Produce a Null-terminated Ascii string in the output buffer specified by Destination and DestinationSize.
> + The Ascii string is produced by converting the data string specified by Source and SourceLength.
> +
> + @param Source Input UINT8 data
> + @param SourceLength Number of UINT8 bytes of data
> + @param Destination Pointer to output string buffer
> + @param DestinationSize Size of ascii buffer. Set to 0 to get the size needed.
> + Caller is responsible for passing in buffer of DestinationSize
> +
> + @retval RETURN_SUCCESS When ascii buffer is filled in.
> + @retval RETURN_INVALID_PARAMETER If Source is NULL or DestinationSize is NULL.
> + @retval RETURN_INVALID_PARAMETER If SourceLength or DestinationSize is bigger than (MAX_ADDRESS - (UINTN)Destination).
> + @retval RETURN_BUFFER_TOO_SMALL If SourceLength is 0 and DestinationSize is <1.
> + @retval RETURN_BUFFER_TOO_SMALL If Destination is NULL or DestinationSize is smaller than required buffersize.
> +
> +**/
> +RETURN_STATUS
> +EFIAPI
> +Base64Encode (
> + IN CONST UINT8 *Source,
> + IN UINTN SourceLength,
> + OUT CHAR8 *Destination OPTIONAL,
> + IN OUT UINTN *DestinationSize
> + )
> +{
> +
> + UINTN RequiredSize;
> + UINTN Left;
> + CONST UINT8 *Inptr;
> + CHAR8 *Outptr;
2. Inptr and Outptr might be able to be removed. please check.
> +
> + //
> + // Check pointers, and SourceLength is valid
> + //
> + if ((Source == NULL) || (DestinationSize == NULL)) {
> + return RETURN_INVALID_PARAMETER;
> + }
> +
> + //
> + // Allow for RFC 4648 test vector 1
> + //
> + if (SourceLength == 0) {
> + if (*DestinationSize < 1) {
> + *DestinationSize = 1;
> + return RETURN_BUFFER_TOO_SMALL;
> + }
> + *DestinationSize = 1;
> + *Destination = '\0';
> + return RETURN_SUCCESS;
> + }
> +
> + //
> + // Check if SourceLength or DestinationSize is valid
> + //
> + if ((SourceLength >= (MAX_ADDRESS - (UINTN)Source)) || (*DestinationSize >= (MAX_ADDRESS - (UINTN)Destination))){
> + return RETURN_INVALID_PARAMETER;
> + }
> +
> + //
> + // 4 ascii per 3 bytes + NULL
> + //
> + RequiredSize = ((SourceLength + 2) / 3) * 4 + 1;
> + if ((Destination == NULL) || *DestinationSize < RequiredSize) {
> + *DestinationSize = RequiredSize;
> + return RETURN_BUFFER_TOO_SMALL; > + }
> +
> + Left = SourceLength;
> + Outptr = Destination;
> + Inptr = Source;
> +
> + //
> + // Encode 24 bits (three bytes) into 4 ascii characters
> + //
> + while (Left >= 3) {
> +
> + *Outptr++ = Encoding_table[( Inptr[0] & 0xfc) >> 2 ];
> + *Outptr++ = Encoding_table[((Inptr[0] & 0x03) << 4) + ((Inptr[1] & 0xf0) >> 4)];
> + *Outptr++ = Encoding_table[((Inptr[1] & 0x0f) << 2) + ((Inptr[2] & 0xc0) >> 6)];
> + *Outptr++ = Encoding_table[( Inptr[2] & 0x3f)];
> + Left -= 3;
> + Inptr += 3;
> + }
> +
> + //
> + // Handle the remainder, and add padding '=' characters as necessary.
> + //
> + switch (Left) {
> + case 0:
> +
> + //
> + // No bytes Left, done.
> + //
> + break;
> + case 1:
> +
> + //
> + // One more data byte, two pad characters
> + //
> + *Outptr++ = Encoding_table[( Inptr[0] & 0xfc) >> 2];
> + *Outptr++ = Encoding_table[((Inptr[0] & 0x03) << 4)];
> + *Outptr++ = '=';
> + *Outptr++ = '=';
> + break;
> + case 2:
> +
> + //
> + // Two more data bytes, and one pad character
> + //
> + *Outptr++ = Encoding_table[( Inptr[0] & 0xfc) >> 2];
> + *Outptr++ = Encoding_table[((Inptr[0] & 0x03) << 4) + ((Inptr[1] & 0xf0) >> 4)];
> + *Outptr++ = Encoding_table[((Inptr[1] & 0x0f) << 2)];
> + *Outptr++ = '=';
> + break;
> + }
> + //
> + // Add terminating NULL
> + //
> + *Outptr = '\0';
> + return RETURN_SUCCESS;
> +}
> +
> +/**
> + Convert Base64 ascii string to binary data based on RFC4648.
> +
> + Produce Null-terminated binary data in the output buffer specified by Destination and DestinationSize.
> + The binary data is produced by converting the Base64 ascii string specified by Source and SourceLength.
> +
> + @param Source Input ASCII characters
> + @param SourceLength Number of ASCII characters
> + @param Destination Pointer to output buffer
> + @param DestinationSize Caller is responsible for passing in buffer of at least DestinationSize.
> + Set 0 to get the size needed. Set to bytes stored on return.
> +
> + @retval RETURN_SUCCESS When binary buffer is filled in.
> + @retval RETURN_INVALID_PARAMETER If Source is NULL or DestinationSize is NULL.
> + @retval RETURN_INVALID_PARAMETER If SourceLength or DestinationSize is bigger than (MAX_ADDRESS -(UINTN)Destination ).
> + @retval RETURN_INVALID_PARAMETER If there is any invalid character in input stream.
> + @retval RETURN_BUFFER_TOO_SMALL If buffer length is smaller than required buffer size.
> + **/
> +RETURN_STATUS
> +EFIAPI
> +Base64Decode (
> + IN CONST CHAR8 *Source,
> + IN UINTN SourceLength,
> + OUT UINT8 *Destination OPTIONAL,
> + IN OUT UINTN *DestinationSize
> + )
> +{
> +
> + UINT8 *BinData;
3. Could be removed. see comments below.
> + UINT32 Value;
> + CHAR8 Chr;
> + INTN BufferSize;
> + UINTN Indx;
> + UINTN Ondx; > + UINTN Icnt;
4. Why not just use SourceIndex/DestinationIndex/Index for above 3 local
variables?
> + UINTN ActualSourceLength;
> +
> + //
> + // Check pointers are not NULL
> + //
> + if ((Source == NULL) || (DestinationSize == NULL)) {
> + DEBUG((DEBUG_ERROR, "Source=%p, DestinationSize=%\n", Source, DestinationSize));
5. Please remove this debug message. It's not proper for a BaseLib API
to print debug message.
> + return RETURN_INVALID_PARAMETER;
> + }
> +
> + //
> + // Check if SourceLength or DestinationSize is valid
> + //
> + if ((SourceLength >= (MAX_ADDRESS - (UINTN)Source)) || (*DestinationSize >= (MAX_ADDRESS - (UINTN)Destination))){
> + return RETURN_INVALID_PARAMETER;
> + }
> +
> + ActualSourceLength = 0;
> + BufferSize = 0;
> +
> + //
> + // Determine the actual number of valid characters in the string.
> + // All invalid characters except selected white space characters,
> + // will cause the Base64 string to be rejected. White space to allow
> + // properly formatted XML will be ignored.
> + //
> + // See section 3.3 of RFC 4648.
> + //
> + for (Indx = 0; Indx < SourceLength; Indx++) {
> +
> + //
> + // '=' is part of the quantum
> + //
> + if (Source[Indx] == '=') {
> + ActualSourceLength++;
> + BufferSize--;
> +
> + //
> + // Only two '=' characters can be valid.
> + //
> + if (BufferSize < -2) {
> + DEBUG((DEBUG_ERROR, "Source=%p, Invalid = at %d\n", Source, Indx));
> + return RETURN_INVALID_PARAMETER;
> + }
> + }
> + else {
> + Chr = Source[Indx];
> + if (BAD_V != Decoding_table[(UINT8) Chr]) {
> +
> + //
> + // The '=' characters are only valid at the end, so any
> + // valid character after an '=', will be flagged as an error.
> + //
> + if (BufferSize < 0) {
> + DEBUG((DEBUG_ERROR, "Source=%p, Invalid character %c at %d\n", Source, Chr, Indx));
> + return RETURN_INVALID_PARAMETER;
> + }
> + ActualSourceLength++;
> + }
> + else {
> +
> + //
> + // The reset of the decoder will ignore all invalid characters allowed here.
> + // Ignoring selected white space is useful. In this case, the decoder will
> + // ignore ' ', '\t', '\n', and '\r'.
> + //
> + if ((Chr != ' ') &&(Chr != '\t') &&(Chr != '\n') &&(Chr != '\r')) {
> + DEBUG((DEBUG_ERROR, "Source=%p, Invalid character %c at %d\n", Source, Chr, Indx));
> + return RETURN_INVALID_PARAMETER;
> + }
> + }
> + }
> + }
> +
> + //
> + // The Base64 character string must be a multiple of 4 character quantums.
> + //
> + if (ActualSourceLength % 4 != 0) {
> + DEBUG((DEBUG_ERROR,"Source=%p, Invalid data length %d\n", Source, ActualSourceLength));
> + return RETURN_INVALID_PARAMETER;
> + }
> +
> + BufferSize += ActualSourceLength / 4 * 3;
> + if (BufferSize < 0) {
> + DEBUG((DEBUG_ERROR,"BufferSize(%d) is wrong because of bad input.\n", BufferSize));
> + return RETURN_INVALID_PARAMETER;
> + }
> +
> + //
> + // BufferSize is >= 0
> + //
> + if ((Destination == NULL) || (*DestinationSize < (UINTN) BufferSize)) {
> + *DestinationSize = BufferSize;
> + return RETURN_BUFFER_TOO_SMALL;
> + }
> +
> + //
> + // If no decodable characters, return a size of zero. RFC 4686 test vector 1.
> + //
> + if (ActualSourceLength == 0) {
> + *DestinationSize = 0;
> + return RETURN_SUCCESS;
> + }
> +
> + BinData = Destination;
6. Can you directly use Destination and remove BinData local variable?
I don't find any further usage of Destination in below code.
> +
> + //
> + // Input data is verified to be a multiple of 4 valid charcters. Process four
> + // characters at a time. Uncounted (ie. invalid) characters will be ignored.
> + //
> + for (Indx = 0, Ondx = 0; (Indx < SourceLength) && (Ondx < *DestinationSize); ) {
> + Value = 0;
> +
> + //
> + // Get 24 bits of data from 4 input characters, each character representing 6 bits
> + //
> + for (Icnt = 0; Icnt < 4; Icnt++) {
> + do {
> + Chr = Decoding_table[(UINT8) Source[Indx++]];
> + } while (Chr == BAD_V);
> + Value <<= 6;
> + Value |= Chr;
> + }
> +
> + //
> + // Store 3 bytes of binary data (24 bits)
> + //
> + *BinData++ = (UINT8) (Value >> 16);
> + Ondx++;
> +
> + //
> + // Due to the '=' special cases for the two bytes at the end,
> + // we have to check the length and not store the padding data
> + //
> + if (Ondx++ < *DestinationSize) {
> + *BinData++ = (UINT8) (Value >> 8);
> + }
> + if (Ondx++ < *DestinationSize) {
> + *BinData++ = (UINT8) Value;
> + }
> + }
> +
> + return RETURN_SUCCESS;
> +}
> +
> /**
> Converts an 8-bit value to an 8-bit BCD value.
>
>
--
Thanks,
Ray
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [PATCH v4] MdePkg/BaseLib: Add Base64Encode() and Base64Decode()
2019-01-30 9:49 ` Ni, Ruiyu
@ 2019-01-31 7:08 ` Zhang, Shenglei
0 siblings, 0 replies; 3+ messages in thread
From: Zhang, Shenglei @ 2019-01-31 7:08 UTC (permalink / raw)
To: Ni, Ray, edk2-devel@lists.01.org
Cc: Kinney, Michael D, Mike Turner, Gao, Liming
> -----Original Message-----
> From: Ni, Ray
> Sent: Wednesday, January 30, 2019 5:49 PM
> To: Zhang, Shenglei <shenglei.zhang@intel.com>; edk2-devel@lists.01.org
> Cc: Kinney, Michael D <michael.d.kinney@intel.com>; Mike Turner
> <miketur@microsoft.com>; Gao, Liming <liming.gao@intel.com>
> Subject: Re: [edk2] [PATCH v4] MdePkg/BaseLib: Add Base64Encode() and
> Base64Decode()
>
> Comments in below.
>
> On 1/30/2019 2:28 PM, Shenglei Zhang wrote:
> > From: Mike Turner <miketur@microsoft.com>
> >
> > 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.
> >
> > 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>
> > ---
> > MdePkg/Include/Library/BaseLib.h | 56 +++++
> > MdePkg/Library/BaseLib/String.c | 344
> +++++++++++++++++++++++++++++++
> > 2 files changed, 400 insertions(+)
> >
> > diff --git a/MdePkg/Include/Library/BaseLib.h
> b/MdePkg/Include/Library/BaseLib.h
> > index 1eb842384e..03173def58 100644
> > --- a/MdePkg/Include/Library/BaseLib.h
> > +++ b/MdePkg/Include/Library/BaseLib.h
> > @@ -2720,6 +2720,62 @@ AsciiStrnToUnicodeStrS (
> > OUT UINTN *DestinationLength
> > );
> >
> > +/**
> > + Convert binary data to a Base64 encoded ascii string based on RFC4648.
> > +
> > + Produce a Null-terminated Ascii string in the output buffer specified by
> Destination and DestinationSize.
> > + The Ascii string is produced by converting the data string specified by
> Source and SourceLength.
> > +
> > + @param Source Input UINT8 data
> > + @param SourceLength Number of UINT8 bytes of data
> > + @param Destination Pointer to output string buffer
> > + @param DestinationSize Size of ascii buffer. Set to 0 to get the size
> needed.
> > + Caller is responsible for passing in buffer of DestinationSize
> > +
> > + @retval RETURN_SUCCESS When ascii buffer is filled in.
> > + @retval RETURN_INVALID_PARAMETER If Source is NULL or
> DestinationSize is NULL.
> > + @retval RETURN_INVALID_PARAMETER If SourceLength or
> DestinationSize is bigger than (MAX_ADDRESS - (UINTN)Destination).
> > + @retval RETURN_BUFFER_TOO_SMALL If SourceLength is 0 and
> DestinationSize is <1.
> > + @retval RETURN_BUFFER_TOO_SMALL If Destination is NULL or
> DestinationSize is smaller than required buffersize.
> > +
> > +**/
> > +RETURN_STATUS
> > +EFIAPI
> > +Base64Encode (
> > + IN CONST UINT8 *Source,
> > + IN UINTN SourceLength,
> > + OUT CHAR8 *Destination OPTIONAL,
> > + IN OUT UINTN *DestinationSize
> > + );
> > +
> > +/**
> > + Convert Base64 ascii string to binary data based on RFC4648.
> > +
> > + Produce Null-terminated binary data in the output buffer specified by
> Destination and DestinationSize.
> > + The binary data is produced by converting the Base64 ascii string
> specified by Source and SourceLength.
> > +
> > + @param Source Input ASCII characters
> > + @param SourceLength Number of ASCII characters
> > + @param Destination Pointer to output buffer
> > + @param DestinationSize Caller is responsible for passing in buffer of at
> least DestinationSize.
> > + Set 0 to get the size needed. Set to bytes stored on return.
> > +
> > + @retval RETURN_SUCCESS When binary buffer is filled in.
> > + @retval RETURN_INVALID_PARAMETER If Source is NULL or
> DestinationSize is NULL.
> > + @retval RETURN_INVALID_PARAMETER If SourceLength or
> DestinationSize is bigger than (MAX_ADDRESS -(UINTN)Destination ).
> > + @retval RETURN_INVALID_PARAMETER If there is any invalid character
> in input stream.
> > + @retval RETURN_BUFFER_TOO_SMALL If buffer length is smaller than
> required buffer size.
> > +
> > + **/
> > +RETURN_STATUS
> > +EFIAPI
> > +Base64Decode (
> > + IN CONST CHAR8 *Source,
> > + IN UINTN SourceLength,
> > + OUT UINT8 *Destination OPTIONAL,
> > + IN OUT UINTN *DestinationSize
> > + );
> > +
> > /**
> > Converts an 8-bit value to an 8-bit BCD value.
> >
> > diff --git a/MdePkg/Library/BaseLib/String.c
> b/MdePkg/Library/BaseLib/String.c
> > index e6df12797d..e4aa5a57e4 100644
> > --- a/MdePkg/Library/BaseLib/String.c
> > +++ b/MdePkg/Library/BaseLib/String.c
> > @@ -1763,6 +1763,350 @@ AsciiStrToUnicodeStr (
> >
> > #endif
> >
> > +//
> > +// The basis for Base64 encoding is RFC 4686
> https://tools.ietf.org/html/rfc4648
> > +//
> > +// RFC 4686 has a number of MAY and SHOULD cases. This
> implementation chooses
> > +// the more restrictive versions for security concerns (see RFC 4686
> section 3.3).
> > +//
> > +// A invalid character, if encountered during the decode operation, causes
> the data
> > +// to be rejected. In addition, the '=' padding character is only allowed at
> the end
> > +// of the Base64 encoded string.
> > +//
> > +#define BAD_V 99
> > +
> > +STATIC CHAR8 Encoding_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
> > + "abcdefghijklmnopqrstuvwxyz"
> > + "0123456789+/";
> > +
> > +STATIC UINT8 Decoding_table[] = {
>
> 1. Can the Encoding_table/Decoding_table pass ECC check?
>
> > + //
> > + // Valid characters
> ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789
> +/
> > + // Also, set '=' as a zero for decoding
> > + // 0 , 1, 2, 3, 4, 5, 6, 7, 8, 9,
> a, b, c, d, e, f
> > + 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
> > + 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
> > + 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
> > + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, BAD_V, BAD_V,
> BAD_V, 0, BAD_V, BAD_V, // 30
> > + BAD_V, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
> 13, 14, // 40
> > + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, BAD_V,
> BAD_V, BAD_V, BAD_V, BAD_V, // 50
> > + BAD_V, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37,
> 38, 39, 40, // 60
> > + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, BAD_V,
> BAD_V, BAD_V, BAD_V, BAD_V, // 70
> > + 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
> > + 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
> > + 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
> > + 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
> > + 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
> > + 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
> > + 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
> > + 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
> > +};
> > +
> > +/**
> > + Convert binary data to a Base64 encoded ascii string based on RFC4648.
> > +
> > + Produce a Null-terminated Ascii string in the output buffer specified by
> Destination and DestinationSize.
> > + The Ascii string is produced by converting the data string specified by
> Source and SourceLength.
> > +
> > + @param Source Input UINT8 data
> > + @param SourceLength Number of UINT8 bytes of data
> > + @param Destination Pointer to output string buffer
> > + @param DestinationSize Size of ascii buffer. Set to 0 to get the size
> needed.
> > + Caller is responsible for passing in buffer of DestinationSize
> > +
> > + @retval RETURN_SUCCESS When ascii buffer is filled in.
> > + @retval RETURN_INVALID_PARAMETER If Source is NULL or
> DestinationSize is NULL.
> > + @retval RETURN_INVALID_PARAMETER If SourceLength or
> DestinationSize is bigger than (MAX_ADDRESS - (UINTN)Destination).
> > + @retval RETURN_BUFFER_TOO_SMALL If SourceLength is 0 and
> DestinationSize is <1.
> > + @retval RETURN_BUFFER_TOO_SMALL If Destination is NULL or
> DestinationSize is smaller than required buffersize.
> > +
> > +**/
> > +RETURN_STATUS
> > +EFIAPI
> > +Base64Encode (
> > + IN CONST UINT8 *Source,
> > + IN UINTN SourceLength,
> > + OUT CHAR8 *Destination OPTIONAL,
> > + IN OUT UINTN *DestinationSize
> > + )
> > +{
> > +
> > + UINTN RequiredSize;
> > + UINTN Left;
> > + CONST UINT8 *Inptr;
> > + CHAR8 *Outptr;
> 2. Inptr and Outptr might be able to be removed. please check.
> > +
> > + //
> > + // Check pointers, and SourceLength is valid
> > + //
> > + if ((Source == NULL) || (DestinationSize == NULL)) {
> > + return RETURN_INVALID_PARAMETER;
> > + }
> > +
> > + //
> > + // Allow for RFC 4648 test vector 1
> > + //
> > + if (SourceLength == 0) {
> > + if (*DestinationSize < 1) {
> > + *DestinationSize = 1;
> > + return RETURN_BUFFER_TOO_SMALL;
> > + }
> > + *DestinationSize = 1;
> > + *Destination = '\0';
> > + return RETURN_SUCCESS;
> > + }
> > +
> > + //
> > + // Check if SourceLength or DestinationSize is valid
> > + //
> > + if ((SourceLength >= (MAX_ADDRESS - (UINTN)Source)) ||
> (*DestinationSize >= (MAX_ADDRESS - (UINTN)Destination))){
> > + return RETURN_INVALID_PARAMETER;
> > + }
> > +
> > + //
> > + // 4 ascii per 3 bytes + NULL
> > + //
> > + RequiredSize = ((SourceLength + 2) / 3) * 4 + 1;
> > + if ((Destination == NULL) || *DestinationSize < RequiredSize) {
> > + *DestinationSize = RequiredSize;
> > + return RETURN_BUFFER_TOO_SMALL; > + }
> > +
> > + Left = SourceLength;
> > + Outptr = Destination;
> > + Inptr = Source;
> > +
> > + //
> > + // Encode 24 bits (three bytes) into 4 ascii characters
> > + //
> > + while (Left >= 3) {
> > +
> > + *Outptr++ = Encoding_table[( Inptr[0] & 0xfc) >> 2 ];
> > + *Outptr++ = Encoding_table[((Inptr[0] & 0x03) << 4) + ((Inptr[1] &
> 0xf0) >> 4)];
> > + *Outptr++ = Encoding_table[((Inptr[1] & 0x0f) << 2) + ((Inptr[2] &
> 0xc0) >> 6)];
> > + *Outptr++ = Encoding_table[( Inptr[2] & 0x3f)];
> > + Left -= 3;
> > + Inptr += 3;
> > + }
> > +
> > + //
> > + // Handle the remainder, and add padding '=' characters as necessary.
> > + //
> > + switch (Left) {
> > + case 0:
> > +
> > + //
> > + // No bytes Left, done.
> > + //
> > + break;
> > + case 1:
> > +
> > + //
> > + // One more data byte, two pad characters
> > + //
> > + *Outptr++ = Encoding_table[( Inptr[0] & 0xfc) >> 2];
> > + *Outptr++ = Encoding_table[((Inptr[0] & 0x03) << 4)];
> > + *Outptr++ = '=';
> > + *Outptr++ = '=';
> > + break;
> > + case 2:
> > +
> > + //
> > + // Two more data bytes, and one pad character
> > + //
> > + *Outptr++ = Encoding_table[( Inptr[0] & 0xfc) >> 2];
> > + *Outptr++ = Encoding_table[((Inptr[0] & 0x03) << 4) + ((Inptr[1] &
> 0xf0) >> 4)];
> > + *Outptr++ = Encoding_table[((Inptr[1] & 0x0f) << 2)];
> > + *Outptr++ = '=';
> > + break;
> > + }
> > + //
> > + // Add terminating NULL
> > + //
> > + *Outptr = '\0';
> > + return RETURN_SUCCESS;
> > +}
> > +
> > +/**
> > + Convert Base64 ascii string to binary data based on RFC4648.
> > +
> > + Produce Null-terminated binary data in the output buffer specified by
> Destination and DestinationSize.
> > + The binary data is produced by converting the Base64 ascii string
> specified by Source and SourceLength.
> > +
> > + @param Source Input ASCII characters
> > + @param SourceLength Number of ASCII characters
> > + @param Destination Pointer to output buffer
> > + @param DestinationSize Caller is responsible for passing in buffer of at
> least DestinationSize.
> > + Set 0 to get the size needed. Set to bytes stored on return.
> > +
> > + @retval RETURN_SUCCESS When binary buffer is filled in.
> > + @retval RETURN_INVALID_PARAMETER If Source is NULL or
> DestinationSize is NULL.
> > + @retval RETURN_INVALID_PARAMETER If SourceLength or
> DestinationSize is bigger than (MAX_ADDRESS -(UINTN)Destination ).
> > + @retval RETURN_INVALID_PARAMETER If there is any invalid character
> in input stream.
> > + @retval RETURN_BUFFER_TOO_SMALL If buffer length is smaller than
> required buffer size.
> > + **/
> > +RETURN_STATUS
> > +EFIAPI
> > +Base64Decode (
> > + IN CONST CHAR8 *Source,
> > + IN UINTN SourceLength,
> > + OUT UINT8 *Destination OPTIONAL,
> > + IN OUT UINTN *DestinationSize
> > + )
> > +{
> > +
> > + UINT8 *BinData;
> 3. Could be removed. see comments below.
> > + UINT32 Value;
> > + CHAR8 Chr;
> > + INTN BufferSize;
> > + UINTN Indx;
> > + UINTN Ondx; > + UINTN Icnt;
> 4. Why not just use SourceIndex/DestinationIndex/Index for above 3 local
> variables?
> > + UINTN ActualSourceLength;
> > +
> > + //
> > + // Check pointers are not NULL
> > + //
> > + if ((Source == NULL) || (DestinationSize == NULL)) {
> > + DEBUG((DEBUG_ERROR, "Source=%p, DestinationSize=%\n", Source,
> DestinationSize));
>
> 5. Please remove this debug message. It's not proper for a BaseLib API
> to print debug message.
>
> > + return RETURN_INVALID_PARAMETER;
> > + }
> > +
> > + //
> > + // Check if SourceLength or DestinationSize is valid
> > + //
> > + if ((SourceLength >= (MAX_ADDRESS - (UINTN)Source)) ||
> (*DestinationSize >= (MAX_ADDRESS - (UINTN)Destination))){
> > + return RETURN_INVALID_PARAMETER;
> > + }
> > +
> > + ActualSourceLength = 0;
> > + BufferSize = 0;
> > +
> > + //
> > + // Determine the actual number of valid characters in the string.
> > + // All invalid characters except selected white space characters,
> > + // will cause the Base64 string to be rejected. White space to allow
> > + // properly formatted XML will be ignored.
> > + //
> > + // See section 3.3 of RFC 4648.
> > + //
> > + for (Indx = 0; Indx < SourceLength; Indx++) {
> > +
> > + //
> > + // '=' is part of the quantum
> > + //
> > + if (Source[Indx] == '=') {
> > + ActualSourceLength++;
> > + BufferSize--;
> > +
> > + //
> > + // Only two '=' characters can be valid.
> > + //
> > + if (BufferSize < -2) {
> > + DEBUG((DEBUG_ERROR, "Source=%p, Invalid = at %d\n", Source,
> Indx));
> > + return RETURN_INVALID_PARAMETER;
> > + }
> > + }
> > + else {
> > + Chr = Source[Indx];
> > + if (BAD_V != Decoding_table[(UINT8) Chr]) {
> > +
> > + //
> > + // The '=' characters are only valid at the end, so any
> > + // valid character after an '=', will be flagged as an error.
> > + //
> > + if (BufferSize < 0) {
> > + DEBUG((DEBUG_ERROR, "Source=%p, Invalid character %c at %d\n",
> Source, Chr, Indx));
> > + return RETURN_INVALID_PARAMETER;
> > + }
> > + ActualSourceLength++;
> > + }
> > + else {
> > +
> > + //
> > + // The reset of the decoder will ignore all invalid characters allowed
> here.
> > + // Ignoring selected white space is useful. In this case, the decoder
> will
> > + // ignore ' ', '\t', '\n', and '\r'.
> > + //
> > + if ((Chr != ' ') &&(Chr != '\t') &&(Chr != '\n') &&(Chr != '\r')) {
> > + DEBUG((DEBUG_ERROR, "Source=%p, Invalid character %c at %d\n",
> Source, Chr, Indx));
> > + return RETURN_INVALID_PARAMETER;
> > + }
> > + }
> > + }
> > + }
> > +
> > + //
> > + // The Base64 character string must be a multiple of 4 character
> quantums.
> > + //
> > + if (ActualSourceLength % 4 != 0) {
> > + DEBUG((DEBUG_ERROR,"Source=%p, Invalid data length %d\n", Source,
> ActualSourceLength));
> > + return RETURN_INVALID_PARAMETER;
> > + }
> > +
> > + BufferSize += ActualSourceLength / 4 * 3;
> > + if (BufferSize < 0) {
> > + DEBUG((DEBUG_ERROR,"BufferSize(%d) is wrong because of bad
> input.\n", BufferSize));
> > + return RETURN_INVALID_PARAMETER;
> > + }
> > +
> > + //
> > + // BufferSize is >= 0
> > + //
> > + if ((Destination == NULL) || (*DestinationSize < (UINTN) BufferSize)) {
> > + *DestinationSize = BufferSize;
> > + return RETURN_BUFFER_TOO_SMALL;
> > + }
> > +
> > + //
> > + // If no decodable characters, return a size of zero. RFC 4686 test vector 1.
> > + //
> > + if (ActualSourceLength == 0) {
> > + *DestinationSize = 0;
> > + return RETURN_SUCCESS;
> > + }
> > +
> > + BinData = Destination;
> 6. Can you directly use Destination and remove BinData local variable?
> I don't find any further usage of Destination in below code.
> > +
> > + //
> > + // Input data is verified to be a multiple of 4 valid charcters. Process four
> > + // characters at a time. Uncounted (ie. invalid) characters will be ignored.
> > + //
> > + for (Indx = 0, Ondx = 0; (Indx < SourceLength) && (Ondx <
> *DestinationSize); ) {
> > + Value = 0;
> > +
> > + //
> > + // Get 24 bits of data from 4 input characters, each character
> representing 6 bits
> > + //
> > + for (Icnt = 0; Icnt < 4; Icnt++) {
> > + do {
> > + Chr = Decoding_table[(UINT8) Source[Indx++]];
> > + } while (Chr == BAD_V);
> > + Value <<= 6;
> > + Value |= Chr;
> > + }
> > +
> > + //
> > + // Store 3 bytes of binary data (24 bits)
> > + //
> > + *BinData++ = (UINT8) (Value >> 16);
> > + Ondx++;
> > +
> > + //
> > + // Due to the '=' special cases for the two bytes at the end,
> > + // we have to check the length and not store the padding data
> > + //
> > + if (Ondx++ < *DestinationSize) {
> > + *BinData++ = (UINT8) (Value >> 8);
> > + }
> > + if (Ondx++ < *DestinationSize) {
> > + *BinData++ = (UINT8) Value;
> > + }
> > + }
> > +
> > + return RETURN_SUCCESS;
> > +}
> > +
> > /**
> > Converts an 8-bit value to an 8-bit BCD value.
> >
> >
>
>
> --
> Thanks,
> Ray
For ECC check, the ECC checking tool seems not to take it as error code.
For other comments, I 'd like to generate a new patch according to your opinions and sent it out.
Thanks,
Shenglei
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2019-01-31 7:08 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2019-01-30 6:28 [PATCH v4] MdePkg/BaseLib: Add Base64Encode() and Base64Decode() Shenglei Zhang
2019-01-30 9:49 ` Ni, Ruiyu
2019-01-31 7:08 ` Zhang, Shenglei
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox