From 01feaa2ecc4e99be6b538d2b1b5fafe850afe132 Mon Sep 17 00:00:00 2001 From: Santiago Garcia Mantinan <43165226+mantinan@users.noreply.github.com> Date: Fri, 7 Aug 2020 11:53:09 +0200 Subject: [PATCH] Second attempt to port our MOTP implementation to an up to date andOTP. This has been lagging around due to several things. I have also struggled to get my branch in sync with master, so I have built patches and applied them directly to mater here. Let's see if this time I can finally get this pushed and submit a pull request. --- .../Activities/IntroScreenActivity.java | 4 +- .../flocke/andotp/Database/Entry.java | 53 +- .../andotp/Dialogs/ManualEntryDialog.java | 13 +- .../flocke/andotp/Utilities/MD5.java | 515 ++++++++++++++++++ .../andotp/Utilities/TokenCalculator.java | 12 + .../flocke/andotp/Utilities/UIHelper.java | 11 +- .../andotp/View/EntriesCardAdapter.java | 68 +++ .../flocke/andotp/View/EntryViewHolder.java | 9 + .../res/layout/component_card_compact.xml | 2 + .../res/layout/component_card_default.xml | 2 + .../main/res/layout/component_card_full.xml | 2 + app/src/main/res/menu/menu_popup.xml | 5 + .../main/res/values-ar-rSA/strings_main.xml | 3 + .../main/res/values-bg-rBG/strings_main.xml | 3 + .../main/res/values-ca-rES/strings_main.xml | 3 + .../main/res/values-cs-rCZ/strings_main.xml | 3 + .../main/res/values-de-rDE/strings_main.xml | 3 + .../main/res/values-el-rGR/strings_main.xml | 3 + .../main/res/values-es-rES/strings_main.xml | 3 + .../main/res/values-fa-rIR/strings_main.xml | 3 + .../main/res/values-fr-rFR/strings_main.xml | 3 + .../main/res/values-gl-rES/strings_main.xml | 3 + .../main/res/values-hi-rIN/strings_main.xml | 3 + .../main/res/values-hu-rHU/strings_main.xml | 3 + .../main/res/values-it-rIT/strings_main.xml | 3 + .../main/res/values-ja-rJP/strings_main.xml | 3 + .../main/res/values-nl-rNL/strings_main.xml | 3 + .../main/res/values-pl-rPL/strings_main.xml | 3 + .../main/res/values-pt-rBR/strings_main.xml | 3 + .../main/res/values-ru-rRU/strings_main.xml | 3 + .../main/res/values-sl-rSI/strings_main.xml | 3 + .../main/res/values-sv-rSE/strings_main.xml | 3 + .../main/res/values-tr-rTR/strings_main.xml | 3 + .../main/res/values-uk-rUA/strings_main.xml | 3 + .../main/res/values-zh-rCN/strings_main.xml | 3 + .../main/res/values-zh-rTW/strings_main.xml | 3 + app/src/main/res/values/strings_main.xml | 3 + 37 files changed, 762 insertions(+), 9 deletions(-) create mode 100644 app/src/main/java/org/shadowice/flocke/andotp/Utilities/MD5.java diff --git a/app/src/main/java/org/shadowice/flocke/andotp/Activities/IntroScreenActivity.java b/app/src/main/java/org/shadowice/flocke/andotp/Activities/IntroScreenActivity.java index 31724ea9..9d6c3973 100644 --- a/app/src/main/java/org/shadowice/flocke/andotp/Activities/IntroScreenActivity.java +++ b/app/src/main/java/org/shadowice/flocke/andotp/Activities/IntroScreenActivity.java @@ -210,7 +210,7 @@ public class IntroScreenActivity extends IntroActivity { } }); - selection.setSelection(selectionMapping.indexOfValue(Constants.EncryptionType.PASSWORD)); + selection.setSelection(selectionMapping.indexOfValue(Constants.EncryptionType.KEYSTORE)); return root; } @@ -466,7 +466,7 @@ public class IntroScreenActivity extends IntroActivity { passwordInput.addTextChangedListener(textWatcher); passwordConfirm.addTextChangedListener(textWatcher); - selection.setSelection(selectionMapping.indexOfValue(Constants.AuthMethod.PASSWORD)); + selection.setSelection(selectionMapping.indexOfValue(Constants.AuthMethod.NONE)); return root; } diff --git a/app/src/main/java/org/shadowice/flocke/andotp/Database/Entry.java b/app/src/main/java/org/shadowice/flocke/andotp/Database/Entry.java index 32acedb3..84b387ed 100644 --- a/app/src/main/java/org/shadowice/flocke/andotp/Database/Entry.java +++ b/app/src/main/java/org/shadowice/flocke/andotp/Database/Entry.java @@ -40,11 +40,12 @@ import java.util.Objects; public class Entry { public enum OTPType { - TOTP, HOTP, STEAM + TOTP, HOTP, MOTP, STEAM } private static final OTPType DEFAULT_TYPE = OTPType.TOTP; private static final int DEFAULT_PERIOD = 30; + private static final String MOTP_NO_PIN_CODE = "PINREQ"; private static final String JSON_SECRET = "secret"; private static final String JSON_ISSUER = "issuer"; @@ -79,6 +80,7 @@ public class Entry { public static final int COLOR_RED = 1; private static final int EXPIRY_TIME = 8; private int color = COLOR_DEFAULT; + private String pin = ""; public Entry(){} @@ -106,6 +108,16 @@ public class Entry { setThumbnailFromIssuer(issuer); } + public Entry(OTPType type, String secret, String issuer, String label, List tags) { + this.type = type; + this.secret = secret.getBytes(); + this.issuer = issuer; + this.label = label; + this.tags = tags; + this.period = TokenCalculator.TOTP_DEFAULT_PERIOD; + setThumbnailFromIssuer(issuer); + } + public Entry(String contents) throws Exception { contents = contents.replaceFirst("otpauth", "http"); Uri uri = Uri.parse(contents); @@ -122,6 +134,9 @@ public class Entry { case "hotp": type = OTPType.HOTP; break; + case "motp": + type = OTPType.MOTP; + break; case "steam": type = OTPType.STEAM; break; @@ -155,7 +170,11 @@ public class Entry { this.issuer = issuer; this.label = label; - this.secret = new Base32().decode(secret.toUpperCase()); + if(type == OTPType.MOTP) { + this.secret = secret.getBytes(); + }else{ + this.secret = new Base32().decode(secret.toUpperCase()); + } if (digits != null) { this.digits = Integer.parseInt(digits); @@ -323,7 +342,7 @@ public class Entry { } public boolean isTimeBased() { - return type == OTPType.TOTP || type == OTPType.STEAM; + return type == OTPType.TOTP || type == OTPType.STEAM || type == OTPType.MOTP; } public boolean isCounterBased() { return type == OTPType.HOTP; } @@ -444,7 +463,19 @@ public class Entry { return currentOTP; } + public String getPin() { + return pin; + } + + public void setPin(String pin) { + this.pin = pin; + } + public boolean updateOTP() { + return updateOTP(false); + } + + public boolean updateOTP(boolean updateNow) { if (type == OTPType.TOTP || type == OTPType.STEAM) { long time = System.currentTimeMillis() / 1000; long counter = time / this.getPeriod(); @@ -465,6 +496,22 @@ public class Entry { } else if (type == OTPType.HOTP) { currentOTP = TokenCalculator.HOTP(secret, counter, digits, algorithm); return true; + } else if (type == OTPType.MOTP) { + long time = System.currentTimeMillis() / 1000; + long counter = time / this.getPeriod(); + if (counter > last_update || updateNow) { + String currentPin = this.getPin(); + if (currentPin.isEmpty()) { + currentOTP = MOTP_NO_PIN_CODE; + } else { + currentOTP = TokenCalculator.MOTP(currentPin, new String(this.secret)); + } + last_update = counter; + setColor(COLOR_DEFAULT); + return true; + } else { + return false; + } } else { return false; } diff --git a/app/src/main/java/org/shadowice/flocke/andotp/Dialogs/ManualEntryDialog.java b/app/src/main/java/org/shadowice/flocke/andotp/Dialogs/ManualEntryDialog.java index 0b0ff63b..7ef6d77a 100644 --- a/app/src/main/java/org/shadowice/flocke/andotp/Dialogs/ManualEntryDialog.java +++ b/app/src/main/java/org/shadowice/flocke/andotp/Dialogs/ManualEntryDialog.java @@ -81,6 +81,7 @@ public class ManualEntryDialog { final LinearLayout periodLayout = inputView.findViewById(R.id.manual_layout_period); final Spinner algorithmInput = inputView.findViewById(R.id.manual_algorithm); final Button tagsInput = inputView.findViewById(R.id.manual_tags); + final Button expandButton = inputView.findViewById(R.id.dialog_expand_button); final ArrayAdapter algorithmAdapter = new ArrayAdapter<>(callingActivity, android.R.layout.simple_expandable_list_item_1, TokenCalculator.HashAlgorithm.values()); final ArrayAdapter typeAdapter = new ArrayAdapter<>(callingActivity, android.R.layout.simple_expandable_list_item_1, Entry.OTPType.values()); @@ -100,6 +101,7 @@ public class ManualEntryDialog { if (type == Entry.OTPType.STEAM) { counterLayout.setVisibility(View.GONE); periodLayout.setVisibility(View.VISIBLE); + expandButton.setVisibility(View.VISIBLE); digitsInput.setText(String.format(Locale.US, "%d", TokenCalculator.STEAM_DEFAULT_DIGITS)); periodInput.setText(String.format(Locale.US, "%d", TokenCalculator.TOTP_DEFAULT_PERIOD)); @@ -111,6 +113,7 @@ public class ManualEntryDialog { } else if (type == Entry.OTPType.TOTP) { counterLayout.setVisibility(View.GONE); periodLayout.setVisibility(View.VISIBLE); + expandButton.setVisibility(View.VISIBLE); digitsInput.setText(String.format(Locale.US, "%d", TokenCalculator.TOTP_DEFAULT_DIGITS)); digitsInput.setEnabled(isNewEntry); @@ -119,11 +122,19 @@ public class ManualEntryDialog { } else if (type == Entry.OTPType.HOTP) { counterLayout.setVisibility(View.VISIBLE); periodLayout.setVisibility(View.GONE); + expandButton.setVisibility(View.VISIBLE); digitsInput.setText(String.format(Locale.US, "%d", TokenCalculator.TOTP_DEFAULT_DIGITS)); digitsInput.setEnabled(isNewEntry); periodInput.setEnabled(isNewEntry); algorithmInput.setEnabled(isNewEntry); + }else if (type == Entry.OTPType.MOTP) { + counterLayout.setVisibility(View.GONE); + periodLayout.setVisibility(View.VISIBLE); + + digitsInput.setText(String.format(Locale.US, "%d", TokenCalculator.TOTP_DEFAULT_DIGITS)); + expandButton.setVisibility(View.GONE); + algorithmInput.setEnabled(isNewEntry); } } @@ -163,8 +174,6 @@ public class ManualEntryDialog { } }); - final Button expandButton = inputView.findViewById(R.id.dialog_expand_button); - // Dirty fix for the compound drawable to avoid crashes on KitKat expandButton.setCompoundDrawablesWithIntrinsicBounds(null, null, callingActivity.getResources().getDrawable(R.drawable.ic_arrow_down_accent), null); diff --git a/app/src/main/java/org/shadowice/flocke/andotp/Utilities/MD5.java b/app/src/main/java/org/shadowice/flocke/andotp/Utilities/MD5.java new file mode 100644 index 00000000..b1985f57 --- /dev/null +++ b/app/src/main/java/org/shadowice/flocke/andotp/Utilities/MD5.java @@ -0,0 +1,515 @@ +/* + * $Header: /u/users20/santtu/src/java/MD5/RCS/MD5.java,v 1.5 1996/12/12 10:47:02 santtu Exp $ + * + * MD5 in Java JDK Beta-2 + * written Santeri Paavolainen, Helsinki Finland 1996 + * (c) Santeri Paavolainen, Helsinki Finland 1996 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * See http://www.cs.hut.fi/~santtu/java/ for more information on this + * class. + * + * This is rather straight re-implementation of the reference implementation + * given in RFC1321 by RSA. + * + * Passes MD5 test suite as defined in RFC1321. + * + * + * This Java class has been derived from the RSA Data Security, Inc. MD5 + * Message-Digest Algorithm and its reference implementation. + * + * + * Revision 1.6 minor changes for j2me, 2003, Matthias Straub + * + * $Log: MD5.java,v $ + * Revision 1.5 1996/12/12 10:47:02 santtu + * Changed GPL to LGPL + * + * Revision 1.4 1996/12/12 10:30:02 santtu + * Some typos, State -> MD5State etc. + * + * Revision 1.3 1996/04/15 07:28:09 santtu + * Added GPL statemets, and RSA derivate stametemetsnnts. + * + * Revision 1.2 1996/03/04 08:05:48 santtu + * Added offsets to Update method + * + * Revision 1.1 1996/01/07 20:51:59 santtu + * Initial revision + * + */ + +/** + * Contains internal state of the MD5 class + */ +package org.shadowice.flocke.andotp.Utilities; + +class MD5State { + /** + * 128-byte state + */ + int state[]; + + /** + * 64-bit character count (could be true Java long?) + */ + int count[]; + + /** + * 64-byte buffer (512 bits) for storing to-be-hashed characters + */ + byte buffer[]; + + public MD5State() { + buffer = new byte[64]; + count = new int[2]; + state = new int[4]; + + state[0] = 0x67452301; + state[1] = 0xefcdab89; + state[2] = 0x98badcfe; + state[3] = 0x10325476; + + count[0] = count[1] = 0; + } + + /** Create this State as a copy of another state */ + public MD5State (MD5State from) { + this(); + + int i; + + for (i = 0; i < buffer.length; i++) + this.buffer[i] = from.buffer[i]; + + for (i = 0; i < state.length; i++) + this.state[i] = from.state[i]; + + for (i = 0; i < count.length; i++) + this.count[i] = from.count[i]; + } +}; + +/** + * Implementation of RSA's MD5 hash generator + * + * @version $Revision: 1.5 $ + * @author Santeri Paavolainen + */ + +public class MD5 { + /** + * MD5 state + */ + MD5State state; + + /** + * If Final() has been called, finals is set to the current finals + * state. Any Update() causes this to be set to null. + */ + MD5State finals; + + /** + * Padding for Final() + */ + static byte padding[] = { + (byte) 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + + /** + * Initialize MD5 internal state (object can be reused just by + * calling Init() after every Final() + */ + public synchronized void Init () { + state = new MD5State(); + finals = null; + } + + /** + * Class constructor + */ + public MD5 () { + this.Init(); + } + + /** + * Initialize class, and update hash with ob.toString() + * + * @param ob Object, ob.toString() is used to update hash + * after initialization + */ + public MD5 (Object ob) { + this(); + Update(ob.toString()); + } + + private int rotate_left (int x, int n) { + return (x << n) | (x >>> (32 - n)); + } + + /* I wonder how many loops and hoops you'll have to go through to + get unsigned add for longs in java */ + + private int uadd (int a, int b) { + long aa, bb; + aa = ((long) a) & 0xffffffffL; + bb = ((long) b) & 0xffffffffL; + + aa += bb; + + return (int) (aa & 0xffffffffL); + } + + private int uadd (int a, int b, int c) { + return uadd(uadd(a, b), c); + } + + private int uadd (int a, int b, int c, int d) { + return uadd(uadd(a, b, c), d); + } + + private int FF (int a, int b, int c, int d, int x, int s, int ac) { + a = uadd(a, ((b & c) | (~b & d)), x, ac); + return uadd(rotate_left(a, s), b); + } + + private int GG (int a, int b, int c, int d, int x, int s, int ac) { + a = uadd(a, ((b & d) | (c & ~d)), x, ac); + return uadd(rotate_left(a, s), b); + } + + private int HH (int a, int b, int c, int d, int x, int s, int ac) { + a = uadd(a, (b ^ c ^ d), x, ac); + return uadd(rotate_left(a, s) , b); + } + + private int II (int a, int b, int c, int d, int x, int s, int ac) { + a = uadd(a, (c ^ (b | ~d)), x, ac); + return uadd(rotate_left(a, s), b); + } + + private int[] Decode (byte buffer[], int len, int shift) { + int out[]; + int i, j; + + out = new int[16]; + + for (i = j = 0; j < len; i++, j += 4) { + out[i] = ((int) (buffer[j + shift] & 0xff)) | + (((int) (buffer[j + 1 + shift] & 0xff)) << 8) | + (((int) (buffer[j + 2 + shift] & 0xff)) << 16) | + (((int) (buffer[j + 3 + shift] & 0xff)) << 24); + +/* System.out.println("out[" + i + "] = \t" + + ((int) buffer[j + 0 + shift] & 0xff) + "\t|\t" + + ((int) buffer[j + 1 + shift] & 0xff) + "\t|\t" + + ((int) buffer[j + 2 + shift] & 0xff) + "\t|\t" + + ((int) buffer[j + 3 + shift] & 0xff));*/ + } + + return out; + } + + private void Transform (MD5State state, byte buffer[], int shift) { + int + a = state.state[0], + b = state.state[1], + c = state.state[2], + d = state.state[3], + x[]; + + x = Decode(buffer, 64, shift); + + /* Round 1 */ + a = FF (a, b, c, d, x[ 0], 7, 0xd76aa478); /* 1 */ + d = FF (d, a, b, c, x[ 1], 12, 0xe8c7b756); /* 2 */ + c = FF (c, d, a, b, x[ 2], 17, 0x242070db); /* 3 */ + b = FF (b, c, d, a, x[ 3], 22, 0xc1bdceee); /* 4 */ + a = FF (a, b, c, d, x[ 4], 7, 0xf57c0faf); /* 5 */ + d = FF (d, a, b, c, x[ 5], 12, 0x4787c62a); /* 6 */ + c = FF (c, d, a, b, x[ 6], 17, 0xa8304613); /* 7 */ + b = FF (b, c, d, a, x[ 7], 22, 0xfd469501); /* 8 */ + a = FF (a, b, c, d, x[ 8], 7, 0x698098d8); /* 9 */ + d = FF (d, a, b, c, x[ 9], 12, 0x8b44f7af); /* 10 */ + c = FF (c, d, a, b, x[10], 17, 0xffff5bb1); /* 11 */ + b = FF (b, c, d, a, x[11], 22, 0x895cd7be); /* 12 */ + a = FF (a, b, c, d, x[12], 7, 0x6b901122); /* 13 */ + d = FF (d, a, b, c, x[13], 12, 0xfd987193); /* 14 */ + c = FF (c, d, a, b, x[14], 17, 0xa679438e); /* 15 */ + b = FF (b, c, d, a, x[15], 22, 0x49b40821); /* 16 */ + + /* Round 2 */ + a = GG (a, b, c, d, x[ 1], 5, 0xf61e2562); /* 17 */ + d = GG (d, a, b, c, x[ 6], 9, 0xc040b340); /* 18 */ + c = GG (c, d, a, b, x[11], 14, 0x265e5a51); /* 19 */ + b = GG (b, c, d, a, x[ 0], 20, 0xe9b6c7aa); /* 20 */ + a = GG (a, b, c, d, x[ 5], 5, 0xd62f105d); /* 21 */ + d = GG (d, a, b, c, x[10], 9, 0x2441453); /* 22 */ + c = GG (c, d, a, b, x[15], 14, 0xd8a1e681); /* 23 */ + b = GG (b, c, d, a, x[ 4], 20, 0xe7d3fbc8); /* 24 */ + a = GG (a, b, c, d, x[ 9], 5, 0x21e1cde6); /* 25 */ + d = GG (d, a, b, c, x[14], 9, 0xc33707d6); /* 26 */ + c = GG (c, d, a, b, x[ 3], 14, 0xf4d50d87); /* 27 */ + b = GG (b, c, d, a, x[ 8], 20, 0x455a14ed); /* 28 */ + a = GG (a, b, c, d, x[13], 5, 0xa9e3e905); /* 29 */ + d = GG (d, a, b, c, x[ 2], 9, 0xfcefa3f8); /* 30 */ + c = GG (c, d, a, b, x[ 7], 14, 0x676f02d9); /* 31 */ + b = GG (b, c, d, a, x[12], 20, 0x8d2a4c8a); /* 32 */ + + /* Round 3 */ + a = HH (a, b, c, d, x[ 5], 4, 0xfffa3942); /* 33 */ + d = HH (d, a, b, c, x[ 8], 11, 0x8771f681); /* 34 */ + c = HH (c, d, a, b, x[11], 16, 0x6d9d6122); /* 35 */ + b = HH (b, c, d, a, x[14], 23, 0xfde5380c); /* 36 */ + a = HH (a, b, c, d, x[ 1], 4, 0xa4beea44); /* 37 */ + d = HH (d, a, b, c, x[ 4], 11, 0x4bdecfa9); /* 38 */ + c = HH (c, d, a, b, x[ 7], 16, 0xf6bb4b60); /* 39 */ + b = HH (b, c, d, a, x[10], 23, 0xbebfbc70); /* 40 */ + a = HH (a, b, c, d, x[13], 4, 0x289b7ec6); /* 41 */ + d = HH (d, a, b, c, x[ 0], 11, 0xeaa127fa); /* 42 */ + c = HH (c, d, a, b, x[ 3], 16, 0xd4ef3085); /* 43 */ + b = HH (b, c, d, a, x[ 6], 23, 0x4881d05); /* 44 */ + a = HH (a, b, c, d, x[ 9], 4, 0xd9d4d039); /* 45 */ + d = HH (d, a, b, c, x[12], 11, 0xe6db99e5); /* 46 */ + c = HH (c, d, a, b, x[15], 16, 0x1fa27cf8); /* 47 */ + b = HH (b, c, d, a, x[ 2], 23, 0xc4ac5665); /* 48 */ + + /* Round 4 */ + a = II (a, b, c, d, x[ 0], 6, 0xf4292244); /* 49 */ + d = II (d, a, b, c, x[ 7], 10, 0x432aff97); /* 50 */ + c = II (c, d, a, b, x[14], 15, 0xab9423a7); /* 51 */ + b = II (b, c, d, a, x[ 5], 21, 0xfc93a039); /* 52 */ + a = II (a, b, c, d, x[12], 6, 0x655b59c3); /* 53 */ + d = II (d, a, b, c, x[ 3], 10, 0x8f0ccc92); /* 54 */ + c = II (c, d, a, b, x[10], 15, 0xffeff47d); /* 55 */ + b = II (b, c, d, a, x[ 1], 21, 0x85845dd1); /* 56 */ + a = II (a, b, c, d, x[ 8], 6, 0x6fa87e4f); /* 57 */ + d = II (d, a, b, c, x[15], 10, 0xfe2ce6e0); /* 58 */ + c = II (c, d, a, b, x[ 6], 15, 0xa3014314); /* 59 */ + b = II (b, c, d, a, x[13], 21, 0x4e0811a1); /* 60 */ + a = II (a, b, c, d, x[ 4], 6, 0xf7537e82); /* 61 */ + d = II (d, a, b, c, x[11], 10, 0xbd3af235); /* 62 */ + c = II (c, d, a, b, x[ 2], 15, 0x2ad7d2bb); /* 63 */ + b = II (b, c, d, a, x[ 9], 21, 0xeb86d391); /* 64 */ + + state.state[0] += a; + state.state[1] += b; + state.state[2] += c; + state.state[3] += d; + } + + /** + * Updates hash with the bytebuffer given (using at maximum length bytes from + * that buffer) + * + * @param stat Which state is updated + * @param buffer Array of bytes to be hashed + * @param offset Offset to buffer array + * @param length Use at maximum `length' bytes (absolute + * maximum is buffer.length) + */ + public void Update (MD5State stat, byte buffer[], int offset, int length) { + int index, partlen, i, start; + +/* System.out.print("Offset = " + offset + "\tLength = " + length + "\t"); + System.out.print("Buffer = "); + for (i = 0; i < buffer.length; i++) + System.out.print((int) (buffer[i] & 0xff) + " "); + System.out.print("\n");*/ + + finals = null; + + /* Length can be told to be shorter, but not inter */ + if ((length - offset)> buffer.length) + length = buffer.length - offset; + + /* compute number of bytes mod 64 */ + index = (int) (stat.count[0] >>> 3) & 0x3f; + + if ((stat.count[0] += (length << 3)) < + (length << 3)) + stat.count[1]++; + + stat.count[1] += length >>> 29; + + partlen = 64 - index; + + if (length >= partlen) { + for (i = 0; i < partlen; i++) + stat.buffer[i + index] = buffer[i + offset]; + + Transform(stat, stat.buffer, 0); + + for (i = partlen; (i + 63) < length; i+= 64) + Transform(stat, buffer, i); + + index = 0; + } else + i = 0; + + /* buffer remaining input */ + if (i < length) { + start = i; + for (; i < length; i++) + stat.buffer[index + i - start] = buffer[i + offset]; + } + } + + /* + * Update()s for other datatypes than byte[] also. Update(byte[], int) + * is only the main driver. + */ + + /** + * Plain update, updates this object + */ + + public void Update (byte buffer[], int offset, int length) { + Update(this.state, buffer, offset, length); + } + + public void Update (byte buffer[], int length) { + Update(this.state, buffer, 0, length); + } + + /** + * Updates hash with given array of bytes + * + * @param buffer Array of bytes to use for updating the hash + */ + public void Update (byte buffer[]) { + Update(buffer, 0, buffer.length); + } + + /** + * Updates hash with a single byte + * + * @param b Single byte to update the hash + */ + public void Update (byte b) { + byte buffer[] = new byte[1]; + buffer[0] = b; + + Update(buffer, 1); + } + + /** + * Update buffer with given string. + * + * @param s String to be update to hash (is used as + * s.getBytes()) + */ + public void Update (String s) { + byte chars[]; + + chars = new byte[s.length()]; + chars=s.getBytes(); + + Update(chars, chars.length); + } + + /** + * Update buffer with a single integer (only & 0xff part is used, + * as a byte) + * + * @param i Integer value, which is then converted to + * byte as i & 0xff + */ + + public void Update (int i) { + Update((byte) (i & 0xff)); + } + + private byte[] Encode (int input[], int len) { + int i, j; + byte out[]; + + out = new byte[len]; + + for (i = j = 0; j < len; i++, j += 4) { + out[j] = (byte) (input[i] & 0xff); + out[j + 1] = (byte) ((input[i] >>> 8) & 0xff); + out[j + 2] = (byte) ((input[i] >>> 16) & 0xff); + out[j + 3] = (byte) ((input[i] >>> 24) & 0xff); + } + + return out; + } + + /** + * Returns array of bytes (16 bytes) representing hash as of the + * current state of this object. Note: getting a hash does not + * invalidate the hash object, it only creates a copy of the real + * state which is finalized. + * + * @return Array of 16 bytes, the hash of all updated bytes + */ + public synchronized byte[] Final () { + byte bits[]; + int index, padlen; + MD5State fin; + + if (finals == null) { + fin = new MD5State(state); + + bits = Encode(fin.count, 8); + + index = (int) ((fin.count[0] >>> 3) & 0x3f); + padlen = (index < 56) ? (56 - index) : (120 - index); + + Update(fin, padding, 0, padlen); + /**/ + Update(fin, bits, 0, 8); + + /* Update() sets finalds to null */ + finals = fin; + } + + return Encode(finals.state, 16); + } + + /** + * Turns array of bytes into string representing each byte as + * unsigned hex number. + * + * @param hash Array of bytes to convert to hex-string + * @return Generated hex string + */ + public static String asHex (byte hash[]) { + StringBuffer buf = new StringBuffer(hash.length * 2); + int i; + + for (i = 0; i < hash.length; i++) { + if (((int) hash[i] & 0xff) < 0x10) + buf.append("0"); + + buf.append(Long.toString((int) hash[i] & 0xff, 16)); + } + + return buf.toString(); + } + + /** + * Returns 32-character hex representation of this objects hash + * + * @return String of this object's hash + */ + public String asHex () { + return asHex(this.Final()); + } +} diff --git a/app/src/main/java/org/shadowice/flocke/andotp/Utilities/TokenCalculator.java b/app/src/main/java/org/shadowice/flocke/andotp/Utilities/TokenCalculator.java index 11a5b7ea..023194ce 100644 --- a/app/src/main/java/org/shadowice/flocke/andotp/Utilities/TokenCalculator.java +++ b/app/src/main/java/org/shadowice/flocke/andotp/Utilities/TokenCalculator.java @@ -26,6 +26,7 @@ package org.shadowice.flocke.andotp.Utilities; import java.nio.ByteBuffer; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; +import java.util.Date; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; @@ -115,4 +116,15 @@ public class TokenCalculator { return r; } + + public static String MOTP(String PIN, String secret) + { + Date dateNow = new Date(); + String epoch = "" + (dateNow.getTime()); + epoch = epoch.substring(0, epoch.length() - 4); + String otp = epoch + secret + PIN; + MD5 hash = new MD5(otp); + otp = hash.asHex().substring(0, 6); + return otp; + } } diff --git a/app/src/main/java/org/shadowice/flocke/andotp/Utilities/UIHelper.java b/app/src/main/java/org/shadowice/flocke/andotp/Utilities/UIHelper.java index 6650ad15..8cd2e638 100644 --- a/app/src/main/java/org/shadowice/flocke/andotp/Utilities/UIHelper.java +++ b/app/src/main/java/org/shadowice/flocke/andotp/Utilities/UIHelper.java @@ -48,10 +48,17 @@ public class UIHelper { .show(); } - public static void showKeyboard(Context context, View view) { + public static void showKeyboard(Context context, View view){ + showKeyboard(context,view,false); + } + + public static void showKeyboard(Context context, View view, Boolean showForced) { if (view != null) { InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE); - imm.showSoftInput(view, 0); + if(showForced) + imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, InputMethodManager.HIDE_IMPLICIT_ONLY); + else + imm.showSoftInput(view, 0); } } diff --git a/app/src/main/java/org/shadowice/flocke/andotp/View/EntriesCardAdapter.java b/app/src/main/java/org/shadowice/flocke/andotp/View/EntriesCardAdapter.java index 83906356..14b4f8c3 100644 --- a/app/src/main/java/org/shadowice/flocke/andotp/View/EntriesCardAdapter.java +++ b/app/src/main/java/org/shadowice/flocke/andotp/View/EntriesCardAdapter.java @@ -36,11 +36,14 @@ import android.text.Editable; import android.text.InputType; import android.text.TextUtils; import android.text.TextWatcher; +import android.text.method.PasswordTransformationMethod; import android.view.LayoutInflater; +import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; +import android.view.inputmethod.InputMethodManager; import android.widget.AdapterView; import android.widget.Button; import android.widget.EditText; @@ -66,6 +69,7 @@ import org.shadowice.flocke.andotp.Utilities.EncryptionHelper; import org.shadowice.flocke.andotp.Utilities.EntryThumbnail; import org.shadowice.flocke.andotp.Utilities.Settings; import org.shadowice.flocke.andotp.Utilities.Tools; +import org.shadowice.flocke.andotp.Utilities.UIHelper; import org.shadowice.flocke.andotp.View.ItemTouchHelper.ItemTouchHelperAdapter; import java.text.Collator; @@ -97,6 +101,8 @@ public class EntriesCardAdapter extends RecyclerView.Adapter private TagsAdapter tagsFilterAdapter; private Settings settings; + private static final int ESTABLISH_PIN_MENU_INDEX = 4; + public EntriesCardAdapter(Context context, TagsAdapter tagsFilterAdapter) { this.context = context; this.tagsFilterAdapter = tagsFilterAdapter; @@ -355,6 +361,14 @@ public class EntriesCardAdapter extends RecyclerView.Adapter public void onCounterLongPressed(int position) { setCounter(position); } + + @Override + public void onItemClickListener(int position) { + final Entry entry = displayedEntries.get(position); + if(entry.getType() == Entry.OTPType.MOTP && entry.getPin().isEmpty()){ + establishPIN(position); + } + } }); return viewHolder; @@ -620,6 +634,52 @@ public class EntriesCardAdapter extends RecyclerView.Adapter alert.show(); } + public void establishPIN(final int pos) { + AlertDialog.Builder builder = new AlertDialog.Builder(context); + + int marginSmall = context.getResources().getDimensionPixelSize(R.dimen.activity_margin_small); + int marginMedium = context.getResources().getDimensionPixelSize(R.dimen.activity_margin_medium); + + final EditText input = new EditText(context); + input.setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); + input.setRawInputType(InputType.TYPE_NUMBER_VARIATION_PASSWORD | InputType.TYPE_CLASS_NUMBER); + input.setText(displayedEntries.get(pos).getPin()); + input.setSingleLine(); + input.requestFocus(); + input.setTransformationMethod(new PasswordTransformationMethod()); + UIHelper.showKeyboard(context,input,true); + + FrameLayout container = new FrameLayout(context); + container.setPaddingRelative(marginMedium, marginSmall, marginMedium, 0); + container.addView(input); + + builder.setTitle(R.string.dialog_title_pin) + .setCancelable(false) + .setView(container) + .setPositiveButton(R.string.button_accept, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialogInterface, int i) { + int realIndex = getRealIndex(pos); + String newPin = input.getEditableText().toString(); + + displayedEntries.get(pos).setPin(newPin); + Entry e = entries.get(realIndex); + e.setPin(newPin); + e.updateOTP(true); + notifyDataSetChanged(); + UIHelper.hideKeyboard(context,input); + } + }) + .setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialogInterface, int i) { + UIHelper.hideKeyboard(context,input); + } + }) + .create() + .show(); + } + public void removeItem(final int pos) { AlertDialog.Builder builder = new AlertDialog.Builder(context); @@ -682,6 +742,11 @@ public class EntriesCardAdapter extends RecyclerView.Adapter MenuInflater inflate = popup.getMenuInflater(); inflate.inflate(R.menu.menu_popup, popup.getMenu()); + if (displayedEntries.get(pos).getType() == Entry.OTPType.MOTP){ + MenuItem item = popup.getMenu().getItem(ESTABLISH_PIN_MENU_INDEX); + item.setVisible(true); + } + popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() { @Override public boolean onMenuItemClick(MenuItem item) { @@ -693,6 +758,9 @@ public class EntriesCardAdapter extends RecyclerView.Adapter } else if(id == R.id.menu_popup_changeImage) { changeThumbnail(pos); return true; + } else if (id == R.id.menu_popup_establishPin) { + establishPIN(pos); + return true; } else if (id == R.id.menu_popup_remove) { removeItem(pos); return true; diff --git a/app/src/main/java/org/shadowice/flocke/andotp/View/EntryViewHolder.java b/app/src/main/java/org/shadowice/flocke/andotp/View/EntryViewHolder.java index fec6d035..5ceb9b14 100644 --- a/app/src/main/java/org/shadowice/flocke/andotp/View/EntryViewHolder.java +++ b/app/src/main/java/org/shadowice/flocke/andotp/View/EntryViewHolder.java @@ -154,6 +154,14 @@ public class EntryViewHolder extends RecyclerView.ViewHolder } }); + itemView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (callback != null) + callback.onItemClickListener(getAdapterPosition()); + } + }); + setTapToReveal(tapToReveal); } @@ -328,6 +336,7 @@ public class EntryViewHolder extends RecyclerView.ViewHolder void onCounterClicked(int position); void onCounterLongPressed(int position); + void onItemClickListener(int position); } /** * Updates the color of OTP to red (if expiring) or default color (if new OTP) diff --git a/app/src/main/res/layout/component_card_compact.xml b/app/src/main/res/layout/component_card_compact.xml index 2fef3704..b3b06071 100644 --- a/app/src/main/res/layout/component_card_compact.xml +++ b/app/src/main/res/layout/component_card_compact.xml @@ -7,6 +7,8 @@ android:layout_height="wrap_content" android:layout_marginTop="@dimen/activity_margin_xxsmall" android:layout_marginBottom="@dimen/activity_margin_xxsmall" + android:clickable="true" + android:foreground="?android:attr/selectableItemBackground" app:contentPadding="0dp" style="?attr/cardStyle"> diff --git a/app/src/main/res/layout/component_card_default.xml b/app/src/main/res/layout/component_card_default.xml index 5a0556ac..f7410884 100644 --- a/app/src/main/res/layout/component_card_default.xml +++ b/app/src/main/res/layout/component_card_default.xml @@ -7,6 +7,8 @@ android:layout_height="wrap_content" android:layout_marginTop="@dimen/activity_margin_xsmall" android:layout_marginBottom="@dimen/activity_margin_xsmall" + android:clickable="true" + android:foreground="?android:attr/selectableItemBackground" app:contentPadding="0dp" style="?attr/cardStyle"> diff --git a/app/src/main/res/layout/component_card_full.xml b/app/src/main/res/layout/component_card_full.xml index dde1bae6..e1943bd3 100644 --- a/app/src/main/res/layout/component_card_full.xml +++ b/app/src/main/res/layout/component_card_full.xml @@ -8,6 +8,8 @@ android:layout_marginTop="@dimen/activity_margin_xsmall" android:layout_marginBottom="@dimen/activity_margin_xsmall" app:contentPadding="0dp" + android:clickable="true" + android:foreground="?android:attr/selectableItemBackground" style="?attr/cardStyle"> + + diff --git a/app/src/main/res/values-ar-rSA/strings_main.xml b/app/src/main/res/values-ar-rSA/strings_main.xml index afe0c71c..37e9b1b4 100644 --- a/app/src/main/res/values-ar-rSA/strings_main.xml +++ b/app/src/main/res/values-ar-rSA/strings_main.xml @@ -10,6 +10,7 @@ كافة العلامات بدون علامات رمز كيو آر مِن صورة + اقبل %d ثا @@ -45,6 +46,7 @@ تغيير الصورة حذف Show QR Code + أدخل رمز PIN أخفقت المصادقة، الرجاء إعادة المحاولة ! أخفقت عملية المصادقة، جارٍ إغلاق التطبيق! @@ -73,6 +75,7 @@ QR Code Deprecation notice أدخل كلمة المرور + دبوس أدخل كلمة المرور تأكيد كلمة المرور الرجاء إدخال بيانات الإعتماد الخاصة بجهازك لتشغيل التطبيق. diff --git a/app/src/main/res/values-bg-rBG/strings_main.xml b/app/src/main/res/values-bg-rBG/strings_main.xml index 22e9705a..2ba9f2d1 100644 --- a/app/src/main/res/values-bg-rBG/strings_main.xml +++ b/app/src/main/res/values-bg-rBG/strings_main.xml @@ -10,6 +10,7 @@ Всички маркери Няма маркери QR код от изображение + приемам %d сек @@ -45,6 +46,7 @@ Промяна на изображението Премахване Показване на QR кода + Въведете ПИН код Неуспешно удостоверяване. Моля, опитайте отново! Неуспешно удостоверяване, затваряне на andOTP! @@ -73,6 +75,7 @@ QR код Остаряло известие Въведете парола + PIN Въведете парола Потвърдете паролата Моля, въведете идентификационните данни на устройството си, за да стартирате andOTP. diff --git a/app/src/main/res/values-ca-rES/strings_main.xml b/app/src/main/res/values-ca-rES/strings_main.xml index 709d06f1..8ab95425 100644 --- a/app/src/main/res/values-ca-rES/strings_main.xml +++ b/app/src/main/res/values-ca-rES/strings_main.xml @@ -10,6 +10,7 @@ Totes les etiquetes Sense etiquetes QR code from image + Acceptar %d s @@ -45,6 +46,7 @@ Canvia la imatge Suprimeix Show QR Code + Introduir PIN Error d\'autenticació. Torneu-ho a intentar! L\'autenticació ha fallat, andOTP es tancarà! @@ -73,6 +75,7 @@ QR Code Deprecation notice Introduïu la contrasenya + PIN Introduïu la contrasenya Confirmeu la contrasenya Per favor, introduïu les vostres credencials per a iniciar andOTP. diff --git a/app/src/main/res/values-cs-rCZ/strings_main.xml b/app/src/main/res/values-cs-rCZ/strings_main.xml index 880df42c..701cafee 100644 --- a/app/src/main/res/values-cs-rCZ/strings_main.xml +++ b/app/src/main/res/values-cs-rCZ/strings_main.xml @@ -10,6 +10,7 @@ Všechny štítky Bez štítku QR kód z obrázku + Přijmout %d s @@ -45,6 +46,7 @@ Změnit obrázek Smazat Ukaž QR kód + Zadejte PIN Chyba ověření, zkuste to prosím znovu! Ověření se nezdařilo, zavírám andOTP! @@ -73,6 +75,7 @@ QR kód Oznámení o ukončení podpory Zadejte heslo + PIN Zadejte heslo Potvrďte heslo Prosím zadejte heslo zařízení ke spuštění andOTP. diff --git a/app/src/main/res/values-de-rDE/strings_main.xml b/app/src/main/res/values-de-rDE/strings_main.xml index 7097975a..1aab1a56 100644 --- a/app/src/main/res/values-de-rDE/strings_main.xml +++ b/app/src/main/res/values-de-rDE/strings_main.xml @@ -10,6 +10,7 @@ Alle Marker Keine Marker QR-Code aus Bild + Akzeptiere %d s @@ -45,6 +46,7 @@ Bild ändern Entfernen Zeige QR-Code + PIN einstellen Authentifizierung fehlgeschlagen, bitte erneut versuchen! Authentifizierung fehlgeschlagen, andOTP wird geschlossen! @@ -73,6 +75,7 @@ QR-Code Hinweis Passwort eingeben + PIN Passwort eingeben Passwort bestätigen Bitte geben Sie die Anmeldeinformationen Ihres Geräts ein, um andOTP zu starten. diff --git a/app/src/main/res/values-el-rGR/strings_main.xml b/app/src/main/res/values-el-rGR/strings_main.xml index 32af516a..8d8b1fc8 100644 --- a/app/src/main/res/values-el-rGR/strings_main.xml +++ b/app/src/main/res/values-el-rGR/strings_main.xml @@ -10,6 +10,7 @@ Όλες οι ετικέτες Χωρίς ετικέτες Κώδικας QR από εικόνα + Αποδοχή %d\" @@ -45,6 +46,7 @@ Αλλαγή εικόνας Διαγραφή Show QR Code + Ορίστε το PIN Η ταυτοποίηση απέτυχε. Παρακαλώ προσπάθησε ξανά! Απέτυχε ο έλεγχος ταυτότητας, κλείνει το andOTP! @@ -74,6 +76,7 @@ Deprecation notice Εισάγετε κωδικό Εισάγετε κωδικό + PIN Επιβεβαίωση κωδικού Παρακαλούμε εισαγάγετε τα διαπιστευτήριά σας για να ανοίξετε το andOTP. Σίγουρα θέλετε να αφαιρέσετε το λογαριασμό \"%1$s\"; diff --git a/app/src/main/res/values-es-rES/strings_main.xml b/app/src/main/res/values-es-rES/strings_main.xml index 269e7d78..205158d3 100644 --- a/app/src/main/res/values-es-rES/strings_main.xml +++ b/app/src/main/res/values-es-rES/strings_main.xml @@ -10,6 +10,7 @@ Todas las etiquetas Sin etiquetas Imagen del código QR + Aceptar %d s @@ -45,6 +46,7 @@ Cambiar imagen Eliminar Mostrar código QR + Introducir pin La autentificación ha fallado, por favor inténtelo otra vez! La autenticación ha fallado, cerrando andOTP! @@ -73,6 +75,7 @@ Código QR Aviso de deprecación Introduzca la contraseña + PIN Introduzca la contraseña Confirmar contraseña Por favor, introduce los credenciales del dispositivo para iniciar andOTP. diff --git a/app/src/main/res/values-fa-rIR/strings_main.xml b/app/src/main/res/values-fa-rIR/strings_main.xml index d12c96a6..349e5ca6 100644 --- a/app/src/main/res/values-fa-rIR/strings_main.xml +++ b/app/src/main/res/values-fa-rIR/strings_main.xml @@ -10,6 +10,7 @@ همه تگ‌ها بدون تگ کد QR از تصویر + قبول کنید %d ثانیه @@ -45,6 +46,7 @@ تغییر تصویر حذف نمایش کد QR + پین را تنظیم کنید احراز هویت ناموفق بود، لطفا دوباره تلاش کنید! احراز هویت ناموفق بود، در حال بستن andOTP! @@ -73,6 +75,7 @@ کد QR اخطار استهلاک گذرواژه را وارد کنید + پین گذرواژه را وارد کنید تایید گذرواژه برای شروع andOTP لطفا اعتبارنامه دستگاه خود را وارد کنید. diff --git a/app/src/main/res/values-fr-rFR/strings_main.xml b/app/src/main/res/values-fr-rFR/strings_main.xml index 23d6d333..02fafad4 100644 --- a/app/src/main/res/values-fr-rFR/strings_main.xml +++ b/app/src/main/res/values-fr-rFR/strings_main.xml @@ -10,6 +10,7 @@ Tous les tags Aucun tag QR-code à partir d\'une image + Accepter %d s @@ -45,6 +46,7 @@ Modifier l\'image Supprimer Montrer le QR Code + Définir le code PIN Échec d\'authentification, veuillez réessayer ! Échec d\'authentification, fermeture d\'andOTP ! @@ -73,6 +75,7 @@ QR Code Avis d\'obsolescence Saisir le mot de passe + PIN Saisir le mot de passe Confirmer le mot de passe Veuillez entrer les informations d\'identification du périphérique pour démarrer andOTP. diff --git a/app/src/main/res/values-gl-rES/strings_main.xml b/app/src/main/res/values-gl-rES/strings_main.xml index 4fefd5e0..6bac2297 100644 --- a/app/src/main/res/values-gl-rES/strings_main.xml +++ b/app/src/main/res/values-gl-rES/strings_main.xml @@ -10,6 +10,7 @@ Todas as etiquetas Sen etiquetas Código QR desde imaxe + Aceptar %d s @@ -45,6 +46,7 @@ Cambiar imaxe Eliminar Mostrar código QR + Introducir o pin Fallou a autenticación, por favor inténtao de novo! Fallou a autenticación, pechando andOTP! @@ -73,6 +75,7 @@ Código QR Aviso de abandono Introducir contrasinal + PIN Introducir contrasinal Confirmar contrasinal Introduce as credenciais do dispositivo para iniciar andOTP. diff --git a/app/src/main/res/values-hi-rIN/strings_main.xml b/app/src/main/res/values-hi-rIN/strings_main.xml index d35563b8..83213916 100644 --- a/app/src/main/res/values-hi-rIN/strings_main.xml +++ b/app/src/main/res/values-hi-rIN/strings_main.xml @@ -10,6 +10,7 @@ सभी टैग कोई टैग नहीं छवि से क्यूआर कोड + स्वीकार करना %d सेकेंड @@ -45,6 +46,7 @@ छवि बदलें हटाएं Show QR Code + पिन सेट करें प्रमाणीकरण असफल| कृपया पुन: प्रयास करें! प्रमाणीकरण विफल, समापन andOTP! @@ -73,6 +75,7 @@ QR Code Deprecation notice अपना पासवोर्ड डाले + पिन अपना पासवोर्ड डाले पासवर्ड की पुष्टि करें AndOTP शुरू करने के लिए कृपया अपनी यंत्र साख दर्ज करें। diff --git a/app/src/main/res/values-hu-rHU/strings_main.xml b/app/src/main/res/values-hu-rHU/strings_main.xml index 45b97e73..d8d34970 100644 --- a/app/src/main/res/values-hu-rHU/strings_main.xml +++ b/app/src/main/res/values-hu-rHU/strings_main.xml @@ -10,6 +10,7 @@ Összes címke Nincs címke QR-kód képből + Elfogad %d másodperc @@ -45,6 +46,7 @@ Kép módosítása Eltávolítás Show QR Code + Állítsa be a PIN-kódot A hitelesítés sikertelen, próbálja újra! A hitelesítés sikertelen, az andOTP bezárul! @@ -73,6 +75,7 @@ QR Code Deprecation notice Jelszó megadása + PIN Jelszó megadása Jelszó megerősítése Kérjük, adja meg a készülék hitelesítő adatait az andOTP indításához. diff --git a/app/src/main/res/values-it-rIT/strings_main.xml b/app/src/main/res/values-it-rIT/strings_main.xml index c31d95eb..7a17d7a0 100644 --- a/app/src/main/res/values-it-rIT/strings_main.xml +++ b/app/src/main/res/values-it-rIT/strings_main.xml @@ -10,6 +10,7 @@ Tutti i tag Nessun tag QR code da immagine + Accettare %d s @@ -45,6 +46,7 @@ Cambia Immagine Rimuovi Mostra codice QR + Imposta PIN Autenticazione non riuscita, riprova! Autenticazione non riuscita, andOTP verrà chiuso! @@ -73,6 +75,7 @@ Codice QR Avviso di deprecazione Inserisci la password + PIN Inserisci la password Conferma la password Si prega di inserire le credenziali del dispositivo per avviare andOTP. diff --git a/app/src/main/res/values-ja-rJP/strings_main.xml b/app/src/main/res/values-ja-rJP/strings_main.xml index 42ef7637..8dce59d0 100644 --- a/app/src/main/res/values-ja-rJP/strings_main.xml +++ b/app/src/main/res/values-ja-rJP/strings_main.xml @@ -10,6 +10,7 @@ すべてのタグ タグなし QR コードを画像からスキャン + 受け入れる %d 秒 @@ -45,6 +46,7 @@ 画像を変更 削除 QR コードを表示 + PINを設定 認証に失敗しました、もう一度やり直してください! 認証に失敗しました、 andOTP を終了します! @@ -73,6 +75,7 @@ QR コード サポート廃止の通知 パスワードを入力 + PIN パスワードを入力 パスワードの確認 andOTP を起動するためにデバイス認証情報を入力してください。 diff --git a/app/src/main/res/values-nl-rNL/strings_main.xml b/app/src/main/res/values-nl-rNL/strings_main.xml index 030f2c9c..df6150c4 100644 --- a/app/src/main/res/values-nl-rNL/strings_main.xml +++ b/app/src/main/res/values-nl-rNL/strings_main.xml @@ -10,6 +10,7 @@ Alle labels Geen labels QR-code van afbeelding + Accepteren %d s @@ -45,6 +46,7 @@ Afbeelding wijzigen Verwijderen Show QR Code + PIN instellen Authenticatie mislukt. Probeer opnieuw! Verificatie is mislukt, andOTP wordt afgesloten! @@ -73,6 +75,7 @@ QR Code Deprecation notice Wachtwoord invoeren + PIN Wachtwoord invoeren Wachtwoord bevestigen Voer de referenties van uw apparaat in om te beginnen met andOTP. diff --git a/app/src/main/res/values-pl-rPL/strings_main.xml b/app/src/main/res/values-pl-rPL/strings_main.xml index 03820e7e..01e353f5 100644 --- a/app/src/main/res/values-pl-rPL/strings_main.xml +++ b/app/src/main/res/values-pl-rPL/strings_main.xml @@ -10,6 +10,7 @@ Wszystkie znaczniki Brak znaczników Kod QR z obrazu + Zaakceptuj %d s @@ -45,6 +46,7 @@ Zmień obraz Usuń Pokaż kod QR + Ustaw PIN Uwierzytelnianie nie powiodło się, proszę spróbować ponownie Uwierzytelnianie nie powiodło się, zamykanie andOTP @@ -73,6 +75,7 @@ Kod QR Informacja o wycofaniu Wprowadź hasło + PIN Wprowadź hasło Potwierdź hasło Podaj swoje dane uwierzytelniające, aby uruchomić andOTP. diff --git a/app/src/main/res/values-pt-rBR/strings_main.xml b/app/src/main/res/values-pt-rBR/strings_main.xml index 1ebe4a3c..6752fb0c 100644 --- a/app/src/main/res/values-pt-rBR/strings_main.xml +++ b/app/src/main/res/values-pt-rBR/strings_main.xml @@ -10,6 +10,7 @@ Todos os marcadores Sem marcadores QR code from image + Aceitar %d s @@ -45,6 +46,7 @@ Alterar imagem Remover Show QR Code + Definir PIN Falha na autenticação. Por favor, tente novamente. Falha na autenticação, fechando o aplicativo. @@ -73,6 +75,7 @@ QR Code Deprecation notice Digite a senha + PIN Digite a senha Confirme a senha Por favor, insira credenciais do dispositivo para iniciar andOTP. diff --git a/app/src/main/res/values-ru-rRU/strings_main.xml b/app/src/main/res/values-ru-rRU/strings_main.xml index f9f1f2d0..a48996cd 100644 --- a/app/src/main/res/values-ru-rRU/strings_main.xml +++ b/app/src/main/res/values-ru-rRU/strings_main.xml @@ -10,6 +10,7 @@ Все теги Нет тегов QR код из изображения + принимать %d сек. @@ -45,6 +46,7 @@ Изменить изображение Убрать Показать QR-код + Установить PIN Ошибка аутентификации! Пожалуйста, попробуйте снова! Ошибка аутентификации, andOTP закрывается! @@ -73,6 +75,7 @@ QR-код Уведомление об устаревании Введите пароль + PIN Введите пароль Подтвердите пароль Пожалуйста, подтвердите учетные данные устройства для открытия andOTP. diff --git a/app/src/main/res/values-sl-rSI/strings_main.xml b/app/src/main/res/values-sl-rSI/strings_main.xml index dd58f52f..2087567a 100644 --- a/app/src/main/res/values-sl-rSI/strings_main.xml +++ b/app/src/main/res/values-sl-rSI/strings_main.xml @@ -10,6 +10,7 @@ Vse oznake Ni oznak QR code from image + Sprejmi %d s @@ -45,6 +46,7 @@ Spremeni podobo Odstrani Show QR Code + Nastavite kodo PIN Prišlo je do napake pri preverjanju vaše identitete. Prosimo, poskusite ponovno! Preverjanje pristnosti ni uspelo, andOTP se zapira! @@ -73,6 +75,7 @@ QR Code Deprecation notice Vnesite geslo + PIN Vnesite geslo Potrdite geslo Prosim, vnesite podatke o napravi za zagon andOTP. diff --git a/app/src/main/res/values-sv-rSE/strings_main.xml b/app/src/main/res/values-sv-rSE/strings_main.xml index 8dbf40f6..8d669819 100644 --- a/app/src/main/res/values-sv-rSE/strings_main.xml +++ b/app/src/main/res/values-sv-rSE/strings_main.xml @@ -10,6 +10,7 @@ Alla taggar Inga taggar QR-kod från bild + Acceptera %d s @@ -45,6 +46,7 @@ Ändra bild Ta bort Visa QR-kod + Ställ in PIN-kod Autentisering misslyckades, vänligen försök igen! Autentisering misslyckades, stänger andOTP! @@ -73,6 +75,7 @@ QR-kod Avskrivningsnotis Ange lösenord + PIN Ange lösenord Bekräfta lösenord Ange dina enhetsuppgifter för att starta andOTP. diff --git a/app/src/main/res/values-tr-rTR/strings_main.xml b/app/src/main/res/values-tr-rTR/strings_main.xml index 1f1146b6..10b1cff0 100644 --- a/app/src/main/res/values-tr-rTR/strings_main.xml +++ b/app/src/main/res/values-tr-rTR/strings_main.xml @@ -10,6 +10,7 @@ Tüm etiketler Etiket yok Görüntüden QR kodu + Kabul etmek %d lar(ler) @@ -45,6 +46,7 @@ Görseli değiştir Kaldır QR Kodunu Göster + PIN ayarla Kimlik doğrulama başarısız oldu, lütfen tekrar deneyin! Kimlik doğrulama başarısız oldu, andOTP kapatılıyor! @@ -73,6 +75,7 @@ QR Kodu Destek sonlandırma bildirimi Şifre girin + PIN Şifre Girin Şifreyi doğrula Lütfen andOTP\'yi başlatmak için cihaz kimlik bilgilerinizi girin. diff --git a/app/src/main/res/values-uk-rUA/strings_main.xml b/app/src/main/res/values-uk-rUA/strings_main.xml index d68d15cf..ac026f75 100644 --- a/app/src/main/res/values-uk-rUA/strings_main.xml +++ b/app/src/main/res/values-uk-rUA/strings_main.xml @@ -10,6 +10,7 @@ Усі мітки Немає міток QR code from image + Прийміть %d с @@ -45,6 +46,7 @@ Змінити зображення Видалити Show QR Code + Встановити PIN-код Аутентифікація не пройдена. Будь ласка, спробуйте ще раз! Автентифікація не пройдена, закриття andOTP! @@ -73,6 +75,7 @@ QR Code Deprecation notice Введіть пароль + PIN-код Введіть пароль Підтвердіть пароль Будь ласка, підтвердіть дані вашого пристрою, щоб запустити andOTP. diff --git a/app/src/main/res/values-zh-rCN/strings_main.xml b/app/src/main/res/values-zh-rCN/strings_main.xml index cdc883f0..587f3df3 100644 --- a/app/src/main/res/values-zh-rCN/strings_main.xml +++ b/app/src/main/res/values-zh-rCN/strings_main.xml @@ -10,6 +10,7 @@ 所有标签 无标签 相册中选择二维码 + 接受 %d 秒 @@ -45,6 +46,7 @@ 更改图像 移除 显示二维码 + 设置密码 验证失败,请重试! 验证失败,正在关闭 andOTP! @@ -73,6 +75,7 @@ 二维码 禁用通知 输入密码 + 密码 输入密码 确认密码 请输入您的设备凭据以启动 andOTP。 diff --git a/app/src/main/res/values-zh-rTW/strings_main.xml b/app/src/main/res/values-zh-rTW/strings_main.xml index b2adda8d..69db925e 100644 --- a/app/src/main/res/values-zh-rTW/strings_main.xml +++ b/app/src/main/res/values-zh-rTW/strings_main.xml @@ -10,6 +10,7 @@ 所有標籤 無標籤 從圖片而來的 QR code + 接受 %d 秒 @@ -45,6 +46,7 @@ 變更影像 移除 顯示 QR Code + 設置密碼 認證失敗,請再試一次! 認證失敗,andOTP 正在關閉! @@ -73,6 +75,7 @@ QR Code 棄用通知 請輸入密碼 + 密碼 輸入密碼 確認密碼 請鍵入設備憑證來開啟 andOTP。 diff --git a/app/src/main/res/values/strings_main.xml b/app/src/main/res/values/strings_main.xml index 50a9fed1..ec21ec38 100644 --- a/app/src/main/res/values/strings_main.xml +++ b/app/src/main/res/values/strings_main.xml @@ -11,6 +11,7 @@ All tags No tags QR code from image + Accept %d s @@ -56,6 +57,7 @@ Change image Remove Show QR Code + Enter pin More options @@ -93,6 +95,7 @@ Tokens usage KeyStore error QR Code + PIN Enter password