Esempio n. 1
0
 def test_plaintext_insert(self):
     setup_test()
     message = 'hello world'
     bl = onionrblocks.insert(message)
     self.assertTrue(bl.startswith('0'))
     self.assertIn(bytesconverter.str_to_bytes(message),
                   onionrstorage.getData(bl))
Esempio n. 2
0
    def addForwardKey(self, newKey, expire=DEFAULT_KEY_EXPIRE):
        newKey = bytesconverter.bytes_to_str(
            unpaddedbase32.repad(bytesconverter.str_to_bytes(newKey)))
        if not stringvalidators.validate_pub_key(newKey):
            # Do not add if something went wrong with the key
            raise onionrexceptions.InvalidPubkey(newKey)

        conn = sqlite3.connect(dbfiles.user_id_info_db,
                               timeout=DATABASE_LOCK_TIMEOUT)
        c = conn.cursor()

        # Get the time we're inserting the key at
        timeInsert = epoch.get_epoch()

        # Look at our current keys for duplicate key data or time
        for entry in self._getForwardKeys():
            if entry[0] == newKey:
                return False
            if entry[1] == timeInsert:
                timeInsert += 1
                # Sleep if our time is the same to prevent dupe time records
                time.sleep(1)

        # Add a forward secrecy key for the peer
        # Prepare the insert
        command = (self.publicKey, newKey, timeInsert, timeInsert + expire)

        c.execute("INSERT INTO forwardKeys VALUES(?, ?, ?, ?);", command)

        conn.commit()
        conn.close()
        return True
Esempio n. 3
0
def generate_deterministic(passphrase, bypassCheck=False):
    """Generate a Ed25519 public key pair from a phase.

    not intended for human-generated key
    """
    passStrength = onionrvalues.PASSWORD_LENGTH
    # Convert to bytes if not already
    passphrase = bytesconverter.str_to_bytes(passphrase)
    # Validate passphrase length
    if not bypassCheck:
        if len(passphrase) < passStrength:
            raise onionrexceptions.PasswordStrengthError(
                "Passphase must be at least %s characters" % (passStrength, ))
    # KDF values
    kdf = nacl.pwhash.argon2id.kdf
    # Does not need to be secret, but must be 16 bytes
    salt = b"U81Q7llrQcdTP0Ux"
    ops = nacl.pwhash.argon2id.OPSLIMIT_SENSITIVE
    mem = nacl.pwhash.argon2id.MEMLIMIT_SENSITIVE

    # Generate seed for ed25519 key
    key = kdf(32, passphrase, salt, opslimit=ops, memlimit=mem)
    key = nacl.signing.SigningKey(key)
    return (key.verify_key.encode(nacl.encoding.Base32Encoder).decode(),
            key.encode(nacl.encoding.Base32Encoder).decode())
Esempio n. 4
0
def getDifficultyForNewBlock(data):
    '''
    Get difficulty for block. Accepts size in integer, Block instance, or str/bytes full block contents
    '''
    if isinstance(data, onionrblockapi.Block):
        dataSizeInBytes = len(bytesconverter.str_to_bytes(data.getRaw()))
    else:
        dataSizeInBytes = len(bytesconverter.str_to_bytes(data))

    minDifficulty = config.get('general.minimum_send_pow', 4)
    totalDifficulty = max(minDifficulty, math.floor(
        dataSizeInBytes / 1000000.0)) + getDifficultyModifier()

    return totalDifficulty

    return retData
Esempio n. 5
0
def pub_key_encrypt(data, pubkey, encodedData=False):
    '''Encrypt to a public key (Curve25519, taken from base32 Ed25519 pubkey)'''
    pubkey = unpaddedbase32.repad(bytesconverter.str_to_bytes(pubkey))
    retVal = ''
    box = None
    data = bytesconverter.str_to_bytes(data)

    pubkey = nacl.signing.VerifyKey(
        pubkey,
        encoder=nacl.encoding.Base32Encoder()).to_curve25519_public_key()

    if encodedData:
        encoding = nacl.encoding.Base64Encoder
    else:
        encoding = nacl.encoding.RawEncoder

    box = nacl.public.SealedBox(pubkey)
    retVal = box.encrypt(data, encoder=encoding)

    return retVal
