diff options
author | Kyle Spearrin <kyle.spearrin@gmail.com> | 2017-01-28 21:44:52 -0500 |
---|---|---|
committer | Kyle Spearrin <kyle.spearrin@gmail.com> | 2017-01-28 21:44:52 -0500 |
commit | e013027cdf1edd647242e5ae93339ea02b3b0f06 (patch) | |
tree | 6fbdee1ec59e6b12284a8a3ab07d6bc2ed6244e0 | |
parent | d3245249262eae3be885ed23cfc6f6f5ca3d2c9e (diff) | |
download | Otp.NET-e013027cdf1edd647242e5ae93339ea02b3b0f06.zip Otp.NET-e013027cdf1edd647242e5ae93339ea02b3b0f06.tar.gz Otp.NET-e013027cdf1edd647242e5ae93339ea02b3b0f06.tar.bz2 |
verification window wasnt working
-rw-r--r-- | src/Otp.NET/IKeyProvider.cs | 47 | ||||
-rw-r--r-- | src/Otp.NET/InMemoryKey.cs | 139 | ||||
-rw-r--r-- | src/Otp.NET/KeyGeneration.cs | 42 | ||||
-rw-r--r-- | src/Otp.NET/Otp.cs | 53 | ||||
-rw-r--r-- | src/Otp.NET/project.json | 2 |
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 |