Exemplo n.º 1
0
    def test_errors(self):
        encrypt(None, get_random_bytes(32))

        with self.assertRaises(Exception):
            decrypt("", None)

        invalid = base64.b64encode(get_random_bytes(45))
        with self.assertRaises(Exception):
            decrypt(invalid, None)
Exemplo n.º 2
0
def encrypt(contents):
    k = crypto.generate_random_key(32)
    v = crypto.generate_random_key(32)
    u = crypto.strbitxor(k, v)
    ciphertext = crypto.encrypt(contents, k)

    try:
        recovered = crypto.decrypt(ciphertext, k).decode('utf-8')
    except UnicodeDecodeError:
        recovered = crypto.decrypt(ciphertext, k)

    if recovered != contents:
        raise Exception("Test decryption failed")
    return {'u': u, 'v': v, 'k': k, 'ciphertext': ciphertext}
Exemplo n.º 3
0
def encrypt(contents):
    k = crypto.generate_random_key(32)
    v = crypto.generate_random_key(32)
    u = crypto.strbitxor(k, v)
    ciphertext = crypto.encrypt(contents, k)

    try:
        recovered = crypto.decrypt(ciphertext, k).decode("utf-8")
    except UnicodeDecodeError:
        recovered = crypto.decrypt(ciphertext, k)

    if recovered != contents:
        raise Exception("Test decryption failed")
    return {"u": u, "v": v, "k": k, "ciphertext": ciphertext}
Exemplo n.º 4
0
def read_private(warn=False):
    global global_password
    if global_password is None:
        setpassword(
            getpass.getpass(
                "Please enter the password to decrypt your keystore: "))

    if os.path.exists('private.yml'):
        with open('private.yml', 'r') as f:
            toread = yaml.load(f, Loader=SafeLoader)
        key = crypto.kdf(global_password, toread['salt'])
        try:
            plain = crypto.decrypt(toread['priv'], key)
        except ValueError:
            raise Exception("Invalid password for keystore")

        return yaml.load(plain, Loader=SafeLoader), toread['salt']

    if warn:
        # file doesn't exist, just invent a salt
        logger.warning("Private certificate data %s does not exist yet." %
                       os.path.abspath("private.yml"))
        logger.warning(
            "Keylime will attempt to load private certificate data again when it is needed."
        )
    return {
        'revoked_keys': []
    }, base64.b64encode(crypto.generate_random_key()).decode()
