summaryrefslogtreecommitdiffstats
path: root/MdePkg/Library/BaseLib/String.c
diff options
context:
space:
mode:
Diffstat (limited to 'MdePkg/Library/BaseLib/String.c')
-rw-r--r--MdePkg/Library/BaseLib/String.c249
1 files changed, 247 insertions, 2 deletions
diff --git a/MdePkg/Library/BaseLib/String.c b/MdePkg/Library/BaseLib/String.c
index f8397035c3..6198ccbc96 100644
--- a/MdePkg/Library/BaseLib/String.c
+++ b/MdePkg/Library/BaseLib/String.c
@@ -1973,8 +1973,253 @@ Base64Decode (
IN OUT UINTN *DestinationSize
)
{
- ASSERT (FALSE);
- return RETURN_INVALID_PARAMETER;
+ BOOLEAN PaddingMode;
+ UINTN SixBitGroupsConsumed;
+ UINT32 Accumulator;
+ UINTN OriginalDestinationSize;
+ UINTN SourceIndex;
+
+ if (DestinationSize == NULL) {
+ return RETURN_INVALID_PARAMETER;
+ }
+
+ //
+ // Check Source array validity.
+ //
+ if (Source == NULL) {
+ if (SourceSize > 0) {
+ //
+ // At least one CHAR8 element at NULL Source.
+ //
+ return RETURN_INVALID_PARAMETER;
+ }
+ } else if (SourceSize > MAX_ADDRESS - (UINTN)Source) {
+ //
+ // Non-NULL Source, but it wraps around.
+ //
+ return RETURN_INVALID_PARAMETER;
+ }
+
+ //
+ // Check Destination array validity.
+ //
+ if (Destination == NULL) {
+ if (*DestinationSize > 0) {
+ //
+ // At least one UINT8 element at NULL Destination.
+ //
+ return RETURN_INVALID_PARAMETER;
+ }
+ } else if (*DestinationSize > MAX_ADDRESS - (UINTN)Destination) {
+ //
+ // Non-NULL Destination, but it wraps around.
+ //
+ return RETURN_INVALID_PARAMETER;
+ }
+
+ //
+ // Check for overlap.
+ //
+ if (Source != NULL && Destination != NULL) {
+ //
+ // Both arrays have been provided, and we know from earlier that each array
+ // is valid in itself.
+ //
+ if ((UINTN)Source + SourceSize <= (UINTN)Destination) {
+ //
+ // Source array precedes Destination array, OK.
+ //
+ } else if ((UINTN)Destination + *DestinationSize <= (UINTN)Source) {
+ //
+ // Destination array precedes Source array, OK.
+ //
+ } else {
+ //
+ // Overlap.
+ //
+ return RETURN_INVALID_PARAMETER;
+ }
+ }
+
+ //
+ // Decoding loop setup.
+ //
+ PaddingMode = FALSE;
+ SixBitGroupsConsumed = 0;
+ Accumulator = 0;
+ OriginalDestinationSize = *DestinationSize;
+ *DestinationSize = 0;
+
+ //
+ // Decoding loop.
+ //
+ for (SourceIndex = 0; SourceIndex < SourceSize; SourceIndex++) {
+ CHAR8 SourceChar;
+ UINT32 Base64Value;
+ UINT8 DestinationOctet;
+
+ SourceChar = Source[SourceIndex];
+
+ //
+ // Whitespace is ignored at all positions (regardless of padding mode).
+ //
+ if (SourceChar == '\t' || SourceChar == '\n' || SourceChar == '\v' ||
+ SourceChar == '\f' || SourceChar == '\r' || SourceChar == ' ') {
+ continue;
+ }
+
+ //
+ // If we're in padding mode, accept another padding character, as long as
+ // that padding character completes the quantum. This completes case (2)
+ // from RFC4648, Chapter 4. "Base 64 Encoding":
+ //
+ // (2) The final quantum of encoding input is exactly 8 bits; here, the
+ // final unit of encoded output will be two characters followed by two
+ // "=" padding characters.
+ //
+ if (PaddingMode) {
+ if (SourceChar == '=' && SixBitGroupsConsumed == 3) {
+ SixBitGroupsConsumed = 0;
+ continue;
+ }
+ return RETURN_INVALID_PARAMETER;
+ }
+
+ //
+ // When not in padding mode, decode Base64Value based on RFC4648, "Table 1:
+ // The Base 64 Alphabet".
+ //
+ if ('A' <= SourceChar && SourceChar <= 'Z') {
+ Base64Value = SourceChar - 'A';
+ } else if ('a' <= SourceChar && SourceChar <= 'z') {
+ Base64Value = 26 + (SourceChar - 'a');
+ } else if ('0' <= SourceChar && SourceChar <= '9') {
+ Base64Value = 52 + (SourceChar - '0');
+ } else if (SourceChar == '+') {
+ Base64Value = 62;
+ } else if (SourceChar == '/') {
+ Base64Value = 63;
+ } else if (SourceChar == '=') {
+ //
+ // Enter padding mode.
+ //
+ PaddingMode = TRUE;
+
+ if (SixBitGroupsConsumed == 2) {
+ //
+ // If we have consumed two 6-bit groups from the current quantum before
+ // encountering the first padding character, then this is case (2) from
+ // RFC4648, Chapter 4. "Base 64 Encoding". Bump SixBitGroupsConsumed,
+ // and we'll enforce another padding character.
+ //
+ SixBitGroupsConsumed = 3;
+ } else if (SixBitGroupsConsumed == 3) {
+ //
+ // If we have consumed three 6-bit groups from the current quantum
+ // before encountering the first padding character, then this is case
+ // (3) from RFC4648, Chapter 4. "Base 64 Encoding". The quantum is now
+ // complete.
+ //
+ SixBitGroupsConsumed = 0;
+ } else {
+ //
+ // Padding characters are not allowed at the first two positions of a
+ // quantum.
+ //
+ return RETURN_INVALID_PARAMETER;
+ }
+
+ //
+ // Wherever in a quantum we enter padding mode, we enforce the padding
+ // bits pending in the accumulator -- from the last 6-bit group just
+ // preceding the padding character -- to be zero. Refer to RFC4648,
+ // Chapter 3.5. "Canonical Encoding".
+ //
+ if (Accumulator != 0) {
+ return RETURN_INVALID_PARAMETER;
+ }
+
+ //
+ // Advance to the next source character.
+ //
+ continue;
+ } else {
+ //
+ // Other characters outside of the encoding alphabet are rejected.
+ //
+ return RETURN_INVALID_PARAMETER;
+ }
+
+ //
+ // Feed the bits of the current 6-bit group of the quantum to the
+ // accumulator.
+ //
+ Accumulator = (Accumulator << 6) | Base64Value;
+ SixBitGroupsConsumed++;
+ switch (SixBitGroupsConsumed) {
+ case 1:
+ //
+ // No octet to spill after consuming the first 6-bit group of the
+ // quantum; advance to the next source character.
+ //
+ continue;
+ case 2:
+ //
+ // 12 bits accumulated (6 pending + 6 new); prepare for spilling an
+ // octet. 4 bits remain pending.
+ //
+ DestinationOctet = (UINT8)(Accumulator >> 4);
+ Accumulator &= 0xF;
+ break;
+ case 3:
+ //
+ // 10 bits accumulated (4 pending + 6 new); prepare for spilling an
+ // octet. 2 bits remain pending.
+ //
+ DestinationOctet = (UINT8)(Accumulator >> 2);
+ Accumulator &= 0x3;
+ break;
+ default:
+ ASSERT (SixBitGroupsConsumed == 4);
+ //
+ // 8 bits accumulated (2 pending + 6 new); prepare for spilling an octet.
+ // The quantum is complete, 0 bits remain pending.
+ //
+ DestinationOctet = (UINT8)Accumulator;
+ Accumulator = 0;
+ SixBitGroupsConsumed = 0;
+ break;
+ }
+
+ //
+ // Store the decoded octet if there's room left. Increment
+ // (*DestinationSize) unconditionally.
+ //
+ if (*DestinationSize < OriginalDestinationSize) {
+ ASSERT (Destination != NULL);
+ Destination[*DestinationSize] = DestinationOctet;
+ }
+ (*DestinationSize)++;
+
+ //
+ // Advance to the next source character.
+ //
+ }
+
+ //
+ // If Source terminates mid-quantum, then Source is invalid.
+ //
+ if (SixBitGroupsConsumed != 0) {
+ return RETURN_INVALID_PARAMETER;
+ }
+
+ //
+ // Done.
+ //
+ if (*DestinationSize <= OriginalDestinationSize) {
+ return RETURN_SUCCESS;
+ }
+ return RETURN_BUFFER_TOO_SMALL;
}
/**