Skip to main content
Loading...

More Python Posts

def parse_ike_proposal(proposal):
    """
    Parse an IKE or ESP proposal string to extract encryption, hash, and DH group in human-readable format.
    
    Args:
        proposal (str): IKE or ESP proposal string, e.g., 'IKE:AES_CBC_128/HMAC_SHA1_96/PRF_HMAC_SHA1/MODP_1024'
                        or 'IKE:AES_GCM_16_256/PRF_HMAC_SHA2_256/ECP_384' or 'ESP:AES_CBC_256/HMAC_SHA1_96/NO_EXT_SEQ'
    
    Returns:
        dict: Dictionary with encryption, hash, and DH group in human-readable format
    """
    dh_mapping = {
        # Standard MODP groups from RFC 2409 and RFC 3526
        'MODP_768': '1',   # 768-bit MODP group
        'MODP_1024': '2',  # 1024-bit MODP group
        'MODP_1536': '5',  # 1536-bit MODP group
        'MODP_2048': '14', # 2048-bit MODP group
        'MODP_3072': '15', # 3072-bit MODP group
        'MODP_4096': '16', # 4096-bit MODP group
        'MODP_6144': '17', # 6144-bit MODP group
        'MODP_8192': '18', # 8192-bit MODP group
        # Elliptic Curve groups from RFC 5114 and RFC 5903
        'ECP_256': '19',   # 256-bit ECP group
        'ECP_384': '20',   # 384-bit ECP group
        'ECP_521': '21',   # 521-bit ECP group
        'ECP_192': '25',   # 192-bit ECP group
        'ECP_224': '26',   # 224-bit ECP group
        # MODP groups with subgroup sizes from RFC 5114
        'MODP_1024_160': '22', # 1024-bit MODP with 160-bit subgroup
        'MODP_2048_224': '23', # 2048-bit MODP with 224-bit subgroup
        'MODP_2048_256': '24', # 2048-bit MODP with 256-bit subgroup
        # Additional groups from RFC 7919 (FFDHE - Finite Field Diffie-Hellman Ephemeral)
        'FFDHE_2048': '256', # 2048-bit FFDHE group
        'FFDHE_3072': '257', # 3072-bit FFDHE group
        'FFDHE_4096': '258', # 4096-bit FFDHE group
        'FFDHE_6144': '259', # 6144-bit FFDHE group
        'FFDHE_8192': '260', # 8192-bit FFDHE group
        # Brainpool curves from RFC 6954
        'BRAINPOOL_P256R1': '28', # 256-bit Brainpool curve
        'BRAINPOOL_P384R1': '29', # 384-bit Brainpool curve
        'BRAINPOOL_P512R1': '30', # 512-bit Brainpool curve
        # Modern elliptic curve groups from RFC 8031
        'CURVE25519': '31', # 256-bit elliptic curve (Curve25519, 128-bit security)
        'CURVE448': '32',   # 448-bit elliptic curve (Curve448, 224-bit security)
    }
    
    enc_mapping = {
        # AES in CBC mode (RFC 3602, commonly used in IPsec and TLS)
        'AES_CBC_128': 'AES-128',       # 128-bit key, CBC mode
        'AES_CBC_192': 'AES-192',       # 192-bit key, CBC mode
        'AES_CBC_256': 'AES-256',       # 256-bit key, CBC mode
        # AES in GCM mode (RFC 4106, authenticated encryption for IPsec/TLS)
        'AES_GCM_16_128': 'AES-GCM-128', # 128-bit key, GCM mode, 16-byte ICV
        'AES_GCM_16_192': 'AES-GCM-192', # 192-bit key, GCM mode, 16-byte ICV
        'AES_GCM_16_256': 'AES-GCM-256', # 256-bit key, GCM mode, 16-byte ICV
        'AES_GCM_8_128': 'AES-GCM-128-8', # 128-bit key, GCM mode, 8-byte ICV
        'AES_GCM_8_256': 'AES-GCM-256-8', # 256-bit key, GCM mode, 8-byte ICV
        'AES_GCM_12_128': 'AES-GCM-128-12', # 128-bit key, GCM mode, 12-byte ICV
        'AES_GCM_12_256': 'AES-GCM-256-12', # 256-bit key, GCM mode, 12-byte ICV
        # AES in CCM mode (RFC 4309, used in IPsec and some wireless protocols)
        'AES_CCM_16_128': 'AES-CCM-128', # 128-bit key, CCM mode, 16-byte ICV
        'AES_CCM_16_256': 'AES-CCM-256', # 256-bit key, CCM mode, 16-byte ICV
        # AES in CTR mode (RFC 3686, used in some VPNs and SSH)
        'AES_CTR_128': 'AES-CTR-128',   # 128-bit key, CTR mode
        'AES_CTR_192': 'AES-CTR-192',   # 192-bit key, CTR mode
        'AES_CTR_256': 'AES-CTR-256',   # 256-bit key, CTR mode
        # Legacy and alternative algorithms
        '3DES_CBC': '3DES',             # Triple DES, CBC mode (RFC 2451, deprecated)
        'DES_CBC': 'DES',               # Single DES, CBC mode (RFC 2405, obsolete)
        'CAMELLIA_CBC_128': 'CAMELLIA-128', # 128-bit Camellia, CBC mode (RFC 5529)
        'CAMELLIA_CBC_256': 'CAMELLIA-256', # 256-bit Camellia, CBC mode (RFC 5529)
        'CHACHA20_POLY1305': 'CHACHA20-POLY1305', # ChaCha20 with Poly1305 (RFC 8032, used in TLS 1.3, OpenVPN)
        'BLOWFISH_CBC': 'BLOWFISH',     # Blowfish, CBC mode (non-standard, used in some OpenSSH/OpenVPN)
        'CAST5_CBC': 'CAST5',           # CAST-128, CBC mode (non-standard, used in some OpenVPN)
        # Null encryption (for testing or integrity-only scenarios, RFC 2410)
        'NULL': 'NULL'                  # No encryption, only integrity protection
    }
    
    hash_mapping = {
        # Legacy hash algorithms (RFC 2403, RFC 2404, deprecated in modern systems)
        'HMAC_MD5': 'MD5',                 # MD5 HMAC, 128-bit output (insecure, legacy use in IPsec/SSH)
        'HMAC_MD5_96': 'MD5-96',           # MD5 HMAC, truncated to 96 bits (IPsec)
        'HMAC_SHA1': 'SHA1',               # SHA1 HMAC, 160-bit output (legacy, used in IPsec/TLS)
        'HMAC_SHA1_96': 'SHA1-96',         # SHA1 HMAC, truncated to 96 bits (IPsec)
        # SHA2-based HMAC algorithms (RFC 4868, used in IPsec, TLS, SSH)
        'HMAC_SHA2_256': 'SHA2-256',       # SHA2-256 HMAC, full 256-bit output
        'HMAC_SHA2_256_128': 'SHA2-256-128', # SHA2-256 HMAC, truncated to 128 bits
        'HMAC_SHA2_384': 'SHA2-384',       # SHA2-384 HMAC, full 384-bit output
        'HMAC_SHA2_384_192': 'SHA2-384-192', # SHA2-384 HMAC, truncated to 192 bits
        'HMAC_SHA2_512': 'SHA2-512',       # SHA2-512 HMAC, full 512-bit output
        'HMAC_SHA2_512_256': 'SHA2-512-256', # SHA2-512 HMAC, truncated to 256 bits
        # SHA3-based HMAC algorithms (RFC 8009, emerging in modern protocols)
        'HMAC_SHA3_224': 'SHA3-224',       # SHA3-224 HMAC, 224-bit output
        'HMAC_SHA3_256': 'SHA3-256',       # SHA3-256 HMAC, 256-bit output
        'HMAC_SHA3_384': 'SHA3-384',       # SHA3-384 HMAC, 384-bit output
        'HMAC_SHA3_512': 'SHA3-512',       # SHA3-512 HMAC, 512-bit output
        # Authenticated encryption integrity (used with AES-GCM/CCM, RFC 4106, RFC 4309)
        'AES_GMAC_128': 'GMAC-128',        # AES-GMAC with 128-bit key
        'AES_GMAC_192': 'GMAC-192',        # AES-GMAC with 192-bit key
        'AES_GMAC_256': 'GMAC-256',        # AES-GMAC with 256-bit key
        # Poly1305 (RFC 8032, used with ChaCha20 in TLS 1.3, OpenVPN)
        'POLY1305': 'POLY1305',            # Poly1305 authenticator, 128-bit output
        # Null authentication (RFC 2410, for testing or encryption-only scenarios)
        'NONE': 'NULL'                     # No integrity protection
    }
    
    # Split the proposal into components
    components = proposal.split('/')
    
    # Initialize result dictionary
    result = {
        'encryption': 'Unknown',
        'hash': 'None',  # Default to 'None' for AEAD ciphers like AES-GCM
        'dh_group': 'None'  # Default to 'None' for ESP or proposals without DH
    }
    
    # Extract components based on expected length
    if len(components) == 4:  # Standard IKE format: IKE:ENC/HASH/PRF/DH
        result['encryption'] = enc_mapping.get(components[0].replace('IKE:', ''), 'Unknown')
        result['hash'] = hash_mapping.get(components[1], 'Unknown')
        result['dh_group'] = dh_mapping.get(components[3], 'None')
    elif len(components) == 3:  # AEAD IKE format: IKE:ENC/PRF/DH or ESP:ENC/HASH/EXT
        result['encryption'] = enc_mapping.get(components[0].replace('IKE:', '').replace('ESP:', ''), 'Unknown')
        if components[0].startswith('IKE:') and components[1].startswith('PRF_'):  # AEAD IKE (e.g., AES-GCM)
            result['hash'] = 'None'
            result['dh_group'] = dh_mapping.get(components[2], 'None')
        else:  # ESP format (e.g., ESP:AES_CBC_256/HMAC_SHA1_96/NO_EXT_SEQ)
            result['hash'] = hash_mapping.get(components[1], 'Unknown')
            result['dh_group'] = 'None'  # ESP proposals typically lack DH groups
    
    return result
 
