Example #1
0
    def _decode_key(self, data):
        """
        # private key file contains:
        # RSAPrivateKey = { version = 0, n, e, d, p, q, d mod p-1, d mod q-1, q**-1 mod p }
        try:
            keylist = BER(data).decode()
        except BERException:
            raise SshException('Unable to parse key file')
        if (type(keylist) is not list) or (len(keylist) < 4) or (keylist[0] != 0):
            raise SshException('Not a valid RSA private key file (bad ber encoding)')
        self.n = keylist[1]
        self.e = keylist[2]
        self.d = keylist[3]
        # not really needed
        self.p = keylist[4]
        self.q = keylist[5]
        self.size = util.bit_length(self.n)
        """

        ver = struct.unpack("B", data[:1])[0]
        if ver != 1:
            raise SshException("Unsupported mnk version [{}].".format(ver))
        i = 1
        l, self.e = sshtype.parseMpint(data[i:])
        i += l
        l, self.n = sshtype.parseMpint(data[i:])
        i += l
        l, self.d = sshtype.parseMpint(data[i:])
        i += l
        l, self.p = sshtype.parseMpint(data[i:])
        i += l
        l, self.q = sshtype.parseMpint(data[i:])
Example #2
0
    def __init__(self, data=None, privdata=None, filename=None, password=None, vals=None, file_obj=None):
        self.n = None
        self.e = None
        self.d = None
        self.p = None
        self.q = None

        self.__public_key = None
        self.__public_key_bytes = None
        self.__private_key = None
        self.__rsassa_pss_signer = None
        self.__rsassa_pss_verifier = None

        if file_obj is not None:
            self._from_private_key(file_obj, password)
            return
        if filename is not None:
            self._from_private_key_file(filename, password)
            return
        if vals is not None:
            self.e, self.n = vals
        else:
            if data is None:
                if privdata is None:
                    raise SshException('Key object may not be empty')
                else:
                    self._decode_key(privdata)
            else:
                i, v = sshtype.parseString(data)
                if v != 'ssh-rsa':
                    raise SshException('Invalid key')
                l, self.e = sshtype.parseMpint(data[i:])
                i += l
                l, self.n = sshtype.parseMpint(data[i:])
        self.size = util.bit_length(self.n)
Example #3
0
    def _decode_key(self, data):
        """
        # private key file contains:
        # RSAPrivateKey = { version = 0, n, e, d, p, q, d mod p-1, d mod q-1, q**-1 mod p }
        try:
            keylist = BER(data).decode()
        except BERException:
            raise SshException('Unable to parse key file')
        if (type(keylist) is not list) or (len(keylist) < 4) or (keylist[0] != 0):
            raise SshException('Not a valid RSA private key file (bad ber encoding)')
        self.n = keylist[1]
        self.e = keylist[2]
        self.d = keylist[3]
        # not really needed
        self.p = keylist[4]
        self.q = keylist[5]
        self.size = util.bit_length(self.n)
        """

        ver = struct.unpack("B", data[:1])[0]
        if ver != 1:
            raise SshException("Unsupported mnk version [{}].".format(ver))
        i = 1
        l, self.e = sshtype.parseMpint(data[i:])
        i += l
        l, self.n = sshtype.parseMpint(data[i:])
        i += l
        l, self.d = sshtype.parseMpint(data[i:])
        i += l
        l, self.p = sshtype.parseMpint(data[i:])
        i += l
        l, self.q = sshtype.parseMpint(data[i:])
Example #4
0
    def __init__(self, data=None, privdata=None, filename=None, password=None, vals=None, file_obj=None):
        self.n = None
        self.e = None
        self.d = None
        self.p = None
        self.q = None

        self.__public_key = None
        self.__private_key = None
        self.__rsassa_pss_signer = None
        self.__rsassa_pss_verifier = None

        if file_obj is not None:
            self._from_private_key(file_obj, password)
            return
        if filename is not None:
            self._from_private_key_file(filename, password)
            return
        if vals is not None:
            self.e, self.n = vals
        else:
            if data is None:
                if privdata is None:
                    raise SshException("Key object may not be empty")
                else:
                    self._decode_key(privdata)
            else:
                i, v = sshtype.parseString(data)
                if v != "ssh-rsa":
                    raise SshException("Invalid key")
                l, self.e = sshtype.parseMpint(data[i:])
                i += l
                l, self.n = sshtype.parseMpint(data[i:])
        self.size = util.bit_length(self.n)
Example #5
0
def _fetch_dmail(handler, dmail_addr, dmail_key):
    de =\
        dmail.DmailEngine(handler.node.chord_engine.tasks, handler.node.db)

    if log.isEnabledFor(logging.INFO):
        dmail_key_enc = mbase32.encode(dmail_key)
        dmail_addr_enc = mbase32.encode(dmail_addr)
        log.info("Fetching dmail (key=[{}]) for address=[{}]."\
            .format(dmail_key_enc, dmail_addr_enc))

    dmail_address = yield from _load_dmail_address(handler, dmail_addr)

    dmail_key_obj = dmail_address.keys[0]

    target_key = dmail_key_obj.target_key
    x_bin = dmail_key_obj.x

    l, x = sshtype.parseMpint(x_bin)

    dm, valid_sig =\
        yield from de.fetch_dmail(bytes(dmail_key), x, target_key)

    if not dm:
        handler._send_partial_content(\
            "Dmail for key [{}] was not found."\
                .format(dmail_key_enc))
        return None, None

    return dm, valid_sig
Example #6
0
    def _dmail_auto_publish(self, dmail_address):
        data_rw = yield from self.engine.tasks.send_get_data(\
            dmail_address.site_key, retry_factor=100)

        if data_rw.data:
            if log.isEnabledFor(logging.DEBUG):
                log.debug("Succeeded in fetching dmail site [{}]; won't"\
                    " auto-publish."\
                        .format(mbase32.encode(dmail_address.site_key)))
            return

        if log.isEnabledFor(logging.INFO):
            log.info("Failed to fetch dmail site [{}]; republishing."\
                .format(mbase32.encode(dmail_address.site_key)))

        private_key = rsakey.RsaKey(privdata=dmail_address.site_privatekey)

        dh = dhgroup14.DhGroup14()
        dh.x = sshtype.parseMpint(dmail_address.keys[0].x)[1]
        dh.generate_e()

        dms = dmail.DmailSite()
        root = dms.root
        root["ssm"] = "mdh-v1"
        root["sse"] = base58.encode(sshtype.encodeMpint(dh.e))
        root["target"] =\
            mbase32.encode(dmail_address.keys[0].target_key)
        root["difficulty"] = int(dmail_address.keys[0].difficulty)

        storing_nodes =\
            yield from self._dmail_engine.publish_dmail_site(private_key, dms)

        if log.isEnabledFor(logging.INFO):
            log.info("Republished Dmail site with [{}] storing nodes."\
                .format(storing_nodes))
Example #7
0
    def _dmail_auto_publish(self, dmail_address):
        data_rw = yield from self.engine.tasks.send_get_data(\
            dmail_address.site_key, retry_factor=100)

        if data_rw.data:
            if log.isEnabledFor(logging.DEBUG):
                log.debug("Succeeded in fetching dmail site [{}]; won't"\
                    " auto-publish."\
                        .format(mbase32.encode(dmail_address.site_key)))
            return

        if log.isEnabledFor(logging.INFO):
            log.info("Failed to fetch dmail site [{}]; republishing."\
                .format(mbase32.encode(dmail_address.site_key)))

        private_key = rsakey.RsaKey(privdata=dmail_address.site_privatekey)

        dh = dhgroup14.DhGroup14()
        dh.x = sshtype.parseMpint(dmail_address.keys[0].x)[1]
        dh.generate_e()

        dms = dmail.DmailSite()
        root = dms.root
        root["ssm"] = "mdh-v1"
        root["sse"] = base58.encode(sshtype.encodeMpint(dh.e))
        root["target"] =\
            mbase32.encode(dmail_address.keys[0].target_key)
        root["difficulty"] = int(dmail_address.keys[0].difficulty)

        storing_nodes =\
            yield from self._dmail_engine.publish_dmail_site(private_key, dms)

        if log.isEnabledFor(logging.INFO):
            log.info("Republished Dmail site with [{}] storing nodes."\
                .format(storing_nodes))
Example #8
0
    def parse(self):
        super().parse()

        i = 1
        l, self.host_key = sshtype.parseBinary(self.buf[i:])
        i += l
        l, self.f = sshtype.parseMpint(self.buf[i:])
        i += l
        l, self.signature = sshtype.parseBinary(self.buf[i:])
