コード例 #1
0
ファイル: test_restful.py プロジェクト: ozoder/keylime
    def test_022_agent_quotes_identity_get(self):
        """Test agent's GET /v2/quotes/identity Interface"""
        global aik

        self.assertIsNotNone(aik, "Required value not set.  Previous step may have failed?")

        nonce = TPM_Utilities.random_password(20)

        numretries = config.getint('tenant','max_retries')
        while numretries >= 0:
            response = tornado_requests.request(
                                                "GET",
                                                "http://%s:%s/v%s/quotes/identity?nonce=%s"%(tenant_templ.cloudagent_ip,tenant_templ.cloudagent_port,self.api_version,nonce)
                                            )
            if response.status_code == 200:
                break
            numretries-=1
            time.sleep(config.getint('tenant','max_retries'))
        self.assertEqual(response.status_code, 200, "Non-successful Agent identity return code!")
        response_body = response.json()

        # Ensure response is well-formed
        self.assertIn("results", response_body, "Malformed response body!")
        self.assertIn("quote", response_body["results"], "Malformed response body!")
        self.assertIn("pubkey", response_body["results"], "Malformed response body!")

        # Check the quote identity
        self.assertTrue(tpm.check_quote(nonce,response_body["results"]["pubkey"],response_body["results"]["quote"],aik), "Invalid quote!")
コード例 #2
0
ファイル: test_restful.py プロジェクト: ozoder/keylime
    def setUpClass(cls):
        """Prepare the keys and payload to give to the CV"""
        contents = "random garbage to test as payload"
        ret = user_data_encrypt.encrypt(contents)
        cls.K = ret['k']
        cls.U = ret['u']
        cls.V = ret['v']
        cls.payload = ret['ciphertext']

        """Set up to register an agent"""
        cls.auth_tag = crypto.do_hmac(cls.K,tenant_templ.agent_uuid)

        """Prepare policies for agent"""
        cls.tpm_policy = config.get('tenant', 'tpm_policy')
        cls.vtpm_policy = config.get('tenant', 'vtpm_policy')
        cls.tpm_policy = TPM_Utilities.readPolicy(cls.tpm_policy)
        cls.vtpm_policy = TPM_Utilities.readPolicy(cls.vtpm_policy)

        """Allow targeting a specific API version (default latest)"""
        cls.api_version = common.API_VERSION
コード例 #3
0
ファイル: tenant.py プロジェクト: ozoder/keylime
    def do_verify(self):
        """initiaite v, agent_id and ip
        initiate the cloudinit sequence"""
        challenge = TPM_Utilities.random_password(20)

        numtries = 0
        while True:
            try:
                response = tornado_requests.request(
                    "GET", "http://%s:%s/keys/verify?challenge=%s" %
                    (self.cloudagent_ip, self.cloudagent_port, challenge))
            except Exception as e:
                # this is one exception that should return a 'keep going' response
                if tornado_requests.is_refused(e):
                    numtries += 1
                    maxr = config.getint('tenant', 'max_retries')
                    if numtries >= maxr:
                        raise UserError(
                            "Quitting after max number of retries to connect to %s"
                            % (self.cloudagent_ip))
                    retry = config.getfloat('tenant', 'retry_interval')
                    logger.info(
                        "Connection to %s refused %d/%d times, trying again in %f seconds..."
                        % (self.cloudagent_ip, numtries, maxr, retry))
                    time.sleep(retry)
                    continue
                else:
                    raise e

            response_body = response.json()
            if response.status_code == 200:
                if "results" not in response_body or 'hmac' not in response_body[
                        'results']:
                    logger.critical(
                        "Error: unexpected http response body from Cloud Agent: %s"
                        % str(response.status_code))
                    break
                mac = response_body['results']['hmac']
                ex_mac = crypto.do_hmac(self.K, challenge)
                if mac == ex_mac:
                    logger.info("Key derivation successful")
                else:
                    raise UserError("Key derivation failed")
            else:
                common.log_http_response(logger, logging.ERROR, response_body)
                retry = config.getfloat('tenant', 'retry_interval')
                raise UserError(
                    "Key derivation not yet complete...trying again in %s seconds...Ctrl-C to stop"
                    % retry)
                time.sleep(retry)
                continue
            break