def process_proposals(proposal_list):
    """
    Process a list of IKE or ESP proposals, grouping by encryption and hash, and listing all DH groups.
    
    Args:
        proposal_list (str): Comma-separated string of IKE or ESP proposals
    
    Returns:
        dict: Dictionary mapping (encryption, hash) tuples to lists of DH groups
    """
    #print("PROPSOSAL LIST:", proposal_list)
    proposals = proposal_list.split(', ')
    grouped_proposals = {}
    
    for proposal in proposals:
        parsed = parse_ike_proposal(proposal.strip())
        key = (parsed['encryption'], parsed['hash'])
        dh_group = parsed['dh_group']
        
        if key not in grouped_proposals:
            grouped_proposals[key] = []
        if dh_group != 'None' and dh_group not in grouped_proposals[key]:
            grouped_proposals[key].append(dh_group)
    
    # Sort DH groups for consistency (numerically by group number)
    for key in grouped_proposals:
        grouped_proposals[key].sort(key=lambda x: int(x))
    
    # Format output as strings
    result = []
    for (enc, hash_val), dh_groups in grouped_proposals.items():
        hash_part = f" Hash {hash_val}" if hash_val != 'None' else ""
        dh_group_part = f" DH Group(s) {' '.join(dh_groups)}" if dh_groups else " DH Group(s) None"
        result.append(f"Encryption {enc}{hash_part}{dh_group_part}")
    
    return result
 
