import os import time, datetime import random import io from zipfile import ZipFile, ZIP_DEFLATED import pyseeder.crypto from pyseeder.utils import PyseederException class SU3File: """SU3 file format""" def __init__(self, signer_id): self.SIGNER_ID = signer_id self.SIGNER_ID_LENGTH = len(self.SIGNER_ID) self.SIGNATURE_TYPE = 0x0006 self.SIGNATURE_LENGTH = 512 self.VERSION_LENGTH = 0x10 self.FILE_TYPE = None self.CONTENT_TYPE = None self.CONTENT = None self.CONTENT_LENGTH = None self.VERSION = str(int(time.time())).encode("utf-8") #self.keytype = "RSA_SHA512_4096" self.OUTPUT = None def write(self, filename, priv_key, priv_key_password=None): """Write file to disc""" nullbyte = bytes([0]) self.OUTPUT = "I2Psu3".encode("utf-8") self.OUTPUT += bytes([0,0]) self.OUTPUT += self.SIGNATURE_TYPE.to_bytes(2, "big") self.OUTPUT += self.SIGNATURE_LENGTH.to_bytes(2, "big") self.OUTPUT += nullbyte self.OUTPUT += bytes([self.VERSION_LENGTH]) self.OUTPUT += nullbyte self.OUTPUT += bytes([self.SIGNER_ID_LENGTH]) self.OUTPUT += self.CONTENT_LENGTH.to_bytes(8, "big") self.OUTPUT += nullbyte self.OUTPUT += bytes([self.FILE_TYPE]) self.OUTPUT += nullbyte self.OUTPUT += bytes([self.CONTENT_TYPE]) self.OUTPUT += bytes([0 for _ in range(12)]) self.OUTPUT += self.VERSION + bytes( [0 for _ in range(16 - len(self.VERSION))]) self.OUTPUT += self.SIGNER_ID.encode("utf-8") self.OUTPUT += self.CONTENT with open(filename + "-data", "wb") as f: f.write(self.OUTPUT) signature = pyseeder.crypto.get_signature(self.OUTPUT, priv_key, priv_key_password) with open(filename + "-sig", "wb") as f: f.write(signature) self.OUTPUT += signature with open(filename, "wb") as f: f.write(self.OUTPUT) def reseed(self, netdb, yggseeds): """Compress netdb entries and set content""" seeds = 75 zip_file = io.BytesIO() dat_files = [] dat_yggfiles = [] if yggseeds > 0: import re pattern = re.compile(b'host=.[23]..:') timelimit = time.time() - float(3600 * 10) # current time minus 10 hours for root, dirs, files in os.walk(netdb): for f in files: if f.endswith(".dat"): path = os.path.join(root, f) file_added = False if os.path.getmtime(path) < timelimit: # modified time older than 10h continue if yggseeds > 0: for line in open(path, "rb"): if pattern.search(line): dat_yggfiles.append(path) file_added = True break if not file_added: dat_files.append(path) if yggseeds > 0: if len(dat_yggfiles) == 0: raise PyseederException("Can't get enough netDb entries with yggdrasil addresses") elif len(dat_yggfiles) > yggseeds: dat_yggfiles = random.sample(dat_yggfiles, yggseeds) seeds = seeds - len(dat_yggfiles) if len(dat_files) == 0: raise PyseederException("Can't get enough netDb entries") elif len(dat_files) > seeds: dat_files = random.sample(dat_files, seeds) dat_files.extend(dat_yggfiles) with ZipFile(zip_file, "w", compression=ZIP_DEFLATED) as zf: for f in dat_files: zf.write(f, arcname=os.path.split(f)[1]) self.FILE_TYPE = 0x00 self.CONTENT_TYPE = 0x03 self.CONTENT = zip_file.getvalue() self.CONTENT_LENGTH = len(self.CONTENT)