/* OtpKeyProv Plugin Copyright (C) 2011-2012 Dominik Reichl This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ using System; using System.Collections.Generic; using System.Text; using System.Diagnostics; using KeePassLib.Utility; namespace OtpKeyProv { public static class EncodingUtil { private const string FmtHex = "Hex"; private const string FmtBase64 = "Base64"; private const string FmtBase32 = "Base32"; private const string FmtUtf8 = "UTF-8"; private const string FmtDec = "Dec"; public static readonly string[] Formats = new string[]{ FmtHex, FmtBase64, FmtUtf8, FmtDec, FmtBase32 }; public static OtpDataFmt? GetOtpDataFormat(String strFmt) { if(strFmt == null) return null; // No assert if(strFmt == FmtHex) return OtpDataFmt.Hex; if(strFmt == FmtBase64) return OtpDataFmt.Base64; if(strFmt == FmtBase32) return OtpDataFmt.Base32; if(strFmt == FmtUtf8) return OtpDataFmt.Utf8; if(strFmt == FmtDec) return OtpDataFmt.Dec; return null; } public static byte[] ParseKey(string strKey, OtpDataFmt fmt) { if(strKey == null) { Debug.Assert(false); return null; } strKey = strKey.Trim(); if(strKey.Length == 0) return null; // No assert if(fmt == OtpDataFmt.Hex) { strKey = strKey.Replace(" ", string.Empty); strKey = strKey.Replace("\t", string.Empty); strKey = strKey.Replace("\r", string.Empty); strKey = strKey.Replace("\n", string.Empty); if((strKey.Length % 2) == 1) strKey = "0" + strKey; return MemUtil.HexStringToByteArray(strKey); } else if(fmt == OtpDataFmt.Base64) { try { return Convert.FromBase64String(strKey); } catch(Exception) { } } else if(fmt == OtpDataFmt.Base32) return ParseBase32(strKey); else if(fmt == OtpDataFmt.Utf8) { try { return StrUtil.Utf8.GetBytes(strKey); } catch(Exception) { } } else if(fmt == OtpDataFmt.Dec) { ulong u; if(ulong.TryParse(strKey, out u)) { byte[] pb = MemUtil.UInt64ToBytes(u); Array.Reverse(pb); // Little endian -> big endian return pb; } } return null; } public static ulong? ParseCounter(string strCounter, OtpDataFmt fmt) { byte[] pb = ParseKey(strCounter, fmt); if(pb == null) return null; if(pb.Length > 8) return null; Array.Reverse(pb); // Big endian -> little endian byte[] pb8 = new byte[8]; Array.Copy(pb, 0, pb8, 0, pb.Length); return MemUtil.BytesToUInt64(pb8); // Little endian } private const string Base32Alph = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; /// /// Decode base32 strings according to RFC 4648. /// private static byte[] ParseBase32(string str) { if((str == null) || ((str.Length % 8) != 0)) return null; List l = new List(); for(int i = 0; i < str.Length; i += 8) { ulong u = 0; int nBits = 0; for(int j = 0; j < 8; ++j) { char ch = char.ToUpper(str[i + j]); if(ch == '=') break; int iValue = Base32Alph.IndexOf(ch); if(iValue < 0) return null; u <<= 5; u += (ulong)iValue; nBits += 5; } int nBitsTooMany = (nBits % 8); u >>= nBitsTooMany; nBits -= nBitsTooMany; Debug.Assert((nBits % 8) == 0); int idxNewBytes = l.Count; while(nBits > 0) { l.Add((byte)(u & 0xFF)); u >>= 8; nBits -= 8; } l.Reverse(idxNewBytes, l.Count - idxNewBytes); } return l.ToArray(); } internal static void SelfTest() { #if DEBUG byte[] pbRes = ParseBase32("MY======"); byte[] pbExp = Encoding.ASCII.GetBytes("f"); if(!MemUtil.ArraysEqual(pbRes, pbExp)) throw new Exception("Base32-1"); pbRes = ParseBase32("MZXQ===="); pbExp = Encoding.ASCII.GetBytes("fo"); if(!MemUtil.ArraysEqual(pbRes, pbExp)) throw new Exception("Base32-2"); pbRes = ParseBase32("MZXW6==="); pbExp = Encoding.ASCII.GetBytes("foo"); if(!MemUtil.ArraysEqual(pbRes, pbExp)) throw new Exception("Base32-3"); pbRes = ParseBase32("MZXW6YQ="); pbExp = Encoding.ASCII.GetBytes("foob"); if(!MemUtil.ArraysEqual(pbRes, pbExp)) throw new Exception("Base32-4"); pbRes = ParseBase32("MZXW6YTB"); pbExp = Encoding.ASCII.GetBytes("fooba"); if(!MemUtil.ArraysEqual(pbRes, pbExp)) throw new Exception("Base32-5"); pbRes = ParseBase32("MZXW6YTBOI======"); pbExp = Encoding.ASCII.GetBytes("foobar"); if(!MemUtil.ArraysEqual(pbRes, pbExp)) throw new Exception("Base32-6"); pbRes = ParseBase32("JNSXSIDQOJXXM2LEMVZCAYTBONSWIIDPNYQG63TFFV2GS3LFEBYGC43TO5XXEZDTFY======"); pbExp = Encoding.ASCII.GetBytes("Key provider based on one-time passwords."); if(!MemUtil.ArraysEqual(pbRes, pbExp)) throw new Exception("Base32-7"); #endif } } }