From 2a4cef68f865cc25283960cc501936440f9f1656 Mon Sep 17 00:00:00 2001 From: Kevin Froman Date: Wed, 18 Jul 2018 02:33:23 -0500 Subject: [PATCH] * onionrblockapi supports pubkey encryption now * can now read messages in mail plugin --- onionr/core.py | 14 +++-- onionr/onionrblockapi.py | 44 ++++++++++++++-- .../static-data/default-plugins/pms/main.py | 52 ++++++++++++++++--- 3 files changed, 94 insertions(+), 16 deletions(-) diff --git a/onionr/core.py b/onionr/core.py index 6798bb12..dd9a48e0 100644 --- a/onionr/core.py +++ b/onionr/core.py @@ -627,13 +627,16 @@ class Core: return None - def getBlocksByType(self, blockType): + def getBlocksByType(self, blockType, orderDate=True): ''' Returns a list of blocks by the type ''' conn = sqlite3.connect(self.blockDB) c = conn.cursor() - execute = 'SELECT hash FROM hashes WHERE dataType=?;' + if orderDate: + execute = 'SELECT hash FROM hashes WHERE dataType=? ORDER BY dateReceived;' + else: + execute = 'SELECT hash FROM hashes WHERE dataType=?;' args = (blockType,) rows = list() for row in c.execute(execute, args): @@ -733,9 +736,10 @@ class Core: signer = self._crypto.symmetricEncrypt(signer, key=symKey, returnEncoded=True).decode() elif encryptType == 'asym': if self._utils.validatePubKey(asymPeer): - jsonMeta = self._crypto.pubKeyEncrypt(jsonMeta, asymPeer, encodedData=True).decode() - data = self._crypto.pubKeyEncrypt(data, asymPeer, encodedData=True).decode() - signature = self._crypto.pubKeyEncrypt(signature, asymPeer, encodedData=True).decode() + jsonMeta = self._crypto.pubKeyEncrypt(jsonMeta, asymPeer, encodedData=True, anonymous=True).decode() + data = self._crypto.pubKeyEncrypt(data, asymPeer, encodedData=True, anonymous=True).decode() + signature = self._crypto.pubKeyEncrypt(signature, asymPeer, encodedData=True, anonymous=True).decode() + signer = self._crypto.pubKeyEncrypt(signer, asymPeer, encodedData=True, anonymous=True).decode() else: raise onionrexceptions.InvalidPubkey(asymPeer + ' is not a valid base32 encoded ed25519 key') diff --git a/onionr/onionrblockapi.py b/onionr/onionrblockapi.py index 8993cbac..74326e9b 100644 --- a/onionr/onionrblockapi.py +++ b/onionr/onionrblockapi.py @@ -18,7 +18,7 @@ along with this program. If not, see . ''' -import core as onionrcore, logger, config, onionrexceptions +import core as onionrcore, logger, config, onionrexceptions, nacl.exceptions import json, os, sys, datetime, base64 class Block: @@ -50,6 +50,9 @@ class Block: self.bheader = {} self.bmetadata = {} self.isEncrypted = False + self.decrypted = False + self.signer = None + self.validSig = False # handle arguments if self.getCore() is None: @@ -69,11 +72,39 @@ class Block: def decrypt(self, anonymous=True, encodedData=True): '''Decrypt a block, loading decrypted data into their vars''' - + if self.decrypted: + return True + retData = False + core = self.getCore() # decrypt data - self.getCore()._crypto.pubKeyDecrypt(self.bcontent, anonymous=anonymous, encodedData=encodedData) + if self.getHeader('encryptType') == 'asym': + try: + self.bcontent = core._crypto.pubKeyDecrypt(self.bcontent, anonymous=anonymous, encodedData=encodedData) + self.bmetadata = json.loads(core._crypto.pubKeyDecrypt(self.bmetadata, anonymous=anonymous, encodedData=encodedData)) + self.signature = core._crypto.pubKeyDecrypt(self.signature, anonymous=anonymous, encodedData=encodedData) + self.signer = core._crypto.pubKeyDecrypt(self.signer, anonymous=anonymous, encodedData=encodedData) + self.signedData = json.dumps(self.bmetadata) + self.bcontent.decode() + except nacl.exceptions.CryptoError: + pass + #logger.debug('Could not decrypt block. Either invalid key or corrupted data') + else: + retData = True + self.decrypted = True + else: + logger.warn('symmetric decryption is not yet supported by this API') + return retData + + def verifySig(self): + '''Verify if a block's signature is signed by its claimed signer''' + core = self.getCore() + + if core._crypto.edVerify(data=self.signedData, key=self.signer, sig=self.signature, encodedData=True): + self.validSig = True + print('ded') + else: + self.validSig = False + return self.validSig - return def update(self, data = None, file = None): ''' @@ -133,8 +164,11 @@ class Block: self.parent = self.getMetadata('parent', None) self.btype = self.getMetadata('type', None) self.signed = ('sig' in self.getHeader() and self.getHeader('sig') != '') + # TODO: detect if signer is hash of pubkey or not + self.signer = self.getHeader('signer', None) self.signature = self.getHeader('sig', None) - self.signedData = (None if not self.isSigned() else self.getHeader('meta') + '\n' + self.getContent()) + # signed data is jsonMeta + block content (no linebreak) + self.signedData = (None if not self.isSigned() else self.getHeader('meta') + self.getContent()) self.date = self.getCore().getBlockDate(self.getHash()) if not self.getDate() is None: diff --git a/onionr/static-data/default-plugins/pms/main.py b/onionr/static-data/default-plugins/pms/main.py index 5d960735..f4a6ad5a 100644 --- a/onionr/static-data/default-plugins/pms/main.py +++ b/onionr/static-data/default-plugins/pms/main.py @@ -19,7 +19,7 @@ ''' # Imports some useful libraries -import logger, config, threading, time, readline +import logger, config, threading, time, readline, datetime from onionrblockapi import Block import onionrexceptions @@ -51,13 +51,53 @@ class OnionrMail: def inbox(self): blockCount = 0 pmBlockMap = {} + pmBlocks = {} + logger.info('Decrypting messages...') + choice = '' - print('Private Messages:') - + # this could use a lot of memory if someone has recieved a lot of messages for blockHash in self.myCore.getBlocksByType('pm'): - blockCount += 1 - pmBlockMap[blockCount] = blockHash - print('%s: %s' % (blockCount, blockHash)) + pmBlocks[blockHash] = Block(blockHash, core=self.myCore) + pmBlocks[blockHash].decrypt() + + while choice not in ('-q', 'q', 'quit'): + blockCount = 0 + for blockHash in pmBlocks: + if not pmBlocks[blockHash].decrypted: + continue + blockCount += 1 + pmBlockMap[blockCount] = blockHash + blockDate = pmBlocks[blockHash].getDate().strftime("%m/%d %H:%M") + print('%s. %s: %s' % (blockCount, blockDate, blockHash)) + + try: + choice = logger.readline('Enter a block number, -r to refresh, or -q to stop: ').strip().lower() + except (EOFError, KeyboardInterrupt): + choice = '-q' + + if choice in ('-q', 'q', 'quit'): + continue + + if choice in ('-r', 'r', 'refresh'): + # dirty hack + self.inbox() + return + + try: + choice = int(choice) + except ValueError: + pass + else: + try: + pmBlockMap[choice] + readBlock = pmBlocks[pmBlockMap[choice]] + except KeyError: + pass + else: + readBlock.verifySig() + print('Message recieved from', readBlock.signer) + print('Valid signature:', readBlock.validSig) + print(self.myCore._utils.escapeAnsi(readBlock.bcontent.decode())) return