Commit abc6cfd1 authored by Pelayo Torres's avatar Pelayo Torres
Browse files

PSK security method

parent 4a6b24cc
Loading
Loading
Loading
Loading
Loading
+107 −3
Original line number Original line Diff line number Diff line
@@ -5,9 +5,13 @@ from datetime import datetime, timedelta


import rfc3987
import rfc3987
from bson import json_util
from bson import json_util
from flask import current_app
from flask import current_app, request
from flask_jwt_extended import create_access_token
from flask_jwt_extended import create_access_token
from pymongo import ReturnDocument
from pymongo import ReturnDocument
import hmac 
import hashlib 
import unicodedata



from ..core.publisher import Publisher
from ..core.publisher import Publisher
from ..models.access_token_claims import AccessTokenClaims
from ..models.access_token_claims import AccessTokenClaims
@@ -82,6 +86,47 @@ class SecurityOperations(Resource):
            token_error = AccessTokenErr(error="invalid_scope", error_description="malformed scope")
            token_error = AccessTokenErr(error="invalid_scope", error_description="malformed scope")
            return make_response(object=clean_empty(token_error.to_dict()), status=400)
            return make_response(object=clean_empty(token_error.to_dict()), status=400)
    
    

    def __derive_psk(self, master_key:str, session_id:str, interface:dict): 
        ## Derive the PSK using the provided master key, session ID, and interface information

        # Interface information
        host = None
        if 'fqdn' in interface:
            host = interface['fqdn']
        elif 'ipv4Addr' in interface:
            host = interface['ipv4Addr']
        elif 'ipv6Addr' in interface:
            host = interface['ipv6Addr']
        port = interface.get('port', None)

        api_prefix = interface.get('apiPrefix', '')
        scheme = "https" if port in (None, 443) else "http"

        interface_info = f"{scheme}://{host}"
        if port and port != 443:
            interface_info += f":{port}"
        interface_info += api_prefix
        
        # Normalize the strings to NFKC form
        p0_string = unicodedata.normalize("NFKC", interface_info).encode("utf-8") 
        p1_string = unicodedata.normalize("NFKC", session_id).encode("utf-8") 
        
        # Convert to octet format (0xFF) 
        p0_octet_string = ' '.join(f'0x{byte:02X}' for byte in p0_string) 
        p1_octet_string = ' '.join(f'0x{byte:02X}' for byte in p1_string) 

        # Convert number of bytes to 16-bit big-endian 
        l0 = ' '.join(f'0x{byte:02X}' for byte in len(p0_octet_string).to_bytes(2, 'big')) 
        l1 = ' '.join(f'0x{byte:02X}' for byte in len(p1_octet_string).to_bytes(2, 'big')) 
        
        # Create S string using FC (0x7A) and the octet strings with their lengths 
        S = "0x7A" + ' ' + p0_octet_string + ' ' + l0 + ' ' + p1_octet_string + ' ' + l1 
        psk = hmac.new(master_key.encode("utf-8"), S.encode("utf-8"), hashlib.sha256).digest() 
        
        return psk


    def __init__(self):
    def __init__(self):
        Resource.__init__(self)
        Resource.__init__(self)
        self.filter_aef_id = "aef_profiles.aef_id"
        self.filter_aef_id = "aef_profiles.aef_id"
@@ -259,7 +304,7 @@ class SecurityOperations(Resource):
                        self.db.capif_service_col)
                        self.db.capif_service_col)
                    services_security_object = capif_service_col.find_one(
                    services_security_object = capif_service_col.find_one(
                        {"api_id": service_instance.api_id, self.filter_aef_id: service_instance.aef_id}, {"aef_profiles.security_methods.$": 1})
                        {"api_id": service_instance.api_id, self.filter_aef_id: service_instance.aef_id}, {"aef_profiles.security_methods.$": 1})

                    current_app.logger.debug("Aef profile: " + str(services_security_object))
                    if services_security_object is None:
                    if services_security_object is None:
                        current_app.logger.error(
                        current_app.logger.error(
                            "Not found service with this aef id: " + service_instance.aef_id)
                            "Not found service with this aef id: " + service_instance.aef_id)