コード例 #4
0
def prepare_get_quote(agent):
    """This method encapsulates the action required to invoke a quote request on the Cloud Agent.
    
    This method is part of the polling loop of the thread launched on Tenant POST. 
    """
    agent['nonce'] = TPM_Utilities.random_password(20)
    
    params = {
        'nonce': agent['nonce'],
        'mask': agent['tpm_policy']['mask'],
        'vmask': agent['vtpm_policy']['mask'],
        }
    
    return params
コード例 #5
0
ファイル: test_restful.py プロジェクト: ozoder/keylime
    def test_041_agent_keys_verify_get(self):
        """Test agent's GET /v2/keys/verify Interface"""
        self.assertIsNotNone(self.K, "Required value not set.  Previous step may have failed?")

        challenge = TPM_Utilities.random_password(20)

        response = tornado_requests.request(
                                            "GET",
                                            "http://%s:%s/v%s/keys/verify?challenge=%s"%(tenant_templ.cloudagent_ip,tenant_templ.cloudagent_port,self.api_version,challenge)
                                        )
        self.assertEqual(response.status_code, 200, "Non-successful Agent verify return code!")
        response_body = response.json()

        # Ensure response is well-formed
        self.assertIn("results", response_body, "Malformed response body!")
        self.assertIn("hmac", response_body["results"], "Malformed response body!")

        # Be sure response is valid
        mac = response_body['results']['hmac']
        ex_mac = crypto.do_hmac(self.K, challenge)
        self.assertEqual(mac, ex_mac, "Agent failed to validate challenge code!")
コード例 #6
0
ファイル: test_restful.py プロジェクト: ozoder/keylime
    def test_040_agent_quotes_integrity_get(self):
        """Test agent's GET /v2/quotes/integrity Interface"""
        global public_key, aik

        self.assertIsNotNone(aik, "Required value not set.  Previous step may have failed?")

        nonce = TPM_Utilities.random_password(20)
        mask = self.tpm_policy["mask"]
        vmask = self.vtpm_policy["mask"]
        partial = "1"
        if public_key is None:
            partial = "0"

        response = tornado_requests.request(
                                            "GET",
                                            "http://%s:%s/v%s/quotes/integrity?nonce=%s&mask=%s&vmask=%s&partial=%s"%(tenant_templ.cloudagent_ip,tenant_templ.cloudagent_port,self.api_version,nonce,mask,vmask,partial)
                                        )
        self.assertEqual(response.status_code, 200, "Non-successful Agent Integrity Get return code!")
        response_body = response.json()

        # Ensure response is well-formed
        self.assertIn("results", response_body, "Malformed response body!")
        self.assertIn("quote", response_body["results"], "Malformed response body!")
        if public_key is None:
            self.assertIn("pubkey", response_body["results"], "Malformed response body!")
            public_key = response_body["results"]["pubkey"]
        self.assertIn("tpm_version", response_body["results"], "Malformed response body!")
        self.assertIn("hash_alg", response_body["results"], "Malformed response body!")

        quote = response_body["results"]["quote"]
        tpm_version = response_body["results"]["tpm_version"]
        hash_alg = response_body["results"]["hash_alg"]

        validQuote = tpm.check_quote(nonce,
                                            public_key,
                                            quote,
                                            aik,
                                            self.tpm_policy,
                                            hash_alg=hash_alg)
        self.assertTrue(validQuote)
