示例#1
0
def generate_key(prefix):
    assert type(prefix) is str

    key = None

    pool = mp.Pool(WORKERS)

    pipes = []
    refs = []

    try:
        for i in range(WORKERS):
            log.debug("Starting worker.")

            lp, rp = mp.Pipe()

            pool.apply_async(\
                _find_key,\
                args=(rp,))

            pipes.append(lp)
            refs.append(rp)

            lp.send((i, prefix))

        ready = mp.connection.wait(pipes)
        privdata = ready[0].recv()
        key = rsakey.RsaKey(privdata=privdata)
    except Exception:
        log.exception("Exception generating key.")

    pool.terminate()

    return key
示例#2
0
文件: peer.py 项目: tailsjoin/morphis
    def __init__(self, engine, dbpeer=None):
        self.engine = engine

        self.version = None
        self.full_node = False

        self.dbid = None
        self.distance = None
        self.direction = None

        self.address = None

        self.node_key = None
        self.node_id = None
        self.channel_handler = ChannelHandler(self)
        self.connection_handler = ConnectionHandler(self)

        self.connection_coop_lock = asyncio.Lock()

        if dbpeer:
            self.dbid = dbpeer.id
            if dbpeer.pubkey:
                self.node_key = rsakey.RsaKey(dbpeer.pubkey)
            self.node_id = dbpeer.node_id
            self.distance = dbpeer.distance
            self.direction = dbpeer.direction

        self._protocol = None
示例#3
0
    def send_dmail_text(self, subject, message_text):
        if message_text.startswith("from: "):
            p0 = message_text.find('\n')
            m_from_asymkey = rsakey.RsaKey(\
                privdata=base58.decode(message_text[6:p0]))
            p0 += 1
        else:
            p0 = 0
            m_from_asymkey = None

        m_dest_ids = []
        while message_text.startswith("to: ", p0):
            p1 = message_text.find('\n')
            m_dest_enc = message_text[p0 + 4:p1]
            m_dest_id, sig_bits = mutil.decode_key(m_dest_enc)
            m_dest_ids.append((m_dest_enc, m_dest_id, sig_bits))
            p0 = p1 + 1

        date = mutil.utc_datetime()

        if message_text[p0] == '\n':
            p0 += 1

        message_text = message_text[p0:]

        storing_nodes = 0

        for dest_id in m_dest_ids:
            storing_nodes += yield from self.send_dmail(\
                m_from_asymkey, dest_id, subject, date, message_text)

        return storing_nodes
示例#4
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))
示例#5
0
文件: node.py 项目: sahabi/morphis
    def _load_key(self):
        key_filename = "data/node_key-rsa{}.mnk".format(self.instance_postfix)

        if os.path.exists(key_filename):
            log.info("Node private key file found, loading.")
            return rsakey.RsaKey(filename=key_filename)
        else:
            log.info("Node private key file missing, generating.")
            node_key = rsakey.RsaKey.generate(bits=4096)
            node_key.write_private_key_file(key_filename)
            return node_key
示例#6
0
文件: mn1.py 项目: tailsjoin/morphis
def connectTaskInsecure(protocol, server_mode):
    m = mnetpacket.SshKexdhReplyMessage()
    if server_mode:
        m.host_key = protocol.server_key.asbytes()
    else:
        m.host_key = protocol.client_key.asbytes()
    m.f = 42
    m.signature = b"test"
    m.encode()

    protocol.write_packet(m)

    pkt = yield from protocol.read_packet()
    if not pkt:
        return False

    m = mnetpacket.SshKexdhReplyMessage(pkt)

    if server_mode:
        if protocol.client_key:
            if protocol.client_key.asbytes() != m.host_key:
                raise SshException("Key provided by client differs from that which we were expecting.")
        else:
            protocol.client_key = rsakey.RsaKey(m.host_key)
    else:
        if protocol.server_key:
            if protocol.server_key.asbytes() != m.host_key:
                raise SshException("Key provided by server differs from that which we were expecting.")
        else:
            protocol.server_key = rsakey.RsaKey(m.host_key)

    r = yield from protocol.connection_handler.peer_authenticated(protocol)
    if not r:
        # Peer is rejected for some reason by higher level.
        protocol.close()
        return False

    return True
