diff --git a/.travis.yml b/.travis.yml index e1cee1fc..603021b5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,6 +3,6 @@ python: - "3.6.4" # install dependencies install: - - sudo apt install gnupg tor + - sudo apt install tor - pip install -r requirements.txt script: make test diff --git a/Makefile b/Makefile index 10ab5390..eb2d27ac 100644 --- a/Makefile +++ b/Makefile @@ -18,7 +18,7 @@ uninstall: test: @rm -rf onionr/data-backup @mv onionr/data onionr/data-backup | true > /dev/null 2>&1 - -@cd onionr; ./tests.py + -@cd onionr; ./tests.py; ./cryptotests.py; @rm -rf onionr/data @mv onionr/data-backup onionr/data | true > /dev/null 2>&1 diff --git a/docs/onionr-logo.png b/docs/onionr-logo.png index dade75ca..4ecf3293 100644 Binary files a/docs/onionr-logo.png and b/docs/onionr-logo.png differ diff --git a/onionr/communicator.py b/onionr/communicator.py index d42b70a4..b10317fc 100755 --- a/onionr/communicator.py +++ b/onionr/communicator.py @@ -33,6 +33,8 @@ class OnionrCommunicate: self._core = core.Core() self._utils = onionrutils.OnionrUtils(self._core) self._crypto = onionrcrypto.OnionrCrypto(self._core) + + self.highFailureAmount = 7 ''' logger.info('Starting Bitcoin Node... with Tor socks port:' + str(sys.argv[2])) try: @@ -47,6 +49,8 @@ class OnionrCommunicate: blockProcessTimer = 0 blockProcessAmount = 5 + highFailureTimer = 0 + highFailureRate = 10 heartBeatTimer = 0 heartBeatRate = 5 pexTimer = 5 # How often we should check for new peers @@ -68,6 +72,11 @@ class OnionrCommunicate: blockProcessTimer += 1 heartBeatTimer += 1 pexCount += 1 + if highFailureTimer == highFailureRate: + highFailureTimer = 0 + for i in self.peerData: + if self.peerData[i]['failCount'] == self.highFailureAmount: + self.peerData[i]['failCount'] -= 1 if pexTimer == pexCount: self.getNewPeers() pexCount = 0 @@ -236,7 +245,7 @@ class OnionrCommunicate: if data != None: url = url + '&data=' + self.urlencode(data) try: - if skipHighFailureAddress and self.peerData[peer]['failCount'] > 10: + if skipHighFailureAddress and self.peerData[peer]['failCount'] > self.highFailureAmount: retData = False logger.debug('Skipping ' + peer + ' because of high failure rate') else: @@ -251,6 +260,7 @@ class OnionrCommunicate: self.peerData[peer]['failCount'] += 1 else: self.peerData[peer]['connectCount'] += 1 + self.peerData[peer]['failCount'] -= 1 self.peerData[peer]['lastConnectTime'] = math.floor(time.time()) return retData diff --git a/onionr/core.py b/onionr/core.py index ef91549c..1b519985 100644 --- a/onionr/core.py +++ b/onionr/core.py @@ -369,16 +369,17 @@ class Core: id text 0 name text, 1 - adders text, 2 - forwardKey text, 3 - dateSeen not null, 4 - bytesStored int, 5 - trust int 6 + pubkey text, 2 + adders text, 3 + forwardKey text, 4 + dateSeen not null, 5 + bytesStored int, 6 + trust int 7 ''' conn = sqlite3.connect(self.peerDB) c = conn.cursor() command = (peer,) - infoNumbers = {'id': 0, 'name': 1, 'adders': 2, 'forwardKey': 3, 'dateSeen': 4, 'bytesStored': 5, 'trust': 6} + infoNumbers = {'id': 0, 'name': 1, 'pubkey': 2, 'adders': 3, 'forwardKey': 4, 'dateSeen': 5, 'bytesStored': 6, 'trust': 7} info = infoNumbers[info] iterCount = 0 retVal = '' diff --git a/onionr/onionrcrypto.py b/onionr/onionrcrypto.py index 4000e272..251d1502 100644 --- a/onionr/onionrcrypto.py +++ b/onionr/onionrcrypto.py @@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . ''' -import nacl.signing, nacl.encoding, nacl.public, os +import nacl.signing, nacl.encoding, nacl.public, nacl.secret, os, binascii, base64 class OnionrCrypto: def __init__(self, coreInstance): @@ -60,36 +60,112 @@ class OnionrCrypto: retData = key.sign(data.encode()) return retData - def pubKeyEncrypt(self, data, pubkey, anonymous=False): + def pubKeyEncrypt(self, data, pubkey, anonymous=False, encodedData=False): '''Encrypt to a public key (Curve25519, taken from base32 Ed25519 pubkey)''' retVal = '' + + if encodedData: + encoding = nacl.encoding.Base64Encoder + else: + encoding = nacl.encoding.RawEncoder + if self.privKey != None and not anonymous: ownKey = nacl.signing.SigningKey(seed=self.privKey, encoder=nacl.encoding.Base32Encoder()) key = nacl.signing.VerifyKey(key=pubkey, encoder=nacl.encoding.Base32Encoder).to_curve25519_public_key() ourBox = nacl.public.Box(ownKey, key) - retVal = ourBox.encrypt(data.encode(), encoder=nacl.encoding.RawEncoder) + retVal = ourBox.encrypt(data.encode(), encoder=encoding) elif anonymous: key = nacl.signing.VerifyKey(key=pubkey, encoder=nacl.encoding.Base32Encoder).to_curve25519_public_key() anonBox = nacl.public.SealedBox(key) - retVal = anonBox.encrypt(data.encode(), encoder=nacl.encoding.RawEncoder) + retVal = anonBox.encrypt(data.encode(), encoder=encoding) return retVal - def pubKeyDecrypt(self, data, peer): + def pubKeyDecrypt(self, data, pubkey, anonymous=False, encodedData=False): '''pubkey decrypt (Curve25519, taken from Ed25519 pubkey)''' - return + retVal = '' + if encodedData: + encoding = nacl.encoding.Base64Encoder + else: + encoding = nacl.encoding.RawEncoder + ownKey = nacl.signing.SigningKey(seed=self.privKey, encoder=nacl.encoding.Base32Encoder()) + if self.privKey != None and not anoymous: + ourBox = nacl.public.Box(ownKey, pubkey) + decrypted = ourBox.decrypt(data, encoder=encoding) + elif anonymous: + anonBox = nacl.public.SealedBox(ownKey) + decrypted = anonBox.decrypt(data.encode(), encoder=encoding) + return decrypted - def symmetricPeerEncrypt(self, data): - '''Salsa20 encrypt data to peer (with mac)''' - return + def symmetricPeerEncrypt(self, data, peer): + '''Salsa20 encrypt data to peer (with mac) + this function does not accept a key, it is a wrapper for encryption with a peer + ''' + key = self._core.getPeerInfo(4) + if type(key) != bytes: + key = self._core.getPeerInfo(2) + encrypted = self.symmetricEncrypt(data, key, encodedKey=True) + return encrypted def symmetricPeerDecrypt(self, data, peer): - '''Salsa20 decrypt data from peer (with mac)''' + '''Salsa20 decrypt data from peer (with mac) + this function does not accept a key, it is a wrapper for encryption with a peer + ''' + key = self._core.getPeerInfo(4) + if type(key) != bytes: + key = self._core.getPeerInfo(2) + decrypted = self.symmetricDecrypt(data, key, encodedKey=True) + return decrypted + return + + def symmetricEncrypt(self, data, key, encodedKey=False, returnEncoded=True): + '''Encrypt data to a 32-byte key (Salsa20-Poly1305 MAC)''' + if encodedKey: + encoding = nacl.encoding.Base64Encoder + else: + encoding = nacl.encoding.RawEncoder + + # Make sure data is bytes + if type(data) != bytes: + data = data.encode() + + box = nacl.secret.SecretBox(key, encoder=encoding) + + if returnEncoded: + encoding = nacl.encoding.Base64Encoder + else: + encoding = nacl.encoding.RawEncoder + + encrypted = box.encrypt(data, encoder=encoding) + return encrypted + + def symmetricDecrypt(self, data, key, encodedKey=False, encodedMessage=False, returnEncoded=False): + '''Decrypt data to a 32-byte key (Salsa20-Poly1305 MAC)''' + if encodedKey: + encoding = nacl.encoding.Base64Encoder + else: + encoding = nacl.encoding.RawEncoder + box = nacl.secret.SecretBox(key, encoder=encoding) + + if encodedMessage: + encoding = nacl.encoding.Base64Encoder + else: + encoding = nacl.encoding.RawEncoder + decrypted = box.decrypt(data, encoder=encoding) + if returnEncoded: + decrypted = base64.b64encode(decrypted) + return decrypted - def generateSymmetric(self, data, peer): - '''Generate symmetric key''' + def generateSymmetricPeer(self, peer): + '''Generate symmetric key for a peer and save it to the peer database''' + key = self.generateSymmetric() + self._core.setPeerInfo(peer, 'forwardKey', key) return + def generateSymmetric(self): + '''Generate a symmetric key (bytes) and return it''' + return binascii.hexlify(nacl.utils.random(nacl.secret.SecretBox.KEY_SIZE)) + def generatePubKey(self): '''Generate a Ed25519 public key pair, return tuple of base64encoded pubkey, privkey''' private_key = nacl.signing.SigningKey.generate()