コード例 #1
0
ファイル: userprofile.py プロジェクト: darkwyrm/pyanselus
    def load_profiles(self) -> RetVal:
        '''
		Loads profile information from the specified JSON file stored in the top level of the 
		profile folder.

		Returns:
		"error" : string
		"profiles" : list
		'''
        profile_list_path = os.path.join(self.profile_folder, 'profiles.json')

        if os.path.exists(profile_list_path):
            profile_data = list()
            try:
                with open(profile_list_path, 'r') as fhandle:
                    profile_data = json.load(fhandle)

            except Exception:
                return RetVal(BadProfileList)

            profiles = list()
            for item in profile_data:
                profile = Profile(
                    os.path.join(self.profile_folder, item['name']))
                profile.set_from_dict(item)
                profiles.append(profile)
                if profile.isdefault:
                    self.default_profile = profile.name

            self.profiles = profiles
        return RetVal()
コード例 #2
0
ファイル: userprofile.py プロジェクト: darkwyrm/pyanselus
    def activate_profile(self, name: str) -> RetVal:
        '''
		Activates the specified profile.

		Returns:
		"error" : string
		"wid" : string
		"host" : string
		"port" : integer
		'''
        if self.active_index >= 0:
            self.profiles[self.active_index].deactivate()
            self.active_index = -1

        if not name:
            return RetVal(BadParameterValue, "BUG: name may not be empty")

        name_squashed = name.casefold()
        active_index = self.__index_for_profile(name_squashed)
        if active_index < 0:
            return RetVal(ResourceNotFound, "%s doesn't exist" % name_squashed)

        self.profile_id = name_squashed

        self.active_index = active_index
        self.profiles[self.active_index].activate()

        out = RetVal()
        out.set_values({
            'wid': self.profiles[active_index].wid,
            'host': self.profiles[active_index].domain,
            'port': self.profiles[active_index].port
        })
        return out
コード例 #3
0
	def connect(self, address: str, port: int) -> RetVal:
		'''Creates a connection to the server.'''
		try:
			sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
			# Set a short timeout in case the server doesn't respond immediately,
			# which is the expectation as soon as a client connects.
			sock.settimeout(10.0)
		except Exception as e:
			return RetVal(ExceptionThrown, e)
		
		try:
			sock.connect((address, port))
			
			# absorb the hello string
			_ = sock.recv(8192)

		except Exception as e:
			sock.close()
			return RetVal(ExceptionThrown, e)

		# Set a timeout of 30 minutes
		sock.settimeout(1800.0)
		
		self.socket = sock
		return RetVal()
コード例 #4
0
def login(conn: ServerConnection, wid: str, serverkey: CryptoString) -> RetVal:
	'''Starts the login process by sending the requested workspace ID.'''
	if not utils.validate_uuid(wid):
		return RetVal(BadParameterValue)

	challenge = b85encode(secrets.token_bytes(32))
	ekey = PublicKey(serverkey)
	status = ekey.encrypt(challenge)
	if status.error():
		return status

	conn.send_message({
		'Action' : "LOGIN",
		'Data' : {
			'Workspace-ID' : wid,
			'Login-Type' : 'PLAIN',
			'Challenge' : status['data']
		}
	})

	response = conn.read_response(server_response)
	if response.error():
		return response
	
	if response['Code'] != 100:
		return wrap_server_error(response)
	
	if response['Data']['Response'] != challenge.decode():
		return RetVal(ServerError, 'server failed to decrypt challenge')
	
	return RetVal()
コード例 #5
0
def unregister(conn: ServerConnection, pwhash: str, wid: str) -> RetVal:
	'''Deletes the online account at the specified server.'''

	if wid and not utils.validate_uuid(wid):
		return RetVal(BadParameterValue, 'bad workspace id')
	
	request = {
		'Action' : 'UNREGISTER',
		'Data' : {
			'Password-Hash' : pwhash
		}
	}
	if wid:
		request['Data']['Workspace-ID'] = wid
	
	status = conn.send_message(request)
	if status.error():
		return status
	
	response = conn.read_response(server_response)

	if response['Code'] == 202:
		return RetVal()

	# This particular command is very simple: make a request, because the server will return
	# one of three possible types of responses: success, pending (for private/moderated 
	# registration modes), or an error. In all of those cases there isn't anything else to do.
	return wrap_server_error(response)
