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}")
"""
Assignment 6

The goal is to make a graph of
who bit who and who was bitten.
There should be 10 nodes and 15 edges.
3 arrows of biting each other and
3 arrows of someone biting themselves.
Networkx can not do self biting
arrows, but it is in the code.
"""

from graphviz import Digraph as DDotGraph
from graphviz import Graph as UDotGraph
import networkx as nx
from networkx.algorithms.dag import transitive_closure
import graphviz as gv
import matplotlib.pyplot as plt
import numpy as np
from numpy.linalg import matrix_power

"""
class DGraph:
    def __init__(self):
        self.d = dict()

    def clear(self):
        self.d = dict()

    def add_node(self,n):
        if not self.d.get(n):
            self.d[n] = set()

    def add_edge(self,e):
        f,t=e
        self.add_node(f)
        self.add_node(t)
        vs=self.d.get(f)
        if not vs:
            self.d[f] = {t}
        else:
            vs.add(t)

    def add_edges_from(self,es):
        for e in es:
            self.add_edge(e)

    def edges(self):
        for f in self.d:
            for t in self.d[f]:
                yield (f,t)

    def number_of_nodes(self):
        return len(self.d)

    def __repr__(self):
        return self.d.__repr__()

    def show(self):
        dot = gv.Digraph()
        for e in self.edges():
            #print(e)
            f, t = e
            dot.edge(str(f), str(t), label='')
        #print(dot.source)
        show(dot)

# displays graph with graphviz
def show(dot, show=True, file_name='graph.gv'):
    dot.render(file_name, view=show)


def showGraph(g,label="",directed=True):
    if directed:
        dot = gv.Digraph()
    else:
        dot = gv.Graph()

    for e in g.edges():
        print(e)
        f, t = e
        dot.edge(str(f), str(t), label=label)
    print(dot.source)
    show(dot)


def bit():
    G = DGraph()
    G.add_edge(("Blade","Samara"))
    G.add_edge(("Shadow","Wolfe"))
    G.add_edge(("Raven", "Austin"))
    G.add_edge(("Blade", "Alice"))
    G.add_edge(("Alice","Brandon"))
    G.add_edge(("Blade", "Wolfe"))
    G.add_edge(("Samara", "Robin"))
    G.add_edge(("Samara", "Raven"))
    G.add_edge(("Samara", "Hamed"))
    G.add_edge(("Wolfe", "Blade"))
    G.add_edge(("Hamed", "Samara"))
    G.add_edge(("Wolfe", "Shadow"))
    G.add_edge(("Brandon", "Brandon"))
    G.add_edge(("Hamed", "Hamed"))
    G.add_edge(("Austin", "Austin"))
    showGraph(G, label="bit")

bit()

def bitten():
    G=DGraph()
    G.add_edge(("Samara","Blade"))
    G.add_edge(("Wolfe","Shadow"))
    G.add_edge(("Austin", "Raven"))
    G.add_edge(("Alice","Blade"))
    G.add_edge(("Brandon", "Alice"))
    G.add_edge(("Wolfe", "Blade" ))
    G.add_edge(("Robin", "Samara"))
    G.add_edge(("Raven", "Samara"))
    G.add_edge(("Hamed", "Samara"))
    G.add_edge(("Blade", "Wolfe"))
    G.add_edge(("Samara", "Hamed"))
    G.add_edge(("Shadow", "Wolfe"))
    G.add_edge(("Brandon", "Brandon"))
    G.add_edge(("Hamed", "Hamed"))
    G.add_edge(("Austin", "Austin"))
    showGraph(G, label="bitten by")

#bitten()

family = ["Blade", "Samara", "Shadow", "Wolfe", "Raven", "Alice"]
"""

