Skip to content
Open
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
28 changes: 7 additions & 21 deletions Fire.Authentication.Private/Base32Encoding.cs
Original file line number Diff line number Diff line change
Expand Up @@ -91,34 +91,20 @@ private static int CharToValue(char c)
{
int value = c;

if ((value >= 'A' && value <= 'Z'))
if (value is >= 'A' and <= 'Z')
{
return value - 'A';
}
if ((value >= '2' && value <= '7'))
{
return value - ('2' - 26);
}
if ((value >= 'a' && value <= 'z'))
{
return value - 'a';
}

throw new ArgumentException("Character is not a Base32 character.", nameof(c));
return value is >= '2' and <= '7'
? value - ('2' - 26)
: value is >= 'a' and <= 'z' ? value - 'a' : throw new ArgumentException("Character is not a Base32 character.", nameof(c));
}

private static char ValueToChar(byte b)
{
if (b < 26)
{
return (char)(b + 'A');
}
if (b < 32)
{
return (char)(b + ('2' - 26));
}

throw new ArgumentException("Byte is not a Base32 value.", nameof(b));
return b < 26
? (char)(b + 'A')
: b < 32 ? (char)(b + ('2' - 26)) : throw new ArgumentException("Byte is not a Base32 value.", nameof(b));
}
}
}
24 changes: 14 additions & 10 deletions Fire.Authentication.Private/Hotp.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,39 +3,43 @@
namespace Fire.Authentication.Private;
public class Hotp : Otp
{
private readonly int _hotpSize;

public int HotpSize => _hotpSize;
public int HotpSize { get; }

public Hotp(byte[] secretKey, OtpHashMode mode = OtpHashMode.Sha1, int hotpSize = 6)
: base(secretKey, mode)
{
VerifyParameters(hotpSize);
_hotpSize = hotpSize;
HotpSize = hotpSize;
}

public Hotp(IKeyProvider key, OtpHashMode mode = OtpHashMode.Sha1, int hotpSize = 6)
: base(key, mode)
{
VerifyParameters(hotpSize);
_hotpSize = hotpSize;
HotpSize = hotpSize;
}

private static void VerifyParameters(int hotpSize)
{
if (hotpSize < 6 || hotpSize > 8)
if (hotpSize is < 6 or > 8)
{
throw new ArgumentOutOfRangeException(nameof(hotpSize));
}
}

public string ComputeHOTP(long counter) => Compute(counter, _hashMode);
public string ComputeHOTP(long counter)
{
return Compute(counter, _hashMode);
}

public bool VerifyHotp(string hotp, long counter) => hotp == ComputeHOTP(counter);
public bool VerifyHotp(string hotp, long counter)
{
return hotp == ComputeHOTP(counter);
}

protected override string Compute(long counter, OtpHashMode mode)
{
var otp = CalculateOtp(KeyUtilities.GetBigEndianBytes(counter), mode);
return Digits(otp, _hotpSize);
long otp = CalculateOtp(KeyUtilities.GetBigEndianBytes(counter), mode);
return Digits(otp, HotpSize);
}
}
15 changes: 10 additions & 5 deletions Fire.Authentication.Private/InMemoryKey.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,15 @@ public InMemoryKey(byte[] key)
}
}

internal byte[] GetCopyOfKey() => (byte[])_keyData.Clone();
internal byte[] GetCopyOfKey()
{
return (byte[])_keyData.Clone();
}

public byte[] ComputeHmac(OtpHashMode mode, byte[] data)
{
using var hmac = CreateHmacHash(mode);
var key = GetCopyOfKey();
using HMAC hmac = CreateHmacHash(mode);
byte[] key = GetCopyOfKey();
try
{
hmac.Key = key;
Expand All @@ -33,11 +36,13 @@ public byte[] ComputeHmac(OtpHashMode mode, byte[] data)
}
}

private static HMAC CreateHmacHash(OtpHashMode otpHashMode) =>
otpHashMode switch
private static HMAC CreateHmacHash(OtpHashMode otpHashMode)
{
return otpHashMode switch
{
OtpHashMode.Sha256 => new HMACSHA256(),
OtpHashMode.Sha512 => new HMACSHA512(),
_ => new HMACSHA1() // OtpHashMode.Sha1 or default
};
}
}
14 changes: 6 additions & 8 deletions Fire.Authentication.Private/KeyGeneration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ public static class KeyGeneration
public static byte[] GenerateRandomKey(int length)
{
byte[] key = new byte[length];
using var rnd = RandomNumberGenerator.Create();
using RandomNumberGenerator rnd = RandomNumberGenerator.Create();
rnd.GetBytes(key);
return key;
}
Expand All @@ -20,15 +20,13 @@ public static byte[] GenerateRandomKey(OtpHashMode mode = OtpHashMode.Sha1)