コード例 #6
0
def iscurrent(conn: ServerConnection, index: int, wid='') -> RetVal:
	'''Finds out if an entry index is current. If wid is empty, the index is checked for the 
	organization.'''
	if wid and not utils.validate_uuid(wid):
		return RetVal(AnsBadRequest).set_value('status', 400)
	
	request = {
		'Action' : 'ISCURRENT',
		'Data' : {
			'Index' : str(index)
		}
	}
	if wid:
		request['Data']['Workspace-ID'] = wid
	conn.send_message(request)

	response = conn.read_response(server_response)
	if response.error():
		return response
	
	if response['Code'] != 200:
		return wrap_server_error(response)
	
	if 'Is-Current' not in response['Data']:
		return RetVal(ServerError, 'server did not return an answer')
	
	return RetVal().set_value('iscurrent', bool(response['Data']['Is-Current'] == 'YES'))
コード例 #7
0
    def is_timestamp_valid(self) -> RetVal:
        '''Checks the validity of the timestamp. As a side effect, it checks the validity of the 
		expiration date field, but it does not check if the entry is actually expired'''
        m = re.match(r'^([0-9]{4})([0-9]{2})([0-9]{2})$',
                     self.fields['Expires'])
        if not m or not _is_valid_date(int(m[2]), int(m[3]), int(m[1])):
            return RetVal(BadData, 'bad expiration date')
        expire_time = datetime.datetime(int(m[1]),
                                        int(m[2]),
                                        int(m[3]),
                                        tzinfo=datetime.timezone(
                                            datetime.timedelta(hours=0)))

        m = re.match(
            r'^([0-9]{4})([0-9]{2})([0-9]{2})T([0-9]{2})([0-9]{2})([0-9]{2})Z$',
            self.fields['Timestamp'])
        if not m or not _is_valid_date(int(m[2]), int(m[3]), int(m[1]),
                                       int(m[4]), int(m[5]), int(m[6])):
            return RetVal(BadData, 'bad timestamp')
        timestamp_time = datetime.datetime(int(m[1]),
                                           int(m[2]),
                                           int(m[3]),
                                           int(m[4]),
                                           int(m[5]),
                                           int(m[6]),
                                           tzinfo=datetime.timezone(
                                               datetime.timedelta(hours=0)))

        if timestamp_time > expire_time:
            return RetVal(BadData, 'bad timestamp')

        return RetVal()
コード例 #8
0
ファイル: userprofile.py プロジェクト: darkwyrm/pyanselus
    def set_default_profile(self, name: str) -> RetVal:
        '''
		Sets the default profile. If there is only one profile -- or none at all -- this call has 
		no effect.
		'''
        if not name:
            return RetVal(BadParameterValue, "BUG: name may not be empty")

        if len(self.profiles) == 1:
            if self.profiles[0].isdefault:
                return RetVal()
            self.profiles[0].isdefault = True
            return self.save_profiles()

        oldindex = -1
        for i in range(0, len(self.profiles)):
            if self.profiles[i].isdefault:
                oldindex = i

        name_squashed = name.casefold()
        newindex = self.__index_for_profile(name_squashed)

        if newindex < 0:
            return RetVal(ResourceNotFound,
                          "New profile %s not found" % name_squashed)

        if oldindex >= 0:
            if name_squashed == self.profiles[oldindex].name:
                return RetVal()
            self.profiles[oldindex].isdefault = False

        self.profiles[newindex].isdefault = True
        return self.save_profiles()
