Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

.net 5 support + small refactoring #33

Merged
merged 1 commit into from
Dec 23, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 11 additions & 10 deletions src/Otp.NET/Base32Encoding.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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)
Expand All @@ -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)
Expand All @@ -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));
}
}
}
40 changes: 14 additions & 26 deletions src/Otp.NET/Hotp.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -36,7 +36,7 @@ namespace OtpNet
/// </remarks>
public class Hotp : Otp
{
private readonly int hotpSize;
private readonly int _hotpSize;

/// <summary>
/// Create a HOTP instance
Expand All @@ -49,7 +49,7 @@ public Hotp(byte[] secretKey, OtpHashMode mode = OtpHashMode.Sha1, int hotpSize
{
VerifyParameters(hotpSize);

this.hotpSize = hotpSize;
_hotpSize = hotpSize;
}

/// <summary>
Expand All @@ -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));
}

/// <summary>
/// Takes a counter and then computes a HOTP value
/// </summary>
/// <param name="timestamp">The timestamp to use for the HOTP calculation</param>
/// <param name="counter"></param>
/// <returns>a HOTP value</returns>
public string ComputeHOTP(long counter)
{
return this.Compute(counter, this.hashMode);
}
public string ComputeHOTP(long counter) => Compute(counter, _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>
/// <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;
}
}
public bool VerifyHotp(string hotp, long counter) => hotp == ComputeHOTP(counter);

/// <summary>
/// Takes a time step and computes a HOTP code
Expand All @@ -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);
}
}
}
2 changes: 1 addition & 1 deletion src/Otp.NET/IKeyProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
38 changes: 19 additions & 19 deletions src/Otp.NET/InMemoryKey.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -49,27 +49,27 @@ namespace OtpNet
/// </remarks>
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;

/// <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))
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);
}

/// <summary>
Expand All @@ -81,10 +81,10 @@ public InMemoryKey(byte[] key)
/// <returns>Plaintext Key</returns>
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;
}
Expand All @@ -97,10 +97,10 @@ internal byte[] GetCopyOfKey()
/// <returns>HMAC of the key and data</returns>
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;
Expand All @@ -120,7 +120,7 @@ public byte[] ComputeHmac(OtpHashMode mode, byte[] data)
/// </summary>
private static HMAC CreateHmacHash(OtpHashMode otpHashMode)
{
HMAC hmacAlgorithm = null;
HMAC hmacAlgorithm;
switch(otpHashMode)
{
case OtpHashMode.Sha256:
Expand Down
28 changes: 17 additions & 11 deletions src/Otp.NET/KeyGeneration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -34,13 +34,13 @@ namespace OtpNet
public static class KeyGeneration
{
/// <summary>
/// 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
/// </summary>
/// <param name="length">Key length</param>
/// <returns>The generated key</returns>
public static byte[] GenerateRandomKey(int length)
{
byte[] key = new byte[length];
var key = new byte[length];
using(var rnd = RandomNumberGenerator.Create())
{
rnd.GetBytes(key);
Expand All @@ -49,7 +49,7 @@ public static byte[] GenerateRandomKey(int length)
}

/// <summary>
/// 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
/// </summary>
/// <param name="mode">HashMode</param>
/// <returns>Key</returns>
Expand All @@ -65,10 +65,14 @@ public static byte[] GenerateRandomKey(OtpHashMode mode = OtpHashMode.Sha1)
/// <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)
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);
}

Expand All @@ -77,12 +81,14 @@ public static byte[] DeriveKeyFromMaster(IKeyProvider masterKey, byte[] publicId
/// </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>
/// <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);
}
public static byte[] DeriveKeyFromMaster(
IKeyProvider masterKey,
int serialNumber,
OtpHashMode mode = OtpHashMode.Sha1) =>
DeriveKeyFromMaster(masterKey, KeyUtilities.GetBigEndianBytes(serialNumber), mode);

private static HashAlgorithm GetHashAlgorithmForMode(OtpHashMode mode)
{
Expand Down
11 changes: 6 additions & 5 deletions src/Otp.NET/KeyUtilities.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
/// </remarks>
internal static void Destroy(byte[] sensitiveData)
{
if(sensitiveData == null)
throw new ArgumentNullException("sensitiveData");
throw new ArgumentNullException(nameof(sensitiveData));
new Random().NextBytes(sensitiveData);
}

Expand All @@ -56,7 +57,7 @@ internal static void Destroy(byte[] sensitiveData)
/// <remarks>
/// RFC 4226 specifies big endian as the method for converting the counter to data to hash.
/// </remarks>
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);
Expand All @@ -70,7 +71,7 @@ static internal byte[] GetBigEndianBytes(long input)
/// <remarks>
/// RFC 4226 specifies big endian as the method for converting the counter to data to hash.
/// </remarks>
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);
Expand Down
4 changes: 2 additions & 2 deletions src/Otp.NET/Otp.NET.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<PropertyGroup>
<Version>1.2.2</Version>
<TargetFrameworks>netstandard2.0;netstandard1.3;net45</TargetFrameworks>
<TargetFrameworks>net45;net5.0;netstandard1.3;netstandard2.0</TargetFrameworks>
<PackageId>Otp.NET</PackageId>
<Title>Otp.NET</Title>
<Authors>Kyle Spearrin</Authors>
Expand All @@ -17,8 +17,8 @@ For documentation and examples visit the project website on GitHub at https://gi
<PackageIconUrl>http://i.imgur.com/XFfC64v.png</PackageIconUrl>
<RepositoryUrl>https://github.com/kspearrin/Otp.NET</RepositoryUrl>
<SignAssembly>true</SignAssembly>
<AssemblyOriginatorKeyFile>vs-signing-key.pfx</AssemblyOriginatorKeyFile>
<RootNamespace>OtpNet</RootNamespace>
<AssemblyOriginatorKeyFile>otp-net.snk</AssemblyOriginatorKeyFile>
</PropertyGroup>

<ItemGroup Condition=" '$(TargetFramework)' == 'net45' ">
Expand Down
Loading