示例#7
0
文件: mn1.py 项目: tailsjoin/morphis
    def verify_server_key(self, key_data, sig):
        if self.server_key:
            if self.server_key.asbytes() != key_data:
                raise SshException("Key provided by server differs from that"\
                    " which we were expecting (address=[{}])."\
                        .format(self.address))
        else:
            self.server_key = rsakey.RsaKey(key_data)

        if not self.server_key.verify_ssh_sig(self.h, sig):
            raise SshException("Signature verification failed (address=[{}])."\
                .format(self.address))

        log.info("Signature validated correctly!")

        r = yield from self.connection_handler.peer_authenticated(self)

        return r
示例#8
0
    def _process_dmail_v2(self, key, x, tb, data_rw):
        dw = DmailWrapper(tb.buf, mp.TargetedBlock.BLOCK_OFFSET)

        if dw.ssm != "mdh-v1":
            raise DmailException(\
                "Unrecognized key exchange method in dmail [{}]."\
                    .format(dw.ssm))

        # Calculate the shared secret.
        kex = dhgroup14.DhGroup14()
        kex.x = x
        kex.generate_e()
        kex.f = dw.ssf

        if dw.sse != kex.e:
            raise DmailException(\
                "Dmail [{}] is encrypted with a different e [{}] than"\
                " the specified x resulted in [{}]."\
                    .format(mbase32.encode(data_rw.data_key), dw.sse, kex.e))

        kex.calculate_k()

        # Generate the AES-256 encryption key.
        key = self._generate_encryption_key(tb.target_key, kex.k)

        # Decrypt the data.
        data = enc.decrypt_data_block(dw.data_enc, key)

        if not data:
            raise DmailException("Dmail data was empty.")

        dmail = Dmail(data)

        if dmail.signature:
            pubkey = rsakey.RsaKey(dmail.sender_pubkey)
            valid_sig =\
                pubkey.verify_rsassa_pss_sig(\
                    data[:dmail.signature_offset], dmail.signature)

            return dmail, valid_sig
        else:
            return dmail, False
示例#9
0
    def do_storeukeyenc(self, arg):
        "<KEY> <DATA> <VERSION> <STOREKEY> [PATH] store base58 encoded DATA"
        " with base58 encoded private KEY."

        args = arg.split(' ')

        key = rsakey.RsaKey(privdata=base58.decode(args[0]))
        data = base58.decode(args[1])
        version = int(args[2])
        storekey = bool(args[3])
        path = args[4] if len(args) > 4 else None

        def key_callback(data_key):
            self.writeln("data_key=[{}].".format(mbase32.encode(data_key)))

        start = datetime.today()

        yield from multipart.store_data(\
            self.peer.engine, data, privatekey=key, path=path,\
            version=version, key_callback=key_callback)

        diff = datetime.today() - start
        self.writeln("multipart.store_data(..) took: {}.".format(diff))