コード例 #9
0
    def chain(self, key: CryptoString, rotate_optional: bool) -> RetVal:
        '''Appends a new entry to the chain, optionally rotating keys which aren't required to be 
		changed. This method requires that the root entry already exist. Note that user cards will 
		not have all the required signatures when the call returns'''
        if len(self.entries) < 1:
            return RetVal(ResourceNotFound, 'missing root entry')

        # Just in case we get some squirrelly non-Org, non-User card type
        chain_method = getattr(self.entries[-1], "chain", None)
        if not chain_method or not callable(chain_method):
            return RetVal(FeatureNotAvailable,
                          "entry doesn't support chaining")

        chaindata = self.entries[-1].chain(key, rotate_optional)
        if chaindata.error():
            return chaindata

        new_entry = chaindata['entry']

        skeystring = CryptoString()
        status = skeystring.set(chaindata['sign.private'])
        if status.error():
            return status

        if new_entry.type == 'User':
            status = new_entry.sign(skeystring, 'User')
        else:
            status = new_entry.sign(skeystring, 'Organization')
        if status.error():
            return status

        chaindata['entry'] = new_entry
        self.entries.append(new_entry)
        return chaindata
コード例 #10
0
    def is_compliant(self) -> RetVal:
        '''Checks the fields to ensure that it meets spec requirements. If a field causes it 
		to be noncompliant, the noncompliant field is also returned'''
        status = self.is_data_compliant()
        if status.error():
            return status

        # Ensure signature compliance
        for info in self.signature_info:
            if info['type'] == SIGINFO_HASH:
                if not self.hash:
                    return RetVal(SignatureMissing, 'Hash')
                else:
                    continue

            if info['optional']:
                # Optional signatures, if present, may not be empty
                if info['name'] in self.signatures and not self.signatures[
                        info['name']]:
                    return RetVal(SignatureMissing,
                                  '%s-Signature' % info['name'])
            else:
                if info['name'] not in self.signatures or not self.signatures[
                        info['name']]:
                    return RetVal(SignatureMissing,
                                  '%s-Signature' % info['name'])

        return RetVal()
コード例 #11
0
    def preregister_account(self, port_str: str, uid: str) -> RetVal:
        '''Create a new account on the local server. This is a simple command because it is not 
		meant to create a local profile.'''

        if port_str:
            try:
                port = int(port_str)
            except:
                return RetVal(BadParameterValue, 'Bad port number')
        else:
            port = 2001

        if port < 0 or port > 65535:
            return RetVal(BadParameterValue, 'Bad port number')

        if '"' in uid or '/' in uid:
            return RetVal(BadParameterValue, "User ID can't contain \" or /")

        status = self.conn.connect('127.0.0.1', port)
        if status.error():
            return status

        regdata = serverconn.preregister(self.conn, '', uid, '')
        if regdata.error():
            return regdata
        self.conn.disconnect()

        if regdata['status'] != 200:
            return regdata

        if 'wid' not in regdata or 'regcode' not in regdata:
            return RetVal(InternalError, 'BUG: bad data from serverconn.preregister()') \
              .set_value('status', 300)

        return regdata
コード例 #12
0
ファイル: userprofile.py プロジェクト: darkwyrm/pyanselus
    def create_profile(self, name) -> RetVal:
        '''
		Creates a profile with the specified name. Profile names are not case-sensitive.

		Returns: 
		RetVal error state also contains a copy of the created profile as "profile"
		'''
        if not name:
            return RetVal(BadParameterValue, "BUG: name may not be empty")

        name_squashed = name.casefold()
        if self.__index_for_profile(name_squashed) >= 0:
            return RetVal(ResourceExists, name)

        profile = Profile(os.path.join(self.profile_folder, name_squashed))
        profile.make_id()
        self.profiles.append(profile)

        if len(self.profiles) == 1:
            profile.isdefault = True
            self.default_profile = name

        status = self.save_profiles()
        if status.error():
            return status

        status.set_value("profile", profile)
        return status