public static byte[] DeriveKeyFromMaster(IKeyProvider masterKey, byte[] identifier, OtpHashMode mode = OtpHashMode.Sha1)
{
if (masterKey == null)
{
throw new ArgumentNullException(nameof(masterKey));
}
return masterKey.ComputeHmac(mode, identifier);
return masterKey == null ? throw new ArgumentNullException(nameof(masterKey)) : masterKey.ComputeHmac(mode, identifier);
}

public static byte[] DeriveKeyFromMaster(IKeyProvider masterKey, int serialNumber, OtpHashMode mode = OtpHashMode.Sha1) =>
DeriveKeyFromMaster(masterKey, KeyUtilities.GetBigEndianBytes(serialNumber), mode);
public static byte[] DeriveKeyFromMaster(IKeyProvider masterKey, int serialNumber, OtpHashMode mode = OtpHashMode.Sha1)
{
return DeriveKeyFromMaster(masterKey, KeyUtilities.GetBigEndianBytes(serialNumber), mode);
}

private static int LengthForMode(OtpHashMode mode)
{
Expand Down
6 changes: 3 additions & 3 deletions Fire.Authentication.Private/KeyUtilities.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@ internal static void Destroy(byte[] sensitiveData)
throw new ArgumentNullException(nameof(sensitiveData));
}

var rng = new Random();
Random rng = new();
rng.NextBytes(sensitiveData);
}

