def test_011_reg_instance_activate_put(self):
        """Test registrar's PUT /v2/instances/{UUID}/activate Interface"""
        global keyblob, aik

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

        key = tpm_initialize.activate_identity(keyblob)
        data = {
            'auth_tag':
            crypto.do_hmac(base64.b64decode(key), tenant_templ.node_uuid),
        }
        v_json_message = json.dumps(data)

        response = tornado_requests.request(
            "PUT",
            "http://%s:%s/v2/instances/%s/activate" %
            (tenant_templ.registrar_ip, tenant_templ.registrar_boot_port,
             tenant_templ.node_uuid),
            data=v_json_message,
            context=None)
        self.assertEqual(
            response.status_code, 200,
            "Non-successful Registrar Instance Activate return code!")
        response_body = response.json()

        # Ensure response is well-formed
        self.assertIn("results", response_body, "Malformed response body!")
示例#2
0
    def decrypt_check(self, decrypted_U, decrypted_V):    
        """Decrypt the Cloud init script with the passed U and V values.
        
        This method will access the received auth tag, and may fail if decoy U and V values were received.
        Do not call directly unless you acquire uvLock. Returns None if decryption unsuccessful, else returns the 
        decrypted agent UUID.
        """
        if self.auth_tag is None:
            return None
        
        if len(decrypted_U) != len(decrypted_V):
            logger.warning("Invalid U len %d or V len %d. skipping..."%(len(decrypted_U),len(decrypted_V)))
            return None
        
        candidate_key = str(crypto.strbitxor(decrypted_U, decrypted_V))
        
        # be very careful printing K, U, or V as they leak in logs stored on unprotected disks
        if common.INSECURE_DEBUG:
            logger.debug("U: " + base64.b64encode(decrypted_U))
            logger.debug("V: " + base64.b64encode(decrypted_V))
            logger.debug("K: " + base64.b64encode(candidate_key))
            
        logger.debug( "auth_tag: " + self.auth_tag)
        ex_mac = crypto.do_hmac(candidate_key,self.agent_uuid)
        
        if ex_mac == self.auth_tag:
            logger.info( "Successfully derived K for UUID %s",self.agent_uuid)
            self.final_U = decrypted_U
            self.K = candidate_key
            return True

        return False
    def test_041_node_keys_verify_get(self):
        """Test node's GET /v2/keys/verify Interface"""
        self.assertIsNotNone(
            self.K, "Required value not set.  Previous step may have failed?")

        challenge = tpm_initialize.random_password(20)

        response = tornado_requests.request(
            "GET", "http://%s:%s/v2/keys/verify/challenge/%s/" %
            (tenant_templ.cloudnode_ip, tenant_templ.cloudnode_port,
             challenge))
        self.assertEqual(response.status_code, 200,
                         "Non-successful Node 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,
                         "Node failed to validate challenge code!")
示例#4
0
 def preloop(self):
     # encrypt the node UUID as a check for delivering the correct key
     self.auth_tag = crypto.do_hmac(self.K, self.node_uuid)
     # be very careful printing K, U, or V as they leak in logs stored on unprotected disks
     if common.DEVELOP_IN_ECLIPSE:
         logger.debug("K:" + base64.b64encode(self.K))
         logger.debug("V:" + base64.b64encode(self.V))
         logger.debug("U:" + base64.b64encode(str(self.U)))
         logger.debug("Auth Tag: " + self.auth_tag)
