summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--OATH.Net.Test/Base32Tests.cs297
-rw-r--r--OATH.Net.Test/KeyTests.cs32
-rw-r--r--OATH.Net.Test/OATH.Net.Test.csproj1
-rw-r--r--OATH.Net/Base32.cs118
-rw-r--r--OATH.Net/CounterBasedOtpGenerator.cs2
-rw-r--r--OATH.Net/Key.cs4
-rw-r--r--OATH.Net/OATH.Net.csproj1
7 files changed, 440 insertions, 15 deletions
diff --git a/OATH.Net.Test/Base32Tests.cs b/OATH.Net.Test/Base32Tests.cs
new file mode 100644
index 0000000..6f928af
--- /dev/null
+++ b/OATH.Net.Test/Base32Tests.cs
@@ -0,0 +1,297 @@
+//------------------------------------------------------------------------------------
+// <copyright file="Base32Tests.cs" company="Stephen Jennings">
+// Copyright 2011 Stephen Jennings. Licensed under the Apache License, Version 2.0.
+// </copyright>
+//------------------------------------------------------------------------------------
+
+namespace OathNet.Test
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Text;
+ using NUnit.Framework;
+
+ public class Base32Tests
+ {
+ [Test]
+ public void ToBase32_10_bytes_1()
+ {
+ var binary = new byte[]
+ {
+ 0xDE, 0xAD, 0xBE, 0xEF, 0x48,
+ 0x65, 0x6C, 0x6C, 0x6F, 0x21
+ };
+ var expected = "32W3532IMVWGY3ZB";
+ var actual = Base32.ToBase32(binary);
+
+ Assert.AreEqual(expected, actual);
+ }
+
+ [Test]
+ public void ToBase32_10_bytes_2()
+ {
+ var binary = new byte[]
+ {
+ 0x48, 0x65, 0x6C, 0x6C, 0x6F,
+ 0x21, 0xDE, 0xAD, 0xBE, 0xEF
+ };
+ var expected = "JBSWY3DPEHPK3PXP";
+ var actual = Base32.ToBase32(binary);
+
+ Assert.AreEqual(expected, actual);
+ }
+
+ [Test]
+ public void ToBase32_9_bytes_1()
+ {
+ var binary = new byte[]
+ {
+ 0xAD, 0xBE, 0xEF, 0x48,
+ 0x65, 0x6C, 0x6C, 0x6F, 0x21
+ };
+ var expected = "VW7O6SDFNRWG6II=";
+ var actual = Base32.ToBase32(binary);
+
+ Assert.AreEqual(expected, actual);
+ }
+
+ [Test]
+ public void ToBase32_9_bytes_2()
+ {
+ var binary = new byte[]
+ {
+ 0x65, 0x6C, 0x6C, 0x6F,
+ 0x21, 0xDE, 0xAD, 0xBE, 0xEF
+ };
+ var expected = "MVWGY3ZB32W353Y=";
+ var actual = Base32.ToBase32(binary);
+
+ Assert.AreEqual(expected, actual);
+ }
+
+ [Test]
+ public void ToBase32_8_bytes_1()
+ {
+ var binary = new byte[]
+ {
+ 0xBE, 0xEF, 0x48,
+ 0x65, 0x6C, 0x6C, 0x6F, 0x21
+ };
+ var expected = "X3XUQZLMNRXSC===";
+ var actual = Base32.ToBase32(binary);
+
+ Assert.AreEqual(expected, actual);
+ }
+
+ [Test]
+ public void ToBase32_8_bytes_2()
+ {
+ var binary = new byte[]
+ {
+ 0x6C, 0x6C, 0x6F,
+ 0x21, 0xDE, 0xAD, 0xBE, 0xEF
+ };
+ var expected = "NRWG6IO6VW7O6===";
+ var actual = Base32.ToBase32(binary);
+
+ Assert.AreEqual(expected, actual);
+ }
+
+ [Test]
+ public void ToBase32_7_bytes_1()
+ {
+ var binary = new byte[]
+ {
+ 0xEF, 0x48,
+ 0x65, 0x6C, 0x6C, 0x6F, 0x21
+ };
+ var expected = "55EGK3DMN4QQ====";
+ var actual = Base32.ToBase32(binary);
+
+ Assert.AreEqual(expected, actual);
+ }
+
+ [Test]
+ public void ToBase32_7_bytes_2()
+ {
+ var binary = new byte[]
+ {
+ 0x6C, 0x6F,
+ 0x21, 0xDE, 0xAD, 0xBE, 0xEF
+ };
+ var expected = "NRXSDXVNX3XQ====";
+ var actual = Base32.ToBase32(binary);
+
+ Assert.AreEqual(expected, actual);
+ }
+
+ [Test]
+ public void ToBase32_6_bytes_1()
+ {
+ var binary = new byte[]
+ {
+ 0x48,
+ 0x65, 0x6C, 0x6C, 0x6F, 0x21
+ };
+ var expected = "JBSWY3DPEE======";
+ var actual = Base32.ToBase32(binary);
+
+ Assert.AreEqual(expected, actual);
+ }
+
+ [Test]
+ public void ToBase32_6_bytes_2()
+ {
+ var binary = new byte[]
+ {
+ 0x6F,
+ 0x21, 0xDE, 0xAD, 0xBE, 0xEF
+ };
+ var expected = "N4Q55LN654======";
+ var actual = Base32.ToBase32(binary);
+
+ Assert.AreEqual(expected, actual);
+ }
+
+ [Test]
+ public void ToBinary_10_bytes_1()
+ {
+ var expected = new byte[]
+ {
+ 0xDE, 0xAD, 0xBE, 0xEF, 0x48,
+ 0x65, 0x6C, 0x6C, 0x6F, 0x21
+ };
+ var base32 = "32W3532IMVWGY3ZB";
+ var actual = Base32.ToBinary(base32);
+
+ Assert.AreEqual(expected, actual);
+ }
+
+ [Test]
+ public void ToBinary_10_bytes_2()
+ {
+ var expected = new byte[]
+ {
+ 0x48, 0x65, 0x6C, 0x6C, 0x6F,
+ 0x21, 0xDE, 0xAD, 0xBE, 0xEF
+ };
+ var base32 = "JBSWY3DPEHPK3PXP";
+ var actual = Base32.ToBinary(base32);
+
+ Assert.AreEqual(expected, actual);
+ }
+
+ [Test]
+ public void ToBinary_9_bytes_1()
+ {
+ var expected = new byte[]
+ {
+ 0xAD, 0xBE, 0xEF, 0x48,
+ 0x65, 0x6C, 0x6C, 0x6F, 0x21
+ };
+ var base32 = "VW7O6SDFNRWG6II=";
+ var actual = Base32.ToBinary(base32);
+
+ Assert.AreEqual(expected, actual);
+ }
+
+ [Test]
+ public void ToBinary_9_bytes_2()
+ {
+ var expected = new byte[]
+ {
+ 0x65, 0x6C, 0x6C, 0x6F,
+ 0x21, 0xDE, 0xAD, 0xBE, 0xEF
+ };
+ var base32 = "MVWGY3ZB32W353Y=";
+ var actual = Base32.ToBinary(base32);
+
+ Assert.AreEqual(expected, actual);
+ }
+
+ [Test]
+ public void ToBinary_8_bytes_1()
+ {
+ var expected = new byte[]
+ {
+ 0xBE, 0xEF, 0x48,
+ 0x65, 0x6C, 0x6C, 0x6F, 0x21
+ };
+ var base32 = "X3XUQZLMNRXSC===";
+ var actual = Base32.ToBinary(base32);
+
+ Assert.AreEqual(expected, actual);
+ }
+
+ [Test]
+ public void ToBinary_8_bytes_2()
+ {
+ var expected = new byte[]
+ {
+ 0x6C, 0x6C, 0x6F,
+ 0x21, 0xDE, 0xAD, 0xBE, 0xEF
+ };
+ var base32 = "NRWG6IO6VW7O6===";
+ var actual = Base32.ToBinary(base32);
+
+ Assert.AreEqual(expected, actual);
+ }
+
+ [Test]
+ public void ToBinary_7_bytes_1()
+ {
+ var expected = new byte[]
+ {
+ 0xEF, 0x48,
+ 0x65, 0x6C, 0x6C, 0x6F, 0x21
+ };
+ var base32 = "55EGK3DMN4QQ====";
+ var actual = Base32.ToBinary(base32);
+
+ Assert.AreEqual(expected, actual);
+ }
+
+ [Test]
+ public void ToBinary_7_bytes_2()
+ {
+ var expected = new byte[]
+ {
+ 0x6C, 0x6F,
+ 0x21, 0xDE, 0xAD, 0xBE, 0xEF
+ };
+ var base32 = "NRXSDXVNX3XQ====";
+ var actual = Base32.ToBinary(base32);
+
+ Assert.AreEqual(expected, actual);
+ }
+
+ [Test]
+ public void ToBinary_6_bytes_1()
+ {
+ var expected = new byte[]
+ {
+ 0x48,
+ 0x65, 0x6C, 0x6C, 0x6F, 0x21
+ };
+ var base32 = "JBSWY3DPEE======";
+ var actual = Base32.ToBinary(base32);
+
+ Assert.AreEqual(expected, actual);
+ }
+
+ [Test]
+ public void ToBinary_6_bytes_2()
+ {
+ var expected = new byte[]
+ {
+ 0x6F,
+ 0x21, 0xDE, 0xAD, 0xBE, 0xEF
+ };
+ var base32 = "N4Q55LN654======";
+ var actual = Base32.ToBinary(base32);
+
+ Assert.AreEqual(expected, actual);
+ }
+ }
+}
diff --git a/OATH.Net.Test/KeyTests.cs b/OATH.Net.Test/KeyTests.cs
index 1c9241c..7ddf93a 100644
--- a/OATH.Net.Test/KeyTests.cs
+++ b/OATH.Net.Test/KeyTests.cs
@@ -23,8 +23,9 @@ namespace OathNet.Test
0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30
};
var key = new Key(keyData);
+ var actual = key.Binary;
- Assert.AreEqual(keyData, key.Binary);
+ Assert.AreEqual(keyData, actual);
}
[Test]
@@ -38,8 +39,9 @@ namespace OathNet.Test
0x31, 0x32
};
var key = new Key(keyData);
+ var actual = key.Binary;
- Assert.AreEqual(keyData, key.Binary);
+ Assert.AreEqual(keyData, actual);
}
[Test]
@@ -52,9 +54,10 @@ namespace OathNet.Test
};
var key = new Key(keyData);
- var base32 = "32W3532IMVWGY3ZB";
+ var actual = key.Base32;
+ var expected = "32W3532IMVWGY3ZB";
- Assert.AreEqual(base32, key.Base32);
+ Assert.AreEqual(expected, actual);
}
[Test]
@@ -67,9 +70,10 @@ namespace OathNet.Test
};
var key = new Key(keyData);
- var base32 = "JBSWY3DPEHPK3PXP";
+ var actual = key.Base32;
+ var expected = "JBSWY3DPEHPK3PXP";
- Assert.AreEqual(base32, key.Base32);
+ Assert.AreEqual(expected, actual);
}
[Test]
@@ -77,8 +81,9 @@ namespace OathNet.Test
{
var base32 = "32W3532IMVWGY3ZB";
var key = new Key(base32);
+ var actual = key.Base32;
- Assert.AreEqual(base32, key.Base32);
+ Assert.AreEqual(base32, actual);
}
[Test]
@@ -86,8 +91,9 @@ namespace OathNet.Test
{
var base32 = "JBSWY3DPEHPK3PXP";
var key = new Key(base32);
+ var actual = key.Base32;
- Assert.AreEqual(base32, key.Base32);
+ Assert.AreEqual(base32, actual);
}
[Test]
@@ -95,13 +101,14 @@ namespace OathNet.Test
{
var base32 = "32W3532IMVWGY3ZB";
var key = new Key(base32);
- var binary = new byte[]
+ var actual = key.Binary;
+ var expected = new byte[]
{
0xDE, 0xAD, 0xBE, 0xEF, 0x48,
0x65, 0x6C, 0x6C, 0x6F, 0x21
};
- Assert.AreEqual(binary, key.Binary);
+ Assert.AreEqual(expected, actual);
}
[Test]
@@ -109,13 +116,14 @@ namespace OathNet.Test
{
var base32 = "JBSWY3DPEHPK3PXP";
var key = new Key(base32);
- var binary = new byte[]
+ var actual = key.Binary;
+ var expected = new byte[]
{
0x48, 0x65, 0x6C, 0x6C, 0x6F,
0x21, 0xDE, 0xAD, 0xBE, 0xEF
};
- Assert.AreEqual(base32, key.Base32);
+ Assert.AreEqual(expected, actual);
}
}
}
diff --git a/OATH.Net.Test/OATH.Net.Test.csproj b/OATH.Net.Test/OATH.Net.Test.csproj
index 5565426..5004614 100644
--- a/OATH.Net.Test/OATH.Net.Test.csproj
+++ b/OATH.Net.Test/OATH.Net.Test.csproj
@@ -55,6 +55,7 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
+ <Compile Include="Base32Tests.cs" />
<Compile Include="KeyTests.cs" />
<Compile Include="MiscExtensionsTests.cs" />
<Compile Include="CounterBasedOtpGeneratorTests.cs" />
diff --git a/OATH.Net/Base32.cs b/OATH.Net/Base32.cs
new file mode 100644
index 0000000..f7ef19f
--- /dev/null
+++ b/OATH.Net/Base32.cs
@@ -0,0 +1,118 @@
+//------------------------------------------------------------------------------------
+// <copyright file="Base32.cs" company="Stephen Jennings">
+// Copyright 2011 Stephen Jennings. Licensed under the Apache License, Version 2.0.
+// </copyright>
+//------------------------------------------------------------------------------------
+
+namespace OathNet
+{
+ using System;
+ using System.Collections;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Text;
+
+ /// <summary>
+ /// Contains methods to convert to and from base-32 according to RFC 3548.
+ /// </summary>
+ public static class Base32
+ {
+ private static readonly string[] Alphabet = new string[]
+ {
+ "A", "B", "C", "D", "E", "F", "G", "H", "I", "J",
+ "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T",
+ "U", "V", "W", "X", "Y", "Z", "2", "3", "4", "5",
+ "6", "7"
+ };
+
+ private static readonly char Padding = '=';
+
+ /// <summary>
+ /// Converts a byte array to a base-32 representation.
+ /// </summary>
+ /// <param name="data">The data to convert.</param>
+ /// <returns>A base-32 encoded string.</returns>
+ public static string ToBase32(byte[] data)
+ {
+ string result = String.Empty;
+
+ var fullSegments = data.Length / 5;
+ var finalSegmentLength = data.Length % 5;
+ var segments = fullSegments + (finalSegmentLength == 0 ? 0 : 1);
+
+ for (int i = 0; i < segments; i++)
+ {
+ var segment = data.Skip(i * 5).Take(5).ToArray();
+ result = String.Concat(result, Base32.ConvertSegmentToBase32(segment));
+ }
+
+ return result;
+ }
+
+ /// <summary>
+ /// Converts a base-32 encoded string to a byte array.
+ /// </summary>
+ /// <param name="base32">A base-32 encoded string.</param>
+ /// <returns>The data represented by the base-32 string.</returns>
+ public static byte[] ToBinary(string base32)
+ {
+ throw new NotImplementedException();
+ }
+
+ private static string ConvertSegmentToBase32(byte[] segment)
+ {
+ if (segment.Length == 0)
+ {
+ return String.Empty;
+ }
+
+ if (segment.Length > 5)
+ {
+ throw new ArgumentException("Segment must be five bytes or fewer.");
+ }
+
+ string result = String.Empty;
+
+ int accumulator = 0;
+ int bitsRemaining = 5;
+ byte[] masks = new byte[] { 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F };
+
+ foreach (var b in segment)
+ {
+ // Accumulate the bits remaining from the previous byte, if any
+ int bottomBitsInThisByte = 8 - bitsRemaining;
+ accumulator += (b >> bottomBitsInThisByte) & masks[Math.Min(bitsRemaining, 5)];
+
+ // Add the accumulated character to the result string
+ result = result + Alphabet[accumulator];
+
+ if (bottomBitsInThisByte >= 5)
+ {
+ bottomBitsInThisByte -= 5;
+
+ // Set the accumulator to the next 5 bits in this byte
+ accumulator = (b >> bottomBitsInThisByte) & masks[5];
+
+ // Add the accumulated character to the result string
+ result = result + Alphabet[accumulator];
+ }
+
+ // Decide how many more bits we need to accumulate from the next byte
+ bitsRemaining = (5 - bottomBitsInThisByte) % 5;
+
+ // Set the accumulator to the remaining bits in this byte
+ accumulator = (b & masks[bottomBitsInThisByte]) << bitsRemaining;
+ }
+
+ if (bitsRemaining > 0)
+ {
+ // Capture the final accumulated value
+ result = result + Alphabet[accumulator];
+ }
+
+ result = result.PadRight(8, Padding);
+
+ return result;
+ }
+ }
+}
diff --git a/OATH.Net/CounterBasedOtpGenerator.cs b/OATH.Net/CounterBasedOtpGenerator.cs
index 70d5922..d876430 100644
--- a/OATH.Net/CounterBasedOtpGenerator.cs
+++ b/OATH.Net/CounterBasedOtpGenerator.cs
@@ -61,7 +61,7 @@ namespace OathNet
/// Initializes a new instance of the CounterBasedOtpGenerator class.
/// This is used when the client and server share a counter value.
/// </summary>
- /// <param name="secretKeyHex">The secret key represented as a sequence of hexadecimal digits.</param>
+ /// <param name="secretKey">The secret key.</param>
/// <param name="otpLength">The number of digits in the OTP to generate.</param>
public CounterBasedOtpGenerator(Key secretKey, int otpLength)
: this(secretKey, otpLength, new SHA1HMACAlgorithm())
diff --git a/OATH.Net/Key.cs b/OATH.Net/Key.cs
index e812e1d..66a98d7 100644
--- a/OATH.Net/Key.cs
+++ b/OATH.Net/Key.cs
@@ -40,7 +40,7 @@ namespace OathNet
/// <param name="base32key">The key to initialize.</param>
public Key(string base32key)
{
- throw new NotImplementedException();
+ this.keyData = OathNet.Base32.ToBinary(base32key);
}
/// <summary>
@@ -61,7 +61,7 @@ namespace OathNet
{
get
{
- throw new NotImplementedException();
+ return OathNet.Base32.ToBase32(this.keyData);
}
}
}
diff --git a/OATH.Net/OATH.Net.csproj b/OATH.Net/OATH.Net.csproj
index 155da01..f1d7dea 100644
--- a/OATH.Net/OATH.Net.csproj
+++ b/OATH.Net/OATH.Net.csproj
@@ -63,6 +63,7 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
+ <Compile Include="Base32.cs" />
<Compile Include="Key.cs" />
<Compile Include="SHA512HMACAlgorithm.cs" />
<Compile Include="SHA256HMACAlgorithm.cs" />