Example #9
0
    def parse(self):
        super().parse()

        i = 1
        l, self.host_key = sshtype.parseBinary(self.buf[i:])
        i += l
        l, self.f = sshtype.parseMpint(self.buf[i:])
        i += l
        l, self.signature = sshtype.parseBinary(self.buf[i:])
Example #10
0
    def parse(self):
        super().parse()
        i = 1
        l, self.data = sshtype.parseBinary(self.buf[i:])
        i += l
        self.targeted = struct.unpack_from("?", self.buf, i)[0]
        i += 1

        if i == len(self.buf):
            return

        l, self.pubkey = sshtype.parseBinary(self.buf[i:])
        i += l
        l, self.path_hash = sshtype.parseBinary(self.buf[i:])
        i += l
        l, self.version = sshtype.parseMpint(self.buf[i:])
        i += l
        l, self.signature = sshtype.parseBinary(self.buf[i:])
Example #11
0
    def parse(self):
        super().parse()
        i = 1
        l, self.data = sshtype.parseBinary(self.buf[i:])
        i += l
        self.targeted = struct.unpack_from("?", self.buf, i)[0]
        i += 1

        if i == len(self.buf):
            return

        l, self.pubkey = sshtype.parseBinary(self.buf[i:])
        i += l
        l, self.path_hash = sshtype.parseBinary(self.buf[i:])
        i += l
        l, self.version = sshtype.parseMpint(self.buf[i:])
        i += l
        l, self.signature = sshtype.parseBinary(self.buf[i:])
Example #12
0
    def parse(self):
        super().parse()
        i = 1
        l, self.data = sshtype.parseBinary(self.buf[i:])
        i += l
        self.original_size = struct.unpack(">L", self.buf[i:i + 4])[0]
        i += 4

        if i == len(self.buf):
            return

        l, self.version = sshtype.parseMpint(self.buf[i:])
        i += l
        l, self.signature = sshtype.parseBinary(self.buf[i:])
        i += l

        if i == len(self.buf):
            return

        l, self.epubkey = sshtype.parseBinary(self.buf[i:])
        i += l
        self.pubkeylen = struct.unpack(">L", self.buf[i:i + 4])[0]
Example #13
0
    def parse(self):
        super().parse()
        i = 1
        l, self.data = sshtype.parseBinary(self.buf[i:])
        i += l
        self.original_size = struct.unpack(">L", self.buf[i : i + 4])[0]
        i += 4

        if i == len(self.buf):
            return

        l, self.version = sshtype.parseMpint(self.buf[i:])
        i += l
        l, self.signature = sshtype.parseBinary(self.buf[i:])
        i += l

        if i == len(self.buf):
            return

        l, self.epubkey = sshtype.parseBinary(self.buf[i:])
        i += l
        self.pubkeylen = struct.unpack(">L", self.buf[i : i + 4])[0]
Example #14
0
    def parse(self):
        super().parse()

        self.e = sshtype.parseMpint(self.buf[1:])[1]
Example #15
0
    def parse(self):
        super().parse()

        self.e = sshtype.parseMpint(self.buf[1:])[1]
Example #16
0
    def _fetch_and_save_dmail(self, dmail_message_key, dmail_address,\
            address_key):
        key_type = type(dmail_message_key)
        if key_type is not bytes:
            assert key_type is bytearray
            dmail_message_key = bytes(dmail_message_key)

        # Fetch the Dmail data from the network.
        l, x_mpint = sshtype.parseMpint(address_key.x)
        dmobj, valid_sig =\
            yield from self.fetch_dmail(\
                dmail_message_key, x_mpint, address_key.target_key)

        if not dmobj:
            if log.isEnabledFor(logging.INFO):
                log.info("Dmail was not found on the network.")
            return False

        def dbcall():
            with self.db.open_session() as sess:
                self.db.lock_table(sess, db.DmailMessage)

                q = sess.query(func.count("*")).select_from(db.DmailMessage)\
                    .filter(db.DmailMessage.data_key == dmail_message_key)

                if q.scalar():
                    return False

                msg = db.DmailMessage()
                msg.dmail_address_id = dmail_address.id
                msg.dmail_key_id = address_key.id
                msg.data_key = dmail_message_key
                msg.sender_dmail_key =\
                    enc.generate_ID(dmobj.sender_pubkey)\
                        if dmobj.sender_pubkey else None
                msg.sender_valid = valid_sig
                msg.subject = dmobj.subject
                msg.date = mutil.parse_iso_datetime(dmobj.date)

                msg.hidden = False
                msg.read = False
                msg.deleted = False

                attach_dmail_tag(sess, msg, "Inbox")

                msg.parts = []

                for part in dmobj.parts:
                    dbpart = db.DmailPart()
                    dbpart.mime_type = part.mime_type
                    dbpart.data = part.data
                    msg.parts.append(dbpart)

                sess.add(msg)

                sess.commit()

        yield from self.loop.run_in_executor(None, dbcall)

        if log.isEnabledFor(logging.INFO):
            log.info("Dmail saved!")

        return True
Example #17
0
    def __send_dmail(self, from_asymkey, recipient, dmail):
        assert type(recipient) is DmailSite

        root = recipient.root
        sse = sshtype.parseMpint(base58.decode(root["sse"]))[1]
        target = root["target"]
        difficulty = root["difficulty"]

        dh = dhgroup14.DhGroup14()
        dh.generate_x()
        dh.generate_e()
        dh.f = sse

        k = dh.calculate_k()

        target_key = mbase32.decode(target)

        key = self._generate_encryption_key(target_key, k)
        dmail_bytes = dmail.encode()

        m, r = enc.encrypt_data_block(dmail_bytes, key)
        if m:
            if r:
                m = m + r
        else:
            m = r

        dw = DmailWrapper()
        dw.ssm = _dh_method_name
        dw.sse = sse
        dw.ssf = dh.e

        if from_asymkey:
            dw.signature = from_asymkey.calc_rsassa_pss_sig(m)
        else:
            dw.signature = b''

        dw.data_len = len(dmail_bytes)
        dw.data_enc = m

        tb = mp.TargetedBlock()
        tb.target_key = target_key
        tb.noonce = int(0).to_bytes(64, "big")
        tb.block = dw

        tb_data = tb.encode()
        tb_header = tb_data[:mp.TargetedBlock.BLOCK_OFFSET]

        if log.isEnabledFor(logging.INFO):
            log.info(\
                "Attempting work on dmail (target=[{}], difficulty=[{}])."\
                    .format(target, difficulty))

        def threadcall():
            return brute.generate_targeted_block(\
                target_key, difficulty, tb_header,\
                mp.TargetedBlock.NOONCE_OFFSET,\
                mp.TargetedBlock.NOONCE_SIZE)

        noonce_bytes = yield from self.loop.run_in_executor(None, threadcall)

        if log.isEnabledFor(logging.INFO):
            log.info("Work found noonce [{}].".format(noonce_bytes))

        mp.TargetedBlock.set_noonce(tb_data, noonce_bytes)

        if log.isEnabledFor(logging.INFO):
            mp.TargetedBlock.set_noonce(tb_header, noonce_bytes)
            log.info("hash=[{}]."\
                .format(mbase32.encode(enc.generate_ID(tb_header))))

        key = None

        def key_callback(val):
            nonlocal key
            key = val

        log.info("Sending dmail to the network.")

        if log.isEnabledFor(logging.DEBUG):
            log.debug("dmail block data=[\n{}]."\
                .format(mutil.hex_dump(tb_data)))

        total_storing = 0
        retry = 0
        while True:
            storing_nodes = yield from\
                self.task_engine.send_store_targeted_data(\
                    tb_data, store_key=True, key_callback=key_callback,\
                    retry_factor=retry * 10)

            total_storing += storing_nodes

            if total_storing >= 3:
                break

            if retry > 32:
                break
            elif retry > 3:
                yield from asyncio.sleep(1)

            retry += 1

        key_enc = mbase32.encode(key)
        id_enc = mbase32.encode(enc.generate_ID(key))

        if log.isEnabledFor(logging.INFO):
            log.info("Dmail sent; key=[{}], id=[{}], storing_nodes=[{}]."\
                .format(key_enc, id_enc, total_storing))

        return total_storing