コード例 #7
0
ファイル: cloud_agent.py プロジェクト: mrcdb/keylime
 def do_GET(self):
     """This method services the GET 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 paramter.  If the uri or parameters are incorrect, a 400 response is returned.
     """
     
     logger.info('GET invoked from ' + str(self.client_address)  + ' with uri:' + self.path)
     
     rest_params = common.get_restful_params(self.path)
     if rest_params is None:
         common.echo_json_response(self, 405, "Not Implemented: Use /keys/ or /quotes/ interfaces")
         return
     
     if "keys" in rest_params and rest_params['keys']=='verify':
         if self.server.K is None:
             logger.info('GET key challenge returning 400 response. bootstrap key not available')
             common.echo_json_response(self, 400, "Bootstrap key not yet available.")
             return
         challenge = rest_params['challenge']
         response={}
         response['hmac'] = crypto.do_hmac(self.server.K, challenge)            
         common.echo_json_response(self, 200, "Success", response)
         logger.info('GET key challenge returning 200 response.')
         
     # If agent pubkey requested
     elif "keys" in rest_params and rest_params["keys"] == "pubkey":
         response = {}
         response['pubkey'] = self.server.rsapublickey_exportable
         
         common.echo_json_response(self, 200, "Success", response)
         logger.info('GET pubkey returning 200 response.')
         return
     
     elif "quotes" in rest_params:
         nonce = rest_params['nonce']
         pcrmask = rest_params['mask'] if 'mask' in rest_params else None
         vpcrmask = rest_params['vmask'] if 'vmask' in rest_params else None
         
         # if the query is not messed up
         if nonce is None:
             logger.warning('GET quote returning 400 response. nonce not provided as an HTTP parameter in request')
             common.echo_json_response(self, 400, "nonce not provided as an HTTP parameter in request")
             return
         
         # Sanitization assurance (for tpm.run() tasks below) 
         if not (nonce.isalnum() and (pcrmask is None or pcrmask.isalnum()) and (vpcrmask is None or vpcrmask.isalnum())):
             logger.warning('GET quote returning 400 response. parameters should be strictly alphanumeric')
             common.echo_json_response(self, 400, "parameters should be strictly alphanumeric")
             return
         
         # identity quotes are always shallow
         hash_alg = tpm.defaults['hash']
         if not tpm.is_vtpm() or rest_params["quotes"]=='identity':
             quote = tpm.create_quote(nonce, self.server.rsapublickey_exportable, pcrmask, hash_alg)
             imaMask = pcrmask
         else:
             quote = tpm.create_deep_quote(nonce, self.server.rsapublickey_exportable, vpcrmask, pcrmask)
             imaMask = vpcrmask
         
         # Allow for a partial quote response (without pubkey) 
         enc_alg = tpm.defaults['encrypt']
         sign_alg = tpm.defaults['sign']
         if "partial" in rest_params and (rest_params["partial"] is None or int(rest_params["partial"],0) == 1):
             response = { 
                 'quote': quote, 
                 'tpm_version': tpm_version,
                 'hash_alg': hash_alg,
                 'enc_alg': enc_alg,
                 'sign_alg': sign_alg,
                 }
         else:
             response = {
                 'quote': quote, 
                 'tpm_version': tpm_version,
                 'hash_alg': hash_alg,
                 'enc_alg': enc_alg,
                 'sign_alg': sign_alg,
                 'pubkey': self.server.rsapublickey_exportable, 
             }
         
         # return a measurement list if available
         if TPM_Utilities.check_mask(imaMask, common.IMA_PCR):
             if not os.path.exists(common.IMA_ML):
                 logger.warn("IMA measurement list not available: %s"%(common.IMA_ML))
             else:
                 with open(common.IMA_ML,'r') as f:
                     ml = f.read()
                 response['ima_measurement_list']=ml
         
         common.echo_json_response(self, 200, "Success", response)
         logger.info('GET %s quote returning 200 response.'%(rest_params["quotes"]))
         return
     
     else:
         logger.warning('GET returning 400 response. uri not supported: ' + self.path)
         common.echo_json_response(self, 400, "uri not supported")
         return