示例#5
0
    def do_verify(self):
        """initiaite v, instance_id and ip
        initiate the cloudinit sequence"""
        challenge = tpm_initialize.random_password(20)

        numtries = 0
        while True:
            try:
                response = tornado_requests.request(
                    "GET", "http://%s:%s/v2/keys/verify/challenge/%s/" %
                    (self.cloudnode_ip, self.cloudnode_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:
                        logger.error(
                            "Quitting after max number of retries to connect to %s"
                            % (self.cloudnode_ip))
                        raise e
                    retry = config.getfloat('tenant', 'retry_interval')
                    logger.info(
                        "Connection to %s refused %d/%d times, trying again in %f seconds..."
                        % (self.cloudnode_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 Node: %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:
                    logger.error("Key derivation failed")
            else:
                common.log_http_response(logger, logging.ERROR, response_body)
                retry = config.getfloat('tenant', 'retry_interval')
                logger.error(
                    "Key derivation not yet complete...trying again in %s seconds...Ctrl-C to stop"
                    % retry)
                time.sleep(retry)
                continue
            break
 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 a node"""
     cls.auth_tag = crypto.do_hmac(cls.K, tenant_templ.node_uuid)
     """Prepare policies for node"""
     cls.tpm_policy = config.get('tenant', 'tpm_policy')
     cls.vtpm_policy = config.get('tenant', 'vtpm_policy')
     cls.tpm_policy = tpm_quote.readPolicy(cls.tpm_policy)
     cls.vtpm_policy = tpm_quote.readPolicy(cls.vtpm_policy)
示例#7
0
def doActivateNode(registrar_ip,registrar_port,instance_id,key):
    global context
    data = {
    'command': 'activate_node',
    'auth_tag': crypto.do_hmac(base64.b64decode(key),instance_id),
    }
            
    v_json_message = json.dumps(data)
    
    response = tornado_requests.request("PUT",
                                        "http://%s:%s/v1/instance_id/%s"%(registrar_ip,registrar_port,instance_id),
                                        data=v_json_message,
                                        context=context)

    if response.status_code == 200:
        logger.info("Registration activated for node %s."%instance_id)
    else:
        logger.error("Error: unexpected http response code from Registrar Server: " + str(response.status_code))
def doActivateNode(registrar_ip,registrar_port,instance_id,key):
    data = {
    'auth_tag': crypto.do_hmac(base64.b64decode(key),instance_id),
    }
            
    v_json_message = json.dumps(data)
    
    response = tornado_requests.request("PUT",
                                        "http://%s:%s/v2/instances/%s/activate"%(registrar_ip,registrar_port,instance_id),
                                        data=v_json_message,
                                        context=None)

    if response.status_code == 200:
        logger.info("Registration activated for node %s."%instance_id)
        return True
    else:
        logger.error("Error: unexpected http response code from Registrar Server: " + str(response.status_code))
        common.log_http_response(logger,logging.ERROR,response.json())
        return False
示例#9
0
    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
示例#10
0
 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
示例#11
0
    def do_PUT(self):
        """This method handles the PUT requests to add agents to the Registrar Server.
        
        Currently, only agents resources are available for PUTing, i.e. /agents. All other PUT uri's
        will return errors.
        """
        rest_params = common.get_restful_params(self.path)
        if rest_params is None:
            common.echo_json_response(
                self, 405, "Not Implemented: Use /agents/ interface")
            return

        if "agents" not in rest_params:
            common.echo_json_response(self, 400, "uri not supported")
            logger.warning(
                'PUT agent returning 400 response. uri not supported: ' +
                self.path)
            return

        agent_id = rest_params["agents"]

        if agent_id is None:
            common.echo_json_response(self, 400, "agent id not found in uri")
            logger.warning(
                'PUT agent returning 400 response. agent id not found in uri '
                + self.path)
            return

        try:
            content_length = int(self.headers.get('Content-Length', 0))
            if content_length == 0:
                common.echo_json_response(self, 400,
                                          "Expected non zero content length")
                logger.warning(
                    'PUT for ' + agent_id +
                    ' returning 400 response. Expected non zero content length.'
                )
                return

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

            if "activate" in rest_params:
                auth_tag = json_body['auth_tag']

                agent = self.server.db.get_agent(agent_id)
                if agent is None:
                    raise Exception(
                        "attempting to activate agent before requesting registrar for %s"
                        % agent_id)

                if agent['virtual']:
                    raise Exception(
                        "attempting to activate virtual AIK using physical interface for %s"
                        % agent_id)

                if common.STUB_TPM:
                    self.server.db.update_agent(agent_id, 'active', True)
                else:
                    ex_mac = crypto.do_hmac(base64.b64decode(agent['key']),
                                            agent_id)
                    if ex_mac == auth_tag:
                        self.server.db.update_agent(agent_id, 'active', True)
                    else:
                        raise Exception(
                            "Auth tag %s does not match expected value %s" %
                            (auth_tag, ex_mac))

                common.echo_json_response(self, 200, "Success")
                logger.info('PUT activated: ' + agent_id)
            elif "vactivate" in rest_params:
                deepquote = json_body.get('deepquote', None)

                agent = self.server.db.get_agent(agent_id)
                if agent is None:
                    raise Exception(
                        "attempting to activate agent before requesting registrar for %s"
                        % agent_id)

                if not agent['virtual']:
                    raise Exception(
                        "attempting to activate physical AIK using virtual interface for %s"
                        % agent_id)

                # get an physical AIK for this host
                registrar_client.init_client_tls(config, 'registrar')
                provider_keys = registrar_client.getKeys(
                    config.get('general', 'provider_registrar_ip'),
                    config.get('general', 'provider_registrar_tls_port'),
                    agent_id)
                # we already have the vaik
                tpm = tpm_obj.getTPM(need_hw_tpm=False,
                                     tpm_version=agent['tpm_version'])
                if not tpm.check_deep_quote(
                        hashlib.sha1(agent['key']).hexdigest(),
                        agent_id + agent['aik'] + agent['ek'], deepquote,
                        agent['aik'], provider_keys['aik']):
                    raise Exception("Deep quote invalid")

                self.server.db.update_agent(agent_id, 'active', True)
                self.server.db.update_agent(agent_id, 'provider_keys',
                                            provider_keys)

                common.echo_json_response(self, 200, "Success")
                logger.info('PUT activated: ' + agent_id)
            else:
                pass
        except Exception as e:
            common.echo_json_response(self, 400, "Error: %s" % e)
            logger.warning("PUT for " + agent_id +
                           " returning 400 response. Error: %s" % e)
            logger.exception(e)
            return
示例#12
0
    def do_PUT(self):
        """This method handles the POST requests to add instances to the Registrar Server. 
        
        Currently, only instances resources are available for POSTing, i.e. /v1/instances. All other POST uri's 
        will return errors. PUT requests require an an instance_id identifying the instance to add, and json 
        block sent in the body with 2 entries: ek and aik.  
        """
        if not self.is_instance_resource():
            self.send_response(400)
            self.end_headers()
            logger.warning('PUT instance returning 400 response. uri not supported: ' + self.path)
            return
        
        instance_id = self.get_resource_name()
        
        if instance_id is None:
            self.send_response(400)
            self.end_headers()
            logger.warning('PUT instance returning 400 response. instance_id not found in uri ' + self.path)
            return                   

        try:
            content_length = int(self.headers.get('Content-Length', 0))
            if content_length == 0:
                self.send_response(400)
                self.end_headers()
                logger.warning('PUT for ' + instance_id + ' returning 400 response. Expected non zero content length.')
                return 
        
            post_body = self.rfile.read(content_length)
            json_body = json.loads(post_body)
            
            command = json_body['command']
            
            if command=='register_node':
                ek = json_body['ek']
                ekcert = json_body['ekcert']
                aik = json_body['aik']
                
                # config option must be on to check for EK certs
                if config.getboolean('registrar','require_ek_cert'):
                    # no EK provided
                    if ekcert is None and not common.DEVELOP_IN_ECLIPSE:
                        raise Exception("No EK cert provided, require_ek_cert option in config set to True")
                    
                    # there is an EK
                    if not common.STUB_TPM and (ekcert!=None and ekcert!='virtual' and not tpm_initialize.verify_ek(base64.b64decode(ekcert), ek)):
                            raise Exception("Invalid EK certificate")
                
                # try to encrypt the AIK
                (blob,key) = tpm_initialize.encryptAIK(instance_id,aik,ek)
                self.server.add_instance(instance_id, key, aik,ek,ekcert)
                response = {
                        'blob': blob,
                }
                json_response = json.dumps(response)
                self.send_response(200)
                self.end_headers()
                self.wfile.write(json_response)
                
                logger.info('PUT returning key blob for instance_id: ' + instance_id)
                return
            elif command=='activate_node':
                auth_tag=json_body['auth_tag']
                
                instance = self.server.find_instance(instance_id)
                if instance is None:
                    raise Exception("attempting to activate instance before requesting registrar for %s"%instance_id)
         
                if instance['virtual']:
                    raise Exception("attempting to activate virtual AIK using physical interface for %s"%instance_id)
                
                if common.STUB_TPM:
                    self.server.update_instance(instance_id, 'active',True)
                else:
                    ex_mac = crypto.do_hmac(base64.b64decode(instance['key']),instance_id)
                    if ex_mac == auth_tag:
                        self.server.update_instance(instance_id, 'active',True)
                    else:
                        raise Exception("Auth tag %s does not match expected value %s"%(auth_tag,ex_mac))
                
                self.send_response(200)
                self.end_headers()
                logger.info('PUT activated: ' + instance_id)      
            elif command=='activate_virtual_node':
                deepquote = json_body.get('deepquote',None)

                instance = self.server.find_instance(instance_id)
                if instance is None:
                    raise Exception("attempting to activate instance before requesting registrar for %s"%instance_id)
                      
                if not instance['virtual']:
                    raise Exception("attempting to activate physical AIK using virtual interface for %s"%instance_id)
                
                # get an physical AIK for this host
                registrar_client.serverAuthTLSContext(config, 'registrar')
                dq_aik = registrar_client.getAIK(config.get('general', 'provider_registrar_ip'), config.get('general', 'provider_registrar_port'), instance_id)
                # we already have the vaik
                if not tpm_quote.check_deep_quote(hashlib.sha1(instance['key']),
                                                  instance_id+instance['aik']+instance['ek'], 
                                                  deepquote,  
                                                  instance['aik'],  
                                                  dq_aik):
                    raise Exception("Deep quote invalid")
                
                self.server.update_instance(instance_id, 'active',True)
                
                self.send_response(200)
                self.end_headers()
                logger.info('PUT activated: ' + instance_id)           
            else:
                pass           
        except Exception as e:
            self.send_response(400)
            self.end_headers()
            logger.warning("PUT for " + instance_id + " returning 400 response. Error: %s"%e)
            logger.warning(traceback.format_exc())
            return