Example #18
0
    def _fetch_and_save_dmail(self, dmail_message_key, dmail_address,\
            address_key):
        key_type = type(dmail_message_key)
        if key_type is not bytes:
            assert key_type is bytearray
            dmail_message_key = bytes(dmail_message_key)

        # Fetch the Dmail data from the network.
        l, x_mpint = sshtype.parseMpint(address_key.x)
        dmobj, valid_sig =\
            yield from self.fetch_dmail(\
                dmail_message_key, x_mpint, address_key.target_key)

        if not dmobj:
            if log.isEnabledFor(logging.INFO):
                log.info("Dmail was not found on the network.")
            return False

        if dmobj.version > 1:
            if dmobj.destination_addr != dmail_address.site_key:
                log.warning(\
                    "Dmail was addressed to [{}], yet passed address was"\
                        " [{}]."\
                            .format(mbase32.encode(dmobj.destination_addr),\
                                mbase32.encode(dmail_address.site_key)))
                sig_valid = False

        # Save the Dmail to our local database.
        def dbcall():
            with self.db.open_session() as sess:
                self.db.lock_table(sess, db.DmailMessage)

                q = sess.query(func.count("*")).select_from(db.DmailMessage)\
                    .filter(db.DmailMessage.data_key == dmail_message_key)

                if q.scalar():
                    return False

                msg = db.DmailMessage()
                msg.dmail_address_id = dmail_address.id
                msg.dmail_key_id = address_key.id
                msg.data_key = dmail_message_key
                msg.sender_dmail_key =\
                    enc.generate_ID(dmobj.sender_pubkey)\
                        if dmobj.sender_pubkey else None
                msg.sender_valid = valid_sig
                msg.subject = dmobj.subject
                msg.date = mutil.parse_iso_datetime(dmobj.date)

                msg.hidden = False
                msg.read = False
                msg.deleted = False

                attach_dmail_tag(sess, msg, "Inbox")

                msg.parts = []

                for part in dmobj.parts:
                    dbpart = db.DmailPart()
                    dbpart.mime_type = part.mime_type
                    dbpart.data = part.data
                    msg.parts.append(dbpart)

                sess.add(msg)

                sess.commit()

        yield from self.loop.run_in_executor(None, dbcall)

        if log.isEnabledFor(logging.INFO):
            log.info("Dmail saved!")

        return True
Example #19
0
    def _send_dmail(self, from_asymkey, recipient, dmail_bytes, signature):
        assert type(recipient) is DmailSite

        # Read in recipient DmailSite.
        root = recipient.root
        sse = sshtype.parseMpint(base58.decode(root["sse"]))[1]
        target_enc = root["target"]
        difficulty = root["difficulty"]

        # Calculate a shared secret.
        dh = dhgroup14.DhGroup14()
        dh.generate_x()
        dh.generate_e()
        dh.f = sse

        k = dh.calculate_k()

        target_key = mbase32.decode(target_enc)

        key = self._generate_encryption_key(target_key, k)

        # Encrypt the Dmail bytes.
        m, r = enc.encrypt_data_block(dmail_bytes, key)
        if m:
            if r:
                m = m + r
        else:
            m = r

        # Store it in a DmailWrapper.
        dw = DmailWrapper()
        dw.ssm = _dh_method_name
        dw.sse = sse
        dw.ssf = dh.e

        dw.data_len = len(dmail_bytes)
        dw.data_enc = m

        # Store the DmailWrapper in a TargetedBlock.
        tb = mp.TargetedBlock()
        tb.target_key = target_key
        tb.nonce = int(0).to_bytes(64, "big")
        tb.block = dw

        tb_data = tb.encode()
        tb_header = tb_data[:mp.TargetedBlock.BLOCK_OFFSET]

        # Do the POW on the TargetedBlock.
        if log.isEnabledFor(logging.INFO):
            log.info(\
                "Attempting work on dmail (target=[{}], difficulty=[{}])."\
                    .format(target_enc, difficulty))

        def threadcall():
            return brute.generate_targeted_block(\
                target_key, difficulty, tb_header,\
                mp.TargetedBlock.NOONCE_OFFSET,\
                mp.TargetedBlock.NOONCE_SIZE)

        nonce_bytes = yield from self.loop.run_in_executor(None, threadcall)

        if log.isEnabledFor(logging.INFO):
            log.info("Work found nonce [{}].".format(nonce_bytes))

        mp.TargetedBlock.set_nonce(tb_data, nonce_bytes)

        if log.isEnabledFor(logging.INFO):
            mp.TargetedBlock.set_nonce(tb_header, nonce_bytes)
            log.info("Message key=[{}]."\
                .format(mbase32.encode(enc.generate_ID(tb_header))))

        key = None

        def key_callback(val):
            nonlocal key
            key = val

        if log.isEnabledFor(logging.DEBUG):
            log.debug("TargetedBlock dump=[\n{}]."\
                .format(mutil.hex_dump(tb_data)))

        # Upload the TargetedBlock to the network.
        log.info("Sending dmail to the network.")

        total_storing = 0
        retry = 0
        while True:
            storing_nodes = yield from\
                self.task_engine.send_store_targeted_data(\
                    tb_data, store_key=True, key_callback=key_callback,\
                    retry_factor=retry * 10)

            total_storing += storing_nodes

            if total_storing >= 3:
                break

            if retry > 32:
                break
            elif retry > 3:
                yield from asyncio.sleep(1)

            retry += 1

        key_enc = mbase32.encode(key)
        id_enc = mbase32.encode(enc.generate_ID(key))

        if log.isEnabledFor(logging.INFO):
            log.info("Dmail sent; key=[{}], id=[{}], storing_nodes=[{}]."\
                .format(key_enc, id_enc, total_storing))

        return total_storing
Example #20
0
def __main():
    global loop

    log.info("mcc running.")

    parser = argparse.ArgumentParser()
    parser.add_argument(\
        "--address",\
        help="The address of the Morphis node to connect to.",\
        default="127.0.0.1:4250")
    parser.add_argument(\
        "--create-dmail",\
        help="Generate and upload a new dmail site.",\
        action="store_true")
    parser.add_argument(\
        "--dburl",\
        help="Specify the database url to use.")
    parser.add_argument(\
        "--fetch-dmail",
        help="Fetch dmail for specified key_id.")
    parser.add_argument(\
        "-i",\
        help="Read file as stdin.")
    parser.add_argument("--nn", type=int,\
        help="Node instance number.")
    parser.add_argument(\
        "--prefix",\
        help="Specify the prefix for various things (currently --create-dmail"\
            ").")
    parser.add_argument(\
        "--scan-dmail",\
        help="Scan the network for available dmails.")
    parser.add_argument(\
        "--send-dmail",\
        help="Send stdin as a dmail with the specified subject. The"\
            " sender and recipients may be specified at the beginning of the"\
            " data as with email headers: 'from: ' and 'to: '.")
    parser.add_argument(\
        "--stat",\
        help="Report node status.",\
        action="store_true")
    parser.add_argument("-l", dest="logconf",\
        help="Specify alternate logging.ini [IF SPECIFIED, THIS MUST BE THE"\
            " FIRST PARAMETER!].")
    parser.add_argument(\
        "--dmail-target",\
        help="Specify the dmail target to validate dmail against.")
    parser.add_argument(\
        "-x",\
        help="Specify the x (Diffie-Hellman private secret) to use.")

    args = parser.parse_args()

    # Load or generate client mcc key.
    key_filename = "data/mcc_key-rsa.mnk"
    if os.path.exists(key_filename):
        log.info("mcc private key file found, loading.")
        client_key = rsakey.RsaKey(filename=key_filename)
    else:
        log.info("mcc private key file missing, generating.")
        client_key = rsakey.RsaKey.generate(bits=4096)
        client_key.write_private_key_file(key_filename)

    # Connect a Morphis Client (lightweight Node) instance.
    mc = client.Client(loop, client_key=client_key, address=args.address)
    r = yield from mc.connect()

    if not r:
        log.warning("Connection failed; exiting.")
        loop.stop()
        return

    dbase = init_db(args)
    de = dmail.DmailEngine(mc, dbase)

    log.info("Processing command requests...")

    if args.stat:
        r = yield from mc.send_command("stat")
        print(r.decode("UTF-8"), end='')

    if args.create_dmail:
        log.info("Creating and uploading dmail site.")

        privkey, data_key, dms, storing_nodes =\
            yield from de.generate_dmail_address(args.prefix)

        print("privkey: {}".format(base58.encode(privkey._encode_key())))
        print("x: {}".format(base58.encode(sshtype.encodeMpint(dms.dh.x))))
        print("dmail address: {}".format(mbase32.encode(data_key)))
        print("storing_nodes=[{}]."\
            .format(base58.encode(privkey._encode_key())))

    if args.send_dmail:
        log.info("Sending dmail.")

        if args.i:
            with open(args.i, "rb") as fh:
                dmail_data = fh.read().decode()
        else:
            dmail_data = stdin.read()

        if log.isEnabledFor(logging.DEBUG):
            log.debug("dmail_data=[{}].".format(dmail_data))

        yield from de.send_dmail_text(args.send_dmail, dmail_data)

    if args.scan_dmail:
        log.info("Scanning dmail address.")

        addr, sig_bits = mutil.decode_key(args.scan_dmail)

        def key_callback(key):
            print("dmail key: [{}].".format(mbase32.encode(key)))

        yield from de.scan_dmail_address(\
            addr, sig_bits, key_callback=key_callback)

    if args.fetch_dmail:
        log.info("Fetching dmail for key=[{}].".format(args.fetch_dmail))

        key = mbase32.decode(args.fetch_dmail)

        if args.x:
            l, x_int = sshtype.parseMpint(base58.decode(args.x))
        else:
            x_int = None

        dmail_target = args.dmail_target

        dm, valid_sig = yield from de.fetch_dmail(key, x_int, dmail_target)

        if not dm:
            raise Exception("No dmail found.")

        if not x_int:
            print("Encrypted dmail data=[\n{}].".format(mutil.hex_dump(dm)))
        else:
            print("Subject: {}\n".format(dm.subject))

            if dm.sender_pubkey:
                print("From: {}"\
                    .format(mbase32.encode(enc.generate_ID(dm.sender_pubkey))))

            i = 0
            for part in dm.parts:
                print("DmailPart[{}]:\n    mime-type=[{}]\n    data=[{}]\n"\
                    .format(i, part.mime_type, part.data))
                i += 1

    log.info("Disconnecting.")

    yield from mc.disconnect()

    loop.stop()