コード例 #8
0
ファイル: tenant.py プロジェクト: ozoder/keylime
    def do_quote(self):
        """initiaite v, agent_id and ip
        initiate the cloudinit sequence"""
        self.nonce = TPM_Utilities.random_password(20)

        numtries = 0
        response = None
        while True:
            # Get quote
            try:
                response = tornado_requests.request(
                    "GET", "http://%s:%s/quotes/identity?nonce=%s" %
                    (self.cloudagent_ip, self.cloudagent_port, self.nonce))
            except Exception as e:
                # this is one exception that should return a 'keep going' response
                if tornado_requests.is_refused(e):
                    numtries += 1
                    maxr = config.getint('tenant', 'max_retries')
                    if numtries >= maxr:
                        raise UserError(
                            "Quitting after max number of retries to connect to %s"
                            % (self.cloudagent_ip))
                    retry = config.getfloat('tenant', 'retry_interval')
                    logger.info(
                        "Connection to %s refused %d/%d times, trying again in %f seconds..."
                        % (self.cloudagent_ip, numtries, maxr, retry))
                    time.sleep(retry)
                    continue
                else:
                    raise e
            break

        try:
            if response is not None and response.status_code != 200:
                raise UserError(
                    "Status command response: %d Unexpected response from Cloud Agent."
                    % response.status_code)

            response_body = response.json()

            if "results" not in response_body:
                raise UserError(
                    "Error: unexpected http response body from Cloud Agent: %s"
                    % str(response.status_code))

            quote = response_body["results"]["quote"]
            logger.debug("agent_quote received quote:" + quote)

            public_key = response_body["results"]["pubkey"]
            logger.debug("agent_quote received public key:" + public_key)

            # Get tpm_version, hash_alg
            tpm_version = response_body["results"]["tpm_version"]
            logger.debug("agent_quote received tpm version:" +
                         str(tpm_version))

            # Ensure hash_alg is in accept_tpm_hash_algs list
            hash_alg = response_body["results"]["hash_alg"]
            logger.debug("agent_quote received hash algorithm:" + hash_alg)
            if not Hash_Algorithms.is_accepted(
                    hash_alg,
                    config.get('tenant', 'accept_tpm_hash_algs').split(',')):
                raise UserError(
                    "TPM Quote is using an unaccepted hash algorithm: %s" %
                    hash_alg)

            # Ensure enc_alg is in accept_tpm_encryption_algs list
            enc_alg = response_body["results"]["enc_alg"]
            logger.debug("agent_quote received encryption algorithm:" +
                         enc_alg)
            if not Encrypt_Algorithms.is_accepted(
                    enc_alg,
                    config.get('tenant',
                               'accept_tpm_encryption_algs').split(',')):
                raise UserError(
                    "TPM Quote is using an unaccepted encryption algorithm: %s"
                    % enc_alg)

            # Ensure sign_alg is in accept_tpm_encryption_algs list
            sign_alg = response_body["results"]["sign_alg"]
            logger.debug("agent_quote received signing algorithm:" + sign_alg)
            if not Sign_Algorithms.is_accepted(
                    sign_alg,
                    config.get('tenant',
                               'accept_tpm_signing_algs').split(',')):
                raise UserError(
                    "TPM Quote is using an unaccepted signing algorithm: %s" %
                    sign_alg)

            if not self.validate_tpm_quote(public_key, quote, tpm_version,
                                           hash_alg):
                raise UserError(
                    "TPM Quote from cloud agent is invalid for nonce: %s" %
                    self.nonce)

            logger.info("Quote from %s validated" % self.cloudagent_ip)

            # encrypt U with the public key
            encrypted_U = crypto.rsa_encrypt(
                crypto.rsa_import_pubkey(public_key), str(self.U))

            b64_encrypted_u = base64.b64encode(encrypted_U)
            logger.debug("b64_encrypted_u: " + b64_encrypted_u)
            data = {
                'encrypted_key': b64_encrypted_u,
                'auth_tag': self.auth_tag
            }

            if self.payload is not None:
                data['payload'] = self.payload

            u_json_message = json.dumps(data)

            #post encrypted U back to CloudAgent
            response = tornado_requests.request(
                "POST",
                "http://%s:%s/keys/ukey" %
                (self.cloudagent_ip, self.cloudagent_port),
                data=u_json_message)

            if response.status_code != 200:
                common.log_http_response(logger, logging.ERROR, response_body)
                raise UserError(
                    "Posting of Encrypted U to the Cloud Agent failed with response code %d"
                    % response.status_code)

        except Exception as e:
            self.do_cvstop()
            raise e