コード例 #13
0
ファイル: userprofile.py プロジェクト: darkwyrm/pyanselus
    def delete_profile(self, name) -> RetVal:
        '''
		Deletes the named profile and all files on disk contained in it.
		'''
        if name == 'default':
            return RetVal(BadParameterValue, "'default' is reserved")

        if not name:
            return RetVal(BadParameterValue, "BUG: name may not be empty")

        name_squashed = name.casefold()
        itemindex = self.__index_for_profile(name_squashed)
        if itemindex < 0:
            return RetVal(ResourceNotFound, "%s doesn't exist" % name)

        profile = self.profiles.pop(itemindex)
        if os.path.exists(profile.path):
            try:
                shutil.rmtree(profile.path)
            except Exception as e:
                return RetVal(ExceptionThrown, e.__str__())

        if profile.isdefault:
            if self.profiles:
                self.profiles[0].isdefault = True

        return self.save_profiles()
コード例 #14
0
def getwid(conn: ServerConnection, uid: str, domain: str) -> RetVal:
	'''Looks up a wid based on the specified user ID and optional domain'''

	if re.findall(r'[\\\/\s"]', uid) or len(uid) >= 64:
		return RetVal(BadParameterValue, 'user id')
	
	if domain:
		m = re.match(r'([a-zA-Z0-9]+\.)+[a-zA-Z0-9]+', domain)
		if not m or len(domain) >= 64:
			return RetVal(BadParameterValue, 'bad domain value')
	
	request = {
		'Action' : 'GETWID',
		'Data' : {
			'User-ID': uid
		}
	}
	if domain:
		request['Data']['Domain'] = domain
	
	status = conn.send_message(request)
	if status.error():
		return status
	
	response = conn.read_response(server_response)
	if response.error():
		return response
	
	if response['Code'] != 200:
		return wrap_server_error(response)
	
	return RetVal().set_value('Workspace-ID', response['Data']['Workspace-ID'])
コード例 #15
0
ファイル: rpc.py プロジェクト: darkwyrm/pyanselus
    def connect(self, host: str, port) -> RetVal:
        '''Creates a connection to the server.'''
        try:
            self.__sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            # Set a short timeout in case the server doesn't respond immediately,
            # which is the expectation as soon as a client connects.
            self.__sock.settimeout(10.0)
        except:
            return RetVal(NetworkError, "Couldn't create a socket")

        out_data = RetVal()
        out_data.set_value('socket', self.__sock)

        try:
            self.ip = socket.gethostbyname(host)
        except socket.gaierror:
            self.disconnect()
            return RetVal(ResourceNotFound, "Couldn't locate host %s" % host)

        try:
            self.__sock.connect((self.ip, port))
            self.port = port

            status = self.read_msg(pyanselus.rpc_schemas.greeting)
            if not status.error():
                self.version = status['msg']['version'].strip()

        except Exception as exc:
            self.disconnect()
            return RetVal(NetworkError,
                          f"Couldn't connect to host {host}: {exc}")

        # Set a timeout of 30 minutes
        self.__sock.settimeout(1800.0)
        return out_data
コード例 #16
0
def get_key(db: sqlite3.Connection, keyid: str) -> RetVal:
    '''Gets the specified key.
	Parameters:
	keyid : uuid

	Returns:
	'error' : string
	'key' : CryptoKey object
	'''

    cursor = db.cursor()
    cursor.execute(
        '''
		SELECT address,type,category,private,public,algorithm
		FROM keys WHERE keyid=?''', (keyid, ))
    results = cursor.fetchone()
    if not results or not results[0]:
        return RetVal(ResourceNotFound)

    if results[1] == 'asymmetric':
        public = base64.b85decode(results[4])
        private = base64.b85decode(results[3])
        key = encryption.EncryptionPair(public, private)
        return RetVal().set_value('key', key)

    if results[1] == 'symmetric':
        private = base64.b85decode(results[3])
        key = encryption.SecretKey(private)
        return RetVal().set_value('key', key)

    return RetVal(BadParameterValue, "Key must be 'asymmetric' or 'symmetric'")