Example #21
0
def serve_get(dispatcher, rpath):
    global top_tags

    log.info("Service .dmail request.")

    req = rpath[len(s_dmail) :]

    #    if log.isEnabledFor(logging.INFO):
    #        log.info("req=[{}].".format(req))

    if req == "" or req == "/" or req == "/goto_new_mail" or req.startswith("/wrapper/"):
        cacheable = False
        if req == "/goto_new_mail":
            tag = "Inbox"
            addr = yield from _load_first_address_with_new_mail(dispatcher)
            if addr:
                log.info("NEW")
                addr_enc = mbase32.encode(addr.site_key)
            else:
                log.info("NO NEW")
                addr_enc = ""
            qline = None
        elif req.startswith("/wrapper/"):
            params = req[9:]

            pq = params.find("?")

            if pq != -1:
                qline = params[pq + 1 :]
                params = params[:pq]
            else:
                qline = None

            p0 = params.find("/")
            if p0 == -1:
                p0 = len(params)
                tag = "Inbox"
            else:
                tag = params[p0 + 1 :]

            addr_enc = params[:p0]

            if addr_enc:
                cacheable = True
                if dispatcher.handle_cache(req):
                    return
        else:
            tag = "Inbox"
            addr_enc = ""
            qline = None

        if not addr_enc:
            dmail_address = yield from _load_default_dmail_address(dispatcher)
            if dmail_address:
                addr_enc = mbase32.encode(dmail_address.site_key)

        msg_list = None
        if qline:
            eparams = parse_qs(qline)
            msg_list = eparams.get("msg_list")
            if msg_list:
                msg_list = msg_list[0]

        if not msg_list:
            msg_list = "morphis://.dmail/msg_list/" + addr_enc + "/" + tag

        template = templates.dmail_page_wrapper[0]
        template = template.format(tag=tag, addr=addr_enc, msg_list_iframe_url=msg_list)

        if cacheable:
            dispatcher.send_content([template, req])
        else:
            dispatcher.send_content(template)
        return

    if req == "/style.css":
        dispatcher.send_content(templates.dmail_css, content_type="text/css")
    elif req == "/logo":
        template = templates.dmail_logo[0]

        current_version = dispatcher.node.morphis_version
        latest_version_number = dispatcher.latest_version_number

        if latest_version_number and current_version != latest_version_number:
            version_str = '<span class="strikethrough nomargin">{}</span>]' '&nbsp;[<a href="{}{}">GET {}</a>'.format(
                current_version,
                dispatcher.handler.maalstroom_url_prefix_str,
                "sp1nara3xhndtgswh7fznt414we4mi3y6kdwbkz4jmt8ocb6"
                "x4w1faqjotjkcrefta11swe3h53dt6oru3r13t667pr7"
                "cpe3ocxeuma/latest_version",
                latest_version_number,
            )
        else:
            version_str = current_version

        connections = dispatcher.connection_count
        if connections == 1:
            connection_str = "1 Connection"
        else:
            connection_str = str(connections) + " Connections"

        template = template.format(version=version_str, connections=connection_str)

        dispatcher.send_content(template)
    elif req.startswith("/nav/"):
        params = req[5:]

        p0 = params.index("/")

        addr_enc = params[:p0]
        tag = params[p0 + 1 :]

        template = templates.dmail_nav[0]

        template = template.format(csrf_token=dispatcher.client_engine.csrf_token, addr=addr_enc, tag=tag)

        dispatcher.send_content(template)
    elif req.startswith("/aside/"):
        params = req[7:]
        p0 = params.index("/")
        addr_enc = params[:p0]
        tag = params[p0 + 1 :]

        addr = mbase32.decode(addr_enc)

        template = templates.dmail_aside[0]

        fmt = {}

        for top_tag in top_tags:
            active = top_tag == tag
            unread_count = yield from _count_unread_dmails(dispatcher, addr, top_tag)

            fmt[top_tag + "_active"] = "active-mailbox" if active else ""
            fmt[top_tag + "_unread_count"] = unread_count if unread_count else ""
            fmt[top_tag + "_unread_class"] = ("active-notify" if active else "inactive-notify") if unread_count else ""

        tags = yield from _load_tags(dispatcher, top_tags)

        tag_rows = []

        unquoted_tag = unquote(tag)

        for tag in tags:
            if unquoted_tag == tag.name:
                active = " active_tag"
            else:
                active = ""

            row = (
                '<li class="bullet{active}"><span class="mailbox-pad">'
                '<a href="morphis://.dmail/wrapper/{addr}/{tag}">{tag}</a>'
                "</span></li>".format(active=active, addr=addr_enc, tag=tag.name)
            )
            tag_rows.append(row)

        template = template.format(
            csrf_token=dispatcher.client_engine.csrf_token, addr=addr_enc, tag=tag, tag_rows="".join(tag_rows), **fmt
        )

        acharset = dispatcher.get_accept_charset()

        dispatcher.send_content(template)
    elif req.startswith("/msg_list/list/"):
        params = req[15:]
        p0 = params.index("/")
        addr_enc = params[:p0]
        tag = unquote(params[p0 + 1 :])

        template = templates.dmail_msg_list_list_start[0]

        addr_heading = "TO" if tag in ("Outbox", "Sent", "Drafts") else "FROM"

        if tag == "Inbox" or tag == "":
            unread_check = '<meta target="self" http-equiv="refresh" content="60"/>'
        else:
            unread_check = ""

        template = template.format(unread_check=unread_check, addr_heading=addr_heading)

        acharset = dispatcher.get_accept_charset()
        dispatcher.send_partial_content(template, True, content_type="text/html; charset={}".format(acharset))

        yield from _list_dmails_for_tag(dispatcher, addr_enc, unquote(tag))

        dispatcher.send_partial_content(templates.dmail_msg_list_list_end[0])
        dispatcher.end_partial_content()
    elif req.startswith("/msg_list/"):
        params = req[10:]

        p0 = params.index("/")
        addr_enc = params[:p0]
        tag = params[p0 + 1 :]

        if dispatcher.handle_cache(req):
            return

        if tag == "Trash":
            empty_trash_button_class = "link-button"
        else:
            empty_trash_button_class = "display_none"

        template = templates.dmail_msg_list[0]
        template = template.format(
            csrf_token=dispatcher.client_engine.csrf_token,
            tag=unquote(tag),
            addr=addr_enc,
            empty_trash_button_class=empty_trash_button_class,
        )

        dispatcher.send_content([template, req])
    elif req == "/new_mail":
        template = templates.dmail_new_mail[0]

        unread_count = yield from _count_unread_dmails(dispatcher)

        template = template.format(unread_count=unread_count)

        dispatcher.send_content(template)

    elif req.startswith("/images/"):
        dispatcher.send_content(templates.imgs[req[8:]])

    elif req.startswith("/tag/view/list/"):
        params = req[15:]

        p0 = params.index("/")
        tag = params[:p0]
        addr_enc = params[p0 + 1 :]

        if log.isEnabledFor(logging.INFO):
            log.info("Viewing dmails with tag [{}] for address [{}].".format(tag, addr_enc))

        start = templates.dmail_tag_view_list_start.replace(b"${TAG_NAME}", tag.encode())
        # FIXME: This is getting inefficient now, maybe time for Flask or
        # something like it. Maybe we can use just it's template renderer.
        start = start.replace(b"${DMAIL_ADDRESS}", addr_enc.encode())
        start = start.replace(b"${DMAIL_ADDRESS2}", "{}...".format(addr_enc[:32]).encode())

        acharset = dispatcher.get_accept_charset()

        dispatcher.send_partial_content(start, True, content_type="text/html; charset={}".format(acharset))

        yield from _list_dmails_for_tag(dispatcher, mbase32.decode(addr_enc), tag)

        dispatcher.send_partial_content(templates.dmail_tag_view_list_end)
        dispatcher.end_partial_content()

    elif req.startswith("/read/content/"):
        params = req[14:]

        msg_dbid = params

        dm = yield from _load_dmail(dispatcher, msg_dbid, fetch_parts=True)

        dmail_text = _format_dmail_content(dm)

        acharset = dispatcher.get_accept_charset()

        dispatcher.send_content(dmail_text.encode(acharset), content_type="text/plain; charset={}".format(acharset))

    elif req.startswith("/read/subject/"):
        params = req[14:]

        msg_dbid = params

        dm = yield from _load_dmail(dispatcher, msg_dbid)

        acharset = dispatcher.get_accept_charset()

        dispatcher.send_content(dm.subject.encode(acharset), content_type="text/plain; charset={}".format(acharset))

    elif req.startswith("/read/"):
        params = req[6:]

        p0 = params.index("/")
        p1 = params.index("/", p0 + 1)

        addr_enc = params[:p0]
        tag = params[p0 + 1 : p1]
        msg_dbid = params[p1 + 1 :]

        def processor(sess, dm):
            dm.read = True
            return True

        dm = yield from _process_dmail_message(dispatcher, msg_dbid, processor, fetch_parts=True, fetch_tags=True)

        if dm.hidden:
            trash_msg = "REMOVE FROM TRASH"
        else:
            trash_msg = "MOVE TO TRASH"

        safe_reply_subject = generate_safe_reply_subject(dm)

        if dm.sender_dmail_key:
            sender_addr = mbase32.encode(dm.sender_dmail_key)
            if dm.sender_valid:
                sender_class = "valid_sender"
            else:
                sender_class = "invalid_sender"
        else:
            sender_addr = "[Anonymous]"
            sender_class = "valid_sender"

        if dm.destination_dmail_key:
            dest_addr_enc = mbase32.encode(dm.destination_dmail_key)
            dest_class = ""
        else:
            dest_addr_enc = ""
            dest_class = " display_none"

        unquoted_tag = unquote(tag)

        existing_tag_rows = []
        if len(dm.tags) > 1:
            remove_tag_class = ""

            for etag in dm.tags:
                if etag.name == unquoted_tag:
                    selected = "selected "
                else:
                    selected = ""

                row = '<option {selected}value"{tag_id}">{tag_name}</option>'.format(
                    selected=selected, tag_id=etag.id, tag_name=etag.name
                )
                existing_tag_rows.append(row)
        else:
            remove_tag_class = "display_none"

        current_tag_names = [x.name for x in dm.tags]
        current_tag_names.extend(top_tags)
        current_tag_names.remove("Inbox")
        tags = yield from _load_tags(dispatcher, current_tag_names)

        available_tag_rows = []

        for atag in tags:
            row = '<option value"{tag_id}">{tag_name}</option>'.format(tag_id=atag.id, tag_name=atag.name)
            available_tag_rows.append(row)

        template = templates.dmail_read[0]
        template = template.format(
            csrf_token=dispatcher.client_engine.csrf_token,
            addr=addr_enc,
            tag=tag,
            safe_reply_subject=safe_reply_subject,
            trash_msg=trash_msg,
            msg_id=msg_dbid,
            sender_class=sender_class,
            sender=sender_addr,
            dest_class=dest_class,
            dest_addr=dest_addr_enc,
            date=mutil.format_human_no_ms_datetime(dm.date),
            remove_tag_class=remove_tag_class,
            existing_tags="".join(existing_tag_rows),
            available_tags="".join(available_tag_rows),
        )

        dispatcher.send_content(template)
    elif req.startswith("/compose/"):
        if len(req) > 8 and req[8] == "/":
            params = req[9:]
        else:
            params = req[8:]

        p0 = params.find("?")
        if p0 != -1:
            eparams = parse_qs(params[p0 + 1 :])

            subject = eparams.get("subject")
            if subject:
                subject = subject[0].replace('"', "&quot;")
            else:
                subject = ""

            sender_addr_enc = eparams.get("sender")
            if sender_addr_enc:
                sender_addr_enc = sender_addr_enc[0]
            else:
                sender_addr_enc = ""

            message_text = eparams.get("message")
            if message_text:
                message_text = message_text[0]
            else:
                message_text = ""
        else:
            subject = ""
            sender_addr_enc = ""
            message_text = ""
            p0 = len(params)

        dest_addr_enc = params[:p0]

        autofocus_fields = {"dest_addr_autofocus": "", "subject_autofocus": "", "message_text_autofocus": ""}
        if not dest_addr_enc:
            autofocus_fields["dest_addr_autofocus"] = " autofocus"
        elif not subject:
            autofocus_fields["subject_autofocus"] = " autofocus"
        elif not message_text:
            autofocus_fields["message_text_autofocus"] = " autofocus"

        addrs = yield from _list_dmail_addresses(dispatcher)

        if sender_addr_enc:
            sender_addr = mbase32.decode(sender_addr_enc)
            default_id = None
        else:
            sender_addr = None
            default_id = yield from _load_default_dmail_address_id(dispatcher)
            if not default_id:
                owner_if_anon_id = ""

        from_addr_options = []

        for addr in addrs:
            if sender_addr:
                selected = addr.site_key.startswith(sender_addr)
            elif default_id:
                selected = addr.id == default_id
            else:
                selected = False

            if selected:
                option = '<option value="{}" selected>{}</option>'
                owner_if_anon_id = addr.id
            else:
                option = '<option value="{}">{}</option>'

            addr_enc = mbase32.encode(addr.site_key)

            from_addr_options.append(option.format(addr.id, addr_enc))

        from_addr_options.append("<option value=" ">[Anonymous]</option>")

        from_addr_options = "".join(from_addr_options)

        template = templates.dmail_compose[0]

        template = template.format(
            csrf_token=dispatcher.client_engine.csrf_token,
            delete_class="display_none",
            owner_if_anon=owner_if_anon_id,
            from_addr_options=from_addr_options,
            dest_addr=dest_addr_enc,
            subject=subject,
            message_text=message_text,
            **autofocus_fields
        )

        acharset = dispatcher.get_accept_charset()
        dispatcher.send_content(template, content_type="text/html; charset={}".format(acharset))
    elif req == "/address_list":
        addrs = yield from _list_dmail_addresses(dispatcher)
        default_id = yield from _load_default_dmail_address_id(dispatcher)

        csrf_token = dispatcher.client_engine.csrf_token

        row_template = templates.dmail_address_list_row[0]

        rows = []

        for addr in addrs:
            site_key_enc = mbase32.encode(addr.site_key)

            if default_id and addr.id == default_id:
                set_default_class = "hidden"
            else:
                set_default_class = ""

            if addr.scan_interval:
                autoscan_link_text = "disable autoscan"
                autoscan_interval = 0
            else:
                autoscan_link_text = "enable autoscan"
                autoscan_interval = 60

            resp = row_template.format(
                csrf_token=csrf_token,
                addr=site_key_enc,
                addr_dbid=addr.id,
                set_default_class=set_default_class,
                autoscan_link_text=autoscan_link_text,
                autoscan_interval=autoscan_interval,
            )

            rows.append(resp)

        rows_content = "".join(rows)

        template = templates.dmail_address_list[0]
        template = template.format(address_list=rows_content)

        dispatcher.send_content(template)

    # Actions.

    elif req.startswith("/create_tag?"):
        query = req[12:]

        qdict = parse_qs(query, keep_blank_values=True)

        csrf_token = qdict["csrf_token"][0]

        if not dispatcher.check_csrf_token(csrf_token):
            return

        tag_name = qdict["tag_name"][0]

        if not tag_name:
            dispatcher.send_204()
            return

        r = yield from _create_tag(dispatcher, tag_name)

        redirect = qdict.get("redirect")
        if r and redirect:
            dispatcher.send_301(redirect[0])
        else:
            dispatcher.send_204()
    elif req.startswith("/modify_message_tag?"):
        query = req[20:]

        qdict = parse_qs(query, keep_blank_values=True)

        csrf_token = qdict["csrf_token"][0]

        if not dispatcher.check_csrf_token(csrf_token):
            return

        submit = qdict["submit"][0]

        def processor(sess, dm):
            if submit == "add_tag":
                dmail.attach_dmail_tag(sess, dm, qdict["add_tag"][0])
                return True
            elif submit == "move_to_tag":
                dm.tags.clear()
                dmail.attach_dmail_tag(sess, dm, qdict["add_tag"][0])
                return True
            elif submit == "remove_tag":
                if len(dm.tags) <= 1:
                    return False

                remove_tag = qdict["remove_tag"][0]
                remove_target = None
                for tag in dm.tags:
                    if tag.name == remove_tag:
                        remove_target = tag
                        break
                dm.tags.remove(remove_target)
                return True
            else:
                return False

        msg_id = qdict["msg_id"][0]

        dm = yield from _process_dmail_message(dispatcher, msg_id, processor, fetch_tags=True)

        redirect = qdict.get("redirect")
        if redirect:
            dispatcher.send_301(redirect[0])
        else:
            dispatcher.send_204()
    elif req.startswith("/refresh/"):
        params = req[9:]

        p0 = params.index("/")

        csrf_token = params[:p0]
        addr_enc = params[p0 + 1 :]

        if not dispatcher.check_csrf_token(csrf_token):
            return

        dmail_address = yield from _load_dmail_address(dispatcher, site_key=mbase32.decode(addr_enc), fetch_keys=True)

        dispatcher.client_engine.trigger_dmail_scan(dmail_address)

        dispatcher.send_204()
    elif req.startswith("/toggle_read/"):
        params = req[13:]

        pq = params.find("?redirect=")
        if pq != -1:
            redirect = params[pq + 10 :]
        else:
            redirect = None
            pq = len(params)

        p0 = params.index("/", 0, pq)

        csrf_token = params[:p0]
        msg_dbid = params[p0 + 1 : pq]

        if not dispatcher.check_csrf_token(csrf_token):
            return

        def processor(sess, dm):
            dm.read = not dm.read
            return True

        yield from _process_dmail_message(dispatcher, msg_dbid, processor)

        if redirect:
            dispatcher.send_301(redirect)
        else:
            dispatcher.send_204()
    elif req.startswith("/toggle_trashed/"):
        params = req[16:]
        pq = params.find("?redirect=")
        if pq != -1:
            redirect = params[pq + 10 :]
        else:
            redirect = None
            pq = len(params)

        p0 = params.index("/", 0, pq)

        csrf_token = params[:p0]
        msg_dbid = params[p0 + 1 : pq]

        if not dispatcher.check_csrf_token(csrf_token):
            return

        def processor(sess, dm):
            dm.hidden = not dm.hidden
            return True

        yield from _process_dmail_message(dispatcher, msg_dbid, processor)

        if redirect:
            dispatcher.send_301(redirect)
        else:
            dispatcher.send_204()
    elif req.startswith("/set_autoscan/"):
        params = req[14:]

        pq = params.find("?redirect=")
        if pq != -1:
            redirect = params[pq + 10 :]
        else:
            redirect = None
            pq = len(params)

        p0 = params.index("/", 0, pq)
        p1 = params.index("/", p0 + 1, pq)

        csrf_token = params[:p0]
        addr_id = int(params[p0 + 1 : p1])
        interval = int(params[p1 + 1 : pq])

        if not dispatcher.check_csrf_token(csrf_token):
            return

        def processor(sess, addr):
            addr.scan_interval = interval
            return True

        addr = yield from _process_dmail_address(dispatcher, processor, addr_id, fetch_keys=True)

        dispatcher.client_engine.update_dmail_autoscan(addr)

        if redirect:
            dispatcher.send_301(redirect)
        else:
            dispatcher.send_204()
    elif req.startswith("/empty_trash/"):
        params = req[13:]

        pq = params.find("?redirect=")
        if pq != -1:
            redirect = params[pq + 10 :]
        else:
            redirect = None
            pq = len(params)

        p0 = params.index("/", 0, pq)

        csrf_token = params[:p0]
        addr_enc = params[p0 + 1 : pq]

        if not dispatcher.check_csrf_token(csrf_token):
            return

        yield from _empty_trash(dispatcher, addr_enc)

        if redirect:
            dispatcher.send_301(redirect)
        else:
            dispatcher.send_204()
    elif req.startswith("/make_address_default/"):
        params = req[22:]

        pq = params.find("?redirect=")
        if pq != -1:
            redirect = params[pq + 10 :]
        else:
            redirect = None
            pq = len(params)

        p0 = params.index("/")

        csrf_token = params[:p0]
        addr_dbid = params[p0 + 1 : pq]

        if not dispatcher.check_csrf_token(csrf_token):
            return

        yield from _set_default_dmail_address(dispatcher, addr_dbid)

        if redirect:
            dispatcher.send_301(redirect)
        else:
            dispatcher.send_204()

    ##### OLD:

    elif req == "/create_address":
        if dispatcher.handle_cache(req):
            return

        template = templates.dmail_create_address[0]

        template = template.format(csrf_token=dispatcher.client_engine.csrf_token)

        dispatcher.send_content([template, req])
    elif req.startswith("/address_config/"):
        params = req[16:]

        addr_enc = params

        dmail_address = yield from _load_dmail_address(dispatcher, site_key=mbase32.decode(addr_enc), fetch_keys=True)

        content = templates.dmail_address_config[0]

        content = content.replace("{csrf_token}", dispatcher.client_engine.csrf_token)
        content = content.replace("${DIFFICULTY}", str(dmail_address.keys[0].difficulty))
        content = content.replace("${DMAIL_ADDRESS_SHORT}", addr_enc[:32])
        content = content.replace("${DMAIL_ADDRESS}", addr_enc)
        content = content.replace("${PRIVATE_KEY}", base58.encode(dmail_address.site_privatekey))
        content = content.replace("${X}", base58.encode(dmail_address.keys[0].x))
        content = content.replace("${TARGET_KEY}", base58.encode(dmail_address.keys[0].target_key))

        dispatcher.send_content([content, None])
    ##### OLD ACTIONS:
    elif req.startswith("/create_address/make_it_so?"):
        query = req[27:]

        qdict = parse_qs(query, keep_blank_values=True)

        prefix = qdict["prefix"][0]
        difficulty = int(qdict["difficulty"][0])
        csrf_token = qdict["csrf_token"][0]

        if not dispatcher.check_csrf_token(csrf_token):
            return

        log.info("prefix=[{}].".format(prefix))
        privkey, dmail_key, dms, storing_nodes = yield from _create_dmail_address(dispatcher, prefix, difficulty)

        dmail_key_enc = mbase32.encode(dmail_key)

        dispatcher.send_partial_content(templates.dmail_frame_start, True)
        if storing_nodes:
            dispatcher.send_partial_content(b"SUCCESS<br/>")
        else:
            dispatcher.send_partial_content(
                "PARTIAL SUCCESS<br/>"
                "<p>Your Dmail site was generated successfully; however,"
                " it failed to be stored on the network. To remedy this,"
                " simply go to your Dmail address page and click the"
                ' [<a target="_self" href="morphis://.dmail/'
                'address_config/{}">Address Settings</a>] link, and then'
                ' click the "Republish Dmail Site" button.</p>'.format(dmail_key_enc).encode()
            )

        dispatcher.send_partial_content(
            '<p>New dmail address: <a href="morphis://.dmail/wrapper/'
            '{addr_enc}">{addr_enc}</a></p>'.format(addr_enc=dmail_key_enc).encode()
        )
        dispatcher.send_partial_content(templates.dmail_frame_end)
        dispatcher.end_partial_content()
    elif req.startswith("/save_address_config/publish?"):
        query = req[29:]

        qdict = parse_qs(query, keep_blank_values=True)

        addr_enc = qdict["dmail_address"][0]
        difficulty = qdict["difficulty"][0]
        csrf_token = qdict["csrf_token"][0]

        if not dispatcher.check_csrf_token(csrf_token):
            return

        def processor(sess, dmail_address):
            if difficulty != dmail_address.keys[0].difficulty:
                dmail_address.keys[0].difficulty = difficulty
                return True
            else:
                return False

        dmail_address = yield from _process_dmail_address(
            dispatcher, processor, site_key=mbase32.decode(addr_enc), fetch_keys=True
        )

        dh = dhgroup14.DhGroup14()
        dh.x = sshtype.parseMpint(dmail_address.keys[0].x)[1]
        dh.generate_e()

        dms = dmail.DmailSite()
        root = dms.root
        root["ssm"] = "mdh-v1"
        root["sse"] = base58.encode(sshtype.encodeMpint(dh.e))
        root["target"] = mbase32.encode(dmail_address.keys[0].target_key)
        root["difficulty"] = int(difficulty)

        private_key = rsakey.RsaKey(privdata=dmail_address.site_privatekey)

        de = dmail.DmailEngine(dispatcher.node.chord_engine.tasks, dispatcher.node.db)

        storing_nodes = yield from de.publish_dmail_site(private_key, dms)

        if storing_nodes:
            dispatcher.send_content(
                templates.dmail_addr_settings_edit_success_content[0].format(addr_enc, addr_enc[:32]).encode()
            )
        else:
            dispatcher.send_content(
                templates.dmail_addr_settings_edit_fail_content[0].format(addr_enc, addr_enc[:32]).encode()
            )
    else:
        dispatcher.send_error(errcode=400)
