| // <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); |
| } |
| } |