コード例 #17
0
def add_key(db: sqlite3.Connection, key: encryption.CryptoKey,
            address: str) -> RetVal:
    '''Adds an encryption key to a workspace.
	Parameters:
	key: CryptoKey from encryption module
	address: full Anselus address, i.e. wid + domain
	
	Returns:
	error : string
	'''
    cursor = db.cursor()
    cursor.execute("SELECT keyid FROM keys WHERE keyid=?", (key.get_id(), ))
    results = cursor.fetchone()
    if results:
        return RetVal(ResourceExists)

    if key.enctype == 'XSALSA20':
        cursor.execute(
            '''INSERT INTO keys(keyid,address,type,category,private,algorithm)
			VALUES(?,?,?,?,?,?)''', (key.get_id(), address, 'symmetric', '',
                            key.get_key(), key.enctype))
        db.commit()
        return RetVal()

    if key.enctype == 'CURVE25519':
        cursor.execute(
            '''INSERT INTO keys(keyid,address,type,category,private,public,algorithm)
			VALUES(?,?,?,?,?,?,?)''',
            (key.get_id(), address, 'asymmetric', '', key.private.as_string(),
             key.public.as_string(), key.enctype))
        db.commit()
        return RetVal()

    return RetVal(BadParameterValue, "Key must be 'asymmetric' or 'symmetric'")
コード例 #18
0
def get_session_private_key(db: sqlite3.Connection, address: str) -> RetVal:
    '''Returns the private key for the device for a session'''
    cursor = db.cursor()
    cursor.execute("SELECT private_key FROM sessions WHERE address=?",
                   (address, ))
    results = cursor.fetchone()
    if not results or not results[0]:
        return RetVal(ResourceNotFound)
    return RetVal().set_value('key', results[0])
コード例 #19
0
ファイル: workspace.py プロジェクト: darkwyrm/pyanselus
    def generate(self, userid: str, server: str, wid: str,
                 pw: encryption.Password) -> RetVal:
        '''Creates all the data needed for an individual workspace account'''

        self.uid = userid
        self.wid = wid
        self.domain = server

        # Add workspace
        status = self.add_to_db(pw)
        if status.error():
            return status

        address = '/'.join([wid, server])

        # Generate user's encryption keys
        keys = {
            'identity': encryption.EncryptionPair(),
            'conrequest': encryption.EncryptionPair(),
            'broadcast': encryption.SecretKey(),
            'folder': encryption.SecretKey()
        }

        # Add encryption keys
        for key in keys.values():
            out = auth.add_key(self.db, key, address)
            if out.error():
                status = self.remove_workspace_entry(wid, server)
                if status.error():
                    return status

        # Add folder mappings
        foldermap = encryption.FolderMapping()

        folderlist = [
            'messages', 'contacts', 'events', 'tasks', 'notes', 'files',
            'files attachments'
        ]

        for folder in folderlist:
            foldermap.MakeID()
            foldermap.Set(address, keys['folder'].get_id(), folder, 'root')
            self.add_folder(foldermap)

        # Create the folders themselves
        try:
            self.path.mkdir(parents=True, exist_ok=True)
        except Exception as e:
            self.remove_from_db()
            return RetVal(ExceptionThrown, e.__str__())

        self.path.joinpath('files').mkdir(exist_ok=True)
        self.path.joinpath('files', 'attachments').mkdir(exist_ok=True)

        self.set_userid(userid)
        return RetVal()
コード例 #20
0
def addentry(conn: ServerConnection, entry: EntryBase, ovkey: CryptoString,
	spair: SigningPair) -> RetVal:
	'''Handles the process to upload an entry to the server.'''

	conn.send_message({
		'Action' : "ADDENTRY",
		'Data' : { 'Base-Entry' : entry.make_bytestring(0).decode() }
	})

	response = conn.read_response(server_response)
	if response['Code'] != 100:
		return wrap_server_error(response)

	for field in ['Organization-Signature', 'Hash', 'Previous-Hash']	:
		if field not in response['Data']:
			return RetVal(ServerError, f"Server did not return required field {field}")

	entry.signatures['Organization'] =  response['Data']['Organization-Signature']

	# A regular client will check the entry cache, pull updates to the org card, and get the 
	# verification key. Because this is just an integration test, we skip all that and just use
	# the known verification key from earlier in the test.
	status = entry.verify_signature(ovkey, 'Organization')
	if status.error():
		return status
	
	entry.prev_hash = response['Data']['Previous-Hash']
	entry.hash = response['Data']['Hash']
	status = entry.verify_hash()
	if status.error():
		return status
	
	# User sign and verify
	status = entry.sign(spair.private, 'User')
	if status.error():
		return status

	status = entry.verify_signature(spair.public, 'User')
	if status.error():
		return status

	status = entry.is_compliant()
	if status.error():
		return status

	conn.send_message({
		'Action' : "ADDENTRY",
		'Data' : { 'User-Signature' : entry.signatures['User'] }
	})
	
	response = conn.read_response(server_response)
	if response['Code'] != 200:
		return wrap_server_error(response)

	return RetVal()
