From: "Ni, Ray" <ray.ni@intel.com>
To: "Zhang, Shenglei" <shenglei.zhang@intel.com>,
"edk2-devel@lists.01.org" <edk2-devel@lists.01.org>
Cc: "Kinney, Michael D" <michael.d.kinney@intel.com>,
"Gao, Liming" <liming.gao@intel.com>
Subject: Re: [PATCH v2] MdePkg/BaseLib: Add Base64Encode() and Base64Decode()
Date: Tue, 29 Jan 2019 03:52:17 +0000 [thread overview]
Message-ID: <734D49CCEBEEF84792F5B80ED585239D5BFF763F@SHSMSX104.ccr.corp.intel.com> (raw)
In-Reply-To: <20190110124331.7788-1-shenglei.zhang@intel.com>
Minor comments regarding the parameter name.
> -----Original Message-----
> From: edk2-devel <edk2-devel-bounces@lists.01.org> On Behalf Of Shenglei
> Zhang
> Sent: Thursday, January 10, 2019 8:44 PM
> To: edk2-devel@lists.01.org
> Cc: Kinney, Michael D <michael.d.kinney@intel.com>; Gao, Liming
> <liming.gao@intel.com>
> Subject: [edk2] [PATCH v2] MdePkg/BaseLib: Add Base64Encode() and
> Base64Decode()
>
> 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
>
> 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 | 345
> +++++++++++++++++++++++++++++++
> 2 files changed, 401 insertions(+)
>
> diff --git a/MdePkg/Include/Library/BaseLib.h
> b/MdePkg/Include/Library/BaseLib.h
> index 1eb842384e..9317888a7e 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
> AsciiPtr and AsciiSize.
> + The Ascii string is produced by converting the data string specified by
> DataPtr and DataLen.
> +
> + @param DataPtr Input UINT8 data
> + @param DataLen Number of UINT8 bytes of data
> + @param AsciiPtr Pointer to output string buffer
> + @param AsciiSize Size of ascii buffer. Set to 0 to get the size needed.
> + Caller is responsible for passing in
> + buffer of AsciiSize
> +
> + @retval RETURN_SUCCESS When ascii buffer is filled in.
> + @retval RETURN_INVALID_PARAMETER If DataPtr is NULL or AsciiSize is
> NULL.
> + @retval RETURN_INVALID_PARAMETER If DataLen or AsciiSize is too big.
> + @retval RETURN_BUFFER_TOO_SMALL If DataLen is 0 and AsciiSize is <1.
> + @retval RETURN_BUFFER_TOO_SMALL If AsciiPtr is NULL or too small.
> +
> +**/
> +RETURN_STATUS
> +EFIAPI
> +Base64Encode (
> + IN CONST UINT8 *DataPtr,
1. Data or Source?
> + IN UINTN DataLen,
2. DataLength or SourceLength?
> + OUT CHAR8 *AsciiPtr OPTIONAL,
3. AsciiString or Destination?
> + IN OUT UINTN *AsciiSize
4. AsciiStringSize or DestinationSize?
> + );
> +
> +/**
> + Convert Base64 ascii string to binary data based on RFC4648.
> +
> + Produce Null-terminated binary data in the output buffer specified by
> BinPtr and BinSize.
> + The binary data is produced by converting the Base64 ascii string specified
> by DataPtr and DataLen.
> +
> + @param DataPtr Input ASCII characters
> + @param DataLen Number of ASCII characters
> + @param BinPtr Pointer to output buffer
> + @param BinSize Caller is responsible for passing in buffer of at least
> BinSize.
> + 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 DataPtr is NULL or BinSize is
> NULL.
> + @retval RETURN_INVALID_PARAMETER If DataLen or BinSize is too big.
> + @retval RETURN_INVALID_PARAMETER If BinPtr NULL and BinSize != 0.
> + @retval RETURN_INVALID_PARAMETER If there is any Invalid character in
> input stream.
> + @retval RETURN_BUFFER_TOO_SMALL If Buffer length is too small.
> + **/
> +RETURN_STATUS
> +EFIAPI
> +Base64Decode (
> + IN CONST CHAR8 *DataPtr,
5. AsciiString or Source? At least I don't think "DataPtr" is proper here. It's not the same "DataPtr" in Encode() API.
> + IN UINTN DataLen,
6. AsciiStringLength or SourceLength?
> + OUT UINT8 *BinPtr OPTIONAL,
7. Data or Destination?
> + IN OUT UINTN *BinSize
8. DataSize or 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..761ff6830a 100644
> --- a/MdePkg/Library/BaseLib/String.c
> +++ b/MdePkg/Library/BaseLib/String.c
> @@ -1763,6 +1763,351 @@ 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
> +ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz012345678
> 9+/
> + // 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
> AsciiPtr and AsciiSize.
> + The Ascii string is produced by converting the data string specified by
> DataPtr and DataLen.
> +
> + @param DataPtr Input UINT8 data
> + @param DataLen Number of UINT8 bytes of data
> + @param AsciiPtr Pointer to output string buffer
> + @param AsciiSize Size of ascii buffer. Set to 0 to get the size needed.
> + Caller is responsible for passing in
> + buffer of AsciiSize
> +
> + @retval RETURN_SUCCESS When ascii buffer is filled in.
> + @retval RETURN_INVALID_PARAMETER If DataPtr is NULL or AsciiSize is
> NULL.
> + @retval RETURN_INVALID_PARAMETER If DataLen or AsciiSize is too big.
> + @retval RETURN_BUFFER_TOO_SMALL If DataLen is 0 and AsciiSize is <1.
> + @retval RETURN_BUFFER_TOO_SMALL If AsciiPtr is NULL or too small.
> +
> +**/
> +RETURN_STATUS
> +EFIAPI
> +Base64Encode (
> + IN CONST UINT8 *DataPtr,
> + IN UINTN DataLen,
> + OUT CHAR8 *AsciiPtr OPTIONAL,
> + IN OUT UINTN *AsciiSize
> + )
> +{
> +
> + UINTN RequiredSize;
> + UINTN Left;
> + CONST UINT8 *Inptr;
> + CHAR8 *Outptr;
> +
> + //
> + // Check pointers, and DataLen is valid // if ((DataPtr == NULL) ||
> + (AsciiSize == NULL)) {
> + return RETURN_INVALID_PARAMETER;
> + }
> +
> + //
> + // Allow for RFC 4648 test vector 1
> + //
> + if (DataLen == 0) {
> + if (*AsciiSize < 1) {
> + *AsciiSize = 1;
> + return RETURN_BUFFER_TOO_SMALL;
> + }
> + *AsciiSize = 1;
> + *AsciiPtr = '\0';
> + return RETURN_SUCCESS;
> + }
> +
> + //
> + // Check if Datalen or AsciiSize is valid // if ((DataLen >=
> + (MAX_ADDRESS - (UINTN)DataPtr)) || (*AsciiSize >= (MAX_ADDRESS -
> (UINTN)AsciiPtr))){
> + return RETURN_INVALID_PARAMETER;
> + }
> +
> + //
> + // 4 ascii per 3 bytes + NULL
> + //
> + RequiredSize = ((DataLen + 2) / 3) * 4 + 1; if ((AsciiPtr == NULL)
> + || *AsciiSize < RequiredSize) {
> + *AsciiSize = RequiredSize;
> + return RETURN_BUFFER_TOO_SMALL;
> + }
> +
> + Left = DataLen;
> + Outptr = AsciiPtr;
> + Inptr = DataPtr;
> +
> + //
> + // 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
> BinPtr and BinSize.
> + The binary data is produced by converting the Base64 ascii string specified
> by DataPtr and DataLen.
> +
> + @param DataPtr Input ASCII characters
> + @param DataLen Number of ASCII characters
> + @param BinPtr Pointer to output buffer
> + @param BinSize Caller is responsible for passing in buffer of at least
> BinSize.
> + 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 DataPtr is NULL or BinSize is
> NULL.
> + @retval RETURN_INVALID_PARAMETER If DataLen or BinSize is too big.
> + @retval RETURN_INVALID_PARAMETER If BinPtr NULL and BinSize != 0.
> + @retval RETURN_INVALID_PARAMETER If there is any Invalid character in
> input stream.
> + @retval RETURN_BUFFER_TOO_SMALL If Buffer length is too small.
> + **/
> +RETURN_STATUS
> +EFIAPI
> +Base64Decode (
> + IN CONST CHAR8 *DataPtr,
> + IN UINTN DataLen,
> + OUT UINT8 *BinPtr OPTIONAL,
> + IN OUT UINTN *BinSize
> + )
> +{
> +
> + UINT8 *BinData;
> + UINT32 Value;
> + CHAR8 Chr;
> + INTN BufferSize;
> + UINTN Indx;
> + UINTN Ondx;
> + UINTN Icnt;
> + UINTN ActualDataLen;
> +
> + //
> + // Check pointers are not NULL
> + //
> + if ((DataPtr == NULL) || (BinSize == NULL)) {
> + DEBUG((DEBUG_ERROR, "DataPtr=%p, BinSize=%\n", DataPtr, BinSize));
> + return RETURN_INVALID_PARAMETER;
> + }
> +
> + //
> + // Check if Datalen or BinSize is valid // if ((DataLen >=
> + (MAX_ADDRESS - (UINTN)DataPtr)) || (*BinSize >= (MAX_ADDRESS -
> (UINTN)BinPtr))){
> + return RETURN_INVALID_PARAMETER;
> + }
> +
> + ActualDataLen = 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 < DataLen; Indx++) {
> +
> + //
> + // '=' is part of the quantum
> + //
> + if (DataPtr[Indx] == '=') {
> + ActualDataLen++;
> + BufferSize--;
> +
> + //
> + // Only two '=' characters can be valid.
> + //
> + if (BufferSize < -2) {
> + DEBUG((DEBUG_ERROR, "DataPtr=%p, Invalid = at %d\n", DataPtr,
> Indx));
> + return RETURN_INVALID_PARAMETER;
> + }
> + }
> + else {
> + Chr = DataPtr[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, "DataPtr=%p, Invalid character %c at %d\n",
> DataPtr, Chr, Indx));
> + return RETURN_INVALID_PARAMETER;
> + }
> + ActualDataLen++;
> + }
> + 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, "DataPtr=%p, Invalid character %c at %d\n",
> DataPtr, Chr, Indx));
> + return RETURN_INVALID_PARAMETER;
> + }
> + }
> + }
> + }
> +
> + //
> + // The Base64 character string must be a multiple of 4 character quantums.
> + //
> + if (ActualDataLen % 4 != 0) {
> + DEBUG((DEBUG_ERROR,"DataPtr=%p, Invalid data length %d\n", DataPtr,
> ActualDataLen));
> + return RETURN_INVALID_PARAMETER;
> + }
> +
> + BufferSize += ActualDataLen / 4 * 3;
> + if (BufferSize < 0) {
> + DEBUG((DEBUG_ERROR,"BufferSize(%d) is wrong.\n", BufferSize));
> + return RETURN_DEVICE_ERROR;
> + }
> +
> + //
> + // BufferSize is >= 0
> + //
> + if ((BinPtr == NULL) || (*BinSize < (UINTN) BufferSize)) {
> + *BinSize = BufferSize;
> + return RETURN_BUFFER_TOO_SMALL;
> + }
> +
> + //
> + // If no decodable characters, return a size of zero. RFC 4686 test vector 1.
> + //
> + if (ActualDataLen == 0) {
> + *BinSize = 0;
> + return RETURN_SUCCESS;
> + }
> +
> + BinData = BinPtr;
> +
> + //
> + // 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 < DataLen) && (Ondx < *BinSize); ) {
> + 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) DataPtr[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++ < *BinSize) {
> + *BinData++ = (UINT8) (Value >> 8);
> + }
> + if (Ondx++ < *BinSize) {
> + *BinData++ = (UINT8) Value;
> + }
> + }
> +
> + return RETURN_SUCCESS;
> +}
> +
> /**
> Converts an 8-bit value to an 8-bit BCD value.
>
> --
> 2.18.0.windows.1
>
> _______________________________________________
> edk2-devel mailing list
> edk2-devel@lists.01.org
> https://lists.01.org/mailman/listinfo/edk2-devel
prev parent reply other threads:[~2019-01-29 3:52 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2019-01-10 12:43 [PATCH v2] MdePkg/BaseLib: Add Base64Encode() and Base64Decode() Shenglei Zhang
2019-01-29 3:52 ` Ni, Ray [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-list from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=734D49CCEBEEF84792F5B80ED585239D5BFF763F@SHSMSX104.ccr.corp.intel.com \
--to=devel@edk2.groups.io \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox