summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorlachlanbell <me@lachlanbell.com.au>2018-01-05 22:23:16 +0800
committerKyle Spearrin <kspearrin@users.noreply.github.com>2018-01-05 09:23:16 -0500
commit6543ec6aae1a50e0f8f83a03247aaf650960633e (patch)
treeb58c631e19aecd3c8c881f9e8822a1aff3fc657c
parent5e0f1857271adad03f80646c6cc93f52f3789bf0 (diff)
downloadOtp.NET-6543ec6aae1a50e0f8f83a03247aaf650960633e.zip
Otp.NET-6543ec6aae1a50e0f8f83a03247aaf650960633e.tar.gz
Otp.NET-6543ec6aae1a50e0f8f83a03247aaf650960633e.tar.bz2
Add HOTP Support (#6)
* Add HOTP * Fix Issue * Fix incorrect RFC number * Change incorrect function name * Add VerifyHotp function * Sign assembly * Update README * Fix small typos
-rw-r--r--README.md38
-rw-r--r--src/Otp.NET/Hotp.cs101
2 files changed, 138 insertions, 1 deletions
diff --git a/README.md b/README.md
index 05489aa..bf02ec8 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
# Otp.NET
-An implementation TOTP [RFC 6238](http://tools.ietf.org/html/rfc6238) in C#. This is a port of the [OtpSharp library](https://bitbucket.org/devinmartin/otp-sharp/overview) to support .NET Core.
+An implementation TOTP [RFC 6238](http://tools.ietf.org/html/rfc6238) and HOTP [RFC 4226](http://tools.ietf.org/html/rfc4226) in C#. This is a port of the [OtpSharp library](https://bitbucket.org/devinmartin/otp-sharp/overview) to support .NET Core.
## Get it on NuGet
@@ -150,6 +150,42 @@ The Totp class constructor can take a TimeCorrection object that will be applied
var totp = new Totp(secretKey, timeCorrection: correction);
```
+## HOTP (HMAC-based One Time Password)
+In addition to TOTP, this library implements HOTP (counter based) code calculation in C#.
+
+## Creation of an HOTP object
+```c#
+using OtpNet;
+```
+
+```c#
+var hotp = new Hotp(secretKey);
+```
+
+There are several options that can be used to change the how the code is calculated. These are all mentioned in the RFC. These options are specified when the HOTP object is created.
+
+Different hash algorithms can be used to calculate the code. The default is Sha1, but Sha256, and Sha512 may be used instead.
+
+To change that behavior from the default of Sha1 simply pass in the OtpHashMode enum with the desired value into the constructor.
+
+```c#
+var hotp = new Hotp(secretKey, mode: OtpHashMode.Sha512);
+```
+
+Finally the truncation level can be specified. Basically this is how many digits do you want your HOTP code to be. The tests in the RFC specify 8, but 6 has become a de-facto standard if not an actual one. For this reason the default is 6 but you can set it to something else. There aren't a lot of tests around this either so use at your own risk (other than the fact that the RFC test table uses HOTP values that are 8 digits).
+
+```c#
+var hotp = new Hotp(secretKey, totpSize: 8);
+```
+
+## Verification
+
+The HOTP implementation provides a mechanism for verifying HOTP codes that are passed in. There is a method called VerifyHotp with an overload that takes a counter value.
+
+```c#
+public bool VerifyHotp(string totp, long counter);
+```
+
## Base32 Encoding
Also included is a Base32 helper.
diff --git a/src/Otp.NET/Hotp.cs b/src/Otp.NET/Hotp.cs
new file mode 100644
index 0000000..ee804b7
--- /dev/null
+++ b/src/Otp.NET/Hotp.cs
@@ -0,0 +1,101 @@
+/*
+Credits to Devin Martin and the original OtpSharp library:
+https://bitbucket.org/devinmartin/otp-sharp/overview
+
+Copyright (C) 2012 Devin Martin
+
+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.
+*/
+
+using System;
+using System.Globalization;
+
+namespace OtpNet
+{
+ /// <summary>
+ /// Calculate HMAC-Based One-Time-Passwords (HOTP) from a secret key
+ /// </summary>
+ /// <remarks>
+ /// The specifications for this are found in RFC 4226
+ /// http://tools.ietf.org/html/rfc4226
+ /// </remarks>
+ public class Hotp: Otp
+ {
+ private readonly int hotpSize;
+
+ /// <summary>
+ /// Create a HOTP instance
+ /// </summary>
+ /// <param name="secretKey">The secret key to use in HOTP calculations</param>
+ /// <param name="mode">The hash mode to use</param>
+ /// <param name="hotpSize">The number of digits that the returning HOTP should have. The default is 6.</param>
+ public Hotp(byte[] secretKey, OtpHashMode mode = OtpHashMode.Sha1, int hotpSize = 6)
+ : base(secretKey, mode)
+ {
+ VerifyParameters(hotpSize);
+
+ this.hotpSize = hotpSize;
+ }
+ private static void VerifyParameters(int hotpSize)
+ {
+ if (!(hotpSize >= 6))
+ throw new ArgumentOutOfRangeException("hotpSize");
+ if (!(hotpSize <= 8))
+ throw new ArgumentOutOfRangeException("hotpSize");
+ }
+ /// <summary>
+ /// Takes a counter and then computes a HOTP value
+ /// </summary>
+ /// <param name="timestamp">The timestamp to use for the HOTP calculation</param>
+ /// <returns>a HOTP value</returns>
+ public string ComputeHOTP(long counter)
+ {
+ return this.Compute(counter, this.hashMode);
+ }
+ /// <summary>
+ /// Verify a value that has been provided with the calculated value
+ /// </summary>
+ /// <param name="hotp">the trial HOTP value</param>
+ /// <param name="counter">The counter value to verify/param>
+ /// <returns>True if there is a match.</returns>
+ public bool VerifyHotp(string hotp, long counter)
+ {
+ if (hotp == ComputeHOTP(counter))
+ {
+ return true;
+ } else
+ {
+ return false;
+ }
+ }
+ /// <summary>
+ /// Takes a time step and computes a HOTP code
+ /// </summary>
+ /// <param name="counter">counter</param>
+ /// <param name="mode">The hash mode to use</param>
+ /// <returns>HOTP calculated code</returns>
+ protected override string Compute(long counter, OtpHashMode mode)
+ {
+ var data = KeyUtilities.GetBigEndianBytes(counter);
+ var otp = this.CalculateOtp(data, mode);
+ return Digits(otp, this.hotpSize);
+ }
+
+ }
+} \ No newline at end of file