Esempio n. 6
0
def handle_announce(request):
    '''
    accept announcement posts, validating POW
    clientAPI should be an instance of the clientAPI server running, request is a instance of a flask request
    '''
    resp = 'failure'
    powHash = ''
    randomData = ''
    newNode = ''

    try:
        newNode = request.form['node'].encode()
    except KeyError:
        logger.warn('No node specified for upload')
        pass
    else:
        try:
            randomData = request.form['random']
            randomData = base64.b64decode(randomData)
        except KeyError:
            logger.warn('No random data specified for upload')
        else:
            nodes = newNode + bytesconverter.str_to_bytes(
                gettransports.get()[0])
            nodes = crypto.hashers.blake2b_hash(nodes)
            powHash = crypto.hashers.blake2b_hash(randomData + nodes)
            try:
                powHash = powHash.decode()
            except AttributeError:
                pass
            if powHash.startswith('0' * onionrvalues.ANNOUNCE_POW):
                newNode = bytesconverter.bytes_to_str(newNode)
                announce_queue = deadsimplekv.DeadSimpleKV(
                    filepaths.announce_cache)
                announce_queue_list = announce_queue.get('new_peers')
                if announce_queue_list is None:
                    announce_queue_list = []

                if stringvalidators.validate_transport(
                        newNode) and not newNode in announce_queue_list:
                    #clientAPI.onionrInst.communicatorInst.newPeers.append(newNode)
                    g.shared_state.get(
                        OnionrCommunicatorDaemon).newPeers.append(newNode)
                    announce_queue.put('new_peers',
                                       announce_queue_list.append(newNode))
                    announce_queue.flush()
                    resp = 'Success'
            else:
                logger.warn(newNode.decode() + ' failed to meet POW: ' +
                            powHash)
    resp = Response(resp)
    if resp == 'failure':
        return resp, 406
    return resp
Esempio n. 7
0
def ed_sign(data, key, encodeResult=False):
    '''Ed25519 sign data'''
    key = unpaddedbase32.repad(bytesconverter.str_to_bytes(key))
    try:
        data = data.encode()
    except AttributeError:
        pass
    key = nacl.signing.SigningKey(seed=key,
                                  encoder=nacl.encoding.Base32Encoder)
    retData = ''
    if encodeResult:
        retData = key.sign(
            data, encoder=nacl.encoding.Base64Encoder).signature.decode(
            )  # .encode() is not the same as nacl.encoding
    else:
        retData = key.sign(data).signature
    return retData
Esempio n. 8
0
def get_block_data(publicAPI, data):
    """data is the block hash in hex"""
    resp = ''
    if stringvalidators.validate_hash(data):
        if not config.get('general.hide_created_blocks', True) or data not in publicAPI.hideBlocks:
            if data in publicAPI._too_many.get(BlockList).get():
                block = apiutils.GetBlockData().get_block_data(data, raw=True, decrypt=False)
                try:
                    block = block.encode('utf-8') # Encode in case data is binary
                except AttributeError:
                    if len(block) == 0:
                        abort(404)
                block = bytesconverter.str_to_bytes(block)
                resp = block
    if len(resp) == 0:
        abort(404)
        resp = ""
    # Has to be octet stream, otherwise binary data fails hash check
    return Response(resp, mimetype='application/octet-stream')
Esempio n. 9
0
def validate_pub_key(key):
    '''
        Validate if a string is a valid base32 encoded Ed25519 key
    '''
    if type(key) is type(None):
        return False
    # Accept keys that have no = padding
    key = unpaddedbase32.repad(bytesconverter.str_to_bytes(key))

    retVal = False
    try:
        nacl.signing.SigningKey(seed=key, encoder=nacl.encoding.Base32Encoder)
    except nacl.exceptions.ValueError:
        pass
    except base64.binascii.Error as err:
        pass
    else:
        retVal = True
    return retVal
Esempio n. 10
0
    def __init__(self, publicKey, saveUser=False, recordExpireSeconds=5):
        try:
            if mnemonickeys.DELIMITER in publicKey:
                publicKey = mnemonickeys.get_base32(publicKey)
                #publicKey = unpaddedbase32.b32encode(bytesconverter.str_to_bytes(publicKey))
        except ValueError:
            pass
        publicKey = bytesconverter.bytes_to_str(
            unpaddedbase32.repad(bytesconverter.str_to_bytes(publicKey)))
        super(ContactManager, self).__init__(publicKey, saveUser=saveUser)
        home = identifyhome.identify_home()
        self.dataDir = home + '/contacts/'
        self.dataFile = '%s/contacts/%s.json' % (home, publicKey)
        self.lastRead = 0
        self.recordExpire = recordExpireSeconds
        self.data = self._loadData()
        self.deleted = False

        if not os.path.exists(self.dataDir):
            os.mkdir(self.dataDir)
