blob: 6d294e05827ed96f44788c1973b74b0831d64a36 [file] [log] [blame] [edit]
// <copyright file="Base64UrlEncoder.cs" company="Selenium Committers">
// Licensed to the Software Freedom Conservancy (SFC) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The SFC licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
// </copyright>
using System;
namespace OpenQA.Selenium.Internal;
/*
* Based on: https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/blob/6.19.0/src/Microsoft.IdentityModel.Tokens/Base64UrlEncoder.cs
*
* Now it is a part of .NET 9+ as System.Buffers.Text.Base64Url
* https://github.com/SeleniumHQ/selenium/issues/14813
*/
/// <summary>
/// Encodes and Decodes strings as Base64Url encoding.
/// </summary>
public static class Base64UrlEncoder
{
private const char base64PadCharacter = '=';
private const string doubleBase64PadCharacter = "==";
private const char base64Character62 = '+';
private const char base64Character63 = '/';
private const char base64UrlCharacter62 = '-';
private const char base64UrlCharacter63 = '_';
/// <summary>
/// Encoding table
/// </summary>
internal static readonly char[] s_base64Table =
{
'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z',
'0','1','2','3','4','5','6','7','8','9',
base64UrlCharacter62,
base64UrlCharacter63
};
/// <summary>
/// Converts a subset of an array of 8-bit unsigned integers to its equivalent string representation which is encoded with base-64-url digits.
/// </summary>
/// <param name="inArray">An array of 8-bit unsigned integers.</param>
/// <returns>The string representation in base 64 url encoding of length elements of inArray, starting at position offset.</returns>
/// <exception cref="ArgumentNullException">'inArray' is null.</exception>
public static string Encode(byte[] inArray)
{
_ = inArray ?? throw new ArgumentNullException(nameof(inArray));
if (inArray.Length == 0)
return string.Empty;
var length = inArray.Length;
int lengthmod3 = length % 3;
int limit = length - lengthmod3;
char[] output = new char[(length + 2) / 3 * 4];
char[] table = s_base64Table;
int i, j = 0;
// takes 3 bytes from inArray and insert 4 bytes into output
for (i = 0; i < limit; i += 3)
{
byte d0 = inArray[i];
byte d1 = inArray[i + 1];
byte d2 = inArray[i + 2];
output[j + 0] = table[d0 >> 2];
output[j + 1] = table[((d0 & 0x03) << 4) | (d1 >> 4)];
output[j + 2] = table[((d1 & 0x0f) << 2) | (d2 >> 6)];
output[j + 3] = table[d2 & 0x3f];
j += 4;
}
//Where we left off before
i = limit;
switch (lengthmod3)
{
case 2:
{
byte d0 = inArray[i];
byte d1 = inArray[i + 1];
output[j + 0] = table[d0 >> 2];
output[j + 1] = table[((d0 & 0x03) << 4) | (d1 >> 4)];
output[j + 2] = table[(d1 & 0x0f) << 2];
j += 3;
}
break;
case 1:
{
byte d0 = inArray[i];
output[j + 0] = table[d0 >> 2];
output[j + 1] = table[(d0 & 0x03) << 4];
j += 2;
}
break;
//default or case 0: no further operations are needed.
}
return new string(output, 0, j);
}
/// <summary>
/// Converts the specified string, which encodes binary data as base-64-url digits, to an equivalent 8-bit unsigned integer array.</summary>
/// <param name="str">base64Url encoded string.</param>
/// <returns>UTF8 bytes.</returns>
public static byte[] DecodeBytes(string str)
{
_ = str ?? throw new ArgumentNullException(nameof(str));
// 62nd char of encoding
str = str.Replace(base64UrlCharacter62, base64Character62);
// 63rd char of encoding
str = str.Replace(base64UrlCharacter63, base64Character63);
// check for padding
switch (str.Length % 4)
{
case 0:
// No pad chars in this case
break;
case 2:
// Two pad chars
str += doubleBase64PadCharacter;
break;
case 3:
// One pad char
str += base64PadCharacter;
break;
default:
throw new FormatException(str);
}
return Convert.FromBase64String(str);
}
}