示例#10
0
    def _do_POST(self, rpath):
        log.info("POST; rpath=[{}].".format(rpath))

        if rpath != ".upload/upload":
            yield from maalstroom.dmail.serve_post(self, rpath)
            return

        if not self.connection_count:
            self.send_error("No connected nodes; cannot upload to the"\
                " network.")
            return

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

        version = None
        path = None
        mime_type = None

        if self.handler.headers["Content-Type"]\
                == "application/x-www-form-urlencoded":
            log.debug("Content-Type=[application/x-www-form-urlencoded].")

            data = yield from self.read_request()
            privatekey = None
        else:
            if log.isEnabledFor(logging.DEBUG):
                log.debug("Content-Type=[{}]."\
                    .format(self.handler.headers["Content-Type"]))

            data = yield from self.read_request()

            form = cgi.FieldStorage(\
                fp=io.BytesIO(data),\
                headers=self.handler.headers,\
                environ={\
                    "REQUEST_METHOD": "POST",\
                    "CONTENT_TYPE": self.handler.headers["Content-Type"]})

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

            formelement = form["fileToUpload"]
            filename = formelement.filename
            data = formelement.file.read()

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

            privatekey = form["privateKey"].value

            if privatekey and privatekey != "${PRIVATE_KEY}":
                if log.isEnabledFor(logging.INFO):
                    log.info("privatekey=[{}].".format(privatekey))

                privatekey = base58.decode(privatekey)

                privatekey = rsakey.RsaKey(privdata=privatekey)

                path = form["path"].value.encode()
                version = form["version"].value
                if not version:
                    version = 0
                else:
                    version = int(version)
                mime_type = form["mime_type"].value
            else:
                privatekey = None

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

        if not privatekey:
            assert not version and not path and not mime_type

        try:
            key_callback = KeyCallback()

            yield from multipart.store_data(\
                self.node.chord_engine, data, privatekey=privatekey,\
                path=path, version=version, key_callback=key_callback,\
                mime_type=mime_type)
        except asyncio.TimeoutError:
            self.send_error(errcode=408)
        except Exception as e:
            log.exception("send_store_data(..)")
            self.send_exception(e)

        if key_callback.data_key:
            enckey = mbase32.encode(key_callback.data_key)
            if privatekey and path:
                url = "{}{}/{}"\
                    .format(\
                        self.handler.maalstroom_url_prefix_str,\
                        enckey,\
                        path.decode("UTF-8"))
            else:
                url = "{}{}"\
                    .format(\
                        self.handler.maalstroom_url_prefix_str,\
                        enckey)

            if privatekey:
                message = '<a id="key" href="{}">updateable key link</a>'\
                    .format(url)

                if key_callback.referred_key:
                    message +=\
                        '<br/><a id="referred_key" href="{}{}">perma link</a>'\
                            .format(\
                                self.handler.maalstroom_url_prefix_str,\
                                mbase32.encode(key_callback.referred_key))
            else:
                message = '<a id="key" href="{}">perma link</a>'.format(url)

            self.send_response(200)
            self.send_header("Content-Type", "text/html")
            self.send_header("Content-Length", len(message))
            self.end_headers()

            self.write(bytes(message, "UTF-8"))
            self.finish_response()
示例#11
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()
示例#12
0
文件: mn1.py 项目: tailsjoin/morphis
def connectTaskSecure(protocol, server_mode):
    # Send KexInit packet.
    opobj = mnetpacket.SshKexInitMessage()
    opobj.cookie = os.urandom(16)
#    opobj.kex_algorithms = "diffie-hellman-group-exchange-sha256"
    opobj.kex_algorithms = "diffie-hellman-group14-sha1"
    opobj.server_host_key_algorithms = "ssh-rsa"
    opobj.encryption_algorithms_client_to_server = "aes256-cbc"
    opobj.encryption_algorithms_server_to_client = "aes256-cbc"
#    opobj.mac_algorithms_client_to_server = "hmac-sha2-512"
#    opobj.mac_algorithms_server_to_client = "hmac-sha2-512"
    opobj.mac_algorithms_client_to_server = "hmac-sha1"
    opobj.mac_algorithms_server_to_client = "hmac-sha1"
    opobj.compression_algorithms_client_to_server = "none"
    opobj.compression_algorithms_server_to_client = "none"
    opobj.encode()

    protocol.local_kex_init_message = opobj.buf

    protocol.write_packet(opobj)

    # Read KexInit packet.
    packet = yield from protocol.read_packet()
    if not packet:
        return False

    if log.isEnabledFor(logging.DEBUG):
        log.debug("X: Received packet [{}].".format(hex_dump(packet)))

    packet_type = mnetpacket.SshPacket.parse_type(packet)

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

    if packet_type != 20:
        log.warning("Peer sent unexpected packet_type[{}], disconnecting.".format(packet_type))
        protocol.close()
        return False

    protocol.remote_kex_init_message = packet

    pobj = mnetpacket.SshKexInitMessage(packet)
    if log.isEnabledFor(logging.DEBUG):
        log.debug("cookie=[{}].".format(pobj.cookie))
    if log.isEnabledFor(logging.INFO):
        log.info("keyExchangeAlgorithms=[{}].".format(pobj.kex_algorithms))

    protocol.waitingForNewKeys = True

