summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKyle Spearrin <kyle.spearrin@gmail.com>2017-01-28 21:44:52 -0500
committerKyle Spearrin <kyle.spearrin@gmail.com>2017-01-28 21:44:52 -0500
commite013027cdf1edd647242e5ae93339ea02b3b0f06 (patch)
tree6fbdee1ec59e6b12284a8a3ab07d6bc2ed6244e0
parentd3245249262eae3be885ed23cfc6f6f5ca3d2c9e (diff)
downloadOtp.NET-e013027cdf1edd647242e5ae93339ea02b3b0f06.zip
Otp.NET-e013027cdf1edd647242e5ae93339ea02b3b0f06.tar.gz
Otp.NET-e013027cdf1edd647242e5ae93339ea02b3b0f06.tar.bz2
verification window wasnt working
-rw-r--r--src/Otp.NET/IKeyProvider.cs47
-rw-r--r--src/Otp.NET/InMemoryKey.cs139
-rw-r--r--src/Otp.NET/KeyGeneration.cs42
-rw-r--r--src/Otp.NET/Otp.cs53
-rw-r--r--src/Otp.NET/project.json2
5 files changed, 232 insertions, 51 deletions
diff --git a/src/Otp.NET/IKeyProvider.cs b/src/Otp.NET/IKeyProvider.cs
new file mode 100644
index 0000000..611d086
--- /dev/null
+++ b/src/Otp.NET/IKeyProvider.cs
@@ -0,0 +1,47 @@
+/*
+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.
+*/
+
+namespace OtpNet
+{
+ /// <summary>
+ /// Interface used to interact with a key
+ /// </summary>
+ public interface IKeyProvider
+ {
+ /// <summary>
+ /// Uses the key to get an HMAC using the specified algorithm and data
+ /// </summary>
+ /// <remarks>
+ /// This is a much better API than the previous API which would briefly expose the key for all derived types.
+ ///
+ /// Now a derived type could be bound to an HSM/smart card/etc if required and a lot of the security limitations
+ /// of in app/memory exposure of the key can be eliminated.
+ /// </remarks>
+ /// <param name="mode">The HMAC algorithm to use</param>
+ /// <param name="data">The data used to compute the HMAC</param>
+ /// <returns>HMAC of the key and data</returns>
+ byte[] ComputeHmac(OtpHashMode mode, byte[] data);
+ }
+}
diff --git a/src/Otp.NET/InMemoryKey.cs b/src/Otp.NET/InMemoryKey.cs
new file mode 100644
index 0000000..14dc694
--- /dev/null
+++ b/src/Otp.NET/InMemoryKey.cs
@@ -0,0 +1,139 @@
+/*
+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.Security.Cryptography;
+
+namespace OtpNet
+{
+ /// <summary>
+ /// Represents a key in memory
+ /// </summary>
+ /// <remarks>
+ /// This will attempt to use the Windows data protection api to encrypt the key in memory.
+ /// However, this type favors working over memory protection. This is an attempt to minimize
+ /// exposure in memory, nothing more. This protection is flawed in many ways and is limited
+ /// to Windows.
+ ///
+ /// In order to use the key to compute an hmac it must be temporarily decrypted, used,
+ /// then re-encrypted. This does expose the key in memory for a time. If a memory dump occurs in this time
+ /// the plaintext key will be part of it. Furthermore, there are potentially
+ /// artifacts from the hmac computation, GC compaction, or any number of other leaks even after
+ /// the key is re-encrypted.
+ ///
+ /// This type favors working over memory protection. If the particular platform isn't supported then,
+ /// unless forced by modifying the IsPlatformSupported method, it will just store the key in a standard
+ /// byte array.
+ /// </remarks>
+ public class InMemoryKey : IKeyProvider
+ {
+ static readonly object platformSupportSync = new object();
+
+ readonly object stateSync = new object();
+ readonly byte[] KeyData;
+ readonly int keyLength;
+
+ /// <summary>
+ /// Creates an instance of a key.
+ /// </summary>
+ /// <param name="key">Plaintext key data</param>
+ public InMemoryKey(byte[] key)
+ {
+ if(!(key != null))
+ throw new ArgumentNullException("key");
+ if(!(key.Length > 0))
+ throw new ArgumentException("The key must not be empty");
+
+ this.keyLength = key.Length;
+ int paddedKeyLength = (int)Math.Ceiling((decimal)key.Length / (decimal)16) * 16;
+ this.KeyData = new byte[paddedKeyLength];
+ Array.Copy(key, this.KeyData, key.Length);
+ }
+
+ /// <summary>
+ /// Gets a copy of the plaintext key
+ /// </summary>
+ /// <remarks>
+ /// This is internal rather than protected so that the tests can use this method
+ /// </remarks>
+ /// <returns>Plaintext Key</returns>
+ internal byte[] GetCopyOfKey()
+ {
+ var plainKey = new byte[this.keyLength];
+ lock(this.stateSync)
+ {
+ Array.Copy(this.KeyData, plainKey, this.keyLength);
+ }
+ return plainKey;
+ }
+
+ /// <summary>
+ /// Uses the key to get an HMAC using the specified algorithm and data
+ /// </summary>
+ /// <param name="mode">The HMAC algorithm to use</param>
+ /// <param name="data">The data used to compute the HMAC</param>
+ /// <returns>HMAC of the key and data</returns>
+ public byte[] ComputeHmac(OtpHashMode mode, byte[] data)
+ {
+ byte[] hashedValue = null;
+ using(HMAC hmac = CreateHmacHash(mode))
+ {
+ byte[] key = this.GetCopyOfKey();
+ try
+ {
+ hmac.Key = key;
+ hashedValue = hmac.ComputeHash(data);
+ }
+ finally
+ {
+ KeyUtilities.Destroy(key);
+ }
+ }
+
+ return hashedValue;
+ }
+
+ /// <summary>
+ /// Create an HMAC object for the specified algorithm
+ /// </summary>
+ private static HMAC CreateHmacHash(OtpHashMode otpHashMode)
+ {
+ HMAC hmacAlgorithm = null;
+ switch(otpHashMode)
+ {
+ case OtpHashMode.Sha256:
+ hmacAlgorithm = new HMACSHA256();
+ break;
+ case OtpHashMode.Sha512:
+ hmacAlgorithm = new HMACSHA512();
+ break;
+ default: //case OtpHashMode.Sha1:
+ hmacAlgorithm = new HMACSHA1();
+ break;
+ }
+ return hmacAlgorithm;
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Otp.NET/KeyGeneration.cs b/src/Otp.NET/KeyGeneration.cs
index 7e5b613..543fef6 100644
--- a/src/Otp.NET/KeyGeneration.cs
+++ b/src/Otp.NET/KeyGeneration.cs
@@ -23,6 +23,9 @@ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
+using System;
+using System.Security.Cryptography;
+
namespace OtpNet
{
/// <summary>
@@ -55,6 +58,45 @@ namespace OtpNet
return GenerateRandomKey(LengthForMode(mode));
}
+ /// <summary>
+ /// Uses the procedure defined in RFC 4226 section 7.5 to derive a key from the master key
+ /// </summary>
+ /// <param name="masterKey">The master key from which to derive a device specific key</param>
+ /// <param name="publicIdentifier">The public identifier that is unique to the authenticating device</param>
+ /// <param name="mode">The hash mode to use. This will determine the resulting key lenght. The default is sha-1 (as per the RFC) which is 20 bytes</param>
+ /// <returns>Derived key</returns>
+ public static byte[] DeriveKeyFromMaster(IKeyProvider masterKey, byte[] publicIdentifier, OtpHashMode mode = OtpHashMode.Sha1)
+ {
+ if(masterKey == null)
+ throw new ArgumentNullException("masterKey");
+ return masterKey.ComputeHmac(mode, publicIdentifier);
+ }
+
+ /// <summary>
+ /// Uses the procedure defined in RFC 4226 section 7.5 to derive a key from the master key
+ /// </summary>
+ /// <param name="masterKey">The master key from which to derive a device specific key</param>
+ /// <param name="serialNumber">A serial number that is unique to the authenticating device</param>
+ /// <param name="mode">The hash mode to use. This will determine the resulting key lenght. The default is sha-1 (as per the RFC) which is 20 bytes</param>
+ /// <returns>Derived key</returns>
+ public static byte[] DeriveKeyFromMaster(IKeyProvider masterKey, int serialNumber, OtpHashMode mode = OtpHashMode.Sha1)
+ {
+ return DeriveKeyFromMaster(masterKey, KeyUtilities.GetBigEndianBytes(serialNumber), mode);
+ }
+
+ private static HashAlgorithm GetHashAlgorithmForMode(OtpHashMode mode)
+ {
+ switch(mode)
+ {
+ case OtpHashMode.Sha256:
+ return SHA256.Create();
+ case OtpHashMode.Sha512:
+ return SHA512.Create();
+ default: //case OtpHashMode.Sha1:
+ return SHA1.Create();
+ }
+ }
+
private static int LengthForMode(OtpHashMode mode)
{
switch(mode)
diff --git a/src/Otp.NET/Otp.cs b/src/Otp.NET/Otp.cs
index 513e7f5..dc407bf 100644
--- a/src/Otp.NET/Otp.cs
+++ b/src/Otp.NET/Otp.cs
@@ -39,7 +39,7 @@ namespace OtpNet
/// <summary>
/// Secret key
/// </summary>
- protected readonly byte[] secretKey;
+ protected readonly IKeyProvider secretKey;
/// <summary>
/// The hash mode to use
@@ -59,7 +59,7 @@ namespace OtpNet
throw new ArgumentException("secretKey empty");
// when passing a key into the constructor the caller may depend on the reference to the key remaining intact.
- this.secretKey = secretKey;
+ this.secretKey = new InMemoryKey(secretKey);
this.hashMode = mode;
}
@@ -77,7 +77,7 @@ namespace OtpNet
/// </summary>
protected internal long CalculateOtp(byte[] data, OtpHashMode mode)
{
- byte[] hmacComputedHash = ComputeHmac(mode, data);
+ byte[] hmacComputedHash = this.secretKey.ComputeHmac(mode, data);
// The RFC has a hard coded index 19 in this value.
// This is the same thing but also accomodates SHA256 and SHA512
@@ -124,52 +124,5 @@ namespace OtpNet
matchedStep = 0;
return false;
}
-
- /// <summary>
- /// Uses the key to get an HMAC using the specified algorithm and data
- /// </summary>
- /// <param name="mode">The HMAC algorithm to use</param>
- /// <param name="data">The data used to compute the HMAC</param>
- /// <returns>HMAC of the key and data</returns>
- private byte[] ComputeHmac(OtpHashMode mode, byte[] data)
- {
- byte[] hashedValue = null;
- using(HMAC hmac = CreateHmacHash(mode))
- {
- try
- {
- hmac.Key = this.secretKey;
- hashedValue = hmac.ComputeHash(data);
- }
- finally
- {
- KeyUtilities.Destroy(this.secretKey);
- }
- }
-
- return hashedValue;
- }
-
- /// <summary>
- /// Create an HMAC object for the specified algorithm
- /// </summary>
- private static HMAC CreateHmacHash(OtpHashMode otpHashMode)
- {
- HMAC hmacAlgorithm = null;
- switch(otpHashMode)
- {
- case OtpHashMode.Sha256:
- hmacAlgorithm = new HMACSHA256();
- break;
- case OtpHashMode.Sha512:
- hmacAlgorithm = new HMACSHA512();
- break;
- default: //case OtpHashMode.Sha1:
- hmacAlgorithm = new HMACSHA1();
- break;
- }
- return hmacAlgorithm;
- }
-
}
} \ No newline at end of file
diff --git a/src/Otp.NET/project.json b/src/Otp.NET/project.json
index 881e381..bc3f51c 100644
--- a/src/Otp.NET/project.json
+++ b/src/Otp.NET/project.json
@@ -11,4 +11,4 @@
},
"net45": {}
}
-}
+} \ No newline at end of file