Example #22
0
def __serve_get(handler, rpath, done_event):
    if len(rpath) == len(s_dmail):
        handler._send_content(pages.dmail_page_content)
    else:
        req = rpath[len(s_dmail):]
        log.info("req=[{}].".format(req))
        if req == "/css":
            handler._send_content(\
                pages.dmail_css_content, content_type="text/css")
        elif req == "/address_list":
            handler._send_partial_content(
                pages.dmail_page_content__f1_start, True)

            site_keys = yield from _list_dmail_addresses(handler)

            for dbid, site_key in site_keys:
                site_key_enc = mbase32.encode(site_key)

                resp = """<span class="nowrap">[<a href="addr/{}">view</a>]"""\
                    """ {}</span><br/>"""\
                        .format(site_key_enc, site_key_enc)

                handler._send_partial_content(resp)

            handler._send_partial_content(pages.dmail_page_content__f1_end)
            handler._end_partial_content()
        elif req.startswith("/compose/form"):
            dest_addr_enc = req[14:] if len(req) > 14 else ""

            handler._send_partial_content(\
                pages.dmail_compose_dmail_form_start, True)

            site_keys = yield from _list_dmail_addresses(handler)

            for dbid, site_key in site_keys:
                site_key_enc = mbase32.encode(site_key)

                sender_element = """<option value="{}">{}</option>"""\
                    .format(dbid, site_key_enc)

                handler._send_partial_content(sender_element)

            handler._send_partial_content(\
                "<option value="">[Anonymous]</option>")

            handler._send_partial_content(\
                pages.dmail_compose_dmail_form_end.replace(\
                    b"${DEST_ADDR}", dest_addr_enc.encode()))

            handler._end_partial_content()
        elif req.startswith("/compose"):
            from_addr = req[9:] if len(req) > 9 else ""

            if from_addr:
                iframe_src = "../compose/form/{}".format(from_addr).encode()
            else:
                iframe_src = "compose/form".encode()

            content = pages.dmail_compose_dmail_content[0].replace(\
                    b"${IFRAME_SRC}", iframe_src)

            handler._send_content([content, None])
        elif req.startswith("/addr/view/"):
            addr_enc = req[11:]

            start = pages.dmail_addr_view_start.replace(\
                b"${DMAIL_ADDRESS}", addr_enc.encode())
            start = start.replace(\
                b"${DMAIL_ADDRESS_SHORT}", addr_enc[:32].encode())

            handler._send_partial_content(start, True)

            handler._send_partial_content(pages.dmail_addr_view_end)
            handler._end_partial_content()
        elif req.startswith("/addr/settings/edit/publish?"):
            query = req[28:]

            qdict = urllib.parse.parse_qs(query, keep_blank_values=True)

            addr_enc = qdict["dmail_address"][0]
            difficulty = qdict["difficulty"][0]

            def processor(dmail_address):
                if difficulty != dmail_address.keys[0].difficulty:
                    dmail_address.keys[0].difficulty = difficulty
                    return True
                else:
                    return False

            dmail_address = yield from\
                _process_dmail_address(\
                    handler, mbase32.decode(addr_enc), processor)

            dh = dhgroup14.DhGroup14()
            dh.x = sshtype.parseMpint(dmail_address.keys[0].x)[1]
            dh.generate_e()

            dms = dmail.DmailSite()
            root = dms.root
            root["target"] =\
                mbase32.encode(dmail_address.keys[0].target_key)
            root["difficulty"] = int(difficulty)
            root["ssm"] = "mdh-v1"
            root["sse"] = base58.encode(sshtype.encodeMpint(dh.e))

            private_key = rsakey.RsaKey(privdata=dmail_address.site_privatekey)

            r = yield from\
                handler.node.chord_engine.tasks.send_store_updateable_key(\
                    dms.export(), private_key,\
                    version=int(time.time()*1000), store_key=True)

            handler._send_content(\
                pages.dmail_addr_settings_edit_success_content[0]\
                    .format(addr_enc, addr_enc[:32]).encode())
        elif req.startswith("/addr/settings/edit/"):
            addr_enc = req[20:]

            dmail_address = yield from\
                _load_dmail_address(handler, mbase32.decode(addr_enc))

            content = pages.dmail_addr_settings_edit_content[0].replace(\
                b"${DIFFICULTY}",\
                str(dmail_address.keys[0].difficulty).encode())
            content = content.replace(\
                b"${DMAIL_ADDRESS_SHORT}", addr_enc[:32].encode())
            content = content.replace(\
                b"${DMAIL_ADDRESS}", addr_enc.encode())
            content = content.replace(\
                b"${PRIVATE_KEY}",\
                base58.encode(dmail_address.site_privatekey).encode())
            content = content.replace(\
                b"${X}", base58.encode(dmail_address.keys[0].x).encode())
            content = content.replace(\
                b"${TARGET_KEY}",\
                base58.encode(dmail_address.keys[0].target_key).encode())

            handler._send_content([content, None])
        elif req.startswith("/addr/settings/"):
            addr_enc = req[15:]

            content = pages.dmail_addr_settings_content[0].replace(\
                b"${IFRAME_SRC}",\
                "edit/{}".format(addr_enc).encode())

            handler._send_content([content, None])
        elif req.startswith("/addr/"):
            addr_enc = req[6:]

            if log.isEnabledFor(logging.INFO):
                log.info("Viewing dmail address [{}].".format(addr_enc))

            content = pages.dmail_address_page_content[0].replace(\
                b"${IFRAME_SRC}", "view/{}".format(addr_enc).encode())

            handler._send_content([content, None])
        elif req.startswith("/tag/view/list/"):
            params = req[15:]

            p0 = params.index('/')
            tag = params[:p0]
            addr_enc = params[p0+1:]

            if log.isEnabledFor(logging.INFO):
                log.info("Viewing dmails with tag [{}] for address [{}]."\
                    .format(tag, addr_enc))

            start = pages.dmail_tag_view_list_start.replace(\
                b"${TAG_NAME}", tag.encode())
            #FIXME: This is getting inefficient now, maybe time for Flask or
            # something like it. Maybe we can use just it's template renderer.
            start = start.replace(b"${DMAIL_ADDRESS}", addr_enc.encode())
            start = start.replace(\
                b"${DMAIL_ADDRESS2}",\
                "{}...".format(addr_enc[:32]).encode())

            handler._send_partial_content(start, True)

            yield from\
                _list_dmails_for_tag(handler, mbase32.decode(addr_enc), tag)

            handler._send_partial_content(pages.dmail_tag_view_list_end)
            handler._end_partial_content()

        elif req.startswith("/tag/view/"):
            params = req[10:]

            content = pages.dmail_tag_view_content[0].replace(\
                b"${IFRAME_SRC}", "../list/{}".format(params).encode())

            handler._send_content(content)
        elif req.startswith("/scan/list/"):
            addr_enc = req[11:]

            if log.isEnabledFor(logging.INFO):
                log.info("Viewing inbox for dmail address [{}]."\
                    .format(addr_enc))

            start = pages.dmail_inbox_start.replace(\
                b"${DMAIL_ADDRESS}", addr_enc.encode())
            start = start.replace(\
                b"${DMAIL_ADDRESS2}", "{}...".format(addr_enc[:32]).encode())

            handler._send_partial_content(start, True)

            addr, significant_bits = mutil.decode_key(addr_enc)

            yield from _scan_new_dmails(handler, addr, significant_bits)

            handler._send_partial_content(pages.dmail_inbox_end)
            handler._end_partial_content()
        elif req.startswith("/scan/"):
            addr_enc = req[6:]

            content = pages.dmail_address_page_content[0].replace(\
                b"${IFRAME_SRC}", "list/{}".format(addr_enc).encode())

            handler._send_content([content, None])
        elif req.startswith("/fetch/view/"):
            keys = req[12:]
            p0 = keys.index('/')
            dmail_addr_enc = keys[:p0]
            dmail_key_enc = keys[p0+1:]

            dmail_addr = mbase32.decode(dmail_addr_enc)
            dmail_key = mbase32.decode(dmail_key_enc)

            dm = yield from _load_dmail(handler, dmail_key)

            if dm:
                valid_sig = dm.sender_valid
            else:
                dm, valid_sig =\
                    yield from _fetch_dmail(handler, dmail_addr, dmail_key)

            dmail_text = _format_dmail(dm, valid_sig)

            handler._send_content(\
                dmail_text.encode(), content_type="text/plain")
        elif req.startswith("/fetch/panel/mark_as_read/"):
            req_data = req[26:]

            p0 = req_data.index('/')
            dmail_key_enc = req_data[p0+1:]
            dmail_key = mbase32.decode(dmail_key_enc)

            def processor(dmail):
                dmail.read = not dmail.read
                return True

            yield from _process_dmail_message(handler, dmail_key, processor)

            handler._send_204()
        elif req.startswith("/fetch/panel/trash/"):
            req_data = req[20:]

            p0 = req_data.index('/')
            dmail_key_enc = req_data[p0+1:]
            dmail_key = mbase32.decode(dmail_key_enc)

            def processor(dmail):
                dmail.hidden = not dmail.hidden
                return True

            yield from _process_dmail_message(handler, dmail_key, processor)

            handler._send_204()
        elif req.startswith("/fetch/panel/"):
            req_data = req[13:]

            content = pages.dmail_fetch_panel_content[0].replace(\
                b"${DMAIL_IDS}", req_data.encode())

            handler._send_content([content, None])
        elif req.startswith("/fetch/wrapper/"):
            req_data = req[15:]

            content = pages.dmail_fetch_wrapper[0].replace(\
                b"${IFRAME_SRC}",\
                "../../view/{}"\
                    .format(req_data).encode())
            #FIXME: This is getting inefficient now, maybe time for Flask or
            # something like it. Maybe we can use just it's template renderer.
            content = content.replace(\
                b"${IFRAME2_SRC}",\
                "../../panel/{}"\
                    .format(req_data).encode())

            handler._send_content([content, None])
        elif req.startswith("/fetch/"):
            req_data = req[7:]

            content = pages.dmail_address_page_content[0].replace(\
                b"${IFRAME_SRC}", "../wrapper/{}".format(req_data).encode())

            handler._send_content([content, None])
        elif req == "/create_address":
            handler._send_content(pages.dmail_create_address_content)
        elif req == "/create_address/form":
            handler._send_content(pages.dmail_create_address_form_content)
        elif req.startswith("/create_address/make_it_so?"):
            query = req[27:]

            qdict = urllib.parse.parse_qs(query, keep_blank_values=True)

            prefix = qdict["prefix"][0]
            difficulty = int(qdict["difficulty"][0])

            log.info("prefix=[{}].".format(prefix))
            privkey, dmail_key, dms =\
                yield from _create_dmail_address(handler, prefix, difficulty)

            dmail_key_enc = mbase32.encode(dmail_key)

            handler._send_partial_content(pages.dmail_frame_start, True)
            handler._send_partial_content(b"SUCCESS<br/>")
            handler._send_partial_content(\
                """<p>New dmail address: <a href="../addr/{}">{}</a></p>"""\
                    .format(dmail_key_enc, dmail_key_enc).encode())
            handler._send_partial_content(pages.dmail_frame_end)
            handler._end_partial_content()
        else:
            handler._handle_error()