コード例 #9
0
ファイル: tenant.py プロジェクト: ozoder/keylime
    def init_add(self, args):
        # command line options can overwrite config values
        if "agent_ip" in args:
            self.cloudagent_ip = args["agent_ip"]

        if 'cv_agent_ip' in args and args['cv_agent_ip'] is not None:
            self.cv_cloudagent_ip = args['cv_agent_ip']
        else:
            self.cv_cloudagent_ip = self.cloudagent_ip

        # Make sure all keys exist in dictionary
        if "file" not in args:
            args["file"] = None
        if "keyfile" not in args:
            args["keyfile"] = None
        if "payload" not in args:
            args["payload"] = None
        if "ca_dir" not in args:
            args["ca_dir"] = None
        if "incl_dir" not in args:
            args["incl_dir"] = None
        if "ca_dir_pw" not in args:
            args["ca_dir_pw"] = None

        # Set up accepted algorithms
        self.accept_tpm_hash_algs = config.get(
            'tenant', 'accept_tpm_hash_algs').split(',')
        self.accept_tpm_encryption_algs = config.get(
            'tenant', 'accept_tpm_encryption_algs').split(',')
        self.accept_tpm_signing_algs = config.get(
            'tenant', 'accept_tpm_signing_algs').split(',')

        # Set up PCR values
        tpm_policy = config.get('tenant', 'tpm_policy')
        if "tpm_policy" in args and args["tpm_policy"] is not None:
            tpm_policy = args["tpm_policy"]
        self.tpm_policy = TPM_Utilities.readPolicy(tpm_policy)
        logger.info("TPM PCR Mask from policy is %s" % self.tpm_policy['mask'])

        vtpm_policy = config.get('tenant', 'vtpm_policy')
        if "vtpm_policy" in args and args["vtpm_policy"] is not None:
            vtpm_policy = args["vtpm_policy"]
        self.vtpm_policy = TPM_Utilities.readPolicy(vtpm_policy)
        logger.info("vTPM PCR Mask from policy is %s" %
                    self.vtpm_policy['mask'])

        # Read command-line path string IMA whitelist
        wl_data = None
        if "ima_whitelist" in args and args["ima_whitelist"] is not None:

            # Auto-enable IMA (or-bit mask)
            self.tpm_policy['mask'] = "0x%X" % (
                int(self.tpm_policy['mask'], 0) + (1 << common.IMA_PCR))

            if type(args["ima_whitelist"]) in [str, unicode]:
                if args["ima_whitelist"] == "default":
                    args["ima_whitelist"] = config.get('tenant',
                                                       'ima_whitelist')
                wl_data = ima.read_whitelist(args["ima_whitelist"])
            elif type(args["ima_whitelist"]) is list:
                wl_data = args["ima_whitelist"]
            else:
                raise UserError("Invalid whitelist provided")

        # Read command-line path string IMA exclude list
        excl_data = None
        if "ima_exclude" in args and args["ima_exclude"] is not None:
            if type(args["ima_exclude"]) in [str, unicode]:
                if args["ima_exclude"] == "default":
                    args["ima_exclude"] = config.get('tenant',
                                                     'ima_excludelist')
                excl_data = ima.read_excllist(args["ima_exclude"])
            elif type(args["ima_exclude"]) is list:
                excl_data = args["ima_exclude"]
            else:
                raise UserError("Invalid exclude list provided")

        # Set up IMA
        if TPM_Utilities.check_mask(self.tpm_policy['mask'],common.IMA_PCR) or \
            TPM_Utilities.check_mask(self.vtpm_policy['mask'],common.IMA_PCR):

            # Process IMA whitelists
            self.ima_whitelist = ima.process_whitelists(wl_data, excl_data)

        # if none
        if (args["file"] is None and args["keyfile"] is None
                and args["ca_dir"] is None):
            raise UserError(
                "You must specify one of -k, -f, or --cert to specify the key/contents to be securely delivered to the agent"
            )

        if args["keyfile"] is not None:
            if args["file"] is not None or args["ca_dir"] is not None:
                raise UserError(
                    "You must specify one of -k, -f, or --cert to specify the key/contents to be securely delivered to the agent"
                )

            # read the keys in
            if type(args["keyfile"]) is dict and "data" in args["keyfile"]:
                if type(args["keyfile"]["data"]) is list and len(
                        args["keyfile"]["data"]) == 1:
                    keyfile = args["keyfile"]["data"][0]
                    if keyfile is None:
                        raise UserError("Invalid key file contents")
                    f = StringIO.StringIO(keyfile)
                else:
                    raise UserError("Invalid key file provided")
            else:
                f = open(args["keyfile"], 'r')
            self.K = base64.b64decode(f.readline())
            self.U = base64.b64decode(f.readline())
            self.V = base64.b64decode(f.readline())
            f.close()

            # read the payload in (opt.)
            if type(args["payload"]) is dict and "data" in args["payload"]:
                if type(args["payload"]["data"]) is list and len(
                        args["payload"]["data"]) > 0:
                    self.payload = args["payload"]["data"][0]
            else:
                if args["payload"] is not None:
                    f = open(args["payload"], 'r')
                    self.payload = f.read()
                    f.close()

        if args["file"] is not None:
            if args["keyfile"] is not None or args["ca_dir"] is not None:
                raise UserError(
                    "You must specify one of -k, -f, or --cert to specify the key/contents to be securely delivered to the agent"
                )

            if type(args["file"]) is dict and "data" in args["file"]:
                if type(args["file"]["data"]) is list and len(
                        args["file"]["data"]) > 0:
                    contents = args["file"]["data"][0]
                    if contents is None:
                        raise UserError("Invalid file payload contents")
                else:
                    raise UserError("Invalid file payload provided")
            else:
                with open(args["file"], 'r') as f:
                    contents = f.read()
            ret = user_data_encrypt.encrypt(contents)
            self.K = ret['k']
            self.U = ret['u']
            self.V = ret['v']
            self.payload = ret['ciphertext']

        if args["ca_dir"] is None and args["incl_dir"] is not None:
            raise UserError(
                "--include option is only valid when used with --cert")
        if args["ca_dir"] is not None:
            if args["file"] is not None or args["keyfile"] is not None:
                raise UserError(
                    "You must specify one of -k, -f, or --cert to specify the key/contents to be securely delivered to the agent"
                )
            if args["ca_dir"] == 'default':
                args["ca_dir"] = common.CA_WORK_DIR

            if "ca_dir_pw" in args and args["ca_dir_pw"] is not None:
                ca_util.setpassword(args["ca_dir_pw"])

            if not os.path.exists(args["ca_dir"]):
                logger.warning(" CA directory does not exist.  Creating...")
                ca_util.cmd_init(args["ca_dir"])

            if not os.path.exists("%s/%s-private.pem" %
                                  (args["ca_dir"], self.agent_uuid)):
                ca_util.cmd_mkcert(args["ca_dir"], self.agent_uuid)

            cert_pkg, serial, subject = ca_util.cmd_certpkg(
                args["ca_dir"], self.agent_uuid)

            # support revocation
            if not os.path.exists(
                    "%s/RevocationNotifier-private.pem" % args["ca_dir"]):
                ca_util.cmd_mkcert(args["ca_dir"], "RevocationNotifier")
            rev_package, _, _ = ca_util.cmd_certpkg(args["ca_dir"],
                                                    "RevocationNotifier")

            # extract public and private keys from package
            sf = cStringIO.StringIO(rev_package)
            with zipfile.ZipFile(sf) as zf:
                privkey = zf.read("RevocationNotifier-private.pem")
                cert = zf.read("RevocationNotifier-cert.crt")

            # put the cert of the revoker into the cert package
            sf = StringIO.StringIO(cert_pkg)
            with zipfile.ZipFile(sf, 'a',
                                 compression=zipfile.ZIP_STORED) as zf:
                zf.writestr('RevocationNotifier-cert.crt', cert)

                # add additional files to zip
                if args["incl_dir"] is not None:
                    if type(args["incl_dir"]) is dict and "data" in args[
                            "incl_dir"] and "name" in args["incl_dir"]:
                        if type(args["incl_dir"]["data"]) is list and type(
                                args["incl_dir"]["name"]) is list:
                            if len(args["incl_dir"]["data"]) != len(
                                    args["incl_dir"]["name"]):
                                raise UserError("Invalid incl_dir provided")
                            for i in range(len(args["incl_dir"]["data"])):
                                zf.writestr(
                                    os.path.basename(
                                        args["incl_dir"]["name"][i]),
                                    args["incl_dir"]["data"][i])
                    else:
                        if os.path.exists(args["incl_dir"]):
                            files = next(os.walk(args["incl_dir"]))[2]
                            for filename in files:
                                with open(
                                        "%s/%s" % (args["incl_dir"], filename),
                                        'rb') as f:
                                    zf.writestr(os.path.basename(f.name),
                                                f.read())
                        else:
                            logger.warn(
                                "Specified include directory %s does not exist.  Skipping..."
                                % args["incl_dir"])

            cert_pkg = sf.getvalue()

            # put the private key into the data to be send to the CV
            self.revocation_key = privkey

            # encrypt up the cert package
            ret = user_data_encrypt.encrypt(cert_pkg)
            self.K = ret['k']
            self.U = ret['u']
            self.V = ret['v']
            self.metadata = {'cert_serial': serial, 'subject': subject}
            self.payload = ret['ciphertext']

        if self.payload is not None and len(self.payload) > config.getint(
                'tenant', 'max_payload_size'):
            raise UserError("Payload size %s exceeds max size %d" % (len(
                self.payload), config.getint('tenant', 'max_payload_size')))