-
Notifications
You must be signed in to change notification settings - Fork 151
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
1,174 additions
and
126 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,364 @@ | ||
// Copyright 2015-2018 The NATS Authors | ||
// Licensed 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. | ||
|
||
using System; | ||
using System.Text; | ||
|
||
namespace NATS.Client | ||
{ | ||
public static class EncodingUtils | ||
{ | ||
public static string ToBase64UrlEncoded(byte[] bytes) | ||
{ | ||
string s = Convert.ToBase64String(bytes); | ||
int at = s.IndexOf('='); | ||
if (at != -1) | ||
{ | ||
s = s.Substring(0, at); | ||
} | ||
return s.Replace("/", "_").Replace("+", "-"); | ||
} | ||
|
||
public static string FromBase64UrlEncoded(string s) | ||
{ | ||
try | ||
{ | ||
return Encoding.ASCII.GetString(Convert.FromBase64String(s)); | ||
} | ||
catch (System.FormatException) | ||
{ | ||
// maybe wasn't padded correctly? | ||
try | ||
{ | ||
return Encoding.ASCII.GetString(Convert.FromBase64String(s + "=")); | ||
} | ||
catch (System.FormatException) | ||
{ | ||
// maybe wasn't padded correctly? | ||
return Encoding.ASCII.GetString(Convert.FromBase64String(s + "==")); | ||
} | ||
} | ||
} | ||
} | ||
|
||
public class Base32 | ||
{ | ||
public static byte[] Decode(string input) | ||
{ | ||
if (string.IsNullOrEmpty(input)) | ||
{ | ||
throw new ArgumentNullException("input"); | ||
} | ||
|
||
input = input.TrimEnd('='); //remove padding characters | ||
int byteCount = input.Length * 5 / 8; //this must be TRUNCATED | ||
byte[] returnArray = new byte[byteCount]; | ||
|
||
byte curByte = 0, bitsRemaining = 8; | ||
int mask = 0, arrayIndex = 0; | ||
|
||
foreach (char c in input) | ||
{ | ||
int cValue = CharToValue(c); | ||
|
||
if (bitsRemaining > 5) | ||
{ | ||
mask = cValue << (bitsRemaining - 5); | ||
curByte = (byte)(curByte | mask); | ||
bitsRemaining -= 5; | ||
} | ||
else | ||
{ | ||
mask = cValue >> (5 - bitsRemaining); | ||
curByte = (byte)(curByte | mask); | ||
returnArray[arrayIndex++] = curByte; | ||
curByte = (byte)(cValue << (3 + bitsRemaining)); | ||
bitsRemaining += 3; | ||
} | ||
} | ||
|
||
//if we didn't end with a full byte | ||
if (arrayIndex != byteCount) | ||
{ | ||
returnArray[arrayIndex] = curByte; | ||
} | ||
|
||
return returnArray; | ||
} | ||
|
||
public static string Encode(byte[] input) | ||
{ | ||
if (input == null || input.Length == 0) | ||
{ | ||
throw new ArgumentNullException("input"); | ||
} | ||
|
||
int charCount = (int)Math.Ceiling(input.Length / 5d) * 8; | ||
char[] returnArray = new char[charCount]; | ||
|
||
byte nextChar = 0, bitsRemaining = 5; | ||
int arrayIndex = 0; | ||
|
||
foreach (byte b in input) | ||
{ | ||
nextChar = (byte)(nextChar | (b >> (8 - bitsRemaining))); | ||
returnArray[arrayIndex++] = ValueToChar(nextChar); | ||
|
||
if (bitsRemaining < 4) | ||
{ | ||
nextChar = (byte)((b >> (3 - bitsRemaining)) & 31); | ||
returnArray[arrayIndex++] = ValueToChar(nextChar); | ||
bitsRemaining += 5; | ||
} | ||
|
||
bitsRemaining -= 3; | ||
nextChar = (byte)((b << bitsRemaining) & 31); | ||
} | ||
|
||
// if we didn't end with a full char | ||
if (arrayIndex < charCount) | ||
{ | ||
returnArray[arrayIndex++] = ValueToChar(nextChar); | ||
// NOTE: Base32 padding omitted | ||
} | ||
|
||
return new string(returnArray, 0, arrayIndex); | ||
} | ||
|
||
private static int CharToValue(char c) | ||
{ | ||
int value = (int)c; | ||
|
||
//65-90 == uppercase letters | ||
if (value < 91 && value > 64) return value - 65; | ||
|
||
//50-55 == numbers 2-7 | ||
if (value < 56 && value > 49) return value - 24; | ||
|
||
//97-122 == lowercase letters | ||
if (value < 123 && value > 96) return value - 97; | ||
|
||
throw new ArgumentException("Character is not a Base32 character.", "c"); | ||
} | ||
|
||
private static char ValueToChar(byte b) | ||
{ | ||
if (b < 26) return (char)(b + 65); | ||
if (b < 32) return (char)(b + 24); | ||
throw new ArgumentException("Byte is not a value Base32 value.", "b"); | ||
} | ||
|
||
// ---------------------------------------------------------------------------------------------------- | ||
// ---------------------------------------------------------------------------------------------------- | ||
// ---------------------------------------------------------------------------------------------------- | ||
// ---------------------------------------------------------------------------------------------------- | ||
// ---------------------------------------------------------------------------------------------------- | ||
// ---------------------------------------------------------------------------------------------------- | ||
// ---------------------------------------------------------------------------------------------------- | ||
|
||
/// <summary> | ||
/// Size of the regular byte in bits | ||
/// </summary> | ||
private const int InByteSize = 8; | ||
|
||
/// <summary> | ||
/// Size of converted byte in bits | ||
/// </summary> | ||
private const int OutByteSize = 5; | ||
|
||
/// <summary> | ||
/// Alphabet | ||
/// </summary> | ||
private const string Base32Alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; | ||
|
||
/// <summary> | ||
/// Convert byte array to Base32 format | ||
/// </summary> | ||
/// <param name="bytes">An array of bytes to convert to Base32 format</param> | ||
/// <returns>Returns a string representing byte array</returns> | ||
public static string ToBase32String(byte[] bytes) | ||
{ | ||
// Check if byte array is null | ||
if (bytes == null) | ||
{ | ||
return null; | ||
} | ||
// Check if empty | ||
else if (bytes.Length == 0) | ||
{ | ||
return string.Empty; | ||
} | ||
|
||
// Prepare container for the final value | ||
StringBuilder builder = new StringBuilder(bytes.Length * InByteSize / OutByteSize); | ||
|
||
// Position in the input buffer | ||
int bytesPosition = 0; | ||
|
||
// Offset inside a single byte that <bytesPosition> points to (from left to right) | ||
// 0 - highest bit, 7 - lowest bit | ||
int bytesSubPosition = 0; | ||
|
||
// Byte to look up in the dictionary | ||
byte outputBase32Byte = 0; | ||
|
||
// The number of bits filled in the current output byte | ||
int outputBase32BytePosition = 0; | ||
|
||
// Iterate through input buffer until we reach past the end of it | ||
while (bytesPosition < bytes.Length) | ||
{ | ||
// Calculate the number of bits we can extract out of current input byte to fill missing bits in the output byte | ||
int bitsAvailableInByte = Math.Min(InByteSize - bytesSubPosition, OutByteSize - outputBase32BytePosition); | ||
|
||
// Make space in the output byte | ||
outputBase32Byte <<= bitsAvailableInByte; | ||
|
||
// Extract the part of the input byte and move it to the output byte | ||
outputBase32Byte |= (byte)(bytes[bytesPosition] >> (InByteSize - (bytesSubPosition + bitsAvailableInByte))); | ||
|
||
// Update current sub-byte position | ||
bytesSubPosition += bitsAvailableInByte; | ||
|
||
// Check overflow | ||
if (bytesSubPosition >= InByteSize) | ||
{ | ||
// Move to the next byte | ||
bytesPosition++; | ||
bytesSubPosition = 0; | ||
} | ||
|
||
// Update current base32 byte completion | ||
outputBase32BytePosition += bitsAvailableInByte; | ||
|
||
// Check overflow or end of input array | ||
if (outputBase32BytePosition >= OutByteSize) | ||
{ | ||
// Drop the overflow bits | ||
outputBase32Byte &= 0x1F; // 0x1F = 00011111 in binary | ||
|
||
// Add current Base32 byte and convert it to character | ||
builder.Append(Base32Alphabet[outputBase32Byte]); | ||
|
||
// Move to the next byte | ||
outputBase32BytePosition = 0; | ||
} | ||
} | ||
|
||
// Check if we have a remainder | ||
if (outputBase32BytePosition > 0) | ||
{ | ||
// Move to the right bits | ||
outputBase32Byte <<= (OutByteSize - outputBase32BytePosition); | ||
|
||
// Drop the overflow bits | ||
outputBase32Byte &= 0x1F; // 0x1F = 00011111 in binary | ||
|
||
// Add current Base32 byte and convert it to character | ||
builder.Append(Base32Alphabet[outputBase32Byte]); | ||
} | ||
|
||
return builder.ToString(); | ||
} | ||
|
||
/// <summary> | ||
/// Convert base32 string to array of bytes | ||
/// </summary> | ||
/// <param name="base32String">Base32 string to convert</param> | ||
/// <returns>Returns a byte array converted from the string</returns> | ||
public static byte[] FromBase32String(string base32String) | ||
{ | ||
// Check if string is null | ||
if (base32String == null) | ||
{ | ||
return null; | ||
} | ||
// Check if empty | ||
else if (base32String == string.Empty) | ||
{ | ||
return new byte[0]; | ||
} | ||
|
||
// Convert to upper-case | ||
string base32StringUpperCase = base32String.ToUpperInvariant(); | ||
|
||
// Prepare output byte array | ||
byte[] outputBytes = new byte[base32StringUpperCase.Length * OutByteSize / InByteSize]; | ||
|
||
// Check the size | ||
if (outputBytes.Length == 0) | ||
{ | ||
throw new ArgumentException("Specified string is not valid Base32 format because it doesn''t have enough data to construct a complete byte array"); | ||
} | ||
|
||
// Position in the string | ||
int base32Position = 0; | ||
|
||
// Offset inside the character in the string | ||
int base32SubPosition = 0; | ||
|
||
// Position within outputBytes array | ||
int outputBytePosition = 0; | ||
|
||
// The number of bits filled in the current output byte | ||
int outputByteSubPosition = 0; | ||
|
||
// Normally we would iterate on the input array but in this case we actually iterate on the output array | ||
// We do it because output array doesn''t have overflow bits, while input does and it will cause output array overflow if we don''t stop in time | ||
while (outputBytePosition < outputBytes.Length) | ||
{ | ||
// Look up current character in the dictionary to convert it to byte | ||
int currentBase32Byte = Base32Alphabet.IndexOf(base32StringUpperCase[base32Position]); | ||
|
||
// Check if found | ||
if (currentBase32Byte < 0) | ||
{ | ||
throw new ArgumentException(string.Format("Specified string is not valid Base32 format because character \"{0}\" does not exist in Base32 alphabet", base32String[base32Position])); | ||
} | ||
|
||
// Calculate the number of bits we can extract out of current input character to fill missing bits in the output byte | ||
int bitsAvailableInByte = Math.Min(OutByteSize - base32SubPosition, InByteSize - outputByteSubPosition); | ||
|
||
// Make space in the output byte | ||
outputBytes[outputBytePosition] <<= bitsAvailableInByte; | ||
|
||
// Extract the part of the input character and move it to the output byte | ||
outputBytes[outputBytePosition] |= (byte)(currentBase32Byte >> (OutByteSize - (base32SubPosition + bitsAvailableInByte))); | ||
|
||
// Update current sub-byte position | ||
outputByteSubPosition += bitsAvailableInByte; | ||
|
||
// Check overflow | ||
if (outputByteSubPosition >= InByteSize) | ||
{ | ||
// Move to the next byte | ||
outputBytePosition++; | ||
outputByteSubPosition = 0; | ||
} | ||
|
||
// Update current base32 byte completion | ||
base32SubPosition += bitsAvailableInByte; | ||
|
||
// Check overflow or end of input array | ||
if (base32SubPosition >= OutByteSize) | ||
{ | ||
// Move to the next character | ||
base32Position++; | ||
base32SubPosition = 0; | ||
} | ||
} | ||
|
||
return outputBytes; | ||
} | ||
} | ||
} |
Oops, something went wrong.