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
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
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
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))
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
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
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
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
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))
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()
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()
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
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