Base32 Decoding

51,060

Solution 1

I had a need for a base32 encoder/decoder, so I spent a couple hours this afternoon throwing this together. I believe it conforms to the standards listed here: https://www.rfc-editor.org/rfc/rfc4648#section-6.

public class Base32Encoding
{
    public static byte[] ToBytes(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 ToString(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);
            while (arrayIndex != charCount) returnArray[arrayIndex++] = '='; //padding
        }

        return new string(returnArray);
    }

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

}

Solution 2

Check this FromBase32String implementation for .NET found here.


Edit: The above link was dead; you can find an archived copy at archive.org

The actual code read:

using System;
using System.Text;

public sealed class Base32 {

      // the valid chars for the encoding
      private static string ValidChars = "QAZ2WSX3" + "EDC4RFV5" + "TGB6YHN7" + "UJM8K9LP";

      /// <summary>
      /// Converts an array of bytes to a Base32-k string.
      /// </summary>
      public static string ToBase32String(byte[] bytes) {
            StringBuilder sb = new StringBuilder();         // holds the base32 chars
            byte index;
            int hi = 5;
            int currentByte = 0;

            while (currentByte < bytes.Length) {
                  // do we need to use the next byte?
                  if (hi > 8) {
                        // get the last piece from the current byte, shift it to the right
                        // and increment the byte counter
                        index = (byte)(bytes[currentByte++] >> (hi - 5));
                        if (currentByte != bytes.Length) {
                              // if we are not at the end, get the first piece from
                              // the next byte, clear it and shift it to the left
                              index = (byte)(((byte)(bytes[currentByte] << (16 - hi)) >> 3) | index);
                        }

                        hi -= 3;
                  } else if(hi == 8) { 
                        index = (byte)(bytes[currentByte++] >> 3);
                        hi -= 3; 
                  } else {

                        // simply get the stuff from the current byte
                        index = (byte)((byte)(bytes[currentByte] << (8 - hi)) >> 3);
                        hi += 5;
                  }

                  sb.Append(ValidChars[index]);
            }

            return sb.ToString();
      }


      /// <summary>
      /// Converts a Base32-k string into an array of bytes.
      /// </summary>
      /// <exception cref="System.ArgumentException">
      /// Input string <paramref name="s">s</paramref> contains invalid Base32-k characters.
      /// </exception>
      public static byte[] FromBase32String(string str) {
            int numBytes = str.Length * 5 / 8;
            byte[] bytes = new Byte[numBytes];

            // all UPPERCASE chars
            str = str.ToUpper();

            int bit_buffer;
            int currentCharIndex;
            int bits_in_buffer;

            if (str.Length < 3) {
                  bytes[0] = (byte)(ValidChars.IndexOf(str[0]) | ValidChars.IndexOf(str[1]) << 5);
                  return bytes;
            }

            bit_buffer = (ValidChars.IndexOf(str[0]) | ValidChars.IndexOf(str[1]) << 5);
            bits_in_buffer = 10;
            currentCharIndex = 2;
            for (int i = 0; i < bytes.Length; i++) {
                  bytes[i] = (byte)bit_buffer;
                  bit_buffer >>= 8;
                  bits_in_buffer -= 8;
                  while (bits_in_buffer < 8 && currentCharIndex < str.Length) {
                        bit_buffer |= ValidChars.IndexOf(str[currentCharIndex++]) << bits_in_buffer;
                        bits_in_buffer += 5;
                  }
            }

            return bytes;
      }
}

Solution 3

This is a really old question, but I happened to stumble on it wanting the same thing for OTP tokens. It turns out that there is base 32 functionality built into the OTP.NET package on NuGet:

Base32Encoding.ToBytes("(your base 32 string here)")

The reverse is also possible:

Base32Encoding.ToString(new byte[] { /* your bytes here */ })

Solution 4

Here are my functions for encoding and decoding. I feel that they are much shorter and concise than the other suggestions. So if you need a small one, try these.

public static string BytesToBase32(byte[] bytes) {
    const string alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
    string output = "";
    for (int bitIndex = 0; bitIndex < bytes.Length * 8; bitIndex += 5) {
        int dualbyte = bytes[bitIndex / 8] << 8;
        if (bitIndex / 8 + 1 < bytes.Length)
            dualbyte |= bytes[bitIndex / 8 + 1];
        dualbyte = 0x1f & (dualbyte >> (16 - bitIndex % 8 - 5));
        output += alphabet[dualbyte];
    }

    return output;
}

