Commmit changes provided by Jakob on

https://github.com/andOTP/andOTP/files/6136564/motp_fixes.patch.txt
This commit is contained in:
Santiago Garcia Mantinan 2021-03-15 11:12:36 +01:00
parent bde29f11cc
commit 8235a930d7
5 changed files with 84 additions and 64 deletions

View file

@ -26,6 +26,7 @@ package org.shadowice.flocke.andotp.Database;
import android.net.Uri;
import org.apache.commons.codec.binary.Base32;
import org.apache.commons.codec.binary.Hex;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
@ -173,7 +174,7 @@ public class Entry {
this.label = label;
if(type == OTPType.MOTP) {
this.secret = secret.getBytes();
}else{
} else {
this.secret = new Base32().decode(secret.toUpperCase());
}
@ -311,14 +312,22 @@ public class Entry {
case STEAM:
type = "steam";
break;
case MOTP:
type = "motp";
break;
default:
return null;
}
Uri.Builder builder = new Uri.Builder()
.scheme("otpauth")
.authority(type)
.appendPath(this.label)
.appendQueryParameter("secret", new Base32().encodeAsString(this.secret));
.appendPath(this.label);
if (this.type == OTPType.MOTP)
builder.appendQueryParameter("secret", new String(this.secret));
else
builder.appendQueryParameter("secret", new Base32().encodeAsString(this.secret));
if (this.issuer != null) {
builder.appendQueryParameter("issuer", this.issuer);
}
@ -361,7 +370,10 @@ public class Entry {
}
public String getSecretEncoded() {
return new String(new Base32().encode(secret));
if (type == OTPType.MOTP)
return new String(secret);
else
return new String(new Base32().encode(secret));
}
public void setSecret(byte[] secret) {
@ -478,10 +490,6 @@ public class Entry {
public void setListId(long newId) {
listId = newId;
}
public boolean updateOTP() {
return updateOTP(false);
}
public boolean updateOTP(boolean updateNow) {
if (type == OTPType.TOTP || type == OTPType.STEAM) {
@ -512,7 +520,7 @@ public class Entry {
if (currentPin.isEmpty()) {
currentOTP = MOTP_NO_PIN_CODE;
} else {
currentOTP = TokenCalculator.MOTP(currentPin, new String(this.secret));
currentOTP = TokenCalculator.MOTP(currentPin, new String(this.secret), time);
}
last_update = counter;
setColor(COLOR_DEFAULT);
@ -589,9 +597,12 @@ public class Entry {
return color;
}
public static boolean validateSecret(String secret) {
public static boolean validateSecret(String secret, OTPType type) {
try {
new Base32().decode(secret.toUpperCase());
if (type == OTPType.MOTP)
Hex.decodeHex(secret);
else
new Base32().decode(secret.toUpperCase());
} catch (Exception e) {
return false;
}

View file

@ -202,13 +202,13 @@ public class ManualEntryDialog {
positiveButton.setOnClickListener(view -> {
//Replace spaces with empty characters
String secret = secretInput.getText().toString().replaceAll("\\s+","");
Entry.OTPType type = (Entry.OTPType) typeInput.getSelectedItem();
if (!Entry.validateSecret(secret)) {
if (!Entry.validateSecret(secret, type)) {
secretInput.setError(callingActivity.getString(R.string.error_invalid_secret));
return;
}
Entry.OTPType type = (Entry.OTPType) typeInput.getSelectedItem();
TokenCalculator.HashAlgorithm algorithm = (TokenCalculator.HashAlgorithm) algorithmInput.getSelectedItem();
int digits = Integer.parseInt(digitsInput.getText().toString());
@ -259,6 +259,27 @@ public class ManualEntryDialog {
if (updateCallback != null)
updateCallback.onUpdate();
}
} else if (type == Entry.OTPType.MOTP) {
if (isNewEntry) {
Entry newEntry;
newEntry = new Entry(type, secret, issuer, label, tagsAdapter.getActiveTags());
newEntry.updateOTP(false);
newEntry.setLastUsed(System.currentTimeMillis());
adapter.addEntry(newEntry);
} else {
oldEntry.setIssuer(issuer, true);
oldEntry.setLabel(label);
oldEntry.setTags(tagsAdapter.getActiveTags());
oldEntry.updateOTP(false);
if (updateCallback != null)
updateCallback.onUpdate();
}
callingActivity.refreshTags();
}
dialog.dismiss();

View file

@ -23,11 +23,12 @@
package org.shadowice.flocke.andotp.Utilities;
import org.apache.commons.codec.binary.Hex;
import java.nio.ByteBuffer;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Date;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
@ -118,28 +119,25 @@ public class TokenCalculator {
return r;
}
public static String MOTP(String PIN, String secret)
public static String MOTP(String PIN, String secret, long epoch)
{
Date dateNow = new Date();
String epoch = "" + (dateNow.getTime());
epoch = epoch.substring(0, epoch.length() - 4);
String otp = epoch + secret + PIN;
String epochText = String.valueOf(epoch / 10);
String hashText = epochText + secret + PIN;
String otp = "";
try {
// Create MD5 Hash
MessageDigest digest = MessageDigest.getInstance("MD5");
digest.update(otp.getBytes());
byte messageDigest[] = digest.digest();
digest.update(hashText.getBytes());
byte[] messageDigest = digest.digest();
// Create Hex String
StringBuffer hexString = new StringBuffer();
for (int i = 0; i < messageDigest.length; i++)
hexString.append(Integer.toHexString(0xFF & messageDigest[i]));
String hexString = Hex.encodeHexString(messageDigest);
otp = hexString.substring(0, 6);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
otp="";
}
return otp;
}
}

View file

@ -39,11 +39,10 @@ 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.Button;
import android.widget.EditText;
import android.widget.Filter;
@ -312,15 +311,21 @@ public class EntriesCardAdapter extends RecyclerView.Adapter<EntryViewHolder>
public void onCardSingleClicked(final int position, final String text) {
switch (settings.getTapSingle()) {
case REVEAL:
establishPinIfNeeded(position);
cardTapToRevealHandler(position);
break;
case COPY:
establishPinIfNeeded(position);
copyHandler(position, text, false);
break;
case COPY_BACKGROUND:
establishPinIfNeeded(position);
copyHandler(position, text, true);
break;
default:
// If tap-to-reveal is disabled a single tab still needs to establish the PIN
if (!settings.getTapToReveal())
establishPinIfNeeded(position);
break;
}
}
@ -329,12 +334,15 @@ public class EntriesCardAdapter extends RecyclerView.Adapter<EntryViewHolder>
public void onCardDoubleClicked(final int position, final String text) {
switch (settings.getTapDouble()) {
case REVEAL:
establishPinIfNeeded(position);
cardTapToRevealHandler(position);
break;
case COPY:
establishPinIfNeeded(position);
copyHandler(position, text, false);
break;
case COPY_BACKGROUND:
establishPinIfNeeded(position);
copyHandler(position, text, true);
break;
default:
@ -351,19 +359,18 @@ public class EntriesCardAdapter extends RecyclerView.Adapter<EntryViewHolder>
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;
}
private void establishPinIfNeeded(int position) {
final Entry entry = displayedEntries.get(position);
if (entry.getType() == Entry.OTPType.MOTP && entry.getPin().isEmpty())
establishPIN(position);
}
private void copyHandler(final int position, final String text, final boolean dropToBackground) {
Tools.copyToClipboard(context, text);
updateLastUsedAndFrequency(position, getRealIndex(position));
@ -620,7 +627,7 @@ public class EntriesCardAdapter extends RecyclerView.Adapter<EntryViewHolder>
input.setSingleLine();
input.requestFocus();
input.setTransformationMethod(new PasswordTransformationMethod());
UIHelper.showKeyboard(context,input,true);
UIHelper.showKeyboard(context, input, true);
FrameLayout container = new FrameLayout(context);
container.setPaddingRelative(marginMedium, marginSmall, marginMedium, 0);
@ -629,26 +636,18 @@ public class EntriesCardAdapter extends RecyclerView.Adapter<EntryViewHolder>
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();
.setPositiveButton(R.string.button_accept, (dialogInterface, 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);
}
displayedEntries.get(pos).setPin(newPin);
Entry e = entries.getEntry(realIndex);
e.setPin(newPin);
e.updateOTP(true);
notifyItemChanged(pos);
UIHelper.hideKeyboard(context, input);
})
.setNegativeButton(android.R.string.cancel, (dialogInterface, i) -> UIHelper.hideKeyboard(context, input))
.create()
.show();
}

View file

@ -151,14 +151,6 @@ public class EntryViewHolder extends RecyclerView.ViewHolder
);
}
});
itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (callback != null)
callback.onItemClickListener(getAdapterPosition());
}
});
}
@FunctionalInterface
@ -362,7 +354,6 @@ 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)