Skip to content

Commit

Permalink
Migrate storage over to Graphenelib
Browse files Browse the repository at this point in the history
  • Loading branch information
xeroc committed Aug 6, 2018
1 parent 090dc19 commit cb969ac
Show file tree
Hide file tree
Showing 14 changed files with 922 additions and 3 deletions.
48 changes: 48 additions & 0 deletions graphenebase/aes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
try:
from Cryptodome.Cipher import AES
from Cryptodome import Random
except ImportError:
try:
from Crypto.Cipher import AES
from Crypto import Random
except ImportError:
raise ImportError("Missing dependency: pyCryptodome")
import hashlib
import base64


class AESCipher(object):
"""
A classical AES Cipher. Can use any size of data and any size of password thanks to padding.
Also ensure the coherence and the type of the data with a unicode to byte converter.
"""
def __init__(self, key):
self.bs = 32
self.key = hashlib.sha256(AESCipher.str_to_bytes(key)).digest()

@staticmethod
def str_to_bytes(data):
u_type = type(b''.decode('utf8'))
if isinstance(data, u_type):
return data.encode('utf8')
return data

def _pad(self, s):
return s + (self.bs - len(s) % self.bs) * AESCipher.str_to_bytes(chr(self.bs - len(s) % self.bs))

@staticmethod
def _unpad(s):
return s[:-ord(s[len(s) - 1:])]

def encrypt(self, raw):
raw = self._pad(AESCipher.str_to_bytes(raw))
iv = Random.new().read(AES.block_size)
cipher = AES.new(self.key, AES.MODE_CBC, iv)
return base64.b64encode(iv + cipher.encrypt(raw)).decode('utf-8')

def decrypt(self, enc):
enc = base64.b64decode(enc)
iv = enc[:AES.block_size]
cipher = AES.new(self.key, AES.MODE_CBC, iv)
return self._unpad(cipher.decrypt(enc[AES.block_size:])).decode('utf-8')

19 changes: 19 additions & 0 deletions graphenestorage/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from .base import (
InRamConfigurationStore,
InRamPlainKeyStore,
InRamEncryptedKeyStore,
SqliteConfigurationStore,
SqlitePlainKeyStore,
SqliteEncryptedKeyStore
)
from .sqlite import SQLiteFile

__all__ = [
"base",
"sqlite",
"ram"
]


def get_default_config_store():
return SqliteConfigurationStore()
139 changes: 139 additions & 0 deletions graphenestorage/base.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
import os
import logging

from .masterpassword import MasterPassword
from .interfaces import KeyInterface, ConfigInterface
from .ram import InRamStore
from .sqlite import SQLiteStore

log = logging.getLogger(__name__)


# Configuration
class InRamConfigurationStore(InRamStore, ConfigInterface):
""" A simple example that shows how to set defaults
for the Base Store
"""
pass


class SqliteConfigurationStore(SQLiteStore, ConfigInterface):
""" This is the configuration storage that stores key/value
pairs in the `config` table of the SQLite3 database.
"""
__tablename__ = "config"
__key__ = "key"
__value__ = "value"


# Keys
class InRamPlainKeyStore(InRamStore, KeyInterface):
""" A simple Store that stores keys unencrypted in RAM
"""
def getPublicKeys(self):
return [k for k, v in self.items()]

def getPrivateKeyForPublicKey(self, pub):
return self.get(str(pub), None)

def add(self, wif, pub):
self[str(pub)] = str(wif)

def delete(self, pub):
InRamStore.delete(self, str(pub))


class InRamEncryptedKeyStore(MasterPassword, InRamStore, KeyInterface):

# Interface to deal with encrypted keys
def getPublicKeys(self):
return [k for k, v in self.items()]

def getPrivateKeyForPublicKey(self, pub):
assert self.unlocked()
wif = self.get(str(pub), None)
if wif:
return self.decrypt(wif) # From Masterpassword

def add(self, wif, pub):
assert self.unlocked() # From Masterpassword
self[str(pub)] = self.encrypt(str(wif)) # From Masterpassword

def delete(self, pub):
InRamStore.delete(self, str(pub))

def is_encrypted(self):
return True

