merge PrivateKey to Destination
parent
29c9c2b8db
commit
be146d74d7
|
@ -27,7 +27,7 @@ Utilities
|
|||
---------
|
||||
|
||||
.. autofunction:: dest_lookup
|
||||
.. autofunction:: new_private_key
|
||||
.. autofunction:: new_destination
|
||||
.. autofunction:: get_sam_address
|
||||
|
||||
Tunnel API
|
||||
|
@ -52,13 +52,13 @@ Data structures
|
|||
|
||||
.. autoattribute:: i2plib.sam.Destination.data
|
||||
.. autoattribute:: i2plib.sam.Destination.base64
|
||||
.. autoattribute:: i2plib.sam.Destination.private_key
|
||||
|
||||
.. autoclass:: i2plib.PrivateKey
|
||||
:members:
|
||||
|
||||
.. autoattribute:: i2plib.sam.PrivateKey.data
|
||||
.. autoattribute:: i2plib.sam.PrivateKey.base64
|
||||
.. autoattribute:: i2plib.sam.PrivateKey.destination
|
||||
|
||||
Exceptions
|
||||
---------------
|
||||
|
|
|
@ -25,11 +25,11 @@ def main(args):
|
|||
raise OSError("No such directory {}".format(args.web_directory))
|
||||
|
||||
if args.key:
|
||||
priv = i2plib.PrivateKey(path=args.key)
|
||||
dest = i2plib.Destination(path=args.key, has_private_key=True)
|
||||
else:
|
||||
priv = i2plib.utils.get_new_private_key(sam_address=sam_address)
|
||||
dest = i2plib.utils.get_new_destination(sam_address=sam_address)
|
||||
|
||||
logging.info("Listening: {}.b32.i2p".format(priv.destination.base32))
|
||||
logging.info("Listening: {}.b32.i2p".format(dest.base32))
|
||||
logging.info("Server: {}:{}".format(server_address[0], server_address[1]))
|
||||
|
||||
# run HTTP server
|
||||
|
@ -41,7 +41,7 @@ def main(args):
|
|||
loop = asyncio.get_event_loop()
|
||||
|
||||
tunnel = i2plib.ServerTunnel(server_address,
|
||||
loop=loop, private_key=priv, sam_address=sam_address)
|
||||
loop=loop, destination=dest, sam_address=sam_address)
|
||||
asyncio.ensure_future(tunnel.run(), loop=loop)
|
||||
|
||||
try:
|
||||
|
|
|
@ -10,7 +10,7 @@ DEST_B32 = "bxwnysaa2nwykldz4ekz6u243x5ctqlcot5acmzj2huylvwr7eyq.b32.i2p"
|
|||
|
||||
async def ping_pong(sam_address, loop):
|
||||
_, server_session_writer = await i2plib.create_session("ppserver",
|
||||
sam_address=sam_address, loop=loop, private_key=PK_B64)
|
||||
sam_address=sam_address, loop=loop, destination=PK_B64)
|
||||
server_reader, server_writer = await i2plib.stream_accept("ppserver",
|
||||
sam_address=sam_address, loop=loop)
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import sys
|
||||
from urllib.parse import urlparse
|
||||
|
||||
import i2plib
|
||||
from i2plib.sam import generate_session_id, lookup, get_socket, StreamSession
|
||||
|
||||
def http_get(url, sam_address):
|
||||
|
|
|
@ -10,7 +10,7 @@ from .__version__ import (
|
|||
from .sam import Destination, PrivateKey
|
||||
|
||||
from .aiosam import (
|
||||
get_sam_socket, dest_lookup, new_private_key,
|
||||
get_sam_socket, dest_lookup, new_destination,
|
||||
create_session, stream_connect, stream_accept
|
||||
)
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
__title__ = 'i2plib'
|
||||
__description__ = 'A modern asynchronous library for building I2P applications.'
|
||||
__url__ = 'https://github.com/l-n-s/i2plib'
|
||||
__version__ = '0.0.7'
|
||||
__version__ = '0.0.8'
|
||||
__author__ = 'Viktor Villainov'
|
||||
__author_email__ = 'supervillain@riseup.net'
|
||||
__license__ = 'MIT'
|
||||
|
|
|
@ -45,26 +45,26 @@ async def dest_lookup(domain, sam_address=i2plib.sam.DEFAULT_ADDRESS,
|
|||
else:
|
||||
raise i2plib.exceptions.SAM_EXCEPTIONS[reply["RESULT"]]()
|
||||
|
||||
async def new_private_key(sam_address=i2plib.sam.DEFAULT_ADDRESS, loop=None,
|
||||
async def new_destination(sam_address=i2plib.sam.DEFAULT_ADDRESS, loop=None,
|
||||
sig_type=i2plib.sam.Destination.default_sig_type):
|
||||
"""A coroutine used to generate a new private key of a chosen signature
|
||||
type.
|
||||
"""A coroutine used to generate a new destination with a private key of a
|
||||
chosen signature type.
|
||||
|
||||
:param sam_address: (optional) SAM API address
|
||||
:param loop: (optional) Event loop instance
|
||||
:param sig_type: (optional) Signature type
|
||||
:return: An instance of i2plib.PrivateKey
|
||||
:return: An instance of i2plib.Destination
|
||||
"""
|
||||
reader, writer = await get_sam_socket(sam_address, loop)
|
||||
writer.write(i2plib.sam.dest_generate(sig_type))
|
||||
reply = parse_reply(await reader.read(BUFFER_SIZE))
|
||||
writer.close()
|
||||
return i2plib.sam.PrivateKey(reply["PRIV"])
|
||||
return i2plib.sam.Destination(reply["PRIV"], has_private_key=True)
|
||||
|
||||
async def create_session(session_name, sam_address=i2plib.sam.DEFAULT_ADDRESS,
|
||||
loop=None, session_ready=None, style="STREAM",
|
||||
signature_type=i2plib.sam.Destination.default_sig_type,
|
||||
private_key=None, options={}, session_created=None,
|
||||
destination=None, options={}, session_created=None,
|
||||
args=()):
|
||||
"""A coroutine used to create a new SAM session.
|
||||
|
||||
|
@ -76,44 +76,46 @@ async def create_session(session_name, sam_address=i2plib.sam.DEFAULT_ADDRESS,
|
|||
:param style: (optional) Session style, can be STREAM, DATAGRAM, RAW
|
||||
:param signature_type: (optional) If the destination is TRANSIENT, this
|
||||
signature type is used
|
||||
:param private_key: (optional) Private key to use in this session. Can be
|
||||
a base64 encoded string, i2plib.sam.PrivateKey instance
|
||||
:param destination: (optional) Destination to use in this session. Can be
|
||||
a base64 encoded string, i2plib.sam.Destination instance
|
||||
or None. TRANSIENT destination is used when it is None.
|
||||
:param options: (optional) A dict object with i2cp options
|
||||
:param session_created: (optional) A coroutine to be executed after the
|
||||
session is created. Executed with arguments
|
||||
(loop, reader, writer, private_key, \*args)
|
||||
(loop, reader, writer, destination, \*args)
|
||||
:param args: (optional) Arguments for a session_created coroutine
|
||||
:return: A (reader, writer) pair
|
||||
"""
|
||||
logging.debug("Creating session {}".format(session_name))
|
||||
if private_key:
|
||||
if type(private_key) == i2plib.sam.PrivateKey:
|
||||
private_key = private_key
|
||||
if destination:
|
||||
if type(destination) == i2plib.sam.Destination:
|
||||
destination = destination
|
||||
else:
|
||||
private_key = i2plib.sam.PrivateKey(private_key)
|
||||
destination = private_key.base64
|
||||
destination = i2plib.sam.Destination(
|
||||
destination, has_private_key=True)
|
||||
|
||||
dest_string = destination.private_key.base64
|
||||
else:
|
||||
private_key = None
|
||||
destination = i2plib.sam.TRANSIENT_DESTINATION
|
||||
dest_string = i2plib.sam.TRANSIENT_DESTINATION
|
||||
|
||||
options = " ".join(["{}={}".format(k, v) for k, v in options.items()])
|
||||
|
||||
reader, writer = await get_sam_socket(sam_address, loop)
|
||||
writer.write(i2plib.sam.session_create(
|
||||
style, session_name, destination, options))
|
||||
style, session_name, dest_string, options))
|
||||
|
||||
reply = parse_reply(await reader.read(BUFFER_SIZE))
|
||||
if reply.ok:
|
||||
if not private_key:
|
||||
private_key = i2plib.sam.PrivateKey(reply["DESTINATION"])
|
||||
logging.debug(private_key.destination.base32)
|
||||
if not destination:
|
||||
destination = i2plib.sam.Destination(
|
||||
reply["DESTINATION"], has_private_key=True)
|
||||
logging.debug(destination.base32)
|
||||
if session_ready:
|
||||
session_ready.set()
|
||||
logging.debug("Session created {}".format(session_name))
|
||||
if session_created:
|
||||
asyncio.ensure_future(session_created(loop, reader, writer,
|
||||
private_key, *args), loop=loop)
|
||||
destination, *args), loop=loop)
|
||||
return (reader, writer)
|
||||
else:
|
||||
raise i2plib.exceptions.SAM_EXCEPTIONS[reply["RESULT"]]()
|
||||
|
|
|
@ -10,6 +10,14 @@ from .exceptions import SAM_EXCEPTIONS
|
|||
|
||||
I2P_B64_CHARS = "-~"
|
||||
|
||||
def i2p_b64encode(x):
|
||||
"""Encode I2P destination"""
|
||||
return b64encode(x, altchars=I2P_B64_CHARS.encode()).decode()
|
||||
|
||||
def i2p_b64decode(x):
|
||||
"""Decode I2P destination"""
|
||||
return b64decode(x, altchars=I2P_B64_CHARS, validate=True)
|
||||
|
||||
SAM_BUFSIZE = 4096
|
||||
DEFAULT_ADDRESS = ("127.0.0.1", 7656)
|
||||
DEFAULT_MIN_VER = "3.1"
|
||||
|
@ -111,6 +119,8 @@ class Destination(object):
|
|||
https://geti2p.net/spec/common-structures#destination
|
||||
|
||||
:param data: (optional) Base64 encoded data or binary data
|
||||
:param path: (optional) A path to a file with binary data
|
||||
:param has_private_key: (optional) Does data have a private key?
|
||||
"""
|
||||
|
||||
ECDSA_SHA256_P256 = 1
|
||||
|
@ -120,20 +130,32 @@ class Destination(object):
|
|||
|
||||
default_sig_type = EdDSA_SHA512_Ed25519
|
||||
|
||||
def __init__(self, data=None):
|
||||
#: Binary private key data
|
||||
_pubkey_size = 256
|
||||
_signkey_size = 128
|
||||
_min_cert_size = 3
|
||||
|
||||
def __init__(self, data=None, path=None, has_private_key=False):
|
||||
#: Binary destination
|
||||
self.data = bytes()
|
||||
#: Base64 encoded private key data
|
||||
#: Base64 encoded destination
|
||||
self.base64 = ""
|
||||
#: i2plib.PrivateKey instance or None
|
||||
self.private_key = None
|
||||
|
||||
if data:
|
||||
if type(data) == bytes:
|
||||
self.data = data
|
||||
self.base64 = b64encode(self.data,
|
||||
altchars=I2P_B64_CHARS.encode()).decode()
|
||||
elif type(data) == str:
|
||||
self.data = b64decode(data, altchars=I2P_B64_CHARS, validate=True)
|
||||
self.base64 = data
|
||||
if path:
|
||||
with open(path, "rb") as f: data = f.read()
|
||||
|
||||
if data and has_private_key:
|
||||
self.private_key = PrivateKey(data)
|
||||
|
||||
cert_len = struct.unpack("!H", self.private_key.data[385:387])[0]
|
||||
data = self.private_key.data[:387+cert_len]
|
||||
|
||||
if not data:
|
||||
raise Exception("Can't create a destination with no data")
|
||||
|
||||
self.data = data if type(data) == bytes else i2p_b64decode(data)
|
||||
self.base64 = data if type(data) == str else i2p_b64encode(data)
|
||||
|
||||
def __repr__(self):
|
||||
return "<Destination: {}>".format(self.base32)
|
||||
|
@ -149,39 +171,14 @@ class PrivateKey(object):
|
|||
|
||||
https://geti2p.net/spec/common-structures#keysandcert
|
||||
|
||||
:param data: (optional) Base64 encoded data or binary data
|
||||
:param path: (optional) Path to a file with binary private key data
|
||||
:param data: Base64 encoded data or binary data
|
||||
"""
|
||||
_pubkey_size = 256
|
||||
_signkey_size = 128
|
||||
_min_cert_size = 3
|
||||
|
||||
def __init__(self, data=None, path=None):
|
||||
#: Binary private key data
|
||||
self.data = bytes()
|
||||
#: Base64 encoded private key data
|
||||
self.base64 = ""
|
||||
#: i2plib.Destination object of the private key
|
||||
self.destination = None
|
||||
|
||||
if path:
|
||||
with open(path, "rb") as f: data = f.read()
|
||||
if data:
|
||||
if type(data) == bytes:
|
||||
self.data = data
|
||||
self.base64 = b64encode(self.data,
|
||||
altchars=I2P_B64_CHARS.encode()).decode()
|
||||
elif type(data) == str:
|
||||
self.data = b64decode(data, altchars=I2P_B64_CHARS, validate=True)
|
||||
self.base64 = data
|
||||
|
||||
cert_len = struct.unpack("!H", self.data[385:387])[0]
|
||||
self.destination = Destination(data=self.data[:387+cert_len])
|
||||
|
||||
def __repr__(self):
|
||||
return "<PrivateKey: {}>".format(self.destination.base32)
|
||||
|
||||
|
||||
def __init__(self, data):
|
||||
#: Binary private key
|
||||
self.data = data if type(data) == bytes else i2p_b64decode(data)
|
||||
#: Base64 encoded private key
|
||||
self.base64 = data if type(data) == str else i2p_b64encode(data)
|
||||
|
||||
class StreamSession(object):
|
||||
|
||||
|
|
|
@ -30,9 +30,9 @@ class I2PTunnel(object):
|
|||
|
||||
:param local_address: A local address to use for a tunnel.
|
||||
E.g. ("127.0.0.1", 6668)
|
||||
:param private_key: (optional) Private key to use in this session. Can be
|
||||
a base64 encoded string, i2plib.sam.PrivateKey instance
|
||||
or None. A new key is created when it is None.
|
||||
:param destination: (optional) Destination to use in this session. Can be
|
||||
a base64 encoded string, i2plib.sam.Destination instance
|
||||
or None. A new destination is created when it is None.
|
||||
:param session_name: (optional) Session nick name. A new session nickname is
|
||||
generated if not specified.
|
||||
:param options: (optional) A dict object with i2cp options
|
||||
|
@ -40,23 +40,23 @@ class I2PTunnel(object):
|
|||
:param sam_address: (optional) SAM API address
|
||||
"""
|
||||
|
||||
def __init__(self, local_address, private_key=None, session_name=None,
|
||||
def __init__(self, local_address, destination=None, session_name=None,
|
||||
options={}, loop=None, sam_address=i2plib.sam.DEFAULT_ADDRESS):
|
||||
self.local_address = local_address
|
||||
self.private_key = private_key
|
||||
self.destination = destination
|
||||
self.session_name = session_name or i2plib.sam.generate_session_id()
|
||||
self.options = options
|
||||
self.loop = loop
|
||||
self.sam_address = sam_address
|
||||
|
||||
async def _pre_run(self):
|
||||
if not self.private_key:
|
||||
self.private_key = await i2plib.new_private_key(
|
||||
if not self.destination:
|
||||
self.destination = await i2plib.new_destination(
|
||||
sam_address=self.sam_address, loop=self.loop)
|
||||
_, self.session_writer = await i2plib.aiosam.create_session(
|
||||
self.session_name, style=self.style, options=self.options,
|
||||
sam_address=self.sam_address,
|
||||
loop=self.loop, private_key=self.private_key)
|
||||
loop=self.loop, destination=self.destination)
|
||||
|
||||
def stop(self):
|
||||
"""Stop the tunnel"""
|
||||
|
@ -169,17 +169,17 @@ if __name__ == '__main__':
|
|||
loop.set_debug(args.debug)
|
||||
|
||||
if args.key:
|
||||
private_key = i2plib.sam.PrivateKey(path=args.key)
|
||||
destination = i2plib.sam.Destination(path=args.key, has_private_key=True)
|
||||
else:
|
||||
private_key = None
|
||||
destination = None
|
||||
|
||||
local_address = i2plib.utils.address_from_string(args.address)
|
||||
|
||||
if args.type == "client":
|
||||
tunnel = ClientTunnel(args.destination, local_address, loop=loop,
|
||||
private_key=private_key, sam_address=SAM_ADDRESS)
|
||||
destination=destination, sam_address=SAM_ADDRESS)
|
||||
elif args.type == "server":
|
||||
tunnel = ServerTunnel(local_address, loop=loop, private_key=private_key,
|
||||
tunnel = ServerTunnel(local_address, loop=loop, destination=destination,
|
||||
sam_address=SAM_ADDRESS)
|
||||
|
||||
asyncio.ensure_future(tunnel.run(), loop=loop)
|
||||
|
|
|
@ -24,12 +24,12 @@ def get_sam_address():
|
|||
else:
|
||||
return i2plib.sam.DEFAULT_ADDRESS
|
||||
|
||||
def get_new_private_key(sam_address=i2plib.sam.DEFAULT_ADDRESS,
|
||||
def get_new_destination(sam_address=i2plib.sam.DEFAULT_ADDRESS,
|
||||
sig_type=i2plib.sam.Destination.default_sig_type):
|
||||
"""Generates new I2P destination of a chosen signature type"""
|
||||
sam_socket = i2plib.sam.get_socket(sam_address)
|
||||
sam_socket.send(i2plib.sam.dest_generate(sig_type))
|
||||
a = i2plib.sam.get_response(sam_socket)
|
||||
sam_socket.close()
|
||||
return i2plib.sam.PrivateKey(a['PRIV'])
|
||||
return i2plib.sam.Destination(a['PRIV'], has_private_key=True)
|
||||
|
||||
|
|
|
@ -46,7 +46,7 @@ async def ping_pong(sam_address, loop):
|
|||
await asyncio.sleep(0.1)
|
||||
|
||||
_, server_session_writer = await i2plib.create_session("ppserver",
|
||||
sam_address=sam_address, loop=loop, private_key=PK_B64)
|
||||
sam_address=sam_address, loop=loop, destination=PK_B64)
|
||||
server_reader, server_writer = await i2plib.stream_accept("ppserver",
|
||||
sam_address=sam_address, loop=loop)
|
||||
|
||||
|
|
|
@ -10,9 +10,9 @@ TEST_DEST_B64 = "Q8VZ41jSRFutjRYDbnDioTfMitFMAMINRqrgcwISQ3ObOyzjBX9Kz~OJz7ShyGq
|
|||
class TestDestination(unittest.TestCase):
|
||||
|
||||
def test_private_key(self):
|
||||
pk = i2plib.sam.PrivateKey(path=TEST_KEY)
|
||||
self.assertEqual(pk.destination.base32, TEST_DEST_B32)
|
||||
self.assertEqual(pk.destination.base64, TEST_DEST_B64)
|
||||
pk = i2plib.sam.Destination(path=TEST_KEY, has_private_key=True)
|
||||
self.assertEqual(pk.base32, TEST_DEST_B32)
|
||||
self.assertEqual(pk.base64, TEST_DEST_B64)
|
||||
|
||||
def test_destination(self):
|
||||
dest = i2plib.sam.Destination(TEST_DEST_B64)
|
||||
|
|
Loading…
Reference in New Issue