#    ke = kex.KexGroup14(protocol)
#    log.info("Calling start_kex()...")
#    r = yield from ke.do_kex()
    ke = kexdhgroup14sha1.KexDhGroup14Sha1(protocol)
    log.info("Calling kex->run()...")
    r = yield from ke.run()

    if not r:
        # Client is rejected for some reason by higher level.
        protocol.close()
        return False

    # Setup encryption now that keys are exchanged.
    protocol.init_outbound_encryption()

    if not protocol.server_mode:
        """ Server gets done automatically since parameters are always there
            before NEWKEYS is received, but client the parameters and NEWKEYS
            message may come in the same tcppacket, so the auto part just turns
            off inbound processing and waits for us to call
            init_inbound_encryption when we have the parameters ready. """
        protocol.init_inbound_encryption()
        protocol.set_inbound_enabled(True)

    packet = yield from protocol.read_packet()
    if not packet:
        return False

    m = mnetpacket.SshNewKeysMessage(packet)
    log.debug("Received SSH_MSG_NEWKEYS.")

    if protocol.server_mode:
        packet = yield from protocol.read_packet()
        if not packet:
            return False

#        m = mnetpacket.SshPacket(None, packet)
#        log.info("X: Received packet (type={}) [{}].".format(m.packet_type, packet))
        m = mnetpacket.SshServiceRequestMessage(packet)
        log.info("Service requested [{}].".format(m.service_name))

        if m.service_name != "ssh-userauth":
            raise SshException("Remote end requested unexpected service (name=[{}]).".format(m.service_name))

        mr = mnetpacket.SshServiceAcceptMessage()
        mr.service_name = "ssh-userauth"
        mr.encode()

        protocol.write_packet(mr)

        packet = yield from protocol.read_packet()
        if not packet:
            return False

        m = mnetpacket.SshUserauthRequestMessage(packet)
        log.info("Userauth requested with method=[{}].".format(m.method_name))

        if m.method_name == "none":
            mr = mnetpacket.SshUserauthFailureMessage()
            mr.auths = "publickey"
            mr.partial_success = False
            mr.encode()

            protocol.write_packet(mr)

            packet = yield from protocol.read_packet()
            if not packet:
                return False

            m = mnetpacket.SshUserauthRequestMessage(packet)
            log.info("Userauth requested with method=[{}].".format(m.method_name))

        if m.method_name != "publickey":
            raise SshException("Unhandled client auth method [{}].".format(m.method_name))
        if m.algorithm_name != "ssh-rsa":
            raise SshException("Unhandled client auth algorithm [{}].".format(m.algorithm_name))

        log.debug("m.signature_present()={}.".format(m.signature_present))

        if not m.signature_present:
            mr = mnetpacket.SshUserauthPkOkMessage()
            mr.algorithm_name = m.algorithm_name
            mr.public_key = m.public_key
            mr.encode()

            protocol.write_packet(mr)

            packet = yield from protocol.read_packet()
            if not packet:
                return False

            m = mnetpacket.SshUserauthRequestMessage(packet)
            log.info("Userauth requested with method=[{}].".format(m.method_name))

            if m.method_name != "publickey":
                raise SshException("Unhandled client auth method [{}].".format(m.method_name))
            if m.algorithm_name != "ssh-rsa":
                raise SshException("Unhandled client auth algorithm [{}].".format(m.algorithm_name))

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

        if protocol.client_key:
            if protocol.client_key.asbytes() != m.public_key:
                raise SshException("Key provided by client differs from that which we were expecting.")
        else:
            protocol.client_key = rsakey.RsaKey(m.public_key)

        buf = bytearray()
        buf += sshtype.encodeBinary(protocol.session_id)
        buf += packet[:-m.signature_length]

        r = protocol.client_key.verify_ssh_sig(buf, m.signature)

        log.info("Userauth signature check result: [{}].".format(r))
        if not r:
            raise SshException("Signature and key provided by client did not match.")

        r = yield from protocol.connection_handler.peer_authenticated(protocol)
        if not r:
            # Client is rejected for some reason by higher level.
            protocol.close()
            return False

        mr = mnetpacket.SshUserauthSuccessMessage()
        mr.encode()

        protocol.write_packet(mr)
    else:
        # client mode.
        m = mnetpacket.SshServiceRequestMessage()
        m.service_name = "ssh-userauth"
        m.encode()

        protocol.write_packet(m)

        packet = yield from protocol.read_packet()
        if not packet:
            return False

        m = mnetpacket.SshServiceAcceptMessage(packet)
        log.info("Service request accepted [{}].".format(m.service_name))

        mr = mnetpacket.SshUserauthRequestMessage()
        mr.user_name = "dev"
        mr.service_name = "ssh-connection"
        mr.method_name = "publickey"
        mr.signature_present = True
        mr.algorithm_name = "ssh-rsa"

        ckey = protocol.client_key
        mr.public_key = ckey.asbytes()

        mr.encode()

        mrb = bytearray()
        mrb += sshtype.encodeBinary(protocol.session_id)
        mrb += mr.buf

        sig = sshtype.encodeBinary(ckey.sign_ssh_data(mrb))

        mrb = mr.buf
        assert mr.buf == mrb
        mrb += sig

        protocol.write_packet(mr)

        packet = yield from protocol.read_packet()
        if not packet:
            return False

        m = mnetpacket.SshUserauthSuccessMessage(packet)
        log.info("Userauth accepted.")

    log.info("Connect task done (server={}).".format(server_mode))