@@ -291,6 +336,36 @@ class SecurityOperations(Resource):
                # Select the highest-priority security method
                # Select the highest-priority security method
                service_instance.sel_security_method = sorted_methods[0]
                service_instance.sel_security_method = sorted_methods[0]


                if service_instance.sel_security_method == "PSK":
                    request.headers.get('X-TLS-Protocol', 'N/A')
                    sesionId = request.headers.get('X-TLS-Session-ID', 'N/A')  
                    Mkey = request.headers.get('X-TLS-MKey', 'N/A') 
                    current_app.logger.info(f"TLS Protocol: {request.headers.get('X-TLS-Protocol', 'N/A')}, Session id: {sesionId}, Master Key: {Mkey}") 

                    interface = None
                    if service_instance.interface_details:
                        current_app.logger.debug("Interface details found")
                        interface = service_instance.interface_details.to_dict()
                    
                    else:
                        current_app.logger.error("Interface details not found")
                        services_security_object = capif_service_col.find_one(
                        {"api_id": service_instance.api_id}, {"aef_profiles": {"$elemMatch": {"aef_id": service_instance.aef_id}}, "_id": 0})
                        current_app.logger.debug("Aef profile: " + str(services_security_object["aef_profiles"][0]))
                        if "interface_descriptions" in services_security_object["aef_profiles"][0]:
                            current_app.logger.debug("Aef profile: " + str(services_security_object["aef_profiles"][0]["interface_descriptions"]))
                            interface = services_security_object["aef_profiles"][0]["interface_descriptions"][0]
                        elif "domain_name" in services_security_object["aef_profiles"][0]:
                            current_app.logger.debug("Aef profile: " + str(services_security_object["aef_profiles"][0]["domain_name"]))
                            interface = services_security_object["aef_profiles"][0]["domain_name"]
                    
                    if interface:
                        current_app.logger.debug("Deriving PSK")
                        psk = self.__derive_psk(Mkey, sesionId, interface)
                        current_app.logger.debug("PSK derived : " + str(psk))

                        service_instance.authorization_info = str(psk)

                # Send service instance to ACL
                # Send service instance to ACL
                current_app.logger.debug("Sending message to create ACL")
                current_app.logger.debug("Sending message to create ACL")
                publish_ops.publish_message("acls-messages", "create-acl:"+str(
                publish_ops.publish_message("acls-messages", "create-acl:"+str(
@@ -483,6 +558,35 @@ class SecurityOperations(Resource):
                        valid_security_method)[0]
                        valid_security_method)[0]
                update_acls.append({"api_id": service_instance.api_id, "aef_id": service_instance.aef_id})
                update_acls.append({"api_id": service_instance.api_id, "aef_id": service_instance.aef_id})


                if service_instance.sel_security_method == "PSK":
                    request.headers.get('X-TLS-Protocol', 'N/A')
                    sesionId = request.headers.get('X-TLS-Session-ID', 'N/A')  
                    Mkey = request.headers.get('X-TLS-MKey', 'N/A') 
                    current_app.logger.info(f"TLS Protocol: {request.headers.get('X-TLS-Protocol', 'N/A')}, Session id: {sesionId}, Master Key: {Mkey}") 

                    interface = None
                    if service_instance.interface_details:
                        current_app.logger.debug("Interface details found")
                        interface = service_instance.interface_details.to_dict()
                    
                    else:
                        current_app.logger.error("Interface details not found")
                        services_security_object = capif_service_col.find_one(
                        {"api_id": service_instance.api_id}, {"aef_profiles": {"$elemMatch": {"aef_id": service_instance.aef_id}}, "_id": 0})
                        current_app.logger.debug("Aef profile: " + str(services_security_object["aef_profiles"][0]))
                        if "interface_descriptions" in services_security_object["aef_profiles"][0]:
                            current_app.logger.debug("Aef profile: " + str(services_security_object["aef_profiles"][0]["interface_descriptions"]))
                            interface = services_security_object["aef_profiles"][0]["interface_descriptions"][0]
                        elif "domain_name" in services_security_object["aef_profiles"][0]:
                            current_app.logger.debug("Aef profile: " + str(services_security_object["aef_profiles"][0]["domain_name"]))
                            interface = services_security_object["aef_profiles"][0]["domain_name"]
                    
                    if interface:
                        current_app.logger.debug("Deriving PSK")
                        psk = self.__derive_psk(Mkey, sesionId, interface)
                        current_app.logger.debug("PSK derived : " + str(psk))

                        service_instance.authorization_info = str(psk)
            service_security = service_security.to_dict()
            service_security = service_security.to_dict()
            service_security = clean_empty(service_security)
            service_security = clean_empty(service_security)


+1 −1
Original line number Original line Diff line number Diff line
@@ -306,7 +306,7 @@ services:
    ports:
    ports:
      - "8080:8080"
      - "8080:8080"
      - "443:443"
      - "443:443"
    image: ${REGISTRY_BASE_URL}/nginx:${OCF_VERSION}
    image: pelayo222/mi-nginx:1.27.1-arm64
    environment:
    environment:
      - CAPIF_HOSTNAME=${CAPIF_HOSTNAME}
      - CAPIF_HOSTNAME=${CAPIF_HOSTNAME}
      - VAULT_HOSTNAME=vault
      - VAULT_HOSTNAME=vault
+1 −1
Original line number Original line Diff line number Diff line
FROM labs.etsi.org:5050/ocf/capif/nginx:1.27.1
FROM pelayo222/mi-nginx:1.27.1-arm64
RUN apt-get update && apt-get install -y jq && apt-get clean
RUN apt-get update && apt-get install -y jq && apt-get clean
RUN apt-get install -y openssl
RUN apt-get install -y openssl
RUN apt-get install -y curl
RUN apt-get install -y curl
+4 −0
Original line number Original line Diff line number Diff line
@@ -142,6 +142,10 @@ http {
                  return 401 $security_error_message;
                  return 401 $security_error_message;
                }
                }


                proxy_set_header X-TLS-Protocol $ssl_protocol;
                proxy_set_header X-TLS-Session-ID $ssl_session_id;
                proxy_set_header X-TLS-MKey $sslkeylog_mk;

                proxy_set_header X-SSL-Client-Cert $ssl_client_cert;
                proxy_set_header X-SSL-Client-Cert $ssl_client_cert;
                proxy_pass http://capif-security:8080;
                proxy_pass http://capif-security:8080;
              }
              }