package de.pinyto.ctSESAM;
import android.util.Base64;
import android.util.Log;
import org.json.JSONException;
import org.json.JSONObject;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.Random;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* PasswordSetting wraps a set of settings for one domain.
*/
public class PasswordSetting {
private String domain;
private String url;
private String username;
private String legacyPassword;
private String notes;
private int iterations = 4096;
private byte[] salt;
private Date cDate;
private Date mDate;
private final String defaultCharacterSetDigits = "0123456789";
private final String defaultCharacterSetLowerCase = "abcdefghijklmnopqrstuvwxyz";
private final String defaultCharacterSetUpperCase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
private final String defaultCharacterSetExtra = "#!\"~|@^°$%&/()[]{}=-_+*<>;:.";
private String characterSetExtra;
private String template = "aAnoxxxxxx";
private boolean synced = false;
PasswordSetting(String domain) {
this.domain = domain;
this.salt = Crypter.createSalt();
this.cDate = Calendar.getInstance().getTime();
this.mDate = this.cDate;
this.characterSetExtra = this.defaultCharacterSetExtra;
this.calculateTemplate();
}
public String getDomain() {
return this.domain;
}
public void setDomain(String domain) {
this.domain = domain;
this.synced = false;
}
public boolean hasUsername() {
return this.username != null && this.username.length() > 0;
}
public String getUsername() {
if (this.username != null) {
return this.username;
} else {
return "";
}
}
public void setUsername(String username) {
if (!username.equals(this.username)) {
this.synced = false;
}
this.username = username;
}
public boolean hasLegacyPassword() {
return this.legacyPassword != null && this.legacyPassword.length() > 0;
}
public String getLegacyPassword() {
if (this.legacyPassword != null) {
return this.legacyPassword;
} else {
return "";
}
}
public void setLegacyPassword(String legacyPassword) {
if (!legacyPassword.equals(this.legacyPassword)) {
this.synced = false;
}
this.legacyPassword = legacyPassword;
}
public String getDefaultCharacterSet() {
String set = "";
set = set + this.defaultCharacterSetDigits;
set = set + this.defaultCharacterSetLowerCase;
set = set + this.defaultCharacterSetUpperCase;
set = set + this.defaultCharacterSetExtra;
return set;
}
public String getLowerCaseCharacterSetAsString() {
return this.defaultCharacterSetLowerCase;
}
public String getUpperCaseCharacterSetAsString() {
return this.defaultCharacterSetUpperCase;
}
public String getDigitsCharacterSetAsString() {
return this.defaultCharacterSetDigits;
}
public List<String> getCharacterSet() {
List<String> characterSet = new ArrayList<>();
String characters = this.getCharacterSetAsString();
for (int i = 0; i < characters.length(); i++) {
characterSet.add(Character.toString(characters.charAt(i)));
}
return characterSet;
}
public String getCharacterSetAsString() {
String set = "";
if (this.getTemplate().contains("n")) {
set = set + this.getDigitsCharacterSetAsString();
}
if (this.getTemplate().contains("a")) {
set = set + this.getLowerCaseCharacterSetAsString();
}
if (this.getTemplate().contains("A")) {
set = set + this.getUpperCaseCharacterSetAsString();
}
if (this.getTemplate().contains("o")) {
set = set + this.getExtraCharacterSetAsString();
}
if (set.length() <= 0) {
set = this.getDefaultCharacterSet();
}
return set;
}
public void setExtraCharacterSet(String extraCharacterSet) {
if (extraCharacterSet == null || extraCharacterSet.length() <= 0) {
this.characterSetExtra = this.defaultCharacterSetExtra;
} else {
this.characterSetExtra = extraCharacterSet;
}
}
public List<String> getExtraCharacterSet() {
List<String> extraCharacterSet = new ArrayList<>();
String characters = this.getExtraCharacterSetAsString();
for (int i = 0; i < characters.length(); i++) {
extraCharacterSet.add(Character.toString(characters.charAt(i)));
}
return extraCharacterSet;
}
public String getExtraCharacterSetAsString() {
if (this.characterSetExtra != null) {
return this.characterSetExtra;
} else {
return this.defaultCharacterSetExtra;
}
}
public List<String> getDigitsCharacterSet() {
List<String> digitsCharacterSet = new ArrayList<>();
String characters = this.defaultCharacterSetDigits;
for (int i = 0; i < characters.length(); i++) {
digitsCharacterSet.add(Character.toString(characters.charAt(i)));
}
return digitsCharacterSet;
}
public List<String> getLowerCaseLettersCharacterSet() {
List<String> lowerCaseLettersCharacterSet = new ArrayList<>();
String characters = this.defaultCharacterSetLowerCase;
for (int i = 0; i < characters.length(); i++) {
lowerCaseLettersCharacterSet.add(Character.toString(characters.charAt(i)));
}
return lowerCaseLettersCharacterSet;
}
public List<String> getUpperCaseLettersCharacterSet() {
List<String> upperCaseLettersCharacterSet = new ArrayList<>();
String characters = this.defaultCharacterSetUpperCase;
for (int i = 0; i < characters.length(); i++) {
upperCaseLettersCharacterSet.add(Character.toString(characters.charAt(i)));
}
return upperCaseLettersCharacterSet;
}
public byte[] getSalt() {
return this.salt;
}
public void setSalt(byte[] salt) {
if (!Arrays.equals(salt, this.salt)) {
this.synced = false;
}
this.salt = salt;
}
public int getLength() {
return this.getTemplate().length();
}
public int getIterations() {
return this.iterations;
}
public void setIterations(int iterations) {
if (iterations != this.iterations) {
this.synced = false;
}
this.iterations = iterations;
}
public Date getCDate() {
return this.cDate;
}
public String getCreationDate() {
DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.ENGLISH);
return df.format(this.cDate);
}
public void setCreationDate(String cDate) {
DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.ENGLISH);
try {
if (df.parse(cDate).compareTo(this.cDate) != 0) {
this.synced = false;
}
this.cDate = df.parse(cDate);
} catch (ParseException e) {
System.out.println("This date has a wrong format: " + cDate);
e.printStackTrace();
}
if (this.cDate.compareTo(this.mDate) > 0) {
this.mDate = this.cDate;
this.synced = false;
}
}
public String getModificationDate() {
DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.ENGLISH);
return df.format(this.mDate);
}
public Date getMDate() {
return this.mDate;
}
public void setModificationDate(String mDate) {
DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.ENGLISH);
try {
if (df.parse(mDate).compareTo(this.mDate) != 0) {
this.synced = false;
}
this.mDate = df.parse(mDate);
} catch (ParseException e) {
System.out.println("This date has a wrong format: " + mDate);
e.printStackTrace();
}
if (this.cDate.compareTo(this.mDate) > 0) {
System.out.println("The modification date was before the creation Date. " +
"Set the creation date to the earlier date.");
this.cDate = this.mDate;
this.synced = false;
}
}
public void setModificationDateToNow() {
this.mDate = Calendar.getInstance().getTime();
if (this.cDate.compareTo(this.mDate) > 0) {
System.out.println("The modification date was before the creation Date. " +
"Set the creation date to the earlier date.");
this.cDate = this.mDate;
}
this.synced = false;
}
public String getNotes() {
if (this.notes != null) {
return this.notes;
} else {
return "";
}
}
public void setNotes(String notes) {
if (!notes.equals(this.notes)) {
this.synced = false;
}
this.notes = notes;
}
public String getUrl() {
if (this.url != null) {
return this.url;
} else {
return "";
}
}
public void setUrl(String url) {
if (!url.equals(this.url)) {
this.synced = false;
}
this.url = url;
}
public String getFullTemplate() {
int complexity = this.getComplexity();
if (complexity >= 0) {
return Integer.toString(complexity) + ";" + this.getTemplate();
} else {
return "";
}
}
private String ShuffleString(String s)
{
int index;
char temp;
char[] array = s.toCharArray();
Random random = new Random();
for (int i = array.length - 1; i > 0; i--)
{
index = random.nextInt(i + 1);
temp = array[index];
array[index] = array[i];
array[i] = temp;
}
return String.valueOf(array);
}
private void calculateTemplate(boolean useLowerCase,
boolean useUpperCase,
boolean useDigits,
boolean useExtra) {
int targetLength = this.getLength();
this.template = "";
boolean aInserted = false;
boolean AInserted = false;
boolean nInserted = false;
boolean oInserted = false;
for (int i = 0; i < targetLength; i++) {
if (useLowerCase && !aInserted) {
this.template = this.template + "a";
aInserted = true;
} else if (useUpperCase && !AInserted) {
this.template = this.template + "A";
AInserted = true;
} else if (useDigits && !nInserted) {
this.template = this.template + "n";
nInserted = true;
} else if (useExtra && !oInserted) {
this.template = this.template + "o";
oInserted = true;
} else {
this.template = this.template + "x";
}
}
this.template = this.ShuffleString(this.template);
}
private void calculateTemplate() {
boolean use_lower_case = this.getTemplate().contains("a");
boolean use_upper_case = this.getTemplate().contains("A");
boolean use_digits = this.getTemplate().contains("n");
boolean use_extra = this.getTemplate().contains("o");
if (!use_lower_case && !use_upper_case && !use_digits && !use_extra) {
use_lower_case = true;
use_upper_case = true;
use_digits = true;
use_extra = true;
}
this.calculateTemplate(use_lower_case, use_upper_case, use_digits, use_extra);
}
public String getTemplate() {
return this.template;
}
public void setTemplate(String template) {
Matcher matcher = Pattern.compile("(([01234567]);)?([aAnox]+)").matcher(template);
if (matcher.matches() && matcher.groupCount() >= 3) {
if (matcher.group(2) != null) {
this.setComplexity(Integer.parseInt(matcher.group(2)));
}
this.template = matcher.group(3);
}
}
public int getComplexity() {
if (this.getTemplate().contains("n") && !this.getTemplate().contains("a") &&
!this.getTemplate().contains("A") && !this.getTemplate().contains("o")) {
return 0;
} else if (!this.getTemplate().contains("n") && this.getTemplate().contains("a") &&
!this.getTemplate().contains("A") && !this.getTemplate().contains("o")) {
return 1;
} else if (!this.getTemplate().contains("n") && !this.getTemplate().contains("a") &&
this.getTemplate().contains("A") && !this.getTemplate().contains("o")) {
return 2;
} else if (this.getTemplate().contains("n") && this.getTemplate().contains("a") &&
!this.getTemplate().contains("A") && !this.getTemplate().contains("o")) {
return 3;
} else if (!this.getTemplate().contains("n") && this.getTemplate().contains("a") &&
this.getTemplate().contains("A") && !this.getTemplate().contains("o")) {
return 4;
} else if (this.getTemplate().contains("n") && this.getTemplate().contains("a") &&
this.getTemplate().contains("A") && !this.getTemplate().contains("o")) {
return 5;
} else if (this.getTemplate().contains("n") && this.getTemplate().contains("a") &&
this.getTemplate().contains("A") && this.getTemplate().contains("o")) {
return 6;
} else {
return -1;
}
}
public void setComplexity(int complexity) {
if (complexity < 0 || complexity > 7) {
Log.e("Complexity error", "Illegal complexity: " + Integer.toString(complexity));
}
}
public boolean isSynced() {
return this.synced;
}
public void setSynced(boolean isSynced) {
this.synced = isSynced;
}
public JSONObject toJSON() {
JSONObject domainObject = new JSONObject();
try {
domainObject.put("domain", this.getDomain());
if (this.url != null && this.url.length() > 0) {
domainObject.put("url", this.getUrl());
}
if (this.username != null && this.username.length() > 0) {
domainObject.put("username", this.getUsername());
}
if (this.legacyPassword != null && this.legacyPassword.length() > 0) {
domainObject.put("legacyPassword", this.getLegacyPassword());
}
if (this.notes != null && this.notes.length() > 0) {
domainObject.put("notes", this.getNotes());
}
domainObject.put("iterations", this.getIterations());
if (this.salt != null && this.salt.length > 0) {
domainObject.put("salt", Base64.encodeToString(this.getSalt(), Base64.DEFAULT));
}
domainObject.put("cDate", this.getCreationDate());
domainObject.put("mDate", this.getModificationDate());
domainObject.put("extras", this.getExtraCharacterSetAsString());
domainObject.put("passwordTemplate", this.getTemplate());
} catch (JSONException e) {
System.out.println("Settings packing error: Unable to pack the JSON data.");
}
return domainObject;
}
public void loadFromJSON(JSONObject loadedSetting) throws JSONException {
if (loadedSetting.has("domain")) {
this.setDomain(loadedSetting.getString("domain"));
}
if (loadedSetting.has("url")) {
this.setUrl(loadedSetting.getString("url"));
}
if (loadedSetting.has("username")) {
this.setUsername(loadedSetting.getString("username"));
}
if (loadedSetting.has("legacyPassword")) {
this.setLegacyPassword(loadedSetting.getString("legacyPassword"));
}
if (loadedSetting.has("notes")) {
this.setNotes(loadedSetting.getString("notes"));
}
if (loadedSetting.has("iterations")) {
this.setIterations(loadedSetting.getInt("iterations"));
}
if (loadedSetting.has("salt")) {
this.setSalt(Base64.decode(loadedSetting.getString("salt"), Base64.DEFAULT));
}
if (loadedSetting.has("cDate")) {
this.setCreationDate(loadedSetting.getString("cDate"));
}
if (loadedSetting.has("mDate")) {
this.setModificationDate(loadedSetting.getString("mDate"));
}
if (loadedSetting.has("extras")) {
this.setExtraCharacterSet(loadedSetting.getString("extras"));
}
if (loadedSetting.has("passwordTemplate")) {
this.setTemplate(loadedSetting.getString("passwordTemplate"));
}
if (loadedSetting.has("length") && loadedSetting.has("usedCharacters") &&
!loadedSetting.has("passwordTemplate")) {
String conversionTemplate = "o";
for (int i = 1; i < loadedSetting.getInt("length"); i++) {
conversionTemplate = conversionTemplate + "x";
}
this.template = conversionTemplate;
this.setExtraCharacterSet(loadedSetting.getString("usedCharacters"));
this.calculateTemplate(false, false, false, true);
}
}
}