Esempio n. 11
0
    def __init__(self, publicKey, saveUser=False):
        """
        OnionrUser is an abstraction for "users" of the network.

        Takes a base32 encoded ed25519 public key, and a bool saveUser
        saveUser determines if we should add a user to our peer database or not.
        """
        publicKey = unpaddedbase32.repad(
            bytesconverter.str_to_bytes(publicKey)).decode()

        self.trust = 0
        self.publicKey = publicKey

        if saveUser and not publicKey == getourkeypair.get_keypair():
            try:
                keydb.addkeys.add_peer(publicKey)
            except (AssertionError, ValueError) as _:
                pass

        self.trust = keydb.userinfo.get_user_info(self.publicKey, 'trust')
        return
Esempio n. 12
0
def set_data(data) -> str:
    '''
        Set the data assciated with a hash
    '''
    storage_counter = storagecounter.StorageCounter()
    data = data
    dataSize = sys.getsizeof(data)
    nonce_hash = crypto.hashers.sha3_hash(
        bytesconverter.str_to_bytes(
            blockmetadata.fromdata.get_block_metadata_from_data(data)[2]))
    nonce_hash = bytesconverter.bytes_to_str(nonce_hash)

    if not type(data) is bytes:
        data = data.encode()

    dataHash = crypto.hashers.sha3_hash(data)

    if type(dataHash) is bytes:
        dataHash = dataHash.decode()
    blockFileName = filepaths.block_data_location + dataHash + '.dat'
    try:
        onionrstorage.getData(dataHash)
    except onionrexceptions.NoDataAvailable:
        if storage_counter.add_bytes(dataSize) != False:
            onionrstorage.store(data, blockHash=dataHash)
            conn = sqlite3.connect(dbfiles.block_meta_db, timeout=30)
            c = conn.cursor()
            c.execute("UPDATE hashes SET dataSaved=1 WHERE hash = ?;",
                      (dataHash, ))
            conn.commit()
            conn.close()
            with open(filepaths.data_nonce_file, 'a') as nonceFile:
                nonceFile.write(nonce_hash + '\n')
        else:
            raise onionrexceptions.DiskAllocationReached
    else:
        raise onionrexceptions.DataExists("Data is already set for " +
                                          dataHash)

    return dataHash
Esempio n. 13
0
    def __init__(self, data, metadata, subproc_count=None):
        """
            Onionr proof of work using multiple processes
            Accepts block data, block metadata
            if subproc_count is not set,
            os.cpu_count() is used to determine the number of processes

            Due to Python GIL multiprocessing/use of external libraries
            is necessary to accelerate CPU bound tasks
        """
        # No known benefit to using more processes than there are cores.
        # Note: os.cpu_count perhaps not always accurate
        if subproc_count is None:
            subproc_count = os.cpu_count()
        self.subproc_count = subproc_count
        self.result = ''
        self.shutdown = False
        self.data = data
        self.metadata = metadata

        """dump dict to measure bytes of json metadata
        Cannot reuse later bc the pow token must be added
        """
        json_metadata = json.dumps(metadata).encode()

        self.data = bytesconverter.str_to_bytes(data)

        compiled_data = bytes(json_metadata + b'\n' + self.data)

        # Calculate difficulty. May use better algorithm in the future.
        self.difficulty = onionrproofs.getDifficultyForNewBlock(compiled_data)

        logger.info('Computing POW (difficulty: %s)...' % (self.difficulty,))

        self.main_hash = '0' * 64
        self.puzzle = self.main_hash[0:min(self.difficulty,
                                           len(self.main_hash))]
        self.shutdown = False
        self.payload = None
