diff --git a/README.md b/README.md
new file mode 100644
index 0000000..9a5ea64
--- /dev/null
+++ b/README.md
@@ -0,0 +1,4 @@
+otpnet
+======
+
+Google Authenticator OTP in C# (.Net)
\ No newline at end of file
diff --git a/otpnet/LICENSE.txt b/otpnet/LICENSE.txt
new file mode 100644
index 0000000..ef257f3
--- /dev/null
+++ b/otpnet/LICENSE.txt
@@ -0,0 +1,37 @@
+OTPNet is licensed under the terms of Apache 2.0
+
+Copyright [2012] [Nathan Adams]
+
+ 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.
+
+This project was based off of OTPHP (https://github.com/lelag/otphp) by Le Lag
+
+Copyright (c) 2011 Le Lag
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
\ No newline at end of file
diff --git a/otpnet/Libraries/Base32Encoding.cs b/otpnet/Libraries/Base32Encoding.cs
new file mode 100644
index 0000000..f27acff
--- /dev/null
+++ b/otpnet/Libraries/Base32Encoding.cs
@@ -0,0 +1,132 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace OTPNet
+{
+ // http://stackoverflow.com/a/7135008/195722
+ 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");
+ }
+
+ }
+}
diff --git a/otpnet/Libraries/StringExtensions.cs b/otpnet/Libraries/StringExtensions.cs
new file mode 100644
index 0000000..8d8d2f6
--- /dev/null
+++ b/otpnet/Libraries/StringExtensions.cs
@@ -0,0 +1,45 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace OTPNet
+{
+ public static class StringExtensions
+ {
+ /// Returns a string array that contains the substrings in this string that are seperated a given fixed length.
+ /// This string object.
+ /// Size of each substring.
+ /// CASE: length > 0 , RESULT: String is split from left to right.
+ /// CASE: length == 0 , RESULT: String is returned as the only entry in the array.
+ /// CASE: length < 0 , RESULT: String is split from right to left.
+ ///
+ /// String array that has been split into substrings of equal length.
+ ///
+ ///
+ /// string s = "1234567890";
+ /// string[] a = s.Split(4); // a == { "1234", "5678", "90" }
+ ///
+ ///
+ public static string[] Split(this string s, int length)
+ {
+ System.Globalization.StringInfo str = new System.Globalization.StringInfo(s);
+
+ int lengthAbs = Math.Abs(length);
+
+ if (str == null || str.LengthInTextElements == 0 || lengthAbs == 0 || str.LengthInTextElements <= lengthAbs)
+ return new string[] { str.ToString() };
+
+ string[] array = new string[(str.LengthInTextElements % lengthAbs == 0 ? str.LengthInTextElements / lengthAbs : (str.LengthInTextElements / lengthAbs) + 1)];
+
+ if (length > 0)
+ for (int iStr = 0, iArray = 0; iStr < str.LengthInTextElements && iArray < array.Length; iStr += lengthAbs, iArray++)
+ array[iArray] = str.SubstringByTextElements(iStr, (str.LengthInTextElements - iStr < lengthAbs ? str.LengthInTextElements - iStr : lengthAbs));
+ else // if (length < 0)
+ for (int iStr = str.LengthInTextElements - 1, iArray = array.Length - 1; iStr >= 0 && iArray >= 0; iStr -= lengthAbs, iArray--)
+ array[iArray] = str.SubstringByTextElements((iStr - lengthAbs < 0 ? 0 : iStr - lengthAbs + 1), (iStr - lengthAbs < 0 ? iStr + 1 : lengthAbs));
+
+ return array;
+ }
+ }
+}
diff --git a/otpnet/Libraries/Unixtime.cs b/otpnet/Libraries/Unixtime.cs
new file mode 100644
index 0000000..3df6da1
--- /dev/null
+++ b/otpnet/Libraries/Unixtime.cs
@@ -0,0 +1,36 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace OTPNet
+{
+ // Issue reported by larry.ellis
+ // Patch provided by andrea.bertin
+ // https://code.google.com/p/otpnet/issues/detail?id=1
+ class Unixtime
+ {
+ private static readonly DateTime Epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
+ private readonly DateTime date;
+
+ public Unixtime()
+ {
+ this.date = DateTime.UtcNow;
+ }
+
+ public Unixtime(int timestamp)
+ {
+ this.date = Epoch.AddSeconds(timestamp);
+ }
+
+ public DateTime toDateTime()
+ {
+ return this.date;
+ }
+
+ public long toTimeStamp()
+ {
+ return (long)this.date.Subtract(Epoch).TotalSeconds;
+ }
+ }
+}
diff --git a/otpnet/OTP/HOTP.cs b/otpnet/OTP/HOTP.cs
new file mode 100644
index 0000000..43340e5
--- /dev/null
+++ b/otpnet/OTP/HOTP.cs
@@ -0,0 +1,58 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace OTPNet
+{
+ /**
+ * HOTP - One time password generator
+ *
+ * The HOTP class allow for the generation
+ * and verification of one-time password using
+ * the HOTP specified algorithm.
+ *
+ * This class is meant to be compatible with
+ * Google Authenticator
+ *
+ * This class was originally ported from the rotp
+ * ruby library available at https://github.com/mdp/rotp
+ */
+ public class HOTP : OTP
+ {
+
+ public HOTP(string secret)
+ : base(secret, 6, HashAlgorithm.SHA1)
+ {
+
+ }
+
+ /**
+ * Get the password for a specific counter value
+ * @param integer $count the counter which is used to
+ * seed the hmac hash function.
+ * @return integer the One Time Password
+ */
+ public int at(int count)
+ {
+ return this.generateOTP(count);
+ }
+
+ /**
+ * Verify if a password is valid for a specific counter value
+ *
+ * @param integer $otp the one-time password
+ * @param integer $counter the counter value
+ * @return bool true if the counter is valid, false otherwise
+ */
+ public bool verify(int otp, int counter)
+ {
+ return (otp == this.at(counter));
+ }
+
+ public string provisioning_uri(string name, int initial_count)
+ {
+ return "otpauth://hotp/" + name + "?secret=" + this.secret + " &counter=" + initial_count.ToString();
+ }
+ }
+}
diff --git a/otpnet/OTP/OTP.cs b/otpnet/OTP/OTP.cs
new file mode 100644
index 0000000..f4f359a
--- /dev/null
+++ b/otpnet/OTP/OTP.cs
@@ -0,0 +1,150 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Security.Cryptography;
+
+namespace OTPNet
+{
+
+ // Please see LICENSE.txt for information on what license this is released under
+ public enum HashAlgorithm
+ {
+ SHA1
+ }
+
+ // This class was ported from this PHP library https://github.com/lelag/otphp which was ported from a Ruby library
+ /**
+ * One Time Password Generator
+ *
+ * The OTP class allow the generation of one-time
+ * password that is described in rfc 4xxx.
+ *
+ * This is class is meant to be compatible with
+ * Google Authenticator.
+ *
+ * This class was originally ported from the rotp
+ * ruby library available at https://github.com/mdp/rotp
+ */
+ public class OTP
+ {
+ /**
+ * The base32 encoded secret key
+ * @var string
+ */
+ public string secret;
+
+ /**
+ * The algorithm used for the hmac hash function
+ * @var string
+ */
+ public HashAlgorithm digest;
+
+ /**
+ * The number of digits in the one-time password
+ * @var integer
+ */
+ public int digits;
+
+ /**
+ * Constructor for the OTP class
+ * @param string $secret the secret key
+ * @param array $opt options array can contain the
+ * following keys :
+ * @param integer digits : the number of digits in the one time password
+ * Currently Google Authenticator only support 6. Defaults to 6.
+ * @param string digest : the algorithm used for the hmac hash function
+ * Google Authenticator only support sha1. Defaults to sha1
+ *
+ * @return new OTP class.
+ */
+ public OTP(string secret)
+ {
+ this.secret = secret;
+ this.digits = 6;
+ this.digest = HashAlgorithm.SHA1;
+ }
+
+ public OTP(string secret, int digits)
+ {
+ // Issue reported by scox.nz
+ // https://code.google.com/p/otpnet/issues/detail?id=2
+ this.secret = secret;
+ this.digits = digits;
+ this.digest = HashAlgorithm.SHA1;
+ }
+
+ public OTP(string secret, int digits, HashAlgorithm digest)
+ {
+ this.secret = secret;
+ this.digits = digits;
+ this.digest = digest;
+ }
+
+ /**
+ * Generate a one-time password
+ *
+ * @param integer $input : number used to seed the hmac hash function.
+ * This number is usually a counter (HOTP) or calculated based on the current
+ * timestamp (see TOTP class).
+ * @return integer the one-time password
+ */
+ public int generateOTP(Int64 input)
+ {
+
+ HMAC hashgenerator = null;
+ switch (digest)
+ {
+ case HashAlgorithm.SHA1:
+ hashgenerator = new HMACSHA1(Base32Encoding.ToBytes(secret));
+ break;
+ }
+
+
+ hashgenerator.ComputeHash(intToByteString(input));
+ string hash = "";
+
+ foreach(byte b in hashgenerator.Hash)
+ {
+ hash += b.ToString("x2");
+ }
+ List hmac = new List();
+ foreach (string s in hash.Split(2))
+ {
+ hmac.Add(Int32.Parse(s, System.Globalization.NumberStyles.HexNumber));
+ }
+
+ int offset = hmac[19] & 0xf;
+ int code = (hmac[offset + 0] & 0x7F) << 24 |
+ (hmac[offset + 1] & 0xFF) << 16 |
+ (hmac[offset + 2] & 0xFF) << 8 |
+ (hmac[offset + 3] & 0xFF);
+
+ return code % (int)Math.Pow((double)10, (double)this.digits);
+
+ }
+
+ public byte[] intToByteString(Int64 i)
+ {
+ List res = new List();
+
+ while (i != 0)
+ {
+ res.Add((byte)(i & 0xFF));
+ i >>= 8;
+ }
+ int rcount = res.Count;
+ for (int z = 0; z < 8 - rcount; z++)
+ res.Add((byte)0);
+ res.Reverse();
+
+ return res.ToArray();
+ }
+
+
+ }
+
+
+
+
+}
diff --git a/otpnet/OTP/TOTP.cs b/otpnet/OTP/TOTP.cs
new file mode 100644
index 0000000..2b4435a
--- /dev/null
+++ b/otpnet/OTP/TOTP.cs
@@ -0,0 +1,120 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace OTPNet
+{
+ /**
+ * TOTP - One time password generator
+ *
+ * The TOTP class allow for the generation
+ * and verification of one-time password using
+ * the TOTP specified algorithm.
+ *
+ * This class is meant to be compatible with
+ * Google Authenticator
+ *
+ * This class was originally ported from the rotp
+ * ruby library available at https://github.com/mdp/rotp
+ */
+ public class TOTP : OTP
+ {
+
+ /**
+ * The interval in seconds for a one-time password timeframe
+ * Defaults to 30
+ * @var integer
+ */
+ public double interval;
+
+
+ public TOTP(string secret)
+ : base(secret, 6, HashAlgorithm.SHA1)
+ {
+ this.interval = 30;
+ }
+
+ public TOTP(string secret, double interval)
+ : base(secret, 6, HashAlgorithm.SHA1)
+ {
+ this.interval = interval;
+ }
+
+ public TOTP(string secret, double interval, int digits)
+ : base(secret, digits, HashAlgorithm.SHA1)
+ {
+ this.interval = interval;
+ }
+
+ public TOTP(string secret, double interval, int digits, HashAlgorithm algo)
+ : base(secret, digits, algo)
+ {
+ this.interval = interval;
+ }
+
+ /**
+ * Get the password for a specific timestamp value
+ *
+ * @param integer $timestamp the timestamp which is timecoded and
+ * used to seed the hmac hash function.
+ * @return integer the One Time Password
+ */
+ public int at(double timestamp)
+ {
+ return this.generateOTP(this.timecode(timestamp));
+ }
+
+ /**
+ * Get the password for the current timestamp value
+ *
+ * @return integer the current One Time Password
+ */
+ public int now()
+ {
+ return this.at(new Unixtime().toTimeStamp());
+ }
+
+ /**
+ * Verify if a password is valid for a specific counter value
+ *
+ * @param integer $otp the one-time password
+ * @param integer $timestamp the timestamp for the a given time, defaults to current time.
+ * @return bool true if the counter is valid, false otherwise
+ */
+ public bool verify(int otp, double timestamp)
+ {
+ return (otp == this.at(timestamp));
+ }
+
+ public bool verify(int otp)
+ {
+ //calls verify(int, int)
+ return this.verify(otp, new Unixtime().toTimeStamp());
+ }
+
+ /**
+ * Returns the uri for a specific secret for totp method.
+ * Can be encoded as a image for simple configuration in
+ * Google Authenticator.
+ *
+ * @param string $name the name of the account / profile
+ * @return string the uri for the hmac secret
+ */
+ public string provisitioning_uri(string name)
+ {
+ return "otpauth://totp/" + name + "?secret=" + this.secret;
+ }
+
+ /**
+ * Transform a timestamp in a counter based on specified internal
+ *
+ * @param integer $timestamp
+ * @return integer the timecode
+ */
+ public Int64 timecode(double timestamp)
+ {
+ return (Int64)(((((timestamp * 1000)) / (this.interval * 1000))));
+ }
+ }
+}
diff --git a/otpnet/OTPNet.csproj b/otpnet/OTPNet.csproj
new file mode 100644
index 0000000..811448f
--- /dev/null
+++ b/otpnet/OTPNet.csproj
@@ -0,0 +1,59 @@
+
+
+
+ Debug
+ AnyCPU
+ 8.0.30703
+ 2.0
+ {F1FD321D-6F2A-4431-8307-6BF959419DBE}
+ Library
+ Properties
+ OTPNet
+ OTPNet
+ v4.0
+ 512
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/otpnet/OTPNet.sln b/otpnet/OTPNet.sln
new file mode 100644
index 0000000..d3035cc
--- /dev/null
+++ b/otpnet/OTPNet.sln
@@ -0,0 +1,20 @@
+
+Microsoft Visual Studio Solution File, Format Version 11.00
+# Visual Studio 2010
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OTPNet", "OTPNet.csproj", "{F1FD321D-6F2A-4431-8307-6BF959419DBE}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {F1FD321D-6F2A-4431-8307-6BF959419DBE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F1FD321D-6F2A-4431-8307-6BF959419DBE}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F1FD321D-6F2A-4431-8307-6BF959419DBE}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F1FD321D-6F2A-4431-8307-6BF959419DBE}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/otpnet/Properties/AssemblyInfo.cs b/otpnet/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..a289cc7
--- /dev/null
+++ b/otpnet/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("OTPNet")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("OTPNet")]
+[assembly: AssemblyCopyright("Copyright © 2013")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("ff624b9a-844a-4ede-9cdd-87025ddd4201")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/otpnet/obj/Debug/OTPNet.csproj.FileListAbsolute.txt b/otpnet/obj/Debug/OTPNet.csproj.FileListAbsolute.txt
new file mode 100644
index 0000000..c1f5672
--- /dev/null
+++ b/otpnet/obj/Debug/OTPNet.csproj.FileListAbsolute.txt
@@ -0,0 +1,4 @@
+C:\repos\otpnet-google\otpnet\bin\Debug\OTPNet.dll
+C:\repos\otpnet-google\otpnet\bin\Debug\OTPNet.pdb
+C:\repos\otpnet-google\otpnet\obj\Debug\OTPNet.dll
+C:\repos\otpnet-google\otpnet\obj\Debug\OTPNet.pdb
diff --git a/otpnettest/Program.cs b/otpnettest/Program.cs
new file mode 100644
index 0000000..1ab090e
--- /dev/null
+++ b/otpnettest/Program.cs
@@ -0,0 +1,17 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using OTPNet;
+
+namespace otpnettest
+{
+ class Program
+ {
+ static void Main(string[] args)
+ {
+ TOTP t = new TOTP(args[0]);
+ Console.WriteLine("Your OTP = " + t.now().ToString("D6"));
+ }
+ }
+}
diff --git a/otpnettest/Properties/AssemblyInfo.cs b/otpnettest/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..0cf6e35
--- /dev/null
+++ b/otpnettest/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("otpnettest")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("otpnettest")]
+[assembly: AssemblyCopyright("Copyright © 2013")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("9b7c314e-7892-4cf6-8ca8-01ef7420f6a3")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/otpnettest/obj/x86/Debug/otpnettest.csproj.FileListAbsolute.txt b/otpnettest/obj/x86/Debug/otpnettest.csproj.FileListAbsolute.txt
new file mode 100644
index 0000000..e712899
--- /dev/null
+++ b/otpnettest/obj/x86/Debug/otpnettest.csproj.FileListAbsolute.txt
@@ -0,0 +1,7 @@
+C:\repos\otpnet-google\otpnettest\bin\Debug\otpnettest.exe
+C:\repos\otpnet-google\otpnettest\bin\Debug\otpnettest.pdb
+C:\repos\otpnet-google\otpnettest\bin\Debug\OTPNet.dll
+C:\repos\otpnet-google\otpnettest\bin\Debug\OTPNet.pdb
+C:\repos\otpnet-google\otpnettest\obj\x86\Debug\otpnettest.csprojResolveAssemblyReference.cache
+C:\repos\otpnet-google\otpnettest\obj\x86\Debug\otpnettest.exe
+C:\repos\otpnet-google\otpnettest\obj\x86\Debug\otpnettest.pdb
diff --git a/otpnettest/otpnettest.csproj b/otpnettest/otpnettest.csproj
new file mode 100644
index 0000000..5a1e00d
--- /dev/null
+++ b/otpnettest/otpnettest.csproj
@@ -0,0 +1,60 @@
+
+
+
+ Debug
+ x86
+ 8.0.30703
+ 2.0
+ {7F244138-C2C0-4A59-894F-276F97A0029A}
+ Exe
+ Properties
+ otpnettest
+ otpnettest
+ v4.0
+ Client
+ 512
+
+
+ x86
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ x86
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+ ..\otpnet\bin\Debug\OTPNet.dll
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/otpnettest/otpnettest.sln b/otpnettest/otpnettest.sln
new file mode 100644
index 0000000..a799c72
--- /dev/null
+++ b/otpnettest/otpnettest.sln
@@ -0,0 +1,20 @@
+
+Microsoft Visual Studio Solution File, Format Version 11.00
+# Visual Studio 2010
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "otpnettest", "otpnettest.csproj", "{7F244138-C2C0-4A59-894F-276F97A0029A}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|x86 = Debug|x86
+ Release|x86 = Release|x86
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {7F244138-C2C0-4A59-894F-276F97A0029A}.Debug|x86.ActiveCfg = Debug|x86
+ {7F244138-C2C0-4A59-894F-276F97A0029A}.Debug|x86.Build.0 = Debug|x86
+ {7F244138-C2C0-4A59-894F-276F97A0029A}.Release|x86.ActiveCfg = Release|x86
+ {7F244138-C2C0-4A59-894F-276F97A0029A}.Release|x86.Build.0 = Release|x86
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal