1800 lines
67 KiB
HTML
1800 lines
67 KiB
HTML
<!doctype html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1" />
|
|
<meta name="generator" content="pdoc 0.7.4" />
|
|
<title>src.onionrblocks.onionrblockapi API documentation</title>
|
|
<meta name="description" content="Onionr - P2P Anonymous Storage Network …" />
|
|
<link href='https://cdnjs.cloudflare.com/ajax/libs/normalize/8.0.0/normalize.min.css' rel='stylesheet'>
|
|
<link href='https://cdnjs.cloudflare.com/ajax/libs/10up-sanitize.css/8.0.0/sanitize.min.css' rel='stylesheet'>
|
|
<link href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/styles/github.min.css" rel="stylesheet">
|
|
<style>.flex{display:flex !important}body{line-height:1.5em}#content{padding:20px}#sidebar{padding:30px;overflow:hidden}.http-server-breadcrumbs{font-size:130%;margin:0 0 15px 0}#footer{font-size:.75em;padding:5px 30px;border-top:1px solid #ddd;text-align:right}#footer p{margin:0 0 0 1em;display:inline-block}#footer p:last-child{margin-right:30px}h1,h2,h3,h4,h5{font-weight:300}h1{font-size:2.5em;line-height:1.1em}h2{font-size:1.75em;margin:1em 0 .50em 0}h3{font-size:1.4em;margin:25px 0 10px 0}h4{margin:0;font-size:105%}a{color:#058;text-decoration:none;transition:color .3s ease-in-out}a:hover{color:#e82}.title code{font-weight:bold}h2[id^="header-"]{margin-top:2em}.ident{color:#900}pre code{background:#f8f8f8;font-size:.8em;line-height:1.4em}code{background:#f2f2f1;padding:1px 4px;overflow-wrap:break-word}h1 code{background:transparent}pre{background:#f8f8f8;border:0;border-top:1px solid #ccc;border-bottom:1px solid #ccc;margin:1em 0;padding:1ex}#http-server-module-list{display:flex;flex-flow:column}#http-server-module-list div{display:flex}#http-server-module-list dt{min-width:10%}#http-server-module-list p{margin-top:0}.toc ul,#index{list-style-type:none;margin:0;padding:0}#index code{background:transparent}#index h3{border-bottom:1px solid #ddd}#index ul{padding:0}#index h4{font-weight:bold}#index h4 + ul{margin-bottom:.6em}@media (min-width:200ex){#index .two-column{column-count:2}}@media (min-width:300ex){#index .two-column{column-count:3}}dl{margin-bottom:2em}dl dl:last-child{margin-bottom:4em}dd{margin:0 0 1em 3em}#header-classes + dl > dd{margin-bottom:3em}dd dd{margin-left:2em}dd p{margin:10px 0}.name{background:#eee;font-weight:bold;font-size:.85em;padding:5px 10px;display:inline-block;min-width:40%}.name:hover{background:#e0e0e0}.name > span:first-child{white-space:nowrap}.name.class > span:nth-child(2){margin-left:.4em}.inherited{color:#999;border-left:5px solid #eee;padding-left:1em}.inheritance em{font-style:normal;font-weight:bold}.desc h2{font-weight:400;font-size:1.25em}.desc h3{font-size:1em}.desc dt code{background:inherit}.source summary,.git-link-div{color:#666;text-align:right;font-weight:400;font-size:.8em;text-transform:uppercase}.source summary > *{white-space:nowrap;cursor:pointer}.git-link{color:inherit;margin-left:1em}.source pre{max-height:500px;overflow:auto;margin:0}.source pre code{font-size:12px;overflow:visible}.hlist{list-style:none}.hlist li{display:inline}.hlist li:after{content:',\2002'}.hlist li:last-child:after{content:none}.hlist .hlist{display:inline;padding-left:1em}img{max-width:100%}.admonition{padding:.1em .5em;margin-bottom:1em}.admonition-title{font-weight:bold}.admonition.note,.admonition.info,.admonition.important{background:#aef}.admonition.todo,.admonition.versionadded,.admonition.tip,.admonition.hint{background:#dfd}.admonition.warning,.admonition.versionchanged,.admonition.deprecated{background:#fd4}.admonition.error,.admonition.danger,.admonition.caution{background:lightpink}</style>
|
|
<style media="screen and (min-width: 700px)">@media screen and (min-width:700px){#sidebar{width:30%}#content{width:70%;max-width:100ch;padding:3em 4em;border-left:1px solid #ddd}pre code{font-size:1em}.item .name{font-size:1em}main{display:flex;flex-direction:row-reverse;justify-content:flex-end}.toc ul ul,#index ul{padding-left:1.5em}.toc > ul > li{margin-top:.5em}}</style>
|
|
<style media="print">@media print{#sidebar h1{page-break-before:always}.source{display:none}}@media print{*{background:transparent !important;color:#000 !important;box-shadow:none !important;text-shadow:none !important}a[href]:after{content:" (" attr(href) ")";font-size:90%}a[href][title]:after{content:none}abbr[title]:after{content:" (" attr(title) ")"}.ir a:after,a[href^="javascript:"]:after,a[href^="#"]:after{content:""}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100% !important}@page{margin:0.5cm}p,h2,h3{orphans:3;widows:3}h1,h2,h3,h4,h5,h6{page-break-after:avoid}}</style>
|
|
</head>
|
|
<body>
|
|
<main>
|
|
<article id="content">
|
|
<header>
|
|
<h1 class="title">Module <code>src.onionrblocks.onionrblockapi</code></h1>
|
|
</header>
|
|
<section id="section-intro">
|
|
<p>Onionr - P2P Anonymous Storage Network</p>
|
|
<p>This file contains the OnionrBlocks class which is a class for working with Onionr blocks</p>
|
|
<details class="source">
|
|
<summary>
|
|
<span>Expand source code</span>
|
|
</summary>
|
|
<pre><code class="python">'''
|
|
Onionr - P2P Anonymous Storage Network
|
|
|
|
This file contains the OnionrBlocks class which is a class for working with Onionr blocks
|
|
'''
|
|
'''
|
|
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 <https://www.gnu.org/licenses/>.
|
|
'''
|
|
import unpaddedbase32
|
|
import binascii
|
|
import logger, config, onionrexceptions, nacl.exceptions
|
|
import json, os, sys, datetime, base64, onionrstorage
|
|
from onionrusers import onionrusers
|
|
from onionrutils import stringvalidators, epoch
|
|
from coredb import blockmetadb
|
|
from onionrutils import bytesconverter
|
|
from onionrstorage import removeblock
|
|
import onionrblocks
|
|
from onionrcrypto import encryption, cryptoutils as cryptoutils, signing
|
|
class Block:
|
|
blockCacheOrder = list() # NEVER write your own code that writes to this!
|
|
blockCache = dict() # should never be accessed directly, look at Block.getCache()
|
|
|
|
def __init__(self, hash = None, type = None, content = None, expire=None, decrypt=False, bypassReplayCheck=False):
|
|
# take from arguments
|
|
# sometimes people input a bytes object instead of str in `hash`
|
|
if (not hash is None) and isinstance(hash, bytes):
|
|
hash = hash.decode()
|
|
|
|
self.hash = hash
|
|
self.btype = type
|
|
self.bcontent = content
|
|
self.expire = expire
|
|
self.bypassReplayCheck = bypassReplayCheck
|
|
|
|
# initialize variables
|
|
self.valid = True
|
|
self.raw = None
|
|
self.signed = False
|
|
self.signature = None
|
|
self.signedData = None
|
|
self.blockFile = None
|
|
self.bheader = {}
|
|
self.bmetadata = {}
|
|
self.isEncrypted = False
|
|
self.decrypted = False
|
|
self.signer = None
|
|
self.validSig = False
|
|
self.autoDecrypt = decrypt
|
|
|
|
self.update()
|
|
|
|
def decrypt(self, encodedData = True):
|
|
'''
|
|
Decrypt a block, loading decrypted data into their vars
|
|
'''
|
|
|
|
if self.decrypted:
|
|
return True
|
|
retData = False
|
|
# decrypt data
|
|
if self.getHeader('encryptType') == 'asym':
|
|
try:
|
|
try:
|
|
self.bcontent = encryption.pub_key_decrypt(self.bcontent, encodedData=encodedData)
|
|
except (binascii.Error, ValueError) as e:
|
|
self.bcontent = encryption.pub_key_decrypt(self.bcontent, encodedData=False)
|
|
bmeta = encryption.pub_key_decrypt(self.bmetadata, encodedData=encodedData)
|
|
try:
|
|
bmeta = bmeta.decode()
|
|
except AttributeError:
|
|
# yet another bytes fix
|
|
pass
|
|
self.bmetadata = json.loads(bmeta)
|
|
self.signature = encryption.pub_key_decrypt(self.signature, encodedData=encodedData)
|
|
self.signer = encryption.pub_key_decrypt(self.signer, encodedData=encodedData)
|
|
self.bheader['signer'] = self.signer.decode()
|
|
self.signedData = json.dumps(self.bmetadata).encode() + self.bcontent
|
|
|
|
if not self.signer is None:
|
|
if not self.verifySig():
|
|
raise onionrexceptions.SignatureError("Block has invalid signature")
|
|
|
|
# Check for replay attacks
|
|
try:
|
|
if epoch.get_epoch() - blockmetadb.get_block_date(self.hash) > 60:
|
|
if not cryptoutils.replay_validator(self.bmetadata['rply']): raise onionrexceptions.ReplayAttack
|
|
except (AssertionError, KeyError, TypeError, onionrexceptions.ReplayAttack) as e:
|
|
if not self.bypassReplayCheck:
|
|
# Zero out variables to prevent reading of replays
|
|
self.bmetadata = {}
|
|
self.signer = ''
|
|
self.bheader['signer'] = ''
|
|
self.signedData = ''
|
|
self.signature = ''
|
|
raise onionrexceptions.ReplayAttack('Signature is too old. possible replay attack')
|
|
try:
|
|
if not self.bmetadata['forwardEnc']: raise KeyError
|
|
except (AssertionError, KeyError) as e:
|
|
pass
|
|
else:
|
|
try:
|
|
self.bcontent = onionrusers.OnionrUser(self.signer).forwardDecrypt(self.bcontent)
|
|
except (onionrexceptions.DecryptionError, nacl.exceptions.CryptoError) as e:
|
|
#logger.error(str(e))
|
|
pass
|
|
except nacl.exceptions.CryptoError:
|
|
pass
|
|
#logger.debug('Could not decrypt block. Either invalid key or corrupted data')
|
|
except onionrexceptions.ReplayAttack:
|
|
logger.warn('%s is possibly a replay attack' % (self.hash,))
|
|
else:
|
|
retData = True
|
|
self.decrypted = True
|
|
return retData
|
|
|
|
def verifySig(self):
|
|
'''
|
|
Verify if a block's signature is signed by its claimed signer
|
|
'''
|
|
if self.signer is None:
|
|
return False
|
|
if signing.ed_verify(data=self.signedData, key=self.signer, sig=self.signature, encodedData=True):
|
|
self.validSig = True
|
|
else:
|
|
self.validSig = False
|
|
return self.validSig
|
|
|
|
def update(self, data = None, file = None):
|
|
'''
|
|
Loads data from a block in to the current object.
|
|
|
|
Inputs:
|
|
- data (str):
|
|
- if None: will load from file by hash
|
|
- else: will load from `data` string
|
|
- file (str):
|
|
- if None: will load from file specified in this parameter
|
|
- else: will load from wherever block is stored by hash
|
|
|
|
Outputs:
|
|
- (bool): indicates whether or not the operation was successful
|
|
'''
|
|
try:
|
|
# import from string
|
|
blockdata = data
|
|
|
|
# import from file
|
|
if blockdata is None:
|
|
try:
|
|
blockdata = onionrstorage.getData(self.getHash())#.decode()
|
|
except AttributeError:
|
|
raise onionrexceptions.NoDataAvailable('Block does not exist')
|
|
else:
|
|
self.blockFile = None
|
|
# parse block
|
|
self.raw = blockdata
|
|
self.bheader = json.loads(self.getRaw()[:self.getRaw().index(b'\n')])
|
|
self.bcontent = self.getRaw()[self.getRaw().index(b'\n') + 1:]
|
|
if ('encryptType' in self.bheader) and (self.bheader['encryptType'] in ('asym', 'sym')):
|
|
self.bmetadata = self.getHeader('meta', None)
|
|
self.isEncrypted = True
|
|
else:
|
|
self.bmetadata = json.loads(self.getHeader('meta', 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)
|
|
# signed data is jsonMeta + block content (no linebreak)
|
|
self.signedData = (None if not self.isSigned() else self.getHeader('meta').encode() + self.getContent())
|
|
self.date = blockmetadb.get_block_date(self.getHash())
|
|
self.claimedTime = self.getHeader('time', None)
|
|
|
|
if not self.getDate() is None:
|
|
self.date = datetime.datetime.fromtimestamp(self.getDate())
|
|
|
|
self.valid = True
|
|
|
|
if self.autoDecrypt:
|
|
self.decrypt()
|
|
|
|
return True
|
|
except Exception as e:
|
|
logger.warn('Failed to parse block %s' % self.getHash(), error = e, timestamp = False)
|
|
|
|
# if block can't be parsed, it's a waste of precious space. Throw it away.
|
|
if not self.delete():
|
|
logger.warn('Failed to delete invalid block %s.' % self.getHash(), error = e)
|
|
else:
|
|
logger.debug('Deleted invalid block %s.' % self.getHash(), timestamp = False)
|
|
|
|
self.valid = False
|
|
return False
|
|
|
|
def delete(self):
|
|
'''
|
|
Deletes the block's file and records, if they exist
|
|
|
|
Outputs:
|
|
- (bool): whether or not the operation was successful
|
|
'''
|
|
|
|
if self.exists():
|
|
try:
|
|
os.remove(self.getBlockFile())
|
|
except TypeError:
|
|
pass
|
|
b_hash = self.getHash()
|
|
onionrstorage.deleteBlock(b_hash)
|
|
removeblock.remove_block(b_hash)
|
|
return True
|
|
return False
|
|
|
|
def save(self, sign = False, recreate = True):
|
|
'''
|
|
Saves a block to file and imports it into Onionr
|
|
|
|
Inputs:
|
|
- sign (bool): whether or not to sign the block before saving
|
|
- recreate (bool): if the block already exists, whether or not to recreate the block and save under a new hash
|
|
|
|
Outputs:
|
|
- (bool): whether or not the operation was successful
|
|
'''
|
|
|
|
try:
|
|
if self.isValid() is True:
|
|
|
|
self.hash = onionrblocks.insert(self.getRaw(), header = self.getType(), sign = sign, meta = self.getMetadata(), expire = self.getExpire())
|
|
if self.hash != False:
|
|
self.update()
|
|
|
|
return self.getHash()
|
|
else:
|
|
logger.warn('Not writing block; it is invalid.')
|
|
except Exception as e:
|
|
logger.error('Failed to save block.', error = e, timestamp = False)
|
|
|
|
return False
|
|
|
|
# getters
|
|
|
|
def getExpire(self):
|
|
'''
|
|
Returns the expire time for a block
|
|
|
|
Outputs:
|
|
- (int): the expire time for a block, or None
|
|
'''
|
|
return self.expire
|
|
|
|
def getHash(self):
|
|
'''
|
|
Returns the hash of the block if saved to file
|
|
|
|
Outputs:
|
|
- (str): the hash of the block, or None
|
|
'''
|
|
|
|
return self.hash
|
|
|
|
def getType(self):
|
|
'''
|
|
Returns the type of the block
|
|
|
|
Outputs:
|
|
- (str): the type of the block
|
|
'''
|
|
return self.btype
|
|
|
|
def getRaw(self):
|
|
'''
|
|
Returns the raw contents of the block, if saved to file
|
|
|
|
Outputs:
|
|
- (bytes): the raw contents of the block, or None
|
|
'''
|
|
|
|
return self.raw
|
|
|
|
def getHeader(self, key = None, default = None):
|
|
'''
|
|
Returns the header information
|
|
|
|
Inputs:
|
|
- key (str): only returns the value of the key in the header
|
|
|
|
Outputs:
|
|
- (dict/str): either the whole header as a dict, or one value
|
|
'''
|
|
|
|
if not key is None:
|
|
if key in self.getHeader():
|
|
return self.getHeader()[key]
|
|
return default
|
|
return self.bheader
|
|
|
|
def getMetadata(self, key = None, default = None):
|
|
'''
|
|
Returns the metadata information
|
|
|
|
Inputs:
|
|
- key (str): only returns the value of the key in the metadata
|
|
|
|
Outputs:
|
|
- (dict/str): either the whole metadata as a dict, or one value
|
|
'''
|
|
|
|
if not key is None:
|
|
if key in self.getMetadata():
|
|
return self.getMetadata()[key]
|
|
return default
|
|
return self.bmetadata
|
|
|
|
def getContent(self):
|
|
'''
|
|
Returns the contents of the block
|
|
|
|
Outputs:
|
|
- (str): the contents of the block
|
|
'''
|
|
|
|
return self.bcontent
|
|
|
|
def getDate(self):
|
|
'''
|
|
Returns the date that the block was received, if loaded from file
|
|
|
|
Outputs:
|
|
- (datetime): the date that the block was received
|
|
'''
|
|
|
|
return self.date
|
|
|
|
def getBlockFile(self):
|
|
'''
|
|
Returns the location of the block file if it is saved
|
|
|
|
Outputs:
|
|
- (str): the location of the block file, or None
|
|
'''
|
|
|
|
return self.blockFile
|
|
|
|
def isValid(self):
|
|
'''
|
|
Checks if the block is valid
|
|
|
|
Outputs:
|
|
- (bool): whether or not the block is valid
|
|
'''
|
|
|
|
return self.valid
|
|
|
|
def isSigned(self):
|
|
'''
|
|
Checks if the block was signed
|
|
|
|
Outputs:
|
|
- (bool): whether or not the block is signed
|
|
'''
|
|
|
|
return self.signed
|
|
|
|
def getSignature(self):
|
|
'''
|
|
Returns the base64-encoded signature
|
|
|
|
Outputs:
|
|
- (str): the signature, or None
|
|
'''
|
|
|
|
return self.signature
|
|
|
|
def getSignedData(self):
|
|
'''
|
|
Returns the data that was signed
|
|
|
|
Outputs:
|
|
- (str): the data that was signed, or None
|
|
'''
|
|
|
|
return self.signedData
|
|
|
|
def isSigner(self, signer, encodedData = True):
|
|
'''
|
|
Checks if the block was signed by the signer inputted
|
|
|
|
Inputs:
|
|
- signer (str): the public key of the signer to check against
|
|
- encodedData (bool): whether or not the `signer` argument is base64 encoded
|
|
|
|
Outputs:
|
|
- (bool): whether or not the signer of the block is the signer inputted
|
|
'''
|
|
signer = unpaddedbase32.repad(bytesconverter.str_to_bytes(signer))
|
|
try:
|
|
if (not self.isSigned()) or (not stringvalidators.validate_pub_key(signer)):
|
|
return False
|
|
|
|
return bool(signing.ed_verify(self.getSignedData(), signer, self.getSignature(), encodedData = encodedData))
|
|
except:
|
|
return False
|
|
|
|
# setters
|
|
|
|
def setType(self, btype):
|
|
'''
|
|
Sets the type of the block
|
|
|
|
Inputs:
|
|
- btype (str): the type of block to be set to
|
|
|
|
Outputs:
|
|
- (Block): the Block instance
|
|
'''
|
|
|
|
self.btype = btype
|
|
return self
|
|
|
|
def setMetadata(self, key, val):
|
|
'''
|
|
Sets a custom metadata value
|
|
|
|
Metadata should not store block-specific data structures.
|
|
|
|
Inputs:
|
|
- key (str): the key
|
|
- val: the value (type is irrelevant)
|
|
|
|
Outputs:
|
|
- (Block): the Block instance
|
|
'''
|
|
|
|
self.bmetadata[key] = val
|
|
return self
|
|
|
|
def setContent(self, bcontent):
|
|
'''
|
|
Sets the contents of the block
|
|
|
|
Inputs:
|
|
- bcontent (str): the contents to be set to
|
|
|
|
Outputs:
|
|
- (Block): the Block instance
|
|
'''
|
|
|
|
self.bcontent = str(bcontent)
|
|
return self
|
|
|
|
# static functions
|
|
def exists(bHash):
|
|
'''
|
|
Checks if a block is saved to file or not
|
|
|
|
Inputs:
|
|
- hash (str/Block):
|
|
- if (Block): check if this block is saved to file
|
|
- if (str): check if a block by this hash is in file
|
|
|
|
Outputs:
|
|
- (bool): whether or not the block file exists
|
|
'''
|
|
|
|
# no input data? scrap it.
|
|
if bHash is None:
|
|
return False
|
|
|
|
if isinstance(bHash, Block):
|
|
bHash = bHash.getHash()
|
|
|
|
ret = isinstance(onionrstorage.getData(bHash), type(None))
|
|
|
|
return not ret</code></pre>
|
|
</details>
|
|
</section>
|
|
<section>
|
|
</section>
|
|
<section>
|
|
</section>
|
|
<section>
|
|
</section>
|
|
<section>
|
|
<h2 class="section-title" id="header-classes">Classes</h2>
|
|
<dl>
|
|
<dt id="src.onionrblocks.onionrblockapi.Block"><code class="flex name class">
|
|
<span>class <span class="ident">Block</span></span>
|
|
<span>(</span><span>hash=None, type=None, content=None, expire=None, decrypt=False, bypassReplayCheck=False)</span>
|
|
</code></dt>
|
|
<dd>
|
|
<section class="desc"></section>
|
|
<details class="source">
|
|
<summary>
|
|
<span>Expand source code</span>
|
|
</summary>
|
|
<pre><code class="python">class Block:
|
|
blockCacheOrder = list() # NEVER write your own code that writes to this!
|
|
blockCache = dict() # should never be accessed directly, look at Block.getCache()
|
|
|
|
def __init__(self, hash = None, type = None, content = None, expire=None, decrypt=False, bypassReplayCheck=False):
|
|
# take from arguments
|
|
# sometimes people input a bytes object instead of str in `hash`
|
|
if (not hash is None) and isinstance(hash, bytes):
|
|
hash = hash.decode()
|
|
|
|
self.hash = hash
|
|
self.btype = type
|
|
self.bcontent = content
|
|
self.expire = expire
|
|
self.bypassReplayCheck = bypassReplayCheck
|
|
|
|
# initialize variables
|
|
self.valid = True
|
|
self.raw = None
|
|
self.signed = False
|
|
self.signature = None
|
|
self.signedData = None
|
|
self.blockFile = None
|
|
self.bheader = {}
|
|
self.bmetadata = {}
|
|
self.isEncrypted = False
|
|
self.decrypted = False
|
|
self.signer = None
|
|
self.validSig = False
|
|
self.autoDecrypt = decrypt
|
|
|
|
self.update()
|
|
|
|
def decrypt(self, encodedData = True):
|
|
'''
|
|
Decrypt a block, loading decrypted data into their vars
|
|
'''
|
|
|
|
if self.decrypted:
|
|
return True
|
|
retData = False
|
|
# decrypt data
|
|
if self.getHeader('encryptType') == 'asym':
|
|
try:
|
|
try:
|
|
self.bcontent = encryption.pub_key_decrypt(self.bcontent, encodedData=encodedData)
|
|
except (binascii.Error, ValueError) as e:
|
|
self.bcontent = encryption.pub_key_decrypt(self.bcontent, encodedData=False)
|
|
bmeta = encryption.pub_key_decrypt(self.bmetadata, encodedData=encodedData)
|
|
try:
|
|
bmeta = bmeta.decode()
|
|
except AttributeError:
|
|
# yet another bytes fix
|
|
pass
|
|
self.bmetadata = json.loads(bmeta)
|
|
self.signature = encryption.pub_key_decrypt(self.signature, encodedData=encodedData)
|
|
self.signer = encryption.pub_key_decrypt(self.signer, encodedData=encodedData)
|
|
self.bheader['signer'] = self.signer.decode()
|
|
self.signedData = json.dumps(self.bmetadata).encode() + self.bcontent
|
|
|
|
if not self.signer is None:
|
|
if not self.verifySig():
|
|
raise onionrexceptions.SignatureError("Block has invalid signature")
|
|
|
|
# Check for replay attacks
|
|
try:
|
|
if epoch.get_epoch() - blockmetadb.get_block_date(self.hash) > 60:
|
|
if not cryptoutils.replay_validator(self.bmetadata['rply']): raise onionrexceptions.ReplayAttack
|
|
except (AssertionError, KeyError, TypeError, onionrexceptions.ReplayAttack) as e:
|
|
if not self.bypassReplayCheck:
|
|
# Zero out variables to prevent reading of replays
|
|
self.bmetadata = {}
|
|
self.signer = ''
|
|
self.bheader['signer'] = ''
|
|
self.signedData = ''
|
|
self.signature = ''
|
|
raise onionrexceptions.ReplayAttack('Signature is too old. possible replay attack')
|
|
try:
|
|
if not self.bmetadata['forwardEnc']: raise KeyError
|
|
except (AssertionError, KeyError) as e:
|
|
pass
|
|
else:
|
|
try:
|
|
self.bcontent = onionrusers.OnionrUser(self.signer).forwardDecrypt(self.bcontent)
|
|
except (onionrexceptions.DecryptionError, nacl.exceptions.CryptoError) as e:
|
|
#logger.error(str(e))
|
|
pass
|
|
except nacl.exceptions.CryptoError:
|
|
pass
|
|
#logger.debug('Could not decrypt block. Either invalid key or corrupted data')
|
|
except onionrexceptions.ReplayAttack:
|
|
logger.warn('%s is possibly a replay attack' % (self.hash,))
|
|
else:
|
|
retData = True
|
|
self.decrypted = True
|
|
return retData
|
|
|
|
def verifySig(self):
|
|
'''
|
|
Verify if a block's signature is signed by its claimed signer
|
|
'''
|
|
if self.signer is None:
|
|
return False
|
|
if signing.ed_verify(data=self.signedData, key=self.signer, sig=self.signature, encodedData=True):
|
|
self.validSig = True
|
|
else:
|
|
self.validSig = False
|
|
return self.validSig
|
|
|
|
def update(self, data = None, file = None):
|
|
'''
|
|
Loads data from a block in to the current object.
|
|
|
|
Inputs:
|
|
- data (str):
|
|
- if None: will load from file by hash
|
|
- else: will load from `data` string
|
|
- file (str):
|
|
- if None: will load from file specified in this parameter
|
|
- else: will load from wherever block is stored by hash
|
|
|
|
Outputs:
|
|
- (bool): indicates whether or not the operation was successful
|
|
'''
|
|
try:
|
|
# import from string
|
|
blockdata = data
|
|
|
|
# import from file
|
|
if blockdata is None:
|
|
try:
|
|
blockdata = onionrstorage.getData(self.getHash())#.decode()
|
|
except AttributeError:
|
|
raise onionrexceptions.NoDataAvailable('Block does not exist')
|
|
else:
|
|
self.blockFile = None
|
|
# parse block
|
|
self.raw = blockdata
|
|
self.bheader = json.loads(self.getRaw()[:self.getRaw().index(b'\n')])
|
|
self.bcontent = self.getRaw()[self.getRaw().index(b'\n') + 1:]
|
|
if ('encryptType' in self.bheader) and (self.bheader['encryptType'] in ('asym', 'sym')):
|
|
self.bmetadata = self.getHeader('meta', None)
|
|
self.isEncrypted = True
|
|
else:
|
|
self.bmetadata = json.loads(self.getHeader('meta', 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)
|
|
# signed data is jsonMeta + block content (no linebreak)
|
|
self.signedData = (None if not self.isSigned() else self.getHeader('meta').encode() + self.getContent())
|
|
self.date = blockmetadb.get_block_date(self.getHash())
|
|
self.claimedTime = self.getHeader('time', None)
|
|
|
|
if not self.getDate() is None:
|
|
self.date = datetime.datetime.fromtimestamp(self.getDate())
|
|
|
|
self.valid = True
|
|
|
|
if self.autoDecrypt:
|
|
self.decrypt()
|
|
|
|
return True
|
|
except Exception as e:
|
|
logger.warn('Failed to parse block %s' % self.getHash(), error = e, timestamp = False)
|
|
|
|
# if block can't be parsed, it's a waste of precious space. Throw it away.
|
|
if not self.delete():
|
|
logger.warn('Failed to delete invalid block %s.' % self.getHash(), error = e)
|
|
else:
|
|
logger.debug('Deleted invalid block %s.' % self.getHash(), timestamp = False)
|
|
|
|
self.valid = False
|
|
return False
|
|
|
|
def delete(self):
|
|
'''
|
|
Deletes the block's file and records, if they exist
|
|
|
|
Outputs:
|
|
- (bool): whether or not the operation was successful
|
|
'''
|
|
|
|
if self.exists():
|
|
try:
|
|
os.remove(self.getBlockFile())
|
|
except TypeError:
|
|
pass
|
|
b_hash = self.getHash()
|
|
onionrstorage.deleteBlock(b_hash)
|
|
removeblock.remove_block(b_hash)
|
|
return True
|
|
return False
|
|
|
|
def save(self, sign = False, recreate = True):
|
|
'''
|
|
Saves a block to file and imports it into Onionr
|
|
|
|
Inputs:
|
|
- sign (bool): whether or not to sign the block before saving
|
|
- recreate (bool): if the block already exists, whether or not to recreate the block and save under a new hash
|
|
|
|
Outputs:
|
|
- (bool): whether or not the operation was successful
|
|
'''
|
|
|
|
try:
|
|
if self.isValid() is True:
|
|
|
|
self.hash = onionrblocks.insert(self.getRaw(), header = self.getType(), sign = sign, meta = self.getMetadata(), expire = self.getExpire())
|
|
if self.hash != False:
|
|
self.update()
|
|
|
|
return self.getHash()
|
|
else:
|
|
logger.warn('Not writing block; it is invalid.')
|
|
except Exception as e:
|
|
logger.error('Failed to save block.', error = e, timestamp = False)
|
|
|
|
return False
|
|
|
|
# getters
|
|
|
|
def getExpire(self):
|
|
'''
|
|
Returns the expire time for a block
|
|
|
|
Outputs:
|
|
- (int): the expire time for a block, or None
|
|
'''
|
|
return self.expire
|
|
|
|
def getHash(self):
|
|
'''
|
|
Returns the hash of the block if saved to file
|
|
|
|
Outputs:
|
|
- (str): the hash of the block, or None
|
|
'''
|
|
|
|
return self.hash
|
|
|
|
def getType(self):
|
|
'''
|
|
Returns the type of the block
|
|
|
|
Outputs:
|
|
- (str): the type of the block
|
|
'''
|
|
return self.btype
|
|
|
|
def getRaw(self):
|
|
'''
|
|
Returns the raw contents of the block, if saved to file
|
|
|
|
Outputs:
|
|
- (bytes): the raw contents of the block, or None
|
|
'''
|
|
|
|
return self.raw
|
|
|
|
def getHeader(self, key = None, default = None):
|
|
'''
|
|
Returns the header information
|
|
|
|
Inputs:
|
|
- key (str): only returns the value of the key in the header
|
|
|
|
Outputs:
|
|
- (dict/str): either the whole header as a dict, or one value
|
|
'''
|
|
|
|
if not key is None:
|
|
if key in self.getHeader():
|
|
return self.getHeader()[key]
|
|
return default
|
|
return self.bheader
|
|
|
|
def getMetadata(self, key = None, default = None):
|
|
'''
|
|
Returns the metadata information
|
|
|
|
Inputs:
|
|
- key (str): only returns the value of the key in the metadata
|
|
|
|
Outputs:
|
|
- (dict/str): either the whole metadata as a dict, or one value
|
|
'''
|
|
|
|
if not key is None:
|
|
if key in self.getMetadata():
|
|
return self.getMetadata()[key]
|
|
return default
|
|
return self.bmetadata
|
|
|
|
def getContent(self):
|
|
'''
|
|
Returns the contents of the block
|
|
|
|
Outputs:
|
|
- (str): the contents of the block
|
|
'''
|
|
|
|
return self.bcontent
|
|
|
|
def getDate(self):
|
|
'''
|
|
Returns the date that the block was received, if loaded from file
|
|
|
|
Outputs:
|
|
- (datetime): the date that the block was received
|
|
'''
|
|
|
|
return self.date
|
|
|
|
def getBlockFile(self):
|
|
'''
|
|
Returns the location of the block file if it is saved
|
|
|
|
Outputs:
|
|
- (str): the location of the block file, or None
|
|
'''
|
|
|
|
return self.blockFile
|
|
|
|
def isValid(self):
|
|
'''
|
|
Checks if the block is valid
|
|
|
|
Outputs:
|
|
- (bool): whether or not the block is valid
|
|
'''
|
|
|
|
return self.valid
|
|
|
|
def isSigned(self):
|
|
'''
|
|
Checks if the block was signed
|
|
|
|
Outputs:
|
|
- (bool): whether or not the block is signed
|
|
'''
|
|
|
|
return self.signed
|
|
|
|
def getSignature(self):
|
|
'''
|
|
Returns the base64-encoded signature
|
|
|
|
Outputs:
|
|
- (str): the signature, or None
|
|
'''
|
|
|
|
return self.signature
|
|
|
|
def getSignedData(self):
|
|
'''
|
|
Returns the data that was signed
|
|
|
|
Outputs:
|
|
- (str): the data that was signed, or None
|
|
'''
|
|
|
|
return self.signedData
|
|
|
|
def isSigner(self, signer, encodedData = True):
|
|
'''
|
|
Checks if the block was signed by the signer inputted
|
|
|
|
Inputs:
|
|
- signer (str): the public key of the signer to check against
|
|
- encodedData (bool): whether or not the `signer` argument is base64 encoded
|
|
|
|
Outputs:
|
|
- (bool): whether or not the signer of the block is the signer inputted
|
|
'''
|
|
signer = unpaddedbase32.repad(bytesconverter.str_to_bytes(signer))
|
|
try:
|
|
if (not self.isSigned()) or (not stringvalidators.validate_pub_key(signer)):
|
|
return False
|
|
|
|
return bool(signing.ed_verify(self.getSignedData(), signer, self.getSignature(), encodedData = encodedData))
|
|
except:
|
|
return False
|
|
|
|
# setters
|
|
|
|
def setType(self, btype):
|
|
'''
|
|
Sets the type of the block
|
|
|
|
Inputs:
|
|
- btype (str): the type of block to be set to
|
|
|
|
Outputs:
|
|
- (Block): the Block instance
|
|
'''
|
|
|
|
self.btype = btype
|
|
return self
|
|
|
|
def setMetadata(self, key, val):
|
|
'''
|
|
Sets a custom metadata value
|
|
|
|
Metadata should not store block-specific data structures.
|
|
|
|
Inputs:
|
|
- key (str): the key
|
|
- val: the value (type is irrelevant)
|
|
|
|
Outputs:
|
|
- (Block): the Block instance
|
|
'''
|
|
|
|
self.bmetadata[key] = val
|
|
return self
|
|
|
|
def setContent(self, bcontent):
|
|
'''
|
|
Sets the contents of the block
|
|
|
|
Inputs:
|
|
- bcontent (str): the contents to be set to
|
|
|
|
Outputs:
|
|
- (Block): the Block instance
|
|
'''
|
|
|
|
self.bcontent = str(bcontent)
|
|
return self
|
|
|
|
# static functions
|
|
def exists(bHash):
|
|
'''
|
|
Checks if a block is saved to file or not
|
|
|
|
Inputs:
|
|
- hash (str/Block):
|
|
- if (Block): check if this block is saved to file
|
|
- if (str): check if a block by this hash is in file
|
|
|
|
Outputs:
|
|
- (bool): whether or not the block file exists
|
|
'''
|
|
|
|
# no input data? scrap it.
|
|
if bHash is None:
|
|
return False
|
|
|
|
if isinstance(bHash, Block):
|
|
bHash = bHash.getHash()
|
|
|
|
ret = isinstance(onionrstorage.getData(bHash), type(None))
|
|
|
|
return not ret</code></pre>
|
|
</details>
|
|
<h3>Class variables</h3>
|
|
<dl>
|
|
<dt id="src.onionrblocks.onionrblockapi.Block.blockCache"><code class="name">var <span class="ident">blockCache</span></code></dt>
|
|
<dd>
|
|
<section class="desc"><p>dict() -> new empty dictionary
|
|
dict(mapping) -> new dictionary initialized from a mapping object's
|
|
(key, value) pairs
|
|
dict(iterable) -> new dictionary initialized as if via:
|
|
d = {}
|
|
for k, v in iterable:
|
|
d[k] = v
|
|
dict(**kwargs) -> new dictionary initialized with the name=value pairs
|
|
in the keyword argument list.
|
|
For example:
|
|
dict(one=1, two=2)</p></section>
|
|
</dd>
|
|
<dt id="src.onionrblocks.onionrblockapi.Block.blockCacheOrder"><code class="name">var <span class="ident">blockCacheOrder</span></code></dt>
|
|
<dd>
|
|
<section class="desc"><p>Built-in mutable sequence.</p>
|
|
<p>If no argument is given, the constructor creates a new empty list.
|
|
The argument must be an iterable if specified.</p></section>
|
|
</dd>
|
|
</dl>
|
|
<h3>Methods</h3>
|
|
<dl>
|
|
<dt id="src.onionrblocks.onionrblockapi.Block.decrypt"><code class="name flex">
|
|
<span>def <span class="ident">decrypt</span></span>(<span>self, encodedData=True)</span>
|
|
</code></dt>
|
|
<dd>
|
|
<section class="desc"><p>Decrypt a block, loading decrypted data into their vars</p></section>
|
|
<details class="source">
|
|
<summary>
|
|
<span>Expand source code</span>
|
|
</summary>
|
|
<pre><code class="python">def decrypt(self, encodedData = True):
|
|
'''
|
|
Decrypt a block, loading decrypted data into their vars
|
|
'''
|
|
|
|
if self.decrypted:
|
|
return True
|
|
retData = False
|
|
# decrypt data
|
|
if self.getHeader('encryptType') == 'asym':
|
|
try:
|
|
try:
|
|
self.bcontent = encryption.pub_key_decrypt(self.bcontent, encodedData=encodedData)
|
|
except (binascii.Error, ValueError) as e:
|
|
self.bcontent = encryption.pub_key_decrypt(self.bcontent, encodedData=False)
|
|
bmeta = encryption.pub_key_decrypt(self.bmetadata, encodedData=encodedData)
|
|
try:
|
|
bmeta = bmeta.decode()
|
|
except AttributeError:
|
|
# yet another bytes fix
|
|
pass
|
|
self.bmetadata = json.loads(bmeta)
|
|
self.signature = encryption.pub_key_decrypt(self.signature, encodedData=encodedData)
|
|
self.signer = encryption.pub_key_decrypt(self.signer, encodedData=encodedData)
|
|
self.bheader['signer'] = self.signer.decode()
|
|
self.signedData = json.dumps(self.bmetadata).encode() + self.bcontent
|
|
|
|
if not self.signer is None:
|
|
if not self.verifySig():
|
|
raise onionrexceptions.SignatureError("Block has invalid signature")
|
|
|
|
# Check for replay attacks
|
|
try:
|
|
if epoch.get_epoch() - blockmetadb.get_block_date(self.hash) > 60:
|
|
if not cryptoutils.replay_validator(self.bmetadata['rply']): raise onionrexceptions.ReplayAttack
|
|
except (AssertionError, KeyError, TypeError, onionrexceptions.ReplayAttack) as e:
|
|
if not self.bypassReplayCheck:
|
|
# Zero out variables to prevent reading of replays
|
|
self.bmetadata = {}
|
|
self.signer = ''
|
|
self.bheader['signer'] = ''
|
|
self.signedData = ''
|
|
self.signature = ''
|
|
raise onionrexceptions.ReplayAttack('Signature is too old. possible replay attack')
|
|
try:
|
|
if not self.bmetadata['forwardEnc']: raise KeyError
|
|
except (AssertionError, KeyError) as e:
|
|
pass
|
|
else:
|
|
try:
|
|
self.bcontent = onionrusers.OnionrUser(self.signer).forwardDecrypt(self.bcontent)
|
|
except (onionrexceptions.DecryptionError, nacl.exceptions.CryptoError) as e:
|
|
#logger.error(str(e))
|
|
pass
|
|
except nacl.exceptions.CryptoError:
|
|
pass
|
|
#logger.debug('Could not decrypt block. Either invalid key or corrupted data')
|
|
except onionrexceptions.ReplayAttack:
|
|
logger.warn('%s is possibly a replay attack' % (self.hash,))
|
|
else:
|
|
retData = True
|
|
self.decrypted = True
|
|
return retData</code></pre>
|
|
</details>
|
|
</dd>
|
|
<dt id="src.onionrblocks.onionrblockapi.Block.delete"><code class="name flex">
|
|
<span>def <span class="ident">delete</span></span>(<span>self)</span>
|
|
</code></dt>
|
|
<dd>
|
|
<section class="desc"><p>Deletes the block's file and records, if they exist</p>
|
|
<p>Outputs:
|
|
- (bool): whether or not the operation was successful</p></section>
|
|
<details class="source">
|
|
<summary>
|
|
<span>Expand source code</span>
|
|
</summary>
|
|
<pre><code class="python">def delete(self):
|
|
'''
|
|
Deletes the block's file and records, if they exist
|
|
|
|
Outputs:
|
|
- (bool): whether or not the operation was successful
|
|
'''
|
|
|
|
if self.exists():
|
|
try:
|
|
os.remove(self.getBlockFile())
|
|
except TypeError:
|
|
pass
|
|
b_hash = self.getHash()
|
|
onionrstorage.deleteBlock(b_hash)
|
|
removeblock.remove_block(b_hash)
|
|
return True
|
|
return False</code></pre>
|
|
</details>
|
|
</dd>
|
|
<dt id="src.onionrblocks.onionrblockapi.Block.exists"><code class="name flex">
|
|
<span>def <span class="ident">exists</span></span>(<span>bHash)</span>
|
|
</code></dt>
|
|
<dd>
|
|
<section class="desc"><p>Checks if a block is saved to file or not</p>
|
|
<p>Inputs:
|
|
- hash (str/Block):
|
|
- if (Block): check if this block is saved to file
|
|
- if (str): check if a block by this hash is in file</p>
|
|
<p>Outputs:
|
|
- (bool): whether or not the block file exists</p></section>
|
|
<details class="source">
|
|
<summary>
|
|
<span>Expand source code</span>
|
|
</summary>
|
|
<pre><code class="python">def exists(bHash):
|
|
'''
|
|
Checks if a block is saved to file or not
|
|
|
|
Inputs:
|
|
- hash (str/Block):
|
|
- if (Block): check if this block is saved to file
|
|
- if (str): check if a block by this hash is in file
|
|
|
|
Outputs:
|
|
- (bool): whether or not the block file exists
|
|
'''
|
|
|
|
# no input data? scrap it.
|
|
if bHash is None:
|
|
return False
|
|
|
|
if isinstance(bHash, Block):
|
|
bHash = bHash.getHash()
|
|
|
|
ret = isinstance(onionrstorage.getData(bHash), type(None))
|
|
|
|
return not ret</code></pre>
|
|
</details>
|
|
</dd>
|
|
<dt id="src.onionrblocks.onionrblockapi.Block.getBlockFile"><code class="name flex">
|
|
<span>def <span class="ident">getBlockFile</span></span>(<span>self)</span>
|
|
</code></dt>
|
|
<dd>
|
|
<section class="desc"><p>Returns the location of the block file if it is saved</p>
|
|
<p>Outputs:
|
|
- (str): the location of the block file, or None</p></section>
|
|
<details class="source">
|
|
<summary>
|
|
<span>Expand source code</span>
|
|
</summary>
|
|
<pre><code class="python">def getBlockFile(self):
|
|
'''
|
|
Returns the location of the block file if it is saved
|
|
|
|
Outputs:
|
|
- (str): the location of the block file, or None
|
|
'''
|
|
|
|
return self.blockFile</code></pre>
|
|
</details>
|
|
</dd>
|
|
<dt id="src.onionrblocks.onionrblockapi.Block.getContent"><code class="name flex">
|
|
<span>def <span class="ident">getContent</span></span>(<span>self)</span>
|
|
</code></dt>
|
|
<dd>
|
|
<section class="desc"><p>Returns the contents of the block</p>
|
|
<p>Outputs:
|
|
- (str): the contents of the block</p></section>
|
|
<details class="source">
|
|
<summary>
|
|
<span>Expand source code</span>
|
|
</summary>
|
|
<pre><code class="python">def getContent(self):
|
|
'''
|
|
Returns the contents of the block
|
|
|
|
Outputs:
|
|
- (str): the contents of the block
|
|
'''
|
|
|
|
return self.bcontent</code></pre>
|
|
</details>
|
|
</dd>
|
|
<dt id="src.onionrblocks.onionrblockapi.Block.getDate"><code class="name flex">
|
|
<span>def <span class="ident">getDate</span></span>(<span>self)</span>
|
|
</code></dt>
|
|
<dd>
|
|
<section class="desc"><p>Returns the date that the block was received, if loaded from file</p>
|
|
<p>Outputs:
|
|
- (datetime): the date that the block was received</p></section>
|
|
<details class="source">
|
|
<summary>
|
|
<span>Expand source code</span>
|
|
</summary>
|
|
<pre><code class="python">def getDate(self):
|
|
'''
|
|
Returns the date that the block was received, if loaded from file
|
|
|
|
Outputs:
|
|
- (datetime): the date that the block was received
|
|
'''
|
|
|
|
return self.date</code></pre>
|
|
</details>
|
|
</dd>
|
|
<dt id="src.onionrblocks.onionrblockapi.Block.getExpire"><code class="name flex">
|
|
<span>def <span class="ident">getExpire</span></span>(<span>self)</span>
|
|
</code></dt>
|
|
<dd>
|
|
<section class="desc"><p>Returns the expire time for a block</p>
|
|
<p>Outputs:
|
|
- (int): the expire time for a block, or None</p></section>
|
|
<details class="source">
|
|
<summary>
|
|
<span>Expand source code</span>
|
|
</summary>
|
|
<pre><code class="python">def getExpire(self):
|
|
'''
|
|
Returns the expire time for a block
|
|
|
|
Outputs:
|
|
- (int): the expire time for a block, or None
|
|
'''
|
|
return self.expire</code></pre>
|
|
</details>
|
|
</dd>
|
|
<dt id="src.onionrblocks.onionrblockapi.Block.getHash"><code class="name flex">
|
|
<span>def <span class="ident">getHash</span></span>(<span>self)</span>
|
|
</code></dt>
|
|
<dd>
|
|
<section class="desc"><p>Returns the hash of the block if saved to file</p>
|
|
<p>Outputs:
|
|
- (str): the hash of the block, or None</p></section>
|
|
<details class="source">
|
|
<summary>
|
|
<span>Expand source code</span>
|
|
</summary>
|
|
<pre><code class="python">def getHash(self):
|
|
'''
|
|
Returns the hash of the block if saved to file
|
|
|
|
Outputs:
|
|
- (str): the hash of the block, or None
|
|
'''
|
|
|
|
return self.hash</code></pre>
|
|
</details>
|
|
</dd>
|
|
<dt id="src.onionrblocks.onionrblockapi.Block.getHeader"><code class="name flex">
|
|
<span>def <span class="ident">getHeader</span></span>(<span>self, key=None, default=None)</span>
|
|
</code></dt>
|
|
<dd>
|
|
<section class="desc"><p>Returns the header information</p>
|
|
<p>Inputs:
|
|
- key (str): only returns the value of the key in the header</p>
|
|
<p>Outputs:
|
|
- (dict/str): either the whole header as a dict, or one value</p></section>
|
|
<details class="source">
|
|
<summary>
|
|
<span>Expand source code</span>
|
|
</summary>
|
|
<pre><code class="python">def getHeader(self, key = None, default = None):
|
|
'''
|
|
Returns the header information
|
|
|
|
Inputs:
|
|
- key (str): only returns the value of the key in the header
|
|
|
|
Outputs:
|
|
- (dict/str): either the whole header as a dict, or one value
|
|
'''
|
|
|
|
if not key is None:
|
|
if key in self.getHeader():
|
|
return self.getHeader()[key]
|
|
return default
|
|
return self.bheader</code></pre>
|
|
</details>
|
|
</dd>
|
|
<dt id="src.onionrblocks.onionrblockapi.Block.getMetadata"><code class="name flex">
|
|
<span>def <span class="ident">getMetadata</span></span>(<span>self, key=None, default=None)</span>
|
|
</code></dt>
|
|
<dd>
|
|
<section class="desc"><p>Returns the metadata information</p>
|
|
<p>Inputs:
|
|
- key (str): only returns the value of the key in the metadata</p>
|
|
<p>Outputs:
|
|
- (dict/str): either the whole metadata as a dict, or one value</p></section>
|
|
<details class="source">
|
|
<summary>
|
|
<span>Expand source code</span>
|
|
</summary>
|
|
<pre><code class="python">def getMetadata(self, key = None, default = None):
|
|
'''
|
|
Returns the metadata information
|
|
|
|
Inputs:
|
|
- key (str): only returns the value of the key in the metadata
|
|
|
|
Outputs:
|
|
- (dict/str): either the whole metadata as a dict, or one value
|
|
'''
|
|
|
|
if not key is None:
|
|
if key in self.getMetadata():
|
|
return self.getMetadata()[key]
|
|
return default
|
|
return self.bmetadata</code></pre>
|
|
</details>
|
|
</dd>
|
|
<dt id="src.onionrblocks.onionrblockapi.Block.getRaw"><code class="name flex">
|
|
<span>def <span class="ident">getRaw</span></span>(<span>self)</span>
|
|
</code></dt>
|
|
<dd>
|
|
<section class="desc"><p>Returns the raw contents of the block, if saved to file</p>
|
|
<p>Outputs:
|
|
- (bytes): the raw contents of the block, or None</p></section>
|
|
<details class="source">
|
|
<summary>
|
|
<span>Expand source code</span>
|
|
</summary>
|
|
<pre><code class="python">def getRaw(self):
|
|
'''
|
|
Returns the raw contents of the block, if saved to file
|
|
|
|
Outputs:
|
|
- (bytes): the raw contents of the block, or None
|
|
'''
|
|
|
|
return self.raw</code></pre>
|
|
</details>
|
|
</dd>
|
|
<dt id="src.onionrblocks.onionrblockapi.Block.getSignature"><code class="name flex">
|
|
<span>def <span class="ident">getSignature</span></span>(<span>self)</span>
|
|
</code></dt>
|
|
<dd>
|
|
<section class="desc"><p>Returns the base64-encoded signature</p>
|
|
<p>Outputs:
|
|
- (str): the signature, or None</p></section>
|
|
<details class="source">
|
|
<summary>
|
|
<span>Expand source code</span>
|
|
</summary>
|
|
<pre><code class="python">def getSignature(self):
|
|
'''
|
|
Returns the base64-encoded signature
|
|
|
|
Outputs:
|
|
- (str): the signature, or None
|
|
'''
|
|
|
|
return self.signature</code></pre>
|
|
</details>
|
|
</dd>
|
|
<dt id="src.onionrblocks.onionrblockapi.Block.getSignedData"><code class="name flex">
|
|
<span>def <span class="ident">getSignedData</span></span>(<span>self)</span>
|
|
</code></dt>
|
|
<dd>
|
|
<section class="desc"><p>Returns the data that was signed</p>
|
|
<p>Outputs:
|
|
- (str): the data that was signed, or None</p></section>
|
|
<details class="source">
|
|
<summary>
|
|
<span>Expand source code</span>
|
|
</summary>
|
|
<pre><code class="python">def getSignedData(self):
|
|
'''
|
|
Returns the data that was signed
|
|
|
|
Outputs:
|
|
- (str): the data that was signed, or None
|
|
'''
|
|
|
|
return self.signedData</code></pre>
|
|
</details>
|
|
</dd>
|
|
<dt id="src.onionrblocks.onionrblockapi.Block.getType"><code class="name flex">
|
|
<span>def <span class="ident">getType</span></span>(<span>self)</span>
|
|
</code></dt>
|
|
<dd>
|
|
<section class="desc"><p>Returns the type of the block</p>
|
|
<p>Outputs:
|
|
- (str): the type of the block</p></section>
|
|
<details class="source">
|
|
<summary>
|
|
<span>Expand source code</span>
|
|
</summary>
|
|
<pre><code class="python">def getType(self):
|
|
'''
|
|
Returns the type of the block
|
|
|
|
Outputs:
|
|
- (str): the type of the block
|
|
'''
|
|
return self.btype</code></pre>
|
|
</details>
|
|
</dd>
|
|
<dt id="src.onionrblocks.onionrblockapi.Block.isSigned"><code class="name flex">
|
|
<span>def <span class="ident">isSigned</span></span>(<span>self)</span>
|
|
</code></dt>
|
|
<dd>
|
|
<section class="desc"><p>Checks if the block was signed</p>
|
|
<p>Outputs:
|
|
- (bool): whether or not the block is signed</p></section>
|
|
<details class="source">
|
|
<summary>
|
|
<span>Expand source code</span>
|
|
</summary>
|
|
<pre><code class="python">def isSigned(self):
|
|
'''
|
|
Checks if the block was signed
|
|
|
|
Outputs:
|
|
- (bool): whether or not the block is signed
|
|
'''
|
|
|
|
return self.signed</code></pre>
|
|
</details>
|
|
</dd>
|
|
<dt id="src.onionrblocks.onionrblockapi.Block.isSigner"><code class="name flex">
|
|
<span>def <span class="ident">isSigner</span></span>(<span>self, signer, encodedData=True)</span>
|
|
</code></dt>
|
|
<dd>
|
|
<section class="desc"><p>Checks if the block was signed by the signer inputted</p>
|
|
<p>Inputs:
|
|
- signer (str): the public key of the signer to check against
|
|
- encodedData (bool): whether or not the <code>signer</code> argument is base64 encoded</p>
|
|
<p>Outputs:
|
|
- (bool): whether or not the signer of the block is the signer inputted</p></section>
|
|
<details class="source">
|
|
<summary>
|
|
<span>Expand source code</span>
|
|
</summary>
|
|
<pre><code class="python">def isSigner(self, signer, encodedData = True):
|
|
'''
|
|
Checks if the block was signed by the signer inputted
|
|
|
|
Inputs:
|
|
- signer (str): the public key of the signer to check against
|
|
- encodedData (bool): whether or not the `signer` argument is base64 encoded
|
|
|
|
Outputs:
|
|
- (bool): whether or not the signer of the block is the signer inputted
|
|
'''
|
|
signer = unpaddedbase32.repad(bytesconverter.str_to_bytes(signer))
|
|
try:
|
|
if (not self.isSigned()) or (not stringvalidators.validate_pub_key(signer)):
|
|
return False
|
|
|
|
return bool(signing.ed_verify(self.getSignedData(), signer, self.getSignature(), encodedData = encodedData))
|
|
except:
|
|
return False</code></pre>
|
|
</details>
|
|
</dd>
|
|
<dt id="src.onionrblocks.onionrblockapi.Block.isValid"><code class="name flex">
|
|
<span>def <span class="ident">isValid</span></span>(<span>self)</span>
|
|
</code></dt>
|
|
<dd>
|
|
<section class="desc"><p>Checks if the block is valid</p>
|
|
<p>Outputs:
|
|
- (bool): whether or not the block is valid</p></section>
|
|
<details class="source">
|
|
<summary>
|
|
<span>Expand source code</span>
|
|
</summary>
|
|
<pre><code class="python">def isValid(self):
|
|
'''
|
|
Checks if the block is valid
|
|
|
|
Outputs:
|
|
- (bool): whether or not the block is valid
|
|
'''
|
|
|
|
return self.valid</code></pre>
|
|
</details>
|
|
</dd>
|
|
<dt id="src.onionrblocks.onionrblockapi.Block.save"><code class="name flex">
|
|
<span>def <span class="ident">save</span></span>(<span>self, sign=False, recreate=True)</span>
|
|
</code></dt>
|
|
<dd>
|
|
<section class="desc"><p>Saves a block to file and imports it into Onionr</p>
|
|
<p>Inputs:
|
|
- sign (bool): whether or not to sign the block before saving
|
|
- recreate (bool): if the block already exists, whether or not to recreate the block and save under a new hash</p>
|
|
<p>Outputs:
|
|
- (bool): whether or not the operation was successful</p></section>
|
|
<details class="source">
|
|
<summary>
|
|
<span>Expand source code</span>
|
|
</summary>
|
|
<pre><code class="python">def save(self, sign = False, recreate = True):
|
|
'''
|
|
Saves a block to file and imports it into Onionr
|
|
|
|
Inputs:
|
|
- sign (bool): whether or not to sign the block before saving
|
|
- recreate (bool): if the block already exists, whether or not to recreate the block and save under a new hash
|
|
|
|
Outputs:
|
|
- (bool): whether or not the operation was successful
|
|
'''
|
|
|
|
try:
|
|
if self.isValid() is True:
|
|
|
|
self.hash = onionrblocks.insert(self.getRaw(), header = self.getType(), sign = sign, meta = self.getMetadata(), expire = self.getExpire())
|
|
if self.hash != False:
|
|
self.update()
|
|
|
|
return self.getHash()
|
|
else:
|
|
logger.warn('Not writing block; it is invalid.')
|
|
except Exception as e:
|
|
logger.error('Failed to save block.', error = e, timestamp = False)
|
|
|
|
return False</code></pre>
|
|
</details>
|
|
</dd>
|
|
<dt id="src.onionrblocks.onionrblockapi.Block.setContent"><code class="name flex">
|
|
<span>def <span class="ident">setContent</span></span>(<span>self, bcontent)</span>
|
|
</code></dt>
|
|
<dd>
|
|
<section class="desc"><p>Sets the contents of the block</p>
|
|
<p>Inputs:
|
|
- bcontent (str): the contents to be set to</p>
|
|
<p>Outputs:
|
|
- (Block): the Block instance</p></section>
|
|
<details class="source">
|
|
<summary>
|
|
<span>Expand source code</span>
|
|
</summary>
|
|
<pre><code class="python">def setContent(self, bcontent):
|
|
'''
|
|
Sets the contents of the block
|
|
|
|
Inputs:
|
|
- bcontent (str): the contents to be set to
|
|
|
|
Outputs:
|
|
- (Block): the Block instance
|
|
'''
|
|
|
|
self.bcontent = str(bcontent)
|
|
return self</code></pre>
|
|
</details>
|
|
</dd>
|
|
<dt id="src.onionrblocks.onionrblockapi.Block.setMetadata"><code class="name flex">
|
|
<span>def <span class="ident">setMetadata</span></span>(<span>self, key, val)</span>
|
|
</code></dt>
|
|
<dd>
|
|
<section class="desc"><p>Sets a custom metadata value</p>
|
|
<p>Metadata should not store block-specific data structures.</p>
|
|
<p>Inputs:
|
|
- key (str): the key
|
|
- val: the value (type is irrelevant)</p>
|
|
<p>Outputs:
|
|
- (Block): the Block instance</p></section>
|
|
<details class="source">
|
|
<summary>
|
|
<span>Expand source code</span>
|
|
</summary>
|
|
<pre><code class="python">def setMetadata(self, key, val):
|
|
'''
|
|
Sets a custom metadata value
|
|
|
|
Metadata should not store block-specific data structures.
|
|
|
|
Inputs:
|
|
- key (str): the key
|
|
- val: the value (type is irrelevant)
|
|
|
|
Outputs:
|
|
- (Block): the Block instance
|
|
'''
|
|
|
|
self.bmetadata[key] = val
|
|
return self</code></pre>
|
|
</details>
|
|
</dd>
|
|
<dt id="src.onionrblocks.onionrblockapi.Block.setType"><code class="name flex">
|
|
<span>def <span class="ident">setType</span></span>(<span>self, btype)</span>
|
|
</code></dt>
|
|
<dd>
|
|
<section class="desc"><p>Sets the type of the block</p>
|
|
<p>Inputs:
|
|
- btype (str): the type of block to be set to</p>
|
|
<p>Outputs:
|
|
- (Block): the Block instance</p></section>
|
|
<details class="source">
|
|
<summary>
|
|
<span>Expand source code</span>
|
|
</summary>
|
|
<pre><code class="python">def setType(self, btype):
|
|
'''
|
|
Sets the type of the block
|
|
|
|
Inputs:
|
|
- btype (str): the type of block to be set to
|
|
|
|
Outputs:
|
|
- (Block): the Block instance
|
|
'''
|
|
|
|
self.btype = btype
|
|
return self</code></pre>
|
|
</details>
|
|
</dd>
|
|
<dt id="src.onionrblocks.onionrblockapi.Block.update"><code class="name flex">
|
|
<span>def <span class="ident">update</span></span>(<span>self, data=None, file=None)</span>
|
|
</code></dt>
|
|
<dd>
|
|
<section class="desc"><p>Loads data from a block in to the current object.</p>
|
|
<p>Inputs:
|
|
- data (str):
|
|
- if None: will load from file by hash
|
|
- else: will load from <code>data</code> string
|
|
- file (str):
|
|
- if None: will load from file specified in this parameter
|
|
- else: will load from wherever block is stored by hash</p>
|
|
<p>Outputs:
|
|
- (bool): indicates whether or not the operation was successful</p></section>
|
|
<details class="source">
|
|
<summary>
|
|
<span>Expand source code</span>
|
|
</summary>
|
|
<pre><code class="python">def update(self, data = None, file = None):
|
|
'''
|
|
Loads data from a block in to the current object.
|
|
|
|
Inputs:
|
|
- data (str):
|
|
- if None: will load from file by hash
|
|
- else: will load from `data` string
|
|
- file (str):
|
|
- if None: will load from file specified in this parameter
|
|
- else: will load from wherever block is stored by hash
|
|
|
|
Outputs:
|
|
- (bool): indicates whether or not the operation was successful
|
|
'''
|
|
try:
|
|
# import from string
|
|
blockdata = data
|
|
|
|
# import from file
|
|
if blockdata is None:
|
|
try:
|
|
blockdata = onionrstorage.getData(self.getHash())#.decode()
|
|
except AttributeError:
|
|
raise onionrexceptions.NoDataAvailable('Block does not exist')
|
|
else:
|
|
self.blockFile = None
|
|
# parse block
|
|
self.raw = blockdata
|
|
self.bheader = json.loads(self.getRaw()[:self.getRaw().index(b'\n')])
|
|
self.bcontent = self.getRaw()[self.getRaw().index(b'\n') + 1:]
|
|
if ('encryptType' in self.bheader) and (self.bheader['encryptType'] in ('asym', 'sym')):
|
|
self.bmetadata = self.getHeader('meta', None)
|
|
self.isEncrypted = True
|
|
else:
|
|
self.bmetadata = json.loads(self.getHeader('meta', 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)
|
|
# signed data is jsonMeta + block content (no linebreak)
|
|
self.signedData = (None if not self.isSigned() else self.getHeader('meta').encode() + self.getContent())
|
|
self.date = blockmetadb.get_block_date(self.getHash())
|
|
self.claimedTime = self.getHeader('time', None)
|
|
|
|
if not self.getDate() is None:
|
|
self.date = datetime.datetime.fromtimestamp(self.getDate())
|
|
|
|
self.valid = True
|
|
|
|
if self.autoDecrypt:
|
|
self.decrypt()
|
|
|
|
return True
|
|
except Exception as e:
|
|
logger.warn('Failed to parse block %s' % self.getHash(), error = e, timestamp = False)
|
|
|
|
# if block can't be parsed, it's a waste of precious space. Throw it away.
|
|
if not self.delete():
|
|
logger.warn('Failed to delete invalid block %s.' % self.getHash(), error = e)
|
|
else:
|
|
logger.debug('Deleted invalid block %s.' % self.getHash(), timestamp = False)
|
|
|
|
self.valid = False
|
|
return False</code></pre>
|
|
</details>
|
|
</dd>
|
|
<dt id="src.onionrblocks.onionrblockapi.Block.verifySig"><code class="name flex">
|
|
<span>def <span class="ident">verifySig</span></span>(<span>self)</span>
|
|
</code></dt>
|
|
<dd>
|
|
<section class="desc"><p>Verify if a block's signature is signed by its claimed signer</p></section>
|
|
<details class="source">
|
|
<summary>
|
|
<span>Expand source code</span>
|
|
</summary>
|
|
<pre><code class="python">def verifySig(self):
|
|
'''
|
|
Verify if a block's signature is signed by its claimed signer
|
|
'''
|
|
if self.signer is None:
|
|
return False
|
|
if signing.ed_verify(data=self.signedData, key=self.signer, sig=self.signature, encodedData=True):
|
|
self.validSig = True
|
|
else:
|
|
self.validSig = False
|
|
return self.validSig</code></pre>
|
|
</details>
|
|
</dd>
|
|
</dl>
|
|
</dd>
|
|
</dl>
|
|
</section>
|
|
</article>
|
|
<nav id="sidebar">
|
|
<h1>Index</h1>
|
|
<div class="toc">
|
|
<ul></ul>
|
|
</div>
|
|
<ul id="index">
|
|
<li><h3>Super-module</h3>
|
|
<ul>
|
|
<li><code><a title="src.onionrblocks" href="index.html">src.onionrblocks</a></code></li>
|
|
</ul>
|
|
</li>
|
|
<li><h3><a href="#header-classes">Classes</a></h3>
|
|
<ul>
|
|
<li>
|
|
<h4><code><a title="src.onionrblocks.onionrblockapi.Block" href="#src.onionrblocks.onionrblockapi.Block">Block</a></code></h4>
|
|
<ul class="two-column">
|
|
<li><code><a title="src.onionrblocks.onionrblockapi.Block.blockCache" href="#src.onionrblocks.onionrblockapi.Block.blockCache">blockCache</a></code></li>
|
|
<li><code><a title="src.onionrblocks.onionrblockapi.Block.blockCacheOrder" href="#src.onionrblocks.onionrblockapi.Block.blockCacheOrder">blockCacheOrder</a></code></li>
|
|
<li><code><a title="src.onionrblocks.onionrblockapi.Block.decrypt" href="#src.onionrblocks.onionrblockapi.Block.decrypt">decrypt</a></code></li>
|
|
<li><code><a title="src.onionrblocks.onionrblockapi.Block.delete" href="#src.onionrblocks.onionrblockapi.Block.delete">delete</a></code></li>
|
|
<li><code><a title="src.onionrblocks.onionrblockapi.Block.exists" href="#src.onionrblocks.onionrblockapi.Block.exists">exists</a></code></li>
|
|
<li><code><a title="src.onionrblocks.onionrblockapi.Block.getBlockFile" href="#src.onionrblocks.onionrblockapi.Block.getBlockFile">getBlockFile</a></code></li>
|
|
<li><code><a title="src.onionrblocks.onionrblockapi.Block.getContent" href="#src.onionrblocks.onionrblockapi.Block.getContent">getContent</a></code></li>
|
|
<li><code><a title="src.onionrblocks.onionrblockapi.Block.getDate" href="#src.onionrblocks.onionrblockapi.Block.getDate">getDate</a></code></li>
|
|
<li><code><a title="src.onionrblocks.onionrblockapi.Block.getExpire" href="#src.onionrblocks.onionrblockapi.Block.getExpire">getExpire</a></code></li>
|
|
<li><code><a title="src.onionrblocks.onionrblockapi.Block.getHash" href="#src.onionrblocks.onionrblockapi.Block.getHash">getHash</a></code></li>
|
|
<li><code><a title="src.onionrblocks.onionrblockapi.Block.getHeader" href="#src.onionrblocks.onionrblockapi.Block.getHeader">getHeader</a></code></li>
|
|
<li><code><a title="src.onionrblocks.onionrblockapi.Block.getMetadata" href="#src.onionrblocks.onionrblockapi.Block.getMetadata">getMetadata</a></code></li>
|
|
<li><code><a title="src.onionrblocks.onionrblockapi.Block.getRaw" href="#src.onionrblocks.onionrblockapi.Block.getRaw">getRaw</a></code></li>
|
|
<li><code><a title="src.onionrblocks.onionrblockapi.Block.getSignature" href="#src.onionrblocks.onionrblockapi.Block.getSignature">getSignature</a></code></li>
|
|
<li><code><a title="src.onionrblocks.onionrblockapi.Block.getSignedData" href="#src.onionrblocks.onionrblockapi.Block.getSignedData">getSignedData</a></code></li>
|
|
<li><code><a title="src.onionrblocks.onionrblockapi.Block.getType" href="#src.onionrblocks.onionrblockapi.Block.getType">getType</a></code></li>
|
|
<li><code><a title="src.onionrblocks.onionrblockapi.Block.isSigned" href="#src.onionrblocks.onionrblockapi.Block.isSigned">isSigned</a></code></li>
|
|
<li><code><a title="src.onionrblocks.onionrblockapi.Block.isSigner" href="#src.onionrblocks.onionrblockapi.Block.isSigner">isSigner</a></code></li>
|
|
<li><code><a title="src.onionrblocks.onionrblockapi.Block.isValid" href="#src.onionrblocks.onionrblockapi.Block.isValid">isValid</a></code></li>
|
|
<li><code><a title="src.onionrblocks.onionrblockapi.Block.save" href="#src.onionrblocks.onionrblockapi.Block.save">save</a></code></li>
|
|
<li><code><a title="src.onionrblocks.onionrblockapi.Block.setContent" href="#src.onionrblocks.onionrblockapi.Block.setContent">setContent</a></code></li>
|
|
<li><code><a title="src.onionrblocks.onionrblockapi.Block.setMetadata" href="#src.onionrblocks.onionrblockapi.Block.setMetadata">setMetadata</a></code></li>
|
|
<li><code><a title="src.onionrblocks.onionrblockapi.Block.setType" href="#src.onionrblocks.onionrblockapi.Block.setType">setType</a></code></li>
|
|
<li><code><a title="src.onionrblocks.onionrblockapi.Block.update" href="#src.onionrblocks.onionrblockapi.Block.update">update</a></code></li>
|
|
<li><code><a title="src.onionrblocks.onionrblockapi.Block.verifySig" href="#src.onionrblocks.onionrblockapi.Block.verifySig">verifySig</a></code></li>
|
|
</ul>
|
|
</li>
|
|
</ul>
|
|
</li>
|
|
</ul>
|
|
</nav>
|
|
</main>
|
|
<footer id="footer">
|
|
<p>Generated by <a href="https://pdoc3.github.io/pdoc"><cite>pdoc</cite> 0.7.4</a>.</p>
|
|
</footer>
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/highlight.min.js"></script>
|
|
<script>hljs.initHighlightingOnLoad()</script>
|
|
</body>
|
|
</html> |