1+ using System . Text ;
2+
3+ namespace ModelContextProtocol . Utils
4+ {
5+ /// <summary>
6+ /// Helper methods for Base64Url encoding and decoding.
7+ /// Based on implementation from MSAL (https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/blob/main/src/client/Microsoft.Identity.Client/Utils/Base64UrlHelpers.cs)
8+ /// </summary>
9+ internal static class Base64UrlHelpers
10+ {
11+ private const char base64UrlCharacter62 = '-' ;
12+ private const char base64UrlCharacter63 = '_' ;
13+
14+ /// <summary>
15+ /// Encoding table for base64url encoding
16+ /// </summary>
17+ internal static readonly char [ ] s_base64Table =
18+ {
19+ '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' ,
20+ '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' ,
21+ '0' , '1' , '2' , '3' , '4' , '5' , '6' , '7' , '8' , '9' ,
22+ base64UrlCharacter62 ,
23+ base64UrlCharacter63
24+ } ;
25+
26+ /// <summary>
27+ /// Converts an array of bytes to a base64url encoded string.
28+ /// </summary>
29+ /// <param name="inArray">An array of 8-bit unsigned integers.</param>
30+ /// <returns>The string representation in base64url encoding of inArray.</returns>
31+ public static string Encode ( byte [ ] inArray )
32+ {
33+ if ( inArray == null )
34+ return string . Empty ;
35+
36+ return Encode ( inArray , 0 , inArray . Length ) ;
37+ }
38+
39+ /// <summary>
40+ /// Converts a subset of an array of 8-bit unsigned integers to its equivalent string representation that is encoded with base-64-url digits.
41+ /// </summary>
42+ /// <param name="inArray">An array of 8-bit unsigned integers.</param>
43+ /// <param name="offset">An offset in inArray.</param>
44+ /// <param name="length">The number of elements of inArray to convert.</param>
45+ /// <returns>The string representation in base64url encoding of length elements of inArray, starting at position offset.</returns>
46+ private static string Encode ( byte [ ] inArray , int offset , int length )
47+ {
48+ _ = inArray ?? throw new ArgumentNullException ( nameof ( inArray ) ) ;
49+
50+ if ( length == 0 )
51+ return string . Empty ;
52+
53+ if ( length < 0 )
54+ throw new ArgumentOutOfRangeException ( nameof ( length ) ) ;
55+
56+ if ( offset < 0 || inArray . Length < offset )
57+ throw new ArgumentOutOfRangeException ( nameof ( offset ) ) ;
58+
59+ if ( inArray . Length < offset + length )
60+ throw new ArgumentOutOfRangeException ( nameof ( length ) ) ;
61+
62+ int lengthmod3 = length % 3 ;
63+ int limit = offset + ( length - lengthmod3 ) ;
64+ char [ ] output = new char [ ( length + 2 ) / 3 * 4 ] ;
65+ char [ ] table = s_base64Table ;
66+ int i , j = 0 ;
67+
68+ // Process three bytes at a time, each three bytes becomes four base64 characters
69+ for ( i = offset ; i < limit ; i += 3 )
70+ {
71+ byte d0 = inArray [ i ] ;
72+ byte d1 = inArray [ i + 1 ] ;
73+ byte d2 = inArray [ i + 2 ] ;
74+
75+ output [ j + 0 ] = table [ d0 >> 2 ] ;
76+ output [ j + 1 ] = table [ ( ( d0 & 0x03 ) << 4 ) | ( d1 >> 4 ) ] ;
77+ output [ j + 2 ] = table [ ( ( d1 & 0x0f ) << 2 ) | ( d2 >> 6 ) ] ;
78+ output [ j + 3 ] = table [ d2 & 0x3f ] ;
79+ j += 4 ;
80+ }
81+
82+ // Handle remaining bytes and padding
83+ i = limit ;
84+
85+ switch ( lengthmod3 )
86+ {
87+ case 2 :
88+ {
89+ byte d0 = inArray [ i ] ;
90+ byte d1 = inArray [ i + 1 ] ;
91+
92+ output [ j + 0 ] = table [ d0 >> 2 ] ;
93+ output [ j + 1 ] = table [ ( ( d0 & 0x03 ) << 4 ) | ( d1 >> 4 ) ] ;
94+ output [ j + 2 ] = table [ ( d1 & 0x0f ) << 2 ] ;
95+ j += 3 ;
96+ }
97+ break ;
98+
99+ case 1 :
100+ {
101+ byte d0 = inArray [ i ] ;
102+
103+ output [ j + 0 ] = table [ d0 >> 2 ] ;
104+ output [ j + 1 ] = table [ ( d0 & 0x03 ) << 4 ] ;
105+ j += 2 ;
106+ }
107+ break ;
108+
109+ // Default or case 0: no further operations are needed.
110+ }
111+
112+ // Return the result without creating any additional string allocations
113+ return new string ( output , 0 , j ) ;
114+ }
115+
116+ /// <summary>
117+ /// Encodes a string using base64url encoding.
118+ /// </summary>
119+ /// <param name="str">The string to encode.</param>
120+ /// <returns>Base64Url encoding of the UTF8 bytes of the input string.</returns>
121+ public static string EncodeString ( string str )
122+ {
123+ if ( str == null )
124+ throw new ArgumentNullException ( nameof ( str ) , "Input string cannot be null." ) ;
125+
126+ return Encode ( Encoding . UTF8 . GetBytes ( str ) ) ?? string . Empty ;
127+ }
128+ }
129+ }
0 commit comments