コード例 #21
0
def split_address(address):
	'''Splits an Anselus numeric address into its two parts.'''
	parts = address.split('/')
	if len(parts) != 2 or \
		not parts[0] or \
		not parts[1] or \
		not validate_uuid(parts[0]):
		return RetVal(BadParameterValue, 'Bad workspace address')
	out = RetVal()
	out.set_value('wid', parts[0])
	out.set_value('domain', parts[1])
	return out
コード例 #22
0
    def verify_hash(self) -> RetVal:
        '''Checks that the entry's actual hash matches that in the hash field'''
        current_hash = CryptoString(self.hash)
        if not current_hash.is_valid():
            return RetVal(InvalidHash,
                          f"{self.hash} is not a valid CryptoString")

        status = self.get_hash(current_hash.prefix)
        if status.error():
            return status

        return RetVal()
コード例 #23
0
def devkey(conn: ServerConnection, devid: str, oldpair: EncryptionPair, newpair: EncryptionPair):
	'''Replaces the specified device's key stored on the server'''
	if not utils.validate_uuid(devid):
		return RetVal(AnsBadRequest, 'Invalid device ID').set_value('status', 400)

	conn.send_message({
		'Action' : "DEVKEY",
		'Data' : { 
			'Device-ID': devid,
			'Old-Key': oldpair.public.as_string(),
			'New-Key': newpair.public.as_string()
		}
	})

	# Receive, decrypt, and return the server challenge
	response = conn.read_response(server_response)
	if response.error():
		return response
	
	if response['Code'] != 100:
		return wrap_server_error(response)

	if 'Challenge' not in response['Data'] or 'New-Challenge' not in response['Data']:
		return RetVal(ServerError, 'server did not return both device challenges')
	
	status = oldpair.decrypt(response['Data']['Challenge'])
	if status.error():
		cancel(conn)
		return RetVal(DecryptionFailure, 'failed to decrypt device challenge for old key')

	request = {
		'Action' : "DEVKEY",
		'Data' : { 
			'Response' : status['data']
		}
	}

	status = newpair.decrypt(response['Data']['New-Challenge'])
	if status.error():
		cancel(conn)
		return RetVal(DecryptionFailure, 'failed to decrypt device challenge for new key')
	request['Data']['New-Response'] = status['data']
	conn.send_message(request)

	response = conn.read_response(None)
	if response.error():
		return response
	
	if response['Code'] == 200:
		return RetVal()
	
	return wrap_server_error(response)
コード例 #24
0
def remove_device_session(db, devid: str) -> RetVal:
    '''
	Removes an authorized device from the workspace. Returns a boolean success code.
	'''
    cursor = db.cursor()
    cursor.execute("SELECT devid FROM sessions WHERE devid=?", (devid, ))
    results = cursor.fetchone()
    if not results or not results[0]:
        return RetVal(ResourceNotFound)

    cursor.execute("DELETE FROM sessions WHERE devid=?", (devid, ))
    db.commit()
    return RetVal()
コード例 #25
0
	def decrypt(self, data : str) -> RetVal:
		'''Decrypt the passed data using the private key and return the raw data in the field 
		'data'. Base85 decoding of the data is optional, but enabled by default.'''
		if not isinstance(data, str):
			return RetVal(BadParameterType, 'string expected')
		
		try:
			sealedbox = nacl.public.SealedBox(nacl.public.PrivateKey(self.private.raw_data()))
			decrypted_data = sealedbox.decrypt(data.encode(), Base85Encoder)
		except Exception as e:
			return RetVal(ExceptionThrown, str(e))
		
		return RetVal().set_value('data', decrypted_data.decode())