Example #23
0
def __main():
    global loop

    log.info("mcc running.")

    parser = argparse.ArgumentParser()
    parser.add_argument(\
        "--address",\
        help="The address of the Morphis node to connect to.",\
        default="127.0.0.1:4250")
    parser.add_argument(\
        "--create-dmail",\
        help="Generate and upload a new dmail site.",\
        action="store_true")
    parser.add_argument(\
        "--dburl",\
        help="Specify the database url to use.")
    parser.add_argument(\
        "--fetch-dmail",
        help="Fetch dmail for specified key_id.")
    parser.add_argument(\
        "-i",\
        help="Read file as stdin.")
    parser.add_argument("--nn", type=int,\
        help="Node instance number.")
    parser.add_argument(\
        "--prefix",\
        help="Specify the prefix for various things (currently --create-dmail"\
            ").")
    parser.add_argument(\
        "--scan-dmail",\
        help="Scan the network for available dmails.")
    parser.add_argument(\
        "--send-dmail",\
        help="Send stdin as a dmail with the specified subject. The"\
            " sender and recipients may be specified at the beginning of the"\
            " data as with email headers: 'from: ' and 'to: '.")
    parser.add_argument(\
        "--stat",\
        help="Report node status.",\
        action="store_true")
    parser.add_argument("-l", dest="logconf",\
        help="Specify alternate logging.ini [IF SPECIFIED, THIS MUST BE THE"\
            " FIRST PARAMETER!].")
    parser.add_argument(\
        "--dmail-target",\
        help="Specify the dmail target to validate dmail against.")
    parser.add_argument(\
        "-x",\
        help="Specify the x (Diffie-Hellman private secret) to use.")

    args = parser.parse_args()

    # Load or generate client mcc key.
    key_filename = "data/mcc_key-rsa.mnk"
    if os.path.exists(key_filename):
        log.info("mcc private key file found, loading.")
        client_key = rsakey.RsaKey(filename=key_filename)
    else:
        log.info("mcc private key file missing, generating.")
        client_key = rsakey.RsaKey.generate(bits=4096)
        client_key.write_private_key_file(key_filename)

    # Connect a Morphis Client (lightweight Node) instance.
    mc = client.Client(loop, client_key=client_key, address=args.address)
    r = yield from mc.connect()

    if not r:
        log.warning("Connection failed; exiting.")
        loop.stop()
        return

    dbase = init_db(args)
    de = dmail.DmailEngine(mc, dbase)

    log.info("Processing command requests...")

    if args.stat:
        r = yield from mc.send_command("stat")
        print(r.decode("UTF-8"), end='')

    if args.create_dmail:
        log.info("Creating and uploading dmail site.")

        privkey, data_key, dms =\
            yield from de.generate_dmail_address(args.prefix)

        print("privkey: {}".format(base58.encode(privkey._encode_key())))
        print("x: {}".format(base58.encode(sshtype.encodeMpint(dms.dh.x))))
        print("dmail address: {}".format(mbase32.encode(data_key)))

    if args.send_dmail:
        log.info("Sending dmail.")

        if args.i:
            with open(args.i, "rb") as fh:
                dmail_data = fh.read().decode()
        else:
            dmail_data = stdin.read()

        if log.isEnabledFor(logging.DEBUG):
            log.debug("dmail_data=[{}].".format(dmail_data))

        yield from de.send_dmail_text(args.send_dmail, dmail_data)

    if args.scan_dmail:
        log.info("Scanning dmail address.")

        addr, sig_bits = mutil.decode_key(args.scan_dmail)

        def key_callback(key):
            print("dmail key: [{}].".format(mbase32.encode(key)))

        yield from de.scan_dmail_address(\
            addr, sig_bits, key_callback=key_callback)

    if args.fetch_dmail:
        log.info("Fetching dmail for key=[{}].".format(args.fetch_dmail))

        key = mbase32.decode(args.fetch_dmail)

        if args.x:
            l, x_int = sshtype.parseMpint(base58.decode(args.x))
        else:
            x_int = None

        dmail_target = args.dmail_target

        dm, valid_sig = yield from de.fetch_dmail(key, x_int, dmail_target)

        if not dm:
            raise Exception("No dmail found.")

        if not x_int:
            print("Encrypted dmail data=[\n{}].".format(mutil.hex_dump(dm)))
        else:
            print("Subject: {}\n".format(dm.subject))

            if dm.sender_pubkey:
                print("From: {}"\
                    .format(mbase32.encode(enc.generate_ID(dm.sender_pubkey))))

            i = 0
            for part in dm.parts:
                print("DmailPart[{}]:\n    mime-type=[{}]\n    data=[{}]\n"\
                    .format(i, part.mime_type, part.data))
                i += 1

    log.info("Disconnecting.")

    yield from mc.disconnect()

    loop.stop()