public void TestOTPDefault() { string[] otps = new string[NB_OTP]; string[] otpRefs = new string[] { "78872199", "07062011", "22261770", "63968432", "18394668", "27476910", "64078108", "27055128", "61637484", "72423775" }; OTP otp = new OTP(); otps[0] = otp.GetCurrentOTP(); Assert.AreEqual(otpRefs[0], otps[0]); for (int n = 1; n < NB_OTP; n++) { otps[n] = otp.GetNextOTP(); Assert.AreEqual(otpRefs[n], otps[n]); } }
public void TestOTPCustom() { string[] otps = new string[NB_OTP]; string[] otpRefs = new string[] { "77150324", "35347368", "80798457", "80798457", "86323714", "72722788", "24190050", "32111478", "01733054", "59344564" }; byte[] secretKey = new byte[SECRET_LENGTH] { 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x40, 0x41, 0x42, 0x43, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, }; OTP otp = new OTP(secretKey: secretKey); otps[0] = otp.GetCurrentOTP(); for (int n = 1; n < NB_OTP; n++) { otps[n] = otp.GetNextOTP(); } }
/// <summary> /// Validate if OTP in header is acceptable. This routine could use some refactoring. /// </summary> /// <param name="headers">HttpHeaders from HttpRequestMessage</param> /// <returns>true if acceptable, false otherwise</returns> private bool ValidateOTP(HttpHeaders headers) { bool result = false; // in a "real" system, we'd go to a user database to get a key corresponding with the Id we've already validated byte[] secretKey = proxy.Controllers.Utility.ConvertToByteArray(ConfigurationManager.AppSettings["OTPKey"]); try { IEnumerable <string> headerValues = headers.GetValues("key"); var incomingOTP = headerValues.FirstOrDefault(); // for the sake of sanity in testing...I allow myself to define a static value for // the OTP that will always validate successfully. :/ I'm queasy just writing this comment. if (!String.IsNullOrEmpty(ConfigurationManager.AppSettings["expectedOTP"])) { if (ConfigurationManager.AppSettings["expectedOTP"].Equals(incomingOTP)) { result = true; } } else { // Calculate what the key should be, compare it what we got in the header string expectedOTP = OTP.GetOTP(secretKey); telemetry.TrackTrace("Incoming key is " + incomingOTP + ", Expected key is " + expectedOTP); if (expectedOTP.Equals(incomingOTP)) { telemetry.TrackTrace("Key in header is valid."); result = true; } else { expectedOTP = OTP.GetLastOTP(secretKey); telemetry.TrackTrace("Incoming key is " + incomingOTP + ", Prior key is " + expectedOTP); if (expectedOTP.Equals(incomingOTP)) { telemetry.TrackTrace("Prior key matches, key considered to be valid."); result = true; } else { expectedOTP = OTP.GetNextOTP(secretKey); telemetry.TrackTrace("Incoming key is " + incomingOTP + ", Next key is " + expectedOTP); if (expectedOTP.Equals(incomingOTP)) { telemetry.TrackTrace("Next key matches, key considered to be valid"); result = true; } else { telemetry.TrackTrace("Key in header is not valid."); } } } } } catch (Exception ex) { telemetry.TrackTrace("Exception when validating key in header: " + ex.Message); result = false; } return(result); // NOTE: In our design, we'd never expect more than one request per 30 second OTP window. // Given that, we should never allow a given OTP key to be matched twice within // Now() +/- 30 seconds, assuming the OTP algorithn would never generate a sequence of keys // in which a key repeated within a 3-window range. So, we *should* be keeping track and // noting what key values have been matched in the last few requests, disallowing any // repeated matches. That's just complexity I don't care about for my uses here so not // pursuing it. Caring at that level would imply we also care about rate-limiting requests, etc. // to defeat brute-force auth attempts. This project is a hobby, not a job. }