-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathBaseEncodings.cs
144 lines (123 loc) · 5.15 KB
/
BaseEncodings.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
using System.Globalization;
namespace simpleauthenticator
{
internal static class BaseEncodings
{
private static readonly IReadOnlyDictionary<char, int> Base32Alphabet = BuildAlphabetLookupTable("ABCDEFGHIJKLMNOPQRSTUVWXYZ234567");
private static readonly IReadOnlyDictionary<char, int> Base64Alphabet = BuildAlphabetLookupTable("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/");
/// <summary>
/// Implement base32 encoding with default alphabet.
/// Defined by RFC4448 (https://datatracker.ietf.org/doc/html/rfc4648).
/// Input is tolerant to character case and allows space characters anywhere (' ', '\r', '\n\, '\t' ) and does not require padding.
/// E.g. 'NVSXG43BM5SQ====', 'NVSX G43B M5SQ==' and 'nvsx G43B m5sq' considered as a valid and equivalent input.
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
/// <exception cref="FormatException"></exception>
public static byte[] FromBase32String(string input)
{
int filteredLength = 0;
foreach (var ch in input)
{
if (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n' || ch == '=')
{
// Skip all whitespaces, line separators and padding symbols
continue;
}
filteredLength += 1;
}
if (filteredLength == 0)
{
return Array.Empty<byte>();
}
var destination = new byte[filteredLength * 5 / 8];
var destinationIndex = 0;
var buffer = 0;
var bitsLeft = 0;
for (var inputIndex = 0; inputIndex < input.Length; inputIndex++)
{
char ch = input[inputIndex];
if (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n' || ch == '=')
{
// Skip all whitespaces, line separators and padding symbols
continue;
}
ch = char.ToUpper(ch, CultureInfo.InvariantCulture);
if (!Base32Alphabet.TryGetValue(ch, out int alphabetIndex))
{
throw new FormatException($"Illegal character at position {inputIndex}: '{ch}'.");
}
buffer <<= 5;
buffer |= alphabetIndex & 31;
bitsLeft += 5;
if (bitsLeft >= 8)
{
destination[destinationIndex++] = (byte)(buffer >> (bitsLeft - 8));
bitsLeft -= 8;
}
}
return destination;
}
/// <summary>
/// Implement base64 encoding with default alphabet.
/// Defined by RFC4448 (https://datatracker.ietf.org/doc/html/rfc4648).
/// Input allows space characters anywhere (' ', '\r', '\n\, '\t' ) and does not require padding.
/// E.g. 'AAAAAAAA=' and 'AAAA AAAA' considered as a valid and equivalent input.
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
/// <exception cref="FormatException"></exception>
public static byte[] FromBase64String(string input)
{
int filteredLength = 0;
foreach (var ch in input)
{
if (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n' || ch == '=')
{
// Skip all whitespaces, line separators and padding symbols
continue;
}
filteredLength += 1;
}
if (filteredLength == 0)
{
return Array.Empty<byte>();
}
var destination = new byte[filteredLength * 6 / 8];
var destinationIndex = 0;
var buffer = 0;
var bitsLeft = 0;
for (var inputIndex = 0; inputIndex < input.Length; inputIndex++)
{
char ch = input[inputIndex];
if (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n' || ch == '=')
{
// Skip all whitespaces, line separators and padding symbols
continue;
}
if (!Base64Alphabet.TryGetValue(ch, out int charIndex))
{
throw new FormatException($"Illegal character at position {inputIndex}: '{ch}'.");
}
buffer <<= 6;
buffer |= charIndex & 63;
bitsLeft += 6;
if (bitsLeft >= 8)
{
destination[destinationIndex++] = (byte)(buffer >> (bitsLeft - 8));
bitsLeft -= 8;
}
}
return destination;
}
private static IReadOnlyDictionary<char, int> BuildAlphabetLookupTable(string alphabet)
{
var dict = new Dictionary<char, int>(alphabet.Length);
for(int index = 0; index < alphabet.Length; index++)
{
dict.Add(alphabet[index], index);
}
return dict;
}
}
}