def locked(self):
return MasterPassword.locked(self)

def unlock(self, password):
return MasterPassword.unlock(self, password)

def lock(self):
return MasterPassword.lock(self)


class SqlitePlainKeyStore(SQLiteStore, KeyInterface):
""" This is the key storage that stores the public key and the
**unencrypted** private key in the `keys` table in the SQLite3
database.
"""
__tablename__ = 'keys'
__key__ = "pub"
__value__ = "wif"

def getPublicKeys(self):
return [k for k, v in self.items()]

def getPrivateKeyForPublicKey(self, pub):
return self[pub]

def add(self, wif, pub=None):
self[str(pub)] = str(wif)

def delete(self, pub):
SQLiteStore.delete(self, str(pub))


class SqliteEncryptedKeyStore(MasterPassword, SQLiteStore, KeyInterface):
""" This is the key storage that stores the public key and the
**encrypted** private key in the `keys` table in the SQLite3 database.
"""
__tablename__ = 'keys'
__key__ = "pub"
__value__ = "wif"

def __init__(self, *args, **kwargs):
SQLiteStore.__init__(self, *args, **kwargs)
MasterPassword.__init__(self, *args, **kwargs)

# Interface to deal with encrypted keys
def getPublicKeys(self):
return [k for k, v in self.items()]

def getPrivateKeyForPublicKey(self, pub):
assert self.unlocked()
wif = self.get(str(pub), None)
if wif:
return self.decrypt(wif) # From Masterpassword

def add(self, wif, pub):
assert self.unlocked() # From Masterpassword
self[str(pub)] = self.encrypt(str(wif)) # From Masterpassword

def delete(self, pub):
SQLiteStore.delete(self, str(pub))

def is_encrypted(self):
return True

def locked(self):
return MasterPassword.locked(self)

def unlock(self, password):
return MasterPassword.unlock(self, password)

def lock(self):
return MasterPassword.lock(self)
4 changes: 4 additions & 0 deletions graphenestorage/exceptions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
class WrongMasterPasswordException(Exception):
""" The password provided could not properly unlock the wallet
"""
pass
115 changes: 115 additions & 0 deletions graphenestorage/interfaces.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
class StoreInterface:

defaults = {}

def __init__(self, *args, **kwargs):
pass

def __setitem__(self, key, value):
""" Sets an item in the store
"""
pass

def __getitem__(self, key):
""" Gets an item from the store as if it was a dictionary
"""
pass

def __iter__(self):
""" Iterates through the store
"""
pass

def __len__(self):
""" return lenght of store
"""
pass

def __contains__(self, key):
""" Tests if a key is contained in the store.
"""
pass

def items(self):
""" returns all items off the store as tuples
"""
pass

def get(self, key, default=None):
""" Return the key if exists or a default value
"""
pass

# Specific for this library
def delete(self, key):
""" Delete a key from the store
"""
pass

def wipe(self):
""" Wipe the store
"""
pass


class KeyInterface(StoreInterface):
""" The BaseKeyStore defines the interface for key storage
"""

# Interface to deal with encrypted keys
def getPublicKeys(self):
""" Returns the public keys stored in the database
"""
pass

def getPrivateKeyForPublicKey(self, pub):
""" Returns the (possibly encrypted) private key that
corresponds to a public key
:param str pub: Public key
The encryption scheme is BIP38
"""
pass

def add(self, wif, pub=None):
""" Add a new public/private key pair (correspondence has to be
checked elsewhere!)
:param str pub: Public key
:param str wif: Private key
"""
pass

def delete(self, pub):
""" Delete a pubkey/privatekey pair from the store
"""
pass

def is_encrypted(self):
""" Returns True/False to indicate required use of unlock
"""
return False

def unlock(self, password):
""" Tries to unlock the wallet if required
"""
pass

def locked(self):
""" is the wallet locked?
"""
return False

def lock(self):
""" Lock the wallet again
"""
pass


class ConfigInterface(StoreInterface):
""" The BaseKeyStore defines the interface for key storage
Inherits StoreInterface. Not additional methods required.
"""
pass
Loading

0 comments on commit cb969ac

Please sign in to comment.