def test_get_tpm2b_public_object_attributes(self): test_pub = base64.b64decode( "ARgAAQALAAUAcgAAABAAFAALCAAAAAAAAQDJBIF+SxeEt8TAwcnMZIvJWs3luBARcI" "HXC7I/XH7ZXbwLyispm/tpvhRw0w60JbwF4om1LbApQbG9cWR7AOi3ykv5bOgszsIG" "DOYJNfWuylW2uQBvMPEeF+ysrCjFTl5HOhXEpaz+E//juoKS2Jh9zYr2kt8rnGAJyj" "a10LUsYNt4h6eyeLVrsZIckkKP4tZwPOokfdX+6YCtGy5Y1buTvBSGNWa+VGo6hZVD" "649mg6EHyv0geSHXojx0Iqjsl/NQXzOCvyuaf6CBu9pkiIZCePlrl2uD1tXEdX0ipB" "B9Fppc/5cJQ2NyJOuvi4MUK5y38QpwnZwd4Utr2WdyEPoF") expected_attributes = (OA_RESTRICTED | OA_USERWITHAUTH | OA_SIGN_ENCRYPT | OA_FIXEDTPM | OA_FIXEDPARENT | OA_SENSITIVEDATAORIGIN) new_attributes = get_tpm2b_public_object_attributes(test_pub) self.assertEqual(new_attributes, expected_attributes)
def do_POST(self): """This method handles the POST requests to add agents to the Registrar Server. Currently, only agents resources are available for POSTing, i.e. /agents. All other POST uri's will return errors. POST requests require an an agent_id identifying the agent to add, and json block sent in the body with 2 entries: ek and aik. """ session = SessionManager().make_session(engine) rest_params = config.get_restful_params(self.path) if rest_params is None: config.echo_json_response( self, 405, "Not Implemented: Use /agents/ interface") return if "agents" not in rest_params: config.echo_json_response(self, 400, "uri not supported") logger.warning( 'POST agent returning 400 response. uri not supported: %s', self.path) return agent_id = rest_params["agents"] if agent_id is None: config.echo_json_response(self, 400, "agent id not found in uri") logger.warning( 'POST agent returning 400 response. agent id not found in uri %s', self.path) return try: content_length = int(self.headers.get('Content-Length', 0)) if content_length == 0: config.echo_json_response(self, 400, "Expected non zero content length") logger.warning( 'POST for %s returning 400 response. Expected non zero content length.', agent_id) return post_body = self.rfile.read(content_length) json_body = json.loads(post_body) ekcert = json_body['ekcert'] aik_tpm = json_body['aik_tpm'] initialize_tpm = tpm() if ekcert is None or ekcert == 'emulator': logger.warning('Agent %s did not submit an ekcert' % agent_id) ek_tpm = json_body['ek_tpm'] else: if 'ek_tpm' in json_body: # This would mean the agent submitted both a non-None ekcert, *and* # an ek_tpm... We can deal with it by just ignoring the ek_tpm they sent logger.warning( 'Overriding ek_tpm for agent %s from ekcert' % agent_id) # If there's an EKCert, we just overwrite their ek_tpm # Note, we don't validate the EKCert here, other than the implicit # "is it a valid x509 cert" check. So it's still untrusted. # This will be validated by the tenant. ek509 = load_der_x509_certificate( base64.b64decode(ekcert), backend=default_backend(), ) ek_tpm = base64.b64encode( tpm2_objects.ek_low_tpm2b_public_from_pubkey( ek509.public_key(), )) aik_attrs = tpm2_objects.get_tpm2b_public_object_attributes( base64.b64decode(aik_tpm), ) if aik_attrs != tpm2_objects.AK_EXPECTED_ATTRS: config.echo_json_response(self, 400, "Invalid AK attributes") logger.warning( "Agent %s submitted AIK with invalid attributes! %s (provided) != %s (expected)", agent_id, tpm2_objects.object_attributes_description(aik_attrs), tpm2_objects.object_attributes_description( tpm2_objects.AK_EXPECTED_ATTRS), ) return # try to encrypt the AIK (blob, key) = initialize_tpm.encryptAIK( agent_id, base64.b64decode(ek_tpm), base64.b64decode(aik_tpm), ) # special behavior if we've registered this uuid before regcount = 1 try: agent = session.query(RegistrarMain).filter_by( agent_id=agent_id).first() except NoResultFound: agent = None except SQLAlchemyError as e: logger.error('SQLAlchemy Error: %s', e) raise if agent is not None: # keep track of how many ek-ekcerts have registered on this uuid regcount = agent.regcount if agent.ek_tpm != ek_tpm or agent.ekcert != ekcert: logger.warning( 'WARNING: Overwriting previous registration for this UUID with new ek-ekcert pair!' ) regcount += 1 # force overwrite logger.info('Overwriting previous registration for this UUID.') try: session.query(RegistrarMain).filter_by( agent_id=agent_id).delete() session.commit() except SQLAlchemyError as e: logger.error('SQLAlchemy Error: %s', e) raise # Add values to database d = {} d['agent_id'] = agent_id d['ek_tpm'] = ek_tpm d['aik_tpm'] = aik_tpm d['ekcert'] = ekcert d['virtual'] = int(ekcert == 'virtual') d['active'] = int(False) d['key'] = key d['provider_keys'] = {} d['regcount'] = regcount try: session.add(RegistrarMain(**d)) session.commit() except SQLAlchemyError as e: logger.error('SQLAlchemy Error: %s', e) raise response = { 'blob': blob, } config.echo_json_response(self, 200, "Success", response) logger.info('POST returning key blob for agent_id: %s', agent_id) except Exception as e: config.echo_json_response(self, 400, "Error: %s" % e) logger.warning("POST for %s returning 400 response. Error: %s", agent_id, e) logger.exception(e)
def do_POST(self): """This method handles the POST requests to add agents to the Registrar Server. Currently, only agents resources are available for POSTing, i.e. /agents. All other POST uri's will return errors. POST requests require an an agent_id identifying the agent to add, and json block sent in the body with 2 entries: ek and aik. """ session = SessionManager().make_session(engine) rest_params = web_util.get_restful_params(self.path) if rest_params is None: web_util.echo_json_response( self, 405, "Not Implemented: Use /agents/ interface") return if not web_util.validate_api_version(self, rest_params["api_version"], logger): return if "agents" not in rest_params: web_util.echo_json_response(self, 400, "uri not supported") logger.warning( "POST agent returning 400 response. uri not supported: %s", self.path) return agent_id = rest_params["agents"] if agent_id is None: web_util.echo_json_response(self, 400, "agent id not found in uri") logger.warning( "POST agent returning 400 response. agent id not found in uri %s", self.path) return # If the agent ID is not valid (wrong set of characters), just # do nothing. if not validators.valid_agent_id(agent_id): web_util.echo_json_response(self, 400, "agent id not valid") logger.error("POST received an invalid agent ID: %s", agent_id) return try: content_length = int(self.headers.get("Content-Length", 0)) if content_length == 0: web_util.echo_json_response( self, 400, "Expected non zero content length") logger.warning( "POST for %s returning 400 response. Expected non zero content length.", agent_id) return post_body = self.rfile.read(content_length) json_body = json.loads(post_body) ekcert = json_body["ekcert"] aik_tpm = json_body["aik_tpm"] initialize_tpm = tpm() if ekcert is None or ekcert == "emulator": logger.warning("Agent %s did not submit an ekcert", agent_id) ek_tpm = json_body["ek_tpm"] else: if "ek_tpm" in json_body: # This would mean the agent submitted both a non-None ekcert, *and* # an ek_tpm... We can deal with it by just ignoring the ek_tpm they sent logger.warning( "Overriding ek_tpm for agent %s from ekcert", agent_id) # If there's an EKCert, we just overwrite their ek_tpm # Note, we don't validate the EKCert here, other than the implicit # "is it a valid x509 cert" check. So it's still untrusted. # This will be validated by the tenant. ek509 = load_der_x509_certificate( base64.b64decode(ekcert), backend=default_backend(), ) ek_tpm = base64.b64encode( tpm2_objects.ek_low_tpm2b_public_from_pubkey( ek509.public_key(), )).decode() aik_attrs = tpm2_objects.get_tpm2b_public_object_attributes( base64.b64decode(aik_tpm), ) if aik_attrs != tpm2_objects.AK_EXPECTED_ATTRS: web_util.echo_json_response(self, 400, "Invalid AK attributes") logger.warning( "Agent %s submitted AIK with invalid attributes! %s (provided) != %s (expected)", agent_id, tpm2_objects.object_attributes_description(aik_attrs), tpm2_objects.object_attributes_description( tpm2_objects.AK_EXPECTED_ATTRS), ) return # try to encrypt the AIK (blob, key) = initialize_tpm.encryptAIK( agent_id, base64.b64decode(ek_tpm), base64.b64decode(aik_tpm), ) # special behavior if we've registered this uuid before regcount = 1 try: agent = session.query(RegistrarMain).filter_by( agent_id=agent_id).first() except NoResultFound: agent = None except SQLAlchemyError as e: logger.error("SQLAlchemy Error: %s", e) raise if agent is not None: # keep track of how many ek-ekcerts have registered on this uuid regcount = agent.regcount if agent.ek_tpm != ek_tpm or agent.ekcert != ekcert: logger.warning( "WARNING: Overwriting previous registration for this UUID with new ek-ekcert pair!" ) regcount += 1 # force overwrite logger.info("Overwriting previous registration for this UUID.") try: session.query(RegistrarMain).filter_by( agent_id=agent_id).delete() session.commit() except SQLAlchemyError as e: logger.error("SQLAlchemy Error: %s", e) raise # Check for ip and port contact_ip = json_body.get("ip", None) contact_port = json_body.get("port", None) # Validate ip and port if contact_ip is not None: try: # Use parser from the standard library instead of implementing our own ipaddress.ip_address(contact_ip) except ValueError: logger.warning( "Contact ip for agent %s is not a valid ip got: %s.", agent_id, contact_ip) contact_ip = None if contact_port is not None: try: contact_port = int(contact_port) if contact_port < 1 or contact_port > 65535: logger.warning( "Contact port for agent %s is not a number between 1 and got: %s.", agent_id, contact_port) contact_port = None except ValueError: logger.warning( "Contact port for agent %s is not a valid number got: %s.", agent_id, contact_port) contact_port = None # Check for mTLS cert mtls_cert = json_body.get("mtls_cert", None) if mtls_cert is None: logger.warning( "Agent %s did not send a mTLS certificate. Most operations will not work!", agent_id) # Add values to database d = {} d["agent_id"] = agent_id d["ek_tpm"] = ek_tpm d["aik_tpm"] = aik_tpm d["ekcert"] = ekcert d["ip"] = contact_ip d["mtls_cert"] = mtls_cert d["port"] = contact_port d["virtual"] = int(ekcert == "virtual") d["active"] = int(False) d["key"] = key d["provider_keys"] = {} d["regcount"] = regcount try: session.add(RegistrarMain(**d)) session.commit() except SQLAlchemyError as e: logger.error("SQLAlchemy Error: %s", e) raise response = { "blob": blob, } web_util.echo_json_response(self, 200, "Success", response) logger.info("POST returning key blob for agent_id: %s", agent_id) except Exception as e: web_util.echo_json_response(self, 400, f"Error: {str(e)}") logger.warning("POST for %s returning 400 response. Error: %s", agent_id, e) logger.exception(e)