# Example usage
if __name__ == "__main__":
    proposals = """IKE:AES_CBC_128/HMAC_SHA1_96/PRF_HMAC_SHA1/MODP_1024, IKE:AES_CBC_128/HMAC_SHA1_96/PRF_HMAC_SHA1/MODP_2048, IKE:AES_CBC_128/HMAC_SHA1_96/PRF_HMAC_SHA1/MODP_3072, IKE:AES_CBC_128/HMAC_SHA1_96/PRF_HMAC_SHA1/MODP_4096, IKE:AES_CBC_128/HMAC_SHA1_96/PRF_HMAC_SHA1/MODP_6144, IKE:AES_CBC_128/HMAC_SHA1_96/PRF_HMAC_SHA1/MODP_8192, IKE:AES_CBC_128/HMAC_SHA1_96/PRF_HMAC_SHA1/ECP_256, IKE:AES_CBC_128/HMAC_SHA1_96/PRF_HMAC_SHA1/ECP_384, IKE:AES_CBC_128/HMAC_SHA1_96/PRF_HMAC_SHA1/ECP_521, IKE:AES_CBC_128/HMAC_SHA1_96/PRF_HMAC_SHA1/MODP_1024_160, IKE:AES_CBC_128/HMAC_SHA1_96/PRF_HMAC_SHA1/MODP_2048_224, IKE:AES_CBC_128/HMAC_SHA1_96/PRF_HMAC_SHA1/MODP_2048_256, IKE:AES_CBC_128/HMAC_SHA2_256_128/PRF_HMAC_SHA2_256/MODP_1024, IKE:AES_CBC_128/HMAC_SHA2_256_128/PRF_HMAC_SHA2_256/MODP_2048, IKE:AES_CBC_128/HMAC_SHA2_256_128/PRF_HMAC_SHA2_256/MODP_3072, IKE:AES_CBC_128/HMAC_SHA2_256_128/PRF_HMAC_SHA2_256/MODP_4096, IKE:AES_CBC_128/HMAC_SHA2_256_128/PRF_HMAC_SHA2_256/MODP_6144, IKE:AES_CBC_128/HMAC_SHA2_256_128/PRF_HMAC_SHA2_256/MODP_8192, IKE:AES_CBC_128/HMAC_SHA2_256_128/PRF_HMAC_SHA2_256/ECP_256, IKE:AES_CBC_128/HMAC_SHA2_256_128/PRF_HMAC_SHA2_256/ECP_384, IKE:AES_CBC_128/HMAC_SHA2_256_128/PRF_HMAC_SHA2_256/ECP_521, IKE:AES_CBC_128/HMAC_SHA2_256_128/PRF_HMAC_SHA2_256/MODP_1024_160, IKE:AES_CBC_128/HMAC_SHA2_256_128/PRF_HMAC_SHA2_256/MODP_2048_224, IKE:AES_CBC_128/HMAC_SHA2_256_128/PRF_HMAC_SHA2_256/MODP_2048_256, IKE:AES_CBC_128/HMAC_SHA2_384_192/PRF_HMAC_SHA2_384/MODP_1024, IKE:AES_CBC_128/HMAC_SHA2_384_192/PRF_HMAC_SHA2_384/MODP_2048, IKE:AES_CBC_128/HMAC_SHA2_384_192/PRF_HMAC_SHA2_384/MODP_3072, IKE:AES_CBC_128/HMAC_SHA2_384_192/PRF_HMAC_SHA2_384/MODP_4096, IKE:AES_CBC_128/HMAC_SHA2_384_192/PRF_HMAC_SHA2_384/MODP_6144, IKE:AES_CBC_128/HMAC_SHA2_384_192/PRF_HMAC_SHA2_384/MODP_8192, IKE:AES_CBC_128/HMAC_SHA2_384_192/PRF_HMAC_SHA2_384/ECP_256, IKE:AES_CBC_128/HMAC_SHA2_384_192/PRF_HMAC_SHA2_384/ECP_384, IKE:AES_CBC_128/HMAC_SHA2_384_192/PRF_HMAC_SHA2_384/ECP_521, IKE:AES_CBC_128/HMAC_SHA2_384_192/PRF_HMAC_SHA2_384/MODP_1024_160, IKE:AES_CBC_128/HMAC_SHA2_384_192/PRF_HMAC_SHA2_384/MODP_2048_224, IKE:AES_CBC_128/HMAC_SHA2_384_192/PRF_HMAC_SHA2_384/MODP_2048_256, IKE:AES_CBC_128/HMAC_SHA2_512_256/PRF_HMAC_SHA2_512/MODP_1024, IKE:AES_CBC_128/HMAC_SHA2_512_256/PRF_HMAC_SHA2_512/MODP_2048, IKE:AES_CBC_128/HMAC_SHA2_512_256/PRF_HMAC_SHA2_512/MODP_3072, IKE:AES_CBC_128/HMAC_SHA2_512_256/PRF_HMAC_SHA2_512/MODP_4096, IKE:AES_CBC_128/HMAC_SHA2_512_256/PRF_HMAC_SHA2_512/MODP_6144, IKE:AES_CBC_128/HMAC_SHA2_512_256/PRF_HMAC_SHA2_512/MODP_8192, IKE:AES_CBC_128/HMAC_SHA2_512_256/PRF_HMAC_SHA2_512/ECP_256, IKE:AES_CBC_128/HMAC_SHA2_512_256/PRF_HMAC_SHA2_512/ECP_384, IKE:AES_CBC_128/HMAC_SHA2_512_256/PRF_HMAC_SHA2_512/ECP_521, IKE:AES_CBC_128/HMAC_SHA2_512_256/PRF_HMAC_SHA2_512/MODP_1024_160, IKE:AES_CBC_128/HMAC_SHA2_512_256/PRF_HMAC_SHA2_512/MODP_2048_224, IKE:AES_CBC_128/HMAC_SHA2_512_256/PRF_HMAC_SHA2_512/MODP_2048_256, IKE:AES_CBC_256/HMAC_SHA1_96/PRF_HMAC_SHA1/MODP_1024, IKE:AES_CBC_256/HMAC_SHA1_96/PRF_HMAC_SHA1/MODP_2048, IKE:AES_CBC_256/HMAC_SHA1_96/PRF_HMAC_SHA1/MODP_3072, IKE:AES_CBC_256/HMAC_SHA1_96/PRF_HMAC_SHA1/MODP_4096, IKE:AES_CBC_256/HMAC_SHA1_96/PRF_HMAC_SHA1/MODP_6144, IKE:AES_CBC_256/HMAC_SHA1_96/PRF_HMAC_SHA1/MODP_8192, IKE:AES_CBC_256/HMAC_SHA1_96/PRF_HMAC_SHA1/ECP_256, IKE:AES_CBC_256/HMAC_SHA1_96/PRF_HMAC_SHA1/ECP_384, IKE:AES_CBC_256/HMAC_SHA1_96/PRF_HMAC_SHA1/ECP_521, IKE:AES_CBC_256/HMAC_SHA1_96/PRF_HMAC_SHA1/MODP_1024_160, IKE:AES_CBC_256/HMAC_SHA1_96/PRF_HMAC_SHA1/MODP_2048_224, IKE:AES_CBC_256/HMAC_SHA1_96/PRF_HMAC_SHA1/MODP_2048_256, IKE:AES_CBC_256/HMAC_SHA2_256_128/PRF_HMAC_SHA2_256/MODP_1024, IKE:AES_CBC_256/HMAC_SHA2_256_128/PRF_HMAC_SHA2_256/MODP_2048, IKE:AES_CBC_256/HMAC_SHA2_256_128/PRF_HMAC_SHA2_256/MODP_3072, IKE:AES_CBC_256/HMAC_SHA2_256_128/PRF_HMAC_SHA2_256/MODP_4096, IKE:AES_CBC_256/HMAC_SHA2_256_128/PRF_HMAC_SHA2_256/MODP_6144, IKE:AES_CBC_256/HMAC_SHA2_256_128/PRF_HMAC_SHA2_256/MODP_8192, IKE:AES_CBC_256/HMAC_SHA2_256_128/PRF_HMAC_SHA2_256/ECP_256, IKE:AES_CBC_256/HMAC_SHA2_256_128/PRF_HMAC_SHA2_256/ECP_384, IKE:AES_CBC_256/HMAC_SHA2_256_128/PRF_HMAC_SHA2_256/ECP_521, IKE:AES_CBC_256/HMAC_SHA2_256_128/PRF_HMAC_SHA2_256/MODP_1024_160, IKE:AES_CBC_256/HMAC_SHA2_256_128/PRF_HMAC_SHA2_256/MODP_2048_224, IKE:AES_CBC_256/HMAC_SHA2_256_128/PRF_HMAC_SHA2_256/MODP_2048_256, IKE:AES_CBC_256/HMAC_SHA2_384_192/PRF_HMAC_SHA2_384/MODP_1024, IKE:AES_CBC_256/HMAC_SHA2_384_192/PRF_HMAC_SHA2_384/MODP_2048, IKE:AES_CBC_256/HMAC_SHA2_384_192/PRF_HMAC_SHA2_384/MODP_3072, IKE:AES_CBC_256/HMAC_SHA2_384_192/PRF_HMAC_SHA2_384/MODP_4096, IKE:AES_CBC_256/HMAC_SHA2_384_192/PRF_HMAC_SHA2_384/MODP_6144, IKE:AES_CBC_256/HMAC_SHA2_384_192/PRF_HMAC_SHA2_384/MODP_8192, IKE:AES_CBC_256/HMAC_SHA2_384_192/PRF_HMAC_SHA2_384/ECP_256, IKE:AES_CBC_256/HMAC_SHA2_384_192/PRF_HMAC_SHA2_384/ECP_384, IKE:AES_CBC_256/HMAC_SHA2_384_192/PRF_HMAC_SHA2_384/ECP_521, IKE:AES_CBC_256/HMAC_SHA2_384_192/PRF_HMAC_SHA2_384/MODP_1024_160, IKE:AES_CBC_256/HMAC_SHA2_384_192/PRF_HMAC_SHA2_384/MODP_2048_224, IKE:AES_CBC_256/HMAC_SHA2_384_192/PRF_HMAC_SHA2_384/MODP_2048_256, IKE:AES_CBC_256/HMAC_SHA2_512_256/PRF_HMAC_SHA2_512/MODP_1024, IKE:AES_CBC_256/HMAC_SHA2_512_256/PRF_HMAC_SHA2_512/MODP_2048, IKE:AES_CBC_256/HMAC_SHA2_512_256/PRF_HMAC_SHA2_512/MODP_3072, IKE:AES_CBC_256/HMAC_SHA2_512_256/PRF_HMAC_SHA2_512/MODP_4096, IKE:AES_CBC_256/HMAC_SHA2_512_256/PRF_HMAC_SHA2_512/MODP_6144, IKE:AES_CBC_256/HMAC_SHA2_512_256/PRF_HMAC_SHA2_512/MODP_8192, IKE:AES_CBC_256/HMAC_SHA2_512_256/PRF_HMAC_SHA2_512/ECP_256, IKE:AES_CBC_256/HMAC_SHA2_512_256/PRF_HMAC_SHA2_512/ECP_384, IKE:AES_CBC_256/HMAC_SHA2_512_256/PRF_HMAC_SHA2_512/ECP_521, IKE:AES_CBC_256/HMAC_SHA2_512_256/PRF_HMAC_SHA2_512/MODP_1024_160, IKE:AES_CBC_256/HMAC_SHA2_512_256/PRF_HMAC_SHA2_512/MODP_2048_224, IKE:AES_CBC_256/HMAC_SHA2_512_256/PRF_HMAC_SHA2_512/MODP_2048_256"""
    
 
    proposals_result = '\n'.join(process_proposals(proposals))
    print(f"AWS tunnel is processing proposals to find a matching configuration. AWS tunnel is configured as follows:\n\n{proposals_result}")
