/*
* Copyright (c) 2015 Jarrad Hope
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package io.syng.entity;
import android.util.Log;
import org.ethereum.crypto.ECKey;
import org.ethereum.crypto.HashUtil;
import org.ethereum.wallet.EtherSaleWallet;
import org.ethereum.wallet.EtherSaleWalletDecoder;
import org.json.JSONObject;
import org.spongycastle.util.encoders.Hex;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import io.syng.util.Encryption;
public class Profile implements Serializable {
private static final String TAG = "Profile";
protected String name;
protected String id = createPrivateKey();
protected List<String> privateKeys = new ArrayList<>();
protected List<String> publicKeys = new ArrayList<>();
/* "password protect profile" (encrypt the private keys) */
protected boolean passwordProtectedProfile;
protected List<Dapp> dapps = new ArrayList<>();
private static final long serialVersionUID = 1L;
protected String passwordHash;
protected transient boolean isEncrypted;
public Profile() {
addPrivateKey(createPrivateKey(), null);
addDefaultApps();
}
public Profile(String privateKey) {
addPrivateKey(privateKey, null);
addDefaultApps();
}
public Profile(List<String> privateKeys) {
for (String privateKey: privateKeys) {
addPrivateKey(privateKey, null);
}
addDefaultApps();
}
protected void addDefaultApps() {
// Add console dapp
Dapp console = new Dapp("Console");
dapps.add(console);
// Add wallet dapp
Dapp wallet = new Dapp("Wallet");
wallet.setUrl("dapp://syng.io/dapps/wallet");
dapps.add(wallet);
// Add contacts dapp
Dapp contacts = new Dapp("Contacts");
contacts.setUrl("dapp://syng.io/dapps/contacts");
dapps.add(contacts);
}
protected String createPrivateKey() {
byte[] privateKey = HashUtil.sha3(HashUtil.randomPeerId());
return Hex.toHexString(privateKey);
}
public List<String> getPrivateKeys(String password) {
List<String> keys = new ArrayList<>();
for (String privateKey: privateKeys) {
String key = passwordProtectedProfile ? decryptPrivateKey(privateKey, password) : privateKey;
keys.add(key);
}
return keys;
}
public List<String> getAddresses() {
return publicKeys;
}
public void addPrivateKeys(List<String> privateKeys, String password) {
for (String privateKey: privateKeys) {
addPrivateKey(privateKey, password);
}
}
protected String getPublicKey(String privateKey) {
ECKey ecKey = ECKey.fromPrivate(Hex.decode(privateKey));
return Hex.toHexString(ecKey.getAddress());
}
public boolean addPrivateKey(String privateKey, String password) {
if (password != null && passwordProtectedProfile) {
if (!checkPassword(password)) {
return false;
}
this.privateKeys.add(encryptPrivateKey(privateKey, password));
} else {
this.privateKeys.add(privateKey);
}
this.publicKeys.add(getPublicKey(privateKey));
return true;
}
private String hash(String text) {
return Hex.toHexString(HashUtil.sha3(text.getBytes()));
}
public void removePrivateKey(String privateKey) {
this.privateKeys.remove(privateKey);
this.publicKeys.remove(getPublicKey(privateKey));
}
public List<Dapp> getDapps() {
return dapps;
}
public void setDapps(List<Dapp> dapps) {
this.dapps = dapps;
}
public void addDapp(Dapp dapp) {
this.dapps.add(dapp);
}
public void updateDapp(Dapp dapp) {
for (Dapp item : dapps) {
if (item.getId().equals(dapp.getId())) {
int index = dapps.indexOf(item);
dapps.set(index, dapp);
}
}
}
public void removeDapp(Dapp dapp) {
this.dapps.remove(dapp);
}
public boolean getPasswordProtectedProfile() {
return passwordProtectedProfile;
}
public void setPasswordProtectedProfile(boolean passwordProtectedProfile) {
this.passwordProtectedProfile = passwordProtectedProfile;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void setId(String id) {
this.id = id;
}
public String getId() {
return id;
}
public void setPassword(String password) {
if (!passwordProtectedProfile) {
this.passwordHash = hash(password);
this.encrypt(password);
}
}
public boolean checkPassword(String password) {
return passwordHash.equals(hash(password));
}
public void encrypt(String password) {
if (!passwordProtectedProfile) {
List<String> encrypted = new ArrayList<>();
for (String privateKey : this.privateKeys) {
encrypted.add(encryptPrivateKey(privateKey, password));
}
this.privateKeys.clear();
this.privateKeys = encrypted;
passwordProtectedProfile = true;
}
}
public boolean decrypt(String password) {
if (passwordProtectedProfile) {
if (!checkPassword(password)) {
return false;
}
List<String> decrypted = new ArrayList<>();
for (String privateKey : this.privateKeys) {
decrypted.add(decryptPrivateKey(privateKey, password));
}
this.privateKeys = decrypted;
passwordProtectedProfile = false;
}
return true;
}
protected String encryptPrivateKey(String privateKey, String password) {
String encryptedKey = Encryption.encrypt(privateKey, password);
if (encryptedKey == null) {
Log.w(TAG, "Could not encrypt private key");
}
return encryptedKey;
}
protected String decryptPrivateKey(String privateKey, String password) {
String decryptedKey = Encryption.decrypt(privateKey, password);
if (decryptedKey == null) {
Log.w(TAG, "Could not decrypt private key");
}
return decryptedKey;
}
public boolean importWallet(String jsonWallet, String importPassword, String currentPassword) {
try {
JSONObject json = new JSONObject(jsonWallet);
byte[] privateKey = null;
EtherSaleWallet wallet = new EtherSaleWallet();
if (json.has("encseed")) {
wallet.setEncseed(json.getString("encseed"));
wallet.setEthaddr(json.getString("ethaddr"));
wallet.setEmail(json.getString("email"));
wallet.setBtcaddr(json.getString("btcaddr"));
EtherSaleWalletDecoder decoder = new EtherSaleWalletDecoder(wallet);
privateKey = decoder.getPrivateKey(importPassword);
} else if (json.has("Crypto")) {
wallet.setEncseed(json.getJSONObject("Crypto").getJSONObject("cipherparams").getString("iv") + json.getJSONObject("Crypto").getString("ciphertext"));
wallet.setEthaddr(json.getString("address"));
EtherSaleWalletDecoder decoder = new EtherSaleWalletDecoder(wallet);
privateKey = decoder.getPrivateKey(importPassword);
}
if (privateKey == null) {
Log.w(TAG, "Invalid json wallet file.");
return false;
}
ECKey key = ECKey.fromPrivate(privateKey);
String address = Hex.toHexString(key.getAddress());
if (address.equals(wallet.getEthaddr())) {
String keyToAdd = Hex.toHexString(privateKey);
if (passwordProtectedProfile) {
addPrivateKey(keyToAdd, currentPassword);
} else {
addPrivateKey(keyToAdd, null);
}
this.publicKeys.add(getPublicKey(keyToAdd));
} else {
Log.w(TAG, "Invalid wallet password.");
return false;
}
} catch (Exception e) {
Log.e(TAG, "Error importing wallet: " + e.getMessage());
return false;
}
return true;
}
public boolean importPrivateKey(String privateKey, String importedKeyPassword, String walletPassword) {
String decryptedKey = importedKeyPassword == null || importedKeyPassword == "" ? privateKey : decryptPrivateKey(privateKey, importedKeyPassword);
if (passwordProtectedProfile) {
if (checkPassword(walletPassword)) {
addPrivateKey(decryptedKey, walletPassword);
} else {
return false;
}
} else {
addPrivateKey(decryptedKey, null);
}
return true;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Profile)) return false;
Profile object = (Profile) o;
return object.getId().equalsIgnoreCase(id);
}
}