def test_generate_default(self): dest = datatypes.Destination() assert dest.enckey.key_type == crypto.EncType.ELGAMAL_2048 assert dest.sigkey.key_type == crypto.SigType.DSA_SHA1 assert dest.cert.type == datatypes.CertificateType.NULL dest2 = datatypes.Destination() assert dest2.enckey.key.y != dest.enckey.key.y assert dest2.sigkey.key.y != dest.sigkey.key.y
def test_serialize_nullcert(self): dest = datatypes.Destination(crypto.ElGamalKey(), crypto.DSAKey()) assert dest.cert.type == datatypes.CertificateType.NULL data = dest.serialize() dest2 = datatypes.Destination(raw=data) assert dest2.enckey.key.y == dest.enckey.key.y assert dest2.sigkey.key.y == dest.sigkey.key.y assert dest2.cert.type == dest.cert.type assert dest2.padding == dest.padding
def test_generate_specify_types(self): dest = datatypes.Destination(crypto.EncType.ELGAMAL_2048, crypto.SigType.DSA_SHA1) assert dest.enckey.key_type == crypto.EncType.ELGAMAL_2048 assert dest.sigkey.key_type == crypto.SigType.DSA_SHA1 assert dest.cert.type == datatypes.CertificateType.NULL dest = datatypes.Destination(sigkey=crypto.SigType.ECDSA_SHA256_P256) self._assert_keycert(dest, crypto.EncType.ELGAMAL_2048, crypto.SigType.ECDSA_SHA256_P256)
def test_serialize_keycert(self): dest = datatypes.Destination(crypto.ElGamalKey(), crypto.ECDSA256Key()) assert dest.cert.type == datatypes.CertificateType.KEY data = dest.serialize() dest2 = datatypes.Destination(raw=data) assert dest2.enckey.key.y == dest.enckey.key.y assert dest2.sigkey.key.get_pubkey() == dest.sigkey.key.get_pubkey() assert dest2.cert.type == dest.cert.type assert dest2.padding == dest.padding
def test_generate_from_keycert(self): keycert = datatypes.KeyCertificate(crypto.DSAKey(), crypto.ElGamalKey()) dest = datatypes.Destination(cert=keycert) assert dest.enckey.key_type == crypto.EncType.ELGAMAL_2048 assert dest.sigkey.key_type == crypto.SigType.DSA_SHA1 assert dest.cert.type == datatypes.CertificateType.NULL keycert = datatypes.KeyCertificate(crypto.ECDSA256Key(), crypto.ElGamalKey()) dest = datatypes.Destination(cert=keycert) self._assert_keycert(dest, crypto.EncType.ELGAMAL_2048, crypto.SigType.ECDSA_SHA256_P256)
def lookup(name, samAddr=_defaultSAMAddr): """ lookup an i2p name :returns i2p.datatypes.Destination: """ if isinstance(name, datatypes.Destination): # if it's already a destination return it :p return name # create new socket for lookup sock = None try: sock = pysocket.create_connection(samAddr) repl = _sam_cmd(sock, _greeting()) if repl.opts['RESULT'] != 'OK': # fail to handshake sock.close() raise pysocket.error(errno.EAGAIN, "cannot connect to i2p router") except pysocket.timeout as ex: raise ex except pysocket.error as ex: raise ex else: assert sock is not None # do lookup repl = _sam_cmd(sock, "NAMING LOOKUP NAME={}".format(name)) # close socket as it is not needed anymore sock.close() if 'VALUE' in repl.opts: dest = datatypes.Destination(raw=repl.opts['VALUE'], b64=True) return dest else: # failed to lookup raise pysocket.herror(errno.EAGAIN, "name not resolved: {}".format(name))
def test_parse(self): dest = datatypes.Destination(crypto.ElGamalKey(), crypto.DSAKey(), datatypes.Certificate()) lease = datatypes.Lease(b'f'*32, 1, datatypes.Date(1)) ls = datatypes.LeaseSet(dest=dest, ls_enckey=crypto.ElGamalKey(), ls_sigkey=crypto.DSAKey(), leases=[lease]) data = ls.serialize() ls2 = datatypes.LeaseSet(raw=data) assert ls2.dest.base64() == ls.dest.base64() assert ls2.enckey.key.y == ls.enckey.key.y assert ls2.sigkey.key.y == ls.sigkey.key.y assert len(ls2.leases) == len(ls.leases)
def test_serialize(self): dest = datatypes.Destination(crypto.ElGamalKey(), crypto.DSAKey(), datatypes.Certificate()) lease = datatypes.Lease(b'f' * 32, 1, datatypes.Date(1)) ls = datatypes.LeaseSet(dest=dest, ls_enckey=crypto.ElGamalKey(), ls_sigkey=crypto.DSAKey(), leases=[lease]) data = ls.serialize() dest.verify(data[:-40], data[-40:])
def recvfrom(self, nbytes, flags=0): """ recv bytes from a remote sender :param nbytes: number of bytes to recv max :return (data, addressInfo): """ data, addr = self._dgram_sock.recvfrom(nbytes) if addr == self._samDgramAddr: # only accept packets from the sam udp address idx = data.index(b'\n') return datatypes.Destination(raw=data[:idx], b64=True), bytearray(data[1+idx:]) else: self._log.warn("invalid source address for sam packet {}".format(addr))
def recvfrom_into(self, buffer, nbytes=None, flags=0): """ recv bytes from a remote sender :param buffer: buffer to recv into :param nbytes: number of bytes to recv max :return (nbytes, addressInfo): """ if nbytes is None: nbytes = len(buffer) data, addr = self._dgram_sock.recvfrom(buffersize) if addr == self._samDgramAddr: # only accept packets from the sam udp address idx = data.index(b'\n') payload = data[1+idx:] l = buffer.write(payload) return l, datatypes.Destination(raw=bytearray(data[:idx]), b64=True) else: self._log.warn("invalid source address for sam packet {}".format(addr))
def accept(self): if len(self._pending_accepts) == 0: # Just in case the user doesn't call listen() self._pending_accepts.append(self._create_accept()) sock, partialDest = self._pending_accepts.pop(0) # read destination for inbound dest, _finished = _sam_readline(sock, partialDest) if not _finished: self._pending_accepts.append((sock, dest)) raise pysocket.error(errno.EWOULDBLOCK, "waiting for client to connect") self._pending_accepts.append(self._create_accept()) # parse destination dest = datatypes.Destination(raw=dest, b64=True) # cache b32 address self._dest_cache[dest.base32()] = dest # override socket functions sock = _WrappedPySocket(sock, self.dest, dest) # return socket return sock, (dest, 0)
def lookup(self, name): """ look up a name :param name: a name or b32 address :returns an i2p.datatypes.Destination instance: """ if isinstance(name, datatypes.Destination): # if it's already a destination return it :p return name # check cache if name in self._dest_cache: # cache hit return self._dest_cache[name] # cache miss, do lookup # create new socket for lookup sock = None try: sock = pysocket.create_connection(self._samAddr) self._samHandshake(sock) except pysocket.timeout as ex: raise ex except pysocket.error as ex: raise ex else: # do lookup repl = _sam_cmd(sock, "NAMING LOOKUP NAME={}".format(name)) # close socket as it is not needed anymore sock.close() if 'VALUE' in repl.opts: dest = datatypes.Destination(raw=repl.opts['VALUE'], b64=True) if not name.endswith(".b32.i2p"): # cache name as it's not a b32 address self._dest_cache[name] = dest # cache base32 address self._dest_cache[dest.base32()] = dest return dest else: # failed to lookup raise pysocket.herror(errno.EAGAIN, "name not resolved: {}".format(name))
def bind(self, keyfile, nickname=None, **i2cpOptions): """ bind to an address :param keyfile: the file containing the private keys to use :param nickname: the nickname to use for tunnels or None for random decided by sam :param i2cpOptions: additional i2cp options to pass into sam """ if nickname is None: nickname = randnick(8) self._state = State.Connecting self._log.info('bind') host, port = None, None if self._type == SAM.SOCK_STREAM: style = "STREAM" elif self._type == SAM.SOCK_DGRAM: style = "DATAGRAM" self._dgram_sock = pysocket.socket(type=pysocket.SOCK_DGRAM) self._dgram_sock.bind(self._dgram_bind) port = self._dgram_sock.getsockname()[1] host = self._dgram_bind[0] else: style = "RAW" self._keys = 'TRANSIENT' if keyfile: if isinstance(keyfile, str): if os.path.exists(keyfile): with open(keyfile, 'rb') as f: self.dest = datatypes.Destination(raw=f, private=True) self._keys = self.dest.base64(True) elif hasattr(keyfile, 'read'): self.dest = datatypes.Destination(raw=keyfile, private=True) self._keys = self.dest.base64(True) cmd = 'SESSION CREATE STYLE={} DESTINATION={} ID={}'.format( style, self._keys, nickname) if host: cmd += " HOST={}".format(host) if port: cmd += " PORT={}".format(port) for opt in i2cpOptions: cmd += " {}={}".format(opt, i2cpOptions[opt]) repl = _sam_cmd(self._samSocket, cmd) if repl.opts['RESULT'] == 'OK': self._keys = repl.opts['DESTINATION'] if self.dest is None: self.dest = datatypes.Destination(raw=self._keys, b64=True, private=True) if keyfile: data = self.dest.serialize(priv=True) if isinstance(keyfile, str): with open(keyfile, 'wb') as f: f.write(data) elif hasattr(keyfile, "write"): keyfile.write(data) self._nick = nickname self._state = State.Ready else: self._state = State.Error # TODO: different types of errors for different types of results from sam raise Error("bad result from sam: {}".format(repl.opts["RESULT"]))
def bind(self, keyfile, nickname=None, **i2cpOptions): """ bind to an address :param keyfile: the file containing the private keys to use or None to not store :param nickname: the nickname to use for tunnels or None for random decided by sam :param i2cpOptions: additional i2cp options to pass into sam """ if nickname is None: nickname = randnick(8) self._state = State.Connecting if self.type == SAM.SOCK_STREAM: style = "STREAM" elif self.type == SAM.SOCK_DGRAM: style = "DATAGRAM" self._dgram_sock = pysocket.socket(type=pysocket.SOCK_DGRAM) self._dgram_sock.bind(self._dgram_bind) port = self._dgram_sock.getsockname()[1] i2cpOptions["HOST"] = self._dgram_bind[0] i2cpOptions["PORT"] = port # apply deferred options self._apply_defered_socket_props(self._dgram_sock) else: # TODO: implement style = "RAW" _keys = 'TRANSIENT' if keyfile: if isinstance(keyfile, str): if os.path.exists(keyfile): with open(keyfile, 'rb') as f: self.dest = datatypes.Destination(raw=f, private=True) _keys = self.dest.base64() elif hasattr(keyfile, 'read'): self.dest = datatypes.Destination(raw=keyfile, private=True) _keys = self.dest.base64() cmd = 'SESSION CREATE STYLE={} DESTINATION={} ID={}'.format(style, _keys, nickname) for opt in i2cpOptions: cmd += " {}={}".format(opt, i2cpOptions[opt]) repl = _sam_cmd(self._samSocket, cmd) if repl.opts['RESULT'] == 'OK': _keys = repl.opts['DESTINATION'] if self.dest is None: self.dest = datatypes.Destination(raw=_keys, b64=True, private=True) if keyfile: data = self.dest.serialize(priv=True) if isinstance(keyfile, str): with open(keyfile, 'wb') as f: f.write(data) elif hasattr(keyfile, "write"): keyfile.write(data) self.socketname = nickname # TODO: do we really want to do this? self._apply_defered_socket_props(self._samSocket) self._state = State.Ready else: self._state = State.Error # TODO: different types of errors for different types of results from sam raise pysocket.error(errno.EADDRNOTAVAIL, "failed to bind destination with i2p: {}".format(repl.opts["RESULT"]))
def TODO_test_parse_eeppriv(self): with open(testkey, 'rb') as rf: dest = datatypes.Destination(raw=rf)
def _test_parse_b64(self, b64, cert_type, data_len): dest = datatypes.Destination(raw=b64, b64=True) assert dest.cert.type == cert_type assert len(dest.cert.data) == data_len
def _test_base32(self, b64, b32): dest = datatypes.Destination(raw=b64, b64=True) assert dest.base32() == b32