class ProposalParser:
    """A class to parse IKE and ESP proposal strings into human-readable formats.

    This class supports parsing of IKE and ESP proposals, extracting encryption, hash, PRF (for IKE),
    and Diffie-Hellman (DH) group information. It also handles the concatenation of these components
    into a structured format, indicating whether Perfect Forward Secrecy (PFS) is enabled for ESP proposals.
    The parser uses predefined mappings for DH groups, encryption algorithms, hash functions, and Pseudo-Random Functions (PRFs).
    It can process a list of proposals and return a formatted string summarizing the cryptographic parameters.
    Attributes:

        dh_mapping (dict): A mapping of Diffie-Hellman groups to their corresponding identifiers

        enc_mapping (dict): A mapping of encryption algorithms to their corresponding identifiers

        hash_mapping (dict): A mapping of hash functions to their corresponding identifiers

        prf_mapping (dict): A mapping of Pseudo-Random Functions to their corresponding identifiers

    Methods:
        parse_ike_proposal(proposal): Parses a single IKE or ESP proposal string into a structured dictionary with encryption, hash, PRF, and DH group information.

        process_proposals(proposal_list): Processes a comma-separated list of IKE or ESP proposals, concatenating encryption, hash, PRF (for IKE), and DH group values, and indicating whether PFS is enabled for ESP proposals.
    
    Usage:
        _proposal_parser = ProposalParser()
        proposal = "IKE:AES_CBC_256/HMAC_SHA2_256/PRF_HMAC_SHA2_256/MODP_2048"
        parsed_proposal = _proposal_parser.parse_ike_proposal(proposal)
        print(parsed_proposal)
        # Output: {'encryption': ['AES256'], 'hash': ['SHA2-256'], 'prf': ['SHA2-256'], 'dh_group': ['14']}
        proposal_list = "IKE:AES_CBC_256/HMAC_SHA2_256/PRF_HMAC_SHA2_256/MODP_2048, ESP:AES_GCM_16_256/HMAC_SHA2_256/MODP_2048"
        formatted_proposal = _proposal_parser.process_proposals(proposal_list)
        print(formatted_proposal)ss
        # Output: "Encryption: AES256, AES128-GCM-16 Hash: SHA2-256, PRF: SHA2-256, DH Group(s): 14 PFS: Enabled"
    """
    
    def __init__(self):
        """Initialize the parser with mappings for DH groups, encryption, hash, and PRF."""
        self.dh_mapping = {
            'MODP_768': '1',
            'MODP_1024': '2',
            'MODP_1536': '5',
            'MODP_2048': '14',
            'MODP_3072': '15',
            'MODP_4096': '16',
            'MODP_6144': '17',
            'MODP_8192': '18',
            'ECP_256': '19',
            'ECP_384': '20',
            'ECP_521': '21',
            'ECP_192': '25',
            'ECP_224': '26',
            'MODP_1024_160': '22',
            'MODP_2048_224': '23',
            'MODP_2048_256': '24',
            'FFDHE_2048': '256',
            'FFDHE_3072': '257',
            'FFDHE_4096': '258',
            'FFDHE_6144': '259',
            'FFDHE_8192': '260',
            'ECP_224_BP': '27',
            'ECP_256_BP': '28',
            'ECP_384_BP': '29',
            'ECP_512_BP': '30',
            'CURVE_25519': '31',
            'CURVE_448': '32',
        }
        
        self.enc_mapping = {
            'AES_CBC_128': 'AES128',
            'AES_CBC_192': 'AES192',
            'AES_CBC_256': 'AES256',
            'AES_GCM_16_128': 'AES128-GCM-16',
            'AES_GCM_16_192': 'AES192-GCM-16',
            'AES_GCM_16_256': 'AES256-GCM-16',
            'AES_GCM_8_128': 'AES128-GCM-8',
            'AES_GCM_8_256': 'AES256-GCM-8',
            'AES_GCM_12_128': 'AES128-GCM-12',
            'AES_GCM_12_256': 'AES256-GCM-12',
            'AES_CCM_16_128': 'AES128-CCM-16',
            'AES_CCM_16_256': 'AES256-CCM-16',
            'AES_CTR_128': 'AES128-CTR',
            'AES_CTR_192': 'AES192-CTR',
            'AES_CTR_256': 'AES256-CTR',
            '3DES_CBC': '3DES',
            'DES_CBC': 'DES',
            'CAMELLIA_CBC_128': 'CAMELLIA128',
            'CAMELLIA_CBC_256': 'CAMELLIA256',
            'CHACHA20_POLY1305': 'CHACHA20-POLY1305',
            'BLOWFISH_CBC': 'BLOWFISH',
            'CAST5_CBC': 'CAST5'
        }
        
        self.hash_mapping = {
            'HMAC_MD5': 'MD5',
            'HMAC_MD5_96': 'MD5',
            'HMAC_SHA1': 'SHA1',
            'HMAC_SHA1_96': 'SHA1',
            'HMAC_SHA2_256': 'SHA2-256',
            'HMAC_SHA2_256_128': 'SHA2-256',
            'HMAC_SHA2_384': 'SHA2-384',
            'HMAC_SHA2_384_192': 'SHA2-384',
            'HMAC_SHA2_512': 'SHA2-512',
            'HMAC_SHA2_512_256': 'SHA2-512',
            'HMAC_SHA3_224': 'SHA3-224',
            'HMAC_SHA3_256': 'SHA3-256',
            'HMAC_SHA3_384': 'SHA3-384',
            'HMAC_SHA3_512': 'SHA3-512',
            'AES_GMAC_128': 'GMAC-128',
            'AES_GMAC_192': 'GMAC-192',
            'AES_GMAC_256': 'GMAC-256',
            'POLY1305': 'POLY1305'
        }
        
        self.prf_mapping = {
            'PRF_HMAC_MD5': 'MD5',
            'PRF_HMAC_SHA1': 'SHA1',
            'PRF_HMAC_SHA2_256': 'SHA2-256',
            'PRF_HMAC_SHA2_384': 'SHA2-384',
            'PRF_HMAC_SHA2_512': 'SHA2-512',
            'PRF_AES128_CMAC': 'AES128-CMAC',
            'PRF_AES128_XCBC': 'AES128-XCBC',
            'PRF_HMAC_SHA3_224': 'SHA3-224',
            'PRF_HMAC_SHA3_256': 'SHA3-256',
            'PRF_HMAC_SHA3_384': 'SHA3-384',
            'PRF_HMAC_SHA3_512': 'SHA3-512'
        }

    def parse_ike_proposal(self, proposal):
        """
        Parse an IKE or ESP proposal string into a structured format.

        Args:
            proposal (str): The proposal string, e.g., "IKE:AES_CBC_256/HMAC_SHA2_256/PRF_HMAC_SHA2_256/MODP_2048"
            
        Returns:
            dict: A dictionary with keys 'encryption', 'hash', 'prf', and 'dh_group'
        """

        # Split the proposal into components based on '/'
        components = proposal.split('/')
        
        result = {
            'encryption': [],
            'hash': [],
            'prf': [],
            'dh_group': []
        }
        
        
        is_ike = proposal.startswith('IKE:')
        is_esp = proposal.startswith('ESP:')
        

        # Remove IKE or ESP prefix if present for easier parsing later
        if is_ike or is_esp:
            components[0] = components[0].replace('IKE:', '').replace('ESP:', '')
        

        enc_components = []
        hash_components = []
        prf_components = []
        dh_components = []

        
        # Determine the current section based on the first component
        current_section = 'enc'
        for component in components:
            if component in self.enc_mapping:
                if current_section != 'enc':
                    current_section = 'enc'
                enc_components.append(component)
            elif component in self.hash_mapping:
                if current_section != 'hash':
                    current_section = 'hash'
                hash_components.append(component)
            elif is_ike and 'PRF_' in component:
                if current_section != 'prf':
                    current_section = 'prf'
                prf_components.append(component)
            elif component in self.dh_mapping:
                if current_section != 'dh':
                    current_section = 'dh'
                dh_components.append(component)
            elif component == 'NO_EXT_SEQ':
                continue  # Skip NO_EXT_SEQ as it’s not relevant to crypto algorithms
        

        # Map encryption components
        for enc in enc_components:
            mapped_enc = self.enc_mapping.get(enc, 'Unknown')
            if mapped_enc != 'Unknown' and mapped_enc not in result['encryption']:
                result['encryption'].append(mapped_enc)
        
        # Map hash components (skip for AEAD ciphers like AES-GCM)
        if not any(enc.startswith('AES_GCM') or enc.startswith('AES_CCM') or enc == 'CHACHA20_POLY1305' for enc in enc_components):
            for hash_val in hash_components:
                mapped_hash = self.hash_mapping.get(hash_val, 'Unknown')
                if mapped_hash != 'Unknown' and mapped_hash not in result['hash']:
                    result['hash'].append(mapped_hash)
        else:
            result['hash'] = ['None']

        
        # Map PRF components
        for prf in prf_components:
            mapped_prf = self.prf_mapping.get(prf, 'Unknown')
            if mapped_prf == "Unknown":
                result['prf'].append(mapped_prf)
            if mapped_prf != 'Unknown' and mapped_prf not in result['prf']:
                result['prf'].append(mapped_prf)


        # Map DH group components
        for dh in dh_components:
            mapped_dh = self.dh_mapping.get(dh)
            if mapped_dh != 'None' and mapped_dh not in result['dh_group']:
                result['dh_group'].append(mapped_dh)

        
        # Handle ESP case (no PRF for ESP proposals)
        if is_esp:
            result['prf'] = ['None']
        
        if not result['encryption']:
            result['encryption'] = ['Unknown']

        if not result['hash']:
            result['hash'] = ['Unknown']

        return result

    def process_proposals(self, proposal_list):
        """
        Process a list of IKE or ESP proposals, concatenating encryption, hash, PRF (for IKE only), 
        and DH group values, and indicate whether PFS is enabled for ESP proposals only.
        
        Args:
            proposal_list (str): Comma-separated string of IKE or ESP proposals
        
        Returns:
            str: Formatted string with concatenated encryption, hash, PRF (for IKE), DH groups, and PFS status (for ESP)
        """
        proposal_list = proposal_list.replace(',', ', ')
        proposals = proposal_list.strip().split(', ')

        
        # Collect unique encryption, hash, PRF, and DH groups
        enc_set = set()
        hash_set = set()
        prf_set = set()
        dh_set = set()


        # Parse each proposal and update the sets for later sorting and formatting
        for proposal in proposals:
            parsed = self.parse_ike_proposal(proposal.strip())
            enc_set.update(parsed['encryption'])
            if parsed['hash'] != ['None']:
                hash_set.update(parsed['hash'])
            if parsed['prf'] != ['None']:
                prf_set.update(parsed['prf'])
            if parsed['dh_group'] != ['None']:
                dh_set.update(parsed['dh_group'])

        
        # Convert sets to sorted lists
        enc_list = sorted(list(enc_set))
        hash_list = sorted(list(hash_set))
        prf_list = sorted(list(prf_set))
        dh_list = sorted(list(dh_set), key=lambda x: int(x))

        
        # Determine PFS status for ESP proposals only
        is_ike = any(proposal.startswith('IKE:') for proposal in proposals)
        pfs_status = "PFS: Enabled" if dh_set and not is_ike else "PFS: None"

        
        # Format output as a single concatenated string
        enc_part = f"Encryption: {', '.join(enc_list)}" if enc_list else "Encryption: None"
        hash_part = f"Hash: {', '.join(hash_list)}" if hash_list else "Hash: None"
        dh_part = f"DH Group(s): {', '.join(dh_list)}" if dh_list else "DH Group(s): None"
        prf_part = f"PRF: {', '.join(prf_list)}" if prf_list else "PRF: None"

        
        # Return formatted string based on whether it's an IKE or ESP proposal
        if is_ike:
            return f"{enc_part} {hash_part} {prf_part} {dh_part}"
        else:
            return f"{enc_part} {hash_part} {dh_part} {pfs_status}"
            