internal static byte[] GetBigEndianBytes(long input)
{
var data = BitConverter.GetBytes(input);
byte[] data = BitConverter.GetBytes(input);
if (BitConverter.IsLittleEndian)
{
Array.Reverse(data);
Expand All @@ -27,7 +27,7 @@ internal static byte[] GetBigEndianBytes(long input)

internal static byte[] GetBigEndianBytes(int input)
{
var data = BitConverter.GetBytes(input);
byte[] data = BitConverter.GetBytes(input);
if (BitConverter.IsLittleEndian)
{
Array.Reverse(data);
Expand Down
30 changes: 18 additions & 12 deletions Fire.Authentication.Private/Otp.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,25 +25,27 @@ public Otp(IKeyProvider key, OtpHashMode mode)

protected internal long CalculateOtp(byte[] data, OtpHashMode mode)
{
var hmacComputedHash = _secretKey.ComputeHmac(mode, data);
byte[] hmacComputedHash = _secretKey.ComputeHmac(mode, data);

int offset = hmacComputedHash[hmacComputedHash.Length - 1] & 0x0F;
return (hmacComputedHash[offset] & 0x7f) << 24
| (hmacComputedHash[offset + 1] & 0xff) << 16
| (hmacComputedHash[offset + 2] & 0xff) << 8
| (hmacComputedHash[offset + 3] & 0xff) % 1000000;
int offset = hmacComputedHash[^1] & 0x0F;
return ((hmacComputedHash[offset] & 0x7f) << 24)
| ((hmacComputedHash[offset + 1] & 0xff) << 16)
| ((hmacComputedHash[offset + 2] & 0xff) << 8)
| ((hmacComputedHash[offset + 3] & 0xff) % 1000000);
}

protected internal static string Digits(long input, int digitCount) =>
(input % (long)Math.Pow(10, digitCount)).ToString().PadLeft(digitCount, '0');
protected internal static string Digits(long input, int digitCount)
{
return (input % (long)Math.Pow(10, digitCount)).ToString().PadLeft(digitCount, '0');
}

protected bool Verify(long initialStep, string valueToVerify, out long matchedStep, VerificationWindow window)
{
window ??= new VerificationWindow();

foreach (var frame in window.ValidationCandidates(initialStep))
foreach (long frame in window.ValidationCandidates(initialStep))
{
var comparisonValue = Compute(frame, _hashMode);
string comparisonValue = Compute(frame, _hashMode);
if (ValuesEqual(comparisonValue, valueToVerify))
{
matchedStep = frame;
Expand All @@ -58,11 +60,15 @@ protected bool Verify(long initialStep, string valueToVerify, out long matchedSt
private bool ValuesEqual(string a, string b)
{
if (a.Length != b.Length)
{
return false;
}

var result = 0;
for (var i = 0; i < a.Length; i++)
int result = 0;
for (int i = 0; i < a.Length; i++)
{
result |= a[i] ^ b[i];
}

return result == 0;
}
Expand Down
37 changes: 22 additions & 15 deletions Fire.Authentication.Private/OtpUri.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@ public OtpUri(
_ = secret ?? throw new ArgumentNullException(nameof(secret));
_ = user ?? throw new ArgumentNullException(nameof(user));
if (digits < 0)
{
throw new ArgumentOutOfRangeException(nameof(digits));
}

Type = schema;
Secret = secret;
Expand Down Expand Up @@ -60,17 +62,22 @@ public OtpUri(
: this(schema, Base32Encoding.ToString(secret), user, issuer, algorithm, digits, period, counter)
{ }

public Uri ToUri() => new Uri(ToString());
public Uri ToUri()
{
return new Uri(ToString());
}

public override string ToString()
{
var parameters = new Dictionary<string, string>
Dictionary<string, string> parameters = new()
{
{ "secret", Secret.TrimEnd('=') }
};

if (!string.IsNullOrWhiteSpace(Issuer))
{
parameters.Add("issuer", Uri.EscapeDataString(Issuer));
}

parameters.Add("algorithm", Algorithm.ToString().ToUpper());
parameters.Add("digits", Digits.ToString());
Expand All @@ -85,28 +92,28 @@ public override string ToString()
break;
}

var uriBuilder = new StringBuilder("otpauth://");
uriBuilder.Append(Type.ToString().ToLowerInvariant());
StringBuilder uriBuilder = new("otpauth://");
_ = uriBuilder.Append(Type.ToString().ToLowerInvariant());

if (!string.IsNullOrWhiteSpace(Issuer))
{
uriBuilder.Append("/");
uriBuilder.Append(Uri.EscapeDataString(Issuer));
_ = uriBuilder.Append("/");
_ = uriBuilder.Append(Uri.EscapeDataString(Issuer));
}

uriBuilder.Append(":");
uriBuilder.Append(Uri.EscapeDataString(User));
uriBuilder.Append("?");
_ = uriBuilder.Append(":");
_ = uriBuilder.Append(Uri.EscapeDataString(User));
_ = uriBuilder.Append("?");

foreach (var pair in parameters)
foreach (KeyValuePair<string, string> pair in parameters)
{
uriBuilder.Append(pair.Key);
uriBuilder.Append("=");
uriBuilder.Append(pair.Value);
uriBuilder.Append("&");
_ = uriBuilder.Append(pair.Key);
_ = uriBuilder.Append("=");
_ = uriBuilder.Append(pair.Value);
_ = uriBuilder.Append("&");
}

uriBuilder.Remove(uriBuilder.Length - 1, 1);
_ = uriBuilder.Remove(uriBuilder.Length - 1, 1);
return uriBuilder.ToString();
}
}
30 changes: 21 additions & 9 deletions Fire.Authentication.Private/TimeCorrection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,27 @@ namespace Fire.Authentication.Private;

public class TimeCorrection
{
public static readonly TimeCorrection UncorrectedInstance = new TimeCorrection();

private TimeCorrection() => CorrectionFactor = TimeSpan.FromSeconds(0);

public TimeCorrection(DateTime correctUtc) => CorrectionFactor = DateTime.UtcNow - correctUtc;

public TimeCorrection(DateTime correctTime, DateTime referenceTime) => CorrectionFactor = referenceTime - correctTime;

public DateTime GetCorrectedTime(DateTime referenceTime) => referenceTime - CorrectionFactor;
public static readonly TimeCorrection UncorrectedInstance = new();

private TimeCorrection()
{
CorrectionFactor = TimeSpan.FromSeconds(0);
}

public TimeCorrection(DateTime correctUtc)
{
CorrectionFactor = DateTime.UtcNow - correctUtc;
}

public TimeCorrection(DateTime correctTime, DateTime referenceTime)
{
CorrectionFactor = referenceTime - correctTime;
}

public DateTime GetCorrectedTime(DateTime referenceTime)
{
return referenceTime - CorrectionFactor;
}

public DateTime CorrectedUtcNow => GetCorrectedTime(DateTime.UtcNow);

Expand Down
Loading