* [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