Exemplo n.º 5
0
def read_private():
    global global_password
    if global_password is None:
        setpassword(getpass.getpass("Please enter the password to decrypt your keystore: "))

    if os.path.exists('private.yml'):
        with open('private.yml','r') as f:
            toread = yaml.load(f, Loader=SafeLoader)
        key = crypto.kdf(global_password,toread['salt'])
        try:
            plain = crypto.decrypt(toread['priv'],key)
        except ValueError:
            raise Exception("Invalid password for keystore")

        return yaml.load(plain, Loader=SafeLoader),toread['salt']
    else:
        #file doesn't exist, just invent a salt
        return {'revoked_keys':[]},base64.b64encode(crypto.generate_random_key()).decode()
    def do_POST(self):
        """This method services the POST request typically from either the Tenant or the Cloud Verifier.

        Only tenant and cloudverifier uri's are supported. Both requests require a nonce parameter.
        The Cloud verifier requires an additional mask parameter.  If the uri or parameters are incorrect, a 400 response is returned.
        """
        rest_params = common.get_restful_params(self.path)

        if rest_params is None:
            common.echo_json_response(self, 405,
                                      "Not Implemented: Use /keys/ interface")
            return

        content_length = int(self.headers.get('Content-Length', 0))
        if content_length <= 0:
            logger.warning(
                'POST returning 400 response, expected content in message. url:  '
                + self.path)
            common.echo_json_response(self, 400, "expected content in message")
            return

        post_body = self.rfile.read(content_length)
        json_body = json.loads(post_body)

        b64_encrypted_key = json_body['encrypted_key']
        decrypted_key = crypto.rsa_decrypt(self.server.rsaprivatekey,
                                           base64.b64decode(b64_encrypted_key))

        have_derived_key = False

        if rest_params["keys"] == "ukey":
            self.server.add_U(decrypted_key)
            self.server.auth_tag = json_body['auth_tag']
            self.server.payload = json_body.get('payload', None)
            have_derived_key = self.server.attempt_decryption(self)
        elif rest_params["keys"] == "vkey":
            self.server.add_V(decrypted_key)
            have_derived_key = self.server.attempt_decryption(self)
        else:
            logger.warning('POST returning  response. uri not supported: ' +
                           self.path)
            common.echo_json_response(self, 400, "uri not supported")
            return
        logger.info('POST of %s key returning 200' %
                    (('V', 'U')[rest_params["keys"] == "ukey"]))
        common.echo_json_response(self, 200, "Success")

        # no key yet, then we're done
        if not have_derived_key:
            return

        # woo hoo we have a key
        # ok lets write out the key now
        secdir = secure_mount.mount(
        )  # confirm that storage is still securely mounted

        # clean out the secure dir of any previous info before we extract files
        if os.path.isdir("%s/unzipped" % secdir):
            shutil.rmtree("%s/unzipped" % secdir)

        # write out key file
        f = open(secdir + "/" + self.server.enc_keyname, 'w')
        f.write(base64.b64encode(self.server.K).decode())
        f.close()

        #stow the U value for later
        tpm.write_key_nvram(self.server.final_U)

        # optionally extend a hash of they key and payload into specified PCR
        tomeasure = self.server.K

        # if we have a good key, now attempt to write out the encrypted payload
        dec_path = "%s/%s" % (secdir,
                              config.get('cloud_agent', "dec_payload_file"))
        enc_path = "%s/encrypted_payload" % common.WORK_DIR

        dec_payload = None
        enc_payload = None
        if self.server.payload is not None:
            dec_payload = crypto.decrypt(self.server.payload,
                                         bytes(self.server.K))

            enc_payload = self.server.payload
        elif os.path.exists(enc_path):
            # if no payload provided, try to decrypt one from a previous run stored in encrypted_payload
            with open(enc_path, 'rb') as f:
                enc_payload = f.read()
            try:
                dec_payload = crypto.decrypt(enc_payload, self.server.K)
                logger.info("Decrypted previous payload in %s to %s" %
                            (enc_path, dec_path))
            except Exception as e:
                logger.warning(
                    "Unable to decrypt previous payload %s with derived key: %s"
                    % (enc_path, e))
                os.remove(enc_path)
                enc_payload = None

        # also write out encrypted payload to be decrytped next time
        if enc_payload is not None:
            with open(enc_path, 'wb') as f:
                f.write(self.server.payload.encode('utf-8'))

        # deal with payload
        payload_thread = None
        if dec_payload is not None:
            tomeasure = tomeasure + dec_payload
            # see if payload is a zip
            zfio = io.BytesIO(dec_payload)
            if config.getboolean(
                    'cloud_agent',
                    'extract_payload_zip') and zipfile.is_zipfile(zfio):
                logger.info("Decrypting and unzipping payload to %s/unzipped" %
                            secdir)
                with zipfile.ZipFile(zfio, 'r') as f:
                    f.extractall('%s/unzipped' % secdir)

                # run an included script if one has been provided
                initscript = config.get('cloud_agent', 'payload_script')
                if initscript is not "":

                    def initthread():
                        import subprocess
                        env = os.environ.copy()
                        env['AGENT_UUID'] = self.server.agent_uuid
                        proc = subprocess.Popen(["/bin/bash", initscript],
                                                env=env,
                                                shell=False,
                                                cwd='%s/unzipped' % secdir,
                                                stdout=subprocess.PIPE,
                                                stderr=subprocess.STDOUT)
                        while True:
                            line = proc.stdout.readline()
                            if line == '' and proc.poll() is not None:
                                break
                            if line:
                                logger.debug("init-output: %s" % line.strip())
                        # should be a no-op as poll already told us it's done
                        proc.wait()

                    if not os.path.exists("%s/unzipped/%s" %
                                          (secdir, initscript)):
                        logger.info(
                            "No payload script %s found in %s/unzipped" %
                            (initscript, secdir))
                    else:
                        logger.info(
                            "Executing payload script: %s/unzipped/%s" %
                            (secdir, initscript))
                        payload_thread = threading.Thread(target=initthread)
            else:
                logger.info("Decrypting payload to %s" % dec_path)
                with open(dec_path, 'wb') as f:
                    f.write(dec_payload)
            zfio.close()

        # now extend a measurement of the payload and key if there was one
        pcr = config.getint('cloud_agent', 'measure_payload_pcr')
        if pcr > 0 and pcr < 24:
            logger.info("extending measurement of payload into PCR %s" % pcr)
            measured = tpm.hashdigest(tomeasure)
            tpm.extendPCR(pcr, measured)

        if payload_thread is not None:
            payload_thread.start()

        return
