/wang-client/src/java/com/robonobo/wang/client/CoinStore.java

https://github.com/coltnz/robonobo · Java · 138 lines · 117 code · 14 blank · 7 comment · 7 complexity · bf1afaf40842e0b8043ba84adbdf5db3 MD5 · raw file

  1. package com.robonobo.wang.client;
  2. import java.io.ByteArrayOutputStream;
  3. import java.io.File;
  4. import java.io.FileInputStream;
  5. import java.io.FileOutputStream;
  6. import java.io.IOException;
  7. import java.security.GeneralSecurityException;
  8. import java.security.MessageDigest;
  9. import java.security.NoSuchAlgorithmException;
  10. import java.util.HashMap;
  11. import java.util.LinkedList;
  12. import java.util.List;
  13. import java.util.Map;
  14. import javax.crypto.Cipher;
  15. import javax.crypto.spec.SecretKeySpec;
  16. import org.apache.commons.logging.Log;
  17. import org.apache.commons.logging.LogFactory;
  18. import com.robonobo.wang.proto.WangProtocol.CoinMsg;
  19. /**
  20. * Stores coins securely on the filesystem. If there are no public methods
  21. * currently being executed, the file store is guaranteed to be in a recoverable
  22. * state.
  23. */
  24. public class CoinStore {
  25. private Log log = LogFactory.getLog(getClass());
  26. private File storageDir;
  27. private Cipher encryptCipher;
  28. private Cipher decryptCipher;
  29. private Map<Integer, List<String>> coinIds = new HashMap<Integer, List<String>>();
  30. public CoinStore(File storageDir, String password) {
  31. // Create our encryption ciphers
  32. try {
  33. byte[] keyArr = generateKey(password, 16);
  34. SecretKeySpec sekritKey = new SecretKeySpec(keyArr, "AES");
  35. encryptCipher = Cipher.getInstance("AES");
  36. encryptCipher.init(Cipher.ENCRYPT_MODE, sekritKey);
  37. decryptCipher = Cipher.getInstance("AES");
  38. decryptCipher.init(Cipher.DECRYPT_MODE, sekritKey);
  39. } catch (Exception e) {
  40. throw new RuntimeException(e);
  41. }
  42. // Load coins from dir
  43. this.storageDir = storageDir;
  44. if(!storageDir.exists())
  45. storageDir.mkdirs();
  46. log.info("CoinStore loading persisted coins from "+storageDir.getAbsolutePath());
  47. double coinVal = 0;
  48. int numCoins = 0;
  49. for (File coinFile : storageDir.listFiles()) {
  50. try {
  51. CoinMsg coin = loadCoinFromFile(coinFile.getName());
  52. if(!coinIds.containsKey(coin.getDenom()))
  53. coinIds.put(coin.getDenom(), new LinkedList<String>());
  54. coinIds.get(coin.getDenom()).add(coin.getCoinId().toString());
  55. coinVal += getDenomValue(coin.getDenom());
  56. numCoins++;
  57. } catch (Exception e) {
  58. log.error("CoinStore encountered error ("+e.getClass().getName()+") loading coin. Skipping.");
  59. }
  60. }
  61. log.info("CoinStore loaded "+numCoins+" coins, value="+coinVal);
  62. }
  63. public synchronized int numCoins(int denom) {
  64. return (coinIds.containsKey(denom)) ? coinIds.get(denom).size() : 0;
  65. }
  66. public synchronized CoinMsg getCoin(int denom) throws CoinStoreException {
  67. if(!coinIds.containsKey(denom) || coinIds.get(denom).size() == 0)
  68. return null;
  69. String coinId = coinIds.get(denom).remove(0);
  70. CoinMsg coin;
  71. try {
  72. coin = loadCoinFromFile(coinId);
  73. } catch (Exception e) {
  74. throw new CoinStoreException(e);
  75. }
  76. new File(storageDir, coinId).delete();
  77. return coin;
  78. }
  79. public synchronized void putCoin(CoinMsg coin) throws CoinStoreException {
  80. if(!coinIds.containsKey(coin.getDenom()))
  81. coinIds.put(coin.getDenom(), new LinkedList<String>());
  82. coinIds.get(coin.getDenom()).add(coin.getCoinId().toString());
  83. try {
  84. saveCoinToFile(coin);
  85. } catch (Exception e) {
  86. throw new CoinStoreException(e);
  87. }
  88. }
  89. private void saveCoinToFile(CoinMsg coin) throws IOException, GeneralSecurityException {
  90. byte[] encArr = encryptCipher.doFinal(coin.toByteArray());
  91. File file = new File(storageDir, coin.getCoinId().toString());
  92. FileOutputStream fos = new FileOutputStream(file);
  93. fos.write(encArr);
  94. fos.close();
  95. }
  96. private CoinMsg loadCoinFromFile(String coinId) throws IOException, GeneralSecurityException {
  97. File file = new File(storageDir, coinId);
  98. FileInputStream fis = new FileInputStream(file);
  99. byte[] readArr = new byte[1024];
  100. ByteArrayOutputStream baos = new ByteArrayOutputStream();
  101. int numRead;
  102. while((numRead = fis.read(readArr)) > 0) {
  103. baos.write(readArr, 0, numRead);
  104. }
  105. fis.close();
  106. byte[] plainArr = decryptCipher.doFinal(baos.toByteArray());
  107. return CoinMsg.parseFrom(plainArr);
  108. }
  109. private byte[] generateKey(String password, int keyLength) {
  110. MessageDigest m;
  111. try {
  112. m = MessageDigest.getInstance("sha-256");
  113. } catch (NoSuchAlgorithmException e) {
  114. throw new RuntimeException();
  115. }
  116. m.update(password.getBytes());
  117. byte[] key = new byte[keyLength];
  118. System.arraycopy(m.digest(), 0, key, 0, keyLength);
  119. return key;
  120. }
  121. private double getDenomValue(Integer denom) {
  122. return Math.pow(2, denom);
  123. }
  124. }