implement editor for TOTP settings in EntryEditActivity, closes https://github.com/PhilippC/keepass2android/issues/770
This commit is contained in:
@@ -33,12 +33,17 @@ using KeePassLib.Security;
|
||||
using Android.Content.PM;
|
||||
using System.IO;
|
||||
using System.Globalization;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using Android.Content.Res;
|
||||
using Android.Database;
|
||||
using Android.Graphics;
|
||||
using Android.Graphics.Drawables;
|
||||
using Android.Util;
|
||||
using keepass2android.Io;
|
||||
using KeePassLib.Serialization;
|
||||
using KeeTrayTOTP.Libraries;
|
||||
using PluginTOTP;
|
||||
using Debug = System.Diagnostics.Debug;
|
||||
using File = System.IO.File;
|
||||
using Object = Java.Lang.Object;
|
||||
@@ -287,7 +292,31 @@ namespace keepass2android
|
||||
EditAdvancedString(ees.FindViewById(Resource.Id.edit_extra));
|
||||
};
|
||||
SetAddExtraStringEnabled();
|
||||
FindViewById(Resource.Id.entry_extras_container).Visibility =
|
||||
|
||||
|
||||
Button configureTotpButton = (Button)FindViewById(Resource.Id.configure_totp);
|
||||
|
||||
configureTotpButton.Visibility = CanConfigureOtpSettings() ? ViewStates.Gone : ViewStates.Visible;
|
||||
configureTotpButton.Click += (sender, e) =>
|
||||
{
|
||||
bool added = false;
|
||||
View ees = FindExtraEditSection("otp");
|
||||
if (ees == null)
|
||||
{
|
||||
LinearLayout container = (LinearLayout) FindViewById(Resource.Id.advanced_container);
|
||||
|
||||
KeyValuePair<string, ProtectedString> pair =
|
||||
new KeyValuePair<string, ProtectedString>("otp", new ProtectedString(true, ""));
|
||||
ees = CreateExtraStringView(pair);
|
||||
container.AddView(ees);
|
||||
added = true;
|
||||
}
|
||||
|
||||
|
||||
EditTotpString(ees.FindViewById(Resource.Id.edit_extra));
|
||||
};
|
||||
|
||||
FindViewById(Resource.Id.entry_extras_container).Visibility =
|
||||
State.EditMode.ShowAddExtras || State.Entry.Strings.Any(s => !PwDefs.IsStandardField(s.Key)) ? ViewStates.Visible : ViewStates.Gone;
|
||||
FindViewById(Resource.Id.entry_binaries_container).Visibility =
|
||||
State.EditMode.ShowAddAttachments || State.Entry.Binaries.Any() ? ViewStates.Visible : ViewStates.Gone;
|
||||
@@ -402,9 +431,17 @@ namespace keepass2android
|
||||
private void SetAddExtraStringEnabled()
|
||||
{
|
||||
((Button)FindViewById(Resource.Id.add_advanced)).Visibility = (!App.Kp2a.CurrentDb.DatabaseFormat.CanHaveCustomFields || !State.EditMode.ShowAddExtras) ? ViewStates.Gone : ViewStates.Visible;
|
||||
((Button)FindViewById(Resource.Id.configure_totp)).Visibility = CanConfigureOtpSettings() ? ViewStates.Gone : ViewStates.Visible;
|
||||
}
|
||||
|
||||
private void MakePasswordVisibleOrHidden()
|
||||
private bool CanConfigureOtpSettings()
|
||||
{
|
||||
return (!App.Kp2a.CurrentDb.DatabaseFormat.CanHaveCustomFields || !State.EditMode.ShowAddExtras)
|
||||
&& (new Kp2aTotp().TryGetAdapter(new PwEntryOutput(State.Entry, App.Kp2a.CurrentDb)) == null || (State.Entry.Strings.GetKeys().Contains("otp"))) //only allow to edit KeeWeb/KeepassXC style otps
|
||||
;
|
||||
}
|
||||
|
||||
private void MakePasswordVisibleOrHidden()
|
||||
{
|
||||
EditText password = (EditText) FindViewById(Resource.Id.entry_password);
|
||||
TextView confpassword = (TextView) FindViewById(Resource.Id.entry_confpassword);
|
||||
@@ -942,7 +979,8 @@ namespace keepass2android
|
||||
binariesGroup.Visibility = ViewStates.Visible;
|
||||
FindViewById(Resource.Id.entry_binaries_container).Visibility = ViewStates.Visible;
|
||||
((Button)FindViewById(Resource.Id.add_advanced)).Visibility = ViewStates.Visible;
|
||||
FindViewById(Resource.Id.entry_extras_container).Visibility = ViewStates.Visible;
|
||||
((Button)FindViewById(Resource.Id.configure_totp)).Visibility = ViewStates.Visible;
|
||||
FindViewById(Resource.Id.entry_extras_container).Visibility = ViewStates.Visible;
|
||||
|
||||
return true;
|
||||
case Android.Resource.Id.Home:
|
||||
@@ -1020,6 +1058,7 @@ namespace keepass2android
|
||||
if (type == "bool")
|
||||
{
|
||||
RelativeLayout ees = (RelativeLayout)LayoutInflater.Inflate(Resource.Layout.entry_edit_section_bool, null);
|
||||
ees.Tag = pair.Key;
|
||||
var keyView = ((TextView)ees.FindViewById(Resource.Id.extrakey));
|
||||
var checkbox = ((CheckBox)ees.FindViewById(Resource.Id.checkbox));
|
||||
var valueView = ((TextView)ees.FindViewById(Resource.Id.value));
|
||||
@@ -1037,6 +1076,7 @@ namespace keepass2android
|
||||
else if (type == "file")
|
||||
{
|
||||
RelativeLayout ees = (RelativeLayout)LayoutInflater.Inflate(Resource.Layout.entry_edit_section_file, null);
|
||||
ees.Tag = pair.Key;
|
||||
var keyView = ((TextView)ees.FindViewById(Resource.Id.extrakey));
|
||||
var titleView = ((TextView)ees.FindViewById(Resource.Id.title));
|
||||
keyView.Text = pair.Key;
|
||||
@@ -1054,6 +1094,7 @@ namespace keepass2android
|
||||
else
|
||||
{
|
||||
RelativeLayout ees = (RelativeLayout)LayoutInflater.Inflate(Resource.Layout.entry_edit_section, null);
|
||||
ees.Tag = pair.Key;
|
||||
var keyView = ((TextView)ees.FindViewById(Resource.Id.extrakey));
|
||||
var titleView = ((TextView)ees.FindViewById(Resource.Id.title));
|
||||
keyView.Text = pair.Key;
|
||||
@@ -1090,8 +1131,184 @@ namespace keepass2android
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void EditTotpString(View sender)
|
||||
{
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
View dlgView = LayoutInflater.Inflate(Resource.Layout.
|
||||
configure_totp_dialog, null);
|
||||
|
||||
private void EditAdvancedString(View sender)
|
||||
|
||||
|
||||
builder.SetView(dlgView);
|
||||
builder.SetNegativeButton(Android.Resource.String.Cancel, (o, args) => { });
|
||||
builder.SetPositiveButton(Android.Resource.String.Ok, (o, args) =>
|
||||
{
|
||||
|
||||
var targetField = ((TextView)((View)sender.Parent).FindViewById(Resource.Id.value));
|
||||
if (targetField != null)
|
||||
{
|
||||
string entryTitle = Util.GetEditText(this, Resource.Id.entry_title);
|
||||
string username = Util.GetEditText(this, Resource.Id.entry_user_name);
|
||||
string secret = dlgView.FindViewById<TextView>(Resource.Id.totp_secret_key).Text;
|
||||
string totpLength = dlgView.FindViewById<EditText>(Resource.Id.totp_length).Text;
|
||||
string timeStep = dlgView.FindViewById<EditText>(Resource.Id.totp_time_step).Text;
|
||||
var checkedTotpId = (int)dlgView.FindViewById<RadioGroup>(Resource.Id.totp_encoding).CheckedRadioButtonId;
|
||||
TotpEncoding encoding = (checkedTotpId == Resource.Id.totp_encoding_steam)
|
||||
? TotpEncoding.Steam : (checkedTotpId == Resource.Id.totp_encoding_rfc6238 ? TotpEncoding.Default : TotpEncoding.Custom);
|
||||
var algorithm = (int)dlgView.FindViewById<Spinner>(Resource.Id.totp_algorithm).SelectedItemPosition;
|
||||
|
||||
targetField.Text = BuildOtpString(entryTitle, username, secret, totpLength, timeStep, encoding, algorithm);
|
||||
}
|
||||
else
|
||||
{
|
||||
Toast.MakeText(this, "did not find target field", ToastLength.Long).Show();
|
||||
}
|
||||
|
||||
|
||||
//not calling State.Entry.Strings.Set(...). We only do this when the user saves the changes.
|
||||
State.EntryModified = true;
|
||||
|
||||
});
|
||||
Dialog dialog = builder.Create();
|
||||
|
||||
dlgView.FindViewById<RadioButton>(Resource.Id.totp_encoding_custom).CheckedChange += (o, args) =>
|
||||
{
|
||||
dlgView.FindViewById(Resource.Id.totp_custom_settings_group).Visibility = args.IsChecked ? ViewStates.Visible : ViewStates.Gone;
|
||||
};
|
||||
|
||||
|
||||
//copy values from entry into dialog
|
||||
View ees = (View)sender.Parent;
|
||||
TotpData totpData = new Kp2aTotp().TryGetTotpData(new PwEntryOutput(State.Entry, App.Kp2a.CurrentDb));
|
||||
if (totpData != null)
|
||||
{
|
||||
dlgView.FindViewById<TextView>(Resource.Id.totp_secret_key).Text = totpData.TotpSeed;
|
||||
if (totpData.Encoder == TotpData.EncoderSteam)
|
||||
{
|
||||
dlgView.FindViewById<RadioButton>(Resource.Id.totp_encoding_steam).Checked = true;
|
||||
}
|
||||
else if ((totpData.Encoder == TotpData.EncoderRfc6238) && (totpData.IsDefaultRfc6238))
|
||||
{
|
||||
dlgView.FindViewById<RadioButton>(Resource.Id.totp_encoding_rfc6238).Checked = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
dlgView.FindViewById<RadioButton>(Resource.Id.totp_encoding_custom).Checked = true;
|
||||
}
|
||||
|
||||
dlgView.FindViewById<EditText>(Resource.Id.totp_length).Text = totpData.Length;
|
||||
dlgView.FindViewById<EditText>(Resource.Id.totp_time_step).Text = totpData.Duration;
|
||||
dlgView.FindViewById <Spinner>(Resource.Id.totp_algorithm).SetSelection(totpData.HashAlgorithm == TotpData.HashSha1 ? 0 : (
|
||||
totpData.HashAlgorithm == TotpData.HashSha256 ? 1:
|
||||
(totpData.HashAlgorithm == TotpData.HashSha256 ? 2 : 0)));
|
||||
|
||||
dlgView.FindViewById(Resource.Id.totp_custom_settings_group).Visibility = dlgView.FindViewById<RadioButton>(Resource.Id.totp_encoding_custom).Checked ? ViewStates.Visible : ViewStates.Gone;
|
||||
}
|
||||
|
||||
_passwordFont.ApplyTo(dlgView.FindViewById<EditText>(Resource.Id.totp_secret_key));
|
||||
Util.SetNoPersonalizedLearning(dlgView);
|
||||
|
||||
|
||||
|
||||
dialog.Show();
|
||||
|
||||
}
|
||||
|
||||
string SanitizeInput(string encodedData)
|
||||
{
|
||||
if (encodedData.Length <= 0)
|
||||
{
|
||||
return encodedData;
|
||||
}
|
||||
|
||||
StringBuilder newEncodedDataBuilder = new StringBuilder(encodedData);
|
||||
int i = 0;
|
||||
foreach (var ch in encodedData)
|
||||
{
|
||||
switch (ch)
|
||||
{
|
||||
case '0':
|
||||
newEncodedDataBuilder[i++] = 'O';
|
||||
break;
|
||||
case '1':
|
||||
newEncodedDataBuilder[i++] = 'L';
|
||||
break;
|
||||
case '8':
|
||||
newEncodedDataBuilder[i++] = 'B';
|
||||
break;
|
||||
default:
|
||||
if (('A' <= ch && ch <= 'Z') || ('a' <= ch && ch <= 'z') || ('2' <= ch && ch <= '7'))
|
||||
{
|
||||
newEncodedDataBuilder[i++] = ch;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
string newEncodedData = newEncodedDataBuilder.ToString().Substring(0, i);
|
||||
|
||||
return AddPadding(newEncodedData);
|
||||
|
||||
}
|
||||
|
||||
|
||||
string AddPadding(string encodedData)
|
||||
{
|
||||
if (encodedData.Length <= 0 || encodedData.Length % 8 == 0) {
|
||||
return encodedData;
|
||||
}
|
||||
|
||||
int rBytes = encodedData.Length % 8;
|
||||
// rBytes must be a member of {2, 4, 5, 7}
|
||||
if (1 == rBytes || 3 == rBytes || 6 == rBytes) {
|
||||
return encodedData;
|
||||
}
|
||||
|
||||
string newEncodedData = encodedData;
|
||||
for (int nPads = 8 - rBytes; nPads > 0; --nPads)
|
||||
{
|
||||
newEncodedData += "=";
|
||||
}
|
||||
|
||||
return newEncodedData;
|
||||
}
|
||||
|
||||
enum TotpEncoding
|
||||
{
|
||||
Default, Steam, Custom
|
||||
}
|
||||
|
||||
private string BuildOtpString(string entryTitle, string userName, string secret, string totpLength, string timeStep, TotpEncoding encoding, int algorithm)
|
||||
{
|
||||
string entryEncoded = string.IsNullOrWhiteSpace(entryTitle)
|
||||
? "Keepass2Android"
|
||||
: System.Uri.EscapeUriString(entryTitle);
|
||||
return $"otpauth://totp/{entryEncoded}:{System.Uri.EscapeUriString(userName)}?" +
|
||||
$"secret={SanitizeInput(secret)}" +
|
||||
$"&issuer={ entryEncoded}"
|
||||
+ (encoding != TotpEncoding.Custom? "" : $"&period={timeStep}&digits={totpLength}&algorithm={AlgorithmIndexToString(algorithm)}") +
|
||||
(encoding == TotpEncoding.Steam ? "&encoder=steam" : "");
|
||||
|
||||
}
|
||||
|
||||
private string AlgorithmIndexToString(in int algorithm)
|
||||
{
|
||||
switch (algorithm)
|
||||
{
|
||||
case 0:
|
||||
return "SHA1";
|
||||
case 1:
|
||||
return "SHA256";
|
||||
case 2:
|
||||
return "SHA512";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
private void EditAdvancedString(View sender)
|
||||
{
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
View dlgView = LayoutInflater.Inflate(Resource.Layout.
|
||||
|
Binary file not shown.
After Width: | Height: | Size: 539 B |
@@ -0,0 +1,13 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M11.99,2C6.47,2 2,6.48 2,12s4.47,10 9.99,10C17.52,22 22,17.52 22,12S17.52,2 11.99,2zM12,20c-4.42,0 -8,-3.58 -8,-8s3.58,-8 8,-8 8,3.58 8,8 -3.58,8 -8,8z"/>
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M12.5,7H11v6l5.25,3.15 0.75,-1.23 -4.5,-2.67z"/>
|
||||
</vector>
|
@@ -0,0 +1,69 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/totp_secret_key"
|
||||
android:singleLine="true"
|
||||
android:inputType="text"
|
||||
android:hint="@string/totp_secret_key"
|
||||
android:dropDownWidth="match_parent"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
/>
|
||||
|
||||
<RadioGroup
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/totp_encoding">
|
||||
<RadioButton
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:checked="true"
|
||||
android:text="@string/totp_encoding_rfc6238"
|
||||
android:id="@+id/totp_encoding_rfc6238" />
|
||||
<RadioButton
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/totp_encoding_steam"
|
||||
android:id="@+id/totp_encoding_steam" />
|
||||
<RadioButton
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/totp_encoding_custom"
|
||||
android:id="@+id/totp_encoding_custom" />
|
||||
</RadioGroup>
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:minWidth="25px"
|
||||
android:minHeight="25px"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/totp_custom_settings_group">
|
||||
<Spinner
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:entries="@array/totp_algorithms"
|
||||
android:prompt="@string/algorithm"
|
||||
android:id="@+id/totp_algorithm" />
|
||||
<EditText
|
||||
android:inputType="numberDecimal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/totp_time_step"
|
||||
android:id="@+id/totp_time_step" />
|
||||
|
||||
<EditText
|
||||
android:inputType="numberDecimal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/totp_length"
|
||||
android:id="@+id/totp_length" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
</LinearLayout>
|
@@ -177,6 +177,19 @@
|
||||
android:layout_weight="1"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/add_extra_string"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/configure_totp"
|
||||
android:background="?android:selectableItemBackground"
|
||||
android:textColor="?android:attr/textColorPrimary"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:textAllCaps="true"
|
||||
android:paddingLeft="8dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:layout_height="wrap_content"
|
||||
android:drawableLeft="@drawable/baseline_schedule_white_24"
|
||||
android:text="@string/configure_totp"/>
|
||||
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
@@ -177,6 +177,12 @@
|
||||
<item>28</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="totp_algorithms">
|
||||
<item>SHA-1</item>
|
||||
<item>SHA-256</item>
|
||||
<item>SHA-512</item>
|
||||
</string-array>
|
||||
|
||||
<string name="design_default">System</string>
|
||||
<string-array name="design_values">
|
||||
<item>Light</item>
|
||||
|
@@ -346,7 +346,15 @@
|
||||
<string name="protection">Protected field</string>
|
||||
<string name="add_binary">Add file attachment</string>
|
||||
<string name="add_extra_string">Add additional string</string>
|
||||
<string name="delete_extra_string">Delete additional string</string>
|
||||
<string name="configure_totp">Configure TOTP</string>
|
||||
<string name="totp_secret_key">Secret key</string>
|
||||
<string name="totp_encoding_rfc6238">Default RFC6238 token settings</string>
|
||||
<string name="totp_encoding_steam">Steam token settings</string>
|
||||
<string name="totp_encoding_custom">Custom token settings</string>
|
||||
<string name="totp_time_step">Time step</string>
|
||||
<string name="totp_length">Code length</string>
|
||||
|
||||
<string name="delete_extra_string">Delete additional string</string>
|
||||
<string name="database_loaded_quickunlock_enabled">%1$s: Locked. QuickUnlock enabled.</string>
|
||||
<string name="database_loaded_unlocked">%1$s: Unlocked.</string>
|
||||
<string name="credentials_dialog_title">Enter server credentials</string>
|
||||
|
@@ -33,9 +33,9 @@ namespace keepass2android
|
||||
res.Encoder = parsedQuery.Get("encoder");
|
||||
string algo = parsedQuery.Get("algorithm");
|
||||
if (algo == "SHA512")
|
||||
res.HashAlgorithm = "HMAC-SHA-512";
|
||||
res.HashAlgorithm = TotpData.HashSha512;
|
||||
if (algo == "SHA256")
|
||||
res.HashAlgorithm = "HMAC-SHA-256";
|
||||
res.HashAlgorithm = TotpData.HashSha256;
|
||||
|
||||
|
||||
//set defaults according to https://github.com/google/google-authenticator/wiki/Key-Uri-Format
|
||||
|
@@ -42,7 +42,7 @@ namespace PluginTOTP
|
||||
string strAlg;
|
||||
entryFields.TryGetValue("TimeOtp-Algorithm", out strAlg);
|
||||
|
||||
|
||||
res.HashAlgorithm = strAlg;
|
||||
res.TotpSecret = pbSecret;
|
||||
res.Length = uLength.ToString();
|
||||
res.Duration = uPeriod.ToString();
|
||||
|
@@ -19,6 +19,23 @@ namespace keepass2android
|
||||
new Keepass2TotpPluginAdapter(),
|
||||
};
|
||||
|
||||
|
||||
public TotpData TryGetTotpData(PwEntryOutput entry)
|
||||
{
|
||||
if (entry == null)
|
||||
return null;
|
||||
foreach (ITotpPluginAdapter adapter in _pluginAdapters)
|
||||
{
|
||||
TotpData totpData = adapter.GetTotpData(entry.OutputStrings.ToDictionary(pair => StrUtil.SafeXmlString(pair.Key), pair => pair.Value.ReadString()), Application.Context, false);
|
||||
if (totpData.IsTotpEntry)
|
||||
{
|
||||
return totpData;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public ITotpPluginAdapter TryGetAdapter(PwEntryOutput entry)
|
||||
{
|
||||
if (entry == null)
|
||||
|
@@ -127,7 +127,7 @@ namespace KeeTrayTOTP.Libraries
|
||||
this.encoder = TOTPEncoder.rfc6238;
|
||||
}
|
||||
|
||||
if(data.Url != null)
|
||||
if(data.TimeCorrectionUrl != null)
|
||||
{
|
||||
{
|
||||
this.TimeCorrection = TimeSpan.Zero;
|
||||
|
@@ -7,11 +7,14 @@ namespace PluginTOTP
|
||||
{
|
||||
public TotpData()
|
||||
{
|
||||
HashAlgorithm = "HMAC-SHA-1";
|
||||
HashAlgorithm = HashSha1;
|
||||
}
|
||||
|
||||
public const string EncoderSteam = "steam";
|
||||
public const string EncoderRfc6238 = "rfc6238";
|
||||
public const string HashSha1 = "HMAC-SHA-1";
|
||||
public const string HashSha256 = "HMAC-SHA-256";
|
||||
public const string HashSha512 = "HMAC-SHA-512";
|
||||
|
||||
|
||||
public bool IsTotpEntry { get; set; }
|
||||
@@ -20,12 +23,28 @@ namespace PluginTOTP
|
||||
public string TotpSeed
|
||||
{
|
||||
set { TotpSecret = Base32.Decode(value.Trim()); }
|
||||
get { return Base32.Encode(TotpSecret); }
|
||||
}
|
||||
public string Duration { get; set; }
|
||||
public string Encoder { get; set; }
|
||||
public string Length { get; set; }
|
||||
public string Url { get; set; }
|
||||
public string TimeCorrectionUrl { get; set; }
|
||||
|
||||
public string HashAlgorithm { get; set; }
|
||||
|
||||
public bool IsDefaultRfc6238
|
||||
{
|
||||
get { return Length == "6" && Duration == "30" && (HashAlgorithm == null || HashAlgorithm == HashSha1); }
|
||||
}
|
||||
|
||||
public static TotpData MakeDefaultRfc6238()
|
||||
{
|
||||
return new TotpData()
|
||||
{
|
||||
Duration = "30",
|
||||
Length = "6",
|
||||
HashAlgorithm = HashSha1
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
@@ -122,7 +122,7 @@ namespace PluginTOTP
|
||||
if (ValidUrl)
|
||||
{
|
||||
NoTimeCorrection = true;
|
||||
res.Url = Settings[2];
|
||||
res.TimeCorrectionUrl = Settings[2];
|
||||
/*var CurrentTimeCorrection = TimeCorrections[Settings[2]];
|
||||
if (CurrentTimeCorrection != null)
|
||||
{
|
||||
|
@@ -314,9 +314,6 @@
|
||||
<None Include="Resources\drawable-xhdpi\2_action_about.png">
|
||||
<Visible>False</Visible>
|
||||
</None>
|
||||
<AndroidResource Include="Resources\layout\edit_extra_string_dialog.xml">
|
||||
<SubType>AndroidResource</SubType>
|
||||
</AndroidResource>
|
||||
<AndroidResource Include="Resources\layout\file_storage_setup.xml">
|
||||
<SubType>AndroidResource</SubType>
|
||||
</AndroidResource>
|
||||
@@ -2070,6 +2067,21 @@
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\menu\menu_selectdb.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\layout\configure_totp_dialog.xml">
|
||||
<SubType>AndroidResource</SubType>
|
||||
<Generator>MSBuild:UpdateGeneratedFiles</Generator>
|
||||
</AndroidResource>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\layout\edit_extra_string_dialog.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\drawable\baseline_schedule_24.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\drawable-xhdpi\baseline_schedule_white_24.png" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" />
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
|
Reference in New Issue
Block a user