def connect(self, name=None, remoteId=None, ha=None, verKeyRaw=None, publicKeyRaw=None): """ Connect to the node specified by name. """ if not name: raise ValueError('Remote name should be specified') if name in self.remotes: remote = self.remotes[name] else: publicKey = z85.encode(publicKeyRaw) if publicKeyRaw else self.getPublicKey(name) verKey = z85.encode(verKeyRaw) if verKeyRaw else self.getVerKey(name) if not ha or not publicKey or (self.isRestricted and not verKey): raise ValueError('{} doesnt have enough info to connect. ' 'Need ha, public key and verkey. {} {} {}'. format(name, ha, verKey, publicKey)) remote = self.addRemote(name, ha, verKey, publicKey) public, secret = self.selfEncKeys remote.connect(self.ctx, public, secret) logger.info("{} looking for {} at {}:{}". format(self, name or remote.name, *remote.ha), extra={"cli": "PLAIN", "tags": ["node-looking"]}) # This should be scheduled as an async task self.sendPingPong(remote, is_ping=True) return remote.uid
def generate(self): verif_key, sig_key = crypto_sign_seed_keypair(self.seed) public_key, secret_key = ep2c(verif_key), es2c(sig_key) public_key_file = self.cert_file_from_key(public_key=z85.encode(public_key).decode("utf-8"), private_key=None) private_key_file = self.cert_file_from_key(public_key=z85.encode(public_key).decode("utf-8"), private_key=z85.encode(secret_key).decode("utf-8")) verif_key_file = self.cert_file_from_key(public_key=z85.encode(verif_key).decode("utf-8"), private_key=None) sig_key_file = self.cert_file_from_key(public_key=z85.encode(verif_key).decode("utf-8"), private_key=z85.encode(sig_key[:32]).decode("utf-8")) public_key = base58.b58encode(public_key).decode("utf-8") verif_key = base58.b58encode(verif_key).decode("utf-8") sk, pk, key_proof = self.bls_generator.generate() bls = Bls(pk=pk, sk=sk, key_pop=key_proof) node = Crypto(name=self.identity_name, public_key=public_key, public_key_file=public_key_file, secret_key=secret_key, secret_key_file=private_key_file, sig_key=sig_key, sig_key_file=sig_key_file, verif_key=verif_key, verif_key_file=verif_key_file, bls_keys=bls, is_client=False) client = Crypto(name=self.identity_name, public_key=public_key, public_key_file=public_key_file, secret_key=secret_key, secret_key_file=private_key_file, sig_key=sig_key, sig_key_file=sig_key_file, verif_key=verif_key, verif_key_file=verif_key_file, bls_keys=bls, is_client=True) return Identity( name=self.identity_name, vault_path=self.vault_path, node=node, client=client, did=self.did, seed=self.seed )
def to_json(self, redact=True): """ Output the current node details in json @return JSON style dictionary """ data = {'node_id': z85.encode(self.node_id), 'address': self.address, 'port': self.port, 'hostname': self.hostname, 'discovered': self.discovered, 'first_contact': self.first_contact, 'last_contact': self.last_contact, 'last_failure': self.last_failure, 'latency_ms': self.latency, 'msg_loss': self.msg_loss, 'failures': self.failures, 'queries_in': self.queries_in, 'queries_out': self.queries_out, 'responses_in': self.responses_in, 'responses_out': self.responses_out, 'status': self.state, } if not redact and self.secret_key: data.update['secret_key'] = z85.encode(self.secret_key) return data
def to_json(self, redact=True): """ Output the current node details in json @return JSON style dictionary """ data = { 'node_id': z85.encode(self.node_id), 'address': self.address, 'port': self.port, 'hostname': self.hostname, 'discovered': self.discovered, 'first_contact': self.first_contact, 'last_contact': self.last_contact, 'last_failure': self.last_failure, 'latency_ms': self.latency, 'msg_loss': self.msg_loss, 'failures': self.failures, 'queries_in': self.queries_in, 'queries_out': self.queries_out, 'responses_in': self.responses_in, 'responses_out': self.responses_out, 'status': self.state, } if not redact and self.secret_key: data.update['secret_key'] = z85.encode(self.secret_key) return data
def connectTo(self, ha, verkey, pubkey): if not self.isConnectedTo(ha=ha): assert pubkey, 'Need public key to connect to {}'.format(ha) zvk = z85.encode(friendlyToRaw(verkey)) if verkey else None zpk = z85.encode(friendlyToRaw(pubkey)) self.connect(name=verkey or pubkey, ha=ha, verKey=zvk, publicKey=zpk) else: logger.debug('{} already connected {}'.format(self, ha))
def _authenticate_curve(self, domain, client_key): """CURVE ZAP authentication""" allowed = False reason = b"" if self.allow_any: allowed = True reason = b"OK" self.log.debug("ALLOWED (CURVE allow any client)") elif self.credentials_providers != {}: # If no explicit domain is specified then use the default domain if not domain: domain = '*' if domain in self.credentials_providers: z85_client_key = z85.encode(client_key) # Callback to check if key is Allowed if (self.credentials_providers[domain].callback( domain, z85_client_key)): allowed = True reason = b"OK" else: reason = b"Unknown key" status = "ALLOWED" if allowed else "DENIED" self.log.debug( "%s (CURVE auth_callback) domain=%s client_key=%s", status, domain, z85_client_key, ) else: reason = b"Unknown domain" else: # If no explicit domain is specified then use the default domain if not domain: domain = '*' if domain in self.certs: # The certs dict stores keys in z85 format, convert binary key to z85 bytes z85_client_key = z85.encode(client_key) if self.certs[domain].get(z85_client_key): allowed = True reason = b"OK" else: reason = b"Unknown key" status = "ALLOWED" if allowed else "DENIED" self.log.debug( "%s (CURVE) domain=%s client_key=%s", status, domain, z85_client_key, ) else: reason = b"Unknown domain" return allowed, reason
def encode(data): data_bin = bytes(data) while len(data_bin) % 4: data_bin = data_bin + "\0" if verbose: print "Encoding {" + data_bin + "} (total " + str(len(data_bin)) + " bytes)" print z85.encode(data_bin) else: sys.stdout.write(z85.encode(data_bin))
def __init__(self, signing_key, witness_list, url, sbb_index): self.log = get_logger("SubBlockBuilder_{}".format(sb_index)) # Comment out below for more granularity in debugging # self.log.setLevel(logging.INFO) #self.log.important("SubBlockBuilder started with url {}".format(url)) # Register signal handler to teardown signal.signal(signal.SIGTERM, self._signal_teardown) # need to revisit this when threading strategy is clear self.loop = asyncio.new_event_loop() asyncio.set_event_loop(self.loop) self.signing_key = signing_key # witness_list should be comma separated list of ip:vk self.witness_table = self._parse_witness_list(witness_list) self.url = url self.sbb_index = sbb_index self.block_num = (int) sbb_index / 16 # hard code this for now self.sub_block_num = (int) sb_index % 16 self.num_txs = 0 self.num_sub_blocks = 0 self.tasks = [] #SenecaInterpreter connect with BlockManager (parent process that spawned this one) self.context = zmq.asyncio.Context() self.socket = self.context.socket(zmq.PAIR) # For communication with main process self.socket.connect(self.url) # do we need this still? or do we move it to a util methods self.verifying_key = wallet.get_vk(self.signing_key) skg = SigningKey(seed=bytes.fromhex(sk)) self.vk = skg.verify_key.encode().hex() self.public_key = self.vk2pk(self.vk) self.private_key = crypto_sign_ed25519_sk_to_curve25519(skg._signing_key).hex() priv = PrivateKey(bytes.fromhex(self.private_key)) publ = priv.public_key self.public_key = public_key = encode(publ._public_key) self.secret = secret_key = encode(priv._private_key) self.pending_txs = LinkedHashTable() self.interpreter = SenecaInterpreter() self._recently_seen = CappedSet(max_size=DUPE_TABLE_SIZE) try: self._subscribe_to_witnesses() # start event loop and start listening witness sockets as well as mgr self.run_loop_second_time() except Exception as e: err_msg = '\n' + '!' * 64 + '\nSBB terminating with exception:\n' + str(traceback.format_exc()) err_msg += '\n' + '!' * 64 + '\n' self.log.error(err_msg) finally: self._teardown()
def genkeys(sk_hex): sk = SigningKey(seed=bytes.fromhex(sk_hex)) vk = sk.verify_key.encode().hex() public_key = VerifyKey(bytes.fromhex(vk)).to_curve25519_public_key()._public_key private_key = crypto_sign_ed25519_sk_to_curve25519(sk._signing_key) return { 'sk': sk_hex, 'vk': vk, 'public_key': public_key.hex(), 'private_key': encode(private_key), 'curve_key': encode(public_key) }
def createEncAndSigKeys(enc_key_dir, sig_key_dir, name, seed=None): seed = seed or randomSeed() if isinstance(seed, str): seed = seed.encode() # ATTENTION: Passing `seed` encoded to bytes or not in # `crypto_sign_seed_keypair` will generate different keypairs verif_key, sig_key = crypto_sign_seed_keypair(seed) createCertsFromKeys(sig_key_dir, name, z85.encode(verif_key), z85.encode(sig_key[:32])) public_key, secret_key = ep2c(verif_key), es2c(sig_key) createCertsFromKeys(enc_key_dir, name, z85.encode(public_key), z85.encode(secret_key)) return (public_key, secret_key), (verif_key, sig_key)
def initRemoteKeys(name, remoteName, baseDir, verkey, override=False): homeDir = ZStack.homeDirPath(baseDir, name) verifDirPath = ZStack.verifDirPath(homeDir) pubDirPath = ZStack.publicDirPath(homeDir) for d in (homeDir, verifDirPath, pubDirPath): os.makedirs(d, exist_ok=True) if isHex(verkey): verkey = unhexlify(verkey) createCertsFromKeys(verifDirPath, remoteName, z85.encode(verkey)) public_key = ed25519PkToCurve25519(verkey) createCertsFromKeys(pubDirPath, remoteName, z85.encode(public_key))
def __init__(self, seed=None): if isinstance(seed, str): seed = bytes.fromhex(seed) if seed is None: seed = secrets.token_bytes(32) self.sk = nacl.signing.SigningKey(seed=seed) self.vk = self.sk.verify_key self.curve_sk = z85.encode( self.sk.to_curve25519_private_key().encode()) self.curve_vk = z85.encode(self.vk.to_curve25519_public_key().encode())
def connect(self, name=None, remoteId=None, ha=None, verKeyRaw=None, publicKeyRaw=None): """ Connect to the node specified by name. """ if not name: raise ValueError('Remote name should be specified') publicKey = None if name in self.remotes: remote = self.remotes[name] else: publicKey = z85.encode( publicKeyRaw) if publicKeyRaw else self.getPublicKey(name) verKey = z85.encode(verKeyRaw) if verKeyRaw else self.getVerKey( name) if not ha or not publicKey or (self.isRestricted and not verKey): raise ValueError( '{} doesnt have enough info to connect. ' 'Need ha, public key and verkey. {} {} {}'.format( name, ha, verKey, publicKey)) remote = self.addRemote(name, ha, verKey, publicKey) public, secret = self.selfEncKeys remote.connect(self.ctx, public, secret) logger.info("{}{} looking for {} at {}:{}".format( CONNECTION_PREFIX, self, name or remote.name, *remote.ha), extra={ "cli": "PLAIN", "tags": ["node-looking"] }) # This should be scheduled as an async task self.sendPingPong(remote, is_ping=True) # re-send previously stashed pings/pongs from unknown remotes logger.trace("{} stashed pongs: {}".format(self.name, str(self._stashed_pongs))) if publicKey in self._stashed_pongs: logger.trace("{} sending stashed pongs to {}".format( self.name, str(publicKey))) self._stashed_pongs.discard(publicKey) self.sendPingPong(name, is_ping=False) return remote.uid
def _authenticate_curve(self, domain, client_key): """CURVE ZAP authentication""" allowed = False reason = b"" if self.allow_any: allowed = True reason = b"OK" self.log.debug("ALLOWED (CURVE allow any client)") elif self.credentials_providers != {}: # If no explicit domain is specified then use the default domain if not domain: domain = '*' if domain in self.credentials_providers: z85_client_key = z85.encode(client_key) # Callback to check if key is Allowed if (self.credentials_providers[domain].callback(domain, z85_client_key)): allowed = True reason = b"OK" else: reason = b"Unknown key" status = "ALLOWED" if allowed else "DENIED" self.log.debug("%s (CURVE auth_callback) domain=%s client_key=%s", status, domain, z85_client_key, ) else: reason = b"Unknown domain" else: # If no explicit domain is specified then use the default domain if not domain: domain = '*' if domain in self.certs: # The certs dict stores keys in z85 format, convert binary key to z85 bytes z85_client_key = z85.encode(client_key) if self.certs[domain].get(z85_client_key): allowed = True reason = b"OK" else: reason = b"Unknown key" status = "ALLOWED" if allowed else "DENIED" self.log.debug("%s (CURVE) domain=%s client_key=%s", status, domain, z85_client_key, ) else: reason = b"Unknown domain" return allowed, reason
def create_from_private_key(cls, private_key): priv = PrivateKey(bytes.fromhex(private_key)) publ = priv.public_key public_key = encode(publ._public_key) secret = encode(priv._private_key) base_filename = join(cls.keys_dir, cls.keyname) public_key_file = "{0}.key".format(base_filename) now = datetime.datetime.now() zmq.auth.certs._write_key_file( public_key_file, zmq.auth.certs._cert_public_banner.format(now), public_key) return public_key, secret
async def test_trusted_curve_with_wrong_peer_public_key( loop, unused_tcp_port_factory): from pseud import Client, Server from pseud.utils import register_rpc server_id = b'server' port = unused_tcp_port_factory() endpoint = f'tcp://127.0.0.1:{port}' server_public, server_secret = zmq.curve_keypair() server = Server(server_id, security_plugin='trusted_curve', public_key=server_public, secret_key=server_secret, loop=loop) server.bind(endpoint) alice_public, alice_secret = \ server.auth_backend.known_identities[b'alice'] client = Client(server_id, user_id=b'alice', security_plugin='trusted_curve', public_key=alice_public, secret_key=alice_secret, peer_public_key=z85.encode(b'R' * 32), timeout=.5, loop=loop) client.connect(endpoint) assert server.socket.mechanism == zmq.CURVE assert client.socket.mechanism == zmq.CURVE register_rpc(name='string.lower')(str.lower) async with server, client: with pytest.raises(asyncio.TimeoutError): await client.string.lower('BAR')
def __str__(self): """ @return: Brief string representation of this node. """ return '{0}:{1} - {2} ({3})'.format(self.address, self.port, z85.encode(self.node_id), self.hostname)
def _authenticate_curve(self, domain, client_key): """CURVE ZAP authentication""" allowed = False reason = b"" if self.allow_any: allowed = True reason = b"OK" self.log.debug("ALLOWED (CURVE allow any client)") else: # If no explicit domain is specified then use the default domain if not domain: domain = '*' if domain in self.certs: # The certs dict stores keys in z85 format, convert binary key to z85 bytes z85_client_key = z85.encode(client_key) if z85_client_key in self.certs[domain] or self.certs[domain] == b'OK': allowed = True reason = b"OK" else: reason = b"Unknown key" status = "ALLOWED" if allowed else "DENIED" self.log.debug("%s (CURVE) domain=%s client_key=%s", status, domain, z85_client_key, ) else: reason = b"Unknown domain" return allowed, reason
def test_trusted_curve_with_wrong_peer_public_key(): from pseud._gevent import Client, Server server_id = 'server' endpoint = 'inproc://{}'.format(__name__) endpoint = 'tcp://127.0.0.1:8998' server_public, server_secret = zmq.curve_keypair() client_public, client_secret = zmq.curve_keypair() client = Client(server_id, security_plugin='trusted_curve', public_key=client_public, secret_key=client_secret, peer_public_key=z85.encode('R' * 32)) server = Server(server_id, security_plugin='trusted_curve', public_key=server_public, secret_key=server_secret) server.bind(endpoint) client.connect(endpoint) assert server.socket.mechanism == zmq.CURVE assert client.socket.mechanism == zmq.CURVE server.start() future = client.string.lower('BAR') with pytest.raises(Timeout): future.get(timeout=0.1) server.stop() client.stop()
def _authenticate_curve(self, domain, client_key): """CURVE ZAP authentication""" allowed = False reason = b"" if self.allow_any: allowed = True reason = b"OK" self.log.debug("ALLOWED (CURVE allow any client)") else: # If no explicit domain is specified then use the default domain if not domain: domain = '*' if domain in self.certs: # The certs dict stores keys in z85 format, convert binary key to z85 bytes z85_client_key = z85.encode(client_key) if z85_client_key in self.certs[domain] or self.certs[ domain] == b'OK': allowed = True reason = b"OK" else: reason = b"Unknown key" status = "ALLOWED" if allowed else "DENIED" self.log.debug( "%s (CURVE) domain=%s client_key=%s", status, domain, z85_client_key, ) else: reason = b"Unknown domain" return allowed, reason
def test_generate_from_public_key(self): self.ironhouse.create_from_public_key(encode(self.public_key.encode())) self.assertTrue(listdir(self.ironhouse.public_keys_dir), 'public keys dir not created') self.assertTrue( exists('{}/ironhouse.key'.format(self.ironhouse.public_keys_dir)), 'public key not generated')
def _authenticate_curve(self, domain, client_key): ''' Perform ZAP authentication check for CURVE mechanism ''' allowed = False reason = b"" if self.allow_any: allowed = True reason = b"OK" logging.debug("ALLOWED (CURVE allow any client)") else: # If no explicit domain is specified then use the default domain if not domain: domain = '*' if domain in self.certs: # The certs dict stores keys in z85 format, convert binary key to z85 bytes z85_client_key = z85.encode(client_key) if z85_client_key in self.certs[domain]: allowed = True reason = b"OK" else: reason = b"Unknown key" status = "DENIED" if allowed: status = "ALLOWED" logging.debug("{0} (CURVE) domain={1} client_key={2}".format(status, domain, z85_client_key)) else: reason = b"Unknown domain" return allowed, reason
def connect(self, name=None, remoteId=None, ha=None, verKeyRaw=None, publicKeyRaw=None): """ Connect to the node specified by name. """ if not name: raise ValueError('Remote name should be specified') publicKey = None if name in self.remotes: remote = self.remotes[name] else: publicKey = z85.encode( publicKeyRaw) if publicKeyRaw else self.getPublicKey(name) verKey = z85.encode( verKeyRaw) if verKeyRaw else self.getVerKey(name) if not ha or not publicKey or (self.isRestricted and not verKey): raise ValueError('{} doesnt have enough info to connect. ' 'Need ha, public key and verkey. {} {} {}'. format(name, ha, verKey, publicKey)) remote = self.addRemote(name, ha, verKey, publicKey) public, secret = self.selfEncKeys remote.connect(self.ctx, public, secret) logger.info("{}{} looking for {} at {}:{}" .format(CONNECTION_PREFIX, self, name or remote.name, *remote.ha), extra={"cli": "PLAIN", "tags": ["node-looking"]}) # This should be scheduled as an async task self.sendPingPong(remote, is_ping=True) # re-send previously stashed pings/pongs from unknown remotes logger.trace("{} stashed pongs: {}".format(self.name, str(self._stashed_pongs))) if publicKey in self._stashed_pongs: logger.trace("{} sending stashed pongs to {}".format(self.name, str(z85_to_friendly(publicKey)))) self._stashed_pongs.discard(publicKey) self.sendPingPong(name, is_ping=False) return remote.uid
def _generate_coupon(): """ Generate coupon using current month/year :return: """ now = datetime.datetime.now() month = now.strftime('%b').upper() year = now.strftime('%y') return z85.encode('{month}{year}-99'.format(month=month, year=year))
def id_for_key(key): """ Given an object key, map this to a node id for storage. @param key: String key for storing an object. @return: node_id of a target node for the given key. """ hasher = hashlib.sha256() hasher.update(key) return z85.encode(hasher.digest())
def decode_key(key): '''Parse and return a Z85 encoded key from other encodings.''' length = len(key) if length == 40: return key elif length == 43: return z85.encode(base64.urlsafe_b64decode(key + '=')) elif length == 44: return z85.encode(base64.urlsafe_b64decode(key)) elif length == 54: return base64.urlsafe_b64decode(key + '==') elif length == 56: return base64.urlsafe_b64decode(key) elif length == 64: return z85.encode(binascii.unhexlify(key)) elif length == 80: return binascii.unhexlify(key) raise ValueError('unknown key encoding')
def connect(self, name=None, remoteId=None, ha=None, verKeyRaw=None, publicKeyRaw=None): """ Connect to the node specified by name. """ if not name: raise ValueError('Name needs to be specified') if name not in self.remotes: publicKey = None if not publicKeyRaw: try: publicKey = self.getPublicKey(name) except KeyError: raise PublicKeyNotFoundOnDisk(self.name, name) else: publicKey = z85.encode(publicKeyRaw) verKey = None if not verKeyRaw: try: verKey = self.getVerKey(name) except KeyError: if self.isRestricted: raise VerKeyNotFoundOnDisk(self.name, name) else: verKey = z85.encode(verKeyRaw) if not (ha and publicKey and (not self.isRestricted or verKey)): raise ValueError('{} doesnt have enough info to connect. ' 'Need ha, public key and verkey. {} {} {}'. format(name, ha, verKey, publicKey)) remote = self.addRemote(name, ha, verKey, publicKey) else: remote = self.remotes[name] public, secret = self.selfEncKeys remote.connect(self.ctx, public, secret) logger.info("{} looking for {} at {}:{}". format(self, name or remote.name, *remote.ha), extra={"cli": "PLAIN", "tags": ["node-looking"]}) # This should be scheduled as an async task self.sendPing(remote) return remote.uid
def test_client_secret(self): client_secret = (b"\x7B\xB8\x64\xB4\x89\xAF\xA3\x67" b"\x1F\xBE\x69\x10\x1F\x94\xB3\x89" b"\x72\xF2\x48\x16\xDF\xB0\x1B\x51" b"\x65\x6B\x3F\xEC\x8D\xFD\x08\x88") encoded = z85.encode(client_secret) self.assertEqual(encoded, b"D:)Q[IlAW!ahhC2ac:9*A}h:p?([4%wOTJ%JR%cs") decoded = z85.decode(encoded) self.assertEqual(decoded, client_secret)
def test_server_secret(self): server_secret = (b"\x8E\x0B\xDD\x69\x76\x28\xB9\x1D" b"\x8F\x24\x55\x87\xEE\x95\xC5\xB0" b"\x4D\x48\x96\x3F\x79\x25\x98\x77" b"\xB4\x9C\xD9\x06\x3A\xEA\xD3\xB7") encoded = z85.encode(server_secret) self.assertEqual(encoded, b"JTKVSB%%)wK0E.X)V>+}o?pNmC{O&4W4b!Ni{Lh6") decoded = z85.decode(encoded) self.assertEqual(decoded, server_secret)
def test_client_public(self): client_public = (b"\xBB\x88\x47\x1D\x65\xE2\x65\x9B" b"\x30\xC5\x5A\x53\x21\xCE\xBB\x5A" b"\xAB\x2B\x70\xA3\x98\x64\x5C\x26" b"\xDC\xA2\xB2\xFC\xB4\x3F\xC5\x18") encoded = z85.encode(client_public) self.assertEqual(encoded, b"Yne@$w-vo<fVvi]a<NY6T1ed:M$fCG*[IaLV{hID") decoded = z85.decode(encoded) self.assertEqual(decoded, client_public)
def test_server_public(self): server_public = (b"\x54\xFC\xBA\x24\xE9\x32\x49\x96" b"\x93\x16\xFB\x61\x7C\x87\x2B\xB0" b"\xC1\xD1\xFF\x14\x80\x04\x27\xC5" b"\x94\xCB\xFA\xCF\x1B\xC2\xD6\x52") encoded = z85.encode(server_public) self.assertEqual(encoded, b"rq:rM>}U?@Lns47E1%kR.o@n%FcmmsL/@{H8]yf7") decoded = z85.decode(encoded) self.assertEqual(decoded, server_public)
def test_server_public(self): server_public = \ b"\x54\xFC\xBA\x24\xE9\x32\x49\x96" \ b"\x93\x16\xFB\x61\x7C\x87\x2B\xB0" \ b"\xC1\xD1\xFF\x14\x80\x04\x27\xC5" \ b"\x94\xCB\xFA\xCF\x1B\xC2\xD6\x52" encoded = z85.encode(server_public) self.assertEqual(encoded, b"rq:rM>}U?@Lns47E1%kR.o@n%FcmmsL/@{H8]yf7") decoded = z85.decode(encoded) self.assertEqual(decoded, server_public)
def test_client_secret(self): client_secret = \ b"\x7B\xB8\x64\xB4\x89\xAF\xA3\x67" \ b"\x1F\xBE\x69\x10\x1F\x94\xB3\x89" \ b"\x72\xF2\x48\x16\xDF\xB0\x1B\x51" \ b"\x65\x6B\x3F\xEC\x8D\xFD\x08\x88" encoded = z85.encode(client_secret) self.assertEqual(encoded, b"D:)Q[IlAW!ahhC2ac:9*A}h:p?([4%wOTJ%JR%cs") decoded = z85.decode(encoded) self.assertEqual(decoded, client_secret)
def verify_peers(self): queried = 0 nodes = self.engine.nodetree.get_all_nodes() random.shuffle(nodes) for x in nodes: if x.is_discovered(): self.engine.txmap.create(Ping, [x.address, x.port, z85.encode(x.node_id)], self.engine) queried += 1 if queried >= self.verify_limit: break
def test_client_public(self): client_public = \ b"\xBB\x88\x47\x1D\x65\xE2\x65\x9B" \ b"\x30\xC5\x5A\x53\x21\xCE\xBB\x5A" \ b"\xAB\x2B\x70\xA3\x98\x64\x5C\x26" \ b"\xDC\xA2\xB2\xFC\xB4\x3F\xC5\x18" encoded = z85.encode(client_public) self.assertEqual(encoded, b"Yne@$w-vo<fVvi]a<NY6T1ed:M$fCG*[IaLV{hID") decoded = z85.decode(encoded) self.assertEqual(decoded, client_public)
def test_server_secret(self): server_secret = \ b"\x8E\x0B\xDD\x69\x76\x28\xB9\x1D" \ b"\x8F\x24\x55\x87\xEE\x95\xC5\xB0" \ b"\x4D\x48\x96\x3F\x79\x25\x98\x77" \ b"\xB4\x9C\xD9\x06\x3A\xEA\xD3\xB7" encoded = z85.encode(server_secret) self.assertEqual(encoded, b"JTKVSB%%)wK0E.X)V>+}o?pNmC{O&4W4b!Ni{Lh6") decoded = z85.decode(encoded) self.assertEqual(decoded, server_secret)
def create_from_private_key(self, private_key): priv = PrivateKey(bytes.fromhex(private_key)) publ = priv.public_key self.public_key = public_key = encode(publ._public_key) secret_key = encode(priv._private_key) base_filename = os.path.join(self.keys_dir, self.keyname) secret_key_file = "{0}.key_secret".format(base_filename) public_key_file = "{0}.key".format(base_filename) now = datetime.datetime.now() zmq.auth.certs._write_key_file( public_key_file, zmq.auth.certs._cert_public_banner.format(now), public_key) zmq.auth.certs._write_key_file( secret_key_file, zmq.auth.certs._cert_secret_banner.format(now), public_key, secret_key=secret_key)
def zap_loop(self, sender, **kwargs): self._zap_greenlet = gevent.getcurrent() sock = self.zap_socket time = gevent.core.time blocked = {} wait_list = [] timeout = None while True: events = sock.poll(timeout) now = time() if events: zap = sock.recv_multipart() version = zap[2] if version != b'1.0': continue domain, address, _, kind = zap[4:8] credentials = zap[8:] if kind == b'CURVE': credentials[0] = z85.encode(credentials[0]) elif kind not in [b'NULL', b'PLAIN']: continue response = zap[:4] if self.authenticate(domain, address, kind, credentials): user = dump_user(domain, address, kind, *credentials[:1]) response.extend([b'200', b'SUCCESS', user, b'']) sock.send_multipart(response) else: try: expire, delay = blocked[address] except KeyError: delay = random.random() else: if now >= expire: delay = random.random() else: delay *= 2 if delay > 100: delay = 100 expire = now + delay bisect.bisect(wait_list, (expire, address, response)) blocked[address] = expire, delay while wait_list: expire, address, response = wait_list[0] if now < expire: break wait_list.pop(0) response.extend([b'400', b'FAIL', b'', b'']) sock.send_multipart(response) try: if now >= blocked[address][0]: blocked.pop(address) except KeyError: pass timeout = (wait_list[0][0] - now) if wait_list else None
def test_generate_from_public_key(self): self.ironhouse.daemon_context, self.ironhouse.daemon_auth = self.ironhouse.secure_context( async=True) self.ironhouse.add_public_key(encode(self.public_key.encode())) self.assertTrue(listdir(self.ironhouse.authorized_keys_dir), 'public keys dir not created') self.assertTrue( exists('{}/{}.key'.format(self.ironhouse.authorized_keys_dir, self.ironhouse.keyname)), 'public key not generated') self.ironhouse.daemon_auth.stop()
def verify_peers(self): queried = 0 nodes = self.engine.nodetree.get_all_nodes() random.shuffle(nodes) for x in nodes: if x.is_discovered(): self.engine.txmap.create( Ping, [x.address, x.port, z85.encode(x.node_id)], self.engine) queried += 1 if queried >= self.verify_limit: break
def add_verifying_key(self, vk: str): # Convert to bytes if hex string bvk = bytes.fromhex(vk) try: pk = crypto_sign_ed25519_pk_to_curve25519(bvk) # Error is thrown if the VK is not within the possibility space of the ED25519 algorithm except RuntimeError: self.log.error('ED25519 Cryptographic error. The key provided is not within the cryptographic key space.') return zvk = z85.encode(pk).decode('utf-8') _write_key_file(self.cert_dir / f'{vk}.key', banner=_cert_public_banner, public_key=zvk)
def _zap_handler(self, message): """ http://rfc.zeromq.org/spec:27 """ (zid, delimiter, version, sequence, domain, address, identity, mechanism, key) = message assert version == b'1.0' assert mechanism == b'CURVE' try: user_id = self.trusted_keys[key] except KeyError: user_id = z85.encode(key) reply = [zid, delimiter, version, sequence, b'200', b'OK', user_id, b''] self.zap_socket.send_multipart(reply)
def curve_user_id(self, client_public_key): """Return the User-Id corresponding to a CURVE client's public key Default implementation uses the z85-encoding of the public key. Override to define a custom mapping of public key : user-id This is only called on successful authentication. Parameters ---------- client_public_key: bytes The client public key used for the given message Returns ------- user_id: unicode The user ID as text """ return z85.encode(client_public_key).decode('ascii')
def test_find_nodes(root, closeme): # seed net = Network([], root) net.join() node = net.get_local() seeds = ["{0}:{1}:{2}".format(node['address'], node['port'], node['node_id'])] closeme.append(net) # can find self assert net.find_nodes(node['node_id']) != None # check for consistency across nodes net2 = Network(seeds, root) net2.join() closeme.append(net2) net3 = Network(seeds, root) net3.join() closeme.append(net3) # some more random nodes for _ in range(20): n = Network(seeds, root) n.join() closeme.append(n) # give them a bit of time to register with the seed time.sleep(2) # find a random id target = z85.encode(generate_random()) # just pull out the id's for comparison nodes = [ x['node_id'] for x in net2.find_nodes(target, 5) ] # same response from other nodes assert nodes == [ x['node_id'] for x in net3.find_nodes(target, 5) ] # check that the routing agrees they're in distance order # need to transform back to raw bytes nodes = [ z85.decode(x) for x in nodes ] nodes2 = list(nodes) distance_sort(nodes2, z85.decode(target)) assert nodes == nodes2
def test_trusted_curve_with_wrong_peer_public_key(self): from pseud import Client, Server from pseud.utils import register_rpc server_id = b'server' endpoint = 'inproc://{}'.format(__name__).encode() endpoint = b'tcp://127.0.0.1:8998' server_public, server_secret = zmq.curve_keypair() server = Server(server_id, security_plugin='trusted_curve', public_key=server_public, secret_key=server_secret, io_loop=self.io_loop) server.bind(endpoint) alice_public, alice_secret = \ server.auth_backend.known_identities[b'alice'] client = Client(server_id, user_id=b'alice', security_plugin='trusted_curve', public_key=alice_public, secret_key=alice_secret, peer_public_key=z85.encode(b'R' * 32), timeout=.5, io_loop=self.io_loop) client.connect(endpoint) assert server.socket.mechanism == zmq.CURVE assert client.socket.mechanism == zmq.CURVE yield server.start() yield client.start() register_rpc(name='string.lower')(str.lower) with pytest.raises(TimeoutError): yield client.string.lower('BAR') server.stop() client.stop()
def handle_authentication(self, sock): '''Restrict connections to approved clients.''' allow = False auth = sock.recv_multipart() version, sequence, domain, address, identity, mechanism = auth[:6] assert version == '1.0' if mechanism == 'CURVE': creds = z85.encode(auth[6]) if domain == 'building.outgoing': allow = creds in self.allow_sub elif domain == 'building.incoming': allow = creds in self.allow_pub elif mechanism == 'NULL': allow, creds = True, '' else: creds = '' _log.info('{} {} at {} via {} {}'.format( 'allow' if allow else 'deny', address, domain, mechanism, creds)) if allow: reply = [version, sequence, "200", "OK", "", ""] else: reply = [version, sequence, "400", "Forbidden", "", ""] sock.send_multipart(reply)
def poll_random(self): # should really find a stale bucket not just random self.engine.txmap.create(FindNodes, [z85.encode(generate_random())], self.engine)
def _send_query(self): self.engine.txmap.create(FindNodes, [z85.encode(id_for_key(self.key))], self.engine, callback=self._closest)
def poll_neighbours(self): self.engine.txmap.create(FindNodes, [z85.encode(self.engine.node.node_id)], self.engine)