/* * Aegis Bitcoin Wallet - The secure Bitcoin wallet for Android * Copyright 2014 Bojan Simic and specularX.co, designed by Reuven Yamrom * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.aegiswallet.utils; import android.content.Context; import android.content.SharedPreferences; import android.content.res.AssetFileDescriptor; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.Color; import android.graphics.Typeface; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.util.Log; import com.aegiswallet.R; import com.aegiswallet.objects.SMSTransactionPojo; import com.google.bitcoin.core.Address; import com.google.bitcoin.core.AddressFormatException; import com.google.bitcoin.core.NetworkParameters; import com.google.zxing.BarcodeFormat; import com.google.zxing.EncodeHintType; import com.google.zxing.WriterException; import com.google.zxing.common.BitMatrix; import com.google.zxing.qrcode.QRCodeWriter; import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel; import org.json.JSONException; import org.json.JSONObject; import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.FilenameFilter; import java.io.IOException; import java.io.InputStream; import java.math.BigDecimal; import java.math.BigInteger; import java.security.SecureRandom; import java.util.ArrayList; import java.util.Hashtable; import java.util.List; import java.util.Locale; import java.util.Map; import javax.annotation.Nonnull; import edu.vt.middleware.dictionary.ArrayWordList; import edu.vt.middleware.dictionary.WordListDictionary; import edu.vt.middleware.dictionary.WordLists; import edu.vt.middleware.dictionary.sort.ArraysSort; import edu.vt.middleware.password.DictionarySubstringRule; import edu.vt.middleware.password.Password; import edu.vt.middleware.password.PasswordData; import edu.vt.middleware.password.PasswordValidator; import edu.vt.middleware.password.Rule; import edu.vt.middleware.password.RuleResult; public class BasicUtils { private static SecureRandom random = new SecureRandom(); public static final String TAG = "BasicUtils"; public static final BigInteger ONE_BTC = new BigInteger("100000000", 10); public static final BigInteger ONE_MBTC = new BigInteger("100000", 10); private static final int ONE_BTC_INT = ONE_BTC.intValue(); private static final int ONE_MBTC_INT = ONE_MBTC.intValue(); private final static QRCodeWriter qrCodeWriter = new QRCodeWriter(); public static String satoshiToBTC(BigInteger value) { return formatValue(value, "", "-", Constants.BTC_MIN_PRECISION, 0); } public static String formatValue(@Nonnull final BigInteger value, final int precision, final int shift) { return formatValue(value, "", "-", precision, shift); } public static String formatValue(@Nonnull final BigInteger value, @Nonnull final String plusSign, @Nonnull final String minusSign, final int precision, final int shift) { long longValue = value.longValue(); final String sign = longValue < 0 ? minusSign : plusSign; if (shift == 0) { if (precision == 2) longValue = longValue - longValue % 1000000 + longValue % 1000000 / 500000 * 1000000; else if (precision == 4) longValue = longValue - longValue % 10000 + longValue % 10000 / 5000 * 10000; else if (precision == 6) longValue = longValue - longValue % 100 + longValue % 100 / 50 * 100; else if (precision == 8) ; else throw new IllegalArgumentException("cannot handle precision/shift: " + precision + "/" + shift); final long absValue = Math.abs(longValue); final long coins = absValue / ONE_BTC_INT; final int satoshis = (int) (absValue % ONE_BTC_INT); if (satoshis % 1000000 == 0) return String.format(Locale.US, "%s%d.%02d", sign, coins, satoshis / 1000000); else if (satoshis % 10000 == 0) return String.format(Locale.US, "%s%d.%04d", sign, coins, satoshis / 10000); else if (satoshis % 100 == 0) return String.format(Locale.US, "%s%d.%06d", sign, coins, satoshis / 100); else return String.format(Locale.US, "%s%d.%08d", sign, coins, satoshis); } else if (shift == 3) { if (precision == 2) longValue = longValue - longValue % 1000 + longValue % 1000 / 500 * 1000; else if (precision == 4) longValue = longValue - longValue % 10 + longValue % 10 / 5 * 10; else if (precision == 5) ; else throw new IllegalArgumentException("cannot handle precision/shift: " + precision + "/" + shift); final long absValue = Math.abs(longValue); final long coins = absValue / ONE_MBTC_INT; final int satoshis = (int) (absValue % ONE_MBTC_INT); if (satoshis % 1000 == 0) return String.format(Locale.US, "%s%d.%02d", sign, coins, satoshis / 1000); else if (satoshis % 10 == 0) return String.format(Locale.US, "%s%d.%04d", sign, coins, satoshis / 10); else return String.format(Locale.US, "%s%d.%05d", sign, coins, satoshis); } else { throw new IllegalArgumentException("cannot handle shift: " + shift); } } public static BigInteger toNanoCoins(final String value, final int shift) { try { final BigInteger nanoCoins = new BigDecimal(value).movePointRight(8 - shift).toBigIntegerExact(); if (nanoCoins.signum() < 0) throw new IllegalArgumentException("negative amount: " + value); if (nanoCoins.compareTo(NetworkParameters.MAX_MONEY) > 0) Log.e(TAG, "AMOUNT TOO LARGE"); return nanoCoins; } catch (ArithmeticException e) { Log.d(TAG, e.getMessage()); return BigInteger.ZERO; } } public static Bitmap encodeAsBitmap(String contents, BarcodeFormat format, int dimension) { Bitmap bitmap = null; try { final Hashtable<EncodeHintType, Object> hints = new Hashtable<EncodeHintType, Object>(); //hints.put(EncodeHintType.MARGIN, 0); hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H); final BitMatrix result = qrCodeWriter.encode(contents, BarcodeFormat.QR_CODE, dimension, dimension, hints); final int width = result.getWidth(); final int height = result.getHeight(); final int[] pixels = new int[width * height]; for (int y = 0; y < height; y++) { final int offset = y * width; for (int x = 0; x < width; x++) { pixels[offset + x] = result.get(x, y) ? Color.BLACK : Color.WHITE; } } bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); bitmap.setPixels(pixels, 0, width, 0, 0, width, height); return bitmap; } catch (WriterException e) { Log.e("Basic Utils", "cannot write to bitmap " + e.getMessage()); } return bitmap; } private static String guessAppropriateEncoding(CharSequence contents) { for (int i = 0; i < contents.length(); i++) { if (contents.charAt(i) > 0xFF) { return "UTF-8"; } } return null; } public static JSONObject parseJSONData(Context context, String fileName) { String JSONString = null; JSONObject JSONObject = null; try { //open the inputStream to the file InputStream inputStream = context.openFileInput(fileName); int sizeOfJSONFile = inputStream.available(); //array that will store all the data byte[] bytes = new byte[sizeOfJSONFile]; //reading data into the array from the file inputStream.read(bytes); //close the input stream inputStream.close(); JSONString = new String(bytes, "UTF-8"); JSONObject = new JSONObject(JSONString); } catch (IOException ex) { ex.printStackTrace(); return null; } catch (JSONException x) { return null; } return JSONObject; } public static String generateSecureKey() { return new BigInteger(512, random).toString(32); } public static boolean isNetworkAvailable(Context context) { ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo(); return activeNetworkInfo != null && activeNetworkInfo.isConnected(); } public static Typeface getCustomTypeFace(Context context) { Typeface tf = Typeface.createFromAsset( context.getAssets(), "fonts/regular.otf"); return tf; } public static String[] loadFileList() { String[] fileList = null; File filesPath = Constants.WALLET_BACKUP_DIRECTORY; if (Constants.WALLET_BACKUP_DIRECTORY.exists() && Constants.WALLET_BACKUP_DIRECTORY.isDirectory()) { final String filePrefix = "AegisWalletBackup"; try { filesPath.mkdirs(); } catch (SecurityException e) { Log.e(TAG, "Could not write to SD card - " + e.toString()); } if (filesPath.exists()) { FilenameFilter filter = new FilenameFilter() { public boolean accept(File dir, String filename) { File sel = new File(dir, filename); return filename.contains(filePrefix) || sel.isDirectory(); } }; fileList = filesPath.list(filter); } else { fileList = new String[0]; } } return fileList; } public static String getNFCErrorMessage(Context context, String writeMessage) { String result = null; Resources resources = context.getResources(); if (writeMessage.equals(Constants.NFC_TAG_TOO_SMALL)) { result = resources.getString(R.string.nfc_error_tag_too_small); } else if (writeMessage.equals(Constants.NFC_READ_ONLY)) { result = resources.getString(R.string.nfc_error_tag_read_only); } else if (writeMessage.equals(Constants.NFC_CANT_CONNECT)) { result = resources.getString(R.string.nfc_error_tag_cant_connect); } else if (writeMessage.equals(Constants.NFC_CANT_FORMAT)) { result = resources.getString(R.string.nfc_error_tag_cant_format); } else if (writeMessage.equals(Constants.NFC_TAG_NOT_SUPPORTED)) { result = resources.getString(R.string.nfc_error_tag_not_supported); } else if (writeMessage.equals(Constants.NFC_WRITE_EXCEPTION)) { result = resources.getString(R.string.nfc_error_tag_write_exception); } return result; } public static boolean isPasswordInDictionary(Context context, String password) { boolean resultBool = false; if(password == null) return false; try { AssetFileDescriptor descriptor = context.getAssets().openFd("commonpasswords.xmf"); FileReader reader = new FileReader(descriptor.getFileDescriptor()); // create a case sensitive word list and sort it ArrayWordList awl = WordLists.createFromReader( new FileReader[]{reader}, true, new ArraysSort()); WordListDictionary dict = new WordListDictionary(awl); DictionarySubstringRule dictRule = new DictionarySubstringRule(dict); dictRule.setWordLength(6); // size of words to check in the password dictRule.setMatchBackwards(true); // match dictionary words backwards List<Rule> ruleList = new ArrayList<Rule>(); ruleList.add(dictRule); PasswordValidator validator = new PasswordValidator(ruleList); PasswordData passwordData = new PasswordData(new Password(password.toLowerCase())); RuleResult result = validator.validate(passwordData); if (result.isValid()) { Log.d(TAG, "Valid password"); resultBool = true; } else { Log.d(TAG, "Invalid password"); } } catch (FileNotFoundException e) { Log.d(TAG, e.getMessage()); } catch (IOException e){ Log.d(TAG, e.getMessage()); } return resultBool; } public static String getAddressFromMessage(String message){ String address = null; String[] splitString = message.split(" "); for(String s : splitString){ try{ Address a = new Address(Constants.NETWORK_PARAMETERS, s); if(a != null){ return a.toString(); } } catch (AddressFormatException e){ continue; } } return address; } public static String findKeyInPrefs(SharedPreferences prefs, String value) { for (Map.Entry<String, ?> entry: prefs.getAll().entrySet()) { if (value.equals(entry.getValue())) { return entry.getKey(); } } return null; // not found } public static ArrayList<SMSTransactionPojo> getAllPendingTransactions(SharedPreferences prefs){ ArrayList<SMSTransactionPojo> list = new ArrayList<SMSTransactionPojo>(); for (Map.Entry<String, ?> entry: prefs.getAll().entrySet()) { SMSTransactionPojo pojo = new SMSTransactionPojo(entry.getValue() + ""); list.add(pojo); } return list; } public static ArrayList<SMSTransactionPojo> getAllRespondedSMSTransactions(SharedPreferences prefs){ ArrayList<SMSTransactionPojo> list = new ArrayList<SMSTransactionPojo>(); for (Map.Entry<String, ?> entry: prefs.getAll().entrySet()) { SMSTransactionPojo pojo = new SMSTransactionPojo(entry.getValue() + ""); if(pojo.getStatus() == Constants.SMS_STATUS_REC){ list.add(pojo); } } return list; } public static ArrayList<SMSTransactionPojo> getAllNotRespondedSMSTransactions(SharedPreferences prefs){ ArrayList<SMSTransactionPojo> list = new ArrayList<SMSTransactionPojo>(); for (Map.Entry<String, ?> entry: prefs.getAll().entrySet()) { SMSTransactionPojo pojo = new SMSTransactionPojo(entry.getValue() + ""); if(pojo.getStatus() == Constants.SMS_STATUS_INIT){ list.add(pojo); } } return list; } }