From 721bf97defa174b7a482f21711593e7650ad22ea Mon Sep 17 00:00:00 2001 From: Philipp Crocoll Date: Tue, 24 Oct 2017 06:37:12 +0200 Subject: [PATCH] update Totp-Code using changes from KeeTrayTotp to support Steam --- .../Totp/KeeOtpPluginAdapter.cs | 4 +- src/keepass2android/Totp/TotpData.cs | 17 +- src/keepass2android/Totp/Totp_Client.cs | 387 ------------------ .../Totp/TrayTotpPluginAdapter.cs | 7 +- .../Totp/UpdateTotpTimerTask.cs | 6 +- src/keepass2android/keepass2android.csproj | 11 +- 6 files changed, 32 insertions(+), 400 deletions(-) delete mode 100644 src/keepass2android/Totp/Totp_Client.cs diff --git a/src/keepass2android/Totp/KeeOtpPluginAdapter.cs b/src/keepass2android/Totp/KeeOtpPluginAdapter.cs index d6c19ae2..36d6db37 100644 --- a/src/keepass2android/Totp/KeeOtpPluginAdapter.cs +++ b/src/keepass2android/Totp/KeeOtpPluginAdapter.cs @@ -52,8 +52,8 @@ namespace PluginTOTP res.TotpSeed = parameters[KeyParameter]; - res.Duration = GetIntOrDefault(parameters, StepParameter, 30); - res.Length = GetIntOrDefault(parameters, SizeParameter, 6); + res.Duration = GetIntOrDefault(parameters, StepParameter, 30).ToString(); + res.Length = GetIntOrDefault(parameters, SizeParameter, 6).ToString(); res.IsTotpEnry = true; return res; diff --git a/src/keepass2android/Totp/TotpData.cs b/src/keepass2android/Totp/TotpData.cs index 43896512..84b34296 100644 --- a/src/keepass2android/Totp/TotpData.cs +++ b/src/keepass2android/Totp/TotpData.cs @@ -1,11 +1,24 @@ +using System.Collections.Generic; + namespace PluginTOTP { struct TotpData { public bool IsTotpEnry { get; set; } public string TotpSeed { get; set; } - public int Duration { get; set; } - public int Length { get; set; } + public string Duration { get; set; } + public string Length { get; set; } + public string Url { get; set; } + public string[] Settings + { + get + { + List settings = new List() { Duration, Length}; + if (Url != null) + settings.Add(Url); + return settings.ToArray(); + } + } } } \ No newline at end of file diff --git a/src/keepass2android/Totp/Totp_Client.cs b/src/keepass2android/Totp/Totp_Client.cs deleted file mode 100644 index 9ac5d8cc..00000000 --- a/src/keepass2android/Totp/Totp_Client.cs +++ /dev/null @@ -1,387 +0,0 @@ -using System; - -namespace OtpProviderClient -{ - /// - /// Provides Time-based One Time Passwords RFC 6238. - /// - public class Totp_Provider - { - /// - /// Time reference for TOTP generation. - /// - private static readonly DateTime UnixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); - - /// - /// Duration of generation of each totp, in seconds. - /// - private int _Duration; - - /// - /// Length of the generated totp. - /// - private int _Length; - - private TimeSpan _TimeCorrection; - /// - /// Sets the time span that is used to match the server's UTC time to ensure accurate generation of Time-based One Time Passwords. - /// - public TimeSpan TimeCorrection { set { _TimeCorrection = value; } } - - /// - /// Instanciates a new Totp_Generator. - /// - /// Duration of generation of each totp, in seconds. - /// Length of the generated totp. - public Totp_Provider(int Duration, int Length) - { - if (!(Duration > 0)) throw new Exception("Invalid Duration."); //Throws an exception if the duration is invalid as the class cannot work without it. - _Duration = Duration; //Defines variable from argument. - if (!((Length > 5) && (Length < 9))) throw new Exception("Invalid Length."); //Throws an exception if the length is invalid as the class cannot work without it. - _Length = Length; //Defines variable from argument. - _TimeCorrection = TimeSpan.Zero; //Defines variable from non-constant default value. - } - - /// - /// Returns current time with correction int UTC format. - /// - public DateTime Now - { - get - { - return DateTime.UtcNow - _TimeCorrection; //Computes current time minus time correction giving the corrected time. - } - } - - /// - /// Returns the time remaining before counter incrementation. - /// - public int Timer - { - get - { - var n = (_Duration - (int)((Now - UnixEpoch).TotalSeconds % _Duration)); //Computes the seconds left before counter incrementation. - return n == 0 ? _Duration : n; //Returns timer value from 30 to 1. - } - } - - /// - /// Returns number of intervals that have elapsed. - /// - public long Counter - { - get - { - var ElapsedSeconds = (long)Math.Floor((Now - UnixEpoch).TotalSeconds); //Compute current counter for current time. - return ElapsedSeconds / _Duration; //Applies specified interval to computed counter. - } - } - - /// - /// Converts an unsigned integer to binary data. - /// - /// Unsigned Integer. - /// Binary data. - private byte[] GetBytes(ulong n) - { - byte[] b = new byte[8]; //Math. - b[0] = (byte)(n >> 56); //Math. - b[1] = (byte)(n >> 48); //Math. - b[2] = (byte)(n >> 40); //Math. - b[3] = (byte)(n >> 32); //Math. - b[4] = (byte)(n >> 24); //Math. - b[5] = (byte)(n >> 16); //Math. - b[6] = (byte)(n >> 8); //Math. - b[7] = (byte)(n); //Math. - return b; - } - - /// - /// Generate a Totp using provided binary data. - /// - /// Binary data. - /// Time-based One Time Password. - public string Generate(byte[] key) - { - System.Security.Cryptography.HMACSHA1 hmac = new System.Security.Cryptography.HMACSHA1(key, true); //Instanciates a new hash provider with a key. - byte[] hash = hmac.ComputeHash(GetBytes((ulong)Counter)); //Generates hash from key using counter. - hmac.Clear(); //Clear hash instance securing the key. - - int offset = hash[hash.Length - 1] & 0xf; //Math. - int binary = //Math. - ((hash[offset] & 0x7f) << 24) //Math. - | ((hash[offset + 1] & 0xff) << 16) //Math. - | ((hash[offset + 2] & 0xff) << 8) //Math. - | (hash[offset + 3] & 0xff); //Math. - - int password = binary % Convert.ToInt32(Math.Pow(10, _Length)); //Math. - return password.ToString(new string('0', _Length)); //Math. - } - } - - /// - /// Provides time correction for Time-based One Time Passwords that require accurate DateTime syncronisation with server. - /// - public class TimeCorrection_Provider - { - /// - /// Timer providing the delay between each time correction check. - /// - private System.Timers.Timer _Timer; - - /// - /// Thread which handles the time correction check. - /// - private System.Threading.Thread Task; - - private bool _Enable; - /// - /// Defines weither or not the class will attempt to get time correction from the server. - /// - public bool Enable { get { return _Enable; } set { _Enable = value; _Timer.Enabled = value; } } - - private static int _Interval = 60; - /// - /// Gets or sets the interval in minutes between each online checks for time correction. - /// - /// Time - public static int Interval { get { return _Interval; } set { _Interval = value; } } - private long _IntervalStretcher; - - private volatile string _Url; - /// - /// Returns the URL this instance is using to checks for time correction. - /// - public string Url { get { return _Url; } } - - private TimeSpan _TimeCorrection; - /// - /// Returns the time span between server UTC time and this computer's UTC time of the last check for time correction. - /// - public TimeSpan TimeCorrection { get { return _TimeCorrection; } } - - private DateTime _LastUpdateDateTime; - /// - /// Returns the date and time in universal format of the last online check for time correction. - /// - public DateTime LastUpdateDateTime { get { return _LastUpdateDateTime; } } - - private bool _LastUpdateSucceded = false; - /// - /// Returns true if the last check for time correction was successful. - /// - public bool LastUpdateSucceded { get { return _LastUpdateSucceded; } } - - /// - /// Instanciates a new Totp_TimeCorrection using the specified URL to contact the server. - /// - /// URL of the server to get check. - /// Enable or disable the time correction check. - public TimeCorrection_Provider(string Url, bool Enable = true) - { - if (Url == string.Empty) throw new Exception("Invalid URL."); //Throws exception if the URL is invalid as the class cannot work without it. - _Url = Url; //Defines variable from argument. - _Enable = Enable; //Defines variable from argument. - _LastUpdateDateTime = DateTime.MinValue; //Defines variable from non-constant default value. - _TimeCorrection = TimeSpan.Zero; //Defines variable from non-constant default value. - _Timer = new System.Timers.Timer(); //Instanciates timer. - _Timer.Elapsed += Timer_Elapsed; //Handles the timer event - _Timer.Interval = 1000; //Defines the timer interval to 1 seconds. - _Timer.Enabled = _Enable; //Defines the timer to run if the class is initially enabled. - Task = new System.Threading.Thread(Task_Thread); //Instanciate a new task. - if (_Enable) Task.Start(); //Starts the new thread if the class is initially enabled. - } - - /// - /// Task that occurs every time the timer's interval has elapsed. - /// - private void Timer_Elapsed(object sender, EventArgs e) - { - _IntervalStretcher++; //Increments timer. - if (_IntervalStretcher >= (60 * _Interval)) //Checks if the specified delay has been reached. - { - _IntervalStretcher = 0; //Resets the timer. - Task_Do(); //Attempts to run a new task - } - } - - /// - /// Instanciates a new task and starts it. - /// - /// Informs if reinstanciation of the task has succeeded or not. Will fail if the thread is still active from a previous time correction check. - private bool Task_Do() - { - if (!Task.IsAlive) //Checks if the task is still running. - { - Task = new System.Threading.Thread(Task_Thread); //Instanciate a new task. - Task.Start(); //Starts the new thread. - return true; //Informs if successful - } - return false; //Informs if failed - } - - /// - /// Event that occurs when the timer has reached the required value. Attempts to get time correction from the server. - /// - private void Task_Thread() - { - try - { - var WebClient = new System.Net.WebClient(); //WebClient to connect to server. - WebClient.DownloadData(_Url); //Downloads the server's page using HTTP or HTTPS. - var DateHeader = WebClient.ResponseHeaders.Get("Date"); //Gets the date from the HTTP header of the downloaded page. - _TimeCorrection = DateTime.UtcNow - DateTime.Parse(DateHeader, System.Globalization.CultureInfo.InvariantCulture.DateTimeFormat).ToUniversalTime(); //Compares the downloaded date to the systems date giving us a timespan. - _LastUpdateSucceded = true; //Informs that the date check has succeeded. - } - catch (Exception) - { - _LastUpdateSucceded = false; //Informs that the date check has failed. - } - _LastUpdateDateTime = DateTime.Now; //Informs when the last update has been attempted (succeeded or not). - } - - /// - /// Perform a time correction check, may a few seconds. - /// - /// Resets the timer to 0. Occurs even if the attempt to attempt a new time correction fails. - /// Attempts to get time correction even if disabled. - /// Informs if the time correction check was attempted or not. Will fail if the thread is still active from a previous time correction check. - public bool CheckNow(bool ResetTimer = true, bool ForceCheck = false) - { - if (ResetTimer) //Checks if the timer should be reset. - { - _IntervalStretcher = 0; //Resets the timer. - } - if (ForceCheck || _Enable) //Checks if this check is forced or if time correction is enabled. - { - return Task_Do(); //Attempts to run a new task and informs if attempt to attemp is a success of fail - } - return false; //Informs if not attempted to attempt - } - } - - /// - /// Utility to deal with Base32 encoding and decoding. - /// - /// - /// http://tools.ietf.org/html/rfc4648 - /// - public static class Base32 - { - /// - /// The number of bits in a base32 encoded character. - /// - private const int encodedBitCount = 5; - /// - /// The number of bits in a byte. - /// - private const int byteBitCount = 8; - /// - /// A string containing all of the base32 characters in order. - /// This allows a simple indexof or [index] to convert between - /// a numeric value and an encoded character and vice versa. - /// - private const string encodingChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; - /// - /// Takes a block of data and converts it to a base 32 encoded string. - /// - /// Input data. - /// base 32 string. - public static string Encode(byte[] data) - { - if (data == null) - throw new ArgumentNullException(); - if (data.Length == 0) - throw new ArgumentNullException(); - - // The output character count is calculated in 40 bit blocks. That is because the least - // common blocks size for both binary (8 bit) and base 32 (5 bit) is 40. Padding must be used - // to fill in the difference. - int outputCharacterCount = (int)Math.Ceiling(data.Length / (decimal)encodedBitCount) * byteBitCount; - char[] outputBuffer = new char[outputCharacterCount]; - - byte workingValue = 0; - short remainingBits = encodedBitCount; - int currentPosition = 0; - - foreach (byte workingByte in data) - { - workingValue = (byte)(workingValue | (workingByte >> (byteBitCount - remainingBits))); - outputBuffer[currentPosition++] = encodingChars[workingValue]; - - if (remainingBits <= byteBitCount - encodedBitCount) - { - workingValue = (byte)((workingByte >> (byteBitCount - encodedBitCount - remainingBits)) & 31); - outputBuffer[currentPosition++] = encodingChars[workingValue]; - remainingBits += encodedBitCount; - } - - remainingBits -= byteBitCount - encodedBitCount; - workingValue = (byte)((workingByte << remainingBits) & 31); - } - - // If we didn't finish, write the last current working char. - if (currentPosition != outputCharacterCount) - outputBuffer[currentPosition++] = encodingChars[workingValue]; - - // RFC 4648 specifies that padding up to the end of the next 40 bit block must be provided - // Since the outputCharacterCount does account for the paddingCharacters, fill it out. - while (currentPosition < outputCharacterCount) - { - // The RFC defined paddinc char is '='. - outputBuffer[currentPosition++] = '='; - } - - return new string(outputBuffer); - } - - /// - /// Takes a base 32 encoded value and converts it back to binary data. - /// - /// Base 32 encoded string. - /// Binary data. - public static byte[] Decode(string base32) - { - if (string.IsNullOrEmpty(base32)) - throw new ArgumentNullException(); - - var unpaddedBase32 = base32.ToUpperInvariant().TrimEnd('='); - foreach (var c in unpaddedBase32) - { - if (encodingChars.IndexOf(c) < 0) - throw new ArgumentException("Base32 contains illegal characters."); - } - - // we have already removed the padding so this will tell us how many actual bytes there should be. - int outputByteCount = unpaddedBase32.Length * encodedBitCount / byteBitCount; - byte[] outputBuffer = new byte[outputByteCount]; - - byte workingByte = 0; - short bitsRemaining = byteBitCount; - int mask = 0; - int arrayIndex = 0; - - foreach (char workingChar in unpaddedBase32) - { - int encodedCharacterNumericValue = encodingChars.IndexOf(workingChar); - - if (bitsRemaining > encodedBitCount) - { - mask = encodedCharacterNumericValue << (bitsRemaining - encodedBitCount); - workingByte = (byte)(workingByte | mask); - bitsRemaining -= encodedBitCount; - } - else - { - mask = encodedCharacterNumericValue >> (encodedBitCount - bitsRemaining); - workingByte = (byte)(workingByte | mask); - outputBuffer[arrayIndex++] = workingByte; - workingByte = (byte)(encodedCharacterNumericValue << (byteBitCount - encodedBitCount + bitsRemaining)); - bitsRemaining += byteBitCount - encodedBitCount; - } - } - - return outputBuffer; - } - } -} \ No newline at end of file diff --git a/src/keepass2android/Totp/TrayTotpPluginAdapter.cs b/src/keepass2android/Totp/TrayTotpPluginAdapter.cs index f3ace0cb..d85d9a5d 100644 --- a/src/keepass2android/Totp/TrayTotpPluginAdapter.cs +++ b/src/keepass2android/Totp/TrayTotpPluginAdapter.cs @@ -73,7 +73,7 @@ namespace PluginTOTP } try { - IsLengthValid = (Settings[1] == "6") || (Settings[1] == "8"); //Length + IsLengthValid = (Settings[1] == "6") || (Settings[1] == "8") || (Settings[1] == "S"); //Length } catch (Exception) { @@ -115,11 +115,12 @@ namespace PluginTOTP { bool NoTimeCorrection = false; string[] Settings = SettingsGet(entryFields); - res.Duration = Convert.ToInt16(Settings[0]); - res.Length = Convert.ToInt16(Settings[1]); + res.Duration = Settings[0]; + res.Length = Settings[1]; if (ValidUrl) { NoTimeCorrection = true; + res.Url = Settings[2]; /*var CurrentTimeCorrection = TimeCorrections[Settings[2]]; if (CurrentTimeCorrection != null) { diff --git a/src/keepass2android/Totp/UpdateTotpTimerTask.cs b/src/keepass2android/Totp/UpdateTotpTimerTask.cs index 08b970fb..840d82a2 100644 --- a/src/keepass2android/Totp/UpdateTotpTimerTask.cs +++ b/src/keepass2android/Totp/UpdateTotpTimerTask.cs @@ -6,8 +6,8 @@ using Java.Util; using KeePassLib.Security; using KeePassLib.Utility; using Keepass2android.Pluginsdk; -using OtpProviderClient; using keepass2android; +using KeeTrayTOTP.Libraries; namespace PluginTOTP { @@ -36,8 +36,8 @@ namespace PluginTOTP if (totpData.IsTotpEnry) { //generate a new totp - Totp_Provider prov = new Totp_Provider(totpData.Duration, totpData.Length); - string totp = prov.Generate(Base32.Decode(totpData.TotpSeed)); + TOTPProvider prov = new TOTPProvider(totpData.Settings); + string totp = prov.Generate(totpData.TotpSeed); //update entry and keyboard UpdateEntryData(totp); //broadcast new field value (update EntryActivity). this might result in another keyboard diff --git a/src/keepass2android/keepass2android.csproj b/src/keepass2android/keepass2android.csproj index c14a056e..1a70fa69 100644 --- a/src/keepass2android/keepass2android.csproj +++ b/src/keepass2android/keepass2android.csproj @@ -196,11 +196,14 @@ + + - + + @@ -703,7 +706,9 @@ - + + Designer + @@ -718,7 +723,7 @@ Designer - +