#    if not server_mode:
#        protocol.close()

    return True
示例#13
0
    def fetch_dmail(self, key, x=None, target_key=None):
        "Fetch the Dmail referred to by key from the network."\
        " Returns a Dmail object, not a db.DmailMessage object."

        data_rw = yield from self.task_engine.send_get_targeted_data(key)

        data = data_rw.data

        if not data:
            return None, None
        if not x:
            return data, None

        tb = mp.TargetedBlock(data)

        if target_key:
            if tb.target_key != target_key:
                tb_tid_enc = mbase32.encode(tb.target_key)
                tid_enc = mbase32.encode(target_key)
                raise DmailException(\
                    "TargetedBlock->target_key [{}] does not match request"\
                    " [{}]."\
                        .format(tb_tid_enc, tid_enc))

        dw = DmailWrapper(tb.buf, mp.TargetedBlock.BLOCK_OFFSET)

        if dw.ssm != "mdh-v1":
            raise DmailException(\
                "Unrecognized key exchange method in dmail [{}]."\
                    .format(dw.ssm))

        kex = dhgroup14.DhGroup14()
        kex.x = x
        kex.generate_e()
        kex.f = dw.ssf

        if dw.sse != kex.e:
            raise DmailException(\
                "Dmail [{}] is encrypted with a different e [{}] than"\
                " the specified x resulted in [{}]."\
                    .format(mbase32.encode(data_rw.data_key), dw.sse, kex.e))

        kex.calculate_k()

        key = self._generate_encryption_key(tb.target_key, kex.k)

        data = enc.decrypt_data_block(dw.data_enc, key)

        if not data:
            raise DmailException("Dmail data was empty.")

        dmail = Dmail(data, 0, dw.data_len)

        if dw.signature:
            signature = dw.signature
            pubkey = rsakey.RsaKey(dmail.sender_pubkey)
            valid_sig = pubkey.verify_rsassa_pss_sig(dw.data_enc, signature)

            return dmail, valid_sig
        else:
            return dmail, False