public static byte[] Base32ToBytes(string base32) {
    const string alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
    List<byte> output = new List<byte>();
    char[] bytes = base32.ToCharArray();
    for (int bitIndex = 0; bitIndex < base32.Length * 5; bitIndex += 8) {
        int dualbyte = alphabet.IndexOf(bytes[bitIndex / 5]) << 10;
        if (bitIndex / 5 + 1 < bytes.Length)
            dualbyte |= alphabet.IndexOf(bytes[bitIndex / 5 + 1]) << 5;
        if (bitIndex / 5 + 2 < bytes.Length)
            dualbyte |= alphabet.IndexOf(bytes[bitIndex / 5 + 2]);

        dualbyte = 0xff & (dualbyte >> (15 - bitIndex % 5 - 8));
        output.Add((byte)(dualbyte));
    }
    return output.ToArray();
}

Solution 5

I've written some flexible standards based implementations of various Base32 and Base64 encoding/decoding methods. Notably: base64url (per rfc4648) and its base32 equivalent.

By default the Base32Url class encodes with only the characters A to Z and 2 to 7. No hyphens, underscores, pluses, slashes or equals are used, making it usable as a URL token in almost all circumstances. Base32Url also supports custom alphabets, case sensitivity/insensitivity, padding/no-padding etc.

This is posted up on code project.

Share:
51,060
trampster
Author by

trampster

I am the creator of JsonSrcGen https://github.com/trampster/JsonSrcGen a high performance .net Json Library that uses .net 5 Source Generators

Updated on July 09, 2022

Comments

  • trampster
    trampster almost 2 years

    I have a base32 string which I need to convert to a byte array. And I'm having trouble finding a conversion method in the .NET framework. I can find methods for base64 but not for base32.

    Convert.FromBase64String – something like this for base32 would be perfect.

    Is there such a method in the framework or do I have to roll my own?

    • Jamie
      Jamie almost 3 years
      I know this is from forever ago, but you should consider changing the accepted answer. If someone blindly just looks at the accepted answer without digging further (their fault, I know), then they would be setting themselves up for some hurt.
  • Daniel LeCheminant
    Daniel LeCheminant over 15 years
    CharToValue can be made much simpler using casting and subtraction ;-] For example, 'b' - 'a' = 1
  • leppie
    leppie over 15 years
    CharToValue is a WTF! Also whats the use of a decoder without an encoder?
  • Nick
    Nick over 12 years
    Does not give me the expected results
  • Devin
    Devin almost 12 years
    +1 Awesome code. I have been looking for a good base 32 encoder/decoder. I set up a bunch of unit tests from the RFC test vectors and nothing here (that I had tried) passed them all. Yours did first shot. Nice job. I am surprised there aren't more upvotes.
  • Darkthread
    Darkthread over 11 years
    My suggestion: "charCount = (int)Math.Ceiling(input.Length / 5d) * 8;" should be "(int)Math.Ceiling(input.Length / 5d * 8)" to remove redundant "=" padding.
  • Albireo
    Albireo over 10 years
    Hello, I used your code in an application I'm building, may I re-license it under the MIT license (with due credit)? You can find the code at github.com/kappa7194/otp/blob/master/Albireo.Otp.Library/…
  • Dirk Bester
    Dirk Bester about 10 years
    Hello, I am using your code and would like to license it under opengl-tutorial.org/download
  • Ka0s
    Ka0s over 9 years
    Thank you, this is the first working answer I find after searching too long. Great code
  • spacer
    spacer about 7 years
    As said on the original page, this is not standard Base32 encoding but it is nice if you need "obfuscated" Base32. +1
  • spacer
    spacer about 7 years
    Well, FromBase32String() is not throwing ArgumentException(or any other) for invalid characters - because IndexOfdoesn't either. IndexOfis returning -1 if character is not found.
  • spacer
    spacer about 7 years
    A solution would be to define an extension method for string, e.g. "IndexOfEx", which would throw the exception instead of returning -1.
  • sidon
    sidon over 6 years
    I like how easy to read this alg is. However decoding appends zero byte at the end if number of source bytes is not multiply of 5
  • ogggre
    ogggre over 4 years
    BEWARE of this implementation. It does not comply to any standards of interoperability, and literally poisoned the whole .NET ecosystem. It goes kaboom on some input strings. What you really need is something complying to RFC 4648.
  • Taylor Buchanan
    Taylor Buchanan over 4 years
    The source of Otp.NET points to Shane's answer on this question...
  • Joe Skeen
    Joe Skeen over 4 years
    Good to know @TaylorBuchanan! I think it's still nice to know that there's a library for it now, so no need to go reinventing it ;)
  • Jamie
    Jamie almost 3 years
    Hopefully, people will look at the second solution instead of blindly going with this one.
  • Patrick
    Patrick over 2 years
    change the for contition to bitIndex / 5 + 1 < bytes.Length