Esempio n. 14
0
def ed_verify(data, key, sig, encodedData=True):
    '''Verify signed data (combined in nacl) to an ed25519 key'''
    key = unpaddedbase32.repad(bytesconverter.str_to_bytes(key))
    try:
        key = nacl.signing.VerifyKey(key=key,
                                     encoder=nacl.encoding.Base32Encoder)
    except nacl.exceptions.ValueError:
        return False
    except binascii.Error:
        logger.warn('Could not load key for verification, invalid padding')
        return False
    retData = False
    sig = base64.b64decode(sig)
    try:
        data = data.encode()
    except AttributeError:
        pass
    try:
        retData = key.verify(data,
                             sig)  # .encode() is not the same as nacl.encoding
    except nacl.exceptions.BadSignatureError:
        pass
    return retData
Esempio n. 15
0
def pub_key_decrypt(data, pubkey='', privkey='', encodedData=False):
    '''pubkey decrypt (Curve25519, taken from Ed25519 pubkey)'''
    if pubkey != '':
        pubkey = unpaddedbase32.repad(bytesconverter.str_to_bytes(pubkey))
    decrypted = False
    if encodedData:
        encoding = nacl.encoding.Base64Encoder
    else:
        encoding = nacl.encoding.RawEncoder
    if privkey == '':
        privkey = our_priv_key
    ownKey = nacl.signing.SigningKey(
        seed=privkey,
        encoder=nacl.encoding.Base32Encoder()).to_curve25519_private_key()

    if stringvalidators.validate_pub_key(privkey):
        privkey = nacl.signing.SigningKey(
            seed=privkey,
            encoder=nacl.encoding.Base32Encoder()).to_curve25519_private_key()
        anonBox = nacl.public.SealedBox(privkey)
    else:
        anonBox = nacl.public.SealedBox(ownKey)
    decrypted = anonBox.decrypt(data, encoder=encoding)
    return decrypted
Esempio n. 16
0
    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
Esempio n. 17
0
def import_block_from_data(content):
    blacklist = onionrblacklist.OnionrBlackList()
    ret_data = False

    content = bytesconverter.str_to_bytes(content)

    data_hash = crypto.hashers.sha3_hash(content)

    if blacklist.inBlacklist(data_hash):
        raise BlacklistedBlock(f'%s is a blacklisted block {data_hash}')

    # returns tuple(metadata, meta), meta is also in metadata
    metas = blockmetadata.get_block_metadata_from_data(content)
    metadata = metas[0]

    # check if metadata is valid
    if validatemetadata.validate_metadata(metadata, metas[2]):
        # check if POW is enough/correct
        if crypto.cryptoutils.verify_POW(content):
            logger.info(f'Imported block passed proof, saving: {data_hash}.',
                        terminal=True)
            try:
                block_hash = onionrstorage.set_data(content)
            except DiskAllocationReached:
                logger.warn('Failed to save block due to full disk allocation')
                raise
            else:
                blockmetadb.add_to_block_DB(block_hash, dataSaved=True)
                # caches block metadata values to block database
                blockmetadata.process_block_metadata(block_hash)
                ret_data = block_hash
        else:
            raise InvalidProof
    else:
        raise InvalidMetadata
    return ret_data
Esempio n. 18
0
def extract_ed25519_from_onion_address(
        address: 'OnionAddressString') -> 'Ed25519PublicKeyBytes':
    address = str_to_bytes(address).replace(b'.onion', b'').upper()
    ed25519 = b32decode(address)[:-3]
    return ed25519
Esempio n. 19
0
 def test_encrypted_insert(self):
     setup_test()
     message = 'hello world2'
     bl = onionrblocks.insert(message, asymPeer=onionrcrypto.pub_key)
     self.assertIn(bytesconverter.str_to_bytes(message),
                   onionrblockapi.Block(bl, decrypt=True).bcontent)
Esempio n. 20
0
def get_motd()->Response:
    motds = blockmetadb.get_blocks_by_type("motd")
    newest_time = 0
    message = "No MOTD currently present."
    for x in motds:
        bl = onionrblocks.onionrblockapi.Block(x)
        if not bl.verifySig() or bl.signer != bytesconverter.bytes_to_str(unpaddedbase32.repad(bytesconverter.str_to_bytes(signer))): continue
        if not bl.isSigner(signer): continue
        if bl.claimedTime > newest_time:
            newest_time = bl.claimedTime
            message = bl.bcontent
    return Response(message, headers={"Content-Type": "text/plain"})
Esempio n. 21
0
 def getHumanReadable(name):
     name = unpaddedbase32.repad(bytesconverter.str_to_bytes(name))
     return Response(mnemonickeys.get_human_readable_ID(name))