diff --git a/src/Otp.NET/Base32Encoding.cs b/src/Otp.NET/Base32Encoding.cs index bbab89d..3e2655c 100644 --- a/src/Otp.NET/Base32Encoding.cs +++ b/src/Otp.NET/Base32Encoding.cs @@ -13,20 +13,21 @@ public static byte[] ToBytes(string input) { if(string.IsNullOrEmpty(input)) { - throw new ArgumentNullException("input"); + throw new ArgumentNullException(nameof(input)); } input = input.TrimEnd('='); //remove padding characters - int byteCount = input.Length * 5 / 8; //this must be TRUNCATED - byte[] returnArray = new byte[byteCount]; + var byteCount = input.Length * 5 / 8; //this must be TRUNCATED + var returnArray = new byte[byteCount]; byte curByte = 0, bitsRemaining = 8; - int mask = 0, arrayIndex = 0; + var arrayIndex = 0; - foreach(char c in input) + foreach(var c in input) { - int cValue = CharToValue(c); + var cValue = CharToValue(c); + int mask; if(bitsRemaining > 5) { mask = cValue << (bitsRemaining - 5); @@ -56,7 +57,7 @@ public static string ToString(byte[] input) { if(input == null || input.Length == 0) { - throw new ArgumentNullException("input"); + throw new ArgumentNullException(nameof(input)); } int charCount = (int)Math.Ceiling(input.Length / 5d) * 8; @@ -93,7 +94,7 @@ public static string ToString(byte[] input) private static int CharToValue(char c) { - int value = (int)c; + int value = c; //65-90 == uppercase letters if(value < 91 && value > 64) @@ -111,7 +112,7 @@ private static int CharToValue(char c) return value - 97; } - throw new ArgumentException("Character is not a Base32 character.", "c"); + throw new ArgumentException("Character is not a Base32 character.", nameof(c)); } private static char ValueToChar(byte b) @@ -126,7 +127,7 @@ private static char ValueToChar(byte b) return (char)(b + 24); } - throw new ArgumentException("Byte is not a Base32 value.", "b"); + throw new ArgumentException("Byte is not a Base32 value.", nameof(b)); } } } diff --git a/src/Otp.NET/Hotp.cs b/src/Otp.NET/Hotp.cs index 705b74b..ac12e48 100644 --- a/src/Otp.NET/Hotp.cs +++ b/src/Otp.NET/Hotp.cs @@ -16,7 +16,7 @@ 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 +FITNESS FOR A PARTICULAR PURPOSE AND NONINFINGEMENT. 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 @@ -36,7 +36,7 @@ namespace OtpNet /// public class Hotp : Otp { - private readonly int hotpSize; + private readonly int _hotpSize; /// /// Create a HOTP instance @@ -49,7 +49,7 @@ public Hotp(byte[] secretKey, OtpHashMode mode = OtpHashMode.Sha1, int hotpSize { VerifyParameters(hotpSize); - this.hotpSize = hotpSize; + _hotpSize = hotpSize; } /// @@ -63,44 +63,32 @@ public Hotp(IKeyProvider key, OtpHashMode mode = OtpHashMode.Sha1, int hotpSize { VerifyParameters(hotpSize); - this.hotpSize = hotpSize; + _hotpSize = hotpSize; } private static void VerifyParameters(int hotpSize) { - if(!(hotpSize >= 6)) - throw new ArgumentOutOfRangeException("hotpSize"); - if(!(hotpSize <= 8)) - throw new ArgumentOutOfRangeException("hotpSize"); + if(hotpSize < 6) + throw new ArgumentOutOfRangeException(nameof(hotpSize)); + if(hotpSize > 8) + throw new ArgumentOutOfRangeException(nameof(hotpSize)); } /// /// Takes a counter and then computes a HOTP value /// /// The timestamp to use for the HOTP calculation + /// /// a HOTP value - public string ComputeHOTP(long counter) - { - return this.Compute(counter, this.hashMode); - } + public string ComputeHOTP(long counter) => Compute(counter, _hashMode); /// /// Verify a value that has been provided with the calculated value /// /// the trial HOTP value - /// The counter value to verify/param> + /// The counter value to verify /// True if there is a match. - public bool VerifyHotp(string hotp, long counter) - { - if(hotp == ComputeHOTP(counter)) - { - return true; - } - else - { - return false; - } - } + public bool VerifyHotp(string hotp, long counter) => hotp == ComputeHOTP(counter); /// /// Takes a time step and computes a HOTP code @@ -111,8 +99,8 @@ public bool VerifyHotp(string hotp, long counter) protected override string Compute(long counter, OtpHashMode mode) { var data = KeyUtilities.GetBigEndianBytes(counter); - var otp = this.CalculateOtp(data, mode); - return Digits(otp, this.hotpSize); + var otp = CalculateOtp(data, mode); + return Digits(otp, _hotpSize); } } } diff --git a/src/Otp.NET/IKeyProvider.cs b/src/Otp.NET/IKeyProvider.cs index 611d086..9a5541e 100644 --- a/src/Otp.NET/IKeyProvider.cs +++ b/src/Otp.NET/IKeyProvider.cs @@ -16,7 +16,7 @@ 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 +FITNESS FOR A PARTICULAR PURPOSE AND NONINFINGEMENT. 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 diff --git a/src/Otp.NET/InMemoryKey.cs b/src/Otp.NET/InMemoryKey.cs index 14dc694..4b89aed 100644 --- a/src/Otp.NET/InMemoryKey.cs +++ b/src/Otp.NET/InMemoryKey.cs @@ -16,7 +16,7 @@ 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 +FITNESS FOR A PARTICULAR PURPOSE AND NONINFINGEMENT. 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 @@ -49,11 +49,11 @@ namespace OtpNet /// public class InMemoryKey : IKeyProvider { - static readonly object platformSupportSync = new object(); + private static readonly object _platformSupportSync = new object(); - readonly object stateSync = new object(); - readonly byte[] KeyData; - readonly int keyLength; + private readonly object _stateSync = new object(); + private readonly byte[] _keyData; + private readonly int _keyLength; /// /// Creates an instance of a key. @@ -61,15 +61,15 @@ public class InMemoryKey : IKeyProvider /// Plaintext key data public InMemoryKey(byte[] key) { - if(!(key != null)) - throw new ArgumentNullException("key"); - if(!(key.Length > 0)) + if(key == null) + throw new ArgumentNullException(nameof(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); + _keyLength = key.Length; + var paddedKeyLength = (int)Math.Ceiling((decimal)key.Length / (decimal)16) * 16; + _keyData = new byte[paddedKeyLength]; + Array.Copy(key, _keyData, key.Length); } /// @@ -81,10 +81,10 @@ public InMemoryKey(byte[] key) /// Plaintext Key internal byte[] GetCopyOfKey() { - var plainKey = new byte[this.keyLength]; - lock(this.stateSync) + var plainKey = new byte[_keyLength]; + lock(_stateSync) { - Array.Copy(this.KeyData, plainKey, this.keyLength); + Array.Copy(_keyData, plainKey, _keyLength); } return plainKey; } @@ -97,10 +97,10 @@ internal byte[] GetCopyOfKey() /// HMAC of the key and data public byte[] ComputeHmac(OtpHashMode mode, byte[] data) { - byte[] hashedValue = null; - using(HMAC hmac = CreateHmacHash(mode)) + byte[] hashedValue; + using(var hmac = CreateHmacHash(mode)) { - byte[] key = this.GetCopyOfKey(); + var key = GetCopyOfKey(); try { hmac.Key = key; @@ -120,7 +120,7 @@ public byte[] ComputeHmac(OtpHashMode mode, byte[] data) /// private static HMAC CreateHmacHash(OtpHashMode otpHashMode) { - HMAC hmacAlgorithm = null; + HMAC hmacAlgorithm; switch(otpHashMode) { case OtpHashMode.Sha256: diff --git a/src/Otp.NET/KeyGeneration.cs b/src/Otp.NET/KeyGeneration.cs index 13ad7c4..1483a18 100644 --- a/src/Otp.NET/KeyGeneration.cs +++ b/src/Otp.NET/KeyGeneration.cs @@ -16,7 +16,7 @@ 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 +FITNESS FOR A PARTICULAR PURPOSE AND NONINFINGEMENT. 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 @@ -34,13 +34,13 @@ namespace OtpNet public static class KeyGeneration { /// - /// Generates a random key in accordance with the RFC recommened length for each algorithm + /// Generates a random key in accordance with the RFC recommended length for each algorithm /// /// Key length /// The generated key public static byte[] GenerateRandomKey(int length) { - byte[] key = new byte[length]; + var key = new byte[length]; using(var rnd = RandomNumberGenerator.Create()) { rnd.GetBytes(key); @@ -49,7 +49,7 @@ public static byte[] GenerateRandomKey(int length) } /// - /// Generates a random key in accordance with the RFC recommened length for each algorithm + /// Generates a random key in accordance with the RFC recommended length for each algorithm /// /// HashMode /// Key @@ -65,10 +65,14 @@ public static byte[] GenerateRandomKey(OtpHashMode mode = OtpHashMode.Sha1) /// The public identifier that is unique to the authenticating device /// 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 /// Derived key - public static byte[] DeriveKeyFromMaster(IKeyProvider masterKey, byte[] publicIdentifier, OtpHashMode mode = OtpHashMode.Sha1) + public static byte[] DeriveKeyFromMaster( + IKeyProvider masterKey, + byte[] publicIdentifier, + OtpHashMode mode = OtpHashMode.Sha1) { if(masterKey == null) - throw new ArgumentNullException("masterKey"); + throw new ArgumentNullException(nameof(masterKey)); + return masterKey.ComputeHmac(mode, publicIdentifier); } @@ -77,12 +81,14 @@ public static byte[] DeriveKeyFromMaster(IKeyProvider masterKey, byte[] publicId /// /// The master key from which to derive a device specific key /// A serial number that is unique to the authenticating device - /// 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 + /// 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 /// Derived key - public static byte[] DeriveKeyFromMaster(IKeyProvider masterKey, int serialNumber, OtpHashMode mode = OtpHashMode.Sha1) - { - return DeriveKeyFromMaster(masterKey, KeyUtilities.GetBigEndianBytes(serialNumber), mode); - } + public static byte[] DeriveKeyFromMaster( + IKeyProvider masterKey, + int serialNumber, + OtpHashMode mode = OtpHashMode.Sha1) => + DeriveKeyFromMaster(masterKey, KeyUtilities.GetBigEndianBytes(serialNumber), mode); private static HashAlgorithm GetHashAlgorithmForMode(OtpHashMode mode) { diff --git a/src/Otp.NET/KeyUtilities.cs b/src/Otp.NET/KeyUtilities.cs index f81dfb3..24283ca 100644 --- a/src/Otp.NET/KeyUtilities.cs +++ b/src/Otp.NET/KeyUtilities.cs @@ -16,7 +16,7 @@ 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 +FITNESS FOR A PARTICULAR PURPOSE AND NONINFINGEMENT. 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 @@ -41,12 +41,13 @@ internal class KeyUtilities /// This isn't foolproof by any means. The garbage collector could have moved the actual /// location in memory to another location during a collection cycle and left the old data in place /// simply marking it as available. We can't control this or even detect it. - /// This method is simply a good faith effort to limit the exposure of sensitive data in memory as much as possible + /// This method is simply a good faith effort to limit the exposure of sensitive data in + /// memory as much as possible /// internal static void Destroy(byte[] sensitiveData) { if(sensitiveData == null) - throw new ArgumentNullException("sensitiveData"); + throw new ArgumentNullException(nameof(sensitiveData)); new Random().NextBytes(sensitiveData); } @@ -56,7 +57,7 @@ internal static void Destroy(byte[] sensitiveData) /// /// RFC 4226 specifies big endian as the method for converting the counter to data to hash. /// - static internal byte[] GetBigEndianBytes(long input) + internal static byte[] GetBigEndianBytes(long input) { // Since .net uses little endian numbers, we need to reverse the byte order to get big endian. var data = BitConverter.GetBytes(input); @@ -70,7 +71,7 @@ static internal byte[] GetBigEndianBytes(long input) /// /// RFC 4226 specifies big endian as the method for converting the counter to data to hash. /// - static internal byte[] GetBigEndianBytes(int input) + internal static byte[] GetBigEndianBytes(int input) { // Since .net uses little endian numbers, we need to reverse the byte order to get big endian. var data = BitConverter.GetBytes(input); diff --git a/src/Otp.NET/Otp.NET.csproj b/src/Otp.NET/Otp.NET.csproj index ba24252..e61e990 100755 --- a/src/Otp.NET/Otp.NET.csproj +++ b/src/Otp.NET/Otp.NET.csproj @@ -2,7 +2,7 @@ 1.2.2 - netstandard2.0;netstandard1.3;net45 + net45;net5.0;netstandard1.3;netstandard2.0 Otp.NET Otp.NET Kyle Spearrin @@ -17,8 +17,8 @@ For documentation and examples visit the project website on GitHub at https://gi http://i.imgur.com/XFfC64v.png https://github.com/kspearrin/Otp.NET true - vs-signing-key.pfx OtpNet + otp-net.snk diff --git a/src/Otp.NET/Otp.cs b/src/Otp.NET/Otp.cs index fcd23cf..a0b2e4f 100644 --- a/src/Otp.NET/Otp.cs +++ b/src/Otp.NET/Otp.cs @@ -16,7 +16,7 @@ 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 +FITNESS FOR A PARTICULAR PURPOSE AND NONINFINGEMENT. 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 @@ -39,12 +39,12 @@ public abstract class Otp /// /// Secret key /// - protected readonly IKeyProvider secretKey; + protected readonly IKeyProvider _secretKey; /// /// The hash mode to use /// - protected readonly OtpHashMode hashMode; + protected readonly OtpHashMode _hashMode; /// /// Constructor for the abstract class using an explicit secret key @@ -53,15 +53,15 @@ public abstract class Otp /// The hash mode to use public Otp(byte[] secretKey, OtpHashMode mode) { - if(!(secretKey != null)) - throw new ArgumentNullException("secretKey"); - if(!(secretKey.Length > 0)) + if(secretKey == null) + throw new ArgumentNullException(nameof(secretKey)); + if(secretKey.Length <= 0) 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 = new InMemoryKey(secretKey); + _secretKey = new InMemoryKey(secretKey); - this.hashMode = mode; + _hashMode = mode; } /// @@ -71,12 +71,9 @@ public Otp(byte[] secretKey, OtpHashMode mode) /// The hash mode to use public Otp(IKeyProvider key, OtpHashMode mode) { - if (key == null) - throw new ArgumentNullException("key"); + _secretKey = key ?? throw new ArgumentNullException(nameof(key)); - this.secretKey = key; - - this.hashMode = mode; + _hashMode = mode; } /// @@ -92,7 +89,7 @@ public Otp(IKeyProvider key, OtpHashMode mode) /// protected internal long CalculateOtp(byte[] data, OtpHashMode mode) { - byte[] hmacComputedHash = this.secretKey.ComputeHmac(mode, data); + var hmacComputedHash = _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 @@ -110,7 +107,7 @@ protected internal long CalculateOtp(byte[] data, OtpHashMode mode) /// protected internal static string Digits(long input, int digitCount) { - var truncatedValue = ((int)input % (int)Math.Pow(10, digitCount)); + var truncatedValue = (int)input % (int)Math.Pow(10, digitCount); return truncatedValue.ToString().PadLeft(digitCount, '0'); } @@ -126,9 +123,11 @@ protected bool Verify(long initialStep, string valueToVerify, out long matchedSt { if(window == null) window = new VerificationWindow(); + foreach(var frame in window.ValidationCandidates(initialStep)) { - var comparisonValue = this.Compute(frame, this.hashMode); + var comparisonValue = Compute(frame, _hashMode); + if(ValuesEqual(comparisonValue, valueToVerify)) { matchedStep = frame; @@ -149,7 +148,7 @@ private bool ValuesEqual(string a, string b) } var result = 0; - for(int i = 0; i < a.Length; i++) + for(var i = 0; i < a.Length; i++) { result |= a[i] ^ b[i]; } diff --git a/src/Otp.NET/OtpHashMode.cs b/src/Otp.NET/OtpHashMode.cs index a613ff5..11600f6 100644 --- a/src/Otp.NET/OtpHashMode.cs +++ b/src/Otp.NET/OtpHashMode.cs @@ -16,7 +16,7 @@ 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 +FITNESS FOR A PARTICULAR PURPOSE AND NONINFINGEMENT. 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 diff --git a/src/Otp.NET/TimeCorrection.cs b/src/Otp.NET/TimeCorrection.cs index 9654080..7001618 100644 --- a/src/Otp.NET/TimeCorrection.cs +++ b/src/Otp.NET/TimeCorrection.cs @@ -16,7 +16,7 @@ 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 +FITNESS FOR A PARTICULAR PURPOSE AND NONINFINGEMENT. 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 @@ -32,12 +32,15 @@ namespace OtpNet /// /// /// In cases where the local system time is incorrect it is preferable to simply correct the system time. - /// This class is provided to handle cases where it isn't possible for the client, the server, or both, to be on the correct time. + /// This class is provided to handle cases where it isn't possible for the client, the server, or both, + /// to be on the correct time. /// - /// This library provides limited facilities to to ping NIST for a correct network time. This class can be used manually however in cases where a server's time is off - /// and the consumer of this library can't control it. In that case create an instance of this class and provide the current server time as the correct time parameter + /// This library provides limited facilities to to ping NIST for a correct network time. + /// This class can be used manually however in cases where a server's time is off + /// and the consumer of this library can't control it. In that case create an instance of this class + /// and provide the current server time as the correct time parameter /// - /// This class is immutable and therefore threadsafe + /// This class is immutable and therefore thread-safe /// public class TimeCorrection { @@ -46,62 +49,48 @@ public class TimeCorrection /// public static readonly TimeCorrection UncorrectedInstance = new TimeCorrection(); - private readonly TimeSpan timeCorrectionFactor; - /// - /// Constructor used solely for the UncorrectedInstance static field to provide an instance without a correction factor. + /// Constructor used solely for the UncorrectedInstance static field to provide an instance without + /// a correction factor. /// - private TimeCorrection() - { - this.timeCorrectionFactor = TimeSpan.FromSeconds(0); - } + private TimeCorrection() => CorrectionFactor = TimeSpan.FromSeconds(0); /// - /// Creates a corrected time object by providing the known correct current UTC time. The current system UTC time will be used as the reference + /// Creates a corrected time object by providing the known correct current UTC time. + /// The current system UTC time will be used as the reference /// /// - /// This overload assumes UTC. If a base and reference time other than UTC are required then use the other overlaod. + /// This overload assumes UTC. If a base and reference time other than UTC are required + /// then use the other overload. /// /// The current correct UTC time - public TimeCorrection(DateTime correctUtc) - { - this.timeCorrectionFactor = DateTime.UtcNow - correctUtc; - } + public TimeCorrection(DateTime correctUtc) => CorrectionFactor = DateTime.UtcNow - correctUtc; /// - /// Creates a corrected time object by providing the known correct current time and the current reference time that needs correction + /// Creates a corrected time object by providing the known correct current time and the current reference + /// time that needs correction /// /// The current correct time - /// The current reference time (time that will have the correction factor applied in subsequent calls) - public TimeCorrection(DateTime correctTime, DateTime referenceTime) - { - this.timeCorrectionFactor = referenceTime - correctTime; - } + /// The current reference time (time that will have the correction + /// factor applied in subsequent calls) + public TimeCorrection(DateTime correctTime, DateTime referenceTime) => + CorrectionFactor = referenceTime - correctTime; /// /// Applies the correction factor to the reference time and returns a corrected time /// /// The reference time /// The reference time with the correction factor applied - public DateTime GetCorrectedTime(DateTime referenceTime) - { - return referenceTime - timeCorrectionFactor; - } + public DateTime GetCorrectedTime(DateTime referenceTime) => referenceTime - CorrectionFactor; /// /// Applies the correction factor to the current system UTC time and returns a corrected time /// - public DateTime CorrectedUtcNow - { - get { return GetCorrectedTime(DateTime.UtcNow); } - } + public DateTime CorrectedUtcNow => GetCorrectedTime(DateTime.UtcNow); /// /// The timespan that is used to calculate a corrected time /// - public TimeSpan CorrectionFactor - { - get { return this.timeCorrectionFactor; } - } + public TimeSpan CorrectionFactor { get; } } } \ No newline at end of file diff --git a/src/Otp.NET/Totp.cs b/src/Otp.NET/Totp.cs index 9ba2337..c3a401c 100644 --- a/src/Otp.NET/Totp.cs +++ b/src/Otp.NET/Totp.cs @@ -16,7 +16,7 @@ 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 +FITNESS FOR A PARTICULAR PURPOSE AND NONINFINGEMENT. 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 @@ -40,64 +40,82 @@ public class Totp : Otp /// /// The number of ticks as Measured at Midnight Jan 1st 1970; /// - const long unixEpochTicks = 621355968000000000L; + private const long UNIX_EPOCH_TICKS = 621355968000000000L; + /// /// A divisor for converting ticks to seconds /// - const long ticksToSeconds = 10000000L; + private const long TICKS_TO_SECONDS = 10000000L; - private readonly int step; - private readonly int totpSize; - private readonly TimeCorrection correctedTime; + private readonly int _step; + private readonly int _totpSize; + private readonly TimeCorrection _correctedTime; /// /// Create a TOTP instance /// /// The secret key to use in TOTP calculations - /// The time window step amount to use in calculating time windows. The default is 30 as recommended in the RFC + /// The time window step amount to use in calculating time windows. + /// The default is 30 as recommended in the RFC /// The hash mode to use /// The number of digits that the returning TOTP should have. The default is 6. - /// If required, a time correction can be specified to compensate of an out of sync local clock - public Totp(byte[] secretKey, int step = 30, OtpHashMode mode = OtpHashMode.Sha1, int totpSize = 6, TimeCorrection timeCorrection = null) + /// If required, a time correction can be specified to compensate of + /// an out of sync local clock + public Totp( + byte[] secretKey, + int step = 30, + OtpHashMode mode = OtpHashMode.Sha1, + int totpSize = 6, + TimeCorrection timeCorrection = null) : base(secretKey, mode) { VerifyParameters(step, totpSize); - this.step = step; - this.totpSize = totpSize; + _step = step; + _totpSize = totpSize; - // we never null check the corrected time object. Since it's readonly, we'll ensure that it isn't null here and provide neatral functionality in this case. - this.correctedTime = timeCorrection ?? TimeCorrection.UncorrectedInstance; + // we never null check the corrected time object. Since it's readonly, we'll + // ensure that it isn't null here and provide neutral functionality in this case. + _correctedTime = timeCorrection ?? TimeCorrection.UncorrectedInstance; } /// /// Create a TOTP instance /// /// The secret key to use in TOTP calculations - /// The time window step amount to use in calculating time windows. The default is 30 as recommended in the RFC + /// The time window step amount to use in calculating time windows. + /// The default is 30 as recommended in the RFC /// The hash mode to use - /// The number of digits that the returning TOTP should have. The default is 6. - /// If required, a time correction can be specified to compensate of an out of sync local clock - public Totp(IKeyProvider key, int step = 30, OtpHashMode mode = OtpHashMode.Sha1, int totpSize = 6, TimeCorrection timeCorrection = null) + /// The number of digits that the returning TOTP should have. + /// The default is 6. + /// If required, a time correction can be specified to compensate + /// of an out of sync local clock + public Totp( + IKeyProvider key, + int step = 30, + OtpHashMode mode = OtpHashMode.Sha1, + int totpSize = 6, + TimeCorrection timeCorrection = null) : base(key, mode) { VerifyParameters(step, totpSize); - this.step = step; - this.totpSize = totpSize; + _step = step; + _totpSize = totpSize; - // we never null check the corrected time object. Since it's readonly, we'll ensure that it isn't null here and provide neatral functionality in this case. - this.correctedTime = timeCorrection ?? TimeCorrection.UncorrectedInstance; + // we never null check the corrected time object. + // Since it's readonly, we'll ensure that it isn't null here and provide neutral functionality in this case. + _correctedTime = timeCorrection ?? TimeCorrection.UncorrectedInstance; } private static void VerifyParameters(int step, int totpSize) { - if(!(step > 0)) - throw new ArgumentOutOfRangeException("step"); - if(!(totpSize > 0)) - throw new ArgumentOutOfRangeException("totpSize"); - if(!(totpSize <= 10)) - throw new ArgumentOutOfRangeException("totpSize"); + if (step <= 0) + throw new ArgumentOutOfRangeException(nameof(step)); + if (totpSize <= 0) + throw new ArgumentOutOfRangeException(nameof(totpSize)); + if (totpSize > 10) + throw new ArgumentOutOfRangeException(nameof(totpSize)); } /// @@ -105,27 +123,23 @@ private static void VerifyParameters(int step, int totpSize) /// /// The timestamp to use for the TOTP calculation /// a TOTP value - public string ComputeTotp(DateTime timestamp) - { - return ComputeTotpFromSpecificTime(this.correctedTime.GetCorrectedTime(timestamp)); - } + public string ComputeTotp(DateTime timestamp) => + ComputeTotpFromSpecificTime(_correctedTime.GetCorrectedTime(timestamp)); /// /// Takes a timestamp and computes a TOTP value for corrected UTC now /// /// - /// It will be corrected against a corrected UTC time using the provided time correction. If none was provided then simply the current UTC will be used. + /// It will be corrected against a corrected UTC time using the provided time correction. + /// If none was provided then simply the current UTC will be used. /// /// a TOTP value - public string ComputeTotp() - { - return this.ComputeTotpFromSpecificTime(this.correctedTime.CorrectedUtcNow); - } + public string ComputeTotp() => ComputeTotpFromSpecificTime(_correctedTime.CorrectedUtcNow); private string ComputeTotpFromSpecificTime(DateTime timestamp) { var window = CalculateTimeStepFromTimestamp(timestamp); - return this.Compute(window, this.hashMode); + return Compute(window, _hashMode); } /// @@ -142,10 +156,8 @@ private string ComputeTotpFromSpecificTime(DateTime timestamp) /// /// The window of steps to verify /// True if there is a match. - public bool VerifyTotp(string totp, out long timeStepMatched, VerificationWindow window = null) - { - return this.VerifyTotpForSpecificTime(this.correctedTime.CorrectedUtcNow, totp, window, out timeStepMatched); - } + public bool VerifyTotp(string totp, out long timeStepMatched, VerificationWindow window = null) => + VerifyTotpForSpecificTime(_correctedTime.CorrectedUtcNow, totp, window, out timeStepMatched); /// /// Verify a value that has been provided with the calculated value @@ -154,20 +166,31 @@ public bool VerifyTotp(string totp, out long timeStepMatched, VerificationWindow /// the trial TOTP value /// /// This is an output parameter that gives that time step that was used to find a match. - /// This is usefule in cases where a TOTP value should only be used once. This value is a unique identifier of the - /// time step (not the value) that can be used to prevent the same step from being used multiple times + /// This is useful in cases where a TOTP value should only be used once. + /// This value is a unique identifier of the time step (not the value) that can be used + /// to prevent the same step from being used multiple times /// /// The window of steps to verify /// True if there is a match. - public bool VerifyTotp(DateTime timestamp, string totp, out long timeStepMatched, VerificationWindow window = null) - { - return this.VerifyTotpForSpecificTime(this.correctedTime.GetCorrectedTime(timestamp), totp, window, out timeStepMatched); - } - - private bool VerifyTotpForSpecificTime(DateTime timestamp, string totp, VerificationWindow window, out long timeStepMatched) + public bool VerifyTotp( + DateTime timestamp, + string totp, + out long timeStepMatched, + VerificationWindow window = null) => + VerifyTotpForSpecificTime( + _correctedTime.GetCorrectedTime(timestamp), + totp, + window, + out timeStepMatched); + + private bool VerifyTotpForSpecificTime( + DateTime timestamp, + string totp, + VerificationWindow window, + out long timeStepMatched) { var initialStep = CalculateTimeStepFromTimestamp(timestamp); - return this.Verify(initialStep, totp, out timeStepMatched, window); + return Verify(initialStep, totp, out timeStepMatched, window); } /// @@ -175,8 +198,8 @@ private bool VerifyTotpForSpecificTime(DateTime timestamp, string totp, Verifica /// private long CalculateTimeStepFromTimestamp(DateTime timestamp) { - var unixTimestamp = (timestamp.Ticks - unixEpochTicks) / ticksToSeconds; - var window = unixTimestamp / (long)this.step; + var unixTimestamp = (timestamp.Ticks - UNIX_EPOCH_TICKS) / TICKS_TO_SECONDS; + var window = unixTimestamp / (long) _step; return window; } @@ -189,7 +212,7 @@ private long CalculateTimeStepFromTimestamp(DateTime timestamp) /// Number of remaining seconds public int RemainingSeconds() { - return RemainingSecondsForSpecificTime(this.correctedTime.CorrectedUtcNow); + return RemainingSecondsForSpecificTime(_correctedTime.CorrectedUtcNow); } /// @@ -197,15 +220,11 @@ public int RemainingSeconds() /// /// The timestamp /// Number of remaining seconds - public int RemainingSeconds(DateTime timestamp) - { - return RemainingSecondsForSpecificTime(this.correctedTime.GetCorrectedTime(timestamp)); - } + public int RemainingSeconds(DateTime timestamp) => + RemainingSecondsForSpecificTime(_correctedTime.GetCorrectedTime(timestamp)); - private int RemainingSecondsForSpecificTime(DateTime timestamp) - { - return this.step - (int)(((timestamp.Ticks - unixEpochTicks) / ticksToSeconds) % this.step); - } + private int RemainingSecondsForSpecificTime(DateTime timestamp) => + _step - (int) (((timestamp.Ticks - UNIX_EPOCH_TICKS) / TICKS_TO_SECONDS) % _step); /// /// Takes a time step and computes a TOTP code @@ -216,8 +235,8 @@ private int RemainingSecondsForSpecificTime(DateTime timestamp) protected override string Compute(long counter, OtpHashMode mode) { var data = KeyUtilities.GetBigEndianBytes(counter); - var otp = this.CalculateOtp(data, mode); - return Digits(otp, this.totpSize); + var otp = CalculateOtp(data, mode); + return Digits(otp, _totpSize); } } } \ No newline at end of file diff --git a/src/Otp.NET/VerificationWindow.cs b/src/Otp.NET/VerificationWindow.cs index d3d8591..46ef01e 100644 --- a/src/Otp.NET/VerificationWindow.cs +++ b/src/Otp.NET/VerificationWindow.cs @@ -32,8 +32,8 @@ namespace OtpNet /// public class VerificationWindow { - private readonly int previous; - private readonly int future; + private readonly int _previous; + private readonly int _future; /// /// Create an instance of a verification window @@ -42,19 +42,19 @@ public class VerificationWindow /// The number of future frames to accept public VerificationWindow(int previous = 0, int future = 0) { - this.previous = previous; - this.future = future; + _previous = previous; + _future = future; } /// - /// Gets an enumberable of all the possible validation candidates + /// Gets an enumerable of all the possible validation candidates /// /// The initial frame to validate - /// Enumberable of all possible frames that need to be validated + /// Enumerable of all possible frames that need to be validated public IEnumerable ValidationCandidates(long initialFrame) { yield return initialFrame; - for(int i = 1; i <= previous; i++) + for(var i = 1; i <= _previous; i++) { var val = initialFrame - i; if(val < 0) @@ -62,13 +62,14 @@ public IEnumerable ValidationCandidates(long initialFrame) yield return val; } - for(int i = 1; i <= future; i++) + for(var i = 1; i <= _future; i++) yield return initialFrame + i; } /// - /// The verification window that accomodates network delay that is recommended in the RFC + /// The verification window that accommodates network delay that is recommended in the RFC /// - public static readonly VerificationWindow RfcSpecifiedNetworkDelay = new VerificationWindow(previous: 1, future: 1); + public static readonly VerificationWindow RfcSpecifiedNetworkDelay = + new VerificationWindow(previous: 1, future: 1); } } diff --git a/src/Otp.NET/otp-net.snk b/src/Otp.NET/otp-net.snk new file mode 100644 index 0000000..b74969a Binary files /dev/null and b/src/Otp.NET/otp-net.snk differ diff --git a/test/Otp.NET.Test/HotpTest.cs b/test/Otp.NET.Test/HotpTest.cs index 041e33d..e880450 100644 --- a/test/Otp.NET.Test/HotpTest.cs +++ b/test/Otp.NET.Test/HotpTest.cs @@ -1,4 +1,4 @@ -using Moq; +using Moq; using NUnit.Framework; namespace OtpNet.Test @@ -6,7 +6,7 @@ namespace OtpNet.Test [TestFixture] public class HotpTest { - private static readonly byte[] rfc4226Secret = new byte[] { + private static readonly byte[] rfc4226Secret = { 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30 @@ -28,16 +28,16 @@ public void ComputeHOTPRfc4226Test(OtpHashMode hash, long counter, string expect string otp = otpCalc.ComputeHOTP(counter); Assert.That(otp, Is.EqualTo(expectedOtp)); } - - [Test()] + + [Test] public void ContructorWithKeyProviderTest() - { + { //Mock a key provider which always returns an all-zero HMAC (causing an all-zero OTP) - Mock keyMock = new Mock(); - keyMock.Setup(key => key.ComputeHmac(It.Is(m => m == OtpHashMode.Sha1), It.IsAny())).Returns(new byte[20]); + Mock keyMock = new Mock(); + keyMock.Setup(key => key.ComputeHmac(It.Is(m => m == OtpHashMode.Sha1), It.IsAny())).Returns(new byte[20]); - var otp = new Hotp(keyMock.Object, OtpHashMode.Sha1, 6); - Assert.That(otp.ComputeHOTP(0), Is.EqualTo("000000")); + var otp = new Hotp(keyMock.Object, OtpHashMode.Sha1, 6); + Assert.That(otp.ComputeHOTP(0), Is.EqualTo("000000")); Assert.That(otp.ComputeHOTP(1), Is.EqualTo("000000")); } } diff --git a/test/Otp.NET.Test/Otp.NET.Test.csproj b/test/Otp.NET.Test/Otp.NET.Test.csproj index f55592a..ecd4270 100644 --- a/test/Otp.NET.Test/Otp.NET.Test.csproj +++ b/test/Otp.NET.Test/Otp.NET.Test.csproj @@ -1,7 +1,7 @@  - netcoreapp2.2 + net5.0;netcoreapp2.2 false OtpNet.Test diff --git a/test/Otp.NET.Test/TotpTest.cs b/test/Otp.NET.Test/TotpTest.cs index 5e5299a..4b359d1 100644 --- a/test/Otp.NET.Test/TotpTest.cs +++ b/test/Otp.NET.Test/TotpTest.cs @@ -5,7 +5,7 @@ namespace OtpNet.Test { - [TestFixture()] + [TestFixture] public class TotpTest { private const string rfc6238SecretSha1 = "12345678901234567890"; @@ -38,7 +38,7 @@ public void ComputeTOTPTest(string secret, OtpHashMode hash, long timestamp, str Assert.That(otp, Is.EqualTo(expectedOtp)); } - [Test()] + [Test] public void ContructorWithKeyProviderTest() { //Mock a key provider which always returns an all-zero HMAC (causing an all-zero OTP)