コード例 #26
0
	def encrypt(self, data : bytes) -> RetVal:
		'''Encrypt the passed data using the public key and return the Base85-encoded data in the 
		field 'data'.'''
		if not isinstance(data, bytes):
			return RetVal(BadParameterType, 'bytes expected')
		
		try:
			sealedbox = nacl.public.SealedBox(nacl.public.PublicKey(self.public.raw_data()))
			encrypted_data = sealedbox.encrypt(data, Base85Encoder).decode()
		except Exception as e:
			return RetVal(ExceptionThrown, str(e))
		
		return RetVal().set_value('data', encrypted_data)
コード例 #27
0
	def sign(self, data : bytes) -> RetVal:
		'''Return a Base85-encoded signature for the supplied data in the field 'signature'.'''
		if not isinstance(data, bytes):
			return RetVal(BadParameterType, 'bytes expected for data')
		
		key = nacl.signing.SigningKey(self.private.raw_data())

		try:
			signed = key.sign(data, Base85Encoder)
		except Exception as e:
			return RetVal(ExceptionThrown, e)
		
		return RetVal().set_value('signature', 'ED25519:' + signed.signature.decode())
コード例 #28
0
	def write(self, text: str) -> RetVal:
		'''Sends a string over a socket'''

		if not self.socket:
			return RetVal(NetworkError, 'Invalid connection')
		
		try:
			self.socket.send(text.encode())
		except Exception as exc:
			self.socket.close()
			return RetVal(ExceptionThrown, exc.__str__())
		
		return RetVal()
コード例 #29
0
def load_encryptionpair(path: str) -> RetVal:
	'''Instantiates a keypair from a file'''
	if not path:
		return RetVal(BadParameterValue, 'path may not be empty')
	
	if not os.path.exists(path):
		return RetVal(ResourceNotFound, '%s exists' % path)
	
	indata = None
	try:
		with open(path, "r") as fhandle:
			indata = json.load(fhandle)
	
	except Exception as e:
		return RetVal(ExceptionThrown, e)
	
	if not isinstance(indata, dict):
		return RetVal(BadData, 'File does not contain an Anselus JSON keypair')

	try:
		jsonschema.validate(indata, __encryption_pair_schema)
	except jsonschema.ValidationError:
		return RetVal(BadData, "file data does not validate")
	except jsonschema.SchemaError:
		return RetVal(InternalError, "BUG: invalid EncryptionPair schema")

	public_key = CryptoString(indata['PublicKey'])
	private_key = CryptoString(indata['PrivateKey'])
	if not public_key.is_valid() or not private_key.is_valid():
		return RetVal(BadData, 'Failure to base85 decode key data')
	
	return RetVal().set_value('keypair', EncryptionPair(public_key, private_key))
コード例 #30
0
def load_secretkey(path: str) -> RetVal:
	'''Instantiates a secret key from a file'''
	if not path:
		return RetVal(BadParameterValue, 'path may not be empty')
	
	if not os.path.exists(path):
		return RetVal(ResourceNotFound, '%s exists' % path)
	
	indata = None
	try:
		with open(path, "r") as fhandle:
			indata = json.load(fhandle)
	
	except Exception as e:
		return RetVal(ExceptionThrown, e)
	
	if not isinstance(indata, dict):
		return RetVal(BadData, 'File does not contain an Anselus JSON secret key')

	try:
		jsonschema.validate(indata, __secret_key_schema)
	except jsonschema.ValidationError:
		return RetVal(BadData, "file data does not validate")
	except jsonschema.SchemaError:
		return RetVal(InternalError, "BUG: invalid SecretKey schema")

	key = CryptoString(indata['SecretKey'])
	if not key.is_valid():
		return RetVal(BadData, 'Failure to base85 decode key data')
	
	return RetVal().set_value('key', SecretKey(key))