#Do transitive closure call out and the
#matrix power operation should be the same
D = nx.DiGraph()
#D.add_nodes_from("SamaraBladeWolfeShadowAliceRavenBrandonRobinHamedAustin")
D.add_edge("Blade","Samara")
D.add_edge("Shadow","Wolfe")
D.add_edge("Raven", "Austin")
D.add_edge("Blade", "Alice")
D.add_edge("Alice","Brandon")
D.add_edge("Blade", "Wolfe")
D.add_edge("Samara", "Robin")
D.add_edge("Samara", "Raven")
D.add_edge("Samara", "Hamed")
D.add_edge("Wolfe", "Blade")
D.add_edge("Hamed", "Samara")
D.add_edge("Wolfe", "Shadow")
D.add_edge("Brandon", "Brandon")
D.add_edge("Hamed", "Hamed")
D.add_edge("Austin", "Austin")

T = transitive_closure(D)

for e in D.edges(): print(e)
for n in D.nodes(): print(n)

def show(H):
    nx.draw(H, with_labels=True, font_weight='bold')
    plt.show()
#Use nx.to_numpy_matrix instead of nx.adjacency_matrix

# M = nx.adjacency_matrix(D)
# MT = nx.adjacency_matrix(T)
M = nx.to_numpy_matrix(D)
MT = nx.to_numpy_matrix(T)
M2 = M@M

def mPower(M, k): #M is numpy matrix
    assert k >= 1
    P = M
    for _ in range(k):
       P = P @ M
    return P

def tc(M):
    #compute transitive closure
    pass

D1 = nx.DiGraph(M)
D2 = nx.DiGraph(M2)

print('Matrix for Original\n', M)
N = nx.to_numpy_array(D,dtype=int)
print('np_array for Original\n', N)
print('\nMatrix for Transitive Closure\n', MT)
N2 = nx.to_numpy_array(T,dtype=int)
print('np_array for Transitive Closure\n', N2)

show(D) #can use D, T, and numpy matrix power operation
show(T)
show(T)
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': 'Redirection',  # RFC 5685, Section 3
        'HASH_ALG': 'Hash Algorithms',  # RFC 7296, Section 3.3.2
        'NATD_S_IP': 'NAT-T (Source IP)',  # RFC 7296, Section 2.23
        'NATD_D_IP': 'NAT-T (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 Not Supported',  # 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 Sync',  # 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
        'REKEY_SA': 'SA Rekeying'  # RFC 7296, Section 3.16
    }
    
    # Notifications valid for each message type
    ike_sa_init_valid = {
        'FRAG_SUP', 'REDIR_SUP', 'HASH_ALG', 'NATD_S_IP', 'NATD_D_IP',
        'SIGN_HASH_ALGS', 'NON_FIRST_FRAGMENTS', 'CHILDLESS_IKEV2_SUP',
        'INTERMEDIATE', 'COOKIE'
    }
    ike_auth_valid = {
        'ESP_TFC_PAD_N', 'MOBIKE_SUP', 'MULT_AUTH', 'EAP_ONLY', 'MSG_ID_SYN_SUP',
        'IPCOMP_SUPPORTED', 'ADD_4_ADDR', 'ADD_6_ADDR', 'INIT_CONTACT',
        'HTTP_CERT_LOOKUP_SUP', 'REKEY_SA'
    }
    
    # Select valid notifications based on message type
    valid_notifications = ike_sa_init_valid if message_type == 'IKE_SA_INIT' else ike_auth_valid
    
    # 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(...)
        if key in valid_notifications and key in value_map:
            result.append(value_map[key])
        else:
            # Log unrecognized notifications for debugging
            print(f"Warning: Unrecognized or invalid notification for {message_type}: {key}")
    
    return ', '.join(result) if result else 'None'

# 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(REDIR_SUP) N(SIGN_HASH_ALGS) N(NON_FIRST_FRAGMENTS) N(CHILDLESS_IKEV2_SUP) N(INTERMEDIATE) N(COOKIE) ]'
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) N(IPCOMP_SUPPORTED) N(ADD_6_ADDR) N(HTTP_CERT_LOOKUP_SUP) N(REKEY_SA) ]'

# 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))