Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use Base64Url in WebEncoders #56959

Merged
merged 3 commits into from
Jul 26, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 51 additions & 1 deletion src/Shared/WebEncoders/WebEncoders.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System;
#if NETCOREAPP
using System.Buffers;
using System.Buffers.Text;
#endif
using System.Diagnostics;
using System.Globalization;
Expand Down Expand Up @@ -66,6 +67,23 @@ public static byte[] Base64UrlDecode(string input, int offset, int count)
return Array.Empty<byte>();
}

#if NET9_0_OR_GREATER
// Legacy behavior of Base64UrlDecode supports either Base64 or Base64Url input.
// If it doesn't have + or /, it can be treated as Base64Url.
ReadOnlySpan<char> inputSpan = input.AsSpan(offset, count);
if (!inputSpan.ContainsAny('+', '/'))
{
return Base64Url.DecodeFromChars(inputSpan);
}

// Otherwise, maintain the legacy behavior of accepting Base64 input. Input that
// contained both +/ and -_ is neither Base64 nor Base64Url and is considered invalid.
if (offset == 0 && count == input.Length)
{
return Convert.FromBase64String(input);
}
#endif

// Create array large enough for the Base64 characters, not just shorter Base64-URL-encoded form.
var buffer = new char[GetArraySizeRequiredToDecode(count)];

Expand Down Expand Up @@ -104,6 +122,23 @@ public static byte[] Base64UrlDecode(string input, int offset, char[] buffer, in
return Array.Empty<byte>();
}

#if NET9_0_OR_GREATER
// Legacy behavior of Base64UrlDecode supports either Base64 or Base64Url input.
// If it doesn't have + or /, it can be treated as Base64Url.
ReadOnlySpan<char> inputSpan = input.AsSpan(offset, count);
if (!inputSpan.ContainsAny('+', '/'))
{
return Base64Url.DecodeFromChars(inputSpan);
}

// Otherwise, maintain the legacy behavior of accepting Base64 input. Input that
// contained both +/ and -_ is neither Base64 nor Base64Url and is considered invalid.
if (offset == 0 && count == input.Length)
{
return Convert.FromBase64String(input);
}
#endif

// Assumption: input is base64url encoded without padding and contains no whitespace.

var paddingCharsToAdd = GetNumBase64PaddingCharsToAddForDecode(count);
Expand All @@ -124,6 +159,13 @@ public static byte[] Base64UrlDecode(string input, int offset, char[] buffer, in

// Copy input into buffer, fixing up '-' -> '+' and '_' -> '/'.
var i = bufferOffset;
#if NET8_0_OR_GREATER
Span<char> bufferSpan = buffer.AsSpan(i, count);
inputSpan.CopyTo(bufferSpan);
bufferSpan.Replace('-', '+');
bufferSpan.Replace('_', '/');
i += count;
#else
for (var j = offset; i - bufferOffset < count; i++, j++)
{
var ch = input[j];
Expand All @@ -140,6 +182,7 @@ public static byte[] Base64UrlDecode(string input, int offset, char[] buffer, in
buffer[i] = ch;
}
}
#endif

// Add the padding characters back.
for (; paddingCharsToAdd > 0; i++, paddingCharsToAdd--)
Expand Down Expand Up @@ -314,6 +357,9 @@ public static int GetArraySizeRequiredToEncode(int count)
[SkipLocalsInit]
public static string Base64UrlEncode(ReadOnlySpan<byte> input)
{
#if NET9_0_OR_GREATER
return Base64Url.EncodeToString(input);
#else
const int StackAllocThreshold = 128;

if (input.IsEmpty)
Expand All @@ -337,6 +383,7 @@ public static string Base64UrlEncode(ReadOnlySpan<byte> input)
}

return base64Url;
#endif
}

#if NET9_0_OR_GREATER
Expand All @@ -347,9 +394,11 @@ public static string Base64UrlEncode(ReadOnlySpan<byte> input)
/// <param name="output">The buffer to place the result in.</param>
/// <returns></returns>
public static int Base64UrlEncode(ReadOnlySpan<byte> input, Span<char> output)
{
return Base64Url.EncodeToChars(input, output);
}
#else
private static int Base64UrlEncode(ReadOnlySpan<byte> input, Span<char> output)
#endif
{
Debug.Assert(output.Length >= GetArraySizeRequiredToEncode(input.Length));

Expand Down Expand Up @@ -383,6 +432,7 @@ private static int Base64UrlEncode(ReadOnlySpan<byte> input, Span<char> output)

return charsWritten;
}
#endif
#endif

private static int GetNumBase64PaddingCharsToAddForDecode(int inputLength)
Expand Down
Loading