Exemplo n.º 7
0
    def do_POST(self):
        """This method services the POST request typically from either the Tenant or the Cloud Verifier.

        Only tenant and cloudverifier uri's are supported. Both requests require a nonce parameter.
        The Cloud verifier requires an additional mask parameter.  If the uri or parameters are incorrect, a 400 response is returned.
        """
        rest_params = web_util.get_restful_params(self.path)

        if rest_params is None:
            web_util.echo_json_response(
                self, 405,
                "Not Implemented: Use /keys/ or /notifications/ interface")
            return

        if not rest_params["api_version"]:
            web_util.echo_json_response(self, 400, "API Version not supported")
            return

        content_length = int(self.headers.get("Content-Length", 0))
        if content_length <= 0:
            logger.warning(
                "POST returning 400 response, expected content in message. url: %s",
                self.path)
            web_util.echo_json_response(self, 400,
                                        "expected content in message")
            return

        post_body = self.rfile.read(content_length)
        try:
            json_body = json.loads(post_body)
        except Exception as e:
            logger.warning(
                "POST returning 400 response, could not parse body data: %s",
                e)
            web_util.echo_json_response(self, 400, "content is invalid")
            return

        if "notifications" in rest_params:
            if rest_params["notifications"] == "revocation":
                revocation_notifier.process_revocation(
                    json_body,
                    perform_actions,
                    cert_path=self.server.revocation_cert_path)
                web_util.echo_json_response(self, 200, "Success")
            else:
                web_util.echo_json_response(
                    self, 400, "Only /notifications/revocation is supported")
            return

        if rest_params.get("keys", None) not in ["ukey", "vkey"]:
            web_util.echo_json_response(
                self, 400, "Only /keys/ukey or /keys/vkey are supported")
            return

        try:
            b64_encrypted_key = json_body["encrypted_key"]
            decrypted_key = crypto.rsa_decrypt(
                self.server.rsaprivatekey, base64.b64decode(b64_encrypted_key))
        except (ValueError, KeyError, TypeError) as e:
            logger.warning(
                "POST returning 400 response, could not parse body data: %s",
                e)
            web_util.echo_json_response(self, 400, "content is invalid")
            return

        have_derived_key = False

        if rest_params["keys"] == "ukey":
            if "auth_tag" not in json_body:
                logger.warning(
                    "POST returning 400 response, U key provided without an auth_tag"
                )
                web_util.echo_json_response(self, 400, "auth_tag is missing")
                return
            self.server.add_U(decrypted_key)
            self.server.auth_tag = json_body["auth_tag"]
            self.server.payload = json_body.get("payload", None)
            have_derived_key = self.server.attempt_decryption()
        elif rest_params["keys"] == "vkey":
            self.server.add_V(decrypted_key)
            have_derived_key = self.server.attempt_decryption()
        else:
            logger.warning("POST returning  response. uri not supported: %s",
                           self.path)
            web_util.echo_json_response(self, 400, "uri not supported")
            return
        logger.info("POST of %s key returning 200",
                    ("V", "U")[rest_params["keys"] == "ukey"])
        web_util.echo_json_response(self, 200, "Success")

        # no key yet, then we're done
        if not have_derived_key:
            return

        # woo hoo we have a key
        # ok lets write out the key now
        secdir = secure_mount.mount(
        )  # confirm that storage is still securely mounted

        # clean out the secure dir of any previous info before we extract files
        if os.path.isdir(os.path.join(secdir, "unzipped")):
            shutil.rmtree(os.path.join(secdir, "unzipped"))

        # write out key file
        with open(os.path.join(secdir, self.server.enc_keyname),
                  "w",
                  encoding="utf-8") as f:
            f.write(base64.b64encode(self.server.K).decode())

        # stow the U value for later
        tpm_instance.write_key_nvram(self.server.final_U)

        # optionally extend a hash of they key and payload into specified PCR
        tomeasure = self.server.K

        # if we have a good key, now attempt to write out the encrypted payload
        dec_path = os.path.join(secdir,
                                config.get("cloud_agent", "dec_payload_file"))
        enc_path = os.path.join(config.WORK_DIR, "encrypted_payload")

        dec_payload = None
        enc_payload = None
        if self.server.payload is not None:
            if not self.server.mtls_cert_enabled and not config.getboolean(
                    "cloud_agent", "enable_insecure_payload", fallback=False):
                logger.warning(
                    'agent mTLS is disabled, and unless "enable_insecure_payload" is set to "True", payloads cannot be deployed'
                )
                enc_payload = None
            else:
                dec_payload = crypto.decrypt(self.server.payload,
                                             bytes(self.server.K))
                enc_payload = self.server.payload

        elif os.path.exists(enc_path):
            # if no payload provided, try to decrypt one from a previous run stored in encrypted_payload
            with open(enc_path, "rb") as f:
                enc_payload = f.read()
            try:
                dec_payload = crypto.decrypt(enc_payload, self.server.K)
                logger.info("Decrypted previous payload in %s to %s", enc_path,
                            dec_path)
            except Exception as e:
                logger.warning(
                    "Unable to decrypt previous payload %s with derived key: %s",
                    enc_path, e)
                os.remove(enc_path)
                enc_payload = None

        # also write out encrypted payload to be decrytped next time
        if enc_payload is not None:
            with open(enc_path, "wb") as f:
                f.write(self.server.payload.encode("utf-8"))

        # deal with payload
        payload_thread = None
        if dec_payload is not None:
            tomeasure = tomeasure + dec_payload
            # see if payload is a zip
            zfio = io.BytesIO(dec_payload)
            if config.getboolean(
                    "cloud_agent",
                    "extract_payload_zip") and zipfile.is_zipfile(zfio):
                logger.info("Decrypting and unzipping payload to %s/unzipped",
                            secdir)
                with zipfile.ZipFile(zfio, "r") as f:
                    f.extractall(os.path.join(secdir, "unzipped"))

                # run an included script if one has been provided
                initscript = config.get("cloud_agent", "payload_script")
                if initscript != "":

                    def initthread():
                        env = os.environ.copy()
                        env["AGENT_UUID"] = self.server.agent_uuid
                        with subprocess.Popen(
                            ["/bin/bash", initscript],
                                env=env,
                                shell=False,
                                cwd=os.path.join(secdir, "unzipped"),
                                stdout=subprocess.PIPE,
                                stderr=subprocess.STDOUT,
                        ) as proc:
                            for line in iter(proc.stdout.readline, b""):
                                logger.debug("init-output: %s", line.strip())
                            # should be a no-op as poll already told us it's done
                            proc.wait()

                    if not os.path.exists(
                            os.path.join(secdir, "unzipped", initscript)):
                        logger.info(
                            "No payload script %s found in %s/unzipped",
                            initscript, secdir)
                    else:
                        logger.info("Executing payload script: %s/unzipped/%s",
                                    secdir, initscript)
                        payload_thread = threading.Thread(target=initthread,
                                                          daemon=True)
            else:
                logger.info("Decrypting payload to %s", dec_path)
                with open(dec_path, "wb") as f:
                    f.write(dec_payload)
            zfio.close()

        # now extend a measurement of the payload and key if there was one
        pcr = config.getint("cloud_agent", "measure_payload_pcr")
        if 0 < pcr < 24:
            logger.info("extending measurement of payload into PCR %s", pcr)
            measured = tpm_instance.hashdigest(tomeasure)
            tpm_instance.extendPCR(pcr, measured)

        if payload_thread is not None:
            payload_thread.start()

        return
Exemplo n.º 8
0
 def test_aes(self):
     message = b"a secret message!"
     aeskey = get_random_bytes(32)
     ciphertext = encrypt(message, aeskey)
     plaintext = decrypt(ciphertext, aeskey)
     self.assertEqual(plaintext, message)