This repository was archived by the owner on Dec 12, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathOtp.cs
More file actions
121 lines (105 loc) · 4.41 KB
/
Copy pathOtp.cs
File metadata and controls
121 lines (105 loc) · 4.41 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
using System;
#if NO_WEB
#else
#endif
namespace OtpSharp
{
/// <summary>
/// An abstract class that contains common OTP calculations
/// </summary>
/// <remarks>
/// https://tools.ietf.org/html/rfc4226
/// </remarks>
public abstract class Otp
{
/// <summary>
/// Secret key
/// </summary>
protected readonly IKeyProvider secretKey;
/// <summary>
/// The hash mode to use
/// </summary>
protected readonly OtpHashMode hashMode;
/// <summary>
/// Constructor for the abstract class. This is to guarantee that all implementations have a secret key
/// </summary>
/// <param name="secretKey"></param>
/// <param name="mode">The hash mode to use</param>
public Otp(byte[] secretKey, OtpHashMode mode)
{
if (!(secretKey != null))
throw new ArgumentNullException("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);
this.hashMode = mode;
}
/// <summary>
/// Constrocutor for the abstract class. This is to guarantee that all implementations have a secret key
/// </summary>
/// <param name="secretKey"></param>
/// <param name="mode">The hash mode to use</param>
public Otp(IKeyProvider secretKey, OtpHashMode mode)
{
if (!(secretKey != null))
throw new ArgumentNullException("secretKey");
this.secretKey = secretKey;
this.hashMode = mode;
}
/// <summary>
/// An abstract definition of a compute method. Takes a counter and runs it through the derived algorithm.
/// </summary>
/// <param name="counter">Counter or step</param>
/// <param name="mode">The hash mode to use</param>
/// <returns>OTP calculated code</returns>
protected abstract string Compute(long counter, OtpHashMode mode);
/// <summary>
/// Helper method that calculates OTPs
/// </summary>
protected internal long CalculateOtp(byte[] data, OtpHashMode mode)
{
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
// hmacComputedHash[19] => hmacComputedHash[hmacComputedHash.Length - 1]
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;
}
/// <summary>
/// truncates a number down to the specified number of digits
/// </summary>
protected internal static string Digits(long input, int digitCount)
{
var truncatedValue = ((int)input % (int)Math.Pow(10, digitCount));
return truncatedValue.ToString().PadLeft(digitCount, '0');
}
/// <summary>
/// Verify an OTP value
/// </summary>
/// <param name="initialStep">The initial step to try</param>
/// <param name="valueToVerify">The value to verify</param>
/// <param name="matchedStep">Output parameter that provides the step where the match was found. If no match was found it will be 0</param>
/// <param name="window">The window to verify</param>
/// <returns>True if a match is found</returns>
protected bool Verify(long initialStep, string valueToVerify, out long matchedStep, VerificationWindow window)
{
if (window == null)
window = new VerificationWindow();
foreach (var frame in window.ValidationCandidates(initialStep))
{
var comparisonValue = this.Compute(frame, this.hashMode);
if (comparisonValue == valueToVerify)
{
matchedStep = frame;
return true;
}
}
matchedStep = 0;
return false;
}
}
}