def to_data(self): """Return this object serialised to a json-encoded dictionary""" data = {} if self.is_null(): return data from Acquire.ObjectStore import datetime_to_string \ as _datetime_to_string from Acquire.ObjectStore import bytes_to_string \ as _bytes_to_string data["user_uid"] = str(self._user_uid) data["session_uid"] = str(self._session_uid) data["identity_url"] = str(self._identity_url) data["identity_uid"] = str(self._identity_uid) data["uid"] = self._uid data["signature"] = _bytes_to_string(self._signature) data["siguid"] = _bytes_to_string(self._siguid) try: data["is_testing"] = self._is_testing except: pass return data
def to_data(self, pubkey=None): """Return a json-serialisable dictionary of the object. If 'pubkey' is supplied, then sensitive data used by the uploader is encrypted """ if self.is_null(): return {} data = {} data["drive_uid"] = self._drive_uid data["file_uid"] = self._file_uid data["secret"] = self._secret data["uid"] = self._uid if pubkey is not None: from Acquire.Crypto import PublicKey as _PublicKey from Acquire.ObjectStore import bytes_to_string \ as _bytes_to_string import json as _json if not isinstance(pubkey, _PublicKey): raise TypeError("pubkey must be type PublicKey") d = _bytes_to_string(pubkey.encrypt(_json.dumps(data))) data = {} data["is_encrypted"] = True data["data"] = d return data
def logout(self): """Log out from the current session""" if self.is_logged_in() or self.is_logging_in(): service = self.identity_service() args = {"session_uid": self._session_uid} if self.is_logged_in(): from Acquire.Client import Authorisation as _Authorisation authorisation = _Authorisation( resource="logout %s" % self._session_uid, user=self) args["authorisation"] = authorisation.to_data() else: # we are not fully logged in so cannot generate an # authorisation for the logout from Acquire.ObjectStore import bytes_to_string \ as _bytes_to_string resource = "logout %s" % self._session_uid signature = self.signing_key().sign(resource) args["signature"] = _bytes_to_string(signature) result = service.call_function(function="logout", args=args) self._status = _LoginStatus.LOGGED_OUT return result
def validate_password(self, password, otpcode, remember_device=False, device_secret=None): """Validate that the passed password and one-time-code are valid. If they are, then do nothing. Otherwise raise an exception. If 'remember_device' is true, then this returns the provisioning uri needed to initialise the OTP code for this account """ if not self.is_active(): raise UserValidationError( "Cannot validate against an inactive account") # see if we can decrypt the private key using the password privkey = _PrivateKey.read_bytes(self._privkey, password) if device_secret: # decrypt the passed device secret and check the supplied # otpcode for that... otp = _OTP.decrypt(_string_to_bytes(device_secret), privkey) otp.verify(otpcode) else: # now decrypt the secret otp and validate the supplied otpcode otp = _OTP.decrypt(self._otp_secret, privkey) otp.verify(otpcode) if remember_device: # create a new OTP that is unique for this device and return # this together with the provisioning code otp = _OTP() otpsecret = _bytes_to_string(otp.encrypt(privkey.public_key())) return (otpsecret, otp.provisioning_uri(self.username()))
def to_data(self): """Return a json-serialisable dictionary for this object. Note that this does not contain any information about the local file itself - just the name it should be called on the object store and the size, checksum and acl. If the file (or compressed file) is sufficiently small then this will also contain the packed version of that file data Returns: dict: JSON serialisable dictionary of object """ data = {} if not self.is_null(): from Acquire.ObjectStore import datetime_to_string \ as _datetime_to_string data["filename"] = self.filename() data["filesize"] = self.filesize() data["checksum"] = self.checksum() if self._aclrules is not None: data["aclrules"] = self._aclrules.to_data() data["drive_uid"] = self.drive_uid() if self._local_filedata is not None: from Acquire.ObjectStore import bytes_to_string \ as _bytes_to_string data["filedata"] = _bytes_to_string(self._local_filedata) if self._compression is not None: data["compression"] = self._compression return data
def logout(self): """Log out from the current session""" if self.is_logged_in() or self.is_logging_in(): identity_url = self.identity_service_url() if identity_url is None: return # create a permission message that can be signed # and then validated by the user permission = "Log out request for %s" % self._session_uid signature = self.signing_key().sign(permission) print("Logging out %s from session %s" % (self._username, self._session_uid)) result = _call_function( identity_url, "logout", args_key=self.identity_service().public_key(), username=self._username, session_uid=self._session_uid, permission=permission, signature=_bytes_to_string(signature)) print(result) self._status = _LoginStatus.LOGGED_OUT return result
def pack_return_value(result, key=None, response_key=None, public_cert=None): """Pack the passed result into a json string, optionally encrypting the result with the passed key, and optionally supplying a public response key, with which the function being called should encrypt the response. If public_cert is provided then we will ask the service to sign their response. Note that you can only ask the service to sign their response if you provide a 'reponse_key' for them to encrypt it with too """ try: sign_result = key["sign_with_service_key"] except: sign_result = False key = _get_key(key) response_key = _get_key(response_key) if response_key: result["encryption_public_key"] = _bytes_to_string( response_key.bytes()) if public_cert: result["sign_with_service_key"] = True elif public_cert: raise PackingError( "You cannot ask the service to sign the response " "without also providing a key to encrypt it with too") result = _json.dumps(result).encode("utf-8") if key: response = {} result_data = key.encrypt(result) if sign_result: # sign using the signing certificate for this service signature = _get_signing_certificate().sign(result_data) response["signature"] = _bytes_to_string(signature) response["data"] = _bytes_to_string(result_data) response["encrypted"] = True result = _json.dumps(response).encode("utf-8") return result
def end_profile(pr, results): pr.disable() t = _tempfile.mktemp() pr.dump_stats(t) with open(t, "rb") as FILE: data = FILE.read() _os.unlink(t) results["profile_data"] = _bytes_to_string(data)
def to_data(self): """Return a data representation of this object (dictionary)""" if self._username is None: return None data = {} data["username"] = self._username data["status"] = self._status data["uuid"] = self._uuid # the keys and secret are arbitrary binary data. # These need to be base64 encoded and then turned into strings data["private_key"] = _bytes_to_string(self._privkey) data["public_key"] = _bytes_to_string(self._pubkey) data["otp_secret"] = _bytes_to_string(self._otp_secret) return data
def end_profile(pr, results): pr.disable() import tempfile as _tempfile from Acquire.ObjectStore import bytes_to_string as _bytes_to_string t = _tempfile.mktemp() pr.dump_stats(t) with open(t, "rb") as FILE: data = FILE.read() _os.unlink(t) results["profile_data"] = _bytes_to_string(data)
def to_data(self, password=None): """Serialise this key to a dictionary, using the supplied password to encrypt the private key and certificate""" data = {} data["uuid"] = self._uuid data["service_type"] = self._service_type data["service_url"] = self._service_url # keys are binary and need to be encoded data["public_certificate"] = self._pubcert.to_data() data["public_key"] = self._pubkey.to_data() if password: # only serialise private data if a password was provided data["private_certificate"] = self._privcert.to_data(password) data["private_key"] = self._privkey.to_data(password) data["otpsecret"] = _bytes_to_string(self._otpsecret) data["admin_password"] = _bytes_to_string(self._admin_password) return data
def create(user_uid, password, primary_password, device_uid=None): """Create the credentials for the user with specified user_uid, optionally logging in via the specified device_uid, using the specified password, to protect the passed "primary_password" This returns the OTP that has been created to be associated with these credentials """ from Acquire.Crypto import PrivateKey as _PrivateKey from Acquire.Crypto import OTP as _OTP from Acquire.ObjectStore import ObjectStore as _ObjectStore from Acquire.Service import get_service_account_bucket \ as _get_service_account_bucket from Acquire.ObjectStore import bytes_to_string as _bytes_to_string if device_uid is None: device_uid = user_uid privkey = _PrivateKey(name="user_creds_key %s" % user_uid) otp = _OTP() otpsecret = otp.encrypt(privkey.public_key()) primary_password = privkey.encrypt(primary_password) data = {"primary_password": _bytes_to_string(primary_password), "private_key": privkey.to_data(passphrase=password), "otpsecret": _bytes_to_string(otpsecret) } key = "%s/credentials/%s/%s" % (_user_root, user_uid, device_uid) bucket = _get_service_account_bucket() _ObjectStore.set_object_from_json(bucket=bucket, key=key, data=data) return otp
def _set_userinfo(self, userinfo, user_uid, service_uid): """Save the userfile for the passed user_uid logging into the passed identity service with service_uid """ from Acquire.ObjectStore import bytes_to_string as _bytes_to_string import json as _json filename = self._get_userinfo_filename(user_uid=user_uid, service_uid=service_uid) key = self._get_wallet_key().public_key() data = _bytes_to_string(key.encrypt(_json.dumps(userinfo))) userinfo = {"username": userinfo["username"], "user_uid": user_uid, "data": data} _write_json(data=userinfo, filename=filename)
def to_data(self, passphrase=None): """Return a json-serialisable dictionary that contains all data for this object Args: passphrase (str, default=None): Passphrase to use to encrypt OSPar Returns: dict: JSON serialisable dictionary """ data = {} if self._url is None: return data from Acquire.ObjectStore import datetime_to_string \ as _datetime_to_string from Acquire.ObjectStore import bytes_to_string \ as _bytes_to_string data["url"] = _bytes_to_string(self._url) data["uid"] = self._uid data["key"] = self._key data["expires_datetime"] = _datetime_to_string(self._expires_datetime) data["is_readable"] = self._is_readable data["is_writeable"] = self._is_writeable try: if self._service_url is not None: data["service_url"] = self._service_url except: pass try: privkey = self._privkey except: privkey = None if privkey is not None: if passphrase is not None: data["privkey"] = privkey.to_data(passphrase) # note that we don't save the driver details as these # are stored separately on the service return data
def encrypt_data(self, data): """Encrypt the passed data so that only the daemon running on the cluster can decrypt it. This will be encrypted as; data = {"cluster_uid" : "CLUSTER_UID", "fingerprint" : "KEY_FINGERPRINT", "encrypted_data" : "ENCRYPTED_DATA"} """ if self.is_null(): raise PermissionError("You cannot encrypt using a null cluster!") from Acquire.ObjectStore import bytes_to_string as _bytes_to_string import json as _json return {"cluster_uid": str(self.uid()), "fingerprint": str(self.public_key().fingerprint()), "encrypted_data": _bytes_to_string( self.public_key().encrypt(_json.dumps(data))) }
def to_data(self): """Return this object serialised to a json-encoded dictionary""" data = {} if self.is_null(): return data data["user_uid"] = str(self._user_uid) data["session_uid"] = str(self._session_uid) data["identity_url"] = str(self._identity_url) data["auth_timestamp"] = self._auth_timestamp data["signature"] = _bytes_to_string(self._signature) try: data["is_testing"] = self._is_testing except: pass return data
def upload(self, chunk): """Upload the next chunk of the file""" if self.is_null(): raise PermissionError("Cannot upload a chunk to a null uploader!") service = self.service() if service is None: raise PermissionError("Cannot upload a chunk to a null service!") # first, compress the chunk from Acquire.ObjectStore import bytes_to_string as _bytes_to_string from Acquire.Crypto import Hash as _Hash import bz2 as _bz2 if isinstance(chunk, str): chunk = chunk.encode("utf-8") chunk = _bz2.compress(chunk) md5 = _Hash.md5(chunk) chunk = _bytes_to_string(chunk) if self._chunk_idx is None: self._chunk_idx = 0 else: self._chunk_idx = self._chunk_idx + 1 secret = _Hash.multi_md5( self._secret, "%s%s%d" % (self._drive_uid, self._file_uid, self._chunk_idx)) args = {} args["drive_uid"] = self._drive_uid args["file_uid"] = self._file_uid args["chunk_index"] = self._chunk_idx args["secret"] = secret args["data"] = chunk args["checksum"] = md5 service.call_function(function="upload_chunk", args=args)
def challenge_service(self, service): """Send a challenge to the passed service, returning the actual service returned. This will only pass if our copy of the service matches us with the copy returned from the actual service. This verifies that there is a real service sitting at that URL, and that we have the right keys and certs """ from Acquire.Crypto import PrivateKey as _PrivateKey from Acquire.ObjectStore import bytes_to_string as _bytes_to_string from Acquire.Service import Service as _Service challenge = _PrivateKey.random_passphrase() pubkey = service.public_key() encrypted_challenge = pubkey.encrypt(challenge) args = {"challenge": _bytes_to_string(encrypted_challenge), "fingerprint": pubkey.fingerprint()} if service.uid().startswith("STAGE"): # we cannot call using the service directly, as it is # not yet fully constructed from Acquire.Service import get_this_service as _get_this_service from Acquire.Service import call_function as _call_function this_service = _get_this_service(need_private_access=True) result = _call_function(service_url=service.service_url(), function=None, args=args, args_key=service.public_key(), response_key=this_service.private_key(), public_cert=service.public_certificate()) else: result = service.call_function(function=None, args=args) if result["response"] != challenge: raise PermissionError( "Failure of the service %s to correctly respond " "to the challenge!" % service) return _Service.from_data(result["service_info"])
def create(username, password, _service_uid=None, _service_public_key=None): """Create a new account with username 'username', which will be secured using the passed password. Note that this will create an account with a specified user UID, meaning that different users can have the same username. We identify the right user via the combination of username, password and OTP code. Normally the UID of the service, and the skeleton key used to encrypt the backup password are obtained directly from the service. However, when initialising a new service we must pass these directly. In those cases, pass the object using _service_uid and _service_public_key This returns a tuple of the user_uid and OTP for the newly-created account """ from Acquire.ObjectStore import create_uuid as _create_uuid from Acquire.Crypto import PrivateKey as _PrivateKey from Acquire.Crypto import PublicKey as _PublicKey from Acquire.ObjectStore import ObjectStore as _ObjectStore from Acquire.Service import get_service_account_bucket \ as _get_service_account_bucket from Acquire.ObjectStore import bytes_to_string as _bytes_to_string from Acquire.Identity import UserCredentials as _UserCredentials from Acquire.ObjectStore import get_datetime_now_to_string \ as _get_datetime_now_to_string if _service_public_key is None: from Acquire.Service import get_this_service as _get_this_service service_pubkey = _get_this_service().public_skeleton_key() assert (service_pubkey is not None) else: service_pubkey = _service_public_key if not isinstance(service_pubkey, _PublicKey): raise TypeError("The service public key must be type PublicKey") if _service_uid is None: from Acquire.Service import get_this_service \ as _get_this_service service_uid = _get_this_service(need_private_access=False).uid() else: service_uid = _service_uid # create a UID for this new user user_uid = _create_uuid() # now create the primary password for this user and use # this to encrypt the special keys for this user privkey = _PrivateKey(name="user_secret_key %s %s" % (username, user_uid)) primary_password = _PrivateKey.random_passphrase() bucket = _get_service_account_bucket() # now create the credentials used to validate a login otp = _UserCredentials.create(user_uid=user_uid, password=password, primary_password=primary_password) # create the user account user = UserAccount(username=username, user_uid=user_uid, private_key=privkey, status="active") # now save a lookup from the username to this user_uid # (many users can have the same username). Use this lookup # to hold a recovery password for this account recovery_password = _bytes_to_string( service_pubkey.encrypt(primary_password)) key = "%s/names/%s/%s" % (_user_root, user.encoded_name(), user_uid) _ObjectStore.set_string_object(bucket=bucket, key=key, string_data=recovery_password) # now save a lookup from the hashed username+password # to the user_uid, so that we can # quickly find matching user_uids (expect few people will have # exactly the same username and password). This will # save the exact time this username-password combination # was set encoded_password = _UserCredentials.hash(username=username, password=password, service_uid=service_uid) key = "%s/passwords/%s/%s" % (_user_root, encoded_password, user_uid) _ObjectStore.set_string_object( bucket=bucket, key=key, string_data=_get_datetime_now_to_string()) # finally(!) save the account itself to the object store key = "%s/uids/%s" % (_user_root, user_uid) data = user.to_data(passphrase=primary_password) _ObjectStore.set_object_from_json(bucket=bucket, key=key, data=data) # return the OTP and user_uid return (user_uid, otp)
def register_service(service, registry_uid): """Call this function to register the passed new service with the specified registry_uid. This function will complete registration and construction of the service """ from Acquire.Service import Service as _Service if not isinstance(service, _Service): raise TypeError("You can only register Service objects") if not service.uid().startswith("STAGE1"): raise PermissionError( "You can only register services that are at STAGE1 of " "construction") if service.service_type() == "registry": from Acquire.Registry import get_registry_details \ as _get_registry_details details = _get_registry_details(registry_uid=registry_uid) from Acquire.Service import Service as _Service canonical_url = _Service.get_canonical_url(details["canonical_url"]) # make sure that everything matches what was specified if canonical_url != service.canonical_url(): raise PermissionError( "Cannot change the canonical URL. I expect %s, but " "you are trying to set to %s" % (service.canonical_url(), details["canonical_url"])) from Acquire.Registry import update_registry_keys_and_certs \ as _update_registry_keys_and_certs _update_registry_keys_and_certs( registry_uid=registry_uid, public_key=service.public_key(), public_certificate=service.public_certificate()) service.create_stage2(service_uid=registry_uid, response=service._uid) return service # get the trusted registry from Acquire.Registry import get_trusted_registry_service \ as _get_trusted_registry_service registry_service = _get_trusted_registry_service(service_uid=registry_uid) if not registry_service.is_registry_service(): raise PermissionError( "You can only register new services on an existing and valid " "registry service. Not %s" % registry_service) from Acquire.ObjectStore import bytes_to_string as _bytes_to_string pubkey = registry_service.public_key() challenge = pubkey.encrypt(service.uid()) args = { "service": service.to_data(), "challenge": _bytes_to_string(challenge), "fingerprint": pubkey.fingerprint() } result = registry_service.call_function(function="register_service", args=args) service_uid = result["service_uid"] response = result["response"] service.create_stage2(service_uid=service_uid, response=response) return service
def send_password(self, url, username=None, remember_password=True, remember_device=None, dryrun=None): """Send a password and one-time code to the supplied login url""" self._manual_password = False self._manual_otpcode = False if not remember_password: remember_device = False # the login URL is of the form "server/code" words = url.split("/") identity_service = "/".join(words[0:-1]) short_uid = words[-1] # get the public key of this identity service service_key = self._get_service_key(identity_service) service_cert = self._get_service_cert(identity_service) if not username: # choose a username from any existing files... username = self._get_username() print("Logging in using username '%s'" % username) password = self._get_user_password(username) otpcode = self._get_otpcode(username) print("\nLogging in to '%s', session '%s'..." % (identity_service, short_uid), end="") _sys.stdout.flush() if dryrun: print("Calling %s with username=%s, password=%s, otpcode=%s, " "remember_device=%s, device_uid=%s, short_uid=%s" % (identity_service, username, password, otpcode, remember_device, self._device_uid, short_uid)) return try: key = _PrivateKey() response = _call_function(identity_service, "login", args_key=service_key, response_key=key, public_cert=service_cert, username=username, password=password, otpcode=otpcode, remember_device=remember_device, device_uid=self._device_uid, short_uid=short_uid) print("SUCCEEDED!") _sys.stdout.flush() except Exception as e: print("FAILED!") _sys.stdout.flush() raise LoginError("Failed to log in: %s" % str(e)) if remember_password: try: provisioning_uri = response["provisioning_uri"] except: provisioning_uri = None try: device_uid = response["device_uid"] except: device_uid = None otpsecret = None if provisioning_uri: try: otpsecret = _re.search(r"secret=([\w\d+]+)&issuer", provisioning_uri).groups()[0] except: pass try: user_info = self._read_userinfo(username) except: user_info = {} if user_info is None: user_info = {} pubkey = self._wallet_key.public_key() must_write = self._manual_password if otpsecret: if self._manual_otpcode: must_write = True if must_write: user_info["username"] = username.encode("utf-8").decode( "utf-8") user_info["password"] = _bytes_to_string( pubkey.encrypt(password.encode("utf-8"))) if otpsecret: user_info["otpsecret"] = _bytes_to_string( pubkey.encrypt(otpsecret.encode("utf-8"))) user_info["device_uid"] = device_uid packed_data = _pack_arguments(user_info) with open(Wallet._get_userfile(username), "wb") as FILE: FILE.write(packed_data) self._manual_password = False self._manual_otpcode = False return response
def call_admin_function(self, function, args={}, service_url=None, remember_password=True, remember_device=None): """Call the admin function 'function' using supplied arguments 'args', on the service at 'service_url' """ self._manual_password = False self._manual_otpcode = False if not remember_password: remember_device = False if not service_url: # choose a service_url from any existing files... service_url = self._get_service_url() # get the public key of this identity service service_key = self._get_service_key(service_url) service_cert = self._get_service_cert(service_url) password = self._get_admin_password(service_url) otpcode = self._get_otpcode(service_url) strargs = str(args) args["password"] = password args["otpcode"] = otpcode args["remember_device"] = remember_device print("\nCalling '%s' with %s... " % (function, strargs), end="") _sys.stdout.flush() try: key = _PrivateKey() response = _call_function(service_url, function, args_key=service_key, response_key=key, public_cert=service_cert, args=args) print("SUCCEEDED!") _sys.stdout.flush() except Exception as e: print("FAILED!") _sys.stdout.flush() raise LoginError("Failed to log in: %s" % str(e)) if remember_password: try: provisioning_uri = response["provisioning_uri"] except: provisioning_uri = None otpsecret = None if provisioning_uri: try: otpsecret = _re.search(r"secret=([\w\d+]+)&issuer", provisioning_uri).groups()[0] except: pass try: service_info = self._read_service_info(service_url) except: service_info = {} if service_info is None: service_info = {} pubkey = self._wallet_key.public_key() must_write = self._manual_password if otpsecret: if self._manual_otpcode: must_write = True if must_write: service_info["service_url"] = service_url.encode( "utf-8").decode("utf-8") service_info["password"] = _bytes_to_string( pubkey.encrypt(password.encode("utf-8"))) if otpsecret: service_info["otpsecret"] = _bytes_to_string( pubkey.encrypt(otpsecret.encode("utf-8"))) packed_data = _pack_arguments(service_info) with open(ServiceWallet._get_service_file(service_url), "wb") as FILE: FILE.write(packed_data) self._manual_password = False self._manual_otpcode = False return response
def run(args): """Call this function to initiate the two-step file-download process. Step 1: download - tells the service to download the file. If the file is small then the file will be in the response. Otherwise a OSPar will be returned that will let you download the file. If this is the case, then you must call step 2... Step 2: downloaded - after you have downloaded the file from the OSPar call OSPar.close() so that the service knows that the OSPar is no longer needed and can be deleted """ drive_uid = args["drive_uid"] filename = args["filename"] try: authorisation = Authorisation.from_data(args["authorisation"]) except: authorisation = None try: par_uid = args["par_uid"] except: par_uid = None try: secret = args["secret"] except: secret = None public_key = PublicKey.from_data(args["encryption_key"]) if "version" in args: version = str(args["version"]) else: version = None if "force_par" in args: force_par = args["force_par"] else: force_par = None if "must_chunk" in args: must_chunk = args["must_chunk"] else: must_chunk = False if must_chunk: must_chunk = True if force_par: force_par = True if par_uid is not None: registry = PARRegistry() (par, identifiers) = registry.load(par_uid=par_uid, secret=secret) else: par = None identifiers = None drive = DriveInfo(drive_uid=drive_uid) return_value = {} (filemeta, filedata, par, downloader) = drive.download(filename=filename, version=version, authorisation=authorisation, encrypt_key=public_key, force_par=force_par, must_chunk=must_chunk, par=par, identifiers=identifiers) if filemeta is not None: return_value["filemeta"] = filemeta.to_data() if filedata is not None: from Acquire.ObjectStore import bytes_to_string as _bytes_to_string return_value["filedata"] = _bytes_to_string(filedata) if par is not None: return_value["download_par"] = par.to_data() if downloader is not None: return_value["downloader"] = downloader.to_data(pubkey=public_key) return return_value
def get_trusted_registry_service(registry_uid=None, service_uid=None, service_url=None): """Return the trusted service info for the registry with specified registry_uid, or get any trusted registry service using either 'service_uid' or 'service_url' as a starting hint to locate a suitable registry """ if service_uid is not None: # for the moment, just try to get one registry. Eventually we should # try to get several in case this one is down registry_uid = get_primary_registry_uid(service_uid) return get_trusted_registry_service(registry_uid=registry_uid) if service_url is not None: if service_url.find(".") != -1: # try the main acquire registry first return get_trusted_registry_service(registry_uid="a0-a0") else: # this must be testing... return get_trusted_registry_service(registry_uid="Z9-Z9") if registry_uid is None: raise PermissionError( "You must specify one of registry_uid, service_uid " "or service_url") from Acquire.Service import get_trusted_service as _get_trusted_service try: registry_service = _get_trusted_service(service_uid=registry_uid, autofetch=False) except: registry_service = None if registry_service is not None: if not registry_service.is_registry_service(): from Acquire.Service import ServiceError raise ServiceError("The requested service (%s) for %s is NOT a " "registry service!" % (registry_service, registry_uid)) if registry_service.uid() != registry_uid: from Acquire.Service import ServiceError raise ServiceError( "Disagreement of UID (%s) is NOT the right registry service!" % registry_service) # everything is ok - we have seen this registry before return registry_service # boostrapping from Acquire.Registry import get_registry_details \ as _get_registry_details details = _get_registry_details(registry_uid) from Acquire.Service import call_function as _call_function from Acquire.Service import Service as _Service from Acquire.Crypto import get_private_key as _get_private_key from Acquire.Crypto import PrivateKey as _PrivateKey from Acquire.Crypto import PublicKey as _PublicKey from Acquire.ObjectStore import bytes_to_string as _bytes_to_string from Acquire.ObjectStore import string_to_bytes as _string_to_bytes privkey = _get_private_key(key="registry") pubkey = _PublicKey.from_data(details["public_key"]) pubcert = _PublicKey.from_data(details["public_certificate"]) # ask the registry to return to us their latest details - use # a challenge-response to make sure that the response is # properly returned challenge = _PrivateKey.random_passphrase() encrypted_challenge = _bytes_to_string(pubkey.encrypt(challenge)) args = { "challenge": encrypted_challenge, "fingerprint": pubkey.fingerprint() } result = _call_function(service_url=details["canonical_url"], function=None, args=args, args_key=pubkey, response_key=privkey, public_cert=pubcert) if result["response"] != challenge: from Acquire.Service import ServiceError raise ServiceError( "The requested service (%s) failed to respond to the challenge!" % registry_service) registry_service = _Service.from_data(result["service_info"]) if not registry_service.is_registry_service(): from Acquire.Service import ServiceError raise ServiceError( "The requested service (%s) is NOT a registry service!" % registry_service) if registry_service.uid() != details["uid"]: from Acquire.Service import ServiceError raise ServiceError( "Disagreement of UID (%s) is NOT the right registry service!" % registry_service) # ok - we've got the registry - add this to the set of # trusted services so that we don't need to bootstrap from # the registry details again from Acquire.Service import trust_service as _trust_service _trust_service(registry_service) return registry_service
def pack_return_value(function=None, payload=None, key=None, response_key=None, public_cert=None, private_cert=None): """Pack the passed result into a json string, optionally encrypting the result with the passed key, and optionally supplying a public response key, with which the function being called should encrypt the response. If public_cert is provided then we will ask the service to sign their response. Note that you can only ask the service to sign their response if you provide a 'reponse_key' for them to encrypt it with too """ try: sign_result = key["sign_with_service_key"] except: sign_result = False key = _get_key(key) response_key = _get_key(response_key) from Acquire.ObjectStore import bytes_to_string as _bytes_to_string from Acquire.ObjectStore import get_datetime_now_to_string \ as _get_datetime_now_to_string result = {} if function is None and "function" in payload: function = payload["function"] if response_key: result["encryption_public_key"] = _bytes_to_string( response_key.bytes()) if public_cert: result["sign_with_service_key"] = public_cert.fingerprint() elif sign_result and (key is None): from Acquire.Service import PackingError raise PackingError( "You cannot ask the service to sign the response " "without also providing a key to encrypt it with too") result["payload"] = payload now = _get_datetime_now_to_string() result["synctime"] = now result["function"] = function if key is None: if sign_result: from Acquire.Service import PackingError raise PackingError( "The service must encrypt the response before it " "can be signed.") else: response = {} result_data = key.encrypt(_json.dumps(result).encode("utf-8")) if sign_result: # sign using the signing certificate for this service signature = _get_signing_certificate( fingerprint=sign_result, private_cert=private_cert).sign(result_data) response["signature"] = _bytes_to_string(signature) response["data"] = _bytes_to_string(result_data) response["encrypted"] = True response["fingerprint"] = key.fingerprint() response["synctime"] = now result = response result = _json.dumps(result).encode("utf-8") return result