# Example usage
if __name__ == "__main__":
    parser = ProposalParser()

    #IKEV1 PROPOSALS AWS DEFAULT
    ikev1_default_proposals = """IKE:AES_CBC_128/HMAC_SHA1_96/PRF_HMAC_SHA1/MODP_1024, IKE:AES_CBC_128/HMAC_SHA1_96/PRF_HMAC_SHA1/MODP_2048, IKE:AES_CBC_128/HMAC_SHA1_96/PRF_HMAC_SHA1/MODP_3072, IKE:AES_CBC_128/HMAC_SHA1_96/PRF_HMAC_SHA1/MODP_4096, IKE:AES_CBC_128/HMAC_SHA1_96/PRF_HMAC_SHA1/MODP_6144, IKE:AES_CBC_128/HMAC_SHA1_96/PRF_HMAC_SHA1/MODP_8192, IKE:AES_CBC_128/HMAC_SHA1_96/PRF_HMAC_SHA1/ECP_256, IKE:AES_CBC_128/HMAC_SHA1_96/PRF_HMAC_SHA1/ECP_384, IKE:AES_CBC_128/HMAC_SHA1_96/PRF_HMAC_SHA1/ECP_521, IKE:AES_CBC_128/HMAC_SHA1_96/PRF_HMAC_SHA1/MODP_1024_160, IKE:AES_CBC_128/HMAC_SHA1_96/PRF_HMAC_SHA1/MODP_2048_224, IKE:AES_CBC_128/HMAC_SHA1_96/PRF_HMAC_SHA1/MODP_2048_256, IKE:AES_CBC_128/HMAC_SHA2_256_128/PRF_HMAC_SHA2_256/MODP_1024, IKE:AES_CBC_128/HMAC_SHA2_256_128/PRF_HMAC_SHA2_256/MODP_2048, IKE:AES_CBC_128/HMAC_SHA2_256_128/PRF_HMAC_SHA2_256/MODP_3072, IKE:AES_CBC_128/HMAC_SHA2_256_128/PRF_HMAC_SHA2_256/MODP_4096, IKE:AES_CBC_128/HMAC_SHA2_256_128/PRF_HMAC_SHA2_256/MODP_6144, IKE:AES_CBC_128/HMAC_SHA2_256_128/PRF_HMAC_SHA2_256/MODP_8192, IKE:AES_CBC_128/HMAC_SHA2_256_128/PRF_HMAC_SHA2_256/ECP_256, IKE:AES_CBC_128/HMAC_SHA2_256_128/PRF_HMAC_SHA2_256/ECP_384, IKE:AES_CBC_128/HMAC_SHA2_256_128/PRF_HMAC_SHA2_256/ECP_521, IKE:AES_CBC_128/HMAC_SHA2_256_128/PRF_HMAC_SHA2_256/MODP_1024_160, IKE:AES_CBC_128/HMAC_SHA2_256_128/PRF_HMAC_SHA2_256/MODP_2048_224, IKE:AES_CBC_128/HMAC_SHA2_256_128/PRF_HMAC_SHA2_256/MODP_2048_256, IKE:AES_CBC_128/HMAC_SHA2_384_192/PRF_HMAC_SHA2_384/MODP_1024, IKE:AES_CBC_128/HMAC_SHA2_384_192/PRF_HMAC_SHA2_384/MODP_2048, IKE:AES_CBC_128/HMAC_SHA2_384_192/PRF_HMAC_SHA2_384/MODP_3072, IKE:AES_CBC_128/HMAC_SHA2_384_192/PRF_HMAC_SHA2_384/MODP_4096, IKE:AES_CBC_128/HMAC_SHA2_384_192/PRF_HMAC_SHA2_384/MODP_6144, IKE:AES_CBC_128/HMAC_SHA2_384_192/PRF_HMAC_SHA2_384/MODP_8192, IKE:AES_CBC_128/HMAC_SHA2_384_192/PRF_HMAC_SHA2_384/ECP_256, IKE:AES_CBC_128/HMAC_SHA2_384_192/PRF_HMAC_SHA2_384/ECP_384, IKE:AES_CBC_128/HMAC_SHA2_384_192/PRF_HMAC_SHA2_384/ECP_521, IKE:AES_CBC_128/HMAC_SHA2_384_192/PRF_HMAC_SHA2_384/MODP_1024_160, IKE:AES_CBC_128/HMAC_SHA2_384_192/PRF_HMAC_SHA2_384/MODP_2048_224, IKE:AES_CBC_128/HMAC_SHA2_384_192/PRF_HMAC_SHA2_384/MODP_2048_256, IKE:AES_CBC_128/HMAC_SHA2_512_256/PRF_HMAC_SHA2_512/MODP_1024, IKE:AES_CBC_128/HMAC_SHA2_512_256/PRF_HMAC_SHA2_512/MODP_2048, IKE:AES_CBC_128/HMAC_SHA2_512_256/PRF_HMAC_SHA2_512/MODP_3072, IKE:AES_CBC_128/HMAC_SHA2_512_256/PRF_HMAC_SHA2_512/MODP_4096, IKE:AES_CBC_128/HMAC_SHA2_512_256/PRF_HMAC_SHA2_512/MODP_6144, IKE:AES_CBC_128/HMAC_SHA2_512_256/PRF_HMAC_SHA2_512/MODP_8192, IKE:AES_CBC_128/HMAC_SHA2_512_256/PRF_HMAC_SHA2_512/ECP_256, IKE:AES_CBC_128/HMAC_SHA2_512_256/PRF_HMAC_SHA2_512/ECP_384, IKE:AES_CBC_128/HMAC_SHA2_512_256/PRF_HMAC_SHA2_512/ECP_521, IKE:AES_CBC_128/HMAC_SHA2_512_256/PRF_HMAC_SHA2_512/MODP_1024_160, IKE:AES_CBC_128/HMAC_SHA2_512_256/PRF_HMAC_SHA2_512/MODP_2048_224, IKE:AES_CBC_128/HMAC_SHA2_512_256/PRF_HMAC_SHA2_512/MODP_2048_256, IKE:AES_CBC_256/HMAC_SHA1_96/PRF_HMAC_SHA1/MODP_1024, IKE:AES_CBC_256/HMAC_SHA1_96/PRF_HMAC_SHA1/MODP_2048, IKE:AES_CBC_256/HMAC_SHA1_96/PRF_HMAC_SHA1/MODP_3072, IKE:AES_CBC_256/HMAC_SHA1_96/PRF_HMAC_SHA1/MODP_4096, IKE:AES_CBC_256/HMAC_SHA1_96/PRF_HMAC_SHA1/MODP_6144, IKE:AES_CBC_256/HMAC_SHA1_96/PRF_HMAC_SHA1/MODP_8192, IKE:AES_CBC_256/HMAC_SHA1_96/PRF_HMAC_SHA1/ECP_256, IKE:AES_CBC_256/HMAC_SHA1_96/PRF_HMAC_SHA1/ECP_384, IKE:AES_CBC_256/HMAC_SHA1_96/PRF_HMAC_SHA1/ECP_521, IKE:AES_CBC_256/HMAC_SHA1_96/PRF_HMAC_SHA1/MODP_1024_160, IKE:AES_CBC_256/HMAC_SHA1_96/PRF_HMAC_SHA1/MODP_2048_224, IKE:AES_CBC_256/HMAC_SHA1_96/PRF_HMAC_SHA1/MODP_2048_256, IKE:AES_CBC_256/HMAC_SHA2_256_128/PRF_HMAC_SHA2_256/MODP_1024, IKE:AES_CBC_256/HMAC_SHA2_256_128/PRF_HMAC_SHA2_256/MODP_2048, IKE:AES_CBC_256/HMAC_SHA2_256_128/PRF_HMAC_SHA2_256/MODP_3072, IKE:AES_CBC_256/HMAC_SHA2_256_128/PRF_HMAC_SHA2_256/MODP_4096, IKE:AES_CBC_256/HMAC_SHA2_256_128/PRF_HMAC_SHA2_256/MODP_6144, IKE:AES_CBC_256/HMAC_SHA2_256_128/PRF_HMAC_SHA2_256/MODP_8192, IKE:AES_CBC_256/HMAC_SHA2_256_128/PRF_HMAC_SHA2_256/ECP_256, IKE:AES_CBC_256/HMAC_SHA2_256_128/PRF_HMAC_SHA2_256/ECP_384, IKE:AES_CBC_256/HMAC_SHA2_256_128/PRF_HMAC_SHA2_256/ECP_521, IKE:AES_CBC_256/HMAC_SHA2_256_128/PRF_HMAC_SHA2_256/MODP_1024_160, IKE:AES_CBC_256/HMAC_SHA2_256_128/PRF_HMAC_SHA2_256/MODP_2048_224, IKE:AES_CBC_256/HMAC_SHA2_256_128/PRF_HMAC_SHA2_256/MODP_2048_256, IKE:AES_CBC_256/HMAC_SHA2_384_192/PRF_HMAC_SHA2_384/MODP_1024, IKE:AES_CBC_256/HMAC_SHA2_384_192/PRF_HMAC_SHA2_384/MODP_2048, IKE:AES_CBC_256/HMAC_SHA2_384_192/PRF_HMAC_SHA2_384/MODP_3072, IKE:AES_CBC_256/HMAC_SHA2_384_192/PRF_HMAC_SHA2_384/MODP_4096, IKE:AES_CBC_256/HMAC_SHA2_384_192/PRF_HMAC_SHA2_384/MODP_6144, IKE:AES_CBC_256/HMAC_SHA2_384_192/PRF_HMAC_SHA2_384/MODP_8192, IKE:AES_CBC_256/HMAC_SHA2_384_192/PRF_HMAC_SHA2_384/ECP_256, IKE:AES_CBC_256/HMAC_SHA2_384_192/PRF_HMAC_SHA2_384/ECP_384, IKE:AES_CBC_256/HMAC_SHA2_384_192/PRF_HMAC_SHA2_384/ECP_521, IKE:AES_CBC_256/HMAC_SHA2_384_192/PRF_HMAC_SHA2_384/MODP_1024_160, IKE:AES_CBC_256/HMAC_SHA2_384_192/PRF_HMAC_SHA2_384/MODP_2048_224, IKE:AES_CBC_256/HMAC_SHA2_384_192/PRF_HMAC_SHA2_384/MODP_2048_256, IKE:AES_CBC_256/HMAC_SHA2_512_256/PRF_HMAC_SHA2_512/MODP_1024, IKE:AES_CBC_256/HMAC_SHA2_512_256/PRF_HMAC_SHA2_512/MODP_2048, IKE:AES_CBC_256/HMAC_SHA2_512_256/PRF_HMAC_SHA2_512/MODP_3072, IKE:AES_CBC_256/HMAC_SHA2_512_256/PRF_HMAC_SHA2_512/MODP_4096, IKE:AES_CBC_256/HMAC_SHA2_512_256/PRF_HMAC_SHA2_512/MODP_6144, IKE:AES_CBC_256/HMAC_SHA2_512_256/PRF_HMAC_SHA2_512/MODP_8192, IKE:AES_CBC_256/HMAC_SHA2_512_256/PRF_HMAC_SHA2_512/ECP_256, IKE:AES_CBC_256/HMAC_SHA2_512_256/PRF_HMAC_SHA2_512/ECP_384, IKE:AES_CBC_256/HMAC_SHA2_512_256/PRF_HMAC_SHA2_512/ECP_521, IKE:AES_CBC_256/HMAC_SHA2_512_256/PRF_HMAC_SHA2_512/MODP_1024_160, IKE:AES_CBC_256/HMAC_SHA2_512_256/PRF_HMAC_SHA2_512/MODP_2048_224, IKE:AES_CBC_256/HMAC_SHA2_512_256/PRF_HMAC_SHA2_512/MODP_2048_256"""

    #ESP Phase 2 proposals example AWS DEFAULT
    ikev1_default_esp_proposals = """ESP:AES_CBC_128/HMAC_SHA1_96/MODP_1024/NO_EXT_SEQ, ESP:AES_CBC_128/HMAC_SHA1_96/MODP_1536/NO_EXT_SEQ, ESP:AES_CBC_128/HMAC_SHA1_96/MODP_2048/NO_EXT_SEQ, ESP:AES_CBC_128/HMAC_SHA1_96/MODP_3072/NO_EXT_SEQ, ESP:AES_CBC_128/HMAC_SHA1_96/MODP_4096/NO_EXT_SEQ, ESP:AES_CBC_128/HMAC_SHA1_96/MODP_6144/NO_EXT_SEQ, ESP:AES_CBC_128/HMAC_SHA1_96/MODP_8192/NO_EXT_SEQ, ESP:AES_CBC_128/HMAC_SHA1_96/ECP_256/NO_EXT_SEQ, ESP:AES_CBC_128/HMAC_SHA1_96/ECP_384/NO_EXT_SEQ, ESP:AES_CBC_128/HMAC_SHA1_96/ECP_521/NO_EXT_SEQ, ESP:AES_CBC_128/HMAC_SHA1_96/MODP_1024_160/NO_EXT_SEQ, ESP:AES_CBC_128/HMAC_SHA1_96/MODP_2048_224/NO_EXT_SEQ, ESP:AES_CBC_128/HMAC_SHA1_96/MODP_2048_256/NO_EXT_SEQ, ESP:AES_CBC_128/HMAC_SHA2_256_128/MODP_1024/NO_EXT_SEQ, ESP:AES_CBC_128/HMAC_SHA2_256_128/MODP_1536/NO_EXT_SEQ, ESP:AES_CBC_128/HMAC_SHA2_256_128/MODP_2048/NO_EXT_SEQ, ESP:AES_CBC_128/HMAC_SHA2_256_128/MODP_3072/NO_EXT_SEQ, ESP:AES_CBC_128/HMAC_SHA2_256_128/MODP_4096/NO_EXT_SEQ, ESP:AES_CBC_128/HMAC_SHA2_256_128/MODP_6144/NO_EXT_SEQ, ESP:AES_CBC_128/HMAC_SHA2_256_128/MODP_8192/NO_EXT_SEQ, ESP:AES_CBC_128/HMAC_SHA2_256_128/ECP_256/NO_EXT_SEQ, ESP:AES_CBC_128/HMAC_SHA2_256_128/ECP_384/NO_EXT_SEQ, ESP:AES_CBC_128/HMAC_SHA2_256_128/ECP_521/NO_EXT_SEQ, ESP:AES_CBC_128/HMAC_SHA2_256_128/MODP_1024_160/NO_EXT_SEQ, ESP:AES_CBC_128/HMAC_SHA2_256_128/MODP_2048_224/NO_EXT_SEQ, ESP:AES_CBC_128/HMAC_SHA2_256_128/MODP_2048_256/NO_EXT_SEQ, ESP:AES_CBC_128/HMAC_SHA2_384_192/MODP_1024/NO_EXT_SEQ, ESP:AES_CBC_128/HMAC_SHA2_384_192/MODP_1536/NO_EXT_SEQ, ESP:AES_CBC_128/HMAC_SHA2_384_192/MODP_2048/NO_EXT_SEQ, ESP:AES_CBC_128/HMAC_SHA2_384_192/MODP_3072/NO_EXT_SEQ, ESP:AES_CBC_128/HMAC_SHA2_384_192/MODP_4096/NO_EXT_SEQ, ESP:AES_CBC_128/HMAC_SHA2_384_192/MODP_6144/NO_EXT_SEQ, ESP:AES_CBC_128/HMAC_SHA2_384_192/MODP_8192/NO_EXT_SEQ, ESP:AES_CBC_128/HMAC_SHA2_384_192/ECP_256/NO_EXT_SEQ, ESP:AES_CBC_128/HMAC_SHA2_384_192/ECP_384/NO_EXT_SEQ, ESP:AES_CBC_128/HMAC_SHA2_384_192/ECP_521/NO_EXT_SEQ, ESP:AES_CBC_128/HMAC_SHA2_384_192/MODP_1024_160/NO_EXT_SEQ, ESP:AES_CBC_128/HMAC_SHA2_384_192/MODP_2048_224/NO_EXT_SEQ, ESP:AES_CBC_128/HMAC_SHA2_384_192/MODP_2048_256/NO_EXT_SEQ, ESP:AES_CBC_128/HMAC_SHA2_512_256/MODP_1024/NO_EXT_SEQ, ESP:AES_CBC_128/HMAC_SHA2_512_256/MODP_1536/NO_EXT_SEQ, ESP:AES_CBC_128/HMAC_SHA2_512_256/MODP_2048/NO_EXT_SEQ, ESP:AES_CBC_128/HMAC_SHA2_512_256/MODP_3072/NO_EXT_SEQ, ESP:AES_CBC_128/HMAC_SHA2_512_256/MODP_4096/NO_EXT_SEQ, ESP:AES_CBC_128/HMAC_SHA2_512_256/MODP_6144/NO_EXT_SEQ, ESP:AES_CBC_128/HMAC_SHA2_512_256/MODP_8192/NO_EXT_SEQ, ESP:AES_CBC_128/HMAC_SHA2_512_256/ECP_256/NO_EXT_SEQ, ESP:AES_CBC_128/HMAC_SHA2_512_256/ECP_384/NO_EXT_SEQ, ESP:AES_CBC_128/HMAC_SHA2_512_256/ECP_521/NO_EXT_SEQ, ESP:AES_CBC_128/HMAC_SHA2_512_256/MODP_1024_160/NO_EXT_SEQ, ESP:AES_CBC_128/HMAC_SHA2_512_256/MODP_2048_224/NO_EXT_SEQ, ESP:AES_CBC_128/HMAC_SHA2_512_256/MODP_2048_256/NO_EXT_SEQ, ESP:AES_CBC_256/HMAC_SHA1_96/MODP_1024/NO_EXT_SEQ, ESP:AES_CBC_256/HMAC_SHA1_96/MODP_1536/NO_EXT_SEQ, ESP:AES_CBC_256/HMAC_SHA1_96/MODP_2048/NO_EXT_SEQ, ESP:AES_CBC_256/HMAC_SHA1_96/MODP_3072/NO_EXT_SEQ, ESP:AES_CBC_256/HMAC_SHA1_96/MODP_4096/NO_EXT_SEQ, ESP:AES_CBC_256/HMAC_SHA1_96/MODP_6144/NO_EXT_SEQ, ESP:AES_CBC_256/HMAC_SHA1_96/MODP_8192/NO_EXT_SEQ, ESP:AES_CBC_256/HMAC_SHA1_96/ECP_256/NO_EXT_SEQ, ESP:AES_CBC_256/HMAC_SHA1_96/ECP_384/NO_EXT_SEQ, ESP:AES_CBC_256/HMAC_SHA1_96/ECP_521/NO_EXT_SEQ, ESP:AES_CBC_256/HMAC_SHA1_96/MODP_1024_160/NO_EXT_SEQ, ESP:AES_CBC_256/HMAC_SHA1_96/MODP_2048_224/NO_EXT_SEQ, ESP:AES_CBC_256/HMAC_SHA1_96/MODP_2048_256/NO_EXT_SEQ,ESP:AES_CBC_256/HMAC_SHA2_256_128/MODP_1024/NO_EXT_SEQ, ESP:AES_CBC_256/HMAC_SHA2_256_128/MODP_1536/NO_EXT_SEQ, ESP:AES_CBC_256/HMAC_SHA2_256_128/MODP_2048/NO_EXT_SEQ, ESP:AES_CBC_256/HMAC_SHA2_256_128/MODP_3072/NO_EXT_SEQ, ESP:AES_CBC_256/HMAC_SHA2_256_128/MODP_4096/NO_EXT_SEQ, ESP:AES_CBC_256/HMAC_SHA2_256_128/MODP_6144/NO_EXT_SEQ, ESP:AES_CBC_256/HMAC_SHA2_256_128/MODP_8192/NO_EXT_SEQ, ESP:AES_CBC_256/HMAC_SHA2_256_128/ECP_256/NO_EXT_SEQ, ESP:AES_CBC_256/HMAC_SHA2_256_128/ECP_384/NO_EXT_SEQ, ESP:AES_CBC_256/HMAC_SHA2_256_128/ECP_521/NO_EXT_SEQ, ESP:AES_CBC_256/HMAC_SHA2_256_128/MODP_1024_160/NO_EXT_SEQ, ESP:AES_CBC_256/HMAC_SHA2_256_128/MODP_2048_224/NO_EXT_SEQ, ESP:AES_CBC_256/HMAC_SHA2_256_128/MODP_2048_256/NO_EXT_SEQ, ESP:AES_CBC_256/HMAC_SHA2_384_192/MODP_1024/NO_EXT_SEQ, ESP:AES_CBC_256/HMAC_SHA2_384_192/MODP_1536/NO_EXT_SEQ, ESP:AES_CBC_256/HMAC_SHA2_384_192/MODP_2048/NO_EXT_SEQ, ESP:AES_CBC_256/HMAC_SHA2_384_192/MODP_3072/NO_EXT_SEQ, ESP:AES_CBC_256/HMAC_SHA2_384_192/MODP_4096/NO_EXT_SEQ, ESP:AES_CBC_256/HMAC_SHA2_384_192/MODP_6144/NO_EXT_SEQ, ESP:AES_CBC_256/HMAC_SHA2_384_192/MODP_8192/NO_EXT_SEQ, ESP:AES_CBC_256/HMAC_SHA2_384_192/ECP_256/NO_EXT_SEQ, ESP:AES_CBC_256/HMAC_SHA2_384_192/ECP_384/NO_EXT_SEQ, ESP:AES_CBC_256/HMAC_SHA2_384_192/ECP_521/NO_EXT_SEQ, ESP:AES_CBC_256/HMAC_SHA2_384_192/MODP_1024_160/NO_EXT_SEQ, ESP:AES_CBC_256/HMAC_SHA2_384_192/MODP_2048_224/NO_EXT_SEQ, ESP:AES_CBC_256/HMAC_SHA2_384_192/MODP_2048_256/NO_EXT_SEQ, ESP:AES_CBC_256/HMAC_SHA2_512_256/MODP_1024/NO_EXT_SEQ, ESP:AES_CBC_256/HMAC_SHA2_512_256/MODP_1536/NO_EXT_SEQ, ESP:AES_CBC_256/HMAC_SHA2_512_256/MODP_2048/NO_EXT_SEQ, ESP:AES_CBC_256/HMAC_SHA2_512_256/MODP_3072/NO_EXT_SEQ, ESP:AES_CBC_256/HMAC_SHA2_512_256/MODP_4096/NO_EXT_SEQ, ESP:AES_CBC_256/HMAC_SHA2_512_256/MODP_6144/NO_EXT_SEQ, ESP:AES_CBC_256/HMAC_SHA2_512_256/MODP_8192/NO_EXT_SEQ, ESP:AES_CBC_256/HMAC_SHA2_512_256/ECP_256/NO_EXT_SEQ, ESP:AES_CBC_256/HMAC_SHA2_512_256/ECP_384/NO_EXT_SEQ, ESP:AES_CBC_256/HMAC_SHA2_512_256/ECP_521/NO_EXT_SEQ, ESP:AES_CBC_256/HMAC_SHA2_512_256/MODP_1024_160/NO_EXT_SEQ, ESP:AES_CBC_256/HMAC_SHA2_512_256/MODP_2048_224/NO_EXT_SEQ, ESP:AES_CBC_256/HMAC_SHA2_512_256/MODP_2048_256/NO_EXT_SEQ, ESP:AES_GCM_16_128/MODP_1024/NO_EXT_SEQ, ESP:AES_GCM_16_128/MODP_1536/NO_EXT_SEQ, ESP:AES_GCM_16_128/MODP_2048/NO_EXT_SEQ, ESP:AES_GCM_16_128/MODP_3072/NO_EXT_SEQ, ESP:AES_GCM_16_128/MODP_4096/NO_EXT_SEQ, ESP:AES_GCM_16_128/MODP_6144/NO_EXT_SEQ, ESP:AES_GCM_16_128/MODP_8192/NO_EXT_SEQ, ESP:AES_GCM_16_128/ECP_256/NO_EXT_SEQ, ESP:AES_GCM_16_128/ECP_384/NO_EXT_SEQ, ESP:AES_GCM_16_128/ECP_521/NO_EXT_SEQ, ESP:AES_GCM_16_128/MODP_1024_160/NO_EXT_SEQ, ESP:AES_GCM_16_128/MODP_2048_224/NO_EXT_SEQ, ESP:AES_GCM_16_128/MODP_2048_256/NO_EXT_SEQ, ESP:AES_GCM_16_256/MODP_1024/NO_EXT_SEQ, ESP:AES_GCM_16_256/MODP_1536/NO_EXT_SEQ, ESP:AES_GCM_16_256/MODP_2048/NO_EXT_SEQ, ESP:AES_GCM_16_256/MODP_3072/NO_EXT_SEQ, ESP:AES_GCM_16_256/MODP_4096/NO_EXT_SEQ, ESP:AES_GCM_16_256/MODP_6144/NO_EXT_SEQ, ESP:AES_GCM_16_256/MODP_8192/NO_EXT_SEQ, ESP:AES_GCM_16_256/ECP_256/NO_EXT_SEQ, ESP:AES_GCM_16_256/ECP_384/NO_EXT_SEQ, ESP:AES_GCM_16_256/ECP_521/NO_EXT_SEQ, ESP:AES_GCM_16_256/MODP_1024_160/NO_EXT_SEQ, ESP:AES_GCM_16_256/MODP_2048_224/NO_EXT_SEQ, ESP:AES_GCM_16_256/MODP_2048_256/NO_EXT_SEQ"""

    #IKEV2 PROPOSALS AWS DEFAULT
    ikev2_default_proposals = """IKE:AES_CBC_128/AES_CBC_256/HMAC_SHA1_96/HMAC_SHA2_256_128/HMAC_SHA2_384_192/HMAC_SHA2_512_256/PRF_HMAC_SHA1/PRF_HMAC_SHA2_256/PRF_HMAC_SHA2_384/PRF_HMAC_SHA2_512/MODP_1024/MODP_2048/MODP_3072/MODP_4096/MODP_6144/MODP_8192/ECP_256/ECP_384/ECP_521/MODP_1024_160/MODP_2048_224/MODP_2048_256, IKE:AES_GCM_16_128/AES_GCM_16_256/PRF_HMAC_SHA1/PRF_HMAC_SHA2_256/PRF_HMAC_SHA2_384/PRF_HMAC_SHA2_512/MODP_1024/MODP_2048/MODP_3072/MODP_4096/MODP_6144/MODP_8192/ECP_256/ECP_384/ECP_521/MODP_1024_160/MODP_2048_224/MODP_2048_256"""

    #ESP PROPOSALS Phase 2 AWS DEFAULT 
    #ikev2_default_esp_proposals = """IKE:AES_CBC_256/HMAC_SHA1_96/PRF_INVALID/MODP_2048"""
    ikev2_default_esp_proposals = """IKE:AES_CBC_128/AES_CBC_256/HMAC_SHA1_96/HMAC_SHA2_256_128/HMAC_SHA2_384_192/HMAC_SHA2_512_256/PRF_HMAC_SHA1/PRF_HMAC_SHA2_256/PRF_HMAC_SHA2_384/PRF_HMAC_SHA2_512/MODP_1024/MODP_2048/MODP_3072/MODP_4096/MODP_6144/MODP_8192/ECP_256/ECP_384/ECP_521/MODP_1024_160/MODP_2048_224/MODP_2048_256, IKE:AES_GCM_16_128/AES_GCM_16_256/PRF_HMAC_SHA1/PRF_HMAC_SHA2_256/PRF_HMAC_SHA2_384/PRF_HMAC_SHA2_512/MODP_1024/MODP_2048/MODP_3072/MODP_4096/MODP_6144/MODP_8192/ECP_256/ECP_384/ECP_521/MODP_1024_160/MODP_2048_224/MODP_2048_256"""
    #ikev2_default_esp_proposals = """IKE:AES_CBC_256/HMAC_SHA1_96/PRF_HMAC_SHA1/MODP_2048"""
    #proposal = "IKE:AES_CBC_128/AES_CBC_256/HMAC_SHA1_96/HMAC_SHA2_256_128/HMAC_SHA2_384_192/HMAC_SHA2_512_256/PRF_HMAC_SHA1/PRF_HMAC_SHA2_256/PRF_HMAC_SHA2_384/PRF_HMAC_SHA2_512/MODP_1024/MODP_2048/MODP_3072/MODP_4096/MODP_6144/MODP_8192/ECP_256/ECP_384/ECP_521/MODP_1024_160/MODP_2048_224/MODP_2048_256, IKE:AES_GCM_16_128/AES_GCM_16_256/PRF_HMAC_SHA1/PRF_HMAC_SHA2_256/PRF_HMAC_SHA2_384/PRF_HMAC_SHA2_512/MODP_1024/MODP_2048/MODP_3072/MODP_4096/MODP_6144/MODP_8192/ECP_256/ECP_384/ECP_521/MODP_1024_160/MODP_2048_224/MODP_2048_256"
    proposal = """
    
    testing = """IKE:AES_CBC_128/HMAC_SHA2_512_256/PRF_HMAC_SHA2_512/MODP_2048,IKE:AES_CBC_128/HMAC_SHA2_512_256/PRF_HMAC_SHA2_512/MODP_3072"""
    # print(f'IKEv1 AWS DEFAULT PROPOSALS\n{parser.process_proposals(ikev1_default_proposals)}')
    # print(f'\n\nIKEv2 AWS DEFAULT PROPOSALS\n{parser.process_proposals(ikev2_default_proposals)}')
    # print(f'\n\nIKEv1 ESP PROPOSALS AWS DEFAULT\n{parser.process_proposals(ikev1_default_esp_proposals)}')
    print(f'\n\nIKEv2 ESP PROPOSALS AWS DEFAULT\n{parser.process_proposals(proposal)}')
import re

def _map_ikev2_vendor_capabilities(message_type, input_string):
    # List of acceptable message types
    valid_message_types = ['IKE_SA_INIT', 'IKE_AUTH']
    
    # Validate message type
    if message_type not in valid_message_types:
        raise ValueError(f"Invalid message type: {message_type}. Must be one of {valid_message_types}")
    
    # Mapping dictionary for IKE values with RFC references
    value_map = {
        # IKE_SA_INIT capabilities
        'FRAG_SUP': 'IKE Fragmentation',  # RFC 7383, Section 3
        'REDIR_SUP': 'IKE Redirection',  # RFC 5685, Section 3
        'HASH_ALG': 'Hash Algorithms',  # RFC 7296, Section 3.3.2
        'NATD_S_IP': 'NAT Detection (Source IP)',  # RFC 7296, Section 2.23
        'NATD_D_IP': 'NAT Detection (Destination IP)',  # RFC 7296, Section 2.23
        'SIGN_HASH_ALGS': 'Signature Hash Algorithms',  # RFC 7296, Section 2.15
        'NON_FIRST_FRAGMENTS': 'Non-First IKE Fragments',  # RFC 7383, Section 3
        'CHILDLESS_IKEV2_SUP': 'Childless IKEv2',  # RFC 6023, Section 3
        'INTERMEDIATE': 'Intermediate Exchange',  # RFC 9242, Section 3
        'COOKIE': 'Cookie-Based DoS Protection',  # RFC 7296, Section 2.6
        # IKE_AUTH capabilities
        'ESP_TFC_PAD_N': 'ESPv3 TFC Padding Disabled',  # RFC 7296, Section 3.3.1
        'MOBIKE_SUP': 'MOBIKE',  # RFC 4555, Section 3
        'MULT_AUTH': 'Multiple Auth',  # RFC 4739, Section 3
        'EAP_ONLY': 'EAP-Only Auth',  # RFC 5998, Section 3
        'MSG_ID_SYN_SUP': 'Message ID Synchronization',  # RFC 6311, Section 3
        'IPCOMP_SUPPORTED': 'IP Payload Compression Support',  # RFC 7296, Section 3.3.2
        'ADD_4_ADDR': 'Additional IPv4 Addresses',  # RFC 4555, Section 3.2
        'ADD_6_ADDR': 'Additional IPv6 Addresses',  # RFC 4555, Section 3.2
        'INIT_CONTACT': 'Initial Contact',  # RFC 7296, Section 3.16
        'HTTP_CERT_LOOKUP_SUP': 'HTTP Certificate Lookup',  # RFC 7296, Section 3.7
    }
        
    # Regex to capture N(...) patterns
    pattern = r'N\([^)]+\)'
    matches = re.findall(pattern, input_string)
    
    # Parse matches and create result list
    result = []
    for match in matches:
        key = match[2:-1]  # Extract content inside N(...)
        # Only include keys valid for the message type
        if key in value_map:
            result.append(value_map[key])
    
    return ', '.join(result)

# Example usage
ike_sa_init_string = '2025-07-18 20:16:22.839 15[ENC] <4> parsed IKE_SA_INIT request 0 [ SA KE No N(NATD_S_IP) N(NATD_D_IP) N(FRAG_SUP) N(HASH_ALG) N(MULT_AUTH) ]'
ike_auth_string = '2025-07-18 20:16:22.898 07[ENC] <4> parsed IKE_AUTH request 1 [ IDi N(INIT_CONTACT) IDr AUTH N(ESP_TFC_PAD_N) SA TSi TSr N(MOBIKE_SUP) N(ADD_4_ADDR)N(MULT_AUTH) N(EAP_ONLY) N(MSG_ID_SYN_SUP) ]'

# Test with IKE_SA_INIT
print("IKE_SA_INIT Results:")
print(_map_ikev2_vendor_capabilities("IKE_SA_INIT", ike_sa_init_string))

# Test with IKE_AUTH
print("\nIKE_AUTH Results:")
print(_map_ikev2_vendor_capabilities("IKE_AUTH", ike_auth_string))