using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Numerics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: CLSCompliant(true)] [assembly: AssemblyTitle("SimpleBase")] [assembly: AssemblyDescription("Base32/Base58 Encoding/Decoding Library")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("SimpleBase")] [assembly: AssemblyCopyright("Copyright © 2014-2016 Sedat Kapanoglu")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("20592f17-d497-4dc4-9483-52911171f130")] [assembly: AssemblyFileVersion("1.3.1")] [assembly: InternalsVisibleTo("SimpleBaseTest")] [assembly: TargetFramework(".NETStandard,Version=v1.3", FrameworkDisplayName = "")] [assembly: AssemblyVersion("1.3.1.0")] namespace SimpleBase; public static class Base16 { private const byte upperCaseOffset = 55; private const byte lowerCaseOffset = 87; private const byte numberOffset = 48; private const byte upperNumberDiff = 7; private const byte lowerUpperDiff = 32; private const string lowerAlphabet = "0123456789abcdef"; private const string upperAlphabet = "0123456789ABCDEF"; public static string EncodeUpper(byte[] bytes) { return encode(bytes, "0123456789ABCDEF"); } public static string EncodeLower(byte[] bytes) { return encode(bytes, "0123456789abcdef"); } private unsafe static string encode(byte[] bytes, string alphabet) { Require.NotNull(bytes, "bytes"); int num = bytes.Length; if (num == 0) { return string.Empty; } string text = new string('\0', num * 2); fixed (char* ptr = text) { fixed (byte* ptr2 = bytes) { fixed (char* ptr3 = alphabet) { char* ptr4 = ptr; char* ptr5 = ptr3; byte* ptr6 = ptr2; for (byte* ptr7 = Pointer.Offset(ptr6, num); ptr6 != ptr7; ptr6++) { int num2 = *ptr6; *(ptr4++) = ptr5[num2 >> 4]; *(ptr4++) = ptr5[num2 & 0xF]; } } } } return text; } public unsafe static byte[] Decode(string text) { Require.NotNull(text, "text"); int length = text.Length; if (length == 0) { return new byte[0]; } if (length % 2 != 0) { throw new ArgumentException("Text cannot be odd length", "text"); } byte[] array = new byte[length / 2]; fixed (byte* ptr = array) { fixed (char* ptr2 = text) { byte* ptr3 = ptr; char* ptr4 = ptr2; char* ptr5 = Pointer.Offset(ptr4, length); while (ptr4 != ptr5) { char num = *(ptr4++); validateHex(num); int hexByte = getHexByte(num); char num2 = *(ptr4++); validateHex(num2); int hexByte2 = getHexByte(num2); *ptr3 = (byte)((hexByte << 4) | hexByte2); ptr3++; } } } return array; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int getHexByte(int character) { int num = character - 48; if (num < 10) { return num; } num -= 7; if (num < 16) { return num; } return num - 32; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static void validateHex(char c) { if ((c < '0' || c > '9') && (c < 'A' || c > 'F') && (c < 'a' || c > 'f')) { throw new InvalidOperationException(string.Format("Invalid hex character: ", c)); } } } public sealed class Base32 { public static readonly Base32 Crockford = new Base32(Base32Alphabet.Crockford); public static readonly Base32 Rfc4648 = new Base32(Base32Alphabet.Rfc4648); public static readonly Base32 ExtendedHex = new Base32(Base32Alphabet.ExtendedHex); private const int bitsPerByte = 8; private const int bitsPerChar = 5; private const char paddingChar = '='; private Base32Alphabet alphabet; private static readonly int[] paddingRemainders = new int[5] { 0, 2, 4, 5, 7 }; public Base32(Base32Alphabet alphabet) { this.alphabet = alphabet; } public unsafe string Encode(byte[] bytes, bool padding) { Require.NotNull(bytes, "bytes"); int num = bytes.Length; if (num == 0) { return string.Empty; } int num2 = ((num - 1) / 5 + 1) * 8; char[] array = new char[num2]; fixed (byte* ptr = bytes) { fixed (char* encodingTable = alphabet.EncodingTable) { fixed (char* ptr2 = array) { char* ptr3 = encodingTable; char* ptr4 = ptr2; char* ptr5 = ptr2 + num2; byte* ptr6 = ptr; int num3 = 8; int num4 = *ptr6; byte* ptr7 = Pointer.Offset(ptr6, num); while (ptr6 != ptr7) { int num5; if (num3 > 5) { num3 -= 5; num5 = num4 >> num3; *(ptr4++) = ptr3[num5]; num4 &= (1 << num3) - 1; } int num6 = 5 - num3; num3 = 8 - num6; num5 = num4 << num6; if (++ptr6 != ptr7) { num4 = *ptr6; num5 |= num4 >> num3; num4 &= (1 << num3) - 1; } *(ptr4++) = ptr3[num5]; } if (padding) { while (ptr4 != ptr5) { *(ptr4++) = '='; } } return new string(ptr2, 0, (int)(ptr4 - ptr2)); } } } } public unsafe byte[] Decode(string text) { Require.NotNull(text, "base32"); text = text.TrimEnd(new char[1] { '=' }); int length = text.Length; if (length == 0) { return new byte[0]; } byte[] decodingTable = alphabet.DecodingTable; int num = decodingTable.Length; int num2 = 8; byte[] array = new byte[length * 5 / 8]; int num3 = 0; fixed (byte* ptr = array) { fixed (char* ptr2 = text) { fixed (byte* ptr3 = decodingTable) { byte* ptr4 = ptr; byte* ptr5 = ptr3; char* ptr6 = ptr2; char* ptr7 = Pointer.Offset(ptr2, length); while (ptr6 != ptr7) { char c = *(ptr6++); if (c >= num) { throw invalidInput(c); } int num4 = ptr5[(int)c] - 1; if (num4 < 0) { throw invalidInput(c); } if (num2 > 5) { num2 -= 5; num3 |= num4 << num2; continue; } int num5 = 5 - num2; num3 |= num4 >> num5; *(ptr4++) = (byte)num3; num4 &= (1 << num5) - 1; num2 = 8 - num5; num3 = num4 << num2; } } } } return array; } private static ArgumentException invalidInput(char c) { return new ArgumentException($"Invalid character value in input: 0x{(int)c:X}", "c"); } } public class Base32Alphabet { public const int Length = 32; private const char highestAsciiCharSupported = 'z'; private static Base32Alphabet crockford = new CrockfordBase32Alphabet(); private static Base32Alphabet rfc4648 = new Base32Alphabet("ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"); private static Base32Alphabet extendedHex = new Base32Alphabet("0123456789ABCDEFGHIJKLMNOPQRSTUV"); public static Base32Alphabet Crockford => crockford; public static Base32Alphabet Rfc4648 => rfc4648; public static Base32Alphabet ExtendedHex => extendedHex; public char[] EncodingTable { get; private set; } public byte[] DecodingTable { get; protected set; } public Base32Alphabet(string chars) { EncodingTable = chars.ToCharArray(); createDecodingTable(chars); } private void createDecodingTable(string chars) { byte[] array = new byte[123]; int length = chars.Length; for (int i = 0; i < length; i++) { char c = chars[i]; byte b = (array[(uint)c] = (byte)(i + 1)); array[(uint)char.ToLowerInvariant(c)] = b; } DecodingTable = array; } } public sealed class Base58 { public static readonly Base58 Bitcoin = new Base58(Base58Alphabet.Bitcoin); public static readonly Base58 Ripple = new Base58(Base58Alphabet.Ripple); public static readonly Base58 Flickr = new Base58(Base58Alphabet.Flickr); private Base58Alphabet alphabet; private static readonly BigInteger baseLength = 58; public Base58(Base58Alphabet alphabet) { Require.NotNull(alphabet, "alphabet"); this.alphabet = alphabet; } public unsafe string Encode(byte[] bytes) { Require.NotNull(bytes, "buffer"); int num = bytes.Length; if (num == 0) { return string.Empty; } fixed (byte* ptr = bytes) { fixed (char* value = alphabet.Value) { byte* ptr2 = ptr; byte* ptr3; for (ptr3 = Pointer.Offset(ptr, num); ptr2 != ptr3 && *ptr2 == 0; ptr2++) { } int num2 = (int)(ptr2 - ptr); char c = *value; if (ptr2 == ptr3) { return new string(c, num2); } int num3 = num * 138 / 100 + 1; int num4 = 0; fixed (byte* ptr4 = new byte[num3]) { byte* ptr5 = ptr4 + num3 - 1; for (; ptr2 != ptr3; ptr2++) { int num5 = *ptr2; int i = 0; byte* ptr6 = ptr5; for (; num5 != 0 || i < num4; i++) { if (ptr6 < ptr4) { break; } num5 += 256 * *ptr6; *ptr6 = (byte)(num5 % 58); num5 /= 58; ptr6--; } num4 = i; } ptr5++; byte* ptr7; for (ptr7 = ptr4; ptr7 != ptr5 && *ptr7 == 0; ptr7++) { } int count = num2 + (int)(ptr5 - ptr7); string text = new string(c, count); fixed (char* ptr8 = text) { char* ptr9 = ptr8 + num2; while (ptr7 != ptr5) { *(ptr9++) = value[(int)(*(ptr7++))]; } } return text; } } } } public unsafe byte[] Decode(string text) { Require.NotNull(text, "text"); int length = text.Length; if (length == 0) { return new byte[0]; } fixed (char* ptr = text) { char* ptr2 = Pointer.Offset(ptr, length); char* ptr3 = ptr; for (char c = alphabet[0]; *ptr3 == c && ptr3 != ptr2; ptr3++) { } int num = (int)(ptr3 - ptr); if (ptr3 == ptr2) { return new byte[num]; } int num2 = length * 733 / 1000 + 1; byte[] array = new byte[num2]; fixed (byte* ptr4 = array) { byte* ptr5 = ptr4 + num2 - 1; while (ptr3 != ptr2) { int num3 = alphabet[*(ptr3++)]; for (byte* ptr6 = ptr5; ptr6 >= ptr4; ptr6--) { num3 += 58 * *ptr6; *ptr6 = (byte)num3; num3 /= 256; } } byte* ptr7; for (ptr7 = ptr4; ptr7 != ptr5 && *ptr7 == 0; ptr7++) { } int num4 = (int)(ptr5 - ptr7) + 1; if (num4 == num2) { return array; } byte[] array2 = new byte[num + num4]; Array.Copy(array, (int)(ptr7 - ptr4), array2, num, num4); return array2; } } } } public sealed class Base58Alphabet { private static readonly Base58Alphabet bitcoin = new Base58Alphabet("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"); private static readonly Base58Alphabet ripple = new Base58Alphabet("rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz"); private static readonly Base58Alphabet flickr = new Base58Alphabet("123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ"); public const int Length = 58; private Dictionary reverseLookupTable; public string Value; public static Base58Alphabet Bitcoin => bitcoin; public static Base58Alphabet Ripple => ripple; public static Base58Alphabet Flickr => flickr; public char this[int index] => Value[index]; public int this[char c] { get { if (!reverseLookupTable.TryGetValue(c, out var value)) { throw new InvalidOperationException($"invalid character: {c}"); } return value; } } public Base58Alphabet(string text) { Require.NotNull(text, "text"); if (text.Length != 58) { throw new ArgumentException("Base58 alphabets need to be 58-characters long", "text"); } Value = text; reverseLookupTable = text.Select((char c, int i) => new KeyValuePair(c, i)).ToDictionary((KeyValuePair i) => i.Key, (KeyValuePair i) => i.Value); } } internal sealed class CrockfordBase32Alphabet : Base32Alphabet { public CrockfordBase32Alphabet() : base("0123456789ABCDEFGHJKMNPQRSTVWXYZ") { byte[] decodingTable = base.DecodingTable; map(decodingTable, 'O', '0'); map(decodingTable, 'I', '1'); map(decodingTable, 'L', '1'); } private static void map(byte[] buffer, char source, char destination) { byte b = (buffer[(uint)source] = buffer[(uint)destination]); buffer[(uint)char.ToLowerInvariant(source)] = b; } } internal static class Pointer { [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe static byte* Offset(byte* ptr, int length) { byte* ptr2 = ptr + length; if (length < 0 || ptr2 < ptr) { throw new InvalidOperationException("Buffer overflow -- buffer too large?"); } return ptr2; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe static char* Offset(char* ptr, int length) { char* ptr2 = ptr + length; if (length < 0 || ptr2 < ptr) { throw new InvalidOperationException("Buffer overflow -- buffer too large?"); } return ptr2; } } internal static class Require { [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static void NotNull(T value, string argName) where T : class { if (value == null) { throw new ArgumentNullException(argName); } } }